fix: emit valid JSON from approve-scripts/deny-scripts --json · npm/cli@f6270d1 · GitHub
Skip to content

Commit f6270d1

Browse files
committed
fix: emit valid JSON from approve-scripts/deny-scripts --json
1 parent b3b7197 commit f6270d1

3 files changed

Lines changed: 187 additions & 46 deletions

File tree

lib/utils/allow-scripts-cmd.js

Lines changed: 26 additions & 0 deletions

test/lib/commands/approve-scripts.js

Lines changed: 142 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,51 @@ t.test('approve-scripts --json outputs structured summary', async t => {
178178
})
179179
})
180180

181+
t.test('approve-scripts --pending --json lists unreviewed packages as JSON', async t => {
182+
const { npm, joinedOutput } = await mockNpm(t, {
183+
prefixDir: setupProject({ withScripts: ['canvas', 'sharp'] }),
184+
config: { 'allow-scripts-pending': true, json: true },
185+
})
186+
await npm.exec('approve-scripts', [])
187+
const parsed = JSON.parse(joinedOutput())
188+
const byName = Object.fromEntries(parsed.allowScripts.map((e) => [e.name, e.changes]))
189+
t.strictSame(byName, {
190+
canvas: [{ key: 'canvas@1.0.0', change: 'pending' }],
191+
sharp: [{ key: 'sharp@1.0.0', change: 'pending' }],
192+
})
193+
})
194+
195+
t.test('approve-scripts --pending --json with no unreviewed emits empty list', async t => {
196+
const { npm, joinedOutput } = await mockNpm(t, {
197+
prefixDir: setupProject({
198+
allowScripts: { canvas: true },
199+
withScripts: ['canvas'],
200+
}),
201+
config: { 'allow-scripts-pending': true, json: true },
202+
})
203+
await npm.exec('approve-scripts', [])
204+
t.strictSame(JSON.parse(joinedOutput()), { allowScripts: [] })
205+
})
206+
207+
t.test('approve-scripts --all --json with no unreviewed emits empty list', async t => {
208+
const { npm, joinedOutput } = await _mockNpm(t, {
209+
prefixDir: {
210+
'package.json': JSON.stringify({ name: 'host', version: '1.0.0' }),
211+
'package-lock.json': JSON.stringify({
212+
name: 'host',
213+
version: '1.0.0',
214+
lockfileVersion: 3,
215+
requires: true,
216+
packages: { '': { name: 'host', version: '1.0.0' } },
217+
}),
218+
node_modules: {},
219+
},
220+
config: { all: true, json: true },
221+
})
222+
await npm.exec('approve-scripts', [])
223+
t.strictSame(JSON.parse(joinedOutput()), { allowScripts: [] })
224+
})
225+
181226
t.test('approve-scripts --all with no unreviewed packages prints message', async t => {
182227
const { npm, joinedOutput } = await _mockNpm(t, {
183228
prefixDir: {
@@ -231,63 +276,83 @@ t.test('approve-scripts --pending lists package with no version', async t => {
231276
t.pass()
232277
})
233278

234-
t.test('approve-scripts groups multiple installed versions of the same package', async t => {
235-
// Two versions of lodash exist in the tree; both have install scripts.
236-
// groupByPackage should put them in the same group (hits the
237-
// `if (!groups[key])` falsy branch on the second node).
238-
const { npm, prefix } = await _mockNpm(t, {
239-
prefixDir: {
240-
'package.json': JSON.stringify({
241-
name: 'host',
242-
version: '1.0.0',
243-
dependencies: { 'top-of-tree': '*' },
244-
}),
245-
'package-lock.json': JSON.stringify({
246-
name: 'host',
279+
const twoVersionFixture = {
280+
'package.json': JSON.stringify({
281+
name: 'host',
282+
version: '1.0.0',
283+
dependencies: { 'top-of-tree': '*' },
284+
}),
285+
'package-lock.json': JSON.stringify({
286+
name: 'host',
287+
version: '1.0.0',
288+
lockfileVersion: 3,
289+
requires: true,
290+
packages: {
291+
'': { name: 'host', version: '1.0.0', dependencies: { 'top-of-tree': '*' } },
292+
'node_modules/lodash': {
293+
version: '4.17.21',
294+
resolved: 'https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz',
295+
hasInstallScript: true,
296+
},
297+
'node_modules/top-of-tree': {
247298
version: '1.0.0',
248-
lockfileVersion: 3,
249-
requires: true,
250-
packages: {
251-
'': { name: 'host', version: '1.0.0', dependencies: { 'top-of-tree': '*' } },
252-
'node_modules/lodash': {
253-
version: '4.17.21',
254-
resolved: 'https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz',
255-
hasInstallScript: true,
256-
},
257-
'node_modules/top-of-tree': {
258-
version: '1.0.0',
259-
resolved: 'https://registry.npmjs.org/top-of-tree/-/top-of-tree-1.0.0.tgz',
260-
dependencies: { lodash: '3.10.1' },
261-
},
262-
'node_modules/top-of-tree/node_modules/lodash': {
263-
version: '3.10.1',
264-
resolved: 'https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz',
265-
hasInstallScript: true,
266-
},
267-
},
299+
resolved: 'https://registry.npmjs.org/top-of-tree/-/top-of-tree-1.0.0.tgz',
300+
dependencies: { lodash: '3.10.1' },
301+
},
302+
'node_modules/top-of-tree/node_modules/lodash': {
303+
version: '3.10.1',
304+
resolved: 'https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz',
305+
hasInstallScript: true,
306+
},
307+
},
308+
}),
309+
node_modules: {
310+
lodash: {
311+
'package.json': JSON.stringify({
312+
name: 'lodash',
313+
version: '4.17.21',
314+
scripts: { install: 'echo install' },
268315
}),
316+
},
317+
'top-of-tree': {
318+
'package.json': JSON.stringify({ name: 'top-of-tree', version: '1.0.0' }),
269319
node_modules: {
270320
lodash: {
271321
'package.json': JSON.stringify({
272322
name: 'lodash',
273-
version: '4.17.21',
323+
version: '3.10.1',
274324
scripts: { install: 'echo install' },
275325
}),
276326
},
277-
'top-of-tree': {
278-
'package.json': JSON.stringify({ name: 'top-of-tree', version: '1.0.0' }),
279-
node_modules: {
280-
lodash: {
281-
'package.json': JSON.stringify({
282-
name: 'lodash',
283-
version: '3.10.1',
284-
scripts: { install: 'echo install' },
285-
}),
286-
},
287-
},
288-
},
289327
},
290328
},
329+
},
330+
}
331+
332+
t.test('approve-scripts --pending --json groups multiple versions under one name', async t => {
333+
// Two versions of lodash are unreviewed; pendingSummary must collapse
334+
// them into a single `lodash` entry (hits the `groups.has(display)`
335+
// truthy branch on the second node).
336+
const { npm, joinedOutput } = await _mockNpm(t, {
337+
prefixDir: twoVersionFixture,
338+
config: { 'allow-scripts-pending': true, json: true },
339+
})
340+
await npm.exec('approve-scripts', [])
341+
const parsed = JSON.parse(joinedOutput())
342+
t.strictSame(parsed.allowScripts.map((e) => e.name), ['lodash'])
343+
t.strictSame(parsed.allowScripts[0].changes.map((c) => c.key).sort(), [
344+
'lodash@3.10.1',
345+
'lodash@4.17.21',
346+
])
347+
t.ok(parsed.allowScripts[0].changes.every((c) => c.change === 'pending'))
348+
})
349+
350+
t.test('approve-scripts groups multiple installed versions of the same package', async t => {
351+
// Two versions of lodash exist in the tree; both have install scripts.
352+
// groupByPackage should put them in the same group (hits the
353+
// `if (!groups[key])` falsy branch on the second node).
354+
const { npm, prefix } = await _mockNpm(t, {
355+
prefixDir: twoVersionFixture,
291356
})
292357
await npm.exec('approve-scripts', ['lodash'])
293358

@@ -328,6 +393,37 @@ t.test('approve-scripts --pending handles node with no version', async t => {
328393
t.match(mockSync.joinedOutput(), / no-version-pkg \(install: do-stuff\)/)
329394
})
330395

396+
t.test('approve-scripts --pending --json handles node with no version', async t => {
397+
// Exercise pendingSummary's `version ? ... : display` falsy branch: the
398+
// key is the bare name when the node has no version field.
399+
const { npm, joinedOutput } = await _mockNpm(t, {
400+
prefixDir: {
401+
'package.json': JSON.stringify({ name: 'host', version: '1.0.0' }),
402+
'package-lock.json': JSON.stringify({
403+
name: 'host',
404+
version: '1.0.0',
405+
lockfileVersion: 3,
406+
requires: true,
407+
packages: { '': { name: 'host', version: '1.0.0' } },
408+
}),
409+
node_modules: {},
410+
},
411+
config: { 'allow-scripts-pending': true, json: true },
412+
mocks: {
413+
'{LIB}/utils/check-allow-scripts.js': async () => [{
414+
node: { packageName: 'no-version-pkg', name: 'no-version-pkg', version: undefined },
415+
scripts: { install: 'do-stuff' },
416+
}],
417+
},
418+
})
419+
await npm.exec('approve-scripts', [])
420+
t.strictSame(JSON.parse(joinedOutput()), {
421+
allowScripts: [
422+
{ name: 'no-version-pkg', changes: [{ key: 'no-version-pkg', change: 'pending' }] },
423+
],
424+
})
425+
})
426+
331427
t.test('forbidden semver range in package.json#allowScripts is dropped with a warning', async t => {
332428
// End-to-end: project declares a caret range in allowScripts. The
333429
// resolver must drop the entry, emit a warning, and the matching node

test/lib/commands/deny-scripts.js

Lines changed: 19 additions & 0 deletions

0 commit comments

Comments
 (0)