Skip to content

Commit

Permalink
Add optimize support for bun.lock files
Browse files Browse the repository at this point in the history
  • Loading branch information
jdalton committed Dec 25, 2024
1 parent 9e97577 commit 8f1c4bc
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 38 deletions.
40 changes: 29 additions & 11 deletions src/commands/optimize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,19 @@ const getOverridesDataByAgent: Record<Agent, GetOverrides> = {
}
}

type AgentLockIncludesFn = (lockSrc: string, name: string) => boolean
type AgentLockIncludesFn = (
lockSrc: string,
name: string,
ext?: string
) => boolean

const lockIncludesByAgent: Record<Agent, AgentLockIncludesFn> = (() => {
function npmLockIncludes(lockSrc: string, name: string) {
// Detects the package name in the following cases:
// "name":
return lockSrc.includes(`"${name}":`)
}

function yarnLockIncludes(lockSrc: string, name: string) {
const escapedName = escapeRegExp(name)
return new RegExp(
Expand All @@ -125,12 +135,13 @@ const lockIncludesByAgent: Record<Agent, AgentLockIncludesFn> = (() => {
}

return {
[BUN]: yarnLockIncludes,
[NPM](lockSrc: string, name: string) {
// Detects the package name in the following cases:
// "name":
return lockSrc.includes(`"${name}":`)
[BUN](lockSrc: string, name: string, lockBasename?: string) {
return (lockBasename === '.lock' ? npmLockIncludes : yarnLockIncludes)(
lockSrc,
name
)
},
[NPM]: npmLockIncludes,
[PNPM](lockSrc: string, name: string) {
const escapedName = escapeRegExp(name)
return new RegExp(
Expand Down Expand Up @@ -576,6 +587,7 @@ function workspacePatternToGlobPattern(workspace: string): string {
type AddOverridesConfig = {
agent: Agent
agentExecPath: string
lockBasename: string
lockSrc: string
manifestEntries: ManifestEntry[]
npmExecPath: string
Expand Down Expand Up @@ -611,6 +623,7 @@ async function addOverrides(
{
agent,
agentExecPath,
lockBasename,
lockSrc,
manifestEntries,
npmExecPath,
Expand Down Expand Up @@ -646,9 +659,9 @@ async function addOverrides(
const thingToScan = isLockScanned
? lockSrc
: await lsByAgent[agent](agentExecPath, pkgPath, { npmExecPath })
const thingScanner = isLockScanned
? lockIncludesByAgent[agent]
: depsIncludesByAgent[agent]
const thingScanner = <AgentLockIncludesFn>(
(isLockScanned ? lockIncludesByAgent[agent] : depsIncludesByAgent[agent])
)
const depEntries = getDependencyEntries(pkgJson)

const overridesDataObjects = <GetOverridesResult[]>[]
Expand Down Expand Up @@ -698,7 +711,10 @@ async function addOverrides(
// Chunk package names to process them in parallel 3 at a time.
await pEach(overridesDataObjects, 3, async ({ overrides, type }) => {
const overrideExists = hasOwn(overrides, origPkgName)
if (overrideExists || thingScanner(thingToScan, origPkgName)) {
if (
overrideExists ||
thingScanner(thingToScan, origPkgName, lockBasename)
) {
const oldSpec = overrideExists ? overrides[origPkgName] : undefined
const depAlias = depAliasMap.get(origPkgName)
const regSpecStartsLike = `${NPM}:${regPkgName}@`
Expand Down Expand Up @@ -803,6 +819,7 @@ export const optimize: CliSubcommand = {
agent,
agentExecPath,
agentVersion,
lockBasename,
lockPath,
lockSrc,
minimumNodeVersion,
Expand Down Expand Up @@ -830,7 +847,7 @@ export const optimize: CliSubcommand = {
)
return
}
const lockName = lockPath ? path.basename(lockPath) : 'lock file'
const lockName = lockPath ? lockBasename : 'lock file'
if (lockSrc === undefined) {
console.error(`✖️ ${COMMAND_TITLE}: No ${lockName} found`)
return
Expand Down Expand Up @@ -865,6 +882,7 @@ export const optimize: CliSubcommand = {
{
agent,
agentExecPath,
lockBasename,
lockSrc,
manifestEntries,
npmExecPath,
Expand Down
62 changes: 35 additions & 27 deletions src/utils/package-manager-detector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ async function getAgentVersion(
}

const LOCKS: Record<string, Agent> = {
'bun.lock': BUN,
'bun.lockb': 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
Expand All @@ -66,43 +67,46 @@ const LOCKS: Record<string, Agent> = {
'node_modules/.package-lock.json': NPM
}

type ReadLockFile = (
lockPath: string,
agentExecPath: string
) => Promise<string | undefined>
type ReadLockFile =
| ((lockPath: string) => Promise<string | undefined>)
| ((lockPath: string, agentExecPath: string) => Promise<string | undefined>)

const readLockFileByAgent: Record<Agent, ReadLockFile> = (() => {
function wrapReader(
reader: (
lockPath: string,
agentExecPath: string
) => Promise<string | undefined>
): ReadLockFile {
return async (lockPath: string, agentExecPath: string) => {
function wrapReader<T extends (...args: any[]) => Promise<any>>(
reader: T
): (...args: Parameters<T>) => Promise<Awaited<ReturnType<T>> | undefined> {
return async (...args: any[]): Promise<any> => {
try {
return await reader(lockPath, agentExecPath)
return await reader(...args)
} catch {}
return undefined
}
}

const binaryReader = wrapReader(readFileBinary)

const defaultReader = wrapReader(
async (lockPath: string) => await readFileUtf8(lockPath)
)

return {
[BUN]: wrapReader(async (lockPath: string, agentExecPath: string) => {
let lockBuffer: Buffer | undefined
try {
lockBuffer = <Buffer>await readFileBinary(lockPath)
} catch {
return undefined
const ext = path.extname(lockPath)
if (ext === '.lock') {
return await defaultReader(lockPath)
}
if (ext === '.lockb') {
const lockBuffer = await binaryReader(lockPath)
if (lockBuffer) {
try {
return parseBunLockb(lockBuffer)
} catch {}
}
// To print a Yarn lockfile to your console without writing it to disk
// use `bun bun.lockb`.
// https://bun.sh/guides/install/yarnlock
return (await spawn(agentExecPath, [lockPath])).stdout.trim()
}
try {
return <string>parseBunLockb(lockBuffer)
} catch {}
// To print a Yarn lockfile to your console without writing it to disk
// use `bun bun.lockb`.
// https://bun.sh/guides/install/yarnlock
return (await spawn(agentExecPath, [lockPath])).stdout.trim()
}),
[NPM]: defaultReader,
[PNPM]: defaultReader,
Expand All @@ -121,6 +125,7 @@ export type DetectResult = Readonly<{
agent: Agent
agentExecPath: string
agentVersion: SemVer | undefined
lockBasename: string | undefined
lockPath: string | undefined
lockSrc: string | undefined
minimumNodeVersion: string
Expand All @@ -139,7 +144,8 @@ export async function detect({
onUnknown
}: DetectOptions = {}): Promise<DetectResult> {
let lockPath = await findUp(Object.keys(LOCKS), { cwd })
const isHiddenLockFile = lockPath?.endsWith('.package-lock.json') ?? false
let lockBasename = lockPath ? path.basename(lockPath) : undefined
const isHiddenLockFile = lockBasename === '.package-lock.json'
const pkgJsonPath = lockPath
? path.resolve(lockPath, `${isHiddenLockFile ? '../' : ''}../package.json`)
: await findUp('package.json', { cwd })
Expand Down Expand Up @@ -173,9 +179,9 @@ export async function detect({
agent === undefined &&
!isHiddenLockFile &&
typeof pkgJsonPath === 'string' &&
typeof lockPath === 'string'
typeof lockBasename === 'string'
) {
agent = <Agent>LOCKS[path.basename(lockPath)]
agent = <Agent>LOCKS[lockBasename]
}
if (agent === undefined) {
agent = NPM
Expand Down Expand Up @@ -238,12 +244,14 @@ export async function detect({
? await readLockFileByAgent[agent](lockPath, agentExecPath)
: undefined
} else {
lockBasename = undefined
lockPath = undefined
}
return <DetectResult>{
agent,
agentExecPath,
agentVersion,
lockBasename,
lockPath,
lockSrc,
minimumNodeVersion,
Expand Down

0 comments on commit 8f1c4bc

Please sign in to comment.