ts-codemod-lib 3.4.0 → 3.4.2
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/dist/functions/ast-transformers/append-as-const.d.mts +2 -2
- package/dist/functions/ast-transformers/append-as-const.d.mts.map +1 -1
- package/dist/functions/ast-transformers/append-as-const.mjs +13 -3
- package/dist/functions/ast-transformers/append-as-const.mjs.map +1 -1
- package/dist/functions/ast-transformers/convert-interface-to-type.d.mts +1 -1
- package/dist/functions/ast-transformers/convert-interface-to-type.d.mts.map +1 -1
- package/dist/functions/ast-transformers/convert-to-readonly.d.mts +2 -2
- package/dist/functions/ast-transformers/convert-to-readonly.d.mts.map +1 -1
- package/dist/functions/ast-transformers/convert-to-readonly.mjs +157 -33
- package/dist/functions/ast-transformers/convert-to-readonly.mjs.map +1 -1
- package/dist/functions/ast-transformers/enable-no-unchecked-indexed-access.d.mts +2 -2
- package/dist/functions/ast-transformers/enable-no-unchecked-indexed-access.d.mts.map +1 -1
- package/dist/functions/ast-transformers/enable-no-unchecked-indexed-access.mjs +24 -6
- package/dist/functions/ast-transformers/enable-no-unchecked-indexed-access.mjs.map +1 -1
- package/dist/functions/ast-transformers/readonly-transformer-helpers/group-union-types.d.mts +32 -2
- 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 +88 -2
- package/dist/functions/ast-transformers/readonly-transformer-helpers/group-union-types.mjs.map +1 -1
- package/dist/functions/ast-transformers/readonly-transformer-helpers/readonly-context.d.mts +1 -1
- package/dist/functions/ast-transformers/readonly-transformer-helpers/readonly-context.d.mts.map +1 -1
- package/dist/functions/ast-transformers/replace-any-with-unknown.d.mts +1 -1
- package/dist/functions/ast-transformers/replace-any-with-unknown.d.mts.map +1 -1
- package/dist/functions/ast-transformers/replace-record-with-unknown-record.d.mts +1 -1
- package/dist/functions/ast-transformers/replace-record-with-unknown-record.d.mts.map +1 -1
- package/dist/functions/ast-transformers/transform-source-code.d.mts +1 -1
- package/dist/functions/ast-transformers/transform-source-code.d.mts.map +1 -1
- package/dist/functions/functions/is-atomic-type-node.d.mts +1 -1
- package/dist/functions/functions/is-atomic-type-node.d.mts.map +1 -1
- package/dist/functions/functions/is-readonly-node.d.mts +1 -1
- package/dist/functions/functions/is-readonly-node.d.mts.map +1 -1
- package/dist/functions/functions/unwrap-readonly.d.mts +1 -1
- package/dist/functions/functions/unwrap-readonly.d.mts.map +1 -1
- package/package.json +14 -14
- package/src/functions/ast-transformers/append-as-const.mts +21 -5
- package/src/functions/ast-transformers/append-as-const.test.mts +24 -0
- package/src/functions/ast-transformers/convert-interface-to-type.mts +1 -1
- package/src/functions/ast-transformers/convert-to-readonly.mts +249 -52
- package/src/functions/ast-transformers/convert-to-readonly.test.mts +421 -7
- package/src/functions/ast-transformers/enable-no-unchecked-indexed-access.mts +30 -8
- package/src/functions/ast-transformers/enable-no-unchecked-indexed-access.test.mts +111 -0
- package/src/functions/ast-transformers/readonly-transformer-helpers/compare-union-types.mts +1 -1
- package/src/functions/ast-transformers/readonly-transformer-helpers/group-union-types.mts +124 -3
- package/src/functions/ast-transformers/readonly-transformer-helpers/readonly-context.mts +1 -1
- package/src/functions/ast-transformers/replace-any-with-unknown.mts +1 -1
- package/src/functions/ast-transformers/replace-record-with-unknown-record.mts +1 -1
- package/src/functions/ast-transformers/transform-source-code.mts +1 -1
- package/src/functions/functions/is-atomic-type-node.mts +1 -1
- package/src/functions/functions/is-readonly-node.mts +1 -1
- package/src/functions/functions/unwrap-readonly.mts +1 -1
|
@@ -11,9 +11,30 @@ import {
|
|
|
11
11
|
type ReadonlyTypeReferenceNode,
|
|
12
12
|
} from '../../functions/index.mjs';
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Sorts the members of a union or intersection into the groups
|
|
16
|
+
* `convert-to-readonly` writes back separately.
|
|
17
|
+
*
|
|
18
|
+
* `typeLiterals` is the group joined and wrapped in a single `Readonly<...>`.
|
|
19
|
+
* It takes type literals, the `Readonly<...>` members that may be merged (see
|
|
20
|
+
* `isMergeableReadonlyTypeReferenceNode`), and, when the union or intersection
|
|
21
|
+
* is itself the argument of a `Readonly<...>` (`underReadonly`), every other
|
|
22
|
+
* member that `Readonly` still means something to (see `staysOutsideReadonly`):
|
|
23
|
+
* taking such a member out of the `Readonly` it was written under would drop
|
|
24
|
+
* its readonly-ness.
|
|
25
|
+
*/
|
|
14
26
|
export const groupUnionIntersectionTypes = (
|
|
15
27
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
16
28
|
types: readonly tsm.TypeNode[],
|
|
29
|
+
{
|
|
30
|
+
operator,
|
|
31
|
+
underReadonly,
|
|
32
|
+
deepReadonlyTypeName,
|
|
33
|
+
}: Readonly<{
|
|
34
|
+
operator: '&' | '|';
|
|
35
|
+
underReadonly: boolean;
|
|
36
|
+
deepReadonlyTypeName: string;
|
|
37
|
+
}>,
|
|
17
38
|
): Readonly<{
|
|
18
39
|
primitives:
|
|
19
40
|
| Readonly<{
|
|
@@ -37,7 +58,7 @@ export const groupUnionIntersectionTypes = (
|
|
|
37
58
|
typeLiterals:
|
|
38
59
|
| Readonly<{
|
|
39
60
|
firstPosition: number;
|
|
40
|
-
nodes: readonly
|
|
61
|
+
nodes: readonly tsm.TypeNode[];
|
|
41
62
|
}>
|
|
42
63
|
| undefined;
|
|
43
64
|
|
|
@@ -71,7 +92,7 @@ export const groupUnionIntersectionTypes = (
|
|
|
71
92
|
typeLiterals:
|
|
72
93
|
| Readonly<{
|
|
73
94
|
firstPosition: number;
|
|
74
|
-
nodes:
|
|
95
|
+
nodes: tsm.TypeNode[];
|
|
75
96
|
}>
|
|
76
97
|
| undefined;
|
|
77
98
|
|
|
@@ -130,7 +151,8 @@ export const groupUnionIntersectionTypes = (
|
|
|
130
151
|
if (
|
|
131
152
|
(t.isKind(tsm.SyntaxKind.TypeLiteral) &&
|
|
132
153
|
!hasCallOrConstructSignature(t)) ||
|
|
133
|
-
|
|
154
|
+
isMergeableReadonlyTypeReferenceNode(t, operator) ||
|
|
155
|
+
(underReadonly && !staysOutsideReadonly(t, deepReadonlyTypeName))
|
|
134
156
|
) {
|
|
135
157
|
if (mut_grouped.typeLiterals === undefined) {
|
|
136
158
|
mut_grouped.typeLiterals = {
|
|
@@ -157,3 +179,102 @@ export const groupUnionIntersectionTypes = (
|
|
|
157
179
|
|
|
158
180
|
return mut_grouped;
|
|
159
181
|
};
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Whether a `Readonly<T>` member of a union or intersection may be merged with
|
|
185
|
+
* the others into one `Readonly<...>`.
|
|
186
|
+
*
|
|
187
|
+
* - In a union, any `T`: `Readonly` distributes over `|`, so `Readonly<A> |
|
|
188
|
+
* Readonly<B>` and `Readonly<A | B>` are the same type.
|
|
189
|
+
* - In an intersection, only a `T` known to be an object type (see
|
|
190
|
+
* `isObjectTypeLiteral`). Over object types `Readonly<A> & Readonly<B>` and
|
|
191
|
+
* `Readonly<A & B>` agree, but `Readonly` maps an array to a readonly array
|
|
192
|
+
* only when its argument _is_ an array, so `Readonly<string[] & X>` leaves the
|
|
193
|
+
* array mutable where `Readonly<string[]> & Readonly<X>` does not. A type
|
|
194
|
+
* parameter or an alias may be such an array.
|
|
195
|
+
*/
|
|
196
|
+
export const isMergeableReadonlyTypeReferenceNode = (
|
|
197
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
198
|
+
node: tsm.TypeNode,
|
|
199
|
+
operator: '&' | '|',
|
|
200
|
+
): node is ReadonlyTypeReferenceNode =>
|
|
201
|
+
isReadonlyTypeReferenceNode(node) &&
|
|
202
|
+
(operator === '|' || isObjectTypeLiteral(node.getTypeArguments()[0]));
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* A type literal or a mapped type known to be an object type, or a union,
|
|
206
|
+
* intersection or `Readonly<...>` of them.
|
|
207
|
+
*/
|
|
208
|
+
const isObjectTypeLiteral = (
|
|
209
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
210
|
+
node: tsm.TypeNode,
|
|
211
|
+
): boolean => {
|
|
212
|
+
const t = removeParentheses(node);
|
|
213
|
+
|
|
214
|
+
if (t.isKind(tsm.SyntaxKind.TypeLiteral)) {
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// A mapped type is an object type unless it maps over `keyof T`, which maps
|
|
219
|
+
// an array `T` to an array.
|
|
220
|
+
if (t.isKind(tsm.SyntaxKind.MappedType)) {
|
|
221
|
+
const constraint = t.getTypeParameter().getConstraint();
|
|
222
|
+
|
|
223
|
+
return !(
|
|
224
|
+
constraint?.isKind(tsm.SyntaxKind.TypeOperator) === true &&
|
|
225
|
+
constraint.getOperator() === tsm.SyntaxKind.KeyOfKeyword
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (isReadonlyTypeReferenceNode(t)) {
|
|
230
|
+
return isObjectTypeLiteral(t.getTypeArguments()[0]);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (
|
|
234
|
+
t.isKind(tsm.SyntaxKind.UnionType) ||
|
|
235
|
+
t.isKind(tsm.SyntaxKind.IntersectionType)
|
|
236
|
+
) {
|
|
237
|
+
return t.getTypeNodes().every(isObjectTypeLiteral);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return false;
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Whether a member of a union or intersection under `Readonly<...>` may be
|
|
245
|
+
* taken out of it as it is.
|
|
246
|
+
*
|
|
247
|
+
* - A function or constructor type, or a type literal with a call or construct
|
|
248
|
+
* signature, loses its signatures under `Readonly`; see
|
|
249
|
+
* `hasCallOrConstructSignature`.
|
|
250
|
+
* - `keyof T` and `unique symbol` are not object types.
|
|
251
|
+
* - `DeepReadonly<T>`, `ReadonlyMap`, `ReadonlySet` and `ReadonlyRecord` are
|
|
252
|
+
* readonly already.
|
|
253
|
+
*/
|
|
254
|
+
const staysOutsideReadonly = (
|
|
255
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
256
|
+
node: tsm.TypeNode,
|
|
257
|
+
deepReadonlyTypeName: string,
|
|
258
|
+
): boolean => {
|
|
259
|
+
if (
|
|
260
|
+
node.isKind(tsm.SyntaxKind.FunctionType) ||
|
|
261
|
+
node.isKind(tsm.SyntaxKind.ConstructorType) ||
|
|
262
|
+
node.isKind(tsm.SyntaxKind.TypeLiteral) ||
|
|
263
|
+
node.isKind(tsm.SyntaxKind.TypeOperator)
|
|
264
|
+
) {
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (node.isKind(tsm.SyntaxKind.TypeReference)) {
|
|
269
|
+
const typeName = node.getTypeName().getText();
|
|
270
|
+
|
|
271
|
+
return (
|
|
272
|
+
typeName === deepReadonlyTypeName ||
|
|
273
|
+
typeName === 'ReadonlyMap' ||
|
|
274
|
+
typeName === 'ReadonlySet' ||
|
|
275
|
+
typeName === 'ReadonlyRecord'
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return false;
|
|
280
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SafeUint, match } from 'ts-data-forge';
|
|
2
|
-
import {
|
|
2
|
+
import type { SafeUintWithSmallInt, StrictExtract } from 'ts-type-forge';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Controls whether to make a layer mutable during recursive transformation
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
isSpreadNamedTupleMemberNode,
|
|
5
5
|
isSpreadParameterNode,
|
|
6
6
|
} from '../functions/index.mjs';
|
|
7
|
-
import {
|
|
7
|
+
import type { TsMorphTransformer } from './types.mjs';
|
|
8
8
|
|
|
9
9
|
const TRANSFORMER_NAME = 'replace-any-with-unknown';
|
|
10
10
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { Arr } from 'ts-data-forge';
|
|
3
3
|
import * as tsm from 'ts-morph';
|
|
4
4
|
import { hasDisableNextLineComment } from '../functions/index.mjs';
|
|
5
|
-
import {
|
|
5
|
+
import type { TsMorphTransformer } from './types.mjs';
|
|
6
6
|
|
|
7
7
|
const TRANSFORMER_NAME = 'replace-record-with-unknown-record';
|
|
8
8
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ISet } from 'ts-data-forge';
|
|
2
2
|
import * as tsm from 'ts-morph';
|
|
3
|
-
import {
|
|
3
|
+
import type { DeepReadonly } from 'ts-type-forge';
|
|
4
4
|
|
|
5
5
|
// Define the set of SyntaxKinds that represent primitive type keywords
|
|
6
6
|
const atomicTypeKeywordKinds = ISet.create<tsm.SyntaxKind>([
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Arr, expectType } from 'ts-data-forge';
|
|
2
2
|
import * as tsm from 'ts-morph';
|
|
3
|
-
import {
|
|
3
|
+
import type { DeepReadonly, StrictOmit } from 'ts-type-forge';
|
|
4
4
|
import { isAtomicTypeNode } from './is-atomic-type-node.mjs';
|
|
5
5
|
|
|
6
6
|
export const isShallowReadonlyTypeNode = (node: tsm.Node): boolean =>
|