1
+ import fs from 'fs/promises'
1
2
import path from 'node:path'
2
3
3
4
import spawn from '@npmcli/promise-spawn'
@@ -8,9 +9,12 @@ import npmPackageArg from 'npm-package-arg'
8
9
import ora from 'ora'
9
10
import pacote from 'pacote'
10
11
import semver from 'semver'
12
+ import { glob as tinyGlob } from 'tinyglobby'
13
+ import { parse as yamlParse } from 'yaml'
11
14
12
15
import { commonFlags } from '../flags'
13
16
import { printFlagList } from '../utils/formatting'
17
+ import { existsSync } from '../utils/fs'
14
18
import { hasOwn } from '../utils/objects'
15
19
import { detect } from '../utils/package-manager-detector'
16
20
import { pEach } from '../utils/promises'
@@ -27,12 +31,12 @@ import type {
27
31
StringKeyValueObject
28
32
} from '../utils/package-manager-detector'
29
33
30
- const distPath = __dirname
31
-
32
34
const COMMAND_TITLE = 'Socket Optimize'
33
35
const OVERRIDES_FIELD_NAME = 'overrides'
36
+ const PNPM_WORKSPACE = 'pnpm-workspace'
34
37
const RESOLUTIONS_FIELD_NAME = 'resolutions'
35
38
39
+ const distPath = __dirname
36
40
const manifestNpmOverrides = getManifestData ( 'npm' ) !
37
41
const packumentCache = new Map ( )
38
42
@@ -101,74 +105,39 @@ const lockIncludesByAgent: Record<Agent, LockIncludes> = {
101
105
}
102
106
103
107
type ModifyManifest = (
104
- editablePkgJson : EditablePackageJson ,
108
+ pkgJson : EditablePackageJson ,
105
109
overrides : Overrides
106
110
) => void
107
111
108
- const updateManifestByAgent : Record < Agent , ModifyManifest > = ( < any > {
109
- __proto__ : null ,
110
- npm ( editablePkgJson : EditablePackageJson , overrides : Overrides ) {
111
- editablePkgJson . update ( {
112
- __proto__ : null ,
112
+ const updateManifestByAgent : Record < Agent , ModifyManifest > = {
113
+ npm ( pkgJson : EditablePackageJson , overrides : Overrides ) {
114
+ pkgJson . update ( {
113
115
[ OVERRIDES_FIELD_NAME ] : overrides
114
116
} )
115
117
} ,
116
- pnpm ( editablePkgJson : EditablePackageJson , overrides : Overrides ) {
117
- editablePkgJson . update ( {
118
+ pnpm ( pkgJson : EditablePackageJson , overrides : Overrides ) {
119
+ pkgJson . update ( {
118
120
pnpm : {
119
- __proto__ : null ,
120
- ...( < object > editablePkgJson . content [ 'pnpm' ] ) ,
121
+ ...( < object > pkgJson . content [ 'pnpm' ] ) ,
121
122
[ OVERRIDES_FIELD_NAME ] : overrides
122
123
}
123
124
} )
124
125
} ,
125
- yarn ( editablePkgJson : EditablePackageJson , overrides : PnpmOrYarnOverrides ) {
126
- editablePkgJson . update ( {
127
- __proto__ : null ,
128
- [ RESOLUTIONS_FIELD_NAME ] : overrides
126
+ yarn ( pkgJson : EditablePackageJson , overrides : Overrides ) {
127
+ pkgJson . update ( {
128
+ [ RESOLUTIONS_FIELD_NAME ] : < PnpmOrYarnOverrides > overrides
129
129
} )
130
130
}
131
- } ) as Record < Agent , ModifyManifest >
132
-
133
- type AddOverridesConfig = {
134
- agent : Agent
135
- isPrivate : boolean
136
- isWorkspace : boolean
137
- lockIncludes : LockIncludes
138
- lockSrc : string
139
- manifestEntries : ManifestEntry [ ]
140
- pkgJsonPath : string
141
- pin : boolean
142
131
}
143
132
144
- type AddOverridesState = {
145
- added : Set < string >
146
- updated : Set < string >
147
- }
148
-
149
- async function addOverrides (
150
- {
151
- agent,
152
- isPrivate,
153
- isWorkspace,
154
- lockSrc,
155
- lockIncludes,
156
- manifestEntries,
157
- pkgJsonPath,
158
- pin
159
- } : AddOverridesConfig ,
160
- state : AddOverridesState
161
- ) : Promise < AddOverridesState > {
162
- const editablePkgJson = await EditablePackageJson . load (
163
- path . dirname ( pkgJsonPath )
164
- )
133
+ function getDependencyEntries ( pkgJson : PackageJsonContent ) {
165
134
const {
166
135
dependencies,
167
136
devDependencies,
168
- peerDependencies ,
169
- optionalDependencies
170
- } = editablePkgJson . content
171
- const depEntries = < [ string , NonNullable < typeof dependencies > ] [ ] > [
137
+ optionalDependencies ,
138
+ peerDependencies
139
+ } = pkgJson
140
+ return < [ string , NonNullable < typeof dependencies > ] [ ] > [
172
141
[
173
142
'dependencies' ,
174
143
dependencies ? { __proto__ : null , ...dependencies } : undefined
@@ -188,19 +157,107 @@ async function addOverrides(
188
157
: undefined
189
158
]
190
159
] . filter ( ( { 1 : o } ) => o )
160
+ }
161
+
162
+ async function getWorkspaces (
163
+ agent : Agent ,
164
+ pkgPath : string ,
165
+ pkgJson : PackageJsonContent
166
+ ) : Promise < string [ ] | undefined > {
167
+ if ( agent !== 'pnpm' ) {
168
+ return Array . isArray ( pkgJson [ 'workspaces' ] )
169
+ ? < string [ ] > pkgJson [ 'workspaces' ] . filter ( isNonEmptyString )
170
+ : undefined
171
+ }
172
+ for ( const workspacePath of [
173
+ path . join ( pkgPath ! , `${ PNPM_WORKSPACE } .yaml` ) ,
174
+ path . join ( pkgPath ! , `${ PNPM_WORKSPACE } .yml` )
175
+ ] ) {
176
+ if ( existsSync ( workspacePath ) ) {
177
+ let packages
178
+ try {
179
+ // eslint-disable-next-line no-await-in-loop
180
+ packages = yamlParse ( await fs . readFile ( workspacePath , 'utf8' ) ) ?. packages
181
+ } catch { }
182
+ if ( Array . isArray ( packages ) ) {
183
+ return packages . filter ( isNonEmptyString )
184
+ }
185
+ }
186
+ }
187
+ return undefined
188
+ }
189
+
190
+ function workspaceToGlobPattern ( workspace : string ) : string {
191
+ const { length } = workspace
192
+ // If the workspace ends with "/"
193
+ if ( workspace . charCodeAt ( length - 1 ) === 47 /*'/'*/ ) {
194
+ return `${ workspace } /*/package.json`
195
+ }
196
+ // If the workspace ends with "/**"
197
+ if (
198
+ workspace . charCodeAt ( length - 1 ) === 42 /*'*'*/ &&
199
+ workspace . charCodeAt ( length - 2 ) === 42 /*'*'*/ &&
200
+ workspace . charCodeAt ( length - 3 ) === 47 /*'/'*/
201
+ ) {
202
+ return `${ workspace } /*/**/package.json`
203
+ }
204
+ // Things like "packages/a" or "packages/*"
205
+ return `${ workspace } /package.json`
206
+ }
207
+
208
+ type AddOverridesConfig = {
209
+ agent : Agent
210
+ lockIncludes : LockIncludes
211
+ lockSrc : string
212
+ manifestEntries : ManifestEntry [ ]
213
+ pkgJson ?: EditablePackageJson | undefined
214
+ pkgPath : string
215
+ pin : boolean
216
+ rootPath : string
217
+ }
218
+
219
+ type AddOverridesState = {
220
+ added : Set < string >
221
+ updated : Set < string >
222
+ }
223
+
224
+ async function addOverrides (
225
+ {
226
+ agent,
227
+ lockIncludes,
228
+ lockSrc,
229
+ manifestEntries,
230
+ pkgJson : editablePkgJson ,
231
+ pkgPath,
232
+ pin,
233
+ rootPath
234
+ } : AddOverridesConfig ,
235
+ state : AddOverridesState = {
236
+ added : new Set ( ) ,
237
+ updated : new Set ( )
238
+ }
239
+ ) : Promise < AddOverridesState > {
240
+ if ( editablePkgJson === undefined ) {
241
+ editablePkgJson = await EditablePackageJson . load ( pkgPath )
242
+ }
243
+ const pkgJson : Readonly < PackageJsonContent > = editablePkgJson . content
244
+ const isRoot = pkgPath === rootPath
245
+ const depEntries = getDependencyEntries ( pkgJson )
246
+ const workspaces = await getWorkspaces ( agent , pkgPath , pkgJson )
247
+ const isWorkspace = ! ! workspaces
191
248
const overridesDataObjects = < GetOverridesResult [ ] > [ ]
192
- if ( isPrivate || isWorkspace ) {
193
- overridesDataObjects . push (
194
- getOverridesDataByAgent [ agent ] ( editablePkgJson . content )
195
- )
249
+ if ( pkgJson [ 'private' ] || isWorkspace ) {
250
+ overridesDataObjects . push ( getOverridesDataByAgent [ agent ] ( pkgJson ) )
196
251
} else {
197
252
overridesDataObjects . push (
198
- getOverridesDataByAgent [ 'npm' ] ( editablePkgJson . content ) ,
199
- getOverridesDataByAgent [ 'yarn' ] ( editablePkgJson . content )
253
+ getOverridesDataByAgent [ 'npm' ] ( pkgJson ) ,
254
+ getOverridesDataByAgent [ 'yarn' ] ( pkgJson )
200
255
)
201
256
}
257
+ const spinner = isRoot
258
+ ? ora ( 'Fetching override manifests...' ) . start ( )
259
+ : undefined
202
260
const depAliasMap = new Map < string , { id : string ; version : string } > ( )
203
- const spinner = ora ( `Fetching override manifests...` ) . start ( )
204
261
// Chunk package names to process them in parallel 3 at a time.
205
262
await pEach ( manifestEntries , 3 , async ( { 1 : data } ) => {
206
263
const { name : regPkgName , package : origPkgName , version } = data
@@ -227,6 +284,9 @@ async function addOverrides(
227
284
} )
228
285
}
229
286
}
287
+ if ( ! isRoot ) {
288
+ return
289
+ }
230
290
// Chunk package names to process them in parallel 3 at a time.
231
291
await pEach ( overridesDataObjects , 3 , async ( { overrides, type } ) => {
232
292
const overrideExists = hasOwn ( overrides , origPkgName )
@@ -263,7 +323,34 @@ async function addOverrides(
263
323
}
264
324
} )
265
325
} )
266
- spinner . stop ( )
326
+ if ( workspaces ) {
327
+ const wsPkgJsonPaths = await tinyGlob (
328
+ workspaces . map ( workspaceToGlobPattern ) ,
329
+ {
330
+ absolute : true ,
331
+ cwd : pkgPath !
332
+ }
333
+ )
334
+ // Chunk package names to process them in parallel 3 at a time.
335
+ await pEach ( wsPkgJsonPaths , 3 , async wsPkgJsonPath => {
336
+ const { added, updated } = await addOverrides ( {
337
+ agent,
338
+ lockSrc,
339
+ lockIncludes,
340
+ manifestEntries,
341
+ pin,
342
+ pkgPath : path . dirname ( wsPkgJsonPath ) ,
343
+ rootPath
344
+ } )
345
+ for ( const regPkgName of added ) {
346
+ state . added . add ( regPkgName )
347
+ }
348
+ for ( const regPkgName of updated ) {
349
+ state . updated . add ( regPkgName )
350
+ }
351
+ } )
352
+ }
353
+ spinner ?. stop ( )
267
354
if ( state . added . size || state . updated . size ) {
268
355
editablePkgJson . update ( < PackageJsonContent > Object . fromEntries ( depEntries ) )
269
356
for ( const { overrides, type } of overridesDataObjects ) {
@@ -318,13 +405,11 @@ export const optimize: CliSubcommand = {
318
405
const {
319
406
agent,
320
407
agentExecPath,
321
- isPrivate,
322
- isWorkspace,
323
408
lockSrc,
324
409
lockPath,
325
410
minimumNodeVersion,
326
- pkgJsonPath,
327
411
pkgJson,
412
+ pkgPath,
328
413
supported
329
414
} = await detect ( {
330
415
cwd,
@@ -345,7 +430,7 @@ export const optimize: CliSubcommand = {
345
430
console . log ( `✘ ${ COMMAND_TITLE } : No ${ lockName } found` )
346
431
return
347
432
}
348
- if ( pkgJson === undefined ) {
433
+ if ( pkgPath === undefined ) {
349
434
console . log ( `✘ ${ COMMAND_TITLE } : No package.json found` )
350
435
return
351
436
}
@@ -354,7 +439,6 @@ export const optimize: CliSubcommand = {
354
439
`⚠️ ${ COMMAND_TITLE } : Package ${ lockName } found at ${ lockPath } `
355
440
)
356
441
}
357
-
358
442
const state : AddOverridesState = {
359
443
added : new Set ( ) ,
360
444
updated : new Set ( )
@@ -369,13 +453,13 @@ export const optimize: CliSubcommand = {
369
453
await addOverrides (
370
454
{
371
455
agent : agent === 'bun' ? 'yarn' : agent ,
372
- isPrivate,
373
- isWorkspace,
374
456
lockIncludes,
375
457
lockSrc,
376
458
manifestEntries,
377
459
pin,
378
- pkgJsonPath
460
+ pkgJson,
461
+ pkgPath,
462
+ rootPath : pkgPath
379
463
} ,
380
464
state
381
465
)
0 commit comments