Skip to content

Commit f3afaa7

Browse files
authored
fix: cleanup host diagnostics in SolutionBuilder on rebuild (#62)
1 parent 8da5b78 commit f3afaa7

File tree

3 files changed

+135
-8
lines changed

3 files changed

+135
-8
lines changed

src/typescript/worker/lib/host/watch-compiler-host.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export function createWatchCompilerHost<TProgram extends ts.BuilderProgram>(
88
createProgram?: ts.CreateProgram<TProgram>,
99
reportDiagnostic?: ts.DiagnosticReporter,
1010
reportWatchStatus?: ts.WatchStatusReporter,
11-
afterProgramCreate?: (program: TProgram) => void
11+
afterProgramCreate?: (program: TProgram) => void,
1212
): ts.WatchCompilerHostOfFilesAndCompilerOptions<TProgram> {
1313
const baseWatchCompilerHost = typescript.createWatchCompilerHost(
1414
parsedConfig.fileNames,
@@ -17,7 +17,7 @@ export function createWatchCompilerHost<TProgram extends ts.BuilderProgram>(
1717
createProgram,
1818
reportDiagnostic,
1919
reportWatchStatus,
20-
parsedConfig.projectReferences
20+
parsedConfig.projectReferences,
2121
);
2222

2323
return {
@@ -28,24 +28,26 @@ export function createWatchCompilerHost<TProgram extends ts.BuilderProgram>(
2828
compilerHost?: ts.CompilerHost,
2929
oldProgram?: TProgram,
3030
configFileParsingDiagnostics?: ReadonlyArray<ts.Diagnostic>,
31-
projectReferences?: ReadonlyArray<ts.ProjectReference> | undefined
31+
projectReferences?: ReadonlyArray<ts.ProjectReference> | undefined,
3232
): TProgram {
3333
return baseWatchCompilerHost.createProgram(
3434
rootNames,
3535
options,
3636
compilerHost,
3737
oldProgram,
3838
configFileParsingDiagnostics,
39-
projectReferences
39+
projectReferences,
4040
);
4141
},
4242
afterProgramCreate(program) {
4343
if (afterProgramCreate) {
4444
afterProgramCreate(program);
4545
}
4646
},
47-
onWatchStatusChange(): void {
48-
// do nothing
47+
onWatchStatusChange(...args) {
48+
if (reportWatchStatus) {
49+
reportWatchStatus(...args);
50+
}
4951
},
5052
watchFile: system.watchFile,
5153
watchDirectory: system.watchDirectory,

src/typescript/worker/lib/program/solution-builder.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,14 @@ export function useSolutionBuilder() {
4646
hostDiagnostics.push(diagnostic);
4747
updateDiagnostics(config.configFile, hostDiagnostics);
4848
},
49-
undefined,
49+
(diagnostic) => {
50+
// clean host diagnostics when rebuild start
51+
// 6031: 'Starting compilation in watch mode...'
52+
// 6032: 'File change detected. Starting incremental compilation...'
53+
if (diagnostic.code === 6031 || diagnostic.code === 6032) {
54+
hostDiagnostics = [];
55+
}
56+
},
5057
undefined,
5158
undefined,
5259
(builderProgram) => {

test/e2e/with-rsbuild/index.test.ts

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { dirname } from 'node:path';
1+
import { dirname, resolve } from 'node:path';
2+
import { readFile, writeFile } from 'node:fs/promises';
23
import { fileURLToPath } from 'node:url';
34
import { expect, test } from '@playwright/test';
45
import {
@@ -191,3 +192,120 @@ test('should host diagnostics in SolutionBuilder (e.g. TS6202 for circular proje
191192

192193
restore();
193194
});
195+
196+
test('should cleanup host diagnostics in SolutionBuilder when rebuild in dev mode', async ({
197+
page,
198+
}) => {
199+
const { logs, restore } = proxyConsole();
200+
201+
const srcIndexPath = resolve(__dirname, 'src/index.ts');
202+
const originalSrcIndex = await readFile(srcIndexPath, 'utf8');
203+
204+
const updateSrcIndex = async (updater: (content: string) => string) => {
205+
const current = await readFile(srcIndexPath, 'utf8');
206+
const next = updater(current);
207+
await writeFile(srcIndexPath, next);
208+
};
209+
210+
const rsbuild = await createRsbuild({
211+
rsbuildConfig: {
212+
tools: {
213+
rspack: {
214+
plugins: [
215+
new TsCheckerRspackPlugin({
216+
async: false,
217+
typescript: {
218+
build: true,
219+
},
220+
}),
221+
],
222+
},
223+
},
224+
},
225+
});
226+
227+
const { server, urls } = await rsbuild.startDevServer();
228+
229+
try {
230+
await page.goto(urls[0]);
231+
232+
expect(logs.find((log) => log.includes('File:') && log.includes('/src/index.ts'))).toBeTruthy();
233+
expect(
234+
logs.find((log) =>
235+
log.includes(`Argument of type 'string' is not assignable to parameter of type 'number'.`),
236+
),
237+
).toBeTruthy();
238+
239+
// 1) Fix TS2345 by changing src/index.ts to `const res = add(1, 2);`
240+
const logLenAfterFirstCompile = logs.length;
241+
await updateSrcIndex((content) =>
242+
content.replace(/const\s+res\s*=\s*add\([^;]*\);/, 'const res = add(1, 2);'),
243+
);
244+
await expect
245+
.poll(
246+
() =>
247+
logs
248+
.slice(logLenAfterFirstCompile)
249+
.some((log) =>
250+
log.includes(
251+
`Argument of type 'string' is not assignable to parameter of type 'number'.`,
252+
),
253+
),
254+
{
255+
timeout: 20_000,
256+
},
257+
)
258+
.toBeFalsy();
259+
260+
// 2) Add a syntax error: `console.log('foo)` and verify `Unterminated string literal.` error appears
261+
// and the previous type error doesn't re-appear in subsequent rebuild logs.
262+
const logLenAfterFixTypeError = logs.length;
263+
await updateSrcIndex((content) => `${content}\nconsole.log('foo)\n`);
264+
await expect
265+
.poll(
266+
() => {
267+
const nextLogs = logs.slice(logLenAfterFixTypeError);
268+
const hasUnterminated = nextLogs.some((log) =>
269+
log.includes('Unterminated string literal.'),
270+
);
271+
const hasTypeError = nextLogs.some((log) =>
272+
log.includes(
273+
`Argument of type 'string' is not assignable to parameter of type 'number'.`,
274+
),
275+
);
276+
return hasUnterminated && !hasTypeError;
277+
},
278+
{
279+
timeout: 20_000,
280+
},
281+
)
282+
.toBeTruthy();
283+
284+
// 3) Fix syntax error: `console.log('foo')` and verify the error is gone.
285+
const logLenAfterUnterminated = logs.length;
286+
await updateSrcIndex((content) => content.replace("console.log('foo)", "console.log('foo')"));
287+
await expect
288+
.poll(
289+
() => {
290+
const nextLogs = logs.slice(logLenAfterUnterminated);
291+
const hasUnterminated = nextLogs.some((log) =>
292+
log.includes('Unterminated string literal.'),
293+
);
294+
const hasTypeError = nextLogs.some((log) =>
295+
log.includes(
296+
`Argument of type 'string' is not assignable to parameter of type 'number'.`,
297+
),
298+
);
299+
return !hasUnterminated && !hasTypeError;
300+
},
301+
{
302+
timeout: 20_000,
303+
},
304+
)
305+
.toBeTruthy();
306+
} finally {
307+
await writeFile(srcIndexPath, originalSrcIndex);
308+
restore();
309+
await server.close();
310+
}
311+
});

0 commit comments

Comments
 (0)