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