@sgfe/eslint-plugin-sg 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
};
|