eslint-plugin-comment-policy 0.1.1 → 0.3.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/AGENTS.md CHANGED
@@ -1,12 +1,14 @@
1
1
  # AGENTS.md — adopting eslint-plugin-comment-policy
2
2
 
3
- Five ESLint 9 (flat-config) rules enforcing a comment policy, namespace
3
+ Six ESLint 9 (flat-config) rules enforcing a comment policy, namespace
4
4
  `comment-policy/`:
5
5
 
6
6
  - `comment-policy/max-comment-lines` — prose-line cap per comment block
7
7
  (default `max` 4, `anchoredMax` 3).
8
8
  - `comment-policy/no-comment-narrative` — change-narrative / history prose.
9
9
  - `comment-policy/no-comment-code-snippet` — code snippet inside a comment.
10
+ - `comment-policy/no-consecutive-comments` — several stand-alone comments in a
11
+ row (default `max` 1, `types` line+block, `skipBlankLines` true).
10
12
  - `comment-policy/no-decorative-comment` — decorative / section markers.
11
13
  - `comment-policy/no-line-comment` — `//` forbidden, `/* */` required.
12
14
 
@@ -20,8 +22,7 @@ Five ESLint 9 (flat-config) rules enforcing a comment policy, namespace
20
22
  export default [commentPolicy.configs.recommended];
21
23
  ```
22
24
 
23
- `recommended` turns **all five rules on at `error`** with defaults. `sdd`
24
- adds default `protectedPatterns` for spec-driven projects.
25
+ `recommended` turns **all six rules on at `error`** with defaults.
25
26
 
26
27
  ## Rule behavior an agent must know
27
28
 
package/README.md CHANGED
@@ -16,13 +16,18 @@ Namespace: `comment-policy/`. Plugin scope (`meta.name`): `cyberash`.
16
16
  | `comment-policy/max-comment-lines` | a comment block with more prose lines than the cap (lower cap for anchored blocks) | no |
17
17
  | `comment-policy/no-comment-narrative` | change-narrative / history prose (`renamed from`, `previously`, `v1.2`, bare ISO dates, …) | no |
18
18
  | `comment-policy/no-comment-code-snippet` | a code snippet (usage example) inside a comment | yes (only when the block is entirely code) |
19
+ | `comment-policy/no-consecutive-comments` | several stand-alone comments stacked one after another (more than `max`) | no |
19
20
  | `comment-policy/no-decorative-comment` | decorative / section markers (`=====`, `#region`, `===text===`) | yes |
20
21
  | `comment-policy/no-line-comment` | any `//` comment; requires `/* */` | yes (converts and merges runs of `//`) |
21
22
 
22
23
  A **comment block** is a run of consecutive full-line `//` comments separated
23
24
  only by whitespace (no blank line). A **prose line** is a comment line that
24
25
  still has a real word (≥3 letters) after comment markers and protected markers
25
- are stripped, so anchor/marker-only lines do not count toward the cap.
26
+ are stripped, so anchor/marker-only lines do not count toward the cap. A
27
+ **comment run** (used by `no-consecutive-comments`) is a sequence of full-line
28
+ comments of an enabled kind separated only by whitespace; a multi-line `/* */`
29
+ counts as one comment, and code or a comment of a non-enabled kind breaks the
30
+ run.
26
31
 
27
32
  ## Install
28
33
 
@@ -40,15 +45,7 @@ import commentPolicy from "eslint-plugin-comment-policy";
40
45
  export default [commentPolicy.configs.recommended];
