Skip to content

Commit e90c7e4

Browse files
authored
feat: add support for "generateTrace" tsconfig option (#523)
1 parent cf24572 commit e90c7e4

File tree

5 files changed

+9464
-10
lines changed

5 files changed

+9464
-10
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,20 @@ npm install --save-dev @types/webpack
358358
yarn add --dev @types/webpack
359359
```
360360

361+
## Profiling types resolution
362+
363+
Starting from TypeScript 4.1.0 (currently in beta), you can profile long type checks by
364+
setting "generateTrace" compiler option. This is an instruction from [microsoft/TypeScript#40063](https://github.com/microsoft/TypeScript/pull/40063):
365+
366+
1. Set "generateTrace": "{folderName}" in your `tsconfig.json`
367+
2. Look in the resulting folder. If you used build mode, there will be a `legend.json` telling you what went where.
368+
Otherwise, there will be `trace.json` file and `types.json` files.
369+
3. Navigate to [edge://tracing](edge://tracing) or [chrome://tracing](chrome://tracing) and load `trace.json`
370+
4. Expand Process 1 with the little triangle in the left sidebar
371+
5. Click on different blocks to see their payloads in the bottom pane
372+
6. Open `types.json` in an editor
373+
7. When you see a type ID in the tracing output, go-to-line {id} to find data about that type
374+
361375

362376
## Related projects
363377

src/typescript-reporter/reporter/TypeScriptReporter.ts

Lines changed: 107 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ import {
1818
import { createPerformance } from '../../profile/Performance';
1919
import { connectTypeScriptPerformance } from '../profile/TypeScriptPerformance';
2020

21+
// write this type as it's available only in the newest TypeScript versions (^4.1.0)
22+
interface Tracing {
23+
startTracing(configFilePath: string, traceDirPath: string, isBuildMode: boolean): void;
24+
stopTracing(typeCatalog: unknown): void;
25+
dumpLegend(): void;
26+
}
27+
2128
function createTypeScriptReporter(configuration: TypeScriptReporterConfiguration): Reporter {
2229
let parsedConfiguration: ts.ParsedCommandLine | undefined;
2330
let parseConfigurationDiagnostics: ts.Diagnostic[] = [];
@@ -49,8 +56,17 @@ function createTypeScriptReporter(configuration: TypeScriptReporterConfiguration
4956
extensions.push(createTypeScriptVueExtension(configuration.extensions.vue));
5057
}
5158

59+
function getConfigFilePathFromCompilerOptions(compilerOptions: ts.CompilerOptions): string {
60+
return (compilerOptions.configFilePath as unknown) as string;
61+
}
62+
5263
function getProjectNameOfBuilderProgram(builderProgram: ts.BuilderProgram): string {
53-
return (builderProgram.getProgram().getCompilerOptions().configFilePath as unknown) as string;
64+
return getConfigFilePathFromCompilerOptions(builderProgram.getProgram().getCompilerOptions());
65+
}
66+
67+
function getTracing(): Tracing | undefined {
68+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
69+
return (typescript as any).tracing;
5470
}
5571

5672
function getDiagnosticsOfBuilderProgram(builderProgram: ts.BuilderProgram) {
@@ -152,6 +168,49 @@ function createTypeScriptReporter(configuration: TypeScriptReporterConfiguration
152168
);
153169
}
154170

171+
function startProfilingIfNeeded() {
172+
if (configuration.profile) {
173+
performance.enable();
174+
}
175+
}
176+
177+
function stopProfilingIfNeeded() {
178+
if (configuration.profile) {
179+
performance.print();
180+
performance.disable();
181+
}
182+
}
183+
184+
function startTracingIfNeeded(compilerOptions: ts.CompilerOptions) {
185+
const tracing = getTracing();
186+
187+
if (compilerOptions.generateTrace && tracing) {
188+
tracing.startTracing(
189+
getConfigFilePathFromCompilerOptions(compilerOptions),
190+
compilerOptions.generateTrace as string,
191+
configuration.build
192+
);
193+
}
194+
}
195+
196+
function stopTracingIfNeeded(program: ts.BuilderProgram) {
197+
const tracing = getTracing();
198+
const compilerOptions = program.getCompilerOptions();
199+
200+
if (compilerOptions.generateTrace && tracing) {
201+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
202+
tracing.stopTracing((program.getProgram() as any).getTypeCatalog());
203+
}
204+
}
205+
206+
function dumpTracingLegendIfNeeded() {
207+
const tracing = getTracing();
208+
209+
if (tracing) {
210+
tracing.dumpLegend();
211+
}
212+
}
213+
155214
return {
156215
getReport: async ({ changedFiles = [], deletedFiles = [] }) => {
157216
// clear cache to be ready for next iteration and to free memory
@@ -227,9 +286,7 @@ function createTypeScriptReporter(configuration: TypeScriptReporterConfiguration
227286
return dependencies;
228287
},
229288
async getIssues() {
230-
if (configuration.profile) {
231-
performance.enable();
232-
}
289+
startProfilingIfNeeded();
233290

234291
parsedConfiguration = parseConfigurationIfNeeded();
235292

@@ -261,7 +318,26 @@ function createTypeScriptReporter(configuration: TypeScriptReporterConfiguration
261318
typescript,
262319
parsedConfiguration,
263320
system,
264-
typescript.createSemanticDiagnosticsBuilderProgram,
321+
(
322+
rootNames,
323+
compilerOptions,
324+
host,
325+
oldProgram,
326+
configFileParsingDiagnostics,
327+
projectReferences
328+
) => {
329+
if (compilerOptions) {
330+
startTracingIfNeeded(compilerOptions);
331+
}
332+
return typescript.createSemanticDiagnosticsBuilderProgram(
333+
rootNames,
334+
compilerOptions,
335+
host,
336+
oldProgram,
337+
configFileParsingDiagnostics,
338+
projectReferences
339+
);
340+
},
265341
undefined,
266342
undefined,
267343
undefined,
@@ -275,6 +351,8 @@ function createTypeScriptReporter(configuration: TypeScriptReporterConfiguration
275351

276352
// emit .tsbuildinfo file if needed
277353
emitTsBuildInfoFileForBuilderProgram(builderProgram);
354+
355+
stopTracingIfNeeded(builderProgram);
278356
},
279357
extensions
280358
);
@@ -308,7 +386,26 @@ function createTypeScriptReporter(configuration: TypeScriptReporterConfiguration
308386
typescript,
309387
parsedConfiguration,
310388
system,
311-
typescript.createSemanticDiagnosticsBuilderProgram,
389+
(
390+
rootNames,
391+
compilerOptions,
392+
host,
393+
oldProgram,
394+
configFileParsingDiagnostics,
395+
projectReferences
396+
) => {
397+
if (compilerOptions) {
398+
startTracingIfNeeded(compilerOptions);
399+
}
400+
return typescript.createSemanticDiagnosticsBuilderProgram(
401+
rootNames,
402+
compilerOptions,
403+
host,
404+
oldProgram,
405+
configFileParsingDiagnostics,
406+
projectReferences
407+
);
408+
},
312409
undefined,
313410
undefined,
314411
(builderProgram) => {
@@ -320,6 +417,8 @@ function createTypeScriptReporter(configuration: TypeScriptReporterConfiguration
320417

321418
// emit .tsbuildinfo file if needed
322419
emitTsBuildInfoFileForBuilderProgram(builderProgram);
420+
421+
stopTracingIfNeeded(builderProgram);
323422
},
324423
extensions
325424
);
@@ -370,10 +469,8 @@ function createTypeScriptReporter(configuration: TypeScriptReporterConfiguration
370469
}
371470
});
372471

373-
if (configuration.profile) {
374-
performance.print();
375-
performance.disable();
376-
}
472+
dumpTracingLegendIfNeeded();
473+
stopProfilingIfNeeded();
377474

378475
return issues;
379476
},
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { readFixture } from './sandbox/Fixture';
2+
import { join } from 'path';
3+
import { createSandbox, Sandbox } from './sandbox/Sandbox';
4+
import {
5+
createWebpackDevServerDriver,
6+
WEBPACK_CLI_VERSION,
7+
WEBPACK_DEV_SERVER_VERSION,
8+
} from './sandbox/WebpackDevServerDriver';
9+
import { FORK_TS_CHECKER_WEBPACK_PLUGIN_VERSION } from './sandbox/Plugin';
10+
11+
describe('TypeScript Generate Trace', () => {
12+
let sandbox: Sandbox;
13+
14+
beforeAll(async () => {
15+
sandbox = await createSandbox();
16+
});
17+
18+
beforeEach(async () => {
19+
await sandbox.reset();
20+
});
21+
22+
afterAll(async () => {
23+
await sandbox.cleanup();
24+
});
25+
26+
it('generates trace for typescript 4.1.0-beta in watch mode', async () => {
27+
await sandbox.load([
28+
await readFixture(join(__dirname, 'fixtures/environment/typescript-basic.fixture'), {
29+
FORK_TS_CHECKER_WEBPACK_PLUGIN_VERSION: JSON.stringify(
30+
FORK_TS_CHECKER_WEBPACK_PLUGIN_VERSION
31+
),
32+
TS_LOADER_VERSION: JSON.stringify('^7.0.0'),
33+
TYPESCRIPT_VERSION: JSON.stringify('4.1.0-beta'),
34+
WEBPACK_VERSION: JSON.stringify('^4.0.0'),
35+
WEBPACK_CLI_VERSION: JSON.stringify(WEBPACK_CLI_VERSION),
36+
WEBPACK_DEV_SERVER_VERSION: JSON.stringify(WEBPACK_DEV_SERVER_VERSION),
37+
ASYNC: JSON.stringify(true),
38+
}),
39+
await readFixture(join(__dirname, 'fixtures/implementation/typescript-basic.fixture')),
40+
]);
41+
42+
// update sandbox to generate trace
43+
await sandbox.patch(
44+
'tsconfig.json',
45+
' "outDir": "./dist"',
46+
[' "outDir": "./dist",', ' "generateTrace": "./traces"'].join('\n')
47+
);
48+
49+
const driver = createWebpackDevServerDriver(sandbox.spawn('npm run webpack-dev-server'), true);
50+
51+
// first compilation is successful
52+
await driver.waitForNoErrors();
53+
54+
expect(await sandbox.exists('traces/trace.json')).toBe(true);
55+
expect(await sandbox.exists('traces/types.json')).toBe(true);
56+
});
57+
58+
it('generates trace for typescript 4.1.0-beta in build mode', async () => {
59+
await sandbox.load([
60+
await readFixture(join(__dirname, 'fixtures/environment/typescript-monorepo.fixture'), {
61+
FORK_TS_CHECKER_WEBPACK_PLUGIN_VERSION: JSON.stringify(
62+
FORK_TS_CHECKER_WEBPACK_PLUGIN_VERSION
63+
),
64+
TYPESCRIPT_VERSION: JSON.stringify('4.1.0-beta'),
65+
WEBPACK_VERSION: JSON.stringify('^4.0.0'),
66+
WEBPACK_CLI_VERSION: JSON.stringify(WEBPACK_CLI_VERSION),
67+
WEBPACK_DEV_SERVER_VERSION: JSON.stringify(WEBPACK_DEV_SERVER_VERSION),
68+
ASYNC: JSON.stringify(true),
69+
MODE: JSON.stringify('readonly'),
70+
}),
71+
await readFixture(join(__dirname, 'fixtures/implementation/typescript-monorepo.fixture')),
72+
]);
73+
74+
// update sandbox to generate trace
75+
await sandbox.patch(
76+
'tsconfig.json',
77+
' "rootDir": "./packages"',
78+
[' "rootDir": "./packages",', ' "generateTrace": "./traces"'].join('\n')
79+
);
80+
81+
const driver = createWebpackDevServerDriver(sandbox.spawn('npm run webpack-dev-server'), true);
82+
83+
// first compilation is successful
84+
await driver.waitForNoErrors();
85+
86+
expect(await sandbox.exists('traces/trace.1.json')).toBe(true);
87+
expect(await sandbox.exists('traces/types.1.json')).toBe(true);
88+
expect(await sandbox.exists('traces/trace.2.json')).toBe(true);
89+
expect(await sandbox.exists('traces/types.2.json')).toBe(true);
90+
expect(await sandbox.exists('traces/legend.json')).toBe(true);
91+
});
92+
});

0 commit comments

Comments
 (0)