eslint-plugin-traceability 1.6.5 → 1.7.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 (48) hide show
  1. package/README.md +39 -1
  2. package/lib/src/index.d.ts +30 -27
  3. package/lib/src/index.js +51 -31
  4. package/lib/src/maintenance/cli.d.ts +12 -0
  5. package/lib/src/maintenance/cli.js +279 -0
  6. package/lib/src/maintenance/detect.js +27 -12
  7. package/lib/src/maintenance/update.js +42 -34
  8. package/lib/src/maintenance/utils.js +30 -30
  9. package/lib/src/rules/helpers/require-story-io.js +51 -15
  10. package/lib/src/rules/helpers/valid-annotation-format-internal.d.ts +30 -0
  11. package/lib/src/rules/helpers/valid-annotation-format-internal.js +36 -0
  12. package/lib/src/rules/helpers/valid-annotation-options.d.ts +118 -0
  13. package/lib/src/rules/helpers/valid-annotation-options.js +167 -0
  14. package/lib/src/rules/helpers/valid-annotation-utils.d.ts +68 -0
  15. package/lib/src/rules/helpers/valid-annotation-utils.js +103 -0
  16. package/lib/src/rules/helpers/valid-implements-utils.d.ts +75 -0
  17. package/lib/src/rules/helpers/valid-implements-utils.js +149 -0
  18. package/lib/src/rules/helpers/valid-story-reference-helpers.d.ts +67 -0
  19. package/lib/src/rules/helpers/valid-story-reference-helpers.js +92 -0
  20. package/lib/src/rules/prefer-implements-annotation.d.ts +39 -0
  21. package/lib/src/rules/prefer-implements-annotation.js +276 -0
  22. package/lib/src/rules/valid-annotation-format.js +255 -208
  23. package/lib/src/rules/valid-req-reference.js +210 -29
  24. package/lib/src/rules/valid-story-reference.d.ts +7 -0
  25. package/lib/src/rules/valid-story-reference.js +38 -80
  26. package/lib/src/utils/annotation-checker.js +2 -145
  27. package/lib/src/utils/branch-annotation-helpers.js +12 -3
  28. package/lib/src/utils/reqAnnotationDetection.d.ts +6 -0
  29. package/lib/src/utils/reqAnnotationDetection.js +152 -0
  30. package/lib/tests/maintenance/cli.test.d.ts +1 -0
  31. package/lib/tests/maintenance/cli.test.js +172 -0
  32. package/lib/tests/plugin-default-export-and-configs.test.js +3 -0
  33. package/lib/tests/rules/prefer-implements-annotation.test.d.ts +1 -0
  34. package/lib/tests/rules/prefer-implements-annotation.test.js +84 -0
  35. package/lib/tests/rules/require-branch-annotation.test.js +3 -2
  36. package/lib/tests/rules/require-req-annotation.test.js +57 -68
  37. package/lib/tests/rules/require-story-annotation.test.js +13 -28
  38. package/lib/tests/rules/require-story-core-edgecases.test.js +3 -58
  39. package/lib/tests/rules/require-story-core.autofix.test.js +5 -41
  40. package/lib/tests/rules/valid-annotation-format.test.js +395 -40
  41. package/lib/tests/rules/valid-req-reference.test.js +34 -0
  42. package/lib/tests/utils/annotation-checker.test.d.ts +23 -0
  43. package/lib/tests/utils/annotation-checker.test.js +24 -17
  44. package/lib/tests/utils/require-story-core-test-helpers.d.ts +10 -0
  45. package/lib/tests/utils/require-story-core-test-helpers.js +75 -0
  46. package/lib/tests/utils/ts-language-options.d.ts +22 -0
  47. package/lib/tests/utils/ts-language-options.js +27 -0
  48. package/package.json +12 -3
