@pnpm/plugin-commands-publishing
Version:
The pack and publish commands of pnpm
258 lines • 10.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.fakeRegularManifest = exports.handler = exports.help = exports.commandNames = exports.cliOptionsTypes = exports.rcOptionsTypes = void 0;
const cli_utils_1 = require("@pnpm/cli-utils");
const config_1 = require("@pnpm/config");
const error_1 = require("@pnpm/error");
const lifecycle_1 = require("@pnpm/lifecycle");
const read_project_manifest_1 = require("@pnpm/read-project-manifest");
const run_npm_1 = require("@pnpm/run-npm");
const rimraf = require("@zkochan/rimraf");
const cpFile = require("cp-file");
const enquirer_1 = require("enquirer");
const fg = require("fast-glob");
const fs = require("mz/fs");
const path = require("path");
const R = require("ramda");
const realpathMissing = require("realpath-missing");
const renderHelp = require("render-help");
const writeJsonFile = require("write-json-file");
const gitChecks_1 = require("./gitChecks");
const recursivePublish_1 = require("./recursivePublish");
function rcOptionsTypes() {
return R.pick([
'access',
'git-checks',
'ignore-scripts',
'npm-path',
'otp',
'publish-branch',
'registry',
'tag',
'unsafe-perm',
], config_1.types);
}
exports.rcOptionsTypes = rcOptionsTypes;
function cliOptionsTypes() {
return {
...rcOptionsTypes(),
'dry-run': Boolean,
'json': Boolean,
'recursive': Boolean,
};
}
exports.cliOptionsTypes = cliOptionsTypes;
exports.commandNames = ['publish'];
function help() {
return renderHelp({
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: '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',
},
],
},
],
url: cli_utils_1.docsUrl('publish'),
usages: ['pnpm publish [<tarball>|<dir>] [--tag <tag>] [--access <public|restricted>] [options]'],
});
}
exports.help = help;
async function handler(opts, params) {
var _a, _b;
if (opts.gitChecks !== false && await gitChecks_1.isGitRepo()) {
const branch = (_a = opts.publishBranch) !== null && _a !== void 0 ? _a : 'master';
if (await gitChecks_1.getCurrentBranch() !== branch) {
const { confirm } = await enquirer_1.prompt({
message: `You are not on ${branch} branch, do you want to continue?`,
name: 'confirm',
type: 'confirm',
}); // tslint:disable-line:no-any
if (!confirm) {
throw new error_1.default('GIT_NOT_CORRECT_BRANCH', `Branch is not on '${branch}'.`);
}
}
if (!(await gitChecks_1.isWorkingTreeClean())) {
throw new error_1.default('GIT_NOT_UNCLEAN', 'Unclean working tree. Commit or stash changes first.');
}
if (!(await gitChecks_1.isRemoteHistoryClean())) {
throw new error_1.default('GIT_NOT_LATEST', 'Remote history differs. Please pull changes.');
}
}
if (opts.recursive && opts.selectedProjectsGraph) {
await recursivePublish_1.default({
...opts,
selectedProjectsGraph: opts.selectedProjectsGraph,
workspaceDir: (_b = opts.workspaceDir) !== null && _b !== void 0 ? _b : process.cwd(),
});
return;
}
if (params.length && params[0].endsWith('.tgz')) {
run_npm_1.default(opts.npmPath, ['publish', ...params]);
return;
}
const dir = params.length && params[0] || process.cwd();
const _runScriptsIfPresent = runScriptsIfPresent.bind(null, {
depPath: dir,
extraBinPaths: opts.extraBinPaths,
pkgRoot: dir,
rawConfig: opts.rawConfig,
rootModulesDir: await realpathMissing(path.join(dir, 'node_modules')),
stdio: 'inherit',
unsafePerm: true,
});
let _status;
await fakeRegularManifest({
dir,
engineStrict: opts.engineStrict,
workspaceDir: opts.workspaceDir || dir,
}, async (publishManifest) => {
// Unfortunately, we cannot support postpack at the moment
if (!opts.ignoreScripts) {
await _runScriptsIfPresent([
'prepublish',
'prepare',
'prepublishOnly',
'prepack',
], publishManifest);
}
const { status } = run_npm_1.default(opts.npmPath, ['publish', '--ignore-scripts', ...opts.argv.original.slice(1)]);
if (!opts.ignoreScripts) {
await _runScriptsIfPresent([
'publish',
'postpublish',
], publishManifest);
}
_status = status;
});
if (_status !== 0) {
process.exit(_status);
}
}
exports.handler = handler;
async function runScriptsIfPresent(opts, scriptNames, manifest) {
var _a;
for (const scriptName of scriptNames) {
if (!((_a = manifest.scripts) === null || _a === void 0 ? void 0 : _a[scriptName]))
continue;
await lifecycle_1.default(scriptName, manifest, opts);
}
}
const LICENSE_GLOB = 'LICEN{S,C}E{,.*}';
const findLicenses = fg.bind(fg, [LICENSE_GLOB]);
// property keys that are copied from publishConfig into the manifest
const PUBLISH_CONFIG_WHITELIST = new Set([
// manifest fields that may make sense to overwrite
'bin',
// https://github.com/stereobooster/package.json#package-bundlers
'main',
'module',
'typings',
'types',
'exports',
'browser',
'esnext',
'es2015',
'unpkg',
'umd:main',
]);
async function fakeRegularManifest(opts, fn) {
// If a workspace package has no License of its own,
// license files from the root of the workspace are used
const copiedLicenses = opts.dir !== opts.workspaceDir && (await findLicenses({ cwd: opts.dir })).length === 0
? await copyLicenses(opts.workspaceDir, opts.dir) : [];
const { fileName, manifest, writeProjectManifest } = await cli_utils_1.readProjectManifest(opts.dir, opts);
const publishManifest = await makePublishManifest(opts.dir, manifest);
const replaceManifest = fileName !== 'package.json' || !R.equals(manifest, publishManifest);
if (replaceManifest) {
await rimraf(path.join(opts.dir, fileName));
await writeJsonFile(path.join(opts.dir, 'package.json'), publishManifest);
}
await fn(publishManifest);
if (replaceManifest) {
await rimraf(path.join(opts.dir, 'package.json'));
await writeProjectManifest(manifest, true);
}
await Promise.all(copiedLicenses.map((copiedLicense) => fs.unlink(copiedLicense)));
}
exports.fakeRegularManifest = fakeRegularManifest;
async function makePublishManifest(dir, originalManifest) {
const publishManifest = {
...originalManifest,
dependencies: await makePublishDependencies(dir, originalManifest.dependencies),
devDependencies: await makePublishDependencies(dir, originalManifest.devDependencies),
optionalDependencies: await makePublishDependencies(dir, originalManifest.optionalDependencies),
};
const { publishConfig } = originalManifest;
if (publishConfig) {
Object.keys(publishConfig)
.filter(key => PUBLISH_CONFIG_WHITELIST.has(key))
.forEach(key => {
publishManifest[key] = publishConfig[key];
});
}
return publishManifest;
}
async function makePublishDependencies(dir, dependencies) {
if (!dependencies)
return dependencies;
const publishDependencies = R.fromPairs(await Promise.all(R.toPairs(dependencies)
.map(async ([depName, depSpec]) => [
depName,
await makePublishDependency(depName, depSpec, dir),
])));
return publishDependencies;
}
async function makePublishDependency(depName, depSpec, dir) {
if (!depSpec.startsWith('workspace:')) {
return depSpec;
}
if (depSpec === 'workspace:*') {
const { manifest } = await read_project_manifest_1.tryReadProjectManifest(path.join(dir, 'node_modules', depName));
if (!manifest || !manifest.version) {
throw new error_1.default('CANNOT_RESOLVE_WORKSPACE_PROTOCOL', `Cannot resolve workspace protocol of dependency "${depName}" ` +
`because this dependency is not installed. Try running "pnpm install".`);
}
return manifest.version;
}
return depSpec.substr(10);
}
async function copyLicenses(sourceDir, destDir) {
const licenses = await findLicenses({ cwd: sourceDir });
if (licenses.length === 0)
return [];
const copiedLicenses = [];
await Promise.all(licenses
.map((licenseRelPath) => path.join(sourceDir, licenseRelPath))
.map((licensePath) => {
const licenseCopyDest = path.join(destDir, path.basename(licensePath));
copiedLicenses.push(licenseCopyDest);
return cpFile(licensePath, licenseCopyDest);
}));
return copiedLicenses;
}
//# sourceMappingURL=publish.js.map