summaryrefslogtreecommitdiff
path: root/packages/integrations/solid
diff options
context:
space:
mode:
Diffstat (limited to 'packages/integrations/solid')
-rw-r--r--packages/integrations/solid/client.js14
-rw-r--r--packages/integrations/solid/package.json41
-rw-r--r--packages/integrations/solid/server.js28
-rw-r--r--packages/integrations/solid/src/index.ts57
-rw-r--r--packages/integrations/solid/static-html.js12
-rw-r--r--packages/integrations/solid/tsconfig.json10
6 files changed, 162 insertions, 0 deletions
diff --git a/packages/integrations/solid/client.js b/packages/integrations/solid/client.js
new file mode 100644
index 000000000..b67b3acdb
--- /dev/null
+++ b/packages/integrations/solid/client.js
@@ -0,0 +1,14 @@
+import { hydrate, createComponent } from 'solid-js/web';
+
+export default (element) => (Component, props, childHTML) => {
+ let children;
+ if (childHTML != null) {
+ children = document.createElement('astro-fragment');
+ children.innerHTML = childHTML;
+ }
+
+ // Using Solid's `hydrate` method ensures that a `root` is created
+ // in order to properly handle reactivity. It also handles
+ // components that are not native HTML elements.
+ hydrate(() => createComponent(Component, { ...props, children }), element);
+};
diff --git a/packages/integrations/solid/package.json b/packages/integrations/solid/package.json
new file mode 100644
index 000000000..5f332631d
--- /dev/null
+++ b/packages/integrations/solid/package.json
@@ -0,0 +1,41 @@
+{
+ "name": "@astrojs/solid-js",
+ "version": "0.0.1",
+ "description": "Use Solid components within Astro",
+ "type": "module",
+ "types": "./dist/index.d.ts",
+ "author": "withastro",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/withastro/astro.git",
+ "directory": "packages/integrations/solid"
+ },
+ "bugs": "https://github.com/withastro/astro/issues",
+ "homepage": "https://astro.build",
+ "exports": {
+ ".": "./dist/index.js",
+ "./*": "./*",
+ "./client.js": "./client.js",
+ "./server.js": "./server.js",
+ "./package.json": "./package.json"
+ },
+ "scripts": {
+ "build": "astro-scripts build \"src/**/*.ts\" && tsc",
+ "dev": "astro-scripts dev \"src/**/*.ts\""
+ },
+ "dependencies": {
+ "babel-preset-solid": "^1.3.6"
+ },
+ "devDependencies": {
+ "astro": "workspace:*",
+ "astro-scripts": "workspace:*",
+ "solid-js": "^1.3.6"
+ },
+ "peerDependencies": {
+ "solid-js": "^1.3.6"
+ },
+ "engines": {
+ "node": "^14.15.0 || >=16.0.0"
+ }
+}
diff --git a/packages/integrations/solid/server.js b/packages/integrations/solid/server.js
new file mode 100644
index 000000000..d32d60a64
--- /dev/null
+++ b/packages/integrations/solid/server.js
@@ -0,0 +1,28 @@
+import { renderToString, ssr, createComponent } from 'solid-js/web';
+
+function check(Component, props, children) {
+ if (typeof Component !== 'function') return false;
+ try {
+ const { html } = renderToStaticMarkup(Component, props, children);
+ return typeof html === 'string';
+ } catch (err) {
+ return false;
+ }
+}
+
+function renderToStaticMarkup(Component, props, children) {
+ const html = renderToString(() =>
+ createComponent(Component, {
+ ...props,
+ // In Solid SSR mode, `ssr` creates the expected structure for `children`.
+ // In Solid client mode, `ssr` is just a stub.
+ children: children != null ? ssr(`<astro-fragment>${children}</astro-fragment>`) : children,
+ })
+ );
+ return { html: html + `<script>window._$HY||(_$HY={events:[],completed:new WeakSet,r:{}})</script>` };
+}
+
+export default {
+ check,
+ renderToStaticMarkup,
+};
diff --git a/packages/integrations/solid/src/index.ts b/packages/integrations/solid/src/index.ts
new file mode 100644
index 000000000..1205c6d09
--- /dev/null
+++ b/packages/integrations/solid/src/index.ts
@@ -0,0 +1,57 @@
+import type { AstroIntegration, AstroRenderer } from 'astro';
+
+function getRenderer(): AstroRenderer {
+ return {
+ name: '@astrojs/solid-js',
+ clientEntrypoint: '@astrojs/solid-js/client.js',
+ serverEntrypoint: '@astrojs/solid-js/server.js',
+ jsxImportSource: 'solid-js',
+ jsxTransformOptions: async ({ ssr }) => {
+ // @ts-expect-error types not found
+ const [{ default: solid }] = await Promise.all([import('babel-preset-solid')]);
+ const options = {
+ presets: [solid({}, { generate: ssr ? 'ssr' : 'dom', hydratable: true })],
+ plugins: [],
+ };
+
+ return options;
+ },
+ };
+}
+
+function getViteConfiguration(isDev: boolean) {
+ // 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'];
+ return {
+ /**
+ * We only need esbuild on .ts or .js files.
+ * .tsx & .jsx files are handled by us
+ */
+ esbuild: { include: /\.ts$/ },
+ resolve: {
+ conditions: ['solid', ...(isDev ? ['development'] : [])],
+ dedupe: nestedDeps,
+ alias: [{ find: /^solid-refresh$/, replacement: '/@solid-refresh' }],
+ },
+ optimizeDeps: {
+ include: nestedDeps,
+ exclude: ['@astrojs/solid-js/server.js'],
+ },
+ ssr: {
+ external: ['babel-preset-solid'],
+ },
+ };
+}
+
+export default function (): AstroIntegration {
+ return {
+ name: '@astrojs/solid-js',
+ hooks: {
+ 'astro:config:setup': ({ command, addRenderer, updateConfig }) => {
+ addRenderer(getRenderer());
+ updateConfig({ vite: getViteConfiguration(command === 'dev') });
+ },
+ },
+ };
+}
diff --git a/packages/integrations/solid/static-html.js b/packages/integrations/solid/static-html.js
new file mode 100644
index 000000000..9f969eac9
--- /dev/null
+++ b/packages/integrations/solid/static-html.js
@@ -0,0 +1,12 @@
+import { ssr } from 'solid-js/web';
+
+/**
+ * Astro passes `children` as a string of HTML, so we need
+ * a wrapper `astro-fragment` to render that content as VNodes.
+ */
+const StaticHtml = ({ innerHTML }) => {
+ if (!innerHTML) return null;
+ return ssr(`<astro-fragment>${innerHTML}</astro-fragment>`);
+};
+
+export default StaticHtml;
diff --git a/packages/integrations/solid/tsconfig.json b/packages/integrations/solid/tsconfig.json
new file mode 100644
index 000000000..44baf375c
--- /dev/null
+++ b/packages/integrations/solid/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "extends": "../../../tsconfig.base.json",
+ "include": ["src"],
+ "compilerOptions": {
+ "allowJs": true,
+ "module": "ES2020",
+ "outDir": "./dist",
+ "target": "ES2020"
+ }
+}