@tony.ganchev/eslint-plugin-header 3.2.5 → 3.2.6
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/lib/rules/eslint-utils.js +1 -1
- package/lib/rules/header.js +384 -490
- package/package.json +9 -8
- package/types/lib/rules/header.d.ts.map +1 -1
|
@@ -36,7 +36,7 @@ module.exports = {
|
|
|
36
36
|
* @param {RuleContext} context ESLint execution context.
|
|
37
37
|
* @returns {SourceCode} The source-code object.
|
|
38
38
|
*/
|
|
39
|
-
contextSourceCode: function(context) {
|
|
39
|
+
contextSourceCode: function (context) {
|
|
40
40
|
return context.sourceCode
|
|
41
41
|
|| /** @type {RuleContext & { getSourceCode: () => SourceCode }} */(context).getSourceCode();
|
|
42
42
|
}
|
package/lib/rules/header.js
CHANGED
|
@@ -33,8 +33,9 @@ const { description, recommended } = require("./header.docs");
|
|
|
33
33
|
const { lineEndingOptions, commentTypeOptions, schema } = require("./header.schema");
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
* @import { Linter, Rule } from "eslint"
|
|
37
|
-
* @import { Comment, SourceLocation
|
|
36
|
+
* @import { JSSyntaxElement, Linter, Rule, SourceCode } from "eslint"
|
|
37
|
+
* @import { Comment, SourceLocation } from "estree"
|
|
38
|
+
* @import { ViolationReport } from "@eslint/core";
|
|
38
39
|
* @typedef {Rule.NodeListener} NodeListener
|
|
39
40
|
* @typedef {Rule.ReportFixer} ReportFixer
|
|
40
41
|
* @typedef {Rule.RuleFixer} RuleFixer
|
|
@@ -175,53 +176,28 @@ function match(actual, expected) {
|
|
|
175
176
|
*/
|
|
176
177
|
function excludeShebangs(comments) {
|
|
177
178
|
/** @type {Comment[]} */
|
|
178
|
-
return comments.filter(function(comment) {
|
|
179
|
+
return comments.filter(function (comment) {
|
|
179
180
|
return comment.type !== "Shebang";
|
|
180
181
|
});
|
|
181
182
|
}
|
|
182
183
|
|
|
183
|
-
/**
|
|
184
|
-
* TypeScript helper to confirm defined type.
|
|
185
|
-
* @template T Target type to validate for definiteness.
|
|
186
|
-
* @param {T | undefined} val The value to validate.
|
|
187
|
-
* @returns {asserts val is T} Validates defined type.
|
|
188
|
-
*/
|
|
189
|
-
function assertDefined(val) {
|
|
190
|
-
assert.strict.notEqual(typeof val, "undefined");
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* TypeScript helper to confirm non-null type.
|
|
195
|
-
* @template T Target type to validate is non-null.
|
|
196
|
-
* @param {T | null} val The value to validate.
|
|
197
|
-
* @returns {asserts val is T} Validates non-null type.
|
|
198
|
-
*/
|
|
199
|
-
function assertNotNull(val) {
|
|
200
|
-
assert.strict.notEqual(val, null);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
184
|
/**
|
|
204
185
|
* Returns either the first block comment or the first set of line comments that
|
|
205
186
|
* are ONLY separated by a single newline. Note that this does not actually
|
|
206
187
|
* check if they are at the start of the file since that is already checked by
|
|
207
188
|
* `hasHeader()`.
|
|
208
|
-
* @param {
|
|
189
|
+
* @param {SourceCode} sourceCode AST.
|
|
209
190
|
* @returns {Comment[]} Lines That constitute the leading comment.
|
|
210
191
|
*/
|
|
211
|
-
function getLeadingComments(
|
|
212
|
-
const sourceCode = contextSourceCode(context);
|
|
192
|
+
function getLeadingComments(sourceCode) {
|
|
213
193
|
const all = excludeShebangs(sourceCode.getAllComments());
|
|
214
|
-
assert.ok(all);
|
|
215
|
-
assert.ok(all.length);
|
|
216
194
|
if (all[0].type.toLowerCase() === commentTypeOptions.block) {
|
|
217
195
|
return [all[0]];
|
|
218
196
|
}
|
|
219
197
|
let i = 1;
|
|
220
198
|
for (; i < all.length; ++i) {
|
|
221
|
-
const previousRange = all[i - 1].range;
|
|
222
|
-
|
|
223
|
-
const currentRange = all[i].range;
|
|
224
|
-
assertDefined(currentRange);
|
|
199
|
+
const previousRange = /** @type {[number, number]} */ (all[i - 1].range);
|
|
200
|
+
const currentRange = /** @type {[number, number]} */ (all[i].range);
|
|
225
201
|
const txt = sourceCode.text.slice(previousRange[1], currentRange[0]);
|
|
226
202
|
if (!txt.match(/^(\r\n|\r|\n)$/)) {
|
|
227
203
|
break;
|
|
@@ -257,14 +233,10 @@ function genCommentBody(commentType, textArray, eol) {
|
|
|
257
233
|
function genCommentsRange(comments) {
|
|
258
234
|
assert.ok(comments.length);
|
|
259
235
|
const firstComment = comments[0];
|
|
260
|
-
|
|
261
|
-
const firstCommentRange = firstComment.range;
|
|
262
|
-
assertDefined(firstCommentRange);
|
|
236
|
+
const firstCommentRange = /** @type {[number, number]} */ (firstComment.range);
|
|
263
237
|
const start = firstCommentRange[0];
|
|
264
238
|
const lastComment = comments.slice(-1)[0];
|
|
265
|
-
|
|
266
|
-
const lastCommentRange = lastComment.range;
|
|
267
|
-
assertDefined(lastCommentRange);
|
|
239
|
+
const lastCommentRange = /** @type {[number, number]} */ (lastComment.range);
|
|
268
240
|
const end = lastCommentRange[1];
|
|
269
241
|
return [start, end];
|
|
270
242
|
}
|
|
@@ -292,17 +264,16 @@ function leadingEmptyLines(src) {
|
|
|
292
264
|
/**
|
|
293
265
|
* Factory for fixer that adds a missing header.
|
|
294
266
|
* @param {CommentType} commentType Type of comment to use.
|
|
295
|
-
* @param {
|
|
267
|
+
* @param {SourceCode} sourceCode AST.
|
|
296
268
|
* @param {string[]} headerLines Lines of the header comment.
|
|
297
269
|
* @param {LineEnding} eol End-of-line characters.
|
|
298
270
|
* @param {number} numNewlines Number of trailing lines after the comment.
|
|
299
271
|
* @returns {ReportFixer} The fix to apply.
|
|
300
272
|
*/
|
|
301
|
-
function genPrependFixer(commentType,
|
|
302
|
-
return function(fixer) {
|
|
273
|
+
function genPrependFixer(commentType, sourceCode, headerLines, eol, numNewlines) {
|
|
274
|
+
return function (fixer) {
|
|
303
275
|
let insertPos = 0;
|
|
304
276
|
let newHeader = genCommentBody(commentType, headerLines, eol);
|
|
305
|
-
const sourceCode = contextSourceCode(context);
|
|
306
277
|
if (sourceCode.text.startsWith("#!")) {
|
|
307
278
|
const firstNewLinePos = sourceCode.text.indexOf("\n");
|
|
308
279
|
insertPos = firstNewLinePos === -1 ? sourceCode.text.length : firstNewLinePos + 1;
|
|
@@ -323,17 +294,17 @@ function genPrependFixer(commentType, context, headerLines, eol, numNewlines) {
|
|
|
323
294
|
/**
|
|
324
295
|
* Factory for fixer that replaces an incorrect header.
|
|
325
296
|
* @param {CommentType} commentType Type of comment to use.
|
|
326
|
-
* @param {
|
|
297
|
+
* @param {SourceCode} sourceCode AST.
|
|
327
298
|
* @param {Comment[]} leadingComments Comment elements to replace.
|
|
328
299
|
* @param {string[]} headerLines Lines of the header comment.
|
|
329
300
|
* @param {LineEnding} eol End-of-line characters.
|
|
330
301
|
* @param {number} numNewlines Number of trailing lines after the comment.
|
|
331
302
|
* @returns {ReportFixer} The fix to apply.
|
|
332
303
|
*/
|
|
333
|
-
function genReplaceFixer(commentType,
|
|
334
|
-
return function(fixer) {
|
|
304
|
+
function genReplaceFixer(commentType, sourceCode, leadingComments, headerLines, eol, numNewlines) {
|
|
305
|
+
return function (fixer) {
|
|
335
306
|
const commentRange = genCommentsRange(leadingComments);
|
|
336
|
-
const emptyLines = leadingEmptyLines(
|
|
307
|
+
const emptyLines = leadingEmptyLines(sourceCode.text.substring(commentRange[1]));
|
|
337
308
|
const missingNewlines = Math.max(0, numNewlines - emptyLines);
|
|
338
309
|
const eols = eol.repeat(missingNewlines);
|
|
339
310
|
return fixer.replaceTextRange(
|
|
@@ -352,7 +323,7 @@ function genReplaceFixer(commentType, context, leadingComments, headerLines, eol
|
|
|
352
323
|
* @returns {ReportFixer} The fix to apply.
|
|
353
324
|
*/
|
|
354
325
|
function genEmptyLinesFixer(leadingComments, eol, missingEmptyLinesCount) {
|
|
355
|
-
return function(fixer) {
|
|
326
|
+
return function (fixer) {
|
|
356
327
|
return fixer.insertTextAfterRange(
|
|
357
328
|
genCommentsRange(leadingComments),
|
|
358
329
|
eol.repeat(missingEmptyLinesCount)
|
|
@@ -487,31 +458,19 @@ function isFileBasedHeaderConfig(config) {
|
|
|
487
458
|
}
|
|
488
459
|
|
|
489
460
|
/**
|
|
490
|
-
*
|
|
491
|
-
*
|
|
492
|
-
* @
|
|
493
|
-
*
|
|
461
|
+
* Transforms file template-based matching rules to inline rules for further
|
|
462
|
+
* use.
|
|
463
|
+
* @param {FileBasedConfig | InlineConfig} matcher The matching rule.
|
|
464
|
+
* @returns {InlineConfig} The resulting normalized configuration.
|
|
494
465
|
*/
|
|
495
|
-
function
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
/**
|
|
500
|
-
* Transforms a set of new-style options adding defaults and standardizing on
|
|
501
|
-
* one of multiple config styles.
|
|
502
|
-
* @param {HeaderOptions} originalOptions New-style options to normalize.
|
|
503
|
-
* @returns {HeaderOptions} Normalized options.
|
|
504
|
-
*/
|
|
505
|
-
function normalizeOptions(originalOptions) {
|
|
506
|
-
const options = structuredClone(originalOptions);
|
|
507
|
-
|
|
508
|
-
if (isFileBasedHeaderConfig(originalOptions.header)) {
|
|
509
|
-
const text = fs.readFileSync(originalOptions.header.file, originalOptions.header.encoding || "utf8");
|
|
466
|
+
function normalizeMatchingRules(matcher) {
|
|
467
|
+
if (isFileBasedHeaderConfig(matcher)) {
|
|
468
|
+
const text = fs.readFileSync(matcher.file, matcher.encoding || "utf8");
|
|
510
469
|
const [commentType, lines] = commentParser(text);
|
|
511
|
-
|
|
470
|
+
return { commentType, lines };
|
|
512
471
|
}
|
|
513
|
-
|
|
514
|
-
|
|
472
|
+
const commentType = matcher.commentType;
|
|
473
|
+
const lines = matcher.lines.flatMap(
|
|
515
474
|
(line) => {
|
|
516
475
|
if (typeof line === "string") {
|
|
517
476
|
return /** @type {HeaderLine[]} */(line.split(/\r?\n/));
|
|
@@ -529,6 +488,19 @@ function normalizeOptions(originalOptions) {
|
|
|
529
488
|
}
|
|
530
489
|
return [{ pattern }];
|
|
531
490
|
});
|
|
491
|
+
return { commentType, lines };
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Transforms a set of new-style options adding defaults and standardizing on
|
|
496
|
+
* one of multiple config styles.
|
|
497
|
+
* @param {HeaderOptions} originalOptions New-style options to normalize.
|
|
498
|
+
* @returns {HeaderOptions} Normalized options.
|
|
499
|
+
*/
|
|
500
|
+
function normalizeOptions(originalOptions) {
|
|
501
|
+
const options = structuredClone(originalOptions);
|
|
502
|
+
|
|
503
|
+
options.header = normalizeMatchingRules(originalOptions.header);
|
|
532
504
|
|
|
533
505
|
if (!options.lineEndings) {
|
|
534
506
|
options.lineEndings = "os";
|
|
@@ -558,9 +530,7 @@ function normalizeOptions(originalOptions) {
|
|
|
558
530
|
*/
|
|
559
531
|
function missingEmptyLinesViolationLoc(leadingComments, actualEmptyLines) {
|
|
560
532
|
assert.ok(leadingComments);
|
|
561
|
-
const loc = leadingComments[leadingComments.length - 1].loc;
|
|
562
|
-
assertDefined(loc);
|
|
563
|
-
assertNotNull(loc);
|
|
533
|
+
const loc = /** @type {SourceLocation} */ (leadingComments[leadingComments.length - 1].loc);
|
|
564
534
|
const lastCommentLineLocEnd = loc.end;
|
|
565
535
|
return {
|
|
566
536
|
start: lastCommentLineLocEnd,
|
|
@@ -571,6 +541,314 @@ function missingEmptyLinesViolationLoc(leadingComments, actualEmptyLines) {
|
|
|
571
541
|
};
|
|
572
542
|
}
|
|
573
543
|
|
|
544
|
+
/**
|
|
545
|
+
* Matches comments against of header content-matching rules. An object performs
|
|
546
|
+
* a number of expensive operations only once and thus can be used multiple
|
|
547
|
+
* times to test different comments.
|
|
548
|
+
*/
|
|
549
|
+
class CommentMatcher {
|
|
550
|
+
/**
|
|
551
|
+
* Initializes the matcher for a specific comment-matching rules.
|
|
552
|
+
* @param {InlineConfig} headerConfig Content-matching rules.
|
|
553
|
+
* @param {string} eol The EOL characters used.
|
|
554
|
+
* @param {number} numLines The requirred minimum number of trailing empty
|
|
555
|
+
* lines.
|
|
556
|
+
*/
|
|
557
|
+
constructor(headerConfig, eol, numLines) {
|
|
558
|
+
this.commentType = headerConfig.commentType;
|
|
559
|
+
this.headerLines = headerConfig.lines.map((line) => isPattern(line) ? line.pattern : line);
|
|
560
|
+
this.eol = eol;
|
|
561
|
+
this.numLines = numLines;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Performs a validation of a comment against a header matching
|
|
566
|
+
* configuration.
|
|
567
|
+
* @param {Comment[]} leadingComments The block comment or sequence of line
|
|
568
|
+
* comments to test.
|
|
569
|
+
* @param {SourceCode} sourceCode The source code AST.
|
|
570
|
+
* @returns {ViolationReport<JSSyntaxElement, string> | null} If set a
|
|
571
|
+
* violation report to pass back to ESLint or interpret as necessary.
|
|
572
|
+
*/
|
|
573
|
+
validate(leadingComments, sourceCode) {
|
|
574
|
+
|
|
575
|
+
const firstLeadingCommentLoc = /** @type {SourceLocation} */ (leadingComments[0].loc);
|
|
576
|
+
const firstLeadingCommentRange = /** @type {[number, number]} */ (leadingComments[0].range);
|
|
577
|
+
|
|
578
|
+
const lastLeadingCommentLoc = /** @type {SourceLocation} */ (leadingComments[leadingComments.length - 1].loc);
|
|
579
|
+
|
|
580
|
+
if (leadingComments[0].type.toLowerCase() !== this.commentType) {
|
|
581
|
+
return {
|
|
582
|
+
loc: {
|
|
583
|
+
start: firstLeadingCommentLoc.start,
|
|
584
|
+
end: lastLeadingCommentLoc.end
|
|
585
|
+
},
|
|
586
|
+
messageId: "incorrectCommentType",
|
|
587
|
+
data: {
|
|
588
|
+
commentType: this.commentType
|
|
589
|
+
},
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
if (this.commentType === commentTypeOptions.line) {
|
|
593
|
+
if (this.headerLines.length === 1) {
|
|
594
|
+
const leadingCommentValues = leadingComments.map((c) => c.value);
|
|
595
|
+
if (
|
|
596
|
+
!match(leadingCommentValues.join("\n"), this.headerLines[0])
|
|
597
|
+
&& !match(leadingCommentValues.join("\r\n"), this.headerLines[0])
|
|
598
|
+
) {
|
|
599
|
+
return {
|
|
600
|
+
loc: {
|
|
601
|
+
start: firstLeadingCommentLoc.start,
|
|
602
|
+
end: lastLeadingCommentLoc.end
|
|
603
|
+
},
|
|
604
|
+
messageId: "incorrectHeader",
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
} else {
|
|
608
|
+
for (let i = 0; i < this.headerLines.length; i++) {
|
|
609
|
+
if (leadingComments.length - 1 < i) {
|
|
610
|
+
return {
|
|
611
|
+
loc: {
|
|
612
|
+
start: lastLeadingCommentLoc.end,
|
|
613
|
+
end: lastLeadingCommentLoc.end
|
|
614
|
+
},
|
|
615
|
+
messageId: "headerTooShort",
|
|
616
|
+
data: {
|
|
617
|
+
remainder: this.headerLines.slice(i).join(this.eol)
|
|
618
|
+
},
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
const headerLine = this.headerLines[i];
|
|
622
|
+
const comment = leadingComments[i];
|
|
623
|
+
const commentLoc = /** @type {SourceLocation} */ (comment.loc);
|
|
624
|
+
if (typeof headerLine === "string") {
|
|
625
|
+
const leadingCommentLength = comment.value.length;
|
|
626
|
+
const headerLineLength = headerLine.length;
|
|
627
|
+
for (let j = 0; j < Math.min(leadingCommentLength, headerLineLength); j++) {
|
|
628
|
+
if (comment.value[j] !== headerLine[j]) {
|
|
629
|
+
return {
|
|
630
|
+
loc: {
|
|
631
|
+
start: {
|
|
632
|
+
column: "//".length + j,
|
|
633
|
+
line: commentLoc.start.line
|
|
634
|
+
},
|
|
635
|
+
end: commentLoc.end
|
|
636
|
+
},
|
|
637
|
+
messageId: "headerLineMismatchAtPos",
|
|
638
|
+
data: {
|
|
639
|
+
expected: headerLine.substring(j)
|
|
640
|
+
},
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
if (leadingCommentLength < headerLineLength) {
|
|
645
|
+
return {
|
|
646
|
+
loc: {
|
|
647
|
+
start: commentLoc.end,
|
|
648
|
+
end: commentLoc.end,
|
|
649
|
+
},
|
|
650
|
+
messageId: "headerLineTooShort",
|
|
651
|
+
data: {
|
|
652
|
+
remainder: headerLine.substring(leadingCommentLength)
|
|
653
|
+
},
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
if (leadingCommentLength > headerLineLength) {
|
|
657
|
+
return {
|
|
658
|
+
loc: {
|
|
659
|
+
start: {
|
|
660
|
+
column: "//".length + headerLineLength,
|
|
661
|
+
line: commentLoc.start.line
|
|
662
|
+
},
|
|
663
|
+
end: commentLoc.end,
|
|
664
|
+
},
|
|
665
|
+
messageId: "headerLineTooLong",
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
} else {
|
|
669
|
+
if (!match(comment.value, headerLine)) {
|
|
670
|
+
return {
|
|
671
|
+
loc: {
|
|
672
|
+
start: {
|
|
673
|
+
column: "//".length,
|
|
674
|
+
line: commentLoc.start.line,
|
|
675
|
+
},
|
|
676
|
+
end: commentLoc.end,
|
|
677
|
+
},
|
|
678
|
+
messageId: "incorrectHeaderLine",
|
|
679
|
+
data: {
|
|
680
|
+
pattern: headerLine.toString()
|
|
681
|
+
},
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
const commentRange = /** @type {[number, number]} */ (leadingComments[this.headerLines.length - 1].range);
|
|
689
|
+
const actualLeadingEmptyLines = leadingEmptyLines(sourceCode.text.substring(commentRange[1]));
|
|
690
|
+
const missingEmptyLines = this.numLines - actualLeadingEmptyLines;
|
|
691
|
+
if (missingEmptyLines > 0) {
|
|
692
|
+
return {
|
|
693
|
+
loc: missingEmptyLinesViolationLoc(leadingComments, actualLeadingEmptyLines),
|
|
694
|
+
messageId: "noNewlineAfterHeader",
|
|
695
|
+
data: {
|
|
696
|
+
expected: this.numLines,
|
|
697
|
+
actual: actualLeadingEmptyLines
|
|
698
|
+
},
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
return null;
|
|
703
|
+
}
|
|
704
|
+
// if block comment pattern has more than 1 line, we also split the
|
|
705
|
+
// comment
|
|
706
|
+
let leadingLines = [leadingComments[0].value];
|
|
707
|
+
if (this.headerLines.length > 1) {
|
|
708
|
+
leadingLines = leadingComments[0].value.split(/\r?\n/);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/** @type {null | string} */
|
|
712
|
+
let errorMessageId = null;
|
|
713
|
+
/** @type {undefined | Record<string, string>} */
|
|
714
|
+
let errorMessageData;
|
|
715
|
+
/** @type {null | SourceLocation} */
|
|
716
|
+
let errorMessageLoc = null;
|
|
717
|
+
for (let i = 0; i < this.headerLines.length; i++) {
|
|
718
|
+
if (leadingLines.length - 1 < i) {
|
|
719
|
+
return {
|
|
720
|
+
loc: {
|
|
721
|
+
start: lastLeadingCommentLoc.end,
|
|
722
|
+
end: lastLeadingCommentLoc.end
|
|
723
|
+
},
|
|
724
|
+
messageId: "headerTooShort",
|
|
725
|
+
data: {
|
|
726
|
+
remainder: this.headerLines.slice(i).join(this.eol)
|
|
727
|
+
},
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
const leadingLine = leadingLines[i];
|
|
731
|
+
const headerLine = this.headerLines[i];
|
|
732
|
+
if (typeof headerLine === "string") {
|
|
733
|
+
for (let j = 0; j < Math.min(leadingLine.length, headerLine.length); j++) {
|
|
734
|
+
if (leadingLine[j] !== headerLine[j]) {
|
|
735
|
+
errorMessageId = "headerLineMismatchAtPos";
|
|
736
|
+
const columnOffset = i === 0 ? "/*".length : 0;
|
|
737
|
+
const line = firstLeadingCommentLoc.start.line + i;
|
|
738
|
+
errorMessageLoc = {
|
|
739
|
+
start: {
|
|
740
|
+
column: columnOffset + j,
|
|
741
|
+
line
|
|
742
|
+
},
|
|
743
|
+
end: {
|
|
744
|
+
column: columnOffset + leadingLine.length,
|
|
745
|
+
line
|
|
746
|
+
}
|
|
747
|
+
};
|
|
748
|
+
errorMessageData = {
|
|
749
|
+
expected: headerLine.substring(j)
|
|
750
|
+
};
|
|
751
|
+
break;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
if (errorMessageId) {
|
|
755
|
+
break;
|
|
756
|
+
}
|
|
757
|
+
if (leadingLine.length < headerLine.length) {
|
|
758
|
+
errorMessageId = "headerLineTooShort";
|
|
759
|
+
const startColumn = (i === 0 ? "/*".length : 0) + leadingLine.length;
|
|
760
|
+
errorMessageLoc = {
|
|
761
|
+
start: {
|
|
762
|
+
column: startColumn,
|
|
763
|
+
line: firstLeadingCommentLoc.start.line + i
|
|
764
|
+
},
|
|
765
|
+
end: {
|
|
766
|
+
column: startColumn + 1,
|
|
767
|
+
line: firstLeadingCommentLoc.start.line + i
|
|
768
|
+
}
|
|
769
|
+
};
|
|
770
|
+
errorMessageData = {
|
|
771
|
+
remainder: headerLine.substring(leadingLine.length)
|
|
772
|
+
};
|
|
773
|
+
break;
|
|
774
|
+
}
|
|
775
|
+
if (leadingLine.length > headerLine.length) {
|
|
776
|
+
errorMessageId = "headerLineTooLong";
|
|
777
|
+
errorMessageLoc = {
|
|
778
|
+
start: {
|
|
779
|
+
column: (i === 0 ? "/*".length : 0) + headerLine.length,
|
|
780
|
+
line: firstLeadingCommentLoc.start.line + i
|
|
781
|
+
},
|
|
782
|
+
end: {
|
|
783
|
+
column: (i === 0 ? "/*".length : 0) + leadingLine.length,
|
|
784
|
+
line: firstLeadingCommentLoc.start.line + i
|
|
785
|
+
}
|
|
786
|
+
};
|
|
787
|
+
break;
|
|
788
|
+
}
|
|
789
|
+
} else {
|
|
790
|
+
if (!match(leadingLine, headerLine)) {
|
|
791
|
+
errorMessageId = "incorrectHeaderLine";
|
|
792
|
+
errorMessageData = {
|
|
793
|
+
pattern: headerLine.toString()
|
|
794
|
+
};
|
|
795
|
+
const columnOffset = i === 0 ? "/*".length : 0;
|
|
796
|
+
errorMessageLoc = {
|
|
797
|
+
start: {
|
|
798
|
+
column: columnOffset + 0,
|
|
799
|
+
line: firstLeadingCommentLoc.start.line + i
|
|
800
|
+
},
|
|
801
|
+
end: {
|
|
802
|
+
column: columnOffset + leadingLine.length,
|
|
803
|
+
line: firstLeadingCommentLoc.start.line + i
|
|
804
|
+
}
|
|
805
|
+
};
|
|
806
|
+
break;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
if (!errorMessageId && leadingLines.length > this.headerLines.length) {
|
|
812
|
+
errorMessageId = "headerTooLong";
|
|
813
|
+
errorMessageLoc = {
|
|
814
|
+
start: {
|
|
815
|
+
column: (this.headerLines.length === 0 ? "/*".length : 0) + 0,
|
|
816
|
+
line: firstLeadingCommentLoc.start.line + this.headerLines.length
|
|
817
|
+
},
|
|
818
|
+
end: {
|
|
819
|
+
column: lastLeadingCommentLoc.end.column - "*/".length,
|
|
820
|
+
line: lastLeadingCommentLoc.end.line
|
|
821
|
+
}
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
if (errorMessageId) {
|
|
826
|
+
return {
|
|
827
|
+
loc: /** @type {SourceLocation} */ (errorMessageLoc),
|
|
828
|
+
messageId: errorMessageId,
|
|
829
|
+
data: errorMessageData,
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
const actualLeadingEmptyLines =
|
|
834
|
+
leadingEmptyLines(sourceCode.text.substring(firstLeadingCommentRange[1]));
|
|
835
|
+
const missingEmptyLines = this.numLines - actualLeadingEmptyLines;
|
|
836
|
+
if (missingEmptyLines > 0) {
|
|
837
|
+
return {
|
|
838
|
+
loc: missingEmptyLinesViolationLoc(leadingComments, actualLeadingEmptyLines),
|
|
839
|
+
messageId: "noNewlineAfterHeader",
|
|
840
|
+
data: {
|
|
841
|
+
expected: this.numLines,
|
|
842
|
+
actual: actualLeadingEmptyLines
|
|
843
|
+
},
|
|
844
|
+
};
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
return null;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
|
|
574
852
|
/** @type {Rule.RuleModule} */
|
|
575
853
|
const headerRule = {
|
|
576
854
|
meta: {
|
|
@@ -609,50 +887,37 @@ const headerRule = {
|
|
|
609
887
|
* @param {RuleContext} context ESLint rule execution context.
|
|
610
888
|
* @returns {NodeListener} The rule definition.
|
|
611
889
|
*/
|
|
612
|
-
create: function(context) {
|
|
890
|
+
create: function (context) {
|
|
613
891
|
|
|
614
|
-
const newStyleOptions = transformLegacyOptions(/** @type {AllHeaderOptions} */
|
|
892
|
+
const newStyleOptions = transformLegacyOptions(/** @type {AllHeaderOptions} */(context.options));
|
|
615
893
|
const options = normalizeOptions(newStyleOptions);
|
|
616
894
|
|
|
617
|
-
assertLineBasedHeaderConfig(options.header);
|
|
618
|
-
const commentType = /** @type {CommentType} */ (options.header.commentType);
|
|
619
|
-
|
|
620
895
|
const eol = getEol(
|
|
621
|
-
/** @type {LineEndingOption} */
|
|
896
|
+
/** @type {LineEndingOption} */(options.lineEndings)
|
|
622
897
|
);
|
|
623
898
|
|
|
624
|
-
/** @type {
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
let canFix = true;
|
|
630
|
-
const headerLines = options.header.lines.map(function(line) {
|
|
631
|
-
// Can only fix regex option if a template is also provided
|
|
899
|
+
const header = /** @type {InlineConfig} */ (options.header);
|
|
900
|
+
|
|
901
|
+
const canFix = !header.lines.some((line) => isPattern(line) && !("template" in line));
|
|
902
|
+
|
|
903
|
+
const fixLines = header.lines.map((line) => {
|
|
632
904
|
if (isPattern(line)) {
|
|
633
|
-
|
|
634
|
-
fixLines.push(/** @type {string} */ (line.template));
|
|
635
|
-
} else {
|
|
636
|
-
canFix = false;
|
|
637
|
-
fixLines.push("");
|
|
638
|
-
}
|
|
639
|
-
return line.pattern;
|
|
640
|
-
} else {
|
|
641
|
-
fixLines.push(/** @type {string} */ (line));
|
|
642
|
-
return line;
|
|
905
|
+
return ("template" in line) ? /** @type {string} */(line.template) : "";
|
|
643
906
|
}
|
|
907
|
+
return /** @type {string} */(line);
|
|
644
908
|
});
|
|
645
909
|
|
|
646
|
-
|
|
647
910
|
const numLines = /** @type {number} */ (options.trailingEmptyLines?.minimum);
|
|
648
911
|
|
|
912
|
+
const headerMatcher = new CommentMatcher(header, eol, numLines);
|
|
913
|
+
|
|
649
914
|
return {
|
|
650
915
|
/**
|
|
651
916
|
* Hooks into the processing of the overall script node to do the
|
|
652
917
|
* header validation.
|
|
653
918
|
* @returns {void}
|
|
654
919
|
*/
|
|
655
|
-
Program: function() {
|
|
920
|
+
Program: function () {
|
|
656
921
|
const sourceCode = contextSourceCode(context);
|
|
657
922
|
if (!hasHeader(sourceCode.text)) {
|
|
658
923
|
const hasShebang = sourceCode.text.startsWith("#!");
|
|
@@ -669,41 +934,10 @@ const headerRule = {
|
|
|
669
934
|
}
|
|
670
935
|
},
|
|
671
936
|
messageId: "missingHeader",
|
|
672
|
-
fix: genPrependFixer(
|
|
673
|
-
commentType,
|
|
674
|
-
context,
|
|
675
|
-
fixLines,
|
|
676
|
-
eol,
|
|
677
|
-
numLines)
|
|
678
|
-
});
|
|
679
|
-
return;
|
|
680
|
-
}
|
|
681
|
-
const leadingComments = getLeadingComments(context);
|
|
682
|
-
const firstLeadingCommentLoc = leadingComments[0].loc;
|
|
683
|
-
const firstLeadingCommentRange = leadingComments[0].range;
|
|
684
|
-
assertDefined(firstLeadingCommentRange);
|
|
685
|
-
|
|
686
|
-
const lastLeadingCommentLoc = leadingComments[leadingComments.length - 1].loc;
|
|
687
|
-
|
|
688
|
-
if (leadingComments[0].type.toLowerCase() !== commentType) {
|
|
689
|
-
assertDefined(firstLeadingCommentLoc);
|
|
690
|
-
assertNotNull(firstLeadingCommentLoc);
|
|
691
|
-
assertDefined(lastLeadingCommentLoc);
|
|
692
|
-
assertNotNull(lastLeadingCommentLoc);
|
|
693
|
-
context.report({
|
|
694
|
-
loc: {
|
|
695
|
-
start: firstLeadingCommentLoc.start,
|
|
696
|
-
end: lastLeadingCommentLoc.end
|
|
697
|
-
},
|
|
698
|
-
messageId: "incorrectCommentType",
|
|
699
|
-
data: {
|
|
700
|
-
commentType: commentType
|
|
701
|
-
},
|
|
702
937
|
fix: canFix
|
|
703
|
-
?
|
|
704
|
-
commentType,
|
|
705
|
-
|
|
706
|
-
leadingComments,
|
|
938
|
+
? genPrependFixer(
|
|
939
|
+
headerMatcher.commentType,
|
|
940
|
+
sourceCode,
|
|
707
941
|
fixLines,
|
|
708
942
|
eol,
|
|
709
943
|
numLines)
|
|
@@ -711,365 +945,25 @@ const headerRule = {
|
|
|
711
945
|
});
|
|
712
946
|
return;
|
|
713
947
|
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
fix: canFix
|
|
732
|
-
? genReplaceFixer(
|
|
733
|
-
commentType,
|
|
734
|
-
context,
|
|
735
|
-
leadingComments,
|
|
736
|
-
fixLines,
|
|
737
|
-
eol,
|
|
738
|
-
numLines)
|
|
739
|
-
: null
|
|
740
|
-
});
|
|
741
|
-
return;
|
|
742
|
-
}
|
|
743
|
-
} else {
|
|
744
|
-
for (let i = 0; i < headerLines.length; i++) {
|
|
745
|
-
if (leadingComments.length - 1 < i) {
|
|
746
|
-
assertDefined(lastLeadingCommentLoc);
|
|
747
|
-
assertNotNull(lastLeadingCommentLoc);
|
|
748
|
-
context.report({
|
|
749
|
-
loc: {
|
|
750
|
-
start: lastLeadingCommentLoc.end,
|
|
751
|
-
end: lastLeadingCommentLoc.end
|
|
752
|
-
},
|
|
753
|
-
messageId: "headerTooShort",
|
|
754
|
-
data: {
|
|
755
|
-
remainder: headerLines.slice(i).join(eol)
|
|
756
|
-
},
|
|
757
|
-
fix: canFix
|
|
758
|
-
? genReplaceFixer(
|
|
759
|
-
commentType,
|
|
760
|
-
context,
|
|
761
|
-
leadingComments,
|
|
762
|
-
fixLines,
|
|
763
|
-
eol,
|
|
764
|
-
numLines)
|
|
765
|
-
: null
|
|
766
|
-
});
|
|
767
|
-
return;
|
|
768
|
-
}
|
|
769
|
-
const headerLine = headerLines[i];
|
|
770
|
-
const comment = leadingComments[i];
|
|
771
|
-
const commentLoc = comment.loc;
|
|
772
|
-
assertDefined(commentLoc);
|
|
773
|
-
assertNotNull(commentLoc);
|
|
774
|
-
if (typeof headerLine === "string") {
|
|
775
|
-
const leadingCommentLength = comment.value.length;
|
|
776
|
-
const headerLineLength = headerLine.length;
|
|
777
|
-
for (let j = 0; j < Math.min(leadingCommentLength, headerLineLength); j++) {
|
|
778
|
-
if (comment.value[j] !== headerLine[j]) {
|
|
779
|
-
context.report({
|
|
780
|
-
loc: {
|
|
781
|
-
start: {
|
|
782
|
-
column: "//".length + j,
|
|
783
|
-
line: commentLoc.start.line
|
|
784
|
-
},
|
|
785
|
-
end: commentLoc.end
|
|
786
|
-
},
|
|
787
|
-
messageId: "headerLineMismatchAtPos",
|
|
788
|
-
data: {
|
|
789
|
-
expected: headerLine.substring(j)
|
|
790
|
-
},
|
|
791
|
-
fix: genReplaceFixer(
|
|
792
|
-
commentType,
|
|
793
|
-
context,
|
|
794
|
-
leadingComments,
|
|
795
|
-
fixLines,
|
|
796
|
-
eol,
|
|
797
|
-
numLines)
|
|
798
|
-
});
|
|
799
|
-
return;
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
if (leadingCommentLength < headerLineLength) {
|
|
803
|
-
context.report({
|
|
804
|
-
loc: {
|
|
805
|
-
start: commentLoc.end,
|
|
806
|
-
end: commentLoc.end,
|
|
807
|
-
},
|
|
808
|
-
messageId: "headerLineTooShort",
|
|
809
|
-
data: {
|
|
810
|
-
remainder: headerLine.substring(leadingCommentLength)
|
|
811
|
-
},
|
|
812
|
-
fix: canFix
|
|
813
|
-
? genReplaceFixer(
|
|
814
|
-
commentType,
|
|
815
|
-
context,
|
|
816
|
-
leadingComments,
|
|
817
|
-
fixLines,
|
|
818
|
-
eol,
|
|
819
|
-
numLines)
|
|
820
|
-
: null
|
|
821
|
-
});
|
|
822
|
-
return;
|
|
823
|
-
}
|
|
824
|
-
if (leadingCommentLength > headerLineLength) {
|
|
825
|
-
context.report({
|
|
826
|
-
loc: {
|
|
827
|
-
start: {
|
|
828
|
-
column: "//".length + headerLineLength,
|
|
829
|
-
line: commentLoc.start.line
|
|
830
|
-
},
|
|
831
|
-
end: commentLoc.end,
|
|
832
|
-
},
|
|
833
|
-
messageId: "headerLineTooLong",
|
|
834
|
-
fix: canFix
|
|
835
|
-
? genReplaceFixer(
|
|
836
|
-
commentType,
|
|
837
|
-
context,
|
|
838
|
-
leadingComments,
|
|
839
|
-
fixLines,
|
|
840
|
-
eol,
|
|
841
|
-
numLines)
|
|
842
|
-
: null
|
|
843
|
-
});
|
|
844
|
-
return;
|
|
845
|
-
}
|
|
846
|
-
} else {
|
|
847
|
-
if (!match(comment.value, headerLine)) {
|
|
848
|
-
context.report({
|
|
849
|
-
loc: {
|
|
850
|
-
start: {
|
|
851
|
-
column: "//".length,
|
|
852
|
-
line: commentLoc.start.line,
|
|
853
|
-
},
|
|
854
|
-
end: commentLoc.end,
|
|
855
|
-
},
|
|
856
|
-
messageId: "incorrectHeaderLine",
|
|
857
|
-
data: {
|
|
858
|
-
pattern: headerLine.toString()
|
|
859
|
-
},
|
|
860
|
-
fix: canFix
|
|
861
|
-
? genReplaceFixer(
|
|
862
|
-
commentType,
|
|
863
|
-
context,
|
|
864
|
-
leadingComments,
|
|
865
|
-
fixLines,
|
|
866
|
-
eol,
|
|
867
|
-
numLines)
|
|
868
|
-
: null
|
|
869
|
-
});
|
|
870
|
-
return;
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
const commentRange = leadingComments[headerLines.length - 1].range;
|
|
877
|
-
assertDefined(commentRange);
|
|
878
|
-
const actualLeadingEmptyLines = leadingEmptyLines(sourceCode.text.substring(commentRange[1]));
|
|
879
|
-
const missingEmptyLines = numLines - actualLeadingEmptyLines;
|
|
880
|
-
if (missingEmptyLines > 0) {
|
|
881
|
-
context.report({
|
|
882
|
-
loc: missingEmptyLinesViolationLoc(leadingComments, actualLeadingEmptyLines),
|
|
883
|
-
messageId: "noNewlineAfterHeader",
|
|
884
|
-
data: {
|
|
885
|
-
expected: numLines,
|
|
886
|
-
actual: actualLeadingEmptyLines
|
|
887
|
-
},
|
|
888
|
-
fix: genEmptyLinesFixer(leadingComments, eol, missingEmptyLines)
|
|
889
|
-
});
|
|
890
|
-
}
|
|
891
|
-
return;
|
|
892
|
-
}
|
|
893
|
-
// if block comment pattern has more than 1 line, we also split
|
|
894
|
-
// the comment
|
|
895
|
-
let leadingLines = [leadingComments[0].value];
|
|
896
|
-
if (headerLines.length > 1) {
|
|
897
|
-
leadingLines = leadingComments[0].value.split(/\r?\n/);
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
/** @type {null | string} */
|
|
901
|
-
let errorMessageId = null;
|
|
902
|
-
/** @type {undefined | Record<string, string>} */
|
|
903
|
-
let errorMessageData;
|
|
904
|
-
/** @type {null | SourceLocation} */
|
|
905
|
-
let errorMessageLoc = null;
|
|
906
|
-
for (let i = 0; i < headerLines.length; i++) {
|
|
907
|
-
if (leadingLines.length - 1 < i) {
|
|
908
|
-
assertDefined(lastLeadingCommentLoc);
|
|
909
|
-
assertNotNull(lastLeadingCommentLoc);
|
|
910
|
-
context.report({
|
|
911
|
-
loc: {
|
|
912
|
-
start: lastLeadingCommentLoc.end,
|
|
913
|
-
end: lastLeadingCommentLoc.end
|
|
914
|
-
},
|
|
915
|
-
messageId: "headerTooShort",
|
|
916
|
-
data: {
|
|
917
|
-
remainder: headerLines.slice(i).join(eol)
|
|
918
|
-
},
|
|
919
|
-
fix: canFix
|
|
920
|
-
? genReplaceFixer(
|
|
921
|
-
commentType,
|
|
922
|
-
context,
|
|
923
|
-
leadingComments,
|
|
924
|
-
fixLines,
|
|
925
|
-
eol,
|
|
926
|
-
numLines)
|
|
927
|
-
: null
|
|
928
|
-
});
|
|
929
|
-
return;
|
|
930
|
-
}
|
|
931
|
-
const leadingLine = leadingLines[i];
|
|
932
|
-
const headerLine = headerLines[i];
|
|
933
|
-
if (typeof headerLine === "string") {
|
|
934
|
-
for (let j = 0; j < Math.min(leadingLine.length, headerLine.length); j++) {
|
|
935
|
-
if (leadingLine[j] !== headerLine[j]) {
|
|
936
|
-
errorMessageId = "headerLineMismatchAtPos";
|
|
937
|
-
const columnOffset = i === 0 ? "/*".length : 0;
|
|
938
|
-
assertDefined(firstLeadingCommentLoc);
|
|
939
|
-
assertNotNull(firstLeadingCommentLoc);
|
|
940
|
-
const line = firstLeadingCommentLoc.start.line + i;
|
|
941
|
-
errorMessageLoc = {
|
|
942
|
-
start: {
|
|
943
|
-
column: columnOffset + j,
|
|
944
|
-
line
|
|
945
|
-
},
|
|
946
|
-
end: {
|
|
947
|
-
column: columnOffset + leadingLine.length,
|
|
948
|
-
line
|
|
949
|
-
}
|
|
950
|
-
};
|
|
951
|
-
errorMessageData = {
|
|
952
|
-
expected: headerLine.substring(j)
|
|
953
|
-
};
|
|
954
|
-
break;
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
if (errorMessageId) {
|
|
958
|
-
break;
|
|
959
|
-
}
|
|
960
|
-
if (leadingLine.length < headerLine.length) {
|
|
961
|
-
errorMessageId = "headerLineTooShort";
|
|
962
|
-
const startColumn = (i === 0 ? "/*".length : 0) + leadingLine.length;
|
|
963
|
-
assertDefined(firstLeadingCommentLoc);
|
|
964
|
-
assertNotNull(firstLeadingCommentLoc);
|
|
965
|
-
errorMessageLoc = {
|
|
966
|
-
start: {
|
|
967
|
-
column: startColumn,
|
|
968
|
-
line: firstLeadingCommentLoc.start.line + i
|
|
969
|
-
},
|
|
970
|
-
end: {
|
|
971
|
-
column: startColumn + 1,
|
|
972
|
-
line: firstLeadingCommentLoc.start.line + i
|
|
973
|
-
}
|
|
974
|
-
};
|
|
975
|
-
errorMessageData = {
|
|
976
|
-
remainder: headerLine.substring(leadingLine.length)
|
|
977
|
-
};
|
|
978
|
-
break;
|
|
979
|
-
}
|
|
980
|
-
if (leadingLine.length > headerLine.length) {
|
|
981
|
-
assertDefined(firstLeadingCommentLoc);
|
|
982
|
-
assertNotNull(firstLeadingCommentLoc);
|
|
983
|
-
errorMessageId = "headerLineTooLong";
|
|
984
|
-
errorMessageLoc = {
|
|
985
|
-
start: {
|
|
986
|
-
column: (i === 0 ? "/*".length : 0) + headerLine.length,
|
|
987
|
-
line: firstLeadingCommentLoc.start.line + i
|
|
988
|
-
},
|
|
989
|
-
end: {
|
|
990
|
-
column: (i === 0 ? "/*".length : 0) + leadingLine.length,
|
|
991
|
-
line: firstLeadingCommentLoc.start.line + i
|
|
992
|
-
}
|
|
993
|
-
};
|
|
994
|
-
break;
|
|
995
|
-
}
|
|
996
|
-
} else {
|
|
997
|
-
if (!match(leadingLine, headerLine)) {
|
|
998
|
-
errorMessageId = "incorrectHeaderLine";
|
|
999
|
-
errorMessageData = {
|
|
1000
|
-
pattern: headerLine.toString()
|
|
1001
|
-
};
|
|
1002
|
-
const columnOffset = i === 0 ? "/*".length : 0;
|
|
1003
|
-
assertDefined(firstLeadingCommentLoc);
|
|
1004
|
-
assertNotNull(firstLeadingCommentLoc);
|
|
1005
|
-
errorMessageLoc = {
|
|
1006
|
-
start: {
|
|
1007
|
-
column: columnOffset + 0,
|
|
1008
|
-
line: firstLeadingCommentLoc.start.line + i
|
|
1009
|
-
},
|
|
1010
|
-
end: {
|
|
1011
|
-
column: columnOffset + leadingLine.length,
|
|
1012
|
-
line: firstLeadingCommentLoc.start.line + i
|
|
1013
|
-
}
|
|
1014
|
-
};
|
|
1015
|
-
break;
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
|
-
if (!errorMessageId && leadingLines.length > headerLines.length) {
|
|
1021
|
-
errorMessageId = "headerTooLong";
|
|
1022
|
-
assertDefined(firstLeadingCommentLoc);
|
|
1023
|
-
assertNotNull(firstLeadingCommentLoc);
|
|
1024
|
-
assertDefined(lastLeadingCommentLoc);
|
|
1025
|
-
assertNotNull(lastLeadingCommentLoc);
|
|
1026
|
-
errorMessageLoc = {
|
|
1027
|
-
start: {
|
|
1028
|
-
column: (headerLines.length === 0 ? "/*".length : 0) + 0,
|
|
1029
|
-
line: firstLeadingCommentLoc.start.line + headerLines.length
|
|
1030
|
-
},
|
|
1031
|
-
end: {
|
|
1032
|
-
column: lastLeadingCommentLoc.end.column - "*/".length,
|
|
1033
|
-
line: lastLeadingCommentLoc.end.line
|
|
1034
|
-
}
|
|
1035
|
-
};
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
if (errorMessageId) {
|
|
1039
|
-
if (canFix && headerLines.length > 1) {
|
|
1040
|
-
fixLines = [fixLines.join(eol)];
|
|
948
|
+
const leadingComments = getLeadingComments(sourceCode);
|
|
949
|
+
|
|
950
|
+
const report = headerMatcher.validate(leadingComments, sourceCode);
|
|
951
|
+
|
|
952
|
+
if (report !== null) {
|
|
953
|
+
if ("messageId" in report && report.messageId === "noNewlineAfterHeader") {
|
|
954
|
+
const { expected, actual } =
|
|
955
|
+
/** @type {{ expected: number, actual: number }} */ (report.data);
|
|
956
|
+
report.fix = genEmptyLinesFixer(leadingComments, eol, expected - actual);
|
|
957
|
+
} else if (canFix) {
|
|
958
|
+
report.fix = genReplaceFixer(
|
|
959
|
+
headerMatcher.commentType,
|
|
960
|
+
sourceCode,
|
|
961
|
+
leadingComments,
|
|
962
|
+
fixLines,
|
|
963
|
+
eol,
|
|
964
|
+
numLines);
|
|
1041
965
|
}
|
|
1042
|
-
|
|
1043
|
-
context.report({
|
|
1044
|
-
loc: errorMessageLoc,
|
|
1045
|
-
messageId: errorMessageId,
|
|
1046
|
-
data: errorMessageData,
|
|
1047
|
-
fix: canFix
|
|
1048
|
-
? genReplaceFixer(
|
|
1049
|
-
commentType,
|
|
1050
|
-
context,
|
|
1051
|
-
leadingComments,
|
|
1052
|
-
fixLines,
|
|
1053
|
-
eol,
|
|
1054
|
-
numLines)
|
|
1055
|
-
: null
|
|
1056
|
-
});
|
|
1057
|
-
return;
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
|
-
const actualLeadingEmptyLines =
|
|
1061
|
-
leadingEmptyLines(sourceCode.text.substring(firstLeadingCommentRange[1]));
|
|
1062
|
-
const missingEmptyLines = numLines - actualLeadingEmptyLines;
|
|
1063
|
-
if (missingEmptyLines > 0) {
|
|
1064
|
-
context.report({
|
|
1065
|
-
loc: missingEmptyLinesViolationLoc(leadingComments, actualLeadingEmptyLines),
|
|
1066
|
-
messageId: "noNewlineAfterHeader",
|
|
1067
|
-
data: {
|
|
1068
|
-
expected: numLines,
|
|
1069
|
-
actual: actualLeadingEmptyLines
|
|
1070
|
-
},
|
|
1071
|
-
fix: genEmptyLinesFixer(leadingComments, eol, missingEmptyLines)
|
|
1072
|
-
});
|
|
966
|
+
context.report(report);
|
|
1073
967
|
}
|
|
1074
968
|
}
|
|
1075
969
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tony.ganchev/eslint-plugin-header",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.6",
|
|
4
4
|
"description": "The native ESLint 9/10 header plugin. A zero-bloat, drop-in replacement for 'eslint-plugin-header' with first-class Flat Config & TypeScript support. Auto-fix Copyright, License, and banner comments in JavaScrip and TypeScript files.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -18,19 +18,20 @@
|
|
|
18
18
|
"CONTRIBUTING.md"
|
|
19
19
|
],
|
|
20
20
|
"devDependencies": {
|
|
21
|
+
"@eslint/core": "^1.1.1",
|
|
21
22
|
"@eslint/js": "^10.0.1",
|
|
22
23
|
"@eslint/markdown": "^7.5.1",
|
|
23
|
-
"@stylistic/eslint-plugin": "^5.
|
|
24
|
+
"@stylistic/eslint-plugin": "^5.10.0",
|
|
24
25
|
"@types/estree": "^1.0.8",
|
|
25
26
|
"@types/json-schema": "^7.0.15",
|
|
26
|
-
"@types/node": "^25.3.
|
|
27
|
-
"c8": "^
|
|
28
|
-
"eslint": "^10.0.
|
|
29
|
-
"eslint-plugin-eslint-plugin": "^7.3.
|
|
27
|
+
"@types/node": "^25.3.5",
|
|
28
|
+
"c8": "^11.0.0",
|
|
29
|
+
"eslint": "^10.0.3",
|
|
30
|
+
"eslint-plugin-eslint-plugin": "^7.3.2",
|
|
30
31
|
"eslint-plugin-jsdoc": "^62.7.1",
|
|
31
32
|
"eslint-plugin-n": "^17.24.0",
|
|
32
|
-
"globals": "^17.
|
|
33
|
-
"markdownlint-cli": "^0.
|
|
33
|
+
"globals": "^17.4.0",
|
|
34
|
+
"markdownlint-cli": "^0.48.0",
|
|
34
35
|
"mocha": "12.0.0-beta-9",
|
|
35
36
|
"testdouble": "^3.20.2",
|
|
36
37
|
"typescript": "^5.9.3",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"header.d.ts","sourceRoot":"","sources":["../../../lib/rules/header.js"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"header.d.ts","sourceRoot":"","sources":["../../../lib/rules/header.js"],"names":[],"mappings":";2BAsCa,iBAAiB;0BACjB,gBAAgB;wBAChB,cAAc;0BACd,gBAAgB;;;;;yBAIhB,IAAI,GAAG,MAAM;;;;;;;;;;aAOZ,MAAM,GAAG,MAAM;;;;;;;;;;;;yBAOhB,MAAM,GAAG,MAAM,GAAG,iBAAiB;;;;;0BAGnC,UAAU,GAAG,UAAU,EAAE;;;;;;+BAEzB,IAAI,GAAG,MAAM,GAAG,SAAS;;;;;6BAGzB;IAAE,WAAW,CAAC,EAAE,gBAAgB,CAAA;CAAE;;;;;0BAElC,OAAO,GAAG,MAAM;;;;;;;;;;UAOf,MAAM;;;;;;;;;;;;;;;iBASN,WAAW;;;;;;WACX,UAAU,EAAE;;;;;;;;;;;;;;;;;;YAcZ,eAAe,GAAG,YAAY;;;;;;;;;;;4BAO/B,4BAA4B,GAAG,cAAc;oCAK7C,CAAC,QAAQ,EAAE,MAAM,CAAC;4CAClB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,CAAC;iCAE5C,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,CAAC;yCACvC,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,cAAc,CAAC;yCAEjE,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,CAAC;iDAEzD,CACV,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,cAAc,CACvB;;;;+BACS,CAAC,aAAa,CAAC,GACvB,qBAAqB,GACrB,6BAA6B,GAC7B,kBAAkB,GAClB,0BAA0B,GAC1B,0BAA0B,GAC1B,kCAAkC;;;;;+BAK1B,iBAAiB,gBAAgB,CAAC;AA0sB/C,8BAA8B;AAC9B,0BADW,eAAe,CAuHxB;0BAv6B4D,QAAQ;4BAAR,QAAQ"}
|