eslint-plugin-traceability 1.21.1 → 1.22.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.
Files changed (57) hide show
  1. package/CHANGELOG.md +7 -2
  2. package/README.md +3 -4
  3. package/lib/src/maintenance/batch.js +0 -2
  4. package/lib/src/maintenance/cli.js +8 -11
  5. package/lib/src/maintenance/commands.d.ts +2 -2
  6. package/lib/src/maintenance/commands.js +2 -3
  7. package/lib/src/maintenance/detect.js +7 -8
  8. package/lib/src/maintenance/report.js +2 -3
  9. package/lib/src/maintenance/storyParser.d.ts +16 -0
  10. package/lib/src/maintenance/storyParser.js +167 -0
  11. package/lib/src/maintenance/update.js +0 -1
  12. package/lib/src/rules/helpers/pattern-validators.d.ts +42 -0
  13. package/lib/src/rules/helpers/pattern-validators.js +65 -0
  14. package/lib/src/rules/helpers/prefer-implements-inline.d.ts +16 -0
  15. package/lib/src/rules/helpers/prefer-implements-inline.js +146 -0
  16. package/lib/src/rules/helpers/require-story-comment-detection.d.ts +47 -0
  17. package/lib/src/rules/helpers/require-story-comment-detection.js +141 -0
  18. package/lib/src/rules/helpers/require-story-core.d.ts +6 -6
  19. package/lib/src/rules/helpers/require-story-core.js +10 -11
  20. package/lib/src/rules/helpers/require-story-helpers.d.ts +5 -63
  21. package/lib/src/rules/helpers/require-story-helpers.js +29 -337
  22. package/lib/src/rules/helpers/require-story-name-extraction.d.ts +35 -0
  23. package/lib/src/rules/helpers/require-story-name-extraction.js +107 -0
  24. package/lib/src/rules/helpers/require-story-node-utils.d.ts +43 -0
  25. package/lib/src/rules/helpers/require-story-node-utils.js +115 -0
  26. package/lib/src/rules/helpers/valid-annotation-format-internal.js +11 -3
  27. package/lib/src/rules/helpers/valid-annotation-options.d.ts +0 -10
  28. package/lib/src/rules/helpers/valid-annotation-options.js +22 -92
  29. package/lib/src/rules/helpers/valid-req-reference-helpers.js +0 -1
  30. package/lib/src/rules/no-redundant-annotation.js +4 -238
  31. package/lib/src/rules/prefer-implements-annotation.d.ts +12 -0
  32. package/lib/src/rules/prefer-implements-annotation.js +9 -164
  33. package/lib/src/rules/require-traceability.d.ts +8 -0
  34. package/lib/src/rules/require-traceability.js +8 -0
  35. package/lib/src/utils/annotation-checker.d.ts +3 -2
  36. package/lib/src/utils/annotation-checker.js +3 -2
  37. package/lib/src/utils/branch-annotation-catch-helpers.d.ts +22 -0
  38. package/lib/src/utils/branch-annotation-catch-helpers.js +70 -0
  39. package/lib/src/utils/branch-annotation-helpers.js +11 -187
  40. package/lib/src/utils/branch-annotation-if-helpers.d.ts +1 -0
  41. package/lib/src/utils/branch-annotation-if-helpers.js +59 -0
  42. package/lib/src/utils/branch-annotation-indent-helpers.d.ts +1 -1
  43. package/lib/src/utils/branch-annotation-switch-helpers.d.ts +8 -2
  44. package/lib/src/utils/branch-annotation-switch-helpers.js +10 -4
  45. package/lib/src/utils/branch-validation.d.ts +9 -0
  46. package/lib/src/utils/branch-validation.js +58 -0
  47. package/lib/src/utils/comment-text-helpers.d.ts +31 -0
  48. package/lib/src/utils/comment-text-helpers.js +54 -0
  49. package/lib/src/utils/redundancy-detector.d.ts +85 -0
  50. package/lib/src/utils/redundancy-detector.js +235 -0
  51. package/lib/tests/maintenance/storyParser.test.d.ts +8 -0
  52. package/lib/tests/maintenance/storyParser.test.js +505 -0
  53. package/lib/tests/rules/no-redundant-annotation.test.js +1 -0
  54. package/lib/tests/rules/require-story-helpers.test.js +3 -2
  55. package/lib/tests/rules/valid-req-reference.test.js +2 -0
  56. package/package.json +18 -10
  57. package/user-docs/api-reference.md +2 -2
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const valid_annotation_format_internal_1 = require("./helpers/valid-annotation-format-internal");
4
+ const prefer_implements_inline_1 = require("./helpers/prefer-implements-inline");
4
5
  // Maximum number of distinct @story paths allowed before treating as "multi-story".
