eslint-plugin-formatjs 6.4.4 → 6.4.6

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 (55) hide show
  1. package/index.d.ts +15 -12
  2. package/index.js +4603 -54
  3. package/index.js.map +1 -0
  4. package/package.json +3 -3
  5. package/util.d.ts +654 -29
  6. package/util.js +62 -134
  7. package/util.js.map +1 -0
  8. package/emoji-data.generated.d.ts +0 -27
  9. package/emoji-data.generated.js +0 -2564
  10. package/emoji-utils.d.ts +0 -43
  11. package/emoji-utils.js +0 -145
  12. package/messages.d.ts +0 -2
  13. package/messages.js +0 -1
  14. package/rules/blocklist-elements.d.ts +0 -14
  15. package/rules/blocklist-elements.js +0 -129
  16. package/rules/enforce-default-message.d.ts +0 -7
  17. package/rules/enforce-default-message.js +0 -57
  18. package/rules/enforce-description.d.ts +0 -11
  19. package/rules/enforce-description.js +0 -97
  20. package/rules/enforce-id.d.ts +0 -8
  21. package/rules/enforce-id.js +0 -135
  22. package/rules/enforce-placeholders.d.ts +0 -3
  23. package/rules/enforce-placeholders.js +0 -128
  24. package/rules/enforce-plural-rules.d.ts +0 -14
  25. package/rules/enforce-plural-rules.js +0 -108
  26. package/rules/no-camel-case.d.ts +0 -3
  27. package/rules/no-camel-case.js +0 -85
  28. package/rules/no-complex-selectors.d.ts +0 -3
  29. package/rules/no-complex-selectors.js +0 -119
  30. package/rules/no-emoji.d.ts +0 -8
  31. package/rules/no-emoji.js +0 -88
  32. package/rules/no-id.d.ts +0 -3
  33. package/rules/no-id.js +0 -48
  34. package/rules/no-invalid-icu.d.ts +0 -3
  35. package/rules/no-invalid-icu.js +0 -56
  36. package/rules/no-literal-string-in-jsx.d.ts +0 -3
  37. package/rules/no-literal-string-in-jsx.js +0 -161
  38. package/rules/no-literal-string-in-object.d.ts +0 -3
  39. package/rules/no-literal-string-in-object.js +0 -59
  40. package/rules/no-missing-icu-plural-one-placeholders.d.ts +0 -5
  41. package/rules/no-missing-icu-plural-one-placeholders.js +0 -94
  42. package/rules/no-multiple-plurals.d.ts +0 -3
  43. package/rules/no-multiple-plurals.js +0 -76
  44. package/rules/no-multiple-whitespaces.d.ts +0 -3
  45. package/rules/no-multiple-whitespaces.js +0 -126
  46. package/rules/no-offset.d.ts +0 -3
  47. package/rules/no-offset.js +0 -75
  48. package/rules/no-useless-message.d.ts +0 -3
  49. package/rules/no-useless-message.js +0 -69
  50. package/rules/prefer-formatted-message.d.ts +0 -3
  51. package/rules/prefer-formatted-message.js +0 -26
  52. package/rules/prefer-full-sentence.d.ts +0 -3
  53. package/rules/prefer-full-sentence.js +0 -111
  54. package/rules/prefer-pound-in-plural.d.ts +0 -3
  55. package/rules/prefer-pound-in-plural.js +0 -163
