UNPKG

rsbuild-plugin-dts

Version:

Rsbuild plugin that supports emitting declaration files for TypeScript.

97 lines (96 loc) 4.83 kB
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 };