Socket
Socket
Sign inDemoInstall

@npmcli/arborist

Package Overview
Dependencies
Maintainers
7
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 0.0.0-pre.9 to 0.0.0-pre.10

74

lib/arborist/build-ideal-tree.js

@@ -60,3 +60,2 @@ // mixin implementing the buildIdealTree method

const _add = Symbol('add')
const _explicitRequests = Symbol('explicitRequests')
const _queueNamedUpdates = Symbol('queueNamedUpdates')

@@ -69,4 +68,8 @@ const _shouldUpdateNode = Symbol('shouldUpdateNode')

const _follow = Symbol('follow')
const _globalStyle = Symbol('globalStyle')
const _globalRootNode = Symbol('globalRootNode')
// used by Reify mixin
const _explicitRequests = Symbol.for('explicitRequests')
const _global = Symbol.for('global')
const _idealTreePrune = Symbol.for('idealTreePrune')

@@ -86,5 +89,13 @@ const _resolvedAdd = Symbol.for('resolvedAdd')

const {
idealTree = null,
global = false,
follow = false,
globalStyle = false,
} = options
this.idealTree = options.idealTree
this[_follow] = !!options.follow
this[_globalStyle] = this[_global] || globalStyle
this[_follow] = !!follow

@@ -110,2 +121,5 @@ this[_explicitRequests] = new Set()

if (!options.add && !options.rm && this[_global])
return Promise.reject(new Error('global requires an add or rm option'))
// first get the virtual tree, if possible. If there's a lockfile, then

@@ -162,4 +176,9 @@ // that defines the ideal tree, unless the root package.json is not

