tstyche 1.0.0-beta.9 → 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/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 get isTypeScriptInstalled() {
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 #parseBoolean(value) {
72
- if (value != null) {
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 Environment.#parseBoolean(process.env["TSTYCHE_NO_COLOR"]);
75
+ return process.env["TSTYCHE_NO_COLOR"] !== "";
89
76
  }
90
- if (process.env["NO_COLOR"] != null && process.env["NO_COLOR"] !== "") {
91
- return true;
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.#noColor = options?.noColors ?? false;
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 === "newline") {
169
- return "\r\n";
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("newline", null)));
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({ noColors: this.#noColor });
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" }, " \u276D "),
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" ? (Scribbler.createElement(Text, { color: "90" },
377
- " ",
378
- this.props.diagnostic.code)) : undefined;
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 ? (Scribbler.createElement(Text, null,
387
- Scribbler.createElement(Line, null),
388
- Scribbler.createElement(CodeSpanText, { ...this.props.diagnostic.origin }))) : undefined;
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.7,5.3.2,current", "Test on all specified versions of TypeScript."],
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 ? (Scribbler.createElement(Text, null,
628
- Scribbler.createElement(Text, { color: "31" },
629
- String(this.props.failed),
630
- " failed"),
631
- Scribbler.createElement(Text, null, ", "))) : undefined,
632
- this.props.skipped > 0 ? (Scribbler.createElement(Text, null,
633
- Scribbler.createElement(Text, { color: "33" },
634
- String(this.props.skipped),
635
- " skipped"),
636
- Scribbler.createElement(Text, null, ", "))) : undefined,
637
- this.props.todo > 0 ? (Scribbler.createElement(Text, null,
638
- Scribbler.createElement(Text, { color: "35" },
639
- String(this.props.todo),
640
- " todo"),
641
- Scribbler.createElement(Text, null, ", "))) : undefined,
642
- this.props.passed > 0 ? (Scribbler.createElement(Text, null,
643
- Scribbler.createElement(Text, { color: "32" },
644
- String(this.props.passed),
645
- " passed"),
646
- Scribbler.createElement(Text, null, ", "))) : undefined,
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 && !this.#hasReportedError,
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 (this.logger.isInteractive()) {
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
- this.logger.eraseLastLine();
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 && member.name.toLowerCase().includes(this.resolvedConfig.skip.toLowerCase()))) {
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-beta.9";
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 5.0.4', '--target 4.7,5.3.2,current'.", supportedTagsText);
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
- allowNoTestFiles: false,
3117
+ disableTestFileLookup: false,
3107
3118
  failFast: false,
3108
- rootPath: "./",
3109
- target: [Environment.isTypeScriptInstalled ? "current" : "latest"],
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(filePath, sourceText) {
3137
- const configFilePath = filePath ?? this.#commandLineOptions.config ?? Path.resolve("./tstyche.config.json");
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
- let configFileText = sourceText ?? "";
3142
- if (sourceText == null && existsSync(configFilePath)) {
3143
- configFileText = await fs.readFile(configFilePath, {
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 { allowNoTestFiles, pathMatch, rootPath, testFileMatch } = this.resolveConfig();
3161
- let testFilePaths = this.compiler.sys.readDirectory(rootPath, undefined, undefined, testFileMatch);
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 && !allowNoTestFiles) {
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.href}'.`];
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
- manifest = { ...manifest, ...freshManifest };
3335
- await this.persist(manifest);
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
- if (Version.satisfies(compilerVersion, "5.3")) {
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() {
@@ -3521,19 +3506,11 @@ class StoreService {
3521
3506
  if (compilerInstance != null) {
3522
3507
  return compilerInstance;
3523
3508
  }
3524
- const modulePaths = [];
3525
- if (tag === "current") {
3526
- try {
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
- }
3509
+ let modulePath;
3510
+ if (tag === "current" && Environment.typescriptPath != null) {
3511
+ modulePath = Environment.typescriptPath;
3532
3512
  }
3533
- if (modulePaths.length === 0) {
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.`));
@@ -3543,31 +3520,27 @@ class StoreService {
3543
3520
  if (compilerInstance != null) {
3544
3521
  return compilerInstance;
3545
3522
  }
3546
- const installedModulePath = await this.#packageInstaller.ensure(version, signal);
3547
- if (installedModulePath != null) {
3548
- modulePaths.push(installedModulePath);
3549
- }
3523
+ modulePath = await this.#packageInstaller.ensure(version, signal);
3550
3524
  }
3551
- if (modulePaths.length !== 0) {
3552
- compilerInstance = await this.#loadModule(modulePaths);
3525
+ if (modulePath != null) {
3526
+ compilerInstance = await this.#loadModule(modulePath);
3553
3527
  this.#compilerInstanceCache.set(tag, compilerInstance);
3554
3528
  this.#compilerInstanceCache.set(compilerInstance.version, compilerInstance);
3555
- return compilerInstance;
3556
3529
  }
3557
- return;
3530
+ return compilerInstance;
3558
3531
  }
3559
- async #loadModule(modulePaths) {
3532
+ async #loadModule(modulePath) {
3560
3533
  const exports = {};
3561
3534
  const module = { exports };
3562
- for (const modulePath of modulePaths) {
3563
- const require = createRequire(modulePath);
3564
- let sourceText = await fs.readFile(modulePath, { encoding: "utf8" });
3565
- if (sourceText.length < 3000) {
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) {
3566
3540
  continue;
3567
3541
  }
3568
- sourceText = sourceText.replace("isTypeAssignableTo,", "isTypeAssignableTo, isTypeIdenticalTo, isTypeSubtypeOf,");
3569
- const compiledWrapper = vm.compileFunction(sourceText, ["exports", "require", "module", "__filename", "__dirname"], { filename: modulePath });
3570
- compiledWrapper(exports, require, module, modulePath, Path.dirname(modulePath));
3542
+ const compiledWrapper = vm.compileFunction(modifiedSourceText, ["exports", "require", "module", "__filename", "__dirname"], { filename: candidatePath });
3543
+ compiledWrapper(exports, createRequire(candidatePath), module, candidatePath, Path.dirname(candidatePath));
3571
3544
  break;
3572
3545
  }
3573
3546
  return module.exports;
@@ -3595,48 +3568,37 @@ class StoreService {
3595
3568
  if (this.#manifest.versions.includes(tag)) {
3596
3569
  return tag;
3597
3570
  }
3598
- const version = this.#manifest.resolutions[tag];
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;
3571
+ return this.#manifest.resolutions[tag];
3607
3572
  }
3608
3573
  async update(signal) {
3609
3574
  await this.#manifestWorker.open(signal, { refresh: true });
3610
3575
  }
3611
3576
  async validateTag(tag, signal) {
3612
3577
  if (tag === "current") {
3613
- return true;
3578
+ return Environment.typescriptPath != null;
3614
3579
  }
3615
3580
  await this.open(signal);
3616
3581
  if (!this.#manifest) {
3617
3582
  return false;
3618
3583
  }
3619
- if (this.#manifest.versions.includes(tag) || tag in this.#manifest.resolutions || tag === "current") {
3620
- return true;
3621
- }
3622
- if (this.#manifest.resolutions["latest"] != null &&
3623
- 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"])))) {
3624
3588
  this.#onDiagnostic(Diagnostic.warning([
3625
3589
  "Failed to update metadata of the 'typescript' package from the registry.",
3626
3590
  `The resolution of the '${tag}' tag may be outdated.`,
3627
3591
  ]));
3628
3592
  }
3629
- return false;
3593
+ return this.#manifest.versions.includes(tag) || tag in this.#manifest.resolutions || tag === "current";
3630
3594
  }
3631
3595
  }
3632
3596
 
3633
3597
  class Cli {
3634
3598
  #abortController = new AbortController();
3635
3599
  #logger;
3636
- #process;
3637
3600
  #storeService;
3638
- constructor(process) {
3639
- this.#process = process;
3601
+ constructor() {
3640
3602
  this.#logger = new Logger();
3641
3603
  this.#storeService = new StoreService();
3642
3604
  }
@@ -3651,7 +3613,7 @@ class Cli {
3651
3613
  switch (diagnostic.category) {
3652
3614
  case "error":
3653
3615
  this.#abortController.abort();
3654
- this.#process.exitCode = 1;
3616
+ process.exitCode = 1;
3655
3617
  this.#logger.writeError(diagnosticText(diagnostic));
3656
3618
  break;
3657
3619
  case "warning":
@@ -3681,28 +3643,27 @@ class Cli {
3681
3643
  await this.#storeService.update(this.#abortController.signal);
3682
3644
  return;
3683
3645
  }
3684
- if (this.#process.exitCode === 1) {
3685
- return;
3686
- }
3687
- 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);
3688
3647
  if (!compiler) {
3689
3648
  return;
3690
3649
  }
3691
3650
  const configService = new ConfigService(compiler, this.#storeService);
3692
3651
  await configService.parseCommandLine(commandLineArguments);
3693
- if (this.#process.exitCode === 1) {
3652
+ if (process.exitCode === 1) {
3694
3653
  return;
3695
3654
  }
3696
3655
  await configService.readConfigFile();
3697
- if (this.#process.exitCode === 1) {
3656
+ if (process.exitCode === 1) {
3698
3657
  return;
3699
3658
  }
3700
3659
  const resolvedConfig = configService.resolveConfig();
3701
3660
  if (configService.commandLineOptions.showConfig === true) {
3702
3661
  this.#logger.writeMessage(formattedText({
3703
3662
  noColor: Environment.noColor,
3663
+ noInteractive: Environment.noInteractive,
3704
3664
  storePath: Environment.storePath,
3705
3665
  timeout: Environment.timeout,
3666
+ typescriptPath: Environment.typescriptPath,
3706
3667
  ...resolvedConfig,
3707
3668
  }));
3708
3669
  return;
@@ -3713,13 +3674,16 @@ class Cli {
3713
3674
  }
3714
3675
  return;
3715
3676
  }
3716
- const testFiles = configService.selectTestFiles();
3717
- if (this.#process.exitCode === 1) {
3718
- return;
3719
- }
3720
- if (configService.commandLineOptions.listFiles === true) {
3721
- this.#logger.writeMessage(formattedText(testFiles));
3722
- return;
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
+ }
3723
3687
  }
3724
3688
  EventEmitter.removeHandler(this.#onStartupEvent);
3725
3689
  const tstyche = new TSTyche(resolvedConfig, this.#storeService);
@@ -3727,4 +3691,4 @@ class Cli {
3727
3691
  }
3728
3692
  }
3729
3693
 
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 };
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 };