Skip to content

Commit b24ed5f

Browse files
committed
publish: stop using npm-registry-client
1 parent 90a069e commit b24ed5f

File tree

3 files changed

+76
-126
lines changed

3 files changed

+76
-126
lines changed

lib/config/figgy-config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ function mkConfig (...providers) {
3636
dmode: npm.modes.exec,
3737
fmode: npm.modes.file,
3838
umask: npm.modes.umask,
39+
npmVersion: npm.version,
40+
tmp: npm.tmp,
3941
Promise: BB
4042
})
4143
const ownerStats = calculateOwner()

lib/publish.js

Lines changed: 65 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,21 @@
33
const BB = require('bluebird')
44

55
const cacache = require('cacache')
6-
const createReadStream = require('graceful-fs').createReadStream
7-
const getPublishConfig = require('./utils/get-publish-config.js')
6+
const figgyPudding = require('figgy-pudding')
7+
const libpub = require('libnpm/publish')
8+
const libunpub = require('libnpm/unpublish')
89
const lifecycle = BB.promisify(require('./utils/lifecycle.js'))
910
const log = require('npmlog')
10-
const mapToRegistry = require('./utils/map-to-registry.js')
11-
const npa = require('npm-package-arg')
12-
const npm = require('./npm.js')
11+
const npa = require('libnpm/parse-arg')
12+
const npmConfig = require('./config/figgy-config.js')
1313
const output = require('./utils/output.js')
14+
const otplease = require('./utils/otplease.js')
1415
const pack = require('./pack')
15-
const pacote = require('pacote')
16+
const { tarball, extract } = require('libnpm')
1617
const pacoteOpts = require('./config/pacote')
1718
const path = require('path')
19+
const readFileAsync = BB.promisify(require('graceful-fs').readFile)
1820
const readJson = BB.promisify(require('read-package-json'))
19-
const readUserInfo = require('./utils/read-user-info.js')
2021
const semver = require('semver')
2122
const statAsync = BB.promisify(require('graceful-fs').stat)
2223

@@ -31,6 +32,16 @@ publish.completion = function (opts, cb) {
3132
return cb()
3233
}
3334

