Files
orca/config/scripts/dev-web-client-freshness.mjs
T
OrcaWinandOrcaWin f151bd965b perf(dev): avoid pairing web rebuilds after test file changes (#29838)
* perf(dev): keep pairing web bundle after test file changes

* fix(dev): detect web input replacement within one timestamp tick

* test(dev): use real files for web replacement coverage

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-16 14:35:06 -07:00

96 lines
2.6 KiB
JavaScript

import { createHash } from 'node:crypto'
import { readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'
const SNAPSHOT_FILENAME = '.orca-dev-web-inputs.json'
const INPUT_PATHS = ['vite.web.config.ts', 'src/renderer', 'src/shared', 'src/preload/api-types.ts']
const TEST_SOURCE_PATTERN = /\.(?:test|spec)\.[cm]?[jt]sx?$/
function collectInputs(targetPath, relativePath, files) {
let stat
try {
stat = statSync(targetPath, { bigint: true })
} catch {
return 0
}
const mtimeMs = Number(stat.mtimeNs) / 1e6
if (!stat.isDirectory()) {
files.push([
relativePath,
...[stat.mtimeNs, stat.ctimeNs, stat.dev, stat.ino, stat.size].map(String)
])
return mtimeMs
}
let latest = mtimeMs
for (const entry of readdirSync(targetPath, { withFileTypes: true }).sort((a, b) =>
a.name < b.name ? -1 : a.name > b.name ? 1 : 0
)) {
if (
entry.name === 'node_modules' ||
entry.name.startsWith('.') ||
(!entry.isDirectory() && TEST_SOURCE_PATTERN.test(entry.name))
) {
continue
}
latest = Math.max(
latest,
collectInputs(join(targetPath, entry.name), join(relativePath, entry.name), files)
)
}
return latest
}
export function captureDevWebClientInputs(root) {
const files = []
const latestMtime = Math.max(
...INPUT_PATHS.map((input) => collectInputs(join(root, input), input, files))
)
return {
signature: createHash('sha256').update(JSON.stringify(files)).digest('hex'),
latestMtime
}
}
function outputIdentity(root) {
const stat = statSync(join(root, 'out', 'web', 'web-index.html'))
return [stat.mtimeMs, stat.size]
}
export function recordDevWebClientInputs(root, inputs) {
try {
writeFileSync(
join(root, 'out', 'web', SNAPSHOT_FILENAME),
JSON.stringify({ version: 1, output: outputIdentity(root), signature: inputs.signature })
)
} catch {
// A missing cache must never prevent dev startup.
}
}
export function isDevWebClientFresh(root) {
let output
try {
output = outputIdentity(root)
} catch {
return false
}
if (output[0] === 0) {
return false
}
const inputs = captureDevWebClientInputs(root)
try {
const previous = JSON.parse(readFileSync(join(root, 'out', 'web', SNAPSHOT_FILENAME), 'utf8'))
if (previous.version === 1 && JSON.stringify(previous.output) === JSON.stringify(output)) {
return previous.signature === inputs.signature
}
} catch {
// Existing standalone and projected builds can be adopted without rebuilding.
}
if (inputs.latestMtime > output[0]) {
return false
}
recordDevWebClientInputs(root, inputs)
return true
}