@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
JavaScript
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"');