@npmcli/arborist
Advanced tools
Comparing version 0.0.16 to 0.0.17
// mixin implementing the buildIdealTree method | ||
const rpj = require('read-package-json-fast') | ||
@@ -10,2 +9,3 @@ const npa = require('npm-package-arg') | ||
const mapWorkspaces = require('@npmcli/map-workspaces') | ||
const promiseCallLimit = require('promise-call-limit') | ||
@@ -47,2 +47,3 @@ const fromPath = require('../from-path.js') | ||
const _applyUserRequests = Symbol('applyUserRequests') | ||
const _inflateAncientLockfile = Symbol('inflateAncientLockfile') | ||
const _buildDeps = Symbol('buildDeps') | ||
@@ -143,2 +144,8 @@ const _buildDepStep = Symbol('buildDepStep') | ||
// an empty array or any falsey value is the same as null | ||
if (!options.add || options.add.length === 0) | ||
options.add = null | ||
if (!options.rm || options.rm.length === 0) | ||
options.rm = null | ||
process.emit('time', 'idealTree') | ||
@@ -163,2 +170,3 @@ | ||
await this[_applyUserRequests](options) | ||
await this[_inflateAncientLockfile]() | ||
await this[_buildDeps]() | ||
@@ -199,3 +207,2 @@ await this[_fixDepFlags]() | ||
) | ||
} | ||
@@ -463,3 +470,52 @@ | ||
async [_inflateAncientLockfile] () { | ||
const { meta, inventory } = this.idealTree | ||
const ancient = meta.ancientLockfile | ||
const old = meta.loadedFromDisk && !(meta.originalLockfileVersion >= 2) | ||
if (inventory.size === 0 || !(ancient || old && this[_complete])) | ||
return | ||
// if the lockfile is from node v5 or earlier, then we'll have to reload | ||
// all the manifests of everything we encounter. this is costly, but at | ||
// least it's just a one-time hit. | ||
process.emit('time', 'idealTree:inflate') | ||
const heading = ancient ? 'ancient lockfile' : 'old lockfile' | ||
this.log.warn(heading, | ||
` | ||
The ${meta.type} file was created with an old version of npm, | ||
so supplemental metadata must be fetched from the registry. | ||
This is a one-time fix-up, please be patient... | ||
`) | ||
this.addTracker('idealTree:inflate') | ||
const queue = [] | ||
for (const node of inventory.values()) { | ||
queue.push(async () => { | ||
this.log.silly('inflate', node.location) | ||
const id = `${node.name}@${node.version}` | ||
const sloc = node.location.substr('node_modules/'.length) | ||
const t = `idealTree:inflate:${sloc}` | ||
this.addTracker(t) | ||
await pacote.manifest(id, { | ||
...this.options, | ||
resolved: node.resolved, | ||
integrity: node.integrity, | ||
fullMetadata: false, | ||
}).then(mani => { | ||
node.package = { ...mani, _id: `${mani.name}@${mani.version}` } | ||
}).catch((er) => { | ||
const warning = `Could not fetch metadata for ${id}` | ||
this.log.warn(heading, warning, er) | ||
}) | ||
this.finishTracker(t) | ||
}) | ||
} | ||
await promiseCallLimit(queue) | ||
this.finishTracker('idealTree:inflate') | ||
process.emit('timeEnd', 'idealTree:inflate') | ||
} | ||
// at this point we have a virtual tree with the actual root node's | ||
@@ -495,3 +551,2 @@ // package deps, which may be partly or entirely incomplete, invalid | ||
const node = this[_depsQueue].shift() | ||
const bd = node.package.bundleDependencies | ||
@@ -498,0 +553,0 @@ const hasBundle = bd && Array.isArray(bd) && bd.length |
@@ -19,2 +19,3 @@ // mixin providing the loadVirtual method | ||
const assignParentage = Symbol('assignParentage') | ||
const loadRoot = Symbol('loadRoot') | ||
const loadNode = Symbol('loadVirtualNode') | ||
@@ -33,3 +34,3 @@ const loadLink = Symbol('loadVirtualLink') | ||
// public method | ||
loadVirtual (options = {}) { | ||
async loadVirtual (options = {}) { | ||
if (this.virtualTree) | ||
@@ -45,21 +46,23 @@ return Promise.resolve(this.virtualTree) | ||
return Shrinkwrap.load({ path: this.path }).then(s => { | ||
if (!s.loadedFromDisk && !options.root) { | ||
const er = new Error('loadVirtual requires existing shrinkwrap file') | ||
throw Object.assign(er, { code: 'ENOLOCK' }) | ||
} | ||
const s = await Shrinkwrap.load({ path: this.path }) | ||
if (!s.loadedFromDisk && !options.root) { | ||
const er = new Error('loadVirtual requires existing shrinkwrap file') | ||
throw Object.assign(er, { code: 'ENOLOCK' }) | ||
} | ||
// when building the ideal tree, we pass in a root node to this function | ||
// otherwise, load it from the root package in the lockfile | ||
const { | ||
root = this[loadWorkspaces]( | ||
this[loadNode]('', s.data.packages[''] || {}), | ||
s | ||
) | ||
} = options | ||
// when building the ideal tree, we pass in a root node to this function | ||
// otherwise, load it from the root package json or the lockfile | ||
const { | ||
root = this[loadWorkspaces](await this[loadRoot](s), s), | ||
} = options | ||
return this[loadFromShrinkwrap](s, root) | ||
}) | ||
return this[loadFromShrinkwrap](s, root) | ||
} | ||
async [loadRoot] (s) { | ||
const pj = this.path + '/package.json' | ||
const pkg = await rpj(pj).catch(() => s.data.packages['']) || {} | ||
return this[loadNode]('', pkg) | ||
} | ||
async [loadFromShrinkwrap] (s, root) { | ||
@@ -66,0 +69,0 @@ root.meta = s |
@@ -25,3 +25,2 @@ // mixin implementing the reify method | ||
const promiseAllRejectLate = require('promise-all-reject-late') | ||
const promiseCallLimit = require('promise-call-limit') | ||
const optionalSet = require('../optional-set.js') | ||
@@ -447,3 +446,6 @@ | ||
const {meta} = this.idealTree | ||
if (meta.loadedFromDisk && !(meta.originalLockfileVersion >= 2)) { | ||
const ancient = meta.ancientLockfile | ||
const old = meta.loadedFromDisk && !(meta.originalLockfileVersion >= 2) | ||
// already replaced with the manifest if it's truly ancient | ||
if (old && !ancient) { | ||
// XXX should have a shared location where package.json is read, | ||
@@ -849,4 +851,16 @@ // so we don't ever read the same pj more than necessary. | ||
// XXX preserve indentation maybe? | ||
// preserve indentation, if possible | ||
const pj = resolve(this.idealTree.path, 'package.json') | ||
const { | ||
[Symbol.for('indent')]: indent, | ||
[Symbol.for('newline')]: newline, | ||
} = this.idealTree.package | ||
const pjData = { | ||
...this.idealTree.package, | ||
_id: undefined, // strip this off | ||
} | ||
const format = indent === undefined ? ' ' : indent | ||
const eol = newline === undefined ? '\n' : newline | ||
const json = (JSON.stringify(pjData, null, format) + '\n') | ||
.replace(/\n/g, eol) | ||
@@ -856,6 +870,3 @@ const saveOpt = { format: this[_formatPackageLock] } | ||
this[_usePackageLock] && this.idealTree.meta.save(saveOpt), | ||
writeFile(pj, JSON.stringify({ | ||
...this.idealTree.package, | ||
_id: undefined, | ||
}, null, 2) + '\n'), | ||
writeFile(pj, json), | ||
]).then(() => process.emit('timeEnd', 'reify:save')) | ||
@@ -862,0 +873,0 @@ } |
@@ -262,2 +262,3 @@ // a module that manages a shrinkwrap file (npm-shrinkwrap.json or | ||
indent = 2, | ||
newline = '\n', | ||
shrinkwrapOnly = false, | ||
@@ -272,2 +273,3 @@ hiddenLockfile = false, | ||
this.indent = indent | ||
this.newline = newline | ||
this.loadedFromDisk = false | ||
@@ -359,8 +361,2 @@ this.type = null | ||
const data = lock || sw || '' | ||
// don't use detect-indent, just pick the first line. | ||
// if the file starts with {" then we have an indent of '', ie, none | ||
// which will default to 2 at save time. | ||
const indent = data.match(/^\{\n?([\s\t]*)"/) | ||
if (indent) | ||
this.indent = indent[1] | ||
@@ -386,2 +382,12 @@ // use shrinkwrap only for deps, otherwise prefer package-lock | ||
}).then(async data => { | ||
// don't use detect-indent, just pick the first line. | ||
// if the file starts with {" then we have an indent of '', ie, none | ||
// which will default to 2 at save time. | ||
const { | ||
[Symbol.for('indent')]: indent, | ||
[Symbol.for('newline')]: newline, | ||
} = data | ||
this.indent = indent !== undefined ? indent : this.indent | ||
this.newline = newline !== undefined ? newline : this.newline | ||
if (!this.hiddenLockfile || !data.packages) | ||
@@ -399,2 +405,3 @@ return data | ||
this.loadedFromDisk = false | ||
this.ancientLockfile = false | ||
return {} | ||
@@ -410,2 +417,5 @@ }).then(lock => { | ||
this.originalLockfileVersion = lock.lockfileVersion | ||
this.ancientLockfile = this.loadedFromDisk && | ||
!(lock.lockfileVersion >= 2) && !lock.requires | ||
// load old lockfile deps into the packages listing | ||
@@ -914,3 +924,5 @@ if (lock.dependencies && !lock.packages) { | ||
const indent = format ? this.indent || 2 : 0 | ||
const json = stringify(this.commit(), swKeyOrder, indent) | ||
const eol = format ? this.newline || '\n' : '' | ||
const data = this.commit() | ||
const json = stringify(data, swKeyOrder, indent).replace(/\n/g, eol) | ||
return Promise.all([ | ||
@@ -917,0 +929,0 @@ writeFile(this.filename, json).catch(er => { |
{ | ||
"name": "@npmcli/arborist", | ||
"version": "0.0.16", | ||
"version": "0.0.17", | ||
"description": "Manage node_modules trees", | ||
@@ -19,6 +19,6 @@ "dependencies": { | ||
"pacote": "^11.1.10", | ||
"parse-conflict-json": "^1.0.0", | ||
"parse-conflict-json": "^1.1.1", | ||
"promise-all-reject-late": "^1.0.0", | ||
"promise-call-limit": "^1.0.1", | ||
"read-package-json-fast": "^1.1.0", | ||
"read-package-json-fast": "^1.2.1", | ||
"readdir-scoped-modules": "^1.1.0", | ||
@@ -25,0 +25,0 @@ "semver": "^7.1.2", |
263838
6417
Updatedparse-conflict-json@^1.1.1