Vite 8: dep scan JSX is only set for the client environment, so ssr scans (e.g. Cloudflare) fail on react/jsx-dev-runtime
维护者通常 1 天内回复
还没有人认领这个 Issue。
评估
- 难度
- 3/5
- 预计耗时
- 1-2 天
- 新手友好度
- 68/100
- Issue 类型
- 缺陷
- 描述清晰度
- 基本清楚
- 活跃度
- 活跃
- 技术栈
- typescript
- 领域
- build-system
调研方向
Start with vite.config.ts and run node scan-check.mjs to reproduce the client and ssr dependency scans. Locate the plugin's config and configEnvironment handling, then verify that both scans complete without the react/jsx-dev-runtime resolution failure while preserving the existing client behavior.
由索引模型根据 Issue 内容生成。
描述
Summary
On Vite 8, the plugin sets the dep scanner's JSX option (optimizeDeps.rolldownOptions.transform.jsx) only in the top-level config hook. Vite applies top-level optimizeDeps to the client environment only, so any other environment with dep discovery on still scans .tsx with Rolldown's default: React's automatic runtime. The scan then fails on an unresolvable react/jsx-dev-runtime import, and dev skips pre-bundling for that environment.
Server environments default to noDiscovery: true, so plain Node SSR never hits this. Frameworks that run SSR outside Node turn discovery back on. We hit it with @cloudflare/vite-plugin 1.60.1 (TanStack Start on Workers), and the repro below does the same with one config line.
This is separate from #262. #363 fixes the client environment but not this one: I ran the repro against #363's pkg.pr.new build, and the client scan passes while the ssr scan still fails. On next, the scanner JSX is still set only in the top-level optimizeDeps, and neither configEnvironment hook sets it.
Versions
vite 8.3.1, vite-plugin-solid 2.11.14 and the #363 build (https://pkg.pr.new/solidjs/solid-vite-plugin/vite-plugin-solid@363), solid-js 1.9.15, Node 24.18.0.
Reproduction
package.json
{
"name": "solid-vite8-ssr-scan-repro",
"private": true,
"type": "module",
"dependencies": { "solid-js": "1.9.15" },
"devDependencies": { "vite": "8.3.1", "vite-plugin-solid": "2.11.14" }
}
vite.config.ts
import { defineConfig } from 'vite'
import solid from 'vite-plugin-solid'
export default defineConfig({
plugins: [solid({ ssr: true })],
environments: {
// Server environments default to noDiscovery: true. Frameworks that run SSR
// outside Node turn it back on (@cloudflare/vite-plugin does); so does this.
ssr: { optimizeDeps: { noDiscovery: false, entries: ['src/entry-server.tsx'] } },
},
})
index.html
<!doctype html><html><body><div id="app"></div><script type="module" src="/src/entry-client.tsx"></script></body></html>
src/entry-client.tsx
import { render } from 'solid-js/web'
import { App } from './App'
render(() => <App />, document.getElementById('app')!)
src/entry-server.tsx
import { renderToString } from 'solid-js/web'
import { App } from './App'
export const render = () => renderToString(() => <App />)
src/App.tsx
import { Pictures } from './Pictures'
export const App = () => <Pictures />
src/Pictures.tsx (uses import.meta.glob, so the same repro also shows #262 on the client; src/pics/a.svg is any SVG)
const pics = import.meta.glob('./pics/*.svg', { eager: true, import: 'default' })
export const Pictures = () => <p>{Object.keys(pics).length} pictures</p>
Run vite and request /. The ssr scan logs:
(!) Failed to run dependency scan. Skipping dependency pre-bundling. Error: The following dependencies are imported but could not be resolved:
react/jsx-dev-runtime (imported by src/Pictures.tsx)
Are they installed?
scan-check.mjs: reports each environment's scan result deterministically
// Starts the dev server, waits for every environment's dep scan, and reports
// which environments' scans failed. Clears the optimizer cache so the scan runs.
import { rmSync } from 'node:fs'
import { createServer } from 'vite'
rmSync('node_modules/.vite', { recursive: true, force: true })
const server = await createServer({ server: { port: 0 }, logLevel: 'silent' })
const failed = {}
for (const [name, env] of Object.entries(server.environments)) {
env.logger = { ...env.logger, error(msg) {
if (msg.includes('Failed to run dependency scan')) failed[name] = msg.replace(/\x1b\[[0-9;]*m/g, '').split('\n').filter(Boolean).slice(0, 3).join(' ').replace(/\s+/g, ' ')
} }
}
await server.listen()
await fetch(server.resolvedUrls.local[0])
for (const [name, env] of Object.entries(server.environments)) {
const opt = env.depsOptimizer
if (!opt || opt.options.noDiscovery) { console.log(`${name}: discovery off, no scan`); continue }
for (let i = 0; i < 100 && !opt.scanProcessing; i++) await new Promise((r) => setTimeout(r, 50))
await opt.scanProcessing
console.log(`${name}: ${failed[name] ? 'SCAN FAILED — ' + failed[name] : 'scan ok'}`)
}
await server.close()
Results
node scan-check.mjs, three runs per row, same result every run:
| vite-plugin-solid | client scan | ssr scan |
|---|---|---|
| 2.11.14 | fails: PARSE_ERROR Unexpected JSX expression (#262) |
fails: react/jsx-dev-runtime unresolved |
| #363 build | ok | fails: react/jsx-dev-runtime unresolved |
| 2.11.14 + workaround below | ok | ok |
In our app, the skipped pre-bundling showed up in the browser as You appear to have multiple instances of Solid, useRouter must be used inside a <RouterProvider>, and uncaught errors on load (Cannot read properties of null (reading 'isServer'), and in another run _$HY is not defined). We have a single solid-js in the lockfile.
Cause
In Vite 8.3.1's config resolution, the client environment's defaults include optimizeDeps: config.optimizeDeps, and the defaults for other environments don't (getDefaultEnvironmentOptions plus the client-only override). So the top-level value the plugin returns from config never reaches ssr: resolving the config above gives environments.ssr.config.optimizeDeps.rolldownOptions.transform.jsx === undefined, while client gets the plugin's value.
Suggested fix
Set the scanner JSX in configEnvironment for every environment instead of (or as well as) top-level, e.g.
configEnvironment(name, config) {
config.optimizeDeps ??= {}
config.optimizeDeps.rolldownOptions ??= {}
config.optimizeDeps.rolldownOptions.transform ??= {}
config.optimizeDeps.rolldownOptions.transform.jsx ??= { runtime: 'classic' }
}
I tested this as a separate plugin in the repro. On top of the #363 build, both scans pass. On 2.11.14 alone, only ssr passes: the ??= keeps the top-level 'preserve' on client, which is #262. So it's #363's change plus this hook.
Workaround
Set it per environment in the app's config. It has to be per environment, because a top-level value is overridden by the plugin on client and never reaches ssr:
const scan = { rolldownOptions: { transform: { jsx: { runtime: 'classic' as const } } } }
export default defineConfig({
environments: {
client: { optimizeDeps: scan },
ssr: { optimizeDeps: scan },
},
})
- 主要语言
- TypeScript
- 星标
- 522
- 派生
- 72
- 平均合并
- 20 小时 1 分钟
- 30 天内合并 PR
- 31
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 没有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
solidjs/solid-vite-plugin 的其他 Issue
-
Test environment detection doesn't consider Vitest workspaces可能已有人在做 @carloitaben 于 44 天前认领。 未关闭
难度 2/5 1-3 小时 新手友好度 78/100
solidjs/solid-vite-plugin#205 · 1 条评论 · 2 个 reaction ·
维护者通常 1 天内回复
-
难度 4/5 3-5 天 新手友好度 65/100
solidjs/solid-vite-plugin#394 ·
维护者通常 1 天内回复
-
Catch-all route chunks are named `_...404_-<hash>.js`; the `..` trips path-traversal guards and breaks the lazy preload可能已有人在做 关联的 PR 仍在进行中或已合并。 未关闭
难度 3/5 1-2 天 新手友好度 68/100
solidjs/solid-vite-plugin#391 ·
维护者通常 1 天内回复
-
难度 4/5 3-5 天 新手友好度 55/100
solidjs/solid-vite-plugin#390 ·
维护者通常 1 天内回复
-
Start mode: generated entries render without the CSP nonce, and there is no per-request seam to supply one可能已有人在做 关联的 PR 仍在进行中或已合并。 未关闭
难度 5/5 一周以上 新手友好度 35/100
solidjs/solid-vite-plugin#388 · 1 条评论 ·
维护者通常 1 天内回复
查看 solidjs/solid-vite-plugin 的全部 Issue
相似的 Issue
-
area:docs bug triage:confirmed
难度 2/5 1-3 小时 新手友好度 74/100
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 65/100
anomalyco/models.dev#8862 · 1 条评论 ·
维护者通常 1 天内回复
-
fix(data-lake): wizard source step still previews the local slug, not the server-disambiguated one可能已有人在做 关联的 PR 仍在进行中或已合并。 未关闭data-lake
难度 2/5 1-3 小时 新手友好度 82/100
维护者通常 1 天内回复
-
ready-for-triage
难度 1/5 1 小时以内 新手友好度 88/100
konflux-ci/konflux-ui#1596 · 1 条评论 ·
维护者通常 1 天内回复
-
enhancement good first issue priority: low size: XS
难度 2/5 1-3 小时 新手友好度 82/100
维护者通常 1 天内回复