@automattic/eslint-plugin-wpvip 0.5.0 → 0.5.2
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/configs/base.js +1 -0
- package/configs/index.js +1 -1
- package/configs/javascript.js +9 -4
- package/configs/react.js +12 -4
- package/configs/typescript.js +0 -9
- package/configs/weak-javascript.js +0 -2
- package/jest.config.js +5 -0
- package/package.json +8 -3
- package/rules/index.js +0 -1
- package/configs/jsx-ally.js +0 -21
- package/rules/no-unused-vars-before-return.js +0 -155
package/configs/base.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./javascript');
|
package/configs/index.js
CHANGED
package/configs/javascript.js
CHANGED
|
@@ -100,10 +100,6 @@ module.exports = {
|
|
|
100
100
|
// will not be awaited in the outer scope.
|
|
101
101
|
'@automattic/wpvip/no-async-foreach': 'error',
|
|
102
102
|
|
|
103
|
-
// Variables that are potentially not needed before a return statement can
|
|
104
|
-
// be assigned after the return.
|
|
105
|
-
'@automattic/wpvip/no-unused-vars-before-return': 'error',
|
|
106
|
-
|
|
107
103
|
// Unguarded getRangeAt calls can throw errors in some browsers.
|
|
108
104
|
'@automattic/wpvip/no-unguarded-get-range-at': 'error',
|
|
109
105
|
|
|
@@ -207,4 +203,13 @@ module.exports = {
|
|
|
207
203
|
|
|
208
204
|
'wrap-iife': ['error', 'any'],
|
|
209
205
|
},
|
|
206
|
+
|
|
207
|
+
settings: {
|
|
208
|
+
'import/resolver': {
|
|
209
|
+
node: {
|
|
210
|
+
extensions: ['.js', '.jsx', '.ts', '.tsx'],
|
|
211
|
+
},
|
|
212
|
+
typescript: 'eslint-import-resolver-typescript',
|
|
213
|
+
},
|
|
214
|
+
},
|
|
210
215
|
};
|
package/configs/react.js
CHANGED
|
@@ -7,7 +7,7 @@ module.exports = {
|
|
|
7
7
|
extends: [
|
|
8
8
|
'plugin:react/recommended',
|
|
9
9
|
'plugin:react-hooks/recommended',
|
|
10
|
-
|
|
10
|
+
'plugin:jsx-a11y/recommended',
|
|
11
11
|
],
|
|
12
12
|
|
|
13
13
|
parserOptions: {
|
|
@@ -22,16 +22,24 @@ module.exports = {
|
|
|
22
22
|
},
|
|
23
23
|
},
|
|
24
24
|
|
|
25
|
-
plugins: ['@automattic/wpvip', 'react', 'react-hooks'],
|
|
25
|
+
plugins: ['@automattic/wpvip', 'jsx-a11y', 'react', 'react-hooks'],
|
|
26
26
|
|
|
27
27
|
rules: {
|
|
28
|
-
'
|
|
28
|
+
'jsx-a11y/label-has-associated-control': [
|
|
29
29
|
'error',
|
|
30
30
|
{
|
|
31
|
-
|
|
31
|
+
assert: 'htmlFor',
|
|
32
32
|
},
|
|
33
33
|
],
|
|
34
34
|
|
|
35
|
+
'jsx-a11y/media-has-caption': 'off',
|
|
36
|
+
|
|
37
|
+
'jsx-a11y/no-noninteractive-tabindex': 'off',
|
|
38
|
+
|
|
39
|
+
'jsx-a11y/role-has-required-aria-props': 'off',
|
|
40
|
+
|
|
41
|
+
'jsx-quotes': 'error',
|
|
42
|
+
|
|
35
43
|
'react/display-name': 'off',
|
|
36
44
|
|
|
37
45
|
'react/jsx-curly-spacing': [
|
package/configs/typescript.js
CHANGED
package/jest.config.js
ADDED
package/package.json
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automattic/eslint-plugin-wpvip",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "ESLint plugin for internal WordPress VIP projects",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"
|
|
7
|
+
"check-types": "tsc __tests__/**/*.ts",
|
|
8
|
+
"jest": "npm run link-plugin && jest",
|
|
8
9
|
"jest:update-snapshot": "jest --updateSnapshot",
|
|
9
10
|
"link-plugin": "mkdir -p ./node_modules/@automattic; ln -fns $(pwd) ./node_modules/@automattic/eslint-plugin-wpvip",
|
|
10
11
|
"lint": "npm run link-plugin && eslint .",
|
|
11
|
-
"test": "jest"
|
|
12
|
+
"test": "npm run jest"
|
|
12
13
|
},
|
|
13
14
|
"repository": {
|
|
14
15
|
"type": "git",
|
|
@@ -47,9 +48,13 @@
|
|
|
47
48
|
"eslint": ">=8"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
51
|
+
"@tsconfig/node18": "1.0.1",
|
|
52
|
+
"@types/eslint": "8.37.0",
|
|
53
|
+
"@types/jest": "29.5.0",
|
|
50
54
|
"eslint": "8.35.0",
|
|
51
55
|
"jest": "29.5.0",
|
|
52
56
|
"prettier": "2.8.4",
|
|
57
|
+
"ts-jest": "29.0.5",
|
|
53
58
|
"typescript": "4.9.5"
|
|
54
59
|
}
|
|
55
60
|
}
|
package/rules/index.js
CHANGED
|
@@ -5,5 +5,4 @@ module.exports = {
|
|
|
5
5
|
'dependency-group': require('./dependency-group'),
|
|
6
6
|
'no-async-foreach': require('./no-async-foreach'),
|
|
7
7
|
'no-unguarded-get-range-at': require('./no-unguarded-get-range-at'),
|
|
8
|
-
'no-unused-vars-before-return': require('./no-unused-vars-before-return'),
|
|
9
8
|
};
|
package/configs/jsx-ally.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Based on:
|
|
3
|
-
* https://github.com/WordPress/gutenberg/blob/%40wordpress/eslint-plugin%4014.1.0/packages/eslint-plugin/configs/jsx-a11y.js
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
module.exports = {
|
|
7
|
-
extends: ['plugin:jsx-a11y/recommended'],
|
|
8
|
-
plugins: ['jsx-a11y'],
|
|
9
|
-
rules: {
|
|
10
|
-
'jsx-a11y/label-has-associated-control': [
|
|
11
|
-
'error',
|
|
12
|
-
{
|
|
13
|
-
assert: 'htmlFor',
|
|
14
|
-
},
|
|
15
|
-
],
|
|
16
|
-
'jsx-a11y/media-has-caption': 'off',
|
|
17
|
-
'jsx-a11y/no-noninteractive-tabindex': 'off',
|
|
18
|
-
'jsx-a11y/role-has-required-aria-props': 'off',
|
|
19
|
-
'jsx-quotes': 'error',
|
|
20
|
-
},
|
|
21
|
-
};
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* From:
|
|
3
|
-
* https://github.com/WordPress/gutenberg/blob/%40wordpress/eslint-plugin%4014.1.0/packages/eslint-plugin/rules/no-unused-vars-before-return.js
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/** @typedef {import('eslint').Scope.Scope} ESLintScope */
|
|
7
|
-
/** @typedef {import('eslint').Rule.RuleContext} ESLintRuleContext */
|
|
8
|
-
/** @typedef {import('estree').Node} ESTreeNode */
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Mapping of function scope objects to a set of identified JSX identifiers
|
|
12
|
-
* within that scope.
|
|
13
|
-
*
|
|
14
|
-
* @type {WeakMap<ESLintScope,Set<ESTreeNode>>}
|
|
15
|
-
*/
|
|
16
|
-
const FUNCTION_SCOPE_JSX_IDENTIFIERS = new WeakMap();
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Returns the closest function scope for the current ESLint context object, or
|
|
20
|
-
* undefined if it cannot be determined.
|
|
21
|
-
*
|
|
22
|
-
* @param {ESLintRuleContext} context ESLint context object.
|
|
23
|
-
*
|
|
24
|
-
* @return {ESLintScope|undefined} Function scope, if known.
|
|
25
|
-
*/
|
|
26
|
-
function getClosestFunctionScope(context) {
|
|
27
|
-
let functionScope = context.getScope();
|
|
28
|
-
while (functionScope.type !== 'function' && functionScope.upper) {
|
|
29
|
-
functionScope = functionScope.upper;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return functionScope;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
module.exports = /** @type {import('eslint').Rule} */ ({
|
|
36
|
-
meta: {
|
|
37
|
-
type: 'problem',
|
|
38
|
-
schema: [
|
|
39
|
-
{
|
|
40
|
-
type: 'object',
|
|
41
|
-
properties: {
|
|
42
|
-
excludePattern: {
|
|
43
|
-
type: 'string',
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
additionalProperties: false,
|
|
47
|
-
},
|
|
48
|
-
],
|
|
49
|
-
},
|
|
50
|
-
/**
|
|
51
|
-
* @param {ESLintRuleContext} context Rule context.
|
|
52
|
-
*/
|
|
53
|
-
create(context) {
|
|
54
|
-
const options = context.options[0] || {};
|
|
55
|
-
const { excludePattern } = options;
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Given an Espree VariableDeclarator node, returns true if the node
|
|
59
|
-
* can be exempted from consideration as unused, or false otherwise. A
|
|
60
|
-
* node can be exempt if it destructures to multiple variables, since
|
|
61
|
-
* those other variables may be used prior to the return statement. A
|
|
62
|
-
* future enhancement could validate that they are in-fact referenced.
|
|
63
|
-
*
|
|
64
|
-
* @param {Object} node Node to test.
|
|
65
|
-
*
|
|
66
|
-
* @return {boolean} Whether declarator is emempt from consideration.
|
|
67
|
-
*/
|
|
68
|
-
function isExemptObjectDestructureDeclarator(node) {
|
|
69
|
-
return node.id.type === 'ObjectPattern' && node.id.properties.length > 1;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return {
|
|
73
|
-
JSXIdentifier(node) {
|
|
74
|
-
// Currently, a scope's variable references does not include JSX
|
|
75
|
-
// identifiers. Account for this by visiting JSX identifiers
|
|
76
|
-
// first, and tracking them in a map per function scope, which
|
|
77
|
-
// is later merged with the known variable references.
|
|
78
|
-
const functionScope = getClosestFunctionScope(context);
|
|
79
|
-
if (!functionScope) {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (!FUNCTION_SCOPE_JSX_IDENTIFIERS.has(functionScope)) {
|
|
84
|
-
FUNCTION_SCOPE_JSX_IDENTIFIERS.set(functionScope, new Set());
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
FUNCTION_SCOPE_JSX_IDENTIFIERS.get(functionScope).add(node);
|
|
88
|
-
},
|
|
89
|
-
'ReturnStatement:exit'(node) {
|
|
90
|
-
const functionScope = getClosestFunctionScope(context);
|
|
91
|
-
if (!functionScope) {
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
for (const variable of functionScope.variables) {
|
|
96
|
-
const declaratorCandidate = variable.defs.find((def) => {
|
|
97
|
-
return (
|
|
98
|
-
def.node.type === 'VariableDeclarator' &&
|
|
99
|
-
// Allow declarations which are not initialized.
|
|
100
|
-
def.node.init &&
|
|
101
|
-
// Target function calls as "expensive".
|
|
102
|
-
def.node.init.type === 'CallExpression' &&
|
|
103
|
-
// Allow unused if part of an object destructuring.
|
|
104
|
-
!isExemptObjectDestructureDeclarator(def.node) &&
|
|
105
|
-
// Only target assignments preceding `return`.
|
|
106
|
-
def.node.range[1] < node.range[1]
|
|
107
|
-
);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
if (!declaratorCandidate) {
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (
|
|
115
|
-
excludePattern !== undefined &&
|
|
116
|
-
// eslint-disable-next-line security/detect-non-literal-regexp
|
|
117
|
-
new RegExp(excludePattern).test(
|
|
118
|
-
declaratorCandidate.node.init.callee.name
|
|
119
|
-
)
|
|
120
|
-
) {
|
|
121
|
-
continue;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// The first entry in `references` is the declaration
|
|
125
|
-
// itself, which can be ignored.
|
|
126
|
-
const identifiers = variable.references
|
|
127
|
-
.slice(1)
|
|
128
|
-
.map((reference) => reference.identifier);
|
|
129
|
-
|
|
130
|
-
// Merge with any JSX identifiers in scope, if any.
|
|
131
|
-
if (FUNCTION_SCOPE_JSX_IDENTIFIERS.has(functionScope)) {
|
|
132
|
-
const jsxIdentifiers =
|
|
133
|
-
FUNCTION_SCOPE_JSX_IDENTIFIERS.get(functionScope);
|
|
134
|
-
|
|
135
|
-
identifiers.push(...jsxIdentifiers);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const isUsedBeforeReturn = identifiers.some(
|
|
139
|
-
(identifier) => identifier.range[1] < node.range[1]
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
if (isUsedBeforeReturn) {
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
context.report(
|
|
147
|
-
declaratorCandidate.node,
|
|
148
|
-
'Variables should not be assigned until just prior its first reference. ' +
|
|
149
|
-
'An early return statement may leave this variable unused.'
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
},
|
|
153
|
-
};
|
|
154
|
-
},
|
|
155
|
-
});
|