@storybook-astro/framework 1.6.0 → 1.7.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/{chunk-RERWLIIN.js → chunk-5POAXNWB.js} +96 -107
- package/dist/chunk-5POAXNWB.js.map +1 -0
- package/dist/chunk-NOQVUQ7R.js +107 -0
- package/dist/chunk-NOQVUQ7R.js.map +1 -0
- package/dist/{chunk-6RIGYMZP.js → chunk-UIGE5653.js} +1 -1
- package/dist/chunk-UIGE5653.js.map +1 -0
- package/dist/chunk-YRG32BBU.js +15 -0
- package/dist/chunk-YRG32BBU.js.map +1 -0
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/middleware.js +4 -2
- package/dist/middleware.js.map +1 -1
- package/dist/preset.js +78 -42
- package/dist/preset.js.map +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +39 -15
- package/dist/testing.js.map +1 -1
- package/dist/vitest/index.js +1 -1
- package/package.json +17 -13
- package/src/astroRenderHandler.ts +39 -2
- package/src/index.ts +8 -1
- package/src/lib/astro-component-marker.test.ts +48 -0
- package/src/lib/reconstruct-component-args.test.ts +97 -0
- package/src/lib/reconstruct-component-args.ts +155 -0
- package/src/lib/separate-story-slots.ts +20 -0
- package/src/preset.ts +60 -12
- package/src/productionRenderRuntime.test.ts +97 -0
- package/src/productionRenderRuntime.ts +46 -29
- package/src/storySsrVite.ts +23 -6
- package/src/testing/astro-runtime.ts +52 -21
- package/src/vitePluginAstroComponentMarker.ts +6 -1
- package/dist/chunk-6RIGYMZP.js.map +0 -1
- package/dist/chunk-RERWLIIN.js.map +0 -1
package/dist/testing.js
CHANGED
|
@@ -10,11 +10,21 @@ import {
|
|
|
10
10
|
composeStory,
|
|
11
11
|
setProjectAnnotations
|
|
12
12
|
} from "./chunk-KXAAX3GN.js";
|
|
13
|
+
import {
|
|
14
|
+
separateStorySlots
|
|
15
|
+
} from "./chunk-YRG32BBU.js";
|
|
13
16
|
import {
|
|
14
17
|
ssrLoadModuleWithFsFallback
|
|
15
18
|
} from "./chunk-BF3USGCF.js";
|
|
16
19
|
import "./chunk-PUTCAN6X.js";
|
|
20
|
+
import {
|
|
21
|
+
markRawSlots,
|
|
22
|
+
patchCreateAstroCompat,
|
|
23
|
+
reconstructProps,
|
|
24
|
+
reconstructSlots
|
|
25
|
+
} from "./chunk-5POAXNWB.js";
|
|
17
26
|
import "./chunk-7YBE4TTI.js";
|
|
27
|
+
import "./chunk-ZIDMHD4S.js";
|
|
18
28
|
import "./chunk-B5HHF6FC.js";
|
|
19
29
|
import "./chunk-G3PMV62Z.js";
|
|
20
30
|
|
|
@@ -194,6 +204,7 @@ async function resolveTestingProjectRoot(component) {
|
|
|
194
204
|
}
|
|
195
205
|
|
|
196
206
|
// src/testing/astro-runtime.ts
|
|
207
|
+
import { serializeAstroComponentMarkers } from "@storybook-astro/renderer/types";
|
|
197
208
|
var astroContainerPromise = null;
|
|
198
209
|
var astroSsrViteServerPromises = /* @__PURE__ */ new Map();
|
|
199
210
|
var astroSsrHandlerPromises = /* @__PURE__ */ new Map();
|
|
@@ -274,20 +285,27 @@ async function resolveAstroComponent(component, resolveFrom) {
|
|
|
274
285
|
}
|
|
275
286
|
return resolvedComponent;
|
|
276
287
|
}
|
|
288
|
+
function setRenderedHtml(html) {
|
|
289
|
+
if (typeof document !== "undefined") {
|
|
290
|
+
document.body.innerHTML = html;
|
|
291
|
+
}
|
|
292
|
+
return html;
|
|
293
|
+
}
|
|
277
294
|
async function renderAstroComponentToDom(component, args, resolveFrom) {
|
|
278
295
|
const moduleId = getComponentModuleId(component);
|
|
296
|
+
const { componentArgs, storySlots } = separateStorySlots(args);
|
|
297
|
+
const serializedArgs = serializeAstroComponentMarkers(componentArgs);
|
|
298
|
+
const serializedSlots = serializeAstroComponentMarkers(storySlots);
|
|
279
299
|
if (moduleId) {
|
|
280
300
|
try {
|
|
281
301
|
const html2 = await renderViaTestingRendererDaemon({
|
|
282
302
|
resolveFrom,
|
|
283
303
|
component: moduleId,
|
|
284
|
-
args
|
|
304
|
+
args: serializedArgs,
|
|
305
|
+
slots: serializedSlots
|
|
285
306
|
});
|
|
286
307
|
if (typeof html2 === "string") {
|
|
287
|
-
|
|
288
|
-
document.body.innerHTML = html2;
|
|
289
|
-
}
|
|
290
|
-
return html2;
|
|
308
|
+
return setRenderedHtml(html2);
|
|
291
309
|
}
|
|
292
310
|
} catch {
|
|
293
311
|
}
|
|
@@ -295,12 +313,10 @@ async function renderAstroComponentToDom(component, args, resolveFrom) {
|
|
|
295
313
|
const handler = await getAstroSsrHandler(resolveFrom);
|
|
296
314
|
const html2 = await handler({
|
|
297
315
|
component: moduleId,
|
|
298
|
-
args
|
|
316
|
+
args: serializedArgs,
|
|
317
|
+
slots: serializedSlots
|
|
299
318
|
});
|
|
300
|
-
|
|
301
|
-
document.body.innerHTML = html2;
|
|
302
|
-
}
|
|
303
|
-
return html2;
|
|
319
|
+
return setRenderedHtml(html2);
|
|
304
320
|
} catch {
|
|
305
321
|
}
|
|
306
322
|
}
|
|
@@ -309,13 +325,21 @@ async function renderAstroComponentToDom(component, args, resolveFrom) {
|
|
|
309
325
|
if (!container) {
|
|
310
326
|
throw new Error("Failed to initialize Astro container for rendering");
|
|
311
327
|
}
|
|
328
|
+
const loadComponent = async (id) => {
|
|
329
|
+
const viteServer = await getAstroSsrViteServer(resolveFrom);
|
|
330
|
+
const mod = await ssrLoadModuleWithFsFallback(viteServer, id);
|
|
331
|
+
return patchCreateAstroCompat(mod.default);
|
|
332
|
+
};
|
|
333
|
+
const reconstructedArgs = await reconstructProps(serializedArgs, { loadComponent });
|
|
334
|
+
const reconstructedSlots = await reconstructSlots(serializedSlots, {
|
|
335
|
+
loadComponent,
|
|
336
|
+
renderToHtml: (child) => container.renderToString(child, {})
|
|
337
|
+
});
|
|
312
338
|
const html = await container.renderToString(resolvedComponent, {
|
|
313
|
-
props:
|
|
339
|
+
props: reconstructedArgs,
|
|
340
|
+
slots: markRawSlots(reconstructedSlots)
|
|
314
341
|
});
|
|
315
|
-
|
|
316
|
-
document.body.innerHTML = html;
|
|
317
|
-
}
|
|
318
|
-
return html;
|
|
342
|
+
return setRenderedHtml(html);
|
|
319
343
|
}
|
|
320
344
|
async function renderComposedStory(story) {
|
|
321
345
|
const meta = story.__storybookAstroMeta;
|
package/dist/testing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/testing/story-composition.ts","../src/testing/astro-runtime.ts","../src/testing/project-root.ts","../src/testing/component-utils.ts"],"sourcesContent":["import {\n composeStories as portableComposeStories,\n composeStory as portableComposeStory,\n setProjectAnnotations as portableSetProjectAnnotations,\n} from '../portable-stories.ts';\nimport type { ProjectAnnotations, Store_CSFExports as StoreCsfExports } from 'storybook/internal/types';\nimport type { AstroRenderer } from '../portable-stories.ts';\nimport type { ComposedStory, StoryMeta } from './types.ts';\n\nexport function composeStories<\n TModule extends StoreCsfExports<AstroRenderer> & Record<string, unknown>\n>(\n storiesImport: TModule,\n projectAnnotations?: ProjectAnnotations<AstroRenderer>\n) {\n const composed = portableComposeStories(storiesImport, projectAnnotations);\n\n for (const [storyExportName, story] of Object.entries(composed)) {\n if (typeof story === 'function') {\n const composedStory = story as ComposedStory;\n\n composedStory.__storybookAstroMeta = storiesImport.default as StoryMeta;\n composedStory.__storybookAstroStoryExport = storiesImport[\n storyExportName as keyof TModule\n ] as ComposedStory['__storybookAstroStoryExport'];\n }\n }\n\n return composed;\n}\n\nexport const composeStory = portableComposeStory;\nexport const setProjectAnnotations = portableSetProjectAnnotations;\n","import { fileURLToPath } from 'node:url';\nimport type { ViteDevServer } from 'vite';\nimport type { Integration as StorybookAstroIntegration } from '../integrations/base.ts';\nimport { resolveTestingIntegrationsForRoot } from './integration-config.ts';\nimport { resolveTestingProjectRoot } from './project-root.ts';\nimport { runWithWorkingDirectory } from './working-directory.ts';\nimport { getComponentModuleId, isAstroComponentFactory, isStorybookAstroClientStub } from './component-utils.ts';\nimport { ssrLoadModuleWithFsFallback } from '../lib/ssr-load-module-with-fs-fallback.ts';\nimport type { ComposedStory } from './types.ts';\nimport { renderViaTestingRendererDaemon } from './renderer-daemon.ts';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nlet astroContainerPromise: Promise<any> | null = null;\n\nconst astroSsrViteServerPromises = new Map<string, Promise<ViteDevServer>>();\n\nconst astroSsrHandlerPromises = new Map<\n string,\n Promise<(data: { component: string; args?: Record<string, unknown> }) => Promise<string>>\n>();\n\nconst testingIntegrationsCache = new Map<string, StorybookAstroIntegration[]>();\n\nfunction getTestingIntegrations(resolveFrom: string) {\n if (!testingIntegrationsCache.has(resolveFrom)) {\n testingIntegrationsCache.set(resolveFrom, resolveTestingIntegrationsForRoot(resolveFrom));\n }\n\n return testingIntegrationsCache.get(resolveFrom)!;\n}\n\nasync function getAstroContainer() {\n if (!astroContainerPromise) {\n const { experimental_AstroContainer: AstroContainer } = await import('astro/container');\n\n astroContainerPromise = AstroContainer.create();\n }\n\n return astroContainerPromise;\n}\n\nasync function getAstroSsrViteServer(resolveFrom: string) {\n if (!astroSsrViteServerPromises.has(resolveFrom)) {\n const { createViteServer } = await import('../viteStorybookAstroMiddlewarePlugin.ts');\n const integrations = getTestingIntegrations(resolveFrom);\n\n astroSsrViteServerPromises.set(\n resolveFrom,\n runWithWorkingDirectory(resolveFrom, () => createViteServer(integrations, resolveFrom))\n );\n }\n\n return astroSsrViteServerPromises.get(resolveFrom)!;\n}\n\nasync function getAstroSsrHandler(resolveFrom: string) {\n if (!astroSsrHandlerPromises.has(resolveFrom)) {\n astroSsrHandlerPromises.set(resolveFrom, (async () => {\n const integrations = getTestingIntegrations(resolveFrom);\n const viteServer = await getAstroSsrViteServer(resolveFrom);\n const middlewareModulePath = fileURLToPath(new URL('../middleware', import.meta.url));\n const middleware = await runWithWorkingDirectory(resolveFrom, () =>\n viteServer.ssrLoadModule(middlewareModulePath, {\n fixStacktrace: true\n })\n );\n\n return middleware.handlerFactory(integrations, {\n loadModule: (id: string) =>\n ssrLoadModuleWithFsFallback(viteServer, id, {\n fixStacktrace: true\n })\n });\n })());\n }\n\n return astroSsrHandlerPromises.get(resolveFrom)!;\n}\n\nasync function resolveAstroComponent(component: unknown, resolveFrom: string) {\n let resolvedComponent = component;\n\n if (!isAstroComponentFactory(resolvedComponent)) {\n throw new Error('Story meta.component must be an Astro component factory.');\n }\n\n if ('moduleId' in resolvedComponent && typeof resolvedComponent.moduleId === 'string') {\n const moduleId = resolvedComponent.moduleId;\n const normalizedModuleId = moduleId.split('?')[0].split('#')[0];\n\n try {\n const mod = await import(/* @vite-ignore */ normalizedModuleId) as Record<string, unknown>;\n\n if (isAstroComponentFactory(mod.default)) {\n resolvedComponent = mod.default;\n }\n } catch {\n // keep current component when direct module import is unavailable\n }\n\n if (isStorybookAstroClientStub(resolvedComponent)) {\n try {\n const viteServer = await getAstroSsrViteServer(resolveFrom);\n const mod = (await ssrLoadModuleWithFsFallback(viteServer, normalizedModuleId)) as Record<string, unknown>;\n\n if (isAstroComponentFactory(mod.default)) {\n resolvedComponent = mod.default;\n }\n } catch {\n // keep current component when SSR module loading is unavailable\n }\n }\n }\n\n return resolvedComponent;\n}\n\nasync function renderAstroComponentToDom(\n component: unknown,\n args: Record<string, unknown>,\n resolveFrom: string\n) {\n const moduleId = getComponentModuleId(component);\n\n if (moduleId) {\n try {\n // Fast path: reuse a single shared SSR daemon instead of spinning SSR in each worker.\n const html = await renderViaTestingRendererDaemon({\n resolveFrom,\n component: moduleId,\n args\n });\n\n if (typeof html === 'string') {\n if (typeof document !== 'undefined') {\n document.body.innerHTML = html;\n }\n\n return html;\n }\n } catch {\n // Fall back to in-worker rendering below when daemon render fails.\n }\n\n try {\n const handler = await getAstroSsrHandler(resolveFrom);\n const html = await handler({\n component: moduleId,\n args\n });\n\n if (typeof document !== 'undefined') {\n document.body.innerHTML = html;\n }\n\n return html;\n } catch {\n // Fall back to direct Container rendering below\n }\n }\n\n const resolvedComponent = await resolveAstroComponent(component, resolveFrom);\n const container = await getAstroContainer();\n \n if (!container) {\n throw new Error('Failed to initialize Astro container for rendering');\n }\n \n const html = await container.renderToString(resolvedComponent, {\n props: args\n });\n\n if (typeof document !== 'undefined') {\n document.body.innerHTML = html;\n }\n\n return html;\n}\n\nasync function renderComposedStory(story: ComposedStory) {\n const meta = story.__storybookAstroMeta;\n const storyExport = story.__storybookAstroStoryExport;\n let component = meta?.component ?? story.component;\n\n if (!isAstroComponentFactory(component)) {\n const maybeRendered = await story();\n\n if (isAstroComponentFactory(maybeRendered)) {\n component = maybeRendered;\n } else if (\n typeof maybeRendered === 'object' &&\n maybeRendered !== null &&\n 'component' in maybeRendered &&\n isAstroComponentFactory((maybeRendered as { component: unknown }).component)\n ) {\n component = (maybeRendered as { component: unknown }).component;\n }\n }\n\n if (!component) {\n throw new Error('Unable to resolve Astro component from composed story.');\n }\n\n const args = {\n ...(meta?.args ?? {}),\n ...(storyExport?.args ?? {}),\n ...(story.args ?? {})\n };\n\n const resolveFrom = await resolveTestingProjectRoot(component);\n\n return renderAstroComponentToDom(component, args, resolveFrom);\n}\n\nexport async function renderStory(story: ComposedStory) {\n return renderComposedStory(story);\n}\n\nexport const renderAstroStory = renderStory;\n","import { existsSync, readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { dirname, join, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { getComponentModuleFilePath } from './component-utils.ts';\n\nconst VITEST_CONFIG_FILES = [\n 'vitest.config.ts',\n 'vitest.config.mts',\n 'vitest.config.js',\n 'vitest.config.mjs',\n 'vitest.config.cjs'\n];\n\nfunction extractStackFilePath(line: string) {\n const trimmed = line.trim();\n\n const match = trimmed.match(/\\((.+):(\\d+):(\\d+)\\)$/) ?? trimmed.match(/^at\\s+(.+):(\\d+):(\\d+)$/);\n\n if (!match) {\n return null;\n }\n\n const rawPath = match[1];\n\n if (rawPath.startsWith('node:')) {\n return null;\n }\n\n if (rawPath.startsWith('file://')) {\n return fileURLToPath(rawPath);\n }\n\n if (rawPath.startsWith('/')) {\n return rawPath;\n }\n\n return null;\n}\n\nasync function getCurrentTestFilePath() {\n try {\n const { expect } = await import('vitest');\n const vitestState = expect.getState() as {\n testPath?: string;\n filepath?: string;\n filePath?: string;\n };\n\n const fromVitestState = vitestState.testPath ?? vitestState.filepath ?? vitestState.filePath;\n\n if (typeof fromVitestState === 'string') {\n const absolutePath = fromVitestState.startsWith('/')\n ? fromVitestState\n : resolve(process.cwd(), fromVitestState);\n\n if (existsSync(absolutePath)) {\n return absolutePath;\n }\n }\n } catch {\n // Fall through to stack-based lookup when Vitest state is unavailable.\n }\n\n const stack = new Error().stack;\n\n if (!stack) {\n return null;\n }\n\n const thisFilePath = fileURLToPath(import.meta.url);\n\n for (const line of stack.split('\\n')) {\n const filePath = extractStackFilePath(line);\n\n if (!filePath) {\n continue;\n }\n\n if (filePath === thisFilePath || filePath.includes('/node_modules/')) {\n continue;\n }\n\n if (existsSync(filePath)) {\n return filePath;\n }\n }\n\n return null;\n}\n\nfunction findNearestVitestConfigDir(startPath: string) {\n let dir = dirname(startPath);\n\n while (true) {\n if (VITEST_CONFIG_FILES.some((name) => existsSync(join(dir, name)))) {\n return dir;\n }\n\n const parent = dirname(dir);\n\n if (parent === dir) {\n break;\n }\n\n dir = parent;\n }\n\n return null;\n}\n\nfunction packageJsonDeclaresAstro(packageJsonPath: string) {\n if (!existsSync(packageJsonPath)) {\n return false;\n }\n\n try {\n const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));\n\n return ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'].some(\n (field) =>\n packageJson[field] &&\n typeof packageJson[field] === 'object' &&\n Object.prototype.hasOwnProperty.call(packageJson[field], 'astro')\n );\n } catch {\n return false;\n }\n}\n\nfunction findNearestAstroPackageDir(startPath: string) {\n let dir = dirname(startPath);\n\n while (true) {\n const packageJsonPath = join(dir, 'package.json');\n\n if (packageJsonDeclaresAstro(packageJsonPath)) {\n return dir;\n }\n\n const parent = dirname(dir);\n\n if (parent === dir) {\n break;\n }\n\n dir = parent;\n }\n\n return null;\n}\n\nfunction canResolveAstroFrom(dir: string) {\n try {\n const require = createRequire(`${join(dir, '__storybook-astro-testing-resolve__.js')}`);\n\n require.resolve('astro/package.json');\n\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function resolveTestingProjectRoot(component: unknown) {\n const currentTestFilePath = await getCurrentTestFilePath();\n const componentModulePath = getComponentModuleFilePath(component);\n const candidates = [\n currentTestFilePath ? findNearestVitestConfigDir(currentTestFilePath) : null,\n currentTestFilePath ? findNearestAstroPackageDir(currentTestFilePath) : null,\n componentModulePath ? findNearestAstroPackageDir(componentModulePath) : null,\n packageJsonDeclaresAstro(join(process.cwd(), 'package.json')) ? process.cwd() : null,\n process.env.INIT_CWD && packageJsonDeclaresAstro(join(process.env.INIT_CWD, 'package.json'))\n ? process.env.INIT_CWD\n : null\n ].filter((value): value is string => Boolean(value));\n\n for (const candidate of candidates) {\n if (canResolveAstroFrom(candidate)) {\n return candidate;\n }\n }\n\n return process.cwd();\n}\n","export function isStorybookAstroClientStub(component: unknown) {\n return (\n typeof component === 'function' &&\n String(component).includes('Astro components are rendered server-side by Storybook')\n );\n}\n\nexport function isAstroComponentFactory(component: unknown) {\n return typeof component === 'function' && 'isAstroComponentFactory' in component;\n}\n\nexport function getComponentModuleId(component: unknown) {\n if (typeof component !== 'function' || !('moduleId' in component)) {\n return null;\n }\n\n if (typeof component.moduleId !== 'string') {\n return null;\n }\n\n return component.moduleId.split('?')[0].split('#')[0];\n}\n\nexport function getComponentModuleFilePath(component: unknown) {\n const moduleId = getComponentModuleId(component);\n\n if (!moduleId || !moduleId.startsWith('/')) {\n return null;\n }\n\n return moduleId;\n}\n"],"mappings":"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","names":["composeStories","composeStory","setProjectAnnotations","fileURLToPath","require","fileURLToPath","html"]}
|
|
1
|
+
{"version":3,"sources":["../src/testing/story-composition.ts","../src/testing/astro-runtime.ts","../src/testing/project-root.ts","../src/testing/component-utils.ts"],"sourcesContent":["import {\n composeStories as portableComposeStories,\n composeStory as portableComposeStory,\n setProjectAnnotations as portableSetProjectAnnotations,\n} from '../portable-stories.ts';\nimport type { ProjectAnnotations, Store_CSFExports as StoreCsfExports } from 'storybook/internal/types';\nimport type { AstroRenderer } from '../portable-stories.ts';\nimport type { ComposedStory, StoryMeta } from './types.ts';\n\nexport function composeStories<\n TModule extends StoreCsfExports<AstroRenderer> & Record<string, unknown>\n>(\n storiesImport: TModule,\n projectAnnotations?: ProjectAnnotations<AstroRenderer>\n) {\n const composed = portableComposeStories(storiesImport, projectAnnotations);\n\n for (const [storyExportName, story] of Object.entries(composed)) {\n if (typeof story === 'function') {\n const composedStory = story as ComposedStory;\n\n composedStory.__storybookAstroMeta = storiesImport.default as StoryMeta;\n composedStory.__storybookAstroStoryExport = storiesImport[\n storyExportName as keyof TModule\n ] as ComposedStory['__storybookAstroStoryExport'];\n }\n }\n\n return composed;\n}\n\nexport const composeStory = portableComposeStory;\nexport const setProjectAnnotations = portableSetProjectAnnotations;\n","import { fileURLToPath } from 'node:url';\nimport type { ViteDevServer } from 'vite';\nimport type { Integration as StorybookAstroIntegration } from '../integrations/base.ts';\nimport { resolveTestingIntegrationsForRoot } from './integration-config.ts';\nimport { resolveTestingProjectRoot } from './project-root.ts';\nimport { runWithWorkingDirectory } from './working-directory.ts';\nimport { getComponentModuleId, isAstroComponentFactory, isStorybookAstroClientStub } from './component-utils.ts';\nimport { ssrLoadModuleWithFsFallback } from '../lib/ssr-load-module-with-fs-fallback.ts';\nimport { separateStorySlots } from '../lib/separate-story-slots.ts';\nimport { reconstructProps, reconstructSlots } from '../lib/reconstruct-component-args.ts';\nimport { patchCreateAstroCompat, markRawSlots } from '../astroRenderHandler.ts';\nimport { serializeAstroComponentMarkers } from '@storybook-astro/renderer/types';\nimport type { ComposedStory } from './types.ts';\nimport { renderViaTestingRendererDaemon } from './renderer-daemon.ts';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nlet astroContainerPromise: Promise<any> | null = null;\n\nconst astroSsrViteServerPromises = new Map<string, Promise<ViteDevServer>>();\n\nconst astroSsrHandlerPromises = new Map<\n string,\n Promise<\n (data: {\n component: string;\n args?: Record<string, unknown>;\n slots?: Record<string, unknown>;\n }) => Promise<string>\n >\n>();\n\nconst testingIntegrationsCache = new Map<string, StorybookAstroIntegration[]>();\n\nfunction getTestingIntegrations(resolveFrom: string) {\n if (!testingIntegrationsCache.has(resolveFrom)) {\n testingIntegrationsCache.set(resolveFrom, resolveTestingIntegrationsForRoot(resolveFrom));\n }\n\n return testingIntegrationsCache.get(resolveFrom)!;\n}\n\nasync function getAstroContainer() {\n if (!astroContainerPromise) {\n const { experimental_AstroContainer: AstroContainer } = await import('astro/container');\n\n astroContainerPromise = AstroContainer.create();\n }\n\n return astroContainerPromise;\n}\n\nasync function getAstroSsrViteServer(resolveFrom: string) {\n if (!astroSsrViteServerPromises.has(resolveFrom)) {\n const { createViteServer } = await import('../viteStorybookAstroMiddlewarePlugin.ts');\n const integrations = getTestingIntegrations(resolveFrom);\n\n astroSsrViteServerPromises.set(\n resolveFrom,\n runWithWorkingDirectory(resolveFrom, () => createViteServer(integrations, resolveFrom))\n );\n }\n\n return astroSsrViteServerPromises.get(resolveFrom)!;\n}\n\nasync function getAstroSsrHandler(resolveFrom: string) {\n if (!astroSsrHandlerPromises.has(resolveFrom)) {\n astroSsrHandlerPromises.set(resolveFrom, (async () => {\n const integrations = getTestingIntegrations(resolveFrom);\n const viteServer = await getAstroSsrViteServer(resolveFrom);\n const middlewareModulePath = fileURLToPath(new URL('../middleware', import.meta.url));\n const middleware = await runWithWorkingDirectory(resolveFrom, () =>\n viteServer.ssrLoadModule(middlewareModulePath, {\n fixStacktrace: true\n })\n );\n\n return middleware.handlerFactory(integrations, {\n loadModule: (id: string) =>\n ssrLoadModuleWithFsFallback(viteServer, id, {\n fixStacktrace: true\n })\n });\n })());\n }\n\n return astroSsrHandlerPromises.get(resolveFrom)!;\n}\n\nasync function resolveAstroComponent(component: unknown, resolveFrom: string) {\n let resolvedComponent = component;\n\n if (!isAstroComponentFactory(resolvedComponent)) {\n throw new Error('Story meta.component must be an Astro component factory.');\n }\n\n if ('moduleId' in resolvedComponent && typeof resolvedComponent.moduleId === 'string') {\n const moduleId = resolvedComponent.moduleId;\n const normalizedModuleId = moduleId.split('?')[0].split('#')[0];\n\n try {\n const mod = await import(/* @vite-ignore */ normalizedModuleId) as Record<string, unknown>;\n\n if (isAstroComponentFactory(mod.default)) {\n resolvedComponent = mod.default;\n }\n } catch {\n // keep current component when direct module import is unavailable\n }\n\n if (isStorybookAstroClientStub(resolvedComponent)) {\n try {\n const viteServer = await getAstroSsrViteServer(resolveFrom);\n const mod = (await ssrLoadModuleWithFsFallback(viteServer, normalizedModuleId)) as Record<string, unknown>;\n\n if (isAstroComponentFactory(mod.default)) {\n resolvedComponent = mod.default;\n }\n } catch {\n // keep current component when SSR module loading is unavailable\n }\n }\n }\n\n return resolvedComponent;\n}\n\nfunction setRenderedHtml(html: string) {\n if (typeof document !== 'undefined') {\n document.body.innerHTML = html;\n }\n\n return html;\n}\n\nasync function renderAstroComponentToDom(\n component: unknown,\n args: Record<string, unknown>,\n resolveFrom: string\n) {\n const moduleId = getComponentModuleId(component);\n\n // Split slot content from props, then serialize any Astro component passed as\n // a prop or slot into a moduleId marker. The handler reconstructs each marker —\n // loading the real server component by moduleId — so a story can nest Astro\n // components without the unrenderable client stub leaking through.\n const { componentArgs, storySlots } = separateStorySlots(args);\n const serializedArgs = serializeAstroComponentMarkers(componentArgs) as Record<string, unknown>;\n const serializedSlots = serializeAstroComponentMarkers(storySlots) as Record<string, unknown>;\n\n if (moduleId) {\n try {\n // Fast path: reuse a single shared SSR daemon instead of spinning SSR in each worker.\n const html = await renderViaTestingRendererDaemon({\n resolveFrom,\n component: moduleId,\n args: serializedArgs,\n slots: serializedSlots\n });\n\n if (typeof html === 'string') {\n return setRenderedHtml(html);\n }\n } catch {\n // Fall back to in-worker rendering below when daemon render fails.\n }\n\n try {\n const handler = await getAstroSsrHandler(resolveFrom);\n const html = await handler({\n component: moduleId,\n args: serializedArgs,\n slots: serializedSlots\n });\n\n return setRenderedHtml(html);\n } catch {\n // Fall back to direct Container rendering below\n }\n }\n\n const resolvedComponent = await resolveAstroComponent(component, resolveFrom);\n const container = await getAstroContainer();\n\n if (!container) {\n throw new Error('Failed to initialize Astro container for rendering');\n }\n\n // The direct fallback has no handler, so reconstruct nested components here:\n // load each marker's real server module by id and render slot markers to HTML.\n const loadComponent = async (id: string) => {\n const viteServer = await getAstroSsrViteServer(resolveFrom);\n const mod = (await ssrLoadModuleWithFsFallback(viteServer, id)) as Record<string, unknown>;\n\n return patchCreateAstroCompat(mod.default);\n };\n const reconstructedArgs = await reconstructProps(serializedArgs, { loadComponent });\n const reconstructedSlots = await reconstructSlots(serializedSlots, {\n loadComponent,\n renderToHtml: (child) => container.renderToString(child, {})\n });\n\n const html = await container.renderToString(resolvedComponent, {\n props: reconstructedArgs,\n slots: markRawSlots(reconstructedSlots)\n });\n\n return setRenderedHtml(html);\n}\n\nasync function renderComposedStory(story: ComposedStory) {\n const meta = story.__storybookAstroMeta;\n const storyExport = story.__storybookAstroStoryExport;\n let component = meta?.component ?? story.component;\n\n if (!isAstroComponentFactory(component)) {\n const maybeRendered = await story();\n\n if (isAstroComponentFactory(maybeRendered)) {\n component = maybeRendered;\n } else if (\n typeof maybeRendered === 'object' &&\n maybeRendered !== null &&\n 'component' in maybeRendered &&\n isAstroComponentFactory((maybeRendered as { component: unknown }).component)\n ) {\n component = (maybeRendered as { component: unknown }).component;\n }\n }\n\n if (!component) {\n throw new Error('Unable to resolve Astro component from composed story.');\n }\n\n const args = {\n ...(meta?.args ?? {}),\n ...(storyExport?.args ?? {}),\n ...(story.args ?? {})\n };\n\n const resolveFrom = await resolveTestingProjectRoot(component);\n\n return renderAstroComponentToDom(component, args, resolveFrom);\n}\n\nexport async function renderStory(story: ComposedStory) {\n return renderComposedStory(story);\n}\n\nexport const renderAstroStory = renderStory;\n","import { existsSync, readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { dirname, join, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { getComponentModuleFilePath } from './component-utils.ts';\n\nconst VITEST_CONFIG_FILES = [\n 'vitest.config.ts',\n 'vitest.config.mts',\n 'vitest.config.js',\n 'vitest.config.mjs',\n 'vitest.config.cjs'\n];\n\nfunction extractStackFilePath(line: string) {\n const trimmed = line.trim();\n\n const match = trimmed.match(/\\((.+):(\\d+):(\\d+)\\)$/) ?? trimmed.match(/^at\\s+(.+):(\\d+):(\\d+)$/);\n\n if (!match) {\n return null;\n }\n\n const rawPath = match[1];\n\n if (rawPath.startsWith('node:')) {\n return null;\n }\n\n if (rawPath.startsWith('file://')) {\n return fileURLToPath(rawPath);\n }\n\n if (rawPath.startsWith('/')) {\n return rawPath;\n }\n\n return null;\n}\n\nasync function getCurrentTestFilePath() {\n try {\n const { expect } = await import('vitest');\n const vitestState = expect.getState() as {\n testPath?: string;\n filepath?: string;\n filePath?: string;\n };\n\n const fromVitestState = vitestState.testPath ?? vitestState.filepath ?? vitestState.filePath;\n\n if (typeof fromVitestState === 'string') {\n const absolutePath = fromVitestState.startsWith('/')\n ? fromVitestState\n : resolve(process.cwd(), fromVitestState);\n\n if (existsSync(absolutePath)) {\n return absolutePath;\n }\n }\n } catch {\n // Fall through to stack-based lookup when Vitest state is unavailable.\n }\n\n const stack = new Error().stack;\n\n if (!stack) {\n return null;\n }\n\n const thisFilePath = fileURLToPath(import.meta.url);\n\n for (const line of stack.split('\\n')) {\n const filePath = extractStackFilePath(line);\n\n if (!filePath) {\n continue;\n }\n\n if (filePath === thisFilePath || filePath.includes('/node_modules/')) {\n continue;\n }\n\n if (existsSync(filePath)) {\n return filePath;\n }\n }\n\n return null;\n}\n\nfunction findNearestVitestConfigDir(startPath: string) {\n let dir = dirname(startPath);\n\n while (true) {\n if (VITEST_CONFIG_FILES.some((name) => existsSync(join(dir, name)))) {\n return dir;\n }\n\n const parent = dirname(dir);\n\n if (parent === dir) {\n break;\n }\n\n dir = parent;\n }\n\n return null;\n}\n\nfunction packageJsonDeclaresAstro(packageJsonPath: string) {\n if (!existsSync(packageJsonPath)) {\n return false;\n }\n\n try {\n const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));\n\n return ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'].some(\n (field) =>\n packageJson[field] &&\n typeof packageJson[field] === 'object' &&\n Object.prototype.hasOwnProperty.call(packageJson[field], 'astro')\n );\n } catch {\n return false;\n }\n}\n\nfunction findNearestAstroPackageDir(startPath: string) {\n let dir = dirname(startPath);\n\n while (true) {\n const packageJsonPath = join(dir, 'package.json');\n\n if (packageJsonDeclaresAstro(packageJsonPath)) {\n return dir;\n }\n\n const parent = dirname(dir);\n\n if (parent === dir) {\n break;\n }\n\n dir = parent;\n }\n\n return null;\n}\n\nfunction canResolveAstroFrom(dir: string) {\n try {\n const require = createRequire(`${join(dir, '__storybook-astro-testing-resolve__.js')}`);\n\n require.resolve('astro/package.json');\n\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function resolveTestingProjectRoot(component: unknown) {\n const currentTestFilePath = await getCurrentTestFilePath();\n const componentModulePath = getComponentModuleFilePath(component);\n const candidates = [\n currentTestFilePath ? findNearestVitestConfigDir(currentTestFilePath) : null,\n currentTestFilePath ? findNearestAstroPackageDir(currentTestFilePath) : null,\n componentModulePath ? findNearestAstroPackageDir(componentModulePath) : null,\n packageJsonDeclaresAstro(join(process.cwd(), 'package.json')) ? process.cwd() : null,\n process.env.INIT_CWD && packageJsonDeclaresAstro(join(process.env.INIT_CWD, 'package.json'))\n ? process.env.INIT_CWD\n : null\n ].filter((value): value is string => Boolean(value));\n\n for (const candidate of candidates) {\n if (canResolveAstroFrom(candidate)) {\n return candidate;\n }\n }\n\n return process.cwd();\n}\n","export function isStorybookAstroClientStub(component: unknown) {\n return (\n typeof component === 'function' &&\n String(component).includes('Astro components are rendered server-side by Storybook')\n );\n}\n\nexport function isAstroComponentFactory(component: unknown) {\n return typeof component === 'function' && 'isAstroComponentFactory' in component;\n}\n\nexport function getComponentModuleId(component: unknown) {\n if (typeof component !== 'function' || !('moduleId' in component)) {\n return null;\n }\n\n if (typeof component.moduleId !== 'string') {\n return null;\n }\n\n return component.moduleId.split('?')[0].split('#')[0];\n}\n\nexport function getComponentModuleFilePath(component: unknown) {\n const moduleId = getComponentModuleId(component);\n\n if (!moduleId || !moduleId.startsWith('/')) {\n return null;\n }\n\n return moduleId;\n}\n"],"mappings":"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","names":["composeStories","composeStory","setProjectAnnotations","fileURLToPath","require","fileURLToPath","html"]}
|
package/dist/vitest/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@storybook-astro/framework",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Community-supported Storybook framework for Astro 5 &
|
|
3
|
+
"version": "1.7.0-canary.2",
|
|
4
|
+
"description": "Community-supported Storybook framework for Astro 5, 6 & 7 components",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -60,6 +60,7 @@
|
|
|
60
60
|
"build": "tsup && node scripts/patch-shim-ref.mjs",
|
|
61
61
|
"dev": "tsup --watch --no-clean",
|
|
62
62
|
"lint": "eslint .",
|
|
63
|
+
"test": "vitest run",
|
|
63
64
|
"prepublishOnly": "tsup && node scripts/patch-shim-ref.mjs"
|
|
64
65
|
},
|
|
65
66
|
"devDependencies": {
|
|
@@ -73,7 +74,7 @@
|
|
|
73
74
|
"@vitejs/plugin-react": "^5.1.4",
|
|
74
75
|
"@vitest/coverage-v8": "4.0.18",
|
|
75
76
|
"alpinejs": "^3.14.9",
|
|
76
|
-
"astro": "^
|
|
77
|
+
"astro": "^6.4.0",
|
|
77
78
|
"eslint": "^9.39.2",
|
|
78
79
|
"storybook-solidjs": "^1.0.0-beta.7",
|
|
79
80
|
"tsup": "^8.5.1",
|
|
@@ -81,21 +82,21 @@
|
|
|
81
82
|
"vitest": "^4.0.18"
|
|
82
83
|
},
|
|
83
84
|
"peerDependencies": {
|
|
84
|
-
"@astrojs/alpinejs": "^0.4.5 || ^0.5.0",
|
|
85
|
-
"@astrojs/preact": "^4.0.8 || ^5.0.0",
|
|
86
|
-
"@astrojs/react": "^4.2.3 || ^5.0.0",
|
|
87
|
-
"@astrojs/solid-js": "^5.0.7 || ^6.0.0",
|
|
88
|
-
"@astrojs/svelte": "^7.0.9 || ^8.0.0",
|
|
89
|
-
"@astrojs/vue": "^5.0.9 || ^6.0.0",
|
|
85
|
+
"@astrojs/alpinejs": "^0.4.5 || ^0.5.0 || ^1.0.0",
|
|
86
|
+
"@astrojs/preact": "^4.0.8 || ^5.0.0 || ^6.0.0",
|
|
87
|
+
"@astrojs/react": "^4.2.3 || ^5.0.0 || ^6.0.0",
|
|
88
|
+
"@astrojs/solid-js": "^5.0.7 || ^6.0.0 || ^7.0.0",
|
|
89
|
+
"@astrojs/svelte": "^7.0.9 || ^8.0.0 || ^9.0.0",
|
|
90
|
+
"@astrojs/vue": "^5.0.9 || ^6.0.0 || ^7.0.0",
|
|
90
91
|
"@preact/preset-vite": "^2.10.1",
|
|
91
92
|
"@storybook/preact": "^10.0.0",
|
|
92
93
|
"@storybook/react": "^10.0.0",
|
|
93
94
|
"@storybook/svelte": "^10.0.0",
|
|
94
95
|
"@storybook/vue3": "^10.0.0",
|
|
95
|
-
"@vitejs/plugin-react": "^5.0.0",
|
|
96
|
+
"@vitejs/plugin-react": "^5.0.0 || ^6.0.0",
|
|
96
97
|
"@vitejs/plugin-vue": "^5.2.3 || ^6.0.0",
|
|
97
98
|
"@vitejs/plugin-vue-jsx": "^4.1.2 || ^5.0.0",
|
|
98
|
-
"astro": "^5.5.3 || ^6.0.0",
|
|
99
|
+
"astro": "^5.5.3 || ^6.0.0 || ^7.0.0",
|
|
99
100
|
"storybook": "^10.0.0",
|
|
100
101
|
"storybook-solidjs": "^1.0.0-beta.7",
|
|
101
102
|
"vite": "^6.4.1 || ^7.0.0 || ^8.0.0"
|
|
@@ -134,6 +135,9 @@
|
|
|
134
135
|
"@storybook/vue3": {
|
|
135
136
|
"optional": true
|
|
136
137
|
},
|
|
138
|
+
"@vitejs/plugin-react": {
|
|
139
|
+
"optional": true
|
|
140
|
+
},
|
|
137
141
|
"@vitejs/plugin-vue": {
|
|
138
142
|
"optional": true
|
|
139
143
|
},
|
|
@@ -145,8 +149,8 @@
|
|
|
145
149
|
}
|
|
146
150
|
},
|
|
147
151
|
"dependencies": {
|
|
148
|
-
"@storybook-astro/renderer": "1.
|
|
149
|
-
"get-tsconfig": "
|
|
152
|
+
"@storybook-astro/renderer": "1.7.0-canary.2",
|
|
153
|
+
"get-tsconfig": "5.0.0-beta.4",
|
|
150
154
|
"hono": "^4.11.12",
|
|
151
155
|
"sanitize-html": "^2.17.0",
|
|
152
156
|
"vite": "^6.4.1 || ^7.0.0 || ^8.0.0"
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { experimental_AstroContainer as AstroContainer } from 'astro/container';
|
|
2
|
+
import { markHTMLString } from 'astro/runtime/server/index.js';
|
|
2
3
|
import type { SanitizationOptions } from './lib/sanitization.ts';
|
|
3
4
|
import { resolveSanitizationOptions, sanitizeRenderPayload } from './lib/sanitization.ts';
|
|
4
5
|
import { reviveDateStrings } from './lib/revive-dates.ts';
|
|
6
|
+
import { reconstructProps, reconstructSlots } from './lib/reconstruct-component-args.ts';
|
|
5
7
|
import { runWithStoryRules, type ResolveRulesConfigModule } from './storyRulesRuntime.ts';
|
|
6
8
|
import type { RenderStoryInput } from './types.ts';
|
|
7
9
|
|
|
@@ -86,7 +88,13 @@ export function createAstroRenderHandler(options: CreateAstroRenderHandlerOption
|
|
|
86
88
|
data.component,
|
|
87
89
|
selectedRules.moduleMocks.size === 0
|
|
88
90
|
);
|
|
89
|
-
|
|
91
|
+
// Resolve Astro components passed as props back to real factories
|
|
92
|
+
// before the other arg processing (factories pass through those
|
|
93
|
+
// untouched), so the parent template can render them with `<Comp />`.
|
|
94
|
+
const reconstructedArgs = await reconstructProps(data.args ?? {}, {
|
|
95
|
+
loadComponent: (moduleId) => loadPatchedComponent(moduleId)
|
|
96
|
+
});
|
|
97
|
+
const processedArgs = await processImageMetadata(reconstructedArgs);
|
|
90
98
|
const revivedArgs = reviveDateStrings(processedArgs);
|
|
91
99
|
const sanitizedPayload = sanitizeRenderPayload(
|
|
92
100
|
{
|
|
@@ -96,11 +104,23 @@ export function createAstroRenderHandler(options: CreateAstroRenderHandlerOption
|
|
|
96
104
|
sanitizationOptions
|
|
97
105
|
);
|
|
98
106
|
|
|
107
|
+
// Render component slots to HTML *after* sanitization so a component's
|
|
108
|
+
// own markup isn't stripped by the slot allowlist (string slots above
|
|
109
|
+
// still are). Markers pass through sanitization untouched.
|
|
110
|
+
const renderedSlots = await reconstructSlots(sanitizedPayload.slots, {
|
|
111
|
+
loadComponent: (moduleId) => loadPatchedComponent(moduleId),
|
|
112
|
+
renderToHtml: (component) =>
|
|
113
|
+
options.container.renderToString(
|
|
114
|
+
component as Parameters<typeof options.container.renderToString>[0],
|
|
115
|
+
{}
|
|
116
|
+
)
|
|
117
|
+
});
|
|
118
|
+
|
|
99
119
|
return options.container.renderToString(
|
|
100
120
|
patchedComponent as Parameters<typeof options.container.renderToString>[0],
|
|
101
121
|
{
|
|
102
122
|
props: sanitizedPayload.args,
|
|
103
|
-
slots:
|
|
123
|
+
slots: markRawSlots(renderedSlots)
|
|
104
124
|
}
|
|
105
125
|
);
|
|
106
126
|
}
|
|
@@ -118,6 +138,23 @@ export function createAstroRenderHandler(options: CreateAstroRenderHandlerOption
|
|
|
118
138
|
};
|
|
119
139
|
}
|
|
120
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Marks each slot's HTML string as already-rendered so the Astro Container emits
|
|
143
|
+
* it raw instead of escaping it. Astro 5 and 7 render string slots raw anyway,
|
|
144
|
+
* but Astro 6 escapes an unmarked string slot — this normalizes all versions.
|
|
145
|
+
* `markHTMLString` tags the string via a global symbol, so it's recognized even
|
|
146
|
+
* across Astro module instances.
|
|
147
|
+
*/
|
|
148
|
+
export function markRawSlots(slots: Record<string, unknown>): Record<string, unknown> {
|
|
149
|
+
const marked: Record<string, unknown> = {};
|
|
150
|
+
|
|
151
|
+
for (const [name, value] of Object.entries(slots)) {
|
|
152
|
+
marked[name] = typeof value === 'string' ? markHTMLString(value) : value;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return marked;
|
|
156
|
+
}
|
|
157
|
+
|
|
121
158
|
export function patchCreateAstroCompat(component: unknown): AstroComponentFactory {
|
|
122
159
|
if (typeof component !== 'function') {
|
|
123
160
|
throw new Error('Expected Astro component factory to be a function.');
|
package/src/index.ts
CHANGED
|
@@ -97,7 +97,14 @@ export function definePreview<Addons extends PreviewAddon<never>[] = []>(
|
|
|
97
97
|
): CsfPreview<AstroRenderer & InferTypes<Addons>> {
|
|
98
98
|
// Kick off the renderer load eagerly so the impl is ready by the time
|
|
99
99
|
// Storybook calls renderToCanvas — but don't await, so this stays sync.
|
|
100
|
-
|
|
100
|
+
// Only do this in a browser: the renderer's entry-preview imports browser-only
|
|
101
|
+
// modules (and the `virtual:storybook-astro-renderer` graph) that aren't
|
|
102
|
+
// available when `.storybook/preview.ts` is evaluated under Node during build
|
|
103
|
+
// prerendering. renderToCanvas never runs there, and the browser path still
|
|
104
|
+
// loads the renderer lazily via `composedRenderToCanvas`.
|
|
105
|
+
if (typeof document !== 'undefined') {
|
|
106
|
+
void loadRendererEntryPreview();
|
|
107
|
+
}
|
|
101
108
|
|
|
102
109
|
return definePreviewBase<AstroRenderer, Addons>({
|
|
103
110
|
...input,
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { test, expect, vi } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
ASTRO_COMPONENT_MARKER,
|
|
4
|
+
isAstroComponentMarker,
|
|
5
|
+
serializeAstroComponentMarkers
|
|
6
|
+
} from '@storybook-astro/renderer/types';
|
|
7
|
+
|
|
8
|
+
const factory = (moduleId: string | undefined) =>
|
|
9
|
+
Object.assign(() => undefined, { isAstroComponentFactory: true as const, moduleId });
|
|
10
|
+
|
|
11
|
+
test('isAstroComponentMarker requires both the flag and a string moduleId', () => {
|
|
12
|
+
expect(isAstroComponentMarker({ [ASTRO_COMPONENT_MARKER]: true, moduleId: '/A.astro' })).toBe(true);
|
|
13
|
+
// A plain object that happens to share one key is not a marker.
|
|
14
|
+
expect(isAstroComponentMarker({ [ASTRO_COMPONENT_MARKER]: true })).toBe(false);
|
|
15
|
+
expect(isAstroComponentMarker({ moduleId: '/A.astro' })).toBe(false);
|
|
16
|
+
expect(isAstroComponentMarker({ [ASTRO_COMPONENT_MARKER]: 'yes', moduleId: '/A.astro' })).toBe(false);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('serializeAstroComponentMarkers replaces factories with markers, including when nested', () => {
|
|
20
|
+
const result = serializeAstroComponentMarkers({
|
|
21
|
+
Icon: factory('/Icon.astro'),
|
|
22
|
+
nested: { Inner: factory('/Inner.astro') },
|
|
23
|
+
list: [factory('/A.astro'), 'keep']
|
|
24
|
+
}) as Record<string, unknown>;
|
|
25
|
+
|
|
26
|
+
expect(result.Icon).toEqual({ [ASTRO_COMPONENT_MARKER]: true, moduleId: '/Icon.astro' });
|
|
27
|
+
expect((result.nested as Record<string, unknown>).Inner).toEqual({
|
|
28
|
+
[ASTRO_COMPONENT_MARKER]: true,
|
|
29
|
+
moduleId: '/Inner.astro'
|
|
30
|
+
});
|
|
31
|
+
expect((result.list as unknown[])[0]).toEqual({ [ASTRO_COMPONENT_MARKER]: true, moduleId: '/A.astro' });
|
|
32
|
+
expect((result.list as unknown[])[1]).toBe('keep');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('serializeAstroComponentMarkers drops a factory with no moduleId and reports it', () => {
|
|
36
|
+
const error = vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
37
|
+
|
|
38
|
+
const result = serializeAstroComponentMarkers({ Icon: factory(undefined) }) as Record<string, unknown>;
|
|
39
|
+
|
|
40
|
+
expect(result.Icon).toBeUndefined();
|
|
41
|
+
expect(error).toHaveBeenCalled();
|
|
42
|
+
|
|
43
|
+
error.mockRestore();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('serializeAstroComponentMarkers leaves non-component values untouched', () => {
|
|
47
|
+
expect(serializeAstroComponentMarkers({ a: 1, b: 'x', c: null })).toEqual({ a: 1, b: 'x', c: null });
|
|
48
|
+
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { test, expect, vi } from 'vitest';
|
|
2
|
+
import { reconstructProps, reconstructSlots } from './reconstruct-component-args.ts';
|
|
3
|
+
|
|
4
|
+
const marker = (moduleId: string) => ({ __astroComponent: true as const, moduleId });
|
|
5
|
+
|
|
6
|
+
// A fake Astro factory: a function carrying the detection flag + moduleId.
|
|
7
|
+
const factory = (moduleId: string) =>
|
|
8
|
+
Object.assign(() => undefined, { isAstroComponentFactory: true as const, moduleId });
|
|
9
|
+
|
|
10
|
+
test('reconstructProps resolves a marker to the loaded factory', async () => {
|
|
11
|
+
const loaded = factory('/Icon.astro');
|
|
12
|
+
const loadComponent = vi.fn().mockResolvedValue(loaded);
|
|
13
|
+
|
|
14
|
+
const result = await reconstructProps({ Icon: marker('/Icon.astro') }, { loadComponent });
|
|
15
|
+
|
|
16
|
+
expect(loadComponent).toHaveBeenCalledWith('/Icon.astro');
|
|
17
|
+
expect(result.Icon).toBe(loaded);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('reconstructProps passes a raw factory through (testing path)', async () => {
|
|
21
|
+
const raw = factory('/Icon.astro');
|
|
22
|
+
const loadComponent = vi.fn();
|
|
23
|
+
|
|
24
|
+
const result = await reconstructProps({ Icon: raw }, { loadComponent });
|
|
25
|
+
|
|
26
|
+
expect(result.Icon).toBe(raw);
|
|
27
|
+
expect(loadComponent).not.toHaveBeenCalled();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('reconstructProps resolves markers nested in objects and arrays', async () => {
|
|
31
|
+
const loadComponent = vi.fn(async (id: string) => factory(id));
|
|
32
|
+
|
|
33
|
+
const result = await reconstructProps(
|
|
34
|
+
{ wrap: { Icon: marker('/A.astro') }, list: [marker('/B.astro'), 'x'] },
|
|
35
|
+
{ loadComponent }
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const resolvedIcon = (result.wrap as Record<string, unknown>).Icon as { moduleId?: string };
|
|
39
|
+
const resolvedListItem = (result.list as Array<{ moduleId?: string }>)[0];
|
|
40
|
+
|
|
41
|
+
expect(resolvedIcon.moduleId).toBe('/A.astro');
|
|
42
|
+
expect(resolvedListItem.moduleId).toBe('/B.astro');
|
|
43
|
+
expect((result.list as unknown[])[1]).toBe('x');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('reconstructProps preserves identity when there are no component references', async () => {
|
|
47
|
+
const args = { title: 'hi', meta: { src: '/img.png', width: 10 } };
|
|
48
|
+
const result = await reconstructProps(args, { loadComponent: vi.fn() });
|
|
49
|
+
|
|
50
|
+
expect(result.meta).toBe(args.meta);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('reconstructSlots renders a marker to HTML via the loaded component', async () => {
|
|
54
|
+
const loaded = factory('/Child.astro');
|
|
55
|
+
const loadComponent = vi.fn().mockResolvedValue(loaded);
|
|
56
|
+
const renderToHtml = vi.fn().mockResolvedValue('<span>child</span>');
|
|
57
|
+
|
|
58
|
+
const result = await reconstructSlots({ default: marker('/Child.astro') }, {
|
|
59
|
+
loadComponent,
|
|
60
|
+
renderToHtml
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
expect(loadComponent).toHaveBeenCalledWith('/Child.astro');
|
|
64
|
+
expect(renderToHtml).toHaveBeenCalledWith(loaded);
|
|
65
|
+
expect(result.default).toBe('<span>child</span>');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('reconstructSlots renders a raw factory slot and passes strings through', async () => {
|
|
69
|
+
const renderToHtml = vi.fn().mockResolvedValue('<b>x</b>');
|
|
70
|
+
|
|
71
|
+
const result = await reconstructSlots(
|
|
72
|
+
{ default: factory('/Child.astro'), footer: '<p>plain</p>' },
|
|
73
|
+
{ loadComponent: vi.fn(), renderToHtml }
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
expect(result.default).toBe('<b>x</b>');
|
|
77
|
+
expect(result.footer).toBe('<p>plain</p>');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('reconstructSlots concatenates an array slot into one HTML string', async () => {
|
|
81
|
+
const renderToHtml = vi.fn().mockResolvedValue('<i>c</i>');
|
|
82
|
+
|
|
83
|
+
const result = await reconstructSlots({ default: [marker('/C.astro'), ' and ', '<u>d</u>'] }, {
|
|
84
|
+
loadComponent: vi.fn(async (id: string) => factory(id)),
|
|
85
|
+
renderToHtml
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
expect(result.default).toBe('<i>c</i> and <u>d</u>');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('reconstructSlots wraps a render failure with the slot name', async () => {
|
|
92
|
+
const renderToHtml = vi.fn().mockRejectedValue(new Error('boom'));
|
|
93
|
+
|
|
94
|
+
await expect(
|
|
95
|
+
reconstructSlots({ header: factory('/C.astro') }, { loadComponent: vi.fn(), renderToHtml })
|
|
96
|
+
).rejects.toThrow(/slot "header".*boom/);
|
|
97
|
+
});
|