35+
const PublishConfig = figgyPudding({
36+
dryRun: 'dry-run',
37+
'dry-run': { default: false },
38+
force: { default: false },
39+
json: { default: false },
40+
Promise: { default: () => Promise },
41+
tag: { default: 'latest' },
42+
tmp: {}
43+
})
44+
3445
module.exports = publish
3546
function publish (args, isRetry, cb) {
3647
if (typeof cb !== 'function') {
@@ -42,15 +53,16 @@ function publish (args, isRetry, cb) {
4253

4354
log.verbose('publish', args)
4455

45-
const t = npm.config.get('tag').trim()
56+
const opts = PublishConfig(npmConfig())
57+
const t = opts.tag.trim()
4658
if (semver.validRange(t)) {
4759
return cb(new Error('Tag name must not be a valid SemVer range: ' + t))
4860
}
4961

50-
return publish_(args[0])
62+
return publish_(args[0], opts)
5163
.then((tarball) => {
5264
const silent = log.level === 'silent'
53-
if (!silent && npm.config.get('json')) {
65+
if (!silent && opts.json) {
5466
output(JSON.stringify(tarball, null, 2))
5567
} else if (!silent) {
5668
output(`+ ${tarball.id}`)
@@ -59,7 +71,7 @@ function publish (args, isRetry, cb) {
5971
.nodeify(cb)
6072
}
6173

62-
function publish_ (arg) {
74+
function publish_ (arg, opts) {
6375
return statAsync(arg).then((stat) => {
6476
if (stat.isDirectory()) {
6577
return stat
@@ -69,17 +81,17 @@ function publish_ (arg) {
6981
throw err
7082
}
7183
}).then(() => {
72-
return publishFromDirectory(arg)
84+
return publishFromDirectory(arg, opts)
7385
}, (err) => {
7486
if (err.code !== 'ENOENT' && err.code !== 'ENOTDIR') {
7587
throw err
7688
} else {
77-
return publishFromPackage(arg)
89+
return publishFromPackage(arg, opts)
7890
}
7991
})
8092
}
8193

82-
function publishFromDirectory (arg) {
94+
function publishFromDirectory (arg, opts) {
8395
// All this readJson is because any of the given scripts might modify the
8496
// package.json in question, so we need to refresh after every step.
8597
let contents
@@ -90,12 +102,12 @@ function publishFromDirectory (arg) {
90102
}).then(() => {
91103
return readJson(path.join(arg, 'package.json'))
92104
}).then((pkg) => {
93-
return cacache.tmp.withTmp(npm.tmp, {tmpPrefix: 'fromDir'}, (tmpDir) => {
105+
return cacache.tmp.withTmp(opts.tmp, {tmpPrefix: 'fromDir'}, (tmpDir) => {
94106
const target = path.join(tmpDir, 'package.tgz')
95107
return pack.packDirectory(pkg, arg, target, null, true)
96108
.tap((c) => { contents = c })
97-
.then((c) => !npm.config.get('json') && pack.logContents(c))
98-
.then(() => upload(arg, pkg, false, target))
109+
.then((c) => !opts.json && pack.logContents(c))
110+
.then(() => upload(pkg, false, target, opts))
99111
})
100112
}).then(() => {
101113
return readJson(path.join(arg, 'package.json'))
@@ -107,121 +119,51 @@ function publishFromDirectory (arg) {
107119
.then(() => contents)
108120
}
109121

110-
function publishFromPackage (arg) {
111-
return cacache.tmp.withTmp(npm.tmp, {tmpPrefix: 'fromPackage'}, (tmp) => {
122+
function publishFromPackage (arg, opts) {
123+
return cacache.tmp.withTmp(opts.tmp, {tmpPrefix: 'fromPackage'}, tmp => {
112124
const extracted = path.join(tmp, 'package')
113125
const target = path.join(tmp, 'package.json')
114-
const opts = pacoteOpts()
115-
return pacote.tarball.toFile(arg, target, opts)
116-
.then(() => pacote.extract(arg, extracted, opts))
126+
const pacOpts = pacoteOpts()
127+
return tarball.toFile(arg, target, pacOpts)
128+
.then(() => extract(arg, extracted, pacOpts))
117129
.then(() => readJson(path.join(extracted, 'package.json')))
118130
.then((pkg) => {
119131
return BB.resolve(pack.getContents(pkg, target))
120-
.tap((c) => !npm.config.get('json') && pack.logContents(c))
121-
.tap(() => upload(arg, pkg, false, target))
132+
.tap((c) => !opts.json && pack.logContents(c))
133+
.tap(() => upload(pkg, false, target, opts))
122134
})
123135
})
124136
}
125137

126-
function upload (arg, pkg, isRetry, cached) {
127-
if (!pkg) {
128-
return BB.reject(new Error('no package.json file found'))
129-
}
130-
if (pkg.private) {
131-
return BB.reject(new Error(
132-
'This package has been marked as private\n' +
133-
"Remove the 'private' field from the package.json to publish it."
134-
))
135-
}
136-
const mappedConfig = getPublishConfig(
137-
pkg.publishConfig,
138-
npm.config,
139-
npm.registry
140-
)
141-
const config = mappedConfig.config
142-
const registry = mappedConfig.client
143-
144-
pkg._npmVersion = npm.version
145-
pkg._nodeVersion = process.versions.node
146-
147-
delete pkg.modules
148-
149-
return BB.fromNode((cb) => {
150-
mapToRegistry(pkg.name, config, (err, registryURI, auth, registryBase) => {
151-
if (err) { return cb(err) }
152-
cb(null, [registryURI, auth, registryBase])
153-
})
154-
}).spread((registryURI, auth, registryBase) => {
155-
// we just want the base registry URL in this case
156-
log.verbose('publish', 'registryBase', registryBase)
157-
log.silly('publish', 'uploading', cached)
158-
159-
pkg._npmUser = {
160-
name: auth.username,
161-
email: auth.email
162-
}
163-
164-
const params = {
165-
metadata: pkg,
166-
body: !npm.config.get('dry-run') && createReadStream(cached),
167-
auth: auth
168-
}
169-
170-
function closeFile () {
171-
if (!npm.config.get('dry-run')) {
172-
params.body.close()
173-
}
174-
}
175-
176-
// registry-frontdoor cares about the access level, which is only
177-
// configurable for scoped packages
178-
if (config.get('access')) {
179-
if (!npa(pkg.name).scope && config.get('access') === 'restricted') {
180-
throw new Error("Can't restrict access to unscoped packages.")
181-
}
182-
183-
params.access = config.get('access')
184-
}
185-
186-
if (npm.config.get('dry-run')) {
187-
log.verbose('publish', '--dry-run mode enabled. Skipping upload.')
188-
return BB.resolve()
189-
}
190-
191-
log.showProgress('publish:' + pkg._id)
192-
return BB.fromNode((cb) => {
193-
registry.publish(registryBase, params, cb)
194-
}).catch((err) => {
195-
if (
196-
err.code === 'EPUBLISHCONFLICT' &&
197-
npm.config.get('force') &&
198-
!isRetry
199-
) {
200-
log.warn('publish', 'Forced publish over ' + pkg._id)
201-
return BB.fromNode((cb) => {
202-
npm.commands.unpublish([pkg._id], cb)
203-
}).finally(() => {
204-
// close the file we are trying to upload, we will open it again.
205-
closeFile()
206-
// ignore errors. Use the force. Reach out with your feelings.
207-
return upload(arg, pkg, true, cached).catch(() => {
208-
// but if it fails again, then report the first error.
209-
throw err
138+
function upload (pkg, isRetry, cached, opts) {
139+
if (!opts.dryRun) {
140+
return readFileAsync(cached).then(tarball => {
141+
return otplease(opts, opts => {
142+
return libpub(pkg, tarball, opts)
143+
}).catch(err => {
144+
if (
145+
err.code === 'EPUBLISHCONFLICT' &&
146+
opts.force &&
147+
!isRetry
148+
) {
149+
log.warn('publish', 'Forced publish over ' + pkg._id)
150+
return otplease(opts, opts => libunpub(
151+
npa.resolve(pkg.name, pkg.version), opts
152+
)).finally(() => {
153+
// ignore errors. Use the force. Reach out with your feelings.
154+
return otplease(opts, opts => {
155+
return upload(pkg, true, tarball, opts)
156+
}).catch(() => {
157+
// but if it fails again, then report the first error.
158+
throw err
159+
})
210160
})
211-
})
212-
} else {
213-
// close the file we are trying to upload, all attempts to resume will open it again
214-
closeFile()
215-
throw err
216-
}
217-
})
218-
}).catch((err) => {
219-
if (err.code !== 'EOTP' && !(err.code === 'E401' && /one-time pass/.test(err.message))) throw err
220-
// we prompt on stdout and read answers from stdin, so they need to be ttys.
221-
if (!process.stdin.isTTY || !process.stdout.isTTY) throw err
222-
return readUserInfo.otp().then((otp) => {
223-
npm.config.set('otp', otp)
224-
return upload(arg, pkg, isRetry, cached)
161+
} else {
162+
throw err
163+
}
164+
})
225165
})
226-
})
166+
} else {
167+
return opts.Promise.resolve(true)
168+
}
227169
}

test/tap/publish-config.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,13 @@ test(function (t) {
4747
// itself functions normally.
4848
//
4949
// Make sure that we don't sit around waiting for lock files
50-
child = common.npm(['publish', '--userconfig=' + pkg + '/fixture_npmrc', '--tag=beta'], {
50+
child = common.npm([
51+
'publish',
52+
'--userconfig=' + pkg + '/fixture_npmrc',
53+
'--tag=beta',
54+
'--loglevel', 'error'
55+
], {
5156
cwd: pkg,
52-
stdio: 'inherit',
5357
env: {
5458
'npm_config_cache_lock_stale': 1000,
5559
'npm_config_cache_lock_wait': 1000,
@@ -58,7 +62,9 @@ test(function (t) {
5862
PATH: process.env.PATH,
5963
USERPROFILE: osenv.home()
6064
}
61-
}, function (err, code) {
65+
}, function (err, code, stdout, stderr) {
66+
t.comment(stdout)
67+
t.comment(stderr)
6268
t.ifError(err, 'publish command finished successfully')
6369
t.notOk(code, 'npm install exited with code 0')
6470
})

0 commit comments

Comments
 (0)