-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
03b7428
commit 0b58a78
Showing
3 changed files
with
178 additions
and
232 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,167 +1,104 @@ | ||
'use strict' | ||
|
||
const BB = require('bluebird') | ||
|
||
const util = require('util') | ||
const log = require('npmlog') | ||
const semver = require('semver') | ||
const pacote = require('pacote') | ||
const cacache = require('cacache') | ||
const figgyPudding = require('figgy-pudding') | ||
const libpub = require('libnpmpublish').publish | ||
const libunpub = require('libnpmpublish').unpublish | ||
const lifecycle = BB.promisify(require('./utils/lifecycle.js')) | ||
const log = require('npmlog') | ||
const npa = require('npm-package-arg') | ||
const npmConfig = require('./config/figgy-config.js') | ||
|
||
const npm = require('./npm.js') | ||
const output = require('./utils/output.js') | ||
const otplease = require('./utils/otplease.js') | ||
const pack = require('./pack') | ||
const { tarball, extract } = require('pacote') | ||
const path = require('path') | ||
const readFileAsync = BB.promisify(require('graceful-fs').readFile) | ||
const readJson = BB.promisify(require('read-package-json')) | ||
const semver = require('semver') | ||
const statAsync = BB.promisify(require('graceful-fs').stat) | ||
|
||
publish.usage = 'npm publish [<tarball>|<folder>] [--tag <tag>] [--access <public|restricted>] [--dry-run]' + | ||
const readJson = util.promisify(require('read-package-json')) | ||
const lifecycle = util.promisify(require('./utils/lifecycle.js')) | ||
const { getTarContents, logTarContents } = require('./utils/tar-contents.js') | ||
|
||
publish.usage = 'npm publish [<folder>] [--tag <tag>] [--access <public|restricted>] [--dry-run]' + | ||
"\n\nPublishes '.' if no argument supplied" + | ||
'\n\nSets tag `latest` if no --tag specified' | ||
|
||
publish.completion = function (opts, cb) { | ||
// publish can complete to a folder with a package.json | ||
// or a tarball, or a tarball url. | ||
// for now, not yet implemented. | ||
return cb() | ||
} | ||
|
||
const PublishConfig = figgyPudding({ | ||
const publishConfig = () => ({ | ||
dryRun: 'dry-run', | ||
'dry-run': { default: false }, | ||
force: { default: false }, | ||
json: { default: false }, | ||
Promise: { default: () => Promise }, | ||
tag: { default: 'latest' }, | ||
tmp: {} | ||
tag: 'defaultTag', | ||
'dry-run': false, | ||
defaultTag: 'latest', | ||
json: false, | ||
tmp: {}, | ||
...npm.flatOptions | ||
}) | ||
|
||
module.exports = publish | ||
function publish (args, isRetry, cb) { | ||
if (typeof cb !== 'function') { | ||
cb = isRetry | ||
isRetry = false | ||
} | ||
function publish (args, cb) { | ||
if (args.length === 0) args = ['.'] | ||
if (args.length !== 1) return cb(publish.usage) | ||
|
||
log.verbose('publish', args) | ||
log.verbose('publish', args) | ||
|
||
const opts = PublishConfig(npmConfig()) | ||
const t = opts.tag.trim() | ||
const opts = publishConfig() | ||
const t = opts.defaultTag.trim() | ||
if (semver.validRange(t)) { | ||
return cb(new Error('Tag name must not be a valid SemVer range: ' + t)) | ||
} | ||
|
||
return publish_(args[0], opts) | ||
.then((tarball) => { | ||
.then(tarball => { | ||
const silent = log.level === 'silent' | ||
if (!silent && opts.json) { | ||
output(JSON.stringify(tarball, null, 2)) | ||
} else if (!silent) { | ||
output(`+ ${tarball.id}`) | ||
} | ||
}) | ||
.nodeify(cb) | ||
} | ||
|
||
function publish_ (arg, opts) { | ||
return statAsync(arg).then((stat) => { | ||
if (stat.isDirectory()) { | ||
return stat | ||
} else { | ||
const err = new Error('not a directory') | ||
err.code = 'ENOTDIR' | ||
throw err | ||
} | ||
}).then(() => { | ||
return publishFromDirectory(arg, opts) | ||
}, (err) => { | ||
if (err.code !== 'ENOENT' && err.code !== 'ENOTDIR') { | ||
throw err | ||
} else { | ||
return publishFromPackage(arg, opts) | ||
} | ||
}) | ||
.then(cb) | ||
} | ||
|
||
function publishFromDirectory (arg, opts) { | ||
// All this readJson is because any of the given scripts might modify the | ||
async function publish_ (arg, opts) { | ||
// all this readJson is because any of the given scripts might modify the | ||
// package.json in question, so we need to refresh after every step. | ||
let contents | ||
return pack.prepareDirectory(arg).then(() => { | ||
return readJson(path.join(arg, 'package.json')) | ||
}).then((pkg) => { | ||
return lifecycle(pkg, 'prepublishOnly', arg) | ||
}).then(() => { | ||
return readJson(path.join(arg, 'package.json')) | ||
}).then((pkg) => { | ||
return cacache.tmp.withTmp(opts.tmp, {tmpPrefix: 'fromDir'}, (tmpDir) => { | ||
const target = path.join(tmpDir, 'package.tgz') | ||
return pack.packDirectory(pkg, arg, target, null, true) | ||
.tap((c) => { contents = c }) | ||
.then((c) => !opts.json && pack.logContents(c)) | ||
.then(() => upload(pkg, false, target, opts)) | ||
}) | ||
}).then(() => { | ||
return readJson(path.join(arg, 'package.json')) | ||
}).tap((pkg) => { | ||
return lifecycle(pkg, 'publish', arg) | ||
}).tap((pkg) => { | ||
return lifecycle(pkg, 'postpublish', arg) | ||
}) | ||
.then(() => contents) | ||
} | ||
let manifest = await readJson(`${arg}/package.json`) | ||
let pkgContents | ||
|
||
if (npm.config.get('ignore-prepublish')) { | ||
// just prepare | ||
await lifecycle(manifest, 'prepare', arg) | ||
} else { | ||
// prepublish & prepare | ||
await lifecycle(manifest, 'prepublish', arg) | ||
await lifecycle(manifest, 'prepare', arg) | ||
} | ||
|
||
function publishFromPackage (arg, opts) { | ||
return cacache.tmp.withTmp(opts.tmp, {tmpPrefix: 'fromPackage'}, tmp => { | ||
const extracted = path.join(tmp, 'package') | ||
const target = path.join(tmp, 'package.json') | ||
return tarball.toFile(arg, target, opts) | ||
.then(() => extract(arg, extracted, opts)) | ||
.then(() => readJson(path.join(extracted, 'package.json'))) | ||
.then((pkg) => { | ||
return BB.resolve(pack.getContents(pkg, target)) | ||
.tap((c) => !opts.json && pack.logContents(c)) | ||
.tap(() => upload(pkg, false, target, opts)) | ||
}) | ||
// prepublishOnly | ||
await lifecycle(manifest, 'prepublishOnly', arg) | ||
|
||
// package and display contents | ||
await cacache.tmp.withTmp(opts.tmp, { tmpPrefix: 'fromDir' }, async (tmpDir) => { | ||
manifest = await readJson(`${arg}/package.json`) | ||
const filename = `${manifest.name}-${manifest.version}.tgz` | ||
const tmpTarget = `${tmpDir}/${filename}` | ||
// pack tarball | ||
await pacote.tarball.file(`file:${arg}`, tmpTarget) | ||
pkgContents = await getTarContents(manifest, tmpTarget, filename) | ||
}) | ||
} | ||
|
||
function upload (pkg, isRetry, cached, opts) { | ||
if (!opts.dryRun) { | ||
return readFileAsync(cached).then(tarball => { | ||
return otplease(opts, opts => { | ||
return libpub(pkg, tarball, opts) | ||
}).catch(err => { | ||
if ( | ||
err.code === 'EPUBLISHCONFLICT' && | ||
opts.force && | ||
!isRetry | ||
) { | ||
log.warn('publish', 'Forced publish over ' + pkg._id) | ||
return otplease(opts, opts => libunpub( | ||
npa.resolve(pkg.name, pkg.version), opts | ||
)).finally(() => { | ||
// ignore errors. Use the force. Reach out with your feelings. | ||
return otplease(opts, opts => { | ||
return upload(pkg, true, tarball, opts) | ||
}).catch(() => { | ||
// but if it fails again, then report the first error. | ||
throw err | ||
}) | ||
}) | ||
} else { | ||
throw err | ||
} | ||
}) | ||
}) | ||
} else { | ||
return opts.Promise.resolve(true) | ||
if (!opts.json) { | ||
logTarContents(pkgContents) | ||
} | ||
|
||
try { | ||
if (!opts.dryRun) { | ||
await otplease(opts, opts => libpub(arg, manifest, opts)) | ||
} | ||
} catch (err) { | ||
throw err | ||
} | ||
|
||
manifest = await readJson(`${arg}/package.json`) | ||
// publish | ||
await lifecycle(manifest, 'publish', arg) | ||
// postpublish | ||
await lifecycle(manifest, 'postpublish', arg) | ||
|
||
return pkgContents | ||
} |
Oops, something went wrong.