ts-codemod-lib 3.5.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -9
- package/dist/functions/ast-transformers/append-as-const.d.mts +13 -4
- package/dist/functions/ast-transformers/append-as-const.d.mts.map +1 -1
- package/dist/functions/ast-transformers/append-as-const.mjs +81 -5
- package/dist/functions/ast-transformers/append-as-const.mjs.map +1 -1
- package/dist/functions/ast-transformers/convert-to-readonly.d.mts +33 -4
- package/dist/functions/ast-transformers/convert-to-readonly.d.mts.map +1 -1
- package/dist/functions/ast-transformers/convert-to-readonly.mjs +155 -30
- package/dist/functions/ast-transformers/convert-to-readonly.mjs.map +1 -1
- package/dist/functions/ast-transformers/readonly-transformer-helpers/group-union-types.d.mts +7 -1
- package/dist/functions/ast-transformers/readonly-transformer-helpers/group-union-types.d.mts.map +1 -1
- package/dist/functions/ast-transformers/readonly-transformer-helpers/group-union-types.mjs +25 -2
- package/dist/functions/ast-transformers/readonly-transformer-helpers/group-union-types.mjs.map +1 -1
- package/dist/functions/constants/index.d.mts +1 -0
- package/dist/functions/constants/index.d.mts.map +1 -1
- package/dist/functions/constants/index.mjs +1 -0
- package/dist/functions/constants/index.mjs.map +1 -1
- package/dist/functions/constants/mutable-prefixes.d.mts +23 -0
- package/dist/functions/constants/mutable-prefixes.d.mts.map +1 -0
- package/dist/functions/constants/mutable-prefixes.mjs +28 -0
- package/dist/functions/constants/mutable-prefixes.mjs.map +1 -0
- package/package.json +19 -19
- package/src/functions/ast-transformers/append-as-const.mts +152 -15
- package/src/functions/ast-transformers/append-as-const.test.mts +79 -1
- package/src/functions/ast-transformers/convert-to-readonly.mts +302 -57
- package/src/functions/ast-transformers/convert-to-readonly.test.mts +738 -49
- package/src/functions/ast-transformers/readonly-transformer-helpers/group-union-types.mts +36 -5
- package/src/functions/constants/index.mts +1 -0
- package/src/functions/constants/mutable-prefixes.mts +28 -0
|
@@ -1980,6 +1980,7 @@ describe(convertToReadonlyTransformer, () => {
|
|
|
1980
1980
|
}
|
|
1981
1981
|
}
|
|
1982
1982
|
`,
|
|
1983
|
+
options: { mutablePropertyPrefixes: ['_mut_'] },
|
|
1983
1984
|
},
|
|
1984
1985
|
{
|
|
1985
1986
|
name: 'Method with "draft" prefix',
|
|
@@ -1997,6 +1998,7 @@ describe(convertToReadonlyTransformer, () => {
|
|
|
1997
1998
|
}
|
|
1998
1999
|
}
|
|
1999
2000
|
`,
|
|
2001
|
+
options: { mutablePropertyPrefixes: ['draft'] },
|
|
2000
2002
|
},
|
|
2001
2003
|
{
|
|
2002
2004
|
name: 'Method without mut_ prefix should be transformed',
|
|
@@ -3926,6 +3928,418 @@ describe(convertToReadonlyTransformer, () => {
|
|
|
3926
3928
|
])('$name', testFn);
|
|
3927
3929
|
});
|
|
3928
3930
|
|
|
3931
|
+
describe('Type literals with a property of an ignored prefix', () => {
|
|
3932
|
+
// `Readonly<T>` would make the ignored property readonly too, so such a
|
|
3933
|
+
// literal is left unwrapped and its other members are marked readonly in
|
|
3934
|
+
// place, as an interface's are.
|
|
3935
|
+
test.each([
|
|
3936
|
+
{
|
|
3937
|
+
name: 'Members other than the ignored one are marked readonly in place',
|
|
3938
|
+
source: dedent`
|
|
3939
|
+
type T = { mut_a: number[]; b: string[]; readonly c: number };
|
|
3940
|
+
`,
|
|
3941
|
+
expected: dedent`
|
|
3942
|
+
type T = { mut_a: number[]; readonly b: readonly string[]; readonly c: number };
|
|
3943
|
+
`,
|
|
3944
|
+
},
|
|
3945
|
+
{
|
|
3946
|
+
name: 'An ignored property already marked readonly is left as written',
|
|
3947
|
+
source: dedent`
|
|
3948
|
+
type T = { readonly mut_a: number[]; b: string[] };
|
|
3949
|
+
`,
|
|
3950
|
+
expected: dedent`
|
|
3951
|
+
type T = { readonly mut_a: number[]; readonly b: readonly string[] };
|
|
3952
|
+
`,
|
|
3953
|
+
},
|
|
3954
|
+
{
|
|
3955
|
+
name: 'A string literal name or a computed name of a literal is recognized',
|
|
3956
|
+
source: dedent`
|
|
3957
|
+
type T = { "mut_a": number[]; ['mut_b']: number[]; [\`mut_c\`]: number[]; d: string[] };
|
|
3958
|
+
`,
|
|
3959
|
+
expected: dedent`
|
|
3960
|
+
type T = { "mut_a": number[]; ['mut_b']: number[]; [\`mut_c\`]: number[]; readonly d: readonly string[] };
|
|
3961
|
+
`,
|
|
3962
|
+
},
|
|
3963
|
+
{
|
|
3964
|
+
name: 'An index signature alongside is marked readonly in place',
|
|
3965
|
+
source: dedent`
|
|
3966
|
+
type T = { mut_a: number[]; [key: string]: unknown };
|
|
3967
|
+
`,
|
|
3968
|
+
expected: dedent`
|
|
3969
|
+
type T = { mut_a: number[]; readonly [key: string]: unknown };
|
|
3970
|
+
`,
|
|
3971
|
+
},
|
|
3972
|
+
{
|
|
3973
|
+
name: 'Every configured prefix is recognized',
|
|
3974
|
+
source: dedent`
|
|
3975
|
+
type T = { draftItems: number[]; b: string[] };
|
|
3976
|
+
type U = { mutable_items: number[]; b: string[] };
|
|
3977
|
+
`,
|
|
3978
|
+
expected: dedent`
|
|
3979
|
+
type T = { draftItems: number[]; readonly b: readonly string[] };
|
|
3980
|
+
type U = { mutable_items: number[]; readonly b: readonly string[] };
|
|
3981
|
+
`,
|
|
3982
|
+
options: {
|
|
3983
|
+
mutableVariablePrefixes: ['draft', 'mutable_'],
|
|
3984
|
+
mutablePropertyPrefixes: ['draft', 'mutable_'],
|
|
3985
|
+
},
|
|
3986
|
+
},
|
|
3987
|
+
{
|
|
3988
|
+
name: '"mut_" alone is the default: it covers "#mut_" and no longer "_mut_"',
|
|
3989
|
+
source: dedent`
|
|
3990
|
+
type T = { _mut_items: number[]; b: string[] };
|
|
3991
|
+
class C { #mut_items: number[] = []; c: string[] = []; }
|
|
3992
|
+
`,
|
|
3993
|
+
expected: dedent`
|
|
3994
|
+
type T = Readonly<{ _mut_items: readonly number[]; b: readonly string[] }>;
|
|
3995
|
+
class C { #mut_items: number[] = []; readonly c: readonly string[] = []; }
|
|
3996
|
+
`,
|
|
3997
|
+
},
|
|
3998
|
+
{
|
|
3999
|
+
name: 'Ancestors are wrapped with the shallow Readonly only',
|
|
4000
|
+
source: dedent`
|
|
4001
|
+
type T = {
|
|
4002
|
+
a: {
|
|
4003
|
+
b: {
|
|
4004
|
+
mut_c: number[];
|
|
4005
|
+
d: string[];
|
|
4006
|
+
};
|
|
4007
|
+
};
|
|
4008
|
+
};
|
|
4009
|
+
`,
|
|
4010
|
+
expected: dedent`
|
|
4011
|
+
type T = Readonly<{
|
|
4012
|
+
a: Readonly<{
|
|
4013
|
+
b: {
|
|
4014
|
+
mut_c: number[];
|
|
4015
|
+
readonly d: readonly string[];
|
|
4016
|
+
};
|
|
4017
|
+
}>;
|
|
4018
|
+
}>;
|
|
4019
|
+
`,
|
|
4020
|
+
},
|
|
4021
|
+
{
|
|
4022
|
+
name: 'Nested in an interface',
|
|
4023
|
+
source: dedent`
|
|
4024
|
+
interface I {
|
|
4025
|
+
a: { mut_b: number[]; c: string[] };
|
|
4026
|
+
}
|
|
4027
|
+
`,
|
|
4028
|
+
expected: dedent`
|
|
4029
|
+
interface I {
|
|
4030
|
+
readonly a: { mut_b: number[]; readonly c: readonly string[] };
|
|
4031
|
+
}
|
|
4032
|
+
`,
|
|
4033
|
+
},
|
|
4034
|
+
{
|
|
4035
|
+
name: 'As an array element',
|
|
4036
|
+
source: dedent`
|
|
4037
|
+
type T = { mut_a: number[] }[];
|
|
4038
|
+
`,
|
|
4039
|
+
expected: dedent`
|
|
4040
|
+
type T = readonly { mut_a: number[] }[];
|
|
4041
|
+
`,
|
|
4042
|
+
},
|
|
4043
|
+
{
|
|
4044
|
+
name: 'In a union, it is not merged into the Readonly of the others',
|
|
4045
|
+
source: dedent`
|
|
4046
|
+
type T = { mut_a: number[]; b: string } | { c: number[] } | { d: string };
|
|
4047
|
+
`,
|
|
4048
|
+
expected: dedent`
|
|
4049
|
+
type T = { mut_a: number[]; readonly b: string } | Readonly<{ c: readonly number[] } | { d: string }>;
|
|
4050
|
+
`,
|
|
4051
|
+
},
|
|
4052
|
+
{
|
|
4053
|
+
name: 'In an intersection, it is not merged into the Readonly of the others',
|
|
4054
|
+
source: dedent`
|
|
4055
|
+
type T = { mut_a: number[]; b: string } & { c: number[] };
|
|
4056
|
+
`,
|
|
4057
|
+
expected: dedent`
|
|
4058
|
+
type T = { mut_a: number[]; readonly b: string } & Readonly<{ c: readonly number[] }>;
|
|
4059
|
+
`,
|
|
4060
|
+
},
|
|
4061
|
+
{
|
|
4062
|
+
name: 'In an intersection under Readonly, it is taken out',
|
|
4063
|
+
source: dedent`
|
|
4064
|
+
type T = Readonly<{ mut_a: number[]; b: string } & { c: string }>;
|
|
4065
|
+
`,
|
|
4066
|
+
expected: dedent`
|
|
4067
|
+
type T = { mut_a: number[]; readonly b: string } & Readonly<{ c: string }>;
|
|
4068
|
+
`,
|
|
4069
|
+
},
|
|
4070
|
+
{
|
|
4071
|
+
// Such a `Readonly` is also what earlier versions wrote.
|
|
4072
|
+
name: 'A Readonly written around it is taken off',
|
|
4073
|
+
source: dedent`
|
|
4074
|
+
type T = Readonly<{ mut_a: number[]; b: string[] }>;
|
|
4075
|
+
`,
|
|
4076
|
+
expected: dedent`
|
|
4077
|
+
type T = { mut_a: number[]; readonly b: readonly string[] };
|
|
4078
|
+
`,
|
|
4079
|
+
},
|
|
4080
|
+
{
|
|
4081
|
+
name: 'Nested Readonly written around it are taken off',
|
|
4082
|
+
source: dedent`
|
|
4083
|
+
type T = Readonly<Readonly<{ mut_a: number[]; b: string }>>;
|
|
4084
|
+
`,
|
|
4085
|
+
expected: dedent`
|
|
4086
|
+
type T = { mut_a: number[]; readonly b: string };
|
|
4087
|
+
`,
|
|
4088
|
+
},
|
|
4089
|
+
{
|
|
4090
|
+
name: 'A Readonly written around it in a union is taken off',
|
|
4091
|
+
source: dedent`
|
|
4092
|
+
type T = Readonly<{ mut_a: number[]; b: string }> | { c: string };
|
|
4093
|
+
`,
|
|
4094
|
+
expected: dedent`
|
|
4095
|
+
type T = { mut_a: number[]; readonly b: string } | Readonly<{ c: string }>;
|
|
4096
|
+
`,
|
|
4097
|
+
},
|
|
4098
|
+
{
|
|
4099
|
+
name: 'A Readonly written around it under a Readonly union is taken off',
|
|
4100
|
+
source: dedent`
|
|
4101
|
+
type T = Readonly<Readonly<{ mut_a: number[] }> | { c: string }>;
|
|
4102
|
+
`,
|
|
4103
|
+
expected: dedent`
|
|
4104
|
+
type T = { mut_a: number[] } | Readonly<{ c: string }>;
|
|
4105
|
+
`,
|
|
4106
|
+
},
|
|
4107
|
+
{
|
|
4108
|
+
// That member gets no `readonly` of its own, so the wrapper stays.
|
|
4109
|
+
name: 'A member skipped by the ignore comment keeps the Readonly',
|
|
4110
|
+
source: dedent`
|
|
4111
|
+
type T = Readonly<{
|
|
4112
|
+
// transformer-ignore-next-line
|
|
4113
|
+
b: string[];
|
|
4114
|
+
mut_a: number[];
|
|
4115
|
+
}>;
|
|
4116
|
+
`,
|
|
4117
|
+
expected: dedent`
|
|
4118
|
+
type T = Readonly<{
|
|
4119
|
+
// transformer-ignore-next-line
|
|
4120
|
+
b: string[];
|
|
4121
|
+
mut_a: number[];
|
|
4122
|
+
}>;
|
|
4123
|
+
`,
|
|
4124
|
+
},
|
|
4125
|
+
{
|
|
4126
|
+
name: 'A literal skipped by the ignore comment keeps the Readonly',
|
|
4127
|
+
source: dedent`
|
|
4128
|
+
type T = Readonly<
|
|
4129
|
+
// transformer-ignore-next-line
|
|
4130
|
+
{ mut_a: number[]; b: string[] }
|
|
4131
|
+
>;
|
|
4132
|
+
`,
|
|
4133
|
+
expected: dedent`
|
|
4134
|
+
type T = Readonly<
|
|
4135
|
+
// transformer-ignore-next-line
|
|
4136
|
+
{ mut_a: number[]; b: string[] }
|
|
4137
|
+
>;
|
|
4138
|
+
`,
|
|
4139
|
+
},
|
|
4140
|
+
{
|
|
4141
|
+
name: 'A literal skipped by the ignore comment in an intersection under Readonly stays under it',
|
|
4142
|
+
source: dedent`
|
|
4143
|
+
type T = Readonly<
|
|
4144
|
+
{ c: string } &
|
|
4145
|
+
// transformer-ignore-next-line
|
|
4146
|
+
{ mut_a: number[]; b: string[] }
|
|
4147
|
+
>;
|
|
4148
|
+
`,
|
|
4149
|
+
expected: dedent`
|
|
4150
|
+
type T = Readonly<
|
|
4151
|
+
{ c: string } &
|
|
4152
|
+
// transformer-ignore-next-line
|
|
4153
|
+
{ mut_a: number[]; b: string[] }
|
|
4154
|
+
>;
|
|
4155
|
+
`,
|
|
4156
|
+
},
|
|
4157
|
+
{
|
|
4158
|
+
name: 'An ignored property skipped by the ignore comment does not keep the Readonly',
|
|
4159
|
+
source: dedent`
|
|
4160
|
+
type T = {
|
|
4161
|
+
// transformer-ignore-next-line
|
|
4162
|
+
mut_a: number[];
|
|
4163
|
+
b: string[];
|
|
4164
|
+
};
|
|
4165
|
+
`,
|
|
4166
|
+
expected: dedent`
|
|
4167
|
+
type T = {
|
|
4168
|
+
// transformer-ignore-next-line
|
|
4169
|
+
mut_a: number[];
|
|
4170
|
+
readonly b: readonly string[];
|
|
4171
|
+
};
|
|
4172
|
+
`,
|
|
4173
|
+
},
|
|
4174
|
+
{
|
|
4175
|
+
// `Readonly` is what makes a method or an accessor readonly, and
|
|
4176
|
+
// neither takes a `readonly` modifier.
|
|
4177
|
+
name: 'A literal with a method signature or an accessor is wrapped as before',
|
|
4178
|
+
source: dedent`
|
|
4179
|
+
type T = { mut_a: number[]; f(): void; b: string[] };
|
|
4180
|
+
type U = Readonly<{ mut_a: number[]; get g(): string; set g(v: string) }>;
|
|
4181
|
+
`,
|
|
4182
|
+
expected: dedent`
|
|
4183
|
+
type T = Readonly<{ mut_a: number[]; f(): void; b: readonly string[] }>;
|
|
4184
|
+
type U = Readonly<{ mut_a: number[]; get g(): string; set g(v: string) }>;
|
|
4185
|
+
`,
|
|
4186
|
+
},
|
|
4187
|
+
{
|
|
4188
|
+
// `Readonly<A> & { ... }` would make an array `A` readonly.
|
|
4189
|
+
name: 'In an intersection under Readonly beside a type that may be an array, it stays under it',
|
|
4190
|
+
source: dedent`
|
|
4191
|
+
type T<A> = Readonly<A & { mut_a: number[]; b: string }>;
|
|
4192
|
+
`,
|
|
4193
|
+
expected: dedent`
|
|
4194
|
+
type T<A> = Readonly<A & { mut_a: number[]; readonly b: string }>;
|
|
4195
|
+
`,
|
|
4196
|
+
},
|
|
4197
|
+
{
|
|
4198
|
+
name: 'In an intersection under Readonly beside a primitive, it is taken out',
|
|
4199
|
+
source: dedent`
|
|
4200
|
+
type T = Readonly<string & { mut_a: number[]; b: string }>;
|
|
4201
|
+
`,
|
|
4202
|
+
expected: dedent`
|
|
4203
|
+
type T = string & { mut_a: number[]; readonly b: string };
|
|
4204
|
+
`,
|
|
4205
|
+
},
|
|
4206
|
+
{
|
|
4207
|
+
name: 'An accessor or a method of an ignored prefix is left as written',
|
|
4208
|
+
source: dedent`
|
|
4209
|
+
type T = { get mut_x(): number[]; set mut_x(v: number[]); mut_f(a: string[]): void; b: string[] };
|
|
4210
|
+
`,
|
|
4211
|
+
expected: dedent`
|
|
4212
|
+
type T = { get mut_x(): number[]; set mut_x(v: number[]); mut_f(a: string[]): void; readonly b: readonly string[] };
|
|
4213
|
+
`,
|
|
4214
|
+
},
|
|
4215
|
+
{
|
|
4216
|
+
name: 'A comment after the literal is kept when the Readonly is taken off',
|
|
4217
|
+
source: dedent`
|
|
4218
|
+
type T = Readonly<{ mut_a: number[]; b: string } /* note */>;
|
|
4219
|
+
type U = Readonly<{ mut_a: number[] } // note
|
|
4220
|
+
>[];
|
|
4221
|
+
`,
|
|
4222
|
+
expected: dedent`
|
|
4223
|
+
type T = /* note */ { mut_a: number[]; readonly b: string };
|
|
4224
|
+
type U =
|
|
4225
|
+
// note
|
|
4226
|
+
readonly { mut_a: number[] }[];
|
|
4227
|
+
`,
|
|
4228
|
+
},
|
|
4229
|
+
{
|
|
4230
|
+
name: 'A Readonly written around a single-member union of a literal with a call signature is kept',
|
|
4231
|
+
source: dedent`
|
|
4232
|
+
type T = Readonly<| { (): void; mut_a: number[] }>;
|
|
4233
|
+
`,
|
|
4234
|
+
expected: dedent`
|
|
4235
|
+
type T = Readonly<{ (): void; mut_a: number[] }>;
|
|
4236
|
+
`,
|
|
4237
|
+
},
|
|
4238
|
+
{
|
|
4239
|
+
name: 'A Readonly written around a literal with a call signature is kept',
|
|
4240
|
+
source: dedent`
|
|
4241
|
+
type T = Readonly<{ (): void; mut_a: number[]; b: string }>;
|
|
4242
|
+
`,
|
|
4243
|
+
expected: dedent`
|
|
4244
|
+
type T = Readonly<{ (): void; mut_a: number[]; b: string }>;
|
|
4245
|
+
`,
|
|
4246
|
+
},
|
|
4247
|
+
{
|
|
4248
|
+
name: 'A member that needs no wrapper does not keep the Readonly',
|
|
4249
|
+
source: dedent`
|
|
4250
|
+
type T = {
|
|
4251
|
+
mut_a: number[];
|
|
4252
|
+
mut_f(): void;
|
|
4253
|
+
// transformer-ignore-next-line
|
|
4254
|
+
readonly b: string;
|
|
4255
|
+
};
|
|
4256
|
+
`,
|
|
4257
|
+
expected: dedent`
|
|
4258
|
+
type T = {
|
|
4259
|
+
mut_a: number[];
|
|
4260
|
+
mut_f(): void;
|
|
4261
|
+
// transformer-ignore-next-line
|
|
4262
|
+
readonly b: string;
|
|
4263
|
+
};
|
|
4264
|
+
`,
|
|
4265
|
+
},
|
|
4266
|
+
{
|
|
4267
|
+
name: 'A Readonly written around it under an indexed access is taken off',
|
|
4268
|
+
source: dedent`
|
|
4269
|
+
type T = Readonly<{ mut_a: number[]; b: string[] }>['b'];
|
|
4270
|
+
`,
|
|
4271
|
+
expected: dedent`
|
|
4272
|
+
type T = { mut_a: number[]; b: readonly string[] }['b'];
|
|
4273
|
+
`,
|
|
4274
|
+
},
|
|
4275
|
+
{
|
|
4276
|
+
name: 'A DeepReadonly written explicitly is kept',
|
|
4277
|
+
source: dedent`
|
|
4278
|
+
type T = DeepReadonly<{ a: { mut_b: number[]; c: string[] } }>;
|
|
4279
|
+
`,
|
|
4280
|
+
expected: dedent`
|
|
4281
|
+
type T = DeepReadonly<{ a: { mut_b: number[]; c: string[] } }>;
|
|
4282
|
+
`,
|
|
4283
|
+
},
|
|
4284
|
+
{
|
|
4285
|
+
name: 'Under an indexed access',
|
|
4286
|
+
source: dedent`
|
|
4287
|
+
type T = { mut_a: number[]; b: string[] }['b'];
|
|
4288
|
+
`,
|
|
4289
|
+
expected: dedent`
|
|
4290
|
+
type T = { mut_a: number[]; b: readonly string[] }['b'];
|
|
4291
|
+
`,
|
|
4292
|
+
},
|
|
4293
|
+
{
|
|
4294
|
+
name: 'Without the prefix configured, the literal is wrapped',
|
|
4295
|
+
source: dedent`
|
|
4296
|
+
type T = { mut_a: number[]; b: string[] };
|
|
4297
|
+
`,
|
|
4298
|
+
expected: dedent`
|
|
4299
|
+
type T = Readonly<{ mut_a: readonly number[]; b: readonly string[] }>;
|
|
4300
|
+
`,
|
|
4301
|
+
options: { mutableVariablePrefixes: [], mutablePropertyPrefixes: [] },
|
|
4302
|
+
},
|
|
4303
|
+
])('$name', testFn);
|
|
4304
|
+
|
|
4305
|
+
test.each([
|
|
4306
|
+
dedent`
|
|
4307
|
+
type T = {
|
|
4308
|
+
a: { mut_b: number[]; c: string[] } | { d: number[] };
|
|
4309
|
+
mut_e: { f: string[] };
|
|
4310
|
+
};
|
|
4311
|
+
`,
|
|
4312
|
+
'type T = Readonly<{ mut_a: number[]; b: string }> | { c: string };',
|
|
4313
|
+
'type T = Readonly<{ mut_a: number[]; b: string }> & { c: string };',
|
|
4314
|
+
'type T = Readonly<{ mut_a: number[] }> | Readonly<{ c: string }>;',
|
|
4315
|
+
'type T = Readonly<Readonly<{ mut_a: number[] }> | { c: string }>;',
|
|
4316
|
+
'type T = Readonly<{ mut_a: number[]; b: string } & { c: string }>;',
|
|
4317
|
+
'type T = { a: string } | { mut_b: number[] } | { c: string };',
|
|
4318
|
+
'type T<A> = Readonly<A & { mut_a: number[]; b: string }>;',
|
|
4319
|
+
'type T = Readonly<string & { mut_a: number[]; b: string }>;',
|
|
4320
|
+
'type T = Readonly<string[] & { mut_a: number[]; b: string }>;',
|
|
4321
|
+
'type T = Readonly<(() => void) & { mut_a: number[]; b: string }>;',
|
|
4322
|
+
'type T = Readonly<{ mut_a: number[] } & { c: string } & string>;',
|
|
4323
|
+
'type T = Readonly<{ mut_a: number[]; b: string } /* note */>;',
|
|
4324
|
+
'type T = Readonly<{ mut_a: number[] } // note\n>[];',
|
|
4325
|
+
'type T = Readonly<{ mut_a: number[] } /* c */> | { d: string };',
|
|
4326
|
+
'type T = Readonly<\n // transformer-ignore-next-line\n { mut_a: number[]; b: string[] }\n>;',
|
|
4327
|
+
])(
|
|
4328
|
+
'a type literal with an ignored property reaches a fixed point: %s',
|
|
4329
|
+
(code) => {
|
|
4330
|
+
const once = transformSourceCode(code, false, [
|
|
4331
|
+
convertToReadonlyTransformer(),
|
|
4332
|
+
]);
|
|
4333
|
+
|
|
4334
|
+
const twice = transformSourceCode(once, false, [
|
|
4335
|
+
convertToReadonlyTransformer(),
|
|
4336
|
+
]);
|
|
4337
|
+
|
|
4338
|
+
expect(twice).toBe(once);
|
|
4339
|
+
},
|
|
4340
|
+
);
|
|
4341
|
+
});
|
|
4342
|
+
|
|
3929
4343
|
describe('Call and construct signatures', () => {
|
|
3930
4344
|
// `Readonly<T>` is a mapped type over `keyof T`, and a call or construct
|
|
3931
4345
|
// signature is not a property, so wrapping a type literal that carries one
|
|
@@ -4442,7 +4856,7 @@ describe(convertToReadonlyTransformer, () => {
|
|
|
4442
4856
|
])('$name', testFn);
|
|
4443
4857
|
});
|
|
4444
4858
|
|
|
4445
|
-
describe('
|
|
4859
|
+
describe('mutableVariablePrefixes and mutablePropertyPrefixes options', () => {
|
|
4446
4860
|
test.each([
|
|
4447
4861
|
{
|
|
4448
4862
|
name: 'Variable declarations',
|
|
@@ -4465,10 +4879,10 @@ describe(convertToReadonlyTransformer, () => {
|
|
|
4465
4879
|
{
|
|
4466
4880
|
name: 'Type aliases',
|
|
4467
4881
|
source: dedent`
|
|
4468
|
-
type
|
|
4882
|
+
type MutableFoo = number[]
|
|
4469
4883
|
`,
|
|
4470
4884
|
expected: dedent`
|
|
4471
|
-
type
|
|
4885
|
+
type MutableFoo = number[]
|
|
4472
4886
|
`,
|
|
4473
4887
|
},
|
|
4474
4888
|
{
|
|
@@ -4477,12 +4891,15 @@ describe(convertToReadonlyTransformer, () => {
|
|
|
4477
4891
|
type T = { mut_value: number[]; normalValue: string[] }
|
|
4478
4892
|
`,
|
|
4479
4893
|
expected: dedent`
|
|
4480
|
-
type T =
|
|
4894
|
+
type T = {
|
|
4481
4895
|
mut_value: number[];
|
|
4482
|
-
normalValue: readonly string[];
|
|
4483
|
-
}
|
|
4896
|
+
readonly normalValue: readonly string[];
|
|
4897
|
+
};
|
|
4484
4898
|
`,
|
|
4485
|
-
options: {
|
|
4899
|
+
options: {
|
|
4900
|
+
mutableVariablePrefixes: ['mut_'],
|
|
4901
|
+
mutablePropertyPrefixes: ['mut_'],
|
|
4902
|
+
},
|
|
4486
4903
|
},
|
|
4487
4904
|
{
|
|
4488
4905
|
name: 'Multiple ignore prefixes',
|
|
@@ -4490,13 +4907,16 @@ describe(convertToReadonlyTransformer, () => {
|
|
|
4490
4907
|
type T = { mut_value: number[]; mutable_data: string[]; normalValue: boolean[] };
|
|
4491
4908
|
`,
|
|
4492
4909
|
expected: dedent`
|
|
4493
|
-
type T =
|
|
4910
|
+
type T = {
|
|
4494
4911
|
mut_value: number[];
|
|
4495
4912
|
mutable_data: string[];
|
|
4496
|
-
normalValue: readonly boolean[];
|
|
4497
|
-
}
|
|
4913
|
+
readonly normalValue: readonly boolean[];
|
|
4914
|
+
};
|
|
4498
4915
|
`,
|
|
4499
|
-
options: {
|
|
4916
|
+
options: {
|
|
4917
|
+
mutableVariablePrefixes: ['mut_', 'mutable_'],
|
|
4918
|
+
mutablePropertyPrefixes: ['mut_', 'mutable_'],
|
|
4919
|
+
},
|
|
4500
4920
|
},
|
|
4501
4921
|
{
|
|
4502
4922
|
name: 'Ignore prefix in nested properties',
|
|
@@ -4510,13 +4930,16 @@ describe(convertToReadonlyTransformer, () => {
|
|
|
4510
4930
|
`,
|
|
4511
4931
|
expected: dedent`
|
|
4512
4932
|
type T = Readonly<{
|
|
4513
|
-
data:
|
|
4933
|
+
data: {
|
|
4514
4934
|
mut_items: number[];
|
|
4515
|
-
items: readonly string[];
|
|
4516
|
-
}
|
|
4935
|
+
readonly items: readonly string[];
|
|
4936
|
+
};
|
|
4517
4937
|
}>;
|
|
4518
4938
|
`,
|
|
4519
|
-
options: {
|
|
4939
|
+
options: {
|
|
4940
|
+
mutableVariablePrefixes: ['mut_'],
|
|
4941
|
+
mutablePropertyPrefixes: ['mut_'],
|
|
4942
|
+
},
|
|
4520
4943
|
},
|
|
4521
4944
|
{
|
|
4522
4945
|
name: 'Ignore prefix in interface',
|
|
@@ -4532,7 +4955,10 @@ describe(convertToReadonlyTransformer, () => {
|
|
|
4532
4955
|
readonly roles: readonly string[];
|
|
4533
4956
|
}
|
|
4534
4957
|
`,
|
|
4535
|
-
options: {
|
|
4958
|
+
options: {
|
|
4959
|
+
mutableVariablePrefixes: ['mut_'],
|
|
4960
|
+
mutablePropertyPrefixes: ['mut_'],
|
|
4961
|
+
},
|
|
4536
4962
|
},
|
|
4537
4963
|
{
|
|
4538
4964
|
name: 'Ignore prefixes for various name node types in interface',
|
|
@@ -4554,7 +4980,10 @@ describe(convertToReadonlyTransformer, () => {
|
|
|
4554
4980
|
readonly ['mu' + 't_d']: readonly string[]; // ComputedPropertyName
|
|
4555
4981
|
}
|
|
4556
4982
|
`,
|
|
4557
|
-
options: {
|
|
4983
|
+
options: {
|
|
4984
|
+
mutableVariablePrefixes: ['mut_'],
|
|
4985
|
+
mutablePropertyPrefixes: ['mut_'],
|
|
4986
|
+
},
|
|
4558
4987
|
},
|
|
4559
4988
|
{
|
|
4560
4989
|
name: 'Ignore prefix with index signature',
|
|
@@ -4566,13 +4995,16 @@ describe(convertToReadonlyTransformer, () => {
|
|
|
4566
4995
|
};
|
|
4567
4996
|
`,
|
|
4568
4997
|
expected: dedent`
|
|
4569
|
-
type T =
|
|
4998
|
+
type T = {
|
|
4570
4999
|
mut_items: {
|
|
4571
5000
|
[key: string]: number[];
|
|
4572
5001
|
};
|
|
4573
|
-
}
|
|
5002
|
+
};
|
|
4574
5003
|
`,
|
|
4575
|
-
options: {
|
|
5004
|
+
options: {
|
|
5005
|
+
mutableVariablePrefixes: ['mut_'],
|
|
5006
|
+
mutablePropertyPrefixes: ['mut_'],
|
|
5007
|
+
},
|
|
4576
5008
|
},
|
|
4577
5009
|
{
|
|
4578
5010
|
name: 'Index signature in ignored property',
|
|
@@ -4590,21 +5022,27 @@ describe(convertToReadonlyTransformer, () => {
|
|
|
4590
5022
|
};
|
|
4591
5023
|
}
|
|
4592
5024
|
`,
|
|
4593
|
-
options: {
|
|
5025
|
+
options: {
|
|
5026
|
+
mutableVariablePrefixes: ['mut_'],
|
|
5027
|
+
mutablePropertyPrefixes: ['mut_'],
|
|
5028
|
+
},
|
|
4594
5029
|
},
|
|
4595
5030
|
{
|
|
4596
5031
|
name: 'Type alias with ignored prefix',
|
|
4597
5032
|
source: dedent`
|
|
4598
|
-
type
|
|
5033
|
+
type MutableConfig = {
|
|
4599
5034
|
items: string[];
|
|
4600
5035
|
};
|
|
4601
5036
|
`,
|
|
4602
5037
|
expected: dedent`
|
|
4603
|
-
type
|
|
5038
|
+
type MutableConfig = {
|
|
4604
5039
|
items: string[];
|
|
4605
5040
|
};
|
|
4606
5041
|
`,
|
|
4607
|
-
options: {
|
|
5042
|
+
options: {
|
|
5043
|
+
mutableVariablePrefixes: ['mut_'],
|
|
5044
|
+
mutablePropertyPrefixes: ['mut_'],
|
|
5045
|
+
},
|
|
4608
5046
|
},
|
|
4609
5047
|
{
|
|
4610
5048
|
name: 'Function with ignored prefix',
|
|
@@ -4614,21 +5052,27 @@ describe(convertToReadonlyTransformer, () => {
|
|
|
4614
5052
|
expected: dedent`
|
|
4615
5053
|
function mut_process(data: string[]): number[] { return []; }
|
|
4616
5054
|
`,
|
|
4617
|
-
options: {
|
|
5055
|
+
options: {
|
|
5056
|
+
mutableVariablePrefixes: ['mut_'],
|
|
5057
|
+
mutablePropertyPrefixes: ['mut_'],
|
|
5058
|
+
},
|
|
4618
5059
|
},
|
|
4619
5060
|
{
|
|
4620
5061
|
name: 'Class with ignored prefix',
|
|
4621
5062
|
source: dedent`
|
|
4622
|
-
class
|
|
5063
|
+
class MutableStore {
|
|
4623
5064
|
items: string[] = [];
|
|
4624
5065
|
}
|
|
4625
5066
|
`,
|
|
4626
5067
|
expected: dedent`
|
|
4627
|
-
class
|
|
5068
|
+
class MutableStore {
|
|
4628
5069
|
items: string[] = [];
|
|
4629
5070
|
}
|
|
4630
5071
|
`,
|
|
4631
|
-
options: {
|
|
5072
|
+
options: {
|
|
5073
|
+
mutableVariablePrefixes: ['mut_'],
|
|
5074
|
+
mutablePropertyPrefixes: ['mut_'],
|
|
5075
|
+
},
|
|
4632
5076
|
},
|
|
4633
5077
|
{
|
|
4634
5078
|
name: 'Variable declaration with ignored prefix',
|
|
@@ -4638,37 +5082,46 @@ describe(convertToReadonlyTransformer, () => {
|
|
|
4638
5082
|
expected: dedent`
|
|
4639
5083
|
const mut_items: string[] = []
|
|
4640
5084
|
`,
|
|
4641
|
-
options: {
|
|
5085
|
+
options: {
|
|
5086
|
+
mutableVariablePrefixes: ['mut_'],
|
|
5087
|
+
mutablePropertyPrefixes: ['mut_'],
|
|
5088
|
+
},
|
|
4642
5089
|
},
|
|
4643
5090
|
{
|
|
4644
5091
|
name: 'Generic type with ignored prefix',
|
|
4645
5092
|
source: dedent`
|
|
4646
|
-
type
|
|
5093
|
+
type MutableList<T> = {
|
|
4647
5094
|
items: T[]
|
|
4648
5095
|
}
|
|
4649
5096
|
`,
|
|
4650
5097
|
expected: dedent`
|
|
4651
|
-
type
|
|
5098
|
+
type MutableList<T> = {
|
|
4652
5099
|
items: T[];
|
|
4653
5100
|
};
|
|
4654
5101
|
`,
|
|
4655
|
-
options: {
|
|
5102
|
+
options: {
|
|
5103
|
+
mutableVariablePrefixes: ['mut_'],
|
|
5104
|
+
mutablePropertyPrefixes: ['mut_'],
|
|
5105
|
+
},
|
|
4656
5106
|
},
|
|
4657
5107
|
{
|
|
4658
5108
|
name: 'Union type with ignored prefix members',
|
|
4659
5109
|
source: dedent`
|
|
4660
|
-
type Result =
|
|
4661
|
-
type
|
|
5110
|
+
type Result = MutableSuccess | Error;
|
|
5111
|
+
type MutableSuccess = {
|
|
4662
5112
|
data: string[]
|
|
4663
5113
|
};
|
|
4664
5114
|
`,
|
|
4665
5115
|
expected: dedent`
|
|
4666
|
-
type Result =
|
|
4667
|
-
type
|
|
5116
|
+
type Result = MutableSuccess | Error;
|
|
5117
|
+
type MutableSuccess = {
|
|
4668
5118
|
data: string[];
|
|
4669
5119
|
};
|
|
4670
5120
|
`,
|
|
4671
|
-
options: {
|
|
5121
|
+
options: {
|
|
5122
|
+
mutableVariablePrefixes: ['mut_'],
|
|
5123
|
+
mutablePropertyPrefixes: ['mut_'],
|
|
5124
|
+
},
|
|
4672
5125
|
},
|
|
4673
5126
|
{
|
|
4674
5127
|
name: 'Property with ignored prefix in class',
|
|
@@ -4684,7 +5137,10 @@ describe(convertToReadonlyTransformer, () => {
|
|
|
4684
5137
|
readonly items: readonly number[] = [];
|
|
4685
5138
|
}
|
|
4686
5139
|
`,
|
|
4687
|
-
options: {
|
|
5140
|
+
options: {
|
|
5141
|
+
mutableVariablePrefixes: ['mut_'],
|
|
5142
|
+
mutablePropertyPrefixes: ['mut_'],
|
|
5143
|
+
},
|
|
4688
5144
|
},
|
|
4689
5145
|
{
|
|
4690
5146
|
name: 'Method parameter with ignored prefix',
|
|
@@ -4698,7 +5154,10 @@ describe(convertToReadonlyTransformer, () => {
|
|
|
4698
5154
|
process(mut_data: string[], data: readonly number[]) {}
|
|
4699
5155
|
}
|
|
4700
5156
|
`,
|
|
4701
|
-
options: {
|
|
5157
|
+
options: {
|
|
5158
|
+
mutableVariablePrefixes: ['mut_'],
|
|
5159
|
+
mutablePropertyPrefixes: ['mut_'],
|
|
5160
|
+
},
|
|
4702
5161
|
},
|
|
4703
5162
|
{
|
|
4704
5163
|
name: 'Function expression with ignored prefix',
|
|
@@ -4708,7 +5167,10 @@ describe(convertToReadonlyTransformer, () => {
|
|
|
4708
5167
|
expected: dedent`
|
|
4709
5168
|
const fn = function mut_process(data: string[]): number[] { return []; }
|
|
4710
5169
|
`,
|
|
4711
|
-
options: {
|
|
5170
|
+
options: {
|
|
5171
|
+
mutableVariablePrefixes: ['mut_'],
|
|
5172
|
+
mutablePropertyPrefixes: ['mut_'],
|
|
5173
|
+
},
|
|
4712
5174
|
},
|
|
4713
5175
|
{
|
|
4714
5176
|
name: 'Array binding pattern in variable declaration (not implemented)',
|
|
@@ -4718,17 +5180,25 @@ describe(convertToReadonlyTransformer, () => {
|
|
|
4718
5180
|
expected: dedent`
|
|
4719
5181
|
const [mut_first, mut_second]: readonly [readonly string[], readonly number[]] = [[], []];
|
|
4720
5182
|
`,
|
|
4721
|
-
options: {
|
|
5183
|
+
options: {
|
|
5184
|
+
mutableVariablePrefixes: ['mut_'],
|
|
5185
|
+
mutablePropertyPrefixes: ['mut_'],
|
|
5186
|
+
},
|
|
4722
5187
|
},
|
|
4723
5188
|
{
|
|
5189
|
+
// The binding pattern itself is not looked at (see the TODO in
|
|
5190
|
+
// `transformNode`); the type literal is left as is for its properties.
|
|
4724
5191
|
name: 'Object binding pattern in variable declaration (not implemented)',
|
|
4725
5192
|
source: dedent`
|
|
4726
5193
|
const { mut_arr, mut_obj }: { mut_arr: string[], mut_obj: number[] } = { mut_arr: [], mut_obj: [] };
|
|
4727
5194
|
`,
|
|
4728
5195
|
expected: dedent`
|
|
4729
|
-
const { mut_arr, mut_obj }:
|
|
5196
|
+
const { mut_arr, mut_obj }: { mut_arr: string[], mut_obj: number[] } = { mut_arr: [], mut_obj: [] };
|
|
4730
5197
|
`,
|
|
4731
|
-
options: {
|
|
5198
|
+
options: {
|
|
5199
|
+
mutableVariablePrefixes: ['mut_'],
|
|
5200
|
+
mutablePropertyPrefixes: ['mut_'],
|
|
5201
|
+
},
|
|
4732
5202
|
},
|
|
4733
5203
|
{
|
|
4734
5204
|
name: 'Parameter with ignored prefix in class constructor',
|
|
@@ -4742,7 +5212,10 @@ describe(convertToReadonlyTransformer, () => {
|
|
|
4742
5212
|
constructor(mut_config: string[]) {}
|
|
4743
5213
|
}
|
|
4744
5214
|
`,
|
|
4745
|
-
options: {
|
|
5215
|
+
options: {
|
|
5216
|
+
mutableVariablePrefixes: ['mut_'],
|
|
5217
|
+
mutablePropertyPrefixes: ['mut_'],
|
|
5218
|
+
},
|
|
4746
5219
|
},
|
|
4747
5220
|
{
|
|
4748
5221
|
name: 'Parameter with ignored prefix in type literal method',
|
|
@@ -4756,7 +5229,10 @@ describe(convertToReadonlyTransformer, () => {
|
|
|
4756
5229
|
process(mut_data: string[]): void;
|
|
4757
5230
|
}>;
|
|
4758
5231
|
`,
|
|
4759
|
-
options: {
|
|
5232
|
+
options: {
|
|
5233
|
+
mutableVariablePrefixes: ['mut_'],
|
|
5234
|
+
mutablePropertyPrefixes: ['mut_'],
|
|
5235
|
+
},
|
|
4760
5236
|
},
|
|
4761
5237
|
{
|
|
4762
5238
|
name: 'Multiple parameters with ignored prefix',
|
|
@@ -4770,7 +5246,10 @@ describe(convertToReadonlyTransformer, () => {
|
|
|
4770
5246
|
update(mut_id: string, mut_data: object[], data: readonly string[]): void;
|
|
4771
5247
|
}
|
|
4772
5248
|
`,
|
|
4773
|
-
options: {
|
|
5249
|
+
options: {
|
|
5250
|
+
mutableVariablePrefixes: ['mut_'],
|
|
5251
|
+
mutablePropertyPrefixes: ['mut_'],
|
|
5252
|
+
},
|
|
4774
5253
|
},
|
|
4775
5254
|
{
|
|
4776
5255
|
name: 'Arrow function with ignored prefix parameters',
|
|
@@ -4780,18 +5259,228 @@ describe(convertToReadonlyTransformer, () => {
|
|
|
4780
5259
|
expected: dedent`
|
|
4781
5260
|
const handler = (mut_event: Event[], event: readonly CustomEvent[]) => {};
|
|
4782
5261
|
`,
|
|
4783
|
-
options: {
|
|
5262
|
+
options: {
|
|
5263
|
+
mutableVariablePrefixes: ['mut_'],
|
|
5264
|
+
mutablePropertyPrefixes: ['mut_'],
|
|
5265
|
+
},
|
|
4784
5266
|
},
|
|
4785
5267
|
{
|
|
4786
|
-
name: '
|
|
5268
|
+
name: '"draft" is not a default prefix',
|
|
4787
5269
|
source: dedent`
|
|
4788
5270
|
const handler = (draft: Event[], event: CustomEvent[]) => {};
|
|
5271
|
+
type T = { draft: boolean; items: string[] };
|
|
5272
|
+
`,
|
|
5273
|
+
expected: dedent`
|
|
5274
|
+
const handler = (draft: readonly Event[], event: readonly CustomEvent[]) => {};
|
|
5275
|
+
type T = Readonly<{ draft: boolean; items: readonly string[] }>;
|
|
5276
|
+
`,
|
|
5277
|
+
},
|
|
5278
|
+
{
|
|
5279
|
+
name: '"Mutable" and "mut_" are the default type prefixes',
|
|
5280
|
+
source: dedent`
|
|
5281
|
+
type MutableFoo = { a: string[] };
|
|
5282
|
+
interface MutableBar { a: string[] }
|
|
5283
|
+
class MutableBaz { a: string[] = []; }
|
|
5284
|
+
const Q = class MutableQ { a: string[] = []; };
|
|
5285
|
+
type mut_Qux = { a: string[] };
|
|
5286
|
+
type Foo = { a: MutableFoo[] };
|
|
4789
5287
|
`,
|
|
4790
5288
|
expected: dedent`
|
|
4791
|
-
|
|
5289
|
+
type MutableFoo = { a: string[] };
|
|
5290
|
+
interface MutableBar { a: string[] }
|
|
5291
|
+
class MutableBaz { a: string[] = []; }
|
|
5292
|
+
const Q = class MutableQ { a: string[] = []; };
|
|
5293
|
+
type mut_Qux = { a: string[] };
|
|
5294
|
+
type Foo = Readonly<{ a: readonly MutableFoo[] }>;
|
|
4792
5295
|
`,
|
|
4793
5296
|
},
|
|
5297
|
+
{
|
|
5298
|
+
name: 'Variable and property prefixes are separate',
|
|
5299
|
+
source: dedent`
|
|
5300
|
+
const handler = (draft: Event[]) => {};
|
|
5301
|
+
type T = { draft: boolean; items: string[] };
|
|
5302
|
+
`,
|
|
5303
|
+
expected: dedent`
|
|
5304
|
+
const handler = (draft: Event[]) => {};
|
|
5305
|
+
type T = Readonly<{ draft: boolean; items: readonly string[] }>;
|
|
5306
|
+
`,
|
|
5307
|
+
options: { mutableVariablePrefixes: ['draft'] },
|
|
5308
|
+
},
|
|
5309
|
+
{
|
|
5310
|
+
name: 'Each kind of name is matched against its own list: variables',
|
|
5311
|
+
source: dedent`
|
|
5312
|
+
type mut_T = { a: string[] };
|
|
5313
|
+
interface mut_I { a: string[] }
|
|
5314
|
+
class mut_C { a: string[] = []; }
|
|
5315
|
+
function mut_f(a: string[]): void {}
|
|
5316
|
+
const mut_x: string[] = [];
|
|
5317
|
+
type U = [mut_a: string[], b: string[]];
|
|
5318
|
+
class D { #mut_p: string[] = []; 'mut_q': string[] = []; }
|
|
5319
|
+
type V = { mut_r: string[]; s: string[] };
|
|
5320
|
+
interface J { mut_a: string[]; f(mut_p: string[]): void }
|
|
5321
|
+
type W = { mut_m(): string[]; b: string[] };
|
|
5322
|
+
class E { mut_m(x: string[]): string[] { return []; } get mut_g(): string[] { return []; } }
|
|
5323
|
+
`,
|
|
5324
|
+
expected: dedent`
|
|
5325
|
+
type mut_T = Readonly<{ a: readonly string[] }>;
|
|
5326
|
+
interface mut_I { readonly a: readonly string[] }
|
|
5327
|
+
class mut_C { readonly a: readonly string[] = []; }
|
|
5328
|
+
function mut_f(a: string[]): void {}
|
|
5329
|
+
const mut_x: string[] = [];
|
|
5330
|
+
type U = readonly [mut_a: readonly string[], b: readonly string[]];
|
|
5331
|
+
class D { readonly #mut_p: readonly string[] = []; readonly 'mut_q': readonly string[] = []; }
|
|
5332
|
+
type V = Readonly<{ mut_r: readonly string[]; s: readonly string[] }>;
|
|
5333
|
+
interface J { readonly mut_a: readonly string[]; f(mut_p: string[]): void }
|
|
5334
|
+
type W = Readonly<{ mut_m(): readonly string[]; b: readonly string[] }>;
|
|
5335
|
+
class E { mut_m(x: readonly string[]): readonly string[] { return []; } get mut_g(): readonly string[] { return []; } }
|
|
5336
|
+
`,
|
|
5337
|
+
options: {
|
|
5338
|
+
mutableVariablePrefixes: ['mut_'],
|
|
5339
|
+
mutableTypePrefixes: [],
|
|
5340
|
+
mutablePropertyPrefixes: [],
|
|
5341
|
+
},
|
|
5342
|
+
},
|
|
5343
|
+
{
|
|
5344
|
+
name: 'Each kind of name is matched against its own list: types',
|
|
5345
|
+
source: dedent`
|
|
5346
|
+
type mut_T = { a: string[] };
|
|
5347
|
+
interface mut_I { a: string[] }
|
|
5348
|
+
class mut_C { a: string[] = []; }
|
|
5349
|
+
function mut_f(a: string[]): void {}
|
|
5350
|
+
const mut_x: string[] = [];
|
|
5351
|
+
type U = [mut_a: string[], b: string[]];
|
|
5352
|
+
class D { #mut_p: string[] = []; 'mut_q': string[] = []; }
|
|
5353
|
+
type V = { mut_r: string[]; s: string[] };
|
|
5354
|
+
interface J { mut_a: string[]; f(mut_p: string[]): void }
|
|
5355
|
+
type W = { mut_m(): string[]; b: string[] };
|
|
5356
|
+
class E { mut_m(x: string[]): string[] { return []; } get mut_g(): string[] { return []; } }
|
|
5357
|
+
`,
|
|
5358
|
+
expected: dedent`
|
|
5359
|
+
type mut_T = { a: string[] };
|
|
5360
|
+
interface mut_I { a: string[] }
|
|
5361
|
+
class mut_C { a: string[] = []; }
|
|
5362
|
+
function mut_f(a: readonly string[]): void {}
|
|
5363
|
+
const mut_x: readonly string[] = [];
|
|
5364
|
+
type U = readonly [mut_a: readonly string[], b: readonly string[]];
|
|
5365
|
+
class D { readonly #mut_p: readonly string[] = []; readonly 'mut_q': readonly string[] = []; }
|
|
5366
|
+
type V = Readonly<{ mut_r: readonly string[]; s: readonly string[] }>;
|
|
5367
|
+
interface J { readonly mut_a: readonly string[]; f(mut_p: readonly string[]): void }
|
|
5368
|
+
type W = Readonly<{ mut_m(): readonly string[]; b: readonly string[] }>;
|
|
5369
|
+
class E { mut_m(x: readonly string[]): readonly string[] { return []; } get mut_g(): readonly string[] { return []; } }
|
|
5370
|
+
`,
|
|
5371
|
+
options: {
|
|
5372
|
+
mutableVariablePrefixes: [],
|
|
5373
|
+
mutableTypePrefixes: ['mut_'],
|
|
5374
|
+
mutablePropertyPrefixes: [],
|
|
5375
|
+
},
|
|
5376
|
+
},
|
|
5377
|
+
{
|
|
5378
|
+
name: 'Each kind of name is matched against its own list: properties',
|
|
5379
|
+
source: dedent`
|
|
5380
|
+
type mut_T = { a: string[] };
|
|
5381
|
+
interface mut_I { a: string[] }
|
|
5382
|
+
class mut_C { a: string[] = []; }
|
|
5383
|
+
function mut_f(a: string[]): void {}
|
|
5384
|
+
const mut_x: string[] = [];
|
|
5385
|
+
type U = [mut_a: string[], b: string[]];
|
|
5386
|
+
class D { #mut_p: string[] = []; 'mut_q': string[] = []; }
|
|
5387
|
+
type V = { mut_r: string[]; s: string[] };
|
|
5388
|
+
interface J { mut_a: string[]; f(mut_p: string[]): void }
|
|
5389
|
+
type W = { mut_m(): string[]; b: string[] };
|
|
5390
|
+
class E { mut_m(x: string[]): string[] { return []; } get mut_g(): string[] { return []; } }
|
|
5391
|
+
`,
|
|
5392
|
+
expected: dedent`
|
|
5393
|
+
type mut_T = Readonly<{ a: readonly string[] }>;
|
|
5394
|
+
interface mut_I { readonly a: readonly string[] }
|
|
5395
|
+
class mut_C { readonly a: readonly string[] = []; }
|
|
5396
|
+
function mut_f(a: readonly string[]): void {}
|
|
5397
|
+
const mut_x: readonly string[] = [];
|
|
5398
|
+
type U = readonly [mut_a: string[], b: readonly string[]];
|
|
5399
|
+
class D { #mut_p: string[] = []; 'mut_q': string[] = []; }
|
|
5400
|
+
type V = { mut_r: string[]; readonly s: readonly string[] };
|
|
5401
|
+
interface J { mut_a: string[]; f(mut_p: readonly string[]): void }
|
|
5402
|
+
type W = { mut_m(): string[]; readonly b: readonly string[] };
|
|
5403
|
+
class E { mut_m(x: string[]): string[] { return []; } get mut_g(): string[] { return []; } }
|
|
5404
|
+
`,
|
|
5405
|
+
options: {
|
|
5406
|
+
mutableVariablePrefixes: [],
|
|
5407
|
+
mutableTypePrefixes: [],
|
|
5408
|
+
mutablePropertyPrefixes: ['mut_'],
|
|
5409
|
+
},
|
|
5410
|
+
},
|
|
5411
|
+
{
|
|
5412
|
+
name: 'A property prefix with a "#" covers private names only',
|
|
5413
|
+
source: dedent`
|
|
5414
|
+
class C { #mut_a: string[] = []; mut_b: string[] = []; }
|
|
5415
|
+
`,
|
|
5416
|
+
expected: dedent`
|
|
5417
|
+
class C { #mut_a: string[] = []; readonly mut_b: readonly string[] = []; }
|
|
5418
|
+
`,
|
|
5419
|
+
options: { mutablePropertyPrefixes: ['#mut_'] },
|
|
5420
|
+
},
|
|
5421
|
+
{
|
|
5422
|
+
name: 'A property prefix does not apply to variables',
|
|
5423
|
+
source: dedent`
|
|
5424
|
+
const handler = (draft: Event[]) => {};
|
|
5425
|
+
type T = { draft: boolean; items: string[] };
|
|
5426
|
+
`,
|
|
5427
|
+
expected: dedent`
|
|
5428
|
+
const handler = (draft: readonly Event[]) => {};
|
|
5429
|
+
type T = { draft: boolean; readonly items: readonly string[] };
|
|
5430
|
+
`,
|
|
5431
|
+
options: { mutablePropertyPrefixes: ['draft'] },
|
|
5432
|
+
},
|
|
5433
|
+
{
|
|
5434
|
+
name: 'A constructor parameter property is a property',
|
|
5435
|
+
source: dedent`
|
|
5436
|
+
class C {
|
|
5437
|
+
draftA: string[] = [];
|
|
5438
|
+
constructor(private draftB: string[], readonly draftD: string[], draftC: string[]) {}
|
|
5439
|
+
}
|
|
5440
|
+
`,
|
|
5441
|
+
expected: dedent`
|
|
5442
|
+
class C {
|
|
5443
|
+
draftA: string[] = [];
|
|
5444
|
+
constructor(private draftB: string[], readonly draftD: string[], draftC: readonly string[]) {}
|
|
5445
|
+
}
|
|
5446
|
+
`,
|
|
5447
|
+
options: { mutablePropertyPrefixes: ['draft'] },
|
|
5448
|
+
},
|
|
5449
|
+
{
|
|
5450
|
+
name: 'A constructor parameter that is not a property is a variable',
|
|
5451
|
+
source: dedent`
|
|
5452
|
+
class C {
|
|
5453
|
+
draftA: string[] = [];
|
|
5454
|
+
constructor(private draftB: string[], draftC: string[]) {}
|
|
5455
|
+
}
|
|
5456
|
+
`,
|
|
5457
|
+
expected: dedent`
|
|
5458
|
+
class C {
|
|
5459
|
+
readonly draftA: readonly string[] = [];
|
|
5460
|
+
constructor(private readonly draftB: readonly string[], draftC: string[]) {}
|
|
5461
|
+
}
|
|
5462
|
+
`,
|
|
5463
|
+
options: { mutableVariablePrefixes: ['draft'] },
|
|
5464
|
+
},
|
|
4794
5465
|
])('$name', testFn);
|
|
5466
|
+
|
|
5467
|
+
test('the removed ignorePrefixes option is refused', () => {
|
|
5468
|
+
expect(() =>
|
|
5469
|
+
convertToReadonlyTransformer(
|
|
5470
|
+
// @ts-expect-error removed in 4.0.0
|
|
5471
|
+
{ ignorePrefixes: ['mut_'] },
|
|
5472
|
+
),
|
|
5473
|
+
).toThrow('mutableVariablePrefixes');
|
|
5474
|
+
});
|
|
5475
|
+
|
|
5476
|
+
test('an unset ignorePrefixes option is accepted', () => {
|
|
5477
|
+
expect(() =>
|
|
5478
|
+
convertToReadonlyTransformer(
|
|
5479
|
+
// @ts-expect-error removed in 4.0.0
|
|
5480
|
+
{ ignorePrefixes: undefined },
|
|
5481
|
+
),
|
|
5482
|
+
).not.toThrow();
|
|
5483
|
+
});
|
|
4795
5484
|
});
|
|
4796
5485
|
|
|
4797
5486
|
describe('Disable comment handling', () => {
|