@storybook-astro/framework 0.1.0-beta.9 → 1.0.0
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/README.md +38 -0
- package/dist/base-IRZo3zgK.d.ts +23 -0
- package/dist/chunk-4SWPVM6R.js +96 -0
- package/dist/chunk-4SWPVM6R.js.map +1 -0
- package/dist/chunk-5EF25G5S.js +69 -0
- package/dist/chunk-5EF25G5S.js.map +1 -0
- package/dist/chunk-7GHEQUPV.js +439 -0
- package/dist/chunk-7GHEQUPV.js.map +1 -0
- package/dist/chunk-C5OH4VBR.js +492 -0
- package/dist/chunk-C5OH4VBR.js.map +1 -0
- package/dist/chunk-DNGQBPT7.js +15 -0
- package/dist/chunk-DNGQBPT7.js.map +1 -0
- package/dist/chunk-E4LB75JN.js +89 -0
- package/dist/chunk-E4LB75JN.js.map +1 -0
- package/dist/chunk-PJEDXZVN.js +240 -0
- package/dist/chunk-PJEDXZVN.js.map +1 -0
- package/dist/chunk-UK43WNEA.js +657 -0
- package/dist/chunk-UK43WNEA.js.map +1 -0
- package/dist/dist-HJOEPVRQ.js +15574 -0
- package/dist/dist-HJOEPVRQ.js.map +1 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +13 -64
- package/dist/index.js.map +1 -1
- package/dist/integrations/index.d.ts +138 -0
- package/dist/integrations/index.js +8 -196
- package/dist/integrations/index.js.map +1 -1
- package/dist/middleware.d.ts +26 -0
- package/dist/middleware.js +179 -0
- package/dist/middleware.js.map +1 -0
- package/dist/portable-stories-BvdaQigq.d.ts +83 -0
- package/dist/preset.d.ts +14 -0
- package/dist/preset.js +5 -1
- package/dist/testing.d.ts +27 -0
- package/dist/testing.js +324 -15539
- package/dist/testing.js.map +1 -1
- package/dist/types-CHTsRtA7.d.ts +42 -0
- package/dist/viteStorybookAstroMiddlewarePlugin-NP2E52IC.js +11 -0
- package/dist/viteStorybookAstroMiddlewarePlugin-NP2E52IC.js.map +1 -0
- package/dist/vitest/index.d.ts +19 -0
- package/dist/vitest/index.js +229 -0
- package/dist/vitest/index.js.map +1 -0
- package/package.json +31 -17
- package/src/importAstroConfig.ts +11 -0
- package/src/index.ts +20 -6
- package/src/integrations/alpine.ts +5 -2
- package/src/integrations/base.ts +2 -2
- package/src/integrations/moduleResolver.ts +43 -0
- package/src/integrations/preact.ts +5 -2
- package/src/integrations/react.ts +5 -2
- package/src/integrations/solid.ts +5 -2
- package/src/integrations/svelte.ts +5 -2
- package/src/integrations/vue.ts +5 -2
- package/src/lib/sanitization.test.ts +232 -0
- package/src/lib/sanitization.ts +338 -0
- package/src/lib/ssr-load-module-with-fs-fallback.ts +29 -0
- package/src/middleware.test.ts +48 -0
- package/src/middleware.ts +204 -96
- package/src/module-mocks.ts +16 -0
- package/src/msw-helpers.ts +1 -0
- package/src/msw.ts +58 -0
- package/src/preset.ts +38 -3
- package/src/rules-options.test.ts +71 -0
- package/src/rules-options.ts +87 -0
- package/src/rules.test.ts +183 -0
- package/src/rules.ts +314 -0
- package/src/testing/astro-runtime.ts +219 -0
- package/src/testing/component-utils.ts +32 -0
- package/src/testing/index.ts +2 -0
- package/src/testing/integration-config.ts +121 -0
- package/src/testing/project-root.ts +185 -0
- package/src/testing/renderer-daemon.ts +269 -0
- package/src/testing/story-composition.ts +33 -0
- package/src/testing/types.ts +14 -0
- package/src/testing/working-directory.ts +28 -0
- package/src/testing.ts +1 -254
- package/src/types.ts +16 -4
- package/src/virtual.d.ts +2 -1
- package/src/vite/createVirtualModulePlugin.test.ts +80 -0
- package/src/vite/createVirtualModulePlugin.ts +25 -0
- package/src/viteAstroContainerRenderersPlugin.ts +60 -26
- package/src/vitePluginAstro.ts +12 -5
- package/src/vitePluginAstroBuildPrerender.ts +665 -204
- package/src/vitePluginAstroRoutesFallback.ts +37 -0
- package/src/vitePluginAstroVueFallback.ts +47 -0
- package/src/viteStorybookAstroMiddlewarePlugin.ts +88 -12
- package/src/viteStorybookRendererFallbackPlugin.ts +13 -23
- package/src/vitest/config.ts +95 -0
- package/src/vitest/global-setup.ts +16 -0
- package/src/vitest/index.ts +2 -0
- package/src/vitest/vite-plugins.ts +187 -0
- package/dist/chunk-KTGNRGDJ.js +0 -561
- package/dist/chunk-KTGNRGDJ.js.map +0 -1
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
|
|
3
|
+
const ROUTES_STUB = `
|
|
4
|
+
export const routes = [];
|
|
5
|
+
`;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Provides fallback resolution for Astro's routes virtual module
|
|
9
|
+
* in Storybook's SSR Vite server.
|
|
10
|
+
*
|
|
11
|
+
* In Astro 6, the manifest and app entrypoints import from "virtual:astro:routes"
|
|
12
|
+
* to get route data. In Storybook's context, there are no routes, so this plugin
|
|
13
|
+
* stubs the virtual module with an empty routes array.
|
|
14
|
+
*/
|
|
15
|
+
export function vitePluginAstroRoutesFallback(): Plugin {
|
|
16
|
+
const VIRTUAL_ID = 'virtual:astro:routes';
|
|
17
|
+
const RESOLVED_VIRTUAL_ID = '\0' + VIRTUAL_ID;
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
name: 'storybook-astro-routes-fallback',
|
|
21
|
+
// Must run before vite:resolve to intercept virtual modules
|
|
22
|
+
// before Vite tries to resolve them as Node package imports
|
|
23
|
+
enforce: 'pre',
|
|
24
|
+
|
|
25
|
+
resolveId(id) {
|
|
26
|
+
if (id === VIRTUAL_ID) {
|
|
27
|
+
return RESOLVED_VIRTUAL_ID;
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
load(id) {
|
|
32
|
+
if (id === RESOLVED_VIRTUAL_ID) {
|
|
33
|
+
return { code: ROUTES_STUB };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
|
|
3
|
+
const VUE_APP_STUB = `
|
|
4
|
+
export const setup = () => {};
|
|
5
|
+
`;
|
|
6
|
+
|
|
7
|
+
// Different versions of @astrojs/vue use different virtual module names
|
|
8
|
+
const VIRTUAL_IDS = ['virtual:astro:vue-app', 'virtual:@astrojs/vue/app'];
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Provides fallback resolution for @astrojs/vue's virtual module
|
|
12
|
+
* in Storybook's SSR Vite server.
|
|
13
|
+
*
|
|
14
|
+
* @astrojs/vue's server.js imports a virtual module to get a setup function
|
|
15
|
+
* for configuring the Vue app instance. The virtual module name varies by version:
|
|
16
|
+
* - v6.0.0-beta.1: "virtual:astro:vue-app"
|
|
17
|
+
* - Later versions: "virtual:@astrojs/vue/app"
|
|
18
|
+
*
|
|
19
|
+
* The Vite plugin that normally creates this virtual module may not run in
|
|
20
|
+
* Storybook's SSR context, so this plugin stubs it with a no-op setup function
|
|
21
|
+
* (the default behavior when no appEntrypoint is configured).
|
|
22
|
+
*/
|
|
23
|
+
export function vitePluginAstroVueFallback(): Plugin {
|
|
24
|
+
const resolvedIds = new Map(VIRTUAL_IDS.map((id) => [id, '\0' + id]));
|
|
25
|
+
const resolvedIdSet = new Set(resolvedIds.values());
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
name: 'storybook-astro-vue-fallback',
|
|
29
|
+
// Must run before vite:resolve to intercept virtual modules
|
|
30
|
+
// before Vite tries to resolve them as Node package imports
|
|
31
|
+
enforce: 'pre',
|
|
32
|
+
|
|
33
|
+
resolveId(id) {
|
|
34
|
+
const resolved = resolvedIds.get(id);
|
|
35
|
+
|
|
36
|
+
if (resolved) {
|
|
37
|
+
return resolved;
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
load(id) {
|
|
42
|
+
if (resolvedIdSet.has(id)) {
|
|
43
|
+
return { code: VUE_APP_STUB };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -1,29 +1,60 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
1
2
|
import { fileURLToPath } from 'node:url';
|
|
2
3
|
import { createServer, type PluginOption, type ViteDevServer } from 'vite';
|
|
3
4
|
import type { RenderRequestMessage, RenderResponseMessage } from '@storybook-astro/renderer/types';
|
|
4
5
|
import type { FrameworkOptions } from './types.ts';
|
|
5
6
|
import type { Integration } from './integrations/index.ts';
|
|
7
|
+
import { importAstroConfig } from './importAstroConfig.ts';
|
|
6
8
|
import { viteAstroContainerRenderersPlugin } from './viteAstroContainerRenderersPlugin.ts';
|
|
7
9
|
import { vitePluginAstroFontsFallback } from './vitePluginAstroFontsFallback.ts';
|
|
10
|
+
import { vitePluginAstroVueFallback } from './vitePluginAstroVueFallback.ts';
|
|
11
|
+
import { vitePluginAstroRoutesFallback } from './vitePluginAstroRoutesFallback.ts';
|
|
12
|
+
import { ssrLoadModuleWithFsFallback } from './lib/ssr-load-module-with-fs-fallback.ts';
|
|
13
|
+
import { resolveRulesConfigFilePath } from './rules-options.ts';
|
|
8
14
|
|
|
9
15
|
export async function vitePluginStorybookAstroMiddleware(options: FrameworkOptions) {
|
|
10
16
|
// The internal Vite server is created lazily inside configureServer (dev-only).
|
|
11
17
|
// During builds, configureServer never fires, so no server is created.
|
|
12
18
|
let viteServer: ViteDevServer | null = null;
|
|
13
19
|
|
|
20
|
+
const resolveFrom = options.resolveFrom ?? process.cwd();
|
|
21
|
+
|
|
14
22
|
const vitePlugin = {
|
|
15
23
|
name: 'storybook-astro-middleware-plugin',
|
|
16
24
|
async configureServer(server) {
|
|
17
|
-
viteServer = await createViteServer(options.integrations);
|
|
25
|
+
viteServer = await createViteServer(options.integrations, resolveFrom);
|
|
26
|
+
const storyRulesConfigFilePath = resolveRulesConfigFilePath(options.storyRules, resolveFrom);
|
|
18
27
|
|
|
19
28
|
const filePath = fileURLToPath(new URL('./middleware', import.meta.url));
|
|
20
29
|
const middleware = await viteServer.ssrLoadModule(filePath, {
|
|
21
30
|
fixStacktrace: true
|
|
22
31
|
});
|
|
23
|
-
|
|
32
|
+
|
|
33
|
+
const createHandler = () => middleware.handlerFactory(options.integrations ?? [], {
|
|
34
|
+
mode: 'development',
|
|
35
|
+
sanitization: options.sanitization,
|
|
36
|
+
rulesConfigFilePath: storyRulesConfigFilePath,
|
|
37
|
+
resolveRulesConfigModule: () =>
|
|
38
|
+
loadRulesConfigModule(viteServer!, storyRulesConfigFilePath),
|
|
39
|
+
loadModule: (id: string) =>
|
|
40
|
+
ssrLoadModuleWithFsFallback(viteServer!, id, {
|
|
41
|
+
fixStacktrace: true
|
|
42
|
+
})
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
let handlerPromise = createHandler();
|
|
46
|
+
|
|
47
|
+
const resetHandler = () => {
|
|
48
|
+
handlerPromise = createHandler();
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
server.watcher.on('add', resetHandler);
|
|
52
|
+
server.watcher.on('change', resetHandler);
|
|
53
|
+
server.watcher.on('unlink', resetHandler);
|
|
24
54
|
|
|
25
55
|
server.ws.on('astro:render:request', async (data: RenderRequestMessage['data']) => {
|
|
26
56
|
try {
|
|
57
|
+
const handler = await handlerPromise;
|
|
27
58
|
const html = await handler(data);
|
|
28
59
|
|
|
29
60
|
server.ws.send('astro:render:response', {
|
|
@@ -57,8 +88,8 @@ export async function vitePluginStorybookAstroMiddleware(options: FrameworkOptio
|
|
|
57
88
|
server.middlewares.use('/_image', (req, res, next) => {
|
|
58
89
|
if (!viteServer) {
|
|
59
90
|
next();
|
|
60
|
-
|
|
61
|
-
return;
|
|
91
|
+
|
|
92
|
+
return;
|
|
62
93
|
}
|
|
63
94
|
// Forward the request to the Astro vite server
|
|
64
95
|
viteServer.middlewares.handle(req, res, (err) => {
|
|
@@ -74,11 +105,11 @@ return;
|
|
|
74
105
|
// The extracted CSS plugins from Astro's internal Vite server cause Vue SFC
|
|
75
106
|
// <style> blocks to be double-processed (once by these plugins, once by
|
|
76
107
|
// Storybook's built-in CSS plugins), resulting in PostCSS errors.
|
|
77
|
-
//
|
|
108
|
+
//
|
|
78
109
|
// Solution: Don't extract Astro's CSS plugins. Storybook's built-in CSS
|
|
79
110
|
// plugins handle both Vue styles AND Astro style sub-modules (which are
|
|
80
111
|
// standard CSS imports like `Component.astro?astro&type=style&index=0&lang.css`).
|
|
81
|
-
//
|
|
112
|
+
//
|
|
82
113
|
// The Astro internal server's CSS plugins are only needed for SSR rendering
|
|
83
114
|
// within that server - they don't need to be shared with Storybook's server.
|
|
84
115
|
return {
|
|
@@ -91,16 +122,17 @@ return;
|
|
|
91
122
|
};
|
|
92
123
|
}
|
|
93
124
|
|
|
94
|
-
export async function createViteServer(integrations: Integration[]) {
|
|
95
|
-
const { getViteConfig } = await
|
|
125
|
+
export async function createViteServer(integrations: Integration[], resolveFrom = process.cwd()) {
|
|
126
|
+
const { getViteConfig } = await importAstroConfig(resolveFrom);
|
|
96
127
|
const safeIntegrations = integrations ?? [];
|
|
128
|
+
const projectAstroResolutionPlugin = createProjectAstroResolutionPlugin(resolveFrom);
|
|
97
129
|
|
|
98
130
|
const config = await getViteConfig(
|
|
99
|
-
{},
|
|
131
|
+
{ root: resolveFrom },
|
|
100
132
|
{
|
|
101
133
|
configFile: false,
|
|
102
134
|
integrations: await Promise.all(
|
|
103
|
-
safeIntegrations.map((integration) => integration.loadIntegration())
|
|
135
|
+
safeIntegrations.map((integration) => integration.loadIntegration(resolveFrom))
|
|
104
136
|
)
|
|
105
137
|
}
|
|
106
138
|
)({ mode: 'development', command: 'serve' });
|
|
@@ -109,9 +141,13 @@ export async function createViteServer(integrations: Integration[]) {
|
|
|
109
141
|
configFile: false,
|
|
110
142
|
...config,
|
|
111
143
|
plugins: [
|
|
144
|
+
projectAstroResolutionPlugin,
|
|
145
|
+
// Fallbacks must come first to intercept before Astro's plugins
|
|
146
|
+
vitePluginAstroFontsFallback(),
|
|
147
|
+
vitePluginAstroVueFallback(),
|
|
148
|
+
vitePluginAstroRoutesFallback(),
|
|
112
149
|
...(config.plugins?.filter(Boolean) ?? []),
|
|
113
|
-
viteAstroContainerRenderersPlugin(safeIntegrations)
|
|
114
|
-
vitePluginAstroFontsFallback()
|
|
150
|
+
viteAstroContainerRenderersPlugin(safeIntegrations)
|
|
115
151
|
]
|
|
116
152
|
});
|
|
117
153
|
|
|
@@ -122,3 +158,43 @@ export async function createViteServer(integrations: Integration[]) {
|
|
|
122
158
|
|
|
123
159
|
return viteServer;
|
|
124
160
|
}
|
|
161
|
+
|
|
162
|
+
function createProjectAstroResolutionPlugin(resolveFrom: string): PluginOption {
|
|
163
|
+
const require = createRequire(import.meta.url);
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
name: 'storybook-astro:resolve-project-astro',
|
|
167
|
+
enforce: 'pre',
|
|
168
|
+
resolveId(id: string) {
|
|
169
|
+
if (id !== 'astro' && !id.startsWith('astro/')) {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
return require.resolve(id, {
|
|
175
|
+
paths: [resolveFrom]
|
|
176
|
+
});
|
|
177
|
+
} catch {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
} satisfies PluginOption;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async function loadRulesConfigModule(viteServer: ViteDevServer, configFilePath?: string) {
|
|
185
|
+
if (!configFilePath) {
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
try {
|
|
190
|
+
return await ssrLoadModuleWithFsFallback(viteServer, configFilePath, {
|
|
191
|
+
fixStacktrace: true
|
|
192
|
+
});
|
|
193
|
+
} catch (error) {
|
|
194
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
195
|
+
|
|
196
|
+
throw new Error(
|
|
197
|
+
`Unable to load framework.options.storyRules config module at ${configFilePath}: ${reason}`
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -1,30 +1,20 @@
|
|
|
1
1
|
import type { Integration } from './integrations/index.ts';
|
|
2
|
+
import { createVirtualModulePlugin } from './vite/createVirtualModulePlugin.ts';
|
|
2
3
|
|
|
3
4
|
export function viteStorybookRendererFallbackPlugin(integrations: Integration[]) {
|
|
4
5
|
const safeIntegrations = integrations ?? [];
|
|
5
|
-
const name = 'storybook-renderer-fallback';
|
|
6
|
-
const virtualModuleId = `virtual:${name}`;
|
|
7
|
-
const resolvedVirtualModuleId = `\0${virtualModuleId}`;
|
|
8
6
|
|
|
9
|
-
return {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return safeIntegrations
|
|
21
|
-
.filter((integration) => integration.storybookEntryPreview)
|
|
22
|
-
.map(
|
|
23
|
-
(integration) =>
|
|
24
|
-
`export * as ${integration.name} from '${integration.storybookEntryPreview}';`
|
|
25
|
-
)
|
|
26
|
-
.join('\n');
|
|
27
|
-
}
|
|
7
|
+
return createVirtualModulePlugin({
|
|
8
|
+
pluginName: 'storybook-renderer-fallback',
|
|
9
|
+
virtualModuleId: 'virtual:storybook-renderer-fallback',
|
|
10
|
+
load() {
|
|
11
|
+
return safeIntegrations
|
|
12
|
+
.filter((integration) => integration.storybookEntryPreview)
|
|
13
|
+
.map(
|
|
14
|
+
(integration) =>
|
|
15
|
+
`export * as ${integration.name} from '${integration.storybookEntryPreview}';`
|
|
16
|
+
)
|
|
17
|
+
.join('\n');
|
|
28
18
|
}
|
|
29
|
-
};
|
|
19
|
+
});
|
|
30
20
|
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { defineConfig as defineVitestConfig } from 'vitest/config';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import type { InlineConfig, PluginOption } from 'vite';
|
|
4
|
+
import type { Integration } from '../integrations/base.ts';
|
|
5
|
+
import { importAstroConfig } from '../importAstroConfig.ts';
|
|
6
|
+
import { vitePluginAstroComponentMarker } from '../vitePluginAstroComponentMarker.ts';
|
|
7
|
+
import { registerTestingIntegrationsForRoot } from '../testing/integration-config.ts';
|
|
8
|
+
import { cjsInteropPlugin, vitestPatchForSolidJs } from './vite-plugins.ts';
|
|
9
|
+
|
|
10
|
+
// Type definition omits 'test' to allow Vitest-specific config options
|
|
11
|
+
// Vite 8 type definitions conflict with Vitest config when used in monorepo
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Vitest config requires any type for test option
|
|
13
|
+
export type TestingDefineConfig = Omit<InlineConfig, 'plugins' | 'test'> & {
|
|
14
|
+
integrations?: Integration[];
|
|
15
|
+
plugins?: PluginOption[];
|
|
16
|
+
astroConfigFile?: false | string;
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
|
+
test?: any;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function normalizeGlobalSetup(globalSetup: string | string[] | undefined, value: string) {
|
|
22
|
+
// Inject our setup without clobbering any user-provided global setup hooks.
|
|
23
|
+
if (!globalSetup) {
|
|
24
|
+
return [value];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (Array.isArray(globalSetup)) {
|
|
28
|
+
if (globalSetup.includes(value)) {
|
|
29
|
+
return globalSetup;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return [...globalSetup, value];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (globalSetup === value) {
|
|
36
|
+
return [globalSetup];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return [globalSetup, value];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function defineConfig(options: TestingDefineConfig) {
|
|
43
|
+
const {
|
|
44
|
+
integrations = [],
|
|
45
|
+
plugins = [],
|
|
46
|
+
root = process.cwd(),
|
|
47
|
+
mode = 'test',
|
|
48
|
+
astroConfigFile = false,
|
|
49
|
+
...rest
|
|
50
|
+
} = options;
|
|
51
|
+
|
|
52
|
+
registerTestingIntegrationsForRoot(root, integrations);
|
|
53
|
+
|
|
54
|
+
const globalSetupFilePath = fileURLToPath(new URL('./global-setup.ts', import.meta.url));
|
|
55
|
+
const testConfig = {
|
|
56
|
+
...rest.test,
|
|
57
|
+
globalSetup: normalizeGlobalSetup(rest.test?.globalSetup, globalSetupFilePath)
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Cast to any to work around Vite 8 type conflicts in monorepo environments
|
|
61
|
+
// where multiple Vite versions exist in node_modules
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Type conflict with Vite 8 in monorepo
|
|
63
|
+
const vitestConfig = defineVitestConfig({
|
|
64
|
+
...rest,
|
|
65
|
+
root,
|
|
66
|
+
mode,
|
|
67
|
+
test: testConfig,
|
|
68
|
+
plugins: [
|
|
69
|
+
cjsInteropPlugin(),
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
71
|
+
vitePluginAstroComponentMarker() as any,
|
|
72
|
+
...plugins
|
|
73
|
+
]
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
75
|
+
} as any);
|
|
76
|
+
|
|
77
|
+
const astroConfigFactoryPromise = Promise
|
|
78
|
+
.all([
|
|
79
|
+
importAstroConfig(root),
|
|
80
|
+
Promise.all(integrations.map((integration) => integration.loadIntegration(root)))
|
|
81
|
+
])
|
|
82
|
+
.then(([astroConfigModule, resolvedIntegrations]) =>
|
|
83
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
84
|
+
astroConfigModule.getViteConfig(vitestConfig as any, {
|
|
85
|
+
configFile: astroConfigFile,
|
|
86
|
+
integrations: [...resolvedIntegrations, vitestPatchForSolidJs()]
|
|
87
|
+
})
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
return async ({ mode: viteMode, command }: { mode: string; command: 'build' | 'serve' }) => {
|
|
91
|
+
const astroConfigFactory = await astroConfigFactoryPromise;
|
|
92
|
+
|
|
93
|
+
return astroConfigFactory({ mode: viteMode, command });
|
|
94
|
+
};
|
|
95
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TESTING_RENDERER_DAEMON_URL_ENV,
|
|
3
|
+
startTestingRendererDaemon
|
|
4
|
+
} from '../testing/renderer-daemon.ts';
|
|
5
|
+
|
|
6
|
+
export default async function globalSetup() {
|
|
7
|
+
const daemon = await startTestingRendererDaemon();
|
|
8
|
+
|
|
9
|
+
// Workers discover the shared renderer via env instead of creating their own SSR stack.
|
|
10
|
+
process.env[TESTING_RENDERER_DAEMON_URL_ENV] = daemon.url;
|
|
11
|
+
|
|
12
|
+
return async () => {
|
|
13
|
+
await daemon.close();
|
|
14
|
+
delete process.env[TESTING_RENDERER_DAEMON_URL_ENV];
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import type { AstroIntegration } from 'astro';
|
|
4
|
+
import type { Plugin } from 'vite';
|
|
5
|
+
|
|
6
|
+
type ResolveConfig = { resolve?: { alias?: Record<string, string> | Array<{ find: string | RegExp; replacement: string }> } };
|
|
7
|
+
|
|
8
|
+
function findPackageDir(pkgName: string): string | null {
|
|
9
|
+
let dir = process.cwd();
|
|
10
|
+
|
|
11
|
+
while (true) {
|
|
12
|
+
const candidate = join(dir, 'node_modules', pkgName);
|
|
13
|
+
|
|
14
|
+
if (existsSync(join(candidate, 'package.json'))) {
|
|
15
|
+
return candidate;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const parent = dirname(dir);
|
|
19
|
+
|
|
20
|
+
if (parent === dir) {
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
dir = parent;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function vitestPatchForSolidJs(): AstroIntegration {
|
|
31
|
+
return {
|
|
32
|
+
name: 'fix-solid',
|
|
33
|
+
hooks: {
|
|
34
|
+
'astro:config:done': ({ config }) => {
|
|
35
|
+
const solidPlugin = config.vite.plugins?.find(
|
|
36
|
+
(plugin) => plugin && 'name' in plugin && plugin.name === 'solid'
|
|
37
|
+
) as Plugin | undefined;
|
|
38
|
+
|
|
39
|
+
if (!solidPlugin) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const originalConfigEnvironment = solidPlugin.configEnvironment;
|
|
44
|
+
|
|
45
|
+
if (typeof originalConfigEnvironment !== 'function') {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Use bracket notation to avoid type assignment issues
|
|
50
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
51
|
+
(solidPlugin as any).configEnvironment = async (name: unknown, resolvedConfig: unknown, opts: unknown): Promise<void> => {
|
|
52
|
+
await (originalConfigEnvironment as (name: unknown, config: unknown, opts: unknown) => Promise<void>)(name, resolvedConfig, opts);
|
|
53
|
+
|
|
54
|
+
const config = resolvedConfig as ResolveConfig;
|
|
55
|
+
|
|
56
|
+
config.resolve ??= {};
|
|
57
|
+
const alias = config.resolve.alias;
|
|
58
|
+
const replacement = 'solid-js/web/dist/web.js';
|
|
59
|
+
|
|
60
|
+
if (Array.isArray(alias)) {
|
|
61
|
+
const hasAlias = alias.some((entry) => {
|
|
62
|
+
if (!entry || typeof entry !== 'object' || !('find' in entry)) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return entry.find === 'solid-js/web' || String(entry.find) === '/^solid-js\\/web$/';
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (!hasAlias) {
|
|
70
|
+
alias.unshift({ find: /^solid-js\/web$/, replacement });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
config.resolve!.alias = {
|
|
77
|
+
...(alias ?? {}),
|
|
78
|
+
'solid-js/web': replacement
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function cjsInteropPlugin(): Plugin {
|
|
87
|
+
return {
|
|
88
|
+
name: 'cjs-esm-interop',
|
|
89
|
+
enforce: 'pre',
|
|
90
|
+
resolveId(id) {
|
|
91
|
+
if (id.startsWith('.') || id.startsWith('/') || id.startsWith('\0') || id.includes('node_modules')) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const parts = id.split('/');
|
|
96
|
+
const pkgName = id.startsWith('@') ? parts.slice(0, 2).join('/') : parts[0];
|
|
97
|
+
const subpath = parts.slice(pkgName.split('/').length).join('/');
|
|
98
|
+
|
|
99
|
+
if (subpath && !['server-renderer', 'server', 'client'].includes(subpath)) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
const nmDir = findPackageDir(pkgName);
|
|
105
|
+
|
|
106
|
+
if (!nmDir) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const pkgJsonPath = join(nmDir, 'package.json');
|
|
111
|
+
|
|
112
|
+
if (!existsSync(pkgJsonPath)) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
|
|
117
|
+
|
|
118
|
+
const exportKey = subpath ? `./${subpath}` : '.';
|
|
119
|
+
const exportEntry = pkgJson.exports?.[exportKey];
|
|
120
|
+
|
|
121
|
+
if (exportEntry) {
|
|
122
|
+
const importEntry = exportEntry.import;
|
|
123
|
+
|
|
124
|
+
if (importEntry) {
|
|
125
|
+
const esmPath =
|
|
126
|
+
typeof importEntry === 'string'
|
|
127
|
+
? importEntry
|
|
128
|
+
: importEntry.default || importEntry.node;
|
|
129
|
+
|
|
130
|
+
if (esmPath) {
|
|
131
|
+
const resolved = join(nmDir, esmPath);
|
|
132
|
+
|
|
133
|
+
if (existsSync(resolved)) {
|
|
134
|
+
return resolved;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (!subpath && pkgJson.module) {
|
|
141
|
+
const resolved = join(nmDir, pkgJson.module);
|
|
142
|
+
|
|
143
|
+
if (existsSync(resolved)) {
|
|
144
|
+
return resolved;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
} catch {
|
|
148
|
+
// Ignore resolution errors
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
transform(code, id) {
|
|
152
|
+
if (!id.includes('node_modules')) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (id.startsWith('\0')) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (/\bexport\s+(default|const|let|var|function|class|\{|\*)/.test(code)) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (!code.includes('module.exports') && !code.includes('exports.')) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const dirPath = id.substring(0, id.lastIndexOf('/'));
|
|
169
|
+
const fileName = id;
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
code: [
|
|
173
|
+
'import { createRequire as __createRequire } from "module";',
|
|
174
|
+
`var __require = __createRequire("file://${dirPath}/");`,
|
|
175
|
+
'var module = { exports: {} };',
|
|
176
|
+
'var exports = module.exports;',
|
|
177
|
+
'function require(id) { return __require(id); }',
|
|
178
|
+
`var __dirname = ${JSON.stringify(dirPath)};`,
|
|
179
|
+
`var __filename = ${JSON.stringify(fileName)};`,
|
|
180
|
+
code,
|
|
181
|
+
'export default module.exports;'
|
|
182
|
+
].join('\n'),
|
|
183
|
+
map: null
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
}
|