tstyche 3.5.0 → 4.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/tstyche.js CHANGED
@@ -140,15 +140,15 @@ class Diagnostic {
140
140
  }
141
141
  const text = typeof diagnostic.messageText === "string"
142
142
  ? diagnostic.messageText
143
- : Diagnostic.#toMessageText(diagnostic.messageText);
143
+ : Diagnostic.toMessageText(diagnostic.messageText);
144
144
  return new Diagnostic(text, DiagnosticCategory.Error, origin).add({ code, related });
145
145
  });
146
146
  }
147
- static #toMessageText(chain) {
147
+ static toMessageText(chain) {
148
148
  const result = [chain.messageText];
149
149
  if (chain.next != null) {
150
150
  for (const nextChain of chain.next) {
151
- result.push(...Diagnostic.#toMessageText(nextChain));
151
+ result.push(...Diagnostic.toMessageText(nextChain));
152
152
  }
153
153
  }
154
154
  return result;
@@ -363,9 +363,6 @@ class Version {
363
363
  static isSatisfiedWith(source, target) {
364
364
  return source === target || Version.#satisfies(source, target);
365
365
  }
366
- static isVersionTag(target) {
367
- return /^\d+/.test(target);
368
- }
369
366
  static #satisfies(source, target) {
370
367
  const sourceElements = source.split(/\.|-/);
371
368
  const targetElements = target.split(/\.|-/);
@@ -394,8 +391,8 @@ class StoreDiagnosticText {
394
391
  static failedToFetchMetadata(registry) {
395
392
  return `Failed to fetch metadata of the 'typescript' package from '${registry}'.`;
396
393
  }
397
- static failedToInstalTypeScript(version) {
398
- return `Failed to install 'typescript@${version}'.`;
394
+ static failedToFetchPackage(version) {
395
+ return `Failed to fetch the 'typescript@${version}' package.`;
399
396
  }
400
397
  static failedToUpdateMetadata(registry) {
401
398
  return `Failed to update metadata of the 'typescript' package from '${registry}'.`;
@@ -499,9 +496,10 @@ class LockService {
499
496
  }
500
497
 
501
498
  class Manifest {
502
- static #version = "2";
499
+ static #version = "3";
503
500
  $version;
504
501
  lastUpdated;
502
+ minorVersions;
505
503
  npmRegistry;
506
504
  packages;
507
505
  resolutions;
@@ -509,6 +507,7 @@ class Manifest {
509
507
  constructor(data) {
510
508
  this.$version = data.$version ?? Manifest.#version;
511
509
  this.lastUpdated = data.lastUpdated ?? Date.now();
510
+ this.minorVersions = data.minorVersions;
512
511
  this.npmRegistry = data.npmRegistry;
513
512
  this.packages = data.packages;
514
513
  this.resolutions = data.resolutions;
@@ -542,6 +541,7 @@ class Manifest {
542
541
  const manifestData = {
543
542
  $version: this.$version,
544
543
  lastUpdated: this.lastUpdated,
544
+ minorVersions: this.minorVersions,
545
545
  npmRegistry: this.npmRegistry,
546
546
  packages: this.packages,
547
547
  resolutions: this.resolutions,
@@ -556,7 +556,7 @@ class ManifestService {
556
556
  #manifestFilePath;
557
557
  #npmRegistry;
558
558
  #storePath;
559
- #supportedVersionRegex = /^(4|5)\.\d\.\d$/;
559
+ #supportedVersionRegex = /^(5)\.\d\.\d$/;
560
560
  constructor(storePath, npmRegistry, fetcher) {
561
561
  this.#storePath = storePath;
562
562
  this.#npmRegistry = npmRegistry;
@@ -591,7 +591,7 @@ class ManifestService {
591
591
  packages[tag] = { integrity: meta.dist.integrity, tarball: meta.dist.tarball };
592
592
  }
593
593
  }
594
- const minorVersions = new Set(versions.map((version) => version.slice(0, -2)));
594
+ const minorVersions = [...new Set(versions.map((version) => version.slice(0, -2)))];
595
595
  for (const tag of minorVersions) {
596
596
  const resolvedVersion = versions.findLast((version) => version.startsWith(tag));
597
597
  if (resolvedVersion != null) {
@@ -608,7 +608,7 @@ class ManifestService {
608
608
  }
609
609
  }
610
610
  }
611
- return new Manifest({ npmRegistry: this.#npmRegistry, packages, resolutions, versions });
611
+ return new Manifest({ minorVersions, npmRegistry: this.#npmRegistry, packages, resolutions, versions });
612
612
  }
613
613
  async open(options) {
614
614
  if (!existsSync(this.#manifestFilePath)) {
@@ -685,9 +685,6 @@ class PackageService {
685
685
  if (response?.body != null) {
686
686
  const targetPath = `${packagePath}-${Math.random().toString(32).slice(2)}`;
687
687
  for await (const file of TarReader.extract(response.body)) {
688
- if (!file.name.startsWith("package/")) {
689
- continue;
690
- }
691
688
  const filePath = Path.join(targetPath, file.name.replace("package/", ""));
692
689
  const directoryPath = Path.dirname(filePath);
693
690
  if (!existsSync(directoryPath)) {
@@ -702,7 +699,7 @@ class PackageService {
702
699
  }
703
700
  async ensure(packageVersion, manifest) {
704
701
  let packagePath = Path.join(this.#storePath, `typescript@${packageVersion}`);
705
- const diagnostic = Diagnostic.error(StoreDiagnosticText.failedToInstalTypeScript(packageVersion));
702
+ const diagnostic = Diagnostic.error(StoreDiagnosticText.failedToFetchPackage(packageVersion));
706
703
  if (await this.#lockService.isLocked(packagePath, diagnostic)) {
707
704
  return;
708
705
  }
@@ -742,11 +739,7 @@ class Store {
742
739
  Store.#packageService = new PackageService(Store.#storePath, Store.#fetcher, Store.#lockService);
743
740
  Store.#manifestService = new ManifestService(Store.#storePath, Store.#npmRegistry, Store.#fetcher);
744
741
  }
745
- static async getSupportedTags() {
746
- await Store.open();
747
- return Store.#supportedTags;
748
- }
749
- static async install(tag) {
742
+ static async fetch(tag) {
750
743
  if (tag === "current") {
751
744
  return;
752
745
  }
@@ -799,14 +792,11 @@ class Store {
799
792
  modulePath = Path.resolve(modulePath, "../tsserverlibrary.js");
800
793
  }
801
794
  const sourceText = await fs.readFile(modulePath, { encoding: "utf8" });
802
- const toExpose = [];
803
- if (Version.isSatisfiedWith(packageVersion, "4.4")) {
804
- toExpose.push("isApplicableIndexType");
805
- }
806
- if (!Version.isSatisfiedWith(packageVersion, "4.6")) {
807
- toExpose.push("getTypeOfSymbol");
808
- }
809
- toExpose.push("isTypeRelatedTo", "relation: { assignable: assignableRelation, identity: identityRelation, subtype: strictSubtypeRelation }");
795
+ const toExpose = [
796
+ "isApplicableIndexType",
797
+ "isTypeRelatedTo",
798
+ "relation: { assignable: assignableRelation, identity: identityRelation }",
799
+ ];
810
800
  const modifiedSourceText = sourceText.replace("return checker;", `return { ...checker, ${toExpose.join(", ")} };`);
811
801
  const compiledWrapper = vm.compileFunction(modifiedSourceText, ["exports", "require", "module", "__filename", "__dirname"], { filename: modulePath });
812
802
  compiledWrapper(exports, createRequire(modulePath), module, modulePath, Path.dirname(modulePath));
@@ -857,7 +847,7 @@ class Target {
857
847
  }
858
848
  await Store.open();
859
849
  if (Store.manifest != null) {
860
- let versions = Object.keys(Store.manifest.resolutions).slice(0, -4);
850
+ let versions = [...Store.manifest.minorVersions];
861
851
  for (const comparator of query.split(" ")) {
862
852
  versions = Target.#filter(comparator, versions);
863
853
  }
@@ -913,15 +903,15 @@ class Options {
913
903
  },
914
904
  {
915
905
  brand: OptionBrand.BareTrue,
916
- description: "Print the list of command line options with brief descriptions and exit.",
906
+ description: "Fetch specified versions of the 'typescript' package and exit.",
917
907
  group: OptionGroup.CommandLine,
918
- name: "help",
908
+ name: "fetch",
919
909
  },
920
910
  {
921
911
  brand: OptionBrand.BareTrue,
922
- description: "Install specified versions of the 'typescript' package and exit.",
912
+ description: "Print the list of command line options with brief descriptions and exit.",
923
913
  group: OptionGroup.CommandLine,
924
- name: "install",
914
+ name: "help",
925
915
  },
926
916
  {
927
917
  brand: OptionBrand.BareTrue,
@@ -1504,11 +1494,11 @@ class ConfigFileParser {
1504
1494
  }
1505
1495
 
1506
1496
  const defaultOptions = {
1507
- checkSourceFiles: false,
1497
+ checkSourceFiles: true,
1508
1498
  failFast: false,
1509
1499
  plugins: [],
1510
- rejectAnyType: false,
1511
- rejectNeverType: false,
1500
+ rejectAnyType: true,
1501
+ rejectNeverType: true,
1512
1502
  reporters: ["list", "summary"],
1513
1503
  rootPath: Path.resolve("./"),
1514
1504
  target: environmentOptions.typescriptModule != null ? ["current"] : ["latest"],
@@ -1909,6 +1899,293 @@ class ResultHandler {
1909
1899
  }
1910
1900
  }
1911
1901
 
1902
+ var TestTreeNodeBrand;
1903
+ (function (TestTreeNodeBrand) {
1904
+ TestTreeNodeBrand["Describe"] = "describe";
1905
+ TestTreeNodeBrand["Test"] = "test";
1906
+ TestTreeNodeBrand["Expect"] = "expect";
1907
+ })(TestTreeNodeBrand || (TestTreeNodeBrand = {}));
1908
+
1909
+ class TestTreeNode {
1910
+ brand;
1911
+ children = [];
1912
+ #compiler;
1913
+ diagnostics = new Set();
1914
+ flags;
1915
+ name = "";
1916
+ node;
1917
+ parent;
1918
+ constructor(compiler, brand, node, parent, flags) {
1919
+ this.brand = brand;
1920
+ this.#compiler = compiler;
1921
+ this.node = node;
1922
+ this.parent = parent;
1923
+ this.flags = flags;
1924
+ if (node.arguments[0] != null && compiler.isStringLiteralLike(node.arguments[0])) {
1925
+ this.name = node.arguments[0].text;
1926
+ }
1927
+ if (node.arguments[1] != null &&
1928
+ compiler.isFunctionLike(node.arguments[1]) &&
1929
+ compiler.isBlock(node.arguments[1].body)) {
1930
+ const blockStart = node.arguments[1].body.getStart();
1931
+ const blockEnd = node.arguments[1].body.getEnd();
1932
+ for (const diagnostic of parent.diagnostics) {
1933
+ if (diagnostic.start != null && diagnostic.start >= blockStart && diagnostic.start <= blockEnd) {
1934
+ this.diagnostics.add(diagnostic);
1935
+ parent.diagnostics.delete(diagnostic);
1936
+ }
1937
+ }
1938
+ }
1939
+ }
1940
+ validate() {
1941
+ const diagnostics = [];
1942
+ const getText = (node) => `'${node.expression.getText()}()' cannot be nested within '${this.node.expression.getText()}()'.`;
1943
+ const getParentCallExpression = (node) => {
1944
+ while (!this.#compiler.isCallExpression(node.parent)) {
1945
+ node = node.parent;
1946
+ }
1947
+ return node.parent;
1948
+ };
1949
+ switch (this.brand) {
1950
+ case TestTreeNodeBrand.Describe:
1951
+ for (const member of this.children) {
1952
+ if (member.brand === TestTreeNodeBrand.Expect) {
1953
+ diagnostics.push(Diagnostic.error(getText(member.node), DiagnosticOrigin.fromNode(getParentCallExpression(member.node))));
1954
+ }
1955
+ }
1956
+ break;
1957
+ case TestTreeNodeBrand.Test:
1958
+ case TestTreeNodeBrand.Expect:
1959
+ for (const member of this.children) {
1960
+ if (member.brand !== TestTreeNodeBrand.Expect) {
1961
+ diagnostics.push(Diagnostic.error(getText(member.node), DiagnosticOrigin.fromNode(member.node)));
1962
+ }
1963
+ }
1964
+ break;
1965
+ }
1966
+ return diagnostics;
1967
+ }
1968
+ }
1969
+
1970
+ class AssertionNode extends TestTreeNode {
1971
+ isNot;
1972
+ matcherName;
1973
+ matcherNode;
1974
+ modifierNode;
1975
+ notNode;
1976
+ source;
1977
+ target;
1978
+ constructor(compiler, brand, node, parent, flags, matcherNode, modifierNode, notNode) {
1979
+ super(compiler, brand, node, parent, flags);
1980
+ this.isNot = notNode != null;
1981
+ this.matcherName = matcherNode.expression.name;
1982
+ this.matcherNode = matcherNode;
1983
+ this.modifierNode = modifierNode;
1984
+ this.source = this.node.typeArguments ?? this.node.arguments;
1985
+ this.target = this.matcherNode.typeArguments ?? this.matcherNode.arguments;
1986
+ for (const diagnostic of parent.diagnostics) {
1987
+ if (diagnostic.start != null && diagnostic.start >= this.source.pos && diagnostic.start <= this.source.end) {
1988
+ this.diagnostics.add(diagnostic);
1989
+ parent.diagnostics.delete(diagnostic);
1990
+ }
1991
+ }
1992
+ }
1993
+ }
1994
+
1995
+ var TestTreeNodeFlags;
1996
+ (function (TestTreeNodeFlags) {
1997
+ TestTreeNodeFlags[TestTreeNodeFlags["None"] = 0] = "None";
1998
+ TestTreeNodeFlags[TestTreeNodeFlags["Fail"] = 1] = "Fail";
1999
+ TestTreeNodeFlags[TestTreeNodeFlags["Only"] = 2] = "Only";
2000
+ TestTreeNodeFlags[TestTreeNodeFlags["Skip"] = 4] = "Skip";
2001
+ TestTreeNodeFlags[TestTreeNodeFlags["Todo"] = 8] = "Todo";
2002
+ })(TestTreeNodeFlags || (TestTreeNodeFlags = {}));
2003
+
2004
+ class IdentifierLookup {
2005
+ #compiler;
2006
+ #identifiers;
2007
+ #moduleSpecifiers = ['"tstyche"', "'tstyche'"];
2008
+ constructor(compiler, identifiers) {
2009
+ this.#compiler = compiler;
2010
+ this.#identifiers = identifiers ?? {
2011
+ namedImports: {
2012
+ describe: undefined,
2013
+ expect: undefined,
2014
+ it: undefined,
2015
+ namespace: undefined,
2016
+ test: undefined,
2017
+ },
2018
+ namespace: undefined,
2019
+ };
2020
+ }
2021
+ handleImportDeclaration(node) {
2022
+ if (this.#moduleSpecifiers.includes(node.moduleSpecifier.getText()) &&
2023
+ node.importClause?.isTypeOnly !== true &&
2024
+ node.importClause?.namedBindings != null) {
2025
+ if (this.#compiler.isNamedImports(node.importClause.namedBindings)) {
2026
+ for (const element of node.importClause.namedBindings.elements) {
2027
+ if (element.isTypeOnly) {
2028
+ continue;
2029
+ }
2030
+ let identifierKey;
2031
+ if (element.propertyName) {
2032
+ identifierKey = element.propertyName.getText();
2033
+ }
2034
+ else {
2035
+ identifierKey = element.name.getText();
2036
+ }
2037
+ if (identifierKey in this.#identifiers.namedImports) {
2038
+ this.#identifiers.namedImports[identifierKey] = element.name.getText();
2039
+ }
2040
+ }
2041
+ }
2042
+ if (this.#compiler.isNamespaceImport(node.importClause.namedBindings)) {
2043
+ this.#identifiers.namespace = node.importClause.namedBindings.name.getText();
2044
+ }
2045
+ }
2046
+ }
2047
+ resolveTestMemberMeta(node) {
2048
+ let flags = TestTreeNodeFlags.None;
2049
+ let expression = node.expression;
2050
+ while (this.#compiler.isPropertyAccessExpression(expression)) {
2051
+ if (expression.expression.getText() === this.#identifiers.namespace) {
2052
+ break;
2053
+ }
2054
+ switch (expression.name.getText()) {
2055
+ case "fail":
2056
+ flags |= TestTreeNodeFlags.Fail;
2057
+ break;
2058
+ case "only":
2059
+ flags |= TestTreeNodeFlags.Only;
2060
+ break;
2061
+ case "skip":
2062
+ flags |= TestTreeNodeFlags.Skip;
2063
+ break;
2064
+ case "todo":
2065
+ flags |= TestTreeNodeFlags.Todo;
2066
+ break;
2067
+ }
2068
+ expression = expression.expression;
2069
+ }
2070
+ let identifierName;
2071
+ if (this.#compiler.isPropertyAccessExpression(expression) &&
2072
+ expression.expression.getText() === this.#identifiers.namespace) {
2073
+ identifierName = expression.name.getText();
2074
+ }
2075
+ else {
2076
+ identifierName = Object.keys(this.#identifiers.namedImports).find((key) => this.#identifiers.namedImports[key] === expression.getText());
2077
+ }
2078
+ if (!identifierName) {
2079
+ return;
2080
+ }
2081
+ switch (identifierName) {
2082
+ case "describe":
2083
+ return { brand: TestTreeNodeBrand.Describe, flags };
2084
+ case "it":
2085
+ case "test":
2086
+ return { brand: TestTreeNodeBrand.Test, flags };
2087
+ case "expect":
2088
+ return { brand: TestTreeNodeBrand.Expect, flags };
2089
+ }
2090
+ return;
2091
+ }
2092
+ }
2093
+
2094
+ class TestTree {
2095
+ children = [];
2096
+ diagnostics;
2097
+ hasOnly = false;
2098
+ sourceFile;
2099
+ constructor(diagnostics, sourceFile) {
2100
+ this.diagnostics = diagnostics;
2101
+ this.sourceFile = sourceFile;
2102
+ }
2103
+ }
2104
+
2105
+ class CollectService {
2106
+ #compiler;
2107
+ constructor(compiler) {
2108
+ this.#compiler = compiler;
2109
+ }
2110
+ #collectTestTreeNodes(node, identifiers, parent) {
2111
+ if (this.#compiler.isCallExpression(node)) {
2112
+ const meta = identifiers.resolveTestMemberMeta(node);
2113
+ if (meta != null && (meta.brand === TestTreeNodeBrand.Describe || meta.brand === TestTreeNodeBrand.Test)) {
2114
+ const testTreeNode = new TestTreeNode(this.#compiler, meta.brand, node, parent, meta.flags);
2115
+ parent.children.push(testTreeNode);
2116
+ EventEmitter.dispatch(["collect:node", { testNode: testTreeNode }]);
2117
+ this.#compiler.forEachChild(node, (node) => {
2118
+ this.#collectTestTreeNodes(node, identifiers, testTreeNode);
2119
+ });
2120
+ return;
2121
+ }
2122
+ if (meta != null && meta.brand === TestTreeNodeBrand.Expect) {
2123
+ const modifierNode = this.#getChainedNode(node, "type");
2124
+ if (!modifierNode) {
2125
+ return;
2126
+ }
2127
+ const notNode = this.#getChainedNode(modifierNode, "not");
2128
+ const matcherNode = this.#getChainedNode(notNode ?? modifierNode)?.parent;
2129
+ if (!matcherNode || !this.#isMatcherNode(matcherNode)) {
2130
+ return;
2131
+ }
2132
+ const assertionNode = new AssertionNode(this.#compiler, meta.brand, node, parent, meta.flags, matcherNode, modifierNode, notNode);
2133
+ parent.children.push(assertionNode);
2134
+ EventEmitter.dispatch(["collect:node", { testNode: assertionNode }]);
2135
+ this.#compiler.forEachChild(node, (node) => {
2136
+ this.#collectTestTreeNodes(node, identifiers, assertionNode);
2137
+ });
2138
+ return;
2139
+ }
2140
+ }
2141
+ if (this.#compiler.isImportDeclaration(node)) {
2142
+ identifiers.handleImportDeclaration(node);
2143
+ return;
2144
+ }
2145
+ this.#compiler.forEachChild(node, (node) => {
2146
+ this.#collectTestTreeNodes(node, identifiers, parent);
2147
+ });
2148
+ }
2149
+ createTestTree(sourceFile, semanticDiagnostics = []) {
2150
+ const testTree = new TestTree(new Set(semanticDiagnostics), sourceFile);
2151
+ EventEmitter.dispatch(["collect:start", { testTree }]);
2152
+ this.#collectTestTreeNodes(sourceFile, new IdentifierLookup(this.#compiler), testTree);
2153
+ EventEmitter.dispatch(["collect:end", { testTree }]);
2154
+ return testTree;
2155
+ }
2156
+ #getChainedNode({ parent }, name) {
2157
+ if (!this.#compiler.isPropertyAccessExpression(parent)) {
2158
+ return;
2159
+ }
2160
+ if (name != null && name !== parent.name.getText()) {
2161
+ return;
2162
+ }
2163
+ return parent;
2164
+ }
2165
+ #isMatcherNode(node) {
2166
+ return this.#compiler.isCallExpression(node) && this.#compiler.isPropertyAccessExpression(node.expression);
2167
+ }
2168
+ }
2169
+
2170
+ class TestTreeHandler {
2171
+ testTree;
2172
+ on([event, payload]) {
2173
+ switch (event) {
2174
+ case "collect:start":
2175
+ this.testTree = payload.testTree;
2176
+ break;
2177
+ case "collect:end":
2178
+ this.testTree = undefined;
2179
+ break;
2180
+ case "collect:node":
2181
+ if (payload.testNode.flags & TestTreeNodeFlags.Only) {
2182
+ this.testTree.hasOnly = true;
2183
+ }
2184
+ break;
2185
+ }
2186
+ }
2187
+ }
2188
+
1912
2189
  function jsx(type, props) {
1913
2190
  return { props, type };
1914
2191
  }
@@ -2150,7 +2427,7 @@ function CommandLineUsageText() {
2150
2427
  const usage = [
2151
2428
  ["tstyche", "Run all tests."],
2152
2429
  ["tstyche path/to/first.test.ts", "Only run the test files with matching path."],
2153
- ["tstyche --target 4.9,5.3.2,current", "Test on all specified versions of TypeScript."],
2430
+ ["tstyche --target 5.3,5.6.2,current", "Test on all specified versions of TypeScript."],
2154
2431
  ];
2155
2432
  const usageText = usage.map(([commandText, descriptionText]) => (jsx(Line, { children: [jsx(CommandText, { text: commandText }), jsx(OptionDescriptionText, { text: descriptionText })] })));
2156
2433
  return jsx(Text, { children: usageText });
@@ -2363,21 +2640,11 @@ class ListReporter extends BaseReporter {
2363
2640
  #hasReportedError = false;
2364
2641
  #hasReportedUses = false;
2365
2642
  #isFileViewExpanded = false;
2366
- #seenDeprecations = new Set();
2367
2643
  get #isLastFile() {
2368
2644
  return this.#fileCount === 0;
2369
2645
  }
2370
2646
  on([event, payload]) {
2371
2647
  switch (event) {
2372
- case "deprecation:info": {
2373
- for (const diagnostic of payload.diagnostics) {
2374
- if (!this.#seenDeprecations.has(diagnostic.text.toString())) {
2375
- this.#fileView.addMessage(diagnosticText(diagnostic));
2376
- this.#seenDeprecations.add(diagnostic.text.toString());
2377
- }
2378
- }
2379
- break;
2380
- }
2381
2648
  case "run:start":
2382
2649
  this.#isFileViewExpanded = payload.result.tasks.length === 1 && this.resolvedConfig.watch !== true;
2383
2650
  break;
@@ -2429,7 +2696,6 @@ class ListReporter extends BaseReporter {
2429
2696
  this.#hasReportedError = true;
2430
2697
  }
2431
2698
  this.#fileView.clear();
2432
- this.#seenDeprecations.clear();
2433
2699
  break;
2434
2700
  case "describe:start":
2435
2701
  if (this.#isFileViewExpanded) {
@@ -2800,19 +3066,19 @@ class Debounce {
2800
3066
  this.#delay = delay;
2801
3067
  this.#onResolve = onResolve;
2802
3068
  }
2803
- clearTimeout() {
3069
+ cancel() {
2804
3070
  clearTimeout(this.#timeout);
2805
3071
  }
2806
- refreshTimeout() {
2807
- this.clearTimeout();
3072
+ refresh() {
3073
+ this.cancel();
2808
3074
  this.#timeout = setTimeout(() => {
2809
3075
  this.#resolve?.(this.#onResolve());
2810
3076
  }, this.#delay);
2811
3077
  }
2812
- resolveWith(value) {
3078
+ resolve(value) {
2813
3079
  this.#resolve?.(value);
2814
3080
  }
2815
- setup() {
3081
+ schedule() {
2816
3082
  return new Promise((resolve) => {
2817
3083
  this.#resolve = resolve;
2818
3084
  });
@@ -2840,13 +3106,13 @@ class WatchService {
2840
3106
  };
2841
3107
  const debounce = new Debounce(100, onResolve);
2842
3108
  const onClose = (reason) => {
2843
- debounce.clearTimeout();
3109
+ debounce.cancel();
2844
3110
  this.#inputService?.close();
2845
3111
  for (const watcher of this.#watchers) {
2846
3112
  watcher.close();
2847
3113
  }
2848
3114
  cancellationToken.cancel(reason);
2849
- debounce.resolveWith([]);
3115
+ debounce.resolve([]);
2850
3116
  };
2851
3117
  if (!environmentOptions.noInteractive) {
2852
3118
  const onInput = (chunk) => {
@@ -2861,9 +3127,9 @@ class WatchService {
2861
3127
  case "\u000D":
2862
3128
  case "\u0020":
2863
3129
  case "a":
2864
- debounce.clearTimeout();
3130
+ debounce.cancel();
2865
3131
  if (this.#watchedTestFiles.size > 0) {
2866
- debounce.resolveWith([...this.#watchedTestFiles.values()]);
3132
+ debounce.resolve([...this.#watchedTestFiles.values()]);
2867
3133
  }
2868
3134
  break;
2869
3135
  }
@@ -2871,7 +3137,7 @@ class WatchService {
2871
3137
  this.#inputService = new InputService(onInput);
2872
3138
  }
2873
3139
  const onChangedFile = (filePath) => {
2874
- debounce.refreshTimeout();
3140
+ debounce.refresh();
2875
3141
  let task = this.#watchedTestFiles.get(filePath);
2876
3142
  if (task != null) {
2877
3143
  this.#changedTestFiles.set(filePath, task);
@@ -2886,7 +3152,7 @@ class WatchService {
2886
3152
  this.#changedTestFiles.delete(filePath);
2887
3153
  this.#watchedTestFiles.delete(filePath);
2888
3154
  if (this.#watchedTestFiles.size === 0) {
2889
- debounce.clearTimeout();
3155
+ debounce.cancel();
2890
3156
  this.#onDiagnostics(Diagnostic.error(SelectDiagnosticText.noTestFilesWereLeft(this.#resolvedConfig)));
2891
3157
  }
2892
3158
  };
@@ -2899,7 +3165,7 @@ class WatchService {
2899
3165
  watcher.watch();
2900
3166
  }
2901
3167
  while (!cancellationToken.isCancellationRequested) {
2902
- const testFiles = await debounce.setup();
3168
+ const testFiles = await debounce.schedule();
2903
3169
  if (testFiles.length > 0) {
2904
3170
  yield testFiles;
2905
3171
  }
@@ -2907,276 +3173,7 @@ class WatchService {
2907
3173
  }
2908
3174
  }
2909
3175
 
2910
- var TestMemberBrand;
2911
- (function (TestMemberBrand) {
2912
- TestMemberBrand["Describe"] = "describe";
2913
- TestMemberBrand["Test"] = "test";
2914
- TestMemberBrand["Expect"] = "expect";
2915
- })(TestMemberBrand || (TestMemberBrand = {}));
2916
-
2917
- class TestMember {
2918
- brand;
2919
- #compiler;
2920
- diagnostics = new Set();
2921
- flags;
2922
- members = [];
2923
- name = "";
2924
- node;
2925
- parent;
2926
- constructor(compiler, brand, node, parent, flags) {
2927
- this.brand = brand;
2928
- this.#compiler = compiler;
2929
- this.node = node;
2930
- this.parent = parent;
2931
- this.flags = flags;
2932
- if (node.arguments[0] != null && compiler.isStringLiteralLike(node.arguments[0])) {
2933
- this.name = node.arguments[0].text;
2934
- }
2935
- if (node.arguments[1] != null &&
2936
- compiler.isFunctionLike(node.arguments[1]) &&
2937
- compiler.isBlock(node.arguments[1].body)) {
2938
- const blockStart = node.arguments[1].body.getStart();
2939
- const blockEnd = node.arguments[1].body.getEnd();
2940
- for (const diagnostic of parent.diagnostics) {
2941
- if (diagnostic.start != null && diagnostic.start >= blockStart && diagnostic.start <= blockEnd) {
2942
- this.diagnostics.add(diagnostic);
2943
- parent.diagnostics.delete(diagnostic);
2944
- }
2945
- }
2946
- }
2947
- }
2948
- validate() {
2949
- const diagnostics = [];
2950
- const getText = (node) => `'${node.expression.getText()}()' cannot be nested within '${this.node.expression.getText()}()'.`;
2951
- const getParentCallExpression = (node) => {
2952
- while (!this.#compiler.isCallExpression(node.parent)) {
2953
- node = node.parent;
2954
- }
2955
- return node.parent;
2956
- };
2957
- switch (this.brand) {
2958
- case TestMemberBrand.Describe:
2959
- for (const member of this.members) {
2960
- if (member.brand === TestMemberBrand.Expect) {
2961
- diagnostics.push(Diagnostic.error(getText(member.node), DiagnosticOrigin.fromNode(getParentCallExpression(member.node))));
2962
- }
2963
- }
2964
- break;
2965
- case TestMemberBrand.Test:
2966
- case TestMemberBrand.Expect:
2967
- for (const member of this.members) {
2968
- if (member.brand !== TestMemberBrand.Expect) {
2969
- diagnostics.push(Diagnostic.error(getText(member.node), DiagnosticOrigin.fromNode(member.node)));
2970
- }
2971
- }
2972
- break;
2973
- }
2974
- return diagnostics;
2975
- }
2976
- }
2977
-
2978
- class Assertion extends TestMember {
2979
- isNot;
2980
- matcherName;
2981
- matcherNode;
2982
- modifierNode;
2983
- notNode;
2984
- source;
2985
- target;
2986
- constructor(compiler, brand, node, parent, flags, matcherNode, modifierNode, notNode) {
2987
- super(compiler, brand, node, parent, flags);
2988
- this.isNot = notNode != null;
2989
- this.matcherName = matcherNode.expression.name;
2990
- this.matcherNode = matcherNode;
2991
- this.modifierNode = modifierNode;
2992
- this.source = this.node.typeArguments ?? this.node.arguments;
2993
- this.target = this.matcherNode.typeArguments ?? this.matcherNode.arguments;
2994
- for (const diagnostic of parent.diagnostics) {
2995
- if (diagnostic.start != null && diagnostic.start >= this.source.pos && diagnostic.start <= this.source.end) {
2996
- this.diagnostics.add(diagnostic);
2997
- parent.diagnostics.delete(diagnostic);
2998
- }
2999
- }
3000
- }
3001
- }
3002
-
3003
- var TestMemberFlags;
3004
- (function (TestMemberFlags) {
3005
- TestMemberFlags[TestMemberFlags["None"] = 0] = "None";
3006
- TestMemberFlags[TestMemberFlags["Fail"] = 1] = "Fail";
3007
- TestMemberFlags[TestMemberFlags["Only"] = 2] = "Only";
3008
- TestMemberFlags[TestMemberFlags["Skip"] = 4] = "Skip";
3009
- TestMemberFlags[TestMemberFlags["Todo"] = 8] = "Todo";
3010
- })(TestMemberFlags || (TestMemberFlags = {}));
3011
-
3012
- class IdentifierLookup {
3013
- #compiler;
3014
- #identifiers;
3015
- #moduleSpecifiers = ['"tstyche"', "'tstyche'"];
3016
- constructor(compiler, identifiers) {
3017
- this.#compiler = compiler;
3018
- this.#identifiers = identifiers ?? {
3019
- namedImports: {
3020
- describe: undefined,
3021
- expect: undefined,
3022
- it: undefined,
3023
- namespace: undefined,
3024
- test: undefined,
3025
- },
3026
- namespace: undefined,
3027
- };
3028
- }
3029
- handleImportDeclaration(node) {
3030
- if (this.#moduleSpecifiers.includes(node.moduleSpecifier.getText()) &&
3031
- node.importClause?.isTypeOnly !== true &&
3032
- node.importClause?.namedBindings != null) {
3033
- if (this.#compiler.isNamedImports(node.importClause.namedBindings)) {
3034
- for (const element of node.importClause.namedBindings.elements) {
3035
- if (element.isTypeOnly) {
3036
- continue;
3037
- }
3038
- let identifierKey;
3039
- if (element.propertyName) {
3040
- identifierKey = element.propertyName.getText();
3041
- }
3042
- else {
3043
- identifierKey = element.name.getText();
3044
- }
3045
- if (identifierKey in this.#identifiers.namedImports) {
3046
- this.#identifiers.namedImports[identifierKey] = element.name.getText();
3047
- }
3048
- }
3049
- }
3050
- if (this.#compiler.isNamespaceImport(node.importClause.namedBindings)) {
3051
- this.#identifiers.namespace = node.importClause.namedBindings.name.getText();
3052
- }
3053
- }
3054
- }
3055
- resolveTestMemberMeta(node) {
3056
- let flags = TestMemberFlags.None;
3057
- let expression = node.expression;
3058
- while (this.#compiler.isPropertyAccessExpression(expression)) {
3059
- if (expression.expression.getText() === this.#identifiers.namespace) {
3060
- break;
3061
- }
3062
- switch (expression.name.getText()) {
3063
- case "fail":
3064
- flags |= TestMemberFlags.Fail;
3065
- break;
3066
- case "only":
3067
- flags |= TestMemberFlags.Only;
3068
- break;
3069
- case "skip":
3070
- flags |= TestMemberFlags.Skip;
3071
- break;
3072
- case "todo":
3073
- flags |= TestMemberFlags.Todo;
3074
- break;
3075
- }
3076
- expression = expression.expression;
3077
- }
3078
- let identifierName;
3079
- if (this.#compiler.isPropertyAccessExpression(expression) &&
3080
- expression.expression.getText() === this.#identifiers.namespace) {
3081
- identifierName = expression.name.getText();
3082
- }
3083
- else {
3084
- identifierName = Object.keys(this.#identifiers.namedImports).find((key) => this.#identifiers.namedImports[key] === expression.getText());
3085
- }
3086
- if (!identifierName) {
3087
- return;
3088
- }
3089
- switch (identifierName) {
3090
- case "describe":
3091
- return { brand: TestMemberBrand.Describe, flags };
3092
- case "it":
3093
- case "test":
3094
- return { brand: TestMemberBrand.Test, flags };
3095
- case "expect":
3096
- return { brand: TestMemberBrand.Expect, flags };
3097
- }
3098
- return;
3099
- }
3100
- }
3101
-
3102
- class TestTree {
3103
- diagnostics;
3104
- members = [];
3105
- sourceFile;
3106
- constructor(diagnostics, sourceFile) {
3107
- this.diagnostics = diagnostics;
3108
- this.sourceFile = sourceFile;
3109
- }
3110
- get hasOnly() {
3111
- function hasOnly(root) {
3112
- return root.members.some((branch) => branch.flags & TestMemberFlags.Only || ("members" in branch && hasOnly(branch)));
3113
- }
3114
- return hasOnly(this);
3115
- }
3116
- }
3117
-
3118
- class CollectService {
3119
- #compiler;
3120
- constructor(compiler) {
3121
- this.#compiler = compiler;
3122
- }
3123
- #collectTestMembers(node, identifiers, parent) {
3124
- if (this.#compiler.isCallExpression(node)) {
3125
- const meta = identifiers.resolveTestMemberMeta(node);
3126
- if (meta != null && (meta.brand === TestMemberBrand.Describe || meta.brand === TestMemberBrand.Test)) {
3127
- const testMember = new TestMember(this.#compiler, meta.brand, node, parent, meta.flags);
3128
- parent.members.push(testMember);
3129
- this.#compiler.forEachChild(node, (node) => {
3130
- this.#collectTestMembers(node, identifiers, testMember);
3131
- });
3132
- return;
3133
- }
3134
- if (meta != null && meta.brand === TestMemberBrand.Expect) {
3135
- const modifierNode = this.#getChainedNode(node, "type");
3136
- if (!modifierNode) {
3137
- return;
3138
- }
3139
- const notNode = this.#getChainedNode(modifierNode, "not");
3140
- const matcherNode = this.#getChainedNode(notNode ?? modifierNode)?.parent;
3141
- if (!matcherNode || !this.#isMatcherNode(matcherNode)) {
3142
- return;
3143
- }
3144
- const assertion = new Assertion(this.#compiler, meta.brand, node, parent, meta.flags, matcherNode, modifierNode, notNode);
3145
- parent.members.push(assertion);
3146
- this.#compiler.forEachChild(node, (node) => {
3147
- this.#collectTestMembers(node, identifiers, assertion);
3148
- });
3149
- return;
3150
- }
3151
- }
3152
- if (this.#compiler.isImportDeclaration(node)) {
3153
- identifiers.handleImportDeclaration(node);
3154
- return;
3155
- }
3156
- this.#compiler.forEachChild(node, (node) => {
3157
- this.#collectTestMembers(node, identifiers, parent);
3158
- });
3159
- }
3160
- createTestTree(sourceFile, semanticDiagnostics = []) {
3161
- const testTree = new TestTree(new Set(semanticDiagnostics), sourceFile);
3162
- this.#collectTestMembers(sourceFile, new IdentifierLookup(this.#compiler), testTree);
3163
- return testTree;
3164
- }
3165
- #getChainedNode({ parent }, name) {
3166
- if (!this.#compiler.isPropertyAccessExpression(parent)) {
3167
- return;
3168
- }
3169
- if (name != null && name !== parent.name.getText()) {
3170
- return;
3171
- }
3172
- return parent;
3173
- }
3174
- #isMatcherNode(node) {
3175
- return this.#compiler.isCallExpression(node) && this.#compiler.isPropertyAccessExpression(node.expression);
3176
- }
3177
- }
3178
-
3179
- class ProjectService {
3176
+ class ProjectService {
3180
3177
  #compiler;
3181
3178
  #lastSeenProject = "";
3182
3179
  #resolvedConfig;
@@ -3234,24 +3231,19 @@ class ProjectService {
3234
3231
  }
3235
3232
  #getDefaultCompilerOptions() {
3236
3233
  const defaultCompilerOptions = {
3234
+ allowImportingTsExtensions: true,
3237
3235
  allowJs: true,
3238
3236
  checkJs: true,
3239
- esModuleInterop: true,
3240
- jsx: "preserve",
3241
- module: "esnext",
3242
- moduleResolution: "node",
3237
+ exactOptionalPropertyTypes: true,
3238
+ jsx: this.#compiler.JsxEmit.Preserve,
3239
+ module: this.#compiler.ModuleKind.NodeNext,
3240
+ moduleResolution: this.#compiler.ModuleResolutionKind.NodeNext,
3241
+ noUncheckedIndexedAccess: true,
3243
3242
  resolveJsonModule: true,
3244
- strictFunctionTypes: true,
3245
- strictNullChecks: true,
3246
- target: "esnext",
3243
+ strict: true,
3244
+ target: this.#compiler.ScriptTarget.ESNext,
3245
+ verbatimModuleSyntax: true,
3247
3246
  };
3248
- if (Version.isSatisfiedWith(this.#compiler.version, "5.4")) {
3249
- defaultCompilerOptions.module = "preserve";
3250
- }
3251
- if (Version.isSatisfiedWith(this.#compiler.version, "5.0")) {
3252
- defaultCompilerOptions.allowImportingTsExtensions = true;
3253
- defaultCompilerOptions.moduleResolution = "bundler";
3254
- }
3255
3247
  return defaultCompilerOptions;
3256
3248
  }
3257
3249
  getDefaultProject(filePath) {
@@ -3343,12 +3335,6 @@ class ExpectDiagnosticText {
3343
3335
  static componentDoesNotAcceptProps(isTypeNode) {
3344
3336
  return `${isTypeNode ? "Component type" : "Component"} does not accept props of the given type.`;
3345
3337
  }
3346
- static matcherIsDeprecated(matcherNameText) {
3347
- return [
3348
- `The '.${matcherNameText}()' matcher is deprecated and will be removed in TSTyche 4.`,
3349
- "To learn more, visit https://tstyche.org/releases/tstyche-3",
3350
- ];
3351
- }
3352
3338
  static matcherIsNotSupported(matcherNameText) {
3353
3339
  return `The '.${matcherNameText}()' matcher is not supported.`;
3354
3340
  }
@@ -3373,18 +3359,9 @@ class ExpectDiagnosticText {
3373
3359
  static typeDoesNotHaveProperty(typeText, propertyNameText) {
3374
3360
  return `Type '${typeText}' does not have property '${propertyNameText}'.`;
3375
3361
  }
3376
- static typeDoesMatch(sourceTypeText, targetTypeText) {
3377
- return `Type '${sourceTypeText}' does match type '${targetTypeText}'.`;
3378
- }
3379
- static typeDoesNotMatch(sourceTypeText, targetTypeText) {
3380
- return `Type '${sourceTypeText}' does not match type '${targetTypeText}'.`;
3381
- }
3382
3362
  static typeHasProperty(typeText, propertyNameText) {
3383
3363
  return `Type '${typeText}' has property '${propertyNameText}'.`;
3384
3364
  }
3385
- static typeIs(typeText) {
3386
- return `Type is '${typeText}'.`;
3387
- }
3388
3365
  static typeIsAssignableTo(sourceTypeText, targetTypeText) {
3389
3366
  return `Type '${sourceTypeText}' is assignable to type '${targetTypeText}'.`;
3390
3367
  }
@@ -3445,18 +3422,9 @@ class MatchWorker {
3445
3422
  checkHasApplicableIndexType(sourceNode, targetNode) {
3446
3423
  const sourceType = this.getType(sourceNode);
3447
3424
  const targetType = this.getType(targetNode);
3448
- if (Version.isSatisfiedWith(this.#compiler.version, "4.4")) {
3449
- return this.#typeChecker
3450
- .getIndexInfosOfType(sourceType)
3451
- .some(({ keyType }) => this.#typeChecker.isApplicableIndexType(targetType, keyType));
3452
- }
3453
- if (targetType.flags & this.#compiler.TypeFlags.StringLiteral) {
3454
- return sourceType.getStringIndexType() != null;
3455
- }
3456
- if (targetType.flags & this.#compiler.TypeFlags.NumberLiteral) {
3457
- return (sourceType.getStringIndexType() ?? sourceType.getNumberIndexType()) != null;
3458
- }
3459
- return false;
3425
+ return this.#typeChecker
3426
+ .getIndexInfosOfType(sourceType)
3427
+ .some(({ keyType }) => this.#typeChecker.isApplicableIndexType(targetType, keyType));
3460
3428
  }
3461
3429
  checkHasProperty(sourceNode, propertyNameText) {
3462
3430
  const sourceType = this.getType(sourceNode);
@@ -3478,10 +3446,6 @@ class MatchWorker {
3478
3446
  this.checkIsAssignableTo(sourceNode, targetNode) &&
3479
3447
  this.checkIsAssignableWith(sourceNode, targetNode));
3480
3448
  }
3481
- checkIsSubtype(sourceNode, targetNode) {
3482
- const relation = this.#typeChecker.relation.subtype;
3483
- return this.#checkIsRelatedTo(sourceNode, targetNode, relation);
3484
- }
3485
3449
  #checkIsRelatedTo(sourceNode, targetNode, relation) {
3486
3450
  const sourceType = relation === this.#typeChecker.relation.identity
3487
3451
  ? this.#simplifyType(this.getType(sourceNode))
@@ -3568,26 +3532,6 @@ class MatchWorker {
3568
3532
  }
3569
3533
  }
3570
3534
 
3571
- class PrimitiveTypeMatcher {
3572
- #targetTypeFlag;
3573
- constructor(targetTypeFlag) {
3574
- this.#targetTypeFlag = targetTypeFlag;
3575
- }
3576
- #explain(matchWorker, sourceNode) {
3577
- const sourceTypeText = matchWorker.getTypeText(sourceNode);
3578
- const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertion);
3579
- return [Diagnostic.error(ExpectDiagnosticText.typeIs(sourceTypeText), origin)];
3580
- }
3581
- match(matchWorker, sourceNode) {
3582
- const sourceType = matchWorker.getType(sourceNode);
3583
- const isMatch = !!(sourceType.flags & this.#targetTypeFlag);
3584
- return {
3585
- explain: () => this.#explain(matchWorker, sourceNode),
3586
- isMatch,
3587
- };
3588
- }
3589
- }
3590
-
3591
3535
  class ToAcceptProps {
3592
3536
  #compiler;
3593
3537
  #typeChecker;
@@ -3875,17 +3819,6 @@ class ToHaveProperty {
3875
3819
  }
3876
3820
  }
3877
3821
 
3878
- class ToMatch extends RelationMatcherBase {
3879
- explainText = ExpectDiagnosticText.typeDoesMatch;
3880
- explainNotText = ExpectDiagnosticText.typeDoesNotMatch;
3881
- match(matchWorker, sourceNode, targetNode) {
3882
- return {
3883
- explain: () => this.explain(matchWorker, sourceNode, targetNode),
3884
- isMatch: matchWorker.checkIsSubtype(sourceNode, targetNode),
3885
- };
3886
- }
3887
- }
3888
-
3889
3822
  class ToRaiseError {
3890
3823
  #compiler;
3891
3824
  constructor(compiler) {
@@ -3955,14 +3888,17 @@ class ToRaiseError {
3955
3888
  };
3956
3889
  }
3957
3890
  #matchExpectedError(diagnostic, targetNode) {
3891
+ if (this.#compiler.isNumericLiteral(targetNode)) {
3892
+ return Number.parseInt(targetNode.text, 10) === diagnostic.code;
3893
+ }
3894
+ const messageText = typeof diagnostic.messageText === "string"
3895
+ ? diagnostic.messageText
3896
+ : Diagnostic.toMessageText(diagnostic.messageText).join("\n");
3958
3897
  if (this.#compiler.isRegularExpressionLiteral(targetNode)) {
3959
3898
  const targetRegex = new RegExp(...targetNode.text.slice(1).split("/"));
3960
- return targetRegex.test(this.#compiler.flattenDiagnosticMessageText(diagnostic.messageText, " ", 0));
3961
- }
3962
- if (this.#compiler.isStringLiteralLike(targetNode)) {
3963
- return this.#compiler.flattenDiagnosticMessageText(diagnostic.messageText, " ", 0).includes(targetNode.text);
3899
+ return targetRegex.test(messageText);
3964
3900
  }
3965
- return Number.parseInt(targetNode.text, 10) === diagnostic.code;
3901
+ return messageText.includes(targetNode.text);
3966
3902
  }
3967
3903
  }
3968
3904
 
@@ -3972,22 +3908,9 @@ class ExpectService {
3972
3908
  #typeChecker;
3973
3909
  toAcceptProps;
3974
3910
  toBe;
3975
- toBeAny;
3976
3911
  toBeAssignableTo;
3977
3912
  toBeAssignableWith;
3978
- toBeBigInt;
3979
- toBeBoolean;
3980
- toBeNever;
3981
- toBeNull;
3982
- toBeNumber;
3983
- toBeString;
3984
- toBeSymbol;
3985
- toBeUndefined;
3986
- toBeUniqueSymbol;
3987
- toBeUnknown;
3988
- toBeVoid;
3989
3913
  toHaveProperty;
3990
- toMatch;
3991
3914
  toRaiseError;
3992
3915
  constructor(compiler, typeChecker, resolvedConfig) {
3993
3916
  this.#compiler = compiler;
@@ -4000,31 +3923,13 @@ class ExpectService {
4000
3923
  }
4001
3924
  this.toAcceptProps = new ToAcceptProps(compiler, typeChecker);
4002
3925
  this.toBe = new ToBe();
4003
- this.toBeAny = new PrimitiveTypeMatcher(compiler.TypeFlags.Any);
4004
3926
  this.toBeAssignableTo = new ToBeAssignableTo();
4005
3927
  this.toBeAssignableWith = new ToBeAssignableWith();
4006
- this.toBeBigInt = new PrimitiveTypeMatcher(compiler.TypeFlags.BigInt);
4007
- this.toBeBoolean = new PrimitiveTypeMatcher(compiler.TypeFlags.Boolean);
4008
- this.toBeNever = new PrimitiveTypeMatcher(compiler.TypeFlags.Never);
4009
- this.toBeNull = new PrimitiveTypeMatcher(compiler.TypeFlags.Null);
4010
- this.toBeNumber = new PrimitiveTypeMatcher(compiler.TypeFlags.Number);
4011
- this.toBeString = new PrimitiveTypeMatcher(compiler.TypeFlags.String);
4012
- this.toBeSymbol = new PrimitiveTypeMatcher(compiler.TypeFlags.ESSymbol);
4013
- this.toBeUndefined = new PrimitiveTypeMatcher(compiler.TypeFlags.Undefined);
4014
- this.toBeUniqueSymbol = new PrimitiveTypeMatcher(compiler.TypeFlags.UniqueESSymbol);
4015
- this.toBeUnknown = new PrimitiveTypeMatcher(compiler.TypeFlags.Unknown);
4016
- this.toBeVoid = new PrimitiveTypeMatcher(compiler.TypeFlags.Void);
4017
3928
  this.toHaveProperty = new ToHaveProperty(compiler);
4018
- this.toMatch = new ToMatch();
4019
3929
  this.toRaiseError = new ToRaiseError(compiler);
4020
3930
  }
4021
3931
  match(assertion, onDiagnostics) {
4022
3932
  const matcherNameText = assertion.matcherName.getText();
4023
- if (matcherNameText === "toMatch") {
4024
- const text = ExpectDiagnosticText.matcherIsDeprecated(matcherNameText);
4025
- const origin = DiagnosticOrigin.fromNode(assertion.matcherName);
4026
- EventEmitter.dispatch(["deprecation:info", { diagnostics: [Diagnostic.warning(text, origin)] }]);
4027
- }
4028
3933
  if (!assertion.source[0]) {
4029
3934
  this.#onSourceArgumentOrTypeArgumentMustBeProvided(assertion, onDiagnostics);
4030
3935
  return;
@@ -4035,7 +3940,6 @@ class ExpectService {
4035
3940
  case "toBe":
4036
3941
  case "toBeAssignableTo":
4037
3942
  case "toBeAssignableWith":
4038
- case "toMatch":
4039
3943
  if (!assertion.target[0]) {
4040
3944
  this.#onTargetArgumentOrTypeArgumentMustBeProvided(assertion, onDiagnostics);
4041
3945
  return;
@@ -4044,19 +3948,6 @@ class ExpectService {
4044
3948
  return;
4045
3949
  }
4046
3950
  return this[matcherNameText].match(matchWorker, assertion.source[0], assertion.target[0], onDiagnostics);
4047
- case "toBeAny":
4048
- case "toBeBigInt":
4049
- case "toBeBoolean":
4050
- case "toBeNever":
4051
- case "toBeNull":
4052
- case "toBeNumber":
4053
- case "toBeString":
4054
- case "toBeSymbol":
4055
- case "toBeUndefined":
4056
- case "toBeUniqueSymbol":
4057
- case "toBeUnknown":
4058
- case "toBeVoid":
4059
- return this[matcherNameText].match(matchWorker, assertion.source[0]);
4060
3951
  case "toHaveProperty":
4061
3952
  if (!assertion.target[0]) {
4062
3953
  this.#onTargetArgumentMustBeProvided("key", assertion, onDiagnostics);
@@ -4095,10 +3986,14 @@ class ExpectService {
4095
3986
  }
4096
3987
  #rejectsTypeArguments(matchWorker, onDiagnostics) {
4097
3988
  for (const rejectedType of this.#rejectTypes) {
3989
+ const allowedKeyword = this.#compiler.SyntaxKind[`${Format.capitalize(rejectedType)}Keyword`];
3990
+ if (matchWorker.assertion.source[0]?.kind === allowedKeyword ||
3991
+ matchWorker.assertion.target[0]?.kind === allowedKeyword) {
3992
+ continue;
3993
+ }
4098
3994
  for (const argumentName of ["source", "target"]) {
4099
3995
  const argumentNode = matchWorker.assertion[argumentName][0];
4100
- if (!argumentNode ||
4101
- argumentNode.kind === this.#compiler.SyntaxKind[`${Format.capitalize(rejectedType)}Keyword`]) {
3996
+ if (!argumentNode) {
4102
3997
  continue;
4103
3998
  }
4104
3999
  if (matchWorker.getType(argumentNode).flags & this.#compiler.TypeFlags[Format.capitalize(rejectedType)]) {
@@ -4119,7 +4014,6 @@ class ExpectService {
4119
4014
  }
4120
4015
 
4121
4016
  class TestTreeWalker {
4122
- #compiler;
4123
4017
  #cancellationToken;
4124
4018
  #expectService;
4125
4019
  #hasOnly;
@@ -4128,59 +4022,60 @@ class TestTreeWalker {
4128
4022
  #taskResult;
4129
4023
  constructor(resolvedConfig, compiler, typeChecker, options) {
4130
4024
  this.#resolvedConfig = resolvedConfig;
4131
- this.#compiler = compiler;
4132
4025
  this.#cancellationToken = options.cancellationToken;
4133
4026
  this.#hasOnly = options.hasOnly || resolvedConfig.only != null || options.position != null;
4134
4027
  this.#position = options.position;
4135
4028
  this.#taskResult = options.taskResult;
4136
4029
  this.#expectService = new ExpectService(compiler, typeChecker, this.#resolvedConfig);
4137
4030
  }
4138
- #resolveRunMode(mode, member) {
4139
- if (member.flags & TestMemberFlags.Fail) {
4031
+ #resolveRunMode(mode, testNode) {
4032
+ if (testNode.flags & TestTreeNodeFlags.Fail) {
4140
4033
  mode |= RunMode.Fail;
4141
4034
  }
4142
- if (member.flags & TestMemberFlags.Only ||
4143
- (this.#resolvedConfig.only != null && member.name.toLowerCase().includes(this.#resolvedConfig.only.toLowerCase()))) {
4035
+ if (testNode.flags & TestTreeNodeFlags.Only ||
4036
+ (this.#resolvedConfig.only != null &&
4037
+ testNode.name.toLowerCase().includes(this.#resolvedConfig.only.toLowerCase()))) {
4144
4038
  mode |= RunMode.Only;
4145
4039
  }
4146
- if (member.flags & TestMemberFlags.Skip ||
4147
- (this.#resolvedConfig.skip != null && member.name.toLowerCase().includes(this.#resolvedConfig.skip.toLowerCase()))) {
4040
+ if (testNode.flags & TestTreeNodeFlags.Skip ||
4041
+ (this.#resolvedConfig.skip != null &&
4042
+ testNode.name.toLowerCase().includes(this.#resolvedConfig.skip.toLowerCase()))) {
4148
4043
  mode |= RunMode.Skip;
4149
4044
  }
4150
- if (member.flags & TestMemberFlags.Todo) {
4045
+ if (testNode.flags & TestTreeNodeFlags.Todo) {
4151
4046
  mode |= RunMode.Todo;
4152
4047
  }
4153
- if (this.#position != null && member.node.getStart() === this.#position) {
4048
+ if (this.#position != null && testNode.node.getStart() === this.#position) {
4154
4049
  mode |= RunMode.Only;
4155
4050
  mode &= ~RunMode.Skip;
4156
4051
  }
4157
4052
  return mode;
4158
4053
  }
4159
- visit(members, runMode, parentResult) {
4160
- for (const member of members) {
4054
+ visit(testNodes, runMode, parentResult) {
4055
+ for (const testNode of testNodes) {
4161
4056
  if (this.#cancellationToken?.isCancellationRequested) {
4162
4057
  break;
4163
4058
  }
4164
- const validationError = member.validate();
4059
+ const validationError = testNode.validate();
4165
4060
  if (validationError.length > 0) {
4166
4061
  EventEmitter.dispatch(["task:error", { diagnostics: validationError, result: this.#taskResult }]);
4167
4062
  break;
4168
4063
  }
4169
- switch (member.brand) {
4170
- case TestMemberBrand.Describe:
4171
- this.#visitDescribe(member, runMode, parentResult);
4064
+ switch (testNode.brand) {
4065
+ case TestTreeNodeBrand.Describe:
4066
+ this.#visitDescribe(testNode, runMode, parentResult);
4172
4067
  break;
4173
- case TestMemberBrand.Test:
4174
- this.#visitTest(member, runMode, parentResult);
4068
+ case TestTreeNodeBrand.Test:
4069
+ this.#visitTest(testNode, runMode, parentResult);
4175
4070
  break;
4176
- case TestMemberBrand.Expect:
4177
- this.#visitAssertion(member, runMode, parentResult);
4071
+ case TestTreeNodeBrand.Expect:
4072
+ this.#visitAssertion(testNode, runMode, parentResult);
4178
4073
  break;
4179
4074
  }
4180
4075
  }
4181
4076
  }
4182
4077
  #visitAssertion(assertion, runMode, parentResult) {
4183
- this.visit(assertion.members, runMode, parentResult);
4078
+ this.visit(assertion.children, runMode, parentResult);
4184
4079
  const expectResult = new ExpectResult(assertion, parentResult);
4185
4080
  EventEmitter.dispatch(["expect:start", { result: expectResult }]);
4186
4081
  runMode = this.#resolveRunMode(runMode, assertion);
@@ -4234,7 +4129,7 @@ class TestTreeWalker {
4234
4129
  ]);
4235
4130
  }
4236
4131
  else {
4237
- this.visit(describe.members, runMode, describeResult);
4132
+ this.visit(describe.children, runMode, describeResult);
4238
4133
  }
4239
4134
  EventEmitter.dispatch(["describe:end", { result: describeResult }]);
4240
4135
  }
@@ -4256,7 +4151,7 @@ class TestTreeWalker {
4256
4151
  ]);
4257
4152
  return;
4258
4153
  }
4259
- this.visit(test.members, runMode, testResult);
4154
+ this.visit(test.children, runMode, testResult);
4260
4155
  if (runMode & RunMode.Skip || (this.#hasOnly && !(runMode & RunMode.Only))) {
4261
4156
  EventEmitter.dispatch(["test:skip", { result: testResult }]);
4262
4157
  return;
@@ -4336,17 +4231,27 @@ class TaskRunner {
4336
4231
  hasOnly: testTree.hasOnly,
4337
4232
  position: task.position,
4338
4233
  });
4339
- testTreeWalker.visit(testTree.members, RunMode.Normal, undefined);
4234
+ testTreeWalker.visit(testTree.children, RunMode.Normal, undefined);
4340
4235
  }
4341
4236
  }
4342
4237
 
4343
4238
  class Runner {
4344
4239
  #eventEmitter = new EventEmitter();
4345
4240
  #resolvedConfig;
4346
- static version = "3.5.0";
4241
+ static version = "4.0.0-beta.0";
4347
4242
  constructor(resolvedConfig) {
4348
4243
  this.#resolvedConfig = resolvedConfig;
4349
4244
  }
4245
+ #addHandlers(cancellationToken) {
4246
+ const resultHandler = new ResultHandler();
4247
+ this.#eventEmitter.addHandler(resultHandler);
4248
+ const testTreeHandler = new TestTreeHandler();
4249
+ this.#eventEmitter.addHandler(testTreeHandler);
4250
+ if (this.#resolvedConfig.failFast) {
4251
+ const cancellationHandler = new CancellationHandler(cancellationToken, CancellationReason.FailFast);
4252
+ this.#eventEmitter.addHandler(cancellationHandler);
4253
+ }
4254
+ }
4350
4255
  async #addReporters() {
4351
4256
  if (this.#resolvedConfig.watch && !environmentOptions.noInteractive) {
4352
4257
  const watchReporter = new WatchReporter(this.#resolvedConfig);
@@ -4374,13 +4279,8 @@ class Runner {
4374
4279
  }
4375
4280
  async run(testFiles, cancellationToken = new CancellationToken()) {
4376
4281
  const tasks = testFiles.map((testFile) => (testFile instanceof Task ? testFile : new Task(testFile)));
4377
- const resultHandler = new ResultHandler();
4378
- this.#eventEmitter.addHandler(resultHandler);
4282
+ this.#addHandlers(cancellationToken);
4379
4283
  await this.#addReporters();
4380
- if (this.#resolvedConfig.failFast) {
4381
- const cancellationHandler = new CancellationHandler(cancellationToken, CancellationReason.FailFast);
4382
- this.#eventEmitter.addHandler(cancellationHandler);
4383
- }
4384
4284
  await this.#run(tasks, cancellationToken);
4385
4285
  if (this.#resolvedConfig.watch) {
4386
4286
  await this.#watch(tasks, cancellationToken);
@@ -4483,9 +4383,9 @@ class Cli {
4483
4383
  OutputService.writeMessage(formattedText({ ...resolvedConfig, ...environmentOptions }));
4484
4384
  continue;
4485
4385
  }
4486
- if (commandLine.includes("--install")) {
4386
+ if (commandLine.includes("--fetch")) {
4487
4387
  for (const tag of resolvedConfig.target) {
4488
- await Store.install(tag);
4388
+ await Store.fetch(tag);
4489
4389
  }
4490
4390
  continue;
4491
4391
  }
@@ -4540,4 +4440,4 @@ class Cli {
4540
4440
  }
4541
4441
  }
4542
4442
 
4543
- export { Assertion, BaseReporter, CancellationHandler, CancellationReason, CancellationToken, Cli, CollectService, Color, Config, ConfigDiagnosticText, DescribeResult, Diagnostic, DiagnosticCategory, DiagnosticOrigin, EventEmitter, ExitCodeHandler, ExpectResult, ExpectService, FileWatcher, InputService, Line, ListReporter, OptionBrand, OptionGroup, Options, OutputService, Path, PluginService, ProjectResult, ProjectService, Result, ResultCount, ResultHandler, ResultStatus, ResultTiming, Runner, Scribbler, Select, SelectDiagnosticText, SetupReporter, SourceFile, Store, SummaryReporter, TargetResult, Task, TaskResult, TestMember, TestMemberBrand, TestMemberFlags, TestResult, TestTree, Text, Version, WatchReporter, WatchService, Watcher, addsPackageText, defaultOptions, describeNameText, diagnosticText, environmentOptions, fileViewText, formattedText, helpText, summaryText, taskStatusText, testNameText, usesCompilerText, waitingForFileChangesText, watchUsageText };
4443
+ export { AssertionNode, BaseReporter, CancellationHandler, CancellationReason, CancellationToken, Cli, CollectService, Color, Config, ConfigDiagnosticText, DescribeResult, Diagnostic, DiagnosticCategory, DiagnosticOrigin, EventEmitter, ExitCodeHandler, ExpectResult, ExpectService, FileWatcher, InputService, Line, ListReporter, OptionBrand, OptionGroup, Options, OutputService, Path, PluginService, ProjectResult, ProjectService, Result, ResultCount, ResultHandler, ResultStatus, ResultTiming, Runner, Scribbler, Select, SelectDiagnosticText, SetupReporter, SourceFile, Store, SummaryReporter, TargetResult, Task, TaskResult, TestResult, TestTree, TestTreeHandler, TestTreeNode, TestTreeNodeBrand, TestTreeNodeFlags, Text, Version, WatchReporter, WatchService, Watcher, addsPackageText, defaultOptions, describeNameText, diagnosticText, environmentOptions, fileViewText, formattedText, helpText, summaryText, taskStatusText, testNameText, usesCompilerText, waitingForFileChangesText, watchUsageText };