tstyche 4.3.0 → 5.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/build/4.x/index.d.cts +251 -0
- package/build/4.x/index.d.ts +251 -0
- package/build/index.d.cts +16 -58
- package/build/index.d.ts +16 -58
- package/build/tstyche.d.ts +227 -314
- package/build/tstyche.js +1461 -1141
- package/package.json +13 -2
package/build/tstyche.js
CHANGED
|
@@ -6,34 +6,117 @@ 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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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 JsonNode {
|
|
54
|
+
origin;
|
|
55
|
+
text;
|
|
56
|
+
constructor(text, origin) {
|
|
57
|
+
this.origin = origin;
|
|
58
|
+
this.text = text;
|
|
59
|
+
}
|
|
60
|
+
getValue(options) {
|
|
61
|
+
if (this.text == null) {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
if (/^['"]/.test(this.text)) {
|
|
65
|
+
return this.text.slice(1, -1);
|
|
66
|
+
}
|
|
67
|
+
if (options?.expectsIdentifier) {
|
|
68
|
+
return this.text;
|
|
69
|
+
}
|
|
70
|
+
if (this.text === "true") {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
if (this.text === "false") {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
if (/^\d/.test(this.text)) {
|
|
77
|
+
return Number.parseFloat(this.text);
|
|
78
|
+
}
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
class SourceService {
|
|
84
|
+
static #files = new Map();
|
|
85
|
+
static delete(filePath) {
|
|
86
|
+
SourceService.#files.delete(filePath);
|
|
87
|
+
}
|
|
88
|
+
static get(source) {
|
|
89
|
+
const file = SourceService.#files.get(source.fileName);
|
|
90
|
+
if (file != null) {
|
|
91
|
+
return file;
|
|
92
|
+
}
|
|
93
|
+
return source;
|
|
94
|
+
}
|
|
95
|
+
static set(source) {
|
|
96
|
+
SourceService.#files.set(source.fileName, source);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
16
99
|
|
|
17
100
|
class DiagnosticOrigin {
|
|
18
|
-
|
|
101
|
+
assertionNode;
|
|
19
102
|
end;
|
|
20
103
|
sourceFile;
|
|
21
104
|
start;
|
|
22
|
-
constructor(start, end, sourceFile,
|
|
105
|
+
constructor(start, end, sourceFile, assertionNode) {
|
|
23
106
|
this.start = start;
|
|
24
107
|
this.end = end;
|
|
25
|
-
this.sourceFile = sourceFile;
|
|
26
|
-
this.
|
|
108
|
+
this.sourceFile = SourceService.get(sourceFile);
|
|
109
|
+
this.assertionNode = assertionNode;
|
|
27
110
|
}
|
|
28
|
-
static fromAssertion(
|
|
29
|
-
const node =
|
|
30
|
-
return new DiagnosticOrigin(node.getStart(), node.getEnd(), node.getSourceFile(),
|
|
111
|
+
static fromAssertion(assertionNode) {
|
|
112
|
+
const node = assertionNode.matcherNameNode.name;
|
|
113
|
+
return new DiagnosticOrigin(node.getStart(), node.getEnd(), node.getSourceFile(), assertionNode);
|
|
31
114
|
}
|
|
32
|
-
static fromNode(node,
|
|
33
|
-
return new DiagnosticOrigin(node.getStart(), node.getEnd(), node.getSourceFile(),
|
|
115
|
+
static fromNode(node, assertionNode) {
|
|
116
|
+
return new DiagnosticOrigin(node.getStart(), node.getEnd(), node.getSourceFile(), assertionNode);
|
|
34
117
|
}
|
|
35
|
-
static fromNodes(nodes,
|
|
36
|
-
return new DiagnosticOrigin(nodes.pos, nodes.end, nodes[0].getSourceFile(),
|
|
118
|
+
static fromNodes(nodes, assertionNode) {
|
|
119
|
+
return new DiagnosticOrigin(nodes.pos, nodes.end, nodes[0].getSourceFile(), assertionNode);
|
|
37
120
|
}
|
|
38
121
|
}
|
|
39
122
|
|
|
@@ -82,32 +165,141 @@ class Diagnostic {
|
|
|
82
165
|
return this;
|
|
83
166
|
}
|
|
84
167
|
static error(text, origin) {
|
|
85
|
-
return new Diagnostic(text,
|
|
168
|
+
return new Diagnostic(text, "error", origin);
|
|
86
169
|
}
|
|
87
170
|
extendWith(text, origin) {
|
|
88
171
|
return new Diagnostic([this.text, text].flat(), this.category, origin ?? this.origin);
|
|
89
172
|
}
|
|
90
|
-
static fromDiagnostics(diagnostics
|
|
173
|
+
static fromDiagnostics(diagnostics) {
|
|
91
174
|
return diagnostics.map((diagnostic) => {
|
|
92
175
|
const code = `ts(${diagnostic.code})`;
|
|
93
176
|
let origin;
|
|
94
177
|
if (isDiagnosticWithLocation(diagnostic)) {
|
|
95
|
-
origin = new DiagnosticOrigin(diagnostic.start, getTextSpanEnd(diagnostic),
|
|
178
|
+
origin = new DiagnosticOrigin(diagnostic.start, getTextSpanEnd(diagnostic), diagnostic.file);
|
|
96
179
|
}
|
|
97
180
|
let related;
|
|
98
181
|
if (diagnostic.relatedInformation != null) {
|
|
99
182
|
related = Diagnostic.fromDiagnostics(diagnostic.relatedInformation);
|
|
100
183
|
}
|
|
101
184
|
const text = getDiagnosticMessageText(diagnostic);
|
|
102
|
-
return new Diagnostic(text,
|
|
185
|
+
return new Diagnostic(text, "error", origin).add({ code, related });
|
|
103
186
|
});
|
|
104
187
|
}
|
|
105
188
|
static warning(text, origin) {
|
|
106
|
-
return new Diagnostic(text,
|
|
189
|
+
return new Diagnostic(text, "warning", origin);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
var DiagnosticCategory;
|
|
194
|
+
(function (DiagnosticCategory) {
|
|
195
|
+
DiagnosticCategory["Error"] = "error";
|
|
196
|
+
DiagnosticCategory["Warning"] = "warning";
|
|
197
|
+
})(DiagnosticCategory || (DiagnosticCategory = {}));
|
|
198
|
+
|
|
199
|
+
class JsonScanner {
|
|
200
|
+
#end;
|
|
201
|
+
#position;
|
|
202
|
+
#previousPosition;
|
|
203
|
+
#sourceFile;
|
|
204
|
+
constructor(sourceFile, options) {
|
|
205
|
+
this.#end = options?.end ?? sourceFile.text.length;
|
|
206
|
+
this.#position = options?.start ?? 0;
|
|
207
|
+
this.#previousPosition = options?.start ?? 0;
|
|
208
|
+
this.#sourceFile = sourceFile;
|
|
209
|
+
}
|
|
210
|
+
#getOrigin() {
|
|
211
|
+
return new DiagnosticOrigin(this.#previousPosition, this.#position, this.#sourceFile);
|
|
212
|
+
}
|
|
213
|
+
isRead() {
|
|
214
|
+
return !(this.#position < this.#end);
|
|
215
|
+
}
|
|
216
|
+
#peekCharacter() {
|
|
217
|
+
return this.#sourceFile.text.charAt(this.#position);
|
|
218
|
+
}
|
|
219
|
+
#peekNextCharacter() {
|
|
220
|
+
return this.#sourceFile.text.charAt(this.#position + 1);
|
|
221
|
+
}
|
|
222
|
+
peekToken(token) {
|
|
223
|
+
this.#skipTrivia();
|
|
224
|
+
return this.#peekCharacter() === token;
|
|
225
|
+
}
|
|
226
|
+
read() {
|
|
227
|
+
this.#skipTrivia();
|
|
228
|
+
this.#previousPosition = this.#position;
|
|
229
|
+
if (/[\s,:\]}]/.test(this.#peekCharacter())) {
|
|
230
|
+
return new JsonNode(undefined, this.#getOrigin());
|
|
231
|
+
}
|
|
232
|
+
let text = "";
|
|
233
|
+
let closingTokenText = "";
|
|
234
|
+
if (/[[{'"]/.test(this.#peekCharacter())) {
|
|
235
|
+
text += this.#readCharacter();
|
|
236
|
+
switch (text) {
|
|
237
|
+
case "[":
|
|
238
|
+
closingTokenText = "]";
|
|
239
|
+
break;
|
|
240
|
+
case "{":
|
|
241
|
+
closingTokenText = "}";
|
|
242
|
+
break;
|
|
243
|
+
default:
|
|
244
|
+
closingTokenText = text;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
while (!this.isRead()) {
|
|
248
|
+
text += this.#readCharacter();
|
|
249
|
+
if (text.slice(-1) === closingTokenText || (!closingTokenText && /[\s,:\]}]/.test(this.#peekCharacter()))) {
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return new JsonNode(text, this.#getOrigin());
|
|
254
|
+
}
|
|
255
|
+
#readCharacter() {
|
|
256
|
+
return this.#sourceFile.text.charAt(this.#position++);
|
|
257
|
+
}
|
|
258
|
+
readToken(token) {
|
|
259
|
+
this.#skipTrivia();
|
|
260
|
+
this.#previousPosition = this.#position;
|
|
261
|
+
const character = this.#peekCharacter();
|
|
262
|
+
if (typeof token === "string" ? token === character : token.test(character)) {
|
|
263
|
+
this.#position++;
|
|
264
|
+
return new JsonNode(character, this.#getOrigin());
|
|
265
|
+
}
|
|
266
|
+
return new JsonNode(undefined, this.#getOrigin());
|
|
267
|
+
}
|
|
268
|
+
#skipTrivia() {
|
|
269
|
+
while (!this.isRead()) {
|
|
270
|
+
if (/\s/.test(this.#peekCharacter())) {
|
|
271
|
+
this.#position++;
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (this.#peekCharacter() === "/") {
|
|
275
|
+
if (this.#peekNextCharacter() === "/") {
|
|
276
|
+
this.#position += 2;
|
|
277
|
+
while (!this.isRead()) {
|
|
278
|
+
if (this.#readCharacter() === "\n") {
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
if (this.#peekNextCharacter() === "*") {
|
|
285
|
+
this.#position += 2;
|
|
286
|
+
while (!this.isRead()) {
|
|
287
|
+
if (this.#peekCharacter() === "*" && this.#peekNextCharacter() === "/") {
|
|
288
|
+
this.#position += 2;
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
this.#position++;
|
|
292
|
+
}
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
this.#previousPosition = this.#position;
|
|
107
299
|
}
|
|
108
300
|
}
|
|
109
301
|
|
|
110
|
-
class
|
|
302
|
+
class JsonSourceFile {
|
|
111
303
|
fileName;
|
|
112
304
|
#lineMap;
|
|
113
305
|
text;
|
|
@@ -141,49 +333,6 @@ class SourceFile {
|
|
|
141
333
|
}
|
|
142
334
|
}
|
|
143
335
|
|
|
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
336
|
class Path {
|
|
188
337
|
static normalizeSlashes;
|
|
189
338
|
static {
|
|
@@ -237,19 +386,22 @@ class ConfigDiagnosticText {
|
|
|
237
386
|
static moduleWasNotFound(specifier) {
|
|
238
387
|
return `The specified module '${specifier}' was not found.`;
|
|
239
388
|
}
|
|
389
|
+
static optionValueMustBe(optionName, optionBrand) {
|
|
390
|
+
return `Value for the '${optionName}' option must be a ${optionBrand}.`;
|
|
391
|
+
}
|
|
240
392
|
static rangeIsNotValid(value) {
|
|
241
393
|
return `The specified range '${value}' is not valid.`;
|
|
242
394
|
}
|
|
395
|
+
static rangeDoesNotMatchSupported(value) {
|
|
396
|
+
return `The specified range '${value}' does not match any supported TypeScript versions.`;
|
|
397
|
+
}
|
|
243
398
|
static rangeUsage() {
|
|
244
399
|
return [
|
|
245
|
-
"A range must be specified using an operator and a minor version.",
|
|
246
|
-
"To set an upper bound, the intersection of two ranges
|
|
247
|
-
"
|
|
400
|
+
"A range must be specified using an operator and a minor version: '>=5.5'.",
|
|
401
|
+
"To set an upper bound, use the intersection of two ranges: '>=5.0 <5.3'.",
|
|
402
|
+
"Use the '||' operator to join ranges into a union: '>=5.2 <=5.3 || 5.4.2 || >5.5'.",
|
|
248
403
|
];
|
|
249
404
|
}
|
|
250
|
-
static requiresValueType(optionName, optionBrand) {
|
|
251
|
-
return `Option '${optionName}' requires a value of type ${optionBrand}.`;
|
|
252
|
-
}
|
|
253
405
|
static seen(element) {
|
|
254
406
|
return `The ${element} was seen here.`;
|
|
255
407
|
}
|
|
@@ -259,46 +411,14 @@ class ConfigDiagnosticText {
|
|
|
259
411
|
static unknownOption(optionName) {
|
|
260
412
|
return `Unknown option '${optionName}'.`;
|
|
261
413
|
}
|
|
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
414
|
static versionIsNotSupported(value) {
|
|
275
|
-
|
|
276
|
-
return "Cannot use 'current' as a target. Failed to resolve the installed TypeScript module.";
|
|
277
|
-
}
|
|
278
|
-
return `TypeScript version '${value}' is not supported.`;
|
|
415
|
+
return `The TypeScript version '${value}' is not supported.`;
|
|
279
416
|
}
|
|
280
417
|
static watchCannotBeEnabled() {
|
|
281
418
|
return "Watch mode cannot be enabled in continuous integration environment.";
|
|
282
419
|
}
|
|
283
420
|
}
|
|
284
421
|
|
|
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
422
|
class Environment {
|
|
303
423
|
static resolve() {
|
|
304
424
|
return {
|
|
@@ -555,6 +675,9 @@ class Manifest {
|
|
|
555
675
|
return;
|
|
556
676
|
}
|
|
557
677
|
resolve(tag) {
|
|
678
|
+
if (tag === "*") {
|
|
679
|
+
return this.resolutions["latest"];
|
|
680
|
+
}
|
|
558
681
|
if (this.versions.includes(tag)) {
|
|
559
682
|
return tag;
|
|
560
683
|
}
|
|
@@ -663,32 +786,58 @@ class ManifestService {
|
|
|
663
786
|
}
|
|
664
787
|
|
|
665
788
|
class TarReader {
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
789
|
+
#leftover = new Uint8Array(0);
|
|
790
|
+
#reader;
|
|
791
|
+
#textDecoder = new TextDecoder();
|
|
792
|
+
constructor(stream) {
|
|
793
|
+
this.#reader = stream.getReader();
|
|
794
|
+
}
|
|
795
|
+
async *extract() {
|
|
796
|
+
while (true) {
|
|
797
|
+
const header = await this.#read(512);
|
|
798
|
+
if (this.#isEndOfArchive(header)) {
|
|
674
799
|
break;
|
|
675
800
|
}
|
|
676
|
-
const
|
|
677
|
-
const
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
801
|
+
const name = this.#textDecoder.decode(header.subarray(0, 100)).replace(/\0.*$/, "");
|
|
802
|
+
const sizeOctal = this.#textDecoder.decode(header.subarray(124, 136)).replace(/\0.*$/, "").trim();
|
|
803
|
+
const size = Number.parseInt(sizeOctal, 8);
|
|
804
|
+
const content = await this.#read(size);
|
|
805
|
+
yield { name, content };
|
|
806
|
+
if (size % 512 !== 0) {
|
|
807
|
+
const toSkip = 512 - (size % 512);
|
|
808
|
+
await this.#read(toSkip);
|
|
682
809
|
}
|
|
683
810
|
}
|
|
684
811
|
}
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
812
|
+
#isEndOfArchive(entry) {
|
|
813
|
+
return entry.every((byte) => byte === 0);
|
|
814
|
+
}
|
|
815
|
+
async #read(n) {
|
|
816
|
+
const result = new Uint8Array(n);
|
|
817
|
+
let filled = 0;
|
|
818
|
+
if (this.#leftover.length > 0) {
|
|
819
|
+
const toCopy = Math.min(this.#leftover.length, n);
|
|
820
|
+
result.set(this.#leftover.subarray(0, toCopy), filled);
|
|
821
|
+
filled += toCopy;
|
|
822
|
+
this.#leftover = this.#leftover.subarray(toCopy);
|
|
823
|
+
if (filled === n) {
|
|
824
|
+
return result;
|
|
825
|
+
}
|
|
690
826
|
}
|
|
691
|
-
|
|
827
|
+
while (filled < n) {
|
|
828
|
+
const { value, done } = await this.#reader.read();
|
|
829
|
+
if (done) {
|
|
830
|
+
break;
|
|
831
|
+
}
|
|
832
|
+
const toCopy = Math.min(value.length, n - filled);
|
|
833
|
+
result.set(value.subarray(0, toCopy), filled);
|
|
834
|
+
filled += toCopy;
|
|
835
|
+
if (toCopy < value.length) {
|
|
836
|
+
this.#leftover = value.subarray(toCopy);
|
|
837
|
+
break;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
return result.subarray(0, filled);
|
|
692
841
|
}
|
|
693
842
|
}
|
|
694
843
|
|
|
@@ -706,16 +855,18 @@ class PackageService {
|
|
|
706
855
|
const response = await this.#fetcher.get(request, diagnostic);
|
|
707
856
|
if (response?.body != null) {
|
|
708
857
|
const targetPath = `${packagePath}-${Math.random().toString(32).slice(2)}`;
|
|
709
|
-
|
|
858
|
+
const stream = response.body.pipeThrough(new DecompressionStream("gzip"));
|
|
859
|
+
const tarReader = new TarReader(stream);
|
|
860
|
+
for await (const file of tarReader.extract()) {
|
|
710
861
|
if (!file.name.startsWith("package/")) {
|
|
711
862
|
continue;
|
|
712
863
|
}
|
|
713
|
-
const filePath = Path.join(targetPath, file.name.replace(
|
|
864
|
+
const filePath = Path.join(targetPath, file.name.replace(/^package\//, ""));
|
|
714
865
|
const directoryPath = Path.dirname(filePath);
|
|
715
866
|
if (!existsSync(directoryPath)) {
|
|
716
867
|
await fs.mkdir(directoryPath, { recursive: true });
|
|
717
868
|
}
|
|
718
|
-
await fs.writeFile(filePath, file.
|
|
869
|
+
await fs.writeFile(filePath, file.content);
|
|
719
870
|
}
|
|
720
871
|
await fs.rename(targetPath, packagePath);
|
|
721
872
|
return packagePath;
|
|
@@ -765,7 +916,7 @@ class Store {
|
|
|
765
916
|
Store.#manifestService = new ManifestService(Store.#storePath, Store.#npmRegistry, Store.#fetcher);
|
|
766
917
|
}
|
|
767
918
|
static async fetch(tag) {
|
|
768
|
-
if (tag === "
|
|
919
|
+
if (tag === "*" && environmentOptions.typescriptModule != null) {
|
|
769
920
|
return;
|
|
770
921
|
}
|
|
771
922
|
await Store.open();
|
|
@@ -782,7 +933,7 @@ class Store {
|
|
|
782
933
|
return compilerInstance;
|
|
783
934
|
}
|
|
784
935
|
let modulePath;
|
|
785
|
-
if (tag === "
|
|
936
|
+
if (tag === "*" && environmentOptions.typescriptModule != null) {
|
|
786
937
|
modulePath = fileURLToPath(environmentOptions.typescriptModule);
|
|
787
938
|
}
|
|
788
939
|
else {
|
|
@@ -830,7 +981,7 @@ class Store {
|
|
|
830
981
|
Store.open = () => Promise.resolve();
|
|
831
982
|
Store.manifest = await Store.#manifestService.open();
|
|
832
983
|
if (Store.manifest != null) {
|
|
833
|
-
Store.#supportedTags = [...Object.keys(Store.manifest.resolutions), ...Store.manifest.versions
|
|
984
|
+
Store.#supportedTags = [...Object.keys(Store.manifest.resolutions), ...Store.manifest.versions];
|
|
834
985
|
}
|
|
835
986
|
}
|
|
836
987
|
static async prune() {
|
|
@@ -840,8 +991,8 @@ class Store {
|
|
|
840
991
|
await Store.#manifestService.open({ refresh: true });
|
|
841
992
|
}
|
|
842
993
|
static async validateTag(tag) {
|
|
843
|
-
if (tag === "
|
|
844
|
-
return
|
|
994
|
+
if (tag === "*") {
|
|
995
|
+
return true;
|
|
845
996
|
}
|
|
846
997
|
await Store.open();
|
|
847
998
|
if (Store.manifest?.isOutdated({ ageTolerance: 60 }) &&
|
|
@@ -859,23 +1010,25 @@ class Store {
|
|
|
859
1010
|
|
|
860
1011
|
class Target {
|
|
861
1012
|
static #rangeRegex = /^[<>]=?\d\.\d( [<>]=?\d\.\d)?$/;
|
|
862
|
-
static async expand(
|
|
863
|
-
|
|
864
|
-
for (const query of queries) {
|
|
865
|
-
if (!Target.isRange(query)) {
|
|
866
|
-
include.push(query);
|
|
867
|
-
continue;
|
|
868
|
-
}
|
|
1013
|
+
static async expand(range, onDiagnostics, origin) {
|
|
1014
|
+
if (Target.isRange(range)) {
|
|
869
1015
|
await Store.open();
|
|
870
1016
|
if (Store.manifest != null) {
|
|
871
1017
|
let versions = [...Store.manifest.minorVersions];
|
|
872
|
-
for (const comparator of
|
|
873
|
-
versions = Target.#filter(comparator, versions);
|
|
1018
|
+
for (const comparator of range.split(" ")) {
|
|
1019
|
+
versions = Target.#filter(comparator.trim(), versions);
|
|
1020
|
+
if (versions.length === 0) {
|
|
1021
|
+
const text = [
|
|
1022
|
+
ConfigDiagnosticText.rangeDoesNotMatchSupported(range),
|
|
1023
|
+
ConfigDiagnosticText.inspectSupportedVersions(),
|
|
1024
|
+
];
|
|
1025
|
+
onDiagnostics(Diagnostic.error(text, origin));
|
|
1026
|
+
}
|
|
874
1027
|
}
|
|
875
|
-
|
|
1028
|
+
return versions;
|
|
876
1029
|
}
|
|
877
1030
|
}
|
|
878
|
-
return
|
|
1031
|
+
return [range];
|
|
879
1032
|
}
|
|
880
1033
|
static #filter(comparator, versions) {
|
|
881
1034
|
const targetVersion = comparator.replace(/^[<>]=?/, "");
|
|
@@ -894,178 +1047,177 @@ class Target {
|
|
|
894
1047
|
static isRange(query) {
|
|
895
1048
|
return Target.#rangeRegex.test(query);
|
|
896
1049
|
}
|
|
1050
|
+
static split(range) {
|
|
1051
|
+
return range.split(/ *\|\| */);
|
|
1052
|
+
}
|
|
897
1053
|
}
|
|
898
1054
|
|
|
899
1055
|
class Options {
|
|
900
1056
|
static #definitions = [
|
|
901
1057
|
{
|
|
902
|
-
brand:
|
|
1058
|
+
brand: "string",
|
|
903
1059
|
description: "The Url to the config file validation schema.",
|
|
904
|
-
group:
|
|
1060
|
+
group: 4,
|
|
905
1061
|
name: "$schema",
|
|
906
1062
|
},
|
|
907
1063
|
{
|
|
908
|
-
brand:
|
|
909
|
-
description: "
|
|
910
|
-
group:
|
|
911
|
-
name: "
|
|
1064
|
+
brand: "boolean",
|
|
1065
|
+
description: "Check declaration files for type errors.",
|
|
1066
|
+
group: 4,
|
|
1067
|
+
name: "checkDeclarationFiles",
|
|
912
1068
|
},
|
|
913
1069
|
{
|
|
914
|
-
brand:
|
|
1070
|
+
brand: "boolean",
|
|
915
1071
|
description: "Check errors silenced by '// @ts-expect-error' directives.",
|
|
916
|
-
group:
|
|
1072
|
+
group: 4,
|
|
917
1073
|
name: "checkSuppressedErrors",
|
|
918
1074
|
},
|
|
919
1075
|
{
|
|
920
|
-
brand:
|
|
1076
|
+
brand: "string",
|
|
921
1077
|
description: "The path to a TSTyche configuration file.",
|
|
922
|
-
group:
|
|
1078
|
+
group: 2,
|
|
923
1079
|
name: "config",
|
|
924
1080
|
},
|
|
925
1081
|
{
|
|
926
|
-
brand:
|
|
1082
|
+
brand: "boolean",
|
|
927
1083
|
description: "Stop running tests after the first failed assertion.",
|
|
928
|
-
group:
|
|
1084
|
+
group: 4 | 2,
|
|
929
1085
|
name: "failFast",
|
|
930
1086
|
},
|
|
931
1087
|
{
|
|
932
|
-
brand:
|
|
1088
|
+
brand: "true",
|
|
933
1089
|
description: "Fetch the specified versions of the 'typescript' package and exit.",
|
|
934
|
-
group:
|
|
1090
|
+
group: 2,
|
|
935
1091
|
name: "fetch",
|
|
936
1092
|
},
|
|
937
1093
|
{
|
|
938
|
-
brand:
|
|
1094
|
+
brand: "list",
|
|
939
1095
|
description: "The list of glob patterns matching the fixture files.",
|
|
940
|
-
group:
|
|
1096
|
+
group: 4,
|
|
941
1097
|
items: {
|
|
942
|
-
brand:
|
|
1098
|
+
brand: "string",
|
|
943
1099
|
name: "fixtureFileMatch",
|
|
944
1100
|
},
|
|
945
1101
|
name: "fixtureFileMatch",
|
|
946
1102
|
},
|
|
947
1103
|
{
|
|
948
|
-
brand:
|
|
1104
|
+
brand: "true",
|
|
949
1105
|
description: "Print the list of command line options with brief descriptions and exit.",
|
|
950
|
-
group:
|
|
1106
|
+
group: 2,
|
|
951
1107
|
name: "help",
|
|
952
1108
|
},
|
|
953
1109
|
{
|
|
954
|
-
brand:
|
|
1110
|
+
brand: "true",
|
|
955
1111
|
description: "Print the list of supported versions of the 'typescript' package and exit.",
|
|
956
|
-
group:
|
|
1112
|
+
group: 2,
|
|
957
1113
|
name: "list",
|
|
958
1114
|
},
|
|
959
1115
|
{
|
|
960
|
-
brand:
|
|
1116
|
+
brand: "true",
|
|
961
1117
|
description: "Print the list of the selected test files and exit.",
|
|
962
|
-
group:
|
|
1118
|
+
group: 2,
|
|
963
1119
|
name: "listFiles",
|
|
964
1120
|
},
|
|
965
1121
|
{
|
|
966
|
-
brand:
|
|
1122
|
+
brand: "string",
|
|
967
1123
|
description: "Only run tests with matching name.",
|
|
968
|
-
group:
|
|
1124
|
+
group: 2,
|
|
969
1125
|
name: "only",
|
|
970
1126
|
},
|
|
971
1127
|
{
|
|
972
|
-
brand:
|
|
1128
|
+
brand: "list",
|
|
973
1129
|
description: "The list of plugins to use.",
|
|
974
|
-
group:
|
|
1130
|
+
group: 2 | 4,
|
|
975
1131
|
items: {
|
|
976
|
-
brand:
|
|
1132
|
+
brand: "string",
|
|
977
1133
|
name: "plugins",
|
|
978
1134
|
},
|
|
979
1135
|
name: "plugins",
|
|
980
1136
|
},
|
|
981
1137
|
{
|
|
982
|
-
brand:
|
|
1138
|
+
brand: "true",
|
|
983
1139
|
description: "Remove all installed versions of the 'typescript' package and exit.",
|
|
984
|
-
group:
|
|
1140
|
+
group: 2,
|
|
985
1141
|
name: "prune",
|
|
986
1142
|
},
|
|
987
1143
|
{
|
|
988
|
-
brand:
|
|
1144
|
+
brand: "boolean",
|
|
989
1145
|
description: "Reject the 'any' type passed as an argument to the 'expect()' function or a matcher.",
|
|
990
|
-
group:
|
|
1146
|
+
group: 4,
|
|
991
1147
|
name: "rejectAnyType",
|
|
992
1148
|
},
|
|
993
1149
|
{
|
|
994
|
-
brand:
|
|
1150
|
+
brand: "boolean",
|
|
995
1151
|
description: "Reject the 'never' type passed as an argument to the 'expect()' function or a matcher.",
|
|
996
|
-
group:
|
|
1152
|
+
group: 4,
|
|
997
1153
|
name: "rejectNeverType",
|
|
998
1154
|
},
|
|
999
1155
|
{
|
|
1000
|
-
brand:
|
|
1156
|
+
brand: "list",
|
|
1001
1157
|
description: "The list of reporters to use.",
|
|
1002
|
-
group:
|
|
1158
|
+
group: 2 | 4,
|
|
1003
1159
|
items: {
|
|
1004
|
-
brand:
|
|
1160
|
+
brand: "string",
|
|
1005
1161
|
name: "reporters",
|
|
1006
1162
|
},
|
|
1007
1163
|
name: "reporters",
|
|
1008
1164
|
},
|
|
1009
1165
|
{
|
|
1010
|
-
brand:
|
|
1166
|
+
brand: "string",
|
|
1011
1167
|
description: "The path to a directory containing files of a test project.",
|
|
1012
|
-
group:
|
|
1168
|
+
group: 4,
|
|
1013
1169
|
name: "rootPath",
|
|
1014
1170
|
},
|
|
1015
1171
|
{
|
|
1016
|
-
brand:
|
|
1172
|
+
brand: "true",
|
|
1017
1173
|
description: "Print the resolved configuration and exit.",
|
|
1018
|
-
group:
|
|
1174
|
+
group: 2,
|
|
1019
1175
|
name: "showConfig",
|
|
1020
1176
|
},
|
|
1021
1177
|
{
|
|
1022
|
-
brand:
|
|
1178
|
+
brand: "string",
|
|
1023
1179
|
description: "Skip tests with matching name.",
|
|
1024
|
-
group:
|
|
1180
|
+
group: 2,
|
|
1025
1181
|
name: "skip",
|
|
1026
1182
|
},
|
|
1027
1183
|
{
|
|
1028
|
-
brand:
|
|
1029
|
-
description: "The
|
|
1030
|
-
group:
|
|
1031
|
-
items: {
|
|
1032
|
-
brand: OptionBrand.String,
|
|
1033
|
-
name: "target",
|
|
1034
|
-
},
|
|
1184
|
+
brand: "range",
|
|
1185
|
+
description: "The range of TypeScript versions to be tested against.",
|
|
1186
|
+
group: 2 | 4 | 8,
|
|
1035
1187
|
name: "target",
|
|
1036
1188
|
},
|
|
1037
1189
|
{
|
|
1038
|
-
brand:
|
|
1190
|
+
brand: "list",
|
|
1039
1191
|
description: "The list of glob patterns matching the test files.",
|
|
1040
|
-
group:
|
|
1192
|
+
group: 4,
|
|
1041
1193
|
items: {
|
|
1042
|
-
brand:
|
|
1194
|
+
brand: "string",
|
|
1043
1195
|
name: "testFileMatch",
|
|
1044
1196
|
},
|
|
1045
1197
|
name: "testFileMatch",
|
|
1046
1198
|
},
|
|
1047
1199
|
{
|
|
1048
|
-
brand:
|
|
1200
|
+
brand: "string",
|
|
1049
1201
|
description: "The look up strategy to be used to find the TSConfig file.",
|
|
1050
|
-
group:
|
|
1202
|
+
group: 2 | 4,
|
|
1051
1203
|
name: "tsconfig",
|
|
1052
1204
|
},
|
|
1053
1205
|
{
|
|
1054
|
-
brand:
|
|
1206
|
+
brand: "true",
|
|
1055
1207
|
description: "Fetch the 'typescript' package metadata from the registry and exit.",
|
|
1056
|
-
group:
|
|
1208
|
+
group: 2,
|
|
1057
1209
|
name: "update",
|
|
1058
1210
|
},
|
|
1059
1211
|
{
|
|
1060
|
-
brand:
|
|
1212
|
+
brand: "true",
|
|
1061
1213
|
description: "Print the version number and exit.",
|
|
1062
|
-
group:
|
|
1214
|
+
group: 2,
|
|
1063
1215
|
name: "version",
|
|
1064
1216
|
},
|
|
1065
1217
|
{
|
|
1066
|
-
brand:
|
|
1218
|
+
brand: "true",
|
|
1067
1219
|
description: "Watch for changes and rerun related test files.",
|
|
1068
|
-
group:
|
|
1220
|
+
group: 2,
|
|
1069
1221
|
name: "watch",
|
|
1070
1222
|
},
|
|
1071
1223
|
];
|
|
@@ -1117,7 +1269,7 @@ class Options {
|
|
|
1117
1269
|
}
|
|
1118
1270
|
return optionValue;
|
|
1119
1271
|
}
|
|
1120
|
-
static async validate(optionName, optionValue,
|
|
1272
|
+
static async validate(optionName, optionValue, onDiagnostics, origin) {
|
|
1121
1273
|
const canonicalOptionName = Options.#getCanonicalOptionName(optionName);
|
|
1122
1274
|
switch (canonicalOptionName) {
|
|
1123
1275
|
case "config":
|
|
@@ -1144,16 +1296,17 @@ class Options {
|
|
|
1144
1296
|
case "target": {
|
|
1145
1297
|
if (/[<>=]/.test(optionValue)) {
|
|
1146
1298
|
if (!Target.isRange(optionValue)) {
|
|
1147
|
-
|
|
1299
|
+
const text = [ConfigDiagnosticText.rangeIsNotValid(optionValue), ...ConfigDiagnosticText.rangeUsage()];
|
|
1300
|
+
onDiagnostics(Diagnostic.error(text, origin));
|
|
1148
1301
|
}
|
|
1149
1302
|
break;
|
|
1150
1303
|
}
|
|
1151
1304
|
if ((await Store.validateTag(optionValue)) === false) {
|
|
1152
|
-
|
|
1305
|
+
const text = [
|
|
1153
1306
|
ConfigDiagnosticText.versionIsNotSupported(optionValue),
|
|
1154
|
-
...ConfigDiagnosticText.usage(optionName, optionBrand),
|
|
1155
1307
|
ConfigDiagnosticText.inspectSupportedVersions(),
|
|
1156
|
-
]
|
|
1308
|
+
];
|
|
1309
|
+
onDiagnostics(Diagnostic.error(text, origin));
|
|
1157
1310
|
}
|
|
1158
1311
|
break;
|
|
1159
1312
|
}
|
|
@@ -1174,7 +1327,7 @@ class Options {
|
|
|
1174
1327
|
}
|
|
1175
1328
|
}
|
|
1176
1329
|
|
|
1177
|
-
class
|
|
1330
|
+
class CommandParser {
|
|
1178
1331
|
#commandLineOptions;
|
|
1179
1332
|
#onDiagnostics;
|
|
1180
1333
|
#options;
|
|
@@ -1183,12 +1336,12 @@ class CommandLineParser {
|
|
|
1183
1336
|
this.#commandLineOptions = commandLine;
|
|
1184
1337
|
this.#pathMatch = pathMatch;
|
|
1185
1338
|
this.#onDiagnostics = onDiagnostics;
|
|
1186
|
-
this.#options = Options.for(
|
|
1339
|
+
this.#options = Options.for(2);
|
|
1187
1340
|
}
|
|
1188
1341
|
#onExpectsValue(optionName, optionBrand) {
|
|
1189
1342
|
const text = [
|
|
1190
1343
|
ConfigDiagnosticText.expectsValue(optionName),
|
|
1191
|
-
|
|
1344
|
+
ConfigDiagnosticText.optionValueMustBe(optionName, optionBrand),
|
|
1192
1345
|
];
|
|
1193
1346
|
this.#onDiagnostics(Diagnostic.error(text));
|
|
1194
1347
|
}
|
|
@@ -1218,18 +1371,18 @@ class CommandLineParser {
|
|
|
1218
1371
|
async #parseOptionValue(commandLineArgs, index, optionName, optionDefinition) {
|
|
1219
1372
|
let optionValue = this.#resolveOptionValue(commandLineArgs[index]);
|
|
1220
1373
|
switch (optionDefinition.brand) {
|
|
1221
|
-
case
|
|
1222
|
-
await Options.validate(optionName, optionValue,
|
|
1374
|
+
case "true":
|
|
1375
|
+
await Options.validate(optionName, optionValue, this.#onDiagnostics);
|
|
1223
1376
|
this.#commandLineOptions[optionDefinition.name] = true;
|
|
1224
1377
|
break;
|
|
1225
|
-
case
|
|
1226
|
-
await Options.validate(optionName, optionValue,
|
|
1378
|
+
case "boolean":
|
|
1379
|
+
await Options.validate(optionName, optionValue, this.#onDiagnostics);
|
|
1227
1380
|
this.#commandLineOptions[optionDefinition.name] = optionValue !== "false";
|
|
1228
1381
|
if (optionValue === "false" || optionValue === "true") {
|
|
1229
1382
|
index++;
|
|
1230
1383
|
}
|
|
1231
1384
|
break;
|
|
1232
|
-
case
|
|
1385
|
+
case "list":
|
|
1233
1386
|
if (optionValue !== "") {
|
|
1234
1387
|
const optionValues = optionValue
|
|
1235
1388
|
.split(",")
|
|
@@ -1237,7 +1390,7 @@ class CommandLineParser {
|
|
|
1237
1390
|
.filter((value) => value !== "")
|
|
1238
1391
|
.map((value) => Options.resolve(optionName, value));
|
|
1239
1392
|
for (const optionValue of optionValues) {
|
|
1240
|
-
await Options.validate(optionName, optionValue,
|
|
1393
|
+
await Options.validate(optionName, optionValue, this.#onDiagnostics);
|
|
1241
1394
|
}
|
|
1242
1395
|
this.#commandLineOptions[optionDefinition.name] = optionValues;
|
|
1243
1396
|
index++;
|
|
@@ -1245,16 +1398,30 @@ class CommandLineParser {
|
|
|
1245
1398
|
}
|
|
1246
1399
|
this.#onExpectsValue(optionName, optionDefinition.brand);
|
|
1247
1400
|
break;
|
|
1248
|
-
case
|
|
1401
|
+
case "string":
|
|
1249
1402
|
if (optionValue !== "") {
|
|
1250
1403
|
optionValue = Options.resolve(optionName, optionValue);
|
|
1251
|
-
await Options.validate(optionName, optionValue,
|
|
1404
|
+
await Options.validate(optionName, optionValue, this.#onDiagnostics);
|
|
1252
1405
|
this.#commandLineOptions[optionDefinition.name] = optionValue;
|
|
1253
1406
|
index++;
|
|
1254
1407
|
break;
|
|
1255
1408
|
}
|
|
1256
1409
|
this.#onExpectsValue(optionName, optionDefinition.brand);
|
|
1257
1410
|
break;
|
|
1411
|
+
case "range":
|
|
1412
|
+
if (optionValue !== "") {
|
|
1413
|
+
const optionValues = [];
|
|
1414
|
+
for (const range of Target.split(optionValue)) {
|
|
1415
|
+
await Options.validate(optionName, range, this.#onDiagnostics);
|
|
1416
|
+
const versions = await Target.expand(range, this.#onDiagnostics);
|
|
1417
|
+
optionValues.push(...versions);
|
|
1418
|
+
}
|
|
1419
|
+
this.#commandLineOptions[optionDefinition.name] = optionValues;
|
|
1420
|
+
index++;
|
|
1421
|
+
break;
|
|
1422
|
+
}
|
|
1423
|
+
this.#onExpectsValue(optionName, "string");
|
|
1424
|
+
break;
|
|
1258
1425
|
}
|
|
1259
1426
|
return index;
|
|
1260
1427
|
}
|
|
@@ -1276,43 +1443,58 @@ class ConfigParser {
|
|
|
1276
1443
|
this.#sourceFile = sourceFile;
|
|
1277
1444
|
this.#options = Options.for(optionGroup);
|
|
1278
1445
|
}
|
|
1279
|
-
#onRequiresValue(
|
|
1446
|
+
#onRequiresValue(optionName, optionBrand, jsonNode, isListItem) {
|
|
1280
1447
|
const text = isListItem
|
|
1281
|
-
? ConfigDiagnosticText.expectsListItemType(
|
|
1282
|
-
: ConfigDiagnosticText.
|
|
1448
|
+
? ConfigDiagnosticText.expectsListItemType(optionName, optionBrand)
|
|
1449
|
+
: ConfigDiagnosticText.optionValueMustBe(optionName, optionBrand);
|
|
1283
1450
|
this.#onDiagnostics(Diagnostic.error(text, jsonNode.origin));
|
|
1284
1451
|
}
|
|
1285
1452
|
async #parseValue(optionDefinition, isListItem = false) {
|
|
1286
1453
|
let jsonNode;
|
|
1287
1454
|
let optionValue;
|
|
1288
1455
|
switch (optionDefinition.brand) {
|
|
1289
|
-
case
|
|
1456
|
+
case "boolean": {
|
|
1290
1457
|
jsonNode = this.#jsonScanner.read();
|
|
1291
1458
|
optionValue = jsonNode.getValue();
|
|
1292
1459
|
if (typeof optionValue !== "boolean") {
|
|
1293
|
-
this.#onRequiresValue(optionDefinition, jsonNode, isListItem);
|
|
1460
|
+
this.#onRequiresValue(optionDefinition.name, optionDefinition.brand, jsonNode, isListItem);
|
|
1294
1461
|
break;
|
|
1295
1462
|
}
|
|
1296
1463
|
break;
|
|
1297
1464
|
}
|
|
1298
|
-
case
|
|
1465
|
+
case "string": {
|
|
1299
1466
|
jsonNode = this.#jsonScanner.read();
|
|
1300
1467
|
optionValue = jsonNode.getValue();
|
|
1301
1468
|
if (typeof optionValue !== "string") {
|
|
1302
|
-
this.#onRequiresValue(optionDefinition, jsonNode, isListItem);
|
|
1469
|
+
this.#onRequiresValue(optionDefinition.name, optionDefinition.brand, jsonNode, isListItem);
|
|
1303
1470
|
break;
|
|
1304
1471
|
}
|
|
1305
1472
|
const rootPath = Path.dirname(this.#sourceFile.fileName);
|
|
1306
1473
|
optionValue = Options.resolve(optionDefinition.name, optionValue, rootPath);
|
|
1307
|
-
await Options.validate(optionDefinition.name, optionValue,
|
|
1474
|
+
await Options.validate(optionDefinition.name, optionValue, this.#onDiagnostics, jsonNode.origin);
|
|
1308
1475
|
break;
|
|
1309
1476
|
}
|
|
1310
|
-
case
|
|
1477
|
+
case "range": {
|
|
1478
|
+
optionValue = [];
|
|
1479
|
+
jsonNode = this.#jsonScanner.read();
|
|
1480
|
+
const ranges = jsonNode.getValue();
|
|
1481
|
+
if (typeof ranges !== "string") {
|
|
1482
|
+
this.#onRequiresValue(optionDefinition.name, "string", jsonNode, isListItem);
|
|
1483
|
+
break;
|
|
1484
|
+
}
|
|
1485
|
+
for (const range of Target.split(ranges)) {
|
|
1486
|
+
await Options.validate(optionDefinition.name, range, this.#onDiagnostics, jsonNode.origin);
|
|
1487
|
+
const versions = await Target.expand(range, this.#onDiagnostics, jsonNode.origin);
|
|
1488
|
+
optionValue.push(...versions);
|
|
1489
|
+
}
|
|
1490
|
+
break;
|
|
1491
|
+
}
|
|
1492
|
+
case "list": {
|
|
1311
1493
|
optionValue = [];
|
|
1312
1494
|
const leftBracketToken = this.#jsonScanner.readToken("[");
|
|
1313
1495
|
if (!leftBracketToken.text) {
|
|
1314
1496
|
jsonNode = this.#jsonScanner.read();
|
|
1315
|
-
this.#onRequiresValue(optionDefinition, jsonNode, isListItem);
|
|
1497
|
+
this.#onRequiresValue(optionDefinition.name, optionDefinition.brand, jsonNode, isListItem);
|
|
1316
1498
|
break;
|
|
1317
1499
|
}
|
|
1318
1500
|
while (!this.#jsonScanner.isRead()) {
|
|
@@ -1410,152 +1592,19 @@ class ConfigParser {
|
|
|
1410
1592
|
}
|
|
1411
1593
|
|
|
1412
1594
|
const defaultOptions = {
|
|
1413
|
-
|
|
1414
|
-
checkSuppressedErrors:
|
|
1595
|
+
checkDeclarationFiles: true,
|
|
1596
|
+
checkSuppressedErrors: true,
|
|
1415
1597
|
failFast: false,
|
|
1416
|
-
fixtureFileMatch: ["**/__fixtures__/*.{ts,tsx}", "**/fixtures/*.{ts,tsx}"],
|
|
1417
|
-
plugins: [],
|
|
1418
|
-
rejectAnyType: true,
|
|
1419
|
-
rejectNeverType: true,
|
|
1420
|
-
reporters: ["list", "summary"],
|
|
1421
|
-
rootPath: Path.resolve("./"),
|
|
1422
|
-
target:
|
|
1423
|
-
testFileMatch: ["**/*.tst.*", "**/__typetests__/*.test.*", "**/typetests/*.test.*"],
|
|
1424
|
-
tsconfig: "findup",
|
|
1425
|
-
};
|
|
1426
|
-
|
|
1427
|
-
class JsonNode {
|
|
1428
|
-
origin;
|
|
1429
|
-
text;
|
|
1430
|
-
constructor(text, origin) {
|
|
1431
|
-
this.origin = origin;
|
|
1432
|
-
this.text = text;
|
|
1433
|
-
}
|
|
1434
|
-
getValue(options) {
|
|
1435
|
-
if (this.text == null) {
|
|
1436
|
-
return undefined;
|
|
1437
|
-
}
|
|
1438
|
-
if (/^['"]/.test(this.text)) {
|
|
1439
|
-
return this.text.slice(1, -1);
|
|
1440
|
-
}
|
|
1441
|
-
if (options?.expectsIdentifier) {
|
|
1442
|
-
return this.text;
|
|
1443
|
-
}
|
|
1444
|
-
if (this.text === "true") {
|
|
1445
|
-
return true;
|
|
1446
|
-
}
|
|
1447
|
-
if (this.text === "false") {
|
|
1448
|
-
return false;
|
|
1449
|
-
}
|
|
1450
|
-
if (/^\d/.test(this.text)) {
|
|
1451
|
-
return Number.parseFloat(this.text);
|
|
1452
|
-
}
|
|
1453
|
-
return undefined;
|
|
1454
|
-
}
|
|
1455
|
-
}
|
|
1456
|
-
|
|
1457
|
-
class JsonScanner {
|
|
1458
|
-
#end;
|
|
1459
|
-
#position;
|
|
1460
|
-
#previousPosition;
|
|
1461
|
-
#sourceFile;
|
|
1462
|
-
constructor(sourceFile, options) {
|
|
1463
|
-
this.#end = options?.end ?? sourceFile.text.length;
|
|
1464
|
-
this.#position = options?.start ?? 0;
|
|
1465
|
-
this.#previousPosition = options?.start ?? 0;
|
|
1466
|
-
this.#sourceFile = sourceFile;
|
|
1467
|
-
}
|
|
1468
|
-
#getOrigin() {
|
|
1469
|
-
return new DiagnosticOrigin(this.#previousPosition, this.#position, this.#sourceFile);
|
|
1470
|
-
}
|
|
1471
|
-
isRead() {
|
|
1472
|
-
return !(this.#position < this.#end);
|
|
1473
|
-
}
|
|
1474
|
-
#peekCharacter() {
|
|
1475
|
-
return this.#sourceFile.text.charAt(this.#position);
|
|
1476
|
-
}
|
|
1477
|
-
#peekNextCharacter() {
|
|
1478
|
-
return this.#sourceFile.text.charAt(this.#position + 1);
|
|
1479
|
-
}
|
|
1480
|
-
peekToken(token) {
|
|
1481
|
-
this.#skipTrivia();
|
|
1482
|
-
return this.#peekCharacter() === token;
|
|
1483
|
-
}
|
|
1484
|
-
read() {
|
|
1485
|
-
this.#skipTrivia();
|
|
1486
|
-
this.#previousPosition = this.#position;
|
|
1487
|
-
if (/[\s,:\]}]/.test(this.#peekCharacter())) {
|
|
1488
|
-
return new JsonNode(undefined, this.#getOrigin());
|
|
1489
|
-
}
|
|
1490
|
-
let text = "";
|
|
1491
|
-
let closingTokenText = "";
|
|
1492
|
-
if (/[[{'"]/.test(this.#peekCharacter())) {
|
|
1493
|
-
text += this.#readCharacter();
|
|
1494
|
-
switch (text) {
|
|
1495
|
-
case "[":
|
|
1496
|
-
closingTokenText = "]";
|
|
1497
|
-
break;
|
|
1498
|
-
case "{":
|
|
1499
|
-
closingTokenText = "}";
|
|
1500
|
-
break;
|
|
1501
|
-
default:
|
|
1502
|
-
closingTokenText = text;
|
|
1503
|
-
}
|
|
1504
|
-
}
|
|
1505
|
-
while (!this.isRead()) {
|
|
1506
|
-
text += this.#readCharacter();
|
|
1507
|
-
if (text.slice(-1) === closingTokenText || (!closingTokenText && /[\s,:\]}]/.test(this.#peekCharacter()))) {
|
|
1508
|
-
break;
|
|
1509
|
-
}
|
|
1510
|
-
}
|
|
1511
|
-
return new JsonNode(text, this.#getOrigin());
|
|
1512
|
-
}
|
|
1513
|
-
#readCharacter() {
|
|
1514
|
-
return this.#sourceFile.text.charAt(this.#position++);
|
|
1515
|
-
}
|
|
1516
|
-
readToken(token) {
|
|
1517
|
-
this.#skipTrivia();
|
|
1518
|
-
this.#previousPosition = this.#position;
|
|
1519
|
-
const character = this.#peekCharacter();
|
|
1520
|
-
if (typeof token === "string" ? token === character : token.test(character)) {
|
|
1521
|
-
this.#position++;
|
|
1522
|
-
return new JsonNode(character, this.#getOrigin());
|
|
1523
|
-
}
|
|
1524
|
-
return new JsonNode(undefined, this.#getOrigin());
|
|
1525
|
-
}
|
|
1526
|
-
#skipTrivia() {
|
|
1527
|
-
while (!this.isRead()) {
|
|
1528
|
-
if (/\s/.test(this.#peekCharacter())) {
|
|
1529
|
-
this.#position++;
|
|
1530
|
-
continue;
|
|
1531
|
-
}
|
|
1532
|
-
if (this.#peekCharacter() === "/") {
|
|
1533
|
-
if (this.#peekNextCharacter() === "/") {
|
|
1534
|
-
this.#position += 2;
|
|
1535
|
-
while (!this.isRead()) {
|
|
1536
|
-
if (this.#readCharacter() === "\n") {
|
|
1537
|
-
break;
|
|
1538
|
-
}
|
|
1539
|
-
}
|
|
1540
|
-
continue;
|
|
1541
|
-
}
|
|
1542
|
-
if (this.#peekNextCharacter() === "*") {
|
|
1543
|
-
this.#position += 2;
|
|
1544
|
-
while (!this.isRead()) {
|
|
1545
|
-
if (this.#peekCharacter() === "*" && this.#peekNextCharacter() === "/") {
|
|
1546
|
-
this.#position += 2;
|
|
1547
|
-
break;
|
|
1548
|
-
}
|
|
1549
|
-
this.#position++;
|
|
1550
|
-
}
|
|
1551
|
-
continue;
|
|
1552
|
-
}
|
|
1553
|
-
}
|
|
1554
|
-
break;
|
|
1555
|
-
}
|
|
1556
|
-
this.#previousPosition = this.#position;
|
|
1557
|
-
}
|
|
1558
|
-
}
|
|
1598
|
+
fixtureFileMatch: ["**/__fixtures__/*.{ts,tsx}", "**/fixtures/*.{ts,tsx}"],
|
|
1599
|
+
plugins: [],
|
|
1600
|
+
rejectAnyType: true,
|
|
1601
|
+
rejectNeverType: true,
|
|
1602
|
+
reporters: ["list", "summary"],
|
|
1603
|
+
rootPath: Path.resolve("./"),
|
|
1604
|
+
target: ["*"],
|
|
1605
|
+
testFileMatch: ["**/*.tst.*", "**/__typetests__/*.test.*", "**/typetests/*.test.*"],
|
|
1606
|
+
tsconfig: "findup",
|
|
1607
|
+
};
|
|
1559
1608
|
|
|
1560
1609
|
class Config {
|
|
1561
1610
|
static #onDiagnostics(diagnostic) {
|
|
@@ -1564,11 +1613,8 @@ class Config {
|
|
|
1564
1613
|
static async parseCommandLine(commandLine) {
|
|
1565
1614
|
const commandLineOptions = {};
|
|
1566
1615
|
const pathMatch = [];
|
|
1567
|
-
const commandLineParser = new
|
|
1616
|
+
const commandLineParser = new CommandParser(commandLineOptions, pathMatch, Config.#onDiagnostics);
|
|
1568
1617
|
await commandLineParser.parse(commandLine);
|
|
1569
|
-
if (commandLineOptions.target != null) {
|
|
1570
|
-
commandLineOptions.target = await Target.expand(commandLineOptions.target);
|
|
1571
|
-
}
|
|
1572
1618
|
return { commandLineOptions, pathMatch };
|
|
1573
1619
|
}
|
|
1574
1620
|
static async parseConfigFile(filePath) {
|
|
@@ -1580,12 +1626,9 @@ class Config {
|
|
|
1580
1626
|
const configFileText = await fs.readFile(configFilePath, {
|
|
1581
1627
|
encoding: "utf8",
|
|
1582
1628
|
});
|
|
1583
|
-
const sourceFile = new
|
|
1584
|
-
const configFileParser = new ConfigParser(configFileOptions,
|
|
1629
|
+
const sourceFile = new JsonSourceFile(configFilePath, configFileText);
|
|
1630
|
+
const configFileParser = new ConfigParser(configFileOptions, 4, sourceFile, new JsonScanner(sourceFile), Config.#onDiagnostics);
|
|
1585
1631
|
await configFileParser.parse();
|
|
1586
|
-
if (configFileOptions.target != null) {
|
|
1587
|
-
configFileOptions.target = await Target.expand(configFileOptions.target);
|
|
1588
|
-
}
|
|
1589
1632
|
}
|
|
1590
1633
|
return { configFileOptions, configFilePath };
|
|
1591
1634
|
}
|
|
@@ -1621,12 +1664,30 @@ class DirectiveDiagnosticText {
|
|
|
1621
1664
|
|
|
1622
1665
|
class Directive {
|
|
1623
1666
|
static #directiveRegex = /^(\/\/ *@tstyche)( *|-)?(\S*)?( *)?(.*)?/i;
|
|
1624
|
-
static
|
|
1667
|
+
static #rangeCache = new WeakMap();
|
|
1668
|
+
static getDirectiveRange(compiler, owner, directiveText) {
|
|
1669
|
+
const directiveRanges = Directive.getDirectiveRanges(compiler, owner.node);
|
|
1670
|
+
return directiveRanges?.find((range) => range.directive?.text === directiveText);
|
|
1671
|
+
}
|
|
1672
|
+
static getDirectiveRanges(compiler, node) {
|
|
1673
|
+
let ranges = Directive.#rangeCache.get(node);
|
|
1674
|
+
if (ranges != null) {
|
|
1675
|
+
return ranges;
|
|
1676
|
+
}
|
|
1677
|
+
let sourceFile;
|
|
1678
|
+
let position = 0;
|
|
1679
|
+
if (compiler.isSourceFile(node)) {
|
|
1680
|
+
sourceFile = node;
|
|
1681
|
+
}
|
|
1682
|
+
else {
|
|
1683
|
+
sourceFile = node.getSourceFile();
|
|
1684
|
+
position = node.getFullStart();
|
|
1685
|
+
}
|
|
1625
1686
|
const comments = compiler.getLeadingCommentRanges(sourceFile.text, position);
|
|
1626
1687
|
if (!comments || comments.length === 0) {
|
|
1627
1688
|
return;
|
|
1628
1689
|
}
|
|
1629
|
-
|
|
1690
|
+
ranges = [];
|
|
1630
1691
|
for (const comment of comments) {
|
|
1631
1692
|
if (comment.kind !== compiler.SyntaxKind.SingleLineCommentTrivia) {
|
|
1632
1693
|
continue;
|
|
@@ -1636,15 +1697,17 @@ class Directive {
|
|
|
1636
1697
|
ranges.push(range);
|
|
1637
1698
|
}
|
|
1638
1699
|
}
|
|
1700
|
+
Directive.#rangeCache.set(node, ranges);
|
|
1639
1701
|
return ranges;
|
|
1640
1702
|
}
|
|
1641
1703
|
static async getInlineConfig(ranges) {
|
|
1642
1704
|
if (!ranges) {
|
|
1643
1705
|
return;
|
|
1644
1706
|
}
|
|
1707
|
+
ranges = Array.isArray(ranges) ? ranges : [ranges];
|
|
1645
1708
|
const inlineConfig = {};
|
|
1646
1709
|
for (const range of ranges) {
|
|
1647
|
-
await Directive.#parse(inlineConfig,
|
|
1710
|
+
await Directive.#parse(inlineConfig, range);
|
|
1648
1711
|
}
|
|
1649
1712
|
return inlineConfig;
|
|
1650
1713
|
}
|
|
@@ -1656,6 +1719,7 @@ class Directive {
|
|
|
1656
1719
|
return;
|
|
1657
1720
|
}
|
|
1658
1721
|
const range = {
|
|
1722
|
+
sourceFile,
|
|
1659
1723
|
namespace: { start: comment.pos, end: comment.pos + namespaceText.length, text: namespaceText },
|
|
1660
1724
|
};
|
|
1661
1725
|
const directiveSeparatorText = match?.[2];
|
|
@@ -1675,45 +1739,61 @@ class Directive {
|
|
|
1675
1739
|
static #onDiagnostics(diagnostic) {
|
|
1676
1740
|
EventEmitter.dispatch(["directive:error", { diagnostics: [diagnostic] }]);
|
|
1677
1741
|
}
|
|
1678
|
-
static async #parse(inlineConfig,
|
|
1679
|
-
switch (
|
|
1742
|
+
static async #parse(inlineConfig, range) {
|
|
1743
|
+
switch (range.directive?.text) {
|
|
1680
1744
|
case "if":
|
|
1681
1745
|
{
|
|
1682
|
-
if (!
|
|
1746
|
+
if (!range.argument?.text) {
|
|
1683
1747
|
const text = DirectiveDiagnosticText.requiresArgument();
|
|
1684
|
-
const origin = new DiagnosticOrigin(
|
|
1748
|
+
const origin = new DiagnosticOrigin(range.namespace.start, range.directive.end, range.sourceFile);
|
|
1685
1749
|
Directive.#onDiagnostics(Diagnostic.error(text, origin));
|
|
1686
1750
|
return;
|
|
1687
1751
|
}
|
|
1688
|
-
const value = await Directive.#parseJson(sourceFile,
|
|
1752
|
+
const value = await Directive.#parseJson(range.sourceFile, range.argument.start, range.argument.end);
|
|
1689
1753
|
inlineConfig.if = value;
|
|
1690
1754
|
}
|
|
1691
1755
|
return;
|
|
1756
|
+
case "fixme":
|
|
1692
1757
|
case "template":
|
|
1693
|
-
if (
|
|
1758
|
+
if (range.argument?.text != null) {
|
|
1694
1759
|
const text = DirectiveDiagnosticText.doesNotTakeArgument();
|
|
1695
|
-
const origin = new DiagnosticOrigin(
|
|
1760
|
+
const origin = new DiagnosticOrigin(range.argument.start, range.argument.end, range.sourceFile);
|
|
1696
1761
|
Directive.#onDiagnostics(Diagnostic.error(text, origin));
|
|
1697
1762
|
}
|
|
1698
|
-
inlineConfig.
|
|
1763
|
+
inlineConfig[range.directive?.text] = true;
|
|
1699
1764
|
return;
|
|
1700
1765
|
}
|
|
1701
|
-
const target =
|
|
1766
|
+
const target = range?.directive ?? range.namespace;
|
|
1702
1767
|
const text = DirectiveDiagnosticText.isNotSupported(target.text);
|
|
1703
|
-
const origin = new DiagnosticOrigin(target.start, target.end, sourceFile);
|
|
1768
|
+
const origin = new DiagnosticOrigin(target.start, target.end, range.sourceFile);
|
|
1704
1769
|
Directive.#onDiagnostics(Diagnostic.error(text, origin));
|
|
1705
1770
|
}
|
|
1706
1771
|
static async #parseJson(sourceFile, start, end) {
|
|
1707
1772
|
const inlineOptions = {};
|
|
1708
|
-
const configParser = new ConfigParser(inlineOptions,
|
|
1773
|
+
const configParser = new ConfigParser(inlineOptions, 8, sourceFile, new JsonScanner(sourceFile, { start, end }), Directive.#onDiagnostics);
|
|
1709
1774
|
await configParser.parse();
|
|
1710
|
-
if ("target" in inlineOptions) {
|
|
1711
|
-
inlineOptions["target"] = await Target.expand(inlineOptions["target"]);
|
|
1712
|
-
}
|
|
1713
1775
|
return inlineOptions;
|
|
1714
1776
|
}
|
|
1715
1777
|
}
|
|
1716
1778
|
|
|
1779
|
+
var OptionBrand;
|
|
1780
|
+
(function (OptionBrand) {
|
|
1781
|
+
OptionBrand["String"] = "string";
|
|
1782
|
+
OptionBrand["SemverRange"] = "range";
|
|
1783
|
+
OptionBrand["Number"] = "number";
|
|
1784
|
+
OptionBrand["Boolean"] = "boolean";
|
|
1785
|
+
OptionBrand["True"] = "true";
|
|
1786
|
+
OptionBrand["List"] = "list";
|
|
1787
|
+
})(OptionBrand || (OptionBrand = {}));
|
|
1788
|
+
|
|
1789
|
+
var OptionGroup;
|
|
1790
|
+
(function (OptionGroup) {
|
|
1791
|
+
OptionGroup[OptionGroup["CommandLine"] = 2] = "CommandLine";
|
|
1792
|
+
OptionGroup[OptionGroup["ConfigFile"] = 4] = "ConfigFile";
|
|
1793
|
+
OptionGroup[OptionGroup["InlineConditions"] = 8] = "InlineConditions";
|
|
1794
|
+
OptionGroup[OptionGroup["ResolvedConfig"] = 6] = "ResolvedConfig";
|
|
1795
|
+
})(OptionGroup || (OptionGroup = {}));
|
|
1796
|
+
|
|
1717
1797
|
class CancellationHandler {
|
|
1718
1798
|
#cancellationToken;
|
|
1719
1799
|
#cancellationReason;
|
|
@@ -1723,7 +1803,7 @@ class CancellationHandler {
|
|
|
1723
1803
|
}
|
|
1724
1804
|
on([, payload]) {
|
|
1725
1805
|
if ("diagnostics" in payload) {
|
|
1726
|
-
if (payload.diagnostics.some((diagnostic) => diagnostic.category ===
|
|
1806
|
+
if (payload.diagnostics.some((diagnostic) => diagnostic.category === "error")) {
|
|
1727
1807
|
this.#cancellationToken.cancel(this.#cancellationReason);
|
|
1728
1808
|
}
|
|
1729
1809
|
}
|
|
@@ -1737,7 +1817,7 @@ class ExitCodeHandler {
|
|
|
1737
1817
|
return;
|
|
1738
1818
|
}
|
|
1739
1819
|
if ("diagnostics" in payload) {
|
|
1740
|
-
if (payload.diagnostics.some((diagnostic) => diagnostic.category ===
|
|
1820
|
+
if (payload.diagnostics.some((diagnostic) => diagnostic.category === "error")) {
|
|
1741
1821
|
this.#setCode(1);
|
|
1742
1822
|
}
|
|
1743
1823
|
}
|
|
@@ -1750,49 +1830,65 @@ class ExitCodeHandler {
|
|
|
1750
1830
|
}
|
|
1751
1831
|
}
|
|
1752
1832
|
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1833
|
+
function createObjectFromKeys(keys, defaultValue) {
|
|
1834
|
+
return Object.fromEntries(keys.map((key) => [key, defaultValue]));
|
|
1835
|
+
}
|
|
1836
|
+
function createTargetCounts() {
|
|
1837
|
+
return createObjectFromKeys(["failed", "passed"], 0);
|
|
1838
|
+
}
|
|
1839
|
+
function createFileCounts() {
|
|
1840
|
+
return createObjectFromKeys(["failed", "passed"], 0);
|
|
1841
|
+
}
|
|
1842
|
+
function createTestCounts() {
|
|
1843
|
+
return createObjectFromKeys(["failed", "passed", "fixme", "skipped", "todo"], 0);
|
|
1844
|
+
}
|
|
1845
|
+
function createAssertionCounts() {
|
|
1846
|
+
return createObjectFromKeys(["failed", "passed", "fixme", "skipped", "todo"], 0);
|
|
1847
|
+
}
|
|
1848
|
+
function createSuppressedCounts() {
|
|
1849
|
+
return createObjectFromKeys(["failed", "matched", "ignored"], 0);
|
|
1850
|
+
}
|
|
1851
|
+
function createResultTiming() {
|
|
1852
|
+
return createObjectFromKeys(["start", "end"], Number.NaN);
|
|
1759
1853
|
}
|
|
1760
1854
|
|
|
1761
1855
|
class DescribeResult {
|
|
1762
1856
|
describe;
|
|
1763
1857
|
parent;
|
|
1764
1858
|
results = [];
|
|
1765
|
-
timing =
|
|
1859
|
+
timing = createResultTiming();
|
|
1766
1860
|
constructor(describe, parent) {
|
|
1767
1861
|
this.describe = describe;
|
|
1768
1862
|
this.parent = parent;
|
|
1769
1863
|
}
|
|
1770
1864
|
}
|
|
1771
1865
|
|
|
1772
|
-
var ResultStatus;
|
|
1773
|
-
(function (ResultStatus) {
|
|
1774
|
-
ResultStatus["Runs"] = "runs";
|
|
1775
|
-
ResultStatus["Passed"] = "passed";
|
|
1776
|
-
ResultStatus["Failed"] = "failed";
|
|
1777
|
-
ResultStatus["Skipped"] = "skipped";
|
|
1778
|
-
ResultStatus["Todo"] = "todo";
|
|
1779
|
-
})(ResultStatus || (ResultStatus = {}));
|
|
1780
|
-
|
|
1781
1866
|
class ExpectResult {
|
|
1782
|
-
|
|
1783
|
-
diagnostics = [];
|
|
1867
|
+
expect;
|
|
1784
1868
|
parent;
|
|
1785
|
-
status =
|
|
1786
|
-
timing =
|
|
1787
|
-
constructor(
|
|
1788
|
-
this.
|
|
1869
|
+
status = "runs";
|
|
1870
|
+
timing = createResultTiming();
|
|
1871
|
+
constructor(expect, parent) {
|
|
1872
|
+
this.expect = expect;
|
|
1789
1873
|
this.parent = parent;
|
|
1790
1874
|
}
|
|
1791
1875
|
}
|
|
1792
1876
|
|
|
1877
|
+
class FileResult {
|
|
1878
|
+
assertionCounts = createAssertionCounts();
|
|
1879
|
+
file;
|
|
1880
|
+
results = [];
|
|
1881
|
+
suppressedCounts = createSuppressedCounts();
|
|
1882
|
+
status = "runs";
|
|
1883
|
+
testCounts = createTestCounts();
|
|
1884
|
+
timing = createResultTiming();
|
|
1885
|
+
constructor(file) {
|
|
1886
|
+
this.file = file;
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1793
1890
|
class ProjectResult {
|
|
1794
1891
|
compilerVersion;
|
|
1795
|
-
diagnostics = [];
|
|
1796
1892
|
projectConfigFilePath;
|
|
1797
1893
|
results = [];
|
|
1798
1894
|
constructor(compilerVersion, projectConfigFilePath) {
|
|
@@ -1801,62 +1897,58 @@ class ProjectResult {
|
|
|
1801
1897
|
}
|
|
1802
1898
|
}
|
|
1803
1899
|
|
|
1804
|
-
class
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1900
|
+
class Result {
|
|
1901
|
+
assertionCounts = createAssertionCounts();
|
|
1902
|
+
fileCounts = createFileCounts();
|
|
1903
|
+
files;
|
|
1904
|
+
results = [];
|
|
1905
|
+
suppressedCounts = createSuppressedCounts();
|
|
1906
|
+
targetCounts = createTargetCounts();
|
|
1907
|
+
testCounts = createTestCounts();
|
|
1908
|
+
timing = createResultTiming();
|
|
1909
|
+
constructor(files) {
|
|
1910
|
+
this.files = files;
|
|
1811
1911
|
}
|
|
1812
1912
|
}
|
|
1813
1913
|
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1914
|
+
var ResultStatus;
|
|
1915
|
+
(function (ResultStatus) {
|
|
1916
|
+
ResultStatus["Runs"] = "runs";
|
|
1917
|
+
ResultStatus["Passed"] = "passed";
|
|
1918
|
+
ResultStatus["Matched"] = "matched";
|
|
1919
|
+
ResultStatus["Failed"] = "failed";
|
|
1920
|
+
ResultStatus["Fixme"] = "fixme";
|
|
1921
|
+
ResultStatus["Skipped"] = "skipped";
|
|
1922
|
+
ResultStatus["Ignored"] = "ignored";
|
|
1923
|
+
ResultStatus["Todo"] = "todo";
|
|
1924
|
+
})(ResultStatus || (ResultStatus = {}));
|
|
1925
|
+
|
|
1926
|
+
class SuppressedResult {
|
|
1927
|
+
suppressed;
|
|
1928
|
+
constructor(suppressed) {
|
|
1929
|
+
this.suppressed = suppressed;
|
|
1824
1930
|
}
|
|
1825
1931
|
}
|
|
1826
1932
|
|
|
1827
1933
|
class TargetResult {
|
|
1934
|
+
files;
|
|
1828
1935
|
results = new Map();
|
|
1829
|
-
status =
|
|
1936
|
+
status = "runs";
|
|
1830
1937
|
target;
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
constructor(target, tasks) {
|
|
1938
|
+
timing = createResultTiming();
|
|
1939
|
+
constructor(target, files) {
|
|
1834
1940
|
this.target = target;
|
|
1835
|
-
this.
|
|
1836
|
-
}
|
|
1837
|
-
}
|
|
1838
|
-
|
|
1839
|
-
class TaskResult {
|
|
1840
|
-
diagnostics = [];
|
|
1841
|
-
expectCount = new ResultCount();
|
|
1842
|
-
results = [];
|
|
1843
|
-
status = ResultStatus.Runs;
|
|
1844
|
-
task;
|
|
1845
|
-
testCount = new ResultCount();
|
|
1846
|
-
timing = new ResultTiming();
|
|
1847
|
-
constructor(task) {
|
|
1848
|
-
this.task = task;
|
|
1941
|
+
this.files = files;
|
|
1849
1942
|
}
|
|
1850
1943
|
}
|
|
1851
1944
|
|
|
1852
1945
|
class TestResult {
|
|
1853
|
-
|
|
1854
|
-
expectCount = new ResultCount();
|
|
1946
|
+
assertionCounts = createAssertionCounts();
|
|
1855
1947
|
parent;
|
|
1856
1948
|
results = [];
|
|
1857
|
-
status =
|
|
1949
|
+
status = "runs";
|
|
1858
1950
|
test;
|
|
1859
|
-
timing =
|
|
1951
|
+
timing = createResultTiming();
|
|
1860
1952
|
constructor(test, parent) {
|
|
1861
1953
|
this.test = test;
|
|
1862
1954
|
this.parent = parent;
|
|
@@ -1866,10 +1958,10 @@ class TestResult {
|
|
|
1866
1958
|
class ResultHandler {
|
|
1867
1959
|
#describeResult;
|
|
1868
1960
|
#expectResult;
|
|
1961
|
+
#fileResult;
|
|
1869
1962
|
#projectResult;
|
|
1870
1963
|
#result;
|
|
1871
1964
|
#targetResult;
|
|
1872
|
-
#taskResult;
|
|
1873
1965
|
#testResult;
|
|
1874
1966
|
on([event, payload]) {
|
|
1875
1967
|
switch (event) {
|
|
@@ -1887,19 +1979,19 @@ class ResultHandler {
|
|
|
1887
1979
|
this.#targetResult.timing.start = Date.now();
|
|
1888
1980
|
break;
|
|
1889
1981
|
case "target:end":
|
|
1890
|
-
if (this.#targetResult.status ===
|
|
1891
|
-
this.#result.
|
|
1982
|
+
if (this.#targetResult.status === "failed") {
|
|
1983
|
+
this.#result.targetCounts.failed++;
|
|
1892
1984
|
}
|
|
1893
1985
|
else {
|
|
1894
|
-
this.#result.
|
|
1895
|
-
this.#targetResult.status =
|
|
1986
|
+
this.#result.targetCounts.passed++;
|
|
1987
|
+
this.#targetResult.status = "passed";
|
|
1896
1988
|
}
|
|
1897
1989
|
this.#targetResult.timing.end = Date.now();
|
|
1898
1990
|
this.#targetResult = undefined;
|
|
1899
1991
|
break;
|
|
1900
1992
|
case "store:error":
|
|
1901
|
-
if (payload.diagnostics.some(({ category }) => category ===
|
|
1902
|
-
this.#targetResult.status =
|
|
1993
|
+
if (payload.diagnostics.some(({ category }) => category === "error")) {
|
|
1994
|
+
this.#targetResult.status = "failed";
|
|
1903
1995
|
}
|
|
1904
1996
|
break;
|
|
1905
1997
|
case "project:uses": {
|
|
@@ -1912,42 +2004,40 @@ class ResultHandler {
|
|
|
1912
2004
|
break;
|
|
1913
2005
|
}
|
|
1914
2006
|
case "project:error":
|
|
1915
|
-
this.#targetResult.status =
|
|
1916
|
-
this.#projectResult.diagnostics.push(...payload.diagnostics);
|
|
2007
|
+
this.#targetResult.status = "failed";
|
|
1917
2008
|
break;
|
|
1918
|
-
case "
|
|
2009
|
+
case "file:start":
|
|
1919
2010
|
this.#projectResult.results.push(payload.result);
|
|
1920
|
-
this.#
|
|
1921
|
-
this.#
|
|
2011
|
+
this.#fileResult = payload.result;
|
|
2012
|
+
this.#fileResult.timing.start = Date.now();
|
|
1922
2013
|
break;
|
|
1923
|
-
case "
|
|
2014
|
+
case "file:error":
|
|
1924
2015
|
case "directive:error":
|
|
1925
2016
|
case "collect:error":
|
|
1926
|
-
this.#targetResult.status =
|
|
1927
|
-
this.#
|
|
1928
|
-
this.#taskResult.diagnostics.push(...payload.diagnostics);
|
|
2017
|
+
this.#targetResult.status = "failed";
|
|
2018
|
+
this.#fileResult.status = "failed";
|
|
1929
2019
|
break;
|
|
1930
|
-
case "
|
|
1931
|
-
if (this.#
|
|
1932
|
-
this.#
|
|
1933
|
-
this.#
|
|
1934
|
-
this.#result.
|
|
1935
|
-
this.#targetResult.status =
|
|
1936
|
-
this.#
|
|
2020
|
+
case "file:end":
|
|
2021
|
+
if (this.#fileResult.status === "failed" ||
|
|
2022
|
+
this.#fileResult.assertionCounts.failed > 0 ||
|
|
2023
|
+
this.#fileResult.testCounts.failed > 0) {
|
|
2024
|
+
this.#result.fileCounts.failed++;
|
|
2025
|
+
this.#targetResult.status = "failed";
|
|
2026
|
+
this.#fileResult.status = "failed";
|
|
1937
2027
|
}
|
|
1938
2028
|
else {
|
|
1939
|
-
this.#result.
|
|
1940
|
-
this.#
|
|
2029
|
+
this.#result.fileCounts.passed++;
|
|
2030
|
+
this.#fileResult.status = "passed";
|
|
1941
2031
|
}
|
|
1942
|
-
this.#
|
|
1943
|
-
this.#
|
|
2032
|
+
this.#fileResult.timing.end = Date.now();
|
|
2033
|
+
this.#fileResult = undefined;
|
|
1944
2034
|
break;
|
|
1945
2035
|
case "describe:start":
|
|
1946
2036
|
if (this.#describeResult) {
|
|
1947
2037
|
this.#describeResult.results.push(payload.result);
|
|
1948
2038
|
}
|
|
1949
2039
|
else {
|
|
1950
|
-
this.#
|
|
2040
|
+
this.#fileResult.results.push(payload.result);
|
|
1951
2041
|
}
|
|
1952
2042
|
this.#describeResult = payload.result;
|
|
1953
2043
|
this.#describeResult.timing.start = Date.now();
|
|
@@ -1961,44 +2051,44 @@ class ResultHandler {
|
|
|
1961
2051
|
this.#describeResult.results.push(payload.result);
|
|
1962
2052
|
}
|
|
1963
2053
|
else {
|
|
1964
|
-
this.#
|
|
2054
|
+
this.#fileResult.results.push(payload.result);
|
|
1965
2055
|
}
|
|
1966
2056
|
this.#testResult = payload.result;
|
|
1967
2057
|
this.#testResult.timing.start = Date.now();
|
|
1968
2058
|
break;
|
|
1969
2059
|
case "test:error":
|
|
1970
|
-
this.#result.testCount.failed++;
|
|
1971
|
-
this.#taskResult.testCount.failed++;
|
|
1972
|
-
this.#testResult.status = ResultStatus.Failed;
|
|
1973
|
-
this.#testResult.diagnostics.push(...payload.diagnostics);
|
|
1974
|
-
this.#testResult.timing.end = Date.now();
|
|
1975
|
-
this.#testResult = undefined;
|
|
1976
|
-
break;
|
|
1977
2060
|
case "test:fail":
|
|
1978
|
-
this.#result.
|
|
1979
|
-
this.#
|
|
1980
|
-
this.#testResult.status =
|
|
2061
|
+
this.#result.testCounts.failed++;
|
|
2062
|
+
this.#fileResult.testCounts.failed++;
|
|
2063
|
+
this.#testResult.status = "failed";
|
|
1981
2064
|
this.#testResult.timing.end = Date.now();
|
|
1982
2065
|
this.#testResult = undefined;
|
|
1983
2066
|
break;
|
|
1984
2067
|
case "test:pass":
|
|
1985
|
-
this.#result.
|
|
1986
|
-
this.#
|
|
1987
|
-
this.#testResult.status =
|
|
2068
|
+
this.#result.testCounts.passed++;
|
|
2069
|
+
this.#fileResult.testCounts.passed++;
|
|
2070
|
+
this.#testResult.status = "passed";
|
|
1988
2071
|
this.#testResult.timing.end = Date.now();
|
|
1989
2072
|
this.#testResult = undefined;
|
|
1990
2073
|
break;
|
|
1991
2074
|
case "test:skip":
|
|
1992
|
-
this.#result.
|
|
1993
|
-
this.#
|
|
1994
|
-
this.#testResult.status =
|
|
2075
|
+
this.#result.testCounts.skipped++;
|
|
2076
|
+
this.#fileResult.testCounts.skipped++;
|
|
2077
|
+
this.#testResult.status = "skipped";
|
|
2078
|
+
this.#testResult.timing.end = Date.now();
|
|
2079
|
+
this.#testResult = undefined;
|
|
2080
|
+
break;
|
|
2081
|
+
case "test:fixme":
|
|
2082
|
+
this.#result.testCounts.fixme++;
|
|
2083
|
+
this.#fileResult.testCounts.fixme++;
|
|
2084
|
+
this.#testResult.status = "fixme";
|
|
1995
2085
|
this.#testResult.timing.end = Date.now();
|
|
1996
2086
|
this.#testResult = undefined;
|
|
1997
2087
|
break;
|
|
1998
2088
|
case "test:todo":
|
|
1999
|
-
this.#result.
|
|
2000
|
-
this.#
|
|
2001
|
-
this.#testResult.status =
|
|
2089
|
+
this.#result.testCounts.todo++;
|
|
2090
|
+
this.#fileResult.testCounts.todo++;
|
|
2091
|
+
this.#testResult.status = "todo";
|
|
2002
2092
|
this.#testResult.timing.end = Date.now();
|
|
2003
2093
|
this.#testResult = undefined;
|
|
2004
2094
|
break;
|
|
@@ -2007,52 +2097,66 @@ class ResultHandler {
|
|
|
2007
2097
|
this.#testResult.results.push(payload.result);
|
|
2008
2098
|
}
|
|
2009
2099
|
else {
|
|
2010
|
-
this.#
|
|
2100
|
+
this.#fileResult.results.push(payload.result);
|
|
2011
2101
|
}
|
|
2012
2102
|
this.#expectResult = payload.result;
|
|
2013
2103
|
this.#expectResult.timing.start = Date.now();
|
|
2014
2104
|
break;
|
|
2015
2105
|
case "expect:error":
|
|
2016
|
-
|
|
2017
|
-
this.#
|
|
2106
|
+
case "expect:fail":
|
|
2107
|
+
this.#result.assertionCounts.failed++;
|
|
2108
|
+
this.#fileResult.assertionCounts.failed++;
|
|
2018
2109
|
if (this.#testResult) {
|
|
2019
|
-
this.#testResult.
|
|
2110
|
+
this.#testResult.assertionCounts.failed++;
|
|
2020
2111
|
}
|
|
2021
|
-
this.#expectResult.status =
|
|
2022
|
-
this.#expectResult.diagnostics.push(...payload.diagnostics);
|
|
2112
|
+
this.#expectResult.status = "failed";
|
|
2023
2113
|
this.#expectResult.timing.end = Date.now();
|
|
2024
2114
|
this.#expectResult = undefined;
|
|
2025
2115
|
break;
|
|
2026
|
-
case "expect:
|
|
2027
|
-
this.#result.
|
|
2028
|
-
this.#
|
|
2116
|
+
case "expect:pass":
|
|
2117
|
+
this.#result.assertionCounts.passed++;
|
|
2118
|
+
this.#fileResult.assertionCounts.passed++;
|
|
2029
2119
|
if (this.#testResult) {
|
|
2030
|
-
this.#testResult.
|
|
2120
|
+
this.#testResult.assertionCounts.passed++;
|
|
2031
2121
|
}
|
|
2032
|
-
this.#expectResult.status =
|
|
2122
|
+
this.#expectResult.status = "passed";
|
|
2033
2123
|
this.#expectResult.timing.end = Date.now();
|
|
2034
2124
|
this.#expectResult = undefined;
|
|
2035
2125
|
break;
|
|
2036
|
-
case "expect:
|
|
2037
|
-
this.#result.
|
|
2038
|
-
this.#
|
|
2126
|
+
case "expect:skip":
|
|
2127
|
+
this.#result.assertionCounts.skipped++;
|
|
2128
|
+
this.#fileResult.assertionCounts.skipped++;
|
|
2039
2129
|
if (this.#testResult) {
|
|
2040
|
-
this.#testResult.
|
|
2130
|
+
this.#testResult.assertionCounts.skipped++;
|
|
2041
2131
|
}
|
|
2042
|
-
this.#expectResult.status =
|
|
2132
|
+
this.#expectResult.status = "skipped";
|
|
2043
2133
|
this.#expectResult.timing.end = Date.now();
|
|
2044
2134
|
this.#expectResult = undefined;
|
|
2045
2135
|
break;
|
|
2046
|
-
case "expect:
|
|
2047
|
-
this.#result.
|
|
2048
|
-
this.#
|
|
2136
|
+
case "expect:fixme":
|
|
2137
|
+
this.#result.assertionCounts.fixme++;
|
|
2138
|
+
this.#fileResult.assertionCounts.fixme++;
|
|
2049
2139
|
if (this.#testResult) {
|
|
2050
|
-
this.#testResult.
|
|
2140
|
+
this.#testResult.assertionCounts.fixme++;
|
|
2051
2141
|
}
|
|
2052
|
-
this.#expectResult.status =
|
|
2142
|
+
this.#expectResult.status = "fixme";
|
|
2053
2143
|
this.#expectResult.timing.end = Date.now();
|
|
2054
2144
|
this.#expectResult = undefined;
|
|
2055
2145
|
break;
|
|
2146
|
+
case "suppressed:error":
|
|
2147
|
+
this.#result.suppressedCounts.failed++;
|
|
2148
|
+
this.#fileResult.suppressedCounts.failed++;
|
|
2149
|
+
this.#targetResult.status = "failed";
|
|
2150
|
+
this.#fileResult.status = "failed";
|
|
2151
|
+
break;
|
|
2152
|
+
case "suppressed:match":
|
|
2153
|
+
this.#result.suppressedCounts.matched++;
|
|
2154
|
+
this.#fileResult.suppressedCounts.matched++;
|
|
2155
|
+
break;
|
|
2156
|
+
case "suppressed:ignore":
|
|
2157
|
+
this.#result.suppressedCounts.ignored++;
|
|
2158
|
+
this.#fileResult.suppressedCounts.ignored++;
|
|
2159
|
+
break;
|
|
2056
2160
|
}
|
|
2057
2161
|
}
|
|
2058
2162
|
}
|
|
@@ -2078,7 +2182,7 @@ function Text({ children, color, indent }) {
|
|
|
2078
2182
|
if (color != null) {
|
|
2079
2183
|
ansiEscapes.push(color);
|
|
2080
2184
|
}
|
|
2081
|
-
return (jsx("text", { indent: indent ?? 0, children: [ansiEscapes.length > 0 ? jsx("ansi", { escapes: ansiEscapes }) : undefined, children, ansiEscapes.length > 0 ? jsx("ansi", { escapes:
|
|
2185
|
+
return (jsx("text", { indent: indent ?? 0, children: [ansiEscapes.length > 0 ? jsx("ansi", { escapes: ansiEscapes }) : undefined, children, ansiEscapes.length > 0 ? jsx("ansi", { escapes: "0" }) : undefined] }));
|
|
2082
2186
|
}
|
|
2083
2187
|
|
|
2084
2188
|
function Line({ children, color, indent }) {
|
|
@@ -2139,7 +2243,7 @@ class Scribbler {
|
|
|
2139
2243
|
}
|
|
2140
2244
|
|
|
2141
2245
|
function addsPackageText(packageVersion, packagePath) {
|
|
2142
|
-
return (jsx(Line, { children: [jsx(Text, { color:
|
|
2246
|
+
return (jsx(Line, { children: [jsx(Text, { color: "90", children: "adds" }), " TypeScript ", packageVersion, jsx(Text, { color: "90", children: [" to ", packagePath] })] }));
|
|
2143
2247
|
}
|
|
2144
2248
|
|
|
2145
2249
|
function describeNameText(name, indent = 0) {
|
|
@@ -2155,13 +2259,13 @@ function BreadcrumbsText({ ancestor }) {
|
|
|
2155
2259
|
ancestor = ancestor.parent;
|
|
2156
2260
|
}
|
|
2157
2261
|
text.push("");
|
|
2158
|
-
return jsx(Text, { color:
|
|
2262
|
+
return jsx(Text, { color: "90", children: text.reverse().join(" ❭ ") });
|
|
2159
2263
|
}
|
|
2160
|
-
function CodeLineText({ gutterWidth, lineNumber, lineNumberColor =
|
|
2161
|
-
return (jsx(Line, { children: [jsx(Text, { color: lineNumberColor, children: lineNumber.toString().padStart(gutterWidth) }), jsx(Text, { color:
|
|
2264
|
+
function CodeLineText({ gutterWidth, lineNumber, lineNumberColor = "90", lineText }) {
|
|
2265
|
+
return (jsx(Line, { children: [jsx(Text, { color: lineNumberColor, children: lineNumber.toString().padStart(gutterWidth) }), jsx(Text, { color: "90", children: " | " }), lineText] }));
|
|
2162
2266
|
}
|
|
2163
2267
|
function SquiggleLineText({ gutterWidth, indentWidth = 0, squiggleColor, squiggleWidth }) {
|
|
2164
|
-
return (jsx(Line, { children: [" ".repeat(gutterWidth), jsx(Text, { color:
|
|
2268
|
+
return (jsx(Line, { children: [" ".repeat(gutterWidth), jsx(Text, { color: "90", children: " | " }), " ".repeat(indentWidth), jsx(Text, { color: squiggleColor, children: "~".repeat(squiggleWidth === 0 ? 1 : squiggleWidth) })] }));
|
|
2165
2269
|
}
|
|
2166
2270
|
function CodeFrameText({ diagnosticCategory, diagnosticOrigin, options }) {
|
|
2167
2271
|
const linesAbove = options?.linesAbove ?? 2;
|
|
@@ -2175,11 +2279,11 @@ function CodeFrameText({ diagnosticCategory, diagnosticOrigin, options }) {
|
|
|
2175
2279
|
const gutterWidth = (lastLine + 1).toString().length + 2;
|
|
2176
2280
|
let highlightColor;
|
|
2177
2281
|
switch (diagnosticCategory) {
|
|
2178
|
-
case
|
|
2179
|
-
highlightColor =
|
|
2282
|
+
case "error":
|
|
2283
|
+
highlightColor = "31";
|
|
2180
2284
|
break;
|
|
2181
|
-
case
|
|
2182
|
-
highlightColor =
|
|
2285
|
+
case "warning":
|
|
2286
|
+
highlightColor = "33";
|
|
2183
2287
|
break;
|
|
2184
2288
|
}
|
|
2185
2289
|
const codeFrame = [];
|
|
@@ -2207,15 +2311,15 @@ function CodeFrameText({ diagnosticCategory, diagnosticOrigin, options }) {
|
|
|
2207
2311
|
}
|
|
2208
2312
|
}
|
|
2209
2313
|
let breadcrumbs;
|
|
2210
|
-
if (showBreadcrumbs && diagnosticOrigin.
|
|
2211
|
-
breadcrumbs = jsx(BreadcrumbsText, { ancestor: diagnosticOrigin.
|
|
2314
|
+
if (showBreadcrumbs && diagnosticOrigin.assertionNode != null) {
|
|
2315
|
+
breadcrumbs = jsx(BreadcrumbsText, { ancestor: diagnosticOrigin.assertionNode.parent });
|
|
2212
2316
|
}
|
|
2213
|
-
const location = (jsx(Line, { children: [" ".repeat(gutterWidth + 2), jsx(Text, { color:
|
|
2317
|
+
const location = (jsx(Line, { children: [" ".repeat(gutterWidth + 2), jsx(Text, { color: "90", children: " at " }), jsx(Text, { color: "36", children: Path.relative("", diagnosticOrigin.sourceFile.fileName) }), jsx(Text, { color: "90", children: `:${firstMarkedLine + 1}:${firstMarkedLineCharacter + 1}` }), breadcrumbs] }));
|
|
2214
2318
|
return (jsx(Text, { children: [codeFrame, jsx(Line, {}), location] }));
|
|
2215
2319
|
}
|
|
2216
2320
|
|
|
2217
2321
|
function DiagnosticText({ codeFrameOptions, diagnostic }) {
|
|
2218
|
-
const code = diagnostic.code ? jsx(Text, { color:
|
|
2322
|
+
const code = diagnostic.code ? jsx(Text, { color: "90", children: [" ", diagnostic.code] }) : undefined;
|
|
2219
2323
|
const text = Array.isArray(diagnostic.text) ? diagnostic.text : [diagnostic.text];
|
|
2220
2324
|
const message = text.map((text, index) => (jsx(Text, { children: [index === 1 ? jsx(Line, {}) : undefined, jsx(Line, { children: [text, index === 0 ? code : undefined] })] })));
|
|
2221
2325
|
const related = diagnostic.related?.map((relatedDiagnostic) => jsx(DiagnosticText, { diagnostic: relatedDiagnostic }));
|
|
@@ -2225,16 +2329,63 @@ function DiagnosticText({ codeFrameOptions, diagnostic }) {
|
|
|
2225
2329
|
function diagnosticText(diagnostic, codeFrameOptions = {}) {
|
|
2226
2330
|
let prefix;
|
|
2227
2331
|
switch (diagnostic.category) {
|
|
2228
|
-
case
|
|
2229
|
-
prefix = jsx(Text, { color:
|
|
2332
|
+
case "error":
|
|
2333
|
+
prefix = jsx(Text, { color: "31", children: "Error: " });
|
|
2230
2334
|
break;
|
|
2231
|
-
case
|
|
2232
|
-
prefix = jsx(Text, { color:
|
|
2335
|
+
case "warning":
|
|
2336
|
+
prefix = jsx(Text, { color: "33", children: "Warning: " });
|
|
2233
2337
|
break;
|
|
2234
2338
|
}
|
|
2235
2339
|
return (jsx(Text, { children: [prefix, jsx(DiagnosticText, { codeFrameOptions: codeFrameOptions, diagnostic: diagnostic })] }));
|
|
2236
2340
|
}
|
|
2237
2341
|
|
|
2342
|
+
function getStatusColor(status) {
|
|
2343
|
+
switch (status) {
|
|
2344
|
+
case "runs":
|
|
2345
|
+
return "33";
|
|
2346
|
+
case "passed":
|
|
2347
|
+
case "matched":
|
|
2348
|
+
return "32";
|
|
2349
|
+
case "failed":
|
|
2350
|
+
return "31";
|
|
2351
|
+
case "fixme":
|
|
2352
|
+
case "skipped":
|
|
2353
|
+
case "ignored":
|
|
2354
|
+
return "33";
|
|
2355
|
+
case "todo":
|
|
2356
|
+
return "35";
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
function duration(timing) {
|
|
2360
|
+
return timing.end - timing.start;
|
|
2361
|
+
}
|
|
2362
|
+
function total(counts) {
|
|
2363
|
+
return Object.values(counts).reduce((sum, value) => sum + value, 0);
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2366
|
+
function FileNameText({ filePath }) {
|
|
2367
|
+
const relativePath = Path.relative("", filePath);
|
|
2368
|
+
const lastPathSeparator = relativePath.lastIndexOf("/");
|
|
2369
|
+
const directoryNameText = relativePath.slice(0, lastPathSeparator + 1);
|
|
2370
|
+
const fileNameText = relativePath.slice(lastPathSeparator + 1);
|
|
2371
|
+
return (jsx(Text, { children: [jsx(Text, { color: "90", children: directoryNameText }), fileNameText] }));
|
|
2372
|
+
}
|
|
2373
|
+
function fileStatusText(status, file) {
|
|
2374
|
+
let statusText;
|
|
2375
|
+
switch (status) {
|
|
2376
|
+
case "runs":
|
|
2377
|
+
statusText = "runs";
|
|
2378
|
+
break;
|
|
2379
|
+
case "passed":
|
|
2380
|
+
statusText = "pass";
|
|
2381
|
+
break;
|
|
2382
|
+
case "failed":
|
|
2383
|
+
statusText = "fail";
|
|
2384
|
+
break;
|
|
2385
|
+
}
|
|
2386
|
+
return (jsx(Line, { children: [jsx(Text, { color: getStatusColor(status), children: statusText }), " ", jsx(FileNameText, { filePath: file.path })] }));
|
|
2387
|
+
}
|
|
2388
|
+
|
|
2238
2389
|
function fileViewText(lines, addEmptyFinalLine) {
|
|
2239
2390
|
return (jsx(Text, { children: [[...lines], addEmptyFinalLine ? jsx(Line, {}) : undefined] }));
|
|
2240
2391
|
}
|
|
@@ -2258,13 +2409,13 @@ function formattedText(input) {
|
|
|
2258
2409
|
}
|
|
2259
2410
|
|
|
2260
2411
|
function HintText({ children }) {
|
|
2261
|
-
return (jsx(Text, { indent: 1, color:
|
|
2412
|
+
return (jsx(Text, { indent: 1, color: "90", children: children }));
|
|
2262
2413
|
}
|
|
2263
2414
|
function HelpHeaderText({ tstycheVersion }) {
|
|
2264
2415
|
return (jsx(Line, { children: ["The TSTyche Type Test Runner", jsx(HintText, { children: tstycheVersion })] }));
|
|
2265
2416
|
}
|
|
2266
2417
|
function CommandText({ hint, text }) {
|
|
2267
|
-
return (jsx(Line, { indent: 1, children: [jsx(Text, { color:
|
|
2418
|
+
return (jsx(Line, { indent: 1, children: [jsx(Text, { color: "34", children: text }), hint && jsx(HintText, { children: hint })] }));
|
|
2268
2419
|
}
|
|
2269
2420
|
function OptionDescriptionText({ text }) {
|
|
2270
2421
|
return jsx(Line, { indent: 1, children: text });
|
|
@@ -2272,8 +2423,8 @@ function OptionDescriptionText({ text }) {
|
|
|
2272
2423
|
function CommandLineUsageText() {
|
|
2273
2424
|
const usage = [
|
|
2274
2425
|
["tstyche", "Run all tests."],
|
|
2275
|
-
["tstyche
|
|
2276
|
-
["tstyche --target 5.3
|
|
2426
|
+
["tstyche query-params", "Only run the matching test file."],
|
|
2427
|
+
["tstyche --target '5.3 || 5.5.2 || >=5.7'", "Test against specific versions of TypeScript."],
|
|
2277
2428
|
];
|
|
2278
2429
|
const usageText = usage.map(([commandText, descriptionText]) => (jsx(Line, { children: [jsx(CommandText, { text: commandText }), jsx(OptionDescriptionText, { text: descriptionText })] })));
|
|
2279
2430
|
return jsx(Text, { children: usageText });
|
|
@@ -2282,16 +2433,19 @@ function CommandLineOptionNameText({ text }) {
|
|
|
2282
2433
|
return jsx(Text, { children: `--${text}` });
|
|
2283
2434
|
}
|
|
2284
2435
|
function CommandLineOptionHintText({ definition }) {
|
|
2285
|
-
if (definition.brand ===
|
|
2436
|
+
if (definition.brand === "list") {
|
|
2286
2437
|
return jsx(Text, { children: `${definition.brand} of ${definition.items.brand}s` });
|
|
2287
2438
|
}
|
|
2439
|
+
if (definition.brand === "range") {
|
|
2440
|
+
return jsx(Text, { children: "string" });
|
|
2441
|
+
}
|
|
2288
2442
|
return jsx(Text, { children: definition.brand });
|
|
2289
2443
|
}
|
|
2290
2444
|
function CommandLineOptionsText({ optionDefinitions }) {
|
|
2291
2445
|
const definitions = [...optionDefinitions.values()];
|
|
2292
2446
|
const optionsText = definitions.map((definition) => {
|
|
2293
2447
|
let hint;
|
|
2294
|
-
if (definition.brand !==
|
|
2448
|
+
if (definition.brand !== "true") {
|
|
2295
2449
|
hint = jsx(CommandLineOptionHintText, { definition: definition });
|
|
2296
2450
|
}
|
|
2297
2451
|
return (jsx(Text, { children: [jsx(CommandText, { text: jsx(CommandLineOptionNameText, { text: definition.name }), hint: hint }), jsx(OptionDescriptionText, { text: definition.description }), jsx(Line, {})] }));
|
|
@@ -2340,69 +2494,60 @@ class OutputService {
|
|
|
2340
2494
|
function RowText({ label, text }) {
|
|
2341
2495
|
return (jsx(Line, { children: [`${label}:`.padEnd(12), text] }));
|
|
2342
2496
|
}
|
|
2343
|
-
function
|
|
2344
|
-
|
|
2497
|
+
function CountsText({ counts, total }) {
|
|
2498
|
+
const countsText = Object.entries(counts).map(([status, count]) => {
|
|
2499
|
+
return (jsx(Text, { children: count > 0 ? (jsx(Text, { children: [jsx(Text, { color: getStatusColor(status), children: [count, " ", status] }), jsx(Text, { children: ", " })] })) : undefined }));
|
|
2500
|
+
});
|
|
2501
|
+
const totalText = (jsx(Text, { children: [total, " ", "total"] }));
|
|
2502
|
+
return (jsx(Text, { children: [countsText, totalText] }));
|
|
2345
2503
|
}
|
|
2346
|
-
function DurationText({
|
|
2504
|
+
function DurationText({ timing }) {
|
|
2505
|
+
const seconds = duration(timing) / 1000;
|
|
2347
2506
|
return jsx(Text, { children: `${Math.round(seconds * 10) / 10}s` });
|
|
2348
2507
|
}
|
|
2349
|
-
function summaryText({
|
|
2350
|
-
const
|
|
2351
|
-
const
|
|
2352
|
-
const
|
|
2353
|
-
const
|
|
2354
|
-
|
|
2508
|
+
function summaryText({ targetCounts, fileCounts, testCounts, assertionCounts, suppressedCounts, timing, }) {
|
|
2509
|
+
const targetCountsTotal = total(targetCounts);
|
|
2510
|
+
const targetCountsText = (jsx(RowText, { label: "Targets", text: jsx(CountsText, { counts: targetCounts, total: targetCountsTotal }) }));
|
|
2511
|
+
const fileCountsTotal = total(fileCounts);
|
|
2512
|
+
const fileCountsText = (jsx(RowText, { label: "Test files", text: jsx(CountsText, { counts: fileCounts, total: fileCountsTotal }) }));
|
|
2513
|
+
const testCountsTotal = total(testCounts);
|
|
2514
|
+
const testCountsText = testCountsTotal > 0 ? (jsx(RowText, { label: "Tests", text: jsx(CountsText, { counts: testCounts, total: testCountsTotal }) })) : undefined;
|
|
2515
|
+
const assertionCountsTotal = total(assertionCounts);
|
|
2516
|
+
const assertionCountsText = assertionCountsTotal > 0 ? (jsx(RowText, { label: "Assertions", text: jsx(CountsText, { counts: assertionCounts, total: assertionCountsTotal }) })) : undefined;
|
|
2517
|
+
const suppressedCountsTotal = total(suppressedCounts);
|
|
2518
|
+
const suppressedCountsText = suppressedCountsTotal > 0 ? (jsx(RowText, { label: "Suppressed", text: jsx(CountsText, { counts: suppressedCounts, total: suppressedCountsTotal }) })) : undefined;
|
|
2519
|
+
const durationText = jsx(RowText, { label: "Duration", text: jsx(DurationText, { timing: timing }) });
|
|
2520
|
+
return (jsx(Text, { children: [targetCountsText, fileCountsText, testCountsText, assertionCountsText, suppressedCountsText, durationText] }));
|
|
2355
2521
|
}
|
|
2356
2522
|
|
|
2357
|
-
function
|
|
2358
|
-
const relativePath = Path.relative("", filePath);
|
|
2359
|
-
const lastPathSeparator = relativePath.lastIndexOf("/");
|
|
2360
|
-
const directoryNameText = relativePath.slice(0, lastPathSeparator + 1);
|
|
2361
|
-
const fileNameText = relativePath.slice(lastPathSeparator + 1);
|
|
2362
|
-
return (jsx(Text, { children: [jsx(Text, { color: Color.Gray, children: directoryNameText }), fileNameText] }));
|
|
2363
|
-
}
|
|
2364
|
-
function taskStatusText(status, task) {
|
|
2365
|
-
let statusColor;
|
|
2523
|
+
function testNameText(status, name, indent = 0) {
|
|
2366
2524
|
let statusText;
|
|
2367
2525
|
switch (status) {
|
|
2368
|
-
case
|
|
2369
|
-
|
|
2370
|
-
statusText = "runs";
|
|
2526
|
+
case "passed":
|
|
2527
|
+
statusText = "+";
|
|
2371
2528
|
break;
|
|
2372
|
-
case
|
|
2373
|
-
|
|
2374
|
-
statusText = "pass";
|
|
2529
|
+
case "failed":
|
|
2530
|
+
statusText = "×";
|
|
2375
2531
|
break;
|
|
2376
|
-
case
|
|
2377
|
-
|
|
2378
|
-
|
|
2532
|
+
case "skipped":
|
|
2533
|
+
statusText = "- skip";
|
|
2534
|
+
break;
|
|
2535
|
+
case "fixme":
|
|
2536
|
+
statusText = "- fixme";
|
|
2379
2537
|
break;
|
|
2380
|
-
}
|
|
2381
|
-
return (jsx(Line, { children: [jsx(Text, { color: statusColor, children: statusText }), " ", jsx(FileNameText, { filePath: task.filePath })] }));
|
|
2382
|
-
}
|
|
2383
|
-
|
|
2384
|
-
function StatusText({ status }) {
|
|
2385
|
-
switch (status) {
|
|
2386
|
-
case "fail":
|
|
2387
|
-
return jsx(Text, { color: Color.Red, children: "\u00D7" });
|
|
2388
|
-
case "pass":
|
|
2389
|
-
return jsx(Text, { color: Color.Green, children: "+" });
|
|
2390
|
-
case "skip":
|
|
2391
|
-
return jsx(Text, { color: Color.Yellow, children: "- skip" });
|
|
2392
2538
|
case "todo":
|
|
2393
|
-
|
|
2539
|
+
statusText = "- todo";
|
|
2540
|
+
break;
|
|
2394
2541
|
}
|
|
2395
|
-
}
|
|
2396
|
-
function testNameText(status, name, indent = 0) {
|
|
2397
|
-
return (jsx(Line, { indent: indent + 1, children: [jsx(StatusText, { status: status }), " ", jsx(Text, { color: Color.Gray, children: name })] }));
|
|
2542
|
+
return (jsx(Line, { indent: indent + 1, children: [jsx(Text, { color: getStatusColor(status), children: statusText }), " ", jsx(Text, { color: "90", children: name })] }));
|
|
2398
2543
|
}
|
|
2399
2544
|
|
|
2400
2545
|
function usesCompilerText(compilerVersion, projectConfigFilePath, options) {
|
|
2401
2546
|
let projectConfigPathText;
|
|
2402
2547
|
if (projectConfigFilePath != null) {
|
|
2403
|
-
projectConfigPathText = (jsx(Text, { color:
|
|
2548
|
+
projectConfigPathText = (jsx(Text, { color: "90", children: [" with ", Path.relative("", projectConfigFilePath)] }));
|
|
2404
2549
|
}
|
|
2405
|
-
return (jsx(Text, { children: [options?.prependEmptyLine ? jsx(Line, {}) : undefined, jsx(Line, { children: [jsx(Text, { color:
|
|
2550
|
+
return (jsx(Text, { children: [options?.prependEmptyLine ? jsx(Line, {}) : undefined, jsx(Line, { children: [jsx(Text, { color: "34", children: "uses" }), " TypeScript ", compilerVersion, projectConfigPathText] }), jsx(Line, {})] }));
|
|
2406
2551
|
}
|
|
2407
2552
|
|
|
2408
2553
|
function waitingForFileChangesText() {
|
|
@@ -2415,7 +2560,7 @@ function watchUsageText() {
|
|
|
2415
2560
|
["x", "to exit."],
|
|
2416
2561
|
];
|
|
2417
2562
|
const usageText = usage.map(([keyText, actionText]) => {
|
|
2418
|
-
return (jsx(Line, { children: [jsx(Text, { color:
|
|
2563
|
+
return (jsx(Line, { children: [jsx(Text, { color: "90", children: "Press" }), jsx(Text, { children: ` ${keyText} ` }), jsx(Text, { color: "90", children: actionText })] }));
|
|
2419
2564
|
});
|
|
2420
2565
|
return jsx(Text, { children: usageText });
|
|
2421
2566
|
}
|
|
@@ -2492,7 +2637,7 @@ class ListReporter extends BaseReporter {
|
|
|
2492
2637
|
on([event, payload]) {
|
|
2493
2638
|
switch (event) {
|
|
2494
2639
|
case "run:start":
|
|
2495
|
-
this.#isFileViewExpanded = payload.result.
|
|
2640
|
+
this.#isFileViewExpanded = payload.result.files.length === 1 && this.resolvedConfig.watch !== true;
|
|
2496
2641
|
break;
|
|
2497
2642
|
case "store:adds":
|
|
2498
2643
|
OutputService.writeMessage(addsPackageText(payload.packageVersion, payload.packagePath));
|
|
@@ -2504,7 +2649,7 @@ class ListReporter extends BaseReporter {
|
|
|
2504
2649
|
}
|
|
2505
2650
|
break;
|
|
2506
2651
|
case "target:start":
|
|
2507
|
-
this.#fileCount = payload.result.
|
|
2652
|
+
this.#fileCount = payload.result.files.length;
|
|
2508
2653
|
this.#hasReportedUses = false;
|
|
2509
2654
|
break;
|
|
2510
2655
|
case "project:uses":
|
|
@@ -2519,25 +2664,26 @@ class ListReporter extends BaseReporter {
|
|
|
2519
2664
|
OutputService.writeError(diagnosticText(diagnostic));
|
|
2520
2665
|
}
|
|
2521
2666
|
break;
|
|
2522
|
-
case "
|
|
2667
|
+
case "file:start":
|
|
2523
2668
|
if (!environmentOptions.noInteractive) {
|
|
2524
|
-
OutputService.writeMessage(
|
|
2669
|
+
OutputService.writeMessage(fileStatusText(payload.result.status, payload.result.file));
|
|
2525
2670
|
}
|
|
2526
2671
|
this.#fileCount--;
|
|
2527
2672
|
this.#hasReportedError = false;
|
|
2528
2673
|
break;
|
|
2529
|
-
case "
|
|
2674
|
+
case "file:error":
|
|
2530
2675
|
case "directive:error":
|
|
2531
2676
|
case "collect:error":
|
|
2677
|
+
case "suppressed:error":
|
|
2532
2678
|
for (const diagnostic of payload.diagnostics) {
|
|
2533
2679
|
this.#fileView.addMessage(diagnosticText(diagnostic));
|
|
2534
2680
|
}
|
|
2535
2681
|
break;
|
|
2536
|
-
case "
|
|
2682
|
+
case "file:end":
|
|
2537
2683
|
if (!environmentOptions.noInteractive) {
|
|
2538
2684
|
OutputService.eraseLastLine();
|
|
2539
2685
|
}
|
|
2540
|
-
OutputService.writeMessage(
|
|
2686
|
+
OutputService.writeMessage(fileStatusText(payload.result.status, payload.result.file));
|
|
2541
2687
|
OutputService.writeMessage(this.#fileView.getViewText({ appendEmptyLine: this.#isLastFile }));
|
|
2542
2688
|
if (this.#fileView.hasErrors) {
|
|
2543
2689
|
OutputService.writeError(this.#fileView.getMessages());
|
|
@@ -2557,7 +2703,12 @@ class ListReporter extends BaseReporter {
|
|
|
2557
2703
|
break;
|
|
2558
2704
|
case "test:skip":
|
|
2559
2705
|
if (this.#isFileViewExpanded) {
|
|
2560
|
-
this.#fileView.addTest("
|
|
2706
|
+
this.#fileView.addTest("skipped", payload.result.test.name);
|
|
2707
|
+
}
|
|
2708
|
+
break;
|
|
2709
|
+
case "test:fixme":
|
|
2710
|
+
if (this.#isFileViewExpanded) {
|
|
2711
|
+
this.#fileView.addTest("fixme", payload.result.test.name);
|
|
2561
2712
|
}
|
|
2562
2713
|
break;
|
|
2563
2714
|
case "test:todo":
|
|
@@ -2567,7 +2718,7 @@ class ListReporter extends BaseReporter {
|
|
|
2567
2718
|
break;
|
|
2568
2719
|
case "test:error":
|
|
2569
2720
|
if (this.#isFileViewExpanded) {
|
|
2570
|
-
this.#fileView.addTest("
|
|
2721
|
+
this.#fileView.addTest("failed", payload.result.test.name);
|
|
2571
2722
|
}
|
|
2572
2723
|
for (const diagnostic of payload.diagnostics) {
|
|
2573
2724
|
this.#fileView.addMessage(diagnosticText(diagnostic));
|
|
@@ -2575,12 +2726,12 @@ class ListReporter extends BaseReporter {
|
|
|
2575
2726
|
break;
|
|
2576
2727
|
case "test:fail":
|
|
2577
2728
|
if (this.#isFileViewExpanded) {
|
|
2578
|
-
this.#fileView.addTest("
|
|
2729
|
+
this.#fileView.addTest("failed", payload.result.test.name);
|
|
2579
2730
|
}
|
|
2580
2731
|
break;
|
|
2581
2732
|
case "test:pass":
|
|
2582
2733
|
if (this.#isFileViewExpanded) {
|
|
2583
|
-
this.#fileView.addTest("
|
|
2734
|
+
this.#fileView.addTest("passed", payload.result.test.name);
|
|
2584
2735
|
}
|
|
2585
2736
|
break;
|
|
2586
2737
|
case "expect:error":
|
|
@@ -2602,10 +2753,10 @@ class SetupReporter {
|
|
|
2602
2753
|
if ("diagnostics" in payload) {
|
|
2603
2754
|
for (const diagnostic of payload.diagnostics) {
|
|
2604
2755
|
switch (diagnostic.category) {
|
|
2605
|
-
case
|
|
2756
|
+
case "error":
|
|
2606
2757
|
OutputService.writeError(diagnosticText(diagnostic));
|
|
2607
2758
|
break;
|
|
2608
|
-
case
|
|
2759
|
+
case "warning":
|
|
2609
2760
|
OutputService.writeWarning(diagnosticText(diagnostic));
|
|
2610
2761
|
break;
|
|
2611
2762
|
}
|
|
@@ -2621,11 +2772,12 @@ class SummaryReporter extends BaseReporter {
|
|
|
2621
2772
|
}
|
|
2622
2773
|
if (event === "run:end") {
|
|
2623
2774
|
OutputService.writeMessage(summaryText({
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2775
|
+
targetCounts: payload.result.targetCounts,
|
|
2776
|
+
fileCounts: payload.result.fileCounts,
|
|
2777
|
+
testCounts: payload.result.testCounts,
|
|
2778
|
+
assertionCounts: payload.result.assertionCounts,
|
|
2779
|
+
suppressedCounts: payload.result.suppressedCounts,
|
|
2780
|
+
timing: payload.result.timing,
|
|
2629
2781
|
}));
|
|
2630
2782
|
}
|
|
2631
2783
|
}
|
|
@@ -2651,18 +2803,18 @@ class WatchReporter extends BaseReporter {
|
|
|
2651
2803
|
}
|
|
2652
2804
|
}
|
|
2653
2805
|
|
|
2654
|
-
class
|
|
2655
|
-
|
|
2806
|
+
class FileLocation {
|
|
2807
|
+
path;
|
|
2656
2808
|
position;
|
|
2657
|
-
constructor(
|
|
2658
|
-
this.
|
|
2809
|
+
constructor(file, position) {
|
|
2810
|
+
this.path = Path.resolve(this.#toPath(file));
|
|
2659
2811
|
this.position = position;
|
|
2660
2812
|
}
|
|
2661
|
-
#toPath(
|
|
2662
|
-
if (typeof
|
|
2663
|
-
return
|
|
2813
|
+
#toPath(file) {
|
|
2814
|
+
if (typeof file === "string" && !file.startsWith("file:")) {
|
|
2815
|
+
return file;
|
|
2664
2816
|
}
|
|
2665
|
-
return fileURLToPath(
|
|
2817
|
+
return fileURLToPath(file);
|
|
2666
2818
|
}
|
|
2667
2819
|
}
|
|
2668
2820
|
|
|
@@ -3000,9 +3152,9 @@ class WatchService {
|
|
|
3000
3152
|
#resolvedConfig;
|
|
3001
3153
|
#watchedTestFiles;
|
|
3002
3154
|
#watchers = [];
|
|
3003
|
-
constructor(resolvedConfig,
|
|
3155
|
+
constructor(resolvedConfig, files) {
|
|
3004
3156
|
this.#resolvedConfig = resolvedConfig;
|
|
3005
|
-
this.#watchedTestFiles = new Map(
|
|
3157
|
+
this.#watchedTestFiles = new Map(files.map((file) => [file.path, file]));
|
|
3006
3158
|
}
|
|
3007
3159
|
#onDiagnostics(diagnostic) {
|
|
3008
3160
|
EventEmitter.dispatch(["watch:error", { diagnostics: [diagnostic] }]);
|
|
@@ -3031,7 +3183,7 @@ class WatchService {
|
|
|
3031
3183
|
case "\u001B":
|
|
3032
3184
|
case "q":
|
|
3033
3185
|
case "x":
|
|
3034
|
-
onClose(
|
|
3186
|
+
onClose("watchClose");
|
|
3035
3187
|
break;
|
|
3036
3188
|
case "\u000D":
|
|
3037
3189
|
case "\u0020":
|
|
@@ -3047,14 +3199,14 @@ class WatchService {
|
|
|
3047
3199
|
}
|
|
3048
3200
|
const onChangedFile = (filePath) => {
|
|
3049
3201
|
debounce.refresh();
|
|
3050
|
-
let
|
|
3051
|
-
if (
|
|
3052
|
-
this.#changedTestFiles.set(filePath,
|
|
3202
|
+
let file = this.#watchedTestFiles.get(filePath);
|
|
3203
|
+
if (file != null) {
|
|
3204
|
+
this.#changedTestFiles.set(filePath, file);
|
|
3053
3205
|
}
|
|
3054
3206
|
else if (Select.isTestFile(filePath, this.#resolvedConfig)) {
|
|
3055
|
-
|
|
3056
|
-
this.#changedTestFiles.set(filePath,
|
|
3057
|
-
this.#watchedTestFiles.set(filePath,
|
|
3207
|
+
file = new FileLocation(filePath);
|
|
3208
|
+
this.#changedTestFiles.set(filePath, file);
|
|
3209
|
+
this.#watchedTestFiles.set(filePath, file);
|
|
3058
3210
|
}
|
|
3059
3211
|
};
|
|
3060
3212
|
const onRemovedFile = (filePath) => {
|
|
@@ -3067,7 +3219,7 @@ class WatchService {
|
|
|
3067
3219
|
};
|
|
3068
3220
|
this.#watchers.push(new Watcher(this.#resolvedConfig.rootPath, onChangedFile, onRemovedFile, { recursive: true }));
|
|
3069
3221
|
const onChangedConfigFile = () => {
|
|
3070
|
-
onClose(
|
|
3222
|
+
onClose("configChange");
|
|
3071
3223
|
};
|
|
3072
3224
|
this.#watchers.push(new FileWatcher(this.#resolvedConfig.configFilePath, onChangedConfigFile));
|
|
3073
3225
|
for (const watcher of this.#watchers) {
|
|
@@ -3082,67 +3234,28 @@ class WatchService {
|
|
|
3082
3234
|
}
|
|
3083
3235
|
}
|
|
3084
3236
|
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
diagnostics = new Set();
|
|
3089
|
-
flags;
|
|
3090
|
-
name = "";
|
|
3091
|
-
node;
|
|
3092
|
-
parent;
|
|
3093
|
-
constructor(compiler, brand, node, parent, flags) {
|
|
3094
|
-
this.brand = brand;
|
|
3095
|
-
this.node = node;
|
|
3096
|
-
this.parent = parent;
|
|
3097
|
-
this.flags = flags;
|
|
3098
|
-
if (node.arguments[0] != null && compiler.isStringLiteralLike(node.arguments[0])) {
|
|
3099
|
-
this.name = node.arguments[0].text;
|
|
3100
|
-
}
|
|
3101
|
-
if (node.arguments[1] != null && compiler.isFunctionLike(node.arguments[1])) {
|
|
3102
|
-
for (const diagnostic of parent.diagnostics) {
|
|
3103
|
-
if (diagnosticBelongsToNode(diagnostic, node.arguments[1].body)) {
|
|
3104
|
-
this.diagnostics.add(diagnostic);
|
|
3105
|
-
parent.diagnostics.delete(diagnostic);
|
|
3106
|
-
}
|
|
3107
|
-
}
|
|
3108
|
-
}
|
|
3109
|
-
}
|
|
3110
|
-
getDirectiveRanges(compiler) {
|
|
3111
|
-
return Directive.getDirectiveRanges(compiler, this.node.getSourceFile(), this.node.getFullStart());
|
|
3237
|
+
function compareDiagnostics(a, b) {
|
|
3238
|
+
if (a.file?.fileName !== b.file?.fileName) {
|
|
3239
|
+
return false;
|
|
3112
3240
|
}
|
|
3241
|
+
return deepCompareKeys(a, b, ["start", "length", "code", "messageText"]);
|
|
3113
3242
|
}
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
this.matcherNode = matcherNode;
|
|
3128
|
-
this.matcherNameNode = matcherNameNode;
|
|
3129
|
-
this.modifierNode = modifierNode;
|
|
3130
|
-
this.source = this.node.typeArguments ?? this.node.arguments;
|
|
3131
|
-
if (compiler.isCallExpression(this.matcherNode)) {
|
|
3132
|
-
this.target = this.matcherNode.typeArguments ?? this.matcherNode.arguments;
|
|
3133
|
-
}
|
|
3134
|
-
for (const diagnostic of parent.diagnostics) {
|
|
3135
|
-
if (diagnosticBelongsToNode(diagnostic, this.source) ||
|
|
3136
|
-
(this.target != null && diagnosticBelongsToNode(diagnostic, this.target))) {
|
|
3137
|
-
this.diagnostics.add(diagnostic);
|
|
3138
|
-
parent.diagnostics.delete(diagnostic);
|
|
3139
|
-
}
|
|
3243
|
+
function deepCompareKeys(a, b, keys) {
|
|
3244
|
+
if (a == null || b == null) {
|
|
3245
|
+
return a === b;
|
|
3246
|
+
}
|
|
3247
|
+
if (typeof a !== typeof b) {
|
|
3248
|
+
return false;
|
|
3249
|
+
}
|
|
3250
|
+
if (typeof a !== "object") {
|
|
3251
|
+
return a === b;
|
|
3252
|
+
}
|
|
3253
|
+
for (const key of Object.keys(a).filter((key) => keys.includes(key))) {
|
|
3254
|
+
if (!(key in b) || !deepCompareKeys(a[key], b[key], keys)) {
|
|
3255
|
+
return false;
|
|
3140
3256
|
}
|
|
3141
3257
|
}
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
function nodeBelongsToArgumentList(compiler, node) {
|
|
3145
|
-
return compiler.isCallExpression(node.parent) && node.parent.arguments.some((argument) => argument === node);
|
|
3258
|
+
return true;
|
|
3146
3259
|
}
|
|
3147
3260
|
function nodeIsChildOfExpressionStatement(compiler, node) {
|
|
3148
3261
|
return compiler.isExpressionStatement(node.parent);
|
|
@@ -3150,59 +3263,170 @@ function nodeIsChildOfExpressionStatement(compiler, node) {
|
|
|
3150
3263
|
|
|
3151
3264
|
class AbilityLayer {
|
|
3152
3265
|
#compiler;
|
|
3153
|
-
#
|
|
3154
|
-
#filePath = "";
|
|
3266
|
+
#editor;
|
|
3155
3267
|
#nodes = [];
|
|
3156
|
-
|
|
3157
|
-
#resolvedConfig;
|
|
3158
|
-
#suppressedErrorsMap;
|
|
3159
|
-
#text = "";
|
|
3160
|
-
constructor(compiler, projectService, resolvedConfig) {
|
|
3268
|
+
constructor(compiler, editor) {
|
|
3161
3269
|
this.#compiler = compiler;
|
|
3162
|
-
this.#
|
|
3163
|
-
this.#resolvedConfig = resolvedConfig;
|
|
3270
|
+
this.#editor = editor;
|
|
3164
3271
|
}
|
|
3165
|
-
#
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3272
|
+
#belongsToNode(node, diagnostic) {
|
|
3273
|
+
switch (node.brand) {
|
|
3274
|
+
case "expect":
|
|
3275
|
+
return (diagnosticBelongsToNode(diagnostic, node.matcherNode) ||
|
|
3276
|
+
diagnosticBelongsToNode(diagnostic, node.source));
|
|
3277
|
+
case "when":
|
|
3278
|
+
return (diagnosticBelongsToNode(diagnostic, node.actionNode) &&
|
|
3279
|
+
!diagnosticBelongsToNode(diagnostic, node.target));
|
|
3280
|
+
}
|
|
3281
|
+
return false;
|
|
3282
|
+
}
|
|
3283
|
+
close(diagnostics) {
|
|
3284
|
+
if (diagnostics != null && this.#nodes.length > 0) {
|
|
3285
|
+
this.#nodes.reverse();
|
|
3286
|
+
for (const diagnostic of diagnostics) {
|
|
3287
|
+
this.#mapToNodes(diagnostic);
|
|
3288
|
+
}
|
|
3172
3289
|
}
|
|
3290
|
+
this.#nodes = [];
|
|
3173
3291
|
}
|
|
3174
|
-
#
|
|
3292
|
+
#mapToNodes(diagnostic) {
|
|
3175
3293
|
for (const node of this.#nodes) {
|
|
3176
|
-
if (
|
|
3294
|
+
if (this.#belongsToNode(node, diagnostic)) {
|
|
3177
3295
|
node.abilityDiagnostics.add(diagnostic);
|
|
3178
|
-
|
|
3296
|
+
break;
|
|
3179
3297
|
}
|
|
3180
3298
|
}
|
|
3181
|
-
return false;
|
|
3182
3299
|
}
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
const
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3300
|
+
visitExpect(expect) {
|
|
3301
|
+
const expectStart = expect.node.getStart();
|
|
3302
|
+
const expectExpressionEnd = expect.node.expression.getEnd();
|
|
3303
|
+
const expectEnd = expect.node.getEnd();
|
|
3304
|
+
const matcherNameEnd = expect.matcherNameNode.getEnd();
|
|
3305
|
+
switch (expect.matcherNameNode.name.text) {
|
|
3306
|
+
case "toBeApplicable":
|
|
3307
|
+
this.#nodes.push(expect);
|
|
3308
|
+
this.#editor.replaceRanges([
|
|
3309
|
+
[expectStart, expectExpressionEnd],
|
|
3310
|
+
[expectEnd, matcherNameEnd],
|
|
3311
|
+
]);
|
|
3194
3312
|
break;
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3313
|
+
case "toBeCallableWith":
|
|
3314
|
+
this.#nodes.push(expect);
|
|
3315
|
+
this.#editor.eraseTrailingComma(expect.source);
|
|
3316
|
+
this.#editor.replaceRanges([
|
|
3317
|
+
[
|
|
3318
|
+
expectStart,
|
|
3319
|
+
expectExpressionEnd,
|
|
3320
|
+
nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode) ? ";" : "",
|
|
3321
|
+
],
|
|
3322
|
+
[expectEnd, matcherNameEnd],
|
|
3323
|
+
]);
|
|
3324
|
+
break;
|
|
3325
|
+
case "toBeConstructableWith":
|
|
3326
|
+
this.#nodes.push(expect);
|
|
3327
|
+
this.#editor.eraseTrailingComma(expect.source);
|
|
3328
|
+
this.#editor.replaceRanges([
|
|
3329
|
+
[
|
|
3330
|
+
expectStart,
|
|
3331
|
+
expectExpressionEnd,
|
|
3332
|
+
nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode) ? "; new" : "new",
|
|
3333
|
+
],
|
|
3334
|
+
[expectEnd, matcherNameEnd],
|
|
3335
|
+
]);
|
|
3336
|
+
break;
|
|
3337
|
+
}
|
|
3338
|
+
}
|
|
3339
|
+
visitWhen(when) {
|
|
3340
|
+
const whenStart = when.node.getStart();
|
|
3341
|
+
const whenExpressionEnd = when.node.expression.getEnd();
|
|
3342
|
+
const whenEnd = when.node.getEnd();
|
|
3343
|
+
const actionNameEnd = when.actionNameNode.getEnd();
|
|
3344
|
+
switch (when.actionNameNode.name.text) {
|
|
3345
|
+
case "isCalledWith":
|
|
3346
|
+
this.#nodes.push(when);
|
|
3347
|
+
this.#editor.eraseTrailingComma(when.target);
|
|
3348
|
+
this.#editor.replaceRanges([
|
|
3349
|
+
[whenStart, whenExpressionEnd, nodeIsChildOfExpressionStatement(this.#compiler, when.actionNode) ? ";" : ""],
|
|
3350
|
+
[whenEnd, actionNameEnd],
|
|
3351
|
+
]);
|
|
3198
3352
|
break;
|
|
3353
|
+
}
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3356
|
+
|
|
3357
|
+
class SourceTextEditor {
|
|
3358
|
+
#filePath = "";
|
|
3359
|
+
#sourceFile;
|
|
3360
|
+
#text = "";
|
|
3361
|
+
open(sourceFile) {
|
|
3362
|
+
this.#sourceFile = sourceFile;
|
|
3363
|
+
this.#filePath = sourceFile.fileName;
|
|
3364
|
+
this.#text = sourceFile.text;
|
|
3365
|
+
}
|
|
3366
|
+
close() {
|
|
3367
|
+
if (this.#sourceFile != null) {
|
|
3368
|
+
SourceService.set(this.#sourceFile);
|
|
3369
|
+
this.#sourceFile = undefined;
|
|
3370
|
+
}
|
|
3371
|
+
this.#filePath = "";
|
|
3372
|
+
this.#text = "";
|
|
3373
|
+
}
|
|
3374
|
+
eraseTrailingComma(node) {
|
|
3375
|
+
if (node.hasTrailingComma) {
|
|
3376
|
+
this.replaceRange(node.end - 1, node.end);
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
3379
|
+
#getErasedRange(start, end) {
|
|
3380
|
+
if (this.#text.indexOf("\n", start) >= end) {
|
|
3381
|
+
return " ".repeat(end - start);
|
|
3382
|
+
}
|
|
3383
|
+
const text = [];
|
|
3384
|
+
for (let index = start; index < end; index++) {
|
|
3385
|
+
const character = this.#text.charAt(index);
|
|
3386
|
+
switch (character) {
|
|
3387
|
+
case "\n":
|
|
3388
|
+
case "\r":
|
|
3389
|
+
text.push(character);
|
|
3390
|
+
break;
|
|
3391
|
+
default:
|
|
3392
|
+
text.push(" ");
|
|
3199
3393
|
}
|
|
3200
|
-
|
|
3394
|
+
}
|
|
3395
|
+
return text.join("");
|
|
3396
|
+
}
|
|
3397
|
+
getFilePath() {
|
|
3398
|
+
return this.#filePath;
|
|
3399
|
+
}
|
|
3400
|
+
getText() {
|
|
3401
|
+
return this.#text;
|
|
3402
|
+
}
|
|
3403
|
+
replaceRange(start, end, replacement) {
|
|
3404
|
+
const rangeText = replacement != null
|
|
3405
|
+
? `${replacement}${this.#getErasedRange(start, end).slice(replacement.length)}`
|
|
3406
|
+
: this.#getErasedRange(start, end);
|
|
3407
|
+
this.#text = `${this.#text.slice(0, start)}${rangeText}${this.#text.slice(end)}`;
|
|
3408
|
+
}
|
|
3409
|
+
replaceRanges(ranges) {
|
|
3410
|
+
for (const [start, end, replacement] of ranges) {
|
|
3411
|
+
this.replaceRange(start, end, replacement);
|
|
3201
3412
|
}
|
|
3202
3413
|
}
|
|
3203
|
-
|
|
3414
|
+
}
|
|
3415
|
+
|
|
3416
|
+
class SuppressedLayer {
|
|
3417
|
+
#compiler;
|
|
3418
|
+
#editor;
|
|
3419
|
+
#expectErrorRegex = /^(\s*)(\/\/ *@ts-expect-error)(!?)(:? *)(.*)?$/gim;
|
|
3420
|
+
#resolvedConfig;
|
|
3421
|
+
#suppressedErrorsMap;
|
|
3422
|
+
constructor(compiler, editor, resolvedConfig) {
|
|
3423
|
+
this.#compiler = compiler;
|
|
3424
|
+
this.#editor = editor;
|
|
3425
|
+
this.#resolvedConfig = resolvedConfig;
|
|
3426
|
+
}
|
|
3427
|
+
#collectSuppressedErrors(text) {
|
|
3204
3428
|
const ranges = [];
|
|
3205
|
-
for (const match of
|
|
3429
|
+
for (const match of text.matchAll(this.#expectErrorRegex)) {
|
|
3206
3430
|
const offsetText = match?.[1];
|
|
3207
3431
|
const directiveText = match?.[2];
|
|
3208
3432
|
const ignoreText = match?.[3];
|
|
@@ -3225,125 +3449,89 @@ class AbilityLayer {
|
|
|
3225
3449
|
}
|
|
3226
3450
|
return ranges;
|
|
3227
3451
|
}
|
|
3228
|
-
close() {
|
|
3229
|
-
if (
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
const diagnostics = languageService?.getSemanticDiagnostics(this.#filePath);
|
|
3233
|
-
if (diagnostics != null) {
|
|
3234
|
-
this.#nodes.reverse();
|
|
3235
|
-
for (const diagnostic of diagnostics) {
|
|
3236
|
-
if (this.#belongsToNode(diagnostic)) {
|
|
3237
|
-
continue;
|
|
3238
|
-
}
|
|
3239
|
-
this.#belongsToDirective(diagnostic);
|
|
3240
|
-
}
|
|
3452
|
+
close(diagnostics) {
|
|
3453
|
+
if (diagnostics != null && this.#suppressedErrorsMap != null) {
|
|
3454
|
+
for (const diagnostic of diagnostics) {
|
|
3455
|
+
this.#mapToDirectives(diagnostic);
|
|
3241
3456
|
}
|
|
3242
3457
|
}
|
|
3243
|
-
this.#filePath = "";
|
|
3244
|
-
this.#nodes = [];
|
|
3245
3458
|
this.#suppressedErrorsMap = undefined;
|
|
3246
|
-
this.#text = "";
|
|
3247
|
-
}
|
|
3248
|
-
#eraseTrailingComma(node, parent) {
|
|
3249
|
-
if (node.hasTrailingComma) {
|
|
3250
|
-
this.#addRanges(parent, [{ start: node.end - 1, end: node.end }]);
|
|
3251
|
-
}
|
|
3252
3459
|
}
|
|
3253
|
-
#
|
|
3254
|
-
if (
|
|
3255
|
-
return
|
|
3256
|
-
}
|
|
3257
|
-
const text = [];
|
|
3258
|
-
for (let index = range.start; index < range.end; index++) {
|
|
3259
|
-
const character = this.#text.charAt(index);
|
|
3260
|
-
switch (character) {
|
|
3261
|
-
case "\n":
|
|
3262
|
-
case "\r":
|
|
3263
|
-
text.push(character);
|
|
3264
|
-
break;
|
|
3265
|
-
default:
|
|
3266
|
-
text.push(" ");
|
|
3267
|
-
}
|
|
3460
|
+
#mapToDirectives(diagnostic) {
|
|
3461
|
+
if (!isDiagnosticWithLocation(diagnostic)) {
|
|
3462
|
+
return;
|
|
3268
3463
|
}
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
switch (assertionNode.matcherNameNode.name.text) {
|
|
3277
|
-
case "toBeApplicable":
|
|
3278
|
-
this.#addRanges(assertionNode, [
|
|
3279
|
-
{ start: expectStart, end: expectExpressionEnd },
|
|
3280
|
-
{ start: expectEnd, end: matcherNameEnd },
|
|
3281
|
-
]);
|
|
3282
|
-
break;
|
|
3283
|
-
case "toBeCallableWith":
|
|
3284
|
-
this.#eraseTrailingComma(assertionNode.source, assertionNode);
|
|
3285
|
-
this.#addRanges(assertionNode, [
|
|
3286
|
-
{
|
|
3287
|
-
start: expectStart,
|
|
3288
|
-
end: expectExpressionEnd,
|
|
3289
|
-
replacement: nodeIsChildOfExpressionStatement(this.#compiler, assertionNode.matcherNode) ? ";" : "",
|
|
3290
|
-
},
|
|
3291
|
-
{ start: expectEnd, end: matcherNameEnd },
|
|
3292
|
-
]);
|
|
3464
|
+
const { file, start } = diagnostic;
|
|
3465
|
+
const lineMap = file.getLineStarts();
|
|
3466
|
+
let line = this.#compiler.getLineAndCharacterOfPosition(file, start).line - 1;
|
|
3467
|
+
while (line >= 0) {
|
|
3468
|
+
const suppressedError = this.#suppressedErrorsMap?.get(line);
|
|
3469
|
+
if (suppressedError != null) {
|
|
3470
|
+
suppressedError.diagnostics.push(diagnostic);
|
|
3293
3471
|
break;
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
{
|
|
3298
|
-
start: expectStart,
|
|
3299
|
-
end: expectExpressionEnd,
|
|
3300
|
-
replacement: nodeIsChildOfExpressionStatement(this.#compiler, assertionNode.matcherNode) ? "; new" : "new",
|
|
3301
|
-
},
|
|
3302
|
-
{ start: expectEnd, end: matcherNameEnd },
|
|
3303
|
-
]);
|
|
3472
|
+
}
|
|
3473
|
+
const lineText = file.text.slice(lineMap[line], lineMap[line + 1]).trim();
|
|
3474
|
+
if (lineText !== "" && !lineText.startsWith("//")) {
|
|
3304
3475
|
break;
|
|
3476
|
+
}
|
|
3477
|
+
line--;
|
|
3305
3478
|
}
|
|
3306
3479
|
}
|
|
3307
|
-
|
|
3308
|
-
const suppressedErrors = this.#collectSuppressedErrors();
|
|
3480
|
+
open(tree) {
|
|
3481
|
+
const suppressedErrors = this.#collectSuppressedErrors(this.#editor.getText());
|
|
3309
3482
|
if (this.#resolvedConfig.checkSuppressedErrors) {
|
|
3310
|
-
|
|
3483
|
+
tree.suppressedErrors = suppressedErrors;
|
|
3311
3484
|
this.#suppressedErrorsMap = new Map();
|
|
3312
3485
|
}
|
|
3313
3486
|
for (const suppressedError of suppressedErrors) {
|
|
3314
3487
|
const { start, end } = suppressedError.directive;
|
|
3315
|
-
|
|
3316
|
-
this.#text = `${this.#text.slice(0, start + 2)}${rangeText}${this.#text.slice(end)}`;
|
|
3488
|
+
this.#editor.replaceRange(start + 2, end);
|
|
3317
3489
|
if (this.#suppressedErrorsMap != null) {
|
|
3318
|
-
const { line } =
|
|
3490
|
+
const { line } = tree.sourceFile.getLineAndCharacterOfPosition(start);
|
|
3319
3491
|
this.#suppressedErrorsMap.set(line, suppressedError);
|
|
3320
3492
|
}
|
|
3321
3493
|
}
|
|
3322
3494
|
}
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3495
|
+
}
|
|
3496
|
+
|
|
3497
|
+
class Layers {
|
|
3498
|
+
#abilityLayer;
|
|
3499
|
+
#editor = new SourceTextEditor();
|
|
3500
|
+
#projectService;
|
|
3501
|
+
#suppressedDiagnostics;
|
|
3502
|
+
#suppressedLayer;
|
|
3503
|
+
constructor(compiler, projectService, resolvedConfig) {
|
|
3504
|
+
this.#projectService = projectService;
|
|
3505
|
+
this.#abilityLayer = new AbilityLayer(compiler, this.#editor);
|
|
3506
|
+
this.#suppressedLayer = new SuppressedLayer(compiler, this.#editor, resolvedConfig);
|
|
3507
|
+
}
|
|
3508
|
+
close() {
|
|
3509
|
+
let isSeenDiagnostic;
|
|
3510
|
+
if (this.#suppressedDiagnostics != null) {
|
|
3511
|
+
const seenDiagnostics = this.#suppressedDiagnostics;
|
|
3512
|
+
this.#suppressedDiagnostics = undefined;
|
|
3513
|
+
isSeenDiagnostic = (diagnostic) => !seenDiagnostics.some((seenDiagnostic) => compareDiagnostics(diagnostic, seenDiagnostic));
|
|
3514
|
+
}
|
|
3515
|
+
const abilityDiagnostics = this.#projectService.getDiagnostics(this.#editor.getFilePath(), this.#editor.getText(), isSeenDiagnostic);
|
|
3516
|
+
this.#abilityLayer.close(abilityDiagnostics);
|
|
3517
|
+
this.#editor.close();
|
|
3518
|
+
}
|
|
3519
|
+
open(tree) {
|
|
3520
|
+
this.#editor.open(tree.sourceFile);
|
|
3521
|
+
this.#suppressedLayer.open(tree);
|
|
3522
|
+
this.#suppressedDiagnostics = this.#projectService.getDiagnostics(this.#editor.getFilePath(), this.#editor.getText());
|
|
3523
|
+
this.#suppressedLayer.close(this.#suppressedDiagnostics);
|
|
3524
|
+
}
|
|
3525
|
+
visit(node) {
|
|
3526
|
+
switch (node.brand) {
|
|
3527
|
+
case "expect":
|
|
3528
|
+
this.#abilityLayer.visitExpect(node);
|
|
3529
|
+
break;
|
|
3530
|
+
case "when":
|
|
3531
|
+
this.#abilityLayer.visitWhen(node);
|
|
3339
3532
|
break;
|
|
3340
3533
|
}
|
|
3341
3534
|
}
|
|
3342
|
-
open(testTree) {
|
|
3343
|
-
this.#filePath = testTree.sourceFile.fileName;
|
|
3344
|
-
this.#text = testTree.sourceFile.text;
|
|
3345
|
-
this.#handleSuppressedErrors(testTree);
|
|
3346
|
-
}
|
|
3347
3535
|
}
|
|
3348
3536
|
|
|
3349
3537
|
class CollectDiagnosticText {
|
|
@@ -3352,22 +3540,61 @@ class CollectDiagnosticText {
|
|
|
3352
3540
|
}
|
|
3353
3541
|
}
|
|
3354
3542
|
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3543
|
+
class TestTreeNode {
|
|
3544
|
+
brand;
|
|
3545
|
+
children = [];
|
|
3546
|
+
diagnostics = new Set();
|
|
3547
|
+
flags;
|
|
3548
|
+
name = "";
|
|
3549
|
+
node;
|
|
3550
|
+
parent;
|
|
3551
|
+
constructor(compiler, brand, node, parent, flags) {
|
|
3552
|
+
this.brand = brand;
|
|
3553
|
+
this.node = node;
|
|
3554
|
+
this.parent = parent;
|
|
3555
|
+
this.flags = flags;
|
|
3556
|
+
if (node.arguments[0] != null && compiler.isStringLiteralLike(node.arguments[0])) {
|
|
3557
|
+
this.name = node.arguments[0].text;
|
|
3558
|
+
}
|
|
3559
|
+
if (node.arguments[1] != null && compiler.isFunctionLike(node.arguments[1])) {
|
|
3560
|
+
for (const diagnostic of parent.diagnostics) {
|
|
3561
|
+
if (diagnosticBelongsToNode(diagnostic, node.arguments[1].body)) {
|
|
3562
|
+
this.diagnostics.add(diagnostic);
|
|
3563
|
+
parent.diagnostics.delete(diagnostic);
|
|
3564
|
+
}
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
}
|
|
3568
|
+
}
|
|
3362
3569
|
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3570
|
+
class ExpectNode extends TestTreeNode {
|
|
3571
|
+
abilityDiagnostics = new Set();
|
|
3572
|
+
isNot;
|
|
3573
|
+
matcherNode;
|
|
3574
|
+
matcherNameNode;
|
|
3575
|
+
modifierNode;
|
|
3576
|
+
notNode;
|
|
3577
|
+
source;
|
|
3578
|
+
target;
|
|
3579
|
+
constructor(compiler, brand, node, parent, flags, matcherNode, matcherNameNode, modifierNode, notNode) {
|
|
3580
|
+
super(compiler, brand, node, parent, flags);
|
|
3581
|
+
this.isNot = notNode != null;
|
|
3582
|
+
this.matcherNode = matcherNode;
|
|
3583
|
+
this.matcherNameNode = matcherNameNode;
|
|
3584
|
+
this.modifierNode = modifierNode;
|
|
3585
|
+
this.source = this.node.typeArguments ?? this.node.arguments;
|
|
3586
|
+
if (compiler.isCallExpression(this.matcherNode)) {
|
|
3587
|
+
this.target = this.matcherNode.typeArguments ?? this.matcherNode.arguments;
|
|
3588
|
+
}
|
|
3589
|
+
for (const diagnostic of parent.diagnostics) {
|
|
3590
|
+
if (diagnosticBelongsToNode(diagnostic, this.source) ||
|
|
3591
|
+
(this.target != null && diagnosticBelongsToNode(diagnostic, this.target))) {
|
|
3592
|
+
this.diagnostics.add(diagnostic);
|
|
3593
|
+
parent.diagnostics.delete(diagnostic);
|
|
3594
|
+
}
|
|
3595
|
+
}
|
|
3596
|
+
}
|
|
3597
|
+
}
|
|
3371
3598
|
|
|
3372
3599
|
class IdentifierLookup {
|
|
3373
3600
|
#compiler;
|
|
@@ -3416,24 +3643,21 @@ class IdentifierLookup {
|
|
|
3416
3643
|
};
|
|
3417
3644
|
}
|
|
3418
3645
|
resolveTestTreeNodeMeta(node) {
|
|
3419
|
-
let flags =
|
|
3646
|
+
let flags = 0;
|
|
3420
3647
|
let expression = node.expression;
|
|
3421
3648
|
while (this.#compiler.isPropertyAccessExpression(expression)) {
|
|
3422
3649
|
if (expression.expression.getText() === this.#identifiers.namespace) {
|
|
3423
3650
|
break;
|
|
3424
3651
|
}
|
|
3425
3652
|
switch (expression.name.getText()) {
|
|
3426
|
-
case "fail":
|
|
3427
|
-
flags |= TestTreeNodeFlags.Fail;
|
|
3428
|
-
break;
|
|
3429
3653
|
case "only":
|
|
3430
|
-
flags |=
|
|
3654
|
+
flags |= 1;
|
|
3431
3655
|
break;
|
|
3432
3656
|
case "skip":
|
|
3433
|
-
flags |=
|
|
3657
|
+
flags |= 2;
|
|
3434
3658
|
break;
|
|
3435
3659
|
case "todo":
|
|
3436
|
-
flags |=
|
|
3660
|
+
flags |= 4;
|
|
3437
3661
|
break;
|
|
3438
3662
|
}
|
|
3439
3663
|
expression = expression.expression;
|
|
@@ -3451,14 +3675,14 @@ class IdentifierLookup {
|
|
|
3451
3675
|
}
|
|
3452
3676
|
switch (identifier) {
|
|
3453
3677
|
case "describe":
|
|
3454
|
-
return { brand:
|
|
3678
|
+
return { brand: "describe", flags, identifier };
|
|
3455
3679
|
case "it":
|
|
3456
3680
|
case "test":
|
|
3457
|
-
return { brand:
|
|
3681
|
+
return { brand: "test", flags, identifier };
|
|
3458
3682
|
case "expect":
|
|
3459
|
-
return { brand:
|
|
3683
|
+
return { brand: "expect", flags, identifier };
|
|
3460
3684
|
case "when":
|
|
3461
|
-
return { brand:
|
|
3685
|
+
return { brand: "when", flags, identifier };
|
|
3462
3686
|
}
|
|
3463
3687
|
return;
|
|
3464
3688
|
}
|
|
@@ -3474,9 +3698,6 @@ class TestTree {
|
|
|
3474
3698
|
this.diagnostics = diagnostics;
|
|
3475
3699
|
this.sourceFile = sourceFile;
|
|
3476
3700
|
}
|
|
3477
|
-
getDirectiveRanges(compiler) {
|
|
3478
|
-
return Directive.getDirectiveRanges(compiler, this.sourceFile);
|
|
3479
|
-
}
|
|
3480
3701
|
}
|
|
3481
3702
|
|
|
3482
3703
|
class WhenNode extends TestTreeNode {
|
|
@@ -3499,12 +3720,12 @@ class WhenNode extends TestTreeNode {
|
|
|
3499
3720
|
}
|
|
3500
3721
|
|
|
3501
3722
|
class CollectService {
|
|
3502
|
-
#
|
|
3723
|
+
#layers;
|
|
3503
3724
|
#compiler;
|
|
3504
3725
|
#identifierLookup;
|
|
3505
3726
|
constructor(compiler, projectService, resolvedConfig) {
|
|
3506
3727
|
this.#compiler = compiler;
|
|
3507
|
-
this.#
|
|
3728
|
+
this.#layers = new Layers(compiler, projectService, resolvedConfig);
|
|
3508
3729
|
this.#identifierLookup = new IdentifierLookup(compiler);
|
|
3509
3730
|
}
|
|
3510
3731
|
#collectTestTreeNodes(node, parent, testTree) {
|
|
@@ -3514,7 +3735,7 @@ class CollectService {
|
|
|
3514
3735
|
if (!this.#checkNode(node, meta, parent)) {
|
|
3515
3736
|
return;
|
|
3516
3737
|
}
|
|
3517
|
-
if (meta.brand ===
|
|
3738
|
+
if (meta.brand === "describe" || meta.brand === "test") {
|
|
3518
3739
|
const testTreeNode = new TestTreeNode(this.#compiler, meta.brand, node, parent, meta.flags);
|
|
3519
3740
|
this.#compiler.forEachChild(node, (node) => {
|
|
3520
3741
|
this.#collectTestTreeNodes(node, testTreeNode, testTree);
|
|
@@ -3522,7 +3743,7 @@ class CollectService {
|
|
|
3522
3743
|
this.#onNode(testTreeNode, parent, testTree);
|
|
3523
3744
|
return;
|
|
3524
3745
|
}
|
|
3525
|
-
if (meta.brand ===
|
|
3746
|
+
if (meta.brand === "expect") {
|
|
3526
3747
|
const modifierNode = this.#getChainedNode(node, "type");
|
|
3527
3748
|
if (!modifierNode) {
|
|
3528
3749
|
return;
|
|
@@ -3536,15 +3757,15 @@ class CollectService {
|
|
|
3536
3757
|
if (!matcherNode) {
|
|
3537
3758
|
return;
|
|
3538
3759
|
}
|
|
3539
|
-
const
|
|
3540
|
-
this.#
|
|
3760
|
+
const expectNode = new ExpectNode(this.#compiler, meta.brand, node, parent, meta.flags, matcherNode, matcherNameNode, modifierNode, notNode);
|
|
3761
|
+
this.#layers.visit(expectNode);
|
|
3541
3762
|
this.#compiler.forEachChild(node, (node) => {
|
|
3542
|
-
this.#collectTestTreeNodes(node,
|
|
3763
|
+
this.#collectTestTreeNodes(node, expectNode, testTree);
|
|
3543
3764
|
});
|
|
3544
|
-
this.#onNode(
|
|
3765
|
+
this.#onNode(expectNode, parent, testTree);
|
|
3545
3766
|
return;
|
|
3546
3767
|
}
|
|
3547
|
-
if (meta.brand ===
|
|
3768
|
+
if (meta.brand === "when") {
|
|
3548
3769
|
const actionNameNode = this.#getChainedNode(node);
|
|
3549
3770
|
if (!actionNameNode) {
|
|
3550
3771
|
return;
|
|
@@ -3556,7 +3777,7 @@ class CollectService {
|
|
|
3556
3777
|
this.#compiler.forEachChild(actionNode, (node) => {
|
|
3557
3778
|
if (this.#compiler.isCallExpression(node)) {
|
|
3558
3779
|
const meta = this.#identifierLookup.resolveTestTreeNodeMeta(node);
|
|
3559
|
-
if (meta?.brand ===
|
|
3780
|
+
if (meta?.brand === "describe" || meta?.brand === "test") {
|
|
3560
3781
|
const text = CollectDiagnosticText.cannotBeNestedWithin(meta.identifier, "when");
|
|
3561
3782
|
const origin = DiagnosticOrigin.fromNode(node);
|
|
3562
3783
|
this.#onDiagnostics(Diagnostic.error(text, origin));
|
|
@@ -3564,7 +3785,7 @@ class CollectService {
|
|
|
3564
3785
|
}
|
|
3565
3786
|
});
|
|
3566
3787
|
const whenNode = new WhenNode(this.#compiler, meta.brand, node, parent, meta.flags, actionNode, actionNameNode);
|
|
3567
|
-
this.#
|
|
3788
|
+
this.#layers.visit(whenNode);
|
|
3568
3789
|
this.#onNode(whenNode, parent, testTree);
|
|
3569
3790
|
return;
|
|
3570
3791
|
}
|
|
@@ -3581,10 +3802,10 @@ class CollectService {
|
|
|
3581
3802
|
createTestTree(sourceFile, semanticDiagnostics = []) {
|
|
3582
3803
|
const testTree = new TestTree(new Set(semanticDiagnostics), sourceFile);
|
|
3583
3804
|
EventEmitter.dispatch(["collect:start", { tree: testTree }]);
|
|
3584
|
-
this.#
|
|
3805
|
+
this.#layers.open(testTree);
|
|
3585
3806
|
this.#identifierLookup.open();
|
|
3586
3807
|
this.#collectTestTreeNodes(sourceFile, testTree, testTree);
|
|
3587
|
-
this.#
|
|
3808
|
+
this.#layers.close();
|
|
3588
3809
|
EventEmitter.dispatch(["collect:end", { tree: testTree }]);
|
|
3589
3810
|
return testTree;
|
|
3590
3811
|
}
|
|
@@ -3599,15 +3820,15 @@ class CollectService {
|
|
|
3599
3820
|
}
|
|
3600
3821
|
#isNodeAllowed(meta, parent) {
|
|
3601
3822
|
switch (meta.brand) {
|
|
3602
|
-
case
|
|
3603
|
-
case
|
|
3604
|
-
if (parent.brand ===
|
|
3823
|
+
case "describe":
|
|
3824
|
+
case "test":
|
|
3825
|
+
if (parent.brand === "test" || parent.brand === "expect") {
|
|
3605
3826
|
return false;
|
|
3606
3827
|
}
|
|
3607
3828
|
break;
|
|
3608
|
-
case
|
|
3609
|
-
case
|
|
3610
|
-
if (parent.brand ===
|
|
3829
|
+
case "expect":
|
|
3830
|
+
case "when":
|
|
3831
|
+
if (parent.brand === "describe") {
|
|
3611
3832
|
return false;
|
|
3612
3833
|
}
|
|
3613
3834
|
break;
|
|
@@ -3646,13 +3867,33 @@ class CollectService {
|
|
|
3646
3867
|
}
|
|
3647
3868
|
#onNode(node, parent, testTree) {
|
|
3648
3869
|
parent.children.push(node);
|
|
3649
|
-
if (node.flags &
|
|
3870
|
+
if (node.flags & 1) {
|
|
3650
3871
|
testTree.hasOnly = true;
|
|
3651
3872
|
}
|
|
3652
3873
|
EventEmitter.dispatch(["collect:node", { node }]);
|
|
3653
3874
|
}
|
|
3654
3875
|
}
|
|
3655
3876
|
|
|
3877
|
+
function nodeBelongsToArgumentList(compiler, node) {
|
|
3878
|
+
return compiler.isCallExpression(node.parent) && node.parent.arguments.some((argument) => argument === node);
|
|
3879
|
+
}
|
|
3880
|
+
|
|
3881
|
+
var TestTreeNodeBrand;
|
|
3882
|
+
(function (TestTreeNodeBrand) {
|
|
3883
|
+
TestTreeNodeBrand["Describe"] = "describe";
|
|
3884
|
+
TestTreeNodeBrand["Test"] = "test";
|
|
3885
|
+
TestTreeNodeBrand["Expect"] = "expect";
|
|
3886
|
+
TestTreeNodeBrand["When"] = "when";
|
|
3887
|
+
})(TestTreeNodeBrand || (TestTreeNodeBrand = {}));
|
|
3888
|
+
|
|
3889
|
+
var TestTreeNodeFlags;
|
|
3890
|
+
(function (TestTreeNodeFlags) {
|
|
3891
|
+
TestTreeNodeFlags[TestTreeNodeFlags["None"] = 0] = "None";
|
|
3892
|
+
TestTreeNodeFlags[TestTreeNodeFlags["Only"] = 1] = "Only";
|
|
3893
|
+
TestTreeNodeFlags[TestTreeNodeFlags["Skip"] = 2] = "Skip";
|
|
3894
|
+
TestTreeNodeFlags[TestTreeNodeFlags["Todo"] = 4] = "Todo";
|
|
3895
|
+
})(TestTreeNodeFlags || (TestTreeNodeFlags = {}));
|
|
3896
|
+
|
|
3656
3897
|
class ProjectService {
|
|
3657
3898
|
#compiler;
|
|
3658
3899
|
#lastSeenProject = "";
|
|
@@ -3700,11 +3941,10 @@ class ProjectService {
|
|
|
3700
3941
|
}
|
|
3701
3942
|
closeFile(filePath) {
|
|
3702
3943
|
this.#service.closeClientFile(filePath);
|
|
3944
|
+
SourceService.delete(filePath);
|
|
3703
3945
|
}
|
|
3704
3946
|
#getDefaultCompilerOptions() {
|
|
3705
3947
|
const defaultCompilerOptions = {
|
|
3706
|
-
allowJs: true,
|
|
3707
|
-
checkJs: true,
|
|
3708
3948
|
exactOptionalPropertyTypes: true,
|
|
3709
3949
|
jsx: this.#compiler.JsxEmit.Preserve,
|
|
3710
3950
|
module: this.#compiler.ModuleKind.NodeNext,
|
|
@@ -3726,11 +3966,20 @@ class ProjectService {
|
|
|
3726
3966
|
getDefaultProject(filePath) {
|
|
3727
3967
|
const project = this.#service.getDefaultProjectForFile(this.#compiler.server.toNormalizedPath(filePath), true);
|
|
3728
3968
|
const compilerOptions = project?.getCompilerOptions();
|
|
3729
|
-
if (this.#resolvedConfig.
|
|
3969
|
+
if (this.#resolvedConfig.checkDeclarationFiles && compilerOptions?.skipLibCheck) {
|
|
3730
3970
|
project?.setCompilerOptions({ ...compilerOptions, skipLibCheck: false });
|
|
3731
3971
|
}
|
|
3732
3972
|
return project;
|
|
3733
3973
|
}
|
|
3974
|
+
getDiagnostics(filePath, sourceText, shouldInclude) {
|
|
3975
|
+
this.openFile(filePath, sourceText);
|
|
3976
|
+
const languageService = this.getLanguageService(filePath);
|
|
3977
|
+
const diagnostics = languageService?.getSemanticDiagnostics(filePath);
|
|
3978
|
+
if (diagnostics != null && shouldInclude != null) {
|
|
3979
|
+
return diagnostics.filter(shouldInclude);
|
|
3980
|
+
}
|
|
3981
|
+
return diagnostics;
|
|
3982
|
+
}
|
|
3734
3983
|
getLanguageService(filePath) {
|
|
3735
3984
|
const project = this.getDefaultProject(filePath);
|
|
3736
3985
|
return project?.getLanguageService(true);
|
|
@@ -3740,7 +3989,7 @@ class ProjectService {
|
|
|
3740
3989
|
const { fileNames } = this.#compiler.parseJsonSourceFileConfigFileContent(configSourceFile, this.#compiler.sys, Path.dirname(this.#resolvedConfig.tsconfig), undefined, this.#resolvedConfig.tsconfig);
|
|
3741
3990
|
return fileNames.includes(filePath);
|
|
3742
3991
|
}
|
|
3743
|
-
openFile(filePath, sourceText
|
|
3992
|
+
openFile(filePath, sourceText) {
|
|
3744
3993
|
switch (this.#resolvedConfig.tsconfig) {
|
|
3745
3994
|
case "findup":
|
|
3746
3995
|
break;
|
|
@@ -3752,19 +4001,19 @@ class ProjectService {
|
|
|
3752
4001
|
? () => this.#resolvedConfig.tsconfig
|
|
3753
4002
|
: () => undefined;
|
|
3754
4003
|
}
|
|
3755
|
-
const { configFileErrors, configFileName } = this.#service.openClientFile(filePath, sourceText, undefined,
|
|
4004
|
+
const { configFileErrors, configFileName } = this.#service.openClientFile(filePath, sourceText, undefined, this.#resolvedConfig.rootPath);
|
|
3756
4005
|
if (configFileName !== this.#lastSeenProject) {
|
|
3757
4006
|
this.#lastSeenProject = configFileName;
|
|
3758
4007
|
EventEmitter.dispatch([
|
|
3759
4008
|
"project:uses",
|
|
3760
4009
|
{ compilerVersion: this.#compiler.version, projectConfigFilePath: configFileName },
|
|
3761
4010
|
]);
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
4011
|
+
if (configFileErrors && configFileErrors.length > 0) {
|
|
4012
|
+
EventEmitter.dispatch([
|
|
4013
|
+
"project:error",
|
|
4014
|
+
{ diagnostics: Diagnostic.fromDiagnostics(configFileErrors) },
|
|
4015
|
+
]);
|
|
4016
|
+
}
|
|
3768
4017
|
}
|
|
3769
4018
|
if (!this.#seenTestFiles.has(filePath)) {
|
|
3770
4019
|
this.#seenTestFiles.add(filePath);
|
|
@@ -3778,13 +4027,16 @@ class ProjectService {
|
|
|
3778
4027
|
if (program.isSourceFileFromExternalLibrary(sourceFile) || program.isSourceFileDefaultLibrary(sourceFile)) {
|
|
3779
4028
|
return false;
|
|
3780
4029
|
}
|
|
4030
|
+
if (this.#resolvedConfig.checkDeclarationFiles && sourceFile.isDeclarationFile) {
|
|
4031
|
+
return true;
|
|
4032
|
+
}
|
|
3781
4033
|
if (Select.isFixtureFile(sourceFile.fileName, { ...this.#resolvedConfig, pathMatch: [] })) {
|
|
3782
4034
|
return true;
|
|
3783
4035
|
}
|
|
3784
4036
|
if (Select.isTestFile(sourceFile.fileName, { ...this.#resolvedConfig, pathMatch: [] })) {
|
|
3785
4037
|
return false;
|
|
3786
4038
|
}
|
|
3787
|
-
return
|
|
4039
|
+
return false;
|
|
3788
4040
|
});
|
|
3789
4041
|
const diagnostics = [];
|
|
3790
4042
|
for (const sourceFile of sourceFilesToCheck) {
|
|
@@ -3817,50 +4069,65 @@ class SuppressedDiagnosticText {
|
|
|
3817
4069
|
}
|
|
3818
4070
|
|
|
3819
4071
|
class SuppressedService {
|
|
3820
|
-
match(
|
|
3821
|
-
|
|
3822
|
-
|
|
4072
|
+
match(testTree) {
|
|
4073
|
+
if (!testTree.suppressedErrors) {
|
|
4074
|
+
return;
|
|
4075
|
+
}
|
|
4076
|
+
for (const suppressedError of testTree.suppressedErrors) {
|
|
4077
|
+
const suppressedResult = new SuppressedResult(suppressedError);
|
|
4078
|
+
if (suppressedError.diagnostics.length === 0) {
|
|
3823
4079
|
continue;
|
|
3824
4080
|
}
|
|
3825
|
-
if (
|
|
3826
|
-
|
|
3827
|
-
const origin = new DiagnosticOrigin(suppressedError.directive.start, suppressedError.directive.end, suppressedErrors.sourceFile);
|
|
3828
|
-
onDiagnostics([Diagnostic.error(text, origin)]);
|
|
4081
|
+
if (suppressedError.ignore) {
|
|
4082
|
+
EventEmitter.dispatch(["suppressed:ignore", { result: suppressedResult }]);
|
|
3829
4083
|
continue;
|
|
3830
4084
|
}
|
|
3831
4085
|
const related = [
|
|
3832
4086
|
Diagnostic.error(SuppressedDiagnosticText.suppressedError(suppressedError.diagnostics.length)),
|
|
3833
|
-
...Diagnostic.fromDiagnostics(suppressedError.diagnostics
|
|
4087
|
+
...Diagnostic.fromDiagnostics(suppressedError.diagnostics),
|
|
3834
4088
|
];
|
|
4089
|
+
const origin = new DiagnosticOrigin(suppressedError.directive.start, suppressedError.directive.end, testTree.sourceFile);
|
|
4090
|
+
if (!suppressedError.argument?.text) {
|
|
4091
|
+
const text = SuppressedDiagnosticText.directiveRequires();
|
|
4092
|
+
this.#onDiagnostics(Diagnostic.error(text, origin).add({ related }), suppressedResult);
|
|
4093
|
+
continue;
|
|
4094
|
+
}
|
|
3835
4095
|
if (suppressedError.diagnostics.length > 1) {
|
|
3836
|
-
const text =
|
|
3837
|
-
|
|
3838
|
-
onDiagnostics([Diagnostic.error(text, origin).add({ related })]);
|
|
4096
|
+
const text = SuppressedDiagnosticText.onlySingleError();
|
|
4097
|
+
this.#onDiagnostics(Diagnostic.error(text, origin).add({ related }), suppressedResult);
|
|
3839
4098
|
continue;
|
|
3840
4099
|
}
|
|
3841
4100
|
let messageText = getDiagnosticMessageText(suppressedError.diagnostics[0]);
|
|
3842
4101
|
if (Array.isArray(messageText)) {
|
|
3843
4102
|
messageText = messageText.join("\n");
|
|
3844
4103
|
}
|
|
3845
|
-
if (!messageText
|
|
3846
|
-
const text =
|
|
3847
|
-
const origin = new DiagnosticOrigin(suppressedError.argument.start, suppressedError.argument.end,
|
|
3848
|
-
onDiagnostics(
|
|
4104
|
+
if (!this.#matchMessage(messageText, suppressedError.argument.text)) {
|
|
4105
|
+
const text = SuppressedDiagnosticText.messageDidNotMatch();
|
|
4106
|
+
const origin = new DiagnosticOrigin(suppressedError.argument.start, suppressedError.argument.end, testTree.sourceFile);
|
|
4107
|
+
this.#onDiagnostics(Diagnostic.error(text, origin).add({ related }), suppressedResult);
|
|
4108
|
+
continue;
|
|
4109
|
+
}
|
|
4110
|
+
EventEmitter.dispatch(["suppressed:match", { result: suppressedResult }]);
|
|
4111
|
+
}
|
|
4112
|
+
}
|
|
4113
|
+
#matchMessage(source, target) {
|
|
4114
|
+
if (target.includes("...")) {
|
|
4115
|
+
let position = 0;
|
|
4116
|
+
for (const segment of target.split("...")) {
|
|
4117
|
+
position = source.indexOf(segment, position);
|
|
4118
|
+
if (position === -1) {
|
|
4119
|
+
break;
|
|
4120
|
+
}
|
|
3849
4121
|
}
|
|
4122
|
+
return position > 0;
|
|
3850
4123
|
}
|
|
4124
|
+
return source.includes(target);
|
|
4125
|
+
}
|
|
4126
|
+
#onDiagnostics(diagnostic, result) {
|
|
4127
|
+
EventEmitter.dispatch(["suppressed:error", { diagnostics: [diagnostic], result }]);
|
|
3851
4128
|
}
|
|
3852
4129
|
}
|
|
3853
4130
|
|
|
3854
|
-
var RunMode;
|
|
3855
|
-
(function (RunMode) {
|
|
3856
|
-
RunMode[RunMode["Normal"] = 0] = "Normal";
|
|
3857
|
-
RunMode[RunMode["Fail"] = 1] = "Fail";
|
|
3858
|
-
RunMode[RunMode["Only"] = 2] = "Only";
|
|
3859
|
-
RunMode[RunMode["Skip"] = 4] = "Skip";
|
|
3860
|
-
RunMode[RunMode["Todo"] = 8] = "Todo";
|
|
3861
|
-
RunMode[RunMode["Void"] = 16] = "Void";
|
|
3862
|
-
})(RunMode || (RunMode = {}));
|
|
3863
|
-
|
|
3864
4131
|
class EnsureDiagnosticText {
|
|
3865
4132
|
static argumentMustBeProvided(argumentNameText) {
|
|
3866
4133
|
return `An argument for '${argumentNameText}' must be provided.`;
|
|
@@ -3961,11 +4228,11 @@ class ExpectDiagnosticText {
|
|
|
3961
4228
|
static isNotAssignableTo(sourceTypeText, targetTypeText) {
|
|
3962
4229
|
return `Type '${sourceTypeText}' is not assignable to type '${targetTypeText}'.`;
|
|
3963
4230
|
}
|
|
3964
|
-
static
|
|
3965
|
-
return `Type '${sourceTypeText}' is assignable
|
|
4231
|
+
static isAssignableFrom(sourceTypeText, targetTypeText) {
|
|
4232
|
+
return `Type '${sourceTypeText}' is assignable from type '${targetTypeText}'.`;
|
|
3966
4233
|
}
|
|
3967
|
-
static
|
|
3968
|
-
return `Type '${sourceTypeText}' is not assignable
|
|
4234
|
+
static isNotAssignableFrom(sourceTypeText, targetTypeText) {
|
|
4235
|
+
return `Type '${sourceTypeText}' is not assignable from type '${targetTypeText}'.`;
|
|
3969
4236
|
}
|
|
3970
4237
|
static isTheSame(sourceTypeText, targetTypeText) {
|
|
3971
4238
|
return `Type '${sourceTypeText}' is the same as type '${targetTypeText}'.`;
|
|
@@ -3984,21 +4251,15 @@ class ExpectDiagnosticText {
|
|
|
3984
4251
|
}
|
|
3985
4252
|
}
|
|
3986
4253
|
|
|
3987
|
-
var Relation;
|
|
3988
|
-
(function (Relation) {
|
|
3989
|
-
Relation["Assignable"] = "assignable";
|
|
3990
|
-
Relation["Identical"] = "identical";
|
|
3991
|
-
})(Relation || (Relation = {}));
|
|
3992
|
-
|
|
3993
4254
|
class MatchWorker {
|
|
3994
|
-
|
|
4255
|
+
assertionNode;
|
|
3995
4256
|
#compiler;
|
|
3996
4257
|
#signatureCache = new Map();
|
|
3997
4258
|
typeChecker;
|
|
3998
|
-
constructor(compiler, typeChecker,
|
|
4259
|
+
constructor(compiler, typeChecker, assertionNode) {
|
|
3999
4260
|
this.#compiler = compiler;
|
|
4000
4261
|
this.typeChecker = typeChecker;
|
|
4001
|
-
this.
|
|
4262
|
+
this.assertionNode = assertionNode;
|
|
4002
4263
|
}
|
|
4003
4264
|
checkHasApplicableIndexType(sourceNode, targetNode) {
|
|
4004
4265
|
const sourceType = this.getType(sourceNode);
|
|
@@ -4014,13 +4275,13 @@ class MatchWorker {
|
|
|
4014
4275
|
.some((property) => this.#compiler.unescapeLeadingUnderscores(property.escapedName) === propertyNameText);
|
|
4015
4276
|
}
|
|
4016
4277
|
checkIsAssignableTo(sourceNode, targetNode) {
|
|
4017
|
-
return this.#checkIsRelatedTo(sourceNode, targetNode,
|
|
4278
|
+
return this.#checkIsRelatedTo(sourceNode, targetNode, "assignable");
|
|
4018
4279
|
}
|
|
4019
4280
|
checkIsAssignableWith(sourceNode, targetNode) {
|
|
4020
|
-
return this.#checkIsRelatedTo(targetNode, sourceNode,
|
|
4281
|
+
return this.#checkIsRelatedTo(targetNode, sourceNode, "assignable");
|
|
4021
4282
|
}
|
|
4022
4283
|
checkIsIdenticalTo(sourceNode, targetNode) {
|
|
4023
|
-
return (this.#checkIsRelatedTo(sourceNode, targetNode,
|
|
4284
|
+
return (this.#checkIsRelatedTo(sourceNode, targetNode, "identical") &&
|
|
4024
4285
|
this.checkIsAssignableTo(sourceNode, targetNode) &&
|
|
4025
4286
|
this.checkIsAssignableWith(sourceNode, targetNode));
|
|
4026
4287
|
}
|
|
@@ -4028,9 +4289,9 @@ class MatchWorker {
|
|
|
4028
4289
|
const sourceType = relation === "identical" ? this.#simplifyType(this.getType(sourceNode)) : this.getType(sourceNode);
|
|
4029
4290
|
const targetType = relation === "identical" ? this.#simplifyType(this.getType(targetNode)) : this.getType(targetNode);
|
|
4030
4291
|
switch (relation) {
|
|
4031
|
-
case
|
|
4292
|
+
case "assignable":
|
|
4032
4293
|
return this.typeChecker.isTypeAssignableTo(sourceType, targetType);
|
|
4033
|
-
case
|
|
4294
|
+
case "identical":
|
|
4034
4295
|
return this.typeChecker.isTypeIdenticalTo(sourceType, targetType);
|
|
4035
4296
|
}
|
|
4036
4297
|
}
|
|
@@ -4071,9 +4332,9 @@ class MatchWorker {
|
|
|
4071
4332
|
this.#compiler.isShorthandPropertyAssignment(symbol.valueDeclaration)) &&
|
|
4072
4333
|
symbol.valueDeclaration.getStart() >= enclosingNode.getStart() &&
|
|
4073
4334
|
symbol.valueDeclaration.getEnd() <= enclosingNode.getEnd()) {
|
|
4074
|
-
return DiagnosticOrigin.fromNode(symbol.valueDeclaration.name, this.
|
|
4335
|
+
return DiagnosticOrigin.fromNode(symbol.valueDeclaration.name, this.assertionNode);
|
|
4075
4336
|
}
|
|
4076
|
-
return DiagnosticOrigin.fromNode(enclosingNode, this.
|
|
4337
|
+
return DiagnosticOrigin.fromNode(enclosingNode, this.assertionNode);
|
|
4077
4338
|
}
|
|
4078
4339
|
#simplifyType(type) {
|
|
4079
4340
|
if (type.isUnionOrIntersection()) {
|
|
@@ -4108,10 +4369,10 @@ class ToAcceptProps {
|
|
|
4108
4369
|
const signatures = matchWorker.getSignatures(sourceNode);
|
|
4109
4370
|
return signatures.reduce((accumulator, signature, index) => {
|
|
4110
4371
|
let diagnostic;
|
|
4111
|
-
const introText = matchWorker.
|
|
4372
|
+
const introText = matchWorker.assertionNode.isNot
|
|
4112
4373
|
? ExpectDiagnosticText.acceptsProps(isExpression)
|
|
4113
4374
|
: ExpectDiagnosticText.doesNotAcceptProps(isExpression);
|
|
4114
|
-
const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.
|
|
4375
|
+
const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.assertionNode);
|
|
4115
4376
|
if (signatures.length > 1) {
|
|
4116
4377
|
const signatureText = this.#typeChecker.signatureToString(signature, sourceNode);
|
|
4117
4378
|
const overloadText = ExpectDiagnosticText.overloadGaveTheFollowingError(index + 1, signatures.length, signatureText);
|
|
@@ -4121,7 +4382,7 @@ class ToAcceptProps {
|
|
|
4121
4382
|
diagnostic = Diagnostic.error([introText], origin);
|
|
4122
4383
|
}
|
|
4123
4384
|
const { diagnostics, isMatch } = this.#explainProperties(matchWorker, signature, targetNode, diagnostic);
|
|
4124
|
-
if (matchWorker.
|
|
4385
|
+
if (matchWorker.assertionNode.isNot ? isMatch : !isMatch) {
|
|
4125
4386
|
accumulator.push(...diagnostics);
|
|
4126
4387
|
}
|
|
4127
4388
|
return accumulator;
|
|
@@ -4185,7 +4446,7 @@ class ToAcceptProps {
|
|
|
4185
4446
|
}
|
|
4186
4447
|
if (this.#isOptionalProperty(targetProperty) && !this.#isOptionalProperty(sourceProperty)) {
|
|
4187
4448
|
const text = [
|
|
4188
|
-
ExpectDiagnosticText.
|
|
4449
|
+
ExpectDiagnosticText.isNotAssignableFrom(sourceTypeText, targetTypeText),
|
|
4189
4450
|
ExpectDiagnosticText.requiresProperty(sourceTypeText, targetPropertyName),
|
|
4190
4451
|
];
|
|
4191
4452
|
const origin = matchWorker.resolveDiagnosticOrigin(targetProperty, targetNode);
|
|
@@ -4198,9 +4459,9 @@ class ToAcceptProps {
|
|
|
4198
4459
|
const targetPropertyTypeText = this.#typeChecker.typeToString(targetPropertyType);
|
|
4199
4460
|
const sourcePropertyTypeText = this.#typeChecker.typeToString(sourcePropertyType);
|
|
4200
4461
|
const text = [
|
|
4201
|
-
ExpectDiagnosticText.
|
|
4462
|
+
ExpectDiagnosticText.isNotAssignableFrom(sourceTypeText, targetTypeText),
|
|
4202
4463
|
ExpectDiagnosticText.typesOfPropertyAreNotCompatible(targetPropertyName),
|
|
4203
|
-
ExpectDiagnosticText.
|
|
4464
|
+
ExpectDiagnosticText.isNotAssignableFrom(sourcePropertyTypeText, targetPropertyTypeText),
|
|
4204
4465
|
];
|
|
4205
4466
|
const origin = matchWorker.resolveDiagnosticOrigin(targetProperty, targetNode);
|
|
4206
4467
|
diagnostics.push(diagnostic.extendWith(text, origin));
|
|
@@ -4212,7 +4473,7 @@ class ToAcceptProps {
|
|
|
4212
4473
|
const targetProperty = targetType.getProperty(sourcePropertyName);
|
|
4213
4474
|
if (!targetProperty && !this.#isOptionalProperty(sourceProperty)) {
|
|
4214
4475
|
const text = [
|
|
4215
|
-
ExpectDiagnosticText.
|
|
4476
|
+
ExpectDiagnosticText.isNotAssignableFrom(sourceTypeText, targetTypeText),
|
|
4216
4477
|
ExpectDiagnosticText.requiresProperty(sourceTypeText, sourcePropertyName),
|
|
4217
4478
|
];
|
|
4218
4479
|
diagnostics.push(diagnostic.extendWith(text));
|
|
@@ -4220,7 +4481,7 @@ class ToAcceptProps {
|
|
|
4220
4481
|
}
|
|
4221
4482
|
}
|
|
4222
4483
|
if (diagnostics.length === 0) {
|
|
4223
|
-
const text = ExpectDiagnosticText.
|
|
4484
|
+
const text = ExpectDiagnosticText.isAssignableFrom(sourceTypeText, targetTypeText);
|
|
4224
4485
|
diagnostics.push(diagnostic.extendWith(text));
|
|
4225
4486
|
return { diagnostics, isMatch: true };
|
|
4226
4487
|
}
|
|
@@ -4229,9 +4490,9 @@ class ToAcceptProps {
|
|
|
4229
4490
|
if (sourceType != null && isUnionType(this.#compiler, sourceType)) {
|
|
4230
4491
|
let accumulator = [];
|
|
4231
4492
|
const isMatch = sourceType.types.some((sourceType) => {
|
|
4232
|
-
const text = matchWorker.
|
|
4233
|
-
? ExpectDiagnosticText.
|
|
4234
|
-
: ExpectDiagnosticText.
|
|
4493
|
+
const text = matchWorker.assertionNode.isNot
|
|
4494
|
+
? ExpectDiagnosticText.isAssignableFrom(sourceTypeText, targetTypeText)
|
|
4495
|
+
: ExpectDiagnosticText.isNotAssignableFrom(sourceTypeText, targetTypeText);
|
|
4235
4496
|
const { diagnostics, isMatch } = explain(sourceType, targetType, diagnostic.extendWith(text));
|
|
4236
4497
|
if (isMatch) {
|
|
4237
4498
|
accumulator = diagnostics;
|
|
@@ -4284,10 +4545,10 @@ class RelationMatcherBase {
|
|
|
4284
4545
|
explain(matchWorker, sourceNode, targetNode) {
|
|
4285
4546
|
const sourceTypeText = matchWorker.getTypeText(sourceNode);
|
|
4286
4547
|
const targetTypeText = matchWorker.getTypeText(targetNode);
|
|
4287
|
-
const text = matchWorker.
|
|
4548
|
+
const text = matchWorker.assertionNode.isNot
|
|
4288
4549
|
? this.explainText(sourceTypeText, targetTypeText)
|
|
4289
4550
|
: this.explainNotText(sourceTypeText, targetTypeText);
|
|
4290
|
-
const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.
|
|
4551
|
+
const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.assertionNode);
|
|
4291
4552
|
return [Diagnostic.error(text, origin)];
|
|
4292
4553
|
}
|
|
4293
4554
|
}
|
|
@@ -4334,18 +4595,22 @@ class ToBeApplicable {
|
|
|
4334
4595
|
}
|
|
4335
4596
|
return text;
|
|
4336
4597
|
}
|
|
4337
|
-
#explain(matchWorker
|
|
4338
|
-
const targetText = this.#resolveTargetText(matchWorker.
|
|
4598
|
+
#explain(matchWorker) {
|
|
4599
|
+
const targetText = this.#resolveTargetText(matchWorker.assertionNode.matcherNode.parent);
|
|
4339
4600
|
const diagnostics = [];
|
|
4340
|
-
if (matchWorker.
|
|
4341
|
-
|
|
4601
|
+
if (matchWorker.assertionNode.abilityDiagnostics.size > 0) {
|
|
4602
|
+
const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertionNode);
|
|
4603
|
+
for (const diagnostic of matchWorker.assertionNode.abilityDiagnostics) {
|
|
4342
4604
|
const text = [ExpectDiagnosticText.cannotBeApplied(targetText), getDiagnosticMessageText(diagnostic)];
|
|
4343
|
-
|
|
4344
|
-
|
|
4605
|
+
let related;
|
|
4606
|
+
if (diagnostic.relatedInformation != null) {
|
|
4607
|
+
related = Diagnostic.fromDiagnostics(diagnostic.relatedInformation);
|
|
4608
|
+
}
|
|
4609
|
+
diagnostics.push(Diagnostic.error(text.flat(), origin).add({ related }));
|
|
4345
4610
|
}
|
|
4346
4611
|
}
|
|
4347
4612
|
else {
|
|
4348
|
-
const origin = DiagnosticOrigin.fromAssertion(matchWorker.
|
|
4613
|
+
const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertionNode);
|
|
4349
4614
|
diagnostics.push(Diagnostic.error(ExpectDiagnosticText.canBeApplied(targetText), origin));
|
|
4350
4615
|
}
|
|
4351
4616
|
return diagnostics;
|
|
@@ -4362,30 +4627,30 @@ class ToBeApplicable {
|
|
|
4362
4627
|
return;
|
|
4363
4628
|
}
|
|
4364
4629
|
return {
|
|
4365
|
-
explain: () => this.#explain(matchWorker
|
|
4366
|
-
isMatch: matchWorker.
|
|
4630
|
+
explain: () => this.#explain(matchWorker),
|
|
4631
|
+
isMatch: matchWorker.assertionNode.abilityDiagnostics.size === 0,
|
|
4367
4632
|
};
|
|
4368
4633
|
}
|
|
4369
4634
|
}
|
|
4370
4635
|
|
|
4371
|
-
class
|
|
4372
|
-
explainText = ExpectDiagnosticText.
|
|
4373
|
-
explainNotText = ExpectDiagnosticText.
|
|
4636
|
+
class ToBeAssignableFrom extends RelationMatcherBase {
|
|
4637
|
+
explainText = ExpectDiagnosticText.isAssignableFrom;
|
|
4638
|
+
explainNotText = ExpectDiagnosticText.isNotAssignableFrom;
|
|
4374
4639
|
match(matchWorker, sourceNode, targetNode) {
|
|
4375
4640
|
return {
|
|
4376
4641
|
explain: () => this.explain(matchWorker, sourceNode, targetNode),
|
|
4377
|
-
isMatch: matchWorker.
|
|
4642
|
+
isMatch: matchWorker.checkIsAssignableWith(sourceNode, targetNode),
|
|
4378
4643
|
};
|
|
4379
4644
|
}
|
|
4380
4645
|
}
|
|
4381
4646
|
|
|
4382
|
-
class
|
|
4383
|
-
explainText = ExpectDiagnosticText.
|
|
4384
|
-
explainNotText = ExpectDiagnosticText.
|
|
4647
|
+
class ToBeAssignableTo extends RelationMatcherBase {
|
|
4648
|
+
explainText = ExpectDiagnosticText.isAssignableTo;
|
|
4649
|
+
explainNotText = ExpectDiagnosticText.isNotAssignableTo;
|
|
4385
4650
|
match(matchWorker, sourceNode, targetNode) {
|
|
4386
4651
|
return {
|
|
4387
4652
|
explain: () => this.explain(matchWorker, sourceNode, targetNode),
|
|
4388
|
-
isMatch: matchWorker.
|
|
4653
|
+
isMatch: matchWorker.checkIsAssignableTo(sourceNode, targetNode),
|
|
4389
4654
|
};
|
|
4390
4655
|
}
|
|
4391
4656
|
}
|
|
@@ -4408,32 +4673,25 @@ class AbilityMatcherBase {
|
|
|
4408
4673
|
const isExpression = nodeBelongsToArgumentList(this.compiler, sourceNode);
|
|
4409
4674
|
const targetText = this.#resolveTargetText(targetNodes);
|
|
4410
4675
|
const diagnostics = [];
|
|
4411
|
-
if (matchWorker.
|
|
4412
|
-
for (const diagnostic of matchWorker.
|
|
4676
|
+
if (matchWorker.assertionNode.abilityDiagnostics.size > 0) {
|
|
4677
|
+
for (const diagnostic of matchWorker.assertionNode.abilityDiagnostics) {
|
|
4678
|
+
const text = [this.explainNotText(isExpression, targetText), getDiagnosticMessageText(diagnostic)];
|
|
4413
4679
|
let origin;
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
origin = new DiagnosticOrigin(diagnostic.start, getTextSpanEnd(diagnostic), sourceNode.getSourceFile(), matchWorker.assertion);
|
|
4417
|
-
text.push(getDiagnosticMessageText(diagnostic));
|
|
4680
|
+
if (isDiagnosticWithLocation(diagnostic) && diagnosticBelongsToNode(diagnostic, targetNodes)) {
|
|
4681
|
+
origin = new DiagnosticOrigin(diagnostic.start, getTextSpanEnd(diagnostic), sourceNode.getSourceFile(), matchWorker.assertionNode);
|
|
4418
4682
|
}
|
|
4419
4683
|
else {
|
|
4420
|
-
|
|
4421
|
-
origin = new DiagnosticOrigin(diagnostic.start, getTextSpanEnd(diagnostic), sourceNode.getSourceFile(), matchWorker.assertion);
|
|
4422
|
-
}
|
|
4423
|
-
else {
|
|
4424
|
-
origin = DiagnosticOrigin.fromAssertion(matchWorker.assertion);
|
|
4425
|
-
}
|
|
4426
|
-
text.push(this.explainNotText(isExpression, targetText), getDiagnosticMessageText(diagnostic));
|
|
4684
|
+
origin = DiagnosticOrigin.fromAssertion(matchWorker.assertionNode);
|
|
4427
4685
|
}
|
|
4428
4686
|
let related;
|
|
4429
4687
|
if (diagnostic.relatedInformation != null) {
|
|
4430
|
-
related = Diagnostic.fromDiagnostics(diagnostic.relatedInformation
|
|
4688
|
+
related = Diagnostic.fromDiagnostics(diagnostic.relatedInformation);
|
|
4431
4689
|
}
|
|
4432
4690
|
diagnostics.push(Diagnostic.error(text.flat(), origin).add({ related }));
|
|
4433
4691
|
}
|
|
4434
4692
|
}
|
|
4435
4693
|
else {
|
|
4436
|
-
const origin = DiagnosticOrigin.fromAssertion(matchWorker.
|
|
4694
|
+
const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertionNode);
|
|
4437
4695
|
diagnostics.push(Diagnostic.error(this.explainText(isExpression, targetText), origin));
|
|
4438
4696
|
}
|
|
4439
4697
|
return diagnostics;
|
|
@@ -4473,7 +4731,7 @@ class ToBeCallableWith extends AbilityMatcherBase {
|
|
|
4473
4731
|
}
|
|
4474
4732
|
return {
|
|
4475
4733
|
explain: () => this.explain(matchWorker, sourceNode, targetNodes),
|
|
4476
|
-
isMatch: matchWorker.
|
|
4734
|
+
isMatch: matchWorker.assertionNode.abilityDiagnostics.size === 0,
|
|
4477
4735
|
};
|
|
4478
4736
|
}
|
|
4479
4737
|
}
|
|
@@ -4508,7 +4766,7 @@ class ToBeConstructableWith extends AbilityMatcherBase {
|
|
|
4508
4766
|
}
|
|
4509
4767
|
return {
|
|
4510
4768
|
explain: () => this.explain(matchWorker, sourceNode, targetNodes),
|
|
4511
|
-
isMatch: matchWorker.
|
|
4769
|
+
isMatch: matchWorker.assertionNode.abilityDiagnostics.size === 0,
|
|
4512
4770
|
};
|
|
4513
4771
|
}
|
|
4514
4772
|
}
|
|
@@ -4528,8 +4786,8 @@ class ToHaveProperty {
|
|
|
4528
4786
|
else {
|
|
4529
4787
|
propertyNameText = `[${this.#compiler.unescapeLeadingUnderscores(targetType.symbol.escapedName)}]`;
|
|
4530
4788
|
}
|
|
4531
|
-
const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.
|
|
4532
|
-
return matchWorker.
|
|
4789
|
+
const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.assertionNode);
|
|
4790
|
+
return matchWorker.assertionNode.isNot
|
|
4533
4791
|
? [Diagnostic.error(ExpectDiagnosticText.hasProperty(sourceTypeText, propertyNameText), origin)]
|
|
4534
4792
|
: [Diagnostic.error(ExpectDiagnosticText.doesNotHaveProperty(sourceTypeText, propertyNameText), origin)];
|
|
4535
4793
|
}
|
|
@@ -4579,28 +4837,28 @@ class ToRaiseError {
|
|
|
4579
4837
|
}
|
|
4580
4838
|
#explain(matchWorker, sourceNode, targetNodes) {
|
|
4581
4839
|
const isExpression = nodeBelongsToArgumentList(this.#compiler, sourceNode);
|
|
4582
|
-
const origin = DiagnosticOrigin.fromAssertion(matchWorker.
|
|
4583
|
-
if (matchWorker.
|
|
4840
|
+
const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertionNode);
|
|
4841
|
+
if (matchWorker.assertionNode.diagnostics.size === 0) {
|
|
4584
4842
|
const text = ExpectDiagnosticText.didNotRaiseError(isExpression);
|
|
4585
4843
|
return [Diagnostic.error(text, origin)];
|
|
4586
4844
|
}
|
|
4587
|
-
if (matchWorker.
|
|
4588
|
-
const count = matchWorker.
|
|
4845
|
+
if (matchWorker.assertionNode.diagnostics.size !== targetNodes.length) {
|
|
4846
|
+
const count = matchWorker.assertionNode.diagnostics.size;
|
|
4589
4847
|
const text = ExpectDiagnosticText.raisedError(isExpression, count, targetNodes.length);
|
|
4590
4848
|
const related = [
|
|
4591
4849
|
Diagnostic.error(ExpectDiagnosticText.raisedTypeError(count)),
|
|
4592
|
-
...Diagnostic.fromDiagnostics([...matchWorker.
|
|
4850
|
+
...Diagnostic.fromDiagnostics([...matchWorker.assertionNode.diagnostics]),
|
|
4593
4851
|
];
|
|
4594
4852
|
return [Diagnostic.error(text, origin).add({ related })];
|
|
4595
4853
|
}
|
|
4596
|
-
return [...matchWorker.
|
|
4854
|
+
return [...matchWorker.assertionNode.diagnostics].reduce((accumulator, diagnostic, index) => {
|
|
4597
4855
|
const targetNode = targetNodes[index];
|
|
4598
4856
|
const isMatch = this.#matchExpectedError(diagnostic, targetNode);
|
|
4599
|
-
if (matchWorker.
|
|
4600
|
-
const text = matchWorker.
|
|
4857
|
+
if (matchWorker.assertionNode.isNot ? isMatch : !isMatch) {
|
|
4858
|
+
const text = matchWorker.assertionNode.isNot
|
|
4601
4859
|
? ExpectDiagnosticText.raisedMatchingError(isExpression)
|
|
4602
4860
|
: ExpectDiagnosticText.didNotRaiseMatchingError(isExpression);
|
|
4603
|
-
const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.
|
|
4861
|
+
const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.assertionNode);
|
|
4604
4862
|
const related = [
|
|
4605
4863
|
Diagnostic.error(ExpectDiagnosticText.raisedTypeError()),
|
|
4606
4864
|
...Diagnostic.fromDiagnostics([diagnostic]),
|
|
@@ -4628,12 +4886,12 @@ class ToRaiseError {
|
|
|
4628
4886
|
}
|
|
4629
4887
|
let isMatch;
|
|
4630
4888
|
if (targetNodes.length === 0) {
|
|
4631
|
-
isMatch = matchWorker.
|
|
4889
|
+
isMatch = matchWorker.assertionNode.diagnostics.size > 0;
|
|
4632
4890
|
}
|
|
4633
4891
|
else {
|
|
4634
4892
|
isMatch =
|
|
4635
|
-
matchWorker.
|
|
4636
|
-
[...matchWorker.
|
|
4893
|
+
matchWorker.assertionNode.diagnostics.size === targetNodes.length &&
|
|
4894
|
+
[...matchWorker.assertionNode.diagnostics].every((diagnostic, index) => this.#matchExpectedError(diagnostic, targetNodes[index]));
|
|
4637
4895
|
}
|
|
4638
4896
|
return {
|
|
4639
4897
|
explain: () => this.#explain(matchWorker, sourceNode, targetNodes),
|
|
@@ -4663,8 +4921,8 @@ class ExpectService {
|
|
|
4663
4921
|
toAcceptProps;
|
|
4664
4922
|
toBe;
|
|
4665
4923
|
toBeApplicable;
|
|
4924
|
+
toBeAssignableFrom;
|
|
4666
4925
|
toBeAssignableTo;
|
|
4667
|
-
toBeAssignableWith;
|
|
4668
4926
|
toBeCallableWith;
|
|
4669
4927
|
toBeConstructableWith;
|
|
4670
4928
|
toHaveProperty;
|
|
@@ -4676,54 +4934,54 @@ class ExpectService {
|
|
|
4676
4934
|
this.toAcceptProps = new ToAcceptProps(compiler, typeChecker);
|
|
4677
4935
|
this.toBe = new ToBe();
|
|
4678
4936
|
this.toBeApplicable = new ToBeApplicable(compiler);
|
|
4937
|
+
this.toBeAssignableFrom = new ToBeAssignableFrom();
|
|
4679
4938
|
this.toBeAssignableTo = new ToBeAssignableTo();
|
|
4680
|
-
this.toBeAssignableWith = new ToBeAssignableWith();
|
|
4681
4939
|
this.toBeCallableWith = new ToBeCallableWith(compiler);
|
|
4682
4940
|
this.toBeConstructableWith = new ToBeConstructableWith(compiler);
|
|
4683
4941
|
this.toHaveProperty = new ToHaveProperty(compiler);
|
|
4684
4942
|
this.toRaiseError = new ToRaiseError(compiler);
|
|
4685
4943
|
}
|
|
4686
|
-
match(
|
|
4687
|
-
const matcherNameText =
|
|
4688
|
-
if (!argumentOrTypeArgumentIsProvided("source", "Source",
|
|
4944
|
+
match(assertionNode, onDiagnostics) {
|
|
4945
|
+
const matcherNameText = assertionNode.matcherNameNode.name.text;
|
|
4946
|
+
if (!argumentOrTypeArgumentIsProvided("source", "Source", assertionNode.source[0], assertionNode.node.expression, onDiagnostics)) {
|
|
4689
4947
|
return;
|
|
4690
4948
|
}
|
|
4691
|
-
const matchWorker = new MatchWorker(this.#compiler, this.#typeChecker,
|
|
4692
|
-
if (!(matcherNameText === "toRaiseError" &&
|
|
4949
|
+
const matchWorker = new MatchWorker(this.#compiler, this.#typeChecker, assertionNode);
|
|
4950
|
+
if (!(matcherNameText === "toRaiseError" && assertionNode.isNot === false) &&
|
|
4693
4951
|
this.#reject.argumentType([
|
|
4694
|
-
["source",
|
|
4695
|
-
["target",
|
|
4952
|
+
["source", assertionNode.source[0]],
|
|
4953
|
+
["target", assertionNode.target?.[0]],
|
|
4696
4954
|
], onDiagnostics)) {
|
|
4697
4955
|
return;
|
|
4698
4956
|
}
|
|
4699
4957
|
switch (matcherNameText) {
|
|
4700
4958
|
case "toAcceptProps":
|
|
4701
4959
|
case "toBe":
|
|
4960
|
+
case "toBeAssignableFrom":
|
|
4702
4961
|
case "toBeAssignableTo":
|
|
4703
|
-
|
|
4704
|
-
if (!argumentOrTypeArgumentIsProvided("target", "Target", assertion.target?.[0], assertion.matcherNameNode.name, onDiagnostics)) {
|
|
4962
|
+
if (!argumentOrTypeArgumentIsProvided("target", "Target", assertionNode.target?.[0], assertionNode.matcherNameNode.name, onDiagnostics)) {
|
|
4705
4963
|
return;
|
|
4706
4964
|
}
|
|
4707
|
-
return this[matcherNameText].match(matchWorker,
|
|
4965
|
+
return this[matcherNameText].match(matchWorker, assertionNode.source[0], assertionNode.target[0], onDiagnostics);
|
|
4708
4966
|
case "toBeApplicable":
|
|
4709
|
-
return this.toBeApplicable.match(matchWorker,
|
|
4967
|
+
return this.toBeApplicable.match(matchWorker, assertionNode.source[0], onDiagnostics);
|
|
4710
4968
|
case "toBeCallableWith":
|
|
4711
4969
|
case "toBeConstructableWith":
|
|
4712
4970
|
case "toRaiseError":
|
|
4713
|
-
return this[matcherNameText].match(matchWorker,
|
|
4971
|
+
return this[matcherNameText].match(matchWorker, assertionNode.source[0], assertionNode.target, onDiagnostics);
|
|
4714
4972
|
case "toHaveProperty":
|
|
4715
|
-
if (!argumentIsProvided("key",
|
|
4973
|
+
if (!argumentIsProvided("key", assertionNode.target?.[0], assertionNode.matcherNameNode.name, onDiagnostics)) {
|
|
4716
4974
|
return;
|
|
4717
4975
|
}
|
|
4718
|
-
return this.toHaveProperty.match(matchWorker,
|
|
4976
|
+
return this.toHaveProperty.match(matchWorker, assertionNode.source[0], assertionNode.target[0], onDiagnostics);
|
|
4719
4977
|
default:
|
|
4720
|
-
this.#onMatcherIsNotSupported(matcherNameText,
|
|
4978
|
+
this.#onMatcherIsNotSupported(matcherNameText, assertionNode, onDiagnostics);
|
|
4721
4979
|
}
|
|
4722
4980
|
return;
|
|
4723
4981
|
}
|
|
4724
|
-
#onMatcherIsNotSupported(matcherNameText,
|
|
4982
|
+
#onMatcherIsNotSupported(matcherNameText, assertionNode, onDiagnostics) {
|
|
4725
4983
|
const text = ExpectDiagnosticText.matcherIsNotSupported(matcherNameText);
|
|
4726
|
-
const origin = DiagnosticOrigin.fromNode(
|
|
4984
|
+
const origin = DiagnosticOrigin.fromNode(assertionNode.matcherNameNode.name);
|
|
4727
4985
|
onDiagnostics(Diagnostic.error(text, origin));
|
|
4728
4986
|
}
|
|
4729
4987
|
}
|
|
@@ -4844,82 +5102,145 @@ class WhenService {
|
|
|
4844
5102
|
}
|
|
4845
5103
|
}
|
|
4846
5104
|
|
|
5105
|
+
class FixmeDiagnosticText {
|
|
5106
|
+
static considerRemoving() {
|
|
5107
|
+
return "Consider removing the '// @tstyche fixme' directive.";
|
|
5108
|
+
}
|
|
5109
|
+
static wasSupposedToFail(target) {
|
|
5110
|
+
return `The '${target}()' was supposed to fail, but it passed.`;
|
|
5111
|
+
}
|
|
5112
|
+
}
|
|
5113
|
+
|
|
5114
|
+
class FixmeService {
|
|
5115
|
+
static #expectRange;
|
|
5116
|
+
static #range;
|
|
5117
|
+
static async start(directive, owner) {
|
|
5118
|
+
const inlineConfig = await Directive.getInlineConfig(directive);
|
|
5119
|
+
if (inlineConfig?.fixme === true) {
|
|
5120
|
+
if (owner.brand === "expect") {
|
|
5121
|
+
FixmeService.#expectRange = { directive, owner, previous: FixmeService.#expectRange };
|
|
5122
|
+
}
|
|
5123
|
+
else {
|
|
5124
|
+
FixmeService.#range = { directive, owner, previous: FixmeService.#range };
|
|
5125
|
+
}
|
|
5126
|
+
}
|
|
5127
|
+
}
|
|
5128
|
+
static isFixme(owner, isPass) {
|
|
5129
|
+
if (owner.brand !== "expect") {
|
|
5130
|
+
return owner === FixmeService.#range?.owner && FixmeService.#range.isFail === true;
|
|
5131
|
+
}
|
|
5132
|
+
if (owner === FixmeService.#expectRange?.owner) {
|
|
5133
|
+
FixmeService.#expectRange.isFail = !isPass;
|
|
5134
|
+
return !isPass;
|
|
5135
|
+
}
|
|
5136
|
+
if (FixmeService.#range != null) {
|
|
5137
|
+
if (!isPass) {
|
|
5138
|
+
FixmeService.#range.isFail = true;
|
|
5139
|
+
return true;
|
|
5140
|
+
}
|
|
5141
|
+
FixmeService.#range.isFail ??= false;
|
|
5142
|
+
}
|
|
5143
|
+
return false;
|
|
5144
|
+
}
|
|
5145
|
+
static end(directive, owner, onFileDiagnostics) {
|
|
5146
|
+
let isFail;
|
|
5147
|
+
if (owner === FixmeService.#expectRange?.owner) {
|
|
5148
|
+
isFail = FixmeService.#expectRange.isFail;
|
|
5149
|
+
FixmeService.#expectRange = FixmeService.#expectRange?.previous;
|
|
5150
|
+
}
|
|
5151
|
+
if (owner === FixmeService.#range?.owner) {
|
|
5152
|
+
isFail = FixmeService.#range?.isFail;
|
|
5153
|
+
FixmeService.#range = FixmeService.#range?.previous;
|
|
5154
|
+
}
|
|
5155
|
+
if (isFail === false) {
|
|
5156
|
+
const targetText = owner.node.expression.getText();
|
|
5157
|
+
const text = [FixmeDiagnosticText.wasSupposedToFail(targetText), FixmeDiagnosticText.considerRemoving()];
|
|
5158
|
+
const origin = new DiagnosticOrigin(directive.namespace.start, directive.directive.end, directive.sourceFile);
|
|
5159
|
+
onFileDiagnostics([Diagnostic.error(text, origin)]);
|
|
5160
|
+
}
|
|
5161
|
+
}
|
|
5162
|
+
}
|
|
5163
|
+
|
|
4847
5164
|
class TestTreeWalker {
|
|
4848
5165
|
#cancellationToken;
|
|
4849
5166
|
#compiler;
|
|
4850
5167
|
#expectService;
|
|
4851
5168
|
#hasOnly;
|
|
4852
|
-
#
|
|
5169
|
+
#onFileDiagnostics;
|
|
4853
5170
|
#position;
|
|
4854
5171
|
#resolvedConfig;
|
|
4855
5172
|
#whenService;
|
|
4856
|
-
constructor(compiler, typeChecker, resolvedConfig,
|
|
5173
|
+
constructor(compiler, typeChecker, resolvedConfig, onFileDiagnostics, options) {
|
|
4857
5174
|
this.#compiler = compiler;
|
|
4858
5175
|
this.#resolvedConfig = resolvedConfig;
|
|
4859
|
-
this.#
|
|
5176
|
+
this.#onFileDiagnostics = onFileDiagnostics;
|
|
4860
5177
|
this.#cancellationToken = options.cancellationToken;
|
|
4861
5178
|
this.#hasOnly = options.hasOnly || resolvedConfig.only != null || options.position != null;
|
|
4862
5179
|
this.#position = options.position;
|
|
4863
5180
|
const reject = new Reject(compiler, typeChecker, resolvedConfig);
|
|
4864
5181
|
this.#expectService = new ExpectService(compiler, typeChecker, reject);
|
|
4865
|
-
this.#whenService = new WhenService(reject,
|
|
5182
|
+
this.#whenService = new WhenService(reject, onFileDiagnostics);
|
|
4866
5183
|
}
|
|
4867
|
-
async #resolveRunMode(
|
|
4868
|
-
const
|
|
4869
|
-
const inlineConfig = await Directive.getInlineConfig(
|
|
5184
|
+
async #resolveRunMode(flags, node) {
|
|
5185
|
+
const ifDirective = Directive.getDirectiveRange(this.#compiler, node, "if");
|
|
5186
|
+
const inlineConfig = await Directive.getInlineConfig(ifDirective);
|
|
4870
5187
|
if (inlineConfig?.if?.target != null && !Version.isIncluded(this.#compiler.version, inlineConfig.if.target)) {
|
|
4871
|
-
|
|
4872
|
-
}
|
|
4873
|
-
if (node.flags & TestTreeNodeFlags.Fail) {
|
|
4874
|
-
mode |= RunMode.Fail;
|
|
5188
|
+
flags |= 8;
|
|
4875
5189
|
}
|
|
4876
|
-
if (node.flags &
|
|
5190
|
+
if (node.flags & 1 ||
|
|
4877
5191
|
(this.#resolvedConfig.only != null && node.name.toLowerCase().includes(this.#resolvedConfig.only.toLowerCase()))) {
|
|
4878
|
-
|
|
5192
|
+
flags |= 1;
|
|
4879
5193
|
}
|
|
4880
|
-
if (node.flags &
|
|
5194
|
+
if (node.flags & 2 ||
|
|
4881
5195
|
(this.#resolvedConfig.skip != null && node.name.toLowerCase().includes(this.#resolvedConfig.skip.toLowerCase()))) {
|
|
4882
|
-
|
|
5196
|
+
flags |= 2;
|
|
4883
5197
|
}
|
|
4884
|
-
if (node.flags &
|
|
4885
|
-
|
|
5198
|
+
if (node.flags & 4) {
|
|
5199
|
+
flags |= 4;
|
|
4886
5200
|
}
|
|
4887
5201
|
if (this.#position != null && node.node.getStart() === this.#position) {
|
|
4888
|
-
|
|
4889
|
-
|
|
5202
|
+
flags |= 1;
|
|
5203
|
+
flags &= -3;
|
|
4890
5204
|
}
|
|
4891
|
-
return
|
|
5205
|
+
return flags;
|
|
4892
5206
|
}
|
|
4893
|
-
async visit(nodes,
|
|
5207
|
+
async visit(nodes, runModeFlags, parentResult) {
|
|
4894
5208
|
for (const node of nodes) {
|
|
4895
5209
|
if (this.#cancellationToken?.isCancellationRequested) {
|
|
4896
5210
|
break;
|
|
4897
5211
|
}
|
|
5212
|
+
const fixmeDirective = Directive.getDirectiveRange(this.#compiler, node, "fixme");
|
|
5213
|
+
if (fixmeDirective) {
|
|
5214
|
+
FixmeService.start(fixmeDirective, node);
|
|
5215
|
+
}
|
|
4898
5216
|
switch (node.brand) {
|
|
4899
|
-
case
|
|
4900
|
-
await this.#visitDescribe(node,
|
|
5217
|
+
case "describe":
|
|
5218
|
+
await this.#visitDescribe(node, runModeFlags, parentResult);
|
|
4901
5219
|
break;
|
|
4902
|
-
case
|
|
4903
|
-
await this.#visitTest(node,
|
|
5220
|
+
case "test":
|
|
5221
|
+
await this.#visitTest(node, runModeFlags, parentResult);
|
|
4904
5222
|
break;
|
|
4905
|
-
case
|
|
4906
|
-
await this.#
|
|
5223
|
+
case "expect":
|
|
5224
|
+
await this.#visitExpect(node, runModeFlags, parentResult);
|
|
4907
5225
|
break;
|
|
4908
|
-
case
|
|
5226
|
+
case "when":
|
|
4909
5227
|
this.#visitWhen(node);
|
|
4910
5228
|
break;
|
|
4911
5229
|
}
|
|
5230
|
+
if (fixmeDirective) {
|
|
5231
|
+
FixmeService.end(fixmeDirective, node, this.#onFileDiagnostics);
|
|
5232
|
+
}
|
|
4912
5233
|
}
|
|
4913
5234
|
}
|
|
4914
|
-
async #
|
|
4915
|
-
await this.visit(
|
|
4916
|
-
|
|
4917
|
-
if (
|
|
5235
|
+
async #visitExpect(expect, runModeFlags, parentResult) {
|
|
5236
|
+
await this.visit(expect.children, runModeFlags, parentResult);
|
|
5237
|
+
runModeFlags = await this.#resolveRunMode(runModeFlags, expect);
|
|
5238
|
+
if (runModeFlags & 8) {
|
|
4918
5239
|
return;
|
|
4919
5240
|
}
|
|
4920
|
-
const expectResult = new ExpectResult(
|
|
5241
|
+
const expectResult = new ExpectResult(expect, parentResult);
|
|
4921
5242
|
EventEmitter.dispatch(["expect:start", { result: expectResult }]);
|
|
4922
|
-
if (
|
|
5243
|
+
if (runModeFlags & 2 || (this.#hasOnly && !(runModeFlags & 1))) {
|
|
4923
5244
|
EventEmitter.dispatch(["expect:skip", { result: expectResult }]);
|
|
4924
5245
|
return;
|
|
4925
5246
|
}
|
|
@@ -4929,59 +5250,57 @@ class TestTreeWalker {
|
|
|
4929
5250
|
{ diagnostics: Array.isArray(diagnostics) ? diagnostics : [diagnostics], result: expectResult },
|
|
4930
5251
|
]);
|
|
4931
5252
|
};
|
|
4932
|
-
if (
|
|
4933
|
-
onExpectDiagnostics(Diagnostic.fromDiagnostics([...
|
|
5253
|
+
if (expect.diagnostics.size > 0 && expect.matcherNameNode.name.text !== "toRaiseError") {
|
|
5254
|
+
onExpectDiagnostics(Diagnostic.fromDiagnostics([...expect.diagnostics]));
|
|
4934
5255
|
return;
|
|
4935
5256
|
}
|
|
4936
|
-
const matchResult = this.#expectService.match(
|
|
5257
|
+
const matchResult = this.#expectService.match(expect, onExpectDiagnostics);
|
|
4937
5258
|
if (!matchResult) {
|
|
4938
5259
|
return;
|
|
4939
5260
|
}
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
onExpectDiagnostics(Diagnostic.error(text, origin));
|
|
4945
|
-
}
|
|
4946
|
-
else {
|
|
4947
|
-
EventEmitter.dispatch(["expect:pass", { result: expectResult }]);
|
|
4948
|
-
}
|
|
5261
|
+
const isPass = expect.isNot ? !matchResult.isMatch : matchResult.isMatch;
|
|
5262
|
+
if (FixmeService.isFixme(expect, isPass)) {
|
|
5263
|
+
EventEmitter.dispatch(["expect:fixme", { result: expectResult }]);
|
|
5264
|
+
return;
|
|
4949
5265
|
}
|
|
4950
|
-
|
|
5266
|
+
if (isPass) {
|
|
4951
5267
|
EventEmitter.dispatch(["expect:pass", { result: expectResult }]);
|
|
4952
5268
|
}
|
|
4953
5269
|
else {
|
|
4954
5270
|
EventEmitter.dispatch(["expect:fail", { diagnostics: matchResult.explain(), result: expectResult }]);
|
|
4955
5271
|
}
|
|
4956
5272
|
}
|
|
4957
|
-
async #visitDescribe(describe,
|
|
4958
|
-
|
|
4959
|
-
if (
|
|
5273
|
+
async #visitDescribe(describe, runModeFlags, parentResult) {
|
|
5274
|
+
runModeFlags = await this.#resolveRunMode(runModeFlags, describe);
|
|
5275
|
+
if (runModeFlags & 8) {
|
|
4960
5276
|
return;
|
|
4961
5277
|
}
|
|
4962
5278
|
const describeResult = new DescribeResult(describe, parentResult);
|
|
4963
5279
|
EventEmitter.dispatch(["describe:start", { result: describeResult }]);
|
|
4964
|
-
if (!(
|
|
5280
|
+
if (!(runModeFlags & 2 ||
|
|
5281
|
+
(this.#hasOnly && !(runModeFlags & 1)) ||
|
|
5282
|
+
runModeFlags & 4) &&
|
|
4965
5283
|
describe.diagnostics.size > 0) {
|
|
4966
|
-
this.#
|
|
5284
|
+
this.#onFileDiagnostics(Diagnostic.fromDiagnostics([...describe.diagnostics]));
|
|
4967
5285
|
}
|
|
4968
5286
|
else {
|
|
4969
|
-
await this.visit(describe.children,
|
|
5287
|
+
await this.visit(describe.children, runModeFlags, describeResult);
|
|
4970
5288
|
}
|
|
4971
5289
|
EventEmitter.dispatch(["describe:end", { result: describeResult }]);
|
|
4972
5290
|
}
|
|
4973
|
-
async #visitTest(test,
|
|
4974
|
-
|
|
4975
|
-
if (
|
|
5291
|
+
async #visitTest(test, runModeFlags, parentResult) {
|
|
5292
|
+
runModeFlags = await this.#resolveRunMode(runModeFlags, test);
|
|
5293
|
+
if (runModeFlags & 8) {
|
|
4976
5294
|
return;
|
|
4977
5295
|
}
|
|
4978
5296
|
const testResult = new TestResult(test, parentResult);
|
|
4979
5297
|
EventEmitter.dispatch(["test:start", { result: testResult }]);
|
|
4980
|
-
if (
|
|
5298
|
+
if (runModeFlags & 4) {
|
|
4981
5299
|
EventEmitter.dispatch(["test:todo", { result: testResult }]);
|
|
4982
5300
|
return;
|
|
4983
5301
|
}
|
|
4984
|
-
if (!(
|
|
5302
|
+
if (!(runModeFlags & 2 || (this.#hasOnly && !(runModeFlags & 1))) &&
|
|
5303
|
+
test.diagnostics.size > 0) {
|
|
4985
5304
|
EventEmitter.dispatch([
|
|
4986
5305
|
"test:error",
|
|
4987
5306
|
{
|
|
@@ -4991,24 +5310,29 @@ class TestTreeWalker {
|
|
|
4991
5310
|
]);
|
|
4992
5311
|
return;
|
|
4993
5312
|
}
|
|
4994
|
-
await this.visit(test.children,
|
|
4995
|
-
if (
|
|
5313
|
+
await this.visit(test.children, runModeFlags, testResult);
|
|
5314
|
+
if (runModeFlags & 2 || (this.#hasOnly && !(runModeFlags & 1))) {
|
|
4996
5315
|
EventEmitter.dispatch(["test:skip", { result: testResult }]);
|
|
4997
5316
|
return;
|
|
4998
5317
|
}
|
|
4999
|
-
|
|
5000
|
-
|
|
5318
|
+
const isPass = testResult.assertionCounts.failed === 0;
|
|
5319
|
+
if (FixmeService.isFixme(test, isPass)) {
|
|
5320
|
+
EventEmitter.dispatch(["test:fixme", { result: testResult }]);
|
|
5321
|
+
return;
|
|
5001
5322
|
}
|
|
5002
|
-
|
|
5323
|
+
if (isPass) {
|
|
5003
5324
|
EventEmitter.dispatch(["test:pass", { result: testResult }]);
|
|
5004
5325
|
}
|
|
5326
|
+
else {
|
|
5327
|
+
EventEmitter.dispatch(["test:fail", { result: testResult }]);
|
|
5328
|
+
}
|
|
5005
5329
|
}
|
|
5006
5330
|
#visitWhen(when) {
|
|
5007
5331
|
this.#whenService.action(when);
|
|
5008
5332
|
}
|
|
5009
5333
|
}
|
|
5010
5334
|
|
|
5011
|
-
class
|
|
5335
|
+
class FileRunner {
|
|
5012
5336
|
#collectService;
|
|
5013
5337
|
#compiler;
|
|
5014
5338
|
#projectService;
|
|
@@ -5021,89 +5345,85 @@ class TaskRunner {
|
|
|
5021
5345
|
this.#collectService = new CollectService(compiler, this.#projectService, this.#resolvedConfig);
|
|
5022
5346
|
}
|
|
5023
5347
|
#onDiagnostics(diagnostics, result) {
|
|
5024
|
-
EventEmitter.dispatch(["
|
|
5348
|
+
EventEmitter.dispatch(["file:error", { diagnostics, result }]);
|
|
5025
5349
|
}
|
|
5026
|
-
async run(
|
|
5350
|
+
async run(file, cancellationToken) {
|
|
5027
5351
|
if (cancellationToken.isCancellationRequested) {
|
|
5028
5352
|
return;
|
|
5029
5353
|
}
|
|
5030
|
-
this.#projectService.openFile(
|
|
5031
|
-
const
|
|
5032
|
-
EventEmitter.dispatch(["
|
|
5033
|
-
await this.#run(
|
|
5034
|
-
EventEmitter.dispatch(["
|
|
5035
|
-
this.#projectService.closeFile(
|
|
5354
|
+
this.#projectService.openFile(file.path);
|
|
5355
|
+
const fileResult = new FileResult(file);
|
|
5356
|
+
EventEmitter.dispatch(["file:start", { result: fileResult }]);
|
|
5357
|
+
await this.#run(file, fileResult, cancellationToken);
|
|
5358
|
+
EventEmitter.dispatch(["file:end", { result: fileResult }]);
|
|
5359
|
+
this.#projectService.closeFile(file.path);
|
|
5036
5360
|
}
|
|
5037
|
-
async #
|
|
5038
|
-
const languageService = this.#projectService.getLanguageService(
|
|
5039
|
-
const syntacticDiagnostics = languageService?.getSyntacticDiagnostics(
|
|
5361
|
+
async #resolveFileFacts(file, fileResult, runModeFlags) {
|
|
5362
|
+
const languageService = this.#projectService.getLanguageService(file.path);
|
|
5363
|
+
const syntacticDiagnostics = languageService?.getSyntacticDiagnostics(file.path);
|
|
5040
5364
|
if (syntacticDiagnostics != null && syntacticDiagnostics.length > 0) {
|
|
5041
|
-
this.#onDiagnostics(Diagnostic.fromDiagnostics(syntacticDiagnostics),
|
|
5365
|
+
this.#onDiagnostics(Diagnostic.fromDiagnostics(syntacticDiagnostics), fileResult);
|
|
5042
5366
|
return;
|
|
5043
5367
|
}
|
|
5044
|
-
const semanticDiagnostics = languageService?.getSemanticDiagnostics(
|
|
5368
|
+
const semanticDiagnostics = languageService?.getSemanticDiagnostics(file.path);
|
|
5045
5369
|
const program = languageService?.getProgram();
|
|
5046
5370
|
const typeChecker = program?.getTypeChecker();
|
|
5047
|
-
const sourceFile = program?.getSourceFile(
|
|
5371
|
+
const sourceFile = program?.getSourceFile(file.path);
|
|
5048
5372
|
if (!sourceFile) {
|
|
5049
5373
|
return;
|
|
5050
5374
|
}
|
|
5051
|
-
const
|
|
5052
|
-
const directiveRanges = testTree.getDirectiveRanges(this.#compiler);
|
|
5375
|
+
const directiveRanges = Directive.getDirectiveRanges(this.#compiler, sourceFile);
|
|
5053
5376
|
const inlineConfig = await Directive.getInlineConfig(directiveRanges);
|
|
5054
5377
|
if (inlineConfig?.if?.target != null && !Version.isIncluded(this.#compiler.version, inlineConfig.if.target)) {
|
|
5055
|
-
|
|
5056
|
-
}
|
|
5057
|
-
if (testTree.suppressedErrors != null) {
|
|
5058
|
-
this.#suppressedService.match(testTree.suppressedErrors, (diagnostics) => {
|
|
5059
|
-
this.#onDiagnostics(diagnostics, taskResult);
|
|
5060
|
-
});
|
|
5378
|
+
runModeFlags |= 2;
|
|
5061
5379
|
}
|
|
5062
5380
|
if (inlineConfig?.template) {
|
|
5063
5381
|
if (semanticDiagnostics != null && semanticDiagnostics.length > 0) {
|
|
5064
|
-
this.#onDiagnostics(Diagnostic.fromDiagnostics(semanticDiagnostics),
|
|
5382
|
+
this.#onDiagnostics(Diagnostic.fromDiagnostics(semanticDiagnostics), fileResult);
|
|
5065
5383
|
return;
|
|
5066
5384
|
}
|
|
5067
|
-
const moduleSpecifier = pathToFileURL(
|
|
5385
|
+
const moduleSpecifier = pathToFileURL(file.path).toString();
|
|
5068
5386
|
const testText = (await import(moduleSpecifier))?.default;
|
|
5069
5387
|
if (typeof testText !== "string") {
|
|
5070
|
-
this.#onDiagnostics([Diagnostic.error("A template test file must export a string.")],
|
|
5388
|
+
this.#onDiagnostics([Diagnostic.error("A template test file must export a string.")], fileResult);
|
|
5071
5389
|
return;
|
|
5072
5390
|
}
|
|
5073
|
-
this.#projectService.openFile(
|
|
5074
|
-
return this.#
|
|
5391
|
+
this.#projectService.openFile(file.path, testText);
|
|
5392
|
+
return this.#resolveFileFacts(file, fileResult, runModeFlags);
|
|
5075
5393
|
}
|
|
5076
|
-
|
|
5394
|
+
const testTree = this.#collectService.createTestTree(sourceFile, semanticDiagnostics);
|
|
5395
|
+
this.#suppressedService.match(testTree);
|
|
5396
|
+
return { runModeFlags, testTree, typeChecker };
|
|
5077
5397
|
}
|
|
5078
|
-
async #run(
|
|
5079
|
-
if (!existsSync(
|
|
5080
|
-
this.#onDiagnostics([Diagnostic.error(`Test file '${
|
|
5398
|
+
async #run(file, fileResult, cancellationToken) {
|
|
5399
|
+
if (!existsSync(file.path)) {
|
|
5400
|
+
this.#onDiagnostics([Diagnostic.error(`Test file '${file.path}' does not exist.`)], fileResult);
|
|
5081
5401
|
return;
|
|
5082
5402
|
}
|
|
5083
|
-
const facts = await this.#
|
|
5403
|
+
const facts = await this.#resolveFileFacts(file, fileResult, 0);
|
|
5084
5404
|
if (!facts) {
|
|
5085
5405
|
return;
|
|
5086
5406
|
}
|
|
5087
5407
|
if (facts.testTree.diagnostics.size > 0) {
|
|
5088
|
-
this.#onDiagnostics(Diagnostic.fromDiagnostics([...facts.testTree.diagnostics]),
|
|
5408
|
+
this.#onDiagnostics(Diagnostic.fromDiagnostics([...facts.testTree.diagnostics]), fileResult);
|
|
5089
5409
|
return;
|
|
5090
5410
|
}
|
|
5091
|
-
const
|
|
5092
|
-
this.#onDiagnostics(diagnostics,
|
|
5411
|
+
const onFileDiagnostics = (diagnostics) => {
|
|
5412
|
+
this.#onDiagnostics(diagnostics, fileResult);
|
|
5093
5413
|
};
|
|
5094
|
-
const testTreeWalker = new TestTreeWalker(this.#compiler, facts.typeChecker, this.#resolvedConfig,
|
|
5414
|
+
const testTreeWalker = new TestTreeWalker(this.#compiler, facts.typeChecker, this.#resolvedConfig, onFileDiagnostics, {
|
|
5095
5415
|
cancellationToken,
|
|
5096
5416
|
hasOnly: facts.testTree.hasOnly,
|
|
5097
|
-
position:
|
|
5417
|
+
position: file.position,
|
|
5098
5418
|
});
|
|
5099
|
-
await testTreeWalker.visit(facts.testTree.children, facts.
|
|
5419
|
+
await testTreeWalker.visit(facts.testTree.children, facts.runModeFlags, undefined);
|
|
5100
5420
|
}
|
|
5101
5421
|
}
|
|
5102
5422
|
|
|
5103
5423
|
class Runner {
|
|
5104
5424
|
#eventEmitter = new EventEmitter();
|
|
5105
5425
|
#resolvedConfig;
|
|
5106
|
-
static version = "
|
|
5426
|
+
static version = "5.0.0-beta.1";
|
|
5107
5427
|
constructor(resolvedConfig) {
|
|
5108
5428
|
this.#resolvedConfig = resolvedConfig;
|
|
5109
5429
|
}
|
|
@@ -5111,7 +5431,7 @@ class Runner {
|
|
|
5111
5431
|
const resultHandler = new ResultHandler();
|
|
5112
5432
|
this.#eventEmitter.addHandler(resultHandler);
|
|
5113
5433
|
if (this.#resolvedConfig.failFast) {
|
|
5114
|
-
const cancellationHandler = new CancellationHandler(cancellationToken,
|
|
5434
|
+
const cancellationHandler = new CancellationHandler(cancellationToken, "failFast");
|
|
5115
5435
|
this.#eventEmitter.addHandler(cancellationHandler);
|
|
5116
5436
|
}
|
|
5117
5437
|
}
|
|
@@ -5140,39 +5460,39 @@ class Runner {
|
|
|
5140
5460
|
}
|
|
5141
5461
|
}
|
|
5142
5462
|
}
|
|
5143
|
-
async run(
|
|
5144
|
-
const
|
|
5463
|
+
async run(files, cancellationToken = new CancellationToken()) {
|
|
5464
|
+
const fileLocations = files.map((file) => (file instanceof FileLocation ? file : new FileLocation(file)));
|
|
5145
5465
|
this.#addHandlers(cancellationToken);
|
|
5146
5466
|
await this.#addReporters();
|
|
5147
|
-
await this.#run(
|
|
5467
|
+
await this.#run(fileLocations, cancellationToken);
|
|
5148
5468
|
if (this.#resolvedConfig.watch) {
|
|
5149
|
-
await this.#watch(
|
|
5469
|
+
await this.#watch(fileLocations, cancellationToken);
|
|
5150
5470
|
}
|
|
5151
5471
|
this.#eventEmitter.removeReporters();
|
|
5152
5472
|
this.#eventEmitter.removeHandlers();
|
|
5153
5473
|
}
|
|
5154
|
-
async #run(
|
|
5155
|
-
const result = new Result(
|
|
5474
|
+
async #run(files, cancellationToken) {
|
|
5475
|
+
const result = new Result(files);
|
|
5156
5476
|
EventEmitter.dispatch(["run:start", { result }]);
|
|
5157
5477
|
for (const target of this.#resolvedConfig.target) {
|
|
5158
|
-
const targetResult = new TargetResult(target,
|
|
5478
|
+
const targetResult = new TargetResult(target, files);
|
|
5159
5479
|
EventEmitter.dispatch(["target:start", { result: targetResult }]);
|
|
5160
5480
|
const compiler = await Store.load(target);
|
|
5161
5481
|
if (compiler) {
|
|
5162
|
-
const
|
|
5163
|
-
for (const
|
|
5164
|
-
await
|
|
5482
|
+
const fileRunner = new FileRunner(compiler, this.#resolvedConfig);
|
|
5483
|
+
for (const file of files) {
|
|
5484
|
+
await fileRunner.run(file, cancellationToken);
|
|
5165
5485
|
}
|
|
5166
5486
|
}
|
|
5167
5487
|
EventEmitter.dispatch(["target:end", { result: targetResult }]);
|
|
5168
5488
|
}
|
|
5169
5489
|
EventEmitter.dispatch(["run:end", { result }]);
|
|
5170
|
-
if (cancellationToken.reason ===
|
|
5490
|
+
if (cancellationToken.reason === "failFast") {
|
|
5171
5491
|
cancellationToken.reset();
|
|
5172
5492
|
}
|
|
5173
5493
|
}
|
|
5174
|
-
async #watch(
|
|
5175
|
-
const watchService = new WatchService(this.#resolvedConfig,
|
|
5494
|
+
async #watch(files, cancellationToken) {
|
|
5495
|
+
const watchService = new WatchService(this.#resolvedConfig, files);
|
|
5176
5496
|
for await (const testFiles of watchService.watch(cancellationToken)) {
|
|
5177
5497
|
await this.#run(testFiles, cancellationToken);
|
|
5178
5498
|
}
|
|
@@ -5182,14 +5502,14 @@ class Runner {
|
|
|
5182
5502
|
class Cli {
|
|
5183
5503
|
#eventEmitter = new EventEmitter();
|
|
5184
5504
|
async run(commandLine, cancellationToken = new CancellationToken()) {
|
|
5185
|
-
const cancellationHandler = new CancellationHandler(cancellationToken,
|
|
5505
|
+
const cancellationHandler = new CancellationHandler(cancellationToken, "configError");
|
|
5186
5506
|
this.#eventEmitter.addHandler(cancellationHandler);
|
|
5187
5507
|
const exitCodeHandler = new ExitCodeHandler();
|
|
5188
5508
|
this.#eventEmitter.addHandler(exitCodeHandler);
|
|
5189
5509
|
const setupReporter = new SetupReporter();
|
|
5190
5510
|
this.#eventEmitter.addReporter(setupReporter);
|
|
5191
5511
|
if (commandLine.includes("--help")) {
|
|
5192
|
-
const options = Options.for(
|
|
5512
|
+
const options = Options.for(2);
|
|
5193
5513
|
OutputService.writeMessage(helpText(options, Runner.version));
|
|
5194
5514
|
return;
|
|
5195
5515
|
}
|
|
@@ -5217,7 +5537,7 @@ class Cli {
|
|
|
5217
5537
|
return;
|
|
5218
5538
|
}
|
|
5219
5539
|
do {
|
|
5220
|
-
if (cancellationToken.reason ===
|
|
5540
|
+
if (cancellationToken.reason === "configChange") {
|
|
5221
5541
|
cancellationToken.reset();
|
|
5222
5542
|
exitCodeHandler.resetCode();
|
|
5223
5543
|
OutputService.clearTerminal();
|
|
@@ -5272,7 +5592,7 @@ class Cli {
|
|
|
5272
5592
|
const runner = new Runner(resolvedConfig);
|
|
5273
5593
|
await runner.run(testFiles, cancellationToken);
|
|
5274
5594
|
PluginService.removeHandlers();
|
|
5275
|
-
} while (cancellationToken.reason ===
|
|
5595
|
+
} while (cancellationToken.reason === "configChange");
|
|
5276
5596
|
this.#eventEmitter.removeHandlers();
|
|
5277
5597
|
}
|
|
5278
5598
|
#waitForChangedFiles(resolvedConfig, cancellationToken) {
|
|
@@ -5281,7 +5601,7 @@ class Cli {
|
|
|
5281
5601
|
cancellationToken.reset();
|
|
5282
5602
|
OutputService.writeMessage(waitingForFileChangesText());
|
|
5283
5603
|
const onChanged = () => {
|
|
5284
|
-
cancellationToken.cancel(
|
|
5604
|
+
cancellationToken.cancel("configChange");
|
|
5285
5605
|
for (const watcher of watchers) {
|
|
5286
5606
|
watcher.close();
|
|
5287
5607
|
}
|
|
@@ -5303,4 +5623,4 @@ class Cli {
|
|
|
5303
5623
|
}
|
|
5304
5624
|
}
|
|
5305
5625
|
|
|
5306
|
-
export {
|
|
5626
|
+
export { BaseReporter, CancellationReason, CancellationToken, Cli, Color, Config, ConfigDiagnosticText, DescribeResult, Diagnostic, DiagnosticCategory, DiagnosticOrigin, Directive, EventEmitter, ExpectResult, FileLocation, FileResult, Line, ListReporter, OptionBrand, OptionGroup, Options, OutputService, Path, PluginService, ProjectResult, Result, ResultStatus, Runner, Scribbler, Select, SelectDiagnosticText, SetupReporter, Store, SummaryReporter, SuppressedResult, TargetResult, TestResult, Text, Version, WatchReporter, addsPackageText, defaultOptions, describeNameText, diagnosticBelongsToNode, diagnosticText, environmentOptions, fileStatusText, fileViewText, formattedText, getDiagnosticMessageText, getTextSpanEnd, helpText, isDiagnosticWithLocation, summaryText, testNameText, usesCompilerText, waitingForFileChangesText, watchUsageText };
|