Skip to content

Commit b3dac20

Browse files
feat(prefer-immutable-types): allow for changing suggestion messages
1 parent b34253f commit b3dac20

File tree

5 files changed

+129
-45
lines changed

5 files changed

+129
-45
lines changed

docs/rules/prefer-immutable-types.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,9 +244,15 @@ type Options = {
244244
};
245245

246246
suggestions?: {
247-
ReadonlyShallow?: Array<Array<{ pattern: string; replace: string }>>;
248-
ReadonlyDeep?: Array<Array<{ pattern: string; replace: string }>>;
249-
Immutable?: Array<Array<{ pattern: string; replace: string }>>;
247+
ReadonlyShallow?: Array<
248+
Array<{ pattern: string; replace: string; message?: string }>
249+
>;
250+
ReadonlyDeep?: Array<
251+
Array<{ pattern: string; replace: string; message?: string }>
252+
>;
253+
Immutable?: Array<
254+
Array<{ pattern: string; replace: string; message?: string }>
255+
>;
250256
};
251257

252258
overrides?: Array<{
@@ -297,14 +303,19 @@ const defaults = {
297303
pattern:
298304
"^([_$a-zA-Z\\xA0-\\uFFFF][_$a-zA-Z0-9\\xA0-\\uFFFF]*\\[\\])$",
299305
replace: "readonly $1",
306+
message: "Prepend with readonly.",
300307
},
301308
{
302309
pattern: "^(Array|Map|Set)<(.+)>$",
303310
replace: "Readonly$1<$2>",
311+
message: "Use Readonly$1 instead of $1.",
304312
},
313+
],
314+
[
305315
{
306316
pattern: "^(.+)$",
307317
replace: "Readonly<$1>",
318+
message: "Surround with Readonly.",
308319
},
309320
],
310321
],

src/rules/prefer-immutable-types.ts

Lines changed: 53 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ type FixerConfigRaw = {
9393
replace: string;
9494
};
9595

96+
type SuggestionsConfigRaw = Array<FixerConfigRaw & { message?: string }>;
97+
9698
type FixerConfigRawMap = Partial<
9799
Record<
98100
"ReadonlyShallow" | "ReadonlyDeep" | "Immutable",
@@ -103,7 +105,7 @@ type FixerConfigRawMap = Partial<
103105
type SuggestionConfigRawMap = Partial<
104106
Record<
105107
"ReadonlyShallow" | "ReadonlyDeep" | "Immutable",
106-
FixerConfigRaw[][] | undefined
108+
SuggestionsConfigRaw[] | undefined
107109
>
108110
>;
109111

@@ -112,7 +114,7 @@ type FixerConfig = {
112114
replace: string;
113115
};
114116

115-
type SuggestionsConfig = FixerConfig[];
117+
type SuggestionsConfig = Array<FixerConfig & { message?: string }>;
116118

