diff --git a/docs/content/commands/npm-dedupe.md b/docs/content/commands/npm-dedupe.md index 377e17d814a6f..53d2e64272a67 100644 --- a/docs/content/commands/npm-dedupe.md +++ b/docs/content/commands/npm-dedupe.md @@ -72,6 +72,12 @@ result in new modules being installed. Using `npm find-dupes` will run the command in `--dry-run` mode. +Note that by default `npm dedupe` will not update the semver values of direct +dependencies in your project `package.json`, if you want to also update +values in `package.json` you can run: `npm dedupe --save` (or add the +`save=true` option to a [configuration file](/configuring-npm/npmrc) +to make that the default behavior). + ### Configuration diff --git a/docs/content/commands/npm-update.md b/docs/content/commands/npm-update.md index ad02118e4687f..1889d60569fe5 100644 --- a/docs/content/commands/npm-update.md +++ b/docs/content/commands/npm-update.md @@ -27,6 +27,12 @@ packages. If no package name is specified, all packages in the specified location (global or local) will be updated. +Note that by default `npm update` will not update the semver values of direct +dependencies in your project `package.json`, if you want to also update +values in `package.json` you can run: `npm update --save` (or add the +`save=true` option to a [configuration file](/configuring-npm/npmrc) +to make that the default behavior). + ### Example For the examples below, assume that the current package is `app` and it depends diff --git a/docs/content/using-npm/config.md b/docs/content/using-npm/config.md index fe197e344dbc0..4cb9bdb0fcce3 100644 --- a/docs/content/using-npm/config.md +++ b/docs/content/using-npm/config.md @@ -1326,7 +1326,8 @@ The base URL of the npm registry. #### `save` -* Default: true +* Default: true unless when using `npm update` or `npm dedupe` where it + defaults to `false` * Type: Boolean Save installed packages to a package.json file as dependencies. @@ -1334,6 +1335,8 @@ Save installed packages to a package.json file as dependencies. When used with the `npm rm` command, removes the dependency from package.json. +Will also prevent writing to package-lock.json if set to `false`. + diff --git a/lib/commands/dedupe.js b/lib/commands/dedupe.js index cc4b119d09d2a..1c270249adb3f 100644 --- a/lib/commands/dedupe.js +++ b/lib/commands/dedupe.js @@ -13,6 +13,7 @@ class Dedupe extends ArboristWorkspaceCmd { 'legacy-bundling', 'strict-peer-deps', 'package-lock', + 'save', 'omit', 'ignore-scripts', 'audit', @@ -29,6 +30,12 @@ class Dedupe extends ArboristWorkspaceCmd { throw er } + // In the context of `npm dedupe` the save + // config value should default to `false` + const save = this.npm.config.isDefault('save') + ? false + : this.npm.config.get('save') + const dryRun = this.npm.config.get('dry-run') const where = this.npm.prefix const opts = { @@ -36,6 +43,7 @@ class Dedupe extends ArboristWorkspaceCmd { log, path: where, dryRun, + save, workspaces: this.workspaceNames, } const arb = new Arborist(opts) diff --git a/lib/commands/update.js b/lib/commands/update.js index ace6738400fad..a5374d0ae41ca 100644 --- a/lib/commands/update.js +++ b/lib/commands/update.js @@ -16,6 +16,7 @@ class Update extends ArboristWorkspaceCmd { 'legacy-bundling', 'strict-peer-deps', 'package-lock', + 'save', 'omit', 'ignore-scripts', 'audit', diff --git a/lib/utils/config/definitions.js b/lib/utils/config/definitions.js index ac8a4e2f6749d..111ebd7898e67 100644 --- a/lib/utils/config/definitions.js +++ b/lib/utils/config/definitions.js @@ -1583,6 +1583,8 @@ define('registry', { define('save', { default: true, + defaultDescription: `true unless when using \`npm update\` or + \`npm dedupe\` where it defaults to \`false\``, usage: '-S|--save|--no-save|--save-prod|--save-dev|--save-optional|--save-peer', type: Boolean, short: 'S', @@ -1591,6 +1593,8 @@ define('save', { When used with the \`npm rm\` command, removes the dependency from package.json. + + Will also prevent writing to package-lock.json if set to \`false\`. `, flatten, }) diff --git a/smoke-tests/index.js b/smoke-tests/index.js index 06ca3dee6422e..2f8258e073d0b 100644 --- a/smoke-tests/index.js +++ b/smoke-tests/index.js @@ -267,3 +267,13 @@ t.test('npm pkg', async t => { 'should have expected npm pkg delete modified package.json result' ) }) + +t.test('npm update --save', async t => { + const cmd = `${npmBin} update --save` + const cmdRes = await exec(cmd) + + t.matchSnapshot(cmdRes.replace(/in.*s/, ''), 'should have expected update --save reify output') + t.matchSnapshot(readFile('package.json'), 'should have expected update --save package.json result') + t.matchSnapshot(readFile('package-lock.json'), 'should have expected update --save lockfile result') +}) + diff --git a/tap-snapshots/smoke-tests/index.js.test.cjs b/tap-snapshots/smoke-tests/index.js.test.cjs index c1316e04d7ad9..b25f9da94e935 100644 --- a/tap-snapshots/smoke-tests/index.js.test.cjs +++ b/tap-snapshots/smoke-tests/index.js.test.cjs @@ -666,6 +666,64 @@ removed 1 package ` +exports[`smoke-tests/index.js TAP npm update --save > should have expected update --save lockfile result 1`] = ` +{ + "name": "project", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "project", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "abbrev": "^1.1.1" + } + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + } + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + } + } +} + +` + +exports[`smoke-tests/index.js TAP npm update --save > should have expected update --save package.json result 1`] = ` +{ + "name": "project", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo /"Error: no test specified/" && exit 1", + "hello": "echo Hello" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "abbrev": "^1.1.1" + } +} + +` + +exports[`smoke-tests/index.js TAP npm update --save > should have expected update --save reify output 1`] = ` + +up to date + +` + exports[`smoke-tests/index.js TAP npm update dep > should have expected update lockfile result 1`] = ` { "name": "project", diff --git a/tap-snapshots/test/lib/load-all-commands.js.test.cjs b/tap-snapshots/test/lib/load-all-commands.js.test.cjs index 6efecf2089e83..e7142c2492322 100644 --- a/tap-snapshots/test/lib/load-all-commands.js.test.cjs +++ b/tap-snapshots/test/lib/load-all-commands.js.test.cjs @@ -170,6 +170,7 @@ npm dedupe Options: [--global-style] [--legacy-bundling] [--strict-peer-deps] [--no-package-lock] +[-S|--save|--no-save|--save-prod|--save-dev|--save-optional|--save-peer] [--omit [--omit ...]] [--ignore-scripts] [--no-audit] [--no-bin-links] [--no-fund] [--dry-run] [-w|--workspace [-w|--workspace ...]] @@ -1061,8 +1062,10 @@ npm update [...] Options: [-g|--global] [--global-style] [--legacy-bundling] [--strict-peer-deps] -[--no-package-lock] [--omit [--omit ...]] -[--ignore-scripts] [--no-audit] [--no-bin-links] [--no-fund] [--dry-run] +[--no-package-lock] +[-S|--save|--no-save|--save-prod|--save-dev|--save-optional|--save-peer] +[--omit [--omit ...]] [--ignore-scripts] +[--no-audit] [--no-bin-links] [--no-fund] [--dry-run] [-w|--workspace [-w|--workspace ...]] [-ws|--workspaces] [--include-workspace-root] diff --git a/tap-snapshots/test/lib/utils/config/definitions.js.test.cjs b/tap-snapshots/test/lib/utils/config/definitions.js.test.cjs index 84bb22ff0ef59..1ee8400d7ae85 100644 --- a/tap-snapshots/test/lib/utils/config/definitions.js.test.cjs +++ b/tap-snapshots/test/lib/utils/config/definitions.js.test.cjs @@ -1406,13 +1406,16 @@ The base URL of the npm registry. exports[`test/lib/utils/config/definitions.js TAP > config description for save 1`] = ` #### \`save\` -* Default: true +* Default: true unless when using \`npm update\` or \`npm dedupe\` where it + defaults to \`false\` * Type: Boolean Save installed packages to a package.json file as dependencies. When used with the \`npm rm\` command, removes the dependency from package.json. + +Will also prevent writing to package-lock.json if set to \`false\`. ` exports[`test/lib/utils/config/definitions.js TAP > config description for save-bundle 1`] = ` diff --git a/tap-snapshots/test/lib/utils/config/describe-all.js.test.cjs b/tap-snapshots/test/lib/utils/config/describe-all.js.test.cjs index 3db90f7679d4e..31fab28de6f48 100644 --- a/tap-snapshots/test/lib/utils/config/describe-all.js.test.cjs +++ b/tap-snapshots/test/lib/utils/config/describe-all.js.test.cjs @@ -1200,7 +1200,8 @@ The base URL of the npm registry. #### \`save\` -* Default: true +* Default: true unless when using \`npm update\` or \`npm dedupe\` where it + defaults to \`false\` * Type: Boolean Save installed packages to a package.json file as dependencies. @@ -1208,6 +1209,8 @@ Save installed packages to a package.json file as dependencies. When used with the \`npm rm\` command, removes the dependency from package.json. +Will also prevent writing to package-lock.json if set to \`false\`. + diff --git a/tap-snapshots/test/lib/utils/npm-usage.js.test.cjs b/tap-snapshots/test/lib/utils/npm-usage.js.test.cjs index 0e48cfa613149..ab8f1cf3b4e87 100644 --- a/tap-snapshots/test/lib/utils/npm-usage.js.test.cjs +++ b/tap-snapshots/test/lib/utils/npm-usage.js.test.cjs @@ -311,6 +311,7 @@ All commands: Options: [--global-style] [--legacy-bundling] [--strict-peer-deps] [--no-package-lock] + [-S|--save|--no-save|--save-prod|--save-dev|--save-optional|--save-peer] [--omit [--omit ...]] [--ignore-scripts] [--no-audit] [--no-bin-links] [--no-fund] [--dry-run] [-w|--workspace [-w|--workspace ...]] @@ -1096,8 +1097,10 @@ All commands: Options: [-g|--global] [--global-style] [--legacy-bundling] [--strict-peer-deps] - [--no-package-lock] [--omit [--omit ...]] - [--ignore-scripts] [--no-audit] [--no-bin-links] [--no-fund] [--dry-run] + [--no-package-lock] + [-S|--save|--no-save|--save-prod|--save-dev|--save-optional|--save-peer] + [--omit [--omit ...]] [--ignore-scripts] + [--no-audit] [--no-bin-links] [--no-fund] [--dry-run] [-w|--workspace [-w|--workspace ...]] [-ws|--workspaces] [--include-workspace-root] diff --git a/test/lib/commands/dedupe.js b/test/lib/commands/dedupe.js index 2e2fae238103f..bf6964081ca79 100644 --- a/test/lib/commands/dedupe.js +++ b/test/lib/commands/dedupe.js @@ -38,11 +38,12 @@ t.test('should remove dupes using Arborist', async (t) => { }) t.test('should remove dupes using Arborist - no arguments', async (t) => { - t.plan(1) + t.plan(2) const { npm } = await loadMockNpm(t, { mocks: { '@npmcli/arborist': function (args) { t.ok(args.dryRun, 'gets dryRun from config') + t.ok(args.save, 'gets user-set save value from config') this.dedupe = () => {} }, '../../lib/utils/reify-output.js': () => {}, @@ -50,6 +51,7 @@ t.test('should remove dupes using Arborist - no arguments', async (t) => { }, config: { 'dry-run': true, + save: true, }, }) await npm.exec('dedupe', []) diff --git a/workspaces/arborist/lib/arborist/reify.js b/workspaces/arborist/lib/arborist/reify.js index 87f521de5483b..ad8d8b5c09caf 100644 --- a/workspaces/arborist/lib/arborist/reify.js +++ b/workspaces/arborist/lib/arborist/reify.js @@ -1142,8 +1142,21 @@ module.exports = cls => class Reifier extends cls { // for install failures. Those still end up in the shrinkwrap, so we // save it first, then prune out the optional trash, and then return it. - // support save=false option - if (options.save === false || this[_global] || this[_dryRun]) { + const skipSave = options.save === false + const save = !skipSave + const hasUpdates = this[_updateAll] || this[_updateNames].length + + // we're going to completely skip save ideal tree in case of a global or + // dry-run install and also if the save option is set to false, EXCEPT for + // update since the expected behavior for npm7+ is for update to + // NOT save to package.json, we make that exception since we still want + // saveIdealTree to be able to write the lockfile + const skipSaveIdealTree = + (skipSave && !hasUpdates) + || this[_global] + || this[_dryRun] + + if (skipSaveIdealTree) { return false } @@ -1165,7 +1178,7 @@ module.exports = cls => class Reifier extends cls { // that we couldn't resolve, this MAY be missing. if we haven't // blown up by now, it's because it was not a problem, though, so // just move on. - if (!child) { + if (!child || !addTree.isTop) { continue } @@ -1284,25 +1297,32 @@ module.exports = cls => class Reifier extends cls { return nodes } - // when using update all alongside with save, we'll make - // sure to refresh every dependency of the root idealTree - if (this[_updateAll] && options.save) { - const nodes = retrieveUpdatedNodes() - updateNodes(nodes) - } else { - // resolvedAdd is the list of user add requests, but with names added - // to things like git repos and tarball file/urls. However, if the - // user requested 'foo@', and we have a foo@file:../foo, then we should - // end up saving the spec we actually used, not whatever they gave us. - if (this[_resolvedAdd].length) { - updateNodes(this[_resolvedAdd]) - } - - // if updating given dependencies by name, restrict the list of - // nodes to check to only those currently in _updateNames - if (this[_updateNames].length && options.save) { - const nodes = retrieveUpdatedNodes(this[_updateNames]) + if (save) { + // when using update all alongside with save, we'll make + // sure to refresh every dependency of the root idealTree + if (this[_updateAll]) { + const nodes = retrieveUpdatedNodes() updateNodes(nodes) + } else { + // resolvedAdd is the list of user add requests, but with names added + // to things like git repos and tarball file/urls. However, if the + // user requested 'foo@', and we have a foo@file:../foo, then we should + // end up saving the spec we actually used, not whatever they gave us. + if (this[_resolvedAdd].length) { + updateNodes(this[_resolvedAdd]) + } + + // if updating given dependencies by name, restrict the list of + // nodes to check to only those currently in _updateNames + if (this[_updateNames].length) { + const nodes = retrieveUpdatedNodes(this[_updateNames]) + updateNodes(nodes) + } + + // grab any from explicitRequests that had deps removed + for (const { from: tree } of this.explicitRequests) { + updatedTrees.add(tree) + } } } @@ -1338,15 +1358,12 @@ module.exports = cls => class Reifier extends cls { await pkgJson.save() } - // grab any from explicitRequests that had deps removed - for (const { from: tree } of this.explicitRequests) { - updatedTrees.add(tree) - } - - for (const tree of updatedTrees) { - // refresh the edges so they have the correct specs - tree.package = tree.package - promises.push(updatePackageJson(tree)) + if (save) { + for (const tree of updatedTrees) { + // refresh the edges so they have the correct specs + tree.package = tree.package + promises.push(updatePackageJson(tree)) + } } await Promise.all(promises) diff --git a/workspaces/arborist/tap-snapshots/test/arborist/reify.js.test.cjs b/workspaces/arborist/tap-snapshots/test/arborist/reify.js.test.cjs index a38242140a231..1f1bbafa373c6 100644 --- a/workspaces/arborist/tap-snapshots/test/arborist/reify.js.test.cjs +++ b/workspaces/arborist/tap-snapshots/test/arborist/reify.js.test.cjs @@ -17113,7 +17113,7 @@ ArboristNode { } ` -exports[`test/arborist/reify.js TAP optional dependency failures optional-dep-allinstall-fail > expect resolving Promise 1`] = ` +exports[`test/arborist/reify.js TAP optional dependency failures optional-dep-allinstall-fail save=false > expect resolving Promise 1`] = ` ArboristNode { "edgesOut": Map { "@isaacs/testing-fail-allinstall" => EdgeOut { @@ -17125,14 +17125,33 @@ ArboristNode { }, "isProjectRoot": true, "location": "", - "name": "tap-testdir-reify-optional-dependency-failures-optional-dep-allinstall-fail", + "name": "tap-testdir-reify-optional-dependency-failures-optional-dep-allinstall-fail-save-false", "packageName": "optional-dep-allinstall-fail", - "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-dep-allinstall-fail", + "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-dep-allinstall-fail-save-false", "version": "1.0.0", } ` -exports[`test/arborist/reify.js TAP optional dependency failures optional-dep-install-fail > expect resolving Promise 1`] = ` +exports[`test/arborist/reify.js TAP optional dependency failures optional-dep-allinstall-fail save=true > expect resolving Promise 1`] = ` +ArboristNode { + "edgesOut": Map { + "@isaacs/testing-fail-allinstall" => EdgeOut { + "name": "@isaacs/testing-fail-allinstall", + "spec": "^1.0.0", + "to": null, + "type": "optional", + }, + }, + "isProjectRoot": true, + "location": "", + "name": "tap-testdir-reify-optional-dependency-failures-optional-dep-allinstall-fail-save-true", + "packageName": "optional-dep-allinstall-fail", + "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-dep-allinstall-fail-save-true", + "version": "1.0.0", +} +` + +exports[`test/arborist/reify.js TAP optional dependency failures optional-dep-install-fail save=false > expect resolving Promise 1`] = ` ArboristNode { "edgesOut": Map { "@isaacs/testing-fail-install" => EdgeOut { @@ -17144,14 +17163,33 @@ ArboristNode { }, "isProjectRoot": true, "location": "", - "name": "tap-testdir-reify-optional-dependency-failures-optional-dep-install-fail", + "name": "tap-testdir-reify-optional-dependency-failures-optional-dep-install-fail-save-false", "packageName": "optional-dep-install-fail", - "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-dep-install-fail", + "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-dep-install-fail-save-false", "version": "1.0.0", } ` -exports[`test/arborist/reify.js TAP optional dependency failures optional-dep-postinstall-fail > expect resolving Promise 1`] = ` +exports[`test/arborist/reify.js TAP optional dependency failures optional-dep-install-fail save=true > expect resolving Promise 1`] = ` +ArboristNode { + "edgesOut": Map { + "@isaacs/testing-fail-install" => EdgeOut { + "name": "@isaacs/testing-fail-install", + "spec": "^1.0.0", + "to": null, + "type": "optional", + }, + }, + "isProjectRoot": true, + "location": "", + "name": "tap-testdir-reify-optional-dependency-failures-optional-dep-install-fail-save-true", + "packageName": "optional-dep-install-fail", + "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-dep-install-fail-save-true", + "version": "1.0.0", +} +` + +exports[`test/arborist/reify.js TAP optional dependency failures optional-dep-postinstall-fail save=false > expect resolving Promise 1`] = ` ArboristNode { "children": Map { "@isaacs/testing-fail-postinstall" => ArboristNode { @@ -17166,7 +17204,7 @@ ArboristNode { "location": "node_modules/@isaacs/testing-fail-postinstall", "name": "@isaacs/testing-fail-postinstall", "optional": true, - "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-dep-postinstall-fail/node_modules/@isaacs/testing-fail-postinstall", + "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-dep-postinstall-fail-save-false/node_modules/@isaacs/testing-fail-postinstall", "resolved": "https://registry.npmjs.org/@isaacs/testing-fail-postinstall/-/testing-fail-postinstall-1.0.0.tgz", "version": "1.0.0", }, @@ -17181,14 +17219,70 @@ ArboristNode { }, "isProjectRoot": true, "location": "", - "name": "tap-testdir-reify-optional-dependency-failures-optional-dep-postinstall-fail", + "name": "tap-testdir-reify-optional-dependency-failures-optional-dep-postinstall-fail-save-false", "packageName": "optional-dep-postinstall-fail", - "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-dep-postinstall-fail", + "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-dep-postinstall-fail-save-false", "version": "1.0.0", } ` -exports[`test/arborist/reify.js TAP optional dependency failures optional-dep-preinstall-fail > expect resolving Promise 1`] = ` +exports[`test/arborist/reify.js TAP optional dependency failures optional-dep-postinstall-fail save=true > expect resolving Promise 1`] = ` +ArboristNode { + "children": Map { + "@isaacs/testing-fail-postinstall" => ArboristNode { + "edgesIn": Set { + EdgeIn { + "from": "", + "name": "@isaacs/testing-fail-postinstall", + "spec": "^1.0.0", + "type": "optional", + }, + }, + "location": "node_modules/@isaacs/testing-fail-postinstall", + "name": "@isaacs/testing-fail-postinstall", + "optional": true, + "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-dep-postinstall-fail-save-true/node_modules/@isaacs/testing-fail-postinstall", + "resolved": "https://registry.npmjs.org/@isaacs/testing-fail-postinstall/-/testing-fail-postinstall-1.0.0.tgz", + "version": "1.0.0", + }, + }, + "edgesOut": Map { + "@isaacs/testing-fail-postinstall" => EdgeOut { + "name": "@isaacs/testing-fail-postinstall", + "spec": "^1.0.0", + "to": "node_modules/@isaacs/testing-fail-postinstall", + "type": "optional", + }, + }, + "isProjectRoot": true, + "location": "", + "name": "tap-testdir-reify-optional-dependency-failures-optional-dep-postinstall-fail-save-true", + "packageName": "optional-dep-postinstall-fail", + "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-dep-postinstall-fail-save-true", + "version": "1.0.0", +} +` + +exports[`test/arborist/reify.js TAP optional dependency failures optional-dep-preinstall-fail save=false > expect resolving Promise 1`] = ` +ArboristNode { + "edgesOut": Map { + "@isaacs/testing-fail-preinstall" => EdgeOut { + "name": "@isaacs/testing-fail-preinstall", + "spec": "^1.0.0", + "to": null, + "type": "optional", + }, + }, + "isProjectRoot": true, + "location": "", + "name": "tap-testdir-reify-optional-dependency-failures-optional-dep-preinstall-fail-save-false", + "packageName": "optional-dep-preinstall-fail", + "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-dep-preinstall-fail-save-false", + "version": "1.0.0", +} +` + +exports[`test/arborist/reify.js TAP optional dependency failures optional-dep-preinstall-fail save=true > expect resolving Promise 1`] = ` ArboristNode { "edgesOut": Map { "@isaacs/testing-fail-preinstall" => EdgeOut { @@ -17200,14 +17294,14 @@ ArboristNode { }, "isProjectRoot": true, "location": "", - "name": "tap-testdir-reify-optional-dependency-failures-optional-dep-preinstall-fail", + "name": "tap-testdir-reify-optional-dependency-failures-optional-dep-preinstall-fail-save-true", "packageName": "optional-dep-preinstall-fail", - "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-dep-preinstall-fail", + "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-dep-preinstall-fail-save-true", "version": "1.0.0", } ` -exports[`test/arborist/reify.js TAP optional dependency failures optional-dep-tgz-missing > expect resolving Promise 1`] = ` +exports[`test/arborist/reify.js TAP optional dependency failures optional-dep-tgz-missing save=false > expect resolving Promise 1`] = ` ArboristNode { "edgesOut": Map { "@isaacs/testing-missing-tgz" => EdgeOut { @@ -17219,14 +17313,33 @@ ArboristNode { }, "isProjectRoot": true, "location": "", - "name": "tap-testdir-reify-optional-dependency-failures-optional-dep-tgz-missing", + "name": "tap-testdir-reify-optional-dependency-failures-optional-dep-tgz-missing-save-false", "packageName": "@isaacs/testing-optional-dep-tgz-missing", - "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-dep-tgz-missing", + "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-dep-tgz-missing-save-false", "version": "1.0.0", } ` -exports[`test/arborist/reify.js TAP optional dependency failures optional-metadep-allinstall-fail > expect resolving Promise 1`] = ` +exports[`test/arborist/reify.js TAP optional dependency failures optional-dep-tgz-missing save=true > expect resolving Promise 1`] = ` +ArboristNode { + "edgesOut": Map { + "@isaacs/testing-missing-tgz" => EdgeOut { + "name": "@isaacs/testing-missing-tgz", + "spec": "^1.0.1", + "to": null, + "type": "optional", + }, + }, + "isProjectRoot": true, + "location": "", + "name": "tap-testdir-reify-optional-dependency-failures-optional-dep-tgz-missing-save-true", + "packageName": "@isaacs/testing-optional-dep-tgz-missing", + "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-dep-tgz-missing-save-true", + "version": "1.0.0", +} +` + +exports[`test/arborist/reify.js TAP optional dependency failures optional-metadep-allinstall-fail save=false > expect resolving Promise 1`] = ` ArboristNode { "edgesOut": Map { "@isaacs/testing-prod-dep-allinstall-fail" => EdgeOut { @@ -17238,14 +17351,33 @@ ArboristNode { }, "isProjectRoot": true, "location": "", - "name": "tap-testdir-reify-optional-dependency-failures-optional-metadep-allinstall-fail", + "name": "tap-testdir-reify-optional-dependency-failures-optional-metadep-allinstall-fail-save-false", + "packageName": "optional-metadep-allinstall-fail", + "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-metadep-allinstall-fail-save-false", + "version": "1.0.0", +} +` + +exports[`test/arborist/reify.js TAP optional dependency failures optional-metadep-allinstall-fail save=true > expect resolving Promise 1`] = ` +ArboristNode { + "edgesOut": Map { + "@isaacs/testing-prod-dep-allinstall-fail" => EdgeOut { + "name": "@isaacs/testing-prod-dep-allinstall-fail", + "spec": "^1.0.1", + "to": null, + "type": "optional", + }, + }, + "isProjectRoot": true, + "location": "", + "name": "tap-testdir-reify-optional-dependency-failures-optional-metadep-allinstall-fail-save-true", "packageName": "optional-metadep-allinstall-fail", - "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-metadep-allinstall-fail", + "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-metadep-allinstall-fail-save-true", "version": "1.0.0", } ` -exports[`test/arborist/reify.js TAP optional dependency failures optional-metadep-install-fail > expect resolving Promise 1`] = ` +exports[`test/arborist/reify.js TAP optional dependency failures optional-metadep-install-fail save=false > expect resolving Promise 1`] = ` ArboristNode { "edgesOut": Map { "@isaacs/testing-prod-dep-install-fail" => EdgeOut { @@ -17257,14 +17389,33 @@ ArboristNode { }, "isProjectRoot": true, "location": "", - "name": "tap-testdir-reify-optional-dependency-failures-optional-metadep-install-fail", + "name": "tap-testdir-reify-optional-dependency-failures-optional-metadep-install-fail-save-false", + "packageName": "optional-metadep-install-fail", + "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-metadep-install-fail-save-false", + "version": "1.0.0", +} +` + +exports[`test/arborist/reify.js TAP optional dependency failures optional-metadep-install-fail save=true > expect resolving Promise 1`] = ` +ArboristNode { + "edgesOut": Map { + "@isaacs/testing-prod-dep-install-fail" => EdgeOut { + "name": "@isaacs/testing-prod-dep-install-fail", + "spec": "^1.0.1", + "to": null, + "type": "optional", + }, + }, + "isProjectRoot": true, + "location": "", + "name": "tap-testdir-reify-optional-dependency-failures-optional-metadep-install-fail-save-true", "packageName": "optional-metadep-install-fail", - "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-metadep-install-fail", + "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-metadep-install-fail-save-true", "version": "1.0.0", } ` -exports[`test/arborist/reify.js TAP optional dependency failures optional-metadep-postinstall-fail > expect resolving Promise 1`] = ` +exports[`test/arborist/reify.js TAP optional dependency failures optional-metadep-postinstall-fail save=false > expect resolving Promise 1`] = ` ArboristNode { "edgesOut": Map { "@isaacs/testing-prod-dep-postinstall-fail" => EdgeOut { @@ -17276,14 +17427,33 @@ ArboristNode { }, "isProjectRoot": true, "location": "", - "name": "tap-testdir-reify-optional-dependency-failures-optional-metadep-postinstall-fail", + "name": "tap-testdir-reify-optional-dependency-failures-optional-metadep-postinstall-fail-save-false", + "packageName": "optional-metadep-postinstall-fail", + "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-metadep-postinstall-fail-save-false", + "version": "1.0.0", +} +` + +exports[`test/arborist/reify.js TAP optional dependency failures optional-metadep-postinstall-fail save=true > expect resolving Promise 1`] = ` +ArboristNode { + "edgesOut": Map { + "@isaacs/testing-prod-dep-postinstall-fail" => EdgeOut { + "name": "@isaacs/testing-prod-dep-postinstall-fail", + "spec": "^1.0.1", + "to": null, + "type": "optional", + }, + }, + "isProjectRoot": true, + "location": "", + "name": "tap-testdir-reify-optional-dependency-failures-optional-metadep-postinstall-fail-save-true", "packageName": "optional-metadep-postinstall-fail", - "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-metadep-postinstall-fail", + "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-metadep-postinstall-fail-save-true", "version": "1.0.0", } ` -exports[`test/arborist/reify.js TAP optional dependency failures optional-metadep-preinstall-fail > expect resolving Promise 1`] = ` +exports[`test/arborist/reify.js TAP optional dependency failures optional-metadep-preinstall-fail save=false > expect resolving Promise 1`] = ` ArboristNode { "edgesOut": Map { "@isaacs/testing-prod-dep-preinstall-fail" => EdgeOut { @@ -17295,14 +17465,33 @@ ArboristNode { }, "isProjectRoot": true, "location": "", - "name": "tap-testdir-reify-optional-dependency-failures-optional-metadep-preinstall-fail", + "name": "tap-testdir-reify-optional-dependency-failures-optional-metadep-preinstall-fail-save-false", + "packageName": "optional-metadep-preinstall-fail", + "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-metadep-preinstall-fail-save-false", + "version": "1.0.0", +} +` + +exports[`test/arborist/reify.js TAP optional dependency failures optional-metadep-preinstall-fail save=true > expect resolving Promise 1`] = ` +ArboristNode { + "edgesOut": Map { + "@isaacs/testing-prod-dep-preinstall-fail" => EdgeOut { + "name": "@isaacs/testing-prod-dep-preinstall-fail", + "spec": "^1.0.1", + "to": null, + "type": "optional", + }, + }, + "isProjectRoot": true, + "location": "", + "name": "tap-testdir-reify-optional-dependency-failures-optional-metadep-preinstall-fail-save-true", "packageName": "optional-metadep-preinstall-fail", - "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-metadep-preinstall-fail", + "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-metadep-preinstall-fail-save-true", "version": "1.0.0", } ` -exports[`test/arborist/reify.js TAP optional dependency failures optional-metadep-tgz-missing > expect resolving Promise 1`] = ` +exports[`test/arborist/reify.js TAP optional dependency failures optional-metadep-tgz-missing save=false > expect resolving Promise 1`] = ` ArboristNode { "edgesOut": Map { "@isaacs/testing-prod-dep-tgz-missing" => EdgeOut { @@ -17314,9 +17503,28 @@ ArboristNode { }, "isProjectRoot": true, "location": "", - "name": "tap-testdir-reify-optional-dependency-failures-optional-metadep-tgz-missing", + "name": "tap-testdir-reify-optional-dependency-failures-optional-metadep-tgz-missing-save-false", + "packageName": "@isaacs/testing-optional-metadep-tgz-missing", + "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-metadep-tgz-missing-save-false", + "version": "1.0.0", +} +` + +exports[`test/arborist/reify.js TAP optional dependency failures optional-metadep-tgz-missing save=true > expect resolving Promise 1`] = ` +ArboristNode { + "edgesOut": Map { + "@isaacs/testing-prod-dep-tgz-missing" => EdgeOut { + "name": "@isaacs/testing-prod-dep-tgz-missing", + "spec": "^1.0.1", + "to": null, + "type": "optional", + }, + }, + "isProjectRoot": true, + "location": "", + "name": "tap-testdir-reify-optional-dependency-failures-optional-metadep-tgz-missing-save-true", "packageName": "@isaacs/testing-optional-metadep-tgz-missing", - "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-metadep-tgz-missing", + "path": "{CWD}/test/arborist/tap-testdir-reify-optional-dependency-failures-optional-metadep-tgz-missing-save-true", "version": "1.0.0", } ` diff --git a/workspaces/arborist/test/arborist/deduper.js b/workspaces/arborist/test/arborist/deduper.js index 3ec37bd6250f4..511ba87bf36eb 100644 --- a/workspaces/arborist/test/arborist/deduper.js +++ b/workspaces/arborist/test/arborist/deduper.js @@ -18,7 +18,7 @@ const fixture = (t, p) => require('../fixtures/reify-cases/' + p)(t) const cache = t.testdir() const dedupeTree = (path, opt) => - new Arborist({ registry, path, cache, ...(opt || {}) }).dedupe(opt) + new Arborist({ registry, path, cache, save: false, ...(opt || {}) }).dedupe(opt) t.test('dedupes with actual tree', async t => { const path = fixture(t, 'dedupe-actual') diff --git a/workspaces/arborist/test/arborist/reify.js b/workspaces/arborist/test/arborist/reify.js index 50b1aebf441e8..c1052526ba89e 100644 --- a/workspaces/arborist/test/arborist/reify.js +++ b/workspaces/arborist/test/arborist/reify.js @@ -256,6 +256,7 @@ t.test('a workspace with a duplicated nested conflicted dep', t => t.test('testing-peer-deps nested with update', t => t.resolveMatchSnapshot(printReified(fixture(t, 'testing-peer-deps-nested'), { update: { names: ['@isaacs/testing-peer-deps'] }, + save: false, }))) t.test('update a bundling node without updating all of its deps', t => { @@ -392,7 +393,7 @@ t.test('multiple bundles at the same level', t => { t.test('update a node without updating its children', t => t.resolveMatchSnapshot(printReified(fixture(t, 'once-outdated'), - { update: { names: ['once'] } }))) + { update: { names: ['once'] }, save: false }))) t.test('do not add shrinkwrapped deps', t => t.resolveMatchSnapshot(printReified( @@ -508,6 +509,7 @@ t.test('update a node without updating a child that has bundle deps', t => { const path = fixture(t, 'testing-bundledeps-3') return t.resolveMatchSnapshot(printReified(path, { update: ['@isaacs/testing-bundledeps-parent'], + save: false, })) }) @@ -524,9 +526,16 @@ t.test('optional dependency failures', t => { 'optional-metadep-postinstall-fail', 'optional-metadep-allinstall-fail', ] - t.plan(cases.length) - cases.forEach(c => t.test(c, t => - t.resolveMatchSnapshot(printReified(fixture(t, c), { update: true })))) + t.plan(cases.length * 2) + let p = [...cases.map(c => t.test(`${c} save=false`, t => + t.resolveMatchSnapshot(printReified(fixture(t, c), + { update: true, save: false }))))] + + // npm update --save + p = [...cases.map(c => t.test(`${c} save=true`, t => + t.resolveMatchSnapshot(printReified(fixture(t, c), + { update: true, save: true }))))] + return p }) t.test('failure to fetch prod dep is failure', t => @@ -665,6 +674,7 @@ t.test('rollbacks', { buffered: false }, t => { return t.resolveMatchSnapshot(a.reify({ update: ['@isaacs/testing-bundledeps-parent'], + save: false, }).then(printTree)) }) @@ -845,6 +855,7 @@ t.test('rollbacks', { buffered: false }, t => { return t.resolveMatchSnapshot(a.reify({ update: ['@isaacs/testing-bundledeps-parent'], + save: false, }).then(tree => printTree(tree))).then(() => { const warnings = check() t.equal(warnings.length, 2) @@ -1367,7 +1378,7 @@ t.test('save complete lockfile on update-all', async t => { const lock = () => fs.readFileSync(`${path}/package-lock.json`, 'utf8') await reify(path, { add: ['abbrev@1.0.4'] }) t.matchSnapshot(lock(), 'should have abbrev 1.0.4') - await reify(path, { update: true }) + await reify(path, { update: true, save: false }) t.matchSnapshot(lock(), 'should update, but not drop root metadata') }) @@ -2478,7 +2489,7 @@ t.test('save package.json on update', t => { 'package.json': JSON.stringify(pjson), }) - await reify(path, { update: true }) + await reify(path, { update: true, save: false }) t.same(require(resolve(path, 'package.json')), pjson, 'should not have changed package.json file on update all') @@ -2490,7 +2501,7 @@ t.test('save package.json on update', t => { 'package-lock.json': JSON.stringify(plock), }) - await reify(path, { update: true }) + await reify(path, { update: true, save: false }) t.same(require(resolve(path, 'package.json')), pjson, 'should not have changed package.json file on update all') @@ -2511,7 +2522,7 @@ t.test('save package.json on update', t => { 'package.json': JSON.stringify(pjson), }) - await reify(path, { update: { names: ['abbrev'] } }) + await reify(path, { update: { names: ['abbrev'] }, save: false }) t.same(require(resolve(path, 'package.json')), pjson, 'should not have changed package.json file on named update')