const fs = require('fs') const util = require('util') const readdir = util.promisify(fs.readdir) const { resolve } = require('path') const Arborist = require('@npmcli/arborist') const npa = require('npm-package-arg') const rpj = require('read-package-json-fast') const semver = require('semver') const reifyFinish = require('../utils/reify-finish.js') const ArboristWorkspaceCmd = require('../arborist-cmd.js') class Link extends ArboristWorkspaceCmd { static description = 'Symlink a package folder' static name = 'link' static usage = [ '[<package-spec>]', ] static params = [ 'save', 'save-exact', 'global', 'global-style', 'legacy-bundling', 'strict-peer-deps', 'package-lock', 'omit', 'ignore-scripts', 'audit', 'bin-links', 'fund', 'dry-run', ...super.params, ] async completion (opts) { const dir = this.npm.globalDir const files = await readdir(dir) return files.filter(f => !/^[._-]/.test(f)) } async exec (args) { if (this.npm.global) { throw Object.assign( new Error( 'link should never be --global.\n' + 'Please re-run this command with --local' ), { code: 'ELINKGLOBAL' } ) } // link with no args: symlink the folder to the global location // link with package arg: symlink the global to the local args = args.filter(a => resolve(a) !== this.npm.prefix) return args.length ? this.linkInstall(args) : this.linkPkg() } async linkInstall (args) { // load current packages from the global space, // and then add symlinks installs locally const globalTop = resolve(this.npm.globalDir, '..') const globalOpts = { ...this.npm.flatOptions, path: globalTop, global: true, prune: false, } const globalArb = new Arborist(globalOpts) // get only current top-level packages from the global space const globals = await globalArb.loadActual({ filter: (node, kid) => !node.isRoot || args.some(a => npa(a).name === kid), }) // any extra arg that is missing from the current // global space should be reified there first const missing = this.missingArgsFromTree(globals, args) if (missing.length) { await globalArb.reify({ ...globalOpts, add: missing, }) } // get a list of module names that should be linked in the local prefix const names = [] for (const a of args) { const arg = npa(a) names.push( arg.type === 'directory' ? (await rpj(resolve(arg.fetchSpec, 'package.json'))).name : arg.name ) } // npm link should not save=true by default unless you're // using any of --save-dev or other types const save = Boolean( this.npm.config.find('save') !== 'default' || this.npm.config.get('save-optional') || this.npm.config.get('save-peer') || this.npm.config.get('save-dev') || this.npm.config.get('save-prod') ) // create a new arborist instance for the local prefix and // reify all the pending names as symlinks there const localArb = new Arborist({ ...this.npm.flatOptions, prune: false, path: this.npm.prefix, save, }) await localArb.reify({ ...this.npm.flatOptions, prune: false, path: this.npm.prefix, add: names.map(l => `file:${resolve(globalTop, 'node_modules', l).replace(/#/g, '%23')}`), save, workspaces: this.workspaceNames, }) await reifyFinish(this.npm, localArb) } async linkPkg () { const wsp = this.workspacePaths const paths = wsp && wsp.length ? wsp : [this.npm.prefix] const add = paths.map(path => `file:${path.replace(/#/g, '%23')}`) const globalTop = resolve(this.npm.globalDir, '..') const arb = new Arborist({ ...this.npm.flatOptions, path: globalTop, global: true, }) await arb.reify({ add, }) await reifyFinish(this.npm, arb) } // Returns a list of items that can't be fulfilled by // things found in the current arborist inventory missingArgsFromTree (tree, args) { if (tree.isLink) { return this.missingArgsFromTree(tree.target, args) } const foundNodes = [] const missing = args.filter(a => { const arg = npa(a) const nodes = tree.children.values() const argFound = [...nodes].every(node => { // TODO: write tests for unmatching version specs, this is hard to test // atm but should be simple once we have a mocked registry again if (arg.name !== node.name /* istanbul ignore next */ || ( arg.version && /* istanbul ignore next */ !semver.satisfies(node.version, arg.version) )) { foundNodes.push(node) return true } }) return argFound }) // remote nodes from the loaded tree in order // to avoid dropping them later when reifying for (const node of foundNodes) { node.parent = null } return missing } } module.exports = Link
Name | Type | Size | Permission | Actions |
---|---|---|---|---|
access.js | File | 5.45 KB | 0644 |
|
adduser.js | File | 2.2 KB | 0644 |
|
audit.js | File | 11.95 KB | 0644 |
|
bin.js | File | 729 B | 0644 |
|
birthday.js | File | 508 B | 0644 |
|
bugs.js | File | 815 B | 0644 |
|
cache.js | File | 7.08 KB | 0644 |
|
ci.js | File | 3.63 KB | 0644 |
|
completion.js | File | 8.91 KB | 0644 |
|
config.js | File | 8.11 KB | 0644 |
|
dedupe.js | File | 1.37 KB | 0644 |
|
deprecate.js | File | 2.06 KB | 0644 |
|
diff.js | File | 8.1 KB | 0644 |
|
dist-tag.js | File | 5.47 KB | 0644 |
|
docs.js | File | 447 B | 0644 |
|
doctor.js | File | 9.22 KB | 0644 |
|
edit.js | File | 2 KB | 0644 |
|
exec.js | File | 2.44 KB | 0644 |
|
explain.js | File | 3.55 KB | 0644 |
|
explore.js | File | 2.33 KB | 0644 |
|
find-dupes.js | File | 602 B | 0644 |
|
fund.js | File | 6.37 KB | 0644 |
|
get.js | File | 524 B | 0644 |
|
help-search.js | File | 5.62 KB | 0644 |
|
help.js | File | 4.53 KB | 0644 |
|
hook.js | File | 3.93 KB | 0644 |
|
init.js | File | 6.81 KB | 0644 |
|
install-ci-test.js | File | 377 B | 0644 |
|
install-test.js | File | 374 B | 0644 |
|
install.js | File | 5.11 KB | 0644 |
|
link.js | File | 5.02 KB | 0644 |
|
ll.js | File | 234 B | 0644 |
|
logout.js | File | 1.34 KB | 0644 |
|
ls.js | File | 16.94 KB | 0644 |
|
org.js | File | 4.2 KB | 0644 |
|
outdated.js | File | 8.84 KB | 0644 |
|
owner.js | File | 5.88 KB | 0644 |
|
pack.js | File | 2.36 KB | 0644 |
|
ping.js | File | 874 B | 0644 |
|
pkg.js | File | 3.47 KB | 0644 |
|
prefix.js | File | 343 B | 0644 |
|
profile.js | File | 11.25 KB | 0644 |
|
prune.js | File | 779 B | 0644 |
|
publish.js | File | 6.33 KB | 0644 |
|
query.js | File | 2.81 KB | 0644 |
|
rebuild.js | File | 2.16 KB | 0644 |
|
repo.js | File | 1.24 KB | 0644 |
|
restart.js | File | 351 B | 0644 |
|
root.js | File | 298 B | 0644 |
|
run-script.js | File | 6.9 KB | 0644 |
|
search.js | File | 2.72 KB | 0644 |
|
set-script.js | File | 2.63 KB | 0644 |
|
set.js | File | 572 B | 0644 |
|
shrinkwrap.js | File | 2.64 KB | 0644 |
|
star.js | File | 1.87 KB | 0644 |
|
stars.js | File | 1.03 KB | 0644 |
|
start.js | File | 341 B | 0644 |
|
stop.js | File | 336 B | 0644 |
|
team.js | File | 4.44 KB | 0644 |
|
test.js | File | 336 B | 0644 |
|
token.js | File | 6.79 KB | 0644 |
|
uninstall.js | File | 1.52 KB | 0644 |
|
unpublish.js | File | 4.51 KB | 0644 |
|
unstar.js | File | 182 B | 0644 |
|
update.js | File | 1.7 KB | 0644 |
|
version.js | File | 3.6 KB | 0644 |
|
view.js | File | 14.38 KB | 0644 |
|
whoami.js | File | 514 B | 0644 |
|