Skip to content

Commit fde3546

Browse files
wraithgarisaacs
authored andcommitted
feat(unpublish): add workspace/dry-run support
PR-URL: #3251 Credit: @wraithgar Close: #3251 Reviewed-by: @ruyadorno, @isaacs
1 parent faa12cc commit fde3546

File tree

9 files changed

+269
-98
lines changed

9 files changed

+269
-98
lines changed

docs/content/commands/npm-unpublish.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,19 @@ passed.
4949

5050
<!-- AUTOGENERATED CONFIG DESCRIPTIONS START -->
5151
<!-- automatically generated, do not edit manually -->
52+
#### `dry-run`
53+
54+
* Default: false
55+
* Type: Boolean
56+
57+
Indicates that you don't want npm to make any changes and that it should
58+
only report what it would have done. This can be passed into any of the
59+
commands that modify your local installation, eg, `install`, `update`,
60+
`dedupe`, `uninstall`, as well as `pack` and `publish`.
61+
62+
Note: This is NOT honored by other network related commands, eg `dist-tags`,
63+
`owner`, etc.
64+
5265
#### `force`
5366

5467
* Default: false
@@ -73,6 +86,38 @@ mistakes, unnecessary performance degradation, and malicious input.
7386
If you don't have a clear idea of what you want to do, it is strongly
7487
recommended that you do not use this option!
7588

89+
#### `workspace`
90+
91+
* Default:
92+
* Type: String (can be set multiple times)
93+
94+
Enable running a command in the context of the configured workspaces of the
95+
current project while filtering by running only the workspaces defined by
96+
this configuration option.
97+
98+
Valid values for the `workspace` config are either:
99+
100+
* Workspace names
101+
* Path to a workspace directory
102+
* Path to a parent workspace directory (will result to selecting all of the
103+
nested workspaces)
104+
105+
When set for the `npm init` command, this may be set to the folder of a
106+
workspace which does not yet exist, to create the folder and set it up as a
107+
brand new workspace within the project.
108+
109+
This value is not exported to the environment for child processes.
110+
111+
#### `workspaces`
112+
113+
* Default: false
114+
* Type: Boolean
115+
116+
Enable running a command in the context of **all** the configured
117+
workspaces.
118+
119+
This value is not exported to the environment for child processes.
120+
76121
<!-- AUTOGENERATED CONFIG DESCRIPTIONS END -->
77122

78123
### See Also

