@modern-js/storybook-builder 0.0.0-next-20230913035856
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +7 -0
- package/.turbo/turbo-build.log +6 -0
- package/CHANGELOG.md +24 -0
- package/LICENSE +21 -0
- package/dist/cjs/addons/components/modern.js +65 -0
- package/dist/cjs/addons/constants.js +11 -0
- package/dist/cjs/addons/index.js +20 -0
- package/dist/cjs/addons/preset/preview.js +14 -0
- package/dist/cjs/addons/type.js +4 -0
- package/dist/cjs/addons/withPluginRuntime.js +20 -0
- package/dist/cjs/build.js +109 -0
- package/dist/cjs/core.js +40 -0
- package/dist/cjs/docgen/actualNameHandler.js +34 -0
- package/dist/cjs/docgen/index.js +74 -0
- package/dist/cjs/docgen/loader.js +37 -0
- package/dist/cjs/docgen/process.js +39 -0
- package/dist/cjs/index.js +35 -0
- package/dist/cjs/plugin-storybook.js +316 -0
- package/dist/cjs/preset.js +61 -0
- package/dist/cjs/types.js +11 -0
- package/dist/cjs/utils.js +142 -0
- package/dist/esm/addons/components/modern.js +44 -0
- package/dist/esm/addons/constants.js +1 -0
- package/dist/esm/addons/index.js +5 -0
- package/dist/esm/addons/preset/preview.js +4 -0
- package/dist/esm/addons/type.js +1 -0
- package/dist/esm/addons/withPluginRuntime.js +10 -0
- package/dist/esm/build.js +83 -0
- package/dist/esm/core.js +30 -0
- package/dist/esm/docgen/actualNameHandler.js +24 -0
- package/dist/esm/docgen/index.js +54 -0
- package/dist/esm/docgen/loader.js +26 -0
- package/dist/esm/docgen/process.js +28 -0
- package/dist/esm/index.js +6 -0
- package/dist/esm/plugin-storybook.js +296 -0
- package/dist/esm/preset.js +36 -0
- package/dist/esm/types.js +1 -0
- package/dist/esm/utils.js +95 -0
- package/dist/types/addons/components/modern.d.ts +5 -0
- package/dist/types/addons/constants.d.ts +1 -0
- package/dist/types/addons/index.d.ts +2 -0
- package/dist/types/addons/preset/preview.d.ts +1 -0
- package/dist/types/addons/type.d.ts +4 -0
- package/dist/types/addons/withPluginRuntime.d.ts +2 -0
- package/dist/types/build.d.ts +7 -0
- package/dist/types/core.d.ts +4 -0
- package/dist/types/docgen/actualNameHandler.d.ts +14 -0
- package/dist/types/docgen/index.d.ts +9 -0
- package/dist/types/docgen/loader.d.ts +2 -0
- package/dist/types/docgen/process.d.ts +10 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/plugin-storybook.d.ts +6 -0
- package/dist/types/preset.d.ts +6 -0
- package/dist/types/types.d.ts +15 -0
- package/dist/types/utils.d.ts +12 -0
- package/index.js +24 -0
- package/modern.config.js +5 -0
- package/package.json +96 -0
- package/src/addons/components/modern.tsx +52 -0
- package/src/addons/constants.ts +1 -0
- package/src/addons/index.ts +8 -0
- package/src/addons/preset/preview.ts +3 -0
- package/src/addons/type.ts +4 -0
- package/src/addons/withPluginRuntime.ts +18 -0
- package/src/build.ts +119 -0
- package/src/core.ts +51 -0
- package/src/docgen/actualNameHandler.ts +57 -0
- package/src/docgen/index.ts +100 -0
- package/src/docgen/loader.ts +34 -0
- package/src/docgen/process.ts +44 -0
- package/src/index.ts +7 -0
- package/src/plugin-storybook.ts +462 -0
- package/src/preset.ts +59 -0
- package/src/types.ts +21 -0
- package/src/utils.ts +131 -0
- package/templates/preview.ejs +54 -0
- package/templates/virtualModuleModernEntry.js.handlebars +43 -0
- package/tsconfig.json +16 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
import { importers, resolver, handlers, parse } from 'react-docgen';
|
2
|
+
import type { DocumentationObject } from 'react-docgen/dist/Documentation';
|
3
|
+
import actualNameHandler from './actualNameHandler';
|
4
|
+
|
5
|
+
const defaultHandlers = Object.values(handlers).map(handler => handler);
|
6
|
+
const importer = importers.makeFsImporter();
|
7
|
+
|
8
|
+
export default ({
|
9
|
+
source,
|
10
|
+
map,
|
11
|
+
filename,
|
12
|
+
}: {
|
13
|
+
source: string;
|
14
|
+
map: string;
|
15
|
+
filename: string;
|
16
|
+
}) => {
|
17
|
+
try {
|
18
|
+
const results = parse(
|
19
|
+
source,
|
20
|
+
resolver.findAllExportedComponentDefinitions,
|
21
|
+
[...defaultHandlers, actualNameHandler],
|
22
|
+
{
|
23
|
+
filename,
|
24
|
+
importer,
|
25
|
+
},
|
26
|
+
) as DocumentationObject[];
|
27
|
+
|
28
|
+
const docgen = results
|
29
|
+
.map(result => {
|
30
|
+
// @ts-expect-error we know actualName is added by actualNameHandler, so it exist
|
31
|
+
const { actualName, ...docgenInfo } = result;
|
32
|
+
if (actualName) {
|
33
|
+
return `${actualName}.__docgenInfo=${JSON.stringify(docgenInfo)}`;
|
34
|
+
}
|
35
|
+
return '';
|
36
|
+
})
|
37
|
+
.filter(Boolean)
|
38
|
+
.join(';');
|
39
|
+
|
40
|
+
return [docgen, map];
|
41
|
+
} catch (e) {
|
42
|
+
return null;
|
43
|
+
}
|
44
|
+
};
|
package/src/index.ts
ADDED
@@ -0,0 +1,462 @@
|
|
1
|
+
/* eslint-disable max-lines */
|
2
|
+
import { isAbsolute, join, resolve } from 'path';
|
3
|
+
import { slash, watch, globby } from '@modern-js/utils';
|
4
|
+
import {
|
5
|
+
BuilderPlugin,
|
6
|
+
SharedBuilderConfig,
|
7
|
+
mergeBuilderConfig,
|
8
|
+
} from '@modern-js/builder-shared';
|
9
|
+
import { CompileOptions } from '@storybook/mdx2-csf';
|
10
|
+
import type {
|
11
|
+
CoreConfig,
|
12
|
+
DocsOptions,
|
13
|
+
Options,
|
14
|
+
PreviewAnnotation,
|
15
|
+
StoriesEntry,
|
16
|
+
} from '@storybook/types';
|
17
|
+
import {
|
18
|
+
normalizeStories,
|
19
|
+
stringifyProcessEnvs,
|
20
|
+
handlebars,
|
21
|
+
readTemplate,
|
22
|
+
loadPreviewOrConfigFile,
|
23
|
+
} from '@storybook/core-common';
|
24
|
+
import { globals } from '@storybook/preview/globals';
|
25
|
+
|
26
|
+
import type {
|
27
|
+
BuilderPluginAPI as WebpackAPI,
|
28
|
+
WebpackConfig,
|
29
|
+
} from '@modern-js/builder-webpack-provider';
|
30
|
+
import type {
|
31
|
+
BuilderPluginAPI as RspackAPI,
|
32
|
+
RspackConfig,
|
33
|
+
} from '@modern-js/builder-rspack-provider';
|
34
|
+
import { unplugin as csfPlugin } from '@storybook/csf-plugin';
|
35
|
+
import { minimatch } from 'minimatch';
|
36
|
+
import { AllBuilderConfig, FrameworkOptions } from './types';
|
37
|
+
import { toImportFn, virtualModule, maybeGetAbsolutePath } from './utils';
|
38
|
+
import { applyDocgenRspack, applyDocgenWebpack } from './docgen';
|
39
|
+
|
40
|
+
const STORIES_FILENAME = 'storybook-stories.js';
|
41
|
+
const STORYBOOK_CONFIG_ENTRY = 'storybook-config-entry.js';
|
42
|
+
|
43
|
+
const closeFn: (() => void | Promise<void>)[] = [];
|
44
|
+
const onClose = (f: () => void | Promise<void>) => {
|
45
|
+
closeFn.push(f);
|
46
|
+
};
|
47
|
+
|
48
|
+
export async function finalize() {
|
49
|
+
await Promise.all([closeFn.map(close => close())]);
|
50
|
+
}
|
51
|
+
|
52
|
+
export const pluginStorybook: (
|
53
|
+
cwd: string,
|
54
|
+
options: Options,
|
55
|
+
) => BuilderPlugin<WebpackAPI | RspackAPI> = (cwd, options) => {
|
56
|
+
return {
|
57
|
+
name: 'builder-plugin-storybook',
|
58
|
+
|
59
|
+
remove: ['builder-plugin-inline'],
|
60
|
+
|
61
|
+
async setup(api) {
|
62
|
+
const matchers: StoriesEntry[] = await options.presets.apply(
|
63
|
+
'stories',
|
64
|
+
[],
|
65
|
+
options,
|
66
|
+
);
|
67
|
+
|
68
|
+
const storyPatterns = normalizeStories(matchers, {
|
69
|
+
configDir: options.configDir,
|
70
|
+
workingDir: options.configDir,
|
71
|
+
}).map(({ directory, files }) => {
|
72
|
+
const pattern = join(directory, files);
|
73
|
+
const absolutePattern = isAbsolute(pattern)
|
74
|
+
? pattern
|
75
|
+
: join(options.configDir, pattern);
|
76
|
+
|
77
|
+
return absolutePattern;
|
78
|
+
});
|
79
|
+
|
80
|
+
api.modifyBuilderConfig(async builderConfig => {
|
81
|
+
// storybook needs a virtual entry,
|
82
|
+
// when new stories get created, the
|
83
|
+
// entry needs to be recauculated
|
84
|
+
await prepareStorybookModules(
|
85
|
+
api.context.cachePath,
|
86
|
+
cwd,
|
87
|
+
options,
|
88
|
+
builderConfig,
|
89
|
+
storyPatterns,
|
90
|
+
);
|
91
|
+
|
92
|
+
// storybook predefined process.env
|
93
|
+
await applyDefines(builderConfig, options);
|
94
|
+
|
95
|
+
// render storybook entry template
|
96
|
+
await applyHTML(builderConfig, options);
|
97
|
+
|
98
|
+
// storybook dom shim
|
99
|
+
await applyReact(builderConfig, options);
|
100
|
+
|
101
|
+
applyExternals(builderConfig);
|
102
|
+
});
|
103
|
+
|
104
|
+
const modifyConfig = async (config: WebpackConfig | RspackConfig) => {
|
105
|
+
config.resolve ??= {};
|
106
|
+
config.resolve.conditionNames = [
|
107
|
+
'require',
|
108
|
+
'node',
|
109
|
+
...(config.resolve.conditionNames || []),
|
110
|
+
];
|
111
|
+
config.resolve.fullySpecified = false;
|
112
|
+
await applyMdxLoader(config, options);
|
113
|
+
await applyCsfPlugin(config, options);
|
114
|
+
};
|
115
|
+
|
116
|
+
if ('modifyWebpackConfig' in api) {
|
117
|
+
api.modifyWebpackConfig(modifyConfig);
|
118
|
+
api.modifyWebpackChain(async chain => {
|
119
|
+
await applyDocgenWebpack(chain, options);
|
120
|
+
});
|
121
|
+
} else if ('modifyRspackConfig' in api) {
|
122
|
+
api.modifyRspackConfig(async config => {
|
123
|
+
await modifyConfig(config);
|
124
|
+
await applyDocgenRspack(config, options);
|
125
|
+
});
|
126
|
+
}
|
127
|
+
},
|
128
|
+
};
|
129
|
+
};
|
130
|
+
|
131
|
+
async function applyCsfPlugin(
|
132
|
+
config: WebpackConfig | RspackConfig,
|
133
|
+
options: Options,
|
134
|
+
) {
|
135
|
+
const { presets } = options;
|
136
|
+
|
137
|
+
const addons = await presets.apply('addons', []);
|
138
|
+
const {
|
139
|
+
options: { bundler },
|
140
|
+
} = await presets.apply<{
|
141
|
+
name: string;
|
142
|
+
options: FrameworkOptions;
|
143
|
+
}>('frameworkOptions');
|
144
|
+
|
145
|
+
const docsOptions =
|
146
|
+
// @ts-expect-error - not sure what type to use here
|
147
|
+
addons.find(a => [a, a.name].includes('@storybook/addon-docs'))?.options ??
|
148
|
+
{};
|
149
|
+
|
150
|
+
config.plugins ??= [];
|
151
|
+
config.plugins.push(
|
152
|
+
bundler === 'rspack'
|
153
|
+
? csfPlugin.rspack(docsOptions)
|
154
|
+
: (csfPlugin.webpack(docsOptions) as any),
|
155
|
+
);
|
156
|
+
}
|
157
|
+
|
158
|
+
async function prepareStorybookModules(
|
159
|
+
tempDir: string,
|
160
|
+
cwd: string,
|
161
|
+
options: Options,
|
162
|
+
builderConfig: SharedBuilderConfig,
|
163
|
+
storyPatterns: string[],
|
164
|
+
) {
|
165
|
+
const mappings = await createStorybookModules(cwd, options, storyPatterns);
|
166
|
+
|
167
|
+
const componentsPath = maybeGetAbsolutePath(`@storybook/components`);
|
168
|
+
const routerPath = maybeGetAbsolutePath(`@storybook/router`);
|
169
|
+
const themingPath = maybeGetAbsolutePath(`@storybook/theming`);
|
170
|
+
|
171
|
+
const storybookPaths: Record<string, string> = {
|
172
|
+
...(componentsPath
|
173
|
+
? {
|
174
|
+
[`@storybook/components`]: componentsPath,
|
175
|
+
}
|
176
|
+
: {}),
|
177
|
+
...(routerPath ? { [`@storybook/router`]: routerPath } : {}),
|
178
|
+
...(themingPath ? { [`@storybook/theming`]: themingPath } : {}),
|
179
|
+
};
|
180
|
+
|
181
|
+
const [mappingsAlias, write] = await virtualModule(tempDir, cwd, mappings);
|
182
|
+
|
183
|
+
builderConfig.source ??= {};
|
184
|
+
builderConfig.source.alias = {
|
185
|
+
...builderConfig.source.alias,
|
186
|
+
...storybookPaths,
|
187
|
+
...mappingsAlias,
|
188
|
+
};
|
189
|
+
|
190
|
+
const watcher = await watchStories(storyPatterns, cwd, write);
|
191
|
+
onClose(async () => {
|
192
|
+
await watcher.close();
|
193
|
+
});
|
194
|
+
}
|
195
|
+
|
196
|
+
async function applyDefines(builderConfig: AllBuilderConfig, options: Options) {
|
197
|
+
const { presets } = options;
|
198
|
+
const envs = await presets.apply<Record<string, string>>('env');
|
199
|
+
|
200
|
+
builderConfig.source ??= {};
|
201
|
+
builderConfig.source.define = {
|
202
|
+
...builderConfig.source.define,
|
203
|
+
...stringifyProcessEnvs(envs),
|
204
|
+
'process.env': JSON.stringify(envs),
|
205
|
+
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
|
206
|
+
};
|
207
|
+
}
|
208
|
+
|
209
|
+
async function applyHTML(builderConfig: AllBuilderConfig, options: Options) {
|
210
|
+
const {
|
211
|
+
presets,
|
212
|
+
packageJson,
|
213
|
+
configType,
|
214
|
+
features,
|
215
|
+
previewUrl,
|
216
|
+
serverChannelUrl,
|
217
|
+
} = options;
|
218
|
+
|
219
|
+
const [
|
220
|
+
coreOptions,
|
221
|
+
frameworkOptions,
|
222
|
+
logLevel,
|
223
|
+
headHtmlSnippet,
|
224
|
+
bodyHtmlSnippet,
|
225
|
+
template,
|
226
|
+
docsOptions,
|
227
|
+
] = await Promise.all([
|
228
|
+
presets.apply<CoreConfig>('core'),
|
229
|
+
presets.apply('frameworkOptions'),
|
230
|
+
presets.apply('logLevel', undefined),
|
231
|
+
presets.apply('previewHead'),
|
232
|
+
presets.apply('previewBody'),
|
233
|
+
presets.apply<string>('previewMainTemplate'),
|
234
|
+
presets.apply<DocsOptions>('docs'),
|
235
|
+
]);
|
236
|
+
|
237
|
+
builderConfig.tools ??= {};
|
238
|
+
builderConfig.tools.htmlPlugin = {
|
239
|
+
...builderConfig.tools.htmlPlugin,
|
240
|
+
template,
|
241
|
+
filename: 'iframe.html',
|
242
|
+
templateParameters: {
|
243
|
+
...(builderConfig.tools.htmlPlugin
|
244
|
+
? // @ts-expect-error
|
245
|
+
builderConfig.tools.htmlPlugin.templateParameters || {}
|
246
|
+
: {}),
|
247
|
+
version: packageJson.version || '',
|
248
|
+
globals: {
|
249
|
+
CONFIG_TYPE: configType,
|
250
|
+
LOGLEVEL: logLevel,
|
251
|
+
FRAMEWORK_OPTIONS: frameworkOptions,
|
252
|
+
CHANNEL_OPTIONS: coreOptions.channelOptions,
|
253
|
+
FEATURES: features,
|
254
|
+
PREVIEW_URL: previewUrl,
|
255
|
+
DOCS_OPTIONS: docsOptions,
|
256
|
+
SERVER_CHANNEL_URL: serverChannelUrl,
|
257
|
+
},
|
258
|
+
headHtmlSnippet,
|
259
|
+
bodyHtmlSnippet,
|
260
|
+
},
|
261
|
+
inject: false,
|
262
|
+
};
|
263
|
+
}
|
264
|
+
|
265
|
+
async function applyMdxLoader(
|
266
|
+
config: { module?: { rules?: any[] } },
|
267
|
+
options: Options & {
|
268
|
+
mdxPluginOptions?: CompileOptions;
|
269
|
+
},
|
270
|
+
) {
|
271
|
+
const { presets, mdxPluginOptions } = options;
|
272
|
+
|
273
|
+
const remarkExternalLinks = await import('remark-external-links');
|
274
|
+
const remarkSlug = await import('remark-slug');
|
275
|
+
|
276
|
+
const mdxLoaderOptions = await presets.apply('mdxLoaderOptions', {
|
277
|
+
skipCsf: true,
|
278
|
+
mdxCompileOptions: {
|
279
|
+
providerImportSource: '@storybook/addon-docs/mdx-react-shim',
|
280
|
+
...mdxPluginOptions?.mdxCompileOptions,
|
281
|
+
remarkPlugins: [
|
282
|
+
remarkSlug,
|
283
|
+
remarkExternalLinks,
|
284
|
+
...(mdxPluginOptions?.mdxCompileOptions?.remarkPlugins ?? []),
|
285
|
+
],
|
286
|
+
},
|
287
|
+
});
|
288
|
+
const mdxLoader = options.features?.legacyMdx1
|
289
|
+
? require.resolve('@storybook/mdx1-csf/loader')
|
290
|
+
: require.resolve('@storybook/mdx2-csf/loader');
|
291
|
+
|
292
|
+
config.module ??= {};
|
293
|
+
config.module.rules ??= [];
|
294
|
+
config.module.rules.push(
|
295
|
+
{
|
296
|
+
test: /(stories|story)\.mdx$/,
|
297
|
+
use: [
|
298
|
+
{
|
299
|
+
loader: mdxLoader,
|
300
|
+
options: {
|
301
|
+
...mdxLoaderOptions,
|
302
|
+
skipCsf: false,
|
303
|
+
},
|
304
|
+
},
|
305
|
+
],
|
306
|
+
},
|
307
|
+
{
|
308
|
+
test: /\.mdx$/,
|
309
|
+
exclude: /(stories|story)\.mdx$/,
|
310
|
+
use: [
|
311
|
+
{
|
312
|
+
loader: mdxLoader,
|
313
|
+
options: mdxLoaderOptions,
|
314
|
+
},
|
315
|
+
],
|
316
|
+
},
|
317
|
+
);
|
318
|
+
}
|
319
|
+
|
320
|
+
function applyExternals(builderConfig: AllBuilderConfig) {
|
321
|
+
const config = mergeBuilderConfig(
|
322
|
+
{
|
323
|
+
output: {
|
324
|
+
externals: builderConfig.output?.externals,
|
325
|
+
},
|
326
|
+
},
|
327
|
+
{
|
328
|
+
output: {
|
329
|
+
externals: globals,
|
330
|
+
},
|
331
|
+
},
|
332
|
+
);
|
333
|
+
|
334
|
+
builderConfig.output = config.output;
|
335
|
+
}
|
336
|
+
|
337
|
+
function getStoriesEntryPath(cwd: string) {
|
338
|
+
return resolve(join(cwd, STORIES_FILENAME));
|
339
|
+
}
|
340
|
+
|
341
|
+
function getStoriesConfigPath(cwd: string) {
|
342
|
+
return resolve(join(cwd, STORYBOOK_CONFIG_ENTRY));
|
343
|
+
}
|
344
|
+
|
345
|
+
async function createStorybookModules(
|
346
|
+
cwd: string,
|
347
|
+
options: Options,
|
348
|
+
storyPatterns: string[],
|
349
|
+
) {
|
350
|
+
const virtualModuleMappings: Record<string, string> = {};
|
351
|
+
|
352
|
+
const { presets } = options;
|
353
|
+
const storiesEntry = await createStoriesEntry(cwd, storyPatterns);
|
354
|
+
virtualModuleMappings[getStoriesEntryPath(cwd)] = storiesEntry;
|
355
|
+
|
356
|
+
const configEntryPath = getStoriesConfigPath(cwd);
|
357
|
+
const previewAnnotations = [
|
358
|
+
...(
|
359
|
+
await presets.apply<PreviewAnnotation[]>(
|
360
|
+
'previewAnnotations',
|
361
|
+
[],
|
362
|
+
options,
|
363
|
+
)
|
364
|
+
).map(entry => {
|
365
|
+
// If entry is an object, use the absolute import specifier.
|
366
|
+
// This is to maintain back-compat with community addons that bundle other addons
|
367
|
+
// and package managers that "hide" sub dependencies (e.g. pnpm / yarn pnp)
|
368
|
+
// The vite builder uses the bare import specifier.
|
369
|
+
if (typeof entry === 'object') {
|
370
|
+
return entry.absolute;
|
371
|
+
}
|
372
|
+
return resolve(cwd, slash(entry));
|
373
|
+
}),
|
374
|
+
loadPreviewOrConfigFile(options),
|
375
|
+
].filter(Boolean);
|
376
|
+
virtualModuleMappings[configEntryPath] = handlebars(
|
377
|
+
await readTemplate(
|
378
|
+
require.resolve(
|
379
|
+
'@modern-js/storybook-builder/templates/virtualModuleModernEntry.js.handlebars',
|
380
|
+
),
|
381
|
+
),
|
382
|
+
{
|
383
|
+
storiesFilename: STORIES_FILENAME,
|
384
|
+
previewAnnotations,
|
385
|
+
},
|
386
|
+
).replace(/\\/g, '\\\\');
|
387
|
+
|
388
|
+
return virtualModuleMappings;
|
389
|
+
}
|
390
|
+
|
391
|
+
async function createStoriesEntry(cwd: string, storyPatterns: string[]) {
|
392
|
+
const stories = (
|
393
|
+
await Promise.all(
|
394
|
+
storyPatterns.map(pattern => {
|
395
|
+
return globby(slash(pattern), { followSymbolicLinks: true });
|
396
|
+
}),
|
397
|
+
)
|
398
|
+
).reduce((carry, stories) => carry.concat(stories), []);
|
399
|
+
|
400
|
+
return await toImportFn(cwd, stories);
|
401
|
+
}
|
402
|
+
|
403
|
+
async function applyReact(config: AllBuilderConfig, options: Options) {
|
404
|
+
let version = '18.0.0';
|
405
|
+
try {
|
406
|
+
// @ts-expect-error
|
407
|
+
({ version } = await import('react-dom/package.json'));
|
408
|
+
} catch (_) {}
|
409
|
+
|
410
|
+
const { legacyRootApi } =
|
411
|
+
(await options.presets.apply<{ legacyRootApi?: boolean } | null>(
|
412
|
+
'frameworkOptions',
|
413
|
+
)) || {};
|
414
|
+
|
415
|
+
const isReact18 = version.startsWith('18') || version.startsWith('0.0.0');
|
416
|
+
const useReact17 = legacyRootApi ?? !isReact18;
|
417
|
+
if (!useReact17) {
|
418
|
+
config.source ??= {};
|
419
|
+
config.source.alias ??= {};
|
420
|
+
// @ts-expect-error
|
421
|
+
config.source.alias['@storybook/react-dom-shim'] =
|
422
|
+
'@storybook/react-dom-shim/dist/react-18';
|
423
|
+
}
|
424
|
+
}
|
425
|
+
|
426
|
+
/**
|
427
|
+
* Storybook scans all stories in the folder and place them in one module.
|
428
|
+
* We need to detect new stories ourself, and regenerate new entry for that
|
429
|
+
* story.
|
430
|
+
*
|
431
|
+
* When `require.context` is usable, we can use that instead.
|
432
|
+
*/
|
433
|
+
async function watchStories(
|
434
|
+
patterns: string[],
|
435
|
+
cwd: string,
|
436
|
+
writeModule: (p: string, content: string) => void,
|
437
|
+
) {
|
438
|
+
const watcher = watch(
|
439
|
+
cwd,
|
440
|
+
async ({ changeType, changedFilePath }) => {
|
441
|
+
if (changeType !== 'add' && changeType !== 'unlink') {
|
442
|
+
return;
|
443
|
+
}
|
444
|
+
|
445
|
+
if (patterns.some(entry => minimatch(changedFilePath, entry))) {
|
446
|
+
// recalculate stories
|
447
|
+
const stories = (
|
448
|
+
await Promise.all(
|
449
|
+
patterns.map(pattern => {
|
450
|
+
return globby(slash(pattern), { followSymbolicLinks: true });
|
451
|
+
}),
|
452
|
+
)
|
453
|
+
).reduce((carry, stories) => carry.concat(stories), []);
|
454
|
+
|
455
|
+
const newStories = await toImportFn(cwd, stories);
|
456
|
+
writeModule(getStoriesEntryPath(cwd), newStories);
|
457
|
+
}
|
458
|
+
},
|
459
|
+
[/node_modules/],
|
460
|
+
);
|
461
|
+
return watcher;
|
462
|
+
}
|
package/src/preset.ts
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
import { join, resolve } from 'path';
|
2
|
+
import type { Options } from '@storybook/types';
|
3
|
+
import { getConfig } from './build';
|
4
|
+
import { STORYBOOK_CONFIG_ENTRY } from './utils';
|
5
|
+
import { BuilderConfig } from './types';
|
6
|
+
|
7
|
+
export const previewMainTemplate = () => {
|
8
|
+
return require.resolve('@modern-js/storybook-builder/templates/preview.ejs');
|
9
|
+
};
|
10
|
+
|
11
|
+
function getStoriesConfigPath(cwd: string) {
|
12
|
+
return resolve(join(cwd, STORYBOOK_CONFIG_ENTRY));
|
13
|
+
}
|
14
|
+
|
15
|
+
export const entries = async (_: unknown, options: Options) => {
|
16
|
+
const result: string[] = [];
|
17
|
+
const { bundler } = await getConfig(options);
|
18
|
+
|
19
|
+
if (options.configType === 'DEVELOPMENT') {
|
20
|
+
// Suppress informational messages when --quiet is specified. webpack-hot-middleware's quiet
|
21
|
+
// parameter would also suppress warnings.
|
22
|
+
result.push(
|
23
|
+
...([
|
24
|
+
`${require.resolve(
|
25
|
+
'webpack-hot-middleware/client',
|
26
|
+
)}?reload=true&quiet=false&noInfo=${options.quiet}`,
|
27
|
+
|
28
|
+
bundler === 'rspack'
|
29
|
+
? require.resolve('@rspack/dev-client/react-refresh-entry')
|
30
|
+
: null,
|
31
|
+
].filter(Boolean) as string[]),
|
32
|
+
);
|
33
|
+
}
|
34
|
+
|
35
|
+
result.push(getStoriesConfigPath(process.cwd()));
|
36
|
+
|
37
|
+
return result;
|
38
|
+
};
|
39
|
+
|
40
|
+
export const modern = (
|
41
|
+
builderConfig: BuilderConfig,
|
42
|
+
options: Options,
|
43
|
+
): BuilderConfig => {
|
44
|
+
// @ts-expect-error
|
45
|
+
return {
|
46
|
+
...builderConfig,
|
47
|
+
|
48
|
+
output: {
|
49
|
+
...builderConfig.output,
|
50
|
+
disableInlineRuntimeChunk: true,
|
51
|
+
distPath: {
|
52
|
+
...builderConfig.output?.distPath,
|
53
|
+
root: options.outputDir,
|
54
|
+
},
|
55
|
+
},
|
56
|
+
};
|
57
|
+
};
|
58
|
+
|
59
|
+
export { decorators } from './addons/preset/preview';
|
package/src/types.ts
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
import type { BuilderConfig as WebpackBuilderConfig } from '@modern-js/builder-webpack-provider';
|
2
|
+
import type { BuilderConfig as RspackBuilderConfig } from '@modern-js/builder-rspack-provider';
|
3
|
+
import { BuilderPlugin } from '@modern-js/builder-shared';
|
4
|
+
|
5
|
+
export type BundlerType = 'webpack' | 'rspack';
|
6
|
+
|
7
|
+
export type { WebpackBuilderConfig, RspackBuilderConfig };
|
8
|
+
|
9
|
+
export type AllBuilderConfig = WebpackBuilderConfig | RspackBuilderConfig;
|
10
|
+
|
11
|
+
export type FrameworkOptions = {
|
12
|
+
bundler?: BundlerType;
|
13
|
+
builderConfig?: AllBuilderConfig;
|
14
|
+
configPath?: string;
|
15
|
+
};
|
16
|
+
|
17
|
+
export type BuilderConfig = AllBuilderConfig & {
|
18
|
+
builderPlugins?: BuilderPlugin[];
|
19
|
+
};
|
20
|
+
|
21
|
+
export { defineConfig } from '@modern-js/builder/cli';
|