eslint-plugin-formatjs 4.2.0 → 4.2.2

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 (88) hide show
  1. package/index.d.ts +22 -0
  2. package/index.d.ts.map +1 -0
  3. package/index.js +38 -0
  4. package/package.json +3 -3
  5. package/rules/blocklist-elements.d.ts +4 -0
  6. package/rules/blocklist-elements.d.ts.map +1 -0
  7. package/rules/blocklist-elements.js +127 -0
  8. package/rules/enforce-default-message.d.ts +4 -0
  9. package/rules/enforce-default-message.d.ts.map +1 -0
  10. package/rules/enforce-default-message.js +57 -0
  11. package/rules/enforce-description.d.ts +4 -0
  12. package/rules/enforce-description.d.ts.map +1 -0
  13. package/rules/enforce-description.js +54 -0
  14. package/rules/enforce-id.d.ts +4 -0
  15. package/rules/enforce-id.d.ts.map +1 -0
  16. package/rules/enforce-id.js +125 -0
  17. package/rules/enforce-placeholders.d.ts +4 -0
  18. package/rules/enforce-placeholders.d.ts.map +1 -0
  19. package/rules/enforce-placeholders.js +118 -0
  20. package/rules/enforce-plural-rules.d.ts +4 -0
  21. package/rules/enforce-plural-rules.d.ts.map +1 -0
  22. package/rules/enforce-plural-rules.js +104 -0
  23. package/rules/no-camel-case.d.ts +4 -0
  24. package/rules/no-camel-case.d.ts.map +1 -0
  25. package/rules/no-camel-case.js +77 -0
  26. package/rules/no-complex-selectors.d.ts +4 -0
  27. package/rules/no-complex-selectors.d.ts.map +1 -0
  28. package/rules/no-complex-selectors.js +99 -0
  29. package/rules/no-emoji.d.ts +4 -0
  30. package/rules/no-emoji.d.ts.map +1 -0
  31. package/rules/no-emoji.js +47 -0
  32. package/rules/no-id.d.ts +4 -0
  33. package/rules/no-id.d.ts.map +1 -0
  34. package/rules/no-id.js +52 -0
  35. package/rules/no-invalid-icu.d.ts +4 -0
  36. package/rules/no-invalid-icu.d.ts.map +1 -0
  37. package/rules/no-invalid-icu.js +54 -0
  38. package/rules/no-literal-string-in-jsx.d.ts +4 -0
  39. package/rules/no-literal-string-in-jsx.d.ts.map +1 -0
  40. package/rules/no-literal-string-in-jsx.js +166 -0
  41. package/rules/no-multiple-plurals.d.ts +4 -0
  42. package/rules/no-multiple-plurals.d.ts.map +1 -0
  43. package/rules/no-multiple-plurals.js +71 -0
  44. package/rules/no-multiple-whitespaces.d.ts +4 -0
  45. package/rules/no-multiple-whitespaces.d.ts.map +1 -0
  46. package/rules/no-multiple-whitespaces.js +146 -0
  47. package/rules/no-offset.d.ts +4 -0
  48. package/rules/no-offset.d.ts.map +1 -0
  49. package/rules/no-offset.js +70 -0
  50. package/util.d.ts +24 -0
  51. package/util.d.ts.map +1 -0
  52. package/util.js +240 -0
  53. package/BUILD +0 -89
  54. package/CHANGELOG.md +0 -892
  55. package/index.ts +0 -38
  56. package/rules/blocklist-elements.ts +0 -159
  57. package/rules/enforce-default-message.ts +0 -71
  58. package/rules/enforce-description.ts +0 -68
  59. package/rules/enforce-id.ts +0 -171
  60. package/rules/enforce-placeholders.ts +0 -161
  61. package/rules/enforce-plural-rules.ts +0 -134
  62. package/rules/no-camel-case.ts +0 -97
  63. package/rules/no-complex-selectors.ts +0 -125
  64. package/rules/no-emoji.ts +0 -60
  65. package/rules/no-id.ts +0 -63
  66. package/rules/no-invalid-icu.ts +0 -69
  67. package/rules/no-literal-string-in-jsx.ts +0 -213
  68. package/rules/no-multiple-plurals.ts +0 -89
  69. package/rules/no-multiple-whitespaces.ts +0 -194
  70. package/rules/no-offset.ts +0 -88
  71. package/tests/blocklist-elements.test.ts +0 -120
  72. package/tests/enforce-default-message.test.ts +0 -260
  73. package/tests/enforce-description.test.ts +0 -117
  74. package/tests/enforce-id.test.ts +0 -209
  75. package/tests/enforce-placeholders.test.ts +0 -170
  76. package/tests/enforce-plural-rules.test.ts +0 -86
  77. package/tests/fixtures.ts +0 -15
  78. package/tests/no-camel-case.test.ts +0 -31
  79. package/tests/no-complex-selectors.test.ts +0 -125
  80. package/tests/no-id.test.ts +0 -151
  81. package/tests/no-invalid-icu.test.ts +0 -106
  82. package/tests/no-literal-string-in-jsx.test.ts +0 -213
  83. package/tests/no-multiple-plurals.test.ts +0 -42
  84. package/tests/no-multiple-whitespaces.test.ts +0 -100
  85. package/tests/no-offset.test.ts +0 -41
  86. package/tests/util.ts +0 -26
  87. package/tsconfig.json +0 -5
  88. package/util.ts +0 -307
