66// linux-x64-gnu vs linux-x64-musl when resolving from the lockfile and may
77// install (or load) the wrong native binary on Alpine/musl hosts.
88//
9+ // The set of packages to check is derived from `optionalDependencies` in
10+ // package.json, so adding a new linux-* platform there automatically extends
11+ // this guard with no script change required. The expected libc value is
12+ // inferred from the package name's `-gnu`/`-musl` suffix (the napi-rs naming
13+ // convention).
14+ //
915// Run via `npm run lint` (or directly) in CI to catch silent regressions from
1016// Dependabot bumps and contributor `npm install` runs.
1117import { readFileSync } from 'node:fs' ;
1218import { dirname , resolve } from 'node:path' ;
1319import { fileURLToPath } from 'node:url' ;
1420
15- const EXPECTED_LIBC = {
16- '@optave/codegraph-linux-arm64-gnu' : 'glibc' ,
17- '@optave/codegraph-linux-x64-gnu' : 'glibc' ,
18- '@optave/codegraph-linux-x64-musl' : 'musl' ,
21+ // Map of napi-rs name suffix → expected `libc` value in package-lock.json.
22+ // Adding a new entry here is the ONLY edit required to teach the verifier
23+ // about a new libc variant — `LINUX_PKG_PATTERN` is derived from these keys
24+ // below so the regex and the map can never drift apart.
25+ const LIBC_BY_SUFFIX = {
26+ gnu : 'glibc' ,
27+ musl : 'musl' ,
1928} ;
29+ const LIBC_SUFFIXES = Object . keys ( LIBC_BY_SUFFIX ) ;
30+ const LINUX_PKG_PATTERN = new RegExp (
31+ `^@optave/codegraph-linux-[^-]+-(${ LIBC_SUFFIXES . join ( '|' ) } )$` ,
32+ ) ;
2033
2134// Resolve relative to this script's location so it works regardless of CWD
2235// (e.g. running `node scripts/verify-lockfile-libc.mjs` from the `scripts/`
23- // subdirectory still finds the repo-root lockfile).
24- const __dirname = dirname ( fileURLToPath ( import . meta. url ) ) ;
25- const lockfilePath = resolve ( __dirname , '..' , 'package-lock.json' ) ;
26- const lock = JSON . parse ( readFileSync ( lockfilePath , 'utf8' ) ) ;
36+ // subdirectory still finds the repo-root manifests).
37+ const __filename = fileURLToPath ( import . meta. url ) ;
38+ const __dirname = dirname ( __filename ) ;
39+ const repoRoot = resolve ( __dirname , '..' ) ;
40+ const pkg = JSON . parse ( readFileSync ( resolve ( repoRoot , 'package.json' ) , 'utf8' ) ) ;
41+ const lock = JSON . parse ( readFileSync ( resolve ( repoRoot , 'package-lock.json' ) , 'utf8' ) ) ;
42+
43+ const optionalDeps = pkg . optionalDependencies ?? { } ;
44+ const linuxPackages = Object . keys ( optionalDeps ) . filter ( ( name ) =>
45+ name . startsWith ( '@optave/codegraph-linux-' ) ,
46+ ) ;
47+
48+ if ( linuxPackages . length === 0 ) {
49+ console . log ( 'package-lock.json libc check: no @optave/codegraph-linux-* packages declared, skipping' ) ;
50+ process . exit ( 0 ) ;
51+ }
52+
2753const failures = [ ] ;
54+ const unknownSuffixes = [ ] ;
2855
29- for ( const [ pkgName , expectedLibc ] of Object . entries ( EXPECTED_LIBC ) ) {
56+ for ( const pkgName of linuxPackages ) {
57+ const match = LINUX_PKG_PATTERN . exec ( pkgName ) ;
58+ if ( ! match ) {
59+ unknownSuffixes . push ( pkgName ) ;
60+ continue ;
61+ }
62+ const expectedLibc = LIBC_BY_SUFFIX [ match [ 1 ] ] ;
3063 const entry = lock . packages ?. [ `node_modules/${ pkgName } ` ] ;
3164 if ( ! entry ) {
3265 failures . push ( `${ pkgName } : missing from package-lock.json` ) ;
@@ -40,6 +73,20 @@ for (const [pkgName, expectedLibc] of Object.entries(EXPECTED_LIBC)) {
4073 }
4174}
4275
76+ if ( unknownSuffixes . length > 0 ) {
77+ const knownRule = LIBC_SUFFIXES . map ( ( s ) => `-${ s } → ${ LIBC_BY_SUFFIX [ s ] } ` ) . join ( ', ' ) ;
78+ console . error (
79+ 'package-lock.json libc discriminator check cannot infer expected libc for:\n' ,
80+ ) ;
81+ for ( const name of unknownSuffixes ) console . error ( ` - ${ name } ` ) ;
82+ console . error (
83+ `\nAdd the new suffix to LIBC_BY_SUFFIX in ${ __filename } \n` +
84+ '(LINUX_PKG_PATTERN is derived from its keys, so no separate regex update\n' +
85+ `is needed). Current rule: ${ knownRule } .` ,
86+ ) ;
87+ process . exit ( 1 ) ;
88+ }
89+
4390if ( failures . length > 0 ) {
4491 console . error ( 'package-lock.json libc discriminator check failed:\n' ) ;
4592 for ( const f of failures ) console . error ( ` - ${ f } ` ) ;
@@ -51,4 +98,6 @@ if (failures.length > 0) {
5198 process . exit ( 1 ) ;
5299}
53100
54- console . log ( 'package-lock.json libc discriminators OK' ) ;
101+ console . log (
102+ `package-lock.json libc discriminators OK (${ linuxPackages . length } linux package(s) checked)` ,
103+ ) ;
0 commit comments