Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
102 commits
Select commit Hold shift + click to select a range
d9ce756
React: Add testing-api entry point
shilman May 10, 2023
66011c9
Merge branch 'next' into shilman/nextjs-server
shilman May 13, 2023
ddce05b
Merge branch 'next' into shilman/nextjs-server
shilman Sep 2, 2023
0edb6c7
Merge branch 'next' into shilman/nextjs-server
shilman Sep 13, 2023
f199ae5
NextJS-server: WIP
shilman Sep 13, 2023
1ca18d6
Update from next
shilman Sep 23, 2023
c9ae3c1
WIP
tmeasday Oct 5, 2023
e9e333f
Split `render` from `renderToCanvas` in react
tmeasday Oct 10, 2023
ce0a59c
Add some extra stuff to composed stories
tmeasday Oct 10, 2023
2ec29e4
Export `PreviewWithSelection`
tmeasday Oct 10, 2023
ce73088
Working new version
tmeasday Oct 11, 2023
9a2f5d4
Call setArgs in Preview
tmeasday Oct 11, 2023
3e0a654
Add simple mocking
shilman Oct 12, 2023
54f2663
Fix README
shilman Oct 12, 2023
f0a1789
Fix plugin tests / types
shilman Oct 12, 2023
daf8e72
Hack types for publish
shilman Oct 12, 2023
c9ba8c4
More TS wrangling
shilman Oct 12, 2023
ad13e90
Fix dependencies
shilman Oct 12, 2023
52cbbfc
Remove dead code to fix build
shilman Oct 12, 2023
38fb3c6
Cleanup generated Preview component
tmeasday Oct 26, 2023
7a7677a
Handle no framework.options specified
shilman Oct 27, 2023
830916e
Add SB-aware function and module mocking
shilman Oct 27, 2023
7ac0147
Merge branch 'tom/nextjs-server' into shilman/nextjs-server-mock
shilman Oct 31, 2023
9ac58a2
Fix Mock.module
shilman Oct 31, 2023
b18bd92
Re-export dependencies used by codegen
shilman Nov 3, 2023
19701f4
Start SB from next process and provide easy config
tmeasday Nov 3, 2023
e66e462
Small fixes
tmeasday Nov 5, 2023
8406fc1
Create a post bundle script for preview-api
tmeasday Nov 6, 2023
a9df89f
Update next version range
shilman Nov 6, 2023
5d91a9f
Fix generated types & import preview from relative dir
shilman Nov 6, 2023
7f15dc2
Revert "Update next version range"
shilman Nov 6, 2023
9ff737f
Allow passing no options to `withStorybook()`
tmeasday Nov 6, 2023
c34bd87
NextJS-server: Fix storybook process management
shilman Nov 7, 2023
ccc68a2
Fix types
shilman Nov 7, 2023
397c2ca
CLI: Add nextjs-server generator
shilman Nov 7, 2023
8d817e3
Automate more of the installation
shilman Nov 8, 2023
ea66c9c
Fix init
shilman Nov 8, 2023
527b332
Merge pull request #24741 from storybookjs/shilman/nextjs-server-cli
shilman Nov 8, 2023
32ab029
Merge branch 'shilman/nextjs-server-mock' into shilman/nextjs-server-…
shilman Nov 8, 2023
0e9b8a7
Merge pull request #24740 from storybookjs/shilman/nextjs-server-proc…
shilman Nov 8, 2023
3b3ffac
Don't add serverActions
shilman Nov 8, 2023
8889347
Remove Configure.mdx
shilman Nov 8, 2023
bb0e7d7
Pages directory working
tmeasday Nov 9, 2023
7a70087
NextJS-server: Remove vite and server dependencies
shilman Nov 13, 2023
a8326d2
Fix lockfile
shilman Nov 13, 2023
a0794c9
More cleanup
shilman Nov 13, 2023
347fb4f
More fixes
shilman Nov 13, 2023
0715a10
Merge pull request #24819 from storybookjs/shilman/nextjs-server-remo…
shilman Nov 13, 2023
5a28806
NextJS-server: Generate route group for storybook
shilman Nov 13, 2023
2ada0f9
Support nested or root layouts in SB group
shilman Nov 13, 2023
83be212
Fix typo
shilman Nov 13, 2023
5859dec
Minor tweak
shilman Nov 13, 2023
8acfdc6
NextJS-server: Fix CLI template generation
shilman Nov 13, 2023
397014c
Fix gitignore
shilman Nov 14, 2023
da48fb1
Merge pull request #24820 from storybookjs/shilman/nextjs-route-groups
shilman Nov 14, 2023
6b49c65
Add preview annotations to generated file
tmeasday Nov 15, 2023
56f64f9
New pages dir approach
tmeasday Nov 15, 2023
32dd6cc
Don't need this
tmeasday Nov 15, 2023
4cefd54
Fix typo
shilman Nov 15, 2023
7bd4d2d
Add typesVersions to fix TS errors
shilman Nov 15, 2023
520c653
Fix TS errors
shilman Nov 15, 2023
3dd5eb3
Simplify
tmeasday Nov 15, 2023
d7b9bfc
Merge remote-tracking branch 'origin/shilman/nextjs-server-mock' into…
tmeasday Nov 15, 2023
1c8a043
Simplify preview to work around react issue
tmeasday Nov 15, 2023
53ef888
Comment change
tmeasday Nov 15, 2023
d241a3e
Remove useEffect stub
shilman Nov 15, 2023
a1ef33e
Pull `importFn` out of preview
tmeasday Nov 16, 2023
d4be434
Small fixes
tmeasday Nov 16, 2023
0a41a08
Merge branch 'tom/support-pages-dir-2' of https://github.com/storyboo…
tmeasday Nov 16, 2023
8b83e8d
Fix regression on app dir
shilman Nov 20, 2023
5cf37db
Refactor app/pages indexers & add auto-detect of appDir
shilman Nov 20, 2023
b1826b1
Fix bad directory name
tmeasday Nov 20, 2023
f2d344d
Fix app dir handling
shilman Nov 20, 2023
5f8f171
Merge pull request #24908 from storybookjs/shilman/nextjs-refactor-in…
shilman Nov 21, 2023
8bef350
Merge pull request #24844 from storybookjs/tom/support-pages-dir-2
shilman Nov 21, 2023
346bba7
NextJS: Call storybook binary directly
shilman Nov 21, 2023
099f47d
NextJS: Verify that NextJS is running on the desired port & fail fast
shilman Nov 21, 2023
878677e
NextJS: Try to fix layout groups
shilman Nov 23, 2023
6f54316
Merge pull request #24928 from storybookjs/shilman/nextjs-call-storyb…
shilman Nov 23, 2023
dc0980d
Merge pull request #24956 from storybookjs/shilman/fix-nextjs-layout-…
shilman Nov 23, 2023
17b74b9
Don't need to set the `x-frame-options` header any more
tmeasday Nov 23, 2023
624c605
Don't preload preview runtime if there is no preview
tmeasday Nov 23, 2023
dab5f8d
Allow setting various directory names
tmeasday Nov 23, 2023
35a8292
Use a proper web view to give loading UI
tmeasday Nov 23, 2023
99a0e69
Merge pull request #24962 from storybookjs/tom/loading-ui
shilman Nov 24, 2023
191688a
Merge pull request #24957 from storybookjs/tom/fix-warnings
shilman Nov 24, 2023
8d040c2
Merge branch 'shilman/nextjs-server-mock' into shilman/nextjs-verify-…
shilman Nov 24, 2023
e2d69f9
Added parent pid check & previewPath configuration
shilman Nov 24, 2023
a234474
Ensure pid file only written once
shilman Nov 27, 2023
3011241
Merge pull request #24930 from storybookjs/shilman/nextjs-verify-port
shilman Nov 27, 2023
efd64a2
NextJS: Turn withStorybook into module export for ergonomics
shilman Nov 29, 2023
ea03eca
Add TS types for future auto-documentation
shilman Nov 29, 2023
9a892d3
Tweak descriptions
shilman Nov 29, 2023
d63baba
Merge pull request #25027 from storybookjs/shilman/nextjs-default-exp…
shilman Nov 29, 2023
4329a78
Merge branch 'next' into shilman/nextjs-server-mock
shilman Nov 29, 2023
92490ae
Add missing type
shilman Nov 29, 2023
0c6319d
Merge branch 'next' into shilman/nextjs-server-mock
shilman Nov 29, 2023
13ff156
Refresh yarn.lock
shilman Nov 29, 2023
9b47b66
Fix preset types
shilman Nov 29, 2023
83c4346
Update yarn.lock
shilman Nov 29, 2023
48644e1
Use `next/navigation.js`
tmeasday Nov 30, 2023
c98bddd
NextJS: Add env var for verifyPort delay
shilman Nov 30, 2023
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
3 changes: 2 additions & 1 deletion code/builders/builder-manager/src/utils/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const renderHTML = async (
refs: Promise<Record<string, Ref>>,
logLevel: Promise<string>,
docsOptions: Promise<DocsOptions>,
{ versionCheck, previewUrl, configType }: Options
{ versionCheck, previewUrl, configType, ignorePreview }: Options
) => {
const titleRef = await title;
const templateRef = await template;
Expand All @@ -54,5 +54,6 @@ export const renderHTML = async (
PREVIEW_URL: JSON.stringify(previewUrl, null, 2), // global preview URL
},
head: (await customHead) || '',
ignorePreview,
});
};
2 changes: 2 additions & 0 deletions code/builders/builder-manager/templates/template.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@
import './sb-manager/runtime.js';
</script>

