-
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.
feat: publish only from local directory
- Loading branch information
1 parent
163ebeb
commit 6421b03
Showing
3 changed files
with
192 additions
and
240 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,105 @@ | ||
'use strict' | ||
|
||
const BB = require('bluebird') | ||
|
||
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 util = require('util') | ||
const log = require('npmlog') | ||
const npa = require('npm-package-arg') | ||
const npmConfig = require('./config/figgy-config.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) | ||
const pack = require('libnpmpack') | ||
const libpub = require('libnpmpublish').publish | ||
const runScript = require('@npmcli/run-script') | ||
|
||
const npm = require('./npm') | ||
const output = require('./utils/output') | ||
const otplease = require('./utils/otplease') | ||
const { getContents, logTar } = require('./utils/tar') | ||
|
||
publish.usage = 'npm publish [<tarball>|<folder>] [--tag <tag>] [--access <public|restricted>] [--dry-run]' + | ||
const readJson = util.promisify(require('read-package-json')) | ||
|
||
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 | ||
} | ||
async 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) => { | ||
const silent = log.level === 'silent' | ||
if (!silent && opts.json) { | ||
output(JSON.stringify(tarball, null, 2)) | ||
} else if (!silent) { | ||
output(`+ ${tarball.id}`) | ||
} | ||
}) | ||
.nodeify(cb) | ||
} | ||
const tarball = await publish_(args[0], opts) | ||
const silent = log.level === 'silent' | ||
if (!silent && opts.json) { | ||
output(JSON.stringify(tarball, null, 2)) | ||
} else if (!silent) { | ||
output(`+ ${tarball.id}`) | ||
} | ||
|
||
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) | ||
} | ||
}) | ||
return Promise.resolve(tarball) | ||
.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`) | ||
|
||
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 runScript({ | ||
event: 'prepublishOnly', | ||
path: arg, | ||
stdio: 'inherit', | ||
pkg: manifest | ||
}) | ||
} | ||
|
||
const tarballData = await pack(arg) | ||
const pkgContents = await getContents(manifest, tarballData) | ||
|
||
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 | ||
} | ||
}) | ||
if (!opts.json) { | ||
logTar(pkgContents, { | ||
log, | ||
unicode: npm.config.get('unicode') | ||
}) | ||
} else { | ||
return opts.Promise.resolve(true) | ||
} | ||
|
||
try { | ||
if (!opts.dryRun) { | ||
await otplease(opts, opts => libpub(arg, manifest, opts)) | ||
} | ||
} catch (err) { | ||
throw err | ||
} | ||
|
||
manifest = await readJson(`${arg}/package.json`) | ||
// publish | ||
await runScript({ | ||
event: 'publish', | ||
path: arg, | ||
stdio: 'inherit', | ||
pkg: manifest | ||
}) | ||
|
||
// postpublish | ||
await runScript({ | ||
event: 'postpublish', | ||
path: arg, | ||
stdio: 'inherit', | ||
pkg: manifest | ||
}) | ||
|
||
return pkgContents | ||
} |
Oops, something went wrong.