fix: key AppSheet render branches and self-heal never-mounted component vnodes
This commit is contained in:
+27
-2
@@ -1,9 +1,34 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import { defineConfig, type Plugin } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
|
||||
const GUARD_MARKER = '[dodo:vue-guard]'
|
||||
|
||||
// vuejs/core#5184/#8146: a stale component vnode that never mounted (component === null)
|
||||
// reaches processComponent -> updateComponent -> shouldUpdateComponent and crashes on
|
||||
// `component.emitsOptions`. Detect it here and self-heal with a clean remount instead,
|
||||
// logging the component pair so the next production occurrence names itself in console.
|
||||
function vueNullComponentGuard(): Plugin {
|
||||
return {
|
||||
name: 'dodo:vue-null-component-guard',
|
||||
enforce: 'pre',
|
||||
transform(code, id) {
|
||||
if (!id.includes('runtime-core.esm-bundler.js')) return null
|
||||
const header = code.indexOf('const processComponent = ')
|
||||
if (header < 0) throw new Error(`${GUARD_MARKER} processComponent not found in runtime-core — Vue internals changed, update the guard anchor`)
|
||||
const anchor = code.indexOf('if (n1 == null) {', header)
|
||||
if (anchor < 0 || anchor - header > 400) throw new Error(`${GUARD_MARKER} mount-branch anchor not found — update the guard anchor`)
|
||||
if (code.slice(header, anchor).includes(GUARD_MARKER)) return null
|
||||
const inject = `if (n1 != null && n1.component == null) {\n try { const nm = (x) => (x && (x.name || x.__name)) || (typeof x === "string" ? x : typeof x === "symbol" ? (x.description || "symbol") : "?"); console.error("${GUARD_MARKER} never-mounted component vnode — self-healing remount", { oldType: nm(n1.type), newType: nm(n2.type), oldKey: n1.key, newKey: n2.key, el: !!n1.el, oldChildren: Array.isArray(n1.children) ? n1.children.map((c) => nm(c.type)) : String(n1.children).slice(0, 60) }); } catch (e) {}\n n1 = null;\n }\n `
|
||||
return { code: code.slice(0, anchor) + inject + code.slice(anchor), map: null }
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [vue(), tailwindcss()],
|
||||
plugins: [vue(), tailwindcss(), vueNullComponentGuard()],
|
||||
// Serve vue through the normal transform pipeline so the guard applies in dev too.
|
||||
optimizeDeps: { exclude: ['vue', '@vue/runtime-dom', '@vue/runtime-core', '@vue/reactivity', '@vue/shared'] },
|
||||
server: { proxy: { '/api': 'http://localhost:8781', '/health': 'http://localhost:8781' } },
|
||||
test: { environment: 'jsdom', setupFiles: ['./vitest.setup.ts'] },
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user