From 40829ec40c33a6d23f18715e60e3395bdcb0467e Mon Sep 17 00:00:00 2001 From: Ruy Adorno Date: Thu, 10 Jun 2021 17:11:08 -0400 Subject: [PATCH] fix(link): do not prune packages `npm link ` is meant to be used as a way to link a local package to an install tree and it's very surprising to users that it may prune extraneous deps from the project. This change switches the default behavior to avoid pruning deps when reifying the dependencies in npm link. Fixes: https://github.com/npm/cli/issues/2554 PR-URL: https://github.com/npm/cli/pull/3399 Credit: @ruyadorno Close: #3399 Reviewed-by: @ljharb, @nlf --- lib/link.js | 2 ++ test/lib/link.js | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/lib/link.js b/lib/link.js index 47fe4b17a272b..d6abf139730bd 100644 --- a/lib/link.js +++ b/lib/link.js @@ -134,12 +134,14 @@ class Link extends ArboristWorkspaceCmd { // reify all the pending names as symlinks there const localArb = new Arborist({ ...this.npm.flatOptions, + prune: false, log: this.npm.log, path: this.npm.prefix, save, }) await localArb.reify({ ...this.npm.flatOptions, + prune: false, path: this.npm.prefix, log: this.npm.log, add: names.map(l => `file:${resolve(globalTop, 'node_modules', l)}`), diff --git a/test/lib/link.js b/test/lib/link.js index 3cad0ff90362d..64375cfc13c2c 100644 --- a/test/lib/link.js +++ b/test/lib/link.js @@ -1,4 +1,5 @@ const { resolve } = require('path') +const fs = require('fs') const Arborist = require('@npmcli/arborist') const t = require('tap') @@ -485,6 +486,55 @@ t.test('link pkg already in global space when prefix is a symlink', (t) => { }) }) +t.test('should not prune dependencies when linking packages', async t => { + const testdir = t.testdir({ + 'global-prefix': { + lib: { + node_modules: { + linked: t.fixture('symlink', '../../../linked'), + }, + }, + }, + linked: { + 'package.json': JSON.stringify({ + name: 'linked', + version: '1.0.0', + }), + }, + 'my-project': { + node_modules: { + foo: { + 'package.json': JSON.stringify({ name: 'foo', version: '1.0.0' }), + }, + }, + 'package.json': JSON.stringify({ + name: 'my-project', + version: '1.0.0', + }), + }, + }) + npm.globalDir = resolve(testdir, 'global-prefix', 'lib', 'node_modules') + npm.prefix = resolve(testdir, 'my-project') + reifyOutput = () => {} + + const _cwd = process.cwd() + process.chdir(npm.prefix) + + await new Promise((res, rej) => { + link.exec(['linked'], (err) => { + if (err) + rej(err) + res() + }) + }) + + t.ok( + fs.statSync(resolve(testdir, 'my-project/node_modules/foo')), + 'should not prune any extraneous dep when running npm link' + ) + process.chdir(_cwd) +}) + t.test('completion', async t => { const testdir = t.testdir({ 'global-prefix': {