Socket
Socket
Sign inDemoInstall

@npmcli/arborist

Package Overview
Dependencies
Maintainers
5
Versions
192
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@npmcli/arborist - npm Package Compare versions

Comparing version 5.6.0 to 5.6.1

1

bin/index.js

@@ -102,2 +102,3 @@ #!/usr/bin/env node

}
return r
})

@@ -104,0 +105,0 @@ }

131

lib/add-rm-pkg-deps.js

@@ -7,4 +7,63 @@ // add and remove dependency specs to/from pkg manifest

const add = ({ pkg, add, saveBundle, saveType }) => {
for (const spec of add) {
addSingle({ pkg, spec, saveBundle, saveType })
for (const { name, rawSpec } of add) {
// if the user does not give us a type, we infer which type(s)
// to keep based on the same order of priority we do when
// building the tree as defined in the _loadDeps method of
// the node class.
if (!saveType) {
saveType = inferSaveType(pkg, name)
}
if (saveType === 'prod') {
// a production dependency can only exist as production (rpj ensures it
// doesn't coexist w/ optional)
deleteSubKey(pkg, 'devDependencies', name, 'dependencies')
deleteSubKey(pkg, 'peerDependencies', name, 'dependencies')
} else if (saveType === 'dev') {
// a dev dependency may co-exist as peer, or optional, but not production
deleteSubKey(pkg, 'dependencies', name, 'devDependencies')
} else if (saveType === 'optional') {
// an optional dependency may co-exist as dev (rpj ensures it doesn't
// coexist w/ prod)
deleteSubKey(pkg, 'peerDependencies', name, 'optionalDependencies')
} else { // peer or peerOptional is all that's left
// a peer dependency may coexist as dev
deleteSubKey(pkg, 'dependencies', name, 'peerDependencies')
deleteSubKey(pkg, 'optionalDependencies', name, 'peerDependencies')
}
const depType = saveTypeMap.get(saveType)
pkg[depType] = pkg[depType] || {}
if (rawSpec !== '' || pkg[depType][name] === undefined) {
pkg[depType][name] = rawSpec || '*'
}
if (saveType === 'optional') {
// Affordance for previous npm versions that require this behaviour
pkg.dependencies = pkg.dependencies || {}
pkg.dependencies[name] = pkg.optionalDependencies[name]
}
if (saveType === 'peer' || saveType === 'peerOptional') {
const pdm = pkg.peerDependenciesMeta || {}
if (saveType === 'peer' && pdm[name] && pdm[name].optional) {
pdm[name].optional = false
} else if (saveType === 'peerOptional') {
pdm[name] = pdm[name] || {}
pdm[name].optional = true
pkg.peerDependenciesMeta = pdm
}
// peerDeps are often also a devDep, so that they can be tested when
// using package managers that don't auto-install peer deps
if (pkg.devDependencies && pkg.devDependencies[name] !== undefined) {
pkg.devDependencies[name] = pkg.peerDependencies[name]
}
}
if (saveBundle && saveType !== 'peer' && saveType !== 'peerOptional') {
// keep it sorted, keep it unique
const bd = new Set(pkg.bundleDependencies || [])
bd.add(name)
pkg.bundleDependencies = [...bd].sort(localeCompare)
}
}

@@ -25,67 +84,2 @@

const addSingle = ({ pkg, spec, saveBundle, saveType }) => {
const { name, rawSpec } = spec
// if the user does not give us a type, we infer which type(s)
// to keep based on the same order of priority we do when
// building the tree as defined in the _loadDeps method of
// the node class.
if (!saveType) {
saveType = inferSaveType(pkg, spec.name)
}
if (saveType === 'prod') {
// a production dependency can only exist as production (rpj ensures it
// doesn't coexist w/ optional)
deleteSubKey(pkg, 'devDependencies', name, 'dependencies')
deleteSubKey(pkg, 'peerDependencies', name, 'dependencies')
} else if (saveType === 'dev') {
// a dev dependency may co-exist as peer, or optional, but not production
deleteSubKey(pkg, 'dependencies', name, 'devDependencies')
} else if (saveType === 'optional') {
// an optional dependency may co-exist as dev (rpj ensures it doesn't
// coexist w/ prod)
deleteSubKey(pkg, 'peerDependencies', name, 'optionalDependencies')
} else { // peer or peerOptional is all that's left
// a peer dependency may coexist as dev
deleteSubKey(pkg, 'dependencies', name, 'peerDependencies')
deleteSubKey(pkg, 'optionalDependencies', name, 'peerDependencies')
}
const depType = saveTypeMap.get(saveType)
pkg[depType] = pkg[depType] || {}
if (rawSpec !== '' || pkg[depType][name] === undefined) {
pkg[depType][name] = rawSpec || '*'
}
if (saveType === 'optional') {
// Affordance for previous npm versions that require this behaviour
pkg.dependencies = pkg.dependencies || {}
pkg.dependencies[name] = pkg.optionalDependencies[name]
}
if (saveType === 'peer' || saveType === 'peerOptional') {
const pdm = pkg.peerDependenciesMeta || {}
if (saveType === 'peer' && pdm[name] && pdm[name].optional) {
pdm[name].optional = false
} else if (saveType === 'peerOptional') {
pdm[name] = pdm[name] || {}
pdm[name].optional = true
pkg.peerDependenciesMeta = pdm
}
// peerDeps are often also a devDep, so that they can be tested when
// using package managers that don't auto-install peer deps
if (pkg.devDependencies && pkg.devDependencies[name] !== undefined) {
pkg.devDependencies[name] = pkg.peerDependencies[name]
}
}
if (saveBundle && saveType !== 'peer' && saveType !== 'peerOptional') {
// keep it sorted, keep it unique
const bd = new Set(pkg.bundleDependencies || [])
bd.add(spec.name)
pkg.bundleDependencies = [...bd].sort(localeCompare)
}
}
// Finds where the package is already in the spec and infers saveType from that

@@ -108,5 +102,4 @@ const inferSaveType = (pkg, name) => {

const { hasOwnProperty } = Object.prototype
const hasSubKey = (pkg, depType, name) => {
return pkg[depType] && hasOwnProperty.call(pkg[depType], name)
return pkg[depType] && Object.prototype.hasOwnProperty.call(pkg[depType], name)
}

@@ -113,0 +106,0 @@

@@ -84,14 +84,7 @@ // mixin implementing the buildIdealTree method

const _globalRootNode = Symbol('globalRootNode')
const _isVulnerable = Symbol.for('isVulnerable')
const _usePackageLock = Symbol.for('usePackageLock')
const _rpcache = Symbol.for('realpathCache')
const _stcache = Symbol.for('statCache')
const _updateFilePath = Symbol('updateFilePath')
const _followSymlinkPath = Symbol('followSymlinkPath')
const _getRelpathSpec = Symbol('getRelpathSpec')
const _retrieveSpecName = Symbol('retrieveSpecName')
const _strictPeerDeps = Symbol('strictPeerDeps')
const _checkEngineAndPlatform = Symbol('checkEngineAndPlatform')
const _checkEngine = Symbol('checkEngine')
const _checkPlatform = Symbol('checkPlatform')
const _virtualRoots = Symbol('virtualRoots')

@@ -232,6 +225,18 @@ const _virtualRoot = Symbol('virtualRoot')

async [_checkEngineAndPlatform] () {
const { engineStrict, npmVersion, nodeVersion } = this.options
for (const node of this.idealTree.inventory.values()) {
if (!node.optional) {
this[_checkEngine](node)
this[_checkPlatform](node)
try {
checkEngine(node.package, npmVersion, nodeVersion, this[_force])
} catch (err) {
if (engineStrict) {
throw err
}
log.warn(err.code, err.message, {
package: err.pkgid,
required: err.required,
current: err.current,
})
}
checkPlatform(node.package, this[_force])
}

@@ -241,26 +246,2 @@ }

[_checkPlatform] (node) {
checkPlatform(node.package, this[_force])
}
[_checkEngine] (node) {
const { engineStrict, npmVersion, nodeVersion } = this.options
const c = () =>
checkEngine(node.package, npmVersion, nodeVersion, this[_force])
if (engineStrict) {
c()
} else {
try {
c()
} catch (er) {
log.warn(er.code, er.message, {
package: er.pkgid,
required: er.required,
current: er.current,
})
}
}
}
[_parseSettings] (options) {

@@ -384,2 +365,3 @@ const update = options.update === true ? { all: true }

process.emit('timeEnd', 'idealTree:init')
return tree
})

@@ -536,82 +518,57 @@ }

// This returns a promise because we might not have the name yet,
// and need to call pacote.manifest to find the name.
[_add] (tree, { add, saveType = null, saveBundle = false }) {
// This returns a promise because we might not have the name yet, and need to
// call pacote.manifest to find the name.
async [_add] (tree, { add, saveType = null, saveBundle = false }) {
// If we have a link it will need to be added relative to the target's path
const path = tree.target.path
// get the name for each of the specs in the list.
// ie, doing `foo@bar` we just return foo
// but if it's a url or git, we don't know the name until we
// fetch it and look in its manifest.
return Promise.all(add.map(async rawSpec => {
// We do NOT provide the path to npa here, because user-additions
// need to be resolved relative to the CWD the user is in.
const spec = await this[_retrieveSpecName](npa(rawSpec))
.then(spec => this[_updateFilePath](spec))
.then(spec => this[_followSymlinkPath](spec))
spec.tree = tree
return spec
})).then(add => {
this[_resolvedAdd].push(...add)
// now add is a list of spec objects with names.
// find a home for each of them!
addRmPkgDeps.add({
pkg: tree.package,
add,
saveBundle,
saveType,
path: this.path,
})
})
}
// ie, doing `foo@bar` we just return foo but if it's a url or git, we
// don't know the name until we fetch it and look in its manifest.
await Promise.all(add.map(async rawSpec => {
// We do NOT provide the path to npa here, because user-additions need to
// be resolved relative to the tree being added to.
let spec = npa(rawSpec)
async [_retrieveSpecName] (spec) {
// if it's just @'' then we reload whatever's there, or get latest
// if it's an explicit tag, we need to install that specific tag version
const isTag = spec.rawSpec && spec.type === 'tag'
// if it's just @'' then we reload whatever's there, or get latest
// if it's an explicit tag, we need to install that specific tag version
const isTag = spec.rawSpec && spec.type === 'tag'
if (spec.name && !isTag) {
return spec
}
// look up the names of file/directory/git specs
if (!spec.name || isTag) {
const mani = await pacote.manifest(spec, { ...this.options })
if (isTag) {
// translate tag to a version
spec = npa(`${mani.name}@${mani.version}`)
}
spec.name = mani.name
}
const mani = await pacote.manifest(spec, { ...this.options })
// if it's a tag type, then we need to run it down to an actual version
if (isTag) {
return npa(`${mani.name}@${mani.version}`)
}
spec.name = mani.name
return spec
}
async [_updateFilePath] (spec) {
if (spec.type === 'file') {
return this[_getRelpathSpec](spec, spec.fetchSpec)
}
return spec
}
async [_followSymlinkPath] (spec) {
if (spec.type === 'directory') {
const real = await (
realpath(spec.fetchSpec, this[_rpcache], this[_stcache])
const { name } = spec
if (spec.type === 'file') {
spec = npa(`file:${relpath(path, spec.fetchSpec).replace(/#/g, '%23')}`, path)
spec.name = name
} else if (spec.type === 'directory') {
try {
const real = await realpath(spec.fetchSpec, this[_rpcache], this[_stcache])
spec = npa(`file:${relpath(path, real).replace(/#/g, '%23')}`, path)
spec.name = name
} catch {
// TODO: create synthetic test case to simulate realpath failure
.catch(/* istanbul ignore next */() => null)
)
}
}
spec.tree = tree
this[_resolvedAdd].push(spec)
}))
return this[_getRelpathSpec](spec, real)
}
return spec
// now this._resolvedAdd is a list of spec objects with names.
// find a home for each of them!
addRmPkgDeps.add({
pkg: tree.package,
add: this[_resolvedAdd],
saveBundle,
saveType,
})
}
[_getRelpathSpec] (spec, filepath) {
/* istanbul ignore else - should also be covered by realpath failure */
if (filepath) {
const { name } = spec
const tree = this.idealTree.target
spec = npa(`file:${relpath(tree.path, filepath).replace(/#/g, '%23')}`, tree.path)
spec.name = name
}
return spec
}
// TODO: provide a way to fix bundled deps by exposing metadata about

@@ -694,6 +651,2 @@ // what's in the bundle at each published manifest. Without that, we

[_isVulnerable] (node) {
return this.auditReport && this.auditReport.isVulnerable(node)
}
[_avoidRange] (name) {

@@ -790,13 +743,14 @@ if (!this.auditReport) {

this.addTracker(t)
await pacote.manifest(spec, {
...this.options,
resolved: resolved,
integrity: integrity,
fullMetadata: false,
}).then(mani => {
try {
const mani = await pacote.manifest(spec, {
...this.options,
resolved: resolved,
integrity: integrity,
fullMetadata: false,
})
node.package = { ...mani, _id: `${mani.name}@${mani.version}` }
}).catch((er) => {
} catch (er) {
const warning = `Could not fetch metadata for ${name}@${id}`
log.warn(heading, warning, er)
})
}
this.finishTracker(t)

@@ -1243,3 +1197,3 @@ })

// fixing a security vulnerability with this package, problem
if (this[_isVulnerable](edge.to)) {
if (this.auditReport && this.auditReport.isVulnerable(edge.to)) {
return true

@@ -1246,0 +1200,0 @@ }

@@ -137,3 +137,3 @@ // The arborist manages three trees:

// returns a set of root dependencies, excluding depdencies that are
// returns a set of root dependencies, excluding dependencies that are
// exclusively workspace dependencies

@@ -140,0 +140,0 @@ excludeWorkspacesDependencySet (tree) {

@@ -350,2 +350,3 @@ // mix-in implementing the loadActual method

} else if (target.then) {
// eslint-disable-next-line promise/catch-or-return
target.then(node => link.target = node)

@@ -352,0 +353,0 @@ }

@@ -362,2 +362,5 @@ // Arborist.rebuild({path = this.path}) will do all the binlinks and

event,
// I do not know why this needs to be on THIS line but refactoring
// this function would be quite a process
// eslint-disable-next-line promise/always-return
cmd: args && args[args.length - 1],

@@ -364,0 +367,0 @@ env,

@@ -72,3 +72,2 @@ // mixin implementing the reify method

const _submitQuickAudit = Symbol('submitQuickAudit')
const _awaitQuickAudit = Symbol('awaitQuickAudit')
const _unpackNewModules = Symbol.for('unpackNewModules')

@@ -160,3 +159,4 @@ const _moveContents = Symbol.for('moveContents')

await this[_copyIdealToActual]()
await this[_awaitQuickAudit]()
// This is a very bad pattern and I can't wait to stop doing it
this.auditReport = await this.auditReport

@@ -536,8 +536,8 @@ this.finishTracker('reify')

.map(path => rimraf(path).catch(er => failures.push([path, er])))
return promiseAllRejectLate(unlinks)
.then(() => {
if (failures.length) {
log.warn('cleanup', 'Failed to remove some directories', failures)
}
})
return promiseAllRejectLate(unlinks).then(() => {
// eslint-disable-next-line promise/always-return
if (failures.length) {
log.warn('cleanup', 'Failed to remove some directories', failures)
}
})
.then(() => process.emit('timeEnd', 'reify:rollback:createSparse'))

@@ -598,17 +598,17 @@ .then(() => this[_rollbackRetireShallowNodes](er))

const { npmVersion, nodeVersion } = this.options
const p = Promise.resolve()
.then(async () => {
// when we reify an optional node, check the engine and platform
// first. be sure to ignore the --force and --engine-strict flags,
// since we always want to skip any optional packages we can't install.
// these checks throwing will result in a rollback and removal
// of the mismatches
if (node.optional) {
checkEngine(node.package, npmVersion, nodeVersion, false)
checkPlatform(node.package, false)
}
await this[_checkBins](node)
await this[_extractOrLink](node)
await this[_warnDeprecated](node)
})
const p = Promise.resolve().then(async () => {
// when we reify an optional node, check the engine and platform
// first. be sure to ignore the --force and --engine-strict flags,
// since we always want to skip any optional packages we can't install.
// these checks throwing will result in a rollback and removal
// of the mismatches
// eslint-disable-next-line promise/always-return
if (node.optional) {
checkEngine(node.package, npmVersion, nodeVersion, false)
checkPlatform(node.package, false)
}
await this[_checkBins](node)
await this[_extractOrLink](node)
await this[_warnDeprecated](node)
})

@@ -923,5 +923,6 @@ return this[_handleOptionalFailure](node, p)

[_submitQuickAudit] () {
async [_submitQuickAudit] () {
if (this.options.audit === false) {
return this.auditReport = null
this.auditReport = null
return
}

@@ -948,14 +949,8 @@

this.auditReport = AuditReport.load(tree, options)
.then(res => {
process.emit('timeEnd', 'reify:audit')
this.auditReport = res
})
this.auditReport = AuditReport.load(tree, options).then(res => {
process.emit('timeEnd', 'reify:audit')
return res
})
}
// return the promise if we're waiting for it, or the replaced result
[_awaitQuickAudit] () {
return this.auditReport
}
// ok! actually unpack stuff into their target locations!

@@ -1135,3 +1130,3 @@ // The sparse tree has already been created, so we walk the diff

// Thankfully, it's pretty unlikely that it'll fail, since rimraf is a tank.
[_removeTrash] () {
async [_removeTrash] () {
process.emit('time', 'reify:trash')

@@ -1146,8 +1141,7 @@ const promises = []

return promiseAllRejectLate(promises).then(() => {
if (failures.length) {
log.warn('cleanup', 'Failed to remove some directories', failures)
}
})
.then(() => process.emit('timeEnd', 'reify:trash'))
await promiseAllRejectLate(promises)
if (failures.length) {
log.warn('cleanup', 'Failed to remove some directories', failures)
}
process.emit('timeEnd', 'reify:trash')
}

@@ -1313,3 +1307,5 @@

}
} catch {}
} catch {
// ignore errors
}
}

@@ -1316,0 +1312,0 @@ return true

@@ -178,3 +178,5 @@ // an object representing the set of vulnerabilities in a tree

const calc = this.calculator.calculate(dep.packageName, advisory)
// eslint-disable-next-line promise/always-return
p.push(calc.then(meta => {
// eslint-disable-next-line promise/always-return
if (meta.testVersion(dep.version, spec)) {

@@ -181,0 +183,0 @@ advisories.add(meta)

@@ -69,2 +69,3 @@ const debug = require('./debug.js')

this[_target] = target
// eslint-disable-next-line promise/always-return, promise/catch-or-return
target.then(node => {

@@ -71,0 +72,0 @@ this[_target] = null

@@ -567,3 +567,4 @@ // inventory, path, realpath, root, and parent

if (!this.path || !root.realpath || !root.path) {
return this[_root] = root
this[_root] = root
return
}

@@ -570,0 +571,0 @@

@@ -187,30 +187,28 @@ // a module that manages a shrinkwrap file (npm-shrinkwrap.json or

return children.catch(() => [])
.then(ents => Promise.all(ents.map(async ent => {
const child = resolve(parent, ent.name)
if (ent.isDirectory() && !/^\./.test(ent.name)) {
await assertNoNewer(path, data, lockTime, child, seen)
} else if (ent.isSymbolicLink()) {
const target = resolve(parent, await readlink(child))
const tstat = await stat(target).catch(
/* istanbul ignore next - windows */ () => null)
seen.add(relpath(path, child))
/* istanbul ignore next - windows cannot do this */
if (tstat && tstat.isDirectory() && !seen.has(relpath(path, target))) {
await assertNoNewer(path, data, lockTime, target, seen)
}
const ents = await children.catch(() => [])
await Promise.all(ents.map(async ent => {
const child = resolve(parent, ent.name)
if (ent.isDirectory() && !/^\./.test(ent.name)) {
await assertNoNewer(path, data, lockTime, child, seen)
} else if (ent.isSymbolicLink()) {
const target = resolve(parent, await readlink(child))
const tstat = await stat(target).catch(
/* istanbul ignore next - windows */ () => null)
seen.add(relpath(path, child))
/* istanbul ignore next - windows cannot do this */
if (tstat && tstat.isDirectory() && !seen.has(relpath(path, target))) {
await assertNoNewer(path, data, lockTime, target, seen)
}
})))
.then(() => {
if (dir !== path) {
return
}
}
}))
if (dir !== path) {
return
}
// assert that all the entries in the lockfile were seen
for (const loc of new Set(Object.keys(data.packages))) {
if (!seen.has(loc)) {
throw 'missing from node_modules: ' + loc
}
}
})
// assert that all the entries in the lockfile were seen
for (const loc of new Set(Object.keys(data.packages))) {
if (!seen.has(loc)) {
throw 'missing from node_modules: ' + loc
}
}
}

@@ -265,3 +263,5 @@

}
} catch (e) {}
} catch {
// ignore errors
}

@@ -447,3 +447,3 @@ return s

load () {
async load () {
// we don't need to load package-lock.json except for top of tree nodes,

@@ -470,3 +470,5 @@ // only npm-shrinkwrap.json.

this.yarnLock.parse(yarn)
} catch (_) {}
} catch {
// ignore errors
}
}

@@ -522,4 +524,6 @@

// load old lockfile deps into the packages listing
// eslint-disable-next-line promise/always-return
if (lock.dependencies && !lock.packages) {
return rpj(this.path + '/package.json').then(pkg => pkg, er => ({}))
// eslint-disable-next-line promise/always-return
.then(pkg => {

@@ -526,0 +530,0 @@ this[_loadAll]('', null, this.data)

@@ -22,3 +22,5 @@ const signals = require('./signals.js')

process.removeListener(sig, sigListeners[sig])
} catch (er) {}
} catch {
// ignore errors
}
}

@@ -66,3 +68,5 @@ process.removeListener('beforeExit', onBeforeExit)

process.on(sig, sigListeners[sig])
} catch (er) {}
} catch {
// ignore errors
}
}

@@ -69,0 +73,0 @@ sigListeners.loaded = true

@@ -24,6 +24,8 @@ const npa = require('npm-package-arg')

}
} catch (_) { }
} catch {
// ignore errors
}
try {
return npa.resolve(name, lock.version, where)
} catch (_) {
} catch {
return {}

@@ -30,0 +32,0 @@ }

