ts-codemod-lib 3.5.1 → 4.0.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 (59) hide show
  1. package/README.md +7 -3
  2. package/dist/functions/ast-transformers/append-as-const.d.mts +20 -12
  3. package/dist/functions/ast-transformers/append-as-const.d.mts.map +1 -1
  4. package/dist/functions/ast-transformers/append-as-const.mjs +170 -88
  5. package/dist/functions/ast-transformers/append-as-const.mjs.map +1 -1
  6. package/dist/functions/ast-transformers/convert-to-readonly.d.mts +25 -6
  7. package/dist/functions/ast-transformers/convert-to-readonly.d.mts.map +1 -1
  8. package/dist/functions/ast-transformers/convert-to-readonly.mjs +272 -255
  9. package/dist/functions/ast-transformers/convert-to-readonly.mjs.map +1 -1
  10. package/dist/functions/ast-transformers/enable-no-unchecked-indexed-access.d.mts +8 -8
  11. package/dist/functions/ast-transformers/enable-no-unchecked-indexed-access.mjs +103 -104
  12. package/dist/functions/ast-transformers/enable-no-unchecked-indexed-access.mjs.map +1 -1
  13. package/dist/functions/ast-transformers/replace-record-with-unknown-record.d.mts +2 -2
  14. package/dist/functions/ast-transformers/replace-record-with-unknown-record.mjs +51 -51
  15. package/dist/functions/ast-transformers/replace-record-with-unknown-record.mjs.map +1 -1
  16. package/dist/functions/ast-transformers/transform-source-code.d.mts.map +1 -1
  17. package/dist/functions/ast-transformers/transform-source-code.mjs +69 -69
  18. package/dist/functions/ast-transformers/transform-source-code.mjs.map +1 -1
  19. package/dist/functions/constants/index.d.mts +1 -0
  20. package/dist/functions/constants/index.d.mts.map +1 -1
  21. package/dist/functions/constants/index.mjs +1 -0
  22. package/dist/functions/constants/index.mjs.map +1 -1
  23. package/dist/functions/constants/mutable-prefixes.d.mts +23 -0
  24. package/dist/functions/constants/mutable-prefixes.d.mts.map +1 -0
  25. package/dist/functions/constants/mutable-prefixes.mjs +28 -0
  26. package/dist/functions/constants/mutable-prefixes.mjs.map +1 -0
  27. package/dist/functions/functions/has-disable-next-line-comment.mjs +23 -23
  28. package/dist/functions/functions/has-disable-next-line-comment.mjs.map +1 -1
  29. package/dist/functions/functions/is-readonly-node.d.mts +2 -2
  30. package/dist/functions/functions/is-readonly-node.d.mts.map +1 -1
  31. package/dist/functions/functions/is-readonly-node.mjs +13 -13
  32. package/dist/functions/functions/is-readonly-node.mjs.map +1 -1
  33. package/dist/functions/functions/wrap-with-parentheses.d.mts +2 -2
  34. package/dist/functions/functions/wrap-with-parentheses.d.mts.map +1 -1
  35. package/dist/functions/functions/wrap-with-parentheses.mjs +87 -32
  36. package/dist/functions/functions/wrap-with-parentheses.mjs.map +1 -1
  37. package/dist/functions/utils/replace-node-with-text.d.mts +10 -11
  38. package/dist/functions/utils/replace-node-with-text.d.mts.map +1 -1
  39. package/dist/functions/utils/replace-node-with-text.mjs +0 -1
  40. package/dist/functions/utils/replace-node-with-text.mjs.map +1 -1
  41. package/package.json +6 -6
  42. package/src/functions/ast-transformers/append-as-const.mts +319 -174
  43. package/src/functions/ast-transformers/append-as-const.test.mts +118 -7
  44. package/src/functions/ast-transformers/convert-to-readonly.mts +568 -489
  45. package/src/functions/ast-transformers/convert-to-readonly.test.mts +381 -74
  46. package/src/functions/ast-transformers/enable-no-unchecked-indexed-access.mts +178 -179
  47. package/src/functions/ast-transformers/enable-no-unchecked-indexed-access.test.mts +29 -29
  48. package/src/functions/ast-transformers/replace-any-with-unknown.test.mts +6 -6
  49. package/src/functions/ast-transformers/replace-record-with-unknown-record.mts +75 -75
  50. package/src/functions/ast-transformers/replace-record-with-unknown-record.test.mts +6 -6
  51. package/src/functions/ast-transformers/transform-source-code.mts +110 -110
  52. package/src/functions/ast-transformers/transformer-specific-ignore.test.mts +6 -6
  53. package/src/functions/constants/index.mts +1 -0
  54. package/src/functions/constants/mutable-prefixes.mts +28 -0
  55. package/src/functions/functions/has-disable-next-line-comment.mts +35 -35
  56. package/src/functions/functions/is-readonly-node.mts +25 -25
  57. package/src/functions/functions/wrap-with-parentheses.mts +138 -52
  58. package/src/functions/functions/wrap-with-parentheses.test.mts +115 -0
  59. package/src/functions/utils/replace-node-with-text.mts +10 -11
@@ -1,5 +1,6 @@
1
- import { Arr, ISet, isString, mapNullable, match, pipe, } from 'ts-data-forge';
1
+ import { Arr, ISet, isRecord, isString, mapNullable, match, pipe, } from 'ts-data-forge';
2
2
  import * as tsm from 'ts-morph';
3
+ import { DEFAULT_MUTABLE_PREFIXES, DEFAULT_MUTABLE_TYPE_PREFIXES, } from '../constants/index.mjs';
3
4
  import { hasCallOrConstructSignature, hasDisableNextLineComment, isAtomicTypeNode, isConditionalTypeDistributionGuard, isReadonlyArrayTypeNode, isReadonlyTupleOrArrayTypeNode, isReadonlyTupleTypeNode, isReadonlyTypeReferenceNode, removeParentheses, shouldAvoidParenthesesForReadonly, unwrapReadonlyTypeArgText, wrapWithParentheses, } from '../functions/index.mjs';
4
5
  import { replaceNodeWithDebugPrint, replaceNodeWithText, } from '../utils/index.mjs';
5
6
  import { groupUnionIntersectionTypes, invalidDeepReadonlyTypeName, isMergeableReadonlyTypeReferenceNode, nextReadonlyContext, } from './readonly-transformer-helpers/index.mjs';
@@ -11,17 +12,20 @@ export const convertToReadonlyTransformer = (options) => {
11
12
  }
12
13
  const DeepReadonlyTypeName = options?.DeepReadonly?.typeName ?? 'DeepReadonly';
13
14
 
