Skip to content

Commit 1342071

Browse files
larsgwiarna
authored andcommitted
install: fix checking for optional dep
Fixes: https://npm.community/t/2569 Credit: @larsgw Reviewed-By: @iarna
1 parent d9970da commit 1342071

File tree

3 files changed

+169
-0
lines changed

3 files changed

+169
-0
lines changed

lib/install/is-only-dev.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ function andIsOnlyDev (name, seen) {
2828
return isDev && !isProd
2929
} else {
3030
if (seen.has(req)) return true
31+
seen = new Set(seen)
3132
seen.add(req)
3233
return isOnlyDev(req, seen)
3334
}

lib/install/is-only-optional.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ function isOptional (node, seen) {
1010
if (seen.has(node) || node.requiredBy.length === 0) {
1111
return false
1212
}
13+
seen = new Set(seen)
1314
seen.add(node)
1415
const swOptional = node.fromShrinkwrap && node.package._optional
1516
return node.requiredBy.every(function (req) {
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
'use strict'
2+
const path = require('path')
3+
const test = require('tap').test
4+
const Tacks = require('tacks')
5+
const File = Tacks.File
6+
const Dir = Tacks.Dir
7+
const common = require('../common-tap.js')
8+
const fs = require('fs')
9+
10+
const basedir = path.join(__dirname, path.basename(__filename, '.js'))
11+
const testdir = path.join(basedir, 'testdir')
12+
const cachedir = path.join(basedir, 'cache')
13+
const globaldir = path.join(basedir, 'global')
14+
const tmpdir = path.join(basedir, 'tmp')
15+
const optionaldir = path.join(testdir, 'optional')
16+
const devdir = path.join(testdir, 'dev')
17+
18+
const env = common.newEnv().extend({
19+
npm_config_cache: cachedir,
20+
npm_config_tmp: tmpdir,
21+
npm_config_prefix: globaldir,
22+
npm_config_registry: common.registry,
23+
npm_config_loglevel: 'error'
24+
})
25+
26+
const fixture = new Tacks(Dir({
27+
cache: Dir(),
28+
global: Dir(),
29+
tmp: Dir(),
30+
testdir: Dir({
31+
'a-1.0.0.tgz': File(Buffer.from(
32+
'1f8b0800000000000003edcfc10e82300c0660ce3ec5d2b38e4eb71d789b' +
33+
'010d41e358187890f0ee56493c71319218937d977feb9aa50daebab886f2' +
34+
'b0a43cc7ce671b4344abb558ab3f2934223b198b4a598bdcc707a38f9c5b' +
35+
'0fb2668c83eb79946fff597611effc131378772528c0c11e6ed4c7b6f37c' +
36+
'53122572a5a640be265fb514a198a0e43729f3f2f06a9043738779defd7a' +
37+
'89244992e4630fd69e456800080000',
38+
'hex'
39+
)),
40+
'b-1.0.0.tgz': File(Buffer.from(
41+
'1f8b08000000000000032b484cce4e4c4fd52f80d07a59c5f9790c540606' +
42+
'06066626260ad8c4c1c0d85c81c1d8d4ccc0d0d0cccc00a80ec830353103' +
43+
'd2d4760836505a5c925804740aa5e640bca200a78708a856ca4bcc4d55b2' +
44+
'524a52d2512a4b2d2acecccf03f20cf50cf40c946ab906da79a360148c82' +
45+
'51300a680400106986b400080000',
46+
'hex'
47+
)),
48+
dev: Dir({
49+
'package.json': File({
50+
name: 'dev',
51+
version: '1.0.0',
52+
devDependencies: {
53+
example: '../example-1.0.0.tgz'
54+
}
55+
})
56+
}),
57+
'example-1.0.0.tgz': File(Buffer.from(
58+
'1f8b0800000000000003ed8fc10ac2300c8677f62946cedaa5d8f5e0db64' +
59+
'5b1853d795758a38f6ee4607e261370722f4bbfce5cb4f493c9527aa39f3' +
60+
'73aa63e85cb23288688d4997fc136d304df6b945adad45e9c923375a72ed' +
61+
'4596b884817a59e5db7fe65bd277fe0923386a190ec0376afd99610b57ee' +
62+
'43d339715aa14231157b7615bbb2e100871148664a65b47b15d450dfa554' +
63+
'ccb2f890d3b4f9f57d9148241259e60112d8208a00080000',
64+
'hex'
65+
)),
66+
optional: Dir({
67+
'package.json': File({
68+
name: 'optional',
69+
version: '1.0.0',
70+
optionalDependencies: {
71+
example: '../example-1.0.0.tgz'
72+
}
73+
})
74+
})
75+
})
76+
}))
77+
78+
function setup () {
79+
cleanup()
80+
fixture.create(basedir)
81+
}
82+
83+
function cleanup () {
84+
fixture.remove(basedir)
85+
}
86+
87+
test('setup', function (t) {
88+
setup()
89+
return common.fakeRegistry.listen()
90+
})
91+
92+
test('optional dependency identification', function (t) {
93+
return common.npm(['install', '--no-optional'], {cwd: optionaldir, env}).then(([code, stdout, stderr]) => {
94+
t.is(code, 0, 'no error code')
95+
t.is(stderr, '', 'no error output')
96+
t.notOk(fs.existsSync(path.join(optionaldir, 'node_modules')), 'did not install anything')
97+
t.similar(JSON.parse(fs.readFileSync(path.join(optionaldir, 'package-lock.json'), 'utf8')), {
98+
dependencies: {
99+
a: {
100+
version: 'file:../a-1.0.0.tgz',
101+
optional: true
102+
},
103+
b: {
104+
version: 'file:../b-1.0.0.tgz',
105+
optional: true
106+
},
107+
example: {
108+
version: '1.0.0',
109+
optional: true
110+
}
111+
}
112+
}, 'locks dependencies as optional')
113+
})
114+
})
115+
116+
test('development dependency identification', function (t) {
117+
return common.npm(['install', '--only=prod'], {cwd: devdir, env}).then(([code, stdout, stderr]) => {
118+
t.is(code, 0, 'no error code')
119+
t.is(stderr, '', 'no error output')
120+
t.notOk(fs.existsSync(path.join(devdir, 'node_modules')), 'did not install anything')
121+
t.similar(JSON.parse(fs.readFileSync(path.join(devdir, 'package-lock.json'), 'utf8')), {
122+
dependencies: {
123+
a: {
124+
version: 'file:../a-1.0.0.tgz',
125+
dev: true
126+
},
127+
b: {
128+
version: 'file:../b-1.0.0.tgz',
129+
dev: true
130+
},
131+
example: {
132+
version: '1.0.0',
133+
dev: true
134+
}
135+
}
136+
}, 'locks dependencies as dev')
137+
})
138+
})
139+
140+
test('default dependency identification', function (t) {
141+
return common.npm(['install'], {cwd: optionaldir, env}).then(([code, stdout, stderr]) => {
142+
t.is(code, 0, 'no error code')
143+
t.is(stderr, '', 'no error output')
144+
t.similar(JSON.parse(fs.readFileSync(path.join(optionaldir, 'package-lock.json'), 'utf8')), {
145+
dependencies: {
146+
a: {
147+
version: 'file:../a-1.0.0.tgz',
148+
optional: true
149+
},
150+
b: {
151+
version: 'file:../b-1.0.0.tgz',
152+
optional: true
153+
},
154+
example: {
155+
version: '1.0.0',
156+
optional: true
157+
}
158+
}
159+
}, 'locks dependencies as optional')
160+
})
161+
})
162+
163+
test('cleanup', function (t) {
164+
common.fakeRegistry.close()
165+
cleanup()
166+
t.done()
167+
})

0 commit comments

Comments
 (0)