ts-codemod-lib 3.3.0 → 3.3.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.
Files changed (69) hide show
  1. package/dist/entry-point.mjs +2 -24
  2. package/dist/entry-point.mjs.map +1 -1
  3. package/dist/functions/ast-transformers/append-as-const.mjs +330 -292
  4. package/dist/functions/ast-transformers/append-as-const.mjs.map +1 -1
  5. package/dist/functions/ast-transformers/convert-interface-to-type.mjs +85 -70
  6. package/dist/functions/ast-transformers/convert-interface-to-type.mjs.map +1 -1
  7. package/dist/functions/ast-transformers/convert-to-readonly.mjs +969 -840
  8. package/dist/functions/ast-transformers/convert-to-readonly.mjs.map +1 -1
  9. package/dist/functions/ast-transformers/enable-no-unchecked-indexed-access.mjs +276 -134
  10. package/dist/functions/ast-transformers/enable-no-unchecked-indexed-access.mjs.map +1 -1
  11. package/dist/functions/ast-transformers/index.mjs +10 -12
  12. package/dist/functions/ast-transformers/index.mjs.map +1 -1
  13. package/dist/functions/ast-transformers/readonly-transformer-helpers/compare-union-types.mjs +14 -7
  14. package/dist/functions/ast-transformers/readonly-transformer-helpers/compare-union-types.mjs.map +1 -1
  15. package/dist/functions/ast-transformers/readonly-transformer-helpers/constants.mjs +12 -15
  16. package/dist/functions/ast-transformers/readonly-transformer-helpers/constants.mjs.map +1 -1
  17. package/dist/functions/ast-transformers/readonly-transformer-helpers/group-union-types.mjs +63 -58
  18. package/dist/functions/ast-transformers/readonly-transformer-helpers/group-union-types.mjs.map +1 -1
  19. package/dist/functions/ast-transformers/readonly-transformer-helpers/index.mjs +5 -5
  20. package/dist/functions/ast-transformers/readonly-transformer-helpers/index.mjs.map +1 -1
  21. package/dist/functions/ast-transformers/readonly-transformer-helpers/readonly-context.mjs +15 -20
  22. package/dist/functions/ast-transformers/readonly-transformer-helpers/readonly-context.mjs.map +1 -1
  23. package/dist/functions/ast-transformers/replace-any-with-unknown.mjs +30 -34
  24. package/dist/functions/ast-transformers/replace-any-with-unknown.mjs.map +1 -1
  25. package/dist/functions/ast-transformers/replace-record-with-unknown-record.mjs +197 -156
  26. package/dist/functions/ast-transformers/replace-record-with-unknown-record.mjs.map +1 -1
  27. package/dist/functions/ast-transformers/transform-source-code.mjs +74 -75
  28. package/dist/functions/ast-transformers/transform-source-code.mjs.map +1 -1
  29. package/dist/functions/ast-transformers/types.mjs +2 -2
  30. package/dist/functions/ast-transformers/types.mjs.map +1 -1
  31. package/dist/functions/constants/ignore-comment-text.mjs +11 -13
  32. package/dist/functions/constants/ignore-comment-text.mjs.map +1 -1
  33. package/dist/functions/constants/index.mjs +2 -2
  34. package/dist/functions/constants/index.mjs.map +1 -1
  35. package/dist/functions/functions/has-disable-next-line-comment.mjs +67 -38
  36. package/dist/functions/functions/has-disable-next-line-comment.mjs.map +1 -1
  37. package/dist/functions/functions/index.mjs +11 -11
  38. package/dist/functions/functions/index.mjs.map +1 -1
  39. package/dist/functions/functions/is-as-const-node.mjs +25 -20
  40. package/dist/functions/functions/is-as-const-node.mjs.map +1 -1
  41. package/dist/functions/functions/is-atomic-type-node.mjs +245 -25
  42. package/dist/functions/functions/is-atomic-type-node.mjs.map +1 -1
  43. package/dist/functions/functions/is-conditional-type-distribution-guard.mjs +34 -13
  44. package/dist/functions/functions/is-conditional-type-distribution-guard.mjs.map +1 -1
  45. package/dist/functions/functions/is-readonly-node.mjs +120 -14
  46. package/dist/functions/functions/is-readonly-node.mjs.map +1 -1
  47. package/dist/functions/functions/is-spread-parameter-node.mjs +5 -6
  48. package/dist/functions/functions/is-spread-parameter-node.mjs.map +1 -1
  49. package/dist/functions/functions/remove-parentheses.mjs +4 -4
  50. package/dist/functions/functions/remove-parentheses.mjs.map +1 -1
  51. package/dist/functions/functions/should-avoid-parentheses-for-readonly.mjs +23 -9
  52. package/dist/functions/functions/should-avoid-parentheses-for-readonly.mjs.map +1 -1
  53. package/dist/functions/functions/unwrap-readonly.mjs +3 -4
  54. package/dist/functions/functions/unwrap-readonly.mjs.map +1 -1
  55. package/dist/functions/functions/wrap-with-parentheses.d.mts.map +1 -1
  56. package/dist/functions/functions/wrap-with-parentheses.mjs +54 -29
  57. package/dist/functions/functions/wrap-with-parentheses.mjs.map +1 -1
  58. package/dist/functions/index.mjs +5 -24
  59. package/dist/functions/index.mjs.map +1 -1
  60. package/dist/functions/utils/index.mjs +2 -2
  61. package/dist/functions/utils/index.mjs.map +1 -1
  62. package/dist/functions/utils/replace-with-debug.mjs +4 -6
  63. package/dist/functions/utils/replace-with-debug.mjs.map +1 -1
  64. package/dist/index.mjs +2 -24
  65. package/dist/index.mjs.map +1 -1
  66. package/package.json +11 -12
  67. package/src/functions/ast-transformers/convert-to-readonly.test.mts +10 -1
  68. package/src/functions/ast-transformers/enable-no-unchecked-indexed-access.test.mts +76 -22
  69. package/src/functions/functions/wrap-with-parentheses.mts +5 -2
@@ -1,873 +1,1002 @@
1
- import { ISet, Arr, pipe, match, mapNullable, isString } from 'ts-data-forge';
1
+ import { Arr, ISet, isString, mapNullable, match, pipe, } from 'ts-data-forge';
2
2
  import * as tsm from 'ts-morph';
