1 | 'use strict';
|
2 | const {findVariable} = require('eslint-utils');
|
3 | const avoidCapture = require('./utils/avoid-capture');
|
4 | const getDocumentationUrl = require('./utils/get-documentation-url');
|
5 | const renameVariable = require('./utils/rename-variable');
|
6 | const methodSelector = require('./utils/method-selector');
|
7 |
|
8 | const ERROR_MESSAGE_ID = 'error';
|
9 |
|
10 | const promiseMethodSelector = (method, argumentsLength, argumentIndex) => [
|
11 | methodSelector({
|
12 | name: method,
|
13 | length: argumentsLength
|
14 | }),
|
15 | `:matches(${
|
16 | [
|
17 | 'FunctionExpression',
|
18 | 'ArrowFunctionExpression'
|
19 | ].map(type => `[arguments.${argumentIndex}.type="${type}"]`).join(', ')
|
20 | })`,
|
21 | `[arguments.${argumentIndex}.params.length=1]`,
|
22 | `[arguments.${argumentIndex}.params.0.type="Identifier"]`
|
23 | ].join('');
|
24 |
|
25 |
|
26 | const promiseCatchSelector = promiseMethodSelector('catch', 1, 0);
|
27 |
|
28 |
|
29 | const promiseThenSelector = promiseMethodSelector('then', 2, 1);
|
30 |
|
31 | const catchSelector = [
|
32 | 'CatchClause',
|
33 | '>',
|
34 | 'Identifier.param'
|
35 | ].join('');
|
36 |
|
37 | const create = context => {
|
38 | const {ecmaVersion} = context.parserOptions;
|
39 | const sourceCode = context.getSourceCode();
|
40 |
|
41 | const options = {
|
42 | name: 'error',
|
43 | ignore: [],
|
44 | ...context.options[0]
|
45 | };
|
46 | const {name: expectedName} = options;
|
47 | const ignore = options.ignore.map(
|
48 | pattern => pattern instanceof RegExp ? pattern : new RegExp(pattern, 'u')
|
49 | );
|
50 | const isNameAllowed = name =>
|
51 | name === expectedName ||
|
52 | ignore.some(regexp => regexp.test(name)) ||
|
53 | name.endsWith(expectedName) ||
|
54 | name.endsWith(expectedName.charAt(0).toUpperCase() + expectedName.slice(1));
|
55 |
|
56 | function check(node) {
|
57 | const originalName = node.name;
|
58 |
|
59 | if (
|
60 | isNameAllowed(originalName) ||
|
61 | isNameAllowed(originalName.replace(/_+$/g, ''))
|
62 | ) {
|
63 | return;
|
64 | }
|
65 |
|
66 | const scope = context.getScope();
|
67 | const variable = findVariable(scope, node);
|
68 |
|
69 | if (originalName === '_' && variable.references.length === 0) {
|
70 | return;
|
71 | }
|
72 |
|
73 | const scopes = [
|
74 | variable.scope,
|
75 | ...variable.references.map(({from}) => from)
|
76 | ];
|
77 | const fixedName = avoidCapture(expectedName, scopes, ecmaVersion);
|
78 |
|
79 | context.report({
|
80 | node,
|
81 | messageId: ERROR_MESSAGE_ID,
|
82 | data: {
|
83 | originalName,
|
84 | fixedName
|
85 | },
|
86 | fix: fixer => renameVariable(variable, fixedName, fixer, sourceCode)
|
87 | });
|
88 | }
|
89 |
|
90 | return {
|
91 | [promiseCatchSelector]: node => {
|
92 | check(node.arguments[0].params[0]);
|
93 | },
|
94 | [promiseThenSelector]: node => {
|
95 | check(node.arguments[1].params[0]);
|
96 | },
|
97 | [catchSelector]: node => {
|
98 | check(node);
|
99 | }
|
100 | };
|
101 | };
|
102 |
|
103 | const schema = [
|
104 | {
|
105 | type: 'object',
|
106 | properties: {
|
107 | name: {
|
108 | type: 'string'
|
109 | },
|
110 | ignore: {
|
111 | type: 'array',
|
112 | uniqueItems: true
|
113 | }
|
114 | }
|
115 | }
|
116 | ];
|
117 |
|
118 | module.exports = {
|
119 | create,
|
120 | meta: {
|
121 | type: 'suggestion',
|
122 | docs: {
|
123 | url: getDocumentationUrl(__filename)
|
124 | },
|
125 | fixable: 'code',
|
126 | schema,
|
127 | messages: {
|
128 | [ERROR_MESSAGE_ID]: 'The catch parameter `{{originalName}}` should be named `{{fixedName}}`.'
|
129 | }
|
130 | }
|
131 | };
|