UNPKG

preconstruct

Version:
1,641 lines (1,359 loc) 68 kB
'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}: