eslint-plugin-traceability 1.20.0 → 1.21.1

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 (53) hide show
  1. package/CHANGELOG.md +3 -3
  2. package/README.md +8 -7
  3. package/lib/src/maintenance/batch.js +1 -0
  4. package/lib/src/maintenance/cli.js +1 -0
  5. package/lib/src/maintenance/commands.js +1 -0
  6. package/lib/src/maintenance/detect.js +1 -0
  7. package/lib/src/maintenance/report.js +1 -0
  8. package/lib/src/maintenance/update.js +1 -0
  9. package/lib/src/rules/helpers/require-story-core.d.ts +2 -0
  10. package/lib/src/rules/helpers/require-story-core.js +7 -4
  11. package/lib/src/rules/helpers/require-story-helpers.d.ts +19 -1
  12. package/lib/src/rules/helpers/require-story-helpers.js +48 -4
  13. package/lib/src/rules/helpers/require-story-io.js +1 -0
  14. package/lib/src/rules/helpers/require-story-visitors.js +6 -0
  15. package/lib/src/rules/helpers/require-test-traceability-helpers.js +1 -0
  16. package/lib/src/rules/helpers/test-callback-exclusion.d.ts +1 -0
  17. package/lib/src/rules/helpers/valid-annotation-format-internal.d.ts +2 -2
  18. package/lib/src/rules/helpers/valid-annotation-format-internal.js +2 -2
  19. package/lib/src/rules/helpers/valid-annotation-format-validators.d.ts +14 -14
  20. package/lib/src/rules/helpers/valid-annotation-format-validators.js +31 -22
  21. package/lib/src/rules/helpers/valid-annotation-utils.js +1 -0
  22. package/lib/src/rules/helpers/valid-req-reference-helpers.js +1 -0
  23. package/lib/src/rules/require-req-annotation.d.ts +2 -3
  24. package/lib/src/rules/require-req-annotation.js +11 -2
  25. package/lib/src/rules/require-story-annotation.js +46 -20
  26. package/lib/src/rules/valid-annotation-format.js +14 -10
  27. package/lib/src/utils/annotation-checker.d.ts +1 -0
  28. package/lib/src/utils/annotation-checker.js +14 -0
  29. package/lib/src/utils/reqAnnotationDetection.js +1 -0
  30. package/lib/tests/config/eslint-config-validation.test.js +1 -0
  31. package/lib/tests/config/flat-config-presets-integration.test.js +1 -0
  32. package/lib/tests/config/require-story-annotation-config.test.js +2 -0
  33. package/lib/tests/fixtures/stale/example.js +1 -0
  34. package/lib/tests/fixtures/update/example.js +1 -0
  35. package/lib/tests/integration/annotation-placement-inside-prettier.integration.test.js +1 -0
  36. package/lib/tests/integration/catch-annotation-prettier.integration.test.js +1 -0
  37. package/lib/tests/integration/else-if-annotation-prettier.integration.test.js +1 -0
  38. package/lib/tests/integration/prettier-test-helpers.js +1 -0
  39. package/lib/tests/integration/require-traceability-aliases.integration.test.js +26 -0
  40. package/lib/tests/integration/require-traceability-test-callbacks.integration.test.js +1 -0
  41. package/lib/tests/maintenance/detect-isolated.test.js +1 -0
  42. package/lib/tests/perf/maintenance-large-workspace.test.js +1 -0
  43. package/lib/tests/perf/valid-annotation-format-large-file.test.js +1 -0
  44. package/lib/tests/plugin-setup.test.js +1 -0
  45. package/lib/tests/rules/error-reporting.test.js +1 -0
  46. package/lib/tests/rules/require-req-annotation.test.js +20 -0
  47. package/lib/tests/rules/require-story-annotation.test.js +26 -0
  48. package/lib/tests/rules/require-test-traceability.test.js +1 -0
  49. package/lib/tests/utils/branch-annotation-catch-insert-position.test.js +1 -0
  50. package/lib/tests/utils/branch-annotation-else-if-insert-position.test.js +1 -0
  51. package/lib/tests/utils/branch-annotation-helpers.test.js +1 -0
  52. package/package.json +1 -1
  53. package/user-docs/api-reference.md +4 -4
package/CHANGELOG.md CHANGED
@@ -1,9 +1,9 @@
1
- # [1.20.0](https://github.com/voder-ai/eslint-plugin-traceability/compare/v1.19.4...v1.20.0) (2025-12-18)
1
+ ## [1.21.1](https://github.com/voder-ai/eslint-plugin-traceability/compare/v1.21.0...v1.21.1) (2025-12-21)
2
2
 
3
3
 
4
- ### Features
4
+ ### Bug Fixes
5
5
 
