eslint-plugin-formatjs 4.11.3 → 4.12.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 (40) hide show
  1. package/index.d.ts +3 -23
  2. package/index.js +36 -37
  3. package/package.json +5 -5
  4. package/rules/blocklist-elements.d.ts +17 -3
  5. package/rules/blocklist-elements.js +50 -46
  6. package/rules/enforce-default-message.d.ts +10 -3
  7. package/rules/enforce-default-message.js +21 -9
  8. package/rules/enforce-description.d.ts +10 -3
  9. package/rules/enforce-description.js +19 -6
  10. package/rules/enforce-id.d.ts +10 -3
  11. package/rules/enforce-id.js +53 -24
  12. package/rules/enforce-placeholders.d.ts +8 -3
  13. package/rules/enforce-placeholders.js +18 -7
  14. package/rules/enforce-plural-rules.d.ts +17 -3
  15. package/rules/enforce-plural-rules.js +20 -20
  16. package/rules/no-camel-case.d.ts +6 -3
  17. package/rules/no-camel-case.js +20 -20
  18. package/rules/no-complex-selectors.d.ts +9 -3
  19. package/rules/no-complex-selectors.js +18 -16
  20. package/rules/no-emoji.d.ts +9 -3
  21. package/rules/no-emoji.js +6 -4
  22. package/rules/no-id.d.ts +6 -3
  23. package/rules/no-id.js +11 -4
  24. package/rules/no-invalid-icu.d.ts +6 -3
  25. package/rules/no-invalid-icu.js +12 -5
  26. package/rules/no-literal-string-in-jsx.d.ts +13 -3
  27. package/rules/no-literal-string-in-jsx.js +10 -7
  28. package/rules/no-multiple-plurals.d.ts +6 -3
  29. package/rules/no-multiple-plurals.js +20 -20
  30. package/rules/no-multiple-whitespaces.d.ts +6 -3
  31. package/rules/no-multiple-whitespaces.js +10 -5
  32. package/rules/no-offset.d.ts +6 -3
  33. package/rules/no-offset.js +19 -19
  34. package/rules/no-useless-message.d.ts +6 -3
  35. package/rules/no-useless-message.js +23 -39
  36. package/rules/prefer-formatted-message.d.ts +6 -3
  37. package/rules/prefer-formatted-message.js +5 -3
  38. package/rules/prefer-pound-in-plural.d.ts +6 -3
  39. package/rules/prefer-pound-in-plural.js +10 -4
  40. package/util.d.ts +2 -2