{
"name": "@npmcli/arborist",
"version": "5.6.0",
"version": "5.6.1",
"description": "Manage node_modules trees",

@@ -14,6 +14,6 @@ "dependencies": {

"@npmcli/package-json": "^2.0.0",
"@npmcli/query": "^1.1.1",
"@npmcli/query": "^1.2.0",
"@npmcli/run-script": "^4.1.3",
"bin-links": "^3.0.0",
"cacache": "^16.0.6",
"bin-links": "^3.0.3",
"cacache": "^16.1.3",
"common-ancestor-path": "^1.0.1",

@@ -28,3 +28,3 @@ "json-parse-even-better-errors": "^2.3.1",

"npm-package-arg": "^9.0.0",
"npm-pick-manifest": "^7.0.0",
"npm-pick-manifest": "^7.0.2",
"npm-registry-fetch": "^13.0.0",

@@ -46,4 +46,4 @@ "npmlog": "^6.0.2",

"devDependencies": {
"@npmcli/eslint-config": "^3.0.1",
"@npmcli/template-oss": "3.5.0",
"@npmcli/eslint-config": "^3.1.0",
"@npmcli/template-oss": "3.8.0",
"benchmark": "^2.1.4",

@@ -109,4 +109,4 @@ "chalk": "^4.1.0",

"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
"version": "3.5.0"
"version": "3.8.0"
}
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc