Skip to content

Commit d09f7d6

Browse files
authored
perf(plugin-dts): require typescript to improve startup performance (#1224)
1 parent d50c33c commit d09f7d6

File tree

3 files changed

+52
-26
lines changed

3 files changed

+52
-26
lines changed

packages/plugin-dts/src/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@ import { dirname, extname, join } from 'node:path';
33
import { fileURLToPath } from 'node:url';
44
import { logger, type RsbuildConfig, type RsbuildPlugin } from '@rsbuild/core';
55
import color from 'picocolors';
6-
import ts from 'typescript';
6+
import type { ParsedCommandLine } from 'typescript';
7+
78
import {
89
cleanDtsFiles,
910
cleanTsBuildInfoFile,
1011
clearTempDeclarationDir,
1112
getDtsEmitPath,
1213
loadTsconfig,
1314
processSourceEntry,
15+
ts,
1416
warnIfOutside,
1517
} from './utils';
1618

@@ -61,7 +63,7 @@ export type DtsGenOptions = Omit<PluginDtsOptions, 'bundle'> & {
6163
dtsEmitPath: string;
6264
build?: boolean;
6365
tsconfigPath: string;
64-
tsConfigResult: ts.ParsedCommandLine;
66+
tsConfigResult: ParsedCommandLine;
6567
userExternals?: NonNullable<RsbuildConfig['output']>['externals'];
6668
apiExtractorOptions?: ApiExtractorOptions;
6769
};

packages/plugin-dts/src/tsc.ts

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,27 @@
11
import { logger } from '@rsbuild/core';
22
import color from 'picocolors';
3-
import ts from 'typescript';
3+
import type {
4+
CompilerHost,
5+
CompilerOptions,
6+
Diagnostic,
7+
FormatDiagnosticsHost,
8+
ParsedCommandLine,
9+
Program,
10+
System,
11+
WatchStatusReporter,
12+
} from 'typescript';
413
import type { DtsRedirect } from './index';
514
import {
615
getTimeCost,
716
processDtsFiles,
817
renameDtsFile,
18+
ts,
919
updateDeclarationMapContent,
1020
} from './utils';
1121

1222
const logPrefixTsc = color.dim('[tsc]');
1323

14-
const formatHost: ts.FormatDiagnosticsHost = {
24+
const formatHost: FormatDiagnosticsHost = {
1525
getCanonicalFileName: (path) => path,
1626
getCurrentDirectory: ts.sys.getCurrentDirectory.bind(ts.sys),
1727
getNewLine: () => ts.sys.newLine,
@@ -21,7 +31,7 @@ export type EmitDtsOptions = {
2131
name: string;
2232
cwd: string;
2333
configPath: string;
24-
tsConfigResult: ts.ParsedCommandLine;
34+
tsConfigResult: ParsedCommandLine;
2535
declarationDir: string;
2636
dtsExtension: string;
2737
rootDir: string;
@@ -32,7 +42,7 @@ export type EmitDtsOptions = {
3242
};
3343

3444
async function handleDiagnosticsAndProcessFiles(
35-
diagnostics: readonly ts.Diagnostic[],
45+
diagnostics: readonly Diagnostic[],
3646
configPath: string,
3747
bundle: boolean,
3848
declarationDir: string,
@@ -117,17 +127,17 @@ export async function emitDtsTsc(
117127

118128
const createProgram = ts.createSemanticDiagnosticsBuilderProgram;
119129

120-
const reportDiagnostic = (diagnostic: ts.Diagnostic) => {
130+
const reportDiagnostic = (diagnostic: Diagnostic) => {
121131
logger.error(
122132
logPrefixTsc,
123133
ts.formatDiagnosticsWithColorAndContext([diagnostic], formatHost),
124134
);
125135
};
126136

127-
const reportWatchStatusChanged: ts.WatchStatusReporter = async (
128-
diagnostic: ts.Diagnostic,
137+
const reportWatchStatusChanged: WatchStatusReporter = async (
138+
diagnostic: Diagnostic,
129139
_newLine: string,
130-
_options: ts.CompilerOptions,
140+
_options: CompilerOptions,
131141
errorCount?: number,
132142
) => {
133143
const message = `${ts.flattenDiagnosticMessageText(
@@ -179,7 +189,7 @@ export async function emitDtsTsc(
179189
}
180190
};
181191

182-
const system: ts.System = {
192+
const system: System = {
183193
...ts.sys,
184194
writeFile: (fileName, contents, writeByteOrderMark) => {
185195
const newFileName = renameDtsFile(fileName, dtsExtension, bundle);
@@ -198,9 +208,8 @@ export async function emitDtsTsc(
198208
if (!isWatch) {
199209
// normal build - npx tsc
200210
if (!build && !compilerOptions.composite) {
201-
const originHost: ts.CompilerHost =
202-
ts.createCompilerHost(compilerOptions);
203-
const host: ts.CompilerHost = {
211+
const originHost: CompilerHost = ts.createCompilerHost(compilerOptions);
212+
const host: CompilerHost = {
204213
...originHost,
205214
writeFile: (
206215
fileName,
@@ -227,7 +236,7 @@ export async function emitDtsTsc(
227236
},
228237
};
229238

230-
const program: ts.Program = ts.createProgram({
239+
const program: Program = ts.createProgram({
231240
rootNames: fileNames,
232241
options: compilerOptions,
233242
projectReferences,
@@ -257,9 +266,9 @@ export async function emitDtsTsc(
257266
);
258267
} else if (!build && compilerOptions.composite) {
259268
// incremental build with composite true - npx tsc
260-
const originHost: ts.CompilerHost =
269+
const originHost: CompilerHost =
261270
ts.createIncrementalCompilerHost(compilerOptions);
262-
const host: ts.CompilerHost = {
271+
const host: CompilerHost = {
263272
...originHost,
264273
writeFile: (
265274
fileName,
@@ -296,7 +305,7 @@ export async function emitDtsTsc(
296305
createProgram,
297306
});
298307

299-
const allDiagnostics: ts.Diagnostic[] = [];
308+
const allDiagnostics: Diagnostic[] = [];
300309
allDiagnostics.push(
301310
...program.getConfigFileParsingDiagnostics(),
302311
...program.getSyntacticDiagnostics(),

packages/plugin-dts/src/utils.ts

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import fs from 'node:fs';
22
import fsP from 'node:fs/promises';
3-
3+
import { createRequire } from 'node:module';
44
import { platform } from 'node:os';
55
import path, {
66
basename,
@@ -12,15 +12,33 @@ import path, {
1212
relative,
1313
resolve,
1414
} from 'node:path';
15+
import { fileURLToPath } from 'node:url';
1516
import { type NapiConfig, parseAsync } from '@ast-grep/napi';
1617
import { logger, type RsbuildConfig } from '@rsbuild/core';
1718
import MagicString from 'magic-string';
1819
import color from 'picocolors';
1920
import { convertPathToPattern, glob } from 'tinyglobby';
2021
import { createMatchPath, loadConfig, type MatchPath } from 'tsconfig-paths';
21-
import ts from 'typescript';
22+
import type {
23+
CompilerOptions,
24+
Diagnostic,
25+
ParsedCommandLine,
26+
} from 'typescript';
2227
import type { DtsEntry, DtsRedirect } from './index';
2328

29+
const __filename = fileURLToPath(import.meta.url);
30+
const require = createRequire(__filename);
31+
32+
/**
33+
* Currently, typescript only provides a CJS bundle, so we use require to load it
34+
* for better startup performance. If we use `import ts from 'typescript'`,
35+
* Node.js will use `cjs-module-lexer` to parse it, which slows down startup time.
36+
*/
37+
// eslint-disable-next-line @typescript-eslint/no-var-requires
38+
const ts = require('typescript') as typeof import('typescript');
39+
40+
export { ts };
41+
2442
const JS_EXTENSIONS: string[] = [
2543
'js',
2644
'mjs',
@@ -40,7 +58,7 @@ export const JS_EXTENSIONS_PATTERN: RegExp = new RegExp(
4058
`\\.(${JS_EXTENSIONS.join('|')})$`,
4159
);
4260

43-
export function loadTsconfig(tsconfigPath: string): ts.ParsedCommandLine {
61+
export function loadTsconfig(tsconfigPath: string): ParsedCommandLine {
4462
const configFile = ts.readConfigFile(
4563
tsconfigPath,
4664
ts.sys.readFile.bind(ts.sys),
@@ -139,10 +157,7 @@ export async function clearTempDeclarationDir(cwd: string): Promise<void> {
139157
await emptyDir(dirPath);
140158
}
141159

142-
export function getFileLoc(
143-
diagnostic: ts.Diagnostic,
144-
configPath: string,
145-
): string {
160+
export function getFileLoc(diagnostic: Diagnostic, configPath: string): string {
146161
if (diagnostic.file) {
147162
const { line, character } = ts.getLineAndCharacterOfPosition(
148163
diagnostic.file,
@@ -605,7 +620,7 @@ export async function cleanDtsFiles(dir: string): Promise<void> {
605620

606621
export async function cleanTsBuildInfoFile(
607622
tsconfigPath: string,
608-
compilerOptions: ts.CompilerOptions,
623+
compilerOptions: CompilerOptions,
609624
): Promise<void> {
610625
const tsconfigDir = dirname(tsconfigPath);
611626
const { outDir, rootDir, tsBuildInfoFile } = compilerOptions;

0 commit comments

Comments
 (0)