eslint-plugin-crisp 1.7.9 → 1.7.11
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/README.md
CHANGED
|
@@ -230,6 +230,7 @@ Each item has emojis denoting:
|
|
|
230
230
|
| [crisp/vue-header-check](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/vue-header-check.js) | Ensures `script`, `template` and `style` tags start with corresponding comment block | | 🟢 |
|
|
231
231
|
| [crisp/vue-html-indent](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/vue-html-indent.js) | Enforces consistent indentation in `template` (supports for Pug) | | 🟢 |
|
|
232
232
|
| [crisp/vue-html-quotes](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/vue-html-quotes.js) | Enforces HTML attributes to be enclosed with double quotes | | 🟢 |
|
|
233
|
+
| [crisp/vue-methods-separator](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/vue-methods-separator.js) | Requires method section comments to be ordered as actions, helpers, then event listeners | | 🟢 |
|
|
233
234
|
| [crisp/vue-no-component-class-shorthand](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/vue-no-component-class-shorthand.js) | Disallows Pug class shorthand on Vue components | | 🟢 |
|
|
234
235
|
| [crisp/vue-no-mixed-class](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/vue-no-mixed-class.js) | Disallows using both `class` and `:class` on the same element | | 🟢 |
|
|
235
236
|
| [crisp/vue-no-name-option](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/vue-no-name-option.js) | Disallows explicit component names in Vue single-file components | | 🟢 |
|
package/package.json
CHANGED
|
@@ -94,6 +94,14 @@ function isReference(node) {
|
|
|
94
94
|
);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
// Checks an empty object or array literal (eg. `{}` or `[]`)
|
|
98
|
+
function isEmptyLiteral(node) {
|
|
99
|
+
return (
|
|
100
|
+
(node.type === "ObjectExpression" && node.properties.length === 0) ||
|
|
101
|
+
(node.type === "ArrayExpression" && node.elements.length === 0)
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
97
105
|
// Checks a fallback expression like `value || "none"` or `value ?? other`
|
|
98
106
|
function isFallback(node) {
|
|
99
107
|
node = unwrapTransparentExpression(node);
|
|
@@ -111,6 +119,7 @@ function isFallback(node) {
|
|
|
111
119
|
|
|
112
120
|
return (
|
|
113
121
|
operand.type === "Literal" ||
|
|
122
|
+
isEmptyLiteral(operand) ||
|
|
114
123
|
isReference(operand) ||
|
|
115
124
|
isFallback(operand)
|
|
116
125
|
);
|
|
@@ -151,6 +160,61 @@ function isReferenceObject(node) {
|
|
|
151
160
|
});
|
|
152
161
|
}
|
|
153
162
|
|
|
163
|
+
// Checks a node is a `logger.levels.<level>` member expression
|
|
164
|
+
function isLevelsReference(node) {
|
|
165
|
+
node = unwrapTransparentExpression(node);
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
Boolean(node) &&
|
|
169
|
+
node.type === "MemberExpression" &&
|
|
170
|
+
node.object.type === "MemberExpression" &&
|
|
171
|
+
getPropertyName(node.object) === "levels" &&
|
|
172
|
+
node.object.object.type === "MemberExpression" &&
|
|
173
|
+
getPropertyName(node.object.object) === "logger"
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Checks a condition tests a logger level (eg. `logger.levels.error === true && x`)
|
|
178
|
+
function isLevelCheck(node) {
|
|
179
|
+
node = unwrapTransparentExpression(node);
|
|
180
|
+
|
|
181
|
+
if (!node) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (node.type === "LogicalExpression" && node.operator === "&&") {
|
|
186
|
+
return isLevelCheck(node.left) || isLevelCheck(node.right);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (
|
|
190
|
+
node.type === "BinaryExpression" &&
|
|
191
|
+
(node.operator === "===" || node.operator === "==")
|
|
192
|
+
) {
|
|
193
|
+
return isLevelsReference(node.left) || isLevelsReference(node.right);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return isLevelsReference(node);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Checks a node is inside the consequent of a logger level guard
|
|
200
|
+
function isGuarded(ancestors, node) {
|
|
201
|
+
const chain = [...ancestors, node];
|
|
202
|
+
|
|
203
|
+
for (let index = chain.length - 2; index >= 0; index--) {
|
|
204
|
+
const ancestor = chain[index];
|
|
205
|
+
|
|
206
|
+
if (
|
|
207
|
+
ancestor.type === "IfStatement" &&
|
|
208
|
+
chain[index + 1] === ancestor.consequent &&
|
|
209
|
+
isLevelCheck(ancestor.test)
|
|
210
|
+
) {
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
|
|
154
218
|
export default {
|
|
155
219
|
meta: {
|
|
156
220
|
type: "suggestion",
|
|
@@ -177,6 +241,11 @@ export default {
|
|
|
177
241
|
return;
|
|
178
242
|
}
|
|
179
243
|
|
|
244
|
+
// Guarded by a log level check: computed values are allowed
|
|
245
|
+
if (isGuarded(sourceCode.getAncestors(node), node)) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
180
249
|
const [message, ...values] = node.arguments;
|
|
181
250
|
|
|
182
251
|
if (
|
|
@@ -1,65 +1,121 @@
|
|
|
1
|
+
const SEPARATORS = [
|
|
2
|
+
"--> ACTIONS <--",
|
|
3
|
+
"--> HELPERS <--",
|
|
4
|
+
"--> EVENT LISTENERS <--"
|
|
5
|
+
];
|
|
6
|
+
|
|
7
|
+
const SEPARATOR_ORDER = new Map(
|
|
8
|
+
SEPARATORS.map((separator, index) => [separator, index])
|
|
9
|
+
);
|
|
10
|
+
const SEPARATOR_LIKE_PATTERN = /^--> .+ <--$/;
|
|
11
|
+
|
|
1
12
|
export default {
|
|
2
13
|
meta: {
|
|
3
14
|
type: "suggestion",
|
|
4
15
|
docs: {
|
|
5
|
-
description:
|
|
16
|
+
description:
|
|
17
|
+
"enforce separator comments and their order in Vue methods block",
|
|
6
18
|
category: "Stylistic Issues",
|
|
7
19
|
recommended: false
|
|
8
20
|
},
|
|
9
21
|
fixable: null,
|
|
10
|
-
schema: []
|
|
22
|
+
schema: [],
|
|
23
|
+
messages: {
|
|
24
|
+
duplicateSeparator:
|
|
25
|
+
"Duplicate methods separator comment '// {{separator}}'.",
|
|
26
|
+
invalidSeparator:
|
|
27
|
+
"Invalid separator comment. Must be '// --> ACTIONS <--', " +
|
|
28
|
+
"'// --> HELPERS <--', or '// --> EVENT LISTENERS <--'.",
|
|
29
|
+
missingSeparator:
|
|
30
|
+
"Methods block must contain at least one separator comment: " +
|
|
31
|
+
"'// --> ACTIONS <--', '// --> HELPERS <--', " +
|
|
32
|
+
"or '// --> EVENT LISTENERS <--'.",
|
|
33
|
+
unorderedSeparator:
|
|
34
|
+
"Methods separator comments must be ordered: '// --> ACTIONS <--', " +
|
|
35
|
+
"then '// --> HELPERS <--', then '// --> EVENT LISTENERS <--'."
|
|
36
|
+
}
|
|
11
37
|
},
|
|
12
38
|
|
|
13
39
|
create(context) {
|
|
14
|
-
const
|
|
15
|
-
const SEPARATOR_LIKE_PATTERN = /^--> .+ <--$/;
|
|
40
|
+
const sourceCode = context.sourceCode;
|
|
16
41
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
42
|
+
function checkMethods(node) {
|
|
43
|
+
const properties = node.value.properties;
|
|
44
|
+
|
|
45
|
+
if (!properties || properties.length === 0) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const comments = sourceCode.getCommentsInside(node.value).filter(
|
|
50
|
+
(comment) => {
|
|
51
|
+
return !properties.some((property) => {
|
|
52
|
+
return (
|
|
53
|
+
comment.range[0] >= property.range[0] &&
|
|
54
|
+
comment.range[1] <= property.range[1]
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
const seenSeparators = new Set();
|
|
60
|
+
let lastSeparatorOrder = -1;
|
|
61
|
+
let separatorCount = 0;
|
|
20
62
|
|
|
21
|
-
|
|
63
|
+
comments.forEach((comment) => {
|
|
64
|
+
if (comment.type !== "Line") {
|
|
22
65
|
return;
|
|
23
66
|
}
|
|
24
67
|
|
|
25
|
-
const
|
|
26
|
-
const
|
|
68
|
+
const separator = comment.value.trim();
|
|
69
|
+
const separatorOrder = SEPARATOR_ORDER.get(separator);
|
|
27
70
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
71
|
+
if (separatorOrder === undefined) {
|
|
72
|
+
if (SEPARATOR_LIKE_PATTERN.test(separator)) {
|
|
73
|
+
context.report({
|
|
74
|
+
node: comment,
|
|
75
|
+
messageId: "invalidSeparator"
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
separatorCount++;
|
|
34
83
|
|
|
35
|
-
if (
|
|
84
|
+
if (seenSeparators.has(separator)) {
|
|
36
85
|
context.report({
|
|
37
|
-
node,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"'// --> ACTIONS <--', '// --> HELPERS <--', " +
|
|
41
|
-
"or '// --> EVENT LISTENERS <--'."
|
|
86
|
+
node: comment,
|
|
87
|
+
messageId: "duplicateSeparator",
|
|
88
|
+
data: { separator }
|
|
42
89
|
});
|
|
90
|
+
|
|
91
|
+
return;
|
|
43
92
|
}
|
|
44
93
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
94
|
+
seenSeparators.add(separator);
|
|
95
|
+
|
|
96
|
+
if (separatorOrder < lastSeparatorOrder) {
|
|
97
|
+
context.report({
|
|
98
|
+
node: comment,
|
|
99
|
+
messageId: "unorderedSeparator"
|
|
100
|
+
});
|
|
101
|
+
} else {
|
|
102
|
+
lastSeparatorOrder = separatorOrder;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
if (separatorCount === 0) {
|
|
107
|
+
context.report({
|
|
108
|
+
node,
|
|
109
|
+
messageId: "missingSeparator"
|
|
61
110
|
});
|
|
62
111
|
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
'ExportDefaultDeclaration > ObjectExpression > Property[key.name="methods"]':
|
|
116
|
+
checkMethods,
|
|
117
|
+
'ExportDefaultDeclaration > CallExpression[callee.name="defineComponent"] > ObjectExpression > Property[key.name="methods"]':
|
|
118
|
+
checkMethods
|
|
63
119
|
};
|
|
64
120
|
}
|
|
65
121
|
};
|
|
@@ -19,6 +19,12 @@ ruleTester.run("logger-arguments", rule, {
|
|
|
19
19
|
"App.logger.debug(\"Loaded value: %s\", this.value ?? \"none\");",
|
|
20
20
|
"App.logger.debug(\"Loaded value: %s\", first || second || \"none\");",
|
|
21
21
|
"App.logger.debug(\"Loaded value: %s\", { user: userId || \"none\" });",
|
|
22
|
+
"if (App.logger.levels.error === true) { App.logger.error(\"Purge failed: %s\", buckets.join(\", \"), error); }",
|
|
23
|
+
"if (App.logger.levels.warn === true) { App.logger.warn(fn_log_format()); }",
|
|
24
|
+
"if (App.logger.levels.error === true && other) { if (x) { App.logger.error(\"Slow: %s\", a + b); } }",
|
|
25
|
+
"if (this._app.logger.levels.debug) { this._app.logger.debug(\"Count: %s\", list.length + 1); }",
|
|
26
|
+
"App.logger.error(\"Failed\", { params: (request.params || {}) });",
|
|
27
|
+
"App.logger.error(\"Failed: %s\", list || []);",
|
|
22
28
|
"App.logger.warn(\"Shutting down\", { bufferedCount });",
|
|
23
29
|
"App.logger.warn(\"Shutting down\", { count: this.count, ...meta });",
|
|
24
30
|
"App.logger.warn(\"Shutting down\", { nested: { bufferedCount } }, logMeta);",
|
|
@@ -123,6 +129,22 @@ ruleTester.run("logger-arguments", rule, {
|
|
|
123
129
|
}
|
|
124
130
|
]
|
|
125
131
|
},
|
|
132
|
+
{
|
|
133
|
+
code: "if (App.logger.levels.error === true) {} else { App.logger.error(\"Purge: %s\", buckets.join(\", \")); }",
|
|
134
|
+
errors: [
|
|
135
|
+
{
|
|
136
|
+
messageId: "reference"
|
|
137
|
+
}
|
|
138
|
+
]
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
code: "if (enabled) { App.logger.error(\"Purge: %s\", buckets.join(\", \")); }",
|
|
142
|
+
errors: [
|
|
143
|
+
{
|
|
144
|
+
messageId: "reference"
|
|
145
|
+
}
|
|
146
|
+
]
|
|
147
|
+
},
|
|
126
148
|
{
|
|
127
149
|
code: "App.logger.debug(\"Retry count: %s\", 3);",
|
|
128
150
|
errors: [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import rule from "../../rules/vue-methods-separator.js";
|
|
2
|
-
import
|
|
2
|
+
import { createVueRuleTester } from "../rule-tester.js";
|
|
3
3
|
|
|
4
|
-
const ruleTester =
|
|
4
|
+
const ruleTester = createVueRuleTester();
|
|
5
5
|
|
|
6
6
|
ruleTester.run("vue-methods-separator", rule, {
|
|
7
7
|
valid: [
|
|
@@ -17,6 +17,98 @@ ruleTester.run("vue-methods-separator", rule, {
|
|
|
17
17
|
}
|
|
18
18
|
};
|
|
19
19
|
`
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
code: `
|
|
23
|
+
export default {
|
|
24
|
+
methods: {
|
|
25
|
+
// --> HELPERS <--
|
|
26
|
+
format() {}
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
`
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
code: `
|
|
33
|
+
export default {
|
|
34
|
+
methods: {
|
|
35
|
+
// --> EVENT LISTENERS <--
|
|
36
|
+
onSave() {}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
`
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
code: `
|
|
43
|
+
export default {
|
|
44
|
+
methods: {
|
|
45
|
+
// --> ACTIONS <--
|
|
46
|
+
save() {},
|
|
47
|
+
// --> EVENT LISTENERS <--
|
|
48
|
+
onSave() {}
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
`
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
code: `
|
|
55
|
+
export default defineComponent({
|
|
56
|
+
methods: {
|
|
57
|
+
// --> ACTIONS <--
|
|
58
|
+
save() {},
|
|
59
|
+
// --> HELPERS <--
|
|
60
|
+
format() {},
|
|
61
|
+
// --> EVENT LISTENERS <--
|
|
62
|
+
onSave() {}
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
`
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
code: `
|
|
69
|
+
export default {
|
|
70
|
+
options: {
|
|
71
|
+
methods: {
|
|
72
|
+
save() {}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
`
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
code: `
|
|
80
|
+
export default {
|
|
81
|
+
methods: {
|
|
82
|
+
// --> ACTIONS <--
|
|
83
|
+
save() {
|
|
84
|
+
// --> EVENT LISTENERS <--
|
|
85
|
+
const status = "saved";
|
|
86
|
+
|
|
87
|
+
return status;
|
|
88
|
+
},
|
|
89
|
+
// --> HELPERS <--
|
|
90
|
+
format() {}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
`
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
filename: "Component.vue",
|
|
97
|
+
code: `
|
|
98
|
+
<template><div /></template>
|
|
99
|
+
<script>
|
|
100
|
+
export default defineComponent({
|
|
101
|
+
methods: {
|
|
102
|
+
// --> ACTIONS <--
|
|
103
|
+
save() {},
|
|
104
|
+
// --> HELPERS <--
|
|
105
|
+
format() {},
|
|
106
|
+
// --> EVENT LISTENERS <--
|
|
107
|
+
onSave() {}
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
</script>
|
|
111
|
+
`
|
|
20
112
|
}
|
|
21
113
|
],
|
|
22
114
|
|
|
@@ -25,10 +117,7 @@ ruleTester.run("vue-methods-separator", rule, {
|
|
|
25
117
|
code: "export default { methods: { save() {} } };",
|
|
26
118
|
errors: [
|
|
27
119
|
{
|
|
28
|
-
|
|
29
|
-
"Methods block must contain at least one separator comment: " +
|
|
30
|
-
"'// --> ACTIONS <--', '// --> HELPERS <--', " +
|
|
31
|
-
"or '// --> EVENT LISTENERS <--'."
|
|
120
|
+
messageId: "missingSeparator"
|
|
32
121
|
}
|
|
33
122
|
]
|
|
34
123
|
},
|
|
@@ -45,9 +134,95 @@ ruleTester.run("vue-methods-separator", rule, {
|
|
|
45
134
|
`,
|
|
46
135
|
errors: [
|
|
47
136
|
{
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
137
|
+
messageId: "invalidSeparator"
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
code: `
|
|
143
|
+
export default {
|
|
144
|
+
methods: {
|
|
145
|
+
// --> HELPERS <--
|
|
146
|
+
format() {},
|
|
147
|
+
// --> ACTIONS <--
|
|
148
|
+
save() {}
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
`,
|
|
152
|
+
errors: [
|
|
153
|
+
{
|
|
154
|
+
messageId: "unorderedSeparator"
|
|
155
|
+
}
|
|
156
|
+
]
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
code: `
|
|
160
|
+
export default {
|
|
161
|
+
methods: {
|
|
162
|
+
// --> ACTIONS <--
|
|
163
|
+
save() {},
|
|
164
|
+
// --> ACTIONS <--
|
|
165
|
+
publish() {}
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
`,
|
|
169
|
+
errors: [
|
|
170
|
+
{
|
|
171
|
+
messageId: "duplicateSeparator",
|
|
172
|
+
data: { separator: "--> ACTIONS <--" }
|
|
173
|
+
}
|
|
174
|
+
]
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
code: `
|
|
178
|
+
export default defineComponent({
|
|
179
|
+
methods: {
|
|
180
|
+
// --> EVENT LISTENERS <--
|
|
181
|
+
onSave() {},
|
|
182
|
+
// --> HELPERS <--
|
|
183
|
+
format() {}
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
`,
|
|
187
|
+
errors: [
|
|
188
|
+
{
|
|
189
|
+
messageId: "unorderedSeparator"
|
|
190
|
+
}
|
|
191
|
+
]
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
code: `
|
|
195
|
+
export default {
|
|
196
|
+
methods: {
|
|
197
|
+
save() {
|
|
198
|
+
// --> ACTIONS <--
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
`,
|
|
203
|
+
errors: [
|
|
204
|
+
{
|
|
205
|
+
messageId: "missingSeparator"
|
|
206
|
+
}
|
|
207
|
+
]
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
filename: "Component.vue",
|
|
211
|
+
code: `
|
|
212
|
+
<script>
|
|
213
|
+
export default defineComponent({
|
|
214
|
+
methods: {
|
|
215
|
+
// --> EVENT LISTENERS <--
|
|
216
|
+
onSave() {},
|
|
217
|
+
// --> ACTIONS <--
|
|
218
|
+
save() {}
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
</script>
|
|
222
|
+
`,
|
|
223
|
+
errors: [
|
|
224
|
+
{
|
|
225
|
+
messageId: "unorderedSeparator"
|
|
51
226
|
}
|
|
52
227
|
]
|
|
53
228
|
}
|