preconstruct
Version:
A smart build tool for libraries
1,113 lines (1,031 loc) • 59.2 kB
JavaScript
"use strict";
function _interopDefault(ex) {
return ex && "object" == typeof ex && "default" in ex ? ex.default : ex;
}
var meow = _interopDefault(require("meow")), _defineProperty = _interopDefault(require("@babel/runtime/helpers/defineProperty")), is = _interopDefault(require("sarcastic")), globby = _interopDefault(require("globby")), fs = require("fs-extra"), path = _interopDefault(require("path")), util = _interopDefault(require("util")), _objectSpread = _interopDefault(require("@babel/runtime/helpers/objectSpread")), inquirer = _interopDefault(require("inquirer")), pLimit = _interopDefault(require("p-limit")), DataLoader = _interopDefault(require("dataloader")), chalk = _interopDefault(require("chalk")), resolveFrom = _interopDefault(require("resolve-from")), packlist = _interopDefault(require("npm-packlist")), equal = _interopDefault(require("fast-deep-equal")), resolve = _interopDefault(require("resolve")), resolve$1 = _interopDefault(require("rollup-plugin-node-resolve")), alias = _interopDefault(require("rollup-plugin-alias")), cjs = _interopDefault(require("rollup-plugin-commonjs")), replace = _interopDefault(require("rollup-plugin-replace")), builtInModules = _interopDefault(require("builtin-modules")), rollup$1 = require("rollup"), fs$1 = require("fs"), json = _interopDefault(require("rollup-plugin-json")), _objectWithoutProperties = _interopDefault(require("@babel/runtime/helpers/objectWithoutProperties")), babel = require("@babel/core"), rollupPluginutils = require("rollup-pluginutils"), Worker = _interopDefault(require("jest-worker")), initHasher = _interopDefault(require("xxhash-wasm")), QuickLRU = _interopDefault(require("quick-lru")), codeFrame = require("@babel/code-frame"), installPackages = _interopDefault(require("install-packages")), ms = _interopDefault(require("ms"));
let EXTENSIONS = [ ".js", ".jsx", ".ts", ".tsx" ], PKG_JSON_CONFIG_FIELD = "preconstruct", itemsByPath = {};
class Item {
constructor(filePath, contents) {
_defineProperty(this, "_contents", void 0), _defineProperty(this, "_stringifiedSavedJson", void 0),
_defineProperty(this, "path", void 0), _defineProperty(this, "directory", void 0),
_defineProperty(this, "json", void 0), _defineProperty(this, "_config", void 0),
this.json = is(JSON.parse(contents), is.object), this._stringifiedSavedJson = JSON.stringify(this.json, null, 2),
this._contents = contents, this.path = filePath, this.directory = path.dirname(filePath),
this._config = this.json[PKG_JSON_CONFIG_FIELD] || {}, void 0 === itemsByPath[this.path] && (itemsByPath[this.path] = new Set()),
itemsByPath[this.path].add(this);
}
updater(json) {
this.json = json;
}
async refresh() {
let contents = await fs.readFile(this.path, "utf-8"), json = is(JSON.parse(contents), is.object);
for (let item of itemsByPath[this.path]) item.updater(json);
}
async save() {
Object.keys(this._config).length ? this.json[PKG_JSON_CONFIG_FIELD] = this._config : delete this.json[PKG_JSON_CONFIG_FIELD];
let stringified = JSON.stringify(this.json, null, 2);
if (stringified !== this._stringifiedSavedJson) {
await fs.writeFile(this.path, JSON.stringify(this.json, null, 2) + "\n"), this._config = this.json[PKG_JSON_CONFIG_FIELD] || {};
for (let item of itemsByPath[this.path]) item.updater(this.json);
return this._stringifiedSavedJson = stringified, !0;
}
return !1;
}
}
class FatalError extends Error {
constructor(message, scope) {
super(message), _defineProperty(this, "scope", void 0), this.scope = scope;
}
}
class UnexpectedBuildError extends FatalError {
constructor(error, pkgName) {
super(`${util.format("", error).trim()}`, pkgName);
}
}
class FixableError extends FatalError {}
let limit = pLimit(1), prefix = `🎁 ${chalk.green("?")}`;
function createPromptConfirmLoader(message) {
let loader = new DataLoader(pkgs => limit(() => (async () => {
if (1 === pkgs.length) {
let {confirm: confirm} = await inquirer.prompt([ {
type: "confirm",
name: "confirm",
message: message,
prefix: prefix + " " + pkgs[0].name
} ]);
return [ confirm ];
}
let {answers: answers} = await inquirer.prompt([ {
type: "checkbox",
name: "answers",
message: message,
choices: pkgs.map(pkg => ({
name: pkg.name,
checked: !0
})),
prefix: prefix
} ]);
return pkgs.map(pkg => answers.includes(pkg.name));
})(), {
cache: !1
}));
return pkg => loader.load(pkg);
}
let promptConfirm = async message => {
let {confirm: confirm} = await inquirer.prompt([ {
type: "confirm",
name: "confirm",
message: message,
prefix: prefix
} ]);
return confirm;
}, doPromptInput = async (message, pkg, defaultAnswer) => {
let {input: input} = await inquirer.prompt([ {
type: "input",
name: "input",
message: message,
prefix: prefix + " " + pkg.name,
default: defaultAnswer
} ]);
return input;
}, promptInput = (message, pkg, defaultAnswer) => limit(() => doPromptInput(message, pkg, defaultAnswer));
function format(args, messageType, scope) {
let fullPrefix = "🎁 " + {
error: chalk.red("error"),
success: chalk.green("success"),
info: chalk.cyan("info")
}[messageType] + (void 0 === scope ? "" : " " + chalk.cyan(scope));
return fullPrefix + util.format("", ...args).split("\n").join("\n" + fullPrefix + " ");
}
function error(message, scope) {
console.error(format([ message ], "error", scope));
}
function success(message, scope) {
console.log(format([ message ], "success", scope));
}
function info(message, scope) {
console.log(format([ message ], "info", scope));
}
async function validateIncludedFiles(pkg) {
try {
await Promise.all(pkg.entrypoints.map(async entrypoint => {
let filename = path.join(entrypoint.directory, "dist", "preconstruct-test-file");
return await fs.ensureFile(filename), fs.writeFile(filename, "test content");
}));
let result = await packlist({
path: pkg.directory
}), messages = [];
if (pkg.entrypoints.forEach(entrypoint => {
let pkgJsonPath = path.relative(pkg.directory, path.resolve(entrypoint.directory, "package.json")), distFilePath = path.relative(pkg.directory, path.resolve(entrypoint.directory, "dist", "preconstruct-test-file")), entrypointName = path.relative(pkg.directory, entrypoint.directory);
result.includes(pkgJsonPath) ? result.includes(distFilePath) || messages.push(`the dist directory ${"" === entrypointName ? "" : `for entrypoint ${chalk.cyan(entrypointName)} `}isn't included in the published files for this package, please add it to the files field in the package's package.json`) : messages.push(`the entrypoint ${chalk.cyan(entrypointName)} isn't included in the published files for this package, please add it to the files field in the package's package.json`);
}), messages.length) throw new FatalError(messages.join("\n"), pkg.name);
} finally {
await Promise.all(pkg.entrypoints.map(entrypoint => fs.remove(path.join(entrypoint.directory, "dist", "preconstruct-test-file"))));
}
}
let unsafeRequire = require, askGlobalLimit = pLimit(1);
class Project extends Item {
constructor(...args) {
super(...args), _defineProperty(this, "packages", void 0);
}
get configPackages() {
return is(this._config.packages, is.default(is.arrayOf(is.string), [ "." ]));
}
get isBolt() {
let hasBolt = !!this.json.bolt, hasYarnWorkspaces = !!this.json.workspaces;
return hasBolt && !hasYarnWorkspaces;
}
static async create(directory) {
let filePath = path.join(directory, "package.json"), contents = await fs.readFile(filePath, "utf-8"), project = new Project(filePath, contents);
return project.packages = await project._packages(), project;
}
get name() {
return is(this.json.name, is.string);
}
set name(name) {
this.json.name = name;
}
async _packages() {
if (!this._config.packages && this.json.workspaces) {
let _workspaces;
Array.isArray(this.json.workspaces) ? _workspaces = this.json.workspaces : Array.isArray(this.json.workspaces.packages) && (_workspaces = this.json.workspaces.packages);
let workspaces = is(_workspaces, is.arrayOf(is.string)), packages = await promptInput("what packages should preconstruct build?", this, workspaces.join(","));
this._config.packages = packages.split(","), await this.save();
}
try {
let lastErr, filenames = await globby(this.configPackages, {
cwd: this.directory,
onlyDirectories: !0,
absolute: !0,
expandDirectories: !1
}), dirsWithoutPkgJson = [], packages = await Promise.all(filenames.map(async x => {
try {
let pkg = await Package.create(x);
return pkg.project = this, pkg;
} catch (err) {
if ("ENOENT" === err.code && err.path === path.join(x, "package.json")) return lastErr = err,
void dirsWithoutPkgJson.push(x);
throw err;
}
}));
if (dirsWithoutPkgJson.length) {
if (error("there are some package directories that do not have package.jsons\nthis is often caused by switching branches.\n\n" + dirsWithoutPkgJson.join("\n") + "\n"),
!await promptConfirm("would you like preconstruct to delete these directories automatically?")) throw lastErr;
return await Promise.all(dirsWithoutPkgJson.map(dir => fs.remove(dir))), this._packages();
}
return await Promise.all(packages.map(pkg => validateIncludedFiles(pkg))), packages;
} catch (error) {
if (error instanceof is.AssertionError) return [];
throw error;
}
}
global(pkg) {
if (void 0 !== this._config.globals && this._config.globals[pkg]) return this._config.globals[pkg];
try {
let pkgJson = unsafeRequire(resolveFrom(this.directory, path.join(pkg, "package.json")));
if (pkgJson && pkgJson[PKG_JSON_CONFIG_FIELD] && pkgJson[PKG_JSON_CONFIG_FIELD].umdName) return pkgJson[PKG_JSON_CONFIG_FIELD].umdName;
} catch (err) {
if ("MODULE_NOT_FOUND" !== err.code) throw err;
}
throw askGlobalLimit(() => (async () => {
if (void 0 !== this._config.globals && this._config.globals[pkg]) return;
let response = await promptInput(`What should the umdName of ${pkg} be?`, this);
this._addGlobal(pkg, response), await this.save();
})());
}
_addGlobal(pkg, name) {
this._config.globals || (this._config.globals = {}), this._config.globals[pkg] = name;
}
}
let errors = {
noSource: source => `no source file was provided, please create a file at ${source} or specify a custom source file with the ${PKG_JSON_CONFIG_FIELD} source option`,
deniedWriteMainField: "changing the main field is required to build",
invalidModuleField: "module field is invalid",
invalidMainField: "main field is invalid",
invalidUmdMainField: "umd:main field is invalid",
invalidBrowserField: "browser field is invalid",
umdNameNotSpecified: `the umd:main field is specified but a umdName option is not specified. please add it to the ${PKG_JSON_CONFIG_FIELD} field in your package.json`,
deniedWriteBrowserField: "building browser bundles for modules that include typeof window or typeof document is currently required",
noEntrypointPkgJson: "There is a missing package.json for an entrypoint",
noEntrypoints: "packages must have at least one entrypoint, this package has no entrypoints"
}, confirms = {
writeMainField: createPromptConfirmLoader("preconstruct is going to change the main field in your package.json, are you okay with that?"),
writeModuleField: createPromptConfirmLoader("would you like to generate module builds? this will write to the module field in your package.json"),
fixModuleField: createPromptConfirmLoader("would you like to fix the module field?"),
fixUmdBuild: createPromptConfirmLoader("would you like to fix the umd field?"),
shouldInstallObjectAssign: createPromptConfirmLoader("Object.assign is polyfilled with object-assign to reduce bundle size when used with react. would you like to install object-assign automatically?"),
shouldInstallBabelRuntime: createPromptConfirmLoader("Babel helpers (functions inserted by babel transforms) should be imported from a @babel/runtime package (which has to be in your dependencies) to reduce bundle size. would you like to install @babel/runtime automatically?"),
addBrowserField: createPromptConfirmLoader("typeof window or typeof document is used in this package. would you like build seperate browser builds for better browser bundle sizes?"),
fixBrowserField: createPromptConfirmLoader("would you like to fix the browser build?"),
createEntrypointPkgJson: createPromptConfirmLoader("A package.json file does not exist for this entrypoint, would you like to create one automatically?")
}, inputs = {
getUmdName: "what should the name used for UMD bundles be?"
}, infos = {
validMainField: "main field is valid",
validModuleField: "module field is valid",
validUmdMainField: "umd:main field is valid",
validEntrypoint: "a valid entry point exists.",
validBrowserField: "browser field is valid",
validPackageEntrypoints: "package entrypoints are valid"
}, successes = {
validProject: "project is valid!",
startedWatching: "started watching!"
};
function getNameForDist(name) {
return name.replace(/.*\//, "");
}
function getValidStringFieldContentForBuildType(type, pkgName) {
let safeName = getNameForDist(pkgName);
switch (type) {
case "main":
return `dist/${safeName}.cjs.js`;
case "module":
return `dist/${safeName}.esm.js`;
case "umd:main":
return `dist/${safeName}.umd.min.js`;
}
throw new Error(`unknown string build type: ${type}. this is likely a bug in preconstruct.`);
}
function getValidObjectFieldContentForBuildType(type, pkgName, hasModuleBuild) {
let safeName = getNameForDist(pkgName);
switch (type) {
case "browser":
{
let obj = {
[`./dist/${safeName}.cjs.js`]: `./dist/${safeName}.browser.cjs.js`
};
return hasModuleBuild && (obj[`./dist/${safeName}.esm.js`] = `./dist/${safeName}.browser.esm.js`),
obj;
}
}
throw new Error(`unknown object build type: ${type}. this is likely a bug in preconstruct.`);
}
function flowTemplate(hasDefaultExport, relativePath) {
return `// @flow\nexport * from "${relativePath}";${hasDefaultExport ? `\nexport { default } from "${relativePath}";` : ""}\n`;
}
function tsTemplate(hasDefaultExport, relativePath) {
return `export * from "${relativePath}";${hasDefaultExport ? `\nexport { default } from "${relativePath}";` : ""}\n`;
}
let camelToPkgJsonField = {
main: "main",
module: "module",
umdMain: "umd:main",
browser: "browser"
};
async function fixPackage(pkg) {
if (0 === pkg.entrypoints.length) throw new FatalError(errors.noEntrypoints, pkg.name);
let fields = {
main: !0,
module: pkg.entrypoints.some(x => x.module),
umdMain: pkg.entrypoints.some(x => x.umdMain),
browser: pkg.entrypoints.some(x => x.browser)
};
return Object.keys(fields).filter(x => fields[x]).forEach(field => {
pkg.setFieldOnEntrypoints(field);
}), (await Promise.all(pkg.entrypoints.map(x => x.save()))).some(x => x);
}
let unsafeRequire$1 = require;
function validatePackage(pkg) {
if (0 === pkg.entrypoints.length) throw new FatalError(errors.noEntrypoints, pkg.name);
let fields = {
module: !!pkg.entrypoints[0].module,
umdMain: !!pkg.entrypoints[0].umdMain,
browser: !!pkg.entrypoints[0].browser
};
if (pkg.entrypoints.forEach(entrypoint => {
Object.keys(fields).forEach(field => {
if (entrypoint[field] && !fields[field]) throw new FixableError(`${pkg.entrypoints[0].name} has a ${camelToPkgJsonField[field]} build but ${entrypoint.name} does not have a ${camelToPkgJsonField[field]} build. Entrypoints in a package must either all have a particular build type or all not have a particular build type.`, pkg.name);
if (!entrypoint[field] && fields[field]) throw new FixableError(`${entrypoint.name} has a ${camelToPkgJsonField[field]} build but ${pkg.entrypoints[0].name} does not have a ${camelToPkgJsonField[field]} build. Entrypoints in a package must either all have a particular build type or all not have a particular build type.`, pkg.name);
});
}), fields.umdMain) {
let sortaAllDeps = new Set([ ...pkg.peerDependencies ? Object.keys(pkg.peerDependencies) : [], ...pkg.dependencies ? Object.keys(pkg.dependencies) : [] ]);
for (let depName in pkg.dependencies) {
let depPkgJson = unsafeRequire$1(resolveFrom(pkg.directory, depName + "/package.json"));
if (depPkgJson.peerDependencies) for (let pkgName in depPkgJson.peerDependencies) if (!sortaAllDeps.has(pkgName)) throw new FatalError(`the package ${chalk.blue(pkg.name)} depends on ${chalk.blue(depName)} which has a peerDependency on ${chalk.blue(pkgName)} but ${chalk.blue(pkgName)} is not specified in the dependencies or peerDependencies of ${chalk.blue(pkg.name)}. please add ${chalk.blue(pkgName)} to the dependencies or peerDependencies of ${chalk.blue(pkg.name)}`, pkg.name);
}
}
}
function validateEntrypointSource(entrypoint) {
try {
if (!entrypoint.source.startsWith(entrypoint.package.directory)) throw new FatalError(`entrypoint source files must be inside their respective package directory but this entrypoint has specified its source file as ${entrypoint.configSource}`, entrypoint.name);
} catch (e) {
if ("MODULE_NOT_FOUND" === e.code) throw new FatalError(errors.noSource(entrypoint.configSource), entrypoint.name);
throw e;
}
}
function isMainFieldValid(entrypoint) {
return entrypoint.main === getValidStringFieldContentForBuildType("main", entrypoint.package.name);
}
function isModuleFieldValid(entrypoint) {
return entrypoint.module === getValidStringFieldContentForBuildType("module", entrypoint.package.name);
}
function isUmdMainFieldValid(entrypoint) {
return entrypoint.umdMain === getValidStringFieldContentForBuildType("umd:main", entrypoint.package.name);
}
function isBrowserFieldValid(entrypoint) {
return equal(entrypoint.browser, getValidObjectFieldContentForBuildType("browser", entrypoint.package.name, null !== entrypoint.module));
}
function isUmdNameSpecified(entrypoint) {
return "string" == typeof entrypoint._config.umdName;
}
function validateEntrypoint(entrypoint, log) {
if (validateEntrypointSource(entrypoint), log && info(infos.validEntrypoint, entrypoint.name),
!isMainFieldValid(entrypoint)) throw new FixableError(errors.invalidMainField, entrypoint.name);
if (log && info(infos.validMainField, entrypoint.name), null !== entrypoint.module) {
if (!isModuleFieldValid(entrypoint)) throw new FixableError(errors.invalidModuleField, entrypoint.name);
log && info(infos.validModuleField, entrypoint.name);
}
if (null !== entrypoint.umdMain) {
if (!isUmdMainFieldValid(entrypoint)) throw new FixableError(errors.invalidUmdMainField, entrypoint.name);
if (!isUmdNameSpecified(entrypoint)) throw new FixableError(errors.umdNameNotSpecified, entrypoint.name);
log && info(infos.validUmdMainField, entrypoint.name);
}
if (null !== entrypoint.browser) {
if ("string" == typeof entrypoint.browser || !isBrowserFieldValid(entrypoint)) throw new FixableError(errors.invalidBrowserField, entrypoint.name);
log && info(infos.validBrowserField, entrypoint.name);
}
}
async function validate(directory) {
let project = await Project.create(directory);
for (let pkg of project.packages) {
validatePackage(pkg);
for (let entrypoint of pkg.entrypoints) validateEntrypoint(entrypoint, !0);
info(infos.validPackageEntrypoints, pkg.name);
}
success(successes.validProject);
}
let fields = [ "version", "description", "main", "module", "umd:main", "browser" ];
function setFieldInOrder(obj, field, value) {
if (field in obj) return _objectSpread({}, obj, {
[field]: value
});
let fieldIndex = fields.indexOf(field), idealField = fields.slice(0, fieldIndex).reverse().find(key => key in obj);
if (void 0 === idealField) return _objectSpread({}, obj, {
[field]: value
});
let newObj = {};
for (let key in obj) newObj[key] = obj[key], key === idealField && (newObj[field] = value);
return newObj;
}
class Entrypoint extends Item {
constructor(filePath, contents, pkg) {
super(filePath, contents), _defineProperty(this, "package", void 0), _defineProperty(this, "_strict", void 0),
this.package = pkg;
}
get name() {
return path.join(this.package.name, path.relative(this.package.directory, this.directory));
}
get main() {
return is(this.json.main, is.maybe(is.string));
}
set main(path) {
this.json = setFieldInOrder(this.json, "main", path);
}
get module() {
return is(this.json.module, is.maybe(is.string));
}
set module(path) {
this.json = setFieldInOrder(this.json, "module", path);
}
get browser() {
return is(this.json.browser, is.maybe(is.either(is.string, is.objectOf(is.string))));
}
set browser(option) {
this.json = setFieldInOrder(this.json, "browser", option);
}
get umdMain() {
return is(this.json["umd:main"], is.maybe(is.string));
}
set umdMain(path) {
this.json = setFieldInOrder(this.json, "umd:main", path);
}
get configSource() {
return is(this._config.source, is.default(is.string, "src/index"));
}
get source() {
return resolve.sync(path.join(this.directory, this.configSource), {
extensions: EXTENSIONS
});
}
get umdName() {
return is(this._config.umdName, is.maybe(is.string));
}
set umdName(umdName) {
null === umdName ? delete this._config.umdName : this._config.umdName = umdName;
}
strict() {
return this._strict || (validatePackage(this.package), validateEntrypoint(this, !1),
this._strict = new StrictEntrypoint(this.path, this._contents, this.package)), this._strict;
}
}
class StrictEntrypoint extends Entrypoint {
get main() {
return is(this.json.main, is.string);
}
set main(path) {
this.json = setFieldInOrder(this.json, "main", path);
}
get browser() {
return is(this.json.browser, is.maybe(is.objectOf(is.string)));
}
set browser(option) {
this.json = setFieldInOrder(this.json, "browser", option);
}
updater(json) {
super.updater(json), validatePackage(this.package), validateEntrypoint(this, !1);
}
strict() {
return this;
}
}
class Package extends Item {
constructor(...args) {
super(...args), _defineProperty(this, "project", void 0), _defineProperty(this, "entrypoints", void 0);
}
get configEntrypoints() {
return is(this._config.entrypoints, is.default(is.arrayOf(is.string), [ "." ]));
}
static async create(directory) {
let filePath = path.join(directory, "package.json"), contents = await fs.readFile(filePath, "utf-8"), pkg = new Package(filePath, contents), entrypointDirectories = await globby(pkg.configEntrypoints, {
cwd: pkg.directory,
onlyDirectories: !0,
absolute: !0,
expandDirectories: !1
});
return pkg.entrypoints = await Promise.all(entrypointDirectories.map(async directory => {
let filename = path.join(directory, "package.json"), contents = null;
try {
contents = await fs.readFile(filename, "utf-8");
} catch (e) {
if ("ENOENT" !== e.code) throw e;
}
return {
filename: filename,
contents: contents
};
})).then(descriptors => {
let getPlainEntrypointContent = () => {
let plainEntrypointObj = {
main: getValidStringFieldContentForBuildType("main", pkg.name)
};
for (let descriptor of descriptors) if (null !== descriptor.contents) {
let parsed = JSON.parse(descriptor.contents);
for (let field of [ "module", "umd:main" ]) void 0 !== parsed[field] && (plainEntrypointObj[field] = getValidStringFieldContentForBuildType(field, pkg.name));
void 0 !== parsed.browser && (plainEntrypointObj.browser = getValidObjectFieldContentForBuildType("browser", pkg.name, void 0 !== plainEntrypointObj.module));
}
let plainEntrypointContents = JSON.stringify(plainEntrypointObj);
return getPlainEntrypointContent = (() => plainEntrypointContents), plainEntrypointContents;
};
return Promise.all(descriptors.map(async ({filename: filename, contents: contents}) => {
if (null === contents) {
if (!await confirms.createEntrypointPkgJson({
name: path.join(pkg.name, path.relative(pkg.directory, directory))
})) throw new FatalError(errors.noEntrypointPkgJson, path.join(pkg.name, path.relative(pkg.directory, directory)));
contents = getPlainEntrypointContent(), await fs.writeFile(filename, contents);
}
return new Entrypoint(filename, contents, pkg);
}));
}), pkg;
}
setFieldOnEntrypoints(field) {
this.entrypoints.forEach(entrypoint => {
switch (field) {
case "main":
entrypoint.main = getValidStringFieldContentForBuildType("main", this.name);
break;
case "module":
entrypoint.module = getValidStringFieldContentForBuildType("module", this.name);
break;
case "browser":
entrypoint.browser = getValidObjectFieldContentForBuildType("browser", this.name, null !== entrypoint.module);
break;
case "umdMain":
entrypoint.umdMain = getValidStringFieldContentForBuildType("umd:main", this.name);
}
});
}
get name() {
return is(this.json.name, is.string);
}
set name(name) {
this.json.name = name;
}
get dependencies() {
return is(this.json.dependencies, is.maybe(is.objectOf(is.string)));
}
get peerDependencies() {
return is(this.json.peerDependencies, is.maybe(is.objectOf(is.string)));
}
}
async function doInit(pkg) {
if (pkg.entrypoints.forEach(entrypoint => {
validateEntrypointSource(entrypoint);
}), pkg.entrypoints.every(entrypoint => isMainFieldValid(entrypoint))) info(infos.validMainField, pkg.name); else {
if (!await confirms.writeMainField(pkg)) throw new FatalError(errors.deniedWriteMainField, pkg.name);
pkg.setFieldOnEntrypoints("main");
}
let allEntrypointsAreMissingAModuleField = pkg.entrypoints.every(entrypoint => null === entrypoint.module), someEntrypointsAreNotValid = pkg.entrypoints.some(entrypoint => !isModuleFieldValid(entrypoint));
if (allEntrypointsAreMissingAModuleField || someEntrypointsAreNotValid) {
if (await confirms.writeModuleField(pkg)) pkg.setFieldOnEntrypoints("module"); else if (!allEntrypointsAreMissingAModuleField) throw new FixableError(errors.invalidModuleField, pkg.name);
} else info(infos.validModuleField, pkg.name);
let someEntrypointsHaveAMaybeInvalidUmdBuild = pkg.entrypoints.some(entrypoint => null !== entrypoint.umdMain), someUmdMainFieldsAreInvalid = pkg.entrypoints.some(entrypoint => !isUmdMainFieldValid(entrypoint)), someUmdNamesAreNotSpecified = pkg.entrypoints.some(entrypoint => !isUmdNameSpecified(entrypoint));
if (someEntrypointsHaveAMaybeInvalidUmdBuild && (someUmdMainFieldsAreInvalid || someUmdNamesAreNotSpecified)) {
if (!await confirms.fixUmdBuild(pkg)) throw new FixableError(errors.invalidUmdMainField, pkg.name);
pkg.setFieldOnEntrypoints("umdMain");
for (let entrypoint of pkg.entrypoints) {
let umdName = await promptInput(inputs.getUmdName, entrypoint);
entrypoint.umdName = umdName;
}
}
let someEntrypointsHaveABrowserField = pkg.entrypoints.some(entrypoint => null !== entrypoint.browser), someEntrypointsHaveAnInvalidBrowserField = pkg.entrypoints.some(entrypoint => !isBrowserFieldValid(entrypoint));
if (someEntrypointsHaveABrowserField && someEntrypointsHaveAnInvalidBrowserField) {
if (!await confirms.fixBrowserField(pkg)) throw new FixableError(errors.invalidBrowserField, pkg.name);
pkg.setFieldOnEntrypoints("browser");
}
await Promise.all(pkg.entrypoints.map(x => x.save()));
}
async function init(directory) {
let project = await Project.create(directory);
await Promise.all(project.packages.map(doInit)), success("initialised project!");
}
const pattern = /require\((["'])@babel\/runtime\/helpers\/esm\/(\w+)["']\)/g;
function rewriteCjsRuntimeHelpers() {
return {
name: "rewrite-cjs-runtime-helpers",
renderChunk: (code, chunkInfo, {format: format}) => "cjs" !== format ? null : code.replace(pattern, (_, quote, path) => `require(${quote}@babel/runtime/helpers/${path}${quote})`)
};
}
function getDevPath(cjsPath) {
return cjsPath.replace(/\.js$/, ".dev.js");
}
function getProdPath(cjsPath) {
return cjsPath.replace(/\.js$/, ".prod.js");
}
function flowAndNodeDevProdEntry(pkg) {
return {
name: "flow-and-prod-dev-entry",
async resolveId(source, importer) {
let resolved = await this.resolve(source, importer, {
skipSelf: !0
});
if (resolved.id.startsWith("\0") || resolved.id.startsWith(pkg.directory)) return resolved;
throw new FatalError(`all relative imports in a package should only import modules inside of their package directory but "${path.relative(pkg.directory, importer)}" is importing "${source}"`, pkg.name);
},
async generateBundle(opts, bundle, something) {
for (const n in bundle) {
const file = bundle[n];
let facadeModuleId = file.facadeModuleId;
if (file.isAsset || !file.isEntry || null == facadeModuleId) continue;
let mainFieldPath = file.fileName.replace(/\.prod\.js$/, ".js"), relativeToSource = path.relative(path.dirname(path.join(opts.dir, file.fileName)), facadeModuleId);
if (!/\.tsx?$/.test(facadeModuleId)) {
let flowMode = !1;
if ((await fs.readFile(facadeModuleId, "utf8")).includes("@flow") && (flowMode = file.exports.includes("default") ? "all" : "named"),
!1 !== flowMode) {
let flowFileSource = flowTemplate("all" === flowMode, relativeToSource), flowFileName = mainFieldPath + ".flow";
bundle[flowFileName] = {
fileName: flowFileName,
isAsset: !0,
source: flowFileSource
};
}
}
let mainEntrySource = `'use strict';\n\nif (process.env.NODE_ENV === "production") {\n module.exports = require("./${path.basename(getProdPath(mainFieldPath))}");\n} else {\n module.exports = require("./${path.basename(getDevPath(mainFieldPath))}");\n}\n`;
bundle[mainFieldPath] = {
fileName: mainFieldPath,
isAsset: !0,
source: mainEntrySource
};
}
}
};
}
function normalize(fileName) {
return fileName.split("\\").join("/");
}
let createLanguageServiceHostClass = typescript => (class {
constructor(parsedConfig, transformers) {
this.parsedConfig = parsedConfig, this.transformers = transformers, this.cwd = process.cwd(),
this.snapshots = {}, this.versions = {}, this.fileNames = new Set(parsedConfig.fileNames);
}
reset() {
this.snapshots = {}, this.versions = {};
}
setLanguageService(service) {
this.service = service;
}
getProjectVersion() {
return 1;
}
setSnapshot(fileName, data) {
fileName = normalize(fileName);
const snapshot = typescript.ScriptSnapshot.fromString(data);
return this.snapshots[fileName] = snapshot, this.versions[fileName] = (this.versions[fileName] || 0) + 1,
this.fileNames.add(fileName), snapshot;
}
getScriptSnapshot(fileName) {
return fileName = normalize(fileName), this.snapshots[fileName] ? this.snapshots[fileName] : fs$1.existsSync(fileName) ? (this.snapshots[fileName] = typescript.ScriptSnapshot.fromString(typescript.sys.readFile(fileName)),
this.versions[fileName] = (this.versions[fileName] || 0) + 1, this.snapshots[fileName]) : void 0;
}
getCurrentDirectory() {
return this.cwd;
}
getScriptVersion(fileName) {
return fileName = normalize(fileName), (this.versions[fileName] || 0).toString();
}
getScriptFileNames() {
return Array.from(this.fileNames.values());
}
getCompilationSettings() {
return this.parsedConfig.options;
}
getDefaultLibFileName(opts) {
return typescript.getDefaultLibFilePath(opts);
}
useCaseSensitiveFileNames() {
return typescript.sys.useCaseSensitiveFileNames;
}
readDirectory(path, extensions, exclude, include) {
return typescript.sys.readDirectory(path, extensions, exclude, include);
}
readFile(path, encoding) {
return typescript.sys.readFile(path, encoding);
}
fileExists(path) {
return typescript.sys.fileExists(path);
}
getTypeRootsVersion() {
return 0;
}
directoryExists(directoryName) {
return typescript.sys.directoryExists(directoryName);
}
getDirectories(directoryName) {
return typescript.sys.getDirectories(directoryName);
}
getCustomTransformers() {}
}), unsafeRequire$2 = require, weakMemoize = function(func) {
let cache = new WeakMap();
return arg => {
if (cache.has(arg)) return cache.get(arg);
let ret = func(arg);
return cache.set(arg, ret), ret;
};
};
function memoize(fn) {
const cache = {};
return arg => (void 0 === cache[arg] && (cache[arg] = fn(arg)), cache[arg]);
}
let getService = weakMemoize(typescript => memoize(async configFileName => {
let configFileContents = await fs.readFile(configFileName, "utf8");
const result = typescript.parseConfigFileTextToJson(configFileName, configFileContents);
let thing = typescript.parseJsonConfigFileContent(result, typescript.sys, process.cwd(), void 0, configFileName), servicesHost = new (createLanguageServiceHostClass(typescript))(thing, []), service = typescript.createLanguageService(servicesHost, typescript.createDocumentRegistry());
return servicesHost.setLanguageService(service), {
service: service,
options: thing.options
};
}));
async function createDeclarationCreator(dirname) {
let typescript;
try {
typescript = unsafeRequire$2(resolveFrom(dirname, "typescript"));
} catch (err) {
if ("MODULE_NOT_FOUND" === err.code) throw new Error("an entrypoint source file ends with the .ts or .tsx extension but the typescript module could not be resolved from the project directory, please install it.");
throw err;
}
let configFileName = typescript.findConfigFile(dirname, typescript.sys.fileExists);
if (!configFileName) throw new Error("an entrypoint source file ends with the .ts extension but no TypeScript config exists, please create one.");
let {service: service, options: options} = await getService(typescript)(configFileName), moduleResolutionCache = typescript.createModuleResolutionCache(dirname, x => x, options);
return {
getDeps: entrypoints => {
let program = service.getProgram();
if (!program) throw new Error("This is an internal error, please open an issue if you see this: program not found");
let resolvedEntrypointPaths = entrypoints.map(x => {
let {resolvedModule: resolvedModule} = typescript.resolveModuleName(path.join(path.dirname(x), path.basename(x, path.extname(x))), dirname, options, typescript.sys, moduleResolutionCache);
if (!resolvedModule) throw new Error("This is an internal error, please open an issue if you see this: ts could not resolve module");
return resolvedModule.resolvedFileName;
}), allDeps = new Set(resolvedEntrypointPaths);
return function searchDeps(deps) {
for (let dep of deps) {
let sourceFile = program.getSourceFile(dep);
if (!sourceFile) throw new Error("This is an internal error, please open an issue if you see this: source file not found");
let internalDeps = new Set();
for (let _ref of sourceFile.imports) {
let {text: text} = _ref, {resolvedModule: resolvedModule} = typescript.resolveModuleName(text, dep, options, typescript.sys, moduleResolutionCache);
resolvedModule && (allDeps.has(resolvedModule.resolvedFileName) || resolvedModule.isExternalLibraryImport || !resolvedModule.resolvedFileName.includes(dirname) || (internalDeps.add(resolvedModule.resolvedFileName),
allDeps.add(resolvedModule.resolvedFileName)));
}
searchDeps(internalDeps);
}
}(new Set(resolvedEntrypointPaths)), allDeps;
},
getDeclarationFile: async filename => {
if (filename.endsWith(".d.ts")) return {
name: filename.replace(dirname, path.join(dirname, "dist", "declarations")),
content: await fs.readFile(filename, "utf8")
};
let output = service.getEmitOutput(filename, !0);
return {
name: output.outputFiles[0].name.replace(dirname, path.join(dirname, "dist", "declarations")),
content: output.outputFiles[0].text
};
}
};
}
let isTsPath = source => /\.tsx?/.test(source);
function typescriptDeclarations(pkg) {
return pkg.entrypoints.some(({source: source}) => isTsPath(source)) ? {
name: "typescript-declarations",
async generateBundle(opts, bundle, something) {
let creator = await createDeclarationCreator(pkg.directory), srcFilenameToDtsFilenameMap = new Map(), deps = creator.getDeps(pkg.entrypoints.map(x => x.source));
await Promise.all([ ...deps ].map(async dep => {
let {name: name, content: content} = await creator.getDeclarationFile(dep);
srcFilenameToDtsFilenameMap.set(dep, name), bundle[name] = {
fileName: name,
isAsset: !0,
source: content
};
}));
for (const n in bundle) {
const file = bundle[n];
let facadeModuleId = file.facadeModuleId;
if (file.isAsset || !file.isEntry || null == facadeModuleId) continue;
let dtsFilename = srcFilenameToDtsFilenameMap.get(facadeModuleId);
if (!dtsFilename) throw new FatalError(`no .d.ts file was found for the entrypoint at ${facadeModuleId}`, pkg.name);
let mainFieldPath = file.fileName.replace(/\.prod\.js$/, ""), relativeToSource = path.relative(path.dirname(path.join(opts.dir, file.fileName)), dtsFilename.replace(/\.d\.ts$/, ""));
relativeToSource.startsWith(".") || (relativeToSource = `./${relativeToSource}`);
let tsFileSource = tsTemplate(file.exports.includes("default"), relativeToSource), tsFileName = mainFieldPath + ".d.ts";
bundle[tsFileName] = {
fileName: tsFileName,
isAsset: !0,
source: tsFileSource
};
}
}
} : {
name: "typescript-declarations"
};
}
let worker, shouldUseWorker = "true" !== process.env.DISABLE_PRECONSTRUCT_WORKER, unsafeRequire$3 = require;
function createWorker() {
worker = shouldUseWorker ? new Worker(require.resolve("preconstruct/worker")) : unsafeRequire$3("preconstruct/worker");
}
function destroyWorker() {
void 0 !== worker && shouldUseWorker && (worker.end(), worker = void 0);
}
function getWorker() {
if (void 0 === worker) throw new Error("worker not defined");
return worker;
}
const regExpCharactersRegExp = /[\\^$.*+?()[\]{}|]/g, escapeRegExpCharacters = str => str.replace(regExpCharactersRegExp, "\\$&"), unpackOptions = (_ref = {}) => {
let {extensions: extensions = babel.DEFAULT_EXTENSIONS, sourcemap: sourcemap = !0, sourcemaps: sourcemaps = !0, sourceMap: sourceMap = !0, sourceMaps: sourceMaps = !0} = _ref, rest = _objectWithoutProperties(_ref, [ "extensions", "sourcemap", "sourcemaps", "sourceMap", "sourceMaps" ]);
return _objectSpread({
extensions: extensions,
plugins: [],
sourceMaps: sourcemap && sourcemaps && sourceMap && sourceMaps
}, rest, {
caller: _objectSpread({
name: "rollup-plugin-babel",
supportsStaticESM: !0,
supportsDynamicImport: !0
}, rest.caller)
});
}, lru = new QuickLRU({
maxSize: 1e3
});
let hasher, hasherPromise = initHasher().then(({h64: h64}) => {
hasher = h64;
}), rollupPluginBabel = pluginOptions => {
const _unpackOptions = unpackOptions(pluginOptions), {exclude: exclude, extensions: extensions, include: include} = _unpackOptions, babelOptions = _objectWithoutProperties(_unpackOptions, [ "exclude", "extensions", "include" ]), extensionRegExp = new RegExp(`(${extensions.map(escapeRegExpCharacters).join("|")})$`), includeExcludeFilter = rollupPluginutils.createFilter(include, exclude);
return {
name: "babel",
transform(code, filename) {
if (!(id => extensionRegExp.test(id) && includeExcludeFilter(id))(filename)) return Promise.resolve(null);
let hash = hasher(filename);
if (lru.has(hash)) {
let cachedResult = lru.get(hash);
if (code === cachedResult.code) return cachedResult.promise;
}
let options = JSON.stringify(_objectSpread({}, babelOptions, {
filename: filename
})), promise = getWorker().transformBabel(code, options);
return lru.set(hash, {
code: code,
promise: promise
}), promise;
}
};
};
function terser(userOptions = {}) {
if (null != userOptions.sourceMap) throw Error("sourceMap option is removed, use sourcemap instead");
return {
name: "terser",
renderChunk(code, chunk, outputOptions) {
const normalizedOptions = _objectSpread({}, userOptions, {
sourceMap: !1 !== userOptions.sourcemap,
module: "es" === outputOptions.format || "esm" === outputOptions.format
});
return normalizedOptions.hasOwnProperty("sourcemap") && delete normalizedOptions.sourcemap,
getWorker().transformTerser(code, JSON.stringify(normalizedOptions)).catch(error => {
const {message: message, line: line, col: column} = error;
throw console.error(codeFrame.codeFrameColumns(code, {
start: {
line: line,
column: column
}
}, {
message: message
})), error;
});
}
};
}
const makeExternalPredicate = externalArr => {
if (0 === externalArr.length) return () => !1;
const pattern = new RegExp(`^(${externalArr.join("|")})($|/)`);
return id => pattern.test(id);
};
let rollup = rollup$1.rollup;
function toUnsafeRollupConfig(config) {
return config;
}
let getRollupConfig = (pkg, entrypoints, aliases, type) => {
let external = [];
pkg.peerDependencies && external.push(...Object.keys(pkg.peerDependencies)), pkg.dependencies && "umd" !== type && external.push(...Object.keys(pkg.dependencies)),
"node-dev" !== type && "node-prod" !== type || external.push(...builtInModules);
let rollupAliases = {};
Object.keys(aliases).forEach(key => {
try {
rollupAliases[key] = resolveFrom(pkg.directory, aliases[key]);
} catch (err) {
if ("MODULE_NOT_FOUND" !== err.code) throw err;
}
});
let input = {};
return entrypoints.forEach(entrypoint => {
input[path.relative(pkg.directory, path.join(entrypoint.directory, "dist", getNameForDist(pkg.name)))] = entrypoint.strict().source;
}), {
input: input,
external: makeExternalPredicate(external),
onwarn: warning => {
switch (warning.code) {
case "EMPTY_BUNDLE":
case "CIRCULAR_DEPENDENCY":
case "UNUSED_EXTERNAL_IMPORT":
break;
case "UNRESOLVED_IMPORT":
if (/^@babel\/runtime\/helpers\//.test(warning.source)) throw (async () => {
if (!await confirms.shouldInstallBabelRuntime(pkg)) throw new FatalError(`@babel/runtime should be in dependencies of ${pkg.name}`, pkg.name);
await limit(() => installPackages({
packages: [ "@babel/runtime" ],
cwd: pkg.directory,
installPeers: !1,
packageManager: pkg.project.isBolt ? "bolt" : void 0
})), await pkg.refresh();
})();
if (!warning.source.startsWith(".")) throw new FatalError(`"${warning.source}" is imported by "${path.relative(pkg.directory, warning.importer)}" but it is not specified in dependencies or peerDependencies`, pkg.name);
default:
throw new FatalError(`There was an error compiling ${pkg.name}: ${chalk.red(warning.toString())}`, pkg.name);
}
},
plugins: [ "node-prod" === type && flowAndNodeDevProdEntry(pkg), "node-prod" === type && typescriptDeclarations(pkg), rollupPluginBabel({
cwd: pkg.project.directory,
plugins: [ [ require.resolve("@babel/plugin-transform-runtime"), {
useESModules: !0
} ] ],
extensions: EXTENSIONS
}), "umd" === type && cjs({
include: [ "**/node_modules/**", "node_modules/**" ]
}), ("browser" === type || "umd" === type) && replace({
"typeof document": JSON.stringify("object"),
"typeof window": JSON.stringify("object")
}), rewriteCjsRuntimeHelpers(), json({
namedExports: !1
}), "umd" === type && alias(rollupAliases), resolve$1({
extensions: EXTENSIONS,
customResolveOptions: {
moduleDirectory: "umd" === type ? "node_modules" : []
}
}), ("umd" === type || "node-prod" === type) && replace({
"process.env.NODE_ENV": '"production"'
}), "umd" === type && terser(), "node-prod" === type && terser({
mangle: !1,
output: {
beautify: !0,
indent_level: 2
}
}) ].filter(Boolean)
};
};
function getAliases(project, getKey = (x => x)) {
let aliases = {};
return project.packages.forEach(pkg => {
pkg.entrypoints.map(x => x.strict()).forEach(entrypoint => {
aliases[getKey(entrypoint.name)] = path.join(pkg.name, path.relative(entrypoint.directory, entrypoint.source));
});
}), aliases;
}
function getGlobals(pkg) {
let stuff = [];
return pkg.peerDependencies && stuff.push(...Object.keys(pkg.peerDependencies)),
pkg.dependencies && stuff.push(...Object.keys(pkg.dependencies)), 0 === stuff.length ? {} : (pkg.peerDependencies ? Object.keys(pkg.peerDependencies) : []).reduce((obj, pkgName) => (obj[pkgName] = pkg.project.global(pkgName),
obj), {});
}
function getRollupConfigs(pkg, aliases) {
let configs = [], strictEntrypoints = pkg.entrypoints.map(x => x.strict()), hasModuleField = null !== strictEntrypoints[0].module;
return configs.push({
config: getRollupConfig(pkg, strictEntrypoints, aliases, "node-dev"),
outputs: [ {
format: "cjs",
entryFileNames: "[name].cjs.dev.js",
chunkFileNames: "dist/[name]-[hash].cjs.dev.js",
dir: pkg.directory,
exports: "named"
}, ...hasModuleField ? [ {
format: "es",
entryFileNames: "[name].esm.js",
chunkFileNames: "dist/[name]-[hash].esm.js",
dir: pkg.directory
} ] : [] ]
}), configs.push({
config: getRollupConfig(pkg, strictEntrypoints, aliases, "node-prod"),
outputs: [ {
format: "cjs",
entryFileNames: "[name].cjs.prod.js",
chunkFileNames: "dist/[name]-[hash].cjs.prod.js",
dir: pkg.directory,
exports: "named"
} ]
}), null !== strictEntrypoints[0].umdMain && pkg.entrypoints.map(x => x.strict()).forEach(entrypoint => {
let umdName = is(entrypoint._config.umdName, is.string);
is(entrypoint.umdMain, is.string), configs.push({
config: getRollupConfig(pkg, [ entrypoint ], aliases, "umd"),
outputs: [ {
format: "umd",
sourcemap: !0,
entryFileNames: "[name].umd.min.js",
name: umdName,
dir: pkg.directory,
globals: getGlobals(pkg)
} ]
});
}), null !== strictEntrypoints[0].browser && configs.push({
config: getRollupConfig(pkg, strictEntrypoints, aliases, "browser"),
outputs: [ {
format: "cjs",
entryFileNames: "[name].browser.cjs.js",
chunkFileNames: "dist/[name]-[hash].browser.cjs.js",
dir: pkg.directory,
exports: "named"
}, ...hasModuleField ? [ {
format: "es",
entryFileNames: "[name].browser.esm.js",
chunkFileNames: "dist/[name]-[hash].browser.esm.js",
dir: pkg.directory
} ] : [] ]
}), configs;
}
let browserPattern = /typeof\s+(window|document)/;
async function buildPackage(pkg, aliases) {
let configs = getRollupConfigs(pkg, aliases);
await Promise.all([ fs.remove(path.join(pkg.directory, "dist")), ...pkg.entrypoints.map(entrypoint => fs.remove(path.join(entrypoint.directory, "dist"))) ]);
let hasCheckedBrowser = null !== pkg.entrypoints[0].browser;
await Promise.all(configs.map(async ({config: config, outputs: outputs}) => {
let bundle = await rollup(config);
const nodeDevOutput = (await Promise.all(outputs.map(outputConfig => bundle.write(outputConfig))))[0].output;
if (!hasCheckedBrowser) {
let allCode = nodeDevOutput.map(({code: code}) => code).join("\n");
if (hasCheckedBrowser = !0, browserPattern.test(allCode)) throw (async () => {
if (!await confirms.addBrowserField(pkg)) throw new FatalError(errors.deniedWriteBrowserField, pkg.name);
pkg.setFieldOnEntrypoints("browser"), await Promise.all(pkg.entrypoints.map(x => x.save()));
})();
}
}));
}
async function retryableBuild(pkg, aliases) {
try {
await buildPackage(pkg, aliases);
} catch (err) {
if (err instanceof Promise) return await err, void await retryableBuild(pkg, aliases);
if (err instanceof FatalError) throw err;
throw new UnexpectedBuildError(err, pkg.name);
}
}
async function build(directory) {
try {
createWorker(), await hasherPromise;
let project = await Project.create(directory);
info("building bundles!");
let aliases = getAliases(project);
await Promise.all(project.packages.map(pkg => retryableBuild(pkg, aliases))), success("built bundles!");
} finally {
destroyWorker();
}
}
function relativePath(id) {
return path.relative(process.cwd(), id);
}
async function watchPackage(pkg, aliases) {
const _configs = getRollupConfigs(pkg, aliases);
await fs.remove(path.join(pkg.directory, "dist"));
let configs = _configs.map(config => _objectSpread({}, toUnsafeRollupConfig(config.config), {
output: config.outputs