Skip to content

Commit e234df1

Browse files
authored
Merge pull request #33906 from storybookjs/version-non-patch-from-10.3.0-alpha.9
Release: Prerelease 10.3.0-alpha.10
2 parents b7ec06a + 8011372 commit e234df1

File tree

24 files changed

+224
-199
lines changed

24 files changed

+224
-199
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 10.2.11
2+
3+
- Addon-Vitest: Fix postinstall a11y installation - [#33888](https://github.com/storybookjs/storybook/pull/33888), thanks @valentinpalkovic!
4+
- Manifests: Use correct story name - [#33709](https://github.com/storybookjs/storybook/pull/33709), thanks @JReinhold!
5+
- Next.js: Handle legacyBehavior prop in Link mock component - [#33862](https://github.com/storybookjs/storybook/pull/33862), thanks @yatishgoel!
6+
- React: Fix manifest stories empty when meta has no explicit title - [#33878](https://github.com/storybookjs/storybook/pull/33878), thanks @kasperpeulen!
7+
18
## 10.2.10
29

310
- Core: Require token for websocket connections - [#33820](https://github.com/storybookjs/storybook/pull/33820), thanks @ghengeveld!

CHANGELOG.prerelease.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 10.3.0-alpha.10
2+
3+
- Addon-Vitest: Fix postinstall a11y installation - [#33888](https://github.com/storybookjs/storybook/pull/33888), thanks @valentinpalkovic!
4+
- Builder-Vite: Use preview annotations as entry points for optimizeDeps - [#33875](https://github.com/storybookjs/storybook/pull/33875), thanks @copilot-swe-agent!
5+
- React Native Web: Fix inconsistent example stories - [#33891](https://github.com/storybookjs/storybook/pull/33891), thanks @danielalanbates!
6+
- Webpack: Improve performance of module-mocking plugins - [#33169](https://github.com/storybookjs/storybook/pull/33169), thanks @valentinpalkovic!
7+
18
## 10.3.0-alpha.9
29

310
- React: Add react-docgen-typescript to component manifest - [#33818](https://github.com/storybookjs/storybook/pull/33818), thanks @kasperpeulen!

code/addons/docs/src/preset.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,9 @@ const optimizeViteDeps = [
215215
'@storybook/addon-docs',
216216
'@storybook/addon-docs/blocks',
217217
'@storybook/addon-docs > @mdx-js/react',
218+
'@storybook/addon-docs > @storybook/react-dom-shim',
219+
'react-dom/client',
220+
'react/jsx-runtime',
218221
];
219222

220223
export { webpackX as webpack, docsX as docs, optimizeViteDeps };

code/builders/builder-vite/src/codegen-modern-iframe-script.test.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { describe, expect, it } from 'vitest';
22

33
import { generateModernIframeScriptCodeFromPreviews } from './codegen-modern-iframe-script';
4+
import { generateAddonSetupCode } from './codegen-set-addon-channel';
5+
import { optimizeViteDeps } from './preset';
46

57
describe('generateModernIframeScriptCodeFromPreviews', () => {
68
it('handle one annotation', async () => {
@@ -131,3 +133,60 @@ describe('generateModernIframeScriptCodeFromPreviews', () => {
131133
`);
132134
});
133135
});
136+
137+
/**
138+
* Extract bare package import specifiers from a block of generated JavaScript/TypeScript code.
139+
* Captures both `import ... from 'pkg'` and `import 'pkg'` forms, excluding:
140+
*
141+
* - Relative paths (start with `.`)
142+
* - Virtual module IDs (start with `virtual:`)
143+
* - Absolute paths (start with `/`)
144+
*/
145+
function extractPackageImports(code: string): string[] {
146+
const importRegex = /import\s+(?:[^'"]*\s+from\s+)?['"]([^'"]+)['"]/g;
147+
const specifiers = new Set<string>();
148+
for (const match of code.matchAll(importRegex)) {
149+
const specifier = match[1];
150+
if (
151+
!specifier.startsWith('.') &&
152+
!specifier.startsWith('virtual:') &&
153+
!specifier.startsWith('/')
154+
) {
155+
specifiers.add(specifier);
156+
}
157+
}
158+
return [...specifiers];
159+
}
160+
161+
describe('optimizeDeps coverage for virtual module imports', () => {
162+
it('every package imported in virtual module code is either in optimizeViteDeps or known to be discovered via entry crawling', async () => {
163+
// Collect all code generated for virtual modules — Vite's dep scanner never sees
164+
// the contents of virtual modules, so any package imported there must be
165+
// pre-bundled explicitly via optimizeViteDeps.
166+
const iframeCode = await generateModernIframeScriptCodeFromPreviews({ frameworkName: 'test' });
167+
const addonCode = await generateAddonSetupCode();
168+
const allVirtualModuleCode = [iframeCode, addonCode].join('\n');
169+
170+
const packageImports = extractPackageImports(allVirtualModuleCode);
171+
172+
// These packages are also imported in real source files (preview annotations, renderer
173+
// previews, addon previews) that ARE added as optimizeDeps entries, so Vite discovers
174+
// them via entry crawling. No explicit optimizeViteDeps entry is required.
175+
const discoveredViaEntries = new Set([
176+
'storybook/preview-api', // Imported in many renderer/addon preview files
177+
'storybook/internal/channels', // Imported in addon preview files
178+
]);
179+
180+
const notCovered = packageImports.filter(
181+
(pkg) => !discoveredViaEntries.has(pkg) && !optimizeViteDeps.includes(pkg)
182+
);
183+
184+
expect(
185+
notCovered,
186+
`The following packages are imported in virtual module code but are NOT covered.\n` +
187+
`They must be added to optimizeViteDeps in builder-vite/src/preset.ts, OR added to\n` +
188+
`the discoveredViaEntries set in this test if they appear in real source entry files.\n` +
189+
`Uncovered: ${notCovered.join(', ')}`
190+
).toHaveLength(0);
191+
});
192+
});

code/builders/builder-vite/src/constants.ts

Lines changed: 0 additions & 118 deletions
This file was deleted.

code/builders/builder-vite/src/optimizeDeps.ts

Lines changed: 0 additions & 30 deletions
This file was deleted.

code/builders/builder-vite/src/plugins/storybook-optimize-deps-plugin.ts

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
import { loadPreviewOrConfigFile } from 'storybook/internal/common';
12
import type { StoryIndexGenerator } from 'storybook/internal/core-server';
2-
import type { Options, StoryIndex } from 'storybook/internal/types';
3+
import type { Options, PreviewAnnotation, StoryIndex } from 'storybook/internal/types';
34

5+
import { resolve } from 'pathe';
46
import { type Plugin } from 'vite';
57

8+
import { processPreviewAnnotation } from '../utils/process-preview-annotation';
69
import { getUniqueImportPaths } from '../utils/unique-import-paths';
710

811
/** A Vite plugin that configures dependency optimization for Storybook's dev server. */
@@ -15,25 +18,37 @@ export function storybookOptimizeDepsPlugin(options: Options): Plugin {
1518
return;
1619
}
1720

18-
const [extraOptimizeDeps, storyIndexGenerator] = await Promise.all([
19-
options.presets.apply('optimizeViteDeps', []),
21+
const projectRoot = resolve(options.configDir, '..');
22+
23+
const [extraOptimizeDeps, storyIndexGenerator, previewAnnotations] = await Promise.all([
24+
options.presets.apply<string[]>('optimizeViteDeps', []),
2025
options.presets.apply<StoryIndexGenerator>('storyIndexGenerator'),
26+
options.presets.apply<PreviewAnnotation[]>('previewAnnotations', [], options),
2127
]);
2228

2329
const index: StoryIndex = await storyIndexGenerator.getIndex();
2430

31+
// Include the user's preview file and all addon/framework/renderer preview annotations
32+
// as optimizer entries so Vite can discover all transitive CJS dependencies automatically.
33+
const previewOrConfigFile = loadPreviewOrConfigFile({ configDir: options.configDir });
34+
const previewAnnotationEntries = [...previewAnnotations, previewOrConfigFile]
35+
.filter((path): path is PreviewAnnotation => path !== undefined)
36+
.map((path) => processPreviewAnnotation(path, projectRoot));
37+
2538
return {
2639
optimizeDeps: {
27-
// Story file paths as entry points for the optimizer
40+
// Story files + preview annotation files as entry points for the dep optimizer.
41+
// Vite will crawl these to discover all transitive CJS dependencies that need
42+
// pre-bundling, removing the need for a hard-coded include list.
2843
entries: [
2944
...(typeof config.optimizeDeps?.entries === 'string'
3045
? [config.optimizeDeps.entries]
31-
: []),
46+
: (config.optimizeDeps?.entries ?? [])),
3247
...getUniqueImportPaths(index),
48+
...previewAnnotationEntries,
3349
],
34-
// Known CJS dependencies that need to be pre-compiled to ESM,
35-
// plus any extra deps from Storybook presets.
36-
include: [...extraOptimizeDeps, ...(config.optimizeDeps?.include || [])],
50+
// Extra deps explicitly included by Storybook presets (e.g. framework-specific packages).
51+
include: [...extraOptimizeDeps, ...(config.optimizeDeps?.include ?? [])],
3752
},
3853
};
3954
},

code/builders/builder-vite/src/preset.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { storybookSanitizeEnvs } from './plugins/storybook-runtime-plugin';
1010
import { viteInjectMockerRuntime } from './plugins/vite-inject-mocker/plugin';
1111
import { viteMockPlugin } from './plugins/vite-mock/plugin';
1212

13+
export const optimizeViteDeps: string[] = ['storybook/internal/preview/runtime'];
14+
1315
/**
1416
* Preset that provides the core Storybook Vite plugins shared between `@storybook/builder-vite` and
1517
* `@storybook/addon-vitest`.

code/builders/builder-vite/src/vite-server.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,15 @@ import { dedent } from 'ts-dedent';
66
import type { InlineConfig, ServerOptions } from 'vite';
77

88
import { createViteLogger } from './logger';
9-
import { getOptimizeDeps } from './optimizeDeps';
109
import { commonConfig } from './vite-config';
1110

1211
export async function createViteServer(options: Options, devServer: Server) {
1312
const { presets } = options;
1413

1514
const commonCfg = await commonConfig(options, 'development');
1615

17-
const optimizeDeps = await getOptimizeDeps(commonCfg);
18-
1916
const config: InlineConfig & { server: ServerOptions } = {
2017
...commonCfg,
21-
// Set up dev server
22-
optimizeDeps: {
23-
...commonCfg.optimizeDeps,
24-
include: [...(commonCfg.optimizeDeps?.include || []), ...optimizeDeps.include],
25-
},
2618
server: {
2719
middlewareMode: true,
2820
hmr: {

code/builders/builder-webpack5/src/plugins/webpack-inject-mocker-runtime-plugin.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const PLUGIN_NAME = 'WebpackInjectMockerRuntimePlugin';
1313
* Storybook preview bundle, are executed.
1414
*/
1515
export class WebpackInjectMockerRuntimePlugin {
16+
private cachedRuntime: string | null = null;
1617
// We need to lazy-require HtmlWebpackPlugin because it's an optional peer dependency.
1718
private getHtmlWebpackPlugin(compiler: Compiler): typeof HtmlWebpackPlugin | null {
1819
try {
@@ -52,20 +53,23 @@ export class WebpackInjectMockerRuntimePlugin {
5253
PLUGIN_NAME,
5354
(data, cb) => {
5455
try {
55-
const runtimeScriptContent = getMockerRuntime();
56+
const runtimeScriptContent =
57+
this.cachedRuntime ?? (this.cachedRuntime = getMockerRuntime());
5658
const runtimeAssetName = 'mocker-runtime-injected.js';
5759

5860
// Use the documented `emitAsset` method to add the pre-bundled runtime script
5961
// to the compilation's assets. This is the standard Webpack way.
60-
compilation.emitAsset(
61-
runtimeAssetName,
62-
new compiler.webpack.sources.RawSource(runtimeScriptContent)
63-
);
62+
if (!compilation.getAsset(runtimeAssetName)) {
63+
compilation.emitAsset(
64+
runtimeAssetName,
65+
new compiler.webpack.sources.RawSource(runtimeScriptContent)
66+
);
67+
data.assets.js.unshift(runtimeAssetName);
68+
}
6469

65-
// Prepend the name of our new asset to the list of JavaScript files.
70+
// Prepend the name of our new asset to the list of JavaScript files, once.
6671
// HtmlWebpackPlugin will automatically create a <script> tag for it
6772
// and place it at the beginning of the body scripts.
68-
data.assets.js.unshift(runtimeAssetName);
6973
cb(null, data);
7074
} catch (error) {
7175
// In case of an error (e.g., file not found), pass it to Webpack's compilation.

0 commit comments

Comments
 (0)