tstyche 4.2.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) {
@@ -225,80 +238,51 @@ class ConfigDiagnosticText {
225
238
  static fileDoesNotExist(filePath) {
226
239
  return `The specified path '${filePath}' does not exist.`;
227
240
  }
241
+ static fileMatchPatternCannotStartWith(optionName, segment) {
242
+ return [
243
+ `A '${optionName}' pattern cannot start with '${segment}'.`,
244
+ "The files are only collected within the 'rootPath' directory.",
245
+ ];
246
+ }
228
247
  static inspectSupportedVersions() {
229
248
  return "Use the '--list' command line option to inspect the list of supported versions.";
230
249
  }
231
250
  static moduleWasNotFound(specifier) {
232
251
  return `The specified module '${specifier}' was not found.`;
233
252
  }
253
+ static optionValueMustBe(optionName, optionBrand) {
254
+ return `Value for the '${optionName}' option must be a ${optionBrand}.`;
255
+ }
234
256
  static rangeIsNotValid(value) {
235
257
  return `The specified range '${value}' is not valid.`;
236
258
  }
259
+ static rangeDoesNotMatchSupported(value) {
260
+ return `The specified range '${value}' does not match any supported TypeScript versions.`;
261
+ }
237
262
  static rangeUsage() {
238
263
  return [
239
- "A range must be specified using an operator and a minor version.",
240
- "To set an upper bound, the intersection of two ranges can be used.",
241
- "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'.",
242
267
  ];
243
268
  }
244
- static requiresValueType(optionName, optionBrand) {
245
- return `Option '${optionName}' requires a value of type ${optionBrand}.`;
246
- }
247
269
  static seen(element) {
248
270
  return `The ${element} was seen here.`;
249
271
  }
250
- static testFileMatchCannotStartWith(segment) {
251
- return [
252
- `A test file match pattern cannot start with '${segment}'.`,
253
- "The test files are only collected within the 'rootPath' directory.",
254
- ];
255
- }
256
272
  static unexpected(element) {
257
273
  return `Unexpected ${element}.`;
258
274
  }
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
690
  }
691
- return TarReader.#textDecoder.decode(view);
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
+ }
703
+ }
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,168 +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.BareTrue,
958
+ brand: "list",
959
+ description: "The list of glob patterns matching the fixture files.",
960
+ group: 4,
961
+ items: {
962
+ brand: "string",
963
+ name: "fixtureFileMatch",
964
+ },
965
+ name: "fixtureFileMatch",
966
+ },
967
+ {
968
+ brand: "true",
939
969
  description: "Print the list of command line options with brief descriptions and exit.",
940
- group: OptionGroup.CommandLine,
970
+ group: 2,
941
971
  name: "help",
942
972
  },
943
973
  {
944
- brand: OptionBrand.BareTrue,
974
+ brand: "true",
945
975
  description: "Print the list of supported versions of the 'typescript' package and exit.",
946
- group: OptionGroup.CommandLine,
976
+ group: 2,
947
977
  name: "list",
948
978
  },
949
979
  {
950
- brand: OptionBrand.BareTrue,
980
+ brand: "true",
951
981
  description: "Print the list of the selected test files and exit.",
952
- group: OptionGroup.CommandLine,
982
+ group: 2,
953
983
  name: "listFiles",
954
984
  },
955
985
  {
956
- brand: OptionBrand.String,
986
+ brand: "string",
957
987
  description: "Only run tests with matching name.",
958
- group: OptionGroup.CommandLine,
988
+ group: 2,
959
989
  name: "only",
960
990
  },
961
991
  {
962
- brand: OptionBrand.List,
992
+ brand: "list",
963
993
  description: "The list of plugins to use.",
964
- group: OptionGroup.CommandLine | OptionGroup.ConfigFile,
994
+ group: 2 | 4,
965
995
  items: {
966
- brand: OptionBrand.String,
996
+ brand: "string",
967
997
  name: "plugins",
968
998
  },
969
999
  name: "plugins",
970
1000
  },
971
1001
  {
972
- brand: OptionBrand.BareTrue,
1002
+ brand: "true",
973
1003
  description: "Remove all installed versions of the 'typescript' package and exit.",
974
- group: OptionGroup.CommandLine,
1004
+ group: 2,
975
1005
  name: "prune",
976
1006
  },
977
1007
  {
978
- brand: OptionBrand.Boolean,
1008
+ brand: "boolean",
979
1009
  description: "Reject the 'any' type passed as an argument to the 'expect()' function or a matcher.",
980
- group: OptionGroup.ConfigFile,
1010
+ group: 4,
981
1011
  name: "rejectAnyType",
982
1012
  },
983
1013
  {
984
- brand: OptionBrand.Boolean,
1014
+ brand: "boolean",
985
1015
  description: "Reject the 'never' type passed as an argument to the 'expect()' function or a matcher.",
986
- group: OptionGroup.ConfigFile,
1016
+ group: 4,
987
1017
  name: "rejectNeverType",
988
1018
  },
989
1019
  {
990
- brand: OptionBrand.List,
1020
+ brand: "list",
991
1021
  description: "The list of reporters to use.",
992
- group: OptionGroup.CommandLine | OptionGroup.ConfigFile,
1022
+ group: 2 | 4,
993
1023
  items: {
994
- brand: OptionBrand.String,
1024
+ brand: "string",
995
1025
  name: "reporters",
996
1026
  },
997
1027
  name: "reporters",
998
1028
  },
999
1029
  {
1000
- brand: OptionBrand.String,
1030
+ brand: "string",
1001
1031
  description: "The path to a directory containing files of a test project.",
1002
- group: OptionGroup.ConfigFile,
1032
+ group: 4,
1003
1033
  name: "rootPath",
1004
1034
  },
1005
1035
  {
1006
- brand: OptionBrand.BareTrue,
1036
+ brand: "true",
1007
1037
  description: "Print the resolved configuration and exit.",
1008
- group: OptionGroup.CommandLine,
1038
+ group: 2,
1009
1039
  name: "showConfig",
1010
1040
  },
1011
1041
  {
1012
- brand: OptionBrand.String,
1042
+ brand: "string",
1013
1043
  description: "Skip tests with matching name.",
1014
- group: OptionGroup.CommandLine,
1044
+ group: 2,
1015
1045
  name: "skip",
1016
1046
  },
1017
1047
  {
1018
- brand: OptionBrand.List,
1019
- description: "The list of TypeScript versions to be tested on.",
1020
- group: OptionGroup.CommandLine | OptionGroup.ConfigFile | OptionGroup.InlineConditions,
1021
- items: {
1022
- brand: OptionBrand.String,
1023
- name: "target",
1024
- },
1048
+ brand: "range",
1049
+ description: "The range of TypeScript versions to be tested against.",
1050
+ group: 2 | 4 | 8,
1025
1051
  name: "target",
1026
1052
  },
1027
1053
  {
1028
- brand: OptionBrand.List,
1054
+ brand: "list",
1029
1055
  description: "The list of glob patterns matching the test files.",
1030
- group: OptionGroup.ConfigFile,
1056
+ group: 4,
1031
1057
  items: {
1032
- brand: OptionBrand.String,
1058
+ brand: "string",
1033
1059
  name: "testFileMatch",
1034
1060
  },
1035
1061
  name: "testFileMatch",
1036
1062
  },
1037
1063
  {
1038
- brand: OptionBrand.String,
1064
+ brand: "string",
1039
1065
  description: "The look up strategy to be used to find the TSConfig file.",
1040
- group: OptionGroup.CommandLine | OptionGroup.ConfigFile,
1066
+ group: 2 | 4,
1041
1067
  name: "tsconfig",
1042
1068
  },
1043
1069
  {
1044
- brand: OptionBrand.BareTrue,
1070
+ brand: "true",
1045
1071
  description: "Fetch the 'typescript' package metadata from the registry and exit.",
1046
- group: OptionGroup.CommandLine,
1072
+ group: 2,
1047
1073
  name: "update",
1048
1074
  },
1049
1075
  {
1050
- brand: OptionBrand.BareTrue,
1076
+ brand: "true",
1051
1077
  description: "Print the version number and exit.",
1052
- group: OptionGroup.CommandLine,
1078
+ group: 2,
1053
1079
  name: "version",
1054
1080
  },
1055
1081
  {
1056
- brand: OptionBrand.BareTrue,
1082
+ brand: "true",
1057
1083
  description: "Watch for changes and rerun related test files.",
1058
- group: OptionGroup.CommandLine,
1084
+ group: 2,
1059
1085
  name: "watch",
1060
1086
  },
1061
1087
  ];
@@ -1107,7 +1133,7 @@ class Options {
1107
1133
  }
1108
1134
  return optionValue;
1109
1135
  }
