diff options
-rw-r--r-- | docs/content/cli-commands/npm-build.md | 34 | ||||
-rw-r--r-- | lib/build.js | 140 | ||||
-rw-r--r-- | lib/config/cmd-list.js | 2 | ||||
-rw-r--r-- | lib/rebuild.js | 112 | ||||
-rw-r--r-- | lib/unbuild.js | 137 |
5 files changed, 45 insertions, 380 deletions
diff --git a/docs/content/cli-commands/npm-build.md b/docs/content/cli-commands/npm-build.md deleted file mode 100644 index eca5397d9..000000000 --- a/docs/content/cli-commands/npm-build.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -section: cli-commands -title: npm-build -description: Build a package ---- - -# npm-build(1) - -## Build a package - -### Synopsis -```shell -npm build [<package-folder>] -``` - -* `<package-folder>`: - A folder containing a `package.json` file in its root. - -### Description - -This is the plumbing command called by `npm link` and `npm install`. - -It should generally be called during installation, but if you need to run it -directly, run: -```bash - npm run-script build -``` - -### See Also - -* [npm install](/cli-commands/install) -* [npm link](/cli-commands/link) -* [npm scripts](/using-npm/scripts) -* [package.json](/configuring-npm/package-json) diff --git a/lib/build.js b/lib/build.js deleted file mode 100644 index f8b3c4933..000000000 --- a/lib/build.js +++ /dev/null @@ -1,140 +0,0 @@ -// npm build command - -// everything about the installation after the creation of -// the .npm/{name}/{version}/package folder. -// linking the modules into the npm.root, -// resolving dependencies, etc. - -// This runs AFTER install or link are completed. - -var npm = require('./npm.js') -var log = require('npmlog') -var chain = require('slide').chain -var path = require('path') -var fs = require('graceful-fs') -var lifecycle = require('./utils/lifecycle.js') -var readJson = require('read-package-json') -var binLinks = require('bin-links') -var binLinksConfig = require('./config/bin-links.js') -var ini = require('ini') -var writeFile = require('write-file-atomic') - -module.exports = build -build.usage = 'npm build [<folder>]' - -build._didBuild = {} -build._noLC = {} -function build (args, global, didPre, didRB, cb) { - if (typeof cb !== 'function') { - cb = didRB - didRB = false - } - if (typeof cb !== 'function') { - cb = didPre - didPre = false - } - if (typeof cb !== 'function') { - cb = global - global = npm.config.get('global') - } - - if (!args.length) { - readJson(path.resolve(npm.localPrefix, 'package.json'), function (er, pkg) { - if (!args.length && pkg && pkg.scripts && pkg.scripts.build) { - log.warn('build', '`npm build` called with no arguments. Did you mean to `npm run-script build`?') - } - cb() - }) - } else { - // it'd be nice to asyncMap these, but actually, doing them - // in parallel generally munges up the output from node-waf - var builder = build_(global, didPre, didRB) - chain(args.map(function (arg) { - return function (cb) { - builder(arg, cb) - } - }), cb) - } -} - -function build_ (global, didPre, didRB) { - return function (folder, cb) { - folder = path.resolve(folder) - if (build._didBuild[folder]) log.info('build', 'already built', folder) - build._didBuild[folder] = true - log.info('build', folder) - readJson(path.resolve(folder, 'package.json'), function (er, pkg) { - if (er) return cb(er) - chain([ - !didPre && [lifecycle, pkg, 'preinstall', folder], - [linkStuff, pkg, folder, global], - !didRB && [rebuildBundles, pkg, folder], - [writeBuiltinConf, pkg, folder], - didPre !== build._noLC && [lifecycle, pkg, 'install', folder], - didPre !== build._noLC && [lifecycle, pkg, 'postinstall', folder] - ], - cb) - }) - } -} - -var writeBuiltinConf = build.writeBuiltinConf = function (pkg, folder, cb) { - // the builtin config is "sticky". Any time npm installs - // itself globally, it puts its builtin config file there - var parent = path.dirname(folder) - var dir = npm.globalDir - - // Make this count for canary, too - if ((pkg.name !== 'npm' && pkg.name !== 'npmc') || - !npm.config.get('global') || - !npm.config.usingBuiltin || - dir !== parent) { - return cb() - } - - var data = ini.stringify(npm.config.sources.builtin.data) - writeFile(path.resolve(folder, 'npmrc'), data, cb) -} - -var linkStuff = build.linkStuff = function (pkg, folder, global, cb) { - // allow to opt out of linking binaries. - if (npm.config.get('bin-links') === false) return cb() - return binLinks(pkg, folder, global, binLinksConfig(pkg), cb) -} - -function rebuildBundles (pkg, folder, cb) { - if (!npm.config.get('rebuild-bundle')) return cb() - - var deps = Object.keys(pkg.dependencies || {}) - .concat(Object.keys(pkg.devDependencies || {})) - var bundles = pkg.bundleDependencies || pkg.bundledDependencies || [] - - fs.readdir(path.resolve(folder, 'node_modules'), function (er, files) { - // error means no bundles - if (er) return cb() - - log.verbose('rebuildBundles', files) - // don't asyncMap these, because otherwise build script output - // gets interleaved and is impossible to read - chain(files.filter(function (file) { - // rebuild if: - // not a .folder, like .bin or .hooks - return !file.match(/^[._-]/) && - // not some old 0.x style bundle - file.indexOf('@') === -1 && - // either not a dep, or explicitly bundled - (deps.indexOf(file) === -1 || bundles.indexOf(file) !== -1) - }).map(function (file) { - file = path.resolve(folder, 'node_modules', file) - return function (cb) { - if (build._didBuild[file]) return cb() - log.verbose('rebuild bundle', file) - // if file is not a package dir, then don't do it. - fs.lstat(path.resolve(file, 'package.json'), function (er) { - if (er) return cb() - build_(false)(file, cb) - }) - } - }), cb) - }) -} diff --git a/lib/config/cmd-list.js b/lib/config/cmd-list.js index a8a977110..5dad17ad0 100644 --- a/lib/config/cmd-list.js +++ b/lib/config/cmd-list.js @@ -123,8 +123,6 @@ var cmdList = [ ] var plumbing = [ - 'build', - 'unbuild', 'xmas', 'substack', 'visnup' 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>/]<name>[@<version>] ...]') -rebuild.usage = usage( - 'rebuild', - 'npm rebuild [[<@scope>/<name>]...]' -) +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 }) diff --git a/lib/unbuild.js b/lib/unbuild.js deleted file mode 100644 index 3e8d3e4f1..000000000 --- a/lib/unbuild.js +++ /dev/null @@ -1,137 +0,0 @@ -module.exports = unbuild -module.exports.rmStuff = rmStuff -unbuild.usage = 'npm unbuild <folder>\n(this is plumbing)' - -const readJson = require('read-package-json') -const gentlyRm = require('./utils/gently-rm.js') -const npm = require('./npm.js') -const path = require('path') -const isInside = require('path-is-inside') -const lifecycle = require('./utils/lifecycle.js') -const asyncMap = require('slide').asyncMap -const chain = require('slide').chain -const log = require('npmlog') -const build = require('./build.js') -const output = require('./utils/output.js') - -// args is a list of folders. -// remove any bins/etc, and then delete the folder. -function unbuild (args, silent, cb) { - if (typeof silent === 'function') { - cb = silent - silent = false - } - asyncMap(args, unbuild_(silent), cb) -} - -function unbuild_ (silent) { - return function (folder, cb_) { - function cb (er) { - cb_(er, path.relative(npm.root, folder)) - } - folder = path.resolve(folder) - const base = isInside(folder, npm.prefix) ? npm.prefix : folder - delete build._didBuild[folder] - log.verbose('unbuild', folder.substr(npm.prefix.length + 1)) - readJson(path.resolve(folder, 'package.json'), function (er, pkg) { - // if no json, then just trash it, but no scripts or whatever. - if (er) return gentlyRm(folder, false, base, cb) - chain( - [ - [lifecycle, pkg, 'preuninstall', folder, { failOk: true }], - [lifecycle, pkg, 'uninstall', folder, { failOk: true }], - !silent && function (cb) { - output('unbuild ' + pkg._id) - cb() - }, - [rmStuff, pkg, folder], - [lifecycle, pkg, 'postuninstall', folder, { failOk: true }], - [gentlyRm, folder, false, base] - ], - cb - ) - }) - } -} - -function rmStuff (pkg, folder, cb) { - // if it's global, and folder is in {prefix}/node_modules, - // then bins are in {prefix}/bin - // otherwise, then bins are in folder/../.bin - const dir = path.dirname(folder) - const scope = path.basename(dir) - const parent = scope.charAt(0) === '@' ? path.dirname(dir) : dir - const gnm = npm.dir - // gnm might be an absolute path, parent might be relative - // this checks they're the same directory regardless - const top = path.relative(gnm, parent) === '' - - log.verbose('unbuild rmStuff', pkg._id, 'from', gnm) - if (!top) log.verbose('unbuild rmStuff', 'in', parent) - asyncMap([rmBins, rmMans], function (fn, cb) { - fn(pkg, folder, parent, top, cb) - }, cb) -} - -function rmBins (pkg, folder, parent, top, cb) { - if (!pkg.bin) return cb() - const binRoot = top ? npm.bin : path.resolve(parent, '.bin') - asyncMap(Object.keys(pkg.bin), function (b, cb) { - if (process.platform === 'win32') { - chain([ - [gentlyRm, path.resolve(binRoot, b) + '.ps1', true, folder], - [gentlyRm, path.resolve(binRoot, b) + '.cmd', true, folder], - [gentlyRm, path.resolve(binRoot, b), true, folder] - ], cb) - } else { - gentlyRm(path.resolve(binRoot, b), true, folder, cb) - } - }, gentlyRmBinRoot) - - function gentlyRmBinRoot (err) { - if (err || top) return cb(err) - return gentlyRm(binRoot, true, parent, cb) - } -} - -function rmMans (pkg, folder, parent, top, cb) { - if (!pkg.man || - !top || - process.platform === 'win32' || - !npm.config.get('global')) { - return cb() - } - const manRoot = path.resolve(npm.config.get('prefix'), 'share', 'man') - log.verbose('rmMans', 'man files are', pkg.man, 'in', manRoot) - asyncMap(pkg.man, function (man, cb) { - if (Array.isArray(man)) { - man.forEach(rmMan) - } else { - rmMan(man) - } - - function rmMan (man) { - log.silly('rmMan', 'preparing to remove', man) - const parseMan = man.match(/(.*\.([0-9]+)(\.gz)?)$/) - if (!parseMan) { - log.error( - 'rmMan', man, 'is not a valid name for a man file.', - 'Man files must end with a number, ' + - 'and optionally a .gz suffix if they are compressed.' - ) - return cb() - } - - const stem = parseMan[1] - const sxn = parseMan[2] - const gz = parseMan[3] || '' - const bn = path.basename(stem) - const manDest = path.join( - manRoot, - 'man' + sxn, - (bn.indexOf(pkg.name) === 0 ? bn : pkg.name + '-' + bn) + '.' + sxn + gz - ) - gentlyRm(manDest, true, cb) - } - }, cb) -} |