tstyche 7.1.0 → 7.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.d.ts +22 -5
- package/dist/api.js +578 -636
- package/package.json +7 -3
package/dist/api.js
CHANGED
|
@@ -77,43 +77,6 @@ class JsonNode {
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
class SourceService {
|
|
81
|
-
static #files = new Map();
|
|
82
|
-
static delete(filePath) {
|
|
83
|
-
SourceService.#files.delete(filePath);
|
|
84
|
-
}
|
|
85
|
-
static get(source) {
|
|
86
|
-
const file = SourceService.#files.get(source.fileName);
|
|
87
|
-
if (file != null) {
|
|
88
|
-
return file;
|
|
89
|
-
}
|
|
90
|
-
return source;
|
|
91
|
-
}
|
|
92
|
-
static set(source) {
|
|
93
|
-
SourceService.#files.set(source.fileName, source);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
class DiagnosticOrigin {
|
|
98
|
-
assertionNode;
|
|
99
|
-
end;
|
|
100
|
-
sourceFile;
|
|
101
|
-
start;
|
|
102
|
-
constructor(start, end, sourceFile, assertionNode) {
|
|
103
|
-
this.start = start;
|
|
104
|
-
this.end = end;
|
|
105
|
-
this.sourceFile = SourceService.get(sourceFile);
|
|
106
|
-
this.assertionNode = assertionNode;
|
|
107
|
-
}
|
|
108
|
-
static fromAssertion(assertionNode) {
|
|
109
|
-
const node = assertionNode.matcherNameNode.name;
|
|
110
|
-
return new DiagnosticOrigin(node.getStart(), node.getEnd(), node.getSourceFile(), assertionNode);
|
|
111
|
-
}
|
|
112
|
-
static fromNode(node, assertionNode) {
|
|
113
|
-
return new DiagnosticOrigin(node.getStart(), node.getEnd(), node.getSourceFile(), assertionNode);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
80
|
function diagnosticBelongsToNode(diagnostic, node) {
|
|
118
81
|
return diagnostic.start != null && diagnostic.start >= node.pos && diagnostic.start <= node.end;
|
|
119
82
|
}
|
|
@@ -131,6 +94,16 @@ function getDiagnosticMessageText(diagnostic) {
|
|
|
131
94
|
? diagnostic.messageText
|
|
132
95
|
: diagnosticMessageChainToText(diagnostic.messageText);
|
|
133
96
|
}
|
|
97
|
+
function getOffset(position, offsets) {
|
|
98
|
+
let diff = 0;
|
|
99
|
+
for (const offset of offsets) {
|
|
100
|
+
if (offset.position > position - diff) {
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
diff += offset.diff;
|
|
104
|
+
}
|
|
105
|
+
return diff;
|
|
106
|
+
}
|
|
134
107
|
function getTextSpanEnd(span) {
|
|
135
108
|
return span.start + span.length;
|
|
136
109
|
}
|
|
@@ -138,6 +111,29 @@ function isDiagnosticWithLocation(diagnostic) {
|
|
|
138
111
|
return diagnostic.file != null && diagnostic.start != null && diagnostic.length != null;
|
|
139
112
|
}
|
|
140
113
|
|
|
114
|
+
class DiagnosticOrigin {
|
|
115
|
+
assertionNode;
|
|
116
|
+
end;
|
|
117
|
+
sourceFile;
|
|
118
|
+
start;
|
|
119
|
+
constructor(start, end, sourceFile, assertionNode) {
|
|
120
|
+
this.start = start;
|
|
121
|
+
this.end = end;
|
|
122
|
+
this.sourceFile = sourceFile;
|
|
123
|
+
this.assertionNode = assertionNode;
|
|
124
|
+
}
|
|
125
|
+
static fromAbilityDiagnostic(diagnostic, assertionNode) {
|
|
126
|
+
return new DiagnosticOrigin(diagnostic.start, getTextSpanEnd(diagnostic), diagnostic.file, assertionNode);
|
|
127
|
+
}
|
|
128
|
+
static fromAssertion(assertionNode) {
|
|
129
|
+
const node = assertionNode.matcherNameNode.name;
|
|
130
|
+
return new DiagnosticOrigin(node.getStart(), node.getEnd(), node.getSourceFile(), assertionNode);
|
|
131
|
+
}
|
|
132
|
+
static fromNode(node, assertionNode) {
|
|
133
|
+
return new DiagnosticOrigin(node.getStart(), node.getEnd(), node.getSourceFile(), assertionNode);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
141
137
|
class Diagnostic {
|
|
142
138
|
category;
|
|
143
139
|
code;
|
|
@@ -190,6 +186,37 @@ var DiagnosticCategory;
|
|
|
190
186
|
DiagnosticCategory["Warning"] = "warning";
|
|
191
187
|
})(DiagnosticCategory || (DiagnosticCategory = {}));
|
|
192
188
|
|
|
189
|
+
class MappedDiagnostic {
|
|
190
|
+
category;
|
|
191
|
+
code;
|
|
192
|
+
file;
|
|
193
|
+
length;
|
|
194
|
+
messageText;
|
|
195
|
+
relatedInformation;
|
|
196
|
+
start;
|
|
197
|
+
constructor(sourceFile, diagnostic, offsets = []) {
|
|
198
|
+
this.file = sourceFile;
|
|
199
|
+
if (diagnostic.start != null) {
|
|
200
|
+
this.start = diagnostic.start - getOffset(diagnostic.start, offsets);
|
|
201
|
+
}
|
|
202
|
+
this.category = diagnostic.category;
|
|
203
|
+
this.code = diagnostic.code;
|
|
204
|
+
this.length = diagnostic.length;
|
|
205
|
+
this.messageText = diagnostic.messageText;
|
|
206
|
+
if ("relatedInformation" in diagnostic && Array.isArray(diagnostic.relatedInformation)) {
|
|
207
|
+
this.relatedInformation = [];
|
|
208
|
+
for (const related of diagnostic.relatedInformation) {
|
|
209
|
+
if (related.file?.fileName === sourceFile.fileName) {
|
|
210
|
+
this.relatedInformation.push(new MappedDiagnostic(sourceFile, related, offsets));
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
this.relatedInformation.push(related);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
193
220
|
class JsonScanner {
|
|
194
221
|
#end;
|
|
195
222
|
#position;
|
|
@@ -307,15 +334,20 @@ class JsonSourceFile {
|
|
|
307
334
|
const result = [0];
|
|
308
335
|
let position = 0;
|
|
309
336
|
while (position < this.text.length) {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
337
|
+
const character = this.text.charAt(position);
|
|
338
|
+
switch (character) {
|
|
339
|
+
case "\n":
|
|
340
|
+
result.push(position + 1);
|
|
341
|
+
break;
|
|
342
|
+
case "\r":
|
|
343
|
+
if (this.text.charAt(position + 1) === "\n") {
|
|
344
|
+
result.push(position + 2);
|
|
345
|
+
position++;
|
|
346
|
+
}
|
|
347
|
+
break;
|
|
315
348
|
}
|
|
316
349
|
position++;
|
|
317
350
|
}
|
|
318
|
-
result.push(position);
|
|
319
351
|
return result;
|
|
320
352
|
}
|
|
321
353
|
getLineStarts() {
|
|
@@ -417,15 +449,31 @@ class ConfigDiagnosticText {
|
|
|
417
449
|
class Environment {
|
|
418
450
|
static resolve() {
|
|
419
451
|
return {
|
|
452
|
+
fetchRetries: Environment.#resolveFetchRetries(),
|
|
453
|
+
fetchTimeout: Environment.#resolveFetchTimeout(),
|
|
420
454
|
isCi: Environment.#resolveIsCi(),
|
|
421
455
|
noColor: Environment.#resolveNoColor(),
|
|
422
456
|
noInteractive: Environment.#resolveNoInteractive(),
|
|
423
457
|
npmRegistry: Environment.#resolveNpmRegistry(),
|
|
424
458
|
storePath: Environment.#resolveStorePath(),
|
|
425
|
-
timeout: Environment.#resolveTimeout(),
|
|
426
459
|
typescriptModule: Environment.#resolveTypeScriptModule(),
|
|
427
460
|
};
|
|
428
461
|
}
|
|
462
|
+
static #resolveFetchRetries() {
|
|
463
|
+
if (process.env["TSTYCHE_FETCH_RETRIES"] != null) {
|
|
464
|
+
return Number(process.env["TSTYCHE_FETCH_RETRIES"]);
|
|
465
|
+
}
|
|
466
|
+
return 2;
|
|
467
|
+
}
|
|
468
|
+
static #resolveFetchTimeout() {
|
|
469
|
+
if (process.env["TSTYCHE_FETCH_TIMEOUT"] != null) {
|
|
470
|
+
return Number.parseFloat(process.env["TSTYCHE_FETCH_TIMEOUT"]);
|
|
471
|
+
}
|
|
472
|
+
if (process.env["TSTYCHE_TIMEOUT"] != null) {
|
|
473
|
+
return Number.parseFloat(process.env["TSTYCHE_TIMEOUT"]);
|
|
474
|
+
}
|
|
475
|
+
return 30;
|
|
476
|
+
}
|
|
429
477
|
static #resolveIsCi() {
|
|
430
478
|
if (process.env["CI"] != null) {
|
|
431
479
|
return process.env["CI"] !== "";
|
|
@@ -468,12 +516,6 @@ class Environment {
|
|
|
468
516
|
}
|
|
469
517
|
return Path.resolve(os.homedir(), ".local", "share", "TSTyche");
|
|
470
518
|
}
|
|
471
|
-
static #resolveTimeout() {
|
|
472
|
-
if (process.env["TSTYCHE_TIMEOUT"] != null) {
|
|
473
|
-
return Number.parseFloat(process.env["TSTYCHE_TIMEOUT"]);
|
|
474
|
-
}
|
|
475
|
-
return 30;
|
|
476
|
-
}
|
|
477
519
|
static #resolveTypeScriptModule() {
|
|
478
520
|
let specifier = "typescript";
|
|
479
521
|
if (process.env["TSTYCHE_TYPESCRIPT_MODULE"] != null) {
|
|
@@ -522,6 +564,10 @@ class Version {
|
|
|
522
564
|
}
|
|
523
565
|
}
|
|
524
566
|
|
|
567
|
+
function sleep(delay) {
|
|
568
|
+
return new Promise((resolve) => setTimeout(resolve, delay));
|
|
569
|
+
}
|
|
570
|
+
|
|
525
571
|
class StoreDiagnosticText {
|
|
526
572
|
static cannotAddTypeScriptPackage(tag) {
|
|
527
573
|
return `Cannot add the 'typescript' package for the '${tag}' tag.`;
|
|
@@ -535,13 +581,13 @@ class StoreDiagnosticText {
|
|
|
535
581
|
static failedToUpdateMetadata(registry) {
|
|
536
582
|
return `Failed to update metadata of the 'typescript' package from '${registry}'.`;
|
|
537
583
|
}
|
|
538
|
-
static maybeNetworkConnectionIssue() {
|
|
539
|
-
return "Might be there is an issue with the registry or the network connection.";
|
|
540
|
-
}
|
|
541
584
|
static maybeOutdatedResolution(tag) {
|
|
542
585
|
return `The resolution of the '${tag}' tag may be outdated.`;
|
|
543
586
|
}
|
|
544
|
-
static
|
|
587
|
+
static networkFailure(retries) {
|
|
588
|
+
return `The network connection failed after ${retries + 1} attempts.`;
|
|
589
|
+
}
|
|
590
|
+
static requestFailed(code) {
|
|
545
591
|
return `The request failed with status code ${code}.`;
|
|
546
592
|
}
|
|
547
593
|
static requestTimeoutWasExceeded(timeout) {
|
|
@@ -554,29 +600,48 @@ class StoreDiagnosticText {
|
|
|
554
600
|
|
|
555
601
|
class Fetcher {
|
|
556
602
|
#onDiagnostics;
|
|
603
|
+
#retries;
|
|
557
604
|
#timeout;
|
|
558
|
-
constructor(onDiagnostics, timeout) {
|
|
605
|
+
constructor(onDiagnostics, retries, timeout) {
|
|
559
606
|
this.#onDiagnostics = onDiagnostics;
|
|
607
|
+
this.#retries = retries;
|
|
560
608
|
this.#timeout = timeout;
|
|
561
609
|
}
|
|
562
610
|
async get(request, diagnostic, options) {
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
611
|
+
let delay = 3000;
|
|
612
|
+
request.headers.set("User-Agent", `tstyche/${"7.2.1"} ${process.platform} ${process.arch}`);
|
|
613
|
+
for (let attempt = 0; attempt <= this.#retries; attempt++) {
|
|
614
|
+
const isLastAttempt = attempt === this.#retries;
|
|
615
|
+
const suppressErrors = options?.suppressErrors || !isLastAttempt;
|
|
616
|
+
try {
|
|
617
|
+
const response = await fetch(request, { signal: AbortSignal.timeout(this.#timeout) });
|
|
618
|
+
if (response.ok) {
|
|
619
|
+
return response;
|
|
620
|
+
}
|
|
621
|
+
if (response.status >= 400 && response.status < 500 && response.status !== 429) {
|
|
622
|
+
if (options?.suppressErrors) {
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
this.#onDiagnostics(diagnostic().extendWith(StoreDiagnosticText.requestFailed(response.status)));
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
if (!suppressErrors) {
|
|
629
|
+
this.#onDiagnostics(diagnostic().extendWith(StoreDiagnosticText.requestFailed(response.status)));
|
|
630
|
+
}
|
|
569
631
|
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
632
|
+
catch (error) {
|
|
633
|
+
if (!suppressErrors) {
|
|
634
|
+
if (error instanceof Error && error.name === "TimeoutError") {
|
|
635
|
+
this.#onDiagnostics(diagnostic().extendWith(StoreDiagnosticText.requestTimeoutWasExceeded(this.#timeout)));
|
|
636
|
+
}
|
|
637
|
+
else {
|
|
638
|
+
this.#onDiagnostics(diagnostic().extendWith(StoreDiagnosticText.networkFailure(this.#retries)));
|
|
639
|
+
}
|
|
640
|
+
}
|
|
576
641
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
642
|
+
if (!isLastAttempt) {
|
|
643
|
+
await sleep(delay);
|
|
644
|
+
delay *= 2;
|
|
580
645
|
}
|
|
581
646
|
}
|
|
582
647
|
return;
|
|
@@ -620,17 +685,14 @@ class LockService {
|
|
|
620
685
|
const waitStartTime = Date.now();
|
|
621
686
|
while (isLocked) {
|
|
622
687
|
if (Date.now() - waitStartTime > this.#timeout) {
|
|
623
|
-
this.#onDiagnostics(diagnostic.extendWith(StoreDiagnosticText.lockWaitTimeoutWasExceeded(this.#timeout)));
|
|
688
|
+
this.#onDiagnostics(diagnostic().extendWith(StoreDiagnosticText.lockWaitTimeoutWasExceeded(this.#timeout)));
|
|
624
689
|
break;
|
|
625
690
|
}
|
|
626
|
-
await
|
|
691
|
+
await sleep(1000);
|
|
627
692
|
isLocked = existsSync(lockFilePath);
|
|
628
693
|
}
|
|
629
694
|
return isLocked;
|
|
630
695
|
}
|
|
631
|
-
#sleep(delay) {
|
|
632
|
-
return new Promise((resolve) => setTimeout(resolve, delay));
|
|
633
|
-
}
|
|
634
696
|
}
|
|
635
697
|
|
|
636
698
|
class Manifest {
|
|
@@ -711,7 +773,7 @@ class ManifestService {
|
|
|
711
773
|
return manifest;
|
|
712
774
|
}
|
|
713
775
|
async #load(options) {
|
|
714
|
-
const diagnostic = Diagnostic.error(StoreDiagnosticText.failedToFetchMetadata(this.#npmRegistry));
|
|
776
|
+
const diagnostic = () => Diagnostic.error(StoreDiagnosticText.failedToFetchMetadata(this.#npmRegistry));
|
|
715
777
|
const request = new Request(new URL("typescript", this.#npmRegistry), {
|
|
716
778
|
headers: {
|
|
717
779
|
["Accept"]: "application/vnd.npm.install-v1+json;q=1.0, application/json;q=0.8, */*",
|
|
@@ -848,7 +910,7 @@ class PackageService {
|
|
|
848
910
|
}
|
|
849
911
|
async ensure(packageVersion, manifest) {
|
|
850
912
|
const packagePath = Path.join(this.#storePath, `typescript@${packageVersion}`);
|
|
851
|
-
const diagnostic = Diagnostic.error(StoreDiagnosticText.failedToFetchPackage(packageVersion));
|
|
913
|
+
const diagnostic = () => Diagnostic.error(StoreDiagnosticText.failedToFetchPackage(packageVersion));
|
|
852
914
|
if (await this.#lockService.isLocked(packagePath, diagnostic)) {
|
|
853
915
|
return;
|
|
854
916
|
}
|
|
@@ -889,12 +951,13 @@ class Store {
|
|
|
889
951
|
static #manifestService;
|
|
890
952
|
static #packageService;
|
|
891
953
|
static #npmRegistry = environmentOptions.npmRegistry;
|
|
954
|
+
static #fetchRetries = environmentOptions.fetchRetries;
|
|
955
|
+
static #fetchTimeout = environmentOptions.fetchTimeout * 1000;
|
|
892
956
|
static #storePath = environmentOptions.storePath;
|
|
893
957
|
static #supportedTags;
|
|
894
|
-
static #timeout = environmentOptions.timeout * 1000;
|
|
895
958
|
static {
|
|
896
|
-
Store.#fetcher = new Fetcher(Store.#onDiagnostics, Store.#
|
|
897
|
-
Store.#lockService = new LockService(Store.#onDiagnostics, Store.#
|
|
959
|
+
Store.#fetcher = new Fetcher(Store.#onDiagnostics, Store.#fetchRetries, Store.#fetchTimeout);
|
|
960
|
+
Store.#lockService = new LockService(Store.#onDiagnostics, Store.#fetchTimeout);
|
|
898
961
|
Store.#packageService = new PackageService(Store.#storePath, Store.#fetcher, Store.#lockService);
|
|
899
962
|
Store.#manifestService = new ManifestService(Store.#storePath, Store.#npmRegistry, Store.#fetcher);
|
|
900
963
|
}
|
|
@@ -2255,8 +2318,8 @@ function CodeFrameText({ diagnosticCategory, diagnosticOrigin, options }) {
|
|
|
2255
2318
|
const linesBelow = options?.linesBelow ?? 3;
|
|
2256
2319
|
const showBreadcrumbs = options?.showBreadcrumbs ?? true;
|
|
2257
2320
|
const lineMap = diagnosticOrigin.sourceFile.getLineStarts();
|
|
2258
|
-
const { character:
|
|
2259
|
-
const { character:
|
|
2321
|
+
const { character: firstMarkedCharacter, line: firstMarkedLine } = diagnosticOrigin.sourceFile.getLineAndCharacterOfPosition(diagnosticOrigin.start);
|
|
2322
|
+
const { character: lastMarkedCharacter, line: lastMarkedLine } = diagnosticOrigin.sourceFile.getLineAndCharacterOfPosition(diagnosticOrigin.end);
|
|
2260
2323
|
const firstLine = Math.max(firstMarkedLine - linesAbove, 0);
|
|
2261
2324
|
const lastLine = Math.min(lastMarkedLine + linesBelow, lineMap.length - 1);
|
|
2262
2325
|
const gutterWidth = (lastLine + 1).toString().length + 2;
|
|
@@ -2278,12 +2341,12 @@ function CodeFrameText({ diagnosticCategory, diagnosticOrigin, options }) {
|
|
|
2278
2341
|
codeFrame.push(jsx(CodeLineText, { gutterWidth: gutterWidth, lineNumber: index + 1, lineNumberColor: highlightColor, lineText: lineText }));
|
|
2279
2342
|
if (index === firstMarkedLine) {
|
|
2280
2343
|
const squiggleLength = index === lastMarkedLine
|
|
2281
|
-
?
|
|
2282
|
-
: lineText.length -
|
|
2283
|
-
codeFrame.push(jsx(SquiggleLineText, { gutterWidth: gutterWidth, indentWidth:
|
|
2344
|
+
? lastMarkedCharacter - firstMarkedCharacter
|
|
2345
|
+
: lineText.length - firstMarkedCharacter;
|
|
2346
|
+
codeFrame.push(jsx(SquiggleLineText, { gutterWidth: gutterWidth, indentWidth: firstMarkedCharacter, squiggleColor: highlightColor, squiggleWidth: squiggleLength }));
|
|
2284
2347
|
}
|
|
2285
2348
|
else if (index === lastMarkedLine) {
|
|
2286
|
-
codeFrame.push(jsx(SquiggleLineText, { gutterWidth: gutterWidth, squiggleColor: highlightColor, squiggleWidth:
|
|
2349
|
+
codeFrame.push(jsx(SquiggleLineText, { gutterWidth: gutterWidth, squiggleColor: highlightColor, squiggleWidth: lastMarkedCharacter }));
|
|
2287
2350
|
}
|
|
2288
2351
|
else {
|
|
2289
2352
|
codeFrame.push(jsx(SquiggleLineText, { gutterWidth: gutterWidth, squiggleColor: highlightColor, squiggleWidth: lineText.length }));
|
|
@@ -2297,7 +2360,7 @@ function CodeFrameText({ diagnosticCategory, diagnosticOrigin, options }) {
|
|
|
2297
2360
|
if (showBreadcrumbs && diagnosticOrigin.assertionNode != null) {
|
|
2298
2361
|
breadcrumbs = jsx(BreadcrumbsText, { ancestor: diagnosticOrigin.assertionNode.parent });
|
|
2299
2362
|
}
|
|
2300
|
-
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}:${
|
|
2363
|
+
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}:${firstMarkedCharacter + 1}` }), breadcrumbs] }));
|
|
2301
2364
|
return (jsx(Text, { children: [codeFrame, jsx(Line, {}), location] }));
|
|
2302
2365
|
}
|
|
2303
2366
|
|
|
@@ -3160,7 +3223,7 @@ class Select {
|
|
|
3160
3223
|
if (testFilePaths.length === 0) {
|
|
3161
3224
|
Select.#onDiagnostics(Diagnostic.error(SelectDiagnosticText.noTestFilesWereSelected(resolvedConfig)));
|
|
3162
3225
|
}
|
|
3163
|
-
return testFilePaths.sort();
|
|
3226
|
+
return testFilePaths.sort((a, b) => a.localeCompare(b));
|
|
3164
3227
|
}
|
|
3165
3228
|
static async #visitDirectory(currentPath, testFilePaths, matchPatterns, resolvedConfig) {
|
|
3166
3229
|
const targetPath = Path.join(resolvedConfig.rootPath, currentPath);
|
|
@@ -3290,7 +3353,7 @@ class WatchService {
|
|
|
3290
3353
|
while (!cancellationToken.isCancellationRequested()) {
|
|
3291
3354
|
const testFiles = await debounce.schedule();
|
|
3292
3355
|
if (testFiles.length > 0) {
|
|
3293
|
-
yield testFiles;
|
|
3356
|
+
yield testFiles.sort((a, b) => a.path.localeCompare(b.path));
|
|
3294
3357
|
}
|
|
3295
3358
|
}
|
|
3296
3359
|
}
|
|
@@ -3335,11 +3398,10 @@ class AbilityLayer {
|
|
|
3335
3398
|
this.#editor = editor;
|
|
3336
3399
|
}
|
|
3337
3400
|
#belongsToNode(node, diagnostic) {
|
|
3338
|
-
return
|
|
3339
|
-
diagnosticBelongsToNode(diagnostic, node.source));
|
|
3401
|
+
return diagnosticBelongsToNode(diagnostic, node.matcherNode) || diagnosticBelongsToNode(diagnostic, node.source);
|
|
3340
3402
|
}
|
|
3341
3403
|
close(diagnostics) {
|
|
3342
|
-
if (diagnostics
|
|
3404
|
+
if (diagnostics.length > 0 && this.#nodes.length > 0) {
|
|
3343
3405
|
this.#nodes.reverse();
|
|
3344
3406
|
for (const diagnostic of diagnostics) {
|
|
3345
3407
|
this.#mapToNodes(diagnostic);
|
|
@@ -3362,12 +3424,63 @@ class AbilityLayer {
|
|
|
3362
3424
|
const matcherNameEnd = expect.matcherNameNode.getEnd();
|
|
3363
3425
|
const matcherNodeEnd = expect.matcherNode.getEnd();
|
|
3364
3426
|
switch (expect.matcherNameNode.name.text) {
|
|
3427
|
+
case "toAcceptProps": {
|
|
3428
|
+
this.#nodes.push(expect);
|
|
3429
|
+
const sourceNode = expect.source[0];
|
|
3430
|
+
const targetNode = expect.target?.[0];
|
|
3431
|
+
if (!sourceNode ||
|
|
3432
|
+
!targetNode ||
|
|
3433
|
+
!(this.#compiler.isIdentifier(sourceNode) ||
|
|
3434
|
+
this.#compiler.isPropertyAccessExpression(sourceNode) ||
|
|
3435
|
+
this.#compiler.isTypeReferenceNode(sourceNode) ||
|
|
3436
|
+
this.#compiler.isExpressionWithTypeArguments(sourceNode)) ||
|
|
3437
|
+
!/^[A-Z_$]/.test(sourceNode.getText()[0]) ||
|
|
3438
|
+
!this.#compiler.isObjectLiteralExpression(targetNode) ||
|
|
3439
|
+
!targetNode.properties.every((property) => (this.#compiler.isPropertyAssignment(property) &&
|
|
3440
|
+
(this.#compiler.isIdentifier(property.name) || this.#compiler.isStringLiteral(property.name))) ||
|
|
3441
|
+
this.#compiler.isSpreadAssignment(property))) {
|
|
3442
|
+
return;
|
|
3443
|
+
}
|
|
3444
|
+
const sourceText = sourceNode.getText();
|
|
3445
|
+
if (nodeBelongsToArgumentList(this.#compiler, sourceNode)) {
|
|
3446
|
+
this.#editor
|
|
3447
|
+
.eraseTrailingComma(expect.source)
|
|
3448
|
+
.erase(expectStart, matcherNodeEnd)
|
|
3449
|
+
.update(expectStart, matcherNameEnd, nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode) ? ";" : "")
|
|
3450
|
+
.update(sourceNode.getStart() - 1, sourceNode.getEnd(), `<${sourceText}`);
|
|
3451
|
+
}
|
|
3452
|
+
else {
|
|
3453
|
+
const id = ["SC", expectStart, Date.now().toString(36)].join("_");
|
|
3454
|
+
this.#editor
|
|
3455
|
+
.erase(expectStart, matcherNodeEnd)
|
|
3456
|
+
.update(expectStart, matcherNameEnd, nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode)
|
|
3457
|
+
? `;const ${id} = undefined as any as ${sourceText};<${id}`
|
|
3458
|
+
: `const ${id} = undefined as any as ${sourceText};<${id}`);
|
|
3459
|
+
}
|
|
3460
|
+
for (const property of targetNode.properties) {
|
|
3461
|
+
if (this.#compiler.isPropertyAssignment(property)) {
|
|
3462
|
+
this.#editor
|
|
3463
|
+
.update(property.name.getStart(), property.name.getEnd(), this.#compiler.isStringLiteral(property.name)
|
|
3464
|
+
? ` ${property.name.getText().slice(1, -1)} `
|
|
3465
|
+
: property.name.getText())
|
|
3466
|
+
.insert(property.initializer.getStart(), "={")
|
|
3467
|
+
.update(property.initializer.getStart(), property.initializer.getEnd(), property.initializer.getText())
|
|
3468
|
+
.insert(property.initializer.getEnd(), "}");
|
|
3469
|
+
continue;
|
|
3470
|
+
}
|
|
3471
|
+
if (this.#compiler.isSpreadAssignment(property)) {
|
|
3472
|
+
this.#editor
|
|
3473
|
+
.insert(property.getStart(), "{")
|
|
3474
|
+
.update(property.getStart(), property.getEnd(), property.getText())
|
|
3475
|
+
.insert(property.getEnd(), "}");
|
|
3476
|
+
}
|
|
3477
|
+
}
|
|
3478
|
+
this.#editor.insert(matcherNodeEnd, "/>");
|
|
3479
|
+
break;
|
|
3480
|
+
}
|
|
3365
3481
|
case "toBeApplicable":
|
|
3366
3482
|
this.#nodes.push(expect);
|
|
3367
|
-
this.#editor.
|
|
3368
|
-
[expectStart, expectExpressionEnd],
|
|
3369
|
-
[expectEnd, matcherNameEnd],
|
|
3370
|
-
]);
|
|
3483
|
+
this.#editor.erase(expectStart, expectExpressionEnd).erase(expectEnd, matcherNameEnd);
|
|
3371
3484
|
break;
|
|
3372
3485
|
case "toBeCallableWith": {
|
|
3373
3486
|
this.#nodes.push(expect);
|
|
@@ -3376,27 +3489,16 @@ class AbilityLayer {
|
|
|
3376
3489
|
return;
|
|
3377
3490
|
}
|
|
3378
3491
|
if (nodeBelongsToArgumentList(this.#compiler, sourceNode)) {
|
|
3379
|
-
this.#editor
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
expectExpressionEnd,
|
|
3384
|
-
nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode) ? ";" : "",
|
|
3385
|
-
],
|
|
3386
|
-
[expectEnd, matcherNameEnd],
|
|
3387
|
-
]);
|
|
3492
|
+
this.#editor
|
|
3493
|
+
.eraseTrailingComma(expect.source)
|
|
3494
|
+
.update(expectStart, expectExpressionEnd, nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode) ? ";" : "")
|
|
3495
|
+
.erase(expectEnd, matcherNameEnd);
|
|
3388
3496
|
}
|
|
3389
3497
|
else {
|
|
3390
3498
|
const sourceText = sourceNode.getFullText();
|
|
3391
|
-
this.#editor.
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
matcherNameEnd,
|
|
3395
|
-
nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode)
|
|
3396
|
-
? `;(undefined as any as ${sourceText})`
|
|
3397
|
-
: `(undefined as any as ${sourceText})`,
|
|
3398
|
-
],
|
|
3399
|
-
]);
|
|
3499
|
+
this.#editor.update(expectStart, matcherNameEnd, nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode)
|
|
3500
|
+
? `;(undefined as any as ${sourceText})`
|
|
3501
|
+
: `(undefined as any as ${sourceText})`);
|
|
3400
3502
|
}
|
|
3401
3503
|
break;
|
|
3402
3504
|
}
|
|
@@ -3407,27 +3509,16 @@ class AbilityLayer {
|
|
|
3407
3509
|
return;
|
|
3408
3510
|
}
|
|
3409
3511
|
if (nodeBelongsToArgumentList(this.#compiler, sourceNode)) {
|
|
3410
|
-
this.#editor
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
expectExpressionEnd,
|
|
3415
|
-
nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode) ? "; new" : "new",
|
|
3416
|
-
],
|
|
3417
|
-
[expectEnd, matcherNameEnd],
|
|
3418
|
-
]);
|
|
3512
|
+
this.#editor
|
|
3513
|
+
.eraseTrailingComma(expect.source)
|
|
3514
|
+
.update(expectStart, expectExpressionEnd, nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode) ? "; new" : "new")
|
|
3515
|
+
.erase(expectEnd, matcherNameEnd);
|
|
3419
3516
|
}
|
|
3420
3517
|
else {
|
|
3421
3518
|
const sourceText = sourceNode.getFullText();
|
|
3422
|
-
this.#editor.
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
matcherNameEnd,
|
|
3426
|
-
nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode)
|
|
3427
|
-
? `;new (undefined as any as ${sourceText})`
|
|
3428
|
-
: `new (undefined as any as ${sourceText})`,
|
|
3429
|
-
],
|
|
3430
|
-
]);
|
|
3519
|
+
this.#editor.update(expectStart, matcherNameEnd, nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode)
|
|
3520
|
+
? `;new (undefined as any as ${sourceText})`
|
|
3521
|
+
: `new (undefined as any as ${sourceText})`);
|
|
3431
3522
|
}
|
|
3432
3523
|
break;
|
|
3433
3524
|
}
|
|
@@ -3439,39 +3530,26 @@ class AbilityLayer {
|
|
|
3439
3530
|
return;
|
|
3440
3531
|
}
|
|
3441
3532
|
if (nodeBelongsToArgumentList(this.#compiler, sourceNode)) {
|
|
3442
|
-
this.#editor
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
expectExpressionEnd,
|
|
3447
|
-
nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode) ? ";" : "",
|
|
3448
|
-
],
|
|
3449
|
-
[expectEnd, matcherNodeEnd],
|
|
3450
|
-
]);
|
|
3533
|
+
this.#editor
|
|
3534
|
+
.eraseTrailingComma(expect.source)
|
|
3535
|
+
.update(expectStart, expectExpressionEnd, nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode) ? ";" : "")
|
|
3536
|
+
.erase(expectEnd, matcherNodeEnd);
|
|
3451
3537
|
if (this.#compiler.isExpressionWithTypeArguments(sourceNode)) {
|
|
3452
|
-
this.#editor.
|
|
3538
|
+
this.#editor.erase(sourceNode.expression.getEnd(), sourceNode.getEnd());
|
|
3453
3539
|
}
|
|
3454
3540
|
}
|
|
3455
3541
|
else {
|
|
3456
3542
|
const sourceText = this.#compiler.isTypeReferenceNode(sourceNode)
|
|
3457
|
-
? sourceNode.typeName.
|
|
3458
|
-
: sourceNode.
|
|
3459
|
-
this.#editor.
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
matcherNodeEnd,
|
|
3463
|
-
nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode)
|
|
3464
|
-
? `;undefined as any as ${sourceText}`
|
|
3465
|
-
: `undefined as any as ${sourceText}`,
|
|
3466
|
-
],
|
|
3467
|
-
]);
|
|
3543
|
+
? sourceNode.typeName.getFullText()
|
|
3544
|
+
: sourceNode.getFullText();
|
|
3545
|
+
this.#editor.update(expectStart, matcherNodeEnd, nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode)
|
|
3546
|
+
? `;undefined as any as ${sourceText}`
|
|
3547
|
+
: `undefined as any as ${sourceText}`);
|
|
3468
3548
|
}
|
|
3469
3549
|
if (targetNode != null) {
|
|
3470
3550
|
const targetText = targetNode.getText().slice(1, -1);
|
|
3471
|
-
if (targetText.trim().length >
|
|
3472
|
-
this.#editor.
|
|
3473
|
-
[targetNode.getFullStart(), targetNode.getEnd(), `<${targetText}>`.padStart(targetNode.getFullWidth())],
|
|
3474
|
-
]);
|
|
3551
|
+
if (targetText.trim().length > 0) {
|
|
3552
|
+
this.#editor.update(targetNode.getFullStart(), targetNode.getEnd(), `<${targetText}>`.padStart(targetNode.getFullWidth()));
|
|
3475
3553
|
}
|
|
3476
3554
|
}
|
|
3477
3555
|
break;
|
|
@@ -3483,30 +3561,21 @@ class AbilityLayer {
|
|
|
3483
3561
|
if (!sourceNode || !targetNode) {
|
|
3484
3562
|
return;
|
|
3485
3563
|
}
|
|
3486
|
-
const sourceText = sourceNode.getFullText();
|
|
3487
|
-
const targetText = targetNode.getFullText();
|
|
3488
3564
|
if (nodeBelongsToArgumentList(this.#compiler, sourceNode)) {
|
|
3489
|
-
this.#editor
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
matcherNodeEnd,
|
|
3494
|
-
nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode)
|
|
3495
|
-
? `;(${sourceText})[${targetText}]`
|
|
3496
|
-
: `(${sourceText})[${targetText}]`,
|
|
3497
|
-
],
|
|
3498
|
-
]);
|
|
3565
|
+
this.#editor
|
|
3566
|
+
.eraseTrailingComma(expect.source)
|
|
3567
|
+
.update(expectStart, expectExpressionEnd, nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode) ? ";" : "")
|
|
3568
|
+
.erase(expectEnd, matcherNodeEnd);
|
|
3499
3569
|
}
|
|
3500
3570
|
else {
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
]);
|
|
3571
|
+
const sourceText = sourceNode.getFullText();
|
|
3572
|
+
this.#editor.update(expectStart, matcherNodeEnd, nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode)
|
|
3573
|
+
? `;(undefined as any as ${sourceText})`
|
|
3574
|
+
: `(undefined as any as ${sourceText})`);
|
|
3575
|
+
}
|
|
3576
|
+
const targetText = targetNode.getText();
|
|
3577
|
+
if (targetText.trim().length > 0) {
|
|
3578
|
+
this.#editor.update(targetNode.getFullStart() - 1, targetNode.getEnd() + 1, `[${targetText}]`.padStart(targetNode.getFullWidth()));
|
|
3510
3579
|
}
|
|
3511
3580
|
break;
|
|
3512
3581
|
}
|
|
@@ -3514,65 +3583,6 @@ class AbilityLayer {
|
|
|
3514
3583
|
}
|
|
3515
3584
|
}
|
|
3516
3585
|
|
|
3517
|
-
class SourceTextEditor {
|
|
3518
|
-
#filePath = "";
|
|
3519
|
-
#sourceFile;
|
|
3520
|
-
#text = "";
|
|
3521
|
-
open(sourceFile) {
|
|
3522
|
-
this.#sourceFile = sourceFile;
|
|
3523
|
-
this.#filePath = sourceFile.fileName;
|
|
3524
|
-
this.#text = sourceFile.text;
|
|
3525
|
-
}
|
|
3526
|
-
close() {
|
|
3527
|
-
if (this.#sourceFile != null) {
|
|
3528
|
-
SourceService.set(this.#sourceFile);
|
|
3529
|
-
this.#sourceFile = undefined;
|
|
3530
|
-
}
|
|
3531
|
-
this.#filePath = "";
|
|
3532
|
-
this.#text = "";
|
|
3533
|
-
}
|
|
3534
|
-
eraseTrailingComma(node) {
|
|
3535
|
-
if (node.hasTrailingComma) {
|
|
3536
|
-
this.replaceRange(node.end - 1, node.end);
|
|
3537
|
-
}
|
|
3538
|
-
}
|
|
3539
|
-
#getErasedRange(start, end) {
|
|
3540
|
-
if (this.#text.indexOf("\n", start) >= end) {
|
|
3541
|
-
return " ".repeat(end - start);
|
|
3542
|
-
}
|
|
3543
|
-
const text = [];
|
|
3544
|
-
for (let index = start; index < end; index++) {
|
|
3545
|
-
const character = this.#text.charAt(index);
|
|
3546
|
-
switch (character) {
|
|
3547
|
-
case "\n":
|
|
3548
|
-
case "\r":
|
|
3549
|
-
text.push(character);
|
|
3550
|
-
break;
|
|
3551
|
-
default:
|
|
3552
|
-
text.push(" ");
|
|
3553
|
-
}
|
|
3554
|
-
}
|
|
3555
|
-
return text.join("");
|
|
3556
|
-
}
|
|
3557
|
-
getFilePath() {
|
|
3558
|
-
return this.#filePath;
|
|
3559
|
-
}
|
|
3560
|
-
getText() {
|
|
3561
|
-
return this.#text;
|
|
3562
|
-
}
|
|
3563
|
-
replaceRange(start, end, replacement) {
|
|
3564
|
-
const rangeText = replacement != null
|
|
3565
|
-
? `${replacement}${this.#getErasedRange(start, end).slice(replacement.length)}`
|
|
3566
|
-
: this.#getErasedRange(start, end);
|
|
3567
|
-
this.#text = `${this.#text.slice(0, start)}${rangeText}${this.#text.slice(end)}`;
|
|
3568
|
-
}
|
|
3569
|
-
replaceRanges(ranges) {
|
|
3570
|
-
for (const [start, end, replacement] of ranges) {
|
|
3571
|
-
this.replaceRange(start, end, replacement);
|
|
3572
|
-
}
|
|
3573
|
-
}
|
|
3574
|
-
}
|
|
3575
|
-
|
|
3576
3586
|
class SuppressedLayer {
|
|
3577
3587
|
#compiler;
|
|
3578
3588
|
#editor;
|
|
@@ -3645,7 +3655,7 @@ class SuppressedLayer {
|
|
|
3645
3655
|
}
|
|
3646
3656
|
for (const suppressedError of suppressedErrors) {
|
|
3647
3657
|
const { start, end } = suppressedError.directive;
|
|
3648
|
-
this.#editor.
|
|
3658
|
+
this.#editor.erase(start, end);
|
|
3649
3659
|
if (this.#suppressedErrorsMap != null) {
|
|
3650
3660
|
const { line } = tree.sourceFile.getLineAndCharacterOfPosition(start);
|
|
3651
3661
|
this.#suppressedErrorsMap.set(line, suppressedError);
|
|
@@ -3654,9 +3664,88 @@ class SuppressedLayer {
|
|
|
3654
3664
|
}
|
|
3655
3665
|
}
|
|
3656
3666
|
|
|
3667
|
+
class TextEditor {
|
|
3668
|
+
#filePath = "";
|
|
3669
|
+
#offset = 0;
|
|
3670
|
+
#offsets = [];
|
|
3671
|
+
#text = "";
|
|
3672
|
+
open(sourceFile) {
|
|
3673
|
+
this.#filePath = sourceFile.fileName;
|
|
3674
|
+
this.#text = sourceFile.text;
|
|
3675
|
+
this.#offset = 0;
|
|
3676
|
+
this.#offsets = [];
|
|
3677
|
+
}
|
|
3678
|
+
close() {
|
|
3679
|
+
this.#filePath = "";
|
|
3680
|
+
this.#text = "";
|
|
3681
|
+
this.#offset = 0;
|
|
3682
|
+
this.#offsets = [];
|
|
3683
|
+
}
|
|
3684
|
+
erase(start, end) {
|
|
3685
|
+
this.#text =
|
|
3686
|
+
this.#text.slice(0, start + this.#offset) +
|
|
3687
|
+
this.#getErased(start + this.#offset, end + this.#offset) +
|
|
3688
|
+
this.#text.slice(end + this.#offset);
|
|
3689
|
+
return this;
|
|
3690
|
+
}
|
|
3691
|
+
eraseTrailingComma(node) {
|
|
3692
|
+
if (node.hasTrailingComma) {
|
|
3693
|
+
this.erase(node.end - 1, node.end);
|
|
3694
|
+
}
|
|
3695
|
+
return this;
|
|
3696
|
+
}
|
|
3697
|
+
insert(position, text) {
|
|
3698
|
+
this.update(position, position, text);
|
|
3699
|
+
return this;
|
|
3700
|
+
}
|
|
3701
|
+
#getErased(start, end) {
|
|
3702
|
+
if (this.#text.indexOf("\n", start) >= end) {
|
|
3703
|
+
return " ".repeat(end - start);
|
|
3704
|
+
}
|
|
3705
|
+
const text = [];
|
|
3706
|
+
for (let index = start; index < end; index++) {
|
|
3707
|
+
const character = this.#text.charAt(index);
|
|
3708
|
+
switch (character) {
|
|
3709
|
+
case "\n":
|
|
3710
|
+
case "\r":
|
|
3711
|
+
text.push(character);
|
|
3712
|
+
break;
|
|
3713
|
+
default:
|
|
3714
|
+
text.push(" ");
|
|
3715
|
+
}
|
|
3716
|
+
}
|
|
3717
|
+
return text.join("");
|
|
3718
|
+
}
|
|
3719
|
+
getFilePath() {
|
|
3720
|
+
return this.#filePath;
|
|
3721
|
+
}
|
|
3722
|
+
getOffsets() {
|
|
3723
|
+
return this.#offsets;
|
|
3724
|
+
}
|
|
3725
|
+
getText() {
|
|
3726
|
+
return this.#text;
|
|
3727
|
+
}
|
|
3728
|
+
#setOffset(start, end, text) {
|
|
3729
|
+
const diff = text.length - (end - start);
|
|
3730
|
+
if (diff > 0) {
|
|
3731
|
+
this.#offset += diff;
|
|
3732
|
+
this.#offsets.push({ position: end, diff });
|
|
3733
|
+
}
|
|
3734
|
+
}
|
|
3735
|
+
update(start, end, text) {
|
|
3736
|
+
this.#text =
|
|
3737
|
+
this.#text.slice(0, start + this.#offset) +
|
|
3738
|
+
text +
|
|
3739
|
+
this.#getErased(start + this.#offset, end + this.#offset).slice(text.length) +
|
|
3740
|
+
this.#text.slice(end + this.#offset);
|
|
3741
|
+
this.#setOffset(start, end, text);
|
|
3742
|
+
return this;
|
|
3743
|
+
}
|
|
3744
|
+
}
|
|
3745
|
+
|
|
3657
3746
|
class Layers {
|
|
3658
3747
|
#abilityLayer;
|
|
3659
|
-
#editor = new
|
|
3748
|
+
#editor = new TextEditor();
|
|
3660
3749
|
#projectService;
|
|
3661
3750
|
#suppressedDiagnostics;
|
|
3662
3751
|
#suppressedLayer;
|
|
@@ -3665,14 +3754,23 @@ class Layers {
|
|
|
3665
3754
|
this.#abilityLayer = new AbilityLayer(compiler, this.#editor);
|
|
3666
3755
|
this.#suppressedLayer = new SuppressedLayer(compiler, this.#editor, resolvedConfig);
|
|
3667
3756
|
}
|
|
3668
|
-
close() {
|
|
3669
|
-
let
|
|
3757
|
+
close(tree) {
|
|
3758
|
+
let seenDiagnostics = [];
|
|
3670
3759
|
if (this.#suppressedDiagnostics != null) {
|
|
3671
|
-
|
|
3760
|
+
seenDiagnostics = this.#suppressedDiagnostics;
|
|
3672
3761
|
this.#suppressedDiagnostics = undefined;
|
|
3673
|
-
isSeenDiagnostic = (diagnostic) => !seenDiagnostics.some((seenDiagnostic) => compareDiagnostics(diagnostic, seenDiagnostic));
|
|
3674
3762
|
}
|
|
3675
|
-
const
|
|
3763
|
+
const diagnostics = this.#projectService.getDiagnostics(this.#editor.getFilePath(), this.#editor.getText());
|
|
3764
|
+
const offsets = this.#editor.getOffsets();
|
|
3765
|
+
const abilityDiagnostics = [];
|
|
3766
|
+
if (diagnostics != null) {
|
|
3767
|
+
for (const diagnostic of diagnostics) {
|
|
3768
|
+
const mappedDiagnostic = new MappedDiagnostic(tree.sourceFile, diagnostic, offsets);
|
|
3769
|
+
if (!seenDiagnostics.some((seenDiagnostic) => compareDiagnostics(mappedDiagnostic, seenDiagnostic))) {
|
|
3770
|
+
abilityDiagnostics.push(mappedDiagnostic);
|
|
3771
|
+
}
|
|
3772
|
+
}
|
|
3773
|
+
}
|
|
3676
3774
|
this.#abilityLayer.close(abilityDiagnostics);
|
|
3677
3775
|
this.#editor.close();
|
|
3678
3776
|
}
|
|
@@ -3680,7 +3778,7 @@ class Layers {
|
|
|
3680
3778
|
this.#editor.open(tree.sourceFile);
|
|
3681
3779
|
this.#suppressedLayer.open(tree);
|
|
3682
3780
|
this.#suppressedDiagnostics = this.#projectService.getDiagnostics(this.#editor.getFilePath(), this.#editor.getText());
|
|
3683
|
-
this.#suppressedLayer.close(this.#suppressedDiagnostics);
|
|
3781
|
+
this.#suppressedLayer.close(this.#suppressedDiagnostics?.map((diagnostic) => new MappedDiagnostic(tree.sourceFile, diagnostic)));
|
|
3684
3782
|
}
|
|
3685
3783
|
visit(node) {
|
|
3686
3784
|
this.#abilityLayer.visitExpect(node);
|
|
@@ -3927,7 +4025,7 @@ class CollectService {
|
|
|
3927
4025
|
this.#layers.open(testTree);
|
|
3928
4026
|
this.#identifierLookup.open();
|
|
3929
4027
|
this.#collectTestTreeNodes(sourceFile, testTree, testTree);
|
|
3930
|
-
this.#layers.close();
|
|
4028
|
+
this.#layers.close(testTree);
|
|
3931
4029
|
EventEmitter.dispatch(["collect:end", { tree: testTree }]);
|
|
3932
4030
|
return testTree;
|
|
3933
4031
|
}
|
|
@@ -4061,7 +4159,6 @@ class ProjectService {
|
|
|
4061
4159
|
}
|
|
4062
4160
|
closeFile(filePath) {
|
|
4063
4161
|
this.#service.closeClientFile(filePath);
|
|
4064
|
-
SourceService.delete(filePath);
|
|
4065
4162
|
}
|
|
4066
4163
|
#getDefaultCompilerOptions() {
|
|
4067
4164
|
const defaultCompilerOptions = {
|
|
@@ -4091,14 +4188,10 @@ class ProjectService {
|
|
|
4091
4188
|
}
|
|
4092
4189
|
return project;
|
|
4093
4190
|
}
|
|
4094
|
-
getDiagnostics(filePath, sourceText
|
|
4191
|
+
getDiagnostics(filePath, sourceText) {
|
|
4095
4192
|
this.openFile(filePath, sourceText);
|
|
4096
4193
|
const languageService = this.getLanguageService(filePath);
|
|
4097
|
-
|
|
4098
|
-
if (diagnostics != null && shouldInclude != null) {
|
|
4099
|
-
return diagnostics.filter(shouldInclude);
|
|
4100
|
-
}
|
|
4101
|
-
return diagnostics;
|
|
4194
|
+
return languageService?.getSemanticDiagnostics(filePath);
|
|
4102
4195
|
}
|
|
4103
4196
|
getLanguageService(filePath) {
|
|
4104
4197
|
const project = this.getDefaultProject(filePath);
|
|
@@ -4263,63 +4356,131 @@ class SuppressedService {
|
|
|
4263
4356
|
}
|
|
4264
4357
|
}
|
|
4265
4358
|
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
return `An argument for '${argumentNameText}' must be provided.`;
|
|
4269
|
-
}
|
|
4270
|
-
static argumentOrTypeArgumentMustBeProvided(argumentNameText, typeArgumentNameText) {
|
|
4271
|
-
return `An argument for '${argumentNameText}' or type argument for '${typeArgumentNameText}' must be provided.`;
|
|
4272
|
-
}
|
|
4273
|
-
static typeArgumentMustBeProvided(typeArgumentNameText) {
|
|
4274
|
-
return `Type argument for '${typeArgumentNameText}' must be provided.`;
|
|
4275
|
-
}
|
|
4359
|
+
function capitalize(text) {
|
|
4360
|
+
return text.replace(/^./, text.charAt(0).toUpperCase());
|
|
4276
4361
|
}
|
|
4277
4362
|
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
const origin = DiagnosticOrigin.fromNode(enclosingNode);
|
|
4282
|
-
onDiagnostics([Diagnostic.error(text, origin)]);
|
|
4283
|
-
return false;
|
|
4363
|
+
class RejectDiagnosticText {
|
|
4364
|
+
static argumentCannotBeOfType(typeText) {
|
|
4365
|
+
return `The argument cannot be of the '${typeText}' type.`;
|
|
4284
4366
|
}
|
|
4285
|
-
|
|
4286
|
-
}
|
|
4287
|
-
|
|
4288
|
-
function argumentOrTypeArgumentIsProvided(argumentNameText, typeArgumentNameText, node, enclosingNode, onDiagnostics) {
|
|
4289
|
-
if (!node) {
|
|
4290
|
-
const text = EnsureDiagnosticText.argumentOrTypeArgumentMustBeProvided(argumentNameText, typeArgumentNameText);
|
|
4291
|
-
const origin = DiagnosticOrigin.fromNode(enclosingNode);
|
|
4292
|
-
onDiagnostics([Diagnostic.error(text, origin)]);
|
|
4293
|
-
return false;
|
|
4367
|
+
static typeArgumentCannotBeOfType(typeText) {
|
|
4368
|
+
return `The type argument cannot be of the '${typeText}' type.`;
|
|
4294
4369
|
}
|
|
4295
|
-
|
|
4296
|
-
}
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
const origin = DiagnosticOrigin.fromNode(enclosingNode);
|
|
4302
|
-
onDiagnostics([Diagnostic.error(text, origin)]);
|
|
4303
|
-
return false;
|
|
4370
|
+
static typeWasRejected(typeText) {
|
|
4371
|
+
const optionNameText = `reject${capitalize(typeText)}Type`;
|
|
4372
|
+
return [
|
|
4373
|
+
`The '${typeText}' type was rejected because the '${optionNameText}' option is enabled.`,
|
|
4374
|
+
`If this check is necessary, pass '${typeText}' as the type argument explicitly.`,
|
|
4375
|
+
];
|
|
4304
4376
|
}
|
|
4305
|
-
return true;
|
|
4306
4377
|
}
|
|
4307
4378
|
|
|
4308
|
-
class
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4379
|
+
class Reject {
|
|
4380
|
+
#compiler;
|
|
4381
|
+
#rejectedArgumentTypes = new Set();
|
|
4382
|
+
#typeChecker;
|
|
4383
|
+
constructor(compiler, program, resolvedConfig) {
|
|
4384
|
+
this.#compiler = compiler;
|
|
4385
|
+
this.#typeChecker = program.getTypeChecker();
|
|
4386
|
+
if (resolvedConfig.rejectAnyType) {
|
|
4387
|
+
this.#rejectedArgumentTypes.add("any");
|
|
4388
|
+
}
|
|
4389
|
+
if (resolvedConfig.rejectNeverType) {
|
|
4390
|
+
this.#rejectedArgumentTypes.add("never");
|
|
4391
|
+
}
|
|
4392
|
+
}
|
|
4393
|
+
argumentType(target, onDiagnostics) {
|
|
4394
|
+
for (const rejectedType of this.#rejectedArgumentTypes) {
|
|
4395
|
+
const allowedKeyword = this.#compiler.SyntaxKind[`${capitalize(rejectedType)}Keyword`];
|
|
4396
|
+
if (target.some((node) => node?.kind === allowedKeyword)) {
|
|
4397
|
+
continue;
|
|
4398
|
+
}
|
|
4399
|
+
for (const node of target) {
|
|
4400
|
+
if (!node) {
|
|
4401
|
+
continue;
|
|
4402
|
+
}
|
|
4403
|
+
if (this.#typeChecker.getTypeAtLocation(node).flags & this.#compiler.TypeFlags[capitalize(rejectedType)]) {
|
|
4404
|
+
const text = [
|
|
4405
|
+
nodeBelongsToArgumentList(this.#compiler, node)
|
|
4406
|
+
? RejectDiagnosticText.argumentCannotBeOfType(rejectedType)
|
|
4407
|
+
: RejectDiagnosticText.typeArgumentCannotBeOfType(rejectedType),
|
|
4408
|
+
...RejectDiagnosticText.typeWasRejected(rejectedType),
|
|
4409
|
+
];
|
|
4410
|
+
const origin = DiagnosticOrigin.fromNode(node);
|
|
4411
|
+
onDiagnostics([Diagnostic.error(text, origin)]);
|
|
4412
|
+
return true;
|
|
4413
|
+
}
|
|
4414
|
+
}
|
|
4415
|
+
}
|
|
4416
|
+
return false;
|
|
4417
|
+
}
|
|
4418
|
+
}
|
|
4419
|
+
|
|
4420
|
+
class Ensure {
|
|
4421
|
+
#compiler;
|
|
4422
|
+
constructor(compiler) {
|
|
4423
|
+
this.#compiler = compiler;
|
|
4424
|
+
}
|
|
4425
|
+
argument(node, enclosingNode, onDiagnostics) {
|
|
4426
|
+
if (!node || !nodeBelongsToArgumentList(this.#compiler, node)) {
|
|
4427
|
+
this.#emitDiagnostic("An argument must be provided.", enclosingNode, onDiagnostics);
|
|
4428
|
+
return false;
|
|
4429
|
+
}
|
|
4430
|
+
return true;
|
|
4431
|
+
}
|
|
4432
|
+
argumentOrTypeArgument(node, enclosingNode, onDiagnostics) {
|
|
4433
|
+
if (!node) {
|
|
4434
|
+
this.#emitDiagnostic("An argument or type argument must be provided.", enclosingNode, onDiagnostics);
|
|
4435
|
+
return false;
|
|
4436
|
+
}
|
|
4437
|
+
return true;
|
|
4438
|
+
}
|
|
4439
|
+
jsxSetup(program, node, onDiagnostics) {
|
|
4440
|
+
const diagnosticText = [];
|
|
4441
|
+
if (!program.getCompilerOptions().jsx) {
|
|
4442
|
+
diagnosticText.push("The matcher requires the 'jsx' compiler option to be configured.");
|
|
4443
|
+
}
|
|
4444
|
+
if (node.getSourceFile().languageVariant !== this.#compiler.LanguageVariant.JSX) {
|
|
4445
|
+
diagnosticText.push("The matcher requires a '.tsx' file extension.");
|
|
4446
|
+
}
|
|
4447
|
+
if (diagnosticText.length > 0) {
|
|
4448
|
+
this.#emitDiagnostic(diagnosticText, node, onDiagnostics);
|
|
4449
|
+
return false;
|
|
4450
|
+
}
|
|
4451
|
+
return true;
|
|
4452
|
+
}
|
|
4453
|
+
typeArgument(node, enclosingNode, onDiagnostics) {
|
|
4454
|
+
if (!node || nodeBelongsToArgumentList(this.#compiler, node)) {
|
|
4455
|
+
this.#emitDiagnostic("A type argument must be provided.", enclosingNode, onDiagnostics);
|
|
4456
|
+
return false;
|
|
4457
|
+
}
|
|
4458
|
+
return true;
|
|
4459
|
+
}
|
|
4460
|
+
#emitDiagnostic(text, enclosingNode, onDiagnostics) {
|
|
4461
|
+
if (!Array.isArray(text)) {
|
|
4462
|
+
text = [text];
|
|
4463
|
+
}
|
|
4464
|
+
const origin = DiagnosticOrigin.fromNode(enclosingNode);
|
|
4465
|
+
onDiagnostics(text.map((text) => Diagnostic.error(text, origin)));
|
|
4466
|
+
}
|
|
4467
|
+
}
|
|
4468
|
+
|
|
4469
|
+
class ExpectDiagnosticText {
|
|
4470
|
+
static argumentMustBe(expectedText) {
|
|
4471
|
+
return `The argument must be ${expectedText}.`;
|
|
4472
|
+
}
|
|
4473
|
+
static typeArgumentMustBe(expectedText) {
|
|
4474
|
+
return `The type argument must be ${expectedText}.`;
|
|
4475
|
+
}
|
|
4476
|
+
static isCallable(isExpression, targetText) {
|
|
4477
|
+
return `${isExpression ? "Expression" : "Type"} is callable ${targetText}.`;
|
|
4478
|
+
}
|
|
4479
|
+
static isNotCallable(isExpression, targetText) {
|
|
4480
|
+
return `${isExpression ? "Expression" : "Type"} is not callable ${targetText}.`;
|
|
4481
|
+
}
|
|
4482
|
+
static isConstructable(isExpression, targetText) {
|
|
4483
|
+
return `${isExpression ? "Expression" : "Type"} is constructable ${targetText}.`;
|
|
4323
4484
|
}
|
|
4324
4485
|
static isNotConstructable(isExpression, targetText) {
|
|
4325
4486
|
return `${isExpression ? "Expression" : "Type"} is not constructable ${targetText}.`;
|
|
@@ -4331,10 +4492,10 @@ class ExpectDiagnosticText {
|
|
|
4331
4492
|
return `${isExpression ? "Expression" : "Type"} is not instantiable ${targetText}.`;
|
|
4332
4493
|
}
|
|
4333
4494
|
static acceptsProps(isExpression) {
|
|
4334
|
-
return `${isExpression ? "Component" : "
|
|
4495
|
+
return `${isExpression ? "Component" : "Type"} accepts props of the given type.`;
|
|
4335
4496
|
}
|
|
4336
4497
|
static doesNotAcceptProps(isExpression) {
|
|
4337
|
-
return `${isExpression ? "Component" : "
|
|
4498
|
+
return `${isExpression ? "Component" : "Type"} does not accept props of the given type.`;
|
|
4338
4499
|
}
|
|
4339
4500
|
static canBeApplied(targetText) {
|
|
4340
4501
|
return `The decorator function can be applied${targetText}.`;
|
|
@@ -4354,9 +4515,6 @@ class ExpectDiagnosticText {
|
|
|
4354
4515
|
static matcherIsNotSupported(matcherNameText) {
|
|
4355
4516
|
return `The '.${matcherNameText}()' matcher is not supported.`;
|
|
4356
4517
|
}
|
|
4357
|
-
static overloadGaveTheFollowingError(index, count, signatureText) {
|
|
4358
|
-
return `Overload ${index} of ${count}, '${signatureText}', gave the following error.`;
|
|
4359
|
-
}
|
|
4360
4518
|
static raisedTypeError(count = 1) {
|
|
4361
4519
|
return `The raised type error${count === 1 ? "" : "s"}:`;
|
|
4362
4520
|
}
|
|
@@ -4394,21 +4552,11 @@ class ExpectDiagnosticText {
|
|
|
4394
4552
|
static isNotTheSame(sourceTypeText, targetTypeText) {
|
|
4395
4553
|
return `Type '${sourceTypeText}' is not the same as type '${targetTypeText}'.`;
|
|
4396
4554
|
}
|
|
4397
|
-
static isNotCompatibleWith(sourceTypeText, targetTypeText) {
|
|
4398
|
-
return `Type '${sourceTypeText}' is not compatible with type '${targetTypeText}'.`;
|
|
4399
|
-
}
|
|
4400
|
-
static requiresProperty(typeText, propertyNameText) {
|
|
4401
|
-
return `Type '${typeText}' requires property '${propertyNameText}'.`;
|
|
4402
|
-
}
|
|
4403
|
-
static typesOfPropertyAreNotCompatible(propertyNameText) {
|
|
4404
|
-
return `Types of property '${propertyNameText}' are not compatible.`;
|
|
4405
|
-
}
|
|
4406
4555
|
}
|
|
4407
4556
|
|
|
4408
4557
|
class MatchWorker {
|
|
4409
4558
|
assertionNode;
|
|
4410
4559
|
#compiler;
|
|
4411
|
-
#signatureCache = new Map();
|
|
4412
4560
|
typeChecker;
|
|
4413
4561
|
constructor(compiler, program, assertionNode) {
|
|
4414
4562
|
this.#compiler = compiler;
|
|
@@ -4432,219 +4580,118 @@ class MatchWorker {
|
|
|
4432
4580
|
: { flags: this.#compiler.TypeFlags.NonPrimitive };
|
|
4433
4581
|
return this.typeChecker.isTypeAssignableTo(type, nonPrimitiveType);
|
|
4434
4582
|
}
|
|
4435
|
-
getParameterType(signature, index) {
|
|
4436
|
-
const parameter = signature.getDeclaration().parameters[index];
|
|
4437
|
-
if (!parameter) {
|
|
4438
|
-
return;
|
|
4439
|
-
}
|
|
4440
|
-
return this.getType(parameter);
|
|
4441
|
-
}
|
|
4442
|
-
getSignatures(node) {
|
|
4443
|
-
let signatures = this.#signatureCache.get(node);
|
|
4444
|
-
if (!signatures) {
|
|
4445
|
-
const type = this.getType(node);
|
|
4446
|
-
signatures = type.getCallSignatures();
|
|
4447
|
-
if (signatures.length === 0) {
|
|
4448
|
-
signatures = type.getConstructSignatures();
|
|
4449
|
-
}
|
|
4450
|
-
}
|
|
4451
|
-
return signatures;
|
|
4452
|
-
}
|
|
4453
4583
|
getTypeText(node) {
|
|
4454
4584
|
return this.typeChecker.typeToString(this.getType(node));
|
|
4455
4585
|
}
|
|
4456
4586
|
getType(node) {
|
|
4457
4587
|
return this.typeChecker.getTypeAtLocation(node);
|
|
4458
4588
|
}
|
|
4459
|
-
resolveDiagnosticOrigin(symbol, enclosingNode) {
|
|
4460
|
-
if (symbol.valueDeclaration != null &&
|
|
4461
|
-
(this.#compiler.isPropertySignature(symbol.valueDeclaration) ||
|
|
4462
|
-
this.#compiler.isPropertyAssignment(symbol.valueDeclaration) ||
|
|
4463
|
-
this.#compiler.isShorthandPropertyAssignment(symbol.valueDeclaration)) &&
|
|
4464
|
-
symbol.valueDeclaration.getStart() >= enclosingNode.getStart() &&
|
|
4465
|
-
symbol.valueDeclaration.getEnd() <= enclosingNode.getEnd()) {
|
|
4466
|
-
return DiagnosticOrigin.fromNode(symbol.valueDeclaration.name, this.assertionNode);
|
|
4467
|
-
}
|
|
4468
|
-
return DiagnosticOrigin.fromNode(enclosingNode, this.assertionNode);
|
|
4469
|
-
}
|
|
4470
4589
|
}
|
|
4471
4590
|
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
return !!(type.flags & compiler.TypeFlags.Union);
|
|
4477
|
-
}
|
|
4478
|
-
function isUniqueSymbolType(compiler, type) {
|
|
4479
|
-
return !!(type.flags & compiler.TypeFlags.UniqueESSymbol);
|
|
4480
|
-
}
|
|
4481
|
-
|
|
4482
|
-
class ToAcceptProps {
|
|
4483
|
-
#compiler;
|
|
4484
|
-
#typeChecker;
|
|
4485
|
-
constructor(compiler, program) {
|
|
4486
|
-
this.#compiler = compiler;
|
|
4487
|
-
this.#typeChecker = program.getTypeChecker();
|
|
4488
|
-
}
|
|
4489
|
-
#explain(matchWorker, sourceNode, targetNode) {
|
|
4490
|
-
const isExpression = nodeBelongsToArgumentList(this.#compiler, sourceNode);
|
|
4491
|
-
const signatures = matchWorker.getSignatures(sourceNode);
|
|
4492
|
-
return signatures.reduce((accumulator, signature, index) => {
|
|
4493
|
-
let diagnostic;
|
|
4494
|
-
const introText = matchWorker.assertionNode.isNot
|
|
4495
|
-
? ExpectDiagnosticText.acceptsProps(isExpression)
|
|
4496
|
-
: ExpectDiagnosticText.doesNotAcceptProps(isExpression);
|
|
4497
|
-
const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.assertionNode);
|
|
4498
|
-
if (signatures.length > 1) {
|
|
4499
|
-
const signatureText = this.#typeChecker.signatureToString(signature, sourceNode);
|
|
4500
|
-
const overloadText = ExpectDiagnosticText.overloadGaveTheFollowingError(index + 1, signatures.length, signatureText);
|
|
4501
|
-
diagnostic = Diagnostic.error([introText, overloadText], origin);
|
|
4502
|
-
}
|
|
4503
|
-
else {
|
|
4504
|
-
diagnostic = Diagnostic.error([introText], origin);
|
|
4505
|
-
}
|
|
4506
|
-
const { diagnostics, isMatch } = this.#explainProperties(matchWorker, signature, targetNode, diagnostic);
|
|
4507
|
-
if (matchWorker.assertionNode.isNot ? isMatch : !isMatch) {
|
|
4508
|
-
accumulator.push(...diagnostics);
|
|
4509
|
-
}
|
|
4510
|
-
return accumulator;
|
|
4511
|
-
}, []);
|
|
4591
|
+
class AbilityMatcherBase {
|
|
4592
|
+
compiler;
|
|
4593
|
+
constructor(compiler) {
|
|
4594
|
+
this.compiler = compiler;
|
|
4512
4595
|
}
|
|
4513
|
-
|
|
4514
|
-
|
|
4596
|
+
getArgumentCountText(nodes) {
|
|
4597
|
+
if (nodes.length === 0) {
|
|
4598
|
+
return "without arguments";
|
|
4599
|
+
}
|
|
4600
|
+
if (nodes.length === 1 && nodes[0]?.kind === this.compiler.SyntaxKind.SpreadElement) {
|
|
4601
|
+
return "with the given arguments";
|
|
4602
|
+
}
|
|
4603
|
+
return `with the given argument${nodes.length === 1 ? "" : "s"}`;
|
|
4515
4604
|
}
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
const targetPropertyName = targetProperty.getName();
|
|
4520
|
-
const sourceProperty = sourceType?.getProperty(targetPropertyName);
|
|
4521
|
-
if (!sourceProperty) {
|
|
4522
|
-
return false;
|
|
4523
|
-
}
|
|
4524
|
-
const targetPropertyType = this.#typeChecker.getTypeOfSymbol(targetProperty);
|
|
4525
|
-
const sourcePropertyType = this.#typeChecker.getTypeOfSymbol(sourceProperty);
|
|
4526
|
-
if (!this.#typeChecker.isTypeAssignableTo(targetPropertyType, sourcePropertyType)) {
|
|
4527
|
-
return false;
|
|
4528
|
-
}
|
|
4529
|
-
}
|
|
4530
|
-
if (sourceType != null) {
|
|
4531
|
-
const sourceProperties = sourceType.getProperties();
|
|
4532
|
-
for (const sourceProperty of sourceProperties) {
|
|
4533
|
-
const targetProperty = targetType.getProperty(sourceProperty.getName());
|
|
4534
|
-
if (!targetProperty && !this.#isOptionalProperty(sourceProperty)) {
|
|
4535
|
-
return false;
|
|
4536
|
-
}
|
|
4537
|
-
}
|
|
4538
|
-
}
|
|
4539
|
-
return true;
|
|
4540
|
-
};
|
|
4541
|
-
if (sourceType != null && isUnionType(this.#compiler, sourceType)) {
|
|
4542
|
-
return sourceType.types.some((sourceType) => check(sourceType, targetType));
|
|
4605
|
+
getTypeArgumentCountText(targetNode) {
|
|
4606
|
+
if (targetNode.elements.length === 0) {
|
|
4607
|
+
return "without type arguments";
|
|
4543
4608
|
}
|
|
4544
|
-
return
|
|
4609
|
+
return `with the given type argument${targetNode.elements.length === 1 ? "" : "s"}`;
|
|
4545
4610
|
}
|
|
4546
|
-
|
|
4547
|
-
const
|
|
4548
|
-
const
|
|
4549
|
-
const
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
const sourceProperty = sourceType?.getProperty(targetPropertyName);
|
|
4557
|
-
if (!sourceProperty) {
|
|
4558
|
-
const text = [
|
|
4559
|
-
ExpectDiagnosticText.isNotCompatibleWith(sourceTypeText, targetTypeText),
|
|
4560
|
-
ExpectDiagnosticText.doesNotHaveProperty(sourceTypeText, targetPropertyName),
|
|
4561
|
-
];
|
|
4562
|
-
const origin = matchWorker.resolveDiagnosticOrigin(targetProperty, targetNode);
|
|
4563
|
-
diagnostics.push(diagnostic.extendWith(text, origin));
|
|
4564
|
-
continue;
|
|
4611
|
+
explain(matchWorker, sourceNode, targetNode, getArgumentCountText) {
|
|
4612
|
+
const isExpression = nodeBelongsToArgumentList(this.compiler, sourceNode);
|
|
4613
|
+
const argumentCountText = getArgumentCountText?.();
|
|
4614
|
+
const diagnostics = [];
|
|
4615
|
+
if (matchWorker.assertionNode.abilityDiagnostics.size > 0) {
|
|
4616
|
+
for (const diagnostic of matchWorker.assertionNode.abilityDiagnostics) {
|
|
4617
|
+
const text = [this.explainNotText(isExpression, argumentCountText), getDiagnosticMessageText(diagnostic)];
|
|
4618
|
+
let origin;
|
|
4619
|
+
if (isDiagnosticWithLocation(diagnostic) && diagnosticBelongsToNode(diagnostic, targetNode)) {
|
|
4620
|
+
origin = DiagnosticOrigin.fromAbilityDiagnostic(diagnostic, matchWorker.assertionNode);
|
|
4565
4621
|
}
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
if (!this.#typeChecker.isTypeAssignableTo(targetPropertyType, sourcePropertyType)) {
|
|
4569
|
-
const targetPropertyTypeText = this.#typeChecker.typeToString(targetPropertyType);
|
|
4570
|
-
const sourcePropertyTypeText = this.#typeChecker.typeToString(sourcePropertyType);
|
|
4571
|
-
const text = [
|
|
4572
|
-
ExpectDiagnosticText.isNotAssignableFrom(sourceTypeText, targetTypeText),
|
|
4573
|
-
ExpectDiagnosticText.typesOfPropertyAreNotCompatible(targetPropertyName),
|
|
4574
|
-
ExpectDiagnosticText.isNotAssignableFrom(sourcePropertyTypeText, targetPropertyTypeText),
|
|
4575
|
-
];
|
|
4576
|
-
const origin = matchWorker.resolveDiagnosticOrigin(targetProperty, targetNode);
|
|
4577
|
-
diagnostics.push(diagnostic.extendWith(text, origin));
|
|
4622
|
+
else {
|
|
4623
|
+
origin = DiagnosticOrigin.fromAssertion(matchWorker.assertionNode);
|
|
4578
4624
|
}
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
const sourcePropertyName = sourceProperty.getName();
|
|
4583
|
-
const targetProperty = targetType.getProperty(sourcePropertyName);
|
|
4584
|
-
if (!targetProperty && !this.#isOptionalProperty(sourceProperty)) {
|
|
4585
|
-
const text = [
|
|
4586
|
-
ExpectDiagnosticText.isNotAssignableFrom(sourceTypeText, targetTypeText),
|
|
4587
|
-
ExpectDiagnosticText.requiresProperty(sourceTypeText, sourcePropertyName),
|
|
4588
|
-
];
|
|
4589
|
-
diagnostics.push(diagnostic.extendWith(text));
|
|
4590
|
-
}
|
|
4625
|
+
let related;
|
|
4626
|
+
if (diagnostic.relatedInformation != null) {
|
|
4627
|
+
related = Diagnostic.fromDiagnostics(diagnostic.relatedInformation);
|
|
4591
4628
|
}
|
|
4629
|
+
diagnostics.push(Diagnostic.error(text.flat(), origin).add({ related }));
|
|
4592
4630
|
}
|
|
4593
|
-
if (diagnostics.length === 0) {
|
|
4594
|
-
const text = ExpectDiagnosticText.isAssignableFrom(sourceTypeText, targetTypeText);
|
|
4595
|
-
diagnostics.push(diagnostic.extendWith(text));
|
|
4596
|
-
return { diagnostics, isMatch: true };
|
|
4597
|
-
}
|
|
4598
|
-
return { diagnostics, isMatch: false };
|
|
4599
|
-
};
|
|
4600
|
-
if (sourceType != null && isUnionType(this.#compiler, sourceType)) {
|
|
4601
|
-
let accumulator = [];
|
|
4602
|
-
const isMatch = sourceType.types.some((sourceType) => {
|
|
4603
|
-
const text = matchWorker.assertionNode.isNot
|
|
4604
|
-
? ExpectDiagnosticText.isAssignableFrom(sourceTypeText, targetTypeText)
|
|
4605
|
-
: ExpectDiagnosticText.isNotAssignableFrom(sourceTypeText, targetTypeText);
|
|
4606
|
-
const { diagnostics, isMatch } = explain(sourceType, targetType, diagnostic.extendWith(text));
|
|
4607
|
-
if (isMatch) {
|
|
4608
|
-
accumulator = diagnostics;
|
|
4609
|
-
}
|
|
4610
|
-
else {
|
|
4611
|
-
accumulator.push(...diagnostics);
|
|
4612
|
-
}
|
|
4613
|
-
return isMatch;
|
|
4614
|
-
});
|
|
4615
|
-
return { diagnostics: accumulator, isMatch };
|
|
4616
4631
|
}
|
|
4617
|
-
|
|
4632
|
+
else {
|
|
4633
|
+
const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertionNode);
|
|
4634
|
+
diagnostics.push(Diagnostic.error(this.explainText(isExpression, argumentCountText), origin));
|
|
4635
|
+
}
|
|
4636
|
+
return diagnostics;
|
|
4618
4637
|
}
|
|
4638
|
+
}
|
|
4639
|
+
|
|
4640
|
+
class ToAcceptProps extends AbilityMatcherBase {
|
|
4641
|
+
explainText = ExpectDiagnosticText.acceptsProps;
|
|
4642
|
+
explainNotText = ExpectDiagnosticText.doesNotAcceptProps;
|
|
4619
4643
|
match(matchWorker, sourceNode, targetNode, onDiagnostics) {
|
|
4620
4644
|
const diagnostics = [];
|
|
4621
|
-
const
|
|
4622
|
-
if (
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4645
|
+
const sourceType = matchWorker.getType(sourceNode);
|
|
4646
|
+
if (!(this.compiler.isIdentifier(sourceNode) ||
|
|
4647
|
+
this.compiler.isPropertyAccessExpression(sourceNode) ||
|
|
4648
|
+
this.compiler.isTypeReferenceNode(sourceNode) ||
|
|
4649
|
+
this.compiler.isExpressionWithTypeArguments(sourceNode)) ||
|
|
4650
|
+
!(sourceType.getCallSignatures().length > 0 || sourceType.getConstructSignatures().length > 0)) {
|
|
4651
|
+
const expectedText = "an identifier of a JSX component";
|
|
4652
|
+
const text = nodeBelongsToArgumentList(this.compiler, sourceNode)
|
|
4653
|
+
? ExpectDiagnosticText.argumentMustBe(expectedText)
|
|
4626
4654
|
: ExpectDiagnosticText.typeArgumentMustBe(expectedText);
|
|
4627
4655
|
const origin = DiagnosticOrigin.fromNode(sourceNode);
|
|
4628
4656
|
diagnostics.push(Diagnostic.error(text, origin));
|
|
4629
4657
|
}
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
const
|
|
4633
|
-
|
|
4658
|
+
else if (!/^[A-Z_$]/.test(sourceNode.getText()[0])) {
|
|
4659
|
+
const expectedText = "an identifier that begins with an uppercase letter";
|
|
4660
|
+
const text = nodeBelongsToArgumentList(this.compiler, sourceNode)
|
|
4661
|
+
? ExpectDiagnosticText.argumentMustBe(expectedText)
|
|
4662
|
+
: ExpectDiagnosticText.typeArgumentMustBe(expectedText);
|
|
4663
|
+
const origin = DiagnosticOrigin.fromNode(sourceNode);
|
|
4664
|
+
diagnostics.push(Diagnostic.error(text, origin));
|
|
4665
|
+
}
|
|
4666
|
+
if (!this.compiler.isObjectLiteralExpression(targetNode)) {
|
|
4667
|
+
const expectedText = "an object literal with key-value pairs";
|
|
4668
|
+
const text = ExpectDiagnosticText.argumentMustBe(expectedText);
|
|
4634
4669
|
const origin = DiagnosticOrigin.fromNode(targetNode);
|
|
4635
4670
|
diagnostics.push(Diagnostic.error(text, origin));
|
|
4636
4671
|
}
|
|
4672
|
+
else {
|
|
4673
|
+
for (const property of targetNode.properties) {
|
|
4674
|
+
if (!(this.compiler.isPropertyAssignment(property) || this.compiler.isSpreadAssignment(property))) {
|
|
4675
|
+
const text = "Each property must be a key-value pair or a spread element.";
|
|
4676
|
+
const origin = DiagnosticOrigin.fromNode(property);
|
|
4677
|
+
diagnostics.push(Diagnostic.error(text, origin));
|
|
4678
|
+
continue;
|
|
4679
|
+
}
|
|
4680
|
+
if (this.compiler.isPropertyAssignment(property) &&
|
|
4681
|
+
!(this.compiler.isIdentifier(property.name) || this.compiler.isStringLiteral(property.name))) {
|
|
4682
|
+
const text = "Property keys must be static identifiers or string literals.";
|
|
4683
|
+
const origin = DiagnosticOrigin.fromNode(property.name);
|
|
4684
|
+
diagnostics.push(Diagnostic.error(text, origin));
|
|
4685
|
+
}
|
|
4686
|
+
}
|
|
4687
|
+
}
|
|
4637
4688
|
if (diagnostics.length > 0) {
|
|
4638
4689
|
onDiagnostics(diagnostics);
|
|
4639
4690
|
return;
|
|
4640
4691
|
}
|
|
4641
|
-
const isMatch = signatures.some((signature) => {
|
|
4642
|
-
const sourceType = matchWorker.getParameterType(signature, 0);
|
|
4643
|
-
return this.#checkProperties(sourceType, targetType);
|
|
4644
|
-
});
|
|
4645
4692
|
return {
|
|
4646
|
-
explain: () => this
|
|
4647
|
-
isMatch,
|
|
4693
|
+
explain: () => this.explain(matchWorker, sourceNode, targetNode),
|
|
4694
|
+
isMatch: matchWorker.assertionNode.abilityDiagnostics.size === 0,
|
|
4648
4695
|
};
|
|
4649
4696
|
}
|
|
4650
4697
|
}
|
|
@@ -5245,7 +5292,7 @@ class ToBeApplicable {
|
|
|
5245
5292
|
if (type.getCallSignatures().length === 0) {
|
|
5246
5293
|
const expectedText = "of a function type";
|
|
5247
5294
|
const text = nodeBelongsToArgumentList(this.#compiler, sourceNode)
|
|
5248
|
-
? ExpectDiagnosticText.argumentMustBe(
|
|
5295
|
+
? ExpectDiagnosticText.argumentMustBe(expectedText)
|
|
5249
5296
|
: ExpectDiagnosticText.typeArgumentMustBe(expectedText);
|
|
5250
5297
|
const origin = DiagnosticOrigin.fromNode(sourceNode);
|
|
5251
5298
|
onDiagnostics([Diagnostic.error(text, origin)]);
|
|
@@ -5280,55 +5327,6 @@ class ToBeAssignableTo extends RelationMatcherBase {
|
|
|
5280
5327
|
}
|
|
5281
5328
|
}
|
|
5282
5329
|
|
|
5283
|
-
class AbilityMatcherBase {
|
|
5284
|
-
compiler;
|
|
5285
|
-
constructor(compiler) {
|
|
5286
|
-
this.compiler = compiler;
|
|
5287
|
-
}
|
|
5288
|
-
getArgumentCountText(nodes) {
|
|
5289
|
-
if (nodes.length === 0) {
|
|
5290
|
-
return "without arguments";
|
|
5291
|
-
}
|
|
5292
|
-
if (nodes.length === 1 && nodes[0]?.kind === this.compiler.SyntaxKind.SpreadElement) {
|
|
5293
|
-
return "with the given arguments";
|
|
5294
|
-
}
|
|
5295
|
-
return `with the given argument${nodes.length === 1 ? "" : "s"}`;
|
|
5296
|
-
}
|
|
5297
|
-
getTypeArgumentCountText(targetNode) {
|
|
5298
|
-
if (targetNode.elements.length === 0) {
|
|
5299
|
-
return "without type arguments";
|
|
5300
|
-
}
|
|
5301
|
-
return `with the given type argument${targetNode.elements.length === 1 ? "" : "s"}`;
|
|
5302
|
-
}
|
|
5303
|
-
explain(matchWorker, sourceNode, targetNode, getArgumentCountText) {
|
|
5304
|
-
const isExpression = nodeBelongsToArgumentList(this.compiler, sourceNode);
|
|
5305
|
-
const argumentCountText = getArgumentCountText();
|
|
5306
|
-
const diagnostics = [];
|
|
5307
|
-
if (matchWorker.assertionNode.abilityDiagnostics.size > 0) {
|
|
5308
|
-
for (const diagnostic of matchWorker.assertionNode.abilityDiagnostics) {
|
|
5309
|
-
const text = [this.explainNotText(isExpression, argumentCountText), getDiagnosticMessageText(diagnostic)];
|
|
5310
|
-
let origin;
|
|
5311
|
-
if (isDiagnosticWithLocation(diagnostic) && diagnosticBelongsToNode(diagnostic, targetNode)) {
|
|
5312
|
-
origin = new DiagnosticOrigin(diagnostic.start, getTextSpanEnd(diagnostic), sourceNode.getSourceFile(), matchWorker.assertionNode);
|
|
5313
|
-
}
|
|
5314
|
-
else {
|
|
5315
|
-
origin = DiagnosticOrigin.fromAssertion(matchWorker.assertionNode);
|
|
5316
|
-
}
|
|
5317
|
-
let related;
|
|
5318
|
-
if (diagnostic.relatedInformation != null) {
|
|
5319
|
-
related = Diagnostic.fromDiagnostics(diagnostic.relatedInformation);
|
|
5320
|
-
}
|
|
5321
|
-
diagnostics.push(Diagnostic.error(text.flat(), origin).add({ related }));
|
|
5322
|
-
}
|
|
5323
|
-
}
|
|
5324
|
-
else {
|
|
5325
|
-
const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertionNode);
|
|
5326
|
-
diagnostics.push(Diagnostic.error(this.explainText(isExpression, argumentCountText), origin));
|
|
5327
|
-
}
|
|
5328
|
-
return diagnostics;
|
|
5329
|
-
}
|
|
5330
|
-
}
|
|
5331
|
-
|
|
5332
5330
|
class ToBeCallableWith extends AbilityMatcherBase {
|
|
5333
5331
|
explainText = ExpectDiagnosticText.isCallable;
|
|
5334
5332
|
explainNotText = ExpectDiagnosticText.isNotCallable;
|
|
@@ -5337,7 +5335,7 @@ class ToBeCallableWith extends AbilityMatcherBase {
|
|
|
5337
5335
|
if (sourceType.getCallSignatures().length === 0) {
|
|
5338
5336
|
const text = [];
|
|
5339
5337
|
if (nodeBelongsToArgumentList(this.compiler, sourceNode)) {
|
|
5340
|
-
text.push(ExpectDiagnosticText.argumentMustBe("
|
|
5338
|
+
text.push(ExpectDiagnosticText.argumentMustBe("a callable expression"));
|
|
5341
5339
|
}
|
|
5342
5340
|
else {
|
|
5343
5341
|
text.push(ExpectDiagnosticText.typeArgumentMustBe("a callable type"));
|
|
@@ -5364,7 +5362,7 @@ class ToBeConstructableWith extends AbilityMatcherBase {
|
|
|
5364
5362
|
if (sourceType.getConstructSignatures().length === 0) {
|
|
5365
5363
|
const text = [];
|
|
5366
5364
|
if (nodeBelongsToArgumentList(this.compiler, sourceNode)) {
|
|
5367
|
-
text.push(ExpectDiagnosticText.argumentMustBe("
|
|
5365
|
+
text.push(ExpectDiagnosticText.argumentMustBe("a constructable expression"));
|
|
5368
5366
|
}
|
|
5369
5367
|
else {
|
|
5370
5368
|
text.push(ExpectDiagnosticText.typeArgumentMustBe("a constructable type"));
|
|
@@ -5393,7 +5391,7 @@ class ToBeInstantiableWith extends AbilityMatcherBase {
|
|
|
5393
5391
|
this.compiler.isExpressionWithTypeArguments(sourceNode))) {
|
|
5394
5392
|
let text;
|
|
5395
5393
|
if (nodeBelongsToArgumentList(this.compiler, sourceNode)) {
|
|
5396
|
-
text = ExpectDiagnosticText.argumentMustBe("
|
|
5394
|
+
text = ExpectDiagnosticText.argumentMustBe("an instantiable expression");
|
|
5397
5395
|
}
|
|
5398
5396
|
else {
|
|
5399
5397
|
text = ExpectDiagnosticText.typeArgumentMustBe("an instantiable type");
|
|
@@ -5424,7 +5422,7 @@ class ToHaveProperty {
|
|
|
5424
5422
|
const sourceTypeText = matchWorker.getTypeText(sourceNode);
|
|
5425
5423
|
const targetType = matchWorker.getType(targetNode);
|
|
5426
5424
|
let propertyNameText;
|
|
5427
|
-
if (
|
|
5425
|
+
if (targetType.flags & (this.#compiler.TypeFlags.StringLiteral | this.#compiler.TypeFlags.NumberLiteral)) {
|
|
5428
5426
|
propertyNameText = targetType.value.toString();
|
|
5429
5427
|
}
|
|
5430
5428
|
else {
|
|
@@ -5442,15 +5440,18 @@ class ToHaveProperty {
|
|
|
5442
5440
|
!matchWorker.extendsObjectType(sourceType)) {
|
|
5443
5441
|
const expectedText = "of an object type";
|
|
5444
5442
|
const text = nodeBelongsToArgumentList(this.#compiler, sourceNode)
|
|
5445
|
-
? ExpectDiagnosticText.argumentMustBe(
|
|
5443
|
+
? ExpectDiagnosticText.argumentMustBe(expectedText)
|
|
5446
5444
|
: ExpectDiagnosticText.typeArgumentMustBe(expectedText);
|
|
5447
5445
|
const origin = DiagnosticOrigin.fromNode(sourceNode);
|
|
5448
5446
|
diagnostics.push(Diagnostic.error(text, origin));
|
|
5449
5447
|
}
|
|
5450
5448
|
const targetType = matchWorker.getType(targetNode);
|
|
5451
|
-
if (!(
|
|
5452
|
-
|
|
5453
|
-
|
|
5449
|
+
if (!(targetType.flags &
|
|
5450
|
+
(this.#compiler.TypeFlags.StringLiteral |
|
|
5451
|
+
this.#compiler.TypeFlags.NumberLiteral |
|
|
5452
|
+
this.#compiler.TypeFlags.UniqueESSymbol))) {
|
|
5453
|
+
const expectedText = "a string, number or symbol";
|
|
5454
|
+
const text = ExpectDiagnosticText.argumentMustBe(expectedText);
|
|
5454
5455
|
const origin = DiagnosticOrigin.fromNode(targetNode);
|
|
5455
5456
|
diagnostics.push(Diagnostic.error(text, origin));
|
|
5456
5457
|
}
|
|
@@ -5509,8 +5510,8 @@ class ToRaiseError {
|
|
|
5509
5510
|
if (!(this.#compiler.isStringLiteralLike(targetNode) ||
|
|
5510
5511
|
this.#compiler.isNumericLiteral(targetNode) ||
|
|
5511
5512
|
this.#compiler.isRegularExpressionLiteral(targetNode))) {
|
|
5512
|
-
const expectedText = "a string, number or regular expression
|
|
5513
|
-
const text = ExpectDiagnosticText.argumentMustBe(
|
|
5513
|
+
const expectedText = "a string, number or regular expression";
|
|
5514
|
+
const text = ExpectDiagnosticText.argumentMustBe(expectedText);
|
|
5514
5515
|
const origin = DiagnosticOrigin.fromNode(targetNode);
|
|
5515
5516
|
diagnostics.push(Diagnostic.error(text, origin));
|
|
5516
5517
|
}
|
|
@@ -5551,6 +5552,7 @@ class ToRaiseError {
|
|
|
5551
5552
|
|
|
5552
5553
|
class ExpectService {
|
|
5553
5554
|
#compiler;
|
|
5555
|
+
#ensure;
|
|
5554
5556
|
#program;
|
|
5555
5557
|
#reject;
|
|
5556
5558
|
toAcceptProps;
|
|
@@ -5563,11 +5565,12 @@ class ExpectService {
|
|
|
5563
5565
|
toBeInstantiableWith;
|
|
5564
5566
|
toHaveProperty;
|
|
5565
5567
|
toRaiseError;
|
|
5566
|
-
constructor(compiler, program,
|
|
5568
|
+
constructor(compiler, program, resolvedConfig) {
|
|
5567
5569
|
this.#compiler = compiler;
|
|
5568
5570
|
this.#program = program;
|
|
5569
|
-
this.#
|
|
5570
|
-
this
|
|
5571
|
+
this.#ensure = new Ensure(compiler);
|
|
5572
|
+
this.#reject = new Reject(compiler, program, resolvedConfig);
|
|
5573
|
+
this.toAcceptProps = new ToAcceptProps(compiler);
|
|
5571
5574
|
this.toBe = new ToBe(compiler, program);
|
|
5572
5575
|
this.toBeApplicable = new ToBeApplicable(compiler);
|
|
5573
5576
|
this.toBeAssignableFrom = new ToBeAssignableFrom();
|
|
@@ -5580,23 +5583,24 @@ class ExpectService {
|
|
|
5580
5583
|
}
|
|
5581
5584
|
match(assertionNode, onDiagnostics) {
|
|
5582
5585
|
const matcherNameText = assertionNode.matcherNameNode.name.text;
|
|
5583
|
-
if (
|
|
5586
|
+
if (matcherNameText === "toAcceptProps" &&
|
|
5587
|
+
!this.#ensure.jsxSetup(this.#program, assertionNode.matcherNameNode.name, onDiagnostics)) {
|
|
5588
|
+
return;
|
|
5589
|
+
}
|
|
5590
|
+
if (!this.#ensure.argumentOrTypeArgument(assertionNode.source[0], assertionNode.node.expression, onDiagnostics)) {
|
|
5584
5591
|
return;
|
|
5585
5592
|
}
|
|
5586
|
-
const matchWorker = new MatchWorker(this.#compiler, this.#program, assertionNode);
|
|
5587
5593
|
if (!(matcherNameText === "toBeInstantiableWith" || (matcherNameText === "toRaiseError" && !assertionNode.isNot)) &&
|
|
5588
|
-
this.#reject.argumentType([
|
|
5589
|
-
["source", assertionNode.source[0]],
|
|
5590
|
-
["target", assertionNode.target?.[0]],
|
|
5591
|
-
], onDiagnostics)) {
|
|
5594
|
+
this.#reject.argumentType([assertionNode.source[0], assertionNode.target?.[0]], onDiagnostics)) {
|
|
5592
5595
|
return;
|
|
5593
5596
|
}
|
|
5597
|
+
const matchWorker = new MatchWorker(this.#compiler, this.#program, assertionNode);
|
|
5594
5598
|
switch (matcherNameText) {
|
|
5595
5599
|
case "toAcceptProps":
|
|
5596
5600
|
case "toBe":
|
|
5597
5601
|
case "toBeAssignableFrom":
|
|
5598
5602
|
case "toBeAssignableTo":
|
|
5599
|
-
if (!
|
|
5603
|
+
if (!this.#ensure.argumentOrTypeArgument(assertionNode.target?.[0], assertionNode.matcherNameNode.name, onDiagnostics)) {
|
|
5600
5604
|
return;
|
|
5601
5605
|
}
|
|
5602
5606
|
return this[matcherNameText].match(matchWorker, assertionNode.source[0], assertionNode.target[0], onDiagnostics);
|
|
@@ -5607,13 +5611,13 @@ class ExpectService {
|
|
|
5607
5611
|
case "toRaiseError":
|
|
5608
5612
|
return this[matcherNameText].match(matchWorker, assertionNode.source[0], assertionNode.target, onDiagnostics);
|
|
5609
5613
|
case "toBeInstantiableWith": {
|
|
5610
|
-
if (!
|
|
5614
|
+
if (!this.#ensure.typeArgument(assertionNode.target?.[0], assertionNode.matcherNameNode.name, onDiagnostics)) {
|
|
5611
5615
|
return;
|
|
5612
5616
|
}
|
|
5613
5617
|
return this.toBeInstantiableWith.match(matchWorker, assertionNode.source[0], assertionNode.target[0], onDiagnostics);
|
|
5614
5618
|
}
|
|
5615
5619
|
case "toHaveProperty":
|
|
5616
|
-
if (!
|
|
5620
|
+
if (!this.#ensure.argument(assertionNode.target?.[0], assertionNode.matcherNameNode.name, onDiagnostics)) {
|
|
5617
5621
|
return;
|
|
5618
5622
|
}
|
|
5619
5623
|
return this.toHaveProperty.match(matchWorker, assertionNode.source[0], assertionNode.target[0], onDiagnostics);
|
|
@@ -5629,67 +5633,6 @@ class ExpectService {
|
|
|
5629
5633
|
}
|
|
5630
5634
|
}
|
|
5631
5635
|
|
|
5632
|
-
function capitalize(text) {
|
|
5633
|
-
return text.replace(/^./, text.charAt(0).toUpperCase());
|
|
5634
|
-
}
|
|
5635
|
-
|
|
5636
|
-
class RejectDiagnosticText {
|
|
5637
|
-
static argumentCannotBeOfType(argumentNameText, typeText) {
|
|
5638
|
-
return `An argument for '${argumentNameText}' cannot be of the '${typeText}' type.`;
|
|
5639
|
-
}
|
|
5640
|
-
static typeArgumentCannotBeOfType(argumentNameText, typeText) {
|
|
5641
|
-
return `A type argument for '${argumentNameText}' cannot be of the '${typeText}' type.`;
|
|
5642
|
-
}
|
|
5643
|
-
static typeWasRejected(typeText) {
|
|
5644
|
-
const optionNameText = `reject${capitalize(typeText)}Type`;
|
|
5645
|
-
return [
|
|
5646
|
-
`The '${typeText}' type was rejected because the '${optionNameText}' option is enabled.`,
|
|
5647
|
-
`If this check is necessary, pass '${typeText}' as the type argument explicitly.`,
|
|
5648
|
-
];
|
|
5649
|
-
}
|
|
5650
|
-
}
|
|
5651
|
-
|
|
5652
|
-
class Reject {
|
|
5653
|
-
#compiler;
|
|
5654
|
-
#rejectedArgumentTypes = new Set();
|
|
5655
|
-
#typeChecker;
|
|
5656
|
-
constructor(compiler, program, resolvedConfig) {
|
|
5657
|
-
this.#compiler = compiler;
|
|
5658
|
-
this.#typeChecker = program.getTypeChecker();
|
|
5659
|
-
if (resolvedConfig.rejectAnyType) {
|
|
5660
|
-
this.#rejectedArgumentTypes.add("any");
|
|
5661
|
-
}
|
|
5662
|
-
if (resolvedConfig.rejectNeverType) {
|
|
5663
|
-
this.#rejectedArgumentTypes.add("never");
|
|
5664
|
-
}
|
|
5665
|
-
}
|
|
5666
|
-
argumentType(target, onDiagnostics) {
|
|
5667
|
-
for (const rejectedType of this.#rejectedArgumentTypes) {
|
|
5668
|
-
const allowedKeyword = this.#compiler.SyntaxKind[`${capitalize(rejectedType)}Keyword`];
|
|
5669
|
-
if (target.some(([, node]) => node?.kind === allowedKeyword)) {
|
|
5670
|
-
continue;
|
|
5671
|
-
}
|
|
5672
|
-
for (const [name, node] of target) {
|
|
5673
|
-
if (!node) {
|
|
5674
|
-
continue;
|
|
5675
|
-
}
|
|
5676
|
-
if (this.#typeChecker.getTypeAtLocation(node).flags & this.#compiler.TypeFlags[capitalize(rejectedType)]) {
|
|
5677
|
-
const text = [
|
|
5678
|
-
nodeBelongsToArgumentList(this.#compiler, node)
|
|
5679
|
-
? RejectDiagnosticText.argumentCannotBeOfType(name, rejectedType)
|
|
5680
|
-
: RejectDiagnosticText.typeArgumentCannotBeOfType(capitalize(name), rejectedType),
|
|
5681
|
-
...RejectDiagnosticText.typeWasRejected(rejectedType),
|
|
5682
|
-
];
|
|
5683
|
-
const origin = DiagnosticOrigin.fromNode(node);
|
|
5684
|
-
onDiagnostics([Diagnostic.error(text, origin)]);
|
|
5685
|
-
return true;
|
|
5686
|
-
}
|
|
5687
|
-
}
|
|
5688
|
-
}
|
|
5689
|
-
return false;
|
|
5690
|
-
}
|
|
5691
|
-
}
|
|
5692
|
-
|
|
5693
5636
|
class FixmeDiagnosticText {
|
|
5694
5637
|
static considerRemoving() {
|
|
5695
5638
|
return "Consider removing the '// @tstyche fixme' directive.";
|
|
@@ -5763,8 +5706,7 @@ class TestTreeWalker {
|
|
|
5763
5706
|
this.#cancellationToken = options.cancellationToken;
|
|
5764
5707
|
this.#hasOnly = options.hasOnly || resolvedConfig.only != null || options.position != null;
|
|
5765
5708
|
this.#position = options.position;
|
|
5766
|
-
|
|
5767
|
-
this.#expectService = new ExpectService(compiler, program, reject);
|
|
5709
|
+
this.#expectService = new ExpectService(compiler, program, resolvedConfig);
|
|
5768
5710
|
}
|
|
5769
5711
|
async #resolveRunMode(flags, node) {
|
|
5770
5712
|
const ifDirective = Directive.getDirectiveRange(this.#compiler, node, "if");
|
|
@@ -5998,7 +5940,7 @@ class FileRunner {
|
|
|
5998
5940
|
class Runner {
|
|
5999
5941
|
#eventEmitter = new EventEmitter();
|
|
6000
5942
|
#resolvedConfig;
|
|
6001
|
-
static version = "7.1
|
|
5943
|
+
static version = "7.2.1";
|
|
6002
5944
|
constructor(resolvedConfig) {
|
|
6003
5945
|
this.#resolvedConfig = resolvedConfig;
|
|
6004
5946
|
}
|
|
@@ -6223,4 +6165,4 @@ class Cli {
|
|
|
6223
6165
|
}
|
|
6224
6166
|
}
|
|
6225
6167
|
|
|
6226
|
-
export { BaseReporter, CancellationReason, CancellationToken, Cli, Color, Config, ConfigDiagnosticText, DescribeResult, Diagnostic, DiagnosticCategory, DiagnosticOrigin, Directive, DotReporter, EventEmitter, ExpectResult, FileLocation, FileResult, Line, ListReporter, OptionBrand, OptionGroup, Options, OutputService, Path, ProjectConfigKind, ProjectResult, Result, ResultStatus, Runner, Scribbler, Select, SelectDiagnosticText, SetupReporter, Store, StreamController, SummaryReporter, SuppressedResult, TargetResult, TestResult, Text, Version, WatchReporter, addsText, defaultOptions, describeNameText, diagnosticBelongsToNode, diagnosticText, dotText, environmentOptions, fileStatusText, formattedText, getDiagnosticMessageText, getTextSpanEnd, helpText, isDiagnosticWithLocation, prologueText, summaryText, testNameText, usesText, waitingForFileChangesText, watchUsageText };
|
|
6168
|
+
export { BaseReporter, CancellationReason, CancellationToken, Cli, Color, Config, ConfigDiagnosticText, DescribeResult, Diagnostic, DiagnosticCategory, DiagnosticOrigin, Directive, DotReporter, EventEmitter, ExpectResult, FileLocation, FileResult, Line, ListReporter, MappedDiagnostic, OptionBrand, OptionGroup, Options, OutputService, Path, ProjectConfigKind, ProjectResult, Result, ResultStatus, Runner, Scribbler, Select, SelectDiagnosticText, SetupReporter, Store, StreamController, SummaryReporter, SuppressedResult, TargetResult, TestResult, Text, Version, WatchReporter, addsText, defaultOptions, describeNameText, diagnosticBelongsToNode, diagnosticText, dotText, environmentOptions, fileStatusText, formattedText, getDiagnosticMessageText, getTextSpanEnd, helpText, isDiagnosticWithLocation, prologueText, summaryText, testNameText, usesText, waitingForFileChangesText, watchUsageText };
|