Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cli): Lazy install storybook #8454

Merged
merged 11 commits into from
Jun 1, 2023
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"license": "MIT",
"workspaces": [
"packages/*",
"packages/auth-providers/*/*"
"packages/auth-providers/*/*",
"packages/cli-packages/*"
],
"scripts": {
"build": "lerna run build",
Expand Down
3 changes: 3 additions & 0 deletions packages/cli-packages/storybook/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# CLI Packages - Storybook

**WIP**: This package is the first example of extracting a command from `@redwoodjs/cli` into it's own CLI plugin package.
23 changes: 23 additions & 0 deletions packages/cli-packages/storybook/build.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import fs from 'node:fs'

import * as esbuild from 'esbuild'
import fg from 'fast-glob'

// Get source files
const sourceFiles = fg.sync(['./src/**/*.ts'])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice makes sense


// Build general source files
const result = await esbuild.build({
entryPoints: sourceFiles,
format: 'cjs',
platform: 'node',
target: ['node18'],
outdir: 'dist',
logLevel: 'info',

// For visualizing the bundle.
// See https://esbuild.github.io/api/#metafile and https://esbuild.github.io/analyze/.
metafile: true,
})

fs.writeFileSync('meta.json', JSON.stringify(result.metafile))
47 changes: 47 additions & 0 deletions packages/cli-packages/storybook/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"name": "@redwoodjs/cli-storybook",
"version": "5.0.0",
"repository": {
"type": "git",
"url": "https://github.com/redwoodjs/redwood.git",
"directory": "packages/cli-packages/storybook"
},
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"build": "yarn node ./build.mjs && yarn build:types",
"build:types": "tsc --build --verbose",
"build:watch": "nodemon --watch src --ext \"js,ts,tsx\" --ignore dist --exec \"yarn build\"",
"prepublishOnly": "NODE_ENV=production yarn build"
},
"jest": {
"testPathIgnorePatterns": [
"/dist/"
]
},
"gitHead": "3905ed045508b861b495f8d5630d76c7a157d8f1",
"dependencies": {
"@redwoodjs/project-config": "5.0.0",
"@redwoodjs/telemetry": "5.0.0",
"@storybook/addon-a11y": "7.0.18",
"@storybook/addon-docs": "7.0.18",
"@storybook/addon-essentials": "7.0.18",
"@storybook/react-webpack5": "7.0.18",
"chalk": "4.1.2",
"execa": "5.1.1",
"lodash.memoize": "4.1.2",
"storybook": "7.0.18",
"terminal-link": "2.1.1",
"yargs": "17.7.2"
},
"devDependencies": {
"esbuild": "0.17.19",
"fast-glob": "3.2.12",
"jest": "29.5.0",
"typescript": "5.1.3"
}
}
Original file line number Diff line number Diff line change
@@ -1,47 +1,58 @@
import terminalLink from 'terminal-link'

import c from '../lib/colors'
import { StorybookYargsOptions } from '../types'

export const command = 'storybook'
export const aliases = ['sb']

export const description =
'Launch Storybook: a tool for building UI components in isolation'
'Launch Storybook: a tool for building UI components and pages in isolation'

export const defaultOptions: StorybookYargsOptions = {
open: true,
build: false,
ci: false,
port: 7910,
buildDirectory: 'public/storybook',
smokeTest: false,
}

