@lewishowles/lint-config 0.4.0 → 0.6.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.
@@ -1,70 +0,0 @@
1
- import { formatJSDocBlockStructure, isJSDoc } from "../utils/jsdoc.js";
2
- import { getCommentText, replaceMinimalComment } from "../utils/source.js";
3
-
4
- /**
5
- * Create the JSDoc block-comment formatting rule.
6
- *
7
- * @returns {object}
8
- * The Oxlint rule definition.
9
- */
10
- export default {
11
- meta: {
12
- docs: { description: "Format JSDoc block comments." },
13
- fixable: "code",
14
- type: "layout",
15
- },
16
- /**
17
- * Create the rule's node visitors.
18
- *
19
- * @param {object} context
20
- * The Oxlint rule context.
21
- *
22
- * @returns {object}
23
- * The visitor functions for this rule.
24
- */
25
- createOnce(context) {
26
- return {
27
- /**
28
- * Format every JSDoc comment's block structure in the file.
29
- */
30
- Program() {
31
- for (const comment of context.sourceCode.getAllComments()) {
32
- if (comment.type !== "Block") {
33
- continue;
34
- }
35
-
36
- // The comment's raw source text.
37
- const commentText = getCommentText(context.sourceCode, comment);
38
-
39
- if (!isJSDoc(commentText)) {
40
- continue;
41
- }
42
-
43
- // The comment, with its block structure and delimiters normalised.
44
- const formattedComment = formatJSDocBlockStructure(context.sourceCode, comment);
45
-
46
- if (formattedComment === commentText) {
47
- continue;
48
- }
49
-
50
- context.report({
51
- /**
52
- * Apply the formatted replacement to the comment.
53
- *
54
- * @param {object} fixer
55
- * The Oxlint fixer.
56
- *
57
- * @returns {object}
58
- * The fix to apply.
59
- */
60
- fix: (fixer) => {
61
- return replaceMinimalComment(fixer, comment, commentText, formattedComment);
62
- },
63
- message: "JSDoc comments must use the configured block format.",
64
- node: comment,
65
- });
66
- }
67
- },
68
- };
69
- },
70
- };
@@ -1,70 +0,0 @@
1
- import { formatJSDocTagFormatting, hasTargetJSDocTag, isJSDoc } from "../utils/jsdoc.js";
2
- import { getCommentText, replaceMinimalComment } from "../utils/source.js";
3
-
4
- /**
5
- * Create the JSDoc tag-formatting rule.
6
- *
7
- * @returns {object}
8
- * The Oxlint rule definition.
9
- */
10
- export default {
11
- meta: {
12
- docs: { description: "Format Phase 1 JSDoc tag spacing and grouping." },
13
- fixable: "code",
14
- type: "layout",
15
- },
16
- /**
17
- * Create the rule's node visitors.
18
- *
19
- * @param {object} context
20
- * The Oxlint rule context.
21
- *
22
- * @returns {object}
23
- * The visitor functions for this rule.
24
- */
25
- createOnce(context) {
26
- return {
27
- /**
28
- * Format every JSDoc comment's tags in the file.
29
- */
30
- Program() {
31
- for (const comment of context.sourceCode.getAllComments()) {
32
- if (comment.type !== "Block") {
33
- continue;
34
- }
35
-
36
- // The comment's raw source text.
37
- const commentText = getCommentText(context.sourceCode, comment);
38
-
39
- if (!isJSDoc(commentText) || !hasTargetJSDocTag(context.sourceCode, comment)) {
40
- continue;
41
- }
42
-
43
- // The comment, with its tag spacing, order, and grouping normalised.
44
- const formattedComment = formatJSDocTagFormatting(context.sourceCode, comment);
45
-
46
- if (formattedComment === commentText) {
47
- continue;
48
- }
49
-
50
- context.report({
51
- /**
52
- * Apply the formatted replacement to the comment.
53
- *
54
- * @param {object} fixer
55
- * The Oxlint fixer.
56
- *
57
- * @returns {object}
58
- * The fix to apply.
59
- */
60
- fix: (fixer) => {
61
- return replaceMinimalComment(fixer, comment, commentText, formattedComment);
62
- },
63
- message: "JSDoc tags must use the configured spacing, order, and grouping.",
64
- node: comment,
65
- });
66
- }
67
- },
68
- };
69
- },
70
- };
@@ -1,86 +0,0 @@
1
- import {
2
- getCommentText,
3
- getLineCommentGroups,
4
- getLineIndent,
5
- getLineStart,
6
- } from "../utils/source.js";
7
-
8
- /**
9
- * Create the line-comment alignment rule.
10
- *
11
- * @returns {object}
12
- * The Oxlint rule definition.
13
- */
14
- export default {
15
- meta: {
16
- docs: { description: "Align wrapped line comments with their first marker." },
17
- fixable: "code",
18
- type: "layout",
19
- },
20
- /**
21
- * Create the rule's node visitors.
22
- *
23
- * @param {object} context
24
- * The Oxlint rule context.
25
- *
26
- * @returns {object}
27
- * The visitor functions for this rule.
28
- */
29
- createOnce(context) {
30
- return {
31
- /**
32
- * Align every wrapped line-comment group in the file.
33
- */
34
- Program() {
35
- for (const commentGroup of getLineCommentGroups(context.sourceCode)) {
36
- if (commentGroup.length < 2) {
37
- continue;
38
- }
39
-
40
- // The group's leading comment's indentation.
41
- const firstIndent = getLineIndent(context.sourceCode, commentGroup[0].range[0]);
42
-
43
- if (firstIndent === null) {
44
- continue;
45
- }
46
-
47
- // The reindentation fixes for the group's comments.
48
- const fixes = [];
49
-
50
- for (const comment of commentGroup) {
51
- // The comment's current indentation.
52
- const indentation = getLineIndent(context.sourceCode, comment.range[0]);
53
-
54
- if (indentation === null || indentation === firstIndent) {
55
- continue;
56
- }
57
-
58
- fixes.push({
59
- range: [getLineStart(context.sourceCode, comment.range[0]), comment.range[1]],
60
- text: `${firstIndent}${getCommentText(context.sourceCode, comment)}`,
61
- });
62
- }
63
-
64
- if (fixes.length === 0) {
65
- continue;
66
- }
67
-
68
- context.report({
69
- /**
70
- * Apply the group's alignment fixes.
71
- *
72
- * @param {object} fixer
73
- * The Oxlint fixer.
74
- *
75
- * @returns {object[]}
76
- * The fixes to apply.
77
- */
78
- fix: (fixer) => fixes.map((fix) => fixer.replaceTextRange(fix.range, fix.text)),
79
- message: "Wrapped line comments must align with the first comment marker.",
80
- node: commentGroup[0],
81
- });
82
- }
83
- },
84
- };
85
- },
86
- };
@@ -1,159 +0,0 @@
1
- import { formatJSDocWrapping, isJSDoc } from "../utils/jsdoc.js";
2
-
3
- import {
4
- getCommentText,
5
- getLineIndent,
6
- getNewline,
7
- isDirectiveComment,
8
- replaceMinimalComment,
9
- } from "../utils/source.js";
10
-
11
- import { formatSentence, wrapWords } from "../utils/wrap.js";
12
-
13
- // The line length this rule wraps comments to.
14
- const maximumLineLength = 80;
15
-
16
- /**
17
- * Wrap a line comment to the configured maximum width.
18
- *
19
- * @param {object} sourceCode
20
- * The Oxlint source code object.
21
- * @param {object} comment
22
- * The line comment token.
23
- *
24
- * @returns {string|null}
25
- * The wrapped comment, or null when it is not a standalone comment.
26
- */
27
- function formatLineComment(sourceCode, comment) {
28
- // The comment's current indentation.
29
- const indentation = getLineIndent(sourceCode, comment.range[0]);
30
-
31
- if (indentation === null) {
32
- return null;
33
- }
34
-
35
- // The available width, allowing for the indent and "// " prefix.
36
- const width = maximumLineLength - indentation.length - 3;
37
- // The comment's undecorated text.
38
- const text = comment.value.trim();
39
-
40
- if (isDirectiveComment(comment)) {
41
- return null;
42
- }
43
-
44
- if (text === "") {
45
- return "//";
46
- }
47
-
48
- return wrapWords(text, Math.max(1, width))
49
- .map((line, index) => `${index === 0 ? "" : indentation}// ${line}`)
50
- .join(getNewline(sourceCode.text));
51
- }
52
-
53
- /**
54
- * Wrap an ordinary block comment to the configured maximum width.
55
- *
56
- * @param {object} sourceCode
57
- * The Oxlint source code object.
58
- * @param {object} comment
59
- * The block comment token.
60
- *
61
- * @returns {string|null}
62
- * The wrapped comment, or null when it is not a standalone comment.
63
- */
64
- function formatBlockComment(sourceCode, comment) {
65
- // The comment's current indentation.
66
- const indentation = getLineIndent(sourceCode, comment.range[0]);
67
-
68
- if (indentation === null) {
69
- return null;
70
- }
71
-
72
- // The comment's raw source text.
73
- const commentText = getCommentText(sourceCode, comment);
74
- // The comment body, sentence-formatted.
75
- const text = formatSentence(commentText.slice(2, -2).trim());
76
- // The available width, allowing for the indent and " * " prefix.
77
- const width = maximumLineLength - indentation.length - 3;
78
- // The comment body, rewrapped to the available width.
79
- const lines = wrapWords(text, Math.max(1, width));
80
-
81
- return ["/*", ...lines.map((line) => `${indentation} * ${line}`), `${indentation} */`].join(
82
- getNewline(sourceCode.text),
83
- );
84
- }
85
-
86
- /**
87
- * Create the maximum-line-length rule.
88
- *
89
- * @returns {object}
90
- * The Oxlint rule definition.
91
- */
92
- export default {
93
- meta: {
94
- docs: { description: "Wrap comments at 80 characters." },
95
- fixable: "code",
96
- type: "layout",
97
- },
98
- /**
99
- * Create the rule's node visitors.
100
- *
101
- * @param {object} context
102
- * The Oxlint rule context.
103
- *
104
- * @returns {object}
105
- * The visitor functions for this rule.
106
- */
107
- createOnce(context) {
108
- return {
109
- /**
110
- * Wrap every over-length comment in the file.
111
- */
112
- Program() {
113
- for (const comment of context.sourceCode.getAllComments()) {
114
- if (comment.type === "Shebang") {
115
- continue;
116
- }
117
-
118
- // The comment's raw source text.
119
- const commentText = getCommentText(context.sourceCode, comment);
120
- // The comment's individual source lines.
121
- const lines = commentText.split(/\r\n|\n|\r/);
122
-
123
- if (!lines.some((line) => line.length > maximumLineLength)) {
124
- continue;
125
- }
126
-
127
- // The comment, rewrapped using the formatter matching its type.
128
- const formattedComment =
129
- comment.type === "Line"
130
- ? formatLineComment(context.sourceCode, comment)
131
- : isJSDoc(commentText)
132
- ? formatJSDocWrapping(context.sourceCode, comment)
133
- : formatBlockComment(context.sourceCode, comment);
134
-
135
- if (formattedComment === null || formattedComment === commentText) {
136
- continue;
137
- }
138
-
139
- context.report({
140
- /**
141
- * Apply the wrapped replacement to the comment.
142
- *
143
- * @param {object} fixer
144
- * The Oxlint fixer.
145
- *
146
- * @returns {object}
147
- * The fix to apply.
148
- */
149
- fix: (fixer) => {
150
- return replaceMinimalComment(fixer, comment, commentText, formattedComment);
151
- },
152
- message: "Comment exceeds 80 characters.",
153
- node: comment,
154
- });
155
- }
156
- },
157
- };
158
- },
159
- };
@@ -1,290 +0,0 @@
1
- import {
2
- getCommentNeighbours,
3
- getCommentText,
4
- getLineCommentGroups,
5
- getLineIndent,
6
- getLineStart,
7
- getNewline,
8
- isDirectiveComment,
9
- isLeadingComment,
10
- } from "../utils/source.js";
11
-
12
- /**
13
- * Return continuation comments indexed by their group leader.
14
- *
15
- * @param {object[][]} lineCommentGroups
16
- * The adjacent line-comment groups.
17
- *
18
- * @returns {object}
19
- * The continuation comments and their leaders.
20
- */
21
- function getLineCommentContinuations(lineCommentGroups) {
22
- // The comments that follow a group's leader, across every group.
23
- const continuationComments = new Set();
24
- // Each group's continuation comments, indexed by their leader.
25
- const continuationsByLeader = new Map();
26
-
27
- for (const group of lineCommentGroups) {
28
- if (group.length < 2) {
29
- continue;
30
- }
31
-
32
- // The group's leading comment and its continuations.
33
- const [leader, ...continuations] = group;
34
-
35
- continuationsByLeader.set(leader, continuations);
36
-
37
- for (const continuation of continuations) {
38
- continuationComments.add(continuation);
39
- }
40
- }
41
-
42
- return { continuationComments, continuationsByLeader };
43
- }
44
-
45
- /**
46
- * Return indentation relative to a comment's current indentation.
47
- *
48
- * @param {string} indentation
49
- * The line indentation.
50
- * @param {string} commentIndent
51
- * The leading comment's current indentation.
52
- *
53
- * @returns {string}
54
- * The indentation to preserve after reindenting.
55
- */
56
- function getRelativeIndent(indentation, commentIndent) {
57
- return indentation.startsWith(commentIndent)
58
- ? indentation.slice(commentIndent.length)
59
- : indentation;
60
- }
61
-
62
- /**
63
- * Reindent every line of a leading comment.
64
- *
65
- * @param {object} sourceCode
66
- * The Oxlint source code object.
67
- * @param {object} comment
68
- * The leading comment token.
69
- * @param {string} commentIndent
70
- * The comment's current indentation.
71
- * @param {string} expectedIndent
72
- * The documented code's indentation.
73
- *
74
- * @returns {string}
75
- * The reindented comment text.
76
- */
77
- function getReindentedCommentText(sourceCode, comment, commentIndent, expectedIndent) {
78
- return getCommentText(sourceCode, comment)
79
- .split(/\r\n|\n|\r/)
80
- .map((line, lineIndex) => {
81
- if (lineIndex === 0) {
82
- return `${expectedIndent}${line}`;
83
- }
84
-
85
- // The line's current indentation.
86
- const lineIndent = line.match(/^[ \t]*/)[0];
87
- // The indentation to preserve relative to the comment's own indent.
88
- const relativeIndent = getRelativeIndent(lineIndent, commentIndent);
89
-
90
- return `${expectedIndent}${relativeIndent}${line.slice(lineIndent.length)}`;
91
- })
92
- .join(getNewline(sourceCode.text));
93
- }
94
-
95
- /**
96
- * Return replacements that align a leading comment group with its code.
97
- *
98
- * @param {object} sourceCode
99
- * The Oxlint source code object.
100
- * @param {object} comment
101
- * The leading comment token.
102
- * @param {object[]} continuations
103
- * The comment group's continuation tokens.
104
- * @param {string} actualIndent
105
- * The comment's current indentation.
106
- * @param {string} expectedIndent
107
- * The documented code's indentation.
108
- *
109
- * @returns {object[]}
110
- * The indentation replacements.
111
- */
112
- function getCommentIndentationFixes(
113
- sourceCode,
114
- comment,
115
- continuations,
116
- actualIndent,
117
- expectedIndent,
118
- ) {
119
- if (actualIndent === expectedIndent) {
120
- return [];
121
- }
122
-
123
- // The replacements, starting with the leading comment's reindent.
124
- const fixes = [
125
- {
126
- range: [getLineStart(sourceCode, comment.range[0]), comment.range[1]],
127
- text: getReindentedCommentText(sourceCode, comment, actualIndent, expectedIndent),
128
- },
129
- ];
130
-
131
- for (const continuation of continuations) {
132
- // The continuation's current indentation.
133
- const continuationIndent = getLineIndent(sourceCode, continuation.range[0]) ?? "";
134
- // The indentation to preserve relative to the leading comment's indent.
135
- const relativeIndent = getRelativeIndent(continuationIndent, actualIndent);
136
-
137
- fixes.push({
138
- range: [getLineStart(sourceCode, continuation.range[0]), continuation.range[1]],
139
- text: `${expectedIndent}${relativeIndent}${getCommentText(sourceCode, continuation)}`,
140
- });
141
- }
142
-
143
- return fixes;
144
- }
145
-
146
- /**
147
- * Return the replacement that closes the gap after a final leading comment.
148
- *
149
- * @param {object} sourceCode
150
- * The Oxlint source code object.
151
- * @param {object} comment
152
- * The leading comment token.
153
- * @param {object} next
154
- * The documented source token.
155
- * @param {object|undefined} followingComment
156
- * The comment after this one in source order, when there is one.
157
- * @param {string} expectedIndent
158
- * The documented code's indentation.
159
- *
160
- * @returns {object|null}
161
- * The gap replacement, or null when none is needed.
162
- */
163
- function getCommentGapFix(sourceCode, comment, next, followingComment, expectedIndent) {
164
- // Whether another comment sits between this one and its documented code.
165
- const followingCommentIntervenes =
166
- followingComment !== undefined && followingComment.range[0] <= next.range[0];
167
-
168
- if (followingCommentIntervenes && !isDirectiveComment(followingComment)) {
169
- return null;
170
- }
171
-
172
- // Stop at an intervening directive so the fix range never overlaps it.
173
- const gapEnd = followingCommentIntervenes ? followingComment.range[0] : next.range[0];
174
-
175
- // What currently follows the comment, up to the code or directive.
176
- const gap = sourceCode.text.slice(comment.range[1], gapEnd);
177
- // The gap the documented code's indentation requires.
178
- const desiredGap = `${getNewline(sourceCode.text)}${expectedIndent}`;
179
-
180
- return gap === desiredGap ? null : { range: [comment.range[1], gapEnd], text: desiredGap };
181
- }
182
-
183
- /**
184
- * Create the immediate-comment-placement rule.
185
- *
186
- * @returns {object}
187
- * The Oxlint rule definition.
188
- */
189
- export default {
190
- meta: {
191
- docs: { description: "Keep comments immediately before documented code." },
192
- fixable: "code",
193
- type: "layout",
194
- },
195
- /**
196
- * Create the rule's node visitors.
197
- *
198
- * @param {object} context
199
- * The Oxlint rule context.
200
- *
201
- * @returns {object}
202
- * The visitor functions for this rule.
203
- */
204
- createOnce(context) {
205
- return {
206
- /**
207
- * Align every leading comment in the file with its documented code.
208
- */
209
- Program() {
210
- // Every comment token in the file, in source order.
211
- const comments = context.sourceCode.getAllComments();
212
-
213
- // The continuation comments and their group leaders.
214
- const { continuationComments, continuationsByLeader } = getLineCommentContinuations(
215
- getLineCommentGroups(context.sourceCode),
216
- );
217
-
218
- for (const [index, comment] of comments.entries()) {
219
- if (
220
- comment.type === "Shebang" ||
221
- isDirectiveComment(comment) ||
222
- continuationComments.has(comment)
223
- ) {
224
- continue;
225
- }
226
-
227
- // The comment's neighbouring token and comment.
228
- const { next, previous } = getCommentNeighbours(context.sourceCode, comment);
229
-
230
- if (next === null || !isLeadingComment(context.sourceCode, comment, previous)) {
231
- continue;
232
- }
233
-
234
- // The documented code's indentation.
235
- const expectedIndent = getLineIndent(context.sourceCode, next.range[0]);
236
- // The comment's current indentation.
237
- const actualIndent = getLineIndent(context.sourceCode, comment.range[0]);
238
-
239
- if (expectedIndent === null || actualIndent === null) {
240
- continue;
241
- }
242
-
243
- // The reindentation fixes for the comment and its continuations.
244
- const fixes = getCommentIndentationFixes(
245
- context.sourceCode,
246
- comment,
247
- continuationsByLeader.get(comment) ?? [],
248
- actualIndent,
249
- expectedIndent,
250
- );
251
-
252
- // The next comment token, used to avoid overlapping gap fixes.
253
- const followingComment = comments[index + 1];
254
-
255
- // The fix that closes the gap between the comment and its code, when needed.
256
- const gapFix = getCommentGapFix(
257
- context.sourceCode,
258
- comment,
259
- next,
260
- followingComment,
261
- expectedIndent,
262
- );
263
-
264
- if (gapFix !== null) {
265
- fixes.push(gapFix);
266
- }
267
-
268
- if (fixes.length === 0) {
269
- continue;
270
- }
271
-
272
- context.report({
273
- /**
274
- * Apply the comment's alignment fixes.
275
- *
276
- * @param {object} fixer
277
- * The Oxlint fixer.
278
- *
279
- * @returns {object[]}
280
- * The fixes to apply.
281
- */
282
- fix: (fixer) => fixes.map((fix) => fixer.replaceTextRange(fix.range, fix.text)),
283
- message: "Comment must be immediately before the documented code.",
284
- node: comment,
285
- });
286
- }
287
- },
288
- };
289
- },
290
- };