Skip to content

Commit 4c9ef90

Browse files
authored
Merge branch 'next' into shilman/onboarding-user-agent
2 parents 8ee4df6 + e14260f commit 4c9ef90

File tree

18 files changed

+364
-16
lines changed

18 files changed

+364
-16
lines changed

code/core/src/common/utils/load-main-config.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
import { relative, resolve } from 'node:path';
1+
import { readFile, rm, writeFile } from 'node:fs/promises';
2+
import { join, parse, relative, resolve } from 'node:path';
23

4+
import { logger } from 'storybook/internal/node-logger';
35
import { MainFileEvaluationError } from 'storybook/internal/server-errors';
46
import type { StorybookConfig } from 'storybook/internal/types';
57

8+
import { dedent } from 'ts-dedent';
9+
610
import { importModule } from '../../shared/utils/module';
711
import { getInterpretedFile } from './interpret-files';
812
import { validateConfigurationFiles } from './validate-configuration-files';
@@ -25,6 +29,37 @@ export async function loadMainConfig({
2529
if (!(e instanceof Error)) {
2630
throw e;
2731
}
32+
if (e.message.includes('require is not defined')) {
33+
logger.info(
34+
'Loading main config failed, trying a temporary fix, Please ensure the main config is valid ESM'
35+
);
36+
const comment =
37+
'// end of Storybook 10 migration assistant header, you can delete the above code';
38+
const content = await readFile(mainPath, 'utf-8');
39+
40+
if (!content.includes(comment)) {
41+
const header = dedent`
42+
import { createRequire } from "node:module";
43+
import { dirname } from "node:path";
44+
import { fileURLToPath } from "node:url";
45+
46+
const __filename = fileURLToPath(import.meta.url);
47+
const __dirname = dirname(__filename);
48+
const require = createRequire(import.meta.url);
49+
`;
50+
51+
const { ext, name, dir } = parse(mainPath);
52+
const modifiedMainPath = join(dir, `${name}.tmp.${ext}`);
53+
await writeFile(modifiedMainPath, [header, comment, content].join('\n\n'));
54+
let out;
55+
try {
56+
out = await importModule(modifiedMainPath);
57+
} finally {
58+
await rm(modifiedMainPath);
59+
}
60+
return out;
61+
}
62+
}
2863

2964
throw new MainFileEvaluationError({
3065
location: relative(process.cwd(), mainPath),

code/frameworks/nextjs-vite/build-config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ const config: BuildEntries = {
1111
exportEntries: ['./preview'],
1212
entryPoint: './src/preview.tsx',
1313
},
14+
{
15+
exportEntries: ['./config/preview'],
16+
entryPoint: './src/config/preview.ts',
17+
dts: false,
18+
},
1419
{
1520
exportEntries: ['./cache.mock'],
1621
entryPoint: './src/export-mocks/cache/index.ts',

code/frameworks/nextjs-vite/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"types": "./dist/export-mocks/cache/index.d.ts",
3636
"default": "./dist/export-mocks/cache/index.js"
3737
},
38+
"./config/preview": "./dist/config/preview.js",
3839
"./headers.mock": {
3940
"types": "./dist/export-mocks/headers/index.d.ts",
4041
"default": "./dist/export-mocks/headers/index.js"
@@ -88,6 +89,7 @@
8889
"@types/node": "^22.0.0",
8990
"next": "^15.2.3",
9091
"postcss-load-config": "^6.0.1",
92+
"semver": "^7.3.5",
9193
"typescript": "^5.8.3"
9294
},
9395
"peerDependencies": {

code/frameworks/nextjs-vite/src/preset.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ import type { StorybookConfigVite } from '@storybook/builder-vite';
1111
import { viteFinal as reactViteFinal } from '@storybook/react-vite/preset';
1212

1313
import postCssLoadConfig from 'postcss-load-config';
14+
import semver from 'semver';
1415

1516
import type { FrameworkOptions } from './types';
17+
import { getNextjsVersion } from './utils';
1618

1719
const require = createRequire(import.meta.url);
1820

@@ -35,8 +37,20 @@ export const core: PresetProperty<'core'> = async (config, options) => {
3537
};
3638

3739
export const previewAnnotations: PresetProperty<'previewAnnotations'> = (entry = []) => {
38-
const result = [...entry, fileURLToPath(import.meta.resolve('@storybook/nextjs-vite/preview'))];
39-
return result;
40+
const annotations = [
41+
...entry,
42+
fileURLToPath(import.meta.resolve('@storybook/nextjs-vite/preview')),
43+
];
44+
45+
const nextjsVersion = getNextjsVersion();
46+
const isNext16orNewer = semver.gte(nextjsVersion, '16.0.0');
47+
48+
// TODO: Remove this once we only support Next.js v16 and above
49+
if (!isNext16orNewer) {
50+
annotations.push(fileURLToPath(import.meta.resolve('@storybook/nextjs-vite/config/preview')));
51+
}
52+
53+
return annotations;
4054
};
4155

4256
export const optimizeViteDeps = [

code/frameworks/nextjs-vite/src/preview.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type * as React from 'react';
22

33
import type { Addon_DecoratorFunction, LoaderFunction } from 'storybook/internal/types';
44

5-
import type { ReactRenderer, StoryFn } from '@storybook/react';
5+
import type { ReactRenderer } from '@storybook/react';
66

77
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
88
// @ts-ignore we must ignore types here as during compilation they are not generated yet
@@ -13,7 +13,6 @@ import { createRouter } from '@storybook/nextjs-vite/router.mock';
1313

1414
import { isNextRouterError } from 'next/dist/client/components/is-next-router-error';
1515

16-
import './config/preview';
1716
import { HeadManagerDecorator } from './head-manager/decorator';
1817
import { ImageDecorator } from './images/decorator';
1918
import { RouterDecorator } from './routing/decorator';
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { readFileSync } from 'node:fs';
2+
import { join } from 'node:path';
3+
4+
import { resolvePackageDir } from '../../../core/src/shared/utils/module';
5+
6+
export const getNextjsVersion = (): string =>
7+
JSON.parse(readFileSync(join(resolvePackageDir('next'), 'package.json'), 'utf8')).version;

code/frameworks/nextjs/build-config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ const config: BuildEntries = {
1111
exportEntries: ['./preview'],
1212
entryPoint: './src/preview.tsx',
1313
},
14+
{
15+
exportEntries: ['./config/preview'],
16+
entryPoint: './src/config/preview.ts',
17+
dts: false,
18+
},
1419
{
1520
exportEntries: ['./cache.mock'],
1621
entryPoint: './src/export-mocks/cache/index.ts',

code/frameworks/nextjs/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"default": "./dist/export-mocks/cache/index.js"
3636
},
3737
"./compatibility/draft-mode.compat": "./dist/compatibility/draft-mode.compat.js",
38+
"./config/preview": "./dist/config/preview.js",
3839
"./export-mocks": "./dist/export-mocks/index.js",
3940
"./headers.mock": {
4041
"types": "./dist/export-mocks/headers/index.d.ts",

code/frameworks/nextjs/src/config/webpack.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import { fileURLToPath } from 'node:url';
22

33
import type { NextConfig } from 'next';
4+
import semver from 'semver';
45
import type { Configuration as WebpackConfig } from 'webpack';
56

6-
import { addScopedAlias, resolveNextConfig } from '../utils';
7+
import { addScopedAlias, getNextjsVersion, resolveNextConfig } from '../utils';
8+
9+
const nextjsVersion = getNextjsVersion();
10+
const isNext16orNewer = semver.gte(nextjsVersion, '16.0.0');
711

812
const tryResolve = (path: string) => {
913
try {
@@ -22,7 +26,10 @@ export const configureConfig = async ({
2226
}): Promise<NextConfig> => {
2327
const nextConfig = await resolveNextConfig({ nextConfigPath });
2428

25-
addScopedAlias(baseConfig, 'next/config');
29+
// TODO: Remove this once we only support Next.js 16 and above
30+
if (!isNext16orNewer) {
31+
addScopedAlias(baseConfig, 'next/config');
32+
}
2633

2734
// @ts-expect-error We know that alias is an object
2835
if (baseConfig.resolve?.alias?.['react-dom']) {
@@ -58,14 +65,17 @@ const setupRuntimeConfig = async (
5865
baseConfig: WebpackConfig,
5966
nextConfig: NextConfig
6067
): Promise<void> => {
61-
const definePluginConfig: Record<string, any> = {
68+
const definePluginConfig: Record<string, any> = {};
69+
70+
// TODO: Remove this once we only support Next.js 16 and above
71+
if (!isNext16orNewer) {
6272
// this mimics what nextjs does client side
6373
// https://github.com/vercel/next.js/blob/57702cb2a9a9dba4b552e0007c16449cf36cfb44/packages/next/client/index.tsx#L101
64-
'process.env.__NEXT_RUNTIME_CONFIG': JSON.stringify({
74+
definePluginConfig['process.env.__NEXT_RUNTIME_CONFIG'] = JSON.stringify({
6575
serverRuntimeConfig: {},
6676
publicRuntimeConfig: nextConfig.publicRuntimeConfig,
67-
}),
68-
};
77+
});
78+
}
6979

7080
const newNextLinkBehavior = (nextConfig.experimental as any)?.newNextLinkBehavior;
7181

code/frameworks/nextjs/src/preset.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import nextBabelPreset from './babel/preset';
1515
import { configureConfig } from './config/webpack';
1616
import TransformFontImports from './font/babel';
1717
import type { FrameworkOptions, StorybookConfig } from './types';
18+
import { getNextjsVersion } from './utils';
1819

1920
export const addons: PresetProperty<'addons'> = [
2021
fileURLToPath(import.meta.resolve('@storybook/preset-react-webpack')),
@@ -48,8 +49,17 @@ export const core: PresetProperty<'core'> = async (config, options) => {
4849
};
4950

5051
export const previewAnnotations: PresetProperty<'previewAnnotations'> = (entry = []) => {
51-
const result = [...entry, fileURLToPath(import.meta.resolve('@storybook/nextjs/preview'))];
52-
return result;
52+
const annotations = [...entry, fileURLToPath(import.meta.resolve('@storybook/nextjs/preview'))];
53+
54+
const nextjsVersion = getNextjsVersion();
55+
const isNext16orNewer = semver.gte(nextjsVersion, '16.0.0');
56+
57+
// TODO: Remove this once we only support Next.js v16 and above
58+
if (!isNext16orNewer) {
59+
annotations.push(fileURLToPath(import.meta.resolve('@storybook/nextjs/config/preview')));
60+
}
61+
62+
return annotations;
5363
};
5464

5565
export const babel: PresetProperty<'babel'> = async (baseConfig: TransformOptions) => {

0 commit comments

Comments
 (0)