export function builder(yargs) {
// TODO: Provide a type for the `yargs` argument
export const builder = (yargs: any) => {
yargs
.option('build', {
describe: 'Build Storybook',
type: 'boolean',
default: false,
default: defaultOptions.build,
})
.option('build-directory', {
describe: 'Directory in web/ to store static files',
type: 'string',
default: 'public/storybook',
default: defaultOptions.buildDirectory,
})
.option('ci', {
describe: 'Start server in CI mode, with no interactive prompts',
type: 'boolean',
default: false,
default: defaultOptions.ci,
})
.option('open', {
describe: 'Open storybook in your browser on start',
type: 'boolean',
default: true,
default: defaultOptions.open,
})
.option('port', {
describe: 'Which port to run storybook on',
type: 'integer',
default: 7910,
default: defaultOptions.port,
})
.option('smoke-test', {
describe:
"CI mode plus smoke-test (skip prompts; don't open browser; exit after successful start)",
type: 'boolean',
default: false,
default: defaultOptions.smokeTest,
})
.check((argv) => {
// TODO: Provide a type for the `argv` argument
.check((argv: any) => {
if (argv.build && argv.smokeTest) {
throw new Error('Can not provide both "--build" and "--smoke-test"')
}
Expand All @@ -64,7 +75,10 @@ export function builder(yargs) {
)
}

export async function handler(options) {
const { handler } = await import('./storybookHandler')
await handler(options)
export const handler = async (options: StorybookYargsOptions) => {
// NOTE: We should provide some visual output before the import to increase
// the perceived performance of the command as there will be delay while we
// load the handler.
const { handler: storybookHandler } = await import('./storybookHandler.js')
await storybookHandler(options)
}
Original file line number Diff line number Diff line change
@@ -1,39 +1,43 @@
import path from 'path'
import path from 'node:path'

import execa from 'execa'

import { getPaths } from '@redwoodjs/project-config'
// Allow import of untyped package
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import { errorTelemetry } from '@redwoodjs/telemetry'

import c from '../lib/colors'
import { getPaths } from '../lib/project'
import { StorybookYargsOptions } from '../types'

const redwoodProjectPaths = getPaths()

export const handler = ({
export const handler = async ({
build,
buildDirectory,
ci,
open,
port,
smokeTest,
}) => {
const cwd = redwoodProjectPaths.web.base
}: StorybookYargsOptions) => {
const cwd = getPaths().web.base
const staticAssetsFolder = path.join(cwd, 'public')
const execaOptions = {
const execaOptions: Partial<execa.Options> = {
stdio: 'inherit',
shell: true,
cwd,
}

// Create the `MockServiceWorker.js` file. See https://mswjs.io/docs/cli/init.
execa.command(`yarn msw init "${staticAssetsFolder}" --no-save`, execaOptions)
await execa.command(
`yarn msw init "${staticAssetsFolder}" --no-save`,
execaOptions
)

const storybookConfigPath = path.dirname(
require.resolve('@redwoodjs/testing/config/storybook/main.js')
)

/** @type {string?} */
let command
let command = ''
const flags = [`--config-dir "${storybookConfigPath}"`]

if (build) {
Expand Down Expand Up @@ -66,10 +70,10 @@ export const handler = ({
}

try {
execa.command(command, execaOptions)
await execa.command(command, execaOptions)
} catch (e) {
console.log(c.error(e.message))
errorTelemetry(process.argv, e.message)
console.log(c.error((e as Error).message))
errorTelemetry(process.argv, (e as Error).message)
process.exit(1)
}
}
17 changes: 17 additions & 0 deletions packages/cli-packages/storybook/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {
command,
aliases,
description,
builder,
handler,
} from './commands/storybook'

export const commands = [
{
command,
aliases,
description,
builder,
handler,
},
]
21 changes: 21 additions & 0 deletions packages/cli-packages/storybook/src/lib/colors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import chalk from 'chalk'

/**
* To keep a consistent color/style palette between cli packages, such as
* \@redwood/cli and \@redwood/create-redwood-app, please keep them compatible
* with one and another. We'll might split up and refactor these into a
* separate package when there is a strong motivation behind it.
*
* Current files:
*
* - packages/cli/src/lib/colors.js (this file)
* - packages/create-redwood-app/src/create-redwood-app.js
*/
export default {
error: chalk.bold.red,
warning: chalk.keyword('orange'),
green: chalk.green,
info: chalk.grey,
bold: chalk.bold,
underline: chalk.underline,
}
19 changes: 19 additions & 0 deletions packages/cli-packages/storybook/src/lib/project.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { memoize } from 'lodash'

import { getPaths as getRedwoodPaths } from '@redwoodjs/project-config'

import c from './colors'

/**
* This wraps the core version of getPaths into something that catches the exception
* and displays a helpful error message.
*/
export const _getPaths = () => {
try {
return getRedwoodPaths()
} catch (e) {
console.error(c.error((e as Error).message))
process.exit(1)
}
}
export const getPaths = memoize(_getPaths)
8 changes: 8 additions & 0 deletions packages/cli-packages/storybook/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export interface StorybookYargsOptions {
open: boolean
build: boolean
ci: boolean
port: number
buildDirectory: string
smokeTest: boolean
}
10 changes: 10 additions & 0 deletions packages/cli-packages/storybook/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "../../../tsconfig.compilerOption.json",
"compilerOptions": {
"baseUrl": ".",
"rootDir": "src",
"tsBuildInfoFile": "dist/tsconfig.tsbuildinfo",
"outDir": "dist"
},
"include": ["src"],
}
2 changes: 0 additions & 2 deletions packages/cli/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import * as prismaCommand from './commands/prisma'
import * as recordCommand from './commands/record'
import * as serveCommand from './commands/serve'
import * as setupCommand from './commands/setup'
import * as storybookCommand from './commands/storybook'
import * as testCommand from './commands/test'
import * as tstojsCommand from './commands/ts-to-js'
import * as typeCheckCommand from './commands/type-check'
Expand Down Expand Up @@ -142,7 +141,6 @@ async function runYargs() {
.command(recordCommand)
.command(serveCommand)
.command(setupCommand)
.command(storybookCommand)
.command(testCommand)
.command(tstojsCommand)
.command(typeCheckCommand)
Expand Down
21 changes: 13 additions & 8 deletions packages/cli/src/lib/packages.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,26 @@ import { getPaths } from './index'

/**
* Installs a module into a user's project. If the module is already installed,
* this function does nothing.
* this function does nothing. If no version is specified, the version will be assumed
* to be the same as that of \@redwoodjs/cli.
*
* @param {string} name The name of the module to install
* @param {string} version The version of the module to install
* @param {string} version The version of the module to install, otherwise the same as that of \@redwoodjs/cli
* @param {boolean} isDevDependency Whether to install as a devDependency or not
* @returns Whether the module was installed or not
*/
export async function installModule(name, version, isDevDependency = true) {
export async function installModule(name, version = undefined) {
if (isModuleInstalled(name)) {
return false
}
await execa.command(
`yarn add ${isDevDependency ? '-D' : ''} ${name}@${version}`,
{
if (version === undefined) {
return await installRedwoodModule(name)
} else {
await execa.command(`yarn add -D ${name}@${version}`, {
stdio: 'inherit',
cwd: getPaths().base,
}
)
})
}
return true
}

Expand All @@ -34,6 +36,7 @@ export async function installModule(name, version, isDevDependency = true) {
* If no remote version can not be found which matches the local cli version then the latest canary version will be used.
*
* @param {string} module A redwoodjs module, e.g. \@redwoodjs/web
* @returns {boolean} Whether the module was installed or not
*/
export async function installRedwoodModule(module) {
const packageJsonPath = require.resolve('@redwoodjs/cli/package.json')
Expand Down Expand Up @@ -63,7 +66,9 @@ export async function installRedwoodModule(module) {
stdio: 'inherit',
cwd: getPaths().base,
})
return true
}
return false
}

/**
Expand Down
Loading