tstyche 4.0.0-beta.0 → 4.0.0-beta.10

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
@@ -93,12 +93,39 @@ class DiagnosticOrigin {
93
93
  this.assertion = assertion;
94
94
  }
95
95
  static fromAssertion(assertion) {
96
- const node = assertion.matcherName;
96
+ const node = assertion.matcherNameNode.name;
97
97
  return new DiagnosticOrigin(node.getStart(), node.getEnd(), node.getSourceFile(), assertion);
98
98
  }
99
99
  static fromNode(node, assertion) {
100
100
  return new DiagnosticOrigin(node.getStart(), node.getEnd(), node.getSourceFile(), assertion);
101
101
  }
102
+ static fromNodes(nodes, assertion) {
103
+ return new DiagnosticOrigin(nodes.pos, nodes.end, nodes[0].getSourceFile(), assertion);
104
+ }
105
+ }
106
+
107
+ function diagnosticBelongsToNode(diagnostic, node) {
108
+ return diagnostic.start != null && diagnostic.start >= node.pos && diagnostic.start <= node.end;
109
+ }
110
+ function diagnosticMessageChainToText(chain) {
111
+ const result = [chain.messageText];
112
+ if (chain.next != null) {
113
+ for (const nextChain of chain.next) {
114
+ result.push(...diagnosticMessageChainToText(nextChain));
115
+ }
116
+ }
117
+ return result;
118
+ }
119
+ function getDiagnosticMessageText(diagnostic) {
120
+ return typeof diagnostic.messageText === "string"
121
+ ? diagnostic.messageText
122
+ : diagnosticMessageChainToText(diagnostic.messageText);
123
+ }
124
+ function getTextSpanEnd(span) {
125
+ return span.start + span.length;
126
+ }
127
+ function isDiagnosticWithLocation(diagnostic) {
128
+ return diagnostic.file != null && diagnostic.start != null && diagnostic.length != null;
102
129
  }
103
130
 
104
131
  class Diagnostic {
@@ -138,21 +165,10 @@ class Diagnostic {
138
165
  if (diagnostic.relatedInformation != null) {
139
166
  related = Diagnostic.fromDiagnostics(diagnostic.relatedInformation);
140
167
  }
141
- const text = typeof diagnostic.messageText === "string"
142
- ? diagnostic.messageText
143
- : Diagnostic.toMessageText(diagnostic.messageText);
168
+ const text = getDiagnosticMessageText(diagnostic);
144
169
  return new Diagnostic(text, DiagnosticCategory.Error, origin).add({ code, related });
145
170
  });
146
171
  }
147
- static toMessageText(chain) {
148
- const result = [chain.messageText];
149
- if (chain.next != null) {
150
- for (const nextChain of chain.next) {
151
- result.push(...Diagnostic.toMessageText(nextChain));
152
- }
153
- }
154
- return result;
155
- }
156
172
  static warning(text, origin) {
157
173
  return new Diagnostic(text, DiagnosticCategory.Warning, origin);
158
174
  }