41
46
  ```
42
47
 
43
- `recommended` turns all five rules on at `error` with defaults. There is also an
44
- `sdd` config that ships default `protectedPatterns` for spec-driven projects
45
- (anchors `partition:TYPE-NNN`, `@covers`, short ids, milestones):
46
-
47
- ```js
48
- import commentPolicy from "eslint-plugin-comment-policy";
49
-
50
- export default [commentPolicy.configs.sdd];
51
- ```
48
+ `recommended` turns all six rules on at `error` with defaults.
52
49
 
53
50
  Or register the plugin and enable rules explicitly:
54
51
 
@@ -62,6 +59,7 @@ export default [
62
59
  "comment-policy/max-comment-lines": ["error", { max: 4, anchoredMax: 3 }],
63
60
  "comment-policy/no-comment-narrative": "error",
64
61
  "comment-policy/no-comment-code-snippet": "error",
62
+ "comment-policy/no-consecutive-comments": "error",
65
63
  "comment-policy/no-decorative-comment": "error",
66
64
  "comment-policy/no-line-comment": "error",
67
65
  },
@@ -74,10 +72,12 @@ export default [
74
72
  ### `protectedPatterns`
75
73
 
76
74
  Shared across `max-comment-lines`, `no-comment-narrative`,
77
- `no-comment-code-snippet`, `no-decorative-comment`, and `no-line-comment`. An
78
- array of regular-expression **source strings**. A block that matches any pattern
79
- is "protected": it gets the lower cap in `max-comment-lines` and is exempt from
80
- `no-comment-narrative` / `no-comment-code-snippet` / `no-decorative-comment`.
75
+ `no-comment-code-snippet`, `no-consecutive-comments`, `no-decorative-comment`,
76
+ and `no-line-comment`. An array of regular-expression **source strings**. A block
77
+ that matches any pattern is "protected": it gets the lower cap in
78
+ `max-comment-lines`, is exempt from `no-comment-narrative` /
79
+ `no-comment-code-snippet` / `no-decorative-comment`, and is excluded from
80
+ `no-consecutive-comments` runs.
81
81
 
82
82
  Order matters: put the longer/more specific pattern first so a marker is fully
83
83
  stripped before a shorter pattern that is its suffix.
@@ -100,6 +100,21 @@ stripped before a shorter pattern that is its suffix.
100
100
  - `extraPatterns` — extra narrative patterns (source strings) added to the
101
101
  built-in set.
102
102
 
103
+ ### `no-consecutive-comments`
104
+
105
+ ```js
106
+ ["error", { types: ["line", "block"], max: 1, skipBlankLines: true, protectedPatterns: [] }]
107
+ ```
108
+
109
+ - `types` (default `["line", "block"]`) — which comment kinds participate in a
110
+ run: `line` (`//`) and/or `block` (`/* */`). A comment of a kind not listed
111
+ breaks the run.
112
+ - `max` (default `1`) — how many consecutive comments are allowed; a run longer
113
+ than `max` is reported (with `max: 1`, any second comment in a row is flagged).
114
+ - `skipBlankLines` (default `true`) — when `true`, comments separated only by
115
+ blank lines still count as consecutive; set `false` to let a blank line break
116
+ the run.
117
+
103
118
  ### `no-comment-code-snippet`, `no-decorative-comment`, `no-line-comment`
104
119
 
105
120
  ```js
package/README.ru.md CHANGED
@@ -16,13 +16,18 @@
16
16
  | `comment-policy/max-comment-lines` | блок комментария, где прозаических строк больше капа (для анкорных блоков кап ниже) | нет |
17
17
  | `comment-policy/no-comment-narrative` | change-narrative / историю (`renamed from`, `previously`, `v1.2`, голые ISO-даты, …) | нет |
18
18
  | `comment-policy/no-comment-code-snippet` | код-сниппет (пример использования) внутри комментария | да (только если блок целиком код) |
19
+ | `comment-policy/no-consecutive-comments` | несколько отдельных комментариев подряд (больше `max`) | нет |
19
20
  | `comment-policy/no-decorative-comment` | декоративные / секционные маркеры (`=====`, `#region`, `===text===`) | да |
20
21
  | `comment-policy/no-line-comment` | любой `//`; требует `/* */` | да (конвертация и склейка подряд идущих `//`) |
21
22
 
22
23
  **Блок комментария** — подряд идущие full-line `//`, разделённые только
23
24
  пробелами (без пустой строки). **Прозаическая строка** — строка комментария, в
24
25
  которой после снятия маркеров комментария и protected-маркеров остаётся реальное
25
- слово (≥3 букв); поэтому чисто анкорные/маркерные строки в кап не идут.
26
+ слово (≥3 букв); поэтому чисто анкорные/маркерные строки в кап не идут. **Серия
27
+ комментариев** (для `no-consecutive-comments`) — последовательность full-line
28
+ комментариев включённого типа, разделённых только пробелами; многострочный
29
+ `/* */` считается одним комментарием, а код или комментарий невключённого типа
30
+ разрывает серию.
26
31
 
27
32
  ## Установка
28
33
 
