From f6a8330175e05a51fa3c02098bf048e9d646b093 Mon Sep 17 00:00:00 2001 From: isaacs Date: Fri, 5 Jun 2020 18:06:07 -0700 Subject: Implement npm rebuild using Arborist This also removes the build and unbuild commands, since they are no longer necessary or used for anything. PR-URL: https://github.com/npm/cli/pull/1401 Credit: @isaacs Close: #1401 Reviewed-by: @ruyadorno --- lib/rebuild.js | 112 +++++++++++++++++++++++---------------------------------- 1 file changed, 45 insertions(+), 67 deletions(-) (limited to 'lib/rebuild.js') diff --git a/lib/rebuild.js b/lib/rebuild.js index bbc5e8f48..df1d29566 100644 --- a/lib/rebuild.js +++ b/lib/rebuild.js @@ -1,78 +1,56 @@ +const Arborist = require('@npmcli/arborist') +const npm = require('./npm.js') +const usageUtil = require('./utils/usage.js') +const { resolve } = require('path') +const output = require('./utils/output.js') +const npa = require('npm-package-arg') +const semver = require('semver') -module.exports = rebuild +const cmd = (args, cb) => rebuild(args).then(() => cb()).catch(cb) -var readInstalled = require('read-installed') -var semver = require('semver') -var log = require('npmlog') -var npm = require('./npm.js') -var npa = require('npm-package-arg') -var usage = require('./utils/usage') -var output = require('./utils/output.js') +const usage = usageUtil('rebuild', 'npm rebuild [[<@scope>/][@] ...]') -rebuild.usage = usage( - 'rebuild', - 'npm rebuild [[<@scope>/]...]' -) +const completion = require('./utils/completion/installed-deep.js') -rebuild.completion = require('./utils/completion/installed-deep.js') +const rebuild = async args => { + const globalTop = resolve(npm.globalDir, '..') + const where = npm.flatOptions.global ? globalTop : npm.prefix + const arb = new Arborist({ + ...npm.flatOptions, + path: where + }) -function rebuild (args, cb) { - var opt = { depth: npm.config.get('depth'), dev: true } - readInstalled(npm.prefix, opt, function (er, data) { - log.info('readInstalled', typeof data) - if (er) return cb(er) - var set = filter(data, args) - var folders = Object.keys(set).filter(function (f) { - return f !== npm.prefix + if (args.length) { + // get the set of nodes matching the name that we want rebuilt + const tree = await arb.loadActual() + const filter = getFilterFn(args) + await arb.rebuild({ + nodes: tree.inventory.filter(filter) }) - if (!folders.length) return cb() - log.silly('rebuild set', folders) - cleanBuild(folders, set, cb) - }) -} + } else { + await arb.rebuild() + } -function cleanBuild (folders, set, cb) { - npm.commands.build(folders, function (er) { - if (er) return cb(er) - output(folders.map(function (f) { - return set[f] + ' ' + f - }).join('\n')) - cb() - }) + output('rebuilt dependencies successfully') } -function filter (data, args, set, seen) { - if (!set) set = {} - if (!seen) seen = new Set() - if (set.hasOwnProperty(data.path)) return set - if (seen.has(data)) return set - seen.add(data) - var pass - if (!args.length) pass = true // rebuild everything - else if (data.name && data._id) { - for (var i = 0, l = args.length; i < l; i++) { - var arg = args[i] - var nv = npa(arg) - var n = nv.name - var v = nv.rawSpec - if (n !== data.name) continue - if (!semver.satisfies(data.version, v, true)) continue - pass = true - break - } - } - if (pass && data._id) { - log.verbose('rebuild', 'path, id', [data.path, data._id]) - set[data.path] = data._id - } - // need to also dive through kids, always. - // since this isn't an install these won't get auto-built unless - // they're not dependencies. - Object.keys(data.dependencies || {}).forEach(function (d) { - // return - var dep = data.dependencies[d] - if (typeof dep === 'string') return - filter(dep, args, set, seen) +const getFilterFn = args => { + const specs = args.map(arg => { + const spec = npa(arg) + if (spec.type === 'tag' && spec.rawSpec === '') + return spec + if (spec.type !== 'range' && spec.type !== 'version') + throw new Error('`npm rebuild` only supports SemVer version/range specifiers') + return spec + }) + return node => specs.some(spec => { + const { version } = node.package + if (spec.name !== node.name) + return false + if (spec.rawSpec === '' || spec.rawSpec === '*') + return true + return semver.satisfies(version, spec.fetchSpec) }) - return set } + +module.exports = Object.assign(cmd, { usage, completion }) -- cgit v1.2.3