5
6
  // @req REQ-MULTI-STORY-DETECT - Centralized threshold constant for detecting multi-story patterns
6
7
  const MULTI_STORY_THRESHOLD = 1;
@@ -223,173 +224,17 @@ function processBlockComment(comment, context) {
223
224
  });
224
225
  }
225
226
  /**
226
- * Extract the leading whitespace and `//` prefix from a line comment's full
227
- * source text so that new inline annotations can be inserted with matching
228
- * indentation and formatting.
229
- *
230
- * @story docs/stories/010.3-DEV-MIGRATE-TO-SUPPORTS.story.md
231
- * @req REQ-MIGRATE-INLINE
232
- */
233
- function getLinePrefixFromText(fullText) {
234
- const match = fullText.match(/^(\s*\/\/\s*)/);
235
- return match ? match[1] : "";
236
- }
237
- /**
238
- * Attempt to construct an inline auto-fix that replaces a contiguous
239
- * sequence of `@story` and `@req` line comments with a single `@supports`
240
- * annotation while preserving the original comment prefix.
241
- *
242
- * @story docs/stories/010.3-DEV-MIGRATE-TO-SUPPORTS.story.md
243
- * @req REQ-MIGRATE-INLINE
244
- */
245
- function tryBuildInlineAutoFix(context, comments, storyIndex, reqIndices) {
246
- const sourceCode = context.getSourceCode();
247
- const storyComment = comments[storyIndex];
248
- const storyNormalized = (0, valid_annotation_format_internal_1.normalizeCommentLine)(storyComment.value || "");
249
- if (!storyNormalized || !/^@story\b/.test(storyNormalized)) {
250
- return null;
251
- }
252
- const storyParts = storyNormalized.split(/\s+/);
253
- if (storyParts.length !== MIN_STORY_TOKENS) {
254
- return null;
255
- }
256
- const storyPath = storyParts[1];
257
- const reqIds = [];
258
- for (const idx of reqIndices) {
259
- const reqComment = comments[idx];
260
- const reqNormalized = (0, valid_annotation_format_internal_1.normalizeCommentLine)(reqComment.value || "");
261
- if (!reqNormalized || !/^@req\b/.test(reqNormalized)) {
262
- return null;
263
- }
264
- const reqParts = reqNormalized.split(/\s+/);
265
- if (reqParts.length !== MIN_REQ_TOKENS) {
266
- return null;
267
- }
268
- reqIds.push(reqParts[1]);
269
- }
270
- if (!reqIds.length) {
271
- return null;
272
- }
273
- const fullText = sourceCode.text.slice(storyComment.range[0], storyComment.range[1]);
274
- const linePrefix = getLinePrefixFromText(fullText);
275
- const implAnnotation = `@supports ${storyPath} ${reqIds.join(" ")}`;
276
- const implLine = `${linePrefix}${implAnnotation}`;
277
- const start = storyComment.range[0];
278
- const end = comments[reqIndices[reqIndices.length - 1]].range[1];
279
- return (fixer) => fixer.replaceTextRange([start, end], implLine);
280
- }
281
- /**
282
- * Coordinate detection and optional migration of a single inline `@story`
283
- * comment and its following `@req` comments, reporting diagnostics and
284
- * scheduling auto-fixes where safe.
227
+ * ESLint rule: prefer-implements-annotation
285
228
  *
286
- * @story docs/stories/010.3-DEV-MIGRATE-TO-SUPPORTS.story.md
287
- * @req REQ-MIGRATE-INLINE
288
- */
289
- function collectReqIndicesAfterStory(group, startIndex) {
290
- const n = group.length;
291
- const reqIndices = [];
292
- let j = startIndex + 1;
293
- while (j < n) {
294
- const next = group[j];
295
- const nextNormalized = (0, valid_annotation_format_internal_1.normalizeCommentLine)(next.value || "");
296
- if (!nextNormalized || /^@supports\b/.test(nextNormalized)) {
297
- break;
298
- }
299
- if (/^@req\b/.test(nextNormalized)) {
300
- reqIndices.push(j);
301
- j += 1;
302
- continue;
303
- }
304
- break;
305
- }
306
- return { reqIndices, nextIndex: j };
307
- }
308
- function handleInlineStorySequence(context, group, startIndex) {
309
- const current = group[startIndex];
310
- const normalized = (0, valid_annotation_format_internal_1.normalizeCommentLine)(current.value || "");
311
- if (!normalized || !/^@story\b/.test(normalized)) {
312
- return startIndex + 1;
313
- }
314
- if (/^@supports\b/.test(normalized)) {
315
- return startIndex + 1;
316
- }
317
- const storyIndex = startIndex;
318
- const { reqIndices, nextIndex } = collectReqIndicesAfterStory(group, startIndex);
319
- if (reqIndices.length === 0) {
320
- context.report({
321
- node: current,
322
- messageId: "preferImplements",
323
- });
324
- return startIndex + 1;
325
- }
326
- const fix = tryBuildInlineAutoFix(context, group, storyIndex, reqIndices);
327
- if (fix) {
328
- context.report({
329
- node: current,
330
- messageId: "preferImplements",
331
- fix,
332
- });
333
- }
334
- else {
335
- context.report({
336
- node: current,
337
- messageId: "preferImplements",
338
- });
339
- }
340
- return nextIndex;
341
- }
342
- /**
343
- * Process a contiguous group of inline line comments, identifying legacy
344
- * `@story`/`@req` sequences and scheduling the corresponding diagnostics
345
- * and potential auto-fixes for migration to `@supports`.
229
+ * Recommend migrating from legacy `@story` + `@req` annotations to the
230
+ * newer `@supports` format. This rule is **disabled by default** and
231
+ * is intended as an optional, opt-in migration aid.
346
232
  *
347
233
  * @story docs/stories/010.3-DEV-MIGRATE-TO-SUPPORTS.story.md
348
- * @req REQ-MIGRATE-INLINE
349
- */
350
- function advanceInlineGroupIndex(context, group, currentIndex) {
351
- const current = group[currentIndex];
352
- const normalized = (0, valid_annotation_format_internal_1.normalizeCommentLine)(current.value || "");
353
- if (!normalized || !/^@story\b/.test(normalized)) {
354
- return currentIndex + 1;
355
- }
356
- return handleInlineStorySequence(context, group, currentIndex);
357
- }
358
- function processInlineGroup(context, group) {
359
- if (group.length === 0)
360
- return;
361
- let i = 0;
362
- while (i < group.length) {
363
- i = advanceInlineGroupIndex(context, group, i);
364
- }
365
- }
366
- /**
367
- * Scan sequences of Line comments for inline legacy @story/@req patterns and
368
- * report diagnostics and optional auto-fixes.
234
+ * @req REQ-OPTIONAL-WARNING - Emit configurable recommendation diagnostics for legacy @story/@req usage
235
+ * @req REQ-MULTI-STORY-DETECT - Detect multi-story patterns that cannot be auto-fixed
236
+ * @req REQ-BACKWARD-COMP-VALIDATION - Keep legacy @story/@req annotations valid when the rule is disabled
369
237
  */
