@d3lm/lint-preset 1.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/dist/rules.js ADDED
@@ -0,0 +1,857 @@
1
+ // src/rules/block-scoped-case.ts
2
+ var leadingWhitespace = (line) => /^[ \t]*/.exec(line)?.[0].length ?? 0;
3
+ var rule = {
4
+ meta: {
5
+ type: "suggestion",
6
+ docs: {
7
+ description: "Require case clauses to use block scope (braces)."
8
+ },
9
+ fixable: "code",
10
+ schema: [],
11
+ messages: {
12
+ missingBlock: "Case clause should be wrapped in a block (braces)."
13
+ }
14
+ },
15
+ create(context) {
16
+ const sourceCode = context.sourceCode;
17
+ return {
18
+ SwitchCase(node) {
19
+ const { consequent } = node;
20
+ if (consequent.length === 0) {
21
+ return;
22
+ }
23
+ if (consequent.length === 1 && consequent[0].type === "BlockStatement") {
24
+ return;
25
+ }
26
+ context.report({
27
+ node,
28
+ messageId: "missingBlock",
29
+ fix(fixer) {
30
+ const caseKeyword = sourceCode.getFirstToken(node);
31
+ if (!caseKeyword) {
32
+ return [];
33
+ }
34
+ const colonToken = sourceCode.getTokenAfter(
35
+ node.test ? node.test : caseKeyword,
36
+ (token) => token.value === ":"
37
+ );
38
+ if (!colonToken) {
39
+ return [];
40
+ }
41
+ const lastConsequent = consequent.at(-1);
42
+ const lastToken = sourceCode.getLastToken(lastConsequent);
43
+ if (!lastToken) {
44
+ return [];
45
+ }
46
+ const caseIndent = " ".repeat(caseKeyword.loc.start.column);
47
+ const bodyIndent = caseIndent + " ";
48
+ const text = sourceCode.getText();
49
+ const bodyText = text.slice(colonToken.range[1], lastToken.range[1]);
50
+ const lines = bodyText.split("\n");
51
+ const restLines = lines.slice(1);
52
+ const restIndents = restLines.filter((line) => line.trim() !== "").map((line) => leadingWhitespace(line));
53
+ const indentDelta = restIndents.length > 0 ? bodyIndent.length - Math.min(...restIndents) : 0;
54
+ const bodyLines = [];
55
+ const firstLine = lines[0].trim();
56
+ if (firstLine) {
57
+ bodyLines.push(bodyIndent + firstLine);
58
+ }
59
+ for (const line of restLines) {
60
+ if (line.trim() === "") {
61
+ bodyLines.push("");
62
+ continue;
63
+ }
64
+ const lead = leadingWhitespace(line);
65
+ const newIndent = Math.max(0, lead + indentDelta);
66
+ bodyLines.push(" ".repeat(newIndent) + line.slice(lead));
67
+ }
68
+ while (bodyLines.length > 0 && bodyLines[0] === "") {
69
+ bodyLines.shift();
70
+ }
71
+ while (bodyLines.length > 0 && bodyLines.at(-1) === "") {
72
+ bodyLines.pop();
73
+ }
74
+ const result = ` {
75
+ ${bodyLines.join("\n")}
76
+ ${caseIndent}}`;
77
+ return fixer.replaceTextRange([colonToken.range[1], lastToken.range[1]], result);
78
+ }
79
+ });
80
+ }
81
+ };
82
+ }
83
+ };
84
+ var block_scoped_case_default = rule;
85
+
86
+ // src/rules/comment-preceding-blank-line.ts
87
+ var rule2 = {
88
+ meta: {
89
+ type: "layout",
90
+ docs: {
91
+ description: "Require a blank line before comments, with an exception for comments inside chained member-call expressions."
92
+ },
93
+ fixable: "whitespace",
94
+ schema: [
95
+ {
96
+ type: "object",
97
+ properties: {
98
+ allowInObjects: {
99
+ type: "boolean"
100
+ },
101
+ allowInArrays: {
102
+ type: "boolean"
103
+ },
104
+ allowInInterfaces: {
105
+ type: "boolean"
106
+ }
107
+ },
108
+ additionalProperties: false
109
+ }
110
+ ],
111
+ messages: {
112
+ missingBlankLine: "Expected blank line before comment."
113
+ }
114
+ },
115
+ create(context) {
116
+ const sourceCode = context.sourceCode;
117
+ const options = context.options[0] ?? {};
118
+ const allowInObjects = options.allowInObjects ?? false;
119
+ const allowInArrays = options.allowInArrays ?? false;
120
+ const allowInInterfaces = options.allowInInterfaces ?? false;
121
+ function isChainedCallContext(comment) {
122
+ if (!comment.range) {
123
+ return false;
124
+ }
125
+ let current = sourceCode.getNodeByRangeIndex(comment.range[0]) ?? void 0;
126
+ while (current?.type === "CallExpression" || current?.type === "MemberExpression") {
127
+ if (current.type === "CallExpression" && current.callee.type === "MemberExpression") {
128
+ return true;
129
+ }
130
+ current = current.parent;
131
+ }
132
+ return false;
133
+ }
134
+ function isCommentAllowedByEnclosure(comment) {
135
+ if (!comment.range) {
136
+ return false;
137
+ }
138
+ const node = sourceCode.getNodeByRangeIndex(comment.range[0]);
139
+ if (!node) {
140
+ return false;
141
+ }
142
+ const type = node.type;
143
+ if (allowInObjects && (type === "ObjectExpression" || type === "ObjectPattern")) {
144
+ return true;
145
+ }
146
+ if (allowInArrays && (type === "ArrayExpression" || type === "ArrayPattern")) {
147
+ return true;
148
+ }
149
+ if (allowInInterfaces && (type === "TSInterfaceBody" || type === "TSTypeLiteral")) {
150
+ return true;
151
+ }
152
+ return false;
153
+ }
154
+ function isAtBlockStart(comment) {
155
+ const tokenBefore = sourceCode.getTokenBefore(comment, {
156
+ includeComments: false
157
+ });
158
+ if (!tokenBefore) {
159
+ return true;
160
+ }
161
+ return ["{", "(", "["].includes(tokenBefore.value);
162
+ }
163
+ return {
164
+ Program() {
165
+ const comments = sourceCode.getAllComments();
166
+ for (const comment of comments) {
167
+ if (!comment.loc || !comment.range) {
168
+ continue;
169
+ }
170
+ const commentLine = comment.loc.start.line;
171
+ if (commentLine <= 1) {
172
+ continue;
173
+ }
174
+ if (isAtBlockStart(comment)) {
175
+ continue;
176
+ }
177
+ if (isChainedCallContext(comment)) {
178
+ continue;
179
+ }
180
+ if (isCommentAllowedByEnclosure(comment)) {
181
+ continue;
182
+ }
183
+ const tokenBefore = sourceCode.getTokenBefore(comment, {
184
+ includeComments: true
185
+ });
186
+ if (!tokenBefore?.loc) {
187
+ continue;
188
+ }
189
+ const linesBetween = commentLine - tokenBefore.loc.end.line;
190
+ if (linesBetween >= 2) {
191
+ continue;
192
+ }
193
+ if (linesBetween === 0) {
194
+ continue;
195
+ }
196
+ const commentStart = comment.range[0];
197
+ const lineStart = sourceCode.getText().lastIndexOf("\n", commentStart - 1) + 1;
198
+ context.report({
199
+ loc: comment.loc,
200
+ messageId: "missingBlankLine",
201
+ fix(fixer) {
202
+ return fixer.insertTextBeforeRange([lineStart, lineStart], "\n");
203
+ }
204
+ });
205
+ }
206
+ }
207
+ };
208
+ }
209
+ };
210
+ var comment_preceding_blank_line_default = rule2;
211
+
212
+ // src/rules/comment-syntax.ts
213
+ var URL_PATTERN = /\bhttps?:\/\//;
214
+ var DIRECTIVE_PATTERN = /^\s*(?:eslint(?:-[a-z-]+)?\b|@ts-[a-z-]+|@(?:jsx|jsxFrag|jsxImportSource|jsxRuntime)\b|istanbul\s|c8\s|v8\s|prettier-ignore\b|stylelint-[a-z-]+|webpack[A-Z][a-zA-Z]*|#(?:region|endregion)\b|type\s*:)/;
215
+ var TAG_PATTERN = /^\s*(?:TODO|FIXME|HACK|NOTE|BUG|XXX|SAFETY|WARNING|INFO|OPTIMIZE|REVIEW|PERF|DEPRECATED)\b/i;
216
+ var FIRST_WORD_PATTERN = /^\s*([A-Za-z_$][\w$]*)/;
217
+ var ALL_CAPS_WORD = /^[A-Z][A-Z0-9_]*$/;
218
+ var JSDOC_TAG_LINE = /^\s*\*\s*@[A-Za-z]/;
219
+ var SINGLE_LINE_JSDOC_TAG = /^@[A-Za-z][\w-]*$/;
220
+ var PARAM_DESC_RE = /^@param\s+(?:\{[^}]*\}\s+)?(\S+)\s+(.*\S)\s*$/;
221
+ var BLOCK_LINE_WITH_TEXT = /^(\s*\*)(\s*)(\S.*?)\s*$/;
222
+ var BLOCK_LINE_EMPTY = /^\s*\*\s*$/;
223
+ var CODE_FENCE_LINE = /^\s*\*\s*```/;
224
+ var DEFAULT_LINE_ENDINGS = Object.freeze(["etc.", "...", "e.g.", "i.e."]);
225
+ var DEFAULT_PARAGRAPH_ENDINGS = Object.freeze([".", "!", "?", ":", ";", "`", ")", "]", "}"]);
226
+ var DEFAULT_OPTIONS = {
227
+ ignoredWords: /* @__PURE__ */ new Set(),
228
+ ignoredPatterns: [],
229
+ ignoreUrls: true,
230
+ ignoreDirectives: true,
231
+ line: {
232
+ requireLeadingSpace: true,
233
+ requireLowercase: true,
234
+ forbidTrailingPunctuation: true,
235
+ allowedEndings: DEFAULT_LINE_ENDINGS
236
+ },
237
+ block: {
238
+ requireCapital: true,
239
+ requireTrailingPunctuation: true,
240
+ enforceOpening: false,
241
+ enforceClosing: false,
242
+ requireSpaceAfterStar: true,
243
+ requireParagraphCapital: true,
244
+ paragraphEndings: DEFAULT_PARAGRAPH_ENDINGS,
245
+ requireJSDocSpacing: true,
246
+ enforceParamStyle: true
247
+ }
248
+ };
249
+ function isUpperCase(char) {
250
+ return char !== char.toLowerCase() && char === char.toUpperCase();
251
+ }
252
+ function isLowerCase(char) {
253
+ return char !== char.toUpperCase() && char === char.toLowerCase();
254
+ }
255
+ function isTrailingPunctuation(char) {
256
+ return char === "." || char === "!" || char === "?" || char === ";" || char === ":";
257
+ }
258
+ function isBlockEndingPunctuation(char) {
259
+ return char === "." || char === "!" || char === "?" || char === ";" || char === ":" || char === ")" || char === "]" || char === "}" || char === "`" || char === ">";
260
+ }
261
+ function indexOfLastNonSpace(value) {
262
+ for (let index = value.length - 1; index >= 0; index -= 1) {
263
+ const char = value.codePointAt(index);
264
+ if (char !== 32 && char !== 9 && char !== 10 && char !== 13) {
265
+ return index;
266
+ }
267
+ }
268
+ return -1;
269
+ }
270
+ function endsWithAny(value, endings) {
271
+ for (const ending of endings) {
272
+ if (value.endsWith(ending)) {
273
+ return true;
274
+ }
275
+ }
276
+ return false;
277
+ }
278
+ function resolveOptions(raw) {
279
+ if (!raw || typeof raw !== "object") {
280
+ return DEFAULT_OPTIONS;
281
+ }
282
+ const input = raw;
283
+ const line = input.line ?? {};
284
+ const block = input.block ?? {};
285
+ const patterns = Array.isArray(input.ignoredPatterns) ? input.ignoredPatterns.map((source) => new RegExp(source)) : DEFAULT_OPTIONS.ignoredPatterns;
286
+ return {
287
+ ignoredWords: new Set(Array.isArray(input.ignoredWords) ? input.ignoredWords : []),
288
+ ignoredPatterns: patterns,
289
+ ignoreUrls: input.ignoreUrls ?? DEFAULT_OPTIONS.ignoreUrls,
290
+ ignoreDirectives: input.ignoreDirectives ?? DEFAULT_OPTIONS.ignoreDirectives,
291
+ line: { ...DEFAULT_OPTIONS.line, ...line },
292
+ block: { ...DEFAULT_OPTIONS.block, ...block }
293
+ };
294
+ }
295
+ function shouldSkipComment(value, options) {
296
+ const trimmed = value.trim();
297
+ if (!trimmed) {
298
+ return true;
299
+ }
300
+ if (options.ignoreDirectives && (DIRECTIVE_PATTERN.test(trimmed) || TAG_PATTERN.test(trimmed))) {
301
+ return true;
302
+ }
303
+ if (options.ignoreUrls && URL_PATTERN.test(trimmed)) {
304
+ return true;
305
+ }
306
+ for (const pattern of options.ignoredPatterns) {
307
+ if (pattern.test(trimmed)) {
308
+ return true;
309
+ }
310
+ }
311
+ return false;
312
+ }
313
+ function checkLineComment(context, comment, options) {
314
+ const { line } = options;
315
+ const raw = comment.value;
316
+ const loc = comment.loc;
317
+ const range = comment.range;
318
+ if (!loc || !range) {
319
+ return;
320
+ }
321
+ const rangeStart = range[0];
322
+ if (line.requireLeadingSpace && raw.length > 0) {
323
+ const first = raw.codePointAt(0);
324
+ if (first !== 32 && first !== 47 && first !== 9) {
325
+ context.report({
326
+ loc,
327
+ messageId: "lineLeadingSpace",
328
+ fix: (fixer) => fixer.insertTextAfterRange([rangeStart, rangeStart + 2], " ")
329
+ });
330
+ }
331
+ }
332
+ const trimmed = raw.trim();
333
+ if (!trimmed) {
334
+ return;
335
+ }
336
+ const firstWordMatch = FIRST_WORD_PATTERN.exec(trimmed);
337
+ if (firstWordMatch && line.requireLowercase) {
338
+ const word = firstWordMatch[1];
339
+ const firstChar = word[0];
340
+ if (isUpperCase(firstChar) && !ALL_CAPS_WORD.test(word) && !options.ignoredWords.has(word)) {
341
+ context.report({
342
+ loc,
343
+ messageId: "lineLowercase",
344
+ fix: (fixer) => {
345
+ const offset = rangeStart + 2 + raw.indexOf(firstChar);
346
+ return fixer.replaceTextRange([offset, offset + 1], firstChar.toLowerCase());
347
+ }
348
+ });
349
+ }
350
+ }
351
+ if (line.forbidTrailingPunctuation && !endsWithAny(trimmed, line.allowedEndings)) {
352
+ const lastChar = trimmed.at(-1) ?? "";
353
+ if (isTrailingPunctuation(lastChar)) {
354
+ context.report({
355
+ loc,
356
+ messageId: "lineTrailingPunctuation",
357
+ fix: (fixer) => {
358
+ const offset = rangeStart + 2 + indexOfLastNonSpace(raw);
359
+ return fixer.removeRange([offset, offset + 1]);
360
+ }
361
+ });
362
+ }
363
+ }
364
+ }
365
+ function checkSingleLineBlock(context, comment, options) {
366
+ const { block } = options;
367
+ const raw = comment.value;
368
+ const loc = comment.loc;
369
+ const range = comment.range;
370
+ if (!loc || !range) {
371
+ return;
372
+ }
373
+ const trimmed = raw.trim();
374
+ if (!trimmed) {
375
+ return;
376
+ }
377
+ if (raw.startsWith("*")) {
378
+ const inner = raw.slice(1).trim();
379
+ if (!inner) {
380
+ return;
381
+ }
382
+ if (SINGLE_LINE_JSDOC_TAG.test(inner)) {
383
+ return;
384
+ }
385
+ const sourceCode = context.sourceCode;
386
+ const sourceLine = sourceCode.lines[loc.start.line - 1] ?? "";
387
+ const prefix = sourceLine.slice(0, loc.start.column);
388
+ const canFix = /^\s*$/.test(prefix);
389
+ context.report({
390
+ loc,
391
+ messageId: "blockSingleLineJSDoc",
392
+ fix: canFix ? (fixer) => fixer.replaceTextRange(range, `/**
393
+ ${prefix} * ${inner}
394
+ ${prefix} */`) : void 0
395
+ });
396
+ return;
397
+ }
398
+ const rangeStart = range[0];
399
+ const firstWordMatch = FIRST_WORD_PATTERN.exec(trimmed);
400
+ if (firstWordMatch && block.requireCapital) {
401
+ const word = firstWordMatch[1];
402
+ const firstChar = word[0];
403
+ if (isLowerCase(firstChar) && !ALL_CAPS_WORD.test(word) && !options.ignoredWords.has(word)) {
404
+ context.report({
405
+ loc,
406
+ messageId: "blockCapital",
407
+ fix: (fixer) => {
408
+ const offset = rangeStart + 2 + raw.indexOf(firstChar);
409
+ return fixer.replaceTextRange([offset, offset + 1], firstChar.toUpperCase());
410
+ }
411
+ });
412
+ }
413
+ }
414
+ if (block.requireTrailingPunctuation) {
415
+ const lastChar = trimmed.at(-1) ?? "";
416
+ if (!isBlockEndingPunctuation(lastChar)) {
417
+ context.report({
418
+ loc,
419
+ messageId: "blockTrailingPunctuation",
420
+ fix: (fixer) => {
421
+ const offset = rangeStart + 2 + indexOfLastNonSpace(raw);
422
+ return fixer.insertTextAfterRange([offset, offset + 1], ".");
423
+ }
424
+ });
425
+ }
426
+ }
427
+ }
428
+ function offsetOfLine(comment, lines, lineIndex) {
429
+ const range = comment.range;
430
+ if (!range) {
431
+ return 0;
432
+ }
433
+ let offset = range[0] + 2;
434
+ for (let index = 0; index < lineIndex; index += 1) {
435
+ offset += lines[index].length + 1;
436
+ }
437
+ return offset;
438
+ }
439
+ function skipParagraphFirstWord(text, options) {
440
+ const match = FIRST_WORD_PATTERN.exec(text);
441
+ if (!match) {
442
+ return true;
443
+ }
444
+ const word = match[1];
445
+ return ALL_CAPS_WORD.test(word) || options.ignoredWords.has(word);
446
+ }
447
+ function reportParagraphEnding(context, comment, text, options) {
448
+ const { block } = options;
449
+ const loc = comment.loc;
450
+ if (!loc) {
451
+ return;
452
+ }
453
+ for (const ending of block.paragraphEndings) {
454
+ if (text.endsWith(ending)) {
455
+ return;
456
+ }
457
+ }
458
+ context.report({
459
+ loc,
460
+ messageId: "blockParagraphEnding",
461
+ data: { endings: `[${block.paragraphEndings.join(" ")}]` }
462
+ });
463
+ }
464
+ function checkParameterLine(context, comment, lines, lineIndex, text, starSegment, spaces) {
465
+ const loc = comment.loc;
466
+ if (!loc) {
467
+ return;
468
+ }
469
+ const match = PARAM_DESC_RE.exec(text);
470
+ if (!match) {
471
+ return;
472
+ }
473
+ const descPart = match[2];
474
+ const textStart = offsetOfLine(comment, lines, lineIndex) + starSegment.length + spaces.length;
475
+ if (!descPart.startsWith("- ")) {
476
+ const prefixMatch = /^@param\s+(?:\{[^}]*\}\s+)?\S+\s+/.exec(text);
477
+ if (!prefixMatch) {
478
+ return;
479
+ }
480
+ const descStartOffset = textStart + prefixMatch[0].length;
481
+ context.report({
482
+ loc,
483
+ messageId: "paramMissingDash",
484
+ fix: (fixer) => fixer.insertTextBeforeRange([descStartOffset, descStartOffset + 1], "- ")
485
+ });
486
+ return;
487
+ }
488
+ const description = descPart.slice(2).trim();
489
+ if (!description) {
490
+ return;
491
+ }
492
+ if (!description.endsWith(".")) {
493
+ const textEndOffset = textStart + text.length;
494
+ context.report({
495
+ loc,
496
+ messageId: "paramDescriptionPeriod",
497
+ fix: (fixer) => fixer.insertTextAfterRange([textEndOffset - 1, textEndOffset], ".")
498
+ });
499
+ }
500
+ }
501
+ function checkMultiLineBlock(context, comment, options) {
502
+ const { block } = options;
503
+ const loc = comment.loc;
504
+ const range = comment.range;
505
+ if (!loc || !range) {
506
+ return;
507
+ }
508
+ const lines = comment.value.split("\n");
509
+ const firstLine = lines[0];
510
+ const lastLine = lines.at(-1) ?? "";
511
+ if (block.enforceOpening) {
512
+ const opening = firstLine.trim();
513
+ if (opening !== "" && opening !== "*") {
514
+ context.report({ loc, messageId: "blockOpening" });
515
+ return;
516
+ }
517
+ }
518
+ if (block.enforceClosing) {
519
+ if (lastLine.trim() !== "") {
520
+ context.report({ loc, messageId: "blockClosing" });
521
+ return;
522
+ }
523
+ if (lines.length >= 3 && BLOCK_LINE_EMPTY.test(lines.at(-2) ?? "")) {
524
+ context.report({ loc, messageId: "blockEmptyLineBeforeClose" });
525
+ }
526
+ }
527
+ let insideCodeBlock = false;
528
+ let paragraphStart = true;
529
+ let previousText;
530
+ for (let index = 1; index < lines.length - 1; index += 1) {
531
+ const line = lines[index];
532
+ if (CODE_FENCE_LINE.test(line)) {
533
+ insideCodeBlock = !insideCodeBlock;
534
+ paragraphStart = true;
535
+ previousText = void 0;
536
+ continue;
537
+ }
538
+ if (insideCodeBlock) {
539
+ continue;
540
+ }
541
+ if (BLOCK_LINE_EMPTY.test(line)) {
542
+ if (previousText !== void 0 && block.requireParagraphCapital) {
543
+ reportParagraphEnding(context, comment, previousText, options);
544
+ }
545
+ paragraphStart = true;
546
+ previousText = void 0;
547
+ continue;
548
+ }
549
+ const match = BLOCK_LINE_WITH_TEXT.exec(line);
550
+ if (!match) {
551
+ continue;
552
+ }
553
+ const [, starSegment, spaces, text] = match;
554
+ if (block.requireSpaceAfterStar && spaces.length === 0) {
555
+ const lineOffset = offsetOfLine(comment, lines, index);
556
+ const starEnd = lineOffset + starSegment.length;
557
+ context.report({
558
+ loc,
559
+ messageId: "blockLineSpaceAfterStar",
560
+ fix: (fixer) => fixer.insertTextAfterRange([starEnd - 1, starEnd], " ")
561
+ });
562
+ continue;
563
+ }
564
+ if (block.requireJSDocSpacing && JSDOC_TAG_LINE.test(line) && previousText !== void 0) {
565
+ const previousLine = lines[index - 1];
566
+ if (!BLOCK_LINE_EMPTY.test(previousLine) && !JSDOC_TAG_LINE.test(previousLine)) {
567
+ context.report({ loc, messageId: "blockJSDocBlankLine" });
568
+ }
569
+ }
570
+ if (paragraphStart && block.requireParagraphCapital && !JSDOC_TAG_LINE.test(line)) {
571
+ const firstChar = text[0];
572
+ if (firstChar && isLowerCase(firstChar) && !skipParagraphFirstWord(text, options)) {
573
+ const lineOffset = offsetOfLine(comment, lines, index);
574
+ const charOffset = lineOffset + starSegment.length + spaces.length;
575
+ context.report({
576
+ loc,
577
+ messageId: "blockParagraphCapital",
578
+ fix: (fixer) => fixer.replaceTextRange([charOffset, charOffset + 1], firstChar.toUpperCase())
579
+ });
580
+ }
581
+ }
582
+ paragraphStart = false;
583
+ if (block.enforceParamStyle && /^@param\s/.test(text)) {
584
+ checkParameterLine(context, comment, lines, index, text, starSegment, spaces);
585
+ previousText = void 0;
586
+ } else {
587
+ previousText = text;
588
+ }
589
+ }
590
+ if (previousText !== void 0 && block.requireParagraphCapital) {
591
+ reportParagraphEnding(context, comment, previousText, options);
592
+ }
593
+ }
594
+ var messages = {
595
+ lineLeadingSpace: "Line comment should start with a space.",
596
+ lineLowercase: "Line comment should start with a lowercase letter.",
597
+ lineTrailingPunctuation: "Line comment should not end with punctuation.",
598
+ blockCapital: "Block comment should start with an uppercase letter.",
599
+ blockTrailingPunctuation: "Block comment should end with punctuation.",
600
+ blockSingleLineJSDoc: "JSDoc-style block comments (/** ... */) must span multiple lines.",
601
+ blockOpening: "Block comment should start with '/**\\n *' (or '/*\\n *').",
602
+ blockClosing: "Block comment should end with '\\n */'.",
603
+ blockEmptyLineBeforeClose: "Block comment should not end with an empty line.",
604
+ blockLineSpaceAfterStar: "Each line of a block comment requires a space after '*'.",
605
+ blockParagraphCapital: "Paragraph should start with an uppercase letter.",
606
+ blockParagraphEnding: "Paragraph should end with one of {{endings}}.",
607
+ blockJSDocBlankLine: "Insert a blank line before JSDoc tags.",
608
+ paramMissingDash: "@param should use ' - ' between name and description.",
609
+ paramDescriptionPeriod: "@param description should end with a period."
610
+ };
611
+ var rule3 = {
612
+ meta: {
613
+ type: "suggestion",
614
+ docs: {
615
+ description: "Enforce comment casing, punctuation, and JSDoc/paragraph structure for line, single-line block, and multi-line block comments."
616
+ },
617
+ fixable: "code",
618
+ schema: [
619
+ {
620
+ type: "object",
621
+ additionalProperties: false,
622
+ properties: {
623
+ ignoredWords: {
624
+ type: "array",
625
+ items: { type: "string" },
626
+ uniqueItems: true
627
+ },
628
+ ignoredPatterns: {
629
+ type: "array",
630
+ items: { type: "string" },
631
+ uniqueItems: true
632
+ },
633
+ ignoreUrls: { type: "boolean" },
634
+ ignoreDirectives: { type: "boolean" },
635
+ line: {
636
+ type: "object",
637
+ additionalProperties: false,
638
+ properties: {
639
+ requireLeadingSpace: { type: "boolean" },
640
+ requireLowercase: { type: "boolean" },
641
+ forbidTrailingPunctuation: { type: "boolean" },
642
+ allowedEndings: {
643
+ type: "array",
644
+ items: { type: "string" },
645
+ uniqueItems: true
646
+ }
647
+ }
648
+ },
649
+ block: {
650
+ type: "object",
651
+ additionalProperties: false,
652
+ properties: {
653
+ requireCapital: { type: "boolean" },
654
+ requireTrailingPunctuation: { type: "boolean" },
655
+ enforceOpening: { type: "boolean" },
656
+ enforceClosing: { type: "boolean" },
657
+ requireSpaceAfterStar: { type: "boolean" },
658
+ requireParagraphCapital: { type: "boolean" },
659
+ paragraphEndings: {
660
+ type: "array",
661
+ items: { type: "string" },
662
+ uniqueItems: true
663
+ },
664
+ requireJSDocSpacing: { type: "boolean" },
665
+ enforceParamStyle: { type: "boolean" }
666
+ }
667
+ }
668
+ }
669
+ }
670
+ ],
671
+ messages
672
+ },
673
+ create(context) {
674
+ const sourceCode = context.sourceCode;
675
+ const options = resolveOptions(context.options[0]);
676
+ return {
677
+ Program() {
678
+ const comments = sourceCode.getAllComments();
679
+ for (const comment of comments) {
680
+ if (shouldSkipComment(comment.value, options)) {
681
+ continue;
682
+ }
683
+ if (comment.type === "Line") {
684
+ checkLineComment(context, comment, options);
685
+ continue;
686
+ }
687
+ if (comment.value.includes("\n")) {
688
+ checkMultiLineBlock(context, comment, options);
689
+ } else {
690
+ checkSingleLineBlock(context, comment, options);
691
+ }
692
+ }
693
+ }
694
+ };
695
+ }
696
+ };
697
+ var comment_syntax_default = rule3;
698
+
699
+ // src/rules/newline-around-multiline.ts
700
+ function isAlwaysPadded(node) {
701
+ return node.type === "MethodDefinition" || node.type === "StaticBlock";
702
+ }
703
+ function isImportOrExport(node) {
704
+ if (node.type === "ImportDeclaration" || node.type === "ExportAllDeclaration") {
705
+ return true;
706
+ }
707
+ return node.type === "ExportNamedDeclaration" && !node.declaration;
708
+ }
709
+ var rule4 = {
710
+ meta: {
711
+ type: "layout",
712
+ docs: {
713
+ description: "Require blank lines around multiline statements, class methods, static blocks, and switch case bodies."
714
+ },
715
+ fixable: "whitespace",
716
+ schema: [],
717
+ messages: {
718
+ missingBlankBefore: "Expected blank line before this.",
719
+ missingBlankAfter: "Expected blank line after this."
720
+ }
721
+ },
722
+ create(context) {
723
+ const sourceCode = context.sourceCode;
724
+ function checkBody(body, options = {}) {
725
+ for (let index = 0; index < body.length; index++) {
726
+ const node = body[index];
727
+ if (!node.loc || isImportOrExport(node)) {
728
+ continue;
729
+ }
730
+ const isMultiline = node.loc.start.line !== node.loc.end.line;
731
+ const forceByKind = options.alwaysPadMembers === true && isAlwaysPadded(node);
732
+ const needsPadding = isMultiline || forceByKind;
733
+ if (!needsPadding) {
734
+ continue;
735
+ }
736
+ if (index > 0) {
737
+ const previous = body[index - 1];
738
+ if (previous.loc && !isImportOrExport(previous)) {
739
+ const linesBetween = node.loc.start.line - previous.loc.end.line;
740
+ if (linesBetween < 2) {
741
+ context.report({
742
+ node,
743
+ messageId: "missingBlankBefore",
744
+ fix(fixer) {
745
+ const previousToken = sourceCode.getLastToken(previous);
746
+ if (!previousToken) {
747
+ return null;
748
+ }
749
+ return fixer.insertTextAfter(previousToken, "\n");
750
+ }
751
+ });
752
+ }
753
+ }
754
+ }
755
+ if (index < body.length - 1) {
756
+ const next = body[index + 1];
757
+ if (next.loc && !isImportOrExport(next)) {
758
+ const linesBetween = next.loc.start.line - node.loc.end.line;
759
+ if (linesBetween < 2) {
760
+ context.report({
761
+ node,
762
+ messageId: "missingBlankAfter",
763
+ fix(fixer) {
764
+ const lastToken = sourceCode.getLastToken(node);
765
+ if (!lastToken) {
766
+ return null;
767
+ }
768
+ return fixer.insertTextAfter(lastToken, "\n");
769
+ }
770
+ });
771
+ }
772
+ }
773
+ }
774
+ }
775
+ }
776
+ return {
777
+ Program(node) {
778
+ checkBody(node.body);
779
+ },
780
+ BlockStatement(node) {
781
+ checkBody(node.body);
782
+ },
783
+ StaticBlock(node) {
784
+ checkBody(node.body);
785
+ },
786
+ SwitchCase(node) {
787
+ checkBody(node.consequent);
788
+ },
789
+ ClassBody(node) {
790
+ checkBody(node.body, { alwaysPadMembers: true });
791
+ }
792
+ };
793
+ }
794
+ };
795
+ var newline_around_multiline_default = rule4;
796
+
797
+ // src/rules/no-implicit-object-return.ts
798
+ var rule5 = {
799
+ meta: {
800
+ type: "suggestion",
801
+ docs: {
802
+ description: "Disallow returning an object literal from an arrow function concise body (e.g. `() => ({})`); require a block body with an explicit return."
803
+ },
804
+ fixable: "code",
805
+ schema: [],
806
+ messages: {
807
+ implicitObjectReturn: "Avoid returning an object literal directly from an arrow function; use a block body with an explicit return."
808
+ }
809
+ },
810
+ create(context) {
811
+ const sourceCode = context.sourceCode;
812
+ return {
813
+ ArrowFunctionExpression(node) {
814
+ const body = node.body;
815
+ if (!node.expression || body.type !== "ObjectExpression") {
816
+ return;
817
+ }
818
+ context.report({
819
+ node: body,
820
+ messageId: "implicitObjectReturn",
821
+ fix(fixer) {
822
+ const arrowToken = sourceCode.getTokenBefore(body, (token) => token.value === "=>");
823
+ const openParen = sourceCode.getTokenBefore(body);
824
+ const closeParen = sourceCode.getTokenAfter(body);
825
+ if (!arrowToken || openParen?.value !== "(" || closeParen?.value !== ")") {
826
+ return null;
827
+ }
828
+ const objectText = sourceCode.getText(body);
829
+ return fixer.replaceTextRange([arrowToken.range[1], closeParen.range[1]], ` { return ${objectText}; }`);
830
+ }
831
+ });
832
+ }
833
+ };
834
+ }
835
+ };
836
+ var no_implicit_object_return_default = rule5;
837
+
838
+ // src/rules/index.ts
839
+ var customRules = {
840
+ meta: {
841
+ name: "@d3lm/lint-preset-rules",
842
+ version: "0.1.0"
843
+ },
844
+ rules: {
845
+ "newline-around-multiline": newline_around_multiline_default,
846
+ "block-scoped-case": block_scoped_case_default,
847
+ "comment-syntax": comment_syntax_default,
848
+ "comment-preceding-blank-line": comment_preceding_blank_line_default,
849
+ "no-implicit-object-return": no_implicit_object_return_default
850
+ }
851
+ };
852
+ var rules_default = customRules;
853
+ export {
854
+ customRules,
855
+ rules_default as default
856
+ };
857
+ //# sourceMappingURL=rules.js.map