Merge branch 'next' into pr/17215

This commit is contained in:
Michael Shilman 2022-04-06 21:46:48 +08:00
commit 0b3635459a
7 changed files with 492 additions and 149 deletions

View File

@ -11,6 +11,19 @@ let compilation: ReturnType<typeof webpackDevMiddleware>;
let reject: (reason?: any) => void;
type WebpackBuilder = Builder<Configuration, Stats>;
type Unpromise<T extends Promise<any>> = T extends Promise<infer U> ? U : never;
type BuilderStartOptions = Partial<Parameters<WebpackBuilder['start']>['0']>;
type BuilderStartResult = Unpromise<ReturnType<WebpackBuilder['start']>>;
type StarterFunction = (
options: BuilderStartOptions
) => AsyncGenerator<unknown, BuilderStartResult, void>;
type BuilderBuildOptions = Partial<Parameters<WebpackBuilder['build']>['0']>;
type BuilderBuildResult = Unpromise<ReturnType<WebpackBuilder['build']>>;
type BuilderFunction = (
options: BuilderBuildOptions
) => AsyncGenerator<unknown, BuilderBuildResult, void>;
export const executor = {
get: async (options: Options) => {
@ -48,11 +61,54 @@ 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<BuilderFunction> | ReturnType<StarterFunction>;
export const bail: WebpackBuilder['bail'] = async () => {
if (asyncIterator) {
try {
// we tell the builder (that started) to stop ASAP and wait
await asyncIterator.throw(new Error());
} catch (e) {
//
}
}
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 +120,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<typeof webpackDevMiddleware>[1] = {
@ -86,6 +143,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 +160,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,8 +179,9 @@ export const build: WebpackBuilder['build'] = async ({ options, startTime }) =>
logger.error(err);
return Promise.resolve(makeStatsFromError(err));
}
yield;
return new Promise((succeed, fail) => {
return new Promise<Stats>((succeed, fail) => {
compiler.run((error, stats) => {
if (error || !stats || stats.hasErrors()) {
logger.error('=> Failed to build the preview');
@ -142,11 +193,22 @@ export const build: WebpackBuilder['build'] = async ({ options, 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'));
}
}
@ -155,10 +217,34 @@ export const build: WebpackBuilder['build'] = async ({ options, startTime }) =>
stats.toJson(config.stats).warnings.forEach((e: string) => logger.warn(e));
}
return succeed(stats);
return succeed(stats as webpackReal.Stats);
});
});
};
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')];

View File

@ -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';
@ -9,6 +9,30 @@ let compilation: ReturnType<typeof webpackDevMiddleware>;
let reject: (reason?: any) => void;
type WebpackBuilder = Builder<Configuration, Stats>;
type Unpromise<T extends Promise<any>> = T extends Promise<infer U> ? U : never;
type BuilderStartOptions = Partial<Parameters<WebpackBuilder['start']>['0']>;
type BuilderStartResult = Unpromise<ReturnType<WebpackBuilder['start']>>;
type StarterFunction = (
options: BuilderStartOptions
) => AsyncGenerator<unknown, BuilderStartResult, void>;
type BuilderBuildOptions = Partial<Parameters<WebpackBuilder['build']>['0']>;
type BuilderBuildResult = Unpromise<ReturnType<WebpackBuilder['build']>>;
type BuilderFunction = (
options: BuilderBuildOptions
) => AsyncGenerator<Stats, BuilderBuildResult, void>;
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;
@ -28,22 +52,55 @@ 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<StarterFunction> | ReturnType<BuilderFunction>;
export const bail: WebpackBuilder['bail'] = async () => {
if (asyncIterator) {
try {
// we tell the builder (that started) to stop ASAP and wait
await asyncIterator.throw(new Error());
} catch (e) {
//
}
}
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();
}
});
};
export const start: WebpackBuilder['start'] = async ({ startTime, options, router }) => {
/**
* 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 +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<typeof webpackDevMiddleware>[1] = {
@ -75,6 +133,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,28 +150,20 @@ 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;
return new Promise((succeed, fail) => {
return new Promise<Stats>((succeed, fail) => {
const compiler = webpackInstance(config);
compiler.run((error, stats) => {
@ -129,7 +180,15 @@ export const build: WebpackBuilder['build'] = async ({ options, 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));
@ -162,5 +221,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')];

View File

@ -1,59 +1,59 @@
// auto generated file, do not edit
export default {
"@storybook/addon-a11y": "6.5.0-alpha.57",
"@storybook/addon-actions": "6.5.0-alpha.57",
"@storybook/addon-backgrounds": "6.5.0-alpha.57",
"@storybook/addon-controls": "6.5.0-alpha.57",
"@storybook/addon-docs": "6.5.0-alpha.57",
"@storybook/addon-essentials": "6.5.0-alpha.57",
"@storybook/addon-interactions": "6.5.0-alpha.57",
"@storybook/addon-jest": "6.5.0-alpha.57",
"@storybook/addon-links": "6.5.0-alpha.57",
"@storybook/addon-measure": "6.5.0-alpha.57",
"@storybook/addon-outline": "6.5.0-alpha.57",
"@storybook/addon-storyshots": "6.5.0-alpha.57",
"@storybook/addon-storyshots-puppeteer": "6.5.0-alpha.57",
"@storybook/addon-storysource": "6.5.0-alpha.57",
"@storybook/addon-toolbars": "6.5.0-alpha.57",
"@storybook/addon-viewport": "6.5.0-alpha.57",
"@storybook/addons": "6.5.0-alpha.57",
"@storybook/angular": "6.5.0-alpha.57",
"@storybook/api": "6.5.0-alpha.57",
"@storybook/builder-webpack4": "6.5.0-alpha.57",
"@storybook/builder-webpack5": "6.5.0-alpha.57",
"@storybook/channel-postmessage": "6.5.0-alpha.57",
"@storybook/channel-websocket": "6.5.0-alpha.57",
"@storybook/channels": "6.5.0-alpha.57",
"@storybook/cli": "6.5.0-alpha.57",
"@storybook/client-api": "6.5.0-alpha.57",
"@storybook/client-logger": "6.5.0-alpha.57",
"@storybook/codemod": "6.5.0-alpha.57",
"@storybook/components": "6.5.0-alpha.57",
"@storybook/core": "6.5.0-alpha.57",
"@storybook/core-client": "6.5.0-alpha.57",
"@storybook/core-common": "6.5.0-alpha.57",
"@storybook/core-events": "6.5.0-alpha.57",
"@storybook/core-server": "6.5.0-alpha.57",
"@storybook/csf-tools": "6.5.0-alpha.57",
"@storybook/docs-tools": "6.5.0-alpha.57",
"@storybook/ember": "6.5.0-alpha.57",
"@storybook/html": "6.5.0-alpha.57",
"@storybook/instrumenter": "6.5.0-alpha.57",
"@storybook/manager-webpack4": "6.5.0-alpha.57",
"@storybook/manager-webpack5": "6.5.0-alpha.57",
"@storybook/node-logger": "6.5.0-alpha.57",
"@storybook/postinstall": "6.5.0-alpha.57",
"@storybook/preact": "6.5.0-alpha.57",
"@storybook/preview-web": "6.5.0-alpha.57",
"@storybook/react": "6.5.0-alpha.57",
"@storybook/router": "6.5.0-alpha.57",
"@storybook/server": "6.5.0-alpha.57",
"@storybook/source-loader": "6.5.0-alpha.57",
"@storybook/store": "6.5.0-alpha.57",
"@storybook/svelte": "6.5.0-alpha.57",
"@storybook/theming": "6.5.0-alpha.57",
"@storybook/ui": "6.5.0-alpha.57",
"@storybook/vue": "6.5.0-alpha.57",
"@storybook/vue3": "6.5.0-alpha.57",
"@storybook/web-components": "6.5.0-alpha.57"
}
'@storybook/addon-a11y': '6.5.0-alpha.57',
'@storybook/addon-actions': '6.5.0-alpha.57',
'@storybook/addon-backgrounds': '6.5.0-alpha.57',
'@storybook/addon-controls': '6.5.0-alpha.57',
'@storybook/addon-docs': '6.5.0-alpha.57',
'@storybook/addon-essentials': '6.5.0-alpha.57',
'@storybook/addon-interactions': '6.5.0-alpha.57',
'@storybook/addon-jest': '6.5.0-alpha.57',
'@storybook/addon-links': '6.5.0-alpha.57',
'@storybook/addon-measure': '6.5.0-alpha.57',
'@storybook/addon-outline': '6.5.0-alpha.57',
'@storybook/addon-storyshots': '6.5.0-alpha.57',
'@storybook/addon-storyshots-puppeteer': '6.5.0-alpha.57',
'@storybook/addon-storysource': '6.5.0-alpha.57',
'@storybook/addon-toolbars': '6.5.0-alpha.57',
'@storybook/addon-viewport': '6.5.0-alpha.57',
'@storybook/addons': '6.5.0-alpha.57',
'@storybook/angular': '6.5.0-alpha.57',
'@storybook/api': '6.5.0-alpha.57',
'@storybook/builder-webpack4': '6.5.0-alpha.57',
'@storybook/builder-webpack5': '6.5.0-alpha.57',
'@storybook/channel-postmessage': '6.5.0-alpha.57',
'@storybook/channel-websocket': '6.5.0-alpha.57',
'@storybook/channels': '6.5.0-alpha.57',
'@storybook/cli': '6.5.0-alpha.57',
'@storybook/client-api': '6.5.0-alpha.57',
'@storybook/client-logger': '6.5.0-alpha.57',
'@storybook/codemod': '6.5.0-alpha.57',
'@storybook/components': '6.5.0-alpha.57',
'@storybook/core': '6.5.0-alpha.57',
'@storybook/core-client': '6.5.0-alpha.57',
'@storybook/core-common': '6.5.0-alpha.57',
'@storybook/core-events': '6.5.0-alpha.57',
'@storybook/core-server': '6.5.0-alpha.57',
'@storybook/csf-tools': '6.5.0-alpha.57',
'@storybook/docs-tools': '6.5.0-alpha.57',
'@storybook/ember': '6.5.0-alpha.57',
'@storybook/html': '6.5.0-alpha.57',
'@storybook/instrumenter': '6.5.0-alpha.57',
'@storybook/manager-webpack4': '6.5.0-alpha.57',
'@storybook/manager-webpack5': '6.5.0-alpha.57',
'@storybook/node-logger': '6.5.0-alpha.57',
'@storybook/postinstall': '6.5.0-alpha.57',
'@storybook/preact': '6.5.0-alpha.57',
'@storybook/preview-web': '6.5.0-alpha.57',
'@storybook/react': '6.5.0-alpha.57',
'@storybook/router': '6.5.0-alpha.57',
'@storybook/server': '6.5.0-alpha.57',
'@storybook/source-loader': '6.5.0-alpha.57',
'@storybook/store': '6.5.0-alpha.57',
'@storybook/svelte': '6.5.0-alpha.57',
'@storybook/theming': '6.5.0-alpha.57',
'@storybook/ui': '6.5.0-alpha.57',
'@storybook/vue': '6.5.0-alpha.57',
'@storybook/vue3': '6.5.0-alpha.57',
'@storybook/web-components': '6.5.0-alpha.57',
};

View File

@ -143,7 +143,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;

View File

@ -96,14 +96,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

View File

@ -16,6 +16,19 @@ let compilation: ReturnType<typeof webpackDevMiddleware>;
let reject: (reason?: any) => void;
type WebpackBuilder = Builder<Configuration, Stats>;
type Unpromise<T extends Promise<any>> = T extends Promise<infer U> ? U : never;
type BuilderStartOptions = Partial<Parameters<WebpackBuilder['start']>['0']>;
type BuilderStartResult = Unpromise<ReturnType<WebpackBuilder['start']>>;
type StarterFunction = (
options: BuilderStartOptions
) => AsyncGenerator<unknown, BuilderStartResult, void>;
type BuilderBuildOptions = Partial<Parameters<WebpackBuilder['build']>['0']>;
type BuilderBuildResult = Unpromise<ReturnType<WebpackBuilder['build']>>;
type BuilderFunction = (
options: BuilderBuildOptions
) => AsyncGenerator<unknown, BuilderBuildResult, void>;
export const WEBPACK_VERSION = '4';
@ -39,19 +52,64 @@ export const executor = {
},
};
export const start: WebpackBuilder['start'] = async ({ startTime, options, router }) => {
let asyncIterator: ReturnType<StarterFunction> | ReturnType<BuilderFunction>;
export const bail: WebpackBuilder['bail'] = async () => {
if (asyncIterator) {
try {
// we tell the builder (that started) to stop ASAP and wait
await asyncIterator.throw(new Error());
} catch (e) {
//
}
}
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 +119,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 +127,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 +149,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<typeof webpackDevMiddleware>[1] = {
@ -104,9 +166,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 +180,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,8 +213,9 @@ export const build: WebpackBuilder['build'] = async ({ options, startTime }) =>
logger.error(err);
return Promise.resolve(makeStatsFromError(err));
}
yield;
return new Promise((succeed, fail) => {
return new Promise<Stats>((succeed, fail) => {
compiler.run((error, stats) => {
if (error || !stats || stats.hasErrors()) {
logger.error('=> Failed to build the manager');
@ -177,6 +246,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'),
];

View File

@ -16,6 +16,19 @@ let compilation: ReturnType<typeof webpackDevMiddleware>;
let reject: (reason?: any) => void;
type WebpackBuilder = Builder<Configuration, Stats>;
type Unpromise<T extends Promise<any>> = T extends Promise<infer U> ? U : never;
type BuilderStartOptions = Partial<Parameters<WebpackBuilder['start']>['0']>;
type BuilderStartResult = Unpromise<ReturnType<WebpackBuilder['start']>>;
type StarterFunction = (
options: BuilderStartOptions
) => AsyncGenerator<unknown, BuilderStartResult, void>;
type BuilderBuildOptions = Partial<Parameters<WebpackBuilder['build']>['0']>;
type BuilderBuildResult = Unpromise<ReturnType<WebpackBuilder['build']>>;
type BuilderFunction = (
options: BuilderBuildOptions
) => AsyncGenerator<unknown, BuilderBuildResult, void>;
export const WEBPACK_VERSION = '5';
@ -39,15 +52,58 @@ export const executor = {
},
};
export const start: WebpackBuilder['start'] = async ({ startTime, options, router }) => {
let asyncIterator: ReturnType<StarterFunction> | ReturnType<BuilderFunction>;
export const bail: WebpackBuilder['bail'] = async () => {
if (asyncIterator) {
try {
// we tell the builder (that started) to stop ASAP and wait
await asyncIterator.throw(new Error());
} catch (e) {
//
}
}
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 +117,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 +125,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 +147,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<typeof webpackDevMiddleware>[1] = {
@ -103,9 +163,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 +177,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,8 +208,9 @@ export const build: WebpackBuilder['build'] = async ({ options, startTime }) =>
logger.error(err);
return Promise.resolve(makeStatsFromError(err));
}
yield;
return new Promise((succeed, fail) => {
return new Promise<Stats>((succeed, fail) => {
compiler.run((error, stats) => {
if (error || !stats || stats.hasErrors()) {
logger.error('=> Failed to build the manager');
@ -174,6 +240,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'),
];