rsbuild-plugin-dts
Version:
Rsbuild plugin that supports emitting declaration files for TypeScript.
97 lines (96 loc) • 4.83 kB
JavaScript
import { logger } from "@rsbuild/core";
import { spawn } from "node:child_process";
import node_path from "node:path";
import { pathToFileURL } from "node:url";
import { processDtsFiles, createRequireFromPackageJson, rewriteDtsExtensions, color, getTimeCost } from "./188.js";
const logPrefixTsgo = color.dim('[tsgo]');
const TYPESCRIPT_PACKAGE_NAME = "typescript";
const NATIVE_PREVIEW_PACKAGE_NAME = "@typescript/native-preview";
const getDtsExecutablePath = async (cwd, packageName)=>{
let packageJsonPath;
try {
packageJsonPath = createRequireFromPackageJson(cwd).resolve(`${packageName}/package.json`);
} catch {
if (packageName === NATIVE_PREVIEW_PACKAGE_NAME) throw new Error('Failed to resolve @typescript/native-preview. Install "typescript@rc" or "@typescript/native-preview" to use TypeScript Go.');
throw new Error('Failed to resolve typescript. Install "typescript@rc" to use TypeScript Go.');
}
const libPath = node_path.resolve(node_path.dirname(packageJsonPath), './lib/getExePath.js');
let fileUrl;
fileUrl = 'win32' === process.platform ? pathToFileURL(libPath).href : libPath;
return import(fileUrl).then((mod)=>{
const getExePath = mod.default;
return getExePath();
});
};
const resolveDtsExecutableCommand = async (dtsBackend, args, cwd)=>{
const dtsExecutableFile = await getDtsExecutablePath(cwd, 'tsc-executable' === dtsBackend ? TYPESCRIPT_PACKAGE_NAME : NATIVE_PREVIEW_PACKAGE_NAME);
return {
command: dtsExecutableFile,
args,
displayCommand: `${dtsExecutableFile} ${args.join(' ')}`
};
};
const generateTsgoArgs = (configPath, declarationDir, build, isWatch)=>{
const args = [];
if (build) args.push('--build', configPath);
else {
args.push('--project', configPath);
args.push('--declarationDir', declarationDir);
}
args.push('--noEmit', 'false');
args.push('--declaration');
args.push('--emitDeclarationOnly');
return args;
};
async function handleDiagnosticsAndProcessFiles(isWatch, hasErrors, tsConfigResult, configPath, bundle, cwd, declarationDir, dtsExtension, redirect, rootDir, paths, banner, footer, name) {
await rewriteDtsExtensions(cwd, declarationDir, dtsExtension, bundle, tsConfigResult.options.declarationMap);
await processDtsFiles(bundle, cwd, declarationDir, dtsExtension, redirect, configPath, rootDir, paths, banner, footer);
if (hasErrors && !isWatch) {
const error = new Error(`Failed to generate declaration files. ${color.dim(`(${name})`)}`);
error.stack = '';
throw error;
}
}
async function emitDtsTsgo(options, _onComplete, bundle = false, isWatch = false, build = false) {
const start = Date.now();
const { configPath, tsConfigResult, declarationDir, name, dtsExtension, rootDir, banner, footer, paths, redirect, cwd, dtsBackend } = options;
const args = generateTsgoArgs(configPath, declarationDir, build, isWatch);
const dtsExecutableCommand = await resolveDtsExecutableCommand(dtsBackend, args, cwd);
logger.debug(logPrefixTsgo, `Running: ${dtsExecutableCommand.displayCommand}`);
return new Promise((resolve, reject)=>{
const childProcess = spawn(dtsExecutableCommand.command, [
...dtsExecutableCommand.args,
'--pretty'
], {
cwd,
stdio: [
'inherit',
'pipe',
'pipe'
]
});
let hasErrors = false;
childProcess.stdout?.on('data', (data)=>{
const output = data.toString();
const lines = output.split('\n');
for (const line of lines)if (line.trim()) logger.log(color.reset(`${logPrefixTsgo} ${line}`));
});
childProcess.stderr?.on('data', (data)=>{
const output = data.toString();
const lines = output.split('\n').filter((line)=>line.trim());
for (const line of lines)logger.error(logPrefixTsgo, line);
});
childProcess.on('close', async (code)=>{
try {
if (0 !== code) hasErrors = true;
await handleDiagnosticsAndProcessFiles(isWatch, hasErrors, tsConfigResult, configPath, bundle, cwd, declarationDir, dtsExtension, redirect, rootDir, paths, banner, footer, name);
if (!hasErrors) if (bundle) logger.info(`declaration files prepared with tsgo in ${getTimeCost(start)} ${color.dim(`(${name})`)}`);
else logger.ready(`declaration files generated with tsgo in ${getTimeCost(start)} ${color.dim(`(${name})`)}`);
resolve(hasErrors);
} catch (error) {
reject(error instanceof Error ? error : new Error(String(error)));
}
});
});
}
export { emitDtsTsgo };