diff --git a/.changeset/empty-dragons-grow.md b/.changeset/empty-dragons-grow.md
new file mode 100644
index 0000000..becfea4
--- /dev/null
+++ b/.changeset/empty-dragons-grow.md
@@ -0,0 +1,5 @@
+---
+'vite-plugin-solid': patch
+---
+
+allow vite 8 in peerDeps
diff --git a/.changeset/pre.json b/.changeset/pre.json
new file mode 100644
index 0000000..f50bf96
--- /dev/null
+++ b/.changeset/pre.json
@@ -0,0 +1,11 @@
+{
+ "mode": "pre",
+ "tag": "next",
+ "initialVersions": {
+ "vite-plugin-solid": "3.0.0-next.2"
+ },
+ "changesets": [
+ "empty-dragons-grow",
+ "six-lions-joke"
+ ]
+}
diff --git a/.changeset/six-lions-joke.md b/.changeset/six-lions-joke.md
new file mode 100644
index 0000000..9d90f97
--- /dev/null
+++ b/.changeset/six-lions-joke.md
@@ -0,0 +1,5 @@
+---
+'vite-plugin-solid': patch
+---
+
+fix: preserve jsx for rolldown dep scan
diff --git a/.changeset/true-boxes-arrive.md b/.changeset/true-boxes-arrive.md
new file mode 100644
index 0000000..4ef6501
--- /dev/null
+++ b/.changeset/true-boxes-arrive.md
@@ -0,0 +1,5 @@
+---
+'vite-plugin-solid': patch
+---
+
+Bump solid-refresh to latest version
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 0cc2505..0d93ef9 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -4,9 +4,15 @@ on:
push:
branches:
- main
+ - next
concurrency: ${{ github.workflow }}-${{ github.ref }}
+permissions:
+ contents: write
+ pull-requests: write
+ id-token: write
+
jobs:
release:
name: Release
@@ -20,10 +26,11 @@ jobs:
# https://github.com/pnpm/pnpm/issues/8953
version: 9.15.3
- - name: Setup Node.js 20.x
- uses: actions/setup-node@v3
+ - name: Setup Node.js 24.x
+ uses: actions/setup-node@v4
with:
- node-version: 20.x
+ node-version: 24.x
+ registry-url: "https://registry.npmjs.org"
- name: Install Dependencies
run: pnpm i --frozen-lockfile
diff --git a/CHANGELOG.md b/CHANGELOG.md
index edcf8b7..987521e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,17 @@
# Changelog
+## 3.0.0-next.4
+
+### Patch Changes
+
+- 5ffcb1e: fix: preserve jsx for rolldown dep scan
+
+## 3.0.0-next.3
+
+### Patch Changes
+
+- 824f72c: allow vite 8 in peerDeps
+
## 2.11.10
### Patch Changes
diff --git a/examples/ssr/package.json b/examples/ssr/package.json
new file mode 100644
index 0000000..a74842f
--- /dev/null
+++ b/examples/ssr/package.json
@@ -0,0 +1,20 @@
+{
+ "name": "example-ssr",
+ "private": "true",
+ "type": "module",
+ "scripts": {
+ "dev": "node server.js",
+ "build": "npm run build:client && npm run build:server",
+ "build:client": "vite build --outDir dist/client",
+ "build:server": "vite build --ssr src/entry-server.tsx --outDir dist/server",
+ "serve": "NODE_ENV=production node server.js"
+ },
+ "devDependencies": {
+ "vite": "^7.0.0",
+ "vite-plugin-solid": "workspace:*"
+ },
+ "dependencies": {
+ "solid-js": "catalog:",
+ "@solidjs/web": "catalog:"
+ }
+}
diff --git a/examples/ssr/server.js b/examples/ssr/server.js
new file mode 100644
index 0000000..be06f6b
--- /dev/null
+++ b/examples/ssr/server.js
@@ -0,0 +1,113 @@
+import { createServer as createHttpServer } from 'node:http';
+import { readFileSync } from 'node:fs';
+import { fileURLToPath } from 'node:url';
+import path from 'node:path';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const isProduction = process.env.NODE_ENV === 'production';
+const port = process.env.PORT || 3000;
+
+function getClientEntry() {
+ if (!isProduction) return '/src/entry-client.tsx';
+ const manifest = JSON.parse(
+ readFileSync(path.resolve(__dirname, 'dist/client/.vite/manifest.json'), 'utf-8'),
+ );
+ const entry = manifest['src/entry-client.tsx'];
+ return '/' + entry.file;
+}
+
+async function start() {
+ let vite;
+
+ const server = createHttpServer(async (req, res) => {
+ const url = req.url || '/';
+
+ try {
+ if (!isProduction) {
+ const handled = await new Promise((resolve) => {
+ vite.middlewares(req, res, () => resolve(false));
+ });
+ if (handled !== false) return;
+ if (!req.headers.accept?.includes('text/html')) {
+ if (!res.headersSent) {
+ res.statusCode = 404;
+ res.end();
+ }
+ return;
+ }
+ }
+
+ if (isProduction && url !== '/' && !url.startsWith('/api')) {
+ const filePath = path.resolve(__dirname, 'dist/client' + url);
+ try {
+ const content = readFileSync(filePath);
+ const ext = path.extname(url);
+ const types = {
+ '.js': 'application/javascript',
+ '.css': 'text/css',
+ '.html': 'text/html',
+ '.json': 'application/json',
+ };
+ res.setHeader('Content-Type', types[ext] || 'application/octet-stream');
+ res.end(content);
+ return;
+ } catch {
+ // Fall through to SSR
+ }
+ }
+
+ let render;
+ if (!isProduction) {
+ ({ render } = await vite.ssrLoadModule('/src/entry-server.tsx'));
+ } else {
+ ({ render } = await import('./dist/server/entry-server.js'));
+ }
+
+ const stream = render();
+ const clientEntry = getClientEntry();
+
+ res.setHeader('Content-Type', 'text/html');
+ res.write('');
+
+ stream.pipe({
+ write(chunk) {
+ let html = chunk;
+ // Inject Vite client script for HMR in dev mode
+ if (!isProduction && html.includes('')) {
+ html = html.replace(
+ '',
+ '',
+ );
+ }
+ // Replace dev entry path with production asset path
+ if (isProduction && html.includes('/src/entry-client.tsx')) {
+ html = html.replace('/src/entry-client.tsx', clientEntry);
+ }
+ return res.write(html);
+ },
+ end() {
+ res.end();
+ },
+ });
+ } catch (e) {
+ if (!isProduction) vite.ssrFixStacktrace(e);
+ console.error(e);
+ res.statusCode = 500;
+ res.end(e.message);
+ }
+ });
+
+ if (!isProduction) {
+ const { createServer } = await import('vite');
+ vite = await createServer({
+ server: { middlewareMode: true, hmr: { server } },
+ appType: 'custom',
+ });
+ }
+
+ server.listen(port, () => {
+ console.log(`Server running at http://localhost:${port}`);
+ });
+}
+
+start();
diff --git a/examples/ssr/src/App.tsx b/examples/ssr/src/App.tsx
new file mode 100644
index 0000000..3b5a513
--- /dev/null
+++ b/examples/ssr/src/App.tsx
@@ -0,0 +1,41 @@
+/* @refresh reload */
+import { createSignal, lazy, Show, Loading } from 'solid-js';
+import { HydrationScript } from '@solidjs/web';
+import Home from './routes/Home';
+
+const About = lazy(() => import('./routes/About'));
+
+export default function App() {
+ const [page, setPage] = createSignal('home');
+
+ return (
+
+
+
+
+ Solid SSR Document Example
+
+
+
+
+
+ }>
+ Loading...}>
+
+
+
+
+
+
+
+ );
+}
diff --git a/examples/ssr/src/entry-client.tsx b/examples/ssr/src/entry-client.tsx
new file mode 100644
index 0000000..1076aee
--- /dev/null
+++ b/examples/ssr/src/entry-client.tsx
@@ -0,0 +1,4 @@
+import { hydrate } from '@solidjs/web';
+import App from './App';
+
+hydrate(() => , document);
diff --git a/examples/ssr/src/entry-server.tsx b/examples/ssr/src/entry-server.tsx
new file mode 100644
index 0000000..b483e4e
--- /dev/null
+++ b/examples/ssr/src/entry-server.tsx
@@ -0,0 +1,7 @@
+import { renderToStream } from '@solidjs/web';
+import manifest from 'virtual:solid-manifest';
+import App from './App';
+
+export function render() {
+ return renderToStream(() => , { manifest });
+}
diff --git a/examples/ssr/src/routes/About.tsx b/examples/ssr/src/routes/About.tsx
new file mode 100644
index 0000000..563edc6
--- /dev/null
+++ b/examples/ssr/src/routes/About.tsx
@@ -0,0 +1,8 @@
+export default function About() {
+ return (
+
+
About
+
This page is lazy-loaded to test moduleUrl injection.
+
+ );
+}
diff --git a/examples/ssr/src/routes/Home.tsx b/examples/ssr/src/routes/Home.tsx
new file mode 100644
index 0000000..8688083
--- /dev/null
+++ b/examples/ssr/src/routes/Home.tsx
@@ -0,0 +1,8 @@
+export default function Home() {
+ return (
+
+
Home
+
Welcome to the Solid SSR example with full document rendering.
+
+ );
+}
diff --git a/examples/ssr/tsconfig.json b/examples/ssr/tsconfig.json
new file mode 100644
index 0000000..c976835
--- /dev/null
+++ b/examples/ssr/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "module": "ESNext",
+ "allowSyntheticDefaultImports": true,
+ "esModuleInterop": true,
+ "resolveJsonModule": true,
+ "moduleResolution": "node",
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "jsxImportSource": "solid-js",
+ "types": ["vite/client", "vite-plugin-solid/virtual-solid-manifest"]
+ }
+}
diff --git a/examples/ssr/vite.config.ts b/examples/ssr/vite.config.ts
new file mode 100644
index 0000000..1c9f1a7
--- /dev/null
+++ b/examples/ssr/vite.config.ts
@@ -0,0 +1,12 @@
+import { defineConfig } from 'vite';
+import solidPlugin from 'vite-plugin-solid';
+
+export default defineConfig({
+ plugins: [solidPlugin({ ssr: true })],
+ build: {
+ manifest: true,
+ rollupOptions: {
+ input: 'src/entry-client.tsx',
+ },
+ },
+});
diff --git a/examples/vite-3/package.json b/examples/vite-3/package.json
index 9476d26..e3b1569 100644
--- a/examples/vite-3/package.json
+++ b/examples/vite-3/package.json
@@ -12,6 +12,7 @@
"vite-plugin-solid": "workspace:*"
},
"dependencies": {
- "solid-js": "catalog:"
+ "solid-js": "catalog:",
+ "@solidjs/web": "catalog:"
}
}
diff --git a/examples/vite-3/src/App.tsx b/examples/vite-3/src/App.tsx
index 16c1f7f..86a85c7 100644
--- a/examples/vite-3/src/App.tsx
+++ b/examples/vite-3/src/App.tsx
@@ -1,15 +1,13 @@
-import { onCleanup, onMount } from "solid-js";
+import { onSettled } from "solid-js";
import { CounterProvider, useCounter } from "./CounterContext";
const title = 'Count';
function Count() {
const counter = useCounter();
- onMount(() => {
+ onSettled(() => {
console.log('Mounted Count');
- });
- onCleanup(() => {
- console.log('Unmounted Count');
+ return () => console.log('Unmounted Count');
});
return (
{title}: {counter.value()}
@@ -18,11 +16,9 @@ function Count() {
function Increment() {
const counter = useCounter();
- onMount(() => {
+ onSettled(() => {
console.log('Mounted Increment');
- });
- onCleanup(() => {
- console.log('Unmounted Increment');
+ return () => console.log('Unmounted Increment');
});
return (