<% if (!ignorePreview) { %>
<link href="./sb-preview/runtime.js" rel="prefetch" as="script" />
<% } %>
</body>
</html>
3 changes: 3 additions & 0 deletions code/frameworks/nextjs-server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Storybook NextJS Server

https://chromatic-ui.notion.site/Storybook-NextJS-Server-df380c489bae4a5e9d6f326d703e4c4c?pvs=4
7 changes: 7 additions & 0 deletions code/frameworks/nextjs-server/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const path = require('path');
const baseConfig = require('../../jest.config.node');

module.exports = {
...baseConfig,
displayName: __dirname.split(path.sep).slice(-2).join(path.posix.sep),
};
135 changes: 135 additions & 0 deletions code/frameworks/nextjs-server/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
{
"name": "@storybook/nextjs-server",
"version": "8.0.0-alpha.0",
"description": "Storybook for NextJS Server: Server-side rendering.",
"keywords": [
"storybook"
],
"homepage": "https://github.com/storybookjs/storybook/tree/next/code/frameworks/nextjs-server",
"bugs": {
"url": "https://github.com/storybookjs/storybook/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/storybookjs/storybook.git",
"directory": "code/frameworks/nextjs-server"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/storybook"
},
"license": "MIT",
"exports": {
".": {
"types": "./dist/index.d.ts",
"node": "./dist/index.js",
"require": "./dist/index.js",
"import": "./dist/index.mjs"
},
"./preset": {
"types": "./dist/preset.d.ts",
"require": "./dist/preset.js"
},
"./plugin": {
"types": "./dist/plugin.d.ts",
"require": "./dist/plugin.js"
},
"./mock": {
"types": "./dist/mock.d.ts",
"node": "./dist/mock.js",
"require": "./dist/mock.js",
"import": "./dist/mock.mjs"
},
"./next-config": {
"node": "./dist/next-config.js"
},
"./pages": {
"types": "./dist/pages/index.d.ts",
"import": "./dist/pages/index.mjs"
},
"./channels": {
"types": "./dist/reexports/channels.d.ts",
"node": "./dist/reexports/channels.js",
"require": "./dist/reexports/channels.js",
"import": "./dist/reexports/channels.mjs"
},
"./core-events": {
"types": "./dist/reexports/core-events.d.ts",
"node": "./dist/reexports/core-events.js",
"require": "./dist/reexports/core-events.js",
"import": "./dist/reexports/core-events.mjs"
},
"./preview-api": {
"types": "./dist/reexports/preview-api.d.ts",
"node": "./dist/reexports/preview-api.js",
"require": "./dist/reexports/preview-api.js",
"import": "./dist/reexports/preview-api.mjs"
},
"./types": {
"types": "./dist/reexports/types.d.ts"
},
"./package.json": "./package.json"
},
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"files": [
"dist/**/*",
"template/**/*",
"README.md",
"*.js",
"*.d.ts"
],
"scripts": {
"check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts",
"prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts"
},
"dependencies": {
"@babel/core": "^7.22.9",
"@babel/types": "^7.22.5",
"@storybook/channels": "workspace:*",
"@storybook/core-common": "workspace:*",
"@storybook/core-events": "workspace:*",
"@storybook/core-server": "workspace:*",
"@storybook/csf-tools": "workspace:*",
"@storybook/node-logger": "workspace:*",
"@storybook/preview-api": "workspace:*",
"@storybook/react": "workspace:*",
"@storybook/types": "workspace:*",
"@types/node": "^18.0.0",
"fs-extra": "^11.1.0",
"ts-dedent": "^2.0.0",
"unplugin": "^1.3.1"
},
"devDependencies": {
"typescript": "~4.9.3"
},
"peerDependencies": {
"next": "^13",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
},
"engines": {
"node": ">=16.0.0"
},
"publishConfig": {
"access": "public"
},
"bundler": {
"entries": [
"./src/index.ts",
"./src/null-builder.ts",
"./src/null-renderer.ts",
"./src/preset.ts",
"./src/mock.ts",
"./src/next-config.cts",
"./src/pages/index.ts",
"./src/reexports/channels.ts",
"./src/reexports/core-events.ts",
"./src/reexports/preview-api.ts",
"./src/reexports/types.ts"
],
"platform": "node"
},
"gitHead": "72ae6b0d965d1ae596159cdb15109c5e13376d78"
}
1 change: 1 addition & 0 deletions code/frameworks/nextjs-server/preset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('./dist/preset');
6 changes: 6 additions & 0 deletions code/frameworks/nextjs-server/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "@storybook/nextjs-server",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"implicitDependencies": [],
"type": "library"
}
1 change: 1 addition & 0 deletions code/frameworks/nextjs-server/server.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './dist/server/index.d';
1 change: 1 addition & 0 deletions code/frameworks/nextjs-server/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './dist/server';
1 change: 1 addition & 0 deletions code/frameworks/nextjs-server/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './types';
181 changes: 181 additions & 0 deletions code/frameworks/nextjs-server/src/indexers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
import { cp, readFile, writeFile } from 'fs/promises';
import { ensureDir, exists } from 'fs-extra';
import { join, relative, resolve } from 'path';
import { dedent } from 'ts-dedent';
import type { Indexer, PreviewAnnotation } from '@storybook/types';
import { loadCsf } from '@storybook/csf-tools';
import type { StorybookNextJSOptions } from './types';

