From 18aab371c4e2ccfa4bb668328f2234b9d80d61dd Mon Sep 17 00:00:00 2001 From: Yann Braga Date: Mon, 28 Mar 2022 17:53:26 +0200 Subject: [PATCH 1/5] feat: make manager and preview build processes cancelable --- lib/builder-webpack4/src/index.ts | 107 +++++++++++++++++++++----- lib/builder-webpack5/src/index.ts | 103 +++++++++++++++++++++---- lib/core-server/src/build-static.ts | 12 ++- lib/core-server/src/dev-server.ts | 10 ++- lib/manager-webpack4/src/index.ts | 113 +++++++++++++++++++++++----- lib/manager-webpack5/src/index.ts | 108 +++++++++++++++++++++----- 6 files changed, 381 insertions(+), 72 deletions(-) diff --git a/lib/builder-webpack4/src/index.ts b/lib/builder-webpack4/src/index.ts index 2462012e12bf..855fbbad5846 100644 --- a/lib/builder-webpack4/src/index.ts +++ b/lib/builder-webpack4/src/index.ts @@ -11,6 +11,19 @@ let compilation: ReturnType; let reject: (reason?: any) => void; type WebpackBuilder = Builder; +type Unpromise> = T extends Promise ? U : never; + +type BuilderStartOptions = Partial['0']>; +type BuilderStartResult = Unpromise>; +type StarterFunction = ( + options: BuilderStartOptions +) => AsyncGenerator; + +type BuilderBuildOptions = Partial['0']>; +type BuilderBuildResult = Unpromise>; +type BuilderFunction = ( + options: BuilderBuildOptions +) => AsyncGenerator; export const executor = { get: async (options: Options) => { @@ -48,11 +61,50 @@ export const makeStatsFromError: (err: string) => Stats = (err) => toJson: () => ({ warnings: [] as any[], errors: [err] }), } as any); -export const start: WebpackBuilder['start'] = async ({ startTime, options, router }) => { +let asyncIterator: ReturnType | ReturnType; + +export const bail: WebpackBuilder['bail'] = async () => { + if (asyncIterator) { + // we tell the builder (that started) to stop ASAP and wait + await asyncIterator.throw(new Error()).catch(() => {}); + } + if (reject) { + reject(); + } + // we wait for the compiler to finish it's work, so it's command-line output doesn't interfere + return new Promise((res, rej) => { + if (process && compilation) { + try { + compilation.close(() => res()); + logger.warn('Force closed preview build'); + } catch (err) { + logger.warn('Unable to close preview build!'); + res(); + } + } else { + res(); + } + }); +}; + +/** + * This function is a generator so that we can abort it mid process + * in case of failure coming from other processes e.g. manager builder + * + * I am sorry for making you read about generators today :') + */ +const starter: StarterFunction = async function* starterGeneratorFn({ + startTime, + options, + router, +}) { const webpackInstance = await executor.get(options); + yield; const config = await getConfig(options); + yield; const compiler = webpackInstance(config); + if (!compiler) { const err = `${config.name}: missing webpack compiler at runtime!`; logger.error(err); @@ -64,6 +116,7 @@ export const start: WebpackBuilder['start'] = async ({ startTime, options, route } const { handler, modulesCount } = await useProgressReporting(router, startTime, options); + yield; new ProgressPlugin({ handler, modulesCount }).apply(compiler); const middlewareOptions: Parameters[1] = { @@ -86,6 +139,7 @@ export const start: WebpackBuilder['start'] = async ({ startTime, options, route waitUntilValid(ready); reject = stop; }); + yield; if (!stats) { throw new Error('no stats after building preview'); @@ -102,26 +156,18 @@ export const start: WebpackBuilder['start'] = async ({ startTime, options, route }; }; -export const bail: WebpackBuilder['bail'] = (e: Error) => { - if (reject) { - reject(); - } - if (process) { - try { - compilation.close(); - logger.warn('Force closed preview build'); - } catch (err) { - logger.warn('Unable to close preview build!'); - } - } - throw e; -}; - -export const build: WebpackBuilder['build'] = async ({ options, startTime }) => { +/** + * This function is a generator so that we can abort it mid process + * in case of failure coming from other processes e.g. manager builder + * + * I am sorry for making you read about generators today :') + */ +const builder: BuilderFunction = async function* builderGeneratorFn({ startTime, options }) { const webpackInstance = await executor.get(options); - + yield; logger.info('=> Compiling preview..'); const config = await getConfig(options); + yield; const compiler = webpackInstance(config); if (!compiler) { @@ -129,6 +175,7 @@ export const build: WebpackBuilder['build'] = async ({ options, startTime }) => logger.error(err); return Promise.resolve(makeStatsFromError(err)); } + yield; return new Promise((succeed, fail) => { compiler.run((error, stats) => { @@ -160,5 +207,29 @@ export const build: WebpackBuilder['build'] = async ({ options, startTime }) => }); }; +export const start = async (options: BuilderStartOptions) => { + asyncIterator = starter(options); + let result; + + do { + // eslint-disable-next-line no-await-in-loop + result = await asyncIterator.next(); + } while (!result.done); + + return result.value; +}; + +export const build = async (options: BuilderStartOptions) => { + asyncIterator = builder(options); + let result; + + do { + // eslint-disable-next-line no-await-in-loop + result = await asyncIterator.next(); + } while (!result.done); + + return result.value; +}; + export const corePresets = [require.resolve('./presets/preview-preset.js')]; export const overridePresets = [require.resolve('./presets/custom-webpack-preset.js')]; diff --git a/lib/builder-webpack5/src/index.ts b/lib/builder-webpack5/src/index.ts index 522c69c060d8..0be082cf1c9e 100644 --- a/lib/builder-webpack5/src/index.ts +++ b/lib/builder-webpack5/src/index.ts @@ -9,6 +9,19 @@ let compilation: ReturnType; let reject: (reason?: any) => void; type WebpackBuilder = Builder; +type Unpromise> = T extends Promise ? U : never; + +type BuilderStartOptions = Partial['0']>; +type BuilderStartResult = Unpromise>; +type StarterFunction = ( + options: BuilderStartOptions +) => AsyncGenerator; + +type BuilderBuildOptions = Partial['0']>; +type BuilderBuildResult = Unpromise>; +type BuilderFunction = ( + options: BuilderBuildOptions +) => AsyncGenerator; export const getConfig: WebpackBuilder['getConfig'] = async (options) => { const { presets } = options; @@ -39,11 +52,51 @@ export const executor = { }, }; -export const start: WebpackBuilder['start'] = async ({ startTime, options, router }) => { +let asyncIterator: ReturnType | ReturnType; + +export const bail: WebpackBuilder['bail'] = async () => { + if (asyncIterator) { + // we tell the builder (that started) to stop ASAP and wait + await asyncIterator.throw(new Error()).catch(() => {}); + } + + if (reject) { + reject(); + } + // we wait for the compiler to finish it's work, so it's command-line output doesn't interfere + return new Promise((res, rej) => { + if (process && compilation) { + try { + compilation.close(() => res()); + logger.warn('Force closed preview build'); + } catch (err) { + logger.warn('Unable to close preview build!'); + res(); + } + } else { + res(); + } + }); +}; + +/** + * This function is a generator so that we can abort it mid process + * in case of failure coming from other processes e.g. preview builder + * + * I am sorry for making you read about generators today :') + */ +const starter: StarterFunction = async function* starterGeneratorFn({ + startTime, + options, + router, +}) { const webpackInstance = await executor.get(options); + yield; const config = await getConfig(options); + yield; const compiler = webpackInstance(config); + if (!compiler) { const err = `${config.name}: missing webpack compiler at runtime!`; logger.error(err); @@ -59,6 +112,7 @@ export const start: WebpackBuilder['start'] = async ({ startTime, options, route } const { handler, modulesCount } = await useProgressReporting(router, startTime, options); + yield; new ProgressPlugin({ handler, modulesCount }).apply(compiler); const middlewareOptions: Parameters[1] = { @@ -75,6 +129,7 @@ export const start: WebpackBuilder['start'] = async ({ startTime, options, route compilation.waitUntilValid(ready); reject = stop; }); + yield; if (!stats) { throw new Error('no stats after building preview'); @@ -91,26 +146,30 @@ export const start: WebpackBuilder['start'] = async ({ startTime, options, route }; }; -export const bail: WebpackBuilder['bail'] = (e: Error) => { - if (reject) { - reject(); - } - if (process) { - try { - compilation.close(); - logger.warn('Force closed preview build'); - } catch (err) { - logger.warn('Unable to close preview build!'); - } - } - throw e; +export const start = async (options: BuilderStartOptions) => { + asyncIterator = starter(options); + let result; + + do { + // eslint-disable-next-line no-await-in-loop + result = await asyncIterator.next(); + } while (!result.done); + + return result.value; }; -export const build: WebpackBuilder['build'] = async ({ options, startTime }) => { +/** + * This function is a generator so that we can abort it mid process + * in case of failure coming from other processes e.g. manager builder + * + * I am sorry for making you read about generators today :') + */ +const builder: BuilderFunction = async function* builderGeneratorFn({ startTime, options }) { const webpackInstance = await executor.get(options); - + yield; logger.info('=> Compiling preview..'); const config = await getConfig(options); + yield; return new Promise((succeed, fail) => { const compiler = webpackInstance(config); @@ -162,5 +221,17 @@ export const build: WebpackBuilder['build'] = async ({ options, startTime }) => }); }; +export const build = async (options: BuilderStartOptions) => { + asyncIterator = builder(options); + let result; + + do { + // eslint-disable-next-line no-await-in-loop + result = await asyncIterator.next(); + } while (!result.done); + + return result.value; +}; + export const corePresets = [require.resolve('./presets/preview-preset.js')]; export const overridePresets = [require.resolve('./presets/custom-webpack-preset.js')]; diff --git a/lib/core-server/src/build-static.ts b/lib/core-server/src/build-static.ts index f5dbd8bc1cda..ba7eae31ac8e 100644 --- a/lib/core-server/src/build-static.ts +++ b/lib/core-server/src/build-static.ts @@ -18,6 +18,7 @@ import type { } from '@storybook/core-common'; import { loadAllPresets, cache, normalizeStories, logConfig } from '@storybook/core-common'; +import prompts from 'prompts'; import { getProdCli } from './cli'; import { outputStats } from './utils/output-stats'; import { @@ -143,7 +144,16 @@ export async function buildStaticStandalone(options: CLIOptions & LoadOptions & options: fullOptions, }); - const [managerStats, previewStats] = await Promise.all([manager, preview]); + const [managerStats, previewStats] = await Promise.all([ + manager.catch(async (err) => { + await previewBuilder.bail(); + throw err; + }), + preview.catch(async (err) => { + await managerBuilder.bail(); + throw err; + }), + ]); if (options.webpackStatsJson) { const target = options.webpackStatsJson === true ? options.outputDir : options.webpackStatsJson; diff --git a/lib/core-server/src/dev-server.ts b/lib/core-server/src/dev-server.ts index 136657833aff..d4cff5e3fdb6 100644 --- a/lib/core-server/src/dev-server.ts +++ b/lib/core-server/src/dev-server.ts @@ -86,14 +86,20 @@ export async function storybookDevServer(options: Options) { }); const [previewResult, managerResult] = await Promise.all([ - preview, + preview.catch(async (err) => { + await managerBuilder.bail(); + throw err; + }), manager // TODO #13083 Restore this when compiling the preview is fast enough // .then((result) => { // if (!options.ci && !options.smokeTest) openInBrowser(address); // return result; // }) - .catch(previewBuilder.bail), + .catch(async (err) => { + await previewBuilder.bail(); + throw err; + }), ]); // TODO #13083 Remove this when compiling the preview is fast enough diff --git a/lib/manager-webpack4/src/index.ts b/lib/manager-webpack4/src/index.ts index 32d09a906ad0..6b6a19c4813a 100644 --- a/lib/manager-webpack4/src/index.ts +++ b/lib/manager-webpack4/src/index.ts @@ -16,6 +16,19 @@ let compilation: ReturnType; let reject: (reason?: any) => void; type WebpackBuilder = Builder; +type Unpromise> = T extends Promise ? U : never; + +type BuilderStartOptions = Partial['0']>; +type BuilderStartResult = Unpromise>; +type StarterFunction = ( + options: BuilderStartOptions +) => AsyncGenerator; + +type BuilderBuildOptions = Partial['0']>; +type BuilderBuildResult = Unpromise>; +type BuilderFunction = ( + options: BuilderBuildOptions +) => AsyncGenerator; export const WEBPACK_VERSION = '4'; @@ -39,19 +52,60 @@ export const executor = { }, }; -export const start: WebpackBuilder['start'] = async ({ startTime, options, router }) => { +let asyncIterator: ReturnType | ReturnType; + +export const bail: WebpackBuilder['bail'] = async () => { + if (asyncIterator) { + // we tell the builder (that started) to stop ASAP and wait + await asyncIterator.throw(new Error()).catch(() => {}); + } + if (reject) { + reject(); + } + // we wait for the compiler to finish it's work, so it's command-line output doesn't interfere + return new Promise((res, rej) => { + if (process && compilation) { + try { + compilation.close(() => res()); + logger.warn('Force closed manager build'); + } catch (err) { + logger.warn('Unable to close manager build!'); + res(); + } + } else { + res(); + } + }); +}; + +/** + * This function is a generator so that we can abort it mid process + * in case of failure coming from other processes e.g. preview builder + * + * I am sorry for making you read about generators today :') + */ +const starter: StarterFunction = async function* starterGeneratorFn({ + startTime, + options, + router, +}) { const prebuiltDir = await getPrebuiltDir(options); if (prebuiltDir && options.managerCache && !options.smokeTest) { logger.info('=> Using prebuilt manager'); router.use('/', express.static(prebuiltDir)); return; } + yield; const config = await getConfig(options); + yield; + if (options.cache) { // Retrieve the Storybook version number to bust cache on upgrades. const packageFile = await findUp('package.json', { cwd: __dirname }); + yield; const { version: storybookVersion } = await fs.readJSON(packageFile); + yield; const cacheKey = `managerConfig-webpack${WEBPACK_VERSION}@${storybookVersion}`; if (options.managerCache) { @@ -61,6 +115,7 @@ export const start: WebpackBuilder['start'] = async ({ startTime, options, route useManagerCache(cacheKey, options, config), fs.pathExists(options.outputDir), ]); + yield; if (useCache && hasOutput && !options.smokeTest) { logger.line(1); // force starting new line logger.info('=> Using cached manager'); @@ -68,13 +123,15 @@ export const start: WebpackBuilder['start'] = async ({ startTime, options, route return; } } else if (!options.smokeTest && (await clearManagerCache(cacheKey, options))) { + yield; logger.line(1); // force starting new line logger.info('=> Cleared cached manager config'); } } const webpackInstance = await executor.get(options); - const compiler = (webpackInstance as any)(config); + yield; + const compiler = webpackInstance(config); if (!compiler) { const err = `${config.name}: missing webpack compiler at runtime!`; @@ -88,6 +145,7 @@ export const start: WebpackBuilder['start'] = async ({ startTime, options, route } const { handler, modulesCount } = await useProgressReporting(router, startTime, options); + yield; new ProgressPlugin({ handler, modulesCount }).apply(compiler); const middlewareOptions: Parameters[1] = { @@ -104,9 +162,10 @@ export const start: WebpackBuilder['start'] = async ({ startTime, options, route compilation.waitUntilValid(ready); reject = stop; }); + yield; if (!stats) { - throw new Error('no stats after building preview'); + throw new Error('no stats after building manager'); } // eslint-disable-next-line consistent-return @@ -117,26 +176,31 @@ export const start: WebpackBuilder['start'] = async ({ startTime, options, route }; }; -export const bail: WebpackBuilder['bail'] = (e: Error) => { - if (reject) { - reject(); - } - if (process) { - try { - compilation.close(); - logger.warn('Force closed preview build'); - } catch (err) { - logger.warn('Unable to close preview build!'); - } - } - throw e; +export const start = async (options: BuilderStartOptions) => { + asyncIterator = starter(options); + let result; + + do { + // eslint-disable-next-line no-await-in-loop + result = await asyncIterator.next(); + } while (!result.done); + + return result.value; }; -export const build: WebpackBuilder['build'] = async ({ options, startTime }) => { +/** + * This function is a generator so that we can abort it mid process + * in case of failure coming from other processes e.g. preview builder + * + * I am sorry for making you read about generators today :') + */ +const builder: BuilderFunction = async function* builderGeneratorFn({ startTime, options }) { logger.info('=> Compiling manager..'); const webpackInstance = await executor.get(options); - + yield; const config = await getConfig(options); + yield; + const statsOptions = typeof config.stats === 'boolean' ? 'minimal' : config.stats; const compiler = webpackInstance(config); @@ -145,6 +209,7 @@ export const build: WebpackBuilder['build'] = async ({ options, startTime }) => logger.error(err); return Promise.resolve(makeStatsFromError(err)); } + yield; return new Promise((succeed, fail) => { compiler.run((error, stats) => { @@ -177,6 +242,18 @@ export const build: WebpackBuilder['build'] = async ({ options, startTime }) => }); }; +export const build = async (options: BuilderStartOptions) => { + asyncIterator = builder(options); + let result; + + do { + // eslint-disable-next-line no-await-in-loop + result = await asyncIterator.next(); + } while (!result.done); + + return result.value; +}; + export const corePresets: WebpackBuilder['corePresets'] = [ require.resolve('./presets/manager-preset'), ]; diff --git a/lib/manager-webpack5/src/index.ts b/lib/manager-webpack5/src/index.ts index 11e2fbeefe93..36447b67820b 100644 --- a/lib/manager-webpack5/src/index.ts +++ b/lib/manager-webpack5/src/index.ts @@ -16,6 +16,19 @@ let compilation: ReturnType; let reject: (reason?: any) => void; type WebpackBuilder = Builder; +type Unpromise> = T extends Promise ? U : never; + +type BuilderStartOptions = Partial['0']>; +type BuilderStartResult = Unpromise>; +type StarterFunction = ( + options: BuilderStartOptions +) => AsyncGenerator; + +type BuilderBuildOptions = Partial['0']>; +type BuilderBuildResult = Unpromise>; +type BuilderFunction = ( + options: BuilderBuildOptions +) => AsyncGenerator; export const WEBPACK_VERSION = '5'; @@ -39,15 +52,54 @@ export const executor = { }, }; -export const start: WebpackBuilder['start'] = async ({ startTime, options, router }) => { +let asyncIterator: ReturnType | ReturnType; + +export const bail: WebpackBuilder['bail'] = async () => { + if (asyncIterator) { + // we tell the builder (that started) to stop ASAP and wait + await asyncIterator.throw(new Error()).catch(() => {}); + } + if (reject) { + reject(); + } + // we wait for the compiler to finish it's work, so it's command-line output doesn't interfere + return new Promise((res, rej) => { + if (process && compilation) { + try { + compilation.close(() => res()); + logger.warn('Force closed manager build'); + } catch (err) { + logger.warn('Unable to close manager build!'); + res(); + } + } else { + res(); + } + }); +}; + +/** + * This function is a generator so that we can abort it mid process + * in case of failure coming from other processes e.g. preview builder + * + * I am sorry for making you read about generators today :') + */ +const starter: StarterFunction = async function* starterGeneratorFn({ + startTime, + options, + router, +}) { const prebuiltDir = await getPrebuiltDir(options); if (prebuiltDir && options.managerCache && !options.smokeTest) { logger.info('=> Using prebuilt manager'); router.use('/', express.static(prebuiltDir)); return; } + yield; const config = await getConfig(options); + yield; + if (options.cache) { // Retrieve the Storybook version number to bust cache on upgrades. const packageFile = await findUp('package.json', { cwd: __dirname }); @@ -61,6 +113,7 @@ export const start: WebpackBuilder['start'] = async ({ startTime, options, route useManagerCache(cacheKey, options, config), fs.pathExists(options.outputDir), ]); + yield; if (useCache && hasOutput && !options.smokeTest) { logger.line(1); // force starting new line logger.info('=> Using cached manager'); @@ -68,12 +121,14 @@ export const start: WebpackBuilder['start'] = async ({ startTime, options, route return; } } else if (!options.smokeTest && (await clearManagerCache(cacheKey, options))) { + yield; logger.line(1); // force starting new line logger.info('=> Cleared cached manager config'); } } const webpackInstance = await executor.get(options); + yield; const compiler = (webpackInstance as any)(config); if (!compiler) { @@ -88,6 +143,7 @@ export const start: WebpackBuilder['start'] = async ({ startTime, options, route } const { handler, modulesCount } = await useProgressReporting(router, startTime, options); + yield; new ProgressPlugin({ handler, modulesCount }).apply(compiler); const middlewareOptions: Parameters[1] = { @@ -103,9 +159,10 @@ export const start: WebpackBuilder['start'] = async ({ startTime, options, route compilation.waitUntilValid(ready); reject = stop; }); + yield; if (!stats) { - throw new Error('no stats after building preview'); + throw new Error('no stats after building manager'); } // eslint-disable-next-line consistent-return @@ -116,26 +173,30 @@ export const start: WebpackBuilder['start'] = async ({ startTime, options, route }; }; -export const bail: WebpackBuilder['bail'] = (e: Error) => { - if (reject) { - reject(); - } - if (process) { - try { - compilation.close(); - logger.warn('Force closed preview build'); - } catch (err) { - logger.warn('Unable to close preview build!'); - } - } - throw e; +export const start = async (options: BuilderStartOptions) => { + asyncIterator = starter(options); + let result; + + do { + // eslint-disable-next-line no-await-in-loop + result = await asyncIterator.next(); + } while (!result.done); + + return result.value; }; -export const build: WebpackBuilder['build'] = async ({ options, startTime }) => { +/** + * This function is a generator so that we can abort it mid process + * in case of failure coming from other processes e.g. preview builder + * + * I am sorry for making you read about generators today :') + */ +const builder: BuilderFunction = async function* builderGeneratorFn({ startTime, options }) { logger.info('=> Compiling manager..'); const webpackInstance = await executor.get(options); - + yield; const config = await getConfig(options); + yield; const compiler = webpackInstance(config); if (!compiler) { @@ -143,6 +204,7 @@ export const build: WebpackBuilder['build'] = async ({ options, startTime }) => logger.error(err); return Promise.resolve(makeStatsFromError(err)); } + yield; return new Promise((succeed, fail) => { compiler.run((error, stats) => { @@ -174,6 +236,18 @@ export const build: WebpackBuilder['build'] = async ({ options, startTime }) => }); }; +export const build = async (options: BuilderStartOptions) => { + asyncIterator = builder(options); + let result; + + do { + // eslint-disable-next-line no-await-in-loop + result = await asyncIterator.next(); + } while (!result.done); + + return result.value; +}; + export const corePresets: WebpackBuilder['corePresets'] = [ require.resolve('./presets/manager-preset'), ]; From bb4b04d945a4eb32c543bd12d2636c8df6b04776 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Wed, 30 Mar 2022 12:58:51 +0200 Subject: [PATCH 2/5] fix typescript issues --- lib/builder-webpack4/src/index.ts | 29 +++++++++---- lib/builder-webpack5/src/index.ts | 70 ++++++++++++++++++------------- lib/manager-webpack4/src/index.ts | 10 +++-- lib/manager-webpack5/src/index.ts | 10 +++-- 4 files changed, 77 insertions(+), 42 deletions(-) diff --git a/lib/builder-webpack4/src/index.ts b/lib/builder-webpack4/src/index.ts index 855fbbad5846..db89a415581d 100644 --- a/lib/builder-webpack4/src/index.ts +++ b/lib/builder-webpack4/src/index.ts @@ -61,12 +61,16 @@ export const makeStatsFromError: (err: string) => Stats = (err) => toJson: () => ({ warnings: [] as any[], errors: [err] }), } as any); -let asyncIterator: ReturnType | ReturnType; +let asyncIterator: ReturnType | ReturnType; export const bail: WebpackBuilder['bail'] = async () => { if (asyncIterator) { - // we tell the builder (that started) to stop ASAP and wait - await asyncIterator.throw(new Error()).catch(() => {}); + try { + // we tell the builder (that started) to stop ASAP and wait + await asyncIterator.throw(new Error()); + } catch (e) { + // + } } if (reject) { reject(); @@ -177,7 +181,7 @@ const builder: BuilderFunction = async function* builderGeneratorFn({ startTime, } yield; - return new Promise((succeed, fail) => { + return new Promise((succeed, fail) => { compiler.run((error, stats) => { if (error || !stats || stats.hasErrors()) { logger.error('=> Failed to build the preview'); @@ -189,11 +193,22 @@ const builder: BuilderFunction = async function* builderGeneratorFn({ startTime, } if (stats && (stats.hasErrors() || stats.hasWarnings())) { - const { warnings, errors } = stats.toJson(config.stats); + const { warnings = [], errors = [] } = stats.toJson( + typeof config.stats === 'string' + ? config.stats + : { + warnings: true, + errors: true, + ...(config.stats as Stats.ToStringOptionsObject), + } + ); errors.forEach((e: string) => logger.error(e)); warnings.forEach((e: string) => logger.error(e)); - return fail(stats); + + return options.debugWebpack + ? fail(stats) + : fail(new Error('=> Webpack failed, learn more with --debug-webpack')); } } @@ -202,7 +217,7 @@ const builder: BuilderFunction = async function* builderGeneratorFn({ startTime, stats.toJson(config.stats).warnings.forEach((e: string) => logger.warn(e)); } - return succeed(stats); + return succeed(stats as webpackReal.Stats); }); }); }; diff --git a/lib/builder-webpack5/src/index.ts b/lib/builder-webpack5/src/index.ts index 0be082cf1c9e..2d2a84c4646e 100644 --- a/lib/builder-webpack5/src/index.ts +++ b/lib/builder-webpack5/src/index.ts @@ -1,4 +1,4 @@ -import webpack, { Stats, Configuration, ProgressPlugin } from 'webpack'; +import webpack, { Stats, Configuration, ProgressPlugin, StatsOptions } from 'webpack'; import webpackDevMiddleware from 'webpack-dev-middleware'; import webpackHotMiddleware from 'webpack-hot-middleware'; import { logger } from '@storybook/node-logger'; @@ -21,7 +21,18 @@ type BuilderBuildOptions = Partial['0']>; type BuilderBuildResult = Unpromise>; type BuilderFunction = ( options: BuilderBuildOptions -) => AsyncGenerator; +) => AsyncGenerator; + +export const executor = { + get: async (options: Options) => { + const version = ((await options.presets.apply('webpackVersion')) || '5') as string; + const webpackInstance = + (await options.presets.apply<{ default: typeof webpack }>('webpackInstance'))?.default || + webpack; + checkWebpackVersion({ version }, '5', 'builder-webpack5'); + return webpackInstance; + }, +}; export const getConfig: WebpackBuilder['getConfig'] = async (options) => { const { presets } = options; @@ -41,23 +52,16 @@ export const getConfig: WebpackBuilder['getConfig'] = async (options) => { ) as any; }; -export const executor = { - get: async (options: Options) => { - const version = ((await options.presets.apply('webpackVersion')) || '5') as string; - const webpackInstance = - (await options.presets.apply<{ default: typeof webpack }>('webpackInstance'))?.default || - webpack; - checkWebpackVersion({ version }, '5', 'builder-webpack5'); - return webpackInstance; - }, -}; - let asyncIterator: ReturnType | ReturnType; export const bail: WebpackBuilder['bail'] = async () => { if (asyncIterator) { - // we tell the builder (that started) to stop ASAP and wait - await asyncIterator.throw(new Error()).catch(() => {}); + try { + // we tell the builder (that started) to stop ASAP and wait + await asyncIterator.throw(new Error()); + } catch (e) { + // + } } if (reject) { @@ -146,18 +150,6 @@ const starter: StarterFunction = async function* starterGeneratorFn({ }; }; -export const start = async (options: BuilderStartOptions) => { - asyncIterator = starter(options); - let result; - - do { - // eslint-disable-next-line no-await-in-loop - result = await asyncIterator.next(); - } while (!result.done); - - return result.value; -}; - /** * This function is a generator so that we can abort it mid process * in case of failure coming from other processes e.g. manager builder @@ -171,7 +163,7 @@ const builder: BuilderFunction = async function* builderGeneratorFn({ startTime, const config = await getConfig(options); yield; - return new Promise((succeed, fail) => { + return new Promise((succeed, fail) => { const compiler = webpackInstance(config); compiler.run((error, stats) => { @@ -188,7 +180,15 @@ const builder: BuilderFunction = async function* builderGeneratorFn({ startTime, } if (stats && (stats.hasErrors() || stats.hasWarnings())) { - const { warnings = [], errors = [] } = stats.toJson({ warnings: true, errors: true }); + const { warnings = [], errors = [] } = stats.toJson( + typeof config.stats === 'string' + ? config.stats + : { + warnings: true, + errors: true, + ...(config.stats as StatsOptions), + } + ); errors.forEach((e) => logger.error(e.message)); warnings.forEach((e) => logger.error(e.message)); @@ -221,6 +221,18 @@ const builder: BuilderFunction = async function* builderGeneratorFn({ startTime, }); }; +export const start = async (options: BuilderStartOptions) => { + asyncIterator = starter(options); + let result; + + do { + // eslint-disable-next-line no-await-in-loop + result = await asyncIterator.next(); + } while (!result.done); + + return result.value; +}; + export const build = async (options: BuilderStartOptions) => { asyncIterator = builder(options); let result; diff --git a/lib/manager-webpack4/src/index.ts b/lib/manager-webpack4/src/index.ts index 6b6a19c4813a..bc6fb99b470e 100644 --- a/lib/manager-webpack4/src/index.ts +++ b/lib/manager-webpack4/src/index.ts @@ -56,8 +56,12 @@ let asyncIterator: ReturnType | ReturnType; export const bail: WebpackBuilder['bail'] = async () => { if (asyncIterator) { - // we tell the builder (that started) to stop ASAP and wait - await asyncIterator.throw(new Error()).catch(() => {}); + try { + // we tell the builder (that started) to stop ASAP and wait + await asyncIterator.throw(new Error()); + } catch (e) { + // + } } if (reject) { reject(); @@ -211,7 +215,7 @@ const builder: BuilderFunction = async function* builderGeneratorFn({ startTime, } yield; - return new Promise((succeed, fail) => { + return new Promise((succeed, fail) => { compiler.run((error, stats) => { if (error || !stats || stats.hasErrors()) { logger.error('=> Failed to build the manager'); diff --git a/lib/manager-webpack5/src/index.ts b/lib/manager-webpack5/src/index.ts index 36447b67820b..dfb134545f3b 100644 --- a/lib/manager-webpack5/src/index.ts +++ b/lib/manager-webpack5/src/index.ts @@ -56,8 +56,12 @@ let asyncIterator: ReturnType | ReturnType; export const bail: WebpackBuilder['bail'] = async () => { if (asyncIterator) { - // we tell the builder (that started) to stop ASAP and wait - await asyncIterator.throw(new Error()).catch(() => {}); + try { + // we tell the builder (that started) to stop ASAP and wait + await asyncIterator.throw(new Error()); + } catch (e) { + // + } } if (reject) { reject(); @@ -206,7 +210,7 @@ const builder: BuilderFunction = async function* builderGeneratorFn({ startTime, } yield; - return new Promise((succeed, fail) => { + return new Promise((succeed, fail) => { compiler.run((error, stats) => { if (error || !stats || stats.hasErrors()) { logger.error('=> Failed to build the manager'); From 6b91d4167c09382a0f3eb5c0f4cb9a7d3ae0e3fa Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Wed, 30 Mar 2022 13:02:52 +0200 Subject: [PATCH 3/5] cleanup --- lib/core-server/src/build-static.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/core-server/src/build-static.ts b/lib/core-server/src/build-static.ts index ba7eae31ac8e..c59532ecd660 100644 --- a/lib/core-server/src/build-static.ts +++ b/lib/core-server/src/build-static.ts @@ -18,7 +18,6 @@ import type { } from '@storybook/core-common'; import { loadAllPresets, cache, normalizeStories, logConfig } from '@storybook/core-common'; -import prompts from 'prompts'; import { getProdCli } from './cli'; import { outputStats } from './utils/output-stats'; import { From f264b5d2c2ee4794e25181b36a1b622c9ba441a0 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Tue, 5 Apr 2022 10:50:21 +0200 Subject: [PATCH 4/5] undo changes to versions.ts --- lib/cli/src/versions.ts | 114 ++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/lib/cli/src/versions.ts b/lib/cli/src/versions.ts index 83ed2b01d0d8..248588037929 100644 --- a/lib/cli/src/versions.ts +++ b/lib/cli/src/versions.ts @@ -1,59 +1,59 @@ // auto generated file, do not edit export default { - '@storybook/addon-a11y': '6.5.0-alpha.55', - '@storybook/addon-actions': '6.5.0-alpha.55', - '@storybook/addon-backgrounds': '6.5.0-alpha.55', - '@storybook/addon-controls': '6.5.0-alpha.55', - '@storybook/addon-docs': '6.5.0-alpha.55', - '@storybook/addon-essentials': '6.5.0-alpha.55', - '@storybook/addon-interactions': '6.5.0-alpha.55', - '@storybook/addon-jest': '6.5.0-alpha.55', - '@storybook/addon-links': '6.5.0-alpha.55', - '@storybook/addon-measure': '6.5.0-alpha.55', - '@storybook/addon-outline': '6.5.0-alpha.55', - '@storybook/addon-storyshots': '6.5.0-alpha.55', - '@storybook/addon-storyshots-puppeteer': '6.5.0-alpha.55', - '@storybook/addon-storysource': '6.5.0-alpha.55', - '@storybook/addon-toolbars': '6.5.0-alpha.55', - '@storybook/addon-viewport': '6.5.0-alpha.55', - '@storybook/addons': '6.5.0-alpha.55', - '@storybook/angular': '6.5.0-alpha.55', - '@storybook/api': '6.5.0-alpha.55', - '@storybook/builder-webpack4': '6.5.0-alpha.55', - '@storybook/builder-webpack5': '6.5.0-alpha.55', - '@storybook/channel-postmessage': '6.5.0-alpha.55', - '@storybook/channel-websocket': '6.5.0-alpha.55', - '@storybook/channels': '6.5.0-alpha.55', - '@storybook/cli': '6.5.0-alpha.55', - '@storybook/client-api': '6.5.0-alpha.55', - '@storybook/client-logger': '6.5.0-alpha.55', - '@storybook/codemod': '6.5.0-alpha.55', - '@storybook/components': '6.5.0-alpha.55', - '@storybook/core': '6.5.0-alpha.55', - '@storybook/core-client': '6.5.0-alpha.55', - '@storybook/core-common': '6.5.0-alpha.55', - '@storybook/core-events': '6.5.0-alpha.55', - '@storybook/core-server': '6.5.0-alpha.55', - '@storybook/csf-tools': '6.5.0-alpha.55', - '@storybook/docs-tools': '6.5.0-alpha.55', - '@storybook/ember': '6.5.0-alpha.55', - '@storybook/html': '6.5.0-alpha.55', - '@storybook/instrumenter': '6.5.0-alpha.55', - '@storybook/manager-webpack4': '6.5.0-alpha.55', - '@storybook/manager-webpack5': '6.5.0-alpha.55', - '@storybook/node-logger': '6.5.0-alpha.55', - '@storybook/postinstall': '6.5.0-alpha.55', - '@storybook/preact': '6.5.0-alpha.55', - '@storybook/preview-web': '6.5.0-alpha.55', - '@storybook/react': '6.5.0-alpha.55', - '@storybook/router': '6.5.0-alpha.55', - '@storybook/server': '6.5.0-alpha.55', - '@storybook/source-loader': '6.5.0-alpha.55', - '@storybook/store': '6.5.0-alpha.55', - '@storybook/svelte': '6.5.0-alpha.55', - '@storybook/theming': '6.5.0-alpha.55', - '@storybook/ui': '6.5.0-alpha.55', - '@storybook/vue': '6.5.0-alpha.55', - '@storybook/vue3': '6.5.0-alpha.55', - '@storybook/web-components': '6.5.0-alpha.55', -}; + "@storybook/addon-a11y": "6.5.0-alpha.55", + "@storybook/addon-actions": "6.5.0-alpha.55", + "@storybook/addon-backgrounds": "6.5.0-alpha.55", + "@storybook/addon-controls": "6.5.0-alpha.55", + "@storybook/addon-docs": "6.5.0-alpha.55", + "@storybook/addon-essentials": "6.5.0-alpha.55", + "@storybook/addon-interactions": "6.5.0-alpha.55", + "@storybook/addon-jest": "6.5.0-alpha.55", + "@storybook/addon-links": "6.5.0-alpha.55", + "@storybook/addon-measure": "6.5.0-alpha.55", + "@storybook/addon-outline": "6.5.0-alpha.55", + "@storybook/addon-storyshots": "6.5.0-alpha.55", + "@storybook/addon-storyshots-puppeteer": "6.5.0-alpha.55", + "@storybook/addon-storysource": "6.5.0-alpha.55", + "@storybook/addon-toolbars": "6.5.0-alpha.55", + "@storybook/addon-viewport": "6.5.0-alpha.55", + "@storybook/addons": "6.5.0-alpha.55", + "@storybook/angular": "6.5.0-alpha.55", + "@storybook/api": "6.5.0-alpha.55", + "@storybook/builder-webpack4": "6.5.0-alpha.55", + "@storybook/builder-webpack5": "6.5.0-alpha.55", + "@storybook/channel-postmessage": "6.5.0-alpha.55", + "@storybook/channel-websocket": "6.5.0-alpha.55", + "@storybook/channels": "6.5.0-alpha.55", + "@storybook/cli": "6.5.0-alpha.55", + "@storybook/client-api": "6.5.0-alpha.55", + "@storybook/client-logger": "6.5.0-alpha.55", + "@storybook/codemod": "6.5.0-alpha.55", + "@storybook/components": "6.5.0-alpha.55", + "@storybook/core": "6.5.0-alpha.55", + "@storybook/core-client": "6.5.0-alpha.55", + "@storybook/core-common": "6.5.0-alpha.55", + "@storybook/core-events": "6.5.0-alpha.55", + "@storybook/core-server": "6.5.0-alpha.55", + "@storybook/csf-tools": "6.5.0-alpha.55", + "@storybook/docs-tools": "6.5.0-alpha.55", + "@storybook/ember": "6.5.0-alpha.55", + "@storybook/html": "6.5.0-alpha.55", + "@storybook/instrumenter": "6.5.0-alpha.55", + "@storybook/manager-webpack4": "6.5.0-alpha.55", + "@storybook/manager-webpack5": "6.5.0-alpha.55", + "@storybook/node-logger": "6.5.0-alpha.55", + "@storybook/postinstall": "6.5.0-alpha.55", + "@storybook/preact": "6.5.0-alpha.55", + "@storybook/preview-web": "6.5.0-alpha.55", + "@storybook/react": "6.5.0-alpha.55", + "@storybook/router": "6.5.0-alpha.55", + "@storybook/server": "6.5.0-alpha.55", + "@storybook/source-loader": "6.5.0-alpha.55", + "@storybook/store": "6.5.0-alpha.55", + "@storybook/svelte": "6.5.0-alpha.55", + "@storybook/theming": "6.5.0-alpha.55", + "@storybook/ui": "6.5.0-alpha.55", + "@storybook/vue": "6.5.0-alpha.55", + "@storybook/vue3": "6.5.0-alpha.55", + "@storybook/web-components": "6.5.0-alpha.55" +} \ No newline at end of file From 7c8d92da29e0bf9e48e8edf836ce2a34530eb67d Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Tue, 5 Apr 2022 10:52:05 +0200 Subject: [PATCH 5/5] restore 2 files, that should not have been changed, bad merge I guess --- lib/theming/src/tests/create.test.js | 8 +------- lib/ui/src/components/sidebar/Brand.tsx | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/lib/theming/src/tests/create.test.js b/lib/theming/src/tests/create.test.js index 4d002f88d9ec..b85bd0ab3f33 100644 --- a/lib/theming/src/tests/create.test.js +++ b/lib/theming/src/tests/create.test.js @@ -56,13 +56,7 @@ describe('create brand', () => { expect(result.brandTarget).not.toBeDefined(); }); it('should accept null', () => { - const result = create({ - base: 'light', - brandTitle: null, - brandUrl: null, - brandImage: null, - brandTarget: null, - }); + const result = create({ base: 'light', brandTitle: null, brandUrl: null, brandImage: null, brandTarget: null }); expect(result).toMatchObject({ brandImage: null, diff --git a/lib/ui/src/components/sidebar/Brand.tsx b/lib/ui/src/components/sidebar/Brand.tsx index e3bbdf527e07..36b8c7016930 100644 --- a/lib/ui/src/components/sidebar/Brand.tsx +++ b/lib/ui/src/components/sidebar/Brand.tsx @@ -33,7 +33,7 @@ export const LogoLink = styled.a(({ theme }) => ({ export const Brand = withTheme(({ theme }) => { const { title = 'Storybook', url = './', image, target } = theme.brand; - const targetValue = target || (url === './' ? '' : '_blank'); + const targetValue = target ? target : (url === './' ? '' : '_blank'); // When image is explicitly set to null, enable custom HTML support if (image === null) {