eslint-plugin-unicorn 64.0.0 → 65.0.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.js +14 -0
- package/package.json +7 -3
- package/readme.md +183 -151
- package/rules/better-dom-traversing.js +291 -0
- package/rules/consistent-compound-words.js +452 -0
- package/rules/consistent-destructuring.js +97 -9
- package/rules/consistent-function-scoping.js +20 -0
- package/rules/consistent-json-file-read.js +245 -0
- package/rules/custom-error-definition.js +89 -4
- package/rules/dom-node-dataset.js +431 -0
- package/rules/escape-case.js +1 -1
- package/rules/expiring-todo-comments.js +42 -81
- package/rules/explicit-length-check.js +139 -6
- package/rules/filename-case.js +95 -21
- package/rules/import-style.js +21 -1
- package/rules/index.js +33 -3
- package/rules/isolated-functions.js +7 -5
- package/rules/no-anonymous-default-export.js +6 -6
- package/rules/no-array-callback-reference.js +89 -23
- package/rules/no-array-fill-with-reference-type.js +127 -0
- package/rules/no-array-for-each.js +17 -1
- package/rules/no-array-from-fill.js +76 -0
- package/rules/no-array-reduce.js +508 -2
- package/rules/no-blob-to-file.js +299 -0
- package/rules/no-canvas-to-image.js +152 -0
- package/rules/no-confusing-array-splice.js +164 -0
- package/rules/no-duplicate-set-values.js +57 -0
- package/rules/no-empty-file.js +31 -2
- package/rules/no-exports-in-scripts.js +34 -0
- package/rules/no-hex-escape.js +1 -1
- package/rules/no-incorrect-query-selector.js +347 -0
- package/rules/no-invalid-file-input-accept.js +475 -0
- package/rules/no-keyword-prefix.js +20 -5
- package/rules/no-late-current-target-access.js +166 -0
- package/rules/no-lonely-if.js +151 -50
- package/rules/no-manually-wrapped-comments.js +151 -0
- package/rules/no-null.js +14 -3
- package/rules/no-this-outside-of-class.js +75 -0
- package/rules/no-typeof-undefined.js +6 -4
- package/rules/no-unnecessary-nested-ternary.js +132 -0
- package/rules/no-unnecessary-polyfills.js +66 -20
- package/rules/no-unreadable-iife.js +33 -3
- package/rules/no-unused-array-method-return.js +301 -0
- package/rules/no-useless-promise-resolve-reject.js +5 -5
- package/rules/no-useless-spread.js +53 -4
- package/rules/no-useless-undefined.js +52 -45
- package/rules/numeric-separators-style.js +13 -4
- package/rules/prefer-array-last-methods.js +93 -0
- package/rules/prefer-array-some.js +66 -5
- package/rules/prefer-at.js +114 -55
- package/rules/prefer-dom-node-append.js +2 -2
- package/rules/prefer-dom-node-remove.js +24 -18
- package/rules/prefer-export-from.js +6 -6
- package/rules/prefer-get-or-insert-computed.js +159 -0
- package/rules/prefer-global-this.js +7 -46
- package/rules/prefer-https.js +83 -0
- package/rules/prefer-includes-over-repeated-comparisons.js +199 -0
- package/rules/prefer-iterator-concat.js +174 -0
- package/rules/prefer-iterator-to-array-at-end.js +123 -0
- package/rules/prefer-keyboard-event-key.js +202 -76
- package/rules/prefer-math-abs.js +397 -0
- package/rules/prefer-math-min-max.js +5 -3
- package/rules/prefer-node-protocol.js +6 -4
- package/rules/prefer-query-selector.js +119 -7
- package/rules/prefer-queue-microtask.js +204 -0
- package/rules/prefer-regexp-test.js +164 -18
- package/rules/prefer-set-has.js +411 -22
- package/rules/prefer-single-call.js +128 -38
- package/rules/prefer-split-limit.js +107 -0
- package/rules/prefer-spread.js +34 -4
- package/rules/prefer-string-match-all.js +352 -0
- package/rules/prefer-string-pad-start-end.js +380 -0
- package/rules/prefer-string-raw.js +90 -19
- package/rules/prefer-string-repeat.js +173 -0
- package/rules/prefer-string-replace-all.js +180 -12
- package/rules/prefer-string-slice.js +54 -3
- package/rules/prefer-top-level-await.js +129 -1
- package/rules/prevent-abbreviations.js +126 -134
- package/rules/require-css-escape.js +141 -0
- package/rules/require-module-specifiers.js +62 -0
- package/rules/require-passive-events.js +292 -0
- package/rules/shared/dom-events.js +7 -7
- package/rules/shared/identifier-checks.js +205 -0
- package/rules/shared/package-json.js +1 -1
- package/rules/string-content.js +34 -5
- package/rules/template-indent.js +52 -3
- package/rules/throw-new-error.js +4 -0
- package/rules/try-complexity.js +184 -0
- package/rules/utils/boolean.js +12 -12
- package/rules/utils/get-duplicate-array-elements.js +77 -0
- package/rules/utils/index.js +1 -0
- package/rules/utils/is-same-reference.js +6 -4
- package/rules/utils/should-add-parentheses-to-await-expression-argument.js +9 -9
- package/rules/utils/should-add-parentheses-to-call-expression-callee.js +12 -10
- package/rules/utils/should-add-parentheses-to-conditional-expression-child.js +9 -7
- package/rules/utils/should-add-parentheses-to-logical-expression-child.js +12 -8
- package/rules/utils/should-add-parentheses-to-unary-expression.js +10 -10
- package/rules/better-regex.js +0 -155
- package/rules/prefer-dom-node-dataset.js +0 -135
- package/rules/prefer-json-parse-buffer.js +0 -161
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
isBooleanExpression,
|
|
7
7
|
isControlFlowTest,
|
|
8
8
|
getBooleanAncestor,
|
|
9
|
+
isSameReference,
|
|
9
10
|
} from './utils/index.js';
|
|
10
11
|
import {fixSpaceAroundKeyword} from './fix/index.js';
|
|
11
12
|
import {isLiteral, isMemberExpression} from './ast/index.js';
|
|
@@ -48,8 +49,107 @@ const zeroStyle = {
|
|
|
48
49
|
test: node => isCompareRight(node, '===', 0),
|
|
49
50
|
};
|
|
50
51
|
|
|
51
|
-
|
|
52
|
+
const shapeProperties = new Set(['depth', 'height', 'width']);
|
|
53
|
+
|
|
54
|
+
function isLengthOrSizeMemberExpression(node) {
|
|
55
|
+
return isMemberExpression(node, {
|
|
56
|
+
properties: ['length', 'size'],
|
|
57
|
+
optional: false,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function isTypeScriptExpression(node) {
|
|
62
|
+
return node?.type === 'TSAsExpression'
|
|
63
|
+
|| node?.type === 'TSSatisfiesExpression'
|
|
64
|
+
|| node?.type === 'TSTypeAssertion'
|
|
65
|
+
|| node?.type === 'TSNonNullExpression';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function unwrapTypeScriptExpression(node) {
|
|
69
|
+
if (isTypeScriptExpression(node)) {
|
|
70
|
+
return unwrapTypeScriptExpression(node.expression);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return node;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function getLengthCheckParent(node, allowTypeScriptExpression) {
|
|
52
77
|
node = node.parent;
|
|
78
|
+
if (allowTypeScriptExpression) {
|
|
79
|
+
while (isTypeScriptExpression(node)) {
|
|
80
|
+
node = node.parent;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return node;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function getLogicalExpressionRoot(node) {
|
|
88
|
+
while (
|
|
89
|
+
isLogicalExpression(node.parent)
|
|
90
|
+
&& node.parent.operator === '&&'
|
|
91
|
+
) {
|
|
92
|
+
node = node.parent;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return node;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function getLogicalExpressionOperands(node) {
|
|
99
|
+
return [node.left, node.right].flatMap(child =>
|
|
100
|
+
child.type === 'LogicalExpression' && child.operator === node.operator
|
|
101
|
+
? getLogicalExpressionOperands(child)
|
|
102
|
+
: [child]);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function hasSameObjectShapePropertyCheck({node, lengthNode}) {
|
|
106
|
+
const root = getLogicalExpressionRoot(node);
|
|
107
|
+
if (
|
|
108
|
+
root.type !== 'LogicalExpression'
|
|
109
|
+
|| root.operator !== '&&'
|
|
110
|
+
) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return getLogicalExpressionOperands(root).some(operand =>
|
|
115
|
+
operand !== node
|
|
116
|
+
&& isMemberExpression(operand, {computed: false, optional: false})
|
|
117
|
+
&& operand.property.type === 'Identifier'
|
|
118
|
+
&& shapeProperties.has(operand.property.name)
|
|
119
|
+
&& isSameReference(operand.object, lengthNode.object));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function getLengthCheckMemberExpression(node) {
|
|
123
|
+
if (node.type === 'UnaryExpression' && node.operator === '!') {
|
|
124
|
+
return getLengthCheckMemberExpression(node.argument);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (
|
|
128
|
+
node.type === 'CallExpression'
|
|
129
|
+
&& node.callee.type === 'Identifier'
|
|
130
|
+
&& node.callee.name === 'Boolean'
|
|
131
|
+
&& node.arguments.length === 1
|
|
132
|
+
) {
|
|
133
|
+
return getLengthCheckMemberExpression(node.arguments[0]);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (node.type !== 'BinaryExpression') {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const left = unwrapTypeScriptExpression(node.left);
|
|
141
|
+
if (isLengthOrSizeMemberExpression(left)) {
|
|
142
|
+
return left;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const right = unwrapTypeScriptExpression(node.right);
|
|
146
|
+
if (isLengthOrSizeMemberExpression(right)) {
|
|
147
|
+
return right;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function getLengthCheckNode(node, {allowTypeScriptExpression = false} = {}) {
|
|
152
|
+
node = getLengthCheckParent(node, allowTypeScriptExpression);
|
|
53
153
|
|
|
54
154
|
// Zero length check
|
|
55
155
|
if (
|
|
@@ -81,7 +181,7 @@ function getLengthCheckNode(node) {
|
|
|
81
181
|
|| isCompareRight(node, '>=', 1)
|
|
82
182
|
// `0 !== foo.length`
|
|
83
183
|
|| isCompareLeft(node, '!==', 0)
|
|
84
|
-
// `0
|
|
184
|
+
// `0 != foo.length`
|
|
85
185
|
|| isCompareLeft(node, '!=', 0)
|
|
86
186
|
// `0 < foo.length`
|
|
87
187
|
|| isCompareLeft(node, '<', 0)
|
|
@@ -94,6 +194,35 @@ function getLengthCheckNode(node) {
|
|
|
94
194
|
return {};
|
|
95
195
|
}
|
|
96
196
|
|
|
197
|
+
function isSameLengthNonZeroCheck(node, lengthNode) {
|
|
198
|
+
const comparisonLengthNode = getLengthCheckMemberExpression(node);
|
|
199
|
+
if (!comparisonLengthNode || !isSameReference(comparisonLengthNode, lengthNode)) {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const {isZeroLengthCheck, node: lengthCheckNode} = getLengthCheckNode(comparisonLengthNode, {allowTypeScriptExpression: true});
|
|
204
|
+
if (!lengthCheckNode) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const {isNegative, node: ancestor} = getBooleanAncestor(lengthCheckNode);
|
|
209
|
+
return ancestor === node && isNegative === isZeroLengthCheck;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function isLengthGuardedByNonZeroCheck(lengthNode) {
|
|
213
|
+
const root = getLogicalExpressionRoot(lengthNode);
|
|
214
|
+
if (
|
|
215
|
+
root.type !== 'LogicalExpression'
|
|
216
|
+
|| root.operator !== '&&'
|
|
217
|
+
) {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return getLogicalExpressionOperands(root).some(operand =>
|
|
222
|
+
operand !== lengthNode
|
|
223
|
+
&& isSameLengthNonZeroCheck(operand, lengthNode));
|
|
224
|
+
}
|
|
225
|
+
|
|
97
226
|
function create(context) {
|
|
98
227
|
const options = context.options[0];
|
|
99
228
|
const nonZeroStyle = nonZeroStyles.get(options['non-zero']);
|
|
@@ -141,10 +270,7 @@ function create(context) {
|
|
|
141
270
|
|
|
142
271
|
context.on('MemberExpression', memberExpression => {
|
|
143
272
|
if (
|
|
144
|
-
!
|
|
145
|
-
properties: ['length', 'size'],
|
|
146
|
-
optional: false,
|
|
147
|
-
})
|
|
273
|
+
!isLengthOrSizeMemberExpression(memberExpression)
|
|
148
274
|
|| memberExpression.object.type === 'ThisExpression'
|
|
149
275
|
) {
|
|
150
276
|
return;
|
|
@@ -179,6 +305,13 @@ function create(context) {
|
|
|
179
305
|
}
|
|
180
306
|
|
|
181
307
|
if (node) {
|
|
308
|
+
if (
|
|
309
|
+
(node === lengthNode && isLengthGuardedByNonZeroCheck(lengthNode))
|
|
310
|
+
|| hasSameObjectShapePropertyCheck({node, lengthNode})
|
|
311
|
+
) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
182
315
|
const isUnsafeNegationInBinaryExpression = node.type === 'UnaryExpression'
|
|
183
316
|
&& node.operator === '!'
|
|
184
317
|
&& node.parent.type === 'BinaryExpression'
|
package/rules/filename-case.js
CHANGED
|
@@ -9,9 +9,11 @@ import {
|
|
|
9
9
|
import cartesianProductSamples from './utils/cartesian-product-samples.js';
|
|
10
10
|
|
|
11
11
|
const MESSAGE_ID = 'filename-case';
|
|
12
|
+
const MESSAGE_ID_DIRECTORY = 'directory-case';
|
|
12
13
|
const MESSAGE_ID_EXTENSION = 'filename-extension';
|
|
13
14
|
const messages = {
|
|
14
15
|
[MESSAGE_ID]: 'Filename is not in {{chosenCases}}. Rename it to {{renamedFilenames}}.',
|
|
16
|
+
[MESSAGE_ID_DIRECTORY]: 'Directory name `{{directory}}` is not in {{chosenCases}}. Rename it to {{renamedDirectories}}.',
|
|
15
17
|
[MESSAGE_ID_EXTENSION]: 'File extension `{{extension}}` is not in lowercase. Rename it to `{{filename}}`.',
|
|
16
18
|
};
|
|
17
19
|
|
|
@@ -59,13 +61,13 @@ function getChosenCases(options) {
|
|
|
59
61
|
return ['kebabCase'];
|
|
60
62
|
}
|
|
61
63
|
|
|
62
|
-
function
|
|
64
|
+
function validateName(words, caseFunctions) {
|
|
63
65
|
return words
|
|
64
66
|
.filter(({ignored}) => !ignored)
|
|
65
67
|
.every(({word}) => caseFunctions.some(caseFunction => caseFunction(word) === word));
|
|
66
68
|
}
|
|
67
69
|
|
|
68
|
-
function
|
|
70
|
+
function fixName(words, caseFunctions, {leading, trailing}) {
|
|
69
71
|
const replacements = words
|
|
70
72
|
.map(({word, ignored}) => ignored ? [word] : caseFunctions.map(caseFunction => caseFunction(word)));
|
|
71
73
|
|
|
@@ -99,10 +101,29 @@ function getFilenameParts(filenameWithExtension, {multipleFileExtensions}) {
|
|
|
99
101
|
return parts;
|
|
100
102
|
}
|
|
101
103
|
|
|
104
|
+
function isInsideCwd(relativePath) {
|
|
105
|
+
return relativePath !== ''
|
|
106
|
+
&& relativePath !== '..'
|
|
107
|
+
&& !relativePath.startsWith(`..${path.sep}`)
|
|
108
|
+
&& !path.isAbsolute(relativePath);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function getPathSegments(filenameWithExtension, cwd) {
|
|
112
|
+
const relativePath = path.relative(cwd, path.resolve(cwd, filenameWithExtension));
|
|
113
|
+
|
|
114
|
+
if (!isInsideCwd(relativePath)) {
|
|
115
|
+
return [path.basename(filenameWithExtension)];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return relativePath
|
|
119
|
+
.split(path.sep)
|
|
120
|
+
.filter(segment => segment !== '.');
|
|
121
|
+
}
|
|
122
|
+
|
|
102
123
|
const leadingUnderscoresRegex = /^(?<leading>_+)(?<tailing>.*)$/;
|
|
103
|
-
function
|
|
104
|
-
const result = leadingUnderscoresRegex.exec(
|
|
105
|
-
const {leading = '', tailing =
|
|
124
|
+
function splitName(name) {
|
|
125
|
+
const result = leadingUnderscoresRegex.exec(name) || {groups: {}};
|
|
126
|
+
const {leading = '', tailing = name} = result.groups;
|
|
106
127
|
|
|
107
128
|
const words = [];
|
|
108
129
|
|
|
@@ -135,6 +156,33 @@ Turns `[a, b, c]` into `a, b, or c`.
|
|
|
135
156
|
*/
|
|
136
157
|
const englishishJoinWords = words => new Intl.ListFormat('en-US', {type: 'disjunction'}).format(words);
|
|
137
158
|
|
|
159
|
+
function getCaseNames(chosenCases) {
|
|
160
|
+
return englishishJoinWords(chosenCases.map(x => cases[x].name));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function getInvalidDirectoryReport(directory, chosenCases, chosenCasesFunctions) {
|
|
164
|
+
const {leading, words} = splitName(directory);
|
|
165
|
+
|
|
166
|
+
if (directory.startsWith('$') || validateName(words, chosenCasesFunctions)) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const renamedDirectories = fixName(words, chosenCasesFunctions, {
|
|
171
|
+
leading,
|
|
172
|
+
trailing: '',
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
loc: {column: 0, line: 1},
|
|
177
|
+
messageId: MESSAGE_ID_DIRECTORY,
|
|
178
|
+
data: {
|
|
179
|
+
directory,
|
|
180
|
+
chosenCases: getCaseNames(chosenCases),
|
|
181
|
+
renamedDirectories: englishishJoinWords(renamedDirectories.map(x => `\`${x}\``)),
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
138
186
|
/** @param {import('eslint').Rule.RuleContext} context */
|
|
139
187
|
const create = context => {
|
|
140
188
|
const options = context.options[0] || {};
|
|
@@ -147,6 +195,7 @@ const create = context => {
|
|
|
147
195
|
return new RegExp(item, 'u');
|
|
148
196
|
});
|
|
149
197
|
const multipleFileExtensions = options.multipleFileExtensions !== false;
|
|
198
|
+
const checkDirectories = options.checkDirectories !== false;
|
|
150
199
|
const chosenCasesFunctions = chosenCases.map(case_ => cases[case_].fn);
|
|
151
200
|
const filenameWithExtension = context.physicalFilename;
|
|
152
201
|
|
|
@@ -155,19 +204,35 @@ const create = context => {
|
|
|
155
204
|
}
|
|
156
205
|
|
|
157
206
|
context.on('Program', () => {
|
|
207
|
+
const pathSegments = getPathSegments(filenameWithExtension, context.cwd);
|
|
208
|
+
const basenameWithExtension = pathSegments.at(-1);
|
|
158
209
|
const {
|
|
159
210
|
basename,
|
|
160
211
|
filename,
|
|
161
212
|
middle,
|
|
162
213
|
extension,
|
|
163
|
-
} = getFilenameParts(
|
|
214
|
+
} = getFilenameParts(basenameWithExtension, {multipleFileExtensions});
|
|
215
|
+
|
|
216
|
+
if (pathSegments.some(segment => ignore.some(regexp => regexp.test(segment)))) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
164
219
|
|
|
165
|
-
if (
|
|
220
|
+
if (checkDirectories) {
|
|
221
|
+
for (const directory of pathSegments.slice(0, -1)) {
|
|
222
|
+
const report = getInvalidDirectoryReport(directory, chosenCases, chosenCasesFunctions);
|
|
223
|
+
|
|
224
|
+
if (report) {
|
|
225
|
+
return report;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (ignoredByDefault.has(basename)) {
|
|
166
231
|
return;
|
|
167
232
|
}
|
|
168
233
|
|
|
169
|
-
const {leading, words} =
|
|
170
|
-
const isValid =
|
|
234
|
+
const {leading, words} = splitName(filename);
|
|
235
|
+
const isValid = filename.startsWith('$') || validateName(words, chosenCasesFunctions);
|
|
171
236
|
|
|
172
237
|
if (isValid) {
|
|
173
238
|
if (!isLowerCase(extension)) {
|
|
@@ -181,7 +246,7 @@ const create = context => {
|
|
|
181
246
|
return;
|
|
182
247
|
}
|
|
183
248
|
|
|
184
|
-
const renamedFilenames =
|
|
249
|
+
const renamedFilenames = fixName(words, chosenCasesFunctions, {
|
|
185
250
|
leading,
|
|
186
251
|
trailing: middle + extension.toLowerCase(),
|
|
187
252
|
});
|
|
@@ -192,7 +257,7 @@ const create = context => {
|
|
|
192
257
|
loc: {column: 0, line: 1},
|
|
193
258
|
messageId: MESSAGE_ID,
|
|
194
259
|
data: {
|
|
195
|
-
chosenCases:
|
|
260
|
+
chosenCases: getCaseNames(chosenCases),
|
|
196
261
|
renamedFilenames: englishishJoinWords(renamedFilenames.map(x => `\`${x}\``)),
|
|
197
262
|
},
|
|
198
263
|
};
|
|
@@ -201,7 +266,8 @@ const create = context => {
|
|
|
201
266
|
|
|
202
267
|
const schema = [
|
|
203
268
|
{
|
|
204
|
-
|
|
269
|
+
description: 'The rule options.',
|
|
270
|
+
anyOf: [
|
|
205
271
|
{
|
|
206
272
|
properties: {
|
|
207
273
|
case: {
|
|
@@ -211,17 +277,21 @@ const schema = [
|
|
|
211
277
|
'kebabCase',
|
|
212
278
|
'pascalCase',
|
|
213
279
|
],
|
|
214
|
-
description: 'The filename case style.',
|
|
280
|
+
description: 'The filename and directory name case style.',
|
|
215
281
|
},
|
|
216
282
|
ignore: {
|
|
217
283
|
type: 'array',
|
|
218
284
|
uniqueItems: true,
|
|
219
|
-
description: '
|
|
285
|
+
description: 'Path segment patterns to ignore.',
|
|
220
286
|
},
|
|
221
287
|
multipleFileExtensions: {
|
|
222
288
|
type: 'boolean',
|
|
223
289
|
description: 'Whether to treat additional, dot-separated parts of a filename as file extensions.',
|
|
224
290
|
},
|
|
291
|
+
checkDirectories: {
|
|
292
|
+
type: 'boolean',
|
|
293
|
+
description: 'Whether to check directory names.',
|
|
294
|
+
},
|
|
225
295
|
},
|
|
226
296
|
additionalProperties: false,
|
|
227
297
|
},
|
|
@@ -231,33 +301,37 @@ const schema = [
|
|
|
231
301
|
properties: {
|
|
232
302
|
camelCase: {
|
|
233
303
|
type: 'boolean',
|
|
234
|
-
description: 'Whether to allow camelCase filenames.',
|
|
304
|
+
description: 'Whether to allow camelCase filenames and directory names.',
|
|
235
305
|
},
|
|
236
306
|
snakeCase: {
|
|
237
307
|
type: 'boolean',
|
|
238
|
-
description: 'Whether to allow snake_case filenames.',
|
|
308
|
+
description: 'Whether to allow snake_case filenames and directory names.',
|
|
239
309
|
},
|
|
240
310
|
kebabCase: {
|
|
241
311
|
type: 'boolean',
|
|
242
|
-
description: 'Whether to allow kebab-case filenames.',
|
|
312
|
+
description: 'Whether to allow kebab-case filenames and directory names.',
|
|
243
313
|
},
|
|
244
314
|
pascalCase: {
|
|
245
315
|
type: 'boolean',
|
|
246
|
-
description: 'Whether to allow PascalCase filenames.',
|
|
316
|
+
description: 'Whether to allow PascalCase filenames and directory names.',
|
|
247
317
|
},
|
|
248
318
|
},
|
|
249
319
|
additionalProperties: false,
|
|
250
|
-
description: 'The allowed filename case styles.',
|
|
320
|
+
description: 'The allowed filename and directory name case styles.',
|
|
251
321
|
},
|
|
252
322
|
ignore: {
|
|
253
323
|
type: 'array',
|
|
254
324
|
uniqueItems: true,
|
|
255
|
-
description: '
|
|
325
|
+
description: 'Path segment patterns to ignore.',
|
|
256
326
|
},
|
|
257
327
|
multipleFileExtensions: {
|
|
258
328
|
type: 'boolean',
|
|
259
329
|
description: 'Whether to treat additional, dot-separated parts of a filename as file extensions.',
|
|
260
330
|
},
|
|
331
|
+
checkDirectories: {
|
|
332
|
+
type: 'boolean',
|
|
333
|
+
description: 'Whether to check directory names.',
|
|
334
|
+
},
|
|
261
335
|
},
|
|
262
336
|
additionalProperties: false,
|
|
263
337
|
},
|
|
@@ -271,7 +345,7 @@ const config = {
|
|
|
271
345
|
meta: {
|
|
272
346
|
type: 'suggestion',
|
|
273
347
|
docs: {
|
|
274
|
-
description: 'Enforce a case style for filenames.',
|
|
348
|
+
description: 'Enforce a case style for filenames and directory names.',
|
|
275
349
|
recommended: true,
|
|
276
350
|
},
|
|
277
351
|
schema,
|
package/rules/import-style.js
CHANGED
|
@@ -2,8 +2,10 @@ import {getStringIfConstant} from '@eslint-community/eslint-utils';
|
|
|
2
2
|
import {isCallExpression} from './ast/index.js';
|
|
3
3
|
|
|
4
4
|
const MESSAGE_ID = 'importStyle';
|
|
5
|
+
const MESSAGE_ID_BANNED = 'importStyleBanned';
|
|
5
6
|
const messages = {
|
|
6
7
|
[MESSAGE_ID]: 'Use {{allowedStyles}} import for module `{{moduleName}}`.',
|
|
8
|
+
[MESSAGE_ID_BANNED]: 'All import styles are disabled for module `{{moduleName}}`. Use the `no-restricted-imports` rule to disallow a module.',
|
|
7
9
|
};
|
|
8
10
|
|
|
9
11
|
const getActualImportDeclarationStyles = importDeclaration => {
|
|
@@ -144,6 +146,12 @@ const create = context => {
|
|
|
144
146
|
.map(name => [name, styles[name] === false ? {} : {...defaultStyles[name], ...styles[name]}]))
|
|
145
147
|
: styles;
|
|
146
148
|
|
|
149
|
+
const bannedModules = new Set(
|
|
150
|
+
Object.entries(styles)
|
|
151
|
+
.filter(([, moduleStyles]) => ['unassigned', 'default', 'namespace', 'named'].every(key => moduleStyles[key] === false))
|
|
152
|
+
.map(([name]) => name),
|
|
153
|
+
);
|
|
154
|
+
|
|
147
155
|
styles = new Map(Object.entries(styles).map(([moduleName, styles]) =>
|
|
148
156
|
[moduleName, new Set(Object.entries(styles).filter(([, isAllowed]) => isAllowed).map(([style]) => style))]));
|
|
149
157
|
|
|
@@ -151,7 +159,19 @@ const create = context => {
|
|
|
151
159
|
|
|
152
160
|
// eslint-disable-next-line max-params
|
|
153
161
|
const report = (node, moduleName, actualImportStyles, allowedImportStyles, isRequire = false) => {
|
|
154
|
-
if (!allowedImportStyles
|
|
162
|
+
if (!allowedImportStyles) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (allowedImportStyles.size === 0) {
|
|
167
|
+
if (bannedModules.has(moduleName)) {
|
|
168
|
+
context.report({
|
|
169
|
+
node,
|
|
170
|
+
messageId: MESSAGE_ID_BANNED,
|
|
171
|
+
data: {moduleName},
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
155
175
|
return;
|
|
156
176
|
}
|
|
157
177
|
|
package/rules/index.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
// Generated file, DO NOT edit
|
|
2
2
|
|
|
3
|
-
export {default as 'better-
|
|
3
|
+
export {default as 'better-dom-traversing'} from './better-dom-traversing.js';
|
|
4
4
|
export {default as 'catch-error-name'} from './catch-error-name.js';
|
|
5
5
|
export {default as 'consistent-assert'} from './consistent-assert.js';
|
|
6
|
+
export {default as 'consistent-compound-words'} from './consistent-compound-words.js';
|
|
6
7
|
export {default as 'consistent-date-clone'} from './consistent-date-clone.js';
|
|
7
8
|
export {default as 'consistent-destructuring'} from './consistent-destructuring.js';
|
|
8
9
|
export {default as 'consistent-empty-array-spread'} from './consistent-empty-array-spread.js';
|
|
9
10
|
export {default as 'consistent-existence-index-check'} from './consistent-existence-index-check.js';
|
|
10
11
|
export {default as 'consistent-function-scoping'} from './consistent-function-scoping.js';
|
|
12
|
+
export {default as 'consistent-json-file-read'} from './consistent-json-file-read.js';
|
|
11
13
|
export {default as 'consistent-template-literal-escape'} from './consistent-template-literal-escape.js';
|
|
12
14
|
export {default as 'custom-error-definition'} from './custom-error-definition.js';
|
|
15
|
+
export {default as 'dom-node-dataset'} from './dom-node-dataset.js';
|
|
13
16
|
export {default as 'empty-brace-spaces'} from './empty-brace-spaces.js';
|
|
14
17
|
export {default as 'error-message'} from './error-message.js';
|
|
15
18
|
export {default as 'escape-case'} from './escape-case.js';
|
|
@@ -23,25 +26,36 @@ export {default as 'no-abusive-eslint-disable'} from './no-abusive-eslint-disabl
|
|
|
23
26
|
export {default as 'no-accessor-recursion'} from './no-accessor-recursion.js';
|
|
24
27
|
export {default as 'no-anonymous-default-export'} from './no-anonymous-default-export.js';
|
|
25
28
|
export {default as 'no-array-callback-reference'} from './no-array-callback-reference.js';
|
|
29
|
+
export {default as 'no-array-fill-with-reference-type'} from './no-array-fill-with-reference-type.js';
|
|
26
30
|
export {default as 'no-array-for-each'} from './no-array-for-each.js';
|
|
31
|
+
export {default as 'no-array-from-fill'} from './no-array-from-fill.js';
|
|
27
32
|
export {default as 'no-array-method-this-argument'} from './no-array-method-this-argument.js';
|
|
28
33
|
export {default as 'no-array-reduce'} from './no-array-reduce.js';
|
|
29
34
|
export {default as 'no-array-reverse'} from './no-array-reverse.js';
|
|
30
35
|
export {default as 'no-array-sort'} from './no-array-sort.js';
|
|
31
36
|
export {default as 'no-await-expression-member'} from './no-await-expression-member.js';
|
|
32
37
|
export {default as 'no-await-in-promise-methods'} from './no-await-in-promise-methods.js';
|
|
38
|
+
export {default as 'no-blob-to-file'} from './no-blob-to-file.js';
|
|
39
|
+
export {default as 'no-canvas-to-image'} from './no-canvas-to-image.js';
|
|
40
|
+
export {default as 'no-confusing-array-splice'} from './no-confusing-array-splice.js';
|
|
33
41
|
export {default as 'no-console-spaces'} from './no-console-spaces.js';
|
|
34
42
|
export {default as 'no-document-cookie'} from './no-document-cookie.js';
|
|
43
|
+
export {default as 'no-duplicate-set-values'} from './no-duplicate-set-values.js';
|
|
35
44
|
export {default as 'no-empty-file'} from './no-empty-file.js';
|
|
45
|
+
export {default as 'no-exports-in-scripts'} from './no-exports-in-scripts.js';
|
|
36
46
|
export {default as 'no-for-loop'} from './no-for-loop.js';
|
|
37
47
|
export {default as 'no-hex-escape'} from './no-hex-escape.js';
|
|
38
48
|
export {default as 'no-immediate-mutation'} from './no-immediate-mutation.js';
|
|
49
|
+
export {default as 'no-incorrect-query-selector'} from './no-incorrect-query-selector.js';
|
|
39
50
|
export {default as 'no-instanceof-builtins'} from './no-instanceof-builtins.js';
|
|
40
51
|
export {default as 'no-invalid-fetch-options'} from './no-invalid-fetch-options.js';
|
|
52
|
+
export {default as 'no-invalid-file-input-accept'} from './no-invalid-file-input-accept.js';
|
|
41
53
|
export {default as 'no-invalid-remove-event-listener'} from './no-invalid-remove-event-listener.js';
|
|
42
54
|
export {default as 'no-keyword-prefix'} from './no-keyword-prefix.js';
|
|
55
|
+
export {default as 'no-late-current-target-access'} from './no-late-current-target-access.js';
|
|
43
56
|
export {default as 'no-lonely-if'} from './no-lonely-if.js';
|
|
44
57
|
export {default as 'no-magic-array-flat-depth'} from './no-magic-array-flat-depth.js';
|
|
58
|
+
export {default as 'no-manually-wrapped-comments'} from './no-manually-wrapped-comments.js';
|
|
45
59
|
export {default as 'no-named-default'} from './no-named-default.js';
|
|
46
60
|
export {default as 'no-negated-condition'} from './no-negated-condition.js';
|
|
47
61
|
export {default as 'no-negation-in-equality-check'} from './no-negation-in-equality-check.js';
|
|
@@ -55,14 +69,17 @@ export {default as 'no-single-promise-in-promise-methods'} from './no-single-pro
|
|
|
55
69
|
export {default as 'no-static-only-class'} from './no-static-only-class.js';
|
|
56
70
|
export {default as 'no-thenable'} from './no-thenable.js';
|
|
57
71
|
export {default as 'no-this-assignment'} from './no-this-assignment.js';
|
|
72
|
+
export {default as 'no-this-outside-of-class'} from './no-this-outside-of-class.js';
|
|
58
73
|
export {default as 'no-typeof-undefined'} from './no-typeof-undefined.js';
|
|
59
74
|
export {default as 'no-unnecessary-array-flat-depth'} from './no-unnecessary-array-flat-depth.js';
|
|
60
75
|
export {default as 'no-unnecessary-array-splice-count'} from './no-unnecessary-array-splice-count.js';
|
|
61
76
|
export {default as 'no-unnecessary-await'} from './no-unnecessary-await.js';
|
|
77
|
+
export {default as 'no-unnecessary-nested-ternary'} from './no-unnecessary-nested-ternary.js';
|
|
62
78
|
export {default as 'no-unnecessary-polyfills'} from './no-unnecessary-polyfills.js';
|
|
63
79
|
export {default as 'no-unnecessary-slice-end'} from './no-unnecessary-slice-end.js';
|
|
64
80
|
export {default as 'no-unreadable-array-destructuring'} from './no-unreadable-array-destructuring.js';
|
|
65
81
|
export {default as 'no-unreadable-iife'} from './no-unreadable-iife.js';
|
|
82
|
+
export {default as 'no-unused-array-method-return'} from './no-unused-array-method-return.js';
|
|
66
83
|
export {default as 'no-unused-properties'} from './no-unused-properties.js';
|
|
67
84
|
export {default as 'no-useless-collection-argument'} from './no-useless-collection-argument.js';
|
|
68
85
|
export {default as 'no-useless-error-capture-stack-trace'} from './no-useless-error-capture-stack-trace.js';
|
|
@@ -81,6 +98,7 @@ export {default as 'prefer-array-find'} from './prefer-array-find.js';
|
|
|
81
98
|
export {default as 'prefer-array-flat-map'} from './prefer-array-flat-map.js';
|
|
82
99
|
export {default as 'prefer-array-flat'} from './prefer-array-flat.js';
|
|
83
100
|
export {default as 'prefer-array-index-of'} from './prefer-array-index-of.js';
|
|
101
|
+
export {default as 'prefer-array-last-methods'} from './prefer-array-last-methods.js';
|
|
84
102
|
export {default as 'prefer-array-some'} from './prefer-array-some.js';
|
|
85
103
|
export {default as 'prefer-at'} from './prefer-at.js';
|
|
86
104
|
export {default as 'prefer-bigint-literals'} from './prefer-bigint-literals.js';
|
|
@@ -91,17 +109,21 @@ export {default as 'prefer-code-point'} from './prefer-code-point.js';
|
|
|
91
109
|
export {default as 'prefer-date-now'} from './prefer-date-now.js';
|
|
92
110
|
export {default as 'prefer-default-parameters'} from './prefer-default-parameters.js';
|
|
93
111
|
export {default as 'prefer-dom-node-append'} from './prefer-dom-node-append.js';
|
|
94
|
-
export {default as 'prefer-dom-node-dataset'} from './prefer-dom-node-dataset.js';
|
|
95
112
|
export {default as 'prefer-dom-node-remove'} from './prefer-dom-node-remove.js';
|
|
96
113
|
export {default as 'prefer-dom-node-text-content'} from './prefer-dom-node-text-content.js';
|
|
97
114
|
export {default as 'prefer-event-target'} from './prefer-event-target.js';
|
|
98
115
|
export {default as 'prefer-export-from'} from './prefer-export-from.js';
|
|
116
|
+
export {default as 'prefer-get-or-insert-computed'} from './prefer-get-or-insert-computed.js';
|
|
99
117
|
export {default as 'prefer-global-this'} from './prefer-global-this.js';
|
|
118
|
+
export {default as 'prefer-https'} from './prefer-https.js';
|
|
100
119
|
export {default as 'prefer-import-meta-properties'} from './prefer-import-meta-properties.js';
|
|
120
|
+
export {default as 'prefer-includes-over-repeated-comparisons'} from './prefer-includes-over-repeated-comparisons.js';
|
|
101
121
|
export {default as 'prefer-includes'} from './prefer-includes.js';
|
|
102
|
-
export {default as 'prefer-
|
|
122
|
+
export {default as 'prefer-iterator-concat'} from './prefer-iterator-concat.js';
|
|
123
|
+
export {default as 'prefer-iterator-to-array-at-end'} from './prefer-iterator-to-array-at-end.js';
|
|
103
124
|
export {default as 'prefer-keyboard-event-key'} from './prefer-keyboard-event-key.js';
|
|
104
125
|
export {default as 'prefer-logical-operator-over-ternary'} from './prefer-logical-operator-over-ternary.js';
|
|
126
|
+
export {default as 'prefer-math-abs'} from './prefer-math-abs.js';
|
|
105
127
|
export {default as 'prefer-math-min-max'} from './prefer-math-min-max.js';
|
|
106
128
|
export {default as 'prefer-math-trunc'} from './prefer-math-trunc.js';
|
|
107
129
|
export {default as 'prefer-modern-dom-apis'} from './prefer-modern-dom-apis.js';
|
|
@@ -115,6 +137,7 @@ export {default as 'prefer-object-from-entries'} from './prefer-object-from-entr
|
|
|
115
137
|
export {default as 'prefer-optional-catch-binding'} from './prefer-optional-catch-binding.js';
|
|
116
138
|
export {default as 'prefer-prototype-methods'} from './prefer-prototype-methods.js';
|
|
117
139
|
export {default as 'prefer-query-selector'} from './prefer-query-selector.js';
|
|
140
|
+
export {default as 'prefer-queue-microtask'} from './prefer-queue-microtask.js';
|
|
118
141
|
export {default as 'prefer-reflect-apply'} from './prefer-reflect-apply.js';
|
|
119
142
|
export {default as 'prefer-regexp-test'} from './prefer-regexp-test.js';
|
|
120
143
|
export {default as 'prefer-response-static-json'} from './prefer-response-static-json.js';
|
|
@@ -122,8 +145,12 @@ export {default as 'prefer-set-has'} from './prefer-set-has.js';
|
|
|
122
145
|
export {default as 'prefer-set-size'} from './prefer-set-size.js';
|
|
123
146
|
export {default as 'prefer-simple-condition-first'} from './prefer-simple-condition-first.js';
|
|
124
147
|
export {default as 'prefer-single-call'} from './prefer-single-call.js';
|
|
148
|
+
export {default as 'prefer-split-limit'} from './prefer-split-limit.js';
|
|
125
149
|
export {default as 'prefer-spread'} from './prefer-spread.js';
|
|
150
|
+
export {default as 'prefer-string-match-all'} from './prefer-string-match-all.js';
|
|
151
|
+
export {default as 'prefer-string-pad-start-end'} from './prefer-string-pad-start-end.js';
|
|
126
152
|
export {default as 'prefer-string-raw'} from './prefer-string-raw.js';
|
|
153
|
+
export {default as 'prefer-string-repeat'} from './prefer-string-repeat.js';
|
|
127
154
|
export {default as 'prefer-string-replace-all'} from './prefer-string-replace-all.js';
|
|
128
155
|
export {default as 'prefer-string-slice'} from './prefer-string-slice.js';
|
|
129
156
|
export {default as 'prefer-string-starts-ends-with'} from './prefer-string-starts-ends-with.js';
|
|
@@ -136,9 +163,11 @@ export {default as 'prefer-type-error'} from './prefer-type-error.js';
|
|
|
136
163
|
export {default as 'prevent-abbreviations'} from './prevent-abbreviations.js';
|
|
137
164
|
export {default as 'relative-url-style'} from './relative-url-style.js';
|
|
138
165
|
export {default as 'require-array-join-separator'} from './require-array-join-separator.js';
|
|
166
|
+
export {default as 'require-css-escape'} from './require-css-escape.js';
|
|
139
167
|
export {default as 'require-module-attributes'} from './require-module-attributes.js';
|
|
140
168
|
export {default as 'require-module-specifiers'} from './require-module-specifiers.js';
|
|
141
169
|
export {default as 'require-number-to-fixed-digits-argument'} from './require-number-to-fixed-digits-argument.js';
|
|
170
|
+
export {default as 'require-passive-events'} from './require-passive-events.js';
|
|
142
171
|
export {default as 'require-post-message-target-origin'} from './require-post-message-target-origin.js';
|
|
143
172
|
export {default as 'string-content'} from './string-content.js';
|
|
144
173
|
export {default as 'switch-case-braces'} from './switch-case-braces.js';
|
|
@@ -146,3 +175,4 @@ export {default as 'switch-case-break-position'} from './switch-case-break-posit
|
|
|
146
175
|
export {default as 'template-indent'} from './template-indent.js';
|
|
147
176
|
export {default as 'text-encoding-identifier-case'} from './text-encoding-identifier-case.js';
|
|
148
177
|
export {default as 'throw-new-error'} from './throw-new-error.js';
|
|
178
|
+
export {default as 'try-complexity'} from './try-complexity.js';
|
|
@@ -53,7 +53,7 @@ const create = context => {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
const globalsValue = allowedGlobals[identifier.name];
|
|
56
|
-
const isGlobalWritable =
|
|
56
|
+
const isGlobalWritable = [true, 'writable', 'writeable'].includes(globalsValue);
|
|
57
57
|
if (isGlobalWritable) {
|
|
58
58
|
continue;
|
|
59
59
|
}
|
|
@@ -82,10 +82,12 @@ const create = context => {
|
|
|
82
82
|
let commentableNode = node;
|
|
83
83
|
while (
|
|
84
84
|
!isComment(previousToken)
|
|
85
|
-
&&
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
85
|
+
&& [
|
|
86
|
+
'VariableDeclarator',
|
|
87
|
+
'VariableDeclaration',
|
|
88
|
+
'ExportNamedDeclaration',
|
|
89
|
+
'ExportDefaultDeclaration',
|
|
90
|
+
].includes(commentableNode.parent.type)
|
|
89
91
|
) {
|
|
90
92
|
commentableNode = commentableNode.parent;
|
|
91
93
|
previousToken = sourceCode.getTokenBefore(commentableNode, {includeComments: true});
|
|
@@ -23,12 +23,12 @@ const messages = {
|
|
|
23
23
|
const isClassKeywordToken = token => token.type === 'Keyword' && token.value === 'class';
|
|
24
24
|
const isAnonymousClassOrFunction = node =>
|
|
25
25
|
(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
)
|
|
26
|
+
[
|
|
27
|
+
'FunctionDeclaration',
|
|
28
|
+
'FunctionExpression',
|
|
29
|
+
'ClassDeclaration',
|
|
30
|
+
'ClassExpression',
|
|
31
|
+
].includes(node.type)
|
|
32
32
|
&& !node.id
|
|
33
33
|
)
|
|
34
34
|
|| node.type === 'ArrowFunctionExpression';
|