Skip to content

Commit 9b299f5

Browse files
committed
Improve error messages, spinner text, and ls scanning
1 parent 83af24b commit 9b299f5

File tree

3 files changed

+188
-72
lines changed

3 files changed

+188
-72
lines changed

src/commands/optimize.ts

Lines changed: 127 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import path from 'node:path'
44
import spawn from '@npmcli/promise-spawn'
55
import EditablePackageJson from '@npmcli/package-json'
66
import { getManifestData } from '@socketsecurity/registry'
7+
//import cacache from 'cacache'
78
import meow from 'meow'
89
import npa from 'npm-package-arg'
910
import ora from 'ora'
@@ -12,6 +13,8 @@ import semver from 'semver'
1213
import { glob as tinyGlob } from 'tinyglobby'
1314
import { parse as yamlParse } from 'yaml'
1415

16+
//import { packumentCache, pacoteCachePath } from '../constants'
17+
import { packumentCache } from '../constants'
1518
import { commonFlags } from '../flags'
1619
import { printFlagList } from '../utils/formatting'
1720
import { existsSync } from '../utils/fs'
@@ -24,11 +27,11 @@ import { isNonEmptyString } from '../utils/strings'
2427

2528
import type { Content as PackageJsonContent } from '@npmcli/package-json'
2629
import type { ManifestEntry } from '@socketsecurity/registry'
30+
import type { Ora } from 'ora'
2731
import type { PacoteOptions } from 'pacote'
2832
import type { CliSubcommand } from '../utils/meow-with-subcommands'
2933
import type {
3034
Agent,
31-
AgentPlusBun,
3235
StringKeyValueObject
3336
} from '../utils/package-manager-detector'
3437

@@ -39,7 +42,6 @@ const RESOLUTIONS_FIELD_NAME = 'resolutions'
3942

4043
const distPath = __dirname
4144
const manifestNpmOverrides = getManifestData('npm')!
42-
const packumentCache = new Map()
4345

4446
type NpmOverrides = { [key: string]: string | StringKeyValueObject }
4547
type PnpmOrYarnOverrides = { [key: string]: string }
@@ -50,10 +52,10 @@ type GetOverridesResult = {
5052
overrides: Overrides
5153
}
5254

