src/main/ts/lockfile/v2.ts
import {
TAuditReport,
TFlags,
TLockfileEntry,
TLockfileObject,
} from '../ifaces'
import { addHiddenProp, formatFlags, formatYaml, invoke, mapFlags, parseYaml, sortObject } from '../util'
export const parse = (raw: string): TLockfileObject => {
const data = parseYaml(raw)
const {__metadata} = data
delete data.__metadata
return Object.entries(data).reduce<Record<string, any>>(
(m, [key, value]: [string, any]) => {
key.split(', ').forEach((k) => {
m[k] = value
})
return m
},
addHiddenProp({}, '__metadata', __metadata),
)
}
export const patchEntry = (
entry: TLockfileEntry,
name: string,
newVersion: string,
npmBin: string,
): TLockfileEntry => {
entry.version = newVersion
entry.resolution = `${name}@npm:${newVersion}`
// NOTE seems like deps are not updated by `yarn mode='--update-lockfile'`, only checksums
entry.dependencies =
sortObject(JSON.parse(
invoke(
npmBin,
['view', `${name}@${newVersion}`, 'dependencies', '--json'],
process.cwd(),
true,
false,
) || 'null',
) || undefined)
delete entry.checksum
return entry
}
export const format = (lockfile: TLockfileObject): string => {
const keymap = Object.entries(lockfile).reduce<Record<string, any>>(
(m, [k, { resolution }]) => {
const entry = m[resolution] || (m[resolution] = [])
entry.push(k)
return m
},
{},
)
const data = Object.values(lockfile).reduce<Record<string, any>>(
(m, value) => {
const key = keymap[value.resolution].join(', ')
m[key] = value
return m
},
{
__metadata: lockfile.__metadata || {
version: 5,
cacheKey: 8,
},
},
)
return `# This file is generated by running "yarn install" inside your project.
# Manual changes might be lost - proceed with caution!
${formatYaml(data, {
quotingType: '"',
flowLevel: -1,
lineWidth: -1,
})
.replace(/\n([^\s"].+):\n/g, '\n"$1":\n')
.replace(/\n(\S)/g, '\n\n$1')
.replace(/resolution: ([^\n"]+)/g, 'resolution: "$1"')}`
}
export const audit = (
flags: TFlags,
temp: string,
bins: Record<string, string>,
): TAuditReport => {
const mapping = {
'audit-level': 'severity',
level: 'severity',
groups: {
key: 'environment',
values: {
dependencies: 'production',
},
},
only: {
key: 'environment',
values: {
prod: 'production',
},
},
}
const _flags = formatFlags(
mapFlags(flags, mapping),
'exclude',
'ignore',
'groups',
'verbose',
)
const report = invoke(
bins.yarn,
['npm', 'audit', '--all', '--json', '--recursive', ..._flags],
temp,
!!flags.silent,
false,
false,
)
return parseAuditReport(report)
}
export const parseAuditReport = (data: string): TAuditReport =>
Object.values(JSON.parse(data).advisories).reduce<TAuditReport>(
(m, { vulnerable_versions, module_name, patched_versions }: any) => {
m[module_name] = {
patched_versions,
vulnerable_versions,
module_name,
}
return m
},
{},
)