const LAYOUT_FILES = ['layout.tsx', 'layout.jsx'];

export const appIndexer = (
allPreviewAnnotations: PreviewAnnotation[],
{ previewPath }: StorybookNextJSOptions
): Indexer => {
return {
test: /(stories|story)\.[tj]sx?$/,
createIndex: async (fileName, opts) => {
console.log('indexing', fileName);
const code = (await readFile(fileName, 'utf-8')).toString();
const csf = await loadCsf(code, { ...opts, fileName }).parse();

const inputAppDir = resolve(__dirname, '../template/app');
const inputGroupDir = join(inputAppDir, 'groupLayouts');
const inputStorybookDir = join(inputAppDir, 'storybook-preview');
const appDir = join(process.cwd(), 'app');
const sbGroupDir = join(appDir, '(sb)');
const storybookDir = join(sbGroupDir, previewPath);
await ensureDir(storybookDir);

try {
await cp(inputStorybookDir, storybookDir, { recursive: true });
const hasRootLayout = await Promise.any(
LAYOUT_FILES.map((file) => exists(join(appDir, file)))
);
const inputLayout = hasRootLayout ? 'layout-nested.tsx' : 'layout-root.tsx';
await cp(`${inputGroupDir}/${inputLayout}`, join(sbGroupDir, 'layout.tsx'));

const routeLayoutTsx = dedent`import type { PropsWithChildren } from 'react';
import React from 'react';
import { Storybook } from './components/Storybook';

export default function NestedLayout({ children }: PropsWithChildren<{}>) {
return <Storybook previewPath="${previewPath}">{children}</Storybook>;
}`;
const routeLayoutFile = join(storybookDir, 'layout.tsx');
await writeFile(routeLayoutFile, routeLayoutTsx);
} catch (err) {
// FIXME: assume we've copied already
// console.log({ err });
}

await Promise.all(
csf.stories.map(async (story) => {
const storyDir = join(storybookDir, story.id);
await ensureDir(storyDir);
const relativeStoryPath = relative(storyDir, fileName).replace(/\.tsx?$/, '');
const { exportName } = story;

const pageTsx = dedent`
import React from 'react';
import { composeStory } from '@storybook/react/testing-api';
import { getArgs } from '../components/args';
import { Prepare, StoryAnnotations } from '../components/Prepare';
import { Args } from '@storybook/react';

const page = async () => {
const stories = await import('${relativeStoryPath}');
const projectAnnotations = {};
const Composed = composeStory(stories.${exportName}, stories.default, projectAnnotations?.default || {}, '${exportName}');
const extraArgs = await getArgs(Composed.id);

const { id, parameters, argTypes, initialArgs } = Composed;
const args = { ...initialArgs, ...extraArgs };

const storyAnnotations: StoryAnnotations<Args> = {
id,
parameters,
argTypes,
initialArgs,
args,
};
return (
<>
<Prepare story={storyAnnotations} />
{/* @ts-ignore TODO -- why? */}
<Composed {...extraArgs} />
</>
);
};
export default page;
`;

const pageFile = join(storyDir, 'page.tsx');
await writeFile(pageFile, pageTsx);
})
);

return csf.indexInputs;
},
};
};

