tstyche 4.3.0 → 5.0.0-beta.0

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/build/tstyche.js CHANGED
@@ -6,34 +6,146 @@ import os from 'node:os';
6
6
  import process from 'node:process';
7
7
  import { createRequire } from 'node:module';
8
8
  import vm from 'node:vm';
9
- import streamConsumers from 'node:stream/consumers';
10
9
 
11
- var DiagnosticCategory;
12
- (function (DiagnosticCategory) {
13
- DiagnosticCategory["Error"] = "error";
14
- DiagnosticCategory["Warning"] = "warning";
15
- })(DiagnosticCategory || (DiagnosticCategory = {}));
10
+ class EventEmitter {
11
+ static instanceCount = 0;
12
+ static #handlers = new Map();
13
+ static #reporters = new Map();
14
+ #scope;
15
+ constructor() {
16
+ this.#scope = EventEmitter.instanceCount++;
17
+ EventEmitter.#handlers.set(this.#scope, new Set());
18
+ EventEmitter.#reporters.set(this.#scope, new Set());
19
+ }
20
+ addHandler(handler) {
21
+ EventEmitter.#handlers.get(this.#scope)?.add(handler);
22
+ }
23
+ addReporter(reporter) {
24
+ EventEmitter.#reporters.get(this.#scope)?.add(reporter);
25
+ }
26
+ static dispatch(event) {
27
+ function forEachHandler(handlers, event) {
28
+ for (const handler of handlers) {
29
+ handler.on(event);
30
+ }
31
+ }
32
+ for (const handlers of EventEmitter.#handlers.values()) {
33
+ forEachHandler(handlers, event);
34
+ }
35
+ for (const handlers of EventEmitter.#reporters.values()) {
36
+ forEachHandler(handlers, event);
37
+ }
38
+ }
39
+ removeHandler(handler) {
40
+ EventEmitter.#handlers.get(this.#scope)?.delete(handler);
41
+ }
42
+ removeReporter(reporter) {
43
+ EventEmitter.#reporters.get(this.#scope)?.delete(reporter);
44
+ }
45
+ removeHandlers() {
46
+ EventEmitter.#handlers.get(this.#scope)?.clear();
47
+ }
48
+ removeReporters() {
49
+ EventEmitter.#reporters.get(this.#scope)?.clear();
50
+ }
51
+ }
52
+
53
+ class Path {
54
+ static normalizeSlashes;
55
+ static {
56
+ if (path.sep === "/") {
57
+ Path.normalizeSlashes = (filePath) => filePath;
58
+ }
59
+ else {
60
+ Path.normalizeSlashes = (filePath) => filePath.replace(/\\/g, "/");
61
+ }
62
+ }
63
+ static dirname(filePath) {
64
+ return Path.normalizeSlashes(path.dirname(filePath));
65
+ }
66
+ static join(...filePaths) {
67
+ return Path.normalizeSlashes(path.join(...filePaths));
68
+ }
69
+ static relative(from, to) {
70
+ const relativePath = Path.normalizeSlashes(path.relative(from, to));
71
+ if (/^\.\.?\//.test(relativePath)) {
72
+ return relativePath;
73
+ }
74
+ return `./${relativePath}`;
75
+ }
76
+ static resolve(...filePaths) {
77
+ return Path.normalizeSlashes(path.resolve(...filePaths));
78
+ }
79
+ }
80
+
81
+ class SourceFile {
82
+ fileName;
83
+ #lineMap;
84
+ text;
85
+ constructor(fileName, text) {
86
+ this.fileName = fileName;
87
+ this.text = text;
88
+ this.#lineMap = this.#createLineMap();
89
+ }
90
+ #createLineMap() {
91
+ const result = [0];
92
+ let position = 0;
93
+ while (position < this.text.length) {
94
+ if (this.text.charAt(position - 1) === "\r") {
95
+ position++;
96
+ }
97
+ if (this.text.charAt(position - 1) === "\n") {
98
+ result.push(position);
99
+ }
100
+ position++;
101
+ }
102
+ result.push(position);
103
+ return result;
104
+ }
105
+ getLineStarts() {
106
+ return this.#lineMap;
107
+ }
108
+ getLineAndCharacterOfPosition(position) {
109
+ const line = this.#lineMap.findLastIndex((line) => line <= position);
110
+ const character = position - this.#lineMap[line];
111
+ return { line, character };
112
+ }
113
+ }
114
+
115
+ class SourceService {
116
+ static #files = new Map();
117
+ static get(source) {
118
+ const file = SourceService.#files.get(source.fileName);
119
+ if (file != null) {
120
+ return file;
121
+ }
122
+ return source;
123
+ }
124
+ static set(source) {
125
+ SourceService.#files.set(source.fileName, source);
126
+ }
127
+ }
16
128
 
