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
@@ -14,39 +14,6 @@ import {
14
14
  } from './convert-to-readonly.mjs';
15
15
  import { transformSourceCode } from './transform-source-code.mjs';
16
16
 
17
- const testFn = async ({
18
- source,
19
- expected,
20
- debug,
21
- options,
22
- isTsx = false,
23
- }: Readonly<{
24
- source: string;
25
- expected: string;
26
- debug?: boolean;
27
- options?: ReadonlyTransformerOptions;
28
- isTsx?: boolean;
29
- }>): Promise<void> => {
30
- if (debug !== true) {
31
- // eslint-disable-next-line vitest/no-restricted-vi-methods
32
- vi.spyOn(console, 'debug').mockImplementation(() => {});
33
-
34
- // eslint-disable-next-line vitest/no-restricted-vi-methods
35
- vi.spyOn(console, 'log').mockImplementation(() => {});
36
-
37
- // eslint-disable-next-line vitest/no-restricted-vi-methods
38
- vi.spyOn(console, 'trace').mockImplementation(() => {});
39
- }
40
-
41
- const transformedCodeRaw = await formatter(
42
- transformSourceCode(source, isTsx, [convertToReadonlyTransformer(options)]),
43
- );
44
-
45
- const expectedFormatted = await formatter(expected);
46
-
47
- expect(transformedCodeRaw).toBe(expectedFormatted);
48
- };
49
-
50
17
  /**
51
18
  * Normalizes whitespace in a code string, primarily for comparing AST structure outputs.
52
19
  * - Preserves newlines immediately following line comments (`//`).
@@ -93,6 +60,39 @@ const formatter = async (code: string): Promise<string> => {
93
60
  });
94
61
  };
95
62
 
63
+ const testFn = async ({
64
+ source,
65
+ expected,
66
+ debug,
67
+ options,
68
+ isTsx = false,
69
+ }: Readonly<{
70
+ source: string;
71
+ expected: string;
72
+ debug?: boolean;
73
+ options?: ReadonlyTransformerOptions;
74
+ isTsx?: boolean;
75
+ }>): Promise<void> => {
76
+ if (debug !== true) {
77
+ // eslint-disable-next-line vitest/no-restricted-vi-methods
78
+ vi.spyOn(console, 'debug').mockImplementation(() => {});
79
+
80
+ // eslint-disable-next-line vitest/no-restricted-vi-methods
81
+ vi.spyOn(console, 'log').mockImplementation(() => {});
82
+
83
+ // eslint-disable-next-line vitest/no-restricted-vi-methods
84
+ vi.spyOn(console, 'trace').mockImplementation(() => {});
85
+ }
86
+
87
+ const transformedCodeRaw = await formatter(
88
+ transformSourceCode(source, isTsx, [convertToReadonlyTransformer(options)]),
89
+ );
90
+
91
+ const expectedFormatted = await formatter(expected);
92
+
93
+ expect(transformedCodeRaw).toBe(expectedFormatted);
94
+ };
95
+
96
96
  describe(convertToReadonlyTransformer, () => {
97
97
  describe('TypeReferences', () => {
98
98
  describe('Sets', () => {
@@ -788,6 +788,33 @@ describe(convertToReadonlyTransformer, () => {
788
788
  function foo(a: readonly number[], b: Promise<readonly number[]>) {}
789
789
  `,
790
790
  },
791
+ {
792
+ name: 'Array<T> whose element type has a parenthesis inside a string literal type',
793
+ source: dedent`
794
+ type T = Array<(A | '(') | (B)>;
795
+ `,
796
+ expected: dedent`
797
+ type T = readonly ((A | '(') | (B))[];
798
+ `,
799
+ },
800
+ {
801
+ name: 'Array<T> whose element type has a parenthesis inside a comment',
802
+ source: dedent`
803
+ type T = Array<(A | /* ( */ C) | (B)>;
804
+ `,
805
+ expected: dedent`
806
+ type T = readonly ((A | /* ( */ C) | (B))[];
807
+ `,
808
+ },
809
+ {
810
+ name: 'Array<T> whose element type has a parenthesis inside a template literal type',
811
+ source: dedent`
812
+ type T = Array<(\`(\${A}\` | C) | (B)>;
813
+ `,
814
+ expected: dedent`
815
+ type T = readonly ((\`(\${A}\` | C) | (B))[];
816
+ `,
817
+ },
791
818
  {
792
819
  name: 'Mutable array types in function args (generic)',
793
820
  source: dedent`
@@ -1980,6 +2007,7 @@ describe(convertToReadonlyTransformer, () => {
1980
2007
  }
1981
2008
  }
1982
2009
  `,
2010
+ options: { mutablePropertyPrefixes: ['_mut_'] },
1983
2011
  },
