tstyche 1.0.0-beta.3 → 1.0.0-beta.5

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/CHANGELOG.md ADDED
@@ -0,0 +1,63 @@
1
+ # Changelog
2
+
3
+ ## [1.0.0-beta.5] - 2023-11-27
4
+
5
+ ### Changed
6
+
7
+ - **Breaking:** Move retry logic to the `Lock` class ([#31](https://github.com/tstyche/tstyche/pull/31))
8
+ - Bring back support for Node.js 16 ([#28](https://github.com/tstyche/tstyche/pull/28), [#27](https://github.com/tstyche/tstyche/pull/27))
9
+
10
+ ### Added
11
+
12
+ - Add support for the `current` target tag ([#33](https://github.com/tstyche/tstyche/pull/33))
13
+
14
+ ### Fixed
15
+
16
+ - Allow `.raiseError()` to take template literals as arguments. ([#35](https://github.com/tstyche/tstyche/pull/35))
17
+
18
+ ## [1.0.0-beta.4] - 2023-11-24
19
+
20
+ ### Added
21
+
22
+ - Use Node.js Fetch API ([#23](https://github.com/tstyche/tstyche/pull/23))
23
+
24
+ ### Removed
25
+
26
+ - **Breaking:** Remove the `context()` helper ([#24](https://github.com/tstyche/tstyche/pull/24))
27
+ - **Breaking:** Drop support for Node.js 16 ([#22](https://github.com/tstyche/tstyche/pull/22))
28
+ - **Breaking:** Rename methods of the `StoreService` class ([`5d74201`](https://github.com/tstyche/tstyche/commit/5d74201))
29
+
30
+ ### Fixed
31
+
32
+ - Tune up behavior of `.skip` and `.only` run mode flags ([#25](https://github.com/tstyche/tstyche/pull/25))
33
+ - Clean up error messages of primitive type matchers ([#21](https://github.com/tstyche/tstyche/pull/21))
34
+ - Normalize `installationPath` path output ([#19](https://github.com/tstyche/tstyche/pull/19))
35
+
36
+ ## [1.0.0-beta.3] - 2023-11-13
37
+
38
+ ### Fixed
39
+
40
+ - Support TypeScript's 'node10' and 'node16' resolutions ([`7dd805a`](https://github.com/tstyche/tstyche/commit/7dd805a), [`9c83e79`](https://github.com/tstyche/tstyche/commit/9c83e79))
41
+
42
+ ## [1.0.0-beta.2] - 2023-11-12
43
+
44
+ ### Fixed
45
+
46
+ - Support TypeScript's 'node10' resolution ([#7](https://github.com/tstyche/tstyche/pull/7))
47
+
48
+ ## [1.0.0-beta.1] - 2023-11-09
49
+
50
+ ### Fixed
51
+
52
+ - Include 'cjs' files in the published package ([`90b6473`](https://github.com/tstyche/tstyche/commit/90b6473))
53
+
54
+ ## [1.0.0-beta.0] - 2023-11-09
55
+
56
+ _First pre-release._
57
+
58
+ [1.0.0-beta.5]: https://github.com/tstyche/tstyche/releases/tag/v1.0.0-beta.5
59
+ [1.0.0-beta.4]: https://github.com/tstyche/tstyche/releases/tag/v1.0.0-beta.4
60
+ [1.0.0-beta.3]: https://github.com/tstyche/tstyche/releases/tag/v1.0.0-beta.3
61
+ [1.0.0-beta.2]: https://github.com/tstyche/tstyche/releases/tag/v1.0.0-beta.2
62
+ [1.0.0-beta.1]: https://github.com/tstyche/tstyche/releases/tag/v1.0.0-beta.1
63
+ [1.0.0-beta.0]: https://github.com/tstyche/tstyche/releases/tag/v1.0.0-beta.0
package/README.md CHANGED
@@ -31,7 +31,7 @@ test("firstItem", () => {
31
31
 
32
32
  To organize, debug and plan tests TSTyche has:
33
33
 
34
- - `test()`, `it()`, `describe()` and `context()` helpers;
34
+ - `test()`, `it()` and `describe()` helpers,
35
35
  - with `.only`, `.skip` and `.todo` run mode flags.
36
36
 
37
37
  ## Assertions
@@ -39,18 +39,18 @@ To organize, debug and plan tests TSTyche has:
39
39
  The assertions can be used to write type tests (like in the above example) or mixed in your functional tests:
40
40
 
41
41
  ```ts
42
+ import assert from "node:assert/strict";
43
+ import { test } from "node:test";
42
44
  import * as tstyche from "tstyche";
43
45
 
44
46
  function secondItem<T>(target: Array<T>): T | undefined {
45
47
  return target[1];
46
48
  }
47
49
 
48
- describe("secondItem", () => {
49
- it("should handle numbers", () => {
50
- expect(secondItem([1, 2, 3])).toBe(1);
50
+ test("handles numbers", () => {
51
+ assert.strictEqual(secondItem([1, 2, 3]), 2);
51
52
 
52
- tstyche.expect(secondItem([1, 2, 3])).type.toEqual<number | undefined>();
53
- });
53
+ tstyche.expect(secondItem([1, 2, 3])).type.toEqual<number | undefined>();
54
54
  });
55
55
  ```
56
56
 
package/build/index.cjs CHANGED
@@ -11,7 +11,6 @@ const noopChain = new Proxy(doNothing, {
11
11
  },
12
12
  });
13
13
 
14
- exports.context = noopChain;
15
14
  exports.describe = noopChain;
16
15
  exports.expect = noopChain;
17
16
  exports.it = noopChain;
package/build/index.d.cts CHANGED
@@ -60,7 +60,7 @@ interface Test {
60
60
  }
61
61
  interface Matchers {
62
62
  /**
63
- * Checks if the source type is `any`.
63
+ * Checks if the `any` type is identical to the source type.
64
64
  */
65
65
  toBeAny: () => void;
66
66
  /**
@@ -77,47 +77,47 @@ interface Matchers {
77
77
  (target: unknown): void;
78
78
  };
79
79
  /**
80
- * Checks if the source type is `bigint`.
80
+ * Checks if the `bigint` type is identical to the source type.
81
81
  */
82
82
  toBeBigInt: () => void;
83
83
  /**
84
- * Checks if the source type is `boolean`.
84
+ * Checks if the `boolean` type is identical to the source type.
85
85
  */
86
86
  toBeBoolean: () => void;
87
87
  /**
88
- * Checks if the source type is `never`.
88
+ * Checks if the `never` type is identical to the source type.
89
89
  */
90
90
  toBeNever: () => void;
91
91
  /**
92
- * Checks if the source type is `null`.
92
+ * Checks if the `null` type is identical to the source type.
93
93
  */
94
94
  toBeNull: () => void;
95
95
  /**
96
- * Checks if the source type is `number`.
96
+ * Checks if the `number` type is identical to the source type.
97
97
  */
98
98
  toBeNumber: () => void;
99
99
  /**
100
- * Checks if the source type is `string`.
100
+ * Checks if the `string` type is identical to the source type.
101
101
  */
102
102
  toBeString: () => void;
103
103
  /**
104
- * Checks if the source type is `symbol`.
104
+ * Checks if the `symbol` type is identical to the source type.
105
105
  */
106
106
  toBeSymbol: () => void;
107
107
  /**
108
- * Checks if the source type is `undefined`.
108
+ * Checks if the `undefined` type is identical to the source type.
109
109
  */
110
110
  toBeUndefined: () => void;
111
111
  /**
112
- * Checks if the source type is `unique symbol`.
112
+ * Checks if the `unique symbol` type is identical to the source type.
113
113
  */
114
114
  toBeUniqueSymbol: () => void;
115
115
  /**
116
- * Checks if the source type is `unknown`.
116
+ * Checks if the `unknown` type is identical to the source type.
117
117
  */
118
118
  toBeUnknown: () => void;
119
119
  /**
120
- * Checks if the source type is `void`.
120
+ * Checks if the `void` type is identical to the source type.
121
121
  */
122
122
  toBeVoid: () => void;
123
123
  /**
@@ -256,10 +256,6 @@ interface Expect {
256
256
  * Defines a test group.
257
257
  */
258
258
  declare const describe: Describe;
259
- /**
260
- * Defines a test group.
261
- */
262
- declare const context: Describe;
263
259
  /**
264
260
  * Defines a single test.
265
261
  */
@@ -273,4 +269,4 @@ declare const it: Test;
273
269
  */
274
270
  declare const expect: Expect;
275
271
 
276
- export { context, describe, expect, it, test };
272
+ export { describe, expect, it, test };
package/build/index.d.ts CHANGED
@@ -60,7 +60,7 @@ interface Test {
60
60
  }
61
61
  interface Matchers {
62
62
  /**
63
- * Checks if the source type is `any`.
63
+ * Checks if the `any` type is identical to the source type.
64
64
  */
65
65
  toBeAny: () => void;
66
66
  /**
@@ -77,47 +77,47 @@ interface Matchers {
77
77
  (target: unknown): void;
78
78
  };
79
79
  /**
80
- * Checks if the source type is `bigint`.
80
+ * Checks if the `bigint` type is identical to the source type.
81
81
  */
82
82
  toBeBigInt: () => void;
83
83
  /**
84
- * Checks if the source type is `boolean`.
84
+ * Checks if the `boolean` type is identical to the source type.
85
85
  */
86
86
  toBeBoolean: () => void;
87
87
  /**
88
- * Checks if the source type is `never`.
88
+ * Checks if the `never` type is identical to the source type.
89
89
  */
90
90
  toBeNever: () => void;
91
91
  /**
92
- * Checks if the source type is `null`.
92
+ * Checks if the `null` type is identical to the source type.
93
93
  */
94
94
  toBeNull: () => void;
95
95
  /**
96
- * Checks if the source type is `number`.
96
+ * Checks if the `number` type is identical to the source type.
97
97
  */
98
98
  toBeNumber: () => void;
99
99
  /**
100
- * Checks if the source type is `string`.
100
+ * Checks if the `string` type is identical to the source type.
101
101
  */
102
102
  toBeString: () => void;
103
103
  /**
104
- * Checks if the source type is `symbol`.
104
+ * Checks if the `symbol` type is identical to the source type.
105
105
  */
106
106
  toBeSymbol: () => void;
107
107
  /**
108
- * Checks if the source type is `undefined`.
108
+ * Checks if the `undefined` type is identical to the source type.
109
109
  */
110
110
  toBeUndefined: () => void;
111
111
  /**
112
- * Checks if the source type is `unique symbol`.
112
+ * Checks if the `unique symbol` type is identical to the source type.
113
113
  */
114
114
  toBeUniqueSymbol: () => void;
115
115
  /**
116
- * Checks if the source type is `unknown`.
116
+ * Checks if the `unknown` type is identical to the source type.
117
117
  */
118
118
  toBeUnknown: () => void;
119
119
  /**
120
- * Checks if the source type is `void`.
120
+ * Checks if the `void` type is identical to the source type.
121
121
  */
122
122
  toBeVoid: () => void;
123
123
  /**
@@ -256,10 +256,6 @@ interface Expect {
256
256
  * Defines a test group.
257
257
  */
258
258
  declare const describe: Describe;
259
- /**
260
- * Defines a test group.
261
- */
262
- declare const context: Describe;
263
259
  /**
264
260
  * Defines a single test.
265
261
  */
@@ -273,4 +269,4 @@ declare const it: Test;
273
269
  */
274
270
  declare const expect: Expect;
275
271
 
276
- export { context, describe, expect, it, test };
272
+ export { describe, expect, it, test };
package/build/index.js CHANGED
@@ -9,4 +9,4 @@ const noopChain = new Proxy(doNothing, {
9
9
  },
10
10
  });
11
11
 
12
- export { noopChain as context, noopChain as describe, noopChain as expect, noopChain as it, noopChain as test };
12
+ export { noopChain as describe, noopChain as expect, noopChain as it, noopChain as test };
@@ -35,10 +35,10 @@ declare class Diagnostic {
35
35
  declare class StoreService {
36
36
  #private;
37
37
  constructor();
38
- getSupportedTags(): Array<string>;
39
- loadCompilerModule(tag: string, signal?: AbortSignal): Promise<typeof ts | undefined>;
38
+ get supportedTags(): Array<string>;
39
+ install(tag: string, signal?: AbortSignal): Promise<string | undefined>;
40
+ load(tag: string, signal?: AbortSignal): Promise<typeof ts | undefined>;
40
41
  open(signal?: AbortSignal): Promise<void>;
41
- prepareCompilerModule(tag: string, signal?: AbortSignal): Promise<string | undefined>;
42
42
  prune(): Promise<void>;
43
43
  resolveTag(tag: string): string | undefined;
44
44
  update(signal?: AbortSignal): Promise<void>;
package/build/tstyche.js CHANGED
@@ -5,7 +5,6 @@ import path from 'node:path';
5
5
  import fs from 'node:fs/promises';
6
6
  import { createRequire } from 'node:module';
7
7
  import { spawn } from 'node:child_process';
8
- import { setInterval } from 'node:timers/promises';
9
8
  import https from 'node:https';
10
9
 
11
10
  class EventEmitter {
@@ -1387,7 +1386,6 @@ class IdentifierLookup {
1387
1386
  this.compiler = compiler;
1388
1387
  this.#identifiers = identifiers ?? {
1389
1388
  namedImports: {
1390
- context: undefined,
1391
1389
  describe: undefined,
1392
1390
  expect: undefined,
1393
1391
  it: undefined,
@@ -1464,7 +1462,6 @@ class IdentifierLookup {
1464
1462
  return;
1465
1463
  }
1466
1464
  switch (identifierName) {
1467
- case "context":
1468
1465
  case "describe":
1469
1466
  return { brand: "describe", flags };
1470
1467
  case "it":
@@ -1621,7 +1618,7 @@ class ProjectService {
1621
1618
  startGroup: doNothing,
1622
1619
  };
1623
1620
  const host = {
1624
- ...compiler.sys,
1621
+ ...this.compiler.sys,
1625
1622
  clearImmediate,
1626
1623
  clearTimeout,
1627
1624
  setImmediate,
@@ -1686,7 +1683,9 @@ class Checker {
1686
1683
  this.#assertNonNullish(assertion.typeChecker, "The 'typeChecker' was not provided.");
1687
1684
  }
1688
1685
  #assertStringsOrNumbers(nodes) {
1689
- return nodes.every((expression) => this.compiler.isStringLiteral(expression) || this.compiler.isNumericLiteral(expression));
1686
+ return nodes.every((expression) => this.compiler.isStringLiteral(expression) ||
1687
+ this.compiler.isNumericLiteral(expression) ||
1688
+ this.compiler.isNoSubstitutionTemplateLiteral(expression));
1690
1689
  }
1691
1690
  explain(assertion) {
1692
1691
  this.#assertNonNullishTypeChecker(assertion);
@@ -1838,7 +1837,9 @@ class Checker {
1838
1837
  };
1839
1838
  const sourceText = assertion.typeChecker.typeToString(assertion.sourceType.type);
1840
1839
  return [
1841
- Diagnostic.error(assertion.isNot ? `Type '${sourceText}' is '${targetText}'.` : `Type '${sourceText}' is not '${targetText}'.`, origin),
1840
+ Diagnostic.error(assertion.isNot
1841
+ ? `Type '${targetText}' is identical to type '${sourceText}'.`
1842
+ : `Type '${targetText}' is not identical to type '${sourceText}'.`, origin),
1842
1843
  ];
1843
1844
  }
1844
1845
  match(assertion) {
@@ -2057,7 +2058,8 @@ class TestTreeWorker {
2057
2058
  const describeResult = new DescribeResult(describe, parentResult);
2058
2059
  EventEmitter.dispatch(["describe:start", { result: describeResult }]);
2059
2060
  runMode = this.#resolveRunMode(runMode, describe);
2060
- if (!(runMode & 4 || runMode & 8) && describe.diagnostics.length > 0) {
2061
+ if (!(runMode & 4 || (this.#hasOnly && !(runMode & 2)) || runMode & 8) &&
2062
+ describe.diagnostics.length > 0) {
2061
2063
  EventEmitter.dispatch([
2062
2064
  "file:error",
2063
2065
  {
@@ -2079,11 +2081,7 @@ class TestTreeWorker {
2079
2081
  EventEmitter.dispatch(["test:todo", { result: testResult }]);
2080
2082
  return;
2081
2083
  }
2082
- if (runMode & 4) {
2083
- EventEmitter.dispatch(["test:skip", { result: testResult }]);
2084
- return;
2085
- }
2086
- if (test.diagnostics.length > 0) {
2084
+ if (!(runMode & 4 || (this.#hasOnly && !(runMode & 2))) && test.diagnostics.length > 0) {
2087
2085
  EventEmitter.dispatch([
2088
2086
  "test:error",
2089
2087
  {
@@ -2094,7 +2092,7 @@ class TestTreeWorker {
2094
2092
  return;
2095
2093
  }
2096
2094
  this.visit(test.members, runMode, testResult);
2097
- if (testResult.expectCount.skipped > 0 && testResult.expectCount.skipped === testResult.expectCount.total) {
2095
+ if (runMode & 4 || (this.#hasOnly && !(runMode & 2))) {
2098
2096
  EventEmitter.dispatch(["test:skip", { result: testResult }]);
2099
2097
  return;
2100
2098
  }
@@ -2196,7 +2194,7 @@ class TaskRunner {
2196
2194
  for (const versionTag of target) {
2197
2195
  const targetResult = new TargetResult(versionTag, testFiles);
2198
2196
  EventEmitter.dispatch(["target:start", { result: targetResult }]);
2199
- const compiler = await this.#storeService.loadCompilerModule(versionTag, signal);
2197
+ const compiler = await this.#storeService.load(versionTag, signal);
2200
2198
  if (compiler) {
2201
2199
  const testFileRunner = new TestFileRunner(this.resolvedConfig, compiler);
2202
2200
  for (const testFile of testFiles) {
@@ -2337,7 +2335,7 @@ class OptionDefinitionsMap {
2337
2335
  items: {
2338
2336
  brand: "string",
2339
2337
  name: "target",
2340
- pattern: "^([45]\\.[0-9](\\.[0-9])?)|beta|latest|next|rc$",
2338
+ pattern: "^([45]\\.[0-9](\\.[0-9])?)|beta|current|latest|next|rc$",
2341
2339
  },
2342
2340
  name: "target",
2343
2341
  },
@@ -2429,11 +2427,11 @@ class OptionUsageText {
2429
2427
  const usageText = [];
2430
2428
  switch (optionName) {
2431
2429
  case "target": {
2432
- const supportedTags = this.#storeService.getSupportedTags();
2430
+ const { supportedTags } = this.#storeService;
2433
2431
  const supportedTagsText = `Supported tags: ${["'", supportedTags.join("', '"), "'"].join("")}.`;
2434
2432
  switch (this.#optionGroup) {
2435
2433
  case 2:
2436
- usageText.push("Argument for the '--target' option must be a single tag or a comma separated list of versions.", "Usage examples: '--target 4.9', '--target 5.0.4', '--target 4.7,4.8,latest'.", supportedTagsText);
2434
+ usageText.push("Argument for the '--target' option must be a single tag or a comma separated list.", "Usage examples: '--target 4.9', '--target 5.0.4', '--target 4.7,4.8,latest'.", supportedTagsText);
2437
2435
  break;
2438
2436
  case 4:
2439
2437
  usageText.push("Item of the 'target' list must be a supported version tag.", supportedTagsText);
@@ -2923,58 +2921,85 @@ class Lock {
2923
2921
  #lockFilePath;
2924
2922
  static #lockSuffix = "__lock__";
2925
2923
  constructor(targetPath) {
2926
- this.#lockFilePath = Lock.getLockFilePath(targetPath);
2924
+ this.#lockFilePath = Lock.#getLockFilePath(targetPath);
2927
2925
  writeFileSync(this.#lockFilePath, "");
2928
2926
  process.on("exit", () => {
2929
2927
  this.release();
2930
2928
  });
2931
2929
  }
2932
- static getLockFilePath(targetPath) {
2930
+ static #getLockFilePath(targetPath) {
2933
2931
  return `${targetPath}${Lock.#lockSuffix}`;
2934
2932
  }
2935
- static isLocked(targetPath) {
2936
- return existsSync(Lock.getLockFilePath(targetPath));
2933
+ static async isLocked(targetPath, options) {
2934
+ let isLocked = existsSync(Lock.#getLockFilePath(targetPath));
2935
+ if (!isLocked) {
2936
+ return isLocked;
2937
+ }
2938
+ if (options?.timeout == null) {
2939
+ return isLocked;
2940
+ }
2941
+ const waitStartTime = Date.now();
2942
+ while (isLocked) {
2943
+ if (options.signal?.aborted === true) {
2944
+ break;
2945
+ }
2946
+ if (Date.now() - waitStartTime > options.timeout) {
2947
+ options.onDiagnostic?.(`Lock wait timeout of ${options.timeout / 1000}s was exceeded.`);
2948
+ break;
2949
+ }
2950
+ await Lock.#sleep(1000);
2951
+ isLocked = existsSync(Lock.#getLockFilePath(targetPath));
2952
+ }
2953
+ return isLocked;
2937
2954
  }
2938
2955
  release() {
2939
2956
  rmSync(this.#lockFilePath, { force: true });
2940
2957
  }
2958
+ static async #sleep(time) {
2959
+ return new Promise((resolve) => setTimeout(resolve, time));
2960
+ }
2941
2961
  }
2942
2962
 
2943
2963
  class CompilerModuleWorker {
2944
2964
  #cachePath;
2965
+ #onDiagnostic;
2945
2966
  #readyFileName = "__ready__";
2946
2967
  #timeout = Environment.timeout * 1000;
2947
- constructor(cachePath) {
2968
+ constructor(cachePath, onDiagnostic) {
2948
2969
  this.#cachePath = cachePath;
2970
+ this.#onDiagnostic = onDiagnostic;
2949
2971
  }
2950
2972
  async ensure(compilerVersion, signal) {
2951
2973
  const installationPath = path.join(this.#cachePath, compilerVersion);
2952
2974
  const readyFilePath = path.join(installationPath, this.#readyFileName);
2953
2975
  const tsserverFilePath = path.join(installationPath, "node_modules", "typescript", "lib", "tsserverlibrary.js");
2954
2976
  const typescriptFilePath = path.join(installationPath, "node_modules", "typescript", "lib", "typescript.js");
2955
- if (Lock.isLocked(installationPath)) {
2956
- for await (const now of setInterval(1000, Date.now(), { signal })) {
2957
- const startTime = Date.now();
2958
- if (!Lock.isLocked(installationPath)) {
2959
- break;
2960
- }
2961
- if (startTime - now > this.#timeout) {
2962
- throw new Error(`Lock wait timeout of ${this.#timeout / 1000}s was exceeded.`);
2963
- }
2964
- }
2977
+ if (await Lock.isLocked(installationPath, {
2978
+ onDiagnostic: (text) => {
2979
+ this.#onDiagnostic(Diagnostic.error([`Failed to install 'typescript@${compilerVersion}'.`, text]));
2980
+ },
2981
+ signal,
2982
+ timeout: this.#timeout,
2983
+ })) {
2984
+ return;
2965
2985
  }
2966
2986
  if (existsSync(readyFilePath)) {
2967
2987
  return tsserverFilePath;
2968
2988
  }
2969
- EventEmitter.dispatch(["store:info", { compilerVersion, installationPath }]);
2970
- await fs.mkdir(installationPath, { recursive: true });
2971
- const lock = new Lock(installationPath);
2972
- await fs.writeFile(path.join(installationPath, "package.json"), this.#getPackageJson(compilerVersion));
2973
- await this.#installPackage(installationPath, signal);
2974
- await fs.writeFile(tsserverFilePath, await this.#getPatched(compilerVersion, tsserverFilePath));
2975
- await fs.writeFile(typescriptFilePath, await this.#getPatched(compilerVersion, typescriptFilePath));
2976
- await fs.writeFile(readyFilePath, "");
2977
- lock.release();
2989
+ EventEmitter.dispatch(["store:info", { compilerVersion, installationPath: this.#normalizePath(installationPath) }]);
2990
+ try {
2991
+ await fs.mkdir(installationPath, { recursive: true });
2992
+ const lock = new Lock(installationPath);
2993
+ await fs.writeFile(path.join(installationPath, "package.json"), this.#getPackageJson(compilerVersion));
2994
+ await this.#installPackage(installationPath, signal);
2995
+ await fs.writeFile(tsserverFilePath, await this.#getPatched(compilerVersion, tsserverFilePath));
2996
+ await fs.writeFile(typescriptFilePath, await this.#getPatched(compilerVersion, typescriptFilePath));
2997
+ await fs.writeFile(readyFilePath, "");
2998
+ lock.release();
2999
+ }
3000
+ catch (error) {
3001
+ this.#onDiagnostic(Diagnostic.fromError(`Failed to install 'typescript@${compilerVersion}'.`, error));
3002
+ }
2978
3003
  return tsserverFilePath;
2979
3004
  }
2980
3005
  #getPackageJson(version) {
@@ -3029,6 +3054,12 @@ class CompilerModuleWorker {
3029
3054
  });
3030
3055
  });
3031
3056
  }
3057
+ #normalizePath(filePath) {
3058
+ if (path.sep === "/") {
3059
+ return filePath;
3060
+ }
3061
+ return filePath.replace(/\\/g, "/");
3062
+ }
3032
3063
  }
3033
3064
 
3034
3065
  class ManifestWorker {
@@ -3119,10 +3150,10 @@ class ManifestWorker {
3119
3150
  manifest.resolutions[tag] = resolvedVersion;
3120
3151
  }
3121
3152
  }
3122
- for (const distributionTagKey of ["beta", "latest", "next", "rc"]) {
3123
- const distributionTagValue = packageMetadata["dist-tags"][distributionTagKey];
3153
+ for (const tagKey of ["beta", "latest", "next", "rc"]) {
3154
+ const distributionTagValue = packageMetadata["dist-tags"][tagKey];
3124
3155
  if (distributionTagValue != null) {
3125
- manifest.resolutions[distributionTagKey] = distributionTagValue;
3156
+ manifest.resolutions[tagKey] = distributionTagValue;
3126
3157
  }
3127
3158
  }
3128
3159
  return manifest;
@@ -3192,17 +3223,29 @@ class StoreService {
3192
3223
  #nodeRequire = createRequire(import.meta.url);
3193
3224
  constructor() {
3194
3225
  this.#cachePath = Environment.storePath;
3195
- this.#compilerModuleWorker = new CompilerModuleWorker(this.#cachePath);
3226
+ this.#compilerModuleWorker = new CompilerModuleWorker(this.#cachePath, this.#onDiagnostic);
3196
3227
  this.#manifestWorker = new ManifestWorker(this.#cachePath, async () => this.prune());
3197
3228
  }
3198
- getSupportedTags() {
3229
+ get supportedTags() {
3199
3230
  if (!this.#manifest) {
3200
3231
  this.#onDiagnostic(Diagnostic.error("Store manifest is not open. Call 'StoreService.open()' first."));
3201
3232
  return [];
3202
3233
  }
3203
- return [...Object.keys(this.#manifest.resolutions), ...this.#manifest.versions].sort();
3234
+ return [...Object.keys(this.#manifest.resolutions), ...this.#manifest.versions, "current"].sort();
3204
3235
  }
3205
- async loadCompilerModule(tag, signal) {
3236
+ async install(tag, signal) {
3237
+ if (!this.#manifest) {
3238
+ this.#onDiagnostic(Diagnostic.error("Store manifest is not open. Call 'StoreService.open()' first."));
3239
+ return;
3240
+ }
3241
+ const version = this.resolveTag(tag);
3242
+ if (version == null) {
3243
+ this.#onDiagnostic(Diagnostic.error(`Cannot add the 'typescript' package for the '${tag}' tag.`));
3244
+ return;
3245
+ }
3246
+ return this.#compilerModuleWorker.ensure(version, signal);
3247
+ }
3248
+ async load(tag, signal) {
3206
3249
  let modulePath;
3207
3250
  if (tag === "local") {
3208
3251
  try {
@@ -3213,47 +3256,22 @@ class StoreService {
3213
3256
  }
3214
3257
  }
3215
3258
  if (modulePath == null) {
3216
- modulePath = await this.prepareCompilerModule(tag, signal);
3259
+ modulePath = await this.install(tag, signal);
3217
3260
  }
3218
3261
  if (modulePath != null) {
3219
3262
  return this.#nodeRequire(modulePath);
3220
3263
  }
3221
3264
  return;
3222
3265
  }
3223
- #onDiagnostic(diagnostic) {
3266
+ #onDiagnostic = (diagnostic) => {
3224
3267
  EventEmitter.dispatch(["store:error", { diagnostics: [diagnostic] }]);
3225
- }
3268
+ };
3226
3269
  async open(signal) {
3227
3270
  if (this.#manifest) {
3228
3271
  return;
3229
3272
  }
3230
3273
  this.#manifest = await this.#manifestWorker.open(signal);
3231
3274
  }
3232
- async prepareCompilerModule(tag, signal) {
3233
- if (!this.#manifest) {
3234
- this.#onDiagnostic(Diagnostic.error("Store manifest is not open. Call 'StoreService.open()' first."));
3235
- return;
3236
- }
3237
- const version = this.resolveTag(tag);
3238
- if (version == null) {
3239
- this.#onDiagnostic(Diagnostic.error(`Cannot add the 'typescript' package for the '${tag}' tag.`));
3240
- return;
3241
- }
3242
- let modulePath;
3243
- try {
3244
- modulePath = await this.#compilerModuleWorker.ensure(version, signal);
3245
- }
3246
- catch (error) {
3247
- this.#onDiagnostic(Diagnostic.fromError(`Failed to install 'typescript@${version}'.`, error));
3248
- }
3249
- if (modulePath != null) {
3250
- if (!("lastUsed" in this.#manifest)) {
3251
- this.#manifest.lastUsed = {};
3252
- }
3253
- this.#manifest.lastUsed[version] = Date.now();
3254
- }
3255
- return modulePath;
3256
- }
3257
3275
  async prune() {
3258
3276
  await fs.rm(this.#cachePath, { force: true, recursive: true });
3259
3277
  }
@@ -3262,6 +3280,14 @@ class StoreService {
3262
3280
  this.#onDiagnostic(Diagnostic.error("Store manifest is not open. Call 'StoreService.open()' first."));
3263
3281
  return;
3264
3282
  }
3283
+ if (tag === "current") {
3284
+ try {
3285
+ tag = this.#nodeRequire("typescript").version;
3286
+ }
3287
+ catch (error) {
3288
+ this.#onDiagnostic(Diagnostic.fromError("Failed to resolve tag 'current'. The 'typescript' package might be not installed.", error));
3289
+ }
3290
+ }
3265
3291
  if (this.#manifest.versions.includes(tag)) {
3266
3292
  return tag;
3267
3293
  }
@@ -3287,7 +3313,7 @@ class StoreService {
3287
3313
  this.#onDiagnostic(Diagnostic.error("Store manifest is not open. Call 'StoreService.open()' first."));
3288
3314
  return false;
3289
3315
  }
3290
- if (this.#manifest.versions.includes(tag) || tag in this.#manifest.resolutions) {
3316
+ if (this.#manifest.versions.includes(tag) || tag in this.#manifest.resolutions || tag === "current") {
3291
3317
  return true;
3292
3318
  }
3293
3319
  if (this.#manifest.resolutions["latest"] != null &&
@@ -3487,7 +3513,7 @@ class Cli {
3487
3513
  if (this.#process.exitCode === 1) {
3488
3514
  return;
3489
3515
  }
3490
- const compiler = await this.#storeService.loadCompilerModule("local", this.#abortController.signal);
3516
+ const compiler = await this.#storeService.load("local", this.#abortController.signal);
3491
3517
  if (!compiler) {
3492
3518
  return;
3493
3519
  }
@@ -3529,7 +3555,7 @@ class Cli {
3529
3555
  }
3530
3556
  if (configService.commandLineOptions.install === true) {
3531
3557
  for (const tag of resolvedConfig.target) {
3532
- await this.#storeService.prepareCompilerModule(tag, this.#abortController.signal);
3558
+ await this.#storeService.install(tag, this.#abortController.signal);
3533
3559
  }
3534
3560
  return;
3535
3561
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tstyche",
3
- "version": "1.0.0-beta.3",
3
+ "version": "1.0.0-beta.5",
4
4
  "description": "The Essential Type Testing Tool.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -28,16 +28,17 @@
28
28
  },
29
29
  "main": "./build/index.js",
30
30
  "types": "./build/index.d.ts",
31
- "bin": "build/bin.js",
31
+ "bin": "./build/bin.js",
32
32
  "files": [
33
- "build/*",
34
- "lib/*.json"
33
+ "build/*"
35
34
  ],
36
35
  "scripts": {
37
36
  "build": "rollup --config rollup.config.js",
38
37
  "build:watch": "yarn build --sourcemap --watch",
39
38
  "clean": "rm -rf build",
40
- "generate": "wireit",
39
+ "generate": "yarn generate:schema && yarn generate:types",
40
+ "generate:schema": "node scripts/generate-schema.js",
41
+ "generate:types": "node scripts/generate-types.js",
41
42
  "lint": "yarn lint:cspell && yarn lint:eslint && yarn lint:prettier",
42
43
  "lint:cspell": "cspell --cache --config cspell.config.json --quiet",
43
44
  "lint:eslint": "yarn lint:eslint:md && yarn lint:eslint:ts",
@@ -56,13 +57,13 @@
56
57
  "devDependencies": {
57
58
  "@jest/globals": "29.7.0",
58
59
  "@rollup/plugin-typescript": "11.1.5",
59
- "@types/node": "20.9.0",
60
- "@typescript-eslint/eslint-plugin": "6.10.0",
61
- "@typescript-eslint/parser": "6.10.0",
60
+ "@types/node": "20.10.0",
61
+ "@typescript-eslint/eslint-plugin": "6.12.0",
62
+ "@typescript-eslint/parser": "6.12.0",
62
63
  "ajv": "8.12.0",
63
64
  "c8": "8.0.1",
64
65
  "cspell": "8.0.0",
65
- "eslint": "8.53.0",
66
+ "eslint": "8.54.0",
66
67
  "eslint-config-prettier": "9.0.0",
67
68
  "eslint-import-resolver-typescript": "3.6.1",
68
69
  "eslint-plugin-import": "2.29.0",
@@ -75,13 +76,12 @@
75
76
  "jest-serializer-ansi-escapes": "2.0.1",
76
77
  "magic-string": "0.30.5",
77
78
  "prettier": "3.1.0",
78
- "rollup": "4.4.0",
79
+ "rollup": "4.6.0",
79
80
  "rollup-plugin-dts": "6.1.0",
80
81
  "rollup-plugin-tsconfig-paths": "1.5.2",
81
82
  "ts-node": "10.9.1",
82
83
  "tslib": "2.6.2",
83
- "typescript": "5.2.2",
84
- "wireit": "0.14.1"
84
+ "typescript": "5.2.2"
85
85
  },
86
86
  "peerDependencies": {
87
87
  "typescript": "4.x || 5.x"
@@ -91,37 +91,8 @@
91
91
  "optional": true
92
92
  }
93
93
  },
94
- "packageManager": "yarn@4.0.1",
94
+ "packageManager": "yarn@3.7.0",
95
95
  "engines": {
96
96
  "node": "^16.14 || 18.x || >=20.x"
97
- },
98
- "wireit": {
99
- "generate": {
100
- "dependencies": [
101
- "generate:schema",
102
- "generate:types"
103
- ]
104
- },
105
- "generate:schema": {
106
- "command": "node scripts/generate-schema.js",
107
- "files": [
108
- "./build",
109
- "./scripts/generate-schema.js"
110
- ],
111
- "output": [
112
- "./lib/schema.json"
113
- ]
114
- },
115
- "generate:types": {
116
- "command": "node scripts/generate-types.js",
117
- "files": [
118
- "./build",
119
- "./scripts/generate-types.js"
120
- ],
121
- "output": [
122
- "./lib/CommandLineOptions.ts",
123
- "./lib/ConfigFileOptions.ts"
124
- ]
125
- }
126
97
  }
127
- }
98
+ }
package/lib/schema.json DELETED
@@ -1,47 +0,0 @@
1
- {
2
- "$schema": "http://json-schema.org/draft-07/schema#",
3
- "definitions": {},
4
- "properties": {
5
- "allowNoTestFiles": {
6
- "default": false,
7
- "description": "Do not raise an error, if no test files are selected.",
8
- "type": "boolean"
9
- },
10
- "failFast": {
11
- "default": false,
12
- "description": "Stop running tests after the first failed assertion.",
13
- "type": "boolean"
14
- },
15
- "rootPath": {
16
- "default": "./",
17
- "description": "The path to a directory containing files of a test project.",
18
- "type": "string"
19
- },
20
- "target": {
21
- "default": [
22
- "latest"
23
- ],
24
- "description": "The list of TypeScript versions to be tested on.",
25
- "items": {
26
- "pattern": "^([45]\\.[0-9](\\.[0-9])?)|beta|latest|next|rc$",
27
- "type": "string"
28
- },
29
- "type": "array",
30
- "uniqueItems": true
31
- },
32
- "testFileMatch": {
33
- "default": [
34
- "**/*.tst.*",
35
- "**/__typetests__/*.test.*",
36
- "**/typetests/*.test.*"
37
- ],
38
- "description": "The list of glob patterns matching the test files.",
39
- "items": {
40
- "type": "string"
41
- },
42
- "type": "array",
43
- "uniqueItems": true
44
- }
45
- },
46
- "type": "object"
47
- }