lib/publish.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class Publish extends BaseCommand {
5858
if (args.length === 0)
5959
args = ['.']
6060
if (args.length !== 1)
61-
throw this.usage
61+
throw this.usageError()
6262

6363
log.verbose('publish', args)
6464

lib/unpublish.js

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
const path = require('path')
22
const util = require('util')
3-
const log = require('npmlog')
43
const npa = require('npm-package-arg')
54
const libaccess = require('libnpmaccess')
65
const npmFetch = require('npm-registry-fetch')
76
const libunpub = require('libnpmpublish').unpublish
87
const readJson = util.promisify(require('read-package-json'))
98

9+
const getWorkspaces = require('./workspaces/get-workspaces.js')
1010
const otplease = require('./utils/otplease.js')
1111
const getIdentity = require('./utils/get-identity.js')
1212

@@ -22,13 +22,13 @@ class Unpublish extends BaseCommand {
2222
}
2323

2424
/* istanbul ignore next - see test/lib/load-all-commands.js */
25-
static get usage () {
26-
return ['[<@scope>/]<pkg>[@<version>]']
25+
static get params () {
26+
return ['dry-run', 'force', 'workspace', 'workspaces']
2727
}
2828

2929
/* istanbul ignore next - see test/lib/load-all-commands.js */
30-
static get params () {
31-
return ['force']
30+
static get usage () {
31+
return ['[<@scope>/]<pkg>[@<version>]']
3232
}
3333

3434
async completion (args) {
@@ -67,25 +67,29 @@ class Unpublish extends BaseCommand {
6767
this.unpublish(args).then(() => cb()).catch(cb)
6868
}
6969

70+
execWorkspaces (args, filters, cb) {
71+
this.unpublishWorkspaces(args, filters).then(() => cb()).catch(cb)
72+
}
73+
7074
async unpublish (args) {
7175
if (args.length > 1)
72-
throw new Error(this.usage)
76+
throw this.usageError()
7377

7478
const spec = args.length && npa(args[0])
7579
const force = this.npm.config.get('force')
76-
const silent = this.npm.config.get('silent')
7780
const loglevel = this.npm.config.get('loglevel')
81+
const silent = loglevel === 'silent'
82+
const dryRun = this.npm.config.get('dry-run')
7883
let pkgName
7984
let pkgVersion
8085

81-
log.silly('unpublish', 'args[0]', args[0])
82-
log.silly('unpublish', 'spec', spec)
86+
this.npm.log.silly('unpublish', 'args[0]', args[0])
87+
this.npm.log.silly('unpublish', 'spec', spec)
8388

84-
if (!spec.rawSpec && !force) {
85-
throw new Error(
89+
if ((!spec || !spec.rawSpec) && !force) {
90+
throw this.usageError(
8691
'Refusing to delete entire project.\n' +
87-
'Run with --force to do this.\n' +
88-
this.usage
92+
'Run with --force to do this.'
8993
)
9094
}
9195

@@ -101,25 +105,43 @@ class Unpublish extends BaseCommand {
101105
if (err && err.code !== 'ENOENT' && err.code !== 'ENOTDIR')
102106
throw err
103107
else
104-
throw new Error(`Usage: ${this.usage}`)
108+
throw this.usageError()
105109
}
106110

107-
log.verbose('unpublish', manifest)
111+
this.npm.log.verbose('unpublish', manifest)
108112

109113
const { name, version, publishConfig } = manifest
110114
const pkgJsonSpec = npa.resolve(name, version)
111115
const optsWithPub = { ...opts, publishConfig }
112-
await otplease(opts, opts => libunpub(pkgJsonSpec, optsWithPub))
116+
if (!dryRun)
117+
await otplease(opts, opts => libunpub(pkgJsonSpec, optsWithPub))
113118
pkgName = name
114119
pkgVersion = version ? `@${version}` : ''
115120
} else {
116-
await otplease(opts, opts => libunpub(spec, opts))
121+
if (!dryRun)
122+
await otplease(opts, opts => libunpub(spec, opts))
117123
pkgName = spec.name
118124
pkgVersion = spec.type === 'version' ? `@${spec.rawSpec}` : ''
119125
}
120126

121-
if (!silent && loglevel !== 'silent')
127+
if (!silent)
122128
this.npm.output(`- ${pkgName}${pkgVersion}`)
123129
}
130+
131+
async unpublishWorkspaces (args, filters) {
132+
const workspaces =
133+
await getWorkspaces(filters, { path: this.npm.localPrefix })
134+
135+
const force = this.npm.config.get('force')
136+
if (!force) {
137+
throw this.usageError(
138+
'Refusing to delete entire project(s).\n' +
139+
'Run with --force to do this.'
140+
)
141+
}
142+
143+
for (const [name] of workspaces.entries())
144+
await this.unpublish([name])
145+
}
124146
}
125147
module.exports = Unpublish

tap-snapshots/test/lib/load-all-commands.js.test.cjs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1005,7 +1005,9 @@ Usage:
10051005
npm unpublish [<@scope>/]<pkg>[@<version>]
10061006
10071007
Options:
1008-
[-f|--force]
1008+
[--dry-run] [-f|--force]
1009+
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
1010+
[-ws|--workspaces]
10091011
10101012
Run "npm help unpublish" for more info
10111013
`

tap-snapshots/test/lib/publish.js.test.cjs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
*/
77
'use strict'
88
exports[`test/lib/publish.js TAP shows usage with wrong set of arguments > should print usage 1`] = `
9-
npm publish
9+
Error:
10+
Usage: npm publish
1011
1112
Publish a package
1213
@@ -18,13 +19,16 @@ Options:
1819
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
1920
[-ws|--workspaces]
2021
21-
Run "npm help publish" for more info
22+
Run "npm help publish" for more info {
23+
"code": "EUSAGE",
24+
}
2225
`
2326

2427
exports[`test/lib/publish.js TAP workspaces all workspaces > should output all publishes 1`] = `
2528
Array [
2629
2730
31+
2832
]
2933
`
3034

@@ -54,6 +58,12 @@ Array [
5458
},
5559
"version": "1.2.3-n",
5660
},
61+
Object {
62+
63+
"name": "workspace-n",
64+
"readme": "ERROR: No README data found!",
65+
"version": "1.2.3-n",
66+
},
5767
]
5868
`
5969

@@ -66,6 +76,9 @@ Array [
6676
},
6777
"workspace-b": {
6878
79+
},
80+
"workspace-n": {
81+
6982
}
7083
}
7184
),
@@ -98,6 +111,12 @@ Array [
98111
},
99112
"version": "1.2.3-n",
100113
},
114+
Object {
115+
116+
"name": "workspace-n",
117+
"readme": "ERROR: No README data found!",
118+
"version": "1.2.3-n",
119+
},
101120
]
102121
`
103122

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/* IMPORTANT
2+
* This snapshot file is auto-generated, but designed for humans.
3+
* It should be checked into source control and tracked carefully.
4+
* Re-generate by setting TAP_SNAPSHOT=1 and running tests.
5+
* Make sure to inspect the output below. Do not ignore changes!
6+
*/
7+
'use strict'
8+
exports[`test/lib/unpublish.js TAP workspaces all workspaces --force > should output all workspaces 1`] = `
9+
- workspace-a- workspace-b- workspace-n
10+
`
11+
12+
exports[`test/lib/unpublish.js TAP workspaces one workspace --force > should output one workspaces 1`] = `
13+
- workspace-a
14+
`

tap-snapshots/test/lib/utils/npm-usage.js.test.cjs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1046,7 +1046,9 @@ All commands:
10461046
npm unpublish [<@scope>/]<pkg>[@<version>]
10471047
10481048
Options:
1049-
[-f|--force]
1049+
[--dry-run] [-f|--force]
1050+
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
1051+
[-ws|--workspaces]
10501052
10511053
Run "npm help unpublish" for more info
10521054

test/lib/publish.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -538,12 +538,12 @@ t.test('workspaces', (t) => {
538538
repository: 'https://github.com/npm/workspace-b',
539539
}),
540540
},
541-
'workspace-c': JSON.stringify({
542-
'package.json': {
541+
'workspace-c': {
542+
'package.json': JSON.stringify({
543543
name: 'workspace-n',
544544
version: '1.2.3-n',
545-
},
546-
}),
545+
}),
546+
},
547547
})
548548

549549
const publishes = []

0 commit comments

Comments
 (0)