diff --git a/src/commands/optimize.ts b/src/commands/optimize.ts index 55d27277..be9f5ae5 100644 --- a/src/commands/optimize.ts +++ b/src/commands/optimize.ts @@ -44,6 +44,7 @@ type PackageJson = Awaited> const { BUN, + LOCK_EXT, NPM, PNPM, UPDATE_SOCKET_OVERRIDES_IN_PACKAGE_LOCK_FILE, @@ -136,10 +137,13 @@ const lockIncludesByAgent: Record = (() => { return { [BUN](lockSrc: string, name: string, lockBasename?: string) { - return (lockBasename === '.lock' ? npmLockIncludes : yarnLockIncludes)( - lockSrc, - name - ) + // This is a bit counterintuitive. When lockBasename ends with a .lockb + // we treat it as a yarn.lock. When lockBasename ends with a .lock we + // treat it as a package-lock.json. The bun.lock format is not identical + // package-lock.json, however it close enough for npmLockIncludes to work. + const lockScanner = + lockBasename?.endsWith(LOCK_EXT) ? npmLockIncludes : yarnLockIncludes + return lockScanner(lockSrc, name) }, [NPM]: npmLockIncludes, [PNPM](lockSrc: string, name: string) { @@ -659,6 +663,10 @@ async function addOverrides( const thingToScan = isLockScanned ? lockSrc : await lsByAgent[agent](agentExecPath, pkgPath, { npmExecPath }) + // The AgentDepsIncludesFn and AgentLockIncludesFn types overlap in their + // first two parameters. AgentLockIncludesFn accepts an optional third + // parameter which AgentDepsIncludesFn will ignore so we cast thingScanner + // as an AgentLockIncludesFn type. const thingScanner = ( (isLockScanned ? lockIncludesByAgent[agent] : depsIncludesByAgent[agent]) ) diff --git a/src/constants.ts b/src/constants.ts index 19253046..441269db 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -9,11 +9,13 @@ type RegistryEnv = typeof registryConstants.ENV type Constants = { readonly API_V0_URL: 'https://api.socket.dev/v0' readonly BABEL_RUNTIME: '@babel/runtime' + readonly BINARY_LOCK_EXT: '.lockb' readonly BUN: 'bun' readonly ENV: RegistryEnv & { UPDATE_SOCKET_OVERRIDES_IN_PACKAGE_LOCK_FILE: boolean } readonly DIST_TYPE: 'module-sync' | 'require' + readonly LOCK_EXT: '.lock' readonly NPM_REGISTRY_URL: 'https://registry.npmjs.org' readonly NPX: 'npx' readonly PNPM: 'pnpm' @@ -43,7 +45,9 @@ const { const API_V0_URL = 'https://api.socket.dev/v0' const BABEL_RUNTIME = '@babel/runtime' +const BINARY_LOCK_EXT = '.lockb' const BUN = 'bun' +const LOCK_EXT = '.lock' const NPM_REGISTRY_URL = 'https://registry.npmjs.org' const NPX = 'npx' const PNPM = 'pnpm' @@ -105,10 +109,12 @@ const constants = createConstantsObject( { API_V0_URL, BABEL_RUNTIME, + BINARY_LOCK_EXT, BUN, ENV: undefined, // Lazily defined values are initialized as `undefined` to keep their key order. DIST_TYPE: undefined, + LOCK_EXT, NPM_REGISTRY_URL, NPX, PNPM, diff --git a/src/utils/package-manager-detector.ts b/src/utils/package-manager-detector.ts index 621940ba..b60fbf46 100644 --- a/src/utils/package-manager-detector.ts +++ b/src/utils/package-manager-detector.ts @@ -16,7 +16,16 @@ import { existsSync, findUp, readFileBinary, readFileUtf8 } from './fs' import type { EditablePackageJson } from '@socketsecurity/registry/lib/packages' import type { SemVer } from 'semver' -const { BUN, NPM, PNPM, VLT, YARN_BERRY, YARN_CLASSIC } = constants +const { + BINARY_LOCK_EXT, + BUN, + LOCK_EXT, + NPM, + PNPM, + VLT, + YARN_BERRY, + YARN_CLASSIC +} = constants export const AGENTS = [BUN, NPM, PNPM, YARN_BERRY, YARN_CLASSIC, VLT] as const export type Agent = (typeof AGENTS)[number] @@ -46,9 +55,10 @@ async function getAgentVersion( return result } +// The order of LOCKS properties IS significant as it affects iteration order. const LOCKS: Record = { - 'bun.lock': BUN, - 'bun.lockb': BUN, + [`bun${LOCK_EXT}`]: BUN, + [`bun${BINARY_LOCK_EXT}`]: BUN, // If both package-lock.json and npm-shrinkwrap.json are present in the root // of a project, npm-shrinkwrap.json will take precedence and package-lock.json // will be ignored. @@ -57,9 +67,9 @@ const LOCKS: Record = { 'package-lock.json': NPM, 'pnpm-lock.yaml': PNPM, 'pnpm-lock.yml': PNPM, - 'yarn.lock': YARN_CLASSIC, + [`yarn${LOCK_EXT}`]: YARN_CLASSIC, 'vlt-lock.json': VLT, - // Look for a hidden lock file if .npmrc has package-lock=false: + // Lastly, look for a hidden lock file which is present if .npmrc has package-lock=false: // https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json#hidden-lockfiles // // Unlike the other LOCKS keys this key contains a directory AND filename so @@ -92,10 +102,10 @@ const readLockFileByAgent: Record = (() => { return { [BUN]: wrapReader(async (lockPath: string, agentExecPath: string) => { const ext = path.extname(lockPath) - if (ext === '.lock') { + if (ext === LOCK_EXT) { return await defaultReader(lockPath) } - if (ext === '.lockb') { + if (ext === BINARY_LOCK_EXT) { const lockBuffer = await binaryReader(lockPath) if (lockBuffer) { try { @@ -107,6 +117,7 @@ const readLockFileByAgent: Record = (() => { // https://bun.sh/guides/install/yarnlock return (await spawn(agentExecPath, [lockPath])).stdout.trim() } + return undefined }), [NPM]: defaultReader, [PNPM]: defaultReader,