@lerna/publish
Version:
Publish packages in the current project
427 lines (407 loc) • 15.4 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var import_core = require("@lerna/core");
var import_dedent = __toESM(require("dedent"));
var import_fs_extra = __toESM(require("fs-extra"));
var import_npm_package_arg = __toESM(require("npm-package-arg"));
var import_os = __toESM(require("os"));
var import_p_reduce = __toESM(require("p-reduce"));
var import_pacote = __toESM(require("pacote"));
var import_path = __toESM(require("path"));
var import_slash = __toESM(require("slash"));
var import_url = require("url");
var import_yargs_parser = require("yargs-parser");
const childProcess = require("@lerna/child-process");
const initPackageJson = require("pify")(require("init-package-json"));
const { builtinNpmrc } = require("./lib/builtin-npmrc");
const { catFile } = require("./lib/cat-file");
const LERNA_MODULE_DATA = require.resolve(import_path.default.join(__dirname, "./lib/lerna-module-data.js"));
const DEFAULT_DESCRIPTION = [
"Now I\u2019m the model of a modern major general",
"The venerated Virginian veteran whose men are all",
"Lining up, to put me up on a pedestal",
"Writin\u2019 letters to relatives",
"Embellishin\u2019 my elegance and eloquence",
"But the elephant is in the room",
"The truth is in ya face when ya hear the British cannons go",
"BOOM"
].join(" / ");
module.exports = function factory(argv) {
return new CreateCommand(argv);
};
class CreateCommand extends import_core.Command {
initialize() {
const {
bin,
description = DEFAULT_DESCRIPTION,
esModule,
keywords,
license,
loc: pkgLocation,
name: pkgName,
yes
} = this.options;
const { name, scope } = (0, import_npm_package_arg.default)(pkgName);
if (!name && pkgName.includes("/")) {
throw new import_core.ValidationError(
"ENOPKGNAME",
"Invalid package name. Use the <loc> positional to specify package directory.\nSee https://github.com/lerna/lerna/tree/main/libs/commands/create#usage for details."
);
}
this.dirName = scope ? name.split("/").pop() : name;
this.pkgName = name;
this.pkgsDir = this._getPackagesDir(pkgLocation);
this.camelName = (0, import_yargs_parser.camelCase)(this.dirName);
this.outDir = esModule ? "dist" : "lib";
this.targetDir = import_path.default.resolve(this.pkgsDir, this.dirName);
this.binDir = import_path.default.join(this.targetDir, "bin");
this.binFileName = bin === true ? this.dirName : bin;
this.libDir = import_path.default.join(this.targetDir, esModule ? "src" : "lib");
this.libFileName = `${this.dirName}.js`;
this.testDir = import_path.default.join(this.targetDir, "__tests__");
this.testFileName = `${this.dirName}.test.js`;
this.conf = (0, import_core.npmConf)({
description,
esModule,
keywords,
scope,
yes
});
this.conf.addFile(builtinNpmrc(), "builtin");
this.conf.set("init-main", `${this.outDir}/${this.libFileName}`);
if (esModule) {
this.conf.set("init-es-module", `${this.outDir}/${this.dirName}.module.js`);
}
if (!this.project.isIndependent()) {
this.conf.set("init-version", this.project.version);
}
if (this.conf.get("init-author-name") === "") {
this.conf.set("init-author-name", this.gitConfig("user.name"));
}
if (this.conf.get("init-author-email") === "") {
this.conf.set("init-author-email", this.gitConfig("user.email"));
}
if (license) {
this.conf.set("init-license", license);
}
if (this.options.private) {
this.conf.set("private", true);
}
if (this.options.loglevel === "silent") {
this.conf.set("silent", true);
}
if (this.binFileName) {
this.conf.set("bin", {
[this.binFileName]: `bin/${this.binFileName}`
});
}
this.conf.set("directories", {
lib: this.outDir,
test: "__tests__"
});
this.setFiles();
this.setHomepage();
this.setPublishConfig();
this.setRepository();
return Promise.resolve(this.setDependencies());
}
_getPackagesDir(pkgLocation) {
const packageParentDirs = this.project.packageParentDirs;
if (!pkgLocation) {
return packageParentDirs[0];
}
const normalizedPkgLocation = import_path.default.resolve(this.project.rootPath, import_path.default.normalize(pkgLocation)).toLowerCase();
const packageParentDirsLower = packageParentDirs.map((p) => p.toLowerCase());
const matchingPathIndex = packageParentDirsLower.findIndex((p) => p.indexOf(normalizedPkgLocation) > -1);
if (matchingPathIndex > -1) {
return packageParentDirs[matchingPathIndex];
}
throw new import_core.ValidationError(
"ENOPKGDIR",
`Location "${pkgLocation}" is not configured as a workspace directory.`
);
}
execute() {
let chain = Promise.resolve();
chain = chain.then(() => import_fs_extra.default.mkdirp(this.libDir));
chain = chain.then(() => import_fs_extra.default.mkdirp(this.testDir));
chain = chain.then(() => Promise.all([this.writeReadme(), this.writeLibFile(), this.writeTestFile()]));
if (this.binFileName) {
chain = chain.then(() => import_fs_extra.default.mkdirp(this.binDir));
chain = chain.then(() => Promise.all([this.writeBinFile(), this.writeCliFile(), this.writeCliTest()]));
}
chain = chain.then(() => initPackageJson(this.targetDir, LERNA_MODULE_DATA, this.conf));
return chain.then((data) => {
if (this.options.esModule) {
this.logger.notice(
"\u2714",
import_dedent.default`
Ensure '${import_path.default.relative(".", this.pkgsDir)}/*/${this.outDir}' has been added to ./.gitignore
Ensure rollup or babel build scripts are in the root
`
);
}
this.logger.success(
"create",
`New package ${data.name} created at ./${import_path.default.relative(".", this.targetDir)}`
);
});
}
gitConfig(prop) {
return childProcess.execSync("git", ["config", "--get", prop], this.execOpts);
}
collectExternalVersions() {
const extVersions = /* @__PURE__ */ new Map();
for (const { externalDependencies } of this.packageGraph.values()) {
for (const [name, resolved] of externalDependencies) {
extVersions.set(name, resolved.fetchSpec);
}
}
return extVersions;
}
hasLocalRelativeFileSpec() {
for (const { localDependencies } of this.packageGraph.values()) {
for (const spec of localDependencies.values()) {
if (spec.type === "directory") {
return true;
}
}
}
}
resolveRelative(depNode) {
const relPath = import_path.default.relative(this.targetDir, depNode.location);
const spec = import_npm_package_arg.default.resolve(depNode.name, relPath, this.targetDir);
return (0, import_slash.default)(spec.saveSpec);
}
setDependencies() {
const inputs = new Set((this.options.dependencies || []).sort());
if (this.options.bin) {
inputs.add("yargs");
}
if (!inputs.size) {
return;
}
const exts = this.collectExternalVersions();
const localRelative = this.hasLocalRelativeFileSpec();
const savePrefix = this.conf.get("save-exact") ? "" : this.conf.get("save-prefix");
const pacoteOpts = this.conf.snapshot;
const decideVersion = (spec) => {
if (this.packageGraph.has(spec.name)) {
const depNode = this.packageGraph.get(spec.name);
if (localRelative) {
return this.resolveRelative(depNode);
}
return `${savePrefix}${depNode.version}`;
}
if (spec.type === "tag" && spec.fetchSpec === "latest") {
if (exts.has(spec.name)) {
return exts.get(spec.name);
}
return import_pacote.default.manifest(spec, pacoteOpts).then((pkg) => `${savePrefix}${pkg.version}`);
}
if (spec.type === "git") {
throw new import_core.ValidationError("EGIT", "Do not use git dependencies");
}
return spec.rawSpec;
};
let chain = Promise.resolve();
chain = chain.then(
() => (0, import_p_reduce.default)(
inputs,
(obj, input) => {
const spec = (0, import_npm_package_arg.default)(input);
return Promise.resolve(spec).then(decideVersion).then((version) => {
obj[spec.name] = version;
return obj;
});
},
{}
)
);
chain = chain.then((dependencies) => {
this.conf.set("dependencies", dependencies);
});
return chain;
}
setFiles() {
const files = [this.outDir];
if (this.options.bin) {
files.unshift("bin");
}
this.conf.set("files", files);
}
setHomepage() {
let { homepage = this.project.manifest.get("homepage") } = this.options;
if (!homepage) {
return;
}
if (homepage.indexOf("http") !== 0) {
homepage = `http://${homepage}`;
}
const hurl = new import_url.URL(homepage);
const relativeTarget = import_path.default.relative(this.project.rootPath, this.targetDir);
if (hurl.hostname.match("github")) {
hurl.pathname = import_path.default.posix.join(hurl.pathname, "tree/main", relativeTarget);
hurl.hash = "readme";
} else if (!this.options.homepage) {
hurl.pathname = import_path.default.posix.join(hurl.pathname, relativeTarget);
}
this.conf.set("homepage", hurl.href);
}
setPublishConfig() {
const scope = this.conf.get("scope");
const registry = this.options.registry || this.conf.get(`${scope}:registry`) || this.conf.get("registry");
const isPublicRegistry = registry === this.conf.root.registry;
const publishConfig = {};
if (scope && isPublicRegistry) {
publishConfig.access = this.options.access || "public";
}
if (registry && !isPublicRegistry) {
publishConfig.registry = registry;
}
if (this.options.tag) {
publishConfig.tag = this.options.tag;
}
if (Object.keys(publishConfig).length) {
this.conf.set("publishConfig", publishConfig);
}
}
setRepository() {
try {
const url = childProcess.execSync("git", ["remote", "get-url", "origin"], this.execOpts);
this.conf.set("repository", url);
} catch (err) {
this.logger.warn("ENOREMOTE", "No git remote found, skipping repository property");
}
}
writeReadme() {
const readmeContent = import_dedent.default`
# \`${this.pkgName}\`
> ${this.options.description || "TODO: description"}
## Usage
\`\`\`
${// eslint-disable-next-line no-nested-ternary
this.options.bin ? import_dedent.default`
npm -g i ${this.pkgName}
${this.binFileName} --help
` : this.options.esModule ? `import ${this.camelName} from '${this.pkgName}';` : `const ${this.camelName} = require('${this.pkgName}');`}
// TODO: DEMONSTRATE API
\`\`\`
`;
return catFile(this.targetDir, "README.md", readmeContent);
}
writeLibFile() {
const libContent = this.options.esModule ? import_dedent.default`
export default function ${this.camelName}() {
return 'Hello from ${this.camelName}';
}
` : import_dedent.default`
'use strict';
module.exports = ${this.camelName};
function ${this.camelName}() {
return 'Hello from ${this.camelName}';
}
`;
return catFile(this.libDir, this.libFileName, libContent);
}
writeTestFile() {
const testContent = this.options.esModule ? import_dedent.default`
import ${this.camelName} from '../src/${this.dirName}.js';
import { strict as assert } from 'assert';
assert.strictEqual(${this.camelName}(), 'Hello from ${this.camelName}');
console.info('${this.camelName} tests passed');
` : import_dedent.default`
'use strict';
const ${this.camelName} = require('..');
const assert = require('assert').strict;
assert.strictEqual(${this.camelName}(), 'Hello from ${this.camelName}');
console.info('${this.camelName} tests passed');
`;
return catFile(this.testDir, this.testFileName, testContent);
}
writeCliFile() {
const cliFileName = "cli.js";
const cliContent = [
this.options.esModule ? import_dedent.default`
import factory from 'yargs/yargs';
import ${this.camelName} from './${this.dirName}';
export default cli;
` : import_dedent.default`
'use strict';
const factory = require('yargs/yargs');
const ${this.camelName} = require('./${this.dirName}');
module.exports = cli;
`,
"",
// blank line
import_dedent.default`
function cli(cwd) {
const parser = factory(null, cwd);
parser.alias('h', 'help');
parser.alias('v', 'version');
parser.usage(
"$0",
"TODO: description",
yargs => {
yargs.options({
// TODO: options
});
},
argv => ${this.camelName}(argv)
);
return parser;
}
`
].join(import_os.default.EOL);
return catFile(this.libDir, cliFileName, cliContent);
}
writeCliTest() {
const cliTestFileName = "cli.test.js";
const cliTestContent = [
this.options.esModule ? import_dedent.default`
import cli from '../src/cli';
` : import_dedent.default`
'use strict';
const cli = require('../lib/cli');
`,
"",
// blank line
import_dedent.default`
describe('${this.pkgName} cli', () => {
// const argv = cli(cwd).parse(['args']);
it('needs tests');
});
`
].join(import_os.default.EOL);
return catFile(this.testDir, cliTestFileName, cliTestContent);
}
writeBinFile() {
const binContent = import_dedent.default`
#!/usr/bin/env node
'use strict';
// eslint-disable-next-line no-unused-expressions
require('../${this.outDir}/cli')${this.options.esModule ? ".default" : ""}().parse(process.argv.slice(2));`;
return catFile(this.binDir, this.binFileName, binContent, { mode: 493 });
}
}
module.exports.CreateCommand = CreateCommand;