1110
- static async validate(optionName, optionValue, optionBrand, onDiagnostics, origin) {
1136
+ static async validate(optionName, optionValue, onDiagnostics, origin) {
1111
1137
  const canonicalOptionName = Options.#getCanonicalOptionName(optionName);
1112
1138
  switch (canonicalOptionName) {
1113
1139
  case "config":
@@ -1134,23 +1160,25 @@ class Options {
1134
1160
  case "target": {
1135
1161
  if (/[<>=]/.test(optionValue)) {
1136
1162
  if (!Target.isRange(optionValue)) {
1137
- onDiagnostics(Diagnostic.error([ConfigDiagnosticText.rangeIsNotValid(optionValue), ...ConfigDiagnosticText.rangeUsage()], origin));
1163
+ const text = [ConfigDiagnosticText.rangeIsNotValid(optionValue), ...ConfigDiagnosticText.rangeUsage()];
1164
+ onDiagnostics(Diagnostic.error(text, origin));
1138
1165
  }
1139
1166
  break;
1140
1167
  }
1141
1168
  if ((await Store.validateTag(optionValue)) === false) {
1142
- onDiagnostics(Diagnostic.error([
1169
+ const text = [
1143
1170
  ConfigDiagnosticText.versionIsNotSupported(optionValue),
1144
- ...ConfigDiagnosticText.usage(optionName, optionBrand),
1145
1171
  ConfigDiagnosticText.inspectSupportedVersions(),
1146
- ], origin));
1172
+ ];
1173
+ onDiagnostics(Diagnostic.error(text, origin));
1147
1174
  }
1148
1175
  break;
1149
1176
  }
1177
+ case "fixtureFileMatch":
1150
1178
  case "testFileMatch":
1151
1179
  for (const segment of ["/", "../"]) {
1152
1180
  if (optionValue.startsWith(segment)) {
1153
- onDiagnostics(Diagnostic.error(ConfigDiagnosticText.testFileMatchCannotStartWith(segment), origin));
1181
+ onDiagnostics(Diagnostic.error(ConfigDiagnosticText.fileMatchPatternCannotStartWith(canonicalOptionName, segment), origin));
1154
1182
  }
1155
1183
  }
1156
1184
  break;
@@ -1163,7 +1191,7 @@ class Options {
1163
1191
  }
1164
1192
  }
1165
1193
 
1166
- class CommandLineParser {
1194
+ class CommandParser {
1167
1195
  #commandLineOptions;
1168
1196
  #onDiagnostics;
1169
1197
  #options;
@@ -1172,12 +1200,12 @@ class CommandLineParser {
1172
1200
  this.#commandLineOptions = commandLine;
1173
1201
  this.#pathMatch = pathMatch;
1174
1202
  this.#onDiagnostics = onDiagnostics;
1175
- this.#options = Options.for(OptionGroup.CommandLine);
1203
+ this.#options = Options.for(2);
1176
1204
  }
1177
1205
  #onExpectsValue(optionName, optionBrand) {
1178
1206
  const text = [
1179
1207
  ConfigDiagnosticText.expectsValue(optionName),
1180
- ...ConfigDiagnosticText.usage(optionName, optionBrand),
1208
+ ConfigDiagnosticText.optionValueMustBe(optionName, optionBrand),
1181
1209
  ];
1182
1210
  this.#onDiagnostics(Diagnostic.error(text));
1183
1211
  }
@@ -1207,18 +1235,18 @@ class CommandLineParser {
1207
1235
  async #parseOptionValue(commandLineArgs, index, optionName, optionDefinition) {
1208
1236
  let optionValue = this.#resolveOptionValue(commandLineArgs[index]);
1209
1237
  switch (optionDefinition.brand) {
1210
- case OptionBrand.BareTrue:
1211
- await Options.validate(optionName, optionValue, optionDefinition.brand, this.#onDiagnostics);
1238
+ case "true":
1239
+ await Options.validate(optionName, optionValue, this.#onDiagnostics);
1212
1240
  this.#commandLineOptions[optionDefinition.name] = true;
1213
1241
  break;
1214
- case OptionBrand.Boolean:
1215
- await Options.validate(optionName, optionValue, optionDefinition.brand, this.#onDiagnostics);
1242
+ case "boolean":
1243
+ await Options.validate(optionName, optionValue, this.#onDiagnostics);
1216
1244
  this.#commandLineOptions[optionDefinition.name] = optionValue !== "false";
1217
1245
  if (optionValue === "false" || optionValue === "true") {
1218
1246
  index++;
1219
1247
  }
1220
1248
  break;
1221
- case OptionBrand.List:
1249
+ case "list":
1222
1250
  if (optionValue !== "") {
1223
1251
  const optionValues = optionValue
1224
1252
  .split(",")
@@ -1226,7 +1254,7 @@ class CommandLineParser {
1226
1254
  .filter((value) => value !== "")
1227
1255
  .map((value) => Options.resolve(optionName, value));
1228
1256
  for (const optionValue of optionValues) {
1229
- await Options.validate(optionName, optionValue, optionDefinition.brand, this.#onDiagnostics);
1257
+ await Options.validate(optionName, optionValue, this.#onDiagnostics);
1230
1258
  }
1231
1259
  this.#commandLineOptions[optionDefinition.name] = optionValues;
1232
1260
  index++;
@@ -1234,16 +1262,30 @@ class CommandLineParser {
1234
1262
  }
1235
1263
  this.#onExpectsValue(optionName, optionDefinition.brand);
1236
1264
  break;
1237
- case OptionBrand.String:
1265
+ case "string":
1238
1266
  if (optionValue !== "") {
1239
1267
  optionValue = Options.resolve(optionName, optionValue);
1240
- await Options.validate(optionName, optionValue, optionDefinition.brand, this.#onDiagnostics);
1268
+ await Options.validate(optionName, optionValue, this.#onDiagnostics);
1241
1269
  this.#commandLineOptions[optionDefinition.name] = optionValue;
1242
1270
  index++;
1243
1271
  break;
1244
1272
  }
1245
1273
  this.#onExpectsValue(optionName, optionDefinition.brand);
1246
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;
1247
1289
  }
1248
1290
  return index;
1249
1291
  }
@@ -1265,43 +1307,58 @@ class ConfigParser {
1265
1307
  this.#sourceFile = sourceFile;
1266
1308
  this.#options = Options.for(optionGroup);
1267
1309
  }
1268
- #onRequiresValue(optionDefinition, jsonNode, isListItem) {
1310
+ #onRequiresValue(optionName, optionBrand, jsonNode, isListItem) {
1269
1311
  const text = isListItem
1270
- ? ConfigDiagnosticText.expectsListItemType(optionDefinition.name, optionDefinition.brand)
1271
- : ConfigDiagnosticText.requiresValueType(optionDefinition.name, optionDefinition.brand);
1312
+ ? ConfigDiagnosticText.expectsListItemType(optionName, optionBrand)
1313
+ : ConfigDiagnosticText.optionValueMustBe(optionName, optionBrand);
1272
1314
  this.#onDiagnostics(Diagnostic.error(text, jsonNode.origin));
1273
1315
  }
1274
1316
  async #parseValue(optionDefinition, isListItem = false) {
1275
1317
  let jsonNode;
1276
1318
  let optionValue;
1277
1319
  switch (optionDefinition.brand) {
1278
- case OptionBrand.Boolean: {
1320
+ case "boolean": {
1279
1321
  jsonNode = this.#jsonScanner.read();
1280
1322
  optionValue = jsonNode.getValue();
1281
1323
  if (typeof optionValue !== "boolean") {
1282
- this.#onRequiresValue(optionDefinition, jsonNode, isListItem);
1324
+ this.#onRequiresValue(optionDefinition.name, optionDefinition.brand, jsonNode, isListItem);
1283
1325
  break;
1284
1326
  }
1285
1327
  break;
1286
1328
  }
1287
- case OptionBrand.String: {
1329
+ case "string": {
1288
1330
  jsonNode = this.#jsonScanner.read();
1289
1331
  optionValue = jsonNode.getValue();
1290
1332
  if (typeof optionValue !== "string") {
1291
- this.#onRequiresValue(optionDefinition, jsonNode, isListItem);
1333
+ this.#onRequiresValue(optionDefinition.name, optionDefinition.brand, jsonNode, isListItem);
1292
1334
  break;
1293
1335
  }
1294
1336
  const rootPath = Path.dirname(this.#sourceFile.fileName);
1295
1337
  optionValue = Options.resolve(optionDefinition.name, optionValue, rootPath);
1296
- await Options.validate(optionDefinition.name, optionValue, optionDefinition.brand, this.#onDiagnostics, jsonNode.origin);
1338
+ await Options.validate(optionDefinition.name, optionValue, this.#onDiagnostics, jsonNode.origin);
1339
+ break;
1340
+ }
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
+ }
1297
1354
  break;
1298
1355
  }
1299
- case OptionBrand.List: {
1356
+ case "list": {
1300
1357
  optionValue = [];
1301
1358
  const leftBracketToken = this.#jsonScanner.readToken("[");
1302
1359
  if (!leftBracketToken.text) {
1303
1360
  jsonNode = this.#jsonScanner.read();
1304
- this.#onRequiresValue(optionDefinition, jsonNode, isListItem);
1361
+ this.#onRequiresValue(optionDefinition.name, optionDefinition.brand, jsonNode, isListItem);
1305
1362
  break;
1306
1363
  }
1307
1364
  while (!this.#jsonScanner.isRead()) {
@@ -1399,15 +1456,16 @@ class ConfigParser {
1399
1456
  }
1400
1457
 
1401
1458
  const defaultOptions = {
1402
- checkSourceFiles: true,
1403
- checkSuppressedErrors: false,
1459
+ checkDeclarationFiles: true,
1460
+ checkSuppressedErrors: true,
1404
1461
  failFast: false,
1462
+ fixtureFileMatch: ["**/__fixtures__/*.{ts,tsx}", "**/fixtures/*.{ts,tsx}"],
1405
1463
  plugins: [],
1406
1464
  rejectAnyType: true,
1407
1465
  rejectNeverType: true,
1408
1466
  reporters: ["list", "summary"],
1409
1467
  rootPath: Path.resolve("./"),
1410
- target: environmentOptions.typescriptModule != null ? ["current"] : ["latest"],
1468
+ target: ["*"],
1411
1469
  testFileMatch: ["**/*.tst.*", "**/__typetests__/*.test.*", "**/typetests/*.test.*"],
1412
1470
  tsconfig: "findup",
1413
1471
  };
@@ -1552,11 +1610,8 @@ class Config {
1552
1610
  static async parseCommandLine(commandLine) {
1553
1611
  const commandLineOptions = {};
1554
1612
  const pathMatch = [];
1555
- const commandLineParser = new CommandLineParser(commandLineOptions, pathMatch, Config.#onDiagnostics);
1613
+ const commandLineParser = new CommandParser(commandLineOptions, pathMatch, Config.#onDiagnostics);
1556
1614
  await commandLineParser.parse(commandLine);
1557
- if (commandLineOptions.target != null) {
1558
- commandLineOptions.target = await Target.expand(commandLineOptions.target);
1559
- }
1560
1615
  return { commandLineOptions, pathMatch };
1561
1616
  }
1562
1617
  static async parseConfigFile(filePath) {
@@ -1569,11 +1624,8 @@ class Config {
1569
1624
  encoding: "utf8",
1570
1625
  });
1571
1626
  const sourceFile = new SourceFile(configFilePath, configFileText);
1572
- 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);
1573
1628
  await configFileParser.parse();
1574
- if (configFileOptions.target != null) {
1575
- configFileOptions.target = await Target.expand(configFileOptions.target);
1576
- }
1577
1629
  }
1578
1630
  return { configFileOptions, configFilePath };
1579
1631
  }
@@ -1609,12 +1661,30 @@ class DirectiveDiagnosticText {
1609
1661
 
1610
1662
  class Directive {
1611
1663
  static #directiveRegex = /^(\/\/ *@tstyche)( *|-)?(\S*)?( *)?(.*)?/i;
1612
- 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
+ }
1613
1683
  const comments = compiler.getLeadingCommentRanges(sourceFile.text, position);
1614
1684
  if (!comments || comments.length === 0) {
1615
1685
  return;
1616
1686
  }
1617
- const ranges = Object.assign([], { sourceFile });
1687
+ ranges = [];
1618
1688
  for (const comment of comments) {
1619
1689
  if (comment.kind !== compiler.SyntaxKind.SingleLineCommentTrivia) {
1620
1690
  continue;
@@ -1624,15 +1694,17 @@ class Directive {
1624
1694
  ranges.push(range);
1625
1695
  }
1626
1696
  }
1697
+ Directive.#rangeCache.set(node, ranges);
1627
1698
  return ranges;
1628
1699
  }
1629
1700
  static async getInlineConfig(ranges) {
1630
1701
  if (!ranges) {
1631
1702
  return;
1632
1703
  }
1704
+ ranges = Array.isArray(ranges) ? ranges : [ranges];
1633
1705
  const inlineConfig = {};
1634
1706
  for (const range of ranges) {
1635
- await Directive.#parse(inlineConfig, ranges.sourceFile, range);
1707
+ await Directive.#parse(inlineConfig, range);
1636
1708
  }
1637
1709
  return inlineConfig;
1638
1710
  }
@@ -1644,6 +1716,7 @@ class Directive {
1644
1716
  return;
1645
1717
  }
1646
1718
  const range = {
1719
+ sourceFile,
1647
1720
  namespace: { start: comment.pos, end: comment.pos + namespaceText.length, text: namespaceText },
1648
1721
  };
1649
1722
  const directiveSeparatorText = match?.[2];
@@ -1663,45 +1736,61 @@ class Directive {
1663
1736
  static #onDiagnostics(diagnostic) {
1664
1737
  EventEmitter.dispatch(["directive:error", { diagnostics: [diagnostic] }]);
1665
1738
  }
1666
- static async #parse(inlineConfig, sourceFile, ranges) {
1667
- switch (ranges.directive?.text) {
1739
+ static async #parse(inlineConfig, range) {
1740
+ switch (range.directive?.text) {
1668
1741
  case "if":
1669
1742
  {
1670
- if (!ranges.argument?.text) {
1743
+ if (!range.argument?.text) {
1671
1744
  const text = DirectiveDiagnosticText.requiresArgument();
1672
- const origin = new DiagnosticOrigin(ranges.namespace.start, ranges.directive.end, sourceFile);
1745
+ const origin = new DiagnosticOrigin(range.namespace.start, range.directive.end, range.sourceFile);
1673
1746
  Directive.#onDiagnostics(Diagnostic.error(text, origin));
1674
1747
  return;
1675
1748
  }
1676
- 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);
1677
1750
  inlineConfig.if = value;
1678
1751
  }
1679
1752
  return;
1753
+ case "fixme":
1680
1754
  case "template":
1681
- if (ranges.argument?.text != null) {
1755
+ if (range.argument?.text != null) {
1682
1756
  const text = DirectiveDiagnosticText.doesNotTakeArgument();
1683
- const origin = new DiagnosticOrigin(ranges.argument.start, ranges.argument.end, sourceFile);
1757
+ const origin = new DiagnosticOrigin(range.argument.start, range.argument.end, range.sourceFile);
1684
1758
  Directive.#onDiagnostics(Diagnostic.error(text, origin));
1685
1759
  }
1686
- inlineConfig.template = true;
1760
+ inlineConfig[range.directive?.text] = true;
1687
1761
  return;
1688
1762
  }
1689
- const target = ranges?.directive ?? ranges.namespace;
1763
+ const target = range?.directive ?? range.namespace;
1690
1764
  const text = DirectiveDiagnosticText.isNotSupported(target.text);
1691
- const origin = new DiagnosticOrigin(target.start, target.end, sourceFile);
1765
+ const origin = new DiagnosticOrigin(target.start, target.end, range.sourceFile);
1692
1766
  Directive.#onDiagnostics(Diagnostic.error(text, origin));
1693
1767
  }
1694
1768
  static async #parseJson(sourceFile, start, end) {
1695
1769
  const inlineOptions = {};
1696
- 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);
1697
1771
  await configParser.parse();
1698
- if ("target" in inlineOptions) {
1699
- inlineOptions["target"] = await Target.expand(inlineOptions["target"]);
1700
- }
1701
1772
  return inlineOptions;
1702
1773
  }
1703
1774
  }
1704
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
+
1705
1794
  class CancellationHandler {
1706
1795
  #cancellationToken;
1707
1796
  #cancellationReason;
@@ -1711,7 +1800,7 @@ class CancellationHandler {
1711
1800
  }
1712
1801
  on([, payload]) {
1713
1802
  if ("diagnostics" in payload) {
1714
- if (payload.diagnostics.some((diagnostic) => diagnostic.category === DiagnosticCategory.Error)) {
1803
+ if (payload.diagnostics.some((diagnostic) => diagnostic.category === "error")) {
1715
1804
  this.#cancellationToken.cancel(this.#cancellationReason);
1716
1805
  }
1717
1806
  }
@@ -1725,7 +1814,7 @@ class ExitCodeHandler {
1725
1814
  return;
1726
1815
  }
1727
1816
  if ("diagnostics" in payload) {
1728
- if (payload.diagnostics.some((diagnostic) => diagnostic.category === DiagnosticCategory.Error)) {
1817
+ if (payload.diagnostics.some((diagnostic) => diagnostic.category === "error")) {
1729
1818
  this.#setCode(1);
1730
1819
  }
1731
1820
  }
@@ -1757,27 +1846,42 @@ class DescribeResult {
1757
1846
  }
1758
1847
  }
1759
1848
 
1760
- var ResultStatus;
1761
- (function (ResultStatus) {
1762
- ResultStatus["Runs"] = "runs";
1763
- ResultStatus["Passed"] = "passed";
1764
- ResultStatus["Failed"] = "failed";
1765
- ResultStatus["Skipped"] = "skipped";
1766
- ResultStatus["Todo"] = "todo";
1767
- })(ResultStatus || (ResultStatus = {}));
1768
-
1769
1849
  class ExpectResult {
1770
- assertion;
1850
+ expect;
1771
1851
  diagnostics = [];
1772
1852
  parent;
1773
- status = ResultStatus.Runs;
1853
+ status = "runs";
1774
1854
  timing = new ResultTiming();
1775
- constructor(assertion, parent) {
1776
- this.assertion = assertion;
1855
+ constructor(expect, parent) {
1856
+ this.expect = expect;
1777
1857
  this.parent = parent;
1778
1858
  }
1779
1859
  }
1780
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
+
1781
1885
  class ProjectResult {
1782
1886
  compilerVersion;
1783
1887
  diagnostics = [];
@@ -1789,51 +1893,38 @@ class ProjectResult {
1789
1893
  }
1790
1894
  }
1791
1895
 
1792
- class ResultCount {
1793
- failed = 0;
1794
- passed = 0;
1795
- skipped = 0;
1796
- todo = 0;
1797
- get total() {
1798
- return this.failed + this.passed + this.skipped + this.todo;
1799
- }
1800
- }
1801
-
1802
1896
  class Result {
1803
1897
  expectCount = new ResultCount();
1804
1898
  fileCount = new ResultCount();
1899
+ files;
1805
1900
  results = [];
1806
1901
  targetCount = new ResultCount();
1807
- tasks;
1808
1902
  testCount = new ResultCount();
1809
1903
  timing = new ResultTiming();
1810
- constructor(tasks) {
1811
- this.tasks = tasks;
1904
+ constructor(files) {
1905
+ this.files = files;
1812
1906
  }
1813
1907
  }
1814
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
+
1815
1919
  class TargetResult {
1920
+ files;
1816
1921
  results = new Map();
1817
- status = ResultStatus.Runs;
1922
+ status = "runs";
1818
1923
  target;
1819
- tasks;
1820
1924
  timing = new ResultTiming();
1821
- constructor(target, tasks) {
1925
+ constructor(target, files) {
1822
1926
  this.target = target;
1823
- this.tasks = tasks;
1824
- }
1825
- }
1826
-
1827
- class TaskResult {
1828
- diagnostics = [];
1829
- expectCount = new ResultCount();
1830
- results = [];
1831
- status = ResultStatus.Runs;
1832
- task;
1833
- testCount = new ResultCount();
1834
- timing = new ResultTiming();
1835
- constructor(task) {
1836
- this.task = task;
1927
+ this.files = files;
1837
1928
  }
1838
1929
  }
1839
1930
 
@@ -1842,7 +1933,7 @@ class TestResult {
1842
1933
  expectCount = new ResultCount();
1843
1934
  parent;
1844
1935
  results = [];
1845
- status = ResultStatus.Runs;
1936
+ status = "runs";
1846
1937
  test;
1847
1938
  timing = new ResultTiming();
1848
1939
  constructor(test, parent) {
@@ -1854,10 +1945,10 @@ class TestResult {
1854
1945
  class ResultHandler {
1855
1946
  #describeResult;
1856
1947
  #expectResult;
1948
+ #fileResult;
1857
1949
  #projectResult;
1858
1950
  #result;
1859
1951
  #targetResult;
1860
- #taskResult;
1861
1952
  #testResult;
1862
1953
  on([event, payload]) {
1863
1954
  switch (event) {
@@ -1875,19 +1966,19 @@ class ResultHandler {
1875
1966
  this.#targetResult.timing.start = Date.now();
1876
1967
  break;
1877
1968
  case "target:end":
1878
- if (this.#targetResult.status === ResultStatus.Failed) {
1969
+ if (this.#targetResult.status === "failed") {
1879
1970
  this.#result.targetCount.failed++;
1880
1971
  }
1881
1972
  else {
1882
1973
  this.#result.targetCount.passed++;
1883
- this.#targetResult.status = ResultStatus.Passed;
1974
+ this.#targetResult.status = "passed";
1884
1975
  }
1885
1976
  this.#targetResult.timing.end = Date.now();
1886
1977
  this.#targetResult = undefined;
1887
1978
  break;
1888
1979
  case "store:error":
1889
- if (payload.diagnostics.some(({ category }) => category === DiagnosticCategory.Error)) {
1890
- this.#targetResult.status = ResultStatus.Failed;
1980
+ if (payload.diagnostics.some(({ category }) => category === "error")) {
1981
+ this.#targetResult.status = "failed";
1891
1982
  }
1892
1983
  break;
1893
1984
  case "project:uses": {
@@ -1900,42 +1991,42 @@ class ResultHandler {
1900
1991
  break;
1901
1992
  }
1902
1993
  case "project:error":
1903
- this.#targetResult.status = ResultStatus.Failed;
1994
+ this.#targetResult.status = "failed";
1904
1995
  this.#projectResult.diagnostics.push(...payload.diagnostics);
1905
1996
  break;
1906
- case "task:start":
1997
+ case "file:start":
1907
1998
  this.#projectResult.results.push(payload.result);
1908
- this.#taskResult = payload.result;
1909
- this.#taskResult.timing.start = Date.now();
1999
+ this.#fileResult = payload.result;
2000
+ this.#fileResult.timing.start = Date.now();
1910
2001
  break;
1911
- case "task:error":
2002
+ case "file:error":
1912
2003
  case "directive:error":
1913
2004
  case "collect:error":
1914
- this.#targetResult.status = ResultStatus.Failed;
1915
- this.#taskResult.status = ResultStatus.Failed;
1916
- this.#taskResult.diagnostics.push(...payload.diagnostics);
2005
+ this.#targetResult.status = "failed";
2006
+ this.#fileResult.status = "failed";
2007
+ this.#fileResult.diagnostics.push(...payload.diagnostics);
1917
2008
  break;
1918
- case "task:end":
1919
- if (this.#taskResult.status === ResultStatus.Failed ||
1920
- this.#taskResult.expectCount.failed > 0 ||
1921
- 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) {
1922
2013
  this.#result.fileCount.failed++;
1923
- this.#targetResult.status = ResultStatus.Failed;
1924
- this.#taskResult.status = ResultStatus.Failed;
2014
+ this.#targetResult.status = "failed";
2015
+ this.#fileResult.status = "failed";
1925
2016
  }
1926
2017
  else {
1927
2018
  this.#result.fileCount.passed++;
1928
- this.#taskResult.status = ResultStatus.Passed;
2019
+ this.#fileResult.status = "passed";
1929
2020
  }
1930
- this.#taskResult.timing.end = Date.now();
1931
- this.#taskResult = undefined;
2021
+ this.#fileResult.timing.end = Date.now();
2022
+ this.#fileResult = undefined;
1932
2023
  break;
1933
2024
  case "describe:start":
1934
2025
  if (this.#describeResult) {
1935
2026
  this.#describeResult.results.push(payload.result);
1936
2027
  }
1937
2028
  else {
1938
- this.#taskResult.results.push(payload.result);
2029
+ this.#fileResult.results.push(payload.result);
1939
2030
  }
1940
2031
  this.#describeResult = payload.result;
1941
2032
  this.#describeResult.timing.start = Date.now();
@@ -1949,44 +2040,51 @@ class ResultHandler {
1949
2040
  this.#describeResult.results.push(payload.result);
1950
2041
  }
1951
2042
  else {
1952
- this.#taskResult.results.push(payload.result);
2043
+ this.#fileResult.results.push(payload.result);
1953
2044
  }
1954
2045
  this.#testResult = payload.result;
1955
2046
  this.#testResult.timing.start = Date.now();
1956
2047
  break;
1957
2048
  case "test:error":
1958
2049
  this.#result.testCount.failed++;
1959
- this.#taskResult.testCount.failed++;
1960
- this.#testResult.status = ResultStatus.Failed;
2050
+ this.#fileResult.testCount.failed++;
2051
+ this.#testResult.status = "failed";
1961
2052
  this.#testResult.diagnostics.push(...payload.diagnostics);
1962
2053
  this.#testResult.timing.end = Date.now();
1963
2054
  this.#testResult = undefined;
1964
2055
  break;
1965
2056
  case "test:fail":
1966
2057
  this.#result.testCount.failed++;
1967
- this.#taskResult.testCount.failed++;
1968
- this.#testResult.status = ResultStatus.Failed;
2058
+ this.#fileResult.testCount.failed++;
2059
+ this.#testResult.status = "failed";
1969
2060
  this.#testResult.timing.end = Date.now();
1970
2061
  this.#testResult = undefined;
1971
2062
  break;
1972
2063
  case "test:pass":
1973
2064
  this.#result.testCount.passed++;
1974
- this.#taskResult.testCount.passed++;
1975
- this.#testResult.status = ResultStatus.Passed;
2065
+ this.#fileResult.testCount.passed++;
2066
+ this.#testResult.status = "passed";
1976
2067
  this.#testResult.timing.end = Date.now();
1977
2068
  this.#testResult = undefined;
1978
2069
  break;
1979
2070
  case "test:skip":
1980
2071
  this.#result.testCount.skipped++;
1981
- this.#taskResult.testCount.skipped++;
1982
- 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";
1983
2081
  this.#testResult.timing.end = Date.now();
1984
2082
  this.#testResult = undefined;
1985
2083
  break;
1986
2084
  case "test:todo":
1987
2085
  this.#result.testCount.todo++;
1988
- this.#taskResult.testCount.todo++;
1989
- this.#testResult.status = ResultStatus.Todo;
2086
+ this.#fileResult.testCount.todo++;
2087
+ this.#testResult.status = "todo";
1990
2088
  this.#testResult.timing.end = Date.now();
1991
2089
  this.#testResult = undefined;
1992
2090
  break;
@@ -1995,49 +2093,59 @@ class ResultHandler {
1995
2093
  this.#testResult.results.push(payload.result);
1996
2094
  }
1997
2095
  else {
1998
- this.#taskResult.results.push(payload.result);
2096
+ this.#fileResult.results.push(payload.result);
1999
2097
  }
2000
2098
  this.#expectResult = payload.result;
2001
2099
  this.#expectResult.timing.start = Date.now();
2002
2100
  break;
2003
2101
  case "expect:error":
2004
2102
  this.#result.expectCount.failed++;
2005
- this.#taskResult.expectCount.failed++;
2103
+ this.#fileResult.expectCount.failed++;
2006
2104
  if (this.#testResult) {
2007
2105
  this.#testResult.expectCount.failed++;
2008
2106
  }
2009
- this.#expectResult.status = ResultStatus.Failed;
2107
+ this.#expectResult.status = "failed";
2010
2108
  this.#expectResult.diagnostics.push(...payload.diagnostics);
2011
2109
  this.#expectResult.timing.end = Date.now();
2012
2110
  this.#expectResult = undefined;
2013
2111
  break;
2014
2112
  case "expect:fail":
2015
2113
  this.#result.expectCount.failed++;
2016
- this.#taskResult.expectCount.failed++;
2114
+ this.#fileResult.expectCount.failed++;
2017
2115
  if (this.#testResult) {
2018
2116
  this.#testResult.expectCount.failed++;
2019
2117
  }
2020
- this.#expectResult.status = ResultStatus.Failed;
2118
+ this.#expectResult.status = "failed";
2021
2119
  this.#expectResult.timing.end = Date.now();
2022
2120
  this.#expectResult = undefined;
2023
2121
  break;
2024
2122
  case "expect:pass":
2025
2123
  this.#result.expectCount.passed++;
2026
- this.#taskResult.expectCount.passed++;
2124
+ this.#fileResult.expectCount.passed++;
2027
2125
  if (this.#testResult) {
2028
2126
  this.#testResult.expectCount.passed++;
2029
2127
  }
2030
- this.#expectResult.status = ResultStatus.Passed;
2128
+ this.#expectResult.status = "passed";
2031
2129
  this.#expectResult.timing.end = Date.now();
2032
2130
  this.#expectResult = undefined;
2033
2131
  break;
2034
2132
  case "expect:skip":
2035
2133
  this.#result.expectCount.skipped++;
2036
- this.#taskResult.expectCount.skipped++;
2134
+ this.#fileResult.expectCount.skipped++;
2037
2135
  if (this.#testResult) {
2038
2136
  this.#testResult.expectCount.skipped++;
2039
2137
  }
2040
- 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";
2041
2149
  this.#expectResult.timing.end = Date.now();
2042
2150
  this.#expectResult = undefined;
2043
2151
  break;
@@ -2066,7 +2174,7 @@ function Text({ children, color, indent }) {
2066
2174
  if (color != null) {
2067
2175
  ansiEscapes.push(color);
2068
2176
  }
2069
- 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] }));
2070
2178
  }
2071
2179
 
2072
2180
  function Line({ children, color, indent }) {
@@ -2127,7 +2235,7 @@ class Scribbler {
2127
2235
  }
2128
2236
 
2129
2237
  function addsPackageText(packageVersion, packagePath) {
2130
- 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] })] }));
2131
2239
  }
2132
2240
 
2133
2241
  function describeNameText(name, indent = 0) {
@@ -2143,13 +2251,13 @@ function BreadcrumbsText({ ancestor }) {
2143
2251
  ancestor = ancestor.parent;
2144
2252
  }
2145
2253
  text.push("");
2146
- return jsx(Text, { color: Color.Gray, children: text.reverse().join(" ❭ ") });
2254
+ return jsx(Text, { color: "90", children: text.reverse().join(" ❭ ") });
2147
2255
  }
2148
- function CodeLineText({ gutterWidth, lineNumber, lineNumberColor = Color.Gray, lineText }) {
2149
- 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] }));
2150
2258
  }
2151
2259
  function SquiggleLineText({ gutterWidth, indentWidth = 0, squiggleColor, squiggleWidth }) {
2152
- 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) })] }));
2153
2261
  }
2154
2262
  function CodeFrameText({ diagnosticCategory, diagnosticOrigin, options }) {
2155
2263
  const linesAbove = options?.linesAbove ?? 2;
@@ -2163,11 +2271,11 @@ function CodeFrameText({ diagnosticCategory, diagnosticOrigin, options }) {
2163
2271
  const gutterWidth = (lastLine + 1).toString().length + 2;
2164
2272
  let highlightColor;
2165
2273
  switch (diagnosticCategory) {
2166
- case DiagnosticCategory.Error:
2167
- highlightColor = Color.Red;
2274
+ case "error":
2275
+ highlightColor = "31";
2168
2276
  break;
2169
- case DiagnosticCategory.Warning:
2170
- highlightColor = Color.Yellow;
2277
+ case "warning":
2278
+ highlightColor = "33";
2171
2279
  break;
2172
2280
  }
2173
2281
  const codeFrame = [];
@@ -2195,32 +2303,59 @@ function CodeFrameText({ diagnosticCategory, diagnosticOrigin, options }) {
2195
2303
  }
2196
2304
  }
2197
2305
  let breadcrumbs;
2198
- if (showBreadcrumbs && diagnosticOrigin.assertion != null) {
2199
- breadcrumbs = jsx(BreadcrumbsText, { ancestor: diagnosticOrigin.assertion.parent });
2306
+ if (showBreadcrumbs && diagnosticOrigin.assertionNode != null) {
2307
+ breadcrumbs = jsx(BreadcrumbsText, { ancestor: diagnosticOrigin.assertionNode.parent });
2200
2308
  }
2201
- 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] }));
2202
2310
  return (jsx(Text, { children: [codeFrame, jsx(Line, {}), location] }));
2203
2311
  }
2204
2312
 
2205
2313
  function DiagnosticText({ codeFrameOptions, diagnostic }) {
2206
- 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;
2207
2315
  const text = Array.isArray(diagnostic.text) ? diagnostic.text : [diagnostic.text];
2208
2316
  const message = text.map((text, index) => (jsx(Text, { children: [index === 1 ? jsx(Line, {}) : undefined, jsx(Line, { children: [text, index === 0 ? code : undefined] })] })));
2209
2317
  const related = diagnostic.related?.map((relatedDiagnostic) => jsx(DiagnosticText, { diagnostic: relatedDiagnostic }));
2210
2318
  const codeFrame = diagnostic.origin ? (jsx(Text, { children: [jsx(Line, {}), jsx(CodeFrameText, { diagnosticCategory: diagnostic.category, diagnosticOrigin: diagnostic.origin, options: codeFrameOptions })] })) : undefined;
2211
2319
  return (jsx(Text, { children: [message, codeFrame, jsx(Line, {}), jsx(Text, { indent: 2, children: related })] }));
2212
2320
  }
2213
- function diagnosticText(diagnostic, codeFrameOptions = {}) {
2214
- let prefix;
2215
- switch (diagnostic.category) {
2216
- case DiagnosticCategory.Error:
2217
- 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";
2348
+ break;
2349
+ case "passed":
2350
+ statusColor = "32";
2351
+ statusText = "pass";
2218
2352
  break;
2219
- case DiagnosticCategory.Warning:
2220
- prefix = jsx(Text, { color: Color.Yellow, children: "Warning: " });
2353
+ case "failed":
2354
+ statusColor = "31";
2355
+ statusText = "fail";
2221
2356
  break;
2222
2357
  }
2223
- 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 })] }));
2224
2359
  }
2225
2360
 
2226
2361
  function fileViewText(lines, addEmptyFinalLine) {
@@ -2246,13 +2381,13 @@ function formattedText(input) {
2246
2381
  }
2247
2382
 
2248
2383
  function HintText({ children }) {
2249
- return (jsx(Text, { indent: 1, color: Color.Gray, children: children }));
2384
+ return (jsx(Text, { indent: 1, color: "90", children: children }));
2250
2385
  }
2251
2386
  function HelpHeaderText({ tstycheVersion }) {
2252
2387
  return (jsx(Line, { children: ["The TSTyche Type Test Runner", jsx(HintText, { children: tstycheVersion })] }));
2253
2388
  }
2254
2389
  function CommandText({ hint, text }) {
2255
- 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 })] }));
2256
2391
  }