@@ -1,126 +1,62 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const STORY_EXAMPLE_PATH = "docs/stories/005.0-DEV-EXAMPLE.story.md";
3
+ const valid_annotation_options_1 = require("./helpers/valid-annotation-options");
4
+ const valid_annotation_utils_1 = require("./helpers/valid-annotation-utils");
5
+ const valid_implements_utils_1 = require("./helpers/valid-implements-utils");
6
+ const valid_annotation_format_internal_1 = require("./helpers/valid-annotation-format-internal");
4
7
  /**
5
- * Constant to represent the "tag not found" index when searching
6
- * for @story or @req within a comment.
7
- *
8
- * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
9
- * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
10
- * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
11
- * @req REQ-AUTOFIX-PRESERVE - Avoid risky text replacements when the annotation tag cannot be located
12
- */
13
- const TAG_NOT_FOUND_INDEX = -1;
14
- /**
15
- * Normalize a raw comment line to make annotation parsing more robust.
16
- *
17
- * This function trims whitespace, keeps any annotation tags that appear
18
- * later in the line, and supports common JSDoc styles such as leading "*".
19
- *
20
- * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
21
- * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
22
- * @req REQ-FLEXIBLE-PARSING - Support reasonable variations in whitespace and formatting
23
- * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
24
- */
25
- function normalizeCommentLine(rawLine) {
26
- const trimmed = rawLine.trim();
27
- if (!trimmed) {
28
- return "";
29
- }
30
- const annotationMatch = trimmed.match(/@story\b|@req\b/);
31
- if (!annotationMatch || annotationMatch.index === undefined) {
32
- const withoutLeadingStar = trimmed.replace(/^\*\s?/, "");
33
- return withoutLeadingStar;
34
- }
35
- return trimmed.slice(annotationMatch.index);
36
- }
37
- /**
38
- * Collapse internal whitespace in an annotation value so that multi-line
39
- * annotations are treated as a single logical value.
40
- *
41
- * Example:
42
- * "docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md" across
43
- * multiple lines will be collapsed before validation.
44
- *
45
- * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
46
- * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
47
- * @req REQ-MULTILINE-SUPPORT - Handle annotations split across multiple lines
48
- * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
49
- */
50
- function collapseAnnotationValue(value) {
51
- return value.replace(/\s+/g, "");
52
- }
53
- /**
54
- * Build a detailed error message for invalid @story annotations.
55
- *
56
- * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
57
- * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
58
- * @req REQ-ERROR-SPECIFICITY - Provide specific error messages for different format violations
59
- * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
60
- */
61
- function buildStoryErrorMessage(kind, value) {
62
- if (kind === "missing") {
63
- return `Missing story path for @story annotation. Expected a path like "${STORY_EXAMPLE_PATH}".`;
64
- }
65
- return `Invalid story path "${value ?? ""}" for @story annotation. Expected a path like "${STORY_EXAMPLE_PATH}".`;
66
- }
67
- /**
68
- * Build a detailed error message for invalid @req annotations.
8
+ * Report an invalid @story annotation without applying a fix.
69
9
  *
70
10
  * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
71
11
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
72
- * @req REQ-ERROR-SPECIFICITY - Provide specific error messages for different format violations
12
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
73
13
  * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
74
14
  */
