Skip to content

Commit 506c671

Browse files
committed
WIP
1 parent b960901 commit 506c671

16 files changed

+11015
-7250
lines changed

package-lock.json

+10,796-7,077
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/storybook-builder/package.json

+6-7
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
"main": "dist/index.js",
1717
"exports": {
1818
".": {
19+
"types": "./index.d.ts",
1920
"require": "./dist/index.js",
20-
"import": "./index.mjs",
21-
"types": "./index.d.ts"
21+
"import": "./index.mjs"
2222
}
2323
},
2424
"engines": {
@@ -47,10 +47,10 @@
4747
"esm"
4848
],
4949
"dependencies": {
50+
"@mdx-js/mdx": "^3.0.0",
5051
"@rollup/plugin-node-resolve": "^15.1.0",
5152
"@rollup/pluginutils": "^5.0.2",
5253
"@storybook/core-common": "^8.5.0",
53-
"@storybook/mdx2-csf": "^1.0.0",
5454
"@storybook/node-logger": "^8.5.0",
5555
"@storybook/preview": "^8.5.0",
5656
"@web/config-loader": "^0.3.2",
@@ -62,15 +62,14 @@
6262
"cjs-module-lexer": "^1.2.3",
6363
"es-module-lexer": "^1.2.1",
6464
"esbuild": "^0.24.0",
65-
"express": "^4.21.2",
66-
"fs-extra": "^11.1.1",
6765
"glob-promise": "^6.0.3",
6866
"lodash-es": "^4.17.21",
6967
"path-browserify": "^1.0.1",
70-
"remark-external-links": "^8.0.0",
71-
"remark-slug": "^6.0.0",
68+
"rehype-external-links": "^3.0.0",
69+
"rehype-slug": "^6.0.0",
7270
"rollup": "^4.4.1",
7371
"rollup-plugin-external-globals": "^0.9.0",
72+
"sirv": "^2.0.4",
7473
"slash": "^5.1.0"
7574
},
7675
"devDependencies": {

packages/storybook-builder/src/esbuild-plugin-commonjs-named-exports.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { Plugin } from 'esbuild';
2-
import { readFile } from 'fs-extra';
3-
import { dirname, relative } from 'path';
2+
import { readFile } from 'node:fs/promises';
3+
import { dirname, relative } from 'node:path';
44

55
export function esbuildPluginCommonjsNamedExports(modules: string[]): Plugin {
66
return {
@@ -65,7 +65,7 @@ export function esbuildPluginCommonjsNamedExports(modules: string[]): Plugin {
6565
});
6666

6767
async function getNamedExports(path: string): Promise<string[]> {
68-
const source = await readFile(path, 'utf8');
68+
const source = await readFile(path, { encoding: 'utf8' });
6969

7070
let exports: string[] = [];
7171
let reexports: string[] = [];

packages/storybook-builder/src/generate-app-script.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,18 @@ ${previewAnnotationURLs
3333
`.trim();
3434

3535
return `
36-
import { composeConfigs, PreviewWeb, ClientApi } from '@storybook/preview-api';
36+
import { setup } from '@storybook/core/preview/runtime';
3737
import '${virtualSetupAddonsFilename}';
38+
39+
setup();
40+
41+
import { composeConfigs, PreviewWeb, ClientApi } from '@storybook/preview-api';
3842
import { importFn } from '${virtualStoriesFilename}';
3943
4044
${getPreviewAnnotationsFunction}
4145
42-
window.__STORYBOOK_PREVIEW__ = window.__STORYBOOK_PREVIEW__ || new PreviewWeb();
46+
window.__STORYBOOK_PREVIEW__ = window.__STORYBOOK_PREVIEW__ || new PreviewWeb(importFn, getProjectAnnotations);
4347
4448
window.__STORYBOOK_STORY_STORE__ = window.__STORYBOOK_STORY_STORE__ || window.__STORYBOOK_PREVIEW__.storyStore;
45-
window.__STORYBOOK_CLIENT_API__ = window.__STORYBOOK_CLIENT_API__ || new ClientApi({ storyStore: window.__STORYBOOK_PREVIEW__.storyStore });
46-
window.__STORYBOOK_PREVIEW__.initialize({ importFn, getProjectAnnotations });
4749
`.trim();
4850
}
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,52 @@
11
import { normalizeStories } from '@storybook/core-common';
2-
import type { CoreConfig, Options } from '@storybook/types';
3-
import { readFile } from 'fs-extra';
2+
import type { DocsOptions, Options, TagsOptions } from '@storybook/types';
3+
import { readFile } from 'node:fs/promises';
44
import { virtualAppFilename } from './virtual-file-names.js';
55

66
export type PreviewHtml = string | undefined;
77

88
export async function generateIframeHtml(options: Options): Promise<string> {
9-
const iframeHtmlTemplate = await readFile(
10-
require.resolve('../static/iframe-template.html'),
11-
'utf-8',
12-
);
13-
const { configType, features, presets, serverChannelUrl } = options;
9+
const iframeHtmlTemplate = await readFile(require.resolve('../static/iframe-template.html'), {
10+
encoding: 'utf-8',
11+
});
12+
const { configType, features, presets } = options;
13+
const build = await presets.apply('build');
1414
const frameworkOptions = await presets.apply<Record<string, any> | null>('frameworkOptions');
1515
const headHtmlSnippet = await presets.apply<PreviewHtml>('previewHead');
1616
const bodyHtmlSnippet = await presets.apply<PreviewHtml>('previewBody');
1717
const logLevel = await presets.apply('logLevel', undefined);
18-
// const docsOptions = await presets.apply<DocsOptions>('docs');
19-
const coreOptions = await presets.apply<CoreConfig>('core');
18+
const docsOptions = await presets.apply<DocsOptions>('docs');
19+
const tagsOptions = await presets.apply<TagsOptions>('tags');
20+
const coreOptions = await presets.apply('core');
2021
const stories = normalizeStories(await options.presets.apply('stories', [], options), {
2122
configDir: options.configDir,
2223
workingDir: process.cwd(),
2324
}).map(specifier => ({
2425
...specifier,
2526
importPathMatcher: specifier.importPathMatcher.source,
2627
}));
27-
return (
28-
iframeHtmlTemplate
29-
.replace('[CONFIG_TYPE HERE]', configType || '')
30-
.replace('[LOGLEVEL HERE]', logLevel || '')
31-
.replace(`'[FRAMEWORK_OPTIONS HERE]'`, JSON.stringify(frameworkOptions))
32-
.replace(
33-
`'[CHANNEL_OPTIONS HERE]'`,
34-
JSON.stringify(coreOptions && coreOptions.channelOptions ? coreOptions.channelOptions : {}),
35-
)
36-
.replace(`'[FEATURES HERE]'`, JSON.stringify(features || {}))
37-
.replace(`'[STORIES HERE]'`, JSON.stringify(stories || {}))
38-
// .replace(`'[DOCS_OPTIONS HERE]'`, JSON.stringify(docsOptions || {}))
39-
.replace(`'[SERVER_CHANNEL_URL HERE]'`, JSON.stringify(serverChannelUrl))
40-
.replace('<!-- [HEAD HTML SNIPPET HERE] -->', headHtmlSnippet || '')
41-
.replace('<!-- [BODY HTML SNIPPET HERE] -->', bodyHtmlSnippet || '')
42-
.replace(`[APP MODULE SRC HERE]`, virtualAppFilename)
43-
);
28+
const otherGlobals = {
29+
...(build?.test?.disableBlocks ? { __STORYBOOK_BLOCKS_EMPTY_MODULE__: {} } : {}),
30+
};
31+
return iframeHtmlTemplate
32+
.replace('[CONFIG_TYPE HERE]', configType || '')
33+
.replace('[LOGLEVEL HERE]', logLevel || '')
34+
.replace(`'[FRAMEWORK_OPTIONS HERE]'`, JSON.stringify(frameworkOptions))
35+
.replace(
36+
`('OTHER_GLOBLALS HERE');`,
37+
Object.entries(otherGlobals)
38+
.map(([k, v]) => `window["${k}"] = ${JSON.stringify(v)};`)
39+
.join(''),
40+
)
41+
.replace(
42+
`'[CHANNEL_OPTIONS HERE]'`,
43+
JSON.stringify(coreOptions && coreOptions.channelOptions ? coreOptions.channelOptions : {}),
44+
)
45+
.replace(`'[FEATURES HERE]'`, JSON.stringify(features || {}))
46+
.replace(`'[STORIES HERE]'`, JSON.stringify(stories || {}))
47+
.replace(`'[DOCS_OPTIONS HERE]'`, JSON.stringify(docsOptions || {}))
48+
.replace(`'[TAGS_OPTIONS HERE]'`, JSON.stringify(tagsOptions || {}))
49+
.replace('<!-- [HEAD HTML SNIPPET HERE] -->', headHtmlSnippet || '')
50+
.replace('<!-- [BODY HTML SNIPPET HERE] -->', bodyHtmlSnippet || '')
51+
.replace(`[APP MODULE SRC HERE]`, virtualAppFilename);
4452
}

packages/storybook-builder/src/generate-setup-addons-script.ts

+5-9
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,15 @@
22

33
export async function generateSetupAddonsScript() {
44
return `
5-
import { createChannel as createPostMessageChannel } from '@storybook/channel-postmessage';
6-
import { createChannel as createWebSocketChannel } from '@storybook/channel-websocket';
7-
import { addons } from '@storybook/preview-api';
5+
import { createBrowserChannel } from 'storybook/internal/channels';
6+
import { addons } from 'storybook/internal/preview-api';
87
9-
const channel = createPostMessageChannel({ page: 'preview' });
8+
const channel = createBrowserChannel({ page: 'preview' });
109
addons.setChannel(channel);
1110
window.__STORYBOOK_ADDONS_CHANNEL__ = channel;
1211
13-
const { SERVER_CHANNEL_URL } = globalThis;
14-
if (SERVER_CHANNEL_URL) {
15-
const serverChannel = createWebSocketChannel({ url: SERVER_CHANNEL_URL });
16-
addons.setServerChannel(serverChannel);
17-
window.__STORYBOOK_SERVER_CHANNEL__ = serverChannel;
12+
if (window.CONFIG_TYPE === 'DEVELOPMENT'){
13+
window.__STORYBOOK_SERVER_CHANNEL__ = channel;
1814
}
1915
`.trim();
2016
}

packages/storybook-builder/src/generate-stories-script.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { normalizePath } from '@rollup/pluginutils';
44
import { logger } from '@storybook/node-logger';
55
import type { Options } from '@storybook/types';
6-
import * as path from 'path';
6+
import { extname, relative } from 'node:path';
77
import { listStories } from './list-stories.js';
88

99
/**
@@ -27,8 +27,8 @@ export async function generateStoriesScript(options: Options) {
2727
*/
2828
async function toImportFn(stories: string[]) {
2929
const objectEntries = stories.map(file => {
30-
const ext = path.extname(file);
31-
const relativePath = normalizePath(path.relative(process.cwd(), file));
30+
const ext = extname(file);
31+
const relativePath = normalizePath(relative(process.cwd(), file));
3232
if (!['.js', '.jsx', '.ts', '.tsx', '.mdx', '.svelte', '.vue'].includes(ext)) {
3333
logger.warn(`Cannot process ${ext} file with storyStoreV7: ${relativePath}`);
3434
}

packages/storybook-builder/src/get-node-module-dir.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { dirname } from 'path';
1+
import { dirname } from 'node:path';
22

33
export function getNodeModuleDir(moduleName: string): string {
44
return dirname(require.resolve(`${moduleName}/package.json`));

packages/storybook-builder/src/index.ts

+19-8
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import { DevServerConfig, mergeConfigs, startDevServer } from '@web/dev-server';
99
import type { DevServer } from '@web/dev-server-core';
1010
import { fromRollup } from '@web/dev-server-rollup';
1111
import { rollupPluginHTML } from '@web/rollup-plugin-html';
12-
import express from 'express';
13-
import * as fs from 'fs-extra';
14-
import { join, parse, resolve } from 'path';
12+
import { cp } from 'node:fs/promises';
13+
import { join, parse, resolve } from 'node:path';
14+
import sirv from 'sirv';
1515
import { OutputOptions, RollupBuild, RollupOptions, rollup } from 'rollup';
1616
import rollupPluginExternalGlobals from 'rollup-plugin-external-globals';
1717
import { generateIframeHtml } from './generate-iframe-html.js';
@@ -58,9 +58,19 @@ export const bail: WdsBuilder['bail'] = async () => {
5858
};
5959

6060
export const start: WdsBuilder['start'] = async ({ startTime, options, router, server }) => {
61-
const previewDirOrigin = join(getNodeModuleDir('@storybook/preview'), 'dist');
62-
router.use('/sb-preview', express.static(previewDirOrigin, { immutable: true, maxAge: '5m' }));
63-
router.use(`/${PREBUNDLED_MODULES_DIR}`, express.static(resolve(`./${PREBUNDLED_MODULES_DIR}`)));
61+
const previewDirOrigin = join(getNodeModuleDir('@storybook/core'), 'dist', 'preview');
62+
router.use(
63+
'/sb-preview',
64+
sirv(previewDirOrigin, {
65+
maxAge: 300000,
66+
dev: true,
67+
immutable: true,
68+
}),
69+
);
70+
router.use(
71+
`/${PREBUNDLED_MODULES_DIR}`,
72+
sirv(resolve(`./${PREBUNDLED_MODULES_DIR}`), { dev: true }),
73+
);
6474

6575
const env = await options.presets.apply<Record<string, string>>('env');
6676

@@ -184,16 +194,17 @@ export const build: WdsBuilder['build'] = async ({ startTime, options }) => {
184194
logger.trace({ message: '=> Preview built', time: process.hrtime(startTime) });
185195
})();
186196

187-
const previewDirOrigin = join(getNodeModuleDir('@storybook/preview'), 'dist');
197+
const previewDirOrigin = join(getNodeModuleDir('@storybook/core'), 'dist', 'preview');
188198
const previewDirTarget = join(options.outputDir || '', `sb-preview`);
189-
const previewFiles = fs.copy(previewDirOrigin, previewDirTarget, {
199+
const previewFiles = cp(previewDirOrigin, previewDirTarget, {
190200
filter: src => {
191201
const { ext } = parse(src);
192202
if (ext) {
193203
return ext === '.js';
194204
}
195205
return true;
196206
},
207+
recursive: true,
197208
});
198209

199210
await Promise.all([rollupBuild, previewFiles]);

packages/storybook-builder/src/list-stories.ts

+3-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { normalizeStories } from '@storybook/core-common';
44
import type { Options } from '@storybook/types';
55
import { promise as glob } from 'glob-promise';
6-
import * as path from 'path';
6+
import { isAbsolute, join } from 'node:path';
77

88
export async function listStories(options: Options) {
99
const slash = (await import('slash')).default; // for CJS compatibility
@@ -14,10 +14,8 @@ export async function listStories(options: Options) {
1414
configDir: options.configDir,
1515
workingDir: options.configDir,
1616
}).map(({ directory, files }) => {
17-
const pattern = path.join(directory, files);
18-
const absolutePattern = path.isAbsolute(pattern)
19-
? pattern
20-
: path.join(options.configDir, pattern);
17+
const pattern = join(directory, files);
18+
const absolutePattern = isAbsolute(pattern) ? pattern : join(options.configDir, pattern);
2119

2220
return glob(slash(absolutePattern), { follow: true });
2321
}),
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { compile } from '@storybook/mdx2-csf';
21
import type { Options } from '@storybook/types';
3-
import { exists, readFile } from 'fs-extra';
4-
import { isAbsolute, sep } from 'path';
5-
import remarkExternalLinks from 'remark-external-links';
6-
import remarkSlug from 'remark-slug';
2+
import { compile } from '@mdx-js/mdx';
3+
import { readFile } from 'node:fs/promises';
4+
import { dirname, join, sep } from 'node:path';
5+
import rehypeExternalLinks from 'rehype-external-links';
6+
import rehypeSlug from 'rehype-slug';
77
import type { Plugin } from 'rollup';
88

99
export function rollupPluginMdx(storybookOptions: Options): Plugin {
@@ -26,39 +26,37 @@ export function rollupPluginMdx(storybookOptions: Options): Plugin {
2626
}
2727
},
2828

29-
async load(id) {
29+
async load(id: string) {
3030
if (!id.endsWith('.mdx.js')) return;
3131

3232
const mdxPath = id.replace(/\.js$/, '');
33-
const mdxCode = await readFile(mdxPath.split('/').join(sep), 'utf8');
33+
const mdxCode = await readFile(mdxPath.split('/').join(sep), { encoding: 'utf8' });
3434

3535
const mdxLoaderOptions = await storybookOptions.presets.apply('mdxLoaderOptions', {
3636
...mdxPluginOptions,
3737
mdxCompileOptions: {
38-
providerImportSource: '@mdx-js/react',
39-
...mdxPluginOptions?.mdxCompileOptions,
40-
remarkPlugins: [remarkSlug, remarkExternalLinks].concat(
41-
mdxPluginOptions?.mdxCompileOptions?.remarkPlugins ?? [],
38+
// TODO(storybook): this is done by Storybook in 3 different places:
39+
// 1. addon-essential preset (so not always working for all users who install plugins individually)
40+
// 2. addon-docs vite plugin (so not applicable to our builder)
41+
// 3. addon-docs webpack loader (also no applicable for us)
42+
// so we need to do this here too, for people who individually include addon-docs
43+
providerImportSource: join(
44+
dirname(require.resolve('@storybook/addon-docs/package.json')),
45+
'/dist/shims/mdx-react-shim.mjs',
4246
),
47+
...mdxPluginOptions?.mdxCompileOptions,
48+
rehypePlugins: [
49+
...(mdxPluginOptions?.mdxCompileOptions?.rehypePlugins ?? []),
50+
rehypeSlug,
51+
rehypeExternalLinks,
52+
],
4353
},
4454
jsxOptions,
4555
});
4656

47-
// workaround for https://github.com/storybookjs/storybook/blob/v7.6.17/code/addons/essentials/src/docs/preset.ts#L10
48-
const { providerImportSource } = mdxLoaderOptions.mdxCompileOptions;
49-
if (isAbsolute(providerImportSource)) {
50-
const providerImportSourceWithExt = providerImportSource + '.mjs';
51-
if (await exists(providerImportSourceWithExt)) {
52-
mdxLoaderOptions.mdxCompileOptions.providerImportSource = providerImportSourceWithExt;
53-
}
54-
}
55-
56-
const jsCode = await compile(mdxCode, {
57-
skipCsf: true,
58-
...mdxLoaderOptions,
59-
});
57+
const mdxResult = await compile(mdxCode, mdxLoaderOptions.mdxCompileOptions);
6058

61-
return jsCode;
59+
return mdxResult.toString();
6260
},
6361
};
6462
}

0 commit comments

Comments
 (0)