eslint-plugin-formatjs 5.4.1 → 6.0.0
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.
- package/context-compat.js +1 -5
- package/index.d.ts +15 -1
- package/index.js +46 -45
- package/package.json +6 -4
- package/rules/blocklist-elements.d.ts +1 -2
- package/rules/blocklist-elements.js +23 -26
- package/rules/enforce-default-message.js +8 -11
- package/rules/enforce-description.js +8 -11
- package/rules/enforce-id.d.ts +1 -0
- package/rules/enforce-id.js +21 -14
- package/rules/enforce-placeholders.js +14 -17
- package/rules/enforce-plural-rules.js +10 -13
- package/rules/no-camel-case.js +11 -14
- package/rules/no-complex-selectors.js +13 -16
- package/rules/no-emoji.js +11 -14
- package/rules/no-id.js +6 -9
- package/rules/no-invalid-icu.js +9 -12
- package/rules/no-literal-string-in-jsx.js +33 -13
- package/rules/no-literal-string-in-object.js +8 -11
- package/rules/no-missing-icu-plural-one-placeholders.js +15 -19
- package/rules/no-multiple-plurals.js +10 -13
- package/rules/no-multiple-whitespaces.js +27 -32
- package/rules/no-offset.js +10 -13
- package/rules/no-useless-message.js +13 -16
- package/rules/prefer-formatted-message.js +4 -7
- package/rules/prefer-pound-in-plural.js +25 -29
- package/util.js +5 -12
- package/lib_esnext/context-compat.d.ts +0 -3
- package/lib_esnext/context-compat.js +0 -6
- package/lib_esnext/index.d.ts +0 -1
- package/lib_esnext/index.js +0 -146
- package/lib_esnext/package.json +0 -31
- package/lib_esnext/rules/blocklist-elements.d.ts +0 -15
- package/lib_esnext/rules/blocklist-elements.js +0 -132
- package/lib_esnext/rules/enforce-default-message.d.ts +0 -10
- package/lib_esnext/rules/enforce-default-message.js +0 -68
- package/lib_esnext/rules/enforce-description.d.ts +0 -10
- package/lib_esnext/rules/enforce-description.js +0 -66
- package/lib_esnext/rules/enforce-id.d.ts +0 -10
- package/lib_esnext/rules/enforce-id.js +0 -153
- package/lib_esnext/rules/enforce-placeholders.d.ts +0 -8
- package/lib_esnext/rules/enforce-placeholders.js +0 -147
- package/lib_esnext/rules/enforce-plural-rules.d.ts +0 -17
- package/lib_esnext/rules/enforce-plural-rules.js +0 -103
- package/lib_esnext/rules/no-camel-case.d.ts +0 -5
- package/lib_esnext/rules/no-camel-case.js +0 -76
- package/lib_esnext/rules/no-complex-selectors.d.ts +0 -9
- package/lib_esnext/rules/no-complex-selectors.js +0 -136
- package/lib_esnext/rules/no-emoji.d.ts +0 -9
- package/lib_esnext/rules/no-emoji.js +0 -99
- package/lib_esnext/rules/no-id.d.ts +0 -5
- package/lib_esnext/rules/no-id.js +0 -58
- package/lib_esnext/rules/no-invalid-icu.d.ts +0 -5
- package/lib_esnext/rules/no-invalid-icu.js +0 -60
- package/lib_esnext/rules/no-literal-string-in-jsx.d.ts +0 -13
- package/lib_esnext/rules/no-literal-string-in-jsx.js +0 -179
- package/lib_esnext/rules/no-literal-string-in-object.d.ts +0 -9
- package/lib_esnext/rules/no-literal-string-in-object.js +0 -90
- package/lib_esnext/rules/no-missing-icu-plural-one-placeholders.d.ts +0 -6
- package/lib_esnext/rules/no-missing-icu-plural-one-placeholders.js +0 -99
- package/lib_esnext/rules/no-multiple-plurals.d.ts +0 -5
- package/lib_esnext/rules/no-multiple-plurals.js +0 -70
- package/lib_esnext/rules/no-multiple-whitespaces.d.ts +0 -5
- package/lib_esnext/rules/no-multiple-whitespaces.js +0 -141
- package/lib_esnext/rules/no-offset.d.ts +0 -5
- package/lib_esnext/rules/no-offset.js +0 -69
- package/lib_esnext/rules/no-useless-message.d.ts +0 -5
- package/lib_esnext/rules/no-useless-message.js +0 -71
- package/lib_esnext/rules/prefer-formatted-message.d.ts +0 -5
- package/lib_esnext/rules/prefer-formatted-message.js +0 -33
- package/lib_esnext/rules/prefer-pound-in-plural.d.ts +0 -5
- package/lib_esnext/rules/prefer-pound-in-plural.js +0 -191
- package/lib_esnext/util.d.ts +0 -32
- package/lib_esnext/util.js +0 -280
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { isPluralElement, parse, } from '@formatjs/icu-messageformat-parser';
|
|
2
|
-
import { getParserServices } from '../context-compat';
|
|
3
|
-
import { extractMessages, getSettings } from '../util';
|
|
4
|
-
var LDML;
|
|
5
|
-
(function (LDML) {
|
|
6
|
-
LDML["zero"] = "zero";
|
|
7
|
-
LDML["one"] = "one";
|
|
8
|
-
LDML["two"] = "two";
|
|
9
|
-
LDML["few"] = "few";
|
|
10
|
-
LDML["many"] = "many";
|
|
11
|
-
LDML["other"] = "other";
|
|
12
|
-
})(LDML || (LDML = {}));
|
|
13
|
-
function verifyAst(plConfig, ast) {
|
|
14
|
-
const errors = [];
|
|
15
|
-
for (const el of ast) {
|
|
16
|
-
if (isPluralElement(el)) {
|
|
17
|
-
const rules = Object.keys(plConfig);
|
|
18
|
-
for (const rule of rules) {
|
|
19
|
-
if (plConfig[rule] && !el.options[rule]) {
|
|
20
|
-
errors.push({ messageId: 'missingPlural', data: { rule } });
|
|
21
|
-
}
|
|
22
|
-
if (!plConfig[rule] && el.options[rule]) {
|
|
23
|
-
errors.push({ messageId: 'forbidden', data: { rule } });
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
const { options } = el;
|
|
27
|
-
for (const selector of Object.keys(options)) {
|
|
28
|
-
errors.push(...verifyAst(plConfig, options[selector].value));
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return errors;
|
|
33
|
-
}
|
|
34
|
-
function checkNode(context, node) {
|
|
35
|
-
const settings = getSettings(context);
|
|
36
|
-
const msgs = extractMessages(node, settings);
|
|
37
|
-
if (!msgs.length) {
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
const plConfig = context.options[0];
|
|
41
|
-
if (!plConfig) {
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
for (const [{ message: { defaultMessage }, messageNode, },] of msgs) {
|
|
45
|
-
if (!defaultMessage || !messageNode) {
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
const errors = verifyAst(plConfig, parse(defaultMessage, {
|
|
49
|
-
ignoreTag: settings.ignoreTag,
|
|
50
|
-
}));
|
|
51
|
-
for (const error of errors) {
|
|
52
|
-
context.report({
|
|
53
|
-
node: messageNode,
|
|
54
|
-
...error,
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
export const name = 'enforce-plural-rules';
|
|
60
|
-
export const rule = {
|
|
61
|
-
meta: {
|
|
62
|
-
type: 'problem',
|
|
63
|
-
docs: {
|
|
64
|
-
description: 'Enforce plural rules to always specify certain categories like `one`/`other`',
|
|
65
|
-
url: 'https://formatjs.github.io/docs/tooling/linter#enforce-plural-rules',
|
|
66
|
-
},
|
|
67
|
-
fixable: 'code',
|
|
68
|
-
schema: [
|
|
69
|
-
{
|
|
70
|
-
type: 'object',
|
|
71
|
-
properties: Object.keys(LDML).reduce((schema, k) => {
|
|
72
|
-
schema[k] = {
|
|
73
|
-
type: 'boolean',
|
|
74
|
-
};
|
|
75
|
-
return schema;
|
|
76
|
-
}, {}),
|
|
77
|
-
additionalProperties: false,
|
|
78
|
-
},
|
|
79
|
-
],
|
|
80
|
-
messages: {
|
|
81
|
-
missingPlural: `Missing plural rule "{{rule}}"`,
|
|
82
|
-
forbidden: `Plural rule "{{rule}}" is forbidden`,
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
defaultOptions: [],
|
|
86
|
-
create(context) {
|
|
87
|
-
const callExpressionVisitor = (node) => checkNode(context, node);
|
|
88
|
-
const parserServices = getParserServices(context);
|
|
89
|
-
//@ts-expect-error defineTemplateBodyVisitor exists in Vue parser
|
|
90
|
-
if (parserServices?.defineTemplateBodyVisitor) {
|
|
91
|
-
//@ts-expect-error
|
|
92
|
-
return parserServices.defineTemplateBodyVisitor({
|
|
93
|
-
CallExpression: callExpressionVisitor,
|
|
94
|
-
}, {
|
|
95
|
-
CallExpression: callExpressionVisitor,
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
return {
|
|
99
|
-
JSXOpeningElement: (node) => checkNode(context, node),
|
|
100
|
-
CallExpression: callExpressionVisitor,
|
|
101
|
-
};
|
|
102
|
-
},
|
|
103
|
-
};
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { isArgumentElement, isPluralElement, parse, } from '@formatjs/icu-messageformat-parser';
|
|
2
|
-
import { getParserServices } from '../context-compat';
|
|
3
|
-
import { extractMessages, getSettings } from '../util';
|
|
4
|
-
export const name = 'no-camel-case';
|
|
5
|
-
const CAMEL_CASE_REGEX = /[A-Z]/;
|
|
6
|
-
function verifyAst(ast) {
|
|
7
|
-
const errors = [];
|
|
8
|
-
for (const el of ast) {
|
|
9
|
-
if (isArgumentElement(el)) {
|
|
10
|
-
if (CAMEL_CASE_REGEX.test(el.value)) {
|
|
11
|
-
errors.push({ messageId: 'camelcase', data: {} });
|
|
12
|
-
}
|
|
13
|
-
continue;
|
|
14
|
-
}
|
|
15
|
-
if (isPluralElement(el)) {
|
|
16
|
-
if (CAMEL_CASE_REGEX.test(el.value)) {
|
|
17
|
-
errors.push({ messageId: 'camelcase', data: {} });
|
|
18
|
-
}
|
|
19
|
-
const { options } = el;
|
|
20
|
-
for (const selector of Object.keys(options)) {
|
|
21
|
-
errors.push(...verifyAst(options[selector].value));
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return errors;
|
|
26
|
-
}
|
|
27
|
-
function checkNode(context, node) {
|
|
28
|
-
const settings = getSettings(context);
|
|
29
|
-
const msgs = extractMessages(node, settings);
|
|
30
|
-
for (const [{ message: { defaultMessage }, messageNode, },] of msgs) {
|
|
31
|
-
if (!defaultMessage || !messageNode) {
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
|
-
const errors = verifyAst(parse(defaultMessage, {
|
|
35
|
-
ignoreTag: settings.ignoreTag,
|
|
36
|
-
}));
|
|
37
|
-
for (const error of errors) {
|
|
38
|
-
context.report({
|
|
39
|
-
node: messageNode,
|
|
40
|
-
...error,
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
export const rule = {
|
|
46
|
-
meta: {
|
|
47
|
-
type: 'problem',
|
|
48
|
-
docs: {
|
|
49
|
-
description: 'Disallow camel case placeholders in message',
|
|
50
|
-
url: 'https://formatjs.github.io/docs/tooling/linter#no-camel-case',
|
|
51
|
-
},
|
|
52
|
-
fixable: 'code',
|
|
53
|
-
schema: [],
|
|
54
|
-
messages: {
|
|
55
|
-
camelcase: 'Camel case arguments are not allowed',
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
defaultOptions: [],
|
|
59
|
-
create(context) {
|
|
60
|
-
const callExpressionVisitor = (node) => checkNode(context, node);
|
|
61
|
-
const parserServices = getParserServices(context);
|
|
62
|
-
//@ts-expect-error defineTemplateBodyVisitor exists in Vue parser
|
|
63
|
-
if (parserServices?.defineTemplateBodyVisitor) {
|
|
64
|
-
//@ts-expect-error
|
|
65
|
-
return parserServices.defineTemplateBodyVisitor({
|
|
66
|
-
CallExpression: callExpressionVisitor,
|
|
67
|
-
}, {
|
|
68
|
-
CallExpression: callExpressionVisitor,
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
return {
|
|
72
|
-
JSXOpeningElement: (node) => checkNode(context, node),
|
|
73
|
-
CallExpression: callExpressionVisitor,
|
|
74
|
-
};
|
|
75
|
-
},
|
|
76
|
-
};
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { RuleModule } from '@typescript-eslint/utils/ts-eslint';
|
|
2
|
-
interface Config {
|
|
3
|
-
limit: number;
|
|
4
|
-
}
|
|
5
|
-
type MessageIds = 'tooComplex' | 'parserError';
|
|
6
|
-
type Options = [Config?];
|
|
7
|
-
export declare const name = "no-complex-selectors";
|
|
8
|
-
export declare const rule: RuleModule<MessageIds, Options>;
|
|
9
|
-
export {};
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { TYPE, parse, } from '@formatjs/icu-messageformat-parser';
|
|
2
|
-
import { getParserServices } from '../context-compat';
|
|
3
|
-
import { extractMessages, getSettings } from '../util';
|
|
4
|
-
function calculateComplexity(ast) {
|
|
5
|
-
// Dynamic programming: define a complexity function f, where:
|
|
6
|
-
// f(plural | select) = sum(f(option) for each option) * f(next element),
|
|
7
|
-
// f(tag) = f(first element of children) * f(next element),
|
|
8
|
-
// f(other) = f(next element),
|
|
9
|
-
// f(out of bound) = 1.
|
|
10
|
-
const complexityByNode = new Map();
|
|
11
|
-
return _calculate(ast, 0);
|
|
12
|
-
function _calculate(ast, index) {
|
|
13
|
-
const element = ast[index];
|
|
14
|
-
if (!element) {
|
|
15
|
-
return 1;
|
|
16
|
-
}
|
|
17
|
-
const cachedComplexity = complexityByNode.get(element);
|
|
18
|
-
if (cachedComplexity !== undefined) {
|
|
19
|
-
return cachedComplexity;
|
|
20
|
-
}
|
|
21
|
-
let complexity;
|
|
22
|
-
switch (element.type) {
|
|
23
|
-
case TYPE.plural:
|
|
24
|
-
case TYPE.select: {
|
|
25
|
-
let sumOfOptions = 0;
|
|
26
|
-
for (const { value } of Object.values(element.options)) {
|
|
27
|
-
sumOfOptions += _calculate(value, 0);
|
|
28
|
-
}
|
|
29
|
-
complexity = sumOfOptions * _calculate(ast, index + 1);
|
|
30
|
-
break;
|
|
31
|
-
}
|
|
32
|
-
case TYPE.tag:
|
|
33
|
-
complexity =
|
|
34
|
-
_calculate(element.children, 0) * _calculate(ast, index + 1);
|
|
35
|
-
break;
|
|
36
|
-
default:
|
|
37
|
-
complexity = _calculate(ast, index + 1);
|
|
38
|
-
break;
|
|
39
|
-
}
|
|
40
|
-
complexityByNode.set(element, complexity);
|
|
41
|
-
return complexity;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
function checkNode(context, node) {
|
|
45
|
-
const settings = getSettings(context);
|
|
46
|
-
const msgs = extractMessages(node, settings);
|
|
47
|
-
if (!msgs.length) {
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
const config = {
|
|
51
|
-
limit: 20,
|
|
52
|
-
...(context.options[0] || {}),
|
|
53
|
-
};
|
|
54
|
-
for (const [{ message: { defaultMessage }, messageNode, },] of msgs) {
|
|
55
|
-
if (!defaultMessage || !messageNode) {
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
let ast;
|
|
59
|
-
try {
|
|
60
|
-
ast = parse(defaultMessage, {
|
|
61
|
-
ignoreTag: settings.ignoreTag,
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
catch (e) {
|
|
65
|
-
context.report({
|
|
66
|
-
node: messageNode,
|
|
67
|
-
messageId: 'parserError',
|
|
68
|
-
data: { message: e instanceof Error ? e.message : String(e) },
|
|
69
|
-
});
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
const complexity = calculateComplexity(ast);
|
|
73
|
-
if (complexity > config.limit) {
|
|
74
|
-
context.report({
|
|
75
|
-
node: messageNode,
|
|
76
|
-
messageId: 'tooComplex',
|
|
77
|
-
data: {
|
|
78
|
-
complexity,
|
|
79
|
-
limit: config.limit,
|
|
80
|
-
},
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
export const name = 'no-complex-selectors';
|
|
86
|
-
export const rule = {
|
|
87
|
-
meta: {
|
|
88
|
-
type: 'problem',
|
|
89
|
-
docs: {
|
|
90
|
-
description: `Make sure a sentence is not too complex.
|
|
91
|
-
Complexity is determined by how many strings are produced when we try to
|
|
92
|
-
flatten the sentence given its selectors. For example:
|
|
93
|
-
"I have {count, plural, one{a dog} other{many dogs}}"
|
|
94
|
-
has the complexity of 2 because flattening the plural selector
|
|
95
|
-
results in 2 sentences: "I have a dog" & "I have many dogs".
|
|
96
|
-
Default complexity limit is 20
|
|
97
|
-
(using Smartling as a reference: https://help.smartling.com/hc/en-us/articles/360008030994-ICU-MessageFormat)
|
|
98
|
-
`,
|
|
99
|
-
url: 'https://formatjs.github.io/docs/tooling/linter#no-complex-selectors',
|
|
100
|
-
},
|
|
101
|
-
schema: [
|
|
102
|
-
{
|
|
103
|
-
type: 'object',
|
|
104
|
-
properties: {
|
|
105
|
-
limit: {
|
|
106
|
-
type: 'number',
|
|
107
|
-
},
|
|
108
|
-
},
|
|
109
|
-
additionalProperties: false,
|
|
110
|
-
},
|
|
111
|
-
],
|
|
112
|
-
fixable: 'code',
|
|
113
|
-
messages: {
|
|
114
|
-
tooComplex: `Message complexity is too high ({{complexity}} vs limit at {{limit}})`,
|
|
115
|
-
parserError: '{{message}}',
|
|
116
|
-
},
|
|
117
|
-
},
|
|
118
|
-
defaultOptions: [{ limit: 20 }],
|
|
119
|
-
create(context) {
|
|
120
|
-
const callExpressionVisitor = (node) => checkNode(context, node);
|
|
121
|
-
const parserServices = getParserServices(context);
|
|
122
|
-
//@ts-expect-error defineTemplateBodyVisitor exists in Vue parser
|
|
123
|
-
if (parserServices?.defineTemplateBodyVisitor) {
|
|
124
|
-
//@ts-expect-error
|
|
125
|
-
return parserServices.defineTemplateBodyVisitor({
|
|
126
|
-
CallExpression: callExpressionVisitor,
|
|
127
|
-
}, {
|
|
128
|
-
CallExpression: callExpressionVisitor,
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
return {
|
|
132
|
-
JSXOpeningElement: (node) => checkNode(context, node),
|
|
133
|
-
CallExpression: callExpressionVisitor,
|
|
134
|
-
};
|
|
135
|
-
},
|
|
136
|
-
};
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { RuleModule } from '@typescript-eslint/utils/ts-eslint';
|
|
2
|
-
export declare const name = "no-emoji";
|
|
3
|
-
type MessageIds = 'notAllowed' | 'notAllowedAboveVersion';
|
|
4
|
-
type NoEmojiConfig = {
|
|
5
|
-
versionAbove: string;
|
|
6
|
-
};
|
|
7
|
-
export type Options = [NoEmojiConfig?];
|
|
8
|
-
export declare const rule: RuleModule<MessageIds, Options>;
|
|
9
|
-
export {};
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { extractEmojis, filterEmojis, getAllEmojis, hasEmoji, isValidEmojiVersion, } from 'unicode-emoji-utils';
|
|
2
|
-
import { getParserServices } from '../context-compat';
|
|
3
|
-
import { extractMessages, getSettings } from '../util';
|
|
4
|
-
export const name = 'no-emoji';
|
|
5
|
-
function checkNode(context, node) {
|
|
6
|
-
const msgs = extractMessages(node, getSettings(context));
|
|
7
|
-
let allowedEmojis = [];
|
|
8
|
-
let versionAbove;
|
|
9
|
-
const [emojiConfig] = context.options;
|
|
10
|
-
if (emojiConfig?.versionAbove &&
|
|
11
|
-
isValidEmojiVersion(emojiConfig.versionAbove) &&
|
|
12
|
-
!versionAbove &&
|
|
13
|
-
allowedEmojis.length === 0) {
|
|
14
|
-
versionAbove = emojiConfig.versionAbove;
|
|
15
|
-
allowedEmojis = getAllEmojis(filterEmojis(versionAbove));
|
|
16
|
-
}
|
|
17
|
-
for (const [{ message: { defaultMessage }, messageNode, },] of msgs) {
|
|
18
|
-
if (!defaultMessage || !messageNode) {
|
|
19
|
-
continue;
|
|
20
|
-
}
|
|
21
|
-
if (hasEmoji(defaultMessage)) {
|
|
22
|
-
if (versionAbove) {
|
|
23
|
-
for (const emoji of extractEmojis(defaultMessage)) {
|
|
24
|
-
if (!allowedEmojis.includes(emoji)) {
|
|
25
|
-
context.report({
|
|
26
|
-
node: messageNode,
|
|
27
|
-
messageId: 'notAllowedAboveVersion',
|
|
28
|
-
data: {
|
|
29
|
-
versionAbove,
|
|
30
|
-
emoji,
|
|
31
|
-
},
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
context.report({
|
|
38
|
-
node: messageNode,
|
|
39
|
-
messageId: 'notAllowed',
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
const versionAboveEnums = [
|
|
46
|
-
'0.6',
|
|
47
|
-
'0.7',
|
|
48
|
-
'1.0',
|
|
49
|
-
'2.0',
|
|
50
|
-
'3.0',
|
|
51
|
-
'4.0',
|
|
52
|
-
'5.0',
|
|
53
|
-
'11.0',
|
|
54
|
-
'12.0',
|
|
55
|
-
'12.1',
|
|
56
|
-
'13.0',
|
|
57
|
-
'13.1',
|
|
58
|
-
'14.0',
|
|
59
|
-
'15.0',
|
|
60
|
-
];
|
|
61
|
-
export const rule = {
|
|
62
|
-
meta: {
|
|
63
|
-
type: 'problem',
|
|
64
|
-
docs: {
|
|
65
|
-
description: 'Disallow emojis in message',
|
|
66
|
-
url: 'https://formatjs.github.io/docs/tooling/linter#no-emoji',
|
|
67
|
-
},
|
|
68
|
-
fixable: 'code',
|
|
69
|
-
schema: [
|
|
70
|
-
{
|
|
71
|
-
type: 'object',
|
|
72
|
-
properties: { versionAbove: { type: 'string', enum: versionAboveEnums } },
|
|
73
|
-
additionalProperties: false,
|
|
74
|
-
},
|
|
75
|
-
],
|
|
76
|
-
messages: {
|
|
77
|
-
notAllowed: 'Emojis are not allowed',
|
|
78
|
-
notAllowedAboveVersion: 'Emojis above version {{versionAbove}} are not allowed - Emoji: {{emoji}}',
|
|
79
|
-
},
|
|
80
|
-
},
|
|
81
|
-
defaultOptions: [],
|
|
82
|
-
create(context) {
|
|
83
|
-
const callExpressionVisitor = (node) => checkNode(context, node);
|
|
84
|
-
const parserServices = getParserServices(context);
|
|
85
|
-
//@ts-expect-error defineTemplateBodyVisitor exists in Vue parser
|
|
86
|
-
if (parserServices?.defineTemplateBodyVisitor) {
|
|
87
|
-
//@ts-expect-error
|
|
88
|
-
return parserServices.defineTemplateBodyVisitor({
|
|
89
|
-
CallExpression: callExpressionVisitor,
|
|
90
|
-
}, {
|
|
91
|
-
CallExpression: callExpressionVisitor,
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
return {
|
|
95
|
-
JSXOpeningElement: (node) => checkNode(context, node),
|
|
96
|
-
CallExpression: callExpressionVisitor,
|
|
97
|
-
};
|
|
98
|
-
},
|
|
99
|
-
};
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { getParserServices } from '../context-compat';
|
|
2
|
-
import { extractMessages, getSettings } from '../util';
|
|
3
|
-
function isComment(token) {
|
|
4
|
-
return !!token && (token.type === 'Block' || token.type === 'Line');
|
|
5
|
-
}
|
|
6
|
-
export const name = 'no-id';
|
|
7
|
-
function checkNode(context, node) {
|
|
8
|
-
const msgs = extractMessages(node, getSettings(context));
|
|
9
|
-
for (const [{ idPropNode }] of msgs) {
|
|
10
|
-
if (idPropNode) {
|
|
11
|
-
context.report({
|
|
12
|
-
node: idPropNode,
|
|
13
|
-
messageId: 'noId',
|
|
14
|
-
fix(fixer) {
|
|
15
|
-
const src = context.getSourceCode();
|
|
16
|
-
const token = src.getTokenAfter(idPropNode);
|
|
17
|
-
const fixes = [fixer.remove(idPropNode)];
|
|
18
|
-
if (token && !isComment(token) && token?.value === ',') {
|
|
19
|
-
fixes.push(fixer.remove(token));
|
|
20
|
-
}
|
|
21
|
-
return fixes;
|
|
22
|
-
},
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
export const rule = {
|
|
28
|
-
meta: {
|
|
29
|
-
type: 'problem',
|
|
30
|
-
docs: {
|
|
31
|
-
description: 'Ban explicit ID from MessageDescriptor',
|
|
32
|
-
url: 'https://formatjs.github.io/docs/tooling/linter#no-id',
|
|
33
|
-
},
|
|
34
|
-
fixable: 'code',
|
|
35
|
-
schema: [],
|
|
36
|
-
messages: {
|
|
37
|
-
noId: 'Manual `id` are not allowed in message descriptor',
|
|
38
|
-
},
|
|
39
|
-
},
|
|
40
|
-
defaultOptions: [],
|
|
41
|
-
create(context) {
|
|
42
|
-
const callExpressionVisitor = (node) => checkNode(context, node);
|
|
43
|
-
const parserServices = getParserServices(context);
|
|
44
|
-
//@ts-expect-error defineTemplateBodyVisitor exists in Vue parser
|
|
45
|
-
if (parserServices?.defineTemplateBodyVisitor) {
|
|
46
|
-
//@ts-expect-error
|
|
47
|
-
return parserServices.defineTemplateBodyVisitor({
|
|
48
|
-
CallExpression: callExpressionVisitor,
|
|
49
|
-
}, {
|
|
50
|
-
CallExpression: callExpressionVisitor,
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
return {
|
|
54
|
-
JSXOpeningElement: (node) => checkNode(context, node),
|
|
55
|
-
CallExpression: callExpressionVisitor,
|
|
56
|
-
};
|
|
57
|
-
},
|
|
58
|
-
};
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { parse } from '@formatjs/icu-messageformat-parser';
|
|
2
|
-
import { getParserServices } from '../context-compat';
|
|
3
|
-
import { extractMessages, getSettings } from '../util';
|
|
4
|
-
export const name = 'no-invalid-icu';
|
|
5
|
-
function checkNode(context, node) {
|
|
6
|
-
const settings = getSettings(context);
|
|
7
|
-
const msgs = extractMessages(node, settings);
|
|
8
|
-
if (!msgs.length) {
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
11
|
-
for (const [{ message: { defaultMessage }, messageNode, },] of msgs) {
|
|
12
|
-
if (!defaultMessage || !messageNode) {
|
|
13
|
-
continue;
|
|
14
|
-
}
|
|
15
|
-
try {
|
|
16
|
-
parse(defaultMessage, {
|
|
17
|
-
ignoreTag: settings.ignoreTag,
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
catch (e) {
|
|
21
|
-
const msg = e instanceof Error ? e.message : e;
|
|
22
|
-
context.report({
|
|
23
|
-
node: messageNode,
|
|
24
|
-
messageId: 'icuError',
|
|
25
|
-
data: { message: `Error parsing ICU string: ${msg}` },
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
export const rule = {
|
|
31
|
-
meta: {
|
|
32
|
-
type: 'problem',
|
|
33
|
-
docs: {
|
|
34
|
-
description: `Make sure ICU messages are formatted correctly with no bad select statements, plurals, etc.`,
|
|
35
|
-
},
|
|
36
|
-
fixable: 'code',
|
|
37
|
-
schema: [],
|
|
38
|
-
messages: {
|
|
39
|
-
icuError: 'Invalid ICU Message format: {{message}}',
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
defaultOptions: [],
|
|
43
|
-
create(context) {
|
|
44
|
-
const callExpressionVisitor = (node) => checkNode(context, node);
|
|
45
|
-
const parserServices = getParserServices(context);
|
|
46
|
-
//@ts-expect-error defineTemplateBodyVisitor exists in Vue parser
|
|
47
|
-
if (parserServices?.defineTemplateBodyVisitor) {
|
|
48
|
-
//@ts-expect-error
|
|
49
|
-
return parserServices.defineTemplateBodyVisitor({
|
|
50
|
-
CallExpression: callExpressionVisitor,
|
|
51
|
-
}, {
|
|
52
|
-
CallExpression: callExpressionVisitor,
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
return {
|
|
56
|
-
JSXOpeningElement: (node) => checkNode(context, node),
|
|
57
|
-
CallExpression: callExpressionVisitor,
|
|
58
|
-
};
|
|
59
|
-
},
|
|
60
|
-
};
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { RuleModule } from '@typescript-eslint/utils/ts-eslint';
|
|
2
|
-
type PropMatcher = readonly [TagNamePattern: string, PropNamePattern: string][];
|
|
3
|
-
type Config = {
|
|
4
|
-
props?: {
|
|
5
|
-
include?: PropMatcher;
|
|
6
|
-
exclude?: PropMatcher;
|
|
7
|
-
};
|
|
8
|
-
};
|
|
9
|
-
type MessageIds = 'noLiteralStringInJsx';
|
|
10
|
-
type Options = [Config?];
|
|
11
|
-
export declare const name = "no-literal-string-in-jsx";
|
|
12
|
-
export declare const rule: RuleModule<MessageIds, Options>;
|
|
13
|
-
export {};
|