2257
2392
  function OptionDescriptionText({ text }) {
2258
2393
  return jsx(Line, { indent: 1, children: text });
@@ -2260,8 +2395,8 @@ function OptionDescriptionText({ text }) {
2260
2395
  function CommandLineUsageText() {
2261
2396
  const usage = [
2262
2397
  ["tstyche", "Run all tests."],
2263
- ["tstyche path/to/first.test.ts", "Only run the test files with matching path."],
2264
- ["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."],
2265
2400
  ];
2266
2401
  const usageText = usage.map(([commandText, descriptionText]) => (jsx(Line, { children: [jsx(CommandText, { text: commandText }), jsx(OptionDescriptionText, { text: descriptionText })] })));
2267
2402
  return jsx(Text, { children: usageText });
@@ -2270,16 +2405,19 @@ function CommandLineOptionNameText({ text }) {
2270
2405
  return jsx(Text, { children: `--${text}` });
2271
2406
  }
2272
2407
  function CommandLineOptionHintText({ definition }) {
2273
- if (definition.brand === OptionBrand.List) {
2408
+ if (definition.brand === "list") {
2274
2409
  return jsx(Text, { children: `${definition.brand} of ${definition.items.brand}s` });
2275
2410
  }
2411
+ if (definition.brand === "range") {
2412
+ return jsx(Text, { children: "string" });
2413
+ }
2276
2414
  return jsx(Text, { children: definition.brand });
2277
2415
  }
2278
2416
  function CommandLineOptionsText({ optionDefinitions }) {
2279
2417
  const definitions = [...optionDefinitions.values()];
2280
2418
  const optionsText = definitions.map((definition) => {
2281
2419
  let hint;
2282
- if (definition.brand !== OptionBrand.BareTrue) {
2420
+ if (definition.brand !== "true") {
2283
2421
  hint = jsx(CommandLineOptionHintText, { definition: definition });
2284
2422
  }
2285
2423
  return (jsx(Text, { children: [jsx(CommandText, { text: jsx(CommandLineOptionNameText, { text: definition.name }), hint: hint }), jsx(OptionDescriptionText, { text: definition.description }), jsx(Line, {})] }));
@@ -2328,69 +2466,44 @@ class OutputService {
2328
2466
  function RowText({ label, text }) {
2329
2467
  return (jsx(Line, { children: [`${label}:`.padEnd(12), text] }));
2330
2468
  }
2331
- function CountText({ failed, passed, skipped, todo, total }) {
2332
- 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"] })] }));
2333
2471
  }
2334
2472
  function DurationText({ seconds }) {
2335
2473
  return jsx(Text, { children: `${Math.round(seconds * 10) / 10}s` });
2336
2474
  }
2337
2475
  function summaryText({ duration, expectCount, fileCount, targetCount, testCount, }) {
2338
- const targetCountText = (jsx(RowText, { label: "Targets", text: jsx(CountText, { failed: targetCount.failed, passed: targetCount.passed, skipped: targetCount.skipped, todo: targetCount.todo, total: targetCount.total }) }));
2339
- 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 }) }));
2340
- const testCountText = (jsx(RowText, { label: "Tests", text: jsx(CountText, { failed: testCount.failed, passed: testCount.passed, skipped: testCount.skipped, todo: testCount.todo, total: testCount.total }) }));
2341
- 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 }) }));
2342
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 }) })] }));
2343
2481
  }
