eslint-plugin-traceability 1.13.1 → 1.14.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 (29) hide show
  1. package/CHANGELOG.md +2 -2
  2. package/lib/src/rules/helpers/require-story-core.js +1 -0
  3. package/lib/src/rules/helpers/require-story-helpers.d.ts +4 -0
  4. package/lib/src/rules/helpers/require-story-helpers.js +92 -3
  5. package/lib/src/rules/helpers/require-test-traceability-helpers.js +24 -0
  6. package/lib/src/rules/helpers/valid-annotation-options.d.ts +32 -1
  7. package/lib/src/rules/helpers/valid-annotation-options.js +144 -1
  8. package/lib/src/rules/helpers/valid-implements-utils.js +13 -5
  9. package/lib/src/rules/no-redundant-annotation.js +12 -0
  10. package/lib/src/rules/prefer-implements-annotation.js +176 -5
  11. package/lib/src/rules/require-branch-annotation.js +73 -1
  12. package/lib/src/rules/require-test-traceability.js +8 -0
  13. package/lib/src/rules/valid-req-reference.js +4 -0
  14. package/lib/src/rules/valid-story-reference.d.ts +9 -0
  15. package/lib/src/rules/valid-story-reference.js +9 -0
  16. package/lib/src/utils/branch-annotation-helpers.d.ts +12 -10
  17. package/lib/src/utils/branch-annotation-helpers.js +31 -140
  18. package/lib/src/utils/branch-annotation-loop-helpers.d.ts +9 -0
  19. package/lib/src/utils/branch-annotation-loop-helpers.js +36 -0
  20. package/lib/src/utils/branch-annotation-report-helpers.d.ts +11 -0
  21. package/lib/src/utils/branch-annotation-report-helpers.js +111 -0
  22. package/lib/tests/integration/dogfooding-validation.test.js +5 -2
  23. package/lib/tests/rules/prefer-implements-annotation.test.js +23 -0
  24. package/lib/tests/rules/require-branch-annotation.test.js +88 -19
  25. package/lib/tests/rules/require-story-annotation.test.js +56 -8
  26. package/lib/tests/utils/temp-dir-helpers.d.ts +6 -1
  27. package/lib/tests/utils/temp-dir-helpers.js +2 -1
  28. package/package.json +1 -1
  29. package/user-docs/api-reference.md +1 -1
package/CHANGELOG.md CHANGED
@@ -1,9 +1,9 @@
1
- ## [1.13.1](https://github.com/voder-ai/eslint-plugin-traceability/compare/v1.13.0...v1.13.1) (2025-12-08)
1
+ ## [1.14.1](https://github.com/voder-ai/eslint-plugin-traceability/compare/v1.14.0...v1.14.1) (2025-12-08)
2
2
 
3
3
 
4
4
  ### Bug Fixes
5
5
 
