@tony.ganchev/eslint-plugin-header 3.1.11 → 3.2.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.
@@ -33,8 +33,8 @@ const assert = require("assert");
33
33
  * This is a really simple and dumb parser, that looks just for a
34
34
  * single kind of comment. It won't detect multiple block comments.
35
35
  * @param {string} commentText comment text.
36
- * @returns {['block' | 'line', string | string[]]} comment type and comment
37
- * content broken into lines.
36
+ * @returns {['block' | 'line', string[]]} comment type and comment content
37
+ * broken into lines.
38
38
  */
39
39
  module.exports = function commentParser(commentText) {
40
40
  assert.strictEqual(typeof commentText, "string");
@@ -46,7 +46,7 @@ module.exports = function commentParser(commentText) {
46
46
  text.split(/\r?\n/).map((line) => line.substring(2))
47
47
  ];
48
48
  } else if (text.startsWith("/*") && text.endsWith("*/")) {
49
- return ["block", text.substring(2, text.length - 2)];
49
+ return ["block", text.substring(2, text.length - 2).split(/\r?\n/)];
50
50
  } else {
51
51
  throw new Error(
52
52
  "Could not parse comment file: the file must contain either just line comments (//) or a single block " +
@@ -0,0 +1,42 @@
1
+ /*
2
+ * MIT License
3
+ *
4
+ * Copyright (c) 2025-present Tony Ganchev and contributors
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the “Software”), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in
14
+ * all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+
25
+ "use strict";
26
+
27
+ /**
28
+ * @typedef {import('eslint').Rule.RuleContext} RuleContext
29
+ * @typedef {import('eslint').SourceCode} SourceCode
30
+ */
31
+
32
+ module.exports = {
33
+ /**
34
+ * Provides compatibility wrapper for ESLint 7 through 10 for getting the
35
+ * full source code object from the execution context.
36
+ * @param {RuleContext} context ESLint execution context.
37
+ * @returns {SourceCode} the source-code object.
38
+ */
39
+ contextSourceCode: function(context) {
40
+ return context.sourceCode || context.getSourceCode();
41
+ }
42
+ };
@@ -28,8 +28,9 @@ const assert = require("assert");
28
28
  const fs = require("fs");
29
29
  const os = require("os");
30
30
  const commentParser = require("../comment-parser");
31
+ const { contextSourceCode } = require("./eslint-utils");
31
32
  const { description, recommended, url } = require("./header.docs");
32
- const { commentTypeOptions, lineEndingOptions, schema } = require("./header.schema");
33
+ const { lineEndingOptions, commentTypeOptions, schema } = require("./header.schema");
33
34
 
34
35
  /**
35
36
  * Import type definitions.
@@ -46,18 +47,40 @@ const { commentTypeOptions, lineEndingOptions, schema } = require("./header.sche
46
47
  */
47
48
 
48
49
  /**
49
- * Local type defintions.
50
- * @typedef {string | { pattern: string, template?: string }} HeaderLine
50
+ * Local type definitions.
51
+ * @typedef {{ pattern: string | RegExp, template?: string }} HeaderLinePattern
52
+ * @typedef {string | RegExp | HeaderLinePattern} HeaderLine
51
53
  * @typedef {(HeaderLine | HeaderLine[])} HeaderLines
52
- * @typedef {{ lineEndings: ('unix' | 'windows') }} HeaderSettings
54
+ * @typedef {{ lineEndings?: ('unix' | 'windows' | 'os') }} HeaderSettings
53
55
  * @typedef {
54
56
  * [string]
55
57
  * | [string, HeaderSettings]
56
- * | [('block' | 'line') | HeaderLines ]
57
- * | [('block' | 'line') | HeaderLines | HeaderSettings]
58
- * | [('block' | 'line') | HeaderLines | number ]
59
- * | [('block' | 'line') | HeaderLines | number | HeaderSettings]
60
- * } HeaderOptions
58
+ * | [('block' | 'line'), HeaderLines ]
59
+ * | [('block' | 'line'), HeaderLines, HeaderSettings]
60
+ * | [('block' | 'line'), HeaderLines, number ]
61
+ * | [('block' | 'line'), HeaderLines, number, HeaderSettings]
62
+ * } LegacyHeaderOptions
63
+ * @typedef {
64
+ * {
65
+ * file: string,
66
+ * encoding?: string
67
+ * }
68
+ * } FileBasedConfig
69
+ * @typedef {
70
+ * {
71
+ * commentType: 'line' | 'block',
72
+ * lines: HeaderLine[]
73
+ * }
74
+ * } LinesBasedConfig
75
+ * @typedef {{ minimum?: number }} TrailingEmptyLines
76
+ * @typedef {
77
+ * {
78
+ * header: FileBasedConfig | LinesBasedConfig,
79
+ * trailingEmptyLines?: TrailingEmptyLines
80
+ * }
81
+ * & HeaderSettings
82
+ * } NewHeaderOptions
83
+ * @typedef {LegacyHeaderOptions | [NewHeaderOptions]} HeaderOptions
61
84
  */
62
85
 
63
86
  /**
@@ -68,7 +91,8 @@ const { commentTypeOptions, lineEndingOptions, schema } = require("./header.sche
68
91
  * object or `false` otherwise.
69
92
  */
70
93
  function isPattern(object) {
71
- return typeof object === "object" && Object.prototype.hasOwnProperty.call(object, "pattern");
94
+ return typeof object === "object"
95
+ && (object instanceof RegExp || Object.prototype.hasOwnProperty.call(object, "pattern"));
72
96
  }
73
97
 
74
98
  /**
@@ -90,7 +114,7 @@ function match(actual, expected) {
90
114
  /**
91
115
  * Remove Unix she-bangs from the list of comments.
92
116
  * @param {Comment[]} comments the list of comment lines.
93
- * @returns {Comment[]} the list of comments with containing all incomming
117
+ * @returns {Comment[]} the list of comments with containing all incoming
94
118
  * comments from `comments` with the shebang comments
95
119
  * omitted.
96
120
  */
@@ -106,17 +130,18 @@ function excludeShebangs(comments) {
106
130
  * check if they are at the start of the file since that is already checked by
107
131
  * `hasHeader()`.
108
132
  * @param {RuleContext} context ESLint execution environment.
109
- * @param {Program} node ESLint AST treee node being processed.
133
+ * @param {Program} node ESLint AST tree node being processed.
110
134
  * @returns {Comment[]} lines that constitute the leading comment.
111
135
  */
112
136
  function getLeadingComments(context, node) {
113
- const all = excludeShebangs(context.sourceCode.getAllComments(node.body.length ? node.body[0] : node));
137
+ const sourceCode = contextSourceCode(context);
138
+ const all = excludeShebangs(sourceCode.getAllComments(node.body.length ? node.body[0] : node));
114
139
  if (all[0].type.toLowerCase() === commentTypeOptions.block) {
115
140
  return [all[0]];
116
141
  }
117
142
  let i = 1;
118
143
  for (; i < all.length; ++i) {
119
- const txt = context.sourceCode.text.slice(all[i - 1].range[1], all[i].range[0]);
144
+ const txt = sourceCode.text.slice(all[i - 1].range[1], all[i].range[0]);
120
145
  if (!txt.match(/^(\r\n|\r|\n)$/)) {
121
146
  break;
122
147
  }
@@ -186,15 +211,16 @@ function leadingEmptyLines(src) {
186
211
  function genPrependFixer(commentType, context, headerLines, eol, numNewlines) {
187
212
  return function(fixer) {
188
213
  let insertPos = 0;
189
- let newHeader = genCommentBody(commentType, headerLines, eol, numNewlines);
190
- if (context.sourceCode.text.startsWith("#!")) {
191
- const firstNewLinePos = context.sourceCode.text.indexOf("\n");
192
- insertPos = firstNewLinePos === -1 ? context.sourceCode.text.length : firstNewLinePos + 1;
214
+ let newHeader = genCommentBody(commentType, headerLines, eol);
215
+ const sourceCode = contextSourceCode(context);
216
+ if (sourceCode.text.startsWith("#!")) {
217
+ const firstNewLinePos = sourceCode.text.indexOf("\n");
218
+ insertPos = firstNewLinePos === -1 ? sourceCode.text.length : firstNewLinePos + 1;
193
219
  if (firstNewLinePos === -1) {
194
220
  newHeader = eol + newHeader;
195
221
  }
196
222
  }
197
- const numEmptyLines = leadingEmptyLines(context.sourceCode.text.substring(insertPos));
223
+ const numEmptyLines = leadingEmptyLines(sourceCode.text.substring(insertPos));
198
224
  const additionalEmptyLines = Math.max(0, numNewlines - numEmptyLines);
199
225
  newHeader += eol.repeat(additionalEmptyLines);
200
226
  return fixer.insertTextBeforeRange(
@@ -219,7 +245,7 @@ function genPrependFixer(commentType, context, headerLines, eol, numNewlines) {
219
245
  function genReplaceFixer(commentType, context, leadingComments, headerLines, eol, numNewlines) {
220
246
  return function(fixer) {
221
247
  const commentRange = genCommentsRange(leadingComments);
222
- const emptyLines = leadingEmptyLines(context.sourceCode.text.substring(commentRange[1]));
248
+ const emptyLines = leadingEmptyLines(contextSourceCode(context).text.substring(commentRange[1]));
223
249
  const missingNewlines = Math.max(0, numNewlines - emptyLines);
224
250
  const eols = eol.repeat(missingNewlines);
225
251
  return fixer.replaceTextRange(
@@ -248,39 +274,25 @@ function genEmptyLinesFixer(leadingComments, eol, missingEmptyLinesCount) {
248
274
  };
249
275
  }
250
276
 
251
- /**
252
- * Finds the option parameter within the list of rule config options.
253
- * @param {HeaderOptions} options the config options passed to the rule.
254
- * @returns {HeaderSettings | null} the settings parameter or `null` if no such
255
- * is defined.
256
- */
257
- function findSettings(options) {
258
- const lastOption = options[options.length - 1];
259
- if (typeof lastOption === "object" && !Array.isArray(lastOption) && lastOption !== null
260
- && !Object.prototype.hasOwnProperty.call(lastOption, "pattern")) {
261
- return lastOption;
262
- }
263
- return null;
264
- }
265
-
266
277
  /**
267
278
  * Returns the used line-termination characters per the rule's config if any or
268
279
  * else based on the runtime environments.
269
- * @param {HeaderOptions} options rule configuration.
280
+ * @param {'os' | 'unix' | 'windows'} style line-ending styles.
270
281
  * @returns {'\n' | '\r\n'} the correct line ending characters for the
271
- * environment.
282
+ * environment.
272
283
  */
273
- function getEOL(options) {
274
- const settings = findSettings(options);
275
- if (settings) {
276
- if (settings.lineEndings === lineEndingOptions.unix) {
284
+ function getEol(style) {
285
+ assert.strictEqual(Object.prototype.hasOwnProperty.call(lineEndingOptions, style), true,
286
+ "lineEnding style should have been populated in normalizeOptions().");
287
+ switch (style) {
288
+ case lineEndingOptions.unix:
277
289
  return "\n";
278
- }
279
- if (settings.lineEndings === lineEndingOptions.windows) {
290
+ case lineEndingOptions.windows:
280
291
  return "\r\n";
281
- }
292
+ case lineEndingOptions.os:
293
+ default:
294
+ return os.EOL;
282
295
  }
283
- return os.EOL;
284
296
  }
285
297
 
286
298
  /**
@@ -294,6 +306,134 @@ function hasHeader(src) {
294
306
  return srcWithoutShebang.startsWith("/*") || srcWithoutShebang.startsWith("//");
295
307
  }
296
308
 
309
+ /**
310
+ * asserts on an expression and adds template texts to the failure message.
311
+ * Helper to write cleaner code.
312
+ * @param {boolean} condition assert condition.
313
+ * @param {string} message assert message on violation.
314
+ */
315
+ function schemaAssert(condition, message) {
316
+ assert.strictEqual(condition, true, message + " - should have been handled by eslint schema validation.");
317
+ }
318
+
319
+ /**
320
+ * Ensures that if legacy options as defined in the original
321
+ * `eslint-plugin-header` are used, they'd be converted to the new object-based
322
+ * configuration. This is not a normalized internal representation of the
323
+ * options in that some settings are still union types and unspecified
324
+ * properties are not replaced by defaults. If the options follow the new
325
+ * format, a simple seep copy would be returned.
326
+ * @param {HeaderOptions} originalOptions the options as configured by the user.
327
+ * @returns {NewHeaderOptions} the transformed new-style options with no
328
+ * normalization.
329
+ */
330
+ function transformLegacyOptions(originalOptions) {
331
+ schemaAssert(originalOptions?.length > 0,
332
+ "header options are required (at least one in addition to the severity)");
333
+ schemaAssert(originalOptions.length <= 4,
334
+ "header options should not be more than four (five including issue severity)");
335
+ if (originalOptions.length === 1 && typeof originalOptions[0] === "object") {
336
+ // The user chose to use the new-style config. We pass a copy of the
337
+ // incoming sole option to not mess with ESLint not relying on the old
338
+ // values of the option.
339
+ return structuredClone(originalOptions[0]);
340
+ }
341
+ // The user chose the legacy-style config. Transform them to new-style
342
+ // config.
343
+ schemaAssert(typeof originalOptions[0] === "string",
344
+ "first header option after severity should be either a filename or 'block' | 'line'");
345
+ /** @type {NewHeaderOptions} */
346
+ const transformedOptions = {};
347
+ // populate header
348
+ if (
349
+ originalOptions.length === 1
350
+ || (
351
+ originalOptions.length === 2
352
+ && typeof originalOptions[1] === "object"
353
+ && !Array.isArray(originalOptions[1])
354
+ && !isPattern(originalOptions[1])
355
+ )) {
356
+ transformedOptions.header = { file: originalOptions[0], encoding: "utf8" };
357
+ } else {
358
+ schemaAssert(Object.prototype.hasOwnProperty.call(commentTypeOptions, originalOptions[0]),
359
+ "Only 'block' or 'line' is accepted as comment type");
360
+ schemaAssert(
361
+ typeof originalOptions[1] === "string"
362
+ || Array.isArray(originalOptions[1])
363
+ || isPattern(originalOptions[1]),
364
+ "second header option after severity should be a string, a pattern, or an array of the previous two");
365
+ transformedOptions.header = {
366
+ commentType: originalOptions[0],
367
+ lines: Array.isArray(originalOptions[1]) ? originalOptions[1] : [originalOptions[1]]
368
+ };
369
+ }
370
+ // configure required line settings
371
+ if (originalOptions.length >= 3) {
372
+ if (typeof originalOptions[2] === "number") {
373
+ transformedOptions.trailingEmptyLines = { minimum: originalOptions[2] };
374
+ if (originalOptions.length === 4) {
375
+ schemaAssert(typeof originalOptions[3] === "object",
376
+ "Fourth header option after severity should be either number of required trailing empty lines or " +
377
+ "a settings object");
378
+ Object.assign(transformedOptions, originalOptions[3]);
379
+ }
380
+ } else {
381
+ schemaAssert(typeof originalOptions[2] === "object",
382
+ "Third header option after severity should be either number of required trailing empty lines or a " +
383
+ "settings object");
384
+ Object.assign(transformedOptions, originalOptions[2]);
385
+ }
386
+ }
387
+ return transformedOptions;
388
+ }
389
+
390
+ /**
391
+ * Transforms a set of new-style options adding defaults and standardizing on
392
+ * one of multiple config styles.
393
+ * @param {NewHeaderOptions} originalOptions new-style options to normalize.
394
+ * @returns {NewHeaderOptions} normalized options.
395
+ */
396
+ function normalizeOptions(originalOptions) {
397
+ const options = structuredClone(originalOptions);
398
+
399
+ if (options.header.file) {
400
+ const text = fs.readFileSync(originalOptions.header.file, originalOptions.header.encoding || "utf8");
401
+ const [commentType, lines] = commentParser(text);
402
+ options.header = { commentType, lines };
403
+ }
404
+
405
+ options.header.lines = options.header.lines.flatMap(
406
+ (line) => {
407
+ if (typeof line === "string") {
408
+ return line.split(/\r?\n/);
409
+ }
410
+ if (line instanceof RegExp) {
411
+ return [{ pattern: line }];
412
+ }
413
+ assert.ok(Object.prototype.hasOwnProperty.call(line, "pattern"));
414
+ const pattern = line.pattern instanceof RegExp ? line.pattern : new RegExp(line.pattern);
415
+ if (Object.prototype.hasOwnProperty.call(line, "template")) {
416
+ return [{
417
+ pattern,
418
+ template: line.template
419
+ }];
420
+ }
421
+ return [{ pattern }];
422
+ });
423
+
424
+ if (!options.lineEndings) {
425
+ options.lineEndings = "os";
426
+ }
427
+
428
+ if (!options.trailingEmptyLines) {
429
+ options.trailingEmptyLines = {};
430
+ }
431
+ if (typeof options.trailingEmptyLines.minimum !== "number") {
432
+ options.trailingEmptyLines.minimum = 1;
433
+ }
434
+ return options;
435
+ }
436
+
297
437
  /**
298
438
  * Calculates the source location of the violation that not enough empty lines
299
439
  * follow the header.
@@ -328,7 +468,15 @@ module.exports = {
328
468
  },
329
469
  fixable: "whitespace",
330
470
  schema,
331
- defaultOptions: [{}],
471
+ defaultOptions: [
472
+ /** @type {HeaderOptions} */
473
+ {
474
+ lineEndings: lineEndingOptions.os,
475
+ trailingEmptyLines: {
476
+ minimum: 1
477
+ }
478
+ }
479
+ ],
332
480
  messages: {
333
481
  headerLineMismatchAtPos: "header line does not match expected after this position; expected: {{expected}}",
334
482
  headerLineTooLong: "header line longer than expected",
@@ -348,46 +496,26 @@ module.exports = {
348
496
  * @returns {NodeListener} the rule definition.
349
497
  */
350
498
  create: function(context) {
351
- let options = context.options;
352
- const numNewlines = options.length > 2 && typeof options[2] === "number" ? options[2] : 1;
353
- const eol = getEOL(options);
354
-
355
- // If just one option then read comment from file
356
- if (options.length === 1 || (options.length === 2 && findSettings(options))) {
357
- const text = fs.readFileSync(context.options[0], "utf8");
358
- options = commentParser(text);
359
- }
360
499
 
361
- const commentType = options[0].toLowerCase();
362
- /** @type {(string | RegExp)[]} */
363
- let headerLines;
500
+ const newStyleOptions = transformLegacyOptions(context.options);
501
+ const options = normalizeOptions(newStyleOptions);
502
+
503
+ const eol = getEol(options.lineEndings);
504
+
364
505
  let fixLines = [];
365
506
  // If any of the lines are regular expressions, then we can't
366
507
  // automatically fix them. We set this to true below once we
367
508
  // ensure none of the lines are of type RegExp
368
- let canFix = false;
369
- if (Array.isArray(options[1])) {
370
- canFix = true;
371
- headerLines = options[1].map(function(line) {
372
- const isRegex = isPattern(line);
373
- // Can only fix regex option if a template is also provided
374
- if (isRegex && !line.template) {
375
- canFix = false;
376
- }
377
- fixLines.push(line.template || line);
378
- return isRegex ? new RegExp(line.pattern) : line;
379
- });
380
- } else if (isPattern(options[1])) {
381
- const line = options[1];
382
- headerLines = [new RegExp(line.pattern)];
509
+ let canFix = true;
510
+ const headerLines = options.header.lines.map(function(line) {
511
+ const isRegex = isPattern(line);
512
+ // Can only fix regex option if a template is also provided
513
+ if (isRegex && !line.template) {
514
+ canFix = false;
515
+ }
383
516
  fixLines.push(line.template || line);
384
- // Same as above for regex and template
385
- canFix = !!line.template;
386
- } else {
387
- canFix = true;
388
- headerLines = options[1].split(/\r?\n/);
389
- fixLines = headerLines;
390
- }
517
+ return isRegex ? line.pattern : line;
518
+ });
391
519
 
392
520
  return {
393
521
  /**
@@ -397,8 +525,9 @@ module.exports = {
397
525
  * @returns {void}
398
526
  */
399
527
  Program: function(node) {
400
- if (!hasHeader(context.sourceCode.text)) {
401
- const hasShebang = context.sourceCode.text.startsWith("#!");
528
+ const sourceCode = contextSourceCode(context);
529
+ if (!hasHeader(sourceCode.text)) {
530
+ const hasShebang = sourceCode.text.startsWith("#!");
402
531
  const line = hasShebang ? 2 : 1;
403
532
  context.report({
404
533
  loc: {
@@ -412,13 +541,19 @@ module.exports = {
412
541
  }
413
542
  },
414
543
  messageId: "missingHeader",
415
- fix: genPrependFixer(commentType, context, fixLines, eol, numNewlines)
544
+ fix: genPrependFixer(
545
+ options.header.commentType,
546
+ context,
547
+ fixLines,
548
+ eol,
549
+ options.trailingEmptyLines.minimum)
416
550
  });
417
551
  return;
418
552
  }
553
+
419
554
  const leadingComments = getLeadingComments(context, node);
420
555
 
421
- if (leadingComments[0].type.toLowerCase() !== commentType) {
556
+ if (leadingComments[0].type.toLowerCase() !== options.header.commentType) {
422
557
  context.report({
423
558
  loc: {
424
559
  start: leadingComments[0].loc.start,
@@ -426,15 +561,21 @@ module.exports = {
426
561
  },
427
562
  messageId: "incorrectCommentType",
428
563
  data: {
429
- commentType: commentType
564
+ commentType: options.header.commentType
430
565
  },
431
566
  fix: canFix
432
- ? genReplaceFixer(commentType, context, leadingComments, fixLines, eol, numNewlines)
567
+ ? genReplaceFixer(
568
+ options.header.commentType,
569
+ context,
570
+ leadingComments,
571
+ fixLines,
572
+ eol,
573
+ options.trailingEmptyLines.minimum)
433
574
  : null
434
575
  });
435
576
  return;
436
577
  }
437
- if (commentType === commentTypeOptions.line) {
578
+ if (options.header.commentType === commentTypeOptions.line) {
438
579
  if (headerLines.length === 1) {
439
580
  const leadingCommentValues = leadingComments.map((c) => c.value);
440
581
  if (
@@ -448,7 +589,13 @@ module.exports = {
448
589
  },
449
590
  messageId: "incorrectHeader",
450
591
  fix: canFix
451
- ? genReplaceFixer(commentType, context, leadingComments, fixLines, eol, numNewlines)
592
+ ? genReplaceFixer(
593
+ options.header.commentType,
594
+ context,
595
+ leadingComments,
596
+ fixLines,
597
+ eol,
598
+ options.trailingEmptyLines.minimum)
452
599
  : null
453
600
  });
454
601
  return;
@@ -466,12 +613,12 @@ module.exports = {
466
613
  },
467
614
  fix: canFix
468
615
  ? genReplaceFixer(
469
- commentType,
616
+ options.header.commentType,
470
617
  context,
471
618
  leadingComments,
472
619
  fixLines,
473
620
  eol,
474
- numNewlines)
621
+ options.trailingEmptyLines.minimum)
475
622
  : null
476
623
  });
477
624
  return;
@@ -494,12 +641,12 @@ module.exports = {
494
641
  expected: headerLines[i].substring(j)
495
642
  },
496
643
  fix: genReplaceFixer(
497
- commentType,
644
+ options.header.commentType,
498
645
  context,
499
646
  leadingComments,
500
647
  fixLines,
501
648
  eol,
502
- numNewlines)
649
+ options.trailingEmptyLines.minimum)
503
650
  });
504
651
  return;
505
652
  }
@@ -515,12 +662,12 @@ module.exports = {
515
662
  },
516
663
  fix: canFix
517
664
  ? genReplaceFixer(
518
- commentType,
665
+ options.header.commentType,
519
666
  context,
520
667
  leadingComments,
521
668
  fixLines,
522
669
  eol,
523
- numNewlines)
670
+ options.trailingEmptyLines.minimum)
524
671
  : null
525
672
  });
526
673
  return;
@@ -537,12 +684,12 @@ module.exports = {
537
684
  messageId: "headerLineTooLong",
538
685
  fix: canFix
539
686
  ? genReplaceFixer(
540
- commentType,
687
+ options.header.commentType,
541
688
  context,
542
689
  leadingComments,
543
690
  fixLines,
544
691
  eol,
545
- numNewlines)
692
+ options.trailingEmptyLines.minimum)
546
693
  : null
547
694
  });
548
695
  return;
@@ -563,12 +710,12 @@ module.exports = {
563
710
  },
564
711
  fix: canFix
565
712
  ? genReplaceFixer(
566
- commentType,
713
+ options.header.commentType,
567
714
  context,
568
715
  leadingComments,
569
716
  fixLines,
570
717
  eol,
571
- numNewlines)
718
+ options.trailingEmptyLines.minimum)
572
719
  : null
573
720
  });
574
721
  return;
@@ -577,15 +724,15 @@ module.exports = {
577
724
  }
578
725
  }
579
726
 
580
- const actualLeadingEmptyLines = leadingEmptyLines(
581
- context.sourceCode.text.substring(leadingComments[headerLines.length - 1].range[1]));
582
- const missingEmptyLines = numNewlines - actualLeadingEmptyLines;
727
+ const actualLeadingEmptyLines =
728
+ leadingEmptyLines(sourceCode.text.substring(leadingComments[headerLines.length - 1].range[1]));
729
+ const missingEmptyLines = options.trailingEmptyLines.minimum - actualLeadingEmptyLines;
583
730
  if (missingEmptyLines > 0) {
584
731
  context.report({
585
732
  loc: missingEmptyLinesViolationLoc(leadingComments, actualLeadingEmptyLines),
586
733
  messageId: "noNewlineAfterHeader",
587
734
  data: {
588
- expected: numNewlines,
735
+ expected: options.trailingEmptyLines.minimum,
589
736
  actual: actualLeadingEmptyLines
590
737
  },
591
738
  fix: genEmptyLinesFixer(leadingComments, eol, missingEmptyLines)
@@ -711,21 +858,27 @@ module.exports = {
711
858
  messageId: errorMessageId,
712
859
  data: errorMessageData,
713
860
  fix: canFix
714
- ? genReplaceFixer(commentType, context, leadingComments, fixLines, eol, numNewlines)
861
+ ? genReplaceFixer(
862
+ options.header.commentType,
863
+ context,
864
+ leadingComments,
865
+ fixLines,
866
+ eol,
867
+ options.trailingEmptyLines.minimum)
715
868
  : null
716
869
  });
717
870
  return;
718
871
  }
719
872
 
720
- const actualLeadingEmptyLines = leadingEmptyLines(
721
- context.sourceCode.text.substring(leadingComments[0].range[1]));
722
- const missingEmptyLines = numNewlines - actualLeadingEmptyLines;
873
+ const actualLeadingEmptyLines =
874
+ leadingEmptyLines(sourceCode.text.substring(leadingComments[0].range[1]));
875
+ const missingEmptyLines = options.trailingEmptyLines.minimum - actualLeadingEmptyLines;
723
876
  if (missingEmptyLines > 0) {
724
877
  context.report({
725
878
  loc: missingEmptyLinesViolationLoc(leadingComments, actualLeadingEmptyLines),
726
879
  messageId: "noNewlineAfterHeader",
727
880
  data: {
728
- expected: numNewlines,
881
+ expected: options.trailingEmptyLines.minimum,
729
882
  actual: actualLeadingEmptyLines
730
883
  },
731
884
  fix: genEmptyLinesFixer(leadingComments, eol, missingEmptyLines)
@@ -735,3 +888,4 @@ module.exports = {
735
888
  };
736
889
  }
737
890
  };
891
+