Skip to content

Commit 9a5e11b

Browse files
Merge branch 'next' into next
2 parents 6141b5b + 7780067 commit 9a5e11b

File tree

85 files changed

+5110
-12
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

85 files changed

+5110
-12
lines changed

code/builders/builder-vite/src/plugins/code-generator-plugin.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -70,33 +70,33 @@ export function codeGeneratorPlugin(options: Options): Plugin {
7070
},
7171
resolveId(source) {
7272
if (source === virtualFileId) {
73-
return virtualFileId;
73+
return `\0${virtualFileId}`;
7474
}
7575
if (source === iframePath) {
7676
return iframeId;
7777
}
7878
if (source === virtualStoriesFile) {
79-
return virtualStoriesFile;
79+
return `\0${virtualStoriesFile}`;
8080
}
8181
if (source === virtualPreviewFile) {
8282
return virtualPreviewFile;
8383
}
8484
if (source === virtualAddonSetupFile) {
85-
return virtualAddonSetupFile;
85+
return `\0${virtualAddonSetupFile}`;
8686
}
8787

8888
return undefined;
8989
},
9090
async load(id, config) {
91-
if (id === virtualStoriesFile) {
91+
if (id === `\0${virtualStoriesFile}`) {
9292
return generateImportFnScriptCode(options);
9393
}
9494

95-
if (id === virtualAddonSetupFile) {
95+
if (id === `\0${virtualAddonSetupFile}`) {
9696
return generateAddonSetupCode();
9797
}
9898

99-
if (id === virtualFileId) {
99+
if (id === `\0${virtualFileId}`) {
100100
return generateModernIframeScriptCode(options, projectRoot);
101101
}
102102

code/core/src/cli/helpers.ts

+1
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ export const frameworkToDefaultBuilder: Record<SupportedFrameworks, CoreBuilder>
144144
'html-vite': CoreBuilder.Vite,
145145
'html-webpack5': CoreBuilder.Webpack5,
146146
nextjs: CoreBuilder.Webpack5,
147+
'experimental-nextjs-vite': CoreBuilder.Vite,
147148
'preact-vite': CoreBuilder.Vite,
148149
'preact-webpack5': CoreBuilder.Webpack5,
149150
qwik: CoreBuilder.Vite,

code/core/src/common/utils/framework-to-renderer.ts

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const frameworkToRenderer: Record<
1111
'html-vite': 'html',
1212
'html-webpack5': 'html',
1313
nextjs: 'react',
14+
'experimental-nextjs-vite': 'react',
1415
'preact-vite': 'preact',
1516
'preact-webpack5': 'preact',
1617
qwik: 'qwik',

code/core/src/common/versions.ts

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export default {
4141
'@storybook/types': '8.3.0-alpha.4',
4242
'@storybook/angular': '8.3.0-alpha.4',
4343
'@storybook/ember': '8.3.0-alpha.4',
44+
'@storybook/experimental-nextjs-vite': '8.3.0-alpha.4',
4445
'@storybook/html-vite': '8.3.0-alpha.4',
4546
'@storybook/html-webpack5': '8.3.0-alpha.4',
4647
'@storybook/nextjs': '8.3.0-alpha.4',

code/core/src/types/modules/frameworks.ts

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
export type SupportedFrameworks =
33
| 'angular'
44
| 'ember'
5+
| 'experimental-nextjs-vite'
56
| 'html-vite'
67
| 'html-webpack5'
78
| 'nextjs'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"rules": {
3+
"global-require": "off",
4+
"no-param-reassign": "off",
5+
"import/no-dynamic-require": "off",
6+
"import/no-unresolved": "off"
7+
},
8+
"overrides": [
9+
{
10+
"files": ["**/*.stories.@(jsx|tsx)"],
11+
"rules": {
12+
"react/no-unknown-property": "off",
13+
"jsx-a11y/anchor-is-valid": "off"
14+
}
15+
},
16+
{
17+
"files": ["**/*.compat.@(tsx|ts)"],
18+
"rules": {
19+
"local-rules/no-uncategorized-errors": "off"
20+
}
21+
}
22+
]
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Storybook for Next.js with Vite Builder
2+
3+
See [documentation](https://storybook.js.org/docs/get-started/frameworks/nextjs?renderer=react) for installation instructions, usage examples, APIs, and more.
4+
5+
## Acknowledgements
6+
7+
This framework borrows heavily from these Storybook addons:
8+
9+
- [storybook-addon-next](https://github.com/RyanClementsHax/storybook-addon-next) by [RyanClementsHax](https://github.com/RyanClementsHax/)
10+
- [storybook-addon-next-router](https://github.com/lifeiscontent/storybook-addon-next-router) by [lifeiscontent](https://github.com/lifeiscontent)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
{
2+
"name": "@storybook/experimental-nextjs-vite",
3+
"version": "8.3.0-alpha.4",
4+
"description": "Storybook for Next.js and Vite",
5+
"keywords": [
6+
"storybook",
7+
"nextjs",
8+
"vite"
9+
],
10+
"homepage": "https://github.com/storybookjs/storybook/tree/next/code/frameworks/experimental-nextjs-vite",
11+
"bugs": {
12+
"url": "https://github.com/storybookjs/storybook/issues"
13+
},
14+
"repository": {
15+
"type": "git",
16+
"url": "https://github.com/storybookjs/storybook.git",
17+
"directory": "code/frameworks/nextjs"
18+
},
19+
"funding": {
20+
"type": "opencollective",
21+
"url": "https://opencollective.com/storybook"
22+
},
23+
"license": "MIT",
24+
"exports": {
25+
".": {
26+
"types": "./dist/index.d.ts",
27+
"node": "./dist/index.js",
28+
"import": "./dist/index.mjs",
29+
"require": "./dist/index.js"
30+
},
31+
"./preset": {
32+
"types": "./dist/preset.d.ts",
33+
"require": "./dist/preset.js"
34+
},
35+
"./dist/preview.mjs": "./dist/preview.mjs",
36+
"./cache.mock": {
37+
"types": "./dist/export-mocks/cache/index.d.ts",
38+
"import": "./dist/export-mocks/cache/index.mjs",
39+
"require": "./dist/export-mocks/cache/index.js"
40+
},
41+
"./headers.mock": {
42+
"types": "./dist/export-mocks/headers/index.d.ts",
43+
"import": "./dist/export-mocks/headers/index.mjs",
44+
"require": "./dist/export-mocks/headers/index.js"
45+
},
46+
"./navigation.mock": {
47+
"types": "./dist/export-mocks/navigation/index.d.ts",
48+
"import": "./dist/export-mocks/navigation/index.mjs",
49+
"require": "./dist/export-mocks/navigation/index.js"
50+
},
51+
"./router.mock": {
52+
"types": "./dist/export-mocks/router/index.d.ts",
53+
"import": "./dist/export-mocks/router/index.mjs",
54+
"require": "./dist/export-mocks/router/index.js"
55+
},
56+
"./package.json": "./package.json"
57+
},
58+
"main": "dist/index.js",
59+
"module": "dist/index.mjs",
60+
"types": "dist/index.d.ts",
61+
"typesVersions": {
62+
"*": {
63+
"*": [
64+
"dist/index.d.ts"
65+
],
66+
"cache.mock": [
67+
"dist/export-mocks/cache/index.d.ts"
68+
],
69+
"headers.mock": [
70+
"dist/export-mocks/headers/index.d.ts"
71+
],
72+
"router.mock": [
73+
"dist/export-mocks/router/index.d.ts"
74+
],
75+
"navigation.mock": [
76+
"dist/export-mocks/navigation/index.d.ts"
77+
]
78+
}
79+
},
80+
"files": [
81+
"dist/**/*",
82+
"template/cli/**/*",
83+
"README.md",
84+
"*.js",
85+
"*.d.ts",
86+
"!src/**/*"
87+
],
88+
"scripts": {
89+
"check": "jiti ../../../scripts/prepare/check.ts",
90+
"prep": "jiti ../../../scripts/prepare/bundle.ts"
91+
},
92+
"dependencies": {
93+
"@storybook/builder-vite": "workspace:*",
94+
"@storybook/react": "workspace:*",
95+
"@storybook/test": "workspace:*",
96+
"styled-jsx": "5.1.6"
97+
},
98+
"devDependencies": {
99+
"@types/node": "^18.0.0",
100+
"next": "^14.2.5",
101+
"typescript": "^5.3.2",
102+
"vite-plugin-storybook-nextjs": "^1.0.0"
103+
},
104+
"peerDependencies": {
105+
"next": "^14.2.5",
106+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta",
107+
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta",
108+
"storybook": "workspace:^",
109+
"vite": "^5.0.0",
110+
"vite-plugin-storybook-nextjs": "^1.0.0"
111+
},
112+
"peerDependenciesMeta": {
113+
"typescript": {
114+
"optional": true
115+
}
116+
},
117+
"optionalDependencies": {
118+
"sharp": "^0.33.3"
119+
},
120+
"engines": {
121+
"node": ">=18.0.0"
122+
},
123+
"publishConfig": {
124+
"access": "public"
125+
},
126+
"bundler": {
127+
"entries": [
128+
"./src/index.ts",
129+
"./src/preset.ts",
130+
"./src/preview.tsx",
131+
"./src/export-mocks/cache/index.ts",
132+
"./src/export-mocks/headers/index.ts",
133+
"./src/export-mocks/router/index.ts",
134+
"./src/export-mocks/navigation/index.ts",
135+
"./src/images/decorator.tsx"
136+
],
137+
"externals": [
138+
"sb-original/image-context"
139+
],
140+
"platform": "node"
141+
},
142+
"gitHead": "e6a7fd8a655c69780bc20b9749c2699e44beae16"
143+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('./dist/preset');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"name": "experimental-nextjs-vite",
3+
"$schema": "../../node_modules/nx/schemas/project-schema.json",
4+
"projectType": "library",
5+
"targets": {
6+
"build": {}
7+
}
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { setConfig } from 'next/config';
2+
3+
// eslint-disable-next-line no-underscore-dangle
4+
setConfig(process.env.__NEXT_RUNTIME_CONFIG);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/* eslint-disable @typescript-eslint/naming-convention */
2+
import { fn } from '@storybook/test';
3+
4+
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
5+
type Callback = (...args: any[]) => Promise<any>;
6+
7+
// mock utilities/overrides (as of Next v14.2.0)
8+
const revalidatePath = fn().mockName('next/cache::revalidatePath');
9+
const revalidateTag = fn().mockName('next/cache::revalidateTag');
10+
const unstable_cache = fn()
11+
.mockName('next/cache::unstable_cache')
12+
.mockImplementation((cb: Callback) => cb);
13+
const unstable_noStore = fn().mockName('next/cache::unstable_noStore');
14+
15+
const cacheExports = {
16+
unstable_cache,
17+
revalidateTag,
18+
revalidatePath,
19+
unstable_noStore,
20+
};
21+
22+
export default cacheExports;
23+
export { unstable_cache, revalidateTag, revalidatePath, unstable_noStore };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// We need this import to be a singleton, and because it's used in multiple entrypoints
2+
// both in ESM and CJS, importing it via the package name instead of having a local import
3+
// is the only way to achieve it actually being a singleton
4+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
5+
// @ts-ignore we must ignore types here as during compilation they are not generated yet
6+
import { headers } from '@storybook/nextjs/headers.mock';
7+
import { fn } from '@storybook/test';
8+
9+
import { RequestCookies } from 'next/dist/compiled/@edge-runtime/cookies';
10+
11+
class RequestCookiesMock extends RequestCookies {
12+
get = fn(super.get.bind(this)).mockName('next/headers::cookies().get');
13+
14+
getAll = fn(super.getAll.bind(this)).mockName('next/headers::cookies().getAll');
15+
16+
has = fn(super.has.bind(this)).mockName('next/headers::cookies().has');
17+
18+
set = fn(super.set.bind(this)).mockName('next/headers::cookies().set');
19+
20+
delete = fn(super.delete.bind(this)).mockName('next/headers::cookies().delete');
21+
}
22+
23+
let requestCookiesMock: RequestCookiesMock;
24+
25+
export const cookies = fn(() => {
26+
if (!requestCookiesMock) {
27+
requestCookiesMock = new RequestCookiesMock(headers());
28+
}
29+
return requestCookiesMock;
30+
}).mockName('next/headers::cookies()');
31+
32+
const originalRestore = cookies.mockRestore.bind(null);
33+
34+
// will be called automatically by the test loader
35+
cookies.mockRestore = () => {
36+
originalRestore();
37+
headers.mockRestore();
38+
requestCookiesMock = new RequestCookiesMock(headers());
39+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { fn } from '@storybook/test';
2+
3+
import { HeadersAdapter } from 'next/dist/server/web/spec-extension/adapters/headers';
4+
5+
class HeadersAdapterMock extends HeadersAdapter {
6+
constructor() {
7+
super({});
8+
}
9+
10+
append = fn(super.append.bind(this)).mockName('next/headers::headers().append');
11+
12+
delete = fn(super.delete.bind(this)).mockName('next/headers::headers().delete');
13+
14+
get = fn(super.get.bind(this)).mockName('next/headers::headers().get');
15+
16+
has = fn(super.has.bind(this)).mockName('next/headers::headers().has');
17+
18+
set = fn(super.set.bind(this)).mockName('next/headers::headers().set');
19+
20+
forEach = fn(super.forEach.bind(this)).mockName('next/headers::headers().forEach');
21+
22+
entries = fn(super.entries.bind(this)).mockName('next/headers::headers().entries');
23+
24+
keys = fn(super.keys.bind(this)).mockName('next/headers::headers().keys');
25+
26+
values = fn(super.values.bind(this)).mockName('next/headers::headers().values');
27+
}
28+
29+
let headersAdapterMock: HeadersAdapterMock;
30+
31+
export const headers = () => {
32+
if (!headersAdapterMock) headersAdapterMock = new HeadersAdapterMock();
33+
return headersAdapterMock;
34+
};
35+
36+
// This fn is called by ./cookies to restore the headers in the right order
37+
headers.mockRestore = () => {
38+
headersAdapterMock = new HeadersAdapterMock();
39+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { fn } from '@storybook/test';
2+
3+
import * as originalHeaders from 'next/dist/client/components/headers';
4+
5+
// re-exports of the actual module
6+
export * from 'next/dist/client/components/headers';
7+
8+
// mock utilities/overrides (as of Next v14.2.0)
9+
export { headers } from './headers';
10+
export { cookies } from './cookies';
11+
12+
// passthrough mocks - keep original implementation but allow for spying
13+
const draftMode = fn(originalHeaders.draftMode).mockName('draftMode');
14+
export { draftMode };

0 commit comments

Comments
 (0)