eslint-plugin-crisp 1.0.90 → 1.0.92
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 +2 -0
- package/index.js +2 -0
- package/package.json +1 -1
- package/recommended-vue.js +2 -0
- package/recommended.js +2 -1
- package/rules/jsdoc-require-description-uppercase.js +224 -0
- package/rules/vue-attribute-comma.js +33 -0
package/README.md
CHANGED
|
@@ -151,6 +151,7 @@ Each item has emojis denoting:
|
|
|
151
151
|
| [crisp/jsdoc-check-optional-params](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/jsdoc-check-optional-params.js) | Requires optional parameters to be surrounded by brackets | | 🟢 |
|
|
152
152
|
| [crisp/jsdoc-enforce-access](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/jsdoc-enforce-access.js) | Requires one of `@public`, `@private`, or `@protected` for functions | | 🟢 |
|
|
153
153
|
| [crisp/jsdoc-enforce-classdesc](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/jsdoc-enforce-classdesc.js) | Ensures JSDoc for class headers to include a non-empty `@classdesc` | 🟠 | 🟢 |
|
|
154
|
+
| [crisp/jsdoc-require-description-uppercase](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/jsdoc-require-description-uppercase.js) | Requires descriptions to start with an uppercase character | 🟠 | 🟢 |
|
|
154
155
|
|
|
155
156
|
#### General Vue rules
|
|
156
157
|
|
|
@@ -191,6 +192,7 @@ Each item has emojis denoting:
|
|
|
191
192
|
|
|
192
193
|
| Name | Description | 🟠 | 🟢 |
|
|
193
194
|
| :- | :- | :- | :- |
|
|
195
|
+
| [crisp/vue-attribute-comma](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/vue-attribute-comma.js) | Disallows trailing comma after attribute | | 🟢 |
|
|
194
196
|
| [crisp/vue-attribute-linebreak](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/vue-attribute-linebreak.js) | Enforces linebreak before first attribute and after last attribute | | 🟢 |
|
|
195
197
|
| [crisp/vue-computed-order](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/vue-computed-order.js) | Ensures computed properties are alphabetically ordered | | 🟢 |
|
|
196
198
|
| [crisp/vue-emits-order](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/vue-emits-order.js) | Ensures emits properties are alphabetically ordered | | 🟢 |
|
package/index.js
CHANGED
|
@@ -20,6 +20,7 @@ module.exports = {
|
|
|
20
20
|
"jsdoc-check-optional-params": require("./rules/jsdoc-check-optional-params"),
|
|
21
21
|
"jsdoc-enforce-access": require("./rules/jsdoc-enforce-access"),
|
|
22
22
|
"jsdoc-enforce-classdesc": require("./rules/jsdoc-enforce-classdesc"),
|
|
23
|
+
"jsdoc-require-description-uppercase": require("./rules/jsdoc-require-description-uppercase"),
|
|
23
24
|
"methods-naming": require("./rules/methods-naming"),
|
|
24
25
|
"methods-ordering": require("./rules/methods-ordering"),
|
|
25
26
|
"multiline-comment-end-backslash": require("./rules/multiline-comment-end-backslash"),
|
|
@@ -34,6 +35,7 @@ module.exports = {
|
|
|
34
35
|
"ternary-parenthesis": require("./rules/ternary-parenthesis"),
|
|
35
36
|
"two-lines-between-class-members": require("./rules/two-lines-between-class-members"),
|
|
36
37
|
"variable-names": require("./rules/variable-names"),
|
|
38
|
+
"vue-attribute-comma": require("./rules/vue-attribute-comma"),
|
|
37
39
|
"vue-attribute-linebreak": require("./rules/vue-attribute-linebreak"),
|
|
38
40
|
"vue-computed-order": require("./rules/vue-computed-order"),
|
|
39
41
|
"vue-emits-order": require("./rules/vue-emits-order"),
|
package/package.json
CHANGED
package/recommended-vue.js
CHANGED
|
@@ -222,6 +222,7 @@ module.exports = {
|
|
|
222
222
|
"crisp/jsdoc-check-optional-params": "error",
|
|
223
223
|
"crisp/jsdoc-enforce-access": "error",
|
|
224
224
|
"crisp/jsdoc-enforce-classdesc": "error",
|
|
225
|
+
"crisp/jsdoc-require-description-uppercase": "error",
|
|
225
226
|
|
|
226
227
|
// General Vue rules
|
|
227
228
|
"vue/attributes-order": [
|
|
@@ -334,6 +335,7 @@ module.exports = {
|
|
|
334
335
|
],
|
|
335
336
|
|
|
336
337
|
// Crisp Vue rules
|
|
338
|
+
"crisp/vue-attribute-comma": "error",
|
|
337
339
|
"crisp/vue-attribute-linebreak": "error",
|
|
338
340
|
"crisp/vue-computed-order": "error",
|
|
339
341
|
"crisp/vue-emits-order": "error",
|
package/recommended.js
CHANGED
|
@@ -209,6 +209,7 @@ module.exports = {
|
|
|
209
209
|
// Crisp JSDoc rules
|
|
210
210
|
"crisp/jsdoc-align-params": "error",
|
|
211
211
|
"crisp/jsdoc-check-indentation": "error",
|
|
212
|
-
"crisp/jsdoc-enforce-classdesc": "error"
|
|
212
|
+
"crisp/jsdoc-enforce-classdesc": "error",
|
|
213
|
+
"crisp/jsdoc-require-description-uppercase": "error"
|
|
213
214
|
}
|
|
214
215
|
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
// Based on https://github.com/gajus/eslint-plugin-jsdoc/blob/main/src/rules/requireDescriptionCompleteSentence.js
|
|
2
|
+
// Removed handling of sentences, punctuation, abbreviations. We just need to check that the description starts with an uppercase character.
|
|
3
|
+
|
|
4
|
+
const { default: iterateJsdoc } = require("eslint-plugin-jsdoc/dist/iterateJsdoc");
|
|
5
|
+
const escapeStringRegexp = require("escape-string-regexp");
|
|
6
|
+
|
|
7
|
+
const otherDescriptiveTags = new Set([
|
|
8
|
+
// 'copyright' and 'see' might be good addition, but as the former may be
|
|
9
|
+
// sensitive text, and the latter may have just a link, they are not
|
|
10
|
+
// included by default
|
|
11
|
+
"summary", "file", "fileoverview", "overview", "classdesc", "todo",
|
|
12
|
+
"deprecated", "throws", "exception", "yields", "yield",
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {string} text
|
|
17
|
+
* @returns {string[]}
|
|
18
|
+
*/
|
|
19
|
+
const extractParagraphs = (text) => {
|
|
20
|
+
return text.split(/(?<![;:])\n\n+/u);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param {string} str
|
|
25
|
+
* @returns {boolean}
|
|
26
|
+
*/
|
|
27
|
+
const isCapitalized = (str) => {
|
|
28
|
+
return str[0] === str[0].toUpperCase();
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {string} str
|
|
33
|
+
* @returns {boolean}
|
|
34
|
+
*/
|
|
35
|
+
const isTable = (str) => {
|
|
36
|
+
return str.charAt(0) === "|";
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {string} str
|
|
41
|
+
* @returns {string}
|
|
42
|
+
*/
|
|
43
|
+
const capitalize = (str) => {
|
|
44
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @param {string} description
|
|
49
|
+
* @param {import('../iterateJsdoc.js').Report} reportOrig
|
|
50
|
+
* @param {import('eslint').Rule.Node} jsdocNode
|
|
51
|
+
* @param {import('eslint').SourceCode} sourceCode
|
|
52
|
+
* @param {import('comment-parser').Spec|{
|
|
53
|
+
* line: import('../iterateJsdoc.js').Integer
|
|
54
|
+
* }} tag
|
|
55
|
+
* @returns {boolean}
|
|
56
|
+
*/
|
|
57
|
+
const validateDescription = (
|
|
58
|
+
description, reportOrig, jsdocNode, sourceCode, tag,
|
|
59
|
+
) => {
|
|
60
|
+
if (!description || (/^\n+$/u).test(description)) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const descriptionNoHeadings = description.replaceAll(/^\s*#[^\n]*(\n|$)/gm, "");
|
|
65
|
+
|
|
66
|
+
const paragraphs = extractParagraphs(descriptionNoHeadings).filter(Boolean);
|
|
67
|
+
|
|
68
|
+
return paragraphs.some((paragraph, parIdx) => {
|
|
69
|
+
const sentences = [paragraph];
|
|
70
|
+
|
|
71
|
+
const fix = /** @type {import('eslint').Rule.ReportFixer} */ (fixer) => {
|
|
72
|
+
let text = sourceCode.getText(jsdocNode);
|
|
73
|
+
|
|
74
|
+
for (const sentence of sentences.filter((sentence_) => {
|
|
75
|
+
return !(/^\s*$/u).test(sentence_) && !isCapitalized(sentence_) &&
|
|
76
|
+
!isTable(sentence_);
|
|
77
|
+
})) {
|
|
78
|
+
const beginning = sentence.split("\n")[0];
|
|
79
|
+
|
|
80
|
+
if ("tag" in tag && tag.tag) {
|
|
81
|
+
const reg = new RegExp(`(@${escapeStringRegexp(tag.tag)}.*)${escapeStringRegexp(beginning)}`, "u");
|
|
82
|
+
|
|
83
|
+
text = text.replace(reg, (_$0, $1) => {
|
|
84
|
+
return $1 + capitalize(beginning);
|
|
85
|
+
});
|
|
86
|
+
} else {
|
|
87
|
+
text = text.replace(new RegExp("((?:[.?!]|\\*|\\})\\s*)" + escapeStringRegexp(beginning), "u"), "$1" + capitalize(beginning));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return fixer.replaceText(jsdocNode, text);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @param {string} msg
|
|
96
|
+
* @param {import('eslint').Rule.ReportFixer | null | undefined} fixer
|
|
97
|
+
* @param {{
|
|
98
|
+
* line?: number | undefined;
|
|
99
|
+
* column?: number | undefined;
|
|
100
|
+
* } | (import('comment-parser').Spec & {
|
|
101
|
+
* line?: number | undefined;
|
|
102
|
+
* column?: number | undefined;
|
|
103
|
+
* })} tagObj
|
|
104
|
+
* @returns {void}
|
|
105
|
+
*/
|
|
106
|
+
const report = (msg, fixer, tagObj) => {
|
|
107
|
+
if ("line" in tagObj) {
|
|
108
|
+
/**
|
|
109
|
+
* @type {{
|
|
110
|
+
* line: number;
|
|
111
|
+
* }}
|
|
112
|
+
*/ (tagObj).line += parIdx * 2;
|
|
113
|
+
} else {
|
|
114
|
+
/** @type {import('comment-parser').Spec} */ (
|
|
115
|
+
tagObj
|
|
116
|
+
).source[0].number += parIdx * 2;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Avoid errors if old column doesn't exist here
|
|
120
|
+
tagObj.column = 0;
|
|
121
|
+
reportOrig(msg, fixer, tagObj);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
if (sentences.some((sentence) => {
|
|
125
|
+
return !(/^\s*$/u).test(sentence) && !isCapitalized(sentence) && !isTable(sentence);
|
|
126
|
+
})) {
|
|
127
|
+
report("Sentences should start with an uppercase character.", fix, tag);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return false;
|
|
131
|
+
});
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
module.exports = iterateJsdoc(({
|
|
135
|
+
sourceCode,
|
|
136
|
+
context,
|
|
137
|
+
jsdoc,
|
|
138
|
+
report,
|
|
139
|
+
jsdocNode,
|
|
140
|
+
utils,
|
|
141
|
+
}) => {
|
|
142
|
+
let {
|
|
143
|
+
description,
|
|
144
|
+
} = utils.getDescription();
|
|
145
|
+
|
|
146
|
+
const indices = [
|
|
147
|
+
...description.matchAll(/```[\s\S]*```/gu),
|
|
148
|
+
].map((match) => {
|
|
149
|
+
const {
|
|
150
|
+
index,
|
|
151
|
+
} = match;
|
|
152
|
+
const [
|
|
153
|
+
{
|
|
154
|
+
length,
|
|
155
|
+
},
|
|
156
|
+
] = match;
|
|
157
|
+
return {
|
|
158
|
+
index,
|
|
159
|
+
length,
|
|
160
|
+
};
|
|
161
|
+
}).reverse();
|
|
162
|
+
|
|
163
|
+
for (const {
|
|
164
|
+
index,
|
|
165
|
+
length,
|
|
166
|
+
} of indices) {
|
|
167
|
+
description = description.slice(0, index) +
|
|
168
|
+
description.slice(/** @type {import('../iterateJsdoc.js').Integer} */ (
|
|
169
|
+
index
|
|
170
|
+
) + length);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (validateDescription(description, report, jsdocNode, sourceCode, {
|
|
174
|
+
line: jsdoc.source[0].number + 1,
|
|
175
|
+
})) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
utils.forEachPreferredTag("description", (matchingJsdocTag) => {
|
|
180
|
+
const desc = `${matchingJsdocTag.name} ${utils.getTagDescription(matchingJsdocTag)}`.trim();
|
|
181
|
+
validateDescription(desc, report, jsdocNode, sourceCode, matchingJsdocTag);
|
|
182
|
+
}, true);
|
|
183
|
+
|
|
184
|
+
const {
|
|
185
|
+
tagsWithNames,
|
|
186
|
+
} = utils.getTagsByType(jsdoc.tags);
|
|
187
|
+
const tagsWithoutNames = utils.filterTags(({
|
|
188
|
+
tag: tagName,
|
|
189
|
+
}) => {
|
|
190
|
+
return otherDescriptiveTags.has(tagName) ||
|
|
191
|
+
utils.hasOptionTag(tagName) && !tagsWithNames.some(({
|
|
192
|
+
tag,
|
|
193
|
+
}) => {
|
|
194
|
+
// If user accidentally adds tags with names (or like `returns`
|
|
195
|
+
// get parsed as having names), do not add to this list
|
|
196
|
+
return tag === tagName;
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
tagsWithNames.some((tag) => {
|
|
201
|
+
const desc = /** @type {string} */ (
|
|
202
|
+
utils.getTagDescription(tag)
|
|
203
|
+
).replace(/^- /u, "").trimEnd();
|
|
204
|
+
|
|
205
|
+
return validateDescription(desc, report, jsdocNode, sourceCode, tag);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
tagsWithoutNames.some((tag) => {
|
|
209
|
+
const desc = `${tag.name} ${utils.getTagDescription(tag)}`.trim();
|
|
210
|
+
|
|
211
|
+
return validateDescription(desc, report, jsdocNode, sourceCode, tag);
|
|
212
|
+
});
|
|
213
|
+
}, {
|
|
214
|
+
iterateAllJsdocs: true,
|
|
215
|
+
meta: {
|
|
216
|
+
docs: {
|
|
217
|
+
description: "Requires that block description, explicit `@description`, and `@param`/`@returns` tag descriptions are written in complete sentences.",
|
|
218
|
+
url: "https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-description-complete-sentence.md#repos-sticky-header",
|
|
219
|
+
},
|
|
220
|
+
fixable: "code",
|
|
221
|
+
schema: [],
|
|
222
|
+
type: "suggestion",
|
|
223
|
+
},
|
|
224
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
meta: {
|
|
3
|
+
type: "problem",
|
|
4
|
+
docs: {
|
|
5
|
+
description: "disallow commas after attribute values in Vue templates",
|
|
6
|
+
category: "Possible Errors",
|
|
7
|
+
recommended: false,
|
|
8
|
+
},
|
|
9
|
+
fixable: "code",
|
|
10
|
+
schema: [], // no options
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
create(context) {
|
|
14
|
+
return context.parserServices.defineTemplateBodyVisitor({
|
|
15
|
+
'VAttribute'(node) {
|
|
16
|
+
const sourceCode = context.getSourceCode();
|
|
17
|
+
const attributeText = sourceCode.getText(node);
|
|
18
|
+
const attributeEndIndex = node.range[1];
|
|
19
|
+
const nextChar = sourceCode.getText().slice(attributeEndIndex, attributeEndIndex + 1);
|
|
20
|
+
|
|
21
|
+
if (nextChar === ',') {
|
|
22
|
+
context.report({
|
|
23
|
+
node,
|
|
24
|
+
message: 'Comma after attribute value is not allowed.',
|
|
25
|
+
fix(fixer) {
|
|
26
|
+
return fixer.removeRange([attributeEndIndex, attributeEndIndex + 1]);
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
},
|
|
33
|
+
};
|