vite-plugin-dts-build
Version:
Fast .d.ts builds for Vite (worker + incremental) with optional dual ESM/CJS support.
215 lines (214 loc) • 6.99 kB
JavaScript
;
const node_path = require("node:path");
const node_worker_threads = require("node:worker_threads");
const node_process = require("node:process");
const ts = require("typescript");
const fsExtra = require("fs-extra");
const log = require("../log-BqpMRi9a.cjs");
const {
readConfigFile,
parseJsonConfigFileContent,
createProgram,
createEmitAndSemanticDiagnosticsBuilderProgram,
createSolutionBuilderHost,
createSolutionBuilder,
getParsedCommandLineOfConfigFile,
getPreEmitDiagnostics,
sys,
formatDiagnostic,
getLineAndCharacterOfPosition,
flattenDiagnosticMessageText
} = ts;
const PROJECT_ROOT = node_process.cwd();
const WORKER_DATA = node_worker_threads.workerData;
const tsconfigPath = node_path.resolve(
WORKER_DATA.tsconfigPath ?? node_path.join(PROJECT_ROOT, "tsconfig.json")
);
const configFile = readConfigFile(tsconfigPath, sys.readFile);
if (configFile.error) {
throw new Error(errFormatDiagnostic(configFile.error));
}
if (WORKER_DATA.include) {
configFile.config.include = stringToStringArray(WORKER_DATA.include);
}
if (WORKER_DATA.exclude) {
configFile.config.exclude = stringToStringArray(WORKER_DATA.exclude);
}
const parsedConfig = parseJsonConfigFileContent(
configFile.config,
sys,
node_path.dirname(tsconfigPath)
);
if (parsedConfig.errors.length > 0) {
parsedConfig.errors.forEach((error) => {
reportDiagnostic(error);
});
throw new Error("Failed to parse tsconfig.json");
}
const compilerOptions = {
incremental: true,
// assumeChangesOnlyAffectDirectDependencies: false,
declaration: true,
// declarationMap: false,
emitDeclarationOnly: true,
// sourceMap: false,
// inlineSourceMap: false,
// traceResolution: false,
...parsedConfig.options,
...WORKER_DATA.compilerOptions ?? {}
};
const distDir = WORKER_DATA.outDir ?? compilerOptions.declarationDir ?? compilerOptions.outDir ?? node_path.join(PROJECT_ROOT, "dist");
const cacheDir = WORKER_DATA.cacheDir ?? node_path.join(PROJECT_ROOT, ".tsBuildCache");
compilerOptions.declarationDir = cacheDir;
if (shouldWarnSourceMapDepth()) {
warnIfSourceMapDepthMismatch();
}
const buildOptions = {
dry: false,
force: false,
verbose: false,
stopBuildOnErrors: false,
...WORKER_DATA.buildOptions ?? {}
};
const checkerMode = compilerOptions?.noEmit === true || buildOptions?.dry === true;
if (WORKER_DATA.mode === "compile") {
(async () => await runCompile())();
} else {
(async () => await runBuild())();
}
async function runCompile() {
const program = createProgram({
rootNames: parsedConfig.fileNames,
options: compilerOptions
});
const emitResult = program.emit();
const allDiagnostics = getPreEmitDiagnostics(program).concat(
emitResult.diagnostics
);
allDiagnostics.forEach((diagnostic) => {
if (diagnostic.file && diagnostic.start !== void 0) {
const { line, character } = getLineAndCharacterOfPosition(
diagnostic.file,
diagnostic.start
);
const message = flattenDiagnosticMessageText(
diagnostic.messageText,
"\n"
);
console.error(
`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`
);
} else {
console.error(flattenDiagnosticMessageText(diagnostic.messageText, "\n"));
}
});
const exitCode = emitResult.emitSkipped ? 1 : 0;
return await copyToDist(exitCode);
}
async function runBuild() {
const host = createSolutionBuilderHost(
sys,
createBuilderProgram,
reportDiagnostic,
reportSolutionBuilderStatus,
reportErrorSummary
);
const parseConfigHost = {
fileExists: sys.fileExists,
readFile: sys.readFile,
readDirectory: sys.readDirectory,
useCaseSensitiveFileNames: sys.useCaseSensitiveFileNames,
getCurrentDirectory: sys.getCurrentDirectory,
onUnRecoverableConfigFileDiagnostic: reportDiagnostic
};
host.getParsedCommandLine = (fileName) => getParsedCommandLineOfConfigFile(
fileName,
compilerOptions,
parseConfigHost
);
const builder = createSolutionBuilder(host, [tsconfigPath], buildOptions);
const exitCode = builder.build();
return await copyToDist(exitCode);
}
function createBuilderProgram(rootNames, options, host, oldProgram, configFileParsingDiagnostics, projectReferences) {
return createEmitAndSemanticDiagnosticsBuilderProgram(
rootNames,
{ ...options ?? {}, ...compilerOptions },
host,
oldProgram,
configFileParsingDiagnostics,
projectReferences
);
}
function reportDiagnostic(diagnostic) {
console.error(errFormatDiagnostic(diagnostic));
}
function reportSolutionBuilderStatus(diagnostic) {
console.info(errFormatDiagnostic(diagnostic));
}
function reportErrorSummary(errorCount) {
if (errorCount !== 0) {
console.error(`${errorCount} errors occurred.`);
}
}
function copyToDist(exitCode) {
return new Promise((resolve2, reject) => {
if (exitCode === 0) {
if (checkerMode) {
node_worker_threads.parentPort?.postMessage("check-end");
resolve2();
} else {
node_worker_threads.parentPort?.postMessage("build-end");
node_worker_threads.parentPort?.once("message", () => {
fsExtra.copy(`${cacheDir}/`, `${distDir}/`).then(() => {
node_worker_threads.parentPort?.close();
resolve2();
}).catch((error) => {
console.error("failed:", error);
reject(error);
});
});
}
} else {
reject();
}
});
}
function shouldWarnSourceMapDepth() {
const sourceMapEnabled = Boolean(
compilerOptions.sourceMap || compilerOptions.inlineSourceMap || compilerOptions.declarationMap
);
return sourceMapEnabled;
}
function warnIfSourceMapDepthMismatch() {
const roots = parsedConfig.fileNames;
const entry = roots[0];
if (entry == null) return;
const depthFromJs = computeRelativeDepth(entry, distDir);
const depthFromTs = computeRelativeDepth(entry, cacheDir);
if (depthFromJs !== depthFromTs) {
log.printWarn(
`SourceMap relative path depth mismatch: distDir -> entry depth(${depthFromJs}) !== cacheDir -> entry depth(${depthFromTs}).
This may cause broken relative source paths inside *.map files after copying. Consider aligning directory nesting.
distDir: ${node_path.relative(entry, distDir)}
cacheDir: ${node_path.relative(entry, cacheDir)}`
);
}
}
function computeRelativeDepth(fromFile, toDir) {
const relPath = node_path.relative(fromFile, toDir);
if (relPath === "") return 0;
const segments = relPath.split(node_path.sep);
const dirSegments = segments.slice(0, -1).filter((seg) => seg !== "" && seg !== ".");
return dirSegments.length;
}
function stringToStringArray(value) {
return typeof value === "string" ? [value] : value;
}
function errFormatDiagnostic(diagnostic) {
return formatDiagnostic(diagnostic, {
getCanonicalFileName: (fileName) => fileName,
getCurrentDirectory: sys.getCurrentDirectory,
getNewLine: () => sys.newLine
});
}