@storybook/builder-vite 0.1.22
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/README.md +120 -0
- package/build.ts +32 -0
- package/code-generator-plugin.ts +102 -0
- package/codegen-entries.ts +46 -0
- package/codegen-iframe-script.ts +93 -0
- package/codegen-importfn-script.ts +49 -0
- package/codegen-modern-iframe-script.ts +82 -0
- package/declarations/extract-stories.d.ts +18 -0
- package/declarations/svetle-stories-loader.d.ts +7 -0
- package/dist/build.d.ts +2 -0
- package/dist/build.js +30 -0
- package/dist/build.js.map +1 -0
- package/dist/code-generator-plugin.d.ts +3 -0
- package/dist/code-generator-plugin.js +124 -0
- package/dist/code-generator-plugin.js.map +1 -0
- package/dist/codegen-entries.d.ts +4 -0
- package/dist/codegen-entries.js +45 -0
- package/dist/codegen-entries.js.map +1 -0
- package/dist/codegen-iframe-script.d.ts +7 -0
- package/dist/codegen-iframe-script.js +82 -0
- package/dist/codegen-iframe-script.js.map +1 -0
- package/dist/codegen-importfn-script.d.ts +2 -0
- package/dist/codegen-importfn-script.js +65 -0
- package/dist/codegen-importfn-script.js.map +1 -0
- package/dist/codegen-modern-iframe-script.d.ts +6 -0
- package/dist/codegen-modern-iframe-script.js +75 -0
- package/dist/codegen-modern-iframe-script.js.map +1 -0
- package/dist/envs.d.ts +8 -0
- package/dist/envs.js +43 -0
- package/dist/envs.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +80 -0
- package/dist/index.js.map +1 -0
- package/dist/inject-export-order-plugin.d.ts +8 -0
- package/dist/inject-export-order-plugin.js +28 -0
- package/dist/inject-export-order-plugin.js.map +1 -0
- package/dist/list-stories.d.ts +2 -0
- package/dist/list-stories.js +36 -0
- package/dist/list-stories.js.map +1 -0
- package/dist/mdx-plugin.d.ts +9 -0
- package/dist/mdx-plugin.js +29 -0
- package/dist/mdx-plugin.js.map +1 -0
- package/dist/mock-core-js.d.ts +5 -0
- package/dist/mock-core-js.js +22 -0
- package/dist/mock-core-js.js.map +1 -0
- package/dist/optimizeDeps.d.ts +9 -0
- package/dist/optimizeDeps.js +136 -0
- package/dist/optimizeDeps.js.map +1 -0
- package/dist/plugins/vue-docgen.d.ts +4 -0
- package/dist/plugins/vue-docgen.js +18 -0
- package/dist/plugins/vue-docgen.js.map +1 -0
- package/dist/source-loader-plugin.d.ts +10 -0
- package/dist/source-loader-plugin.js +27 -0
- package/dist/source-loader-plugin.js.map +1 -0
- package/dist/svelte/csf-plugin.d.ts +8 -0
- package/dist/svelte/csf-plugin.js +64 -0
- package/dist/svelte/csf-plugin.js.map +1 -0
- package/dist/transform-iframe-html.d.ts +3 -0
- package/dist/transform-iframe-html.js +32 -0
- package/dist/transform-iframe-html.js.map +1 -0
- package/dist/types/envs-raw.type.d.ts +1 -0
- package/dist/types/envs-raw.type.js +3 -0
- package/dist/types/envs-raw.type.js.map +1 -0
- package/dist/types/extended-options.type.d.ts +7 -0
- package/dist/types/extended-options.type.js +3 -0
- package/dist/types/extended-options.type.js.map +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +15 -0
- package/dist/types/index.js.map +1 -0
- package/dist/vite-config.d.ts +9 -0
- package/dist/vite-config.js +126 -0
- package/dist/vite-config.js.map +1 -0
- package/dist/vite-server.d.ts +4 -0
- package/dist/vite-server.js +37 -0
- package/dist/vite-server.js.map +1 -0
- package/envs.ts +50 -0
- package/index.ts +75 -0
- package/inject-export-order-plugin.ts +28 -0
- package/input/iframe.html +37 -0
- package/list-stories.ts +21 -0
- package/mdx-plugin.ts +24 -0
- package/mock-core-js.ts +17 -0
- package/optimizeDeps.ts +123 -0
- package/package.json +38 -0
- package/plugins/vue-docgen.ts +16 -0
- package/source-loader-plugin.ts +21 -0
- package/svelte/csf-plugin.ts +46 -0
- package/transform-iframe-html.ts +36 -0
- package/tsconfig.json +9 -0
- package/types/envs-raw.type.ts +1 -0
- package/types/extended-options.type.ts +9 -0
- package/types/index.ts +2 -0
- package/vite-config.ts +128 -0
- package/vite-server.ts +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Storybook builder for Vite
|
|
2
|
+
|
|
3
|
+
Requirements:
|
|
4
|
+
|
|
5
|
+
- Vite 2.5 or newer
|
|
6
|
+
|
|
7
|
+
Have a look at the GitHub issues for known bugs. If you find any new bugs,
|
|
8
|
+
feel free to create an issue or send a pull request!
|
|
9
|
+
|
|
10
|
+
## More maintainers needed!
|
|
11
|
+
|
|
12
|
+
The Vite builder cannot build itself.
|
|
13
|
+
Are you willing to contribute?
|
|
14
|
+
|
|
15
|
+
https://github.com/storybookjs/builder-vite/issues/11
|
|
16
|
+
|
|
17
|
+
Please read the [How to contribute](/CONTRIBUTING.md) guide.
|
|
18
|
+
|
|
19
|
+
### Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install @storybook/builder-vite --save-dev
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
or
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
yarn add --dev @storybook/builder-vite
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Usage
|
|
32
|
+
|
|
33
|
+
In your `main.js` configuration file,
|
|
34
|
+
set `core: { builder: "@storybook/builder-vite" }`.
|
|
35
|
+
|
|
36
|
+
> For autoreload of stories to work, they need to have `.stories.tsx` file suffix.
|
|
37
|
+
> See also [#53](https://github.com/storybookjs/builder-vite/pull/53)
|
|
38
|
+
|
|
39
|
+
The builder supports both development mode in Storybook, and building a static production version.
|
|
40
|
+
|
|
41
|
+
### Customize Vite config
|
|
42
|
+
|
|
43
|
+
The builder will _not_ read your `vite.config.js` file by default.
|
|
44
|
+
|
|
45
|
+
In `.storybook/main.js` (or whatever your Storybook config file is named)
|
|
46
|
+
you can override the Vite config:
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
module.exports = {
|
|
50
|
+
async viteFinal(config, { configType }) {
|
|
51
|
+
// customize the Vite config here
|
|
52
|
+
config.resolve.alias.foo = 'bar';
|
|
53
|
+
|
|
54
|
+
// return the customized config
|
|
55
|
+
return config;
|
|
56
|
+
},
|
|
57
|
+
// ... other options here
|
|
58
|
+
};
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The `viteFinal` function will give you `config` which is
|
|
62
|
+
the builder's own Vite config. You can tweak this as you want,
|
|
63
|
+
for example to set up aliases, add new plugins etc.
|
|
64
|
+
|
|
65
|
+
The `configType` variable will be either `"DEVELOPMENT"` or `"PRODUCTION"`.
|
|
66
|
+
|
|
67
|
+
The function should return the updated Vite configuration.
|
|
68
|
+
|
|
69
|
+
### Svelte Customization
|
|
70
|
+
|
|
71
|
+
When using this builder with Svelte, your `.storybook/main.js` (or equivalent)
|
|
72
|
+
can contain a `svelteOptions` object to pass custom options to
|
|
73
|
+
[`vite-plugin-svelte`](https://github.com/sveltejs/vite-plugin-svelte/tree/main/packages/vite-plugin-svelte):
|
|
74
|
+
|
|
75
|
+
```javascript
|
|
76
|
+
const preprocess = require('svelte-preprocess');
|
|
77
|
+
|
|
78
|
+
module.exports = {
|
|
79
|
+
svelteOptions: {
|
|
80
|
+
preprocess: preprocess({
|
|
81
|
+
typescript: true,
|
|
82
|
+
postcss: true,
|
|
83
|
+
sourceMap: true,
|
|
84
|
+
}),
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Note about working directory
|
|
90
|
+
|
|
91
|
+
The builder will by default enable Vite's [server.fs.strict](https://vitejs.dev/config/#server-fs-strict)
|
|
92
|
+
option, for increased security. The default project `root` is set to the parent directory of the
|
|
93
|
+
storybook configuration directory. This can be overridden in viteFinal.
|
|
94
|
+
|
|
95
|
+
### Getting started with React, Vite and Storybook (on a new project)
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
npm init @vitejs/app vite-react-app --template react && cd vite-react-app
|
|
99
|
+
npm install # or yarn
|
|
100
|
+
npx sb@next init --builder @storybook/builder-vite && npm run storybook
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Known issues
|
|
104
|
+
|
|
105
|
+
- HMR: saving a story file does not hot-module-reload. In svelte, the page is not reloaded either (https://github.com/storybookjs/builder-vite/issues/209). HMR should work when saving component files.
|
|
106
|
+
- Prebundling: Vite restarts if it detects new dependencies which it did not know about and needs to pre-bundle. This breaks within storybook, with confusing error messages. If you see a message in your terminal like `[vite] new dependencies found:`, please add those dependencies to your `optimizeDeps.include` in `viteFinal`. E.g. `config.optimizeDeps.include = [...(config.optimizeDeps?.include ?? []), "storybook-dark-mode"],`.
|
|
107
|
+
- MDX pages are broken when emotion 11 is installed: Adding the configuration [here](https://github.com/storybookjs/builder-vite/issues/219#issuecomment-1023666193) should fix this.
|
|
108
|
+
|
|
109
|
+
## Contributing
|
|
110
|
+
|
|
111
|
+
Contributions are welcome!
|
|
112
|
+
|
|
113
|
+
### About this codebase
|
|
114
|
+
|
|
115
|
+
The code is a monorepo with the core `@storybook/builder-vite` package,
|
|
116
|
+
and examples (like `packages/example-react`) to test the builder implementation with.
|
|
117
|
+
|
|
118
|
+
Similar to the main storybook monorepo, you need yarn , because the project is organized as yarn workspaces.
|
|
119
|
+
This lets you write new code in the core builder package, and instantly use them from
|
|
120
|
+
the example packages.
|
package/build.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { build as viteBuild } from 'vite';
|
|
2
|
+
import { stringifyProcessEnvs } from './envs';
|
|
3
|
+
import { commonConfig } from './vite-config';
|
|
4
|
+
|
|
5
|
+
import type { EnvsRaw, ExtendedOptions } from './types';
|
|
6
|
+
|
|
7
|
+
export async function build(options: ExtendedOptions) {
|
|
8
|
+
const { presets } = options;
|
|
9
|
+
|
|
10
|
+
const baseConfig = await commonConfig(options, 'development');
|
|
11
|
+
const config = {
|
|
12
|
+
...baseConfig,
|
|
13
|
+
build: {
|
|
14
|
+
outDir: options.outputDir,
|
|
15
|
+
emptyOutDir: false, // do not clean before running Vite build - Storybook has already added assets in there!
|
|
16
|
+
sourcemap: true,
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const finalConfig = await presets.apply('viteFinal', config, options);
|
|
21
|
+
|
|
22
|
+
const envsRaw = await presets.apply<Promise<EnvsRaw>>('env');
|
|
23
|
+
// Stringify env variables after getting `envPrefix` from the final config
|
|
24
|
+
const envs = stringifyProcessEnvs(envsRaw, finalConfig.envPrefix);
|
|
25
|
+
// Update `define`
|
|
26
|
+
finalConfig.define = {
|
|
27
|
+
...finalConfig.define,
|
|
28
|
+
...envs,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
await viteBuild(finalConfig);
|
|
32
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { transformIframeHtml } from './transform-iframe-html';
|
|
4
|
+
import { generateIframeScriptCode } from './codegen-iframe-script';
|
|
5
|
+
import { generateModernIframeScriptCode } from './codegen-modern-iframe-script';
|
|
6
|
+
import { generateImportFnScriptCode } from './codegen-importfn-script';
|
|
7
|
+
import { generateVirtualStoryEntryCode, generatePreviewEntryCode } from './codegen-entries';
|
|
8
|
+
|
|
9
|
+
import type { Plugin } from 'vite';
|
|
10
|
+
import type { ExtendedOptions } from './types';
|
|
11
|
+
|
|
12
|
+
export function codeGeneratorPlugin(options: ExtendedOptions): Plugin {
|
|
13
|
+
const virtualFileId = '/virtual:/@storybook/builder-vite/vite-app.js';
|
|
14
|
+
const virtualStoriesFile = '/virtual:/@storybook/builder-vite/storybook-stories.js';
|
|
15
|
+
const virtualPreviewFile = '/virtual:/@storybook/builder-vite/preview-entry.js';
|
|
16
|
+
const iframePath = path.resolve(__dirname, '..', 'input', 'iframe.html');
|
|
17
|
+
let iframeId: string;
|
|
18
|
+
|
|
19
|
+
// noinspection JSUnusedGlobalSymbols
|
|
20
|
+
return {
|
|
21
|
+
name: 'storybook-vite-code-generator-plugin',
|
|
22
|
+
enforce: 'pre',
|
|
23
|
+
configureServer(server) {
|
|
24
|
+
// invalidate the whole vite-app.js script on every file change.
|
|
25
|
+
// (this might be a little too aggressive?)
|
|
26
|
+
server.watcher.on('change', (_e) => {
|
|
27
|
+
const { moduleGraph } = server;
|
|
28
|
+
const appModule = moduleGraph.getModuleById(virtualFileId);
|
|
29
|
+
if (appModule) {
|
|
30
|
+
server.moduleGraph.invalidateModule(appModule);
|
|
31
|
+
}
|
|
32
|
+
const storiesModule = moduleGraph.getModuleById(virtualStoriesFile);
|
|
33
|
+
if (storiesModule) {
|
|
34
|
+
server.moduleGraph.invalidateModule(storiesModule);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
},
|
|
38
|
+
config(config, { command }) {
|
|
39
|
+
// If we are building the static distribution, add iframe.html as an entry.
|
|
40
|
+
// In development mode, it's not an entry - instead, we use an express middleware
|
|
41
|
+
// to serve iframe.html. The reason is that Vite's dev server (at the time of writing)
|
|
42
|
+
// does not support virtual files as entry points.
|
|
43
|
+
if (command === 'build') {
|
|
44
|
+
if (!config.build) {
|
|
45
|
+
config.build = {};
|
|
46
|
+
}
|
|
47
|
+
config.build.rollupOptions = {
|
|
48
|
+
input: iframePath,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
configResolved(config) {
|
|
53
|
+
iframeId = `${config.root}/iframe.html`;
|
|
54
|
+
},
|
|
55
|
+
resolveId(source) {
|
|
56
|
+
if (source === virtualFileId) {
|
|
57
|
+
return virtualFileId;
|
|
58
|
+
} else if (source === iframePath) {
|
|
59
|
+
return iframeId;
|
|
60
|
+
} else if (source === virtualStoriesFile) {
|
|
61
|
+
return virtualStoriesFile;
|
|
62
|
+
} else if (source === virtualPreviewFile) {
|
|
63
|
+
return virtualPreviewFile;
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
async load(id) {
|
|
67
|
+
const storyStoreV7 = options.features?.storyStoreV7;
|
|
68
|
+
if (id === virtualStoriesFile) {
|
|
69
|
+
if (storyStoreV7) {
|
|
70
|
+
return generateImportFnScriptCode(options);
|
|
71
|
+
} else {
|
|
72
|
+
return generateVirtualStoryEntryCode(options);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (id === virtualPreviewFile && !storyStoreV7) {
|
|
77
|
+
return generatePreviewEntryCode(options);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (id === virtualFileId) {
|
|
81
|
+
if (storyStoreV7) {
|
|
82
|
+
return generateModernIframeScriptCode(options, { storiesFilename: virtualStoriesFile });
|
|
83
|
+
} else {
|
|
84
|
+
return generateIframeScriptCode(options, {
|
|
85
|
+
storiesFilename: virtualStoriesFile,
|
|
86
|
+
previewFilename: virtualPreviewFile,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (id === iframeId) {
|
|
92
|
+
return fs.readFileSync(path.resolve(__dirname, '..', 'input', 'iframe.html'), 'utf-8');
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
async transformIndexHtml(html, ctx) {
|
|
96
|
+
if (ctx.path !== '/iframe.html') {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
return transformIframeHtml(html, options);
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { loadPreviewOrConfigFile } from '@storybook/core-common';
|
|
2
|
+
import type { Options } from '@storybook/core-common';
|
|
3
|
+
import slash from 'slash';
|
|
4
|
+
import { normalizePath } from 'vite';
|
|
5
|
+
import type { ExtendedOptions } from './types';
|
|
6
|
+
import { listStories } from './list-stories';
|
|
7
|
+
|
|
8
|
+
const absoluteFilesToImport = (files: string[], name: string) =>
|
|
9
|
+
files.map((el, i) => `import ${name ? `* as ${name}_${i} from ` : ''}'/@fs/${normalizePath(el)}'`).join('\n');
|
|
10
|
+
|
|
11
|
+
export async function generateVirtualStoryEntryCode(options: ExtendedOptions) {
|
|
12
|
+
const { frameworkPath, framework } = options;
|
|
13
|
+
const storyEntries = await listStories(options);
|
|
14
|
+
const resolveMap = storyEntries.reduce<Record<string, string>>(
|
|
15
|
+
(prev, entry) => ({ ...prev, [entry]: entry.replace(slash(process.cwd()), '.') }),
|
|
16
|
+
{}
|
|
17
|
+
);
|
|
18
|
+
const modules = storyEntries.map((entry, i) => `${JSON.stringify(entry)}: story_${i}`).join(',');
|
|
19
|
+
const frameworkImportPath = frameworkPath || `@storybook/${framework}`;
|
|
20
|
+
|
|
21
|
+
return `
|
|
22
|
+
import { configure } from '${frameworkImportPath}';
|
|
23
|
+
${absoluteFilesToImport(storyEntries, 'story')}
|
|
24
|
+
|
|
25
|
+
function loadable(key) {
|
|
26
|
+
return {${modules}}[key];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
Object.assign(loadable, {
|
|
30
|
+
keys: () => (${JSON.stringify(Object.keys(resolveMap))}),
|
|
31
|
+
resolve: (key) => (${JSON.stringify(resolveMap)}[key])
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
export function configStories() {
|
|
35
|
+
configure(loadable, { hot: import.meta.hot }, false);
|
|
36
|
+
}
|
|
37
|
+
`.trim();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function generatePreviewEntryCode({ configDir }: Options) {
|
|
41
|
+
const previewFile = loadPreviewOrConfigFile({ configDir });
|
|
42
|
+
if (!previewFile) return '';
|
|
43
|
+
|
|
44
|
+
return `import * as preview from '${slash(previewFile)}';
|
|
45
|
+
export default preview;`;
|
|
46
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { normalizePath } from 'vite';
|
|
2
|
+
|
|
3
|
+
import type { ExtendedOptions } from './types';
|
|
4
|
+
|
|
5
|
+
interface GenerateIframeScriptCodeOptions {
|
|
6
|
+
storiesFilename: string;
|
|
7
|
+
previewFilename: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function generateIframeScriptCode(
|
|
11
|
+
options: ExtendedOptions,
|
|
12
|
+
{ storiesFilename, previewFilename }: GenerateIframeScriptCodeOptions
|
|
13
|
+
) {
|
|
14
|
+
const { presets } = options;
|
|
15
|
+
|
|
16
|
+
const presetEntries = await presets.apply('config', [], options);
|
|
17
|
+
const configEntries = [...presetEntries].filter(Boolean);
|
|
18
|
+
|
|
19
|
+
const absoluteFilesToImport = (files: string[], name: string) =>
|
|
20
|
+
files.map((el, i) => `import ${name ? `* as ${name}_${i} from ` : ''}'/@fs/${normalizePath(el)}'`).join('\n');
|
|
21
|
+
|
|
22
|
+
const importArray = (name: string, length: number) => new Array(length).fill(0).map((_, i) => `${name}_${i}`);
|
|
23
|
+
|
|
24
|
+
// noinspection UnnecessaryLocalVariableJS
|
|
25
|
+
/** @todo Inline variable and remove `noinspection` */
|
|
26
|
+
// language=JavaScript
|
|
27
|
+
const code = `
|
|
28
|
+
import {
|
|
29
|
+
addDecorator,
|
|
30
|
+
addParameters,
|
|
31
|
+
addLoader,
|
|
32
|
+
addArgTypesEnhancer,
|
|
33
|
+
addArgsEnhancer
|
|
34
|
+
} from '@storybook/client-api';
|
|
35
|
+
import { logger } from '@storybook/client-logger';
|
|
36
|
+
${absoluteFilesToImport(configEntries, 'config')}
|
|
37
|
+
import * as preview from '${previewFilename}';
|
|
38
|
+
// This import should occur after the config imports above
|
|
39
|
+
import { configStories } from '${storiesFilename}';
|
|
40
|
+
|
|
41
|
+
const configs = [${importArray('config', configEntries.length).concat('preview.default').join(',')}].filter(Boolean)
|
|
42
|
+
|
|
43
|
+
configs.forEach(config => {
|
|
44
|
+
Object.keys(config).forEach((key) => {
|
|
45
|
+
const value = config[key];
|
|
46
|
+
switch (key) {
|
|
47
|
+
case 'args':
|
|
48
|
+
case 'argTypes': {
|
|
49
|
+
return logger.warn('Invalid args/argTypes in config, ignoring.', JSON.stringify(value));
|
|
50
|
+
}
|
|
51
|
+
case 'decorators': {
|
|
52
|
+
return value.forEach((decorator) => addDecorator(decorator, false));
|
|
53
|
+
}
|
|
54
|
+
case 'loaders': {
|
|
55
|
+
return value.forEach((loader) => addLoader(loader, false));
|
|
56
|
+
}
|
|
57
|
+
case 'parameters': {
|
|
58
|
+
return addParameters({ ...value }, false);
|
|
59
|
+
}
|
|
60
|
+
case 'argTypesEnhancers': {
|
|
61
|
+
return value.forEach((enhancer) => addArgTypesEnhancer(enhancer));
|
|
62
|
+
}
|
|
63
|
+
case 'argsEnhancers': {
|
|
64
|
+
return value.forEach((enhancer) => addArgsEnhancer(enhancer))
|
|
65
|
+
}
|
|
66
|
+
case 'globals':
|
|
67
|
+
case 'globalTypes': {
|
|
68
|
+
const v = {};
|
|
69
|
+
v[key] = value;
|
|
70
|
+
return addParameters(v, false);
|
|
71
|
+
}
|
|
72
|
+
case 'decorateStory':
|
|
73
|
+
case 'renderToDOM': {
|
|
74
|
+
return null; // This key is not handled directly in v6 mode.
|
|
75
|
+
}
|
|
76
|
+
default: {
|
|
77
|
+
// eslint-disable-next-line prefer-template
|
|
78
|
+
return console.log(key + ' was not supported :( !');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
/* TODO: not quite sure what to do with this, to fix HMR
|
|
85
|
+
if (import.meta.hot) {
|
|
86
|
+
import.meta.hot.accept();
|
|
87
|
+
}
|
|
88
|
+
*/
|
|
89
|
+
|
|
90
|
+
configStories();
|
|
91
|
+
`.trim();
|
|
92
|
+
return code;
|
|
93
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
import { normalizePath } from 'vite';
|
|
3
|
+
import type { Options } from '@storybook/core-common';
|
|
4
|
+
|
|
5
|
+
import { listStories } from './list-stories';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* This file is largely based on https://github.com/storybookjs/storybook/blob/d1195cbd0c61687f1720fefdb772e2f490a46584/lib/core-common/src/utils/to-importFn.ts
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Paths get passed either with no leading './' - e.g. `src/Foo.stories.js`,
|
|
13
|
+
* or with a leading `../` (etc), e.g. `../src/Foo.stories.js`.
|
|
14
|
+
* We want to deal in importPaths relative to the working dir, so we normalize
|
|
15
|
+
*/
|
|
16
|
+
function toImportPath(relativePath: string) {
|
|
17
|
+
return relativePath.startsWith('../') ? relativePath : `./${relativePath}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* This function takes an array of stories and creates a mapping between the stories' relative paths
|
|
22
|
+
* to the working directory and their dynamic imports. The import is done in an asynchronous function
|
|
23
|
+
* to delay loading. It then creates a function, `importFn(path)`, which resolves a path to an import
|
|
24
|
+
* function and this is called by Storybook to fetch a story dynamically when needed.
|
|
25
|
+
* @param stories An array of absolute story paths.
|
|
26
|
+
*/
|
|
27
|
+
async function toImportFn(stories: string[]) {
|
|
28
|
+
const objectEntries = stories.map((file) => {
|
|
29
|
+
return ` '${toImportPath(normalizePath(path.relative(process.cwd(), file)))}': async () => import('/@fs/${file}')`;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
return `
|
|
33
|
+
const importers = {
|
|
34
|
+
${objectEntries.join(',\n')}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export async function importFn(path) {
|
|
38
|
+
return importers[path]();
|
|
39
|
+
}
|
|
40
|
+
`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function generateImportFnScriptCode(options: Options) {
|
|
44
|
+
// First we need to get an array of stories and their absolute paths.
|
|
45
|
+
const stories = await listStories(options);
|
|
46
|
+
|
|
47
|
+
// We can then call toImportFn to create a function that can be used to load each story dynamically.
|
|
48
|
+
return (await toImportFn(stories)).trim();
|
|
49
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { loadPreviewOrConfigFile } from '@storybook/core-common';
|
|
2
|
+
import { normalizePath } from 'vite';
|
|
3
|
+
|
|
4
|
+
import type { ExtendedOptions } from './types';
|
|
5
|
+
|
|
6
|
+
interface GenerateModernIframeScriptCodeOptions {
|
|
7
|
+
storiesFilename: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function generateModernIframeScriptCode(
|
|
11
|
+
options: ExtendedOptions,
|
|
12
|
+
{ storiesFilename }: GenerateModernIframeScriptCodeOptions
|
|
13
|
+
) {
|
|
14
|
+
const { presets, configDir } = options;
|
|
15
|
+
|
|
16
|
+
const previewOrConfigFile = loadPreviewOrConfigFile({ configDir });
|
|
17
|
+
const presetEntries = await presets.apply('config', [], options);
|
|
18
|
+
const configEntries = [...presetEntries, previewOrConfigFile]
|
|
19
|
+
.filter(Boolean)
|
|
20
|
+
.map((configEntry) => `/@fs/${normalizePath(configEntry)}`);
|
|
21
|
+
|
|
22
|
+
// noinspection UnnecessaryLocalVariableJS
|
|
23
|
+
/**
|
|
24
|
+
* This code is largely taken from https://github.com/storybookjs/storybook/blob/d1195cbd0c61687f1720fefdb772e2f490a46584/lib/builder-webpack4/src/preview/virtualModuleModernEntry.js.handlebars
|
|
25
|
+
* Some small tweaks were made to `getProjectAnnotations` (since `import()` needs to be resolved asynchronously)
|
|
26
|
+
* and the HMR implementation has been tweaked to work with Vite.
|
|
27
|
+
* @todo Inline variable and remove `noinspection`
|
|
28
|
+
*/
|
|
29
|
+
// language=JavaScript
|
|
30
|
+
const code = `
|
|
31
|
+
import global from 'global';
|
|
32
|
+
|
|
33
|
+
import { composeConfigs, PreviewWeb } from '@storybook/preview-web';
|
|
34
|
+
import { ClientApi } from '@storybook/client-api';
|
|
35
|
+
import { addons } from '@storybook/addons';
|
|
36
|
+
import createPostMessageChannel from '@storybook/channel-postmessage';
|
|
37
|
+
import createWebSocketChannel from '@storybook/channel-websocket';
|
|
38
|
+
|
|
39
|
+
import { importFn } from '${storiesFilename}';
|
|
40
|
+
|
|
41
|
+
const { SERVER_CHANNEL_URL } = global;
|
|
42
|
+
|
|
43
|
+
const getProjectAnnotations = async () =>
|
|
44
|
+
composeConfigs(await Promise.all([${configEntries
|
|
45
|
+
.map((configEntry) => `import('${configEntry}')`)
|
|
46
|
+
.join(',\n')}]));
|
|
47
|
+
|
|
48
|
+
const channel = createPostMessageChannel({ page: 'preview' });
|
|
49
|
+
addons.setChannel(channel);
|
|
50
|
+
|
|
51
|
+
if (SERVER_CHANNEL_URL) {
|
|
52
|
+
const serverChannel = createWebSocketChannel({ url: SERVER_CHANNEL_URL });
|
|
53
|
+
addons.setServerChannel(serverChannel);
|
|
54
|
+
window.__STORYBOOK_SERVER_CHANNEL__ = serverChannel;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const preview = new PreviewWeb();
|
|
58
|
+
|
|
59
|
+
window.__STORYBOOK_PREVIEW__ = preview;
|
|
60
|
+
window.__STORYBOOK_STORY_STORE__ = preview.storyStore;
|
|
61
|
+
window.__STORYBOOK_ADDONS_CHANNEL__ = channel;
|
|
62
|
+
window.__STORYBOOK_CLIENT_API__ = new ClientApi({ storyStore: preview.storyStore });
|
|
63
|
+
|
|
64
|
+
preview.initialize({ importFn, getProjectAnnotations });
|
|
65
|
+
|
|
66
|
+
if (import.meta.hot) {
|
|
67
|
+
import.meta.hot.accept('${storiesFilename}', (newModule) => {
|
|
68
|
+
|
|
69
|
+
// importFn has changed so we need to patch the new one in
|
|
70
|
+
preview.onStoriesChanged({ importFn: newModule.importFn });
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
import.meta.hot.accept(${JSON.stringify(configEntries)}, ([...newConfigEntries]) => {
|
|
74
|
+
const newGetProjectAnnotations = () => composeConfigs(newConfigEntries);
|
|
75
|
+
|
|
76
|
+
// getProjectAnnotations has changed so we need to patch the new one in
|
|
77
|
+
preview.onGetProjectAnnotationsChanged({ getProjectAnnotations: newGetProjectAnnotations });
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
`.trim();
|
|
81
|
+
return code;
|
|
82
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @see https://github.com/storybookjs/addon-svelte-csf/blob/f72b8f28dabbb99c92e12d0170d3c1db4397ee7c/src/parser/extract-stories.ts
|
|
3
|
+
*/
|
|
4
|
+
declare module '@storybook/addon-svelte-csf/dist/cjs/parser/extract-stories' {
|
|
5
|
+
interface StoryDef {
|
|
6
|
+
name: string;
|
|
7
|
+
template: boolean;
|
|
8
|
+
source: string;
|
|
9
|
+
hasArgs: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface StoriesDef {
|
|
13
|
+
stories: Record<string, StoryDef>;
|
|
14
|
+
allocatedIds: string[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function extractStories(component: string): { stories: StoriesDef; allocatedIds: string[] };
|
|
18
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @see https://github.com/storybookjs/addon-svelte-csf/blob/f72b8f28dabbb99c92e12d0170d3c1db4397ee7c/src/parser/svelte-stories-loader.ts
|
|
3
|
+
* @see https://github.com/sveltejs/svelte/blob/deed340cf5d9c278f9a0605297ad6e4a3a1579d9/src/compiler/compile/utils/get_name_from_filename.ts
|
|
4
|
+
*/
|
|
5
|
+
declare module '@storybook/addon-svelte-csf/dist/cjs/parser/svelte-stories-loader' {
|
|
6
|
+
function getNameFromFilename(filename: string): string;
|
|
7
|
+
}
|
package/dist/build.d.ts
ADDED
package/dist/build.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.build = void 0;
|
|
4
|
+
const vite_1 = require("vite");
|
|
5
|
+
const envs_1 = require("./envs");
|
|
6
|
+
const vite_config_1 = require("./vite-config");
|
|
7
|
+
async function build(options) {
|
|
8
|
+
const { presets } = options;
|
|
9
|
+
const baseConfig = await (0, vite_config_1.commonConfig)(options, 'development');
|
|
10
|
+
const config = {
|
|
11
|
+
...baseConfig,
|
|
12
|
+
build: {
|
|
13
|
+
outDir: options.outputDir,
|
|
14
|
+
emptyOutDir: false,
|
|
15
|
+
sourcemap: true,
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
const finalConfig = await presets.apply('viteFinal', config, options);
|
|
19
|
+
const envsRaw = await presets.apply('env');
|
|
20
|
+
// Stringify env variables after getting `envPrefix` from the final config
|
|
21
|
+
const envs = (0, envs_1.stringifyProcessEnvs)(envsRaw, finalConfig.envPrefix);
|
|
22
|
+
// Update `define`
|
|
23
|
+
finalConfig.define = {
|
|
24
|
+
...finalConfig.define,
|
|
25
|
+
...envs,
|
|
26
|
+
};
|
|
27
|
+
await (0, vite_1.build)(finalConfig);
|
|
28
|
+
}
|
|
29
|
+
exports.build = build;
|
|
30
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../build.ts"],"names":[],"mappings":";;;AAAA,+BAA0C;AAC1C,iCAA8C;AAC9C,+CAA6C;AAItC,KAAK,UAAU,KAAK,CAAC,OAAwB;IAClD,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAE5B,MAAM,UAAU,GAAG,MAAM,IAAA,0BAAY,EAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IAC9D,MAAM,MAAM,GAAG;QACb,GAAG,UAAU;QACb,KAAK,EAAE;YACL,MAAM,EAAE,OAAO,CAAC,SAAS;YACzB,WAAW,EAAE,KAAK;YAClB,SAAS,EAAE,IAAI;SAChB;KACF,CAAC;IAEF,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAEtE,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,KAAK,CAAmB,KAAK,CAAC,CAAC;IAC7D,0EAA0E;IAC1E,MAAM,IAAI,GAAG,IAAA,2BAAoB,EAAC,OAAO,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC;IAClE,kBAAkB;IAClB,WAAW,CAAC,MAAM,GAAG;QACnB,GAAG,WAAW,CAAC,MAAM;QACrB,GAAG,IAAI;KACR,CAAC;IAEF,MAAM,IAAA,YAAS,EAAC,WAAW,CAAC,CAAC;AAC/B,CAAC;AAzBD,sBAyBC","sourcesContent":["import { build as viteBuild } from 'vite';\nimport { stringifyProcessEnvs } from './envs';\nimport { commonConfig } from './vite-config';\n\nimport type { EnvsRaw, ExtendedOptions } from './types';\n\nexport async function build(options: ExtendedOptions) {\n const { presets } = options;\n\n const baseConfig = await commonConfig(options, 'development');\n const config = {\n ...baseConfig,\n build: {\n outDir: options.outputDir,\n emptyOutDir: false, // do not clean before running Vite build - Storybook has already added assets in there!\n sourcemap: true,\n },\n };\n\n const finalConfig = await presets.apply('viteFinal', config, options);\n\n const envsRaw = await presets.apply<Promise<EnvsRaw>>('env');\n // Stringify env variables after getting `envPrefix` from the final config\n const envs = stringifyProcessEnvs(envsRaw, finalConfig.envPrefix);\n // Update `define`\n finalConfig.define = {\n ...finalConfig.define,\n ...envs,\n };\n\n await viteBuild(finalConfig);\n}\n"]}
|