-
-
Notifications
You must be signed in to change notification settings - Fork 9.4k
/
Copy pathpreset.ts
255 lines (225 loc) · 8.69 KB
/
preset.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
import { dirname, isAbsolute, join } from 'node:path';
import { logger } from 'storybook/internal/node-logger';
import type { DocsOptions, Options, PresetProperty } from 'storybook/internal/types';
import type { CsfPluginOptions } from '@storybook/csf-plugin';
import type { CompileOptions } from './compiler';
/**
* Get the resolvedReact preset, which points either to the user's react dependencies or the react
* dependencies shipped with addon-docs if the user has not installed react explicitly.
*/
const getResolvedReact = async (options: Options) => {
const resolvedReact = (await options.presets.apply('resolvedReact', {})) as any;
// resolvedReact should always be set by the time we get here, but just in case, we'll default to addon-docs's react dependencies
return {
react: resolvedReact.react ?? dirname(require.resolve('react/package.json')),
reactDom: resolvedReact.reactDom ?? dirname(require.resolve('react-dom/package.json')),
// In Webpack, symlinked MDX files will cause @mdx-js/react to not be resolvable if it is not hoisted
// This happens for the SB monorepo's template stories when a sandbox has a different react version than
// addon-docs, causing addon-docs's dependencies not to be hoisted.
// This might also affect regular users who have a similar setup.
// Explicitly alias @mdx-js/react to avoid this issue.
mdx: resolvedReact.mdx ?? dirname(require.resolve('@mdx-js/react')),
};
};
async function webpack(
webpackConfig: any = {},
options: Options & {
csfPluginOptions: CsfPluginOptions | null;
mdxPluginOptions?: CompileOptions;
} /* & Parameters<
typeof createCompiler
>[0] */
) {
const { module = {} } = webpackConfig;
const { csfPluginOptions = {}, mdxPluginOptions = {} } = options;
const rehypeSlug = (await import('rehype-slug')).default;
const rehypeExternalLinks = (await import('rehype-external-links')).default;
const mdxLoaderOptions: CompileOptions = await options.presets.apply('mdxLoaderOptions', {
...mdxPluginOptions,
mdxCompileOptions: {
providerImportSource: join(
dirname(require.resolve('@storybook/addon-docs/package.json')),
'/dist/shims/mdx-react-shim.mjs'
),
...mdxPluginOptions.mdxCompileOptions,
rehypePlugins: [
...(mdxPluginOptions?.mdxCompileOptions?.rehypePlugins ?? []),
rehypeSlug,
rehypeExternalLinks,
],
},
});
logger.info(`Addon-docs: using MDX3`);
// Use the resolvedReact preset to alias react and react-dom to either the users version or the version shipped with addon-docs
const { react, reactDom, mdx } = await getResolvedReact(options);
let alias;
/**
* Add aliases for `@storybook/addon-docs` & `@storybook/blocks` These must be singletons to avoid
* multiple instances of react & emotion being loaded, both would cause the components to fail to
* render.
*
* In the future the `@storybook/theming` and `@storybook/components` can be removed, as they
* should be singletons in the future due to the peerDependency on `storybook` package.
*/
const cliPath = dirname(require.resolve('storybook/package.json'));
const themingPath = join(cliPath, 'core', 'theming', 'index.js');
const themingCreatePath = join(cliPath, 'core', 'theming', 'create.js');
const componentsPath = join(cliPath, 'core', 'components', 'index.js');
const blocksPath = dirname(require.resolve('@storybook/blocks/package.json'));
if (Array.isArray(webpackConfig.resolve?.alias)) {
alias = [...webpackConfig.resolve?.alias];
alias.push(
{
name: 'react',
alias: react,
},
{
name: 'react-dom',
alias: reactDom,
},
{
name: '@mdx-js/react',
alias: mdx,
},
{
name: '@storybook/theming/create',
alias: themingCreatePath,
},
{
name: '@storybook/theming',
alias: themingPath,
},
{
name: '@storybook/components',
alias: componentsPath,
},
{
name: '@storybook/blocks',
alias: blocksPath,
}
);
} else {
alias = {
...webpackConfig.resolve?.alias,
react,
'@storybook/theming/create': themingCreatePath,
'@storybook/theming': themingPath,
'@storybook/components': componentsPath,
'@storybook/blocks': blocksPath,
'react-dom': reactDom,
'@mdx-js/react': mdx,
};
}
const result = {
...webpackConfig,
plugins: [
...(webpackConfig.plugins || []),
...(csfPluginOptions
? [(await import('@storybook/csf-plugin')).webpack(csfPluginOptions)]
: []),
],
resolve: {
...webpackConfig.resolve,
alias,
},
module: {
...module,
rules: [
...(module.rules || []),
{
test: /\.mdx$/,
exclude: /(stories|story)\.mdx$/,
use: [
{
loader: require.resolve('./mdx-loader'),
options: mdxLoaderOptions,
},
],
},
],
},
};
return result;
}
const docs = (docsOptions: DocsOptions) => {
return {
...docsOptions,
defaultName: 'Docs',
autodocs: 'tag',
};
};
export const addons: PresetProperty<'addons'> = [
require.resolve('@storybook/react-dom-shim/dist/preset'),
];
export const viteFinal = async (config: any, options: Options) => {
const { plugins = [] } = config;
const { mdxPlugin } = await import('./plugins/mdx-plugin');
const rehypeSlug = (await import('rehype-slug')).default;
const rehypeExternalLinks = (await import('rehype-external-links')).default;
// Use the resolvedReact preset to alias react and react-dom to either the users version or the version shipped with addon-docs
const { react, reactDom, mdx } = await getResolvedReact(options);
const cliPath = dirname(require.resolve('storybook/package.json'));
const themingPath = join(cliPath, 'core', 'theming', 'index.js');
const themingCreatePath = join(cliPath, 'core', 'theming', 'create.js');
const componentsPath = join(cliPath, 'core', 'components', 'index.js');
const blocksPath = dirname(require.resolve('@storybook/blocks/package.json'));
const packageDeduplicationPlugin = {
name: 'storybook:package-deduplication',
enforce: 'pre',
config: () => ({
resolve: {
alias: {
react,
// Vite doesn't respect export maps when resolving an absolute path, so we need to do that manually here
...(isAbsolute(reactDom) && { 'react-dom/server': `${reactDom}/server.browser.js` }),
'react-dom': reactDom,
'@mdx-js/react': mdx,
/**
* Add aliases for `@storybook/addon-docs` & `@storybook/blocks` These must be singletons
* to avoid multiple instances of react & emotion being loaded, both would cause the
* components to fail to render.
*
* In the future the `@storybook/theming` and `@storybook/components` can be removed, as
* they should be singletons in the future due to the peerDependency on `storybook`
* package.
*/
'@storybook/theming/create': themingCreatePath,
'@storybook/theming': themingPath,
'@storybook/components': componentsPath,
'@storybook/blocks': blocksPath,
},
},
}),
};
// add alias plugin early to ensure any other plugins that also add the aliases will override this
// eg. the preact vite plugin adds its own aliases
plugins.unshift(packageDeduplicationPlugin);
// mdx plugin needs to be before any react plugins
plugins.unshift(mdxPlugin(options));
return config;
};
/*
* This is a workaround for https://github.com/Swatinem/rollup-plugin-dts/issues/162
* something down the dependency chain is using typescript namespaces, which are not supported by rollup-plugin-dts
*/
const webpackX = webpack as any;
const docsX = docs as any;
/**
* If the user has not installed react explicitly in their project, the resolvedReact preset will
* not be set. We then set it here in addon-docs to use addon-docs's react version that always
* exists. This is just a fallback that never overrides the existing preset, but ensures that there
* is always a resolved react.
*/
export const resolvedReact = async (existing: any) => ({
react: existing?.react ?? dirname(require.resolve('react/package.json')),
reactDom: existing?.reactDom ?? dirname(require.resolve('react-dom/package.json')),
mdx: existing?.mdx ?? dirname(require.resolve('@mdx-js/react')),
});
const optimizeViteDeps = [
'@mdx-js/react',
'@storybook/addon-docs > acorn-jsx',
'@storybook/addon-docs',
'@storybook/addon-essentials/docs/mdx-react-shim',
'markdown-to-jsx',
];
export { webpackX as webpack, docsX as docs, optimizeViteDeps };