vite-plugin-dts-build
Version:
A Vite plugin that runs TypeScript build process in a separate worker thread for better performance and efficient incremental builds
183 lines (182 loc) • 5.69 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 {
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;
const buildOptions = {
dry: false,
force: false,
verbose: false,
stopBuildOnErrors: true,
...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 stringToStringArray(value) {
return typeof value === "string" ? [value] : value;
}
function errFormatDiagnostic(diagnostic) {
return formatDiagnostic(diagnostic, {
getCanonicalFileName: (fileName) => fileName,
getCurrentDirectory: sys.getCurrentDirectory,
getNewLine: () => sys.newLine
});
}