UNPKG

@pnpm/plugin-commands-publishing

Version:
326 lines 13.7 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.commandNames = void 0; exports.rcOptionsTypes = rcOptionsTypes; exports.cliOptionsTypes = cliOptionsTypes; exports.help = help; exports.removePnpmSpecificOptions = removePnpmSpecificOptions; exports.handler = handler; exports.publish = publish; exports.runScriptsIfPresent = runScriptsIfPresent; const fs_1 = require("fs"); const path_1 = __importDefault(require("path")); const cli_utils_1 = require("@pnpm/cli-utils"); const common_cli_options_help_1 = require("@pnpm/common-cli-options-help"); const config_1 = require("@pnpm/config"); const error_1 = require("@pnpm/error"); const lifecycle_1 = require("@pnpm/lifecycle"); const run_npm_1 = require("@pnpm/run-npm"); const git_utils_1 = require("@pnpm/git-utils"); const network_auth_header_1 = require("@pnpm/network.auth-header"); const plugin_commands_env_1 = require("@pnpm/plugin-commands-env"); const enquirer_1 = require("enquirer"); const rimraf_1 = __importDefault(require("@zkochan/rimraf")); const pick_1 = __importDefault(require("ramda/src/pick")); const realpath_missing_1 = __importDefault(require("realpath-missing")); const render_help_1 = __importDefault(require("render-help")); const tempy_1 = __importDefault(require("tempy")); const pack = __importStar(require("./pack.js")); const recursivePublish_js_1 = require("./recursivePublish.js"); function rcOptionsTypes() { return (0, pick_1.default)([ 'access', 'git-checks', 'ignore-scripts', 'provenance', 'npm-path', 'otp', 'publish-branch', 'registry', 'tag', 'unsafe-perm', 'embed-readme', ], config_1.types); } function cliOptionsTypes() { return { ...rcOptionsTypes(), 'dry-run': Boolean, force: Boolean, json: Boolean, recursive: Boolean, 'report-summary': Boolean, }; } exports.commandNames = ['publish']; function help() { return (0, render_help_1.default)({ description: 'Publishes a package to the npm registry.', descriptionLists: [ { title: 'Options', list: [ { description: "Don't check if current branch is your publish branch, clean, and up to date", name: '--no-git-checks', }, { description: 'Sets branch name to publish. Default is master', name: '--publish-branch', }, { description: 'Does everything a publish would do except actually publishing to the registry', name: '--dry-run', }, { description: 'Show information in JSON format', name: '--json', }, { description: 'Registers the published package with the given tag. By default, the "latest" tag is used.', name: '--tag <tag>', }, { description: 'Tells the registry whether this package should be published as public or restricted', name: '--access <public|restricted>', }, { description: 'Ignores any publish related lifecycle scripts (prepublishOnly, postpublish, and the like)', name: '--ignore-scripts', }, { description: 'Packages are proceeded to be published even if their current version is already in the registry. This is useful when a "prepublishOnly" script bumps the version of the package before it is published', name: '--force', }, { description: 'Save the list of the newly published packages to "pnpm-publish-summary.json". Useful when some other tooling is used to report the list of published packages.', name: '--report-summary', }, { description: 'When publishing packages that require two-factor authentication, this option can specify a one-time password', name: '--otp', }, { description: 'Publish all packages from the workspace', name: '--recursive', shortAlias: '-r', }, ], }, common_cli_options_help_1.FILTERING, ], url: (0, cli_utils_1.docsUrl)('publish'), usages: ['pnpm publish [<tarball>|<dir>] [--tag <tag>] [--access <public|restricted>] [options]'], }); } const GIT_CHECKS_HINT = 'If you want to disable Git checks on publish, set the "git-checks" setting to "false", or run again with "--no-git-checks".'; /** * Remove pnpm-specific CLI options that npm doesn't recognize. */ function removePnpmSpecificOptions(args) { const booleanOptions = new Set([ '--no-git-checks', '--embed-readme', '--no-embed-readme', ]); const optionsWithValue = new Set([ '--publish-branch', '--npm-path', ]); const result = []; let i = 0; while (i < args.length) { const arg = args[i]; if (booleanOptions.has(arg)) { // Skip only the boolean option itself i++; } else if (optionsWithValue.has(arg)) { // Skip the option and its value i++; // Skip the value if it exists and doesn't look like another option if (i < args.length && args[i][0] !== '-') { i++; } } else { result.push(arg); i++; } } return result; } async function handler(opts, params) { const result = await publish(opts, params); if (result?.manifest) return; return result; } async function publish(opts, params) { if (opts.gitChecks !== false && await (0, git_utils_1.isGitRepo)()) { if (!(await (0, git_utils_1.isWorkingTreeClean)())) { throw new error_1.PnpmError('GIT_UNCLEAN', 'Unclean working tree. Commit or stash changes first.', { hint: GIT_CHECKS_HINT, }); } const branches = opts.publishBranch ? [opts.publishBranch] : ['master', 'main']; const currentBranch = await (0, git_utils_1.getCurrentBranch)(); if (currentBranch === null) { throw new error_1.PnpmError('GIT_UNKNOWN_BRANCH', `The Git HEAD may not attached to any branch, but your "publish-branch" is set to "${branches.join('|')}".`, { hint: GIT_CHECKS_HINT, }); } if (!branches.includes(currentBranch)) { const { confirm } = await (0, enquirer_1.prompt)({ message: `You're on branch "${currentBranch}" but your "publish-branch" is set to "${branches.join('|')}". \ Do you want to continue?`, name: 'confirm', type: 'confirm', }); // eslint-disable-line @typescript-eslint/no-explicit-any if (!confirm) { throw new error_1.PnpmError('GIT_NOT_CORRECT_BRANCH', `Branch is not on '${branches.join('|')}'.`, { hint: GIT_CHECKS_HINT, }); } } if (!(await (0, git_utils_1.isRemoteHistoryClean)())) { throw new error_1.PnpmError('GIT_NOT_LATEST', 'Remote history differs. Please pull changes.', { hint: GIT_CHECKS_HINT, }); } } if (opts.recursive && (opts.selectedProjectsGraph != null)) { const { exitCode } = await (0, recursivePublish_js_1.recursivePublish)({ ...opts, selectedProjectsGraph: opts.selectedProjectsGraph, workspaceDir: opts.workspaceDir ?? process.cwd(), }); return { exitCode }; } let args = opts.argv.original.slice(1); const dirInParams = (params.length > 0) ? params[0] : undefined; if (dirInParams) { args = args.filter(arg => arg !== params[0]); } args = removePnpmSpecificOptions(args); if (dirInParams != null && (dirInParams.endsWith('.tgz') || dirInParams?.endsWith('.tar.gz'))) { const { status } = (0, run_npm_1.runNpm)(opts.npmPath, ['publish', dirInParams, ...args]); return { exitCode: status ?? 0 }; } const dir = dirInParams ?? opts.dir ?? process.cwd(); const _runScriptsIfPresent = runScriptsIfPresent.bind(null, { depPath: dir, extraBinPaths: opts.extraBinPaths, extraEnv: opts.extraEnv, pkgRoot: dir, rawConfig: opts.rawConfig, rootModulesDir: await (0, realpath_missing_1.default)(path_1.default.join(dir, 'node_modules')), stdio: 'inherit', unsafePerm: true, // when running scripts explicitly, assume that they're trusted. prepareExecutionEnv: plugin_commands_env_1.prepareExecutionEnv.bind(null, opts), }); const { manifest } = await (0, cli_utils_1.readProjectManifest)(dir, opts); // Unfortunately, we cannot support postpack at the moment if (!opts.ignoreScripts) { await _runScriptsIfPresent([ 'prepublishOnly', 'prepublish', ], manifest); } // We have to publish the tarball from another location. // Otherwise, npm would publish the package with the package.json file // from the current working directory, ignoring the package.json file // that was generated and packed to the tarball. const packDestination = tempy_1.default.directory(); const { tarballPath } = await pack.api({ ...opts, dir, packDestination, dryRun: false, }); await copyNpmrc({ dir, workspaceDir: opts.workspaceDir, packDestination }); const { status } = (0, run_npm_1.runNpm)(opts.npmPath, ['publish', '--ignore-scripts', path_1.default.basename(tarballPath), ...args], { cwd: packDestination, env: getEnvWithTokens(opts), }); await (0, rimraf_1.default)(packDestination); if (status != null && status !== 0) { return { exitCode: status }; } if (!opts.ignoreScripts) { await _runScriptsIfPresent([ 'publish', 'postpublish', ], manifest); } return { manifest }; } /** * The npm CLI doesn't support token helpers, so we transform the token helper settings * to regular auth token settings that the npm CLI can understand. */ function getEnvWithTokens(opts) { const tokenHelpers = Object.entries(opts.rawConfig).filter(([key]) => key.endsWith(':tokenHelper')); const tokenHelpersFromArgs = opts.argv.original .filter(arg => arg.includes(':tokenHelper=')) .map(arg => arg.split('=', 2)); const env = {}; for (const [key, helperPath] of tokenHelpers.concat(tokenHelpersFromArgs)) { const authHeader = (0, network_auth_header_1.loadToken)(helperPath, key); const authType = authHeader.startsWith('Bearer') ? '_authToken' : '_auth'; const registry = key.replace(/:tokenHelper$/, ''); env[`NPM_CONFIG_${registry}:${authType}`] = authType === '_authToken' ? authHeader.slice('Bearer '.length) : authHeader.replace(/Basic /i, ''); } return env; } async function copyNpmrc({ dir, workspaceDir, packDestination }) { const localNpmrc = path_1.default.join(dir, '.npmrc'); if ((0, fs_1.existsSync)(localNpmrc)) { await fs_1.promises.copyFile(localNpmrc, path_1.default.join(packDestination, '.npmrc')); return; } if (!workspaceDir) return; const workspaceNpmrc = path_1.default.join(workspaceDir, '.npmrc'); if ((0, fs_1.existsSync)(workspaceNpmrc)) { await fs_1.promises.copyFile(workspaceNpmrc, path_1.default.join(packDestination, '.npmrc')); } } async function runScriptsIfPresent(opts, scriptNames, manifest) { for (const scriptName of scriptNames) { if (!manifest.scripts?.[scriptName]) continue; await (0, lifecycle_1.runLifecycleHook)(scriptName, manifest, opts); // eslint-disable-line no-await-in-loop } } //# sourceMappingURL=publish.js.map