11cb0ef41Sopenharmony_ciconst log = require('../utils/log-shim.js') 21cb0ef41Sopenharmony_ciconst semver = require('semver') 31cb0ef41Sopenharmony_ciconst pack = require('libnpmpack') 41cb0ef41Sopenharmony_ciconst libpub = require('libnpmpublish').publish 51cb0ef41Sopenharmony_ciconst runScript = require('@npmcli/run-script') 61cb0ef41Sopenharmony_ciconst pacote = require('pacote') 71cb0ef41Sopenharmony_ciconst npa = require('npm-package-arg') 81cb0ef41Sopenharmony_ciconst npmFetch = require('npm-registry-fetch') 91cb0ef41Sopenharmony_ciconst replaceInfo = require('../utils/replace-info.js') 101cb0ef41Sopenharmony_ci 111cb0ef41Sopenharmony_ciconst otplease = require('../utils/otplease.js') 121cb0ef41Sopenharmony_ciconst { getContents, logTar } = require('../utils/tar.js') 131cb0ef41Sopenharmony_ci 141cb0ef41Sopenharmony_ci// for historical reasons, publishConfig in package.json can contain ANY config 151cb0ef41Sopenharmony_ci// keys that npm supports in .npmrc files and elsewhere. We *may* want to 161cb0ef41Sopenharmony_ci// revisit this at some point, and have a minimal set that's a SemVer-major 171cb0ef41Sopenharmony_ci// change that ought to get a RFC written on it. 181cb0ef41Sopenharmony_ciconst { flatten } = require('@npmcli/config/lib/definitions') 191cb0ef41Sopenharmony_ciconst pkgJson = require('@npmcli/package-json') 201cb0ef41Sopenharmony_ci 211cb0ef41Sopenharmony_ciconst BaseCommand = require('../base-command.js') 221cb0ef41Sopenharmony_ciclass Publish extends BaseCommand { 231cb0ef41Sopenharmony_ci static description = 'Publish a package' 241cb0ef41Sopenharmony_ci static name = 'publish' 251cb0ef41Sopenharmony_ci static params = [ 261cb0ef41Sopenharmony_ci 'tag', 271cb0ef41Sopenharmony_ci 'access', 281cb0ef41Sopenharmony_ci 'dry-run', 291cb0ef41Sopenharmony_ci 'otp', 301cb0ef41Sopenharmony_ci 'workspace', 311cb0ef41Sopenharmony_ci 'workspaces', 321cb0ef41Sopenharmony_ci 'include-workspace-root', 331cb0ef41Sopenharmony_ci 'provenance', 341cb0ef41Sopenharmony_ci ] 351cb0ef41Sopenharmony_ci 361cb0ef41Sopenharmony_ci static usage = ['<package-spec>'] 371cb0ef41Sopenharmony_ci static workspaces = true 381cb0ef41Sopenharmony_ci static ignoreImplicitWorkspace = false 391cb0ef41Sopenharmony_ci 401cb0ef41Sopenharmony_ci async exec (args) { 411cb0ef41Sopenharmony_ci if (args.length === 0) { 421cb0ef41Sopenharmony_ci args = ['.'] 431cb0ef41Sopenharmony_ci } 441cb0ef41Sopenharmony_ci if (args.length !== 1) { 451cb0ef41Sopenharmony_ci throw this.usageError() 461cb0ef41Sopenharmony_ci } 471cb0ef41Sopenharmony_ci 481cb0ef41Sopenharmony_ci log.verbose('publish', replaceInfo(args)) 491cb0ef41Sopenharmony_ci 501cb0ef41Sopenharmony_ci const unicode = this.npm.config.get('unicode') 511cb0ef41Sopenharmony_ci const dryRun = this.npm.config.get('dry-run') 521cb0ef41Sopenharmony_ci const json = this.npm.config.get('json') 531cb0ef41Sopenharmony_ci const defaultTag = this.npm.config.get('tag') 541cb0ef41Sopenharmony_ci const ignoreScripts = this.npm.config.get('ignore-scripts') 551cb0ef41Sopenharmony_ci const { silent } = this.npm 561cb0ef41Sopenharmony_ci 571cb0ef41Sopenharmony_ci if (semver.validRange(defaultTag)) { 581cb0ef41Sopenharmony_ci throw new Error('Tag name must not be a valid SemVer range: ' + defaultTag.trim()) 591cb0ef41Sopenharmony_ci } 601cb0ef41Sopenharmony_ci 611cb0ef41Sopenharmony_ci const opts = { ...this.npm.flatOptions, progress: false } 621cb0ef41Sopenharmony_ci log.disableProgress() 631cb0ef41Sopenharmony_ci 641cb0ef41Sopenharmony_ci // you can publish name@version, ./foo.tgz, etc. 651cb0ef41Sopenharmony_ci // even though the default is the 'file:.' cwd. 661cb0ef41Sopenharmony_ci const spec = npa(args[0]) 671cb0ef41Sopenharmony_ci let manifest = await this.getManifest(spec, opts) 681cb0ef41Sopenharmony_ci 691cb0ef41Sopenharmony_ci // only run scripts for directory type publishes 701cb0ef41Sopenharmony_ci if (spec.type === 'directory' && !ignoreScripts) { 711cb0ef41Sopenharmony_ci await runScript({ 721cb0ef41Sopenharmony_ci event: 'prepublishOnly', 731cb0ef41Sopenharmony_ci path: spec.fetchSpec, 741cb0ef41Sopenharmony_ci stdio: 'inherit', 751cb0ef41Sopenharmony_ci pkg: manifest, 761cb0ef41Sopenharmony_ci banner: !silent, 771cb0ef41Sopenharmony_ci }) 781cb0ef41Sopenharmony_ci } 791cb0ef41Sopenharmony_ci 801cb0ef41Sopenharmony_ci // we pass dryRun: true to libnpmpack so it doesn't write the file to disk 811cb0ef41Sopenharmony_ci const tarballData = await pack(spec, { 821cb0ef41Sopenharmony_ci ...opts, 831cb0ef41Sopenharmony_ci foregroundScripts: this.npm.config.isDefault('foreground-scripts') 841cb0ef41Sopenharmony_ci ? true 851cb0ef41Sopenharmony_ci : this.npm.config.get('foreground-scripts'), 861cb0ef41Sopenharmony_ci dryRun: true, 871cb0ef41Sopenharmony_ci prefix: this.npm.localPrefix, 881cb0ef41Sopenharmony_ci workspaces: this.workspacePaths, 891cb0ef41Sopenharmony_ci }) 901cb0ef41Sopenharmony_ci const pkgContents = await getContents(manifest, tarballData) 911cb0ef41Sopenharmony_ci 921cb0ef41Sopenharmony_ci // The purpose of re-reading the manifest is in case it changed, 931cb0ef41Sopenharmony_ci // so that we send the latest and greatest thing to the registry 941cb0ef41Sopenharmony_ci // note that publishConfig might have changed as well! 951cb0ef41Sopenharmony_ci manifest = await this.getManifest(spec, opts, true) 961cb0ef41Sopenharmony_ci 971cb0ef41Sopenharmony_ci // JSON already has the package contents 981cb0ef41Sopenharmony_ci if (!json) { 991cb0ef41Sopenharmony_ci logTar(pkgContents, { unicode }) 1001cb0ef41Sopenharmony_ci } 1011cb0ef41Sopenharmony_ci 1021cb0ef41Sopenharmony_ci const resolved = npa.resolve(manifest.name, manifest.version) 1031cb0ef41Sopenharmony_ci const registry = npmFetch.pickRegistry(resolved, opts) 1041cb0ef41Sopenharmony_ci const creds = this.npm.config.getCredentialsByURI(registry) 1051cb0ef41Sopenharmony_ci const noCreds = !(creds.token || creds.username || creds.certfile && creds.keyfile) 1061cb0ef41Sopenharmony_ci const outputRegistry = replaceInfo(registry) 1071cb0ef41Sopenharmony_ci 1081cb0ef41Sopenharmony_ci if (noCreds) { 1091cb0ef41Sopenharmony_ci const msg = `This command requires you to be logged in to ${outputRegistry}` 1101cb0ef41Sopenharmony_ci if (dryRun) { 1111cb0ef41Sopenharmony_ci log.warn('', `${msg} (dry-run)`) 1121cb0ef41Sopenharmony_ci } else { 1131cb0ef41Sopenharmony_ci throw Object.assign(new Error(msg), { code: 'ENEEDAUTH' }) 1141cb0ef41Sopenharmony_ci } 1151cb0ef41Sopenharmony_ci } 1161cb0ef41Sopenharmony_ci 1171cb0ef41Sopenharmony_ci const access = opts.access === null ? 'default' : opts.access 1181cb0ef41Sopenharmony_ci let msg = `Publishing to ${outputRegistry} with tag ${defaultTag} and ${access} access` 1191cb0ef41Sopenharmony_ci if (dryRun) { 1201cb0ef41Sopenharmony_ci msg = `${msg} (dry-run)` 1211cb0ef41Sopenharmony_ci } 1221cb0ef41Sopenharmony_ci 1231cb0ef41Sopenharmony_ci log.notice('', msg) 1241cb0ef41Sopenharmony_ci 1251cb0ef41Sopenharmony_ci if (!dryRun) { 1261cb0ef41Sopenharmony_ci await otplease(this.npm, opts, o => libpub(manifest, tarballData, o)) 1271cb0ef41Sopenharmony_ci } 1281cb0ef41Sopenharmony_ci 1291cb0ef41Sopenharmony_ci if (spec.type === 'directory' && !ignoreScripts) { 1301cb0ef41Sopenharmony_ci await runScript({ 1311cb0ef41Sopenharmony_ci event: 'publish', 1321cb0ef41Sopenharmony_ci path: spec.fetchSpec, 1331cb0ef41Sopenharmony_ci stdio: 'inherit', 1341cb0ef41Sopenharmony_ci pkg: manifest, 1351cb0ef41Sopenharmony_ci banner: !silent, 1361cb0ef41Sopenharmony_ci }) 1371cb0ef41Sopenharmony_ci 1381cb0ef41Sopenharmony_ci await runScript({ 1391cb0ef41Sopenharmony_ci event: 'postpublish', 1401cb0ef41Sopenharmony_ci path: spec.fetchSpec, 1411cb0ef41Sopenharmony_ci stdio: 'inherit', 1421cb0ef41Sopenharmony_ci pkg: manifest, 1431cb0ef41Sopenharmony_ci banner: !silent, 1441cb0ef41Sopenharmony_ci }) 1451cb0ef41Sopenharmony_ci } 1461cb0ef41Sopenharmony_ci 1471cb0ef41Sopenharmony_ci if (!this.suppressOutput) { 1481cb0ef41Sopenharmony_ci if (!silent && json) { 1491cb0ef41Sopenharmony_ci this.npm.output(JSON.stringify(pkgContents, null, 2)) 1501cb0ef41Sopenharmony_ci } else if (!silent) { 1511cb0ef41Sopenharmony_ci this.npm.output(`+ ${pkgContents.id}`) 1521cb0ef41Sopenharmony_ci } 1531cb0ef41Sopenharmony_ci } 1541cb0ef41Sopenharmony_ci 1551cb0ef41Sopenharmony_ci return pkgContents 1561cb0ef41Sopenharmony_ci } 1571cb0ef41Sopenharmony_ci 1581cb0ef41Sopenharmony_ci async execWorkspaces (args) { 1591cb0ef41Sopenharmony_ci // Suppresses JSON output in publish() so we can handle it here 1601cb0ef41Sopenharmony_ci this.suppressOutput = true 1611cb0ef41Sopenharmony_ci 1621cb0ef41Sopenharmony_ci const results = {} 1631cb0ef41Sopenharmony_ci const json = this.npm.config.get('json') 1641cb0ef41Sopenharmony_ci const { silent } = this.npm 1651cb0ef41Sopenharmony_ci await this.setWorkspaces() 1661cb0ef41Sopenharmony_ci 1671cb0ef41Sopenharmony_ci for (const [name, workspace] of this.workspaces.entries()) { 1681cb0ef41Sopenharmony_ci let pkgContents 1691cb0ef41Sopenharmony_ci try { 1701cb0ef41Sopenharmony_ci pkgContents = await this.exec([workspace]) 1711cb0ef41Sopenharmony_ci } catch (err) { 1721cb0ef41Sopenharmony_ci if (err.code === 'EPRIVATE') { 1731cb0ef41Sopenharmony_ci log.warn( 1741cb0ef41Sopenharmony_ci 'publish', 1751cb0ef41Sopenharmony_ci `Skipping workspace ${ 1761cb0ef41Sopenharmony_ci this.npm.chalk.green(name) 1771cb0ef41Sopenharmony_ci }, marked as ${ 1781cb0ef41Sopenharmony_ci this.npm.chalk.bold('private') 1791cb0ef41Sopenharmony_ci }` 1801cb0ef41Sopenharmony_ci ) 1811cb0ef41Sopenharmony_ci continue 1821cb0ef41Sopenharmony_ci } 1831cb0ef41Sopenharmony_ci throw err 1841cb0ef41Sopenharmony_ci } 1851cb0ef41Sopenharmony_ci // This needs to be in-line w/ the rest of the output that non-JSON 1861cb0ef41Sopenharmony_ci // publish generates 1871cb0ef41Sopenharmony_ci if (!silent && !json) { 1881cb0ef41Sopenharmony_ci this.npm.output(`+ ${pkgContents.id}`) 1891cb0ef41Sopenharmony_ci } else { 1901cb0ef41Sopenharmony_ci results[name] = pkgContents 1911cb0ef41Sopenharmony_ci } 1921cb0ef41Sopenharmony_ci } 1931cb0ef41Sopenharmony_ci 1941cb0ef41Sopenharmony_ci if (!silent && json) { 1951cb0ef41Sopenharmony_ci this.npm.output(JSON.stringify(results, null, 2)) 1961cb0ef41Sopenharmony_ci } 1971cb0ef41Sopenharmony_ci } 1981cb0ef41Sopenharmony_ci 1991cb0ef41Sopenharmony_ci // if it's a directory, read it from the file system 2001cb0ef41Sopenharmony_ci // otherwise, get the full metadata from whatever it is 2011cb0ef41Sopenharmony_ci // XXX can't pacote read the manifest from a directory? 2021cb0ef41Sopenharmony_ci async getManifest (spec, opts, logWarnings = false) { 2031cb0ef41Sopenharmony_ci let manifest 2041cb0ef41Sopenharmony_ci if (spec.type === 'directory') { 2051cb0ef41Sopenharmony_ci const changes = [] 2061cb0ef41Sopenharmony_ci const pkg = await pkgJson.fix(spec.fetchSpec, { changes }) 2071cb0ef41Sopenharmony_ci if (changes.length && logWarnings) { 2081cb0ef41Sopenharmony_ci /* eslint-disable-next-line max-len */ 2091cb0ef41Sopenharmony_ci log.warn('publish', 'npm auto-corrected some errors in your package.json when publishing. Please run "npm pkg fix" to address these errors.') 2101cb0ef41Sopenharmony_ci log.warn('publish', `errors corrected:\n${changes.join('\n')}`) 2111cb0ef41Sopenharmony_ci } 2121cb0ef41Sopenharmony_ci // Prepare is the special function for publishing, different than normalize 2131cb0ef41Sopenharmony_ci const { content } = await pkg.prepare() 2141cb0ef41Sopenharmony_ci manifest = content 2151cb0ef41Sopenharmony_ci } else { 2161cb0ef41Sopenharmony_ci manifest = await pacote.manifest(spec, { 2171cb0ef41Sopenharmony_ci ...opts, 2181cb0ef41Sopenharmony_ci fullmetadata: true, 2191cb0ef41Sopenharmony_ci fullReadJson: true, 2201cb0ef41Sopenharmony_ci }) 2211cb0ef41Sopenharmony_ci } 2221cb0ef41Sopenharmony_ci if (manifest.publishConfig) { 2231cb0ef41Sopenharmony_ci flatten(manifest.publishConfig, opts) 2241cb0ef41Sopenharmony_ci } 2251cb0ef41Sopenharmony_ci return manifest 2261cb0ef41Sopenharmony_ci } 2271cb0ef41Sopenharmony_ci} 2281cb0ef41Sopenharmony_cimodule.exports = Publish 229