diff --git a/README.md b/README.md index e12c575f..b4f32f22 100644 --- a/README.md +++ b/README.md @@ -347,10 +347,10 @@ yarn add --dev @types/webpack ## Profiling types resolution -Starting from TypeScript 4.1.0, you can profile long type checks by +When using TypeScript 4.3.0 or newer you can profile long type checks by setting "generateTrace" compiler option. This is an instruction from [microsoft/TypeScript#40063](https://github.com/microsoft/TypeScript/pull/40063): -1. Set "generateTrace": "{folderName}" in your `tsconfig.json` +1. Set "generateTrace": "{folderName}" in your `tsconfig.json` (under `compilerOptions`) 2. Look in the resulting folder. If you used build mode, there will be a `legend.json` telling you what went where. Otherwise, there will be `trace.json` file and `types.json` files. 3. Navigate to [edge://tracing](edge://tracing) or [chrome://tracing](chrome://tracing) and load `trace.json` diff --git a/src/typescript/worker/lib/program/program.ts b/src/typescript/worker/lib/program/program.ts index db9457d7..670ee340 100644 --- a/src/typescript/worker/lib/program/program.ts +++ b/src/typescript/worker/lib/program/program.ts @@ -4,6 +4,7 @@ import { getConfigFilePathFromProgram, getParsedConfig } from '../config'; import { updateDiagnostics, getDiagnosticsOfProgram } from '../diagnostics'; import { emitDtsIfNeeded } from '../emit'; import { createCompilerHost } from '../host/compiler-host'; +import { startTracingIfNeeded, stopTracingIfNeeded } from '../tracing'; import { typescript } from '../typescript'; let compilerHost: ts.CompilerHost | undefined; @@ -16,6 +17,7 @@ export function useProgram() { compilerHost = createCompilerHost(parsedConfig); } if (!program) { + startTracingIfNeeded(parsedConfig.options); program = typescript.createProgram({ rootNames: parsedConfig.fileNames, options: parsedConfig.options, @@ -26,6 +28,7 @@ export function useProgram() { updateDiagnostics(getConfigFilePathFromProgram(program), getDiagnosticsOfProgram(program)); emitDtsIfNeeded(program); + stopTracingIfNeeded(program); } export function invalidateProgram(withHost = false) { diff --git a/src/typescript/worker/lib/tracing.ts b/src/typescript/worker/lib/tracing.ts index b2c473d1..fe6aec61 100644 --- a/src/typescript/worker/lib/tracing.ts +++ b/src/typescript/worker/lib/tracing.ts @@ -4,37 +4,44 @@ import { getConfigFilePathFromCompilerOptions } from './config'; import { typescript } from './typescript'; import { config } from './worker-config'; -// write this type as it's available only starting from TypeScript 4.1.0 +// these types are internal in TypeScript, so reproduce them here +type TracingMode = 'project' | 'build' | 'server'; interface Tracing { - startTracing(configFilePath: string, traceDirPath: string, isBuildMode: boolean): void; - stopTracing(typeCatalog: unknown): void; - dumpLegend(): void; + startTracing?: (tracingMode: TracingMode, traceDir: string, configFilePath?: string) => void; + + tracing?: { + stopTracing(): void; + dumpLegend(): void; + }; } // eslint-disable-next-line @typescript-eslint/no-explicit-any -const tracing: Tracing | undefined = (typescript as any).tracing; +const traceableTypescript: Tracing = typescript as any; export function startTracingIfNeeded(compilerOptions: ts.CompilerOptions) { - if (compilerOptions.generateTrace && tracing) { - tracing.startTracing( - getConfigFilePathFromCompilerOptions(compilerOptions), - compilerOptions.generateTrace as string, - config.build + if ( + typeof compilerOptions.generateTrace === 'string' && + typeof traceableTypescript.startTracing === 'function' + ) { + traceableTypescript.startTracing( + config.build ? 'build' : 'project', + compilerOptions.generateTrace, + getConfigFilePathFromCompilerOptions(compilerOptions) ); } } -export function stopTracingIfNeeded(program: ts.BuilderProgram) { +export function stopTracingIfNeeded(program: ts.Program | ts.BuilderProgram) { const compilerOptions = program.getCompilerOptions(); - if (compilerOptions.generateTrace && tracing) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - tracing.stopTracing((program.getProgram() as any).getTypeCatalog()); + if ( + typeof compilerOptions.generateTrace === 'string' && + typeof traceableTypescript.tracing?.stopTracing === 'function' + ) { + traceableTypescript.tracing.stopTracing(); } } export function dumpTracingLegendIfNeeded() { - if (tracing) { - tracing.dumpLegend(); - } + traceableTypescript.tracing?.dumpLegend(); } diff --git a/test/e2e/type-script-tracing.spec.ts b/test/e2e/type-script-tracing.spec.ts new file mode 100644 index 00000000..85d31ab7 --- /dev/null +++ b/test/e2e/type-script-tracing.spec.ts @@ -0,0 +1,40 @@ +import path from 'path'; + +import { extractWebpackErrors } from './driver/webpack-errors-extractor'; + +describe('TypeScript Tracing', () => { + it.each([ + { build: false, typescript: '~4.3.0' }, + { build: true, typescript: '~4.3.0' }, + { build: false, typescript: '~4.6.0' }, + { build: true, typescript: '~4.6.0' }, + ])('can generate trace files for %p', async ({ build, ...dependencies }) => { + await sandbox.load(path.join(__dirname, 'fixtures/typescript-basic')); + await sandbox.install('yarn', { ...dependencies }); + + // enable tracing + await sandbox.patch( + 'tsconfig.json', + '"outDir": "./dist"', + '"outDir": "./dist",\n"generateTrace": "./traces"' + ); + + await sandbox.write( + 'fork-ts-checker.config.js', + `module.exports = ${JSON.stringify({ typescript: { build } })};` + ); + + const webpackResult = await sandbox.exec('yarn webpack --mode=development'); + const errors = extractWebpackErrors(webpackResult); + expect(errors).toEqual([]); + + expect(await sandbox.exists('dist')).toEqual(true); + + expect(await sandbox.list('./traces')).toEqual( + expect.arrayContaining([ + expect.objectContaining({ name: expect.stringMatching(/types.*\.json/) }), + expect.objectContaining({ name: expect.stringMatching(/trace.*\.json/) }), + ]) + ); + }); +});