tstyche 3.0.0-beta.5 → 3.0.0-beta.6

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.
@@ -132,7 +132,8 @@ declare enum OptionBrand {
132
132
 
133
133
  declare enum OptionGroup {
134
134
  CommandLine = 2,
135
- ConfigFile = 4
135
+ ConfigFile = 4,
136
+ ResolvedConfig = 6
136
137
  }
137
138
 
138
139
  declare class ConfigDiagnosticText {
@@ -146,6 +147,7 @@ declare class ConfigDiagnosticText {
146
147
  static testFileMatchCannotStartWith(segment: string): Array<string>;
147
148
  static requiresValueType(optionName: string, optionBrand: OptionBrand, optionGroup: OptionGroup): string;
148
149
  static unknownOption(optionName: string): string;
150
+ static usage(optionName: string, optionBrand: OptionBrand, optionGroup: OptionGroup): Promise<Array<string>>;
149
151
  static versionIsNotSupported(value: string): string;
150
152
  static watchCannotBeEnabled(): string;
151
153
  }
@@ -305,6 +307,8 @@ interface ListTypeOptionDefinition extends BaseOptionDefinition {
305
307
  declare class Options {
306
308
  #private;
307
309
  static for(optionGroup: OptionGroup): Map<string, OptionDefinition>;
310
+ static resolve(optionName: string, optionValue: string, rootPath?: string): string;
311
+ static validate(optionDefinition: OptionDefinition | ItemDefinition, optionValue: string, optionGroup: OptionGroup, onDiagnostics: DiagnosticsHandler$1, origin?: DiagnosticOrigin): Promise<void>;
308
312
  }
309
313
 
310
314
  declare const defaultOptions: Required<ConfigFileOptions>;
@@ -714,25 +718,6 @@ declare class ResultHandler implements EventHandler {
714
718
  on([event, payload]: Event): void;
715
719
  }
716
720
 
717
- interface Hooks {
718
- /**
719
- * Is called after configuration is resolved and allows to modify it.
720
- */
721
- config?: (resolvedConfig: ResolvedConfig) => ResolvedConfig | Promise<ResolvedConfig>;
722
- /**
723
- * Is called after test files are selected and allows to modify the list.
724
- */
725
- select?: (testFiles: Array<string>) => Array<string | URL> | Promise<Array<string | URL>>;
726
- }
727
-
728
- declare class HooksService {
729
- #private;
730
- static addHandler(hooks: Hooks): void;
731
- static call(hook: "config", resolvedConfig: ResolvedConfig): Promise<ResolvedConfig>;
732
- static call(hook: "select", testFiles: Array<string>): Promise<Array<string | URL>>;
733
- static removeHandlers(): void;
734
- }
735
-
736
721
  type InputHandler = (chunk: string) => void;
737
722
  declare class InputService {
738
723
  #private;
@@ -849,6 +834,32 @@ declare class Path {
849
834
  static resolve(...filePaths: Array<string>): string;
850
835
  }
851
836
 
837
+ interface SelectHookContext {
838
+ resolvedConfig: ResolvedConfig;
839
+ }
840
+ interface Plugin {
841
+ /**
842
+ * The name of this plugin.
843
+ */
844
+ name: string;
845
+ /**
846
+ * Is called after configuration is resolved and allows to modify it.
847
+ */
848
+ config?: (resolvedConfig: ResolvedConfig) => ResolvedConfig | Promise<ResolvedConfig>;
849
+ /**
850
+ * Is called after test files are selected and allows to modify the list.
851
+ */
852
+ select?: (this: SelectHookContext, testFiles: Array<string>) => Array<string | URL> | Promise<Array<string | URL>>;
853
+ }
854
+
855
+ type Hooks = Required<Omit<Plugin, "name">>;
856
+ declare class PluginService {
857
+ #private;
858
+ static addHandler(plugin: Plugin): void;
859
+ static call<T extends keyof Hooks>(hook: T, argument: Parameters<Hooks[T]>[0], context: ThisParameterType<Hooks[T]>): Promise<Awaited<ReturnType<Hooks[T]>>>;
860
+ static removeHandlers(): void;
861
+ }
862
+
852
863
  declare class ProjectService {
853
864
  #private;
854
865
  constructor(resolvedConfig: ResolvedConfig, compiler: typeof ts);
@@ -917,4 +928,4 @@ declare class WatchService {
917
928
  watch(cancellationToken: CancellationToken): AsyncIterable<Array<Task>>;
918
929
  }
919
930
 
920
- export { Assertion, BaseReporter, CancellationHandler, CancellationReason, CancellationToken, Cli, CollectService, Color, type CommandLineOptions, Config, ConfigDiagnosticText, type ConfigFileOptions, DescribeResult, Diagnostic, DiagnosticCategory, DiagnosticOrigin, type DiagnosticsHandler$1 as DiagnosticsHandler, type EnvironmentOptions, type Event, EventEmitter, type EventHandler, ExitCodeHandler, ExpectResult, ExpectService, type FileWatchHandler, FileWatcher, type Hooks, HooksService, type InputHandler, InputService, type ItemDefinition, Line, ListReporter, type MatchResult, OptionBrand, type OptionDefinition, OptionGroup, Options, OutputService, Path, ProjectResult, ProjectService, type Reporter, type ReporterEvent, type ResolvedConfig, Result, ResultCount, ResultHandler, ResultStatus, ResultTiming, Runner, Scribbler, ScribblerJsx, type ScribblerOptions, Select, SelectDiagnosticText, SetupReporter, SourceFile, Store, SummaryReporter, TargetResult, type TargetResultStatus, Task, TaskResult, type TaskResultStatus, TestMember, TestMemberBrand, TestMemberFlags, TestResult, TestTree, Text, type TypeChecker, Version, type WatchHandler, WatchReporter, WatchService, Watcher, type WatcherOptions, addsPackageText, defaultOptions, describeNameText, diagnosticText, environmentOptions, fileViewText, formattedText, helpText, summaryText, taskStatusText, testNameText, usesCompilerText, waitingForFileChangesText, watchUsageText };
931
+ export { Assertion, BaseReporter, CancellationHandler, CancellationReason, CancellationToken, Cli, CollectService, Color, type CommandLineOptions, Config, ConfigDiagnosticText, type ConfigFileOptions, DescribeResult, Diagnostic, DiagnosticCategory, DiagnosticOrigin, type DiagnosticsHandler$1 as DiagnosticsHandler, type EnvironmentOptions, type Event, EventEmitter, type EventHandler, ExitCodeHandler, ExpectResult, ExpectService, type FileWatchHandler, FileWatcher, type InputHandler, InputService, type ItemDefinition, Line, ListReporter, type MatchResult, OptionBrand, type OptionDefinition, OptionGroup, Options, OutputService, Path, type Plugin, PluginService, ProjectResult, ProjectService, type Reporter, type ReporterEvent, type ResolvedConfig, Result, ResultCount, ResultHandler, ResultStatus, ResultTiming, Runner, Scribbler, ScribblerJsx, type ScribblerOptions, Select, SelectDiagnosticText, SetupReporter, SourceFile, Store, SummaryReporter, TargetResult, type TargetResultStatus, Task, TaskResult, type TaskResultStatus, TestMember, TestMemberBrand, TestMemberFlags, TestResult, TestTree, Text, type TypeChecker, Version, type WatchHandler, WatchReporter, WatchService, Watcher, type WatcherOptions, addsPackageText, defaultOptions, describeNameText, diagnosticText, environmentOptions, fileViewText, formattedText, helpText, summaryText, taskStatusText, testNameText, usesCompilerText, waitingForFileChangesText, watchUsageText };
package/build/tstyche.js CHANGED
@@ -1,64 +1,12 @@
1
- import { writeFileSync, rmSync, existsSync, watch } from 'node:fs';
2
1
  import fs from 'node:fs/promises';
3
- import path from 'node:path';
4
- import { pathToFileURL, fileURLToPath } from 'node:url';
5
2
  import { createRequire } from 'node:module';
6
3
  import vm from 'node:vm';
7
4
  import os from 'node:os';
8
5
  import process from 'node:process';
6
+ import path from 'node:path';
7
+ import { writeFileSync, rmSync, existsSync, watch } from 'node:fs';
9
8
  import streamConsumers from 'node:stream/consumers';
10
-
11
- class ConfigDiagnosticText {
12
- static expected(element) {
13
- return `Expected ${element}.`;
14
- }
15
- static expectsListItemType(optionName, optionBrand) {
16
- return `Item of the '${optionName}' list must be of type ${optionBrand}.`;
17
- }
18
- static expectsValue(optionName, optionGroup) {
19
- optionName = ConfigDiagnosticText.#optionName(optionName, optionGroup);
20
- return `Option '${optionName}' expects a value.`;
21
- }
22
- static fileDoesNotExist(filePath) {
23
- return `The specified path '${filePath}' does not exist.`;
24
- }
25
- static moduleWasNotFound(specifier) {
26
- return `The specified module '${specifier}' was not found.`;
27
- }
28
- static #optionName(optionName, optionGroup) {
29
- switch (optionGroup) {
30
- case 2:
31
- return `--${optionName}`;
32
- case 4:
33
- return optionName;
34
- }
35
- }
36
- static seen(element) {
37
- return `The ${element} was seen here.`;
38
- }
39
- static testFileMatchCannotStartWith(segment) {
40
- return [
41
- `A test file match pattern cannot start with '${segment}'.`,
42
- "The test files are only collected within the 'rootPath' directory.",
43
- ];
44
- }
45
- static requiresValueType(optionName, optionBrand, optionGroup) {
46
- optionName = ConfigDiagnosticText.#optionName(optionName, optionGroup);
47
- return `Option '${optionName}' requires a value of type ${optionBrand}.`;
48
- }
49
- static unknownOption(optionName) {
50
- return `Unknown option '${optionName}'.`;
51
- }
52
- static versionIsNotSupported(value) {
53
- if (value === "current") {
54
- return "Cannot use 'current' as a target. Failed to resolve the path to the currently installed TypeScript module.";
55
- }
56
- return `TypeScript version '${value}' is not supported.`;
57
- }
58
- static watchCannotBeEnabled() {
59
- return "Watch mode cannot be enabled in continuous integration environment.";
60
- }
61
- }
9
+ import { pathToFileURL, fileURLToPath } from 'node:url';
62
10
 
63
11
  class DiagnosticOrigin {
64
12
  assertion;
@@ -169,49 +117,6 @@ class SourceFile {
169
117
  }
170
118
  }
171
119
 
172
- class EventEmitter {
173
- static instanceCount = 0;
174
- static #handlers = new Map();
175
- static #reporters = new Map();
176
- #scope;
177
- constructor() {
178
- this.#scope = EventEmitter.instanceCount++;
179
- EventEmitter.#handlers.set(this.#scope, new Set());
180
- EventEmitter.#reporters.set(this.#scope, new Set());
181
- }
182
- addHandler(handler) {
183
- EventEmitter.#handlers.get(this.#scope)?.add(handler);
184
- }
185
- addReporter(reporter) {
186
- EventEmitter.#reporters.get(this.#scope)?.add(reporter);
187
- }
188
- static dispatch(event) {
189
- function forEachHandler(handlers, event) {
190
- for (const handler of handlers) {
191
- handler.on(event);
192
- }
193
- }
194
- for (const handlers of EventEmitter.#handlers.values()) {
195
- forEachHandler(handlers, event);
196
- }
197
- for (const handlers of EventEmitter.#reporters.values()) {
198
- forEachHandler(handlers, event);
199
- }
200
- }
201
- removeHandler(handler) {
202
- EventEmitter.#handlers.get(this.#scope)?.delete(handler);
203
- }
204
- removeReporter(reporter) {
205
- EventEmitter.#reporters.get(this.#scope)?.delete(reporter);
206
- }
207
- removeHandlers() {
208
- EventEmitter.#handlers.get(this.#scope)?.clear();
209
- }
210
- removeReporters() {
211
- EventEmitter.#reporters.get(this.#scope)?.clear();
212
- }
213
- }
214
-
215
120
  class Path {
216
121
  static normalizeSlashes;
217
122
  static {
@@ -317,6 +222,49 @@ class Environment {
317
222
 
318
223
  const environmentOptions = Environment.resolve();
319
224
 
225
+ class EventEmitter {
226
+ static instanceCount = 0;
227
+ static #handlers = new Map();
228
+ static #reporters = new Map();
229
+ #scope;
230
+ constructor() {
231
+ this.#scope = EventEmitter.instanceCount++;
232
+ EventEmitter.#handlers.set(this.#scope, new Set());
233
+ EventEmitter.#reporters.set(this.#scope, new Set());
234
+ }
235
+ addHandler(handler) {
236
+ EventEmitter.#handlers.get(this.#scope)?.add(handler);
237
+ }
238
+ addReporter(reporter) {
239
+ EventEmitter.#reporters.get(this.#scope)?.add(reporter);
240
+ }
241
+ static dispatch(event) {
242
+ function forEachHandler(handlers, event) {
243
+ for (const handler of handlers) {
244
+ handler.on(event);
245
+ }
246
+ }
247
+ for (const handlers of EventEmitter.#handlers.values()) {
248
+ forEachHandler(handlers, event);
249
+ }
250
+ for (const handlers of EventEmitter.#reporters.values()) {
251
+ forEachHandler(handlers, event);
252
+ }
253
+ }
254
+ removeHandler(handler) {
255
+ EventEmitter.#handlers.get(this.#scope)?.delete(handler);
256
+ }
257
+ removeReporter(reporter) {
258
+ EventEmitter.#reporters.get(this.#scope)?.delete(reporter);
259
+ }
260
+ removeHandlers() {
261
+ EventEmitter.#handlers.get(this.#scope)?.clear();
262
+ }
263
+ removeReporters() {
264
+ EventEmitter.#reporters.get(this.#scope)?.clear();
265
+ }
266
+ }
267
+
320
268
  class Version {
321
269
  static isGreaterThan(source, target) {
322
270
  return !(source === target) && Version.#satisfies(source, target);
@@ -809,90 +757,73 @@ class Store {
809
757
  }
810
758
  }
811
759
 
812
- class OptionUsageText {
813
- #optionGroup;
814
- constructor(optionGroup) {
815
- this.#optionGroup = optionGroup;
760
+ class ConfigDiagnosticText {
761
+ static expected(element) {
762
+ return `Expected ${element}.`;
763
+ }
764
+ static expectsListItemType(optionName, optionBrand) {
765
+ return `Item of the '${optionName}' list must be of type ${optionBrand}.`;
766
+ }
767
+ static expectsValue(optionName, optionGroup) {
768
+ optionName = ConfigDiagnosticText.#optionName(optionName, optionGroup);
769
+ return `Option '${optionName}' expects a value.`;
770
+ }
771
+ static fileDoesNotExist(filePath) {
772
+ return `The specified path '${filePath}' does not exist.`;
773
+ }
774
+ static moduleWasNotFound(specifier) {
775
+ return `The specified module '${specifier}' was not found.`;
816
776
  }
817
- async get(optionName, optionBrand) {
818
- const usageText = [];
777
+ static #optionName(optionName, optionGroup) {
778
+ return optionGroup === 2 ? `--${optionName}` : optionName;
779
+ }
780
+ static seen(element) {
781
+ return `The ${element} was seen here.`;
782
+ }
783
+ static testFileMatchCannotStartWith(segment) {
784
+ return [
785
+ `A test file match pattern cannot start with '${segment}'.`,
786
+ "The test files are only collected within the 'rootPath' directory.",
787
+ ];
788
+ }
789
+ static requiresValueType(optionName, optionBrand, optionGroup) {
790
+ const optionNameText = ConfigDiagnosticText.#optionName(optionName, optionGroup);
791
+ return `Option '${optionNameText}' requires a value of type ${optionBrand}.`;
792
+ }
793
+ static unknownOption(optionName) {
794
+ return `Unknown option '${optionName}'.`;
795
+ }
796
+ static async usage(optionName, optionBrand, optionGroup) {
797
+ const text = [];
819
798
  switch (optionName) {
820
799
  case "target": {
821
- switch (this.#optionGroup) {
800
+ switch (optionGroup) {
822
801
  case 2:
823
- usageText.push("Value for the '--target' option must be a single tag or a comma separated list.", "Usage examples: '--target 4.9', '--target latest', '--target 4.9,5.3.2,current'.");
802
+ text.push("Value for the '--target' option must be a single tag or a comma separated list.", "Usage examples: '--target 4.9', '--target latest', '--target 4.9,5.3.2,current'.");
824
803
  break;
825
804
  case 4:
826
- usageText.push("Item of the 'target' list must be a supported version tag.");
805
+ text.push("Item of the 'target' list must be a supported version tag.");
827
806
  break;
828
807
  }
829
808
  const supportedTags = await Store.getSupportedTags();
830
809
  if (supportedTags != null) {
831
- usageText.push(`Supported tags: ${["'", supportedTags.join("', '"), "'"].join("")}.`);
810
+ text.push(`Supported tags: ${["'", supportedTags.join("', '"), "'"].join("")}.`);
832
811
  }
833
812
  break;
834
813
  }
835
814
  default:
836
- usageText.push(ConfigDiagnosticText.requiresValueType(optionName, optionBrand, this.#optionGroup));
815
+ text.push(ConfigDiagnosticText.requiresValueType(optionName, optionBrand, optionGroup));
837
816
  }
838
- return usageText;
839
- }
840
- }
841
-
842
- class OptionValidator {
843
- #onDiagnostics;
844
- #optionGroup;
845
- #optionUsageText;
846
- constructor(optionGroup, onDiagnostics) {
847
- this.#optionGroup = optionGroup;
848
- this.#onDiagnostics = onDiagnostics;
849
- this.#optionUsageText = new OptionUsageText(this.#optionGroup);
817
+ return text;
850
818
  }
851
- async check(optionName, optionValue, optionBrand, origin) {
852
- switch (optionName) {
853
- case "tsconfig":
854
- if (["findup", "ignore"].includes(optionValue)) {
855
- break;
856
- }
857
- case "config":
858
- case "rootPath":
859
- if (!existsSync(optionValue)) {
860
- this.#onDiagnostics(Diagnostic.error(ConfigDiagnosticText.fileDoesNotExist(optionValue), origin));
861
- }
862
- break;
863
- case "reporters":
864
- if (["list", "summary"].includes(optionValue)) {
865
- break;
866
- }
867
- case "plugins":
868
- try {
869
- await import(optionValue);
870
- }
871
- catch {
872
- this.#onDiagnostics(Diagnostic.error(ConfigDiagnosticText.moduleWasNotFound(optionValue), origin));
873
- }
874
- break;
875
- case "target":
876
- if ((await Store.validateTag(optionValue)) === false) {
877
- this.#onDiagnostics(Diagnostic.error([
878
- ConfigDiagnosticText.versionIsNotSupported(optionValue),
879
- ...(await this.#optionUsageText.get(optionName, optionBrand)),
880
- ], origin));
881
- }
882
- break;
883
- case "testFileMatch":
884
- for (const segment of ["/", "../"]) {
885
- if (optionValue.startsWith(segment)) {
886
- this.#onDiagnostics(Diagnostic.error(ConfigDiagnosticText.testFileMatchCannotStartWith(segment), origin));
887
- }
888
- }
889
- break;
890
- case "watch":
891
- if (environmentOptions.isCi) {
892
- this.#onDiagnostics(Diagnostic.error(ConfigDiagnosticText.watchCannotBeEnabled(), origin));
893
- }
894
- break;
819
+ static versionIsNotSupported(value) {
820
+ if (value === "current") {
821
+ return "Cannot use 'current' as a target. Failed to resolve the path to the currently installed TypeScript module.";
895
822
  }
823
+ return `TypeScript version '${value}' is not supported.`;
824
+ }
825
+ static watchCannotBeEnabled() {
826
+ return "Watch mode cannot be enabled in continuous integration environment.";
896
827
  }
897
828
  }
898
829
 
@@ -1039,29 +970,89 @@ class Options {
1039
970
  }
1040
971
  return definitionMap;
1041
972
  }
973
+ static resolve(optionName, optionValue, rootPath = ".") {
974
+ switch (optionName) {
975
+ case "tsconfig":
976
+ if (["findup", "ignore"].includes(optionValue)) {
977
+ break;
978
+ }
979
+ case "config":
980
+ case "rootPath":
981
+ optionValue = Path.resolve(rootPath, optionValue);
982
+ break;
983
+ case "plugins":
984
+ case "reporters":
985
+ if (optionValue.startsWith(".")) {
986
+ optionValue = pathToFileURL(Path.join(Path.relative(".", rootPath), optionValue)).toString();
987
+ }
988
+ break;
989
+ }
990
+ return optionValue;
991
+ }
992
+ static async validate(optionDefinition, optionValue, optionGroup, onDiagnostics, origin) {
993
+ switch (optionDefinition.name) {
994
+ case "tsconfig":
995
+ if (["findup", "ignore"].includes(optionValue)) {
996
+ break;
997
+ }
998
+ case "config":
999
+ case "rootPath":
1000
+ if (!existsSync(optionValue)) {
1001
+ onDiagnostics(Diagnostic.error(ConfigDiagnosticText.fileDoesNotExist(optionValue), origin));
1002
+ }
1003
+ break;
1004
+ case "reporters":
1005
+ if (["list", "summary"].includes(optionValue)) {
1006
+ break;
1007
+ }
1008
+ case "plugins":
1009
+ try {
1010
+ await import(optionValue);
1011
+ }
1012
+ catch {
1013
+ onDiagnostics(Diagnostic.error(ConfigDiagnosticText.moduleWasNotFound(optionValue), origin));
1014
+ }
1015
+ break;
1016
+ case "target":
1017
+ if ((await Store.validateTag(optionValue)) === false) {
1018
+ onDiagnostics(Diagnostic.error([
1019
+ ConfigDiagnosticText.versionIsNotSupported(optionValue),
1020
+ await ConfigDiagnosticText.usage(optionDefinition.name, optionDefinition.brand, optionGroup),
1021
+ ].flat(), origin));
1022
+ }
1023
+ break;
1024
+ case "testFileMatch":
1025
+ for (const segment of ["/", "../"]) {
1026
+ if (optionValue.startsWith(segment)) {
1027
+ onDiagnostics(Diagnostic.error(ConfigDiagnosticText.testFileMatchCannotStartWith(segment), origin));
1028
+ }
1029
+ }
1030
+ break;
1031
+ case "watch":
1032
+ if (environmentOptions.isCi) {
1033
+ onDiagnostics(Diagnostic.error(ConfigDiagnosticText.watchCannotBeEnabled(), origin));
1034
+ }
1035
+ break;
1036
+ }
1037
+ }
1042
1038
  }
1043
1039
 
1044
1040
  class CommandLineParser {
1045
1041
  #commandLineOptions;
1046
1042
  #onDiagnostics;
1047
1043
  #options;
1048
- #optionGroup = 2;
1049
- #optionUsageText;
1050
- #optionValidator;
1051
1044
  #pathMatch;
1052
1045
  constructor(commandLine, pathMatch, onDiagnostics) {
1053
1046
  this.#commandLineOptions = commandLine;
1054
1047
  this.#pathMatch = pathMatch;
1055
1048
  this.#onDiagnostics = onDiagnostics;
1056
- this.#options = Options.for(this.#optionGroup);
1057
- this.#optionUsageText = new OptionUsageText(this.#optionGroup);
1058
- this.#optionValidator = new OptionValidator(this.#optionGroup, this.#onDiagnostics);
1049
+ this.#options = Options.for(2);
1059
1050
  }
1060
1051
  async #onExpectsValue(optionDefinition) {
1061
1052
  const text = [
1062
- ConfigDiagnosticText.expectsValue(optionDefinition.name, this.#optionGroup),
1063
- ...(await this.#optionUsageText.get(optionDefinition.name, optionDefinition.brand)),
1064
- ];
1053
+ ConfigDiagnosticText.expectsValue(optionDefinition.name, 2),
1054
+ await ConfigDiagnosticText.usage(optionDefinition.name, optionDefinition.brand, 2),
1055
+ ].flat();
1065
1056
  this.#onDiagnostics(Diagnostic.error(text));
1066
1057
  }
1067
1058
  async parse(commandLineArgs) {
@@ -1092,11 +1083,11 @@ class CommandLineParser {
1092
1083
  let optionValue = this.#resolveOptionValue(commandLineArgs[index]);
1093
1084
  switch (optionDefinition.brand) {
1094
1085
  case "bareTrue":
1095
- await this.#optionValidator.check(optionDefinition.name, optionValue, optionDefinition.brand);
1086
+ await Options.validate(optionDefinition, optionValue, 2, this.#onDiagnostics);
1096
1087
  this.#commandLineOptions[optionDefinition.name] = true;
1097
1088
  break;
1098
1089
  case "boolean":
1099
- await this.#optionValidator.check(optionDefinition.name, optionValue, optionDefinition.brand);
1090
+ await Options.validate(optionDefinition, optionValue, 2, this.#onDiagnostics);
1100
1091
  this.#commandLineOptions[optionDefinition.name] = optionValue !== "false";
1101
1092
  if (optionValue === "false" || optionValue === "true") {
1102
1093
  index++;
@@ -1104,20 +1095,13 @@ class CommandLineParser {
1104
1095
  break;
1105
1096
  case "list":
1106
1097
  if (optionValue !== "") {
1107
- let optionValues = optionValue
1098
+ const optionValues = optionValue
1108
1099
  .split(",")
1109
1100
  .map((value) => value.trim())
1110
- .filter((value) => value !== "");
1111
- if (optionDefinition.name === "plugins" || optionDefinition.name === "reporters") {
1112
- optionValues = optionValues.map((optionValue) => {
1113
- if (optionValue.startsWith(".")) {
1114
- return pathToFileURL(optionValue).toString();
1115
- }
1116
- return optionValue;
1117
- });
1118
- }
1101
+ .filter((value) => value !== "")
1102
+ .map((value) => Options.resolve(optionDefinition.name, value));
1119
1103
  for (const optionValue of optionValues) {
1120
- await this.#optionValidator.check(optionDefinition.name, optionValue, optionDefinition.brand);
1104
+ await Options.validate(optionDefinition, optionValue, 2, this.#onDiagnostics);
1121
1105
  }
1122
1106
  this.#commandLineOptions[optionDefinition.name] = optionValues;
1123
1107
  index++;
@@ -1127,11 +1111,8 @@ class CommandLineParser {
1127
1111
  break;
1128
1112
  case "string":
1129
1113
  if (optionValue !== "") {
1130
- if (optionDefinition.name === "config" ||
1131
- (optionDefinition.name === "tsconfig" && !["findup", "ignore"].includes(optionValue))) {
1132
- optionValue = Path.resolve(optionValue);
1133
- }
1134
- await this.#optionValidator.check(optionDefinition.name, optionValue, optionDefinition.brand);
1114
+ optionValue = Options.resolve(optionDefinition.name, optionValue);
1115
+ await Options.validate(optionDefinition, optionValue, 2, this.#onDiagnostics);
1135
1116
  this.#commandLineOptions[optionDefinition.name] = optionValue;
1136
1117
  index++;
1137
1118
  break;
@@ -1279,21 +1260,18 @@ class ConfigFileParser {
1279
1260
  #jsonScanner;
1280
1261
  #onDiagnostics;
1281
1262
  #options;
1282
- #optionGroup = 4;
1283
- #optionValidator;
1284
1263
  #sourceFile;
1285
1264
  constructor(configFileOptions, sourceFile, onDiagnostics) {
1286
1265
  this.#configFileOptions = configFileOptions;
1287
1266
  this.#sourceFile = sourceFile;
1288
1267
  this.#onDiagnostics = onDiagnostics;
1289
- this.#options = Options.for(this.#optionGroup);
1268
+ this.#options = Options.for(4);
1290
1269
  this.#jsonScanner = new JsonScanner(this.#sourceFile);
1291
- this.#optionValidator = new OptionValidator(this.#optionGroup, this.#onDiagnostics);
1292
1270
  }
1293
1271
  #onRequiresValue(optionDefinition, jsonNode, isListItem) {
1294
1272
  const text = isListItem
1295
1273
  ? ConfigDiagnosticText.expectsListItemType(optionDefinition.name, optionDefinition.brand)
1296
- : ConfigDiagnosticText.requiresValueType(optionDefinition.name, optionDefinition.brand, this.#optionGroup);
1274
+ : ConfigDiagnosticText.requiresValueType(optionDefinition.name, optionDefinition.brand, 4);
1297
1275
  this.#onDiagnostics(Diagnostic.error(text, jsonNode.origin));
1298
1276
  }
1299
1277
  async #parseValue(optionDefinition, isListItem = false) {
@@ -1316,17 +1294,9 @@ class ConfigFileParser {
1316
1294
  this.#onRequiresValue(optionDefinition, jsonNode, isListItem);
1317
1295
  break;
1318
1296
  }
1319
- if (optionDefinition.name === "plugins" || optionDefinition.name === "reporters") {
1320
- if (optionValue.startsWith(".")) {
1321
- const configFilePath = Path.relative(".", Path.dirname(this.#sourceFile.fileName));
1322
- optionValue = pathToFileURL(Path.join(configFilePath, optionValue)).toString();
1323
- }
1324
- }
1325
- if (optionDefinition.name === "rootPath" ||
1326
- (optionDefinition.name === "tsconfig" && !["findup", "ignore"].includes(optionValue))) {
1327
- optionValue = Path.resolve(Path.dirname(this.#sourceFile.fileName), optionValue);
1328
- }
1329
- await this.#optionValidator.check(optionDefinition.name, optionValue, optionDefinition.brand, jsonNode.origin);
1297
+ const rootPath = Path.dirname(this.#sourceFile.fileName);
1298
+ optionValue = Options.resolve(optionDefinition.name, optionValue, rootPath);
1299
+ await Options.validate(optionDefinition, optionValue, 4, this.#onDiagnostics, jsonNode.origin);
1330
1300
  break;
1331
1301
  }
1332
1302
  case "list": {
@@ -1492,6 +1462,7 @@ var OptionGroup;
1492
1462
  (function (OptionGroup) {
1493
1463
  OptionGroup[OptionGroup["CommandLine"] = 2] = "CommandLine";
1494
1464
  OptionGroup[OptionGroup["ConfigFile"] = 4] = "ConfigFile";
1465
+ OptionGroup[OptionGroup["ResolvedConfig"] = 6] = "ResolvedConfig";
1495
1466
  })(OptionGroup || (OptionGroup = {}));
1496
1467
 
1497
1468
  class CancellationHandler {
@@ -1837,25 +1808,6 @@ class ResultHandler {
1837
1808
  }
1838
1809
  }
1839
1810
 
1840
- class HooksService {
1841
- static #handlers = new Set();
1842
- static addHandler(hooks) {
1843
- HooksService.#handlers.add(hooks);
1844
- }
1845
- static async call(hook, options) {
1846
- for (const handler of HooksService.#handlers) {
1847
- const result = await handler[hook]?.(options);
1848
- if (result != null) {
1849
- options = result;
1850
- }
1851
- }
1852
- return options;
1853
- }
1854
- static removeHandlers() {
1855
- HooksService.#handlers.clear();
1856
- }
1857
- }
1858
-
1859
1811
  function jsx(type, props) {
1860
1812
  return { props, type };
1861
1813
  }
@@ -2237,6 +2189,25 @@ function watchUsageText() {
2237
2189
  return jsx(Text, { children: usageText });
2238
2190
  }
2239
2191
 
2192
+ class PluginService {
2193
+ static #handlers = new Map();
2194
+ static addHandler(plugin) {
2195
+ PluginService.#handlers.set(plugin.name, plugin);
2196
+ }
2197
+ static async call(hook, argument, context) {
2198
+ let result = argument;
2199
+ for (const [, plugin] of PluginService.#handlers) {
2200
+ if (hook in plugin) {
2201
+ result = await plugin[hook].call(context, result);
2202
+ }
2203
+ }
2204
+ return result;
2205
+ }
2206
+ static removeHandlers() {
2207
+ PluginService.#handlers.clear();
2208
+ }
2209
+ }
2210
+
2240
2211
  class BaseReporter {
2241
2212
  resolvedConfig;
2242
2213
  constructor(resolvedConfig) {
@@ -3781,7 +3752,7 @@ class ToRaiseError {
3781
3752
  if (this.#compiler.isStringLiteralLike(targetNode)) {
3782
3753
  return this.#compiler.flattenDiagnosticMessageText(diagnostic.messageText, " ", 0).includes(targetNode.text);
3783
3754
  }
3784
- return Number.parseInt(targetNode.text) === diagnostic.code;
3755
+ return Number.parseInt(targetNode.text, 10) === diagnostic.code;
3785
3756
  }
3786
3757
  }
3787
3758
 
@@ -4126,7 +4097,7 @@ class TaskRunner {
4126
4097
  class Runner {
4127
4098
  #eventEmitter = new EventEmitter();
4128
4099
  #resolvedConfig;
4129
- static version = "3.0.0-beta.5";
4100
+ static version = "3.0.0-beta.6";
4130
4101
  constructor(resolvedConfig) {
4131
4102
  this.#resolvedConfig = resolvedConfig;
4132
4103
  }
@@ -4250,11 +4221,11 @@ class Cli {
4250
4221
  }
4251
4222
  continue;
4252
4223
  }
4253
- for (const plugin of resolvedConfig.plugins) {
4254
- const hooks = (await import(plugin)).default;
4255
- HooksService.addHandler(hooks);
4224
+ for (const pluginIdentifier of resolvedConfig.plugins) {
4225
+ const plugin = (await import(pluginIdentifier)).default;
4226
+ PluginService.addHandler(plugin);
4256
4227
  }
4257
- resolvedConfig = await HooksService.call("config", resolvedConfig);
4228
+ resolvedConfig = await PluginService.call("config", resolvedConfig, {});
4258
4229
  if (commandLine.includes("--showConfig")) {
4259
4230
  OutputService.writeMessage(formattedText({ ...resolvedConfig, ...environmentOptions }));
4260
4231
  continue;
@@ -4275,7 +4246,7 @@ class Cli {
4275
4246
  continue;
4276
4247
  }
4277
4248
  }
4278
- testFiles = await HooksService.call("select", testFiles);
4249
+ testFiles = await PluginService.call("select", testFiles, { resolvedConfig });
4279
4250
  if (commandLine.includes("--listFiles")) {
4280
4251
  OutputService.writeMessage(formattedText(testFiles.map((testFile) => testFile.toString())));
4281
4252
  continue;
@@ -4284,7 +4255,7 @@ class Cli {
4284
4255
  this.#eventEmitter.removeReporter(setupReporter);
4285
4256
  const runner = new Runner(resolvedConfig);
4286
4257
  await runner.run(testFiles, cancellationToken);
4287
- HooksService.removeHandlers();
4258
+ PluginService.removeHandlers();
4288
4259
  } while (cancellationToken.reason === "configChange");
4289
4260
  this.#eventEmitter.removeHandlers();
4290
4261
  }
@@ -4316,4 +4287,4 @@ class Cli {
4316
4287
  }
4317
4288
  }
4318
4289
 
4319
- export { Assertion, BaseReporter, CancellationHandler, CancellationReason, CancellationToken, Cli, CollectService, Color, Config, ConfigDiagnosticText, DescribeResult, Diagnostic, DiagnosticCategory, DiagnosticOrigin, EventEmitter, ExitCodeHandler, ExpectResult, ExpectService, FileWatcher, HooksService, InputService, Line, ListReporter, OptionBrand, OptionGroup, Options, OutputService, Path, ProjectResult, ProjectService, Result, ResultCount, ResultHandler, ResultStatus, ResultTiming, Runner, Scribbler, Select, SelectDiagnosticText, SetupReporter, SourceFile, Store, SummaryReporter, TargetResult, Task, TaskResult, TestMember, TestMemberBrand, TestMemberFlags, TestResult, TestTree, Text, Version, WatchReporter, WatchService, Watcher, addsPackageText, defaultOptions, describeNameText, diagnosticText, environmentOptions, fileViewText, formattedText, helpText, summaryText, taskStatusText, testNameText, usesCompilerText, waitingForFileChangesText, watchUsageText };
4290
+ export { Assertion, BaseReporter, CancellationHandler, CancellationReason, CancellationToken, Cli, CollectService, Color, Config, ConfigDiagnosticText, DescribeResult, Diagnostic, DiagnosticCategory, DiagnosticOrigin, EventEmitter, ExitCodeHandler, ExpectResult, ExpectService, FileWatcher, InputService, Line, ListReporter, OptionBrand, OptionGroup, Options, OutputService, Path, PluginService, ProjectResult, ProjectService, Result, ResultCount, ResultHandler, ResultStatus, ResultTiming, Runner, Scribbler, Select, SelectDiagnosticText, SetupReporter, SourceFile, Store, SummaryReporter, TargetResult, Task, TaskResult, TestMember, TestMemberBrand, TestMemberFlags, TestResult, TestTree, Text, Version, WatchReporter, WatchService, Watcher, addsPackageText, defaultOptions, describeNameText, diagnosticText, environmentOptions, fileViewText, formattedText, helpText, summaryText, taskStatusText, testNameText, usesCompilerText, waitingForFileChangesText, watchUsageText };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tstyche",
3
- "version": "3.0.0-beta.5",
3
+ "version": "3.0.0-beta.6",
4
4
  "description": "The Essential Type Testing Tool.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -63,14 +63,14 @@
63
63
  "devDependencies": {
64
64
  "@biomejs/biome": "1.9.4",
65
65
  "@rollup/plugin-typescript": "12.1.1",
66
- "@types/node": "20.16.14",
67
- "@types/react": "18.3.11",
66
+ "@types/node": "20.17.1",
67
+ "@types/react": "18.3.12",
68
68
  "ajv": "8.17.1",
69
69
  "cspell": "8.15.4",
70
70
  "magic-string": "0.30.12",
71
71
  "monocart-coverage-reports": "2.11.1",
72
72
  "pretty-ansi": "2.0.0",
73
- "rollup": "4.24.0",
73
+ "rollup": "4.24.2",
74
74
  "rollup-plugin-dts": "6.1.1",
75
75
  "tslib": "2.8.0",
76
76
  "typescript": "5.6.3"