17
129
  class DiagnosticOrigin {
18
- assertion;
130
+ assertionNode;
19
131
  end;
20
132
  sourceFile;
21
133
  start;
22
- constructor(start, end, sourceFile, assertion) {
134
+ constructor(start, end, sourceFile, assertionNode) {
23
135
  this.start = start;
24
136
  this.end = end;
25
- this.sourceFile = sourceFile;
26
- this.assertion = assertion;
137
+ this.sourceFile = SourceService.get(sourceFile);
138
+ this.assertionNode = assertionNode;
27
139
  }
28
- static fromAssertion(assertion) {
29
- const node = assertion.matcherNameNode.name;
30
- return new DiagnosticOrigin(node.getStart(), node.getEnd(), node.getSourceFile(), assertion);
140
+ static fromAssertion(assertionNode) {
141
+ const node = assertionNode.matcherNameNode.name;
142
+ return new DiagnosticOrigin(node.getStart(), node.getEnd(), node.getSourceFile(), assertionNode);
31
143
  }
32
- static fromNode(node, assertion) {
33
- return new DiagnosticOrigin(node.getStart(), node.getEnd(), node.getSourceFile(), assertion);
144
+ static fromNode(node, assertionNode) {
145
+ return new DiagnosticOrigin(node.getStart(), node.getEnd(), node.getSourceFile(), assertionNode);
34
146
  }
35
- static fromNodes(nodes, assertion) {
36
- return new DiagnosticOrigin(nodes.pos, nodes.end, nodes[0].getSourceFile(), assertion);
147
+ static fromNodes(nodes, assertionNode) {
148
+ return new DiagnosticOrigin(nodes.pos, nodes.end, nodes[0].getSourceFile(), assertionNode);
37
149
  }
38
150
  }
39
151
 
@@ -82,135 +194,36 @@ class Diagnostic {
82
194
  return this;
83
195
  }
84
196
  static error(text, origin) {
85
- return new Diagnostic(text, DiagnosticCategory.Error, origin);
197
+ return new Diagnostic(text, "error", origin);
86
198
  }
87
199
  extendWith(text, origin) {
88
200
  return new Diagnostic([this.text, text].flat(), this.category, origin ?? this.origin);
89
201
  }
90
- static fromDiagnostics(diagnostics, sourceFile) {
202
+ static fromDiagnostics(diagnostics) {
91
203
  return diagnostics.map((diagnostic) => {
92
204
  const code = `ts(${diagnostic.code})`;
93
205
  let origin;
94
206
  if (isDiagnosticWithLocation(diagnostic)) {
95
- origin = new DiagnosticOrigin(diagnostic.start, getTextSpanEnd(diagnostic), sourceFile ?? diagnostic.file);
207
+ origin = new DiagnosticOrigin(diagnostic.start, getTextSpanEnd(diagnostic), diagnostic.file);
96
208
  }
97
209
  let related;
98
210
  if (diagnostic.relatedInformation != null) {
99
211
  related = Diagnostic.fromDiagnostics(diagnostic.relatedInformation);
100
212
  }
101
213
  const text = getDiagnosticMessageText(diagnostic);
102
- return new Diagnostic(text, DiagnosticCategory.Error, origin).add({ code, related });
214
+ return new Diagnostic(text, "error", origin).add({ code, related });
103
215
  });
104
216
  }
105
217
  static warning(text, origin) {
106
- return new Diagnostic(text, DiagnosticCategory.Warning, origin);
107
- }
108
- }
109
-
110
- class SourceFile {
111
- fileName;
112
- #lineMap;
113
- text;
114
- constructor(fileName, text) {
115
- this.fileName = fileName;
116
- this.text = text;
117
- this.#lineMap = this.#createLineMap();
118
- }
119
- #createLineMap() {
120
- const result = [0];
121
- let position = 0;
122
- while (position < this.text.length) {
123
- if (this.text.charAt(position - 1) === "\r") {
124
- position++;
125
- }
126
- if (this.text.charAt(position - 1) === "\n") {
127
- result.push(position);
128
- }
129
- position++;
130
- }
131
- result.push(position);
132
- return result;
133
- }
134
- getLineStarts() {
135
- return this.#lineMap;
136
- }
137
- getLineAndCharacterOfPosition(position) {
138
- const line = this.#lineMap.findLastIndex((line) => line <= position);
139
- const character = position - this.#lineMap[line];
140
- return { line, character };
218
+ return new Diagnostic(text, "warning", origin);
141
219
  }
142
220
  }
143
221
 
144
- class EventEmitter {
145
- static instanceCount = 0;
146
- static #handlers = new Map();
147
- static #reporters = new Map();
148
- #scope;
149
- constructor() {
150
- this.#scope = EventEmitter.instanceCount++;
151
- EventEmitter.#handlers.set(this.#scope, new Set());
152
- EventEmitter.#reporters.set(this.#scope, new Set());
153
- }
154
- addHandler(handler) {
155
- EventEmitter.#handlers.get(this.#scope)?.add(handler);
156
- }
157
- addReporter(reporter) {
158
- EventEmitter.#reporters.get(this.#scope)?.add(reporter);
159
- }
160
- static dispatch(event) {
161
- function forEachHandler(handlers, event) {
162
- for (const handler of handlers) {
163
- handler.on(event);
164
- }
165
- }
166
- for (const handlers of EventEmitter.#handlers.values()) {
167
- forEachHandler(handlers, event);
168
- }
169
- for (const handlers of EventEmitter.#reporters.values()) {
170
- forEachHandler(handlers, event);
171
- }
172
- }
173
- removeHandler(handler) {
174
- EventEmitter.#handlers.get(this.#scope)?.delete(handler);
175
- }
176
- removeReporter(reporter) {
177
- EventEmitter.#reporters.get(this.#scope)?.delete(reporter);
178
- }
179
- removeHandlers() {
180
- EventEmitter.#handlers.get(this.#scope)?.clear();
181
- }
182
- removeReporters() {
183
- EventEmitter.#reporters.get(this.#scope)?.clear();
184
- }
185
- }
186
-
187
- class Path {
188
- static normalizeSlashes;
189
- static {
190
- if (path.sep === "/") {
191
- Path.normalizeSlashes = (filePath) => filePath;
192
- }
193
- else {
194
- Path.normalizeSlashes = (filePath) => filePath.replace(/\\/g, "/");
195
- }
196
- }
197
- static dirname(filePath) {
198
- return Path.normalizeSlashes(path.dirname(filePath));
199
- }
200
- static join(...filePaths) {
201
- return Path.normalizeSlashes(path.join(...filePaths));
202
- }
203
- static relative(from, to) {
204
- const relativePath = Path.normalizeSlashes(path.relative(from, to));
205
- if (/^\.\.?\//.test(relativePath)) {
206
- return relativePath;
207
- }
208
- return `./${relativePath}`;
209
- }
210
- static resolve(...filePaths) {
211
- return Path.normalizeSlashes(path.resolve(...filePaths));
212
- }
213
- }
222
+ var DiagnosticCategory;
223
+ (function (DiagnosticCategory) {
224
+ DiagnosticCategory["Error"] = "error";
225
+ DiagnosticCategory["Warning"] = "warning";
226
+ })(DiagnosticCategory || (DiagnosticCategory = {}));
214
227
 
215
228
  class ConfigDiagnosticText {
216
229
  static expected(element) {
@@ -237,19 +250,22 @@ class ConfigDiagnosticText {
237
250
  static moduleWasNotFound(specifier) {
238
251
  return `The specified module '${specifier}' was not found.`;
239
252
  }
253
+ static optionValueMustBe(optionName, optionBrand) {
254
+ return `Value for the '${optionName}' option must be a ${optionBrand}.`;
255
+ }
240
256
  static rangeIsNotValid(value) {
241
257
  return `The specified range '${value}' is not valid.`;
242
258
  }
259
+ static rangeDoesNotMatchSupported(value) {
260
+ return `The specified range '${value}' does not match any supported TypeScript versions.`;
261
+ }
243
262
  static rangeUsage() {
244
263
  return [
245
- "A range must be specified using an operator and a minor version.",
246
- "To set an upper bound, the intersection of two ranges can be used.",
247
- "Examples: '>=5.5', '>=5.0 <5.3'.",
264
+ "A range must be specified using an operator and a minor version: '>=5.5'.",
265
+ "To set an upper bound, use the intersection of two ranges: '>=5.0 <5.3'.",
266
+ "Use the '||' operator to join ranges into a union: '>=5.2 <=5.3 || 5.4.2 || >5.5'.",
248
267
  ];
249
268
  }
250
- static requiresValueType(optionName, optionBrand) {
251
- return `Option '${optionName}' requires a value of type ${optionBrand}.`;
252
- }
253
269
  static seen(element) {
254
270
  return `The ${element} was seen here.`;
255
271
  }
@@ -259,46 +275,14 @@ class ConfigDiagnosticText {
259
275
  static unknownOption(optionName) {
260
276
  return `Unknown option '${optionName}'.`;
261
277
  }
262
- static usage(optionName, optionBrand) {
263
- switch (optionName.startsWith("--") ? optionName.slice(2) : optionName) {
264
- case "target": {
265
- const text = [];
266
- if (optionName.startsWith("--")) {
267
- text.push("Value for the '--target' option must be a string or a comma separated list.", "Examples: '--target 5.2', '--target next', '--target '>=5.0 <5.3, 5.4.2, >=5.5''.");
268
- }
269
- return text;
270
- }
271
- }
272
- return [ConfigDiagnosticText.requiresValueType(optionName, optionBrand)];
273
- }
274
278
  static versionIsNotSupported(value) {
275
- if (value === "current") {
276
- return "Cannot use 'current' as a target. Failed to resolve the installed TypeScript module.";
277
- }
278
- return `TypeScript version '${value}' is not supported.`;
279
+ return `The TypeScript version '${value}' is not supported.`;
279
280
  }
280
281
  static watchCannotBeEnabled() {
281
282
  return "Watch mode cannot be enabled in continuous integration environment.";
282
283
  }
283
284
  }
284
285
 
285
- var OptionBrand;
286
- (function (OptionBrand) {
287
- OptionBrand["String"] = "string";
288
- OptionBrand["Number"] = "number";
289
- OptionBrand["Boolean"] = "boolean";
290
- OptionBrand["BareTrue"] = "bareTrue";
291
- OptionBrand["List"] = "list";
292
- })(OptionBrand || (OptionBrand = {}));
293
-
294
- var OptionGroup;
295
- (function (OptionGroup) {
296
- OptionGroup[OptionGroup["CommandLine"] = 2] = "CommandLine";
297
- OptionGroup[OptionGroup["ConfigFile"] = 4] = "ConfigFile";
298
- OptionGroup[OptionGroup["InlineConditions"] = 8] = "InlineConditions";
299
- OptionGroup[OptionGroup["ResolvedConfig"] = 6] = "ResolvedConfig";
300
- })(OptionGroup || (OptionGroup = {}));
301
-
302
286
  class Environment {
303
287
  static resolve() {
304
288
  return {
@@ -555,6 +539,9 @@ class Manifest {
555
539
  return;
556
540
  }
557
541
  resolve(tag) {
542
+ if (tag === "*") {
543
+ return this.resolutions["latest"];
544
+ }
558
545
  if (this.versions.includes(tag)) {
559
546
  return tag;
560
547
  }
@@ -663,32 +650,58 @@ class ManifestService {
663
650
  }
664
651
 
665
652
  class TarReader {
666
- static #textDecoder = new TextDecoder();
667
- static async *extract(stream) {
668
- const decompressedStream = stream.pipeThrough(new DecompressionStream("gzip"));
669
- const buffer = await streamConsumers.arrayBuffer(decompressedStream);
670
- let offset = 0;
671
- while (offset < buffer.byteLength - 512) {
672
- const name = TarReader.#read(buffer, offset, 100);
673
- if (name.length === 0) {
653
+ #leftover = new Uint8Array(0);
654
+ #reader;
655
+ #textDecoder = new TextDecoder();
656
+ constructor(stream) {
657
+ this.#reader = stream.getReader();
658
+ }
659
+ async *extract() {
660
+ while (true) {
661
+ const header = await this.#read(512);
662
+ if (this.#isEndOfArchive(header)) {
674
663
  break;
675
664
  }
676
- const size = Number.parseInt(TarReader.#read(buffer, offset + 124, 12), 8);
677
- const contents = new Uint8Array(buffer, offset + 512, size);
678
- yield { name, contents };
679
- offset += 512 + 512 * Math.trunc(size / 512);
680
- if (size % 512) {
681
- offset += 512;
665
+ const name = this.#textDecoder.decode(header.subarray(0, 100)).replace(/\0.*$/, "");
666
+ const sizeOctal = this.#textDecoder.decode(header.subarray(124, 136)).replace(/\0.*$/, "").trim();
667
+ const size = Number.parseInt(sizeOctal, 8);
668
+ const content = await this.#read(size);
669
+ yield { name, content };
670
+ if (size % 512 !== 0) {
671
+ const toSkip = 512 - (size % 512);
672
+ await this.#read(toSkip);
682
673
  }
683
674
  }
684
675
  }
685
- static #read(buffer, byteOffset, length) {
686
- let view = new Uint8Array(buffer, byteOffset, length);
687
- const zeroIndex = view.indexOf(0);
688
- if (zeroIndex !== -1) {
689
- view = view.subarray(0, zeroIndex);
676
+ #isEndOfArchive(entry) {
677
+ return entry.every((byte) => byte === 0);
678
+ }
679
+ async #read(n) {
680
+ const result = new Uint8Array(n);
681
+ let filled = 0;
682
+ if (this.#leftover.length > 0) {
683
+ const toCopy = Math.min(this.#leftover.length, n);
684
+ result.set(this.#leftover.subarray(0, toCopy), filled);
685
+ filled += toCopy;
686
+ this.#leftover = this.#leftover.subarray(toCopy);
687
+ if (filled === n) {
688
+ return result;
689
+ }
690
+ }
691
+ while (filled < n) {
692
+ const { value, done } = await this.#reader.read();
693
+ if (done) {
694
+ break;
695
+ }
696
+ const toCopy = Math.min(value.length, n - filled);
697
+ result.set(value.subarray(0, toCopy), filled);
698
+ filled += toCopy;
699
+ if (toCopy < value.length) {
700
+ this.#leftover = value.subarray(toCopy);
701
+ break;
702
+ }
690
703
  }
691
- return TarReader.#textDecoder.decode(view);
704
+ return result.subarray(0, filled);
692
705
  }
693
706
  }
694
707
 
@@ -706,16 +719,18 @@ class PackageService {
706
719
  const response = await this.#fetcher.get(request, diagnostic);
707
720
  if (response?.body != null) {
708
721
  const targetPath = `${packagePath}-${Math.random().toString(32).slice(2)}`;
709
- for await (const file of TarReader.extract(response.body)) {
722
+ const stream = response.body.pipeThrough(new DecompressionStream("gzip"));
723
+ const tarReader = new TarReader(stream);
724
+ for await (const file of tarReader.extract()) {
710
725
  if (!file.name.startsWith("package/")) {
711
726
  continue;
712
727
  }
713
- const filePath = Path.join(targetPath, file.name.replace("package/", ""));
728
+ const filePath = Path.join(targetPath, file.name.replace(/^package\//, ""));
714
729
  const directoryPath = Path.dirname(filePath);
715
730
  if (!existsSync(directoryPath)) {
716
731
  await fs.mkdir(directoryPath, { recursive: true });
717
732
  }
718
- await fs.writeFile(filePath, file.contents);
733
+ await fs.writeFile(filePath, file.content);
719
734
  }
720
735
  await fs.rename(targetPath, packagePath);
721
736
  return packagePath;
@@ -765,7 +780,7 @@ class Store {
765
780
  Store.#manifestService = new ManifestService(Store.#storePath, Store.#npmRegistry, Store.#fetcher);
766
781
  }
767
782
  static async fetch(tag) {
768
- if (tag === "current") {
783
+ if (tag === "*" && environmentOptions.typescriptModule != null) {
769
784
  return;
770
785
  }
771
786
  await Store.open();
@@ -782,7 +797,7 @@ class Store {
782
797
  return compilerInstance;
783
798
  }
784
799
  let modulePath;
785
- if (tag === "current" && environmentOptions.typescriptModule != null) {
800
+ if (tag === "*" && environmentOptions.typescriptModule != null) {
786
801
  modulePath = fileURLToPath(environmentOptions.typescriptModule);
787
802
  }
788
803
  else {
@@ -830,7 +845,7 @@ class Store {
830
845
  Store.open = () => Promise.resolve();
831
846
  Store.manifest = await Store.#manifestService.open();
832
847
  if (Store.manifest != null) {
833
- Store.#supportedTags = [...Object.keys(Store.manifest.resolutions), ...Store.manifest.versions, "current"].sort();
848
+ Store.#supportedTags = [...Object.keys(Store.manifest.resolutions), ...Store.manifest.versions];
834
849
  }
835
850
  }
836
851
  static async prune() {
@@ -840,8 +855,8 @@ class Store {
840
855
  await Store.#manifestService.open({ refresh: true });
841
856
  }
842
857
  static async validateTag(tag) {
843
- if (tag === "current") {
844
- return environmentOptions.typescriptModule != null;
858
+ if (tag === "*") {
859
+ return true;
845
860
  }
846
861
  await Store.open();
847
862
  if (Store.manifest?.isOutdated({ ageTolerance: 60 }) &&
@@ -859,23 +874,25 @@ class Store {
859
874
 
860
875
  class Target {
861
876
  static #rangeRegex = /^[<>]=?\d\.\d( [<>]=?\d\.\d)?$/;
862
- static async expand(queries) {
863
- const include = [];
864
- for (const query of queries) {
865
- if (!Target.isRange(query)) {
866
- include.push(query);
867
- continue;
868
- }
877
+ static async expand(range, onDiagnostics, origin) {
878
+ if (Target.isRange(range)) {
869
879
  await Store.open();
870
880
  if (Store.manifest != null) {
871
881
  let versions = [...Store.manifest.minorVersions];
872
- for (const comparator of query.split(" ")) {
873
- versions = Target.#filter(comparator, versions);
882
+ for (const comparator of range.split(" ")) {
883
+ versions = Target.#filter(comparator.trim(), versions);
884
+ if (versions.length === 0) {
885
+ const text = [
886
+ ConfigDiagnosticText.rangeDoesNotMatchSupported(range),
887
+ ConfigDiagnosticText.inspectSupportedVersions(),
888
+ ];
889
+ onDiagnostics(Diagnostic.error(text, origin));
890
+ }
874
891
  }
875
- include.push(...versions);
892
+ return versions;
876
893
  }
877
894
  }
878
- return include;
895
+ return [range];
879
896
  }
880
897
  static #filter(comparator, versions) {
881
898
  const targetVersion = comparator.replace(/^[<>]=?/, "");
@@ -894,178 +911,177 @@ class Target {
894
911
  static isRange(query) {
895
912
  return Target.#rangeRegex.test(query);
896
913
  }
914
+ static split(range) {
915
+ return range.split(/ *\|\| */);
916
+ }
897
917
  }
898
918
 
899
919
  class Options {
900
920
  static #definitions = [
901
921
  {
902
- brand: OptionBrand.String,
922
+ brand: "string",
903
923
  description: "The Url to the config file validation schema.",
904
- group: OptionGroup.ConfigFile,
924
+ group: 4,
905
925
  name: "$schema",
906
926
  },
907
927
  {
908
- brand: OptionBrand.Boolean,
909
- description: "Enable type error reporting for source files.",
910
- group: OptionGroup.ConfigFile,
911
- name: "checkSourceFiles",
928
+ brand: "boolean",
929
+ description: "Check declaration files for type errors.",
930
+ group: 4,
931
+ name: "checkDeclarationFiles",
912
932
  },
913
933
  {
914
- brand: OptionBrand.Boolean,
934
+ brand: "boolean",
915
935
  description: "Check errors silenced by '// @ts-expect-error' directives.",
916
- group: OptionGroup.ConfigFile,
936
+ group: 4,
917
937
  name: "checkSuppressedErrors",
918
938
  },
919
939
  {
920
- brand: OptionBrand.String,
940
+ brand: "string",
921
941
  description: "The path to a TSTyche configuration file.",
922
- group: OptionGroup.CommandLine,
942
+ group: 2,
923
943
  name: "config",
924
944
  },
925
945
  {
926
- brand: OptionBrand.Boolean,
946
+ brand: "boolean",
927
947
  description: "Stop running tests after the first failed assertion.",
928
- group: OptionGroup.ConfigFile | OptionGroup.CommandLine,
948
+ group: 4 | 2,
929
949
  name: "failFast",
930
950
  },
931
951
  {
932
- brand: OptionBrand.BareTrue,
952
+ brand: "true",
933
953
  description: "Fetch the specified versions of the 'typescript' package and exit.",
934
- group: OptionGroup.CommandLine,
954
+ group: 2,
935
955
  name: "fetch",
936
956
  },
937
957
  {
938
- brand: OptionBrand.List,
958
+ brand: "list",
939
959
  description: "The list of glob patterns matching the fixture files.",
940
- group: OptionGroup.ConfigFile,
960
+ group: 4,
941
961
  items: {
942
- brand: OptionBrand.String,
962
+ brand: "string",
943
963
  name: "fixtureFileMatch",
944
964
  },
945
965
  name: "fixtureFileMatch",
946
966
  },
947
967
  {
948
- brand: OptionBrand.BareTrue,
968
+ brand: "true",
949
969
  description: "Print the list of command line options with brief descriptions and exit.",
950
- group: OptionGroup.CommandLine,
970
+ group: 2,
951
971
  name: "help",
952
972
  },
953
973
  {
954
- brand: OptionBrand.BareTrue,
974
+ brand: "true",
955
975
  description: "Print the list of supported versions of the 'typescript' package and exit.",
956
- group: OptionGroup.CommandLine,
976
+ group: 2,
957
977
  name: "list",
958
978
  },
959
979
  {
960
- brand: OptionBrand.BareTrue,
980
+ brand: "true",
961
981
  description: "Print the list of the selected test files and exit.",
962
- group: OptionGroup.CommandLine,
982
+ group: 2,
963
983
  name: "listFiles",
964
984
  },
965
985
  {
966
- brand: OptionBrand.String,
986
+ brand: "string",
967
987
  description: "Only run tests with matching name.",
968
- group: OptionGroup.CommandLine,
988
+ group: 2,
969
989
  name: "only",
970
990
  },
971
991
  {
972
- brand: OptionBrand.List,
992
+ brand: "list",
973
993
  description: "The list of plugins to use.",
974
- group: OptionGroup.CommandLine | OptionGroup.ConfigFile,
994
+ group: 2 | 4,
975
995
  items: {
976
- brand: OptionBrand.String,
996
+ brand: "string",
977
997
  name: "plugins",
978
998
  },
979
999
  name: "plugins",
980
1000
  },
981
1001
  {
982
- brand: OptionBrand.BareTrue,
1002
+ brand: "true",
983
1003
  description: "Remove all installed versions of the 'typescript' package and exit.",
984
- group: OptionGroup.CommandLine,
1004
+ group: 2,
985
1005
  name: "prune",
986
1006
  },
987
1007
  {
988
- brand: OptionBrand.Boolean,
1008
+ brand: "boolean",
989
1009
  description: "Reject the 'any' type passed as an argument to the 'expect()' function or a matcher.",
990
- group: OptionGroup.ConfigFile,
1010
+ group: 4,
991
1011
  name: "rejectAnyType",
992
1012
  },
993
1013
  {
994
- brand: OptionBrand.Boolean,
1014
+ brand: "boolean",
995
1015
  description: "Reject the 'never' type passed as an argument to the 'expect()' function or a matcher.",
996
- group: OptionGroup.ConfigFile,
1016
+ group: 4,
997
1017
  name: "rejectNeverType",
998
1018
  },
999
1019
  {
1000
- brand: OptionBrand.List,
1020
+ brand: "list",
1001
1021
  description: "The list of reporters to use.",
1002
- group: OptionGroup.CommandLine | OptionGroup.ConfigFile,
1022
+ group: 2 | 4,
1003
1023
  items: {
1004
- brand: OptionBrand.String,
1024
+ brand: "string",
1005
1025
  name: "reporters",
1006
1026
  },
1007
1027
  name: "reporters",
1008
1028
  },
1009
1029
  {
1010
- brand: OptionBrand.String,
1030
+ brand: "string",
1011
1031
  description: "The path to a directory containing files of a test project.",
1012
- group: OptionGroup.ConfigFile,
1032
+ group: 4,
1013
1033
  name: "rootPath",
1014
1034
  },
1015
1035
  {
1016
- brand: OptionBrand.BareTrue,
1036
+ brand: "true",
1017
1037
  description: "Print the resolved configuration and exit.",
1018
- group: OptionGroup.CommandLine,
1038
+ group: 2,
1019
1039
  name: "showConfig",
1020
1040
  },
1021
1041
  {
1022
- brand: OptionBrand.String,
1042
+ brand: "string",
1023
1043
  description: "Skip tests with matching name.",
1024
- group: OptionGroup.CommandLine,
1044
+ group: 2,
1025
1045
  name: "skip",
1026
1046
  },
1027
1047
  {
1028
- brand: OptionBrand.List,
1029
- description: "The list of TypeScript versions to be tested on.",
1030
- group: OptionGroup.CommandLine | OptionGroup.ConfigFile | OptionGroup.InlineConditions,
1031
- items: {
1032
- brand: OptionBrand.String,
1033
- name: "target",
1034
- },
1048
+ brand: "range",
1049
+ description: "The range of TypeScript versions to be tested against.",
1050
+ group: 2 | 4 | 8,
1035
1051
  name: "target",
1036
1052
  },
1037
1053
  {
1038
- brand: OptionBrand.List,
1054
+ brand: "list",
1039
1055
  description: "The list of glob patterns matching the test files.",
1040
- group: OptionGroup.ConfigFile,
1056
+ group: 4,
1041
1057
  items: {
1042
- brand: OptionBrand.String,
1058
+ brand: "string",
1043
1059
  name: "testFileMatch",
1044
1060
  },
1045
1061
  name: "testFileMatch",
1046
1062
  },
1047
1063
  {
1048
- brand: OptionBrand.String,
1064
+ brand: "string",
1049
1065
  description: "The look up strategy to be used to find the TSConfig file.",
1050
- group: OptionGroup.CommandLine | OptionGroup.ConfigFile,
1066
+ group: 2 | 4,
1051
1067
  name: "tsconfig",
1052
1068
  },
1053
1069
  {
1054
- brand: OptionBrand.BareTrue,
1070
+ brand: "true",
1055
1071
  description: "Fetch the 'typescript' package metadata from the registry and exit.",
1056
- group: OptionGroup.CommandLine,
1072
+ group: 2,
1057
1073
  name: "update",
1058
1074
  },
1059
1075
  {
1060
- brand: OptionBrand.BareTrue,
1076
+ brand: "true",
1061
1077
  description: "Print the version number and exit.",
1062
- group: OptionGroup.CommandLine,
1078
+ group: 2,
1063
1079
  name: "version",
1064
1080
  },
1065
1081
  {
1066
- brand: OptionBrand.BareTrue,
1082
+ brand: "true",
1067
1083
  description: "Watch for changes and rerun related test files.",
1068
- group: OptionGroup.CommandLine,
1084
+ group: 2,
1069
1085
  name: "watch",
1070
1086
  },
1071
1087
  ];
@@ -1117,7 +1133,7 @@ class Options {
1117
1133
  }
1118
1134
  return optionValue;
1119
1135
  }
1120
- static async validate(optionName, optionValue, optionBrand, onDiagnostics, origin) {
1136
+ static async validate(optionName, optionValue, onDiagnostics, origin) {
1121
1137
  const canonicalOptionName = Options.#getCanonicalOptionName(optionName);
1122
1138
  switch (canonicalOptionName) {
1123
1139
  case "config":
@@ -1144,16 +1160,17 @@ class Options {
1144
1160
  case "target": {
1145
1161
  if (/[<>=]/.test(optionValue)) {
1146
1162
  if (!Target.isRange(optionValue)) {
1147
- onDiagnostics(Diagnostic.error([ConfigDiagnosticText.rangeIsNotValid(optionValue), ...ConfigDiagnosticText.rangeUsage()], origin));
1163
+ const text = [ConfigDiagnosticText.rangeIsNotValid(optionValue), ...ConfigDiagnosticText.rangeUsage()];
1164
+ onDiagnostics(Diagnostic.error(text, origin));
1148
1165
  }
1149
1166
  break;
1150
1167
  }
1151
1168
  if ((await Store.validateTag(optionValue)) === false) {
1152
- onDiagnostics(Diagnostic.error([
1169
+ const text = [
1153
1170
  ConfigDiagnosticText.versionIsNotSupported(optionValue),
1154
- ...ConfigDiagnosticText.usage(optionName, optionBrand),
1155
1171
  ConfigDiagnosticText.inspectSupportedVersions(),
1156
- ], origin));
1172
+ ];
1173
+ onDiagnostics(Diagnostic.error(text, origin));
1157
1174
  }
1158
1175
  break;
1159
1176
  }
@@ -1174,7 +1191,7 @@ class Options {
1174
1191
  }
1175
1192
  }
1176
1193
 
1177
- class CommandLineParser {
1194
+ class CommandParser {
1178
1195
  #commandLineOptions;
1179
1196
  #onDiagnostics;
1180
1197
  #options;
@@ -1183,12 +1200,12 @@ class CommandLineParser {
1183
1200
  this.#commandLineOptions = commandLine;
1184
1201
  this.#pathMatch = pathMatch;
1185
1202
  this.#onDiagnostics = onDiagnostics;
1186
- this.#options = Options.for(OptionGroup.CommandLine);
1203
+ this.#options = Options.for(2);
1187
1204
  }
1188
1205
  #onExpectsValue(optionName, optionBrand) {
1189
1206
  const text = [
1190
1207
  ConfigDiagnosticText.expectsValue(optionName),
1191
- ...ConfigDiagnosticText.usage(optionName, optionBrand),
1208
+ ConfigDiagnosticText.optionValueMustBe(optionName, optionBrand),
1192
1209
  ];
1193
1210
  this.#onDiagnostics(Diagnostic.error(text));
1194
1211
  }
@@ -1218,18 +1235,18 @@ class CommandLineParser {
1218
1235
  async #parseOptionValue(commandLineArgs, index, optionName, optionDefinition) {
1219
1236
  let optionValue = this.#resolveOptionValue(commandLineArgs[index]);
1220
1237
  switch (optionDefinition.brand) {
1221
- case OptionBrand.BareTrue:
1222
- await Options.validate(optionName, optionValue, optionDefinition.brand, this.#onDiagnostics);
1238
+ case "true":
1239
+ await Options.validate(optionName, optionValue, this.#onDiagnostics);
1223
1240
  this.#commandLineOptions[optionDefinition.name] = true;
1224
1241
  break;
1225
- case OptionBrand.Boolean:
1226
- await Options.validate(optionName, optionValue, optionDefinition.brand, this.#onDiagnostics);
1242
+ case "boolean":
1243
+ await Options.validate(optionName, optionValue, this.#onDiagnostics);
1227
1244
  this.#commandLineOptions[optionDefinition.name] = optionValue !== "false";
1228
1245
  if (optionValue === "false" || optionValue === "true") {
1229
1246
  index++;
1230
1247
  }
1231
1248
  break;
1232
- case OptionBrand.List:
1249
+ case "list":
1233
1250
  if (optionValue !== "") {
1234
1251
  const optionValues = optionValue
1235
1252
  .split(",")
@@ -1237,7 +1254,7 @@ class CommandLineParser {
1237
1254
  .filter((value) => value !== "")
1238
1255
  .map((value) => Options.resolve(optionName, value));
1239
1256
  for (const optionValue of optionValues) {
1240
- await Options.validate(optionName, optionValue, optionDefinition.brand, this.#onDiagnostics);
1257
+ await Options.validate(optionName, optionValue, this.#onDiagnostics);
1241
1258
  }
1242
1259
  this.#commandLineOptions[optionDefinition.name] = optionValues;
1243
1260
  index++;
@@ -1245,16 +1262,30 @@ class CommandLineParser {
1245
1262
  }
1246
1263
  this.#onExpectsValue(optionName, optionDefinition.brand);
1247
1264
  break;
1248
- case OptionBrand.String:
1265
+ case "string":
1249
1266
  if (optionValue !== "") {
1250
1267
  optionValue = Options.resolve(optionName, optionValue);
1251
- await Options.validate(optionName, optionValue, optionDefinition.brand, this.#onDiagnostics);
1268
+ await Options.validate(optionName, optionValue, this.#onDiagnostics);
1252
1269
  this.#commandLineOptions[optionDefinition.name] = optionValue;
1253
1270
  index++;
1254
1271
  break;
1255
1272
  }
1256
1273
  this.#onExpectsValue(optionName, optionDefinition.brand);
1257
1274
  break;
1275
+ case "range":
1276
+ if (optionValue !== "") {
1277
+ const optionValues = [];
1278
+ for (const range of Target.split(optionValue)) {
1279
+ await Options.validate(optionName, range, this.#onDiagnostics);
1280
+ const versions = await Target.expand(range, this.#onDiagnostics);
1281
+ optionValues.push(...versions);
1282
+ }
1283
+ this.#commandLineOptions[optionDefinition.name] = optionValues;
1284
+ index++;
1285
+ break;
1286
+ }
1287
+ this.#onExpectsValue(optionName, "string");
1288
+ break;
1258
1289
  }
1259
1290
  return index;
1260
1291
  }
@@ -1276,43 +1307,58 @@ class ConfigParser {
1276
1307
  this.#sourceFile = sourceFile;
1277
1308
  this.#options = Options.for(optionGroup);
1278
1309
  }
1279
- #onRequiresValue(optionDefinition, jsonNode, isListItem) {
1310
+ #onRequiresValue(optionName, optionBrand, jsonNode, isListItem) {
1280
1311
  const text = isListItem
1281
- ? ConfigDiagnosticText.expectsListItemType(optionDefinition.name, optionDefinition.brand)
1282
- : ConfigDiagnosticText.requiresValueType(optionDefinition.name, optionDefinition.brand);
1312
+ ? ConfigDiagnosticText.expectsListItemType(optionName, optionBrand)
1313
+ : ConfigDiagnosticText.optionValueMustBe(optionName, optionBrand);
1283
1314
  this.#onDiagnostics(Diagnostic.error(text, jsonNode.origin));
1284
1315
  }
1285
1316
  async #parseValue(optionDefinition, isListItem = false) {
1286
1317
  let jsonNode;
1287
1318
  let optionValue;
1288
1319
  switch (optionDefinition.brand) {
1289
- case OptionBrand.Boolean: {
1320
+ case "boolean": {
1290
1321
  jsonNode = this.#jsonScanner.read();
1291
1322
  optionValue = jsonNode.getValue();
1292
1323
  if (typeof optionValue !== "boolean") {
1293
- this.#onRequiresValue(optionDefinition, jsonNode, isListItem);
1324
+ this.#onRequiresValue(optionDefinition.name, optionDefinition.brand, jsonNode, isListItem);
1294
1325
  break;
1295
1326
  }
1296
1327
  break;
1297
1328
  }
1298
- case OptionBrand.String: {
1329
+ case "string": {
1299
1330
  jsonNode = this.#jsonScanner.read();
1300
1331
  optionValue = jsonNode.getValue();
1301
1332
  if (typeof optionValue !== "string") {
1302
- this.#onRequiresValue(optionDefinition, jsonNode, isListItem);
1333
+ this.#onRequiresValue(optionDefinition.name, optionDefinition.brand, jsonNode, isListItem);
1303
1334
  break;
1304
1335
  }
1305
1336
  const rootPath = Path.dirname(this.#sourceFile.fileName);
1306
1337
  optionValue = Options.resolve(optionDefinition.name, optionValue, rootPath);
1307
- await Options.validate(optionDefinition.name, optionValue, optionDefinition.brand, this.#onDiagnostics, jsonNode.origin);
1338
+ await Options.validate(optionDefinition.name, optionValue, this.#onDiagnostics, jsonNode.origin);
1308
1339
  break;
1309
1340
  }
1310
- case OptionBrand.List: {
1341
+ case "range": {
1342
+ optionValue = [];
1343
+ jsonNode = this.#jsonScanner.read();
1344
+ const ranges = jsonNode.getValue();
1345
+ if (typeof ranges !== "string") {
1346
+ this.#onRequiresValue(optionDefinition.name, "string", jsonNode, isListItem);
1347
+ break;
1348
+ }
1349
+ for (const range of Target.split(ranges)) {
1350
+ await Options.validate(optionDefinition.name, range, this.#onDiagnostics, jsonNode.origin);
1351
+ const versions = await Target.expand(range, this.#onDiagnostics, jsonNode.origin);
1352
+ optionValue.push(...versions);
1353
+ }
1354
+ break;
1355
+ }
1356
+ case "list": {
1311
1357
  optionValue = [];
1312
1358
  const leftBracketToken = this.#jsonScanner.readToken("[");
1313
1359
  if (!leftBracketToken.text) {
1314
1360
  jsonNode = this.#jsonScanner.read();
1315
- this.#onRequiresValue(optionDefinition, jsonNode, isListItem);
1361
+ this.#onRequiresValue(optionDefinition.name, optionDefinition.brand, jsonNode, isListItem);
1316
1362
  break;
1317
1363
  }
1318
1364
  while (!this.#jsonScanner.isRead()) {
@@ -1410,8 +1456,8 @@ class ConfigParser {
1410
1456
  }
1411
1457
 
1412
1458
  const defaultOptions = {
1413
- checkSourceFiles: true,
1414
- checkSuppressedErrors: false,
1459
+ checkDeclarationFiles: true,
1460
+ checkSuppressedErrors: true,
1415
1461
  failFast: false,
1416
1462
  fixtureFileMatch: ["**/__fixtures__/*.{ts,tsx}", "**/fixtures/*.{ts,tsx}"],
1417
1463
  plugins: [],
@@ -1419,7 +1465,7 @@ const defaultOptions = {
1419
1465
  rejectNeverType: true,
1420
1466
  reporters: ["list", "summary"],
1421
1467
  rootPath: Path.resolve("./"),
1422
- target: environmentOptions.typescriptModule != null ? ["current"] : ["latest"],
1468
+ target: ["*"],
1423
1469
  testFileMatch: ["**/*.tst.*", "**/__typetests__/*.test.*", "**/typetests/*.test.*"],
1424
1470
  tsconfig: "findup",
1425
1471
  };
@@ -1564,11 +1610,8 @@ class Config {
1564
1610
  static async parseCommandLine(commandLine) {
1565
1611
  const commandLineOptions = {};
1566
1612
  const pathMatch = [];
1567
- const commandLineParser = new CommandLineParser(commandLineOptions, pathMatch, Config.#onDiagnostics);
1613
+ const commandLineParser = new CommandParser(commandLineOptions, pathMatch, Config.#onDiagnostics);
1568
1614
  await commandLineParser.parse(commandLine);
1569
- if (commandLineOptions.target != null) {
1570
- commandLineOptions.target = await Target.expand(commandLineOptions.target);
1571
- }
1572
1615
  return { commandLineOptions, pathMatch };
1573
1616
  }
1574
1617
  static async parseConfigFile(filePath) {
@@ -1581,11 +1624,8 @@ class Config {
1581
1624
  encoding: "utf8",
1582
1625
  });
1583
1626
  const sourceFile = new SourceFile(configFilePath, configFileText);
1584
- const configFileParser = new ConfigParser(configFileOptions, OptionGroup.ConfigFile, sourceFile, new JsonScanner(sourceFile), Config.#onDiagnostics);
1627
+ const configFileParser = new ConfigParser(configFileOptions, 4, sourceFile, new JsonScanner(sourceFile), Config.#onDiagnostics);
1585
1628
  await configFileParser.parse();
1586
- if (configFileOptions.target != null) {
1587
- configFileOptions.target = await Target.expand(configFileOptions.target);
1588
- }
1589
1629
  }
1590
1630
  return { configFileOptions, configFilePath };
1591
1631
  }
@@ -1621,12 +1661,30 @@ class DirectiveDiagnosticText {
1621
1661
 
1622
1662
  class Directive {
1623
1663
  static #directiveRegex = /^(\/\/ *@tstyche)( *|-)?(\S*)?( *)?(.*)?/i;
1624
- static getDirectiveRanges(compiler, sourceFile, position = 0) {
1664
+ static #rangeCache = new WeakMap();
1665
+ static getDirectiveRange(compiler, owner, directiveText) {
1666
+ const directiveRanges = Directive.getDirectiveRanges(compiler, owner.node);
1667
+ return directiveRanges?.find((range) => range.directive?.text === directiveText);
1668
+ }
1669
+ static getDirectiveRanges(compiler, node) {
1670
+ let ranges = Directive.#rangeCache.get(node);
1671
+ if (ranges != null) {
1672
+ return ranges;
1673
+ }
1674
+ let sourceFile;
1675
+ let position = 0;
1676
+ if (compiler.isSourceFile(node)) {
1677
+ sourceFile = node;
1678
+ }
1679
+ else {
1680
+ sourceFile = node.getSourceFile();
1681
+ position = node.getFullStart();
1682
+ }
1625
1683
  const comments = compiler.getLeadingCommentRanges(sourceFile.text, position);
1626
1684
  if (!comments || comments.length === 0) {
1627
1685
  return;
1628
1686
  }
1629
- const ranges = Object.assign([], { sourceFile });
1687
+ ranges = [];
1630
1688
  for (const comment of comments) {
1631
1689
  if (comment.kind !== compiler.SyntaxKind.SingleLineCommentTrivia) {
1632
1690
  continue;
@@ -1636,15 +1694,17 @@ class Directive {
1636
1694
  ranges.push(range);
1637
1695
  }
1638
1696
  }
1697
+ Directive.#rangeCache.set(node, ranges);
1639
1698
  return ranges;
1640
1699
  }
1641
1700
  static async getInlineConfig(ranges) {
1642
1701
  if (!ranges) {
1643
1702
  return;
1644
1703
  }
1704
+ ranges = Array.isArray(ranges) ? ranges : [ranges];
1645
1705
  const inlineConfig = {};
1646
1706
  for (const range of ranges) {
1647
- await Directive.#parse(inlineConfig, ranges.sourceFile, range);
1707
+ await Directive.#parse(inlineConfig, range);
1648
1708
  }
1649
1709
  return inlineConfig;
1650
1710
  }
@@ -1656,6 +1716,7 @@ class Directive {
1656
1716
  return;
1657
1717
  }
1658
1718
  const range = {
1719
+ sourceFile,
1659
1720
  namespace: { start: comment.pos, end: comment.pos + namespaceText.length, text: namespaceText },
1660
1721
  };
1661
1722
  const directiveSeparatorText = match?.[2];
@@ -1675,45 +1736,61 @@ class Directive {
1675
1736
  static #onDiagnostics(diagnostic) {
1676
1737
  EventEmitter.dispatch(["directive:error", { diagnostics: [diagnostic] }]);
1677
1738
  }
1678
- static async #parse(inlineConfig, sourceFile, ranges) {
1679
- switch (ranges.directive?.text) {
1739
+ static async #parse(inlineConfig, range) {
1740
+ switch (range.directive?.text) {
1680
1741
  case "if":
1681
1742
  {
1682
- if (!ranges.argument?.text) {
1743
+ if (!range.argument?.text) {
1683
1744
  const text = DirectiveDiagnosticText.requiresArgument();
1684
- const origin = new DiagnosticOrigin(ranges.namespace.start, ranges.directive.end, sourceFile);
1745
+ const origin = new DiagnosticOrigin(range.namespace.start, range.directive.end, range.sourceFile);
1685
1746
  Directive.#onDiagnostics(Diagnostic.error(text, origin));
1686
1747
  return;
1687
1748
  }
1688
- const value = await Directive.#parseJson(sourceFile, ranges.argument.start, ranges.argument.end);
1749
+ const value = await Directive.#parseJson(range.sourceFile, range.argument.start, range.argument.end);
1689
1750
  inlineConfig.if = value;
1690
1751
  }
1691
1752
  return;
1753
+ case "fixme":
1692
1754
  case "template":
1693
- if (ranges.argument?.text != null) {
1755
+ if (range.argument?.text != null) {
1694
1756
  const text = DirectiveDiagnosticText.doesNotTakeArgument();
1695
- const origin = new DiagnosticOrigin(ranges.argument.start, ranges.argument.end, sourceFile);
1757
+ const origin = new DiagnosticOrigin(range.argument.start, range.argument.end, range.sourceFile);
1696
1758
  Directive.#onDiagnostics(Diagnostic.error(text, origin));
1697
1759
  }
1698
- inlineConfig.template = true;
1760
+ inlineConfig[range.directive?.text] = true;
1699
1761
  return;
1700
1762
  }
1701
- const target = ranges?.directive ?? ranges.namespace;
1763
+ const target = range?.directive ?? range.namespace;
1702
1764
  const text = DirectiveDiagnosticText.isNotSupported(target.text);
1703
- const origin = new DiagnosticOrigin(target.start, target.end, sourceFile);
1765
+ const origin = new DiagnosticOrigin(target.start, target.end, range.sourceFile);
1704
1766
  Directive.#onDiagnostics(Diagnostic.error(text, origin));
1705
1767
  }
1706
1768
  static async #parseJson(sourceFile, start, end) {
1707
1769
  const inlineOptions = {};
1708
- const configParser = new ConfigParser(inlineOptions, OptionGroup.InlineConditions, sourceFile, new JsonScanner(sourceFile, { start, end }), Directive.#onDiagnostics);
1770
+ const configParser = new ConfigParser(inlineOptions, 8, sourceFile, new JsonScanner(sourceFile, { start, end }), Directive.#onDiagnostics);
1709
1771
  await configParser.parse();
1710
- if ("target" in inlineOptions) {
1711
- inlineOptions["target"] = await Target.expand(inlineOptions["target"]);
1712
- }
1713
1772
  return inlineOptions;
1714
1773
  }
1715
1774
  }
1716
1775
 
1776
+ var OptionBrand;
1777
+ (function (OptionBrand) {
1778
+ OptionBrand["String"] = "string";
1779
+ OptionBrand["SemverRange"] = "range";
1780
+ OptionBrand["Number"] = "number";
1781
+ OptionBrand["Boolean"] = "boolean";
1782
+ OptionBrand["True"] = "true";
1783
+ OptionBrand["List"] = "list";
1784
+ })(OptionBrand || (OptionBrand = {}));
1785
+
1786
+ var OptionGroup;
1787
+ (function (OptionGroup) {
1788
+ OptionGroup[OptionGroup["CommandLine"] = 2] = "CommandLine";
1789
+ OptionGroup[OptionGroup["ConfigFile"] = 4] = "ConfigFile";
1790
+ OptionGroup[OptionGroup["InlineConditions"] = 8] = "InlineConditions";
1791
+ OptionGroup[OptionGroup["ResolvedConfig"] = 6] = "ResolvedConfig";
1792
+ })(OptionGroup || (OptionGroup = {}));
1793
+
1717
1794
  class CancellationHandler {
1718
1795
  #cancellationToken;
1719
1796
  #cancellationReason;
@@ -1723,7 +1800,7 @@ class CancellationHandler {
1723
1800
  }
1724
1801
  on([, payload]) {
1725
1802
  if ("diagnostics" in payload) {
1726
- if (payload.diagnostics.some((diagnostic) => diagnostic.category === DiagnosticCategory.Error)) {
1803
+ if (payload.diagnostics.some((diagnostic) => diagnostic.category === "error")) {
1727
1804
  this.#cancellationToken.cancel(this.#cancellationReason);
1728
1805
  }
1729
1806
  }
@@ -1737,7 +1814,7 @@ class ExitCodeHandler {
1737
1814
  return;
1738
1815
  }
1739
1816
  if ("diagnostics" in payload) {
1740
- if (payload.diagnostics.some((diagnostic) => diagnostic.category === DiagnosticCategory.Error)) {
1817
+ if (payload.diagnostics.some((diagnostic) => diagnostic.category === "error")) {
1741
1818
  this.#setCode(1);
1742
1819
  }
1743
1820
  }
@@ -1769,27 +1846,42 @@ class DescribeResult {
1769
1846
  }
1770
1847
  }
1771
1848
 
1772
- var ResultStatus;
1773
- (function (ResultStatus) {
1774
- ResultStatus["Runs"] = "runs";
1775
- ResultStatus["Passed"] = "passed";
1776
- ResultStatus["Failed"] = "failed";
1777
- ResultStatus["Skipped"] = "skipped";
1778
- ResultStatus["Todo"] = "todo";
1779
- })(ResultStatus || (ResultStatus = {}));
1780
-
1781
1849
  class ExpectResult {
1782
- assertion;
1850
+ expect;
1783
1851
  diagnostics = [];
1784
1852
  parent;
1785
- status = ResultStatus.Runs;
1853
+ status = "runs";
1786
1854
  timing = new ResultTiming();
1787
- constructor(assertion, parent) {
1788
- this.assertion = assertion;
1855
+ constructor(expect, parent) {
1856
+ this.expect = expect;
1789
1857
  this.parent = parent;
1790
1858
  }
1791
1859
  }
1792
1860
 
1861
+ class ResultCount {
1862
+ failed = 0;
1863
+ passed = 0;
1864
+ skipped = 0;
1865
+ fixme = 0;
1866
+ todo = 0;
1867
+ get total() {
1868
+ return this.failed + this.passed + this.skipped + this.fixme + this.todo;
1869
+ }
1870
+ }
1871
+
1872
+ class FileResult {
1873
+ diagnostics = [];
1874
+ expectCount = new ResultCount();
1875
+ file;
1876
+ results = [];
1877
+ status = "runs";
1878
+ testCount = new ResultCount();
1879
+ timing = new ResultTiming();
1880
+ constructor(file) {
1881
+ this.file = file;
1882
+ }
1883
+ }
1884
+
1793
1885
  class ProjectResult {
1794
1886
  compilerVersion;
1795
1887
  diagnostics = [];
@@ -1801,51 +1893,38 @@ class ProjectResult {
1801
1893
  }
1802
1894
  }
1803
1895
 
1804
- class ResultCount {
1805
- failed = 0;
1806
- passed = 0;
1807
- skipped = 0;
1808
- todo = 0;
1809
- get total() {
1810
- return this.failed + this.passed + this.skipped + this.todo;
1811
- }
1812
- }
1813
-
1814
1896
  class Result {
1815
1897
  expectCount = new ResultCount();
1816
1898
  fileCount = new ResultCount();
1899
+ files;
1817
1900
  results = [];
1818
1901
  targetCount = new ResultCount();
1819
- tasks;
1820
1902
  testCount = new ResultCount();
1821
1903
  timing = new ResultTiming();
1822
- constructor(tasks) {
1823
- this.tasks = tasks;
1904
+ constructor(files) {
1905
+ this.files = files;
1824
1906
  }
1825
1907
  }
1826
1908
 
1909
+ var ResultStatus;
1910
+ (function (ResultStatus) {
1911
+ ResultStatus["Runs"] = "runs";
1912
+ ResultStatus["Passed"] = "passed";
1913
+ ResultStatus["Failed"] = "failed";
1914
+ ResultStatus["Skipped"] = "skipped";
1915
+ ResultStatus["Fixme"] = "fixme";
1916
+ ResultStatus["Todo"] = "todo";
1917
+ })(ResultStatus || (ResultStatus = {}));
1918
+
1827
1919
  class TargetResult {
1920
+ files;
1828
1921
  results = new Map();
1829
- status = ResultStatus.Runs;
1922
+ status = "runs";
1830
1923
  target;
1831
- tasks;
1832
1924
  timing = new ResultTiming();
1833
- constructor(target, tasks) {
1925
+ constructor(target, files) {
1834
1926
  this.target = target;
1835
- this.tasks = tasks;
1836
- }
1837
- }
1838
-
1839
- class TaskResult {
1840
- diagnostics = [];
1841
- expectCount = new ResultCount();
1842
- results = [];
1843
- status = ResultStatus.Runs;
1844
- task;
1845
- testCount = new ResultCount();
1846
- timing = new ResultTiming();
1847
- constructor(task) {
1848
- this.task = task;
1927
+ this.files = files;
1849
1928
  }
1850
1929
  }
1851
1930
 
@@ -1854,7 +1933,7 @@ class TestResult {
1854
1933
  expectCount = new ResultCount();
1855
1934
  parent;
1856
1935
  results = [];
1857
- status = ResultStatus.Runs;
1936
+ status = "runs";
1858
1937
  test;
1859
1938
  timing = new ResultTiming();
1860
1939
  constructor(test, parent) {
@@ -1866,10 +1945,10 @@ class TestResult {
1866
1945
  class ResultHandler {
1867
1946
  #describeResult;
1868
1947
  #expectResult;
1948
+ #fileResult;
1869
1949
  #projectResult;
1870
1950
  #result;
1871
1951
  #targetResult;
1872
- #taskResult;
1873
1952
  #testResult;
1874
1953
  on([event, payload]) {
1875
1954
  switch (event) {
@@ -1887,19 +1966,19 @@ class ResultHandler {
1887
1966
  this.#targetResult.timing.start = Date.now();
1888
1967
  break;
1889
1968
  case "target:end":
1890
- if (this.#targetResult.status === ResultStatus.Failed) {
1969
+ if (this.#targetResult.status === "failed") {
1891
1970
  this.#result.targetCount.failed++;
1892
1971
  }
1893
1972
  else {
1894
1973
  this.#result.targetCount.passed++;
1895
- this.#targetResult.status = ResultStatus.Passed;
1974
+ this.#targetResult.status = "passed";
1896
1975
  }
1897
1976
  this.#targetResult.timing.end = Date.now();
1898
1977
  this.#targetResult = undefined;
1899
1978
  break;
1900
1979
  case "store:error":
1901
- if (payload.diagnostics.some(({ category }) => category === DiagnosticCategory.Error)) {
1902
- this.#targetResult.status = ResultStatus.Failed;
1980
+ if (payload.diagnostics.some(({ category }) => category === "error")) {
1981
+ this.#targetResult.status = "failed";
1903
1982
  }
1904
1983
  break;
1905
1984
  case "project:uses": {
@@ -1912,42 +1991,42 @@ class ResultHandler {
1912
1991
  break;
1913
1992
  }
1914
1993
  case "project:error":
1915
- this.#targetResult.status = ResultStatus.Failed;
1994
+ this.#targetResult.status = "failed";
1916
1995
  this.#projectResult.diagnostics.push(...payload.diagnostics);
1917
1996
  break;
1918
- case "task:start":
1997
+ case "file:start":
1919
1998
  this.#projectResult.results.push(payload.result);
1920
- this.#taskResult = payload.result;
1921
- this.#taskResult.timing.start = Date.now();
1999
+ this.#fileResult = payload.result;
2000
+ this.#fileResult.timing.start = Date.now();
1922
2001
  break;
1923
- case "task:error":
2002
+ case "file:error":
1924
2003
  case "directive:error":
1925
2004
  case "collect:error":
1926
- this.#targetResult.status = ResultStatus.Failed;
1927
- this.#taskResult.status = ResultStatus.Failed;
1928
- this.#taskResult.diagnostics.push(...payload.diagnostics);
2005
+ this.#targetResult.status = "failed";
2006
+ this.#fileResult.status = "failed";
2007
+ this.#fileResult.diagnostics.push(...payload.diagnostics);
1929
2008
  break;
1930
- case "task:end":
1931
- if (this.#taskResult.status === ResultStatus.Failed ||
1932
- this.#taskResult.expectCount.failed > 0 ||
1933
- this.#taskResult.testCount.failed > 0) {
2009
+ case "file:end":
2010
+ if (this.#fileResult.status === "failed" ||
2011
+ this.#fileResult.expectCount.failed > 0 ||
2012
+ this.#fileResult.testCount.failed > 0) {
1934
2013
  this.#result.fileCount.failed++;
1935
- this.#targetResult.status = ResultStatus.Failed;
1936
- this.#taskResult.status = ResultStatus.Failed;
2014
+ this.#targetResult.status = "failed";
2015
+ this.#fileResult.status = "failed";
1937
2016
  }
1938
2017
  else {
1939
2018
  this.#result.fileCount.passed++;
1940
- this.#taskResult.status = ResultStatus.Passed;
2019
+ this.#fileResult.status = "passed";
1941
2020
  }
1942
- this.#taskResult.timing.end = Date.now();
1943
- this.#taskResult = undefined;
2021
+ this.#fileResult.timing.end = Date.now();
2022
+ this.#fileResult = undefined;
1944
2023
  break;
1945
2024
  case "describe:start":
1946
2025
  if (this.#describeResult) {
1947
2026
  this.#describeResult.results.push(payload.result);
1948
2027
  }
1949
2028
  else {
1950
- this.#taskResult.results.push(payload.result);
2029
+ this.#fileResult.results.push(payload.result);
1951
2030
  }
1952
2031
  this.#describeResult = payload.result;
1953
2032
  this.#describeResult.timing.start = Date.now();
@@ -1961,44 +2040,51 @@ class ResultHandler {
1961
2040
  this.#describeResult.results.push(payload.result);
1962
2041
  }
1963
2042
  else {
1964
- this.#taskResult.results.push(payload.result);
2043
+ this.#fileResult.results.push(payload.result);
1965
2044
  }
1966
2045
  this.#testResult = payload.result;
1967
2046
  this.#testResult.timing.start = Date.now();
1968
2047
  break;
1969
2048
  case "test:error":
1970
2049
  this.#result.testCount.failed++;
1971
- this.#taskResult.testCount.failed++;
1972
- this.#testResult.status = ResultStatus.Failed;
2050
+ this.#fileResult.testCount.failed++;
2051
+ this.#testResult.status = "failed";
1973
2052
  this.#testResult.diagnostics.push(...payload.diagnostics);
1974
2053
  this.#testResult.timing.end = Date.now();
1975
2054
  this.#testResult = undefined;
1976
2055
  break;
1977
2056
  case "test:fail":
1978
2057
  this.#result.testCount.failed++;
1979
- this.#taskResult.testCount.failed++;
1980
- this.#testResult.status = ResultStatus.Failed;
2058
+ this.#fileResult.testCount.failed++;
2059
+ this.#testResult.status = "failed";
1981
2060
  this.#testResult.timing.end = Date.now();
1982
2061
  this.#testResult = undefined;
1983
2062
  break;
1984
2063
  case "test:pass":
1985
2064
  this.#result.testCount.passed++;
1986
- this.#taskResult.testCount.passed++;
1987
- this.#testResult.status = ResultStatus.Passed;
2065
+ this.#fileResult.testCount.passed++;
2066
+ this.#testResult.status = "passed";
1988
2067
  this.#testResult.timing.end = Date.now();
1989
2068
  this.#testResult = undefined;
1990
2069
  break;
1991
2070
  case "test:skip":
1992
2071
  this.#result.testCount.skipped++;
1993
- this.#taskResult.testCount.skipped++;
1994
- this.#testResult.status = ResultStatus.Skipped;
2072
+ this.#fileResult.testCount.skipped++;
2073
+ this.#testResult.status = "skipped";
2074
+ this.#testResult.timing.end = Date.now();
2075
+ this.#testResult = undefined;
2076
+ break;
2077
+ case "test:fixme":
2078
+ this.#result.testCount.fixme++;
2079
+ this.#fileResult.testCount.fixme++;
2080
+ this.#testResult.status = "fixme";
1995
2081
  this.#testResult.timing.end = Date.now();
1996
2082
  this.#testResult = undefined;
1997
2083
  break;
1998
2084
  case "test:todo":
1999
2085
  this.#result.testCount.todo++;
2000
- this.#taskResult.testCount.todo++;
2001
- this.#testResult.status = ResultStatus.Todo;
2086
+ this.#fileResult.testCount.todo++;
2087
+ this.#testResult.status = "todo";
2002
2088
  this.#testResult.timing.end = Date.now();
2003
2089
  this.#testResult = undefined;
2004
2090
  break;
@@ -2007,49 +2093,59 @@ class ResultHandler {
2007
2093
  this.#testResult.results.push(payload.result);
2008
2094
  }
2009
2095
  else {
2010
- this.#taskResult.results.push(payload.result);
2096
+ this.#fileResult.results.push(payload.result);
2011
2097
  }
2012
2098
  this.#expectResult = payload.result;
2013
2099
  this.#expectResult.timing.start = Date.now();
2014
2100
  break;
2015
2101
  case "expect:error":
2016
2102
  this.#result.expectCount.failed++;
2017
- this.#taskResult.expectCount.failed++;
2103
+ this.#fileResult.expectCount.failed++;
2018
2104
  if (this.#testResult) {
2019
2105
  this.#testResult.expectCount.failed++;
2020
2106
  }
2021
- this.#expectResult.status = ResultStatus.Failed;
2107
+ this.#expectResult.status = "failed";
2022
2108
  this.#expectResult.diagnostics.push(...payload.diagnostics);
2023
2109
  this.#expectResult.timing.end = Date.now();
2024
2110
  this.#expectResult = undefined;
2025
2111
  break;
2026
2112
  case "expect:fail":
2027
2113
  this.#result.expectCount.failed++;
2028
- this.#taskResult.expectCount.failed++;
2114
+ this.#fileResult.expectCount.failed++;
2029
2115
  if (this.#testResult) {
2030
2116
  this.#testResult.expectCount.failed++;
2031
2117
  }
2032
- this.#expectResult.status = ResultStatus.Failed;
2118
+ this.#expectResult.status = "failed";
2033
2119
  this.#expectResult.timing.end = Date.now();
2034
2120
  this.#expectResult = undefined;
2035
2121
  break;
2036
2122
  case "expect:pass":
2037
2123
  this.#result.expectCount.passed++;
2038
- this.#taskResult.expectCount.passed++;
2124
+ this.#fileResult.expectCount.passed++;
2039
2125
  if (this.#testResult) {
2040
2126
  this.#testResult.expectCount.passed++;
2041
2127
  }
2042
- this.#expectResult.status = ResultStatus.Passed;
2128
+ this.#expectResult.status = "passed";
2043
2129
  this.#expectResult.timing.end = Date.now();
2044
2130
  this.#expectResult = undefined;
2045
2131
  break;
2046
2132
  case "expect:skip":
2047
2133
  this.#result.expectCount.skipped++;
2048
- this.#taskResult.expectCount.skipped++;
2134
+ this.#fileResult.expectCount.skipped++;
2049
2135
  if (this.#testResult) {
2050
2136
  this.#testResult.expectCount.skipped++;
2051
2137
  }
2052
- this.#expectResult.status = ResultStatus.Skipped;
2138
+ this.#expectResult.status = "skipped";
2139
+ this.#expectResult.timing.end = Date.now();
2140
+ this.#expectResult = undefined;
2141
+ break;
2142
+ case "expect:fixme":
2143
+ this.#result.expectCount.fixme++;
2144
+ this.#fileResult.expectCount.fixme++;
2145
+ if (this.#testResult) {
2146
+ this.#testResult.expectCount.fixme++;
2147
+ }
2148
+ this.#expectResult.status = "fixme";
2053
2149
  this.#expectResult.timing.end = Date.now();
2054
2150
  this.#expectResult = undefined;
2055
2151
  break;
@@ -2078,7 +2174,7 @@ function Text({ children, color, indent }) {
2078
2174
  if (color != null) {
2079
2175
  ansiEscapes.push(color);
2080
2176
  }
2081
- return (jsx("text", { indent: indent ?? 0, children: [ansiEscapes.length > 0 ? jsx("ansi", { escapes: ansiEscapes }) : undefined, children, ansiEscapes.length > 0 ? jsx("ansi", { escapes: Color.Reset }) : undefined] }));
2177
+ return (jsx("text", { indent: indent ?? 0, children: [ansiEscapes.length > 0 ? jsx("ansi", { escapes: ansiEscapes }) : undefined, children, ansiEscapes.length > 0 ? jsx("ansi", { escapes: "0" }) : undefined] }));
2082
2178
  }
2083
2179
 
2084
2180
  function Line({ children, color, indent }) {
@@ -2139,7 +2235,7 @@ class Scribbler {
2139
2235
  }
2140
2236
 
2141
2237
  function addsPackageText(packageVersion, packagePath) {
2142
- return (jsx(Line, { children: [jsx(Text, { color: Color.Gray, children: "adds" }), " TypeScript ", packageVersion, jsx(Text, { color: Color.Gray, children: [" to ", packagePath] })] }));
2238
+ return (jsx(Line, { children: [jsx(Text, { color: "90", children: "adds" }), " TypeScript ", packageVersion, jsx(Text, { color: "90", children: [" to ", packagePath] })] }));
2143
2239
  }
2144
2240
 
2145
2241
  function describeNameText(name, indent = 0) {
@@ -2155,13 +2251,13 @@ function BreadcrumbsText({ ancestor }) {
2155
2251
  ancestor = ancestor.parent;
2156
2252
  }
2157
2253
  text.push("");
2158
- return jsx(Text, { color: Color.Gray, children: text.reverse().join(" ❭ ") });
2254
+ return jsx(Text, { color: "90", children: text.reverse().join(" ❭ ") });
2159
2255
  }
2160
- function CodeLineText({ gutterWidth, lineNumber, lineNumberColor = Color.Gray, lineText }) {
2161
- return (jsx(Line, { children: [jsx(Text, { color: lineNumberColor, children: lineNumber.toString().padStart(gutterWidth) }), jsx(Text, { color: Color.Gray, children: " | " }), lineText] }));
2256
+ function CodeLineText({ gutterWidth, lineNumber, lineNumberColor = "90", lineText }) {
2257
+ return (jsx(Line, { children: [jsx(Text, { color: lineNumberColor, children: lineNumber.toString().padStart(gutterWidth) }), jsx(Text, { color: "90", children: " | " }), lineText] }));
2162
2258
  }
2163
2259
  function SquiggleLineText({ gutterWidth, indentWidth = 0, squiggleColor, squiggleWidth }) {
2164
- return (jsx(Line, { children: [" ".repeat(gutterWidth), jsx(Text, { color: Color.Gray, children: " | " }), " ".repeat(indentWidth), jsx(Text, { color: squiggleColor, children: "~".repeat(squiggleWidth === 0 ? 1 : squiggleWidth) })] }));
2260
+ return (jsx(Line, { children: [" ".repeat(gutterWidth), jsx(Text, { color: "90", children: " | " }), " ".repeat(indentWidth), jsx(Text, { color: squiggleColor, children: "~".repeat(squiggleWidth === 0 ? 1 : squiggleWidth) })] }));
2165
2261
  }
2166
2262
  function CodeFrameText({ diagnosticCategory, diagnosticOrigin, options }) {
2167
2263
  const linesAbove = options?.linesAbove ?? 2;
@@ -2175,11 +2271,11 @@ function CodeFrameText({ diagnosticCategory, diagnosticOrigin, options }) {
2175
2271
  const gutterWidth = (lastLine + 1).toString().length + 2;
2176
2272
  let highlightColor;
2177
2273
  switch (diagnosticCategory) {
2178
- case DiagnosticCategory.Error:
2179
- highlightColor = Color.Red;
2274
+ case "error":
2275
+ highlightColor = "31";
2180
2276
  break;
2181
- case DiagnosticCategory.Warning:
2182
- highlightColor = Color.Yellow;
2277
+ case "warning":
2278
+ highlightColor = "33";
2183
2279
  break;
2184
2280
  }
2185
2281
  const codeFrame = [];
@@ -2207,32 +2303,59 @@ function CodeFrameText({ diagnosticCategory, diagnosticOrigin, options }) {
2207
2303
  }
2208
2304
  }
2209
2305
  let breadcrumbs;
2210
- if (showBreadcrumbs && diagnosticOrigin.assertion != null) {
2211
- breadcrumbs = jsx(BreadcrumbsText, { ancestor: diagnosticOrigin.assertion.parent });
2306
+ if (showBreadcrumbs && diagnosticOrigin.assertionNode != null) {
2307
+ breadcrumbs = jsx(BreadcrumbsText, { ancestor: diagnosticOrigin.assertionNode.parent });
2212
2308
  }
2213
- const location = (jsx(Line, { children: [" ".repeat(gutterWidth + 2), jsx(Text, { color: Color.Gray, children: " at " }), jsx(Text, { color: Color.Cyan, children: Path.relative("", diagnosticOrigin.sourceFile.fileName) }), jsx(Text, { color: Color.Gray, children: `:${firstMarkedLine + 1}:${firstMarkedLineCharacter + 1}` }), breadcrumbs] }));
2309
+ const location = (jsx(Line, { children: [" ".repeat(gutterWidth + 2), jsx(Text, { color: "90", children: " at " }), jsx(Text, { color: "36", children: Path.relative("", diagnosticOrigin.sourceFile.fileName) }), jsx(Text, { color: "90", children: `:${firstMarkedLine + 1}:${firstMarkedLineCharacter + 1}` }), breadcrumbs] }));
2214
2310
  return (jsx(Text, { children: [codeFrame, jsx(Line, {}), location] }));
2215
2311
  }
2216
2312
 
2217
2313
  function DiagnosticText({ codeFrameOptions, diagnostic }) {
2218
- const code = diagnostic.code ? jsx(Text, { color: Color.Gray, children: [" ", diagnostic.code] }) : undefined;
2314
+ const code = diagnostic.code ? jsx(Text, { color: "90", children: [" ", diagnostic.code] }) : undefined;
2219
2315
  const text = Array.isArray(diagnostic.text) ? diagnostic.text : [diagnostic.text];
2220
2316
  const message = text.map((text, index) => (jsx(Text, { children: [index === 1 ? jsx(Line, {}) : undefined, jsx(Line, { children: [text, index === 0 ? code : undefined] })] })));
2221
2317
  const related = diagnostic.related?.map((relatedDiagnostic) => jsx(DiagnosticText, { diagnostic: relatedDiagnostic }));
2222
2318
  const codeFrame = diagnostic.origin ? (jsx(Text, { children: [jsx(Line, {}), jsx(CodeFrameText, { diagnosticCategory: diagnostic.category, diagnosticOrigin: diagnostic.origin, options: codeFrameOptions })] })) : undefined;
2223
2319
  return (jsx(Text, { children: [message, codeFrame, jsx(Line, {}), jsx(Text, { indent: 2, children: related })] }));
2224
2320
  }
2225
- function diagnosticText(diagnostic, codeFrameOptions = {}) {
2226
- let prefix;
2227
- switch (diagnostic.category) {
2228
- case DiagnosticCategory.Error:
2229
- prefix = jsx(Text, { color: Color.Red, children: "Error: " });
2321
+ function diagnosticText(diagnostic, codeFrameOptions = {}) {
2322
+ let prefix;
2323
+ switch (diagnostic.category) {
2324
+ case "error":
2325
+ prefix = jsx(Text, { color: "31", children: "Error: " });
2326
+ break;
2327
+ case "warning":
2328
+ prefix = jsx(Text, { color: "33", children: "Warning: " });
2329
+ break;
2330
+ }
2331
+ return (jsx(Text, { children: [prefix, jsx(DiagnosticText, { codeFrameOptions: codeFrameOptions, diagnostic: diagnostic })] }));
2332
+ }
2333
+
2334
+ function FileNameText({ filePath }) {
2335
+ const relativePath = Path.relative("", filePath);
2336
+ const lastPathSeparator = relativePath.lastIndexOf("/");
2337
+ const directoryNameText = relativePath.slice(0, lastPathSeparator + 1);
2338
+ const fileNameText = relativePath.slice(lastPathSeparator + 1);
2339
+ return (jsx(Text, { children: [jsx(Text, { color: "90", children: directoryNameText }), fileNameText] }));
2340
+ }
2341
+ function fileStatusText(status, file) {
2342
+ let statusColor;
2343
+ let statusText;
2344
+ switch (status) {
2345
+ case "runs":
2346
+ statusColor = "33";
2347
+ statusText = "runs";
2230
2348
  break;
2231
- case DiagnosticCategory.Warning:
2232
- prefix = jsx(Text, { color: Color.Yellow, children: "Warning: " });
2349
+ case "passed":
2350
+ statusColor = "32";
2351
+ statusText = "pass";
2352
+ break;
2353
+ case "failed":
2354
+ statusColor = "31";
2355
+ statusText = "fail";
2233
2356
  break;
2234
2357
  }
2235
- return (jsx(Text, { children: [prefix, jsx(DiagnosticText, { codeFrameOptions: codeFrameOptions, diagnostic: diagnostic })] }));
2358
+ return (jsx(Line, { children: [jsx(Text, { color: statusColor, children: statusText }), " ", jsx(FileNameText, { filePath: file.path })] }));
2236
2359
  }
2237
2360
 
2238
2361
  function fileViewText(lines, addEmptyFinalLine) {
@@ -2258,13 +2381,13 @@ function formattedText(input) {
2258
2381
  }
2259
2382
 
2260
2383
  function HintText({ children }) {
2261
- return (jsx(Text, { indent: 1, color: Color.Gray, children: children }));
2384
+ return (jsx(Text, { indent: 1, color: "90", children: children }));
2262
2385
  }
2263
2386
  function HelpHeaderText({ tstycheVersion }) {
2264
2387
  return (jsx(Line, { children: ["The TSTyche Type Test Runner", jsx(HintText, { children: tstycheVersion })] }));
2265
2388
  }
2266
2389
  function CommandText({ hint, text }) {
2267
- return (jsx(Line, { indent: 1, children: [jsx(Text, { color: Color.Blue, children: text }), hint && jsx(HintText, { children: hint })] }));
2390
+ return (jsx(Line, { indent: 1, children: [jsx(Text, { color: "34", children: text }), hint && jsx(HintText, { children: hint })] }));
2268
2391
  }
2269
2392
  function OptionDescriptionText({ text }) {
2270
2393
  return jsx(Line, { indent: 1, children: text });
@@ -2272,8 +2395,8 @@ function OptionDescriptionText({ text }) {
2272
2395
  function CommandLineUsageText() {
2273
2396
  const usage = [
2274
2397
  ["tstyche", "Run all tests."],
2275
- ["tstyche path/to/first.test.ts", "Only run the test files with matching path."],
2276
- ["tstyche --target 5.3,5.6.2,current", "Test on all specified versions of TypeScript."],
2398
+ ["tstyche query-params", "Only run the matching test file."],
2399
+ ["tstyche --target '5.3 || 5.5.2 || >=5.7'", "Test against specific versions of TypeScript."],
2277
2400
  ];
2278
2401
  const usageText = usage.map(([commandText, descriptionText]) => (jsx(Line, { children: [jsx(CommandText, { text: commandText }), jsx(OptionDescriptionText, { text: descriptionText })] })));
2279
2402
  return jsx(Text, { children: usageText });
@@ -2282,16 +2405,19 @@ function CommandLineOptionNameText({ text }) {
2282
2405
  return jsx(Text, { children: `--${text}` });
2283
2406
  }
2284
2407
  function CommandLineOptionHintText({ definition }) {
2285
- if (definition.brand === OptionBrand.List) {
2408
+ if (definition.brand === "list") {
2286
2409
  return jsx(Text, { children: `${definition.brand} of ${definition.items.brand}s` });
2287
2410
  }
2411
+ if (definition.brand === "range") {
2412
+ return jsx(Text, { children: "string" });
2413
+ }
2288
2414
  return jsx(Text, { children: definition.brand });
2289
2415
  }
2290
2416
  function CommandLineOptionsText({ optionDefinitions }) {
2291
2417
  const definitions = [...optionDefinitions.values()];
2292
2418
  const optionsText = definitions.map((definition) => {
2293
2419
  let hint;
2294
- if (definition.brand !== OptionBrand.BareTrue) {
2420
+ if (definition.brand !== "true") {
2295
2421
  hint = jsx(CommandLineOptionHintText, { definition: definition });
2296
2422
  }
2297
2423
  return (jsx(Text, { children: [jsx(CommandText, { text: jsx(CommandLineOptionNameText, { text: definition.name }), hint: hint }), jsx(OptionDescriptionText, { text: definition.description }), jsx(Line, {})] }));
@@ -2340,69 +2466,44 @@ class OutputService {
2340
2466
  function RowText({ label, text }) {
2341
2467
  return (jsx(Line, { children: [`${label}:`.padEnd(12), text] }));
2342
2468
  }
2343
- function CountText({ failed, passed, skipped, todo, total }) {
2344
- return (jsx(Text, { children: [failed > 0 ? (jsx(Text, { children: [jsx(Text, { color: Color.Red, children: [failed, " failed"] }), jsx(Text, { children: ", " })] })) : undefined, skipped > 0 ? (jsx(Text, { children: [jsx(Text, { color: Color.Yellow, children: [skipped, " skipped"] }), jsx(Text, { children: ", " })] })) : undefined, todo > 0 ? (jsx(Text, { children: [jsx(Text, { color: Color.Magenta, children: [todo, " todo"] }), jsx(Text, { children: ", " })] })) : undefined, passed > 0 ? (jsx(Text, { children: [jsx(Text, { color: Color.Green, children: [passed, " passed"] }), jsx(Text, { children: ", " })] })) : undefined, jsx(Text, { children: [total, " total"] })] }));
2469
+ function CountText({ failed, fixme, passed, skipped, todo, total }) {
2470
+ return (jsx(Text, { children: [failed > 0 ? (jsx(Text, { children: [jsx(Text, { color: "31", children: [failed, " failed"] }), jsx(Text, { children: ", " })] })) : undefined, fixme > 0 ? (jsx(Text, { children: [jsx(Text, { color: "33", children: [fixme, " fixme"] }), jsx(Text, { children: ", " })] })) : undefined, skipped > 0 ? (jsx(Text, { children: [jsx(Text, { color: "33", children: [skipped, " skipped"] }), jsx(Text, { children: ", " })] })) : undefined, todo > 0 ? (jsx(Text, { children: [jsx(Text, { color: "35", children: [todo, " todo"] }), jsx(Text, { children: ", " })] })) : undefined, passed > 0 ? (jsx(Text, { children: [jsx(Text, { color: "32", children: [passed, " passed"] }), jsx(Text, { children: ", " })] })) : undefined, jsx(Text, { children: [total, " total"] })] }));
2345
2471
  }
2346
2472
  function DurationText({ seconds }) {
2347
2473
  return jsx(Text, { children: `${Math.round(seconds * 10) / 10}s` });
2348
2474
  }
2349
2475
  function summaryText({ duration, expectCount, fileCount, targetCount, testCount, }) {
2350
- const targetCountText = (jsx(RowText, { label: "Targets", text: jsx(CountText, { failed: targetCount.failed, passed: targetCount.passed, skipped: targetCount.skipped, todo: targetCount.todo, total: targetCount.total }) }));
2351
- const fileCountText = (jsx(RowText, { label: "Test files", text: jsx(CountText, { failed: fileCount.failed, passed: fileCount.passed, skipped: fileCount.skipped, todo: fileCount.todo, total: fileCount.total }) }));
2352
- const testCountText = (jsx(RowText, { label: "Tests", text: jsx(CountText, { failed: testCount.failed, passed: testCount.passed, skipped: testCount.skipped, todo: testCount.todo, total: testCount.total }) }));
2353
- const assertionCountText = (jsx(RowText, { label: "Assertions", text: jsx(CountText, { failed: expectCount.failed, passed: expectCount.passed, skipped: expectCount.skipped, todo: expectCount.todo, total: expectCount.total }) }));
2476
+ const targetCountText = (jsx(RowText, { label: "Targets", text: jsx(CountText, { failed: targetCount.failed, fixme: targetCount.fixme, passed: targetCount.passed, skipped: targetCount.skipped, todo: targetCount.todo, total: targetCount.total }) }));
2477
+ const fileCountText = (jsx(RowText, { label: "Test files", text: jsx(CountText, { failed: fileCount.failed, fixme: fileCount.fixme, passed: fileCount.passed, skipped: fileCount.skipped, todo: fileCount.todo, total: fileCount.total }) }));
2478
+ const testCountText = (jsx(RowText, { label: "Tests", text: jsx(CountText, { failed: testCount.failed, fixme: testCount.fixme, passed: testCount.passed, skipped: testCount.skipped, todo: testCount.todo, total: testCount.total }) }));
2479
+ const assertionCountText = (jsx(RowText, { label: "Assertions", text: jsx(CountText, { failed: expectCount.failed, fixme: expectCount.fixme, passed: expectCount.passed, skipped: expectCount.skipped, todo: expectCount.todo, total: expectCount.total }) }));
2354
2480
  return (jsx(Text, { children: [targetCountText, fileCountText, testCount.total > 0 ? testCountText : undefined, expectCount.total > 0 ? assertionCountText : undefined, jsx(RowText, { label: "Duration", text: jsx(DurationText, { seconds: duration / 1000 }) })] }));
2355
2481
  }
2356
2482
 
2357
- function FileNameText({ filePath }) {
2358
- const relativePath = Path.relative("", filePath);
2359
- const lastPathSeparator = relativePath.lastIndexOf("/");
2360
- const directoryNameText = relativePath.slice(0, lastPathSeparator + 1);
2361
- const fileNameText = relativePath.slice(lastPathSeparator + 1);
2362
- return (jsx(Text, { children: [jsx(Text, { color: Color.Gray, children: directoryNameText }), fileNameText] }));
2363
- }
2364
- function taskStatusText(status, task) {
2365
- let statusColor;
2366
- let statusText;
2367
- switch (status) {
2368
- case ResultStatus.Runs:
2369
- statusColor = Color.Yellow;
2370
- statusText = "runs";
2371
- break;
2372
- case ResultStatus.Passed:
2373
- statusColor = Color.Green;
2374
- statusText = "pass";
2375
- break;
2376
- case ResultStatus.Failed:
2377
- statusColor = Color.Red;
2378
- statusText = "fail";
2379
- break;
2380
- }
2381
- return (jsx(Line, { children: [jsx(Text, { color: statusColor, children: statusText }), " ", jsx(FileNameText, { filePath: task.filePath })] }));
2382
- }
2383
-
2384
2483
  function StatusText({ status }) {
2385
2484
  switch (status) {
2386
2485
  case "fail":
2387
- return jsx(Text, { color: Color.Red, children: "\u00D7" });
2486
+ return jsx(Text, { color: "31", children: "\u00D7" });
2388
2487
  case "pass":
2389
- return jsx(Text, { color: Color.Green, children: "+" });
2488
+ return jsx(Text, { color: "32", children: "+" });
2390
2489
  case "skip":
2391
- return jsx(Text, { color: Color.Yellow, children: "- skip" });
2490
+ return jsx(Text, { color: "33", children: "- skip" });
2491
+ case "fixme":
2492
+ return jsx(Text, { color: "33", children: "- fixme" });
2392
2493
  case "todo":
2393
- return jsx(Text, { color: Color.Magenta, children: "- todo" });
2494
+ return jsx(Text, { color: "35", children: "- todo" });
2394
2495
  }
2395
2496
  }
2396
2497
  function testNameText(status, name, indent = 0) {
2397
- return (jsx(Line, { indent: indent + 1, children: [jsx(StatusText, { status: status }), " ", jsx(Text, { color: Color.Gray, children: name })] }));
2498
+ return (jsx(Line, { indent: indent + 1, children: [jsx(StatusText, { status: status }), " ", jsx(Text, { color: "90", children: name })] }));
2398
2499
  }
2399
2500
 
2400
2501
  function usesCompilerText(compilerVersion, projectConfigFilePath, options) {
2401
2502
  let projectConfigPathText;
2402
2503
  if (projectConfigFilePath != null) {
2403
- projectConfigPathText = (jsx(Text, { color: Color.Gray, children: [" with ", Path.relative("", projectConfigFilePath)] }));
2504
+ projectConfigPathText = (jsx(Text, { color: "90", children: [" with ", Path.relative("", projectConfigFilePath)] }));
2404
2505
  }
2405
- return (jsx(Text, { children: [options?.prependEmptyLine ? jsx(Line, {}) : undefined, jsx(Line, { children: [jsx(Text, { color: Color.Blue, children: "uses" }), " TypeScript ", compilerVersion, projectConfigPathText] }), jsx(Line, {})] }));
2506
+ return (jsx(Text, { children: [options?.prependEmptyLine ? jsx(Line, {}) : undefined, jsx(Line, { children: [jsx(Text, { color: "34", children: "uses" }), " TypeScript ", compilerVersion, projectConfigPathText] }), jsx(Line, {})] }));
2406
2507
  }
2407
2508
 
2408
2509
  function waitingForFileChangesText() {
@@ -2415,7 +2516,7 @@ function watchUsageText() {
2415
2516
  ["x", "to exit."],
2416
2517
  ];
2417
2518
  const usageText = usage.map(([keyText, actionText]) => {
2418
- return (jsx(Line, { children: [jsx(Text, { color: Color.Gray, children: "Press" }), jsx(Text, { children: ` ${keyText} ` }), jsx(Text, { color: Color.Gray, children: actionText })] }));
2519
+ return (jsx(Line, { children: [jsx(Text, { color: "90", children: "Press" }), jsx(Text, { children: ` ${keyText} ` }), jsx(Text, { color: "90", children: actionText })] }));
2419
2520
  });
2420
2521
  return jsx(Text, { children: usageText });
2421
2522
  }
@@ -2492,7 +2593,7 @@ class ListReporter extends BaseReporter {
2492
2593
  on([event, payload]) {
2493
2594
  switch (event) {
2494
2595
  case "run:start":
2495
- this.#isFileViewExpanded = payload.result.tasks.length === 1 && this.resolvedConfig.watch !== true;
2596
+ this.#isFileViewExpanded = payload.result.files.length === 1 && this.resolvedConfig.watch !== true;
2496
2597
  break;
2497
2598
  case "store:adds":
2498
2599
  OutputService.writeMessage(addsPackageText(payload.packageVersion, payload.packagePath));
@@ -2504,7 +2605,7 @@ class ListReporter extends BaseReporter {
2504
2605
  }
2505
2606
  break;
2506
2607
  case "target:start":
2507
- this.#fileCount = payload.result.tasks.length;
2608
+ this.#fileCount = payload.result.files.length;
2508
2609
  this.#hasReportedUses = false;
2509
2610
  break;
2510
2611
  case "project:uses":
@@ -2519,25 +2620,25 @@ class ListReporter extends BaseReporter {
2519
2620
  OutputService.writeError(diagnosticText(diagnostic));
2520
2621
  }
2521
2622
  break;
2522
- case "task:start":
2623
+ case "file:start":
2523
2624
  if (!environmentOptions.noInteractive) {
2524
- OutputService.writeMessage(taskStatusText(payload.result.status, payload.result.task));
2625
+ OutputService.writeMessage(fileStatusText(payload.result.status, payload.result.file));
2525
2626
  }
2526
2627
  this.#fileCount--;
2527
2628
  this.#hasReportedError = false;
2528
2629
  break;
2529
- case "task:error":
2630
+ case "file:error":
2530
2631
  case "directive:error":
2531
2632
  case "collect:error":
2532
2633
  for (const diagnostic of payload.diagnostics) {
2533
2634
  this.#fileView.addMessage(diagnosticText(diagnostic));
2534
2635
  }
2535
2636
  break;
2536
- case "task:end":
2637
+ case "file:end":
2537
2638
  if (!environmentOptions.noInteractive) {
2538
2639
  OutputService.eraseLastLine();
2539
2640
  }
2540
- OutputService.writeMessage(taskStatusText(payload.result.status, payload.result.task));
2641
+ OutputService.writeMessage(fileStatusText(payload.result.status, payload.result.file));
2541
2642
  OutputService.writeMessage(this.#fileView.getViewText({ appendEmptyLine: this.#isLastFile }));
2542
2643
  if (this.#fileView.hasErrors) {
2543
2644
  OutputService.writeError(this.#fileView.getMessages());
@@ -2560,6 +2661,11 @@ class ListReporter extends BaseReporter {
2560
2661
  this.#fileView.addTest("skip", payload.result.test.name);
2561
2662
  }
2562
2663
  break;
2664
+ case "test:fixme":
2665
+ if (this.#isFileViewExpanded) {
2666
+ this.#fileView.addTest("fixme", payload.result.test.name);
2667
+ }
2668
+ break;
2563
2669
  case "test:todo":
2564
2670
  if (this.#isFileViewExpanded) {
2565
2671
  this.#fileView.addTest("todo", payload.result.test.name);
@@ -2602,10 +2708,10 @@ class SetupReporter {
2602
2708
  if ("diagnostics" in payload) {
2603
2709
  for (const diagnostic of payload.diagnostics) {
2604
2710
  switch (diagnostic.category) {
2605
- case DiagnosticCategory.Error:
2711
+ case "error":
2606
2712
  OutputService.writeError(diagnosticText(diagnostic));
2607
2713
  break;
2608
- case DiagnosticCategory.Warning:
2714
+ case "warning":
2609
2715
  OutputService.writeWarning(diagnosticText(diagnostic));
2610
2716
  break;
2611
2717
  }
@@ -2651,18 +2757,18 @@ class WatchReporter extends BaseReporter {
2651
2757
  }
2652
2758
  }
2653
2759
 
2654
- class Task {
2655
- filePath;
2760
+ class FileLocation {
2761
+ path;
2656
2762
  position;
2657
- constructor(filePath, position) {
2658
- this.filePath = Path.resolve(this.#toPath(filePath));
2763
+ constructor(file, position) {
2764
+ this.path = Path.resolve(this.#toPath(file));
2659
2765
  this.position = position;
2660
2766
  }
2661
- #toPath(filePath) {
2662
- if (typeof filePath === "string" && !filePath.startsWith("file:")) {
2663
- return filePath;
2767
+ #toPath(file) {
2768
+ if (typeof file === "string" && !file.startsWith("file:")) {
2769
+ return file;
2664
2770
  }
2665
- return fileURLToPath(filePath);
2771
+ return fileURLToPath(file);
2666
2772
  }
2667
2773
  }
2668
2774
 
@@ -3000,9 +3106,9 @@ class WatchService {
3000
3106
  #resolvedConfig;
3001
3107
  #watchedTestFiles;
3002
3108
  #watchers = [];
3003
- constructor(resolvedConfig, tasks) {
3109
+ constructor(resolvedConfig, files) {
3004
3110
  this.#resolvedConfig = resolvedConfig;
3005
- this.#watchedTestFiles = new Map(tasks.map((task) => [task.filePath, task]));
3111
+ this.#watchedTestFiles = new Map(files.map((file) => [file.path, file]));
3006
3112
  }
3007
3113
  #onDiagnostics(diagnostic) {
3008
3114
  EventEmitter.dispatch(["watch:error", { diagnostics: [diagnostic] }]);
@@ -3031,7 +3137,7 @@ class WatchService {
3031
3137
  case "\u001B":
3032
3138
  case "q":
3033
3139
  case "x":
3034
- onClose(CancellationReason.WatchClose);
3140
+ onClose("watchClose");
3035
3141
  break;
3036
3142
  case "\u000D":
3037
3143
  case "\u0020":
@@ -3047,14 +3153,14 @@ class WatchService {
3047
3153
  }
3048
3154
  const onChangedFile = (filePath) => {
3049
3155
  debounce.refresh();
3050
- let task = this.#watchedTestFiles.get(filePath);
3051
- if (task != null) {
3052
- this.#changedTestFiles.set(filePath, task);
3156
+ let file = this.#watchedTestFiles.get(filePath);
3157
+ if (file != null) {
3158
+ this.#changedTestFiles.set(filePath, file);
3053
3159
  }
3054
3160
  else if (Select.isTestFile(filePath, this.#resolvedConfig)) {
3055
- task = new Task(filePath);
3056
- this.#changedTestFiles.set(filePath, task);
3057
- this.#watchedTestFiles.set(filePath, task);
3161
+ file = new FileLocation(filePath);
3162
+ this.#changedTestFiles.set(filePath, file);
3163
+ this.#watchedTestFiles.set(filePath, file);
3058
3164
  }
3059
3165
  };
3060
3166
  const onRemovedFile = (filePath) => {
@@ -3067,7 +3173,7 @@ class WatchService {
3067
3173
  };
3068
3174
  this.#watchers.push(new Watcher(this.#resolvedConfig.rootPath, onChangedFile, onRemovedFile, { recursive: true }));
3069
3175
  const onChangedConfigFile = () => {
3070
- onClose(CancellationReason.ConfigChange);
3176
+ onClose("configChange");
3071
3177
  };
3072
3178
  this.#watchers.push(new FileWatcher(this.#resolvedConfig.configFilePath, onChangedConfigFile));
3073
3179
  for (const watcher of this.#watchers) {
@@ -3082,65 +3188,6 @@ class WatchService {
3082
3188
  }
3083
3189
  }
3084
3190
 
3085
- class TestTreeNode {
3086
- brand;
3087
- children = [];
3088
- diagnostics = new Set();
3089
- flags;
3090
- name = "";
3091
- node;
3092
- parent;
3093
- constructor(compiler, brand, node, parent, flags) {
3094
- this.brand = brand;
3095
- this.node = node;
3096
- this.parent = parent;
3097
- this.flags = flags;
3098
- if (node.arguments[0] != null && compiler.isStringLiteralLike(node.arguments[0])) {
3099
- this.name = node.arguments[0].text;
3100
- }
3101
- if (node.arguments[1] != null && compiler.isFunctionLike(node.arguments[1])) {
3102
- for (const diagnostic of parent.diagnostics) {
3103
- if (diagnosticBelongsToNode(diagnostic, node.arguments[1].body)) {
3104
- this.diagnostics.add(diagnostic);
3105
- parent.diagnostics.delete(diagnostic);
3106
- }
3107
- }
3108
- }
3109
- }
3110
- getDirectiveRanges(compiler) {
3111
- return Directive.getDirectiveRanges(compiler, this.node.getSourceFile(), this.node.getFullStart());
3112
- }
3113
- }
3114
-
3115
- class AssertionNode extends TestTreeNode {
3116
- abilityDiagnostics = new Set();
3117
- isNot;
3118
- matcherNode;
3119
- matcherNameNode;
3120
- modifierNode;
3121
- notNode;
3122
- source;
3123
- target;
3124
- constructor(compiler, brand, node, parent, flags, matcherNode, matcherNameNode, modifierNode, notNode) {
3125
- super(compiler, brand, node, parent, flags);
3126
- this.isNot = notNode != null;
3127
- this.matcherNode = matcherNode;
3128
- this.matcherNameNode = matcherNameNode;
3129
- this.modifierNode = modifierNode;
3130
- this.source = this.node.typeArguments ?? this.node.arguments;
3131
- if (compiler.isCallExpression(this.matcherNode)) {
3132
- this.target = this.matcherNode.typeArguments ?? this.matcherNode.arguments;
3133
- }
3134
- for (const diagnostic of parent.diagnostics) {
3135
- if (diagnosticBelongsToNode(diagnostic, this.source) ||
3136
- (this.target != null && diagnosticBelongsToNode(diagnostic, this.target))) {
3137
- this.diagnostics.add(diagnostic);
3138
- parent.diagnostics.delete(diagnostic);
3139
- }
3140
- }
3141
- }
3142
- }
3143
-
3144
3191
  function nodeBelongsToArgumentList(compiler, node) {
3145
3192
  return compiler.isCallExpression(node.parent) && node.parent.arguments.some((argument) => argument === node);
3146
3193
  }
@@ -3150,7 +3197,7 @@ function nodeIsChildOfExpressionStatement(compiler, node) {
3150
3197
 
3151
3198
  class AbilityLayer {
3152
3199
  #compiler;
3153
- #expectErrorRegex = /^( *)(\/\/ *@ts-expect-error)(!?)(:? *)(.*)?$/gim;
3200
+ #expectErrorRegex = /^(\s*)(\/\/ *@ts-expect-error)(!?)(:? *)(.*)?$/gim;
3154
3201
  #filePath = "";
3155
3202
  #nodes = [];
3156
3203
  #projectService;
@@ -3171,16 +3218,27 @@ class AbilityLayer {
3171
3218
  this.#text = `${this.#text.slice(0, range.start)}${rangeText}${this.#text.slice(range.end)}`;
3172
3219
  }
3173
3220
  }
3174
- #belongsToNode(diagnostic) {
3221
+ #belongsToNode(node, diagnostic) {
3222
+ switch (node.brand) {
3223
+ case "expect":
3224
+ return (diagnosticBelongsToNode(diagnostic, node.matcherNode) &&
3225
+ !diagnosticBelongsToNode(diagnostic, node.source));
3226
+ case "when":
3227
+ return (diagnosticBelongsToNode(diagnostic, node.actionNode) &&
3228
+ !diagnosticBelongsToNode(diagnostic, node.target));
3229
+ }
3230
+ return false;
3231
+ }
3232
+ #mapToNodes(diagnostic) {
3175
3233
  for (const node of this.#nodes) {
3176
- if (diagnosticBelongsToNode(diagnostic, "matcherNode" in node ? node.matcherNode : node.actionNode)) {
3234
+ if (this.#belongsToNode(node, diagnostic)) {
3177
3235
  node.abilityDiagnostics.add(diagnostic);
3178
3236
  return true;
3179
3237
  }
3180
3238
  }
3181
3239
  return false;
3182
3240
  }
3183
- #belongsToDirective(diagnostic) {
3241
+ #mapToDirectives(diagnostic) {
3184
3242
  if (!isDiagnosticWithLocation(diagnostic)) {
3185
3243
  return;
3186
3244
  }
@@ -3225,18 +3283,19 @@ class AbilityLayer {
3225
3283
  }
3226
3284
  return ranges;
3227
3285
  }
3228
- close() {
3286
+ close(testTree) {
3229
3287
  if (this.#nodes.length > 0 || this.#suppressedErrorsMap != null) {
3288
+ SourceService.set(testTree.sourceFile);
3230
3289
  this.#projectService.openFile(this.#filePath, this.#text, this.#resolvedConfig.rootPath);
3231
3290
  const languageService = this.#projectService.getLanguageService(this.#filePath);
3232
3291
  const diagnostics = languageService?.getSemanticDiagnostics(this.#filePath);
3233
3292
  if (diagnostics != null) {
3234
3293
  this.#nodes.reverse();
3235
3294
  for (const diagnostic of diagnostics) {
3236
- if (this.#belongsToNode(diagnostic)) {
3295
+ if (this.#mapToNodes(diagnostic)) {
3237
3296
  continue;
3238
3297
  }
3239
- this.#belongsToDirective(diagnostic);
3298
+ this.#mapToDirectives(diagnostic);
3240
3299
  }
3241
3300
  }
3242
3301
  }
@@ -3268,36 +3327,36 @@ class AbilityLayer {
3268
3327
  }
3269
3328
  return text.join("");
3270
3329
  }
3271
- handleAssertion(assertionNode) {
3272
- const expectStart = assertionNode.node.getStart();
3273
- const expectExpressionEnd = assertionNode.node.expression.getEnd();
3274
- const expectEnd = assertionNode.node.getEnd();
3275
- const matcherNameEnd = assertionNode.matcherNameNode.getEnd();
3276
- switch (assertionNode.matcherNameNode.name.text) {
3330
+ handleExpect(expect) {
3331
+ const expectStart = expect.node.getStart();
3332
+ const expectExpressionEnd = expect.node.expression.getEnd();
3333
+ const expectEnd = expect.node.getEnd();
3334
+ const matcherNameEnd = expect.matcherNameNode.getEnd();
3335
+ switch (expect.matcherNameNode.name.text) {
3277
3336
  case "toBeApplicable":
3278
- this.#addRanges(assertionNode, [
3337
+ this.#addRanges(expect, [
3279
3338
  { start: expectStart, end: expectExpressionEnd },
3280
3339
  { start: expectEnd, end: matcherNameEnd },
3281
3340
  ]);
3282
3341
  break;
3283
3342
  case "toBeCallableWith":
3284
- this.#eraseTrailingComma(assertionNode.source, assertionNode);
3285
- this.#addRanges(assertionNode, [
3343
+ this.#eraseTrailingComma(expect.source, expect);
3344
+ this.#addRanges(expect, [
3286
3345
  {
3287
3346
  start: expectStart,
3288
3347
  end: expectExpressionEnd,
3289
- replacement: nodeIsChildOfExpressionStatement(this.#compiler, assertionNode.matcherNode) ? ";" : "",
3348
+ replacement: nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode) ? ";" : "",
3290
3349
  },
3291
3350
  { start: expectEnd, end: matcherNameEnd },
3292
3351
  ]);
3293
3352
  break;
3294
3353
  case "toBeConstructableWith":
3295
- this.#eraseTrailingComma(assertionNode.source, assertionNode);
3296
- this.#addRanges(assertionNode, [
3354
+ this.#eraseTrailingComma(expect.source, expect);
3355
+ this.#addRanges(expect, [
3297
3356
  {
3298
3357
  start: expectStart,
3299
3358
  end: expectExpressionEnd,
3300
- replacement: nodeIsChildOfExpressionStatement(this.#compiler, assertionNode.matcherNode) ? "; new" : "new",
3359
+ replacement: nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode) ? "; new" : "new",
3301
3360
  },
3302
3361
  { start: expectEnd, end: matcherNameEnd },
3303
3362
  ]);
@@ -3307,7 +3366,7 @@ class AbilityLayer {
3307
3366
  #handleSuppressedErrors(testTree) {
3308
3367
  const suppressedErrors = this.#collectSuppressedErrors();
3309
3368
  if (this.#resolvedConfig.checkSuppressedErrors) {
3310
- testTree.suppressedErrors = Object.assign(suppressedErrors, { sourceFile: testTree.sourceFile });
3369
+ testTree.suppressedErrors = suppressedErrors;
3311
3370
  this.#suppressedErrorsMap = new Map();
3312
3371
  }
3313
3372
  for (const suppressedError of suppressedErrors) {
@@ -3352,22 +3411,61 @@ class CollectDiagnosticText {
3352
3411
  }
3353
3412
  }
3354
3413
 
3355
- var TestTreeNodeBrand;
3356
- (function (TestTreeNodeBrand) {
3357
- TestTreeNodeBrand["Describe"] = "describe";
3358
- TestTreeNodeBrand["Test"] = "test";
3359
- TestTreeNodeBrand["Expect"] = "expect";
3360
- TestTreeNodeBrand["When"] = "when";
3361
- })(TestTreeNodeBrand || (TestTreeNodeBrand = {}));
3414
+ class TestTreeNode {
3415
+ brand;
3416
+ children = [];
3417
+ diagnostics = new Set();
3418
+ flags;
3419
+ name = "";
3420
+ node;
3421
+ parent;
3422
+ constructor(compiler, brand, node, parent, flags) {
3423
+ this.brand = brand;
3424
+ this.node = node;
3425
+ this.parent = parent;
3426
+ this.flags = flags;
3427
+ if (node.arguments[0] != null && compiler.isStringLiteralLike(node.arguments[0])) {
3428
+ this.name = node.arguments[0].text;
3429
+ }
3430
+ if (node.arguments[1] != null && compiler.isFunctionLike(node.arguments[1])) {
3431
+ for (const diagnostic of parent.diagnostics) {
3432
+ if (diagnosticBelongsToNode(diagnostic, node.arguments[1].body)) {
3433
+ this.diagnostics.add(diagnostic);
3434
+ parent.diagnostics.delete(diagnostic);
3435
+ }
3436
+ }
3437
+ }
3438
+ }
3439
+ }
3362
3440
 
3363
- var TestTreeNodeFlags;
3364
- (function (TestTreeNodeFlags) {
3365
- TestTreeNodeFlags[TestTreeNodeFlags["None"] = 0] = "None";
3366
- TestTreeNodeFlags[TestTreeNodeFlags["Fail"] = 1] = "Fail";
3367
- TestTreeNodeFlags[TestTreeNodeFlags["Only"] = 2] = "Only";
3368
- TestTreeNodeFlags[TestTreeNodeFlags["Skip"] = 4] = "Skip";
3369
- TestTreeNodeFlags[TestTreeNodeFlags["Todo"] = 8] = "Todo";
3370
- })(TestTreeNodeFlags || (TestTreeNodeFlags = {}));
3441
+ class ExpectNode extends TestTreeNode {
3442
+ abilityDiagnostics = new Set();
3443
+ isNot;
3444
+ matcherNode;
3445
+ matcherNameNode;
3446
+ modifierNode;
3447
+ notNode;
3448
+ source;
3449
+ target;
3450
+ constructor(compiler, brand, node, parent, flags, matcherNode, matcherNameNode, modifierNode, notNode) {
3451
+ super(compiler, brand, node, parent, flags);
3452
+ this.isNot = notNode != null;
3453
+ this.matcherNode = matcherNode;
3454
+ this.matcherNameNode = matcherNameNode;
3455
+ this.modifierNode = modifierNode;
3456
+ this.source = this.node.typeArguments ?? this.node.arguments;
3457
+ if (compiler.isCallExpression(this.matcherNode)) {
3458
+ this.target = this.matcherNode.typeArguments ?? this.matcherNode.arguments;
3459
+ }
3460
+ for (const diagnostic of parent.diagnostics) {
3461
+ if (diagnosticBelongsToNode(diagnostic, this.source) ||
3462
+ (this.target != null && diagnosticBelongsToNode(diagnostic, this.target))) {
3463
+ this.diagnostics.add(diagnostic);
3464
+ parent.diagnostics.delete(diagnostic);
3465
+ }
3466
+ }
3467
+ }
3468
+ }
3371
3469
 
3372
3470
  class IdentifierLookup {
3373
3471
  #compiler;
@@ -3416,24 +3514,21 @@ class IdentifierLookup {
3416
3514
  };
3417
3515
  }
3418
3516
  resolveTestTreeNodeMeta(node) {
3419
- let flags = TestTreeNodeFlags.None;
3517
+ let flags = 0;
3420
3518
  let expression = node.expression;
3421
3519
  while (this.#compiler.isPropertyAccessExpression(expression)) {
3422
3520
  if (expression.expression.getText() === this.#identifiers.namespace) {
3423
3521
  break;
3424
3522
  }
3425
3523
  switch (expression.name.getText()) {
3426
- case "fail":
3427
- flags |= TestTreeNodeFlags.Fail;
3428
- break;
3429
3524
  case "only":
3430
- flags |= TestTreeNodeFlags.Only;
3525
+ flags |= 1;
3431
3526
  break;
3432
3527
  case "skip":
3433
- flags |= TestTreeNodeFlags.Skip;
3528
+ flags |= 2;
3434
3529
  break;
3435
3530
  case "todo":
3436
- flags |= TestTreeNodeFlags.Todo;
3531
+ flags |= 4;
3437
3532
  break;
3438
3533
  }
3439
3534
  expression = expression.expression;
@@ -3451,14 +3546,14 @@ class IdentifierLookup {
3451
3546
  }
3452
3547
  switch (identifier) {
3453
3548
  case "describe":
3454
- return { brand: TestTreeNodeBrand.Describe, flags, identifier };
3549
+ return { brand: "describe", flags, identifier };
3455
3550
  case "it":
3456
3551
  case "test":
3457
- return { brand: TestTreeNodeBrand.Test, flags, identifier };
3552
+ return { brand: "test", flags, identifier };
3458
3553
  case "expect":
3459
- return { brand: TestTreeNodeBrand.Expect, flags, identifier };
3554
+ return { brand: "expect", flags, identifier };
3460
3555
  case "when":
3461
- return { brand: TestTreeNodeBrand.When, flags, identifier };
3556
+ return { brand: "when", flags, identifier };
3462
3557
  }
3463
3558
  return;
3464
3559
  }
@@ -3474,9 +3569,6 @@ class TestTree {
3474
3569
  this.diagnostics = diagnostics;
3475
3570
  this.sourceFile = sourceFile;
3476
3571
  }
3477
- getDirectiveRanges(compiler) {
3478
- return Directive.getDirectiveRanges(compiler, this.sourceFile);
3479
- }
3480
3572
  }
3481
3573
 
3482
3574
  class WhenNode extends TestTreeNode {
@@ -3514,7 +3606,7 @@ class CollectService {
3514
3606
  if (!this.#checkNode(node, meta, parent)) {
3515
3607
  return;
3516
3608
  }
3517
- if (meta.brand === TestTreeNodeBrand.Describe || meta.brand === TestTreeNodeBrand.Test) {
3609
+ if (meta.brand === "describe" || meta.brand === "test") {
3518
3610
  const testTreeNode = new TestTreeNode(this.#compiler, meta.brand, node, parent, meta.flags);
3519
3611
  this.#compiler.forEachChild(node, (node) => {
3520
3612
  this.#collectTestTreeNodes(node, testTreeNode, testTree);
@@ -3522,7 +3614,7 @@ class CollectService {
3522
3614
  this.#onNode(testTreeNode, parent, testTree);
3523
3615
  return;
3524
3616
  }
3525
- if (meta.brand === TestTreeNodeBrand.Expect) {
3617
+ if (meta.brand === "expect") {
3526
3618
  const modifierNode = this.#getChainedNode(node, "type");
3527
3619
  if (!modifierNode) {
3528
3620
  return;
@@ -3536,15 +3628,15 @@ class CollectService {
3536
3628
  if (!matcherNode) {
3537
3629
  return;
3538
3630
  }
3539
- const assertionNode = new AssertionNode(this.#compiler, meta.brand, node, parent, meta.flags, matcherNode, matcherNameNode, modifierNode, notNode);
3540
- this.#abilityLayer.handleAssertion(assertionNode);
3631
+ const expectNode = new ExpectNode(this.#compiler, meta.brand, node, parent, meta.flags, matcherNode, matcherNameNode, modifierNode, notNode);
3632
+ this.#abilityLayer.handleExpect(expectNode);
3541
3633
  this.#compiler.forEachChild(node, (node) => {
3542
- this.#collectTestTreeNodes(node, assertionNode, testTree);
3634
+ this.#collectTestTreeNodes(node, expectNode, testTree);
3543
3635
  });
3544
- this.#onNode(assertionNode, parent, testTree);
3636
+ this.#onNode(expectNode, parent, testTree);
3545
3637
  return;
3546
3638
  }
3547
- if (meta.brand === TestTreeNodeBrand.When) {
3639
+ if (meta.brand === "when") {
3548
3640
  const actionNameNode = this.#getChainedNode(node);
3549
3641
  if (!actionNameNode) {
3550
3642
  return;
@@ -3556,7 +3648,7 @@ class CollectService {
3556
3648
  this.#compiler.forEachChild(actionNode, (node) => {
3557
3649
  if (this.#compiler.isCallExpression(node)) {
3558
3650
  const meta = this.#identifierLookup.resolveTestTreeNodeMeta(node);
3559
- if (meta?.brand === TestTreeNodeBrand.Describe || meta?.brand === TestTreeNodeBrand.Test) {
3651
+ if (meta?.brand === "describe" || meta?.brand === "test") {
3560
3652
  const text = CollectDiagnosticText.cannotBeNestedWithin(meta.identifier, "when");
3561
3653
  const origin = DiagnosticOrigin.fromNode(node);
3562
3654
  this.#onDiagnostics(Diagnostic.error(text, origin));
@@ -3584,7 +3676,7 @@ class CollectService {
3584
3676
  this.#abilityLayer.open(testTree);
3585
3677
  this.#identifierLookup.open();
3586
3678
  this.#collectTestTreeNodes(sourceFile, testTree, testTree);
3587
- this.#abilityLayer.close();
3679
+ this.#abilityLayer.close(testTree);
3588
3680
  EventEmitter.dispatch(["collect:end", { tree: testTree }]);
3589
3681
  return testTree;
3590
3682
  }
@@ -3599,15 +3691,15 @@ class CollectService {
3599
3691
  }
3600
3692
  #isNodeAllowed(meta, parent) {
3601
3693
  switch (meta.brand) {
3602
- case TestTreeNodeBrand.Describe:
3603
- case TestTreeNodeBrand.Test:
3604
- if (parent.brand === TestTreeNodeBrand.Test || parent.brand === TestTreeNodeBrand.Expect) {
3694
+ case "describe":
3695
+ case "test":
3696
+ if (parent.brand === "test" || parent.brand === "expect") {
3605
3697
  return false;
3606
3698
  }
3607
3699
  break;
3608
- case TestTreeNodeBrand.Expect:
3609
- case TestTreeNodeBrand.When:
3610
- if (parent.brand === TestTreeNodeBrand.Describe) {
3700
+ case "expect":
3701
+ case "when":
3702
+ if (parent.brand === "describe") {
3611
3703
  return false;
3612
3704
  }
3613
3705
  break;
@@ -3646,13 +3738,29 @@ class CollectService {
3646
3738
  }
3647
3739
  #onNode(node, parent, testTree) {
3648
3740
  parent.children.push(node);
3649
- if (node.flags & TestTreeNodeFlags.Only) {
3741
+ if (node.flags & 1) {
3650
3742
  testTree.hasOnly = true;
3651
3743
  }
3652
3744
  EventEmitter.dispatch(["collect:node", { node }]);
3653
3745
  }
3654
3746
  }
3655
3747
 
3748
+ var TestTreeNodeBrand;
3749
+ (function (TestTreeNodeBrand) {
3750
+ TestTreeNodeBrand["Describe"] = "describe";
3751
+ TestTreeNodeBrand["Test"] = "test";
3752
+ TestTreeNodeBrand["Expect"] = "expect";
3753
+ TestTreeNodeBrand["When"] = "when";
3754
+ })(TestTreeNodeBrand || (TestTreeNodeBrand = {}));
3755
+
3756
+ var TestTreeNodeFlags;
3757
+ (function (TestTreeNodeFlags) {
3758
+ TestTreeNodeFlags[TestTreeNodeFlags["None"] = 0] = "None";
3759
+ TestTreeNodeFlags[TestTreeNodeFlags["Only"] = 1] = "Only";
3760
+ TestTreeNodeFlags[TestTreeNodeFlags["Skip"] = 2] = "Skip";
3761
+ TestTreeNodeFlags[TestTreeNodeFlags["Todo"] = 4] = "Todo";
3762
+ })(TestTreeNodeFlags || (TestTreeNodeFlags = {}));
3763
+
3656
3764
  class ProjectService {
3657
3765
  #compiler;
3658
3766
  #lastSeenProject = "";
@@ -3703,8 +3811,6 @@ class ProjectService {
3703
3811
  }
3704
3812
  #getDefaultCompilerOptions() {
3705
3813
  const defaultCompilerOptions = {
3706
- allowJs: true,
3707
- checkJs: true,
3708
3814
  exactOptionalPropertyTypes: true,
3709
3815
  jsx: this.#compiler.JsxEmit.Preserve,
3710
3816
  module: this.#compiler.ModuleKind.NodeNext,
@@ -3726,7 +3832,7 @@ class ProjectService {
3726
3832
  getDefaultProject(filePath) {
3727
3833
  const project = this.#service.getDefaultProjectForFile(this.#compiler.server.toNormalizedPath(filePath), true);
3728
3834
  const compilerOptions = project?.getCompilerOptions();
3729
- if (this.#resolvedConfig.checkSourceFiles && compilerOptions?.skipLibCheck) {
3835
+ if (this.#resolvedConfig.checkDeclarationFiles && compilerOptions?.skipLibCheck) {
3730
3836
  project?.setCompilerOptions({ ...compilerOptions, skipLibCheck: false });
3731
3837
  }
3732
3838
  return project;
@@ -3759,12 +3865,12 @@ class ProjectService {
3759
3865
  "project:uses",
3760
3866
  { compilerVersion: this.#compiler.version, projectConfigFilePath: configFileName },
3761
3867
  ]);
3762
- }
3763
- if (configFileErrors && configFileErrors.length > 0) {
3764
- EventEmitter.dispatch([
3765
- "project:error",
3766
- { diagnostics: Diagnostic.fromDiagnostics(configFileErrors) },
3767
- ]);
3868
+ if (configFileErrors && configFileErrors.length > 0) {
3869
+ EventEmitter.dispatch([
3870
+ "project:error",
3871
+ { diagnostics: Diagnostic.fromDiagnostics(configFileErrors) },
3872
+ ]);
3873
+ }
3768
3874
  }
3769
3875
  if (!this.#seenTestFiles.has(filePath)) {
3770
3876
  this.#seenTestFiles.add(filePath);
@@ -3778,13 +3884,16 @@ class ProjectService {
3778
3884
  if (program.isSourceFileFromExternalLibrary(sourceFile) || program.isSourceFileDefaultLibrary(sourceFile)) {
3779
3885
  return false;
3780
3886
  }
3887
+ if (this.#resolvedConfig.checkDeclarationFiles && sourceFile.isDeclarationFile) {
3888
+ return true;
3889
+ }
3781
3890
  if (Select.isFixtureFile(sourceFile.fileName, { ...this.#resolvedConfig, pathMatch: [] })) {
3782
3891
  return true;
3783
3892
  }
3784
3893
  if (Select.isTestFile(sourceFile.fileName, { ...this.#resolvedConfig, pathMatch: [] })) {
3785
3894
  return false;
3786
3895
  }
3787
- return this.#resolvedConfig.checkSourceFiles;
3896
+ return false;
3788
3897
  });
3789
3898
  const diagnostics = [];
3790
3899
  for (const sourceFile of sourceFilesToCheck) {
@@ -3817,24 +3926,26 @@ class SuppressedDiagnosticText {
3817
3926
  }
3818
3927
 
3819
3928
  class SuppressedService {
3820
- match(suppressedErrors, onDiagnostics) {
3821
- for (const suppressedError of suppressedErrors) {
3929
+ match(testTree, onDiagnostics) {
3930
+ if (!testTree.suppressedErrors) {
3931
+ return;
3932
+ }
3933
+ for (const suppressedError of testTree.suppressedErrors) {
3822
3934
  if (suppressedError.diagnostics.length === 0 || suppressedError.ignore) {
3823
3935
  continue;
3824
3936
  }
3937
+ const related = [
3938
+ Diagnostic.error(SuppressedDiagnosticText.suppressedError(suppressedError.diagnostics.length)),
3939
+ ...Diagnostic.fromDiagnostics(suppressedError.diagnostics),
3940
+ ];
3941
+ const origin = new DiagnosticOrigin(suppressedError.directive.start, suppressedError.directive.end, testTree.sourceFile);
3825
3942
  if (!suppressedError.argument?.text) {
3826
3943
  const text = SuppressedDiagnosticText.directiveRequires();
3827
- const origin = new DiagnosticOrigin(suppressedError.directive.start, suppressedError.directive.end, suppressedErrors.sourceFile);
3828
- onDiagnostics([Diagnostic.error(text, origin)]);
3944
+ onDiagnostics([Diagnostic.error(text, origin).add({ related })]);
3829
3945
  continue;
3830
3946
  }
3831
- const related = [
3832
- Diagnostic.error(SuppressedDiagnosticText.suppressedError(suppressedError.diagnostics.length)),
3833
- ...Diagnostic.fromDiagnostics(suppressedError.diagnostics, suppressedErrors.sourceFile),
3834
- ];
3835
3947
  if (suppressedError.diagnostics.length > 1) {
3836
3948
  const text = [SuppressedDiagnosticText.onlySingleError()];
3837
- const origin = new DiagnosticOrigin(suppressedError.directive.start, suppressedError.directive.end, suppressedErrors.sourceFile);
3838
3949
  onDiagnostics([Diagnostic.error(text, origin).add({ related })]);
3839
3950
  continue;
3840
3951
  }
@@ -3842,25 +3953,28 @@ class SuppressedService {
3842
3953
  if (Array.isArray(messageText)) {
3843
3954
  messageText = messageText.join("\n");
3844
3955
  }
3845
- if (!messageText.includes(suppressedError.argument.text)) {
3956
+ if (!this.#matchMessage(messageText, suppressedError.argument.text)) {
3846
3957
  const text = [SuppressedDiagnosticText.messageDidNotMatch()];
3847
- const origin = new DiagnosticOrigin(suppressedError.argument.start, suppressedError.argument.end, suppressedErrors.sourceFile);
3958
+ const origin = new DiagnosticOrigin(suppressedError.argument.start, suppressedError.argument.end, testTree.sourceFile);
3848
3959
  onDiagnostics([Diagnostic.error(text, origin).add({ related })]);
3849
3960
  }
3850
3961
  }
3851
3962
  }
3963
+ #matchMessage(source, target) {
3964
+ if (target.includes("...")) {
3965
+ let position = 0;
3966
+ for (const segment of target.split("...")) {
3967
+ position = source.indexOf(segment, position);
3968
+ if (position === -1) {
3969
+ break;
3970
+ }
3971
+ }
3972
+ return position > 0;
3973
+ }
3974
+ return source.includes(target);
3975
+ }
3852
3976
  }
3853
3977
 
3854
- var RunMode;
3855
- (function (RunMode) {
3856
- RunMode[RunMode["Normal"] = 0] = "Normal";
3857
- RunMode[RunMode["Fail"] = 1] = "Fail";
3858
- RunMode[RunMode["Only"] = 2] = "Only";
3859
- RunMode[RunMode["Skip"] = 4] = "Skip";
3860
- RunMode[RunMode["Todo"] = 8] = "Todo";
3861
- RunMode[RunMode["Void"] = 16] = "Void";
3862
- })(RunMode || (RunMode = {}));
3863
-
3864
3978
  class EnsureDiagnosticText {
3865
3979
  static argumentMustBeProvided(argumentNameText) {
3866
3980
  return `An argument for '${argumentNameText}' must be provided.`;
@@ -3984,21 +4098,15 @@ class ExpectDiagnosticText {
3984
4098
  }
3985
4099
  }
3986
4100
 
3987
- var Relation;
3988
- (function (Relation) {
3989
- Relation["Assignable"] = "assignable";
3990
- Relation["Identical"] = "identical";
3991
- })(Relation || (Relation = {}));
3992
-
3993
4101
  class MatchWorker {
3994
- assertion;
4102
+ assertionNode;
3995
4103
  #compiler;
3996
4104
  #signatureCache = new Map();
3997
4105
  typeChecker;
3998
- constructor(compiler, typeChecker, assertion) {
4106
+ constructor(compiler, typeChecker, assertionNode) {
3999
4107
  this.#compiler = compiler;
4000
4108
  this.typeChecker = typeChecker;
4001
- this.assertion = assertion;
4109
+ this.assertionNode = assertionNode;
4002
4110
  }
4003
4111
  checkHasApplicableIndexType(sourceNode, targetNode) {
4004
4112
  const sourceType = this.getType(sourceNode);
@@ -4014,13 +4122,13 @@ class MatchWorker {
4014
4122
  .some((property) => this.#compiler.unescapeLeadingUnderscores(property.escapedName) === propertyNameText);
4015
4123
  }
4016
4124
  checkIsAssignableTo(sourceNode, targetNode) {
4017
- return this.#checkIsRelatedTo(sourceNode, targetNode, Relation.Assignable);
4125
+ return this.#checkIsRelatedTo(sourceNode, targetNode, "assignable");
4018
4126
  }
4019
4127
  checkIsAssignableWith(sourceNode, targetNode) {
4020
- return this.#checkIsRelatedTo(targetNode, sourceNode, Relation.Assignable);
4128
+ return this.#checkIsRelatedTo(targetNode, sourceNode, "assignable");
4021
4129
  }
4022
4130
  checkIsIdenticalTo(sourceNode, targetNode) {
4023
- return (this.#checkIsRelatedTo(sourceNode, targetNode, Relation.Identical) &&
4131
+ return (this.#checkIsRelatedTo(sourceNode, targetNode, "identical") &&
4024
4132
  this.checkIsAssignableTo(sourceNode, targetNode) &&
4025
4133
  this.checkIsAssignableWith(sourceNode, targetNode));
4026
4134
  }
@@ -4028,9 +4136,9 @@ class MatchWorker {
4028
4136
  const sourceType = relation === "identical" ? this.#simplifyType(this.getType(sourceNode)) : this.getType(sourceNode);
4029
4137
  const targetType = relation === "identical" ? this.#simplifyType(this.getType(targetNode)) : this.getType(targetNode);
4030
4138
  switch (relation) {
4031
- case Relation.Assignable:
4139
+ case "assignable":
4032
4140
  return this.typeChecker.isTypeAssignableTo(sourceType, targetType);
4033
- case Relation.Identical:
4141
+ case "identical":
4034
4142
  return this.typeChecker.isTypeIdenticalTo(sourceType, targetType);
4035
4143
  }
4036
4144
  }
@@ -4071,9 +4179,9 @@ class MatchWorker {
4071
4179
  this.#compiler.isShorthandPropertyAssignment(symbol.valueDeclaration)) &&
4072
4180
  symbol.valueDeclaration.getStart() >= enclosingNode.getStart() &&
4073
4181
  symbol.valueDeclaration.getEnd() <= enclosingNode.getEnd()) {
4074
- return DiagnosticOrigin.fromNode(symbol.valueDeclaration.name, this.assertion);
4182
+ return DiagnosticOrigin.fromNode(symbol.valueDeclaration.name, this.assertionNode);
4075
4183
  }
4076
- return DiagnosticOrigin.fromNode(enclosingNode, this.assertion);
4184
+ return DiagnosticOrigin.fromNode(enclosingNode, this.assertionNode);
4077
4185
  }
4078
4186
  #simplifyType(type) {
4079
4187
  if (type.isUnionOrIntersection()) {
@@ -4108,10 +4216,10 @@ class ToAcceptProps {
4108
4216
  const signatures = matchWorker.getSignatures(sourceNode);
4109
4217
  return signatures.reduce((accumulator, signature, index) => {
4110
4218
  let diagnostic;
4111
- const introText = matchWorker.assertion.isNot
4219
+ const introText = matchWorker.assertionNode.isNot
4112
4220
  ? ExpectDiagnosticText.acceptsProps(isExpression)
4113
4221
  : ExpectDiagnosticText.doesNotAcceptProps(isExpression);
4114
- const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.assertion);
4222
+ const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.assertionNode);
4115
4223
  if (signatures.length > 1) {
4116
4224
  const signatureText = this.#typeChecker.signatureToString(signature, sourceNode);
4117
4225
  const overloadText = ExpectDiagnosticText.overloadGaveTheFollowingError(index + 1, signatures.length, signatureText);
@@ -4121,7 +4229,7 @@ class ToAcceptProps {
4121
4229
  diagnostic = Diagnostic.error([introText], origin);
4122
4230
  }
4123
4231
  const { diagnostics, isMatch } = this.#explainProperties(matchWorker, signature, targetNode, diagnostic);
4124
- if (matchWorker.assertion.isNot ? isMatch : !isMatch) {
4232
+ if (matchWorker.assertionNode.isNot ? isMatch : !isMatch) {
4125
4233
  accumulator.push(...diagnostics);
4126
4234
  }
4127
4235
  return accumulator;
@@ -4229,7 +4337,7 @@ class ToAcceptProps {
4229
4337
  if (sourceType != null && isUnionType(this.#compiler, sourceType)) {
4230
4338
  let accumulator = [];
4231
4339
  const isMatch = sourceType.types.some((sourceType) => {
4232
- const text = matchWorker.assertion.isNot
4340
+ const text = matchWorker.assertionNode.isNot
4233
4341
  ? ExpectDiagnosticText.isAssignableWith(sourceTypeText, targetTypeText)
4234
4342
  : ExpectDiagnosticText.isNotAssignableWith(sourceTypeText, targetTypeText);
4235
4343
  const { diagnostics, isMatch } = explain(sourceType, targetType, diagnostic.extendWith(text));
@@ -4284,10 +4392,10 @@ class RelationMatcherBase {
4284
4392
  explain(matchWorker, sourceNode, targetNode) {
4285
4393
  const sourceTypeText = matchWorker.getTypeText(sourceNode);
4286
4394
  const targetTypeText = matchWorker.getTypeText(targetNode);
4287
- const text = matchWorker.assertion.isNot
4395
+ const text = matchWorker.assertionNode.isNot
4288
4396
  ? this.explainText(sourceTypeText, targetTypeText)
4289
4397
  : this.explainNotText(sourceTypeText, targetTypeText);
4290
- const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.assertion);
4398
+ const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.assertionNode);
4291
4399
  return [Diagnostic.error(text, origin)];
4292
4400
  }
4293
4401
  }
@@ -4334,18 +4442,22 @@ class ToBeApplicable {
4334
4442
  }
4335
4443
  return text;
4336
4444
  }
4337
- #explain(matchWorker, sourceNode) {
4338
- const targetText = this.#resolveTargetText(matchWorker.assertion.matcherNode.parent);
4445
+ #explain(matchWorker) {
4446
+ const targetText = this.#resolveTargetText(matchWorker.assertionNode.matcherNode.parent);
4339
4447
  const diagnostics = [];
4340
- if (matchWorker.assertion.abilityDiagnostics.size > 0) {
4341
- for (const diagnostic of matchWorker.assertion.abilityDiagnostics) {
4448
+ if (matchWorker.assertionNode.abilityDiagnostics.size > 0) {
4449
+ const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertionNode);
4450
+ for (const diagnostic of matchWorker.assertionNode.abilityDiagnostics) {
4342
4451
  const text = [ExpectDiagnosticText.cannotBeApplied(targetText), getDiagnosticMessageText(diagnostic)];
4343
- const origin = DiagnosticOrigin.fromNode(sourceNode);
4344
- diagnostics.push(Diagnostic.error(text.flat(), origin));
4452
+ let related;
4453
+ if (diagnostic.relatedInformation != null) {
4454
+ related = Diagnostic.fromDiagnostics(diagnostic.relatedInformation);
4455
+ }
4456
+ diagnostics.push(Diagnostic.error(text.flat(), origin).add({ related }));
4345
4457
  }
4346
4458
  }
4347
4459
  else {
4348
- const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertion);
4460
+ const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertionNode);
4349
4461
  diagnostics.push(Diagnostic.error(ExpectDiagnosticText.canBeApplied(targetText), origin));
4350
4462
  }
4351
4463
  return diagnostics;
@@ -4362,8 +4474,8 @@ class ToBeApplicable {
4362
4474
  return;
4363
4475
  }
4364
4476
  return {
4365
- explain: () => this.#explain(matchWorker, sourceNode),
4366
- isMatch: matchWorker.assertion.abilityDiagnostics.size === 0,
4477
+ explain: () => this.#explain(matchWorker),
4478
+ isMatch: matchWorker.assertionNode.abilityDiagnostics.size === 0,
4367
4479
  };
4368
4480
  }
4369
4481
  }
@@ -4408,32 +4520,25 @@ class AbilityMatcherBase {
4408
4520
  const isExpression = nodeBelongsToArgumentList(this.compiler, sourceNode);
4409
4521
  const targetText = this.#resolveTargetText(targetNodes);
4410
4522
  const diagnostics = [];
4411
- if (matchWorker.assertion.abilityDiagnostics.size > 0) {
4412
- for (const diagnostic of matchWorker.assertion.abilityDiagnostics) {
4523
+ if (matchWorker.assertionNode.abilityDiagnostics.size > 0) {
4524
+ for (const diagnostic of matchWorker.assertionNode.abilityDiagnostics) {
4525
+ const text = [this.explainNotText(isExpression, targetText), getDiagnosticMessageText(diagnostic)];
4413
4526
  let origin;
4414
- const text = [];
4415
- if (isDiagnosticWithLocation(diagnostic) && diagnosticBelongsToNode(diagnostic, sourceNode)) {
4416
- origin = new DiagnosticOrigin(diagnostic.start, getTextSpanEnd(diagnostic), sourceNode.getSourceFile(), matchWorker.assertion);
4417
- text.push(getDiagnosticMessageText(diagnostic));
4527
+ if (isDiagnosticWithLocation(diagnostic) && diagnosticBelongsToNode(diagnostic, targetNodes)) {
4528
+ origin = new DiagnosticOrigin(diagnostic.start, getTextSpanEnd(diagnostic), sourceNode.getSourceFile(), matchWorker.assertionNode);
4418
4529
  }
4419
4530
  else {
4420
- if (isDiagnosticWithLocation(diagnostic) && diagnosticBelongsToNode(diagnostic, targetNodes)) {
4421
- origin = new DiagnosticOrigin(diagnostic.start, getTextSpanEnd(diagnostic), sourceNode.getSourceFile(), matchWorker.assertion);
4422
- }
4423
- else {
4424
- origin = DiagnosticOrigin.fromAssertion(matchWorker.assertion);
4425
- }
4426
- text.push(this.explainNotText(isExpression, targetText), getDiagnosticMessageText(diagnostic));
4531
+ origin = DiagnosticOrigin.fromAssertion(matchWorker.assertionNode);
4427
4532
  }
4428
4533
  let related;
4429
4534
  if (diagnostic.relatedInformation != null) {
4430
- related = Diagnostic.fromDiagnostics(diagnostic.relatedInformation, sourceNode.getSourceFile());
4535
+ related = Diagnostic.fromDiagnostics(diagnostic.relatedInformation);
4431
4536
  }
4432
4537
  diagnostics.push(Diagnostic.error(text.flat(), origin).add({ related }));
4433
4538
  }
4434
4539
  }
4435
4540
  else {
4436
- const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertion);
4541
+ const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertionNode);
4437
4542
  diagnostics.push(Diagnostic.error(this.explainText(isExpression, targetText), origin));
4438
4543
  }
4439
4544
  return diagnostics;
@@ -4473,7 +4578,7 @@ class ToBeCallableWith extends AbilityMatcherBase {
4473
4578
  }
4474
4579
  return {
4475
4580
  explain: () => this.explain(matchWorker, sourceNode, targetNodes),
4476
- isMatch: matchWorker.assertion.abilityDiagnostics.size === 0,
4581
+ isMatch: matchWorker.assertionNode.abilityDiagnostics.size === 0,
4477
4582
  };
4478
4583
  }
4479
4584
  }
@@ -4508,7 +4613,7 @@ class ToBeConstructableWith extends AbilityMatcherBase {
4508
4613
  }
4509
4614
  return {
4510
4615
  explain: () => this.explain(matchWorker, sourceNode, targetNodes),
4511
- isMatch: matchWorker.assertion.abilityDiagnostics.size === 0,
4616
+ isMatch: matchWorker.assertionNode.abilityDiagnostics.size === 0,
4512
4617
  };
4513
4618
  }
4514
4619
  }
@@ -4528,8 +4633,8 @@ class ToHaveProperty {
4528
4633
  else {
4529
4634
  propertyNameText = `[${this.#compiler.unescapeLeadingUnderscores(targetType.symbol.escapedName)}]`;
4530
4635
  }
4531
- const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.assertion);
4532
- return matchWorker.assertion.isNot
4636
+ const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.assertionNode);
4637
+ return matchWorker.assertionNode.isNot
4533
4638
  ? [Diagnostic.error(ExpectDiagnosticText.hasProperty(sourceTypeText, propertyNameText), origin)]
4534
4639
  : [Diagnostic.error(ExpectDiagnosticText.doesNotHaveProperty(sourceTypeText, propertyNameText), origin)];
4535
4640
  }
@@ -4579,28 +4684,28 @@ class ToRaiseError {
4579
4684
  }
4580
4685
  #explain(matchWorker, sourceNode, targetNodes) {
4581
4686
  const isExpression = nodeBelongsToArgumentList(this.#compiler, sourceNode);
4582
- const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertion);
4583
- if (matchWorker.assertion.diagnostics.size === 0) {
4687
+ const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertionNode);
4688
+ if (matchWorker.assertionNode.diagnostics.size === 0) {
4584
4689
  const text = ExpectDiagnosticText.didNotRaiseError(isExpression);
4585
4690
  return [Diagnostic.error(text, origin)];
4586
4691
  }
4587
- if (matchWorker.assertion.diagnostics.size !== targetNodes.length) {
4588
- const count = matchWorker.assertion.diagnostics.size;
4692
+ if (matchWorker.assertionNode.diagnostics.size !== targetNodes.length) {
4693
+ const count = matchWorker.assertionNode.diagnostics.size;
4589
4694
  const text = ExpectDiagnosticText.raisedError(isExpression, count, targetNodes.length);
4590
4695
  const related = [
4591
4696
  Diagnostic.error(ExpectDiagnosticText.raisedTypeError(count)),
4592
- ...Diagnostic.fromDiagnostics([...matchWorker.assertion.diagnostics]),
4697
+ ...Diagnostic.fromDiagnostics([...matchWorker.assertionNode.diagnostics]),
4593
4698
  ];
4594
4699
  return [Diagnostic.error(text, origin).add({ related })];
4595
4700
  }
4596
- return [...matchWorker.assertion.diagnostics].reduce((accumulator, diagnostic, index) => {
4701
+ return [...matchWorker.assertionNode.diagnostics].reduce((accumulator, diagnostic, index) => {
4597
4702
  const targetNode = targetNodes[index];
4598
4703
  const isMatch = this.#matchExpectedError(diagnostic, targetNode);
4599
- if (matchWorker.assertion.isNot ? isMatch : !isMatch) {
4600
- const text = matchWorker.assertion.isNot
4704
+ if (matchWorker.assertionNode.isNot ? isMatch : !isMatch) {
4705
+ const text = matchWorker.assertionNode.isNot
4601
4706
  ? ExpectDiagnosticText.raisedMatchingError(isExpression)
4602
4707
  : ExpectDiagnosticText.didNotRaiseMatchingError(isExpression);
4603
- const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.assertion);
4708
+ const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.assertionNode);
4604
4709
  const related = [
4605
4710
  Diagnostic.error(ExpectDiagnosticText.raisedTypeError()),
4606
4711
  ...Diagnostic.fromDiagnostics([diagnostic]),
@@ -4628,12 +4733,12 @@ class ToRaiseError {
4628
4733
  }
4629
4734
  let isMatch;
4630
4735
  if (targetNodes.length === 0) {
4631
- isMatch = matchWorker.assertion.diagnostics.size > 0;
4736
+ isMatch = matchWorker.assertionNode.diagnostics.size > 0;
4632
4737
  }
4633
4738
  else {
4634
4739
  isMatch =
4635
- matchWorker.assertion.diagnostics.size === targetNodes.length &&
4636
- [...matchWorker.assertion.diagnostics].every((diagnostic, index) => this.#matchExpectedError(diagnostic, targetNodes[index]));
4740
+ matchWorker.assertionNode.diagnostics.size === targetNodes.length &&
4741
+ [...matchWorker.assertionNode.diagnostics].every((diagnostic, index) => this.#matchExpectedError(diagnostic, targetNodes[index]));
4637
4742
  }
4638
4743
  return {
4639
4744
  explain: () => this.#explain(matchWorker, sourceNode, targetNodes),
@@ -4683,16 +4788,16 @@ class ExpectService {
4683
4788
  this.toHaveProperty = new ToHaveProperty(compiler);
4684
4789
  this.toRaiseError = new ToRaiseError(compiler);
4685
4790
  }
4686
- match(assertion, onDiagnostics) {
4687
- const matcherNameText = assertion.matcherNameNode.name.text;
4688
- if (!argumentOrTypeArgumentIsProvided("source", "Source", assertion.source[0], assertion.node.expression, onDiagnostics)) {
4791
+ match(assertionNode, onDiagnostics) {
4792
+ const matcherNameText = assertionNode.matcherNameNode.name.text;
4793
+ if (!argumentOrTypeArgumentIsProvided("source", "Source", assertionNode.source[0], assertionNode.node.expression, onDiagnostics)) {
4689
4794
  return;
4690
4795
  }
4691
- const matchWorker = new MatchWorker(this.#compiler, this.#typeChecker, assertion);
4692
- if (!(matcherNameText === "toRaiseError" && assertion.isNot === false) &&
4796
+ const matchWorker = new MatchWorker(this.#compiler, this.#typeChecker, assertionNode);
4797
+ if (!(matcherNameText === "toRaiseError" && assertionNode.isNot === false) &&
4693
4798
  this.#reject.argumentType([
4694
- ["source", assertion.source[0]],
4695
- ["target", assertion.target?.[0]],
4799
+ ["source", assertionNode.source[0]],
4800
+ ["target", assertionNode.target?.[0]],
4696
4801
  ], onDiagnostics)) {
4697
4802
  return;
4698
4803
  }
@@ -4701,29 +4806,29 @@ class ExpectService {
4701
4806
  case "toBe":
4702
4807
  case "toBeAssignableTo":
4703
4808
  case "toBeAssignableWith":
4704
- if (!argumentOrTypeArgumentIsProvided("target", "Target", assertion.target?.[0], assertion.matcherNameNode.name, onDiagnostics)) {
4809
+ if (!argumentOrTypeArgumentIsProvided("target", "Target", assertionNode.target?.[0], assertionNode.matcherNameNode.name, onDiagnostics)) {
4705
4810
  return;
4706
4811
  }
4707
- return this[matcherNameText].match(matchWorker, assertion.source[0], assertion.target[0], onDiagnostics);
4812
+ return this[matcherNameText].match(matchWorker, assertionNode.source[0], assertionNode.target[0], onDiagnostics);
4708
4813
  case "toBeApplicable":
4709
- return this.toBeApplicable.match(matchWorker, assertion.source[0], onDiagnostics);
4814
+ return this.toBeApplicable.match(matchWorker, assertionNode.source[0], onDiagnostics);
4710
4815
  case "toBeCallableWith":
4711
4816
  case "toBeConstructableWith":
4712
4817
  case "toRaiseError":
4713
- return this[matcherNameText].match(matchWorker, assertion.source[0], assertion.target, onDiagnostics);
4818
+ return this[matcherNameText].match(matchWorker, assertionNode.source[0], assertionNode.target, onDiagnostics);
4714
4819
  case "toHaveProperty":
4715
- if (!argumentIsProvided("key", assertion.target?.[0], assertion.matcherNameNode.name, onDiagnostics)) {
4820
+ if (!argumentIsProvided("key", assertionNode.target?.[0], assertionNode.matcherNameNode.name, onDiagnostics)) {
4716
4821
  return;
4717
4822
  }
4718
- return this.toHaveProperty.match(matchWorker, assertion.source[0], assertion.target[0], onDiagnostics);
4823
+ return this.toHaveProperty.match(matchWorker, assertionNode.source[0], assertionNode.target[0], onDiagnostics);
4719
4824
  default:
4720
- this.#onMatcherIsNotSupported(matcherNameText, assertion, onDiagnostics);
4825
+ this.#onMatcherIsNotSupported(matcherNameText, assertionNode, onDiagnostics);
4721
4826
  }
4722
4827
  return;
4723
4828
  }
4724
- #onMatcherIsNotSupported(matcherNameText, assertion, onDiagnostics) {
4829
+ #onMatcherIsNotSupported(matcherNameText, assertionNode, onDiagnostics) {
4725
4830
  const text = ExpectDiagnosticText.matcherIsNotSupported(matcherNameText);
4726
- const origin = DiagnosticOrigin.fromNode(assertion.matcherNameNode.name);
4831
+ const origin = DiagnosticOrigin.fromNode(assertionNode.matcherNameNode.name);
4727
4832
  onDiagnostics(Diagnostic.error(text, origin));
4728
4833
  }
4729
4834
  }
@@ -4844,82 +4949,145 @@ class WhenService {
4844
4949
  }
4845
4950
  }
4846
4951
 
4952
+ class FixmeDiagnosticText {
4953
+ static considerRemoving() {
4954
+ return "Consider removing the '// @tstyche fixme' directive.";
4955
+ }
4956
+ static wasSupposedToFail(target) {
4957
+ return `The '${target}()' was supposed to fail, but it passed.`;
4958
+ }
4959
+ }
4960
+
4961
+ class FixmeService {
4962
+ static #expectRange;
4963
+ static #range;
4964
+ static async start(directive, owner) {
4965
+ const inlineConfig = await Directive.getInlineConfig(directive);
4966
+ if (inlineConfig?.fixme === true) {
4967
+ if (owner.brand === "expect") {
4968
+ FixmeService.#expectRange = { directive, owner, previous: FixmeService.#expectRange };
4969
+ }
4970
+ else {
4971
+ FixmeService.#range = { directive, owner, previous: FixmeService.#range };
4972
+ }
4973
+ }
4974
+ }
4975
+ static isFixme(owner, isPass) {
4976
+ if (owner.brand !== "expect") {
4977
+ return owner === FixmeService.#range?.owner && FixmeService.#range.isFail === true;
4978
+ }
4979
+ if (owner === FixmeService.#expectRange?.owner) {
4980
+ FixmeService.#expectRange.isFail = !isPass;
4981
+ return !isPass;
4982
+ }
4983
+ if (FixmeService.#range != null) {
4984
+ if (!isPass) {
4985
+ FixmeService.#range.isFail = true;
4986
+ return true;
4987
+ }
4988
+ FixmeService.#range.isFail ??= false;
4989
+ }
4990
+ return false;
4991
+ }
4992
+ static end(directive, owner, onFileDiagnostics) {
4993
+ let isFail;
4994
+ if (owner === FixmeService.#expectRange?.owner) {
4995
+ isFail = FixmeService.#expectRange.isFail;
4996
+ FixmeService.#expectRange = FixmeService.#expectRange?.previous;
4997
+ }
4998
+ if (owner === FixmeService.#range?.owner) {
4999
+ isFail = FixmeService.#range?.isFail;
5000
+ FixmeService.#range = FixmeService.#range?.previous;
5001
+ }
5002
+ if (isFail === false) {
5003
+ const targetText = owner.node.expression.getText();
5004
+ const text = [FixmeDiagnosticText.wasSupposedToFail(targetText), FixmeDiagnosticText.considerRemoving()];
5005
+ const origin = new DiagnosticOrigin(directive.namespace.start, directive.directive.end, directive.sourceFile);
5006
+ onFileDiagnostics([Diagnostic.error(text, origin)]);
5007
+ }
5008
+ }
5009
+ }
5010
+
4847
5011
  class TestTreeWalker {
4848
5012
  #cancellationToken;
4849
5013
  #compiler;
4850
5014
  #expectService;
4851
5015
  #hasOnly;
4852
- #onTaskDiagnostics;
5016
+ #onFileDiagnostics;
4853
5017
  #position;
4854
5018
  #resolvedConfig;
4855
5019
  #whenService;
4856
- constructor(compiler, typeChecker, resolvedConfig, onTaskDiagnostics, options) {
5020
+ constructor(compiler, typeChecker, resolvedConfig, onFileDiagnostics, options) {
4857
5021
  this.#compiler = compiler;
4858
5022
  this.#resolvedConfig = resolvedConfig;
4859
- this.#onTaskDiagnostics = onTaskDiagnostics;
5023
+ this.#onFileDiagnostics = onFileDiagnostics;
4860
5024
  this.#cancellationToken = options.cancellationToken;
4861
5025
  this.#hasOnly = options.hasOnly || resolvedConfig.only != null || options.position != null;
4862
5026
  this.#position = options.position;
4863
5027
  const reject = new Reject(compiler, typeChecker, resolvedConfig);
4864
5028
  this.#expectService = new ExpectService(compiler, typeChecker, reject);
4865
- this.#whenService = new WhenService(reject, onTaskDiagnostics);
5029
+ this.#whenService = new WhenService(reject, onFileDiagnostics);
4866
5030
  }
4867
- async #resolveRunMode(mode, node) {
4868
- const directiveRanges = node.getDirectiveRanges(this.#compiler);
4869
- const inlineConfig = await Directive.getInlineConfig(directiveRanges);
5031
+ async #resolveRunMode(flags, node) {
5032
+ const ifDirective = Directive.getDirectiveRange(this.#compiler, node, "if");
5033
+ const inlineConfig = await Directive.getInlineConfig(ifDirective);
4870
5034
  if (inlineConfig?.if?.target != null && !Version.isIncluded(this.#compiler.version, inlineConfig.if.target)) {
4871
- mode |= RunMode.Void;
5035
+ flags |= 8;
4872
5036
  }
4873
- if (node.flags & TestTreeNodeFlags.Fail) {
4874
- mode |= RunMode.Fail;
4875
- }
4876
- if (node.flags & TestTreeNodeFlags.Only ||
5037
+ if (node.flags & 1 ||
4877
5038
  (this.#resolvedConfig.only != null && node.name.toLowerCase().includes(this.#resolvedConfig.only.toLowerCase()))) {
4878
- mode |= RunMode.Only;
5039
+ flags |= 1;
4879
5040
  }
4880
- if (node.flags & TestTreeNodeFlags.Skip ||
5041
+ if (node.flags & 2 ||
4881
5042
  (this.#resolvedConfig.skip != null && node.name.toLowerCase().includes(this.#resolvedConfig.skip.toLowerCase()))) {
4882
- mode |= RunMode.Skip;
5043
+ flags |= 2;
4883
5044
  }
4884
- if (node.flags & TestTreeNodeFlags.Todo) {
4885
- mode |= RunMode.Todo;
5045
+ if (node.flags & 4) {
5046
+ flags |= 4;
4886
5047
  }
4887
5048
  if (this.#position != null && node.node.getStart() === this.#position) {
4888
- mode |= RunMode.Only;
4889
- mode &= ~RunMode.Skip;
5049
+ flags |= 1;
5050
+ flags &= -3;
4890
5051
  }
4891
- return mode;
5052
+ return flags;
4892
5053
  }
4893
- async visit(nodes, runMode, parentResult) {
5054
+ async visit(nodes, runModeFlags, parentResult) {
4894
5055
  for (const node of nodes) {
4895
5056
  if (this.#cancellationToken?.isCancellationRequested) {
4896
5057
  break;
4897
5058
  }
5059
+ const fixmeDirective = Directive.getDirectiveRange(this.#compiler, node, "fixme");
5060
+ if (fixmeDirective) {
5061
+ FixmeService.start(fixmeDirective, node);
5062
+ }
4898
5063
  switch (node.brand) {
4899
- case TestTreeNodeBrand.Describe:
4900
- await this.#visitDescribe(node, runMode, parentResult);
5064
+ case "describe":
5065
+ await this.#visitDescribe(node, runModeFlags, parentResult);
4901
5066
  break;
4902
- case TestTreeNodeBrand.Test:
4903
- await this.#visitTest(node, runMode, parentResult);
5067
+ case "test":
5068
+ await this.#visitTest(node, runModeFlags, parentResult);
4904
5069
  break;
4905
- case TestTreeNodeBrand.Expect:
4906
- await this.#visitAssertion(node, runMode, parentResult);
5070
+ case "expect":
5071
+ await this.#visitExpect(node, runModeFlags, parentResult);
4907
5072
  break;
4908
- case TestTreeNodeBrand.When:
5073
+ case "when":
4909
5074
  this.#visitWhen(node);
4910
5075
  break;
4911
5076
  }
5077
+ if (fixmeDirective) {
5078
+ FixmeService.end(fixmeDirective, node, this.#onFileDiagnostics);
5079
+ }
4912
5080
  }
4913
5081
  }
4914
- async #visitAssertion(assertion, runMode, parentResult) {
4915
- await this.visit(assertion.children, runMode, parentResult);
4916
- runMode = await this.#resolveRunMode(runMode, assertion);
4917
- if (runMode & RunMode.Void) {
5082
+ async #visitExpect(expect, runModeFlags, parentResult) {
5083
+ await this.visit(expect.children, runModeFlags, parentResult);
5084
+ runModeFlags = await this.#resolveRunMode(runModeFlags, expect);
5085
+ if (runModeFlags & 8) {
4918
5086
  return;
4919
5087
  }
4920
- const expectResult = new ExpectResult(assertion, parentResult);
5088
+ const expectResult = new ExpectResult(expect, parentResult);
4921
5089
  EventEmitter.dispatch(["expect:start", { result: expectResult }]);
4922
- if (runMode & RunMode.Skip || (this.#hasOnly && !(runMode & RunMode.Only))) {
5090
+ if (runModeFlags & 2 || (this.#hasOnly && !(runModeFlags & 1))) {
4923
5091
  EventEmitter.dispatch(["expect:skip", { result: expectResult }]);
4924
5092
  return;
4925
5093
  }
@@ -4929,59 +5097,57 @@ class TestTreeWalker {
4929
5097
  { diagnostics: Array.isArray(diagnostics) ? diagnostics : [diagnostics], result: expectResult },
4930
5098
  ]);
4931
5099
  };
4932
- if (assertion.diagnostics.size > 0 && assertion.matcherNameNode.name.text !== "toRaiseError") {
4933
- onExpectDiagnostics(Diagnostic.fromDiagnostics([...assertion.diagnostics]));
5100
+ if (expect.diagnostics.size > 0 && expect.matcherNameNode.name.text !== "toRaiseError") {
5101
+ onExpectDiagnostics(Diagnostic.fromDiagnostics([...expect.diagnostics]));
4934
5102
  return;
4935
5103
  }
4936
- const matchResult = this.#expectService.match(assertion, onExpectDiagnostics);
5104
+ const matchResult = this.#expectService.match(expect, onExpectDiagnostics);
4937
5105
  if (!matchResult) {
4938
5106
  return;
4939
5107
  }
4940
- if (assertion.isNot ? !matchResult.isMatch : matchResult.isMatch) {
4941
- if (runMode & RunMode.Fail) {
4942
- const text = ["The assertion was supposed to fail, but it passed.", "Consider removing the '.fail' flag."];
4943
- const origin = DiagnosticOrigin.fromNode(assertion.node.expression.name);
4944
- onExpectDiagnostics(Diagnostic.error(text, origin));
4945
- }
4946
- else {
4947
- EventEmitter.dispatch(["expect:pass", { result: expectResult }]);
4948
- }
5108
+ const isPass = expect.isNot ? !matchResult.isMatch : matchResult.isMatch;
5109
+ if (FixmeService.isFixme(expect, isPass)) {
5110
+ EventEmitter.dispatch(["expect:fixme", { result: expectResult }]);
5111
+ return;
4949
5112
  }
4950
- else if (runMode & RunMode.Fail) {
5113
+ if (isPass) {
4951
5114
  EventEmitter.dispatch(["expect:pass", { result: expectResult }]);
4952
5115
  }
4953
5116
  else {
4954
5117
  EventEmitter.dispatch(["expect:fail", { diagnostics: matchResult.explain(), result: expectResult }]);
4955
5118
  }
4956
5119
  }
4957
- async #visitDescribe(describe, runMode, parentResult) {
4958
- runMode = await this.#resolveRunMode(runMode, describe);
4959
- if (runMode & RunMode.Void) {
5120
+ async #visitDescribe(describe, runModeFlags, parentResult) {
5121
+ runModeFlags = await this.#resolveRunMode(runModeFlags, describe);
5122
+ if (runModeFlags & 8) {
4960
5123
  return;
4961
5124
  }
4962
5125
  const describeResult = new DescribeResult(describe, parentResult);
4963
5126
  EventEmitter.dispatch(["describe:start", { result: describeResult }]);
4964
- if (!(runMode & RunMode.Skip || (this.#hasOnly && !(runMode & RunMode.Only)) || runMode & RunMode.Todo) &&
5127
+ if (!(runModeFlags & 2 ||
5128
+ (this.#hasOnly && !(runModeFlags & 1)) ||
5129
+ runModeFlags & 4) &&
4965
5130
  describe.diagnostics.size > 0) {
4966
- this.#onTaskDiagnostics(Diagnostic.fromDiagnostics([...describe.diagnostics]));
5131
+ this.#onFileDiagnostics(Diagnostic.fromDiagnostics([...describe.diagnostics]));
4967
5132
  }
4968
5133
  else {
4969
- await this.visit(describe.children, runMode, describeResult);
5134
+ await this.visit(describe.children, runModeFlags, describeResult);
4970
5135
  }
4971
5136
  EventEmitter.dispatch(["describe:end", { result: describeResult }]);
4972
5137
  }
4973
- async #visitTest(test, runMode, parentResult) {
4974
- runMode = await this.#resolveRunMode(runMode, test);
4975
- if (runMode & RunMode.Void) {
5138
+ async #visitTest(test, runModeFlags, parentResult) {
5139
+ runModeFlags = await this.#resolveRunMode(runModeFlags, test);
5140
+ if (runModeFlags & 8) {
4976
5141
  return;
4977
5142
  }
4978
5143
  const testResult = new TestResult(test, parentResult);
4979
5144
  EventEmitter.dispatch(["test:start", { result: testResult }]);
4980
- if (runMode & RunMode.Todo) {
5145
+ if (runModeFlags & 4) {
4981
5146
  EventEmitter.dispatch(["test:todo", { result: testResult }]);
4982
5147
  return;
4983
5148
  }
4984
- if (!(runMode & RunMode.Skip || (this.#hasOnly && !(runMode & RunMode.Only))) && test.diagnostics.size > 0) {
5149
+ if (!(runModeFlags & 2 || (this.#hasOnly && !(runModeFlags & 1))) &&
5150
+ test.diagnostics.size > 0) {
4985
5151
  EventEmitter.dispatch([
4986
5152
  "test:error",
4987
5153
  {
@@ -4991,24 +5157,29 @@ class TestTreeWalker {
4991
5157
  ]);
4992
5158
  return;
4993
5159
  }
4994
- await this.visit(test.children, runMode, testResult);
4995
- if (runMode & RunMode.Skip || (this.#hasOnly && !(runMode & RunMode.Only))) {
5160
+ await this.visit(test.children, runModeFlags, testResult);
5161
+ if (runModeFlags & 2 || (this.#hasOnly && !(runModeFlags & 1))) {
4996
5162
  EventEmitter.dispatch(["test:skip", { result: testResult }]);
4997
5163
  return;
4998
5164
  }
4999
- if (testResult.expectCount.failed > 0) {
5000
- EventEmitter.dispatch(["test:fail", { result: testResult }]);
5165
+ const isPass = testResult.expectCount.failed === 0;
5166
+ if (FixmeService.isFixme(test, isPass)) {
5167
+ EventEmitter.dispatch(["test:fixme", { result: testResult }]);
5168
+ return;
5001
5169
  }
5002
- else {
5170
+ if (isPass) {
5003
5171
  EventEmitter.dispatch(["test:pass", { result: testResult }]);
5004
5172
  }
5173
+ else {
5174
+ EventEmitter.dispatch(["test:fail", { result: testResult }]);
5175
+ }
5005
5176
  }
5006
5177
  #visitWhen(when) {
5007
5178
  this.#whenService.action(when);
5008
5179
  }
5009
5180
  }
5010
5181
 
5011
- class TaskRunner {
5182
+ class FileRunner {
5012
5183
  #collectService;
5013
5184
  #compiler;
5014
5185
  #projectService;
@@ -5021,89 +5192,87 @@ class TaskRunner {
5021
5192
  this.#collectService = new CollectService(compiler, this.#projectService, this.#resolvedConfig);
5022
5193
  }
5023
5194
  #onDiagnostics(diagnostics, result) {
5024
- EventEmitter.dispatch(["task:error", { diagnostics, result }]);
5195
+ EventEmitter.dispatch(["file:error", { diagnostics, result }]);
5025
5196
  }
5026
- async run(task, cancellationToken) {
5197
+ async run(file, cancellationToken) {
5027
5198
  if (cancellationToken.isCancellationRequested) {
5028
5199
  return;
5029
5200
  }
5030
- this.#projectService.openFile(task.filePath, undefined, this.#resolvedConfig.rootPath);
5031
- const taskResult = new TaskResult(task);
5032
- EventEmitter.dispatch(["task:start", { result: taskResult }]);
5033
- await this.#run(task, taskResult, cancellationToken);
5034
- EventEmitter.dispatch(["task:end", { result: taskResult }]);
5035
- this.#projectService.closeFile(task.filePath);
5201
+ this.#projectService.openFile(file.path, undefined, this.#resolvedConfig.rootPath);
5202
+ const fileResult = new FileResult(file);
5203
+ EventEmitter.dispatch(["file:start", { result: fileResult }]);
5204
+ await this.#run(file, fileResult, cancellationToken);
5205
+ EventEmitter.dispatch(["file:end", { result: fileResult }]);
5206
+ this.#projectService.closeFile(file.path);
5036
5207
  }
5037
- async #resolveTaskFacts(task, taskResult, runMode = RunMode.Normal) {
5038
- const languageService = this.#projectService.getLanguageService(task.filePath);
5039
- const syntacticDiagnostics = languageService?.getSyntacticDiagnostics(task.filePath);
5208
+ async #resolveFileFacts(file, fileResult, runModeFlags) {
5209
+ const languageService = this.#projectService.getLanguageService(file.path);
5210
+ const syntacticDiagnostics = languageService?.getSyntacticDiagnostics(file.path);
5040
5211
  if (syntacticDiagnostics != null && syntacticDiagnostics.length > 0) {
5041
- this.#onDiagnostics(Diagnostic.fromDiagnostics(syntacticDiagnostics), taskResult);
5212
+ this.#onDiagnostics(Diagnostic.fromDiagnostics(syntacticDiagnostics), fileResult);
5042
5213
  return;
5043
5214
  }
5044
- const semanticDiagnostics = languageService?.getSemanticDiagnostics(task.filePath);
5215
+ const semanticDiagnostics = languageService?.getSemanticDiagnostics(file.path);
5045
5216
  const program = languageService?.getProgram();
5046
5217
  const typeChecker = program?.getTypeChecker();
5047
- const sourceFile = program?.getSourceFile(task.filePath);
5218
+ const sourceFile = program?.getSourceFile(file.path);
5048
5219
  if (!sourceFile) {
5049
5220
  return;
5050
5221
  }
5051
- const testTree = this.#collectService.createTestTree(sourceFile, semanticDiagnostics);
5052
- const directiveRanges = testTree.getDirectiveRanges(this.#compiler);
5222
+ const directiveRanges = Directive.getDirectiveRanges(this.#compiler, sourceFile);
5053
5223
  const inlineConfig = await Directive.getInlineConfig(directiveRanges);
5054
5224
  if (inlineConfig?.if?.target != null && !Version.isIncluded(this.#compiler.version, inlineConfig.if.target)) {
5055
- runMode |= RunMode.Skip;
5056
- }
5057
- if (testTree.suppressedErrors != null) {
5058
- this.#suppressedService.match(testTree.suppressedErrors, (diagnostics) => {
5059
- this.#onDiagnostics(diagnostics, taskResult);
5060
- });
5225
+ runModeFlags |= 2;
5061
5226
  }
5062
5227
  if (inlineConfig?.template) {
5063
5228
  if (semanticDiagnostics != null && semanticDiagnostics.length > 0) {
5064
- this.#onDiagnostics(Diagnostic.fromDiagnostics(semanticDiagnostics), taskResult);
5229
+ this.#onDiagnostics(Diagnostic.fromDiagnostics(semanticDiagnostics), fileResult);
5065
5230
  return;
5066
5231
  }
5067
- const moduleSpecifier = pathToFileURL(task.filePath).toString();
5232
+ const moduleSpecifier = pathToFileURL(file.path).toString();
5068
5233
  const testText = (await import(moduleSpecifier))?.default;
5069
5234
  if (typeof testText !== "string") {
5070
- this.#onDiagnostics([Diagnostic.error("A template test file must export a string.")], taskResult);
5235
+ this.#onDiagnostics([Diagnostic.error("A template test file must export a string.")], fileResult);
5071
5236
  return;
5072
5237
  }
5073
- this.#projectService.openFile(task.filePath, testText, this.#resolvedConfig.rootPath);
5074
- return this.#resolveTaskFacts(task, taskResult, runMode);
5238
+ this.#projectService.openFile(file.path, testText, this.#resolvedConfig.rootPath);
5239
+ return this.#resolveFileFacts(file, fileResult, runModeFlags);
5075
5240
  }
5076
- return { runMode, testTree, typeChecker };
5241
+ const testTree = this.#collectService.createTestTree(sourceFile, semanticDiagnostics);
5242
+ this.#suppressedService.match(testTree, (diagnostics) => {
5243
+ this.#onDiagnostics(diagnostics, fileResult);
5244
+ });
5245
+ return { runModeFlags, testTree, typeChecker };
5077
5246
  }
5078
- async #run(task, taskResult, cancellationToken) {
5079
- if (!existsSync(task.filePath)) {
5080
- this.#onDiagnostics([Diagnostic.error(`Test file '${task.filePath}' does not exist.`)], taskResult);
5247
+ async #run(file, fileResult, cancellationToken) {
5248
+ if (!existsSync(file.path)) {
5249
+ this.#onDiagnostics([Diagnostic.error(`Test file '${file.path}' does not exist.`)], fileResult);
5081
5250
  return;
5082
5251
  }
5083
- const facts = await this.#resolveTaskFacts(task, taskResult);
5252
+ const facts = await this.#resolveFileFacts(file, fileResult, 0);
5084
5253
  if (!facts) {
5085
5254
  return;
5086
5255
  }
5087
5256
  if (facts.testTree.diagnostics.size > 0) {
5088
- this.#onDiagnostics(Diagnostic.fromDiagnostics([...facts.testTree.diagnostics]), taskResult);
5257
+ this.#onDiagnostics(Diagnostic.fromDiagnostics([...facts.testTree.diagnostics]), fileResult);
5089
5258
  return;
5090
5259
  }
5091
- const onTaskDiagnostics = (diagnostics) => {
5092
- this.#onDiagnostics(diagnostics, taskResult);
5260
+ const onFileDiagnostics = (diagnostics) => {
5261
+ this.#onDiagnostics(diagnostics, fileResult);
5093
5262
  };
5094
- const testTreeWalker = new TestTreeWalker(this.#compiler, facts.typeChecker, this.#resolvedConfig, onTaskDiagnostics, {
5263
+ const testTreeWalker = new TestTreeWalker(this.#compiler, facts.typeChecker, this.#resolvedConfig, onFileDiagnostics, {
5095
5264
  cancellationToken,
5096
5265
  hasOnly: facts.testTree.hasOnly,
5097
- position: task.position,
5266
+ position: file.position,
5098
5267
  });
5099
- await testTreeWalker.visit(facts.testTree.children, facts.runMode, undefined);
5268
+ await testTreeWalker.visit(facts.testTree.children, facts.runModeFlags, undefined);
5100
5269
  }
5101
5270
  }
5102
5271
 
5103
5272
  class Runner {
5104
5273
  #eventEmitter = new EventEmitter();
5105
5274
  #resolvedConfig;
5106
- static version = "4.3.0";
5275
+ static version = "5.0.0-beta.0";
5107
5276
  constructor(resolvedConfig) {
5108
5277
  this.#resolvedConfig = resolvedConfig;
5109
5278
  }
@@ -5111,7 +5280,7 @@ class Runner {
5111
5280
  const resultHandler = new ResultHandler();
5112
5281
  this.#eventEmitter.addHandler(resultHandler);
5113
5282
  if (this.#resolvedConfig.failFast) {
5114
- const cancellationHandler = new CancellationHandler(cancellationToken, CancellationReason.FailFast);
5283
+ const cancellationHandler = new CancellationHandler(cancellationToken, "failFast");
5115
5284
  this.#eventEmitter.addHandler(cancellationHandler);
5116
5285
  }
5117
5286
  }
@@ -5140,39 +5309,39 @@ class Runner {
5140
5309
  }
5141
5310
  }
5142
5311
  }
5143
- async run(testFiles, cancellationToken = new CancellationToken()) {
5144
- const tasks = testFiles.map((testFile) => (testFile instanceof Task ? testFile : new Task(testFile)));
5312
+ async run(files, cancellationToken = new CancellationToken()) {
5313
+ const fileLocations = files.map((file) => (file instanceof FileLocation ? file : new FileLocation(file)));
5145
5314
  this.#addHandlers(cancellationToken);
5146
5315
  await this.#addReporters();
5147
- await this.#run(tasks, cancellationToken);
5316
+ await this.#run(fileLocations, cancellationToken);
5148
5317
  if (this.#resolvedConfig.watch) {
5149
- await this.#watch(tasks, cancellationToken);
5318
+ await this.#watch(fileLocations, cancellationToken);
5150
5319
  }
5151
5320
  this.#eventEmitter.removeReporters();
5152
5321
  this.#eventEmitter.removeHandlers();
5153
5322
  }
5154
- async #run(tasks, cancellationToken) {
5155
- const result = new Result(tasks);
5323
+ async #run(files, cancellationToken) {
5324
+ const result = new Result(files);
5156
5325
  EventEmitter.dispatch(["run:start", { result }]);
5157
5326
  for (const target of this.#resolvedConfig.target) {
5158
- const targetResult = new TargetResult(target, tasks);
5327
+ const targetResult = new TargetResult(target, files);
5159
5328
  EventEmitter.dispatch(["target:start", { result: targetResult }]);
5160
5329
  const compiler = await Store.load(target);
5161
5330
  if (compiler) {
5162
- const taskRunner = new TaskRunner(compiler, this.#resolvedConfig);
5163
- for (const task of tasks) {
5164
- await taskRunner.run(task, cancellationToken);
5331
+ const fileRunner = new FileRunner(compiler, this.#resolvedConfig);
5332
+ for (const file of files) {
5333
+ await fileRunner.run(file, cancellationToken);
5165
5334
  }
5166
5335
  }
5167
5336
  EventEmitter.dispatch(["target:end", { result: targetResult }]);
5168
5337
  }
5169
5338
  EventEmitter.dispatch(["run:end", { result }]);
5170
- if (cancellationToken.reason === CancellationReason.FailFast) {
5339
+ if (cancellationToken.reason === "failFast") {
5171
5340
  cancellationToken.reset();
5172
5341
  }
5173
5342
  }
5174
- async #watch(testFiles, cancellationToken) {
5175
- const watchService = new WatchService(this.#resolvedConfig, testFiles);
5343
+ async #watch(files, cancellationToken) {
5344
+ const watchService = new WatchService(this.#resolvedConfig, files);
5176
5345
  for await (const testFiles of watchService.watch(cancellationToken)) {
5177
5346
  await this.#run(testFiles, cancellationToken);
5178
5347
  }
@@ -5182,14 +5351,14 @@ class Runner {
5182
5351
  class Cli {
5183
5352
  #eventEmitter = new EventEmitter();
5184
5353
  async run(commandLine, cancellationToken = new CancellationToken()) {
5185
- const cancellationHandler = new CancellationHandler(cancellationToken, CancellationReason.ConfigError);
5354
+ const cancellationHandler = new CancellationHandler(cancellationToken, "configError");
5186
5355
  this.#eventEmitter.addHandler(cancellationHandler);
5187
5356
  const exitCodeHandler = new ExitCodeHandler();
5188
5357
  this.#eventEmitter.addHandler(exitCodeHandler);
5189
5358
  const setupReporter = new SetupReporter();
5190
5359
  this.#eventEmitter.addReporter(setupReporter);
5191
5360
  if (commandLine.includes("--help")) {
5192
- const options = Options.for(OptionGroup.CommandLine);
5361
+ const options = Options.for(2);
5193
5362
  OutputService.writeMessage(helpText(options, Runner.version));
5194
5363
  return;
5195
5364
  }
@@ -5217,7 +5386,7 @@ class Cli {
5217
5386
  return;
5218
5387
  }
5219
5388
  do {
5220
- if (cancellationToken.reason === CancellationReason.ConfigChange) {
5389
+ if (cancellationToken.reason === "configChange") {
5221
5390
  cancellationToken.reset();
5222
5391
  exitCodeHandler.resetCode();
5223
5392
  OutputService.clearTerminal();
@@ -5272,7 +5441,7 @@ class Cli {
5272
5441
  const runner = new Runner(resolvedConfig);
5273
5442
  await runner.run(testFiles, cancellationToken);
5274
5443
  PluginService.removeHandlers();
5275
- } while (cancellationToken.reason === CancellationReason.ConfigChange);
5444
+ } while (cancellationToken.reason === "configChange");
5276
5445
  this.#eventEmitter.removeHandlers();
5277
5446
  }
5278
5447
  #waitForChangedFiles(resolvedConfig, cancellationToken) {
@@ -5281,7 +5450,7 @@ class Cli {
5281
5450
  cancellationToken.reset();
5282
5451
  OutputService.writeMessage(waitingForFileChangesText());
5283
5452
  const onChanged = () => {
5284
- cancellationToken.cancel(CancellationReason.ConfigChange);
5453
+ cancellationToken.cancel("configChange");
5285
5454
  for (const watcher of watchers) {
5286
5455
  watcher.close();
5287
5456
  }
@@ -5303,4 +5472,4 @@ class Cli {
5303
5472
  }
5304
5473
  }
5305
5474
 
5306
- export { AssertionNode, BaseReporter, CancellationHandler, CancellationReason, CancellationToken, Cli, CollectService, Color, Config, ConfigDiagnosticText, DescribeResult, Diagnostic, DiagnosticCategory, DiagnosticOrigin, Directive, EventEmitter, ExitCodeHandler, ExpectResult, ExpectService, FileWatcher, Glob, InputService, Line, ListReporter, OptionBrand, OptionGroup, Options, OutputService, Path, PluginService, ProjectResult, ProjectService, Reject, Result, ResultCount, ResultHandler, ResultStatus, ResultTiming, Runner, Scribbler, Select, SelectDiagnosticText, SetupReporter, SourceFile, Store, SummaryReporter, SuppressedService, TargetResult, Task, TaskResult, TestResult, TestTree, TestTreeNode, TestTreeNodeBrand, TestTreeNodeFlags, Text, Version, WatchReporter, WatchService, Watcher, WhenNode, WhenService, addsPackageText, argumentIsProvided, argumentOrTypeArgumentIsProvided, defaultOptions, describeNameText, diagnosticBelongsToNode, diagnosticText, environmentOptions, fileViewText, formattedText, getDiagnosticMessageText, getTextSpanEnd, helpText, isDiagnosticWithLocation, nodeBelongsToArgumentList, summaryText, taskStatusText, testNameText, usesCompilerText, waitingForFileChangesText, watchUsageText };
5475
+ export { BaseReporter, CancellationHandler, CancellationReason, CancellationToken, Cli, CollectService, Color, Config, ConfigDiagnosticText, DescribeResult, Diagnostic, DiagnosticCategory, DiagnosticOrigin, Directive, EventEmitter, ExitCodeHandler, ExpectNode, ExpectResult, ExpectService, FileLocation, FileResult, FileWatcher, Glob, InputService, Line, ListReporter, OptionBrand, OptionGroup, Options, OutputService, Path, PluginService, ProjectResult, ProjectService, Reject, Result, ResultCount, ResultHandler, ResultStatus, ResultTiming, Runner, Scribbler, Select, SelectDiagnosticText, SetupReporter, SourceFile, SourceService, Store, SummaryReporter, SuppressedService, TargetResult, TestResult, TestTree, TestTreeNode, TestTreeNodeBrand, TestTreeNodeFlags, Text, Version, WatchReporter, WatchService, Watcher, WhenNode, WhenService, addsPackageText, argumentIsProvided, argumentOrTypeArgumentIsProvided, defaultOptions, describeNameText, diagnosticBelongsToNode, diagnosticText, environmentOptions, fileStatusText, fileViewText, formattedText, getDiagnosticMessageText, getTextSpanEnd, helpText, isDiagnosticWithLocation, nodeBelongsToArgumentList, summaryText, testNameText, usesCompilerText, waitingForFileChangesText, watchUsageText };