2344
2482
 
2345
- function FileNameText({ filePath }) {
2346
- const relativePath = Path.relative("", filePath);
2347
- const lastPathSeparator = relativePath.lastIndexOf("/");
2348
- const directoryNameText = relativePath.slice(0, lastPathSeparator + 1);
2349
- const fileNameText = relativePath.slice(lastPathSeparator + 1);
2350
- return (jsx(Text, { children: [jsx(Text, { color: Color.Gray, children: directoryNameText }), fileNameText] }));
2351
- }
2352
- function taskStatusText(status, task) {
2353
- let statusColor;
2354
- let statusText;
2355
- switch (status) {
2356
- case ResultStatus.Runs:
2357
- statusColor = Color.Yellow;
2358
- statusText = "runs";
2359
- break;
2360
- case ResultStatus.Passed:
2361
- statusColor = Color.Green;
2362
- statusText = "pass";
2363
- break;
2364
- case ResultStatus.Failed:
2365
- statusColor = Color.Red;
2366
- statusText = "fail";
2367
- break;
2368
- }
2369
- return (jsx(Line, { children: [jsx(Text, { color: statusColor, children: statusText }), " ", jsx(FileNameText, { filePath: task.filePath })] }));
2370
- }
2371
-
2372
2483
  function StatusText({ status }) {
2373
2484
  switch (status) {
2374
2485
  case "fail":
2375
- return jsx(Text, { color: Color.Red, children: "\u00D7" });
2486
+ return jsx(Text, { color: "31", children: "\u00D7" });
2376
2487
  case "pass":
2377
- return jsx(Text, { color: Color.Green, children: "+" });
2488
+ return jsx(Text, { color: "32", children: "+" });
2378
2489
  case "skip":
2379
- 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" });
2380
2493
  case "todo":
2381
- return jsx(Text, { color: Color.Magenta, children: "- todo" });
2494
+ return jsx(Text, { color: "35", children: "- todo" });
2382
2495
  }
2383
2496
  }
2384
2497
  function testNameText(status, name, indent = 0) {
2385
- 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 })] }));
2386
2499
  }
2387
2500
 
2388
2501
  function usesCompilerText(compilerVersion, projectConfigFilePath, options) {
2389
2502
  let projectConfigPathText;
2390
2503
  if (projectConfigFilePath != null) {
2391
- projectConfigPathText = (jsx(Text, { color: Color.Gray, children: [" with ", Path.relative("", projectConfigFilePath)] }));
2504
+ projectConfigPathText = (jsx(Text, { color: "90", children: [" with ", Path.relative("", projectConfigFilePath)] }));
2392
2505
  }
2393
- 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, {})] }));
2394
2507
  }
2395
2508
 
2396
2509
  function waitingForFileChangesText() {
@@ -2403,7 +2516,7 @@ function watchUsageText() {
2403
2516
  ["x", "to exit."],
2404
2517
  ];
2405
2518
  const usageText = usage.map(([keyText, actionText]) => {
2406
- 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 })] }));
2407
2520
  });
2408
2521
  return jsx(Text, { children: usageText });
2409
2522
  }
