diff --git a/docs/contribute/code.mdx b/docs/contribute/code.mdx index c02ba28efbac..fe23efbcb59a 100644 --- a/docs/contribute/code.mdx +++ b/docs/contribute/code.mdx @@ -102,6 +102,19 @@ Otherwise, if it affects the `Manager` (the outermost Storybook `iframe` where t ![Storybook manager preview](../_assets/addons/manager-preview.png) +The `yarn build` commands accepts arguments to help speed up your development workflow: +* `--all` will cause all packages to be built +* `--watch` will enable watch mode (and skip the watch mode prompt) +* `--prod` will build for production (and skip the production mode prompt) +* individual package names can be passed, without the `@storybook/` prefix, e.g. `storybook`, `addon-docs`, etc. + +For example, to build Storybook and the docs addon in watch mode, run: + +```shell +yarn build --watch storybook addon-docs +``` + + ## Check your work When you're done coding, add documentation and tests as appropriate. That simplifies the PR review process, which means your code will get merged faster. diff --git a/scripts/build-package.ts b/scripts/build-package.ts index 6f25888a42d0..39ec50244a95 100644 --- a/scripts/build-package.ts +++ b/scripts/build-package.ts @@ -5,9 +5,9 @@ * * You can pass a list of package names to build, or use the `--all` flag to build all packages. * - * You can also pass the `--watch` flag to build in watch mode. + * Pass the `--watch` flag to build in watch mode or `--no-watch` to skip the watch mode prompt. * - * You can also pass the `--prod` flag to build in production mode. + * Pass the `--prod` flag to build in production mode or `--no-prod` to skip the production prompt. * * When you pass no package names, you will be prompted to select which packages to build. */ @@ -25,8 +25,20 @@ import { findMostMatchText } from './utils/diff'; import { getCodeWorkspaces } from './utils/workspace'; async function run() { - const packages = (await getCodeWorkspaces()).filter(({ name }) => name !== '@storybook/code'); - const packageTasks = packages + const packages = (await getCodeWorkspaces()) + .filter(({ name }) => name !== '@storybook/code') + .sort((a) => (a.name === 'storybook' ? -1 : 0)); // Place main package first in option list + + const tasks: Record< + string, + { + name: string; + defaultValue: boolean; + suffix: string; + value?: unknown; + location?: string; + } + > = packages .map((pkg) => { let suffix = pkg.name.replace('@storybook/', ''); if (pkg.name === '@storybook/cli') { @@ -36,7 +48,6 @@ async function run() { ...pkg, suffix, defaultValue: false, - helpText: `build only the ${pkg.name} package`, }; }) .reduce( @@ -44,86 +55,59 @@ async function run() { acc[next.name] = next; return acc; }, - {} as Record< - string, - { name: string; defaultValue: boolean; suffix: string; helpText: string } - > + {} as Record ); - const tasks: Record< - string, - { - name: string; - defaultValue: boolean; - suffix: string; - helpText: string; - value?: any; - location?: string; - } - > = { - watch: { - name: `watch`, - defaultValue: false, - suffix: '--watch', - helpText: 'build on watch mode', - }, - prod: { - name: `prod`, - defaultValue: false, - suffix: '--prod', - helpText: 'build on production mode', - }, - ...packageTasks, - }; - const main = program .version('5.0.0') - .option('--all', `build everything ${picocolors.gray('(all)')}`); + .allowExcessArguments(true) + .option('--all', `build everything ${picocolors.gray('(all)')}`, false) + .option('--watch', `build in watch mode`) + .option('--prod', `build in production mode`) + .option('--no-watch', `do not build in watch mode`) + .option('--no-prod', `do not build in production mode`); + + main.parse(process.argv); - Object.keys(tasks) - .reduce((acc, key) => acc.option(tasks[key].suffix, tasks[key].helpText), main) - .parse(process.argv); + const opts = main.opts(); + let watchMode = opts.watch; + let prodMode = opts.prod; Object.keys(tasks).forEach((key) => { - const opts = program.opts(); - // checks if a flag is passed e.g. yarn build --@storybook/addon-docs --watch - const containsFlag = program.args.includes(tasks[key].suffix); + // checks if a flag is passed e.g. yarn build addon-docs --watch + const containsFlag = main.args.includes(tasks[key].suffix); tasks[key].value = containsFlag || opts.all; }); - let watchMode = process.argv.includes('--watch'); - let prodMode = process.argv.includes('--prod'); - let selection = Object.keys(tasks) - .map((key) => tasks[key]) - .filter((item) => !['watch', 'prod'].includes(item.name) && item.value === true); - - // user has passed invalid package name(s) - try to guess the correct package name(s) - if ((!selection.length && main.args.length >= 1) || selection.length !== main.args.length) { - const suffixList = Object.values(tasks) - .filter((t) => t.name.includes('@storybook')) - .map((t) => t.suffix); - - for (const arg of main.args) { - if (!suffixList.includes(arg)) { - const matchText = findMostMatchText(suffixList, arg); - - if (matchText) { - console.log( - `${picocolors.red('Error')}: ${picocolors.cyan( - arg - )} is not a valid package name, Did you mean ${picocolors.cyan(matchText)}?` - ); - } + let selection = Object.values(tasks).filter((item) => item.value === true); + + // check for invalid package name(s) and try to guess the correct package name(s) + const suffixList = Object.values(tasks).map((t) => t.suffix); + let hasInvalidName = false; + + for (const arg of main.args) { + if (!suffixList.includes(arg)) { + const matchText = findMostMatchText(suffixList, arg); + + if (matchText) { + hasInvalidName = true; + process.stderr.write( + `${picocolors.red('Error')}: ${picocolors.cyan( + arg + )} is not a valid package name, Did you mean ${picocolors.cyan(matchText)}?\n` + ); } } + } - process.exit(0); + if (hasInvalidName) { + process.exit(1); } if (!selection.length) { selection = await prompts( [ - { + watchMode === undefined && { type: 'toggle', name: 'watch', message: 'Start in watch mode', @@ -131,7 +115,7 @@ async function run() { active: 'yes', inactive: 'no', }, - { + prodMode === undefined && { type: 'toggle', name: 'prod', message: 'Start in production mode', @@ -144,7 +128,7 @@ async function run() { message: 'Select the packages to build', name: 'todo', min: 1, - hint: 'You can also run directly with package name like `yarn build core`, or `yarn build --all` for all packages!', + hint: 'You can also run directly with package name like `yarn build storybook`, or `yarn build --all` for all packages!', // @ts-expect-error @types incomplete optionsPerPage: windowSize.height - 3, // 3 lines for extra info choices: packages.map(({ name: key }) => ({ @@ -162,7 +146,7 @@ async function run() { }); } - console.log('Building selected packages...'); + process.stdout.write('Building selected packages...\n'); let lastName = ''; selection.forEach(async (v) => {