@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.
Files changed (94) hide show
  1. package/README.md +120 -0
  2. package/build.ts +32 -0
  3. package/code-generator-plugin.ts +102 -0
  4. package/codegen-entries.ts +46 -0
  5. package/codegen-iframe-script.ts +93 -0
  6. package/codegen-importfn-script.ts +49 -0
  7. package/codegen-modern-iframe-script.ts +82 -0
  8. package/declarations/extract-stories.d.ts +18 -0
  9. package/declarations/svetle-stories-loader.d.ts +7 -0
  10. package/dist/build.d.ts +2 -0
  11. package/dist/build.js +30 -0
  12. package/dist/build.js.map +1 -0
  13. package/dist/code-generator-plugin.d.ts +3 -0
  14. package/dist/code-generator-plugin.js +124 -0
  15. package/dist/code-generator-plugin.js.map +1 -0
  16. package/dist/codegen-entries.d.ts +4 -0
  17. package/dist/codegen-entries.js +45 -0
  18. package/dist/codegen-entries.js.map +1 -0
  19. package/dist/codegen-iframe-script.d.ts +7 -0
  20. package/dist/codegen-iframe-script.js +82 -0
  21. package/dist/codegen-iframe-script.js.map +1 -0
  22. package/dist/codegen-importfn-script.d.ts +2 -0
  23. package/dist/codegen-importfn-script.js +65 -0
  24. package/dist/codegen-importfn-script.js.map +1 -0
  25. package/dist/codegen-modern-iframe-script.d.ts +6 -0
  26. package/dist/codegen-modern-iframe-script.js +75 -0
  27. package/dist/codegen-modern-iframe-script.js.map +1 -0
  28. package/dist/envs.d.ts +8 -0
  29. package/dist/envs.js +43 -0
  30. package/dist/envs.js.map +1 -0
  31. package/dist/index.d.ts +9 -0
  32. package/dist/index.js +80 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/inject-export-order-plugin.d.ts +8 -0
  35. package/dist/inject-export-order-plugin.js +28 -0
  36. package/dist/inject-export-order-plugin.js.map +1 -0
  37. package/dist/list-stories.d.ts +2 -0
  38. package/dist/list-stories.js +36 -0
  39. package/dist/list-stories.js.map +1 -0
  40. package/dist/mdx-plugin.d.ts +9 -0
  41. package/dist/mdx-plugin.js +29 -0
  42. package/dist/mdx-plugin.js.map +1 -0
  43. package/dist/mock-core-js.d.ts +5 -0
  44. package/dist/mock-core-js.js +22 -0
  45. package/dist/mock-core-js.js.map +1 -0
  46. package/dist/optimizeDeps.d.ts +9 -0
  47. package/dist/optimizeDeps.js +136 -0
  48. package/dist/optimizeDeps.js.map +1 -0
  49. package/dist/plugins/vue-docgen.d.ts +4 -0
  50. package/dist/plugins/vue-docgen.js +18 -0
  51. package/dist/plugins/vue-docgen.js.map +1 -0
  52. package/dist/source-loader-plugin.d.ts +10 -0
  53. package/dist/source-loader-plugin.js +27 -0
  54. package/dist/source-loader-plugin.js.map +1 -0
  55. package/dist/svelte/csf-plugin.d.ts +8 -0
  56. package/dist/svelte/csf-plugin.js +64 -0
  57. package/dist/svelte/csf-plugin.js.map +1 -0
  58. package/dist/transform-iframe-html.d.ts +3 -0
  59. package/dist/transform-iframe-html.js +32 -0
  60. package/dist/transform-iframe-html.js.map +1 -0
  61. package/dist/types/envs-raw.type.d.ts +1 -0
  62. package/dist/types/envs-raw.type.js +3 -0
  63. package/dist/types/envs-raw.type.js.map +1 -0
  64. package/dist/types/extended-options.type.d.ts +7 -0
  65. package/dist/types/extended-options.type.js +3 -0
  66. package/dist/types/extended-options.type.js.map +1 -0
  67. package/dist/types/index.d.ts +2 -0
  68. package/dist/types/index.js +15 -0
  69. package/dist/types/index.js.map +1 -0
  70. package/dist/vite-config.d.ts +9 -0
  71. package/dist/vite-config.js +126 -0
  72. package/dist/vite-config.js.map +1 -0
  73. package/dist/vite-server.d.ts +4 -0
  74. package/dist/vite-server.js +37 -0
  75. package/dist/vite-server.js.map +1 -0
  76. package/envs.ts +50 -0
  77. package/index.ts +75 -0
  78. package/inject-export-order-plugin.ts +28 -0
  79. package/input/iframe.html +37 -0
  80. package/list-stories.ts +21 -0
  81. package/mdx-plugin.ts +24 -0
  82. package/mock-core-js.ts +17 -0
  83. package/optimizeDeps.ts +123 -0
  84. package/package.json +38 -0
  85. package/plugins/vue-docgen.ts +16 -0
  86. package/source-loader-plugin.ts +21 -0
  87. package/svelte/csf-plugin.ts +46 -0
  88. package/transform-iframe-html.ts +36 -0
  89. package/tsconfig.json +9 -0
  90. package/types/envs-raw.type.ts +1 -0
  91. package/types/extended-options.type.ts +9 -0
  92. package/types/index.ts +2 -0
  93. package/vite-config.ts +128 -0
  94. 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
+ }
@@ -0,0 +1,2 @@
1
+ import type { ExtendedOptions } from './types';
2
+ export declare function build(options: ExtendedOptions): Promise<void>;
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"]}
@@ -0,0 +1,3 @@
1
+ import type { Plugin } from 'vite';
2
+ import type { ExtendedOptions } from './types';
3
+ export declare function codeGeneratorPlugin(options: ExtendedOptions): Plugin;