tstyche 3.0.0 → 3.1.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 +4 -4
- package/build/tstyche.d.ts +55 -7
- package/build/tstyche.js +310 -170
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -57,17 +57,17 @@ 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
|
|
60
|
+
- `.toAcceptProps()` checks JSX component props type,
|
|
61
61
|
- `.toHaveProperty()` looks up keys on an object type,
|
|
62
62
|
- `.toRaiseError()` captures the type error message or code,
|
|
63
63
|
- `.toBeString()`, `.toBeNumber()`, `.toBeVoid()` and 9 more shorthand checks for primitive types.
|
|
64
64
|
|
|
65
65
|
## Runner
|
|
66
66
|
|
|
67
|
-
The `tstyche` command is the heart of TSTyche. For example, it can select test files by path, filter tests by name and pass them through
|
|
67
|
+
The `tstyche` command is the heart of TSTyche. For example, it can select test files by path, filter tests by name and pass them through a range of TypeScript versions:
|
|
68
68
|
|
|
69
|
-
```
|
|
70
|
-
tstyche
|
|
69
|
+
```shell
|
|
70
|
+
tstyche query-params --only multiple --target '>=5.0 <5.3'
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
This simple! (And it has watch mode too.)
|
package/build/tstyche.d.ts
CHANGED
|
@@ -107,13 +107,13 @@ interface MatcherNode extends ts.CallExpression {
|
|
|
107
107
|
}
|
|
108
108
|
declare class Assertion extends TestMember {
|
|
109
109
|
isNot: boolean;
|
|
110
|
+
matcherName: ts.MemberName;
|
|
110
111
|
matcherNode: MatcherNode;
|
|
111
112
|
modifierNode: ts.PropertyAccessExpression;
|
|
112
113
|
notNode: ts.PropertyAccessExpression | undefined;
|
|
114
|
+
source: ts.NodeArray<ts.Expression> | ts.NodeArray<ts.TypeNode>;
|
|
115
|
+
target: ts.NodeArray<ts.Expression> | ts.NodeArray<ts.TypeNode>;
|
|
113
116
|
constructor(compiler: typeof ts, brand: TestMemberBrand, node: ts.CallExpression, parent: TestTree | TestMember, flags: TestMemberFlags, matcherNode: MatcherNode, modifierNode: ts.PropertyAccessExpression, notNode?: ts.PropertyAccessExpression);
|
|
114
|
-
get matcherName(): ts.MemberName;
|
|
115
|
-
get source(): ts.NodeArray<ts.Expression> | ts.NodeArray<ts.TypeNode>;
|
|
116
|
-
get target(): ts.NodeArray<ts.Expression> | ts.NodeArray<ts.TypeNode>;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
declare class CollectService {
|
|
@@ -135,12 +135,15 @@ declare class ConfigDiagnosticText {
|
|
|
135
135
|
static expectsListItemType(optionName: string, optionBrand: OptionBrand): string;
|
|
136
136
|
static expectsValue(optionName: string): string;
|
|
137
137
|
static fileDoesNotExist(filePath: string): string;
|
|
138
|
+
static inspectSupportedVersions(): string;
|
|
138
139
|
static moduleWasNotFound(specifier: string): string;
|
|
140
|
+
static rangeIsNotValid(value: string): string;
|
|
141
|
+
static rangeUsage(): Array<string>;
|
|
142
|
+
static requiresValueType(optionName: string, optionBrand: OptionBrand): string;
|
|
139
143
|
static seen(element: string): string;
|
|
140
144
|
static testFileMatchCannotStartWith(segment: string): Array<string>;
|
|
141
|
-
static requiresValueType(optionName: string, optionBrand: OptionBrand): string;
|
|
142
145
|
static unknownOption(optionName: string): string;
|
|
143
|
-
static usage(optionName: string, optionBrand: OptionBrand):
|
|
146
|
+
static usage(optionName: string, optionBrand: OptionBrand): Array<string>;
|
|
144
147
|
static versionIsNotSupported(value: string): string;
|
|
145
148
|
static watchCannotBeEnabled(): string;
|
|
146
149
|
}
|
|
@@ -157,6 +160,14 @@ interface ConfigFileOptions {
|
|
|
157
160
|
* The list of plugins to use.
|
|
158
161
|
*/
|
|
159
162
|
plugins?: Array<string>;
|
|
163
|
+
/**
|
|
164
|
+
* Reject the 'any' type passed as an argument to the 'expect()' function or a matcher.
|
|
165
|
+
*/
|
|
166
|
+
rejectAnyType?: boolean;
|
|
167
|
+
/**
|
|
168
|
+
* Reject the 'never' type passed as an argument to the 'expect()' function or a matcher.
|
|
169
|
+
*/
|
|
170
|
+
rejectNeverType?: boolean;
|
|
160
171
|
/**
|
|
161
172
|
* The list of reporters to use.
|
|
162
173
|
*/
|
|
@@ -199,6 +210,10 @@ interface CommandLineOptions {
|
|
|
199
210
|
* Install specified versions of the 'typescript' package and exit.
|
|
200
211
|
*/
|
|
201
212
|
install?: boolean;
|
|
213
|
+
/**
|
|
214
|
+
* Print the list of supported versions of the 'typescript' package and exit.
|
|
215
|
+
*/
|
|
216
|
+
list?: boolean;
|
|
202
217
|
/**
|
|
203
218
|
* Print the list of the selected test files and exit.
|
|
204
219
|
*/
|
|
@@ -296,7 +311,6 @@ interface PrimitiveTypeOptionDefinition extends BaseOptionDefinition {
|
|
|
296
311
|
interface ItemDefinition {
|
|
297
312
|
brand: OptionBrand.String;
|
|
298
313
|
name: string;
|
|
299
|
-
pattern?: string;
|
|
300
314
|
}
|
|
301
315
|
interface ListTypeOptionDefinition extends BaseOptionDefinition {
|
|
302
316
|
brand: OptionBrand.List;
|
|
@@ -588,7 +602,7 @@ declare class ExpectService {
|
|
|
588
602
|
private toHaveProperty;
|
|
589
603
|
private toMatch;
|
|
590
604
|
private toRaiseError;
|
|
591
|
-
constructor(compiler: typeof ts, typeChecker: TypeChecker);
|
|
605
|
+
constructor(compiler: typeof ts, typeChecker: TypeChecker, resolvedConfig?: ResolvedConfig);
|
|
592
606
|
match(assertion: Assertion, onDiagnostics: DiagnosticsHandler<Diagnostic | Array<Diagnostic>>): MatchResult | undefined;
|
|
593
607
|
}
|
|
594
608
|
|
|
@@ -779,8 +793,41 @@ declare class SelectDiagnosticText {
|
|
|
779
793
|
static noTestFilesWereSelected(resolvedConfig: ResolvedConfig): Array<string>;
|
|
780
794
|
}
|
|
781
795
|
|
|
796
|
+
interface ManifestData {
|
|
797
|
+
$version?: string;
|
|
798
|
+
lastUpdated?: number;
|
|
799
|
+
npmRegistry: string;
|
|
800
|
+
packages: Record<string, {
|
|
801
|
+
integrity: string;
|
|
802
|
+
tarball: string;
|
|
803
|
+
}>;
|
|
804
|
+
resolutions: Record<string, string>;
|
|
805
|
+
versions: Array<string>;
|
|
806
|
+
}
|
|
807
|
+
declare class Manifest {
|
|
808
|
+
#private;
|
|
809
|
+
$version: string;
|
|
810
|
+
lastUpdated: number;
|
|
811
|
+
npmRegistry: string;
|
|
812
|
+
packages: Record<string, {
|
|
813
|
+
integrity: string;
|
|
814
|
+
tarball: string;
|
|
815
|
+
}>;
|
|
816
|
+
resolutions: Record<string, string>;
|
|
817
|
+
versions: Array<string>;
|
|
818
|
+
constructor(data: ManifestData);
|
|
819
|
+
isOutdated(options?: {
|
|
820
|
+
ageTolerance?: number;
|
|
821
|
+
}): boolean;
|
|
822
|
+
static parse(text: string): Manifest | undefined;
|
|
823
|
+
resolve(tag: string): string | undefined;
|
|
824
|
+
stringify(): string;
|
|
825
|
+
}
|
|
826
|
+
|
|
782
827
|
declare class Store {
|
|
783
828
|
#private;
|
|
829
|
+
static manifest: Manifest | undefined;
|
|
830
|
+
/** @deprecated Use 'Store.manifest' directly. */
|
|
784
831
|
static getSupportedTags(): Promise<Array<string> | undefined>;
|
|
785
832
|
static install(tag: string): Promise<void>;
|
|
786
833
|
static load(tag: string): Promise<typeof ts | undefined>;
|
|
@@ -794,6 +841,7 @@ declare class Version {
|
|
|
794
841
|
#private;
|
|
795
842
|
static isGreaterThan(source: string, target: string): boolean;
|
|
796
843
|
static isSatisfiedWith(source: string, target: string): boolean;
|
|
844
|
+
/** @deprecated Name of this method is misleading and it is also not needed. */
|
|
797
845
|
static isVersionTag(target: string): boolean;
|
|
798
846
|
}
|
|
799
847
|
|
package/build/tstyche.js
CHANGED
|
@@ -1,13 +1,80 @@
|
|
|
1
|
+
import { writeFileSync, rmSync, existsSync, watch } from 'node:fs';
|
|
1
2
|
import fs from 'node:fs/promises';
|
|
2
|
-
import
|
|
3
|
+
import path from 'node:path';
|
|
3
4
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
4
|
-
import vm from 'node:vm';
|
|
5
5
|
import os from 'node:os';
|
|
6
6
|
import process from 'node:process';
|
|
7
|
-
import
|
|
8
|
-
import
|
|
7
|
+
import { createRequire } from 'node:module';
|
|
8
|
+
import vm from 'node:vm';
|
|
9
9
|
import streamConsumers from 'node:stream/consumers';
|
|
10
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) {
|
|
19
|
+
return `Option '${optionName}' expects a value.`;
|
|
20
|
+
}
|
|
21
|
+
static fileDoesNotExist(filePath) {
|
|
22
|
+
return `The specified path '${filePath}' does not exist.`;
|
|
23
|
+
}
|
|
24
|
+
static inspectSupportedVersions() {
|
|
25
|
+
return "Use the '--list' command line option to inspect the list of supported versions.";
|
|
26
|
+
}
|
|
27
|
+
static moduleWasNotFound(specifier) {
|
|
28
|
+
return `The specified module '${specifier}' was not found.`;
|
|
29
|
+
}
|
|
30
|
+
static rangeIsNotValid(value) {
|
|
31
|
+
return `The specified range '${value}' is not valid.`;
|
|
32
|
+
}
|
|
33
|
+
static rangeUsage() {
|
|
34
|
+
return [
|
|
35
|
+
"A range must be specified using an operator and a minor version.",
|
|
36
|
+
"To set an upper bound, the intersection of two ranges can be used.",
|
|
37
|
+
"Examples: '>=5.5', '>=5.0 <5.3'.",
|
|
38
|
+
];
|
|
39
|
+
}
|
|
40
|
+
static requiresValueType(optionName, optionBrand) {
|
|
41
|
+
return `Option '${optionName}' requires a value of type ${optionBrand}.`;
|
|
42
|
+
}
|
|
43
|
+
static seen(element) {
|
|
44
|
+
return `The ${element} was seen here.`;
|
|
45
|
+
}
|
|
46
|
+
static testFileMatchCannotStartWith(segment) {
|
|
47
|
+
return [
|
|
48
|
+
`A test file match pattern cannot start with '${segment}'.`,
|
|
49
|
+
"The test files are only collected within the 'rootPath' directory.",
|
|
50
|
+
];
|
|
51
|
+
}
|
|
52
|
+
static unknownOption(optionName) {
|
|
53
|
+
return `Unknown option '${optionName}'.`;
|
|
54
|
+
}
|
|
55
|
+
static usage(optionName, optionBrand) {
|
|
56
|
+
switch (optionName.startsWith("--") ? optionName.slice(2) : optionName) {
|
|
57
|
+
case "target": {
|
|
58
|
+
const text = [];
|
|
59
|
+
if (optionName.startsWith("--")) {
|
|
60
|
+
text.push("Value for the '--target' option must be a string or a comma separated list.", "Examples: '--target 5.2', '--target next', '--target '>=5.0 <5.3, 5.4.2, >=5.5''.");
|
|
61
|
+
}
|
|
62
|
+
return text;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return [ConfigDiagnosticText.requiresValueType(optionName, optionBrand)];
|
|
66
|
+
}
|
|
67
|
+
static versionIsNotSupported(value) {
|
|
68
|
+
if (value === "current") {
|
|
69
|
+
return "Cannot use 'current' as a target. Failed to resolve the installed TypeScript module.";
|
|
70
|
+
}
|
|
71
|
+
return `TypeScript version '${value}' is not supported.`;
|
|
72
|
+
}
|
|
73
|
+
static watchCannotBeEnabled() {
|
|
74
|
+
return "Watch mode cannot be enabled in continuous integration environment.";
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
11
78
|
class DiagnosticOrigin {
|
|
12
79
|
assertion;
|
|
13
80
|
end;
|
|
@@ -117,6 +184,49 @@ class SourceFile {
|
|
|
117
184
|
}
|
|
118
185
|
}
|
|
119
186
|
|
|
187
|
+
class EventEmitter {
|
|
188
|
+
static instanceCount = 0;
|
|
189
|
+
static #handlers = new Map();
|
|
190
|
+
static #reporters = new Map();
|
|
191
|
+
#scope;
|
|
192
|
+
constructor() {
|
|
193
|
+
this.#scope = EventEmitter.instanceCount++;
|
|
194
|
+
EventEmitter.#handlers.set(this.#scope, new Set());
|
|
195
|
+
EventEmitter.#reporters.set(this.#scope, new Set());
|
|
196
|
+
}
|
|
197
|
+
addHandler(handler) {
|
|
198
|
+
EventEmitter.#handlers.get(this.#scope)?.add(handler);
|
|
199
|
+
}
|
|
200
|
+
addReporter(reporter) {
|
|
201
|
+
EventEmitter.#reporters.get(this.#scope)?.add(reporter);
|
|
202
|
+
}
|
|
203
|
+
static dispatch(event) {
|
|
204
|
+
function forEachHandler(handlers, event) {
|
|
205
|
+
for (const handler of handlers) {
|
|
206
|
+
handler.on(event);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
for (const handlers of EventEmitter.#handlers.values()) {
|
|
210
|
+
forEachHandler(handlers, event);
|
|
211
|
+
}
|
|
212
|
+
for (const handlers of EventEmitter.#reporters.values()) {
|
|
213
|
+
forEachHandler(handlers, event);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
removeHandler(handler) {
|
|
217
|
+
EventEmitter.#handlers.get(this.#scope)?.delete(handler);
|
|
218
|
+
}
|
|
219
|
+
removeReporter(reporter) {
|
|
220
|
+
EventEmitter.#reporters.get(this.#scope)?.delete(reporter);
|
|
221
|
+
}
|
|
222
|
+
removeHandlers() {
|
|
223
|
+
EventEmitter.#handlers.get(this.#scope)?.clear();
|
|
224
|
+
}
|
|
225
|
+
removeReporters() {
|
|
226
|
+
EventEmitter.#reporters.get(this.#scope)?.clear();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
120
230
|
class Path {
|
|
121
231
|
static normalizeSlashes;
|
|
122
232
|
static {
|
|
@@ -222,49 +332,6 @@ class Environment {
|
|
|
222
332
|
|
|
223
333
|
const environmentOptions = Environment.resolve();
|
|
224
334
|
|
|
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
|
-
|
|
268
335
|
class Version {
|
|
269
336
|
static isGreaterThan(source, target) {
|
|
270
337
|
return !(source === target) && Version.#satisfies(source, target);
|
|
@@ -465,6 +532,7 @@ class ManifestService {
|
|
|
465
532
|
#manifestFilePath;
|
|
466
533
|
#npmRegistry;
|
|
467
534
|
#storePath;
|
|
535
|
+
#supportedVersionRegex = /^(4|5)\.\d\.\d$/;
|
|
468
536
|
constructor(storePath, npmRegistry, fetcher) {
|
|
469
537
|
this.#storePath = storePath;
|
|
470
538
|
this.#npmRegistry = npmRegistry;
|
|
@@ -494,12 +562,12 @@ class ManifestService {
|
|
|
494
562
|
const versions = [];
|
|
495
563
|
const packageMetadata = (await response.json());
|
|
496
564
|
for (const [tag, meta] of Object.entries(packageMetadata.versions)) {
|
|
497
|
-
if (
|
|
565
|
+
if (this.#supportedVersionRegex.test(tag)) {
|
|
498
566
|
versions.push(tag);
|
|
499
567
|
packages[tag] = { integrity: meta.dist.integrity, tarball: meta.dist.tarball };
|
|
500
568
|
}
|
|
501
569
|
}
|
|
502
|
-
const minorVersions =
|
|
570
|
+
const minorVersions = new Set(versions.map((version) => version.slice(0, -2)));
|
|
503
571
|
for (const tag of minorVersions) {
|
|
504
572
|
const resolvedVersion = versions.findLast((version) => version.startsWith(tag));
|
|
505
573
|
if (resolvedVersion != null) {
|
|
@@ -528,7 +596,7 @@ class ManifestService {
|
|
|
528
596
|
await this.prune();
|
|
529
597
|
return this.#create();
|
|
530
598
|
}
|
|
531
|
-
if (manifest.isOutdated() || options?.refresh
|
|
599
|
+
if (manifest.isOutdated() || options?.refresh) {
|
|
532
600
|
const freshManifest = await this.#load({ suppressErrors: !options?.refresh });
|
|
533
601
|
if (freshManifest != null) {
|
|
534
602
|
await this.#persist(freshManifest);
|
|
@@ -637,7 +705,7 @@ class Store {
|
|
|
637
705
|
static #compilerInstanceCache = new Map();
|
|
638
706
|
static #fetcher;
|
|
639
707
|
static #lockService;
|
|
640
|
-
static
|
|
708
|
+
static manifest;
|
|
641
709
|
static #manifestService;
|
|
642
710
|
static #packageService;
|
|
643
711
|
static #npmRegistry = environmentOptions.npmRegistry;
|
|
@@ -659,12 +727,12 @@ class Store {
|
|
|
659
727
|
return;
|
|
660
728
|
}
|
|
661
729
|
await Store.open();
|
|
662
|
-
const version = Store
|
|
730
|
+
const version = Store.manifest?.resolve(tag);
|
|
663
731
|
if (!version) {
|
|
664
732
|
Store.#onDiagnostics(Diagnostic.error(StoreDiagnosticText.cannotAddTypeScriptPackage(tag)));
|
|
665
733
|
return;
|
|
666
734
|
}
|
|
667
|
-
await Store.#packageService.ensure(version, Store
|
|
735
|
+
await Store.#packageService.ensure(version, Store.manifest);
|
|
668
736
|
}
|
|
669
737
|
static async load(tag) {
|
|
670
738
|
let compilerInstance = Store.#compilerInstanceCache.get(tag);
|
|
@@ -677,7 +745,7 @@ class Store {
|
|
|
677
745
|
}
|
|
678
746
|
else {
|
|
679
747
|
await Store.open();
|
|
680
|
-
const version = Store
|
|
748
|
+
const version = Store.manifest?.resolve(tag);
|
|
681
749
|
if (!version) {
|
|
682
750
|
Store.#onDiagnostics(Diagnostic.error(StoreDiagnosticText.cannotAddTypeScriptPackage(tag)));
|
|
683
751
|
return;
|
|
@@ -686,7 +754,7 @@ class Store {
|
|
|
686
754
|
if (compilerInstance != null) {
|
|
687
755
|
return compilerInstance;
|
|
688
756
|
}
|
|
689
|
-
const packagePath = await Store.#packageService.ensure(version, Store
|
|
757
|
+
const packagePath = await Store.#packageService.ensure(version, Store.manifest);
|
|
690
758
|
if (packagePath != null) {
|
|
691
759
|
modulePath = Path.join(packagePath, "lib", "typescript.js");
|
|
692
760
|
}
|
|
@@ -725,13 +793,9 @@ class Store {
|
|
|
725
793
|
}
|
|
726
794
|
static async open() {
|
|
727
795
|
Store.open = () => Promise.resolve();
|
|
728
|
-
Store
|
|
729
|
-
if (Store
|
|
730
|
-
Store.#supportedTags = [
|
|
731
|
-
...Object.keys(Store.#manifest.resolutions),
|
|
732
|
-
...Store.#manifest.versions,
|
|
733
|
-
"current",
|
|
734
|
-
].sort();
|
|
796
|
+
Store.manifest = await Store.#manifestService.open();
|
|
797
|
+
if (Store.manifest != null) {
|
|
798
|
+
Store.#supportedTags = [...Object.keys(Store.manifest.resolutions), ...Store.manifest.versions, "current"].sort();
|
|
735
799
|
}
|
|
736
800
|
}
|
|
737
801
|
static async prune() {
|
|
@@ -745,10 +809,10 @@ class Store {
|
|
|
745
809
|
return environmentOptions.typescriptModule != null;
|
|
746
810
|
}
|
|
747
811
|
await Store.open();
|
|
748
|
-
if (Store
|
|
749
|
-
(
|
|
750
|
-
(Store
|
|
751
|
-
Version.isGreaterThan(tag, Store
|
|
812
|
+
if (Store.manifest?.isOutdated({ ageTolerance: 60 }) &&
|
|
813
|
+
(!/^\d/.test(tag) ||
|
|
814
|
+
(Store.manifest.resolutions["latest"] != null &&
|
|
815
|
+
Version.isGreaterThan(tag, Store.manifest.resolutions["latest"])))) {
|
|
752
816
|
Store.#onDiagnostics(Diagnostic.warning([
|
|
753
817
|
StoreDiagnosticText.failedToUpdateMetadata(Store.#npmRegistry),
|
|
754
818
|
StoreDiagnosticText.maybeOutdatedResolution(tag),
|
|
@@ -758,64 +822,42 @@ class Store {
|
|
|
758
822
|
}
|
|
759
823
|
}
|
|
760
824
|
|
|
761
|
-
class
|
|
762
|
-
static
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
return `The specified module '${specifier}' was not found.`;
|
|
776
|
-
}
|
|
777
|
-
static seen(element) {
|
|
778
|
-
return `The ${element} was seen here.`;
|
|
779
|
-
}
|
|
780
|
-
static testFileMatchCannotStartWith(segment) {
|
|
781
|
-
return [
|
|
782
|
-
`A test file match pattern cannot start with '${segment}'.`,
|
|
783
|
-
"The test files are only collected within the 'rootPath' directory.",
|
|
784
|
-
];
|
|
785
|
-
}
|
|
786
|
-
static requiresValueType(optionName, optionBrand) {
|
|
787
|
-
return `Option '${optionName}' requires a value of type ${optionBrand}.`;
|
|
788
|
-
}
|
|
789
|
-
static unknownOption(optionName) {
|
|
790
|
-
return `Unknown option '${optionName}'.`;
|
|
791
|
-
}
|
|
792
|
-
static async usage(optionName, optionBrand) {
|
|
793
|
-
switch (optionName.startsWith("--") ? optionName.slice(2) : optionName) {
|
|
794
|
-
case "target": {
|
|
795
|
-
const text = [];
|
|
796
|
-
if (optionName.startsWith("--")) {
|
|
797
|
-
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'.");
|
|
798
|
-
}
|
|
799
|
-
else {
|
|
800
|
-
text.push("Item of the 'target' list must be a supported version tag.");
|
|
801
|
-
}
|
|
802
|
-
const supportedTags = await Store.getSupportedTags();
|
|
803
|
-
if (supportedTags != null) {
|
|
804
|
-
text.push(`Supported tags: ${["'", supportedTags.join("', '"), "'"].join("")}.`);
|
|
825
|
+
class Target {
|
|
826
|
+
static #rangeRegex = /^[<>]=?\d\.\d( [<>]=?\d\.\d)?$/;
|
|
827
|
+
static async expand(queries) {
|
|
828
|
+
const include = [];
|
|
829
|
+
for (const query of queries) {
|
|
830
|
+
if (!Target.isRange(query)) {
|
|
831
|
+
include.push(query);
|
|
832
|
+
continue;
|
|
833
|
+
}
|
|
834
|
+
await Store.open();
|
|
835
|
+
if (Store.manifest != null) {
|
|
836
|
+
let versions = Object.keys(Store.manifest.resolutions).slice(0, -4);
|
|
837
|
+
for (const comparator of query.split(" ")) {
|
|
838
|
+
versions = Target.#filter(comparator, versions);
|
|
805
839
|
}
|
|
806
|
-
|
|
840
|
+
include.push(...versions);
|
|
807
841
|
}
|
|
808
842
|
}
|
|
809
|
-
return
|
|
843
|
+
return include;
|
|
810
844
|
}
|
|
811
|
-
static
|
|
812
|
-
|
|
813
|
-
|
|
845
|
+
static #filter(comparator, versions) {
|
|
846
|
+
const targetVersion = comparator.replace(/^[<>]=?/, "");
|
|
847
|
+
switch (comparator.charAt(0)) {
|
|
848
|
+
case ">":
|
|
849
|
+
return versions.filter((sourceVersion) => comparator.charAt(1) === "="
|
|
850
|
+
? Version.isSatisfiedWith(sourceVersion, targetVersion)
|
|
851
|
+
: Version.isGreaterThan(sourceVersion, targetVersion));
|
|
852
|
+
case "<":
|
|
853
|
+
return versions.filter((sourceVersion) => comparator.charAt(1) === "="
|
|
854
|
+
? Version.isSatisfiedWith(targetVersion, sourceVersion)
|
|
855
|
+
: Version.isGreaterThan(targetVersion, sourceVersion));
|
|
814
856
|
}
|
|
815
|
-
return
|
|
857
|
+
return [];
|
|
816
858
|
}
|
|
817
|
-
static
|
|
818
|
-
return
|
|
859
|
+
static isRange(query) {
|
|
860
|
+
return Target.#rangeRegex.test(query);
|
|
819
861
|
}
|
|
820
862
|
}
|
|
821
863
|
|
|
@@ -851,6 +893,12 @@ class Options {
|
|
|
851
893
|
group: 2,
|
|
852
894
|
name: "install",
|
|
853
895
|
},
|
|
896
|
+
{
|
|
897
|
+
brand: "bareTrue",
|
|
898
|
+
description: "Print the list of supported versions of the 'typescript' package and exit.",
|
|
899
|
+
group: 2,
|
|
900
|
+
name: "list",
|
|
901
|
+
},
|
|
854
902
|
{
|
|
855
903
|
brand: "bareTrue",
|
|
856
904
|
description: "Print the list of the selected test files and exit.",
|
|
@@ -879,6 +927,18 @@ class Options {
|
|
|
879
927
|
group: 2,
|
|
880
928
|
name: "prune",
|
|
881
929
|
},
|
|
930
|
+
{
|
|
931
|
+
brand: "boolean",
|
|
932
|
+
description: "Reject the 'any' type passed as an argument to the 'expect()' function or a matcher.",
|
|
933
|
+
group: 4,
|
|
934
|
+
name: "rejectAnyType",
|
|
935
|
+
},
|
|
936
|
+
{
|
|
937
|
+
brand: "boolean",
|
|
938
|
+
description: "Reject the 'never' type passed as an argument to the 'expect()' function or a matcher.",
|
|
939
|
+
group: 4,
|
|
940
|
+
name: "rejectNeverType",
|
|
941
|
+
},
|
|
882
942
|
{
|
|
883
943
|
brand: "list",
|
|
884
944
|
description: "The list of reporters to use.",
|
|
@@ -914,7 +974,6 @@ class Options {
|
|
|
914
974
|
items: {
|
|
915
975
|
brand: "string",
|
|
916
976
|
name: "target",
|
|
917
|
-
pattern: "^([45]\\.[0-9](\\.[0-9])?)|beta|current|latest|next|rc$",
|
|
918
977
|
},
|
|
919
978
|
name: "target",
|
|
920
979
|
},
|
|
@@ -1025,14 +1084,22 @@ class Options {
|
|
|
1025
1084
|
}
|
|
1026
1085
|
onDiagnostics(Diagnostic.error(ConfigDiagnosticText.moduleWasNotFound(optionValue), origin));
|
|
1027
1086
|
break;
|
|
1028
|
-
case "target":
|
|
1087
|
+
case "target": {
|
|
1088
|
+
if (/[<>=]/.test(optionValue)) {
|
|
1089
|
+
if (!Target.isRange(optionValue)) {
|
|
1090
|
+
onDiagnostics(Diagnostic.error([ConfigDiagnosticText.rangeIsNotValid(optionValue), ...ConfigDiagnosticText.rangeUsage()], origin));
|
|
1091
|
+
}
|
|
1092
|
+
break;
|
|
1093
|
+
}
|
|
1029
1094
|
if ((await Store.validateTag(optionValue)) === false) {
|
|
1030
1095
|
onDiagnostics(Diagnostic.error([
|
|
1031
1096
|
ConfigDiagnosticText.versionIsNotSupported(optionValue),
|
|
1032
|
-
|
|
1033
|
-
|
|
1097
|
+
...ConfigDiagnosticText.usage(optionName, optionBrand),
|
|
1098
|
+
ConfigDiagnosticText.inspectSupportedVersions(),
|
|
1099
|
+
], origin));
|
|
1034
1100
|
}
|
|
1035
1101
|
break;
|
|
1102
|
+
}
|
|
1036
1103
|
case "testFileMatch":
|
|
1037
1104
|
for (const segment of ["/", "../"]) {
|
|
1038
1105
|
if (optionValue.startsWith(segment)) {
|
|
@@ -1311,13 +1378,13 @@ class ConfigFileParser {
|
|
|
1311
1378
|
break;
|
|
1312
1379
|
}
|
|
1313
1380
|
case "list": {
|
|
1381
|
+
optionValue = [];
|
|
1314
1382
|
const leftBracketToken = this.#jsonScanner.readToken("[");
|
|
1315
1383
|
if (!leftBracketToken.text) {
|
|
1316
1384
|
jsonNode = this.#jsonScanner.read();
|
|
1317
1385
|
this.#onRequiresValue(optionDefinition, jsonNode, isListItem);
|
|
1318
1386
|
break;
|
|
1319
1387
|
}
|
|
1320
|
-
optionValue = [];
|
|
1321
1388
|
while (!this.#jsonScanner.isRead()) {
|
|
1322
1389
|
if (this.#jsonScanner.peekToken("]")) {
|
|
1323
1390
|
break;
|
|
@@ -1409,6 +1476,8 @@ class ConfigFileParser {
|
|
|
1409
1476
|
const defaultOptions = {
|
|
1410
1477
|
failFast: false,
|
|
1411
1478
|
plugins: [],
|
|
1479
|
+
rejectAnyType: false,
|
|
1480
|
+
rejectNeverType: false,
|
|
1412
1481
|
reporters: ["list", "summary"],
|
|
1413
1482
|
rootPath: Path.resolve("./"),
|
|
1414
1483
|
target: environmentOptions.typescriptModule != null ? ["current"] : ["latest"],
|
|
@@ -1425,6 +1494,9 @@ class Config {
|
|
|
1425
1494
|
const pathMatch = [];
|
|
1426
1495
|
const commandLineParser = new CommandLineParser(commandLineOptions, pathMatch, Config.#onDiagnostics);
|
|
1427
1496
|
await commandLineParser.parse(commandLine);
|
|
1497
|
+
if (commandLineOptions.target != null) {
|
|
1498
|
+
commandLineOptions.target = await Target.expand(commandLineOptions.target);
|
|
1499
|
+
}
|
|
1428
1500
|
return { commandLineOptions, pathMatch };
|
|
1429
1501
|
}
|
|
1430
1502
|
static async parseConfigFile(filePath) {
|
|
@@ -1439,6 +1511,9 @@ class Config {
|
|
|
1439
1511
|
const sourceFile = new SourceFile(configFilePath, configFileText);
|
|
1440
1512
|
const configFileParser = new ConfigFileParser(configFileOptions, sourceFile, Config.#onDiagnostics);
|
|
1441
1513
|
await configFileParser.parse();
|
|
1514
|
+
if (configFileOptions.target != null) {
|
|
1515
|
+
configFileOptions.target = await Target.expand(configFileOptions.target);
|
|
1516
|
+
}
|
|
1442
1517
|
}
|
|
1443
1518
|
return { configFileOptions, configFilePath };
|
|
1444
1519
|
}
|
|
@@ -2182,7 +2257,7 @@ function usesCompilerText(compilerVersion, projectConfigFilePath, options) {
|
|
|
2182
2257
|
if (projectConfigFilePath != null) {
|
|
2183
2258
|
projectConfigPathText = (jsx(Text, { color: "90", children: [" with ", Path.relative("", projectConfigFilePath)] }));
|
|
2184
2259
|
}
|
|
2185
|
-
return (jsx(Text, { children: [options?.prependEmptyLine
|
|
2260
|
+
return (jsx(Text, { children: [options?.prependEmptyLine ? jsx(Line, {}) : undefined, jsx(Line, { children: [jsx(Text, { color: "34", children: "uses" }), " TypeScript ", compilerVersion, projectConfigPathText] }), jsx(Line, {})] }));
|
|
2186
2261
|
}
|
|
2187
2262
|
|
|
2188
2263
|
function waitingForFileChangesText() {
|
|
@@ -2255,7 +2330,7 @@ class FileView {
|
|
|
2255
2330
|
return this.#messages;
|
|
2256
2331
|
}
|
|
2257
2332
|
getViewText(options) {
|
|
2258
|
-
return fileViewText(this.#lines, options?.appendEmptyLine
|
|
2333
|
+
return fileViewText(this.#lines, options?.appendEmptyLine || this.hasErrors);
|
|
2259
2334
|
}
|
|
2260
2335
|
}
|
|
2261
2336
|
|
|
@@ -2413,7 +2488,7 @@ class SetupReporter {
|
|
|
2413
2488
|
|
|
2414
2489
|
class SummaryReporter extends BaseReporter {
|
|
2415
2490
|
on([event, payload]) {
|
|
2416
|
-
if (this.resolvedConfig.watch
|
|
2491
|
+
if (this.resolvedConfig.watch) {
|
|
2417
2492
|
return;
|
|
2418
2493
|
}
|
|
2419
2494
|
if (event === "run:end") {
|
|
@@ -2625,6 +2700,28 @@ class SelectDiagnosticText {
|
|
|
2625
2700
|
|
|
2626
2701
|
class Select {
|
|
2627
2702
|
static #patternsCache = new WeakMap();
|
|
2703
|
+
static async #getAccessibleFileSystemEntries(targetPath) {
|
|
2704
|
+
const directories = [];
|
|
2705
|
+
const files = [];
|
|
2706
|
+
try {
|
|
2707
|
+
const entries = await fs.readdir(targetPath, { withFileTypes: true });
|
|
2708
|
+
for (const entry of entries) {
|
|
2709
|
+
let entryMeta = entry;
|
|
2710
|
+
if (entry.isSymbolicLink()) {
|
|
2711
|
+
entryMeta = await fs.stat([targetPath, entry.name].join("/"));
|
|
2712
|
+
}
|
|
2713
|
+
if (entryMeta.isDirectory()) {
|
|
2714
|
+
directories.push(entry.name);
|
|
2715
|
+
}
|
|
2716
|
+
else if (entryMeta.isFile()) {
|
|
2717
|
+
files.push(entry.name);
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
catch {
|
|
2722
|
+
}
|
|
2723
|
+
return { directories, files };
|
|
2724
|
+
}
|
|
2628
2725
|
static #getMatchPatterns(globPatterns) {
|
|
2629
2726
|
let matchPatterns = Select.#patternsCache.get(globPatterns);
|
|
2630
2727
|
if (!matchPatterns) {
|
|
@@ -2653,18 +2750,6 @@ class Select {
|
|
|
2653
2750
|
static #onDiagnostics(diagnostic) {
|
|
2654
2751
|
EventEmitter.dispatch(["select:error", { diagnostics: [diagnostic] }]);
|
|
2655
2752
|
}
|
|
2656
|
-
static async #resolveEntryMeta(entry, targetPath) {
|
|
2657
|
-
if (!entry.isSymbolicLink()) {
|
|
2658
|
-
return entry;
|
|
2659
|
-
}
|
|
2660
|
-
let entryMeta;
|
|
2661
|
-
try {
|
|
2662
|
-
entryMeta = await fs.stat([targetPath, entry.name].join("/"));
|
|
2663
|
-
}
|
|
2664
|
-
catch {
|
|
2665
|
-
}
|
|
2666
|
-
return entryMeta;
|
|
2667
|
-
}
|
|
2668
2753
|
static async selectFiles(resolvedConfig) {
|
|
2669
2754
|
const matchPatterns = Select.#getMatchPatterns(resolvedConfig.testFileMatch);
|
|
2670
2755
|
const testFilePaths = [];
|
|
@@ -2676,20 +2761,18 @@ class Select {
|
|
|
2676
2761
|
}
|
|
2677
2762
|
static async #visitDirectory(currentPath, testFilePaths, matchPatterns, resolvedConfig) {
|
|
2678
2763
|
const targetPath = Path.join(resolvedConfig.rootPath, currentPath);
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
if (entryMeta?.isDirectory() && Select.#isDirectoryIncluded(entryPath, matchPatterns)) {
|
|
2685
|
-
await Select.#visitDirectory(entryPath, testFilePaths, matchPatterns, resolvedConfig);
|
|
2686
|
-
}
|
|
2687
|
-
else if (entryMeta?.isFile() && Select.#isFileIncluded(entryPath, matchPatterns, resolvedConfig)) {
|
|
2688
|
-
testFilePaths.push([targetPath, entry.name].join("/"));
|
|
2689
|
-
}
|
|
2764
|
+
const entries = await Select.#getAccessibleFileSystemEntries(targetPath);
|
|
2765
|
+
for (const directoryName of entries.directories) {
|
|
2766
|
+
const directoryPath = [currentPath, directoryName].join("/");
|
|
2767
|
+
if (Select.#isDirectoryIncluded(directoryPath, matchPatterns)) {
|
|
2768
|
+
await Select.#visitDirectory(directoryPath, testFilePaths, matchPatterns, resolvedConfig);
|
|
2690
2769
|
}
|
|
2691
2770
|
}
|
|
2692
|
-
|
|
2771
|
+
for (const fileName of entries.files) {
|
|
2772
|
+
const filePath = [currentPath, fileName].join("/");
|
|
2773
|
+
if (Select.#isFileIncluded(filePath, matchPatterns, resolvedConfig)) {
|
|
2774
|
+
testFilePaths.push([targetPath, fileName].join("/"));
|
|
2775
|
+
}
|
|
2693
2776
|
}
|
|
2694
2777
|
}
|
|
2695
2778
|
}
|
|
@@ -2871,14 +2954,20 @@ class TestMember {
|
|
|
2871
2954
|
|
|
2872
2955
|
class Assertion extends TestMember {
|
|
2873
2956
|
isNot;
|
|
2957
|
+
matcherName;
|
|
2874
2958
|
matcherNode;
|
|
2875
2959
|
modifierNode;
|
|
2876
2960
|
notNode;
|
|
2961
|
+
source;
|
|
2962
|
+
target;
|
|
2877
2963
|
constructor(compiler, brand, node, parent, flags, matcherNode, modifierNode, notNode) {
|
|
2878
2964
|
super(compiler, brand, node, parent, flags);
|
|
2879
2965
|
this.isNot = notNode != null;
|
|
2966
|
+
this.matcherName = matcherNode.expression.name;
|
|
2880
2967
|
this.matcherNode = matcherNode;
|
|
2881
2968
|
this.modifierNode = modifierNode;
|
|
2969
|
+
this.source = this.node.typeArguments ?? this.node.arguments;
|
|
2970
|
+
this.target = this.matcherNode.typeArguments ?? this.matcherNode.arguments;
|
|
2882
2971
|
for (const diagnostic of parent.diagnostics) {
|
|
2883
2972
|
if (diagnostic.start != null && diagnostic.start >= this.source.pos && diagnostic.start <= this.source.end) {
|
|
2884
2973
|
this.diagnostics.add(diagnostic);
|
|
@@ -2886,15 +2975,6 @@ class Assertion extends TestMember {
|
|
|
2886
2975
|
}
|
|
2887
2976
|
}
|
|
2888
2977
|
}
|
|
2889
|
-
get matcherName() {
|
|
2890
|
-
return this.matcherNode.expression.name;
|
|
2891
|
-
}
|
|
2892
|
-
get source() {
|
|
2893
|
-
return this.node.typeArguments ?? this.node.arguments;
|
|
2894
|
-
}
|
|
2895
|
-
get target() {
|
|
2896
|
-
return this.matcherNode.typeArguments ?? this.matcherNode.arguments;
|
|
2897
|
-
}
|
|
2898
2978
|
}
|
|
2899
2979
|
|
|
2900
2980
|
class IdentifierLookup {
|
|
@@ -3178,7 +3258,16 @@ class ProjectService {
|
|
|
3178
3258
|
}
|
|
3179
3259
|
}
|
|
3180
3260
|
|
|
3261
|
+
class Format {
|
|
3262
|
+
static capitalize(text) {
|
|
3263
|
+
return text.replace(/^./, text.charAt(0).toUpperCase());
|
|
3264
|
+
}
|
|
3265
|
+
}
|
|
3266
|
+
|
|
3181
3267
|
class ExpectDiagnosticText {
|
|
3268
|
+
static argumentCannotBeOfType(argumentNameText, typeText) {
|
|
3269
|
+
return `An argument for '${argumentNameText}' cannot be of the '${typeText}' type.`;
|
|
3270
|
+
}
|
|
3182
3271
|
static argumentOrTypeArgumentMustBeProvided(argumentNameText, typeArgumentNameText) {
|
|
3183
3272
|
return `An argument for '${argumentNameText}' or type argument for '${typeArgumentNameText}' must be provided.`;
|
|
3184
3273
|
}
|
|
@@ -3209,6 +3298,9 @@ class ExpectDiagnosticText {
|
|
|
3209
3298
|
static raisedTypeError(count = 1) {
|
|
3210
3299
|
return `The raised type error${count === 1 ? "" : "s"}:`;
|
|
3211
3300
|
}
|
|
3301
|
+
static typeArgumentCannotBeOfType(argumentNameText, typeText) {
|
|
3302
|
+
return `A type argument for '${argumentNameText}' cannot be of the '${typeText}' type.`;
|
|
3303
|
+
}
|
|
3212
3304
|
static typeArgumentMustBe(argumentNameText, expectedText) {
|
|
3213
3305
|
return `A type argument for '${argumentNameText}' must be ${expectedText}.`;
|
|
3214
3306
|
}
|
|
@@ -3270,6 +3362,13 @@ class ExpectDiagnosticText {
|
|
|
3270
3362
|
static typesOfPropertyAreNotCompatible(propertyNameText) {
|
|
3271
3363
|
return `Types of property '${propertyNameText}' are not compatible.`;
|
|
3272
3364
|
}
|
|
3365
|
+
static typeWasRejected(typeText) {
|
|
3366
|
+
const optionNameText = `reject${Format.capitalize(typeText)}Type`;
|
|
3367
|
+
return [
|
|
3368
|
+
`The '${typeText}' type was rejected because the '${optionNameText}' option is enabled.`,
|
|
3369
|
+
`If this check is necessary, pass '${typeText}' as the type argument explicitly.`,
|
|
3370
|
+
];
|
|
3371
|
+
}
|
|
3273
3372
|
}
|
|
3274
3373
|
|
|
3275
3374
|
class MatchWorker {
|
|
@@ -3371,9 +3470,6 @@ class MatchWorker {
|
|
|
3371
3470
|
}
|
|
3372
3471
|
return type;
|
|
3373
3472
|
}
|
|
3374
|
-
isAnyOrNeverType(type) {
|
|
3375
|
-
return !!(type.flags & (this.#compiler.TypeFlags.Any | this.#compiler.TypeFlags.Never));
|
|
3376
|
-
}
|
|
3377
3473
|
isStringOrNumberLiteralType(type) {
|
|
3378
3474
|
return !!(type.flags & this.#compiler.TypeFlags.StringOrNumberLiteral);
|
|
3379
3475
|
}
|
|
@@ -3670,7 +3766,8 @@ class ToHaveProperty {
|
|
|
3670
3766
|
match(matchWorker, sourceNode, targetNode, onDiagnostics) {
|
|
3671
3767
|
const diagnostics = [];
|
|
3672
3768
|
const sourceType = matchWorker.getType(sourceNode);
|
|
3673
|
-
if (
|
|
3769
|
+
if (sourceType.flags & (this.#compiler.TypeFlags.Any | this.#compiler.TypeFlags.Never) ||
|
|
3770
|
+
!matchWorker.extendsObjectType(sourceType)) {
|
|
3674
3771
|
const expectedText = "of an object type";
|
|
3675
3772
|
const text = this.#compiler.isTypeNode(sourceNode)
|
|
3676
3773
|
? ExpectDiagnosticText.typeArgumentMustBe("Source", expectedText)
|
|
@@ -3798,6 +3895,7 @@ class ToRaiseError {
|
|
|
3798
3895
|
|
|
3799
3896
|
class ExpectService {
|
|
3800
3897
|
#compiler;
|
|
3898
|
+
#rejectTypes = new Set();
|
|
3801
3899
|
#typeChecker;
|
|
3802
3900
|
toAcceptProps;
|
|
3803
3901
|
toBe;
|
|
@@ -3818,9 +3916,15 @@ class ExpectService {
|
|
|
3818
3916
|
toHaveProperty;
|
|
3819
3917
|
toMatch;
|
|
3820
3918
|
toRaiseError;
|
|
3821
|
-
constructor(compiler, typeChecker) {
|
|
3919
|
+
constructor(compiler, typeChecker, resolvedConfig) {
|
|
3822
3920
|
this.#compiler = compiler;
|
|
3823
3921
|
this.#typeChecker = typeChecker;
|
|
3922
|
+
if (resolvedConfig?.rejectAnyType) {
|
|
3923
|
+
this.#rejectTypes.add("any");
|
|
3924
|
+
}
|
|
3925
|
+
if (resolvedConfig?.rejectNeverType) {
|
|
3926
|
+
this.#rejectTypes.add("never");
|
|
3927
|
+
}
|
|
3824
3928
|
this.toAcceptProps = new ToAcceptProps(compiler, typeChecker);
|
|
3825
3929
|
this.toBe = new ToBe();
|
|
3826
3930
|
this.toBeAny = new PrimitiveTypeMatcher(compiler.TypeFlags.Any);
|
|
@@ -3863,6 +3967,9 @@ class ExpectService {
|
|
|
3863
3967
|
this.#onTargetArgumentOrTypeArgumentMustBeProvided(assertion, onDiagnostics);
|
|
3864
3968
|
return;
|
|
3865
3969
|
}
|
|
3970
|
+
if (this.#rejectsTypeArguments(matchWorker, onDiagnostics)) {
|
|
3971
|
+
return;
|
|
3972
|
+
}
|
|
3866
3973
|
return this[matcherNameText].match(matchWorker, assertion.source[0], assertion.target[0], onDiagnostics);
|
|
3867
3974
|
case "toBeAny":
|
|
3868
3975
|
case "toBeBigInt":
|
|
@@ -3884,6 +3991,9 @@ class ExpectService {
|
|
|
3884
3991
|
}
|
|
3885
3992
|
return this.toHaveProperty.match(matchWorker, assertion.source[0], assertion.target[0], onDiagnostics);
|
|
3886
3993
|
case "toRaiseError":
|
|
3994
|
+
if (assertion.isNot && this.#rejectsTypeArguments(matchWorker, onDiagnostics)) {
|
|
3995
|
+
return;
|
|
3996
|
+
}
|
|
3887
3997
|
return this.toRaiseError.match(matchWorker, assertion.source[0], [...assertion.target], onDiagnostics);
|
|
3888
3998
|
default:
|
|
3889
3999
|
this.#onMatcherIsNotSupported(matcherNameText, assertion, onDiagnostics);
|
|
@@ -3910,6 +4020,29 @@ class ExpectService {
|
|
|
3910
4020
|
const origin = DiagnosticOrigin.fromNode(assertion.matcherName);
|
|
3911
4021
|
onDiagnostics(Diagnostic.error(text, origin));
|
|
3912
4022
|
}
|
|
4023
|
+
#rejectsTypeArguments(matchWorker, onDiagnostics) {
|
|
4024
|
+
for (const rejectedType of this.#rejectTypes) {
|
|
4025
|
+
for (const argumentName of ["source", "target"]) {
|
|
4026
|
+
const argumentNode = matchWorker.assertion[argumentName][0];
|
|
4027
|
+
if (!argumentNode ||
|
|
4028
|
+
argumentNode.kind === this.#compiler.SyntaxKind[`${Format.capitalize(rejectedType)}Keyword`]) {
|
|
4029
|
+
continue;
|
|
4030
|
+
}
|
|
4031
|
+
if (matchWorker.getType(argumentNode).flags & this.#compiler.TypeFlags[Format.capitalize(rejectedType)]) {
|
|
4032
|
+
const text = [
|
|
4033
|
+
this.#compiler.isTypeNode(argumentNode)
|
|
4034
|
+
? ExpectDiagnosticText.typeArgumentCannotBeOfType(Format.capitalize(argumentName), rejectedType)
|
|
4035
|
+
: ExpectDiagnosticText.argumentCannotBeOfType(argumentName, rejectedType),
|
|
4036
|
+
...ExpectDiagnosticText.typeWasRejected(rejectedType),
|
|
4037
|
+
];
|
|
4038
|
+
const origin = DiagnosticOrigin.fromNode(argumentNode);
|
|
4039
|
+
onDiagnostics(Diagnostic.error(text, origin));
|
|
4040
|
+
return true;
|
|
4041
|
+
}
|
|
4042
|
+
}
|
|
4043
|
+
}
|
|
4044
|
+
return false;
|
|
4045
|
+
}
|
|
3913
4046
|
}
|
|
3914
4047
|
|
|
3915
4048
|
class TestTreeWalker {
|
|
@@ -3927,7 +4060,7 @@ class TestTreeWalker {
|
|
|
3927
4060
|
this.#hasOnly = options.hasOnly || resolvedConfig.only != null || options.position != null;
|
|
3928
4061
|
this.#position = options.position;
|
|
3929
4062
|
this.#taskResult = options.taskResult;
|
|
3930
|
-
this.#expectService = new ExpectService(compiler, typeChecker);
|
|
4063
|
+
this.#expectService = new ExpectService(compiler, typeChecker, this.#resolvedConfig);
|
|
3931
4064
|
}
|
|
3932
4065
|
#resolveRunMode(mode, member) {
|
|
3933
4066
|
if (member.flags & 1) {
|
|
@@ -3952,7 +4085,7 @@ class TestTreeWalker {
|
|
|
3952
4085
|
}
|
|
3953
4086
|
visit(members, runMode, parentResult) {
|
|
3954
4087
|
for (const member of members) {
|
|
3955
|
-
if (this.#cancellationToken?.isCancellationRequested
|
|
4088
|
+
if (this.#cancellationToken?.isCancellationRequested) {
|
|
3956
4089
|
break;
|
|
3957
4090
|
}
|
|
3958
4091
|
const validationError = member.validate();
|
|
@@ -4076,7 +4209,7 @@ class TaskRunner {
|
|
|
4076
4209
|
this.#projectService = new ProjectService(this.#resolvedConfig, compiler);
|
|
4077
4210
|
}
|
|
4078
4211
|
run(task, cancellationToken) {
|
|
4079
|
-
if (cancellationToken?.isCancellationRequested
|
|
4212
|
+
if (cancellationToken?.isCancellationRequested) {
|
|
4080
4213
|
return;
|
|
4081
4214
|
}
|
|
4082
4215
|
this.#projectService.openFile(task.filePath, undefined, this.#resolvedConfig.rootPath);
|
|
@@ -4137,7 +4270,7 @@ class TaskRunner {
|
|
|
4137
4270
|
class Runner {
|
|
4138
4271
|
#eventEmitter = new EventEmitter();
|
|
4139
4272
|
#resolvedConfig;
|
|
4140
|
-
static version = "3.
|
|
4273
|
+
static version = "3.1.1";
|
|
4141
4274
|
constructor(resolvedConfig) {
|
|
4142
4275
|
this.#resolvedConfig = resolvedConfig;
|
|
4143
4276
|
}
|
|
@@ -4161,7 +4294,7 @@ class Runner {
|
|
|
4161
4294
|
}
|
|
4162
4295
|
}
|
|
4163
4296
|
}
|
|
4164
|
-
if (this.#resolvedConfig.watch
|
|
4297
|
+
if (this.#resolvedConfig.watch) {
|
|
4165
4298
|
const watchReporter = new WatchReporter(this.#resolvedConfig);
|
|
4166
4299
|
this.#eventEmitter.addReporter(watchReporter);
|
|
4167
4300
|
}
|
|
@@ -4176,7 +4309,7 @@ class Runner {
|
|
|
4176
4309
|
this.#eventEmitter.addHandler(cancellationHandler);
|
|
4177
4310
|
}
|
|
4178
4311
|
await this.#run(tasks, cancellationToken);
|
|
4179
|
-
if (this.#resolvedConfig.watch
|
|
4312
|
+
if (this.#resolvedConfig.watch) {
|
|
4180
4313
|
await this.#watch(tasks, cancellationToken);
|
|
4181
4314
|
}
|
|
4182
4315
|
this.#eventEmitter.removeReporters();
|
|
@@ -4228,6 +4361,13 @@ class Cli {
|
|
|
4228
4361
|
OutputService.writeMessage(formattedText(Runner.version));
|
|
4229
4362
|
return;
|
|
4230
4363
|
}
|
|
4364
|
+
if (commandLine.includes("--list")) {
|
|
4365
|
+
await Store.open();
|
|
4366
|
+
if (Store.manifest != null) {
|
|
4367
|
+
OutputService.writeMessage(formattedText({ resolutions: Store.manifest.resolutions, versions: Store.manifest.versions }));
|
|
4368
|
+
}
|
|
4369
|
+
return;
|
|
4370
|
+
}
|
|
4231
4371
|
if (commandLine.includes("--prune")) {
|
|
4232
4372
|
await Store.prune();
|
|
4233
4373
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tstyche",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"description": "The Essential Type Testing Tool.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -63,17 +63,17 @@
|
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"@biomejs/biome": "1.9.4",
|
|
65
65
|
"@rollup/plugin-typescript": "12.1.1",
|
|
66
|
-
"@types/node": "22.
|
|
67
|
-
"@types/react": "18.3.
|
|
66
|
+
"@types/node": "22.10.1",
|
|
67
|
+
"@types/react": "18.3.13",
|
|
68
68
|
"ajv": "8.17.1",
|
|
69
|
-
"cspell": "8.
|
|
70
|
-
"magic-string": "0.30.
|
|
71
|
-
"monocart-coverage-reports": "2.11.
|
|
72
|
-
"pretty-ansi": "
|
|
73
|
-
"rollup": "4.
|
|
69
|
+
"cspell": "8.16.1",
|
|
70
|
+
"magic-string": "0.30.14",
|
|
71
|
+
"monocart-coverage-reports": "2.11.3",
|
|
72
|
+
"pretty-ansi": "3.0.0",
|
|
73
|
+
"rollup": "4.28.0",
|
|
74
74
|
"rollup-plugin-dts": "6.1.1",
|
|
75
75
|
"tslib": "2.8.1",
|
|
76
|
-
"typescript": "5.
|
|
76
|
+
"typescript": "5.7.2"
|
|
77
77
|
},
|
|
78
78
|
"peerDependencies": {
|
|
79
79
|
"typescript": "4.x || 5.x"
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"optional": true
|
|
84
84
|
}
|
|
85
85
|
},
|
|
86
|
-
"packageManager": "yarn@4.5.
|
|
86
|
+
"packageManager": "yarn@4.5.3",
|
|
87
87
|
"engines": {
|
|
88
88
|
"node": ">=18.19"
|
|
89
89
|
}
|