CI / typecheck + test + build (windows) (push) Waiting to run
fix(ime): actually ship @xterm/xterm 6.1.0-beta.304 (v1.0.21 built from stale node_modules with 6.0.0); add predist dependency-consistency gate (scripts/verify-deps.cjs) and rename-retry shim for the AV scan EPERM race
60 lines
2.6 KiB
JavaScript
60 lines
2.6 KiB
JavaScript
/* Dependency consistency gate: node_modules must match package-lock.json.
|
|
|
|
Usage: node scripts/verify-deps.cjs (runs first in `predist`)
|
|
|
|
Why it exists: v1.0.21 shipped an input-method fix that never reached the
|
|
build. @xterm/xterm had been pinned to 6.1.0-beta.304 in package.json, but
|
|
node_modules still held 6.0.0 from an older install — `npm install
|
|
--package-lock-only` (the last step of predist) rewrites only the lockfile,
|
|
so the lockfile agreed with package.json while the tree on disk stayed
|
|
stale, and the bundler took the stale tree. An install that never happened
|
|
is invisible to every other check, so it gets its own gate here.
|
|
|
|
Scope: version equality only, for every entry the lockfile lists under
|
|
node_modules. The root entry ("") is deliberately excluded — bumping
|
|
package.json makes the lockfile root version lag by design until the
|
|
`npm install --package-lock-only` at the end of predist rewrites it. */
|
|
const fs = require('node:fs')
|
|
const path = require('node:path')
|
|
|
|
const ROOT = path.join(__dirname, '..')
|
|
const lock = JSON.parse(fs.readFileSync(path.join(ROOT, 'package-lock.json'), 'utf8'))
|
|
const packages = lock.packages ?? {}
|
|
|
|
const mismatched = []
|
|
let checked = 0
|
|
// Optional entries are the cross-platform variants (darwin/linux/arm64 …) that
|
|
// npm records in the lockfile but never installs on this machine. Their absence
|
|
// is expected, not drift.
|
|
let skippedOptional = 0
|
|
|
|
for (const [key, entry] of Object.entries(packages)) {
|
|
if (key === '' || !key.startsWith('node_modules/')) continue
|
|
// The key is the path under node_modules, so it also resolves nested
|
|
// ("node_modules/a/node_modules/b") and scoped ("node_modules/@scope/pkg")
|
|
// entries without any name reconstruction.
|
|
let installed
|
|
try {
|
|
installed = JSON.parse(fs.readFileSync(path.join(ROOT, key, 'package.json'), 'utf8')).version
|
|
} catch {
|
|
installed = undefined
|
|
}
|
|
if (installed === undefined) {
|
|
if (entry.optional) skippedOptional++
|
|
else mismatched.push([key, entry.version, 'MISSING'])
|
|
continue
|
|
}
|
|
checked++
|
|
if (installed !== entry.version) mismatched.push([key, entry.version, installed])
|
|
}
|
|
|
|
if (mismatched.length) {
|
|
console.error('node_modules does not match package-lock.json:')
|
|
for (const [name, expected, actual] of mismatched) {
|
|
console.error(` ${name}: lockfile expects ${expected}, installed ${actual}`)
|
|
}
|
|
console.error('\nnode_modules 与 package-lock.json 不一致,请运行 npm ci 重装依赖')
|
|
process.exit(1)
|
|
}
|
|
console.log(`dependencies ok: ${checked} packages verified, ${skippedOptional} optional entries skipped`)
|