@storybook-astro/framework 0.1.0-beta.9 → 1.0.1

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 (92) hide show
  1. package/README.md +38 -0
  2. package/dist/base-IRZo3zgK.d.ts +23 -0
  3. package/dist/chunk-4SWPVM6R.js +96 -0
  4. package/dist/chunk-4SWPVM6R.js.map +1 -0
  5. package/dist/chunk-5EF25G5S.js +69 -0
  6. package/dist/chunk-5EF25G5S.js.map +1 -0
  7. package/dist/chunk-7GHEQUPV.js +439 -0
  8. package/dist/chunk-7GHEQUPV.js.map +1 -0
  9. package/dist/chunk-C5OH4VBR.js +492 -0
  10. package/dist/chunk-C5OH4VBR.js.map +1 -0
  11. package/dist/chunk-DNGQBPT7.js +15 -0
  12. package/dist/chunk-DNGQBPT7.js.map +1 -0
  13. package/dist/chunk-E4LB75JN.js +89 -0
  14. package/dist/chunk-E4LB75JN.js.map +1 -0
  15. package/dist/chunk-KSDXET2L.js +660 -0
  16. package/dist/chunk-KSDXET2L.js.map +1 -0
  17. package/dist/chunk-PJEDXZVN.js +240 -0
  18. package/dist/chunk-PJEDXZVN.js.map +1 -0
  19. package/dist/dist-HJOEPVRQ.js +15574 -0
  20. package/dist/dist-HJOEPVRQ.js.map +1 -0
  21. package/dist/index.d.ts +42 -0
  22. package/dist/index.js +13 -64
  23. package/dist/index.js.map +1 -1
  24. package/dist/integrations/index.d.ts +138 -0
  25. package/dist/integrations/index.js +8 -196
  26. package/dist/integrations/index.js.map +1 -1
  27. package/dist/middleware.d.ts +26 -0
  28. package/dist/middleware.js +179 -0
  29. package/dist/middleware.js.map +1 -0
  30. package/dist/portable-stories-BvdaQigq.d.ts +83 -0
  31. package/dist/preset.d.ts +14 -0
  32. package/dist/preset.js +5 -1
  33. package/dist/testing.d.ts +27 -0
  34. package/dist/testing.js +324 -15539
  35. package/dist/testing.js.map +1 -1
  36. package/dist/types-CHTsRtA7.d.ts +42 -0
  37. package/dist/viteStorybookAstroMiddlewarePlugin-NP2E52IC.js +11 -0
  38. package/dist/viteStorybookAstroMiddlewarePlugin-NP2E52IC.js.map +1 -0
  39. package/dist/vitest/index.d.ts +19 -0
  40. package/dist/vitest/index.js +229 -0
  41. package/dist/vitest/index.js.map +1 -0
  42. package/package.json +31 -17
  43. package/src/importAstroConfig.ts +11 -0
  44. package/src/index.ts +20 -6
  45. package/src/integrations/alpine.ts +5 -2
  46. package/src/integrations/base.ts +2 -2
  47. package/src/integrations/moduleResolver.ts +43 -0
  48. package/src/integrations/preact.ts +5 -2
  49. package/src/integrations/react.ts +5 -2
  50. package/src/integrations/solid.ts +5 -2
  51. package/src/integrations/svelte.ts +5 -2
  52. package/src/integrations/vue.ts +5 -2
  53. package/src/lib/sanitization.test.ts +232 -0
  54. package/src/lib/sanitization.ts +338 -0
  55. package/src/lib/ssr-load-module-with-fs-fallback.ts +29 -0
  56. package/src/middleware.test.ts +48 -0
  57. package/src/middleware.ts +204 -96
  58. package/src/module-mocks.ts +16 -0
  59. package/src/msw-helpers.ts +1 -0
  60. package/src/msw.ts +58 -0
  61. package/src/preset.ts +47 -3
  62. package/src/rules-options.test.ts +71 -0
  63. package/src/rules-options.ts +87 -0
  64. package/src/rules.test.ts +183 -0
  65. package/src/rules.ts +314 -0
  66. package/src/testing/astro-runtime.ts +219 -0
  67. package/src/testing/component-utils.ts +32 -0
  68. package/src/testing/index.ts +2 -0
  69. package/src/testing/integration-config.ts +121 -0
  70. package/src/testing/project-root.ts +185 -0
  71. package/src/testing/renderer-daemon.ts +269 -0
  72. package/src/testing/story-composition.ts +33 -0
  73. package/src/testing/types.ts +14 -0
  74. package/src/testing/working-directory.ts +28 -0
  75. package/src/testing.ts +1 -254
  76. package/src/types.ts +16 -4
  77. package/src/virtual.d.ts +2 -1
  78. package/src/vite/createVirtualModulePlugin.test.ts +80 -0
  79. package/src/vite/createVirtualModulePlugin.ts +25 -0
  80. package/src/viteAstroContainerRenderersPlugin.ts +60 -26
  81. package/src/vitePluginAstro.ts +12 -5
  82. package/src/vitePluginAstroBuildPrerender.ts +665 -204
  83. package/src/vitePluginAstroRoutesFallback.ts +37 -0
  84. package/src/vitePluginAstroVueFallback.ts +47 -0
  85. package/src/viteStorybookAstroMiddlewarePlugin.ts +88 -12
  86. package/src/viteStorybookRendererFallbackPlugin.ts +13 -23
  87. package/src/vitest/config.ts +95 -0
  88. package/src/vitest/global-setup.ts +16 -0
  89. package/src/vitest/index.ts +2 -0
  90. package/src/vitest/vite-plugins.ts +187 -0
  91. package/dist/chunk-KTGNRGDJ.js +0 -561
  92. package/dist/chunk-KTGNRGDJ.js.map +0 -1
