1- // Lists production store packages from lockfile data .
1+ // Lists current-target production packages for Docker's offline prune store seed .
22import fs from "node:fs" ;
33import path from "node:path" ;
44import { parse } from "yaml" ;
55
66const parsed = JSON . parse ( fs . readFileSync ( 0 , "utf8" ) ) ;
77const roots = Array . isArray ( parsed ) ? parsed : [ parsed ] ;
88const specs = new Set ( ) ;
9+ const target = {
10+ cpu : process . arch ,
11+ libc : detectLibc ( ) ,
12+ os : process . platform ,
13+ } ;
914
1015function packageSpec ( name , version ) {
1116 if ( ! name || ! version || typeof version !== "string" ) {
@@ -22,26 +27,72 @@ function packageSpec(name, version) {
2227 return `${ name } @${ normalizedVersion } ` ;
2328}
2429
25- function packageSpecFromLockfileKey ( key ) {
30+ function detectLibc ( ) {
31+ if ( process . platform !== "linux" ) {
32+ return undefined ;
33+ }
34+ const report = process . report ?. getReport ?. ( ) ;
35+ return report ?. header ?. glibcVersionRuntime ? "glibc" : "musl" ;
36+ }
37+
38+ function matchesTargetSelector ( selector , value ) {
39+ if ( ! Array . isArray ( selector ) || ! value ) {
40+ return true ;
41+ }
42+ const blocked = selector . some ( ( entry ) => entry === `!${ value } ` ) ;
43+ if ( blocked ) {
44+ return false ;
45+ }
46+ const allowed = selector . filter ( ( entry ) => typeof entry === "string" && ! entry . startsWith ( "!" ) ) ;
47+ return allowed . length === 0 || allowed . includes ( value ) ;
48+ }
49+
50+ function packageEntryForSpec ( lockfile , spec ) {
51+ return lockfile ?. packages ?. [ spec ] ?? lockfile ?. packages ?. [ `/${ spec } ` ] ;
52+ }
53+
54+ function normalizeLockfilePackageKey ( key ) {
2655 if ( typeof key !== "string" ) {
2756 return undefined ;
2857 }
29- const normalizedKey = ( key . startsWith ( "/" ) ? key . slice ( 1 ) : key ) . replace ( / \( .+ \) $ / , "" ) ;
30- const separator = normalizedKey . lastIndexOf ( "@" ) ;
31- if ( separator <= 0 ) {
58+ return ( key . startsWith ( "/" ) ? key . slice ( 1 ) : key ) . replace ( / \( .+ \) $ / , "" ) ;
59+ }
60+
61+ function snapshotForSpec ( lockfile , spec ) {
62+ const snapshots = lockfile ?. snapshots ;
63+ if ( ! snapshots ) {
3264 return undefined ;
3365 }
34- return packageSpec ( normalizedKey . slice ( 0 , separator ) , normalizedKey . slice ( separator + 1 ) ) ;
66+ return (
67+ snapshots [ spec ] ??
68+ snapshots [ `/${ spec } ` ] ??
69+ Object . entries ( snapshots ) . find ( ( [ key ] ) => normalizeLockfilePackageKey ( key ) === spec ) ?. [ 1 ]
70+ ) ;
3571}
3672
37- function visitListNode ( node ) {
73+ function packageSupportsTarget ( lockfile , spec ) {
74+ const entry = packageEntryForSpec ( lockfile , spec ) ;
75+ return (
76+ matchesTargetSelector ( entry ?. os , target . os ) &&
77+ matchesTargetSelector ( entry ?. cpu , target . cpu ) &&
78+ matchesTargetSelector ( entry ?. libc , target . libc )
79+ ) ;
80+ }
81+
82+ function addSpec ( lockfile , spec ) {
83+ if ( spec && packageSupportsTarget ( lockfile , spec ) ) {
84+ specs . add ( spec ) ;
85+ }
86+ }
87+
88+ function visitListNode ( lockfile , node ) {
3889 for ( const dep of Object . values ( node . dependencies ?? { } ) ) {
3990 const name = dep . from || dep . name ;
4091 const spec = packageSpec ( name , dep . version ) ;
4192 if ( spec && dep . resolved ?. startsWith ( "https://registry.npmjs.org/" ) ) {
42- specs . add ( spec ) ;
93+ addSpec ( lockfile , spec ) ;
4394 }
44- visitListNode ( dep ) ;
95+ visitListNode ( lockfile , dep ) ;
4596 }
4697}
4798
@@ -53,15 +104,6 @@ function readLockfile() {
53104 return parse ( fs . readFileSync ( lockfilePath , "utf8" ) ) ;
54105}
55106
56- function addLockfilePackages ( lockfile ) {
57- for ( const key of Object . keys ( lockfile ?. packages ?? { } ) ) {
58- const spec = packageSpecFromLockfileKey ( key ) ;
59- if ( spec ) {
60- specs . add ( spec ) ;
61- }
62- }
63- }
64-
65107function addSnapshotClosure ( lockfile ) {
66108 const snapshots = lockfile ?. snapshots ;
67109 const packages = lockfile ?. packages ;
@@ -76,26 +118,36 @@ function addSnapshotClosure(lockfile) {
76118 continue ;
77119 }
78120 visited . add ( spec ) ;
79- const snapshot = snapshots [ spec ] ;
121+ const snapshot = snapshotForSpec ( lockfile , spec ) ;
80122 if ( ! snapshot ) {
81123 continue ;
82124 }
83- for ( const [ name , version ] of Object . entries ( snapshot . dependencies ?? { } ) ) {
125+ const addDependencySpec = ( name , version ) => {
84126 const depSpec = packageSpec ( name , typeof version === "string" ? version : version ?. version ) ;
85- if ( ! depSpec || ! packages [ depSpec ] || specs . has ( depSpec ) ) {
86- continue ;
127+ if (
128+ ! depSpec ||
129+ ! packages [ depSpec ] ||
130+ specs . has ( depSpec ) ||
131+ ! packageSupportsTarget ( lockfile , depSpec )
132+ ) {
133+ return ;
87134 }
88135 specs . add ( depSpec ) ;
89136 pending . push ( depSpec ) ;
137+ } ;
138+ for ( const [ name , version ] of Object . entries ( snapshot . dependencies ?? { } ) ) {
139+ addDependencySpec ( name , version ) ;
140+ }
141+ for ( const [ name , version ] of Object . entries ( snapshot . optionalDependencies ?? { } ) ) {
142+ addDependencySpec ( name , version ) ;
90143 }
91144 }
92145}
93146
147+ const lockfile = readLockfile ( ) ;
94148for ( const root of roots ) {
95- visitListNode ( root ) ;
149+ visitListNode ( lockfile , root ) ;
96150}
97- const lockfile = readLockfile ( ) ;
98151addSnapshotClosure ( lockfile ) ;
99- addLockfilePackages ( lockfile ) ;
100152
101153process . stdout . write ( [ ...specs ] . toSorted ( ( a , b ) => a . localeCompare ( b ) ) . join ( "\n" ) ) ;
0 commit comments