diff --git a/packages/plugin-dts/src/index.ts b/packages/plugin-dts/src/index.ts index 3a1cf7260..757d1d9a9 100644 --- a/packages/plugin-dts/src/index.ts +++ b/packages/plugin-dts/src/index.ts @@ -3,7 +3,8 @@ import { dirname, extname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { logger, type RsbuildConfig, type RsbuildPlugin } from '@rsbuild/core'; import color from 'picocolors'; -import ts from 'typescript'; +import type { ParsedCommandLine } from 'typescript'; + import { cleanDtsFiles, cleanTsBuildInfoFile, @@ -11,6 +12,7 @@ import { getDtsEmitPath, loadTsconfig, processSourceEntry, + ts, warnIfOutside, } from './utils'; @@ -61,7 +63,7 @@ export type DtsGenOptions = Omit & { dtsEmitPath: string; build?: boolean; tsconfigPath: string; - tsConfigResult: ts.ParsedCommandLine; + tsConfigResult: ParsedCommandLine; userExternals?: NonNullable['externals']; apiExtractorOptions?: ApiExtractorOptions; }; diff --git a/packages/plugin-dts/src/tsc.ts b/packages/plugin-dts/src/tsc.ts index 9bf9e1545..521ecbb37 100644 --- a/packages/plugin-dts/src/tsc.ts +++ b/packages/plugin-dts/src/tsc.ts @@ -1,17 +1,27 @@ import { logger } from '@rsbuild/core'; import color from 'picocolors'; -import ts from 'typescript'; +import type { + CompilerHost, + CompilerOptions, + Diagnostic, + FormatDiagnosticsHost, + ParsedCommandLine, + Program, + System, + WatchStatusReporter, +} from 'typescript'; import type { DtsRedirect } from './index'; import { getTimeCost, processDtsFiles, renameDtsFile, + ts, updateDeclarationMapContent, } from './utils'; const logPrefixTsc = color.dim('[tsc]'); -const formatHost: ts.FormatDiagnosticsHost = { +const formatHost: FormatDiagnosticsHost = { getCanonicalFileName: (path) => path, getCurrentDirectory: ts.sys.getCurrentDirectory.bind(ts.sys), getNewLine: () => ts.sys.newLine, @@ -21,7 +31,7 @@ export type EmitDtsOptions = { name: string; cwd: string; configPath: string; - tsConfigResult: ts.ParsedCommandLine; + tsConfigResult: ParsedCommandLine; declarationDir: string; dtsExtension: string; rootDir: string; @@ -32,7 +42,7 @@ export type EmitDtsOptions = { }; async function handleDiagnosticsAndProcessFiles( - diagnostics: readonly ts.Diagnostic[], + diagnostics: readonly Diagnostic[], configPath: string, bundle: boolean, declarationDir: string, @@ -117,17 +127,17 @@ export async function emitDtsTsc( const createProgram = ts.createSemanticDiagnosticsBuilderProgram; - const reportDiagnostic = (diagnostic: ts.Diagnostic) => { + const reportDiagnostic = (diagnostic: Diagnostic) => { logger.error( logPrefixTsc, ts.formatDiagnosticsWithColorAndContext([diagnostic], formatHost), ); }; - const reportWatchStatusChanged: ts.WatchStatusReporter = async ( - diagnostic: ts.Diagnostic, + const reportWatchStatusChanged: WatchStatusReporter = async ( + diagnostic: Diagnostic, _newLine: string, - _options: ts.CompilerOptions, + _options: CompilerOptions, errorCount?: number, ) => { const message = `${ts.flattenDiagnosticMessageText( @@ -179,7 +189,7 @@ export async function emitDtsTsc( } }; - const system: ts.System = { + const system: System = { ...ts.sys, writeFile: (fileName, contents, writeByteOrderMark) => { const newFileName = renameDtsFile(fileName, dtsExtension, bundle); @@ -198,9 +208,8 @@ export async function emitDtsTsc( if (!isWatch) { // normal build - npx tsc if (!build && !compilerOptions.composite) { - const originHost: ts.CompilerHost = - ts.createCompilerHost(compilerOptions); - const host: ts.CompilerHost = { + const originHost: CompilerHost = ts.createCompilerHost(compilerOptions); + const host: CompilerHost = { ...originHost, writeFile: ( fileName, @@ -227,7 +236,7 @@ export async function emitDtsTsc( }, }; - const program: ts.Program = ts.createProgram({ + const program: Program = ts.createProgram({ rootNames: fileNames, options: compilerOptions, projectReferences, @@ -257,9 +266,9 @@ export async function emitDtsTsc( ); } else if (!build && compilerOptions.composite) { // incremental build with composite true - npx tsc - const originHost: ts.CompilerHost = + const originHost: CompilerHost = ts.createIncrementalCompilerHost(compilerOptions); - const host: ts.CompilerHost = { + const host: CompilerHost = { ...originHost, writeFile: ( fileName, @@ -296,7 +305,7 @@ export async function emitDtsTsc( createProgram, }); - const allDiagnostics: ts.Diagnostic[] = []; + const allDiagnostics: Diagnostic[] = []; allDiagnostics.push( ...program.getConfigFileParsingDiagnostics(), ...program.getSyntacticDiagnostics(), diff --git a/packages/plugin-dts/src/utils.ts b/packages/plugin-dts/src/utils.ts index 23126694b..c08c2b136 100644 --- a/packages/plugin-dts/src/utils.ts +++ b/packages/plugin-dts/src/utils.ts @@ -1,6 +1,6 @@ import fs from 'node:fs'; import fsP from 'node:fs/promises'; - +import { createRequire } from 'node:module'; import { platform } from 'node:os'; import path, { basename, @@ -12,15 +12,33 @@ import path, { relative, resolve, } from 'node:path'; +import { fileURLToPath } from 'node:url'; import { type NapiConfig, parseAsync } from '@ast-grep/napi'; import { logger, type RsbuildConfig } from '@rsbuild/core'; import MagicString from 'magic-string'; import color from 'picocolors'; import { convertPathToPattern, glob } from 'tinyglobby'; import { createMatchPath, loadConfig, type MatchPath } from 'tsconfig-paths'; -import ts from 'typescript'; +import type { + CompilerOptions, + Diagnostic, + ParsedCommandLine, +} from 'typescript'; import type { DtsEntry, DtsRedirect } from './index'; +const __filename = fileURLToPath(import.meta.url); +const require = createRequire(__filename); + +/** + * Currently, typescript only provides a CJS bundle, so we use require to load it + * for better startup performance. If we use `import ts from 'typescript'`, + * Node.js will use `cjs-module-lexer` to parse it, which slows down startup time. + */ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const ts = require('typescript') as typeof import('typescript'); + +export { ts }; + const JS_EXTENSIONS: string[] = [ 'js', 'mjs', @@ -40,7 +58,7 @@ export const JS_EXTENSIONS_PATTERN: RegExp = new RegExp( `\\.(${JS_EXTENSIONS.join('|')})$`, ); -export function loadTsconfig(tsconfigPath: string): ts.ParsedCommandLine { +export function loadTsconfig(tsconfigPath: string): ParsedCommandLine { const configFile = ts.readConfigFile( tsconfigPath, ts.sys.readFile.bind(ts.sys), @@ -139,10 +157,7 @@ export async function clearTempDeclarationDir(cwd: string): Promise { await emptyDir(dirPath); } -export function getFileLoc( - diagnostic: ts.Diagnostic, - configPath: string, -): string { +export function getFileLoc(diagnostic: Diagnostic, configPath: string): string { if (diagnostic.file) { const { line, character } = ts.getLineAndCharacterOfPosition( diagnostic.file, @@ -605,7 +620,7 @@ export async function cleanDtsFiles(dir: string): Promise { export async function cleanTsBuildInfoFile( tsconfigPath: string, - compilerOptions: ts.CompilerOptions, + compilerOptions: CompilerOptions, ): Promise { const tsconfigDir = dirname(tsconfigPath); const { outDir, rootDir, tsBuildInfoFile } = compilerOptions;