6
- * extend branch placement standard docs and helpers ([ae05a52](https://github.com/voder-ai/eslint-plugin-traceability/commit/ae05a5232b724060f7b1baae9a6be6eedf466617))
6
+ * **rules:** harden valid-annotation-format parsing ([7c914f1](https://github.com/voder-ai/eslint-plugin-traceability/commit/7c914f100b93923bec63f97dfa122e1c4686ecff))
7
7
 
8
8
  # Changelog
9
9
 
package/README.md CHANGED
@@ -117,15 +117,16 @@ Traceability annotations are typically placed immediately adjacent to the code t
117
117
  }
118
118
  ```
119
119
 
120
+ The `annotationPlacement` option is also supported by the function-level rules (`traceability/require-story-annotation` and `traceability/require-req-annotation`) when you configure them directly. In `"inside"` mode, these rules treat only the first comment-only lines inside function and method bodies as satisfying the annotation requirement; JSDoc and before-function comments are ignored for block-bodied functions and methods, while TypeScript declarations and signature-only nodes continue to use before-node annotations.
121
+
120
122
  - **Function-level (`traceability/require-story-annotation`, `traceability/require-req-annotation`)**
121
123
 
122
- Function-level rules continue to accept annotations:
123
- - As JSDoc blocks immediately preceding the function, or
124
- - As line comments placed directly before the function declaration or expression.
124
+ Function-level rules support both before-function and inside-body placement, controlled by the same `annotationPlacement` option described above:
125
+
126
+ - `"before"` – Annotations are written as JSDoc blocks immediately preceding the function, or as line comments placed directly before the function declaration or expression.
127
+ - `"inside"` – Annotations are expected to appear on the first comment-only lines inside function and method bodies; comments before the function are ignored for block-bodied functions in this mode, while TypeScript declarations and signature-only nodes still rely on before-node annotations.
125
128
 
126
- Function-level rules now support the same placement configuration model as branches:
127
- - By default, annotations are still placed immediately before the function (JSDoc or line comments).
128
- - When you configure `annotationPlacement: "inside"` on `traceability/require-story-annotation`, the rule prefers annotations as the first comment-only lines inside the function or method body, mirroring the branch-level inside-brace standard from Story 028.0. Declaration-only shapes such as `TSDeclareFunction` and `TSMethodSignature` remain before-function only, since they have no executable body.
129
+ For full details and migration guidance between placement styles, see the [API Reference](user-docs/api-reference.md) and the migration guide ([user-docs/migration-guide.md](user-docs/migration-guide.md)).
129
130
 
130
131
  For full configuration details and migration guidance between placement styles, see:
131
132
 
@@ -429,4 +430,4 @@ For the canonical, user-facing security policy (including how to report vulnerab
429
430
  - Contribution guide: <https://github.com/voder-ai/eslint-plugin-traceability/blob/main/CONTRIBUTING.md>
430
431
  - Issue tracker: <https://github.com/voder-ai/eslint-plugin-traceability/issues>
431
432
  - Changelog: [CHANGELOG.md](CHANGELOG.md)
432
- - Versioning and Releases: This project uses semantic-release for automated versioning. The authoritative list of published versions and release notes is on GitHub Releases: <https://github.com/voder-ai/eslint-plugin-traceability/releases>
433
+ - Versioning and Releases: This project uses semantic-release for automated versioning. The authoritative list of published versions and release notes is on GitHub Releases: <https://github.com/voder-ai/eslint-plugin-traceability/releases>
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.batchUpdateAnnotations = batchUpdateAnnotations;
4
4
  exports.verifyAnnotations = verifyAnnotations;
5
+ /* eslint-disable traceability/valid-annotation-format */
5
6
  const update_1 = require("./update");
6
7
  const detect_1 = require("./detect");
7
8
  /**
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
+ /* eslint-disable traceability/valid-annotation-format */
3
4
  Object.defineProperty(exports, "__esModule", { value: true });
4
5
  exports.runMaintenanceCli = runMaintenanceCli;
5
6
  const commands_1 = require("./commands");
@@ -5,6 +5,7 @@ exports.handleDetect = handleDetect;
5
5
  exports.handleVerify = handleVerify;
6
6
  exports.handleReport = handleReport;
7
7
  exports.handleUpdate = handleUpdate;
8
+ /* eslint-disable traceability/valid-annotation-format */
8
9
  /**
9
10
  * Subcommand handlers for the traceability-maint CLI.
10
11
  *
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.detectStaleAnnotations = detectStaleAnnotations;
37
+ /* eslint-disable traceability/valid-annotation-format */
37
38
  const fs = __importStar(require("fs"));
38
39
  const path = __importStar(require("path"));
39
40
  const utils_1 = require("./utils");
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.generateMaintenanceReport = generateMaintenanceReport;
4
+ /* eslint-disable traceability/valid-annotation-format */
4
5
  const detect_1 = require("./detect");
5
6
  /**
6
7
  * Generate a report of maintenance operations performed
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.updateAnnotationReferences = updateAnnotationReferences;
37
+ /* eslint-disable traceability/valid-annotation-format */
37
38
  const fs = __importStar(require("fs"));
38
39
  const utils_1 = require("./utils");
39
40
  /**
@@ -38,6 +38,7 @@ import type { Rule } from "eslint";
38
38
  type CoreReportOptions = {
39
39
  annotationTemplateOverride?: string;
40
40
  autoFixToggle?: boolean;
41
+ annotationPlacement?: "before" | "inside";
41
42
  };
42
43
  type ReportDeps = {
43
44
  hasStoryAnnotation: (_sourceCode: any, _node: any) => boolean;
@@ -53,6 +54,7 @@ type ReportDeps = {
53
54
  shouldApplyAutoFix: (_autoFix: boolean | undefined) => boolean;
54
55
  createAddStoryFix: (_target: any, _annotationTemplate: string) => any;
55
56
  createMethodFix: (_node: any, _annotationTemplate: string) => any;
57
+ hasStoryAnnotationWithPlacement?: (_sourceCode: any, _node: any, _placement: "before" | "inside") => boolean;
56
58
  };
57
59
  /**
58
60
  * Core helper to report a missing @story annotation for a function-like node.
@@ -5,6 +5,7 @@ exports.createAddStoryFix = createAddStoryFix;
5
5
  exports.createMethodFix = createMethodFix;
6
6
  exports.coreReportMissing = coreReportMissing;
7
7
  exports.coreReportMethod = coreReportMethod;
8
+ /* eslint-disable traceability/valid-annotation-format */
8
9
  /**
9
10
  * Compute the insertion start offset for inserting annotations before a node.
10
11
  * This helper ensures we insert before any export wrapper when present, while
@@ -158,11 +159,13 @@ function coreReportMissing(deps, context, sourceCode, config) {
158
159
  const { node, target: passedTarget, options = {} } = config;
159
160
  withSafeReporting("coreReportMissing", () => {
160
161
  const annotationPlacement = resolveAnnotationPlacement(options);
161
- if (typeof deps.hasStoryAnnotationWithPlacement === "function" &&
162
- deps.hasStoryAnnotationWithPlacement(sourceCode, node, annotationPlacement)) {
163
- return;
162
+ const hasWithPlacement = deps.hasStoryAnnotationWithPlacement;
163
+ if (typeof hasWithPlacement === "function") {
164
+ if (hasWithPlacement(sourceCode, node, annotationPlacement)) {
165
+ return;
166
+ }
164
167
  }
165
- if (deps.hasStoryAnnotation(sourceCode, node)) {
168
+ else if (deps.hasStoryAnnotation(sourceCode, node)) {
166
169
  return;
167
170
  }
168
171
  const functionName = deps.getReportedFunctionName(node);
@@ -18,6 +18,7 @@ import { type CallbackExclusionOptions } from "./test-callback-exclusion";
18
18
  interface ReportOptions extends CallbackExclusionOptions {
19
19
  annotationTemplateOverride?: string;
20
20
  autoFixToggle?: boolean;
21
+ annotationPlacement?: "before" | "inside";
21
22
  }
22
23
  /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
23
24
  declare function getAnnotationTemplate(override?: string, _options?: CallbackExclusionOptions): string;
@@ -53,7 +54,24 @@ declare function leadingCommentsHasStory(node: any): boolean;
53
54
  * @req REQ-ANNOTATION-REQUIRED - Detect existing story annotations in JSDoc or comments
54
55
  */
55
56
  declare function hasStoryAnnotation(sourceCode: any, node: any): boolean;
56
- declare const hasStoryAnnotationWithPlacement: typeof hasStoryAnnotation;
57
+ /**
58
+ * Placement-aware story detection helper used by core reporting.
59
+ *
60
+ * When annotationPlacement is "inside" and the node supports inside-brace
61
+ * semantics, this helper only treats annotations found on the first
62
+ * comment-only lines inside the function or method body as satisfying the
63
+ * requirement. JSDoc and before-function comments are intentionally ignored so
64
+ * that misplaced annotations are reported as violations under the inside
65
+ * standard.
66
+ *
67
+ * For nodes that do not support inside placement (such as TS declarations,
68
+ * signature-only nodes, or functions without block bodies), this helper
69
+ * delegates to the existing hasStoryAnnotation heuristics so that they
70
+ * continue to rely on before-function placement.
71
+ *
72
+ * @supports docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md REQ-ALL-BLOCK-TYPES REQ-INSIDE-BRACE-PLACEMENT REQ-PLACEMENT-CONFIG
73
+ */
74
+ declare function hasStoryAnnotationWithPlacement(sourceCode: any, node: any, annotationPlacement: "before" | "inside"): boolean;
57
75
  /**
58
76
  * Determine AST node where annotation should be inserted
59
77
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.fallbackTextBeforeHasStory = exports.parentChainHasStory = exports.linesBeforeHasStory = exports.EXPORT_PRIORITY_VALUES = exports.DEFAULT_SCOPE = exports.getNodeName = exports.hasStoryAnnotationWithPlacement = exports.STORY_PATH = void 0;
3
+ exports.fallbackTextBeforeHasStory = exports.parentChainHasStory = exports.linesBeforeHasStory = exports.EXPORT_PRIORITY_VALUES = exports.DEFAULT_SCOPE = exports.getNodeName = exports.STORY_PATH = void 0;
4
4
  exports.getAnnotationTemplate = getAnnotationTemplate;
5
5
  exports.shouldApplyAutoFix = shouldApplyAutoFix;
6
6
  exports.isExportedNode = isExportedNode;
@@ -8,6 +8,7 @@ exports.jsdocHasStory = jsdocHasStory;
8
8
  exports.commentsBeforeHasStory = commentsBeforeHasStory;
9
9
  exports.leadingCommentsHasStory = leadingCommentsHasStory;
10
10
  exports.hasStoryAnnotation = hasStoryAnnotation;
11
+ exports.hasStoryAnnotationWithPlacement = hasStoryAnnotationWithPlacement;
11
12
  exports.extractName = extractName;
12
13
  exports.resolveTargetNode = resolveTargetNode;
13
14
  exports.shouldProcessNode = shouldProcessNode;
@@ -19,6 +20,7 @@ Object.defineProperty(exports, "parentChainHasStory", { enumerable: true, get: f
19
20
  Object.defineProperty(exports, "fallbackTextBeforeHasStory", { enumerable: true, get: function () { return require_story_io_1.fallbackTextBeforeHasStory; } });
20
21
  const require_story_utils_1 = require("./require-story-utils");
21
22
  Object.defineProperty(exports, "getNodeName", { enumerable: true, get: function () { return require_story_utils_1.getNodeName; } });
23
+ const function_annotation_helpers_1 = require("../../utils/function-annotation-helpers");
22
24
  const require_story_core_1 = require("./require-story-core");
23
25
  Object.defineProperty(exports, "DEFAULT_SCOPE", { enumerable: true, get: function () { return require_story_core_1.DEFAULT_SCOPE; } });
24
26
  Object.defineProperty(exports, "EXPORT_PRIORITY_VALUES", { enumerable: true, get: function () { return require_story_core_1.EXPORT_PRIORITY_VALUES; } });
@@ -220,9 +222,49 @@ function hasStoryAnnotation(sourceCode, node) {
220
222
  }
221
223
  return false;
222
224
  }
223
- // Placement-aware alias reserved for future inside-brace function placement.
224
- const hasStoryAnnotationWithPlacement = hasStoryAnnotation;
225
- exports.hasStoryAnnotationWithPlacement = hasStoryAnnotationWithPlacement;
225
+ /**
226
+ * Placement-aware story detection helper used by core reporting.
227
+ *
228
+ * When annotationPlacement is "inside" and the node supports inside-brace
229
+ * semantics, this helper only treats annotations found on the first
230
+ * comment-only lines inside the function or method body as satisfying the
231
+ * requirement. JSDoc and before-function comments are intentionally ignored so
232
+ * that misplaced annotations are reported as violations under the inside
233
+ * standard.
234
+ *
235
+ * For nodes that do not support inside placement (such as TS declarations,
236
+ * signature-only nodes, or functions without block bodies), this helper
237
+ * delegates to the existing hasStoryAnnotation heuristics so that they
238
+ * continue to rely on before-function placement.
239
+ *
240
+ * @supports docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md REQ-ALL-BLOCK-TYPES REQ-INSIDE-BRACE-PLACEMENT REQ-PLACEMENT-CONFIG
241
+ */
242
+ function hasStoryAnnotationWithPlacement(sourceCode, node, annotationPlacement) {
243
+ // Backward-compatible default: use existing heuristics when placement is
244
+ // "before" or when the function does not support inside-brace semantics.
245
+ if (annotationPlacement !== "inside" ||
246
+ !(0, function_annotation_helpers_1.supportsInsidePlacementForFunction)(node)) {
247
+ return hasStoryAnnotation(sourceCode, node);
248
+ }
249
+ try {
250
+ const insideText = (0, function_annotation_helpers_1.getFunctionInsideBodyCommentText)(sourceCode, node);
251
+ if (typeof insideText === "string" &&
252
+ (insideText.includes("@story") || insideText.includes("@supports"))) {
253
+ return true;
254
+ }
255
+ }
256
+ catch (error) {
257
+ if (process.env.TRACEABILITY_DEBUG === "1") {
258
+ // Debug logging only when explicitly enabled for troubleshooting helper failures.
259
+ console.error("[traceability] hasStoryAnnotationWithPlacement failed for node", error?.message ?? error);
260
+ }
261
+ }
262
+ // In inside-placement mode for block-bodied functions and methods we
263
+ // intentionally do not fall back to before-function heuristics; callers
264
+ // should treat this as a missing annotation so that misplaced comments are
265
+ // reported as violations.
266
+ return false;
267
+ }
226
268
  /**
227
269
  * Determine AST node where annotation should be inserted
228
270
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
@@ -375,6 +417,7 @@ function resolveAnnotationTargetNode(sourceCode, node, passedTarget) {
375
417
  function reportMissing(context, sourceCode, config) {
376
418
  (0, require_story_core_1.coreReportMissing)({
377
419
  hasStoryAnnotation,
420
+ hasStoryAnnotationWithPlacement,
378
421
  getReportedFunctionName,
379
422
  resolveAnnotationTargetNode,
380
423
  getNameNodeForReport,
@@ -390,6 +433,7 @@ function reportMissing(context, sourceCode, config) {
390
433
  function reportMethod(context, sourceCode, config) {
391
434
  (0, require_story_core_1.coreReportMethod)({
392
435
  hasStoryAnnotation,
436
+ hasStoryAnnotationWithPlacement,
393
437
  getReportedFunctionName,
394
438
  resolveAnnotationTargetNode,
395
439
  getNameNodeForReport,
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
+ /* eslint-disable traceability/valid-annotation-format */
2
3
  /**
3
4
  * IO helpers for require-story detection moved to reduce helper module size
4
5
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
@@ -33,6 +33,7 @@ function buildFunctionDeclarationVisitor(context, sourceCode, options) {
33
33
  options: {
34
34
  annotationTemplateOverride: options.annotationTemplate,
35
35
  autoFixToggle: options.autoFix,
36
+ annotationPlacement: options.annotationPlacement,
36
37
  },
37
38
  });
38
39
  }
@@ -68,6 +69,7 @@ function buildFunctionExpressionVisitor(context, sourceCode, options) {
68
69
  options: {
69
70
  annotationTemplateOverride: options.annotationTemplate,
70
71
  autoFixToggle: options.autoFix,
72
+ annotationPlacement: options.annotationPlacement,
71
73
  },
72
74
  });
73
75
  }
@@ -96,6 +98,7 @@ function buildArrowFunctionVisitor(context, sourceCode, options) {
96
98
  options: {
97
99
  annotationTemplateOverride: options.annotationTemplate,
98
100
  autoFixToggle: options.autoFix,
101
+ annotationPlacement: options.annotationPlacement,
99
102
  },
100
103
  });
101
104
  }
@@ -123,6 +126,7 @@ function buildTSDeclareFunctionVisitor(context, sourceCode, options) {
123
126
  options: {
124
127
  annotationTemplateOverride: options.annotationTemplate,
125
128
  autoFixToggle: options.autoFix,
129
+ annotationPlacement: options.annotationPlacement,
126
130
  },
127
131
  });
128
132
  }
@@ -151,6 +155,7 @@ function buildTSMethodSignatureVisitor(context, sourceCode, options) {
151
155
  options: {
152
156
  annotationTemplateOverride: options.methodAnnotationTemplate ?? options.annotationTemplate,
153
157
  autoFixToggle: options.autoFix,
158
+ annotationPlacement: options.annotationPlacement,
154
159
  },
155
160
  });
156
161
  }
@@ -177,6 +182,7 @@ function buildMethodDefinitionVisitor(context, sourceCode, options) {
177
182
  options: {
178
183
  annotationTemplateOverride: options.methodAnnotationTemplate ?? options.annotationTemplate,
179
184
  autoFixToggle: options.autoFix,
185
+ annotationPlacement: options.annotationPlacement,
180
186
  },
181
187
  });
182
188
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.determineIsTestFile = determineIsTestFile;
4
4
  exports.ensureFileSupportsAnnotation = ensureFileSupportsAnnotation;
5
5
  exports.handleCallExpression = handleCallExpression;
6
+ /* eslint-disable traceability/valid-annotation-format */
6
7
  /**
7
8
  * Helper utilities for the require-test-traceability rule.
8
9
  *
@@ -21,6 +21,7 @@ import type { TSESTree } from "@typescript-eslint/utils";
21
21
  interface CallbackExclusionOptions {
22
22
  excludeTestCallbacks?: boolean;
23
23
  additionalTestHelperNames?: string[];
24
+ annotationPlacement?: "before" | "inside";
24
25
  }
25
26
  type TraceabilityNodeWithParent = TSESTree.Node & {
26
27
  parent?: TraceabilityNodeWithParent | null;
@@ -19,7 +19,7 @@ export interface PendingAnnotation {
19
19
  * boundaries), keeps any annotation tags that appear later in the line, and
20
20
  * supports common JSDoc styles such as leading "*".
21
21
  *
22
- * It detects @story, @req, and @supports tags while preserving the rest
22
+ * It detects `@story`, `@req`, and `@supports` tags while preserving the rest
23
23
  * of the line for downstream logic.
24
24
  */
25
25
  export declare function normalizeCommentLine(rawLine: string): string;
@@ -27,7 +27,7 @@ export declare function normalizeCommentLine(rawLine: string): string;
27
27
  * Detect whether a normalized comment line starts with a non-traceability JSDoc tag.
28
28
  *
29
29
  * This is used to distinguish regular JSDoc tags (e.g. @param, @returns) from
30
- * traceability-related annotations such as @story, @req, and @supports.
30
+ * traceability-related annotations such as `@story`, `@req`, and `@supports`.
31
31
  *
32
32
  * Supports coexistence with JSDoc by:
33
33
  * - Detecting boundaries between traceability tags and other tags
@@ -15,7 +15,7 @@ exports.isNonTraceabilityJSDocTagLine = isNonTraceabilityJSDocTagLine;
15
15
  * boundaries), keeps any annotation tags that appear later in the line, and
16
16
  * supports common JSDoc styles such as leading "*".
17
17
  *
18
- * It detects @story, @req, and @supports tags while preserving the rest
18
+ * It detects `@story`, `@req`, and `@supports` tags while preserving the rest
19
19
  * of the line for downstream logic.
20
20
  */
21
21
  function normalizeCommentLine(rawLine) {
@@ -40,7 +40,7 @@ function normalizeCommentLine(rawLine) {
40
40
  * Detect whether a normalized comment line starts with a non-traceability JSDoc tag.
41
41
  *
42
42
  * This is used to distinguish regular JSDoc tags (e.g. @param, @returns) from
43
- * traceability-related annotations such as @story, @req, and @supports.
43
+ * traceability-related annotations such as `@story`, `@req`, and `@supports`.
44
44
  *
45
45
  * Supports coexistence with JSDoc by:
46
46
  * - Detecting boundaries between traceability tags and other tags
@@ -7,9 +7,9 @@
7
7
  * to read while still preserving all existing behavior.
8
8
  *
9
9
  * The implementation in this module supports:
10
- * - validation of @story annotations
11
- * - validation of @req annotations
12
- * - validation of @implements/@supports-style annotations
10
+ * - validation of `@story` annotations
11
+ * - validation of `@req` annotations
12
+ * - validation of `@implements`/`@supports`-style annotations
13
13
  * - safe, minimal auto-fixes for certain invalid formats
14
14
  *
15
15
  * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
@@ -33,9 +33,9 @@
33
33
  import type { ResolvedAnnotationOptions } from "./valid-annotation-options";
34
34
  import type { PendingAnnotation } from "./valid-annotation-format-internal";
35
35
  /**
36
- * Report an invalid @story annotation without applying a fix.
36
+ * Report an invalid `@story` annotation without applying a fix.
37
37
  *
38
- * The invalid @story annotation is detected and reported but left unchanged.
38
+ * The invalid `@story` annotation is detected and reported but left unchanged.
39
39
  *
40
40
  * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
41
41
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
@@ -44,10 +44,10 @@ import type { PendingAnnotation } from "./valid-annotation-format-internal";
44
44
  */
45
45
  export declare function reportInvalidStoryFormat(context: any, comment: any, collapsed: string, options: ResolvedAnnotationOptions): void;
46
46
  /**
47
- * Compute the text replacement for an invalid @story annotation within a comment.
47
+ * Compute the text replacement for an invalid `@story` annotation within a comment.
48
48
  *
49
49
  * This helper:
50
- * - finds the @story tag in the raw comment text,
50
+ * - finds the `@story` tag in the raw comment text,
51
51
  * - computes the character range of its value,
52
52
  * - and returns an ESLint fix that replaces only that range.
53
53
  *
@@ -59,7 +59,7 @@ export declare function reportInvalidStoryFormat(context: any, comment: any, col
59
59
  */
60
60
  export declare function createStoryFix(context: any, comment: any, fixed: string): null | (() => any);
61
61
  /**
62
- * Report an invalid @story annotation and attempt a minimal, safe auto-fix
62
+ * Report an invalid `@story` annotation and attempt a minimal, safe auto-fix
63
63
  * for common path suffix issues by locating and replacing the path text
64
64
  * within the original comment.
65
65
  *
@@ -76,10 +76,10 @@ export declare function createStoryFix(context: any, comment: any, fixed: string
76
76
  */
77
77
  export declare function reportInvalidStoryFormatWithFix(context: any, comment: any, collapsed: string, fixed: string): void;
78
78
  /**
79
- * Validate a @story annotation value and report detailed errors when needed.
79
+ * Validate a `@story` annotation value and report detailed errors when needed.
80
80
  * Where safe and unambiguous, apply an automatic fix for missing suffixes.
81
81
  *
82
- * Processing of @story values includes:
82
+ * Processing of `@story` values includes:
83
83
  * - trimming whitespace,
84
84
  * - collapsing multi-line text,
85
85
  * - matching against the configured story regex,
@@ -97,7 +97,7 @@ export declare function reportInvalidStoryFormatWithFix(context: any, comment: a
97
97
  */
98
98
  export declare function validateStoryAnnotation(context: any, comment: any, rawValue: string, options: ResolvedAnnotationOptions): void;
99
99
  /**
100
- * Validate a @req annotation value and report detailed errors when needed.
100
+ * Validate a `@req` annotation value and report detailed errors when needed.
101
101
  *
102
102
  * This behavior covers:
103
103
  * - detecting missing identifiers,
@@ -115,14 +115,14 @@ export declare function validateStoryAnnotation(context: any, comment: any, rawV
115
115
  */
116
116
  export declare function validateReqAnnotation(context: any, comment: any, rawValue: string, options: ResolvedAnnotationOptions): void;
117
117
  /**
118
- * Validate an @supports annotation value and report detailed errors when needed.
118
+ * Validate an `@supports` annotation value and report detailed errors when needed.
119
119
  *
120
120
  * Expected format:
121
- * @supports <storyPath> <REQ-ID> [<REQ-ID> ...]
121
+ * `@supports <storyPath> <REQ-ID> [<REQ-ID> ...]`
122
122
  *
123
123
  * Validation rules:
124
124
  * - Value must include at least a story path and one requirement ID.
125
- * - Story path must match the same storyPattern used for @story (no auto-fix).
125
+ * - Story path must match the same storyPattern used for `@story` (no auto-fix).
126
126
  * - Each subsequent token must match reqPattern and is validated individually.
127
127
  *
128
128
  * Story path issues are reported with "invalidImplementsFormat" and
@@ -8,9 +8,9 @@
8
8
  * to read while still preserving all existing behavior.
9
9
  *
10
10
  * The implementation in this module supports:
11
- * - validation of @story annotations
12
- * - validation of @req annotations
13
- * - validation of @implements/@supports-style annotations
11
+ * - validation of `@story` annotations
12
+ * - validation of `@req` annotations
13
+ * - validation of `@implements`/`@supports`-style annotations
14
14
  * - safe, minimal auto-fixes for certain invalid formats
15
15
  *
16
16
  * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
@@ -43,9 +43,9 @@ const valid_annotation_utils_1 = require("./valid-annotation-utils");
43
43
  const valid_implements_utils_1 = require("./valid-implements-utils");
44
44
  const valid_annotation_options_1 = require("./valid-annotation-options");
45
45
  /**
46
- * Report an invalid @story annotation without applying a fix.
46
+ * Report an invalid `@story` annotation without applying a fix.
47
47
  *
48
- * The invalid @story annotation is detected and reported but left unchanged.
48
+ * The invalid `@story` annotation is detected and reported but left unchanged.
49
49
  *
50
50
  * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
51
51
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
@@ -60,10 +60,10 @@ function reportInvalidStoryFormat(context, comment, collapsed, options) {
60
60
  });
61
61
  }
62
62
  /**
63
- * Compute the text replacement for an invalid @story annotation within a comment.
63
+ * Compute the text replacement for an invalid `@story` annotation within a comment.
64
64
  *
65
65
  * This helper:
66
- * - finds the @story tag in the raw comment text,
66
+ * - finds the `@story` tag in the raw comment text,
67
67
  * - computes the character range of its value,
68
68
  * - and returns an ESLint fix that replaces only that range.
69
69
  *
@@ -103,7 +103,7 @@ function createStoryFix(context, comment, fixed) {
103
103
  return () => (fixer) => fixer.replaceTextRange(fixRange, fixed);
104
104
  }
105
105
  /**
106
- * Report an invalid @story annotation and attempt a minimal, safe auto-fix
106
+ * Report an invalid `@story` annotation and attempt a minimal, safe auto-fix
107
107
  * for common path suffix issues by locating and replacing the path text
108
108
  * within the original comment.
109
109
  *
@@ -136,10 +136,10 @@ function reportInvalidStoryFormatWithFix(context, comment, collapsed, fixed) {
136
136
  });
137
137
  }
138
138
  /**
139
- * Validate a @story annotation value and report detailed errors when needed.
139
+ * Validate a `@story` annotation value and report detailed errors when needed.
140
140
  * Where safe and unambiguous, apply an automatic fix for missing suffixes.
141
141
  *
142
- * Processing of @story values includes:
142
+ * Processing of `@story` values includes:
143
143
  * - trimming whitespace,
144
144
  * - collapsing multi-line text,
145
145
  * - matching against the configured story regex,
@@ -194,7 +194,7 @@ function validateStoryAnnotation(context, comment, rawValue, options) {
194
194
  reportInvalidStoryFormat(context, comment, collapsed, options);
195
195
  }
196
196
  /**
197
- * Validate a @req annotation value and report detailed errors when needed.
197
+ * Validate a `@req` annotation value and report detailed errors when needed.
198
198
  *
199
199
  * This behavior covers:
200
200
  * - detecting missing identifiers,
@@ -222,33 +222,42 @@ function validateReqAnnotation(context, comment, rawValue, options) {
222
222
  });
223
223
  return;
224
224
  }
225
- const collapsed = (0, valid_annotation_utils_1.collapseAnnotationValue)(trimmed);
226
- // Allow mixed @req/@supports lines to pass without additional @req validation,
227
- // while still validating simple multi-line @req identifiers that collapse
228
- // to a single token.
229
- if (collapsed.includes("@supports")) {
225
+ // Allow mixed `@req`/`@supports` lines to pass without additional `@req` validation.
226
+ if (trimmed.includes("@supports")) {
230
227
  return;
231
228
  }
232
- const reqPattern = options.reqPattern;
229
+ const tokens = trimmed.split(/\s+/).filter(Boolean);
230
+ let reqId = tokens[0] || trimmed;
231
+ for (let index = 1; index < tokens.length; index += 1) {
232
+ const token = tokens[index];
233
+ if (token === "-" || token === "–" || token === "—")
234
+ break;
235
+ const candidate = `${reqId}${token}`;
236
+ if (reqId.endsWith("-") || options.reqPattern.test(candidate)) {
237
+ reqId = candidate;
238
+ continue;
239
+ }
240
+ break;
241
+ }
233
242
  // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
234
243
  // @req REQ-REQ-FORMAT - Flag @req identifiers that do not match the configured pattern
235
- if (!reqPattern.test(collapsed)) {
244
+ if (!options.reqPattern.test(reqId)) {
236
245
  context.report({
237
246
  node: comment,
238
247
  messageId: "invalidReqFormat",
239
- data: { details: (0, valid_annotation_utils_1.buildReqErrorMessage)("invalid", collapsed, options) },
248
+ data: { details: (0, valid_annotation_utils_1.buildReqErrorMessage)("invalid", reqId, options) },
240
249
  });
241
250
  }
242
251
  }
243
252
  /**
244
- * Validate an @supports annotation value and report detailed errors when needed.
253
+ * Validate an `@supports` annotation value and report detailed errors when needed.
245
254
  *
246
255
  * Expected format:
247
- * @supports <storyPath> <REQ-ID> [<REQ-ID> ...]
256
+ * `@supports <storyPath> <REQ-ID> [<REQ-ID> ...]`
248
257
  *
249
258
  * Validation rules:
250
259
  * - Value must include at least a story path and one requirement ID.
251
- * - Story path must match the same storyPattern used for @story (no auto-fix).
260
+ * - Story path must match the same storyPattern used for `@story` (no auto-fix).
252
261
  * - Each subsequent token must match reqPattern and is validated individually.
253
262
  *
254
263
  * Story path issues are reported with "invalidImplementsFormat" and
@@ -5,6 +5,7 @@ exports.collapseAnnotationValue = collapseAnnotationValue;
5
5
  exports.getFixedStoryPath = getFixedStoryPath;
6
6
  exports.buildStoryErrorMessage = buildStoryErrorMessage;
7
7
  exports.buildReqErrorMessage = buildReqErrorMessage;
8
+ /* eslint-disable traceability/valid-annotation-format */
8
9
  const valid_annotation_options_1 = require("./valid-annotation-options");
9
10
  /**
10
11
  * Shared constants and helpers for annotation-format validation.
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.createValidReqReferenceProgramVisitor = createValidReqReferenceProgramVisitor;
7
+ /* eslint-disable traceability/valid-annotation-format */
7
8
  /* eslint-env node */
8
9
  /**
9
10
  * Helper utilities for the "valid-req-reference" rule.
@@ -7,9 +7,8 @@
7
7
  * @req REQ-CONFIGURABLE-SCOPE - Allow configuration of which exports are checked
8
8
  * @req REQ-EXPORT-PRIORITY - Allow configuration of export priority behavior
9
9
  *
10
- * Note: This rule accepts annotationPlacement for configuration parity with
11
- * require-story-annotation, but currently still requires annotations before
12
- * the function (no inside-function support yet).
10
+ * This rule honors the same `annotationPlacement` semantics as
11
+ * `require-story-annotation` for block-bodied functions and methods.
13
12
  */
14
13
  import type { Rule } from "eslint";
15
14
  declare const rule: Rule.RuleModule;