@storybook-astro/framework 1.2.0 → 1.3.0-canary.2
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/dist/{base-IRZo3zgK.d.ts → base-DT67T5pi.d.ts} +1 -0
- package/dist/{chunk-T7NWIO5S.js → chunk-2EABPTOY.js} +5 -5
- package/dist/{chunk-PJEDXZVN.js → chunk-7YBE4TTI.js} +2 -1
- package/dist/chunk-7YBE4TTI.js.map +1 -0
- package/dist/{chunk-POHTFYST.js → chunk-AYYMNFI6.js} +104 -6
- package/dist/chunk-AYYMNFI6.js.map +1 -0
- package/dist/{chunk-OUEDTRBO.js → chunk-B454DGX6.js} +259 -67
- package/dist/chunk-B454DGX6.js.map +1 -0
- package/dist/chunk-B5HHF6FC.js +116 -0
- package/dist/chunk-B5HHF6FC.js.map +1 -0
- package/dist/chunk-CU57AJUW.js +1402 -0
- package/dist/chunk-CU57AJUW.js.map +1 -0
- package/dist/{chunk-DNGQBPT7.js → chunk-PUTCAN6X.js} +5 -2
- package/dist/{chunk-DNGQBPT7.js.map → chunk-PUTCAN6X.js.map} +1 -1
- package/dist/{chunk-4SWPVM6R.js → chunk-WUTCMEF5.js} +2 -2
- package/dist/index.d.ts +17 -7
- package/dist/index.js +6 -5
- package/dist/index.js.map +1 -1
- package/dist/integrations/index.d.ts +2 -1
- package/dist/integrations/index.js +1 -1
- package/dist/middleware.js +18 -131
- package/dist/middleware.js.map +1 -1
- package/dist/{types-C-jan6Px.d.ts → preset-BvgHg2of.d.ts} +8 -11
- package/dist/preset.d.ts +2 -10
- package/dist/preset.js +5 -4
- package/dist/renderer/renderer-dev.js +62 -0
- package/dist/renderer/renderer-dev.js.map +1 -0
- package/dist/renderer/renderer-server.js +92 -0
- package/dist/renderer/renderer-server.js.map +1 -0
- package/dist/renderer/renderer-static.js +54 -0
- package/dist/renderer/renderer-static.js.map +1 -0
- package/dist/testing.js +12 -11
- package/dist/testing.js.map +1 -1
- package/dist/{viteStorybookAstroMiddlewarePlugin-2EFKTECT.js → viteStorybookAstroMiddlewarePlugin-UB6ZLJ4B.js} +4 -3
- package/dist/vitest/global-setup.js +6 -5
- package/dist/vitest/global-setup.js.map +1 -1
- package/dist/vitest/index.d.ts +1 -1
- package/dist/vitest/index.js +3 -3
- package/package.json +14 -43
- package/src/astroImageService.ts +57 -0
- package/src/astroRenderHandler.ts +205 -0
- package/src/importAstroConfig.ts +1 -1
- package/src/index.ts +2 -0
- package/src/integrations/alpine.ts +1 -0
- package/src/integrations/base.ts +6 -0
- package/src/lib/revive-dates.test.ts +106 -0
- package/src/lib/revive-dates.ts +51 -0
- package/src/middleware.ts +29 -200
- package/src/module-mocks.ts +153 -5
- package/src/preset.ts +38 -8
- package/src/productionRenderRuntime.ts +187 -0
- package/src/rules.test.ts +52 -4
- package/src/rules.ts +54 -7
- package/src/server/index.ts +101 -31
- package/src/storyRulesRuntime.ts +34 -0
- package/src/storySsrVite.ts +240 -0
- package/src/types.ts +0 -9
- package/src/virtual.d.ts +17 -3
- package/src/vite/{astroFilesVirtualModulePlugin.ts → astroFilesPlugin.ts} +4 -4
- package/src/vite/sanitizeConfigPlugin.ts +18 -0
- package/src/vite/{storybookAstroServerAuthConfigVirtualModulePlugin.test.ts → serverAuthPlugin.test.ts} +7 -10
- package/src/vite/{storybookAstroServerAuthConfigVirtualModulePlugin.ts → serverAuthPlugin.ts} +6 -9
- package/src/vite/serverRuntimePlugin.ts +109 -0
- package/src/vite/{storybookAstroRulesConfigVirtualModulePlugin.ts → storyRulesPlugin.ts} +6 -7
- package/src/vite/{createVirtualModulePlugin.test.ts → virtualModulePlugin.test.ts} +5 -5
- package/src/vite/{createVirtualModulePlugin.ts → virtualModulePlugin.ts} +2 -2
- package/src/viteAstroContainerRenderersPlugin.ts +72 -2
- package/src/vitePluginAstroBuildPrerender.ts +75 -646
- package/src/vitePluginAstroBuildServer.ts +217 -165
- package/src/vitePluginAstroBuildShared.test.ts +87 -0
- package/src/vitePluginAstroBuildShared.ts +465 -0
- package/src/vitePluginStoryModuleMocks.ts +29 -0
- package/src/viteStorybookAstroMiddlewarePlugin.ts +8 -0
- package/src/viteStorybookAstroRendererPlugin.ts +13 -6
- package/src/viteStorybookRendererFallbackPlugin.ts +2 -2
- package/dist/chunk-OUEDTRBO.js.map +0 -1
- package/dist/chunk-PBISP7PA.js +0 -1137
- package/dist/chunk-PBISP7PA.js.map +0 -1
- package/dist/chunk-PJEDXZVN.js.map +0 -1
- package/dist/chunk-POHTFYST.js.map +0 -1
- package/dist/node/index.d.ts +0 -10
- package/dist/node/index.js +0 -10
- package/dist/node/index.js.map +0 -1
- package/src/vite/storybookAstroSanitizationConfigVirtualModulePlugin.ts +0 -21
- /package/dist/{chunk-T7NWIO5S.js.map → chunk-2EABPTOY.js.map} +0 -0
- /package/dist/{chunk-4SWPVM6R.js.map → chunk-WUTCMEF5.js.map} +0 -0
- /package/dist/{viteStorybookAstroMiddlewarePlugin-2EFKTECT.js.map → viteStorybookAstroMiddlewarePlugin-UB6ZLJ4B.js.map} +0 -0
package/src/middleware.ts
CHANGED
|
@@ -1,51 +1,34 @@
|
|
|
1
|
+
/// <reference path="./virtual.d.ts" />
|
|
2
|
+
|
|
1
3
|
import { pathToFileURL } from 'node:url';
|
|
2
4
|
import { experimental_AstroContainer as AstroContainer } from 'astro/container';
|
|
5
|
+
import { ensureAstroPassthroughImageService } from './astroImageService.ts';
|
|
6
|
+
import { createAstroRenderHandler, type HandlerProps } from './astroRenderHandler.ts';
|
|
3
7
|
import type { Integration } from './integrations/index.ts';
|
|
4
|
-
import { installPassthroughImageService } from './lib/passthrough-image-service.ts';
|
|
5
8
|
import type { SanitizationOptions } from './lib/sanitization.ts';
|
|
6
|
-
import {
|
|
7
|
-
import { resolveStoryModuleMock, withStoryModuleMocks } from './module-mocks.ts';
|
|
8
|
-
import { selectStoryRules, withStoryRuleCleanups } from './rules.ts';
|
|
9
|
-
import type { RenderStoryInput } from './types.ts';
|
|
9
|
+
import { resolveStoryModuleMock } from './module-mocks.ts';
|
|
10
10
|
import { addRenderers, resolveClientModules } from 'virtual:astro-container-renderers';
|
|
11
11
|
|
|
12
12
|
type ResolveRulesConfigModule = () => unknown | Promise<unknown>;
|
|
13
13
|
|
|
14
|
-
type AstroCreateResult = {
|
|
15
|
-
createAstro?: (...args: unknown[]) => unknown;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
type AstroComponentFactory = ((
|
|
19
|
-
result: AstroCreateResult,
|
|
20
|
-
props: unknown,
|
|
21
|
-
slots: unknown
|
|
22
|
-
) => unknown) & {
|
|
23
|
-
isAstroComponentFactory?: boolean;
|
|
24
|
-
moduleId?: string;
|
|
25
|
-
propagation?: unknown;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export type HandlerProps = {
|
|
29
|
-
component: string;
|
|
30
|
-
args?: Record<string, unknown>;
|
|
31
|
-
slots?: Record<string, unknown>;
|
|
32
|
-
story?: RenderStoryInput;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
14
|
type HandlerFactoryOptions = {
|
|
36
15
|
sanitization?: SanitizationOptions;
|
|
37
16
|
rulesConfigFilePath?: string;
|
|
38
17
|
resolveRulesConfigModule?: ResolveRulesConfigModule;
|
|
39
18
|
loadModule?: (id: string) => Promise<{ default: unknown }>;
|
|
19
|
+
invalidateModuleGraph?: () => void;
|
|
20
|
+
resolveModule?: (specifier: string) => string | undefined;
|
|
40
21
|
};
|
|
41
22
|
|
|
42
|
-
export
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
23
|
+
export type { HandlerProps };
|
|
24
|
+
|
|
25
|
+
export async function handlerFactory(
|
|
26
|
+
_integrations: Integration[],
|
|
27
|
+
options?: HandlerFactoryOptions
|
|
28
|
+
) {
|
|
29
|
+
ensureAstroPassthroughImageService();
|
|
46
30
|
|
|
47
31
|
const container = await AstroContainer.create({
|
|
48
|
-
// Somewhat hacky way to force client-side Storybook's Vite to resolve modules properly
|
|
49
32
|
resolve: async (specifier) => {
|
|
50
33
|
const mockedModule = resolveStoryModuleMock(specifier);
|
|
51
34
|
|
|
@@ -53,6 +36,12 @@ export async function handlerFactory(_integrations: Integration[], options?: Han
|
|
|
53
36
|
return mockedModule;
|
|
54
37
|
}
|
|
55
38
|
|
|
39
|
+
const customResolution = options?.resolveModule?.(specifier);
|
|
40
|
+
|
|
41
|
+
if (customResolution) {
|
|
42
|
+
return customResolution;
|
|
43
|
+
}
|
|
44
|
+
|
|
56
45
|
if (specifier.startsWith('astro:scripts')) {
|
|
57
46
|
return `/@id/${specifier}`;
|
|
58
47
|
}
|
|
@@ -68,7 +57,7 @@ export async function handlerFactory(_integrations: Integration[], options?: Han
|
|
|
68
57
|
});
|
|
69
58
|
|
|
70
59
|
addRenderers(container);
|
|
71
|
-
|
|
60
|
+
|
|
72
61
|
const loadModule =
|
|
73
62
|
options?.loadModule ??
|
|
74
63
|
((id: string) => {
|
|
@@ -76,173 +65,13 @@ export async function handlerFactory(_integrations: Integration[], options?: Han
|
|
|
76
65
|
|
|
77
66
|
return import(/* @vite-ignore */ normalizedId);
|
|
78
67
|
});
|
|
79
|
-
const componentCache = new Map<string, Promise<AstroComponentFactory>>();
|
|
80
|
-
let renderQueue = Promise.resolve<void>(undefined);
|
|
81
|
-
|
|
82
|
-
async function loadPatchedComponent(componentId: string, useCache = true) {
|
|
83
|
-
if (!useCache) {
|
|
84
|
-
const { default: component } = await loadModule(componentId);
|
|
85
|
-
|
|
86
|
-
return patchCreateAstroCompat(component);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (!componentCache.has(componentId)) {
|
|
90
|
-
componentCache.set(componentId, (async () => {
|
|
91
|
-
const { default: component } = await loadModule(componentId);
|
|
92
|
-
|
|
93
|
-
return patchCreateAstroCompat(component);
|
|
94
|
-
})());
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const cachedComponent = componentCache.get(componentId);
|
|
98
|
-
|
|
99
|
-
if (!cachedComponent) {
|
|
100
|
-
throw new Error(`Failed to load Astro component: ${componentId}`);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
try {
|
|
104
|
-
return await cachedComponent;
|
|
105
|
-
} catch (error) {
|
|
106
|
-
// Drop failed entries so transient/module errors can recover on the next request.
|
|
107
|
-
componentCache.delete(componentId);
|
|
108
|
-
throw error;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return async function handler(data: HandlerProps) {
|
|
113
|
-
const executeRender = async () => {
|
|
114
|
-
const rulesConfigModule = options?.resolveRulesConfigModule
|
|
115
|
-
? await options.resolveRulesConfigModule()
|
|
116
|
-
: undefined;
|
|
117
|
-
|
|
118
|
-
const selectedRules = await selectStoryRules({
|
|
119
|
-
configModule: rulesConfigModule,
|
|
120
|
-
configFilePath: options?.rulesConfigFilePath,
|
|
121
|
-
story: data.story
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
return withStoryRuleCleanups(selectedRules.cleanups, async () => {
|
|
125
|
-
return withStoryModuleMocks(selectedRules.moduleMocks, async () => {
|
|
126
|
-
const patchedComponent = await loadPatchedComponent(
|
|
127
|
-
data.component,
|
|
128
|
-
selectedRules.moduleMocks.size === 0
|
|
129
|
-
);
|
|
130
|
-
const processedArgs = await processImageMetadata(data.args ?? {});
|
|
131
|
-
const sanitizedPayload = sanitizeRenderPayload(
|
|
132
|
-
{
|
|
133
|
-
args: processedArgs,
|
|
134
|
-
slots: data.slots ?? {}
|
|
135
|
-
},
|
|
136
|
-
sanitizationOptions
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
return container.renderToString(
|
|
140
|
-
patchedComponent as Parameters<typeof container.renderToString>[0],
|
|
141
|
-
{
|
|
142
|
-
props: sanitizedPayload.args,
|
|
143
|
-
slots: sanitizedPayload.slots
|
|
144
|
-
}
|
|
145
|
-
);
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
const resultPromise = renderQueue.then(executeRender, executeRender);
|
|
151
|
-
|
|
152
|
-
renderQueue = resultPromise.then(
|
|
153
|
-
() => undefined,
|
|
154
|
-
() => undefined
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
return resultPromise;
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function patchCreateAstroCompat(component: unknown): AstroComponentFactory {
|
|
162
|
-
if (typeof component !== 'function') {
|
|
163
|
-
throw new Error('Expected Astro component factory to be a function.');
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const originalComponent = component as AstroComponentFactory;
|
|
167
|
-
const wrapped = ((result: AstroCreateResult, props: unknown, slots: unknown) => {
|
|
168
|
-
if (result && typeof result.createAstro === 'function') {
|
|
169
|
-
const originalCreateAstro = result.createAstro;
|
|
170
|
-
const runtimeExpectsAstroGlobal = originalCreateAstro.length >= 3;
|
|
171
68
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
return originalComponent(result, props, slots);
|
|
182
|
-
}) as AstroComponentFactory;
|
|
183
|
-
|
|
184
|
-
wrapped.isAstroComponentFactory = originalComponent.isAstroComponentFactory;
|
|
185
|
-
wrapped.moduleId = originalComponent.moduleId;
|
|
186
|
-
wrapped.propagation = originalComponent.propagation;
|
|
187
|
-
|
|
188
|
-
return wrapped;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
async function processImageMetadata(
|
|
192
|
-
args: Record<string, unknown>
|
|
193
|
-
): Promise<Record<string, unknown>> {
|
|
194
|
-
const processed: Record<string, unknown> = {};
|
|
195
|
-
|
|
196
|
-
for (const [key, value] of Object.entries(args)) {
|
|
197
|
-
if (isImageMetadata(value)) {
|
|
198
|
-
// Keep ImageMetadata as a plain object — Astro's image service checks
|
|
199
|
-
// isESMImportedImage (typeof src === 'object') and skips the /@fs/ string
|
|
200
|
-
// validation that throws LocalImageUsedWrongly. Converting to a URL string
|
|
201
|
-
// causes that error when the string starts with /@fs/.
|
|
202
|
-
processed[key] = value;
|
|
203
|
-
|
|
204
|
-
continue;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
if (Array.isArray(value)) {
|
|
208
|
-
processed[key] = await Promise.all(
|
|
209
|
-
value.map(async (item) => {
|
|
210
|
-
if (isImageMetadata(item)) {
|
|
211
|
-
return item;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
if (isRecord(item)) {
|
|
215
|
-
return processImageMetadata(item);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
return item;
|
|
219
|
-
})
|
|
220
|
-
);
|
|
221
|
-
|
|
222
|
-
continue;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if (isRecord(value)) {
|
|
226
|
-
processed[key] = await processImageMetadata(value);
|
|
227
|
-
|
|
228
|
-
continue;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
processed[key] = value;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
return processed;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
function isImageMetadata(value: unknown): value is Record<string, unknown> {
|
|
238
|
-
return (
|
|
239
|
-
isRecord(value) &&
|
|
240
|
-
typeof value.src === 'string' &&
|
|
241
|
-
('width' in value || 'height' in value || 'format' in value)
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
247
|
-
return typeof value === 'object' && value !== null;
|
|
69
|
+
return createAstroRenderHandler({
|
|
70
|
+
container,
|
|
71
|
+
sanitization: options?.sanitization,
|
|
72
|
+
rulesConfigFilePath: options?.rulesConfigFilePath,
|
|
73
|
+
resolveRulesConfigModule: options?.resolveRulesConfigModule,
|
|
74
|
+
loadModule,
|
|
75
|
+
invalidateModuleGraph: options?.invalidateModuleGraph
|
|
76
|
+
});
|
|
248
77
|
}
|
package/src/module-mocks.ts
CHANGED
|
@@ -1,16 +1,164 @@
|
|
|
1
|
-
|
|
1
|
+
const STORYBOOK_ASTRO_GET_MOCK_EXPORT = '__STORYBOOK_ASTRO_GET_STORY_MODULE_MOCK_EXPORT__';
|
|
2
2
|
|
|
3
|
-
export
|
|
3
|
+
export const STORYBOOK_ASTRO_INLINE_MODULE_PREFIX = 'virtual:storybook-astro-inline-module:';
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
export type StoryModuleMockEntry = {
|
|
6
|
+
replacement: string;
|
|
7
|
+
inlineModuleId?: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type StoryModuleMockFactoryResult = Record<string, unknown>;
|
|
11
|
+
|
|
12
|
+
export type StoryModuleMocks = Map<string, StoryModuleMockEntry>;
|
|
13
|
+
|
|
14
|
+
type StoryModuleMocksGlobal = typeof globalThis & {
|
|
15
|
+
__STORYBOOK_ASTRO_STORY_MODULE_MOCK_STATE__?: {
|
|
16
|
+
activeModuleMocksStack: StoryModuleMocks[];
|
|
17
|
+
inlineModuleExports: Map<string, StoryModuleMockFactoryResult>;
|
|
18
|
+
inlineModuleSequence: number;
|
|
19
|
+
};
|
|
20
|
+
__STORYBOOK_ASTRO_GET_STORY_MODULE_MOCK_EXPORT__?: (moduleId: string, exportName: string) => unknown;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const moduleMocksGlobal = globalThis as StoryModuleMocksGlobal;
|
|
24
|
+
|
|
25
|
+
const moduleMockState =
|
|
26
|
+
moduleMocksGlobal.__STORYBOOK_ASTRO_STORY_MODULE_MOCK_STATE__ ??
|
|
27
|
+
(moduleMocksGlobal.__STORYBOOK_ASTRO_STORY_MODULE_MOCK_STATE__ = {
|
|
28
|
+
activeModuleMocksStack: [],
|
|
29
|
+
inlineModuleExports: new Map<string, StoryModuleMockFactoryResult>(),
|
|
30
|
+
inlineModuleSequence: 0
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
if (typeof moduleMocksGlobal[STORYBOOK_ASTRO_GET_MOCK_EXPORT] !== 'function') {
|
|
34
|
+
moduleMocksGlobal[STORYBOOK_ASTRO_GET_MOCK_EXPORT] = getStoryModuleMockExport;
|
|
35
|
+
}
|
|
6
36
|
|
|
7
37
|
export async function withStoryModuleMocks<T>(
|
|
8
38
|
moduleMocks: StoryModuleMocks,
|
|
9
39
|
callback: () => Promise<T>
|
|
10
40
|
): Promise<T> {
|
|
11
|
-
|
|
41
|
+
moduleMockState.activeModuleMocksStack.push(moduleMocks);
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
return await callback();
|
|
45
|
+
} finally {
|
|
46
|
+
moduleMockState.activeModuleMocksStack.pop();
|
|
47
|
+
cleanupInlineModuleMocks(moduleMocks);
|
|
48
|
+
}
|
|
12
49
|
}
|
|
13
50
|
|
|
14
51
|
export function resolveStoryModuleMock(specifier: string): string | undefined {
|
|
15
|
-
return
|
|
52
|
+
return getActiveModuleMocks()?.get(specifier)?.replacement;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function createPathStoryModuleMock(replacement: string): StoryModuleMockEntry {
|
|
56
|
+
return {
|
|
57
|
+
replacement
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function createInlineStoryModuleMock(
|
|
62
|
+
exportsObject: StoryModuleMockFactoryResult
|
|
63
|
+
): StoryModuleMockEntry {
|
|
64
|
+
const inlineModuleId = `storybook-astro-inline-module:${moduleMockState.inlineModuleSequence}`;
|
|
65
|
+
|
|
66
|
+
moduleMockState.inlineModuleSequence += 1;
|
|
67
|
+
moduleMockState.inlineModuleExports.set(inlineModuleId, exportsObject);
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
replacement: `${STORYBOOK_ASTRO_INLINE_MODULE_PREFIX}${inlineModuleId}`,
|
|
71
|
+
inlineModuleId
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function loadStoryInlineModule(resolvedId: string): string | undefined {
|
|
76
|
+
const inlineModuleId = normalizeInlineModuleId(resolvedId);
|
|
77
|
+
|
|
78
|
+
if (!inlineModuleId) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const exportsObject = moduleMockState.inlineModuleExports.get(inlineModuleId);
|
|
83
|
+
|
|
84
|
+
if (!exportsObject) {
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return createInlineModuleSource(inlineModuleId, exportsObject);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function cleanupInlineModuleMocks(moduleMocks: StoryModuleMocks) {
|
|
92
|
+
for (const mockEntry of moduleMocks.values()) {
|
|
93
|
+
if (mockEntry.inlineModuleId) {
|
|
94
|
+
moduleMockState.inlineModuleExports.delete(mockEntry.inlineModuleId);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function getActiveModuleMocks(): StoryModuleMocks | undefined {
|
|
100
|
+
return moduleMockState.activeModuleMocksStack[moduleMockState.activeModuleMocksStack.length - 1];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function getStoryModuleMockExport(moduleId: string, exportName: string): unknown {
|
|
104
|
+
const exportsObject = moduleMockState.inlineModuleExports.get(moduleId);
|
|
105
|
+
|
|
106
|
+
if (!exportsObject) {
|
|
107
|
+
throw new Error(`Inline story module mock is unavailable: ${moduleId}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return exportsObject[exportName];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function createInlineModuleSource(
|
|
114
|
+
inlineModuleId: string,
|
|
115
|
+
exportsObject: StoryModuleMockFactoryResult
|
|
116
|
+
): string {
|
|
117
|
+
const exportNames = Object.keys(exportsObject);
|
|
118
|
+
const sourceLines = [
|
|
119
|
+
`const getStoryModuleMockExport = globalThis.${STORYBOOK_ASTRO_GET_MOCK_EXPORT};`,
|
|
120
|
+
"if (typeof getStoryModuleMockExport !== 'function') {",
|
|
121
|
+
" throw new Error('Inline story module mock helper is unavailable.');",
|
|
122
|
+
'}',
|
|
123
|
+
''
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
if (Object.prototype.hasOwnProperty.call(exportsObject, 'default')) {
|
|
127
|
+
sourceLines.push(
|
|
128
|
+
`export default getStoryModuleMockExport(${JSON.stringify(inlineModuleId)}, 'default');`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
for (const exportName of exportNames) {
|
|
133
|
+
if (exportName === 'default') {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
assertValidExportName(exportName);
|
|
138
|
+
sourceLines.push(
|
|
139
|
+
`export const ${exportName} = getStoryModuleMockExport(${JSON.stringify(inlineModuleId)}, ${JSON.stringify(exportName)});`
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (sourceLines[sourceLines.length - 1] === '') {
|
|
144
|
+
sourceLines.push('export {};');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return sourceLines.join('\n');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function assertValidExportName(exportName: string) {
|
|
151
|
+
if (!/^[$A-Z_a-z][$\w]*$/u.test(exportName)) {
|
|
152
|
+
throw new Error(`Story module mock export name is invalid: ${exportName}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function normalizeInlineModuleId(resolvedId: string): string | undefined {
|
|
157
|
+
const normalizedId = resolvedId.startsWith('\0') ? resolvedId.slice(1) : resolvedId;
|
|
158
|
+
|
|
159
|
+
if (!normalizedId.startsWith(STORYBOOK_ASTRO_INLINE_MODULE_PREFIX)) {
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return normalizedId.slice(STORYBOOK_ASTRO_INLINE_MODULE_PREFIX.length);
|
|
16
164
|
}
|
package/src/preset.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { dirname } from 'node:path';
|
|
1
2
|
import type { StorybookConfigVite, FrameworkOptions } from './types.ts';
|
|
2
3
|
import { vitePluginStorybookAstroMiddleware } from './viteStorybookAstroMiddlewarePlugin.ts';
|
|
3
4
|
import { viteStorybookRendererFallbackPlugin } from './viteStorybookRendererFallbackPlugin.ts';
|
|
@@ -23,15 +24,19 @@ export const core = {
|
|
|
23
24
|
renderer: import.meta.resolve('@storybook-astro/renderer')
|
|
24
25
|
};
|
|
25
26
|
|
|
26
|
-
export const viteFinal: StorybookConfigVite['viteFinal'] = async (config,
|
|
27
|
-
const
|
|
27
|
+
export const viteFinal: StorybookConfigVite['viteFinal'] = async (config, storybookOptions) => {
|
|
28
|
+
const { configType, presets, configDir } = storybookOptions;
|
|
29
|
+
const frameworkOptions = await presets.apply<FrameworkOptions>('frameworkOptions');
|
|
30
|
+
const options = {
|
|
31
|
+
...frameworkOptions,
|
|
32
|
+
resolveFrom: frameworkOptions.resolveFrom ?? dirname(configDir)
|
|
33
|
+
} satisfies FrameworkOptions;
|
|
28
34
|
|
|
29
35
|
if (!config.plugins) {
|
|
30
36
|
config.plugins = [];
|
|
31
37
|
}
|
|
32
38
|
|
|
33
39
|
const integrations = options.integrations ?? [];
|
|
34
|
-
const resolveFrom = options.resolveFrom ?? process.cwd();
|
|
35
40
|
const renderMode = options.renderMode ?? 'server';
|
|
36
41
|
const mode = configType === 'DEVELOPMENT' ? 'development' : 'production';
|
|
37
42
|
const command = configType === 'DEVELOPMENT' ? 'serve' : 'build';
|
|
@@ -54,7 +59,7 @@ export const viteFinal: StorybookConfigVite['viteFinal'] = async (config, { conf
|
|
|
54
59
|
vitePluginAstroComponentMarker() as any,
|
|
55
60
|
vitePluginAstroIntegrationOptsFallback(),
|
|
56
61
|
vitePluginAstroToolbarFallback(),
|
|
57
|
-
vitePluginAstroVueFallback()
|
|
62
|
+
vitePluginAstroVueFallback()
|
|
58
63
|
);
|
|
59
64
|
|
|
60
65
|
if (configType === 'DEVELOPMENT') {
|
|
@@ -97,7 +102,13 @@ export const viteFinal: StorybookConfigVite['viteFinal'] = async (config, { conf
|
|
|
97
102
|
aliases['react-dom'] = 'react-dom';
|
|
98
103
|
}
|
|
99
104
|
|
|
100
|
-
const finalConfig = await mergeWithAstroConfig(
|
|
105
|
+
const finalConfig = await mergeWithAstroConfig(
|
|
106
|
+
config,
|
|
107
|
+
integrations,
|
|
108
|
+
options.resolveFrom,
|
|
109
|
+
mode,
|
|
110
|
+
command
|
|
111
|
+
);
|
|
101
112
|
|
|
102
113
|
// Exclude Astro integration packages from dependency optimization because
|
|
103
114
|
// they import virtual modules that esbuild cannot resolve.
|
|
@@ -107,7 +118,17 @@ export const viteFinal: StorybookConfigVite['viteFinal'] = async (config, { conf
|
|
|
107
118
|
if (!finalConfig.optimizeDeps.exclude) {
|
|
108
119
|
finalConfig.optimizeDeps.exclude = [];
|
|
109
120
|
}
|
|
110
|
-
for (const pkg of [
|
|
121
|
+
for (const pkg of [
|
|
122
|
+
'@astrojs/vue',
|
|
123
|
+
'@astrojs/vue/client.js',
|
|
124
|
+
'@astrojs/vue/server.js',
|
|
125
|
+
'@astrojs/react',
|
|
126
|
+
'@astrojs/react/client.js',
|
|
127
|
+
'@astrojs/react/server.js',
|
|
128
|
+
'@astrojs/preact',
|
|
129
|
+
'@astrojs/preact/client.js',
|
|
130
|
+
'@astrojs/preact/server.js'
|
|
131
|
+
]) {
|
|
111
132
|
if (!finalConfig.optimizeDeps.exclude.includes(pkg)) {
|
|
112
133
|
finalConfig.optimizeDeps.exclude.push(pkg);
|
|
113
134
|
}
|
|
@@ -121,6 +142,16 @@ export const viteFinal: StorybookConfigVite['viteFinal'] = async (config, { conf
|
|
|
121
142
|
if (!finalConfig.optimizeDeps.exclude.includes('@storybook-astro/renderer')) {
|
|
122
143
|
finalConfig.optimizeDeps.exclude.push('@storybook-astro/renderer');
|
|
123
144
|
}
|
|
145
|
+
// fsevents is a macOS-only native chokidar dep with a .node binary that
|
|
146
|
+
// esbuild's prebundler can't load. storybook/internal/preview-api can pass
|
|
147
|
+
// through the transform pipeline twice when used by CSF Next portable
|
|
148
|
+
// stories, producing a duplicate __vite__injectQuery import in the
|
|
149
|
+
// generated chunk; excluding it from prebundling collapses the duplicate.
|
|
150
|
+
for (const pkg of ['fsevents', 'storybook/internal/preview-api']) {
|
|
151
|
+
if (!finalConfig.optimizeDeps.exclude.includes(pkg)) {
|
|
152
|
+
finalConfig.optimizeDeps.exclude.push(pkg);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
124
155
|
// Mark integration virtual modules as external so the dep bundler doesn't
|
|
125
156
|
// try to resolve them (they are Vite virtual modules with no real package).
|
|
126
157
|
// Set both esbuildOptions (Vite ≤7) and rolldownOptions (Vite 8+, Rolldown)
|
|
@@ -146,8 +177,7 @@ export const viteFinal: StorybookConfigVite['viteFinal'] = async (config, { conf
|
|
|
146
177
|
}
|
|
147
178
|
}
|
|
148
179
|
|
|
149
|
-
// Vite 8+
|
|
150
|
-
// Use a loose cast because rolldownOptions is absent from Vite <8 types.
|
|
180
|
+
// Vite 8+ uses Rolldown for dependency optimization.
|
|
151
181
|
const optimizeDepsMut = finalConfig.optimizeDeps as Record<string, unknown>;
|
|
152
182
|
const rolldownOpts = (optimizeDepsMut.rolldownOptions ?? {}) as { external?: string[] };
|
|
153
183
|
|