@@ -2480,7 +2593,7 @@ class ListReporter extends BaseReporter {
2480
2593
  on([event, payload]) {
2481
2594
  switch (event) {
2482
2595
  case "run:start":
2483
- this.#isFileViewExpanded = payload.result.tasks.length === 1 && this.resolvedConfig.watch !== true;
2596
+ this.#isFileViewExpanded = payload.result.files.length === 1 && this.resolvedConfig.watch !== true;
2484
2597
  break;
2485
2598
  case "store:adds":
2486
2599
  OutputService.writeMessage(addsPackageText(payload.packageVersion, payload.packagePath));
@@ -2492,7 +2605,7 @@ class ListReporter extends BaseReporter {
2492
2605
  }
2493
2606
  break;
2494
2607
  case "target:start":
2495
- this.#fileCount = payload.result.tasks.length;
2608
+ this.#fileCount = payload.result.files.length;
2496
2609
  this.#hasReportedUses = false;
2497
2610
  break;
2498
2611
  case "project:uses":
@@ -2507,25 +2620,25 @@ class ListReporter extends BaseReporter {
2507
2620
  OutputService.writeError(diagnosticText(diagnostic));
2508
2621
  }
2509
2622
  break;
2510
- case "task:start":
2623
+ case "file:start":
2511
2624
  if (!environmentOptions.noInteractive) {
2512
- OutputService.writeMessage(taskStatusText(payload.result.status, payload.result.task));
2625
+ OutputService.writeMessage(fileStatusText(payload.result.status, payload.result.file));
2513
2626
  }
2514
2627
  this.#fileCount--;
2515
2628
  this.#hasReportedError = false;
2516
2629
  break;
2517
- case "task:error":
2630
+ case "file:error":
2518
2631
  case "directive:error":
2519
2632
  case "collect:error":
2520
2633
  for (const diagnostic of payload.diagnostics) {
2521
2634
  this.#fileView.addMessage(diagnosticText(diagnostic));
2522
2635
  }
2523
2636
  break;
2524
- case "task:end":
2637
+ case "file:end":
2525
2638
  if (!environmentOptions.noInteractive) {
2526
2639
  OutputService.eraseLastLine();
2527
2640
  }
2528
- OutputService.writeMessage(taskStatusText(payload.result.status, payload.result.task));
2641
+ OutputService.writeMessage(fileStatusText(payload.result.status, payload.result.file));
2529
2642
  OutputService.writeMessage(this.#fileView.getViewText({ appendEmptyLine: this.#isLastFile }));
2530
2643
  if (this.#fileView.hasErrors) {
2531
2644
  OutputService.writeError(this.#fileView.getMessages());
@@ -2548,6 +2661,11 @@ class ListReporter extends BaseReporter {
2548
2661
  this.#fileView.addTest("skip", payload.result.test.name);
2549
2662
  }
2550
2663
  break;
2664
+ case "test:fixme":
2665
+ if (this.#isFileViewExpanded) {
2666
+ this.#fileView.addTest("fixme", payload.result.test.name);
2667
+ }
2668
+ break;
2551
2669
  case "test:todo":
2552
2670
  if (this.#isFileViewExpanded) {
2553
2671
  this.#fileView.addTest("todo", payload.result.test.name);
@@ -2590,10 +2708,10 @@ class SetupReporter {
2590
2708
  if ("diagnostics" in payload) {
2591
2709
  for (const diagnostic of payload.diagnostics) {
2592
2710
  switch (diagnostic.category) {
2593
- case DiagnosticCategory.Error:
2711
+ case "error":
2594
2712
  OutputService.writeError(diagnosticText(diagnostic));
2595
2713
  break;
2596
- case DiagnosticCategory.Warning:
2714
+ case "warning":
2597
2715
  OutputService.writeWarning(diagnosticText(diagnostic));
2598
2716
  break;
2599
2717
  }
@@ -2639,18 +2757,18 @@ class WatchReporter extends BaseReporter {
2639
2757
  }
2640
2758
  }
2641
2759
 
2642
- class Task {
2643
- filePath;
2760
+ class FileLocation {
2761
+ path;
2644
2762
  position;
2645
- constructor(filePath, position) {
2646
- this.filePath = Path.resolve(this.#toPath(filePath));
2763
+ constructor(file, position) {
2764
+ this.path = Path.resolve(this.#toPath(file));
2647
2765
  this.position = position;
2648
2766
  }
2649
- #toPath(filePath) {
2650
- if (typeof filePath === "string" && !filePath.startsWith("file:")) {
2651
- return filePath;
2767
+ #toPath(file) {
2768
+ if (typeof file === "string" && !file.startsWith("file:")) {
2769
+ return file;
2652
2770
  }
2653
- return fileURLToPath(filePath);
2771
+ return fileURLToPath(file);
2654
2772
  }
2655
2773
  }
2656
2774
 
@@ -2916,6 +3034,10 @@ class Select {
2916
3034
  }
2917
3035
  return matchPatterns.includedFile.test(filePath);
2918
3036
  }
3037
+ static isFixtureFile(filePath, resolvedConfig) {
3038
+ const matchPatterns = Select.#getMatchPatterns(resolvedConfig.fixtureFileMatch);
3039
+ return Select.#isFileIncluded(Path.relative(resolvedConfig.rootPath, filePath), matchPatterns, resolvedConfig);
3040
+ }
2919
3041
  static isTestFile(filePath, resolvedConfig) {
2920
3042
  const matchPatterns = Select.#getMatchPatterns(resolvedConfig.testFileMatch);
2921
3043
  return Select.#isFileIncluded(Path.relative(resolvedConfig.rootPath, filePath), matchPatterns, resolvedConfig);
@@ -2984,9 +3106,9 @@ class WatchService {
2984
3106
  #resolvedConfig;
2985
3107
  #watchedTestFiles;
2986
3108
  #watchers = [];
2987
- constructor(resolvedConfig, tasks) {
3109
+ constructor(resolvedConfig, files) {
2988
3110
  this.#resolvedConfig = resolvedConfig;
2989
- this.#watchedTestFiles = new Map(tasks.map((task) => [task.filePath, task]));
3111
+ this.#watchedTestFiles = new Map(files.map((file) => [file.path, file]));
2990
3112
  }
2991
3113
  #onDiagnostics(diagnostic) {
2992
3114
  EventEmitter.dispatch(["watch:error", { diagnostics: [diagnostic] }]);
@@ -3015,7 +3137,7 @@ class WatchService {
3015
3137
  case "\u001B":
3016
3138
  case "q":
3017
3139
  case "x":
3018
- onClose(CancellationReason.WatchClose);
3140
+ onClose("watchClose");
3019
3141
  break;
3020
3142
  case "\u000D":
3021
3143
  case "\u0020":
@@ -3031,14 +3153,14 @@ class WatchService {
3031
3153
  }
3032
3154
  const onChangedFile = (filePath) => {
3033
3155
  debounce.refresh();
3034
- let task = this.#watchedTestFiles.get(filePath);
3035
- if (task != null) {
3036
- this.#changedTestFiles.set(filePath, task);
3156
+ let file = this.#watchedTestFiles.get(filePath);
3157
+ if (file != null) {
3158
+ this.#changedTestFiles.set(filePath, file);
3037
3159
  }
3038
3160
  else if (Select.isTestFile(filePath, this.#resolvedConfig)) {
3039
- task = new Task(filePath);
3040
- this.#changedTestFiles.set(filePath, task);
3041
- this.#watchedTestFiles.set(filePath, task);
3161
+ file = new FileLocation(filePath);
3162
+ this.#changedTestFiles.set(filePath, file);
3163
+ this.#watchedTestFiles.set(filePath, file);
3042
3164
  }
3043
3165
  };
3044
3166
  const onRemovedFile = (filePath) => {
@@ -3051,7 +3173,7 @@ class WatchService {
3051
3173
  };
3052
3174
  this.#watchers.push(new Watcher(this.#resolvedConfig.rootPath, onChangedFile, onRemovedFile, { recursive: true }));
3053
3175
  const onChangedConfigFile = () => {
3054
- onClose(CancellationReason.ConfigChange);
3176
+ onClose("configChange");
3055
3177
  };
3056
3178
  this.#watchers.push(new FileWatcher(this.#resolvedConfig.configFilePath, onChangedConfigFile));
3057
3179
  for (const watcher of this.#watchers) {
@@ -3066,65 +3188,6 @@ class WatchService {
3066
3188
  }
3067
3189
  }
3068
3190
 
3069
- class TestTreeNode {
3070
- brand;
3071
- children = [];
3072
- diagnostics = new Set();
3073
- flags;
3074
- name = "";
3075
- node;
3076
- parent;
3077
- constructor(compiler, brand, node, parent, flags) {
3078
- this.brand = brand;
3079
- this.node = node;
3080
- this.parent = parent;
3081
- this.flags = flags;
3082
- if (node.arguments[0] != null && compiler.isStringLiteralLike(node.arguments[0])) {
3083
- this.name = node.arguments[0].text;
3084
- }
3085
- if (node.arguments[1] != null && compiler.isFunctionLike(node.arguments[1])) {
3086
- for (const diagnostic of parent.diagnostics) {
3087
- if (diagnosticBelongsToNode(diagnostic, node.arguments[1].body)) {
3088
- this.diagnostics.add(diagnostic);
3089
- parent.diagnostics.delete(diagnostic);
3090
- }
3091
- }
3092
- }
3093
- }
3094
- getDirectiveRanges(compiler) {
3095
- return Directive.getDirectiveRanges(compiler, this.node.getSourceFile(), this.node.getFullStart());
3096
- }
3097
- }
3098
-
3099
- class AssertionNode extends TestTreeNode {
3100
- abilityDiagnostics = new Set();
3101
- isNot;
3102
- matcherNode;
3103
- matcherNameNode;
3104
- modifierNode;
3105
- notNode;
3106
- source;
3107
- target;
3108
- constructor(compiler, brand, node, parent, flags, matcherNode, matcherNameNode, modifierNode, notNode) {
3109
- super(compiler, brand, node, parent, flags);
3110
- this.isNot = notNode != null;
3111
- this.matcherNode = matcherNode;
3112
- this.matcherNameNode = matcherNameNode;
3113
- this.modifierNode = modifierNode;
3114
- this.source = this.node.typeArguments ?? this.node.arguments;
3115
- if (compiler.isCallExpression(this.matcherNode)) {
3116
- this.target = this.matcherNode.typeArguments ?? this.matcherNode.arguments;
3117
- }
3118
- for (const diagnostic of parent.diagnostics) {
3119
- if (diagnosticBelongsToNode(diagnostic, this.source) ||
3120
- (this.target != null && diagnosticBelongsToNode(diagnostic, this.target))) {
3121
- this.diagnostics.add(diagnostic);
3122
- parent.diagnostics.delete(diagnostic);
3123
- }
3124
- }
3125
- }
3126
- }
3127
-
3128
3191
  function nodeBelongsToArgumentList(compiler, node) {
3129
3192
  return compiler.isCallExpression(node.parent) && node.parent.arguments.some((argument) => argument === node);
3130
3193
  }
@@ -3134,7 +3197,7 @@ function nodeIsChildOfExpressionStatement(compiler, node) {
3134
3197
 
3135
3198
  class AbilityLayer {
3136
3199
  #compiler;
3137
- #expectErrorRegex = /^( *)(\/\/ *@ts-expect-error)(!?)(:? *)(.*)?$/gim;
3200
+ #expectErrorRegex = /^(\s*)(\/\/ *@ts-expect-error)(!?)(:? *)(.*)?$/gim;
3138
3201
  #filePath = "";
3139
3202
  #nodes = [];
3140
3203
  #projectService;
@@ -3155,16 +3218,27 @@ class AbilityLayer {
3155
3218
  this.#text = `${this.#text.slice(0, range.start)}${rangeText}${this.#text.slice(range.end)}`;
3156
3219
  }
3157
3220
  }
3158
- #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) {
3159
3233
  for (const node of this.#nodes) {
3160
- if (diagnosticBelongsToNode(diagnostic, "matcherNode" in node ? node.matcherNode : node.actionNode)) {
3234
+ if (this.#belongsToNode(node, diagnostic)) {
3161
3235
  node.abilityDiagnostics.add(diagnostic);
3162
3236
  return true;
3163
3237
  }
3164
3238
  }
3165
3239
  return false;
3166
3240
  }
3167
- #belongsToDirective(diagnostic) {
3241
+ #mapToDirectives(diagnostic) {
3168
3242
  if (!isDiagnosticWithLocation(diagnostic)) {
3169
3243
  return;
3170
3244
  }
@@ -3209,18 +3283,19 @@ class AbilityLayer {
3209
3283
  }
3210
3284
  return ranges;
3211
3285
  }
3212
- close() {
3286
+ close(testTree) {
3213
3287
  if (this.#nodes.length > 0 || this.#suppressedErrorsMap != null) {
3288
+ SourceService.set(testTree.sourceFile);
3214
3289
  this.#projectService.openFile(this.#filePath, this.#text, this.#resolvedConfig.rootPath);
3215
3290
  const languageService = this.#projectService.getLanguageService(this.#filePath);
3216
3291
  const diagnostics = languageService?.getSemanticDiagnostics(this.#filePath);
3217
3292
  if (diagnostics != null) {
3218
3293
  this.#nodes.reverse();
3219
3294
  for (const diagnostic of diagnostics) {
3220
- if (this.#belongsToNode(diagnostic)) {
3295
+ if (this.#mapToNodes(diagnostic)) {
3221
3296
  continue;
3222
3297
  }
3223
- this.#belongsToDirective(diagnostic);
3298
+ this.#mapToDirectives(diagnostic);
3224
3299
  }
3225
3300
  }
3226
3301
  }
@@ -3252,36 +3327,36 @@ class AbilityLayer {
3252
3327
  }
3253
3328
  return text.join("");
3254
3329
  }
3255
- handleAssertion(assertionNode) {
3256
- const expectStart = assertionNode.node.getStart();
3257
- const expectExpressionEnd = assertionNode.node.expression.getEnd();
3258
- const expectEnd = assertionNode.node.getEnd();
3259
- const matcherNameEnd = assertionNode.matcherNameNode.getEnd();
3260
- 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) {
3261
3336
  case "toBeApplicable":
3262
- this.#addRanges(assertionNode, [
3337
+ this.#addRanges(expect, [
3263
3338
  { start: expectStart, end: expectExpressionEnd },
3264
3339
  { start: expectEnd, end: matcherNameEnd },
3265
3340
  ]);
3266
3341
  break;
3267
3342
  case "toBeCallableWith":
3268
- this.#eraseTrailingComma(assertionNode.source, assertionNode);
3269
- this.#addRanges(assertionNode, [
3343
+ this.#eraseTrailingComma(expect.source, expect);
3344
+ this.#addRanges(expect, [
3270
3345
  {
3271
3346
  start: expectStart,
3272
3347
  end: expectExpressionEnd,
3273
- replacement: nodeIsChildOfExpressionStatement(this.#compiler, assertionNode.matcherNode) ? ";" : "",
3348
+ replacement: nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode) ? ";" : "",
3274
3349
  },
3275
3350
  { start: expectEnd, end: matcherNameEnd },
3276
3351
  ]);
3277
3352
  break;
3278
3353
  case "toBeConstructableWith":
3279
- this.#eraseTrailingComma(assertionNode.source, assertionNode);
3280
- this.#addRanges(assertionNode, [
3354
+ this.#eraseTrailingComma(expect.source, expect);
3355
+ this.#addRanges(expect, [
3281
3356
  {
3282
3357
  start: expectStart,
3283
3358
  end: expectExpressionEnd,
3284
- replacement: nodeIsChildOfExpressionStatement(this.#compiler, assertionNode.matcherNode) ? "; new" : "new",
3359
+ replacement: nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode) ? "; new" : "new",
3285
3360
  },
3286
3361
  { start: expectEnd, end: matcherNameEnd },
3287
3362
  ]);
@@ -3291,7 +3366,7 @@ class AbilityLayer {
3291
3366
  #handleSuppressedErrors(testTree) {
3292
3367
  const suppressedErrors = this.#collectSuppressedErrors();
3293
3368
  if (this.#resolvedConfig.checkSuppressedErrors) {
3294
- testTree.suppressedErrors = Object.assign(suppressedErrors, { sourceFile: testTree.sourceFile });
3369
+ testTree.suppressedErrors = suppressedErrors;
3295
3370
  this.#suppressedErrorsMap = new Map();
3296
3371
  }
3297
3372
  for (const suppressedError of suppressedErrors) {
@@ -3336,22 +3411,61 @@ class CollectDiagnosticText {
3336
3411
  }
3337
3412
  }
3338
3413
 
3339
- var TestTreeNodeBrand;
3340
- (function (TestTreeNodeBrand) {
3341
- TestTreeNodeBrand["Describe"] = "describe";
3342
- TestTreeNodeBrand["Test"] = "test";
3343
- TestTreeNodeBrand["Expect"] = "expect";
3344
- TestTreeNodeBrand["When"] = "when";
3345
- })(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
+ }
3346
3440
 
3347
- var TestTreeNodeFlags;
3348
- (function (TestTreeNodeFlags) {
3349
- TestTreeNodeFlags[TestTreeNodeFlags["None"] = 0] = "None";
3350
- TestTreeNodeFlags[TestTreeNodeFlags["Fail"] = 1] = "Fail";
3351
- TestTreeNodeFlags[TestTreeNodeFlags["Only"] = 2] = "Only";
3352
- TestTreeNodeFlags[TestTreeNodeFlags["Skip"] = 4] = "Skip";
3353
- TestTreeNodeFlags[TestTreeNodeFlags["Todo"] = 8] = "Todo";
3354
- })(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
+ }
3355
3469
 
3356
3470
  class IdentifierLookup {
3357
3471
  #compiler;
@@ -3400,24 +3514,21 @@ class IdentifierLookup {
3400
3514
  };
3401
3515
  }
3402
3516
  resolveTestTreeNodeMeta(node) {
3403
- let flags = TestTreeNodeFlags.None;
3517
+ let flags = 0;
3404
3518
  let expression = node.expression;
3405
3519
  while (this.#compiler.isPropertyAccessExpression(expression)) {
3406
3520
  if (expression.expression.getText() === this.#identifiers.namespace) {
3407
3521
  break;
3408
3522
  }
3409
3523
  switch (expression.name.getText()) {
3410
- case "fail":
3411
- flags |= TestTreeNodeFlags.Fail;
3412
- break;
3413
3524
  case "only":
3414
- flags |= TestTreeNodeFlags.Only;
3525
+ flags |= 1;
3415
3526
  break;
3416
3527
  case "skip":
3417
- flags |= TestTreeNodeFlags.Skip;
3528
+ flags |= 2;
3418
3529
  break;
3419
3530
  case "todo":
3420
- flags |= TestTreeNodeFlags.Todo;
3531
+ flags |= 4;
3421
3532
  break;
3422
3533
  }
3423
3534
  expression = expression.expression;
@@ -3435,14 +3546,14 @@ class IdentifierLookup {
3435
3546
  }
3436
3547
  switch (identifier) {
3437
3548
  case "describe":
3438
- return { brand: TestTreeNodeBrand.Describe, flags, identifier };
3549
+ return { brand: "describe", flags, identifier };
3439
3550
  case "it":
3440
3551
  case "test":
3441
- return { brand: TestTreeNodeBrand.Test, flags, identifier };
3552
+ return { brand: "test", flags, identifier };
3442
3553
  case "expect":
3443
- return { brand: TestTreeNodeBrand.Expect, flags, identifier };
3554
+ return { brand: "expect", flags, identifier };
3444
3555
  case "when":
3445
- return { brand: TestTreeNodeBrand.When, flags, identifier };
3556
+ return { brand: "when", flags, identifier };
3446
3557
  }
3447
3558
  return;
3448
3559
  }
@@ -3458,9 +3569,6 @@ class TestTree {
3458
3569
  this.diagnostics = diagnostics;
3459
3570
  this.sourceFile = sourceFile;
3460
3571
  }
3461
- getDirectiveRanges(compiler) {
3462
- return Directive.getDirectiveRanges(compiler, this.sourceFile);
3463
- }
3464
3572
  }
3465
3573
 
3466
3574
  class WhenNode extends TestTreeNode {
@@ -3498,7 +3606,7 @@ class CollectService {
3498
3606
  if (!this.#checkNode(node, meta, parent)) {
3499
3607
  return;
3500
3608
  }
3501
- if (meta.brand === TestTreeNodeBrand.Describe || meta.brand === TestTreeNodeBrand.Test) {
3609
+ if (meta.brand === "describe" || meta.brand === "test") {
3502
3610
  const testTreeNode = new TestTreeNode(this.#compiler, meta.brand, node, parent, meta.flags);
3503
3611
  this.#compiler.forEachChild(node, (node) => {
3504
3612
  this.#collectTestTreeNodes(node, testTreeNode, testTree);
@@ -3506,7 +3614,7 @@ class CollectService {
3506
3614
  this.#onNode(testTreeNode, parent, testTree);
3507
3615
  return;
3508
3616
  }
3509
- if (meta.brand === TestTreeNodeBrand.Expect) {
3617
+ if (meta.brand === "expect") {
3510
3618
  const modifierNode = this.#getChainedNode(node, "type");
3511
3619
  if (!modifierNode) {
3512
3620
  return;
@@ -3520,15 +3628,15 @@ class CollectService {
3520
3628
  if (!matcherNode) {
3521
3629
  return;
3522
3630
  }
3523
- const assertionNode = new AssertionNode(this.#compiler, meta.brand, node, parent, meta.flags, matcherNode, matcherNameNode, modifierNode, notNode);
3524
- 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);
3525
3633
  this.#compiler.forEachChild(node, (node) => {
3526
- this.#collectTestTreeNodes(node, assertionNode, testTree);
3634
+ this.#collectTestTreeNodes(node, expectNode, testTree);
3527
3635
  });
3528
- this.#onNode(assertionNode, parent, testTree);
3636
+ this.#onNode(expectNode, parent, testTree);
3529
3637
  return;
3530
3638
  }
3531
- if (meta.brand === TestTreeNodeBrand.When) {
3639
+ if (meta.brand === "when") {
3532
3640
  const actionNameNode = this.#getChainedNode(node);
3533
3641
  if (!actionNameNode) {
3534
3642
  return;
@@ -3540,7 +3648,7 @@ class CollectService {
3540
3648
  this.#compiler.forEachChild(actionNode, (node) => {
3541
3649
  if (this.#compiler.isCallExpression(node)) {
3542
3650
  const meta = this.#identifierLookup.resolveTestTreeNodeMeta(node);
3543
- if (meta?.brand === TestTreeNodeBrand.Describe || meta?.brand === TestTreeNodeBrand.Test) {
3651
+ if (meta?.brand === "describe" || meta?.brand === "test") {
3544
3652
  const text = CollectDiagnosticText.cannotBeNestedWithin(meta.identifier, "when");
3545
3653
  const origin = DiagnosticOrigin.fromNode(node);
3546
3654
  this.#onDiagnostics(Diagnostic.error(text, origin));
@@ -3568,7 +3676,7 @@ class CollectService {
3568
3676
  this.#abilityLayer.open(testTree);
3569
3677
  this.#identifierLookup.open();
3570
3678
  this.#collectTestTreeNodes(sourceFile, testTree, testTree);
3571
- this.#abilityLayer.close();
3679
+ this.#abilityLayer.close(testTree);
3572
3680
  EventEmitter.dispatch(["collect:end", { tree: testTree }]);
3573
3681
  return testTree;
3574
3682
  }
@@ -3583,15 +3691,15 @@ class CollectService {
3583
3691
  }
3584
3692
  #isNodeAllowed(meta, parent) {
3585
3693
  switch (meta.brand) {
3586
- case TestTreeNodeBrand.Describe:
3587
- case TestTreeNodeBrand.Test:
3588
- if (parent.brand === TestTreeNodeBrand.Test || parent.brand === TestTreeNodeBrand.Expect) {
3694
+ case "describe":
3695
+ case "test":
3696
+ if (parent.brand === "test" || parent.brand === "expect") {
3589
3697
  return false;
3590
3698
  }
3591
3699
  break;
3592
- case TestTreeNodeBrand.Expect:
3593
- case TestTreeNodeBrand.When:
3594
- if (parent.brand === TestTreeNodeBrand.Describe) {
3700
+ case "expect":
3701
+ case "when":
3702
+ if (parent.brand === "describe") {
3595
3703
  return false;
3596
3704
  }
3597
3705
  break;
@@ -3630,13 +3738,29 @@ class CollectService {
3630
3738
  }
3631
3739
  #onNode(node, parent, testTree) {
3632
3740
  parent.children.push(node);
3633
- if (node.flags & TestTreeNodeFlags.Only) {
3741
+ if (node.flags & 1) {
3634
3742
  testTree.hasOnly = true;
3635
3743
  }
3636
3744
  EventEmitter.dispatch(["collect:node", { node }]);
3637
3745
  }
3638
3746
  }
3639
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
+
3640
3764
  class ProjectService {
3641
3765
  #compiler;
3642
3766
  #lastSeenProject = "";
@@ -3687,8 +3811,6 @@ class ProjectService {
3687
3811
  }
3688
3812
  #getDefaultCompilerOptions() {
3689
3813
  const defaultCompilerOptions = {
3690
- allowJs: true,
3691
- checkJs: true,
3692
3814
  exactOptionalPropertyTypes: true,
3693
3815
  jsx: this.#compiler.JsxEmit.Preserve,
3694
3816
  module: this.#compiler.ModuleKind.NodeNext,
@@ -3710,7 +3832,7 @@ class ProjectService {
3710
3832
  getDefaultProject(filePath) {
3711
3833
  const project = this.#service.getDefaultProjectForFile(this.#compiler.server.toNormalizedPath(filePath), true);
3712
3834
  const compilerOptions = project?.getCompilerOptions();
3713
- if (this.#resolvedConfig.checkSourceFiles && compilerOptions?.skipLibCheck) {
3835
+ if (this.#resolvedConfig.checkDeclarationFiles && compilerOptions?.skipLibCheck) {
3714
3836
  project?.setCompilerOptions({ ...compilerOptions, skipLibCheck: false });
3715
3837
  }
3716
3838
  return project;
@@ -3743,14 +3865,14 @@ class ProjectService {
3743
3865
  "project:uses",
3744
3866
  { compilerVersion: this.#compiler.version, projectConfigFilePath: configFileName },
3745
3867
  ]);
3868
+ if (configFileErrors && configFileErrors.length > 0) {
3869
+ EventEmitter.dispatch([
3870
+ "project:error",
3871
+ { diagnostics: Diagnostic.fromDiagnostics(configFileErrors) },
3872
+ ]);
3873
+ }
3746
3874
  }
3747
- if (configFileErrors && configFileErrors.length > 0) {
3748
- EventEmitter.dispatch([
3749
- "project:error",
3750
- { diagnostics: Diagnostic.fromDiagnostics(configFileErrors) },
3751
- ]);
3752
- }
3753
- if (this.#resolvedConfig.checkSourceFiles && !this.#seenTestFiles.has(filePath)) {
3875
+ if (!this.#seenTestFiles.has(filePath)) {
3754
3876
  this.#seenTestFiles.add(filePath);
3755
3877
  const languageService = this.getLanguageService(filePath);
3756
3878
  const program = languageService?.getProgram();
@@ -3762,10 +3884,16 @@ class ProjectService {
3762
3884
  if (program.isSourceFileFromExternalLibrary(sourceFile) || program.isSourceFileDefaultLibrary(sourceFile)) {
3763
3885
  return false;
3764
3886
  }
3887
+ if (this.#resolvedConfig.checkDeclarationFiles && sourceFile.isDeclarationFile) {
3888
+ return true;
3889
+ }
3890
+ if (Select.isFixtureFile(sourceFile.fileName, { ...this.#resolvedConfig, pathMatch: [] })) {
3891
+ return true;
3892
+ }
3765
3893
  if (Select.isTestFile(sourceFile.fileName, { ...this.#resolvedConfig, pathMatch: [] })) {
3766
3894
  return false;
3767
3895
  }
3768
- return true;
3896
+ return false;
3769
3897
  });
3770
3898
  const diagnostics = [];
3771
3899
  for (const sourceFile of sourceFilesToCheck) {
@@ -3798,24 +3926,26 @@ class SuppressedDiagnosticText {
3798
3926
  }
3799
3927
 
3800
3928
  class SuppressedService {
3801
- match(suppressedErrors, onDiagnostics) {
3802
- for (const suppressedError of suppressedErrors) {
3929
+ match(testTree, onDiagnostics) {
3930
+ if (!testTree.suppressedErrors) {
3931
+ return;
3932
+ }
3933
+ for (const suppressedError of testTree.suppressedErrors) {
3803
3934
  if (suppressedError.diagnostics.length === 0 || suppressedError.ignore) {
3804
3935
  continue;
3805
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);
3806
3942
  if (!suppressedError.argument?.text) {
3807
3943
  const text = SuppressedDiagnosticText.directiveRequires();
3808
- const origin = new DiagnosticOrigin(suppressedError.directive.start, suppressedError.directive.end, suppressedErrors.sourceFile);
3809
- onDiagnostics([Diagnostic.error(text, origin)]);
3944
+ onDiagnostics([Diagnostic.error(text, origin).add({ related })]);
3810
3945
  continue;
3811
3946
  }
3812
- const related = [
3813
- Diagnostic.error(SuppressedDiagnosticText.suppressedError(suppressedError.diagnostics.length)),
3814
- ...Diagnostic.fromDiagnostics(suppressedError.diagnostics, suppressedErrors.sourceFile),
3815
- ];
3816
3947
  if (suppressedError.diagnostics.length > 1) {
3817
3948
  const text = [SuppressedDiagnosticText.onlySingleError()];
3818
- const origin = new DiagnosticOrigin(suppressedError.directive.start, suppressedError.directive.end, suppressedErrors.sourceFile);
3819
3949
  onDiagnostics([Diagnostic.error(text, origin).add({ related })]);
3820
3950
  continue;
3821
3951
  }
@@ -3823,25 +3953,28 @@ class SuppressedService {
3823
3953
  if (Array.isArray(messageText)) {
3824
3954
  messageText = messageText.join("\n");
3825
3955
  }
3826
- if (!messageText.includes(suppressedError.argument.text)) {
3956
+ if (!this.#matchMessage(messageText, suppressedError.argument.text)) {
3827
3957
  const text = [SuppressedDiagnosticText.messageDidNotMatch()];
3828
- 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);
3829
3959
  onDiagnostics([Diagnostic.error(text, origin).add({ related })]);
3830
3960
  }
3831
3961
  }
3832
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
+ }
3833
3976
  }
3834
3977
 
3835
- var RunMode;
3836
- (function (RunMode) {
3837
- RunMode[RunMode["Normal"] = 0] = "Normal";
3838
- RunMode[RunMode["Fail"] = 1] = "Fail";
3839
- RunMode[RunMode["Only"] = 2] = "Only";
3840
- RunMode[RunMode["Skip"] = 4] = "Skip";
3841
- RunMode[RunMode["Todo"] = 8] = "Todo";
3842
- RunMode[RunMode["Void"] = 16] = "Void";
3843
- })(RunMode || (RunMode = {}));
3844
-
3845
3978
  class EnsureDiagnosticText {
3846
3979
  static argumentMustBeProvided(argumentNameText) {
3847
3980
  return `An argument for '${argumentNameText}' must be provided.`;
@@ -3965,21 +4098,15 @@ class ExpectDiagnosticText {
3965
4098
  }
3966
4099
  }
3967
4100
 
3968
- var Relation;
3969
- (function (Relation) {
3970
- Relation["Assignable"] = "assignable";
3971
- Relation["Identical"] = "identical";
3972
- })(Relation || (Relation = {}));
3973
-
3974
4101
  class MatchWorker {
3975
- assertion;
4102
+ assertionNode;
3976
4103
  #compiler;
3977
4104
  #signatureCache = new Map();
3978
4105
  typeChecker;
3979
- constructor(compiler, typeChecker, assertion) {
4106
+ constructor(compiler, typeChecker, assertionNode) {
3980
4107
  this.#compiler = compiler;
3981
4108
  this.typeChecker = typeChecker;
3982
- this.assertion = assertion;
4109
+ this.assertionNode = assertionNode;
3983
4110
  }
3984
4111
  checkHasApplicableIndexType(sourceNode, targetNode) {
3985
4112
  const sourceType = this.getType(sourceNode);
@@ -3995,13 +4122,13 @@ class MatchWorker {
3995
4122
  .some((property) => this.#compiler.unescapeLeadingUnderscores(property.escapedName) === propertyNameText);
3996
4123
  }
3997
4124
  checkIsAssignableTo(sourceNode, targetNode) {
3998
- return this.#checkIsRelatedTo(sourceNode, targetNode, Relation.Assignable);
4125
+ return this.#checkIsRelatedTo(sourceNode, targetNode, "assignable");
3999
4126
  }
4000
4127
  checkIsAssignableWith(sourceNode, targetNode) {
4001
- return this.#checkIsRelatedTo(targetNode, sourceNode, Relation.Assignable);
4128
+ return this.#checkIsRelatedTo(targetNode, sourceNode, "assignable");
4002
4129
  }
4003
4130
  checkIsIdenticalTo(sourceNode, targetNode) {
4004
- return (this.#checkIsRelatedTo(sourceNode, targetNode, Relation.Identical) &&
4131
+ return (this.#checkIsRelatedTo(sourceNode, targetNode, "identical") &&
4005
4132
  this.checkIsAssignableTo(sourceNode, targetNode) &&
4006
4133
  this.checkIsAssignableWith(sourceNode, targetNode));
4007
4134
  }
@@ -4009,9 +4136,9 @@ class MatchWorker {
4009
4136
  const sourceType = relation === "identical" ? this.#simplifyType(this.getType(sourceNode)) : this.getType(sourceNode);
4010
4137
  const targetType = relation === "identical" ? this.#simplifyType(this.getType(targetNode)) : this.getType(targetNode);
4011
4138
  switch (relation) {
4012
- case Relation.Assignable:
4139
+ case "assignable":
4013
4140
  return this.typeChecker.isTypeAssignableTo(sourceType, targetType);
4014
- case Relation.Identical:
4141
+ case "identical":
4015
4142
  return this.typeChecker.isTypeIdenticalTo(sourceType, targetType);
4016
4143
  }
4017
4144
  }
@@ -4052,9 +4179,9 @@ class MatchWorker {
4052
4179
  this.#compiler.isShorthandPropertyAssignment(symbol.valueDeclaration)) &&
4053
4180
  symbol.valueDeclaration.getStart() >= enclosingNode.getStart() &&
4054
4181
  symbol.valueDeclaration.getEnd() <= enclosingNode.getEnd()) {
4055
- return DiagnosticOrigin.fromNode(symbol.valueDeclaration.name, this.assertion);
4182
+ return DiagnosticOrigin.fromNode(symbol.valueDeclaration.name, this.assertionNode);
4056
4183
  }
4057
- return DiagnosticOrigin.fromNode(enclosingNode, this.assertion);
4184
+ return DiagnosticOrigin.fromNode(enclosingNode, this.assertionNode);
4058
4185
  }
4059
4186
  #simplifyType(type) {
4060
4187
  if (type.isUnionOrIntersection()) {
@@ -4089,10 +4216,10 @@ class ToAcceptProps {
4089
4216
  const signatures = matchWorker.getSignatures(sourceNode);
4090
4217
  return signatures.reduce((accumulator, signature, index) => {
4091
4218
  let diagnostic;
4092
- const introText = matchWorker.assertion.isNot
4219
+ const introText = matchWorker.assertionNode.isNot
4093
4220
  ? ExpectDiagnosticText.acceptsProps(isExpression)
4094
4221
  : ExpectDiagnosticText.doesNotAcceptProps(isExpression);
4095
- const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.assertion);
4222
+ const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.assertionNode);
4096
4223
  if (signatures.length > 1) {
4097
4224
  const signatureText = this.#typeChecker.signatureToString(signature, sourceNode);
4098
4225
  const overloadText = ExpectDiagnosticText.overloadGaveTheFollowingError(index + 1, signatures.length, signatureText);
@@ -4102,7 +4229,7 @@ class ToAcceptProps {
4102
4229
  diagnostic = Diagnostic.error([introText], origin);
4103
4230
  }
4104
4231
  const { diagnostics, isMatch } = this.#explainProperties(matchWorker, signature, targetNode, diagnostic);
4105
- if (matchWorker.assertion.isNot ? isMatch : !isMatch) {
4232
+ if (matchWorker.assertionNode.isNot ? isMatch : !isMatch) {
4106
4233
  accumulator.push(...diagnostics);
4107
4234
  }
4108
4235
  return accumulator;
@@ -4210,7 +4337,7 @@ class ToAcceptProps {
4210
4337
  if (sourceType != null && isUnionType(this.#compiler, sourceType)) {
4211
4338
  let accumulator = [];
4212
4339
  const isMatch = sourceType.types.some((sourceType) => {
4213
- const text = matchWorker.assertion.isNot
4340
+ const text = matchWorker.assertionNode.isNot
4214
4341
  ? ExpectDiagnosticText.isAssignableWith(sourceTypeText, targetTypeText)
4215
4342
  : ExpectDiagnosticText.isNotAssignableWith(sourceTypeText, targetTypeText);
4216
4343
  const { diagnostics, isMatch } = explain(sourceType, targetType, diagnostic.extendWith(text));
@@ -4265,10 +4392,10 @@ class RelationMatcherBase {
4265
4392
  explain(matchWorker, sourceNode, targetNode) {
4266
4393
  const sourceTypeText = matchWorker.getTypeText(sourceNode);
4267
4394
  const targetTypeText = matchWorker.getTypeText(targetNode);
4268
- const text = matchWorker.assertion.isNot
4395
+ const text = matchWorker.assertionNode.isNot
4269
4396
  ? this.explainText(sourceTypeText, targetTypeText)
4270
4397
  : this.explainNotText(sourceTypeText, targetTypeText);
4271
- const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.assertion);
4398
+ const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.assertionNode);
4272
4399
  return [Diagnostic.error(text, origin)];
4273
4400
  }
4274
4401
  }
@@ -4315,18 +4442,22 @@ class ToBeApplicable {
4315
4442
  }
4316
4443
  return text;
4317
4444
  }
4318
- #explain(matchWorker, sourceNode) {
4319
- const targetText = this.#resolveTargetText(matchWorker.assertion.matcherNode.parent);
4445
+ #explain(matchWorker) {
4446
+ const targetText = this.#resolveTargetText(matchWorker.assertionNode.matcherNode.parent);
4320
4447
  const diagnostics = [];
4321
- if (matchWorker.assertion.abilityDiagnostics.size > 0) {
4322
- 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) {
4323
4451
  const text = [ExpectDiagnosticText.cannotBeApplied(targetText), getDiagnosticMessageText(diagnostic)];
4324
- const origin = DiagnosticOrigin.fromNode(sourceNode);
4325
- 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 }));
4326
4457
  }
4327
4458
  }
4328
4459
  else {
4329
- const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertion);
4460
+ const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertionNode);
4330
4461
  diagnostics.push(Diagnostic.error(ExpectDiagnosticText.canBeApplied(targetText), origin));
4331
4462
  }
4332
4463
  return diagnostics;
@@ -4343,8 +4474,8 @@ class ToBeApplicable {
4343
4474
  return;
4344
4475
  }
4345
4476
  return {
4346
- explain: () => this.#explain(matchWorker, sourceNode),
4347
- isMatch: matchWorker.assertion.abilityDiagnostics.size === 0,
4477
+ explain: () => this.#explain(matchWorker),
4478
+ isMatch: matchWorker.assertionNode.abilityDiagnostics.size === 0,
4348
4479
  };
4349
4480
  }
4350
4481
  }
@@ -4389,32 +4520,25 @@ class AbilityMatcherBase {
4389
4520
  const isExpression = nodeBelongsToArgumentList(this.compiler, sourceNode);
4390
4521
  const targetText = this.#resolveTargetText(targetNodes);
4391
4522
  const diagnostics = [];
4392
- if (matchWorker.assertion.abilityDiagnostics.size > 0) {
4393
- 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)];
4394
4526
  let origin;
4395
- const text = [];
4396
- if (isDiagnosticWithLocation(diagnostic) && diagnosticBelongsToNode(diagnostic, sourceNode)) {
4397
- origin = new DiagnosticOrigin(diagnostic.start, getTextSpanEnd(diagnostic), sourceNode.getSourceFile(), matchWorker.assertion);
4398
- text.push(getDiagnosticMessageText(diagnostic));
4527
+ if (isDiagnosticWithLocation(diagnostic) && diagnosticBelongsToNode(diagnostic, targetNodes)) {
4528
+ origin = new DiagnosticOrigin(diagnostic.start, getTextSpanEnd(diagnostic), sourceNode.getSourceFile(), matchWorker.assertionNode);
4399
4529
  }
4400
4530
  else {
4401
- if (isDiagnosticWithLocation(diagnostic) && diagnosticBelongsToNode(diagnostic, targetNodes)) {
4402
- origin = new DiagnosticOrigin(diagnostic.start, getTextSpanEnd(diagnostic), sourceNode.getSourceFile(), matchWorker.assertion);
4403
- }
4404
- else {
4405
- origin = DiagnosticOrigin.fromAssertion(matchWorker.assertion);
4406
- }
4407
- text.push(this.explainNotText(isExpression, targetText), getDiagnosticMessageText(diagnostic));
4531
+ origin = DiagnosticOrigin.fromAssertion(matchWorker.assertionNode);
4408
4532
  }
4409
4533
  let related;
4410
4534
  if (diagnostic.relatedInformation != null) {
4411
- related = Diagnostic.fromDiagnostics(diagnostic.relatedInformation, sourceNode.getSourceFile());
4535
+ related = Diagnostic.fromDiagnostics(diagnostic.relatedInformation);
4412
4536
  }
4413
4537
  diagnostics.push(Diagnostic.error(text.flat(), origin).add({ related }));
4414
4538
  }
4415
4539
  }
4416
4540
  else {
4417
- const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertion);
4541
+ const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertionNode);
4418
4542
  diagnostics.push(Diagnostic.error(this.explainText(isExpression, targetText), origin));
4419
4543
  }
4420
4544
  return diagnostics;
@@ -4454,7 +4578,7 @@ class ToBeCallableWith extends AbilityMatcherBase {
4454
4578
  }
4455
4579
  return {
4456
4580
  explain: () => this.explain(matchWorker, sourceNode, targetNodes),
4457
- isMatch: matchWorker.assertion.abilityDiagnostics.size === 0,
4581
+ isMatch: matchWorker.assertionNode.abilityDiagnostics.size === 0,
4458
4582
  };
4459
4583
  }
4460
4584
  }
@@ -4489,7 +4613,7 @@ class ToBeConstructableWith extends AbilityMatcherBase {
4489
4613
  }
4490
4614
  return {
4491
4615
  explain: () => this.explain(matchWorker, sourceNode, targetNodes),
4492
- isMatch: matchWorker.assertion.abilityDiagnostics.size === 0,
4616
+ isMatch: matchWorker.assertionNode.abilityDiagnostics.size === 0,
4493
4617
  };
4494
4618
  }
4495
4619
  }
@@ -4509,8 +4633,8 @@ class ToHaveProperty {
4509
4633
  else {
4510
4634
  propertyNameText = `[${this.#compiler.unescapeLeadingUnderscores(targetType.symbol.escapedName)}]`;
4511
4635
  }
4512
- const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.assertion);
4513
- return matchWorker.assertion.isNot
4636
+ const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.assertionNode);
4637
+ return matchWorker.assertionNode.isNot
4514
4638
  ? [Diagnostic.error(ExpectDiagnosticText.hasProperty(sourceTypeText, propertyNameText), origin)]
4515
4639
  : [Diagnostic.error(ExpectDiagnosticText.doesNotHaveProperty(sourceTypeText, propertyNameText), origin)];
4516
4640
  }
@@ -4560,28 +4684,28 @@ class ToRaiseError {
4560
4684
  }
4561
4685
  #explain(matchWorker, sourceNode, targetNodes) {
4562
4686
  const isExpression = nodeBelongsToArgumentList(this.#compiler, sourceNode);
4563
- const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertion);
4564
- if (matchWorker.assertion.diagnostics.size === 0) {
4687
+ const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertionNode);
4688
+ if (matchWorker.assertionNode.diagnostics.size === 0) {
4565
4689
  const text = ExpectDiagnosticText.didNotRaiseError(isExpression);
4566
4690
  return [Diagnostic.error(text, origin)];
4567
4691
  }
4568
- if (matchWorker.assertion.diagnostics.size !== targetNodes.length) {
4569
- const count = matchWorker.assertion.diagnostics.size;
4692
+ if (matchWorker.assertionNode.diagnostics.size !== targetNodes.length) {
4693
+ const count = matchWorker.assertionNode.diagnostics.size;
4570
4694
  const text = ExpectDiagnosticText.raisedError(isExpression, count, targetNodes.length);
4571
4695
  const related = [
4572
4696
  Diagnostic.error(ExpectDiagnosticText.raisedTypeError(count)),
4573
- ...Diagnostic.fromDiagnostics([...matchWorker.assertion.diagnostics]),
4697
+ ...Diagnostic.fromDiagnostics([...matchWorker.assertionNode.diagnostics]),
4574
4698
  ];
4575
4699
  return [Diagnostic.error(text, origin).add({ related })];
4576
4700
  }
4577
- return [...matchWorker.assertion.diagnostics].reduce((accumulator, diagnostic, index) => {
4701
+ return [...matchWorker.assertionNode.diagnostics].reduce((accumulator, diagnostic, index) => {
4578
4702
  const targetNode = targetNodes[index];
4579
4703
  const isMatch = this.#matchExpectedError(diagnostic, targetNode);
4580
- if (matchWorker.assertion.isNot ? isMatch : !isMatch) {
4581
- const text = matchWorker.assertion.isNot
4704
+ if (matchWorker.assertionNode.isNot ? isMatch : !isMatch) {
4705
+ const text = matchWorker.assertionNode.isNot
4582
4706
  ? ExpectDiagnosticText.raisedMatchingError(isExpression)
4583
4707
  : ExpectDiagnosticText.didNotRaiseMatchingError(isExpression);
4584
- const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.assertion);
4708
+ const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.assertionNode);
4585
4709
  const related = [
4586
4710
  Diagnostic.error(ExpectDiagnosticText.raisedTypeError()),
4587
4711
  ...Diagnostic.fromDiagnostics([diagnostic]),
@@ -4609,12 +4733,12 @@ class ToRaiseError {
4609
4733
  }
4610
4734
  let isMatch;
4611
4735
  if (targetNodes.length === 0) {
4612
- isMatch = matchWorker.assertion.diagnostics.size > 0;
4736
+ isMatch = matchWorker.assertionNode.diagnostics.size > 0;
4613
4737
  }
4614
4738
  else {
4615
4739
  isMatch =
4616
- matchWorker.assertion.diagnostics.size === targetNodes.length &&
4617
- [...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]));
4618
4742
  }
4619
4743
  return {
4620
4744
  explain: () => this.#explain(matchWorker, sourceNode, targetNodes),
@@ -4664,16 +4788,16 @@ class ExpectService {
4664
4788
  this.toHaveProperty = new ToHaveProperty(compiler);
4665
4789
  this.toRaiseError = new ToRaiseError(compiler);
4666
4790
  }
4667
- match(assertion, onDiagnostics) {
4668
- const matcherNameText = assertion.matcherNameNode.name.text;
4669
- 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)) {
4670
4794
  return;
4671
4795
  }
4672
- const matchWorker = new MatchWorker(this.#compiler, this.#typeChecker, assertion);
4673
- if (!(matcherNameText === "toRaiseError" && assertion.isNot === false) &&
4796
+ const matchWorker = new MatchWorker(this.#compiler, this.#typeChecker, assertionNode);
4797
+ if (!(matcherNameText === "toRaiseError" && assertionNode.isNot === false) &&
4674
4798
  this.#reject.argumentType([
4675
- ["source", assertion.source[0]],
4676
- ["target", assertion.target?.[0]],
4799
+ ["source", assertionNode.source[0]],
4800
+ ["target", assertionNode.target?.[0]],
4677
4801
  ], onDiagnostics)) {
4678
4802
  return;
4679
4803
  }
@@ -4682,29 +4806,29 @@ class ExpectService {
4682
4806
  case "toBe":
4683
4807
  case "toBeAssignableTo":
4684
4808
  case "toBeAssignableWith":
4685
- if (!argumentOrTypeArgumentIsProvided("target", "Target", assertion.target?.[0], assertion.matcherNameNode.name, onDiagnostics)) {
4809
+ if (!argumentOrTypeArgumentIsProvided("target", "Target", assertionNode.target?.[0], assertionNode.matcherNameNode.name, onDiagnostics)) {
4686
4810
  return;
4687
4811
  }
4688
- 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);
4689
4813
  case "toBeApplicable":
4690
- return this.toBeApplicable.match(matchWorker, assertion.source[0], onDiagnostics);
4814
+ return this.toBeApplicable.match(matchWorker, assertionNode.source[0], onDiagnostics);
4691
4815
  case "toBeCallableWith":
4692
4816
  case "toBeConstructableWith":
4693
4817
  case "toRaiseError":
4694
- return this[matcherNameText].match(matchWorker, assertion.source[0], assertion.target, onDiagnostics);
4818
+ return this[matcherNameText].match(matchWorker, assertionNode.source[0], assertionNode.target, onDiagnostics);
4695
4819
  case "toHaveProperty":
4696
- if (!argumentIsProvided("key", assertion.target?.[0], assertion.matcherNameNode.name, onDiagnostics)) {
4820
+ if (!argumentIsProvided("key", assertionNode.target?.[0], assertionNode.matcherNameNode.name, onDiagnostics)) {
4697
4821
  return;
4698
4822
  }
4699
- 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);
4700
4824
  default:
4701
- this.#onMatcherIsNotSupported(matcherNameText, assertion, onDiagnostics);
4825
+ this.#onMatcherIsNotSupported(matcherNameText, assertionNode, onDiagnostics);
4702
4826
  }
4703
4827
  return;
4704
4828
  }
4705
- #onMatcherIsNotSupported(matcherNameText, assertion, onDiagnostics) {
4829
+ #onMatcherIsNotSupported(matcherNameText, assertionNode, onDiagnostics) {
4706
4830
  const text = ExpectDiagnosticText.matcherIsNotSupported(matcherNameText);
4707
- const origin = DiagnosticOrigin.fromNode(assertion.matcherNameNode.name);
4831
+ const origin = DiagnosticOrigin.fromNode(assertionNode.matcherNameNode.name);
4708
4832
  onDiagnostics(Diagnostic.error(text, origin));
4709
4833
  }
4710
4834
  }
@@ -4825,82 +4949,145 @@ class WhenService {
4825
4949
  }
4826
4950
  }
4827
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
+
4828
5011
  class TestTreeWalker {
4829
5012
  #cancellationToken;
4830
5013
  #compiler;
4831
5014
  #expectService;
4832
5015
  #hasOnly;
4833
- #onTaskDiagnostics;
5016
+ #onFileDiagnostics;
4834
5017
  #position;
4835
5018
  #resolvedConfig;
4836
5019
  #whenService;
4837
- constructor(compiler, typeChecker, resolvedConfig, onTaskDiagnostics, options) {
5020
+ constructor(compiler, typeChecker, resolvedConfig, onFileDiagnostics, options) {
4838
5021
  this.#compiler = compiler;
4839
5022
  this.#resolvedConfig = resolvedConfig;
4840
- this.#onTaskDiagnostics = onTaskDiagnostics;
5023
+ this.#onFileDiagnostics = onFileDiagnostics;
4841
5024
  this.#cancellationToken = options.cancellationToken;
4842
5025
  this.#hasOnly = options.hasOnly || resolvedConfig.only != null || options.position != null;
4843
5026
  this.#position = options.position;
4844
5027
  const reject = new Reject(compiler, typeChecker, resolvedConfig);
4845
5028
  this.#expectService = new ExpectService(compiler, typeChecker, reject);
4846
- this.#whenService = new WhenService(reject, onTaskDiagnostics);
5029
+ this.#whenService = new WhenService(reject, onFileDiagnostics);
4847
5030
  }
4848
- async #resolveRunMode(mode, node) {
4849
- const directiveRanges = node.getDirectiveRanges(this.#compiler);
4850
- 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);
4851
5034
  if (inlineConfig?.if?.target != null && !Version.isIncluded(this.#compiler.version, inlineConfig.if.target)) {
4852
- mode |= RunMode.Void;
5035
+ flags |= 8;
4853
5036
  }
4854
- if (node.flags & TestTreeNodeFlags.Fail) {
4855
- mode |= RunMode.Fail;
4856
- }
4857
- if (node.flags & TestTreeNodeFlags.Only ||
5037
+ if (node.flags & 1 ||
4858
5038
  (this.#resolvedConfig.only != null && node.name.toLowerCase().includes(this.#resolvedConfig.only.toLowerCase()))) {
4859
- mode |= RunMode.Only;
5039
+ flags |= 1;
4860
5040
  }
4861
- if (node.flags & TestTreeNodeFlags.Skip ||
5041
+ if (node.flags & 2 ||
4862
5042
  (this.#resolvedConfig.skip != null && node.name.toLowerCase().includes(this.#resolvedConfig.skip.toLowerCase()))) {
4863
- mode |= RunMode.Skip;
5043
+ flags |= 2;
4864
5044
  }
4865
- if (node.flags & TestTreeNodeFlags.Todo) {
4866
- mode |= RunMode.Todo;
5045
+ if (node.flags & 4) {
5046
+ flags |= 4;
4867
5047
  }
4868
5048
  if (this.#position != null && node.node.getStart() === this.#position) {
4869
- mode |= RunMode.Only;
4870
- mode &= ~RunMode.Skip;
5049
+ flags |= 1;
5050
+ flags &= -3;
4871
5051
  }
4872
- return mode;
5052
+ return flags;
4873
5053
  }
4874
- async visit(nodes, runMode, parentResult) {
5054
+ async visit(nodes, runModeFlags, parentResult) {
4875
5055
  for (const node of nodes) {
4876
5056
  if (this.#cancellationToken?.isCancellationRequested) {
4877
5057
  break;
4878
5058
  }
5059
+ const fixmeDirective = Directive.getDirectiveRange(this.#compiler, node, "fixme");
5060
+ if (fixmeDirective) {
5061
+ FixmeService.start(fixmeDirective, node);
5062
+ }
4879
5063
  switch (node.brand) {
4880
- case TestTreeNodeBrand.Describe:
4881
- await this.#visitDescribe(node, runMode, parentResult);
5064
+ case "describe":
5065
+ await this.#visitDescribe(node, runModeFlags, parentResult);
4882
5066
  break;
4883
- case TestTreeNodeBrand.Test:
4884
- await this.#visitTest(node, runMode, parentResult);
5067
+ case "test":
5068
+ await this.#visitTest(node, runModeFlags, parentResult);
4885
5069
  break;
4886
- case TestTreeNodeBrand.Expect:
4887
- await this.#visitAssertion(node, runMode, parentResult);
5070
+ case "expect":
5071
+ await this.#visitExpect(node, runModeFlags, parentResult);
4888
5072
  break;
4889
- case TestTreeNodeBrand.When:
5073
+ case "when":
4890
5074
  this.#visitWhen(node);
4891
5075
  break;
4892
5076
  }
5077
+ if (fixmeDirective) {
5078
+ FixmeService.end(fixmeDirective, node, this.#onFileDiagnostics);
5079
+ }
4893
5080
  }
4894
5081
  }
4895
- async #visitAssertion(assertion, runMode, parentResult) {
4896
- await this.visit(assertion.children, runMode, parentResult);
4897
- runMode = await this.#resolveRunMode(runMode, assertion);
4898
- 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) {
4899
5086
  return;
4900
5087
  }
4901
- const expectResult = new ExpectResult(assertion, parentResult);
5088
+ const expectResult = new ExpectResult(expect, parentResult);
4902
5089
  EventEmitter.dispatch(["expect:start", { result: expectResult }]);
4903
- if (runMode & RunMode.Skip || (this.#hasOnly && !(runMode & RunMode.Only))) {
5090
+ if (runModeFlags & 2 || (this.#hasOnly && !(runModeFlags & 1))) {
4904
5091
  EventEmitter.dispatch(["expect:skip", { result: expectResult }]);
4905
5092
  return;
4906
5093
  }
@@ -4910,59 +5097,57 @@ class TestTreeWalker {
4910
5097
  { diagnostics: Array.isArray(diagnostics) ? diagnostics : [diagnostics], result: expectResult },
4911
5098
  ]);
4912
5099
  };
4913
- if (assertion.diagnostics.size > 0 && assertion.matcherNameNode.name.text !== "toRaiseError") {
4914
- onExpectDiagnostics(Diagnostic.fromDiagnostics([...assertion.diagnostics]));
5100
+ if (expect.diagnostics.size > 0 && expect.matcherNameNode.name.text !== "toRaiseError") {
5101
+ onExpectDiagnostics(Diagnostic.fromDiagnostics([...expect.diagnostics]));
4915
5102
  return;
4916
5103
  }
4917
- const matchResult = this.#expectService.match(assertion, onExpectDiagnostics);
5104
+ const matchResult = this.#expectService.match(expect, onExpectDiagnostics);
4918
5105
  if (!matchResult) {
4919
5106
  return;
4920
5107
  }
4921
- if (assertion.isNot ? !matchResult.isMatch : matchResult.isMatch) {
4922
- if (runMode & RunMode.Fail) {
4923
- const text = ["The assertion was supposed to fail, but it passed.", "Consider removing the '.fail' flag."];
4924
- const origin = DiagnosticOrigin.fromNode(assertion.node.expression.name);
4925
- onExpectDiagnostics(Diagnostic.error(text, origin));
4926
- }
4927
- else {
4928
- EventEmitter.dispatch(["expect:pass", { result: expectResult }]);
4929
- }
5108
+ const isPass = expect.isNot ? !matchResult.isMatch : matchResult.isMatch;
5109
+ if (FixmeService.isFixme(expect, isPass)) {
5110
+ EventEmitter.dispatch(["expect:fixme", { result: expectResult }]);
5111
+ return;
4930
5112
  }
4931
- else if (runMode & RunMode.Fail) {
5113
+ if (isPass) {
4932
5114
  EventEmitter.dispatch(["expect:pass", { result: expectResult }]);
4933
5115
  }
4934
5116
  else {
4935
5117
  EventEmitter.dispatch(["expect:fail", { diagnostics: matchResult.explain(), result: expectResult }]);
4936
5118
  }
4937
5119
  }
4938
- async #visitDescribe(describe, runMode, parentResult) {
4939
- runMode = await this.#resolveRunMode(runMode, describe);
4940
- if (runMode & RunMode.Void) {
5120
+ async #visitDescribe(describe, runModeFlags, parentResult) {
5121
+ runModeFlags = await this.#resolveRunMode(runModeFlags, describe);
5122
+ if (runModeFlags & 8) {
4941
5123
  return;
4942
5124
  }
4943
5125
  const describeResult = new DescribeResult(describe, parentResult);
4944
5126
  EventEmitter.dispatch(["describe:start", { result: describeResult }]);
4945
- if (!(runMode & RunMode.Skip || (this.#hasOnly && !(runMode & RunMode.Only)) || runMode & RunMode.Todo) &&
5127
+ if (!(runModeFlags & 2 ||
5128
+ (this.#hasOnly && !(runModeFlags & 1)) ||
5129
+ runModeFlags & 4) &&
4946
5130
  describe.diagnostics.size > 0) {
4947
- this.#onTaskDiagnostics(Diagnostic.fromDiagnostics([...describe.diagnostics]));
5131
+ this.#onFileDiagnostics(Diagnostic.fromDiagnostics([...describe.diagnostics]));
4948
5132
  }
4949
5133
  else {
4950
- await this.visit(describe.children, runMode, describeResult);
5134
+ await this.visit(describe.children, runModeFlags, describeResult);
4951
5135
  }
4952
5136
  EventEmitter.dispatch(["describe:end", { result: describeResult }]);
4953
5137
  }
4954
- async #visitTest(test, runMode, parentResult) {
4955
- runMode = await this.#resolveRunMode(runMode, test);
4956
- if (runMode & RunMode.Void) {
5138
+ async #visitTest(test, runModeFlags, parentResult) {
5139
+ runModeFlags = await this.#resolveRunMode(runModeFlags, test);
5140
+ if (runModeFlags & 8) {
4957
5141
  return;
4958
5142
  }
4959
5143
  const testResult = new TestResult(test, parentResult);
4960
5144
  EventEmitter.dispatch(["test:start", { result: testResult }]);
4961
- if (runMode & RunMode.Todo) {
5145
+ if (runModeFlags & 4) {
4962
5146
  EventEmitter.dispatch(["test:todo", { result: testResult }]);
4963
5147
  return;
4964
5148
  }
4965
- 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) {
4966
5151
  EventEmitter.dispatch([
4967
5152
  "test:error",
4968
5153
  {
@@ -4972,24 +5157,29 @@ class TestTreeWalker {
4972
5157
  ]);
4973
5158
  return;
4974
5159
  }
4975
- await this.visit(test.children, runMode, testResult);
4976
- 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))) {
4977
5162
  EventEmitter.dispatch(["test:skip", { result: testResult }]);
4978
5163
  return;
4979
5164
  }
4980
- if (testResult.expectCount.failed > 0) {
4981
- 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;
4982
5169
  }
4983
- else {
5170
+ if (isPass) {
4984
5171
  EventEmitter.dispatch(["test:pass", { result: testResult }]);
4985
5172
  }
5173
+ else {
5174
+ EventEmitter.dispatch(["test:fail", { result: testResult }]);
5175
+ }
4986
5176
  }
4987
5177
  #visitWhen(when) {
4988
5178
  this.#whenService.action(when);
4989
5179
  }
4990
5180
  }
4991
5181
 
4992
- class TaskRunner {
5182
+ class FileRunner {
4993
5183
  #collectService;
4994
5184
  #compiler;
4995
5185
  #projectService;
@@ -5002,89 +5192,87 @@ class TaskRunner {
5002
5192
  this.#collectService = new CollectService(compiler, this.#projectService, this.#resolvedConfig);
5003
5193
  }
5004
5194
  #onDiagnostics(diagnostics, result) {
5005
- EventEmitter.dispatch(["task:error", { diagnostics, result }]);
5195
+ EventEmitter.dispatch(["file:error", { diagnostics, result }]);
5006
5196
  }
5007
- async run(task, cancellationToken) {
5197
+ async run(file, cancellationToken) {
5008
5198
  if (cancellationToken.isCancellationRequested) {
5009
5199
  return;
5010
5200
  }
5011
- this.#projectService.openFile(task.filePath, undefined, this.#resolvedConfig.rootPath);
5012
- const taskResult = new TaskResult(task);
5013
- EventEmitter.dispatch(["task:start", { result: taskResult }]);
5014
- await this.#run(task, taskResult, cancellationToken);
5015
- EventEmitter.dispatch(["task:end", { result: taskResult }]);
5016
- 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);
5017
5207
  }
5018
- async #resolveTaskFacts(task, taskResult, runMode = RunMode.Normal) {
5019
- const languageService = this.#projectService.getLanguageService(task.filePath);
5020
- 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);
5021
5211
  if (syntacticDiagnostics != null && syntacticDiagnostics.length > 0) {
5022
- this.#onDiagnostics(Diagnostic.fromDiagnostics(syntacticDiagnostics), taskResult);
5212
+ this.#onDiagnostics(Diagnostic.fromDiagnostics(syntacticDiagnostics), fileResult);
5023
5213
  return;
5024
5214
  }
5025
- const semanticDiagnostics = languageService?.getSemanticDiagnostics(task.filePath);
5215
+ const semanticDiagnostics = languageService?.getSemanticDiagnostics(file.path);
5026
5216
  const program = languageService?.getProgram();
5027
5217
  const typeChecker = program?.getTypeChecker();
5028
- const sourceFile = program?.getSourceFile(task.filePath);
5218
+ const sourceFile = program?.getSourceFile(file.path);
5029
5219
  if (!sourceFile) {
5030
5220
  return;
5031
5221
  }
5032
- const testTree = this.#collectService.createTestTree(sourceFile, semanticDiagnostics);
5033
- const directiveRanges = testTree.getDirectiveRanges(this.#compiler);
5222
+ const directiveRanges = Directive.getDirectiveRanges(this.#compiler, sourceFile);
5034
5223
  const inlineConfig = await Directive.getInlineConfig(directiveRanges);
5035
5224
  if (inlineConfig?.if?.target != null && !Version.isIncluded(this.#compiler.version, inlineConfig.if.target)) {
5036
- runMode |= RunMode.Skip;
5037
- }
5038
- if (testTree.suppressedErrors != null) {
5039
- this.#suppressedService.match(testTree.suppressedErrors, (diagnostics) => {
5040
- this.#onDiagnostics(diagnostics, taskResult);
5041
- });
5225
+ runModeFlags |= 2;
5042
5226
  }
5043
5227
  if (inlineConfig?.template) {
5044
5228
  if (semanticDiagnostics != null && semanticDiagnostics.length > 0) {
5045
- this.#onDiagnostics(Diagnostic.fromDiagnostics(semanticDiagnostics), taskResult);
5229
+ this.#onDiagnostics(Diagnostic.fromDiagnostics(semanticDiagnostics), fileResult);
5046
5230
  return;
5047
5231
  }
5048
- const moduleSpecifier = pathToFileURL(task.filePath).toString();
5232
+ const moduleSpecifier = pathToFileURL(file.path).toString();
5049
5233
  const testText = (await import(moduleSpecifier))?.default;
5050
5234
  if (typeof testText !== "string") {
5051
- 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);
5052
5236
  return;
5053
5237
  }
5054
- this.#projectService.openFile(task.filePath, testText, this.#resolvedConfig.rootPath);
5055
- return this.#resolveTaskFacts(task, taskResult, runMode);
5238
+ this.#projectService.openFile(file.path, testText, this.#resolvedConfig.rootPath);
5239
+ return this.#resolveFileFacts(file, fileResult, runModeFlags);
5056
5240
  }
5057
- 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 };
5058
5246
  }
5059
- async #run(task, taskResult, cancellationToken) {
5060
- if (!existsSync(task.filePath)) {
5061
- 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);
5062
5250
  return;
5063
5251
  }
5064
- const facts = await this.#resolveTaskFacts(task, taskResult);
5252
+ const facts = await this.#resolveFileFacts(file, fileResult, 0);
5065
5253
  if (!facts) {
5066
5254
  return;
5067
5255
  }
5068
5256
  if (facts.testTree.diagnostics.size > 0) {
5069
- this.#onDiagnostics(Diagnostic.fromDiagnostics([...facts.testTree.diagnostics]), taskResult);
5257
+ this.#onDiagnostics(Diagnostic.fromDiagnostics([...facts.testTree.diagnostics]), fileResult);
5070
5258
  return;
5071
5259
  }
5072
- const onTaskDiagnostics = (diagnostics) => {
5073
- this.#onDiagnostics(diagnostics, taskResult);
5260
+ const onFileDiagnostics = (diagnostics) => {
5261
+ this.#onDiagnostics(diagnostics, fileResult);
5074
5262
  };
5075
- const testTreeWalker = new TestTreeWalker(this.#compiler, facts.typeChecker, this.#resolvedConfig, onTaskDiagnostics, {
5263
+ const testTreeWalker = new TestTreeWalker(this.#compiler, facts.typeChecker, this.#resolvedConfig, onFileDiagnostics, {
5076
5264
  cancellationToken,
5077
5265
  hasOnly: facts.testTree.hasOnly,
5078
- position: task.position,
5266
+ position: file.position,
5079
5267
  });
5080
- await testTreeWalker.visit(facts.testTree.children, facts.runMode, undefined);
5268
+ await testTreeWalker.visit(facts.testTree.children, facts.runModeFlags, undefined);
5081
5269
  }
5082
5270
  }
5083
5271
 
5084
5272
  class Runner {
5085
5273
  #eventEmitter = new EventEmitter();
5086
5274
  #resolvedConfig;
5087
- static version = "4.2.0";
5275
+ static version = "5.0.0-beta.0";
5088
5276
  constructor(resolvedConfig) {
5089
5277
  this.#resolvedConfig = resolvedConfig;
5090
5278
  }
@@ -5092,7 +5280,7 @@ class Runner {
5092
5280
  const resultHandler = new ResultHandler();
5093
5281
  this.#eventEmitter.addHandler(resultHandler);
5094
5282
  if (this.#resolvedConfig.failFast) {
5095
- const cancellationHandler = new CancellationHandler(cancellationToken, CancellationReason.FailFast);
5283
+ const cancellationHandler = new CancellationHandler(cancellationToken, "failFast");
5096
5284
  this.#eventEmitter.addHandler(cancellationHandler);
5097
5285
  }
5098
5286
  }
@@ -5121,39 +5309,39 @@ class Runner {
5121
5309
  }
5122
5310
  }
5123
5311
  }
5124
- async run(testFiles, cancellationToken = new CancellationToken()) {
5125
- 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)));
5126
5314
  this.#addHandlers(cancellationToken);
5127
5315
  await this.#addReporters();
5128
- await this.#run(tasks, cancellationToken);
5316
+ await this.#run(fileLocations, cancellationToken);
5129
5317
  if (this.#resolvedConfig.watch) {
5130
- await this.#watch(tasks, cancellationToken);
5318
+ await this.#watch(fileLocations, cancellationToken);
5131
5319
  }
5132
5320
  this.#eventEmitter.removeReporters();
5133
5321
  this.#eventEmitter.removeHandlers();
5134
5322
  }
5135
- async #run(tasks, cancellationToken) {
5136
- const result = new Result(tasks);
5323
+ async #run(files, cancellationToken) {
5324
+ const result = new Result(files);
5137
5325
  EventEmitter.dispatch(["run:start", { result }]);
5138
5326
  for (const target of this.#resolvedConfig.target) {
5139
- const targetResult = new TargetResult(target, tasks);
5327
+ const targetResult = new TargetResult(target, files);
5140
5328
  EventEmitter.dispatch(["target:start", { result: targetResult }]);
5141
5329
  const compiler = await Store.load(target);
5142
5330
  if (compiler) {
5143
- const taskRunner = new TaskRunner(compiler, this.#resolvedConfig);
5144
- for (const task of tasks) {
5145
- 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);
5146
5334
  }
5147
5335
  }
5148
5336
  EventEmitter.dispatch(["target:end", { result: targetResult }]);
5149
5337
  }
5150
5338
  EventEmitter.dispatch(["run:end", { result }]);
5151
- if (cancellationToken.reason === CancellationReason.FailFast) {
5339
+ if (cancellationToken.reason === "failFast") {
5152
5340
  cancellationToken.reset();
5153
5341
  }
5154
5342
  }
5155
- async #watch(testFiles, cancellationToken) {
5156
- const watchService = new WatchService(this.#resolvedConfig, testFiles);
5343
+ async #watch(files, cancellationToken) {
5344
+ const watchService = new WatchService(this.#resolvedConfig, files);
5157
5345
  for await (const testFiles of watchService.watch(cancellationToken)) {
5158
5346
  await this.#run(testFiles, cancellationToken);
5159
5347
  }
@@ -5163,14 +5351,14 @@ class Runner {
5163
5351
  class Cli {
5164
5352
  #eventEmitter = new EventEmitter();
5165
5353
  async run(commandLine, cancellationToken = new CancellationToken()) {
5166
- const cancellationHandler = new CancellationHandler(cancellationToken, CancellationReason.ConfigError);
5354
+ const cancellationHandler = new CancellationHandler(cancellationToken, "configError");
5167
5355
  this.#eventEmitter.addHandler(cancellationHandler);
5168
5356
  const exitCodeHandler = new ExitCodeHandler();
5169
5357
  this.#eventEmitter.addHandler(exitCodeHandler);
5170
5358
  const setupReporter = new SetupReporter();
5171
5359
  this.#eventEmitter.addReporter(setupReporter);
5172
5360
  if (commandLine.includes("--help")) {
5173
- const options = Options.for(OptionGroup.CommandLine);
5361
+ const options = Options.for(2);
5174
5362
  OutputService.writeMessage(helpText(options, Runner.version));
5175
5363
  return;
5176
5364
  }
@@ -5198,7 +5386,7 @@ class Cli {
5198
5386
  return;
5199
5387
  }
5200
5388
  do {
5201
- if (cancellationToken.reason === CancellationReason.ConfigChange) {
5389
+ if (cancellationToken.reason === "configChange") {
5202
5390
  cancellationToken.reset();
5203
5391
  exitCodeHandler.resetCode();
5204
5392
  OutputService.clearTerminal();
@@ -5253,7 +5441,7 @@ class Cli {
5253
5441
  const runner = new Runner(resolvedConfig);
5254
5442
  await runner.run(testFiles, cancellationToken);
5255
5443
  PluginService.removeHandlers();
5256
- } while (cancellationToken.reason === CancellationReason.ConfigChange);
5444
+ } while (cancellationToken.reason === "configChange");
5257
5445
  this.#eventEmitter.removeHandlers();
5258
5446
  }
5259
5447
  #waitForChangedFiles(resolvedConfig, cancellationToken) {
@@ -5262,7 +5450,7 @@ class Cli {
5262
5450
  cancellationToken.reset();
5263
5451
  OutputService.writeMessage(waitingForFileChangesText());
5264
5452
  const onChanged = () => {
5265
- cancellationToken.cancel(CancellationReason.ConfigChange);
5453
+ cancellationToken.cancel("configChange");
5266
5454
  for (const watcher of watchers) {
5267
5455
  watcher.close();
5268
5456
  }
@@ -5284,4 +5472,4 @@ class Cli {
5284
5472
  }
5285
5473
  }
5286
5474
 
5287
- 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 };