6
- * refine no-redundant-annotation rule tests and behavior ([7d72670](https://github.com/voder-ai/eslint-plugin-traceability/commit/7d726702e3ad2268778c06de3f3a9673033e3a61))
6
+ * implement branch and function behaviors for branch annotations story ([00f9655](https://github.com/voder-ai/eslint-plugin-traceability/commit/00f9655c8a284278d69c7d09d106a340ada57827))
7
7
 
8
8
  # Changelog
9
9
 
@@ -82,6 +82,7 @@ function createMethodFix(node, annotationTemplate) {
82
82
  exports.DEFAULT_SCOPE = [
83
83
  "FunctionDeclaration",
84
84
  "FunctionExpression",
85
+ "ArrowFunctionExpression",
85
86
  "MethodDefinition",
86
87
  "TSMethodSignature",
87
88
  "TSDeclareFunction",
@@ -18,6 +18,7 @@ interface ReportOptions {
18
18
  annotationTemplateOverride?: string;
19
19
  autoFixToggle?: boolean;
20
20
  }
21
+ /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
21
22
  declare function getAnnotationTemplate(override?: string): string;
22
23
  declare function shouldApplyAutoFix(autoFix: boolean | undefined): boolean;
23
24
  /**
@@ -63,12 +64,15 @@ declare function resolveTargetNode(sourceCode: any, node: any): any;
63
64
  * @req REQ-ANNOTATION-REQUIRED - Walk node and parents to find Identifier/Key name
64
65
  */
65
66
  declare function extractName(node: any): string;
67
+ /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
66
68
  declare function shouldProcessNode(node: any, scope: string[], exportPriority?: string): boolean;
69
+ /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
67
70
  declare function reportMissing(context: Rule.RuleContext, sourceCode: any, config: {
68
71
  node: any;
69
72
  target?: any;
70
73
  options?: ReportOptions;
71
74
  }): void;
75
+ /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
72
76
  declare function reportMethod(context: Rule.RuleContext, sourceCode: any, config: {
73
77
  node: any;
74
78
  target?: any;
@@ -23,6 +23,7 @@ const require_story_core_1 = require("./require-story-core");
23
23
  Object.defineProperty(exports, "DEFAULT_SCOPE", { enumerable: true, get: function () { return require_story_core_1.DEFAULT_SCOPE; } });
24
24
  Object.defineProperty(exports, "EXPORT_PRIORITY_VALUES", { enumerable: true, get: function () { return require_story_core_1.EXPORT_PRIORITY_VALUES; } });
25
25
  Object.defineProperty(exports, "STORY_PATH", { enumerable: true, get: function () { return require_story_core_1.STORY_PATH; } });
26
+ /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
26
27
  function getAnnotationTemplate(override) {
27
28
  if (typeof override === "string" && override.trim().length > 0) {
28
29
  return override.trim();
@@ -39,11 +40,81 @@ function shouldApplyAutoFix(autoFix) {
39
40
  * Build the effective annotation template and autofix toggle
40
41
  * from the provided report options.
41
42
  */
43
+ /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
42
44
  function buildTemplateConfig(options) {
43
45
  const effectiveTemplate = getAnnotationTemplate(options?.annotationTemplateOverride);
44
46
  const allowFix = shouldApplyAutoFix(options?.autoFixToggle);
45
47
  return { effectiveTemplate, allowFix };
46
48
  }
49
+ /**
50
+ * Determine whether a node represents an anonymous arrow function expression
51
+ * where the parent variable declarator has no explicit Identifier name.
52
+ *
53
+ * @supports docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md REQ-ARROW-FUNCTION-EXCLUDED
54
+ */
55
+ function isAnonymousArrowFunction(node) {
56
+ return !!node && node.type === "ArrowFunctionExpression";
57
+ }
58
+ /**
59
+ * Determine whether a function-like node is nested within another function.
60
+ *
61
+ * @supports docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md REQ-NESTED-FUNCTION-INHERITANCE
62
+ */
63
+ function isNestedFunction(node) {
64
+ let current = node?.parent;
65
+ while (current) {
66
+ if (current.type === "FunctionDeclaration" ||
67
+ current.type === "FunctionExpression" ||
68
+ current.type === "ArrowFunctionExpression" ||
69
+ current.type === "MethodDefinition" ||
70
+ current.type === "TSDeclareFunction" ||
71
+ current.type === "TSMethodSignature") {
72
+ return true;
73
+ }
74
+ current = current.parent;
75
+ }
76
+ return false;
77
+ }
78
+ /**
79
+ * Determine whether a function-like node is effectively anonymous for the
80
+ * purposes of nested-function inheritance. Named functions must always carry
81
+ * their own annotations, while anonymous nested functions may inherit.
82
+ *
83
+ * @supports docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md REQ-NESTED-FUNCTION-INHERITANCE
84
+ */
85
+ function isEffectivelyAnonymousFunction(node) {
86
+ const name = getContainerKeyOrIdName(node) ?? getDirectIdentifierName(node);
87
+ if (typeof name === "string" && name.length > 0 && name !== "(anonymous)") {
88
+ return false;
89
+ }
90
+ return true;
91
+ }
92
+ /**
93
+ * Determine whether a function node is required to carry its own annotation
94
+ * according to Story 004.0-DEV-BRANCH-ANNOTATIONS rules.
95
+ *
96
+ * - Anonymous arrow functions used as callbacks are excluded from
97
+ * function-level annotation requirements.
98
+ * - Named arrow functions must be annotated.
99
+ * - Nested anonymous functions may inherit their parent function's
100
+ * annotation and therefore are not required to be annotated directly.
101
+ * - Named nested functions must always carry their own explicit annotations.
102
+ *
103
+ * @supports docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md REQ-ARROW-FUNCTION-EXCLUDED REQ-NESTED-FUNCTION-INHERITANCE
104
+ */
105
+ function requiresOwnFunctionAnnotation(node) {
106
+ // Anonymous arrow functions used as callbacks are excluded from function-level
107
+ // requirements when they are nested inside another function or method.
108
+ if (isAnonymousArrowFunction(node) &&
109
+ isNestedFunction(node) &&
110
+ isEffectivelyAnonymousFunction(node)) {
111
+ return false;
112
+ }
113
+ if (isNestedFunction(node) && isEffectivelyAnonymousFunction(node)) {
114
+ return false;
115
+ }
116
+ return true;
117
+ }
47
118
  /**
48
119
  * Determine if a node is in an export declaration
49
120
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
@@ -105,6 +176,7 @@ function leadingCommentsHasStory(node) {
105
176
  */
106
177
  function hasStoryAnnotation(sourceCode, node) {
107
178
  try {
179
+ // Direct, node-local checks always apply first.
108
180
  if (jsdocHasStory(sourceCode, node)) {
109
181
  return true;
110
182
  }
@@ -114,13 +186,20 @@ function hasStoryAnnotation(sourceCode, node) {
114
186
  if (leadingCommentsHasStory(node)) {
115
187
  return true;
116
188
  }
117
- if ((0, require_story_io_1.linesBeforeHasStory)(sourceCode, node)) {
189
+ if (!isNestedFunction(node) && (0, require_story_io_1.linesBeforeHasStory)(sourceCode, node)) {
190
+ return true;
191
+ }
192
+ const canInherit = isNestedFunction(node) && isEffectivelyAnonymousFunction(node);
193
+ // Only nodes that are allowed to inherit annotations (e.g., nested anonymous
194
+ // callbacks) may treat parent-chain comments or broad fallback text as
195
+ // satisfying the annotation requirement.
196
+ if (canInherit && (0, require_story_io_1.parentChainHasStory)(sourceCode, node)) {
118
197
  return true;
119
198
  }
120
- if ((0, require_story_io_1.parentChainHasStory)(sourceCode, node)) {
199
+ if (canInherit && (0, require_story_io_1.fallbackTextBeforeHasStory)(sourceCode, node)) {
121
200
  return true;
122
201
  }
123
- if ((0, require_story_io_1.fallbackTextBeforeHasStory)(sourceCode, node)) {
202
+ if (canInherit) {
124
203
  return true;
125
204
  }
126
205
  }
@@ -226,7 +305,15 @@ function extractName(node) {
226
305
  }
227
306
  return "(anonymous)";
228
307
  }
308
+ /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
229
309
  function shouldProcessNode(node, scope, exportPriority = "all") {
310
+ if (node &&
311
+ (node.type === "FunctionDeclaration" ||
312
+ node.type === "FunctionExpression" ||
313
+ node.type === "ArrowFunctionExpression") &&
314
+ !requiresOwnFunctionAnnotation(node)) {
315
+ return false;
316
+ }
230
317
  if (!scope.includes(node.type)) {
231
318
  return false;
232
319
  }
@@ -271,6 +358,7 @@ function getNameNodeForReport(node) {
271
358
  function resolveAnnotationTargetNode(sourceCode, node, passedTarget) {
272
359
  return passedTarget ?? resolveTargetNode(sourceCode, node);
273
360
  }
361
+ /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
274
362
  function reportMissing(context, sourceCode, config) {
275
363
  (0, require_story_core_1.coreReportMissing)({
276
364
  hasStoryAnnotation,
@@ -285,6 +373,7 @@ function reportMissing(context, sourceCode, config) {
285
373
  createMethodFix: require_story_core_1.createMethodFix,
286
374
  }, context, sourceCode, config);
287
375
  }
376
+ /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
288
377
  function reportMethod(context, sourceCode, config) {
289
378
  (0, require_story_core_1.coreReportMethod)({
290
379
  hasStoryAnnotation,
@@ -94,6 +94,11 @@ function ensureFileSupportsAnnotation(context, sourceCode, autoFixOptions) {
94
94
  function isTestCallName(name) {
95
95
  return ["describe", "it", "test", "context"].includes(name);
96
96
  }
97
+ /**
98
+ * Extract the test framework call name from a CallExpression callee.
99
+ *
100
+ * @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-FRAMEWORK-COMPAT
101
+ */
97
102
  function getCalleeName(node) {
98
103
  if (node.callee.type === "Identifier") {
99
104
  return node.callee.name;
@@ -104,6 +109,11 @@ function getCalleeName(node) {
104
109
  }
105
110
  return null;
106
111
  }
112
+ /**
113
+ * Extract the first string literal argument from a CallExpression, if present.
114
+ *
115
+ * @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-DESCRIBE-STORY REQ-TEST-IT-REQ-PREFIX
116
+ */
107
117
  function getFirstArgumentLiteral(node) {
108
118
  const arg = node.arguments && node.arguments[0];
109
119
  if (!arg)
@@ -189,6 +199,13 @@ function createUpdatedStringLiteralRaw(originalNode, newValue, sourceCode) {
189
199
  // Fallback: let JSON.stringify choose a safe representation.
190
200
  return JSON.stringify(newValue);
191
201
  }
202
+ /**
203
+ * Validate describe() calls to ensure they include a story reference
204
+ * matching the configured describeRegex when required.
205
+ *
206
+ * @story docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md
207
+ * @req REQ-TEST-DESCRIBE-STORY
208
+ */
192
209
  function handleDescribeCall(context, node, description, options) {
193
210
  const { describeRegex, requireDescribeStory } = options;
194
211
  if (!requireDescribeStory)
@@ -200,6 +217,13 @@ function handleDescribeCall(context, node, description, options) {
200
217
  });
201
218
  }
202
219
  }
220
+ /**
221
+ * Validate it() and test() calls to ensure their descriptions start with a
222
+ * [REQ-XXX] prefix, optionally normalizing malformed prefixes when enabled.
223
+ *
224
+ * @story docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md
225
+ * @req REQ-TEST-IT-REQ-PREFIX
226
+ */
203
227
  function handleItOrTestCall(context, node, description, options) {
204
228
  const { sourceCode, requireTestReqPrefix, autoFixTestPrefixFormat } = options;
205
229
  if (!requireTestReqPrefix)
@@ -69,16 +69,47 @@ export interface ResolvedAnnotationOptions {
69
69
  reqExample: string;
70
70
  autoFix: boolean;
71
71
  }
72
+ /**
73
+ * Get the default requirement ID example string used in error messages.
74
+ *
75
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
76
+ * @req REQ-PATTERN-CONFIG - Provide a default requirement ID example value
77
+ */
72
78
  export declare function getDefaultReqExample(): string;
79
+ /**
80
+ * Expose the most recently resolved options so other helpers can reuse
81
+ * the same defaults without re-resolving configuration.
82
+ *
83
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
84
+ * @req REQ-PATTERN-CONFIG - Share resolved default patterns across helpers
85
+ * @req REQ-BACKWARD-COMPAT - Maintain a stable default configuration
86
+ */
73
87
  export declare function getResolvedDefaults(): ResolvedAnnotationOptions;
88
+ /**
89
+ * Retrieve an array of configuration error messages collected during
90
+ * option resolution, typically regex validation failures.
91
+ *
92
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
93
+ * @req REQ-PATTERN-CONFIG - Surface configuration problems to callers
94
+ */
74
95
  export declare function getOptionErrors(): string[];
75
96
  /**
76
97
  * Resolve user options into concrete, validated configuration.
77
98
  * Falls back to existing defaults when options are not provided or invalid.
99
+ *
100
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
101
+ * @req REQ-PATTERN-CONFIG - Drive rule behavior from configurable patterns
102
+ * @req REQ-REGEX-VALIDATION - Validate all configured regex patterns
103
+ * @req REQ-BACKWARD-COMPAT - Maintain behavior when no custom options set
78
104
  */
79
105
  export declare function resolveOptions(rawOptions: unknown[]): ResolvedAnnotationOptions;
80
106
  /**
81
- * Build the JSON schema for rule options.
107
+ * Build the JSON Schema definition that validates rule configuration
108
+ * passed to ESLint, ensuring option shapes and types are correct.
109
+ *
110
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
111
+ * @req REQ-SCHEMA-VALIDATION - Define a JSON Schema for rule options
112
+ * @req REQ-PATTERN-CONFIG - Describe configurable pattern and example fields
82
113
  */
83
114
  export declare function getRuleSchema(): {
84
115
  type: string;
@@ -5,15 +5,39 @@ exports.getResolvedDefaults = getResolvedDefaults;
5
5
  exports.getOptionErrors = getOptionErrors;
6
6
  exports.resolveOptions = resolveOptions;
7
7
  exports.getRuleSchema = getRuleSchema;
8
+ /**
9
+ * Get the default regular expression used to validate story paths.
10
+ *
11
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
12
+ * @req REQ-PATTERN-CONFIG - Provide a default story path pattern
13
+ */
8
14
  function getDefaultStoryPattern() {
9
15
  return /^docs\/stories\/[0-9]+\.[0-9]+-DEV-[\w-]+\.story\.md$/;
10
16
  }
17
+ /**
18
+ * Get the default story example string used in error messages.
19
+ *
20
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
21
+ * @req REQ-PATTERN-CONFIG - Provide a default story example value
22
+ */
11
23
  function getDefaultStoryExample() {
12
24
  return "docs/stories/005.0-DEV-EXAMPLE.story.md";
13
25
  }
26
+ /**
27
+ * Get the default regular expression used to validate requirement IDs.
28
+ *
29
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
30
+ * @req REQ-PATTERN-CONFIG - Provide a default requirement ID pattern
31
+ */
14
32
  function getDefaultReqPattern() {
15
33
  return /^REQ-[A-Z0-9-]+$/;
16
34
  }
35
+ /**
36
+ * Get the default requirement ID example string used in error messages.
37
+ *
38
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
39
+ * @req REQ-PATTERN-CONFIG - Provide a default requirement ID example value
40
+ */
17
41
  function getDefaultReqExample() {
18
42
  return "REQ-EXAMPLE";
19
43
  }
@@ -32,15 +56,33 @@ let resolvedDefaults = {
32
56
  * Collected configuration errors encountered while resolving options.
33
57
  */
34
58
  let optionErrors = [];
59
+ /**
60
+ * Expose the most recently resolved options so other helpers can reuse
61
+ * the same defaults without re-resolving configuration.
62
+ *
63
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
64
+ * @req REQ-PATTERN-CONFIG - Share resolved default patterns across helpers
65
+ * @req REQ-BACKWARD-COMPAT - Maintain a stable default configuration
66
+ */
35
67
  function getResolvedDefaults() {
36
68
  return resolvedDefaults;
37
69
  }
70
+ /**
71
+ * Retrieve an array of configuration error messages collected during
72
+ * option resolution, typically regex validation failures.
73
+ *
74
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
75
+ * @req REQ-PATTERN-CONFIG - Surface configuration problems to callers
76
+ */
38
77
  function getOptionErrors() {
39
78
  return optionErrors;
40
79
  }
41
80
  /**
42
81
  * Build a stable, engine-independent configuration error message
43
82
  * for invalid regex options.
83
+ *
84
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
85
+ * @req REQ-PATTERN-CONFIG - Provide consistent regex validation diagnostics
44
86
  */
45
87
  function buildInvalidRegexError(field, pattern) {
46
88
  return `Invalid regular expression for option "${field}": "${pattern}"`;
@@ -105,13 +147,38 @@ function resolveExample(nestedExample, flatExample, defaultExample) {
105
147
  }
106
148
  return defaultExample;
107
149
  }
150
+ /**
151
+ * Extract and normalize user-provided options from the raw ESLint
152
+ * options array into an AnnotationRuleOptions object.
153
+ *
154
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
155
+ * @req REQ-PATTERN-CONFIG - Accept structured configuration for patterns
156
+ * @req REQ-BACKWARD-COMPAT - Tolerate missing or malformed options
157
+ */
108
158
  function getUserOptions(rawOptions) {
109
159
  return normalizeUserOptions(rawOptions);
110
160
  }
161
+ /**
162
+ * Resolve the auto-fix flag, defaulting to true when the option
163
+ * is not explicitly provided by the user.
164
+ *
165
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
166
+ * @req REQ-PATTERN-CONFIG - Support configuration of fix behavior
167
+ * @req REQ-BACKWARD-COMPAT - Preserve default auto-fix behavior
168
+ */
111
169
  function resolveAutoFixFlag(user) {
112
170
  const autoFixFlag = user?.autoFix;
113
171
  return typeof autoFixFlag === "boolean" ? autoFixFlag : true;
114
172
  }
173
+ /**
174
+ * Resolve the story path pattern from nested or flat configuration
175
+ * fields, validating and falling back to the default as needed.
176
+ *
177
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
178
+ * @req REQ-PATTERN-CONFIG - Allow configurable story path patterns
179
+ * @req REQ-REGEX-VALIDATION - Validate story path regex options
180
+ * @req REQ-BACKWARD-COMPAT - Use a default when no pattern is provided
181
+ */
115
182
  function resolveStoryPattern(nestedStoryPattern, flatStoryPattern) {
116
183
  return resolvePattern({
117
184
  nestedPattern: nestedStoryPattern,
@@ -121,6 +188,15 @@ function resolveStoryPattern(nestedStoryPattern, flatStoryPattern) {
121
188
  defaultPattern: getDefaultStoryPattern(),
122
189
  });
123
190
  }
191
+ /**
192
+ * Resolve the requirement ID pattern from nested or flat configuration
193
+ * fields, validating and falling back to the default as needed.
194
+ *
195
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
196
+ * @req REQ-PATTERN-CONFIG - Allow configurable requirement ID patterns
197
+ * @req REQ-REGEX-VALIDATION - Validate requirement ID regex options
198
+ * @req REQ-BACKWARD-COMPAT - Use a default when no pattern is provided
199
+ */
124
200
  function resolveReqPattern(nestedReqPattern, flatReqPattern) {
125
201
  return resolvePattern({
126
202
  nestedPattern: nestedReqPattern,
@@ -130,36 +206,93 @@ function resolveReqPattern(nestedReqPattern, flatReqPattern) {
130
206
  defaultPattern: getDefaultReqPattern(),
131
207
  });
132
208
  }
209
+ /**
210
+ * Resolve the story example string from nested or flat configuration
211
+ * fields, preferring user-provided values and falling back to the default.
212
+ *
213
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
214
+ * @req REQ-EXAMPLE-MESSAGES - Allow custom story examples in messages
215
+ * @req REQ-BACKWARD-COMPAT - Use a default story example when omitted
216
+ */
133
217
  function resolveStoryExample(nestedStoryExample, flatStoryExample) {
134
218
  return resolveExample(nestedStoryExample, flatStoryExample, getDefaultStoryExample());
135
219
  }
220
+ /**
221
+ * Resolve the requirement ID example string from nested or flat configuration
222
+ * fields, preferring user-provided values and falling back to the default.
223
+ *
224
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
225
+ * @req REQ-EXAMPLE-MESSAGES - Allow custom requirement ID examples in messages
226
+ * @req REQ-BACKWARD-COMPAT - Use a default requirement ID example when omitted
227
+ */
136
228
  function resolveReqExample(nestedReqExample, flatReqExample) {
137
229
  return resolveExample(nestedReqExample, flatReqExample, getDefaultReqExample());
138
230
  }
231
+ /**
232
+ * Collect user-provided story pattern inputs from both nested and flat
233
+ * configuration fields to support backward-compatible shapes.
234
+ *
235
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
236
+ * @req REQ-PATTERN-CONFIG - Read story patterns from multiple option shapes
237
+ * @req REQ-BACKWARD-COMPAT - Support legacy flat storyPathPattern
238
+ */
139
239
  function getStoryPatternInputs(user) {
140
240
  return {
141
241
  nestedStoryPattern: user?.story?.pattern,
142
242
  flatStoryPattern: user?.storyPathPattern,
143
243
  };
144
244
  }
245
+ /**
246
+ * Collect user-provided story example inputs from both nested and flat
247
+ * configuration fields to support backward-compatible shapes.
248
+ *
249
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
250
+ * @req REQ-EXAMPLE-MESSAGES - Read story examples from multiple option shapes
251
+ * @req REQ-BACKWARD-COMPAT - Support legacy flat storyPathExample
252
+ */
145
253
  function getStoryExampleInputs(user) {
146
254
  return {
147
255
  nestedStoryExample: user?.story?.example,
148
256
  flatStoryExample: user?.storyPathExample,
149
257
  };
150
258
  }
259
+ /**
260
+ * Collect user-provided requirement ID pattern inputs from both nested
261
+ * and flat configuration fields to support backward-compatible shapes.
262
+ *
263
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
264
+ * @req REQ-PATTERN-CONFIG - Read requirement ID patterns from multiple shapes
265
+ * @req REQ-BACKWARD-COMPAT - Support legacy flat requirementIdPattern
266
+ */
151
267
  function getReqPatternInputs(user) {
152
268
  return {
153
269
  nestedReqPattern: user?.req?.pattern,
154
270
  flatReqPattern: user?.requirementIdPattern,
155
271
  };
156
272
  }
273
+ /**
274
+ * Collect user-provided requirement ID example inputs from both nested
275
+ * and flat configuration fields to support backward-compatible shapes.
276
+ *
277
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
278
+ * @req REQ-EXAMPLE-MESSAGES - Read requirement ID examples from multiple shapes
279
+ * @req REQ-BACKWARD-COMPAT - Support legacy flat requirementIdExample
280
+ */
157
281
  function getReqExampleInputs(user) {
158
282
  return {
159
283
  nestedReqExample: user?.req?.example,
160
284
  flatReqExample: user?.requirementIdExample,
161
285
  };
162
286
  }
287
+ /**
288
+ * Internal helper to resolve all rule options into a concrete, validated
289
+ * ResolvedAnnotationOptions structure, applying defaults and validation.
290
+ *
291
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
292
+ * @req REQ-PATTERN-CONFIG - Combine pattern and example configuration
293
+ * @req REQ-REGEX-VALIDATION - Enforce regex validity during resolution
294
+ * @req REQ-BACKWARD-COMPAT - Respect defaults when options are missing
295
+ */
163
296
  function resolveOptionsInternal(user) {
164
297
  const { nestedStoryPattern, flatStoryPattern } = getStoryPatternInputs(user);
165
298
  const { nestedStoryExample, flatStoryExample } = getStoryExampleInputs(user);
@@ -181,6 +314,11 @@ function resolveOptionsInternal(user) {
181
314
  /**
182
315
  * Resolve user options into concrete, validated configuration.
183
316
  * Falls back to existing defaults when options are not provided or invalid.
317
+ *
318
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
319
+ * @req REQ-PATTERN-CONFIG - Drive rule behavior from configurable patterns
320
+ * @req REQ-REGEX-VALIDATION - Validate all configured regex patterns
321
+ * @req REQ-BACKWARD-COMPAT - Maintain behavior when no custom options set
184
322
  */
185
323
  function resolveOptions(rawOptions) {
186
324
  optionErrors = [];
@@ -190,7 +328,12 @@ function resolveOptions(rawOptions) {
190
328
  return resolvedDefaults;
191
329
  }
192
330
  /**
193
- * Build the JSON schema for rule options.
331
+ * Build the JSON Schema definition that validates rule configuration
332
+ * passed to ESLint, ensuring option shapes and types are correct.
333
+ *
334
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
335
+ * @req REQ-SCHEMA-VALIDATION - Define a JSON Schema for rule options
336
+ * @req REQ-PATTERN-CONFIG - Describe configurable pattern and example fields
194
337
  */
195
338
  function getRuleSchema() {
196
339
  return [
@@ -81,10 +81,14 @@ function reportInvalidImplementsReqId(context, comment, reqId, options) {
81
81
  });
82
82
  }
83
83
  /**
84
- * Prepare and validate the token array for an @supports value.
84
+ * Parse the raw token stream for an @implements annotation into a structured
85
+ * representation with a single storyPath and an array of requirement IDs.
85
86
  *
86
- * Returns { storyPath, reqIds } when tokens are present and structurally valid,
87
- * or null when a missing-value condition has been reported.
87
+ * Handles trimming, token splitting, and basic structural checks, and reports
88
+ * missing-value conditions via the provided dependency helpers.
89
+ *
90
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
91
+ * @req REQ-IMPLEMENTS-TOKENS
88
92
  */
89
93
  function parseImplementsTokens(deps, context, comment, rest) {
90
94
  const { MIN_IMPLEMENTS_TOKENS, reportMissingImplementsValue, reportMissingImplementsReqIds, } = deps;
@@ -103,8 +107,12 @@ function parseImplementsTokens(deps, context, comment, rest) {
103
107
  return { storyPath, reqIds };
104
108
  }
105
109
  /**
106
- * Validate the parsed storyPath and reqIds against the provided patterns and
107
- * delegate reporting of any invalid tokens.
110
+ * Validate a previously parsed @implements token structure against configured
111
+ * story and requirement patterns, reporting any configuration or format errors
112
+ * via the supplied dependency helpers.
113
+ *
114
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
115
+ * @req REQ-IMPLEMENTS-TOKENS
108
116
  */
109
117
  function validateImplementsTokens(deps, context, comment, rest) {
110
118
  const { reportInvalidImplementsStoryPath, reportInvalidImplementsReqId } = deps;
@@ -21,6 +21,12 @@ const DEFAULT_ALWAYS_COVERED_STATEMENTS = [
21
21
  const DEFAULT_STRICTNESS = "moderate";
22
22
  const DEFAULT_ALLOW_EMPHASIS_DUPLICATION = false;
23
23
  const DEFAULT_MAX_SCOPE_DEPTH = 3;
24
+ /**
25
+ * Normalize and apply defaults to rule options for the redundancy detector.
26
+ *
27
+ * @story docs/stories/027.0-DEV-REDUNDANT-ANNOTATION-DETECTION.story.md
28
+ * @req REQ-REDUNDANT-OPTIONS
29
+ */
24
30
  function normalizeOptions(raw) {
25
31
  const strictness = raw && typeof raw.strictness === "string"
26
32
  ? raw.strictness
@@ -287,6 +293,12 @@ const rule = {
287
293
  redundantAnnotation: "Annotation on this statement is redundant; it is already covered by its containing scope.",
288
294
  },
289
295
  },
296
+ /**
297
+ * Wire up the ESLint visitors that detect and fix redundant annotations.
298
+ *
299
+ * @story docs/stories/027.0-DEV-REDUNDANT-ANNOTATION-DETECTION.story.md
300
+ * @req REQ-REDUNDANT-DETECTION
301
+ */
290
302
  create(context) {
291
303
  const options = normalizeOptions(context.options[0]);
292
304
  return {