1984
2012
  {
1985
2013
  name: 'Method with "draft" prefix',
@@ -1997,6 +2025,7 @@ describe(convertToReadonlyTransformer, () => {
1997
2025
  }
1998
2026
  }
1999
2027
  `,
2028
+ options: { mutablePropertyPrefixes: ['draft'] },
2000
2029
  },
2001
2030
  {
2002
2031
  name: 'Method without mut_ prefix should be transformed',
@@ -3977,15 +4006,20 @@ describe(convertToReadonlyTransformer, () => {
3977
4006
  type T = { draftItems: number[]; readonly b: readonly string[] };
3978
4007
  type U = { mutable_items: number[]; readonly b: readonly string[] };
3979
4008
  `,
3980
- options: { ignorePrefixes: ['draft', 'mutable_'] },
4009
+ options: {
4010
+ mutableVariablePrefixes: ['draft', 'mutable_'],
4011
+ mutablePropertyPrefixes: ['draft', 'mutable_'],
4012
+ },
3981
4013
  },
3982
4014
  {
3983
- name: 'The "_mut_" prefix is recognized by default',
4015
+ name: '"mut_" alone is the default: it covers "#mut_" and no longer "_mut_"',
3984
4016
  source: dedent`
3985
4017
  type T = { _mut_items: number[]; b: string[] };
4018
+ class C { #mut_items: number[] = []; c: string[] = []; }
3986
4019
  `,
3987
4020
  expected: dedent`
3988
- type T = { _mut_items: number[]; readonly b: readonly string[] };
4021
+ type T = Readonly<{ _mut_items: readonly number[]; b: readonly string[] }>;
4022
+ class C { #mut_items: number[] = []; readonly c: readonly string[] = []; }
3989
4023
  `,
3990
4024
  },
3991
4025
  {
@@ -4291,7 +4325,7 @@ describe(convertToReadonlyTransformer, () => {
4291
4325
  expected: dedent`
4292
4326
  type T = Readonly<{ mut_a: readonly number[]; b: readonly string[] }>;
4293
4327
  `,
4294
- options: { ignorePrefixes: [] },
4328
+ options: { mutableVariablePrefixes: [], mutablePropertyPrefixes: [] },
4295
4329
  },
4296
4330
  ])('$name', testFn);
4297
4331
 
@@ -4849,7 +4883,7 @@ describe(convertToReadonlyTransformer, () => {
4849
4883
  ])('$name', testFn);
4850
4884
  });
4851
4885
 
