UNPKG

@repka-kit/ts

Version:

Next generation build tools for monorepo: lint, bundle and package your TypeScript projects

1,714 lines (1,672 loc) 237 kB
import { basename, join, extname, isAbsolute, posix, resolve, normalize, relative, dirname } from 'node:path'; import virtual from '@rollup/plugin-virtual'; import { i as isTruthy } from './chunk.PjcLlUr0.js'; import { stat, readFile as readFile$1, chmod, readlink, realpath, mkdir, symlink, copyFile, unlink, readdir, writeFile as writeFile$1, rm } from 'node:fs/promises'; import { createFilter } from '@rollup/pluginutils'; import MagicString from 'magic-string'; import { o as once, g as getModuleRootDirectoryForImportMetaUrl, b as binPath } from './chunk.gqGVJwT_.js'; import { rollup, watch } from 'rollup'; import { l as logger, s as spawnToPromise } from './chunk.2EX25_CG.js'; import generatePackageJsonPlugin from 'rollup-plugin-generate-package-json'; import assert from 'node:assert'; import fg from 'fast-glob'; import commonjs from '@rollup/plugin-commonjs'; import json from '@rollup/plugin-json'; import resolve$1 from '@rollup/plugin-node-resolve'; import analyze from 'rollup-plugin-analyzer'; import { transform } from 'esbuild'; import { spawn } from 'node:child_process'; import { b as readPackageJson, r as readMonorepoPackagesGlobs, c as eslint, e as ensureEslintConfigFilesExist, g as readCwdPackageJson } from './chunk.7OqoG9sS.js'; import { e as escapeRegExp } from './chunk.dnsVc6Jo.js'; import { t as tscCompositeTypeCheckAt, a as tscCompositeTypeCheck } from './chunk.Ide2Qyoh.js'; import { a as jestIntegrationTests, b as jestUnitTests } from './chunk.gSglsk4Z.js'; import { c as configFilePath } from './chunk.1WcKPfKf.js'; import { performance } from 'node:perf_hooks'; async function allFulfilled(args) { const results = await Promise.allSettled(args); const resultsArr = results; return resultsArr.map((result) => { if (result.status === "rejected") { throw result.reason; } return result.value; }); } function hasOne(arr) { return arr.length > 0; } async function validateBinEntryPoints({ packageName, packageDirectory, bin }) { const binEntries = typeof bin === "string" && packageName ? Object.entries({ [basename(packageName)]: bin }) : typeof bin === "object" ? Object.entries(bin) : []; if (binEntries.length === 0) { return { binEntryPoints: [] }; } const validBins = []; const invalidBins = []; for (const [bin2, value] of binEntries) { const fileExists = await stat(join(packageDirectory, value)).then((result) => result.isFile()).catch(() => false); if (!fileExists) { logger.warn( `package.json "bin" prop is invalid: the key "${bin2}" points to a file "${value}" that does not exist.` ); invalidBins.push(bin2); continue; } const fileContents = await readFile$1(join(packageDirectory, value), "utf-8"); const firstLine = fileContents.split("\n")[0]; if (!firstLine || !firstLine.startsWith("#!/usr/bin/env ")) { logger.warn( `package.json "bin" prop is invalid: the key "${bin2}" points to a file "${value}" that does not have a shebang, ie "#!/usr/bin/env tsx". The shebang is required to be able to run the TypeScript file when the bin command is executed at dev-time.` ); invalidBins.push(bin2); continue; } validBins.push({ binName: bin2, sourceFilePath: value, format: value.endsWith(".cts") ? "cjs" : "esm" }); } return { binEntryPoints: validBins, ignoredBinEntryPoints: Object.fromEntries( binEntries.filter(([key]) => invalidBins.includes(key)) ) }; } function line(template, ...values) { return String.raw({ raw: template }, ...values).replaceAll(/\s*\n\s*/g, " ").trim(); } function isConditionsOnlyEntry(exports) { if (typeof exports !== "object" || !exports) { return false; } const startsWithDot = Object.keys(exports).some((key) => key.startsWith(".")); return !startsWithDot; } function resolvePackageJsonExportEntry(entry, exportConditions = ["types", "node", "default"]) { if (typeof entry === "string") { return entry; } if (typeof entry !== "object" || !entry) { return void 0; } const entries = Object.entries(entry); const conditionEntries = entries.filter(([key]) => !key.startsWith(".")); const nonConditions = entries.filter(([key]) => key.startsWith(".")); if (nonConditions.length > 0) { throw new Error( `Unexpected "exports" entry - found ${nonConditions.map(([key]) => `"${key}"`).join(", ")} but expected only conditions, ie "types", "node", "browser", "default", ... etc. See https://nodejs.org/api/packages.html#conditional-exports for more information` ); } for (const condition of exportConditions) { const result = conditionEntries.find(([key]) => key === condition); if (result) { const value = result[1]; if (typeof value === "string") { return value; } if (typeof value !== "object" || !value) { return void 0; } return resolvePackageJsonExportEntry(value, exportConditions); } } return void 0; } const determineName = (key = "main") => { const extension = extname(key); return key === "." ? "main" : [ ...new Set( key.replace(extension, "").replaceAll(/\.|\/|\*/g, "-").replaceAll(/-+/g, "-").replaceAll(/^-|-$/g, "").replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase().split("-") ) ].join("-"); }; const allowedExtensions = [ ".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs", ".cts", ".mts" ]; function changeExtension$1(path, newExtension) { const ext = extname(path); if (!ext) { return path; } return path.replaceAll( new RegExp(escapeRegExp(extname(path)) + "$", "g"), newExtension ); } function outputPathFromSourcePath(sourcePath) { if (isAbsolute(sourcePath)) { throw new Error("Should use relative paths only"); } const result = changeExtension$1( sourcePath.replace("./src/", "./dist/"), ".js" ); if (result.startsWith("./dist/")) { return result; } return "./" + posix.join("./dist", result); } const resolveEntryPoints = async (opts, deps = { fg: (pattern) => fg(pattern, { onlyFiles: true, cwd: opts.packageDirectory }), warn: (message) => logger.warn(message) }) => { const { results, ignored, exportEntry, entryPoint, outputRootDirectory } = opts; const chunkName = determineName(entryPoint); const addToIgnored = (warning) => { if (entryPoint) { ignored[entryPoint] = exportEntry; } else { Object.assign(ignored, exportEntry); } if (warning) { deps.warn(warning); } }; const addEntry = async (opts2) => { const { sourcePath: sourcePath2, outputPath: outputPath2 } = opts2; if (sourcePath2 === "./package.json") { addToIgnored(); return; } if (outputPath2.startsWith("./src/")) { addToIgnored( line` The "exports" entry "${entryPoint || "."}" output path "${outputPath2}" points to the "./src/" directory. Ignoring. ` ); return; } if (!outputPath2.startsWith(outputRootDirectory)) { addToIgnored( line` The "exports" entry "${entryPoint || "."}" output path "${outputPath2}" must start with "${outputRootDirectory}" directory. Ignoring. ` ); return; } const isGlob = sourcePath2.includes("*"); const entries = isGlob ? await deps.fg(sourcePath2) : [sourcePath2]; if (entries.length === 0) { addToIgnored( line` The "exports" entry "${entryPoint || "."}" doesn't match any files that can be bundled by the bundler. ` ); return; } const allowedEntries = entries.filter( (entry) => allowedExtensions.includes(extname(entry)) ); const otherEntries = entries.filter( (entry) => !allowedExtensions.includes(extname(entry)) ); if (otherEntries.length > 0) { addToIgnored( line` The "exports" entry "${entryPoint || "."}" matches files that might fail to bundle: ` + ["", ...otherEntries].join("\n - ") ); } for (const globTarget of allowedEntries) { const globChunkName = isGlob ? determineName([chunkName, globTarget].join("-")) : chunkName; const resolvedOutputPath = isGlob ? "./" + posix.join( changeExtension$1(outputPath2, "").replaceAll("*", "").replaceAll(/\/+/g, "/").replaceAll(/\/$/g, ""), changeExtension$1(basename(globTarget), ".js") ) : changeExtension$1(outputPath2, ".js"); if (sourcePath2 === resolvedOutputPath) { addToIgnored( line` The "exports" entry "${entryPoint || "."}" has both the input source file path and the output path resolve to the same file at "${resolvedOutputPath}". Ignoring. ` ); break; } const conflicts = [...Object.values(results)].filter((result) => { return result.outputPath === resolvedOutputPath; }); if (hasOne(conflicts)) { addToIgnored( line` The "exports" entry "${entryPoint || "."}" resolves to the same output path as another entry point "${conflicts[0].entryPoint}". Ignoring. ` ); break; } results[globChunkName] = { entryPoint: entryPoint || ".", sourcePath: globTarget, outputPath: resolvedOutputPath, chunkName: globChunkName }; } }; if (typeof exportEntry === "string") { await addEntry({ sourcePath: exportEntry, outputPath: outputPathFromSourcePath(exportEntry) }); return; } const sourcePath = resolvePackageJsonExportEntry(exportEntry, ["bundle"]); if (!sourcePath) { addToIgnored(); return; } const outputPathCandidate = resolvePackageJsonExportEntry(exportEntry, [ "default" ]); const outputPath = !outputPathCandidate || outputPathCandidate === sourcePath ? outputPathFromSourcePath(sourcePath) : outputPathCandidate; await addEntry({ sourcePath, outputPath }); }; async function validateEntryPoints(opts, deps = { fg: (pattern) => fg(pattern, { onlyFiles: true, cwd: opts.packageDirectory }), warn: (message) => logger.warn(message) }) { const results = {}; const ignored = {}; if (!opts.exportEntry) { return { entryPoints: [], ignoredEntryPoints: ignored }; } else if (typeof opts.exportEntry === "object") { if (isConditionsOnlyEntry(opts.exportEntry)) { await resolveEntryPoints( { results, ignored, exportEntry: opts.exportEntry, packageDirectory: opts.packageDirectory, outputRootDirectory: "./dist/" }, deps ); } else { for (const [key, entry] of Object.entries( opts.exportEntry )) { if (!entry) { ignored[key] = entry; continue; } if (typeof entry !== "string" && typeof entry !== "object") { ignored[key] = entry; deps.warn( line` Expected "string" or "object" as exports entry - got "${String(entry)}" ` ); continue; } await resolveEntryPoints( { results, ignored, exportEntry: entry, entryPoint: key, packageDirectory: opts.packageDirectory, outputRootDirectory: "./dist/" }, deps ); } } } else if (typeof opts.exportEntry === "string") { await resolveEntryPoints( { results, ignored, exportEntry: opts.exportEntry, packageDirectory: opts.packageDirectory, outputRootDirectory: "./dist/" }, deps ); } return { entryPoints: Object.values(results), ignoredEntryPoints: ignored }; } function validatePackageJson(packageJson) { const name = packageJson.name; if (!name) { throw new Error('"name" in package.json should be defined'); } const version = packageJson.version; if (!version) { throw new Error('"version" in package.json should be defined'); } const type = packageJson.type; if (type !== "module") { throw new Error('"type" in package.json should be "module"'); } const exports = packageJson.exports; if (!exports) { if (!packageJson.bin && !packageJson.main) { throw new Error( line` "exports" in package.json should be defined, for starters you can point it to your main entry point; ie "./src/index.ts". Alternatively, you can also define "bin" or "main" in package.json. ` ); } } if (packageJson.typings) { throw new Error( '"typings" in package.json should not be defined, use "types"' ); } return { ...packageJson, name, version, type, exports }; } async function tryBuildingPackageConfig(deps, customized) { const packageJson = await deps.readPackageJson(); if (!customized) { validatePackageJson(packageJson); } const name = packageJson.name; if (!name) { throw new Error('"name" of the package is not specified'); } const version = packageJson.version; if (!version) { throw new Error('"version" of the package is not specified'); } const entryPointsBuildResult = await deps.buildEntryPoints(); const binEntryPointsBuildResult = await deps.buildBinEntryPoints(); if (entryPointsBuildResult.entryPoints.length === 0 && binEntryPointsBuildResult.binEntryPoints.length === 0) { throw new Error( "The package doesn't have any entry points, nothing to bundle!" ); } return { name, version, dependencies: packageJson.dependencies || {}, devDependencies: packageJson.devDependencies || {}, ...entryPointsBuildResult, ...binEntryPointsBuildResult }; } async function loadNodePackageConfigs(opts) { const directory = (opts == null ? void 0 : opts.directory) || process.cwd(); const deps = { readPackageJson: () => readPackageJson(join(directory, "package.json")), buildConfig: () => tryBuildingPackageConfig(deps, false), buildEntryPoints: () => Promise.resolve(deps.readPackageJson()).then( (packageJson) => validateEntryPoints({ exportEntry: packageJson.exports || {}, packageDirectory: directory }) ), buildBinEntryPoints: () => Promise.resolve(deps.readPackageJson()).then( (packageJson) => validateBinEntryPoints({ packageName: packageJson.name, packageDirectory: directory, bin: packageJson.bin }) ) }; const customDeps = (opts == null ? void 0 : opts.packageConfig) ? opts.packageConfig(deps) : deps; return await tryBuildingPackageConfig(customDeps, customDeps !== deps); } const makeExecutable = async (binPath) => { const mode = await stat(binPath); await chmod(binPath, mode.mode | 73); }; const rollupMakeExecutablePlugin = () => { return { name: "makeExecutable", async writeBundle(options, bundle) { if (options.dir) { for (const key of Object.keys(bundle)) { const binPath = join(options.dir, key); await makeExecutable(binPath); } } else if (options.file) { const binPath = options.file; await makeExecutable(binPath); } } }; }; const rollupRemoveShebangPlugin = (opts) => { const filter = createFilter(opts == null ? void 0 : opts.include, opts == null ? void 0 : opts.exclude); return { name: "removeShebang", renderChunk(code, chunk, options) { if (!filter(chunk.fileName)) return null; const sourceMaps = Boolean(options.sourcemap); const result = { code, map: null }; if (code.startsWith("#!")) { const magicString = new MagicString(code).remove( 0, code.indexOf("\n") + 1 ); result.code = magicString.toString(); if (sourceMaps) { result.map = magicString.generateMap({ hires: true }); } } return result; } }; }; function buildBinInputs(entryPoints, sourceFilePath) { return entryPoints.reduce( (acc, entry) => ({ ...acc, [entry.binName]: sourceFilePath ? sourceFilePath(entry) : entry.sourceFilePath }), {} ); } function buildShebangBinsPlugins(entryPoints) { const bundledEsmBins = entryPoints.filter((entry) => entry.format === "esm"); const bundledEsmBinsInfo = new Map( bundledEsmBins.map(({ binName }) => [ `../${binName}.js`, { virtualModuleLocation: `./src/bin/${binName}.bundled.ts`, proxySourceCode: `#!/usr/bin/env node export * from "${`../${binName}.js`}";` } ]) ); const bundledEsmBinsPlugins = bundledEsmBins.length > 0 ? [ virtual( Object.fromEntries( [...bundledEsmBinsInfo.values()].map((entry) => [ entry.virtualModuleLocation, entry.proxySourceCode ]) ) ), { name: "resolve:bundledEsmBinModules", resolveId(source) { if (bundledEsmBinsInfo.has(source)) { return { id: source, external: true }; } return null; } } ] : []; return { plugins: bundledEsmBinsPlugins.filter(isTruthy) }; } function buildShebangBinsBundleConfig({ config, defaultRollupConfig }) { if (config.binEntryPoints.length === 0) { return { bundledEsmBins: [], bundledEsmBinsInputs: {}, bundledEsmBinsPlugins: [], binConfigs: [] }; } const binEntryPoints = config.binEntryPoints; const { plugins } = buildShebangBinsPlugins(binEntryPoints); const standard = defaultRollupConfig(); const removeExistingShebangPrefix = rollupRemoveShebangPlugin({ include: binEntryPoints.flatMap((entry) => `**/${entry.binName}.*`) }); const shared = { ...standard, plugins: [...plugins, standard.plugins] }; const bundledBinOutput = { ...shared.output, dir: "./dist/bin", plugins: [rollupMakeExecutablePlugin(), removeExistingShebangPrefix] }; const cjsBins = binEntryPoints.filter((entry) => entry.format === "cjs"); const esmBins = binEntryPoints.filter((entry) => entry.format === "esm"); const banner = `#!/usr/bin/env node`; const cjsConfig = cjsBins.length > 0 && { ...shared, input: buildBinInputs(cjsBins), output: [ { ...bundledBinOutput, format: "cjs", entryFileNames: `[name].cjs`, chunkFileNames: `chunk.[hash].cjs`, banner: (chunk) => chunk.isEntry ? banner : "" } ] }; const esmConfig = esmBins.length > 0 && { ...shared, input: buildBinInputs( esmBins, ({ binName }) => `./src/bin/${binName}.bundled.ts` ), output: [ { ...bundledBinOutput, format: "esm", entryFileNames: `[name].mjs`, chunkFileNames: `chunk.[hash].mjs`, banner: (chunk) => chunk.isEntry ? banner : "" } ] }; const bundledEsmBinsInputs = buildBinInputs(esmBins); return { binEntryPoints, /** * Bins that are part of main config which are bundled together with * main entry point to dedupe as much code as possible. We can only do that * for bins which are ESM and are not dependency-bin * * Entry points for the above bins to be merged with main entry points */ bundledEsmBinsInputs, bundledEsmBinsPlugins: [removeExistingShebangPrefix], /** * Extra rollup configs that represent following: * - CJS output that is not part of the main bundle * - ESM output, which redirects to the main bundle */ binConfigs: [cjsConfig, esmConfig].filter(isTruthy).filter((m) => m.input && Object.keys(m.input).length > 0) }; } const rollupCache = once(() => { return { modules: [], plugins: {} }; }); async function rollupBuild(opts) { const { output: outputProp, ...inputProps } = opts; const output = Array.isArray(outputProp) ? outputProp : outputProp ? [outputProp] : []; const builder = await rollup({ ...inputProps, cache: rollupCache() }); const results = await Promise.all(output.map((out) => builder.write(out))); if (builder.getTimings) { logger.log("Timings for", inputProps.input); logger.log(builder.getTimings()); } return results; } function transformPackageJson(opts) { const { entryPoints, ignoredEntryPoints, binEntryPoints, ignoredBinEntryPoints } = opts; const main = entryPoints.find((entry) => entry.chunkName === "main"); return (packageJson) => { const keys = [ "name", "version", "type", "license", "description", "author", "keywords", "bugs", "repository", "version", "type", "bin", "peerDependencies", "peerDependenciesMeta", "engines" ]; const copyValues = Object.fromEntries( Object.entries(packageJson).filter(([key]) => keys.includes(key)) ); const { name, version, type, ...rest } = copyValues; assert(!!name && typeof name === "string"); assert(!!version && typeof version === "string"); assert(!!type && typeof type === "string"); const bin = Object.fromEntries([ ...binEntryPoints.map( (bin2) => [ bin2.binName, `./bin/${bin2.binName}.${bin2.format === "cjs" ? "cjs" : "mjs"}` ] ), ...Object.entries(ignoredBinEntryPoints || {}) ]); const mainOutput = main ? "./" + posix.relative("./dist", main.outputPath) : void 0; const exports = entryPoints.length === 1 ? Object.entries(ignoredEntryPoints || {}).length === 0 ? mainOutput : { ...ignoredEntryPoints, ...mainOutput && { ".": mainOutput } } : entryPoints.reduce( (acc, entry) => ({ ...acc, [entry.entryPoint]: "./" + posix.relative("./dist", entry.outputPath) }), { ...opts.ignoredEntryPoints } ); const next = { name, version, type, ...rest, ..."main" in packageJson && mainOutput && { main: mainOutput }, ...Object.keys(bin).length > 0 && { bin }, ...(typeof exports === "string" || exports && Object.entries(exports).length > 0) && { exports } }; return next; }; } const rollupPackageJsonPlugin = (opts) => { return generatePackageJsonPlugin({ additionalDependencies: opts.externals, outputFolder: opts.outDir, baseContents: (packageJson) => { const result = transformPackageJson({ entryPoints: opts.entryPoints, ignoredEntryPoints: opts.ignoredEntryPoints, binEntryPoints: opts.binEntryPoints, ignoredBinEntryPoints: opts.ignoredBinEntryPoints })(packageJson); return opts.packageJson ? opts.packageJson(result) : result; } }); }; function entriesFromGlobs({ source, exclude, include, options }) { const entries = fg.stream( [...exclude ? exclude.map((glob) => `!${glob}`) : [], ...include], { followSymbolicLinks: false, ...options, onlyFiles: false, stats: true, objectMode: true, cwd: source, absolute: true } ); return entries; } function getDeps(opts) { var _a, _b; const rethrowToCaptureStackTrace = (err) => { throw Object.assign(new Error(err.message, { cause: err }), { code: err.code, path: err.path, errno: err.errno, syscall: err.syscall }); }; const normalDeps = { stat, realpath, readlink, mkdir, symlink, copyFile, unlink }; const dryRunDeps = { stat, realpath, readlink, mkdir: () => { return Promise.resolve(); }, symlink: () => { return Promise.resolve(); }, copyFile: () => { return Promise.resolve(); }, unlink: () => { return Promise.resolve(); } }; const nonVerboseDeps = ((_a = opts.options) == null ? void 0 : _a.dryRun) ? dryRunDeps : normalDeps; const verboseDeps = Object.fromEntries( Object.entries(nonVerboseDeps).map(([key, value]) => [ key, async (...args) => { let result; try { result = await value(...args); return result; } finally { if (typeof result !== "undefined") { logger.debug(key, ...args, "->", result); } else { logger.debug(key, ...args); } } } ]) ); const deps = ((_b = opts.options) == null ? void 0 : _b.verbose) ? verboseDeps : nonVerboseDeps; return Object.fromEntries( Object.entries(deps).map( ([key, fn]) => [ key, (...args) => fn(...args).catch(rethrowToCaptureStackTrace) ] ) ); } async function copyFiles(opts) { var _a; const deps = getDeps(opts); const createdDirs = /* @__PURE__ */ new Set(); const symlinkEntries = []; const source = resolve(opts.source || "."); for await (const entry of entriesFromGlobs(opts)) { if ((_a = opts.options) == null ? void 0 : _a.dryRun) { console.log("found entry", entry); } const { stats } = entry; const sourcePath = normalize(entry.path); const targetPath = join(opts.destination, relative(source, sourcePath)); if (stats.isSymbolicLink()) { symlinkEntries.push(entry); } else if (stats.isFile()) { const targetDirectory = dirname(targetPath); if (!stats.isDirectory() && !createdDirs.has(targetDirectory)) { await deps.mkdir(targetDirectory, { recursive: true }); createdDirs.add(targetDirectory); } await deps.copyFile(sourcePath, targetPath).catch(async (err) => { const handle = async (handleCase = "throw") => { if (handleCase === "overwrite") { await deps.unlink(targetPath); await deps.copyFile(sourcePath, targetPath); return Promise.resolve(); } else if (handleCase === "ignore") { return Promise.resolve(); } else { return Promise.reject(err); } }; if (err.code === "EACCES" || err.code === "EPERM") { return handle(opts.accessError); } if (err.code === "EEXIST") { return handle(opts.existsError); } return Promise.reject(err); }); } else if (stats.isDirectory()) { await deps.mkdir(targetPath, { recursive: true }); createdDirs.add(targetPath); } else ; } const realSource = await deps.realpath(source); for (const entry of symlinkEntries) { const sourcePath = normalize(entry.path); const linkPath = join(opts.destination, relative(source, sourcePath)); const link = await deps.readlink(sourcePath); const realLinkTarget = await deps.realpath(sourcePath).catch((err) => { if (err.code === "ENOENT") { return Promise.resolve(null); } else { return Promise.reject(err); } }); const realLinkStats = process.platform === "win32" && realLinkTarget ? await deps.stat(realLinkTarget) : void 0; const symlinkType = (stats) => stats.isDirectory() ? "dir" : "file"; const linkTargetIsWithinSourceDir = realLinkTarget ? realLinkTarget.startsWith(realSource) : sourcePath.startsWith(source); const isExistingLinkDifferent = async () => { const existingLink = await readlink(linkPath); if (isAbsolute(existingLink) && isAbsolute(link)) { if (relative(opts.destination, existingLink) !== relative(source, link)) { return true; } } else if (normalize(existingLink) !== normalize(link)) { return true; } return false; }; const targetWithinDest = isAbsolute(link) ? join(opts.destination, relative(source, link)) : link; const target = linkTargetIsWithinSourceDir ? targetWithinDest : ( // no way but to create a symlink to the target // outside destination or try to create the broken link realLinkTarget || sourcePath ); await deps.symlink( target, linkPath, realLinkStats ? symlinkType(realLinkStats) : void 0 ).catch(async (err) => { const handle = async (handleCase = "throw") => { if (handleCase === "overwrite") { await deps.unlink(linkPath); await deps.symlink( target, linkPath, realLinkStats ? symlinkType(realLinkStats) : void 0 ); return Promise.resolve(); } else if (handleCase === "ignore") { return Promise.resolve(); } else { return Promise.reject(err); } }; if (err.code === "EEXIST" && await isExistingLinkDifferent()) { return handle(opts.existsError); } }); } } const rollupPluginCopy = (opts) => { return { name: "copy", async buildStart() { const entries = fg.stream(opts.include, { followSymbolicLinks: false, ...opts.options }); for await (const entry of entries) { this.addWatchFile(entry); } }, async buildEnd() { await copyFiles({ ...opts }); } }; }; async function rollupWatch(configs) { const tasks = /* @__PURE__ */ new Set(); const addTask = (task) => { const promise = task.finally(() => { tasks.delete(promise); }); tasks.add(promise); }; const watcher = watch(configs); watcher.on("event", (ev) => { if (ev.code === "BUNDLE_END") { addTask(ev.result.close()); } if (ev.code === "ERROR" && ev.result) { addTask(ev.result.close()); } }); try { process.on("SIGINT", () => { addTask(watcher.close()); }); await new Promise((res) => { const stop = () => { watcher.off("close", stop); res(); }; watcher.on("close", stop); }); await Promise.all([...tasks]); } finally { await watcher.close(); } } function getDefaultExportFromCjs (x) { return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; } var require$$0 = [ "assert", "async_hooks", "buffer", "child_process", "cluster", "console", "constants", "crypto", "dgram", "diagnostics_channel", "dns", "domain", "events", "fs", "http", "http2", "https", "inspector", "module", "net", "os", "path", "perf_hooks", "process", "punycode", "querystring", "readline", "repl", "stream", "string_decoder", "timers", "tls", "trace_events", "tty", "url", "util", "v8", "vm", "wasi", "worker_threads", "zlib" ]; var _static = require$$0; var nodeBuiltins = /*@__PURE__*/getDefaultExportFromCjs(_static); const allBuiltins = once( () => nodeBuiltins.flatMap((builtin) => [builtin, `node:${builtin}`]).concat(["fs/promises", "node:fs/promises"]) ); const resolveNodeBuiltinsPlugin = () => { return { name: "node:builtins", resolveId(source) { if (allBuiltins().includes(source)) { return { id: !source.startsWith("node:") ? `node:${source}` : source, external: true }; } return null; } }; }; const defaultOptions = { treeShaking: true }; function esbuild({ include, exclude, ...options }) { options = { ...defaultOptions, ...options }; const filter = createFilter(include, exclude); return { name: "esbuild", async transform(src, id) { if (!filter(id)) { return null; } options.sourcefile = id; const { code, map } = await transform(src, options); return { code, map }; } }; } async function fileExists$1(filePath) { try { return (await stat(filePath)).isFile(); } catch (err) { return false; } } async function isDirectory(filePath) { try { return (await stat(filePath)).isDirectory(); } catch (err) { return false; } } const resolvedFilesCache = /* @__PURE__ */ new Map(); async function resolveFilePath(file, extensions2, resolveIndex) { try { if (resolvedFilesCache.has(file)) return resolvedFilesCache.get(file); const name = basename(file); const dir = dirname(file); const files = await readdir(dir); if (files.includes(name) && await fileExists$1(file)) { resolvedFilesCache.set(file, file); return file; } for (const ext of extensions2) { const fileName = `${name}${ext}`; const filePath = `${file}${ext}`; if (files.includes(fileName) && await fileExists$1(filePath)) { resolvedFilesCache.set(file, filePath); return filePath; } } if (resolveIndex === true && await isDirectory(file)) { const files2 = await readdir(file); for (const ext of extensions2) { const fileName = `index${ext}`; const filePath = resolve(file, fileName); if (files2.includes(fileName) && await fileExists$1(filePath)) { resolvedFilesCache.set(file, filePath); return filePath; } } } } catch (err) { console.error(err); } return null; } const DEFAULT_EXTENSIONS = [".mjs", ".js"]; function extensions({ extensions: extensions2 = DEFAULT_EXTENSIONS, resolveIndex = true } = {}) { if (extensions2.length === 0) { throw new Error(`[rollup-plugin-extensions] Extensions must be a non-empty array of strings. e.g "{ extensions: ['.ts, '.jsx', '.jsx'] }" `); } return { name: "extensions", resolveId(id, parent) { if (isAbsolute(id)) { return resolveFilePath(resolve(id), extensions2, resolveIndex); } if (parent === void 0) { return resolveFilePath( resolve(process.cwd(), id), extensions2, resolveIndex ); } if (id[0] !== ".") return null; return resolveFilePath( // local file path resolve(dirname(parent), id), extensions2, resolveIndex ); } }; } function combineArrays(key, before, after) { if (!before && !after) { return {}; } return { ...(before || after) && { [key]: [...before ? before : [], ...after ? after : []].filter( isTruthy ) } }; } const combineExternalProp = (before, after) => combineArrays("external", before, after); const combinePluginsProp = (before, after) => combineInputPluginsOptionsProp(before, after); const combineInputPluginsOptionsProp = (before, after) => { return { plugins: [before, after] }; }; function combineDefaultRollupConfigBuildOpts(before, after) { if (before === after) { return before; } return { ...before, ...after, ...combineExternalProp(before.external, after == null ? void 0 : after.external), ...combineInputPluginsOptionsProp(before.plugins, after == null ? void 0 : after.plugins) }; } function defaultRollupConfig(opts) { const config = { plugins: plugins(opts), external: (opts == null ? void 0 : opts.external) ? opts.external : [], output: { sourcemap: "inline", chunkFileNames: `chunk.[hash].js`, entryFileNames: `[name].js`, format: "esm" } }; return config; } const declareRollupPlugin = (plugin) => plugin; const plugins = (opts) => { const resolveIdFn = opts == null ? void 0 : opts.resolveId; return [ opts == null ? void 0 : opts.plugins, resolveIdFn && declareRollupPlugin({ name: "rollupNodeConfig:resolveId", async resolveId(id, importer, options) { return await Promise.resolve( resolveIdFn.bind(this)(id, importer, options) ); } }), resolveNodeBuiltinsPlugin(), resolve$1({ exportConditions: ["node"] }), commonjs({ ignoreTryCatch: true }), json(), extensions({ extensions: [".ts", ".tsx", ".cts", ".mts"] }), esbuild({ target: "node16", sourcemap: true, minify: (opts == null ? void 0 : opts.minify) ?? false, format: "esm", loader: "tsx", include: ["**/*.tsx"] }), esbuild({ target: "node16", sourcemap: true, minify: (opts == null ? void 0 : opts.minify) ?? false, format: "esm", loader: "ts", include: ["**/*.ts", "**/*.cts", "**/*.mts"] }), // swc({ // module: { // type: 'es6', // }, // jsc: { // parser: { // syntax: 'typescript', // }, // target: 'es2022', // }, // sourceMaps: true, // minify: true, // }), (opts == null ? void 0 : opts.analyze) && analyze({ summaryOnly: true, limit: 5 }) ]; }; function declareTask(opts) { return opts; } const externalsFromDependencies = (dependenciesParam, opts) => { const dependencies = Object.keys(dependenciesParam || {}); return [.../* @__PURE__ */ new Set([...dependencies, ...(opts == null ? void 0 : opts.externals) || []])]; }; function buildForNode(opts) { return declareTask({ name: "build", args: opts, execute: async () => { const config = await loadNodePackageConfigs(opts); const { entryPoints, ignoredEntryPoints, ignoredBinEntryPoints } = config; const allExternals = externalsFromDependencies(config.dependencies, opts); const baseOpts = { external: allExternals, resolveId: opts == null ? void 0 : opts.resolveId, plugins: opts == null ? void 0 : opts.plugins }; const defaultConfig = (opts2) => defaultRollupConfig( combineDefaultRollupConfigBuildOpts(baseOpts, opts2) ); const { binConfigs, bundledEsmBinsInputs, bundledEsmBinsPlugins, binEntryPoints } = buildShebangBinsBundleConfig({ config, defaultRollupConfig: defaultConfig }); const extraConfigs = (opts == null ? void 0 : opts.extraRollupConfigs) ? await Promise.resolve( opts.extraRollupConfigs({ config, defaultRollupConfig: defaultConfig }) ) : []; const buildExportsConfig = (rollupOpts) => { const config2 = defaultConfig(rollupOpts); return { ...config2, input: { ...Object.fromEntries( Object.values(entryPoints).map( ({ chunkName, sourcePath }) => [chunkName, sourcePath] ) ), ...bundledEsmBinsInputs }, output: { ...config2.output, dir: "./dist", entryFileNames: (chunk) => { const entryPoint = entryPoints.find( (entry) => entry.chunkName === chunk.name ); const outputPath = entryPoint == null ? void 0 : entryPoint.outputPath; return outputPath ? posix.relative("./dist", outputPath) : "[name].js"; } }, ...combinePluginsProp(config2.plugins, [ rollupPackageJsonPlugin({ outDir: "./dist", entryPoints, ignoredEntryPoints, binEntryPoints: binEntryPoints || [], ignoredBinEntryPoints, externals: allExternals, packageJson: opts == null ? void 0 : opts.outputPackageJson }), ...((opts == null ? void 0 : opts.copy) || []).map((opts2) => rollupPluginCopy(opts2)), ...bundledEsmBinsPlugins ]) }; }; const exportsConfig = (opts == null ? void 0 : opts.buildExportsConfig) ? await Promise.resolve( opts.buildExportsConfig({ config, defaultRollupConfig: buildExportsConfig }) ) : [buildExportsConfig()]; const configs = [...exportsConfig, ...binConfigs, ...extraConfigs]; await allFulfilled(configs.map((config2) => rollupBuild(config2))); return configs; }, watch: async (configs) => { await rollupWatch(configs); } }); } async function loadTsConfigJson(packageDirectory = process.cwd()) { const content = await readFile$1( join(packageDirectory, "tsconfig.json"), "utf-8" ); const parsed = JSON.parse(content); if (typeof parsed !== "object" || parsed === null) { throw new Error( `Error when trying to load "tsconfig.json", expected an object but got ${typeof parsed}` ); } const compilerOptions = parsed["compilerOptions"]; if (typeof compilerOptions !== "object" || compilerOptions === null) { return {}; } const outDir = compilerOptions["outDir"]; if (typeof outDir !== "string") { return { compilerOptions: {} }; } return { compilerOptions: { outDir } }; } function changeExtension(path, newExtension) { const ext = extname(path); if (!ext) { return path; } return path.replaceAll( new RegExp(escapeRegExp(extname(path)) + "$", "g"), newExtension ); } function extractPackageName([key, value]) { if (value.startsWith("workspace:")) { const result = /workspace:(.*)@(.*)/.exec(value); if (result) { const [, packageName] = result; if (packageName) { return packageName; } } } return key; } async function loadMonorepoDependencies(packageDirectory = process.cwd()) { const { root, packagesGlobs } = await readMonorepoPackagesGlobs(); const packageJson = await readPackageJson( join(packageDirectory, "package.json") ); if (packagesGlobs.length === 0) { return []; } const packageJsons = fg.stream( packagesGlobs.map((glob) => `${glob}/package.json`), { cwd: root } ); const searchingPackageGlobsFinished = new Promise((res, rej) => { packageJsons.on("end", res); packageJsons.on("error", rej); }); const tasks = []; const runTask = (cb) => { return (...args) => { tasks.push(cb(...args)); }; }; const unresolvedDependencies = new Map( [ ...Object.entries(packageJson.dependencies || {}), ...Object.entries(packageJson.devDependencies || {}) ].map((dependency) => { const packageName = extractPackageName(dependency); const aliasName = dependency[0]; return [ packageName, { packageName, aliasName } ]; }) ); const resolvedDependencies = []; packageJsons.on( "data", runTask(async (relativePath) => { const packageJsonFile = join(root, relativePath); const otherPackageJson = await readPackageJson(packageJsonFile); if (unresolvedDependencies.size === 0) { return; } if (!otherPackageJson.name) { logger.warn( `Package json at path ${packageJsonFile} doesn't have a name!` ); return; } const resolvedPackage = unresolvedDependencies.get(otherPackageJson.name); if (!resolvedPackage) { return; } unresolvedDependencies.delete(otherPackageJson.name); const packageDirectory2 = dirname(packageJsonFile); resolvedDependencies.push({ packageDirectory: packageDirectory2, ...resolvedPackage }); }) ); await searchingPackageGlobsFinished.then(() => Promise.allSettled(tasks)); return resolvedDependencies; } async function declarationsViaDtsBundleGenerator(opts) { var _a; const [tsConfig, packageConfig, dependencies] = await Promise.all([ loadTsConfigJson(), loadNodePackageConfigs(opts), loadMonorepoDependencies() ]); const dependencyOutDirs = await Promise.all( [ ...dependencies.filter( (dependency) => !(opts == null ? void 0 : opts.unstable_skipDependencies) || !opts.unstable_skipDependencies.includes(dependency.packageName) ).map((directory) => directory.packageDirectory), process.cwd() ].map( (directory) => Promise.all([ loadTsConfigJson(directory), tscCompositeTypeCheckAt(directory, { exitCodes: (opts == null ? void 0 : opts.unstable_ignoreTypeScriptErrors) ? [0] : "any" }) ]).then(([tsconfig]) => { var _a2; return (_a2 = tsconfig.compilerOptions) == null ? void 0 : _a2.outDir; }) ) ); const entryPoints = packageConfig.entryPoints; const outDir = ((_a = tsConfig.compilerOptions) == null ? void 0 : _a.outDir) || ".tsc-out"; const dtsBundleGeneratorConfigFile = { compilationOptions: { preferredConfigPath: "./tsconfig.json", compilerOptions: { composite: false, baseUrl: ".", paths: Object.fromEntries( dependencies.map((dep, i) => [ dep.aliasName, [ relative( process.cwd(), join( dep.packageDirectory, dependencyOutDirs[i] || ".tsc-out", "src" ) ) ] ]) ) } }, entries: entryPoints.map((entry) => { const input = join(outDir, entry.sourcePath); const output = join(changeExtension(entry.outputPath, ".d.ts")); return { filePath: input, outFile: output, libraries: { inlinedLibraries: Object.keys(packageConfig.devDependencies).filter( (f) => !f.startsWith("@types") ) }, noCheck: opts == null ? void 0 : opts.unstable_ignoreTypeScriptErrors }; }) }; await runDtsBundleGeneratorViaStdIn(dtsBundleGeneratorConfigFile); } async function runDtsBundleGeneratorViaStdIn(config) { const { type, path } = getModuleRootDirectoryForImportMetaUrl({ importMetaUrl: import.meta.url }); const child = spawn( process.execPath, type === "bundled" ? [join(path, "./bin/dts-bundle-generator.cjs")] : [ await binPath({ binName: "tsx", binScriptPath: "tsx/dist/cli.mjs" }), join(path, "./src/bin/dts-bundle-generator.cts") ], { cwd: process.cwd(), stdio: ["pipe", "inherit", "inherit"], env: { ...process.env, LOG_LEVEL: logger.logLevel } } ); child.stdin.setDefaultEncoding("utf-8"); const writeToStdin = () => new Promise((res, rej) => { child.stdin.write(JSON.stringify(config), (err) => { if (err) { rej(err); } else { child.stdin.end(res); } }); }); await Promise.all([ writeToStdin(), spawnToPromise(child, { cwd: process.cwd(), exitCodes: "inherit" }) ]); } async function fileExists(path) { return stat(path).then((result) => result.isFile()).catch(() => false); } async function readFile(path) { return await readFile$1(path, { encoding: "utf-8" }); } async function writeFile(path, data) { await writeFile$1(path, data, "utf-8"); } async function ensureTsConfigExists(opts, deps = { fileExists, readFile, writeFile }) { const directory = (opts == null ? void 0 : opts.directory) ?? process.cwd(); if (!(opts == null ? void 0 : opts.directory)) { const cwdPackageJsonPath = join(directory, "package.json"); const packageJsonExists = await deps.fileExists(cwdPackageJsonPath); if (!packageJsonExists) { return; } } const path = join(directory, "tsconfig.json"); const configExists = await deps.fileExists(path); if (configExists) { return; } const text = await deps.readFile(configFilePath("tsconfig.pkg.json")); await deps.writeFile(path, text); } function declarations(opts) { return declareTask({ name: "declarations", args: void 0, execute: async () => { await ensureTsConfigExists(); await declarationsViaDtsBundleGenerator(opts); } }); } function integrationTest(opts) { return declareTask({ name: "integration", args: void 0, execute: async () => { const args = (opts == null ? void 0 : opts.processArgs) ?? process.argv.slice(2); const isHelpMode = args.includes("-h") || args.includes("--help"); if (isHelpMode) { await jestIntegrationTests(args); return; } const testsDir = await stat("./src/__integration__").catch(() => null); if (!testsDir || !testsDir.isDirectory()) { logger.info( `There is nothing to test here it seems, integrations tests are expected in "./src/__integration__" directory` ); return; } await jestIntegrationTests(args); } }); } function lint(opts) { return declareTask({ name: "lint", args: void 0, execute: async () => { const args = (opts == null ? void 0 : opts.processArgs) ?? process.argv.slice(2); const isEslintHelpMode = args.includes("-h") || args.includes("--help"); if (isEslintHelpMode) { await eslint(["--help"]); return; } await allFulfilled([ ensureTsConfigExists().then(() => tscCompositeTypeCheck()), ensureEslintConfigFilesExist().then(() => eslint(args)) ]); } }); } let FORCE_COLOR, NODE_DISABLE_COLORS, NO_COLOR, TERM, isTTY=true; if (typeof process !== 'undefined') { ({ FORCE_COLOR, NODE_DISABLE_COLORS, NO_COLOR, TERM } = process.env || {}); isTTY = process.stdout && process.stdout.isTTY; } const $ = { enabled: !NODE_DISABLE_COLORS && NO_COLOR == null && TERM !== 'dumb' && ( FORCE_COLOR != null && FORCE_COLOR !== '0' || isTTY ) }; function init(x, y) { let rgx = new RegExp(`\\x1b\\[${y}m`, 'g'); let open = `\x1b[${x}m`, close = `\x1b[${y}m`; return function (txt) { if (!$.enabled || txt == null) return txt; return open + (!!~(''+txt).indexOf(close) ? txt.replace(rgx, close + open) : txt) + close; }; } const bold = init(1, 22); const red = init(31, 39); const green = init(32, 39); const yellow = init(33, 39); const blue = init(34, 39); const white = init(37, 39); // background colors const bgBlack = init(40, 49); async function rmrfDist(root = process.cwd()) { const pkgStat = await stat(join(root, "./package.json")); if (!pkgStat.isFile()) { throw new Error('Expected current directory to contain "package.json"');