Last active
February 6, 2024 00:31
-
-
Save cau777/b08ca0d164e6e5df8ecdbab1187fe45d to your computer and use it in GitHub Desktop.
Simple TS script that reads patches from `pnpm audit` and manually applies them to package.json overrides, so that `pnpm` recognizes them properly. Meant to be used with `ts-node`
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { readFileSync, writeFileSync } from 'fs' | |
import semver from 'semver' | |
import { exec } from 'child_process' | |
// Currently, pnpm (version 8.5.1) is not recognizing overrides properly on install | |
// This is a temporary solution that simplifies the overrides generated by pnpm audit -P --json | |
// It basically gets rid of the vulnerable version specifier | |
interface SimplifiedPackageConfig { | |
pnpm?: { | |
overrides?: Record<string, string> | |
} | |
} | |
interface SimplifiedAudit { | |
advisories: Record< | |
string, | |
{ | |
module_name: string | |
patched_versions: string | |
} | |
> | |
} | |
const extractModuleName = (selector: string) => { | |
// Taking into account packages that start with @ like @babel/core | |
const separation = selector.lastIndexOf('@') | |
if (separation === 0 || separation === -1) return selector | |
return selector.substring(0, separation) | |
} | |
const chooseGreaterVersion = (oldVersion: string | undefined, newVersion: string) => { | |
if (!oldVersion) return newVersion | |
// If a range is passed to minVersion, it returns the minimum version that satisfies it | |
// Otherwise, the version itself is returned | |
const oldMin = semver.minVersion(oldVersion) | |
const newMin = semver.minVersion(newVersion) | |
// This shouldn't happen since pnpm is generating those versions | |
if (!oldMin) throw new Error(`Could not parse version ${oldMin}`) | |
if (!newMin) throw new Error(`Could not parse version ${newMin}`) | |
return semver.gt(newMin, oldMin) ? newVersion : oldVersion | |
} | |
const main = async () => { | |
const configJson = readFileSync('./package.json').toString() | |
const config = JSON.parse(configJson) as SimplifiedPackageConfig | |
const overrides: Record<string, string> = config.pnpm?.overrides ?? {} | |
const countBefore = Object.entries(overrides).length | |
const auditJson = await new Promise<string>((res, rej) => { | |
exec('pnpm audit --json -P', (error, output) => { | |
// Default behavior when there are vulnerabilities | |
if (!error || error.code === 1) res(output) | |
else rej(error) | |
}) | |
}) | |
const audit = JSON.parse(auditJson) as SimplifiedAudit | |
const entries = Object.values(audit.advisories) | |
console.log(`Processing ${entries.length} overrides`) | |
// Only process new overrides to avoid changing dependencies that are working | |
entries.forEach(({ module_name: moduleName, patched_versions: patchedVersions }) => { | |
// This means that there are no patch available | |
if (patchedVersions === '<0.0.0') return | |
const name = extractModuleName(moduleName) | |
const oldVersion = overrides[name] | |
overrides[name] = chooseGreaterVersion(oldVersion, patchedVersions) | |
}) | |
config.pnpm ??= {} | |
config.pnpm.overrides = overrides | |
// Add newline so prettier does not complain | |
writeFileSync('./package.json', `${JSON.stringify(config, undefined, 2)}\n`) | |
console.log( | |
`Successfully added ${ | |
Object.entries(overrides).length - countBefore | |
} overrides. Run pnpm install to apply the changes` | |
) | |
} | |
if (require.main === module) { | |
main().then(() => { | |
process.exit(0); | |
}, (err) => { | |
console.error(err) | |
process.exit(1) | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment