fix(desktop): keep react-router in one runtime chunk

This commit is contained in:
notkisk 2026-08-09 22:25:01 +01:00 committed by ethernet
parent e400dca96e
commit 481ccdafb7
3 changed files with 56 additions and 3 deletions

View File

@ -11,10 +11,12 @@
// inherits it. It fails loud and early instead of shipping a broken bundle.
// See issues #39484 (renderer blank page) and #41327 / #39472 (dashboard 404).
import { existsSync, statSync, readdirSync } from "fs"
import { existsSync, readFileSync, statSync, readdirSync } from "fs"
import { join, resolve } from "path"
import { isMain } from "./utils.mjs"
const ROUTER_CONTEXT_ERROR = "may be used only in the context of a"
// Pure check — returns { ok: true } or { ok: false, error: "..." }.
// Kept side-effect-free so it can be unit tested without spawning a process.
export function checkDistBuilt(distDir) {
@ -41,6 +43,17 @@ export function checkDistBuilt(distDir) {
return { ok: false, error: `dist/assets has no built JS bundle (expected vite output under ${assetsDir})` }
}
const routerContextAssets = readdirSync(assetsDir)
.filter(name => name.endsWith(".js"))
.filter(name => readFileSync(join(assetsDir, name), "utf8").includes(ROUTER_CONTEXT_ERROR))
if (routerContextAssets.length > 1) {
return {
ok: false,
error: `react-router context invariant found in multiple JS assets: ${routerContextAssets.join(", ")}`
}
}
return { ok: true }
}

View File

@ -14,6 +14,14 @@ function makeDist(extra) {
return { tempRoot, distDir }
}
function writeRouterAsset(distDir, name) {
fs.writeFileSync(
path.join(distDir, 'assets', name),
`throw new Error('may be used only in the context of a <Router>')`,
'utf8',
)
}
test('checkDistBuilt passes when index.html + an assets JS bundle exist', () => {
const { tempRoot, distDir } = makeDist(d => {
fs.writeFileSync(path.join(d, 'index.html'), '<!doctype html><div id=root></div>', 'utf8')
@ -82,3 +90,35 @@ test('checkDistBuilt fails when assets/ has no JS bundle', () => {
fs.rmSync(tempRoot, { recursive: true, force: true })
}
})
test('checkDistBuilt passes when the Router context invariant is in one JS asset', () => {
const { tempRoot, distDir } = makeDist(d => {
fs.writeFileSync(path.join(d, 'index.html'), '<!doctype html>', 'utf8')
fs.mkdirSync(path.join(d, 'assets'))
writeRouterAsset(d, 'vendor-react-abc123.js')
fs.writeFileSync(path.join(d, 'assets', 'command-def456.js'), 'console.log(1)', 'utf8')
})
try {
assert.deepEqual(checkDistBuilt(distDir), { ok: true })
} finally {
fs.rmSync(tempRoot, { recursive: true, force: true })
}
})
test('checkDistBuilt fails when the Router context invariant is in multiple JS assets', () => {
const { tempRoot, distDir } = makeDist(d => {
fs.writeFileSync(path.join(d, 'index.html'), '<!doctype html>', 'utf8')
fs.mkdirSync(path.join(d, 'assets'))
writeRouterAsset(d, 'vendor-react-abc123.js')
writeRouterAsset(d, 'command-def456.js')
})
try {
const result = checkDistBuilt(distDir)
assert.equal(result.ok, false)
assert.match(result.error, /react-router context invariant found in multiple JS assets/)
assert.match(result.error, /vendor-react-abc123\.js/)
assert.match(result.error, /command-def456\.js/)
} finally {
fs.rmSync(tempRoot, { recursive: true, force: true })
}
})

View File

@ -112,7 +112,7 @@ export default defineConfig(({ command }) => ({
// the heavy chunk, and the entry then statically imports 19 MB of
// shiki just to reach react/hast utils — putting the heavy chunk
// right back on the boot path.
{ name: 'vendor-react', test: /node_modules[\\/](react|react-dom|scheduler)[\\/]/ },
{ name: 'vendor-react', test: /node_modules[\\/](react|react-dom|scheduler|react-router)[\\/]/ },
{
name: 'vendor-md',
test: /node_modules[\\/](property-information|hast-util-[^\\/]+|mdast-util-[^\\/]+|micromark[^\\/]*|unist-util-[^\\/]+|vfile[^\\/]*|unified|stringify-entities|space-separated-tokens|comma-separated-tokens|zwitch|html-void-elements|devlop|style-to-js|style-to-object|clsx)[\\/]/
@ -152,7 +152,7 @@ export default defineConfig(({ command }) => ({
'react/jsx-dev-runtime': path.resolve(__dirname, '../../node_modules/react/jsx-dev-runtime.js'),
'react/jsx-runtime': path.resolve(__dirname, '../../node_modules/react/jsx-runtime.js')
},
dedupe: ['react', 'react-dom']
dedupe: ['react', 'react-dom', 'react-router']
},
server: {
host: '127.0.0.1',