tsc-ts 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -1
- package/dist/api/compiler/ast.d.ts +15 -3
- package/dist/api/compiler/ast.js +36 -15
- package/dist/api/compiler/checker-classes.js +2 -1
- package/dist/api/compiler/checker-contextual.d.ts +20 -0
- package/dist/api/compiler/checker-contextual.js +111 -1
- package/dist/api/compiler/checker-expressions.js +14 -1
- package/dist/api/compiler/checker-grammar.d.ts +39 -4
- package/dist/api/compiler/checker-grammar.js +239 -75
- package/dist/api/compiler/checker-heritage.js +20 -14
- package/dist/api/compiler/checker-jsx.js +2 -1
- package/dist/api/compiler/checker-modules.js +4 -2
- package/dist/api/compiler/checker-source-file.js +1 -1
- package/dist/api/compiler/checker-type-factory.js +70 -19
- package/dist/api/compiler/checker-type-nodes.d.ts +1 -1
- package/dist/api/compiler/checker-type-nodes.js +5 -5
- package/dist/api/compiler/checker-unused.js +15 -10
- package/dist/api/compiler/checker.d.ts +7 -1
- package/dist/api/compiler/checker.js +14 -4
- package/dist/api/compiler/compile-program.d.ts +5 -0
- package/dist/api/compiler/compile-program.js +8 -0
- package/dist/api/compiler/expr-record.d.ts +11 -0
- package/dist/api/compiler/expr-record.js +53 -0
- package/dist/api/compiler/file-loader.d.ts +5 -0
- package/dist/api/compiler/file-loader.js +7 -0
- package/dist/api/compiler/identifier-name.d.ts +9 -0
- package/dist/api/compiler/identifier-name.js +10 -0
- package/dist/api/compiler/jsdoc.js +4 -2
- package/dist/api/compiler/parser.d.ts +2 -1
- package/dist/api/compiler/parser.js +10 -8
- package/dist/api/compiler/printer/factory.js +3 -2
- package/dist/api/compiler/printer/printer.js +3 -2
- package/dist/api/compiler/program-checker.d.ts +18 -0
- package/dist/api/compiler/program-checker.js +27 -0
- package/dist/api/compiler/reparser.js +4 -3
- package/dist/api/compiler/scanner.d.ts +8 -0
- package/dist/api/compiler/scanner.js +58 -42
- package/dist/api/compiler/shared-results.d.ts +376 -143
- package/dist/api/compiler/shared-results.js +1550 -773
- package/dist/api/compiler/transformers/declarations.js +2 -1
- package/package.json +15 -10
- package/src/compiler/ast.ts +39 -15
- package/src/compiler/cgroup.ts +157 -0
- package/src/compiler/checker-classes.ts +2 -1
- package/src/compiler/checker-contextual.ts +104 -0
- package/src/compiler/checker-expressions.ts +16 -1
- package/src/compiler/checker-grammar.ts +247 -88
- package/src/compiler/checker-heritage.ts +31 -17
- package/src/compiler/checker-jsx.ts +2 -1
- package/src/compiler/checker-modules.ts +4 -2
- package/src/compiler/checker-source-file.ts +1 -1
- package/src/compiler/checker-type-factory.ts +67 -21
- package/src/compiler/checker-type-nodes.ts +5 -5
- package/src/compiler/checker-unused.ts +13 -8
- package/src/compiler/checker.ts +15 -4
- package/src/compiler/compile-program.ts +8 -0
- package/src/compiler/decoder.ts +2 -1
- package/src/compiler/doctor-report.ts +133 -0
- package/src/compiler/expr-record.ts +59 -0
- package/src/compiler/file-loader.ts +8 -0
- package/src/compiler/identifier-name.ts +13 -0
- package/src/compiler/jsdoc.ts +4 -2
- package/src/compiler/parallel-check.ts +364 -146
- package/src/compiler/parallel-emit.ts +3 -3
- package/src/compiler/parse-thread.ts +24 -2
- package/src/compiler/parser.ts +10 -8
- package/src/compiler/printer/factory.ts +3 -2
- package/src/compiler/printer/printer.ts +4 -2
- package/src/compiler/program-checker.ts +28 -0
- package/src/compiler/reparser.ts +7 -3
- package/src/compiler/run-config.ts +1383 -0
- package/src/compiler/run-record-read.ts +87 -0
- package/src/compiler/run-records.ts +575 -0
- package/src/compiler/scanner.ts +57 -41
- package/src/compiler/shared-results.ts +1626 -896
- package/src/compiler/thread-entry.ts +3 -1
- package/src/compiler/transformers/declarations.ts +2 -1
- package/src/compiler/tsc-build.ts +64 -6
- package/src/compiler/tsc-cli.ts +33 -4
- package/src/compiler/tsc-compile.ts +223 -9
- package/src/compiler/tsc-watch.ts +25 -4
|
@@ -257,7 +257,9 @@ function runParseThread(job: ParseThreadData, drivers: ThreadDrivers): void {
|
|
|
257
257
|
const port = parentPort!;
|
|
258
258
|
port.on('message', (message: unknown) => {
|
|
259
259
|
const chunk = message as LoadMessage;
|
|
260
|
-
if (chunk.stop)
|
|
260
|
+
if (chunk.stop) {
|
|
261
|
+
port.close();
|
|
262
|
+
}
|
|
261
263
|
// The main thread read the project, so this one can too; if not, the program's FileLoader
|
|
262
264
|
// parses the files of the chunk (the main thread sees this thread fail).
|
|
263
265
|
else if (thread === undefined) throw new Error('A parse thread could not read the project');
|
|
@@ -161,6 +161,7 @@ import { ConstantKind } from './reference-resolver.js';
|
|
|
161
161
|
import { DeclarationTracker } from './declarations-tracker.js';
|
|
162
162
|
import { Transformer } from './transformer.js';
|
|
163
163
|
import { declarationCount, getDeclarations, SymbolFlags } from '../symbol.js';
|
|
164
|
+
import { IdentifierName } from '../identifier-name.js';
|
|
164
165
|
|
|
165
166
|
export class DeclarationTransformer extends Transformer {
|
|
166
167
|
readonly host: OutputPathsHost;
|
|
@@ -1934,7 +1935,7 @@ export class DeclarationTransformer extends Transformer {
|
|
|
1934
1935
|
keyword === SyntaxKind.DefaultKeyword
|
|
1935
1936
|
) {
|
|
1936
1937
|
// A parse tree node, so names it uses are resolved where it's written.
|
|
1937
|
-
const identifier = new Identifier(text);
|
|
1938
|
+
const identifier = new Identifier(new IdentifierName(text));
|
|
1938
1939
|
identifier.parent = name.parent;
|
|
1939
1940
|
return identifier;
|
|
1940
1941
|
}
|
|
@@ -62,12 +62,15 @@ import {
|
|
|
62
62
|
exitStatusOf,
|
|
63
63
|
rechecksOnThreads,
|
|
64
64
|
reportEmitAndErrors,
|
|
65
|
+
saveRunRecord,
|
|
65
66
|
Statistics,
|
|
66
67
|
statisticsOf,
|
|
67
68
|
SystemTimeStamps,
|
|
68
69
|
} from './tsc-compile.js';
|
|
69
70
|
import { sharesPublishedGraphs } from '@scriptc/threads';
|
|
70
|
-
import { checkerThreads,
|
|
71
|
+
import { checkerThreads, shareWithCheckers, startParallelCheck } from './parallel-check.js';
|
|
72
|
+
import { preParseConfig, reportRunConfig, type RunConfig } from './run-config.js';
|
|
73
|
+
import { openRecord } from './run-record-read.js';
|
|
71
74
|
import { emitThreads } from './parallel-emit.js';
|
|
72
75
|
import { LoadKind, ParseSession, clearPreparedFiles, parallelParse, reportPreparedFiles } from './parallel-parse.js';
|
|
73
76
|
import { assignSymbolIdsAtCreation } from './symbol.js';
|
|
@@ -182,6 +185,8 @@ class BuildTask {
|
|
|
182
185
|
buildKind = BuildKind.None;
|
|
183
186
|
filesToDelete: string[] = [];
|
|
184
187
|
configTime = 0;
|
|
188
|
+
/** The project's run configuration (run-config.ts), one per project of the build. */
|
|
189
|
+
run: RunConfig | undefined = undefined;
|
|
185
190
|
|
|
186
191
|
constructor(readonly config: string) {}
|
|
187
192
|
}
|
|
@@ -726,6 +731,33 @@ export class Orchestrator {
|
|
|
726
731
|
const host = new BuildCompilerHost(sys, this.fs, this.locale);
|
|
727
732
|
const configOptions = config.options;
|
|
728
733
|
const checkers = threads ? checkerThreads(configOptions.checkers, configOptions.singleThreaded) : 1;
|
|
734
|
+
// The projects of a build that builds several at once run beside each other, and each one's timings
|
|
735
|
+
// are the machine's, not its own: none keeps a record (run-records.ts). A watcher's builds measure a
|
|
736
|
+
// change, not the project: they keep none either, and read none.
|
|
737
|
+
const builders = this.command.buildOptions.builders ?? (configOptions.singleThreaded === true ? 1 : 4);
|
|
738
|
+
const learning = builders === 1 && !this.watching;
|
|
739
|
+
const opened = learning
|
|
740
|
+
? openRecord({
|
|
741
|
+
configFile: configOptions.configFilePath ?? task.config,
|
|
742
|
+
rootNames: config.fileNames,
|
|
743
|
+
options: configOptions,
|
|
744
|
+
})
|
|
745
|
+
: undefined;
|
|
746
|
+
// One configuration per project: its parse threads, and the pins of its checker count and sharing.
|
|
747
|
+
const run = (task.run = preParseConfig(configOptions, checkers, process.env, opened?.record, learning));
|
|
748
|
+
if (opened !== undefined) {
|
|
749
|
+
run.recordDirectory = opened.directory;
|
|
750
|
+
run.recordKey = opened.key;
|
|
751
|
+
run.recordFile = opened.file;
|
|
752
|
+
}
|
|
753
|
+
if (builders > 1) {
|
|
754
|
+
run.records = false;
|
|
755
|
+
run.recordsReason = 'concurrent projects';
|
|
756
|
+
}
|
|
757
|
+
if (this.watching) {
|
|
758
|
+
run.records = false;
|
|
759
|
+
run.recordsReason = 'watch mode';
|
|
760
|
+
}
|
|
729
761
|
if (threads && configOptions.singleThreaded !== true && parallelParse.spawn !== undefined) {
|
|
730
762
|
// Threads parse and bind the program's files ahead of it (parallel-parse.ts), as for plain
|
|
731
763
|
// compiles. Here also when the program is checked on this thread, with one checker or after a
|
|
@@ -735,7 +767,7 @@ export class Orchestrator {
|
|
|
735
767
|
// and parse threads don't load them again.
|
|
736
768
|
const libraryDirectory = getNormalizedAbsolutePath(sys.defaultLibraryPath, sys.currentDirectory);
|
|
737
769
|
const session = new ParseSession(
|
|
738
|
-
parseThreads
|
|
770
|
+
run.parseThreads,
|
|
739
771
|
configOptions,
|
|
740
772
|
sys.currentDirectory,
|
|
741
773
|
task.config,
|
|
@@ -783,6 +815,15 @@ export class Orchestrator {
|
|
|
783
815
|
times.bindTime = performance.now() - bindStart;
|
|
784
816
|
const options = program.options;
|
|
785
817
|
const count = mayUseThreads ? checkerThreads(options.checkers, options.singleThreaded, program) : 1;
|
|
818
|
+
// The run's configuration reports the count that checks: a checker thread run decides again once
|
|
819
|
+
// the plan is made (`startParallelCheck`, run-config.ts).
|
|
820
|
+
if (task.run !== undefined) task.run.checkers = count;
|
|
821
|
+
// A project rebuilt on its old build info checks the files a change affects, not the project: it
|
|
822
|
+
// keeps no sample, which records only keep full checks of (`run-records.ts`).
|
|
823
|
+
if (task.run !== undefined && oldSnapshot !== undefined && isIncremental(options)) {
|
|
824
|
+
task.run.records = false;
|
|
825
|
+
task.run.recordsReason = 'incremental recheck';
|
|
826
|
+
}
|
|
786
827
|
if (count > 1 && oldSnapshot !== undefined) {
|
|
787
828
|
if (isIncremental(options)) return this.recheckOnThreads(task, checked, times, oldSnapshot, count, then);
|
|
788
829
|
// Build info of a project that is no longer incremental has no pending emit to give the threads,
|
|
@@ -794,10 +835,20 @@ export class Orchestrator {
|
|
|
794
835
|
if (count > 1) {
|
|
795
836
|
// Checker threads also emit the files they check; the incremental program records their
|
|
796
837
|
// declaration signatures on this thread.
|
|
797
|
-
startParallelCheck(
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
838
|
+
startParallelCheck(
|
|
839
|
+
checked,
|
|
840
|
+
count,
|
|
841
|
+
(checkTime) => {
|
|
842
|
+
this.emitAndReport(task, checked, times, oldSnapshot, checkTime);
|
|
843
|
+
then();
|
|
844
|
+
},
|
|
845
|
+
true,
|
|
846
|
+
undefined,
|
|
847
|
+
true,
|
|
848
|
+
undefined,
|
|
849
|
+
undefined,
|
|
850
|
+
task.run,
|
|
851
|
+
);
|
|
801
852
|
return true;
|
|
802
853
|
}
|
|
803
854
|
if (mayUseThreads && this.emitsOnThreads(checked, oldSnapshot))
|
|
@@ -899,6 +950,9 @@ export class Orchestrator {
|
|
|
899
950
|
true,
|
|
900
951
|
incrementalProgram.emitKindsOnCheckers(files),
|
|
901
952
|
incrementalProgram.checksOnCheckers,
|
|
953
|
+
undefined,
|
|
954
|
+
undefined,
|
|
955
|
+
task.run,
|
|
902
956
|
);
|
|
903
957
|
return true;
|
|
904
958
|
}
|
|
@@ -945,10 +999,14 @@ export class Orchestrator {
|
|
|
945
999
|
task.exitStatus = exitStatusOf(result);
|
|
946
1000
|
if (parallelCheckTime !== undefined) times.checkTime = parallelCheckTime;
|
|
947
1001
|
const options = program.options;
|
|
1002
|
+
// A project's sample is kept whether or not the build reports statistics, and the section reports
|
|
1003
|
+
// where it went (`run-records.ts`).
|
|
1004
|
+
const records = saveRunRecord(sys, incrementalProgram, task.run);
|
|
948
1005
|
if (options.diagnostics === true || options.extendedDiagnostics === true) {
|
|
949
1006
|
times.totalTime = sys.sinceStart();
|
|
950
1007
|
const statistics = statisticsOf(incrementalProgram);
|
|
951
1008
|
statistics.report(sys);
|
|
1009
|
+
if (options.extendedDiagnostics === true && task.run !== undefined) reportRunConfig(sys, task.run, records);
|
|
952
1010
|
task.statistics = statistics;
|
|
953
1011
|
}
|
|
954
1012
|
const status = task.status;
|
package/src/compiler/tsc-cli.ts
CHANGED
|
@@ -33,8 +33,10 @@ import { Colors, defaultIsPretty, header, printBuildHelp, printHelp, printVersio
|
|
|
33
33
|
import { generateTSConfig } from './init.js';
|
|
34
34
|
import { isIncremental, type CompilerOptions } from './options.js';
|
|
35
35
|
import { combinePaths, getDirectoryPath, normalizePath } from './path.js';
|
|
36
|
-
import { checkerThreads
|
|
36
|
+
import { checkerThreads } from './parallel-check.js';
|
|
37
37
|
import { ParseSession, parallelParse, reportPreparedFiles } from './parallel-parse.js';
|
|
38
|
+
import { preParseConfig, type RunConfig } from './run-config.js';
|
|
39
|
+
import { openRecord } from './run-record-read.js';
|
|
38
40
|
import { showConfig, type ShowConfigInput } from './show-config.js';
|
|
39
41
|
import { assignSymbolIdsAtCreation } from './symbol.js';
|
|
40
42
|
import { sharesPublishedGraphs } from '@scriptc/threads';
|
|
@@ -163,6 +165,8 @@ export type CommandLineResult =
|
|
|
163
165
|
readonly errorSummary: ErrorSummaryReporter;
|
|
164
166
|
/** The build info of an incremental program, read while threads parsed its files; `undefined` if not read yet. */
|
|
165
167
|
readonly buildInfo?: EarlyBuildInfo;
|
|
168
|
+
/** How the run is configured: parse threads, checker count and sharing (run-config.ts). */
|
|
169
|
+
readonly config: RunConfig;
|
|
166
170
|
}
|
|
167
171
|
/** Threads are preparing the program's files; `run` calls back with the result once they are done. */
|
|
168
172
|
| { readonly kind: 'deferred'; readonly run: (done: (result: CommandLineResult) => void) => void }
|
|
@@ -363,6 +367,31 @@ function executeCompile(sys: System, commandLine: ParsedCommandLine): CommandLin
|
|
|
363
367
|
const configStart = performance.now();
|
|
364
368
|
mark('config start');
|
|
365
369
|
const errors: Diagnostic[] = [];
|
|
370
|
+
// The run's configuration, made once the config's options are known: its parse threads are needed
|
|
371
|
+
// before the first file is parsed.
|
|
372
|
+
let run: RunConfig | undefined = undefined;
|
|
373
|
+
const runConfigOf = (projectOptions: CompilerOptions): RunConfig => {
|
|
374
|
+
if (run === undefined) {
|
|
375
|
+
const opened = openRecord({
|
|
376
|
+
configFile: projectOptions.configFilePath ?? (configFileName === '' ? undefined : configFileName),
|
|
377
|
+
rootNames: fileNames,
|
|
378
|
+
options: projectOptions,
|
|
379
|
+
});
|
|
380
|
+
const made = preParseConfig(
|
|
381
|
+
projectOptions,
|
|
382
|
+
checkerThreads(projectOptions.checkers, projectOptions.singleThreaded),
|
|
383
|
+
process.env,
|
|
384
|
+
opened?.record,
|
|
385
|
+
);
|
|
386
|
+
if (opened !== undefined) {
|
|
387
|
+
made.recordDirectory = opened.directory;
|
|
388
|
+
made.recordKey = opened.key;
|
|
389
|
+
made.recordFile = opened.file;
|
|
390
|
+
}
|
|
391
|
+
run = made;
|
|
392
|
+
}
|
|
393
|
+
return run;
|
|
394
|
+
};
|
|
366
395
|
// Where threads parse and bind the program's files ahead of it (parallel-parse.ts), they start with
|
|
367
396
|
// the libraries and the root files found so far while the config's `include` patterns are matched.
|
|
368
397
|
let session: ParseSession | undefined = undefined;
|
|
@@ -370,7 +399,7 @@ function executeCompile(sys: System, commandLine: ParsedCommandLine): CommandLin
|
|
|
370
399
|
if (projectOptions.watch === true || options.showConfig === true || !parsesAhead(projectOptions)) return undefined;
|
|
371
400
|
assignSymbolIdsAtCreation();
|
|
372
401
|
const started = new ParseSession(
|
|
373
|
-
|
|
402
|
+
runConfigOf(projectOptions).parseThreads,
|
|
374
403
|
projectOptions,
|
|
375
404
|
sys.currentDirectory,
|
|
376
405
|
configFileName,
|
|
@@ -424,20 +453,20 @@ function executeCompile(sys: System, commandLine: ParsedCommandLine): CommandLin
|
|
|
424
453
|
reporter,
|
|
425
454
|
errorSummary,
|
|
426
455
|
buildInfo,
|
|
456
|
+
config: runConfigOf(config.options),
|
|
427
457
|
};
|
|
428
458
|
};
|
|
429
459
|
if (parsesAhead(config.options)) {
|
|
430
460
|
// Threads parse and bind the files the program is expected to load first (parallel-parse.ts), and
|
|
431
461
|
// share the program it becomes: its symbols get their ids when they are created.
|
|
432
462
|
assignSymbolIdsAtCreation();
|
|
433
|
-
const threads = checkerThreads(config.options.checkers, config.options.singleThreaded);
|
|
434
463
|
return {
|
|
435
464
|
kind: 'deferred',
|
|
436
465
|
run: (done: (result: CommandLineResult) => void) => {
|
|
437
466
|
const session =
|
|
438
467
|
early ??
|
|
439
468
|
new ParseSession(
|
|
440
|
-
|
|
469
|
+
runConfigOf(config.options).parseThreads,
|
|
441
470
|
config.options,
|
|
442
471
|
sys.currentDirectory,
|
|
443
472
|
configFileName,
|
|
@@ -26,7 +26,9 @@ import {
|
|
|
26
26
|
} from './incremental.js';
|
|
27
27
|
import {
|
|
28
28
|
checkerThreads,
|
|
29
|
+
memoryLimit,
|
|
29
30
|
parallelCheck,
|
|
31
|
+
residentMB,
|
|
30
32
|
shareWithCheckers,
|
|
31
33
|
startParallelCheck,
|
|
32
34
|
traceSingleChecker,
|
|
@@ -52,6 +54,22 @@ import {
|
|
|
52
54
|
type System,
|
|
53
55
|
SystemFileSystem,
|
|
54
56
|
} from './tsc-cli.js';
|
|
57
|
+
import { reportRunConfig, Source, type CheckSignals, type RunConfig, type RecordsReport } from './run-config.js';
|
|
58
|
+
import { availableCpus } from './cgroup.js';
|
|
59
|
+
import { doctorReportPath, setDoctorCompile, type DoctorCompile, type DoctorSignals } from './doctor-report.js';
|
|
60
|
+
import {
|
|
61
|
+
displayPath,
|
|
62
|
+
recordEnvironment,
|
|
63
|
+
recordKey,
|
|
64
|
+
recordsDirectory,
|
|
65
|
+
writeRecord,
|
|
66
|
+
type RecordChoice,
|
|
67
|
+
type RecordSample,
|
|
68
|
+
type RecordShape,
|
|
69
|
+
} from './run-records.js';
|
|
70
|
+
import { recordOptionsOf } from './run-record-read.js';
|
|
71
|
+
import { version } from './help.js';
|
|
72
|
+
import { loadavg } from 'node:os';
|
|
55
73
|
|
|
56
74
|
type CompileWork = Extract<CommandLineResult, { kind: 'compile' }>;
|
|
57
75
|
|
|
@@ -93,6 +111,12 @@ export function compile(sys: System, work: CompileWork): ExitStatus {
|
|
|
93
111
|
let compilerProgram: CompilerProgram;
|
|
94
112
|
if (work.incremental) {
|
|
95
113
|
const old = oldSnapshotOf(sys, work, times);
|
|
114
|
+
// An incremental recheck on old build info checks the files a change affects, not the program:
|
|
115
|
+
// a record's samples are full checks (`run-records.ts`).
|
|
116
|
+
if (old !== undefined && work.config !== undefined) {
|
|
117
|
+
work.config.records = false;
|
|
118
|
+
work.config.recordsReason = 'incremental recheck';
|
|
119
|
+
}
|
|
96
120
|
const changesStart = performance.now();
|
|
97
121
|
compilerProgram = new IncrementalProgram(checked, times, old, new SystemTimeStamps(sys));
|
|
98
122
|
times.changesComputeTime = performance.now() - changesStart;
|
|
@@ -106,7 +130,7 @@ export function compile(sys: System, work: CompileWork): ExitStatus {
|
|
|
106
130
|
outputLocale(program.options),
|
|
107
131
|
);
|
|
108
132
|
traceSingleChecker(checked);
|
|
109
|
-
return emitStatusAndReportStatistics(sys, compilerProgram, result);
|
|
133
|
+
return emitStatusAndReportStatistics(sys, compilerProgram, result, work.config);
|
|
110
134
|
}
|
|
111
135
|
|
|
112
136
|
/** The old state of `work`'s incremental program: read while threads parsed its files, or now. */
|
|
@@ -136,7 +160,12 @@ function oldSnapshotOf(sys: System, work: CompileWork, times: CompileTimes): Sna
|
|
|
136
160
|
export function compileThreads(_sys: System, work: CompileWork): number {
|
|
137
161
|
const options = work.program.options;
|
|
138
162
|
mark('compile threads');
|
|
139
|
-
|
|
163
|
+
// The run's configuration counts the checkers the program allows: one where it checks little, or
|
|
164
|
+
// where its text doesn't fit the machine's memory as many. A checker thread run decides again once
|
|
165
|
+
// the plan is made (`startParallelCheck`, run-config.ts).
|
|
166
|
+
const count = checkerThreads(options.checkers, options.singleThreaded, work.program);
|
|
167
|
+
work.config.checkers = count;
|
|
168
|
+
return count;
|
|
140
169
|
}
|
|
141
170
|
|
|
142
171
|
/** The number of threads to emit JavaScript on while this thread checks (`compileWithEmitThreads`), or 0. */
|
|
@@ -186,7 +215,7 @@ export function compileWithEmitThreads(
|
|
|
186
215
|
outputLocale(program.options),
|
|
187
216
|
);
|
|
188
217
|
traceSingleChecker(checked);
|
|
189
|
-
done(emitStatusAndReportStatistics(sys, compilerProgram, result));
|
|
218
|
+
done(emitStatusAndReportStatistics(sys, compilerProgram, result, work.config));
|
|
190
219
|
};
|
|
191
220
|
const emitKinds = emitKindsOf(compilerProgram);
|
|
192
221
|
if (emitKinds !== undefined) threads = emitThreads(program, program.options.singleThreaded, emitKinds);
|
|
@@ -333,7 +362,7 @@ export function compileWithDeclarationThread(
|
|
|
333
362
|
outputLocale(options),
|
|
334
363
|
);
|
|
335
364
|
traceSingleChecker(checked);
|
|
336
|
-
done(emitStatusAndReportStatistics(sys, compilerProgram, result));
|
|
365
|
+
done(emitStatusAndReportStatistics(sys, compilerProgram, result, work.config));
|
|
337
366
|
};
|
|
338
367
|
startDeclarationErrorThread(checked, (declarations) => {
|
|
339
368
|
// `PlainProgram.getDeclarationDiagnostics` reads them.
|
|
@@ -383,6 +412,11 @@ export function compileInParallel(
|
|
|
383
412
|
if (sharesPublishedGraphs) shareWithCheckers(checked);
|
|
384
413
|
checking = program.getSyntacticDiagnostics().length === 0 && program.getProgramDiagnostics().length === 0;
|
|
385
414
|
}
|
|
415
|
+
if (!checking && work.config !== undefined) {
|
|
416
|
+
// The threads only emit: nothing is checked, so there is no sample to keep.
|
|
417
|
+
work.config.records = false;
|
|
418
|
+
work.config.recordsReason = 'nothing checked';
|
|
419
|
+
}
|
|
386
420
|
const finish = (checkTime: number): void => {
|
|
387
421
|
// An incremental program here has no build info (`compileThreads`).
|
|
388
422
|
let compilerProgram: CompilerProgram;
|
|
@@ -402,7 +436,7 @@ export function compileInParallel(
|
|
|
402
436
|
);
|
|
403
437
|
times.checkTime = checkTime;
|
|
404
438
|
mark(`reported ${result.diagnostics.length} diagnostics`);
|
|
405
|
-
done(emitStatusAndReportStatistics(sys, compilerProgram, result));
|
|
439
|
+
done(emitStatusAndReportStatistics(sys, compilerProgram, result, work.config));
|
|
406
440
|
};
|
|
407
441
|
const options = program.options;
|
|
408
442
|
if (!checking && (options.noEmit === true || options.noEmitOnError === true)) return finish(0);
|
|
@@ -440,10 +474,13 @@ export function compileInParallel(
|
|
|
440
474
|
true,
|
|
441
475
|
kinds,
|
|
442
476
|
checking,
|
|
477
|
+
undefined,
|
|
478
|
+
undefined,
|
|
479
|
+
work.config,
|
|
443
480
|
);
|
|
444
481
|
return;
|
|
445
482
|
}
|
|
446
|
-
startParallelCheck(checked, threads, finish, true, undefined, checking);
|
|
483
|
+
startParallelCheck(checked, threads, finish, true, undefined, checking, undefined, undefined, work.config);
|
|
447
484
|
}
|
|
448
485
|
|
|
449
486
|
/**
|
|
@@ -463,6 +500,12 @@ function recheckInParallel(
|
|
|
463
500
|
done: (status: ExitStatus) => void,
|
|
464
501
|
): void {
|
|
465
502
|
const program = work.program;
|
|
503
|
+
// An incremental recheck measures a part of the program, which a record's samples are not comparable
|
|
504
|
+
// with full checks (`run-records.ts`).
|
|
505
|
+
if (work.config !== undefined) {
|
|
506
|
+
work.config.records = false;
|
|
507
|
+
work.config.recordsReason = 'incremental recheck';
|
|
508
|
+
}
|
|
466
509
|
const old = oldSnapshotOf(sys, work, times);
|
|
467
510
|
// The program is shared before its checker does more than merge the globals; the incremental program
|
|
468
511
|
// compares the references of its files with the old state on as many threads as check.
|
|
@@ -487,7 +530,7 @@ function recheckInParallel(
|
|
|
487
530
|
);
|
|
488
531
|
times.checkTime += threadsCheckTime;
|
|
489
532
|
mark(`reported ${result.diagnostics.length} diagnostics`);
|
|
490
|
-
done(emitStatusAndReportStatistics(sys, incrementalProgram, result));
|
|
533
|
+
done(emitStatusAndReportStatistics(sys, incrementalProgram, result, work.config));
|
|
491
534
|
};
|
|
492
535
|
// Reporting checks only programs without syntactic, program and global errors (getDiagnosticsOfProgram).
|
|
493
536
|
// The threads emit the files they check, as the checker that checks a file emits it on this thread: into
|
|
@@ -513,6 +556,9 @@ function recheckInParallel(
|
|
|
513
556
|
true,
|
|
514
557
|
incrementalProgram.emitKindsOnCheckers(files),
|
|
515
558
|
incrementalProgram.checksOnCheckers,
|
|
559
|
+
undefined,
|
|
560
|
+
undefined,
|
|
561
|
+
work.config,
|
|
516
562
|
);
|
|
517
563
|
}
|
|
518
564
|
|
|
@@ -580,20 +626,179 @@ export function reportEmitAndErrors(
|
|
|
580
626
|
return new CompileResult(all, emitResult);
|
|
581
627
|
}
|
|
582
628
|
|
|
583
|
-
/**
|
|
629
|
+
/**
|
|
630
|
+
* The exit status of a compilation, after printing `--diagnostics` statistics and, under
|
|
631
|
+
* `--extendedDiagnostics` only, the run configuration's `Suggestions:` section (run-config.ts).
|
|
632
|
+
*/
|
|
584
633
|
export function emitStatusAndReportStatistics(
|
|
585
634
|
sys: System,
|
|
586
635
|
program: CompilerProgram,
|
|
587
636
|
result: CompileResult,
|
|
637
|
+
config: RunConfig | undefined = undefined,
|
|
588
638
|
): ExitStatus {
|
|
589
639
|
const options = program.options;
|
|
640
|
+
const records = saveRunRecord(sys, program, config);
|
|
641
|
+
if (doctorReportPath() !== undefined) setDoctorCompile(doctorCompileOf(program, config));
|
|
590
642
|
if (options.diagnostics === true || options.extendedDiagnostics === true) {
|
|
591
643
|
program.times.totalTime = sys.sinceStart();
|
|
592
644
|
statisticsOf(program).report(sys);
|
|
645
|
+
if (options.extendedDiagnostics === true && config !== undefined) reportRunConfig(sys, config, records);
|
|
593
646
|
}
|
|
594
647
|
return exitStatusOf(result);
|
|
595
648
|
}
|
|
596
649
|
|
|
650
|
+
/**
|
|
651
|
+
* Writes this run's sample to its record (`run-records.ts`) and says what the section's `Records:` line
|
|
652
|
+
* reports. A run keeps no record where it may not write one (watch mode, a build's concurrent projects,
|
|
653
|
+
* `--singleThreaded`, Node builds, `TSC_TS_AUTO=0` or `static`, `TSC_TS_CACHE=0`), where it checked
|
|
654
|
+
* nothing, and where it rechecked the files a change affects on old build info (`recheckInParallel`, a
|
|
655
|
+
* build's projects). The write is best effort: a cache directory that cannot be written reports why and
|
|
656
|
+
* the run goes on.
|
|
657
|
+
*/
|
|
658
|
+
export function saveRunRecord(
|
|
659
|
+
sys: System,
|
|
660
|
+
program: CompilerProgram,
|
|
661
|
+
config: RunConfig | undefined,
|
|
662
|
+
): RecordsReport | undefined {
|
|
663
|
+
if (config === undefined) return undefined;
|
|
664
|
+
if (!config.records) return { off: config.recordsReason ?? 'not measured' };
|
|
665
|
+
if (!sharesPublishedGraphs) return { off: 'Node.js builds' };
|
|
666
|
+
if (program.options.singleThreaded === true) return { off: '--singleThreaded' };
|
|
667
|
+
// A run that stopped before checking (`--noCheck`, syntax errors) has nothing to sample.
|
|
668
|
+
if (!program.checkedTheProgram) return { off: 'nothing checked' };
|
|
669
|
+
// A run that read its record before parsing already knows the directory and key (`openRecord`); a
|
|
670
|
+
// build project that did not computes them now, from the parsed program.
|
|
671
|
+
const directory = config.recordDirectory ?? recordsDirectory(recordEnvironment());
|
|
672
|
+
if (directory === undefined) return { off: 'TSC_TS_CACHE=0' };
|
|
673
|
+
const shape = config.shape ?? recordShapeOf(program, config);
|
|
674
|
+
const key = config.recordKey ?? recordKeyOf(program);
|
|
675
|
+
const written = writeRecord(
|
|
676
|
+
directory,
|
|
677
|
+
key,
|
|
678
|
+
recordSampleOf(sys, program, config),
|
|
679
|
+
shape,
|
|
680
|
+
chosenOf(config),
|
|
681
|
+
config.rejected,
|
|
682
|
+
);
|
|
683
|
+
if (written.path !== undefined) return { path: displayPath(written.path) };
|
|
684
|
+
return { unavailable: written.reason };
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/** The key this run's record has: the build, the project, the options and the machine (`run-records.ts`). */
|
|
688
|
+
function recordKeyOf(program: CompilerProgram): string {
|
|
689
|
+
return recordKey({
|
|
690
|
+
version,
|
|
691
|
+
executable: process.execPath,
|
|
692
|
+
configFile: program.options.configFilePath,
|
|
693
|
+
rootNames: program.program.commandLine.fileNames,
|
|
694
|
+
options: recordOptionsOf(program.options),
|
|
695
|
+
cpus: Math.max(1, availableCpus()),
|
|
696
|
+
memoryBytes: memoryLimit(),
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/** What this compile measured, for a doctor child's report (`doctor-report.ts`). */
|
|
701
|
+
function doctorCompileOf(program: CompilerProgram, config: RunConfig | undefined): DoctorCompile {
|
|
702
|
+
const times = program.times;
|
|
703
|
+
const signals = config?.signals;
|
|
704
|
+
return {
|
|
705
|
+
version,
|
|
706
|
+
configFile: program.options.configFilePath,
|
|
707
|
+
key: recordKeyOf(program),
|
|
708
|
+
checkers: config?.checkers ?? 1,
|
|
709
|
+
share: config?.share ?? false,
|
|
710
|
+
parseThreads: config?.parseThreads ?? 1,
|
|
711
|
+
times: {
|
|
712
|
+
parseMs: times.parseTime + times.bindTime,
|
|
713
|
+
checkMs: times.checkTime,
|
|
714
|
+
totalMs:
|
|
715
|
+
times.totalTime !== 0 ? times.totalTime : times.parseTime + times.bindTime + times.checkTime + times.emitTime,
|
|
716
|
+
},
|
|
717
|
+
shape: recordShapeOf(program, config),
|
|
718
|
+
signals: signals === undefined ? undefined : doctorSignalsOf(signals),
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/** The bound program's signals as a doctor report keeps them. */
|
|
723
|
+
function doctorSignalsOf(signals: CheckSignals): DoctorSignals {
|
|
724
|
+
return {
|
|
725
|
+
count: signals.count,
|
|
726
|
+
ranges: signals.ranges,
|
|
727
|
+
library: signals.library,
|
|
728
|
+
own: signals.own,
|
|
729
|
+
redundancy: signals.redundancy,
|
|
730
|
+
declarationShare: signals.declarationShare,
|
|
731
|
+
nodeModulesShare: signals.nodeModulesShare,
|
|
732
|
+
fileShare: signals.fileShare,
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/** The program's size, and the library and redundancy signals where the run computed them. */
|
|
737
|
+
function recordShapeOf(program: CompilerProgram, config: RunConfig | undefined): RecordShape {
|
|
738
|
+
let files = 0;
|
|
739
|
+
let chars = 0;
|
|
740
|
+
let declarationChars = 0;
|
|
741
|
+
for (const file of program.program.getSourceFiles()) {
|
|
742
|
+
files++;
|
|
743
|
+
chars += file.end;
|
|
744
|
+
if (file.isDeclarationFile) declarationChars += file.end;
|
|
745
|
+
}
|
|
746
|
+
const signals = config?.signals;
|
|
747
|
+
return {
|
|
748
|
+
files,
|
|
749
|
+
chars,
|
|
750
|
+
declarationChars,
|
|
751
|
+
checkChars: chars - declarationChars,
|
|
752
|
+
library: signals === undefined ? 0 : Math.round(signals.library),
|
|
753
|
+
own: signals === undefined ? 0 : Math.round(signals.own),
|
|
754
|
+
redundancy: signals === undefined ? 0 : signals.redundancy,
|
|
755
|
+
fileShare: signals === undefined ? 0 : Math.max(signals.declarationShare, signals.nodeModulesShare),
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
/** What this run measured, for its record (`RecordSample`, without the number the record gives it). */
|
|
760
|
+
function recordSampleOf(sys: System, program: CompilerProgram, config: RunConfig): Omit<RecordSample, 'run'> {
|
|
761
|
+
const times = program.times;
|
|
762
|
+
const usage = process.cpuUsage();
|
|
763
|
+
const cpus = Math.max(1, availableCpus());
|
|
764
|
+
return {
|
|
765
|
+
at: Math.floor(Date.now() / 1000),
|
|
766
|
+
role: config.role,
|
|
767
|
+
checkers: config.checkers,
|
|
768
|
+
share: config.share,
|
|
769
|
+
bounded: config.bounded,
|
|
770
|
+
parseThreads: config.parseThreads,
|
|
771
|
+
parseMs: Math.round(times.parseTime + times.bindTime),
|
|
772
|
+
checkMs: Math.round(times.checkTime),
|
|
773
|
+
totalMs: Math.round(sys.sinceStart()),
|
|
774
|
+
cpuMs: Math.round((usage.user + usage.system) / 1000),
|
|
775
|
+
peakMB: residentMB()[1] ?? 0,
|
|
776
|
+
load: Math.round((loadavg()[0]! / cpus) * 100) / 100,
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* The configuration this run chose for the next one, as its record keeps it (the record numbers the run
|
|
782
|
+
* that chose it). The learning rules name it (`config.chosen`); a run without them records the choice it
|
|
783
|
+
* used, and whether a pin made it.
|
|
784
|
+
*/
|
|
785
|
+
function chosenOf(config: RunConfig): RecordChoice {
|
|
786
|
+
if (config.chosen !== undefined) return config.chosen;
|
|
787
|
+
return {
|
|
788
|
+
checkers: config.checkers,
|
|
789
|
+
share: config.share,
|
|
790
|
+
bounded: config.bounded,
|
|
791
|
+
parseThreads: config.parseThreads,
|
|
792
|
+
source:
|
|
793
|
+
config.checkersSource === Source.Pinned ||
|
|
794
|
+
config.shareSource === Source.Pinned ||
|
|
795
|
+
config.boundedSource === Source.Pinned
|
|
796
|
+
? 'pinned'
|
|
797
|
+
: 'default',
|
|
798
|
+
since: 0,
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
|
|
597
802
|
export function exitStatusOf(result: CompileResult): ExitStatus {
|
|
598
803
|
if (result.diagnostics.length === 0) return ExitStatus.Success;
|
|
599
804
|
return result.emitResult.emitSkipped
|
|
@@ -673,6 +878,8 @@ export class Statistics {
|
|
|
673
878
|
rows.push([prefix + 'Types', String(this.types)]);
|
|
674
879
|
rows.push([prefix + 'Instantiations', String(this.instantiations)]);
|
|
675
880
|
rows.push([prefix + 'Memory used', `${Math.floor(this.memoryUsed / 1024)}K`]);
|
|
881
|
+
// The table has tsgo's rows. No runtime counter matches its allocation count (runtime.MemStats.Mallocs),
|
|
882
|
+
// so this one stays 0.
|
|
676
883
|
rows.push([prefix + 'Memory allocs', String(this.memoryAllocs)]);
|
|
677
884
|
const t = this.times;
|
|
678
885
|
if (t.configTime !== 0) rows.push([prefix + 'Config time', formatDuration(t.configTime)]);
|
|
@@ -711,7 +918,14 @@ export function statisticsOf(compilerProgram: CompilerProgram): Statistics {
|
|
|
711
918
|
statistics.identifiers += file.identifierCount;
|
|
712
919
|
statistics.symbols += file.symbolCount;
|
|
713
920
|
}
|
|
714
|
-
|
|
921
|
+
// Every checker's counts, the checker threads' included, as tsgo's statisticsFromProgram reports them.
|
|
922
|
+
const counts = compilerProgram.checked.checkerCounts();
|
|
923
|
+
statistics.symbols += counts.symbols;
|
|
924
|
+
statistics.types = counts.types;
|
|
925
|
+
statistics.instantiations = counts.instantiations;
|
|
926
|
+
// tsgo reports the Go heap in use (runtime.MemStats.Alloc); the closest this runtime has is the
|
|
927
|
+
// process's resident set, which includes the checker threads' heaps.
|
|
928
|
+
statistics.memoryUsed = process.memoryUsage.rss();
|
|
715
929
|
const t = statistics.times;
|
|
716
930
|
const source = compilerProgram.times;
|
|
717
931
|
t.configTime = source.configTime;
|
|
@@ -43,7 +43,8 @@ import {
|
|
|
43
43
|
} from './tsc-cli.js';
|
|
44
44
|
import { isIncremental } from './options.js';
|
|
45
45
|
import { ParseSession, clearPreparedFiles, parallelParse, takePreparedFile } from './parallel-parse.js';
|
|
46
|
-
import { checkerThreads,
|
|
46
|
+
import { checkerThreads, shareWithCheckers, startParallelCheck } from './parallel-check.js';
|
|
47
|
+
import { preParseConfig, type RunConfig } from './run-config.js';
|
|
47
48
|
import { assignSymbolIdsAtCreation, symbolIdsAtCreation } from './symbol.js';
|
|
48
49
|
import { Orchestrator, StatusReporter, startWatchRebuild } from './tsc-build.js';
|
|
49
50
|
import { mark } from './timeline.js';
|
|
@@ -452,6 +453,8 @@ class Watcher {
|
|
|
452
453
|
/** Whether a rebuild is running (`poll`), and whether a check came during it. */
|
|
453
454
|
private building = false;
|
|
454
455
|
private checkPending = false;
|
|
456
|
+
/** The first program's run configuration, which its rebuilds use too (run-config.ts). */
|
|
457
|
+
private run: RunConfig | undefined = undefined;
|
|
455
458
|
|
|
456
459
|
constructor(
|
|
457
460
|
private readonly sys: System,
|
|
@@ -516,8 +519,15 @@ class Watcher {
|
|
|
516
519
|
const config = copyCommandLine(this.config);
|
|
517
520
|
const options = config.options;
|
|
518
521
|
const incremental = isIncremental(options);
|
|
522
|
+
// A watch chooses its configuration once, for its first program: parse threads are fixed before
|
|
523
|
+
// the program is parsed, and its rebuilds check what changed as that program did.
|
|
524
|
+
const run = (this.run = preParseConfig(options, checkerThreads(options.checkers, options.singleThreaded)));
|
|
525
|
+
// A watcher's rebuilds measure a change, not the project: it reads records for its first program only
|
|
526
|
+
// and keeps none (run-records.ts).
|
|
527
|
+
run.records = false;
|
|
528
|
+
run.recordsReason = 'watch mode';
|
|
519
529
|
const session = new ParseSession(
|
|
520
|
-
parseThreads
|
|
530
|
+
run.parseThreads,
|
|
521
531
|
options,
|
|
522
532
|
sys.currentDirectory,
|
|
523
533
|
config.configName,
|
|
@@ -585,7 +595,8 @@ class Watcher {
|
|
|
585
595
|
this.finishBuild(checked);
|
|
586
596
|
finish();
|
|
587
597
|
};
|
|
588
|
-
if (this.snapshot === undefined && threads > 1)
|
|
598
|
+
if (this.snapshot === undefined && threads > 1)
|
|
599
|
+
startParallelCheck(checked, threads, report, true, undefined, true, undefined, undefined, run);
|
|
589
600
|
else report();
|
|
590
601
|
});
|
|
591
602
|
if (incremental && sys.fileExists(config.buildInfoFileName())) session.queueAhead();
|
|
@@ -802,7 +813,17 @@ class Watcher {
|
|
|
802
813
|
this.report(incremental, changedFiles);
|
|
803
814
|
done();
|
|
804
815
|
};
|
|
805
|
-
startParallelCheck(
|
|
816
|
+
startParallelCheck(
|
|
817
|
+
checked,
|
|
818
|
+
threads,
|
|
819
|
+
finish,
|
|
820
|
+
true,
|
|
821
|
+
incremental.emitKindsOnCheckers(files),
|
|
822
|
+
true,
|
|
823
|
+
undefined,
|
|
824
|
+
undefined,
|
|
825
|
+
this.run,
|
|
826
|
+
);
|
|
806
827
|
return;
|
|
807
828
|
}
|
|
808
829
|
}
|