Skip to content

Commit b087c0c

Browse files
committed
feat: clean dts files / buildinfo / .rslib temp folder
1 parent da8191a commit b087c0c

File tree

21 files changed

+495
-55
lines changed

21 files changed

+495
-55
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ coverage/
1111
doc_build/
1212
playwright-report/
1313
tsconfig.tsbuildinfo
14+
tsconfig.esm.tsbuildinfo
15+
tsconfig.cjs.tsbuildinfo
1416
test-temp-*
1517
test-results
1618

packages/plugin-dts/src/dts.ts

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,9 @@ import {
1010
} from 'node:path';
1111
import { logger } from '@rsbuild/core';
1212
import color from 'picocolors';
13-
import ts from 'typescript';
1413
import type { DtsGenOptions } from './index';
1514
import { emitDts } from './tsc';
16-
import {
17-
calcLongestCommonPath,
18-
ensureTempDeclarationDir,
19-
loadTsconfig,
20-
} from './utils';
15+
import { calcLongestCommonPath, ensureTempDeclarationDir } from './utils';
2116

2217
const isObject = (obj: unknown): obj is Record<string, any> =>
2318
Object.prototype.toString.call(obj) === '[object Object]';
@@ -113,9 +108,10 @@ export const calcBundledPackages = (options: {
113108
export async function generateDts(data: DtsGenOptions): Promise<void> {
114109
const {
115110
bundle,
116-
distPath,
111+
dtsEmitPath,
117112
dtsEntry,
118113
tsconfigPath,
114+
tsConfigResult,
119115
name,
120116
cwd,
121117
build,
@@ -125,33 +121,25 @@ export async function generateDts(data: DtsGenOptions): Promise<void> {
125121
userExternals,
126122
banner,
127123
footer,
128-
rootDistPath,
129124
} = data;
130125
logger.start(`Generating DTS... ${color.gray(`(${name})`)}`);
131-
const configPath = ts.findConfigFile(cwd, ts.sys.fileExists, tsconfigPath);
132-
if (!configPath) {
133-
logger.error(`tsconfig.json not found in ${cwd}`);
134-
throw new Error();
135-
}
136-
const { options: rawCompilerOptions, fileNames } = loadTsconfig(configPath);
126+
127+
const { options: rawCompilerOptions, fileNames } = tsConfigResult;
137128

138129
// The longest common path of all non-declaration input files.
139130
// If composite is set, the default is instead the directory containing the tsconfig.json file.
140131
// see https://www.typescriptlang.org/tsconfig/#rootDir
141132
const rootDir =
142133
rawCompilerOptions.rootDir ??
143134
(rawCompilerOptions.composite
144-
? dirname(configPath)
135+
? dirname(tsconfigPath)
145136
: await calcLongestCommonPath(
146137
fileNames.filter((fileName) => !/\.d\.(ts|mts|cts)$/.test(fileName)),
147138
)) ??
148-
dirname(configPath);
149-
150-
const dtsEmitPath =
151-
distPath ?? rawCompilerOptions.declarationDir ?? rootDistPath;
139+
dirname(tsconfigPath);
152140

153141
const resolvedDtsEmitPath = normalize(
154-
resolve(dirname(configPath), dtsEmitPath),
142+
resolve(dirname(tsconfigPath), dtsEmitPath),
155143
);
156144

157145
if (build) {
@@ -173,13 +161,15 @@ export async function generateDts(data: DtsGenOptions): Promise<void> {
173161
: 'declarationDir';
174162
throw Error(
175163
`Please set ${info}: "${dtsEmitPath}" in ${color.underline(
176-
configPath,
164+
tsconfigPath,
177165
)} to keep it same as "dts.distPath" or "output.distPath.root" field in lib config.`,
178166
);
179167
}
180168
}
181169

182-
const declarationDir = bundle ? ensureTempDeclarationDir(cwd) : dtsEmitPath;
170+
const declarationDir = bundle
171+
? ensureTempDeclarationDir(cwd, name)
172+
: dtsEmitPath;
183173

184174
const { name: entryName, path: entryPath } = dtsEntry;
185175
let entry = '';
@@ -232,7 +222,8 @@ export async function generateDts(data: DtsGenOptions): Promise<void> {
232222
{
233223
name,
234224
cwd,
235-
configPath,
225+
configPath: tsconfigPath,
226+
tsConfigResult,
236227
declarationDir,
237228
dtsExtension,
238229
banner,

packages/plugin-dts/src/index.ts

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,14 @@ import { fork } from 'node:child_process';
22
import { dirname, extname, join } from 'node:path';
33
import { fileURLToPath } from 'node:url';
44
import { type RsbuildConfig, type RsbuildPlugin, logger } from '@rsbuild/core';
5-
import { processSourceEntry } from './utils';
5+
import ts from 'typescript';
6+
import {
7+
cleanDtsFiles,
8+
cleanTsBuildInfoFile,
9+
clearTempDeclarationDir,
10+
loadTsconfig,
11+
processSourceEntry,
12+
} from './utils';
613

714
const __filename = fileURLToPath(import.meta.url);
815
const __dirname = dirname(__filename);
@@ -34,9 +41,10 @@ export type DtsGenOptions = PluginDtsOptions & {
3441
cwd: string;
3542
isWatch: boolean;
3643
dtsEntry: DtsEntry;
37-
rootDistPath: string;
44+
dtsEmitPath: string;
3845
build?: boolean;
39-
tsconfigPath?: string;
46+
tsconfigPath: string;
47+
tsConfigResult: ts.ParsedCommandLine;
4048
userExternals?: NonNullable<RsbuildConfig['output']>['externals'];
4149
};
4250

@@ -62,33 +70,67 @@ export const pluginDts = (options: PluginDtsOptions = {}): RsbuildPlugin => ({
6270
let promisesResult: TaskResult[] = [];
6371

6472
api.onBeforeEnvironmentCompile(
65-
({ isWatch, isFirstCompile, environment }) => {
73+
async ({ isWatch, isFirstCompile, environment }) => {
6674
if (!isFirstCompile) {
6775
return;
6876
}
6977

7078
const { config } = environment;
7179

72-
const jsExtension = extname(__filename);
73-
const childProcess = fork(join(__dirname, `./dts${jsExtension}`), [], {
74-
stdio: 'inherit',
75-
});
76-
7780
// TODO: @microsoft/api-extractor only support single entry to bundle DTS
7881
// use first element of Record<string, string> type entry config
7982
const dtsEntry = processSourceEntry(
8083
options.bundle!,
8184
config.source?.entry,
8285
);
8386

87+
const cwd = api.context.rootPath;
88+
const tsconfigPath = ts.findConfigFile(
89+
cwd,
90+
ts.sys.fileExists,
91+
config.source.tsconfigPath,
92+
);
93+
94+
if (!tsconfigPath) {
95+
logger.error(`tsconfig.json not found in ${cwd}`);
96+
throw new Error();
97+
}
98+
99+
const tsConfigResult = loadTsconfig(tsconfigPath);
100+
const dtsEmitPath =
101+
options.distPath ??
102+
tsConfigResult.options.declarationDir ??
103+
config.output?.distPath?.root;
104+
105+
const jsExtension = extname(__filename);
106+
const childProcess = fork(join(__dirname, `./dts${jsExtension}`), [], {
107+
stdio: 'inherit',
108+
});
109+
110+
const { cleanDistPath } = config.output;
111+
112+
// clean dts files
113+
if (cleanDistPath !== false) {
114+
await cleanDtsFiles(dtsEmitPath);
115+
}
116+
117+
// clean .rslib temp folder
118+
if (options.bundle) {
119+
await clearTempDeclarationDir(cwd);
120+
}
121+
122+
// clean tsbuildinfo file
123+
await cleanTsBuildInfoFile(tsconfigPath, tsConfigResult);
124+
84125
const dtsGenOptions: DtsGenOptions = {
85126
...options,
86127
dtsEntry,
128+
dtsEmitPath,
87129
userExternals: config.output.externals,
88-
rootDistPath: config.output?.distPath?.root,
89-
tsconfigPath: config.source.tsconfigPath,
130+
tsconfigPath,
131+
tsConfigResult,
90132
name: environment.name,
91-
cwd: api.context.rootPath,
133+
cwd,
92134
isWatch,
93135
};
94136

packages/plugin-dts/src/tsc.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
import { logger } from '@rsbuild/core';
22
import color from 'picocolors';
33
import ts from 'typescript';
4-
import {
5-
getFileLoc,
6-
getTimeCost,
7-
loadTsconfig,
8-
processDtsFiles,
9-
} from './utils';
4+
import { getFileLoc, getTimeCost, processDtsFiles } from './utils';
105

116
export type EmitDtsOptions = {
127
name: string;
138
cwd: string;
149
configPath: string;
10+
tsConfigResult: ts.ParsedCommandLine;
1511
declarationDir: string;
1612
dtsExtension: string;
1713
banner?: string;
@@ -63,14 +59,20 @@ export async function emitDts(
6359
build = false,
6460
): Promise<void> {
6561
const start = Date.now();
66-
const { configPath, declarationDir, name, dtsExtension, banner, footer } =
67-
options;
68-
const configFileParseResult = loadTsconfig(configPath);
62+
const {
63+
configPath,
64+
tsConfigResult,
65+
declarationDir,
66+
name,
67+
dtsExtension,
68+
banner,
69+
footer,
70+
} = options;
6971
const {
7072
options: rawCompilerOptions,
7173
fileNames,
7274
projectReferences,
73-
} = configFileParseResult;
75+
} = tsConfigResult;
7476

7577
const compilerOptions = {
7678
...rawCompilerOptions,
@@ -160,9 +162,8 @@ export async function emitDts(
160162
options: compilerOptions,
161163
projectReferences,
162164
host,
163-
configFileParsingDiagnostics: ts.getConfigFileParsingDiagnostics(
164-
configFileParseResult,
165-
),
165+
configFileParsingDiagnostics:
166+
ts.getConfigFileParsingDiagnostics(tsConfigResult),
166167
});
167168

168169
const emitResult = program.emit();
@@ -190,9 +191,8 @@ export async function emitDts(
190191
const program = ts.createIncrementalProgram({
191192
rootNames: fileNames,
192193
options: compilerOptions,
193-
configFileParsingDiagnostics: ts.getConfigFileParsingDiagnostics(
194-
configFileParseResult,
195-
),
194+
configFileParsingDiagnostics:
195+
ts.getConfigFileParsingDiagnostics(tsConfigResult),
196196
projectReferences,
197197
host,
198198
createProgram,

packages/plugin-dts/src/utils.ts

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import fs from 'node:fs';
22
import fsP from 'node:fs/promises';
33
import { platform } from 'node:os';
4-
import path, { join } from 'node:path';
4+
import path, { basename, dirname, join, relative, resolve } from 'node:path';
55
import { type RsbuildConfig, logger } from '@rsbuild/core';
66
import MagicString from 'magic-string';
77
import color from 'picocolors';
@@ -23,8 +23,8 @@ export function loadTsconfig(tsconfigPath: string): ts.ParsedCommandLine {
2323
export const TEMP_FOLDER = '.rslib';
2424
export const TEMP_DTS_DIR: string = `${TEMP_FOLDER}/declarations`;
2525

26-
export function ensureTempDeclarationDir(cwd: string): string {
27-
const dirPath = path.join(cwd, TEMP_DTS_DIR);
26+
export function ensureTempDeclarationDir(cwd: string, name: string): string {
27+
const dirPath = path.join(cwd, TEMP_DTS_DIR, name);
2828

2929
if (fs.existsSync(dirPath)) {
3030
return dirPath;
@@ -38,6 +38,16 @@ export function ensureTempDeclarationDir(cwd: string): string {
3838
return dirPath;
3939
}
4040

41+
export async function clearTempDeclarationDir(cwd: string): Promise<void> {
42+
const dirPath = path.join(cwd, TEMP_DTS_DIR);
43+
44+
try {
45+
await fsP.rm(dirPath, { recursive: true, force: true });
46+
} catch (error) {
47+
logger.error(`Error clearing temporary directory ${dirPath}: ${error}`);
48+
}
49+
}
50+
4151
export function getFileLoc(
4252
diagnostic: ts.Diagnostic,
4353
configPath: string,
@@ -196,3 +206,41 @@ export async function calcLongestCommonPath(
196206

197207
return lca;
198208
}
209+
210+
export async function cleanDtsFiles(dir: string): Promise<void> {
211+
const patterns = ['/**/*.d.ts', '/**/*.d.cts', '/**/*.d.mts'];
212+
const files = await Promise.all(
213+
patterns.map((pattern) =>
214+
glob(convertPath(join(dir, pattern)), { absolute: true }),
215+
),
216+
);
217+
218+
const allFiles = files.flat();
219+
220+
await Promise.all(allFiles.map((file) => fsP.rm(file, { force: true })));
221+
}
222+
223+
export async function cleanTsBuildInfoFile(
224+
tsconfigPath: string,
225+
tsConfigResult: ts.ParsedCommandLine,
226+
): Promise<void> {
227+
const tsconfigDir = dirname(tsconfigPath);
228+
const { outDir, rootDir, tsBuildInfoFile } = tsConfigResult.options;
229+
let tsbuildInfoFilePath = `${basename(
230+
tsconfigPath,
231+
'.json',
232+
)}${tsBuildInfoFile ?? '.tsbuildinfo'}`;
233+
if (outDir) {
234+
if (rootDir) {
235+
tsbuildInfoFilePath = join(
236+
outDir,
237+
relative(resolve(tsconfigDir, rootDir), tsconfigDir),
238+
tsbuildInfoFilePath,
239+
);
240+
} else {
241+
tsbuildInfoFilePath = join(outDir, tsbuildInfoFilePath);
242+
}
243+
}
244+
245+
await fsP.rm(tsbuildInfoFilePath, { force: true });
246+
}

pnpm-lock.yaml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)