Skip to content

Commit 822cc33

Browse files
feat(prefer-immutable-types): allow for changing suggestion messages (#828)
1 parent 3ed5aa6 commit 822cc33

File tree

5 files changed

+198
-75
lines changed

5 files changed

+198
-75
lines changed

docs/rules/prefer-immutable-types.md

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

242242
suggestions?: {
243-
ReadonlyShallow?: Array<Array<{ pattern: string; replace: string }>>;
244-
ReadonlyDeep?: Array<Array<{ pattern: string; replace: string }>>;
245-
Immutable?: Array<Array<{ pattern: string; replace: string }>>;
243+
ReadonlyShallow?: Array<
244+
Array<{ pattern: string; replace: string; message?: string }>
245+
>;
246+
ReadonlyDeep?: Array<
247+
Array<{ pattern: string; replace: string; message?: string }>
248+
>;
249+
Immutable?: Array<
250+
Array<{ pattern: string; replace: string; message?: string }>
251+
>;
246252
};
247253
};
248254
```
@@ -262,14 +268,19 @@ const defaults = {
262268
pattern:
263269
"^([_$a-zA-Z\\xA0-\\uFFFF][_$a-zA-Z0-9\\xA0-\\uFFFF]*\\[\\])$",
264270
replace: "readonly $1",
271+
message: "Prepend with readonly.",
265272
},
266273
{
267274
pattern: "^(Array|Map|Set)<(.+)>$",
268275
replace: "Readonly$1<$2>",
276+
message: "Use Readonly$1 instead of $1.",
269277
},
278+
],
279+
[
270280
{
271281
pattern: "^(.+)$",
272282
replace: "Readonly<$1>",
283+
message: "Surround with Readonly.",
273284
},
274285
],
275286
],

src/rules/prefer-immutable-types.ts

Lines changed: 73 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ type FixerConfigRaw = {
6969
replace: string;
7070
};
7171

72+
type SuggestionsConfigRaw = Array<FixerConfigRaw & { message?: string }>;
73+
7274
type FixerConfigRawMap = Partial<
7375
Record<
7476
"ReadonlyShallow" | "ReadonlyDeep" | "Immutable",
@@ -79,7 +81,7 @@ type FixerConfigRawMap = Partial<
7981
type SuggestionConfigRawMap = Partial<
8082
Record<
8183
"ReadonlyShallow" | "ReadonlyDeep" | "Immutable",
82-
FixerConfigRaw[][] | undefined
84+
SuggestionsConfigRaw[] | undefined
8385
>
8486
>;
8587

@@ -88,7 +90,7 @@ type FixerConfig = {
8890
replace: string;
8991
};
9092

91-
type SuggestionsConfig = FixerConfig[];
93+
type SuggestionsConfig = Array<FixerConfig & { message?: string }>;
9294

9395
/**
9496
* The options this rule can take.
@@ -205,6 +207,7 @@ const suggestionsSchema: JSONSchema4 = {
205207
properties: {
206208
pattern: { type: "string" },
207209
replace: { type: "string" },
210+
message: { type: "string" },
208211
},
209212
additionalProperties: false,
210213
},
@@ -275,14 +278,19 @@ const defaultOptions: Options = [
275278
pattern:
276279
"^([_$a-zA-Z\\xA0-\\uFFFF][_$a-zA-Z0-9\\xA0-\\uFFFF]*\\[\\])$",
277280
replace: "readonly $1",
281+
message: "Prepend with readonly.",
278282
},
279283
{
280284
pattern: "^(Array|Map|Set)<(.+)>$",
281285
replace: "Readonly$1<$2>",
286+
message: "Use Readonly$1 instead of $1.",
282287
},
288+
],
289+
[
283290
{
284291
pattern: "^(.+)$",
285292
replace: "Readonly<$1>",
293+
message: "Surround with Readonly.",
286294
},
287295
],
288296
],
@@ -303,6 +311,8 @@ const errorMessages = {
303311
propertyImmutability:
304312
'Property should have an immutability of at least "{{ expected }}" (actual: "{{ actual }}").',
305313
propertyModifier: "Property should have a readonly modifier.",
314+
propertyModifierSuggestion: "Add readonly modifier.",
315+
userDefined: "{{ message }}",
306316
} as const;
307317

308318
/**
@@ -331,7 +341,7 @@ type Descriptor = RuleResult<
331341

332342
type AllFixers = {
333343
fix: ReportFixFunction | null;
334-
suggestionFixers: ReportFixFunction[] | null;
344+
suggestionFixers: Array<{ fix: ReportFixFunction; message: string }> | null;
335345
};
336346

337347
/**
@@ -383,14 +393,27 @@ function getConfiguredSuggestionFixers(
383393
suggestionsConfigs: SuggestionsConfig[],
384394
) {
385395
return suggestionsConfigs
386-
.map((configs): NonNullable<Descriptor["fix"]> | null => {
387-
const config = configs.find((c) => c.pattern.test(text));
388-
if (config === undefined) {
389-
return null;
390-
}
391-
return (fixer) =>
392-
fixer.replaceText(node, text.replace(config.pattern, config.replace));
393-
})
396+
.map(
397+
(
398+
configs,
399+
): { fix: NonNullable<Descriptor["fix"]>; message: string } | null => {
400+
const config = configs.find((c) => c.pattern.test(text));
401+
if (config === undefined) {
402+
return null;
403+
}
404+
return {
405+
fix: (fixer) =>
406+
fixer.replaceText(
407+
node,
408+
text.replace(config.pattern, config.replace),
409+
),
410+
message:
411+
config.message === undefined
412+
? `Replace with: ${text.replace(config.pattern, config.replace)}`
413+
: text.replace(config.pattern, config.message),
414+
};
415+
},
416+
)
394417
.filter(isDefined);
395418
}
396419

@@ -504,17 +527,16 @@ function getParameterTypeViolations(
504527

505528
const parameterProperty = isTSParameterProperty(param);
506529
if (parameterProperty && !param.readonly) {
507-
const messageId = "propertyModifier";
508530
const fix: NonNullable<Descriptor["fix"]> | null = (fixer) =>
509531
fixer.insertTextBefore(param.parameter, "readonly ");
510532

511533
return {
512534
node: param,
513-
messageId,
535+
messageId: "propertyModifier",
514536
fix: fixerConfigs === false ? null : fix,
515537
suggest: [
516538
{
517-
messageId,
539+
messageId: "propertyModifierSuggestion",
518540
fix,
519541
},
520542
],
@@ -564,21 +586,20 @@ function getParameterTypeViolations(
564586
suggestionsConfigs,
565587
);
566588

567-
const messageId = "parameter";
568-
const data = {
569-
actual: Immutability[immutability],
570-
expected: Immutability[enforcement],
571-
};
572-
573589
return {
574590
node: actualParam,
575-
messageId,
576-
data,
591+
messageId: "parameter",
592+
data: {
593+
actual: Immutability[immutability],
594+
expected: Immutability[enforcement],
595+
},
577596
fix,
578597
suggest:
579-
suggestionFixers?.map((fix) => ({
580-
messageId,
581-
data,
598+
suggestionFixers?.map(({ fix, message }) => ({
599+
messageId: "userDefined",
600+
data: {
601+
message,
602+
},
582603
fix,
583604
})) ?? null,
584605
};
@@ -658,22 +679,21 @@ function getReturnTypeViolations(
658679
suggestionsConfigs,
659680
);
660681

661-
const messageId = "returnType";
662-
const data = {
663-
actual: Immutability[immutability],
664-
expected: Immutability[enforcement],
665-
};
666-
667682
return [
668683
{
669684
node: node.returnType,
670-
messageId,
671-
data,
685+
messageId: "returnType",
686+
data: {
687+
actual: Immutability[immutability],
688+
expected: Immutability[enforcement],
689+
},
672690
fix,
673691
suggest:
674-
suggestionFixers?.map((fix) => ({
675-
messageId,
676-
data,
692+
suggestionFixers?.map(({ fix, message }) => ({
693+
messageId: "userDefined",
694+
data: {
695+
message,
696+
},
677697
fix,
678698
})) ?? null,
679699
},
@@ -713,22 +733,21 @@ function getReturnTypeViolations(
713733
suggestionsConfigs,
714734
);
715735

716-
const messageId = "returnType";
717-
const data = {
718-
actual: Immutability[immutability],
719-
expected: Immutability[enforcement],
720-
};
721-
722736
return [
723737
{
724738
node: hasID(node) && node.id !== null ? node.id : node,
725-
messageId,
726-
data,
739+
messageId: "returnType",
740+
data: {
741+
actual: Immutability[immutability],
742+
expected: Immutability[enforcement],
743+
},
727744
fix,
728745
suggest:
729-
suggestionFixers?.map((fix) => ({
730-
messageId,
731-
data,
746+
suggestionFixers?.map(({ fix, message }) => ({
747+
messageId: "userDefined",
748+
data: {
749+
message,
750+
},
732751
fix,
733752
})) ?? null,
734753
},
@@ -807,17 +826,16 @@ function checkVariable(
807826
const fix: NonNullable<Descriptor["fix"]> | null = (fixer) =>
808827
fixer.insertTextBefore(node.key, "readonly ");
809828

810-
const messageId = "propertyModifier";
811829
return {
812830
context,
813831
descriptors: [
814832
{
815833
node,
816-
messageId,
834+
messageId: "propertyModifier",
817835
fix: rawFixerConfig === undefined ? null : fix,
818836
suggest: [
819837
{
820-
messageId,
838+
messageId: "propertyModifierSuggestion",
821839
fix,
822840
},
823841
],
@@ -912,9 +930,11 @@ function checkVariable(
912930
data,
913931
fix,
914932
suggest:
915-
suggestionFixers?.map((fix) => ({
916-
messageId,
917-
data,
933+
suggestionFixers?.map(({ fix, message }) => ({
934+
messageId: "userDefined",
935+
data: {
936+
message,
937+
},
918938
fix,
919939
})) ?? null,
920940
};

0 commit comments

Comments
 (0)