75
- function buildReqErrorMessage(kind, value) {
76
- if (kind === "missing") {
77
- return 'Missing requirement ID for @req annotation. Expected an identifier like "REQ-EXAMPLE".';
78
- }
79
- return `Invalid requirement ID "${value ?? ""}" for @req annotation. Expected an identifier like "REQ-EXAMPLE" (uppercase letters, numbers, and dashes only).`;
15
+ function reportInvalidStoryFormat(context, comment, collapsed, options) {
16
+ context.report({
17
+ node: comment,
18
+ messageId: "invalidStoryFormat",
19
+ data: { details: (0, valid_annotation_utils_1.buildStoryErrorMessage)("invalid", collapsed, options) },
20
+ });
80
21
  }
81
22
  /**
82
- * Attempt a minimal, safe auto-fix for common @story path suffix issues.
23
+ * Compute the text replacement for an invalid @story annotation within a comment.
83
24
  *
84
- * Only handles:
85
- * - missing ".md"
86
- * - missing ".story.md"
87
- * and skips any paths with traversal segments (e.g. "..").
25
+ * This helper:
26
+ * - finds the @story tag in the raw comment text,
27
+ * - computes the character range of its value,
28
+ * - and returns an ESLint fix that replaces only that range.
88
29
  *
89
- * Returns the fixed path when safe, or null if no fix should be applied.
30
+ * Returns null when the tag or value cannot be safely located.
90
31
  *
91
32
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
92
- * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
93
- * @req REQ-AUTOFIX-SAFE - Auto-fix must be conservative and never broaden the referenced path
94
- * @req REQ-AUTOFIX-PRESERVE - Preserve surrounding formatting when normalizing story path suffixes
33
+ * @req REQ-AUTOFIX-SAFE
34
+ * @req REQ-AUTOFIX-PRESERVE
95
35
  */
96
- function getFixedStoryPath(original) {
97
- if (original.includes("..")) {
36
+ function createStoryFix(context, comment, fixed) {
37
+ const sourceCode = context.getSourceCode();
38
+ const commentText = sourceCode.getText(comment);
39
+ const search = "@story";
40
+ const tagIndex = commentText.indexOf(search);
41
+ if (tagIndex === valid_annotation_utils_1.TAG_NOT_FOUND_INDEX) {
98
42
  return null;
99
43
  }
100
- if (/\.story\.md$/.test(original)) {
44
+ const afterTagIndex = tagIndex + search.length;
45
+ const rest = commentText.slice(afterTagIndex);
46
+ const valueMatch = rest.match(/[^\S\r\n]*([^\r\n*]+)/);
47
+ if (!valueMatch || valueMatch.index === undefined) {
101
48
  return null;
102
49
  }
103
- if (/\.story$/.test(original)) {
104
- return `${original}.md`;
105
- }
106
- if (/\.md$/.test(original)) {
107
- return original.replace(/\.md$/, ".story.md");
108
- }
109
- return `${original}.story.md`;
110
- }
111
- /**
112
- * Report an invalid @story annotation without applying a fix.
113
- *
114
- * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
115
- * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
116
- * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
117
- */
118
- function reportInvalidStoryFormat(context, comment, collapsed) {
119
- context.report({
120
- node: comment,
121
- messageId: "invalidStoryFormat",
122
- data: { details: buildStoryErrorMessage("invalid", collapsed) },
123
- });
50
+ const valueStartInComment = afterTagIndex +
51
+ valueMatch.index +
52
+ (valueMatch[0].length - valueMatch[1].length);
53
+ const valueEndInComment = valueStartInComment + valueMatch[1].length;
54
+ const start = comment.range[0];
55
+ const fixRange = [
56
+ start + valueStartInComment,
57
+ start + valueEndInComment,
58
+ ];
59
+ return () => (fixer) => fixer.replaceTextRange(fixRange, fixed);
124
60
  }
125
61
  /**
126
62
  * Report an invalid @story annotation and attempt a minimal, safe auto-fix
@@ -135,43 +71,25 @@ function reportInvalidStoryFormat(context, comment, collapsed) {
135
71
  *
136
72
  * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
137
73
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
74
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
138
75
  * @req REQ-PATH-FORMAT - Validate @story paths follow expected patterns
139
76
  * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
140
77
  * @req REQ-AUTOFIX-SAFE - Auto-fix must be conservative and avoid changing semantics
141
78
  * @req REQ-AUTOFIX-PRESERVE - Auto-fix must preserve surrounding formatting and comments
142
79
  */
143
80
  function reportInvalidStoryFormatWithFix(context, comment, collapsed, fixed) {
144
- const sourceCode = context.getSourceCode();
145
- const commentText = sourceCode.getText(comment);
146
- const search = "@story";
147
- const tagIndex = commentText.indexOf(search);
148
- if (tagIndex === TAG_NOT_FOUND_INDEX) {
149
- reportInvalidStoryFormat(context, comment, collapsed);
81
+ const fixFactory = createStoryFix(context, comment, fixed);
82
+ if (!fixFactory) {
83
+ reportInvalidStoryFormat(context, comment, collapsed, (0, valid_annotation_options_1.getResolvedDefaults)());
150
84
  return;
151
85
  }
152
- const afterTagIndex = tagIndex + search.length;
153
- const rest = commentText.slice(afterTagIndex);
154
- const valueMatch = rest.match(/[^\S\r\n]*([^\r\n*]+)/);
155
- if (!valueMatch || valueMatch.index === undefined) {
156
- reportInvalidStoryFormat(context, comment, collapsed);
157
- return;
158
- }
159
- const valueStartInComment = afterTagIndex +
160
- valueMatch.index +
161
- (valueMatch[0].length - valueMatch[1].length);
162
- const valueEndInComment = valueStartInComment + valueMatch[1].length;
163
- const start = comment.range[0];
164
- const fixRange = [
165
- start + valueStartInComment,
166
- start + valueEndInComment,
167
- ];
168
86
  context.report({
169
87
  node: comment,
170
88
  messageId: "invalidStoryFormat",
171
- data: { details: buildStoryErrorMessage("invalid", collapsed) },
172
- fix(fixer) {
173
- return fixer.replaceTextRange(fixRange, fixed);
89
+ data: {
90
+ details: (0, valid_annotation_utils_1.buildStoryErrorMessage)("invalid", collapsed, (0, valid_annotation_options_1.getResolvedDefaults)()),
174
91
  },
92
+ fix: fixFactory(),
175
93
  });
176
94
  }
177
95
  /**
@@ -180,147 +98,235 @@ function reportInvalidStoryFormatWithFix(context, comment, collapsed, fixed) {
180
98
  *
181
99
  * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
182
100
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
101
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
183
102
  * @req REQ-PATH-FORMAT - Validate @story paths follow expected patterns
184
103
  * @req REQ-ERROR-SPECIFICITY - Provide specific error messages for different format violations
185
104
  * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
105
+ * @req REQ-REGEX-VALIDATION - Validate configurable story regex patterns and fall back safely
106
+ * @req REQ-BACKWARD-COMP - Preserve behavior when invalid regex config is supplied
107
+ * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
186
108
  */
187
- function validateStoryAnnotation(context, comment, rawValue) {
109
+ function validateStoryAnnotation(context, comment, rawValue, options) {
188
110
  const trimmed = rawValue.trim();
189
111
  if (!trimmed) {
190
112
  context.report({
191
113
  node: comment,
192
114
  messageId: "invalidStoryFormat",
193
- data: { details: buildStoryErrorMessage("missing", null) },
115
+ data: { details: (0, valid_annotation_utils_1.buildStoryErrorMessage)("missing", null, options) },
194
116
  });
195
117
  return;
196
118
  }
197
- const collapsed = collapseAnnotationValue(trimmed);
198
- const pathPattern = /^docs\/stories\/[0-9]+\.[0-9]+-DEV-[\w-]+\.story\.md$/;
119
+ const collapsed = (0, valid_annotation_utils_1.collapseAnnotationValue)(trimmed);
120
+ const pathPattern = options.storyPattern;
199
121
  if (pathPattern.test(collapsed)) {
200
122
  return;
201
123
  }
202
124
  if (/\s/.test(trimmed)) {
203
- reportInvalidStoryFormat(context, comment, collapsed);
125
+ reportInvalidStoryFormat(context, comment, collapsed, options);
204
126
  return;
205
127
  }
206
- const fixed = getFixedStoryPath(collapsed);
128
+ const fixed = (0, valid_annotation_utils_1.getFixedStoryPath)(collapsed);
207
129
  if (fixed && pathPattern.test(fixed)) {
208
130
  reportInvalidStoryFormatWithFix(context, comment, collapsed, fixed);
209
131
  return;
210
132
  }
211
- reportInvalidStoryFormat(context, comment, collapsed);
133
+ reportInvalidStoryFormat(context, comment, collapsed, options);
212
134
  }
213
135
  /**
214
136
  * Validate a @req annotation value and report detailed errors when needed.
215
137
  *
216
138
  * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
217
139
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
140
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
218
141
  * @req REQ-REQ-FORMAT - Validate @req identifiers follow expected patterns
219
142
  * @req REQ-ERROR-SPECIFICITY - Provide specific error messages for different format violations
143
+ * @req REQ-REGEX-VALIDATION - Validate configurable requirement regex patterns and fall back safely
144
+ * @req REQ-BACKWARD-COMP - Preserve behavior when invalid regex config is supplied
145
+ * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
220
146
  */
221
- function validateReqAnnotation(context, comment, rawValue) {
147
+ function validateReqAnnotation(context, comment, rawValue, options) {
222
148
  const trimmed = rawValue.trim();
223
149
  if (!trimmed) {
224
150
  context.report({
225
151
  node: comment,
226
152
  messageId: "invalidReqFormat",
227
- data: { details: buildReqErrorMessage("missing", null) },
153
+ data: { details: (0, valid_annotation_utils_1.buildReqErrorMessage)("missing", null, options) },
228
154
  });
229
155
  return;
230
156
  }
231
- const collapsed = collapseAnnotationValue(trimmed);
232
- const reqPattern = /^REQ-[A-Z0-9-]+$/;
157
+ const collapsed = (0, valid_annotation_utils_1.collapseAnnotationValue)(trimmed);
158
+ const reqPattern = options.reqPattern;
233
159
  if (!reqPattern.test(collapsed)) {
234
160
  context.report({
235
161
  node: comment,
236
162
  messageId: "invalidReqFormat",
237
- data: { details: buildReqErrorMessage("invalid", collapsed) },
163
+ data: { details: (0, valid_annotation_utils_1.buildReqErrorMessage)("invalid", collapsed, options) },
238
164
  });
239
165
  }
240
166
  }
241
167
  /**
242
- * Process a single comment node and validate any @story/@req annotations it contains.
168
+ * Validate an @implements annotation value and report detailed errors when needed.
169
+ *
170
+ * Expected format:
171
+ * @implements <storyPath> <REQ-ID> [<REQ-ID> ...]
172
+ *
173
+ * Validation rules:
174
+ * - Value must include at least a story path and one requirement ID.
175
+ * - Story path must match the same storyPattern used for @story (no auto-fix).
176
+ * - Each subsequent token must match reqPattern and is validated individually.
177
+ *
178
+ * Story path issues are reported with "invalidImplementsFormat" and
179
+ * requirement ID issues reuse the existing "invalidReqFormat" message.
180
+ *
181
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
182
+ * @req REQ-IMPLEMENTS-PARSE - Parse @implements annotations without affecting @story/@req
183
+ * @req REQ-FORMAT-VALIDATION - Validate @implements story path and requirement IDs
184
+ * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
185
+ */
186
+ function validateImplementsAnnotation(context, comment, rawValue, options) {
187
+ const deps = {
188
+ MIN_IMPLEMENTS_TOKENS: valid_implements_utils_1.MIN_IMPLEMENTS_TOKENS,
189
+ reportMissingImplementsReqIds: valid_implements_utils_1.reportMissingImplementsReqIds,
190
+ reportMissingImplementsValue: valid_implements_utils_1.reportMissingImplementsValue,
191
+ reportInvalidImplementsReqId: valid_implements_utils_1.reportInvalidImplementsReqId,
192
+ reportInvalidImplementsStoryPath: valid_implements_utils_1.reportInvalidImplementsStoryPath,
193
+ };
194
+ (0, valid_implements_utils_1.validateImplementsAnnotationHelper)(deps, context, comment, {
195
+ rawValue,
196
+ options,
197
+ });
198
+ }
199
+ /**
200
+ * Finalize and validate the currently pending annotation, if any.
201
+ *
202
+ * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
203
+ * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
204
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
205
+ * @req REQ-SYNTAX-VALIDATION - Validate annotation syntax matches specification
206
+ * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
207
+ * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
208
+ */
209
+ function finalizePendingAnnotation(context, comment, options, pending) {
210
+ if (!pending) {
211
+ return null;
212
+ }
213
+ // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
214
+ // @story docs/stories/008.0-DEV-AUTO-FIX.story.md
215
+ // @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
216
+ // @req REQ-SYNTAX-VALIDATION - Dispatch validation based on annotation type
217
+ // @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
218
+ // @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
219
+ if (pending.type === "story") {
220
+ validateStoryAnnotation(context, comment, pending.value, options);
221
+ }
222
+ else {
223
+ validateReqAnnotation(context, comment, pending.value, options);
224
+ }
225
+ return null;
226
+ }
227
+ /**
228
+ * Process a single normalized comment line and update the pending annotation state.
243
229
  *
244
- * Supports annotations whose values span multiple lines within the same
230
+ * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
231
+ * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
232
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
233
+ * @req REQ-SYNTAX-VALIDATION - Start new pending annotation when a tag is found
234
+ * @req REQ-MULTILINE-SUPPORT - Treat subsequent lines as continuation for pending annotation
235
+ * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
236
+ * @req REQ-IMPLEMENTS-PARSE - Parse @implements annotations without affecting @story/@req
237
+ * @req REQ-FORMAT-VALIDATION - Validate @implements story path and requirement IDs
238
+ * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
239
+ */
240
+ function processCommentLine({ normalized, pending, context, comment, options, }) {
241
+ if (!normalized) {
242
+ return pending;
243
+ }
244
+ const isStory = /@story\b/.test(normalized);
245
+ const isReq = /@req\b/.test(normalized);
246
+ const isImplements = /@implements\b/.test(normalized);
247
+ // Handle @implements as an immediate, single-line annotation
248
+ if (isImplements) {
249
+ const implementsValue = normalized.replace(/^@implements\b/, "").trim();
250
+ validateImplementsAnnotation(context, comment, implementsValue, options);
251
+ return pending;
252
+ }
253
+ // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
254
+ // @story docs/stories/008.0-DEV-AUTO-FIX.story.md
255
+ // @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
256
+ // @req REQ-SYNTAX-VALIDATION - Start new pending annotation when a tag is found
257
+ // @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
258
+ // @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
259
+ if (isStory || isReq) {
260
+ finalizePendingAnnotation(context, comment, options, pending);
261
+ const value = normalized.replace(/^@story\b|^@req\b/, "").trim();
262
+ return {
263
+ type: isStory ? "story" : "req",
264
+ value,
265
+ hasValue: value.trim().length > 0,
266
+ };
267
+ }
268
+ // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
269
+ // @story docs/stories/008.0-DEV-AUTO-FIX.story.md
270
+ // @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
271
+ // @req REQ-MULTILINE-SUPPORT - Treat subsequent lines as continuation for pending annotation
272
+ // @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
273
+ // @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
274
+ if (pending) {
275
+ const continuation = normalized.trim();
276
+ if (!continuation) {
277
+ return pending;
278
+ }
279
+ const updatedValue = pending.value
280
+ ? `${pending.value} ${continuation}`
281
+ : continuation;
282
+ return {
283
+ ...pending,
284
+ value: updatedValue,
285
+ hasValue: pending.hasValue || continuation.length > 0,
286
+ };
287
+ }
288
+ return pending;
289
+ }
290
+ /**
291
+ * Process a single comment node and validate any @story/@req/@implements annotations it contains.
292
+ *
293
+ * Supports @story and @req annotations whose values span multiple lines within the same
245
294
  * comment block, collapsing whitespace so that the logical value can be
246
295
  * validated against the configured patterns.
247
296
  *
297
+ * @implements annotations are validated immediately per-line and are not
298
+ * accumulated into pending multi-line state.
299
+ *
248
300
  * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
249
301
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
302
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
250
303
  * @req REQ-MULTILINE-SUPPORT - Handle annotations split across multiple lines
251
304
  * @req REQ-FLEXIBLE-PARSING - Support reasonable variations in whitespace and formatting
252
305
  * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
306
+ * @req REQ-IMPLEMENTS-PARSE - Parse @implements annotations without affecting @story/@req
307
+ * @req REQ-FORMAT-VALIDATION - Validate @implements story path and requirement IDs
308
+ * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
253
309
  */
254
- function processComment(context, comment) {
310
+ function processComment(context, comment, options) {
255
311
  const rawLines = (comment.value || "").split(/\r?\n/);
256
312
  let pending = null;
257
- /**
258
- * Finalize and validate the currently pending annotation, if any.
259
- *
260
- * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
261
- * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
262
- * @req REQ-SYNTAX-VALIDATION - Validate annotation syntax matches specification
263
- * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
264
- */
265
- function finalizePending() {
266
- if (!pending) {
267
- return;
268
- }
269
- // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
270
- // @story docs/stories/008.0-DEV-AUTO-FIX.story.md
271
- // @req REQ-SYNTAX-VALIDATION - Dispatch validation based on annotation type
272
- // @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
273
- if (pending.type === "story") {
274
- validateStoryAnnotation(context, comment, pending.value);
275
- }
276
- else {
277
- validateReqAnnotation(context, comment, pending.value);
278
- }
279
- pending = null;
280
- }
281
313
  rawLines.forEach((rawLine) => {
282
- const normalized = normalizeCommentLine(rawLine);
283
- if (!normalized) {
284
- return;
285
- }
286
- const isStory = /@story\b/.test(normalized);
287
- const isReq = /@req\b/.test(normalized);
288
- // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
289
- // @story docs/stories/008.0-DEV-AUTO-FIX.story.md
290
- // @req REQ-SYNTAX-VALIDATION - Start new pending annotation when a tag is found
291
- // @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
292
- if (isStory || isReq) {
293
- finalizePending();
294
- const value = normalized.replace(/^@story\b|^@req\b/, "").trim();
295
- pending = {
296
- type: isStory ? "story" : "req",
297
- value,
298
- hasValue: value.trim().length > 0,
299
- };
300
- return;
301
- }
302
- // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
303
- // @story docs/stories/008.0-DEV-AUTO-FIX.story.md
304
- // @req REQ-MULTILINE-SUPPORT - Treat subsequent lines as continuation for pending annotation
305
- // @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
306
- if (pending) {
307
- const continuation = normalized.trim();
308
- if (!continuation) {
309
- return;
310
- }
311
- pending.value = pending.value
312
- ? `${pending.value} ${continuation}`
313
- : continuation;
314
- pending.hasValue = pending.hasValue || continuation.length > 0;
315
- }
314
+ const normalized = (0, valid_annotation_format_internal_1.normalizeCommentLine)(rawLine);
315
+ pending = processCommentLine({
316
+ normalized,
317
+ pending,
318
+ context,
319
+ comment,
320
+ options,
321
+ });
316
322
  });
317
- finalizePending();
323
+ finalizePendingAnnotation(context, comment, options, pending);
318
324
  }
319
325
  exports.default = {
320
326
  meta: {
321
327
  type: "problem",
322
328
  docs: {
323
- description: "Validate format and syntax of @story and @req annotations",
329
+ description: "Validate format and syntax of @story, @req, and @implements annotations",
324
330
  recommended: "error",
325
331
  },
326
332
  messages: {
@@ -338,8 +344,22 @@ exports.default = {
338
344
  * @req REQ-ERROR-CONSISTENCY - Use shared "Invalid annotation format: {{details}}." message pattern across rules
339
345
  */
340
346
  invalidReqFormat: "Invalid annotation format: {{details}}.",
347
+ /**
348
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
349
+ * @req REQ-ERROR-SPECIFIC - Provide specific details about invalid @implements annotation format
350
+ * @req REQ-ERROR-CONTEXT - Include human-readable details about the expected @implements annotation format
351
+ * @req REQ-ERROR-CONSISTENCY - Use shared "Invalid annotation format: {{details}}." message pattern across rules
352
+ * @req REQ-FORMAT-VALIDATION - Validate @implements story path and requirement IDs
353
+ */
354
+ invalidImplementsFormat: "Invalid annotation format: {{details}}.",
355
+ /**
356
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
357
+ * @req REQ-REGEX-VALIDATION - Surface configuration errors for invalid regex patterns
358
+ * @req REQ-BACKWARD-COMP - Preserve behavior by falling back to default patterns on error
359
+ */
360
+ invalidRuleConfiguration: "Invalid configuration for valid-annotation-format: {{details}}",
341
361
  },
342
- schema: [],
362
+ schema: (0, valid_annotation_options_1.getRuleSchema)(),
343
363
  /**
344
364
  * This rule's fixable support is limited to safe @story path suffix normalization per Story 008.0.
345
365
  * Fixes are limited strictly to adjusting the suffix portion of the @story path (e.g., adding
@@ -348,32 +368,59 @@ exports.default = {
348
368
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
349
369
  * @req REQ-AUTOFIX-SAFE
350
370
  * @req REQ-AUTOFIX-PRESERVE
371
+ * @req REQ-REGEX-VALIDATION - Ensure regex configuration does not affect fix safety
372
+ * @req REQ-BACKWARD-COMP - Maintain previous auto-fix behavior under invalid configs
351
373
  */
352
374
  fixable: "code",
353
375
  },
354
376
  /**
355
377
  * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
356
378
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
379
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
380
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
357
381
  * @req REQ-SYNTAX-VALIDATION - Ensure rule create function validates annotations syntax
358
382
  * @req REQ-FORMAT-SPECIFICATION - Implement formatting checks per specification
359
383
  * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
384
+ * @req REQ-REGEX-VALIDATION - Derive validation regexes from shared options helper
385
+ * @req REQ-BACKWARD-COMP - Fall back to default patterns and continue validation on config errors
386
+ * @req REQ-IMPLEMENTS-PARSE - Parse @implements annotations without affecting @story/@req
387
+ * @req REQ-FORMAT-VALIDATION - Validate @implements story path and requirement IDs
388
+ * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
360
389
  */
361
390
  create(context) {
362
391
  const sourceCode = context.getSourceCode();
392
+ const options = (0, valid_annotation_options_1.resolveOptions)(context.options || []);
393
+ const optionErrors = (0, valid_annotation_options_1.getOptionErrors)();
363
394
  return {
364
395
  /**
365
- * Program-level handler that inspects all comments for @story and @req tags
396
+ * Program-level handler that inspects all comments for @story, @req, and @implements tags
366
397
  *
367
398
  * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
368
399
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
400
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
401
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
369
402
  * @req REQ-PATH-FORMAT - Validate @story paths follow expected patterns
370
403
  * @req REQ-REQ-FORMAT - Validate @req identifiers follow expected patterns
371
404
  * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
405
+ * @req REQ-REGEX-VALIDATION - Surface regex configuration errors without blocking validation
406
+ * @req REQ-BACKWARD-COMP - Continue validating comments using default patterns on error
407
+ * @req REQ-IMPLEMENTS-PARSE - Parse @implements annotations without affecting @story/@req
408
+ * @req REQ-FORMAT-VALIDATION - Validate @implements story path and requirement IDs
409
+ * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
372
410
  */
373
- Program() {
411
+ Program(node) {
412
+ if (optionErrors && optionErrors.length > 0) {
413
+ optionErrors.forEach((details) => {
414
+ context.report({
415
+ node,
416
+ messageId: "invalidRuleConfiguration",
417
+ data: { details },
418
+ });
419
+ });
420
+ }
374
421
  const comments = sourceCode.getAllComments() || [];
375
422
  comments.forEach((comment) => {
376
- processComment(context, comment);
423
+ processComment(context, comment, options);
377
424
  });
378
425
  },
379
426
  };