53-
const getOverridesDataByAgent: Record<AgentPlusBun, GetOverrides> = {
55+
const getOverridesDataByAgent: Record<Agent, GetOverrides> = {
5456
bun(pkgJson: PackageJsonContent) {
5557
const overrides = (pkgJson as any)?.resolutions ?? {}
56-
return { type: 'yarn', overrides }
58+
return { type: 'yarn/berry', overrides }
5759
},
5860
// npm overrides documentation:
5961
// https://docs.npmjs.com/cli/v10/configuring-npm/package-json#overrides
@@ -69,15 +71,21 @@ const getOverridesDataByAgent: Record<AgentPlusBun, GetOverrides> = {
6971
},
7072
// Yarn resolutions documentation:
7173
// https://yarnpkg.com/configuration/manifest#resolutions
72-
yarn(pkgJson: PackageJsonContent) {
74+
'yarn/berry'(pkgJson: PackageJsonContent) {
7375
const overrides = (pkgJson as any)?.resolutions ?? {}
74-
return { type: 'yarn', overrides }
76+
return { type: 'yarn/berry', overrides }
77+
},
78+
// Yarn resolutions documentation:
79+
// https://classic.yarnpkg.com/en/docs/selective-version-resolutions
80+
'yarn/classic'(pkgJson: PackageJsonContent) {
81+
const overrides = (pkgJson as any)?.resolutions ?? {}
82+
return { type: 'yarn/classic', overrides }
7583
}
7684
}
7785

7886
type AgentLockIncludesFn = (lockSrc: string, name: string) => boolean
7987

80-
const lockIncludesByAgent: Record<AgentPlusBun, AgentLockIncludesFn> = (() => {
88+
const lockIncludesByAgent: Record<Agent, AgentLockIncludesFn> = (() => {
8189
const yarn = (lockSrc: string, name: string) => {
8290
const escapedName = escapeRegExp(name)
8391
return new RegExp(
@@ -109,7 +117,8 @@ const lockIncludesByAgent: Record<AgentPlusBun, AgentLockIncludesFn> = (() => {
109117
'm'
110118
).test(lockSrc)
111119
},
112-
yarn
120+
'yarn/berry': yarn,
121+
'yarn/classic': yarn
113122
}
114123
})()
115124

@@ -118,7 +127,7 @@ type AgentModifyManifestFn = (
118127
overrides: Overrides
119128
) => void
120129

121-
const updateManifestByAgent: Record<AgentPlusBun, AgentModifyManifestFn> = {
130+
const updateManifestByAgent: Record<Agent, AgentModifyManifestFn> = {
122131
bun(pkgJson: EditablePackageJson, overrides: Overrides) {
123132
pkgJson.update({
124133
[RESOLUTIONS_FIELD_NAME]: <PnpmOrYarnOverrides>overrides
@@ -137,7 +146,12 @@ const updateManifestByAgent: Record<AgentPlusBun, AgentModifyManifestFn> = {
137146
}
138147
})
139148
},
140-
yarn(pkgJson: EditablePackageJson, overrides: Overrides) {
149+
'yarn/berry'(pkgJson: EditablePackageJson, overrides: Overrides) {
150+
pkgJson.update({
151+
[RESOLUTIONS_FIELD_NAME]: <PnpmOrYarnOverrides>overrides
152+
})
153+
},
154+
'yarn/classic'(pkgJson: EditablePackageJson, overrides: Overrides) {
141155
pkgJson.update({
142156
[RESOLUTIONS_FIELD_NAME]: <PnpmOrYarnOverrides>overrides
143157
})
@@ -150,7 +164,7 @@ type AgentListDepsFn = (
150164
rootPath: string
151165
) => Promise<string>
152166

153-
const lsByAgent: Record<AgentPlusBun, AgentListDepsFn> = {
167+
const lsByAgent: Record<Agent, AgentListDepsFn> = {
154168
async bun(agentExecPath: string, cwd: string, _rootPath: string) {
155169
try {
156170
// Bun does not support filtering by production packages yet.
@@ -166,8 +180,10 @@ const lsByAgent: Record<AgentPlusBun, AgentListDepsFn> = {
166180
['ls', '--parseable', '--omit', 'dev', '--all'],
167181
{ cwd }
168182
)
183+
stdout = stdout.trim()
169184
stdout = stdout.replaceAll(cwd, '')
170-
return rootPath === cwd ? stdout : stdout.replaceAll(rootPath, '')
185+
stdout = rootPath === cwd ? stdout : stdout.replaceAll(rootPath, '')
186+
return stdout.replaceAll('\\', '/')
171187
} catch {}
172188
return ''
173189
},
@@ -178,12 +194,14 @@ const lsByAgent: Record<AgentPlusBun, AgentListDepsFn> = {
178194
['ls', '--parseable', '--prod', '--depth', 'Infinity'],
179195
{ cwd }
180196
)
197+
stdout = stdout.trim()
181198
stdout = stdout.replaceAll(cwd, '')
182-
return rootPath === cwd ? stdout : stdout.replaceAll(rootPath, '')
199+
stdout = rootPath === cwd ? stdout : stdout.replaceAll(rootPath, '')
200+
return stdout.replaceAll('\\', '/')
183201
} catch {}
184202
return ''
185203
},
186-
async yarn(agentExecPath: string, cwd: string, _rootPath: string) {
204+
async 'yarn/berry'(agentExecPath: string, cwd: string, _rootPath: string) {
187205
try {
188206
return (
189207
// Yarn Berry does not support filtering by production packages yet.
@@ -192,24 +210,33 @@ const lsByAgent: Record<AgentPlusBun, AgentListDepsFn> = {
192210
await spawn(agentExecPath, ['info', '--recursive', '--name-only'], {
193211
cwd
194212
})
195-
).stdout
213+
).stdout.trim()
196214
)
197215
} catch {}
216+
return ''
217+
},
218+
async 'yarn/classic'(agentExecPath: string, cwd: string, _rootPath: string) {
198219
try {
199220
// However, Yarn Classic does support it.
200-
return (await spawn(agentExecPath, ['list', '--prod'], { cwd })).stdout
221+
// https://github.com/yarnpkg/yarn/releases/tag/v1.0.0
222+
// > Fix: Excludes dev dependencies from the yarn list output when the
223+
// environment is production
224+
return (
225+
await spawn(agentExecPath, ['list', '--prod'], { cwd })
226+
).stdout.trim()
201227
} catch {}
202228
return ''
203229
}
204230
}
205231

206232
type AgentDepsIncludesFn = (stdout: string, name: string) => boolean
207233

208-
const depsIncludesByAgent: Record<AgentPlusBun, AgentDepsIncludesFn> = {
209-
bun: (stdout: string, name: string) => stdout.includes(name),
210-
npm: (stdout: string, name: string) => stdout.includes(name),
211-
pnpm: (stdout: string, name: string) => stdout.includes(name),
212-
yarn: (stdout: string, name: string) => stdout.includes(name)
234+
const depsIncludesByAgent: Record<Agent, AgentDepsIncludesFn> = {
235+
bun: (stdout: string, name: string) => stdout.includes(` ${name}@`),
236+
npm: (stdout: string, name: string) => stdout.includes(`/${name}\n`),
237+
pnpm: (stdout: string, name: string) => stdout.includes(`/${name}\n`),
238+
'yarn/berry': (stdout: string, name: string) => stdout.includes(` ${name}@`),
239+
'yarn/classic': (stdout: string, name: string) => stdout.includes(` ${name}@`)
213240
}
214241

215242
function getDependencyEntries(pkgJson: PackageJsonContent) {
@@ -242,7 +269,7 @@ function getDependencyEntries(pkgJson: PackageJsonContent) {
242269
}
243270

244271
async function getWorkspaces(
245-
agent: AgentPlusBun,
272+
agent: Agent,
246273
pkgPath: string,
247274
pkgJson: PackageJsonContent
248275
): Promise<string[] | undefined> {
@@ -288,7 +315,7 @@ function workspaceToGlobPattern(workspace: string): string {
288315
}
289316

290317
type AddOverridesConfig = {
291-
agent: AgentPlusBun
318+
agent: Agent
292319
agentExecPath: string
293320
lockSrc: string
294321
manifestEntries: ManifestEntry[]
@@ -301,6 +328,7 @@ type AddOverridesConfig = {
301328

302329
type AddOverridesState = {
303330
added: Set<string>
331+
spinner?: Ora | undefined
304332
updated: Set<string>
305333
}
306334

@@ -318,12 +346,14 @@ async function addOverrides(
318346
}: AddOverridesConfig,
319347
state: AddOverridesState = {
320348
added: new Set(),
349+
spinner: undefined,
321350
updated: new Set()
322351
}
323352
): Promise<AddOverridesState> {
324353
if (editablePkgJson === undefined) {
325354
editablePkgJson = await EditablePackageJson.load(pkgPath)
326355
}
356+
const { spinner } = state
327357
const pkgJson: Readonly<PackageJsonContent> = editablePkgJson.content
328358
const isRoot = pkgPath === rootPath
329359
const isLockScanned = isRoot && !prod
@@ -342,12 +372,12 @@ async function addOverrides(
342372
} else {
343373
overridesDataObjects.push(
344374
getOverridesDataByAgent['npm'](pkgJson),
345-
getOverridesDataByAgent['yarn'](pkgJson)
375+
getOverridesDataByAgent['yarn/classic'](pkgJson)
346376
)
347377
}
348-
const spinner = isRoot
349-
? ora('Fetching override manifests...').start()
350-
: undefined
378+
if (spinner) {
379+
spinner.text = `Adding overrides${isRoot ? '' : ` to ${path.relative(rootPath, pkgPath)}`}...`
380+
}
351381
const depAliasMap = new Map<string, { id: string; version: string }>()
352382
// Chunk package names to process them in parallel 3 at a time.
353383
await pEach(manifestEntries, 3, async ({ 1: data }) => {
@@ -428,21 +458,29 @@ async function addOverrides(
428458
workspaces.map(workspaceToGlobPattern),
429459
{
430460
absolute: true,
431-
cwd: pkgPath!
461+
cwd: pkgPath!,
462+
ignore: ['**/node_modules/**', '**/bower_components/**']
432463
}
433464
)
434465
// Chunk package names to process them in parallel 3 at a time.
435466
await pEach(wsPkgJsonPaths, 3, async wsPkgJsonPath => {
436-
const { added, updated } = await addOverrides({
437-
agent,
438-
agentExecPath,
439-
lockSrc,
440-
manifestEntries,
441-
pin,
442-
pkgPath: path.dirname(wsPkgJsonPath),
443-
prod,
444-
rootPath
445-
})
467+
const { added, updated } = await addOverrides(
468+
{
469+
agent,
470+
agentExecPath,
471+
lockSrc,
472+
manifestEntries,
473+
pin,
474+
pkgPath: path.dirname(wsPkgJsonPath),
475+
prod,
476+
rootPath
477+
},
478+
{
479+
added: new Set(),
480+
spinner,
481+
updated: new Set()
482+
}
483+
)
446484
for (const regPkgName of added) {
447485
state.added.add(regPkgName)
448486
}
@@ -451,8 +489,7 @@ async function addOverrides(
451489
}
452490
})
453491
}
454-
spinner?.stop()
455-
if (state.added.size || state.updated.size) {
492+
if (state.added.size > 0 || state.updated.size > 0) {
456493
editablePkgJson.update(<PackageJsonContent>Object.fromEntries(depEntries))
457494
for (const { overrides, type } of overridesDataObjects) {
458495
updateManifestByAgent[type](editablePkgJson, toSortedObject(overrides))
@@ -462,6 +499,34 @@ async function addOverrides(
462499
return state
463500
}
464501

502+
// type ExtractOptions = pacote.Options & {
503+
// tmpPrefix?: string
504+
// [key: string]: any
505+
// }
506+
507+
// async function extractPackage(pkgNameOrId: string, options: ExtractOptions | undefined, callback: (tmpDirPath: string) => any) {
508+
// if (arguments.length === 2 && typeof options === 'function') {
509+
// callback = options
510+
// options = undefined
511+
// }
512+
// const { tmpPrefix, ...extractOptions } = { __proto__: null, ...options }
513+
// // cacache.tmp.withTmp DOES return a promise.
514+
// await cacache.tmp.withTmp(
515+
// pacoteCachePath,
516+
// { tmpPrefix },
517+
// // eslint-disable-next-line @typescript-eslint/no-misused-promises
518+
// async tmpDirPath => {
519+
// await pacote.extract(pkgNameOrId, tmpDirPath, {
520+
// __proto__: null,
521+
// packumentCache,
522+
// preferOffline: true,
523+
// ...<Omit<typeof extractOptions, '__proto__'>>extractOptions
524+
// })
525+
// await callback(tmpDirPath)
526+
// }
527+
// )
528+
// }
529+
465530
type FetchPackageManifestOptions = {
466531
signal?: AbortSignal
467532
}
@@ -506,6 +571,7 @@ export const optimize: CliSubcommand = {
506571
const {
507572
agent,
508573
agentExecPath,
574+
agentVersion,
509575
lockSrc,
510576
lockPath,
511577
minimumNodeVersion,
@@ -535,15 +601,24 @@ export const optimize: CliSubcommand = {
535601
console.log(`✘ ${COMMAND_TITLE}: No package.json found`)
536602
return
537603
}
604+
if (prod && (agent === 'bun' || agent === 'yarn/berry')) {
605+
console.log(
606+
`✘ ${COMMAND_TITLE}: --prod not supported for ${agent}${agentVersion ? `@${agentVersion.toString()}` : ''}`
607+
)
608+
return
609+
}
538610
if (lockPath && path.relative(cwd, lockPath).startsWith('.')) {
539611
console.log(
540612
`⚠️ ${COMMAND_TITLE}: Package ${lockName} found at ${lockPath}`
541613
)
542614
}
615+
const spinner = ora('Socket optimizing...')
543616
const state: AddOverridesState = {
544617
added: new Set(),
618+
spinner,
545619
updated: new Set()
546620
}
621+
spinner.start()
547622
if (lockSrc) {
548623
const nodeRange = `>=${minimumNodeVersion}`
549624
const manifestEntries = manifestNpmOverrides.filter(({ 1: data }) =>
@@ -580,18 +655,23 @@ export const optimize: CliSubcommand = {
580655
if (isNpm || pkgJsonChanged) {
581656
// Always update package-lock.json until the npm overrides PR lands:
582657
// https://github.com/npm/cli/pull/7025
583-
const spinner = ora(`Updating ${lockName}...`).start()
658+
spinner.text = `Updating ${lockName}...`
584659
try {
585660
if (isNpm) {
586661
const wrapperPath = path.join(distPath, 'npm-cli.js')
587-
await spawn(process.execPath, [wrapperPath, 'install'], {
588-
stdio: 'pipe',
589-
env: {
590-
...process.env,
591-
UPDATE_SOCKET_OVERRIDES_IN_PACKAGE_LOCK_FILE: '1'
662+
await spawn(
663+
process.execPath,
664+
[wrapperPath, 'install', '--no-audit', '--no-fund'],
665+
{
666+
stdio: 'pipe',
667+
env: {
668+
...process.env,
669+
UPDATE_SOCKET_OVERRIDES_IN_PACKAGE_LOCK_FILE: '1'
670+
}
592671
}
593-
})
672+
)
594673
} else {
674+
// All package managers support the "install" command.
595675
await spawn(agentExecPath, ['install'], { stdio: 'pipe' })
596676
}
597677
spinner.stop()

0 commit comments

Comments
 (0)