@@ -40,15 +45,7 @@ import commentPolicy from "eslint-plugin-comment-policy";
40
45
  export default [commentPolicy.configs.recommended];
41
46
  ```
42
47
 
43
- `recommended` включает все пять правил на `error` с дефолтами. Есть также конфиг
44
- `sdd` с дефолтными `protectedPatterns` для spec-driven проектов (анкоры
45
- `partition:TYPE-NNN`, `@covers`, short-id, milestone):
46
-
47
- ```js
48
- import commentPolicy from "eslint-plugin-comment-policy";
49
-
50
- export default [commentPolicy.configs.sdd];
51
- ```
48
+ `recommended` включает все шесть правил на `error` с дефолтами.
52
49
 
53
50
  Либо подключить плагин и включить правила вручную:
54
51
 
@@ -62,6 +59,7 @@ export default [
62
59
  "comment-policy/max-comment-lines": ["error", { max: 4, anchoredMax: 3 }],
63
60
  "comment-policy/no-comment-narrative": "error",
64
61
  "comment-policy/no-comment-code-snippet": "error",
62
+ "comment-policy/no-consecutive-comments": "error",
65
63
  "comment-policy/no-decorative-comment": "error",
66
64
  "comment-policy/no-line-comment": "error",
67
65
  },
@@ -74,11 +72,12 @@ export default [
74
72
  ### `protectedPatterns`
75
73
 
76
74
  Общая для `max-comment-lines`, `no-comment-narrative`,
77
- `no-comment-code-snippet`, `no-decorative-comment` и `no-line-comment`. Массив
78
- **строк-исходников** регулярных выражений. Блок, совпавший с любым паттерном,
79
- считается «защищённым»: получает пониженный кап в `max-comment-lines` и
80
- исключается из `no-comment-narrative` / `no-comment-code-snippet` /
81
- `no-decorative-comment`.
75
+ `no-comment-code-snippet`, `no-consecutive-comments`, `no-decorative-comment` и
76
+ `no-line-comment`. Массив **строк-исходников** регулярных выражений. Блок,
77
+ совпавший с любым паттерном, считается «защищённым»: получает пониженный кап в
78
+ `max-comment-lines`, исключается из `no-comment-narrative` /
79
+ `no-comment-code-snippet` / `no-decorative-comment` и не учитывается в сериях
80
+ `no-consecutive-comments`.
82
81
 
83
82
  Порядок важен: более длинный/специфичный паттерн ставьте раньше, чтобы маркер
84
83
  снимался целиком до более короткого паттерна, который является его суффиксом.
@@ -101,6 +100,21 @@ export default [
101
100
  - `extraPatterns` — дополнительные narrative-паттерны (строки-исходники) к
102
101
  встроенному набору.
103
102
 
103
+ ### `no-consecutive-comments`
104
+
105
+ ```js
106
+ ["error", { types: ["line", "block"], max: 1, skipBlankLines: true, protectedPatterns: [] }]
107
+ ```
108
+
109
+ - `types` (деф. `["line", "block"]`) — какие типы комментариев участвуют в серии:
110
+ `line` (`//`) и/или `block` (`/* */`). Комментарий типа не из списка разрывает
111
+ серию.
112
+ - `max` (деф. `1`) — сколько комментариев подряд допустимо; серия длиннее `max`
113
+ репортится (при `max: 1` ловится любой второй комментарий подряд).
114
+ - `skipBlankLines` (деф. `true`) — при `true` комментарии, разделённые только
115
+ пустыми строками, всё равно считаются подряд идущими; `false` — пустая строка
116
+ разрывает серию.
117
+
104
118
  ### `no-comment-code-snippet`, `no-decorative-comment`, `no-line-comment`
105
119
 