package/index.ts DELETED
@@ -1,38 +0,0 @@
1
- import blocklistElements from './rules/blocklist-elements'
2
- import enforceDefaultMessage from './rules/enforce-default-message'
3
- import enforceDescription from './rules/enforce-description'
4
- import enforceId from './rules/enforce-id'
5
- import enforcePlaceholders from './rules/enforce-placeholders'
6
- import noInvalidICU from './rules/no-invalid-icu'
7
- import enforcePluralRules from './rules/enforce-plural-rules'
8
- import noCamelCase from './rules/no-camel-case'
9
- import noComplexSelectors from './rules/no-complex-selectors'
10
- import noEmoji from './rules/no-emoji'
11
- import noId from './rules/no-id'
12
- import noMultiplePlurals from './rules/no-multiple-plurals'
13
- import noMultipleWhitespaces from './rules/no-multiple-whitespaces'
14
- import noOffset from './rules/no-offset'
15
- import noLiteralStringInJsx from './rules/no-literal-string-in-jsx'
16
- const plugin = {
17
- rules: {
18
- 'blocklist-elements': blocklistElements,
19
- 'enforce-default-message': enforceDefaultMessage,
20
- 'enforce-description': enforceDescription,
21
- 'enforce-id': enforceId,
22
- 'enforce-placeholders': enforcePlaceholders,
23
- 'enforce-plural-rules': enforcePluralRules,
24
- 'no-camel-case': noCamelCase,
25
- 'no-complex-selectors': noComplexSelectors,
26
- 'no-emoji': noEmoji,
27
- 'no-id': noId,
28
- 'no-literal-string-in-jsx': noLiteralStringInJsx,
29
- 'no-multiple-plurals': noMultiplePlurals,
30
- 'no-multiple-whitespaces': noMultipleWhitespaces,
31
- 'no-invalid-icu': noInvalidICU,
32
- 'no-offset': noOffset,
33
- },
34
- }
35
-
36
- export type Plugin = typeof plugin
37
-
38
- module.exports = plugin
@@ -1,159 +0,0 @@
1
- import {Rule} from 'eslint'
2
- import {extractMessages, getSettings} from '../util'
3
- import {
4
- parse,
5
- isPluralElement,
6
- MessageFormatElement,
7
- isLiteralElement,
8
- isArgumentElement,
9
- isNumberElement,
10
- isDateElement,
11
- isTimeElement,
12
- isSelectElement,
13
- isTagElement,
14
- } from '@formatjs/icu-messageformat-parser'
15
- import {TSESTree} from '@typescript-eslint/typescript-estree'
16
-
17
- class BlacklistElement extends Error {
18
- public message: string
19
- constructor(type: Element) {
20
- super()
21
- this.message = `${type} element is blocklisted`
22
- }
23
- }
24
-
25
- enum Element {
26
- literal = 'literal',
27
- argument = 'argument',
28
- number = 'number',
29
- date = 'date',
30
- time = 'time',
31
- select = 'select',
32
- selectordinal = 'selectordinal',
33
- plural = 'plural',
34
- tag = 'tag',
35
- }
36
-
37
- function verifyAst(blocklist: Element[], ast: MessageFormatElement[]) {
38
- for (const el of ast) {
39
- if (isLiteralElement(el) && blocklist.includes(Element.literal)) {
40
- throw new BlacklistElement(Element.literal)
41
- }
42
- if (isArgumentElement(el) && blocklist.includes(Element.argument)) {
43
- throw new BlacklistElement(Element.argument)
44
- }
45
- if (isNumberElement(el) && blocklist.includes(Element.number)) {
46
- throw new BlacklistElement(Element.number)
47
- }
48
- if (isDateElement(el) && blocklist.includes(Element.date)) {
49
- throw new BlacklistElement(Element.date)
50
- }
51
- if (isTimeElement(el) && blocklist.includes(Element.time)) {
52
- throw new BlacklistElement(Element.time)
53
- }
54
- if (isSelectElement(el) && blocklist.includes(Element.select)) {
55
- throw new BlacklistElement(Element.select)
56
- }
57
- if (isTagElement(el) && blocklist.includes(Element.tag)) {
58
- throw new BlacklistElement(Element.tag)
59
- }
60
- if (isPluralElement(el)) {
61
- if (blocklist.includes(Element.plural)) {
62
- throw new BlacklistElement(Element.argument)
63
- }
64
- if (
65
- el.pluralType === 'ordinal' &&
66
- blocklist.includes(Element.selectordinal)
67
- ) {
68
- throw new BlacklistElement(Element.selectordinal)
69
- }
70
- }
71
- if (isSelectElement(el) || isPluralElement(el)) {
72
- const {options} = el
73
- for (const selector of Object.keys(options)) {
74
- verifyAst(blocklist, options[selector].value)
75
- }
76
- }
77
- }
78
- }
79
-
80
- function checkNode(context: Rule.RuleContext, node: TSESTree.Node) {
81
- const settings = getSettings(context)
82
- const msgs = extractMessages(node, settings)
83
- if (!msgs.length) {
84
- return
85
- }
86
-
87
- const blocklist = context.options[0]
88
- if (!Array.isArray(blocklist) || !blocklist.length) {
89
- return
90
- }
91
- for (const [
92
- {
93
- message: {defaultMessage},
94
- messageNode,
95
- },
96
- ] of msgs) {
97
- if (!defaultMessage || !messageNode) {
98
- continue
99
- }
100
- try {
101
- verifyAst(
102
- context.options[0],
103
- parse(defaultMessage, {
104
- ignoreTag: settings.ignoreTag,
105
- })
106
- )
107
- } catch (e) {
108
- context.report({
109
- node: messageNode as any,
110
- message: e instanceof Error ? e.message : String(e),
111
- })
112
- }
113
- }
114
- }
115
-
116
- const rule: Rule.RuleModule = {
117
- meta: {
118
- type: 'problem',
119
- docs: {
120
- description: 'Disallow specific elements in ICU message format',
121
- category: 'Errors',
122
- recommended: false,
123
- url: 'https://formatjs.io/docs/tooling/linter#blocklist-elements',
124
- },
125
- fixable: 'code',
126
- schema: [
127
- {
128
- type: 'array',
129
- properties: {
130
- items: {
131
- type: 'string',
132
- enum: Object.keys(Element),
133
- },
134
- },
135
- },
136
- ],
137
- },
138
- create(context) {
139
- const callExpressionVisitor = (node: TSESTree.Node) =>
140
- checkNode(context, node)
141
-
142
- if (context.parserServices.defineTemplateBodyVisitor) {
143
- return context.parserServices.defineTemplateBodyVisitor(
144
- {
145
- CallExpression: callExpressionVisitor,
146
- },
147
- {
148
- CallExpression: callExpressionVisitor,
149
- }
150
- )
151
- }
152
- return {
153
- JSXOpeningElement: (node: TSESTree.Node) => checkNode(context, node),
154
- CallExpression: callExpressionVisitor,
155
- }
156
- },
157
- }
158
-
159
- export default rule
@@ -1,71 +0,0 @@
1
- import {Rule} from 'eslint'
2
- import {extractMessages, getSettings} from '../util'
3
- import {TSESTree} from '@typescript-eslint/typescript-estree'
4
-
5
- function checkNode(context: Rule.RuleContext, node: TSESTree.Node) {
6
- const msgs = extractMessages(node, getSettings(context))
7
- const {
8
- options: [type],
9
- } = context
10
- for (const [
11
- {
12
- message: {defaultMessage},
13
- messageNode,
14
- },
15
- ] of msgs) {
16
- if (!defaultMessage) {
17
- if (type === 'literal' && messageNode) {
18
- context.report({
19
- node: messageNode as any,
20
- message: `"defaultMessage" must be:
21
- - a string literal or
22
- - template literal without variable`,
23
- })
24
- } else if (!messageNode) {
25
- context.report({
26
- node: node as any,
27
- message: '`defaultMessage` has to be specified in message descriptor',
28
- })
29
- }
30
- }
31
- }
32
- }
33
-
34
- const rule: Rule.RuleModule = {
35
- meta: {
36
- type: 'problem',
37
- docs: {
38
- description: 'Enforce defaultMessage in message descriptor',
39
- category: 'Errors',
40
- recommended: false,
41
- url: 'https://formatjs.io/docs/tooling/linter#enforce-default-message',
42
- },
43
- fixable: 'code',
44
- schema: [
45
- {
46
- enum: ['literal', 'anything'],
47
- },
48
- ],
49
- },
50
- create(context) {
51
- const callExpressionVisitor = (node: TSESTree.Node) =>
52
- checkNode(context, node)
53
-
54
- if (context.parserServices.defineTemplateBodyVisitor) {
55
- return context.parserServices.defineTemplateBodyVisitor(
56
- {
57
- CallExpression: callExpressionVisitor,
58
- },
59
- {
60
- CallExpression: callExpressionVisitor,
61
- }
62
- )
63
- }
64
- return {
65
- JSXOpeningElement: (node: TSESTree.Node) => checkNode(context, node),
66
- CallExpression: callExpressionVisitor,
67
- }
68
- },
69
- }
70
-
71
- export default rule
@@ -1,68 +0,0 @@
1
- import {Rule} from 'eslint'
2
- import {extractMessages, getSettings} from '../util'
3
- import {TSESTree} from '@typescript-eslint/typescript-estree'
4
-
5
- function checkNode(context: Rule.RuleContext, node: TSESTree.Node) {
6
- const msgs = extractMessages(node, getSettings(context))
7
- const {
8
- options: [type],
9
- } = context
10
- for (const [
11
- {
12
- message: {description},
13
- descriptionNode,
14
- },
15
- ] of msgs) {
16
- if (!description) {
17
- if (type === 'literal' && descriptionNode) {
18
- context.report({
19
- node: descriptionNode as any,
20
- message:
21
- '`description` has to be a string literal (not function call or variable)',
22
- })
23
- } else if (!descriptionNode) {
24
- context.report({
25
- node: node as any,
26
- message: '`description` has to be specified in message descriptor',
27
- })
28
- }
29
- }
30
- }
31
- }
32
-
33
- export default {
34
- meta: {
35
- type: 'problem',
36
- docs: {
37
- description: 'Enforce description in message descriptor',
38
- category: 'Errors',
39
- recommended: false,
40
- url: 'https://formatjs.io/docs/tooling/linter#enforce-description',
41
- },
42
- fixable: 'code',
43
- schema: [
44
- {
45
- enum: ['literal', 'anything'],
46
- },
47
- ],
48
- },
49
- create(context) {
50
- const callExpressionVisitor = (node: TSESTree.Node) =>
51
- checkNode(context, node)
52
-
53
- if (context.parserServices.defineTemplateBodyVisitor) {
54
- return context.parserServices.defineTemplateBodyVisitor(
55
- {
56
- CallExpression: callExpressionVisitor,
57
- },
58
- {
59
- CallExpression: callExpressionVisitor,
60
- }
61
- )
62
- }
63
- return {
64
- JSXOpeningElement: (node: TSESTree.Node) => checkNode(context, node),
65
- CallExpression: callExpressionVisitor,
66
- }
67
- },
68
- } as Rule.RuleModule
@@ -1,171 +0,0 @@
1
- import {Rule} from 'eslint'
2
- import {extractMessages, getSettings} from '../util'
3
- import {TSESTree} from '@typescript-eslint/typescript-estree'
4
- import {interpolateName} from '@formatjs/ts-transformer'
5
-
6
- interface Opts {
7
- idInterpolationPattern: string
8
- idWhitelistRegexps?: RegExp[]
9
- }
10
-
11
- function checkNode(
12
- context: Rule.RuleContext,
13
- node: TSESTree.Node,
14
- {idInterpolationPattern, idWhitelistRegexps}: Opts
15
- ) {
16
- const msgs = extractMessages(node, getSettings(context))
17
- for (const [
18
- {
19
- message: {defaultMessage, description, id},
20
- idPropNode,
21
- descriptionNode,
22
- messagePropNode,
23
- },
24
- ] of msgs) {
25
- if (!idInterpolationPattern && !idPropNode) {
26
- context.report({
27
- node: node as any,
28
- message: `id must be specified`,
29
- })
30
- } else if (idInterpolationPattern) {
31
- if (!defaultMessage) {
32
- context.report({
33
- node: node as any,
34
- message: `defaultMessage must be a string literal to calculate generated IDs`,
35
- })
36
- } else if (!description && descriptionNode) {
37
- context.report({
38
- node: node as any,
39
- message: `description must be a string literal to calculate generated IDs`,
40
- })
41
- } else {
42
- if (
43
- idWhitelistRegexps &&
44
- id &&
45
- idWhitelistRegexps.some((r: RegExp) => r.test(id))
46
- ) {
47
- // messageId is allowlisted so skip interpolation id check
48
- return
49
- }
50
-
51
- const correctId = interpolateName(
52
- {
53
- resourcePath: context.getFilename(),
54
- } as any,
55
- idInterpolationPattern,
56
- {
57
- content: description
58
- ? `${defaultMessage}#${description}`
59
- : defaultMessage,
60
- }
61
- )
62
- if (id !== correctId) {
63
- let message = `"id" does not match with hash pattern ${idInterpolationPattern}`
64
- if (idWhitelistRegexps) {
65
- message += ` or allowlisted patterns ["${idWhitelistRegexps
66
- .map(r => r.toString())
67
- .join('", "')}"]`
68
- }
69
-
70
- context.report({
71
- node: node as any,
72
- message: `${message}.
73
- Expected: ${correctId}
74
- Actual: ${id}`,
75
- fix(fixer) {
76
- if (idPropNode) {
77
- if (idPropNode.type === 'JSXAttribute') {
78
- return fixer.replaceText(
79
- idPropNode as any,
80
- `id="${correctId}"`
81
- )
82
- }
83
- return fixer.replaceText(
84
- idPropNode as any,
85
- `id: '${correctId}'`
86
- )
87
- }
88
- // Insert after default message node
89
- if (messagePropNode!.type === 'JSXAttribute') {
90
- return fixer.insertTextAfter(
91
- messagePropNode as any,
92
- ` id="${correctId}"`
93
- )
94
- }
95
- return fixer.replaceText(
96
- messagePropNode as any,
97
- `defaultMessage: '${defaultMessage}', id: '${correctId}'`
98
- )
99
- },
100
- })
101
- }
102
- }
103
- }
104
- }
105
- }
106
-
107
- export default {
108
- meta: {
109
- type: 'problem',
110
- docs: {
111
- description: 'Enforce (generated) ID in message descriptor',
112
- category: 'Errors',
113
- recommended: false,
114
- url: 'https://formatjs.io/docs/tooling/linter#enforce-id',
115
- },
116
- fixable: 'code',
117
- schema: [
118
- {
119
- type: 'object',
120
- properties: {
121
- idInterpolationPattern: {
122
- type: 'string',
123
- description:
124
- 'Pattern to verify ID against. Recommended value: [sha512:contenthash:base64:6]',
125
- },
126
- idWhitelist: {
127
- type: 'array',
128
- description:
129
- "An array of strings with regular expressions. This array allows allowlist custom ids for messages. For example '`\\\\.`' allows any id which has dot; `'^payment_.*'` - allows any custom id which has prefix `payment_`. Be aware that any backslash \\ provided via string must be escaped with an additional backslash.",
130
- items: {
131
- type: 'string',
132
- },
133
- },
134
- },
135
- required: ['idInterpolationPattern'],
136
- additionalProperties: false,
137
- },
138
- ],
139
- },
140
- create(context) {
141
- const tmp = context?.options?.[0]
142
- const opts = {
143
- idInterpolationPattern: tmp?.idInterpolationPattern,
144
- } as Opts
145
- if (Array.isArray(tmp?.idWhitelist)) {
146
- const {idWhitelist} = tmp
147
- opts.idWhitelistRegexps = idWhitelist.map(
148
- (str: string) => new RegExp(str, 'i')
149
- )
150
- }
151
-
152
- const callExpressionVisitor = (node: TSESTree.Node) =>
153
- checkNode(context, node, opts)
154
-
155
- if (context.parserServices.defineTemplateBodyVisitor) {
156
- return context.parserServices.defineTemplateBodyVisitor(
157
- {
158
- CallExpression: callExpressionVisitor,
159
- },
160
- {
161
- CallExpression: callExpressionVisitor,
162
- }
163
- )
164
- }
165
- return {
166
- JSXOpeningElement: (node: TSESTree.Node) =>
167
- checkNode(context, node, opts),
168
- CallExpression: callExpressionVisitor,
169
- }
170
- },
171
- } as Rule.RuleModule
@@ -1,161 +0,0 @@
1
- import {Rule} from 'eslint'
2
- import {TSESTree} from '@typescript-eslint/typescript-estree'
3
- import {extractMessages, getSettings} from '../util'
4
- import {
5
- parse,
6
- isPluralElement,
7
- MessageFormatElement,
8
- isLiteralElement,
9
- isSelectElement,
10
- isPoundElement,
11
- isTagElement,
12
- } from '@formatjs/icu-messageformat-parser'
13
-
14
- class PlaceholderEnforcement extends Error {
15
- public message: string
16
- constructor(message: string) {
17
- super()
18
- this.message = message
19
- }
20
- }
21
-
22
- function keyExistsInExpression(
23
- key: string,
24
- values: TSESTree.Expression | undefined
25
- ) {
26
- if (!values) {
27
- return false
28
- }
29
- if (values.type !== 'ObjectExpression') {
30
- return true // True bc we cannot evaluate this
31
- }
32
- if (values.properties.find(prop => prop.type === 'SpreadElement')) {
33
- return true // True bc there's a spread element
34
- }
35
- return !!values.properties.find(prop => {
36
- if (prop.type !== 'Property') {
37
- return false
38
- }
39
- switch (prop.key.type) {
40
- case 'Identifier':
41
- return prop.key.name === key
42
- case 'Literal':
43
- return prop.key.value === key
44
- }
45
- return false
46
- })
47
- }
48
-
49
- function verifyAst(
50
- ast: MessageFormatElement[],
51
- values: TSESTree.Expression | undefined,
52
- ignoreList: Set<string>
53
- ) {
54
- for (const el of ast) {
55
- if (isLiteralElement(el) || isPoundElement(el)) {
56
- continue
57
- }
58
- const key = el.value
59
- if (!ignoreList.has(key) && !keyExistsInExpression(key, values)) {
60
- throw new PlaceholderEnforcement(
61
- `Missing value for placeholder "${el.value}"`
62
- )
63
- }
64
-
65
- if (isPluralElement(el) || isSelectElement(el)) {
66
- for (const selector of Object.keys(el.options)) {
67
- verifyAst(el.options[selector].value, values, ignoreList)
68
- }
69
- }
70
-
71
- if (isTagElement(el)) {
72
- verifyAst(el.children, values, ignoreList)
73
- }
74
- }
75
- }
76
-
77
- function checkNode(context: Rule.RuleContext, node: TSESTree.Node) {
78
- const settings = getSettings(context)
79
- const msgs = extractMessages(node, {
80
- excludeMessageDeclCalls: true,
81
- ...settings,
82
- })
83
- const {
84
- options: [opt],
85
- } = context
86
- const ignoreList = new Set<string>(opt?.ignoreList || [])
87
- for (const [
88
- {
89
- message: {defaultMessage},
90
- messageNode,
91
- },
92
- values,
93
- ] of msgs) {
94
- if (!defaultMessage || !messageNode) {
95
- continue
96
- }
97
- try {
98
- verifyAst(
99
- parse(defaultMessage, {
100
- ignoreTag: settings.ignoreTag,
101
- }),
102
- values,
103
- ignoreList
104
- )
105
- } catch (e) {
106
- context.report({
107
- node: messageNode as any,
108
- message: e instanceof Error ? e.message : String(e),
109
- })
110
- }
111
- }
112
- }
113
-
114
- const rule: Rule.RuleModule = {
115
- meta: {
116
- type: 'problem',
117
- docs: {
118
- description:
119
- 'Enforce that all messages with placeholders have enough passed-in values',
120
- category: 'Errors',
121
- recommended: true,
122
- url: 'https://formatjs.io/docs/tooling/linter#enforce-placeholders',
123
- },
124
- fixable: 'code',
125
- schema: [
126
- {
127
- type: 'object',
128
- properties: {
129
- ignoreList: {
130
- type: 'array',
131
- items: {
132
- type: 'string',
133
- },
134
- },
135
- },
136
- additionalProperties: false,
137
- },
138
- ],
139
- },
140
- create(context) {
141
- const callExpressionVisitor = (node: TSESTree.Node) =>
142
- checkNode(context, node)
143
-
144
- if (context.parserServices.defineTemplateBodyVisitor) {
145
- return context.parserServices.defineTemplateBodyVisitor(
146
- {
147
- CallExpression: callExpressionVisitor,
148
- },
149
- {
150
- CallExpression: callExpressionVisitor,
151
- }
152
- )
153
- }
154
- return {
155
- JSXOpeningElement: (node: TSESTree.Node) => checkNode(context, node),
156
- CallExpression: callExpressionVisitor,
157
- }
158
- },
159
- }
160
-
161
- export default rule