eslint-plugin-crisp 1.0.20 → 1.0.22
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 +6 -2
- package/package.json +1 -1
- package/recommended-vue.js +31 -22
- package/recommended.js +4 -2
- package/rules/{jsdocs-align-params.js → jsdoc-align-params.js} +1 -1
- package/rules/jsdoc-check-indentation.js +95 -0
- package/rules/ternary-parenthesis.js +8 -0
- package/rules/vue-computed-order.js +40 -0
- package/rules/vue-emits-order.js +40 -0
- package/rules/vue-header-check.js +48 -0
package/index.js
CHANGED
|
@@ -10,9 +10,11 @@ module.exports = {
|
|
|
10
10
|
"constructor-variables": require("./rules/constructor-variables"),
|
|
11
11
|
"header-check": require("./rules/header-check"),
|
|
12
12
|
"header-comments-check": require("./rules/header-comments-check"),
|
|
13
|
+
"jsdoc-align-params": require("./rules/jsdoc-align-params"),
|
|
14
|
+
"jsdoc-check-indentation": require("./rules/jsdoc-check-indentation"),
|
|
15
|
+
"jsdoc-check-optional-params": require("./rules/jsdoc-check-optional-params"),
|
|
13
16
|
"jsdoc-enforce-access": require("./rules/jsdoc-enforce-access"),
|
|
14
17
|
"jsdoc-enforce-classdesc": require("./rules/jsdoc-enforce-classdesc"),
|
|
15
|
-
"jsdoc-check-optional-params": require("./rules/jsdoc-check-optional-params"),
|
|
16
18
|
"methods-naming": require("./rules/methods-naming"),
|
|
17
19
|
"multiline-comment-end-backslash": require("./rules/multiline-comment-end-backslash"),
|
|
18
20
|
"no-async": require("./rules/no-async"),
|
|
@@ -24,10 +26,12 @@ module.exports = {
|
|
|
24
26
|
"regex-in-constructor": require("./rules/regex-in-constructor"),
|
|
25
27
|
"ternary-parenthesis": require("./rules/ternary-parenthesis"),
|
|
26
28
|
"multiline-comment-end-backslash": require("./rules/multiline-comment-end-backslash"),
|
|
27
|
-
"jsdocs-align-params": require("./rules/jsdocs-align-params"),
|
|
28
29
|
"two-lines-between-class-members": require("./rules/two-lines-between-class-members"),
|
|
29
30
|
"variable-names": require("./rules/variable-names"),
|
|
30
31
|
"align-consecutive-class-assignements": require("./rules/align-consecutive-class-assignements"),
|
|
32
|
+
"vue-computed-order": require("./rules/vue-computed-order"),
|
|
33
|
+
"vue-emits-order": require("./rules/vue-emits-order"),
|
|
34
|
+
"vue-header-check": require("./rules/vue-header-check"),
|
|
31
35
|
"vue-html-quotes": require("./rules/vue-html-quotes"),
|
|
32
36
|
"vue-props-declaration-order": require("./rules/vue-props-declaration-order")
|
|
33
37
|
}
|
package/package.json
CHANGED
package/recommended-vue.js
CHANGED
|
@@ -34,6 +34,7 @@ module.exports = {
|
|
|
34
34
|
"no-trailing-spaces": "error",
|
|
35
35
|
"no-tabs": "error",
|
|
36
36
|
"object-curly-spacing": ["error", "always"],
|
|
37
|
+
"prefer-arrow-callback": "error",
|
|
37
38
|
"semi": ["error", "always"],
|
|
38
39
|
"semi-style": ["error", "last"],
|
|
39
40
|
"semi-spacing": ["error", { "before": false, "after": true }],
|
|
@@ -47,6 +48,8 @@ module.exports = {
|
|
|
47
48
|
{ blankLine: "always", prev: "const", next: "*" },
|
|
48
49
|
{ blankLine: "any", prev: "const", next: "const" },
|
|
49
50
|
{ blankLine: "always", prev: "block-like", next: "*" },
|
|
51
|
+
{ blankLine: "always", prev: "*", next: "break" },
|
|
52
|
+
{ blankLine: "any", prev: "empty", next: "break" },
|
|
50
53
|
{ blankLine: "always", prev: "*", next: "block-like" },
|
|
51
54
|
{ blankLine: "any", prev: "case", next: "case" },
|
|
52
55
|
{ blankLine: "always", prev: "continue", next: "*" },
|
|
@@ -59,10 +62,29 @@ module.exports = {
|
|
|
59
62
|
{ "avoidEscape": true, "allowTemplateLiterals": true }
|
|
60
63
|
],
|
|
61
64
|
|
|
65
|
+
// Crisp JS rules
|
|
66
|
+
"crisp/header-check": "error",
|
|
67
|
+
"crisp/header-comments-check": "error",
|
|
68
|
+
"crisp/methods-naming": "error",
|
|
69
|
+
"crisp/multiline-comment-end-backslash": "error",
|
|
70
|
+
"crisp/no-async": "error",
|
|
71
|
+
"crisp/no-var-in-blocks": "error",
|
|
72
|
+
"crisp/no-useless-template-literals": "error",
|
|
73
|
+
"crisp/one-space-after-operator": ["error", { "checkColon": false }],
|
|
74
|
+
"crisp/regex-in-constructor": "error",
|
|
75
|
+
"crisp/ternary-parenthesis": "error",
|
|
76
|
+
"crisp/variable-names": "error",
|
|
77
|
+
"crisp/no-short-parameters": [
|
|
78
|
+
"error",
|
|
79
|
+
|
|
80
|
+
{
|
|
81
|
+
exceptions: ["_", "$", "x", "y"]
|
|
82
|
+
}
|
|
83
|
+
],
|
|
84
|
+
|
|
62
85
|
// JSDoc rules
|
|
63
86
|
"jsdoc/require-param-description": "off",
|
|
64
87
|
"jsdoc/newline-after-description": "off",
|
|
65
|
-
"jsdoc/check-indentation": "error",
|
|
66
88
|
"jsdoc/require-jsdoc": [
|
|
67
89
|
"error",
|
|
68
90
|
|
|
@@ -100,14 +122,17 @@ module.exports = {
|
|
|
100
122
|
],
|
|
101
123
|
|
|
102
124
|
// Crisp JSDoc rules
|
|
125
|
+
"crisp/jsdoc-align-params": "error",
|
|
126
|
+
"crisp/jsdoc-check-indentation": "error",
|
|
103
127
|
"crisp/jsdoc-check-optional-params": "error",
|
|
104
128
|
"crisp/jsdoc-enforce-access": "error",
|
|
105
129
|
"crisp/jsdoc-enforce-classdesc": "error",
|
|
106
|
-
"crisp/jsdocs-align-params": "error",
|
|
107
130
|
|
|
108
131
|
// General Vue rules
|
|
109
|
-
"vue/no-v-html": "off",
|
|
110
132
|
"vue/html-quotes": "off",
|
|
133
|
+
"vue/no-v-html": "off",
|
|
134
|
+
"vue/prefer-prop-type-boolean-first": "error",
|
|
135
|
+
"vue/prefer-true-attribute-shorthand": "error",
|
|
111
136
|
"vue/attributes-order": [
|
|
112
137
|
"error",
|
|
113
138
|
|
|
@@ -151,26 +176,10 @@ module.exports = {
|
|
|
151
176
|
}
|
|
152
177
|
],
|
|
153
178
|
|
|
154
|
-
// Crisp JS rules
|
|
155
|
-
"crisp/header-check": "error",
|
|
156
|
-
"crisp/header-comments-check": "error",
|
|
157
|
-
"crisp/methods-naming": "error",
|
|
158
|
-
"crisp/multiline-comment-end-backslash": "error",
|
|
159
|
-
"crisp/no-async": "error",
|
|
160
|
-
"crisp/no-var-in-blocks": "error",
|
|
161
|
-
"crisp/no-useless-template-literals": "error",
|
|
162
|
-
"crisp/one-space-after-operator": ["error", { "checkColon": false }],
|
|
163
|
-
"crisp/regex-in-constructor": "error",
|
|
164
|
-
"crisp/variable-names": "error",
|
|
165
|
-
"crisp/no-short-parameters": [
|
|
166
|
-
"error",
|
|
167
|
-
|
|
168
|
-
{
|
|
169
|
-
exceptions: ["_", "$", "x", "y"]
|
|
170
|
-
}
|
|
171
|
-
],
|
|
172
|
-
|
|
173
179
|
// Crisp Vue rules
|
|
180
|
+
"crisp/vue-computed-order": "error",
|
|
181
|
+
"crisp/vue-emits-order": "error",
|
|
182
|
+
"crisp/vue-header-check": "error",
|
|
174
183
|
"crisp/vue-html-quotes": "error",
|
|
175
184
|
"crisp/vue-props-declaration-order": "error"
|
|
176
185
|
},
|
package/recommended.js
CHANGED
|
@@ -46,6 +46,8 @@ module.exports = {
|
|
|
46
46
|
{ blankLine: "always", prev: "const", next: "*" },
|
|
47
47
|
{ blankLine: "any", prev: "const", next: "const" },
|
|
48
48
|
{ blankLine: "always", prev: "block-like", next: "*" },
|
|
49
|
+
{ blankLine: "always", prev: "*", next: "break" },
|
|
50
|
+
{ blankLine: "any", prev: "empty", next: "break" },
|
|
49
51
|
{ blankLine: "always", prev: "*", next: "block-like" },
|
|
50
52
|
{ blankLine: "any", prev: "case", next: "case" },
|
|
51
53
|
{ blankLine: "always", prev: "continue", next: "*" },
|
|
@@ -61,7 +63,6 @@ module.exports = {
|
|
|
61
63
|
// JSDoc rules
|
|
62
64
|
"jsdoc/require-param-description": "off",
|
|
63
65
|
"jsdoc/newline-after-description": "off",
|
|
64
|
-
"jsdoc/check-indentation": "error",
|
|
65
66
|
"jsdoc/require-jsdoc": [
|
|
66
67
|
"error",
|
|
67
68
|
|
|
@@ -99,8 +100,9 @@ module.exports = {
|
|
|
99
100
|
],
|
|
100
101
|
|
|
101
102
|
// Crisp JSDoc rules
|
|
103
|
+
"crisp/jsdoc-align-params": "error",
|
|
104
|
+
"crisp/jsdoc-check-indentation": "error",
|
|
102
105
|
"crisp/jsdoc-enforce-classdesc": "error",
|
|
103
|
-
"crisp/jsdocs-align-params": "error",
|
|
104
106
|
|
|
105
107
|
// Crisp JS rules
|
|
106
108
|
"crisp/align-one-var": "error",
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// Based on https://github.com/gajus/eslint-plugin-jsdoc/blob/main/src/rules/checkIndentation.js
|
|
2
|
+
|
|
3
|
+
const { default: iterateJsdoc } = require("eslint-plugin-jsdoc/dist/iterateJsdoc");
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param {string} str
|
|
7
|
+
* @param {string[]} excludeTags
|
|
8
|
+
* @returns {string}
|
|
9
|
+
*/
|
|
10
|
+
const maskExcludedContent = (str, excludeTags) => {
|
|
11
|
+
const regContent = new RegExp(`([ \\t]+\\*)[ \\t]@(?:${excludeTags.join('|')})(?=[ \\n])([\\w|\\W]*?\\n)(?=[ \\t]*\\*(?:[ \\t]*@\\w+\\s|\\/))`, 'gu');
|
|
12
|
+
|
|
13
|
+
return str.replace(regContent, (_match, margin, code) => {
|
|
14
|
+
return (margin + '\n').repeat(code.match(/\n/gu).length);
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {string} str
|
|
20
|
+
* @returns {string}
|
|
21
|
+
*/
|
|
22
|
+
const maskCodeBlocks = (str) => {
|
|
23
|
+
const regContent = /([ \t]+\*)[ \t]```[^\n]*?([\w|\W]*?\n)(?=[ \t]*\*(?:[ \t]*(?:```|@\w+\s)|\/))/gu;
|
|
24
|
+
|
|
25
|
+
return str.replace(regContent, (_match, margin, code) => {
|
|
26
|
+
return (margin + '\n').repeat(code.match(/\n/gu).length);
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const maskLineContinuations = (str) => {
|
|
31
|
+
// Match a line ending with a backslash (first capture group) and the \
|
|
32
|
+
// line following it (second capture group)
|
|
33
|
+
const regContent = /(\*\s.*\\)\n(\s*\*\s+.+)/gu;
|
|
34
|
+
|
|
35
|
+
return str.replace(regContent, (_match, lineBeforeContinuation, lineAfterContinuation) => {
|
|
36
|
+
// Normalize the line after the continuation to remove any extra spaces \
|
|
37
|
+
// after the '*'. The goal is to treat the continuation line as a \
|
|
38
|
+
// direct extension of the previous line.
|
|
39
|
+
const normalizedLine = lineAfterContinuation.replace(/(\*\s)(.+)/, "$1$2");
|
|
40
|
+
|
|
41
|
+
// Return the combined line (previous line + continuation line)
|
|
42
|
+
return lineBeforeContinuation + normalizedLine;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = iterateJsdoc(({
|
|
47
|
+
sourceCode,
|
|
48
|
+
jsdocNode,
|
|
49
|
+
report,
|
|
50
|
+
context,
|
|
51
|
+
}) => {
|
|
52
|
+
const options = context.options[0] || {};
|
|
53
|
+
const /** @type {{excludeTags: string[]}} */ {
|
|
54
|
+
excludeTags = [
|
|
55
|
+
'example',
|
|
56
|
+
],
|
|
57
|
+
} = options;
|
|
58
|
+
|
|
59
|
+
const reg = /^(?:\/?\**|[ \t]*)\*[ \t]{2}/gmu;
|
|
60
|
+
|
|
61
|
+
const textWithoutCodeBlocks = maskCodeBlocks(sourceCode.getText(jsdocNode));
|
|
62
|
+
const textWithoutExcluded = excludeTags.length ? maskExcludedContent(textWithoutCodeBlocks, excludeTags) : textWithoutCodeBlocks;
|
|
63
|
+
const text = maskLineContinuations(textWithoutExcluded);
|
|
64
|
+
|
|
65
|
+
if (reg.test(text)) {
|
|
66
|
+
const lineBreaks = text.slice(0, reg.lastIndex).match(/\n/gu) || [];
|
|
67
|
+
report('There must be no indentation.', null, {
|
|
68
|
+
line: lineBreaks.length,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}, {
|
|
72
|
+
iterateAllJsdocs: true,
|
|
73
|
+
meta: {
|
|
74
|
+
docs: {
|
|
75
|
+
description: 'Reports invalid padding inside JSDoc blocks.',
|
|
76
|
+
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-indentation.md#repos-sticky-header',
|
|
77
|
+
},
|
|
78
|
+
schema: [
|
|
79
|
+
{
|
|
80
|
+
additionalProperties: false,
|
|
81
|
+
properties: {
|
|
82
|
+
excludeTags: {
|
|
83
|
+
items: {
|
|
84
|
+
pattern: '^\\S+$',
|
|
85
|
+
type: 'string',
|
|
86
|
+
},
|
|
87
|
+
type: 'array',
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
type: 'object',
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
type: 'layout',
|
|
94
|
+
},
|
|
95
|
+
});
|
|
@@ -7,6 +7,7 @@ module.exports = {
|
|
|
7
7
|
recommended: false,
|
|
8
8
|
},
|
|
9
9
|
schema: [], // no options
|
|
10
|
+
fixable: "code",
|
|
10
11
|
},
|
|
11
12
|
create(context) {
|
|
12
13
|
return {
|
|
@@ -20,6 +21,13 @@ module.exports = {
|
|
|
20
21
|
context.report({
|
|
21
22
|
node,
|
|
22
23
|
message: "The condition in ternary expressions with an operator should be wrapped in parentheses",
|
|
24
|
+
fix(fixer) {
|
|
25
|
+
const conditionText = sourceCode.getText(node.test);
|
|
26
|
+
return [
|
|
27
|
+
fixer.insertTextBefore(node.test, "("),
|
|
28
|
+
fixer.insertTextAfter(node.test, ")"),
|
|
29
|
+
];
|
|
30
|
+
},
|
|
23
31
|
});
|
|
24
32
|
}
|
|
25
33
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
meta: {
|
|
3
|
+
type: "suggestion",
|
|
4
|
+
docs: {
|
|
5
|
+
description: "enforce computed properties alphabetical order",
|
|
6
|
+
category: "Stylistic Issues",
|
|
7
|
+
recommended: false
|
|
8
|
+
},
|
|
9
|
+
fixable: "code",
|
|
10
|
+
schema: [] // no options
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
create(context) {
|
|
14
|
+
return {
|
|
15
|
+
'ExportDefaultDeclaration Property[key.name="computed"]'(node) {
|
|
16
|
+
const properties = node.value.properties;
|
|
17
|
+
|
|
18
|
+
properties.slice(0, -1).forEach((property, index) => {
|
|
19
|
+
const nextProperty = properties[index + 1];
|
|
20
|
+
if (property.key.name > nextProperty.key.name) {
|
|
21
|
+
context.report({
|
|
22
|
+
node: nextProperty,
|
|
23
|
+
message: "Computed properties should be in alphabetical order.",
|
|
24
|
+
fix(fixer) {
|
|
25
|
+
const sourceCode = context.getSourceCode();
|
|
26
|
+
const propertyText = sourceCode.getText(property);
|
|
27
|
+
const nextPropertyText = sourceCode.getText(nextProperty);
|
|
28
|
+
|
|
29
|
+
return [
|
|
30
|
+
fixer.replaceText(property, nextPropertyText),
|
|
31
|
+
fixer.replaceText(nextProperty, propertyText)
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
meta: {
|
|
3
|
+
type: "suggestion",
|
|
4
|
+
docs: {
|
|
5
|
+
description: "enforce emits alphabetical order",
|
|
6
|
+
category: "Stylistic Issues",
|
|
7
|
+
recommended: false
|
|
8
|
+
},
|
|
9
|
+
fixable: "code",
|
|
10
|
+
schema: [] // no options
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
create(context) {
|
|
14
|
+
return {
|
|
15
|
+
'ExportDefaultDeclaration Property[key.name="emits"]'(node) {
|
|
16
|
+
const elements = node.value.elements;
|
|
17
|
+
|
|
18
|
+
elements.slice(0, -1).forEach((element, index) => {
|
|
19
|
+
const nextElement = elements[index + 1];
|
|
20
|
+
if (element.value > nextElement.value) {
|
|
21
|
+
context.report({
|
|
22
|
+
node: nextElement,
|
|
23
|
+
message: "Emits should be in alphabetical order.",
|
|
24
|
+
fix(fixer) {
|
|
25
|
+
const sourceCode = context.getSourceCode();
|
|
26
|
+
const elementText = sourceCode.getText(element);
|
|
27
|
+
const nextElementText = sourceCode.getText(nextElement);
|
|
28
|
+
|
|
29
|
+
return [
|
|
30
|
+
fixer.replaceText(element, nextElementText),
|
|
31
|
+
fixer.replaceText(nextElement, elementText)
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
meta: {
|
|
6
|
+
type: "suggestion",
|
|
7
|
+
docs: {
|
|
8
|
+
description: "Enforce Vue section headers",
|
|
9
|
+
category: "Best Practices",
|
|
10
|
+
recommended: false,
|
|
11
|
+
},
|
|
12
|
+
fixable: null // This rule is not auto-fixable
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
create(context) {
|
|
16
|
+
return {
|
|
17
|
+
Program(node) {
|
|
18
|
+
const fileName = context.getFilename();
|
|
19
|
+
const fileContent = fs.readFileSync(path.resolve(fileName), "utf8");
|
|
20
|
+
|
|
21
|
+
// Determine the file extension
|
|
22
|
+
const fileExtension = path.extname(fileName);
|
|
23
|
+
|
|
24
|
+
if (fileExtension === ".vue") {
|
|
25
|
+
// Define the headers
|
|
26
|
+
const headers = ["TEMPLATE", "SCRIPT", "STYLE"];
|
|
27
|
+
|
|
28
|
+
// Define the Vue tags
|
|
29
|
+
const vueTags = ["template", "script", "style"];
|
|
30
|
+
|
|
31
|
+
// Check each tag individually
|
|
32
|
+
vueTags.forEach((tag, index) => {
|
|
33
|
+
const tagRegExp = new RegExp(`<${tag}`);
|
|
34
|
+
const headerRegExp = new RegExp(`<!--\\s*\\*{73}\\s*\\n\\s*${headers[index]}\\s*\\n\\s*\\*{73}\\s*-->\\s*\\n\\s*<${tag}`);
|
|
35
|
+
|
|
36
|
+
// If the tag exists without the corresponding header, report an error
|
|
37
|
+
if (tagRegExp.test(fileContent) && !headerRegExp.test(fileContent)) {
|
|
38
|
+
context.report({
|
|
39
|
+
node,
|
|
40
|
+
message: `The '<${tag}>' section must be preceded by the '${headers[index]}' header comment.`,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
};
|