eslint-plugin-formatjs 4.11.3 → 4.12.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/index.d.ts +3 -23
- package/index.js +36 -37
- package/package.json +5 -5
- package/rules/blocklist-elements.d.ts +17 -3
- package/rules/blocklist-elements.js +50 -46
- package/rules/enforce-default-message.d.ts +10 -3
- package/rules/enforce-default-message.js +21 -9
- package/rules/enforce-description.d.ts +10 -3
- package/rules/enforce-description.js +19 -6
- package/rules/enforce-id.d.ts +10 -3
- package/rules/enforce-id.js +53 -24
- package/rules/enforce-placeholders.d.ts +8 -3
- package/rules/enforce-placeholders.js +18 -7
- package/rules/enforce-plural-rules.d.ts +17 -3
- package/rules/enforce-plural-rules.js +20 -20
- package/rules/no-camel-case.d.ts +6 -3
- package/rules/no-camel-case.js +20 -20
- package/rules/no-complex-selectors.d.ts +9 -3
- package/rules/no-complex-selectors.js +18 -16
- package/rules/no-emoji.d.ts +9 -3
- package/rules/no-emoji.js +6 -4
- package/rules/no-id.d.ts +6 -3
- package/rules/no-id.js +11 -4
- package/rules/no-invalid-icu.d.ts +6 -3
- package/rules/no-invalid-icu.js +12 -5
- package/rules/no-literal-string-in-jsx.d.ts +13 -3
- package/rules/no-literal-string-in-jsx.js +10 -7
- package/rules/no-multiple-plurals.d.ts +6 -3
- package/rules/no-multiple-plurals.js +20 -20
- package/rules/no-multiple-whitespaces.d.ts +6 -3
- package/rules/no-multiple-whitespaces.js +10 -5
- package/rules/no-offset.d.ts +6 -3
- package/rules/no-offset.js +19 -19
- package/rules/no-useless-message.d.ts +6 -3
- package/rules/no-useless-message.js +23 -39
- package/rules/prefer-formatted-message.d.ts +6 -3
- package/rules/prefer-formatted-message.js +5 -3
- package/rules/prefer-pound-in-plural.d.ts +6 -3
- package/rules/prefer-pound-in-plural.js +10 -4
- package/util.d.ts +2 -2
package/index.d.ts
CHANGED
|
@@ -1,24 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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.
|
|
3
|
+
"version": "4.12.0",
|
|
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.
|
|
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.
|
|
32
|
-
"@formatjs/
|
|
33
|
-
"@formatjs/
|
|
31
|
+
"unicode-emoji-utils": "^1.2.0",
|
|
32
|
+
"@formatjs/ts-transformer": "3.13.10",
|
|
33
|
+
"@formatjs/icu-messageformat-parser": "2.7.4"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"eslint": "7 || 8"
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
29
|
+
errors.push(getMessage(Element.literal));
|
|
27
30
|
}
|
|
28
31
|
if ((0, icu_messageformat_parser_1.isArgumentElement)(el) && blocklist.includes(Element.argument)) {
|
|
29
|
-
|
|
32
|
+
errors.push(getMessage(Element.argument));
|
|
30
33
|
}
|
|
31
34
|
if ((0, icu_messageformat_parser_1.isNumberElement)(el) && blocklist.includes(Element.number)) {
|
|
32
|
-
|
|
35
|
+
errors.push(getMessage(Element.number));
|
|
33
36
|
}
|
|
34
37
|
if ((0, icu_messageformat_parser_1.isDateElement)(el) && blocklist.includes(Element.date)) {
|
|
35
|
-
|
|
38
|
+
errors.push(getMessage(Element.date));
|
|
36
39
|
}
|
|
37
40
|
if ((0, icu_messageformat_parser_1.isTimeElement)(el) && blocklist.includes(Element.time)) {
|
|
38
|
-
|
|
41
|
+
errors.push(getMessage(Element.time));
|
|
39
42
|
}
|
|
40
43
|
if ((0, icu_messageformat_parser_1.isSelectElement)(el) && blocklist.includes(Element.select)) {
|
|
41
|
-
|
|
44
|
+
errors.push(getMessage(Element.select));
|
|
42
45
|
}
|
|
43
46
|
if ((0, icu_messageformat_parser_1.isTagElement)(el) && blocklist.includes(Element.tag)) {
|
|
44
|
-
|
|
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
|
-
|
|
51
|
+
errors.push(getMessage(Element.argument));
|
|
49
52
|
}
|
|
50
53
|
if (el.pluralType === 'ordinal' &&
|
|
51
54
|
blocklist.includes(Element.selectordinal)) {
|
|
52
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
|
85
|
-
|
|
86
|
+
node,
|
|
87
|
+
...error,
|
|
86
88
|
});
|
|
87
89
|
}
|
|
88
90
|
}
|
|
89
91
|
}
|
|
90
|
-
const
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
113
|
-
|
|
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 {
|
|
2
|
-
declare
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
|
|
25
|
+
messageId: 'defaultMessage',
|
|
21
26
|
});
|
|
22
27
|
}
|
|
23
28
|
}
|
|
24
29
|
}
|
|
25
30
|
}
|
|
26
|
-
|
|
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
|
-
|
|
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 {
|
|
2
|
-
declare
|
|
3
|
-
|
|
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
|
-
|
|
18
|
+
messageId: 'enforceDescriptionLiteral',
|
|
13
19
|
});
|
|
14
20
|
}
|
|
15
21
|
else if (!descriptionNode) {
|
|
16
22
|
context.report({
|
|
17
23
|
node: node,
|
|
18
|
-
|
|
24
|
+
messageId: 'enforceDescription',
|
|
19
25
|
});
|
|
20
26
|
}
|
|
21
27
|
}
|
|
22
28
|
}
|
|
23
29
|
}
|
|
24
|
-
exports.
|
|
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
|
-
|
|
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
|
}, {
|
package/rules/enforce-id.d.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
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 {};
|
package/rules/enforce-id.js
CHANGED
|
@@ -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
|
|
11
|
-
|
|
11
|
+
node,
|
|
12
|
+
messageId: 'enforceId',
|
|
12
13
|
});
|
|
13
14
|
}
|
|
14
15
|
else if (idInterpolationPattern) {
|
|
15
16
|
if (!defaultMessage) {
|
|
16
17
|
context.report({
|
|
17
|
-
node
|
|
18
|
-
|
|
18
|
+
node,
|
|
19
|
+
messageId: 'enforceIdDefaultMessage',
|
|
19
20
|
});
|
|
20
21
|
}
|
|
21
22
|
else if (!description && descriptionNode) {
|
|
22
23
|
context.report({
|
|
23
|
-
node
|
|
24
|
-
|
|
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
|
|
43
|
+
let messageId = 'enforceIdMatching';
|
|
44
|
+
let messageData = {
|
|
45
|
+
idInterpolationPattern,
|
|
46
|
+
expected: correctId,
|
|
47
|
+
actual: id,
|
|
48
|
+
};
|
|
43
49
|
if (idWhitelistRegexps) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
|
76
|
+
return null;
|
|
65
77
|
},
|
|
66
78
|
});
|
|
67
79
|
}
|
|
@@ -69,13 +81,12 @@ Actual: ${id}`,
|
|
|
69
81
|
}
|
|
70
82
|
}
|
|
71
83
|
}
|
|
72
|
-
exports.
|
|
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
|
|
105
|
-
|
|
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
|
}, {
|