14
-
15
-
16
-
17
- const ignorePrefixes = ISet.create(options?.ignorePrefixes ?? ['mut_', '#mut_', '_mut_', 'draft']);
15
+ const untypedOptions = options;
16
+ if (isRecord(untypedOptions) &&
17
+ untypedOptions['ignorePrefixes'] !== undefined) {
18
+ throw new Error(`The "ignorePrefixes" option of ${TRANSFORMER_NAME} was replaced by "mutableVariablePrefixes", "mutableTypePrefixes" and "mutablePropertyPrefixes".`);
19
+ }
18
20
  const optionsInternal = {
19
21
  DeepReadonly: {
20
22
  typeName: DeepReadonlyTypeName,
21
23
  applyLevel: 'keep',
22
24
  },
23
25
  ignoreEmptyObjectTypes: options?.ignoreEmptyObjectTypes ?? true,
24
- ignoredPrefixes: ignorePrefixes,
26
+ mutableVariablePrefixes: ISet.create(options?.mutableVariablePrefixes ?? DEFAULT_MUTABLE_PREFIXES),
27
+ mutableTypePrefixes: ISet.create(options?.mutableTypePrefixes ?? DEFAULT_MUTABLE_TYPE_PREFIXES),
28
+ mutablePropertyPrefixes: ISet.create(options?.mutablePropertyPrefixes ?? DEFAULT_MUTABLE_PREFIXES),
25
29
  recordStyle: options?.recordStyle ?? 'ReadonlyRecord',
26
30
  debugPrint: options?.debug === true ? console.debug : () => { },
27
31
  replaceNode: options?.debug === true
@@ -41,6 +45,18 @@ const initialReadonlyContext = {
41
45
  type: 'none',
42
46
  indexedAccessDepth: 0,
43
47
  };
48
+
49
+ const hasMutableTypeName = (name, options) => name !== undefined &&
50
+ options.mutableTypePrefixes.some((p) => name.startsWith(p));
51
+
52
+
53
+
54
+
55
+ const mutablePrefixesOfParameter = (
56
+
57
+ param, options) => param.isParameterProperty()
58
+ ? options.mutablePropertyPrefixes
59
+ : options.mutableVariablePrefixes;
44
60
  const transformNode = (node, readonlyContext, options) => {
45
61
  if (tsm.Node.isTypeNode(node) ||
46
62
  node.isKind(tsm.SyntaxKind.InterfaceDeclaration) ||
@@ -58,10 +74,10 @@ const transformNode = (node, readonlyContext, options) => {
58
74
 
59
75
  if (node.isKind(tsm.SyntaxKind.VariableDeclaration)) {
60
76
  const nodeName = node.getName();
61
- if (options.ignoredPrefixes.some((p) => nodeName.startsWith(p))) {
77
+ if (options.mutableVariablePrefixes.some((p) => nodeName.startsWith(p))) {
62
78
 
63
79
 
64
- options.debugPrint('skipped variable declaration by ignorePrefixes');
80
+ options.debugPrint('skipped variable declaration by mutableVariablePrefixes');
65
81
  return;
66
82
  }
67
83
 
@@ -77,12 +93,12 @@ const transformNode = (node, readonlyContext, options) => {
77
93
 
78
94
  if ((node.isKind(tsm.SyntaxKind.FunctionDeclaration) ||
79
95
  node.isKind(tsm.SyntaxKind.FunctionExpression)) &&
80
- options.ignoredPrefixes.some((p) => node.getName()?.startsWith(p) === true)) {
96
+ options.mutableVariablePrefixes.some((p) => node.getName()?.startsWith(p) === true)) {
81
97
  return;
82
98
  }
83
99
  if (node.isKind(tsm.SyntaxKind.Parameter)) {
84
100
  const nodeName = node.getName();
85
- if (options.ignoredPrefixes.some((p) => nodeName.startsWith(p))) {
101
+ if (mutablePrefixesOfParameter(node, options).some((p) => nodeName.startsWith(p))) {
86
102
 
87
103
 
88
104
  return;
@@ -97,7 +113,7 @@ const transformNode = (node, readonlyContext, options) => {
97
113
 
98
114
 
99
115
  if (node.isKind(tsm.SyntaxKind.TypeAliasDeclaration) &&
100
- options.ignoredPrefixes.some((p) => node.getName().startsWith(p))) {
116
+ hasMutableTypeName(node.getName(), options)) {
101
117
  return;
102
118
  }
103
119
  if (node.isKind(tsm.SyntaxKind.TypeReference)) {
@@ -147,16 +163,21 @@ const transformNode = (node, readonlyContext, options) => {
147
163
  if (node.isKind(tsm.SyntaxKind.InterfaceDeclaration)) {
148
164
 
149
165
 
150
- if (options.ignoredPrefixes.some((p) => node.getName().startsWith(p))) {
166
+ if (hasMutableTypeName(node.getName(), options)) {
151
167
  return;
152
168
  }
153
169
  transformInterfaceDeclarationNode(node, options);
154
170
  return;
155
171
  }
172
+
173
+ if (node.isKind(tsm.SyntaxKind.ClassExpression) &&
174
+ hasMutableTypeName(node.getName(), options)) {
175
+ return;
176
+ }
156
177
  if (node.isKind(tsm.SyntaxKind.ClassDeclaration)) {
157
178
 
158
179
 
159
- if (options.ignoredPrefixes.some((p) => node.getName()?.startsWith(p) === true)) {
180
+ if (hasMutableTypeName(node.getName(), options)) {
160
181
  return;
161
182
  }
162
183
  transformClassDeclarationNode(node, options);
@@ -189,6 +210,137 @@ const transformNode = (node, readonlyContext, options) => {
189
210
 
190
211
 
191
212
 
213
+ const readonlyRecordText = (keyText, valueText, options) => options.recordStyle === 'ReadonlyRecord'
214
+ ? `ReadonlyRecord<${keyText}, ${valueText}>`
215
+ : `Readonly<Record<${keyText}, ${valueText}>>`;
216
+
217
+
218
+
219
+
220
+
221
+ const commentsBetween = (
222
+
223
+ sourceFile, from, to) => {
224
+ if (to <= from) {
225
+ return '';
226
+ }
227
+ const scanner = tsm.ts.createScanner(tsm.ts.ScriptTarget.Latest, false, tsm.ts.LanguageVariant.Standard, sourceFile.getFullText(), undefined, from, to - from);
228
+ const mut_comments = [];
229
+ while (scanner.scan() !== tsm.ts.SyntaxKind.EndOfFileToken) {
230
+ const token = scanner.getToken();
231
+
232
+ const separator = token === tsm.ts.SyntaxKind.SingleLineCommentTrivia
233
+ ? '\n'
234
+ : token === tsm.ts.SyntaxKind.MultiLineCommentTrivia
235
+ ? ' '
236
+ : undefined;
237
+ if (separator !== undefined) {
238
+ mut_comments.push(`${scanner.getTokenText()}${separator}`);
239
+ }
240
+ }
241
+ return mut_comments.join('');
242
+ };
243
+
244
+ const getDeepReadonlyTypeArg = (
245
+
246
+ node, deepReadonlyTypeName) => {
247
+ if (!node.isKind(tsm.SyntaxKind.TypeReference)) {
248
+ return undefined;
249
+ }
250
+ const typeName = node.getTypeName();
251
+ const typeArguments = node.getTypeArguments();
252
+ return typeName.isKind(tsm.SyntaxKind.Identifier) &&
253
+ typeName.getText() === deepReadonlyTypeName &&
254
+ Arr.isFixedLengthArray(1, typeArguments)
255
+ ? typeArguments[0]
256
+ : undefined;
257
+ };
258
+ const checkIfPropertyNameShouldBeIgnored = (
259
+
260
+ nameNode, options) => {
261
+
262
+
263
+ return (
264
+
265
+ (nameNode.isKind(tsm.SyntaxKind.Identifier) &&
266
+ pipe(nameNode.getText()).map((nm) => options.mutablePropertyPrefixes.some((p) => nm.startsWith(p))).value) ||
267
+
268
+ (nameNode.isKind(tsm.SyntaxKind.StringLiteral) &&
269
+ pipe(nameNode.getLiteralValue()).map((nm) => options.mutablePropertyPrefixes.some((p) => nm.startsWith(p))).value) ||
270
+
271
+ (nameNode.isKind(tsm.SyntaxKind.PrivateIdentifier) &&
272
+ pipe(nameNode.getText()).map((nm) => options.mutablePropertyPrefixes.some((p) => nm.startsWith(p) || nm.startsWith(`#${p}`))).value) ||
273
+
274
+ (nameNode.isKind(tsm.SyntaxKind.ComputedPropertyName) &&
275
+ pipe(nameNode.getExpression()).map((exp) => {
276
+ if (exp.isKind(tsm.SyntaxKind.StringLiteral) ||
277
+ exp.isKind(tsm.SyntaxKind.NoSubstitutionTemplateLiteral)) {
278
+ const nm = exp.getLiteralValue();
279
+ return options.mutablePropertyPrefixes.some((p) => nm.startsWith(p));
280
+ }
281
+ return false;
282
+ }).value));
283
+ };
284
+
285
+
286
+
287
+
288
+
289
+
290
+
291
+
292
+
293
+
294
+
295
+
296
+
297
+
298
+
299
+ const isLeftUnwrapped = (
300
+
301
+ node, options) => {
302
+ const members = node.getMembers();
303
+ const isMethodOrAccessor = (
304
+
305
+ mb) => mb.isKind(tsm.SyntaxKind.MethodSignature) ||
306
+ mb.isKind(tsm.SyntaxKind.GetAccessor) ||
307
+ mb.isKind(tsm.SyntaxKind.SetAccessor);
308
+ const isIgnoredMember = (
309
+
310
+ mb) => (mb.isKind(tsm.SyntaxKind.PropertySignature) || isMethodOrAccessor(mb)) &&
311
+ checkIfPropertyNameShouldBeIgnored(mb.getNameNode(), options);
312
+ const needsReadonlyWrapper = (
313
+
314
+ mb) => !isIgnoredMember(mb) &&
315
+ (isMethodOrAccessor(mb) ||
316
+ ((mb.isKind(tsm.SyntaxKind.PropertySignature) ||
317
+ mb.isKind(tsm.SyntaxKind.IndexSignature)) &&
318
+ !mb.hasModifier(tsm.SyntaxKind.ReadonlyKeyword) &&
319
+ hasDisableNextLineComment(mb)));
320
+ return members.some(isIgnoredMember) && !members.some(needsReadonlyWrapper);
321
+ };
322
+
323
+ const hasDisableNextLineCommentAround = (node) => {
324
+ if (hasDisableNextLineComment(node, TRANSFORMER_NAME)) {
325
+ return true;
326
+ }
327
+ const parent = node.getParent();
328
+ return (parent?.isKind(tsm.SyntaxKind.ParenthesizedType) === true &&
329
+ hasDisableNextLineCommentAround(parent));
330
+ };
331
+
332
+
333
+
334
+
335
+
336
+
337
+
338
+ const takesOffReadonly = (
339
+
340
+ node, options) => isLeftUnwrapped(node, options) &&
341
+ !hasCallOrConstructSignature(node) &&
342
+ !hasDisableNextLineCommentAround(node);
343
+
192
344
 
193
345
 
194
346
 
@@ -511,10 +663,6 @@ node, readonlyContext, options) => {
511
663
  }
512
664
  };
513
665
 
514
- const readonlyRecordText = (keyText, valueText, options) => options.recordStyle === 'ReadonlyRecord'
515
- ? `ReadonlyRecord<${keyText}, ${valueText}>`
516
- : `Readonly<Record<${keyText}, ${valueText}>>`;
517
-
518
666
  const transformArrayTypeNode = (
519
667
 
520
668
  node, readonlyContext, options) => {
@@ -555,7 +703,7 @@ node, readonlyContext, options) => {
555
703
  });
556
704
  for (const el of node.getElements()) {
557
705
  if (el.isKind(tsm.SyntaxKind.NamedTupleMember)) {
558
- if (options.ignoredPrefixes.every((p) => !el.getName().startsWith(p))) {
706
+ if (options.mutablePropertyPrefixes.every((p) => !el.getName().startsWith(p))) {
559
707
  transformNode(el.getTypeNode(), nextReadonlyContextValue, options);
560
708
  }
561
709
  }
@@ -781,7 +929,7 @@ param, options) => {
781
929
  return;
782
930
  }
783
931
  const name = param.getName();
784
- if (options.ignoredPrefixes.some((p) => name.startsWith(p))) {
932
+ if (mutablePrefixesOfParameter(param, options).some((p) => name.startsWith(p))) {
785
933
  return;
786
934
  }
787
935
 
@@ -819,104 +967,6 @@ const transformUnionTypeNode = (
819
967
  node, readonlyContext, options) => {
820
968
  transformUnionOrIntersectionTypeNodeImpl(node, readonlyContext, options, '|');
821
969
  };
822
- const transformUnionOrIntersectionTypeNodeImpl = (
823
-
824
- node, readonlyContext, options, operator) => {
825
- const nextReadonlyContextValue = nextReadonlyContext({
826
- currentReadonlyContext: readonlyContext,
827
- nextReadonlyContextType: 'none',
828
- indexedAccessDepthChange: 'keep',
829
- });
830
-
831
- for (const n of node.getTypeNodes() ) {
832
- transformNode(n, nextReadonlyContextValue, options);
833
- }
834
- const newTypes = node.getTypeNodes();
835
- const withLeadingComments = (
836
-
837
- member, memberText) => `${leadingCommentsOfMember(node, member)}${memberText}`;
838
-
839
-
840
- const wrapsTypeLiterals = nextReadonlyContextValue.type !== 'DeepReadonly' &&
841
- nextReadonlyContextValue.indexedAccessDepth === 0;
842
-
843
-
844
- const underReadonly = readonlyContext.type === 'Readonly' &&
845
- readonlyContext.indexedAccessDepth === 0 &&
846
- isTypeArgumentOfReadonly(node);
847
- const { primitives, arraysAndTuples, typeLiterals, others } = groupUnionIntersectionTypes(newTypes, {
848
- operator,
849
- underReadonly,
850
- deepReadonlyTypeName: options.DeepReadonly.typeName,
851
- keptUnwrapped: (t) => wrapsTypeLiterals && takesOffReadonly(t, options),
852
- });
853
- options.debugPrint({ primitives, arraysAndTuples, typeLiterals, others });
854
-
855
- const isEmptyTypeLiteral = (
856
-
857
- n) => n.isKind(tsm.SyntaxKind.TypeLiteral) && Arr.isEmpty(n.getMembers());
858
- const nonEmptyTypeLiterals = typeLiterals === undefined
859
- ? undefined
860
- : options.ignoreEmptyObjectTypes
861
- ? typeLiterals.nodes.filter((n) => !isEmptyTypeLiteral(n))
862
- : typeLiterals.nodes;
863
- const emptyTypeLiterals = typeLiterals === undefined || !options.ignoreEmptyObjectTypes
864
- ? undefined
865
- : typeLiterals.nodes.filter(isEmptyTypeLiteral);
866
- const typeLiteralsWrappedWithReadonly = nonEmptyTypeLiterals === undefined || Arr.isEmpty(nonEmptyTypeLiterals)
867
- ? []
868
- : [
869
- unionToString({
870
- types: nonEmptyTypeLiterals.map((n) => withLeadingComments(n, isMergeableReadonlyTypeReferenceNode(n, operator)
871
- ?
872
-
873
- readonlyTypeArgTextSplicedInto(n, operator)
874
- : n.isKind(tsm.SyntaxKind.TypeLiteral)
875
- ? n.getText()
876
- :
877
- wrapWithParentheses(n.getText()))),
878
- op: operator,
879
- wrapWithReadonly: wrapsTypeLiterals,
880
- }),
881
- ];
882
-
883
- const sorted = [
884
- mapNullable(primitives, (a) => [
885
- a.nodes.map((n) => withLeadingComments(n, n.getText())),
886
- a.firstPosition,
887
- ]),
888
- mapNullable(arraysAndTuples, (a) => [
889
- a.nodes.map((n) => withLeadingComments(n, n.getText())),
890
- a.firstPosition,
891
- ]),
892
- nonEmptyTypeLiterals !== undefined && Arr.isNonEmpty(nonEmptyTypeLiterals)
893
- ? mapNullable(typeLiterals, (a) => [typeLiteralsWrappedWithReadonly, a.firstPosition])
894
- : undefined,
895
- emptyTypeLiterals !== undefined && Arr.isNonEmpty(emptyTypeLiterals)
896
- ? mapNullable(typeLiterals, (a) => [
897
- emptyTypeLiterals.map((n) => withLeadingComments(n, n.getText())),
898
-
899
- a.firstPosition +
900
- (nonEmptyTypeLiterals !== undefined &&
901
- Arr.isNonEmpty(nonEmptyTypeLiterals)
902
- ? 0.5
903
- : 0),
904
- ])
905
- : undefined,
906
- mapNullable(others, (a) => [
907
-
908
-
909
- operator === '|'
910
- ? mergeDeepReadonlyUnionMembers(a.nodes, options.DeepReadonly.typeName, withLeadingComments)
911
- : a.nodes.map((n) => withLeadingComments(n, wrapWithParentheses(n.getText()))),
912
- a.firstPosition,
913
- ]),
914
- ]
915
- .filter((x) => x !== undefined)
916
- .toSorted((a, b) => a[1] - b[1])
917
- .flatMap((a) => a[0]);
918
-
919
-
920
970
 
921
971
 
922
972
 
@@ -925,14 +975,28 @@ node, readonlyContext, options, operator) => {
925
975
 
926
976
 
927
977
 
978
+ const textWithoutParentheses = (
928
979
 
980
+ outer,
929
981
 
982
+ inner) => `${commentsBetween(outer.getSourceFile(), outer.getPos(), inner.getStart())}${inner.getText()}`;
983
+ const unionToStringImpl = ({ types, op, wrapWithReadonly, }) => Arr.isEmpty(types)
984
+ ? 'never'
985
+ : Arr.isFixedLengthArray(1, types)
986
+ ? wrapWithReadonly === false
987
+ ? wrapWithParentheses(types[0])
988
+ : `${isString(wrapWithReadonly) ? wrapWithReadonly : 'Readonly'}<${types[0]}>`
989
+ : wrapWithReadonly === false
990
+ ? wrapWithParentheses(types.join(` ${op} `))
991
+ : `${isString(wrapWithReadonly) ? wrapWithReadonly : 'Readonly'}<${types.join(` ${op} `)}>`;
992
+ const unionToString = ({ types: typesRaw, op, wrapWithReadonly, }) => {
930
993
 
931
994
 
932
995
 
933
996
 
934
997
 
935
- options.replaceNode(node, unionToString({ types: sorted, op: operator, wrapWithReadonly: false }));
998
+ const types = typesRaw.map((s) => s.trim());
999
+ return unionToStringImpl({ types, op, wrapWithReadonly });
936
1000
  };
937
1001
 
938
1002
 
@@ -1052,79 +1116,121 @@ node, operator) => {
1052
1116
  ? textWithoutParentheses(typeArgument, T)
1053
1117
  : unwrapReadonlyTypeArgText(node);
1054
1118
  };
1119
+ const transformUnionOrIntersectionTypeNodeImpl = (
1055
1120
 
1121
+ node, readonlyContext, options, operator) => {
1122
+ const nextReadonlyContextValue = nextReadonlyContext({
1123
+ currentReadonlyContext: readonlyContext,
1124
+ nextReadonlyContextType: 'none',
1125
+ indexedAccessDepthChange: 'keep',
1126
+ });
1056
1127
 
1128
+ for (const n of node.getTypeNodes() ) {
1129
+ transformNode(n, nextReadonlyContextValue, options);
1130
+ }
1131
+ const newTypes = node.getTypeNodes();
1132
+ const withLeadingComments = (
1057
1133
 
1134
+ member, memberText) => `${leadingCommentsOfMember(node, member)}${memberText}`;
1058
1135
 
1059
1136
 
1137
+ const wrapsTypeLiterals = nextReadonlyContextValue.type !== 'DeepReadonly' &&
1138
+ nextReadonlyContextValue.indexedAccessDepth === 0;
1060
1139
 
1061
1140
 
1141
+ const underReadonly = readonlyContext.type === 'Readonly' &&
1142
+ readonlyContext.indexedAccessDepth === 0 &&
1143
+ isTypeArgumentOfReadonly(node);
1144
+ const { primitives, arraysAndTuples, typeLiterals, others } = groupUnionIntersectionTypes(newTypes, {
1145
+ operator,
1146
+ underReadonly,
1147
+ deepReadonlyTypeName: options.DeepReadonly.typeName,
1148
+ keptUnwrapped: (t) => wrapsTypeLiterals && takesOffReadonly(t, options),
1149
+ });
1150
+ options.debugPrint({ primitives, arraysAndTuples, typeLiterals, others });
1062
1151
 
1063
- const textWithoutParentheses = (
1152
+ const isEmptyTypeLiteral = (
1064
1153
 
1065
- outer,
1154
+ n) => n.isKind(tsm.SyntaxKind.TypeLiteral) && Arr.isEmpty(n.getMembers());
1155
+ const nonEmptyTypeLiterals = typeLiterals === undefined
1156
+ ? undefined
1157
+ : options.ignoreEmptyObjectTypes
1158
+ ? typeLiterals.nodes.filter((n) => !isEmptyTypeLiteral(n))
1159
+ : typeLiterals.nodes;
1160
+ const emptyTypeLiterals = typeLiterals === undefined || !options.ignoreEmptyObjectTypes
1161
+ ? undefined
1162
+ : typeLiterals.nodes.filter(isEmptyTypeLiteral);
1163
+ const typeLiteralsWrappedWithReadonly = nonEmptyTypeLiterals === undefined || Arr.isEmpty(nonEmptyTypeLiterals)
1164
+ ? []
1165
+ : [
1166
+ unionToString({
1167
+ types: nonEmptyTypeLiterals.map((n) => withLeadingComments(n, isMergeableReadonlyTypeReferenceNode(n, operator)
1168
+ ?
1066
1169
 
1067
- inner) => `${commentsBetween(outer.getSourceFile(), outer.getPos(), inner.getStart())}${inner.getText()}`;
1170
+ readonlyTypeArgTextSplicedInto(n, operator)
1171
+ : n.isKind(tsm.SyntaxKind.TypeLiteral)
1172
+ ? n.getText()
1173
+ :
1174
+ wrapWithParentheses(n.getText()))),
1175
+ op: operator,
1176
+ wrapWithReadonly: wrapsTypeLiterals,
1177
+ }),
1178
+ ];
1068
1179
 
1180
+ const sorted = [
1181
+ mapNullable(primitives, (a) => [
1182
+ a.nodes.map((n) => withLeadingComments(n, n.getText())),
1183
+ a.firstPosition,
1184
+ ]),
1185
+ mapNullable(arraysAndTuples, (a) => [
1186
+ a.nodes.map((n) => withLeadingComments(n, n.getText())),
1187
+ a.firstPosition,
1188
+ ]),
1189
+ nonEmptyTypeLiterals !== undefined && Arr.isNonEmpty(nonEmptyTypeLiterals)
1190
+ ? mapNullable(typeLiterals, (a) => [typeLiteralsWrappedWithReadonly, a.firstPosition])
1191
+ : undefined,
1192
+ emptyTypeLiterals !== undefined && Arr.isNonEmpty(emptyTypeLiterals)
1193
+ ? mapNullable(typeLiterals, (a) => [
1194
+ emptyTypeLiterals.map((n) => withLeadingComments(n, n.getText())),
1069
1195
 
1196
+ a.firstPosition +
1197
+ (nonEmptyTypeLiterals !== undefined &&
1198
+ Arr.isNonEmpty(nonEmptyTypeLiterals)
1199
+ ? 0.5
1200
+ : 0),
1201
+ ])
1202
+ : undefined,
1203
+ mapNullable(others, (a) => [
1070
1204
 
1071
1205
 
1206
+ operator === '|'
1207
+ ? mergeDeepReadonlyUnionMembers(a.nodes, options.DeepReadonly.typeName, withLeadingComments)
1208
+ : a.nodes.map((n) => withLeadingComments(n, wrapWithParentheses(n.getText()))),
1209
+ a.firstPosition,
1210
+ ]),
1211
+ ]
1212
+ .filter((x) => x !== undefined)
1213
+ .toSorted((a, b) => a[1] - b[1])
1214
+ .flatMap((a) => a[0]);
1072
1215
 
1073
- const commentsBetween = (
1074
1216
 
1075
- sourceFile, from, to) => {
1076
- if (to <= from) {
1077
- return '';
1078
- }
1079
- const scanner = tsm.ts.createScanner(tsm.ts.ScriptTarget.Latest, false, tsm.ts.LanguageVariant.Standard, sourceFile.getFullText(), undefined, from, to - from);
1080
- const mut_comments = [];
1081
- while (scanner.scan() !== tsm.ts.SyntaxKind.EndOfFileToken) {
1082
- const token = scanner.getToken();
1083
1217
 
1084
- const separator = token === tsm.ts.SyntaxKind.SingleLineCommentTrivia
1085
- ? '\n'
1086
- : token === tsm.ts.SyntaxKind.MultiLineCommentTrivia
1087
- ? ' '
1088
- : undefined;
1089
- if (separator !== undefined) {
1090
- mut_comments.push(`${scanner.getTokenText()}${separator}`);
1091
- }
1092
- }
1093
- return mut_comments.join('');
1094
- };
1095
1218
 
1096
- const getDeepReadonlyTypeArg = (
1097
1219
 
1098
- node, deepReadonlyTypeName) => {
1099
- if (!node.isKind(tsm.SyntaxKind.TypeReference)) {
1100
- return undefined;
1101
- }
1102
- const typeName = node.getTypeName();
1103
- const typeArguments = node.getTypeArguments();
1104
- return typeName.isKind(tsm.SyntaxKind.Identifier) &&
1105
- typeName.getText() === deepReadonlyTypeName &&
1106
- Arr.isFixedLengthArray(1, typeArguments)
1107
- ? typeArguments[0]
1108
- : undefined;
1109
- };
1110
- const unionToString = ({ types: typesRaw, op, wrapWithReadonly, }) => {
1111
1220
 
1112
1221
 
1113
1222
 
1114
1223
 
1115
1224
 
1116
- const types = typesRaw.map((s) => s.trim());
1117
- return unionToStringImpl({ types, op, wrapWithReadonly });
1225
+
1226
+
1227
+
1228
+
1229
+
1230
+
1231
+
1232
+ options.replaceNode(node, unionToString({ types: sorted, op: operator, wrapWithReadonly: false }));
1118
1233
  };
1119
- const unionToStringImpl = ({ types, op, wrapWithReadonly, }) => Arr.isEmpty(types)
1120
- ? 'never'
1121
- : Arr.isFixedLengthArray(1, types)
1122
- ? wrapWithReadonly === false
1123
- ? wrapWithParentheses(types[0])
1124
- : `${isString(wrapWithReadonly) ? wrapWithReadonly : 'Readonly'}<${types[0]}>`
1125
- : wrapWithReadonly === false
1126
- ? wrapWithParentheses(types.join(` ${op} `))
1127
- : `${isString(wrapWithReadonly) ? wrapWithReadonly : 'Readonly'}<${types.join(` ${op} `)}>`;
1128
1234
 
1129
1235
  const transformParenthesizedTypeNode = (
1130
1236
 
@@ -1138,16 +1244,12 @@ node, readonlyContext, options) => {
1138
1244
  const T = node.getTypeNode();
1139
1245
  if (
1140
1246
 
1141
-
1142
1247
  T.isKind(tsm.SyntaxKind.TypeReference) ||
1143
1248
 
1144
-
1145
1249
  T.isKind(tsm.SyntaxKind.ArrayType) ||
1146
1250
 
1147
-
1148
1251
  T.isKind(tsm.SyntaxKind.TupleType) ||
1149
1252
 
1150
-
1151
1253
  isAtomicTypeNode(T) ||
1152
1254
 
1153
1255
 
@@ -1244,89 +1346,4 @@ node, readonlyModifier, readonlyContext, options) => {
1244
1346
  }
1245
1347
  }
1246
1348
  };
1247
- const checkIfPropertyNameShouldBeIgnored = (
1248
-
1249
- nameNode, options) => {
1250
-
1251
-
1252
- return (
1253
-
1254
- (nameNode.isKind(tsm.SyntaxKind.Identifier) &&
1255
- pipe(nameNode.getText()).map((nm) => options.ignoredPrefixes.some((p) => nm.startsWith(p))).value) ||
1256
-
1257
- (nameNode.isKind(tsm.SyntaxKind.StringLiteral) &&
1258
- pipe(nameNode.getLiteralValue()).map((nm) => options.ignoredPrefixes.some((p) => nm.startsWith(p))).value) ||
1259
-
1260
- (nameNode.isKind(tsm.SyntaxKind.PrivateIdentifier) &&
1261
- pipe(nameNode.getText()).map((nm) => options.ignoredPrefixes.some((p) => nm.startsWith(`#${p}`))).value) ||
1262
-
1263
- (nameNode.isKind(tsm.SyntaxKind.ComputedPropertyName) &&
1264
- pipe(nameNode.getExpression()).map((exp) => {
1265
- if (exp.isKind(tsm.SyntaxKind.StringLiteral) ||
1266
- exp.isKind(tsm.SyntaxKind.NoSubstitutionTemplateLiteral)) {
1267
- const nm = exp.getLiteralValue();
1268
- return options.ignoredPrefixes.some((p) => nm.startsWith(p));
1269
- }
1270
- return false;
1271
- }).value));
1272
- };
1273
-
1274
-
1275
-
1276
-
1277
-
1278
-
1279
-
1280
-
1281
-
1282
-
1283
-
1284
-
1285
-
1286
-
1287
-
1288
- const isLeftUnwrapped = (
1289
-
1290
- node, options) => {
1291
- const members = node.getMembers();
1292
- const isMethodOrAccessor = (
1293
-
1294
- mb) => mb.isKind(tsm.SyntaxKind.MethodSignature) ||
1295
- mb.isKind(tsm.SyntaxKind.GetAccessor) ||
1296
- mb.isKind(tsm.SyntaxKind.SetAccessor);
1297
- const isIgnoredMember = (
1298
-
1299
- mb) => (mb.isKind(tsm.SyntaxKind.PropertySignature) || isMethodOrAccessor(mb)) &&
1300
- checkIfPropertyNameShouldBeIgnored(mb.getNameNode(), options);
1301
- const needsReadonlyWrapper = (
1302
-
1303
- mb) => !isIgnoredMember(mb) &&
1304
- (isMethodOrAccessor(mb) ||
1305
- ((mb.isKind(tsm.SyntaxKind.PropertySignature) ||
1306
- mb.isKind(tsm.SyntaxKind.IndexSignature)) &&
1307
- !mb.hasModifier(tsm.SyntaxKind.ReadonlyKeyword) &&
1308
- hasDisableNextLineComment(mb)));
1309
- return members.some(isIgnoredMember) && !members.some(needsReadonlyWrapper);
1310
- };
1311
-
1312
- const hasDisableNextLineCommentAround = (node) => {
1313
- if (hasDisableNextLineComment(node, TRANSFORMER_NAME)) {
1314
- return true;
1315
- }
1316
- const parent = node.getParent();
1317
- return (parent?.isKind(tsm.SyntaxKind.ParenthesizedType) === true &&
1318
- hasDisableNextLineCommentAround(parent));
1319
- };
1320
-
1321
-
1322
-
1323
-
1324
-
1325
-
1326
-
1327
- const takesOffReadonly = (
1328
-
1329
- node, options) => isLeftUnwrapped(node, options) &&
1330
- !hasCallOrConstructSignature(node) &&
1331
- !hasDisableNextLineCommentAround(node);
1332
1349
  //# sourceMappingURL=convert-to-readonly.mjs.map