preconstruct
Version:
A smart build tool for libraries
1,641 lines (1,359 loc) • 68 kB
JavaScript
'use strict';
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var meow = _interopDefault(require('meow'));
var _defineProperty = _interopDefault(require('@babel/runtime/helpers/defineProperty'));
var is = _interopDefault(require('sarcastic'));
var globby = _interopDefault(require('globby'));
var fs = require('fs-extra');
var path = _interopDefault(require('path'));
var util = _interopDefault(require('util'));
var _objectSpread = _interopDefault(require('@babel/runtime/helpers/objectSpread'));
var inquirer = _interopDefault(require('inquirer'));
var pLimit = _interopDefault(require('p-limit'));
var DataLoader = _interopDefault(require('dataloader'));
var chalk = _interopDefault(require('chalk'));
var resolveFrom = _interopDefault(require('resolve-from'));
var packlist = _interopDefault(require('npm-packlist'));
var equal = _interopDefault(require('fast-deep-equal'));
var resolve = _interopDefault(require('resolve'));
var resolve$1 = _interopDefault(require('rollup-plugin-node-resolve'));
var alias = _interopDefault(require('rollup-plugin-alias'));
var cjs = _interopDefault(require('rollup-plugin-commonjs'));
var replace = _interopDefault(require('rollup-plugin-replace'));
var builtInModules = _interopDefault(require('builtin-modules'));
var rollup$1 = require('rollup');
var fs$1 = require('fs');
var json = _interopDefault(require('rollup-plugin-json'));
var _objectWithoutProperties = _interopDefault(require('@babel/runtime/helpers/objectWithoutProperties'));
var babel = require('@babel/core');
var rollupPluginutils = require('rollup-pluginutils');
var Worker = _interopDefault(require('jest-worker'));
var initHasher = _interopDefault(require('xxhash-wasm'));
var QuickLRU = _interopDefault(require('quick-lru'));
var codeFrame = require('@babel/code-frame');
var installPackages = _interopDefault(require('install-packages'));
var ms = _interopDefault(require('ms'));
let EXTENSIONS = [".js", ".jsx", ".ts", ".tsx"];
let PKG_JSON_CONFIG_FIELD = "preconstruct";
let 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] || {};
if (itemsByPath[this.path] === undefined) {
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");
let json = is(JSON.parse(contents), is.object);
for (let item of itemsByPath[this.path]) {
item.updater(json);
}
}
async save() {
if (Object.keys(this._config).length) {
this.json[PKG_JSON_CONFIG_FIELD] = this._config;
} else {
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);
}
this._stringifiedSavedJson = stringified;
return true;
}
return false;
}
}
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); // there might be a simpler solution to this than using dataloader but it works so ¯\_(ツ)_/¯
let prefix = `🎁 ${chalk.green("?")}`;
function createPromptConfirmLoader(message) {
let loader = new DataLoader(pkgs => limit(() => (async () => {
if (pkgs.length === 1) {
let {
confirm
} = await inquirer.prompt([{
type: "confirm",
name: "confirm",
message,
prefix: prefix + " " + pkgs[0].name
}]);
return [confirm];
}
let {
answers
} = await inquirer.prompt([{
type: "checkbox",
name: "answers",
message,
choices: pkgs.map(pkg => ({
name: pkg.name,
checked: true
})),
prefix
}]);
return pkgs.map(pkg => {
return answers.includes(pkg.name);
});
})(), {
cache: false
}));
return pkg => loader.load(pkg);
}
let promptConfirm = async message => {
let {
confirm
} = await inquirer.prompt([{
type: "confirm",
name: "confirm",
message,
prefix: prefix
}]);
return confirm;
};
let doPromptInput = async (message, pkg, defaultAnswer) => {
let {
input
} = await inquirer.prompt([{
type: "input",
name: "input",
message,
prefix: prefix + " " + pkg.name,
default: defaultAnswer
}]);
return input;
};
let promptInput = (message, pkg, defaultAnswer) => limit(() => doPromptInput(message, pkg, defaultAnswer));
function format(args, messageType, scope) {
let prefix = {
error: chalk.red("error"),
success: chalk.green("success"),
info: chalk.cyan("info")
}[messageType];
let fullPrefix = "🎁 " + prefix + (scope === undefined ? "" : " " + 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");
await fs.ensureFile(filename);
return fs.writeFile(filename, "test content");
}));
let result = await packlist({
path: pkg.directory
}); // check that we're including the package.json and main file
// TODO: add Flow and TS check and if they're ignored, don't write them
let messages = [];
pkg.entrypoints.forEach(entrypoint => {
let pkgJsonPath = path.relative(pkg.directory, path.resolve(entrypoint.directory, "package.json"));
let distFilePath = path.relative(pkg.directory, path.resolve(entrypoint.directory, "dist", "preconstruct-test-file"));
let entrypointName = path.relative(pkg.directory, entrypoint.directory);
if (!result.includes(pkgJsonPath)) {
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`);
} else if (!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`);
}
});
if (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;
let 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), ["."]));
} // probably gonna be irrelevant later but i want it for now
get isBolt() {
// we only want to return true when there is bolt config
// AND no yarn workspaces config
// because emotion has a bolt config and yarn workspaces
// and if you have both, you probably want workspaces
let hasBolt = !!this.json.bolt;
let hasYarnWorkspaces = !!this.json.workspaces;
return hasBolt && !hasYarnWorkspaces;
}
static async create(directory) {
let filePath = path.join(directory, "package.json");
let contents = await fs.readFile(filePath, "utf-8");
let project = new Project(filePath, contents);
project.packages = await project._packages();
return project;
}
get name() {
return is(this.json.name, is.string);
}
set name(name) {
this.json.name = name;
}
async _packages() {
// suport bolt later probably
// maybe lerna too though probably not
if (!this._config.packages && this.json.workspaces) {
let _workspaces;
if (Array.isArray(this.json.workspaces)) {
_workspaces = this.json.workspaces;
} else if (Array.isArray(this.json.workspaces.packages)) {
_workspaces = this.json.workspaces.packages;
}
let workspaces = is(_workspaces, is.arrayOf(is.string));
let packages = await promptInput("what packages should preconstruct build?", this, workspaces.join(","));
this._config.packages = packages.split(",");
await this.save();
}
try {
let filenames = await globby(this.configPackages, {
cwd: this.directory,
onlyDirectories: true,
absolute: true,
expandDirectories: false
});
let dirsWithoutPkgJson = [];
let lastErr;
let packages = await Promise.all(filenames.map(async x => {
try {
let pkg = await Package.create(x);
pkg.project = this;
return pkg;
} catch (err) {
if (err.code === "ENOENT" && err.path === path.join(x, "package.json")) {
lastErr = err;
dirsWithoutPkgJson.push(x);
return undefined;
}
throw err;
}
}));
if (dirsWithoutPkgJson.length) {
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");
if (!(await promptConfirm("would you like preconstruct to delete these directories automatically?"))) {
throw lastErr;
}
await Promise.all(dirsWithoutPkgJson.map(dir => fs.remove(dir)));
return this._packages();
}
await Promise.all(packages.map(pkg => validateIncludedFiles(pkg)));
return packages;
} catch (error) {
if (error instanceof is.AssertionError) {
return [];
}
throw error;
}
}
global(pkg) {
if (this._config.globals !== undefined && this._config.globals[pkg]) {
return this._config.globals[pkg];
} else {
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 (err.code !== "MODULE_NOT_FOUND") {
throw err;
}
}
throw askGlobalLimit(() => (async () => {
// if while we were waiting, that global was added, return
if (this._config.globals !== undefined && 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) {
if (!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"
};
let 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?")
};
let inputs = {
getUmdName: "what should the name used for UMD bundles be?"
};
let 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"
};
let 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`
};
if (hasModuleBuild) {
obj[`./dist/${safeName}.esm.js`] = `./dist/${safeName}.browser.esm.js`;
}
return obj;
}
}
throw new Error(`unknown object build type: ${type}. this is likely a bug in preconstruct.`);
}
function flowTemplate(hasDefaultExport, relativePath) {
return `// @flow
export * from "${relativePath}";${hasDefaultExport ? `\nexport { default } from "${relativePath}";` : ""}\n`;
}
function tsTemplate(hasDefaultExport, relativePath) {
return `export * from "${relativePath}";${hasDefaultExport ? `\nexport { default } from "${relativePath}";` : ""}\n`;
}
/*::
import { Package } from "./package";
*/
let camelToPkgJsonField = {
main: "main",
module: "module",
umdMain: "umd:main",
browser: "browser"
};
async function fixPackage(pkg) {
if (pkg.entrypoints.length === 0) {
throw new FatalError(errors.noEntrypoints, pkg.name);
}
let fields = {
main: true,
module: pkg.entrypoints.some(x => x.module),
umdMain: pkg.entrypoints.some(x => x.umdMain),
browser: pkg.entrypoints.some(x => x.browser)
};
Object.keys(fields).filter(x => fields[x]).forEach(field => {
pkg.setFieldOnEntrypoints(field);
});
return (await Promise.all(pkg.entrypoints.map(x => x.save()))).some(x => x);
}
let unsafeRequire$1 = require;
function validatePackage(pkg) {
if (pkg.entrypoints.length === 0) {
throw new FatalError(errors.noEntrypoints, pkg.name);
}
let fields = {
// main is intentionally not here, since it's always required
// it will be validated in validateEntrypoint and the case
// which this function validates will never happen
module: !!pkg.entrypoints[0].module,
umdMain: !!pkg.entrypoints[0].umdMain,
browser: !!pkg.entrypoints[0].browser
};
pkg.entrypoints.forEach(entrypoint => {
Object.keys(fields).forEach(field => {
if ( // $FlowFixMe
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 ( // $FlowFixMe
!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);
}
});
}); // TODO: do this well
if (fields.umdMain) {
// this is a sorta naive check
// but it's handling the most common case
// i don't think it's worth implementing this well at this exact moment
// because i'm guessing doing it well would cause more problems than it would solve
// this will likely change in the future
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);
}
}
}
}
}
}
// just does validation
// used in build and watch
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 (e.code === "MODULE_NOT_FOUND") {
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, entrypoint.module !== null));
}
function isUmdNameSpecified(entrypoint) {
return typeof entrypoint._config.umdName === "string";
}
function validateEntrypoint(entrypoint, log) {
validateEntrypointSource(entrypoint);
if (log) {
info(infos.validEntrypoint, entrypoint.name);
}
if (!isMainFieldValid(entrypoint)) {
throw new FixableError(errors.invalidMainField, entrypoint.name);
}
if (log) {
info(infos.validMainField, entrypoint.name);
}
if (entrypoint.module !== null) {
if (isModuleFieldValid(entrypoint)) {
if (log) {
info(infos.validModuleField, entrypoint.name);
}
} else {
throw new FixableError(errors.invalidModuleField, entrypoint.name);
}
}
if (entrypoint.umdMain !== null) {
if (isUmdMainFieldValid(entrypoint)) {
if (isUmdNameSpecified(entrypoint)) {
if (log) {
info(infos.validUmdMainField, entrypoint.name);
}
} else {
throw new FixableError(errors.umdNameNotSpecified, entrypoint.name);
}
} else {
throw new FixableError(errors.invalidUmdMainField, entrypoint.name);
}
}
if (entrypoint.browser !== null) {
if (typeof entrypoint.browser === "string" || !isBrowserFieldValid(entrypoint)) {
throw new FixableError(errors.invalidBrowserField, entrypoint.name);
} else if (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, true);
}
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);
let idealField = fields.slice(0, fieldIndex).reverse().find(key => {
return key in obj;
});
if (idealField === undefined) {
return _objectSpread({}, obj, {
[field]: value
});
}
let newObj = {};
for (let key in obj) {
newObj[key] = obj[key];
if (key === idealField) {
newObj[field] = value;
}
}
return newObj;
}
/*::
import { Package } from './package'
*/
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) {
if (umdName === null) {
delete this._config.umdName;
} else {
this._config.umdName = umdName;
}
}
strict() {
if (!this._strict) {
validatePackage(this.package);
validateEntrypoint(this, false);
this._strict = new StrictEntrypoint(this.path, this._contents, this.package);
}
return 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, false);
}
strict() {
return this;
}
}
/*::
import {Project} from './project'
*/
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");
let contents = await fs.readFile(filePath, "utf-8");
let pkg = new Package(filePath, contents);
let entrypointDirectories = await globby(pkg.configEntrypoints, {
cwd: pkg.directory,
onlyDirectories: true,
absolute: true,
expandDirectories: false
});
pkg.entrypoints = await Promise.all(entrypointDirectories.map(async directory => {
let filename = path.join(directory, "package.json");
let contents = null;
try {
contents = await fs.readFile(filename, "utf-8");
} catch (e) {
if (e.code !== "ENOENT") {
throw e;
}
}
return {
filename,
contents
};
})).then(descriptors => {
let getPlainEntrypointContent = () => {
let plainEntrypointObj = {
main: getValidStringFieldContentForBuildType("main", pkg.name)
};
for (let descriptor of descriptors) {
if (descriptor.contents !== null) {
let parsed = JSON.parse(descriptor.contents);
for (let field of ["module", "umd:main"]) {
if (parsed[field] !== undefined) {
plainEntrypointObj[field] = getValidStringFieldContentForBuildType(field, pkg.name);
}
}
if (parsed.browser !== undefined) {
plainEntrypointObj.browser = getValidObjectFieldContentForBuildType("browser", pkg.name, plainEntrypointObj.module !== undefined);
}
}
}
let plainEntrypointContents = JSON.stringify(plainEntrypointObj);
getPlainEntrypointContent = () => plainEntrypointContents;
return plainEntrypointContents;
};
return Promise.all(descriptors.map(async ({
filename,
contents
}) => {
if (contents === null) {
let shouldCreateEntrypointPkgJson = await confirms.createEntrypointPkgJson({
name: path.join(pkg.name, path.relative(pkg.directory, directory))
});
if (!shouldCreateEntrypointPkgJson) {
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);
}));
});
return 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, entrypoint.module !== null);
break;
}
case "umdMain":
{
entrypoint.umdMain = getValidStringFieldContentForBuildType("umd:main", this.name);
break;
}
}
});
}
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) {
pkg.entrypoints.forEach(entrypoint => {
validateEntrypointSource(entrypoint);
});
if (pkg.entrypoints.every(entrypoint => isMainFieldValid(entrypoint))) {
info(infos.validMainField, pkg.name);
} else {
let canWriteMainField = await confirms.writeMainField(pkg);
if (!canWriteMainField) {
throw new FatalError(errors.deniedWriteMainField, pkg.name);
}
pkg.setFieldOnEntrypoints("main");
}
let allEntrypointsAreMissingAModuleField = pkg.entrypoints.every(entrypoint => entrypoint.module === null);
let someEntrypointsAreNotValid = pkg.entrypoints.some(entrypoint => !isModuleFieldValid(entrypoint));
if (allEntrypointsAreMissingAModuleField || someEntrypointsAreNotValid) {
let canWriteModuleField = await confirms.writeModuleField(pkg);
if (canWriteModuleField) {
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 => entrypoint.umdMain !== null);
let someUmdMainFieldsAreInvalid = pkg.entrypoints.some(entrypoint => !isUmdMainFieldValid(entrypoint));
let someUmdNamesAreNotSpecified = pkg.entrypoints.some(entrypoint => !isUmdNameSpecified(entrypoint));
if (someEntrypointsHaveAMaybeInvalidUmdBuild && (someUmdMainFieldsAreInvalid || someUmdNamesAreNotSpecified)) {
let shouldWriteUMDBuilds = await confirms.fixUmdBuild(pkg);
if (shouldWriteUMDBuilds) {
pkg.setFieldOnEntrypoints("umdMain");
for (let entrypoint of pkg.entrypoints) {
let umdName = await promptInput(inputs.getUmdName, entrypoint);
entrypoint.umdName = umdName;
}
} else {
throw new FixableError(errors.invalidUmdMainField, pkg.name);
}
}
let someEntrypointsHaveABrowserField = pkg.entrypoints.some(entrypoint => entrypoint.browser !== null);
let someEntrypointsHaveAnInvalidBrowserField = pkg.entrypoints.some(entrypoint => !isBrowserFieldValid(entrypoint));
if (someEntrypointsHaveABrowserField && someEntrypointsHaveAnInvalidBrowserField) {
let shouldFixBrowserField = await confirms.fixBrowserField(pkg);
if (shouldFixBrowserField) {
pkg.setFieldOnEntrypoints("browser");
} else {
throw new FixableError(errors.invalidBrowserField, pkg.name);
}
}
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
}) {
if (format !== "cjs") {
return null;
}
return code.replace(pattern, (_, quote, path) => {
return `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: true
});
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);
},
// eslint-disable-next-line no-unused-vars
async generateBundle(opts, bundle, something) {
for (const n in bundle) {
const file = bundle[n]; // $FlowFixMe
let facadeModuleId = file.facadeModuleId;
if (file.isAsset || !file.isEntry || facadeModuleId == null) {
continue;
}
let mainFieldPath = file.fileName.replace(/\.prod\.js$/, ".js");
let relativeToSource = path.relative(path.dirname(path.join(opts.dir, file.fileName)), facadeModuleId);
let isEntrySourceTypeScript = /\.tsx?$/.test(facadeModuleId);
if (!isEntrySourceTypeScript) {
let flowMode = false;
let source = await fs.readFile(facadeModuleId, "utf8");
if (source.includes("@flow")) {
flowMode = file.exports.includes("default") ? "all" : "named";
}
if (flowMode !== false) {
let flowFileSource = flowTemplate(flowMode === "all", relativeToSource);
let flowFileName = mainFieldPath + ".flow";
bundle[flowFileName] = {
fileName: flowFileName,
isAsset: true,
source: flowFileSource
};
}
}
let mainEntrySource = `'use strict';
if (${// tricking static analysis is fun...
"process" + ".env.NODE_ENV"} === "production") {
module.exports = require("./${path.basename(getProdPath(mainFieldPath))}");
} else {
module.exports = require("./${path.basename(getDevPath(mainFieldPath))}");
}\n`;
bundle[mainFieldPath] = {
fileName: mainFieldPath,
isAsset: true,
source: mainEntrySource
};
}
}
};
}
function normalize(fileName) {
return fileName.split("\\").join("/");
}
let createLanguageServiceHostClass = typescript => class LanguageServiceHost {
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);
this.snapshots[fileName] = snapshot;
this.versions[fileName] = (this.versions[fileName] || 0) + 1;
this.fileNames.add(fileName);
return snapshot;
}
getScriptSnapshot(fileName) {
fileName = normalize(fileName);
if (this.snapshots[fileName]) return this.snapshots[fileName];
if (fs$1.existsSync(fileName)) {
this.snapshots[fileName] = typescript.ScriptSnapshot.fromString(typescript.sys.readFile(fileName));
this.versions[fileName] = (this.versions[fileName] || 0) + 1;
return this.snapshots[fileName];
}
return undefined;
}
getCurrentDirectory() {
return this.cwd;
}
getScriptVersion(fileName) {
fileName = normalize(fileName);
return (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() {
return undefined;
}
};
let unsafeRequire$2 = require;
let weakMemoize = function (func) {
// $FlowFixMe flow doesn't include all non-primitive types as allowed for weakmaps
let cache = new WeakMap();
return arg => {
if (cache.has(arg)) {
// $FlowFixMe
return cache.get(arg);
}
let ret = func(arg);
cache.set(arg, ret);
return ret;
};
};
function memoize(fn) {
const cache = {};
return arg => {
if (cache[arg] === undefined) cache[arg] = fn(arg);
return 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(), undefined, configFileName);
let LanguageServiceHostClass = createLanguageServiceHostClass(typescript);
let servicesHost = new LanguageServiceHostClass(thing, []);
let service = typescript.createLanguageService(servicesHost, typescript.createDocumentRegistry());
servicesHost.setLanguageService(service);
return {
service,
options: thing.options
};
}));
async function createDeclarationCreator(dirname) {
let typescript;
try {
typescript = unsafeRequire$2(resolveFrom(dirname, "typescript"));
} catch (err) {
if (err.code === "MODULE_NOT_FOUND") {
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,
options
} = await getService(typescript)(configFileName);
let 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
} = 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;
});
let allDeps = new Set(resolvedEntrypointPaths);
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
} = _ref;
let {
resolvedModule
} = typescript.resolveModuleName(text, dep, options, typescript.sys, moduleResolutionCache);
if (resolvedModule) {
if (!allDeps.has(resolvedModule.resolvedFileName) && !resolvedModule.isExternalLibraryImport && resolvedModule.resolvedFileName.includes(dirname)) {
internalDeps.add(resolvedModule.resolvedFileName);
allDeps.add(resolvedModule.resolvedFileName);
}
}
}
searchDeps(internalDeps);
}
}
searchDeps(new Set(resolvedEntrypointPaths));
return 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, true);
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) {
if (!pkg.entrypoints.some(({
source
}) => isTsPath(source))) {
return {
name: "typescript-declarations"
};
}
return {
name: "typescript-declarations",
// eslint-disable-next-line no-unused-vars
async generateBundle(opts, bundle, something) {
let creator = await createDeclarationCreator(pkg.directory);
let srcFilenameToDtsFilenameMap = new Map();
let deps = creator.getDeps(pkg.entrypoints.map(x => x.source));
await Promise.all([...deps].map(async dep => {
let {
name,
content
} = await creator.getDeclarationFile(dep);
srcFilenameToDtsFilenameMap.set(dep, name);
bundle[name] = {
fileName: name,
isAsset: true,
source: content
};
}));
for (const n in bundle) {
const file = bundle[n]; // $FlowFixMe
let facadeModuleId = file.facadeModuleId;
if (file.isAsset || !file.isEntry || facadeModuleId == null) {
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$/, "");
let relativeToSource = path.relative(path.dirname(path.join(opts.dir, file.fileName)), dtsFilename.replace(/\.d\.ts$/, ""));
if (!relativeToSource.startsWith(".")) {
relativeToSource = `./${relativeToSource}`;
}
let tsFileSource = tsTemplate(file.exports.includes("default"), relativeToSource);
let tsFileName = mainFieldPath + ".d.ts";
bundle[tsFileName] = {
fileName: tsFileName,
isAsset: true,
source: tsFileSource
};
}
}
};
}
let shouldUseWorker = process.env.DISABLE_PRECONSTRUCT_WORKER !== "true";
let worker;
let unsafeRequire$3 = require;
function createWorker() {
if (shouldUseWorker) {
worker = new Worker(require.resolve("preconstruct/worker"));
} else {
worker = unsafeRequire$3("preconstruct/worker");
}
}
function destroyWorker() {
if (worker !== undefined && shouldUseWorker) {
worker.end();
worker = undefined;
}
}
function getWorker() {
if (worker === undefined) {
throw new Error("worker not defined");
}
return worker;
}
const regExpCharactersRegExp = /[\\^$.*+?()[\]{}|]/g;
const escapeRegExpCharacters = str => str.replace(regExpCharactersRegExp, "\\$&");
const unpackOptions = (_ref = {}) => {
let {
extensions = babel.DEFAULT_EXTENSIONS,
// rollup uses sourcemap, babel uses sourceMaps
// just normalize them here so people don't have to worry about it
sourcemap = true,
sourcemaps = true,
sourceMap = true,
sourceMaps = true
} = _ref,
rest = _objectWithoutProperties(_ref, ["extensions", "sourcemap", "sourcemaps", "sourceMap", "sourceMaps"]);
return _objectSpread({
extensions,
plugins: [],
sourceMaps: sourcemap && sourcemaps && sourceMap && sourceMaps
}, rest, {
caller: _objectSpread({
name: "rollup-plugin-babel",
supportsStaticESM: true,
supportsDynamicImport: true
}, rest.caller)
});
};
const lru = new QuickLRU({
maxSize: 1000
});
let hasher;
let hasherPromise = initHasher().then(({
h64
}) => {
hasher = h64;
});
let rollupPluginBabel = pluginOptions => {
const _unpackOptions = unpackOptions(pluginOptions),
{
exclude,
extensions,
include
} = _unpackOptions,
babelOptions = _objectWithoutProperties(_unpackOptions, ["exclude", "extensions", "include"]);
const extensionRegExp = new RegExp(`(${extensions.map(escapeRegExpCharacters).join("|")})$`);
const includeExcludeFilter = rollupPluginutils.createFilter(include, exclude);
const filter = id => extensionRegExp.test(id) && includeExcludeFilter(id);
return {
name: "babel",
transform(code, filename) {
if (!filter(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
}));
let promise = getWorker().transformBabel(code, options);
lru.set(hash, {
code,
promise
});
return promise;
}
};
};
function terser(userOptions = {}) {
if (userOptions.sourceMap != null) {
throw Error("sourceMap option is removed, use sourcemap instead");
}
return {
name: "terser",
renderChunk(code, chunk, outputOptions) {
// TODO rewrite with object spread after node6 drop
const normalizedOptions = _objectSpread({}, userOptions, {
sourceMap: userOptions.sourcemap !== false,
module: outputOptions.format === "es" || outputOptions.format === "esm"
});
if (normalizedOptions.hasOwnProperty("sourcemap")) {
delete normalizedOptions["sourcemap"];
}
const result = getWorker().transformTerser(code, JSON.stringify(normalizedOptions)).catch(error => {
const {
message,
line,
col: column
} = error;
console.error(codeFrame.codeFrameColumns(code, {
start: {
line,
column
}
}, {
message
}));
throw error;
});
return result;
}
};
}
const makeExternalPredicate = externalArr => {
if (externalArr.length === 0) {
return () => false;
}
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 = [];
if (pkg.peerDependencies) {
external.push(...Object.keys(pkg.peerDependencies));
}
if (pkg.dependencies && type !== "umd") {
external.push(...Object.keys(pkg.dependencies));
}
if (type === "node-dev" || type === "node-prod") {
external.push(...builtInModules);
}
let rollupAliases = {};
Object.keys(aliases).forEach(key => {
try {
rollupAliases[key] = resolveFrom(pkg.directory, aliases[key]);
} catch (err) {
if (err.code !== "MODULE_NOT_FOUND") {
throw err;
}
}
});
let input = {};
entrypoints.forEach(entrypoint => {
input[path.relative(pkg.directory, path.join(entrypoint.directory, "dist", getNameForDist(pkg.name)))] = entrypoint.strict().source;
});
const config = {
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 () => {
let shouldInstallBabelRuntime = await confirms.shouldInstallBabelRuntime(pkg);
if (shouldInstallBabelRuntime) {
await limit(() => installPackages({
packages: ["@babel/runtime"],
cwd: pkg.directory,
installPeers: false,
packageManager: pkg.project.isBolt ? "bolt" : undefined
}));
await pkg.refresh();
} else {
throw new FatalError(`@babel/runtime should be in dependencies of ${pkg.name}`, pkg.name);
}
})();
}
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}: