rsbuild-plugin-dts
Version:
Rsbuild plugin that supports emitting declaration files for TypeScript.
151 lines (150 loc) • 8.29 kB
JavaScript
import { logger } from "@rsbuild/core";
import { updateDeclarationMapContent, loadTypescript, processDtsFiles, color, renameDtsFile, getTimeCost } from "./188.js";
const logPrefixTsc = color.dim('[tsc]');
const createFormatHost = (ts)=>({
getCanonicalFileName: (path)=>path,
getCurrentDirectory: ts.sys.getCurrentDirectory.bind(ts.sys),
getNewLine: ()=>ts.sys.newLine
});
async function handleDiagnosticsAndProcessFiles(ts, formatHost, diagnostics, configPath, bundle, cwd, declarationDir, dtsExtension, redirect, rootDir, paths, banner, footer, name) {
const diagnosticMessages = [];
for (const diagnostic of diagnostics){
const message = ts.formatDiagnosticsWithColorAndContext([
diagnostic
], formatHost);
diagnosticMessages.push(message);
}
await processDtsFiles(bundle, cwd, declarationDir, dtsExtension, redirect, configPath, rootDir, paths, banner, footer);
if (diagnosticMessages.length) {
for (const message of diagnosticMessages)logger.error(logPrefixTsc, message);
const error = new Error(`Failed to generate declaration files. ${color.dim(`(${name})`)}`);
error.stack = '';
throw error;
}
}
async function emitDtsTsc(options, onComplete, bundle = false, isWatch = false, build = false) {
const start = Date.now();
const { configPath, tsConfigResult, declarationDir, name, dtsExtension, rootDir, cwd, banner, footer, paths, redirect } = options;
const ts = loadTypescript(cwd);
const formatHost = createFormatHost(ts);
const { options: rawCompilerOptions, fileNames, projectReferences } = tsConfigResult;
const compilerOptions = {
...rawCompilerOptions,
configFilePath: configPath,
noEmit: false,
declaration: true,
declarationDir,
emitDeclarationOnly: true
};
const createProgram = ts.createSemanticDiagnosticsBuilderProgram;
const reportDiagnostic = (diagnostic)=>{
logger.error(logPrefixTsc, ts.formatDiagnosticsWithColorAndContext([
diagnostic
], formatHost));
};
const reportWatchStatusChanged = async (diagnostic, _newLine, _options, errorCount)=>{
const message = `${ts.flattenDiagnosticMessageText(diagnostic.messageText, formatHost.getNewLine())} ${color.dim(`(${name})`)}`;
if (6031 === diagnostic.code || 6032 === diagnostic.code) logger.info(logPrefixTsc, message);
if (6194 === diagnostic.code) {
if (0 !== errorCount && errorCount) logger.error(logPrefixTsc, message);
else {
logger.info(logPrefixTsc, message);
onComplete(true);
}
await processDtsFiles(bundle, cwd, declarationDir, dtsExtension, redirect, configPath, rootDir, paths, banner, footer);
}
if (6193 === diagnostic.code) {
logger.error(logPrefixTsc, message);
await processDtsFiles(bundle, cwd, declarationDir, dtsExtension, redirect, configPath, rootDir, paths, banner, footer);
}
};
const system = {
...ts.sys,
writeFile: (fileName, contents, writeByteOrderMark)=>{
const newFileName = renameDtsFile(fileName, dtsExtension, bundle);
const newContents = updateDeclarationMapContent(fileName, contents, dtsExtension, bundle, compilerOptions.declarationMap);
ts.sys.writeFile(newFileName, newContents, writeByteOrderMark);
}
};
if (isWatch) if (build) {
const host = ts.createSolutionBuilderWithWatchHost(system, createProgram, reportDiagnostic, void 0, reportWatchStatusChanged);
const solutionBuilder = ts.createSolutionBuilderWithWatch(host, [
configPath
], compilerOptions, {
watch: true
});
solutionBuilder.build();
} else {
const host = ts.createWatchCompilerHost(configPath, compilerOptions, system, createProgram, reportDiagnostic, reportWatchStatusChanged);
ts.createWatchProgram(host);
}
else {
if (build || compilerOptions.composite) if (!build && compilerOptions.composite) {
const originHost = ts.createIncrementalCompilerHost(compilerOptions);
const host = {
...originHost,
writeFile: (fileName, contents, writeByteOrderMark, onError, sourceFiles)=>{
const newFileName = renameDtsFile(fileName, dtsExtension, bundle);
const newContents = updateDeclarationMapContent(fileName, contents, dtsExtension, bundle, compilerOptions.declarationMap);
originHost.writeFile(newFileName, newContents, writeByteOrderMark, onError, sourceFiles);
}
};
const program = ts.createIncrementalProgram({
rootNames: fileNames,
options: compilerOptions,
configFileParsingDiagnostics: ts.getConfigFileParsingDiagnostics(tsConfigResult),
projectReferences,
host,
createProgram
});
const allDiagnostics = [];
allDiagnostics.push(...program.getConfigFileParsingDiagnostics(), ...program.getSyntacticDiagnostics(), ...program.getOptionsDiagnostics(), ...program.getGlobalDiagnostics(), ...program.getSemanticDiagnostics(), ...program.getDeclarationDiagnostics());
const emitResult = program.emit();
allDiagnostics.push(...emitResult.diagnostics);
const sortAndDeduplicateDiagnostics = ts.sortAndDeduplicateDiagnostics(allDiagnostics);
await handleDiagnosticsAndProcessFiles(ts, formatHost, sortAndDeduplicateDiagnostics, configPath, bundle, cwd, declarationDir, dtsExtension, redirect, rootDir, paths, banner, footer, name);
} else {
let errorNumber = 0;
const reportErrorSummary = (errorCount)=>{
errorNumber = errorCount;
};
const host = ts.createSolutionBuilderHost(system, createProgram, reportDiagnostic, void 0, reportErrorSummary);
const solutionBuilder = ts.createSolutionBuilder(host, [
configPath
], compilerOptions);
solutionBuilder.build();
await processDtsFiles(bundle, cwd, declarationDir, dtsExtension, redirect, configPath, rootDir, paths, banner, footer);
if (errorNumber > 0) {
const error = new Error(`Failed to generate declaration files. ${color.dim(`(${name})`)}`);
error.stack = '';
throw error;
}
}
else {
const originHost = ts.createCompilerHost(compilerOptions);
const host = {
...originHost,
writeFile: (fileName, contents, writeByteOrderMark, onError, sourceFiles)=>{
const newFileName = renameDtsFile(fileName, dtsExtension, bundle);
const newContents = updateDeclarationMapContent(fileName, contents, dtsExtension, bundle, compilerOptions.declarationMap);
originHost.writeFile(newFileName, newContents, writeByteOrderMark, onError, sourceFiles);
}
};
const program = ts.createProgram({
rootNames: fileNames,
options: compilerOptions,
projectReferences,
host,
configFileParsingDiagnostics: ts.getConfigFileParsingDiagnostics(tsConfigResult)
});
const preEmitDiagnostics = ts.getPreEmitDiagnostics(program);
const emitResult = program.emit();
const allDiagnostics = preEmitDiagnostics.concat(emitResult.diagnostics);
const sortAndDeduplicateDiagnostics = ts.sortAndDeduplicateDiagnostics(allDiagnostics);
await handleDiagnosticsAndProcessFiles(ts, formatHost, sortAndDeduplicateDiagnostics, configPath, bundle, cwd, declarationDir, dtsExtension, redirect, rootDir, paths, banner, footer, name);
}
if (bundle) logger.info(`declaration files prepared in ${getTimeCost(start)} ${color.dim(`(${name})`)}`);
else logger.ready(`declaration files generated in ${getTimeCost(start)} ${color.dim(`(${name})`)}`);
}
}
export { emitDtsTsc };