Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/npm/cli.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/content/commands/npm-dedupe.md6
-rw-r--r--docs/content/commands/npm-update.md6
-rw-r--r--docs/content/using-npm/config.md9
-rw-r--r--lib/commands/dedupe.js8
-rw-r--r--lib/commands/update.js15
-rw-r--r--lib/utils/config/definitions.js8
-rw-r--r--smoke-tests/index.js53
-rw-r--r--tap-snapshots/test/lib/load-all-commands.js.test.cjs7
-rw-r--r--tap-snapshots/test/lib/utils/config/definitions.js.test.cjs9
-rw-r--r--tap-snapshots/test/lib/utils/config/describe-all.js.test.cjs9
-rw-r--r--tap-snapshots/test/lib/utils/npm-usage.js.test.cjs7
-rw-r--r--test/fixtures/mock-npm.js2
-rw-r--r--test/lib/commands/dedupe.js4
-rw-r--r--test/lib/commands/update.js15
-rw-r--r--workspaces/arborist/lib/arborist/build-ideal-tree.js2
-rw-r--r--workspaces/arborist/lib/arborist/reify.js106
-rw-r--r--workspaces/arborist/tap-snapshots/test/arborist/reify.js.test.cjs686
-rw-r--r--workspaces/arborist/test/arborist/deduper.js2
-rw-r--r--workspaces/arborist/test/arborist/reify.js152
-rw-r--r--workspaces/arborist/test/fixtures/reify-cases/workspaces-need-update.js83
-rw-r--r--workspaces/arborist/test/fixtures/workspaces-need-update/a/package.json6
-rw-r--r--workspaces/arborist/test/fixtures/workspaces-need-update/b/package.json5
-rw-r--r--workspaces/arborist/test/fixtures/workspaces-need-update/package-lock.json53
-rw-r--r--workspaces/arborist/test/fixtures/workspaces-need-update/package.json6
24 files changed, 1178 insertions, 81 deletions
diff --git a/docs/content/commands/npm-dedupe.md b/docs/content/commands/npm-dedupe.md
index 377e17d81..53d2e6427 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
<!-- AUTOGENERATED CONFIG DESCRIPTIONS START -->
diff --git a/docs/content/commands/npm-update.md b/docs/content/commands/npm-update.md
index ad02118e4..1889d6056 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 fe197e344..83a385e08 100644
--- a/docs/content/using-npm/config.md
+++ b/docs/content/using-npm/config.md
@@ -1326,13 +1326,16 @@ 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.
+Save installed packages to a `package.json` file as dependencies.
When used with the `npm rm` command, removes the dependency from
-package.json.
+`package.json`.
+
+Will also prevent writing to `package-lock.json` if set to `false`.
<!-- automatically generated, do not edit manually -->
<!-- see lib/utils/config/definitions.js -->
diff --git a/lib/commands/dedupe.js b/lib/commands/dedupe.js
index cc4b119d0..1c270249a 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 a8bbc4c96..c55d7cf57 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',
@@ -40,19 +41,27 @@ class Update extends ArboristWorkspaceCmd {
? global
: this.npm.prefix
+ // In the context of `npm update` the save
+ // config value should default to `false`
+ const save = this.npm.config.isDefault('save')
+ ? false
+ : this.npm.config.get('save')
+
if (this.npm.config.get('depth')) {
log.warn('update', 'The --depth option no longer has any effect. See RFC0019.\n' +
'https://github.com/npm/rfcs/blob/latest/implemented/0019-remove-update-depth-option.md')
}
- const arb = new Arborist({
+ const opts = {
...this.npm.flatOptions,
log,
path: where,
+ save,
workspaces: this.workspaceNames,
- })
+ }
+ const arb = new Arborist(opts)
- await arb.reify({ update })
+ await arb.reify({ ...opts, update })
await reifyFinish(this.npm, arb)
}
}
diff --git a/lib/utils/config/definitions.js b/lib/utils/config/definitions.js
index ac8a4e2f6..95d79f0f0 100644
--- a/lib/utils/config/definitions.js
+++ b/lib/utils/config/definitions.js
@@ -1583,14 +1583,18 @@ 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',
description: `
- Save installed packages to a package.json file as dependencies.
+ Save installed packages to a \`package.json\` file as dependencies.
When used with the \`npm rm\` command, removes the dependency from
- package.json.
+ \`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 06ca3dee6..83e54ae9f 100644
--- a/smoke-tests/index.js
+++ b/smoke-tests/index.js
@@ -267,3 +267,56 @@ t.test('npm pkg', async t => {
'should have expected npm pkg delete modified package.json result'
)
})
+
+t.test('npm update --no-save --no-package-lock', async t => {
+ // setup, manually reset dep value
+ await exec(`${npmBin} pkg set "dependencies.abbrev==1.0.4"`)
+ await exec(`${npmBin} install`)
+ await exec(`${npmBin} pkg set "dependencies.abbrev=^1.0.4"`)
+
+ const cmd = `${npmBin} update --no-save --no-package-lock`
+ await exec(cmd)
+
+ t.equal(
+ JSON.parse(readFile('package.json')).dependencies.abbrev,
+ '^1.0.4',
+ 'should have expected update --no-save --no-package-lock package.json result'
+ )
+ t.equal(
+ JSON.parse(readFile('package-lock.json')).packages['node_modules/abbrev'].version,
+ '1.0.4',
+ 'should have expected update --no-save --no-package-lock lockfile result'
+ )
+})
+
+t.test('npm update --no-save', async t => {
+ const cmd = `${npmBin} update --no-save`
+ await exec(cmd)
+
+ t.equal(
+ JSON.parse(readFile('package.json')).dependencies.abbrev,
+ '^1.0.4',
+ 'should have expected update --no-save package.json result'
+ )
+ t.equal(
+ JSON.parse(readFile('package-lock.json')).packages['node_modules/abbrev'].version,
+ '1.1.1',
+ 'should have expected update --no-save lockfile result'
+ )
+})
+
+t.test('npm update --save', async t => {
+ const cmd = `${npmBin} update --save`
+ await exec(cmd)
+
+ t.equal(
+ JSON.parse(readFile('package.json')).dependencies.abbrev,
+ '^1.1.1',
+ 'should have expected update --save package.json result'
+ )
+ t.equal(
+ JSON.parse(readFile('package-lock.json')).packages['node_modules/abbrev'].version,
+ '1.1.1',
+ 'should have expected update --save lockfile result'
+ )
+})
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 6efecf208..e7142c249 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 <dev|optional|peer> [--omit <dev|optional|peer> ...]] [--ignore-scripts]
[--no-audit] [--no-bin-links] [--no-fund] [--dry-run]
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
@@ -1061,8 +1062,10 @@ npm update [<pkg>...]
Options:
[-g|--global] [--global-style] [--legacy-bundling] [--strict-peer-deps]
-[--no-package-lock] [--omit <dev|optional|peer> [--omit <dev|optional|peer> ...]]
-[--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 <dev|optional|peer> [--omit <dev|optional|peer> ...]] [--ignore-scripts]
+[--no-audit] [--no-bin-links] [--no-fund] [--dry-run]
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
[-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 84bb22ff0..459c5de8d 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.
+Save installed packages to a \`package.json\` file as dependencies.
When used with the \`npm rm\` command, removes the dependency from
-package.json.
+\`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 3db90f767..ffa661732 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,13 +1200,16 @@ 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.
+Save installed packages to a \`package.json\` file as dependencies.
When used with the \`npm rm\` command, removes the dependency from
-package.json.
+\`package.json\`.
+
+Will also prevent writing to \`package-lock.json\` if set to \`false\`.
<!-- automatically generated, do not edit manually -->
<!-- see lib/utils/config/definitions.js -->
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 0e48cfa61..ab8f1cf3b 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 <dev|optional|peer> [--omit <dev|optional|peer> ...]] [--ignore-scripts]
[--no-audit] [--no-bin-links] [--no-fund] [--dry-run]
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
@@ -1096,8 +1097,10 @@ All commands:
Options:
[-g|--global] [--global-style] [--legacy-bundling] [--strict-peer-deps]
- [--no-package-lock] [--omit <dev|optional|peer> [--omit <dev|optional|peer> ...]]
- [--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 <dev|optional|peer> [--omit <dev|optional|peer> ...]] [--ignore-scripts]
+ [--no-audit] [--no-bin-links] [--no-fund] [--dry-run]
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
[-ws|--workspaces] [--include-workspace-root]
diff --git a/test/fixtures/mock-npm.js b/test/fixtures/mock-npm.js
index 751885531..c3dc47f5d 100644
--- a/test/fixtures/mock-npm.js
+++ b/test/fixtures/mock-npm.js
@@ -147,6 +147,8 @@ class MockNpm {
// for now just set `find` to what config.find should return
// this works cause `find` is not an existing config entry
find: (k) => ({ ...realConfig.defaults, ...config })[k],
+ // for now isDefault is going to just return false if a value was defined
+ isDefault: (k) => !Object.prototype.hasOwnProperty.call(config, k),
get: (k) => ({ ...realConfig.defaults, ...config })[k],
set: (k, v) => config[k] = v,
list: [{ ...realConfig.defaults, ...config }],
diff --git a/test/lib/commands/dedupe.js b/test/lib/commands/dedupe.js
index 2e2fae238..bf6964081 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/test/lib/commands/update.js b/test/lib/commands/update.js
index aecb2c32b..2b464bfab 100644
--- a/test/lib/commands/update.js
+++ b/test/lib/commands/update.js
@@ -27,7 +27,7 @@ t.afterEach(() => {
})
t.test('no args', async t => {
- t.plan(4)
+ t.plan(5)
npm.prefix = '/project/a'
@@ -39,6 +39,7 @@ t.test('no args', async t => {
{
...npm.flatOptions,
path: npm.prefix,
+ save: false,
workspaces: null,
},
'should call arborist contructor with expected args'
@@ -46,7 +47,8 @@ t.test('no args', async t => {
t.match(log, {}, 'log is passed in')
}
- reify ({ update }) {
+ reify ({ save, update }) {
+ t.equal(save, false, 'should default to save=false')
t.equal(update, true, 'should update all deps')
}
}
@@ -64,9 +66,10 @@ t.test('no args', async t => {
})
t.test('with args', async t => {
- t.plan(4)
+ t.plan(5)
npm.prefix = '/project/a'
+ config.save = true
class Arborist {
constructor (args) {
@@ -76,6 +79,7 @@ t.test('with args', async t => {
{
...npm.flatOptions,
path: npm.prefix,
+ save: true,
workspaces: null,
},
'should call arborist contructor with expected args'
@@ -83,7 +87,8 @@ t.test('with args', async t => {
t.match(log, {}, 'log is passed in')
}
- reify ({ update }) {
+ reify ({ save, update }) {
+ t.equal(save, true, 'should pass save if manually set')
t.same(update, ['ipt'], 'should update listed deps')
}
}
@@ -140,7 +145,7 @@ t.test('update --global', async t => {
const { path, log, ...rest } = args
t.same(
rest,
- { ...npm.flatOptions, workspaces: undefined },
+ { ...npm.flatOptions, save: true, workspaces: undefined },
'should call arborist contructor with expected options'
)
diff --git a/workspaces/arborist/lib/arborist/build-ideal-tree.js b/workspaces/arborist/lib/arborist/build-ideal-tree.js
index dffcd546b..ff0cc3480 100644
--- a/workspaces/arborist/lib/arborist/build-ideal-tree.js
+++ b/workspaces/arborist/lib/arborist/build-ideal-tree.js
@@ -41,7 +41,7 @@ const _complete = Symbol('complete')
const _depsSeen = Symbol('depsSeen')
const _depsQueue = Symbol('depsQueue')
const _currentDep = Symbol('currentDep')
-const _updateAll = Symbol('updateAll')
+const _updateAll = Symbol.for('updateAll')
const _mutateTree = Symbol('mutateTree')
const _flagsSuspect = Symbol.for('flagsSuspect')
const _workspaces = Symbol.for('workspaces')
diff --git a/workspaces/arborist/lib/arborist/reify.js b/workspaces/arborist/lib/arborist/reify.js
index 547e54ac3..d5e703238 100644
--- a/workspaces/arborist/lib/arborist/reify.js
+++ b/workspaces/arborist/lib/arborist/reify.js
@@ -58,6 +58,8 @@ const _bundleUnpacked = Symbol('bundleUnpacked')
const _bundleMissing = Symbol('bundleMissing')
const _reifyNode = Symbol.for('reifyNode')
const _extractOrLink = Symbol('extractOrLink')
+const _updateAll = Symbol.for('updateAll')
+const _updateNames = Symbol.for('updateNames')
// defined by rebuild mixin
const _checkBins = Symbol.for('checkBins')
const _symlink = Symbol('symlink')
@@ -1140,21 +1142,33 @@ 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 save = !(options.save === false)
+
+ // we check for updates in order to make sure we run save ideal tree
+ // even though save=false since we want `npm update` to be able to
+ // write to package-lock files by default
+ 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 by default.
+ const saveIdealTree = !(
+ (!save && !hasUpdates)
+ || this[_global]
+ || this[_dryRun]
+ )
+
+ if (!saveIdealTree) {
return false
}
process.emit('time', 'reify:save')
const updatedTrees = new Set()
-
- // 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) {
- for (const { name, tree: addTree } of this[_resolvedAdd]) {
+ const updateNodes = nodes => {
+ for (const { name, tree: addTree } of nodes) {
// addTree either the root, or a workspace
const edge = addTree.edgesOut.get(name)
const pkg = addTree.package
@@ -1168,7 +1182,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
}
@@ -1259,6 +1273,63 @@ module.exports = cls => class Reifier extends cls {
}
}
+ // helper that retrieves an array of nodes that were
+ // potentially updated during the reify process, in order
+ // to limit the number of nodes to check and update, only
+ // select nodes from the inventory that are direct deps
+ // of a given package.json (project root or a workspace)
+ // and in ase of using a list of `names`, restrict nodes
+ // to only names that are found in this list
+ const retrieveUpdatedNodes = names => {
+ const filterDirectDependencies = node =>
+ !node.isRoot && node.resolveParent.isRoot
+ && (!names || names.includes(node.name))
+ const directDeps = this.idealTree.inventory
+ .filter(filterDirectDependencies)
+
+ // traverses the list of direct dependencies and collect all nodes
+ // to be updated, since any of them might have changed during reify
+ const nodes = []
+ for (const node of directDeps) {
+ for (const edgeIn of node.edgesIn) {
+ nodes.push({
+ name: node.name,
+ tree: edgeIn.from.target,
+ })
+ }
+ }
+ return nodes
+ }
+
+ 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)
+ }
+ }
+ }
+
// preserve indentation, if possible
const {
[Symbol.for('indent')]: indent,
@@ -1291,15 +1362,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 7a6a3e714..6d38d724d 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,70 @@ 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-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 {
+ "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-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",
+ },
+ },
+ "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-false",
+ "packageName": "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-postinstall-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 {
@@ -17166,7 +17241,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-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",
},
@@ -17181,14 +17256,14 @@ 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-true",
"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-true",
"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-preinstall-fail save=false > expect resolving Promise 1`] = `
ArboristNode {
"edgesOut": Map {
"@isaacs/testing-fail-preinstall" => EdgeOut {
@@ -17200,14 +17275,33 @@ 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-false",
"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-false",
"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-preinstall-fail save=true > 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-true",
+ "packageName": "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 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",
+ "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-install-fail > expect resolving Promise 1`] = `
+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-save-true",
+ "version": "1.0.0",
+}
+`
+
+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",
+ "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-tgz-missing > expect resolving Promise 1`] = `
+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-save-true",
+ "version": "1.0.0",
+}
+`
+
+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",
}
`
@@ -32468,6 +32676,422 @@ exports[`test/arborist/reify.js TAP save complete lockfile on update-all > shoul
`
+exports[`test/arborist/reify.js TAP save package.json on update should not save any with save=false and package-lock=false > should update lockfile with many deps updated package.json save=false 1`] = `
+{
+ "name": "workspaces-need-update",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "workspaces": [
+ "a",
+ "b"
+ ],
+ "dependencies": {
+ "abbrev": "^1.0.4"
+ }
+ },
+ "a": {
+ "dependencies": {
+ "abbrev": "^1.0.4",
+ "once": "^1.3.2"
+ }
+ },
+ "b": {
+ "dependencies": {
+ "abbrev": "^1.0.4"
+ }
+ },
+ "node_modules/a": {
+ "resolved": "a",
+ "link": true
+ },
+ "node_modules/abbrev": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.4.tgz",
+ "integrity": "sha1-vVWuXkE7oXIu5Mq6H26hBBSlns0="
+ },
+ "node_modules/b": {
+ "resolved": "b",
+ "link": true
+ },
+ "node_modules/once": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz",
+ "integrity": "sha1-2P7sqTsDnsHc3ud0HJK9rF4oCBs=",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ }
+ }
+}
+`
+
+exports[`test/arborist/reify.js TAP save package.json on update should not save many deps in multiple package.json when using save=false > should update lockfile with many deps updated package.json save=false 1`] = `
+{
+ "name": "tap-testdir-reify-save-package.json-on-update-should-not-save-many-deps-in-multiple-package.json-when-using-save-false",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "workspaces": [
+ "a",
+ "b"
+ ],
+ "dependencies": {
+ "abbrev": "^1.0.4"
+ }
+ },
+ "a": {
+ "dependencies": {
+ "abbrev": "^1.0.4",
+ "once": "^1.3.2"
+ }
+ },
+ "b": {
+ "dependencies": {
+ "abbrev": "^1.0.4"
+ }
+ },
+ "node_modules/a": {
+ "resolved": "a",
+ "link": true
+ },
+ "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=="
+ },
+ "node_modules/b": {
+ "resolved": "b",
+ "link": true
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ }
+ },
+ "dependencies": {
+ "a": {
+ "version": "file:a",
+ "requires": {
+ "abbrev": "^1.0.4",
+ "once": "^1.3.2"
+ }
+ },
+ "abbrev": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
+ },
+ "b": {
+ "version": "file:b",
+ "requires": {
+ "abbrev": "^1.0.4"
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ }
+ }
+}
+
+`
+
+exports[`test/arborist/reify.js TAP save package.json on update should save many deps in multiple package.json when using save=true > should update lockfile with many deps updated package.json save=true 1`] = `
+{
+ "name": "tap-testdir-reify-save-package.json-on-update-should-save-many-deps-in-multiple-package.json-when-using-save-true",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "workspaces": [
+ "a",
+ "b"
+ ],
+ "dependencies": {
+ "abbrev": "^1.1.1"
+ }
+ },
+ "a": {
+ "dependencies": {
+ "abbrev": "^1.1.1",
+ "once": "^1.4.0"
+ }
+ },
+ "b": {
+ "dependencies": {
+ "abbrev": "^1.1.1"
+ }
+ },
+ "node_modules/a": {
+ "resolved": "a",
+ "link": true
+ },
+ "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=="
+ },
+ "node_modules/b": {
+ "resolved": "b",
+ "link": true
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ }
+ },
+ "dependencies": {
+ "a": {
+ "version": "file:a",
+ "requires": {
+ "abbrev": "^1.0.4",
+ "once": "^1.3.2"
+ }
+ },
+ "abbrev": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
+ },
+ "b": {
+ "version": "file:b",
+ "requires": {
+ "abbrev": "^1.0.4"
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ }
+ }
+}
+
+`
+
+exports[`test/arborist/reify.js TAP save package.json on update should update named dep across multiple package.json using save=true > should update lockfile with many deps updated package.json save=true 1`] = `
+{
+ "name": "tap-testdir-reify-save-package.json-on-update-should-update-named-dep-across-multiple-package.json-using-save-true",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "workspaces": [
+ "a",
+ "b"
+ ],
+ "dependencies": {
+ "abbrev": "^1.1.1"
+ }
+ },
+ "a": {
+ "dependencies": {
+ "abbrev": "^1.1.1",
+ "once": "^1.3.2"
+ }
+ },
+ "b": {
+ "dependencies": {
+ "abbrev": "^1.1.1"
+ }
+ },
+ "node_modules/a": {
+ "resolved": "a",
+ "link": true
+ },
+ "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=="
+ },
+ "node_modules/b": {
+ "resolved": "b",
+ "link": true
+ },
+ "node_modules/once": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz",
+ "integrity": "sha1-2P7sqTsDnsHc3ud0HJK9rF4oCBs=",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ }
+ },
+ "dependencies": {
+ "a": {
+ "version": "file:a",
+ "requires": {
+ "abbrev": "^1.0.4",
+ "once": "^1.3.2"
+ }
+ },
+ "abbrev": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
+ },
+ "b": {
+ "version": "file:b",
+ "requires": {
+ "abbrev": "^1.0.4"
+ }
+ },
+ "once": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz",
+ "integrity": "sha1-2P7sqTsDnsHc3ud0HJK9rF4oCBs=",
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ }
+ }
+}
+
+`
+
+exports[`test/arborist/reify.js TAP save package.json on update should update single named dep across multiple package.json using save=true > should update lockfile with single dep updated package.json save=true 1`] = `
+{
+ "name": "tap-testdir-reify-save-package.json-on-update-should-update-single-named-dep-across-multiple-package.json-using-save-true",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "workspaces": [
+ "a",
+ "b"
+ ],
+ "dependencies": {
+ "abbrev": "^1.0.4"
+ }
+ },
+ "a": {
+ "dependencies": {
+ "abbrev": "^1.0.4",
+ "once": "^1.4.0"
+ }
+ },
+ "b": {
+ "dependencies": {
+ "abbrev": "^1.0.4"
+ }
+ },
+ "node_modules/a": {
+ "resolved": "a",
+ "link": true
+ },
+ "node_modules/abbrev": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.4.tgz",
+ "integrity": "sha1-vVWuXkE7oXIu5Mq6H26hBBSlns0="
+ },
+ "node_modules/b": {
+ "resolved": "b",
+ "link": true
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ }
+ },
+ "dependencies": {
+ "a": {
+ "version": "file:a",
+ "requires": {
+ "abbrev": "^1.0.4",
+ "once": "^1.3.2"
+ }
+ },
+ "abbrev": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.4.tgz",
+ "integrity": "sha1-vVWuXkE7oXIu5Mq6H26hBBSlns0="
+ },
+ "b": {
+ "version": "file:b",
+ "requires": {
+ "abbrev": "^1.0.4"
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ }
+ }
+}
+
+`
+
exports[`test/arborist/reify.js TAP save proper lockfile with bins when upgrading lockfile complete=false > should upgrade, with bins in place 1`] = `
{
"name": "tap-testdir-reify-save-proper-lockfile-with-bins-when-upgrading-lockfile-complete-false",
diff --git a/workspaces/arborist/test/arborist/deduper.js b/workspaces/arborist/test/arborist/deduper.js
index 3ec37bd62..511ba87bf 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 c8c4cb137..d5fc166a5 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')
})
@@ -2432,3 +2443,134 @@ t.test('add local dep with existing dev + peer/optional', async t => {
t.equal(tree.children.get('abbrev').resolved, 'file:../../dep', 'resolved')
t.equal(tree.children.size, 1, 'children')
})
+
+t.test('save package.json on update', t => {
+ t.test('should save many deps in multiple package.json when using save=true', async t => {
+ const path = fixture(t, 'workspaces-need-update')
+
+ await reify(path, { update: true, save: true })
+
+ t.same(
+ require(resolve(path, 'package.json')),
+ { dependencies: { abbrev: '^1.1.1' }, workspaces: ['a', 'b'] },
+ 'should save top level dep update to root package.json'
+ )
+ t.same(
+ require(resolve(path, 'a', 'package.json')),
+ { dependencies: { abbrev: '^1.1.1', once: '^1.4.0' } },
+ 'should save workspace dep to its package.json file')
+
+ t.matchSnapshot(
+ fs.readFileSync(resolve(path, 'package-lock.json'), 'utf8'),
+ 'should update lockfile with many deps updated package.json save=true'
+ )
+ })
+
+ t.test('should not save many deps in multiple package.json when using save=false', async t => {
+ const path = fixture(t, 'workspaces-need-update')
+
+ await reify(path, { update: true, save: false })
+
+ t.same(
+ require(resolve(path, 'package.json')),
+ {
+ dependencies: { abbrev: '^1.0.4' },
+ workspaces: ['a', 'b'],
+ },
+ 'should not save top level dep update to root package.json'
+ )
+ t.same(
+ require(resolve(path, 'a', 'package.json')),
+ { dependencies: { abbrev: '^1.0.4', once: '^1.3.2' } },
+ 'should not save workspace dep to its package.json file')
+
+ // package-lock entries will still get updated:
+ t.matchSnapshot(
+ fs.readFileSync(resolve(path, 'package-lock.json'), 'utf8'),
+ 'should update lockfile with many deps updated package.json save=false'
+ )
+ })
+
+ t.test('should not save any with save=false and package-lock=false', async t => {
+ const path = fixture(t, 'workspaces-need-update')
+
+ await reify(path, { update: true, save: false, packageLock: false })
+
+ t.same(
+ require(resolve(path, 'package.json')),
+ {
+ dependencies: { abbrev: '^1.0.4' },
+ workspaces: ['a', 'b'],
+ },
+ 'should not save top level dep update to root package.json'
+ )
+ t.same(
+ require(resolve(path, 'a', 'package.json')),
+ { dependencies: { abbrev: '^1.0.4', once: '^1.3.2' } },
+ 'should not save workspace dep to its package.json file')
+
+ // package-lock entries will still get updated:
+ t.matchSnapshot(
+ JSON.stringify(JSON.parse(fs.readFileSync(resolve(path, 'package-lock.json'), 'utf8')), null, 2),
+ 'should update lockfile with many deps updated package.json save=false'
+ )
+ })
+
+ t.test('should update named dep across multiple package.json using save=true', async t => {
+ const path = fixture(t, 'workspaces-need-update')
+
+ await reify(path, { update: ['abbrev'], save: true })
+
+ t.same(
+ require(resolve(path, 'package.json')),
+ {
+ dependencies: { abbrev: '^1.1.1' },
+ workspaces: ['a', 'b'],
+ },
+ 'should save top level dep update to root package.json'
+ )
+ t.same(
+ require(resolve(path, 'a', 'package.json')),
+ { dependencies: { abbrev: '^1.1.1', once: '^1.3.2' } },
+ 'should save only workspace a updated dep to its package.json file')
+ t.same(
+ require(resolve(path, 'b', 'package.json')),
+ { dependencies: { abbrev: '^1.1.1' } },
+ 'should save only workspace b updated dep to its package.json file')
+
+ t.matchSnapshot(
+ fs.readFileSync(resolve(path, 'package-lock.json'), 'utf8'),
+ 'should update lockfile with many deps updated package.json save=true'
+ )
+ })
+
+ t.test('should update single named dep across multiple package.json using save=true', async t => {
+ const path = fixture(t, 'workspaces-need-update')
+
+ await reify(path, { update: ['once'], save: true })
+
+ t.same(
+ require(resolve(path, 'package.json')),
+ {
+ dependencies: { abbrev: '^1.0.4' },
+ workspaces: ['a', 'b'],
+ },
+ 'should save no top level dep update to root package.json'
+ )
+ t.same(
+ require(resolve(path, 'a', 'package.json')),
+ { dependencies: { abbrev: '^1.0.4', once: '^1.4.0' } },
+ 'should save only workspace single updated dep to its package.json file')
+ t.same(
+ require(resolve(path, 'b', 'package.json')),
+ { dependencies: { abbrev: '^1.0.4' } },
+ 'should not change workspace b package.json file')
+
+ t.matchSnapshot(
+ fs.readFileSync(resolve(path, 'package-lock.json'), 'utf8'),
+ 'should update lockfile with single dep updated package.json save=true'
+ )
+ })
+
+ t.end()
+})
diff --git a/workspaces/arborist/test/fixtures/reify-cases/workspaces-need-update.js b/workspaces/arborist/test/fixtures/reify-cases/workspaces-need-update.js
new file mode 100644
index 000000000..2d57f05a0
--- /dev/null
+++ b/workspaces/arborist/test/fixtures/reify-cases/workspaces-need-update.js
@@ -0,0 +1,83 @@
+// generated from test/fixtures/workspaces-need-update
+module.exports = t => {
+ const path = t.testdir({
+ "a": {
+ "package.json": JSON.stringify({
+ "dependencies": {
+ "abbrev": "^1.0.4",
+ "once": "^1.3.2"
+ }
+ })
+ },
+ "b": {
+ "package.json": JSON.stringify({
+ "dependencies": {
+ "abbrev": "^1.0.4"
+ }
+ })
+ },
+ "package-lock.json": JSON.stringify({
+ "name": "workspaces-need-update",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "workspaces": [
+ "a",
+ "b"
+ ],
+ "dependencies": {
+ "abbrev": "^1.0.4"
+ }
+ },
+ "a": {
+ "dependencies": {
+ "abbrev": "^1.0.4",
+ "once": "^1.3.2"
+ }
+ },
+ "b": {
+ "dependencies": {
+ "abbrev": "^1.0.4"
+ }
+ },
+ "node_modules/a": {
+ "resolved": "a",
+ "link": true
+ },
+ "node_modules/abbrev": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.4.tgz",
+ "integrity": "sha1-vVWuXkE7oXIu5Mq6H26hBBSlns0="
+ },
+ "node_modules/b": {
+ "resolved": "b",
+ "link": true
+ },
+ "node_modules/once": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz",
+ "integrity": "sha1-2P7sqTsDnsHc3ud0HJK9rF4oCBs=",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ }
+ }
+ }),
+ "package.json": JSON.stringify({
+ "dependencies": {
+ "abbrev": "^1.0.4"
+ },
+ "workspaces": [
+ "a",
+ "b"
+ ]
+ })
+})
+ return path
+}
diff --git a/workspaces/arborist/test/fixtures/workspaces-need-update/a/package.json b/workspaces/arborist/test/fixtures/workspaces-need-update/a/package.json
new file mode 100644
index 000000000..765847437
--- /dev/null
+++ b/workspaces/arborist/test/fixtures/workspaces-need-update/a/package.json
@@ -0,0 +1,6 @@
+{
+ "dependencies": {
+ "abbrev": "^1.0.4",
+ "once": "^1.3.2"
+ }
+}
diff --git a/workspaces/arborist/test/fixtures/workspaces-need-update/b/package.json b/workspaces/arborist/test/fixtures/workspaces-need-update/b/package.json
new file mode 100644
index 000000000..8af6070f2
--- /dev/null
+++ b/workspaces/arborist/test/fixtures/workspaces-need-update/b/package.json
@@ -0,0 +1,5 @@
+{
+ "dependencies": {
+ "abbrev": "^1.0.4"
+ }
+}
diff --git a/workspaces/arborist/test/fixtures/workspaces-need-update/package-lock.json b/workspaces/arborist/test/fixtures/workspaces-need-update/package-lock.json
new file mode 100644
index 000000000..43865795a
--- /dev/null
+++ b/workspaces/arborist/test/fixtures/workspaces-need-update/package-lock.json
@@ -0,0 +1,53 @@
+{
+ "name": "workspaces-need-update",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "workspaces": [
+ "a",
+ "b"
+ ],
+ "dependencies": {
+ "abbrev": "^1.0.4"
+ }
+ },
+ "a": {
+ "dependencies": {
+ "abbrev": "^1.0.4",
+ "once": "^1.3.2"
+ }
+ },
+ "b": {
+ "dependencies": {
+ "abbrev": "^1.0.4"
+ }
+ },
+ "node_modules/a": {
+ "resolved": "a",
+ "link": true
+ },
+ "node_modules/abbrev": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.4.tgz",
+ "integrity": "sha1-vVWuXkE7oXIu5Mq6H26hBBSlns0="
+ },
+ "node_modules/b": {
+ "resolved": "b",
+ "link": true
+ },
+ "node_modules/once": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz",
+ "integrity": "sha1-2P7sqTsDnsHc3ud0HJK9rF4oCBs=",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ }
+ }
+}
diff --git a/workspaces/arborist/test/fixtures/workspaces-need-update/package.json b/workspaces/arborist/test/fixtures/workspaces-need-update/package.json
new file mode 100644
index 000000000..ebdce120c
--- /dev/null
+++ b/workspaces/arborist/test/fixtures/workspaces-need-update/package.json
@@ -0,0 +1,6 @@
+{
+ "dependencies": {
+ "abbrev": "^1.0.4"
+ },
+ "workspaces": ["a", "b"]
+}