tsc-ts 0.0.9 → 0.0.10
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 +1 -1
- package/dist/api/compiler/ast.d.ts +2 -0
- package/dist/api/compiler/ast.js +3 -0
- package/dist/api/compiler/checker-calls.js +5 -1
- package/dist/api/compiler/checker-grammar.js +3 -2
- package/dist/api/compiler/checker-inference.js +8 -2
- package/dist/api/compiler/checker-members.js +2 -1
- package/dist/api/compiler/checker-type-factory.js +8 -2
- package/dist/api/compiler/checker.d.ts +11 -0
- package/dist/api/compiler/checker.js +40 -0
- package/dist/api/compiler/compile-program.d.ts +26 -2
- package/dist/api/compiler/compile-program.js +100 -7
- package/dist/api/compiler/diagnostics.d.ts +22 -0
- package/dist/api/compiler/diagnostics.js +86 -4
- package/dist/api/compiler/file-loader.d.ts +11 -1
- package/dist/api/compiler/file-loader.js +63 -7
- package/dist/api/compiler/parallel-parse.d.ts +3 -2
- package/dist/api/compiler/parallel-parse.js +17 -5
- package/dist/api/compiler/printer/emit-context.d.ts +2 -0
- package/dist/api/compiler/printer/emit-context.js +4 -0
- package/dist/api/compiler/printer/printer.d.ts +48 -2
- package/dist/api/compiler/printer/printer.js +238 -14
- package/dist/api/compiler/program-checker.d.ts +4 -0
- package/dist/api/compiler/program-checker.js +4 -0
- package/dist/api/compiler/program.d.ts +2 -6
- package/dist/api/compiler/program.js +5 -30
- package/dist/api/compiler/relations.js +10 -0
- package/dist/api/compiler/symbol.d.ts +1 -1
- package/dist/api/compiler/symbol.js +8 -10
- package/package.json +9 -9
- package/src/compiler/ast.ts +3 -0
- package/src/compiler/checker-calls.ts +3 -1
- package/src/compiler/checker-grammar.ts +3 -2
- package/src/compiler/checker-inference.ts +7 -2
- package/src/compiler/checker-members.ts +3 -1
- package/src/compiler/checker-type-factory.ts +4 -2
- package/src/compiler/checker.ts +37 -0
- package/src/compiler/compile-program.ts +121 -7
- package/src/compiler/diagnostics.ts +84 -3
- package/src/compiler/file-loader.ts +58 -6
- package/src/compiler/incremental.ts +80 -13
- package/src/compiler/parallel-check.ts +129 -44
- package/src/compiler/parallel-emit.ts +320 -0
- package/src/compiler/parallel-parse.ts +16 -5
- package/src/compiler/parse-thread.ts +3 -0
- package/src/compiler/printer/emit-context.ts +5 -0
- package/src/compiler/printer/printer.ts +231 -16
- package/src/compiler/program-checker.ts +4 -0
- package/src/compiler/program.ts +5 -25
- package/src/compiler/relations.ts +7 -0
- package/src/compiler/symbol.ts +15 -10
- package/src/compiler/thread-entry.ts +45 -2
- package/src/compiler/tsc-build.ts +63 -9
- package/src/compiler/tsc-cli.ts +5 -0
- package/src/compiler/tsc-compile.ts +278 -3
|
@@ -1574,7 +1574,9 @@ export function getPropertyOfTypeEx(
|
|
|
1574
1574
|
): Symbol | undefined {
|
|
1575
1575
|
if (!isOwnReducedApparentType(t)) t = getReducedApparentTypeWorker(c, t);
|
|
1576
1576
|
if ((t.flags & TypeFlags.Object) !== 0) {
|
|
1577
|
-
|
|
1577
|
+
// The first test of resolveStructuredTypeMembers, here where most lookups find the members resolved.
|
|
1578
|
+
const resolved =
|
|
1579
|
+
(t.objectFlags & ObjectFlags.MembersResolved) !== 0 ? (t as StructuredType) : resolveStructuredTypeMembers(c, t);
|
|
1578
1580
|
const symbol = getResolvedMember(resolved, name);
|
|
1579
1581
|
if (symbol !== undefined && includeTypeOnlyMembers) return symbol;
|
|
1580
1582
|
if (symbol !== undefined) {
|
|
@@ -998,7 +998,8 @@ export function unionContainsSortedTypes(c: Checker, union: UnionType, types: re
|
|
|
998
998
|
export function insertType(c: Checker, types: Type[], t: Type): boolean {
|
|
999
999
|
const index = binarySearchTypes(c, types, t);
|
|
1000
1000
|
if (index >= 0) return false;
|
|
1001
|
-
|
|
1001
|
+
if (~index === types.length) types.push(t);
|
|
1002
|
+
else types.splice(~index, 0, t);
|
|
1002
1003
|
return true;
|
|
1003
1004
|
}
|
|
1004
1005
|
|
|
@@ -1214,7 +1215,8 @@ function insertTypeNearLast(c: Checker, set: TypeSet, t: Type): void {
|
|
|
1214
1215
|
if (r < 0) low = middle + 1;
|
|
1215
1216
|
else high = middle - 1;
|
|
1216
1217
|
}
|
|
1217
|
-
|
|
1218
|
+
if (low === types.length) types.push(t);
|
|
1219
|
+
else types.splice(low, 0, t);
|
|
1218
1220
|
set.last = low;
|
|
1219
1221
|
}
|
|
1220
1222
|
|
package/src/compiler/checker.ts
CHANGED
|
@@ -245,6 +245,30 @@ class DiagnosticCollection {
|
|
|
245
245
|
return this.global.slice();
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
+
/** The global diagnostics `withoutNewGlobals` removed. */
|
|
249
|
+
removedGlobals = 0;
|
|
250
|
+
|
|
251
|
+
/** Runs `run`, then removes the global diagnostics it added. */
|
|
252
|
+
withoutNewGlobals<T>(run: () => T): T {
|
|
253
|
+
const outer = this.log;
|
|
254
|
+
const added: Diagnostic[] = [];
|
|
255
|
+
this.log = added;
|
|
256
|
+
try {
|
|
257
|
+
return run();
|
|
258
|
+
} finally {
|
|
259
|
+
this.log = outer;
|
|
260
|
+
if (outer !== undefined) for (const d of added) outer.push(d);
|
|
261
|
+
const globals = new Set<Diagnostic>();
|
|
262
|
+
for (const d of added) if (d.file === undefined) globals.add(d);
|
|
263
|
+
if (globals.size > 0) {
|
|
264
|
+
this.removedGlobals += globals.size;
|
|
265
|
+
let kept = 0;
|
|
266
|
+
for (const d of this.global) if (!globals.has(d)) this.global[kept++] = d;
|
|
267
|
+
this.global.length = kept;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
248
272
|
/** Records the diagnostics added from now on. */
|
|
249
273
|
startLog(): void {
|
|
250
274
|
this.log = [];
|
|
@@ -940,6 +964,19 @@ export class Checker implements TypeChecking {
|
|
|
940
964
|
return this.diagnostics.globals();
|
|
941
965
|
}
|
|
942
966
|
|
|
967
|
+
/**
|
|
968
|
+
* Runs `run` (such as emitting declarations), then forgets the global diagnostics it reported:
|
|
969
|
+
* the program collects global diagnostics before it emits.
|
|
970
|
+
*/
|
|
971
|
+
withoutGlobalDiagnostics<T>(run: () => T): T {
|
|
972
|
+
return this.diagnostics.withoutNewGlobals(run);
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
/** The global diagnostics `withoutGlobalDiagnostics` forgot so far. */
|
|
976
|
+
forgottenGlobalDiagnostics(): number {
|
|
977
|
+
return this.diagnostics.removedGlobals;
|
|
978
|
+
}
|
|
979
|
+
|
|
943
980
|
// ---- Links
|
|
944
981
|
|
|
945
982
|
/** The rarely used links of `symbol`, created on first use. */
|
|
@@ -13,7 +13,11 @@ import { CheckerDeclarationEmitResolver, CheckerEmitResolver } from './checker-e
|
|
|
13
13
|
import { getEmitModuleFormatOfFile } from './program-utilities.js';
|
|
14
14
|
import { DeclarationGlobals, SyntacticDeclarationEmitResolver } from './transformers/declarations-syntactic.js';
|
|
15
15
|
import { mark } from './timeline.js';
|
|
16
|
-
import
|
|
16
|
+
import { DecoratorMetadata } from './transformers/decorator-metadata.js';
|
|
17
|
+
import { SyntaxFacts } from './transformers/syntax-facts.js';
|
|
18
|
+
import type { TypeReferenceSerializationKind } from './transformers/type-serialization.js';
|
|
19
|
+
import { EmitContext } from './printer/emit-context.js';
|
|
20
|
+
import { type EmitResolver, SyntacticEmitResolver } from './transformers/emit-resolver.js';
|
|
17
21
|
import { ModuleFormatHost } from './transformers/module.js';
|
|
18
22
|
import { type MergedGlobals, ModuleLookup, ReferenceResolver } from './transformers/reference-resolver.js';
|
|
19
23
|
|
|
@@ -193,6 +197,100 @@ export class EmitIndexes {
|
|
|
193
197
|
}
|
|
194
198
|
}
|
|
195
199
|
|
|
200
|
+
/**
|
|
201
|
+
* The JavaScript emit of a program's files without a checker, for emit threads (parallel-emit.ts).
|
|
202
|
+
* JavaScript emit asks the checker only how decorator metadata serializes a type
|
|
203
|
+
* (`CheckerEmitResolver`): with `kinds`, the answers the main thread's checker gave
|
|
204
|
+
* (`recordMetadataKinds`), else files with decorator metadata are emitted by a `ProgramEmitter`.
|
|
205
|
+
*/
|
|
206
|
+
export class JavaScriptEmitter {
|
|
207
|
+
private readonly host: ProgramEmitHost;
|
|
208
|
+
private readonly recorded: RecordedKindsEmitResolver | undefined;
|
|
209
|
+
|
|
210
|
+
constructor(program: Program, kinds: readonly number[] = []) {
|
|
211
|
+
const options = program.options;
|
|
212
|
+
const indexes = program.emitIndexes;
|
|
213
|
+
const references = new ReferenceResolver(
|
|
214
|
+
options,
|
|
215
|
+
program.getSourceFiles(),
|
|
216
|
+
new ProgramModules(program),
|
|
217
|
+
indexes !== undefined ? indexes.globals : undefined,
|
|
218
|
+
);
|
|
219
|
+
this.recorded = kinds.length > 0 ? new RecordedKindsEmitResolver(options, references, kinds) : undefined;
|
|
220
|
+
this.host = new ProgramEmitHost(program, this.recorded ?? new SyntacticEmitResolver(options, references));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Emits `file`, or returns undefined if its decorator metadata asked for a kind that wasn't recorded: what
|
|
225
|
+
* was written is then not the file's emit, and it must be emitted with the checker.
|
|
226
|
+
*/
|
|
227
|
+
emit(file: SourceFile, writer: OutputWriter): EmitResult | undefined {
|
|
228
|
+
const host = this.host;
|
|
229
|
+
const recorded = this.recorded;
|
|
230
|
+
if (recorded !== undefined) recorded.missed = false;
|
|
231
|
+
host.writer = writer;
|
|
232
|
+
const result = emitFiles(host, [file], EmitOnly.Js);
|
|
233
|
+
host.writer = undefined;
|
|
234
|
+
return recorded !== undefined && recorded.missed ? undefined : result;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* The serialization kinds of decorator metadata that the checker of `checked` gives the type references of
|
|
240
|
+
* `files`, in the order JavaScript emit asks for them: the type name's node id, the location's node id (-1 for
|
|
241
|
+
* none) and the kind, for each. Only the decorator metadata transform runs, as the first transform of emit.
|
|
242
|
+
*/
|
|
243
|
+
export function recordMetadataKinds(checked: CheckedProgram, files: readonly SourceFile[]): number[] {
|
|
244
|
+
const program = checked.program;
|
|
245
|
+
const options = program.options;
|
|
246
|
+
const indexes = program.emitIndexes;
|
|
247
|
+
const references = new ReferenceResolver(
|
|
248
|
+
options,
|
|
249
|
+
program.getSourceFiles(),
|
|
250
|
+
new ProgramModules(program),
|
|
251
|
+
indexes !== undefined ? indexes.globals : undefined,
|
|
252
|
+
);
|
|
253
|
+
const resolver = new RecordingEmitResolver(options, references, checked.checker);
|
|
254
|
+
for (const file of files)
|
|
255
|
+
new DecoratorMetadata(new EmitContext(), options, resolver, new SyntaxFacts()).transformSourceFile(file);
|
|
256
|
+
return resolver.kinds;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** A `CheckerEmitResolver` that records the serialization kinds it gives (`recordMetadataKinds`). */
|
|
260
|
+
class RecordingEmitResolver extends CheckerEmitResolver {
|
|
261
|
+
readonly kinds: number[] = [];
|
|
262
|
+
|
|
263
|
+
getTypeReferenceSerializationKind(typeName: Node, location: Node | undefined): TypeReferenceSerializationKind {
|
|
264
|
+
const kind = super.getTypeReferenceSerializationKind(typeName, location);
|
|
265
|
+
this.kinds.push(typeName.id, location === undefined ? -1 : location.id, kind);
|
|
266
|
+
return kind;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** Answers serialization kinds from those recorded on the main thread; `missed` once one wasn't. */
|
|
271
|
+
class RecordedKindsEmitResolver extends SyntacticEmitResolver {
|
|
272
|
+
/** The kind and location of each type name, by the type name's node id. */
|
|
273
|
+
private readonly byName = new Map<number, number>();
|
|
274
|
+
missed = false;
|
|
275
|
+
|
|
276
|
+
constructor(
|
|
277
|
+
options: CompilerOptions,
|
|
278
|
+
referenceResolver: ReferenceResolver,
|
|
279
|
+
private readonly kinds: readonly number[],
|
|
280
|
+
) {
|
|
281
|
+
super(options, referenceResolver);
|
|
282
|
+
for (let i = 0; i < kinds.length; i += 3) if (!this.byName.has(kinds[i]!)) this.byName.set(kinds[i]!, i);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
getTypeReferenceSerializationKind(typeName: Node, location: Node | undefined): TypeReferenceSerializationKind {
|
|
286
|
+
const at = this.byName.get(typeName.id);
|
|
287
|
+
if (at !== undefined && this.kinds[at + 1] === (location === undefined ? -1 : location.id))
|
|
288
|
+
return this.kinds[at + 2]! as TypeReferenceSerializationKind;
|
|
289
|
+
this.missed = true;
|
|
290
|
+
return super.getTypeReferenceSerializationKind(typeName, location);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
196
294
|
/** A checked program and the emit of its files. */
|
|
197
295
|
export class ProgramEmitter {
|
|
198
296
|
private host: ProgramEmitHost | undefined = undefined;
|
|
@@ -354,16 +452,25 @@ export class PlainProgram extends CompilerProgram {
|
|
|
354
452
|
const program = this.program;
|
|
355
453
|
const files = program.getSourceFiles();
|
|
356
454
|
const parallel = this.checked.emittedInParallel;
|
|
357
|
-
// Checker threads emitted the files they checked (parallel-check.ts)
|
|
455
|
+
// Checker threads emitted the files they checked (parallel-check.ts), or emit threads their JavaScript
|
|
456
|
+
// (parallel-emit.ts); the rest is emitted here, in order.
|
|
358
457
|
const result =
|
|
359
458
|
parallel === undefined
|
|
360
459
|
? this.emitter.emit(files, EmitOnly.All, writer)
|
|
361
460
|
: combineEmitResults(
|
|
362
461
|
files.map((file, i) => {
|
|
363
462
|
const emitted = parallel[i];
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
463
|
+
// The declaration thread emitted the declarations of the files it could (parallel-emit.ts).
|
|
464
|
+
const ahead = this.checked.declarationsEmittedAhead;
|
|
465
|
+
const declarations = (): EmitResult => ahead?.[i] ?? this.emitter.emit([file], EmitOnly.Dts, writer);
|
|
466
|
+
if (emitted === undefined)
|
|
467
|
+
return ahead !== undefined
|
|
468
|
+
? combineEmitResults([this.emitter.emit([file], EmitOnly.Js, writer), declarations()])
|
|
469
|
+
: this.emitter.emit([file], EmitOnly.All, writer);
|
|
470
|
+
const written = writeHeldOutputs(emitted, writer);
|
|
471
|
+
// Emit threads emit JavaScript only.
|
|
472
|
+
if (!this.checked.javaScriptEmittedOnThreads || !getEmitDeclarations(options)) return written;
|
|
473
|
+
return combineEmitResults([written, declarations()]);
|
|
367
474
|
}),
|
|
368
475
|
);
|
|
369
476
|
if (options.noEmit === true) {
|
|
@@ -390,9 +497,15 @@ export function handleNoEmitOnError(program: CompilerProgram): EmitResult | unde
|
|
|
390
497
|
|
|
391
498
|
/**
|
|
392
499
|
* Config and syntax errors; without syntax errors, program, global and semantic errors too, and
|
|
393
|
-
* declaration errors under `noEmit` (or `withDeclarationDiagnostics`).
|
|
500
|
+
* declaration errors under `noEmit` (or `withDeclarationDiagnostics`). With `declarationsLater`, the
|
|
501
|
+
* declaration errors are left to `program.emit`, which reports them under `noEmit` (without
|
|
502
|
+
* `noEmitOnError`) whether or not other errors were found.
|
|
394
503
|
*/
|
|
395
|
-
export function getDiagnosticsOfProgram(
|
|
504
|
+
export function getDiagnosticsOfProgram(
|
|
505
|
+
program: CompilerProgram,
|
|
506
|
+
withDeclarationDiagnostics: boolean,
|
|
507
|
+
declarationsLater: boolean = false,
|
|
508
|
+
): Diagnostic[] {
|
|
396
509
|
const result: Diagnostic[] = [...program.getConfigFileParsingDiagnostics()];
|
|
397
510
|
const configCount = result.length;
|
|
398
511
|
for (const d of program.getSyntacticDiagnostics()) result.push(d);
|
|
@@ -414,6 +527,7 @@ export function getDiagnosticsOfProgram(program: CompilerProgram, withDeclaratio
|
|
|
414
527
|
mark('semantic diagnostics');
|
|
415
528
|
}
|
|
416
529
|
if (
|
|
530
|
+
!declarationsLater &&
|
|
417
531
|
(withDeclarationDiagnostics || options.noEmit === true) &&
|
|
418
532
|
getEmitDeclarations(options) &&
|
|
419
533
|
result.length === configCount
|
|
@@ -347,6 +347,11 @@ export class Diagnostic {
|
|
|
347
347
|
readonly reportsDeprecated: boolean;
|
|
348
348
|
skippedOnNoEmit = false;
|
|
349
349
|
repopulateInfo: RepopulateInfo | undefined = undefined;
|
|
350
|
+
/**
|
|
351
|
+
* What `formatDiagnostic` gives for this diagnostic in `formattedReports.format`, where the checker
|
|
352
|
+
* thread that found it formatted it (`packResult`); `''` otherwise.
|
|
353
|
+
*/
|
|
354
|
+
formatted = '';
|
|
350
355
|
|
|
351
356
|
constructor(
|
|
352
357
|
file: DiagnosticFile | undefined,
|
|
@@ -388,6 +393,7 @@ export class Diagnostic {
|
|
|
388
393
|
result.relatedInformation = this.relatedInformation;
|
|
389
394
|
result.skippedOnNoEmit = this.skippedOnNoEmit;
|
|
390
395
|
result.repopulateInfo = this.repopulateInfo;
|
|
396
|
+
result.formatted = this.formatted;
|
|
391
397
|
return result;
|
|
392
398
|
}
|
|
393
399
|
}
|
|
@@ -510,9 +516,13 @@ function compareRelatedInfo(a: readonly Diagnostic[], b: readonly Diagnostic[]):
|
|
|
510
516
|
/** The order tsc reports diagnostics in: by file, position, code, arguments, then elaboration. */
|
|
511
517
|
export function compareDiagnostics(a: Diagnostic, b: Diagnostic): number {
|
|
512
518
|
if (a === b) return 0;
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
519
|
+
const c = compareCodePoints(fileNameOf(a), fileNameOf(b));
|
|
520
|
+
return c !== 0 ? c : compareDiagnosticsInFile(a, b);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/** `compareDiagnostics` of two diagnostics whose files have the same name. */
|
|
524
|
+
function compareDiagnosticsInFile(a: Diagnostic, b: Diagnostic): number {
|
|
525
|
+
let c = a.pos - b.pos;
|
|
516
526
|
if (c !== 0) return c;
|
|
517
527
|
c = a.end - b.end;
|
|
518
528
|
if (c !== 0) return c;
|
|
@@ -527,6 +537,61 @@ export function compareDiagnostics(a: Diagnostic, b: Diagnostic): number {
|
|
|
527
537
|
return compareRelatedInfo(a.relatedInformation, b.relatedInformation);
|
|
528
538
|
}
|
|
529
539
|
|
|
540
|
+
/**
|
|
541
|
+
* `diagnostics` in the order of `compareDiagnostics`, equal ones in their order in `diagnostics`. File
|
|
542
|
+
* names are compared once per pair of files rather than once per pair of diagnostics: the distinct
|
|
543
|
+
* files are sorted by name, and each diagnostic sorts by its file's place in that order, then by the
|
|
544
|
+
* rest of `compareDiagnostics`. A program's diagnostics come a file at a time, so the files are found
|
|
545
|
+
* from the runs of one file.
|
|
546
|
+
*/
|
|
547
|
+
export function sortDiagnostics(diagnostics: readonly Diagnostic[]): Diagnostic[] {
|
|
548
|
+
const n = diagnostics.length;
|
|
549
|
+
if (n < 2) return diagnostics.slice();
|
|
550
|
+
const fileIndices = new Map<DiagnosticFile | undefined, number>();
|
|
551
|
+
const files: (DiagnosticFile | undefined)[] = [];
|
|
552
|
+
const fileOf: number[] = [];
|
|
553
|
+
let last: DiagnosticFile | undefined = undefined;
|
|
554
|
+
let lastIndex = -1;
|
|
555
|
+
for (let i = 0; i < n; i++) {
|
|
556
|
+
const file = diagnostics[i]!.file;
|
|
557
|
+
if (lastIndex < 0 || file !== last) {
|
|
558
|
+
let k = fileIndices.get(file);
|
|
559
|
+
if (k === undefined) {
|
|
560
|
+
k = files.length;
|
|
561
|
+
fileIndices.set(file, k);
|
|
562
|
+
files.push(file);
|
|
563
|
+
}
|
|
564
|
+
last = file;
|
|
565
|
+
lastIndex = k;
|
|
566
|
+
}
|
|
567
|
+
fileOf.push(lastIndex);
|
|
568
|
+
}
|
|
569
|
+
// Files with the same name share a rank, as they compare equal.
|
|
570
|
+
const nameOf = (k: number): string => {
|
|
571
|
+
const file = files[k];
|
|
572
|
+
return file === undefined ? '' : file.fileName;
|
|
573
|
+
};
|
|
574
|
+
const byName: number[] = [];
|
|
575
|
+
for (let k = 0; k < files.length; k++) byName.push(k);
|
|
576
|
+
byName.sort((x, y) => compareCodePoints(nameOf(x), nameOf(y)));
|
|
577
|
+
const rank: number[] = [];
|
|
578
|
+
for (let k = 0; k < files.length; k++) rank.push(0);
|
|
579
|
+
for (let k = 1; k < byName.length; k++)
|
|
580
|
+
rank[byName[k]!] = rank[byName[k - 1]!]! + (nameOf(byName[k]!) === nameOf(byName[k - 1]!) ? 0 : 1);
|
|
581
|
+
const order: number[] = [];
|
|
582
|
+
for (let i = 0; i < n; i++) order.push(i);
|
|
583
|
+
order.sort((x, y) => {
|
|
584
|
+
const c = rank[fileOf[x]!]! - rank[fileOf[y]!]!;
|
|
585
|
+
if (c !== 0) return c;
|
|
586
|
+
const a = diagnostics[x]!;
|
|
587
|
+
const b = diagnostics[y]!;
|
|
588
|
+
return a === b ? 0 : compareDiagnosticsInFile(a, b);
|
|
589
|
+
});
|
|
590
|
+
const sorted: Diagnostic[] = [];
|
|
591
|
+
for (const i of order) sorted.push(diagnostics[i]!);
|
|
592
|
+
return sorted;
|
|
593
|
+
}
|
|
594
|
+
|
|
530
595
|
function equalChain(a: readonly Diagnostic[], b: readonly Diagnostic[]): boolean {
|
|
531
596
|
if (a.length !== b.length) return false;
|
|
532
597
|
for (let i = 0; i < a.length; i++) {
|
|
@@ -625,13 +690,29 @@ function formatDiagnosticIn(diagnostic: Diagnostic, fileName: string, options: D
|
|
|
625
690
|
return result + flattenDiagnosticMessage(diagnostic, options.newLine, options.locale) + options.newLine;
|
|
626
691
|
}
|
|
627
692
|
|
|
693
|
+
/**
|
|
694
|
+
* The plain format the main thread reports a compilation's diagnostics in (`DiagnosticReporter.plainFormat`),
|
|
695
|
+
* which its checker threads receive a copy of and format the diagnostics they send back in
|
|
696
|
+
* (`Diagnostic.formatted`). Unset where diagnostics are reported with color and context, or not at all.
|
|
697
|
+
*/
|
|
698
|
+
export class FormattedReports {
|
|
699
|
+
format: DiagnosticFormatOptions | undefined = undefined;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
export const formattedReports = new FormattedReports();
|
|
703
|
+
|
|
628
704
|
/** `formatDiagnostic` of each diagnostic. */
|
|
629
705
|
export function formatDiagnostics(diagnostics: readonly Diagnostic[], options: DiagnosticFormatOptions): string {
|
|
630
706
|
let result = '';
|
|
707
|
+
const formatted = options === formattedReports.format;
|
|
631
708
|
// Sorted diagnostics come a file at a time: the relative name of the last file is kept.
|
|
632
709
|
let lastFile: DiagnosticFile | undefined = undefined;
|
|
633
710
|
let lastName = '';
|
|
634
711
|
for (const diagnostic of diagnostics) {
|
|
712
|
+
if (formatted && diagnostic.formatted !== '') {
|
|
713
|
+
result += diagnostic.formatted;
|
|
714
|
+
continue;
|
|
715
|
+
}
|
|
635
716
|
const file = diagnostic.file;
|
|
636
717
|
if (file !== undefined && file !== lastFile) {
|
|
637
718
|
lastFile = file;
|
|
@@ -256,6 +256,8 @@ class ParseTask {
|
|
|
256
256
|
jsxRuntimeImport: JsxRuntimeImport | undefined = undefined;
|
|
257
257
|
/** The task of the same file name that was loaded instead of this one. */
|
|
258
258
|
loadedTask: ParseTask | undefined = undefined;
|
|
259
|
+
/** Whether an import or a type reference directive adds the file (`ProcessedFiles.addedFiles`). */
|
|
260
|
+
readonly byImport: boolean;
|
|
259
261
|
|
|
260
262
|
constructor(
|
|
261
263
|
readonly normalizedFilePath: string,
|
|
@@ -266,7 +268,10 @@ class ParseTask {
|
|
|
266
268
|
/** A JavaScript file found in `node_modules`, loaded only within `maxNodeModuleJsDepth`. */
|
|
267
269
|
readonly elideOnDepth: boolean,
|
|
268
270
|
readonly packageId: PackageId | undefined,
|
|
269
|
-
) {
|
|
271
|
+
) {
|
|
272
|
+
const kind = includeReason !== undefined ? includeReason.kind : -1;
|
|
273
|
+
this.byImport = kind === FileIncludeKind.Import || kind === FileIncludeKind.TypeReferenceDirective;
|
|
274
|
+
}
|
|
270
275
|
|
|
271
276
|
addSubTask(
|
|
272
277
|
fileName: string,
|
|
@@ -296,12 +301,20 @@ class ParseTask {
|
|
|
296
301
|
}
|
|
297
302
|
}
|
|
298
303
|
|
|
304
|
+
const noData: readonly ParseTaskData[] = [];
|
|
305
|
+
|
|
299
306
|
/** The tasks of one path, by file name casing. */
|
|
300
307
|
class ParseTaskData {
|
|
301
308
|
readonly tasks = new Map<string, ParseTask>();
|
|
302
309
|
lowestDepth = Number.MAX_SAFE_INTEGER;
|
|
303
310
|
startedSubTasks = false;
|
|
304
311
|
packageId: PackageId | undefined = undefined;
|
|
312
|
+
/** The index of this path's file in the program's files, once collected; -1 for none. */
|
|
313
|
+
index = -1;
|
|
314
|
+
/** For a deduplicated package's path, the tasks of the copy kept (`collect`). */
|
|
315
|
+
keptCopy: ParseTaskData | undefined = undefined;
|
|
316
|
+
/** The paths the file's imports and type reference directives add, once collected. */
|
|
317
|
+
added: readonly ParseTaskData[] = noData;
|
|
305
318
|
|
|
306
319
|
constructor(task: ParseTask) {
|
|
307
320
|
this.tasks.set(task.normalizedFilePath, task);
|
|
@@ -419,6 +432,11 @@ export class ProcessedFiles {
|
|
|
419
432
|
* threads (`preparedPackageJsonLookups`); `undefined` where they may miss some.
|
|
420
433
|
*/
|
|
421
434
|
readonly preparedPackageJsons: readonly PackageJsonLookup[] | undefined,
|
|
435
|
+
/**
|
|
436
|
+
* The files each file's imports and type reference directives added to the program, by index into
|
|
437
|
+
* `files`, without the file itself.
|
|
438
|
+
*/
|
|
439
|
+
readonly addedFiles: readonly (readonly number[])[],
|
|
422
440
|
) {}
|
|
423
441
|
}
|
|
424
442
|
|
|
@@ -446,6 +464,8 @@ export function withReplacedFile(processed: ProcessedFiles, file: SourceFile): P
|
|
|
446
464
|
processed.redirectFilesByPath,
|
|
447
465
|
processed.projectReferenceMapper,
|
|
448
466
|
processed.preparedPackageJsons,
|
|
467
|
+
// The file has the same references (`canReplaceFile`), and keeps its index.
|
|
468
|
+
processed.addedFiles,
|
|
449
469
|
);
|
|
450
470
|
}
|
|
451
471
|
|
|
@@ -1393,16 +1413,24 @@ export class FileLoader {
|
|
|
1393
1413
|
const packageIdToSourceFile = new Map<string, SourceFile>();
|
|
1394
1414
|
const checkCasing = this.options.forceConsistentCasingInFileNames !== false;
|
|
1395
1415
|
const seen = new Map<ParseTaskData, string>();
|
|
1396
|
-
|
|
1397
|
-
|
|
1416
|
+
// The paths each file's imports and type reference directives add (the tasks of each path), for
|
|
1417
|
+
// `ProcessedFiles.addedFiles`: recorded while collecting visits them, as their files are only known
|
|
1418
|
+
// once every file is collected.
|
|
1419
|
+
const fileData: ParseTaskData[] = [];
|
|
1420
|
+
const libData = new Map<SourceFile, ParseTaskData>();
|
|
1421
|
+
const packageIdToData = new Map<string, ParseTaskData>();
|
|
1422
|
+
|
|
1423
|
+
const collectFiles = (tasks: readonly ParseTask[], added: ParseTaskData[] | undefined): void => {
|
|
1398
1424
|
for (let task of tasks) {
|
|
1399
1425
|
const includeReason = task.includeReason;
|
|
1426
|
+
const byImport = task.byImport;
|
|
1400
1427
|
// Automatic type directive tasks are an implementation detail, not a reason.
|
|
1401
1428
|
if (task.redirectedParseTask === undefined && !task.isForAutomaticTypeDirective) {
|
|
1402
1429
|
if (task.loadedTask !== undefined) task = task.loadedTask;
|
|
1403
1430
|
if (includeReason !== undefined) addIncludeReason(includeProcessor, task, includeReason);
|
|
1404
1431
|
}
|
|
1405
1432
|
const data = this.taskDataByPath.get(task.path);
|
|
1433
|
+
if (added !== undefined && data !== undefined && byImport) added.push(data);
|
|
1406
1434
|
if (!task.loaded || data === undefined) continue;
|
|
1407
1435
|
|
|
1408
1436
|
const checkedName = seen.get(data);
|
|
@@ -1457,13 +1485,18 @@ export class FileLoader {
|
|
|
1457
1485
|
),
|
|
1458
1486
|
);
|
|
1459
1487
|
filesByPath.set(task.path, packageIdFile);
|
|
1488
|
+
data.keptCopy = packageIdToData.get(key);
|
|
1460
1489
|
if (data.lowestDepth > 0) sourceFilesFoundSearchingNodeModules.add(task.path);
|
|
1461
1490
|
continue;
|
|
1462
1491
|
}
|
|
1463
|
-
if (file !== undefined)
|
|
1492
|
+
if (file !== undefined) {
|
|
1493
|
+
packageIdToSourceFile.set(key, file);
|
|
1494
|
+
packageIdToData.set(key, data);
|
|
1495
|
+
}
|
|
1464
1496
|
}
|
|
1465
1497
|
|
|
1466
|
-
|
|
1498
|
+
let own: ParseTaskData[] | undefined = undefined;
|
|
1499
|
+
if (task.subTasks !== undefined && task.subTasks.length > 0) collectFiles(task.subTasks, (own = []));
|
|
1467
1500
|
|
|
1468
1501
|
const redirected = task.redirectedParseTask;
|
|
1469
1502
|
if (redirected !== undefined) {
|
|
@@ -1485,10 +1518,13 @@ export class FileLoader {
|
|
|
1485
1518
|
}
|
|
1486
1519
|
if (task.libFile !== undefined) {
|
|
1487
1520
|
libFiles.push(file);
|
|
1521
|
+
libData.set(file, data);
|
|
1488
1522
|
libFilesByPath.set(path, task.libFile);
|
|
1489
1523
|
} else {
|
|
1490
1524
|
files.push(file);
|
|
1525
|
+
fileData.push(data);
|
|
1491
1526
|
}
|
|
1527
|
+
if (own !== undefined) data.added = own;
|
|
1492
1528
|
filesByPath.set(path, file);
|
|
1493
1529
|
resolvedModules.set(path, task.resolutionsInFile);
|
|
1494
1530
|
typeResolutionsInFile.set(path, task.typeResolutionsInFile);
|
|
@@ -1500,13 +1536,28 @@ export class FileLoader {
|
|
|
1500
1536
|
if (data.lowestDepth > 0) sourceFilesFoundSearchingNodeModules.add(path);
|
|
1501
1537
|
}
|
|
1502
1538
|
};
|
|
1503
|
-
collectFiles(this.rootTasks);
|
|
1539
|
+
collectFiles(this.rootTasks, undefined);
|
|
1504
1540
|
|
|
1505
1541
|
const priorities = new Map<SourceFile, number>();
|
|
1506
1542
|
for (const file of libFiles) priorities.set(file, this.libFilePriority(file));
|
|
1507
1543
|
libFiles.sort((a, b) => (priorities.get(a) ?? 0) - (priorities.get(b) ?? 0));
|
|
1508
1544
|
const allFiles = [...libFiles, ...files];
|
|
1509
1545
|
for (const redirect of redirectFilesByPath.values()) redirect.index += libFiles.length;
|
|
1546
|
+
// The files a file adds, without reading its resolutions again or looking up their paths.
|
|
1547
|
+
const dataOfFiles: ParseTaskData[] = [];
|
|
1548
|
+
for (const file of libFiles) dataOfFiles.push(libData.get(file)!);
|
|
1549
|
+
for (const data of fileData) dataOfFiles.push(data);
|
|
1550
|
+
for (let i = 0; i < dataOfFiles.length; i++) dataOfFiles[i]!.index = i;
|
|
1551
|
+
const addedFiles: number[][] = [];
|
|
1552
|
+
for (let i = 0; i < dataOfFiles.length; i++) {
|
|
1553
|
+
const added: number[] = [];
|
|
1554
|
+
for (const data of dataOfFiles[i]!.added) {
|
|
1555
|
+
const kept = data.keptCopy;
|
|
1556
|
+
const k = kept !== undefined ? kept.index : data.index;
|
|
1557
|
+
if (k >= 0 && k !== i) added.push(k);
|
|
1558
|
+
}
|
|
1559
|
+
addedFiles.push(added);
|
|
1560
|
+
}
|
|
1510
1561
|
|
|
1511
1562
|
const keys = [...this.libResolutions.keys()].sort();
|
|
1512
1563
|
for (const key of keys) {
|
|
@@ -1536,6 +1587,7 @@ export class FileLoader {
|
|
|
1536
1587
|
redirectFilesByPath,
|
|
1537
1588
|
this.mapper,
|
|
1538
1589
|
this.tookPreparedResolutions ? preparedPackageJsonLookups() : noPackageJsonLookups,
|
|
1590
|
+
addedFiles,
|
|
1539
1591
|
);
|
|
1540
1592
|
}
|
|
1541
1593
|
}
|
|
@@ -1864,8 +1864,21 @@ class EmitFilesHandler {
|
|
|
1864
1864
|
const files = program.program.getSourceFiles();
|
|
1865
1865
|
const emittedOnCheckers = this.emittedOnCheckers();
|
|
1866
1866
|
const wrapped = this.wrap(writer);
|
|
1867
|
-
const
|
|
1868
|
-
|
|
1867
|
+
const ahead = this.declarationsEmittedAhead();
|
|
1868
|
+
const result = program.checked.javaScriptEmittedOnThreads
|
|
1869
|
+
? combineEmitResults(
|
|
1870
|
+
files.map((file) =>
|
|
1871
|
+
this.emitAfterEmitThreads(
|
|
1872
|
+
file,
|
|
1873
|
+
EmitOnly.All,
|
|
1874
|
+
emittedOnCheckers.get(file),
|
|
1875
|
+
ahead.get(file),
|
|
1876
|
+
writer,
|
|
1877
|
+
wrapped,
|
|
1878
|
+
),
|
|
1879
|
+
),
|
|
1880
|
+
)
|
|
1881
|
+
: emittedOnCheckers.size === 0
|
|
1869
1882
|
? program.emitter.emit(files, EmitOnly.All, wrapped)
|
|
1870
1883
|
: combineEmitResults(
|
|
1871
1884
|
files.map((file) => {
|
|
@@ -1901,20 +1914,76 @@ class EmitFilesHandler {
|
|
|
1901
1914
|
/**
|
|
1902
1915
|
* The emit results of the files checker threads emitted right after checking them (parallel-check.ts),
|
|
1903
1916
|
* which are not emitted again here. Checker threads emit every file of a program without old build
|
|
1904
|
-
* info, with the outputs its options select, as its pending emit asks.
|
|
1917
|
+
* info, with the outputs its options select, as its pending emit asks. Emit threads emit only the
|
|
1918
|
+
* JavaScript of the files (`javaScriptEmittedOnThreads`, `emitAfterEmitThreads`).
|
|
1905
1919
|
*/
|
|
1906
1920
|
private emittedOnCheckers(): Map<SourceFile, EmitResult> {
|
|
1921
|
+
return this.byFile(this.program.checked.emittedInParallel);
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
/** The declaration emit of the files the declaration thread emitted (parallel-emit.ts). */
|
|
1925
|
+
private declarationsEmittedAhead(): Map<SourceFile, EmitResult> {
|
|
1926
|
+
return this.byFile(this.program.checked.declarationsEmittedAhead);
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
/** The results of `results`, by file index, by file. */
|
|
1930
|
+
private byFile(results: readonly (EmitResult | undefined)[] | undefined): Map<SourceFile, EmitResult> {
|
|
1907
1931
|
const result = new Map<SourceFile, EmitResult>();
|
|
1908
|
-
|
|
1909
|
-
if (emitted === undefined) return result;
|
|
1932
|
+
if (results === undefined) return result;
|
|
1910
1933
|
const files = this.program.program.getSourceFiles();
|
|
1911
1934
|
for (let i = 0; i < files.length; i++) {
|
|
1912
|
-
const fileResult =
|
|
1935
|
+
const fileResult = results[i];
|
|
1913
1936
|
if (fileResult !== undefined) result.set(files[i]!, fileResult);
|
|
1914
1937
|
}
|
|
1915
1938
|
return result;
|
|
1916
1939
|
}
|
|
1917
1940
|
|
|
1941
|
+
/**
|
|
1942
|
+
* The emit of `file` with `emitOnly` where emit threads emitted the JavaScript of the program's files
|
|
1943
|
+
* while this thread checked it (`javaScriptEmittedOnThreads`, parallel-emit.ts): the outputs an emit thread
|
|
1944
|
+
* emitted of its JavaScript (`javaScript`) and the declaration thread of its declarations (`declarations`),
|
|
1945
|
+
* and the rest emitted here. A file no thread emitted is emitted here as without the threads.
|
|
1946
|
+
*/
|
|
1947
|
+
private emitAfterEmitThreads(
|
|
1948
|
+
file: SourceFile,
|
|
1949
|
+
emitOnly: EmitOnly,
|
|
1950
|
+
javaScript: EmitResult | undefined,
|
|
1951
|
+
declarations: EmitResult | undefined,
|
|
1952
|
+
writer: OutputWriter,
|
|
1953
|
+
wrapped: OutputWriter,
|
|
1954
|
+
): EmitResult {
|
|
1955
|
+
const program = this.program;
|
|
1956
|
+
if (wrapped instanceof SignatureRecordingWriter) wrapped.file = file;
|
|
1957
|
+
if (javaScript === undefined && declarations === undefined) return program.emitter.emit([file], emitOnly, wrapped);
|
|
1958
|
+
const results: EmitResult[] = [];
|
|
1959
|
+
if (emitOnly !== EmitOnly.Dts)
|
|
1960
|
+
results.push(
|
|
1961
|
+
javaScript !== undefined
|
|
1962
|
+
? this.writeThreadOutputs(file, javaScript, writer)
|
|
1963
|
+
: program.emitter.emit([file], EmitOnly.Js, writer),
|
|
1964
|
+
);
|
|
1965
|
+
if (emitOnly !== EmitOnly.Js && getEmitDeclarations(program.snapshot.options))
|
|
1966
|
+
results.push(
|
|
1967
|
+
declarations !== undefined
|
|
1968
|
+
? this.writeThreadOutputs(file, declarations, writer)
|
|
1969
|
+
: program.emitter.emit([file], EmitOnly.Dts, wrapped),
|
|
1970
|
+
);
|
|
1971
|
+
return combineEmitResults(results);
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
/**
|
|
1975
|
+
* Writes the outputs a thread emitted for `file` with `writer`, as this thread would have written them:
|
|
1976
|
+
* its declaration signatures are recorded (`recordDeclarationSignatures`), and the unchanged declaration
|
|
1977
|
+
* files of a composite program are left unwritten.
|
|
1978
|
+
*/
|
|
1979
|
+
private writeThreadOutputs(file: SourceFile, emitted: EmitResult, writer: OutputWriter): EmitResult {
|
|
1980
|
+
const skips = this.recordDeclarationSignatures(file, emitted.declarationSignatures);
|
|
1981
|
+
return writeHeldOutputs(
|
|
1982
|
+
emitted,
|
|
1983
|
+
skips === undefined ? writer : new HeldDeclarationWriter(writer, skips, this.program.host),
|
|
1984
|
+
);
|
|
1985
|
+
}
|
|
1986
|
+
|
|
1918
1987
|
/** Records declaration signatures when declarations are emitted. */
|
|
1919
1988
|
private wrap(writer: OutputWriter): OutputWriter {
|
|
1920
1989
|
return getEmitDeclarations(this.program.snapshot.options) ? new SignatureRecordingWriter(this, writer) : writer;
|
|
@@ -1926,6 +1995,7 @@ class EmitFilesHandler {
|
|
|
1926
1995
|
collectAllAffectedFiles(program);
|
|
1927
1996
|
const wrapped = this.wrap(writer);
|
|
1928
1997
|
const emittedOnCheckers = this.emittedOnCheckers();
|
|
1998
|
+
const ahead = this.declarationsEmittedAhead();
|
|
1929
1999
|
for (const [path, emitKind] of snapshot.affectedFilesPendingEmit) {
|
|
1930
2000
|
const affectedFile = program.program.getSourceFileByPath(path);
|
|
1931
2001
|
if (affectedFile === undefined || !program.program.sourceFileMayBeEmitted(affectedFile, false)) {
|
|
@@ -1939,13 +2009,10 @@ class EmitFilesHandler {
|
|
|
1939
2009
|
if (!this.isForDtsErrors) {
|
|
1940
2010
|
const emitOnly = emitOnlyOfPendingKind(pendingKind);
|
|
1941
2011
|
const emitted = emittedOnCheckers.get(affectedFile);
|
|
1942
|
-
if (
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
skips === undefined ? writer : new HeldDeclarationWriter(writer, skips, program.host),
|
|
1947
|
-
);
|
|
1948
|
-
} else {
|
|
2012
|
+
if (program.checked.javaScriptEmittedOnThreads)
|
|
2013
|
+
result = this.emitAfterEmitThreads(affectedFile, emitOnly, emitted, ahead.get(affectedFile), writer, wrapped);
|
|
2014
|
+
else if (emitted !== undefined) result = this.writeThreadOutputs(affectedFile, emitted, writer);
|
|
2015
|
+
else {
|
|
1949
2016
|
if (wrapped instanceof SignatureRecordingWriter) wrapped.file = affectedFile;
|
|
1950
2017
|
result = program.emitter.emit([affectedFile], emitOnly, wrapped);
|
|
1951
2018
|
}
|