tstyche 4.0.0-beta.7 → 4.0.0-beta.8

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.
Files changed (2) hide show
  1. package/build/tstyche.js +64 -60
  2. package/package.json +2 -2
package/build/tstyche.js CHANGED
@@ -3173,27 +3173,27 @@ class IdentifierLookup {
3173
3173
  }
3174
3174
  expression = expression.expression;
3175
3175
  }
3176
- let identifierName;
3176
+ let identifier;
3177
3177
  if (this.#compiler.isPropertyAccessExpression(expression) &&
3178
3178
  expression.expression.getText() === this.#identifiers.namespace) {
3179
- identifierName = expression.name.getText();
3179
+ identifier = expression.name.getText();
3180
3180
  }
3181
3181
  else {
3182
- identifierName = Object.keys(this.#identifiers.namedImports).find((key) => this.#identifiers.namedImports[key] === expression.getText());
3182
+ identifier = Object.keys(this.#identifiers.namedImports).find((key) => this.#identifiers.namedImports[key] === expression.getText());
3183
3183
  }
3184
- if (!identifierName) {
3184
+ if (!identifier) {
3185
3185
  return;
3186
3186
  }
3187
- switch (identifierName) {
3187
+ switch (identifier) {
3188
3188
  case "describe":
3189
- return { brand: TestTreeNodeBrand.Describe, flags };
3189
+ return { brand: TestTreeNodeBrand.Describe, flags, identifier };
3190
3190
  case "it":
3191
3191
  case "test":
3192
- return { brand: TestTreeNodeBrand.Test, flags };
3192
+ return { brand: TestTreeNodeBrand.Test, flags, identifier };
3193
3193
  case "expect":
3194
- return { brand: TestTreeNodeBrand.Expect, flags };
3194
+ return { brand: TestTreeNodeBrand.Expect, flags, identifier };
3195
3195
  case "when":
3196
- return { brand: TestTreeNodeBrand.When, flags };
3196
+ return { brand: TestTreeNodeBrand.When, flags, identifier };
3197
3197
  }
3198
3198
  return;
3199
3199
  }
@@ -3232,6 +3232,7 @@ class WhenNode extends TestTreeNode {
3232
3232
  class CollectService {
3233
3233
  #abilityLayer;
3234
3234
  #compiler;
3235
+ #identifierLookup;
3235
3236
  #projectService;
3236
3237
  #resolvedConfig;
3237
3238
  #testTree;
@@ -3240,14 +3241,15 @@ class CollectService {
3240
3241
  this.#projectService = projectService;
3241
3242
  this.#resolvedConfig = resolvedConfig;
3242
3243
  this.#abilityLayer = new AbilityLayer(compiler, this.#projectService, this.#resolvedConfig);
3244
+ this.#identifierLookup = new IdentifierLookup(compiler);
3243
3245
  }
3244
- #collectTestTreeNodes(node, identifiers, parent) {
3246
+ #collectTestTreeNodes(node, parent) {
3245
3247
  if (this.#compiler.isCallExpression(node)) {
3246
- const meta = identifiers.resolveTestMemberMeta(node);
3248
+ const meta = this.#identifierLookup.resolveTestMemberMeta(node);
3247
3249
  if (meta != null && (meta.brand === TestTreeNodeBrand.Describe || meta.brand === TestTreeNodeBrand.Test)) {
3248
3250
  const testTreeNode = new TestTreeNode(this.#compiler, meta.brand, node, parent, meta.flags);
3249
3251
  this.#compiler.forEachChild(node, (node) => {
3250
- this.#collectTestTreeNodes(node, identifiers, testTreeNode);
3252
+ this.#collectTestTreeNodes(node, testTreeNode);
3251
3253
  });
3252
3254
  this.#onNode(testTreeNode, parent);
3253
3255
  return;
@@ -3269,7 +3271,7 @@ class CollectService {
3269
3271
  const assertionNode = new AssertionNode(this.#compiler, meta.brand, node, parent, meta.flags, matcherNode, matcherNameNode, modifierNode, notNode);
3270
3272
  this.#abilityLayer.handleAssertion(assertionNode);
3271
3273
  this.#compiler.forEachChild(node, (node) => {
3272
- this.#collectTestTreeNodes(node, identifiers, assertionNode);
3274
+ this.#collectTestTreeNodes(node, assertionNode);
3273
3275
  });
3274
3276
  this.#onNode(assertionNode, parent);
3275
3277
  return;
@@ -3285,9 +3287,9 @@ class CollectService {
3285
3287
  }
3286
3288
  this.#compiler.forEachChild(actionNode, (node) => {
3287
3289
  if (this.#compiler.isCallExpression(node)) {
3288
- const meta = identifiers.resolveTestMemberMeta(node);
3290
+ const meta = this.#identifierLookup.resolveTestMemberMeta(node);
3289
3291
  if (meta != null && (meta.brand === TestTreeNodeBrand.Describe || meta.brand === TestTreeNodeBrand.Test)) {
3290
- const text = CollectDiagnosticText.cannotBeNestedWithin(node.expression.getText(), "when");
3292
+ const text = CollectDiagnosticText.cannotBeNestedWithin(meta.identifier, "when");
3291
3293
  const origin = DiagnosticOrigin.fromNode(node);
3292
3294
  this.#onDiagnostics(Diagnostic.error(text, origin));
3293
3295
  }
@@ -3300,11 +3302,11 @@ class CollectService {
3300
3302
  }
3301
3303
  }
3302
3304
  if (this.#compiler.isImportDeclaration(node)) {
3303
- identifiers.handleImportDeclaration(node);
3305
+ this.#identifierLookup.handleImportDeclaration(node);
3304
3306
  return;
3305
3307
  }
3306
3308
  this.#compiler.forEachChild(node, (node) => {
3307
- this.#collectTestTreeNodes(node, identifiers, parent);
3309
+ this.#collectTestTreeNodes(node, parent);
3308
3310
  });
3309
3311
  }
3310
3312
  createTestTree(sourceFile, semanticDiagnostics = []) {
@@ -3312,7 +3314,7 @@ class CollectService {
3312
3314
  EventEmitter.dispatch(["collect:start", { tree: testTree }]);
3313
3315
  this.#testTree = testTree;
3314
3316
  this.#abilityLayer.open(sourceFile);
3315
- this.#collectTestTreeNodes(sourceFile, new IdentifierLookup(this.#compiler), testTree);
3317
+ this.#collectTestTreeNodes(sourceFile, testTree);
3316
3318
  this.#abilityLayer.close();
3317
3319
  this.#testTree = undefined;
3318
3320
  EventEmitter.dispatch(["collect:end", { tree: testTree }]);
@@ -3362,6 +3364,7 @@ class ProjectService {
3362
3364
  #lastSeenProject = "";
3363
3365
  #resolvedConfig;
3364
3366
  #seenPrograms = new WeakSet();
3367
+ #seenTestFiles = new Set();
3365
3368
  #service;
3366
3369
  constructor(compiler, resolvedConfig) {
3367
3370
  this.#compiler = compiler;
@@ -3399,15 +3402,6 @@ class ProjectService {
3399
3402
  useInferredProjectPerProjectRoot: true,
3400
3403
  useSingleInferredProject: false,
3401
3404
  });
3402
- switch (this.#resolvedConfig.tsconfig) {
3403
- case "findup":
3404
- break;
3405
- case "ignore":
3406
- this.#service.getConfigFileNameForFile = () => undefined;
3407
- break;
3408
- default:
3409
- this.#service.getConfigFileNameForFile = () => this.#resolvedConfig.tsconfig;
3410
- }
3411
3405
  this.#service.setCompilerOptionsForInferredProjects(this.#getDefaultCompilerOptions());
3412
3406
  }
3413
3407
  closeFile(filePath) {
@@ -3442,7 +3436,23 @@ class ProjectService {
3442
3436
  const project = this.getDefaultProject(filePath);
3443
3437
  return project?.getLanguageService(true);
3444
3438
  }
3439
+ #isFileIncluded(filePath) {
3440
+ const configSourceFile = this.#compiler.readJsonConfigFile(this.#resolvedConfig.tsconfig, this.#compiler.sys.readFile);
3441
+ const { fileNames } = this.#compiler.parseJsonSourceFileConfigFileContent(configSourceFile, this.#compiler.sys, Path.dirname(this.#resolvedConfig.tsconfig), undefined, this.#resolvedConfig.tsconfig);
3442
+ return fileNames.includes(filePath);
3443
+ }
3445
3444
  openFile(filePath, sourceText, projectRootPath) {
3445
+ switch (this.#resolvedConfig.tsconfig) {
3446
+ case "findup":
3447
+ break;
3448
+ case "ignore":
3449
+ this.#service.getConfigFileNameForFile = () => undefined;
3450
+ break;
3451
+ default:
3452
+ this.#service.getConfigFileNameForFile = this.#isFileIncluded(filePath)
3453
+ ? () => this.#resolvedConfig.tsconfig
3454
+ : () => undefined;
3455
+ }
3446
3456
  const { configFileErrors, configFileName } = this.#service.openClientFile(filePath, sourceText, undefined, projectRootPath);
3447
3457
  if (configFileName !== this.#lastSeenProject) {
3448
3458
  this.#lastSeenProject = configFileName;
@@ -3457,29 +3467,29 @@ class ProjectService {
3457
3467
  { diagnostics: Diagnostic.fromDiagnostics(configFileErrors) },
3458
3468
  ]);
3459
3469
  }
3460
- if (this.#resolvedConfig.checkSourceFiles && !sourceText) {
3470
+ if (this.#resolvedConfig.checkSourceFiles && !this.#seenTestFiles.has(filePath)) {
3471
+ this.#seenTestFiles.add(filePath);
3461
3472
  const languageService = this.getLanguageService(filePath);
3462
3473
  const program = languageService?.getProgram();
3463
3474
  if (!program || this.#seenPrograms.has(program)) {
3464
3475
  return;
3465
3476
  }
3466
3477
  this.#seenPrograms.add(program);
3467
- const filesToCheck = [];
3468
- for (const sourceFile of program.getSourceFiles()) {
3478
+ const sourceFilesToCheck = program.getSourceFiles().filter((sourceFile) => {
3469
3479
  if (program.isSourceFileFromExternalLibrary(sourceFile) || program.isSourceFileDefaultLibrary(sourceFile)) {
3470
- continue;
3480
+ return false;
3471
3481
  }
3472
- if (!Select.isTestFile(sourceFile.fileName, { ...this.#resolvedConfig, pathMatch: [] })) {
3473
- filesToCheck.push(sourceFile);
3482
+ if (Select.isTestFile(sourceFile.fileName, { ...this.#resolvedConfig, pathMatch: [] })) {
3483
+ return false;
3474
3484
  }
3475
- }
3485
+ return true;
3486
+ });
3476
3487
  const diagnostics = [];
3477
- for (const sourceFile of filesToCheck) {
3488
+ for (const sourceFile of sourceFilesToCheck) {
3478
3489
  diagnostics.push(...program.getSyntacticDiagnostics(sourceFile), ...program.getSemanticDiagnostics(sourceFile));
3479
3490
  }
3480
3491
  if (diagnostics.length > 0) {
3481
3492
  EventEmitter.dispatch(["project:error", { diagnostics: Diagnostic.fromDiagnostics(diagnostics) }]);
3482
- return;
3483
3493
  }
3484
3494
  }
3485
3495
  }
@@ -4587,7 +4597,7 @@ class TestTreeWalker {
4587
4597
  EventEmitter.dispatch(["test:pass", { result: testResult }]);
4588
4598
  }
4589
4599
  }
4590
- #visitWhen(when, _runMode, _parentResult) {
4600
+ #visitWhen(when, runMode, parentResult) {
4591
4601
  if (when.abilityDiagnostics != null && when.abilityDiagnostics.size > 0) {
4592
4602
  const diagnostics = [];
4593
4603
  for (const diagnostic of when.abilityDiagnostics) {
@@ -4610,6 +4620,7 @@ class TestTreeWalker {
4610
4620
  EventEmitter.dispatch(["task:error", { diagnostics, result: this.#taskResult }]);
4611
4621
  return;
4612
4622
  }
4623
+ this.visit(when.children, runMode, parentResult);
4613
4624
  }
4614
4625
  }
4615
4626
 
@@ -4625,6 +4636,9 @@ class TaskRunner {
4625
4636
  this.#projectService = new ProjectService(compiler, this.#resolvedConfig);
4626
4637
  this.#collectService = new CollectService(compiler, this.#projectService, this.#resolvedConfig);
4627
4638
  }
4639
+ #onDiagnostics(diagnostics, result) {
4640
+ EventEmitter.dispatch(["task:error", { diagnostics, result }]);
4641
+ }
4628
4642
  async run(task, cancellationToken) {
4629
4643
  if (cancellationToken.isCancellationRequested) {
4630
4644
  return;
@@ -4638,19 +4652,13 @@ class TaskRunner {
4638
4652
  }
4639
4653
  async #run(task, taskResult, cancellationToken) {
4640
4654
  if (!existsSync(task.filePath)) {
4641
- EventEmitter.dispatch([
4642
- "task:error",
4643
- { diagnostics: [Diagnostic.error(`Test file '${task.filePath}' does not exist.`)], result: taskResult },
4644
- ]);
4655
+ this.#onDiagnostics([Diagnostic.error(`Test file '${task.filePath}' does not exist.`)], taskResult);
4645
4656
  return;
4646
4657
  }
4647
4658
  let languageService = this.#projectService.getLanguageService(task.filePath);
4648
4659
  const syntacticDiagnostics = languageService?.getSyntacticDiagnostics(task.filePath);
4649
4660
  if (syntacticDiagnostics != null && syntacticDiagnostics.length > 0) {
4650
- EventEmitter.dispatch([
4651
- "task:error",
4652
- { diagnostics: Diagnostic.fromDiagnostics(syntacticDiagnostics), result: taskResult },
4653
- ]);
4661
+ this.#onDiagnostics(Diagnostic.fromDiagnostics(syntacticDiagnostics), taskResult);
4654
4662
  return;
4655
4663
  }
4656
4664
  let semanticDiagnostics = languageService?.getSemanticDiagnostics(task.filePath);
@@ -4658,21 +4666,20 @@ class TaskRunner {
4658
4666
  let sourceFile = program?.getSourceFile(task.filePath);
4659
4667
  if (sourceFile?.text.startsWith("// @tstyche-template")) {
4660
4668
  if (semanticDiagnostics != null && semanticDiagnostics.length > 0) {
4661
- EventEmitter.dispatch([
4662
- "task:error",
4663
- { diagnostics: Diagnostic.fromDiagnostics(semanticDiagnostics), result: taskResult },
4664
- ]);
4669
+ this.#onDiagnostics(Diagnostic.fromDiagnostics(semanticDiagnostics), taskResult);
4670
+ return;
4671
+ }
4672
+ const moduleSpecifier = pathToFileURL(task.filePath).toString();
4673
+ const testText = (await import(moduleSpecifier))?.default;
4674
+ if (typeof testText !== "string") {
4675
+ this.#onDiagnostics([Diagnostic.error("A template test file must export a string.")], taskResult);
4665
4676
  return;
4666
4677
  }
4667
- const text = (await import(task.filePath)).default;
4668
- this.#projectService.openFile(task.filePath, text, this.#resolvedConfig.rootPath);
4678
+ this.#projectService.openFile(task.filePath, testText, this.#resolvedConfig.rootPath);
4669
4679
  languageService = this.#projectService.getLanguageService(task.filePath);
4670
4680
  const syntacticDiagnostics = languageService?.getSyntacticDiagnostics(task.filePath);
4671
4681
  if (syntacticDiagnostics != null && syntacticDiagnostics.length > 0) {
4672
- EventEmitter.dispatch([
4673
- "task:error",
4674
- { diagnostics: Diagnostic.fromDiagnostics(syntacticDiagnostics), result: taskResult },
4675
- ]);
4682
+ this.#onDiagnostics(Diagnostic.fromDiagnostics(syntacticDiagnostics), taskResult);
4676
4683
  return;
4677
4684
  }
4678
4685
  semanticDiagnostics = languageService?.getSemanticDiagnostics(task.filePath);
@@ -4690,10 +4697,7 @@ class TaskRunner {
4690
4697
  return;
4691
4698
  }
4692
4699
  if (testTree.diagnostics.size > 0) {
4693
- EventEmitter.dispatch([
4694
- "task:error",
4695
- { diagnostics: Diagnostic.fromDiagnostics([...testTree.diagnostics]), result: taskResult },
4696
- ]);
4700
+ this.#onDiagnostics(Diagnostic.fromDiagnostics([...testTree.diagnostics]), taskResult);
4697
4701
  return;
4698
4702
  }
4699
4703
  const typeChecker = program?.getTypeChecker();
@@ -4710,7 +4714,7 @@ class TaskRunner {
4710
4714
  class Runner {
4711
4715
  #eventEmitter = new EventEmitter();
4712
4716
  #resolvedConfig;
4713
- static version = "4.0.0-beta.7";
4717
+ static version = "4.0.0-beta.8";
4714
4718
  constructor(resolvedConfig) {
4715
4719
  this.#resolvedConfig = resolvedConfig;
4716
4720
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tstyche",
3
- "version": "4.0.0-beta.7",
3
+ "version": "4.0.0-beta.8",
4
4
  "description": "The Essential Type Testing Tool.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -65,7 +65,7 @@
65
65
  "@types/node": "22.15.3",
66
66
  "@types/react": "19.1.2",
67
67
  "ajv": "8.17.1",
68
- "cspell": "8.19.4",
68
+ "cspell": "9.0.0",
69
69
  "magic-string": "0.30.17",
70
70
  "monocart-coverage-reports": "2.12.4",
71
71
  "pretty-ansi": "3.0.0",