mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
121 lines
4.7 KiB
JavaScript
121 lines
4.7 KiB
JavaScript
import fs from 'node:fs'
|
|
import path from 'node:path'
|
|
import process from 'node:process'
|
|
|
|
export const RENDERER_BUILD_DIR = path.join('out', 'renderer')
|
|
|
|
// Entry/modulepreload closure is fetched/evaluated before first paint and costs every launch regardless of feature use.
|
|
export function readRendererBootGraph(rendererDir) {
|
|
const html = fs.readFileSync(path.join(rendererDir, 'index.html'), 'utf8')
|
|
const entries = [...html.matchAll(/<script[^>]+type="module"[^>]+src="([^"]+)"/g)].map(
|
|
(match) => match[1]
|
|
)
|
|
const preloads = [...html.matchAll(/<link[^>]+rel="modulepreload"[^>]+href="([^"]+)"/g)].map(
|
|
(match) => match[1]
|
|
)
|
|
const files = [...new Set([...entries, ...preloads])]
|
|
const chunks = files.map((href) => {
|
|
const file = path.join(rendererDir, href.replace(/^\.?\//, ''))
|
|
return { href, file, bytes: fs.statSync(file).size }
|
|
})
|
|
return {
|
|
chunks: chunks.sort((left, right) => right.bytes - left.bytes),
|
|
totalBytes: chunks.reduce((total, chunk) => total + chunk.bytes, 0)
|
|
}
|
|
}
|
|
|
|
// Match unique payload literals: these features load after first paint/from routes, so a boot-graph hit exposes a regressed static import.
|
|
export function bootGraphForbiddenPayloads(root = process.cwd()) {
|
|
return [
|
|
{ label: '@xterm/addon-webgl', signature: 'WebGL2 not supported' },
|
|
{
|
|
label: '@xterm/addon-image',
|
|
signature: 'invalid storageLimit, should be at least 0.5 MB and not exceed 1G'
|
|
},
|
|
{ label: 'i18n/locales/en.json', signature: prunedAwayEnglishSignature(root) }
|
|
// Exclude zod: other boot-path modules still need it. Keep emojibase-data static: async catalog loading can persist submitted :wink: literally.
|
|
]
|
|
}
|
|
|
|
// Derive full-catalog-only marker from longest English value pruned at runtime so copy edits cannot stale the guard.
|
|
export function prunedAwayEnglishSignature(root = process.cwd()) {
|
|
const flatten = (value, prefix = '', out = new Map()) => {
|
|
if (typeof value === 'string') {
|
|
out.set(prefix, value)
|
|
return out
|
|
}
|
|
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
return out
|
|
}
|
|
for (const [key, child] of Object.entries(value)) {
|
|
flatten(child, prefix ? `${prefix}.${key}` : key, out)
|
|
}
|
|
return out
|
|
}
|
|
const read = (relative) =>
|
|
flatten(JSON.parse(fs.readFileSync(path.join(root, ...relative.split('/')), 'utf8')))
|
|
const full = read('src/renderer/src/i18n/locales/en.json')
|
|
const runtimeRequired = read('src/renderer/src/i18n/en-runtime-required.json')
|
|
let longest = ''
|
|
for (const [key, value] of full) {
|
|
if (!runtimeRequired.has(key) && value.length > longest.length) {
|
|
longest = value
|
|
}
|
|
}
|
|
if (longest.length < 40) {
|
|
throw new Error(
|
|
'No sufficiently distinctive pruned English value to probe the boot graph with.'
|
|
)
|
|
}
|
|
return longest
|
|
}
|
|
|
|
export function findForbiddenBootPayloads(rendererDir, payloads) {
|
|
const { chunks } = readRendererBootGraph(rendererDir)
|
|
const violations = []
|
|
for (const chunk of chunks) {
|
|
const code = fs.readFileSync(chunk.file, 'utf8')
|
|
for (const payload of payloads) {
|
|
if (code.includes(payload.signature)) {
|
|
violations.push({ label: payload.label, chunk: path.basename(chunk.file) })
|
|
}
|
|
}
|
|
}
|
|
return violations
|
|
}
|
|
|
|
// V8 keeps a whole script as UTF-16 when any char is above U+00FF, doubling that boot chunk's memory.
|
|
export function findTwoByteBootChunks(rendererDir) {
|
|
return readRendererBootGraph(rendererDir)
|
|
.chunks.filter((chunk) => /[\u0100-\uffff]/.test(fs.readFileSync(chunk.file, 'utf8')))
|
|
.map((chunk) => path.basename(chunk.file))
|
|
}
|
|
|
|
export function verifyRendererBootGraph(root = process.cwd()) {
|
|
const rendererDir = path.join(root, RENDERER_BUILD_DIR)
|
|
const { chunks, totalBytes } = readRendererBootGraph(rendererDir)
|
|
const violations = findForbiddenBootPayloads(rendererDir, bootGraphForbiddenPayloads(root))
|
|
console.log(
|
|
`Renderer boot graph: ${chunks.length} chunks, ${(totalBytes / 1024).toFixed(1)} KB minified.`
|
|
)
|
|
const twoByteChunks = findTwoByteBootChunks(rendererDir)
|
|
if (twoByteChunks.length > 0) {
|
|
console.error('Boot chunks contain chars above U+00FF that output.charset cannot escape:')
|
|
for (const chunk of twoByteChunks) {
|
|
console.error(` ${chunk}`)
|
|
}
|
|
console.error('Escape them in the source (e.g. \\u2014 inside String.raw regex sources).')
|
|
}
|
|
if (violations.length > 0) {
|
|
console.error('Payloads that must stay off the renderer boot graph are preloaded again:')
|
|
for (const violation of violations) {
|
|
console.error(` ${violation.label} -> ${violation.chunk}`)
|
|
}
|
|
console.error('')
|
|
console.error(
|
|
'Load them after first paint (see primeTerminalWebglAddon) or from a route chunk.'
|
|
)
|
|
}
|
|
return twoByteChunks.length > 0 || violations.length > 0 ? 1 : 0
|
|
}
|