106
120
  ```js
package/dist/index.cjs CHANGED
@@ -86,9 +86,9 @@ function blockLoc(block) {
86
86
  }
87
87
  //#endregion
88
88
  //#region src/rules/max-comment-lines.ts
89
- const DEFAULT_MAX = 4;
89
+ const DEFAULT_MAX$1 = 4;
90
90
  const DEFAULT_ANCHORED_MAX = 3;
91
- const rule$4 = {
91
+ const rule$5 = {
92
92
  defaultOptions: [{}],
93
93
  meta: {
94
94
  type: "suggestion",
@@ -121,7 +121,7 @@ const rule$4 = {
121
121
  },
122
122
  create(context) {
123
123
  const option = context.options[0] ?? {};
124
- const max = option.max ?? DEFAULT_MAX;
124
+ const max = option.max ?? DEFAULT_MAX$1;
125
125
  const anchoredMax = option.anchoredMax ?? DEFAULT_ANCHORED_MAX;
126
126
  const { detect, strip } = compileProtected(option.protectedPatterns ?? []);
127
127
  const sourceCode = context.sourceCode;
@@ -165,7 +165,7 @@ function snippetInfo(block) {
165
165
  pure: nonEmpty.length >= 2 && codeish.length === nonEmpty.length
166
166
  };
167
167
  }
168
- const rule$3 = {
168
+ const rule$4 = {
169
169
  defaultOptions: [{}],
170
170
  meta: {
171
171
  type: "suggestion",
@@ -216,7 +216,7 @@ const NARRATIVE = [
216
216
  /\bv\d+\.\d+\b/i,
217
217
  /\b\d{4}-\d{2}-\d{2}\b/
218
218
  ];
219
- const rule$2 = {
219
+ const rule$3 = {
220
220
  defaultOptions: [{}],
221
221
  meta: {
222
222
  type: "suggestion",
@@ -258,6 +258,105 @@ const rule$2 = {
258
258
  }
259
259
  };
260
260
  //#endregion
261
+ //#region src/lib/comment-runs.ts
262
+ function commentRuns(sourceCode, options) {
263
+ const text = sourceCode.text;
264
+ const isFullLine = (c) => {
265
+ const lineText = sourceCode.lines[c.loc.start.line - 1] ?? "";
266
+ return /^\s*$/.test(lineText.slice(0, c.loc.start.column));
267
+ };
268
+ const isCounted = (c) => {
269
+ const kind = c.type === "Line" ? "line" : "block";
270
+ return options.types.has(kind) && isFullLine(c) && !hasProtectedToken(sourceCode.getText(c), options.detect);
271
+ };
272
+ const isAdjacent = (prev, curr) => {
273
+ const between = text.slice(prev.range[1], curr.range[0]);
274
+ return options.skipBlankLines ? /^\s*$/.test(between) : onlyWhitespaceNoBlank(between);
275
+ };
276
+ const runs = [];
277
+ let current = [];
278
+ const flush = () => {
279
+ if (current.length === 0) return;
280
+ runs.push({
281
+ comments: current,
282
+ loc: {
283
+ start: current[0].loc.start,
284
+ end: current[current.length - 1].loc.end
285
+ }
286
+ });
287
+ current = [];
288
+ };
289
+ for (const c of sourceCode.getAllComments()) {
290
+ if (!isCounted(c)) {
291
+ flush();
292
+ continue;
293
+ }
294
+ if (current.length > 0 && !isAdjacent(current[current.length - 1], c)) flush();
295
+ current.push(c);
296
+ }
297
+ flush();
298
+ return runs;
299
+ }
300
+ //#endregion
301
+ //#region src/rules/no-consecutive-comments.ts
302
+ const DEFAULT_MAX = 1;
303
+ const DEFAULT_TYPES = ["line", "block"];
304
+ const rule$2 = {
305
+ defaultOptions: [{}],
306
+ meta: {
307
+ type: "suggestion",
308
+ docs: {
309
+ description: "Forbid several stand-alone comments stacked one after another; merge or remove the extras.",
310
+ url: "https://github.com/cyberash-dev/eslint-plugin-comment-policy#no-consecutive-comments"
311
+ },
312
+ schema: [{
313
+ type: "object",
314
+ properties: {
315
+ types: {
316
+ type: "array",
317
+ items: {
318
+ type: "string",
319
+ enum: ["line", "block"]
320
+ }
321
+ },
322
+ max: {
323
+ type: "integer",
324
+ minimum: 1
325
+ },
326
+ skipBlankLines: { type: "boolean" },
327
+ protectedPatterns: {
328
+ type: "array",
329
+ items: { type: "string" }
330
+ }
331
+ },
332
+ additionalProperties: false
333
+ }],
334
+ messages: { consecutiveComments: "{{count}} consecutive comments (> {{max}}); merge them into one or remove the extras" }
335
+ },
336
+ create(context) {
337
+ const option = context.options[0] ?? {};
338
+ const max = option.max ?? DEFAULT_MAX;
339
+ const skipBlankLines = option.skipBlankLines ?? true;
340
+ const types = new Set(option.types ?? DEFAULT_TYPES);
341
+ const { detect } = compileProtected(option.protectedPatterns ?? []);
342
+ const sourceCode = context.sourceCode;
343
+ return { "Program:exit"() {
344
+ for (const run of commentRuns(sourceCode, {
345
+ types,
346
+ skipBlankLines,
347
+ detect
348
+ })) if (run.comments.length > max) context.report({
349
+ loc: run.loc,
350
+ messageId: "consecutiveComments",
351
+ data: {
352
+ count: run.comments.length,
353
+ max
354
+ }
355
+ });
356
+ } };
357
+ }
358
+ };
359
+ //#endregion
261
360
  //#region src/lib/decorative.ts
262
361
  const DECORATIVE = [
263
362
  /^[=*#_-]{3,}$/,
@@ -389,26 +488,18 @@ const rule = {
389
488
  }
390
489
  };
391
490
  //#endregion
392
- //#region src/lib/sdd-patterns.ts
393
- const TYPES = "BL|SUR|CON|INV|POL|DEL|DLT|NFR|REQ|MIG|CST|SCN|LCN|GAR|EXT";
394
- const SDD_PROTECTED_PATTERNS = [
395
- "@covers\\s+\\S+(?:\\s+\\w+=\\S+)*",
396
- `\\b[a-z][a-z0-9]*(?:-[a-z0-9]+)*(?::[a-z0-9]+(?:-[a-z0-9]+)*)*:(?:${TYPES})-\\d+\\b`,
397
- `\\b(?:${TYPES})-\\d+\\b`,
398
- "\\bM\\d+[A-Z]+-\\d+\\b"
399
- ];
400
- //#endregion
401
491
  //#region src/index.ts
402
492
  const configs = {};
403
493
  const plugin = {
404
494
  meta: {
405
495
  name: "cyberash",
406
- version: "0.1.1"
496
+ version: "0.3.0"
407
497
  },
408
498
  rules: {
409
- "max-comment-lines": rule$4,
410
- "no-comment-narrative": rule$2,
411
- "no-comment-code-snippet": rule$3,
499
+ "max-comment-lines": rule$5,
500
+ "no-comment-narrative": rule$3,
501
+ "no-comment-code-snippet": rule$4,
502
+ "no-consecutive-comments": rule$2,
412
503
  "no-decorative-comment": rule$1,
413
504
  "no-line-comment": rule
414
505
  },
@@ -423,24 +514,10 @@ configs.recommended = {
423
514
  }],
424
515
  "comment-policy/no-comment-narrative": ["error"],
425
516
  "comment-policy/no-comment-code-snippet": ["error"],
517
+ "comment-policy/no-consecutive-comments": ["error"],
426
518
  "comment-policy/no-decorative-comment": ["error"],
427
519
  "comment-policy/no-line-comment": ["error"]
428
520
  }
429
521
  };
430
- const sddProtected = [...SDD_PROTECTED_PATTERNS];
431
- configs.sdd = {
432
- plugins: { "comment-policy": plugin },
433
- rules: {
434
- "comment-policy/max-comment-lines": ["error", {
435
- max: 4,
436
- anchoredMax: 3,
437
- protectedPatterns: sddProtected
438
- }],
439
- "comment-policy/no-comment-narrative": ["error", { protectedPatterns: sddProtected }],
440
- "comment-policy/no-comment-code-snippet": ["error", { protectedPatterns: sddProtected }],
441
- "comment-policy/no-decorative-comment": ["error", { protectedPatterns: sddProtected }],
442
- "comment-policy/no-line-comment": ["error", { protectedPatterns: sddProtected }]
443
- }
444
- };
445
522
  //#endregion
446
523
  module.exports = plugin;
package/dist/index.mjs CHANGED
@@ -86,9 +86,9 @@ function blockLoc(block) {
86
86
  }
87
87
  //#endregion
88
88
  //#region src/rules/max-comment-lines.ts
89
- const DEFAULT_MAX = 4;
89
+ const DEFAULT_MAX$1 = 4;
90
90
  const DEFAULT_ANCHORED_MAX = 3;
91
- const rule$4 = {
91
+ const rule$5 = {
92
92
  defaultOptions: [{}],
93
93
  meta: {
94
94
  type: "suggestion",
@@ -121,7 +121,7 @@ const rule$4 = {
121
121
  },
122
122
  create(context) {
123
123
  const option = context.options[0] ?? {};
124
- const max = option.max ?? DEFAULT_MAX;
124
+ const max = option.max ?? DEFAULT_MAX$1;
125
125
  const anchoredMax = option.anchoredMax ?? DEFAULT_ANCHORED_MAX;
126
126
  const { detect, strip } = compileProtected(option.protectedPatterns ?? []);
127
127
  const sourceCode = context.sourceCode;
@@ -165,7 +165,7 @@ function snippetInfo(block) {
165
165
  pure: nonEmpty.length >= 2 && codeish.length === nonEmpty.length
166
166
  };
167
167
  }
168
- const rule$3 = {
168
+ const rule$4 = {
169
169
  defaultOptions: [{}],
170
170
  meta: {
171
171
  type: "suggestion",
@@ -216,7 +216,7 @@ const NARRATIVE = [
216
216
  /\bv\d+\.\d+\b/i,
217
217
  /\b\d{4}-\d{2}-\d{2}\b/
218
218
  ];
219
- const rule$2 = {
219
+ const rule$3 = {
220
220
  defaultOptions: [{}],
221
221
  meta: {
222
222
  type: "suggestion",
@@ -258,6 +258,105 @@ const rule$2 = {
258
258
  }
259
259
  };
260
260
  //#endregion
261
+ //#region src/lib/comment-runs.ts
262
+ function commentRuns(sourceCode, options) {
263
+ const text = sourceCode.text;
264
+ const isFullLine = (c) => {
265
+ const lineText = sourceCode.lines[c.loc.start.line - 1] ?? "";
266
+ return /^\s*$/.test(lineText.slice(0, c.loc.start.column));
267
+ };
268
+ const isCounted = (c) => {
269
+ const kind = c.type === "Line" ? "line" : "block";
270
+ return options.types.has(kind) && isFullLine(c) && !hasProtectedToken(sourceCode.getText(c), options.detect);
271
+ };
272
+ const isAdjacent = (prev, curr) => {
273
+ const between = text.slice(prev.range[1], curr.range[0]);
274
+ return options.skipBlankLines ? /^\s*$/.test(between) : onlyWhitespaceNoBlank(between);
275
+ };
276
+ const runs = [];
277
+ let current = [];
278
+ const flush = () => {
279
+ if (current.length === 0) return;
280
+ runs.push({
281
+ comments: current,
282
+ loc: {
283
+ start: current[0].loc.start,
284
+ end: current[current.length - 1].loc.end
285
+ }
286
+ });
287
+ current = [];
288
+ };
289
+ for (const c of sourceCode.getAllComments()) {
290
+ if (!isCounted(c)) {
291
+ flush();
292
+ continue;
293
+ }
294
+ if (current.length > 0 && !isAdjacent(current[current.length - 1], c)) flush();
295
+ current.push(c);
296
+ }
297
+ flush();
298
+ return runs;
299
+ }
300
+ //#endregion
301
+ //#region src/rules/no-consecutive-comments.ts
302
+ const DEFAULT_MAX = 1;
303
+ const DEFAULT_TYPES = ["line", "block"];
304
+ const rule$2 = {
305
+ defaultOptions: [{}],
306
+ meta: {
307
+ type: "suggestion",
308
+ docs: {
309
+ description: "Forbid several stand-alone comments stacked one after another; merge or remove the extras.",
310
+ url: "https://github.com/cyberash-dev/eslint-plugin-comment-policy#no-consecutive-comments"
311
+ },
312
+ schema: [{
313
+ type: "object",
314
+ properties: {
315
+ types: {
316
+ type: "array",
317
+ items: {
318
+ type: "string",
319
+ enum: ["line", "block"]
320
+ }
321
+ },
322
+ max: {
323
+ type: "integer",
324
+ minimum: 1
325
+ },
326
+ skipBlankLines: { type: "boolean" },
327
+ protectedPatterns: {
328
+ type: "array",
329
+ items: { type: "string" }
330
+ }
331
+ },
332
+ additionalProperties: false
333
+ }],
334
+ messages: { consecutiveComments: "{{count}} consecutive comments (> {{max}}); merge them into one or remove the extras" }
335
+ },
336
+ create(context) {
337
+ const option = context.options[0] ?? {};
338
+ const max = option.max ?? DEFAULT_MAX;
339
+ const skipBlankLines = option.skipBlankLines ?? true;
340
+ const types = new Set(option.types ?? DEFAULT_TYPES);
341
+ const { detect } = compileProtected(option.protectedPatterns ?? []);
342
+ const sourceCode = context.sourceCode;
343
+ return { "Program:exit"() {
344
+ for (const run of commentRuns(sourceCode, {
345
+ types,
346
+ skipBlankLines,
347
+ detect
348
+ })) if (run.comments.length > max) context.report({
349
+ loc: run.loc,
350
+ messageId: "consecutiveComments",
351
+ data: {
352
+ count: run.comments.length,
353
+ max
354
+ }
355
+ });
356
+ } };
357
+ }
358
+ };
359
+ //#endregion
261
360
  //#region src/lib/decorative.ts
262
361
  const DECORATIVE = [
263
362
  /^[=*#_-]{3,}$/,
@@ -389,26 +488,18 @@ const rule = {
389
488
  }
390
489
  };
391
490
  //#endregion
392
- //#region src/lib/sdd-patterns.ts
393
- const TYPES = "BL|SUR|CON|INV|POL|DEL|DLT|NFR|REQ|MIG|CST|SCN|LCN|GAR|EXT";
394
- const SDD_PROTECTED_PATTERNS = [
395
- "@covers\\s+\\S+(?:\\s+\\w+=\\S+)*",
396
- `\\b[a-z][a-z0-9]*(?:-[a-z0-9]+)*(?::[a-z0-9]+(?:-[a-z0-9]+)*)*:(?:${TYPES})-\\d+\\b`,
397
- `\\b(?:${TYPES})-\\d+\\b`,
398
- "\\bM\\d+[A-Z]+-\\d+\\b"
399
- ];
400
- //#endregion
401
491
  //#region src/index.ts
402
492
  const configs = {};
403
493
  const plugin = {
404
494
  meta: {
405
495
  name: "cyberash",
406
- version: "0.1.1"
496
+ version: "0.3.0"
407
497
  },
408
498
  rules: {
409
- "max-comment-lines": rule$4,
410
- "no-comment-narrative": rule$2,
411
- "no-comment-code-snippet": rule$3,
499
+ "max-comment-lines": rule$5,
500
+ "no-comment-narrative": rule$3,
501
+ "no-comment-code-snippet": rule$4,
502
+ "no-consecutive-comments": rule$2,
412
503
  "no-decorative-comment": rule$1,
413
504
  "no-line-comment": rule
414
505
  },
@@ -423,24 +514,10 @@ configs.recommended = {
423
514
  }],
424
515
  "comment-policy/no-comment-narrative": ["error"],
425
516
  "comment-policy/no-comment-code-snippet": ["error"],
517
+ "comment-policy/no-consecutive-comments": ["error"],
426
518
  "comment-policy/no-decorative-comment": ["error"],
427
519
  "comment-policy/no-line-comment": ["error"]
428
520
  }
429
521
  };
430
- const sddProtected = [...SDD_PROTECTED_PATTERNS];
431
- configs.sdd = {
432
- plugins: { "comment-policy": plugin },
433
- rules: {
434
- "comment-policy/max-comment-lines": ["error", {
435
- max: 4,
436
- anchoredMax: 3,
437
- protectedPatterns: sddProtected
438
- }],
439
- "comment-policy/no-comment-narrative": ["error", { protectedPatterns: sddProtected }],
440
- "comment-policy/no-comment-code-snippet": ["error", { protectedPatterns: sddProtected }],
441
- "comment-policy/no-decorative-comment": ["error", { protectedPatterns: sddProtected }],
442
- "comment-policy/no-line-comment": ["error", { protectedPatterns: sddProtected }]
443
- }
444
- };
445
522
  //#endregion
446
523
  export { plugin as default };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-comment-policy",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "ESLint rules enforcing a per-file comment policy: prose-line caps, no change-narrative, no code snippets, no decorative markers, block comments only (JS + TS).",
5
5
  "type": "module",
6
6
  "license": "MIT",