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

Webpack5: Resolve circular dependency and fix HMR #24974

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions code/builders/builder-webpack5/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
},
"./templates/virtualModuleModernEntry.js.handlebars": "./templates/virtualModuleModernEntry.js.handlebars",
"./templates/preview.ejs": "./templates/preview.ejs",
"./templates/virtualModuleEntry.template.js": "./templates/virtualModuleEntry.template.js",
"./templates/virtualModuleStory.template.js": "./templates/virtualModuleStory.template.js",
"./package.json": "./package.json"
},
"main": "dist/index.js",
Expand Down
1 change: 1 addition & 0 deletions code/builders/builder-webpack5/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
import prettyTime from 'pretty-hrtime';

export * from './types';
export * from './preview/virtual-module-mapping';

export const printDuration = (startTime: [number, number]) =>
prettyTime(process.hrtime(startTime))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ import {
normalizeStories,
isPreservingSymlinks,
} from '@storybook/core-common';
import type { BuilderOptions } from '@storybook/core-webpack';
import { getVirtualModuleMapping } from '@storybook/core-webpack';
import { type BuilderOptions } from '@storybook/core-webpack';
import { dedent } from 'ts-dedent';
import type { TypescriptOptions } from '../types';
import { createBabelLoader, createSWCLoader } from './loaders';
import { getVirtualModules } from './virtual-module-mapping';

const getAbsolutePath = <I extends string>(input: I): I =>
dirname(require.resolve(join(input, 'package.json'))) as any;
Expand Down Expand Up @@ -135,18 +135,16 @@ export default async (
externals['@storybook/blocks'] = '__STORYBOOK_BLOCKS_EMPTY_MODULE__';
}

const virtualModuleMapping = await getVirtualModuleMapping(options);

Object.keys(virtualModuleMapping).forEach((key) => {
entries.push(key);
});
const { virtualModules: virtualModuleMapping, entries: dynamicEntries } = await getVirtualModules(
options
);

