UNPKG

@strapi/pack-up

Version:

Simple tools for creating interoperable CJS & ESM packages.

1,434 lines 68.7 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const fs$1 = require("fs/promises"); const ora = require("ora"); const os = require("os"); const node = require("esbuild-register/dist/node"); const fs = require("fs"); const path = require("path"); const pkgUp = require("pkg-up"); const chalk = require("chalk"); const yup = require("yup"); const browserslistToEsbuild = require("browserslist-to-esbuild"); const ts = require("typescript"); const rxjs = require("rxjs"); const react = require("@vitejs/plugin-react-swc"); const node_module = require("node:module"); const chokidar = require("chokidar"); const esbuild = require("esbuild"); const prompts = require("prompts"); const ini = require("ini"); const getLatestVersion = require("get-latest-version"); const gitUrlParse = require("git-url-parse"); const outdent = require("outdent"); const _interopDefault = (e) => e && e.__esModule ? e : { default: e }; function _interopNamespace(e) { if (e && e.__esModule) return e; const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } }); if (e) { for (const k in e) { if (k !== "default") { const d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: () => e[k] }); } } } n.default = e; return Object.freeze(n); } const fs__default = /* @__PURE__ */ _interopDefault(fs$1); const ora__default = /* @__PURE__ */ _interopDefault(ora); const os__default = /* @__PURE__ */ _interopDefault(os); const fs__namespace = /* @__PURE__ */ _interopNamespace(fs); const path__namespace = /* @__PURE__ */ _interopNamespace(path); const pkgUp__default = /* @__PURE__ */ _interopDefault(pkgUp); const chalk__default = /* @__PURE__ */ _interopDefault(chalk); const yup__namespace = /* @__PURE__ */ _interopNamespace(yup); const browserslistToEsbuild__default = /* @__PURE__ */ _interopDefault(browserslistToEsbuild); const ts__default = /* @__PURE__ */ _interopDefault(ts); const react__default = /* @__PURE__ */ _interopDefault(react); const chokidar__default = /* @__PURE__ */ _interopDefault(chokidar); const esbuild__default = /* @__PURE__ */ _interopDefault(esbuild); const prompts__default = /* @__PURE__ */ _interopDefault(prompts); const ini__default = /* @__PURE__ */ _interopDefault(ini); const getLatestVersion__default = /* @__PURE__ */ _interopDefault(getLatestVersion); const gitUrlParse__default = /* @__PURE__ */ _interopDefault(gitUrlParse); const CONFIG_FILE_NAMES = [ "packup.config.ts", "packup.config.js", "packup.config.cjs", "packup.config.mjs" ]; const loadConfig = async ({ cwd, logger }) => { const pkgPath = await pkgUp__default.default({ cwd }); if (!pkgPath) { logger.debug( "Could not find a package.json in the current directory, therefore no config was loaded" ); return void 0; } const root = path__namespace.dirname(pkgPath); for (const fileName of CONFIG_FILE_NAMES) { const configPath = path__namespace.resolve(root, fileName); const exists = fs__namespace.existsSync(configPath); if (exists) { const esbuildOptions = { extensions: [".js", ".mjs", ".ts"] }; const { unregister } = node.register(esbuildOptions); const mod = require(configPath); unregister(); const config = mod?.default || mod || void 0; if (config) { logger.debug("Loaded configuration:", os__default.default.EOL, config); } return config; } } return void 0; }; const defineConfig = (configOptions) => configOptions; function resolveConfigProperty(prop, initialValue) { if (prop === void 0 || prop === null) { return initialValue; } if (typeof prop === "function") { return prop(initialValue); } return prop; } const isError = (err) => err instanceof Error; const validateExportsOrdering = async ({ pkg, logger }) => { if (pkg.exports) { const exports2 = Object.entries(pkg.exports); for (const [expPath, exp] of exports2) { if (typeof exp === "string") { continue; } const keys = Object.keys(exp); if (!assertFirst("types", keys)) { throw new Error(`exports["${expPath}"]: the 'types' property should be the first property`); } if (exp.node) { const nodeKeys = Object.keys(exp.node); if (!assertOrder("module", "import", nodeKeys)) { throw new Error( `exports["${expPath}"]: the 'node.module' property should come before the 'node.import' property` ); } if (!assertOrder("import", "require", nodeKeys)) { logger.warn( `exports["${expPath}"]: the 'node.import' property should come before the 'node.require' property` ); } if (!assertOrder("module", "require", nodeKeys)) { logger.warn( `exports["${expPath}"]: the 'node.module' property should come before 'node.require' property` ); } if (exp.import && exp.node.import && !assertOrder("node", "import", keys)) { throw new Error( `exports["${expPath}"]: the 'node' property should come before the 'import' property` ); } if (exp.module && exp.node.module && !assertOrder("node", "module", keys)) { throw new Error( `exports["${expPath}"]: the 'node' property should come before the 'module' property` ); } if (exp.node.import && (!exp.node.require || exp.require === exp.node.require) && !exp.node.module) { logger.warn( `exports["${expPath}"]: the 'node.module' property should be added so bundlers don't unintentionally try to bundle 'node.import'. Its value should be '"module": "${exp.import}"'` ); } if (exp.node.import && !exp.node.require && exp.node.module && exp.import && exp.node.module !== exp.import) { throw new Error( `exports["${expPath}"]: the 'node.module' property should match 'import'` ); } if (exp.require && exp.node.require && exp.require === exp.node.require) { throw new Error( `exports["${expPath}"]: the 'node.require' property isn't necessary as it's identical to 'require'` ); } else if (exp.require && exp.node.require && !assertOrder("node", "require", keys)) { throw new Error( `exports["${expPath}"]: the 'node' property should come before the 'require' property` ); } } else { if (!assertOrder("import", "require", keys)) { logger.warn( `exports["${expPath}"]: the 'import' property should come before the 'require' property` ); } if (!assertOrder("module", "import", keys)) { logger.warn( `exports["${expPath}"]: the 'module' property should come before 'import' property` ); } } if (!assertLast("default", keys)) { throw new Error( `exports["${expPath}"]: the 'default' property should be the last property` ); } } } else if (!["main", "module"].some((key) => Object.prototype.hasOwnProperty.call(pkg, key))) { throw new Error("'package.json' must contain a 'main' and 'module' property"); } return pkg; }; function assertFirst(key, arr) { const aIdx = arr.indexOf(key); if (aIdx === -1) { return true; } return aIdx === 0; } function assertLast(key, arr) { const aIdx = arr.indexOf(key); if (aIdx === -1) { return true; } return aIdx === arr.length - 1; } function assertOrder(keyA, keyB, arr) { const aIdx = arr.indexOf(keyA); const bIdx = arr.indexOf(keyB); if (aIdx === -1 || bIdx === -1) { return true; } return aIdx < bIdx; } const DEFAULT_PKG_EXT_MAP = { // pkg.type: "commonjs" commonjs: { cjs: ".js", es: ".mjs" }, // pkg.type: "module" module: { cjs: ".cjs", es: ".js" } }; const getExportExtensionMap = () => { return DEFAULT_PKG_EXT_MAP; }; const validateExports = (_exports, options) => { const { extMap, pkg } = options; const ext = extMap[pkg.type || "commonjs"]; const errors = []; for (const exp of _exports) { if (exp.require && !exp.require.endsWith(ext.cjs)) { errors.push( `package.json with 'type: "${pkg.type}"' - 'exports["${exp._path}"].require' must end with "${ext.cjs}"` ); } if (exp.import && !exp.import.endsWith(ext.es)) { errors.push( `package.json with 'type: "${pkg.type}"' - 'exports["${exp._path}"].import' must end with "${ext.es}"` ); } } return errors; }; const parseExports = ({ extMap, pkg }) => { const rootExport = { _path: ".", types: pkg.types, source: pkg.source || "", require: pkg.main, import: pkg.module, default: pkg.module || pkg.main || "" }; const extraExports = []; const errors = []; if (pkg.exports) { if (!pkg.exports["./package.json"]) { errors.push('package.json: `exports["./package.json"] must be declared.'); } Object.entries(pkg.exports).forEach(([path2, entry]) => { if (path2.endsWith(".json")) { if (path2 === "./package.json" && entry !== "./package.json") { errors.push(`package.json: 'exports["./package.json"]' must be './package.json'.`); } } else if (Boolean(entry) && typeof entry === "object" && !Array.isArray(entry)) { if (path2 === ".") { if (entry.require && rootExport.require && entry.require !== rootExport.require) { errors.push( "package.json: mismatch between 'main' and 'exports.require'. These must be equal." ); } if (entry.import && rootExport.import && entry.import !== rootExport.import) { errors.push( "package.json: mismatch between 'module' and 'exports.import' These must be equal." ); } if (entry.types && rootExport.types && entry.types !== rootExport.types) { errors.push( "package.json: mismatch between 'types' and 'exports.types'. These must be equal." ); } if (entry.source && rootExport.source && entry.source !== rootExport.source) { errors.push( "package.json: mismatch between 'source' and 'exports.source'. These must be equal." ); } Object.assign(rootExport, entry); } else { const extraExport = { _exported: true, _path: path2, ...entry }; extraExports.push(extraExport); } } else if (!["string", "object"].includes(typeof entry)) { errors.push("package.json: exports must be an object or string"); } }); } const _exports = [ /** * In the case of strapi plugins, we don't have a root export because we * ship a server side and client side package. So this can be completely omitted. */ Object.values(rootExport).some((exp) => exp !== rootExport._path && Boolean(exp)) && rootExport, ...extraExports ].filter((exp) => Boolean(exp)); errors.push(...validateExports(_exports, { extMap, pkg })); if (errors.length) { throw new Error(`${os__default.default.EOL}- ${errors.join(`${os__default.default.EOL}- `)}`); } return _exports; }; const createLogger = (options = {}) => { const { silent = false, debug = false } = options; const state = { errors: 0, warning: 0 }; return { get warnings() { return state.warning; }, get errors() { return state.errors; }, debug(...args) { if (silent || !debug) { return; } console.debug(chalk__default.default.cyan("[DEBUG] "), ...args); }, info(...args) { if (silent) { return; } console.info(chalk__default.default.blue("[INFO] "), ...args); }, log(...args) { if (silent) { return; } console.log(...args); }, warn(...args) { state.warning += 1; if (silent) { return; } console.warn(chalk__default.default.yellow("[WARN] "), ...args); }, error(...args) { state.errors += 1; if (silent) { return; } console.error(chalk__default.default.red("[ERROR] "), ...args); }, success(...args) { if (silent) { return; } console.info(chalk__default.default.green("[SUCCESS] "), ...args); } }; }; const record = (value) => yup__namespace.object( typeof value === "object" && value ? Object.entries(value).reduce((acc, [key]) => { acc[key] = yup__namespace.string().required(); return acc; }, {}) : {} ).optional(); const packageJsonSchema = yup__namespace.object({ name: yup__namespace.string().required(), version: yup__namespace.string().required(), description: yup__namespace.string().optional(), author: yup__namespace.lazy((value) => { if (typeof value === "object") { return yup__namespace.object({ name: yup__namespace.string().required(), email: yup__namespace.string().optional(), url: yup__namespace.string().optional() }).optional(); } return yup__namespace.string().optional(); }), keywords: yup__namespace.array(yup__namespace.string()).optional(), type: yup__namespace.mixed().oneOf(["commonjs", "module"]).optional(), license: yup__namespace.string().optional(), repository: yup__namespace.object({ type: yup__namespace.string().required(), url: yup__namespace.string().required() }).optional(), bugs: yup__namespace.object({ url: yup__namespace.string().required() }).optional(), homepage: yup__namespace.string().optional(), // TODO: be nice just to make this either a string or a record of strings. bin: yup__namespace.lazy((value) => { if (typeof value === "object") { return record(value); } return yup__namespace.string().optional(); }), // TODO: be nice just to make this either a string or a record of strings. browser: yup__namespace.lazy((value) => { if (typeof value === "object") { return record(value); } return yup__namespace.string().optional(); }), main: yup__namespace.string().optional(), module: yup__namespace.string().optional(), source: yup__namespace.string().optional(), types: yup__namespace.string().optional(), exports: yup__namespace.lazy( (value) => yup__namespace.object( typeof value === "object" ? Object.entries(value).reduce( (acc, [key, v]) => { if (typeof v === "object") { acc[key] = yup__namespace.object({ types: yup__namespace.string().optional(), source: yup__namespace.string().required(), browser: yup__namespace.object({ source: yup__namespace.string().required(), import: yup__namespace.string().optional(), require: yup__namespace.string().optional() }).optional(), node: yup__namespace.object({ source: yup__namespace.string().optional(), module: yup__namespace.string().optional(), import: yup__namespace.string().optional(), require: yup__namespace.string().optional() }).optional(), module: yup__namespace.string().optional(), import: yup__namespace.string().optional(), require: yup__namespace.string().optional(), default: yup__namespace.string().required() }).noUnknown(true); } else { acc[key] = yup__namespace.string().required(); } return acc; }, {} ) : void 0 ).optional() ), files: yup__namespace.array(yup__namespace.string()).optional(), scripts: yup__namespace.lazy(record), dependencies: yup__namespace.lazy(record), devDependencies: yup__namespace.lazy(record), peerDependencies: yup__namespace.lazy(record), engines: yup__namespace.lazy(record), browserslist: yup__namespace.array(yup__namespace.string().required()).optional() }); const loadPkg = async ({ cwd, logger }) => { const pkgPath = await pkgUp__default.default({ cwd }); if (!pkgPath) { throw new Error("Could not find a package.json in the current directory"); } const buffer = await fs__default.default.readFile(pkgPath); const pkg = JSON.parse(buffer.toString()); logger.debug("Loaded package.json:", os__default.default.EOL, pkg); return pkg; }; const validatePkg = async ({ pkg }) => { try { const validatedPkg = await packageJsonSchema.validate(pkg, { strict: true }); return validatedPkg; } catch (err) { if (err instanceof yup__namespace.ValidationError) { switch (err.type) { case "required": if (err.path) { throw new Error( `'${err.path}' in 'package.json' is required as type '${chalk__default.default.magenta( yup__namespace.reach(packageJsonSchema, err.path).type )}'` ); } break; case "matches": if (err.params && err.path && "value" in err.params && "regex" in err.params) { throw new Error( `'${err.path}' in 'package.json' must be of type '${chalk__default.default.magenta( err.params.regex )}' (recieved the value '${chalk__default.default.magenta(err.params.value)}')` ); } break; case "noUnknown": if (err.path && err.params && "unknown" in err.params) { throw new Error( `'${err.path}' in 'package.json' contains the unknown key ${chalk__default.default.magenta( err.params.unknown )}, for compatability only the following keys are allowed: ${chalk__default.default.magenta( "['types', 'source', 'import', 'require', 'default']" )}` ); } break; default: if (err.path && err.params && "type" in err.params && "value" in err.params) { throw new Error( `'${err.path}' in 'package.json' must be of type '${chalk__default.default.magenta( err.params.type )}' (recieved '${chalk__default.default.magenta(typeof err.params.value)}')` ); } } } throw err; } }; const loadTsConfig = ({ cwd, path: path2, logger }) => { const providedPath = path2.split(path__namespace.default.sep); const [configFileName] = providedPath.slice(-1); const pathToConfig = path__namespace.default.join(cwd, providedPath.slice(0, -1).join(path__namespace.default.sep)); const configPath = ts__default.default.findConfigFile(pathToConfig, ts__default.default.sys.fileExists, configFileName); if (!configPath) { return void 0; } const configFile = ts__default.default.readConfigFile(configPath, ts__default.default.sys.readFile); const parsedConfig = ts__default.default.parseJsonConfigFileContent(configFile.config, ts__default.default.sys, pathToConfig); logger.debug("Loaded user TS config:", os__default.default.EOL, parsedConfig); const { outDir } = parsedConfig.raw.compilerOptions; if (!outDir) { throw new Error("tsconfig.json is missing 'compilerOptions.outDir'"); } parsedConfig.options = { ...parsedConfig.options, declaration: true, declarationDir: outDir, emitDeclarationOnly: true, noEmit: false, outDir }; logger.debug("Using TS config:", os__default.default.EOL, parsedConfig); return { config: parsedConfig, path: configPath }; }; const DEFAULT_BROWSERS_LIST_CONFIG = [ "last 3 major versions", "Firefox ESR", "last 2 Opera versions", "not dead", "node 18.0.0" ]; const createBuildContext = async ({ config, cwd, extMap, logger, pkg }) => { const tsConfig = loadTsConfig({ cwd, path: resolveConfigProperty(config.tsconfig, "tsconfig.build.json"), logger }); const targets = { "*": browserslistToEsbuild__default.default(pkg.browserslist ?? DEFAULT_BROWSERS_LIST_CONFIG), node: browserslistToEsbuild__default.default(["node 18.0.0"]), web: ["esnext"] }; const parsedExports = parseExports({ extMap, pkg }).reduce( (acc, x) => { const { _path: exportPath, ...exportEntry } = x; return { ...acc, [exportPath]: exportEntry }; }, {} ); const exports2 = resolveConfigProperty(config.exports, parsedExports); const parsedExternals = [ ...pkg.dependencies ? Object.keys(pkg.dependencies) : [], ...pkg.peerDependencies ? Object.keys(pkg.peerDependencies) : [] ]; const external = config && Array.isArray(config.externals) ? [...parsedExternals, ...config.externals] : parsedExternals; const outputPaths = Object.values(exports2).flatMap((exportEntry) => { return [ exportEntry.import, exportEntry.require, exportEntry.browser?.import, exportEntry.browser?.require, exportEntry.node?.source && exportEntry.node.import, exportEntry.node?.source && exportEntry.node.require ].filter(Boolean); }).map((p) => path__namespace.default.resolve(cwd, p)); const commonDistPath = findCommonDirPath(outputPaths); if (commonDistPath === cwd) { throw new Error( "all output files must share a common parent directory which is not the root package directory" ); } if (commonDistPath && !pathContains(cwd, commonDistPath)) { throw new Error("all output files must be located within the package"); } const configDistPath = config?.dist ? path__namespace.default.resolve(cwd, config.dist) : void 0; const distPath = configDistPath || commonDistPath; if (!distPath) { throw new Error("could not detect 'dist' path"); } return { config, cwd, distPath, exports: exports2, external, extMap, logger, pkg, runtime: config?.runtime, targets, ts: tsConfig }; }; const pathContains = (containerPath, itemPath) => { return !path__namespace.default.relative(containerPath, itemPath).startsWith(".."); }; const findCommonDirPath = (filePaths) => { let commonPath; for (const filePath of filePaths) { let dirPath = path__namespace.default.dirname(filePath); if (!commonPath) { commonPath = dirPath; continue; } while (dirPath !== commonPath) { dirPath = path__namespace.default.dirname(dirPath); if (dirPath === commonPath) { break; } if (pathContains(dirPath, commonPath)) { commonPath = dirPath; break; } if (dirPath === ".") { return void 0; } } } return commonPath; }; const createTasks = (mode) => async (ctx) => { const tasks = []; const dtsTask = { type: `${mode}:dts`, entries: [] }; const viteTasks = {}; const createViteTask = (format, runtime, { output, ...restEntry }) => { const buildId = `${format}:${output}`; if (viteTasks[buildId]) { viteTasks[buildId]?.entries.push(restEntry); if (output !== viteTasks[buildId]?.output) { ctx.logger.warn( "Multiple entries with different outputs for the same format are not supported. The first output will be used." ); } } else { viteTasks[buildId] = { type: `${mode}:js`, format, output, runtime, entries: [restEntry] }; } }; const exps = Object.entries(ctx.exports).map(([exportPath, exportEntry]) => ({ ...exportEntry, _path: exportPath })); for (const exp of exps) { if (exp.types) { const importId = path__namespace.default.join(ctx.pkg.name, exp._path); dtsTask.entries.push({ importId, exportPath: exp._path, sourcePath: exp.source, targetPath: exp.types }); } if (exp.require) { createViteTask("cjs", ctx.runtime ?? "*", { path: exp._path, entry: exp.source, output: exp.require }); } if (exp.import) { createViteTask("es", ctx.runtime ?? "*", { path: exp._path, entry: exp.source, output: exp.import }); } if (exp.browser?.require) { createViteTask("cjs", "web", { path: exp._path, entry: exp.browser?.source || exp.source, output: exp.browser.require }); } if (exp.browser?.import) { createViteTask("cjs", "web", { path: exp._path, entry: exp.browser?.source || exp.source, output: exp.browser.import }); } } const bundles = ctx.config.bundles ?? []; for (const bundle of bundles) { const idx = bundles.indexOf(bundle); if (bundle.require) { createViteTask("cjs", (bundle.runtime || ctx.runtime) ?? "*", { path: `bundle_cjs_${idx}`, entry: bundle.source, output: bundle.require }); } if (bundle.import) { createViteTask("es", (bundle.runtime || ctx.runtime) ?? "*", { path: `bundle_esm_${idx}`, entry: bundle.source, output: bundle.import }); } if (bundle.types) { const importId = path__namespace.default.join(ctx.pkg.name, bundle.source); dtsTask.entries.push({ importId, exportPath: bundle.source, sourcePath: bundle.source, targetPath: bundle.types, tsconfig: bundle.tsconfig }); } } if (dtsTask.entries.length) { tasks.push(dtsTask); } if (Object.values(viteTasks).length) { tasks.push(...Object.values(viteTasks)); } return tasks; }; const createBuildTasks = createTasks("build"); const createWatchTasks = createTasks("watch"); const printDiagnostic = (diagnostic, { logger, cwd }) => { let output = ts__default.default.flattenDiagnosticMessageText(diagnostic.messageText, ts__default.default.sys.newLine); if (diagnostic.file && diagnostic.start) { const { line, character } = ts__default.default.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start); const message = ts__default.default.flattenDiagnosticMessageText(diagnostic.messageText, ts__default.default.sys.newLine); const file = path__namespace.default.relative(cwd, diagnostic.file.fileName); output = [ `${chalk__default.default.cyan(file)}:${chalk__default.default.cyan(line + 1)}:${chalk__default.default.cyan(character + 1)} - `, `${chalk__default.default.gray(`TS${diagnostic.code}:`)} ${message}` ].join(""); } switch (diagnostic.category) { case ts__default.default.DiagnosticCategory.Error: logger.error(output); break; case ts__default.default.DiagnosticCategory.Warning: logger.warn(output); break; case ts__default.default.DiagnosticCategory.Message: logger.info(output); break; case ts__default.default.DiagnosticCategory.Suggestion: logger.info(output); break; } }; const dtsBuildTask = { print(ctx, task) { const entries = [ " entries:", ...task.entries.map( (entry) => [ " – ", chalk__default.default.green(`${entry.importId} `), `${chalk__default.default.cyan(entry.sourcePath)} ${chalk__default.default.gray("->")} ${chalk__default.default.cyan(entry.targetPath)}` ].join("") ) ]; ctx.logger.log(["Building type files:", ...entries].join(os__default.default.EOL)); }, run$(ctx, task) { return new rxjs.Observable((subscriber) => { Promise.all( task.entries.map(async (entry) => { const tsconfig = entry.tsconfig ? loadTsConfig({ cwd: ctx.cwd, path: entry.tsconfig, logger: ctx.logger }) : ctx.ts; if (!tsconfig) { ctx.logger.warn( `You've added a types entry but no tsconfig.json was found for ${entry.targetPath}. Skipping...` ); return; } const program = ts__default.default.createProgram(tsconfig.config.fileNames, tsconfig.config.options); const emitResult = program.emit(); const allDiagnostics = ts__default.default.getPreEmitDiagnostics(program).concat(emitResult.diagnostics); for (const diagnostic of allDiagnostics) { printDiagnostic(diagnostic, { logger: ctx.logger, cwd: ctx.cwd }); } const errors = allDiagnostics.filter( (diag) => diag.category === ts__default.default.DiagnosticCategory.Error ); if (errors.length) { throw new Error("Failed to compile TypeScript definitions"); } }) ).then(() => { subscriber.complete(); }).catch((err) => { subscriber.error(err); }); }); }, async success(ctx, task) { const msg = [ "Built types, entries:", task.entries.map( (entry) => ` ${chalk__default.default.blue(`${entry.importId}`)}: ${entry.sourcePath} -> ${entry.targetPath}` ).join(os__default.default.EOL) ]; ctx.logger.success(msg.join(os__default.default.EOL)); }, async fail(ctx, task, err) { if (isError(err)) { ctx.logger.error(err.message); } } }; const dtsWatchTask = { print(ctx, task) { const msg = [ "Building Types, entries:", task.entries.map( (entry) => ` ${chalk__default.default.blue(`${entry.importId}`)}: ${entry.sourcePath} -> ${entry.targetPath}` ).join(os__default.default.EOL) ]; ctx.logger.success(msg.join(os__default.default.EOL)); }, run$(ctx, task) { let programs = []; return new rxjs.Observable((subscriber) => { Promise.all( task.entries.map(async (entry) => { const tsconfig = entry.tsconfig ? loadTsConfig({ cwd: ctx.cwd, path: entry.tsconfig, logger: ctx.logger }) : ctx.ts; if (!tsconfig) { ctx.logger.warn( `You've added a types entry but no tsconfig.json was found for ${entry.targetPath}. Skipping...` ); return; } const compilerHost = ts__default.default.createWatchCompilerHost( tsconfig.path, tsconfig.config.options, ts__default.default.sys, ts__default.default.createEmitAndSemanticDiagnosticsBuilderProgram, (diagnostic) => { subscriber.next(diagnostic); }, (diagnostic) => { subscriber.next(diagnostic); } ); return ts__default.default.createWatchProgram(compilerHost); }) ).then((progs) => { programs = progs; }).catch((err) => { subscriber.error(err); }); return () => { programs.forEach((prog) => { prog?.close(); }); }; }); }, async success(ctx, task, diagnostic) { const { logger, cwd } = ctx; if (diagnostic.code === 6194) { this.print(ctx, task); } if (diagnostic.category === ts__default.default.DiagnosticCategory.Message || diagnostic.category === ts__default.default.DiagnosticCategory.Suggestion) { return; } printDiagnostic(diagnostic, { logger, cwd }); }, async fail(ctx, task, err) { if (isError(err)) { ctx.logger.error(err); } } }; const resolveViteConfig = async (ctx, task) => { const { cwd, distPath, targets, external, extMap, pkg, exports: exportMap } = ctx; const { entries, format, output, runtime } = task; const outputExt = extMap[pkg.type || "commonjs"][format]; const outDir = path__namespace.default.relative(cwd, distPath); const { createLogger: createLogger2 } = await import("vite"); const customLogger = createLogger2(); customLogger.warn = (msg) => ctx.logger.warn(msg); customLogger.warnOnce = (msg) => ctx.logger.warn(msg); customLogger.error = (msg) => ctx.logger.error(msg); customLogger.info = () => { }; const exportIds = Object.keys(exportMap).map((exportPath) => path__namespace.default.join(pkg.name, exportPath)); const sourcePaths = Object.values(exportMap).map((exp) => path__namespace.default.resolve(cwd, exp.source)); const basePlugins = runtime === "node" ? [] : [react__default.default()]; const plugins = ctx.config.plugins ? typeof ctx.config.plugins === "function" ? ctx.config.plugins({ runtime }) : ctx.config.plugins : []; const config = { configFile: false, root: cwd, mode: "production", logLevel: "warn", clearScreen: false, customLogger, build: { minify: resolveConfigProperty(ctx.config.minify, false), sourcemap: resolveConfigProperty(ctx.config.sourcemap, true), /** * The task runner will clear this for us */ emptyOutDir: false, target: targets[runtime], outDir, lib: { entry: entries.map((e) => e.entry), formats: [format], /** * this enforces the file name to match what the output we've * determined from the package.json exports. However, when preserving modules * we want to let Rollup handle the file names. */ fileName: resolveConfigProperty(ctx.config.preserveModules, false) ? void 0 : () => { return `${path__namespace.default.relative(outDir, output).replace(/\.[^/.]+$/, "")}${outputExt}`; } }, rollupOptions: { external(id, importer) { if (exportIds?.includes(id)) { return true; } if (importer && (id.startsWith(".") || id.startsWith("/"))) { const idPath = path__namespace.default.resolve(path__namespace.default.dirname(importer), id); if (sourcePaths?.includes(idPath)) { ctx.logger.warn( `detected self-referencing import – treating as external: ${path__namespace.default.relative( cwd, idPath )}` ); return true; } } const idParts = id.split("/"); const name = idParts[0]?.startsWith("@") ? `${idParts[0]}/${idParts[1]}` : idParts[0]; const builtinModulesWithNodePrefix = [ ...node_module.builtinModules, ...node_module.builtinModules.map((modName) => `node:${modName}`) ]; if (name && external.includes(name) || name && builtinModulesWithNodePrefix.includes(name)) { return true; } return false; }, output: { preserveModules: resolveConfigProperty(ctx.config.preserveModules, false), /** * Mimic TypeScript's behavior, by setting the value to "auto" to control * how Rollup handles default, namespace and dynamic imports from external * dependencies in formats like CommonJS that do not natively support * these concepts. Mainly styled-components@5 * * For more info see https://rollupjs.org/configuration-options/#output-interop */ interop: "auto", chunkFileNames() { const parts = outputExt.split("."); if (parts.length === 3) { return `_chunks/[name]-[hash].${parts[2]}`; } return `_chunks/[name]-[hash]${outputExt}`; } } } }, plugins: [...basePlugins, ...plugins] }; return import("vite").then( ({ mergeConfig }) => mergeConfig(config, ctx.config.unstable_viteConfig ?? {}) ); }; const viteBuildTask = { print(ctx, task) { const targetLines = [ " target:", ...ctx.targets[task.runtime].map((t) => chalk__default.default.cyan(` - ${t}`)) ]; const entries = [ " entries:", ...task.entries.map( (entry) => [ " – ", chalk__default.default.green(`${path__namespace.default.join(ctx.pkg.name, entry.path)}: `), `${chalk__default.default.cyan(entry.entry)} ${chalk__default.default.gray("→")} ${chalk__default.default.cyan(task.output)}` ].join("") ) ]; ctx.logger.log( ["Building javascript files:", ` format: ${task.format}`, ...targetLines, ...entries].join( os__default.default.EOL ) ); }, run$(ctx, task) { return new rxjs.Observable((subscriber) => { resolveViteConfig(ctx, task).then((config) => { ctx.logger.debug("Vite config:", os__default.default.EOL, config); import("vite").then(({ build: build2 }) => { build2(config).then(() => { subscriber.complete(); }).catch((err) => { subscriber.error(err); }); }); }); }); }, async success(ctx, task) { const msg = [ `Built javascript (runtime: ${task.runtime} – target: ${task.format})`, task.entries.map( (e) => ` ${chalk__default.default.blue(path__namespace.default.join(ctx.pkg.name, e.path))}: ${e.entry} -> ${task.output}` ).join(os__default.default.EOL) ]; ctx.logger.success(msg.join(os__default.default.EOL)); }, async fail(ctx, task, err) { if (isError(err)) { ctx.logger.error(err.message); } } }; const viteWatchTask = { print(ctx, task) { const msg = [ `Building Javascript (runtime: ${task.runtime} – target: ${task.format})`, task.entries.map( (e) => ` ${chalk__default.default.blue(path__namespace.default.join(ctx.pkg.name, e.path))}: ${e.entry} -> ${task.output}` ).join(os__default.default.EOL) ]; ctx.logger.success(msg.join(os__default.default.EOL)); }, run$(ctx, task) { return new rxjs.Observable((subscriber) => { let watcher = null; resolveViteConfig(ctx, task).then((config) => { ctx.logger.debug(`Vite config:${os__default.default.EOL}`, config); import("vite").then(({ build: build2 }) => { build2({ ...config, mode: "development", build: { ...config.build, watch: {} } }).then((rollupWatcher) => { watcher = rollupWatcher; if ("on" in watcher && typeof watcher.on === "function") { watcher.on("event", (ev) => { subscriber.next(ev); }); } }); }); }); return () => { if (watcher !== null && "close" in watcher && typeof watcher.close === "function") { watcher.close(); } }; }); }, success(ctx, task, result) { switch (result.code) { case "BUNDLE_END": this.print(ctx, task); break; case "ERROR": ctx.logger.error(result.error); break; } }, fail(ctx, task, err) { if (isError(err)) { ctx.logger.error(err); } } }; const taskHandlers = { "build:js": viteBuildTask, "build:dts": dtsBuildTask, "watch:js": viteWatchTask, "watch:dts": dtsWatchTask }; const build = async (opts = {}) => { process.env.NODE_ENV = process.env.NODE_ENV || "production"; const { silent, debug, cwd = process.cwd(), configFile = true, config: providedConfig, ...configOptions } = opts; const logger = createLogger({ silent, debug }); const packageJsonLoader = ora__default.default(`Verifying package.json ${os__default.default.EOL}`).start(); const rawPkg = await loadPkg({ cwd, logger }).catch((err) => { packageJsonLoader.fail(); if (isError(err)) { logger.error(err.message); } logger.debug(`Path checked – ${cwd}`); process.exit(1); }); const validatedPkg = await validatePkg({ pkg: rawPkg }).catch((err) => { packageJsonLoader.fail(); if (isError(err)) { logger.error(err.message); } process.exit(1); }); const packageJson = await validateExportsOrdering({ pkg: validatedPkg, logger }).catch((err) => { packageJsonLoader.fail(); if (isError(err)) { logger.error(err.message); } process.exit(1); }); packageJsonLoader.succeed("Verified package.json"); const config = configFile ? await loadConfig({ cwd, logger }) : providedConfig; const buildContextLoader = ora__default.default(`Creating build context ${os__default.default.EOL}`).start(); const extMap = getExportExtensionMap(); const ctx = await createBuildContext({ config: { ...config, ...configOptions }, cwd, extMap, logger, pkg: packageJson }).catch((err) => { buildContextLoader.fail(); if (isError(err)) { logger.error(err.message); } process.exit(1); }); logger.debug(`Build context: ${os__default.default.EOL}`, ctx); const buildTasks = await createBuildTasks(ctx); buildContextLoader.succeed("Created build context"); try { logger.debug(`Cleaning dist folder: ${ctx.distPath}`); await fs__default.default.rm(ctx.distPath, { recursive: true, force: true }); logger.debug("Cleaned dist folder"); } catch { logger.debug("There was no dist folder to clean"); } for (const task of buildTasks) { const handler = taskHandlers[task.type]; handler.print(ctx, task); try { const result = await handler.run$(ctx, task).toPromise(); handler.success(ctx, task, result); } catch (err) { handler.fail(ctx, task, err); throw err; } } }; const watch = async (opts) => { const { silent, debug, cwd = process.cwd(), configFile = true, config: providedConfig } = opts; const logger = createLogger({ silent, debug }); logger.debug("watching config files"); const configFilePaths = ["package.json", ...CONFIG_FILE_NAMES].map( (fileName) => path__namespace.default.resolve(cwd, fileName).split(path__namespace.default.sep).join(path__namespace.default.posix.sep) ); const watcher$ = new rxjs.Observable((subscriber) => { const watcher = chokidar__default.default.watch(configFilePaths, { ignoreInitial: true }); const handleEvent = (event, filePath) => { subscriber.next({ event, path: filePath }); }; watcher.on("all", handleEvent); return () => { watcher.off("all", handleEvent); watcher.close(); }; }); const configFiles$ = watcher$.pipe( rxjs.scan((files, { event, path: filePath }) => { if (event === "add") { logger.debug("config file added", filePath); return [...files, filePath]; } if (event === "unlink") { logger.debug("config file removed", filePath); return files.filter((fPath) => fPath !== filePath); } if (event === "change") { logger.log( "--------------------------------------------------------------------------------" ); logger.info(path__namespace.default.relative(cwd, filePath), "changed"); return files.slice(0); } return files; }, configFilePaths), rxjs.startWith(configFilePaths), rxjs.distinctUntilChanged() ); const ctx$ = configFiles$.pipe( rxjs.switchMap(async (configFiles) => { const files = configFiles.map((f) => path__namespace.default.relative(cwd, f)); const packageJsonPath = files.find((f) => f === "package.json"); if (!packageJsonPath) { throw new Error("missing package.json"); } const rawPkg = await loadPkg({ cwd, logger }); const validatedPkg = await validatePkg({ pkg: rawPkg }).catch((err) => { logger.error(err.message); process.exit(1); }); const packageJson = await validateExportsOrdering({ pkg: validatedPkg, logger }).catch( (err) => { logger.error(err.message); process.exit(1); } ); const config = configFile ? await loadConfig({ cwd, logger }) : providedConfig; const extMap = getExportExtensionMap(); return createBuildContext({ config: { ...config }, cwd, extMap, logger, pkg: packageJson }).catch((err) => { logger.error(err.message); process.exit(1); }); }) ); ctx$.subscribe(async (ctx) => { const watchTasks = await createWatchTasks(ctx); for (const task of watchTasks) { const handler = taskHandlers[task.type]; const result$ = handler.run$(ctx, task); result$.subscribe({ error(err) { handler.fail(ctx, task, err); process.exit(1); }, next(result) { handler.success(ctx, task, result); } }); } }); }; const isEmptyDirectory = async (dir) => { const files = await fs$1.readdir(dir); return files.length === 0; }; const isDirectory = async (dir) => { const stats = await fs$1.lstat(dir); return stats.isDirectory(); }; const pathExists = async (path2) => { try { await fs$1.access(path2); return true; } catch (error) { return false; } }; const ensurePackagePathIsViable = async (path2) => { const exists = await pathExists(path2); if (!exists) { await fs$1.mkdir(path2, { recursive: true }); } const isEmpty = await isEmptyDirectory(path2); if (!isEmpty) { throw new Error(`${path2} is not empty`); } const isDir = await isDirectory(path2); if (!isDir) { throw new Error(`${path2} is not a directory`); } }; const check = async (opts = {}) => { const { silent, debug, cwd = process.cwd() } = opts; const logger = createLogger({ silent, debug }); const packageJsonLoader = ora__default.default(`Verifying package.json ${os__default.default.EOL}`).start(); const rawPkg = await loadPkg({ cwd, logger }).catch((err) => { packageJsonLoader.fail(); logger.error(err.message); logger.debug(`Path checked – ${cwd}`); process.exit(1); }); const validatedPkg = await validatePkg({ pkg: rawPkg }).catch((err) => { packageJsonLoader.fail(); logger.error(err.message); process.exit(1); }); const packageJson = await validateExportsOrdering({ pkg: validatedPkg, logger }).catch((err) => { packageJsonLoader.fail(); logger.error(err.message); process.exit(1); }); packageJsonLoader.succeed("Verified package.json"); const config = await loadConfig({ cwd, logger }); const extMap = getExportExtensionMap(); const ctx = await createBuildContext({ config: { ...config }, cwd, extMap, logger, pkg: packageJson }).catch((err) => { logger.error(err.message); process.exit(1); }); logger.debug(`Build context: ${os__default.default.EOL}`, ctx); const missingExports = []; const checkingFilePathsLoader = ora__default.default("Checking files for exports").start(); for (const exp of Object.values(ctx.exports)) { if (exp.source && !await pathExists(path.resolve(ctx.cwd, exp.source))) { missingExports.push(exp.source); } if (exp.types && !await pathExists(path.resolve(ctx.cwd, exp.types))) { missingExports.push(exp.types); } if (exp.require && !await pathExists(path.resolve(ctx.cwd, exp.require))) { missingExports.push(exp.require); } if (exp.import && !await pathExists(path.resolve(ctx.cwd, exp.import))) { missingExports.push(exp.import); } if (exp.module && !await pathExists(path.resolve(ctx.cwd, exp.module))) { missingExports.push(exp.module); } if (exp.default && !await pathExists(path.resolve(ctx.cwd, exp.default))) { missingExports.push(exp.default); } if (exp.browser) { if (exp.browser.source && !await pathExists(path.resolve(ctx.cwd, exp.browser.source))) { missingExports.push(exp.browser.source); } if (exp.browser.import && !await pathExists(path.resolve(ctx.cwd, exp.browser.import))) { missingExports.push(exp.browser.import); } if (exp.browser.require && !await pathExists(path.resolve(ctx.cwd, exp.browser.require))) { missingExports.push(exp.browser.require); } } if (exp.node) { if (exp.node.source && !await pathExists(path.resolve(ctx.cwd, exp.node.source))) { missingExports.push(exp.node.source); } if (exp.node.import && !await pathExists(path.resolve(ctx.cwd, exp.node.import))) { missingExports.push(exp.node.import); } if (exp.node.require && !await pathExists(path.resolve(ctx.cwd, exp.node.require))) { missingExports.push(exp.node.require); } if (exp.node.module && !await pathExists(path.resolve(ctx.cwd, exp.node.module))) { missingExports.push(exp.node.module); } } } if (missingExports.length) { checkingFilePathsLoader.fail(""); logger.error( [ "Missing files for exports:", ...missingExports.map((str) => ` ${chalk__default.default.blue(str)} -> ${path.reso