Skip to content

Commit c84d9d8

Browse files
committed
fix(unpublish): properly apply publishConfig
The tests for unpublish were mocked so heavily they weren't actually asserting anything. In rewriting them several bugs were found. - `write=true` was not being consistenly used when fetching packument data, it is now. - The decision on when to load the local package.json file was not working at all, that has been fixed. If the cwd contains a package.json whose name matches the package you are uninstalling, the local package.json will be read and its publishConfig applied to your request. - dead code inside the `npm unpublish` path was removed. There is no need to check if you are unpublishing the last version here, you're already unpublishing the entire project. - publishConfig is now being applied through the config flatten method, not a raw object assignment.
1 parent 362831c commit c84d9d8

4 files changed

Lines changed: 388 additions & 442 deletions

File tree

lib/commands/unpublish.js

Lines changed: 45 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
const path = require('path')
2-
const util = require('util')
3-
const npa = require('npm-package-arg')
41
const libaccess = require('libnpmaccess')
5-
const npmFetch = require('npm-registry-fetch')
62
const libunpub = require('libnpmpublish').unpublish
3+
const npa = require('npm-package-arg')
4+
const npmFetch = require('npm-registry-fetch')
5+
const path = require('path')
6+
const util = require('util')
77
const readJson = util.promisify(require('read-package-json'))
8+
9+
const flatten = require('../utils/config/flatten.js')
10+
const getIdentity = require('../utils/get-identity.js')
811
const log = require('../utils/log-shim')
912
const otplease = require('../utils/otplease.js')
10-
const getIdentity = require('../utils/get-identity.js')
1113

1214
const LAST_REMAINING_VERSION_ERROR = 'Refusing to delete the last version of the package. ' +
1315
'It will block from republishing a new version for 24 hours.\n' +
@@ -22,7 +24,8 @@ class Unpublish extends BaseCommand {
2224
static ignoreImplicitWorkspace = false
2325

2426
async getKeysOfVersions (name, opts) {
25-
const json = await npmFetch.json(npa(name).escapedName, opts)
27+
const pkgUri = npa(name).escapedName
28+
const json = await npmFetch.json(`${pkgUri}?write=true`, opts)
2629
return Object.keys(json.versions)
2730
}
2831

@@ -67,12 +70,10 @@ class Unpublish extends BaseCommand {
6770
throw this.usageError()
6871
}
6972

70-
const spec = args.length && npa(args[0])
73+
let spec = args.length && npa(args[0])
7174
const force = this.npm.config.get('force')
7275
const { silent } = this.npm
7376
const dryRun = this.npm.config.get('dry-run')
74-
let pkgName
75-
let pkgVersion
7677

7778
log.silly('unpublish', 'args[0]', args[0])
7879
log.silly('unpublish', 'spec', spec)
@@ -85,53 +86,52 @@ class Unpublish extends BaseCommand {
8586
}
8687

8788
const opts = { ...this.npm.flatOptions }
88-
if (!spec || path.resolve(spec.name) === this.npm.localPrefix) {
89-
// if there's a package.json in the current folder, then
90-
// read the package name and version out of that.
89+
90+
let pkgName
91+
let pkgVersion
92+
let manifest
93+
let manifestErr
94+
try {
9195
const pkgJson = path.join(this.npm.localPrefix, 'package.json')
92-
let manifest
93-
try {
94-
manifest = await readJson(pkgJson)
95-
} catch (err) {
96-
if (err && err.code !== 'ENOENT' && err.code !== 'ENOTDIR') {
97-
throw err
98-
} else {
96+
manifest = await readJson(pkgJson)
97+
} catch (err) {
98+
manifestErr = err
99+
}
100+
if (spec) {
101+
// If cwd has a package.json with a name that matches the package being
102+
// unpublished, load up the publishConfig
103+
if (manifest && manifest.name === spec.name && manifest.publishConfig) {
104+
flatten(manifest.publishConfig, opts)
105+
}
106+
const versions = await this.getKeysOfVersions(spec.name, opts)
107+
if (versions.length === 1 && !force) {
108+
throw this.usageError(LAST_REMAINING_VERSION_ERROR)
109+
}
110+
pkgName = spec.name
111+
pkgVersion = spec.type === 'version' ? `@${spec.rawSpec}` : ''
112+
} else {
113+
if (manifestErr) {
114+
if (manifestErr.code === 'ENOENT' || manifestErr.code === 'ENOTDIR') {
99115
throw this.usageError()
116+
} else {
117+
throw manifestErr
100118
}
101119
}
102120

103121
log.verbose('unpublish', manifest)
104122

105-
const { name, version, publishConfig } = manifest
106-
const pkgJsonSpec = npa.resolve(name, version)
107-
const optsWithPub = { ...opts, publishConfig }
108-
109-
const versions = await this.getKeysOfVersions(name, optsWithPub)
110-
if (versions.length === 1 && !force) {
111-
throw this.usageError(
112-
LAST_REMAINING_VERSION_ERROR
113-
)
123+
spec = npa.resolve(manifest.name, manifest.version)
124+
if (manifest.publishConfig) {
125+
flatten(manifest.publishConfig, opts)
114126
}
115127

116-
if (!dryRun) {
117-
await otplease(opts, opts => libunpub(pkgJsonSpec, optsWithPub))
118-
}
119-
pkgName = name
120-
pkgVersion = version ? `@${version}` : ''
121-
} else {
122-
const versions = await this.getKeysOfVersions(spec.name, opts)
123-
if (versions.length === 1 && !force) {
124-
throw this.usageError(
125-
LAST_REMAINING_VERSION_ERROR
126-
)
127-
}
128-
if (!dryRun) {
129-
await otplease(opts, opts => libunpub(spec, opts))
130-
}
131-
pkgName = spec.name
132-
pkgVersion = spec.type === 'version' ? `@${spec.rawSpec}` : ''
128+
pkgName = manifest.name
129+
pkgVersion = manifest.version ? `@${manifest.version}` : ''
133130
}
134131

132+
if (!dryRun) {
133+
await otplease(opts, opts => libunpub(spec, opts))
134+
}
135135
if (!silent) {
136136
this.npm.output(`- ${pkgName}${pkgVersion}`)
137137
}

tap-snapshots/test/lib/commands/unpublish.js.test.cjs

Lines changed: 0 additions & 14 deletions
This file was deleted.

test/fixtures/tnock.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22

33
const nock = require('nock')
44

5-
// TODO (other tests actually make network calls today, which is bad)
6-
// nock.disableNetConnect()
5+
// Uncomment this to find requests that aren't matching
6+
// nock.emitter.on('no match', req => console.log(req.options))
77

88
module.exports = tnock
9-
function tnock (t, host) {
10-
const server = nock(host)
9+
function tnock (t, host, opts) {
10+
nock.disableNetConnect()
11+
const server = nock(host, opts)
1112
t.teardown(function () {
13+
nock.enableNetConnect()
1214
server.done()
1315
})
1416
return server

0 commit comments

Comments
 (0)