UNPKG

preconstruct

Version:
1,113 lines (1,031 loc) 59.2 kB
"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