return {
name: 'preview',
mode: isProd ? 'production' : 'development',
bail: isProd,
devtool: options.build?.test?.disableSourcemaps ? false : 'cheap-module-source-map',
entry: entries,
entry: [...(entries ?? []), ...dynamicEntries],
output: {
path: resolve(process.cwd(), outputDir),
filename: isProd ? '[name].[contenthash:8].iframe.bundle.js' : '[name].iframe.bundle.js',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import type { Options, PreviewAnnotation } from '@storybook/types';
import { isAbsolute, join, resolve } from 'path';
import {
getBuilderOptions,
getRendererName,
Expand All @@ -9,17 +7,19 @@ import {
normalizeStories,
readTemplate,
} from '@storybook/core-common';
import type { Options, PreviewAnnotation } from '@storybook/types';
import { isAbsolute, join, resolve } from 'path';
import slash from 'slash';
import type { BuilderOptions } from './types';
import { toImportFn } from './to-importFn';
import { toRequireContextString } from './to-require-context';
import { toImportFn, toRequireContextString } from '@storybook/core-webpack';
import type { BuilderOptions } from '../types';

export const getVirtualModuleMapping = async (options: Options) => {
const virtualModuleMapping: Record<string, string> = {};
export const getVirtualModules = async (options: Options) => {
const virtualModules: Record<string, string> = {};
const builderOptions = await getBuilderOptions<BuilderOptions>(options);
const workingDir = process.cwd();
const isProd = options.configType === 'PRODUCTION';
const nonNormalizedStories = await options.presets.apply('stories', []);
const entries = [];

const stories = normalizeStories(nonNormalizedStories, {
configDir: options.configDir,
Expand Down Expand Up @@ -53,9 +53,9 @@ export const getVirtualModuleMapping = async (options: Options) => {
const storiesPath = resolve(join(workingDir, storiesFilename));

const needPipelinedImport = !!builderOptions.lazyCompilation && !isProd;
virtualModuleMapping[storiesPath] = toImportFn(stories, { needPipelinedImport });
virtualModules[storiesPath] = toImportFn(stories, { needPipelinedImport });
const configEntryPath = resolve(join(workingDir, 'storybook-config-entry.js'));
virtualModuleMapping[configEntryPath] = handlebars(
virtualModules[configEntryPath] = handlebars(
await readTemplate(
require.resolve(
'@storybook/builder-webpack5/templates/virtualModuleModernEntry.js.handlebars'
Expand All @@ -67,14 +67,16 @@ export const getVirtualModuleMapping = async (options: Options) => {
}
// We need to double escape `\` for webpack. We may have some in windows paths
).replace(/\\/g, '\\\\');
entries.push(configEntryPath);
} else {
const rendererName = await getRendererName(options);

const rendererInitEntry = resolve(join(workingDir, 'storybook-init-renderer-entry.js'));
virtualModuleMapping[rendererInitEntry] = `import '${slash(rendererName)}';`;
virtualModules[rendererInitEntry] = `import '${slash(rendererName)}';`;
entries.push(rendererInitEntry);

const entryTemplate = await readTemplate(
join(__dirname, '..', 'templates', 'virtualModuleEntry.template.js')
require.resolve('@storybook/builder-webpack5/templates/virtualModuleEntry.template.js')
);

previewAnnotations.forEach((previewAnnotationFilename: string | undefined) => {
Expand All @@ -87,25 +89,30 @@ export const getVirtualModuleMapping = async (options: Options) => {
: `${previewAnnotationFilename}-generated-config-entry.js`;
// NOTE: although this file is also from the `dist/cjs` directory, it is actually a ESM
// file, see https://github.com/storybookjs/storybook/pull/16727#issuecomment-986485173
virtualModuleMapping[entryFilename] = interpolate(entryTemplate, {
virtualModules[entryFilename] = interpolate(entryTemplate, {
previewAnnotationFilename,
});
entries.push(entryFilename);
});
if (stories.length > 0) {
const storyTemplate = await readTemplate(
join(__dirname, '..', 'templates', 'virtualModuleStory.template.js')
require.resolve('@storybook/builder-webpack5/templates/virtualModuleStory.template.js')
);
// NOTE: this file has a `.cjs` extension as it is a CJS file (from `dist/cjs`) and runs
// in the user's webpack mode, which may be strict about the use of require/import.
// See https://github.com/storybookjs/storybook/issues/14877
const storiesFilename = resolve(join(workingDir, `generated-stories-entry.cjs`));
virtualModuleMapping[storiesFilename] = interpolate(storyTemplate, {
virtualModules[storiesFilename] = interpolate(storyTemplate, {
rendererName,
})
// Make sure we also replace quotes for this one
.replace("'{{stories}}'", stories.map(toRequireContextString).join(','));
entries.push(storiesFilename);
}
}

return virtualModuleMapping;
return {
virtualModules,
entries,
};
};
1 change: 0 additions & 1 deletion code/frameworks/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@
"@storybook/builder-webpack5": "workspace:*",
"@storybook/core-common": "workspace:*",
"@storybook/core-events": "workspace:*",
"@storybook/core-webpack": "workspace:*",
"@storybook/node-logger": "workspace:*",
"@storybook/preset-react-webpack": "workspace:*",
"@storybook/preview-api": "workspace:*",
Expand Down
4 changes: 2 additions & 2 deletions code/frameworks/nextjs/src/swc/loader.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getProjectRoot } from '@storybook/core-common';
import { getVirtualModuleMapping } from '@storybook/core-webpack';
import { getVirtualModules } from '@storybook/builder-webpack5';
import type { Options, Preset } from '@storybook/types';
import type { NextConfig } from 'next';
import path from 'path';
Expand Down Expand Up @@ -29,7 +29,7 @@ export const configureSWCLoader = async (

const dir = getProjectRoot();

const virtualModules = await getVirtualModuleMapping(options);
const { virtualModules } = await getVirtualModules(options);

baseConfig.module.rules = [
// TODO: Remove filtering in Storybook 8.0
Expand Down
1 change: 0 additions & 1 deletion code/lib/core-webpack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
"ts-dedent": "^2.0.0"
},
"devDependencies": {
"slash": "^5.1.0",
"typescript": "~4.9.3",
"webpack": "5"
},
Expand Down
1 change: 0 additions & 1 deletion code/lib/core-webpack/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,3 @@ export * from './check-webpack-version';
export * from './merge-webpack-config';
export * from './to-importFn';
export * from './to-require-context';
export * from './virtual-module-mapping';
4 changes: 1 addition & 3 deletions code/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6490,7 +6490,6 @@ __metadata:
"@storybook/node-logger": "workspace:*"
"@storybook/types": "workspace:*"
"@types/node": "npm:^18.0.0"
slash: "npm:^5.1.0"
ts-dedent: "npm:^2.0.0"
typescript: "npm:~4.9.3"
webpack: "npm:5"
Expand Down Expand Up @@ -6858,7 +6857,6 @@ __metadata:
"@storybook/builder-webpack5": "workspace:*"
"@storybook/core-common": "workspace:*"
"@storybook/core-events": "workspace:*"
"@storybook/core-webpack": "workspace:*"
"@storybook/node-logger": "workspace:*"
"@storybook/preset-react-webpack": "workspace:*"
"@storybook/preview-api": "workspace:*"
Expand Down Expand Up @@ -27444,7 +27442,7 @@ __metadata:
languageName: node
linkType: hard

"slash@npm:^5.0.0, slash@npm:^5.1.0":
"slash@npm:^5.0.0":
version: 5.1.0
resolution: "slash@npm:5.1.0"
checksum: eb48b815caf0bdc390d0519d41b9e0556a14380f6799c72ba35caf03544d501d18befdeeef074bc9c052acf69654bc9e0d79d7f1de0866284137a40805299eb3
Expand Down