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-dist-tag.md12
-rw-r--r--lib/dist-tag.js56
-rw-r--r--tap-snapshots/test-lib-dist-tag.js-TAP.test.js112
-rw-r--r--tap-snapshots/test-lib-utils-npm-usage.js-TAP.test.js3
-rw-r--r--test/lib/dist-tag.js199
5 files changed, 345 insertions, 37 deletions
diff --git a/docs/content/commands/npm-dist-tag.md b/docs/content/commands/npm-dist-tag.md
index 585da16ad..158c3417e 100644
--- a/docs/content/commands/npm-dist-tag.md
+++ b/docs/content/commands/npm-dist-tag.md
@@ -88,6 +88,18 @@ semver as `>=1.4.0 <1.5.0`. See <https://github.com/npm/npm/issues/6082>.
The simplest way to avoid semver problems with tags is to use tags that do
not begin with a number or the letter `v`.
+### Configuration
+
+#### workspaces
+
+Only supported by `ls`. Enables listing dist-tags of all workspace
+contexts defined in the current `package.json`.
+
+#### workspace
+
+Only supported by `ls`. Enables listing dist-tags of workspace contexts
+limiting results to only those specified by this config item.
+
### See Also
* [npm publish](/commands/npm-publish)
diff --git a/lib/dist-tag.js b/lib/dist-tag.js
index 13ec37fd8..64e8abc01 100644
--- a/lib/dist-tag.js
+++ b/lib/dist-tag.js
@@ -5,6 +5,7 @@ const semver = require('semver')
const otplease = require('./utils/otplease.js')
const readLocalPkgName = require('./utils/read-local-package.js')
+const getWorkspaces = require('./workspaces/get-workspaces.js')
const BaseCommand = require('./base-command.js')
class DistTag extends BaseCommand {
@@ -13,6 +14,11 @@ class DistTag extends BaseCommand {
}
/* istanbul ignore next - see test/lib/load-all-commands.js */
+ static get params () {
+ return ['workspace', 'workspaces']
+ }
+
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
static get name () {
return 'dist-tag'
}
@@ -43,15 +49,14 @@ class DistTag extends BaseCommand {
async distTag ([cmdName, pkg, tag]) {
const opts = this.npm.flatOptions
- const has = (items) => new Set(items).has(cmdName)
- if (has(['add', 'a', 'set', 's']))
+ if (['add', 'a', 'set', 's'].includes(cmdName))
return this.add(pkg, tag, opts)
- if (has(['rm', 'r', 'del', 'd', 'remove']))
+ if (['rm', 'r', 'del', 'd', 'remove'].includes(cmdName))
return this.remove(pkg, tag, opts)
- if (has(['ls', 'l', 'sl', 'list']))
+ if (['ls', 'l', 'sl', 'list'].includes(cmdName))
return this.list(pkg, opts)
if (!pkg) {
@@ -62,6 +67,33 @@ class DistTag extends BaseCommand {
throw this.usage
}
+ execWorkspaces (args, filters, cb) {
+ this.distTagWorkspaces(args, filters).then(() => cb()).catch(cb)
+ }
+
+ async distTagWorkspaces ([cmdName, pkg, tag], filters) {
+ // cmdName is some form of list
+ // pkg is one of:
+ // - unset
+ // - .
+ // - .@version
+ if (['ls', 'l', 'sl', 'list'].includes(cmdName) && (!pkg || pkg === '.' || /^\.@/.test(pkg)))
+ return this.listWorkspaces(filters)
+
+ // pkg is unset
+ // cmdName is one of:
+ // - unset
+ // - .
+ // - .@version
+ if (!pkg && (!cmdName || cmdName === '.' || /^\.@/.test(cmdName)))
+ return this.listWorkspaces(filters)
+
+ // anything else is just a regular dist-tag command
+ // so we fallback to the non-workspaces implementation
+ log.warn('Ignoring workspaces for specified package')
+ return this.distTag([cmdName, pkg, tag])
+ }
+
async add (spec, tag, opts) {
spec = npa(spec || '')
const version = spec.rawSpec
@@ -145,6 +177,22 @@ class DistTag extends BaseCommand {
}
}
+ async listWorkspaces (filters) {
+ const workspaces =
+ await getWorkspaces(filters, { path: this.npm.localPrefix })
+
+ for (const [name] of workspaces) {
+ try {
+ this.npm.output(`${name}:`)
+ await this.list(npa(name), this.npm.flatOptions)
+ } catch (err) {
+ // set the exitCode directly, but ignore the error
+ // since it will have already been logged by this.list()
+ process.exitCode = 1
+ }
+ }
+ }
+
async fetchTags (spec, opts) {
const data = await regFetch.json(
`/-/package/${spec.escapedName}/dist-tags`,
diff --git a/tap-snapshots/test-lib-dist-tag.js-TAP.test.js b/tap-snapshots/test-lib-dist-tag.js-TAP.test.js
index 06936795b..ea25b568b 100644
--- a/tap-snapshots/test-lib-dist-tag.js-TAP.test.js
+++ b/tap-snapshots/test-lib-dist-tag.js-TAP.test.js
@@ -15,6 +15,9 @@ npm dist-tag add <pkg>@<version> [<tag>]
npm dist-tag rm <pkg> <tag>
npm dist-tag ls [<pkg>]
+Options:
+[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]] [-ws|--workspaces]
+
alias: dist-tags
Run "npm help dist-tag" for more info
@@ -30,6 +33,9 @@ npm dist-tag add <pkg>@<version> [<tag>]
npm dist-tag rm <pkg> <tag>
npm dist-tag ls [<pkg>]
+Options:
+[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]] [-ws|--workspaces]
+
alias: dist-tags
Run "npm help dist-tag" for more info
@@ -54,6 +60,9 @@ npm dist-tag add <pkg>@<version> [<tag>]
npm dist-tag rm <pkg> <tag>
npm dist-tag ls [<pkg>]
+Options:
+[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]] [-ws|--workspaces]
+
alias: dist-tags
Run "npm help dist-tag" for more info
@@ -75,6 +84,9 @@ npm dist-tag add <pkg>@<version> [<tag>]
npm dist-tag rm <pkg> <tag>
npm dist-tag ls [<pkg>]
+Options:
+[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]] [-ws|--workspaces]
+
alias: dist-tags
Run "npm help dist-tag" for more info
@@ -126,6 +138,9 @@ npm dist-tag add <pkg>@<version> [<tag>]
npm dist-tag rm <pkg> <tag>
npm dist-tag ls [<pkg>]
+Options:
+[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]] [-ws|--workspaces]
+
alias: dist-tags
Run "npm help dist-tag" for more info
@@ -142,3 +157,100 @@ dist-tag add b to @scoped/another@0.6.0
dist-tag add b is already set to version 0.6.0
`
+
+exports[`test/lib/dist-tag.js TAP workspaces no args > printed the expected output 1`] = `
+workspace-a:
+latest-a: 1.0.0
+latest: 1.0.0
+workspace-b:
+latest-b: 2.0.0
+latest: 2.0.0
+workspace-c:
+latest-c: 3.0.0
+latest: 3.0.0
+`
+
+exports[`test/lib/dist-tag.js TAP workspaces no args, one failing workspace sets exitCode to 1 > printed the expected output 1`] = `
+workspace-a:
+latest-a: 1.0.0
+latest: 1.0.0
+workspace-b:
+latest-b: 2.0.0
+latest: 2.0.0
+workspace-c:
+latest-c: 3.0.0
+latest: 3.0.0
+workspace-d:
+`
+
+exports[`test/lib/dist-tag.js TAP workspaces no args, one workspace > printed the expected output 1`] = `
+workspace-a:
+latest-a: 1.0.0
+latest: 1.0.0
+`
+
+exports[`test/lib/dist-tag.js TAP workspaces one arg -- . > printed the expected output 1`] = `
+workspace-a:
+latest-a: 1.0.0
+latest: 1.0.0
+workspace-b:
+latest-b: 2.0.0
+latest: 2.0.0
+workspace-c:
+latest-c: 3.0.0
+latest: 3.0.0
+`
+
+exports[`test/lib/dist-tag.js TAP workspaces one arg -- .@1, ignores version spec > printed the expected output 1`] = `
+workspace-a:
+latest-a: 1.0.0
+latest: 1.0.0
+workspace-b:
+latest-b: 2.0.0
+latest: 2.0.0
+workspace-c:
+latest-c: 3.0.0
+latest: 3.0.0
+`
+
+exports[`test/lib/dist-tag.js TAP workspaces one arg -- list > printed the expected output 1`] = `
+workspace-a:
+latest-a: 1.0.0
+latest: 1.0.0
+workspace-b:
+latest-b: 2.0.0
+latest: 2.0.0
+workspace-c:
+latest-c: 3.0.0
+latest: 3.0.0
+`
+
+exports[`test/lib/dist-tag.js TAP workspaces two args -- list, . > printed the expected output 1`] = `
+workspace-a:
+latest-a: 1.0.0
+latest: 1.0.0
+workspace-b:
+latest-b: 2.0.0
+latest: 2.0.0
+workspace-c:
+latest-c: 3.0.0
+latest: 3.0.0
+`
+
+exports[`test/lib/dist-tag.js TAP workspaces two args -- list, .@1, ignores version spec > printed the expected output 1`] = `
+workspace-a:
+latest-a: 1.0.0
+latest: 1.0.0
+workspace-b:
+latest-b: 2.0.0
+latest: 2.0.0
+workspace-c:
+latest-c: 3.0.0
+latest: 3.0.0
+`
+
+exports[`test/lib/dist-tag.js TAP workspaces two args -- list, @scoped/pkg, logs a warning and ignores workspaces > printed the expected output 1`] = `
+a: 0.0.1
+b: 0.5.0
+latest: 1.0.0
+`
diff --git a/tap-snapshots/test-lib-utils-npm-usage.js-TAP.test.js b/tap-snapshots/test-lib-utils-npm-usage.js-TAP.test.js
index 45863cdf4..19beaaa85 100644
--- a/tap-snapshots/test-lib-utils-npm-usage.js-TAP.test.js
+++ b/tap-snapshots/test-lib-utils-npm-usage.js-TAP.test.js
@@ -323,6 +323,9 @@ All commands:
npm dist-tag rm <pkg> <tag>
npm dist-tag ls [<pkg>]
+ Options:
+ [-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]] [-ws|--workspaces]
+
alias: dist-tags
Run "npm help dist-tag" for more info
diff --git a/test/lib/dist-tag.js b/test/lib/dist-tag.js
index 9415dacbe..5e54c8f99 100644
--- a/test/lib/dist-tag.js
+++ b/test/lib/dist-tag.js
@@ -1,10 +1,16 @@
const requireInject = require('require-inject')
const mockNpm = require('../fixtures/mock-npm')
-const { test } = require('tap')
+const { afterEach, test } = require('tap')
let result = ''
let log = ''
+afterEach((cb) => {
+ result = ''
+ log = ''
+ cb()
+})
+
const routeMap = {
'/-/package/@scoped%2fpkg/dist-tags': {
latest: '1.0.0',
@@ -22,6 +28,18 @@ const routeMap = {
b: '0.6.0',
c: '7.7.7',
},
+ '/-/package/workspace-a/dist-tags': {
+ latest: '1.0.0',
+ 'latest-a': '1.0.0',
+ },
+ '/-/package/workspace-b/dist-tags': {
+ latest: '2.0.0',
+ 'latest-b': '2.0.0',
+ },
+ '/-/package/workspace-c/dist-tags': {
+ latest: '3.0.0',
+ 'latest-c': '3.0.0',
+ },
}
let npmRegistryFetchMock = (url, opts) => {
@@ -57,7 +75,7 @@ const npm = mockNpm({
global: false,
},
output: msg => {
- result = msg
+ result = result ? [result, msg].join('\n') : msg
},
})
const distTag = new DistTag(npm)
@@ -74,8 +92,6 @@ test('ls in current package', (t) => {
result,
'should list available tags for current package'
)
- result = ''
- log = ''
t.end()
})
})
@@ -92,8 +108,6 @@ test('no args in current package', (t) => {
result,
'should default to listing available tags for current package'
)
- result = ''
- log = ''
t.end()
})
})
@@ -102,8 +116,6 @@ test('borked cmd usage', (t) => {
npm.prefix = t.testdir({})
distTag.exec(['borked', '@scoped/pkg'], (err) => {
t.matchSnapshot(err, 'should show usage error')
- result = ''
- log = ''
t.end()
})
})
@@ -116,8 +128,6 @@ test('ls on named package', (t) => {
result,
'should list tags for the specified package'
)
- result = ''
- log = ''
t.end()
})
})
@@ -133,8 +143,6 @@ test('ls on missing package', (t) => {
err,
'should throw error message'
)
- result = ''
- log = ''
t.end()
})
})
@@ -150,8 +158,6 @@ test('ls on missing name in current package', (t) => {
err,
'should throw usage error message'
)
- result = ''
- log = ''
t.end()
})
})
@@ -164,14 +170,154 @@ test('only named package arg', (t) => {
result,
'should default to listing tags for the specified package'
)
- result = ''
- log = ''
t.end()
})
})
+test('workspaces', (t) => {
+ npm.localPrefix = t.testdir({
+ 'package.json': JSON.stringify({
+ name: 'root',
+ version: '1.0.0',
+ workspaces: ['workspace-a', 'workspace-b', 'workspace-c'],
+ }),
+ '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',
+ }),
+ },
+ 'workspace-c': {
+ 'package.json': JSON.stringify({
+ name: 'workspace-c',
+ version: '1.0.0',
+ }),
+ },
+ })
+
+ t.test('no args', t => {
+ distTag.execWorkspaces([], [], (err) => {
+ t.ifError(err)
+ t.matchSnapshot(result, 'printed the expected output')
+ t.end()
+ })
+ })
+
+ t.test('no args, one workspace', t => {
+ distTag.execWorkspaces([], ['workspace-a'], (err) => {
+ t.ifError(err)
+ t.matchSnapshot(result, 'printed the expected output')
+ t.end()
+ })
+ })
+
+ t.test('one arg -- .', t => {
+ distTag.execWorkspaces(['.'], [], (err) => {
+ t.ifError(err)
+ t.matchSnapshot(result, 'printed the expected output')
+ t.end()
+ })
+ })
+
+ t.test('one arg -- .@1, ignores version spec', t => {
+ distTag.execWorkspaces(['.@'], [], (err) => {
+ t.ifError(err)
+ t.matchSnapshot(result, 'printed the expected output')
+ t.end()
+ })
+ })
+
+ t.test('one arg -- list', t => {
+ distTag.execWorkspaces(['list'], [], (err) => {
+ t.ifError(err)
+ t.matchSnapshot(result, 'printed the expected output')
+ t.end()
+ })
+ })
+
+ t.test('two args -- list, .', t => {
+ distTag.execWorkspaces(['list', '.'], [], (err) => {
+ t.ifError(err)
+ t.matchSnapshot(result, 'printed the expected output')
+ t.end()
+ })
+ })
+
+ t.test('two args -- list, .@1, ignores version spec', t => {
+ distTag.execWorkspaces(['list', '.@'], [], (err) => {
+ t.ifError(err)
+ t.matchSnapshot(result, 'printed the expected output')
+ t.end()
+ })
+ })
+
+ t.test('two args -- list, @scoped/pkg, logs a warning and ignores workspaces', t => {
+ distTag.execWorkspaces(['list', '@scoped/pkg'], [], (err) => {
+ t.ifError(err)
+ t.match(log, 'Ignoring workspaces for specified package', 'logs a warning')
+ t.matchSnapshot(result, 'printed the expected output')
+ t.end()
+ })
+ })
+
+ t.test('no args, one failing workspace sets exitCode to 1', t => {
+ npm.localPrefix = t.testdir({
+ 'package.json': JSON.stringify({
+ name: 'root',
+ version: '1.0.0',
+ workspaces: ['workspace-a', 'workspace-b', 'workspace-c', 'workspace-d'],
+ }),
+ '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',
+ }),
+ },
+ 'workspace-c': {
+ 'package.json': JSON.stringify({
+ name: 'workspace-c',
+ version: '1.0.0',
+ }),
+ },
+ 'workspace-d': {
+ 'package.json': JSON.stringify({
+ name: 'workspace-d',
+ version: '1.0.0',
+ }),
+ },
+ })
+
+ distTag.execWorkspaces([], [], (err) => {
+ t.ifError(err)
+ t.equal(process.exitCode, 1, 'set the error status')
+ process.exitCode = 0
+ t.match(log, 'dist-tag ls Couldn\'t get dist-tag data for workspace-d@latest', 'logs the error')
+ t.matchSnapshot(result, 'printed the expected output')
+ t.end()
+ })
+ })
+
+ t.end()
+})
+
test('add new tag', (t) => {
const _nrf = npmRegistryFetchMock
+ t.teardown(() => {
+ npmRegistryFetchMock = _nrf
+ })
+
npmRegistryFetchMock = async (url, opts) => {
t.equal(opts.method, 'PUT', 'should trigger request to add new tag')
t.equal(opts.body, '7.7.7', 'should point to expected version')
@@ -183,9 +329,6 @@ test('add new tag', (t) => {
result,
'should return success msg'
)
- result = ''
- log = ''
- npmRegistryFetchMock = _nrf
t.end()
})
})
@@ -202,8 +345,6 @@ test('add using valid semver range as name', (t) => {
log,
'should return success msg'
)
- result = ''
- log = ''
t.end()
})
})
@@ -212,8 +353,6 @@ test('add missing args', (t) => {
npm.prefix = t.testdir({})
distTag.exec(['add', '@scoped/another@7.7.7'], (err) => {
t.matchSnapshot(err, 'should exit usage error message')
- result = ''
- log = ''
t.end()
})
})
@@ -222,8 +361,6 @@ test('add missing pkg name', (t) => {
npm.prefix = t.testdir({})
distTag.exec(['add', null], (err) => {
t.matchSnapshot(err, 'should exit usage error message')
- result = ''
- log = ''
t.end()
})
})
@@ -236,13 +373,16 @@ test('set existing version', (t) => {
log,
'should log warn msg'
)
- log = ''
t.end()
})
})
test('remove existing tag', (t) => {
const _nrf = npmRegistryFetchMock
+ t.teardown(() => {
+ npmRegistryFetchMock = _nrf
+ })
+
npmRegistryFetchMock = async (url, opts) => {
t.equal(opts.method, 'DELETE', 'should trigger request to remove tag')
}
@@ -251,9 +391,6 @@ test('remove existing tag', (t) => {
t.ifError(err, 'npm dist-tags rm')
t.matchSnapshot(log, 'should log remove info')
t.matchSnapshot(result, 'should return success msg')
- result = ''
- log = ''
- npmRegistryFetchMock = _nrf
t.end()
})
})
@@ -267,8 +404,6 @@ test('remove non-existing tag', (t) => {
'should exit with error'
)
t.matchSnapshot(log, 'should log error msg')
- result = ''
- log = ''
t.end()
})
})
@@ -277,8 +412,6 @@ test('remove missing pkg name', (t) => {
npm.prefix = t.testdir({})
distTag.exec(['rm', null], (err) => {
t.matchSnapshot(err, 'should exit usage error message')
- result = ''
- log = ''
t.end()
})
})