export const pagesIndexer = (
allPreviewAnnotations: PreviewAnnotation[],
{ previewPath }: StorybookNextJSOptions
): Indexer => {
const workingDir = process.cwd(); // TODO we should probably put this on the preset options

return {
test: /(stories|story)\.[tj]sx?$/,
createIndex: async (fileName, opts) => {
console.log('indexing', fileName);
const code = (await readFile(fileName, 'utf-8')).toString();
const csf = await loadCsf(code, { ...opts, fileName }).parse();

const routeDir = 'pages';
const storybookDir = join(process.cwd(), routeDir, previewPath);
await ensureDir(storybookDir);

const indexTsx = dedent`
import React from 'react';
import { Storybook } from './components/Storybook';

const page = () => <Storybook />;
export default page;
`;
const indexFile = join(storybookDir, 'index.tsx');
await writeFile(indexFile, indexTsx);

const projectAnnotationImports = allPreviewAnnotations
.map((path, index) => `const projectAnnotations${index} = await import('${path}');`)
.join('\n');

const projectAnnotationArray = allPreviewAnnotations
.map((_, index) => `projectAnnotations${index}`)
.join(',');

const storybookTsx = dedent`
import React, { useEffect } from 'react';
import { composeConfigs } from '@storybook/preview-api';
import { Preview } from '@storybook/nextjs-server/pages';

const getProjectAnnotations = async () => {
${projectAnnotationImports}
return composeConfigs([${projectAnnotationArray}]);
}

export const Storybook = () => (
<Preview getProjectAnnotations={getProjectAnnotations} previewPath="${previewPath}" />
);
`;

const componentsDir = join(storybookDir, 'components');
await ensureDir(componentsDir);
const storybookFile = join(componentsDir, 'Storybook.tsx');
await writeFile(storybookFile, storybookTsx);

const componentId = csf.stories[0].id.split('--')[0];
const relativeStoryPath = relative(storybookDir, fileName).replace(/\.tsx?$/, '');
const importPath = relative(workingDir, fileName).replace(/^([^./])/, './$1');

const csfImportTsx = dedent`
import React from 'react';
import { Storybook } from './components/Storybook';
import * as stories from '${relativeStoryPath}';

if (typeof window !== 'undefined') {
window._storybook_onImport('${importPath}', stories);
}

const page = () => <Storybook />;
export default page;
`;

const csfImportFile = join(storybookDir, `${componentId}.tsx`);
await writeFile(csfImportFile, csfImportTsx);

return csf.indexInputs;
},
};
};
Loading