package/index.d.ts CHANGED
@@ -1,24 +1,4 @@
1
- declare const plugin: {
2
- rules: {
3
- 'blocklist-elements': import("eslint").Rule.RuleModule;
4
- 'enforce-default-message': import("eslint").Rule.RuleModule;
5
- 'enforce-description': import("eslint").Rule.RuleModule;
6
- 'enforce-id': import("eslint").Rule.RuleModule;
7
- 'enforce-placeholders': import("eslint").Rule.RuleModule;
8
- 'enforce-plural-rules': import("eslint").Rule.RuleModule;
9
- 'no-camel-case': import("eslint").Rule.RuleModule;
10
- 'no-complex-selectors': import("eslint").Rule.RuleModule;
11
- 'no-emoji': import("eslint").Rule.RuleModule;
12
- 'no-id': import("eslint").Rule.RuleModule;
13
- 'no-invalid-icu': import("eslint").Rule.RuleModule;
14
- 'no-literal-string-in-jsx': import("eslint").Rule.RuleModule;
15
- 'no-multiple-plurals': import("eslint").Rule.RuleModule;
16
- 'no-multiple-whitespaces': import("eslint").Rule.RuleModule;
17
- 'no-offset': import("eslint").Rule.RuleModule;
18
- 'no-useless-message': import("eslint").Rule.RuleModule;
19
- 'prefer-formatted-message': import("eslint").Rule.RuleModule;
20
- 'prefer-pound-in-plural': import("eslint").Rule.RuleModule;
21
- };
1
+ import { RuleModule } from '@typescript-eslint/utils/ts-eslint';
2
+ export type Plugin = {
3
+ rules: Record<string, RuleModule<string, readonly unknown[]>>;
22
4
  };
23
- export type Plugin = typeof plugin;
24
- export {};
package/index.js CHANGED
@@ -1,44 +1,43 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const tslib_1 = require("tslib");
4
- const blocklist_elements_1 = tslib_1.__importDefault(require("./rules/blocklist-elements"));
5
- const enforce_default_message_1 = tslib_1.__importDefault(require("./rules/enforce-default-message"));
6
- const enforce_description_1 = tslib_1.__importDefault(require("./rules/enforce-description"));
7
- const enforce_id_1 = tslib_1.__importDefault(require("./rules/enforce-id"));
8
- const enforce_placeholders_1 = tslib_1.__importDefault(require("./rules/enforce-placeholders"));
9
- const no_invalid_icu_1 = tslib_1.__importDefault(require("./rules/no-invalid-icu"));
10
- const enforce_plural_rules_1 = tslib_1.__importDefault(require("./rules/enforce-plural-rules"));
11
- const no_camel_case_1 = tslib_1.__importDefault(require("./rules/no-camel-case"));
12
- const no_complex_selectors_1 = tslib_1.__importDefault(require("./rules/no-complex-selectors"));
13
- const no_emoji_1 = tslib_1.__importDefault(require("./rules/no-emoji"));
14
- const no_id_1 = tslib_1.__importDefault(require("./rules/no-id"));
15
- const no_multiple_plurals_1 = tslib_1.__importDefault(require("./rules/no-multiple-plurals"));
16
- const no_multiple_whitespaces_1 = tslib_1.__importDefault(require("./rules/no-multiple-whitespaces"));
17
- const no_offset_1 = tslib_1.__importDefault(require("./rules/no-offset"));
18
- const no_literal_string_in_jsx_1 = tslib_1.__importDefault(require("./rules/no-literal-string-in-jsx"));
19
- const no_useless_message_1 = tslib_1.__importDefault(require("./rules/no-useless-message"));
20
- const prefer_formatted_message_1 = tslib_1.__importDefault(require("./rules/prefer-formatted-message"));
21
- const prefer_pound_in_plural_1 = tslib_1.__importDefault(require("./rules/prefer-pound-in-plural"));
3
+ const blocklist_elements_1 = require("./rules/blocklist-elements");
4
+ const enforce_default_message_1 = require("./rules/enforce-default-message");
5
+ const enforce_description_1 = require("./rules/enforce-description");
6
+ const enforce_id_1 = require("./rules/enforce-id");
7
+ const enforce_placeholders_1 = require("./rules/enforce-placeholders");
8
+ const no_invalid_icu_1 = require("./rules/no-invalid-icu");
9
+ const enforce_plural_rules_1 = require("./rules/enforce-plural-rules");
10
+ const no_camel_case_1 = require("./rules/no-camel-case");
11
+ const no_complex_selectors_1 = require("./rules/no-complex-selectors");
12
+ const no_emoji_1 = require("./rules/no-emoji");
13
+ const no_id_1 = require("./rules/no-id");
14
+ const no_multiple_plurals_1 = require("./rules/no-multiple-plurals");
15
+ const no_multiple_whitespaces_1 = require("./rules/no-multiple-whitespaces");
16
+ const no_offset_1 = require("./rules/no-offset");
17
+ const no_literal_string_in_jsx_1 = require("./rules/no-literal-string-in-jsx");
18
+ const no_useless_message_1 = require("./rules/no-useless-message");
19
+ const prefer_formatted_message_1 = require("./rules/prefer-formatted-message");
20
+ const prefer_pound_in_plural_1 = require("./rules/prefer-pound-in-plural");
22
21
  const plugin = {
23
22
  rules: {
24
- 'blocklist-elements': blocklist_elements_1.default,
25
- 'enforce-default-message': enforce_default_message_1.default,
26
- 'enforce-description': enforce_description_1.default,
27
- 'enforce-id': enforce_id_1.default,
28
- 'enforce-placeholders': enforce_placeholders_1.default,
29
- 'enforce-plural-rules': enforce_plural_rules_1.default,
30
- 'no-camel-case': no_camel_case_1.default,
31
- 'no-complex-selectors': no_complex_selectors_1.default,
32
- 'no-emoji': no_emoji_1.default,
33
- 'no-id': no_id_1.default,
34
- 'no-invalid-icu': no_invalid_icu_1.default,
35
- 'no-literal-string-in-jsx': no_literal_string_in_jsx_1.default,
36
- 'no-multiple-plurals': no_multiple_plurals_1.default,
37
- 'no-multiple-whitespaces': no_multiple_whitespaces_1.default,
38
- 'no-offset': no_offset_1.default,
39
- 'no-useless-message': no_useless_message_1.default,
40
- 'prefer-formatted-message': prefer_formatted_message_1.default,
41
- 'prefer-pound-in-plural': prefer_pound_in_plural_1.default,
23
+ [blocklist_elements_1.name]: blocklist_elements_1.rule,
24
+ [enforce_default_message_1.name]: enforce_default_message_1.rule,
25
+ [enforce_description_1.name]: enforce_description_1.rule,
26
+ [enforce_id_1.name]: enforce_id_1.rule,
27
+ [enforce_placeholders_1.name]: enforce_placeholders_1.rule,
28
+ [enforce_plural_rules_1.name]: enforce_plural_rules_1.rule,
29
+ [no_camel_case_1.name]: no_camel_case_1.rule,
30
+ [no_complex_selectors_1.name]: no_complex_selectors_1.rule,
31
+ [no_emoji_1.name]: no_emoji_1.rule,
32
+ [no_id_1.name]: no_id_1.rule,
33
+ [no_invalid_icu_1.name]: no_invalid_icu_1.rule,
34
+ [no_literal_string_in_jsx_1.name]: no_literal_string_in_jsx_1.rule,
35
+ [no_multiple_plurals_1.name]: no_multiple_plurals_1.rule,
36
+ [no_multiple_whitespaces_1.name]: no_multiple_whitespaces_1.rule,
37
+ [no_offset_1.name]: no_offset_1.rule,
38
+ [no_useless_message_1.name]: no_useless_message_1.rule,
39
+ [prefer_formatted_message_1.name]: prefer_formatted_message_1.rule,
40
+ [prefer_pound_in_plural_1.name]: prefer_pound_in_plural_1.rule,
42
41
  },
43
42
  };
44
43
  module.exports = plugin;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-formatjs",
3
- "version": "4.11.3",
3
+ "version": "4.12.1",
4
4
  "description": "ESLint plugin for formatjs",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -22,15 +22,15 @@
22
22
  "dependencies": {
23
23
  "@types/eslint": "7 || 8",
24
24
  "@types/picomatch": "^2.3.0",
25
- "@typescript-eslint/utils": "^6.5.0",
25
+ "@typescript-eslint/utils": "^6.18.1",
26
26
  "emoji-regex": "^10.2.1",
27
27
  "magic-string": "^0.30.0",
28
28
  "picomatch": "^2.3.1",
29
29
  "tslib": "2.6.2",
30
30
  "typescript": "5",
31
- "unicode-emoji-utils": "^1.1.1",
32
- "@formatjs/icu-messageformat-parser": "2.7.3",
33
- "@formatjs/ts-transformer": "3.13.9"
31
+ "unicode-emoji-utils": "^1.2.0",
32
+ "@formatjs/icu-messageformat-parser": "2.7.5",
33
+ "@formatjs/ts-transformer": "3.13.11"
34
34
  },
35
35
  "peerDependencies": {
36
36
  "eslint": "7 || 8"
@@ -1,3 +1,17 @@
1
- import { Rule } from 'eslint';
2
- declare const rule: Rule.RuleModule;
3
- export default rule;
1
+ import { RuleModule, RuleListener } from '@typescript-eslint/utils/ts-eslint';
2
+ type MessageIds = 'blocklist';
3
+ type Options = [Element[]?];
4
+ export declare const name = "blocklist-elements";
5
+ export declare enum Element {
6
+ literal = "literal",
7
+ argument = "argument",
8
+ number = "number",
9
+ date = "date",
10
+ time = "time",
11
+ select = "select",
12
+ selectordinal = "selectordinal",
13
+ plural = "plural",
14
+ tag = "tag"
15
+ }
16
+ export declare const rule: RuleModule<MessageIds, Options, RuleListener>;
17
+ export {};
@@ -1,12 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rule = exports.Element = exports.name = void 0;
3
4
  const icu_messageformat_parser_1 = require("@formatjs/icu-messageformat-parser");
4
5
  const util_1 = require("../util");
5
- class BlacklistElement extends Error {
6
- constructor(type) {
7
- super();
8
- this.message = `${type} element is blocklisted`;
9
- }
6
+ exports.name = 'blocklist-elements';
7
+ function getMessage(type) {
8
+ return {
9
+ messageId: 'blocklist',
10
+ data: { type },
11
+ };
10
12
  }
11
13
  var Element;
12
14
  (function (Element) {
@@ -19,37 +21,38 @@ var Element;
19
21
  Element["selectordinal"] = "selectordinal";
20
22
  Element["plural"] = "plural";
21
23
  Element["tag"] = "tag";
22
- })(Element || (Element = {}));
24
+ })(Element || (exports.Element = Element = {}));
23
25
  function verifyAst(blocklist, ast) {
26
+ const errors = [];
24
27
  for (const el of ast) {
25
28
  if ((0, icu_messageformat_parser_1.isLiteralElement)(el) && blocklist.includes(Element.literal)) {
26
- throw new BlacklistElement(Element.literal);
29
+ errors.push(getMessage(Element.literal));
27
30
  }
28
31
  if ((0, icu_messageformat_parser_1.isArgumentElement)(el) && blocklist.includes(Element.argument)) {
29
- throw new BlacklistElement(Element.argument);
32
+ errors.push(getMessage(Element.argument));
30
33
  }
31
34
  if ((0, icu_messageformat_parser_1.isNumberElement)(el) && blocklist.includes(Element.number)) {
32
- throw new BlacklistElement(Element.number);
35
+ errors.push(getMessage(Element.number));
33
36
  }
34
37
  if ((0, icu_messageformat_parser_1.isDateElement)(el) && blocklist.includes(Element.date)) {
35
- throw new BlacklistElement(Element.date);
38
+ errors.push(getMessage(Element.date));
36
39
  }
37
40
  if ((0, icu_messageformat_parser_1.isTimeElement)(el) && blocklist.includes(Element.time)) {
38
- throw new BlacklistElement(Element.time);
41
+ errors.push(getMessage(Element.time));
39
42
  }
40
43
  if ((0, icu_messageformat_parser_1.isSelectElement)(el) && blocklist.includes(Element.select)) {
41
- throw new BlacklistElement(Element.select);
44
+ errors.push(getMessage(Element.select));
42
45
  }
43
46
  if ((0, icu_messageformat_parser_1.isTagElement)(el) && blocklist.includes(Element.tag)) {
44
- throw new BlacklistElement(Element.tag);
47
+ errors.push(getMessage(Element.tag));
45
48
  }
46
49
  if ((0, icu_messageformat_parser_1.isPluralElement)(el)) {
47
50
  if (blocklist.includes(Element.plural)) {
48
- throw new BlacklistElement(Element.argument);
51
+ errors.push(getMessage(Element.argument));
49
52
  }
50
53
  if (el.pluralType === 'ordinal' &&
51
54
  blocklist.includes(Element.selectordinal)) {
52
- throw new BlacklistElement(Element.selectordinal);
55
+ errors.push(getMessage(Element.selectordinal));
53
56
  }
54
57
  }
55
58
  if ((0, icu_messageformat_parser_1.isSelectElement)(el) || (0, icu_messageformat_parser_1.isPluralElement)(el)) {
@@ -59,6 +62,7 @@ function verifyAst(blocklist, ast) {
59
62
  }
60
63
  }
61
64
  }
65
+ return errors;
62
66
  }
63
67
  function checkNode(context, node) {
64
68
  const settings = (0, util_1.getSettings)(context);
@@ -74,54 +78,54 @@ function checkNode(context, node) {
74
78
  if (!defaultMessage || !messageNode) {
75
79
  continue;
76
80
  }
77
- try {
78
- verifyAst(context.options[0], (0, icu_messageformat_parser_1.parse)(defaultMessage, {
79
- ignoreTag: settings.ignoreTag,
80
- }));
81
- }
82
- catch (e) {
81
+ const errors = verifyAst(blocklist, (0, icu_messageformat_parser_1.parse)(defaultMessage, {
82
+ ignoreTag: settings.ignoreTag,
83
+ }));
84
+ for (const error of errors) {
83
85
  context.report({
84
- node: messageNode,
85
- message: e instanceof Error ? e.message : String(e),
86
+ node,
87
+ ...error,
86
88
  });
87
89
  }
88
90
  }
89
91
  }
90
- const rule = {
92
+ const create = (context) => {
93
+ const callExpressionVisitor = (node) => checkNode(context, node);
94
+ //@ts-expect-error defineTemplateBodyVisitor exists in Vue parser
95
+ if (context.parserServices?.defineTemplateBodyVisitor) {
96
+ //@ts-expect-error
97
+ return context.parserServices.defineTemplateBodyVisitor({
98
+ CallExpression: callExpressionVisitor,
99
+ }, {
100
+ CallExpression: callExpressionVisitor,
101
+ });
102
+ }
103
+ return {
104
+ JSXOpeningElement: (node) => checkNode(context, node),
105
+ CallExpression: callExpressionVisitor,
106
+ };
107
+ };
108
+ exports.rule = {
91
109
  meta: {
92
110
  type: 'problem',
93
111
  docs: {
94
112
  description: 'Disallow specific elements in ICU message format',
95
- category: 'Errors',
96
- recommended: false,
97
113
  url: 'https://formatjs.io/docs/tooling/linter#blocklist-elements',
98
114
  },
99
115
  fixable: 'code',
100
116
  schema: [
101
117
  {
102
118
  type: 'array',
103
- properties: {
104
- items: {
105
- type: 'string',
106
- enum: Object.keys(Element),
107
- },
119
+ items: {
120
+ type: 'string',
121
+ enum: Object.keys(Element),
108
122
  },
109
123
  },
110
124
  ],
125
+ messages: {
126
+ blocklist: `{{type}} element is blocklisted`,
127
+ },
111
128
  },
112
- create(context) {
113
- const callExpressionVisitor = (node) => checkNode(context, node);
114
- if (context.parserServices.defineTemplateBodyVisitor) {
115
- return context.parserServices.defineTemplateBodyVisitor({
116
- CallExpression: callExpressionVisitor,
117
- }, {
118
- CallExpression: callExpressionVisitor,
119
- });
120
- }
121
- return {
122
- JSXOpeningElement: (node) => checkNode(context, node),
123
- CallExpression: callExpressionVisitor,
124
- };
125
- },
129
+ defaultOptions: [],
130
+ create,
126
131
  };
127
- exports.default = rule;
@@ -1,3 +1,10 @@
1
- import { Rule } from 'eslint';
2
- declare const rule: Rule.RuleModule;
3
- export default rule;
1
+ import { RuleModule, RuleListener } from '@typescript-eslint/utils/ts-eslint';
2
+ export declare enum Option {
3
+ literal = "literal",
4
+ anything = "anything"
5
+ }
6
+ type MessageIds = 'defaultMessage' | 'defaultMessageLiteral';
7
+ type Options = [`${Option}`?];
8
+ export declare const name = "enforce-default-message";
9
+ export declare const rule: RuleModule<MessageIds, Options, RuleListener>;
10
+ export {};
@@ -1,6 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rule = exports.name = exports.Option = void 0;
3
4
  const util_1 = require("../util");
5
+ var Option;
6
+ (function (Option) {
7
+ Option["literal"] = "literal";
8
+ Option["anything"] = "anything";
9
+ })(Option || (exports.Option = Option = {}));
10
+ exports.name = 'enforce-default-message';
4
11
  function checkNode(context, node) {
5
12
  const msgs = (0, util_1.extractMessages)(node, (0, util_1.getSettings)(context));
6
13
  const { options: [type], } = context;
@@ -9,39 +16,45 @@ function checkNode(context, node) {
9
16
  if (type === 'literal' && messageNode) {
10
17
  context.report({
11
18
  node: messageNode,
12
- message: `"defaultMessage" must be:
13
- - a string literal or
14
- - template literal without variable`,
19
+ messageId: 'defaultMessageLiteral',
15
20
  });
16
21
  }
17
22
  else if (!messageNode) {
18
23
  context.report({
19
24
  node: node,
20
- message: '`defaultMessage` has to be specified in message descriptor',
25
+ messageId: 'defaultMessage',
21
26
  });
22
27
  }
23
28
  }
24
29
  }
25
30
  }
26
- const rule = {
31
+ exports.rule = {
27
32
  meta: {
28
33
  type: 'problem',
29
34
  docs: {
30
35
  description: 'Enforce defaultMessage in message descriptor',
31
- category: 'Errors',
32
- recommended: false,
33
36
  url: 'https://formatjs.io/docs/tooling/linter#enforce-default-message',
34
37
  },
35
38
  fixable: 'code',
36
39
  schema: [
37
40
  {
38
- enum: ['literal', 'anything'],
41
+ type: 'string',
42
+ enum: Object.keys(Option),
39
43
  },
40
44
  ],
45
+ messages: {
46
+ defaultMessageLiteral: `"defaultMessage" must be:
47
+ - a string literal or
48
+ - template literal without variable`,
49
+ defaultMessage: '`defaultMessage` has to be specified in message descriptor',
50
+ },
41
51
  },
52
+ defaultOptions: [],
42
53
  create(context) {
43
54
  const callExpressionVisitor = (node) => checkNode(context, node);
55
+ //@ts-expect-error defineTemplateBodyVisitor exists in Vue parser
44
56
  if (context.parserServices.defineTemplateBodyVisitor) {
57
+ //@ts-expect-error
45
58
  return context.parserServices.defineTemplateBodyVisitor({
46
59
  CallExpression: callExpressionVisitor,
47
60
  }, {
@@ -54,4 +67,3 @@ const rule = {
54
67
  };
55
68
  },
56
69
  };
57
- exports.default = rule;
@@ -1,3 +1,10 @@
1
- import { Rule } from 'eslint';
2
- declare const _default: Rule.RuleModule;
3
- export default _default;
1
+ import { RuleModule, RuleListener } from '@typescript-eslint/utils/ts-eslint';
2
+ export declare enum Option {
3
+ literal = "literal",
4
+ anything = "anything"
5
+ }
6
+ type MessageIds = 'enforceDescription' | 'enforceDescriptionLiteral';
7
+ type Options = [`${Option}`?];
8
+ export declare const name = "enforce-description";
9
+ export declare const rule: RuleModule<MessageIds, Options, RuleListener>;
10
+ export {};
@@ -1,6 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rule = exports.name = exports.Option = void 0;
3
4
  const util_1 = require("../util");
5
+ var Option;
6
+ (function (Option) {
7
+ Option["literal"] = "literal";
8
+ Option["anything"] = "anything";
9
+ })(Option || (exports.Option = Option = {}));
4
10
  function checkNode(context, node) {
5
11
  const msgs = (0, util_1.extractMessages)(node, (0, util_1.getSettings)(context));
6
12
  const { options: [type], } = context;
@@ -9,37 +15,44 @@ function checkNode(context, node) {
9
15
  if (type === 'literal' && descriptionNode) {
10
16
  context.report({
11
17
  node: descriptionNode,
12
- message: '`description` has to be a string literal (not function call or variable)',
18
+ messageId: 'enforceDescriptionLiteral',
13
19
  });
14
20
  }
15
21
  else if (!descriptionNode) {
16
22
  context.report({
17
23
  node: node,
18
- message: '`description` has to be specified in message descriptor',
24
+ messageId: 'enforceDescription',
19
25
  });
20
26
  }
21
27
  }
22
28
  }
23
29
  }
24
- exports.default = {
30
+ exports.name = 'enforce-description';
31
+ exports.rule = {
25
32
  meta: {
26
33
  type: 'problem',
27
34
  docs: {
28
35
  description: 'Enforce description in message descriptor',
29
- category: 'Errors',
30
- recommended: false,
31
36
  url: 'https://formatjs.io/docs/tooling/linter#enforce-description',
32
37
  },
33
38
  fixable: 'code',
34
39
  schema: [
35
40
  {
36
- enum: ['literal', 'anything'],
41
+ type: 'string',
42
+ enum: Object.keys(Option),
37
43
  },
38
44
  ],
45
+ messages: {
46
+ enforceDescription: '`description` has to be specified in message descriptor',
47
+ enforceDescriptionLiteral: '`description` has to be a string literal (not function call or variable)',
48
+ },
39
49
  },
50
+ defaultOptions: [],
40
51
  create(context) {
41
52
  const callExpressionVisitor = (node) => checkNode(context, node);
53
+ //@ts-expect-error defineTemplateBodyVisitor exists in Vue parser
42
54
  if (context.parserServices.defineTemplateBodyVisitor) {
55
+ //@ts-expect-error
43
56
  return context.parserServices.defineTemplateBodyVisitor({
44
57
  CallExpression: callExpressionVisitor,
45
58
  }, {
@@ -1,3 +1,10 @@
1
- import { Rule } from 'eslint';
2
- declare const _default: Rule.RuleModule;
3
- export default _default;
1
+ import { RuleModule, RuleListener } from '@typescript-eslint/utils/ts-eslint';
2
+ export type Option = {
3
+ idInterpolationPattern: string;
4
+ idWhitelist?: string[];
5
+ };
6
+ type MessageIds = 'enforceId' | 'enforceIdDefaultMessage' | 'enforceIdDescription' | 'enforceIdMatching' | 'enforceIdMatchingAllowlisted';
7
+ type Options = [Option];
8
+ export declare const name = "enforce-id";
9
+ export declare const rule: RuleModule<MessageIds, Options, RuleListener>;
10
+ export {};
@@ -1,27 +1,28 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rule = exports.name = void 0;
3
4
  const ts_transformer_1 = require("@formatjs/ts-transformer");
4
5
  const util_1 = require("../util");
5
- function checkNode(context, node, { idInterpolationPattern, idWhitelistRegexps }) {
6
+ function checkNode(context, node, { idInterpolationPattern, idWhitelistRegexps, }) {
6
7
  const msgs = (0, util_1.extractMessages)(node, (0, util_1.getSettings)(context));
7
8
  for (const [{ message: { defaultMessage, description, id }, idPropNode, descriptionNode, messagePropNode, },] of msgs) {
8
9
  if (!idInterpolationPattern && !idPropNode) {
9
10
  context.report({
10
- node: node,
11
- message: `id must be specified`,
11
+ node,
12
+ messageId: 'enforceId',
12
13
  });
13
14
  }
14
15
  else if (idInterpolationPattern) {
15
16
  if (!defaultMessage) {
16
17
  context.report({
17
- node: node,
18
- message: `defaultMessage must be a string literal to calculate generated IDs`,
18
+ node,
19
+ messageId: 'enforceIdDefaultMessage',
19
20
  });
20
21
  }
21
22
  else if (!description && descriptionNode) {
22
23
  context.report({
23
- node: node,
24
- message: `description must be a string literal to calculate generated IDs`,
24
+ node,
25
+ messageId: 'enforceIdDescription',
25
26
  });
26
27
  }
27
28
  else {
@@ -39,17 +40,25 @@ function checkNode(context, node, { idInterpolationPattern, idWhitelistRegexps }
39
40
  : defaultMessage,
40
41
  });
41
42
  if (id !== correctId) {
42
- let message = `"id" does not match with hash pattern ${idInterpolationPattern}`;
43
+ let messageId = 'enforceIdMatching';
44
+ let messageData = {
45
+ idInterpolationPattern,
46
+ expected: correctId,
47
+ actual: id,
48
+ };
43
49
  if (idWhitelistRegexps) {
44
- message += ` or allowlisted patterns ["${idWhitelistRegexps
45
- .map(r => r.toString())
46
- .join('", "')}"]`;
50
+ messageId = 'enforceIdMatchingAllowlisted';
51
+ messageData = {
52
+ ...messageData,
53
+ idWhitelist: idWhitelistRegexps
54
+ .map(r => `"${r.toString()}"`)
55
+ .join(', '),
56
+ };
47
57
  }
48
58
  context.report({
49
- node: node,
50
- message: `${message}.
51
- Expected: ${correctId}
52
- Actual: ${id}`,
59
+ node,
60
+ messageId,
61
+ data: messageData,
53
62
  fix(fixer) {
54
63
  if (idPropNode) {
55
64
  if (idPropNode.type === 'JSXAttribute') {
@@ -57,11 +66,14 @@ Actual: ${id}`,
57
66
  }
58
67
  return fixer.replaceText(idPropNode, `id: '${correctId}'`);
59
68
  }
60
- // Insert after default message node
61
- if (messagePropNode.type === 'JSXAttribute') {
62
- return fixer.insertTextAfter(messagePropNode, ` id="${correctId}"`);
69
+ if (messagePropNode) {
70
+ // Insert after default message node
71
+ if (messagePropNode.type === 'JSXAttribute') {
72
+ return fixer.insertTextAfter(messagePropNode, ` id="${correctId}"`);
73
+ }
74
+ return fixer.insertTextAfter(messagePropNode, `, id: '${correctId}'`);
63
75
  }
64
- return fixer.insertTextAfter(messagePropNode, `, id: '${correctId}'`);
76
+ return null;
65
77
  },
66
78
  });
67
79
  }
@@ -69,13 +81,12 @@ Actual: ${id}`,
69
81
  }
70
82
  }
71
83
  }
72
- exports.default = {
84
+ exports.name = 'enforce-id';
85
+ exports.rule = {
73
86
  meta: {
74
87
  type: 'problem',
75
88
  docs: {
76
89
  description: 'Enforce (generated) ID in message descriptor',
77
- category: 'Errors',
78
- recommended: false,
79
90
  url: 'https://formatjs.io/docs/tooling/linter#enforce-id',
80
91
  },
81
92
  fixable: 'code',
@@ -99,10 +110,26 @@ exports.default = {
99
110
  additionalProperties: false,
100
111
  },
101
112
  ],
113
+ messages: {
114
+ enforceId: `id must be specified`,
115
+ enforceIdDefaultMessage: `defaultMessage must be a string literal to calculate generated IDs`,
116
+ enforceIdDescription: `description must be a string literal to calculate generated IDs`,
117
+ enforceIdMatching: `"id" does not match with hash pattern {{idInterpolationPattern}}.
118
+ Expected: {{expected}}
119
+ Actual: {{actual}}`,
120
+ enforceIdMatchingAllowlisted: `"id" does not match with hash pattern {{idInterpolationPattern}} or allowlisted patterns {{idWhitelist}}.
121
+ Expected: {{expected}}
122
+ Actual: {{actual}}`,
123
+ },
102
124
  },
125
+ defaultOptions: [
126
+ {
127
+ idInterpolationPattern: '[sha512:contenthash:base64:6]',
128
+ },
129
+ ],
103
130
  create(context) {
104
- const tmp = context?.options?.[0];
105
- const opts = {
131
+ const tmp = context.options[0];
132
+ let opts = {
106
133
  idInterpolationPattern: tmp?.idInterpolationPattern,
107
134
  };
108
135
  if (Array.isArray(tmp?.idWhitelist)) {
@@ -110,7 +137,9 @@ exports.default = {
110
137
  opts.idWhitelistRegexps = idWhitelist.map((str) => new RegExp(str, 'i'));
111
138
  }
112
139
  const callExpressionVisitor = (node) => checkNode(context, node, opts);
140
+ //@ts-expect-error defineTemplateBodyVisitor exists in Vue parser
113
141
  if (context.parserServices.defineTemplateBodyVisitor) {
142
+ //@ts-expect-error
114
143
  return context.parserServices.defineTemplateBodyVisitor({
115
144
  CallExpression: callExpressionVisitor,
116
145
  }, {