@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
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { createServer, mergeConfig, type Plugin, type ViteDevServer } from 'vite';
|
|
3
|
+
import type { experimental_AstroContainer as AstroContainer } from 'astro/container';
|
|
4
|
+
import { ensureAstroPassthroughImageService } from './astroImageService.ts';
|
|
5
|
+
import { importAstroConfig } from './importAstroConfig.ts';
|
|
6
|
+
import type { Integration } from './integrations/index.ts';
|
|
7
|
+
import { ssrLoadModuleWithFsFallback } from './lib/ssr-load-module-with-fs-fallback.ts';
|
|
8
|
+
import { resolveStoryModuleMock } from './module-mocks.ts';
|
|
9
|
+
import { vitePluginAstroFontsFallback } from './vitePluginAstroFontsFallback.ts';
|
|
10
|
+
import { vitePluginAstroIntegrationOptsFallback } from './vitePluginAstroIntegrationOptsFallback.ts';
|
|
11
|
+
import { vitePluginAstroRoutesFallback } from './vitePluginAstroRoutesFallback.ts';
|
|
12
|
+
import { vitePluginAstroVueFallback } from './vitePluginAstroVueFallback.ts';
|
|
13
|
+
import { vitePluginStoryModuleMocks } from './vitePluginStoryModuleMocks.ts';
|
|
14
|
+
|
|
15
|
+
export async function createStorySsrViteServer(options: {
|
|
16
|
+
integrations: Integration[];
|
|
17
|
+
trackedSpecifiers: Set<string>;
|
|
18
|
+
resolveFrom: string;
|
|
19
|
+
}) {
|
|
20
|
+
const { getViteConfig, passthroughImageService } = await importAstroConfig(options.resolveFrom);
|
|
21
|
+
const astroConfig = await getViteConfig(
|
|
22
|
+
{ root: options.resolveFrom },
|
|
23
|
+
{
|
|
24
|
+
configFile: false,
|
|
25
|
+
integrations: await Promise.all(
|
|
26
|
+
options.integrations.map((integration) => integration.loadIntegration(options.resolveFrom))
|
|
27
|
+
),
|
|
28
|
+
image: { service: passthroughImageService() }
|
|
29
|
+
}
|
|
30
|
+
)({
|
|
31
|
+
mode: 'production',
|
|
32
|
+
command: 'serve'
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const config = mergeConfig(astroConfig, {
|
|
36
|
+
appType: 'custom',
|
|
37
|
+
server: {
|
|
38
|
+
middlewareMode: true
|
|
39
|
+
},
|
|
40
|
+
ssr: {
|
|
41
|
+
// Keep Astro runtime classes in the Vite SSR graph so containers and
|
|
42
|
+
// components share SlotString/HTMLString instances during prerendering.
|
|
43
|
+
noExternal: /^astro(\/.+)?$/
|
|
44
|
+
},
|
|
45
|
+
plugins: [
|
|
46
|
+
createProjectAstroResolutionPlugin(options.resolveFrom),
|
|
47
|
+
vitePluginAstroFontsFallback(),
|
|
48
|
+
vitePluginAstroIntegrationOptsFallback(),
|
|
49
|
+
vitePluginAstroVueFallback(),
|
|
50
|
+
vitePluginAstroRoutesFallback(),
|
|
51
|
+
vitePluginStoryModuleMocks(),
|
|
52
|
+
createTrackedSpecifierStubPlugin(options.trackedSpecifiers)
|
|
53
|
+
]
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const viteServer = await createServer(config);
|
|
57
|
+
|
|
58
|
+
await viteServer.pluginContainer.buildStart({});
|
|
59
|
+
|
|
60
|
+
return viteServer;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function loadRulesConfigModule(viteServer: ViteDevServer, configFilePath?: string) {
|
|
64
|
+
if (!configFilePath) {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
return await ssrLoadModuleWithFsFallback(viteServer, configFilePath, {
|
|
70
|
+
fixStacktrace: true
|
|
71
|
+
});
|
|
72
|
+
} catch (error) {
|
|
73
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
74
|
+
|
|
75
|
+
throw new Error(
|
|
76
|
+
`Unable to load framework.options.storyRules config module at ${configFilePath}: ${reason}`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function createClientModuleResolver(
|
|
82
|
+
integrations: Integration[],
|
|
83
|
+
staticModuleMap: Record<string, string>
|
|
84
|
+
) {
|
|
85
|
+
return function resolveClientModule(specifier: string) {
|
|
86
|
+
if (Object.hasOwn(staticModuleMap, specifier)) {
|
|
87
|
+
return staticModuleMap[specifier];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const normalizedSpecifier = specifier.replace(/\\/g, '/').replace(/\?.*$/, '');
|
|
91
|
+
|
|
92
|
+
if (Object.hasOwn(staticModuleMap, normalizedSpecifier)) {
|
|
93
|
+
return staticModuleMap[normalizedSpecifier];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
for (const integration of integrations) {
|
|
97
|
+
const resolution = integration.resolveClient(specifier);
|
|
98
|
+
|
|
99
|
+
if (resolution) {
|
|
100
|
+
return resolution;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export async function createProductionAstroContainer(options: {
|
|
107
|
+
integrations: Integration[];
|
|
108
|
+
resolveClientModule: (specifier: string) => string | undefined;
|
|
109
|
+
viteServer: ViteDevServer;
|
|
110
|
+
}) {
|
|
111
|
+
ensureAstroPassthroughImageService();
|
|
112
|
+
|
|
113
|
+
// Astro 6's container wraps each slot value in a SlotString, and the
|
|
114
|
+
// rendering pipeline detects raw-HTML slot chunks via `instanceof SlotString`.
|
|
115
|
+
// If the container is loaded by Node's ESM resolver while components are
|
|
116
|
+
// loaded through Vite's SSR graph, the two paths produce different
|
|
117
|
+
// SlotString classes and the instanceof check fails — slot content then
|
|
118
|
+
// takes the escaping code path. Loading the container via the same Vite
|
|
119
|
+
// SSR server keeps the class identity consistent.
|
|
120
|
+
const containerModule = (await options.viteServer.ssrLoadModule('astro/container')) as {
|
|
121
|
+
experimental_AstroContainer: typeof AstroContainer;
|
|
122
|
+
};
|
|
123
|
+
const ViteAstroContainer = containerModule.experimental_AstroContainer;
|
|
124
|
+
|
|
125
|
+
const container = await ViteAstroContainer.create({
|
|
126
|
+
resolve: async (specifier) => {
|
|
127
|
+
const mockedModule = resolveStoryModuleMock(specifier);
|
|
128
|
+
|
|
129
|
+
if (mockedModule) {
|
|
130
|
+
return mockedModule;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const resolution = options.resolveClientModule(specifier);
|
|
134
|
+
|
|
135
|
+
if (resolution) {
|
|
136
|
+
return resolution;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return specifier;
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
await addContainerRenderers(
|
|
144
|
+
container,
|
|
145
|
+
options.integrations,
|
|
146
|
+
options.resolveClientModule,
|
|
147
|
+
options.viteServer
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
return container;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export async function addContainerRenderers(
|
|
154
|
+
container: Awaited<ReturnType<typeof AstroContainer.create>>,
|
|
155
|
+
integrations: Integration[],
|
|
156
|
+
resolveClientModule: (specifier: string) => string | undefined,
|
|
157
|
+
viteServer: ViteDevServer
|
|
158
|
+
) {
|
|
159
|
+
for (const integration of integrations) {
|
|
160
|
+
const serverRenderer = integration.renderer.server;
|
|
161
|
+
|
|
162
|
+
if (serverRenderer) {
|
|
163
|
+
const serverRendererModule = await viteServer.ssrLoadModule(serverRenderer.entrypoint);
|
|
164
|
+
const renderer = serverRendererModule.default ?? serverRendererModule;
|
|
165
|
+
|
|
166
|
+
if (integration.name === 'solid' && isRecord(renderer)) {
|
|
167
|
+
container.addServerRenderer({
|
|
168
|
+
name: serverRenderer.name,
|
|
169
|
+
renderer: {
|
|
170
|
+
...renderer,
|
|
171
|
+
name: serverRenderer.name
|
|
172
|
+
} as never
|
|
173
|
+
});
|
|
174
|
+
} else {
|
|
175
|
+
container.addServerRenderer({
|
|
176
|
+
name: serverRenderer.name,
|
|
177
|
+
renderer
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const clientRenderer = integration.renderer.client;
|
|
183
|
+
|
|
184
|
+
if (clientRenderer) {
|
|
185
|
+
const resolvedEntrypoint =
|
|
186
|
+
resolveClientModule(clientRenderer.entrypoint) ?? clientRenderer.entrypoint;
|
|
187
|
+
|
|
188
|
+
container.addClientRenderer({
|
|
189
|
+
name: clientRenderer.name,
|
|
190
|
+
entrypoint: resolvedEntrypoint
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function createProjectAstroResolutionPlugin(resolveFrom: string): Plugin {
|
|
197
|
+
const require = createRequire(import.meta.url);
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
name: 'storybook-astro:resolve-project-astro-shared',
|
|
201
|
+
enforce: 'pre',
|
|
202
|
+
resolveId(id: string) {
|
|
203
|
+
if (id !== 'astro' && !id.startsWith('astro/')) {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
return require.resolve(id, {
|
|
209
|
+
paths: [resolveFrom]
|
|
210
|
+
});
|
|
211
|
+
} catch {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
} satisfies Plugin;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function createTrackedSpecifierStubPlugin(trackedSpecifiers: Set<string>): Plugin {
|
|
219
|
+
return {
|
|
220
|
+
name: 'storybook-astro:shared-ssr-stubs',
|
|
221
|
+
resolveId(id: string) {
|
|
222
|
+
if (trackedSpecifiers.has(id)) {
|
|
223
|
+
return `\0storybook-astro-shared-ssr-stub:${encodeURIComponent(id)}`;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return null;
|
|
227
|
+
},
|
|
228
|
+
load(id: string) {
|
|
229
|
+
if (id.startsWith('\0storybook-astro-shared-ssr-stub:')) {
|
|
230
|
+
return 'export default undefined;';
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
} satisfies Plugin;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
239
|
+
return typeof value === 'object' && value !== null;
|
|
240
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -37,15 +37,6 @@ type StaticFrameworkOptions = BaseFrameworkOptions & {
|
|
|
37
37
|
renderMode: 'static';
|
|
38
38
|
storyRules?: StoryRulesOptions;
|
|
39
39
|
server?: never;
|
|
40
|
-
/**
|
|
41
|
-
* Additional source directories (relative to `resolveFrom`) to scan for
|
|
42
|
-
* hydratable client components (JSX/TSX/Vue/Svelte). Use this when stories
|
|
43
|
-
* reference components that live outside the default `src/components` scan
|
|
44
|
-
* root — for example, workspace packages included in the `stories` globs.
|
|
45
|
-
*
|
|
46
|
-
* @example ['../../packages/components/src']
|
|
47
|
-
*/
|
|
48
|
-
componentRoots?: string[];
|
|
49
40
|
};
|
|
50
41
|
|
|
51
42
|
export type FrameworkOptions = ServerFrameworkOptions | StaticFrameworkOptions;
|
package/src/virtual.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ declare module 'virtual:storybook-astro-renderer' {
|
|
|
22
22
|
export function applyStyles(): void;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
declare module 'virtual:storybook-astro-
|
|
25
|
+
declare module 'virtual:storybook-astro/sanitize-config' {
|
|
26
26
|
import type { SanitizationOptions } from './lib/sanitization.ts';
|
|
27
27
|
|
|
28
28
|
const sanitization: SanitizationOptions | undefined;
|
|
@@ -30,16 +30,30 @@ declare module 'virtual:storybook-astro-sanitization-config' {
|
|
|
30
30
|
export default sanitization;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
declare module 'virtual:storybook-astro
|
|
33
|
+
declare module 'virtual:storybook-astro/story-rules' {
|
|
34
34
|
const configModule: unknown;
|
|
35
35
|
|
|
36
36
|
export default configModule;
|
|
37
37
|
export const storybookAstroStoryRulesConfigFilePath: string | undefined;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
declare module 'virtual:storybook-astro
|
|
40
|
+
declare module 'virtual:storybook-astro/server-auth' {
|
|
41
41
|
export const storybookAstroServerAuthToken: string | undefined;
|
|
42
42
|
export const storybookAstroServerAuthHeader: string;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
declare module 'virtual:storybook-astro/server-runtime' {
|
|
46
|
+
import type { Integration } from './integrations/index.ts';
|
|
47
|
+
|
|
48
|
+
export const runtimeConfig: {
|
|
49
|
+
snapshotDirName: string;
|
|
50
|
+
storyRulesConfigRelativePath: string | undefined;
|
|
51
|
+
componentPathMap: Record<string, string>;
|
|
52
|
+
staticModuleMap: Record<string, string>;
|
|
53
|
+
staticCssMap: Record<string, string[]>;
|
|
54
|
+
trackedSpecifiers: string[];
|
|
55
|
+
};
|
|
56
|
+
export const integrations: Integration[];
|
|
57
|
+
}
|
|
58
|
+
|
|
45
59
|
declare module 'virtual:storybook-renderer-fallback' {}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Plugin } from 'vite';
|
|
2
|
-
import {
|
|
2
|
+
import { createVirtualModule } from './virtualModulePlugin.ts';
|
|
3
3
|
|
|
4
4
|
type ImportRecord = {
|
|
5
5
|
id: string;
|
|
@@ -7,9 +7,9 @@ type ImportRecord = {
|
|
|
7
7
|
importStatement: string;
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
-
export function
|
|
11
|
-
return
|
|
12
|
-
pluginName: 'storybook-astro:
|
|
10
|
+
export function astroFilesPlugin(astroComponents: string[]): Plugin {
|
|
11
|
+
return createVirtualModule({
|
|
12
|
+
pluginName: 'storybook-astro:astro-files',
|
|
13
13
|
virtualModuleId: 'virtual:astro-files',
|
|
14
14
|
load() {
|
|
15
15
|
const imports = astroComponents.reduce<ImportRecord[]>((records, file, index) => {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
import type { SanitizationOptions } from '../lib/sanitization.ts';
|
|
3
|
+
import { serializeSanitizationOptions } from '../lib/sanitization.ts';
|
|
4
|
+
import { createVirtualModule } from './virtualModulePlugin.ts';
|
|
5
|
+
|
|
6
|
+
export const SANITIZE_CONFIG_MODULE_ID = 'virtual:storybook-astro/sanitize-config';
|
|
7
|
+
|
|
8
|
+
export function sanitizeConfigPlugin(options?: SanitizationOptions): Plugin {
|
|
9
|
+
return createVirtualModule({
|
|
10
|
+
pluginName: 'storybook-astro:sanitize-config',
|
|
11
|
+
virtualModuleId: SANITIZE_CONFIG_MODULE_ID,
|
|
12
|
+
load() {
|
|
13
|
+
const serializedConfig = serializeSanitizationOptions(options);
|
|
14
|
+
|
|
15
|
+
return `export default ${serializedConfig};`;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import type { PluginOption } from 'vite';
|
|
2
2
|
import { describe, expect, test } from 'vitest';
|
|
3
|
-
import {
|
|
4
|
-
STORYBOOK_ASTRO_SERVER_AUTH_CONFIG_VIRTUAL_MODULE_ID,
|
|
5
|
-
storybookAstroServerAuthConfigVirtualModulePlugin
|
|
6
|
-
} from './storybookAstroServerAuthConfigVirtualModulePlugin.ts';
|
|
3
|
+
import { SERVER_AUTH_MODULE_ID, serverAuthPlugin } from './serverAuthPlugin.ts';
|
|
7
4
|
|
|
8
5
|
function getPlugin(pluginOption: PluginOption) {
|
|
9
6
|
if (Array.isArray(pluginOption)) {
|
|
@@ -34,19 +31,19 @@ function getHookHandler<T extends (...args: unknown[]) => unknown>(hook: unknown
|
|
|
34
31
|
throw new Error('Expected hook to be a function or an object with a handler function.');
|
|
35
32
|
}
|
|
36
33
|
|
|
37
|
-
describe('
|
|
34
|
+
describe('serverAuthPlugin', () => {
|
|
38
35
|
test('normalizes auth token and header values', async () => {
|
|
39
36
|
const plugin = getPlugin(
|
|
40
|
-
|
|
37
|
+
serverAuthPlugin({
|
|
41
38
|
authToken: ' test-token ',
|
|
42
39
|
authHeader: ' X-Storybook-Token '
|
|
43
40
|
})
|
|
44
41
|
);
|
|
45
42
|
const resolveId = getHookHandler<(id: string) => string | undefined>(plugin.resolveId);
|
|
46
43
|
const load = getHookHandler<(id: string) => Promise<string | undefined>>(plugin.load);
|
|
47
|
-
const resolvedId = resolveId(
|
|
44
|
+
const resolvedId = resolveId(SERVER_AUTH_MODULE_ID);
|
|
48
45
|
|
|
49
|
-
expect(resolvedId).toBe(`\0${
|
|
46
|
+
expect(resolvedId).toBe(`\0${SERVER_AUTH_MODULE_ID}`);
|
|
50
47
|
await expect(load(resolvedId!)).resolves.toBe(
|
|
51
48
|
'export const storybookAstroServerAuthToken = "test-token";\n' +
|
|
52
49
|
'export const storybookAstroServerAuthHeader = "x-storybook-token";'
|
|
@@ -55,14 +52,14 @@ describe('storybookAstroServerAuthConfigVirtualModulePlugin', () => {
|
|
|
55
52
|
|
|
56
53
|
test('falls back to authorization header and undefined token', async () => {
|
|
57
54
|
const plugin = getPlugin(
|
|
58
|
-
|
|
55
|
+
serverAuthPlugin({
|
|
59
56
|
authToken: ' '
|
|
60
57
|
})
|
|
61
58
|
);
|
|
62
59
|
const load = getHookHandler<(id: string) => Promise<string | undefined>>(plugin.load);
|
|
63
60
|
|
|
64
61
|
await expect(
|
|
65
|
-
load(`\0${
|
|
62
|
+
load(`\0${SERVER_AUTH_MODULE_ID}`)
|
|
66
63
|
).resolves.toBe(
|
|
67
64
|
'export const storybookAstroServerAuthToken = undefined;\n' +
|
|
68
65
|
'export const storybookAstroServerAuthHeader = "authorization";'
|
package/src/vite/{storybookAstroServerAuthConfigVirtualModulePlugin.ts → serverAuthPlugin.ts}
RENAMED
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
import type { Plugin } from 'vite';
|
|
2
2
|
import type { ServerBuildOptions } from '../types.ts';
|
|
3
|
-
import {
|
|
3
|
+
import { createVirtualModule } from './virtualModulePlugin.ts';
|
|
4
4
|
|
|
5
|
-
export const
|
|
6
|
-
'virtual:storybook-astro-server-auth-config';
|
|
5
|
+
export const SERVER_AUTH_MODULE_ID = 'virtual:storybook-astro/server-auth';
|
|
7
6
|
|
|
8
|
-
export function
|
|
9
|
-
options?: ServerBuildOptions
|
|
10
|
-
): Plugin {
|
|
7
|
+
export function serverAuthPlugin(options?: ServerBuildOptions): Plugin {
|
|
11
8
|
const authToken = normalizeOptionalString(options?.authToken);
|
|
12
9
|
const authHeader = normalizeAuthHeader(options?.authHeader);
|
|
13
10
|
|
|
14
|
-
return
|
|
15
|
-
pluginName: 'storybook-astro:
|
|
16
|
-
virtualModuleId:
|
|
11
|
+
return createVirtualModule({
|
|
12
|
+
pluginName: 'storybook-astro:server-auth',
|
|
13
|
+
virtualModuleId: SERVER_AUTH_MODULE_ID,
|
|
17
14
|
load() {
|
|
18
15
|
return [
|
|
19
16
|
`export const storybookAstroServerAuthToken = ${
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { relative } from 'node:path';
|
|
2
|
+
import type { Integration } from '../integrations/index.ts';
|
|
3
|
+
import { resolveRulesConfigFilePath } from '../rules-options.ts';
|
|
4
|
+
import type { FrameworkOptions } from '../types.ts';
|
|
5
|
+
import { createVirtualModule } from './virtualModulePlugin.ts';
|
|
6
|
+
|
|
7
|
+
export const SERVER_RUNTIME_MODULE_ID = 'virtual:storybook-astro/server-runtime';
|
|
8
|
+
const integrationsModuleId = '@storybook-astro/framework/integrations';
|
|
9
|
+
|
|
10
|
+
function getIntegrationFactoryName(integration: Integration): string {
|
|
11
|
+
return integration.factoryName ?? integration.name;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Produces the virtual module that hands the standalone render server its build-time config. */
|
|
15
|
+
export function serverRuntimePlugin(options: {
|
|
16
|
+
integrations?: FrameworkOptions['integrations'];
|
|
17
|
+
storyRules?: FrameworkOptions['storyRules'];
|
|
18
|
+
resolveFrom: string;
|
|
19
|
+
snapshotDirName: string;
|
|
20
|
+
componentPathMap: Record<string, string>;
|
|
21
|
+
staticModuleMap: Record<string, string>;
|
|
22
|
+
staticCssMap: Record<string, string[]>;
|
|
23
|
+
trackedSpecifiers: string[];
|
|
24
|
+
}) {
|
|
25
|
+
return createVirtualModule({
|
|
26
|
+
pluginName: 'storybook-astro:server-runtime',
|
|
27
|
+
virtualModuleId: SERVER_RUNTIME_MODULE_ID,
|
|
28
|
+
load() {
|
|
29
|
+
const storyRulesConfigFilePath = resolveRulesConfigFilePath(options.storyRules, options.resolveFrom);
|
|
30
|
+
const storyRulesConfigRelativePath = storyRulesConfigFilePath
|
|
31
|
+
? relative(options.resolveFrom, storyRulesConfigFilePath).replace(/\\/g, '/')
|
|
32
|
+
: undefined;
|
|
33
|
+
const integrations = options.integrations ?? [];
|
|
34
|
+
// Keep the generated module small: one config object for plain data,
|
|
35
|
+
// one integrations export for real factory calls.
|
|
36
|
+
const runtimeConfig = {
|
|
37
|
+
snapshotDirName: options.snapshotDirName,
|
|
38
|
+
storyRulesConfigRelativePath,
|
|
39
|
+
componentPathMap: options.componentPathMap,
|
|
40
|
+
staticModuleMap: options.staticModuleMap,
|
|
41
|
+
staticCssMap: options.staticCssMap,
|
|
42
|
+
trackedSpecifiers: options.trackedSpecifiers
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return [
|
|
46
|
+
createIntegrationImports(integrations),
|
|
47
|
+
`export const runtimeConfig = ${serializeValue(runtimeConfig)};`,
|
|
48
|
+
`export const integrations = [${createIntegrationFactoryCalls(integrations)}];`
|
|
49
|
+
].join('\n');
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Imports only the integration factories used by this runtime bundle. */
|
|
55
|
+
function createIntegrationImports(integrations: Integration[]) {
|
|
56
|
+
const factoryNames = Array.from(new Set(integrations.map(getIntegrationFactoryName)));
|
|
57
|
+
|
|
58
|
+
if (factoryNames.length === 0) {
|
|
59
|
+
return '';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return `import { ${factoryNames.join(', ')} } from ${JSON.stringify(integrationsModuleId)};`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Recreates the configured integration list inside the generated runtime module. */
|
|
66
|
+
function createIntegrationFactoryCalls(integrations: Integration[]) {
|
|
67
|
+
return integrations
|
|
68
|
+
.map((integration) => `${getIntegrationFactoryName(integration)}(${serializeValue(integration.options)})`)
|
|
69
|
+
.join(', ');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Serializes plain runtime config values into executable module source. */
|
|
73
|
+
function serializeValue(value: unknown): string {
|
|
74
|
+
if (value === undefined) {
|
|
75
|
+
return 'undefined';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (value === null) {
|
|
79
|
+
return 'null';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (typeof value === 'string') {
|
|
83
|
+
return JSON.stringify(value);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
87
|
+
return JSON.stringify(value);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (value instanceof RegExp) {
|
|
91
|
+
return value.toString();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (Array.isArray(value)) {
|
|
95
|
+
return `[${value.map((entry) => serializeValue(entry)).join(', ')}]`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (isRecord(value)) {
|
|
99
|
+
return `{${Object.entries(value)
|
|
100
|
+
.map(([key, entryValue]) => `${JSON.stringify(key)}: ${serializeValue(entryValue)}`)
|
|
101
|
+
.join(', ')}}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
throw new Error('Unable to serialize Storybook Astro server runtime configuration.');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
108
|
+
return typeof value === 'object' && value !== null;
|
|
109
|
+
}
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
import type { Plugin } from 'vite';
|
|
2
2
|
import type { StoryRulesOptions } from '../rules-options.ts';
|
|
3
3
|
import { resolveRulesConfigFilePath } from '../rules-options.ts';
|
|
4
|
-
import {
|
|
4
|
+
import { createVirtualModule } from './virtualModulePlugin.ts';
|
|
5
5
|
|
|
6
|
-
export const
|
|
7
|
-
'virtual:storybook-astro-story-rules-config';
|
|
6
|
+
export const STORY_RULES_MODULE_ID = 'virtual:storybook-astro/story-rules';
|
|
8
7
|
|
|
9
|
-
export function
|
|
8
|
+
export function storyRulesPlugin(
|
|
10
9
|
options?: StoryRulesOptions,
|
|
11
10
|
resolveFrom = process.cwd()
|
|
12
11
|
): Plugin {
|
|
13
|
-
return
|
|
14
|
-
pluginName: 'storybook-astro:
|
|
15
|
-
virtualModuleId:
|
|
12
|
+
return createVirtualModule({
|
|
13
|
+
pluginName: 'storybook-astro:story-rules',
|
|
14
|
+
virtualModuleId: STORY_RULES_MODULE_ID,
|
|
16
15
|
load() {
|
|
17
16
|
const configFilePath = resolveRulesConfigFilePath(options, resolveFrom);
|
|
18
17
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PluginOption } from 'vite';
|
|
2
2
|
import { describe, expect, test, vi } from 'vitest';
|
|
3
|
-
import {
|
|
3
|
+
import { createVirtualModule } from './virtualModulePlugin.ts';
|
|
4
4
|
|
|
5
5
|
function getPlugin(pluginOption: PluginOption) {
|
|
6
6
|
if (Array.isArray(pluginOption)) {
|
|
@@ -31,9 +31,9 @@ function getHookHandler<T extends (...args: unknown[]) => unknown>(hook: unknown
|
|
|
31
31
|
throw new Error('Expected hook to be a function or an object with a handler function.');
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
describe('
|
|
34
|
+
describe('createVirtualModule', () => {
|
|
35
35
|
test('resolves configured virtual module id with a null-byte prefix', () => {
|
|
36
|
-
const pluginOption =
|
|
36
|
+
const pluginOption = createVirtualModule({
|
|
37
37
|
pluginName: 'test:virtual-module',
|
|
38
38
|
virtualModuleId: 'virtual:test-module',
|
|
39
39
|
load: () => 'export default true;'
|
|
@@ -48,7 +48,7 @@ describe('createVirtualModulePlugin', () => {
|
|
|
48
48
|
|
|
49
49
|
test('loads module content only for the resolved virtual module id', async () => {
|
|
50
50
|
const load = vi.fn(() => 'export const message = "hello";');
|
|
51
|
-
const pluginOption =
|
|
51
|
+
const pluginOption = createVirtualModule({
|
|
52
52
|
pluginName: 'test:virtual-module',
|
|
53
53
|
virtualModuleId: 'virtual:test-module',
|
|
54
54
|
load
|
|
@@ -67,7 +67,7 @@ describe('createVirtualModulePlugin', () => {
|
|
|
67
67
|
});
|
|
68
68
|
|
|
69
69
|
test('supports asynchronous virtual module loaders', async () => {
|
|
70
|
-
const pluginOption =
|
|
70
|
+
const pluginOption = createVirtualModule({
|
|
71
71
|
pluginName: 'test:virtual-module',
|
|
72
72
|
virtualModuleId: 'virtual:test-module',
|
|
73
73
|
load: async () => 'export default "async";'
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { Plugin } from 'vite';
|
|
2
2
|
|
|
3
|
-
type
|
|
3
|
+
type CreateVirtualModuleOptions = {
|
|
4
4
|
pluginName: string;
|
|
5
5
|
virtualModuleId: string;
|
|
6
6
|
load: (id: string) => string | Promise<string> | undefined;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
-
export function
|
|
9
|
+
export function createVirtualModule(options: CreateVirtualModuleOptions): Plugin {
|
|
10
10
|
const resolvedVirtualModuleId = `\0${options.virtualModuleId}`;
|
|
11
11
|
|
|
12
12
|
return {
|