typeroll
Version:
An extremely fast TypeScript declaration file generator and bundler that outputs to a single file.
745 lines (729 loc) • 28.5 kB
JavaScript
var import_node_module = require("node:module");
var __create = Object.create;
var __getProtoOf = Object.getPrototypeOf;
var __defProp = Object.defineProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __toESM = (mod, isNodeMode, target) => {
target = mod != null ? __create(__getProtoOf(mod)) : {};
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
for (let key of __getOwnPropNames(mod))
if (!__hasOwnProp.call(to, key))
__defProp(to, key, {
get: () => mod[key],
enumerable: true
});
return to;
};
var __moduleCache = /* @__PURE__ */ new WeakMap;
var __toCommonJS = (from) => {
var entry = __moduleCache.get(from), desc;
if (entry)
return entry;
entry = __defProp({}, "__esModule", { value: true });
if (from && typeof from === "object" || typeof from === "function")
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
get: () => from[key],
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
}));
__moduleCache.set(from, entry);
return entry;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, {
get: all[name],
enumerable: true,
configurable: true,
set: (newValue) => all[name] = () => newValue
});
};
// src/index.ts
var exports_src = {};
__export(exports_src, {
logIsolatedDeclarationErrors: () => logIsolatedDeclarationErrors,
generateDts: () => generateDts
});
module.exports = __toCommonJS(exports_src);
// src/isolated-decl-logger.ts
var import_picocolors = __toESM(require("picocolors"));
// src/utils.ts
var import_node_fs = require("node:fs");
var import_node_path = require("node:path");
var import_coffi = require("coffi");
var import_oxc_minify = require("oxc-minify");
var import_std_env = require("std-env");
// src/re.ts
var IMPORT_TYPE_RE = /import\s+type\s+/g;
var EXPORT_TYPE_RE = /export\s+type\s+/g;
var IMPORT_EXPORT_NAMES_RE = /(import|export)\s*{([^}]*)}/g;
var IMPORT_EXPORT_WITH_DEFAULT_RE = /(import|export)(\s+[^{,]+,)?\s*{([^}]*)}/g;
var TYPE_WORD_RE = /\btype\s+/g;
var EXPORT_DEFAULT_RE = /\bexport\s+default\s+/g;
var EXPORT_RE = /\bexport\s+/g;
var TOKENIZE_RE = /(\s+|\/\/.*?(?:\n|$)|\/\*[\s\S]*?\*\/|[a-zA-Z_$][a-zA-Z0-9_$]*|"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\])*`|\d+(?:\.\d*)?(?:[eE][+-]?\d+)?|[(){}\[\],.;:]|=>|&&|\|\||[=!<>]=?|\+\+|--|[-+*/%&|^!~?]|\.{3}|::|\.)/g;
var CAPITAL_LETTER_RE = /[A-Z]/;
var JS_RE = /\.[cm]?jsx?$/;
var TS_RE = /\.[cm]?tsx?$|\.d\.[cm]?ts$/;
var EXTENSION_REGEX = /\.(d\.(ts|cts|mts)|[cm]?[jt]s)$/;
var NODE_MODULES_RE = /node_modules/;
// src/utils.ts
function isTypeScriptFile(path2) {
if (!path2)
return false;
return TS_RE.test(path2);
}
function returnPathIfExists(path2) {
return import_node_fs.existsSync(path2) ? path2 : null;
}
function getExtension(filename) {
const match = filename.match(EXTENSION_REGEX);
if (!match)
return "";
const ext = match[0];
return ext;
}
function replaceExtension(filename, newExt) {
if (EXTENSION_REGEX.test(filename)) {
return filename.replace(EXTENSION_REGEX, newExt);
}
return filename + newExt;
}
function deleteExtension(filename) {
return filename.replace(EXTENSION_REGEX, "");
}
async function loadTsConfig(cwd, preferredPath) {
const config = await import_coffi.loadConfig({
name: "tsconfig",
extensions: [".json"],
preferredPath,
cwd
});
return config;
}
function getShortFilePath(filePath, maxLength = 3) {
const fileParts = filePath.split("/");
const shortPath = fileParts.slice(-maxLength).join("/");
return shortPath;
}
function generateRandomString(length = 10) {
return Array.from({ length }, () => String.fromCharCode(97 + Math.floor(Math.random() * 26))).join("");
}
function isDev() {
return import_std_env.isDevelopment || !import_std_env.isCI;
}
function isNullOrUndefined(value) {
return value === undefined || value === null;
}
function cleanPath(path2) {
let cleaned = import_node_path.normalize(path2).replace(/\\/g, "/");
cleaned = cleaned.replace(/^[a-zA-Z]:\//, "");
cleaned = cleaned.replace(/^\/+/, "");
cleaned = cleaned.replace(/\/+/g, "/");
return cleaned;
}
function getDeclarationExtensionFromJsExtension(ext) {
if (ext === ".mjs")
return ".d.mts";
if (ext === ".cjs")
return ".d.cts";
return ".d.ts";
}
async function getFilesFromGlobs(patterns, cwd) {
const includePatterns = patterns.filter((p) => !p.startsWith("!"));
const excludePatterns = patterns.filter((p) => p.startsWith("!")).map((p) => p.slice(1));
const includedFiles = new Set;
for (const pattern of includePatterns) {
const glob = new Bun.Glob(pattern);
for await (const file of glob.scan(cwd)) {
includedFiles.add(file);
}
}
if (excludePatterns.length > 0) {
for (const pattern of excludePatterns) {
const glob = new Bun.Glob(pattern);
for await (const file of glob.scan(cwd)) {
includedFiles.delete(file);
}
}
}
return Array.from(includedFiles);
}
function filterTypescriptFiles(files) {
return files.filter((file) => isTypeScriptFile(file));
}
function minifyDts(dts) {
return import_oxc_minify.minify(`${generateRandomString()}.d.ts`, dts, {
codegen: {
removeWhitespace: true
},
mangle: false,
compress: false,
sourcemap: false
}).code;
}
// src/isolated-decl-logger.ts
var SEVERITY_CONFIG = {
dev: {
Error: { color: import_picocolors.default.yellow, prefix: "WARN" },
Warning: { color: import_picocolors.default.yellow, prefix: "WARN" },
Advice: { color: import_picocolors.default.blue, prefix: "ADVICE" },
default: { color: import_picocolors.default.blue, prefix: "WARN" }
},
prod: {
Error: { color: import_picocolors.default.red, prefix: "Error" },
Warning: { color: import_picocolors.default.yellow, prefix: "Warning" },
Advice: { color: import_picocolors.default.blue, prefix: "Advice" },
default: { color: import_picocolors.default.red, prefix: "Error" }
}
};
var ISOLATED_DECLARATION_ERRORS = {
TS9007: `Function requires an explicit return type, e.g., \`function foo(): ${import_picocolors.default.green("string")} { ... }\`.`,
TS9008: `Method requires an explicit return type, e.g., \`myMethod(): ${import_picocolors.default.green("number")} { ... }\`.`,
TS9009: "Ensure at least one accessor (getter/setter) has an explicit return type.",
TS9010: `Variable requires an explicit type annotation, e.g., \`let name: ${import_picocolors.default.green("string")} = "Bob";\`.`,
TS9011: `Function parameter requires an explicit type, e.g., \`(param: ${import_picocolors.default.green("number")}) => {}\`.`,
TS9012: `Class property requires an explicit type, e.g., \`class MyClass { id: ${import_picocolors.default.green("number")}; }\`.`,
TS9013: "Expression type cannot be inferred. Add a type annotation where this expression is assigned or used.",
TS9014: "Computed property names must be simple (e.g., string/number literals or basic identifiers).",
TS9015: "Either add an explicit type to the object or avoid spread.",
TS9016: "Either add an explicit type to the object or use full `key: value` syntax.",
TS9017: `For array type inference, use \`as const\` (e.g., \`[1, 2, 3] ${import_picocolors.default.green("as const")}\`).`,
TS9018: "Either add an explicit type to the array or avoid spread.",
TS9019: `Avoid direct export of destructured bindings. Instead, declare and then export, e.g., \`const { x } = obj; ${import_picocolors.default.green("export { x };")}\`.`,
TS9020: "Enum member initializers must be simple, constant values (like numbers or strings), not expressions or external references.",
TS9021: "The `extends` clause must refer to a direct class name, not an expression.",
TS9022: `Class expressions cannot infer types. Assign the class expression to an explicitly typed variable, e.g., \`const MyClass: ${import_picocolors.default.green("typeof OtherClass")} = class { ... };\`.`,
TS9023: `Properties assigned to functions must be explicitly declared on the function type/interface, e.g., \`interface MyFunc { (): void; ${import_picocolors.default.green("myProp: string;")} }\`.`,
TS9025: `Parameter can implicitly be \`undefined\`. Explicitly add \`| undefined\` to its type, e.g., \`param?: string\` becomes \`param: ${import_picocolors.default.green("string | undefined")}\`.`,
TS9037: `Default export requires an explicit type, e.g., \`const MyValue: ${import_picocolors.default.green("number")} = 42; export default MyValue;\`.`,
TS9038: "Computed property names in class/object literals must be simple (string/number literals or plain identifiers), not complex expressions.",
TS9039: "A type references a private class member (`{name}`). Private members cannot be part of publicly exposed types."
};
function logIsolatedDeclarationErrors(errors) {
if (!errors.length)
return;
const hasErrors = errors.some(({ error }) => error.severity === "Error");
console.log();
errors.forEach(logSingle);
if (hasErrors) {
if (!isDev())
process.exit(1);
}
}
function logSingle({ error, file, content }) {
const label = error.labels?.[0];
const errorCode = extractErrorCode(error.message);
const errorMessage = ISOLATED_DECLARATION_ERRORS[errorCode];
if (!errorMessage)
return;
const position = label ? calculatePosition(content, label.start) : "";
const shortPath = getShortFilePath(file);
const { color, prefix } = getSeverityFormatting(error.severity);
const formattedMessage = `${color(prefix)} ${shortPath}${position}: ${errorCode} ${errorMessage}`;
const codeFrame = label ? generateOxcCodeFrame(content, label.start, label.end) : error.codeframe || "";
const helpMessage = error.helpMessage ? `
${import_picocolors.default.cyan("Help:")} ${error.helpMessage}` : "";
console.log(`${formattedMessage}${helpMessage}
${import_picocolors.default.gray(codeFrame)}
`);
}
function extractErrorCode(message) {
return message.split(":")[0];
}
function getSeverityFormatting(severity) {
const config = SEVERITY_CONFIG[isDev() ? "dev" : "prod"];
return config[severity] || config.default;
}
function calculatePosition(sourceText, labelStart) {
if (labelStart === undefined)
return "";
const lines = sourceText.slice(0, labelStart).split(`
`);
const lineNumber = lines.length;
const columnStart = lines[lines.length - 1].length + 1;
return ` (${lineNumber}:${columnStart})`;
}
function generateOxcCodeFrame(sourceText, start, end) {
const lines = sourceText.split(`
`);
const errorLine = sourceText.slice(0, start).split(`
`).length;
const lineContent = lines[errorLine - 1];
const lastNewlineIndex = sourceText.slice(0, start).lastIndexOf(`
`);
const startCol = start - lastNewlineIndex - 1;
const endCol = end ? Math.min(end - lastNewlineIndex - 1, lineContent.length) : startCol + 1;
const underlineLength = Math.max(1, endCol - startCol);
const arrowLine = " ".repeat(startCol) + import_picocolors.default.dim(import_picocolors.default.blue("⎯".repeat(underlineLength)));
return `${lineContent}
${arrowLine}`;
}
// src/generate.ts
var import_node_path3 = __toESM(require("node:path"));
var import_oxc_transform = require("oxc-transform");
var import_ts_import_resolver = require("ts-import-resolver");
// src/constants.ts
var EMPTY_EXPORT = "export {};";
// src/errors.ts
class TyperollError extends Error {
constructor(message) {
super(`typeroll: ${message}`);
}
}
// src/fake-js.ts
var import_parser = require("@babel/parser");
// src/ast.ts
function isLikelyVariableOrTypeName(token) {
return CAPITAL_LETTER_RE.test(token) && !token.startsWith("/*") && !token.startsWith("@") && !token.startsWith('"') && !token.startsWith("'") && !token.startsWith("`");
}
function isImportDeclaration(node) {
return node.type === "ImportDeclaration";
}
function removeExportSyntaxes(text) {
return text.replace(EXPORT_DEFAULT_RE, "").replace(EXPORT_RE, "");
}
function isExportAllDeclaration(node) {
return node.type === "ExportAllDeclaration";
}
function isReExportStatement(node) {
return node.type === "ExportNamedDeclaration" && !node.declaration;
}
function isSideEffectImport(node) {
return node.type === "ImportDeclaration" && node.specifiers.length === 0;
}
function hasExportModifier(node, text) {
return node.type.startsWith("Export") || text.trim().startsWith("export");
}
function hasDefaultExportModifier(node, text) {
return node.type === "ExportDefaultDeclaration" || text.trim().startsWith("export default");
}
function isDefaultReExport(node) {
return node.type === "ExportDefaultDeclaration" && node.declaration?.type === "Identifier";
}
function getName(node, source) {
if (!node)
return null;
if (node.type === "ExportNamedDeclaration" && node.declaration) {
return getName(node.declaration, source);
}
if (node.type === "ExportDefaultDeclaration" && node.declaration) {
if (node.declaration.type === "Identifier") {
return node.declaration.name;
}
return getName(node.declaration, source);
}
switch (node.type) {
case "TSInterfaceDeclaration":
case "TSTypeAliasDeclaration":
case "ClassDeclaration":
case "TSEnumDeclaration":
case "FunctionDeclaration":
case "TSDeclareFunction":
if (node.id && node.id.type === "Identifier") {
return node.id.name;
}
break;
case "TSModuleDeclaration":
if (node.id && node.id.type === "Identifier") {
return node.id.name;
}
break;
case "VariableDeclaration": {
const declarations = node.declarations;
if (declarations?.length === 1 && declarations[0].id?.type === "Identifier") {
return declarations[0].id.name;
}
break;
}
}
return null;
}
function getCommentText(comments) {
if (!comments)
return null;
return comments.map((comment) => {
return comment.type === "CommentBlock" ? `/*${comment.value}*/` : comment.type === "CommentLine" ? `//${comment.value}` : null;
}).join(`
`);
}
function getAllImportNames(body) {
const importNames = [];
for (const statement of body) {
if (isImportDeclaration(statement)) {
const importDecl = statement;
if (importDecl.specifiers) {
for (const specifier of importDecl.specifiers) {
if (specifier.type === "ImportDefaultSpecifier") {
importNames.push(specifier.local.name);
} else if (specifier.type === "ImportSpecifier") {
importNames.push(specifier.local.name);
} else if (specifier.type === "ImportNamespaceSpecifier") {
importNames.push(specifier.local.name);
}
}
}
}
}
return importNames;
}
// src/fake-js.ts
async function dtsToFakeJs(dtsContent) {
const parsed = import_parser.parse(dtsContent, {
sourceType: "module",
plugins: ["typescript"]
});
const referencedNames = new Set;
const exportedNames = new Set;
const result = [];
for (const name of getAllImportNames(parsed.program.body)) {
referencedNames.add(name);
}
for (const statement of parsed.program.body) {
if (isNullOrUndefined(statement.start) || isNullOrUndefined(statement.end)) {
continue;
}
const statementText = dtsContent.substring(statement.start, statement.end);
const name = getName(statement, dtsContent);
if (name) {
referencedNames.add(name);
}
const isDefaultExport = hasDefaultExportModifier(statement, statementText);
if (isDefaultExport) {
result.push(`export { ${name} as default };`);
if (isDefaultReExport(statement)) {
continue;
}
}
if (isImportDeclaration(statement) || isExportAllDeclaration(statement) || isReExportStatement(statement)) {
if (isSideEffectImport(statement)) {
continue;
}
const jsImportExport = jsifyImportExport(statementText);
result.push(jsImportExport);
continue;
}
let leadingComment = null;
leadingComment = getCommentText(statement.leadingComments);
let statementTextWithCommentsAttatched = `${leadingComment ? `${leadingComment}
` : ""}${statementText}`;
const isExported = hasExportModifier(statement, statementText);
if (isExported) {
statementTextWithCommentsAttatched = removeExportSyntaxes(statementTextWithCommentsAttatched);
}
const tokens = tokenizeText(statementTextWithCommentsAttatched, referencedNames);
const varName = name || generateRandomString();
result.push(`var ${varName} = [${tokens.join(", ")}];`);
if (isExported && !isDefaultExport && !exportedNames.has(varName)) {
result.push(`export { ${varName} };`);
exportedNames.add(varName);
}
}
return result.join(`
`);
}
async function fakeJsToDts(fakeJsContent) {
const parseResult = import_parser.parse(fakeJsContent, {
sourceType: "module",
attachComment: false
});
const program = parseResult.program;
const resultParts = [];
for (const statement of program.body) {
if (isNullOrUndefined(statement.start) || isNullOrUndefined(statement.end)) {
continue;
}
const statementText = fakeJsContent.substring(statement.start, statement.end);
if (isImportDeclaration(statement) || isExportAllDeclaration(statement) || isReExportStatement(statement)) {
if (isImportDeclaration(statement)) {
resultParts.push(statementText.replace(/.(?:mjs|cjs|js)\b/g, ""));
continue;
}
resultParts.push(statementText);
continue;
}
if (statement.type === "ExpressionStatement") {
const namespaceDecl = handleNamespace(statement);
if (namespaceDecl) {
resultParts.push(namespaceDecl);
continue;
}
}
if (statement.type === "VariableDeclaration") {
for (const declaration of statement.declarations) {
if (declaration.init?.type === "ArrayExpression") {
const dtsContent = processTokenArray(declaration.init);
if (dtsContent) {
resultParts.push(dtsContent);
}
}
}
}
}
return resultParts.join(`
`);
}
function jsifyImportExport(text) {
let result = text.replace(IMPORT_TYPE_RE, "import ").replace(EXPORT_TYPE_RE, "export ").replace(IMPORT_EXPORT_NAMES_RE, (_, keyword, names) => `${keyword} {${names.replace(TYPE_WORD_RE, "")}}`);
result = result.replace(IMPORT_EXPORT_WITH_DEFAULT_RE, (_, keyword, defaultPart = "", names = "") => {
const cleanedNames = names.replace(TYPE_WORD_RE, "");
return `${keyword}${defaultPart}{${cleanedNames}}`;
});
return result;
}
function tokenizeText(text, referencedNames) {
const tokens = [];
let match;
TOKENIZE_RE.lastIndex = 0;
while (true) {
match = TOKENIZE_RE.exec(text);
if (match === null)
break;
const token = match[0];
if (isLikelyVariableOrTypeName(token) || referencedNames.has(token)) {
tokens.push(token);
} else {
tokens.push(JSON.stringify(escapeNewlinesAndTabs(token)));
}
}
return tokens;
}
function processTokenArray(arrayLiteral) {
if (arrayLiteral.type !== "ArrayExpression") {
return null;
}
const tokens = [];
for (const element of arrayLiteral.elements) {
if (!element)
continue;
const processed = processTokenElement(element);
if (processed !== null) {
tokens.push(processed);
}
}
return tokens.join("");
}
function processTokenElement(element) {
if (element.type === "StringLiteral" && typeof element.value === "string") {
return unescapeNewlinesAndTabs(element.value);
}
if (element.type === "Identifier") {
return element.name;
}
if (element.type === "TemplateLiteral") {
const parts = [];
parts.push(unescapeNewlinesAndTabs(element.quasis[0]?.value?.raw || ""));
for (let i = 0;i < element.expressions.length; i++) {
const expr = element.expressions[i];
if (expr.type === "Identifier") {
parts.push(expr.name);
}
parts.push(unescapeNewlinesAndTabs(element.quasis[i + 1]?.value?.raw || ""));
}
return parts.join("");
}
return null;
}
function escapeNewlinesAndTabs(text) {
return text.replace(/\n/g, "__typeroll_intermediate_new__line__").replace(/\t/g, "__typeroll_intermediate__tab__");
}
function unescapeNewlinesAndTabs(text) {
return text.replace(/__typeroll_intermediate_new__line__/g, `
`).replace(/__typeroll_intermediate__tab__/g, "\t");
}
function handleNamespace(stmt) {
const expr = stmt.expression;
if (!expr || expr.type !== "CallExpression" || expr.callee?.type !== "Identifier" || expr.arguments?.length !== 2 || expr.arguments[0].type !== "Identifier" || expr.arguments[1].type !== "ObjectExpression") {
return null;
}
const namespaceName = expr.arguments[0].name;
const properties = expr.arguments[1].properties.filter((prop) => prop.type === "ObjectProperty").map((prop) => {
if (prop.type === "ObjectProperty" && prop.key.type === "Identifier" && prop.value.type === "ArrowFunctionExpression" && prop.value.body.type === "Identifier") {
const keyName = prop.key.name;
const returnName = prop.value.body.name;
return keyName === returnName ? keyName : `${returnName} as ${keyName}`;
}
return null;
}).filter(Boolean);
if (properties.length === 0) {
return null;
}
return `declare namespace ${namespaceName} {
export { ${properties.join(", ")} };
}`;
}
// src/resolver.ts
var import_node_path2 = require("node:path");
var import_node_process = __toESM(require("node:process"));
var import_oxc_resolver = require("oxc-resolver");
function createResolver({
tsconfig,
cwd = import_node_process.default.cwd(),
resolveOption
}) {
const resolver = new import_oxc_resolver.ResolverFactory({
mainFields: ["types", "typings", "module", "main"],
conditionNames: ["types", "typings", "import", "require"],
extensions: [".d.ts", ".d.mts", ".d.cts", ".ts", ".mts", ".cts"],
tsconfig: tsconfig ? { configFile: tsconfig, references: "auto" } : undefined
});
const resolutionCache = new Map;
return (importSource, importer) => {
if (importSource === "bun")
return null;
const cacheKey = `${importSource}:${importer || ""}`;
if (resolutionCache.has(cacheKey)) {
return resolutionCache.get(cacheKey) || null;
}
let shouldResolve = false;
if (resolveOption !== undefined) {
if (typeof resolveOption === "boolean") {
shouldResolve = resolveOption;
} else if (Array.isArray(resolveOption)) {
shouldResolve = resolveOption.some((resolver2) => {
if (typeof resolver2 === "string") {
return resolver2 === importSource;
}
return resolver2.test(importSource);
});
}
}
if (!shouldResolve) {
resolutionCache.set(cacheKey, null);
return null;
}
const directory = importer ? import_node_path2.dirname(importer) : cwd;
const resolution = resolver.sync(directory, importSource);
if (!resolution.path) {
resolutionCache.set(cacheKey, null);
return null;
}
const resolved = resolution.path;
if (JS_RE.test(resolved)) {
const dts = returnPathIfExists(resolved.replace(JS_RE, ".d.ts")) || returnPathIfExists(resolved.replace(JS_RE, ".d.mts")) || returnPathIfExists(resolved.replace(JS_RE, ".d.cts"));
const result2 = isTypeScriptFile(dts) ? dts : null;
resolutionCache.set(cacheKey, result2);
return result2;
}
const result = isTypeScriptFile(resolved) ? resolved : null;
resolutionCache.set(cacheKey, result);
return result;
};
}
// src/generate.ts
async function generateDts(entrypoints, options = {}) {
const { resolve, preferredTsConfigPath, naming } = options;
const cwd = options.cwd ? import_node_path3.default.resolve(options.cwd) : process.cwd();
const tsconfig = await loadTsConfig(cwd, preferredTsConfigPath);
const nonAbsoluteEntrypoints = entrypoints.filter((entrypoint) => !import_node_path3.default.isAbsolute(entrypoint));
const resolvedEntrypoints = await getFilesFromGlobs(nonAbsoluteEntrypoints, cwd);
const absoluteEntrypoints = entrypoints.filter((entrypoint) => import_node_path3.default.isAbsolute(entrypoint));
if (!filterTypescriptFiles([...resolvedEntrypoints, ...absoluteEntrypoints]).length) {
throw new TyperollError("One or more of the entrypoints you provided do not exist. Please check that each entrypoint points to a valid file.");
}
const collectedErrors = [];
const resolver = createResolver({
cwd,
resolveOption: resolve,
tsconfig: tsconfig.filepath
});
const fakeJsPlugin = {
name: "fake-js",
setup(build) {
build.onResolve({ filter: /.*/ }, (args) => {
if (!NODE_MODULES_RE.test(args.importer)) {
const resolved = import_ts_import_resolver.resolveTsImportPath({
importer: args.importer,
path: args.path,
cwd,
tsconfig: tsconfig.config
});
if (resolved && isTypeScriptFile(resolved)) {
return { path: resolved };
}
}
const resolvedFromNodeModules = resolver(args.path, args.importer);
if (resolvedFromNodeModules) {
return { path: resolvedFromNodeModules };
}
return {
path: args.path,
external: true
};
});
build.onLoad({ filter: /\.(ts|tsx|d\.ts|d\.mts|d\.cts)$/ }, async (args) => {
const sourceText = await Bun.file(args.path).text();
const declarationResult = import_oxc_transform.isolatedDeclaration(args.path, sourceText);
let fakeJsContent = "";
if (!collectedErrors.some((e) => e.file === args.path)) {
for (const error of declarationResult.errors) {
collectedErrors.push({
error,
file: args.path,
content: sourceText
});
}
}
if (declarationResult.code) {
fakeJsContent = await dtsToFakeJs(declarationResult.code);
} else {
fakeJsContent = EMPTY_EXPORT;
}
return {
loader: "js",
contents: fakeJsContent
};
});
}
};
const result = await Bun.build({
entrypoints: [
...filterTypescriptFiles(resolvedEntrypoints).map((entry) => import_node_path3.default.resolve(import_node_path3.default.join(cwd, entry))),
...filterTypescriptFiles(absoluteEntrypoints)
],
format: "esm",
target: "node",
naming,
splitting: options.splitting,
plugins: [fakeJsPlugin],
packages: "external",
minify: options.minify,
throw: false
});
if (!result.success) {
throw new TyperollError(`DTS bundling failed: ${result.logs}`);
}
const outputs = result.outputs.filter((output) => output.kind === "chunk" || output.kind === "entry-point");
const bundledFiles = [];
for (const output of outputs) {
const bundledFakeJsContent = await output.text();
const dtsContent = await fakeJsToDts(bundledFakeJsContent);
const entrypoint = output.kind === "entry-point" ? entrypoints[bundledFiles.length] : undefined;
const chunkFileName = output.kind === "chunk" ? replaceExtension(import_node_path3.default.basename(output.path), getDeclarationExtensionFromJsExtension(getExtension(output.path))) : undefined;
const outputPath = cleanPath(replaceExtension(cleanPath(output.path), getDeclarationExtensionFromJsExtension(getExtension(output.path))));
const treeshakedDts = import_oxc_transform.isolatedDeclaration(`${generateRandomString()}.d.ts`, dtsContent);
if (!treeshakedDts.code.length && !treeshakedDts.errors.length) {
continue;
}
if (treeshakedDts.errors.length && !treeshakedDts.code) {
throw new TyperollError(`DTS treeshaking failed for ${entrypoint || outputPath}
${JSON.stringify(treeshakedDts.errors, null, 2)}`);
}
bundledFiles.push({
kind: output.kind === "entry-point" ? "entry-point" : "chunk",
entrypoint,
chunkFileName,
outputPath,
dts: options.minify ? minifyDts(treeshakedDts.code) : treeshakedDts.code,
pathInfo: {
outputPathWithoutExtension: deleteExtension(outputPath),
ext: getExtension(outputPath)
}
});
}
return {
files: bundledFiles,
errors: collectedErrors
};
}