4852
- describe('ignorePrefixes option', () => {
4886
+ describe('mutableVariablePrefixes and mutablePropertyPrefixes options', () => {
4853
4887
  test.each([
4854
4888
  {
4855
4889
  name: 'Variable declarations',
@@ -4872,10 +4906,10 @@ describe(convertToReadonlyTransformer, () => {
4872
4906
  {
4873
4907
  name: 'Type aliases',
4874
4908
  source: dedent`
4875
- type mut_Foo = number[]
4909
+ type MutableFoo = number[]
4876
4910
  `,
4877
4911
  expected: dedent`
4878
- type mut_Foo = number[]
4912
+ type MutableFoo = number[]
4879
4913
  `,
4880
4914
  },
4881
4915
  {
@@ -4889,7 +4923,10 @@ describe(convertToReadonlyTransformer, () => {
4889
4923
  readonly normalValue: readonly string[];
4890
4924
  };
4891
4925
  `,
4892
- options: { ignorePrefixes: ['mut_'] },
4926
+ options: {
4927
+ mutableVariablePrefixes: ['mut_'],
4928
+ mutablePropertyPrefixes: ['mut_'],
4929
+ },
4893
4930
  },
4894
4931
  {
4895
4932
  name: 'Multiple ignore prefixes',
@@ -4903,7 +4940,10 @@ describe(convertToReadonlyTransformer, () => {
4903
4940
  readonly normalValue: readonly boolean[];
4904
4941
  };
4905
4942
  `,
4906
- options: { ignorePrefixes: ['mut_', 'mutable_'] },
4943
+ options: {
4944
+ mutableVariablePrefixes: ['mut_', 'mutable_'],
4945
+ mutablePropertyPrefixes: ['mut_', 'mutable_'],
4946
+ },
4907
4947
  },
4908
4948
  {
4909
4949
  name: 'Ignore prefix in nested properties',
@@ -4923,7 +4963,10 @@ describe(convertToReadonlyTransformer, () => {
4923
4963
  };
4924
4964
  }>;
4925
4965
  `,
4926
- options: { ignorePrefixes: ['mut_'] },
4966
+ options: {
4967
+ mutableVariablePrefixes: ['mut_'],
4968
+ mutablePropertyPrefixes: ['mut_'],
4969
+ },
4927
4970
  },
4928
4971
  {
4929
4972
  name: 'Ignore prefix in interface',
@@ -4939,7 +4982,10 @@ describe(convertToReadonlyTransformer, () => {
4939
4982
  readonly roles: readonly string[];
4940
4983
  }
4941
4984
  `,
4942
- options: { ignorePrefixes: ['mut_'] },
4985
+ options: {
4986
+ mutableVariablePrefixes: ['mut_'],
4987
+ mutablePropertyPrefixes: ['mut_'],
4988
+ },
4943
4989
  },
4944
4990
  {
4945
4991
  name: 'Ignore prefixes for various name node types in interface',
@@ -4961,7 +5007,10 @@ describe(convertToReadonlyTransformer, () => {
4961
5007
  readonly ['mu' + 't_d']: readonly string[]; // ComputedPropertyName
4962
5008
  }
4963
5009
  `,
4964
- options: { ignorePrefixes: ['mut_'] },
5010
+ options: {
5011
+ mutableVariablePrefixes: ['mut_'],
5012
+ mutablePropertyPrefixes: ['mut_'],
5013
+ },
4965
5014
  },
4966
5015
  {
4967
5016
  name: 'Ignore prefix with index signature',
@@ -4979,7 +5028,10 @@ describe(convertToReadonlyTransformer, () => {
4979
5028
  };
4980
5029
  };
4981
5030
  `,
4982
- options: { ignorePrefixes: ['mut_'] },
5031
+ options: {
5032
+ mutableVariablePrefixes: ['mut_'],
5033
+ mutablePropertyPrefixes: ['mut_'],
5034
+ },
4983
5035
  },
4984
5036
  {
4985
5037
  name: 'Index signature in ignored property',
@@ -4997,21 +5049,27 @@ describe(convertToReadonlyTransformer, () => {
4997
5049
  };
4998
5050
  }
4999
5051
  `,
5000
- options: { ignorePrefixes: ['mut_'] },
5052
+ options: {
5053
+ mutableVariablePrefixes: ['mut_'],
5054
+ mutablePropertyPrefixes: ['mut_'],
5055
+ },
5001
5056
  },
5002
5057
  {
5003
5058
  name: 'Type alias with ignored prefix',
5004
5059
  source: dedent`
5005
- type mut_Config = {
5060
+ type MutableConfig = {
5006
5061
  items: string[];
5007
5062
  };
5008
5063
  `,
5009
5064
  expected: dedent`
5010
- type mut_Config = {
5065
+ type MutableConfig = {
5011
5066
  items: string[];
5012
5067
  };
5013
5068
  `,
5014
- options: { ignorePrefixes: ['mut_'] },
5069
+ options: {
5070
+ mutableVariablePrefixes: ['mut_'],
5071
+ mutablePropertyPrefixes: ['mut_'],
5072
+ },
5015
5073
  },
5016
5074
  {
5017
5075
  name: 'Function with ignored prefix',
@@ -5021,21 +5079,27 @@ describe(convertToReadonlyTransformer, () => {
5021
5079
  expected: dedent`
5022
5080
  function mut_process(data: string[]): number[] { return []; }
5023
5081
  `,
5024
- options: { ignorePrefixes: ['mut_'] },
5082
+ options: {
5083
+ mutableVariablePrefixes: ['mut_'],
5084
+ mutablePropertyPrefixes: ['mut_'],
5085
+ },
5025
5086
  },
5026
5087
  {
5027
5088
  name: 'Class with ignored prefix',
5028
5089
  source: dedent`
5029
- class mut_Store {
5090
+ class MutableStore {
5030
5091
  items: string[] = [];
5031
5092
  }
5032
5093
  `,
5033
5094
  expected: dedent`
5034
- class mut_Store {
5095
+ class MutableStore {
5035
5096
  items: string[] = [];
5036
5097
  }
5037
5098
  `,
5038
- options: { ignorePrefixes: ['mut_'] },
5099
+ options: {
5100
+ mutableVariablePrefixes: ['mut_'],
5101
+ mutablePropertyPrefixes: ['mut_'],
5102
+ },
5039
5103
  },
5040
5104
  {
5041
5105
  name: 'Variable declaration with ignored prefix',
@@ -5045,37 +5109,46 @@ describe(convertToReadonlyTransformer, () => {
5045
5109
  expected: dedent`
5046
5110
  const mut_items: string[] = []
5047
5111
  `,
5048
- options: { ignorePrefixes: ['mut_'] },
5112
+ options: {
5113
+ mutableVariablePrefixes: ['mut_'],
5114
+ mutablePropertyPrefixes: ['mut_'],
5115
+ },
5049
5116
  },
5050
5117
  {
5051
5118
  name: 'Generic type with ignored prefix',
5052
5119
  source: dedent`
5053
- type mut_List<T> = {
5120
+ type MutableList<T> = {
5054
5121
  items: T[]
5055
5122
  }
5056
5123
  `,
5057
5124
  expected: dedent`
5058
- type mut_List<T> = {
5125
+ type MutableList<T> = {
5059
5126
  items: T[];
5060
5127
  };
5061
5128
  `,
5062
- options: { ignorePrefixes: ['mut_'] },
5129
+ options: {
5130
+ mutableVariablePrefixes: ['mut_'],
5131
+ mutablePropertyPrefixes: ['mut_'],
5132
+ },
5063
5133
  },
5064
5134
  {
5065
5135
  name: 'Union type with ignored prefix members',
5066
5136
  source: dedent`
5067
- type Result = mut_Success | Error;
5068
- type mut_Success = {
5137
+ type Result = MutableSuccess | Error;
5138
+ type MutableSuccess = {
5069
5139
  data: string[]
5070
5140
  };
5071
5141
  `,
5072
5142
  expected: dedent`
5073
- type Result = mut_Success | Error;
5074
- type mut_Success = {
5143
+ type Result = MutableSuccess | Error;
5144
+ type MutableSuccess = {
5075
5145
  data: string[];
5076
5146
  };
5077
5147
  `,
5078
- options: { ignorePrefixes: ['mut_'] },
5148
+ options: {
5149
+ mutableVariablePrefixes: ['mut_'],
5150
+ mutablePropertyPrefixes: ['mut_'],
5151
+ },
5079
5152
  },
5080
5153
  {
5081
5154
  name: 'Property with ignored prefix in class',
@@ -5091,7 +5164,10 @@ describe(convertToReadonlyTransformer, () => {
5091
5164
  readonly items: readonly number[] = [];
5092
5165
  }
5093
5166
  `,
5094
- options: { ignorePrefixes: ['mut_'] },
5167
+ options: {
5168
+ mutableVariablePrefixes: ['mut_'],
5169
+ mutablePropertyPrefixes: ['mut_'],
5170
+ },
5095
5171
  },
5096
5172
  {
5097
5173
  name: 'Method parameter with ignored prefix',
@@ -5105,7 +5181,10 @@ describe(convertToReadonlyTransformer, () => {
5105
5181
  process(mut_data: string[], data: readonly number[]) {}
5106
5182
  }
5107
5183
  `,
5108
- options: { ignorePrefixes: ['mut_'] },
5184
+ options: {
5185
+ mutableVariablePrefixes: ['mut_'],
5186
+ mutablePropertyPrefixes: ['mut_'],
5187
+ },
5109
5188
  },
5110
5189
  {
5111
5190
  name: 'Function expression with ignored prefix',
@@ -5115,7 +5194,10 @@ describe(convertToReadonlyTransformer, () => {
5115
5194
  expected: dedent`
5116
5195
  const fn = function mut_process(data: string[]): number[] { return []; }
5117
5196
  `,
5118
- options: { ignorePrefixes: ['mut_'] },
5197
+ options: {
5198
+ mutableVariablePrefixes: ['mut_'],
5199
+ mutablePropertyPrefixes: ['mut_'],
5200
+ },
5119
5201
  },
5120
5202
  {
5121
5203
  name: 'Array binding pattern in variable declaration (not implemented)',
@@ -5125,7 +5207,10 @@ describe(convertToReadonlyTransformer, () => {
5125
5207
  expected: dedent`
5126
5208
  const [mut_first, mut_second]: readonly [readonly string[], readonly number[]] = [[], []];
5127
5209
  `,
5128
- options: { ignorePrefixes: ['mut_'] },
5210
+ options: {
5211
+ mutableVariablePrefixes: ['mut_'],
5212
+ mutablePropertyPrefixes: ['mut_'],
5213
+ },
5129
5214
  },
5130
5215
  {
5131
5216
  // The binding pattern itself is not looked at (see the TODO in
@@ -5137,7 +5222,10 @@ describe(convertToReadonlyTransformer, () => {
5137
5222
  expected: dedent`
5138
5223
  const { mut_arr, mut_obj }: { mut_arr: string[], mut_obj: number[] } = { mut_arr: [], mut_obj: [] };
5139
5224
  `,
5140
- options: { ignorePrefixes: ['mut_'] },
5225
+ options: {
5226
+ mutableVariablePrefixes: ['mut_'],
5227
+ mutablePropertyPrefixes: ['mut_'],
5228
+ },
5141
5229
  },
5142
5230
  {
5143
5231
  name: 'Parameter with ignored prefix in class constructor',
@@ -5151,7 +5239,10 @@ describe(convertToReadonlyTransformer, () => {
5151
5239
  constructor(mut_config: string[]) {}
5152
5240
  }
5153
5241
  `,
5154
- options: { ignorePrefixes: ['mut_'] },
5242
+ options: {
5243
+ mutableVariablePrefixes: ['mut_'],
5244
+ mutablePropertyPrefixes: ['mut_'],
5245
+ },
5155
5246
  },
5156
5247
  {
5157
5248
  name: 'Parameter with ignored prefix in type literal method',
@@ -5165,7 +5256,10 @@ describe(convertToReadonlyTransformer, () => {
5165
5256
  process(mut_data: string[]): void;
5166
5257
  }>;
5167
5258
  `,
5168
- options: { ignorePrefixes: ['mut_'] },
5259
+ options: {
5260
+ mutableVariablePrefixes: ['mut_'],
5261
+ mutablePropertyPrefixes: ['mut_'],
5262
+ },
5169
5263
  },
5170
5264
  {
5171
5265
  name: 'Multiple parameters with ignored prefix',
@@ -5179,7 +5273,10 @@ describe(convertToReadonlyTransformer, () => {
5179
5273
  update(mut_id: string, mut_data: object[], data: readonly string[]): void;
5180
5274
  }
5181
5275
  `,
5182
- options: { ignorePrefixes: ['mut_'] },
5276
+ options: {
5277
+ mutableVariablePrefixes: ['mut_'],
5278
+ mutablePropertyPrefixes: ['mut_'],
5279
+ },
5183
5280
  },
5184
5281
  {
5185
5282
  name: 'Arrow function with ignored prefix parameters',
@@ -5189,18 +5286,228 @@ describe(convertToReadonlyTransformer, () => {
5189
5286
  expected: dedent`
5190
5287
  const handler = (mut_event: Event[], event: readonly CustomEvent[]) => {};
5191
5288
  `,
5192
- options: { ignorePrefixes: ['mut_'] },
5289
+ options: {
5290
+ mutableVariablePrefixes: ['mut_'],
5291
+ mutablePropertyPrefixes: ['mut_'],
5292
+ },
5193
5293
  },
5194
5294
  {
5195
- name: 'Arrow function parameters with "draft" prefix (default)',
5295
+ name: '"draft" is not a default prefix',
5196
5296
  source: dedent`
5197
5297
  const handler = (draft: Event[], event: CustomEvent[]) => {};
5298
+ type T = { draft: boolean; items: string[] };
5299
+ `,
5300
+ expected: dedent`
5301
+ const handler = (draft: readonly Event[], event: readonly CustomEvent[]) => {};
5302
+ type T = Readonly<{ draft: boolean; items: readonly string[] }>;
5303
+ `,
5304
+ },
5305
+ {
5306
+ name: '"Mutable" and "mut_" are the default type prefixes',
5307
+ source: dedent`
5308
+ type MutableFoo = { a: string[] };
5309
+ interface MutableBar { a: string[] }
5310
+ class MutableBaz { a: string[] = []; }
5311
+ const Q = class MutableQ { a: string[] = []; };
5312
+ type mut_Qux = { a: string[] };
5313
+ type Foo = { a: MutableFoo[] };
5314
+ `,
5315
+ expected: dedent`
5316
+ type MutableFoo = { a: string[] };
5317
+ interface MutableBar { a: string[] }
5318
+ class MutableBaz { a: string[] = []; }
5319
+ const Q = class MutableQ { a: string[] = []; };
5320
+ type mut_Qux = { a: string[] };
5321
+ type Foo = Readonly<{ a: readonly MutableFoo[] }>;
5322
+ `,
5323
+ },
5324
+ {
5325
+ name: 'Variable and property prefixes are separate',
5326
+ source: dedent`
5327
+ const handler = (draft: Event[]) => {};
5328
+ type T = { draft: boolean; items: string[] };
5329
+ `,
5330
+ expected: dedent`
5331
+ const handler = (draft: Event[]) => {};
5332
+ type T = Readonly<{ draft: boolean; items: readonly string[] }>;
5333
+ `,
5334
+ options: { mutableVariablePrefixes: ['draft'] },
5335
+ },
5336
+ {
5337
+ name: 'Each kind of name is matched against its own list: variables',
5338
+ source: dedent`
5339
+ type mut_T = { a: string[] };
5340
+ interface mut_I { a: string[] }
5341
+ class mut_C { a: string[] = []; }
5342
+ function mut_f(a: string[]): void {}
5343
+ const mut_x: string[] = [];
5344
+ type U = [mut_a: string[], b: string[]];
5345
+ class D { #mut_p: string[] = []; 'mut_q': string[] = []; }
5346
+ type V = { mut_r: string[]; s: string[] };
5347
+ interface J { mut_a: string[]; f(mut_p: string[]): void }
5348
+ type W = { mut_m(): string[]; b: string[] };
5349
+ class E { mut_m(x: string[]): string[] { return []; } get mut_g(): string[] { return []; } }
5350
+ `,
5351
+ expected: dedent`
5352
+ type mut_T = Readonly<{ a: readonly string[] }>;
5353
+ interface mut_I { readonly a: readonly string[] }
5354
+ class mut_C { readonly a: readonly string[] = []; }
5355
+ function mut_f(a: string[]): void {}
5356
+ const mut_x: string[] = [];
5357
+ type U = readonly [mut_a: readonly string[], b: readonly string[]];
5358
+ class D { readonly #mut_p: readonly string[] = []; readonly 'mut_q': readonly string[] = []; }
5359
+ type V = Readonly<{ mut_r: readonly string[]; s: readonly string[] }>;
5360
+ interface J { readonly mut_a: readonly string[]; f(mut_p: string[]): void }
5361
+ type W = Readonly<{ mut_m(): readonly string[]; b: readonly string[] }>;
5362
+ class E { mut_m(x: readonly string[]): readonly string[] { return []; } get mut_g(): readonly string[] { return []; } }
5363
+ `,
5364
+ options: {
5365
+ mutableVariablePrefixes: ['mut_'],
5366
+ mutableTypePrefixes: [],
5367
+ mutablePropertyPrefixes: [],
5368
+ },
5369
+ },
5370
+ {
5371
+ name: 'Each kind of name is matched against its own list: types',
5372
+ source: dedent`
5373
+ type mut_T = { a: string[] };
5374
+ interface mut_I { a: string[] }
5375
+ class mut_C { a: string[] = []; }
5376
+ function mut_f(a: string[]): void {}
5377
+ const mut_x: string[] = [];
5378
+ type U = [mut_a: string[], b: string[]];
5379
+ class D { #mut_p: string[] = []; 'mut_q': string[] = []; }
5380
+ type V = { mut_r: string[]; s: string[] };
5381
+ interface J { mut_a: string[]; f(mut_p: string[]): void }
5382
+ type W = { mut_m(): string[]; b: string[] };
5383
+ class E { mut_m(x: string[]): string[] { return []; } get mut_g(): string[] { return []; } }
5384
+ `,
5385
+ expected: dedent`
5386
+ type mut_T = { a: string[] };
5387
+ interface mut_I { a: string[] }
5388
+ class mut_C { a: string[] = []; }
5389
+ function mut_f(a: readonly string[]): void {}
5390
+ const mut_x: readonly string[] = [];
5391
+ type U = readonly [mut_a: readonly string[], b: readonly string[]];
5392
+ class D { readonly #mut_p: readonly string[] = []; readonly 'mut_q': readonly string[] = []; }
5393
+ type V = Readonly<{ mut_r: readonly string[]; s: readonly string[] }>;
5394
+ interface J { readonly mut_a: readonly string[]; f(mut_p: readonly string[]): void }
5395
+ type W = Readonly<{ mut_m(): readonly string[]; b: readonly string[] }>;
5396
+ class E { mut_m(x: readonly string[]): readonly string[] { return []; } get mut_g(): readonly string[] { return []; } }
5397
+ `,
5398
+ options: {
5399
+ mutableVariablePrefixes: [],
5400
+ mutableTypePrefixes: ['mut_'],
5401
+ mutablePropertyPrefixes: [],
5402
+ },
5403
+ },
5404
+ {
5405
+ name: 'Each kind of name is matched against its own list: properties',
5406
+ source: dedent`
5407
+ type mut_T = { a: string[] };
5408
+ interface mut_I { a: string[] }
5409
+ class mut_C { a: string[] = []; }
5410
+ function mut_f(a: string[]): void {}
5411
+ const mut_x: string[] = [];
5412
+ type U = [mut_a: string[], b: string[]];
5413
+ class D { #mut_p: string[] = []; 'mut_q': string[] = []; }
5414
+ type V = { mut_r: string[]; s: string[] };
5415
+ interface J { mut_a: string[]; f(mut_p: string[]): void }
5416
+ type W = { mut_m(): string[]; b: string[] };
5417
+ class E { mut_m(x: string[]): string[] { return []; } get mut_g(): string[] { return []; } }
5418
+ `,
5419
+ expected: dedent`
5420
+ type mut_T = Readonly<{ a: readonly string[] }>;
5421
+ interface mut_I { readonly a: readonly string[] }
5422
+ class mut_C { readonly a: readonly string[] = []; }
5423
+ function mut_f(a: readonly string[]): void {}
5424
+ const mut_x: readonly string[] = [];
5425
+ type U = readonly [mut_a: string[], b: readonly string[]];
5426
+ class D { #mut_p: string[] = []; 'mut_q': string[] = []; }
5427
+ type V = { mut_r: string[]; readonly s: readonly string[] };
5428
+ interface J { mut_a: string[]; f(mut_p: readonly string[]): void }
5429
+ type W = { mut_m(): string[]; readonly b: readonly string[] };
5430
+ class E { mut_m(x: string[]): string[] { return []; } get mut_g(): string[] { return []; } }
5431
+ `,
5432
+ options: {
5433
+ mutableVariablePrefixes: [],
5434
+ mutableTypePrefixes: [],
5435
+ mutablePropertyPrefixes: ['mut_'],
5436
+ },
5437
+ },
5438
+ {
5439
+ name: 'A property prefix with a "#" covers private names only',
5440
+ source: dedent`
5441
+ class C { #mut_a: string[] = []; mut_b: string[] = []; }
5442
+ `,
5443
+ expected: dedent`
5444
+ class C { #mut_a: string[] = []; readonly mut_b: readonly string[] = []; }
5445
+ `,
5446
+ options: { mutablePropertyPrefixes: ['#mut_'] },
5447
+ },
5448
+ {
5449
+ name: 'A property prefix does not apply to variables',
5450
+ source: dedent`
5451
+ const handler = (draft: Event[]) => {};
5452
+ type T = { draft: boolean; items: string[] };
5453
+ `,
5454
+ expected: dedent`
5455
+ const handler = (draft: readonly Event[]) => {};
5456
+ type T = { draft: boolean; readonly items: readonly string[] };
5457
+ `,
5458
+ options: { mutablePropertyPrefixes: ['draft'] },
5459
+ },
5460
+ {
5461
+ name: 'A constructor parameter property is a property',
5462
+ source: dedent`
5463
+ class C {
5464
+ draftA: string[] = [];
5465
+ constructor(private draftB: string[], readonly draftD: string[], draftC: string[]) {}
5466
+ }
5467
+ `,
5468
+ expected: dedent`
5469
+ class C {
5470
+ draftA: string[] = [];
5471
+ constructor(private draftB: string[], readonly draftD: string[], draftC: readonly string[]) {}
5472
+ }
5473
+ `,
5474
+ options: { mutablePropertyPrefixes: ['draft'] },
5475
+ },
5476
+ {
5477
+ name: 'A constructor parameter that is not a property is a variable',
5478
+ source: dedent`
5479
+ class C {
5480
+ draftA: string[] = [];
5481
+ constructor(private draftB: string[], draftC: string[]) {}
5482
+ }
5198
5483
  `,
5199
5484
  expected: dedent`
5200
- const handler = (draft: Event[], event: readonly CustomEvent[]) => {};
5485
+ class C {
5486
+ readonly draftA: readonly string[] = [];
5487
+ constructor(private readonly draftB: readonly string[], draftC: string[]) {}
5488
+ }
5201
5489
  `,
5490
+ options: { mutableVariablePrefixes: ['draft'] },
5202
5491
  },
5203
5492
  ])('$name', testFn);
5493
+
5494
+ test('the removed ignorePrefixes option is refused', () => {
5495
+ expect(() =>
5496
+ convertToReadonlyTransformer(
5497
+ // @ts-expect-error removed in 4.0.0
5498
+ { ignorePrefixes: ['mut_'] },
5499
+ ),
5500
+ ).toThrow('mutableVariablePrefixes');
5501
+ });
5502
+
5503
+ test('an unset ignorePrefixes option is accepted', () => {
5504
+ expect(() =>
5505
+ convertToReadonlyTransformer(
5506
+ // @ts-expect-error removed in 4.0.0
5507
+ { ignorePrefixes: undefined },
5508
+ ),
5509
+ ).not.toThrow();
5510
+ });
5204
5511
  });
5205
5512
 
5206
5513
  describe('Disable comment handling', () => {