tstyche 3.0.0 → 3.1.0
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 +1 -1
- package/build/tstyche.d.ts +55 -7
- package/build/tstyche.js +313 -170
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ 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.
|
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,39 @@ class Store {
|
|
|
758
822
|
}
|
|
759
823
|
}
|
|
760
824
|
|
|
761
|
-
class
|
|
762
|
-
static
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
static moduleWasNotFound(specifier) {
|
|
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 expand(queries) {
|
|
828
|
+
const include = [];
|
|
829
|
+
for (const query of queries) {
|
|
830
|
+
if (!Target.isRange(query)) {
|
|
831
|
+
include.push(query);
|
|
832
|
+
continue;
|
|
833
|
+
}
|
|
834
|
+
if (Store.manifest != null) {
|
|
835
|
+
let versions = Object.keys(Store.manifest.resolutions).slice(0, -4);
|
|
836
|
+
for (const comparator of query.split(" ")) {
|
|
837
|
+
versions = Target.#filter(comparator, versions);
|
|
805
838
|
}
|
|
806
|
-
|
|
839
|
+
include.push(...versions);
|
|
807
840
|
}
|
|
808
841
|
}
|
|
809
|
-
return
|
|
842
|
+
return include;
|
|
810
843
|
}
|
|
811
|
-
static
|
|
812
|
-
|
|
813
|
-
|
|
844
|
+
static #filter(comparator, versions) {
|
|
845
|
+
const targetVersionIndex = versions.findIndex((version) => version === comparator.replace(/^[<>]=?/, ""));
|
|
846
|
+
if (targetVersionIndex !== -1) {
|
|
847
|
+
switch (comparator.charAt(0)) {
|
|
848
|
+
case ">":
|
|
849
|
+
return versions.slice(comparator.charAt(1) === "=" ? targetVersionIndex : targetVersionIndex + 1);
|
|
850
|
+
case "<":
|
|
851
|
+
return versions.slice(0, comparator.charAt(1) === "=" ? targetVersionIndex + 1 : targetVersionIndex);
|
|
852
|
+
}
|
|
814
853
|
}
|
|
815
|
-
return
|
|
854
|
+
return [];
|
|
816
855
|
}
|
|
817
|
-
static
|
|
818
|
-
return
|
|
856
|
+
static isRange(query) {
|
|
857
|
+
return Target.#rangeRegex.test(query);
|
|
819
858
|
}
|
|
820
859
|
}
|
|
821
860
|
|
|
@@ -851,6 +890,12 @@ class Options {
|
|
|
851
890
|
group: 2,
|
|
852
891
|
name: "install",
|
|
853
892
|
},
|
|
893
|
+
{
|
|
894
|
+
brand: "bareTrue",
|
|
895
|
+
description: "Print the list of supported versions of the 'typescript' package and exit.",
|
|
896
|
+
group: 2,
|
|
897
|
+
name: "list",
|
|
898
|
+
},
|
|
854
899
|
{
|
|
855
900
|
brand: "bareTrue",
|
|
856
901
|
description: "Print the list of the selected test files and exit.",
|
|
@@ -879,6 +924,18 @@ class Options {
|
|
|
879
924
|
group: 2,
|
|
880
925
|
name: "prune",
|
|
881
926
|
},
|
|
927
|
+
{
|
|
928
|
+
brand: "boolean",
|
|
929
|
+
description: "Reject the 'any' type passed as an argument to the 'expect()' function or a matcher.",
|
|
930
|
+
group: 4,
|
|
931
|
+
name: "rejectAnyType",
|
|
932
|
+
},
|
|
933
|
+
{
|
|
934
|
+
brand: "boolean",
|
|
935
|
+
description: "Reject the 'never' type passed as an argument to the 'expect()' function or a matcher.",
|
|
936
|
+
group: 4,
|
|
937
|
+
name: "rejectNeverType",
|
|
938
|
+
},
|
|
882
939
|
{
|
|
883
940
|
brand: "list",
|
|
884
941
|
description: "The list of reporters to use.",
|
|
@@ -914,7 +971,6 @@ class Options {
|
|
|
914
971
|
items: {
|
|
915
972
|
brand: "string",
|
|
916
973
|
name: "target",
|
|
917
|
-
pattern: "^([45]\\.[0-9](\\.[0-9])?)|beta|current|latest|next|rc$",
|
|
918
974
|
},
|
|
919
975
|
name: "target",
|
|
920
976
|
},
|
|
@@ -1025,14 +1081,33 @@ class Options {
|
|
|
1025
1081
|
}
|
|
1026
1082
|
onDiagnostics(Diagnostic.error(ConfigDiagnosticText.moduleWasNotFound(optionValue), origin));
|
|
1027
1083
|
break;
|
|
1028
|
-
case "target":
|
|
1084
|
+
case "target": {
|
|
1085
|
+
if (/[<>=]/.test(optionValue)) {
|
|
1086
|
+
if (Target.isRange(optionValue)) {
|
|
1087
|
+
for (const value of optionValue.split(" ").map((value) => value.replace(/^[<>]=?/, ""))) {
|
|
1088
|
+
if ((await Store.validateTag(value)) === false) {
|
|
1089
|
+
onDiagnostics(Diagnostic.error([
|
|
1090
|
+
ConfigDiagnosticText.versionIsNotSupported(value),
|
|
1091
|
+
...ConfigDiagnosticText.usage(optionName, optionBrand),
|
|
1092
|
+
ConfigDiagnosticText.inspectSupportedVersions(),
|
|
1093
|
+
], origin));
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
else {
|
|
1098
|
+
onDiagnostics(Diagnostic.error([ConfigDiagnosticText.rangeIsNotValid(optionValue), ...ConfigDiagnosticText.rangeUsage()], origin));
|
|
1099
|
+
}
|
|
1100
|
+
break;
|
|
1101
|
+
}
|
|
1029
1102
|
if ((await Store.validateTag(optionValue)) === false) {
|
|
1030
1103
|
onDiagnostics(Diagnostic.error([
|
|
1031
1104
|
ConfigDiagnosticText.versionIsNotSupported(optionValue),
|
|
1032
|
-
|
|
1033
|
-
|
|
1105
|
+
...ConfigDiagnosticText.usage(optionName, optionBrand),
|
|
1106
|
+
ConfigDiagnosticText.inspectSupportedVersions(),
|
|
1107
|
+
], origin));
|
|
1034
1108
|
}
|
|
1035
1109
|
break;
|
|
1110
|
+
}
|
|
1036
1111
|
case "testFileMatch":
|
|
1037
1112
|
for (const segment of ["/", "../"]) {
|
|
1038
1113
|
if (optionValue.startsWith(segment)) {
|
|
@@ -1311,13 +1386,13 @@ class ConfigFileParser {
|
|
|
1311
1386
|
break;
|
|
1312
1387
|
}
|
|
1313
1388
|
case "list": {
|
|
1389
|
+
optionValue = [];
|
|
1314
1390
|
const leftBracketToken = this.#jsonScanner.readToken("[");
|
|
1315
1391
|
if (!leftBracketToken.text) {
|
|
1316
1392
|
jsonNode = this.#jsonScanner.read();
|
|
1317
1393
|
this.#onRequiresValue(optionDefinition, jsonNode, isListItem);
|
|
1318
1394
|
break;
|
|
1319
1395
|
}
|
|
1320
|
-
optionValue = [];
|
|
1321
1396
|
while (!this.#jsonScanner.isRead()) {
|
|
1322
1397
|
if (this.#jsonScanner.peekToken("]")) {
|
|
1323
1398
|
break;
|
|
@@ -1409,6 +1484,8 @@ class ConfigFileParser {
|
|
|
1409
1484
|
const defaultOptions = {
|
|
1410
1485
|
failFast: false,
|
|
1411
1486
|
plugins: [],
|
|
1487
|
+
rejectAnyType: false,
|
|
1488
|
+
rejectNeverType: false,
|
|
1412
1489
|
reporters: ["list", "summary"],
|
|
1413
1490
|
rootPath: Path.resolve("./"),
|
|
1414
1491
|
target: environmentOptions.typescriptModule != null ? ["current"] : ["latest"],
|
|
@@ -1453,6 +1530,7 @@ class Config {
|
|
|
1453
1530
|
if ("config" in resolvedConfig) {
|
|
1454
1531
|
delete resolvedConfig.config;
|
|
1455
1532
|
}
|
|
1533
|
+
resolvedConfig.target = Target.expand(resolvedConfig.target);
|
|
1456
1534
|
return resolvedConfig;
|
|
1457
1535
|
}
|
|
1458
1536
|
static resolveConfigFilePath(filePath) {
|
|
@@ -2182,7 +2260,7 @@ function usesCompilerText(compilerVersion, projectConfigFilePath, options) {
|
|
|
2182
2260
|
if (projectConfigFilePath != null) {
|
|
2183
2261
|
projectConfigPathText = (jsx(Text, { color: "90", children: [" with ", Path.relative("", projectConfigFilePath)] }));
|
|
2184
2262
|
}
|
|
2185
|
-
return (jsx(Text, { children: [options?.prependEmptyLine
|
|
2263
|
+
return (jsx(Text, { children: [options?.prependEmptyLine ? jsx(Line, {}) : undefined, jsx(Line, { children: [jsx(Text, { color: "34", children: "uses" }), " TypeScript ", compilerVersion, projectConfigPathText] }), jsx(Line, {})] }));
|
|
2186
2264
|
}
|
|
2187
2265
|
|
|
2188
2266
|
function waitingForFileChangesText() {
|
|
@@ -2255,7 +2333,7 @@ class FileView {
|
|
|
2255
2333
|
return this.#messages;
|
|
2256
2334
|
}
|
|
2257
2335
|
getViewText(options) {
|
|
2258
|
-
return fileViewText(this.#lines, options?.appendEmptyLine
|
|
2336
|
+
return fileViewText(this.#lines, options?.appendEmptyLine || this.hasErrors);
|
|
2259
2337
|
}
|
|
2260
2338
|
}
|
|
2261
2339
|
|
|
@@ -2413,7 +2491,7 @@ class SetupReporter {
|
|
|
2413
2491
|
|
|
2414
2492
|
class SummaryReporter extends BaseReporter {
|
|
2415
2493
|
on([event, payload]) {
|
|
2416
|
-
if (this.resolvedConfig.watch
|
|
2494
|
+
if (this.resolvedConfig.watch) {
|
|
2417
2495
|
return;
|
|
2418
2496
|
}
|
|
2419
2497
|
if (event === "run:end") {
|
|
@@ -2625,6 +2703,28 @@ class SelectDiagnosticText {
|
|
|
2625
2703
|
|
|
2626
2704
|
class Select {
|
|
2627
2705
|
static #patternsCache = new WeakMap();
|
|
2706
|
+
static async #getAccessibleFileSystemEntries(targetPath) {
|
|
2707
|
+
const directories = [];
|
|
2708
|
+
const files = [];
|
|
2709
|
+
try {
|
|
2710
|
+
const entries = await fs.readdir(targetPath, { withFileTypes: true });
|
|
2711
|
+
for (const entry of entries) {
|
|
2712
|
+
let entryMeta = entry;
|
|
2713
|
+
if (entry.isSymbolicLink()) {
|
|
2714
|
+
entryMeta = await fs.stat([targetPath, entry.name].join("/"));
|
|
2715
|
+
}
|
|
2716
|
+
if (entryMeta.isDirectory()) {
|
|
2717
|
+
directories.push(entry.name);
|
|
2718
|
+
}
|
|
2719
|
+
else if (entryMeta.isFile()) {
|
|
2720
|
+
files.push(entry.name);
|
|
2721
|
+
}
|
|
2722
|
+
}
|
|
2723
|
+
}
|
|
2724
|
+
catch {
|
|
2725
|
+
}
|
|
2726
|
+
return { directories, files };
|
|
2727
|
+
}
|
|
2628
2728
|
static #getMatchPatterns(globPatterns) {
|
|
2629
2729
|
let matchPatterns = Select.#patternsCache.get(globPatterns);
|
|
2630
2730
|
if (!matchPatterns) {
|
|
@@ -2653,18 +2753,6 @@ class Select {
|
|
|
2653
2753
|
static #onDiagnostics(diagnostic) {
|
|
2654
2754
|
EventEmitter.dispatch(["select:error", { diagnostics: [diagnostic] }]);
|
|
2655
2755
|
}
|
|
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
2756
|
static async selectFiles(resolvedConfig) {
|
|
2669
2757
|
const matchPatterns = Select.#getMatchPatterns(resolvedConfig.testFileMatch);
|
|
2670
2758
|
const testFilePaths = [];
|
|
@@ -2676,20 +2764,18 @@ class Select {
|
|
|
2676
2764
|
}
|
|
2677
2765
|
static async #visitDirectory(currentPath, testFilePaths, matchPatterns, resolvedConfig) {
|
|
2678
2766
|
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
|
-
}
|
|
2767
|
+
const entries = await Select.#getAccessibleFileSystemEntries(targetPath);
|
|
2768
|
+
for (const directoryName of entries.directories) {
|
|
2769
|
+
const directoryPath = [currentPath, directoryName].join("/");
|
|
2770
|
+
if (Select.#isDirectoryIncluded(directoryPath, matchPatterns)) {
|
|
2771
|
+
await Select.#visitDirectory(directoryPath, testFilePaths, matchPatterns, resolvedConfig);
|
|
2690
2772
|
}
|
|
2691
2773
|
}
|
|
2692
|
-
|
|
2774
|
+
for (const fileName of entries.files) {
|
|
2775
|
+
const filePath = [currentPath, fileName].join("/");
|
|
2776
|
+
if (Select.#isFileIncluded(filePath, matchPatterns, resolvedConfig)) {
|
|
2777
|
+
testFilePaths.push([targetPath, fileName].join("/"));
|
|
2778
|
+
}
|
|
2693
2779
|
}
|
|
2694
2780
|
}
|
|
2695
2781
|
}
|
|
@@ -2871,14 +2957,20 @@ class TestMember {
|
|
|
2871
2957
|
|
|
2872
2958
|
class Assertion extends TestMember {
|
|
2873
2959
|
isNot;
|
|
2960
|
+
matcherName;
|
|
2874
2961
|
matcherNode;
|
|
2875
2962
|
modifierNode;
|
|
2876
2963
|
notNode;
|
|
2964
|
+
source;
|
|
2965
|
+
target;
|
|
2877
2966
|
constructor(compiler, brand, node, parent, flags, matcherNode, modifierNode, notNode) {
|
|
2878
2967
|
super(compiler, brand, node, parent, flags);
|
|
2879
2968
|
this.isNot = notNode != null;
|
|
2969
|
+
this.matcherName = matcherNode.expression.name;
|
|
2880
2970
|
this.matcherNode = matcherNode;
|
|
2881
2971
|
this.modifierNode = modifierNode;
|
|
2972
|
+
this.source = this.node.typeArguments ?? this.node.arguments;
|
|
2973
|
+
this.target = this.matcherNode.typeArguments ?? this.matcherNode.arguments;
|
|
2882
2974
|
for (const diagnostic of parent.diagnostics) {
|
|
2883
2975
|
if (diagnostic.start != null && diagnostic.start >= this.source.pos && diagnostic.start <= this.source.end) {
|
|
2884
2976
|
this.diagnostics.add(diagnostic);
|
|
@@ -2886,15 +2978,6 @@ class Assertion extends TestMember {
|
|
|
2886
2978
|
}
|
|
2887
2979
|
}
|
|
2888
2980
|
}
|
|
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
2981
|
}
|
|
2899
2982
|
|
|
2900
2983
|
class IdentifierLookup {
|
|
@@ -3178,7 +3261,16 @@ class ProjectService {
|
|
|
3178
3261
|
}
|
|
3179
3262
|
}
|
|
3180
3263
|
|
|
3264
|
+
class Format {
|
|
3265
|
+
static capitalize(text) {
|
|
3266
|
+
return text.replace(/^./, text.charAt(0).toUpperCase());
|
|
3267
|
+
}
|
|
3268
|
+
}
|
|
3269
|
+
|
|
3181
3270
|
class ExpectDiagnosticText {
|
|
3271
|
+
static argumentCannotBeOfType(argumentNameText, typeText) {
|
|
3272
|
+
return `An argument for '${argumentNameText}' cannot be of the '${typeText}' type.`;
|
|
3273
|
+
}
|
|
3182
3274
|
static argumentOrTypeArgumentMustBeProvided(argumentNameText, typeArgumentNameText) {
|
|
3183
3275
|
return `An argument for '${argumentNameText}' or type argument for '${typeArgumentNameText}' must be provided.`;
|
|
3184
3276
|
}
|
|
@@ -3209,6 +3301,9 @@ class ExpectDiagnosticText {
|
|
|
3209
3301
|
static raisedTypeError(count = 1) {
|
|
3210
3302
|
return `The raised type error${count === 1 ? "" : "s"}:`;
|
|
3211
3303
|
}
|
|
3304
|
+
static typeArgumentCannotBeOfType(argumentNameText, typeText) {
|
|
3305
|
+
return `A type argument for '${argumentNameText}' cannot be of the '${typeText}' type.`;
|
|
3306
|
+
}
|
|
3212
3307
|
static typeArgumentMustBe(argumentNameText, expectedText) {
|
|
3213
3308
|
return `A type argument for '${argumentNameText}' must be ${expectedText}.`;
|
|
3214
3309
|
}
|
|
@@ -3270,6 +3365,13 @@ class ExpectDiagnosticText {
|
|
|
3270
3365
|
static typesOfPropertyAreNotCompatible(propertyNameText) {
|
|
3271
3366
|
return `Types of property '${propertyNameText}' are not compatible.`;
|
|
3272
3367
|
}
|
|
3368
|
+
static typeWasRejected(typeText) {
|
|
3369
|
+
const optionNameText = `reject${Format.capitalize(typeText)}Type`;
|
|
3370
|
+
return [
|
|
3371
|
+
`The '${typeText}' type was rejected because the '${optionNameText}' option is enabled.`,
|
|
3372
|
+
`If this check is necessary, pass '${typeText}' as the type argument explicitly.`,
|
|
3373
|
+
];
|
|
3374
|
+
}
|
|
3273
3375
|
}
|
|
3274
3376
|
|
|
3275
3377
|
class MatchWorker {
|
|
@@ -3371,9 +3473,6 @@ class MatchWorker {
|
|
|
3371
3473
|
}
|
|
3372
3474
|
return type;
|
|
3373
3475
|
}
|
|
3374
|
-
isAnyOrNeverType(type) {
|
|
3375
|
-
return !!(type.flags & (this.#compiler.TypeFlags.Any | this.#compiler.TypeFlags.Never));
|
|
3376
|
-
}
|
|
3377
3476
|
isStringOrNumberLiteralType(type) {
|
|
3378
3477
|
return !!(type.flags & this.#compiler.TypeFlags.StringOrNumberLiteral);
|
|
3379
3478
|
}
|
|
@@ -3670,7 +3769,8 @@ class ToHaveProperty {
|
|
|
3670
3769
|
match(matchWorker, sourceNode, targetNode, onDiagnostics) {
|
|
3671
3770
|
const diagnostics = [];
|
|
3672
3771
|
const sourceType = matchWorker.getType(sourceNode);
|
|
3673
|
-
if (
|
|
3772
|
+
if (sourceType.flags & (this.#compiler.TypeFlags.Any | this.#compiler.TypeFlags.Never) ||
|
|
3773
|
+
!matchWorker.extendsObjectType(sourceType)) {
|
|
3674
3774
|
const expectedText = "of an object type";
|
|
3675
3775
|
const text = this.#compiler.isTypeNode(sourceNode)
|
|
3676
3776
|
? ExpectDiagnosticText.typeArgumentMustBe("Source", expectedText)
|
|
@@ -3798,6 +3898,7 @@ class ToRaiseError {
|
|
|
3798
3898
|
|
|
3799
3899
|
class ExpectService {
|
|
3800
3900
|
#compiler;
|
|
3901
|
+
#rejectTypes = new Set();
|
|
3801
3902
|
#typeChecker;
|
|
3802
3903
|
toAcceptProps;
|
|
3803
3904
|
toBe;
|
|
@@ -3818,9 +3919,15 @@ class ExpectService {
|
|
|
3818
3919
|
toHaveProperty;
|
|
3819
3920
|
toMatch;
|
|
3820
3921
|
toRaiseError;
|
|
3821
|
-
constructor(compiler, typeChecker) {
|
|
3922
|
+
constructor(compiler, typeChecker, resolvedConfig) {
|
|
3822
3923
|
this.#compiler = compiler;
|
|
3823
3924
|
this.#typeChecker = typeChecker;
|
|
3925
|
+
if (resolvedConfig?.rejectAnyType) {
|
|
3926
|
+
this.#rejectTypes.add("any");
|
|
3927
|
+
}
|
|
3928
|
+
if (resolvedConfig?.rejectNeverType) {
|
|
3929
|
+
this.#rejectTypes.add("never");
|
|
3930
|
+
}
|
|
3824
3931
|
this.toAcceptProps = new ToAcceptProps(compiler, typeChecker);
|
|
3825
3932
|
this.toBe = new ToBe();
|
|
3826
3933
|
this.toBeAny = new PrimitiveTypeMatcher(compiler.TypeFlags.Any);
|
|
@@ -3863,6 +3970,9 @@ class ExpectService {
|
|
|
3863
3970
|
this.#onTargetArgumentOrTypeArgumentMustBeProvided(assertion, onDiagnostics);
|
|
3864
3971
|
return;
|
|
3865
3972
|
}
|
|
3973
|
+
if (this.#rejectsTypeArguments(matchWorker, onDiagnostics)) {
|
|
3974
|
+
return;
|
|
3975
|
+
}
|
|
3866
3976
|
return this[matcherNameText].match(matchWorker, assertion.source[0], assertion.target[0], onDiagnostics);
|
|
3867
3977
|
case "toBeAny":
|
|
3868
3978
|
case "toBeBigInt":
|
|
@@ -3884,6 +3994,9 @@ class ExpectService {
|
|
|
3884
3994
|
}
|
|
3885
3995
|
return this.toHaveProperty.match(matchWorker, assertion.source[0], assertion.target[0], onDiagnostics);
|
|
3886
3996
|
case "toRaiseError":
|
|
3997
|
+
if (assertion.isNot && this.#rejectsTypeArguments(matchWorker, onDiagnostics)) {
|
|
3998
|
+
return;
|
|
3999
|
+
}
|
|
3887
4000
|
return this.toRaiseError.match(matchWorker, assertion.source[0], [...assertion.target], onDiagnostics);
|
|
3888
4001
|
default:
|
|
3889
4002
|
this.#onMatcherIsNotSupported(matcherNameText, assertion, onDiagnostics);
|
|
@@ -3910,6 +4023,29 @@ class ExpectService {
|
|
|
3910
4023
|
const origin = DiagnosticOrigin.fromNode(assertion.matcherName);
|
|
3911
4024
|
onDiagnostics(Diagnostic.error(text, origin));
|
|
3912
4025
|
}
|
|
4026
|
+
#rejectsTypeArguments(matchWorker, onDiagnostics) {
|
|
4027
|
+
for (const rejectedType of this.#rejectTypes) {
|
|
4028
|
+
for (const argumentName of ["source", "target"]) {
|
|
4029
|
+
const argumentNode = matchWorker.assertion[argumentName][0];
|
|
4030
|
+
if (!argumentNode ||
|
|
4031
|
+
argumentNode.kind === this.#compiler.SyntaxKind[`${Format.capitalize(rejectedType)}Keyword`]) {
|
|
4032
|
+
continue;
|
|
4033
|
+
}
|
|
4034
|
+
if (matchWorker.getType(argumentNode).flags & this.#compiler.TypeFlags[Format.capitalize(rejectedType)]) {
|
|
4035
|
+
const text = [
|
|
4036
|
+
this.#compiler.isTypeNode(argumentNode)
|
|
4037
|
+
? ExpectDiagnosticText.typeArgumentCannotBeOfType(Format.capitalize(argumentName), rejectedType)
|
|
4038
|
+
: ExpectDiagnosticText.argumentCannotBeOfType(argumentName, rejectedType),
|
|
4039
|
+
...ExpectDiagnosticText.typeWasRejected(rejectedType),
|
|
4040
|
+
];
|
|
4041
|
+
const origin = DiagnosticOrigin.fromNode(argumentNode);
|
|
4042
|
+
onDiagnostics(Diagnostic.error(text, origin));
|
|
4043
|
+
return true;
|
|
4044
|
+
}
|
|
4045
|
+
}
|
|
4046
|
+
}
|
|
4047
|
+
return false;
|
|
4048
|
+
}
|
|
3913
4049
|
}
|
|
3914
4050
|
|
|
3915
4051
|
class TestTreeWalker {
|
|
@@ -3927,7 +4063,7 @@ class TestTreeWalker {
|
|
|
3927
4063
|
this.#hasOnly = options.hasOnly || resolvedConfig.only != null || options.position != null;
|
|
3928
4064
|
this.#position = options.position;
|
|
3929
4065
|
this.#taskResult = options.taskResult;
|
|
3930
|
-
this.#expectService = new ExpectService(compiler, typeChecker);
|
|
4066
|
+
this.#expectService = new ExpectService(compiler, typeChecker, this.#resolvedConfig);
|
|
3931
4067
|
}
|
|
3932
4068
|
#resolveRunMode(mode, member) {
|
|
3933
4069
|
if (member.flags & 1) {
|
|
@@ -3952,7 +4088,7 @@ class TestTreeWalker {
|
|
|
3952
4088
|
}
|
|
3953
4089
|
visit(members, runMode, parentResult) {
|
|
3954
4090
|
for (const member of members) {
|
|
3955
|
-
if (this.#cancellationToken?.isCancellationRequested
|
|
4091
|
+
if (this.#cancellationToken?.isCancellationRequested) {
|
|
3956
4092
|
break;
|
|
3957
4093
|
}
|
|
3958
4094
|
const validationError = member.validate();
|
|
@@ -4076,7 +4212,7 @@ class TaskRunner {
|
|
|
4076
4212
|
this.#projectService = new ProjectService(this.#resolvedConfig, compiler);
|
|
4077
4213
|
}
|
|
4078
4214
|
run(task, cancellationToken) {
|
|
4079
|
-
if (cancellationToken?.isCancellationRequested
|
|
4215
|
+
if (cancellationToken?.isCancellationRequested) {
|
|
4080
4216
|
return;
|
|
4081
4217
|
}
|
|
4082
4218
|
this.#projectService.openFile(task.filePath, undefined, this.#resolvedConfig.rootPath);
|
|
@@ -4137,7 +4273,7 @@ class TaskRunner {
|
|
|
4137
4273
|
class Runner {
|
|
4138
4274
|
#eventEmitter = new EventEmitter();
|
|
4139
4275
|
#resolvedConfig;
|
|
4140
|
-
static version = "3.
|
|
4276
|
+
static version = "3.1.0";
|
|
4141
4277
|
constructor(resolvedConfig) {
|
|
4142
4278
|
this.#resolvedConfig = resolvedConfig;
|
|
4143
4279
|
}
|
|
@@ -4161,7 +4297,7 @@ class Runner {
|
|
|
4161
4297
|
}
|
|
4162
4298
|
}
|
|
4163
4299
|
}
|
|
4164
|
-
if (this.#resolvedConfig.watch
|
|
4300
|
+
if (this.#resolvedConfig.watch) {
|
|
4165
4301
|
const watchReporter = new WatchReporter(this.#resolvedConfig);
|
|
4166
4302
|
this.#eventEmitter.addReporter(watchReporter);
|
|
4167
4303
|
}
|
|
@@ -4176,7 +4312,7 @@ class Runner {
|
|
|
4176
4312
|
this.#eventEmitter.addHandler(cancellationHandler);
|
|
4177
4313
|
}
|
|
4178
4314
|
await this.#run(tasks, cancellationToken);
|
|
4179
|
-
if (this.#resolvedConfig.watch
|
|
4315
|
+
if (this.#resolvedConfig.watch) {
|
|
4180
4316
|
await this.#watch(tasks, cancellationToken);
|
|
4181
4317
|
}
|
|
4182
4318
|
this.#eventEmitter.removeReporters();
|
|
@@ -4228,6 +4364,13 @@ class Cli {
|
|
|
4228
4364
|
OutputService.writeMessage(formattedText(Runner.version));
|
|
4229
4365
|
return;
|
|
4230
4366
|
}
|
|
4367
|
+
if (commandLine.includes("--list")) {
|
|
4368
|
+
await Store.open();
|
|
4369
|
+
if (Store.manifest != null) {
|
|
4370
|
+
OutputService.writeMessage(formattedText({ resolutions: Store.manifest.resolutions, versions: Store.manifest.versions }));
|
|
4371
|
+
}
|
|
4372
|
+
return;
|
|
4373
|
+
}
|
|
4231
4374
|
if (commandLine.includes("--prune")) {
|
|
4232
4375
|
await Store.prune();
|
|
4233
4376
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tstyche",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.0",
|
|
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.
|
|
66
|
+
"@types/node": "22.10.1",
|
|
67
67
|
"@types/react": "18.3.12",
|
|
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
|
}
|