370
- function processInlineComments(context, lineComments) {
371
- if (!lineComments.length)
372
- return;
373
- // Group by contiguous line numbers
374
- let group = [lineComments[0]];
375
- const flushGroup = () => {
376
- processInlineGroup(context, group);
377
- group = [];
378
- };
379
- for (let idx = 1; idx < lineComments.length; idx++) {
380
- const prev = lineComments[idx - 1];
381
- const curr = lineComments[idx];
382
- if (curr.loc.start.line === prev.loc.start.line + 1 &&
383
- curr.loc.start.column === prev.loc.start.column) {
384
- group.push(curr);
385
- }
386
- else {
387
- flushGroup();
388
- group.push(curr);
389
- }
390
- }
391
- flushGroup();
392
- }
393
238
  /**
394
239
  * ESLint rule: prefer-implements-annotation
395
240
  *
@@ -472,7 +317,7 @@ const preferImplementsAnnotationRule = {
472
317
  processBlockComment(comment, context);
473
318
  });
474
319
  const lineComments = comments.filter((comment) => comment.type === "Line");
475
- processInlineComments(context, lineComments);
320
+ (0, prefer_implements_inline_1.processInlineComments)(context, lineComments);
476
321
  },
477
322
  };
478
323
  },
@@ -2,6 +2,14 @@
2
2
  * Composite ESLint rule that enforces both story and requirement traceability
3
3
  * annotations on functions and methods.
4
4
  *
5
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
6
+ * @req REQ-ANNOTATION-REQUIRED - Require both @story and @req annotations via composition
7
+ * @req REQ-FUNCTION-DETECTION - Detect functions via composed rules
8
+ * @req REQ-CONFIGURABLE-SCOPE - Support scope configuration through underlying rules
9
+ * @req REQ-EXPORT-PRIORITY - Support export priority through underlying rules
10
+ * @req REQ-ERROR-LOCATION - Report errors at function locations via composed rules
11
+ * @req REQ-TYPESCRIPT-SUPPORT - Support TypeScript syntax via composed rules
12
+ *
5
13
  * Implements Story 003.0-DEV-FUNCTION-ANNOTATIONS with:
6
14
  * - REQ-ANNOTATION-REQUIRED
7
15
  * - REQ-FUNCTION-DETECTION
@@ -3,6 +3,14 @@
3
3
  * Composite ESLint rule that enforces both story and requirement traceability
4
4
  * annotations on functions and methods.
5
5
  *
6
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
7
+ * @req REQ-ANNOTATION-REQUIRED - Require both @story and @req annotations via composition
8
+ * @req REQ-FUNCTION-DETECTION - Detect functions via composed rules
9
+ * @req REQ-CONFIGURABLE-SCOPE - Support scope configuration through underlying rules
10
+ * @req REQ-EXPORT-PRIORITY - Support export priority through underlying rules
11
+ * @req REQ-ERROR-LOCATION - Report errors at function locations via composed rules
12
+ * @req REQ-TYPESCRIPT-SUPPORT - Support TypeScript syntax via composed rules
13
+ *
6
14
  * Implements Story 003.0-DEV-FUNCTION-ANNOTATIONS with:
7
15
  * - REQ-ANNOTATION-REQUIRED
8
16
  * - REQ-FUNCTION-DETECTION
@@ -1,15 +1,16 @@
1
1
  /**
2
2
  * Helper to check @req annotation presence on TS declare functions and method signatures.
3
+ *
3
4
  * This helper is intentionally scope/exportPriority agnostic and focuses solely
4
5
  * on detection and reporting of @req annotations for the given node.
6
+ *
5
7
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
6
8
  * @req REQ-TYPESCRIPT-SUPPORT - Support TypeScript-specific function syntax
7
9
  * @req REQ-ANNOTATION-REQ-DETECTION - Determine presence of @req annotation
8
10
  * @req REQ-ANNOTATION-REPORTING - Report missing @req annotation to context
9
11
  * @param context - ESLint rule context used to obtain source and report problems
10
12
  * @param node - Function-like AST node whose surrounding comments should be inspected
11
- * @param options - Optional configuration controlling behaviour (e.g., enableFix)
12
- * @returns void
13
+ * @param options - Optional configuration controlling behaviour (e.g., enableFix, annotationPlacement)
13
14
  */
