tstyche 1.0.0-beta.8 → 1.0.0-rc
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/CHANGELOG.md +24 -0
- package/README.md +13 -13
- package/build/bin.js +2 -1
- package/build/index.d.cts +6 -6
- package/build/index.d.ts +6 -6
- package/build/tstyche.d.ts +43 -43
- package/build/tstyche.js +316 -343
- package/package.json +21 -22
package/build/tstyche.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import process from 'node:process';
|
|
1
2
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
2
3
|
import { createRequire } from 'node:module';
|
|
3
4
|
import os from 'node:os';
|
|
@@ -24,9 +25,6 @@ class EventEmitter {
|
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
class Path {
|
|
27
|
-
static basename(filePath) {
|
|
28
|
-
return Path.normalizeSlashes(path.basename(filePath));
|
|
29
|
-
}
|
|
30
28
|
static dirname(filePath) {
|
|
31
29
|
return Path.normalizeSlashes(path.dirname(filePath));
|
|
32
30
|
}
|
|
@@ -52,46 +50,41 @@ class Path {
|
|
|
52
50
|
}
|
|
53
51
|
|
|
54
52
|
class Environment {
|
|
55
|
-
static #isTypeScriptInstalled = Environment.#resolveIsTypeScriptInstalled();
|
|
56
53
|
static #noColor = Environment.#resolveNoColor();
|
|
54
|
+
static #noInteractive = Environment.#resolveNoInteractive();
|
|
57
55
|
static #storePath = Environment.#resolveStorePath();
|
|
58
56
|
static #timeout = Environment.#resolveTimeout();
|
|
59
|
-
static
|
|
60
|
-
return Environment.#isTypeScriptInstalled;
|
|
61
|
-
}
|
|
57
|
+
static #typescriptPath = Environment.#resolveTypeScriptPath();
|
|
62
58
|
static get noColor() {
|
|
63
59
|
return Environment.#noColor;
|
|
64
60
|
}
|
|
61
|
+
static get noInteractive() {
|
|
62
|
+
return Environment.#noInteractive;
|
|
63
|
+
}
|
|
65
64
|
static get storePath() {
|
|
66
65
|
return Environment.#storePath;
|
|
67
66
|
}
|
|
68
67
|
static get timeout() {
|
|
69
68
|
return Environment.#timeout;
|
|
70
69
|
}
|
|
71
|
-
static
|
|
72
|
-
|
|
73
|
-
return ["1", "on", "t", "true", "y", "yes"].includes(value.toLowerCase());
|
|
74
|
-
}
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
static #resolveIsTypeScriptInstalled() {
|
|
78
|
-
try {
|
|
79
|
-
createRequire(import.meta.url).resolve("typescript");
|
|
80
|
-
return true;
|
|
81
|
-
}
|
|
82
|
-
catch {
|
|
83
|
-
return false;
|
|
84
|
-
}
|
|
70
|
+
static get typescriptPath() {
|
|
71
|
+
return Environment.#typescriptPath;
|
|
85
72
|
}
|
|
86
73
|
static #resolveNoColor() {
|
|
87
74
|
if (process.env["TSTYCHE_NO_COLOR"] != null) {
|
|
88
|
-
return
|
|
75
|
+
return process.env["TSTYCHE_NO_COLOR"] !== "";
|
|
89
76
|
}
|
|
90
|
-
if (process.env["NO_COLOR"] != null
|
|
91
|
-
return
|
|
77
|
+
if (process.env["NO_COLOR"] != null) {
|
|
78
|
+
return process.env["NO_COLOR"] !== "";
|
|
92
79
|
}
|
|
93
80
|
return false;
|
|
94
81
|
}
|
|
82
|
+
static #resolveNoInteractive() {
|
|
83
|
+
if (process.env["TSTYCHE_NO_INTERACTIVE"] != null) {
|
|
84
|
+
return process.env["TSTYCHE_NO_INTERACTIVE"] !== "";
|
|
85
|
+
}
|
|
86
|
+
return !process.stdout.isTTY;
|
|
87
|
+
}
|
|
95
88
|
static #resolveStorePath() {
|
|
96
89
|
if (process.env["TSTYCHE_STORE_PATH"] != null) {
|
|
97
90
|
return Path.resolve(process.env["TSTYCHE_STORE_PATH"]);
|
|
@@ -113,6 +106,19 @@ class Environment {
|
|
|
113
106
|
}
|
|
114
107
|
return 30;
|
|
115
108
|
}
|
|
109
|
+
static #resolveTypeScriptPath() {
|
|
110
|
+
let moduleId = "typescript";
|
|
111
|
+
if (process.env["TSTYCHE_TYPESCRIPT_PATH"] != null) {
|
|
112
|
+
moduleId = process.env["TSTYCHE_TYPESCRIPT_PATH"];
|
|
113
|
+
}
|
|
114
|
+
let resolvedPath;
|
|
115
|
+
try {
|
|
116
|
+
resolvedPath = createRequire(import.meta.url).resolve(moduleId);
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
}
|
|
120
|
+
return resolvedPath;
|
|
121
|
+
}
|
|
116
122
|
}
|
|
117
123
|
|
|
118
124
|
var Color;
|
|
@@ -128,9 +134,11 @@ var Color;
|
|
|
128
134
|
})(Color || (Color = {}));
|
|
129
135
|
|
|
130
136
|
class Scribbler {
|
|
137
|
+
#newLine;
|
|
131
138
|
#noColor;
|
|
132
139
|
constructor(options) {
|
|
133
|
-
this.#
|
|
140
|
+
this.#newLine = options?.newLine ?? "\n";
|
|
141
|
+
this.#noColor = options?.noColor ?? false;
|
|
134
142
|
}
|
|
135
143
|
static createElement(type, props, ...children) {
|
|
136
144
|
return {
|
|
@@ -165,8 +173,8 @@ class Scribbler {
|
|
|
165
173
|
return this.#escapeSequence(flags);
|
|
166
174
|
}
|
|
167
175
|
}
|
|
168
|
-
if (element.type === "
|
|
169
|
-
return
|
|
176
|
+
if (element.type === "newLine") {
|
|
177
|
+
return this.#newLine;
|
|
170
178
|
}
|
|
171
179
|
if (element.type === "text") {
|
|
172
180
|
const indentLevel = typeof element.props?.["indent"] === "number" ? element.props["indent"] : 0;
|
|
@@ -233,7 +241,7 @@ class Line {
|
|
|
233
241
|
render() {
|
|
234
242
|
return (Scribbler.createElement("text", null,
|
|
235
243
|
Scribbler.createElement(Text, { color: this.props.color, indent: this.props.indent }, this.props.children),
|
|
236
|
-
Scribbler.createElement("
|
|
244
|
+
Scribbler.createElement("newLine", null)));
|
|
237
245
|
}
|
|
238
246
|
}
|
|
239
247
|
|
|
@@ -246,20 +254,11 @@ class Logger {
|
|
|
246
254
|
this.#noColor = options?.noColor ?? Environment.noColor;
|
|
247
255
|
this.#stderr = options?.stderr ?? process.stderr;
|
|
248
256
|
this.#stdout = options?.stdout ?? process.stdout;
|
|
249
|
-
this.#scribbler = new Scribbler({
|
|
257
|
+
this.#scribbler = new Scribbler({ noColor: this.#noColor });
|
|
250
258
|
}
|
|
251
259
|
eraseLastLine() {
|
|
252
|
-
if (!this.isInteractive()) {
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
260
|
this.#stdout.write("\u001B[1A\u001B[0K");
|
|
256
261
|
}
|
|
257
|
-
isInteractive() {
|
|
258
|
-
if ("isTTY" in this.#stdout && typeof this.#stdout.isTTY === "boolean") {
|
|
259
|
-
return this.#stdout.isTTY;
|
|
260
|
-
}
|
|
261
|
-
return false;
|
|
262
|
-
}
|
|
263
262
|
#write(stream, body) {
|
|
264
263
|
const elements = Array.isArray(body) ? body : [body];
|
|
265
264
|
for (const element of elements) {
|
|
@@ -346,7 +345,7 @@ class CodeSpanText {
|
|
|
346
345
|
}
|
|
347
346
|
}
|
|
348
347
|
const breadcrumbs = this.props.breadcrumbs?.flatMap((ancestor) => [
|
|
349
|
-
Scribbler.createElement(Text, { color: "90" }, "
|
|
348
|
+
Scribbler.createElement(Text, { color: "90" }, " ❭ "),
|
|
350
349
|
Scribbler.createElement(Text, null, ancestor),
|
|
351
350
|
]);
|
|
352
351
|
const location = (Scribbler.createElement(Line, null,
|
|
@@ -373,9 +372,11 @@ class DiagnosticText {
|
|
|
373
372
|
this.props = props;
|
|
374
373
|
}
|
|
375
374
|
render() {
|
|
376
|
-
const code = typeof this.props.diagnostic.code === "string"
|
|
377
|
-
"
|
|
378
|
-
|
|
375
|
+
const code = typeof this.props.diagnostic.code === "string"
|
|
376
|
+
? Scribbler.createElement(Text, { color: "90" },
|
|
377
|
+
" ",
|
|
378
|
+
this.props.diagnostic.code)
|
|
379
|
+
: undefined;
|
|
379
380
|
const text = Array.isArray(this.props.diagnostic.text) ? this.props.diagnostic.text : [this.props.diagnostic.text];
|
|
380
381
|
const message = text.map((text, index) => (Scribbler.createElement(Text, null,
|
|
381
382
|
index === 1 ? Scribbler.createElement(Line, null) : undefined,
|
|
@@ -383,9 +384,11 @@ class DiagnosticText {
|
|
|
383
384
|
text,
|
|
384
385
|
code))));
|
|
385
386
|
const related = this.props.diagnostic.related?.map((relatedDiagnostic) => (Scribbler.createElement(DiagnosticText, { diagnostic: relatedDiagnostic })));
|
|
386
|
-
const codeSpan = this.props.diagnostic.origin
|
|
387
|
-
Scribbler.createElement(
|
|
388
|
-
|
|
387
|
+
const codeSpan = this.props.diagnostic.origin
|
|
388
|
+
? (Scribbler.createElement(Text, null,
|
|
389
|
+
Scribbler.createElement(Line, null),
|
|
390
|
+
Scribbler.createElement(CodeSpanText, { ...this.props.diagnostic.origin })))
|
|
391
|
+
: undefined;
|
|
389
392
|
return (Scribbler.createElement(Text, null,
|
|
390
393
|
message,
|
|
391
394
|
codeSpan,
|
|
@@ -482,7 +485,7 @@ function formattedText(input) {
|
|
|
482
485
|
const usageExamples = [
|
|
483
486
|
["tstyche", "Run all tests."],
|
|
484
487
|
["tstyche path/to/first.test.ts", "Only run the test files with matching path."],
|
|
485
|
-
["tstyche --target 4.
|
|
488
|
+
["tstyche --target 4.9,5.3.2,current", "Test on all specified versions of TypeScript."],
|
|
486
489
|
];
|
|
487
490
|
class HintText {
|
|
488
491
|
props;
|
|
@@ -624,26 +627,34 @@ class CountText {
|
|
|
624
627
|
}
|
|
625
628
|
render() {
|
|
626
629
|
return (Scribbler.createElement(Text, null,
|
|
627
|
-
this.props.failed > 0
|
|
628
|
-
Scribbler.createElement(Text,
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
630
|
+
this.props.failed > 0
|
|
631
|
+
? (Scribbler.createElement(Text, null,
|
|
632
|
+
Scribbler.createElement(Text, { color: "31" },
|
|
633
|
+
String(this.props.failed),
|
|
634
|
+
" failed"),
|
|
635
|
+
Scribbler.createElement(Text, null, ", ")))
|
|
636
|
+
: undefined,
|
|
637
|
+
this.props.skipped > 0
|
|
638
|
+
? (Scribbler.createElement(Text, null,
|
|
639
|
+
Scribbler.createElement(Text, { color: "33" },
|
|
640
|
+
String(this.props.skipped),
|
|
641
|
+
" skipped"),
|
|
642
|
+
Scribbler.createElement(Text, null, ", ")))
|
|
643
|
+
: undefined,
|
|
644
|
+
this.props.todo > 0
|
|
645
|
+
? (Scribbler.createElement(Text, null,
|
|
646
|
+
Scribbler.createElement(Text, { color: "35" },
|
|
647
|
+
String(this.props.todo),
|
|
648
|
+
" todo"),
|
|
649
|
+
Scribbler.createElement(Text, null, ", ")))
|
|
650
|
+
: undefined,
|
|
651
|
+
this.props.passed > 0
|
|
652
|
+
? (Scribbler.createElement(Text, null,
|
|
653
|
+
Scribbler.createElement(Text, { color: "32" },
|
|
654
|
+
String(this.props.passed),
|
|
655
|
+
" passed"),
|
|
656
|
+
Scribbler.createElement(Text, null, ", ")))
|
|
657
|
+
: undefined,
|
|
647
658
|
Scribbler.createElement(Text, null,
|
|
648
659
|
String(this.props.total),
|
|
649
660
|
Scribbler.createElement(Text, null, " total"))));
|
|
@@ -815,92 +826,6 @@ class SummaryReporter extends Reporter {
|
|
|
815
826
|
}
|
|
816
827
|
}
|
|
817
828
|
|
|
818
|
-
class Diagnostic {
|
|
819
|
-
text;
|
|
820
|
-
category;
|
|
821
|
-
origin;
|
|
822
|
-
code;
|
|
823
|
-
related;
|
|
824
|
-
constructor(text, category, origin) {
|
|
825
|
-
this.text = text;
|
|
826
|
-
this.category = category;
|
|
827
|
-
this.origin = origin;
|
|
828
|
-
}
|
|
829
|
-
add(options) {
|
|
830
|
-
if (options.code != null) {
|
|
831
|
-
this.code = options.code;
|
|
832
|
-
}
|
|
833
|
-
if (options.origin != null) {
|
|
834
|
-
this.origin = options.origin;
|
|
835
|
-
}
|
|
836
|
-
if (options.related != null) {
|
|
837
|
-
this.related = options.related;
|
|
838
|
-
}
|
|
839
|
-
return this;
|
|
840
|
-
}
|
|
841
|
-
static error(text, origin) {
|
|
842
|
-
return new Diagnostic(text, "error", origin);
|
|
843
|
-
}
|
|
844
|
-
static fromDiagnostics(diagnostics, compiler) {
|
|
845
|
-
return diagnostics.map((diagnostic) => {
|
|
846
|
-
let category;
|
|
847
|
-
switch (diagnostic.category) {
|
|
848
|
-
case compiler.DiagnosticCategory.Error:
|
|
849
|
-
category = "error";
|
|
850
|
-
break;
|
|
851
|
-
default:
|
|
852
|
-
category = "warning";
|
|
853
|
-
}
|
|
854
|
-
const code = `ts(${diagnostic.code})`;
|
|
855
|
-
const text = compiler.flattenDiagnosticMessageText(diagnostic.messageText, "\r\n");
|
|
856
|
-
if (Diagnostic.isTsDiagnosticWithLocation(diagnostic)) {
|
|
857
|
-
const origin = {
|
|
858
|
-
end: diagnostic.start + diagnostic.length,
|
|
859
|
-
file: diagnostic.file,
|
|
860
|
-
start: diagnostic.start,
|
|
861
|
-
};
|
|
862
|
-
return new Diagnostic(text, category, origin).add({ code });
|
|
863
|
-
}
|
|
864
|
-
return new Diagnostic(text, category).add({ code });
|
|
865
|
-
});
|
|
866
|
-
}
|
|
867
|
-
static fromError(text, error) {
|
|
868
|
-
const messageText = Array.isArray(text) ? text : [text];
|
|
869
|
-
if (error instanceof Error) {
|
|
870
|
-
if (error.cause != null) {
|
|
871
|
-
messageText.push(this.#normalizeMessage(String(error.cause)));
|
|
872
|
-
}
|
|
873
|
-
messageText.push(this.#normalizeMessage(String(error.message)));
|
|
874
|
-
if (error.stack != null) {
|
|
875
|
-
const stackLines = error.stack
|
|
876
|
-
.split("\n")
|
|
877
|
-
.slice(1)
|
|
878
|
-
.map((line) => line.trimStart());
|
|
879
|
-
messageText.push(...stackLines);
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
return Diagnostic.error(messageText);
|
|
883
|
-
}
|
|
884
|
-
static isTsDiagnosticWithLocation(diagnostic) {
|
|
885
|
-
return diagnostic.file != null && diagnostic.start != null && diagnostic.length != null;
|
|
886
|
-
}
|
|
887
|
-
static #normalizeMessage(text) {
|
|
888
|
-
if (text.endsWith(".")) {
|
|
889
|
-
return text;
|
|
890
|
-
}
|
|
891
|
-
return `${text}.`;
|
|
892
|
-
}
|
|
893
|
-
static warning(text, origin) {
|
|
894
|
-
return new Diagnostic(text, "warning", origin);
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
var DiagnosticCategory;
|
|
899
|
-
(function (DiagnosticCategory) {
|
|
900
|
-
DiagnosticCategory["Error"] = "error";
|
|
901
|
-
DiagnosticCategory["Warning"] = "warning";
|
|
902
|
-
})(DiagnosticCategory || (DiagnosticCategory = {}));
|
|
903
|
-
|
|
904
829
|
class FileViewService {
|
|
905
830
|
#indent = 0;
|
|
906
831
|
#lines = [];
|
|
@@ -967,19 +892,13 @@ class ThoroughReporter extends Reporter {
|
|
|
967
892
|
this.#currentProjectConfigFilePath = undefined;
|
|
968
893
|
break;
|
|
969
894
|
case "project:info":
|
|
970
|
-
if (this.#currentCompilerVersion !== payload.compilerVersion
|
|
971
|
-
this.#currentProjectConfigFilePath !== payload.projectConfigFilePath) {
|
|
895
|
+
if (this.#currentCompilerVersion !== payload.compilerVersion
|
|
896
|
+
|| this.#currentProjectConfigFilePath !== payload.projectConfigFilePath) {
|
|
972
897
|
this.logger.writeMessage(usesCompilerStepText(payload.compilerVersion, payload.projectConfigFilePath, {
|
|
973
|
-
prependEmptyLine: this.#currentCompilerVersion != null && !this.#hasReportedAdds
|
|
898
|
+
prependEmptyLine: this.#currentCompilerVersion != null && !this.#hasReportedAdds
|
|
899
|
+
&& !this.#hasReportedError,
|
|
974
900
|
}));
|
|
975
901
|
this.#hasReportedAdds = false;
|
|
976
|
-
if (payload.projectConfigFilePath == null) {
|
|
977
|
-
const text = [
|
|
978
|
-
"The default compiler options are used for the following tests files.",
|
|
979
|
-
"Make sure that 'tsconfig.json' exists and the test files are included in the program.",
|
|
980
|
-
];
|
|
981
|
-
this.logger.writeWarning(diagnosticText(Diagnostic.warning(text)));
|
|
982
|
-
}
|
|
983
902
|
this.#currentCompilerVersion = payload.compilerVersion;
|
|
984
903
|
this.#currentProjectConfigFilePath = payload.projectConfigFilePath;
|
|
985
904
|
}
|
|
@@ -990,7 +909,7 @@ class ThoroughReporter extends Reporter {
|
|
|
990
909
|
}
|
|
991
910
|
break;
|
|
992
911
|
case "file:start":
|
|
993
|
-
if (
|
|
912
|
+
if (!Environment.noInteractive) {
|
|
994
913
|
this.logger.writeMessage(fileStatusText(payload.result.status, payload.result.testFile));
|
|
995
914
|
}
|
|
996
915
|
this.#fileCount--;
|
|
@@ -1002,7 +921,9 @@ class ThoroughReporter extends Reporter {
|
|
|
1002
921
|
}
|
|
1003
922
|
break;
|
|
1004
923
|
case "file:end":
|
|
1005
|
-
|
|
924
|
+
if (!Environment.noInteractive) {
|
|
925
|
+
this.logger.eraseLastLine();
|
|
926
|
+
}
|
|
1006
927
|
this.logger.writeMessage(fileStatusText(payload.result.status, payload.result.testFile));
|
|
1007
928
|
this.logger.writeMessage(this.#fileView.getViewText({ appendEmptyLine: this.#isLastFile }));
|
|
1008
929
|
if (this.#fileView.hasErrors) {
|
|
@@ -1204,9 +1125,9 @@ class ResultManager {
|
|
|
1204
1125
|
this.#fileResult.diagnostics.push(...payload.diagnostics);
|
|
1205
1126
|
break;
|
|
1206
1127
|
case "file:end":
|
|
1207
|
-
if (this.#fileResult.status === "failed"
|
|
1208
|
-
this.#fileResult.expectCount.failed > 0
|
|
1209
|
-
this.#fileResult.testCount.failed > 0) {
|
|
1128
|
+
if (this.#fileResult.status === "failed"
|
|
1129
|
+
|| this.#fileResult.expectCount.failed > 0
|
|
1130
|
+
|| this.#fileResult.testCount.failed > 0) {
|
|
1210
1131
|
this.#result.fileCount.failed++;
|
|
1211
1132
|
this.#targetResult.status = "failed";
|
|
1212
1133
|
this.#fileResult.status = "failed";
|
|
@@ -1368,6 +1289,85 @@ class TestResult {
|
|
|
1368
1289
|
}
|
|
1369
1290
|
}
|
|
1370
1291
|
|
|
1292
|
+
class Diagnostic {
|
|
1293
|
+
text;
|
|
1294
|
+
category;
|
|
1295
|
+
origin;
|
|
1296
|
+
code;
|
|
1297
|
+
related;
|
|
1298
|
+
constructor(text, category, origin) {
|
|
1299
|
+
this.text = text;
|
|
1300
|
+
this.category = category;
|
|
1301
|
+
this.origin = origin;
|
|
1302
|
+
}
|
|
1303
|
+
add(options) {
|
|
1304
|
+
if (options.code != null) {
|
|
1305
|
+
this.code = options.code;
|
|
1306
|
+
}
|
|
1307
|
+
if (options.origin != null) {
|
|
1308
|
+
this.origin = options.origin;
|
|
1309
|
+
}
|
|
1310
|
+
if (options.related != null) {
|
|
1311
|
+
this.related = options.related;
|
|
1312
|
+
}
|
|
1313
|
+
return this;
|
|
1314
|
+
}
|
|
1315
|
+
static error(text, origin) {
|
|
1316
|
+
return new Diagnostic(text, "error", origin);
|
|
1317
|
+
}
|
|
1318
|
+
static fromDiagnostics(diagnostics, compiler) {
|
|
1319
|
+
return diagnostics.map((diagnostic) => {
|
|
1320
|
+
const category = "error";
|
|
1321
|
+
const code = `ts(${diagnostic.code})`;
|
|
1322
|
+
let origin;
|
|
1323
|
+
const text = compiler.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
|
|
1324
|
+
if (Diagnostic.isTsDiagnosticWithLocation(diagnostic)) {
|
|
1325
|
+
origin = {
|
|
1326
|
+
end: diagnostic.start + diagnostic.length,
|
|
1327
|
+
file: diagnostic.file,
|
|
1328
|
+
start: diagnostic.start,
|
|
1329
|
+
};
|
|
1330
|
+
}
|
|
1331
|
+
return new Diagnostic(text, category, origin).add({ code });
|
|
1332
|
+
});
|
|
1333
|
+
}
|
|
1334
|
+
static fromError(text, error) {
|
|
1335
|
+
const messageText = Array.isArray(text) ? text : [text];
|
|
1336
|
+
if (error instanceof Error) {
|
|
1337
|
+
if (error.cause != null) {
|
|
1338
|
+
messageText.push(this.#normalizeMessage(String(error.cause)));
|
|
1339
|
+
}
|
|
1340
|
+
messageText.push(this.#normalizeMessage(String(error.message)));
|
|
1341
|
+
if (error.stack != null) {
|
|
1342
|
+
const stackLines = error.stack
|
|
1343
|
+
.split("\n")
|
|
1344
|
+
.slice(1)
|
|
1345
|
+
.map((line) => line.trimStart());
|
|
1346
|
+
messageText.push(...stackLines);
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
return Diagnostic.error(messageText);
|
|
1350
|
+
}
|
|
1351
|
+
static isTsDiagnosticWithLocation(diagnostic) {
|
|
1352
|
+
return diagnostic.file != null && diagnostic.start != null && diagnostic.length != null;
|
|
1353
|
+
}
|
|
1354
|
+
static #normalizeMessage(text) {
|
|
1355
|
+
if (text.endsWith(".")) {
|
|
1356
|
+
return text;
|
|
1357
|
+
}
|
|
1358
|
+
return `${text}.`;
|
|
1359
|
+
}
|
|
1360
|
+
static warning(text, origin) {
|
|
1361
|
+
return new Diagnostic(text, "warning", origin);
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
var DiagnosticCategory;
|
|
1366
|
+
(function (DiagnosticCategory) {
|
|
1367
|
+
DiagnosticCategory["Error"] = "error";
|
|
1368
|
+
DiagnosticCategory["Warning"] = "warning";
|
|
1369
|
+
})(DiagnosticCategory || (DiagnosticCategory = {}));
|
|
1370
|
+
|
|
1371
1371
|
class TestMember {
|
|
1372
1372
|
brand;
|
|
1373
1373
|
node;
|
|
@@ -1386,9 +1386,9 @@ class TestMember {
|
|
|
1386
1386
|
if (node.arguments[0] != null && this.compiler.isStringLiteralLike(node.arguments[0])) {
|
|
1387
1387
|
this.name = node.arguments[0].text;
|
|
1388
1388
|
}
|
|
1389
|
-
if (node.arguments[1] != null
|
|
1390
|
-
parent.compiler.isFunctionLike(node.arguments[1])
|
|
1391
|
-
parent.compiler.isBlock(node.arguments[1].body)) {
|
|
1389
|
+
if (node.arguments[1] != null
|
|
1390
|
+
&& parent.compiler.isFunctionLike(node.arguments[1])
|
|
1391
|
+
&& parent.compiler.isBlock(node.arguments[1].body)) {
|
|
1392
1392
|
const blockStart = node.arguments[1].body.getStart();
|
|
1393
1393
|
const blockEnd = node.arguments[1].body.getEnd();
|
|
1394
1394
|
for (const diagnostic of parent.diagnostics) {
|
|
@@ -1455,11 +1455,11 @@ class Assertion extends TestMember {
|
|
|
1455
1455
|
const argStart = this.source[0]?.getStart();
|
|
1456
1456
|
const argEnd = this.source[0]?.getEnd();
|
|
1457
1457
|
for (const diagnostic of parent.diagnostics) {
|
|
1458
|
-
if (diagnostic.start != null
|
|
1459
|
-
argStart != null
|
|
1460
|
-
argEnd != null
|
|
1461
|
-
diagnostic.start >= argStart
|
|
1462
|
-
diagnostic.start <= argEnd) {
|
|
1458
|
+
if (diagnostic.start != null
|
|
1459
|
+
&& argStart != null
|
|
1460
|
+
&& argEnd != null
|
|
1461
|
+
&& diagnostic.start >= argStart
|
|
1462
|
+
&& diagnostic.start <= argEnd) {
|
|
1463
1463
|
this.diagnostics.add(diagnostic);
|
|
1464
1464
|
parent.diagnostics.delete(diagnostic);
|
|
1465
1465
|
}
|
|
@@ -1516,9 +1516,9 @@ class IdentifierLookup {
|
|
|
1516
1516
|
};
|
|
1517
1517
|
}
|
|
1518
1518
|
handleImportDeclaration(node) {
|
|
1519
|
-
if (this.#moduleSpecifiers.includes(node.moduleSpecifier.getText())
|
|
1520
|
-
node.importClause?.isTypeOnly !== true
|
|
1521
|
-
node.importClause?.namedBindings != null) {
|
|
1519
|
+
if (this.#moduleSpecifiers.includes(node.moduleSpecifier.getText())
|
|
1520
|
+
&& node.importClause?.isTypeOnly !== true
|
|
1521
|
+
&& node.importClause?.namedBindings != null) {
|
|
1522
1522
|
if (this.compiler.isNamedImports(node.importClause.namedBindings)) {
|
|
1523
1523
|
for (const element of node.importClause.namedBindings.elements) {
|
|
1524
1524
|
if (element.isTypeOnly) {
|
|
@@ -1565,8 +1565,8 @@ class IdentifierLookup {
|
|
|
1565
1565
|
expression = expression.expression;
|
|
1566
1566
|
}
|
|
1567
1567
|
let identifierName;
|
|
1568
|
-
if (this.compiler.isPropertyAccessExpression(expression)
|
|
1569
|
-
expression.expression.getText() === this.#identifiers.namespace) {
|
|
1568
|
+
if (this.compiler.isPropertyAccessExpression(expression)
|
|
1569
|
+
&& expression.expression.getText() === this.#identifiers.namespace) {
|
|
1570
1570
|
identifierName = expression.name.getText();
|
|
1571
1571
|
}
|
|
1572
1572
|
else {
|
|
@@ -2029,8 +2029,8 @@ class Expect {
|
|
|
2029
2029
|
}
|
|
2030
2030
|
const sourceType = this.#getType(assertion.source[0]);
|
|
2031
2031
|
const nonPrimitiveType = { flags: this.compiler.TypeFlags.NonPrimitive };
|
|
2032
|
-
if (sourceType.flags & (this.compiler.TypeFlags.Any | this.compiler.TypeFlags.Never)
|
|
2033
|
-
!this.typeChecker.isTypeAssignableTo(sourceType, nonPrimitiveType)) {
|
|
2032
|
+
if (sourceType.flags & (this.compiler.TypeFlags.Any | this.compiler.TypeFlags.Never)
|
|
2033
|
+
|| !this.typeChecker.isTypeAssignableTo(sourceType, nonPrimitiveType)) {
|
|
2034
2034
|
this.#onSourceArgumentMustBeObjectType(assertion.source[0], expectResult);
|
|
2035
2035
|
return;
|
|
2036
2036
|
}
|
|
@@ -2162,6 +2162,37 @@ class Expect {
|
|
|
2162
2162
|
}
|
|
2163
2163
|
}
|
|
2164
2164
|
|
|
2165
|
+
class Version {
|
|
2166
|
+
static isGreaterThan(source, target) {
|
|
2167
|
+
return !(source === target) && Version.#satisfies(source, target);
|
|
2168
|
+
}
|
|
2169
|
+
static isSatisfiedWith(source, target) {
|
|
2170
|
+
return source === target || Version.#satisfies(source, target);
|
|
2171
|
+
}
|
|
2172
|
+
static isVersionTag(target) {
|
|
2173
|
+
return /^\d+/.test(target);
|
|
2174
|
+
}
|
|
2175
|
+
static #satisfies(source, target) {
|
|
2176
|
+
const sourceElements = source.split(/\.|-/);
|
|
2177
|
+
const targetElements = target.split(/\.|-/);
|
|
2178
|
+
function compare(index = 0) {
|
|
2179
|
+
const sourceElement = sourceElements[index];
|
|
2180
|
+
const targetElement = targetElements[index];
|
|
2181
|
+
if (sourceElement > targetElement) {
|
|
2182
|
+
return true;
|
|
2183
|
+
}
|
|
2184
|
+
if (sourceElement < targetElement) {
|
|
2185
|
+
return false;
|
|
2186
|
+
}
|
|
2187
|
+
if (index === sourceElements.length - 1 || index === targetElements.length - 1) {
|
|
2188
|
+
return true;
|
|
2189
|
+
}
|
|
2190
|
+
return compare(index + 1);
|
|
2191
|
+
}
|
|
2192
|
+
return compare();
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2165
2196
|
class ProjectService {
|
|
2166
2197
|
compiler;
|
|
2167
2198
|
#service;
|
|
@@ -2205,10 +2236,30 @@ class ProjectService {
|
|
|
2205
2236
|
useInferredProjectPerProjectRoot: true,
|
|
2206
2237
|
useSingleInferredProject: false,
|
|
2207
2238
|
});
|
|
2239
|
+
this.#service.setCompilerOptionsForInferredProjects(this.#getDefaultCompilerOptions());
|
|
2208
2240
|
}
|
|
2209
2241
|
closeFile(filePath) {
|
|
2210
2242
|
this.#service.closeClientFile(filePath);
|
|
2211
2243
|
}
|
|
2244
|
+
#getDefaultCompilerOptions() {
|
|
2245
|
+
const defaultCompilerOptions = {
|
|
2246
|
+
allowJs: true,
|
|
2247
|
+
checkJs: true,
|
|
2248
|
+
esModuleInterop: true,
|
|
2249
|
+
jsx: "preserve",
|
|
2250
|
+
module: "esnext",
|
|
2251
|
+
moduleResolution: "node",
|
|
2252
|
+
resolveJsonModule: true,
|
|
2253
|
+
strictFunctionTypes: true,
|
|
2254
|
+
strictNullChecks: true,
|
|
2255
|
+
target: "esnext",
|
|
2256
|
+
};
|
|
2257
|
+
if (Version.isSatisfiedWith(this.compiler.version, "5")) {
|
|
2258
|
+
defaultCompilerOptions["allowImportingTsExtensions"] = true;
|
|
2259
|
+
defaultCompilerOptions.moduleResolution = "bundler";
|
|
2260
|
+
}
|
|
2261
|
+
return defaultCompilerOptions;
|
|
2262
|
+
}
|
|
2212
2263
|
getDefaultProject(filePath) {
|
|
2213
2264
|
return this.#service.getDefaultProjectForFile(this.compiler.server.toNormalizedPath(filePath), true);
|
|
2214
2265
|
}
|
|
@@ -2255,14 +2306,15 @@ class TestTreeWorker {
|
|
|
2255
2306
|
if (member.flags & 1) {
|
|
2256
2307
|
mode |= 1;
|
|
2257
2308
|
}
|
|
2258
|
-
if (member.flags & 2
|
|
2259
|
-
(this.resolvedConfig.only != null
|
|
2260
|
-
member.name.toLowerCase().includes(this.resolvedConfig.only.toLowerCase()))
|
|
2261
|
-
(this.#position != null && member.node.getStart() === this.#position)) {
|
|
2309
|
+
if (member.flags & 2
|
|
2310
|
+
|| (this.resolvedConfig.only != null
|
|
2311
|
+
&& member.name.toLowerCase().includes(this.resolvedConfig.only.toLowerCase()))
|
|
2312
|
+
|| (this.#position != null && member.node.getStart() === this.#position)) {
|
|
2262
2313
|
mode |= 2;
|
|
2263
2314
|
}
|
|
2264
|
-
if (member.flags & 4
|
|
2265
|
-
(this.resolvedConfig.skip != null
|
|
2315
|
+
if (member.flags & 4
|
|
2316
|
+
|| (this.resolvedConfig.skip != null
|
|
2317
|
+
&& member.name.toLowerCase().includes(this.resolvedConfig.skip.toLowerCase()))) {
|
|
2266
2318
|
mode |= 4;
|
|
2267
2319
|
}
|
|
2268
2320
|
if (member.flags & 8) {
|
|
@@ -2364,8 +2416,8 @@ class TestTreeWorker {
|
|
|
2364
2416
|
const describeResult = new DescribeResult(describe, parentResult);
|
|
2365
2417
|
EventEmitter.dispatch(["describe:start", { result: describeResult }]);
|
|
2366
2418
|
runMode = this.#resolveRunMode(runMode, describe);
|
|
2367
|
-
if (!(runMode & 4 || (this.#hasOnly && !(runMode & 2)) || runMode & 8)
|
|
2368
|
-
describe.diagnostics.size > 0) {
|
|
2419
|
+
if (!(runMode & 4 || (this.#hasOnly && !(runMode & 2)) || runMode & 8)
|
|
2420
|
+
&& describe.diagnostics.size > 0) {
|
|
2369
2421
|
EventEmitter.dispatch([
|
|
2370
2422
|
"file:error",
|
|
2371
2423
|
{
|
|
@@ -2525,7 +2577,7 @@ class TSTyche {
|
|
|
2525
2577
|
#abortController = new AbortController();
|
|
2526
2578
|
#storeService;
|
|
2527
2579
|
#taskRunner;
|
|
2528
|
-
static version = "1.0.0-
|
|
2580
|
+
static version = "1.0.0-rc";
|
|
2529
2581
|
constructor(resolvedConfig, storeService) {
|
|
2530
2582
|
this.resolvedConfig = resolvedConfig;
|
|
2531
2583
|
this.#storeService = storeService;
|
|
@@ -2535,8 +2587,8 @@ class TSTyche {
|
|
|
2535
2587
|
#addEventHandlers() {
|
|
2536
2588
|
EventEmitter.addHandler(([eventName, payload]) => {
|
|
2537
2589
|
if (eventName.includes("error") || eventName.includes("fail")) {
|
|
2538
|
-
if ("diagnostics" in payload
|
|
2539
|
-
!payload.diagnostics.some(({ category }) => category === "error")) {
|
|
2590
|
+
if ("diagnostics" in payload
|
|
2591
|
+
&& !payload.diagnostics.some(({ category }) => category === "error")) {
|
|
2540
2592
|
return;
|
|
2541
2593
|
}
|
|
2542
2594
|
process.exitCode = 1;
|
|
@@ -2570,18 +2622,18 @@ class TSTyche {
|
|
|
2570
2622
|
|
|
2571
2623
|
class OptionDefinitionsMap {
|
|
2572
2624
|
static #definitions = [
|
|
2573
|
-
{
|
|
2574
|
-
brand: "boolean",
|
|
2575
|
-
description: "Do not raise an error, if no test files are selected.",
|
|
2576
|
-
group: 4 | 2,
|
|
2577
|
-
name: "allowNoTestFiles",
|
|
2578
|
-
},
|
|
2579
2625
|
{
|
|
2580
2626
|
brand: "string",
|
|
2581
2627
|
description: "The path to a TSTyche configuration file.",
|
|
2582
2628
|
group: 2,
|
|
2583
2629
|
name: "config",
|
|
2584
2630
|
},
|
|
2631
|
+
{
|
|
2632
|
+
brand: "boolean",
|
|
2633
|
+
description: "Do not search for the test files.",
|
|
2634
|
+
group: 4,
|
|
2635
|
+
name: "disableTestFileLookup",
|
|
2636
|
+
},
|
|
2585
2637
|
{
|
|
2586
2638
|
brand: "boolean",
|
|
2587
2639
|
description: "Stop running tests after the first failed assertion.",
|
|
@@ -2714,10 +2766,10 @@ class OptionDiagnosticText {
|
|
|
2714
2766
|
unknownOption(optionName) {
|
|
2715
2767
|
return `Unknown option '${optionName}'.`;
|
|
2716
2768
|
}
|
|
2717
|
-
unknownProperty(optionName) {
|
|
2718
|
-
return `Unknown property '${optionName}'.`;
|
|
2719
|
-
}
|
|
2720
2769
|
versionIsNotSupported(value) {
|
|
2770
|
+
if (value === "current") {
|
|
2771
|
+
return "Cannot use 'current' as a target. Failed to resolve the path to the currently installed TypeScript module.";
|
|
2772
|
+
}
|
|
2721
2773
|
return `TypeScript version '${value}' is not supported.`;
|
|
2722
2774
|
}
|
|
2723
2775
|
}
|
|
@@ -2739,7 +2791,7 @@ class OptionUsageText {
|
|
|
2739
2791
|
const supportedTagsText = `Supported tags: ${["'", supportedTags.join("', '"), "'"].join("")}.`;
|
|
2740
2792
|
switch (this.#optionGroup) {
|
|
2741
2793
|
case 2:
|
|
2742
|
-
usageText.push("Argument for the '--target' option must be a single tag or a comma separated list.", "Usage examples: '--target 4.9', '--target
|
|
2794
|
+
usageText.push("Argument for the '--target' option must be a single tag or a comma separated list.", "Usage examples: '--target 4.9', '--target latest', '--target 4.9,5.3.2,current'.", supportedTagsText);
|
|
2743
2795
|
break;
|
|
2744
2796
|
case 4:
|
|
2745
2797
|
usageText.push("Item of the 'target' list must be a supported version tag.", supportedTagsText);
|
|
@@ -2908,13 +2960,10 @@ class ConfigFileOptionsWorker {
|
|
|
2908
2960
|
this.#optionValidator = new OptionValidator(4, this.#storeService, this.#onDiagnostic);
|
|
2909
2961
|
}
|
|
2910
2962
|
#isDoubleQuotedString(node, sourceFile) {
|
|
2911
|
-
return (node.kind === this.compiler.SyntaxKind.StringLiteral
|
|
2912
|
-
sourceFile.text.slice(this.#skipTrivia(node.pos, sourceFile), node.end).startsWith('"'));
|
|
2963
|
+
return (node.kind === this.compiler.SyntaxKind.StringLiteral
|
|
2964
|
+
&& sourceFile.text.slice(this.#skipTrivia(node.pos, sourceFile), node.end).startsWith('"'));
|
|
2913
2965
|
}
|
|
2914
2966
|
async parse(sourceText) {
|
|
2915
|
-
if (sourceText === "") {
|
|
2916
|
-
return;
|
|
2917
|
-
}
|
|
2918
2967
|
const configSourceFile = this.compiler.parseJsonText(this.#configFilePath, sourceText);
|
|
2919
2968
|
if (configSourceFile.parseDiagnostics.length > 0) {
|
|
2920
2969
|
for (const diagnostic of Diagnostic.fromDiagnostics(configSourceFile.parseDiagnostics, this.compiler)) {
|
|
@@ -2961,11 +3010,6 @@ class ConfigFileOptionsWorker {
|
|
|
2961
3010
|
}
|
|
2962
3011
|
async #parseOptionValue(sourceFile, valueExpression, optionDefinition, isListItem = false) {
|
|
2963
3012
|
switch (valueExpression.kind) {
|
|
2964
|
-
case this.compiler.SyntaxKind.NullKeyword:
|
|
2965
|
-
if (optionDefinition.nullable === true) {
|
|
2966
|
-
return null;
|
|
2967
|
-
}
|
|
2968
|
-
break;
|
|
2969
3013
|
case this.compiler.SyntaxKind.TrueKeyword:
|
|
2970
3014
|
if (optionDefinition.brand === "boolean") {
|
|
2971
3015
|
return true;
|
|
@@ -3009,39 +3053,6 @@ class ConfigFileOptionsWorker {
|
|
|
3009
3053
|
return value;
|
|
3010
3054
|
}
|
|
3011
3055
|
break;
|
|
3012
|
-
case this.compiler.SyntaxKind.ObjectLiteralExpression:
|
|
3013
|
-
if (optionDefinition.brand === "object" && "getDefinition" in optionDefinition) {
|
|
3014
|
-
const propertyDefinition = optionDefinition.getDefinition(4);
|
|
3015
|
-
const propertyOptions = {};
|
|
3016
|
-
for (const property of valueExpression.properties) {
|
|
3017
|
-
if (this.compiler.isPropertyAssignment(property)) {
|
|
3018
|
-
if (!this.#isDoubleQuotedString(property.name, sourceFile)) {
|
|
3019
|
-
const origin = {
|
|
3020
|
-
end: property.end,
|
|
3021
|
-
file: sourceFile,
|
|
3022
|
-
start: this.#skipTrivia(property.pos, sourceFile),
|
|
3023
|
-
};
|
|
3024
|
-
this.#onDiagnostic(Diagnostic.error(this.#optionDiagnosticText.doubleQuotesExpected(), origin));
|
|
3025
|
-
continue;
|
|
3026
|
-
}
|
|
3027
|
-
const optionName = this.#resolvePropertyName(property);
|
|
3028
|
-
const optionDefinition = propertyDefinition.get(optionName);
|
|
3029
|
-
if (optionDefinition) {
|
|
3030
|
-
propertyOptions[optionDefinition.name] = await this.#parseOptionValue(sourceFile, property.initializer, optionDefinition);
|
|
3031
|
-
}
|
|
3032
|
-
else {
|
|
3033
|
-
const origin = {
|
|
3034
|
-
end: property.end,
|
|
3035
|
-
file: sourceFile,
|
|
3036
|
-
start: this.#skipTrivia(property.pos, sourceFile),
|
|
3037
|
-
};
|
|
3038
|
-
this.#onDiagnostic(Diagnostic.error(this.#optionDiagnosticText.unknownProperty(optionName), origin));
|
|
3039
|
-
}
|
|
3040
|
-
}
|
|
3041
|
-
}
|
|
3042
|
-
return propertyOptions;
|
|
3043
|
-
}
|
|
3044
|
-
break;
|
|
3045
3056
|
}
|
|
3046
3057
|
const origin = {
|
|
3047
3058
|
end: valueExpression.end,
|
|
@@ -3103,10 +3114,10 @@ class ConfigService {
|
|
|
3103
3114
|
#commandLineOptions = {};
|
|
3104
3115
|
#configFileOptions = {};
|
|
3105
3116
|
static #defaultOptions = {
|
|
3106
|
-
|
|
3117
|
+
disableTestFileLookup: false,
|
|
3107
3118
|
failFast: false,
|
|
3108
|
-
rootPath: "./",
|
|
3109
|
-
target: [Environment.
|
|
3119
|
+
rootPath: Path.resolve("./"),
|
|
3120
|
+
target: [Environment.typescriptPath == null ? "latest" : "current"],
|
|
3110
3121
|
testFileMatch: ["**/*.tst.*", "**/__typetests__/*.test.*", "**/typetests/*.test.*"],
|
|
3111
3122
|
};
|
|
3112
3123
|
#pathMatch = [];
|
|
@@ -3133,17 +3144,17 @@ class ConfigService {
|
|
|
3133
3144
|
const commandLineWorker = new CommandLineOptionsWorker(this.#commandLineOptions, this.#pathMatch, this.#storeService, this.#onDiagnostic);
|
|
3134
3145
|
await commandLineWorker.parse(commandLineArgs);
|
|
3135
3146
|
}
|
|
3136
|
-
async readConfigFile(
|
|
3137
|
-
const configFilePath =
|
|
3147
|
+
async readConfigFile() {
|
|
3148
|
+
const configFilePath = this.#commandLineOptions.config ?? Path.resolve("./tstyche.config.json");
|
|
3149
|
+
if (!existsSync(configFilePath)) {
|
|
3150
|
+
return;
|
|
3151
|
+
}
|
|
3138
3152
|
this.#configFileOptions = {
|
|
3139
3153
|
rootPath: Path.dirname(configFilePath),
|
|
3140
3154
|
};
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
encoding: "utf8",
|
|
3145
|
-
});
|
|
3146
|
-
}
|
|
3155
|
+
const configFileText = await fs.readFile(configFilePath, {
|
|
3156
|
+
encoding: "utf8",
|
|
3157
|
+
});
|
|
3147
3158
|
const configFileWorker = new ConfigFileOptionsWorker(this.compiler, this.#configFileOptions, configFilePath, this.#storeService, this.#onDiagnostic);
|
|
3148
3159
|
await configFileWorker.parse(configFileText);
|
|
3149
3160
|
}
|
|
@@ -3157,15 +3168,15 @@ class ConfigService {
|
|
|
3157
3168
|
return mergedOptions;
|
|
3158
3169
|
}
|
|
3159
3170
|
selectTestFiles() {
|
|
3160
|
-
const {
|
|
3161
|
-
let testFilePaths = this.compiler.sys.readDirectory(rootPath,
|
|
3171
|
+
const { pathMatch, rootPath, testFileMatch } = this.resolveConfig();
|
|
3172
|
+
let testFilePaths = this.compiler.sys.readDirectory(rootPath, ["ts", "tsx", "mts", "cts", "js", "jsx", "mjs", "cjs"], undefined, testFileMatch);
|
|
3162
3173
|
if (pathMatch.length > 0) {
|
|
3163
3174
|
testFilePaths = testFilePaths.filter((testFilePath) => pathMatch.some((match) => {
|
|
3164
3175
|
const relativeTestFilePath = Path.relative("", testFilePath);
|
|
3165
3176
|
return relativeTestFilePath.toLowerCase().includes(match.toLowerCase());
|
|
3166
3177
|
}));
|
|
3167
3178
|
}
|
|
3168
|
-
if (testFilePaths.length === 0
|
|
3179
|
+
if (testFilePaths.length === 0) {
|
|
3169
3180
|
const text = [
|
|
3170
3181
|
"No test files were selected using current configuration.",
|
|
3171
3182
|
`Root path: ${rootPath}`,
|
|
@@ -3187,7 +3198,6 @@ var OptionBrand;
|
|
|
3187
3198
|
OptionBrand["Boolean"] = "boolean";
|
|
3188
3199
|
OptionBrand["True"] = "true";
|
|
3189
3200
|
OptionBrand["List"] = "list";
|
|
3190
|
-
OptionBrand["Object"] = "object";
|
|
3191
3201
|
})(OptionBrand || (OptionBrand = {}));
|
|
3192
3202
|
|
|
3193
3203
|
var OptionGroup;
|
|
@@ -3226,6 +3236,7 @@ class ManifestWorker {
|
|
|
3226
3236
|
}, (result) => {
|
|
3227
3237
|
if (result.statusCode !== 200) {
|
|
3228
3238
|
reject(new Error(`Request failed with status code ${String(result.statusCode)}.`));
|
|
3239
|
+
result.resume();
|
|
3229
3240
|
return;
|
|
3230
3241
|
}
|
|
3231
3242
|
result.setEncoding("utf8");
|
|
@@ -3275,7 +3286,7 @@ class ManifestWorker {
|
|
|
3275
3286
|
}
|
|
3276
3287
|
catch (error) {
|
|
3277
3288
|
if (!options.quite) {
|
|
3278
|
-
const text = [`Failed to fetch metadata of the 'typescript' package from '${this.#registryUrl.
|
|
3289
|
+
const text = [`Failed to fetch metadata of the 'typescript' package from '${this.#registryUrl.toString()}'.`];
|
|
3279
3290
|
if (error instanceof Error && error.name !== "AbortError") {
|
|
3280
3291
|
text.push("Might be there is an issue with the registry or the network connection.");
|
|
3281
3292
|
}
|
|
@@ -3331,8 +3342,8 @@ class ManifestWorker {
|
|
|
3331
3342
|
const quite = options?.refresh !== true;
|
|
3332
3343
|
const freshManifest = await this.#load(signal, { quite });
|
|
3333
3344
|
if (freshManifest != null) {
|
|
3334
|
-
|
|
3335
|
-
|
|
3345
|
+
await this.persist(freshManifest);
|
|
3346
|
+
return freshManifest;
|
|
3336
3347
|
}
|
|
3337
3348
|
}
|
|
3338
3349
|
return manifest;
|
|
@@ -3388,28 +3399,6 @@ class Lock {
|
|
|
3388
3399
|
}
|
|
3389
3400
|
}
|
|
3390
3401
|
|
|
3391
|
-
class Version {
|
|
3392
|
-
static satisfies(source, target) {
|
|
3393
|
-
const sourceElements = source.split(/\.|-/);
|
|
3394
|
-
const targetElements = target.split(/\.|-/);
|
|
3395
|
-
function compare(index = 0) {
|
|
3396
|
-
const sourceElement = sourceElements[index];
|
|
3397
|
-
const targetElement = targetElements[index];
|
|
3398
|
-
if (sourceElement > targetElement) {
|
|
3399
|
-
return true;
|
|
3400
|
-
}
|
|
3401
|
-
if (sourceElement === targetElement) {
|
|
3402
|
-
if (index === targetElements.length - 1) {
|
|
3403
|
-
return true;
|
|
3404
|
-
}
|
|
3405
|
-
return compare(index + 1);
|
|
3406
|
-
}
|
|
3407
|
-
return false;
|
|
3408
|
-
}
|
|
3409
|
-
return compare();
|
|
3410
|
-
}
|
|
3411
|
-
}
|
|
3412
|
-
|
|
3413
3402
|
class PackageInstaller {
|
|
3414
3403
|
#onDiagnostic;
|
|
3415
3404
|
#readyFileName = "__ready__";
|
|
@@ -3455,10 +3444,7 @@ class PackageInstaller {
|
|
|
3455
3444
|
this.#onDiagnostic(Diagnostic.fromError(`Failed to install 'typescript@${compilerVersion}'.`, error));
|
|
3456
3445
|
}
|
|
3457
3446
|
}
|
|
3458
|
-
|
|
3459
|
-
return Path.join(installationPath, "node_modules", "typescript", "lib", "typescript.js");
|
|
3460
|
-
}
|
|
3461
|
-
return Path.join(installationPath, "node_modules", "typescript", "lib", "tsserverlibrary.js");
|
|
3447
|
+
return Path.join(installationPath, "node_modules", "typescript", "lib", "typescript.js");
|
|
3462
3448
|
}
|
|
3463
3449
|
async #install(cwd, signal) {
|
|
3464
3450
|
const args = ["install", "--ignore-scripts", "--no-bin-links", "--no-package-lock"];
|
|
@@ -3490,7 +3476,6 @@ class StoreService {
|
|
|
3490
3476
|
#compilerInstanceCache = new Map();
|
|
3491
3477
|
#manifest;
|
|
3492
3478
|
#manifestWorker;
|
|
3493
|
-
#nodeRequire = createRequire(import.meta.url);
|
|
3494
3479
|
#packageInstaller;
|
|
3495
3480
|
#storePath;
|
|
3496
3481
|
constructor() {
|
|
@@ -3522,18 +3507,10 @@ class StoreService {
|
|
|
3522
3507
|
return compilerInstance;
|
|
3523
3508
|
}
|
|
3524
3509
|
let modulePath;
|
|
3525
|
-
if (tag === "current") {
|
|
3526
|
-
|
|
3527
|
-
modulePath = this.#nodeRequire.resolve("typescript");
|
|
3528
|
-
}
|
|
3529
|
-
catch (error) {
|
|
3530
|
-
this.#onDiagnostic(Diagnostic.fromError("Failed to resolve locally installed 'typescript' package. It might be not installed.", error));
|
|
3531
|
-
}
|
|
3510
|
+
if (tag === "current" && Environment.typescriptPath != null) {
|
|
3511
|
+
modulePath = Environment.typescriptPath;
|
|
3532
3512
|
}
|
|
3533
|
-
|
|
3534
|
-
if (tag === "current") {
|
|
3535
|
-
return;
|
|
3536
|
-
}
|
|
3513
|
+
else {
|
|
3537
3514
|
const version = await this.resolveTag(tag, signal);
|
|
3538
3515
|
if (version == null) {
|
|
3539
3516
|
this.#onDiagnostic(Diagnostic.error(`Cannot add the 'typescript' package for the '${tag}' tag.`));
|
|
@@ -3549,18 +3526,23 @@ class StoreService {
|
|
|
3549
3526
|
compilerInstance = await this.#loadModule(modulePath);
|
|
3550
3527
|
this.#compilerInstanceCache.set(tag, compilerInstance);
|
|
3551
3528
|
this.#compilerInstanceCache.set(compilerInstance.version, compilerInstance);
|
|
3552
|
-
return compilerInstance;
|
|
3553
3529
|
}
|
|
3554
|
-
return;
|
|
3530
|
+
return compilerInstance;
|
|
3555
3531
|
}
|
|
3556
3532
|
async #loadModule(modulePath) {
|
|
3557
3533
|
const exports = {};
|
|
3558
|
-
const require = createRequire(modulePath);
|
|
3559
3534
|
const module = { exports };
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3535
|
+
const candidatePaths = [Path.join(Path.dirname(modulePath), "tsserverlibrary.js"), modulePath];
|
|
3536
|
+
for (const candidatePath of candidatePaths) {
|
|
3537
|
+
const sourceText = await fs.readFile(candidatePath, { encoding: "utf8" });
|
|
3538
|
+
const modifiedSourceText = sourceText.replace("return checker;", "return { ...checker, isTypeIdenticalTo, isTypeSubtypeOf };");
|
|
3539
|
+
if (modifiedSourceText.length === sourceText.length) {
|
|
3540
|
+
continue;
|
|
3541
|
+
}
|
|
3542
|
+
const compiledWrapper = vm.compileFunction(modifiedSourceText, ["exports", "require", "module", "__filename", "__dirname"], { filename: candidatePath });
|
|
3543
|
+
compiledWrapper(exports, createRequire(candidatePath), module, candidatePath, Path.dirname(candidatePath));
|
|
3544
|
+
break;
|
|
3545
|
+
}
|
|
3564
3546
|
return module.exports;
|
|
3565
3547
|
}
|
|
3566
3548
|
#onDiagnostic = (diagnostic) => {
|
|
@@ -3586,48 +3568,37 @@ class StoreService {
|
|
|
3586
3568
|
if (this.#manifest.versions.includes(tag)) {
|
|
3587
3569
|
return tag;
|
|
3588
3570
|
}
|
|
3589
|
-
|
|
3590
|
-
if (this.#manifestWorker.isOutdated(this.#manifest, 60) &&
|
|
3591
|
-
Object.keys(this.#manifest.resolutions).slice(-5).includes(tag)) {
|
|
3592
|
-
this.#onDiagnostic(Diagnostic.warning([
|
|
3593
|
-
"Failed to update metadata of the 'typescript' package from the registry.",
|
|
3594
|
-
`The resolution of the '${tag}' tag may be outdated.`,
|
|
3595
|
-
]));
|
|
3596
|
-
}
|
|
3597
|
-
return version;
|
|
3571
|
+
return this.#manifest.resolutions[tag];
|
|
3598
3572
|
}
|
|
3599
3573
|
async update(signal) {
|
|
3600
3574
|
await this.#manifestWorker.open(signal, { refresh: true });
|
|
3601
3575
|
}
|
|
3602
3576
|
async validateTag(tag, signal) {
|
|
3603
3577
|
if (tag === "current") {
|
|
3604
|
-
return
|
|
3578
|
+
return Environment.typescriptPath != null;
|
|
3605
3579
|
}
|
|
3606
3580
|
await this.open(signal);
|
|
3607
3581
|
if (!this.#manifest) {
|
|
3608
3582
|
return false;
|
|
3609
3583
|
}
|
|
3610
|
-
if (this.#
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
tag.startsWith(this.#manifest.resolutions["latest"].slice(0, 3))) {
|
|
3584
|
+
if (this.#manifestWorker.isOutdated(this.#manifest, 60)
|
|
3585
|
+
&& (!Version.isVersionTag(tag)
|
|
3586
|
+
|| (this.#manifest.resolutions["latest"] != null
|
|
3587
|
+
&& Version.isGreaterThan(tag, this.#manifest.resolutions["latest"])))) {
|
|
3615
3588
|
this.#onDiagnostic(Diagnostic.warning([
|
|
3616
3589
|
"Failed to update metadata of the 'typescript' package from the registry.",
|
|
3617
3590
|
`The resolution of the '${tag}' tag may be outdated.`,
|
|
3618
3591
|
]));
|
|
3619
3592
|
}
|
|
3620
|
-
return
|
|
3593
|
+
return this.#manifest.versions.includes(tag) || tag in this.#manifest.resolutions || tag === "current";
|
|
3621
3594
|
}
|
|
3622
3595
|
}
|
|
3623
3596
|
|
|
3624
3597
|
class Cli {
|
|
3625
3598
|
#abortController = new AbortController();
|
|
3626
3599
|
#logger;
|
|
3627
|
-
#process;
|
|
3628
3600
|
#storeService;
|
|
3629
|
-
constructor(
|
|
3630
|
-
this.#process = process;
|
|
3601
|
+
constructor() {
|
|
3631
3602
|
this.#logger = new Logger();
|
|
3632
3603
|
this.#storeService = new StoreService();
|
|
3633
3604
|
}
|
|
@@ -3642,7 +3613,7 @@ class Cli {
|
|
|
3642
3613
|
switch (diagnostic.category) {
|
|
3643
3614
|
case "error":
|
|
3644
3615
|
this.#abortController.abort();
|
|
3645
|
-
|
|
3616
|
+
process.exitCode = 1;
|
|
3646
3617
|
this.#logger.writeError(diagnosticText(diagnostic));
|
|
3647
3618
|
break;
|
|
3648
3619
|
case "warning":
|
|
@@ -3672,28 +3643,27 @@ class Cli {
|
|
|
3672
3643
|
await this.#storeService.update(this.#abortController.signal);
|
|
3673
3644
|
return;
|
|
3674
3645
|
}
|
|
3675
|
-
|
|
3676
|
-
return;
|
|
3677
|
-
}
|
|
3678
|
-
const compiler = await this.#storeService.load(Environment.isTypeScriptInstalled ? "current" : "latest", this.#abortController.signal);
|
|
3646
|
+
const compiler = await this.#storeService.load(Environment.typescriptPath == null ? "latest" : "current", this.#abortController.signal);
|
|
3679
3647
|
if (!compiler) {
|
|
3680
3648
|
return;
|
|
3681
3649
|
}
|
|
3682
3650
|
const configService = new ConfigService(compiler, this.#storeService);
|
|
3683
3651
|
await configService.parseCommandLine(commandLineArguments);
|
|
3684
|
-
if (
|
|
3652
|
+
if (process.exitCode === 1) {
|
|
3685
3653
|
return;
|
|
3686
3654
|
}
|
|
3687
3655
|
await configService.readConfigFile();
|
|
3688
|
-
if (
|
|
3656
|
+
if (process.exitCode === 1) {
|
|
3689
3657
|
return;
|
|
3690
3658
|
}
|
|
3691
3659
|
const resolvedConfig = configService.resolveConfig();
|
|
3692
3660
|
if (configService.commandLineOptions.showConfig === true) {
|
|
3693
3661
|
this.#logger.writeMessage(formattedText({
|
|
3694
3662
|
noColor: Environment.noColor,
|
|
3663
|
+
noInteractive: Environment.noInteractive,
|
|
3695
3664
|
storePath: Environment.storePath,
|
|
3696
3665
|
timeout: Environment.timeout,
|
|
3666
|
+
typescriptPath: Environment.typescriptPath,
|
|
3697
3667
|
...resolvedConfig,
|
|
3698
3668
|
}));
|
|
3699
3669
|
return;
|
|
@@ -3704,13 +3674,16 @@ class Cli {
|
|
|
3704
3674
|
}
|
|
3705
3675
|
return;
|
|
3706
3676
|
}
|
|
3707
|
-
|
|
3708
|
-
if (
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3677
|
+
let testFiles = [];
|
|
3678
|
+
if (!resolvedConfig.disableTestFileLookup) {
|
|
3679
|
+
testFiles = configService.selectTestFiles();
|
|
3680
|
+
if (testFiles.length === 0) {
|
|
3681
|
+
return;
|
|
3682
|
+
}
|
|
3683
|
+
if (configService.commandLineOptions.listFiles === true) {
|
|
3684
|
+
this.#logger.writeMessage(formattedText(testFiles));
|
|
3685
|
+
return;
|
|
3686
|
+
}
|
|
3714
3687
|
}
|
|
3715
3688
|
EventEmitter.removeHandler(this.#onStartupEvent);
|
|
3716
3689
|
const tstyche = new TSTyche(resolvedConfig, this.#storeService);
|
|
@@ -3718,4 +3691,4 @@ class Cli {
|
|
|
3718
3691
|
}
|
|
3719
3692
|
}
|
|
3720
3693
|
|
|
3721
|
-
export { Assertion, Cli, CollectService, Color, ConfigService, DescribeResult, Diagnostic, DiagnosticCategory, Environment, EventEmitter, Expect, ExpectResult, FileResult, Line, Logger, OptionBrand, OptionDefinitionsMap, OptionGroup, Path, ProjectResult, ProjectService, Reporter, Result, ResultCount, ResultManager, ResultStatus, ResultTiming, Scribbler, StoreService, SummaryReporter, TSTyche, TargetResult, TaskRunner, TestMember, TestMemberBrand, TestMemberFlags, TestResult, TestTree, Text, ThoroughReporter, addsPackageStepText, describeNameText, diagnosticText, fileStatusText, fileViewText, formattedText, helpText, summaryText, testNameText, usesCompilerStepText };
|
|
3694
|
+
export { Assertion, Cli, CollectService, Color, ConfigService, DescribeResult, Diagnostic, DiagnosticCategory, Environment, EventEmitter, Expect, ExpectResult, FileResult, Line, Logger, OptionBrand, OptionDefinitionsMap, OptionGroup, Path, ProjectResult, ProjectService, Reporter, Result, ResultCount, ResultManager, ResultStatus, ResultTiming, Scribbler, StoreService, SummaryReporter, TSTyche, TargetResult, TaskRunner, TestMember, TestMemberBrand, TestMemberFlags, TestResult, TestTree, Text, ThoroughReporter, Version, addsPackageStepText, describeNameText, diagnosticText, fileStatusText, fileViewText, formattedText, helpText, summaryText, testNameText, usesCompilerStepText };
|