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:
authorRuy Adorno <ruyadorno@hotmail.com>2022-03-24 18:46:54 +0300
committerGitHub <noreply@github.com>2022-03-24 18:46:54 +0300
commit723a0918a5a9d9f795584f85d04506fafda9ca42 (patch)
tree37f0f659d7b81abd9b6cb6d2038ce83571e2c2b4
parent362831c4eba2554b44feec60fdff197d92eac0c1 (diff)
feat(version): reify on workspace version change (#4588)
Adds a minimalistic reify step that updates the installed tree after a version change within one of the configured workspaces when using any of the workspaces config options. It's also possible to use the `--save` config option in order to auto update semver ranges of dependencies declarations accross dependent `package.json` files. Fixes: https://github.com/npm/cli/issues/3403 Relates to: https://github.com/npm/rfcs/issues/556 Relates to: https://github.com/npm/cli/issues/3757 Relates to: https://github.com/npm/cli/issues/4193
-rw-r--r--docs/content/commands/npm-version.md11
-rw-r--r--docs/content/using-npm/config.md11
-rw-r--r--lib/commands/version.js35
-rw-r--r--lib/utils/config/definitions.js10
-rw-r--r--tap-snapshots/test/lib/commands/config.js.test.cjs2
-rw-r--r--tap-snapshots/test/lib/commands/version.js.test.cjs94
-rw-r--r--tap-snapshots/test/lib/load-all-commands.js.test.cjs2
-rw-r--r--tap-snapshots/test/lib/utils/config/definitions.js.test.cjs11
-rw-r--r--tap-snapshots/test/lib/utils/config/describe-all.js.test.cjs11
-rw-r--r--tap-snapshots/test/lib/utils/npm-usage.js.test.cjs2
-rw-r--r--test/lib/commands/version.js117
11 files changed, 297 insertions, 9 deletions
diff --git a/docs/content/commands/npm-version.md b/docs/content/commands/npm-version.md
index 86e2ce90e..713e5ae41 100644
--- a/docs/content/commands/npm-version.md
+++ b/docs/content/commands/npm-version.md
@@ -144,6 +144,17 @@ This value is not exported to the environment for child processes.
<!-- automatically generated, do not edit manually -->
<!-- see lib/utils/config/definitions.js -->
+#### `workspaces-update`
+
+* Default: true
+* Type: Boolean
+
+If set to true, the npm cli will run an update after operations that may
+possibly change the workspaces installed to the `node_modules` folder.
+
+<!-- automatically generated, do not edit manually -->
+<!-- see lib/utils/config/definitions.js -->
+
#### `include-workspace-root`
* Default: false
diff --git a/docs/content/using-npm/config.md b/docs/content/using-npm/config.md
index df259715f..76e5e35e6 100644
--- a/docs/content/using-npm/config.md
+++ b/docs/content/using-npm/config.md
@@ -1823,6 +1823,17 @@ This value is not exported to the environment for child processes.
<!-- automatically generated, do not edit manually -->
<!-- see lib/utils/config/definitions.js -->
+#### `workspaces-update`
+
+* Default: true
+* Type: Boolean
+
+If set to true, the npm cli will run an update after operations that may
+possibly change the workspaces installed to the `node_modules` folder.
+
+<!-- automatically generated, do not edit manually -->
+<!-- see lib/utils/config/definitions.js -->
+
#### `yes`
* Default: null
diff --git a/lib/commands/version.js b/lib/commands/version.js
index f49a309a7..ed506f663 100644
--- a/lib/commands/version.js
+++ b/lib/commands/version.js
@@ -3,6 +3,9 @@ const { resolve } = require('path')
const { promisify } = require('util')
const readFile = promisify(require('fs').readFile)
+const Arborist = require('@npmcli/arborist')
+const reifyFinish = require('../utils/reify-finish.js')
+
const BaseCommand = require('../base-command.js')
class Version extends BaseCommand {
@@ -17,6 +20,7 @@ class Version extends BaseCommand {
'sign-git-tag',
'workspace',
'workspaces',
+ 'workspaces-update',
'include-workspace-root',
]
@@ -81,6 +85,7 @@ class Version extends BaseCommand {
async changeWorkspaces (args, filters) {
const prefix = this.npm.config.get('tag-version-prefix')
await this.setWorkspaces(filters)
+ const updatedWorkspaces = []
for (const [name, path] of this.workspaces) {
this.npm.output(name)
const version = await libnpmversion(args[0], {
@@ -88,8 +93,10 @@ class Version extends BaseCommand {
'git-tag-version': false,
path,
})
+ updatedWorkspaces.push(name)
this.npm.output(`${prefix}${version}`)
}
+ return this.update(updatedWorkspaces)
}
async list (results = {}) {
@@ -129,6 +136,34 @@ class Version extends BaseCommand {
}
return this.list(results)
}
+
+ async update (args) {
+ if (!this.npm.flatOptions.workspacesUpdate || !args.length) {
+ return
+ }
+
+ // default behavior is to not save by default in order to avoid
+ // race condition problems when publishing multiple workspaces
+ // that have dependencies on one another, it might still be useful
+ // in some cases, which then need to set --save
+ const save = this.npm.config.isDefault('save')
+ ? false
+ : this.npm.config.get('save')
+
+ // runs a minimalistic reify update, targetting only the workspaces
+ // that had version updates and skipping fund/audit/save
+ const opts = {
+ ...this.npm.flatOptions,
+ audit: false,
+ fund: false,
+ path: this.npm.localPrefix,
+ save,
+ }
+ const arb = new Arborist(opts)
+
+ await arb.reify({ ...opts, update: args })
+ await reifyFinish(this.npm, arb)
+ }
}
module.exports = Version
diff --git a/lib/utils/config/definitions.js b/lib/utils/config/definitions.js
index ddccb1475..abc989d0e 100644
--- a/lib/utils/config/definitions.js
+++ b/lib/utils/config/definitions.js
@@ -2270,6 +2270,16 @@ define('workspaces', {
},
})
+define('workspaces-update', {
+ default: true,
+ type: Boolean,
+ description: `
+ If set to true, the npm cli will run an update after operations that may
+ possibly change the workspaces installed to the \`node_modules\` folder.
+ `,
+ flatten,
+})
+
define('yes', {
default: null,
type: [null, Boolean],
diff --git a/tap-snapshots/test/lib/commands/config.js.test.cjs b/tap-snapshots/test/lib/commands/config.js.test.cjs
index 8e9791523..0806c68ca 100644
--- a/tap-snapshots/test/lib/commands/config.js.test.cjs
+++ b/tap-snapshots/test/lib/commands/config.js.test.cjs
@@ -155,6 +155,7 @@ exports[`test/lib/commands/config.js TAP config list --json > output matches sna
"which": null,
"workspace": [],
"workspaces": null,
+ "workspaces-update": true,
"yes": null,
"metrics-registry": "https://registry.npmjs.org/"
}
@@ -308,6 +309,7 @@ viewer = "{VIEWER}"
which = null
workspace = []
workspaces = null
+workspaces-update = true
yes = null
; "global" config from {GLOBALPREFIX}/npmrc
diff --git a/tap-snapshots/test/lib/commands/version.js.test.cjs b/tap-snapshots/test/lib/commands/version.js.test.cjs
new file mode 100644
index 000000000..e19f9b8ee
--- /dev/null
+++ b/tap-snapshots/test/lib/commands/version.js.test.cjs
@@ -0,0 +1,94 @@
+/* IMPORTANT
+ * This snapshot file is auto-generated, but designed for humans.
+ * It should be checked into source control and tracked carefully.
+ * Re-generate by setting TAP_SNAPSHOT=1 and running tests.
+ * Make sure to inspect the output below. Do not ignore changes!
+ */
+'use strict'
+exports[`test/lib/commands/version.js TAP empty versions workspaces with one arg, all workspaces > must match snapshot 1`] = `
+{
+ "name": "workspaces-test",
+ "version": "1.0.0",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "workspaces-test",
+ "version": "1.0.0",
+ "workspaces": [
+ "workspace-a",
+ "workspace-b"
+ ]
+ },
+ "node_modules/workspace-a": {
+ "resolved": "workspace-a",
+ "link": true
+ },
+ "node_modules/workspace-b": {
+ "resolved": "workspace-b",
+ "link": true
+ },
+ "workspace-a": {
+ "version": "2.0.0"
+ },
+ "workspace-b": {
+ "version": "2.0.0"
+ }
+ },
+ "dependencies": {
+ "workspace-a": {
+ "version": "file:workspace-a"
+ },
+ "workspace-b": {
+ "version": "file:workspace-b"
+ }
+ }
+}
+
+`
+
+exports[`test/lib/commands/version.js TAP empty versions workspaces with one arg, all workspaces, saves package.json > must match snapshot 1`] = `
+{
+ "name": "workspaces-test",
+ "version": "1.0.0",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "workspaces-test",
+ "version": "1.0.0",
+ "workspaces": [
+ "workspace-a",
+ "workspace-b"
+ ],
+ "dependencies": {
+ "workspace-a": "^2.0.0",
+ "workspace-b": "^2.0.0"
+ }
+ },
+ "node_modules/workspace-a": {
+ "resolved": "workspace-a",
+ "link": true
+ },
+ "node_modules/workspace-b": {
+ "resolved": "workspace-b",
+ "link": true
+ },
+ "workspace-a": {
+ "version": "2.0.0"
+ },
+ "workspace-b": {
+ "version": "2.0.0"
+ }
+ },
+ "dependencies": {
+ "workspace-a": {
+ "version": "file:workspace-a"
+ },
+ "workspace-b": {
+ "version": "file:workspace-b"
+ }
+ }
+}
+
+`
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 1ad8aee29..9fe2a5491 100644
--- a/tap-snapshots/test/lib/load-all-commands.js.test.cjs
+++ b/tap-snapshots/test/lib/load-all-commands.js.test.cjs
@@ -1092,7 +1092,7 @@ Options:
[--allow-same-version] [--no-commit-hooks] [--no-git-tag-version] [--json]
[--preid prerelease-id] [--sign-git-tag]
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
-[-ws|--workspaces] [--include-workspace-root]
+[-ws|--workspaces] [--no-workspaces-update] [--include-workspace-root]
alias: verison
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 373f094a5..4d3a6f150 100644
--- a/tap-snapshots/test/lib/utils/config/definitions.js.test.cjs
+++ b/tap-snapshots/test/lib/utils/config/definitions.js.test.cjs
@@ -151,6 +151,7 @@ Array [
"which",
"workspace",
"workspaces",
+ "workspaces-update",
"yes",
]
`
@@ -1915,6 +1916,16 @@ _unless_ one or more workspaces are specified in the \`workspace\` config.
This value is not exported to the environment for child processes.
`
+exports[`test/lib/utils/config/definitions.js TAP > config description for workspaces-update 1`] = `
+#### \`workspaces-update\`
+
+* Default: true
+* Type: Boolean
+
+If set to true, the npm cli will run an update after operations that may
+possibly change the workspaces installed to the \`node_modules\` folder.
+`
+
exports[`test/lib/utils/config/definitions.js TAP > config description for yes 1`] = `
#### \`yes\`
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 3a7d90db0..94ddbe2b1 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
@@ -1697,6 +1697,17 @@ This value is not exported to the environment for child processes.
<!-- automatically generated, do not edit manually -->
<!-- see lib/utils/config/definitions.js -->
+#### \`workspaces-update\`
+
+* Default: true
+* Type: Boolean
+
+If set to true, the npm cli will run an update after operations that may
+possibly change the workspaces installed to the \`node_modules\` folder.
+
+<!-- automatically generated, do not edit manually -->
+<!-- see lib/utils/config/definitions.js -->
+
#### \`yes\`
* Default: null
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 8f73b93f3..181e47da7 100644
--- a/tap-snapshots/test/lib/utils/npm-usage.js.test.cjs
+++ b/tap-snapshots/test/lib/utils/npm-usage.js.test.cjs
@@ -1125,7 +1125,7 @@ All commands:
[--allow-same-version] [--no-commit-hooks] [--no-git-tag-version] [--json]
[--preid prerelease-id] [--sign-git-tag]
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
- [-ws|--workspaces] [--include-workspace-root]
+ [-ws|--workspaces] [--no-workspaces-update] [--include-workspace-root]
alias: verison
diff --git a/test/lib/commands/version.js b/test/lib/commands/version.js
index 980353897..154f6a6f8 100644
--- a/test/lib/commands/version.js
+++ b/test/lib/commands/version.js
@@ -1,3 +1,5 @@
+const { readFileSync, statSync } = require('fs')
+const { resolve } = require('path')
const t = require('tap')
const { fake: mockNpm } = require('../../fixtures/mock-npm')
const mockGlobals = require('../../fixtures/mock-globals.js')
@@ -10,8 +12,13 @@ const config = {
'tag-version-prefix': 'v',
json: false,
}
+const flatOptions = {
+ workspacesUpdate: true,
+}
const npm = mockNpm({
config,
+ flatOptions,
+ localPrefix: '',
prefix: '',
version: '1.0.0',
output: (...msg) => {
@@ -21,14 +28,16 @@ const npm = mockNpm({
},
})
const mocks = {
- libnpmversion: noop,
+ '../../../lib/utils/reify-finish.js': noop,
}
const Version = t.mock('../../../lib/commands/version.js', mocks)
const version = new Version(npm)
t.afterEach(() => {
+ flatOptions.workspacesUpdate = true
config.json = false
+ npm.localPrefix = ''
npm.prefix = ''
result = []
})
@@ -120,7 +129,7 @@ t.test('empty versions', t => {
...mocks,
libnpmversion: (arg, opts) => {
t.equal(arg, 'major', 'should forward expected value')
- t.same(
+ t.match(
opts,
{
path: '',
@@ -271,7 +280,6 @@ t.test('empty versions', t => {
})
t.test('with one arg, all workspaces', async t => {
- const libNpmVersionArgs = []
const testDir = t.testdir({
'package.json': JSON.stringify(
{
@@ -296,12 +304,54 @@ t.test('empty versions', t => {
},
})
const Version = t.mock('../../../lib/commands/version.js', {
- ...mocks,
- libnpmversion: (arg, opts) => {
- libNpmVersionArgs.push([arg, opts])
- return '2.0.0'
+ '../../../lib/utils/reify-finish.js': noop,
+ })
+ npm.localPrefix = testDir
+ npm.prefix = testDir
+ const version = new Version(npm)
+
+ await version.execWorkspaces(['major'], [])
+ t.same(
+ result,
+ ['workspace-a', 'v2.0.0', 'workspace-b', 'v2.0.0'],
+ 'outputs the new version for only the workspaces prefixed by the tagVersionPrefix'
+ )
+
+ t.matchSnapshot(readFileSync(resolve(testDir, 'package-lock.json'), 'utf8'))
+ })
+
+ t.test('with one arg, all workspaces, saves package.json', async t => {
+ const testDir = t.testdir({
+ 'package.json': JSON.stringify(
+ {
+ name: 'workspaces-test',
+ version: '1.0.0',
+ workspaces: ['workspace-a', 'workspace-b'],
+ dependencies: {
+ 'workspace-a': '^1.0.0',
+ 'workspace-b': '^1.0.0',
+ },
+ },
+ null,
+ 2
+ ),
+ 'workspace-a': {
+ 'package.json': JSON.stringify({
+ name: 'workspace-a',
+ version: '1.0.0',
+ }),
},
+ 'workspace-b': {
+ 'package.json': JSON.stringify({
+ name: 'workspace-b',
+ version: '1.0.0',
+ }),
+ },
+ })
+ const Version = t.mock('../../../lib/commands/version.js', {
+ '../../../lib/utils/reify-finish.js': noop,
})
+ config.save = true
npm.localPrefix = testDir
npm.prefix = testDir
const version = new Version(npm)
@@ -312,6 +362,8 @@ t.test('empty versions', t => {
['workspace-a', 'v2.0.0', 'workspace-b', 'v2.0.0'],
'outputs the new version for only the workspaces prefixed by the tagVersionPrefix'
)
+
+ t.matchSnapshot(readFileSync(resolve(testDir, 'package-lock.json'), 'utf8'))
})
t.test('too many args', async t => {
@@ -321,6 +373,57 @@ t.test('empty versions', t => {
'should throw usage instructions error'
)
})
+
+ t.test('no workspaces-update', async t => {
+ flatOptions.workspacesUpdate = false
+
+ const libNpmVersionArgs = []
+ const testDir = t.testdir({
+ 'package.json': JSON.stringify(
+ {
+ name: 'workspaces-test',
+ version: '1.0.0',
+ workspaces: ['workspace-a', 'workspace-b'],
+ },
+ null,
+ 2
+ ),
+ 'workspace-a': {
+ 'package.json': JSON.stringify({
+ name: 'workspace-a',
+ version: '1.0.0',
+ }),
+ },
+ 'workspace-b': {
+ 'package.json': JSON.stringify({
+ name: 'workspace-b',
+ version: '1.0.0',
+ }),
+ },
+ })
+ const Version = t.mock('../../../lib/commands/version.js', {
+ ...mocks,
+ libnpmversion: (arg, opts) => {
+ libNpmVersionArgs.push([arg, opts])
+ return '2.0.0'
+ },
+ })
+ npm.localPrefix = testDir
+ npm.prefix = testDir
+ const version = new Version(npm)
+
+ await version.execWorkspaces(['major'], [])
+ t.same(
+ result,
+ ['workspace-a', 'v2.0.0', 'workspace-b', 'v2.0.0'],
+ 'outputs the new version for only the workspaces prefixed by the tagVersionPrefix'
+ )
+
+ t.throws(
+ () => statSync(resolve(testDir, 'package-lock.json')),
+ 'should not have a lockfile since have not reified'
+ )
+ })
})
t.end()