@@ -556,7 +572,6 @@ class ManifestService {
556
572
  #manifestFilePath;
557
573
  #npmRegistry;
558
574
  #storePath;
559
- #supportedVersionRegex = /^(5)\.\d\.\d$/;
560
575
  constructor(storePath, npmRegistry, fetcher) {
561
576
  this.#storePath = storePath;
562
577
  this.#npmRegistry = npmRegistry;
@@ -586,7 +601,7 @@ class ManifestService {
586
601
  const versions = [];
587
602
  const packageMetadata = (await response.json());
588
603
  for (const [tag, meta] of Object.entries(packageMetadata.versions)) {
589
- if (this.#supportedVersionRegex.test(tag)) {
604
+ if (!tag.includes("-") && Version.isSatisfiedWith(tag, "4.7.2")) {
590
605
  versions.push(tag);
591
606
  packages[tag] = { integrity: meta.dist.integrity, tarball: meta.dist.tarball };
592
607
  }
@@ -685,6 +700,9 @@ class PackageService {
685
700
  if (response?.body != null) {
686
701
  const targetPath = `${packagePath}-${Math.random().toString(32).slice(2)}`;
687
702
  for await (const file of TarReader.extract(response.body)) {
703
+ if (!file.name.startsWith("package/")) {
704
+ continue;
705
+ }
688
706
  const filePath = Path.join(targetPath, file.name.replace("package/", ""));
689
707
  const directoryPath = Path.dirname(filePath);
690
708
  if (!existsSync(directoryPath)) {
@@ -792,11 +810,7 @@ class Store {
792
810
  modulePath = Path.resolve(modulePath, "../tsserverlibrary.js");
793
811
  }
794
812
  const sourceText = await fs.readFile(modulePath, { encoding: "utf8" });
795
- const toExpose = [
796
- "isApplicableIndexType",
797
- "isTypeRelatedTo",
798
- "relation: { assignable: assignableRelation, identity: identityRelation }",
799
- ];
813
+ const toExpose = ["isApplicableIndexType", "isTypeIdenticalTo"];
800
814
  const modifiedSourceText = sourceText.replace("return checker;", `return { ...checker, ${toExpose.join(", ")} };`);
801
815
  const compiledWrapper = vm.compileFunction(modifiedSourceText, ["exports", "require", "module", "__filename", "__dirname"], { filename: modulePath });
802
816
  compiledWrapper(exports, createRequire(modulePath), module, modulePath, Path.dirname(modulePath));
@@ -903,7 +917,7 @@ class Options {
903
917
  },
904
918
  {
905
919
  brand: OptionBrand.BareTrue,
906
- description: "Fetch specified versions of the 'typescript' package and exit.",
920
+ description: "Fetch the specified versions of the 'typescript' package and exit.",
907
921
  group: OptionGroup.CommandLine,
908
922
  name: "fetch",
909
923
  },
@@ -1656,14 +1670,12 @@ class ResultCount {
1656
1670
  class Result {
1657
1671
  expectCount = new ResultCount();
1658
1672
  fileCount = new ResultCount();
1659
- resolvedConfig;
1660
1673
  results = [];
1661
1674
  targetCount = new ResultCount();
1662
1675
  tasks;
1663
1676
  testCount = new ResultCount();
1664
1677
  timing = new ResultTiming();
1665
- constructor(resolvedConfig, tasks) {
1666
- this.resolvedConfig = resolvedConfig;
1678
+ constructor(tasks) {
1667
1679
  this.tasks = tasks;
1668
1680
  }
1669
1681
  }
@@ -1765,6 +1777,7 @@ class ResultHandler {
1765
1777
  this.#taskResult.timing.start = Date.now();
1766
1778
  break;
1767
1779
  case "task:error":
1780
+ case "collect:error":
1768
1781
  this.#targetResult.status = ResultStatus.Failed;
1769
1782
  this.#taskResult.status = ResultStatus.Failed;
1770
1783
  this.#taskResult.diagnostics.push(...payload.diagnostics);
@@ -1899,293 +1912,6 @@ class ResultHandler {
1899
1912
  }
1900
1913
  }
1901
1914
 
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
-
2189
1915
  function jsx(type, props) {
2190
1916
  return { props, type };
2191
1917
  }
@@ -2500,39 +2226,12 @@ function CountText({ failed, passed, skipped, todo, total }) {
2500
2226
  function DurationText({ seconds }) {
2501
2227
  return jsx(Text, { children: `${Math.round(seconds * 10) / 10}s` });
2502
2228
  }
2503
- function MatchText({ text }) {
2504
- if (typeof text === "string") {
2505
- return jsx(Text, { children: ["'", text, "'"] });
2506
- }
2507
- if (text.length === 1) {
2508
- return jsx(Text, { children: ["'", ...text, "'"] });
2509
- }
2510
- const lastItem = text.pop();
2511
- return (jsx(Text, { children: [text.map((match, index, list) => (jsx(Text, { children: ["'", match, "'", index === list.length - 1 ? jsx(Text, { children: " " }) : jsx(Text, { color: Color.Gray, children: ", " })] }))), jsx(Text, { color: Color.Gray, children: "or" }), " '", lastItem, "'"] }));
2512
- }
2513
- function RanFilesText({ onlyMatch, pathMatch, skipMatch }) {
2514
- const testNameMatchText = [];
2515
- if (onlyMatch != null) {
2516
- testNameMatchText.push(jsx(Text, { children: [jsx(Text, { color: Color.Gray, children: "matching " }), jsx(MatchText, { text: onlyMatch })] }));
2517
- }
2518
- if (skipMatch != null) {
2519
- testNameMatchText.push(jsx(Text, { children: [onlyMatch && jsx(Text, { color: Color.Gray, children: " and " }), jsx(Text, { color: Color.Gray, children: "not matching " }), jsx(MatchText, { text: skipMatch })] }));
2520
- }
2521
- let pathMatchText;
2522
- if (pathMatch.length > 0) {
2523
- pathMatchText = (jsx(Text, { children: [jsx(Text, { color: Color.Gray, children: "test files matching " }), jsx(MatchText, { text: pathMatch }), jsx(Text, { color: Color.Gray, children: "." })] }));
2524
- }
2525
- else {
2526
- pathMatchText = jsx(Text, { color: Color.Gray, children: "all test files." });
2527
- }
2528
- return (jsx(Line, { children: [jsx(Text, { color: Color.Gray, children: "Ran " }), testNameMatchText.length > 0 ? jsx(Text, { color: Color.Gray, children: "tests " }) : undefined, testNameMatchText, testNameMatchText.length > 0 ? jsx(Text, { color: Color.Gray, children: " in " }) : undefined, pathMatchText] }));
2529
- }
2530
- function summaryText({ duration, expectCount, fileCount, onlyMatch, pathMatch, skipMatch, targetCount, testCount, }) {
2229
+ function summaryText({ duration, expectCount, fileCount, targetCount, testCount, }) {
2531
2230
  const targetCountText = (jsx(RowText, { label: "Targets", text: jsx(CountText, { failed: targetCount.failed, passed: targetCount.passed, skipped: targetCount.skipped, todo: targetCount.todo, total: targetCount.total }) }));
2532
2231
  const fileCountText = (jsx(RowText, { label: "Test files", text: jsx(CountText, { failed: fileCount.failed, passed: fileCount.passed, skipped: fileCount.skipped, todo: fileCount.todo, total: fileCount.total }) }));
2533
2232
  const testCountText = (jsx(RowText, { label: "Tests", text: jsx(CountText, { failed: testCount.failed, passed: testCount.passed, skipped: testCount.skipped, todo: testCount.todo, total: testCount.total }) }));
2534
2233
  const assertionCountText = (jsx(RowText, { label: "Assertions", text: jsx(CountText, { failed: expectCount.failed, passed: expectCount.passed, skipped: expectCount.skipped, todo: expectCount.todo, total: expectCount.total }) }));
2535
- return (jsx(Text, { children: [targetCountText, fileCountText, testCount.total > 0 ? testCountText : undefined, expectCount.total > 0 ? assertionCountText : undefined, jsx(RowText, { label: "Duration", text: jsx(DurationText, { seconds: duration / 1000 }) }), jsx(Line, {}), jsx(RanFilesText, { onlyMatch: onlyMatch, pathMatch: pathMatch, skipMatch: skipMatch })] }));
2234
+ return (jsx(Text, { children: [targetCountText, fileCountText, testCount.total > 0 ? testCountText : undefined, expectCount.total > 0 ? assertionCountText : undefined, jsx(RowText, { label: "Duration", text: jsx(DurationText, { seconds: duration / 1000 }) })] }));
2536
2235
  }
2537
2236
 
2538
2237
  function StatusText({ status }) {
@@ -2681,6 +2380,7 @@ class ListReporter extends BaseReporter {
2681
2380
  this.#hasReportedError = false;
2682
2381
  break;
2683
2382
  case "task:error":
2383
+ case "collect:error":
2684
2384
  for (const diagnostic of payload.diagnostics) {
2685
2385
  this.#fileView.addMessage(diagnosticText(diagnostic));
2686
2386
  }
@@ -2776,9 +2476,6 @@ class SummaryReporter extends BaseReporter {
2776
2476
  duration: payload.result.timing.duration,
2777
2477
  expectCount: payload.result.expectCount,
2778
2478
  fileCount: payload.result.fileCount,
2779
- onlyMatch: payload.result.resolvedConfig.only,
2780
- pathMatch: payload.result.resolvedConfig.pathMatch,
2781
- skipMatch: payload.result.resolvedConfig.skip,
2782
2479
  targetCount: payload.result.targetCount,
2783
2480
  testCount: payload.result.testCount,
2784
2481
  }));
@@ -3173,20 +2870,512 @@ class WatchService {
3173
2870
  }
3174
2871
  }
3175
2872
 
3176
- class ProjectService {
3177
- #compiler;
3178
- #lastSeenProject = "";
3179
- #resolvedConfig;
3180
- #seenPrograms = new WeakSet();
3181
- #service;
3182
- constructor(resolvedConfig, compiler) {
3183
- this.#resolvedConfig = resolvedConfig;
3184
- this.#compiler = compiler;
3185
- const noop = () => undefined;
3186
- const noopLogger = {
3187
- close: noop,
3188
- endGroup: noop,
3189
- getLogFileName: noop,
2873
+ class TestTreeNode {
2874
+ brand;
2875
+ children = [];
2876
+ diagnostics = new Set();
2877
+ flags;
2878
+ name = "";
2879
+ node;
2880
+ parent;
2881
+ constructor(compiler, brand, node, parent, flags) {
2882
+ this.brand = brand;
2883
+ this.node = node;
2884
+ this.parent = parent;
2885
+ this.flags = flags;
2886
+ if (node.arguments[0] != null && compiler.isStringLiteralLike(node.arguments[0])) {
2887
+ this.name = node.arguments[0].text;
2888
+ }
2889
+ if (node.arguments[1] != null && compiler.isFunctionLike(node.arguments[1])) {
2890
+ for (const diagnostic of parent.diagnostics) {
2891
+ if (diagnosticBelongsToNode(diagnostic, node.arguments[1].body)) {
2892
+ this.diagnostics.add(diagnostic);
2893
+ parent.diagnostics.delete(diagnostic);
2894
+ }
2895
+ }
2896
+ }
2897
+ }
2898
+ }
2899
+
2900
+ class AssertionNode extends TestTreeNode {
2901
+ abilityDiagnostics;
2902
+ isNot;
2903
+ matcherNode;
2904
+ matcherNameNode;
2905
+ modifierNode;
2906
+ notNode;
2907
+ source;
2908
+ target;
2909
+ constructor(compiler, brand, node, parent, flags, matcherNode, matcherNameNode, modifierNode, notNode) {
2910
+ super(compiler, brand, node, parent, flags);
2911
+ this.isNot = notNode != null;
2912
+ this.matcherNode = matcherNode;
2913
+ this.matcherNameNode = matcherNameNode;
2914
+ this.modifierNode = modifierNode;
2915
+ this.source = this.node.typeArguments ?? this.node.arguments;
2916
+ if (compiler.isCallExpression(this.matcherNode)) {
2917
+ this.target = this.matcherNode.typeArguments ?? this.matcherNode.arguments;
2918
+ }
2919
+ for (const diagnostic of parent.diagnostics) {
2920
+ if (diagnosticBelongsToNode(diagnostic, this.source)) {
2921
+ this.diagnostics.add(diagnostic);
2922
+ parent.diagnostics.delete(diagnostic);
2923
+ }
2924
+ }
2925
+ }
2926
+ }
2927
+
2928
+ function nodeBelongsToArgumentList(compiler, node) {
2929
+ return compiler.isCallExpression(node.parent) && node.parent.arguments.some((argument) => argument === node);
2930
+ }
2931
+ function nodeIsChildOfExpressionStatement(compiler, node) {
2932
+ return compiler.isExpressionStatement(node.parent);
2933
+ }
2934
+
2935
+ class AbilityLayer {
2936
+ #compiler;
2937
+ #filePath = "";
2938
+ #nodes = [];
2939
+ #projectService;
2940
+ #resolvedConfig;
2941
+ #text = "";
2942
+ constructor(compiler, projectService, resolvedConfig) {
2943
+ this.#compiler = compiler;
2944
+ this.#projectService = projectService;
2945
+ this.#resolvedConfig = resolvedConfig;
2946
+ }
2947
+ #getErasedRangeText(range) {
2948
+ if (this.#text.indexOf("\n", range.start) >= range.end) {
2949
+ return " ".repeat(range.end - range.start);
2950
+ }
2951
+ const text = [];
2952
+ for (let index = range.start; index < range.end; index++) {
2953
+ const character = this.#text.charAt(index);
2954
+ switch (character) {
2955
+ case "\n":
2956
+ case "\r":
2957
+ text.push(character);
2958
+ break;
2959
+ default:
2960
+ text.push(" ");
2961
+ }
2962
+ }
2963
+ return text.join("");
2964
+ }
2965
+ #addRanges(node, ranges) {
2966
+ this.#nodes.push(node);
2967
+ for (const range of ranges) {
2968
+ const rangeText = range.replacement != null
2969
+ ? `${range.replacement}${this.#getErasedRangeText(range).slice(range.replacement.length)}`
2970
+ : this.#getErasedRangeText(range);
2971
+ this.#text = `${this.#text.slice(0, range.start)}${rangeText}${this.#text.slice(range.end)}`;
2972
+ }
2973
+ }
2974
+ close() {
2975
+ if (this.#nodes.length > 0) {
2976
+ this.#projectService.openFile(this.#filePath, this.#text, this.#resolvedConfig.rootPath);
2977
+ const languageService = this.#projectService.getLanguageService(this.#filePath);
2978
+ const diagnostics = new Set(languageService?.getSemanticDiagnostics(this.#filePath));
2979
+ for (const node of this.#nodes.reverse()) {
2980
+ for (const diagnostic of diagnostics) {
2981
+ if (diagnosticBelongsToNode(diagnostic, "matcherNode" in node ? node.matcherNode : node.actionNode)) {
2982
+ if (!node.abilityDiagnostics) {
2983
+ node.abilityDiagnostics = new Set();
2984
+ }
2985
+ node.abilityDiagnostics.add(diagnostic);
2986
+ diagnostics.delete(diagnostic);
2987
+ }
2988
+ }
2989
+ }
2990
+ }
2991
+ this.#filePath = "";
2992
+ this.#nodes = [];
2993
+ this.#text = "";
2994
+ }
2995
+ #eraseTrailingComma(node, parent) {
2996
+ if (node.hasTrailingComma) {
2997
+ this.#addRanges(parent, [{ start: node.end - 1, end: node.end }]);
2998
+ }
2999
+ }
3000
+ handleWhen(whenNode) {
3001
+ const whenStart = whenNode.node.getStart();
3002
+ const whenExpressionEnd = whenNode.node.expression.getEnd();
3003
+ const whenEnd = whenNode.node.getEnd();
3004
+ const actionNameEnd = whenNode.actionNameNode.getEnd();
3005
+ switch (whenNode.actionNameNode.name.text) {
3006
+ case "isCalledWith":
3007
+ this.#eraseTrailingComma(whenNode.target, whenNode);
3008
+ this.#addRanges(whenNode, [
3009
+ {
3010
+ start: whenStart,
3011
+ end: whenExpressionEnd,
3012
+ replacement: nodeIsChildOfExpressionStatement(this.#compiler, whenNode.actionNode) ? ";" : "",
3013
+ },
3014
+ { start: whenEnd, end: actionNameEnd },
3015
+ ]);
3016
+ break;
3017
+ }
3018
+ }
3019
+ handleAssertion(assertionNode) {
3020
+ const expectStart = assertionNode.node.getStart();
3021
+ const expectExpressionEnd = assertionNode.node.expression.getEnd();
3022
+ const expectEnd = assertionNode.node.getEnd();
3023
+ const matcherNameEnd = assertionNode.matcherNameNode.getEnd();
3024
+ switch (assertionNode.matcherNameNode.name.text) {
3025
+ case "toBeApplicable":
3026
+ this.#addRanges(assertionNode, [
3027
+ { start: expectStart, end: expectExpressionEnd },
3028
+ { start: expectEnd, end: matcherNameEnd },
3029
+ ]);
3030
+ break;
3031
+ case "toBeCallableWith":
3032
+ this.#eraseTrailingComma(assertionNode.source, assertionNode);
3033
+ this.#addRanges(assertionNode, [
3034
+ {
3035
+ start: expectStart,
3036
+ end: expectExpressionEnd,
3037
+ replacement: nodeIsChildOfExpressionStatement(this.#compiler, assertionNode.matcherNode) ? ";" : "",
3038
+ },
3039
+ { start: expectEnd, end: matcherNameEnd },
3040
+ ]);
3041
+ break;
3042
+ case "toBeConstructableWith":
3043
+ this.#eraseTrailingComma(assertionNode.source, assertionNode);
3044
+ this.#addRanges(assertionNode, [
3045
+ {
3046
+ start: expectStart,
3047
+ end: expectExpressionEnd,
3048
+ replacement: nodeIsChildOfExpressionStatement(this.#compiler, assertionNode.matcherNode) ? "; new" : "new",
3049
+ },
3050
+ { start: expectEnd, end: matcherNameEnd },
3051
+ ]);
3052
+ break;
3053
+ }
3054
+ }
3055
+ open(sourceFile) {
3056
+ this.#filePath = sourceFile.fileName;
3057
+ this.#text = sourceFile.text;
3058
+ }
3059
+ }
3060
+
3061
+ class CollectDiagnosticText {
3062
+ static cannotBeNestedWithin(source, target) {
3063
+ return `'${source}()' cannot be nested within '${target}()'.`;
3064
+ }
3065
+ }
3066
+
3067
+ var TestTreeNodeBrand;
3068
+ (function (TestTreeNodeBrand) {
3069
+ TestTreeNodeBrand["Describe"] = "describe";
3070
+ TestTreeNodeBrand["Test"] = "test";
3071
+ TestTreeNodeBrand["Expect"] = "expect";
3072
+ TestTreeNodeBrand["When"] = "when";
3073
+ })(TestTreeNodeBrand || (TestTreeNodeBrand = {}));
3074
+
3075
+ var TestTreeNodeFlags;
3076
+ (function (TestTreeNodeFlags) {
3077
+ TestTreeNodeFlags[TestTreeNodeFlags["None"] = 0] = "None";
3078
+ TestTreeNodeFlags[TestTreeNodeFlags["Fail"] = 1] = "Fail";
3079
+ TestTreeNodeFlags[TestTreeNodeFlags["Only"] = 2] = "Only";
3080
+ TestTreeNodeFlags[TestTreeNodeFlags["Skip"] = 4] = "Skip";
3081
+ TestTreeNodeFlags[TestTreeNodeFlags["Todo"] = 8] = "Todo";
3082
+ })(TestTreeNodeFlags || (TestTreeNodeFlags = {}));
3083
+
3084
+ class IdentifierLookup {
3085
+ #compiler;
3086
+ #identifiers;
3087
+ #moduleSpecifiers = ['"tstyche"', "'tstyche'"];
3088
+ constructor(compiler) {
3089
+ this.#compiler = compiler;
3090
+ }
3091
+ handleImportDeclaration(node) {
3092
+ if (this.#moduleSpecifiers.includes(node.moduleSpecifier.getText()) &&
3093
+ node.importClause?.isTypeOnly !== true &&
3094
+ node.importClause?.namedBindings != null) {
3095
+ if (this.#compiler.isNamedImports(node.importClause.namedBindings)) {
3096
+ for (const element of node.importClause.namedBindings.elements) {
3097
+ if (element.isTypeOnly) {
3098
+ continue;
3099
+ }
3100
+ let identifierKey;
3101
+ if (element.propertyName) {
3102
+ identifierKey = element.propertyName.getText();
3103
+ }
3104
+ else {
3105
+ identifierKey = element.name.getText();
3106
+ }
3107
+ if (identifierKey in this.#identifiers.namedImports) {
3108
+ this.#identifiers.namedImports[identifierKey] = element.name.getText();
3109
+ }
3110
+ }
3111
+ }
3112
+ if (this.#compiler.isNamespaceImport(node.importClause.namedBindings)) {
3113
+ this.#identifiers.namespace = node.importClause.namedBindings.name.getText();
3114
+ }
3115
+ }
3116
+ }
3117
+ open() {
3118
+ this.#identifiers = {
3119
+ namedImports: {
3120
+ describe: undefined,
3121
+ expect: undefined,
3122
+ it: undefined,
3123
+ namespace: undefined,
3124
+ test: undefined,
3125
+ when: undefined,
3126
+ },
3127
+ namespace: undefined,
3128
+ };
3129
+ }
3130
+ resolveTestTreeNodeMeta(node) {
3131
+ let flags = TestTreeNodeFlags.None;
3132
+ let expression = node.expression;
3133
+ while (this.#compiler.isPropertyAccessExpression(expression)) {
3134
+ if (expression.expression.getText() === this.#identifiers.namespace) {
3135
+ break;
3136
+ }
3137
+ switch (expression.name.getText()) {
3138
+ case "fail":
3139
+ flags |= TestTreeNodeFlags.Fail;
3140
+ break;
3141
+ case "only":
3142
+ flags |= TestTreeNodeFlags.Only;
3143
+ break;
3144
+ case "skip":
3145
+ flags |= TestTreeNodeFlags.Skip;
3146
+ break;
3147
+ case "todo":
3148
+ flags |= TestTreeNodeFlags.Todo;
3149
+ break;
3150
+ }
3151
+ expression = expression.expression;
3152
+ }
3153
+ let identifier;
3154
+ if (this.#compiler.isPropertyAccessExpression(expression) &&
3155
+ expression.expression.getText() === this.#identifiers.namespace) {
3156
+ identifier = expression.name.getText();
3157
+ }
3158
+ else {
3159
+ identifier = Object.keys(this.#identifiers.namedImports).find((key) => this.#identifiers.namedImports[key] === expression.getText());
3160
+ }
3161
+ if (!identifier) {
3162
+ return;
3163
+ }
3164
+ switch (identifier) {
3165
+ case "describe":
3166
+ return { brand: TestTreeNodeBrand.Describe, flags, identifier };
3167
+ case "it":
3168
+ case "test":
3169
+ return { brand: TestTreeNodeBrand.Test, flags, identifier };
3170
+ case "expect":
3171
+ return { brand: TestTreeNodeBrand.Expect, flags, identifier };
3172
+ case "when":
3173
+ return { brand: TestTreeNodeBrand.When, flags, identifier };
3174
+ }
3175
+ return;
3176
+ }
3177
+ }
3178
+
3179
+ class TestTree {
3180
+ children = [];
3181
+ diagnostics;
3182
+ hasOnly = false;
3183
+ sourceFile;
3184
+ constructor(diagnostics, sourceFile) {
3185
+ this.diagnostics = diagnostics;
3186
+ this.sourceFile = sourceFile;
3187
+ }
3188
+ }
3189
+
3190
+ class WhenNode extends TestTreeNode {
3191
+ actionNode;
3192
+ actionNameNode;
3193
+ abilityDiagnostics;
3194
+ target;
3195
+ constructor(compiler, brand, node, parent, flags, actionNode, actionNameNode) {
3196
+ super(compiler, brand, node, parent, flags);
3197
+ this.actionNode = actionNode;
3198
+ this.actionNameNode = actionNameNode;
3199
+ this.target = this.node.typeArguments ?? this.node.arguments;
3200
+ for (const diagnostic of parent.diagnostics) {
3201
+ if (diagnosticBelongsToNode(diagnostic, node)) {
3202
+ this.diagnostics.add(diagnostic);
3203
+ parent.diagnostics.delete(diagnostic);
3204
+ }
3205
+ }
3206
+ }
3207
+ }
3208
+
3209
+ class CollectService {
3210
+ #abilityLayer;
3211
+ #compiler;
3212
+ #identifierLookup;
3213
+ constructor(compiler, projectService, resolvedConfig) {
3214
+ this.#compiler = compiler;
3215
+ this.#abilityLayer = new AbilityLayer(compiler, projectService, resolvedConfig);
3216
+ this.#identifierLookup = new IdentifierLookup(compiler);
3217
+ }
3218
+ #collectTestTreeNodes(node, parent, testTree) {
3219
+ if (this.#compiler.isCallExpression(node)) {
3220
+ const meta = this.#identifierLookup.resolveTestTreeNodeMeta(node);
3221
+ if (meta != null) {
3222
+ if (!this.#checkNode(node, meta, parent)) {
3223
+ return;
3224
+ }
3225
+ if (meta.brand === TestTreeNodeBrand.Describe || meta.brand === TestTreeNodeBrand.Test) {
3226
+ const testTreeNode = new TestTreeNode(this.#compiler, meta.brand, node, parent, meta.flags);
3227
+ this.#compiler.forEachChild(node, (node) => {
3228
+ this.#collectTestTreeNodes(node, testTreeNode, testTree);
3229
+ });
3230
+ this.#onNode(testTreeNode, parent, testTree);
3231
+ return;
3232
+ }
3233
+ if (meta.brand === TestTreeNodeBrand.Expect) {
3234
+ const modifierNode = this.#getChainedNode(node, "type");
3235
+ if (!modifierNode) {
3236
+ return;
3237
+ }
3238
+ const notNode = this.#getChainedNode(modifierNode, "not");
3239
+ const matcherNameNode = this.#getChainedNode(notNode ?? modifierNode);
3240
+ if (!matcherNameNode) {
3241
+ return;
3242
+ }
3243
+ const matcherNode = this.#getMatcherNode(matcherNameNode);
3244
+ if (!matcherNode) {
3245
+ return;
3246
+ }
3247
+ const assertionNode = new AssertionNode(this.#compiler, meta.brand, node, parent, meta.flags, matcherNode, matcherNameNode, modifierNode, notNode);
3248
+ this.#abilityLayer.handleAssertion(assertionNode);
3249
+ this.#compiler.forEachChild(node, (node) => {
3250
+ this.#collectTestTreeNodes(node, assertionNode, testTree);
3251
+ });
3252
+ this.#onNode(assertionNode, parent, testTree);
3253
+ return;
3254
+ }
3255
+ if (meta.brand === TestTreeNodeBrand.When) {
3256
+ const actionNameNode = this.#getChainedNode(node);
3257
+ if (!actionNameNode) {
3258
+ return;
3259
+ }
3260
+ const actionNode = this.#getActionNode(actionNameNode);
3261
+ if (!actionNode) {
3262
+ return;
3263
+ }
3264
+ this.#compiler.forEachChild(actionNode, (node) => {
3265
+ if (this.#compiler.isCallExpression(node)) {
3266
+ const meta = this.#identifierLookup.resolveTestTreeNodeMeta(node);
3267
+ if (meta?.brand === TestTreeNodeBrand.Describe || meta?.brand === TestTreeNodeBrand.Test) {
3268
+ const text = CollectDiagnosticText.cannotBeNestedWithin(meta.identifier, "when");
3269
+ const origin = DiagnosticOrigin.fromNode(node);
3270
+ this.#onDiagnostics(Diagnostic.error(text, origin));
3271
+ }
3272
+ }
3273
+ });
3274
+ const whenNode = new WhenNode(this.#compiler, meta.brand, node, parent, meta.flags, actionNode, actionNameNode);
3275
+ this.#abilityLayer.handleWhen(whenNode);
3276
+ this.#onNode(whenNode, parent, testTree);
3277
+ return;
3278
+ }
3279
+ }
3280
+ }
3281
+ if (this.#compiler.isImportDeclaration(node)) {
3282
+ this.#identifierLookup.handleImportDeclaration(node);
3283
+ return;
3284
+ }
3285
+ this.#compiler.forEachChild(node, (node) => {
3286
+ this.#collectTestTreeNodes(node, parent, testTree);
3287
+ });
3288
+ }
3289
+ createTestTree(sourceFile, semanticDiagnostics = []) {
3290
+ const testTree = new TestTree(new Set(semanticDiagnostics), sourceFile);
3291
+ EventEmitter.dispatch(["collect:start", { tree: testTree }]);
3292
+ this.#abilityLayer.open(sourceFile);
3293
+ this.#identifierLookup.open();
3294
+ this.#collectTestTreeNodes(sourceFile, testTree, testTree);
3295
+ this.#abilityLayer.close();
3296
+ EventEmitter.dispatch(["collect:end", { tree: testTree }]);
3297
+ return testTree;
3298
+ }
3299
+ #checkNode(node, meta, parent) {
3300
+ if ("brand" in parent && !this.#isNodeAllowed(meta, parent)) {
3301
+ const text = CollectDiagnosticText.cannotBeNestedWithin(meta.identifier, parent.node.expression.getText());
3302
+ const origin = DiagnosticOrigin.fromNode(node);
3303
+ this.#onDiagnostics(Diagnostic.error(text, origin));
3304
+ return false;
3305
+ }
3306
+ return true;
3307
+ }
3308
+ #isNodeAllowed(meta, parent) {
3309
+ switch (meta.brand) {
3310
+ case TestTreeNodeBrand.Describe:
3311
+ case TestTreeNodeBrand.Test:
3312
+ if (parent.brand === TestTreeNodeBrand.Test || parent.brand === TestTreeNodeBrand.Expect) {
3313
+ return false;
3314
+ }
3315
+ break;
3316
+ case TestTreeNodeBrand.Expect:
3317
+ case TestTreeNodeBrand.When:
3318
+ if (parent.brand === TestTreeNodeBrand.Describe) {
3319
+ return false;
3320
+ }
3321
+ break;
3322
+ }
3323
+ return true;
3324
+ }
3325
+ #getChainedNode({ parent }, name) {
3326
+ if (!this.#compiler.isPropertyAccessExpression(parent)) {
3327
+ return;
3328
+ }
3329
+ if (name != null && name !== parent.name.getText()) {
3330
+ return;
3331
+ }
3332
+ return parent;
3333
+ }
3334
+ #getMatcherNode(node) {
3335
+ if (this.#compiler.isCallExpression(node.parent)) {
3336
+ return node.parent;
3337
+ }
3338
+ if (this.#compiler.isDecorator(node.parent)) {
3339
+ return node.parent;
3340
+ }
3341
+ if (this.#compiler.isParenthesizedExpression(node.parent)) {
3342
+ return this.#getMatcherNode(node.parent);
3343
+ }
3344
+ return;
3345
+ }
3346
+ #getActionNode(node) {
3347
+ if (this.#compiler.isCallExpression(node.parent)) {
3348
+ return node.parent;
3349
+ }
3350
+ return;
3351
+ }
3352
+ #onDiagnostics(diagnostic) {
3353
+ EventEmitter.dispatch(["collect:error", { diagnostics: [diagnostic] }]);
3354
+ }
3355
+ #onNode(node, parent, testTree) {
3356
+ parent.children.push(node);
3357
+ if (node.flags & TestTreeNodeFlags.Only) {
3358
+ testTree.hasOnly = true;
3359
+ }
3360
+ EventEmitter.dispatch(["collect:node", { node }]);
3361
+ }
3362
+ }
3363
+
3364
+ class ProjectService {
3365
+ #compiler;
3366
+ #lastSeenProject = "";
3367
+ #resolvedConfig;
3368
+ #seenPrograms = new WeakSet();
3369
+ #seenTestFiles = new Set();
3370
+ #service;
3371
+ constructor(compiler, resolvedConfig) {
3372
+ this.#compiler = compiler;
3373
+ this.#resolvedConfig = resolvedConfig;
3374
+ const noop = () => undefined;
3375
+ const noopLogger = {
3376
+ close: noop,
3377
+ endGroup: noop,
3378
+ getLogFileName: noop,
3190
3379
  hasLevel: () => false,
3191
3380
  info: noop,
3192
3381
  loggingEnabled: () => false,
@@ -3215,15 +3404,6 @@ class ProjectService {
3215
3404
  useInferredProjectPerProjectRoot: true,
3216
3405
  useSingleInferredProject: false,
3217
3406
  });
3218
- switch (this.#resolvedConfig.tsconfig) {
3219
- case "findup":
3220
- break;
3221
- case "ignore":
3222
- this.#service.getConfigFileNameForFile = () => undefined;
3223
- break;
3224
- default:
3225
- this.#service.getConfigFileNameForFile = () => this.#resolvedConfig.tsconfig;
3226
- }
3227
3407
  this.#service.setCompilerOptionsForInferredProjects(this.#getDefaultCompilerOptions());
3228
3408
  }
3229
3409
  closeFile(filePath) {
@@ -3231,7 +3411,6 @@ class ProjectService {
3231
3411
  }
3232
3412
  #getDefaultCompilerOptions() {
3233
3413
  const defaultCompilerOptions = {
3234
- allowImportingTsExtensions: true,
3235
3414
  allowJs: true,
3236
3415
  checkJs: true,
3237
3416
  exactOptionalPropertyTypes: true,
@@ -3242,8 +3421,11 @@ class ProjectService {
3242
3421
  resolveJsonModule: true,
3243
3422
  strict: true,
3244
3423
  target: this.#compiler.ScriptTarget.ESNext,
3245
- verbatimModuleSyntax: true,
3246
3424
  };
3425
+ if (Version.isSatisfiedWith(this.#compiler.version, "5.0")) {
3426
+ defaultCompilerOptions.allowImportingTsExtensions = true;
3427
+ defaultCompilerOptions.verbatimModuleSyntax = true;
3428
+ }
3247
3429
  return defaultCompilerOptions;
3248
3430
  }
3249
3431
  getDefaultProject(filePath) {
@@ -3258,7 +3440,23 @@ class ProjectService {
3258
3440
  const project = this.getDefaultProject(filePath);
3259
3441
  return project?.getLanguageService(true);
3260
3442
  }
3443
+ #isFileIncluded(filePath) {
3444
+ const configSourceFile = this.#compiler.readJsonConfigFile(this.#resolvedConfig.tsconfig, this.#compiler.sys.readFile);
3445
+ const { fileNames } = this.#compiler.parseJsonSourceFileConfigFileContent(configSourceFile, this.#compiler.sys, Path.dirname(this.#resolvedConfig.tsconfig), undefined, this.#resolvedConfig.tsconfig);
3446
+ return fileNames.includes(filePath);
3447
+ }
3261
3448
  openFile(filePath, sourceText, projectRootPath) {
3449
+ switch (this.#resolvedConfig.tsconfig) {
3450
+ case "findup":
3451
+ break;
3452
+ case "ignore":
3453
+ this.#service.getConfigFileNameForFile = () => undefined;
3454
+ break;
3455
+ default:
3456
+ this.#service.getConfigFileNameForFile = this.#isFileIncluded(filePath)
3457
+ ? () => this.#resolvedConfig.tsconfig
3458
+ : () => undefined;
3459
+ }
3262
3460
  const { configFileErrors, configFileName } = this.#service.openClientFile(filePath, sourceText, undefined, projectRootPath);
3263
3461
  if (configFileName !== this.#lastSeenProject) {
3264
3462
  this.#lastSeenProject = configFileName;
@@ -3273,29 +3471,29 @@ class ProjectService {
3273
3471
  { diagnostics: Diagnostic.fromDiagnostics(configFileErrors) },
3274
3472
  ]);
3275
3473
  }
3276
- if (this.#resolvedConfig.checkSourceFiles) {
3474
+ if (this.#resolvedConfig.checkSourceFiles && !this.#seenTestFiles.has(filePath)) {
3475
+ this.#seenTestFiles.add(filePath);
3277
3476
  const languageService = this.getLanguageService(filePath);
3278
3477
  const program = languageService?.getProgram();
3279
3478
  if (!program || this.#seenPrograms.has(program)) {
3280
3479
  return;
3281
3480
  }
3282
3481
  this.#seenPrograms.add(program);
3283
- const filesToCheck = [];
3284
- for (const sourceFile of program.getSourceFiles()) {
3482
+ const sourceFilesToCheck = program.getSourceFiles().filter((sourceFile) => {
3285
3483
  if (program.isSourceFileFromExternalLibrary(sourceFile) || program.isSourceFileDefaultLibrary(sourceFile)) {
3286
- continue;
3484
+ return false;
3287
3485
  }
3288
- if (!Select.isTestFile(sourceFile.fileName, { ...this.#resolvedConfig, pathMatch: [] })) {
3289
- filesToCheck.push(sourceFile);
3486
+ if (Select.isTestFile(sourceFile.fileName, { ...this.#resolvedConfig, pathMatch: [] })) {
3487
+ return false;
3290
3488
  }
3291
- }
3489
+ return true;
3490
+ });
3292
3491
  const diagnostics = [];
3293
- for (const sourceFile of filesToCheck) {
3492
+ for (const sourceFile of sourceFilesToCheck) {
3294
3493
  diagnostics.push(...program.getSyntacticDiagnostics(sourceFile), ...program.getSemanticDiagnostics(sourceFile));
3295
3494
  }
3296
3495
  if (diagnostics.length > 0) {
3297
3496
  EventEmitter.dispatch(["project:error", { diagnostics: Diagnostic.fromDiagnostics(diagnostics) }]);
3298
- return;
3299
3497
  }
3300
3498
  }
3301
3499
  }
@@ -3310,30 +3508,74 @@ var RunMode;
3310
3508
  RunMode[RunMode["Todo"] = 8] = "Todo";
3311
3509
  })(RunMode || (RunMode = {}));
3312
3510
 
3313
- class Format {
3314
- static capitalize(text) {
3315
- return text.replace(/^./, text.charAt(0).toUpperCase());
3511
+ class EnsureDiagnosticText {
3512
+ static argumentMustBeProvided(argumentNameText) {
3513
+ return `An argument for '${argumentNameText}' must be provided.`;
3514
+ }
3515
+ static argumentOrTypeArgumentMustBeProvided(argumentNameText, typeArgumentNameText) {
3516
+ return `An argument for '${argumentNameText}' or type argument for '${typeArgumentNameText}' must be provided.`;
3517
+ }
3518
+ }
3519
+
3520
+ function argumentIsProvided(argumentNameText, node, enclosingNode, onDiagnostics) {
3521
+ if (!node) {
3522
+ const text = EnsureDiagnosticText.argumentMustBeProvided(argumentNameText);
3523
+ const origin = DiagnosticOrigin.fromNode(enclosingNode);
3524
+ onDiagnostics([Diagnostic.error(text, origin)]);
3525
+ return false;
3526
+ }
3527
+ return true;
3528
+ }
3529
+
3530
+ function argumentOrTypeArgumentIsProvided(argumentNameText, typeArgumentNameText, node, enclosingNode, onDiagnostics) {
3531
+ if (!node) {
3532
+ const text = EnsureDiagnosticText.argumentOrTypeArgumentMustBeProvided(argumentNameText, typeArgumentNameText);
3533
+ const origin = DiagnosticOrigin.fromNode(enclosingNode);
3534
+ onDiagnostics([Diagnostic.error(text, origin)]);
3535
+ return false;
3536
+ }
3537
+ return true;
3538
+ }
3539
+
3540
+ class ExpectDiagnosticText {
3541
+ static argumentMustBe(argumentNameText, expectedText) {
3542
+ return `An argument for '${argumentNameText}' must be ${expectedText}.`;
3543
+ }
3544
+ static typeArgumentMustBe(argumentNameText, expectedText) {
3545
+ return `A type argument for '${argumentNameText}' must be ${expectedText}.`;
3546
+ }
3547
+ static isCallable(isExpression, targetText) {
3548
+ return `${isExpression ? "Expression" : "Type"} is callable ${targetText}.`;
3549
+ }
3550
+ static isNotCallable(isExpression, targetText) {
3551
+ return `${isExpression ? "Expression" : "Type"} is not callable ${targetText}.`;
3552
+ }
3553
+ static isConstructable(isExpression, targetText) {
3554
+ return `${isExpression ? "Expression" : "Type"} is constructable ${targetText}.`;
3555
+ }
3556
+ static isNotConstructable(isExpression, targetText) {
3557
+ return `${isExpression ? "Expression" : "Type"} is not constructable ${targetText}.`;
3316
3558
  }
3317
- }
3318
-
3319
- class ExpectDiagnosticText {
3320
- static argumentCannotBeOfType(argumentNameText, typeText) {
3321
- return `An argument for '${argumentNameText}' cannot be of the '${typeText}' type.`;
3559
+ static acceptsProps(isExpression) {
3560
+ return `${isExpression ? "Component" : "Component type"} accepts props of the given type.`;
3322
3561
  }
3323
- static argumentOrTypeArgumentMustBeProvided(argumentNameText, typeArgumentNameText) {
3324
- return `An argument for '${argumentNameText}' or type argument for '${typeArgumentNameText}' must be provided.`;
3562
+ static doesNotAcceptProps(isExpression) {
3563
+ return `${isExpression ? "Component" : "Component type"} does not accept props of the given type.`;
3325
3564
  }
3326
- static argumentMustBe(argumentNameText, expectedText) {
3327
- return `An argument for '${argumentNameText}' must be ${expectedText}.`;
3565
+ static canBeApplied(targetText) {
3566
+ return `The decorator function can be applied${targetText}.`;
3328
3567
  }
3329
- static argumentMustBeProvided(argumentNameText) {
3330
- return `An argument for '${argumentNameText}' must be provided.`;
3568
+ static cannotBeApplied(targetText) {
3569
+ return `The decorator function cannot be applied${targetText}.`;
3570
+ }
3571
+ static hasProperty(typeText, propertyNameText) {
3572
+ return `Type '${typeText}' has property '${propertyNameText}'.`;
3331
3573
  }
3332
- static componentAcceptsProps(isTypeNode) {
3333
- return `${isTypeNode ? "Component type" : "Component"} accepts props of the given type.`;
3574
+ static doesNotHaveProperty(typeText, propertyNameText) {
3575
+ return `Type '${typeText}' does not have property '${propertyNameText}'.`;
3334
3576
  }
3335
- static componentDoesNotAcceptProps(isTypeNode) {
3336
- return `${isTypeNode ? "Component type" : "Component"} does not accept props of the given type.`;
3577
+ static didYouMeanToUse(suggestionText) {
3578
+ return `Did you mean to use ${suggestionText}?`;
3337
3579
  }
3338
3580
  static matcherIsNotSupported(matcherNameText) {
3339
3581
  return `The '.${matcherNameText}()' matcher is not supported.`;
@@ -3344,87 +3586,73 @@ class ExpectDiagnosticText {
3344
3586
  static raisedTypeError(count = 1) {
3345
3587
  return `The raised type error${count === 1 ? "" : "s"}:`;
3346
3588
  }
3347
- static typeArgumentCannotBeOfType(argumentNameText, typeText) {
3348
- return `A type argument for '${argumentNameText}' cannot be of the '${typeText}' type.`;
3349
- }
3350
- static typeArgumentMustBe(argumentNameText, expectedText) {
3351
- return `A type argument for '${argumentNameText}' must be ${expectedText}.`;
3352
- }
3353
- static typeDidNotRaiseError(isTypeNode) {
3354
- return `${isTypeNode ? "Type" : "Expression type"} did not raise a type error.`;
3589
+ static raisedError(isExpression, count, targetCount) {
3590
+ let countText = "a";
3591
+ if (count > 1 || targetCount > 1) {
3592
+ countText = count > targetCount ? `${count}` : `only ${count}`;
3593
+ }
3594
+ return `${isExpression ? "Expression" : "Type"} raised ${countText} type error${count === 1 ? "" : "s"}.`;
3355
3595
  }
3356
- static typeDidNotRaiseMatchingError(isTypeNode) {
3357
- return `${isTypeNode ? "Type" : "Expression type"} did not raise a matching type error.`;
3596
+ static didNotRaiseError(isExpression) {
3597
+ return `${isExpression ? "Expression" : "Type"} did not raise a type error.`;
3358
3598
  }
3359
- static typeDoesNotHaveProperty(typeText, propertyNameText) {
3360
- return `Type '${typeText}' does not have property '${propertyNameText}'.`;
3599
+ static raisedMatchingError(isExpression) {
3600
+ return `${isExpression ? "Expression" : "Type"} raised a matching type error.`;
3361
3601
  }
3362
- static typeHasProperty(typeText, propertyNameText) {
3363
- return `Type '${typeText}' has property '${propertyNameText}'.`;
3602
+ static didNotRaiseMatchingError(isExpression) {
3603
+ return `${isExpression ? "Expression" : "Type"} did not raise a matching type error.`;
3364
3604
  }
3365
- static typeIsAssignableTo(sourceTypeText, targetTypeText) {
3605
+ static isAssignableTo(sourceTypeText, targetTypeText) {
3366
3606
  return `Type '${sourceTypeText}' is assignable to type '${targetTypeText}'.`;
3367
3607
  }
3368
- static typeIsAssignableWith(sourceTypeText, targetTypeText) {
3369
- return `Type '${sourceTypeText}' is assignable with type '${targetTypeText}'.`;
3370
- }
3371
- static typeIsIdenticalTo(sourceTypeText, targetTypeText) {
3372
- return `Type '${sourceTypeText}' is identical to type '${targetTypeText}'.`;
3373
- }
3374
- static typeIsNotAssignableTo(sourceTypeText, targetTypeText) {
3608
+ static isNotAssignableTo(sourceTypeText, targetTypeText) {
3375
3609
  return `Type '${sourceTypeText}' is not assignable to type '${targetTypeText}'.`;
3376
3610
  }
3377
- static typeIsNotAssignableWith(sourceTypeText, targetTypeText) {
3378
- return `Type '${sourceTypeText}' is not assignable with type '${targetTypeText}'.`;
3611
+ static isAssignableWith(sourceTypeText, targetTypeText) {
3612
+ return `Type '${sourceTypeText}' is assignable with type '${targetTypeText}'.`;
3379
3613
  }
3380
- static typeIsNotCompatibleWith(sourceTypeText, targetTypeText) {
3381
- return `Type '${sourceTypeText}' is not compatible with type '${targetTypeText}'.`;
3614
+ static isNotAssignableWith(sourceTypeText, targetTypeText) {
3615
+ return `Type '${sourceTypeText}' is not assignable with type '${targetTypeText}'.`;
3382
3616
  }
3383
- static typeIsNotIdenticalTo(sourceTypeText, targetTypeText) {
3384
- return `Type '${sourceTypeText}' is not identical to type '${targetTypeText}'.`;
3617
+ static isTheSame(sourceTypeText, targetTypeText) {
3618
+ return `Type '${sourceTypeText}' is the same as type '${targetTypeText}'.`;
3385
3619
  }
3386
- static typeRaisedError(isTypeNode, count, targetCount) {
3387
- let countText = "a";
3388
- if (count > 1 || targetCount > 1) {
3389
- countText = count > targetCount ? `${count}` : `only ${count}`;
3390
- }
3391
- return `${isTypeNode ? "Type" : "Expression type"} raised ${countText} type error${count === 1 ? "" : "s"}.`;
3620
+ static isNotTheSame(sourceTypeText, targetTypeText) {
3621
+ return `Type '${sourceTypeText}' is not the same as type '${targetTypeText}'.`;
3392
3622
  }
3393
- static typeRaisedMatchingError(isTypeNode) {
3394
- return `${isTypeNode ? "Type" : "Expression type"} raised a matching type error.`;
3623
+ static isNotCompatibleWith(sourceTypeText, targetTypeText) {
3624
+ return `Type '${sourceTypeText}' is not compatible with type '${targetTypeText}'.`;
3395
3625
  }
3396
- static typeRequiresProperty(typeText, propertyNameText) {
3626
+ static requiresProperty(typeText, propertyNameText) {
3397
3627
  return `Type '${typeText}' requires property '${propertyNameText}'.`;
3398
3628
  }
3399
3629
  static typesOfPropertyAreNotCompatible(propertyNameText) {
3400
3630
  return `Types of property '${propertyNameText}' are not compatible.`;
3401
3631
  }
3402
- static typeWasRejected(typeText) {
3403
- const optionNameText = `reject${Format.capitalize(typeText)}Type`;
3404
- return [
3405
- `The '${typeText}' type was rejected because the '${optionNameText}' option is enabled.`,
3406
- `If this check is necessary, pass '${typeText}' as the type argument explicitly.`,
3407
- ];
3408
- }
3409
3632
  }
3410
3633
 
3634
+ var Relation;
3635
+ (function (Relation) {
3636
+ Relation["Assignable"] = "assignable";
3637
+ Relation["Identical"] = "identical";
3638
+ })(Relation || (Relation = {}));
3639
+
3411
3640
  class MatchWorker {
3412
3641
  assertion;
3413
3642
  #compiler;
3414
3643
  #signatureCache = new Map();
3415
- #typeCache = new Map();
3416
- #typeChecker;
3644
+ typeChecker;
3417
3645
  constructor(compiler, typeChecker, assertion) {
3418
3646
  this.#compiler = compiler;
3419
- this.#typeChecker = typeChecker;
3647
+ this.typeChecker = typeChecker;
3420
3648
  this.assertion = assertion;
3421
3649
  }
3422
3650
  checkHasApplicableIndexType(sourceNode, targetNode) {
3423
3651
  const sourceType = this.getType(sourceNode);
3424
3652
  const targetType = this.getType(targetNode);
3425
- return this.#typeChecker
3653
+ return this.typeChecker
3426
3654
  .getIndexInfosOfType(sourceType)
3427
- .some(({ keyType }) => this.#typeChecker.isApplicableIndexType(targetType, keyType));
3655
+ .some(({ keyType }) => this.typeChecker.isApplicableIndexType(targetType, keyType));
3428
3656
  }
3429
3657
  checkHasProperty(sourceNode, propertyNameText) {
3430
3658
  const sourceType = this.getType(sourceNode);
@@ -3433,38 +3661,36 @@ class MatchWorker {
3433
3661
  .some((property) => this.#compiler.unescapeLeadingUnderscores(property.escapedName) === propertyNameText);
3434
3662
  }
3435
3663
  checkIsAssignableTo(sourceNode, targetNode) {
3436
- const relation = this.#typeChecker.relation.assignable;
3437
- return this.#checkIsRelatedTo(sourceNode, targetNode, relation);
3664
+ return this.#checkIsRelatedTo(sourceNode, targetNode, Relation.Assignable);
3438
3665
  }
3439
3666
  checkIsAssignableWith(sourceNode, targetNode) {
3440
- const relation = this.#typeChecker.relation.assignable;
3441
- return this.#checkIsRelatedTo(targetNode, sourceNode, relation);
3667
+ return this.#checkIsRelatedTo(targetNode, sourceNode, Relation.Assignable);
3442
3668
  }
3443
3669
  checkIsIdenticalTo(sourceNode, targetNode) {
3444
- const relation = this.#typeChecker.relation.identity;
3445
- return (this.#checkIsRelatedTo(sourceNode, targetNode, relation) &&
3670
+ return (this.#checkIsRelatedTo(sourceNode, targetNode, Relation.Identical) &&
3446
3671
  this.checkIsAssignableTo(sourceNode, targetNode) &&
3447
3672
  this.checkIsAssignableWith(sourceNode, targetNode));
3448
3673
  }
3449
3674
  #checkIsRelatedTo(sourceNode, targetNode, relation) {
3450
- const sourceType = relation === this.#typeChecker.relation.identity
3451
- ? this.#simplifyType(this.getType(sourceNode))
3452
- : this.getType(sourceNode);
3453
- const targetType = relation === this.#typeChecker.relation.identity
3454
- ? this.#simplifyType(this.getType(targetNode))
3455
- : this.getType(targetNode);
3456
- return this.#typeChecker.isTypeRelatedTo(sourceType, targetType, relation);
3675
+ const sourceType = relation === "identical" ? this.#simplifyType(this.getType(sourceNode)) : this.getType(sourceNode);
3676
+ const targetType = relation === "identical" ? this.#simplifyType(this.getType(targetNode)) : this.getType(targetNode);
3677
+ switch (relation) {
3678
+ case Relation.Assignable:
3679
+ return this.typeChecker.isTypeAssignableTo(sourceType, targetType);
3680
+ case Relation.Identical:
3681
+ return this.typeChecker.isTypeIdenticalTo(sourceType, targetType);
3682
+ }
3457
3683
  }
3458
3684
  extendsObjectType(type) {
3459
3685
  const nonPrimitiveType = { flags: this.#compiler.TypeFlags.NonPrimitive };
3460
- return this.#typeChecker.isTypeAssignableTo(type, nonPrimitiveType);
3686
+ return this.typeChecker.isTypeAssignableTo(type, nonPrimitiveType);
3461
3687
  }
3462
3688
  getParameterType(signature, index) {
3463
3689
  const parameter = signature.getDeclaration().parameters[index];
3464
3690
  if (!parameter) {
3465
3691
  return;
3466
3692
  }
3467
- return this.#getTypeOfNode(parameter);
3693
+ return this.getType(parameter);
3468
3694
  }
3469
3695
  getSignatures(node) {
3470
3696
  let signatures = this.#signatureCache.get(node);
@@ -3478,37 +3704,10 @@ class MatchWorker {
3478
3704
  return signatures;
3479
3705
  }
3480
3706
  getTypeText(node) {
3481
- const type = this.getType(node);
3482
- return this.#typeChecker.typeToString(type);
3707
+ return this.typeChecker.typeToString(this.getType(node));
3483
3708
  }
3484
3709
  getType(node) {
3485
- return this.#compiler.isTypeNode(node) ? this.#getTypeOfTypeNode(node) : this.#getTypeOfNode(node);
3486
- }
3487
- #getTypeOfNode(node) {
3488
- let type = this.#typeCache.get(node);
3489
- if (!type) {
3490
- type = this.#typeChecker.getTypeAtLocation(node);
3491
- }
3492
- return type;
3493
- }
3494
- #getTypeOfTypeNode(node) {
3495
- let type = this.#typeCache.get(node);
3496
- if (!type) {
3497
- type = this.#typeChecker.getTypeFromTypeNode(node);
3498
- }
3499
- return type;
3500
- }
3501
- isStringOrNumberLiteralType(type) {
3502
- return !!(type.flags & this.#compiler.TypeFlags.StringOrNumberLiteral);
3503
- }
3504
- isObjectType(type) {
3505
- return !!(type.flags & this.#compiler.TypeFlags.Object);
3506
- }
3507
- isUnionType(type) {
3508
- return !!(type.flags & this.#compiler.TypeFlags.Union);
3509
- }
3510
- isUniqueSymbolType(type) {
3511
- return !!(type.flags & this.#compiler.TypeFlags.UniqueESSymbol);
3710
+ return this.typeChecker.getTypeAtLocation(node);
3512
3711
  }
3513
3712
  resolveDiagnosticOrigin(symbol, enclosingNode) {
3514
3713
  if (symbol.valueDeclaration != null &&
@@ -3524,7 +3723,7 @@ class MatchWorker {
3524
3723
  #simplifyType(type) {
3525
3724
  if (type.isUnionOrIntersection()) {
3526
3725
  const candidateType = this.#simplifyType(type.types[0]);
3527
- if (type.types.every((type) => this.#typeChecker.isTypeRelatedTo(this.#simplifyType(type), candidateType, this.#typeChecker.relation.identity))) {
3726
+ if (type.types.every((type) => this.typeChecker.isTypeIdenticalTo(this.#simplifyType(type), candidateType))) {
3528
3727
  return candidateType;
3529
3728
  }
3530
3729
  }
@@ -3532,6 +3731,16 @@ class MatchWorker {
3532
3731
  }
3533
3732
  }
3534
3733
 
3734
+ function isStringOrNumberLiteralType(compiler, type) {
3735
+ return !!(type.flags & compiler.TypeFlags.StringOrNumberLiteral);
3736
+ }
3737
+ function isUnionType(compiler, type) {
3738
+ return !!(type.flags & compiler.TypeFlags.Union);
3739
+ }
3740
+ function isUniqueSymbolType(compiler, type) {
3741
+ return !!(type.flags & compiler.TypeFlags.UniqueESSymbol);
3742
+ }
3743
+
3535
3744
  class ToAcceptProps {
3536
3745
  #compiler;
3537
3746
  #typeChecker;
@@ -3540,12 +3749,13 @@ class ToAcceptProps {
3540
3749
  this.#typeChecker = typeChecker;
3541
3750
  }
3542
3751
  #explain(matchWorker, sourceNode, targetNode) {
3752
+ const isExpression = nodeBelongsToArgumentList(this.#compiler, sourceNode);
3543
3753
  const signatures = matchWorker.getSignatures(sourceNode);
3544
3754
  return signatures.reduce((accumulator, signature, index) => {
3545
3755
  let diagnostic;
3546
3756
  const introText = matchWorker.assertion.isNot
3547
- ? ExpectDiagnosticText.componentAcceptsProps(this.#compiler.isTypeNode(sourceNode))
3548
- : ExpectDiagnosticText.componentDoesNotAcceptProps(this.#compiler.isTypeNode(sourceNode));
3757
+ ? ExpectDiagnosticText.acceptsProps(isExpression)
3758
+ : ExpectDiagnosticText.doesNotAcceptProps(isExpression);
3549
3759
  const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.assertion);
3550
3760
  if (signatures.length > 1) {
3551
3761
  const signatureText = this.#typeChecker.signatureToString(signature, sourceNode);
@@ -3565,7 +3775,7 @@ class ToAcceptProps {
3565
3775
  #isOptionalProperty(symbol) {
3566
3776
  return symbol.declarations?.every((declaration) => this.#compiler.isPropertySignature(declaration) && declaration.questionToken != null);
3567
3777
  }
3568
- #checkProperties(matchWorker, sourceType, targetType) {
3778
+ #checkProperties(sourceType, targetType) {
3569
3779
  const check = (sourceType, targetType) => {
3570
3780
  for (const targetProperty of targetType.getProperties()) {
3571
3781
  const targetPropertyName = targetProperty.getName();
@@ -3593,7 +3803,7 @@ class ToAcceptProps {
3593
3803
  }
3594
3804
  return true;
3595
3805
  };
3596
- if (sourceType != null && matchWorker.isUnionType(sourceType)) {
3806
+ if (sourceType != null && isUnionType(this.#compiler, sourceType)) {
3597
3807
  return sourceType.types.some((sourceType) => check(sourceType, targetType));
3598
3808
  }
3599
3809
  return check(sourceType, targetType);
@@ -3611,8 +3821,8 @@ class ToAcceptProps {
3611
3821
  const sourceProperty = sourceType?.getProperty(targetPropertyName);
3612
3822
  if (!sourceProperty) {
3613
3823
  const text = [
3614
- ExpectDiagnosticText.typeIsNotCompatibleWith(sourceTypeText, targetTypeText),
3615
- ExpectDiagnosticText.typeDoesNotHaveProperty(sourceTypeText, targetPropertyName),
3824
+ ExpectDiagnosticText.isNotCompatibleWith(sourceTypeText, targetTypeText),
3825
+ ExpectDiagnosticText.doesNotHaveProperty(sourceTypeText, targetPropertyName),
3616
3826
  ];
3617
3827
  const origin = matchWorker.resolveDiagnosticOrigin(targetProperty, targetNode);
3618
3828
  diagnostics.push(diagnostic.extendWith(text, origin));
@@ -3620,8 +3830,8 @@ class ToAcceptProps {
3620
3830
  }
3621
3831
  if (this.#isOptionalProperty(targetProperty) && !this.#isOptionalProperty(sourceProperty)) {
3622
3832
  const text = [
3623
- ExpectDiagnosticText.typeIsNotAssignableWith(sourceTypeText, targetTypeText),
3624
- ExpectDiagnosticText.typeRequiresProperty(sourceTypeText, targetPropertyName),
3833
+ ExpectDiagnosticText.isNotAssignableWith(sourceTypeText, targetTypeText),
3834
+ ExpectDiagnosticText.requiresProperty(sourceTypeText, targetPropertyName),
3625
3835
  ];
3626
3836
  const origin = matchWorker.resolveDiagnosticOrigin(targetProperty, targetNode);
3627
3837
  diagnostics.push(diagnostic.extendWith(text, origin));
@@ -3633,9 +3843,9 @@ class ToAcceptProps {
3633
3843
  const targetPropertyTypeText = this.#typeChecker.typeToString(targetPropertyType);
3634
3844
  const sourcePropertyTypeText = this.#typeChecker.typeToString(sourcePropertyType);
3635
3845
  const text = [
3636
- ExpectDiagnosticText.typeIsNotAssignableWith(sourceTypeText, targetTypeText),
3846
+ ExpectDiagnosticText.isNotAssignableWith(sourceTypeText, targetTypeText),
3637
3847
  ExpectDiagnosticText.typesOfPropertyAreNotCompatible(targetPropertyName),
3638
- ExpectDiagnosticText.typeIsNotAssignableWith(sourcePropertyTypeText, targetPropertyTypeText),
3848
+ ExpectDiagnosticText.isNotAssignableWith(sourcePropertyTypeText, targetPropertyTypeText),
3639
3849
  ];
3640
3850
  const origin = matchWorker.resolveDiagnosticOrigin(targetProperty, targetNode);
3641
3851
  diagnostics.push(diagnostic.extendWith(text, origin));
@@ -3647,26 +3857,26 @@ class ToAcceptProps {
3647
3857
  const targetProperty = targetType.getProperty(sourcePropertyName);
3648
3858
  if (!targetProperty && !this.#isOptionalProperty(sourceProperty)) {
3649
3859
  const text = [
3650
- ExpectDiagnosticText.typeIsNotAssignableWith(sourceTypeText, targetTypeText),
3651
- ExpectDiagnosticText.typeRequiresProperty(sourceTypeText, sourcePropertyName),
3860
+ ExpectDiagnosticText.isNotAssignableWith(sourceTypeText, targetTypeText),
3861
+ ExpectDiagnosticText.requiresProperty(sourceTypeText, sourcePropertyName),
3652
3862
  ];
3653
3863
  diagnostics.push(diagnostic.extendWith(text));
3654
3864
  }
3655
3865
  }
3656
3866
  }
3657
3867
  if (diagnostics.length === 0) {
3658
- const text = ExpectDiagnosticText.typeIsAssignableWith(sourceTypeText, targetTypeText);
3868
+ const text = ExpectDiagnosticText.isAssignableWith(sourceTypeText, targetTypeText);
3659
3869
  diagnostics.push(diagnostic.extendWith(text));
3660
3870
  return { diagnostics, isMatch: true };
3661
3871
  }
3662
3872
  return { diagnostics, isMatch: false };
3663
3873
  };
3664
- if (sourceType != null && matchWorker.isUnionType(sourceType)) {
3874
+ if (sourceType != null && isUnionType(this.#compiler, sourceType)) {
3665
3875
  let accumulator = [];
3666
3876
  const isMatch = sourceType.types.some((sourceType) => {
3667
3877
  const text = matchWorker.assertion.isNot
3668
- ? ExpectDiagnosticText.typeIsAssignableWith(sourceTypeText, targetTypeText)
3669
- : ExpectDiagnosticText.typeIsNotAssignableWith(sourceTypeText, targetTypeText);
3878
+ ? ExpectDiagnosticText.isAssignableWith(sourceTypeText, targetTypeText)
3879
+ : ExpectDiagnosticText.isNotAssignableWith(sourceTypeText, targetTypeText);
3670
3880
  const { diagnostics, isMatch } = explain(sourceType, targetType, diagnostic.extendWith(text));
3671
3881
  if (isMatch) {
3672
3882
  accumulator = diagnostics;
@@ -3685,18 +3895,18 @@ class ToAcceptProps {
3685
3895
  const signatures = matchWorker.getSignatures(sourceNode);
3686
3896
  if (signatures.length === 0) {
3687
3897
  const expectedText = "of a function or class type";
3688
- const text = this.#compiler.isTypeNode(sourceNode)
3689
- ? ExpectDiagnosticText.typeArgumentMustBe("Source", expectedText)
3690
- : ExpectDiagnosticText.argumentMustBe("source", expectedText);
3898
+ const text = nodeBelongsToArgumentList(this.#compiler, sourceNode)
3899
+ ? ExpectDiagnosticText.argumentMustBe("source", expectedText)
3900
+ : ExpectDiagnosticText.typeArgumentMustBe("Source", expectedText);
3691
3901
  const origin = DiagnosticOrigin.fromNode(sourceNode);
3692
3902
  diagnostics.push(Diagnostic.error(text, origin));
3693
3903
  }
3694
3904
  const targetType = matchWorker.getType(targetNode);
3695
- if (!matchWorker.isObjectType(targetType)) {
3905
+ if (!(targetType.flags & this.#compiler.TypeFlags.Object)) {
3696
3906
  const expectedText = "of an object type";
3697
- const text = this.#compiler.isTypeNode(targetNode)
3698
- ? ExpectDiagnosticText.typeArgumentMustBe("Target", expectedText)
3699
- : ExpectDiagnosticText.argumentMustBe("target", expectedText);
3907
+ const text = nodeBelongsToArgumentList(this.#compiler, targetNode)
3908
+ ? ExpectDiagnosticText.argumentMustBe("target", expectedText)
3909
+ : ExpectDiagnosticText.typeArgumentMustBe("Target", expectedText);
3700
3910
  const origin = DiagnosticOrigin.fromNode(targetNode);
3701
3911
  diagnostics.push(Diagnostic.error(text, origin));
3702
3912
  }
@@ -3706,7 +3916,7 @@ class ToAcceptProps {
3706
3916
  }
3707
3917
  const isMatch = signatures.some((signature) => {
3708
3918
  const sourceType = matchWorker.getParameterType(signature, 0);
3709
- return this.#checkProperties(matchWorker, sourceType, targetType);
3919
+ return this.#checkProperties(sourceType, targetType);
3710
3920
  });
3711
3921
  return {
3712
3922
  explain: () => this.#explain(matchWorker, sourceNode, targetNode),
@@ -3728,8 +3938,8 @@ class RelationMatcherBase {
3728
3938
  }
3729
3939
 
3730
3940
  class ToBe extends RelationMatcherBase {
3731
- explainText = ExpectDiagnosticText.typeIsIdenticalTo;
3732
- explainNotText = ExpectDiagnosticText.typeIsNotIdenticalTo;
3941
+ explainText = ExpectDiagnosticText.isTheSame;
3942
+ explainNotText = ExpectDiagnosticText.isNotTheSame;
3733
3943
  match(matchWorker, sourceNode, targetNode) {
3734
3944
  return {
3735
3945
  explain: () => this.explain(matchWorker, sourceNode, targetNode),
@@ -3738,9 +3948,74 @@ class ToBe extends RelationMatcherBase {
3738
3948
  }
3739
3949
  }
3740
3950
 
3951
+ class ToBeApplicable {
3952
+ #compiler;
3953
+ constructor(compiler) {
3954
+ this.#compiler = compiler;
3955
+ }
3956
+ #resolveTargetText(node) {
3957
+ let text = "";
3958
+ switch (node.kind) {
3959
+ case this.#compiler.SyntaxKind.ClassDeclaration:
3960
+ text = "class";
3961
+ break;
3962
+ case this.#compiler.SyntaxKind.MethodDeclaration:
3963
+ text = "method";
3964
+ break;
3965
+ case this.#compiler.SyntaxKind.PropertyDeclaration:
3966
+ text = node.modifiers?.some((modifier) => modifier.kind === this.#compiler.SyntaxKind.AccessorKeyword)
3967
+ ? "accessor"
3968
+ : "field";
3969
+ break;
3970
+ case this.#compiler.SyntaxKind.GetAccessor:
3971
+ text = "getter";
3972
+ break;
3973
+ case this.#compiler.SyntaxKind.SetAccessor:
3974
+ text = "setter";
3975
+ break;
3976
+ }
3977
+ if (text.length > 0) {
3978
+ text = ` to this ${text}`;
3979
+ }
3980
+ return text;
3981
+ }
3982
+ #explain(matchWorker, sourceNode) {
3983
+ const targetText = this.#resolveTargetText(matchWorker.assertion.matcherNode.parent);
3984
+ const diagnostics = [];
3985
+ if (matchWorker.assertion.abilityDiagnostics) {
3986
+ for (const diagnostic of matchWorker.assertion.abilityDiagnostics) {
3987
+ const text = [ExpectDiagnosticText.cannotBeApplied(targetText), getDiagnosticMessageText(diagnostic)];
3988
+ const origin = DiagnosticOrigin.fromNode(sourceNode);
3989
+ diagnostics.push(Diagnostic.error(text.flat(), origin));
3990
+ }
3991
+ }
3992
+ else {
3993
+ const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertion);
3994
+ diagnostics.push(Diagnostic.error(ExpectDiagnosticText.canBeApplied(targetText), origin));
3995
+ }
3996
+ return diagnostics;
3997
+ }
3998
+ match(matchWorker, sourceNode, onDiagnostics) {
3999
+ const type = matchWorker.getType(sourceNode);
4000
+ if (type.getCallSignatures().length === 0) {
4001
+ const expectedText = "of a function type";
4002
+ const text = nodeBelongsToArgumentList(this.#compiler, sourceNode)
4003
+ ? ExpectDiagnosticText.argumentMustBe("source", expectedText)
4004
+ : ExpectDiagnosticText.typeArgumentMustBe("Source", expectedText);
4005
+ const origin = DiagnosticOrigin.fromNode(sourceNode);
4006
+ onDiagnostics([Diagnostic.error(text, origin)]);
4007
+ return;
4008
+ }
4009
+ return {
4010
+ explain: () => this.#explain(matchWorker, sourceNode),
4011
+ isMatch: !matchWorker.assertion.abilityDiagnostics,
4012
+ };
4013
+ }
4014
+ }
4015
+
3741
4016
  class ToBeAssignableTo extends RelationMatcherBase {
3742
- explainText = ExpectDiagnosticText.typeIsAssignableTo;
3743
- explainNotText = ExpectDiagnosticText.typeIsNotAssignableTo;
4017
+ explainText = ExpectDiagnosticText.isAssignableTo;
4018
+ explainNotText = ExpectDiagnosticText.isNotAssignableTo;
3744
4019
  match(matchWorker, sourceNode, targetNode) {
3745
4020
  return {
3746
4021
  explain: () => this.explain(matchWorker, sourceNode, targetNode),
@@ -3750,8 +4025,8 @@ class ToBeAssignableTo extends RelationMatcherBase {
3750
4025
  }
3751
4026
 
3752
4027
  class ToBeAssignableWith extends RelationMatcherBase {
3753
- explainText = ExpectDiagnosticText.typeIsAssignableWith;
3754
- explainNotText = ExpectDiagnosticText.typeIsNotAssignableWith;
4028
+ explainText = ExpectDiagnosticText.isAssignableWith;
4029
+ explainNotText = ExpectDiagnosticText.isNotAssignableWith;
3755
4030
  match(matchWorker, sourceNode, targetNode) {
3756
4031
  return {
3757
4032
  explain: () => this.explain(matchWorker, sourceNode, targetNode),
@@ -3760,6 +4035,167 @@ class ToBeAssignableWith extends RelationMatcherBase {
3760
4035
  }
3761
4036
  }
3762
4037
 
4038
+ class ToBeCallableWith {
4039
+ #compiler;
4040
+ constructor(compiler) {
4041
+ this.#compiler = compiler;
4042
+ }
4043
+ #resolveTargetText(nodes) {
4044
+ if (nodes.length === 0) {
4045
+ return "without arguments";
4046
+ }
4047
+ if (nodes.length === 1 && nodes[0]?.kind === this.#compiler.SyntaxKind.SpreadElement) {
4048
+ return "with the given arguments";
4049
+ }
4050
+ return `with the given argument${nodes.length === 1 ? "" : "s"}`;
4051
+ }
4052
+ #explain(matchWorker, sourceNode, targetNodes) {
4053
+ const isExpression = nodeBelongsToArgumentList(this.#compiler, sourceNode);
4054
+ const targetText = this.#resolveTargetText(targetNodes);
4055
+ const diagnostics = [];
4056
+ if (matchWorker.assertion.abilityDiagnostics) {
4057
+ for (const diagnostic of matchWorker.assertion.abilityDiagnostics) {
4058
+ const text = [
4059
+ ExpectDiagnosticText.isNotCallable(isExpression, targetText),
4060
+ getDiagnosticMessageText(diagnostic),
4061
+ ];
4062
+ let origin;
4063
+ if (isDiagnosticWithLocation(diagnostic) && diagnosticBelongsToNode(diagnostic, targetNodes)) {
4064
+ origin = new DiagnosticOrigin(diagnostic.start, getTextSpanEnd(diagnostic), sourceNode.getSourceFile());
4065
+ }
4066
+ else {
4067
+ origin =
4068
+ targetNodes.length > 0
4069
+ ? DiagnosticOrigin.fromNodes(targetNodes)
4070
+ : DiagnosticOrigin.fromAssertion(matchWorker.assertion);
4071
+ }
4072
+ let related;
4073
+ if (diagnostic.relatedInformation != null) {
4074
+ related = Diagnostic.fromDiagnostics(diagnostic.relatedInformation);
4075
+ }
4076
+ diagnostics.push(Diagnostic.error(text.flat(), origin).add({ related }));
4077
+ }
4078
+ }
4079
+ else {
4080
+ const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertion);
4081
+ diagnostics.push(Diagnostic.error(ExpectDiagnosticText.isCallable(isExpression, targetText), origin));
4082
+ }
4083
+ return diagnostics;
4084
+ }
4085
+ match(matchWorker, sourceNode, targetNodes, onDiagnostics) {
4086
+ let type;
4087
+ if (this.#compiler.isCallExpression(sourceNode)) {
4088
+ type = matchWorker.typeChecker.getResolvedSignature(sourceNode)?.getReturnType();
4089
+ }
4090
+ if (this.#compiler.isArrowFunction(sourceNode) ||
4091
+ this.#compiler.isFunctionDeclaration(sourceNode) ||
4092
+ this.#compiler.isFunctionExpression(sourceNode) ||
4093
+ this.#compiler.isExpressionWithTypeArguments(sourceNode) ||
4094
+ this.#compiler.isIdentifier(sourceNode) ||
4095
+ this.#compiler.isPropertyAccessExpression(sourceNode)) {
4096
+ type = matchWorker.getType(sourceNode);
4097
+ }
4098
+ if (!type || type.getCallSignatures().length === 0) {
4099
+ const text = [];
4100
+ if (nodeBelongsToArgumentList(this.#compiler, sourceNode)) {
4101
+ text.push(ExpectDiagnosticText.argumentMustBe("source", "a callable expression"));
4102
+ }
4103
+ else {
4104
+ text.push(ExpectDiagnosticText.typeArgumentMustBe("Source", "a callable type"));
4105
+ }
4106
+ if (type != null && type.getConstructSignatures().length > 0) {
4107
+ text.push(ExpectDiagnosticText.didYouMeanToUse("the '.toBeConstructableWith()' matcher"));
4108
+ }
4109
+ const origin = DiagnosticOrigin.fromNode(sourceNode);
4110
+ onDiagnostics([Diagnostic.error(text, origin)]);
4111
+ return;
4112
+ }
4113
+ return {
4114
+ explain: () => this.#explain(matchWorker, sourceNode, targetNodes),
4115
+ isMatch: !matchWorker.assertion.abilityDiagnostics,
4116
+ };
4117
+ }
4118
+ }
4119
+
4120
+ class ToBeConstructableWith {
4121
+ #compiler;
4122
+ constructor(compiler) {
4123
+ this.#compiler = compiler;
4124
+ }
4125
+ #resolveTargetText(nodes) {
4126
+ if (nodes.length === 0) {
4127
+ return "without arguments";
4128
+ }
4129
+ if (nodes.length === 1 && nodes[0]?.kind === this.#compiler.SyntaxKind.SpreadElement) {
4130
+ return "with the given arguments";
4131
+ }
4132
+ return `with the given argument${nodes.length === 1 ? "" : "s"}`;
4133
+ }
4134
+ #explain(matchWorker, sourceNode, targetNodes) {
4135
+ const isExpression = nodeBelongsToArgumentList(this.#compiler, sourceNode);
4136
+ const targetText = this.#resolveTargetText(targetNodes);
4137
+ const diagnostics = [];
4138
+ if (matchWorker.assertion.abilityDiagnostics) {
4139
+ for (const diagnostic of matchWorker.assertion.abilityDiagnostics) {
4140
+ const text = [
4141
+ ExpectDiagnosticText.isNotConstructable(isExpression, targetText),
4142
+ getDiagnosticMessageText(diagnostic),
4143
+ ];
4144
+ let origin;
4145
+ if (isDiagnosticWithLocation(diagnostic) && diagnosticBelongsToNode(diagnostic, targetNodes)) {
4146
+ origin = new DiagnosticOrigin(diagnostic.start, getTextSpanEnd(diagnostic), sourceNode.getSourceFile());
4147
+ }
4148
+ else {
4149
+ origin =
4150
+ targetNodes.length > 0
4151
+ ? DiagnosticOrigin.fromNodes(targetNodes)
4152
+ : DiagnosticOrigin.fromAssertion(matchWorker.assertion);
4153
+ }
4154
+ let related;
4155
+ if (diagnostic.relatedInformation != null) {
4156
+ related = Diagnostic.fromDiagnostics(diagnostic.relatedInformation);
4157
+ }
4158
+ diagnostics.push(Diagnostic.error(text.flat(), origin).add({ related }));
4159
+ }
4160
+ }
4161
+ else {
4162
+ const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertion);
4163
+ diagnostics.push(Diagnostic.error(ExpectDiagnosticText.isConstructable(isExpression, targetText), origin));
4164
+ }
4165
+ return diagnostics;
4166
+ }
4167
+ match(matchWorker, sourceNode, targetNodes, onDiagnostics) {
4168
+ let type;
4169
+ if (this.#compiler.isCallExpression(sourceNode)) {
4170
+ type = matchWorker.typeChecker.getResolvedSignature(sourceNode)?.getReturnType();
4171
+ }
4172
+ if (this.#compiler.isExpressionWithTypeArguments(sourceNode) ||
4173
+ this.#compiler.isIdentifier(sourceNode) ||
4174
+ this.#compiler.isPropertyAccessExpression(sourceNode)) {
4175
+ type = matchWorker.getType(sourceNode);
4176
+ }
4177
+ if (!type || type.getConstructSignatures().length === 0) {
4178
+ const text = [];
4179
+ if (nodeBelongsToArgumentList(this.#compiler, sourceNode)) {
4180
+ text.push(ExpectDiagnosticText.argumentMustBe("source", "a constructable expression"));
4181
+ }
4182
+ else {
4183
+ text.push(ExpectDiagnosticText.typeArgumentMustBe("Source", "a constructable type"));
4184
+ }
4185
+ if (type != null && type.getCallSignatures().length > 0) {
4186
+ text.push(ExpectDiagnosticText.didYouMeanToUse("the '.toBeCallableWith()' matcher"));
4187
+ }
4188
+ const origin = DiagnosticOrigin.fromNode(sourceNode);
4189
+ onDiagnostics([Diagnostic.error(text, origin)]);
4190
+ return;
4191
+ }
4192
+ return {
4193
+ explain: () => this.#explain(matchWorker, sourceNode, targetNodes),
4194
+ isMatch: !matchWorker.assertion.abilityDiagnostics,
4195
+ };
4196
+ }
4197
+ }
4198
+
3763
4199
  class ToHaveProperty {
3764
4200
  #compiler;
3765
4201
  constructor(compiler) {
@@ -3769,7 +4205,7 @@ class ToHaveProperty {
3769
4205
  const sourceTypeText = matchWorker.getTypeText(sourceNode);
3770
4206
  const targetType = matchWorker.getType(targetNode);
3771
4207
  let propertyNameText;
3772
- if (matchWorker.isStringOrNumberLiteralType(targetType)) {
4208
+ if (isStringOrNumberLiteralType(this.#compiler, targetType)) {
3773
4209
  propertyNameText = targetType.value.toString();
3774
4210
  }
3775
4211
  else {
@@ -3777,8 +4213,8 @@ class ToHaveProperty {
3777
4213
  }
3778
4214
  const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.assertion);
3779
4215
  return matchWorker.assertion.isNot
3780
- ? [Diagnostic.error(ExpectDiagnosticText.typeHasProperty(sourceTypeText, propertyNameText), origin)]
3781
- : [Diagnostic.error(ExpectDiagnosticText.typeDoesNotHaveProperty(sourceTypeText, propertyNameText), origin)];
4216
+ ? [Diagnostic.error(ExpectDiagnosticText.hasProperty(sourceTypeText, propertyNameText), origin)]
4217
+ : [Diagnostic.error(ExpectDiagnosticText.doesNotHaveProperty(sourceTypeText, propertyNameText), origin)];
3782
4218
  }
3783
4219
  match(matchWorker, sourceNode, targetNode, onDiagnostics) {
3784
4220
  const diagnostics = [];
@@ -3786,18 +4222,18 @@ class ToHaveProperty {
3786
4222
  if (sourceType.flags & (this.#compiler.TypeFlags.Any | this.#compiler.TypeFlags.Never) ||
3787
4223
  !matchWorker.extendsObjectType(sourceType)) {
3788
4224
  const expectedText = "of an object type";
3789
- const text = this.#compiler.isTypeNode(sourceNode)
3790
- ? ExpectDiagnosticText.typeArgumentMustBe("Source", expectedText)
3791
- : ExpectDiagnosticText.argumentMustBe("source", expectedText);
4225
+ const text = nodeBelongsToArgumentList(this.#compiler, sourceNode)
4226
+ ? ExpectDiagnosticText.argumentMustBe("source", expectedText)
4227
+ : ExpectDiagnosticText.typeArgumentMustBe("Source", expectedText);
3792
4228
  const origin = DiagnosticOrigin.fromNode(sourceNode);
3793
4229
  diagnostics.push(Diagnostic.error(text, origin));
3794
4230
  }
3795
4231
  const targetType = matchWorker.getType(targetNode);
3796
4232
  let propertyNameText = "";
3797
- if (matchWorker.isStringOrNumberLiteralType(targetType)) {
4233
+ if (isStringOrNumberLiteralType(this.#compiler, targetType)) {
3798
4234
  propertyNameText = targetType.value.toString();
3799
4235
  }
3800
- else if (matchWorker.isUniqueSymbolType(targetType)) {
4236
+ else if (isUniqueSymbolType(this.#compiler, targetType)) {
3801
4237
  propertyNameText = this.#compiler.unescapeLeadingUnderscores(targetType.escapedName);
3802
4238
  }
3803
4239
  else {
@@ -3825,15 +4261,15 @@ class ToRaiseError {
3825
4261
  this.#compiler = compiler;
3826
4262
  }
3827
4263
  #explain(matchWorker, sourceNode, targetNodes) {
3828
- const isTypeNode = this.#compiler.isTypeNode(sourceNode);
4264
+ const isExpression = nodeBelongsToArgumentList(this.#compiler, sourceNode);
3829
4265
  const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertion);
3830
4266
  if (matchWorker.assertion.diagnostics.size === 0) {
3831
- const text = ExpectDiagnosticText.typeDidNotRaiseError(isTypeNode);
4267
+ const text = ExpectDiagnosticText.didNotRaiseError(isExpression);
3832
4268
  return [Diagnostic.error(text, origin)];
3833
4269
  }
3834
4270
  if (matchWorker.assertion.diagnostics.size !== targetNodes.length) {
3835
4271
  const count = matchWorker.assertion.diagnostics.size;
3836
- const text = ExpectDiagnosticText.typeRaisedError(isTypeNode, count, targetNodes.length);
4272
+ const text = ExpectDiagnosticText.raisedError(isExpression, count, targetNodes.length);
3837
4273
  const related = [
3838
4274
  Diagnostic.error(ExpectDiagnosticText.raisedTypeError(count)),
3839
4275
  ...Diagnostic.fromDiagnostics([...matchWorker.assertion.diagnostics]),
@@ -3845,8 +4281,8 @@ class ToRaiseError {
3845
4281
  const isMatch = this.#matchExpectedError(diagnostic, targetNode);
3846
4282
  if (matchWorker.assertion.isNot ? isMatch : !isMatch) {
3847
4283
  const text = matchWorker.assertion.isNot
3848
- ? ExpectDiagnosticText.typeRaisedMatchingError(isTypeNode)
3849
- : ExpectDiagnosticText.typeDidNotRaiseMatchingError(isTypeNode);
4284
+ ? ExpectDiagnosticText.raisedMatchingError(isExpression)
4285
+ : ExpectDiagnosticText.didNotRaiseMatchingError(isExpression);
3850
4286
  const origin = DiagnosticOrigin.fromNode(targetNode, matchWorker.assertion);
3851
4287
  const related = [
3852
4288
  Diagnostic.error(ExpectDiagnosticText.raisedTypeError()),
@@ -3891,9 +4327,10 @@ class ToRaiseError {
3891
4327
  if (this.#compiler.isNumericLiteral(targetNode)) {
3892
4328
  return Number.parseInt(targetNode.text, 10) === diagnostic.code;
3893
4329
  }
3894
- const messageText = typeof diagnostic.messageText === "string"
3895
- ? diagnostic.messageText
3896
- : Diagnostic.toMessageText(diagnostic.messageText).join("\n");
4330
+ let messageText = getDiagnosticMessageText(diagnostic);
4331
+ if (Array.isArray(messageText)) {
4332
+ messageText = messageText.join("\n");
4333
+ }
3897
4334
  if (this.#compiler.isRegularExpressionLiteral(targetNode)) {
3898
4335
  const targetRegex = new RegExp(...targetNode.text.slice(1).split("/"));
3899
4336
  return targetRegex.test(messageText);
@@ -3904,61 +4341,64 @@ class ToRaiseError {
3904
4341
 
3905
4342
  class ExpectService {
3906
4343
  #compiler;
3907
- #rejectTypes = new Set();
4344
+ #reject;
3908
4345
  #typeChecker;
3909
4346
  toAcceptProps;
3910
4347
  toBe;
4348
+ toBeApplicable;
3911
4349
  toBeAssignableTo;
3912
4350
  toBeAssignableWith;
4351
+ toBeCallableWith;
4352
+ toBeConstructableWith;
3913
4353
  toHaveProperty;
3914
4354
  toRaiseError;
3915
- constructor(compiler, typeChecker, resolvedConfig) {
4355
+ constructor(compiler, typeChecker, reject) {
3916
4356
  this.#compiler = compiler;
4357
+ this.#reject = reject;
3917
4358
  this.#typeChecker = typeChecker;
3918
- if (resolvedConfig?.rejectAnyType) {
3919
- this.#rejectTypes.add("any");
3920
- }
3921
- if (resolvedConfig?.rejectNeverType) {
3922
- this.#rejectTypes.add("never");
3923
- }
3924
4359
  this.toAcceptProps = new ToAcceptProps(compiler, typeChecker);
3925
4360
  this.toBe = new ToBe();
4361
+ this.toBeApplicable = new ToBeApplicable(compiler);
3926
4362
  this.toBeAssignableTo = new ToBeAssignableTo();
3927
4363
  this.toBeAssignableWith = new ToBeAssignableWith();
4364
+ this.toBeCallableWith = new ToBeCallableWith(compiler);
4365
+ this.toBeConstructableWith = new ToBeConstructableWith(compiler);
3928
4366
  this.toHaveProperty = new ToHaveProperty(compiler);
3929
4367
  this.toRaiseError = new ToRaiseError(compiler);
3930
4368
  }
3931
4369
  match(assertion, onDiagnostics) {
3932
- const matcherNameText = assertion.matcherName.getText();
3933
- if (!assertion.source[0]) {
3934
- this.#onSourceArgumentOrTypeArgumentMustBeProvided(assertion, onDiagnostics);
4370
+ const matcherNameText = assertion.matcherNameNode.name.text;
4371
+ if (!argumentOrTypeArgumentIsProvided("source", "Source", assertion.source[0], assertion.node.expression, onDiagnostics)) {
3935
4372
  return;
3936
4373
  }
3937
4374
  const matchWorker = new MatchWorker(this.#compiler, this.#typeChecker, assertion);
4375
+ if (!(matcherNameText === "toRaiseError" && assertion.isNot === false) &&
4376
+ this.#reject.argumentType([
4377
+ ["source", assertion.source[0]],
4378
+ ["target", assertion.target?.[0]],
4379
+ ], onDiagnostics)) {
4380
+ return;
4381
+ }
3938
4382
  switch (matcherNameText) {
3939
4383
  case "toAcceptProps":
3940
4384
  case "toBe":
3941
4385
  case "toBeAssignableTo":
3942
4386
  case "toBeAssignableWith":
3943
- if (!assertion.target[0]) {
3944
- this.#onTargetArgumentOrTypeArgumentMustBeProvided(assertion, onDiagnostics);
3945
- return;
3946
- }
3947
- if (this.#rejectsTypeArguments(matchWorker, onDiagnostics)) {
4387
+ if (!argumentOrTypeArgumentIsProvided("target", "Target", assertion.target?.[0], assertion.matcherNameNode.name, onDiagnostics)) {
3948
4388
  return;
3949
4389
  }
3950
4390
  return this[matcherNameText].match(matchWorker, assertion.source[0], assertion.target[0], onDiagnostics);
4391
+ case "toBeApplicable":
4392
+ return this.toBeApplicable.match(matchWorker, assertion.source[0], onDiagnostics);
4393
+ case "toBeCallableWith":
4394
+ case "toBeConstructableWith":
4395
+ case "toRaiseError":
4396
+ return this[matcherNameText].match(matchWorker, assertion.source[0], assertion.target, onDiagnostics);
3951
4397
  case "toHaveProperty":
3952
- if (!assertion.target[0]) {
3953
- this.#onTargetArgumentMustBeProvided("key", assertion, onDiagnostics);
4398
+ if (!argumentIsProvided("key", assertion.target?.[0], assertion.matcherNameNode.name, onDiagnostics)) {
3954
4399
  return;
3955
4400
  }
3956
4401
  return this.toHaveProperty.match(matchWorker, assertion.source[0], assertion.target[0], onDiagnostics);
3957
- case "toRaiseError":
3958
- if (assertion.isNot && this.#rejectsTypeArguments(matchWorker, onDiagnostics)) {
3959
- return;
3960
- }
3961
- return this.toRaiseError.match(matchWorker, assertion.source[0], [...assertion.target], onDiagnostics);
3962
4402
  default:
3963
4403
  this.#onMatcherIsNotSupported(matcherNameText, assertion, onDiagnostics);
3964
4404
  }
@@ -3966,45 +4406,64 @@ class ExpectService {
3966
4406
  }
3967
4407
  #onMatcherIsNotSupported(matcherNameText, assertion, onDiagnostics) {
3968
4408
  const text = ExpectDiagnosticText.matcherIsNotSupported(matcherNameText);
3969
- const origin = DiagnosticOrigin.fromNode(assertion.matcherName);
4409
+ const origin = DiagnosticOrigin.fromNode(assertion.matcherNameNode.name);
3970
4410
  onDiagnostics(Diagnostic.error(text, origin));
3971
4411
  }
3972
- #onSourceArgumentOrTypeArgumentMustBeProvided(assertion, onDiagnostics) {
3973
- const text = ExpectDiagnosticText.argumentOrTypeArgumentMustBeProvided("source", "Source");
3974
- const origin = DiagnosticOrigin.fromNode(assertion.node.expression);
3975
- onDiagnostics(Diagnostic.error(text, origin));
4412
+ }
4413
+
4414
+ function capitalize(text) {
4415
+ return text.replace(/^./, text.charAt(0).toUpperCase());
4416
+ }
4417
+
4418
+ class RejectDiagnosticText {
4419
+ static argumentCannotBeOfType(argumentNameText, typeText) {
4420
+ return `An argument for '${argumentNameText}' cannot be of the '${typeText}' type.`;
3976
4421
  }
3977
- #onTargetArgumentMustBeProvided(argumentNameText, assertion, onDiagnostics) {
3978
- const text = ExpectDiagnosticText.argumentMustBeProvided(argumentNameText);
3979
- const origin = DiagnosticOrigin.fromNode(assertion.matcherName);
3980
- onDiagnostics(Diagnostic.error(text, origin));
4422
+ static typeArgumentCannotBeOfType(argumentNameText, typeText) {
4423
+ return `A type argument for '${argumentNameText}' cannot be of the '${typeText}' type.`;
3981
4424
  }
3982
- #onTargetArgumentOrTypeArgumentMustBeProvided(assertion, onDiagnostics) {
3983
- const text = ExpectDiagnosticText.argumentOrTypeArgumentMustBeProvided("target", "Target");
3984
- const origin = DiagnosticOrigin.fromNode(assertion.matcherName);
3985
- onDiagnostics(Diagnostic.error(text, origin));
4425
+ static typeWasRejected(typeText) {
4426
+ const optionNameText = `reject${capitalize(typeText)}Type`;
4427
+ return [
4428
+ `The '${typeText}' type was rejected because the '${optionNameText}' option is enabled.`,
4429
+ `If this check is necessary, pass '${typeText}' as the type argument explicitly.`,
4430
+ ];
4431
+ }
4432
+ }
4433
+
4434
+ class Reject {
4435
+ #compiler;
4436
+ #rejectedArgumentTypes = new Set();
4437
+ #typeChecker;
4438
+ constructor(compiler, typeChecker, resolvedConfig) {
4439
+ this.#compiler = compiler;
4440
+ this.#typeChecker = typeChecker;
4441
+ if (resolvedConfig?.rejectAnyType) {
4442
+ this.#rejectedArgumentTypes.add("any");
4443
+ }
4444
+ if (resolvedConfig?.rejectNeverType) {
4445
+ this.#rejectedArgumentTypes.add("never");
4446
+ }
3986
4447
  }
3987
- #rejectsTypeArguments(matchWorker, onDiagnostics) {
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) {
4448
+ argumentType(target, onDiagnostics) {
4449
+ for (const rejectedType of this.#rejectedArgumentTypes) {
4450
+ const allowedKeyword = this.#compiler.SyntaxKind[`${capitalize(rejectedType)}Keyword`];
4451
+ if (target.some(([, node]) => node?.kind === allowedKeyword)) {
3992
4452
  continue;
3993
4453
  }
3994
- for (const argumentName of ["source", "target"]) {
3995
- const argumentNode = matchWorker.assertion[argumentName][0];
3996
- if (!argumentNode) {
4454
+ for (const [name, node] of target) {
4455
+ if (!node) {
3997
4456
  continue;
3998
4457
  }
3999
- if (matchWorker.getType(argumentNode).flags & this.#compiler.TypeFlags[Format.capitalize(rejectedType)]) {
4458
+ if (this.#typeChecker.getTypeAtLocation(node).flags & this.#compiler.TypeFlags[capitalize(rejectedType)]) {
4000
4459
  const text = [
4001
- this.#compiler.isTypeNode(argumentNode)
4002
- ? ExpectDiagnosticText.typeArgumentCannotBeOfType(Format.capitalize(argumentName), rejectedType)
4003
- : ExpectDiagnosticText.argumentCannotBeOfType(argumentName, rejectedType),
4004
- ...ExpectDiagnosticText.typeWasRejected(rejectedType),
4460
+ nodeBelongsToArgumentList(this.#compiler, node)
4461
+ ? RejectDiagnosticText.argumentCannotBeOfType(name, rejectedType)
4462
+ : RejectDiagnosticText.typeArgumentCannotBeOfType(capitalize(name), rejectedType),
4463
+ ...RejectDiagnosticText.typeWasRejected(rejectedType),
4005
4464
  ];
4006
- const origin = DiagnosticOrigin.fromNode(argumentNode);
4007
- onDiagnostics(Diagnostic.error(text, origin));
4465
+ const origin = DiagnosticOrigin.fromNode(node);
4466
+ onDiagnostics([Diagnostic.error(text, origin)]);
4008
4467
  return true;
4009
4468
  }
4010
4469
  }
@@ -4013,20 +4472,78 @@ class ExpectService {
4013
4472
  }
4014
4473
  }
4015
4474
 
4475
+ class WhenDiagnosticText {
4476
+ static actionIsNotSupported(actionNameText) {
4477
+ return `The '.${actionNameText}()' action is not supported.`;
4478
+ }
4479
+ }
4480
+
4481
+ class WhenService {
4482
+ #onDiagnostics;
4483
+ #reject;
4484
+ constructor(reject, onDiagnostics) {
4485
+ this.#reject = reject;
4486
+ this.#onDiagnostics = onDiagnostics;
4487
+ }
4488
+ action(when) {
4489
+ if (!argumentIsProvided("target", when.target[0], when.node.expression, this.#onDiagnostics) ||
4490
+ this.#reject.argumentType([["target", when.target[0]]], this.#onDiagnostics)) {
4491
+ return;
4492
+ }
4493
+ const actionNameText = when.actionNameNode.name.getText();
4494
+ switch (actionNameText) {
4495
+ case "isCalledWith":
4496
+ break;
4497
+ default:
4498
+ this.#onActionIsNotSupported(actionNameText, when, this.#onDiagnostics);
4499
+ return;
4500
+ }
4501
+ if (when.abilityDiagnostics != null && when.abilityDiagnostics.size > 0) {
4502
+ const diagnostics = [];
4503
+ for (const diagnostic of when.abilityDiagnostics) {
4504
+ if (isDiagnosticWithLocation(diagnostic)) {
4505
+ const text = getDiagnosticMessageText(diagnostic);
4506
+ let origin;
4507
+ if (isDiagnosticWithLocation(diagnostic) && diagnosticBelongsToNode(diagnostic, when.node)) {
4508
+ origin = DiagnosticOrigin.fromNodes(when.target);
4509
+ }
4510
+ else {
4511
+ origin = new DiagnosticOrigin(diagnostic.start, getTextSpanEnd(diagnostic), when.node.getSourceFile());
4512
+ }
4513
+ let related;
4514
+ if (diagnostic.relatedInformation != null) {
4515
+ related = Diagnostic.fromDiagnostics(diagnostic.relatedInformation);
4516
+ }
4517
+ diagnostics.push(Diagnostic.error(text, origin).add({ related }));
4518
+ }
4519
+ }
4520
+ this.#onDiagnostics(diagnostics);
4521
+ }
4522
+ }
4523
+ #onActionIsNotSupported(actionNameText, when, onDiagnostics) {
4524
+ const text = WhenDiagnosticText.actionIsNotSupported(actionNameText);
4525
+ const origin = DiagnosticOrigin.fromNode(when.actionNameNode.name);
4526
+ onDiagnostics([Diagnostic.error(text, origin)]);
4527
+ }
4528
+ }
4529
+
4016
4530
  class TestTreeWalker {
4017
4531
  #cancellationToken;
4018
4532
  #expectService;
4019
4533
  #hasOnly;
4534
+ #onTaskDiagnostics;
4020
4535
  #position;
4021
4536
  #resolvedConfig;
4022
- #taskResult;
4023
- constructor(resolvedConfig, compiler, typeChecker, options) {
4537
+ #whenService;
4538
+ constructor(compiler, typeChecker, resolvedConfig, onTaskDiagnostics, options) {
4024
4539
  this.#resolvedConfig = resolvedConfig;
4540
+ this.#onTaskDiagnostics = onTaskDiagnostics;
4025
4541
  this.#cancellationToken = options.cancellationToken;
4026
4542
  this.#hasOnly = options.hasOnly || resolvedConfig.only != null || options.position != null;
4027
4543
  this.#position = options.position;
4028
- this.#taskResult = options.taskResult;
4029
- this.#expectService = new ExpectService(compiler, typeChecker, this.#resolvedConfig);
4544
+ const reject = new Reject(compiler, typeChecker, resolvedConfig);
4545
+ this.#expectService = new ExpectService(compiler, typeChecker, reject);
4546
+ this.#whenService = new WhenService(reject, onTaskDiagnostics);
4030
4547
  }
4031
4548
  #resolveRunMode(mode, testNode) {
4032
4549
  if (testNode.flags & TestTreeNodeFlags.Fail) {
@@ -4051,25 +4568,23 @@ class TestTreeWalker {
4051
4568
  }
4052
4569
  return mode;
4053
4570
  }
4054
- visit(testNodes, runMode, parentResult) {
4055
- for (const testNode of testNodes) {
4571
+ visit(nodes, runMode, parentResult) {
4572
+ for (const node of nodes) {
4056
4573
  if (this.#cancellationToken?.isCancellationRequested) {
4057
4574
  break;
4058
4575
  }
4059
- const validationError = testNode.validate();
4060
- if (validationError.length > 0) {
4061
- EventEmitter.dispatch(["task:error", { diagnostics: validationError, result: this.#taskResult }]);
4062
- break;
4063
- }
4064
- switch (testNode.brand) {
4576
+ switch (node.brand) {
4065
4577
  case TestTreeNodeBrand.Describe:
4066
- this.#visitDescribe(testNode, runMode, parentResult);
4578
+ this.#visitDescribe(node, runMode, parentResult);
4067
4579
  break;
4068
4580
  case TestTreeNodeBrand.Test:
4069
- this.#visitTest(testNode, runMode, parentResult);
4581
+ this.#visitTest(node, runMode, parentResult);
4070
4582
  break;
4071
4583
  case TestTreeNodeBrand.Expect:
4072
- this.#visitAssertion(testNode, runMode, parentResult);
4584
+ this.#visitAssertion(node, runMode, parentResult);
4585
+ break;
4586
+ case TestTreeNodeBrand.When:
4587
+ this.#visitWhen(node);
4073
4588
  break;
4074
4589
  }
4075
4590
  }
@@ -4089,7 +4604,7 @@ class TestTreeWalker {
4089
4604
  { diagnostics: Array.isArray(diagnostics) ? diagnostics : [diagnostics], result: expectResult },
4090
4605
  ]);
4091
4606
  };
4092
- if (assertion.diagnostics.size > 0 && assertion.matcherName.getText() !== "toRaiseError") {
4607
+ if (assertion.diagnostics.size > 0 && assertion.matcherNameNode.name.text !== "toRaiseError") {
4093
4608
  onExpectDiagnostics(Diagnostic.fromDiagnostics([...assertion.diagnostics]));
4094
4609
  return;
4095
4610
  }
@@ -4120,13 +4635,7 @@ class TestTreeWalker {
4120
4635
  runMode = this.#resolveRunMode(runMode, describe);
4121
4636
  if (!(runMode & RunMode.Skip || (this.#hasOnly && !(runMode & RunMode.Only)) || runMode & RunMode.Todo) &&
4122
4637
  describe.diagnostics.size > 0) {
4123
- EventEmitter.dispatch([
4124
- "task:error",
4125
- {
4126
- diagnostics: Diagnostic.fromDiagnostics([...describe.diagnostics]),
4127
- result: this.#taskResult,
4128
- },
4129
- ]);
4638
+ this.#onTaskDiagnostics(Diagnostic.fromDiagnostics([...describe.diagnostics]));
4130
4639
  }
4131
4640
  else {
4132
4641
  this.visit(describe.children, runMode, describeResult);
@@ -4163,71 +4672,86 @@ class TestTreeWalker {
4163
4672
  EventEmitter.dispatch(["test:pass", { result: testResult }]);
4164
4673
  }
4165
4674
  }
4675
+ #visitWhen(when) {
4676
+ this.#whenService.action(when);
4677
+ }
4166
4678
  }
4167
4679
 
4168
4680
  class TaskRunner {
4169
- #compiler;
4170
4681
  #collectService;
4682
+ #compiler;
4171
4683
  #resolvedConfig;
4172
4684
  #projectService;
4173
- constructor(resolvedConfig, compiler) {
4174
- this.#resolvedConfig = resolvedConfig;
4685
+ constructor(compiler, resolvedConfig) {
4175
4686
  this.#compiler = compiler;
4176
- this.#collectService = new CollectService(compiler);
4177
- this.#projectService = new ProjectService(this.#resolvedConfig, compiler);
4687
+ this.#resolvedConfig = resolvedConfig;
4688
+ this.#projectService = new ProjectService(compiler, this.#resolvedConfig);
4689
+ this.#collectService = new CollectService(compiler, this.#projectService, this.#resolvedConfig);
4178
4690
  }
4179
- run(task, cancellationToken) {
4180
- if (cancellationToken?.isCancellationRequested) {
4691
+ #onDiagnostics(diagnostics, result) {
4692
+ EventEmitter.dispatch(["task:error", { diagnostics, result }]);
4693
+ }
4694
+ async run(task, cancellationToken) {
4695
+ if (cancellationToken.isCancellationRequested) {
4181
4696
  return;
4182
4697
  }
4183
4698
  this.#projectService.openFile(task.filePath, undefined, this.#resolvedConfig.rootPath);
4184
4699
  const taskResult = new TaskResult(task);
4185
4700
  EventEmitter.dispatch(["task:start", { result: taskResult }]);
4186
- this.#run(task, taskResult, cancellationToken);
4701
+ await this.#run(task, taskResult, cancellationToken);
4187
4702
  EventEmitter.dispatch(["task:end", { result: taskResult }]);
4188
4703
  this.#projectService.closeFile(task.filePath);
4189
4704
  }
4190
- #run(task, taskResult, cancellationToken) {
4705
+ async #run(task, taskResult, cancellationToken) {
4191
4706
  if (!existsSync(task.filePath)) {
4192
- EventEmitter.dispatch([
4193
- "task:error",
4194
- { diagnostics: [Diagnostic.error(`Test file '${task.filePath}' does not exist.`)], result: taskResult },
4195
- ]);
4196
- return;
4197
- }
4198
- const languageService = this.#projectService.getLanguageService(task.filePath);
4199
- if (!languageService) {
4707
+ this.#onDiagnostics([Diagnostic.error(`Test file '${task.filePath}' does not exist.`)], taskResult);
4200
4708
  return;
4201
4709
  }
4202
- const syntacticDiagnostics = languageService.getSyntacticDiagnostics(task.filePath);
4203
- if (syntacticDiagnostics.length > 0) {
4204
- EventEmitter.dispatch([
4205
- "task:error",
4206
- { diagnostics: Diagnostic.fromDiagnostics(syntacticDiagnostics), result: taskResult },
4207
- ]);
4710
+ let languageService = this.#projectService.getLanguageService(task.filePath);
4711
+ const syntacticDiagnostics = languageService?.getSyntacticDiagnostics(task.filePath);
4712
+ if (syntacticDiagnostics != null && syntacticDiagnostics.length > 0) {
4713
+ this.#onDiagnostics(Diagnostic.fromDiagnostics(syntacticDiagnostics), taskResult);
4208
4714
  return;
4209
4715
  }
4210
- const semanticDiagnostics = languageService.getSemanticDiagnostics(task.filePath);
4211
- const program = languageService.getProgram();
4212
- if (!program) {
4213
- return;
4716
+ let semanticDiagnostics = languageService?.getSemanticDiagnostics(task.filePath);
4717
+ let program = languageService?.getProgram();
4718
+ let sourceFile = program?.getSourceFile(task.filePath);
4719
+ if (sourceFile?.text.startsWith("// @tstyche-template")) {
4720
+ if (semanticDiagnostics != null && semanticDiagnostics.length > 0) {
4721
+ this.#onDiagnostics(Diagnostic.fromDiagnostics(semanticDiagnostics), taskResult);
4722
+ return;
4723
+ }
4724
+ const moduleSpecifier = pathToFileURL(task.filePath).toString();
4725
+ const testText = (await import(moduleSpecifier))?.default;
4726
+ if (typeof testText !== "string") {
4727
+ this.#onDiagnostics([Diagnostic.error("A template test file must export a string.")], taskResult);
4728
+ return;
4729
+ }
4730
+ this.#projectService.openFile(task.filePath, testText, this.#resolvedConfig.rootPath);
4731
+ languageService = this.#projectService.getLanguageService(task.filePath);
4732
+ const syntacticDiagnostics = languageService?.getSyntacticDiagnostics(task.filePath);
4733
+ if (syntacticDiagnostics != null && syntacticDiagnostics.length > 0) {
4734
+ this.#onDiagnostics(Diagnostic.fromDiagnostics(syntacticDiagnostics), taskResult);
4735
+ return;
4736
+ }
4737
+ semanticDiagnostics = languageService?.getSemanticDiagnostics(task.filePath);
4738
+ program = languageService?.getProgram();
4739
+ sourceFile = program?.getSourceFile(task.filePath);
4214
4740
  }
4215
- const sourceFile = program.getSourceFile(task.filePath);
4216
4741
  if (!sourceFile) {
4217
4742
  return;
4218
4743
  }
4219
4744
  const testTree = this.#collectService.createTestTree(sourceFile, semanticDiagnostics);
4220
4745
  if (testTree.diagnostics.size > 0) {
4221
- EventEmitter.dispatch([
4222
- "task:error",
4223
- { diagnostics: Diagnostic.fromDiagnostics([...testTree.diagnostics]), result: taskResult },
4224
- ]);
4746
+ this.#onDiagnostics(Diagnostic.fromDiagnostics([...testTree.diagnostics]), taskResult);
4225
4747
  return;
4226
4748
  }
4227
- const typeChecker = program.getTypeChecker();
4228
- const testTreeWalker = new TestTreeWalker(this.#resolvedConfig, this.#compiler, typeChecker, {
4749
+ const typeChecker = program?.getTypeChecker();
4750
+ const onTaskDiagnostics = (diagnostics) => {
4751
+ this.#onDiagnostics(diagnostics, taskResult);
4752
+ };
4753
+ const testTreeWalker = new TestTreeWalker(this.#compiler, typeChecker, this.#resolvedConfig, onTaskDiagnostics, {
4229
4754
  cancellationToken,
4230
- taskResult,
4231
4755
  hasOnly: testTree.hasOnly,
4232
4756
  position: task.position,
4233
4757
  });
@@ -4238,15 +4762,13 @@ class TaskRunner {
4238
4762
  class Runner {
4239
4763
  #eventEmitter = new EventEmitter();
4240
4764
  #resolvedConfig;
4241
- static version = "4.0.0-beta.0";
4765
+ static version = "4.0.0-beta.10";
4242
4766
  constructor(resolvedConfig) {
4243
4767
  this.#resolvedConfig = resolvedConfig;
4244
4768
  }
4245
4769
  #addHandlers(cancellationToken) {
4246
4770
  const resultHandler = new ResultHandler();
4247
4771
  this.#eventEmitter.addHandler(resultHandler);
4248
- const testTreeHandler = new TestTreeHandler();
4249
- this.#eventEmitter.addHandler(testTreeHandler);
4250
4772
  if (this.#resolvedConfig.failFast) {
4251
4773
  const cancellationHandler = new CancellationHandler(cancellationToken, CancellationReason.FailFast);
4252
4774
  this.#eventEmitter.addHandler(cancellationHandler);
@@ -4289,16 +4811,16 @@ class Runner {
4289
4811
  this.#eventEmitter.removeHandlers();
4290
4812
  }
4291
4813
  async #run(tasks, cancellationToken) {
4292
- const result = new Result(this.#resolvedConfig, tasks);
4814
+ const result = new Result(tasks);
4293
4815
  EventEmitter.dispatch(["run:start", { result }]);
4294
4816
  for (const target of this.#resolvedConfig.target) {
4295
4817
  const targetResult = new TargetResult(target, tasks);
4296
4818
  EventEmitter.dispatch(["target:start", { result: targetResult }]);
4297
4819
  const compiler = await Store.load(target);
4298
4820
  if (compiler) {
4299
- const taskRunner = new TaskRunner(this.#resolvedConfig, compiler);
4821
+ const taskRunner = new TaskRunner(compiler, this.#resolvedConfig);
4300
4822
  for (const task of tasks) {
4301
- taskRunner.run(task, cancellationToken);
4823
+ await taskRunner.run(task, cancellationToken);
4302
4824
  }
4303
4825
  }
4304
4826
  EventEmitter.dispatch(["target:end", { result: targetResult }]);
@@ -4440,4 +4962,4 @@ class Cli {
4440
4962
  }
4441
4963
  }
4442
4964
 
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 };
4965
+ 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, Reject, Result, ResultCount, ResultHandler, ResultStatus, ResultTiming, Runner, Scribbler, Select, SelectDiagnosticText, SetupReporter, SourceFile, Store, SummaryReporter, TargetResult, Task, TaskResult, TestResult, TestTree, TestTreeNode, TestTreeNodeBrand, TestTreeNodeFlags, Text, Version, WatchReporter, WatchService, Watcher, WhenNode, WhenService, addsPackageText, argumentIsProvided, argumentOrTypeArgumentIsProvided, defaultOptions, describeNameText, diagnosticBelongsToNode, diagnosticText, environmentOptions, fileViewText, formattedText, getDiagnosticMessageText, getTextSpanEnd, helpText, isDiagnosticWithLocation, nodeBelongsToArgumentList, summaryText, taskStatusText, testNameText, usesCompilerText, waitingForFileChangesText, watchUsageText };