tstyche 1.0.0-beta.9 → 1.0.0-rc.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/CHANGELOG.md +32 -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 +41 -45
- package/build/tstyche.js +315 -357
- package/package.json +24 -25
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,33 @@ 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.4")) {
|
|
2258
|
+
defaultCompilerOptions.module = "preserve";
|
|
2259
|
+
}
|
|
2260
|
+
if (Version.isSatisfiedWith(this.compiler.version, "5.0")) {
|
|
2261
|
+
defaultCompilerOptions["allowImportingTsExtensions"] = true;
|
|
2262
|
+
defaultCompilerOptions.moduleResolution = "bundler";
|
|
2263
|
+
}
|
|
2264
|
+
return defaultCompilerOptions;
|
|
2265
|
+
}
|
|
2212
2266
|
getDefaultProject(filePath) {
|
|
2213
2267
|
return this.#service.getDefaultProjectForFile(this.compiler.server.toNormalizedPath(filePath), true);
|
|
2214
2268
|
}
|
|
@@ -2255,14 +2309,15 @@ class TestTreeWorker {
|
|
|
2255
2309
|
if (member.flags & 1) {
|
|
2256
2310
|
mode |= 1;
|
|
2257
2311
|
}
|
|
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)) {
|
|
2312
|
+
if (member.flags & 2
|
|
2313
|
+
|| (this.resolvedConfig.only != null
|
|
2314
|
+
&& member.name.toLowerCase().includes(this.resolvedConfig.only.toLowerCase()))
|
|
2315
|
+
|| (this.#position != null && member.node.getStart() === this.#position)) {
|
|
2262
2316
|
mode |= 2;
|
|
2263
2317
|
}
|
|
2264
|
-
if (member.flags & 4
|
|
2265
|
-
(this.resolvedConfig.skip != null
|
|
2318
|
+
if (member.flags & 4
|
|
2319
|
+
|| (this.resolvedConfig.skip != null
|
|
2320
|
+
&& member.name.toLowerCase().includes(this.resolvedConfig.skip.toLowerCase()))) {
|
|
2266
2321
|
mode |= 4;
|
|
2267
2322
|
}
|
|
2268
2323
|
if (member.flags & 8) {
|
|
@@ -2364,8 +2419,8 @@ class TestTreeWorker {
|
|
|
2364
2419
|
const describeResult = new DescribeResult(describe, parentResult);
|
|
2365
2420
|
EventEmitter.dispatch(["describe:start", { result: describeResult }]);
|
|
2366
2421
|
runMode = this.#resolveRunMode(runMode, describe);
|
|
2367
|
-
if (!(runMode & 4 || (this.#hasOnly && !(runMode & 2)) || runMode & 8)
|
|
2368
|
-
describe.diagnostics.size > 0) {
|
|
2422
|
+
if (!(runMode & 4 || (this.#hasOnly && !(runMode & 2)) || runMode & 8)
|
|
2423
|
+
&& describe.diagnostics.size > 0) {
|
|
2369
2424
|
EventEmitter.dispatch([
|
|
2370
2425
|
"file:error",
|
|
2371
2426
|
{
|
|
@@ -2525,7 +2580,7 @@ class TSTyche {
|
|
|
2525
2580
|
#abortController = new AbortController();
|
|
2526
2581
|
#storeService;
|
|
2527
2582
|
#taskRunner;
|
|
2528
|
-
static version = "1.0.0-
|
|
2583
|
+
static version = "1.0.0-rc.1";
|
|
2529
2584
|
constructor(resolvedConfig, storeService) {
|
|
2530
2585
|
this.resolvedConfig = resolvedConfig;
|
|
2531
2586
|
this.#storeService = storeService;
|
|
@@ -2535,8 +2590,8 @@ class TSTyche {
|
|
|
2535
2590
|
#addEventHandlers() {
|
|
2536
2591
|
EventEmitter.addHandler(([eventName, payload]) => {
|
|
2537
2592
|
if (eventName.includes("error") || eventName.includes("fail")) {
|
|
2538
|
-
if ("diagnostics" in payload
|
|
2539
|
-
!payload.diagnostics.some(({ category }) => category === "error")) {
|
|
2593
|
+
if ("diagnostics" in payload
|
|
2594
|
+
&& !payload.diagnostics.some(({ category }) => category === "error")) {
|
|
2540
2595
|
return;
|
|
2541
2596
|
}
|
|
2542
2597
|
process.exitCode = 1;
|
|
@@ -2570,12 +2625,6 @@ class TSTyche {
|
|
|
2570
2625
|
|
|
2571
2626
|
class OptionDefinitionsMap {
|
|
2572
2627
|
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
2628
|
{
|
|
2580
2629
|
brand: "string",
|
|
2581
2630
|
description: "The path to a TSTyche configuration file.",
|
|
@@ -2714,10 +2763,10 @@ class OptionDiagnosticText {
|
|
|
2714
2763
|
unknownOption(optionName) {
|
|
2715
2764
|
return `Unknown option '${optionName}'.`;
|
|
2716
2765
|
}
|
|
2717
|
-
unknownProperty(optionName) {
|
|
2718
|
-
return `Unknown property '${optionName}'.`;
|
|
2719
|
-
}
|
|
2720
2766
|
versionIsNotSupported(value) {
|
|
2767
|
+
if (value === "current") {
|
|
2768
|
+
return "Cannot use 'current' as a target. Failed to resolve the path to the currently installed TypeScript module.";
|
|
2769
|
+
}
|
|
2721
2770
|
return `TypeScript version '${value}' is not supported.`;
|
|
2722
2771
|
}
|
|
2723
2772
|
}
|
|
@@ -2739,7 +2788,7 @@ class OptionUsageText {
|
|
|
2739
2788
|
const supportedTagsText = `Supported tags: ${["'", supportedTags.join("', '"), "'"].join("")}.`;
|
|
2740
2789
|
switch (this.#optionGroup) {
|
|
2741
2790
|
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
|
|
2791
|
+
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
2792
|
break;
|
|
2744
2793
|
case 4:
|
|
2745
2794
|
usageText.push("Item of the 'target' list must be a supported version tag.", supportedTagsText);
|
|
@@ -2908,13 +2957,10 @@ class ConfigFileOptionsWorker {
|
|
|
2908
2957
|
this.#optionValidator = new OptionValidator(4, this.#storeService, this.#onDiagnostic);
|
|
2909
2958
|
}
|
|
2910
2959
|
#isDoubleQuotedString(node, sourceFile) {
|
|
2911
|
-
return (node.kind === this.compiler.SyntaxKind.StringLiteral
|
|
2912
|
-
sourceFile.text.slice(this.#skipTrivia(node.pos, sourceFile), node.end).startsWith('"'));
|
|
2960
|
+
return (node.kind === this.compiler.SyntaxKind.StringLiteral
|
|
2961
|
+
&& sourceFile.text.slice(this.#skipTrivia(node.pos, sourceFile), node.end).startsWith('"'));
|
|
2913
2962
|
}
|
|
2914
2963
|
async parse(sourceText) {
|
|
2915
|
-
if (sourceText === "") {
|
|
2916
|
-
return;
|
|
2917
|
-
}
|
|
2918
2964
|
const configSourceFile = this.compiler.parseJsonText(this.#configFilePath, sourceText);
|
|
2919
2965
|
if (configSourceFile.parseDiagnostics.length > 0) {
|
|
2920
2966
|
for (const diagnostic of Diagnostic.fromDiagnostics(configSourceFile.parseDiagnostics, this.compiler)) {
|
|
@@ -2961,11 +3007,6 @@ class ConfigFileOptionsWorker {
|
|
|
2961
3007
|
}
|
|
2962
3008
|
async #parseOptionValue(sourceFile, valueExpression, optionDefinition, isListItem = false) {
|
|
2963
3009
|
switch (valueExpression.kind) {
|
|
2964
|
-
case this.compiler.SyntaxKind.NullKeyword:
|
|
2965
|
-
if (optionDefinition.nullable === true) {
|
|
2966
|
-
return null;
|
|
2967
|
-
}
|
|
2968
|
-
break;
|
|
2969
3010
|
case this.compiler.SyntaxKind.TrueKeyword:
|
|
2970
3011
|
if (optionDefinition.brand === "boolean") {
|
|
2971
3012
|
return true;
|
|
@@ -3009,39 +3050,6 @@ class ConfigFileOptionsWorker {
|
|
|
3009
3050
|
return value;
|
|
3010
3051
|
}
|
|
3011
3052
|
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
3053
|
}
|
|
3046
3054
|
const origin = {
|
|
3047
3055
|
end: valueExpression.end,
|
|
@@ -3103,10 +3111,9 @@ class ConfigService {
|
|
|
3103
3111
|
#commandLineOptions = {};
|
|
3104
3112
|
#configFileOptions = {};
|
|
3105
3113
|
static #defaultOptions = {
|
|
3106
|
-
allowNoTestFiles: false,
|
|
3107
3114
|
failFast: false,
|
|
3108
|
-
rootPath: "./",
|
|
3109
|
-
target: [Environment.
|
|
3115
|
+
rootPath: Path.resolve("./"),
|
|
3116
|
+
target: [Environment.typescriptPath == null ? "latest" : "current"],
|
|
3110
3117
|
testFileMatch: ["**/*.tst.*", "**/__typetests__/*.test.*", "**/typetests/*.test.*"],
|
|
3111
3118
|
};
|
|
3112
3119
|
#pathMatch = [];
|
|
@@ -3133,17 +3140,17 @@ class ConfigService {
|
|
|
3133
3140
|
const commandLineWorker = new CommandLineOptionsWorker(this.#commandLineOptions, this.#pathMatch, this.#storeService, this.#onDiagnostic);
|
|
3134
3141
|
await commandLineWorker.parse(commandLineArgs);
|
|
3135
3142
|
}
|
|
3136
|
-
async readConfigFile(
|
|
3137
|
-
const configFilePath =
|
|
3143
|
+
async readConfigFile() {
|
|
3144
|
+
const configFilePath = this.#commandLineOptions.config ?? Path.resolve("./tstyche.config.json");
|
|
3145
|
+
if (!existsSync(configFilePath)) {
|
|
3146
|
+
return;
|
|
3147
|
+
}
|
|
3138
3148
|
this.#configFileOptions = {
|
|
3139
3149
|
rootPath: Path.dirname(configFilePath),
|
|
3140
3150
|
};
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
encoding: "utf8",
|
|
3145
|
-
});
|
|
3146
|
-
}
|
|
3151
|
+
const configFileText = await fs.readFile(configFilePath, {
|
|
3152
|
+
encoding: "utf8",
|
|
3153
|
+
});
|
|
3147
3154
|
const configFileWorker = new ConfigFileOptionsWorker(this.compiler, this.#configFileOptions, configFilePath, this.#storeService, this.#onDiagnostic);
|
|
3148
3155
|
await configFileWorker.parse(configFileText);
|
|
3149
3156
|
}
|
|
@@ -3157,15 +3164,15 @@ class ConfigService {
|
|
|
3157
3164
|
return mergedOptions;
|
|
3158
3165
|
}
|
|
3159
3166
|
selectTestFiles() {
|
|
3160
|
-
const {
|
|
3161
|
-
let testFilePaths = this.compiler.sys.readDirectory(rootPath,
|
|
3167
|
+
const { pathMatch, rootPath, testFileMatch } = this.resolveConfig();
|
|
3168
|
+
let testFilePaths = this.compiler.sys.readDirectory(rootPath, ["ts", "tsx", "mts", "cts", "js", "jsx", "mjs", "cjs"], undefined, testFileMatch);
|
|
3162
3169
|
if (pathMatch.length > 0) {
|
|
3163
3170
|
testFilePaths = testFilePaths.filter((testFilePath) => pathMatch.some((match) => {
|
|
3164
3171
|
const relativeTestFilePath = Path.relative("", testFilePath);
|
|
3165
3172
|
return relativeTestFilePath.toLowerCase().includes(match.toLowerCase());
|
|
3166
3173
|
}));
|
|
3167
3174
|
}
|
|
3168
|
-
if (testFilePaths.length === 0
|
|
3175
|
+
if (testFilePaths.length === 0) {
|
|
3169
3176
|
const text = [
|
|
3170
3177
|
"No test files were selected using current configuration.",
|
|
3171
3178
|
`Root path: ${rootPath}`,
|
|
@@ -3187,7 +3194,6 @@ var OptionBrand;
|
|
|
3187
3194
|
OptionBrand["Boolean"] = "boolean";
|
|
3188
3195
|
OptionBrand["True"] = "true";
|
|
3189
3196
|
OptionBrand["List"] = "list";
|
|
3190
|
-
OptionBrand["Object"] = "object";
|
|
3191
3197
|
})(OptionBrand || (OptionBrand = {}));
|
|
3192
3198
|
|
|
3193
3199
|
var OptionGroup;
|
|
@@ -3226,6 +3232,7 @@ class ManifestWorker {
|
|
|
3226
3232
|
}, (result) => {
|
|
3227
3233
|
if (result.statusCode !== 200) {
|
|
3228
3234
|
reject(new Error(`Request failed with status code ${String(result.statusCode)}.`));
|
|
3235
|
+
result.resume();
|
|
3229
3236
|
return;
|
|
3230
3237
|
}
|
|
3231
3238
|
result.setEncoding("utf8");
|
|
@@ -3275,7 +3282,7 @@ class ManifestWorker {
|
|
|
3275
3282
|
}
|
|
3276
3283
|
catch (error) {
|
|
3277
3284
|
if (!options.quite) {
|
|
3278
|
-
const text = [`Failed to fetch metadata of the 'typescript' package from '${this.#registryUrl.
|
|
3285
|
+
const text = [`Failed to fetch metadata of the 'typescript' package from '${this.#registryUrl.toString()}'.`];
|
|
3279
3286
|
if (error instanceof Error && error.name !== "AbortError") {
|
|
3280
3287
|
text.push("Might be there is an issue with the registry or the network connection.");
|
|
3281
3288
|
}
|
|
@@ -3331,8 +3338,8 @@ class ManifestWorker {
|
|
|
3331
3338
|
const quite = options?.refresh !== true;
|
|
3332
3339
|
const freshManifest = await this.#load(signal, { quite });
|
|
3333
3340
|
if (freshManifest != null) {
|
|
3334
|
-
|
|
3335
|
-
|
|
3341
|
+
await this.persist(freshManifest);
|
|
3342
|
+
return freshManifest;
|
|
3336
3343
|
}
|
|
3337
3344
|
}
|
|
3338
3345
|
return manifest;
|
|
@@ -3388,28 +3395,6 @@ class Lock {
|
|
|
3388
3395
|
}
|
|
3389
3396
|
}
|
|
3390
3397
|
|
|
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
3398
|
class PackageInstaller {
|
|
3414
3399
|
#onDiagnostic;
|
|
3415
3400
|
#readyFileName = "__ready__";
|
|
@@ -3455,10 +3440,7 @@ class PackageInstaller {
|
|
|
3455
3440
|
this.#onDiagnostic(Diagnostic.fromError(`Failed to install 'typescript@${compilerVersion}'.`, error));
|
|
3456
3441
|
}
|
|
3457
3442
|
}
|
|
3458
|
-
|
|
3459
|
-
return Path.join(installationPath, "node_modules", "typescript", "lib", "typescript.js");
|
|
3460
|
-
}
|
|
3461
|
-
return Path.join(installationPath, "node_modules", "typescript", "lib", "tsserverlibrary.js");
|
|
3443
|
+
return Path.join(installationPath, "node_modules", "typescript", "lib", "typescript.js");
|
|
3462
3444
|
}
|
|
3463
3445
|
async #install(cwd, signal) {
|
|
3464
3446
|
const args = ["install", "--ignore-scripts", "--no-bin-links", "--no-package-lock"];
|
|
@@ -3490,7 +3472,6 @@ class StoreService {
|
|
|
3490
3472
|
#compilerInstanceCache = new Map();
|
|
3491
3473
|
#manifest;
|
|
3492
3474
|
#manifestWorker;
|
|
3493
|
-
#nodeRequire = createRequire(import.meta.url);
|
|
3494
3475
|
#packageInstaller;
|
|
3495
3476
|
#storePath;
|
|
3496
3477
|
constructor() {
|
|
@@ -3521,19 +3502,11 @@ class StoreService {
|
|
|
3521
3502
|
if (compilerInstance != null) {
|
|
3522
3503
|
return compilerInstance;
|
|
3523
3504
|
}
|
|
3524
|
-
|
|
3525
|
-
if (tag === "current") {
|
|
3526
|
-
|
|
3527
|
-
modulePaths.push(this.#nodeRequire.resolve("typescript/lib/tsserverlibrary.js"), 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
|
-
}
|
|
3505
|
+
let modulePath;
|
|
3506
|
+
if (tag === "current" && Environment.typescriptPath != null) {
|
|
3507
|
+
modulePath = Environment.typescriptPath;
|
|
3532
3508
|
}
|
|
3533
|
-
|
|
3534
|
-
if (tag === "current") {
|
|
3535
|
-
return;
|
|
3536
|
-
}
|
|
3509
|
+
else {
|
|
3537
3510
|
const version = await this.resolveTag(tag, signal);
|
|
3538
3511
|
if (version == null) {
|
|
3539
3512
|
this.#onDiagnostic(Diagnostic.error(`Cannot add the 'typescript' package for the '${tag}' tag.`));
|
|
@@ -3543,31 +3516,27 @@ class StoreService {
|
|
|
3543
3516
|
if (compilerInstance != null) {
|
|
3544
3517
|
return compilerInstance;
|
|
3545
3518
|
}
|
|
3546
|
-
|
|
3547
|
-
if (installedModulePath != null) {
|
|
3548
|
-
modulePaths.push(installedModulePath);
|
|
3549
|
-
}
|
|
3519
|
+
modulePath = await this.#packageInstaller.ensure(version, signal);
|
|
3550
3520
|
}
|
|
3551
|
-
if (
|
|
3552
|
-
compilerInstance = await this.#loadModule(
|
|
3521
|
+
if (modulePath != null) {
|
|
3522
|
+
compilerInstance = await this.#loadModule(modulePath);
|
|
3553
3523
|
this.#compilerInstanceCache.set(tag, compilerInstance);
|
|
3554
3524
|
this.#compilerInstanceCache.set(compilerInstance.version, compilerInstance);
|
|
3555
|
-
return compilerInstance;
|
|
3556
3525
|
}
|
|
3557
|
-
return;
|
|
3526
|
+
return compilerInstance;
|
|
3558
3527
|
}
|
|
3559
|
-
async #loadModule(
|
|
3528
|
+
async #loadModule(modulePath) {
|
|
3560
3529
|
const exports = {};
|
|
3561
3530
|
const module = { exports };
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3531
|
+
const candidatePaths = [Path.join(Path.dirname(modulePath), "tsserverlibrary.js"), modulePath];
|
|
3532
|
+
for (const candidatePath of candidatePaths) {
|
|
3533
|
+
const sourceText = await fs.readFile(candidatePath, { encoding: "utf8" });
|
|
3534
|
+
const modifiedSourceText = sourceText.replace("return checker;", "return { ...checker, isTypeIdenticalTo, isTypeSubtypeOf };");
|
|
3535
|
+
if (modifiedSourceText.length === sourceText.length) {
|
|
3566
3536
|
continue;
|
|
3567
3537
|
}
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
compiledWrapper(exports, require, module, modulePath, Path.dirname(modulePath));
|
|
3538
|
+
const compiledWrapper = vm.compileFunction(modifiedSourceText, ["exports", "require", "module", "__filename", "__dirname"], { filename: candidatePath });
|
|
3539
|
+
compiledWrapper(exports, createRequire(candidatePath), module, candidatePath, Path.dirname(candidatePath));
|
|
3571
3540
|
break;
|
|
3572
3541
|
}
|
|
3573
3542
|
return module.exports;
|
|
@@ -3595,48 +3564,36 @@ class StoreService {
|
|
|
3595
3564
|
if (this.#manifest.versions.includes(tag)) {
|
|
3596
3565
|
return tag;
|
|
3597
3566
|
}
|
|
3598
|
-
|
|
3599
|
-
if (this.#manifestWorker.isOutdated(this.#manifest, 60) &&
|
|
3600
|
-
Object.keys(this.#manifest.resolutions).slice(-5).includes(tag)) {
|
|
3601
|
-
this.#onDiagnostic(Diagnostic.warning([
|
|
3602
|
-
"Failed to update metadata of the 'typescript' package from the registry.",
|
|
3603
|
-
`The resolution of the '${tag}' tag may be outdated.`,
|
|
3604
|
-
]));
|
|
3605
|
-
}
|
|
3606
|
-
return version;
|
|
3567
|
+
return this.#manifest.resolutions[tag];
|
|
3607
3568
|
}
|
|
3608
3569
|
async update(signal) {
|
|
3609
3570
|
await this.#manifestWorker.open(signal, { refresh: true });
|
|
3610
3571
|
}
|
|
3611
3572
|
async validateTag(tag, signal) {
|
|
3612
3573
|
if (tag === "current") {
|
|
3613
|
-
return
|
|
3574
|
+
return Environment.typescriptPath != null;
|
|
3614
3575
|
}
|
|
3615
3576
|
await this.open(signal);
|
|
3616
3577
|
if (!this.#manifest) {
|
|
3617
3578
|
return false;
|
|
3618
3579
|
}
|
|
3619
|
-
if (this.#
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
tag.startsWith(this.#manifest.resolutions["latest"].slice(0, 3))) {
|
|
3580
|
+
if (this.#manifestWorker.isOutdated(this.#manifest, 60)
|
|
3581
|
+
&& (!Version.isVersionTag(tag)
|
|
3582
|
+
|| (this.#manifest.resolutions["latest"] != null
|
|
3583
|
+
&& Version.isGreaterThan(tag, this.#manifest.resolutions["latest"])))) {
|
|
3624
3584
|
this.#onDiagnostic(Diagnostic.warning([
|
|
3625
3585
|
"Failed to update metadata of the 'typescript' package from the registry.",
|
|
3626
3586
|
`The resolution of the '${tag}' tag may be outdated.`,
|
|
3627
3587
|
]));
|
|
3628
3588
|
}
|
|
3629
|
-
return
|
|
3589
|
+
return this.#manifest.versions.includes(tag) || tag in this.#manifest.resolutions || tag === "current";
|
|
3630
3590
|
}
|
|
3631
3591
|
}
|
|
3632
3592
|
|
|
3633
3593
|
class Cli {
|
|
3634
|
-
#abortController = new AbortController();
|
|
3635
3594
|
#logger;
|
|
3636
|
-
#process;
|
|
3637
3595
|
#storeService;
|
|
3638
|
-
constructor(
|
|
3639
|
-
this.#process = process;
|
|
3596
|
+
constructor() {
|
|
3640
3597
|
this.#logger = new Logger();
|
|
3641
3598
|
this.#storeService = new StoreService();
|
|
3642
3599
|
}
|
|
@@ -3650,8 +3607,7 @@ class Cli {
|
|
|
3650
3607
|
for (const diagnostic of payload.diagnostics) {
|
|
3651
3608
|
switch (diagnostic.category) {
|
|
3652
3609
|
case "error":
|
|
3653
|
-
|
|
3654
|
-
this.#process.exitCode = 1;
|
|
3610
|
+
process.exitCode = 1;
|
|
3655
3611
|
this.#logger.writeError(diagnosticText(diagnostic));
|
|
3656
3612
|
break;
|
|
3657
3613
|
case "warning":
|
|
@@ -3678,48 +3634,50 @@ class Cli {
|
|
|
3678
3634
|
return;
|
|
3679
3635
|
}
|
|
3680
3636
|
if (commandLineArguments.includes("--update")) {
|
|
3681
|
-
await this.#storeService.update(
|
|
3637
|
+
await this.#storeService.update();
|
|
3682
3638
|
return;
|
|
3683
3639
|
}
|
|
3684
|
-
|
|
3685
|
-
return;
|
|
3686
|
-
}
|
|
3687
|
-
const compiler = await this.#storeService.load(Environment.isTypeScriptInstalled ? "current" : "latest", this.#abortController.signal);
|
|
3640
|
+
const compiler = await this.#storeService.load(Environment.typescriptPath == null ? "latest" : "current");
|
|
3688
3641
|
if (!compiler) {
|
|
3689
3642
|
return;
|
|
3690
3643
|
}
|
|
3691
3644
|
const configService = new ConfigService(compiler, this.#storeService);
|
|
3692
3645
|
await configService.parseCommandLine(commandLineArguments);
|
|
3693
|
-
if (
|
|
3646
|
+
if (process.exitCode === 1) {
|
|
3694
3647
|
return;
|
|
3695
3648
|
}
|
|
3696
3649
|
await configService.readConfigFile();
|
|
3697
|
-
if (
|
|
3650
|
+
if (process.exitCode === 1) {
|
|
3698
3651
|
return;
|
|
3699
3652
|
}
|
|
3700
3653
|
const resolvedConfig = configService.resolveConfig();
|
|
3701
3654
|
if (configService.commandLineOptions.showConfig === true) {
|
|
3702
3655
|
this.#logger.writeMessage(formattedText({
|
|
3703
3656
|
noColor: Environment.noColor,
|
|
3657
|
+
noInteractive: Environment.noInteractive,
|
|
3704
3658
|
storePath: Environment.storePath,
|
|
3705
3659
|
timeout: Environment.timeout,
|
|
3660
|
+
typescriptPath: Environment.typescriptPath,
|
|
3706
3661
|
...resolvedConfig,
|
|
3707
3662
|
}));
|
|
3708
3663
|
return;
|
|
3709
3664
|
}
|
|
3710
3665
|
if (configService.commandLineOptions.install === true) {
|
|
3711
3666
|
for (const tag of resolvedConfig.target) {
|
|
3712
|
-
await this.#storeService.install(tag
|
|
3667
|
+
await this.#storeService.install(tag);
|
|
3713
3668
|
}
|
|
3714
3669
|
return;
|
|
3715
3670
|
}
|
|
3716
|
-
|
|
3717
|
-
if (
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3671
|
+
let testFiles = [];
|
|
3672
|
+
if (resolvedConfig.testFileMatch.length !== 0) {
|
|
3673
|
+
testFiles = configService.selectTestFiles();
|
|
3674
|
+
if (testFiles.length === 0) {
|
|
3675
|
+
return;
|
|
3676
|
+
}
|
|
3677
|
+
if (configService.commandLineOptions.listFiles === true) {
|
|
3678
|
+
this.#logger.writeMessage(formattedText(testFiles));
|
|
3679
|
+
return;
|
|
3680
|
+
}
|
|
3723
3681
|
}
|
|
3724
3682
|
EventEmitter.removeHandler(this.#onStartupEvent);
|
|
3725
3683
|
const tstyche = new TSTyche(resolvedConfig, this.#storeService);
|
|
@@ -3727,4 +3685,4 @@ class Cli {
|
|
|
3727
3685
|
}
|
|
3728
3686
|
}
|
|
3729
3687
|
|
|
3730
|
-
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 };
|
|
3688
|
+
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 };
|