eslint-plugin-imports-regulation 0.2.7 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import type { TSESLint } from '@typescript-eslint/utils';
2
- type MessageIds = 'groupOrder' | 'blankLinesBetween' | 'unexpectedBlankLine' | 'typeSpecifiersLast' | 'markTypeOnly' | 'typeOnlyImport' | 'typeOnlySpecifier' | 'blankLinesAfter';
2
+ type MessageIds = 'groupOrder' | 'blankLinesBetween' | 'unexpectedBlankLine' | 'blankLineBelowComment' | 'typeSpecifiersLast' | 'markTypeOnly' | 'typeOnlyImport' | 'typeOnlySpecifier' | 'blankLinesAfter';
3
3
  type Origin = 'external' | 'internal';
4
4
  type Options = [
5
5
  {
@@ -65,7 +65,8 @@ const rule = {
65
65
  messages: {
66
66
  groupOrder: 'Expected this {{group}} import before the {{other}} imports.',
67
67
  blankLinesBetween: 'Expected {{expected}} blank {{lines}} between external imports and internal imports, found {{actual}}.',
68
- unexpectedBlankLine: 'Unexpected blank line between imports in the same group.',
68
+ unexpectedBlankLine: 'Expected no blank lines between imports in the same group, found {{actual}}.',
69
+ blankLineBelowComment: 'Expected no blank lines between a comment and the import it belongs to, found {{actual}}.',
69
70
  typeSpecifiersLast: 'Type specifiers must come after value specifiers.',
70
71
  markTypeOnly: 'An import of nothing but types must be written `import type { A }`.',
71
72
  typeOnlyImport: 'Everything this imports is only used as a type — write `import type`.',
@@ -389,6 +390,32 @@ const rule = {
389
390
  }
390
391
  return entries;
391
392
  };
393
+ /**
394
+ * An entry's text with the blank lines below its comments taken out, and how many there were.
395
+ * Only the whitespace between comments is looked at: a blank line inside a block comment is
396
+ * the comment's own.
397
+ */
398
+ const tightened = (entry) => {
399
+ let blank = 0;
400
+ let text = '';
401
+ let cursor = entry.start;
402
+ const closeGap = (to) => {
403
+ const gap = source.text.slice(cursor, to);
404
+ const breaks = (gap.match(BREAK_GLOBAL) ?? []).length;
405
+ blank += Math.max(breaks - 1, 0);
406
+ text += breaks > 1 ? `${eol}${/[^\r\n]*$/.exec(gap)?.[0] ?? ''}` : gap;
407
+ };
408
+ for (const comment of source.getAllComments()) {
409
+ if (comment.range[0] < entry.start || comment.range[1] > entry.node.range[0])
410
+ continue;
411
+ closeGap(comment.range[0]);
412
+ text += source.text.slice(comment.range[0], comment.range[1]);
413
+ cursor = comment.range[1];
414
+ }
415
+ closeGap(entry.node.range[0]);
416
+ text += source.text.slice(entry.node.range[0], entry.end);
417
+ return { blank, text };
418
+ };
392
419
  const lineAt = (index) => source.lines[index] ?? '';
393
420
  const isBlank = (index) => lineAt(index).trim() === '';
394
421
  /**
@@ -457,7 +484,7 @@ const rule = {
457
484
  const boundary = isInternalGroup(entry.group) !== isInternalGroup(previous.group);
458
485
  text += eol.repeat(boundary ? blankLinesBetween + 1 : 1);
459
486
  }
460
- text += source.text.slice(entry.start, entry.end);
487
+ text += tightened(entry).text;
461
488
  previous = entry;
462
489
  }
463
490
  return fixer.replaceTextRange([first.start, last.end], text);
@@ -484,8 +511,19 @@ const rule = {
484
511
  const blank = Math.max((gap.match(BREAK_GLOBAL) ?? []).length - 1, 0);
485
512
  const boundary = isInternalGroup(entry.group) !== isInternalGroup(previous.group);
486
513
  const wanted = boundary ? blankLinesBetween : 0;
514
+ const below = tightened(entry).blank;
515
+ if (below > 0) {
516
+ context.report({
517
+ node: entry.node,
518
+ messageId: 'blankLineBelowComment',
519
+ data: { actual: String(below) },
520
+ fix: rewrite,
521
+ });
522
+ }
487
523
  if (blank === wanted)
488
524
  continue;
525
+ // Every gap, each with the whole-run fix: ESLint applies one, and the rest are gone on
526
+ // the next pass.
489
527
  if (boundary) {
490
528
  context.report({
491
529
  node: entry.node,
@@ -495,9 +533,13 @@ const rule = {
495
533
  });
496
534
  }
497
535
  else {
498
- context.report({ node: entry.node, messageId: 'unexpectedBlankLine', fix: rewrite });
536
+ context.report({
537
+ node: entry.node,
538
+ messageId: 'unexpectedBlankLine',
539
+ data: { actual: String(blank) },
540
+ fix: rewrite,
541
+ });
499
542
  }
500
- return;
501
543
  }
502
544
  };
503
545
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-imports-regulation",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "description": "Order imports: external type-only, external, blank line, internal type-only, internal — with type specifiers last inside each import.",
5
5
  "author": "Robert Sandiford",
6
6
  "type": "module",