package/src/testing.ts CHANGED
@@ -1,254 +1 @@
1
- /**
2
- * Testing utilities for @storybook-astro/framework
3
- *
4
- * Provides test helpers for validating Storybook stories in Vitest,
5
- * and Vite plugins needed for the test environment.
6
- *
7
- * @example
8
- * ```ts
9
- * // In a test file:
10
- * import { composeStories } from '@storybook-astro/framework';
11
- * import { testStoryRenders, testStoryComposition } from '@storybook-astro/framework/testing';
12
- * import * as stories from './Card.stories.jsx';
13
- *
14
- * const { Default } = composeStories(stories);
15
- * testStoryComposition('Default', Default);
16
- * testStoryRenders('Card Default', Default);
17
- * ```
18
- *
19
- * @example
20
- * ```ts
21
- * // In vitest.config.ts:
22
- * import { cjsInteropPlugin } from '@storybook-astro/framework/testing';
23
- * import { defineConfig } from 'vitest/config';
24
- *
25
- * export default defineConfig({
26
- * plugins: [cjsInteropPlugin()],
27
- * // ...
28
- * });
29
- * ```
30
- */
31
-
32
- // eslint-disable-next-line n/no-extraneous-import
33
- import { test, expect } from 'vitest';
34
- import { existsSync, readFileSync } from 'node:fs';
35
- import { join } from 'node:path';
36
- import type { Plugin } from 'vite';
37
-
38
- // ---------------------------------------------------------------------------
39
- // Test helpers
40
- // ---------------------------------------------------------------------------
41
-
42
- /**
43
- * Registers a Vitest test that validates a composed story can render.
44
- *
45
- * The test will:
46
- * - Pass if the component renders successfully in Storybook
47
- * - Pass if the Storybook runtime is not available (expected in Vitest)
48
- * - Pass if a framework hits an SSR-only limitation (e.g. Solid)
49
- * - Fail if the component has a broken framework integration or missing renderer
50
- *
51
- * @param storyName - Display name for the test
52
- * @param story - A composed story returned by `composeStories`
53
- */
54
- export function testStoryRenders(storyName: string, story: any) {
55
- test(`${storyName} renders in Storybook`, async () => {
56
- expect(story).toBeDefined();
57
- expect(typeof story).toBe('function');
58
-
59
- try {
60
- // First try calling the story directly - this will use our custom render function
61
- // which can detect broken framework integrations immediately
62
- const directResult = story();
63
-
64
- // If direct call succeeds, try the full Storybook run method
65
- const result = await story.run?.() || directResult;
66
-
67
- // If we get here, the component should have rendered successfully
68
- expect(result).toBeDefined();
69
-
70
- // For Astro components, check that we have component and args
71
- if (result.component) {
72
- expect(result.component).toBeDefined();
73
- expect(result.args).toBeDefined();
74
- }
75
-
76
- console.warn(`✓ ${storyName} rendered successfully`);
77
-
78
- } catch (error: any) {
79
- const errorMessage = error.message;
80
-
81
- // Check if this is an expected error when Storybook is not running
82
- if (errorMessage.includes('renderToCanvas is not a function')) {
83
- // This indicates the component is properly configured but Storybook runtime isn't available
84
- // This is acceptable for Astro components that work in Storybook
85
- console.warn(`✓ ${storyName} is properly configured (Storybook runtime not available)`);
86
-
87
- return;
88
- }
89
-
90
- // SSR limitation: some framework components (e.g. Solid) are compiled in
91
- // SSR mode for the test environment. Client-only APIs are unavailable
92
- // during story.run(), but the component works in Storybook's browser.
93
- if (errorMessage.includes('Client-only API called on the server side')) {
94
- console.warn(`✓ ${storyName} is properly configured (SSR-only test limitation)`);
95
-
96
- return;
97
- }
98
-
99
- // Check for renderer not found errors (indicates broken integration)
100
- if (errorMessage.includes('Renderer') && errorMessage.includes('not found')) {
101
- console.error(`✗ ${storyName} failed: ${errorMessage}`);
102
- throw new Error(`${storyName} has a broken framework integration: ${errorMessage}`);
103
- }
104
-
105
- // Check for missing renderer parameter
106
- if (errorMessage.includes('no renderer is specified')) {
107
- console.error(`✗ ${storyName} failed: ${errorMessage}`);
108
- throw new Error(`${storyName} is missing renderer parameter: ${errorMessage}`);
109
- }
110
-
111
- // Any other error indicates a real problem with the component
112
- console.error(`✗ ${storyName} failed with unexpected error:`, error);
113
- throw new Error(`${storyName} failed to render: ${errorMessage}`);
114
- }
115
- });
116
- }
117
-
118
- /**
119
- * Registers a Vitest test that checks basic story composition.
120
- *
121
- * Validates that the story can be imported, composed, and has the
122
- * expected name. Optionally checks that args match expected values.
123
- *
124
- * @param storyName - Expected `story.storyName` value
125
- * @param story - A composed story returned by `composeStories`
126
- * @param expectedArgs - Optional args to assert with `toEqual`
127
- */
128
- export function testStoryComposition(storyName: string, story: any, expectedArgs?: any) {
129
- test(`${storyName} can be composed`, () => {
130
- expect(story).toBeDefined();
131
- expect(typeof story).toBe('function');
132
- expect(story.storyName).toBe(storyName);
133
-
134
- if (expectedArgs) {
135
- expect(story.args).toEqual(expectedArgs);
136
- }
137
- });
138
- }
139
-
140
- // ---------------------------------------------------------------------------
141
- // Vite plugins for testing
142
- // ---------------------------------------------------------------------------
143
-
144
- /**
145
- * Vite plugin that wraps CJS modules with ESM-compatible shims.
146
- *
147
- * Vite 6's ESM module runner cannot evaluate raw CommonJS modules that use
148
- * `module.exports` or `exports`. This plugin detects CJS modules in
149
- * node_modules during transform and wraps them so they work in ESM context.
150
- *
151
- * Use this in your `vitest.config.ts` plugins array when testing with
152
- * Astro 6 and Vite 6+.
153
- *
154
- * @example
155
- * ```ts
156
- * import { cjsInteropPlugin } from '@storybook-astro/framework/testing';
157
- *
158
- * export default defineConfig({
159
- * plugins: [cjsInteropPlugin()],
160
- * });
161
- * ```
162
- */
163
- export function cjsInteropPlugin(): Plugin {
164
- return {
165
- name: 'cjs-esm-interop',
166
- enforce: 'pre',
167
- resolveId(id) {
168
- // When Vite resolves a bare import in SSR/test context, redirect
169
- // packages that have ESM entry points to those entries instead of
170
- // their CJS "main" or "require" entries.
171
- if (id.startsWith('.') || id.startsWith('/') || id.startsWith('\0') || id.includes('node_modules')) {return;}
172
-
173
- // Find the package's node_modules directory
174
- const parts = id.split('/');
175
- const pkgName = id.startsWith('@') ? parts.slice(0, 2).join('/') : parts[0];
176
- const subpath = parts.slice(pkgName.split('/').length).join('/');
177
-
178
- // Only redirect the main entry (no subpath or common subpaths)
179
- if (subpath && !['server-renderer', 'server', 'client'].includes(subpath)) {return;}
180
-
181
- try {
182
- // Find the package.json
183
- const nmDir = join(process.cwd(), 'node_modules', pkgName);
184
- const pkgJsonPath = join(nmDir, 'package.json');
185
-
186
- if (!existsSync(pkgJsonPath)) {return;}
187
-
188
- const pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
189
-
190
- // Check for ESM entry in exports map
191
- const exportKey = subpath ? `./${subpath}` : '.';
192
- const exportEntry = pkgJson.exports?.[exportKey];
193
-
194
- if (exportEntry) {
195
- const importEntry = exportEntry.import;
196
-
197
- if (importEntry) {
198
- const esmPath = typeof importEntry === 'string'
199
- ? importEntry
200
- : importEntry.default || importEntry.node;
201
-
202
- if (esmPath) {
203
- const resolved = join(nmDir, esmPath);
204
-
205
- if (existsSync(resolved)) {
206
- return resolved;
207
- }
208
- }
209
- }
210
- }
211
-
212
- // Fallback: check the "module" field
213
- if (!subpath && pkgJson.module) {
214
- const resolved = join(nmDir, pkgJson.module);
215
-
216
- if (existsSync(resolved)) {
217
- return resolved;
218
- }
219
- }
220
- } catch {
221
- // Ignore resolution errors
222
- }
223
- },
224
- transform(code, id) {
225
- // Only transform node_modules files
226
- if (!id.includes('node_modules')) {return;}
227
- // Skip virtual modules
228
- if (id.startsWith('\0')) {return;}
229
- // Skip files that already use ESM exports
230
- if (/\bexport\s+(default|const|let|var|function|class|\{|\*)/.test(code)) {return;}
231
- // Only wrap files that use CJS patterns
232
- if (!code.includes('module.exports') && !code.includes('exports.')) {return;}
233
-
234
- const dirPath = id.substring(0, id.lastIndexOf('/'));
235
- const fileName = id;
236
-
237
-
238
- return {
239
- code: [
240
- 'import { createRequire as __createRequire } from "module";',
241
- `var __require = __createRequire("file://${dirPath}/");`,
242
- 'var module = { exports: {} };',
243
- 'var exports = module.exports;',
244
- 'function require(id) { return __require(id); }',
245
- `var __dirname = ${JSON.stringify(dirPath)};`,
246
- `var __filename = ${JSON.stringify(fileName)};`,
247
- code,
248
- 'export default module.exports;',
249
- ].join('\n'),
250
- map: null,
251
- };
252
- }
253
- };
254
- }
1
+ export * from './testing/index.ts';
package/src/types.ts CHANGED
@@ -1,18 +1,30 @@
1
1
  import type { CompatibleString, Options } from 'storybook/internal/types';
2
2
  import type { InlineConfig } from 'vite';
3
3
  import type { Integration } from './integrations/index.ts';
4
+ import type { SanitizationOptions } from './lib/sanitization.ts';
5
+ import type { StoryRulesOptions } from './rules-options.ts';
4
6
 
5
7
  type FrameworkName = CompatibleString<'@storybook-astro/framework'>;
6
8
 
7
- export type { Integration };
9
+ export type { Integration, SanitizationOptions, StoryRulesOptions };
10
+
11
+ export type RenderStoryInput = {
12
+ id: string;
13
+ title?: string;
14
+ name?: string;
15
+ };
16
+
8
17
  export type FrameworkOptions = {
9
- integrations: Integration[];
18
+ integrations?: Integration[];
19
+ sanitization?: SanitizationOptions;
20
+ storyRules?: StoryRulesOptions;
21
+ resolveFrom?: string;
10
22
  };
11
23
 
12
24
  type StorybookConfigFramework = {
13
25
  framework: {
14
26
  name: FrameworkName;
15
- options: FrameworkOptions;
27
+ options?: FrameworkOptions;
16
28
  };
17
29
  };
18
30
 
@@ -21,5 +33,5 @@ export type StorybookConfig = StorybookConfigFramework;
21
33
  type ViteFinal = (config: InlineConfig, options: Options) => InlineConfig | Promise<InlineConfig>;
22
34
 
23
35
  export type StorybookConfigVite = {
24
- viteFinal?: ViteFinal;
36
+ viteFinal?: ViteFinal;
25
37
  };
package/src/virtual.d.ts CHANGED
@@ -2,6 +2,7 @@ declare module 'virtual:astro-container-renderers' {
2
2
  import type { experimental_AstroContainer as AstroContainer } from 'astro/container';
3
3
 
4
4
  export function addRenderers(container: AstroContainer): void;
5
+ export function resolveClientModules(specifier: string): string | undefined;
5
6
  }
6
7
 
7
- declare module 'virtual:storybook-renderer-fallback' {}
8
+ declare module 'virtual:storybook-renderer-fallback' {}
@@ -0,0 +1,80 @@
1
+ import type { PluginOption } from 'vite';
2
+ import { describe, expect, test, vi } from 'vitest';
3
+ import { createVirtualModulePlugin } from './createVirtualModulePlugin.ts';
4
+
5
+ function getPlugin(pluginOption: PluginOption) {
6
+ if (Array.isArray(pluginOption)) {
7
+ throw new Error('Expected a single plugin object, but got a plugin array.');
8
+ }
9
+
10
+ if (!pluginOption || typeof pluginOption !== 'object') {
11
+ throw new Error('Expected plugin option to be an object.');
12
+ }
13
+
14
+ return pluginOption;
15
+ }
16
+
17
+ function getHookHandler<T extends (...args: unknown[]) => unknown>(hook: unknown): T {
18
+ if (typeof hook === 'function') {
19
+ return hook as T;
20
+ }
21
+
22
+ if (
23
+ typeof hook === 'object' &&
24
+ hook !== null &&
25
+ 'handler' in hook &&
26
+ typeof (hook as { handler?: unknown }).handler === 'function'
27
+ ) {
28
+ return (hook as { handler: T }).handler;
29
+ }
30
+
31
+ throw new Error('Expected hook to be a function or an object with a handler function.');
32
+ }
33
+
34
+ describe('createVirtualModulePlugin', () => {
35
+ test('resolves configured virtual module id with a null-byte prefix', () => {
36
+ const pluginOption = createVirtualModulePlugin({
37
+ pluginName: 'test:virtual-module',
38
+ virtualModuleId: 'virtual:test-module',
39
+ load: () => 'export default true;'
40
+ });
41
+ const plugin = getPlugin(pluginOption);
42
+ const resolveId = getHookHandler<(id: string) => string | undefined>(plugin.resolveId);
43
+
44
+ expect(plugin.name).toBe('test:virtual-module');
45
+ expect(resolveId('virtual:test-module')).toBe('\0virtual:test-module');
46
+ expect(resolveId('virtual:other-module')).toBeUndefined();
47
+ });
48
+
49
+ test('loads module content only for the resolved virtual module id', async () => {
50
+ const load = vi.fn(() => 'export const message = "hello";');
51
+ const pluginOption = createVirtualModulePlugin({
52
+ pluginName: 'test:virtual-module',
53
+ virtualModuleId: 'virtual:test-module',
54
+ load
55
+ });
56
+ const plugin = getPlugin(pluginOption);
57
+ const loadModule = getHookHandler<(id: string) => Promise<string | undefined>>(plugin.load);
58
+
59
+ const result = await loadModule('\0virtual:test-module');
60
+
61
+ expect(result).toBe('export const message = "hello";');
62
+ expect(load).toHaveBeenCalledTimes(1);
63
+ expect(load).toHaveBeenCalledWith('\0virtual:test-module');
64
+
65
+ await expect(loadModule('virtual:test-module')).resolves.toBeUndefined();
66
+ expect(load).toHaveBeenCalledTimes(1);
67
+ });
68
+
69
+ test('supports asynchronous virtual module loaders', async () => {
70
+ const pluginOption = createVirtualModulePlugin({
71
+ pluginName: 'test:virtual-module',
72
+ virtualModuleId: 'virtual:test-module',
73
+ load: async () => 'export default "async";'
74
+ });
75
+ const plugin = getPlugin(pluginOption);
76
+ const loadModule = getHookHandler<(id: string) => Promise<string | undefined>>(plugin.load);
77
+
78
+ await expect(loadModule('\0virtual:test-module')).resolves.toBe('export default "async";');
79
+ });
80
+ });
@@ -0,0 +1,25 @@
1
+ import type { PluginOption } from 'vite';
2
+
3
+ type CreateVirtualModulePluginOptions = {
4
+ pluginName: string;
5
+ virtualModuleId: string;
6
+ load: (id: string) => string | Promise<string> | undefined;
7
+ };
8
+
9
+ export function createVirtualModulePlugin(options: CreateVirtualModulePluginOptions): PluginOption {
10
+ const resolvedVirtualModuleId = `\0${options.virtualModuleId}`;
11
+
12
+ return {
13
+ name: options.pluginName,
14
+ resolveId(id) {
15
+ if (id === options.virtualModuleId) {
16
+ return resolvedVirtualModuleId;
17
+ }
18
+ },
19
+ async load(id) {
20
+ if (id === resolvedVirtualModuleId) {
21
+ return options.load(id);
22
+ }
23
+ }
24
+ } satisfies PluginOption;
25
+ }
@@ -1,35 +1,69 @@
1
1
  import type { Integration } from './integrations/index.ts';
2
+ import { createVirtualModulePlugin } from './vite/createVirtualModulePlugin.ts';
2
3
 
3
- export function viteAstroContainerRenderersPlugin(integrations: Integration[]) {
4
+ type PluginOptions = {
5
+ mode?: 'development' | 'production';
6
+ staticModuleMap?: Record<string, string>;
7
+ };
8
+
9
+ export function viteAstroContainerRenderersPlugin(
10
+ integrations: Integration[],
11
+ options: PluginOptions = {}
12
+ ) {
4
13
  const safeIntegrations = integrations ?? [];
5
- const name = 'astro-container-renderers';
6
- const virtualModuleId = `virtual:${name}`;
7
- const resolvedVirtualModuleId = `\0${virtualModuleId}`;
8
-
9
- return {
10
- name,
11
-
12
- resolveId(id: string) {
13
- if (id === virtualModuleId) {
14
- return resolvedVirtualModuleId;
15
- }
16
- },
17
-
18
- load(id: string) {
19
- if (id === resolvedVirtualModuleId) {
20
- const importStatements = buildImportStatements(safeIntegrations);
21
-
22
- const code = `
23
- ${importStatements}
24
- export function addRenderers(container) {
25
- ${safeIntegrations.map((integration) => buildServerRenderer(integration) + '\n' + buildClientRenderer(integration)).join('\n')}
14
+ const mode = options.mode ?? 'development';
15
+ const staticModuleMap = options.staticModuleMap ?? {};
16
+
17
+ return createVirtualModulePlugin({
18
+ pluginName: 'storybook-astro:container-renderers',
19
+ virtualModuleId: 'virtual:astro-container-renderers',
20
+ load() {
21
+ const importStatements = buildImportStatements(safeIntegrations);
22
+ const clientResolvers =
23
+ mode === 'development'
24
+ ? safeIntegrations
25
+ .filter((integration) => typeof integration.resolveClient === 'function')
26
+ .map((integration) =>
27
+ integration.resolveClient.toString().replace(/^resolveClient/, 'function')
28
+ )
29
+ .join(',\n')
30
+ : '';
31
+
32
+ return `
33
+ ${importStatements}
34
+
35
+ export function addRenderers(container) {
36
+ ${safeIntegrations.map((integration) => buildServerRenderer(integration) + '\n' + buildClientRenderer(integration)).join('\n')}
37
+ }
38
+
39
+ const staticClientModules = ${JSON.stringify(staticModuleMap, null, 2)};
40
+
41
+ const clientModulesResolvers = [
42
+ ${clientResolvers}
43
+ ];
44
+
45
+ export function resolveClientModules(specifier) {
46
+ if (Object.hasOwn(staticClientModules, specifier)) {
47
+ return staticClientModules[specifier];
48
+ }
49
+
50
+ const normalizedSpecifier = specifier.replace(/\\\\/g, '/').replace(/\\?.*$/, '');
51
+
52
+ if (Object.hasOwn(staticClientModules, normalizedSpecifier)) {
53
+ return staticClientModules[normalizedSpecifier];
26
54
  }
27
- `;
28
55
 
29
- return code;
30
- }
56
+ for (const resolver of clientModulesResolvers) {
57
+ const resolution = resolver(specifier);
58
+
59
+ if (resolution) {
60
+ return resolution;
61
+ }
62
+ }
63
+ }
64
+ `;
31
65
  }
32
- };
66
+ });
33
67
  }
34
68
 
35
69
  function buildImportStatements(integrations: Integration[]) {
@@ -1,5 +1,6 @@
1
1
  import { mergeConfig, type InlineConfig } from 'vite';
2
2
  import type { Integration } from './integrations/index.ts';
3
+ import { importAstroConfig } from './importAstroConfig.ts';
3
4
 
4
5
  const ASTRO_PLUGINS_THAT_ARE_SUPPOSEDLY_NOT_NEEDED_IN_STORYBOOK = [
5
6
  '@astro/plugin-actions',
@@ -27,8 +28,14 @@ const ASTRO_PLUGINS_THAT_ARE_SUPPOSEDLY_NOT_NEEDED_IN_STORYBOOK = [
27
28
  'astro:vite-plugin-file-url'
28
29
  ];
29
30
 
30
- export async function mergeWithAstroConfig(config: InlineConfig, integrations: Integration[] = []) {
31
- const { getViteConfig } = await import('astro/config');
31
+ export async function mergeWithAstroConfig(
32
+ config: InlineConfig,
33
+ integrations: Integration[] = [],
34
+ resolveFrom = process.cwd(),
35
+ mode = 'development',
36
+ command: 'build' | 'serve' = 'serve'
37
+ ) {
38
+ const { getViteConfig } = await importAstroConfig(resolveFrom);
32
39
  const safeIntegrations = integrations ?? [];
33
40
 
34
41
  const astroConfig = await getViteConfig(
@@ -36,12 +43,12 @@ export async function mergeWithAstroConfig(config: InlineConfig, integrations: I
36
43
  {
37
44
  configFile: false,
38
45
  integrations: await Promise.all(
39
- safeIntegrations.map((integration) => integration.loadIntegration())
46
+ safeIntegrations.map((integration) => integration.loadIntegration(resolveFrom))
40
47
  )
41
48
  }
42
49
  )({
43
- mode: 'development',
44
- command: 'serve'
50
+ mode,
51
+ command
45
52
  });
46
53
 
47
54
  const filteredPlugins = astroConfig