[_initTree] () {
return rpj(this.path + '/package.json')
.then(pkg => this[_rootNodeFromPackage](pkg))
return (
this[_global] ? this[_globalRootNode]()
: rpj(this.path + '/package.json')
.then(
pkg => this[_rootNodeFromPackage](pkg),
er => this[_rootNodeFromPackage]({})
))
// ok to not have a virtual tree. probably initial install.

@@ -169,7 +188,7 @@ // When updating all, we load the shrinkwrap, but don't bother

// reconstructing it anyway.
.then(root => this[_updateAll]
? Shrinkwrap.load({ path: this.path }).then(meta => {
meta.reset()
root.meta = meta
return root
.then(root => this[_global] ? root
: this[_updateAll] ? Shrinkwrap.load({ path: this.path }).then(meta => {
meta.reset()
root.meta = meta
return root
})

@@ -186,2 +205,13 @@ : this.loadVirtual({ root }))

[_globalRootNode] () {
const root = this[_rootNodeFromPackage]({})
// this is a gross kludge to handle the fact that we don't save
// metadata on the root node in global installs, because the "root"
// node is something like /usr/local/lib/node_modules.
const meta = new Shrinkwrap({ path: this.path })
meta.reset()
root.meta = meta
return Promise.resolve(root)
}
[_rootNodeFromPackage] (pkg) {

@@ -196,2 +226,3 @@ return new Node({

optional: false,
global: this[_global],
})

@@ -209,4 +240,7 @@ }

if (options.rm && options.rm.length)
if (options.rm && options.rm.length) {
addRmPkgDeps.rm(this.idealTree.package, options.rm)
for (const name of options.rm)
this[_explicitRequests].add(name)
}

@@ -223,4 +257,4 @@ // triggers a refresh of all edgesOut

// add => might return promise
// might not have name, call pacote.manifest (find name)
// This returns a promise because we might not have the name yet,
// and need to call pacote.manifest to find the name.
[_add] (add) {

@@ -234,2 +268,6 @@ const promises = []

for (const [type, specs] of Object.entries(add)) {
// 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.
const p = Promise.all(specs.map(s => {

@@ -251,2 +289,8 @@ const spec = npa(s, this.path)

} else {
// XXX this is a bit inefficient. we parse the spec, and then
// immediately throw it away and keep just the rawSpec. Later,
// when creating the Node, we parse them AGAIN for the Edge objs
// and multiple other times when checking if a dep is valid.
// It'd be eleganter to parse it once, and keep the spec around as
// an object for all future uses, even though npa is pretty fast.
this[_resolvedAdd][type] = this[_resolvedAdd][type] || {}

@@ -259,2 +303,3 @@ this[_resolvedAdd][type][spec.name] = spec.rawSpec

}
return Promise.all(promises).then(() => {

@@ -542,2 +587,7 @@ // get the list of deps that we're explicitly requesting, so that

break
// when installing globally, or just in global style, we never place
// deps above the first level.
if (this[_globalStyle] && check.resolveParent === this.idealTree)
break
}

@@ -544,0 +594,0 @@

76

lib/arborist/load-actual.js

@@ -15,8 +15,8 @@ // mix-in implementing the loadActual method

const loadFSNode = Symbol('loadFSNode')
const newNode = Symbol('newNode')
const newLink = Symbol('newLink')
const loadFSTree = Symbol('loadFSTree')
const loadFSChildren = Symbol('loadFSChildren')
const findFSParents = Symbol('findFSParents')
const _loadFSNode = Symbol('loadFSNode')
const _newNode = Symbol('newNode')
const _newLink = Symbol('newLink')
const _loadFSTree = Symbol('loadFSTree')
const _loadFSChildren = Symbol('loadFSChildren')
const _findFSParents = Symbol('findFSParents')

@@ -31,2 +31,5 @@ const _actualTreeLoaded = Symbol('actualTreeLoaded')

const _filter = Symbol('filter')
const _global = Symbol.for('global')
module.exports = cls => class ActualLoader extends cls {

@@ -36,2 +39,4 @@ constructor (options) {

this[_global] = !!options.global
// the tree of nodes on disk

@@ -64,3 +69,3 @@ this.actualTree = options.actualTree

// public method
loadActual () {
loadActual (options = {}) {
// mostly realpath to throw if the root doesn't exist

@@ -70,4 +75,19 @@ if (this.actualTree)

const { global = false, filter = () => true } = options
this[_filter] = filter
if (global) {
return realpath(this.path, this[_rpcache], this[_stcache])
.then(real => this[this.path === real ? _newNode : _newLink]({
path: this.path,
realpath: real,
pkg: {},
global,
})).then(node => {
this.actualTree = node
return this[_loadActualActually]()
})
}
return realpath(this.path, this[_rpcache], this[_stcache])
.then(real => this[loadFSNode]({ path: this.path, real }))
.then(real => this[_loadFSNode]({ path: this.path, real }))
.then(node => {

@@ -80,2 +100,3 @@ // XXX only rely on this if the hidden lockfile is the newest thing?

this.actualTree = node
return Shrinkwrap.load({

@@ -116,4 +137,4 @@ path: node.realpath,

// important when a link points at a node we end up visiting later.
return this[loadFSTree](this.actualTree)
.then(() => this[findFSParents]())
return this[_loadFSTree](this.actualTree)
.then(() => this[_findFSParents]())
.then(() => calcDepFlags(this.actualTree))

@@ -123,7 +144,7 @@ .then(() => this.actualTree)

[loadFSNode] ({ path, parent, real, root }) {
[_loadFSNode] ({ path, parent, real, root }) {
if (!real)
return realpath(path, this[_rpcache], this[_stcache])
.then(
real => this[loadFSNode]({ path, parent, real, root }),
real => this[_loadFSNode]({ path, parent, real, root }),
// if realpath fails, just provide a dummy error node

@@ -150,3 +171,3 @@ error => new Node({ error, path, realpath: path, parent, root })

.then(([pkg, error]) => {
return this[path === real ? newNode : newLink]({
return this[path === real ? _newNode : _newLink]({
path,

@@ -175,3 +196,3 @@ realpath: real,

// Hence this kludge.
[newNode] (options) {
[_newNode] (options) {
// check it for an fsParent if it's a tree top. there's a decent chance

@@ -188,3 +209,3 @@ // it'll get parented later, making the fsParent scan a no-op, but better

[newLink] (options) {
[_newLink] (options) {
const { realpath } = options

@@ -203,4 +224,4 @@ this[_linkTargets].add(realpath)

if (nmParent) {
return this[loadFSNode]({ path: nmParent, root: link.root })
.then(node => this[loadFSTree](node))
return this[_loadFSNode]({ path: nmParent, root: link.root })
.then(node => this[_loadFSTree](node))
.then(() => link)

@@ -214,3 +235,3 @@ }

[loadFSTree] (node) {
[_loadFSTree] (node) {
const did = this[_actualTreeLoaded]

@@ -222,3 +243,3 @@ node = node.target || node

if (node.then)
return node.then(node => this[loadFSTree](node))
return node.then(node => this[_loadFSTree](node))

@@ -231,7 +252,7 @@ // impossible except in pathological ELOOP cases

did.add(node.realpath)
return this[loadFSChildren](node)
return this[_loadFSChildren](node)
.then(() => Promise.all(
[...node.children.entries()]
.filter(([name, kid]) => !did.has(kid.realpath))
.map(([name, kid]) => this[loadFSTree](kid))))
.map(([name, kid]) => this[_loadFSTree](kid))))
}

@@ -241,3 +262,3 @@

// and attach them to the node as a parent
[loadFSChildren] (node) {
[_loadFSChildren] (node) {
const nm = resolve(node.realpath, 'node_modules')

@@ -248,6 +269,7 @@ return readdir(nm).then(kids => {

kids.filter(kid => !/^(@[^/]+\/)?\./.test(kid))
.map(kid => this[loadFSNode]({
parent: node,
path: resolve(nm, kid),
})))
.filter(kid => this[_filter](node, kid))
.map(kid => this[_loadFSNode]({
parent: node,
path: resolve(nm, kid),
})))
},

@@ -262,3 +284,3 @@ // error in the readdir is not fatal, just means no kids

// world would not have noticed.
[findFSParents] () {
[_findFSParents] () {
for (const path of this[_linkTargets]) {

@@ -265,0 +287,0 @@ const node = this[_cache].get(path)

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

const _handleOptionalFailure = Symbol('handleOptionalFailure')
const _loadTrees = Symbol('loadTrees')

@@ -81,3 +82,3 @@ // shared symbols for swapping out when testing

const _global = Symbol('global')
const _global = Symbol.for('global')
const _ignoreScripts = Symbol('ignoreScripts')

@@ -89,3 +90,3 @@ const _scriptShell = Symbol('scriptShell')

const _idealTreePrune = Symbol.for('idealTreePrune')
const _explicitRootInstalls = Symbol.for('explicitRootInstalls')
const _explicitRequests = Symbol.for('explicitRequests')
const _resolvedAdd = Symbol.for('resolvedAdd')

@@ -99,3 +100,2 @@

ignoreScripts = false,
global = false,
force = false,

@@ -107,3 +107,2 @@ scriptShell,

this[_ignoreScripts] = !!ignoreScripts
this[_global] = !!global
this[_force] = !!force

@@ -129,3 +128,3 @@ this[_scriptShell] = scriptShell

this.addTracker('reify')
return Promise.all([this.loadActual(), this.buildIdealTree(options)])
return this[_loadTrees](options)
.then(() => this[_diffTrees]())

@@ -149,2 +148,20 @@ .then(() => this[_retireShallowNodes]())

// when doing a local install, we load everything and figure it all out.
// when doing a global install, we *only* care about the explicit requests.
[_loadTrees] (options) {
if (!this[_global])
return Promise.all([this.loadActual(), this.buildIdealTree(options)])
// the global install space tends to have a lot of stuff in it. don't
// load all of it, just what we care about. we won't be saving a
// hidden lockfile in there anyway.
const actualOpts = {
global: true,
filter: (node, kid) => !node.isRoot ? true
: this[_explicitRequests].has(kid),
}
return this.buildIdealTree(options)
.then(() => this.loadActual(actualOpts))
}
[_diffTrees] () {

@@ -203,12 +220,12 @@ // XXX if we have an existing diff already, there should be a way

.catch(er => {
// ignore missing shims, since only windows has them anyway
if (er.code === 'ENOENT' && from.match(/\.(cmd|ps1)$/))
return
// Occasionally an expected bin file might not exist in the package,
// or a shim/symlink might have been moved aside. If we've already
// handled the most common cause of ENOENT (dir doesn't exist yet),
// then just ignore any ENOENT.
if (er.code === 'ENOENT')
return didMkdirp ? null : mkdirp(dirname(to)).then(() =>
this[_renamePath](from, to, true))
else if (er.code === 'EEXIST')
return rimraf(to).then(() => rename(from, to))
else if (er.code === 'ENOENT' && !didMkdirp) {
// often this is a matter of the target dir not existing
return mkdirp(dirname(to)).then(() =>
this[_renamePath](from, to, true))
} else
else
throw er

@@ -397,5 +414,5 @@ })

path: node.path,
top: node.isTop,
top: node.isTop || node.globalTop,
force: this[_force],
global: this[_global],
global: node.globalTop,
})

@@ -475,5 +492,5 @@ }

path: node.path,
top: node.isTop,
top: node.isTop || node.globalTop,
force: this[_force],
global: this[_global],
global: node.globalTop,
}))

@@ -722,3 +739,3 @@ }

// support save=false option
if (options.save === false)
if (options.save === false || this[_global])
return

@@ -780,4 +797,4 @@

return this.actualTree.meta.save()
return !this[_global] && this.actualTree.meta.save()
}
}

@@ -36,2 +36,3 @@ // inventory, path, realpath, root, and parent

const {normalize} = require('read-package-json-fast')
const {getPaths: getBinPaths} = require('bin-links')

@@ -57,2 +58,3 @@ /* istanbul ignore next */

const _delistFromMeta = Symbol('_delistFromMeta')
const _global = Symbol.for('global')

@@ -89,4 +91,8 @@ const relpath = require('./relpath.js')

peer = true,
global = false,
} = options
// true if part of a global install
this[_global] = global
this.errors = error ? [error] : []

@@ -198,2 +204,11 @@ const pkg = normalize(options.pkg || {})

get global () {
return this.root[_global]
}
// true for packages installed directly in the global node_modules folder
get globalTop () {
return this.global && this.parent.isRoot
}
get binPaths () {

@@ -203,14 +218,8 @@ if (!this.parent)

// just use a string resolution rather than path functions because
// we already know that the path is .../node_modules/node.name
const base = this.path.slice(0, -1 * this.name.length)
const nmbin = resolve(base, '.bin')
const paths = []
for (const bin of Object.keys(this[_package].bin || {})) {
paths.push(resolve(nmbin, bin))
paths.push(resolve(nmbin, bin + '.cmd'))
paths.push(resolve(nmbin, bin + '.ps1'))
}
return paths
return getBinPaths({
pkg: this[_package],
path: this.path,
global: this.global,
top: this.globalTop,
})
}

@@ -217,0 +226,0 @@

{
"name": "@npmcli/arborist",
"version": "0.0.0-pre.9",
"version": "0.0.0-pre.10",
"description": "Manage node_modules trees",

@@ -8,3 +8,3 @@ "dependencies": {

"@npmcli/run-script": "^1.2.1",
"bin-links": "^2.0.0",
"bin-links": "^2.1.2",
"json-stringify-nice": "^1.1.1",

@@ -11,0 +11,0 @@ "mkdirp-infer-owner": "^1.0.2",

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