Skip to content

Commit

Permalink
Support solidjs libraries (#5059)
Browse files Browse the repository at this point in the history
  • Loading branch information
bluwy authored Oct 12, 2022
1 parent 5cab045 commit f7fcdfe
Show file tree
Hide file tree
Showing 10 changed files with 104 additions and 8 deletions.
5 changes: 5 additions & 0 deletions .changeset/ten-phones-drop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/solid-js': minor
---

Auto ssr.noExternal solidjs dependencies
5 changes: 5 additions & 0 deletions .changeset/thin-parents-breathe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Support strict dependency install for libraries with JSX
1 change: 1 addition & 0 deletions packages/astro/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
7 changes: 6 additions & 1 deletion packages/astro/src/vite-plugin-jsx/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ interface TransformJSXOptions {
mode: string;
renderer: AstroRenderer;
ssr: boolean;
root: URL;
}

async function transformJSX({
Expand All @@ -106,12 +107,13 @@ async function transformJSX({
id,
ssr,
renderer,
root,
}: TransformJSXOptions): Promise<TransformResult> {
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,
Expand Down Expand Up @@ -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) {
Expand All @@ -220,6 +223,7 @@ export default function jsx({ settings, logging }: AstroPluginJSXOptions): Plugi
renderer: defaultJSXRendererEntry[1],
mode,
ssr,
root: settings.config.root,
});
}

Expand Down Expand Up @@ -286,6 +290,7 @@ https://docs.astro.build/en/core-concepts/framework-components/#installing-integ
renderer: selectedJsxRenderer,
mode,
ssr,
root: settings.config.root,
});
},
};
Expand Down
13 changes: 10 additions & 3 deletions packages/astro/src/vite-plugin-jsx/tag.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -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<PluginObj> {
const astroServerPath = fileURLToPath(
await importMetaResolve('astro/server/index.js', root.toString())
);
return {
visitor: {
Program: {
Expand All @@ -29,7 +36,7 @@ export default function tagExportsWithRenderer({
t.identifier('__astro_tag_component__')
),
],
t.stringLiteral('astro/server/index.js')
t.stringLiteral(astroServerPath)
)
);
},
Expand Down
1 change: 1 addition & 0 deletions packages/astro/test/fixtures/solid-component/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"@astrojs/solid-js": "workspace:*",
"@solidjs/router": "^0.5.0",
"astro": "workspace:*",
"solid-js": "^1.5.6"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
---
import Hello from '../components/Hello.jsx';
import WithNewlines from '../components/WithNewlines.jsx';
import { Router } from "@solidjs/router";
---
<html>
<head><title>Solid</title></head>
<body>
<div>
<Hello client:load />
<WithNewlines client:load />
<Router />
</div>
</body>
</html>
57 changes: 57 additions & 0 deletions packages/integrations/solid/src/dependencies.ts
Original file line number Diff line number Diff line change
@@ -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<string, any>) {
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<string[]>((acc, dep, i) => {
if (pkgs[i] && pkgs[i].exports && containsSolidField(pkgs[i].exports)) acc.push(dep);
return acc;
}, []);
}
9 changes: 5 additions & 4 deletions packages/integrations/solid/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { AstroIntegration, AstroRenderer } from 'astro';
import { getSolidDeps } from './dependencies.js';

function getRenderer(): AstroRenderer {
return {
Expand All @@ -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'];
Expand All @@ -45,7 +46,7 @@ function getViteConfiguration(isDev: boolean) {
ssr: {
external: ['babel-preset-solid'],
target: 'node',
noExternal: ['solid-js'],
noExternal: ['solid-js', ...getSolidDeps(root)],
},
};
}
Expand All @@ -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) });
},
},
};
Expand Down
12 changes: 12 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit f7fcdfe

Please sign in to comment.