ts-codemod-lib 3.5.0 → 4.0.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 +32 -9
- package/dist/functions/ast-transformers/append-as-const.d.mts +13 -4
- package/dist/functions/ast-transformers/append-as-const.d.mts.map +1 -1
- package/dist/functions/ast-transformers/append-as-const.mjs +81 -5
- package/dist/functions/ast-transformers/append-as-const.mjs.map +1 -1
- package/dist/functions/ast-transformers/convert-to-readonly.d.mts +33 -4
- package/dist/functions/ast-transformers/convert-to-readonly.d.mts.map +1 -1
- package/dist/functions/ast-transformers/convert-to-readonly.mjs +155 -30
- package/dist/functions/ast-transformers/convert-to-readonly.mjs.map +1 -1
- package/dist/functions/ast-transformers/readonly-transformer-helpers/group-union-types.d.mts +7 -1
- package/dist/functions/ast-transformers/readonly-transformer-helpers/group-union-types.d.mts.map +1 -1
- package/dist/functions/ast-transformers/readonly-transformer-helpers/group-union-types.mjs +25 -2
- package/dist/functions/ast-transformers/readonly-transformer-helpers/group-union-types.mjs.map +1 -1
- package/dist/functions/constants/index.d.mts +1 -0
- package/dist/functions/constants/index.d.mts.map +1 -1
- package/dist/functions/constants/index.mjs +1 -0
- package/dist/functions/constants/index.mjs.map +1 -1
- package/dist/functions/constants/mutable-prefixes.d.mts +23 -0
- package/dist/functions/constants/mutable-prefixes.d.mts.map +1 -0
- package/dist/functions/constants/mutable-prefixes.mjs +28 -0
- package/dist/functions/constants/mutable-prefixes.mjs.map +1 -0
- package/package.json +19 -19
- package/src/functions/ast-transformers/append-as-const.mts +152 -15
- package/src/functions/ast-transformers/append-as-const.test.mts +79 -1
- package/src/functions/ast-transformers/convert-to-readonly.mts +302 -57
- package/src/functions/ast-transformers/convert-to-readonly.test.mts +738 -49
- package/src/functions/ast-transformers/readonly-transformer-helpers/group-union-types.mts +36 -5
- package/src/functions/constants/index.mts +1 -0
- package/src/functions/constants/mutable-prefixes.mts +28 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The default `mutableVariablePrefixes` and `mutablePropertyPrefixes` of the
|
|
3
|
+
* transformers: a name that starts with one of these is left mutable. A
|
|
4
|
+
* private name (`#mut_x`) is matched with or after its `#`.
|
|
5
|
+
*
|
|
6
|
+
* The ESLint counterpart is `ignoredMutablePattern` in
|
|
7
|
+
* `libs/eslint-config-typed/src/rules/eslint-functional-rules.mts` (and, for a
|
|
8
|
+
* property, the `ignoreAccessorPattern` of `functional/immutable-data` there);
|
|
9
|
+
* `libs/eslint-config-typed/src/rules/eslint-functional-rules.test.mts` fails
|
|
10
|
+
* when the two differ; it reads this package's `dist/`, so build first.
|
|
11
|
+
*/
|
|
12
|
+
export declare const DEFAULT_MUTABLE_PREFIXES: readonly string[];
|
|
13
|
+
/**
|
|
14
|
+
* The default `mutableTypePrefixes` of the transformers: a type alias, an
|
|
15
|
+
* interface or a class whose name starts with one of these is left mutable,
|
|
16
|
+
* and `append-as-const` adds no `as const` where the value has to be
|
|
17
|
+
* assignable to it (an annotated variable, `satisfies`, an arrow's return).
|
|
18
|
+
* `Mutable` suits the PascalCase of type names; `mut_` is accepted as for
|
|
19
|
+
* variables. No ESLint rule reads type names, so there is no counterpart to
|
|
20
|
+
* keep in step.
|
|
21
|
+
*/
|
|
22
|
+
export declare const DEFAULT_MUTABLE_TYPE_PREFIXES: readonly string[];
|
|
23
|
+
//# sourceMappingURL=mutable-prefixes.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mutable-prefixes.d.mts","sourceRoot":"","sources":["../../../src/functions/constants/mutable-prefixes.mts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,EAAE,SAAS,MAAM,EAEpD,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,6BAA6B,EAAE,SAAS,MAAM,EAGzD,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
export const DEFAULT_MUTABLE_PREFIXES = Object.freeze([
|
|
13
|
+
'mut_',
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
export const DEFAULT_MUTABLE_TYPE_PREFIXES = Object.freeze([
|
|
25
|
+
'Mutable',
|
|
26
|
+
'mut_',
|
|
27
|
+
]);
|
|
28
|
+
//# sourceMappingURL=mutable-prefixes.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mutable-prefixes.mjs","sourceRoot":"","sources":["../../../src/functions/constants/mutable-prefixes.mts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAsB,MAAM,CAAC,MAAM,CAAC;IACvE,MAAM;CACP,CAAC,CAAC;AAEH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAsB,MAAM,CAAC,MAAM,CAAC;IAC5E,SAAS;IACT,MAAM;CACP,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-codemod-lib",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -38,27 +38,27 @@
|
|
|
38
38
|
"LICENSE"
|
|
39
39
|
],
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"ts-data-forge": "^14.
|
|
41
|
+
"ts-data-forge": "^14.10.0",
|
|
42
42
|
"ts-morph": "^28.0.0",
|
|
43
43
|
"ts-type-forge": "^9.2.2"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@types/node": "26.6.
|
|
47
|
-
"@vitest/browser-playwright": "5.0.
|
|
48
|
-
"@vitest/coverage-v8": "5.0.
|
|
49
|
-
"@vitest/ui": "5.0.
|
|
50
|
-
"cspell": "10.
|
|
46
|
+
"@types/node": "26.6.5",
|
|
47
|
+
"@vitest/browser-playwright": "5.0.5",
|
|
48
|
+
"@vitest/coverage-v8": "5.0.5",
|
|
49
|
+
"@vitest/ui": "5.0.5",
|
|
50
|
+
"cspell": "10.4.0",
|
|
51
51
|
"dedent": "1.7.2",
|
|
52
52
|
"esbuild": "0.28.2",
|
|
53
|
-
"eslint": "10.
|
|
54
|
-
"eslint-config-typed": "
|
|
55
|
-
"eslint-plugin-ts-data-forge": "0.
|
|
56
|
-
"eslint-plugin-ts-fortress": "1.3.
|
|
57
|
-
"eslint-plugin-ts-type-forge": "0.8.
|
|
53
|
+
"eslint": "10.13.0",
|
|
54
|
+
"eslint-config-typed": "6.0.0",
|
|
55
|
+
"eslint-plugin-ts-data-forge": "0.10.4",
|
|
56
|
+
"eslint-plugin-ts-fortress": "1.3.9",
|
|
57
|
+
"eslint-plugin-ts-type-forge": "0.8.14",
|
|
58
58
|
"jiti": "2.7.0",
|
|
59
|
-
"npm-run-all2": "9.0.
|
|
60
|
-
"playwright": "1.64.
|
|
61
|
-
"prettier": "3.9.
|
|
59
|
+
"npm-run-all2": "9.0.4",
|
|
60
|
+
"playwright": "1.64.1",
|
|
61
|
+
"prettier": "3.9.10",
|
|
62
62
|
"prettier-plugin-organize-imports": "4.3.0",
|
|
63
63
|
"prettier-plugin-packagejson": "3.0.2",
|
|
64
64
|
"ts-repo-utils": "10.7.1",
|
|
@@ -67,15 +67,15 @@
|
|
|
67
67
|
"typedoc-github-theme": "0.4.0",
|
|
68
68
|
"typescript": "6.0.3",
|
|
69
69
|
"typescript-native": "npm:typescript@7.0.2",
|
|
70
|
-
"vite": "8.3.
|
|
71
|
-
"vitest": "5.0.
|
|
70
|
+
"vite": "8.3.5",
|
|
71
|
+
"vitest": "5.0.5"
|
|
72
72
|
},
|
|
73
73
|
"engines": {
|
|
74
|
-
"node": ">=22.
|
|
74
|
+
"node": ">=22.23.4",
|
|
75
75
|
"pnpm": ">=9.0.0"
|
|
76
76
|
},
|
|
77
77
|
"volta": {
|
|
78
|
-
"node": "26.12.
|
|
78
|
+
"node": "26.12.1"
|
|
79
79
|
},
|
|
80
80
|
"publishConfig": {
|
|
81
81
|
"access": "public",
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import { Arr, ISet } from 'ts-data-forge';
|
|
1
|
+
import { Arr, ISet, isRecord } from 'ts-data-forge';
|
|
2
2
|
import * as tsm from 'ts-morph';
|
|
3
3
|
import type { DeepReadonly } from 'ts-type-forge';
|
|
4
|
+
import {
|
|
5
|
+
DEFAULT_MUTABLE_PREFIXES,
|
|
6
|
+
DEFAULT_MUTABLE_TYPE_PREFIXES,
|
|
7
|
+
} from '../constants/index.mjs';
|
|
4
8
|
import {
|
|
5
9
|
hasDisableNextLineComment,
|
|
6
10
|
isAsConstNode,
|
|
@@ -16,13 +20,26 @@ const TRANSFORMER_NAME = 'append-as-const';
|
|
|
16
20
|
export const appendAsConstTransformer = (
|
|
17
21
|
options?: AppendAsConstTransformerOptions,
|
|
18
22
|
): TsMorphTransformer => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
// Removed in 4.0.0; a caller without types would otherwise lose it silently.
|
|
24
|
+
const untypedOptions: unknown = options;
|
|
25
|
+
|
|
26
|
+
if (
|
|
27
|
+
isRecord(untypedOptions) &&
|
|
28
|
+
untypedOptions['ignorePrefixes'] !== undefined
|
|
29
|
+
) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`The "ignorePrefixes" option of ${TRANSFORMER_NAME} was renamed to "mutableVariablePrefixes".`,
|
|
32
|
+
);
|
|
33
|
+
}
|
|
22
34
|
|
|
23
35
|
const optionsInternal: AppendAsConstTransformerOptionsInternal = {
|
|
24
36
|
applyLevel: options?.applyLevel ?? 'avoidInFunctionArgs',
|
|
25
|
-
|
|
37
|
+
mutableVariablePrefixes: ISet.create(
|
|
38
|
+
options?.mutableVariablePrefixes ?? DEFAULT_MUTABLE_PREFIXES,
|
|
39
|
+
),
|
|
40
|
+
mutableTypePrefixes: ISet.create(
|
|
41
|
+
options?.mutableTypePrefixes ?? DEFAULT_MUTABLE_TYPE_PREFIXES,
|
|
42
|
+
),
|
|
26
43
|
removeAsConstForConstTypeParameters:
|
|
27
44
|
options?.removeAsConstForConstTypeParameters ?? true,
|
|
28
45
|
|
|
@@ -63,13 +80,23 @@ export type AppendAsConstTransformerOptions = DeepReadonly<{
|
|
|
63
80
|
applyLevel?: 'all' | 'avoidInFunctionArgs';
|
|
64
81
|
|
|
65
82
|
/**
|
|
66
|
-
*
|
|
83
|
+
* Prefixes of the names of variables that are left mutable: their
|
|
84
|
+
* initializers get no `as const`, nor does a value annotated with their type
|
|
85
|
+
* (`typeof mut_x`).
|
|
67
86
|
*
|
|
68
|
-
*
|
|
87
|
+
* @default ['mut_']
|
|
88
|
+
*/
|
|
89
|
+
mutableVariablePrefixes?: string[];
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Prefixes of the names of mutable types (see `convert-to-readonly`): a
|
|
93
|
+
* variable whose type annotation names such a type, an expression that
|
|
94
|
+
* `satisfies` one and an arrow function body returning one get no
|
|
95
|
+
* `as const`, which would make them unassignable to it.
|
|
69
96
|
*
|
|
70
|
-
* @default ['
|
|
97
|
+
* @default ['Mutable', 'mut_']
|
|
71
98
|
*/
|
|
72
|
-
|
|
99
|
+
mutableTypePrefixes?: string[];
|
|
73
100
|
|
|
74
101
|
/**
|
|
75
102
|
* Whether to remove redundant `as const` assertions from call arguments
|
|
@@ -93,7 +120,8 @@ export type AppendAsConstTransformerOptions = DeepReadonly<{
|
|
|
93
120
|
|
|
94
121
|
type AppendAsConstTransformerOptionsInternal = DeepReadonly<{
|
|
95
122
|
applyLevel: 'all' | 'avoidInFunctionArgs';
|
|
96
|
-
|
|
123
|
+
mutableVariablePrefixes: ISet<string>;
|
|
124
|
+
mutableTypePrefixes: ISet<string>;
|
|
97
125
|
removeAsConstForConstTypeParameters: boolean;
|
|
98
126
|
|
|
99
127
|
debugPrint: (...args: readonly unknown[]) => void;
|
|
@@ -143,11 +171,13 @@ const transformNode = (
|
|
|
143
171
|
if (node.isKind(tsm.SyntaxKind.VariableDeclaration)) {
|
|
144
172
|
const nodeName = node.getName();
|
|
145
173
|
|
|
146
|
-
// check for
|
|
147
|
-
if (options.
|
|
174
|
+
// check for mutableVariablePrefixes
|
|
175
|
+
if (options.mutableVariablePrefixes.some((p) => nodeName.startsWith(p))) {
|
|
148
176
|
// Skip conversion for variable declarations with ignored prefixes
|
|
149
177
|
// Example: const mut_foo: string[] = []; -> remains as is, without appending `as const`
|
|
150
|
-
options.debugPrint(
|
|
178
|
+
options.debugPrint(
|
|
179
|
+
'skipped variable declaration by mutableVariablePrefixes',
|
|
180
|
+
);
|
|
151
181
|
|
|
152
182
|
return;
|
|
153
183
|
}
|
|
@@ -167,6 +197,18 @@ const transformNode = (
|
|
|
167
197
|
return;
|
|
168
198
|
}
|
|
169
199
|
|
|
200
|
+
// Example: const foo: MutableFoo = { a: [] }; -> remains as is, because
|
|
201
|
+
// `as const` would make the initializer unassignable to `MutableFoo`
|
|
202
|
+
const typeNode = node.getTypeNode();
|
|
203
|
+
|
|
204
|
+
if (refersToMutableType(typeNode, options)) {
|
|
205
|
+
options.debugPrint(
|
|
206
|
+
'skipped variable declaration annotated with a mutable type',
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
170
212
|
const declarationKindKeywords = node
|
|
171
213
|
.getVariableStatement()
|
|
172
214
|
?.getDeclarationKindKeywords()
|
|
@@ -189,13 +231,13 @@ const transformNode = (
|
|
|
189
231
|
}
|
|
190
232
|
|
|
191
233
|
// const [a, b] = ...;
|
|
192
|
-
// TODO: Support
|
|
234
|
+
// TODO: Support mutableVariablePrefixes in ArrayBindingPattern
|
|
193
235
|
// if (ts.isArrayBindingPattern(nodeName)) {
|
|
194
236
|
// // for (const [i, el] of nodeName.elements.entries())
|
|
195
237
|
// }
|
|
196
238
|
|
|
197
239
|
// const { x, y } = ...;
|
|
198
|
-
// TODO: Support
|
|
240
|
+
// TODO: Support mutableVariablePrefixes in ObjectBindingPattern
|
|
199
241
|
// if (ts.isObjectBindingPattern(nodeName)) {
|
|
200
242
|
// // for (const [i, el] of nodeName.elements.entries())
|
|
201
243
|
// }
|
|
@@ -218,6 +260,14 @@ const transformNode = (
|
|
|
218
260
|
|
|
219
261
|
// pass by ([X satisfies ...] -> X)
|
|
220
262
|
if (tsm.Node.isSatisfiesExpression(node)) {
|
|
263
|
+
// Example: { a: [] } satisfies MutableFoo -> remains as is, because
|
|
264
|
+
// `as const` would make it fail to satisfy `MutableFoo`
|
|
265
|
+
if (refersToMutableType(node.getTypeNode(), options)) {
|
|
266
|
+
options.debugPrint('skipped satisfies expression with a mutable type');
|
|
267
|
+
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
221
271
|
transformNode(node.getExpression(), context, options);
|
|
222
272
|
|
|
223
273
|
return;
|
|
@@ -236,6 +286,14 @@ const transformNode = (
|
|
|
236
286
|
|
|
237
287
|
// pass by arrow function body ([() => X] -> X)
|
|
238
288
|
if (tsm.Node.isArrowFunction(node)) {
|
|
289
|
+
// Example: (): MutableFoo => ({ a: [] }) -> remains as is, because
|
|
290
|
+
// `as const` would make the body unassignable to `MutableFoo`
|
|
291
|
+
if (refersToMutableType(node.getReturnTypeNode(), options)) {
|
|
292
|
+
options.debugPrint('skipped arrow function with a mutable return type');
|
|
293
|
+
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
|
|
239
297
|
const body = node.getBody();
|
|
240
298
|
|
|
241
299
|
transformNode(body, context, options);
|
|
@@ -682,3 +740,82 @@ const isBareConstTypeParameterReference = (
|
|
|
682
740
|
constTypeParameterNames.has(typeName.getText())
|
|
683
741
|
);
|
|
684
742
|
};
|
|
743
|
+
|
|
744
|
+
/** `ns.MutableFoo` -> `MutableFoo` */
|
|
745
|
+
const lastNameOf = (
|
|
746
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
747
|
+
entityName: tsm.EntityName,
|
|
748
|
+
): string =>
|
|
749
|
+
(tsm.Node.isQualifiedName(entityName)
|
|
750
|
+
? entityName.getRight()
|
|
751
|
+
: entityName
|
|
752
|
+
).getText();
|
|
753
|
+
|
|
754
|
+
/** `mut_x.items` -> `mut_x` */
|
|
755
|
+
const firstNameOf = (
|
|
756
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
757
|
+
entityName: tsm.EntityName,
|
|
758
|
+
): string =>
|
|
759
|
+
tsm.Node.isQualifiedName(entityName)
|
|
760
|
+
? firstNameOf(entityName.getLeft())
|
|
761
|
+
: entityName.getText();
|
|
762
|
+
|
|
763
|
+
const startsWithAny = (prefixes: ISet<string>, name: string): boolean =>
|
|
764
|
+
prefixes.some((p) => name.startsWith(p));
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* The name a `typeof` starts from: a variable (`mut_x`) or a class, which
|
|
768
|
+
* `convert-to-readonly` leaves mutable by its type prefix (`MutableStore`).
|
|
769
|
+
*/
|
|
770
|
+
const isMutableValueName = (
|
|
771
|
+
name: string,
|
|
772
|
+
options: AppendAsConstTransformerOptionsInternal,
|
|
773
|
+
): boolean =>
|
|
774
|
+
startsWithAny(options.mutableVariablePrefixes, name) ||
|
|
775
|
+
startsWithAny(options.mutableTypePrefixes, name);
|
|
776
|
+
|
|
777
|
+
/**
|
|
778
|
+
* Whether `typeNode` names a type of a `mutableTypePrefixes` prefix anywhere
|
|
779
|
+
* in it (`MutableFoo`, `ns.MutableFoo[]`, `import('./m').MutableFoo | X`), or
|
|
780
|
+
* the type of a variable of a `mutableVariablePrefixes` prefix or a class of
|
|
781
|
+
* a `mutableTypePrefixes` one (`typeof mut_x`, `typeof MutableStore`). It reads names only, so an alias of another name for such
|
|
782
|
+
* a type is missed.
|
|
783
|
+
*/
|
|
784
|
+
const refersToMutableType = (
|
|
785
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
786
|
+
typeNode: tsm.TypeNode | undefined,
|
|
787
|
+
options: AppendAsConstTransformerOptionsInternal,
|
|
788
|
+
): boolean => {
|
|
789
|
+
if (typeNode === undefined) {
|
|
790
|
+
return false;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
const refers = (n: tsm.Node): boolean => {
|
|
794
|
+
if (n.isKind(tsm.SyntaxKind.TypeReference)) {
|
|
795
|
+
return startsWithAny(
|
|
796
|
+
options.mutableTypePrefixes,
|
|
797
|
+
lastNameOf(n.getTypeName()),
|
|
798
|
+
);
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
if (n.isKind(tsm.SyntaxKind.ImportType)) {
|
|
802
|
+
const qualifier = n.getQualifier();
|
|
803
|
+
|
|
804
|
+
return (
|
|
805
|
+
qualifier !== undefined &&
|
|
806
|
+
// typeof import('./m').mut_x names a value
|
|
807
|
+
(n.compilerNode.isTypeOf
|
|
808
|
+
? isMutableValueName(firstNameOf(qualifier), options)
|
|
809
|
+
: startsWithAny(options.mutableTypePrefixes, lastNameOf(qualifier)))
|
|
810
|
+
);
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// typeof mut_x, typeof mut_x.items, typeof MutableClass
|
|
814
|
+
return (
|
|
815
|
+
n.isKind(tsm.SyntaxKind.TypeQuery) &&
|
|
816
|
+
isMutableValueName(firstNameOf(n.getExprName()), options)
|
|
817
|
+
);
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
return refers(typeNode) || typeNode.getFirstDescendant(refers) !== undefined;
|
|
821
|
+
};
|
|
@@ -1013,7 +1013,25 @@ describe(appendAsConstTransformer, () => {
|
|
|
1013
1013
|
])('$name', testFn);
|
|
1014
1014
|
});
|
|
1015
1015
|
|
|
1016
|
-
describe('
|
|
1016
|
+
describe('mutableVariablePrefixes option (mut_ prefix)', () => {
|
|
1017
|
+
test('the removed ignorePrefixes option is refused', () => {
|
|
1018
|
+
expect(() =>
|
|
1019
|
+
appendAsConstTransformer(
|
|
1020
|
+
// @ts-expect-error removed in 4.0.0
|
|
1021
|
+
{ ignorePrefixes: ['mut_'] },
|
|
1022
|
+
),
|
|
1023
|
+
).toThrow('mutableVariablePrefixes');
|
|
1024
|
+
});
|
|
1025
|
+
|
|
1026
|
+
test('an unset ignorePrefixes option is accepted', () => {
|
|
1027
|
+
expect(() =>
|
|
1028
|
+
appendAsConstTransformer(
|
|
1029
|
+
// @ts-expect-error removed in 4.0.0
|
|
1030
|
+
{ ignorePrefixes: undefined },
|
|
1031
|
+
),
|
|
1032
|
+
).not.toThrow();
|
|
1033
|
+
});
|
|
1034
|
+
|
|
1017
1035
|
test.each([
|
|
1018
1036
|
{
|
|
1019
1037
|
name: 'single variable with mut_ prefix',
|
|
@@ -1025,6 +1043,66 @@ describe(appendAsConstTransformer, () => {
|
|
|
1025
1043
|
source: 'const mut_a = [1, 2], mut_b = { c: 3 };',
|
|
1026
1044
|
expected: 'const mut_a = [1, 2], mut_b = { c: 3 };',
|
|
1027
1045
|
},
|
|
1046
|
+
{
|
|
1047
|
+
name: '"draft" and "_mut_" are not default prefixes',
|
|
1048
|
+
source: 'const draft = { a: [1] }, _mut_b = [2];',
|
|
1049
|
+
expected: 'const draft = { a: [1] } as const, _mut_b = [2] as const;',
|
|
1050
|
+
},
|
|
1051
|
+
{
|
|
1052
|
+
name: 'a variable annotated with a mutable type gets no as const',
|
|
1053
|
+
source: dedent`
|
|
1054
|
+
const a: MutableFoo = { b: [1] };
|
|
1055
|
+
const c: ns.MutableFoo | undefined = { b: [1] };
|
|
1056
|
+
const d: readonly MutableFoo[] = [{ b: [1] }];
|
|
1057
|
+
const f: mut_Foo = { b: [1] };
|
|
1058
|
+
const g: import('./m').MutableFoo = { b: [1] };
|
|
1059
|
+
const h = { b: [1] } satisfies MutableFoo;
|
|
1060
|
+
const i = (): MutableFoo => ({ b: [1] });
|
|
1061
|
+
const j: typeof mut_x = [1];
|
|
1062
|
+
const k: typeof mut_y.items = [1];
|
|
1063
|
+
const l: typeof import('./m').mut_z = [1];
|
|
1064
|
+
const m: InstanceType<typeof MutableStore> = { items: [] };
|
|
1065
|
+
const n: InstanceType<typeof import('./m').MutableStore> = { items: [] };
|
|
1066
|
+
const o = ((): MutableFoo => ({ b: [1] }));
|
|
1067
|
+
const p = cond ? ({ b: [1] } satisfies MutableFoo) : q;
|
|
1068
|
+
const e: Foo = { b: [1] };
|
|
1069
|
+
`,
|
|
1070
|
+
expected: dedent`
|
|
1071
|
+
const a: MutableFoo = { b: [1] };
|
|
1072
|
+
const c: ns.MutableFoo | undefined = { b: [1] };
|
|
1073
|
+
const d: readonly MutableFoo[] = [{ b: [1] }];
|
|
1074
|
+
const f: mut_Foo = { b: [1] };
|
|
1075
|
+
const g: import('./m').MutableFoo = { b: [1] };
|
|
1076
|
+
const h = { b: [1] } satisfies MutableFoo;
|
|
1077
|
+
const i = (): MutableFoo => ({ b: [1] });
|
|
1078
|
+
const j: typeof mut_x = [1];
|
|
1079
|
+
const k: typeof mut_y.items = [1];
|
|
1080
|
+
const l: typeof import('./m').mut_z = [1];
|
|
1081
|
+
const m: InstanceType<typeof MutableStore> = { items: [] };
|
|
1082
|
+
const n: InstanceType<typeof import('./m').MutableStore> = { items: [] };
|
|
1083
|
+
const o = ((): MutableFoo => ({ b: [1] }));
|
|
1084
|
+
const p = cond ? ({ b: [1] } satisfies MutableFoo) : q;
|
|
1085
|
+
const e: Foo = { b: [1] } as const;
|
|
1086
|
+
`,
|
|
1087
|
+
},
|
|
1088
|
+
{
|
|
1089
|
+
name: 'mutableTypePrefixes replaces the default',
|
|
1090
|
+
source: dedent`
|
|
1091
|
+
const a: MutableFoo = { b: [1] };
|
|
1092
|
+
const c: MyFoo = { b: [1] };
|
|
1093
|
+
`,
|
|
1094
|
+
expected: dedent`
|
|
1095
|
+
const a: MutableFoo = { b: [1] } as const;
|
|
1096
|
+
const c: MyFoo = { b: [1] };
|
|
1097
|
+
`,
|
|
1098
|
+
options: { mutableTypePrefixes: ['My'] },
|
|
1099
|
+
},
|
|
1100
|
+
{
|
|
1101
|
+
name: 'the prefixes named in the option replace the default',
|
|
1102
|
+
source: 'const draft = { a: [1] }, mut_b = [2];',
|
|
1103
|
+
expected: 'const draft = { a: [1] }, mut_b = [2] as const;',
|
|
1104
|
+
options: { mutableVariablePrefixes: ['draft'] },
|
|
1105
|
+
},
|
|
1028
1106
|
])('$name', testFn);
|
|
1029
1107
|
});
|
|
1030
1108
|
|