117119
/**
118120
* The options this rule can take.
@@ -216,6 +218,7 @@ const suggestionsSchema: JSONSchema4 = {
216218
properties: {
217219
pattern: { type: "string" },
218220
replace: { type: "string" },
221+
message: { type: "string" },
219222
},
220223
additionalProperties: false,
221224
},
@@ -286,14 +289,19 @@ const defaultOptions: RawOptions = [
286289
pattern:
287290
"^([_$a-zA-Z\\xA0-\\uFFFF][_$a-zA-Z0-9\\xA0-\\uFFFF]*\\[\\])$",
288291
replace: "readonly $1",
292+
message: "Prepend with readonly.",
289293
},
290294
{
291295
pattern: "^(Array|Map|Set)<(.+)>$",
292296
replace: "Readonly$1<$2>",
297+
message: "Use Readonly$1 instead of $1.",
293298
},
299+
],
300+
[
294301
{
295302
pattern: "^(.+)$",
296303
replace: "Readonly<$1>",
304+
message: "Surround with Readonly.",
297305
},
298306
],
299307
],
@@ -314,6 +322,7 @@ const errorMessages = {
314322
propertyImmutability:
315323
'Property should have an immutability of at least "{{ expected }}" (actual: "{{ actual }}").',
316324
propertyModifier: "Property should have a readonly modifier.",
325+
userDefined: "{{ message }}",
317326
} as const;
318327

319328
/**
@@ -342,7 +351,7 @@ type Descriptor = RuleResult<
342351

343352
type AllFixers = {
344353
fix: ReportFixFunction | null;
345-
suggestionFixers: ReportFixFunction[] | null;
354+
suggestionFixers: Array<{ fix: ReportFixFunction; message: string }> | null;
346355
};
347356

348357
/**
@@ -394,14 +403,27 @@ function getConfiguredSuggestionFixers(
394403
suggestionsConfigs: ReadonlyArray<SuggestionsConfig>,
395404
) {
396405
return suggestionsConfigs
397-
.map((configs): NonNullable<Descriptor["fix"]> | null => {
398-
const config = configs.find((c) => c.pattern.test(text));
399-
if (config === undefined) {
400-
return null;
401-
}
402-
return (fixer) =>
403-
fixer.replaceText(node, text.replace(config.pattern, config.replace));
404-
})
406+
.map(
407+
(
408+
configs,
409+
): { fix: NonNullable<Descriptor["fix"]>; message: string } | null => {
410+
const config = configs.find((c) => c.pattern.test(text));
411+
if (config === undefined) {
412+
return null;
413+
}
414+
return {
415+
fix: (fixer) =>
416+
fixer.replaceText(
417+
node,
418+
text.replace(config.pattern, config.replace),
419+
),
420+
message:
421+
config.message === undefined
422+
? `Replace with: ${text.replace(config.pattern, config.replace)}`
423+
: text.replace(config.pattern, config.message),
424+
};
425+
},
426+
)
405427
.filter(isDefined);
406428
}
407429

@@ -596,9 +618,11 @@ function getParameterTypeViolations(
596618
data,
597619
fix,
598620
suggest:
599-
suggestionFixers?.map((fix) => ({
600-
messageId,
601-
data,
621+
suggestionFixers?.map(({ fix, message }) => ({
622+
messageId: "userDefined",
623+
data: {
624+
message,
625+
},
602626
fix,
603627
})) ?? null,
604628
};
@@ -722,9 +746,11 @@ function getReturnTypeViolations(
722746
data,
723747
fix,
724748
suggest:
725-
suggestionFixers?.map((fix) => ({
726-
messageId,
727-
data,
749+
suggestionFixers?.map(({ fix, message }) => ({
750+
messageId: "userDefined",
751+
data: {
752+
message,
753+
},
728754
fix,
729755
})) ?? null,
730756
},
@@ -795,9 +821,11 @@ function getReturnTypeViolations(
795821
data,
796822
fix,
797823
suggest:
798-
suggestionFixers?.map((fix) => ({
799-
messageId,
800-
data,
824+
suggestionFixers?.map(({ fix, message }) => ({
825+
messageId: "userDefined",
826+
data: {
827+
message,
828+
},
801829
fix,
802830
})) ?? null,
803831
},
@@ -995,9 +1023,11 @@ function checkVariable(
9951023
data,
9961024
fix,
9971025
suggest:
998-
suggestionFixers?.map((fix) => ({
999-
messageId,
1000-
data,
1026+
suggestionFixers?.map(({ fix, message }) => ({
1027+
messageId: "userDefined",
1028+
data: {
1029+
message,
1030+
},
10011031
fix,
10021032
})) ?? null,
10031033
};

tests/rules/prefer-immutable-types/ts/parameters/invalid.ts

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ const tests: Array<
4646
column: 14,
4747
suggestions: [
4848
{
49-
messageId: "parameter",
49+
messageId: "userDefined",
50+
data: { message: "Surround with Readonly." },
5051
output:
5152
"function foo(arg1: Readonly<{ foo: string }>, arg2: { foo: number }) {}",
5253
},
@@ -59,7 +60,8 @@ const tests: Array<
5960
column: 37,
6061
suggestions: [
6162
{
62-
messageId: "parameter",
63+
messageId: "userDefined",
64+
data: { message: "Surround with Readonly." },
6365
output:
6466
"function foo(arg1: { foo: string }, arg2: Readonly<{ foo: number }>) {}",
6567
},
@@ -179,7 +181,8 @@ const tests: Array<
179181
column: 5,
180182
suggestions: [
181183
{
182-
messageId: "propertyModifier",
184+
messageId: "userDefined",
185+
data: { message: "Prepend with readonly." },
183186
output: dedent`
184187
class Klass {
185188
constructor (
@@ -199,7 +202,8 @@ const tests: Array<
199202
column: 5,
200203
suggestions: [
201204
{
202-
messageId: "propertyModifier",
205+
messageId: "userDefined",
206+
data: { message: "Prepend with readonly." },
203207
output: dedent`
204208
class Klass {
205209
constructor (
@@ -219,7 +223,8 @@ const tests: Array<
219223
column: 5,
220224
suggestions: [
221225
{
222-
messageId: "propertyModifier",
226+
messageId: "userDefined",
227+
data: { message: "Prepend with readonly." },
223228
output: dedent`
224229
class Klass {
225230
constructor (
@@ -245,7 +250,8 @@ const tests: Array<
245250
column: 46,
246251
suggestions: [
247252
{
248-
messageId: "parameter",
253+
messageId: "userDefined",
254+
data: { message: "Surround with Readonly." },
249255
output:
250256
"function foo(arg0: { foo: string | number }, arg1: Readonly<{ foo: string | number }>): arg0 is { foo: number } {}",
251257
},
@@ -295,7 +301,8 @@ const tests: Array<
295301
column: 14,
296302
suggestions: [
297303
{
298-
messageId: "parameter",
304+
messageId: "userDefined",
305+
data: { message: "Replace with: ReadonlyDeep<{ foo: string }>" },
299306
output: "function foo(arg1: ReadonlyDeep<{ foo: string }>) {}",
300307
},
301308
],
@@ -329,7 +336,10 @@ const tests: Array<
329336
column: 14,
330337
suggestions: [
331338
{
332-
messageId: "parameter",
339+
messageId: "userDefined",
340+
data: {
341+
message: "Replace with: ReadonlyDeep<{ foo: { bar: string } }>",
342+
},
333343
output:
334344
"function foo(arg1: ReadonlyDeep<{ foo: { bar: string } }>) {}",
335345
},
@@ -357,7 +367,10 @@ const tests: Array<
357367
column: 14,
358368
suggestions: [
359369
{
360-
messageId: "parameter",
370+
messageId: "userDefined",
371+
data: {
372+
message: "Use ReadonlyArray instead of Array.",
373+
},
361374
output: dedent`
362375
function foo(arg: ReadonlyArray<string>) {}
363376
function foo(arg: string[]) {}
@@ -378,7 +391,10 @@ const tests: Array<
378391
column: 14,
379392
suggestions: [
380393
{
381-
messageId: "parameter",
394+
messageId: "userDefined",
395+
data: {
396+
message: "Prepend with readonly.",
397+
},
382398
output: dedent`
383399
function foo(arg: Array<string>) {}
384400
function foo(arg: readonly string[]) {}
@@ -399,7 +415,10 @@ const tests: Array<
399415
column: 14,
400416
suggestions: [
401417
{
402-
messageId: "parameter",
418+
messageId: "userDefined",
419+
data: {
420+
message: "Use ReadonlySet instead of Set.",
421+
},
403422
output: dedent`
404423
function foo(arg: Array<string>) {}
405424
function foo(arg: string[]) {}
@@ -420,7 +439,10 @@ const tests: Array<
420439
column: 14,
421440
suggestions: [
422441
{
423-
messageId: "parameter",
442+
messageId: "userDefined",
443+
data: {
444+
message: "Use ReadonlyMap instead of Map.",
445+
},
424446
output: dedent`
425447
function foo(arg: Array<string>) {}
426448
function foo(arg: string[]) {}

tests/rules/prefer-immutable-types/ts/return-types/invalid.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,10 @@ const tests: Array<
6868
column: 26,
6969
suggestions: [
7070
{
71-
messageId: "returnType",
71+
messageId: "userDefined",
72+
data: {
73+
message: "Surround with Readonly.",
74+
},
7275
output: dedent`
7376
function foo(arg: number): Readonly<{ foo: string }>;
7477
function foo(arg: string): Readonly<{ foo: number }>;
@@ -85,7 +88,10 @@ const tests: Array<
8588
column: 27,
8689
suggestions: [
8790
{
88-
messageId: "returnType",
91+
messageId: "userDefined",
92+
data: {
93+
message: "Surround with Readonly.",
94+
},
8995
output: dedent`
9096
function foo(arg: number): { foo: string };
9197
function foo(arg: string): Readonly<{ foo: number }>;

0 commit comments

Comments
 (0)