@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.
Files changed (92) hide show
  1. package/README.md +38 -0
  2. package/dist/base-IRZo3zgK.d.ts +23 -0
  3. package/dist/chunk-4SWPVM6R.js +96 -0
  4. package/dist/chunk-4SWPVM6R.js.map +1 -0
  5. package/dist/chunk-5EF25G5S.js +69 -0
  6. package/dist/chunk-5EF25G5S.js.map +1 -0
  7. package/dist/chunk-7GHEQUPV.js +439 -0
  8. package/dist/chunk-7GHEQUPV.js.map +1 -0
  9. package/dist/chunk-C5OH4VBR.js +492 -0
  10. package/dist/chunk-C5OH4VBR.js.map +1 -0
  11. package/dist/chunk-DNGQBPT7.js +15 -0
  12. package/dist/chunk-DNGQBPT7.js.map +1 -0
  13. package/dist/chunk-E4LB75JN.js +89 -0
  14. package/dist/chunk-E4LB75JN.js.map +1 -0
  15. package/dist/chunk-PJEDXZVN.js +240 -0
  16. package/dist/chunk-PJEDXZVN.js.map +1 -0
  17. package/dist/chunk-UK43WNEA.js +657 -0
  18. package/dist/chunk-UK43WNEA.js.map +1 -0
  19. package/dist/dist-HJOEPVRQ.js +15574 -0
  20. package/dist/dist-HJOEPVRQ.js.map +1 -0
  21. package/dist/index.d.ts +42 -0
  22. package/dist/index.js +13 -64
  23. package/dist/index.js.map +1 -1
  24. package/dist/integrations/index.d.ts +138 -0
  25. package/dist/integrations/index.js +8 -196
  26. package/dist/integrations/index.js.map +1 -1
  27. package/dist/middleware.d.ts +26 -0
  28. package/dist/middleware.js +179 -0
  29. package/dist/middleware.js.map +1 -0
  30. package/dist/portable-stories-BvdaQigq.d.ts +83 -0
  31. package/dist/preset.d.ts +14 -0
  32. package/dist/preset.js +5 -1
  33. package/dist/testing.d.ts +27 -0
  34. package/dist/testing.js +324 -15539
  35. package/dist/testing.js.map +1 -1
  36. package/dist/types-CHTsRtA7.d.ts +42 -0
  37. package/dist/viteStorybookAstroMiddlewarePlugin-NP2E52IC.js +11 -0
  38. package/dist/viteStorybookAstroMiddlewarePlugin-NP2E52IC.js.map +1 -0
  39. package/dist/vitest/index.d.ts +19 -0
  40. package/dist/vitest/index.js +229 -0
  41. package/dist/vitest/index.js.map +1 -0
  42. package/package.json +31 -17
  43. package/src/importAstroConfig.ts +11 -0
  44. package/src/index.ts +20 -6
  45. package/src/integrations/alpine.ts +5 -2
  46. package/src/integrations/base.ts +2 -2
  47. package/src/integrations/moduleResolver.ts +43 -0
  48. package/src/integrations/preact.ts +5 -2
  49. package/src/integrations/react.ts +5 -2
  50. package/src/integrations/solid.ts +5 -2
  51. package/src/integrations/svelte.ts +5 -2
  52. package/src/integrations/vue.ts +5 -2
  53. package/src/lib/sanitization.test.ts +232 -0
  54. package/src/lib/sanitization.ts +338 -0
  55. package/src/lib/ssr-load-module-with-fs-fallback.ts +29 -0
  56. package/src/middleware.test.ts +48 -0
  57. package/src/middleware.ts +204 -96
  58. package/src/module-mocks.ts +16 -0
  59. package/src/msw-helpers.ts +1 -0
  60. package/src/msw.ts +58 -0
  61. package/src/preset.ts +38 -3
  62. package/src/rules-options.test.ts +71 -0
  63. package/src/rules-options.ts +87 -0
  64. package/src/rules.test.ts +183 -0
  65. package/src/rules.ts +314 -0
  66. package/src/testing/astro-runtime.ts +219 -0
  67. package/src/testing/component-utils.ts +32 -0
  68. package/src/testing/index.ts +2 -0
  69. package/src/testing/integration-config.ts +121 -0
  70. package/src/testing/project-root.ts +185 -0
  71. package/src/testing/renderer-daemon.ts +269 -0
  72. package/src/testing/story-composition.ts +33 -0
  73. package/src/testing/types.ts +14 -0
  74. package/src/testing/working-directory.ts +28 -0
  75. package/src/testing.ts +1 -254
  76. package/src/types.ts +16 -4
  77. package/src/virtual.d.ts +2 -1
  78. package/src/vite/createVirtualModulePlugin.test.ts +80 -0
  79. package/src/vite/createVirtualModulePlugin.ts +25 -0
  80. package/src/viteAstroContainerRenderersPlugin.ts +60 -26
  81. package/src/vitePluginAstro.ts +12 -5
  82. package/src/vitePluginAstroBuildPrerender.ts +665 -204
  83. package/src/vitePluginAstroRoutesFallback.ts +37 -0
  84. package/src/vitePluginAstroVueFallback.ts +47 -0
  85. package/src/viteStorybookAstroMiddlewarePlugin.ts +88 -12
  86. package/src/viteStorybookRendererFallbackPlugin.ts +13 -23
  87. package/src/vitest/config.ts +95 -0
  88. package/src/vitest/global-setup.ts +16 -0
  89. package/src/vitest/index.ts +2 -0
  90. package/src/vitest/vite-plugins.ts +187 -0
  91. package/dist/chunk-KTGNRGDJ.js +0 -561
  92. package/dist/chunk-KTGNRGDJ.js.map +0 -1
@@ -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
- const handler = await middleware.handlerFactory(options.integrations ?? []);
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 import('astro/config');
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
- name,
11
-
12
- resolveId(id: string) {
13
- if (id === virtualModuleId) {
14
- return resolvedVirtualModuleId;
15
- }
16
- },
17
-
18
- load(id: string) {
19
- if (id === resolvedVirtualModuleId) {
20
- 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,2 @@
1
+ export { defineConfig, type TestingDefineConfig } from './config.ts';
2
+ export { cjsInteropPlugin, vitestPatchForSolidJs } from './vite-plugins.ts';
@@ -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
+ }