diff --git a/.changeset/ten-phones-drop.md b/.changeset/ten-phones-drop.md new file mode 100644 index 000000000000..4233ec43300a --- /dev/null +++ b/.changeset/ten-phones-drop.md @@ -0,0 +1,5 @@ +--- +'@astrojs/solid-js': minor +--- + +Auto ssr.noExternal solidjs dependencies diff --git a/.changeset/thin-parents-breathe.md b/.changeset/thin-parents-breathe.md new file mode 100644 index 000000000000..2867ab3b62f7 --- /dev/null +++ b/.changeset/thin-parents-breathe.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Support strict dependency install for libraries with JSX diff --git a/packages/astro/package.json b/packages/astro/package.json index f135854b196f..252a169f6ef3 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -126,6 +126,7 @@ "gray-matter": "^4.0.3", "html-entities": "^2.3.3", "html-escaper": "^3.0.3", + "import-meta-resolve": "^2.1.0", "kleur": "^4.1.4", "magic-string": "^0.25.9", "mime": "^3.0.0", diff --git a/packages/astro/src/vite-plugin-jsx/index.ts b/packages/astro/src/vite-plugin-jsx/index.ts index 5c84ec512332..357e441f6c7a 100644 --- a/packages/astro/src/vite-plugin-jsx/index.ts +++ b/packages/astro/src/vite-plugin-jsx/index.ts @@ -98,6 +98,7 @@ interface TransformJSXOptions { mode: string; renderer: AstroRenderer; ssr: boolean; + root: URL; } async function transformJSX({ @@ -106,12 +107,13 @@ async function transformJSX({ id, ssr, renderer, + root, }: TransformJSXOptions): Promise { const { jsxTransformOptions } = renderer; const options = await jsxTransformOptions!({ mode, ssr }); const plugins = [...(options.plugins || [])]; if (ssr) { - plugins.push(tagExportsPlugin({ rendererName: renderer.name })); + plugins.push(await tagExportsPlugin({ rendererName: renderer.name, root })); } const result = await babel.transformAsync(code, { presets: options.presets, @@ -204,6 +206,7 @@ export default function jsx({ settings, logging }: AstroPluginJSXOptions): Plugi renderer: astroJSXRenderer, mode, ssr, + root: settings.config.root, }); } if (defaultJSXRendererEntry && jsxRenderersIntegrationOnly.size === 1) { @@ -220,6 +223,7 @@ export default function jsx({ settings, logging }: AstroPluginJSXOptions): Plugi renderer: defaultJSXRendererEntry[1], mode, ssr, + root: settings.config.root, }); } @@ -286,6 +290,7 @@ https://docs.astro.build/en/core-concepts/framework-components/#installing-integ renderer: selectedJsxRenderer, mode, ssr, + root: settings.config.root, }); }, }; diff --git a/packages/astro/src/vite-plugin-jsx/tag.ts b/packages/astro/src/vite-plugin-jsx/tag.ts index b6ba7c302263..1b957b58d283 100644 --- a/packages/astro/src/vite-plugin-jsx/tag.ts +++ b/packages/astro/src/vite-plugin-jsx/tag.ts @@ -1,3 +1,5 @@ +import { fileURLToPath } from 'url'; +import { resolve as importMetaResolve } from 'import-meta-resolve'; import type { PluginObj } from '@babel/core'; import * as t from '@babel/types'; @@ -9,11 +11,16 @@ import * as t from '@babel/types'; * This plugin crawls each export in the file and "tags" each export with a given `rendererName`. * This allows us to automatically match a component to a renderer and skip the usual `check()` calls. */ -export default function tagExportsWithRenderer({ +export default async function tagExportsWithRenderer({ rendererName, + root, }: { rendererName: string; -}): PluginObj { + root: URL; +}): Promise { + const astroServerPath = fileURLToPath( + await importMetaResolve('astro/server/index.js', root.toString()) + ); return { visitor: { Program: { @@ -29,7 +36,7 @@ export default function tagExportsWithRenderer({ t.identifier('__astro_tag_component__') ), ], - t.stringLiteral('astro/server/index.js') + t.stringLiteral(astroServerPath) ) ); }, diff --git a/packages/astro/test/fixtures/solid-component/package.json b/packages/astro/test/fixtures/solid-component/package.json index 6863b1978762..cf557f06d0d7 100644 --- a/packages/astro/test/fixtures/solid-component/package.json +++ b/packages/astro/test/fixtures/solid-component/package.json @@ -4,6 +4,7 @@ "private": true, "dependencies": { "@astrojs/solid-js": "workspace:*", + "@solidjs/router": "^0.5.0", "astro": "workspace:*", "solid-js": "^1.5.6" } diff --git a/packages/astro/test/fixtures/solid-component/src/pages/index.astro b/packages/astro/test/fixtures/solid-component/src/pages/index.astro index 1f7522cedcb4..06c48e18a68d 100644 --- a/packages/astro/test/fixtures/solid-component/src/pages/index.astro +++ b/packages/astro/test/fixtures/solid-component/src/pages/index.astro @@ -1,6 +1,7 @@ --- import Hello from '../components/Hello.jsx'; import WithNewlines from '../components/WithNewlines.jsx'; +import { Router } from "@solidjs/router"; --- Solid @@ -8,6 +9,7 @@ import WithNewlines from '../components/WithNewlines.jsx';
+
diff --git a/packages/integrations/solid/src/dependencies.ts b/packages/integrations/solid/src/dependencies.ts new file mode 100644 index 000000000000..bdcfb4c8f562 --- /dev/null +++ b/packages/integrations/solid/src/dependencies.ts @@ -0,0 +1,57 @@ +// This file is a fork of vite-plugin-solid. +// Original: https://github.com/solidjs/vite-plugin-solid/blob/03130c8a0a2ceaab9a07e16f1e1df832b996e1b8/src/index.ts#L251-L297 +// License: MIT (https://github.com/solidjs/vite-plugin-solid/blob/03130c8a0a2ceaab9a07e16f1e1df832b996e1b8/package.json#L38) + +import fs from 'fs'; +import path from 'path'; +import { createRequire } from 'module'; +import { fileURLToPath } from 'url'; + +function containsSolidField(fields: Record) { + const keys = Object.keys(fields); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + if (key === 'solid') return true; + if (typeof fields[key] === 'object' && containsSolidField(fields[key])) return true; + } + return false; +} + +export function getSolidDeps(root: URL) { + const pkgPath = path.join(fileURLToPath(root), 'package.json'); + if (!fs.existsSync(pkgPath)) { + // eslint-disable-next-line no-console + console.log('No package.json found at project root'); + return []; + } + const require = createRequire(pkgPath); + const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')); + const deps = [...Object.keys(pkg.dependencies || {}), ...Object.keys(pkg.devDependencies || {})]; + const pkgs = deps.map((dep) => { + try { + return require(`${dep}/package.json`); + } catch { + try { + let dir = path.dirname(require.resolve(dep)); + while (dir) { + const subPkgPath = path.join(dir, 'package.json'); + if (fs.existsSync(subPkgPath)) { + const subPkg = JSON.parse(fs.readFileSync(subPkgPath, 'utf-8')); + if (subPkg && subPkg.name === dep) return subPkg; + } + const parent = path.dirname(dir); + if (parent === dir) { + break; + } + dir = parent; + } + } catch (e) { + console.warn("Couldn't find package.json for", dep, e); + } + } + }); + return deps.reduce((acc, dep, i) => { + if (pkgs[i] && pkgs[i].exports && containsSolidField(pkgs[i].exports)) acc.push(dep); + return acc; + }, []); +} diff --git a/packages/integrations/solid/src/index.ts b/packages/integrations/solid/src/index.ts index 20df48297b3f..5241141a92c1 100644 --- a/packages/integrations/solid/src/index.ts +++ b/packages/integrations/solid/src/index.ts @@ -1,4 +1,5 @@ import type { AstroIntegration, AstroRenderer } from 'astro'; +import { getSolidDeps } from './dependencies.js'; function getRenderer(): AstroRenderer { return { @@ -23,7 +24,7 @@ function getRenderer(): AstroRenderer { }; } -function getViteConfiguration(isDev: boolean) { +function getViteConfiguration(isDev: boolean, root: URL) { // https://github.com/solidjs/vite-plugin-solid // We inject the dev mode only if the user explicitely wants it or if we are in dev (serve) mode const nestedDeps = ['solid-js', 'solid-js/web', 'solid-js/store', 'solid-js/html', 'solid-js/h']; @@ -45,7 +46,7 @@ function getViteConfiguration(isDev: boolean) { ssr: { external: ['babel-preset-solid'], target: 'node', - noExternal: ['solid-js'], + noExternal: ['solid-js', ...getSolidDeps(root)], }, }; } @@ -54,9 +55,9 @@ export default function (): AstroIntegration { return { name: '@astrojs/solid-js', hooks: { - 'astro:config:setup': ({ command, addRenderer, updateConfig }) => { + 'astro:config:setup': ({ command, addRenderer, updateConfig, config }) => { addRenderer(getRenderer()); - updateConfig({ vite: getViteConfiguration(command === 'dev') }); + updateConfig({ vite: getViteConfiguration(command === 'dev', config.root) }); }, }, }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6bc130ed5dae..67501c8385be 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -415,6 +415,7 @@ importers: gray-matter: ^4.0.3 html-entities: ^2.3.3 html-escaper: ^3.0.3 + import-meta-resolve: ^2.1.0 kleur: ^4.1.4 magic-string: ^0.25.9 mime: ^3.0.0 @@ -484,6 +485,7 @@ importers: gray-matter: 4.0.3 html-entities: 2.3.3 html-escaper: 3.0.3 + import-meta-resolve: 2.1.0 kleur: 4.1.5 magic-string: 0.25.9 mime: 3.0.0 @@ -2117,10 +2119,12 @@ importers: packages/astro/test/fixtures/solid-component: specifiers: '@astrojs/solid-js': workspace:* + '@solidjs/router': ^0.5.0 astro: workspace:* solid-js: ^1.5.6 dependencies: '@astrojs/solid-js': link:../../../../integrations/solid + '@solidjs/router': 0.5.0_solid-js@1.5.7 astro: link:../../.. solid-js: 1.5.7 @@ -9282,6 +9286,14 @@ packages: picomatch: 2.3.1 dev: false + /@solidjs/router/0.5.0_solid-js@1.5.7: + resolution: {integrity: sha512-rNR07l21tWWDVmCbaapggB89rEX7jlM2XChpTLqEGEnj46LzVZ8zgvjcF6NNKScByAlLpoQUkVIjB2KHpcMi+w==} + peerDependencies: + solid-js: ^1.5.3 + dependencies: + solid-js: 1.5.7 + dev: false + /@spectrum-icons/ui/3.3.3_jshkqybnlidashxgfmbz45rdaq: resolution: {integrity: sha512-KGPRQidTviIZWI1VHQCqNZ1dgPuOjXVTjr0ELih4y7+fV2r39qeukiKCs0AzePs7VUj04sZGRL+ZezKcvk/98Q==} peerDependencies: