From ba4f7fea8fca8e3509469a218f094fe69095888b Mon Sep 17 00:00:00 2001 From: Gar Date: Thu, 8 Apr 2021 10:24:31 -0700 Subject: [PATCH 1/5] licensee@8.2.0 --- package-lock.json | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0599dfb6483d8..d09fe6ba13cba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -701,9 +701,9 @@ } }, "node_modules/@blueoak/list": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@blueoak/list/-/list-1.0.2.tgz", - "integrity": "sha512-KyqT0kkdxgbGys9mvo/1Mgdt/LGvUFPCZIK9pWPIfOM2mYzMDd/eVYy4sMP1YqvVI129k0alxRyM53H2MAs/Nw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@blueoak/list/-/list-2.0.0.tgz", + "integrity": "sha512-yQ6/CTy6DYvmJOAIw/BJjKeNG2ZyF8uxgTN8Yvcv4L9YavoVp9xUgmoVUKN5l24NGPDQpswavNanHOqB00ZNXg==", "dev": true }, "node_modules/@eslint/eslintrc": { @@ -4968,12 +4968,12 @@ } }, "node_modules/licensee": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/licensee/-/licensee-8.1.0.tgz", - "integrity": "sha512-rnXUmUuLzZrGfm3bfWNl71Emw/OJqwUyIrIRq5D06Ct9EbiFnZtiydA5ryf4FDPikdneJ0l1Q+g6TuMjpWGfrA==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/licensee/-/licensee-8.2.0.tgz", + "integrity": "sha512-Z5fQ+qP85N38klGijH0bXiWVlKqLKDMqsckKL+VcA+ZQ/DJK5cpIpvryGHtREaQ3Ah5jrgtXN8mHfII7UtlsJg==", "dev": true, "dependencies": { - "@blueoak/list": "^1.0.2", + "@blueoak/list": "^2.0.0", "correct-license-metadata": "^1.0.1", "docopt": "^0.6.2", "fs-access": "^2.0.0", @@ -10980,9 +10980,9 @@ } }, "@blueoak/list": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@blueoak/list/-/list-1.0.2.tgz", - "integrity": "sha512-KyqT0kkdxgbGys9mvo/1Mgdt/LGvUFPCZIK9pWPIfOM2mYzMDd/eVYy4sMP1YqvVI129k0alxRyM53H2MAs/Nw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@blueoak/list/-/list-2.0.0.tgz", + "integrity": "sha512-yQ6/CTy6DYvmJOAIw/BJjKeNG2ZyF8uxgTN8Yvcv4L9YavoVp9xUgmoVUKN5l24NGPDQpswavNanHOqB00ZNXg==", "dev": true }, "@eslint/eslintrc": { @@ -14153,12 +14153,12 @@ } }, "licensee": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/licensee/-/licensee-8.1.0.tgz", - "integrity": "sha512-rnXUmUuLzZrGfm3bfWNl71Emw/OJqwUyIrIRq5D06Ct9EbiFnZtiydA5ryf4FDPikdneJ0l1Q+g6TuMjpWGfrA==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/licensee/-/licensee-8.2.0.tgz", + "integrity": "sha512-Z5fQ+qP85N38klGijH0bXiWVlKqLKDMqsckKL+VcA+ZQ/DJK5cpIpvryGHtREaQ3Ah5jrgtXN8mHfII7UtlsJg==", "dev": true, "requires": { - "@blueoak/list": "^1.0.2", + "@blueoak/list": "^2.0.0", "correct-license-metadata": "^1.0.1", "docopt": "^0.6.2", "fs-access": "^2.0.0", From 1f3e88ebaf4901d8f9f07b43404d824fef7e5ff5 Mon Sep 17 00:00:00 2001 From: nlf Date: Tue, 6 Apr 2021 07:33:52 -0700 Subject: [PATCH 2/5] feat(workspaces): implement workspace support for dist-tag PR-URL: https://github.com/npm/cli/pull/3032 Credit: @nlf Close: #3032 Reviewed-by: @wraithgar --- docs/content/commands/npm-dist-tag.md | 12 ++ lib/dist-tag.js | 56 ++++- .../test-lib-dist-tag.js-TAP.test.js | 112 ++++++++++ .../test-lib-utils-npm-usage.js-TAP.test.js | 3 + test/lib/dist-tag.js | 199 +++++++++++++++--- 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 585da16ad2d2c..158c3417e7cba 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 . 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 13ec37fd8cb1d..64e8abc013745 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 { @@ -12,6 +13,11 @@ class DistTag extends BaseCommand { return 'Modify package distribution tags' } + /* 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 06936795bcf03..ea25b568b0662 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 @ [] npm dist-tag rm npm dist-tag ls [] +Options: +[-w|--workspace [-w|--workspace ...]] [-ws|--workspaces] + alias: dist-tags Run "npm help dist-tag" for more info @@ -30,6 +33,9 @@ npm dist-tag add @ [] npm dist-tag rm npm dist-tag ls [] +Options: +[-w|--workspace [-w|--workspace ...]] [-ws|--workspaces] + alias: dist-tags Run "npm help dist-tag" for more info @@ -54,6 +60,9 @@ npm dist-tag add @ [] npm dist-tag rm npm dist-tag ls [] +Options: +[-w|--workspace [-w|--workspace ...]] [-ws|--workspaces] + alias: dist-tags Run "npm help dist-tag" for more info @@ -75,6 +84,9 @@ npm dist-tag add @ [] npm dist-tag rm npm dist-tag ls [] +Options: +[-w|--workspace [-w|--workspace ...]] [-ws|--workspaces] + alias: dist-tags Run "npm help dist-tag" for more info @@ -126,6 +138,9 @@ npm dist-tag add @ [] npm dist-tag rm npm dist-tag ls [] +Options: +[-w|--workspace [-w|--workspace ...]] [-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 45863cdf4e02f..19beaaa85ea48 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 npm dist-tag ls [] + Options: + [-w|--workspace [-w|--workspace ...]] [-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 9415dacbe4756..5e54c8f991cfd 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() }) }) From 6e31df4e7957337962fd3d93e495931e3592bb9e Mon Sep 17 00:00:00 2001 From: Gar Date: Tue, 6 Apr 2021 08:28:36 -0700 Subject: [PATCH 3/5] feat(pack): add workspace support PR-URL: https://github.com/npm/cli/pull/3033 Credit: @wraithgar Close: #3033 Reviewed-by: @darcyclarke --- docs/content/commands/npm-pack.md | 23 +++- lib/pack.js | 23 +++- lib/view.js | 2 +- .../test-lib-utils-npm-usage.js-TAP.test.js | 2 +- tap-snapshots/test-lib-view.js-TAP.test.js | 2 +- test/fixtures/mock-npm.js | 14 +++ test/lib/pack.js | 103 ++++++++++++++++++ 7 files changed, 161 insertions(+), 8 deletions(-) diff --git a/docs/content/commands/npm-pack.md b/docs/content/commands/npm-pack.md index cc6b669efb1ef..de3eeb731a9e3 100644 --- a/docs/content/commands/npm-pack.md +++ b/docs/content/commands/npm-pack.md @@ -10,6 +10,25 @@ description: Create a tarball from a package npm pack [[<@scope>/]...] [--dry-run] ``` +### Configuration + +#### dry-run + +Do everything that pack usually does without actually packing anything. +That is, report on what would have gone into the tarball, but nothing +else. + +#### workspaces + +Enables workspaces context while creating tarballs. Tarballs for each +workspaces will be generated. + +#### workspace + +Enables workspaces context and limits results to only those specified by +this config item. Tarballs will only be generated for the packages +named in the workspaces given here. + ### Description For anything that's installable (that is, a package folder, tarball, @@ -23,10 +42,6 @@ overwritten the second time. If no arguments are supplied, then npm packs the current package folder. -The `--dry-run` argument will do everything that pack usually does without -actually packing anything. That is, it reports on what would have gone -into the tarball, but nothing else. - ### See Also * [npm-packlist package](http://npm.im/npm-packlist) diff --git a/lib/pack.js b/lib/pack.js index 92a2fbd7ac33c..8e61efabb36e4 100644 --- a/lib/pack.js +++ b/lib/pack.js @@ -3,6 +3,7 @@ const log = require('npmlog') const pacote = require('pacote') const libpack = require('libnpmpack') const npa = require('npm-package-arg') +const getWorkspaces = require('./workspaces/get-workspaces.js') const { getContents, logTar } = require('./utils/tar.js') @@ -23,7 +24,7 @@ class Pack extends BaseCommand { /* istanbul ignore next - see test/lib/load-all-commands.js */ static get params () { - return ['dry-run'] + return ['dry-run', 'workspace', 'workspaces'] } /* istanbul ignore next - see test/lib/load-all-commands.js */ @@ -35,6 +36,10 @@ class Pack extends BaseCommand { this.pack(args).then(() => cb()).catch(cb) } + execWorkspaces (args, filters, cb) { + this.packWorkspaces(args, filters).then(() => cb()).catch(cb) + } + async pack (args) { if (args.length === 0) args = ['.'] @@ -62,5 +67,21 @@ class Pack extends BaseCommand { this.npm.output(tar.filename.replace(/^@/, '').replace(/\//, '-')) } } + + async packWorkspaces (args, filters) { + // If they either ask for nothing, or explicitly include '.' in the args, + // we effectively translate that into each workspace requested + + const useWorkspaces = args.length === 0 || args.includes('.') + + if (!useWorkspaces) { + this.npm.log.warn('Ignoring workspaces for specified package(s)') + return this.pack(args) + } + + const workspaces = + await getWorkspaces(filters, { path: this.npm.localPrefix }) + return this.pack([...workspaces.values(), ...args.filter(a => a !== '.')]) + } } module.exports = Pack diff --git a/lib/view.js b/lib/view.js index fb280f0d58248..91b32e2fd38fa 100644 --- a/lib/view.js +++ b/lib/view.js @@ -151,7 +151,7 @@ class View extends BaseCommand { const local = /^\.@/.test(pkg) || pkg === '.' if (!local) { - this.npm.log.warn('Ignoring workspaces for remote package') + this.npm.log.warn('Ignoring workspaces for specified package(s)') return this.view([pkg, ...args]) } let wholePackument = false 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 19beaaa85ea48..6bd38772ee368 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 @@ -625,7 +625,7 @@ All commands: npm pack [[<@scope>/]...] Options: - [--dry-run] + [--dry-run] [-w|--workspace [-w|--workspace ...]] [-ws|--workspaces] Run "npm help pack" for more info diff --git a/tap-snapshots/test-lib-view.js-TAP.test.js b/tap-snapshots/test-lib-view.js-TAP.test.js index 02810e31a5087..1cdf356356af9 100644 --- a/tap-snapshots/test-lib-view.js-TAP.test.js +++ b/tap-snapshots/test-lib-view.js-TAP.test.js @@ -450,7 +450,7 @@ dist-tags: ` exports[`test/lib/view.js TAP workspaces remote package name > must match snapshot 1`] = ` -Ignoring workspaces for remote package +Ignoring workspaces for specified package(s) ` exports[`test/lib/view.js TAP workspaces remote package name > must match snapshot 2`] = ` diff --git a/test/fixtures/mock-npm.js b/test/fixtures/mock-npm.js index c47758111fd40..01f482bde291b 100644 --- a/test/fixtures/mock-npm.js +++ b/test/fixtures/mock-npm.js @@ -2,10 +2,24 @@ // npm.config You still need a separate flatOptions but this is the first step // to eventually just using npm itself +const mockLog = { + clearProgress: () => {}, + disableProgress: () => {}, + enableProgress: () => {}, + http: () => {}, + info: () => {}, + levels: [], + notice: () => {}, + pause: () => {}, + silly: () => {}, + verbose: () => {}, + warn: () => {}, +} const mockNpm = (base = {}) => { const config = base.config || {} const flatOptions = base.flatOptions || {} return { + log: mockLog, ...base, flatOptions, config: { diff --git a/test/lib/pack.js b/test/lib/pack.js index a5163acfdc49b..4bcfe8ae3fd9c 100644 --- a/test/lib/pack.js +++ b/test/lib/pack.js @@ -1,6 +1,7 @@ const t = require('tap') const requireInject = require('require-inject') const mockNpm = require('../fixtures/mock-npm') +const pacote = require('pacote') const OUTPUT = [] const output = (...msg) => OUTPUT.push(msg) @@ -11,6 +12,16 @@ const libnpmpack = async (spec, opts) => { return '' } +const mockPacote = { + manifest: (spec) => { + if (spec.type === 'directory') + return pacote.manifest(spec) + return { + name: spec.name || 'test-package', + version: spec.version || '1.0.0-test', + } + }, +} t.afterEach(cb => { OUTPUT.length = 0 @@ -152,3 +163,95 @@ t.test('should log pack contents', (t) => { t.end() }) }) + +t.test('workspaces', (t) => { + 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 Pack = requireInject('../../lib/pack.js', { + libnpmpack, + pacote: mockPacote, + npmlog: { + notice: () => {}, + showProgress: () => {}, + clearProgress: () => {}, + }, + }) + const npm = mockNpm({ + localPrefix: testDir, + config: { + unicode: false, + json: false, + 'dry-run': false, + }, + output, + }) + const pack = new Pack(npm) + + t.test('all workspaces', (t) => { + pack.execWorkspaces([], [], er => { + if (er) + throw er + + t.strictSame(OUTPUT, [ + ['workspace-a-1.0.0.tgz'], + ['workspace-b-1.0.0.tgz'], + ]) + t.end() + }) + }) + + t.test('all workspaces, `.` first arg', (t) => { + pack.execWorkspaces(['.'], [], er => { + if (er) + throw er + + t.strictSame(OUTPUT, [ + ['workspace-a-1.0.0.tgz'], + ['workspace-b-1.0.0.tgz'], + ]) + t.end() + }) + }) + + t.test('one workspace', (t) => { + pack.execWorkspaces([], ['workspace-a'], er => { + if (er) + throw er + + t.strictSame(OUTPUT, [ + ['workspace-a-1.0.0.tgz'], + ]) + t.end() + }) + }) + + t.test('specific package', (t) => { + pack.execWorkspaces(['abbrev'], [], er => { + if (er) + throw er + + t.strictSame(OUTPUT, [ + ['abbrev-1.0.0-test.tgz'], + ]) + t.end() + }) + }) + t.end() +}) From 2bea185e8d225487285f6075642e4a2f2432179b Mon Sep 17 00:00:00 2001 From: Gar Date: Thu, 8 Apr 2021 10:32:41 -0700 Subject: [PATCH 4/5] docs: changelog for v7.9.0 --- CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e696a7c3e942..8155d019159f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,23 @@ +## v7.9.0 (2021-04-08) + +### FEATURES + +* [`1f3e88eba`](https://github.com/npm/cli/commit/1f3e88ebaf4901d8f9f07b43404d824fef7e5ff5) + [#3032](https://github.com/npm/cli/issues/3032) + feat(dist-tag): add workspace support + ([@nlf](https://github.com/nlf)) +* [`6e31df4e7`](https://github.com/npm/cli/commit/6e31df4e7957337962fd3d93e495931e3592bb9e) + [#3033](https://github.com/npm/cli/issues/3033) + feat(pack): add workspace support + ([@wraithgar](https://github.com/wraithgar)) + +### DEPENDENCIES + +* [`ba4f7fea8`](https://github.com/npm/cli/commit/ba4f7fea8fca8e3509469a218f094fe69095888b) + `licensee@8.2.0` + ## v7.8.0 (2021-04-01) + ### FEATURES From 13843f489401d918e7f1a41ed1ff636fc3feb603 Mon Sep 17 00:00:00 2001 From: Gar Date: Thu, 8 Apr 2021 10:32:59 -0700 Subject: [PATCH 5/5] 7.9.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index d09fe6ba13cba..ce3bfda5676cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "npm", - "version": "7.8.0", + "version": "7.9.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "npm", - "version": "7.8.0", + "version": "7.9.0", "bundleDependencies": [ "@npmcli/arborist", "@npmcli/ci-detect", diff --git a/package.json b/package.json index b6f2e0be7efcd..965f029aaab1a 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "7.8.0", + "version": "7.9.0", "name": "npm", "description": "a package manager for JavaScript", "keywords": [