1+ import fs from 'fs/promises'
12import path from 'node:path'
23
34import spawn from '@npmcli/promise-spawn'
@@ -8,9 +9,12 @@ import npmPackageArg from 'npm-package-arg'
89import ora from 'ora'
910import pacote from 'pacote'
1011import semver from 'semver'
12+ import { glob as tinyGlob } from 'tinyglobby'
13+ import { parse as yamlParse } from 'yaml'
1114
1215import { commonFlags } from '../flags'
1316import { printFlagList } from '../utils/formatting'
17+ import { existsSync } from '../utils/fs'
1418import { hasOwn } from '../utils/objects'
1519import { detect } from '../utils/package-manager-detector'
1620import { pEach } from '../utils/promises'
@@ -27,12 +31,12 @@ import type {
2731 StringKeyValueObject
2832} from '../utils/package-manager-detector'
2933
30- const distPath = __dirname
31-
3234const COMMAND_TITLE = 'Socket Optimize'
3335const OVERRIDES_FIELD_NAME = 'overrides'
36+ const PNPM_WORKSPACE = 'pnpm-workspace'
3437const RESOLUTIONS_FIELD_NAME = 'resolutions'
3538
39+ const distPath = __dirname
3640const manifestNpmOverrides = getManifestData ( 'npm' ) !
3741const packumentCache = new Map ( )
3842
@@ -101,74 +105,39 @@ const lockIncludesByAgent: Record<Agent, LockIncludes> = {
101105}
102106
103107type ModifyManifest = (
104- editablePkgJson : EditablePackageJson ,
108+ pkgJson : EditablePackageJson ,
105109 overrides : Overrides
106110) => void
107111
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 ( {
113115 [ OVERRIDES_FIELD_NAME ] : overrides
114116 } )
115117 } ,
116- pnpm ( editablePkgJson : EditablePackageJson , overrides : Overrides ) {
117- editablePkgJson . update ( {
118+ pnpm ( pkgJson : EditablePackageJson , overrides : Overrides ) {
119+ pkgJson . update ( {
118120 pnpm : {
119- __proto__ : null ,
120- ...( < object > editablePkgJson . content [ 'pnpm' ] ) ,
121+ ...( < object > pkgJson . content [ 'pnpm' ] ) ,
121122 [ OVERRIDES_FIELD_NAME ] : overrides
122123 }
123124 } )
124125 } ,
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
129129 } )
130130 }
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
142131}
143132
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 ) {
165134 const {
166135 dependencies,
167136 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 > ] [ ] > [
172141 [
173142 'dependencies' ,
174143 dependencies ? { __proto__ : null , ...dependencies } : undefined
@@ -188,19 +157,107 @@ async function addOverrides(
188157 : undefined
189158 ]
190159 ] . 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
191248 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 ) )
196251 } else {
197252 overridesDataObjects . push (
198- getOverridesDataByAgent [ 'npm' ] ( editablePkgJson . content ) ,
199- getOverridesDataByAgent [ 'yarn' ] ( editablePkgJson . content )
253+ getOverridesDataByAgent [ 'npm' ] ( pkgJson ) ,
254+ getOverridesDataByAgent [ 'yarn' ] ( pkgJson )
200255 )
201256 }
257+ const spinner = isRoot
258+ ? ora ( 'Fetching override manifests...' ) . start ( )
259+ : undefined
202260 const depAliasMap = new Map < string , { id : string ; version : string } > ( )
203- const spinner = ora ( `Fetching override manifests...` ) . start ( )
204261 // Chunk package names to process them in parallel 3 at a time.
205262 await pEach ( manifestEntries , 3 , async ( { 1 : data } ) => {
206263 const { name : regPkgName , package : origPkgName , version } = data
@@ -227,6 +284,9 @@ async function addOverrides(
227284 } )
228285 }
229286 }
287+ if ( ! isRoot ) {
288+ return
289+ }
230290 // Chunk package names to process them in parallel 3 at a time.
231291 await pEach ( overridesDataObjects , 3 , async ( { overrides, type } ) => {
232292 const overrideExists = hasOwn ( overrides , origPkgName )
@@ -263,7 +323,34 @@ async function addOverrides(
263323 }
264324 } )
265325 } )
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 ( )
267354 if ( state . added . size || state . updated . size ) {
268355 editablePkgJson . update ( < PackageJsonContent > Object . fromEntries ( depEntries ) )
269356 for ( const { overrides, type } of overridesDataObjects ) {
@@ -318,13 +405,11 @@ export const optimize: CliSubcommand = {
318405 const {
319406 agent,
320407 agentExecPath,
321- isPrivate,
322- isWorkspace,
323408 lockSrc,
324409 lockPath,
325410 minimumNodeVersion,
326- pkgJsonPath,
327411 pkgJson,
412+ pkgPath,
328413 supported
329414 } = await detect ( {
330415 cwd,
@@ -345,7 +430,7 @@ export const optimize: CliSubcommand = {
345430 console . log ( `✘ ${ COMMAND_TITLE } : No ${ lockName } found` )
346431 return
347432 }
348- if ( pkgJson === undefined ) {
433+ if ( pkgPath === undefined ) {
349434 console . log ( `✘ ${ COMMAND_TITLE } : No package.json found` )
350435 return
351436 }
@@ -354,7 +439,6 @@ export const optimize: CliSubcommand = {
354439 `⚠️ ${ COMMAND_TITLE } : Package ${ lockName } found at ${ lockPath } `
355440 )
356441 }
357-
358442 const state : AddOverridesState = {
359443 added : new Set ( ) ,
360444 updated : new Set ( )
@@ -369,13 +453,13 @@ export const optimize: CliSubcommand = {
369453 await addOverrides (
370454 {
371455 agent : agent === 'bun' ? 'yarn' : agent ,
372- isPrivate,
373- isWorkspace,
374456 lockIncludes,
375457 lockSrc,
376458 manifestEntries,
377459 pin,
378- pkgJsonPath
460+ pkgJson,
461+ pkgPath,
462+ rootPath : pkgPath
379463 } ,
380464 state
381465 )
0 commit comments