eslint-plugin-no-mistakes 0.92.0 → 0.93.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/package.json
CHANGED
|
@@ -2,12 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
const { matches, expressionInfo } = require("./forbidden-member-chains-match");
|
|
4
4
|
|
|
5
|
-
const PARAM_PARENTS = new Set([
|
|
6
|
-
"FunctionDeclaration",
|
|
7
|
-
"FunctionExpression",
|
|
8
|
-
"ArrowFunctionExpression",
|
|
9
|
-
]);
|
|
10
|
-
|
|
11
5
|
function patternKey(prop) {
|
|
12
6
|
const key = prop.key;
|
|
13
7
|
if (key.type === "Identifier") return key.name;
|
|
@@ -24,8 +18,19 @@ function objectPattern(node) {
|
|
|
24
18
|
}
|
|
25
19
|
|
|
26
20
|
function destructureVisitors({ compiled, context, suppressed, info }) {
|
|
21
|
+
// The outer pattern and a default can reach the same property and chain.
|
|
22
|
+
// `const { panel: { open } = other.panel } = other` reports that pair once.
|
|
23
|
+
const reported = new WeakMap();
|
|
24
|
+
|
|
27
25
|
function report(node, chain) {
|
|
28
26
|
if (suppressed(node)) return;
|
|
27
|
+
let seen = reported.get(node);
|
|
28
|
+
if (seen?.has(chain)) return;
|
|
29
|
+
if (!seen) {
|
|
30
|
+
seen = new Set();
|
|
31
|
+
reported.set(node, seen);
|
|
32
|
+
}
|
|
33
|
+
seen.add(chain);
|
|
29
34
|
context.report({ node, messageId: "forbiddenChain", data: { message: chain.message } });
|
|
30
35
|
}
|
|
31
36
|
|
|
@@ -63,8 +68,11 @@ function destructureVisitors({ compiled, context, suppressed, info }) {
|
|
|
63
68
|
const pattern = objectPattern(node.left);
|
|
64
69
|
if (pattern) check(pattern, node.right);
|
|
65
70
|
},
|
|
71
|
+
// A default is a receiver at any depth, not only on a function parameter.
|
|
72
|
+
// The parser only allows one in a parameter, property, or array element.
|
|
73
|
+
// `const { x: { open } = other.panel } = {}` reports `*.panel.open`.
|
|
74
|
+
// An array element is too: `const [{ open } = other.panel] = []`.
|
|
66
75
|
AssignmentPattern(node) {
|
|
67
|
-
if (!PARAM_PARENTS.has(node.parent?.type)) return;
|
|
68
76
|
const pattern = objectPattern(node.left);
|
|
69
77
|
if (pattern) check(pattern, node.right);
|
|
70
78
|
},
|
|
@@ -17,29 +17,19 @@ function nonStar() {
|
|
|
17
17
|
return text(128, "^(?!\\*$)[\\s\\S]+$");
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
items: [{ enum: ["*"] }],
|
|
34
|
-
additionalItems: nonStar(),
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function pathSchema(pattern, starMin, plainMax, starMax) {
|
|
39
|
-
return {
|
|
40
|
-
anyOf: [pathString(pattern), segments(plainMax), starThen(starMin, starMax)],
|
|
41
|
-
};
|
|
42
|
-
}
|
|
20
|
+
// Segment rules run in assertPath so a bad path names the segment. A JSON Schema
|
|
21
|
+
// anyOf only says the value matched none of the alternatives.
|
|
22
|
+
const pathValue = {
|
|
23
|
+
anyOf: [
|
|
24
|
+
{ type: "string", minLength: 1, maxLength: 256 },
|
|
25
|
+
{
|
|
26
|
+
type: "array",
|
|
27
|
+
minItems: 1,
|
|
28
|
+
maxItems: 32,
|
|
29
|
+
items: { type: "string", minLength: 1, maxLength: 128 },
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
};
|
|
43
33
|
|
|
44
34
|
function chain(properties, required) {
|
|
45
35
|
return {
|
|
@@ -60,20 +50,17 @@ const schema = [
|
|
|
60
50
|
maxItems: 64,
|
|
61
51
|
items: {
|
|
62
52
|
anyOf: [
|
|
63
|
-
chain({ path:
|
|
64
|
-
"path",
|
|
65
|
-
"message",
|
|
66
|
-
]),
|
|
53
|
+
chain({ path: pathValue }, ["path", "message"]),
|
|
67
54
|
chain(
|
|
68
55
|
{
|
|
69
|
-
path:
|
|
56
|
+
path: pathValue,
|
|
70
57
|
receiver: { type: "string", enum: ["*"] },
|
|
71
58
|
},
|
|
72
59
|
["path", "message", "receiver"],
|
|
73
60
|
),
|
|
74
61
|
chain(
|
|
75
62
|
{
|
|
76
|
-
path:
|
|
63
|
+
path: pathValue,
|
|
77
64
|
receiver: nonStar(),
|
|
78
65
|
},
|
|
79
66
|
["path", "message", "receiver"],
|
|
@@ -85,7 +72,48 @@ const schema = [
|
|
|
85
72
|
},
|
|
86
73
|
];
|
|
87
74
|
|
|
75
|
+
function shownPath(path) {
|
|
76
|
+
return Array.isArray(path) ? path.join(".") : String(path);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function assertPath(path, named) {
|
|
80
|
+
const parts = Array.isArray(path) ? path : String(path).split(".");
|
|
81
|
+
const shown = shownPath(path);
|
|
82
|
+
const empty = parts.findIndex((part) => part === "");
|
|
83
|
+
if (empty !== -1) throw new Error(`empty segment at index ${empty} in '${shown}'`);
|
|
84
|
+
const star = parts.findIndex((part, index) => part === "*" && index !== 0);
|
|
85
|
+
if (star !== -1) {
|
|
86
|
+
throw new Error(`'*' segment at index ${star} in '${shown}'; '*' is only a leading segment`);
|
|
87
|
+
}
|
|
88
|
+
if (typeof path === "string") {
|
|
89
|
+
const pattern = new RegExp(named ? NAMED_PATH : OPEN_PATH);
|
|
90
|
+
if (pattern.test(path)) return;
|
|
91
|
+
const limit = named && parts[0] !== "*" ? MAX_SEGMENTS - 1 : MAX_SEGMENTS;
|
|
92
|
+
if (parts.length > limit) {
|
|
93
|
+
throw new Error(`path '${shown}' has ${parts.length} segments; the maximum is ${limit}`);
|
|
94
|
+
}
|
|
95
|
+
throw new Error(`malformed path '${shown}'`);
|
|
96
|
+
}
|
|
97
|
+
const leadingStar = parts[0] === "*";
|
|
98
|
+
const limit = leadingStar ? MAX_SEGMENTS : named ? MAX_SEGMENTS - 1 : MAX_SEGMENTS;
|
|
99
|
+
const min = leadingStar ? (named ? 1 : 2) : 1;
|
|
100
|
+
if (parts.length < min) {
|
|
101
|
+
throw new Error(`path '${shown}' has ${parts.length} segments; at least ${min} are required`);
|
|
102
|
+
}
|
|
103
|
+
if (parts.length > limit) {
|
|
104
|
+
throw new Error(`path '${shown}' has ${parts.length} segments; the maximum is ${limit}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function assertChains(entries) {
|
|
109
|
+
if (!Array.isArray(entries)) return;
|
|
110
|
+
for (const entry of entries) {
|
|
111
|
+
assertPath(entry.path, typeof entry.receiver === "string" && entry.receiver !== "*");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
88
115
|
function compile(entry) {
|
|
116
|
+
assertPath(entry.path, typeof entry.receiver === "string" && entry.receiver !== "*");
|
|
89
117
|
const parts = Array.isArray(entry.path) ? entry.path.slice() : entry.path.split(".");
|
|
90
118
|
let wildcard = false;
|
|
91
119
|
let body = parts;
|
|
@@ -101,4 +129,4 @@ function compile(entry) {
|
|
|
101
129
|
return { wildcard, parts: body, message: entry.message };
|
|
102
130
|
}
|
|
103
131
|
|
|
104
|
-
module.exports = { schema, compile };
|
|
132
|
+
module.exports = { schema, compile, assertChains };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
const { ruleSuppression } = require("../rule-suppression");
|
|
4
|
-
const { schema, compile } = require("./forbidden-member-chains-schema");
|
|
4
|
+
const { schema, compile, assertChains } = require("./forbidden-member-chains-schema");
|
|
5
5
|
const {
|
|
6
6
|
matches,
|
|
7
7
|
computeChain,
|
|
@@ -20,6 +20,7 @@ module.exports = {
|
|
|
20
20
|
create(context) {
|
|
21
21
|
const compiled = [];
|
|
22
22
|
const entries = context.options[0]?.chains;
|
|
23
|
+
assertChains(entries);
|
|
23
24
|
if (Array.isArray(entries)) {
|
|
24
25
|
for (const entry of entries) compiled.push(compile(entry));
|
|
25
26
|
}
|