@prowise/eslint-config-linting-rules 2.4.6 → 3.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/README.md +3 -3
- package/base.js +1 -1
- package/eslint.config.js +24 -6
- package/front-react.js +2 -2
- package/front-solid.js +0 -2
- package/next.js +2 -2
- package/package.json +1 -1
- package/rules/baseSettings.js +322 -624
- package/rules/file-progress.js +62 -0
- package/rules/jsdoc-newline-before-returns.js +48 -0
- package/rules/jsdoc-optional-param-brackets.js +351 -0
- package/rules/jsdoc-single-line.js +44 -0
- package/rules/jsx-pascal-case-rule.js +1 -1
- package/rules/plugin-import-rules.js +13 -192
- package/rules/plugin-jsdoc-rules.js +81 -116
- package/rules/plugin-jsx-a11y-rules.js +1 -1
- package/rules/plugin-next-rules.js +28 -36
- package/rules/plugin-node-rules.js +72 -36
- package/rules/plugin-react-rules.js +111 -627
- package/rules/plugin-stylistic-rules.js +126 -84
- package/rules/plugin-typescript-rules.js +90 -200
- package/rules/plugin-vitest-rules.js +209 -21
- package/eslint.back.js +0 -9
- package/rules/plugin-solid-rules.js +0 -136
- package/rules/test-rules.js +0 -331
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import nanospinner from "nanospinner";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import pc from "picocolors";
|
|
4
|
+
|
|
5
|
+
const spinner = nanospinner.createSpinner("", {
|
|
6
|
+
frames: ["|", "/", "-", "\\"],
|
|
7
|
+
color: "cyan",
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
let bindExit = false;
|
|
11
|
+
let initialReportDone = false;
|
|
12
|
+
|
|
13
|
+
const defaultSettings = {
|
|
14
|
+
hide: false,
|
|
15
|
+
hideFileName: false,
|
|
16
|
+
successMessage: "Lint done.",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const exitCallback = (exitCode, settings) => {
|
|
20
|
+
if (exitCode === 0 && settings.hide !== true) {
|
|
21
|
+
spinner.success({ text: settings.successMessage });
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const create = (context) => {
|
|
26
|
+
const settings = { ...defaultSettings, ...context.settings.progress };
|
|
27
|
+
|
|
28
|
+
if (!bindExit) {
|
|
29
|
+
process.on("exit", (code) => {
|
|
30
|
+
exitCallback(code, settings);
|
|
31
|
+
});
|
|
32
|
+
bindExit = true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (settings.hide) {
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!settings.hideFileName) {
|
|
40
|
+
const { filename } = context;
|
|
41
|
+
const relativeFilePath = path.relative(context.cwd, filename);
|
|
42
|
+
spinner.update({ text: `Processing: ${pc.green(relativeFilePath)} \n` });
|
|
43
|
+
} else if (!initialReportDone) {
|
|
44
|
+
spinner.update({ text: "Linting...\n" });
|
|
45
|
+
initialReportDone = true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
spinner.spin();
|
|
49
|
+
return {};
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const progress = {
|
|
53
|
+
name: import.meta.filename,
|
|
54
|
+
meta: {
|
|
55
|
+
type: "suggestion",
|
|
56
|
+
messages: [],
|
|
57
|
+
schema: [],
|
|
58
|
+
},
|
|
59
|
+
create,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export default { rules: { activate: progress } };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enforce a blank JSDoc line immediately before the @returns tag.
|
|
3
|
+
*/
|
|
4
|
+
export default {
|
|
5
|
+
meta: {
|
|
6
|
+
type: "layout",
|
|
7
|
+
docs: {
|
|
8
|
+
description: "require a blank line before @returns in JSDoc blocks",
|
|
9
|
+
},
|
|
10
|
+
fixable: "whitespace",
|
|
11
|
+
schema: [],
|
|
12
|
+
},
|
|
13
|
+
create: (context) => {
|
|
14
|
+
const comments = context.sourceCode.getAllComments();
|
|
15
|
+
return {
|
|
16
|
+
Program: () => {
|
|
17
|
+
for (const comment of comments) {
|
|
18
|
+
if (comment.type !== "Block" || !comment.value.startsWith("*")) continue;
|
|
19
|
+
const lines = comment.value.split("\n");
|
|
20
|
+
for (let i = 0; i < lines.length; i++) {
|
|
21
|
+
if (lines[i].trim().startsWith("* @returns")) {
|
|
22
|
+
if (i > 0 && lines[i - 1].trim() !== "*") {
|
|
23
|
+
context.report({
|
|
24
|
+
loc: {
|
|
25
|
+
start: {
|
|
26
|
+
line: comment.loc.start.line + i,
|
|
27
|
+
column: 0,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
message: "JSDoc blocks must have a blank line before `@returns`.",
|
|
31
|
+
fix: (fixer) => {
|
|
32
|
+
const prevLineEnd = comment.range[0] +
|
|
33
|
+
2 +
|
|
34
|
+
lines.slice(0, i).map((l) => l.length + 1).reduce((a, b) => a + b, 0);
|
|
35
|
+
return fixer.insertTextAfterRange(
|
|
36
|
+
[prevLineEnd - 1, prevLineEnd],
|
|
37
|
+
" *\n",
|
|
38
|
+
);
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
},
|
|
48
|
+
};
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enforce brackets around optional parameter names in JSDoc @param tags.
|
|
3
|
+
* When a parameter has a `?` in its TypeScript signature or has a default value,
|
|
4
|
+
* the JSDoc @param tag should use brackets: `@param [paramName]` instead of `@param paramName`.
|
|
5
|
+
*/
|
|
6
|
+
export default {
|
|
7
|
+
meta: {
|
|
8
|
+
type: "problem",
|
|
9
|
+
docs: {
|
|
10
|
+
description: "require brackets around optional parameter names in JSDoc @param tags",
|
|
11
|
+
},
|
|
12
|
+
fixable: "code",
|
|
13
|
+
schema: [],
|
|
14
|
+
messages: {
|
|
15
|
+
missingBrackets: "Optional parameter '{{paramName}}' should be wrapped in brackets: @param [{{paramName}}]",
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
create: (context) => {
|
|
19
|
+
/**
|
|
20
|
+
* Extracts parameter name from JSDoc @param line (without brackets).
|
|
21
|
+
*
|
|
22
|
+
* @param line - The JSDoc line.
|
|
23
|
+
*
|
|
24
|
+
* @returns - The parameter name or null.
|
|
25
|
+
*/
|
|
26
|
+
function extractParamName(line) {
|
|
27
|
+
// Match: @param {Type} paramName or @param paramName (but not [paramName])
|
|
28
|
+
// Also matches nested properties like paramName.property
|
|
29
|
+
const match = line.match(/@param\s+(?:\{[^}]+}\s+)?(\w+(?:\.\w+)*)/);
|
|
30
|
+
return match ? match[1] : null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Checks if a parameter name already has correct brackets.
|
|
35
|
+
*
|
|
36
|
+
* @param line - The JSDoc line.
|
|
37
|
+
* @param paramName - The parameter name to check (including nested properties).
|
|
38
|
+
*
|
|
39
|
+
* @returns - True if brackets are correctly placed.
|
|
40
|
+
*/
|
|
41
|
+
function hasBrackets(line, paramName) {
|
|
42
|
+
// Check if the entire parameter path is wrapped in brackets
|
|
43
|
+
// e.g., [paramName] or [paramName.property]
|
|
44
|
+
const escapedParamName = paramName.replace(/\./g, "\\.");
|
|
45
|
+
// eslint-disable-next-line no-useless-escape
|
|
46
|
+
const correctBracketPattern = new RegExp(`@param\s+(?:\{[^}]+}\s+)?\[${escapedParamName}\]`);
|
|
47
|
+
return correctBracketPattern.test(line);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Checks if a function parameter is optional.
|
|
52
|
+
*
|
|
53
|
+
* @param param - The function parameter node.
|
|
54
|
+
*
|
|
55
|
+
* @returns - True if the parameter is optional.
|
|
56
|
+
*/
|
|
57
|
+
function isOptionalParam(param) {
|
|
58
|
+
// Check if the parameter has a question mark (optional: true)
|
|
59
|
+
if (param.optional) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Check if the parameter has a default value
|
|
64
|
+
if (param.type === "AssignmentPattern") {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// For TypeScript, check TSParameterProperty
|
|
69
|
+
if (param.type === "TSParameterProperty") {
|
|
70
|
+
return param.parameter.optional || param.parameter.type === "AssignmentPattern";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Gets parameter name from a parameter node.
|
|
78
|
+
*
|
|
79
|
+
* @param param - The parameter node.
|
|
80
|
+
*
|
|
81
|
+
* @returns - The parameter name or null.
|
|
82
|
+
*/
|
|
83
|
+
function getParamName(param) {
|
|
84
|
+
if (param.type === "Identifier") {
|
|
85
|
+
return param.name;
|
|
86
|
+
}
|
|
87
|
+
if (param.type === "ObjectPattern") {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
if (param.type === "AssignmentPattern" && param.left.type === "Identifier") {
|
|
91
|
+
return param.left.name;
|
|
92
|
+
}
|
|
93
|
+
if (param.type === "TSParameterProperty") {
|
|
94
|
+
if (param.parameter.type === "Identifier") {
|
|
95
|
+
return param.parameter.name;
|
|
96
|
+
}
|
|
97
|
+
if (param.parameter.type === "AssignmentPattern" && param.parameter.left.type === "Identifier") {
|
|
98
|
+
return param.parameter.left.name;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Collects optional JSDoc parameter paths from a destructured object parameter.
|
|
106
|
+
*
|
|
107
|
+
* @param pattern - The object pattern node.
|
|
108
|
+
* @param parentName - The top-level JSDoc parameter name.
|
|
109
|
+
* @param typeAnnotation - The TypeScript type annotation node.
|
|
110
|
+
* @param optionalParams - The set to mutate with optional param paths.
|
|
111
|
+
*/
|
|
112
|
+
function collectOptionalObjectPatternParams(pattern, parentName, typeAnnotation, optionalParams) {
|
|
113
|
+
if (!pattern || pattern.type !== "ObjectPattern") {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const typeNode = typeAnnotation?.typeAnnotation;
|
|
118
|
+
if (!typeNode || typeNode.type !== "TSTypeLiteral") {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
for (const property of pattern.properties) {
|
|
123
|
+
if (property.type !== "Property" || property.computed) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const keyName = property.key.type === "Identifier" ? property.key.name : null;
|
|
128
|
+
|
|
129
|
+
if (!keyName) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const matchingMember = typeNode.members.find((member) =>
|
|
134
|
+
member.type === "TSPropertySignature" &&
|
|
135
|
+
!member.computed &&
|
|
136
|
+
member.key.type === "Identifier" &&
|
|
137
|
+
member.key.name === keyName,
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
if (matchingMember?.optional) {
|
|
141
|
+
optionalParams.add(`${parentName}.${keyName}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Gets a TypeScript type literal for props defined on a variable annotation like FC<{ ... }>.
|
|
148
|
+
*
|
|
149
|
+
* @param node - The function node.
|
|
150
|
+
*
|
|
151
|
+
* @returns - The type literal node or null.
|
|
152
|
+
*/
|
|
153
|
+
function getVariablePropsTypeLiteral(node) {
|
|
154
|
+
if (!node.parent || node.parent.type !== "VariableDeclarator") {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const typeAnnotation = node.parent.id?.typeAnnotation?.typeAnnotation;
|
|
159
|
+
|
|
160
|
+
if (!typeAnnotation || typeAnnotation.type !== "TSTypeReference") {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const typeParams = typeAnnotation.typeArguments?.params;
|
|
165
|
+
|
|
166
|
+
if (!typeParams || typeParams.length === 0) {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const [firstTypeParam] = typeParams;
|
|
171
|
+
|
|
172
|
+
return firstTypeParam?.type === "TSTypeLiteral" ? firstTypeParam : null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Gets the JSDoc comment associated with a node.
|
|
177
|
+
*
|
|
178
|
+
* @param node - The AST node.
|
|
179
|
+
*
|
|
180
|
+
* @returns - The JSDoc comment or null.
|
|
181
|
+
*/
|
|
182
|
+
function getJSDocComment(node) {
|
|
183
|
+
// For arrow/function expressions in variable declarations, get the VariableDeclaration
|
|
184
|
+
let targetNode = node;
|
|
185
|
+
if (node.parent && node.parent.type === "VariableDeclarator") {
|
|
186
|
+
targetNode = node.parent.parent; // VariableDeclaration
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// For function expressions inside class methods, get the MethodDefinition
|
|
190
|
+
if (node.parent && node.parent.type === "MethodDefinition") {
|
|
191
|
+
targetNode = node.parent;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// For exported functions, check the parent node
|
|
195
|
+
if (targetNode.parent && targetNode.parent.type === "ExportNamedDeclaration") {
|
|
196
|
+
targetNode = targetNode.parent;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const comments = context.sourceCode.getCommentsBefore(targetNode);
|
|
200
|
+
// Look for the last block comment that looks like JSDoc
|
|
201
|
+
for (let i = comments.length - 1; i >= 0; i--) {
|
|
202
|
+
const comment = comments[i];
|
|
203
|
+
if (comment.type === "Block" && comment.value.startsWith("*")) {
|
|
204
|
+
return comment;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Checks a function node for optional parameters.
|
|
212
|
+
*
|
|
213
|
+
* @param node - The function node.
|
|
214
|
+
*/
|
|
215
|
+
function checkFunction(node) {
|
|
216
|
+
if (!node.params || node.params.length === 0) return;
|
|
217
|
+
|
|
218
|
+
const jsdocComment = getJSDocComment(node);
|
|
219
|
+
if (!jsdocComment) return;
|
|
220
|
+
|
|
221
|
+
// Build a set of optional parameter names
|
|
222
|
+
const optionalParams = new Set();
|
|
223
|
+
for (const param of node.params) {
|
|
224
|
+
if (isOptionalParam(param)) {
|
|
225
|
+
const paramName = getParamName(param);
|
|
226
|
+
if (paramName) {
|
|
227
|
+
optionalParams.add(paramName);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (param.type === "ObjectPattern") {
|
|
232
|
+
collectOptionalObjectPatternParams(param, "props", param.typeAnnotation, optionalParams);
|
|
233
|
+
|
|
234
|
+
const variablePropsTypeLiteral = getVariablePropsTypeLiteral(node);
|
|
235
|
+
if (variablePropsTypeLiteral) {
|
|
236
|
+
collectOptionalObjectPatternParams(
|
|
237
|
+
param,
|
|
238
|
+
"props",
|
|
239
|
+
{ typeAnnotation: variablePropsTypeLiteral },
|
|
240
|
+
optionalParams,
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (param.type === "AssignmentPattern" && param.left.type === "ObjectPattern") {
|
|
246
|
+
collectOptionalObjectPatternParams(param.left, "props", param.left.typeAnnotation, optionalParams);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (optionalParams.size === 0) return;
|
|
251
|
+
|
|
252
|
+
// Check JSDoc @param tags
|
|
253
|
+
const lines = jsdocComment.value.split("\n");
|
|
254
|
+
for (let i = 0; i < lines.length; i++) {
|
|
255
|
+
const line = lines[i];
|
|
256
|
+
const trimmedLine = line.trim();
|
|
257
|
+
|
|
258
|
+
if (trimmedLine.startsWith("* @param")) {
|
|
259
|
+
const paramName = extractParamName(trimmedLine);
|
|
260
|
+
|
|
261
|
+
if (paramName && !hasBrackets(trimmedLine, paramName)) {
|
|
262
|
+
// Check if this is a top-level optional param or a nested property
|
|
263
|
+
const isTopLevelOptional = optionalParams.has(paramName);
|
|
264
|
+
const isNestedProperty = paramName.includes(".");
|
|
265
|
+
const parentParam = isNestedProperty ? paramName.split(".")[0] : null;
|
|
266
|
+
const isNestedOfOptional = isNestedProperty && parentParam && optionalParams.has(parentParam);
|
|
267
|
+
|
|
268
|
+
if (isTopLevelOptional || isNestedOfOptional) {
|
|
269
|
+
const lineNumber = jsdocComment.loc.start.line + i;
|
|
270
|
+
|
|
271
|
+
context.report({
|
|
272
|
+
loc: {
|
|
273
|
+
start: {
|
|
274
|
+
line: lineNumber,
|
|
275
|
+
column: 0,
|
|
276
|
+
},
|
|
277
|
+
end: {
|
|
278
|
+
line: lineNumber,
|
|
279
|
+
column: line.length,
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
messageId: "missingBrackets",
|
|
283
|
+
data: {
|
|
284
|
+
paramName,
|
|
285
|
+
},
|
|
286
|
+
fix: (fixer) => {
|
|
287
|
+
// Calculate the position in the comment
|
|
288
|
+
const lineStart = jsdocComment.range[0] + 2 + // "/*"
|
|
289
|
+
lines.slice(0, i).reduce((sum, l) => sum + l.length + 1, 0);
|
|
290
|
+
|
|
291
|
+
// Find the parameter name in the line and wrap it with brackets
|
|
292
|
+
// Escape dots in paramName for regex
|
|
293
|
+
const escapedParamName = paramName.replace(/\./g, "\\.");
|
|
294
|
+
// Match the pattern, accounting for the * prefix and whitespace
|
|
295
|
+
const paramMatch = line.match(new RegExp(`(\\*\\s*@param\\s+(?:\\{[^}]+\\}\\s+)?)(${escapedParamName})(?=\\s|$)`));
|
|
296
|
+
if (paramMatch) {
|
|
297
|
+
const paramStartInLine = paramMatch.index + paramMatch[1].length;
|
|
298
|
+
const paramStart = lineStart + paramStartInLine;
|
|
299
|
+
const paramEnd = paramStart + paramName.length;
|
|
300
|
+
|
|
301
|
+
return [
|
|
302
|
+
fixer.insertTextBeforeRange([paramStart, paramEnd], "["),
|
|
303
|
+
fixer.insertTextAfterRange([paramStart, paramEnd], "]"),
|
|
304
|
+
];
|
|
305
|
+
}
|
|
306
|
+
return null;
|
|
307
|
+
},
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Checks a variable declarator with function initialization.
|
|
317
|
+
*
|
|
318
|
+
* @param node - The VariableDeclarator node.
|
|
319
|
+
*/
|
|
320
|
+
function checkVariableDeclarator(node) {
|
|
321
|
+
// Check if the init is a function expression or arrow function
|
|
322
|
+
if (node.init &&
|
|
323
|
+
(node.init.type === "FunctionExpression" ||
|
|
324
|
+
node.init.type === "ArrowFunctionExpression")) {
|
|
325
|
+
checkFunction(node.init);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Checks a function declaration.
|
|
331
|
+
*
|
|
332
|
+
* @param node - The function declaration node.
|
|
333
|
+
*/
|
|
334
|
+
function checkFunctionDeclaration(node) {
|
|
335
|
+
// Only check top-level function declarations (not nested ones)
|
|
336
|
+
// For arrow/function expressions, they'll be checked via VariableDeclarator
|
|
337
|
+
if (node.parent.type === "ExportNamedDeclaration" ||
|
|
338
|
+
node.parent.type === "Program") {
|
|
339
|
+
checkFunction(node);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return {
|
|
344
|
+
FunctionDeclaration: checkFunctionDeclaration,
|
|
345
|
+
VariableDeclarator: checkVariableDeclarator,
|
|
346
|
+
MethodDefinition: (node) => {
|
|
347
|
+
if (node.value) checkFunction(node.value);
|
|
348
|
+
},
|
|
349
|
+
};
|
|
350
|
+
},
|
|
351
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enforce single-line JSDoc comments to use inline format.
|
|
3
|
+
*/
|
|
4
|
+
export default {
|
|
5
|
+
meta: {
|
|
6
|
+
type: "layout",
|
|
7
|
+
docs: {
|
|
8
|
+
description: "require single-line JSDoc comments to use /** ... */ format instead of multi-line format",
|
|
9
|
+
},
|
|
10
|
+
fixable: "whitespace",
|
|
11
|
+
schema: [],
|
|
12
|
+
},
|
|
13
|
+
create: (context) => {
|
|
14
|
+
const comments = context.sourceCode.getAllComments();
|
|
15
|
+
return {
|
|
16
|
+
Program: () => {
|
|
17
|
+
for (const comment of comments) {
|
|
18
|
+
if (comment.type !== "Block" || !comment.value.startsWith("*")) continue;
|
|
19
|
+
|
|
20
|
+
const lines = comment.value.split("\n");
|
|
21
|
+
|
|
22
|
+
// Skip if it's already a single-line comment (doesn't contain newlines in the value)
|
|
23
|
+
if (lines.length === 1) continue;
|
|
24
|
+
|
|
25
|
+
// Check if this is a multi-line block that's actually just one line of content
|
|
26
|
+
const contentLines = lines
|
|
27
|
+
.map((line) => line.trim())
|
|
28
|
+
.filter((line) => line !== "*" && line !== "");
|
|
29
|
+
|
|
30
|
+
// If there's only one line of actual content (not counting empty * lines)
|
|
31
|
+
if (contentLines.length === 1) {
|
|
32
|
+
const content = contentLines[0].replace(/^\*\s*/, "").replace(/\s*$/, "");
|
|
33
|
+
|
|
34
|
+
context.report({
|
|
35
|
+
loc: comment.loc,
|
|
36
|
+
message: "Single-line JSDoc comments should use inline format: /** ... */",
|
|
37
|
+
fix: (fixer) => fixer.replaceTextRange(comment.range, `/** ${content} */`),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
};
|
|
@@ -31,7 +31,7 @@ export const jsxElementRule = {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
function isPascalCase(name) {
|
|
34
|
-
return /^[A-Z][a-zA-Z0-9]
|
|
34
|
+
return (/^[A-Z][a-zA-Z0-9]*$/).test(name) &&
|
|
35
35
|
// allow exceptions for the following names
|
|
36
36
|
!["GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PATCH"].includes(name);
|
|
37
37
|
}
|