3
- import { hasDisableNextLineComment } from '../functions/has-disable-next-line-comment.mjs';
4
- import { isAtomicTypeNode } from '../functions/is-atomic-type-node.mjs';
5
- import { isConditionalTypeDistributionGuard } from '../functions/is-conditional-type-distribution-guard.mjs';
6
- import { isReadonlyTypeReferenceNode, isReadonlyTupleOrArrayTypeNode, isReadonlyArrayTypeNode, isReadonlyTupleTypeNode } from '../functions/is-readonly-node.mjs';
7
- import { removeParentheses } from '../functions/remove-parentheses.mjs';
8
- import { shouldAvoidParenthesesForReadonly } from '../functions/should-avoid-parentheses-for-readonly.mjs';
9
- import { unwrapReadonlyTypeArgText } from '../functions/unwrap-readonly.mjs';
10
- import { wrapWithParentheses } from '../functions/wrap-with-parentheses.mjs';
11
- import { replaceNodeWithDebugPrint } from '../utils/replace-with-debug.mjs';
12
- import { invalidDeepReadonlyTypeName } from './readonly-transformer-helpers/constants.mjs';
13
- import { groupUnionIntersectionTypes } from './readonly-transformer-helpers/group-union-types.mjs';
14
- import { nextReadonlyContext } from './readonly-transformer-helpers/readonly-context.mjs';
15
-
16
- const TRANSFORMER_NAME = "convert-to-readonly";
17
- const convertToReadonlyTransformer = (options) => {
18
- if (options?.DeepReadonly?.typeName !== void 0 && invalidDeepReadonlyTypeName.has(options.DeepReadonly.typeName)) {
19
- throw new Error(
20
- `Invalid DeepReadonly typeName "${options.DeepReadonly.typeName}" passed to convertToReadonlyType`
21
- );
22
- }
23
- const DeepReadonlyTypeName = options?.DeepReadonly?.typeName ?? "DeepReadonly";
24
- const ignorePrefixes = ISet.create(
25
- options?.ignorePrefixes ?? ["mut_", "#mut_", "_mut_", "draft"]
26
- );
27
- const optionsInternal = {
28
- DeepReadonly: {
29
- typeName: DeepReadonlyTypeName,
30
- applyLevel: "keep"
31
- },
32
- ignoreEmptyObjectTypes: options?.ignoreEmptyObjectTypes ?? true,
33
- ignoredPrefixes: ignorePrefixes,
34
- recordStyle: options?.recordStyle ?? "ReadonlyRecord",
35
- debugPrint: options?.debug === true ? console.debug : () => {
36
- },
37
- replaceNode: options?.debug === true ? replaceNodeWithDebugPrint : (node, newNodeText) => node.replaceWithText(newNodeText)
38
- };
39
- return {
40
- name: TRANSFORMER_NAME,
41
- transform: (sourceAst) => {
42
- for (const node of sourceAst.getChildren()) {
43
- transformNode(node, initialReadonlyContext, optionsInternal);
44
- }
45
- }
46
- };
3
+ import { hasDisableNextLineComment, isAtomicTypeNode, isConditionalTypeDistributionGuard, isReadonlyArrayTypeNode, isReadonlyTupleOrArrayTypeNode, isReadonlyTupleTypeNode, isReadonlyTypeReferenceNode, removeParentheses, shouldAvoidParenthesesForReadonly, unwrapReadonlyTypeArgText, wrapWithParentheses, } from '../functions/index.mjs';
4
+ import { replaceNodeWithDebugPrint } from '../utils/index.mjs';
5
+ import { groupUnionIntersectionTypes, invalidDeepReadonlyTypeName, nextReadonlyContext, } from './readonly-transformer-helpers/index.mjs';
6
+ const TRANSFORMER_NAME = 'convert-to-readonly';
7
+ export const convertToReadonlyTransformer = (options) => {
8
+ if (options?.DeepReadonly?.typeName !== undefined &&
9
+ invalidDeepReadonlyTypeName.has(options.DeepReadonly.typeName)) {
10
+ throw new Error(`Invalid DeepReadonly typeName "${options.DeepReadonly.typeName}" passed to convertToReadonlyType`);
11
+ }
12
+ const DeepReadonlyTypeName = options?.DeepReadonly?.typeName ?? 'DeepReadonly';
13
+ const ignorePrefixes = ISet.create(options?.ignorePrefixes ?? ['mut_', '#mut_', '_mut_', 'draft']);
14
+ const optionsInternal = {
15
+ DeepReadonly: {
16
+ typeName: DeepReadonlyTypeName,
17
+ applyLevel: 'keep',
18
+ },
19
+ ignoreEmptyObjectTypes: options?.ignoreEmptyObjectTypes ?? true,
20
+ ignoredPrefixes: ignorePrefixes,
21
+ recordStyle: options?.recordStyle ?? 'ReadonlyRecord',
22
+ debugPrint: options?.debug === true ? console.debug : () => { },
23
+ replaceNode: options?.debug === true
24
+ ? replaceNodeWithDebugPrint
25
+ : (node, newNodeText) => node.replaceWithText(newNodeText),
26
+ };
27
+ return {
28
+ name: TRANSFORMER_NAME,
29
+ transform: (sourceAst) => {
30
+ for (const node of sourceAst.getChildren()) {
31
+ transformNode(node, initialReadonlyContext, optionsInternal);
32
+ }
33
+ },
34
+ };
47
35
  };
48
36
  const initialReadonlyContext = {
49
- type: "none",
50
- indexedAccessDepth: 0
37
+ type: 'none',
38
+ indexedAccessDepth: 0,
51
39
  };
52
40
  const transformNode = (node, readonlyContext, options) => {
53
- if (tsm.Node.isTypeNode(node) || node.isKind(tsm.SyntaxKind.InterfaceDeclaration) || node.isKind(tsm.SyntaxKind.ClassDeclaration)) {
54
- options.debugPrint();
55
- options.debugPrint(`transformNode [${node.getKindName()}] `);
56
- options.debugPrint(node.getFullText());
57
- options.debugPrint({ readonlyContext });
58
- options.debugPrint();
59
- }
60
- if (hasDisableNextLineComment(node, TRANSFORMER_NAME)) {
61
- options.debugPrint("skipped by disable-next-line comment");
62
- return;
63
- }
64
- if (node.isKind(tsm.SyntaxKind.VariableDeclaration)) {
65
- const nodeName = node.getName();
66
- if (options.ignoredPrefixes.some((p) => nodeName.startsWith(p))) {
67
- options.debugPrint("skipped variable declaration by ignorePrefixes");
68
- return;
69
- }
70
- }
71
- if ((node.isKind(tsm.SyntaxKind.FunctionDeclaration) || node.isKind(tsm.SyntaxKind.FunctionExpression)) && options.ignoredPrefixes.some((p) => node.getName()?.startsWith(p) === true)) {
72
- return;
73
- }
74
- if (node.isKind(tsm.SyntaxKind.Parameter)) {
75
- const nodeName = node.getName();
76
- if (options.ignoredPrefixes.some((p) => nodeName.startsWith(p))) {
77
- return;
78
- }
79
- }
80
- if (node.isKind(tsm.SyntaxKind.TypeAliasDeclaration) && options.ignoredPrefixes.some((p) => node.getName().startsWith(p))) {
81
- return;
82
- }
83
- if (node.isKind(tsm.SyntaxKind.TypeReference)) {
84
- if (readonlyContext.type === "readonly") {
85
- throw new Error(
86
- 'Invalid readonlyContext "readonly" passed to TypeReferenceNode'
87
- );
88
- }
89
- transformTypeReferenceNode(node, readonlyContext, options);
90
- return;
91
- }
92
- if (node.isKind(tsm.SyntaxKind.ArrayType)) {
93
- transformArrayTypeNode(node, readonlyContext, options);
94
- return;
95
- }
96
- if (node.isKind(tsm.SyntaxKind.TupleType)) {
97
- transformTupleTypeNode(node, readonlyContext, options);
98
- return;
99
- }
100
- if (node.isKind(tsm.SyntaxKind.RestType)) {
101
- transformRestTypeNode(node, readonlyContext, options);
102
- return;
103
- }
104
- if (node.isKind(tsm.SyntaxKind.TypeOperator)) {
105
- if (readonlyContext.type === "readonly") {
106
- throw new Error(
107
- 'Invalid readonlyContext "readonly" passed to TypeOperatorNode'
108
- );
109
- }
110
- transformTypeOperatorNode(node, readonlyContext, options);
111
- return;
112
- }
113
- if (node.isKind(tsm.SyntaxKind.TypeLiteral)) {
114
- if (readonlyContext.type === "readonly") {
115
- throw new Error(
116
- 'Invalid readonlyContext "readonly" passed to TypeLiteralNode'
117
- );
118
- }
119
- transformTypeLiteralNode(node, readonlyContext, options);
120
- return;
121
- }
122
- if (node.isKind(tsm.SyntaxKind.IndexedAccessType)) {
123
- transformIndexedAccessTypeNode(node, readonlyContext, options);
124
- return;
125
- }
126
- if (node.isKind(tsm.SyntaxKind.MappedType)) {
127
- if (readonlyContext.type === "readonly") {
128
- throw new Error(
129
- 'Invalid readonlyContext "readonly" passed to MappedTypeNode'
130
- );
131
- }
132
- transformMappedTypeNode(node, readonlyContext, options);
133
- return;
134
- }
135
- if (node.isKind(tsm.SyntaxKind.InterfaceDeclaration)) {
136
- if (options.ignoredPrefixes.some((p) => node.getName().startsWith(p))) {
137
- return;
138
- }
139
- transformInterfaceDeclarationNode(node, options);
140
- return;
141
- }
142
- if (node.isKind(tsm.SyntaxKind.ClassDeclaration)) {
143
- if (options.ignoredPrefixes.some(
144
- (p) => node.getName()?.startsWith(p) === true
145
- )) {
146
- return;
147
- }
148
- transformClassDeclarationNode(node, options);
149
- return;
150
- }
151
- if (node.isKind(tsm.SyntaxKind.IntersectionType)) {
152
- if (readonlyContext.type === "readonly") {
153
- throw new Error(
154
- 'Invalid readonlyContext "readonly" passed to IntersectionTypeNode'
155
- );
156
- }
157
- transformIntersectionTypeNode(node, readonlyContext, options);
158
- return;
159
- }
160
- if (node.isKind(tsm.SyntaxKind.UnionType)) {
161
- if (readonlyContext.type === "readonly") {
162
- throw new Error(
163
- 'Invalid readonlyContext "readonly" passed to UnionTypeNode'
164
- );
165
- }
166
- transformUnionTypeNode(node, readonlyContext, options);
167
- return;
168
- }
169
- if (node.isKind(tsm.SyntaxKind.ParenthesizedType)) {
170
- transformParenthesizedTypeNode(node, readonlyContext, options);
171
- return;
172
- }
173
- for (const child of node.getChildren()) {
174
- transformNode(child, initialReadonlyContext, options);
175
- }
176
- };
177
- const transformTypeReferenceNode = (node, readonlyContext, options) => {
178
- const typeNameStr = node.getTypeName().getText();
179
- const typeArguments = node.getTypeArguments();
180
- if (typeNameStr === "Array" || typeNameStr === "ReadonlyArray") {
181
- if (!Arr.isFixedLengthArray(1, typeArguments)) {
182
- throw new Error(
183
- `Unexpected number of type arguments "${typeArguments.length}" for ${typeNameStr}.`
184
- );
185
- }
186
- transformNode(
187
- typeArguments[0],
188
- {
189
- type: "none",
190
- indexedAccessDepth: readonlyContext.indexedAccessDepth
191
- },
192
- options
193
- );
194
- const T = node.getTypeArguments()[0];
195
- options.replaceNode(
196
- node,
197
- readonlyContext.type === "DeepReadonly" ? wrapWithParentheses(`${wrapWithParentheses(T.getFullText())}[]`) : wrapWithParentheses(
198
- `readonly ${wrapWithParentheses(T.getFullText())}[]`
199
- )
200
- );
201
- return;
202
- }
203
- if (typeNameStr === "Set") {
204
- if (!Arr.isFixedLengthArray(1, typeArguments)) {
205
- throw new Error(
206
- `Unexpected number of type arguments "${typeArguments.length}" for Set.`
207
- );
208
- }
209
- transformNode(
210
- typeArguments[0],
211
- {
212
- type: "none",
213
- indexedAccessDepth: readonlyContext.indexedAccessDepth
214
- },
215
- options
216
- );
217
- const T = node.getTypeArguments()[0];
218
- options.replaceNode(node, `ReadonlySet<${T.getFullText()}>`);
219
- return;
220
- }
221
- if (typeNameStr === "Map") {
222
- if (!Arr.isFixedLengthArray(2, typeArguments)) {
223
- throw new Error(
224
- `Unexpected number of type arguments "${typeArguments.length}" for Map.`
225
- );
41
+ if (tsm.Node.isTypeNode(node) ||
42
+ node.isKind(tsm.SyntaxKind.InterfaceDeclaration) ||
43
+ node.isKind(tsm.SyntaxKind.ClassDeclaration)) {
44
+ options.debugPrint();
45
+ options.debugPrint(`transformNode [${node.getKindName()}] `);
46
+ options.debugPrint(node.getFullText());
47
+ options.debugPrint({ readonlyContext });
48
+ options.debugPrint();
49
+ }
50
+ if (hasDisableNextLineComment(node, TRANSFORMER_NAME)) {
51
+ options.debugPrint('skipped by disable-next-line comment');
52
+ return;
226
53
  }
227
- const nextReadonlyContextValue = nextReadonlyContext({
228
- currentReadonlyContext: readonlyContext,
229
- nextReadonlyContextType: "none",
230
- indexedAccessDepthChange: "keep"
231
- });
232
- for (const t of typeArguments) {
233
- transformNode(t, nextReadonlyContextValue, options);
234
- }
235
- const [K, V] = node.getTypeArguments();
236
- options.replaceNode(
237
- node,
238
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
239
- `ReadonlyMap<${K.getFullText()}, ${V.getFullText()}>`
240
- );
241
- return;
242
- }
243
- if (typeNameStr === "Record") {
244
- if (!Arr.isFixedLengthArray(2, typeArguments)) {
245
- throw new Error(
246
- `Unexpected number of type arguments "${typeArguments.length}" for Record.`
247
- );
54
+
55
+ if (node.isKind(tsm.SyntaxKind.VariableDeclaration)) {
56
+ const nodeName = node.getName();
57
+ if (options.ignoredPrefixes.some((p) => nodeName.startsWith(p))) {
58
+
59
+
60
+ options.debugPrint('skipped variable declaration by ignorePrefixes');
61
+ return;
62
+ }
63
+
64
+
65
+
66
+
67
+
68
+
69
+
70
+
248
71
  }
249
- const nextReadonlyContextValue = nextReadonlyContext({
250
- currentReadonlyContext: readonlyContext,
251
- nextReadonlyContextType: "none",
252
- indexedAccessDepthChange: "keep"
253
- });
254
- for (const t of typeArguments) {
255
- transformNode(t, nextReadonlyContextValue, options);
256
- }
257
- if (readonlyContext.type === "none" && readonlyContext.indexedAccessDepth === 0) {
258
- const [K, V] = node.getTypeArguments();
259
- options.replaceNode(
260
- node,
261
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
262
- readonlyRecordText(K.getFullText(), V.getFullText(), options)
263
- );
264
- }
265
- return;
266
- }
267
- if (typeNameStr === "ReadonlyRecord") {
268
- if (!Arr.isFixedLengthArray(2, typeArguments)) {
269
- throw new Error(
270
- `Unexpected number of type arguments "${typeArguments.length}" for ReadonlyRecord.`
271
- );
72
+
73
+
74
+ if ((node.isKind(tsm.SyntaxKind.FunctionDeclaration) ||
75
+ node.isKind(tsm.SyntaxKind.FunctionExpression)) &&
76
+ options.ignoredPrefixes.some((p) => node.getName()?.startsWith(p) === true)) {
77
+ return;
272
78
  }
273
- const nextReadonlyContextValue = nextReadonlyContext({
274
- currentReadonlyContext: readonlyContext,
275
- nextReadonlyContextType: "none",
276
- indexedAccessDepthChange: "keep"
277
- });
278
- for (const t of typeArguments) {
279
- transformNode(t, nextReadonlyContextValue, options);
280
- }
281
- const [K, V] = node.getTypeArguments();
282
- const bareRecordText = (
283
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
284
- `Record<${K.getFullText()}, ${V.getFullText()}>`
285
- );
286
- if (readonlyContext.type === "Readonly" && readonlyContext.indexedAccessDepth === 0) {
287
- options.replaceNode(node, bareRecordText);
288
- return;
289
- }
290
- if (options.recordStyle === "Readonly<Record>") {
291
- if (readonlyContext.type !== "none" || readonlyContext.indexedAccessDepth > 0) {
292
- options.replaceNode(node, bareRecordText);
79
+ if (node.isKind(tsm.SyntaxKind.Parameter)) {
80
+ const nodeName = node.getName();
81
+ if (options.ignoredPrefixes.some((p) => nodeName.startsWith(p))) {
82
+
83
+
84
+ return;
85
+ }
86
+
87
+
88
+
89
+
90
+
91
+
92
+ }
93
+
94
+
95
+ if (node.isKind(tsm.SyntaxKind.TypeAliasDeclaration) &&
96
+ options.ignoredPrefixes.some((p) => node.getName().startsWith(p))) {
293
97
  return;
294
- }
295
- options.replaceNode(node, `Readonly<${bareRecordText}>`);
296
- return;
297
- }
298
- return;
299
- }
300
- if (typeNameStr === "Readonly") {
301
- if (!Arr.isFixedLengthArray(1, typeArguments)) {
302
- throw new Error(
303
- `Unexpected number of type arguments "${typeArguments.length}" for Readonly.`
304
- );
305
- }
306
- transformNode(
307
- typeArguments[0],
308
- nextReadonlyContext({
309
- currentReadonlyContext: readonlyContext,
310
- nextReadonlyContextType: "Readonly",
311
- indexedAccessDepthChange: "keep"
312
- }),
313
- options
314
- );
315
- const T = removeParentheses(node.getTypeArguments()[0]);
316
- if (readonlyContext.type === "DeepReadonly") {
317
- options.replaceNode(node, T.getFullText());
318
- return;
319
- }
320
- if (readonlyContext.indexedAccessDepth > 0) {
321
- options.replaceNode(node, T.getFullText());
322
- return;
323
- }
324
- if (isReadonlyTypeReferenceNode(T)) {
325
- options.replaceNode(node, T.getFullText());
326
- return;
327
- }
328
- if (isAtomicTypeNode(T)) {
329
- options.replaceNode(node, T.getFullText());
330
- return;
331
- }
332
- if (T.isKind(tsm.SyntaxKind.TypeReference) && T.getTypeName().getText() === "Record") {
333
- const recordTypeArguments = T.getTypeArguments();
334
- if (Arr.isFixedLengthArray(2, recordTypeArguments)) {
335
- options.replaceNode(
336
- node,
337
- readonlyRecordText(
338
- recordTypeArguments[0].getFullText(),
339
- recordTypeArguments[1].getFullText(),
340
- options
341
- )
342
- );
98
+ }
99
+ if (node.isKind(tsm.SyntaxKind.TypeReference)) {
100
+ if (readonlyContext.type === 'readonly') {
101
+ throw new Error('Invalid readonlyContext "readonly" passed to TypeReferenceNode');
102
+ }
103
+ transformTypeReferenceNode(node, readonlyContext, options);
104
+ return;
105
+ }
106
+ if (node.isKind(tsm.SyntaxKind.ArrayType)) {
107
+ transformArrayTypeNode(node, readonlyContext, options);
108
+ return;
109
+ }
110
+ if (node.isKind(tsm.SyntaxKind.TupleType)) {
111
+ transformTupleTypeNode(node, readonlyContext, options);
112
+ return;
113
+ }
114
+ if (node.isKind(tsm.SyntaxKind.RestType)) {
115
+ transformRestTypeNode(node, readonlyContext, options);
116
+ return;
117
+ }
118
+ if (node.isKind(tsm.SyntaxKind.TypeOperator)) {
119
+ if (readonlyContext.type === 'readonly') {
120
+ throw new Error('Invalid readonlyContext "readonly" passed to TypeOperatorNode');
121
+ }
122
+ transformTypeOperatorNode(node, readonlyContext, options);
123
+ return;
124
+ }
125
+ if (node.isKind(tsm.SyntaxKind.TypeLiteral)) {
126
+ if (readonlyContext.type === 'readonly') {
127
+ throw new Error('Invalid readonlyContext "readonly" passed to TypeLiteralNode');
128
+ }
129
+ transformTypeLiteralNode(node, readonlyContext, options);
130
+ return;
131
+ }
132
+ if (node.isKind(tsm.SyntaxKind.IndexedAccessType)) {
133
+ transformIndexedAccessTypeNode(node, readonlyContext, options);
134
+ return;
135
+ }
136
+ if (node.isKind(tsm.SyntaxKind.MappedType)) {
137
+ if (readonlyContext.type === 'readonly') {
138
+ throw new Error('Invalid readonlyContext "readonly" passed to MappedTypeNode');
139
+ }
140
+ transformMappedTypeNode(node, readonlyContext, options);
141
+ return;
142
+ }
143
+ if (node.isKind(tsm.SyntaxKind.InterfaceDeclaration)) {
144
+
145
+
146
+ if (options.ignoredPrefixes.some((p) => node.getName().startsWith(p))) {
147
+ return;
148
+ }
149
+ transformInterfaceDeclarationNode(node, options);
150
+ return;
151
+ }
152
+ if (node.isKind(tsm.SyntaxKind.ClassDeclaration)) {
153
+
154
+
155
+ if (options.ignoredPrefixes.some((p) => node.getName()?.startsWith(p) === true)) {
156
+ return;
157
+ }
158
+ transformClassDeclarationNode(node, options);
159
+ return;
160
+ }
161
+ if (node.isKind(tsm.SyntaxKind.IntersectionType)) {
162
+ if (readonlyContext.type === 'readonly') {
163
+ throw new Error('Invalid readonlyContext "readonly" passed to IntersectionTypeNode');
164
+ }
165
+ transformIntersectionTypeNode(node, readonlyContext, options);
166
+ return;
167
+ }
168
+ if (node.isKind(tsm.SyntaxKind.UnionType)) {
169
+ if (readonlyContext.type === 'readonly') {
170
+ throw new Error('Invalid readonlyContext "readonly" passed to UnionTypeNode');
171
+ }
172
+ transformUnionTypeNode(node, readonlyContext, options);
343
173
  return;
344
- }
345
- }
346
- if (T.isKind(tsm.SyntaxKind.ArrayType) || T.isKind(tsm.SyntaxKind.TupleType)) {
347
- const readonlyText = `readonly ${T.getFullText()}`;
348
- options.replaceNode(
349
- node,
350
- shouldAvoidParenthesesForReadonly(node) ? readonlyText : wrapWithParentheses(readonlyText)
351
- );
352
- return;
353
- }
354
- if (isReadonlyTupleOrArrayTypeNode(T)) {
355
- options.replaceNode(node, T.getFullText());
356
- return;
357
- }
358
- if (T.isKind(tsm.SyntaxKind.UnionType) || T.isKind(tsm.SyntaxKind.IntersectionType)) {
359
- options.replaceNode(node, T.getFullText());
360
- return;
361
- }
362
- return;
363
- }
364
- if (typeNameStr === options.DeepReadonly.typeName) {
365
- if (!Arr.isFixedLengthArray(1, typeArguments)) {
366
- throw new Error(
367
- `Unexpected number of type arguments "${typeArguments.length}" for Readonly.`
368
- );
369
- }
370
- transformNode(
371
- typeArguments[0],
372
- nextReadonlyContext({
373
- currentReadonlyContext: readonlyContext,
374
- nextReadonlyContextType: "DeepReadonly"
375
- }),
376
- options
377
- );
378
- const T = node.getTypeArguments()[0];
379
- if (readonlyContext.type === "DeepReadonly") {
380
- options.replaceNode(node, T.getFullText());
381
- return;
382
- }
383
- if (isAtomicTypeNode(T)) {
384
- options.replaceNode(node, T.getFullText());
385
- return;
386
- }
387
- if (T.isKind(tsm.SyntaxKind.ArrayType) && isAtomicTypeNode(T.getElementTypeNode())) {
388
- const readonlyText = `readonly ${T.getFullText()}`;
389
- options.replaceNode(
390
- node,
391
- shouldAvoidParenthesesForReadonly(node) ? readonlyText : wrapWithParentheses(readonlyText)
392
- );
393
- return;
394
- }
395
- if (T.isKind(tsm.SyntaxKind.TupleType) && T.getElements().every(isAtomicTypeNode)) {
396
- const readonlyText = `readonly ${T.getFullText()}`;
397
- options.replaceNode(
398
- node,
399
- shouldAvoidParenthesesForReadonly(node) ? readonlyText : wrapWithParentheses(readonlyText)
400
- );
401
- return;
402
- }
403
- return;
404
- }
405
- {
406
- const nextReadonlyContextValue = nextReadonlyContext({
407
- currentReadonlyContext: readonlyContext,
408
- nextReadonlyContextType: "none",
409
- indexedAccessDepthChange: "keep"
410
- });
411
- for (const t of node.getTypeArguments()) {
412
- transformNode(t, nextReadonlyContextValue, options);
413
174
  }
414
- }
175
+ if (node.isKind(tsm.SyntaxKind.ParenthesizedType)) {
176
+
177
+ transformParenthesizedTypeNode(node, readonlyContext, options);
178
+ return;
179
+ }
180
+ for (const child of node.getChildren()) {
181
+ transformNode(child, initialReadonlyContext, options);
182
+ }
415
183
  };
416
- const readonlyRecordText = (keyText, valueText, options) => options.recordStyle === "ReadonlyRecord" ? `ReadonlyRecord<${keyText}, ${valueText}>` : `Readonly<Record<${keyText}, ${valueText}>>`;
417
- const transformArrayTypeNode = (node, readonlyContext, options) => {
418
- transformNode(
419
- node.getElementTypeNode(),
420
- nextReadonlyContext({
421
- currentReadonlyContext: readonlyContext,
422
- nextReadonlyContextType: "none",
423
- indexedAccessDepthChange: "decr"
424
- }),
425
- options
426
- );
427
- switch (readonlyContext.type) {
428
- case "DeepReadonly":
429
- case "readonly":
430
- return;
431
- // remain tr(E)[] as is
432
- // Unnecessary `Readonly` wrapper will be remove in transformTypeReferenceNode
433
- case "Readonly":
434
- case "none":
435
- if (readonlyContext.indexedAccessDepth === 0 && !isConditionalTypeDistributionGuard(node)) {
436
- const readonlyText = `readonly ${node.getFullText()}`;
437
- options.replaceNode(
438
- node,
439
- shouldAvoidParenthesesForReadonly(node) ? readonlyText : wrapWithParentheses(readonlyText)
440
- );
441
- }
442
- }
184
+
185
+
186
+
187
+
188
+
189
+
190
+
191
+
192
+
193
+
194
+
195
+
196
+
197
+
198
+
199
+
200
+
201
+
202
+ const transformTypeReferenceNode = (
203
+
204
+ node, readonlyContext, options) => {
205
+ const typeNameStr = node.getTypeName().getText();
206
+ const typeArguments = node.getTypeArguments();
207
+
208
+ if (typeNameStr === 'Array' || typeNameStr === 'ReadonlyArray') {
209
+ if (!Arr.isFixedLengthArray(1, typeArguments)) {
210
+ throw new Error(`Unexpected number of type arguments "${typeArguments.length}" for ${typeNameStr}.`);
211
+ }
212
+
213
+ transformNode(typeArguments[0], {
214
+ type: 'none',
215
+ indexedAccessDepth: readonlyContext.indexedAccessDepth,
216
+ }, options);
217
+
218
+ const T = node.getTypeArguments()[0];
219
+ options.replaceNode(node, readonlyContext.type === 'DeepReadonly'
220
+ ? wrapWithParentheses(`${wrapWithParentheses(T.getFullText())}[]`)
221
+ : wrapWithParentheses(`readonly ${wrapWithParentheses(T.getFullText())}[]`));
222
+ return;
223
+ }
224
+
225
+ if (typeNameStr === 'Set') {
226
+ if (!Arr.isFixedLengthArray(1, typeArguments)) {
227
+ throw new Error(`Unexpected number of type arguments "${typeArguments.length}" for Set.`);
228
+ }
229
+
230
+ transformNode(typeArguments[0], {
231
+ type: 'none',
232
+ indexedAccessDepth: readonlyContext.indexedAccessDepth,
233
+ }, options);
234
+
235
+ const T = node.getTypeArguments()[0];
236
+ options.replaceNode(node, `ReadonlySet<${T.getFullText()}>`);
237
+ return;
238
+ }
239
+
240
+ if (typeNameStr === 'Map') {
241
+ if (!Arr.isFixedLengthArray(2, typeArguments)) {
242
+ throw new Error(`Unexpected number of type arguments "${typeArguments.length}" for Map.`);
243
+ }
244
+ const nextReadonlyContextValue = nextReadonlyContext({
245
+ currentReadonlyContext: readonlyContext,
246
+ nextReadonlyContextType: 'none',
247
+ indexedAccessDepthChange: 'keep',
248
+ });
249
+
250
+ for (const t of typeArguments) {
251
+ transformNode(t, nextReadonlyContextValue, options);
252
+ }
253
+ const [K, V] = node.getTypeArguments();
254
+ options.replaceNode(node,
255
+
256
+ `ReadonlyMap<${K.getFullText()}, ${V.getFullText()}>`);
257
+ return;
258
+ }
259
+
260
+ if (typeNameStr === 'Record') {
261
+ if (!Arr.isFixedLengthArray(2, typeArguments)) {
262
+ throw new Error(`Unexpected number of type arguments "${typeArguments.length}" for Record.`);
263
+ }
264
+ const nextReadonlyContextValue = nextReadonlyContext({
265
+ currentReadonlyContext: readonlyContext,
266
+ nextReadonlyContextType: 'none',
267
+ indexedAccessDepthChange: 'keep',
268
+ });
269
+
270
+ for (const t of typeArguments) {
271
+ transformNode(t, nextReadonlyContextValue, options);
272
+ }
273
+
274
+
275
+
276
+
277
+
278
+
279
+ if (readonlyContext.type === 'none' &&
280
+ readonlyContext.indexedAccessDepth === 0) {
281
+ const [K, V] = node.getTypeArguments();
282
+ options.replaceNode(node,
283
+
284
+ readonlyRecordText(K.getFullText(), V.getFullText(), options));
285
+ }
286
+ return;
287
+ }
288
+
289
+ if (typeNameStr === 'ReadonlyRecord') {
290
+ if (!Arr.isFixedLengthArray(2, typeArguments)) {
291
+ throw new Error(`Unexpected number of type arguments "${typeArguments.length}" for ReadonlyRecord.`);
292
+ }
293
+ const nextReadonlyContextValue = nextReadonlyContext({
294
+ currentReadonlyContext: readonlyContext,
295
+ nextReadonlyContextType: 'none',
296
+ indexedAccessDepthChange: 'keep',
297
+ });
298
+
299
+ for (const t of typeArguments) {
300
+ transformNode(t, nextReadonlyContextValue, options);
301
+ }
302
+ const [K, V] = node.getTypeArguments();
303
+ const bareRecordText =
304
+
305
+ `Record<${K.getFullText()}, ${V.getFullText()}>`;
306
+
307
+
308
+
309
+ if (readonlyContext.type === 'Readonly' &&
310
+ readonlyContext.indexedAccessDepth === 0) {
311
+ options.replaceNode(node, bareRecordText);
312
+ return;
313
+ }
314
+ if (options.recordStyle === 'Readonly<Record>') {
315
+
316
+
317
+
318
+
319
+
320
+ if (readonlyContext.type !== 'none' ||
321
+ readonlyContext.indexedAccessDepth > 0) {
322
+ options.replaceNode(node, bareRecordText);
323
+ return;
324
+ }
325
+ options.replaceNode(node, `Readonly<${bareRecordText}>`);
326
+ return;
327
+ }
328
+
329
+
330
+
331
+
332
+
333
+ return;
334
+ }
335
+
336
+ if (typeNameStr === 'Readonly') {
337
+ if (!Arr.isFixedLengthArray(1, typeArguments)) {
338
+ throw new Error(`Unexpected number of type arguments "${typeArguments.length}" for Readonly.`);
339
+ }
340
+
341
+ transformNode(typeArguments[0], nextReadonlyContext({
342
+ currentReadonlyContext: readonlyContext,
343
+ nextReadonlyContextType: 'Readonly',
344
+ indexedAccessDepthChange: 'keep',
345
+ }), options);
346
+
347
+ const T = removeParentheses(node.getTypeArguments()[0]);
348
+
349
+ if (readonlyContext.type === 'DeepReadonly') {
350
+ options.replaceNode(node, T.getFullText());
351
+ return;
352
+ }
353
+
354
+ if (readonlyContext.indexedAccessDepth > 0) {
355
+ options.replaceNode(node, T.getFullText());
356
+ return;
357
+ }
358
+
359
+ if (isReadonlyTypeReferenceNode(T)) {
360
+ options.replaceNode(node, T.getFullText());
361
+ return;
362
+ }
363
+
364
+ if (isAtomicTypeNode(T)) {
365
+ options.replaceNode(node, T.getFullText());
366
+ return;
367
+ }
368
+
369
+
370
+ if (T.isKind(tsm.SyntaxKind.TypeReference) &&
371
+ T.getTypeName().getText() === 'Record') {
372
+ const recordTypeArguments = T.getTypeArguments();
373
+ if (Arr.isFixedLengthArray(2, recordTypeArguments)) {
374
+ options.replaceNode(node, readonlyRecordText(recordTypeArguments[0].getFullText(), recordTypeArguments[1].getFullText(), options));
375
+ return;
376
+ }
377
+ }
378
+
379
+
380
+
381
+
382
+
383
+ if (T.isKind(tsm.SyntaxKind.ArrayType) ||
384
+ T.isKind(tsm.SyntaxKind.TupleType)) {
385
+ const readonlyText = `readonly ${T.getFullText()}`;
386
+ options.replaceNode(node, shouldAvoidParenthesesForReadonly(node)
387
+ ? readonlyText
388
+ : wrapWithParentheses(readonlyText));
389
+ return;
390
+ }
391
+
392
+
393
+
394
+ if (isReadonlyTupleOrArrayTypeNode(T)) {
395
+ options.replaceNode(node, T.getFullText());
396
+ return;
397
+ }
398
+
399
+
400
+ if (T.isKind(tsm.SyntaxKind.UnionType) ||
401
+ T.isKind(tsm.SyntaxKind.IntersectionType)) {
402
+ options.replaceNode(node, T.getFullText());
403
+ return;
404
+ }
405
+ return;
406
+ }
407
+
408
+ if (typeNameStr === options.DeepReadonly.typeName) {
409
+ if (!Arr.isFixedLengthArray(1, typeArguments)) {
410
+ throw new Error(`Unexpected number of type arguments "${typeArguments.length}" for Readonly.`);
411
+ }
412
+
413
+ transformNode(typeArguments[0], nextReadonlyContext({
414
+ currentReadonlyContext: readonlyContext,
415
+ nextReadonlyContextType: 'DeepReadonly',
416
+ }), options);
417
+
418
+ const T = node.getTypeArguments()[0];
419
+
420
+ if (readonlyContext.type === 'DeepReadonly') {
421
+ options.replaceNode(node, T.getFullText());
422
+ return;
423
+ }
424
+
425
+ if (isAtomicTypeNode(T)) {
426
+ options.replaceNode(node, T.getFullText());
427
+ return;
428
+ }
429
+
430
+
431
+
432
+
433
+ if (T.isKind(tsm.SyntaxKind.ArrayType) &&
434
+ isAtomicTypeNode(T.getElementTypeNode())) {
435
+ const readonlyText = `readonly ${T.getFullText()}`;
436
+ options.replaceNode(node, shouldAvoidParenthesesForReadonly(node)
437
+ ? readonlyText
438
+ : wrapWithParentheses(readonlyText));
439
+ return;
440
+ }
441
+
442
+
443
+ if (T.isKind(tsm.SyntaxKind.TupleType) &&
444
+ T.getElements().every(isAtomicTypeNode)) {
445
+ const readonlyText = `readonly ${T.getFullText()}`;
446
+ options.replaceNode(node, shouldAvoidParenthesesForReadonly(node)
447
+ ? readonlyText
448
+ : wrapWithParentheses(readonlyText));
449
+ return;
450
+ }
451
+ return;
452
+ }
453
+ {
454
+ const nextReadonlyContextValue = nextReadonlyContext({
455
+ currentReadonlyContext: readonlyContext,
456
+ nextReadonlyContextType: 'none',
457
+ indexedAccessDepthChange: 'keep',
458
+ });
459
+
460
+ for (const t of node.getTypeArguments()) {
461
+ transformNode(t, nextReadonlyContextValue, options);
462
+ }
463
+ }
443
464
  };
444
- const transformTupleTypeNode = (node, readonlyContext, options) => {
445
- {
446
- const nextReadonlyContextValue = nextReadonlyContext({
447
- currentReadonlyContext: readonlyContext,
448
- nextReadonlyContextType: "none",
449
- indexedAccessDepthChange: "decr"
450
- });
451
- for (const el of node.getElements()) {
452
- if (el.isKind(tsm.SyntaxKind.NamedTupleMember)) {
453
- if (options.ignoredPrefixes.every((p) => !el.getName().startsWith(p))) {
454
- transformNode(el.getTypeNode(), nextReadonlyContextValue, options);
455
- }
456
- } else {
457
- transformNode(el, nextReadonlyContextValue, options);
458
- }
459
- }
460
- }
461
- switch (readonlyContext.type) {
462
- case "DeepReadonly":
463
- case "readonly":
464
- return;
465
- // remain tr(E)[] as is
466
- // Unnecessary `Readonly` wrapper will be remove in transformTypeReferenceNode
467
- case "Readonly":
468
- case "none":
469
- if (readonlyContext.indexedAccessDepth === 0 && !isConditionalTypeDistributionGuard(node)) {
470
- const readonlyText = `readonly ${node.getFullText()}`;
471
- options.replaceNode(
472
- node,
473
- shouldAvoidParenthesesForReadonly(node) ? readonlyText : wrapWithParentheses(readonlyText)
474
- );
475
- }
476
- }
465
+
466
+ const readonlyRecordText = (keyText, valueText, options) => options.recordStyle === 'ReadonlyRecord'
467
+ ? `ReadonlyRecord<${keyText}, ${valueText}>`
468
+ : `Readonly<Record<${keyText}, ${valueText}>>`;
469
+
470
+ const transformArrayTypeNode = (
471
+
472
+ node, readonlyContext, options) => {
473
+
474
+ transformNode(node.getElementTypeNode(), nextReadonlyContext({
475
+ currentReadonlyContext: readonlyContext,
476
+ nextReadonlyContextType: 'none',
477
+ indexedAccessDepthChange: 'decr',
478
+ }), options);
479
+ switch (readonlyContext.type) {
480
+ case 'DeepReadonly':
481
+ case 'readonly':
482
+ return;
483
+
484
+ case 'Readonly':
485
+ case 'none':
486
+
487
+
488
+ if (readonlyContext.indexedAccessDepth === 0 &&
489
+ !isConditionalTypeDistributionGuard(node)) {
490
+ const readonlyText = `readonly ${node.getFullText()}`;
491
+ options.replaceNode(node, shouldAvoidParenthesesForReadonly(node)
492
+ ? readonlyText
493
+ : wrapWithParentheses(readonlyText));
494
+ }
495
+ }
477
496
  };
478
- const transformRestTypeNode = (node, readonlyContext, options) => {
479
- transformNode(
480
- node.getTypeNode(),
481
- nextReadonlyContext({
482
- currentReadonlyContext: readonlyContext,
483
- nextReadonlyContextType: "none",
484
- indexedAccessDepthChange: "decr"
485
- }),
486
- options
487
- );
488
- const R = removeParentheses(node.getTypeNode());
489
- if (isReadonlyArrayTypeNode(R) || isReadonlyTupleTypeNode(R)) {
490
- options.replaceNode(
491
- node,
492
- `...${R.getTypeNode().getFullText()}`
493
- );
494
- }
497
+
498
+ const transformTupleTypeNode = (
499
+
500
+ node, readonlyContext, options) => {
501
+
502
+ {
503
+ const nextReadonlyContextValue = nextReadonlyContext({
504
+ currentReadonlyContext: readonlyContext,
505
+ nextReadonlyContextType: 'none',
506
+ indexedAccessDepthChange: 'decr',
507
+ });
508
+ for (const el of node.getElements()) {
509
+ if (el.isKind(tsm.SyntaxKind.NamedTupleMember)) {
510
+ if (options.ignoredPrefixes.every((p) => !el.getName().startsWith(p))) {
511
+ transformNode(el.getTypeNode(), nextReadonlyContextValue, options);
512
+ }
513
+ }
514
+ else {
515
+ transformNode(el, nextReadonlyContextValue, options);
516
+ }
517
+ }
518
+ }
519
+ switch (readonlyContext.type) {
520
+ case 'DeepReadonly':
521
+ case 'readonly':
522
+ return;
523
+
524
+ case 'Readonly':
525
+ case 'none':
526
+
527
+
528
+ if (readonlyContext.indexedAccessDepth === 0 &&
529
+ !isConditionalTypeDistributionGuard(node)) {
530
+ const readonlyText = `readonly ${node.getFullText()}`;
531
+ options.replaceNode(node, shouldAvoidParenthesesForReadonly(node)
532
+ ? readonlyText
533
+ : wrapWithParentheses(readonlyText));
534
+ }
535
+ }
495
536
  };
496
- const transformTypeOperatorNode = (node, readonlyContext, options) => {
497
- transformNode(
498
- node.getTypeNode(),
499
- nextReadonlyContext({
500
- currentReadonlyContext: readonlyContext,
501
- nextReadonlyContextType: node.getOperator() === tsm.SyntaxKind.ReadonlyKeyword ? "readonly" : "none",
502
- indexedAccessDepthChange: "decr"
503
- }),
504
- options
505
- );
506
- switch (readonlyContext.type) {
507
- // DeepReadonly<readonly E[]> -> DeepReadonly<E[]>
508
- case "DeepReadonly":
509
- options.replaceNode(node, node.getTypeNode().getFullText());
510
- return;
511
- case "Readonly":
512
- case "none":
513
- if (readonlyContext.indexedAccessDepth > 0) {
514
- options.replaceNode(node, node.getTypeNode().getFullText());
515
- }
516
- }
537
+
538
+ const transformRestTypeNode = (
539
+
540
+ node, readonlyContext, options) => {
541
+
542
+ transformNode(node.getTypeNode() , nextReadonlyContext({
543
+ currentReadonlyContext: readonlyContext,
544
+ nextReadonlyContextType: 'none',
545
+ indexedAccessDepthChange: 'decr',
546
+ }), options);
547
+ const R = removeParentheses(node.getTypeNode());
548
+
549
+
550
+ if (isReadonlyArrayTypeNode(R) || isReadonlyTupleTypeNode(R)) {
551
+ options.replaceNode(node, `...${R.getTypeNode().getFullText()}` );
552
+ }
517
553
  };
518
- const transformTypeLiteralNode = (node, readonlyContext, options) => {
519
- if (options.ignoreEmptyObjectTypes && Arr.isEmpty(node.getMembers())) {
520
- return;
521
- }
522
- transformMembers(
523
- node.getMembers(),
524
- "remove",
525
- nextReadonlyContext({
526
- currentReadonlyContext: readonlyContext,
527
- nextReadonlyContextType: "none",
528
- indexedAccessDepthChange: "decr"
529
- }),
530
- options
531
- );
532
- switch (readonlyContext.type) {
533
- case "DeepReadonly":
534
- case "Readonly":
535
- return;
536
- case "none": {
537
- if (readonlyContext.indexedAccessDepth === 0) {
538
- options.replaceNode(node, `Readonly<${node.getFullText()}>`);
539
- }
540
- }
541
- }
554
+
555
+
556
+
557
+
558
+ const transformTypeOperatorNode = (
559
+
560
+ node, readonlyContext, options) => {
561
+
562
+ transformNode(node.getTypeNode(), nextReadonlyContext({
563
+ currentReadonlyContext: readonlyContext,
564
+ nextReadonlyContextType: node.getOperator() === tsm.SyntaxKind.ReadonlyKeyword
565
+ ? 'readonly'
566
+ : 'none',
567
+ indexedAccessDepthChange: 'decr',
568
+ }), options);
569
+ switch (readonlyContext.type) {
570
+
571
+ case 'DeepReadonly':
572
+ options.replaceNode(node, node.getTypeNode().getFullText());
573
+ return;
574
+ case 'Readonly':
575
+ case 'none':
576
+ if (readonlyContext.indexedAccessDepth > 0) {
577
+ options.replaceNode(node, node.getTypeNode().getFullText());
578
+ }
579
+ }
542
580
  };
543
- const transformIndexedAccessTypeNode = (node, readonlyContext, options) => {
544
- transformNode(
545
- node.getObjectTypeNode(),
546
- nextReadonlyContext({
547
- currentReadonlyContext: readonlyContext,
548
- nextReadonlyContextType: "none",
549
- indexedAccessDepthChange: "incr"
550
- }),
551
- options
552
- );
553
- transformNode(
554
- node.getIndexTypeNode(),
555
- nextReadonlyContext({
556
- currentReadonlyContext: readonlyContext,
557
- nextReadonlyContextType: "Readonly",
558
- indexedAccessDepthChange: "decr"
559
- }),
560
- options
561
- );
581
+
582
+ const transformTypeLiteralNode = (
583
+
584
+ node, readonlyContext, options) => {
585
+ if (options.ignoreEmptyObjectTypes && Arr.isEmpty(node.getMembers())) {
586
+ return;
587
+ }
588
+
589
+ transformMembers(node.getMembers(), 'remove', nextReadonlyContext({
590
+ currentReadonlyContext: readonlyContext,
591
+ nextReadonlyContextType: 'none',
592
+ indexedAccessDepthChange: 'decr',
593
+ }), options);
594
+ switch (readonlyContext.type) {
595
+ case 'DeepReadonly':
596
+ case 'Readonly':
597
+
598
+ return;
599
+ case 'none': {
600
+ if (readonlyContext.indexedAccessDepth === 0) {
601
+
602
+ options.replaceNode(node, `Readonly<${node.getFullText()}>`);
603
+ }
604
+ }
605
+ }
562
606
  };
563
- const transformMappedTypeNode = (node, readonlyContext, options) => {
564
- const typeNode = node.getTypeNode();
565
- if (typeNode !== void 0) {
566
- transformNode(
567
- typeNode,
568
- nextReadonlyContext({
607
+
608
+ const transformIndexedAccessTypeNode = (
609
+
610
+ node, readonlyContext, options) => {
611
+
612
+ transformNode(node.getObjectTypeNode(), nextReadonlyContext({
613
+ currentReadonlyContext: readonlyContext,
614
+ nextReadonlyContextType: 'none',
615
+ indexedAccessDepthChange: 'incr',
616
+ }), options);
617
+ transformNode(node.getIndexTypeNode() , nextReadonlyContext({
569
618
  currentReadonlyContext: readonlyContext,
570
- nextReadonlyContextType: "none",
571
- indexedAccessDepthChange: "decr"
572
- }),
573
- options
574
- );
575
- }
576
- const questionTokenText = pipe(node.getQuestionToken()?.getText()).mapNullable(
577
- (t) => t === "?" ? "?" : t === "-" ? "-?" : t === "+" ? "+?" : void 0
578
- ).value ?? "";
579
- const nameTypeText = pipe(node.getNameTypeNode()?.getFullText()).mapNullable(
580
- (t) => ` as ${t.trim()}`
581
- ).value;
582
- const result = pipe(
583
- // remove readonlyToken
584
- `{ [${node.getTypeParameter().getFullText()}${nameTypeText ?? ""}]${questionTokenText}: ${node.getTypeNode()?.getFullText()} }`
585
- ).map(
586
- (mpt) => match(readonlyContext.type, {
587
- // Don't wrap with Readonly if already readonly or unnecessary
588
- DeepReadonly: mpt,
589
- Readonly: mpt,
590
- none: readonlyContext.indexedAccessDepth > 0 ? mpt : `Readonly<${mpt}>`
591
- })
592
- ).value;
593
- options.replaceNode(node, result);
619
+ nextReadonlyContextType: 'Readonly',
620
+ indexedAccessDepthChange: 'decr',
621
+ }), options);
594
622
  };
595
- const transformInterfaceDeclarationNode = (node, options) => {
596
- for (const param of node.getTypeParameters()) {
597
- transformNode(param, initialReadonlyContext, options);
598
- }
599
- for (const n of node.getHeritageClauses()) {
600
- transformNode(n, initialReadonlyContext, options);
601
- }
602
- transformMembers(node.getMembers(), "add", initialReadonlyContext, options);
623
+
624
+
625
+
626
+
627
+
628
+
629
+ const transformMappedTypeNode = (
630
+
631
+ node, readonlyContext, options) => {
632
+ const typeNode = node.getTypeNode();
633
+ if (typeNode !== undefined) {
634
+ transformNode(typeNode, nextReadonlyContext({
635
+ currentReadonlyContext: readonlyContext,
636
+ nextReadonlyContextType: 'none',
637
+ indexedAccessDepthChange: 'decr',
638
+ }), options);
639
+ }
640
+ const questionTokenText = pipe(node.getQuestionToken()?.getText()).mapNullable((t) => t === '?' ? '?' : t === '-' ? '-?' : t === '+' ? '+?' : undefined).value ?? '';
641
+
642
+
643
+ const nameTypeText = pipe(node.getNameTypeNode()?.getFullText()).mapNullable((t) => ` as ${t.trim()}`).value;
644
+ const result = pipe(
645
+
646
+ `{ [${node.getTypeParameter().getFullText()}${nameTypeText ?? ''}]${questionTokenText}: ${node.getTypeNode()?.getFullText()} }`).map((mpt) => match(readonlyContext.type, {
647
+
648
+ DeepReadonly: mpt,
649
+ Readonly: mpt,
650
+ none: readonlyContext.indexedAccessDepth > 0 ? mpt : `Readonly<${mpt}>`,
651
+ })).value;
652
+ options.replaceNode(node, result);
603
653
  };
604
- const transformClassDeclarationNode = (node, options) => {
605
- for (const n of node.getTypeParameters()) {
606
- transformNode(n, initialReadonlyContext, options);
607
- }
608
- for (const n of node.getHeritageClauses()) {
609
- transformNode(n, initialReadonlyContext, options);
610
- }
611
- for (const mb of node.getMembers()) {
612
- if (hasDisableNextLineComment(mb)) {
613
- options.debugPrint("skipped member by disable-next-line comment");
614
- continue;
615
- }
616
- if (mb.isKind(tsm.SyntaxKind.PropertyDeclaration)) {
617
- if (!checkIfPropertyNameShouldBeIgnored(mb.getNameNode(), options)) {
618
- mb.setIsReadonly(true);
619
- const type = mb.getTypeNode();
620
- if (type !== void 0) {
621
- transformNode(type, initialReadonlyContext, options);
622
- }
623
- const initializer = mb.getInitializer();
624
- if (initializer !== void 0) {
625
- transformNode(initializer, initialReadonlyContext, options);
626
- }
627
- }
628
- continue;
629
- }
630
- if ((mb.isKind(tsm.SyntaxKind.MethodDeclaration) || mb.isKind(tsm.SyntaxKind.GetAccessor) || mb.isKind(tsm.SyntaxKind.SetAccessor)) && checkIfPropertyNameShouldBeIgnored(mb.getNameNode(), options)) {
631
- options.debugPrint("skipped method by ignored prefix");
632
- continue;
633
- }
634
- transformNode(mb, initialReadonlyContext, options);
635
- }
636
- for (const mb of node.getChildrenOfKind(tsm.SyntaxKind.IndexSignature)) {
637
- transformIndexSignatureDeclaration(
638
- mb,
639
- "add",
640
- initialReadonlyContext,
641
- options
642
- );
643
- }
644
- for (const ctor of node.getConstructors()) {
645
- for (const param of ctor.getParameters()) {
646
- transformConstructorParameter(param, options);
647
- }
648
- const body = ctor.getBody();
649
- if (body !== void 0) {
650
- transformNode(body, initialReadonlyContext, options);
651
- }
652
- }
654
+
655
+ const transformInterfaceDeclarationNode = (
656
+
657
+ node, options) => {
658
+ for (const param of node.getTypeParameters()) {
659
+ transformNode(param, initialReadonlyContext, options);
660
+ }
661
+ for (const n of node.getHeritageClauses()) {
662
+ transformNode(n, initialReadonlyContext, options);
663
+ }
664
+ transformMembers(node.getMembers(), 'add', initialReadonlyContext, options);
653
665
  };
654
- const transformConstructorParameter = (param, options) => {
655
- if (hasDisableNextLineComment(param)) {
656
- options.debugPrint(
657
- "skipped class constructor parameter by disable-next-line comment"
658
- );
659
- return;
660
- }
661
- const name = param.getName();
662
- if (options.ignoredPrefixes.some((p) => name.startsWith(p))) {
663
- return;
664
- }
665
- const scope = param.getScope();
666
- if (scope === tsm.Scope.Public || scope === tsm.Scope.Protected || scope === tsm.Scope.Private) {
667
- param.setIsReadonly(true);
668
- }
669
- const type = param.getTypeNode();
670
- if (type !== void 0) {
671
- transformNode(type, initialReadonlyContext, options);
672
- }
666
+ const transformClassDeclarationNode = (
667
+
668
+ node, options) => {
669
+ for (const n of node.getTypeParameters()) {
670
+ transformNode(n, initialReadonlyContext, options);
671
+ }
672
+ for (const n of node.getHeritageClauses()) {
673
+ transformNode(n, initialReadonlyContext, options);
674
+ }
675
+ for (const mb of node.getMembers()) {
676
+ if (hasDisableNextLineComment(mb)) {
677
+ options.debugPrint('skipped member by disable-next-line comment');
678
+ continue;
679
+ }
680
+ if (mb.isKind(tsm.SyntaxKind.PropertyDeclaration)) {
681
+ if (!checkIfPropertyNameShouldBeIgnored(mb.getNameNode(), options)) {
682
+ mb.setIsReadonly(true);
683
+ const type = mb.getTypeNode();
684
+ if (type !== undefined) {
685
+ transformNode(type, initialReadonlyContext, options);
686
+ }
687
+ const initializer = mb.getInitializer();
688
+ if (initializer !== undefined) {
689
+ transformNode(initializer, initialReadonlyContext, options);
690
+ }
691
+ }
692
+ continue;
693
+ }
694
+
695
+
696
+ if ((mb.isKind(tsm.SyntaxKind.MethodDeclaration) ||
697
+ mb.isKind(tsm.SyntaxKind.GetAccessor) ||
698
+ mb.isKind(tsm.SyntaxKind.SetAccessor)) &&
699
+ checkIfPropertyNameShouldBeIgnored(mb.getNameNode(), options)) {
700
+ options.debugPrint('skipped method by ignored prefix');
701
+ continue;
702
+ }
703
+ transformNode(mb, initialReadonlyContext, options);
704
+ }
705
+ for (const mb of node.getChildrenOfKind(tsm.SyntaxKind.IndexSignature)) {
706
+ transformIndexSignatureDeclaration(mb, 'add', initialReadonlyContext, options);
707
+ }
708
+ for (const ctor of node.getConstructors()) {
709
+ for (const param of ctor.getParameters()) {
710
+ transformConstructorParameter(param, options);
711
+ }
712
+ const body = ctor.getBody();
713
+ if (body !== undefined) {
714
+ transformNode(body, initialReadonlyContext, options);
715
+ }
716
+ }
673
717
  };
674
- const transformIntersectionTypeNode = (node, readonlyContext, options) => {
675
- transformUnionOrIntersectionTypeNodeImpl(node, readonlyContext, options, "&");
718
+ const transformConstructorParameter = (
719
+
720
+ param, options) => {
721
+ if (hasDisableNextLineComment(param)) {
722
+ options.debugPrint('skipped class constructor parameter by disable-next-line comment');
723
+ return;
724
+ }
725
+ const name = param.getName();
726
+ if (options.ignoredPrefixes.some((p) => name.startsWith(p))) {
727
+ return;
728
+ }
729
+
730
+ const scope = param.getScope();
731
+
732
+
733
+
734
+ if (scope === tsm.Scope.Public ||
735
+ scope === tsm.Scope.Protected ||
736
+ scope === tsm.Scope.Private) {
737
+ param.setIsReadonly(true);
738
+ }
739
+ const type = param.getTypeNode();
740
+ if (type !== undefined) {
741
+ transformNode(type, initialReadonlyContext, options);
742
+ }
676
743
  };
677
- const transformUnionTypeNode = (node, readonlyContext, options) => {
678
- transformUnionOrIntersectionTypeNodeImpl(node, readonlyContext, options, "|");
744
+
745
+
746
+
747
+
748
+ const transformIntersectionTypeNode = (
749
+
750
+ node, readonlyContext, options) => {
751
+ transformUnionOrIntersectionTypeNodeImpl(node, readonlyContext, options, '&');
679
752
  };
680
- const transformUnionOrIntersectionTypeNodeImpl = (node, readonlyContext, options, operator) => {
681
- const nextReadonlyContextValue = nextReadonlyContext({
682
- currentReadonlyContext: readonlyContext,
683
- nextReadonlyContextType: "none",
684
- indexedAccessDepthChange: "keep"
685
- });
686
- for (const n of node.getTypeNodes()) {
687
- transformNode(n, nextReadonlyContextValue, options);
688
- }
689
- const newTypes = node.getTypeNodes();
690
- const { primitives, arraysAndTuples, typeLiterals, others } = groupUnionIntersectionTypes(newTypes);
691
- options.debugPrint({ primitives, arraysAndTuples, typeLiterals, others });
692
- const isEmptyTypeLiteral = (n) => n.isKind(tsm.SyntaxKind.TypeLiteral) && Arr.isEmpty(n.getMembers());
693
- const nonEmptyTypeLiterals = typeLiterals === void 0 ? void 0 : options.ignoreEmptyObjectTypes ? typeLiterals.nodes.filter((n) => !isEmptyTypeLiteral(n)) : typeLiterals.nodes;
694
- const emptyTypeLiterals = typeLiterals === void 0 || !options.ignoreEmptyObjectTypes ? void 0 : typeLiterals.nodes.filter(isEmptyTypeLiteral);
695
- const typeLiteralsWrappedWithReadonly = nonEmptyTypeLiterals === void 0 || Arr.isEmpty(nonEmptyTypeLiterals) ? [] : [
696
- unionToString({
697
- types: nonEmptyTypeLiterals.map(
698
- (n) => isReadonlyTypeReferenceNode(n) ? (
699
- // NOTE: Readonly<A & B> -> (A & B)
700
- unwrapReadonlyTypeArgText(n)
701
- ) : n.getFullText()
702
- ),
703
- op: operator,
704
- wrapWithReadonly: nextReadonlyContextValue.type !== "DeepReadonly" && nextReadonlyContextValue.indexedAccessDepth === 0
705
- })
706
- ];
707
- const sorted = [
708
- mapNullable(
709
- primitives,
710
- (a) => [a.nodes.map((n) => n.getFullText()), a.firstPosition]
711
- ),
712
- mapNullable(
713
- arraysAndTuples,
714
- (a) => [a.nodes.map((n) => n.getFullText()), a.firstPosition]
715
- ),
716
- nonEmptyTypeLiterals !== void 0 && Arr.isNonEmpty(nonEmptyTypeLiterals) ? mapNullable(
717
- typeLiterals,
718
- (a) => [typeLiteralsWrappedWithReadonly, a.firstPosition]
719
- ) : void 0,
720
- emptyTypeLiterals !== void 0 && Arr.isNonEmpty(emptyTypeLiterals) ? mapNullable(
721
- typeLiterals,
722
- (a) => [
723
- emptyTypeLiterals.map((n) => n.getFullText()),
724
- // Use a position after non-empty type literals if they exist
725
- a.firstPosition + (nonEmptyTypeLiterals !== void 0 && Arr.isNonEmpty(nonEmptyTypeLiterals) ? 0.5 : 0)
726
- ]
727
- ) : void 0,
728
- mapNullable(
729
- others,
730
- (a) => [
731
- a.nodes.map((n) => wrapWithParentheses(n.getFullText())),
732
- a.firstPosition
733
- ]
734
- )
735
- ].filter((x) => x !== void 0).toSorted((a, b) => a[1] - b[1]).flatMap((a) => a[0]);
736
- options.replaceNode(
737
- node,
738
- unionToString({ types: sorted, op: operator, wrapWithReadonly: false })
739
- );
753
+
754
+
755
+
756
+
757
+ const transformUnionTypeNode = (
758
+
759
+ node, readonlyContext, options) => {
760
+ transformUnionOrIntersectionTypeNodeImpl(node, readonlyContext, options, '|');
740
761
  };
741
- const unionToString = ({
742
- types: typesRaw,
743
- op,
744
- wrapWithReadonly
745
- }) => {
746
- const types = typesRaw.map((s) => s.trim());
747
- return unionToStringImpl({ types, op, wrapWithReadonly });
762
+ const transformUnionOrIntersectionTypeNodeImpl = (
763
+
764
+ node, readonlyContext, options, operator) => {
765
+ const nextReadonlyContextValue = nextReadonlyContext({
766
+ currentReadonlyContext: readonlyContext,
767
+ nextReadonlyContextType: 'none',
768
+ indexedAccessDepthChange: 'keep',
769
+ });
770
+
771
+ for (const n of node.getTypeNodes() ) {
772
+ transformNode(n, nextReadonlyContextValue, options);
773
+ }
774
+ const newTypes = node.getTypeNodes();
775
+ const { primitives, arraysAndTuples, typeLiterals, others } = groupUnionIntersectionTypes(newTypes);
776
+ options.debugPrint({ primitives, arraysAndTuples, typeLiterals, others });
777
+
778
+ const isEmptyTypeLiteral = (
779
+
780
+ n) => n.isKind(tsm.SyntaxKind.TypeLiteral) && Arr.isEmpty(n.getMembers());
781
+ const nonEmptyTypeLiterals = typeLiterals === undefined
782
+ ? undefined
783
+ : options.ignoreEmptyObjectTypes
784
+ ? typeLiterals.nodes.filter((n) => !isEmptyTypeLiteral(n))
785
+ : typeLiterals.nodes;
786
+ const emptyTypeLiterals = typeLiterals === undefined || !options.ignoreEmptyObjectTypes
787
+ ? undefined
788
+ : typeLiterals.nodes.filter(isEmptyTypeLiteral);
789
+ const typeLiteralsWrappedWithReadonly = nonEmptyTypeLiterals === undefined || Arr.isEmpty(nonEmptyTypeLiterals)
790
+ ? []
791
+ : [
792
+ unionToString({
793
+ types: nonEmptyTypeLiterals.map((n) => isReadonlyTypeReferenceNode(n)
794
+ ?
795
+ unwrapReadonlyTypeArgText(n)
796
+ : n.getFullText()),
797
+ op: operator,
798
+ wrapWithReadonly: nextReadonlyContextValue.type !== 'DeepReadonly' &&
799
+ nextReadonlyContextValue.indexedAccessDepth === 0,
800
+ }),
801
+ ];
802
+
803
+ const sorted = [
804
+ mapNullable(primitives, (a) => [a.nodes.map((n) => n.getFullText()), a.firstPosition]),
805
+ mapNullable(arraysAndTuples, (a) => [a.nodes.map((n) => n.getFullText()), a.firstPosition]),
806
+ nonEmptyTypeLiterals !== undefined && Arr.isNonEmpty(nonEmptyTypeLiterals)
807
+ ? mapNullable(typeLiterals, (a) => [typeLiteralsWrappedWithReadonly, a.firstPosition])
808
+ : undefined,
809
+ emptyTypeLiterals !== undefined && Arr.isNonEmpty(emptyTypeLiterals)
810
+ ? mapNullable(typeLiterals, (a) => [
811
+ emptyTypeLiterals.map((n) => n.getFullText()),
812
+
813
+ a.firstPosition +
814
+ (nonEmptyTypeLiterals !== undefined &&
815
+ Arr.isNonEmpty(nonEmptyTypeLiterals)
816
+ ? 0.5
817
+ : 0),
818
+ ])
819
+ : undefined,
820
+ mapNullable(others, (a) => [
821
+ a.nodes.map((n) => wrapWithParentheses(n.getFullText())),
822
+ a.firstPosition,
823
+ ]),
824
+ ]
825
+ .filter((x) => x !== undefined)
826
+ .toSorted((a, b) => a[1] - b[1])
827
+ .flatMap((a) => a[0]);
828
+
829
+
830
+
831
+
832
+
833
+
834
+
835
+
836
+
837
+
838
+
839
+
840
+
841
+
842
+
843
+
844
+
845
+ options.replaceNode(node, unionToString({ types: sorted, op: operator, wrapWithReadonly: false }));
748
846
  };
749
- const unionToStringImpl = ({
750
- types,
751
- op,
752
- wrapWithReadonly
753
- }) => Arr.isEmpty(types) ? "never" : Arr.isFixedLengthArray(1, types) ? wrapWithReadonly === false ? wrapWithParentheses(types[0]) : `${isString(wrapWithReadonly) ? wrapWithReadonly : "Readonly"}<${types[0]}>` : wrapWithReadonly === false ? wrapWithParentheses(types.join(` ${op} `)) : `${isString(wrapWithReadonly) ? wrapWithReadonly : "Readonly"}<${types.join(` ${op} `)}>`;
754
- const transformParenthesizedTypeNode = (node, readonlyContext, options) => {
755
- const typeNode = node.getTypeNode();
756
- if (typeNode.isKind(tsm.SyntaxKind.ParenthesizedType)) {
757
- transformParenthesizedTypeNode(typeNode, readonlyContext, options);
758
- }
759
- transformNode(node.getTypeNode(), readonlyContext, options);
760
- const T = node.getTypeNode();
761
- if (
762
- // remove () if T is TypeReferenceNode
763
- // e.g. `(Readonly<A>) |-> Readonly<A>`
764
- T.isKind(tsm.SyntaxKind.TypeReference) || // remove () if T is ArrayTypeNode
765
- // e.g. `(A[]) |-> A[]`
766
- T.isKind(tsm.SyntaxKind.ArrayType) || // remove () if T is TupleTypeNode
767
- // e.g. `([A]) |-> [A]`
768
- T.isKind(tsm.SyntaxKind.TupleType) || // remove () if T is PrimitiveTypeNode
769
- // e.g. `(number) |-> number`
770
- isAtomicTypeNode(T) || // remove () if T is TypeLiteralNode
771
- // e.g. `({ member: V }) |-> { member: V }`
772
- T.isKind(tsm.SyntaxKind.TypeLiteral) || // collapse redundant nested parentheses if T is itself a ParenthesizedType
773
- // e.g. `((X)) |-> (X)`
774
- //
775
- // This is what makes the transform terminate on nested parentheses. The
776
- // union/intersection handler defensively wraps its result in parentheses
777
- // for precedence safety, which turns `(A | B)` into `((A | B))` when the
778
- // union already sits inside a `ParenthesizedType`. Collapsing the redundant
779
- // outer layer here means each pass removes exactly the layer the wrapping
780
- // added, so the parentheses no longer grow without bound (which previously
781
- // overflowed the parser stack on inputs like `type T = (('a' | 'b'))`).
782
- T.isKind(tsm.SyntaxKind.ParenthesizedType)
783
- ) {
784
- options.replaceNode(node, T.getFullText());
785
- }
847
+ const unionToString = ({ types: typesRaw, op, wrapWithReadonly, }) => {
848
+
849
+
850
+
851
+
852
+
853
+
854
+ const types = typesRaw.map((s) => s.trim());
855
+ return unionToStringImpl({ types, op, wrapWithReadonly });
786
856
  };
787
- const transformMembers = (members, readonlyModifier, readonlyContext, options) => {
788
- for (const mb of members) {
789
- if (hasDisableNextLineComment(mb)) {
790
- options.debugPrint("skipped by member disable-next-line comment");
791
- continue;
792
- }
793
- if (mb.isKind(tsm.SyntaxKind.PropertySignature)) {
794
- if (!checkIfPropertyNameShouldBeIgnored(mb.getNameNode(), options)) {
795
- transformPropertySignature(
796
- mb,
797
- readonlyModifier,
798
- readonlyContext,
799
- options
800
- );
801
- }
802
- continue;
803
- }
804
- if (mb.isKind(tsm.SyntaxKind.IndexSignature)) {
805
- transformIndexSignatureDeclaration(
806
- mb,
807
- readonlyModifier,
808
- readonlyContext,
809
- options
810
- );
811
- continue;
812
- }
813
- transformNode(mb, readonlyContext, options);
814
- }
857
+ const unionToStringImpl = ({ types, op, wrapWithReadonly, }) => Arr.isEmpty(types)
858
+ ? 'never'
859
+ : Arr.isFixedLengthArray(1, types)
860
+ ? wrapWithReadonly === false
861
+ ? wrapWithParentheses(types[0])
862
+ : `${isString(wrapWithReadonly) ? wrapWithReadonly : 'Readonly'}<${types[0]}>`
863
+ : wrapWithReadonly === false
864
+ ? wrapWithParentheses(types.join(` ${op} `))
865
+ : `${isString(wrapWithReadonly) ? wrapWithReadonly : 'Readonly'}<${types.join(` ${op} `)}>`;
866
+
867
+ const transformParenthesizedTypeNode = (
868
+
869
+ node, readonlyContext, options) => {
870
+ const typeNode = node.getTypeNode();
871
+ if (typeNode.isKind(tsm.SyntaxKind.ParenthesizedType)) {
872
+
873
+ transformParenthesizedTypeNode(typeNode, readonlyContext, options);
874
+ }
875
+ transformNode(node.getTypeNode(), readonlyContext, options);
876
+ const T = node.getTypeNode();
877
+ if (
878
+
879
+
880
+ T.isKind(tsm.SyntaxKind.TypeReference) ||
881
+
882
+
883
+ T.isKind(tsm.SyntaxKind.ArrayType) ||
884
+
885
+
886
+ T.isKind(tsm.SyntaxKind.TupleType) ||
887
+
888
+
889
+ isAtomicTypeNode(T) ||
890
+
891
+
892
+ T.isKind(tsm.SyntaxKind.TypeLiteral) ||
893
+
894
+
895
+
896
+
897
+
898
+
899
+
900
+
901
+
902
+
903
+ T.isKind(tsm.SyntaxKind.ParenthesizedType)) {
904
+ options.replaceNode(node, T.getFullText());
905
+ }
815
906
  };
816
- const transformPropertySignature = (node, readonlyModifier, readonlyContext, options) => {
817
- if (readonlyModifier === "remove" || readonlyContext.type === "DeepReadonly") {
818
- node.setIsReadonly(false);
819
- } else {
820
- node.setIsReadonly(true);
821
- }
822
- {
823
- const type = node.getTypeNode();
824
- if (type !== void 0) {
825
- transformNode(type, readonlyContext, options);
826
- }
827
- }
907
+
908
+
909
+
910
+
911
+
912
+ const transformMembers = (
913
+
914
+ members, readonlyModifier, readonlyContext, options) => {
915
+ for (const mb of members) {
916
+ if (hasDisableNextLineComment(mb)) {
917
+ options.debugPrint('skipped by member disable-next-line comment');
918
+ continue;
919
+ }
920
+ if (mb.isKind(tsm.SyntaxKind.PropertySignature)) {
921
+ if (!checkIfPropertyNameShouldBeIgnored(mb.getNameNode(), options)) {
922
+ transformPropertySignature(mb, readonlyModifier, readonlyContext, options);
923
+ }
924
+ continue;
925
+ }
926
+ if (mb.isKind(tsm.SyntaxKind.IndexSignature)) {
927
+ transformIndexSignatureDeclaration(mb, readonlyModifier, readonlyContext, options);
928
+ continue;
929
+ }
930
+ transformNode(mb, readonlyContext, options);
931
+ }
828
932
  };
829
- const transformIndexSignatureDeclaration = (node, readonlyModifier, readonlyContext, options) => {
830
- if (hasDisableNextLineComment(node, TRANSFORMER_NAME)) {
831
- options.debugPrint("skipped index signature by disable-next-line comment");
832
- return;
833
- }
834
- if (readonlyModifier === "remove" || readonlyContext.type === "DeepReadonly") {
835
- node.toggleModifier("readonly", false);
836
- } else {
837
- node.toggleModifier("readonly", true);
838
- }
839
- {
840
- const key = node.getKeyTypeNode();
841
- transformNode(key, readonlyContext, options);
842
- }
843
- {
844
- const ret = node.getReturnTypeNode();
845
- if (ret !== void 0) {
846
- transformNode(ret, readonlyContext, options);
847
- }
848
- }
933
+ const transformPropertySignature = (
934
+
935
+ node, readonlyModifier, readonlyContext, options) => {
936
+ if (readonlyModifier === 'remove' ||
937
+ readonlyContext.type === 'DeepReadonly') {
938
+ node.setIsReadonly(false);
939
+ }
940
+ else {
941
+ node.setIsReadonly(true);
942
+ }
943
+ {
944
+ const type = node.getTypeNode();
945
+ if (type !== undefined) {
946
+ transformNode(type, readonlyContext, options);
947
+ }
948
+ }
849
949
  };
850
- const checkIfPropertyNameShouldBeIgnored = (nameNode, options) => {
851
- return (
852
- // mut_x: number[]
853
- nameNode.isKind(tsm.SyntaxKind.Identifier) && pipe(nameNode.getText()).map(
854
- (nm) => options.ignoredPrefixes.some((p) => nm.startsWith(p))
855
- ).value || // "mut_x": number[]
856
- nameNode.isKind(tsm.SyntaxKind.StringLiteral) && pipe(nameNode.getLiteralValue()).map(
857
- (nm) => options.ignoredPrefixes.some((p) => nm.startsWith(p))
858
- ).value || // #memberName: number[] (class only)
859
- nameNode.isKind(tsm.SyntaxKind.PrivateIdentifier) && pipe(nameNode.getText()).map(
860
- (nm) => options.ignoredPrefixes.some((p) => nm.startsWith(`#${p}`))
861
- ).value || // [`mut_x`]: number[]
862
- nameNode.isKind(tsm.SyntaxKind.ComputedPropertyName) && pipe(nameNode.getExpression()).map((exp) => {
863
- if (exp.isKind(tsm.SyntaxKind.StringLiteral)) {
864
- const nm = exp.getLiteralValue();
865
- return options.ignoredPrefixes.some((p) => nm.startsWith(p));
866
- }
867
- return false;
868
- }).value
869
- );
950
+ const transformIndexSignatureDeclaration = (
951
+
952
+ node, readonlyModifier, readonlyContext, options) => {
953
+ if (hasDisableNextLineComment(node, TRANSFORMER_NAME)) {
954
+ options.debugPrint('skipped index signature by disable-next-line comment');
955
+ return;
956
+ }
957
+ if (readonlyModifier === 'remove' ||
958
+ readonlyContext.type === 'DeepReadonly') {
959
+
960
+ node.toggleModifier('readonly', false);
961
+ }
962
+ else {
963
+ node.toggleModifier('readonly', true);
964
+
965
+ }
966
+ {
967
+ const key = node.getKeyTypeNode();
968
+ transformNode(key, readonlyContext, options);
969
+ }
970
+ {
971
+ const ret = node.getReturnTypeNode();
972
+ if (ret !== undefined) {
973
+ transformNode(ret, readonlyContext, options);
974
+ }
975
+ }
870
976
  };
977
+ const checkIfPropertyNameShouldBeIgnored = (
978
+
979
+ nameNode, options) => {
871
980
 
872
- export { convertToReadonlyTransformer };
873
- //# sourceMappingURL=convert-to-readonly.mjs.map
981
+
982
+ return (
983
+
984
+ (nameNode.isKind(tsm.SyntaxKind.Identifier) &&
985
+ pipe(nameNode.getText()).map((nm) => options.ignoredPrefixes.some((p) => nm.startsWith(p))).value) ||
986
+
987
+ (nameNode.isKind(tsm.SyntaxKind.StringLiteral) &&
988
+ pipe(nameNode.getLiteralValue()).map((nm) => options.ignoredPrefixes.some((p) => nm.startsWith(p))).value) ||
989
+
990
+ (nameNode.isKind(tsm.SyntaxKind.PrivateIdentifier) &&
991
+ pipe(nameNode.getText()).map((nm) => options.ignoredPrefixes.some((p) => nm.startsWith(`#${p}`))).value) ||
992
+
993
+ (nameNode.isKind(tsm.SyntaxKind.ComputedPropertyName) &&
994
+ pipe(nameNode.getExpression()).map((exp) => {
995
+ if (exp.isKind(tsm.SyntaxKind.StringLiteral)) {
996
+ const nm = exp.getLiteralValue();
997
+ return options.ignoredPrefixes.some((p) => nm.startsWith(p));
998
+ }
999
+ return false;
1000
+ }).value));
1001
+ };
1002
+ //# sourceMappingURL=convert-to-readonly.mjs.map