tstyche 4.0.0-beta.0 → 4.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -57,9 +57,10 @@ test("handles numbers", () => {
57
57
  Here is the list of all matchers:
58
58
 
59
59
  - `.toBe()`, `.toBeAssignableTo()`, `.toBeAssignableWith()` compare types or types of expression,
60
- - `.toAcceptProps()` checks JSX component props type,
60
+ - `.toAcceptProps()` checks the type of JSX component props,
61
+ - `.toBeApplicable` ensures that the decorator function can be applied,
61
62
  - `.toHaveProperty()` looks up keys on an object type,
62
- - `.toRaiseError()` captures the type error message or code.
63
+ - `.toRaiseError()` captures the message or code of a type error.
63
64
 
64
65
  ## Runner
65
66
 
package/build/index.d.cts CHANGED
@@ -97,6 +97,10 @@ interface Matchers {
97
97
  */
98
98
  (target: unknown): void;
99
99
  };
100
+ /**
101
+ * Checks if the decorator function can be applied.
102
+ */
103
+ toBeApplicable: (target: unknown, context: DecoratorContext) => void;
100
104
  /**
101
105
  * Checks if the source type is assignable to the target type.
102
106
  */
package/build/index.d.ts CHANGED
@@ -97,6 +97,10 @@ interface Matchers {
97
97
  */
98
98
  (target: unknown): void;
99
99
  };
100
+ /**
101
+ * Checks if the decorator function can be applied.
102
+ */
103
+ toBeApplicable: (target: unknown, context: DecoratorContext) => void;
100
104
  /**
101
105
  * Checks if the source type is assignable to the target type.
102
106
  */
@@ -102,24 +102,16 @@ declare class TestTree {
102
102
  constructor(diagnostics: Set<ts.Diagnostic>, sourceFile: ts.SourceFile);
103
103
  }
104
104
 
105
- interface MatcherNode extends ts.CallExpression {
106
- expression: ts.PropertyAccessExpression;
107
- }
108
105
  declare class AssertionNode extends TestTreeNode {
106
+ abilityDiagnostics: Set<ts.Diagnostic> | undefined;
109
107
  isNot: boolean;
110
- matcherName: ts.MemberName;
111
- matcherNode: MatcherNode;
108
+ matcherNode: ts.CallExpression | ts.Decorator;
109
+ matcherNameNode: ts.PropertyAccessExpression;
112
110
  modifierNode: ts.PropertyAccessExpression;
113
111
  notNode: ts.PropertyAccessExpression | undefined;
114
112
  source: ts.NodeArray<ts.Expression> | ts.NodeArray<ts.TypeNode>;
115
- target: ts.NodeArray<ts.Expression> | ts.NodeArray<ts.TypeNode>;
116
- constructor(compiler: typeof ts, brand: TestTreeNodeBrand, node: ts.CallExpression, parent: TestTree | TestTreeNode, flags: TestTreeNodeFlags, matcherNode: MatcherNode, modifierNode: ts.PropertyAccessExpression, notNode?: ts.PropertyAccessExpression);
117
- }
118
-
119
- declare class CollectService {
120
- #private;
121
- constructor(compiler: typeof ts);
122
- createTestTree(sourceFile: ts.SourceFile, semanticDiagnostics?: Array<ts.Diagnostic>): TestTree;
113
+ target: ts.NodeArray<ts.Expression> | ts.NodeArray<ts.TypeNode> | undefined;
114
+ constructor(compiler: typeof ts, brand: TestTreeNodeBrand, node: ts.CallExpression, parent: TestTree | TestTreeNode, flags: TestTreeNodeFlags, matcherNode: ts.CallExpression | ts.Decorator, matcherNameNode: ts.PropertyAccessExpression, modifierNode: ts.PropertyAccessExpression, notNode?: ts.PropertyAccessExpression);
123
115
  }
124
116
 
125
117
  declare enum OptionBrand {
@@ -207,7 +199,7 @@ interface CommandLineOptions {
207
199
  */
208
200
  failFast?: boolean;
209
201
  /**
210
- * Fetch specified versions of the 'typescript' package and exit.
202
+ * Fetch the specified versions of the 'typescript' package and exit.
211
203
  */
212
204
  fetch?: boolean;
213
205
  /**
@@ -330,6 +322,21 @@ declare class Options {
330
322
 
331
323
  declare const defaultOptions: Required<ConfigFileOptions>;
332
324
 
325
+ declare class ProjectService {
326
+ #private;
327
+ constructor(resolvedConfig: ResolvedConfig, compiler: typeof ts);
328
+ closeFile(filePath: string): void;
329
+ getDefaultProject(filePath: string): ts.server.Project | undefined;
330
+ getLanguageService(filePath: string): ts.LanguageService | undefined;
331
+ openFile(filePath: string, sourceText?: string | undefined, projectRootPath?: string | undefined): void;
332
+ }
333
+
334
+ declare class CollectService {
335
+ #private;
336
+ constructor(compiler: typeof ts, projectService: ProjectService, resolvedConfig: ResolvedConfig);
337
+ createTestTree(sourceFile: ts.SourceFile, semanticDiagnostics?: Array<ts.Diagnostic>): TestTree;
338
+ }
339
+
333
340
  interface EnvironmentOptions {
334
341
  /**
335
342
  * Is `true` if the process is running in continuous integration environment.
@@ -592,6 +599,7 @@ declare class ExpectService {
592
599
  #private;
593
600
  private toAcceptProps;
594
601
  private toBe;
602
+ private toBeApplicable;
595
603
  private toBeAssignableTo;
596
604
  private toBeAssignableWith;
597
605
  private toHaveProperty;
@@ -767,15 +775,6 @@ declare class PluginService {
767
775
  static removeHandlers(): void;
768
776
  }
769
777
 
770
- declare class ProjectService {
771
- #private;
772
- constructor(resolvedConfig: ResolvedConfig, compiler: typeof ts);
773
- closeFile(filePath: string): void;
774
- getDefaultProject(filePath: string): ts.server.Project | undefined;
775
- getLanguageService(filePath: string): ts.LanguageService | undefined;
776
- openFile(filePath: string, sourceText?: string | undefined, projectRootPath?: string | undefined): void;
777
- }
778
-
779
778
  declare class Runner {
780
779
  #private;
781
780
  static version: string;
package/build/tstyche.js CHANGED
@@ -93,7 +93,7 @@ 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) {
@@ -903,7 +903,7 @@ class Options {
903
903
  },
904
904
  {
905
905
  brand: OptionBrand.BareTrue,
906
- description: "Fetch specified versions of the 'typescript' package and exit.",
906
+ description: "Fetch the specified versions of the 'typescript' package and exit.",
907
907
  group: OptionGroup.CommandLine,
908
908
  name: "fetch",
909
909
  },
@@ -1948,17 +1948,17 @@ class TestTreeNode {
1948
1948
  };
1949
1949
  switch (this.brand) {
1950
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))));
1951
+ for (const child of this.children) {
1952
+ if (child.brand === TestTreeNodeBrand.Expect) {
1953
+ diagnostics.push(Diagnostic.error(getText(child.node), DiagnosticOrigin.fromNode(getParentCallExpression(child.node))));
1954
1954
  }
1955
1955
  }
1956
1956
  break;
1957
1957
  case TestTreeNodeBrand.Test:
1958
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)));
1959
+ for (const child of this.children) {
1960
+ if (child.brand !== TestTreeNodeBrand.Expect) {
1961
+ diagnostics.push(Diagnostic.error(getText(child.node), DiagnosticOrigin.fromNode(child.node)));
1962
1962
  }
1963
1963
  }
1964
1964
  break;
@@ -1968,21 +1968,24 @@ class TestTreeNode {
1968
1968
  }
1969
1969
 
1970
1970
  class AssertionNode extends TestTreeNode {
1971
+ abilityDiagnostics;
1971
1972
  isNot;
1972
- matcherName;
1973
1973
  matcherNode;
1974
+ matcherNameNode;
1974
1975
  modifierNode;
1975
1976
  notNode;
1976
1977
  source;
1977
1978
  target;
1978
- constructor(compiler, brand, node, parent, flags, matcherNode, modifierNode, notNode) {
1979
+ constructor(compiler, brand, node, parent, flags, matcherNode, matcherNameNode, modifierNode, notNode) {
1979
1980
  super(compiler, brand, node, parent, flags);
1980
1981
  this.isNot = notNode != null;
1981
- this.matcherName = matcherNode.expression.name;
1982
1982
  this.matcherNode = matcherNode;
1983
+ this.matcherNameNode = matcherNameNode;
1983
1984
  this.modifierNode = modifierNode;
1984
1985
  this.source = this.node.typeArguments ?? this.node.arguments;
1985
- this.target = this.matcherNode.typeArguments ?? this.matcherNode.arguments;
1986
+ if (compiler.isCallExpression(this.matcherNode)) {
1987
+ this.target = this.matcherNode.typeArguments ?? this.matcherNode.arguments;
1988
+ }
1986
1989
  for (const diagnostic of parent.diagnostics) {
1987
1990
  if (diagnostic.start != null && diagnostic.start >= this.source.pos && diagnostic.start <= this.source.end) {
1988
1991
  this.diagnostics.add(diagnostic);
@@ -1992,6 +1995,86 @@ class AssertionNode extends TestTreeNode {
1992
1995
  }
1993
1996
  }
1994
1997
 
1998
+ class AbilityLayer {
1999
+ #filePath = "";
2000
+ #nodes = [];
2001
+ #projectService;
2002
+ #resolvedConfig;
2003
+ #text = "";
2004
+ constructor(projectService, resolvedConfig) {
2005
+ this.#projectService = projectService;
2006
+ this.#resolvedConfig = resolvedConfig;
2007
+ }
2008
+ #getErasedRangeText(range) {
2009
+ if (this.#text.indexOf("\n", range.start) >= range.end) {
2010
+ return " ".repeat(range.end - range.start);
2011
+ }
2012
+ const text = [];
2013
+ for (let i = range.start; i < range.end; i++) {
2014
+ switch (this.#text.charAt(i)) {
2015
+ case "\n":
2016
+ case "\r":
2017
+ text.push(this.#text.charAt(i));
2018
+ break;
2019
+ default:
2020
+ text.push(" ");
2021
+ }
2022
+ }
2023
+ return text.join("");
2024
+ }
2025
+ #addRanges(node, ranges) {
2026
+ this.#nodes.push(node);
2027
+ for (const range of ranges) {
2028
+ const rangeText = this.#getErasedRangeText(range);
2029
+ this.#text = `${this.#text.slice(0, range.start)}${rangeText}${this.#text.slice(range.end)}`;
2030
+ }
2031
+ }
2032
+ close() {
2033
+ if (this.#nodes.length > 0) {
2034
+ this.#projectService.openFile(this.#filePath, this.#text, this.#resolvedConfig.rootPath);
2035
+ const languageService = this.#projectService.getLanguageService(this.#filePath);
2036
+ const diagnostics = new Set(languageService?.getSemanticDiagnostics(this.#filePath)?.toReversed());
2037
+ if (diagnostics.size > 0) {
2038
+ for (const node of this.#nodes.toReversed()) {
2039
+ for (const diagnostic of diagnostics) {
2040
+ if (diagnostic.start != null &&
2041
+ diagnostic.start >= node.matcherNode.pos &&
2042
+ diagnostic.start <= node.matcherNode.end) {
2043
+ if (!node.abilityDiagnostics) {
2044
+ node.abilityDiagnostics = new Set();
2045
+ }
2046
+ node.abilityDiagnostics.add(diagnostic);
2047
+ diagnostics.delete(diagnostic);
2048
+ }
2049
+ }
2050
+ }
2051
+ }
2052
+ }
2053
+ this.#filePath = "";
2054
+ this.#nodes = [];
2055
+ this.#text = "";
2056
+ }
2057
+ handleNode(assertionNode) {
2058
+ switch (assertionNode.matcherNameNode.name.text) {
2059
+ case "toBeApplicable": {
2060
+ const expectStart = assertionNode.node.pos;
2061
+ const expectExpressionEnd = assertionNode.node.expression.end;
2062
+ const expectEnd = assertionNode.node.end;
2063
+ const matcherNameEnd = assertionNode.matcherNameNode.end;
2064
+ this.#addRanges(assertionNode, [
2065
+ { end: expectExpressionEnd + 1, start: expectStart },
2066
+ { end: matcherNameEnd, start: expectEnd - 1 },
2067
+ ]);
2068
+ break;
2069
+ }
2070
+ }
2071
+ }
2072
+ open(sourceFile) {
2073
+ this.#filePath = sourceFile.fileName;
2074
+ this.#text = sourceFile.text;
2075
+ }
2076
+ }
2077
+
1995
2078
  var TestTreeNodeFlags;
1996
2079
  (function (TestTreeNodeFlags) {
1997
2080
  TestTreeNodeFlags[TestTreeNodeFlags["None"] = 0] = "None";
@@ -2103,9 +2186,15 @@ class TestTree {
2103
2186
  }
2104
2187
 
2105
2188
  class CollectService {
2189
+ #abilityLayer;
2106
2190
  #compiler;
2107
- constructor(compiler) {
2191
+ #projectService;
2192
+ #resolvedConfig;
2193
+ constructor(compiler, projectService, resolvedConfig) {
2108
2194
  this.#compiler = compiler;
2195
+ this.#projectService = projectService;
2196
+ this.#resolvedConfig = resolvedConfig;
2197
+ this.#abilityLayer = new AbilityLayer(this.#projectService, this.#resolvedConfig);
2109
2198
  }
2110
2199
  #collectTestTreeNodes(node, identifiers, parent) {
2111
2200
  if (this.#compiler.isCallExpression(node)) {
@@ -2125,12 +2214,17 @@ class CollectService {
2125
2214
  return;
2126
2215
  }
2127
2216
  const notNode = this.#getChainedNode(modifierNode, "not");
2128
- const matcherNode = this.#getChainedNode(notNode ?? modifierNode)?.parent;
2129
- if (!matcherNode || !this.#isMatcherNode(matcherNode)) {
2217
+ const matcherNameNode = this.#getChainedNode(notNode ?? modifierNode);
2218
+ if (!matcherNameNode) {
2219
+ return;
2220
+ }
2221
+ const matcherNode = this.#getMatcherNode(matcherNameNode);
2222
+ if (!matcherNode) {
2130
2223
  return;
2131
2224
  }
2132
- const assertionNode = new AssertionNode(this.#compiler, meta.brand, node, parent, meta.flags, matcherNode, modifierNode, notNode);
2225
+ const assertionNode = new AssertionNode(this.#compiler, meta.brand, node, parent, meta.flags, matcherNode, matcherNameNode, modifierNode, notNode);
2133
2226
  parent.children.push(assertionNode);
2227
+ this.#abilityLayer.handleNode(assertionNode);
2134
2228
  EventEmitter.dispatch(["collect:node", { testNode: assertionNode }]);
2135
2229
  this.#compiler.forEachChild(node, (node) => {
2136
2230
  this.#collectTestTreeNodes(node, identifiers, assertionNode);
@@ -2149,7 +2243,9 @@ class CollectService {
2149
2243
  createTestTree(sourceFile, semanticDiagnostics = []) {
2150
2244
  const testTree = new TestTree(new Set(semanticDiagnostics), sourceFile);
2151
2245
  EventEmitter.dispatch(["collect:start", { testTree }]);
2246
+ this.#abilityLayer.open(sourceFile);
2152
2247
  this.#collectTestTreeNodes(sourceFile, new IdentifierLookup(this.#compiler), testTree);
2248
+ this.#abilityLayer.close();
2153
2249
  EventEmitter.dispatch(["collect:end", { testTree }]);
2154
2250
  return testTree;
2155
2251
  }
@@ -2162,8 +2258,17 @@ class CollectService {
2162
2258
  }
2163
2259
  return parent;
2164
2260
  }
2165
- #isMatcherNode(node) {
2166
- return this.#compiler.isCallExpression(node) && this.#compiler.isPropertyAccessExpression(node.expression);
2261
+ #getMatcherNode(node) {
2262
+ if (this.#compiler.isCallExpression(node.parent)) {
2263
+ return node.parent;
2264
+ }
2265
+ if (this.#compiler.isDecorator(node.parent)) {
2266
+ return node.parent;
2267
+ }
2268
+ if (this.#compiler.isParenthesizedExpression(node.parent)) {
2269
+ return this.#getMatcherNode(node.parent);
2270
+ }
2271
+ return;
2167
2272
  }
2168
2273
  }
2169
2274
 
@@ -3335,6 +3440,12 @@ class ExpectDiagnosticText {
3335
3440
  static componentDoesNotAcceptProps(isTypeNode) {
3336
3441
  return `${isTypeNode ? "Component type" : "Component"} does not accept props of the given type.`;
3337
3442
  }
3443
+ static decoratorCanBeApplied(targetText) {
3444
+ return `The decorator function can be applied${targetText}.`;
3445
+ }
3446
+ static decoratorCanNotBeApplied(targetText) {
3447
+ return `The decorator function can not be applied${targetText}.`;
3448
+ }
3338
3449
  static matcherIsNotSupported(matcherNameText) {
3339
3450
  return `The '.${matcherNameText}()' matcher is not supported.`;
3340
3451
  }
@@ -3738,6 +3849,76 @@ class ToBe extends RelationMatcherBase {
3738
3849
  }
3739
3850
  }
3740
3851
 
3852
+ class ToBeApplicable {
3853
+ #compiler;
3854
+ constructor(compiler) {
3855
+ this.#compiler = compiler;
3856
+ }
3857
+ #resolveTargetText(node) {
3858
+ let text = "";
3859
+ switch (node.kind) {
3860
+ case this.#compiler.SyntaxKind.ClassDeclaration:
3861
+ text = "class";
3862
+ break;
3863
+ case this.#compiler.SyntaxKind.MethodDeclaration:
3864
+ text = "method";
3865
+ break;
3866
+ case this.#compiler.SyntaxKind.PropertyDeclaration:
3867
+ text = node.modifiers?.some((modifier) => modifier.kind === this.#compiler.SyntaxKind.AccessorKeyword)
3868
+ ? "accessor"
3869
+ : "field";
3870
+ break;
3871
+ case this.#compiler.SyntaxKind.GetAccessor:
3872
+ text = "getter";
3873
+ break;
3874
+ case this.#compiler.SyntaxKind.SetAccessor:
3875
+ text = "setter";
3876
+ break;
3877
+ }
3878
+ if (text.length > 0) {
3879
+ text = ` to this ${text}`;
3880
+ }
3881
+ return text;
3882
+ }
3883
+ #explain(matchWorker, sourceNode) {
3884
+ const targetText = this.#resolveTargetText(matchWorker.assertion.matcherNode.parent);
3885
+ const diagnostics = [];
3886
+ if (matchWorker.assertion.abilityDiagnostics) {
3887
+ for (const diagnostic of matchWorker.assertion.abilityDiagnostics) {
3888
+ const text = [
3889
+ ExpectDiagnosticText.decoratorCanNotBeApplied(targetText),
3890
+ typeof diagnostic.messageText === "string"
3891
+ ? diagnostic.messageText
3892
+ : Diagnostic.toMessageText(diagnostic.messageText),
3893
+ ];
3894
+ const origin = DiagnosticOrigin.fromNode(sourceNode);
3895
+ diagnostics.push(Diagnostic.error(text.flat(), origin));
3896
+ }
3897
+ }
3898
+ else {
3899
+ const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertion);
3900
+ diagnostics.push(Diagnostic.error(ExpectDiagnosticText.decoratorCanBeApplied(targetText), origin));
3901
+ }
3902
+ return diagnostics;
3903
+ }
3904
+ match(matchWorker, sourceNode, onDiagnostics) {
3905
+ const type = matchWorker.getType(sourceNode);
3906
+ if (type.getCallSignatures().length === 0) {
3907
+ const expectedText = "of a function type";
3908
+ const text = this.#compiler.isTypeNode(sourceNode)
3909
+ ? ExpectDiagnosticText.typeArgumentMustBe("Source", expectedText)
3910
+ : ExpectDiagnosticText.argumentMustBe("source", expectedText);
3911
+ const origin = DiagnosticOrigin.fromNode(sourceNode);
3912
+ onDiagnostics([Diagnostic.error(text, origin)]);
3913
+ return;
3914
+ }
3915
+ return {
3916
+ explain: () => this.#explain(matchWorker, sourceNode),
3917
+ isMatch: !matchWorker.assertion.abilityDiagnostics,
3918
+ };
3919
+ }
3920
+ }
3921
+
3741
3922
  class ToBeAssignableTo extends RelationMatcherBase {
3742
3923
  explainText = ExpectDiagnosticText.typeIsAssignableTo;
3743
3924
  explainNotText = ExpectDiagnosticText.typeIsNotAssignableTo;
@@ -3908,6 +4089,7 @@ class ExpectService {
3908
4089
  #typeChecker;
3909
4090
  toAcceptProps;
3910
4091
  toBe;
4092
+ toBeApplicable;
3911
4093
  toBeAssignableTo;
3912
4094
  toBeAssignableWith;
3913
4095
  toHaveProperty;
@@ -3923,41 +4105,42 @@ class ExpectService {
3923
4105
  }
3924
4106
  this.toAcceptProps = new ToAcceptProps(compiler, typeChecker);
3925
4107
  this.toBe = new ToBe();
4108
+ this.toBeApplicable = new ToBeApplicable(compiler);
3926
4109
  this.toBeAssignableTo = new ToBeAssignableTo();
3927
4110
  this.toBeAssignableWith = new ToBeAssignableWith();
3928
4111
  this.toHaveProperty = new ToHaveProperty(compiler);
3929
4112
  this.toRaiseError = new ToRaiseError(compiler);
3930
4113
  }
3931
4114
  match(assertion, onDiagnostics) {
3932
- const matcherNameText = assertion.matcherName.getText();
4115
+ const matcherNameText = assertion.matcherNameNode.name.text;
3933
4116
  if (!assertion.source[0]) {
3934
4117
  this.#onSourceArgumentOrTypeArgumentMustBeProvided(assertion, onDiagnostics);
3935
4118
  return;
3936
4119
  }
3937
4120
  const matchWorker = new MatchWorker(this.#compiler, this.#typeChecker, assertion);
4121
+ if (!(matcherNameText === "toRaiseError" && assertion.isNot === false) &&
4122
+ this.#rejectsTypeArguments(matchWorker, onDiagnostics)) {
4123
+ return;
4124
+ }
3938
4125
  switch (matcherNameText) {
3939
4126
  case "toAcceptProps":
3940
4127
  case "toBe":
3941
4128
  case "toBeAssignableTo":
3942
4129
  case "toBeAssignableWith":
3943
- if (!assertion.target[0]) {
4130
+ if (!assertion.target?.[0]) {
3944
4131
  this.#onTargetArgumentOrTypeArgumentMustBeProvided(assertion, onDiagnostics);
3945
4132
  return;
3946
4133
  }
3947
- if (this.#rejectsTypeArguments(matchWorker, onDiagnostics)) {
3948
- return;
3949
- }
3950
4134
  return this[matcherNameText].match(matchWorker, assertion.source[0], assertion.target[0], onDiagnostics);
4135
+ case "toBeApplicable":
4136
+ return this.toBeApplicable.match(matchWorker, assertion.source[0], onDiagnostics);
3951
4137
  case "toHaveProperty":
3952
- if (!assertion.target[0]) {
4138
+ if (!assertion.target?.[0]) {
3953
4139
  this.#onTargetArgumentMustBeProvided("key", assertion, onDiagnostics);
3954
4140
  return;
3955
4141
  }
3956
4142
  return this.toHaveProperty.match(matchWorker, assertion.source[0], assertion.target[0], onDiagnostics);
3957
4143
  case "toRaiseError":
3958
- if (assertion.isNot && this.#rejectsTypeArguments(matchWorker, onDiagnostics)) {
3959
- return;
3960
- }
3961
4144
  return this.toRaiseError.match(matchWorker, assertion.source[0], [...assertion.target], onDiagnostics);
3962
4145
  default:
3963
4146
  this.#onMatcherIsNotSupported(matcherNameText, assertion, onDiagnostics);
@@ -3966,7 +4149,7 @@ class ExpectService {
3966
4149
  }
3967
4150
  #onMatcherIsNotSupported(matcherNameText, assertion, onDiagnostics) {
3968
4151
  const text = ExpectDiagnosticText.matcherIsNotSupported(matcherNameText);
3969
- const origin = DiagnosticOrigin.fromNode(assertion.matcherName);
4152
+ const origin = DiagnosticOrigin.fromNode(assertion.matcherNameNode.name);
3970
4153
  onDiagnostics(Diagnostic.error(text, origin));
3971
4154
  }
3972
4155
  #onSourceArgumentOrTypeArgumentMustBeProvided(assertion, onDiagnostics) {
@@ -3976,23 +4159,23 @@ class ExpectService {
3976
4159
  }
3977
4160
  #onTargetArgumentMustBeProvided(argumentNameText, assertion, onDiagnostics) {
3978
4161
  const text = ExpectDiagnosticText.argumentMustBeProvided(argumentNameText);
3979
- const origin = DiagnosticOrigin.fromNode(assertion.matcherName);
4162
+ const origin = DiagnosticOrigin.fromNode(assertion.matcherNameNode.name);
3980
4163
  onDiagnostics(Diagnostic.error(text, origin));
3981
4164
  }
3982
4165
  #onTargetArgumentOrTypeArgumentMustBeProvided(assertion, onDiagnostics) {
3983
4166
  const text = ExpectDiagnosticText.argumentOrTypeArgumentMustBeProvided("target", "Target");
3984
- const origin = DiagnosticOrigin.fromNode(assertion.matcherName);
4167
+ const origin = DiagnosticOrigin.fromNode(assertion.matcherNameNode.name);
3985
4168
  onDiagnostics(Diagnostic.error(text, origin));
3986
4169
  }
3987
4170
  #rejectsTypeArguments(matchWorker, onDiagnostics) {
3988
4171
  for (const rejectedType of this.#rejectTypes) {
3989
4172
  const allowedKeyword = this.#compiler.SyntaxKind[`${Format.capitalize(rejectedType)}Keyword`];
3990
4173
  if (matchWorker.assertion.source[0]?.kind === allowedKeyword ||
3991
- matchWorker.assertion.target[0]?.kind === allowedKeyword) {
4174
+ matchWorker.assertion.target?.[0]?.kind === allowedKeyword) {
3992
4175
  continue;
3993
4176
  }
3994
4177
  for (const argumentName of ["source", "target"]) {
3995
- const argumentNode = matchWorker.assertion[argumentName][0];
4178
+ const argumentNode = matchWorker.assertion[argumentName]?.[0];
3996
4179
  if (!argumentNode) {
3997
4180
  continue;
3998
4181
  }
@@ -4089,7 +4272,7 @@ class TestTreeWalker {
4089
4272
  { diagnostics: Array.isArray(diagnostics) ? diagnostics : [diagnostics], result: expectResult },
4090
4273
  ]);
4091
4274
  };
4092
- if (assertion.diagnostics.size > 0 && assertion.matcherName.getText() !== "toRaiseError") {
4275
+ if (assertion.diagnostics.size > 0 && assertion.matcherNameNode.name.text !== "toRaiseError") {
4093
4276
  onExpectDiagnostics(Diagnostic.fromDiagnostics([...assertion.diagnostics]));
4094
4277
  return;
4095
4278
  }
@@ -4173,8 +4356,8 @@ class TaskRunner {
4173
4356
  constructor(resolvedConfig, compiler) {
4174
4357
  this.#resolvedConfig = resolvedConfig;
4175
4358
  this.#compiler = compiler;
4176
- this.#collectService = new CollectService(compiler);
4177
4359
  this.#projectService = new ProjectService(this.#resolvedConfig, compiler);
4360
+ this.#collectService = new CollectService(compiler, this.#projectService, this.#resolvedConfig);
4178
4361
  }
4179
4362
  run(task, cancellationToken) {
4180
4363
  if (cancellationToken?.isCancellationRequested) {
@@ -4238,7 +4421,7 @@ class TaskRunner {
4238
4421
  class Runner {
4239
4422
  #eventEmitter = new EventEmitter();
4240
4423
  #resolvedConfig;
4241
- static version = "4.0.0-beta.0";
4424
+ static version = "4.0.0-beta.1";
4242
4425
  constructor(resolvedConfig) {
4243
4426
  this.#resolvedConfig = resolvedConfig;
4244
4427
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tstyche",
3
- "version": "4.0.0-beta.0",
3
+ "version": "4.0.0-beta.1",
4
4
  "description": "The Essential Type Testing Tool.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -60,17 +60,17 @@
60
60
  "devDependencies": {
61
61
  "@biomejs/biome": "1.9.4",
62
62
  "@rollup/plugin-typescript": "12.1.2",
63
- "@types/node": "22.13.14",
64
- "@types/react": "19.0.12",
63
+ "@types/node": "22.14.0",
64
+ "@types/react": "19.1.0",
65
65
  "ajv": "8.17.1",
66
66
  "cspell": "8.18.1",
67
67
  "magic-string": "0.30.17",
68
68
  "monocart-coverage-reports": "2.12.3",
69
69
  "pretty-ansi": "3.0.0",
70
- "rollup": "4.38.0",
70
+ "rollup": "4.39.0",
71
71
  "rollup-plugin-dts": "6.2.1",
72
72
  "tslib": "2.8.1",
73
- "typescript": "5.8.2"
73
+ "typescript": "5.8.3"
74
74
  },
75
75
  "peerDependencies": {
76
76
  "typescript": "5.x"
@@ -80,7 +80,7 @@
80
80
  "optional": true
81
81
  }
82
82
  },
83
- "packageManager": "yarn@4.8.0",
83
+ "packageManager": "yarn@4.8.1",
84
84
  "engines": {
85
85
  "node": ">=20.9"
86
86
  }