@@ -1,161 +0,0 @@
1
- import * as picomatchNs from "picomatch";
2
- const picomatch = picomatchNs.default ?? picomatchNs;
3
- const propMatcherSchema = {
4
- type: "array",
5
- items: {
6
- type: "array",
7
- items: [{ type: "string" }, { type: "string" }]
8
- }
9
- };
10
- const defaultPropIncludePattern = [
11
- ["*", "aria-{label,description,details,errormessage}"],
12
- ["[a-z]*([a-z0-9])", "(placeholder|title)"],
13
- ["img", "alt"]
14
- ];
15
- const defaultPropExcludePattern = [];
16
- function stringifyJsxTagName(tagName) {
17
- switch (tagName.type) {
18
- case "JSXIdentifier": return tagName.name;
19
- case "JSXMemberExpression": return `${stringifyJsxTagName(tagName.object)}.${tagName.property.name}`;
20
- case "JSXNamespacedName": return `${tagName.namespace.name}:${tagName.name.name}`;
21
- }
22
- }
23
- function compilePropMatcher(propMatcher) {
24
- return propMatcher.map(([tagNamePattern, propNamePattern]) => {
25
- return [picomatch.makeRe(tagNamePattern, { contains: false }), picomatch.makeRe(propNamePattern, { contains: false })];
26
- });
27
- }
28
- export const name = "no-literal-string-in-jsx";
29
- export const rule = {
30
- meta: {
31
- type: "problem",
32
- docs: {
33
- description: "Disallow untranslated literal strings without translation.",
34
- url: "https://formatjs.github.io/docs/tooling/linter#no-literal-string-in-jsx"
35
- },
36
- schema: [{
37
- type: "object",
38
- additionalProperties: false,
39
- properties: { props: {
40
- type: "object",
41
- additionalProperties: false,
42
- properties: {
43
- include: { ...propMatcherSchema },
44
- exclude: { ...propMatcherSchema }
45
- }
46
- } }
47
- }],
48
- messages: { noLiteralStringInJsx: "Cannot have untranslated text in JSX" }
49
- },
50
- create(context) {
51
- const userConfig = context.options[0] || {};
52
- const propIncludePattern = compilePropMatcher([...defaultPropIncludePattern, ...userConfig.props?.include ?? []]);
53
- const propExcludePattern = compilePropMatcher([...defaultPropExcludePattern, ...userConfig.props?.exclude ?? []]);
54
- const lexicalJsxStack = [];
55
- const shouldSkipCurrentJsxElement = () => {
56
- const currentJsxNode = lexicalJsxStack[lexicalJsxStack.length - 1];
57
- if (currentJsxNode.type === "JSXFragment") {
58
- return false;
59
- }
60
- const nameString = stringifyJsxTagName(currentJsxNode.openingElement.name);
61
- // Check if children should be excluded
62
- for (const [tagNamePattern, propNamePattern] of propExcludePattern) {
63
- if (tagNamePattern.test(nameString) && propNamePattern.test("children")) {
64
- return true;
65
- }
66
- }
67
- return false;
68
- };
69
- const shouldSkipCurrentJsxAttribute = (node) => {
70
- const currentJsxNode = lexicalJsxStack[lexicalJsxStack.length - 1];
71
- if (currentJsxNode.type === "JSXFragment") {
72
- return false;
73
- }
74
- const nameString = stringifyJsxTagName(currentJsxNode.openingElement.name);
75
- const attributeName = typeof node.name.name === "string" ? node.name.name : node.name.name.name;
76
- // match exclude
77
- for (const [tagNamePattern, propNamePattern] of propExcludePattern) {
78
- if (tagNamePattern.test(nameString) && propNamePattern.test(attributeName)) {
79
- return true;
80
- }
81
- }
82
- // match include
83
- for (const [tagNamePattern, propNamePattern] of propIncludePattern) {
84
- if (tagNamePattern.test(nameString) && propNamePattern.test(attributeName)) {
85
- return false;
86
- }
87
- }
88
- return true;
89
- };
90
- const checkJSXExpression = (node) => {
91
- // Check if this is either a string literal / template literal, or the concat of them.
92
- // It also ignores the empty string.
93
- if (node.type === "Literal" && typeof node.value === "string" && node.value.length > 0 || node.type === "TemplateLiteral" && (node.quasis.length > 1 || node.quasis[0].value.raw.length > 0)) {
94
- context.report({
95
- node,
96
- messageId: "noLiteralStringInJsx"
97
- });
98
- } else if (node.type === "BinaryExpression" && node.operator === "+") {
99
- checkJSXExpression(node.left);
100
- checkJSXExpression(node.right);
101
- } else if (node.type === "ConditionalExpression") {
102
- checkJSXExpression(node.consequent);
103
- checkJSXExpression(node.alternate);
104
- } else if (node.type === "LogicalExpression") {
105
- checkJSXExpression(node.left);
106
- checkJSXExpression(node.right);
107
- }
108
- };
109
- return {
110
- JSXElement: (node) => {
111
- lexicalJsxStack.push(node);
112
- },
113
- "JSXElement:exit": () => {
114
- lexicalJsxStack.pop();
115
- },
116
- JSXFragment: (node) => {
117
- lexicalJsxStack.push(node);
118
- },
119
- "JSXFragment:exit": () => {
120
- lexicalJsxStack.pop();
121
- },
122
- JSXAttribute: (node) => {
123
- if (shouldSkipCurrentJsxAttribute(node)) {
124
- return;
125
- }
126
- if (!node.value) {
127
- return;
128
- }
129
- if (node.value.type === "Literal" && typeof node.value.value === "string" && node.value.value.length > 0) {
130
- context.report({
131
- node,
132
- messageId: "noLiteralStringInJsx"
133
- });
134
- } else if (node.value.type === "JSXExpressionContainer" && node.value.expression.type !== "JSXEmptyExpression") {
135
- checkJSXExpression(node.value.expression);
136
- }
137
- },
138
- JSXText: (node) => {
139
- // Ignore purely spacing fragments
140
- if (!node.value.replace(/\s*/gm, "")) {
141
- return;
142
- }
143
- if (shouldSkipCurrentJsxElement()) {
144
- return;
145
- }
146
- context.report({
147
- node,
148
- messageId: "noLiteralStringInJsx"
149
- });
150
- },
151
- "JSXElement > JSXExpressionContainer": (node) => {
152
- if (shouldSkipCurrentJsxElement()) {
153
- return;
154
- }
155
- if (node.expression.type !== "JSXEmptyExpression") {
156
- checkJSXExpression(node.expression);
157
- }
158
- }
159
- };
160
- }
161
- };
@@ -1,3 +0,0 @@
1
- import type { Rule } from "eslint";
2
- export declare const name = "no-literal-string-in-object";
3
- export declare const rule: Rule.RuleModule;
@@ -1,59 +0,0 @@
1
- export const name = "no-literal-string-in-object";
2
- export const rule = {
3
- meta: {
4
- type: "problem",
5
- docs: {
6
- description: "Enforce translation of specific object properties",
7
- url: "https://formatjs.github.io/docs/tooling/linter#no-literal-string-in-object"
8
- },
9
- schema: [{
10
- type: "object",
11
- properties: { include: {
12
- type: "array",
13
- items: { type: "string" },
14
- default: ["label"]
15
- } },
16
- additionalProperties: false
17
- }],
18
- messages: { untranslatedProperty: "Object property: `{{propertyKey}}` might contain an untranslated literal string" }
19
- },
20
- create(context) {
21
- const propertyVisitor = (node) => {
22
- checkProperty(context, node);
23
- };
24
- const parserServices = context.sourceCode.parserServices;
25
- if (parserServices?.defineTemplateBodyVisitor) {
26
- return parserServices.defineTemplateBodyVisitor({ Property: propertyVisitor }, { Property: propertyVisitor });
27
- }
28
- return { Property: propertyVisitor };
29
- }
30
- };
31
- function checkProperty(context, node) {
32
- const config = {
33
- include: ["label"],
34
- ...context.options[0]
35
- };
36
- const propertyKey = node.key.type === "Identifier" ? node.key.name : node.key.type === "Literal" && typeof node.key.value === "string" ? node.key.value : null;
37
- if (!propertyKey || !config.include.includes(propertyKey)) {
38
- return;
39
- }
40
- checkPropertyValue(context, node.value, propertyKey);
41
- }
42
- function checkPropertyValue(context, node, propertyKey) {
43
- if (node.type === "Literal" && typeof node.value === "string" && node.value.length > 0 || node.type === "TemplateLiteral" && (node.quasis.length > 1 || node.quasis[0].value.raw.length > 0)) {
44
- context.report({
45
- node,
46
- messageId: "untranslatedProperty",
47
- data: { propertyKey }
48
- });
49
- } else if (node.type === "BinaryExpression" && node.operator === "+") {
50
- checkPropertyValue(context, node.left, propertyKey);
51
- checkPropertyValue(context, node.right, propertyKey);
52
- } else if (node.type === "ConditionalExpression") {
53
- checkPropertyValue(context, node.consequent, propertyKey);
54
- checkPropertyValue(context, node.alternate, propertyKey);
55
- } else if (node.type === "LogicalExpression") {
56
- checkPropertyValue(context, node.left, propertyKey);
57
- checkPropertyValue(context, node.right, propertyKey);
58
- }
59
- }
@@ -1,5 +0,0 @@
1
- import type { Rule } from "eslint";
2
- import { type CoreMessageIds } from "../messages.js";
3
- export declare const name = "no-missing-icu-plural-one-placeholders";
4
- export type MessageIds = "noMissingIcuPluralOnePlaceholders" | CoreMessageIds;
5
- export declare const rule: Rule.RuleModule;
@@ -1,94 +0,0 @@
1
- import { isLiteralElement, isPluralElement, isSelectElement, isTagElement, parse } from "@formatjs/icu-messageformat-parser";
2
- import MagicString from "magic-string";
3
- import { extractMessages, patchMessage } from "../util.js";
4
- import { CORE_MESSAGES } from "../messages.js";
5
- export const name = "no-missing-icu-plural-one-placeholders";
6
- function verifyAst(context, messageNode, ast) {
7
- const patches = [];
8
- _verifyAstAndReplace(ast);
9
- if (patches.length > 0) {
10
- const patchedMessage = patchMessage(messageNode, ast, (content) => {
11
- return patches.reduce((magicString, patch) => {
12
- switch (patch.type) {
13
- case "prependLeft": return magicString.prependLeft(patch.index, patch.content);
14
- case "remove": return magicString.remove(patch.start, patch.end);
15
- case "update": return magicString.update(patch.start, patch.end, patch.content);
16
- }
17
- }, new MagicString(content)).toString();
18
- });
19
- context.report({
20
- node: messageNode,
21
- messageId: "noMissingIcuPluralOnePlaceholders",
22
- fix: patchedMessage !== null ? (fixer) => fixer.replaceText(messageNode, patchedMessage) : null
23
- });
24
- }
25
- function _verifyAstAndReplace(ast, root = true) {
26
- for (const el of ast) {
27
- if (isPluralElement(el) && el.options["one"]) {
28
- _verifyAstAndReplace(el.options["one"].value, false);
29
- } else if (isSelectElement(el)) {
30
- for (const { value } of Object.values(el.options)) {
31
- _verifyAstAndReplace(value, root);
32
- }
33
- } else if (isTagElement(el)) {
34
- _verifyAstAndReplace(el.children, root);
35
- } else if (!root && isLiteralElement(el)) {
36
- const match = el.value.match(/\b1\b/);
37
- if (match && el.location) {
38
- patches.push({
39
- type: "update",
40
- start: el.location.start.offset,
41
- end: el.location.end.offset,
42
- content: el.value.replace(match[0], "#")
43
- });
44
- }
45
- }
46
- }
47
- }
48
- }
49
- function checkNode(context, node) {
50
- const msgs = extractMessages(node);
51
- for (const [{ message: { defaultMessage }, messageNode }] of msgs) {
52
- if (!defaultMessage || !messageNode) {
53
- continue;
54
- }
55
- let ast;
56
- try {
57
- ast = parse(defaultMessage, { captureLocation: true });
58
- } catch (e) {
59
- context.report({
60
- node: messageNode,
61
- messageId: "parseError",
62
- data: { error: e.message }
63
- });
64
- continue;
65
- }
66
- verifyAst(context, messageNode, ast);
67
- }
68
- }
69
- export const rule = {
70
- meta: {
71
- type: "problem",
72
- docs: {
73
- description: "We use `one {# item}` instead of `one {1 item}` in ICU messages as some locales use the `one` formatting for other similar numbers.",
74
- url: "https://formatjs.github.io/docs/tooling/linter#no-explicit-icu-plural"
75
- },
76
- fixable: "code",
77
- messages: {
78
- ...CORE_MESSAGES,
79
- noMissingIcuPluralOnePlaceholders: "Use `one {# item}` instead of `one {1 item}` in ICU messages."
80
- },
81
- schema: []
82
- },
83
- create(context) {
84
- const callExpressionVisitor = (node) => checkNode(context, node);
85
- const parserServices = context.sourceCode.parserServices;
86
- if (parserServices?.defineTemplateBodyVisitor) {
87
- return parserServices.defineTemplateBodyVisitor({ CallExpression: callExpressionVisitor }, { CallExpression: callExpressionVisitor });
88
- }
89
- return {
90
- JSXOpeningElement: (node) => checkNode(context, node),
91
- CallExpression: callExpressionVisitor
92
- };
93
- }
94
- };
@@ -1,3 +0,0 @@
1
- import type { Rule } from "eslint";
2
- export declare const name = "no-multiple-plurals";
3
- export declare const rule: Rule.RuleModule;
@@ -1,76 +0,0 @@
1
- import { isPluralElement, parse } from "@formatjs/icu-messageformat-parser";
2
- import { extractMessages, getSettings } from "../util.js";
3
- import { CORE_MESSAGES } from "../messages.js";
4
- function verifyAst(ast, pluralCount = { count: 0 }) {
5
- const errors = [];
6
- for (const el of ast) {
7
- if (isPluralElement(el)) {
8
- pluralCount.count++;
9
- if (pluralCount.count > 1) {
10
- errors.push({
11
- messageId: "noMultiplePlurals",
12
- data: {}
13
- });
14
- }
15
- const { options } = el;
16
- for (const selector of Object.keys(options)) {
17
- errors.push(...verifyAst(options[selector].value, pluralCount));
18
- }
19
- }
20
- }
21
- return errors;
22
- }
23
- function checkNode(context, node) {
24
- const settings = getSettings(context);
25
- const msgs = extractMessages(node, settings);
26
- for (const [{ message: { defaultMessage }, messageNode }] of msgs) {
27
- if (!defaultMessage || !messageNode) {
28
- continue;
29
- }
30
- let ast;
31
- try {
32
- ast = parse(defaultMessage, { ignoreTag: settings.ignoreTag });
33
- } catch (e) {
34
- context.report({
35
- node: messageNode,
36
- messageId: "parseError",
37
- data: { error: e.message }
38
- });
39
- continue;
40
- }
41
- const errors = verifyAst(ast);
42
- for (const error of errors) {
43
- context.report({
44
- node,
45
- ...error
46
- });
47
- }
48
- }
49
- }
50
- export const name = "no-multiple-plurals";
51
- export const rule = {
52
- meta: {
53
- type: "problem",
54
- docs: {
55
- description: "Disallow multiple plural rules in the same message",
56
- url: "https://formatjs.github.io/docs/tooling/linter#no-multiple-plurals"
57
- },
58
- fixable: "code",
59
- schema: [],
60
- messages: {
61
- ...CORE_MESSAGES,
62
- noMultiplePlurals: "Multiple plural rules in the same message"
63
- }
64
- },
65
- create(context) {
66
- const callExpressionVisitor = (node) => checkNode(context, node);
67
- const parserServices = context.sourceCode.parserServices;
68
- if (parserServices?.defineTemplateBodyVisitor) {
69
- return parserServices.defineTemplateBodyVisitor({ CallExpression: callExpressionVisitor }, { CallExpression: callExpressionVisitor });
70
- }
71
- return {
72
- JSXOpeningElement: (node) => checkNode(context, node),
73
- CallExpression: callExpressionVisitor
74
- };
75
- }
76
- };
@@ -1,3 +0,0 @@
1
- import type { Rule } from "eslint";
2
- export declare const name = "no-multiple-whitespaces";
3
- export declare const rule: Rule.RuleModule;
@@ -1,126 +0,0 @@
1
- import { parse, TYPE } from "@formatjs/icu-messageformat-parser";
2
- import { extractMessages, getSettings, patchMessage } from "../util.js";
3
- import { CORE_MESSAGES } from "../messages.js";
4
- function isAstValid(ast) {
5
- for (const element of ast) {
6
- switch (element.type) {
7
- case TYPE.literal:
8
- if (/\s{2,}/gm.test(element.value)) {
9
- return false;
10
- }
11
- break;
12
- case TYPE.argument:
13
- case TYPE.date:
14
- case TYPE.number:
15
- case TYPE.pound:
16
- case TYPE.time: break;
17
- case TYPE.plural:
18
- case TYPE.select: {
19
- for (const option of Object.values(element.options)) {
20
- if (!isAstValid(option.value)) {
21
- return false;
22
- }
23
- }
24
- break;
25
- }
26
- case TYPE.tag: return isAstValid(element.children);
27
- }
28
- }
29
- return true;
30
- }
31
- function trimMultiWhitespaces(message, ast) {
32
- const literalElements = [];
33
- const collectLiteralElements = (elements) => {
34
- for (const element of elements) {
35
- switch (element.type) {
36
- case TYPE.literal:
37
- literalElements.push(element);
38
- break;
39
- case TYPE.argument:
40
- case TYPE.date:
41
- case TYPE.number:
42
- case TYPE.pound:
43
- case TYPE.time: break;
44
- case TYPE.plural:
45
- case TYPE.select: {
46
- for (const option of Object.values(element.options)) {
47
- collectLiteralElements(option.value);
48
- }
49
- break;
50
- }
51
- case TYPE.tag:
52
- collectLiteralElements(element.children);
53
- break;
54
- }
55
- }
56
- };
57
- collectLiteralElements(ast);
58
- // Surgically trim whitespaces in the literal element ranges.
59
- // This is to preserve the original whitespaces and newlines info that are lost to parsing.
60
- let trimmedFragments = [];
61
- let currentOffset = 0;
62
- for (const literal of literalElements) {
63
- const { start, end } = literal.location;
64
- const startOffset = start.offset;
65
- const endOffset = end.offset;
66
- trimmedFragments.push(message.slice(currentOffset, startOffset));
67
- trimmedFragments.push(message.slice(startOffset, endOffset).replace(/\s{2,}/gm, " "));
68
- currentOffset = endOffset;
69
- }
70
- trimmedFragments.push(message.slice(currentOffset));
71
- return trimmedFragments.join("");
72
- }
73
- function checkNode(context, node) {
74
- const msgs = extractMessages(node, getSettings(context));
75
- for (const [{ message: { defaultMessage }, messageNode }] of msgs) {
76
- if (!defaultMessage || !messageNode) {
77
- continue;
78
- }
79
- let ast;
80
- try {
81
- ast = parse(defaultMessage, { captureLocation: true });
82
- } catch (e) {
83
- context.report({
84
- node: messageNode,
85
- messageId: "parseError",
86
- data: { error: e instanceof Error ? e.message : String(e) }
87
- });
88
- return;
89
- }
90
- if (!isAstValid(ast)) {
91
- const newMessage = patchMessage(messageNode, ast, trimMultiWhitespaces);
92
- context.report({
93
- node: messageNode,
94
- messageId: "noMultipleWhitespaces",
95
- fix: newMessage !== null ? (fixer) => fixer.replaceText(messageNode, newMessage) : null
96
- });
97
- }
98
- }
99
- }
100
- export const name = "no-multiple-whitespaces";
101
- export const rule = {
102
- meta: {
103
- type: "problem",
104
- docs: {
105
- description: "Prevents usage of multiple consecutive whitespaces in message",
106
- url: "https://formatjs.github.io/docs/tooling/linter#no-multiple-whitespaces"
107
- },
108
- messages: {
109
- ...CORE_MESSAGES,
110
- noMultipleWhitespaces: "Multiple consecutive whitespaces are not allowed"
111
- },
112
- fixable: "code",
113
- schema: []
114
- },
115
- create(context) {
116
- const callExpressionVisitor = (node) => checkNode(context, node);
117
- const parserServices = context.sourceCode.parserServices;
118
- if (parserServices?.defineTemplateBodyVisitor) {
119
- return parserServices.defineTemplateBodyVisitor({ CallExpression: callExpressionVisitor }, { CallExpression: callExpressionVisitor });
120
- }
121
- return {
122
- JSXOpeningElement: (node) => checkNode(context, node),
123
- CallExpression: callExpressionVisitor
124
- };
125
- }
126
- };
@@ -1,3 +0,0 @@
1
- import type { Rule } from "eslint";
2
- export declare const name = "no-offset";
3
- export declare const rule: Rule.RuleModule;
@@ -1,75 +0,0 @@
1
- import { isPluralElement, parse } from "@formatjs/icu-messageformat-parser";
2
- import { extractMessages, getSettings } from "../util.js";
3
- import { CORE_MESSAGES } from "../messages.js";
4
- function verifyAst(ast) {
5
- const errors = [];
6
- for (const el of ast) {
7
- if (isPluralElement(el)) {
8
- if (el.offset) {
9
- errors.push({
10
- messageId: "noOffset",
11
- data: {}
12
- });
13
- }
14
- const { options } = el;
15
- for (const selector of Object.keys(options)) {
16
- errors.push(...verifyAst(options[selector].value));
17
- }
18
- }
19
- }
20
- return errors;
21
- }
22
- function checkNode(context, node) {
23
- const settings = getSettings(context);
24
- const msgs = extractMessages(node, settings);
25
- for (const [{ message: { defaultMessage }, messageNode }] of msgs) {
26
- if (!defaultMessage || !messageNode) {
27
- continue;
28
- }
29
- let ast;
30
- try {
31
- ast = parse(defaultMessage, { ignoreTag: settings.ignoreTag });
32
- } catch (e) {
33
- context.report({
34
- node: messageNode,
35
- messageId: "parseError",
36
- data: { error: e.message }
37
- });
38
- continue;
39
- }
40
- const errors = verifyAst(ast);
41
- for (const error of errors) {
42
- context.report({
43
- node: messageNode,
44
- ...error
45
- });
46
- }
47
- }
48
- }
49
- export const name = "no-offset";
50
- export const rule = {
51
- meta: {
52
- type: "problem",
53
- docs: {
54
- description: "Disallow offset in plural rules",
55
- url: "https://formatjs.github.io/docs/tooling/linter#no-offset"
56
- },
57
- fixable: "code",
58
- messages: {
59
- ...CORE_MESSAGES,
60
- noOffset: "offset is not allowed"
61
- },
62
- schema: []
63
- },
64
- create(context) {
65
- const callExpressionVisitor = (node) => checkNode(context, node);
66
- const parserServices = context.sourceCode.parserServices;
67
- if (parserServices?.defineTemplateBodyVisitor) {
68
- return parserServices.defineTemplateBodyVisitor({ CallExpression: callExpressionVisitor }, { CallExpression: callExpressionVisitor });
69
- }
70
- return {
71
- JSXOpeningElement: (node) => checkNode(context, node),
72
- CallExpression: callExpressionVisitor
73
- };
74
- }
75
- };
@@ -1,3 +0,0 @@
1
- import type { Rule } from "eslint";
2
- export declare const name = "no-useless-message";
3
- export declare const rule: Rule.RuleModule;
@@ -1,69 +0,0 @@
1
- import { parse, TYPE } from "@formatjs/icu-messageformat-parser";
2
- import { extractMessages, getSettings } from "../util.js";
3
- import { CORE_MESSAGES } from "../messages.js";
4
- function verifyAst(ast) {
5
- if (ast.length !== 1) {
6
- return;
7
- }
8
- switch (ast[0].type) {
9
- case TYPE.argument: return "unnecessaryFormat";
10
- case TYPE.number: return "unnecessaryFormatNumber";
11
- case TYPE.date: return "unnecessaryFormatDate";
12
- case TYPE.time: return "unnecessaryFormatTime";
13
- }
14
- }
15
- function checkNode(context, node) {
16
- const settings = getSettings(context);
17
- const msgs = extractMessages(node, settings);
18
- for (const [{ message: { defaultMessage }, messageNode }] of msgs) {
19
- if (!defaultMessage || !messageNode) {
20
- continue;
21
- }
22
- let ast;
23
- try {
24
- ast = parse(defaultMessage, { ignoreTag: settings.ignoreTag });
25
- } catch (e) {
26
- context.report({
27
- node: messageNode,
28
- messageId: "parseError",
29
- data: { error: e.message }
30
- });
31
- continue;
32
- }
33
- const messageId = verifyAst(ast);
34
- if (messageId) context.report({
35
- node: messageNode,
36
- messageId
37
- });
38
- }
39
- }
40
- export const name = "no-useless-message";
41
- export const rule = {
42
- meta: {
43
- type: "problem",
44
- docs: {
45
- description: "Disallow unnecessary formatted message",
46
- url: "https://formatjs.github.io/docs/tooling/linter#no-useless-message"
47
- },
48
- fixable: "code",
49
- schema: [],
50
- messages: {
51
- ...CORE_MESSAGES,
52
- unnecessaryFormat: "Unnecessary formatted message.",
53
- unnecessaryFormatNumber: "Unnecessary formatted message: just use FormattedNumber or intl.formatNumber.",
54
- unnecessaryFormatDate: "Unnecessary formatted message: just use FormattedDate or intl.formatDate.",
55
- unnecessaryFormatTime: "Unnecessary formatted message: just use FormattedTime or intl.formatTime."
56
- }
57
- },
58
- create(context) {
59
- const callExpressionVisitor = (node) => checkNode(context, node);
60
- const parserServices = context.sourceCode.parserServices;
61
- if (parserServices?.defineTemplateBodyVisitor) {
62
- return parserServices.defineTemplateBodyVisitor({ CallExpression: callExpressionVisitor }, { CallExpression: callExpressionVisitor });
63
- }
64
- return {
65
- JSXOpeningElement: (node) => checkNode(context, node),
66
- CallExpression: callExpressionVisitor
67
- };
68
- }
69
- };