@sgfe/eslint-plugin-sg 1.0.3
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.
Potentially problematic release.
This version of @sgfe/eslint-plugin-sg might be problematic. Click here for more details.
- package/LICENSE.md +25 -0
- package/README.md +188 -0
- package/configs/all-type-checked.js +10 -0
- package/configs/all.js +11 -0
- package/configs/recommended.js +11 -0
- package/configs/rules-recommended.js +11 -0
- package/configs/rules.js +11 -0
- package/configs/tests-recommended.js +11 -0
- package/configs/tests.js +11 -0
- package/lib/index.js +90 -0
- package/lib/rules/consistent-output.js +70 -0
- package/lib/rules/fixer-return.js +170 -0
- package/lib/rules/meta-property-ordering.js +108 -0
- package/lib/rules/no-deprecated-context-methods.js +98 -0
- package/lib/rules/no-deprecated-report-api.js +83 -0
- package/lib/rules/no-identical-tests.js +87 -0
- package/lib/rules/no-missing-message-ids.js +101 -0
- package/lib/rules/no-missing-placeholders.js +131 -0
- package/lib/rules/no-only-tests.js +99 -0
- package/lib/rules/no-property-in-node.js +86 -0
- package/lib/rules/no-unused-message-ids.js +139 -0
- package/lib/rules/no-unused-placeholders.js +127 -0
- package/lib/rules/no-useless-token-range.js +174 -0
- package/lib/rules/prefer-message-ids.js +109 -0
- package/lib/rules/prefer-object-rule.js +83 -0
- package/lib/rules/prefer-output-null.js +77 -0
- package/lib/rules/prefer-placeholders.js +102 -0
- package/lib/rules/prefer-replace-text.js +91 -0
- package/lib/rules/report-message-format.js +133 -0
- package/lib/rules/require-meta-docs-description.js +110 -0
- package/lib/rules/require-meta-docs-url.js +175 -0
- package/lib/rules/require-meta-fixable.js +137 -0
- package/lib/rules/require-meta-has-suggestions.js +168 -0
- package/lib/rules/require-meta-schema.js +162 -0
- package/lib/rules/require-meta-type.js +77 -0
- package/lib/rules/test-case-property-ordering.js +107 -0
- package/lib/rules/test-case-shorthand-strings.js +124 -0
- package/lib/utils.js +936 -0
- package/package.json +76 -0
@@ -0,0 +1,109 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
const utils = require('../utils');
|
4
|
+
const { getStaticValue } = require('eslint-utils');
|
5
|
+
|
6
|
+
// ------------------------------------------------------------------------------
|
7
|
+
// Rule Definition
|
8
|
+
// ------------------------------------------------------------------------------
|
9
|
+
|
10
|
+
/** @type {import('eslint').Rule.RuleModule} */
|
11
|
+
module.exports = {
|
12
|
+
meta: {
|
13
|
+
type: 'problem',
|
14
|
+
docs: {
|
15
|
+
description:
|
16
|
+
'require using `messageId` instead of `message` or `desc` to report rule violations',
|
17
|
+
category: 'Rules',
|
18
|
+
recommended: true,
|
19
|
+
url: 'https://github.com/eslint-community/eslint-plugin-eslint-plugin/tree/HEAD/docs/rules/prefer-message-ids.md',
|
20
|
+
},
|
21
|
+
fixable: null,
|
22
|
+
schema: [],
|
23
|
+
messages: {
|
24
|
+
messagesMissing:
|
25
|
+
'`meta.messages` must contain at least one violation message.',
|
26
|
+
foundMessage:
|
27
|
+
'Use `messageId` instead of `message` (for violations) or `desc` (for suggestions).',
|
28
|
+
},
|
29
|
+
},
|
30
|
+
|
31
|
+
create(context) {
|
32
|
+
const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
|
33
|
+
const ruleInfo = utils.getRuleInfo(sourceCode);
|
34
|
+
if (!ruleInfo) {
|
35
|
+
return {};
|
36
|
+
}
|
37
|
+
|
38
|
+
let contextIdentifiers;
|
39
|
+
|
40
|
+
// ----------------------------------------------------------------------
|
41
|
+
// Public
|
42
|
+
// ----------------------------------------------------------------------
|
43
|
+
|
44
|
+
return {
|
45
|
+
Program(ast) {
|
46
|
+
const scope = sourceCode.getScope?.(ast) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < v9.0.0
|
47
|
+
contextIdentifiers = utils.getContextIdentifiers(
|
48
|
+
sourceCode.scopeManager,
|
49
|
+
ast
|
50
|
+
);
|
51
|
+
|
52
|
+
const metaNode = ruleInfo.meta;
|
53
|
+
const messagesNode =
|
54
|
+
metaNode &&
|
55
|
+
metaNode.properties &&
|
56
|
+
metaNode.properties.find(
|
57
|
+
(p) => p.type === 'Property' && utils.getKeyName(p) === 'messages'
|
58
|
+
);
|
59
|
+
|
60
|
+
if (!messagesNode) {
|
61
|
+
context.report({
|
62
|
+
node: metaNode || ruleInfo.create,
|
63
|
+
messageId: 'messagesMissing',
|
64
|
+
});
|
65
|
+
return;
|
66
|
+
}
|
67
|
+
|
68
|
+
const staticValue = getStaticValue(messagesNode.value, scope);
|
69
|
+
if (!staticValue) {
|
70
|
+
return;
|
71
|
+
}
|
72
|
+
|
73
|
+
if (
|
74
|
+
typeof staticValue.value === 'object' &&
|
75
|
+
staticValue.value.constructor === Object &&
|
76
|
+
Object.keys(staticValue.value).length === 0
|
77
|
+
) {
|
78
|
+
context.report({
|
79
|
+
node: messagesNode.value,
|
80
|
+
messageId: 'messagesMissing',
|
81
|
+
});
|
82
|
+
}
|
83
|
+
},
|
84
|
+
CallExpression(node) {
|
85
|
+
if (
|
86
|
+
node.callee.type === 'MemberExpression' &&
|
87
|
+
contextIdentifiers.has(node.callee.object) &&
|
88
|
+
node.callee.property.type === 'Identifier' &&
|
89
|
+
node.callee.property.name === 'report'
|
90
|
+
) {
|
91
|
+
const reportInfo = utils.getReportInfo(node, context);
|
92
|
+
if (!reportInfo) {
|
93
|
+
return;
|
94
|
+
}
|
95
|
+
|
96
|
+
const reportMessagesAndDataArray = utils
|
97
|
+
.collectReportViolationAndSuggestionData(reportInfo)
|
98
|
+
.filter((obj) => obj.message);
|
99
|
+
for (const { message } of reportMessagesAndDataArray) {
|
100
|
+
context.report({
|
101
|
+
node: message.parent,
|
102
|
+
messageId: 'foundMessage',
|
103
|
+
});
|
104
|
+
}
|
105
|
+
}
|
106
|
+
},
|
107
|
+
};
|
108
|
+
},
|
109
|
+
};
|
@@ -0,0 +1,83 @@
|
|
1
|
+
/**
|
2
|
+
* @author Brad Zacher <https://github.com/bradzacher>
|
3
|
+
*/
|
4
|
+
|
5
|
+
'use strict';
|
6
|
+
|
7
|
+
const utils = require('../utils');
|
8
|
+
|
9
|
+
// ------------------------------------------------------------------------------
|
10
|
+
// Rule Definition
|
11
|
+
// ------------------------------------------------------------------------------
|
12
|
+
|
13
|
+
/** @type {import('eslint').Rule.RuleModule} */
|
14
|
+
module.exports = {
|
15
|
+
meta: {
|
16
|
+
type: 'suggestion',
|
17
|
+
docs: {
|
18
|
+
description: 'disallow function-style rules',
|
19
|
+
category: 'Rules',
|
20
|
+
recommended: true,
|
21
|
+
url: 'https://github.com/eslint-community/eslint-plugin-eslint-plugin/tree/HEAD/docs/rules/prefer-object-rule.md',
|
22
|
+
},
|
23
|
+
fixable: 'code',
|
24
|
+
schema: [],
|
25
|
+
messages: {
|
26
|
+
preferObject: 'Rules should be declared using the object style.',
|
27
|
+
},
|
28
|
+
},
|
29
|
+
|
30
|
+
create(context) {
|
31
|
+
// ----------------------------------------------------------------------
|
32
|
+
// Public
|
33
|
+
// ----------------------------------------------------------------------
|
34
|
+
|
35
|
+
const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
|
36
|
+
const ruleInfo = utils.getRuleInfo(sourceCode);
|
37
|
+
if (!ruleInfo) {
|
38
|
+
return {};
|
39
|
+
}
|
40
|
+
|
41
|
+
return {
|
42
|
+
Program() {
|
43
|
+
if (ruleInfo.isNewStyle) {
|
44
|
+
return;
|
45
|
+
}
|
46
|
+
|
47
|
+
context.report({
|
48
|
+
node: ruleInfo.create,
|
49
|
+
messageId: 'preferObject',
|
50
|
+
*fix(fixer) {
|
51
|
+
// note - we intentionally don't worry about formatting here, as otherwise we have
|
52
|
+
// to indent the function correctly
|
53
|
+
|
54
|
+
if (
|
55
|
+
ruleInfo.create.type === 'FunctionExpression' ||
|
56
|
+
ruleInfo.create.type === 'FunctionDeclaration'
|
57
|
+
) {
|
58
|
+
const openParenToken = sourceCode.getFirstToken(
|
59
|
+
ruleInfo.create,
|
60
|
+
(token) => token.type === 'Punctuator' && token.value === '('
|
61
|
+
);
|
62
|
+
|
63
|
+
/* istanbul ignore if */
|
64
|
+
if (!openParenToken) {
|
65
|
+
// this shouldn't happen, but guarding against crashes just in case
|
66
|
+
return null;
|
67
|
+
}
|
68
|
+
|
69
|
+
yield fixer.replaceTextRange(
|
70
|
+
[ruleInfo.create.range[0], openParenToken.range[0]],
|
71
|
+
'{create'
|
72
|
+
);
|
73
|
+
yield fixer.insertTextAfter(ruleInfo.create, '}');
|
74
|
+
} else if (ruleInfo.create.type === 'ArrowFunctionExpression') {
|
75
|
+
yield fixer.insertTextBefore(ruleInfo.create, '{create: ');
|
76
|
+
yield fixer.insertTextAfter(ruleInfo.create, '}');
|
77
|
+
}
|
78
|
+
},
|
79
|
+
});
|
80
|
+
},
|
81
|
+
};
|
82
|
+
},
|
83
|
+
};
|
@@ -0,0 +1,77 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview disallows invalid RuleTester test cases where the `output` matches the `code`
|
3
|
+
* @author 薛定谔的猫<hh_2013@foxmail.com>
|
4
|
+
*/
|
5
|
+
|
6
|
+
'use strict';
|
7
|
+
|
8
|
+
const utils = require('../utils');
|
9
|
+
|
10
|
+
// ------------------------------------------------------------------------------
|
11
|
+
// Rule Definition
|
12
|
+
// ------------------------------------------------------------------------------
|
13
|
+
|
14
|
+
/** @type {import('eslint').Rule.RuleModule} */
|
15
|
+
module.exports = {
|
16
|
+
meta: {
|
17
|
+
type: 'suggestion',
|
18
|
+
docs: {
|
19
|
+
description:
|
20
|
+
'disallow invalid RuleTester test cases where the `output` matches the `code`',
|
21
|
+
category: 'Tests',
|
22
|
+
recommended: true,
|
23
|
+
url: 'https://github.com/eslint-community/eslint-plugin-eslint-plugin/tree/HEAD/docs/rules/prefer-output-null.md',
|
24
|
+
},
|
25
|
+
fixable: 'code',
|
26
|
+
schema: [],
|
27
|
+
messages: {
|
28
|
+
useOutputNull:
|
29
|
+
'Use `output: null` to assert that a test case is not autofixed.',
|
30
|
+
},
|
31
|
+
},
|
32
|
+
|
33
|
+
create(context) {
|
34
|
+
// ----------------------------------------------------------------------
|
35
|
+
// Public
|
36
|
+
// ----------------------------------------------------------------------
|
37
|
+
|
38
|
+
const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
|
39
|
+
|
40
|
+
return {
|
41
|
+
Program(ast) {
|
42
|
+
utils.getTestInfo(context, ast).forEach((testRun) => {
|
43
|
+
testRun.invalid.forEach((test) => {
|
44
|
+
/**
|
45
|
+
* Get a test case's giving keyname node.
|
46
|
+
* @param {string} the keyname to find.
|
47
|
+
* @returns {Node} found node; if not found, return null;
|
48
|
+
*/
|
49
|
+
function getTestInfo(key) {
|
50
|
+
if (test.type === 'ObjectExpression') {
|
51
|
+
return test.properties.find(
|
52
|
+
(item) => item.type === 'Property' && item.key.name === key
|
53
|
+
);
|
54
|
+
}
|
55
|
+
return null;
|
56
|
+
}
|
57
|
+
|
58
|
+
const code = getTestInfo('code');
|
59
|
+
const output = getTestInfo('output');
|
60
|
+
|
61
|
+
if (
|
62
|
+
output &&
|
63
|
+
sourceCode.getText(output.value) ===
|
64
|
+
sourceCode.getText(code.value)
|
65
|
+
) {
|
66
|
+
context.report({
|
67
|
+
node: output,
|
68
|
+
messageId: 'useOutputNull',
|
69
|
+
fix: (fixer) => fixer.replaceText(output.value, 'null'),
|
70
|
+
});
|
71
|
+
}
|
72
|
+
});
|
73
|
+
});
|
74
|
+
},
|
75
|
+
};
|
76
|
+
},
|
77
|
+
};
|
@@ -0,0 +1,102 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview require using placeholders for dynamic report messages
|
3
|
+
* @author Teddy Katz
|
4
|
+
*/
|
5
|
+
|
6
|
+
'use strict';
|
7
|
+
|
8
|
+
const utils = require('../utils');
|
9
|
+
const { findVariable } = require('eslint-utils');
|
10
|
+
|
11
|
+
// ------------------------------------------------------------------------------
|
12
|
+
// Rule Definition
|
13
|
+
// ------------------------------------------------------------------------------
|
14
|
+
|
15
|
+
/** @type {import('eslint').Rule.RuleModule} */
|
16
|
+
module.exports = {
|
17
|
+
meta: {
|
18
|
+
type: 'suggestion',
|
19
|
+
docs: {
|
20
|
+
description: 'require using placeholders for dynamic report messages',
|
21
|
+
category: 'Rules',
|
22
|
+
recommended: false,
|
23
|
+
url: 'https://github.com/eslint-community/eslint-plugin-eslint-plugin/tree/HEAD/docs/rules/prefer-placeholders.md',
|
24
|
+
},
|
25
|
+
fixable: null,
|
26
|
+
schema: [],
|
27
|
+
messages: {
|
28
|
+
usePlaceholders:
|
29
|
+
'Use report message placeholders instead of string concatenation.',
|
30
|
+
},
|
31
|
+
},
|
32
|
+
|
33
|
+
create(context) {
|
34
|
+
let contextIdentifiers;
|
35
|
+
|
36
|
+
const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
|
37
|
+
const { scopeManager } = sourceCode;
|
38
|
+
|
39
|
+
// ----------------------------------------------------------------------
|
40
|
+
// Public
|
41
|
+
// ----------------------------------------------------------------------
|
42
|
+
|
43
|
+
return {
|
44
|
+
Program(ast) {
|
45
|
+
contextIdentifiers = utils.getContextIdentifiers(scopeManager, ast);
|
46
|
+
},
|
47
|
+
CallExpression(node) {
|
48
|
+
if (
|
49
|
+
node.callee.type === 'MemberExpression' &&
|
50
|
+
contextIdentifiers.has(node.callee.object) &&
|
51
|
+
node.callee.property.type === 'Identifier' &&
|
52
|
+
node.callee.property.name === 'report'
|
53
|
+
) {
|
54
|
+
const reportInfo = utils.getReportInfo(node, context);
|
55
|
+
|
56
|
+
if (!reportInfo) {
|
57
|
+
return;
|
58
|
+
}
|
59
|
+
|
60
|
+
const reportMessagesAndDataArray = utils
|
61
|
+
.collectReportViolationAndSuggestionData(reportInfo)
|
62
|
+
.filter((obj) => obj.message);
|
63
|
+
for (let { message: messageNode } of reportMessagesAndDataArray) {
|
64
|
+
if (messageNode.type === 'Identifier') {
|
65
|
+
// See if we can find the variable declaration.
|
66
|
+
|
67
|
+
const variable = findVariable(
|
68
|
+
scopeManager.acquire(messageNode) || scopeManager.globalScope,
|
69
|
+
messageNode
|
70
|
+
);
|
71
|
+
|
72
|
+
if (
|
73
|
+
!variable ||
|
74
|
+
!variable.defs ||
|
75
|
+
!variable.defs[0] ||
|
76
|
+
!variable.defs[0].node ||
|
77
|
+
variable.defs[0].node.type !== 'VariableDeclarator' ||
|
78
|
+
!variable.defs[0].node.init
|
79
|
+
) {
|
80
|
+
return;
|
81
|
+
}
|
82
|
+
|
83
|
+
messageNode = variable.defs[0].node.init;
|
84
|
+
}
|
85
|
+
|
86
|
+
if (
|
87
|
+
(messageNode.type === 'TemplateLiteral' &&
|
88
|
+
messageNode.expressions.length > 0) ||
|
89
|
+
(messageNode.type === 'BinaryExpression' &&
|
90
|
+
messageNode.operator === '+')
|
91
|
+
) {
|
92
|
+
context.report({
|
93
|
+
node: messageNode,
|
94
|
+
messageId: 'usePlaceholders',
|
95
|
+
});
|
96
|
+
}
|
97
|
+
}
|
98
|
+
}
|
99
|
+
},
|
100
|
+
};
|
101
|
+
},
|
102
|
+
};
|
@@ -0,0 +1,91 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview prefer using `replaceText()` instead of `replaceTextRange()`
|
3
|
+
* @author 薛定谔的猫<hh_2013@foxmail.com>
|
4
|
+
*/
|
5
|
+
|
6
|
+
'use strict';
|
7
|
+
|
8
|
+
const utils = require('../utils');
|
9
|
+
|
10
|
+
// ------------------------------------------------------------------------------
|
11
|
+
// Rule Definition
|
12
|
+
// ------------------------------------------------------------------------------
|
13
|
+
|
14
|
+
/** @type {import('eslint').Rule.RuleModule} */
|
15
|
+
module.exports = {
|
16
|
+
meta: {
|
17
|
+
type: 'suggestion',
|
18
|
+
docs: {
|
19
|
+
description:
|
20
|
+
'require using `replaceText()` instead of `replaceTextRange()`',
|
21
|
+
category: 'Rules',
|
22
|
+
recommended: false,
|
23
|
+
url: 'https://github.com/eslint-community/eslint-plugin-eslint-plugin/tree/HEAD/docs/rules/prefer-replace-text.md',
|
24
|
+
},
|
25
|
+
fixable: null,
|
26
|
+
schema: [],
|
27
|
+
messages: {
|
28
|
+
useReplaceText: 'Use replaceText instead of replaceTextRange.',
|
29
|
+
},
|
30
|
+
},
|
31
|
+
|
32
|
+
create(context) {
|
33
|
+
const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
|
34
|
+
let funcInfo = {
|
35
|
+
upper: null,
|
36
|
+
codePath: null,
|
37
|
+
shouldCheck: false,
|
38
|
+
node: null,
|
39
|
+
};
|
40
|
+
let contextIdentifiers;
|
41
|
+
|
42
|
+
return {
|
43
|
+
Program(ast) {
|
44
|
+
contextIdentifiers = utils.getContextIdentifiers(
|
45
|
+
sourceCode.scopeManager,
|
46
|
+
ast
|
47
|
+
);
|
48
|
+
},
|
49
|
+
|
50
|
+
// Stacks this function's information.
|
51
|
+
onCodePathStart(codePath, node) {
|
52
|
+
funcInfo = {
|
53
|
+
upper: funcInfo,
|
54
|
+
codePath,
|
55
|
+
shouldCheck:
|
56
|
+
utils.isAutoFixerFunction(node, contextIdentifiers) ||
|
57
|
+
utils.isSuggestionFixerFunction(node, contextIdentifiers),
|
58
|
+
node,
|
59
|
+
};
|
60
|
+
},
|
61
|
+
|
62
|
+
// Pops this function's information.
|
63
|
+
onCodePathEnd() {
|
64
|
+
funcInfo = funcInfo.upper;
|
65
|
+
},
|
66
|
+
|
67
|
+
// Checks the replaceTextRange arguments.
|
68
|
+
'CallExpression[arguments.length=2]'(node) {
|
69
|
+
if (
|
70
|
+
funcInfo.shouldCheck &&
|
71
|
+
node.callee.type === 'MemberExpression' &&
|
72
|
+
node.callee.property.name === 'replaceTextRange'
|
73
|
+
) {
|
74
|
+
const arg = node.arguments[0];
|
75
|
+
const isIdenticalNodeRange =
|
76
|
+
arg.type === 'ArrayExpression' &&
|
77
|
+
arg.elements[0].type === 'MemberExpression' &&
|
78
|
+
arg.elements[1].type === 'MemberExpression' &&
|
79
|
+
sourceCode.getText(arg.elements[0].object) ===
|
80
|
+
sourceCode.getText(arg.elements[1].object);
|
81
|
+
if (isIdenticalNodeRange) {
|
82
|
+
context.report({
|
83
|
+
node,
|
84
|
+
messageId: 'useReplaceText',
|
85
|
+
});
|
86
|
+
}
|
87
|
+
}
|
88
|
+
},
|
89
|
+
};
|
90
|
+
},
|
91
|
+
};
|
@@ -0,0 +1,133 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview enforce a consistent format for rule report messages
|
3
|
+
* @author Teddy Katz
|
4
|
+
*/
|
5
|
+
|
6
|
+
'use strict';
|
7
|
+
|
8
|
+
const { getStaticValue } = require('eslint-utils');
|
9
|
+
const utils = require('../utils');
|
10
|
+
|
11
|
+
// ------------------------------------------------------------------------------
|
12
|
+
// Rule Definition
|
13
|
+
// ------------------------------------------------------------------------------
|
14
|
+
|
15
|
+
/** @type {import('eslint').Rule.RuleModule} */
|
16
|
+
module.exports = {
|
17
|
+
meta: {
|
18
|
+
type: 'suggestion',
|
19
|
+
docs: {
|
20
|
+
description: 'enforce a consistent format for rule report messages',
|
21
|
+
category: 'Rules',
|
22
|
+
recommended: false,
|
23
|
+
url: 'https://github.com/eslint-community/eslint-plugin-eslint-plugin/tree/HEAD/docs/rules/report-message-format.md',
|
24
|
+
},
|
25
|
+
fixable: null,
|
26
|
+
schema: [{ type: 'string' }],
|
27
|
+
messages: {
|
28
|
+
noMatch: "Report message does not match the pattern '{{pattern}}'.",
|
29
|
+
},
|
30
|
+
},
|
31
|
+
|
32
|
+
create(context) {
|
33
|
+
const pattern = new RegExp(context.options[0] || '');
|
34
|
+
let contextIdentifiers;
|
35
|
+
|
36
|
+
/**
|
37
|
+
* Report a message node if it doesn't match the given formatting
|
38
|
+
* @param {ASTNode} message The message AST node
|
39
|
+
* @returns {void}
|
40
|
+
*/
|
41
|
+
function processMessageNode(message, scope) {
|
42
|
+
const staticValue = getStaticValue(message, scope);
|
43
|
+
if (
|
44
|
+
(message.type === 'Literal' &&
|
45
|
+
typeof message.value === 'string' &&
|
46
|
+
!pattern.test(message.value)) ||
|
47
|
+
(message.type === 'TemplateLiteral' &&
|
48
|
+
message.quasis.length === 1 &&
|
49
|
+
!pattern.test(message.quasis[0].value.cooked)) ||
|
50
|
+
(staticValue && !pattern.test(staticValue.value))
|
51
|
+
) {
|
52
|
+
context.report({
|
53
|
+
node: message,
|
54
|
+
messageId: 'noMatch',
|
55
|
+
data: { pattern: context.options[0] || '' },
|
56
|
+
});
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
|
61
|
+
const ruleInfo = utils.getRuleInfo(sourceCode);
|
62
|
+
if (!ruleInfo) {
|
63
|
+
return {};
|
64
|
+
}
|
65
|
+
|
66
|
+
// ----------------------------------------------------------------------
|
67
|
+
// Public
|
68
|
+
// ----------------------------------------------------------------------
|
69
|
+
|
70
|
+
return {
|
71
|
+
Program(ast) {
|
72
|
+
const scope = sourceCode.getScope?.(ast) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < v9.0.0
|
73
|
+
contextIdentifiers = utils.getContextIdentifiers(
|
74
|
+
sourceCode.scopeManager,
|
75
|
+
ast
|
76
|
+
);
|
77
|
+
|
78
|
+
const messagesObject =
|
79
|
+
ruleInfo &&
|
80
|
+
ruleInfo.meta &&
|
81
|
+
ruleInfo.meta.type === 'ObjectExpression' &&
|
82
|
+
ruleInfo.meta.properties.find(
|
83
|
+
(prop) =>
|
84
|
+
prop.type === 'Property' && utils.getKeyName(prop) === 'messages'
|
85
|
+
);
|
86
|
+
|
87
|
+
if (
|
88
|
+
!messagesObject ||
|
89
|
+
messagesObject.value.type !== 'ObjectExpression'
|
90
|
+
) {
|
91
|
+
return;
|
92
|
+
}
|
93
|
+
|
94
|
+
messagesObject.value.properties
|
95
|
+
.filter((prop) => prop.type === 'Property')
|
96
|
+
.map((prop) => prop.value)
|
97
|
+
.forEach((it) => processMessageNode(it, scope));
|
98
|
+
},
|
99
|
+
CallExpression(node) {
|
100
|
+
const scope = sourceCode.getScope?.(node) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < v9.0.0
|
101
|
+
if (
|
102
|
+
node.callee.type === 'MemberExpression' &&
|
103
|
+
contextIdentifiers.has(node.callee.object) &&
|
104
|
+
node.callee.property.type === 'Identifier' &&
|
105
|
+
node.callee.property.name === 'report'
|
106
|
+
) {
|
107
|
+
const reportInfo = utils.getReportInfo(node, context);
|
108
|
+
const message = reportInfo && reportInfo.message;
|
109
|
+
const suggest = reportInfo && reportInfo.suggest;
|
110
|
+
|
111
|
+
if (message) {
|
112
|
+
processMessageNode(message, scope);
|
113
|
+
}
|
114
|
+
|
115
|
+
if (suggest && suggest.type === 'ArrayExpression') {
|
116
|
+
suggest.elements
|
117
|
+
.flatMap((obj) =>
|
118
|
+
obj.type === 'ObjectExpression' ? obj.properties : []
|
119
|
+
)
|
120
|
+
.filter(
|
121
|
+
(prop) =>
|
122
|
+
prop.type === 'Property' &&
|
123
|
+
prop.key.type === 'Identifier' &&
|
124
|
+
prop.key.name === 'message'
|
125
|
+
)
|
126
|
+
.map((prop) => prop.value)
|
127
|
+
.forEach((it) => processMessageNode(it, scope));
|
128
|
+
}
|
129
|
+
}
|
130
|
+
},
|
131
|
+
};
|
132
|
+
},
|
133
|
+
};
|