14
15
  export declare function checkReqAnnotation(context: any, node: any, options?: {
15
16
  enableFix?: boolean;
@@ -169,16 +169,17 @@ function reportMissing(context, node, enableFix = true) {
169
169
  }
170
170
  /**
171
171
  * Helper to check @req annotation presence on TS declare functions and method signatures.
172
+ *
172
173
  * This helper is intentionally scope/exportPriority agnostic and focuses solely
173
174
  * on detection and reporting of @req annotations for the given node.
175
+ *
174
176
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
175
177
  * @req REQ-TYPESCRIPT-SUPPORT - Support TypeScript-specific function syntax
176
178
  * @req REQ-ANNOTATION-REQ-DETECTION - Determine presence of @req annotation
177
179
  * @req REQ-ANNOTATION-REPORTING - Report missing @req annotation to context
178
180
  * @param context - ESLint rule context used to obtain source and report problems
179
181
  * @param node - Function-like AST node whose surrounding comments should be inspected
180
- * @param options - Optional configuration controlling behaviour (e.g., enableFix)
181
- * @returns void
182
+ * @param options - Optional configuration controlling behaviour (e.g., enableFix, annotationPlacement)
182
183
  */
183
184
  function checkReqAnnotation(context, node, options) {
184
185
  const { enableFix = true } = options ?? {};
@@ -0,0 +1,22 @@
1
+ import type { Rule } from "eslint";
2
+ /**
3
+ * Gather comment text from inside a CatchClause body.
4
+ *
5
+ * Uses dual-position detection strategy: first attempts getCommentsInside API,
6
+ * then falls back to line-based scanning if API is unavailable or returns empty.
7
+ *
8
+ * @story docs/stories/025.0-DEV-CATCH-ANNOTATION-POSITION.story.md
9
+ * @req REQ-DUAL-POSITION-DETECTION REQ-FALLBACK-LOGIC
10
+ * @param sourceCode - ESLint source code object providing comment access APIs
11
+ * @param node - CatchClause AST node whose body will be scanned for comments
12
+ * @returns Concatenated comment text from inside the catch body, or empty string if none found
13
+ */
14
+ export declare function getInsideCatchCommentText(sourceCode: ReturnType<Rule.RuleContext["getSourceCode"]>, node: any): string;
15
+ /**
16
+ * Gather comment text from the first contiguous comment lines inside a TryStatement block body.
17
+ * @supports docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md REQ-INSIDE-BRACE-PLACEMENT
18
+ * @param sourceCode - ESLint source code object providing line access
19
+ * @param node - TryStatement AST node whose block will be scanned for comments
20
+ * @returns Concatenated comment text from inside the try block, or empty string if none found
21
+ */
22
+ export declare function getInsideTryBlockCommentText(sourceCode: ReturnType<Rule.RuleContext["getSourceCode"]>, node: any): string;
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getInsideCatchCommentText = getInsideCatchCommentText;
4
+ exports.getInsideTryBlockCommentText = getInsideTryBlockCommentText;
5
+ const branch_annotation_helpers_1 = require("./branch-annotation-helpers");
6
+ const comment_text_helpers_1 = require("./comment-text-helpers");
7
+ /**
8
+ * Gather comment text from inside a CatchClause body.
9
+ *
10
+ * Uses dual-position detection strategy: first attempts getCommentsInside API,
11
+ * then falls back to line-based scanning if API is unavailable or returns empty.
12
+ *
13
+ * @story docs/stories/025.0-DEV-CATCH-ANNOTATION-POSITION.story.md
14
+ * @req REQ-DUAL-POSITION-DETECTION REQ-FALLBACK-LOGIC
15
+ * @param sourceCode - ESLint source code object providing comment access APIs
16
+ * @param node - CatchClause AST node whose body will be scanned for comments
17
+ * @returns Concatenated comment text from inside the catch body, or empty string if none found
18
+ */
19
+ function getInsideCatchCommentText(sourceCode, node) {
20
+ const getCommentsInside = sourceCode.getCommentsInside;
21
+ if (node.body && typeof getCommentsInside === "function") {
22
+ try {
23
+ const insideComments = getCommentsInside(node.body) || [];
24
+ const insideText = insideComments.map(comment_text_helpers_1.extractCommentValue).join(" ");
25
+ if (insideText) {
26
+ return insideText;
27
+ }
28
+ }
29
+ catch {
30
+ // fall through to line-based fallback
31
+ }
32
+ }
33
+ if (node.body && node.body.loc && node.body.loc.start && node.body.loc.end) {
34
+ const lines = sourceCode.lines;
35
+ const startIndex = node.body.loc.start.line - 1;
36
+ const endIndex = node.body.loc.end.line - 1;
37
+ const insideText = (0, branch_annotation_helpers_1.scanCommentLinesInRange)(lines, startIndex + 1, endIndex);
38
+ if (insideText) {
39
+ return insideText;
40
+ }
41
+ }
42
+ return "";
43
+ }
44
+ /**
45
+ * Gather comment text from the first contiguous comment lines inside a TryStatement block body.
46
+ * @supports docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md REQ-INSIDE-BRACE-PLACEMENT
47
+ * @param sourceCode - ESLint source code object providing line access
48
+ * @param node - TryStatement AST node whose block will be scanned for comments
49
+ * @returns Concatenated comment text from inside the try block, or empty string if none found
50
+ */
51
+ function getInsideTryBlockCommentText(sourceCode, node) {
52
+ const block = node && node.block;
53
+ if (!block ||
54
+ block.type !== "BlockStatement" ||
55
+ !block.loc ||
56
+ !block.loc.start ||
57
+ !block.loc.end ||
58
+ typeof block.loc.start.line !== "number" ||
59
+ typeof block.loc.end.line !== "number") {
60
+ return "";
61
+ }
62
+ const lines = sourceCode.lines;
63
+ const startIndex = block.loc.start.line - 1;
64
+ const endIndex = block.loc.end.line - 1;
65
+ const insideText = (0, branch_annotation_helpers_1.scanCommentLinesInRange)(lines, startIndex + 1, endIndex);
66
+ if (insideText) {
67
+ return insideText;
68
+ }
69
+ return "";
70
+ }
@@ -12,6 +12,9 @@ const branch_annotation_loop_helpers_1 = require("./branch-annotation-loop-helpe
12
12
  const branch_annotation_if_helpers_1 = require("./branch-annotation-if-helpers");
13
13
  const branch_annotation_switch_helpers_1 = require("./branch-annotation-switch-helpers");
14
14
  const branch_annotation_story_fix_helpers_1 = require("./branch-annotation-story-fix-helpers");
15
+ const branch_annotation_catch_helpers_1 = require("./branch-annotation-catch-helpers");
16
+ const branch_validation_1 = require("./branch-validation");
17
+ const comment_text_helpers_1 = require("./comment-text-helpers");
15
18
  /**
16
19
  * Valid branch types for require-branch-annotation rule.
17
20
  * @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
@@ -35,96 +38,7 @@ exports.DEFAULT_BRANCH_TYPES = [
35
38
  * @req REQ-CONFIGURABLE-SCOPE - Allow configuration of branch types for annotation enforcement
36
39
  */
37
40
  function validateBranchTypes(context) {
38
- const options = context.options[0] || {};
39
- /**
40
- * Conditional branch checking whether branchTypes option was provided.
41
- * @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
42
- * @req REQ-TRACEABILITY-CONDITIONAL - Trace configuration branch existence check
43
- */
44
- if (Array.isArray(options.branchTypes)) {
45
- /**
46
- * Predicate to determine whether a provided branch type is invalid.
47
- * @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
48
- * @req REQ-TRACEABILITY-FILTER-CALLBACK - Trace filter callback for invalid branch type detection
49
- */
50
- function isInvalidType(t) {
51
- return !exports.DEFAULT_BRANCH_TYPES.includes(t);
52
- }
53
- const invalidTypes = options.branchTypes.filter(isInvalidType);
54
- /**
55
- * Conditional branch checking whether any invalid types were found.
56
- * @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
57
- * @req REQ-TRACEABILITY-INVALID-DETECTION - Trace handling when invalid types are detected
58
- */
59
- if (invalidTypes.length > 0) {
60
- /**
61
- * Program listener produced when configuration is invalid.
62
- * @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
63
- * @req REQ-TRACEABILITY-PROGRAM-LISTENER - Trace Program listener reporting invalid config values
64
- */
65
- function ProgramHandler(node) {
66
- /**
67
- * Report a single invalid type for the given Program node.
68
- * @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
69
- * @req REQ-TRACEABILITY-FOR-EACH-CALLBACK - Trace reporting for each invalid type
70
- */
71
- function reportInvalidType(t) {
72
- context.report({
73
- node,
74
- message: `Value "${t}" should be equal to one of the allowed values: ${exports.DEFAULT_BRANCH_TYPES.join(", ")}`,
75
- });
76
- }
77
- invalidTypes.forEach(reportInvalidType);
78
- }
79
- return { Program: ProgramHandler };
80
- }
81
- }
82
- return Array.isArray(options.branchTypes)
83
- ? options.branchTypes
84
- : Array.from(exports.DEFAULT_BRANCH_TYPES);
85
- }
86
- /**
87
- * Extract the raw value from a comment node.
88
- * @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
89
- * @req REQ-TRACEABILITY-MAP-CALLBACK - Trace mapping of comment nodes to their text values
90
- */
91
- function extractCommentValue(_c) {
92
- return _c.value;
93
- }
94
- /**
95
- * Extract trimmed comment text for a given source line index or return null
96
- * when the line is blank or not a comment. This helper centralizes the
97
- * formatter-aware rules used by branch helpers when scanning for contiguous
98
- * comment lines around branches.
99
- * @supports docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md REQ-COMMENT-ASSOCIATION
100
- * @supports docs/stories/025.0-DEV-CATCH-ANNOTATION-POSITION.story.md REQ-DUAL-POSITION-DETECTION REQ-FALLBACK-LOGIC
101
- * @supports docs/stories/026.0-DEV-ELSE-IF-ANNOTATION-POSITION.story.md REQ-DUAL-POSITION-DETECTION-ELSE-IF REQ-FALLBACK-LOGIC-ELSE-IF
102
- */
103
- function getCommentTextAtLine(lines, index) {
104
- const line = lines[index];
105
- if (!line || !line.trim()) {
106
- return null;
107
- }
108
- if (!/^\s*(\/\/|\/\*)/.test(line)) {
109
- return null;
110
- }
111
- return line.trim();
112
- }
113
- /**
114
- * Collect a single contiguous comment line at the given index, appending its
115
- * trimmed text to the accumulator. Returns true when a valid comment was
116
- * collected and false when scanning should stop (blank or non-comment line).
117
- * @supports docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md REQ-COMMENT-ASSOCIATION
118
- * @supports docs/stories/025.0-DEV-CATCH-ANNOTATION-POSITION.story.md REQ-DUAL-POSITION-DETECTION REQ-FALLBACK-LOGIC
119
- * @supports docs/stories/026.0-DEV-ELSE-IF-ANNOTATION-POSITION.story.md REQ-DUAL-POSITION-DETECTION-ELSE-IF REQ-FALLBACK-LOGIC-ELSE-IF
120
- */
121
- function collectCommentLine(lines, index, comments) {
122
- const commentText = getCommentTextAtLine(lines, index);
123
- if (!commentText) {
124
- return false;
125
- }
126
- comments.push(commentText);
127
- return true;
41
+ return (0, branch_validation_1.validateBranchTypes)(context, exports.DEFAULT_BRANCH_TYPES);
128
42
  }
129
43
  /**
130
44
  * Scan contiguous formatter-aware comment lines between the provided 0-based
@@ -148,62 +62,13 @@ function scanCommentLinesInRange(lines, startIndex, endIndexInclusive) {
148
62
  const lastIndex = Math.min(endIndexInclusive, lines.length - 1);
149
63
  let i = startIndex;
150
64
  while (i <= lastIndex) {
151
- if (!collectCommentLine(lines, i, comments)) {
65
+ if (!(0, comment_text_helpers_1.collectCommentLine)(lines, i, comments)) {
152
66
  break;
153
67
  }
154
68
  i++;
155
69
  }
156
70
  return comments.join(" ");
157
71
  }
158
- function getInsideCatchCommentText(sourceCode, node) {
159
- const getCommentsInside = sourceCode.getCommentsInside;
160
- if (node.body && typeof getCommentsInside === "function") {
161
- try {
162
- const insideComments = getCommentsInside(node.body) || [];
163
- const insideText = insideComments.map(extractCommentValue).join(" ");
164
- if (insideText) {
165
- return insideText;
166
- }
167
- }
168
- catch {
169
- // fall through to line-based fallback
170
- }
171
- }
172
- if (node.body && node.body.loc && node.body.loc.start && node.body.loc.end) {
173
- const lines = sourceCode.lines;
174
- const startIndex = node.body.loc.start.line - 1;
175
- const endIndex = node.body.loc.end.line - 1;
176
- const insideText = scanCommentLinesInRange(lines, startIndex + 1, endIndex);
177
- if (insideText) {
178
- return insideText;
179
- }
180
- }
181
- return "";
182
- }
183
- /**
184
- * Gather comment text from the first contiguous comment lines inside a TryStatement block body.
185
- * @supports docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md REQ-INSIDE-BRACE-PLACEMENT REQ-PLACEMENT-CONFIG
186
- */
187
- function getInsideTryBlockCommentText(sourceCode, node) {
188
- const block = node && node.block;
189
- if (!block ||
190
- block.type !== "BlockStatement" ||
191
- !block.loc ||
192
- !block.loc.start ||
193
- !block.loc.end ||
194
- typeof block.loc.start.line !== "number" ||
195
- typeof block.loc.end.line !== "number") {
196
- return "";
197
- }
198
- const lines = sourceCode.lines;
199
- const startIndex = block.loc.start.line - 1;
200
- const endIndex = block.loc.end.line - 1;
201
- const insideText = scanCommentLinesInRange(lines, startIndex + 1, endIndex);
202
- if (insideText) {
203
- return insideText;
204
- }
205
- return "";
206
- }
207
72
  /**
208
73
  * Gather annotation text for CatchClause nodes, supporting both before-catch and inside-catch positions.
209
74
  * @story docs/stories/025.0-DEV-CATCH-ANNOTATION-POSITION.story.md
@@ -212,7 +77,7 @@ function getInsideTryBlockCommentText(sourceCode, node) {
212
77
  */
213
78
  function gatherCatchClauseCommentText(sourceCode, node, annotationPlacement, beforeText) {
214
79
  if (annotationPlacement === "inside") {
215
- const insideText = getInsideCatchCommentText(sourceCode, node);
80
+ const insideText = (0, branch_annotation_catch_helpers_1.getInsideCatchCommentText)(sourceCode, node);
216
81
  if (insideText) {
217
82
  return insideText;
218
83
  }
@@ -223,7 +88,7 @@ function gatherCatchClauseCommentText(sourceCode, node, annotationPlacement, bef
223
88
  /@supports\b/.test(beforeText)) {
224
89
  return beforeText;
225
90
  }
226
- const insideText = getInsideCatchCommentText(sourceCode, node);
91
+ const insideText = (0, branch_annotation_catch_helpers_1.getInsideCatchCommentText)(sourceCode, node);
227
92
  if (insideText) {
228
93
  return insideText;
229
94
  }
@@ -235,54 +100,13 @@ function gatherCatchClauseCommentText(sourceCode, node, annotationPlacement, bef
235
100
  * leading comment text unchanged. When placement is "inside", it switches to inside-brace
236
101
  * semantics and scans for comments at the top of the consequent block.
237
102
  * @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
238
- * @supports REQ-INSIDE-BRACE-PLACEMENT
239
- * @supports REQ-PLACEMENT-CONFIG
240
- * @supports REQ-DEFAULT-BACKWARD-COMPAT
103
+ * @supports docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md REQ-INSIDE-BRACE-PLACEMENT REQ-PLACEMENT-CONFIG REQ-DEFAULT-BACKWARD-COMPAT
241
104
  */
242
- function gatherSimpleIfCommentText(sourceCode, node, annotationPlacement, beforeText) {
243
- if (annotationPlacement === "before") {
244
- return beforeText;
245
- }
246
- if (annotationPlacement !== "inside") {
247
- return beforeText;
248
- }
249
- if (!node.consequent || node.consequent.type !== "BlockStatement") {
250
- return "";
251
- }
252
- const consequent = node.consequent;
253
- const getCommentsInside = sourceCode.getCommentsInside;
254
- if (typeof getCommentsInside === "function") {
255
- try {
256
- const insideComments = getCommentsInside(consequent) || [];
257
- const insideText = insideComments.map(extractCommentValue).join(" ");
258
- if (insideText) {
259
- return insideText;
260
- }
261
- }
262
- catch {
263
- // fall through to line-based fallback
264
- }
265
- }
266
- if (consequent.loc &&
267
- consequent.loc.start &&
268
- consequent.loc.end &&
269
- typeof consequent.loc.start.line === "number" &&
270
- typeof consequent.loc.end.line === "number") {
271
- const lines = sourceCode.lines;
272
- const startIndex = consequent.loc.start.line - 1;
273
- const endIndex = consequent.loc.end.line - 1;
274
- const insideText = scanCommentLinesInRange(lines, startIndex + 1, endIndex);
275
- if (insideText) {
276
- return insideText;
277
- }
278
- }
279
- return "";
280
- }
281
105
  function handleTryCatchBranch(sourceCode, node, context) {
282
106
  const { annotationPlacement, beforeText } = context;
283
107
  if (node.type === "TryStatement") {
284
108
  if (annotationPlacement === "inside") {
285
- const insideText = getInsideTryBlockCommentText(sourceCode, node);
109
+ const insideText = (0, branch_annotation_catch_helpers_1.getInsideTryBlockCommentText)(sourceCode, node);
286
110
  if (insideText) {
287
111
  return insideText;
288
112
  }
@@ -351,7 +175,7 @@ function gatherIfBranchCommentText(sourceCode, node, parent, context) {
351
175
  beforeText,
352
176
  });
353
177
  }
354
- return gatherSimpleIfCommentText(sourceCode, node, annotationPlacement, beforeText);
178
+ return (0, branch_annotation_if_helpers_1.gatherSimpleIfCommentText)(sourceCode, node, annotationPlacement, beforeText);
355
179
  }
356
180
  /**
357
181
  * Internal helper that performs type-based dispatch for gathering branch comment text.
@@ -387,7 +211,7 @@ function gatherBranchCommentTextByType(sourceCode, node, parent, context) {
387
211
  */
388
212
  function gatherBranchCommentText(sourceCode, node, parent, annotationPlacement = "before") {
389
213
  const beforeComments = sourceCode.getCommentsBefore(node) || [];
390
- const beforeText = beforeComments.map(extractCommentValue).join(" ");
214
+ const beforeText = beforeComments.map(comment_text_helpers_1.extractCommentValue).join(" ");
391
215
  const handled = gatherBranchCommentTextByType(sourceCode, node, parent, {
392
216
  annotationPlacement,
393
217
  beforeText,
@@ -10,3 +10,4 @@ export declare function gatherElseIfCommentText(sourceCode: ReturnType<Rule.Rule
10
10
  annotationPlacement: AnnotationPlacement;
11
11
  beforeText: string;
12
12
  }): string;
13
+ export declare function gatherSimpleIfCommentText(sourceCode: any, node: any, annotationPlacement: "before" | "inside", beforeText: string): string;