eslint-plugin-traceability 1.10.0 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +3 -2
  2. package/README.md +1 -0
  3. package/lib/src/maintenance/cli.js +12 -12
  4. package/lib/src/maintenance/detect.js +19 -19
  5. package/lib/src/rules/helpers/require-story-core.d.ts +2 -15
  6. package/lib/src/rules/helpers/require-story-core.js +4 -71
  7. package/lib/src/rules/helpers/require-story-helpers.d.ts +32 -8
  8. package/lib/src/rules/helpers/require-story-helpers.js +44 -15
  9. package/lib/src/rules/helpers/require-story-visitors.js +47 -6
  10. package/lib/src/rules/helpers/valid-annotation-format-internal.d.ts +11 -0
  11. package/lib/src/rules/helpers/valid-annotation-format-internal.js +21 -0
  12. package/lib/src/rules/helpers/valid-annotation-format-validators.d.ts +125 -0
  13. package/lib/src/rules/helpers/valid-annotation-format-validators.js +274 -0
  14. package/lib/src/rules/helpers/valid-annotation-options.d.ts +6 -0
  15. package/lib/src/rules/helpers/valid-annotation-options.js +4 -0
  16. package/lib/src/rules/helpers/valid-annotation-utils.js +31 -31
  17. package/lib/src/rules/helpers/valid-story-reference-helpers.js +19 -19
  18. package/lib/src/rules/prefer-implements-annotation.js +29 -1
  19. package/lib/src/rules/require-story-annotation.js +15 -0
  20. package/lib/src/rules/require-test-traceability.js +1 -6
  21. package/lib/src/rules/valid-annotation-format.js +10 -243
  22. package/lib/src/utils/annotation-checker.js +1 -1
  23. package/lib/tests/perf/maintenance-cli-large-workspace.test.d.ts +1 -0
  24. package/lib/tests/perf/maintenance-cli-large-workspace.test.js +130 -0
  25. package/lib/tests/perf/maintenance-large-workspace.test.d.ts +1 -0
  26. package/lib/tests/perf/maintenance-large-workspace.test.js +149 -0
  27. package/lib/tests/rules/auto-fix-behavior-008.test.js +23 -0
  28. package/lib/tests/rules/require-story-core.autofix.test.js +9 -3
  29. package/lib/tests/rules/require-story-core.test.js +13 -7
  30. package/lib/tests/rules/require-story-helpers-edgecases.test.js +1 -1
  31. package/lib/tests/rules/require-story-helpers.test.js +14 -8
  32. package/lib/tests/rules/valid-annotation-format.test.js +71 -0
  33. package/lib/tests/utils/require-story-core-test-helpers.d.ts +1 -1
  34. package/lib/tests/utils/require-story-core-test-helpers.js +16 -16
  35. package/lib/tests/utils/temp-dir-helpers.js +1 -1
  36. package/package.json +9 -2
  37. package/user-docs/api-reference.md +8 -4
  38. package/user-docs/examples.md +42 -0
@@ -28,3 +28,14 @@ export interface PendingAnnotation {
28
28
  * of the line for downstream logic.
29
29
  */
30
30
  export declare function normalizeCommentLine(rawLine: string): string;
31
+ /**
32
+ * Detect whether a normalized comment line starts with a non-traceability JSDoc tag.
33
+ *
34
+ * This is used to distinguish regular JSDoc tags (e.g. @param, @returns) from
35
+ * traceability-related annotations such as @story, @req, and @supports.
36
+ *
37
+ * @supports docs/stories/022.0-DEV-JSDOC-COEXISTENCE.story.md
38
+ * @req REQ-JSDOC-BOUNDARY-DETECTION
39
+ * @req REQ-JSDOC-TAG-COEXISTENCE
40
+ */
41
+ export declare function isNonTraceabilityJSDocTagLine(normalized: string): boolean;
@@ -13,6 +13,7 @@
13
13
  */
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.normalizeCommentLine = normalizeCommentLine;
16
+ exports.isNonTraceabilityJSDocTagLine = isNonTraceabilityJSDocTagLine;
16
17
  /**
17
18
  * Normalize a raw comment line to make annotation parsing more robust.
18
19
  *
@@ -34,3 +35,23 @@ function normalizeCommentLine(rawLine) {
34
35
  }
35
36
  return trimmed.slice(annotationMatch.index);
36
37
  }
38
+ /**
39
+ * Detect whether a normalized comment line starts with a non-traceability JSDoc tag.
40
+ *
41
+ * This is used to distinguish regular JSDoc tags (e.g. @param, @returns) from
42
+ * traceability-related annotations such as @story, @req, and @supports.
43
+ *
44
+ * @supports docs/stories/022.0-DEV-JSDOC-COEXISTENCE.story.md
45
+ * @req REQ-JSDOC-BOUNDARY-DETECTION
46
+ * @req REQ-JSDOC-TAG-COEXISTENCE
47
+ */
48
+ function isNonTraceabilityJSDocTagLine(normalized) {
49
+ const trimmed = normalized.trimStart();
50
+ if (!trimmed || !trimmed.startsWith("@")) {
51
+ return false;
52
+ }
53
+ if (/^@(story|req|supports)\b/.test(trimmed)) {
54
+ return false;
55
+ }
56
+ return true;
57
+ }
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Validators and helper functions for the valid-annotation-format rule.
3
+ *
4
+ * This module contains the core validation logic that was originally
5
+ * embedded in src/rules/valid-annotation-format.ts. It is extracted
6
+ * here to keep the main rule module smaller and easier to read while
7
+ * preserving existing behavior.
8
+ *
9
+ * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
10
+ * @story docs/stories/007.0-DEV-ERROR-REPORTING.story.md
11
+ * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
12
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
13
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
14
+ * @req REQ-FORMAT-SPECIFICATION
15
+ * @req REQ-SYNTAX-VALIDATION
16
+ * @req REQ-PATH-FORMAT
17
+ * @req REQ-REQ-FORMAT
18
+ * @req REQ-MULTILINE-SUPPORT
19
+ * @req REQ-AUTOFIX-FORMAT
20
+ * @req REQ-ERROR-SPECIFICITY
21
+ * @req REQ-REGEX-VALIDATION
22
+ * @req REQ-BACKWARD-COMP
23
+ * @req REQ-SUPPORTS-PARSE
24
+ * @req REQ-FORMAT-VALIDATION
25
+ * @req REQ-MIXED-SUPPORT
26
+ */
27
+ import type { ResolvedAnnotationOptions } from "./valid-annotation-options";
28
+ import type { PendingAnnotation } from "./valid-annotation-format-internal";
29
+ /**
30
+ * Report an invalid @story annotation without applying a fix.
31
+ *
32
+ * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
33
+ * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
34
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
35
+ * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
36
+ */
37
+ export declare function reportInvalidStoryFormat(context: any, comment: any, collapsed: string, options: ResolvedAnnotationOptions): void;
38
+ /**
39
+ * Compute the text replacement for an invalid @story annotation within a comment.
40
+ *
41
+ * This helper:
42
+ * - finds the @story tag in the raw comment text,
43
+ * - computes the character range of its value,
44
+ * - and returns an ESLint fix that replaces only that range.
45
+ *
46
+ * Returns null when the tag or value cannot be safely located.
47
+ *
48
+ * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
49
+ * @req REQ-AUTOFIX-SAFE
50
+ * @req REQ-AUTOFIX-PRESERVE
51
+ */
52
+ export declare function createStoryFix(context: any, comment: any, fixed: string): null | (() => any);
53
+ /**
54
+ * Report an invalid @story annotation and attempt a minimal, safe auto-fix
55
+ * for common path suffix issues by locating and replacing the path text
56
+ * within the original comment.
57
+ *
58
+ * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
59
+ * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
60
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
61
+ * @req REQ-PATH-FORMAT - Validate @story paths follow expected patterns
62
+ * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
63
+ * @req REQ-AUTOFIX-SAFE - Auto-fix must be conservative and avoid changing semantics
64
+ * @req REQ-AUTOFIX-PRESERVE - Auto-fix must preserve surrounding formatting and comments
65
+ */
66
+ export declare function reportInvalidStoryFormatWithFix(context: any, comment: any, collapsed: string, fixed: string): void;
67
+ /**
68
+ * Validate a @story annotation value and report detailed errors when needed.
69
+ * Where safe and unambiguous, apply an automatic fix for missing suffixes.
70
+ *
71
+ * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
72
+ * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
73
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
74
+ * @req REQ-PATH-FORMAT - Validate @story paths follow expected patterns
75
+ * @req REQ-ERROR-SPECIFICITY - Provide specific error messages for different format violations
76
+ * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
77
+ * @req REQ-REGEX-VALIDATION - Validate configurable story regex patterns and fall back safely
78
+ * @req REQ-BACKWARD-COMP - Preserve behavior when invalid regex config is supplied
79
+ * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
80
+ */
81
+ export declare function validateStoryAnnotation(context: any, comment: any, rawValue: string, options: ResolvedAnnotationOptions): void;
82
+ /**
83
+ * Validate a @req annotation value and report detailed errors when needed.
84
+ *
85
+ * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
86
+ * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
87
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
88
+ * @req REQ-REQ-FORMAT - Validate @req identifiers follow expected patterns
89
+ * @req REQ-ERROR-SPECIFICITY - Provide specific error messages for different format violations
90
+ * @req REQ-REGEX-VALIDATION - Validate configurable requirement regex patterns and fall back safely
91
+ * @req REQ-BACKWARD-COMP - Preserve behavior when invalid regex config is supplied
92
+ * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
93
+ */
94
+ export declare function validateReqAnnotation(context: any, comment: any, rawValue: string, options: ResolvedAnnotationOptions): void;
95
+ /**
96
+ * Validate an @supports annotation value and report detailed errors when needed.
97
+ *
98
+ * Expected format:
99
+ * @supports <storyPath> <REQ-ID> [<REQ-ID> ...]
100
+ *
101
+ * Validation rules:
102
+ * - Value must include at least a story path and one requirement ID.
103
+ * - Story path must match the same storyPattern used for @story (no auto-fix).
104
+ * - Each subsequent token must match reqPattern and is validated individually.
105
+ *
106
+ * Story path issues are reported with "invalidImplementsFormat" and
107
+ * requirement ID issues reuse the existing "invalidReqFormat" message.
108
+ *
109
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
110
+ * @req REQ-SUPPORTS-PARSE - Parse @supports annotations without affecting @story/@req
111
+ * @req REQ-FORMAT-VALIDATION - Validate @implements story path and requirement IDs
112
+ * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
113
+ */
114
+ export declare function validateImplementsAnnotation(context: any, comment: any, rawValue: string, options: ResolvedAnnotationOptions): void;
115
+ /**
116
+ * Finalize and validate the currently pending annotation, if any.
117
+ *
118
+ * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
119
+ * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
120
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
121
+ * @req REQ-SYNTAX-VALIDATION - Validate annotation syntax matches specification
122
+ * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
123
+ * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
124
+ */
125
+ export declare function finalizePendingAnnotation(context: any, comment: any, options: ResolvedAnnotationOptions, pending: PendingAnnotation | null): PendingAnnotation | null;
@@ -0,0 +1,274 @@
1
+ "use strict";
2
+ /**
3
+ * Validators and helper functions for the valid-annotation-format rule.
4
+ *
5
+ * This module contains the core validation logic that was originally
6
+ * embedded in src/rules/valid-annotation-format.ts. It is extracted
7
+ * here to keep the main rule module smaller and easier to read while
8
+ * preserving existing behavior.
9
+ *
10
+ * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
11
+ * @story docs/stories/007.0-DEV-ERROR-REPORTING.story.md
12
+ * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
13
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
14
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
15
+ * @req REQ-FORMAT-SPECIFICATION
16
+ * @req REQ-SYNTAX-VALIDATION
17
+ * @req REQ-PATH-FORMAT
18
+ * @req REQ-REQ-FORMAT
19
+ * @req REQ-MULTILINE-SUPPORT
20
+ * @req REQ-AUTOFIX-FORMAT
21
+ * @req REQ-ERROR-SPECIFICITY
22
+ * @req REQ-REGEX-VALIDATION
23
+ * @req REQ-BACKWARD-COMP
24
+ * @req REQ-SUPPORTS-PARSE
25
+ * @req REQ-FORMAT-VALIDATION
26
+ * @req REQ-MIXED-SUPPORT
27
+ */
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.reportInvalidStoryFormat = reportInvalidStoryFormat;
30
+ exports.createStoryFix = createStoryFix;
31
+ exports.reportInvalidStoryFormatWithFix = reportInvalidStoryFormatWithFix;
32
+ exports.validateStoryAnnotation = validateStoryAnnotation;
33
+ exports.validateReqAnnotation = validateReqAnnotation;
34
+ exports.validateImplementsAnnotation = validateImplementsAnnotation;
35
+ exports.finalizePendingAnnotation = finalizePendingAnnotation;
36
+ const valid_annotation_utils_1 = require("./valid-annotation-utils");
37
+ const valid_implements_utils_1 = require("./valid-implements-utils");
38
+ const valid_annotation_options_1 = require("./valid-annotation-options");
39
+ /**
40
+ * Report an invalid @story annotation without applying a fix.
41
+ *
42
+ * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
43
+ * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
44
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
45
+ * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
46
+ */
47
+ function reportInvalidStoryFormat(context, comment, collapsed, options) {
48
+ context.report({
49
+ node: comment,
50
+ messageId: "invalidStoryFormat",
51
+ data: { details: (0, valid_annotation_utils_1.buildStoryErrorMessage)("invalid", collapsed, options) },
52
+ });
53
+ }
54
+ /**
55
+ * Compute the text replacement for an invalid @story annotation within a comment.
56
+ *
57
+ * This helper:
58
+ * - finds the @story tag in the raw comment text,
59
+ * - computes the character range of its value,
60
+ * - and returns an ESLint fix that replaces only that range.
61
+ *
62
+ * Returns null when the tag or value cannot be safely located.
63
+ *
64
+ * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
65
+ * @req REQ-AUTOFIX-SAFE
66
+ * @req REQ-AUTOFIX-PRESERVE
67
+ */
68
+ function createStoryFix(context, comment, fixed) {
69
+ const sourceCode = context.getSourceCode();
70
+ const commentText = sourceCode.getText(comment);
71
+ const search = "@story";
72
+ const tagIndex = commentText.indexOf(search);
73
+ // @story docs/stories/008.0-DEV-AUTO-FIX.story.md
74
+ // @req REQ-AUTOFIX-SAFE - Skip auto-fix when @story tag cannot be reliably located
75
+ if (tagIndex === valid_annotation_utils_1.TAG_NOT_FOUND_INDEX) {
76
+ return null;
77
+ }
78
+ const afterTagIndex = tagIndex + search.length;
79
+ const rest = commentText.slice(afterTagIndex);
80
+ const valueMatch = rest.match(/[^\S\r\n]*([^\r\n*]+)/);
81
+ // @story docs/stories/008.0-DEV-AUTO-FIX.story.md
82
+ // @req REQ-AUTOFIX-SAFE - Abort auto-fix when story value range cannot be safely determined
83
+ if (!valueMatch || valueMatch.index === undefined) {
84
+ return null;
85
+ }
86
+ const valueStartInComment = afterTagIndex +
87
+ valueMatch.index +
88
+ (valueMatch[0].length - valueMatch[1].length);
89
+ const valueEndInComment = valueStartInComment + valueMatch[1].length;
90
+ const start = comment.range[0];
91
+ const fixRange = [
92
+ start + valueStartInComment,
93
+ start + valueEndInComment,
94
+ ];
95
+ return () => (fixer) => fixer.replaceTextRange(fixRange, fixed);
96
+ }
97
+ /**
98
+ * Report an invalid @story annotation and attempt a minimal, safe auto-fix
99
+ * for common path suffix issues by locating and replacing the path text
100
+ * within the original comment.
101
+ *
102
+ * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
103
+ * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
104
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
105
+ * @req REQ-PATH-FORMAT - Validate @story paths follow expected patterns
106
+ * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
107
+ * @req REQ-AUTOFIX-SAFE - Auto-fix must be conservative and avoid changing semantics
108
+ * @req REQ-AUTOFIX-PRESERVE - Auto-fix must preserve surrounding formatting and comments
109
+ */
110
+ function reportInvalidStoryFormatWithFix(context, comment, collapsed, fixed) {
111
+ const fixFactory = createStoryFix(context, comment, fixed);
112
+ // @story docs/stories/008.0-DEV-AUTO-FIX.story.md
113
+ // @req REQ-AUTOFIX-SAFE - Fall back to reporting without fix when safe fix cannot be created
114
+ if (!fixFactory) {
115
+ reportInvalidStoryFormat(context, comment, collapsed, (0, valid_annotation_options_1.getResolvedDefaults)());
116
+ return;
117
+ }
118
+ context.report({
119
+ node: comment,
120
+ messageId: "invalidStoryFormat",
121
+ data: {
122
+ details: (0, valid_annotation_utils_1.buildStoryErrorMessage)("invalid", collapsed, (0, valid_annotation_options_1.getResolvedDefaults)()),
123
+ },
124
+ fix: fixFactory(),
125
+ });
126
+ }
127
+ /**
128
+ * Validate a @story annotation value and report detailed errors when needed.
129
+ * Where safe and unambiguous, apply an automatic fix for missing suffixes.
130
+ *
131
+ * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
132
+ * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
133
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
134
+ * @req REQ-PATH-FORMAT - Validate @story paths follow expected patterns
135
+ * @req REQ-ERROR-SPECIFICITY - Provide specific error messages for different format violations
136
+ * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
137
+ * @req REQ-REGEX-VALIDATION - Validate configurable story regex patterns and fall back safely
138
+ * @req REQ-BACKWARD-COMP - Preserve behavior when invalid regex config is supplied
139
+ * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
140
+ */
141
+ function validateStoryAnnotation(context, comment, rawValue, options) {
142
+ const trimmed = rawValue.trim();
143
+ // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
144
+ // @req REQ-PATH-FORMAT - Treat missing @story value as a specific validation error
145
+ if (!trimmed) {
146
+ context.report({
147
+ node: comment,
148
+ messageId: "invalidStoryFormat",
149
+ data: { details: (0, valid_annotation_utils_1.buildStoryErrorMessage)("missing", null, options) },
150
+ });
151
+ return;
152
+ }
153
+ const collapsed = (0, valid_annotation_utils_1.collapseAnnotationValue)(trimmed);
154
+ const pathPattern = options.storyPattern;
155
+ // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
156
+ // @req REQ-PATH-FORMAT - Accept @story value when it matches configured storyPattern
157
+ if (pathPattern.test(collapsed)) {
158
+ return;
159
+ }
160
+ // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
161
+ // @req REQ-PATH-FORMAT - Reject @story values containing internal whitespace as invalid
162
+ if (/\s/.test(trimmed)) {
163
+ reportInvalidStoryFormat(context, comment, collapsed, options);
164
+ return;
165
+ }
166
+ const fixed = (0, valid_annotation_utils_1.getFixedStoryPath)(collapsed);
167
+ // @story docs/stories/008.0-DEV-AUTO-FIX.story.md
168
+ // @req REQ-AUTOFIX-FORMAT - Apply suffix-only auto-fix when it yields a pattern-compliant path
169
+ if (fixed && pathPattern.test(fixed)) {
170
+ if (options.autoFix !== false) {
171
+ reportInvalidStoryFormatWithFix(context, comment, collapsed, fixed);
172
+ return;
173
+ }
174
+ reportInvalidStoryFormat(context, comment, collapsed, options);
175
+ return;
176
+ }
177
+ reportInvalidStoryFormat(context, comment, collapsed, options);
178
+ }
179
+ /**
180
+ * Validate a @req annotation value and report detailed errors when needed.
181
+ *
182
+ * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
183
+ * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
184
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
185
+ * @req REQ-REQ-FORMAT - Validate @req identifiers follow expected patterns
186
+ * @req REQ-ERROR-SPECIFICITY - Provide specific error messages for different format violations
187
+ * @req REQ-REGEX-VALIDATION - Validate configurable requirement regex patterns and fall back safely
188
+ * @req REQ-BACKWARD-COMP - Preserve behavior when invalid regex config is supplied
189
+ * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
190
+ */
191
+ function validateReqAnnotation(context, comment, rawValue, options) {
192
+ const trimmed = rawValue.trim();
193
+ // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
194
+ // @req REQ-REQ-FORMAT - Treat missing @req value as a specific validation error
195
+ if (!trimmed) {
196
+ context.report({
197
+ node: comment,
198
+ messageId: "invalidReqFormat",
199
+ data: { details: (0, valid_annotation_utils_1.buildReqErrorMessage)("missing", null, options) },
200
+ });
201
+ return;
202
+ }
203
+ const collapsed = (0, valid_annotation_utils_1.collapseAnnotationValue)(trimmed);
204
+ const reqPattern = options.reqPattern;
205
+ // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
206
+ // @req REQ-REQ-FORMAT - Flag @req identifiers that do not match the configured pattern
207
+ if (!reqPattern.test(collapsed)) {
208
+ context.report({
209
+ node: comment,
210
+ messageId: "invalidReqFormat",
211
+ data: { details: (0, valid_annotation_utils_1.buildReqErrorMessage)("invalid", collapsed, options) },
212
+ });
213
+ }
214
+ }
215
+ /**
216
+ * Validate an @supports annotation value and report detailed errors when needed.
217
+ *
218
+ * Expected format:
219
+ * @supports <storyPath> <REQ-ID> [<REQ-ID> ...]
220
+ *
221
+ * Validation rules:
222
+ * - Value must include at least a story path and one requirement ID.
223
+ * - Story path must match the same storyPattern used for @story (no auto-fix).
224
+ * - Each subsequent token must match reqPattern and is validated individually.
225
+ *
226
+ * Story path issues are reported with "invalidImplementsFormat" and
227
+ * requirement ID issues reuse the existing "invalidReqFormat" message.
228
+ *
229
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
230
+ * @req REQ-SUPPORTS-PARSE - Parse @supports annotations without affecting @story/@req
231
+ * @req REQ-FORMAT-VALIDATION - Validate @implements story path and requirement IDs
232
+ * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
233
+ */
234
+ function validateImplementsAnnotation(context, comment, rawValue, options) {
235
+ const deps = {
236
+ MIN_IMPLEMENTS_TOKENS: valid_implements_utils_1.MIN_IMPLEMENTS_TOKENS,
237
+ reportMissingImplementsReqIds: valid_implements_utils_1.reportMissingImplementsReqIds,
238
+ reportMissingImplementsValue: valid_implements_utils_1.reportMissingImplementsValue,
239
+ reportInvalidImplementsReqId: valid_implements_utils_1.reportInvalidImplementsReqId,
240
+ reportInvalidImplementsStoryPath: valid_implements_utils_1.reportInvalidImplementsStoryPath,
241
+ };
242
+ (0, valid_implements_utils_1.validateImplementsAnnotationHelper)(deps, context, comment, {
243
+ rawValue,
244
+ options,
245
+ });
246
+ }
247
+ /**
248
+ * Finalize and validate the currently pending annotation, if any.
249
+ *
250
+ * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
251
+ * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
252
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
253
+ * @req REQ-SYNTAX-VALIDATION - Validate annotation syntax matches specification
254
+ * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
255
+ * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
256
+ */
257
+ function finalizePendingAnnotation(context, comment, options, pending) {
258
+ // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
259
+ // @req REQ-MULTILINE-SUPPORT - Do nothing when there is no pending multi-line annotation to finalize
260
+ if (!pending) {
261
+ return null;
262
+ }
263
+ // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
264
+ // @req REQ-SYNTAX-VALIDATION - Dispatch to @story or @req validator based on pending annotation type
265
+ // @req REQ-AUTOFIX-FORMAT - Route to story validator which may apply safe auto-fixes
266
+ // @req REQ-MIXED-SUPPORT - Ensure @story and @req annotations are handled independently
267
+ if (pending.type === "story") {
268
+ validateStoryAnnotation(context, comment, pending.value, options);
269
+ }
270
+ else {
271
+ validateReqAnnotation(context, comment, pending.value, options);
272
+ }
273
+ return null;
274
+ }
@@ -53,6 +53,11 @@ export interface AnnotationRuleOptions {
53
53
  * Human-readable example requirement ID used in error messages.
54
54
  */
55
55
  requirementIdExample?: string;
56
+ /**
57
+ * Global toggle for auto-fix behavior in valid-annotation-format.
58
+ * When false, no automatic suffix-normalization fixes are applied.
59
+ */
60
+ autoFix?: boolean;
56
61
  }
57
62
  /**
58
63
  * Resolved, runtime-ready options for the rule.
@@ -62,6 +67,7 @@ export interface ResolvedAnnotationOptions {
62
67
  storyExample: string;
63
68
  reqPattern: RegExp;
64
69
  reqExample: string;
70
+ autoFix: boolean;
65
71
  }
66
72
  export declare function getDefaultReqExample(): string;
67
73
  export declare function getResolvedDefaults(): ResolvedAnnotationOptions;
@@ -26,6 +26,7 @@ let resolvedDefaults = {
26
26
  storyExample: getDefaultStoryExample(),
27
27
  reqPattern: getDefaultReqPattern(),
28
28
  reqExample: getDefaultReqExample(),
29
+ autoFix: true,
29
30
  };
30
31
  /**
31
32
  * Collected configuration errors encountered while resolving options.
@@ -119,6 +120,8 @@ function resolveOptions(rawOptions) {
119
120
  const flatReqPattern = user?.requirementIdPattern;
120
121
  const nestedReqExample = user?.req?.example;
121
122
  const flatReqExample = user?.requirementIdExample;
123
+ const autoFixFlag = user?.autoFix;
124
+ const autoFix = typeof autoFixFlag === "boolean" ? autoFixFlag : true;
122
125
  const storyPattern = resolvePattern({
123
126
  nestedPattern: nestedStoryPattern,
124
127
  nestedFieldName: "story.pattern",
@@ -140,6 +143,7 @@ function resolveOptions(rawOptions) {
140
143
  storyExample,
141
144
  reqPattern,
142
145
  reqExample,
146
+ autoFix,
143
147
  };
144
148
  return resolvedDefaults;
145
149
  }
@@ -39,7 +39,7 @@ exports.STORY_EXAMPLE_PATH = "docs/stories/005.0-DEV-EXAMPLE.story.md";
39
39
  * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
40
40
  */
41
41
  function collapseAnnotationValue(value) {
42
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-MULTILINE-SUPPORT
42
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-MULTILINE-SUPPORT
43
43
  return value.replace(/\s+/g, "");
44
44
  }
45
45
  /**
@@ -62,42 +62,42 @@ function collapseAnnotationValue(value) {
62
62
  */
63
63
  function getFixedStoryPath(original) {
64
64
  // @story docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md | REQ-AUTOFIX-SAFE - Reject auto-fix when the path contains ".." traversal segments to avoid broadening the reference.
65
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces correctness of the story identifier by rejecting paths that use unsafe traversal segments.
65
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces correctness of the story identifier by rejecting paths that use unsafe traversal segments.
66
66
  if (original.includes("..")) {
67
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT
68
- // @implements docs/stories/008.0-DEV-AUTO-FIX.story.md REQ-AUTOFIX-SAFE
69
- // @implements docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-AUTOFIX-SAFE
67
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT
68
+ // @supports docs/stories/008.0-DEV-AUTO-FIX.story.md REQ-AUTOFIX-SAFE
69
+ // @supports docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-AUTOFIX-SAFE
70
70
  return null;
71
71
  }
72
72
  // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md | REQ-AUTOFIX-FORMAT - Leave correctly formatted ".story.md" paths unchanged so diagnostics are not hidden by redundant fixes.
73
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces correctness of the story identifier by recognizing already valid ".story.md" paths without altering them.
73
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces correctness of the story identifier by recognizing already valid ".story.md" paths without altering them.
74
74
  if (/\.story\.md$/.test(original)) {
75
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT
76
- // @implements docs/stories/008.0-DEV-AUTO-FIX.story.md REQ-AUTOFIX-FORMAT
77
- // @implements docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-AUTOFIX-FORMAT
75
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT
76
+ // @supports docs/stories/008.0-DEV-AUTO-FIX.story.md REQ-AUTOFIX-FORMAT
77
+ // @supports docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-AUTOFIX-FORMAT
78
78
  return null;
79
79
  }
80
80
  // @story docs/stories/008.0-DEV-AUTO-FIX.story.md | REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE - When ".story" is present but ".md" is missing, append only the extension without altering the base path.
81
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces correctness of the story identifier by completing a partially correct ".story" suffix to the canonical ".story.md".
81
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces correctness of the story identifier by completing a partially correct ".story" suffix to the canonical ".story.md".
82
82
  if (/\.story$/.test(original)) {
83
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT
84
- // @implements docs/stories/008.0-DEV-AUTO-FIX.story.md REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE
85
- // @implements docs/stories/010.2-REQ-STORY-PATH-AUTOFIX.story.md REQ-AUTOFIX-FORMAT
83
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT
84
+ // @supports docs/stories/008.0-DEV-AUTO-FIX.story.md REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE
85
+ // @supports docs/stories/010.2-REQ-STORY-PATH-AUTOFIX.story.md REQ-AUTOFIX-FORMAT
86
86
  return `${original}.md`;
87
87
  }
88
88
  // @story docs/stories/010.2-REQ-STORY-PATH-AUTOFIX.story.md | REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE - Normalize plain ".md" doc paths to ".story.md" while keeping the rest of the path intact.
89
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces correctness of the story identifier by transforming generic ".md" references into canonical ".story.md" story paths.
89
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces correctness of the story identifier by transforming generic ".md" references into canonical ".story.md" story paths.
90
90
  if (/\.md$/.test(original)) {
91
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT
92
- // @implements docs/stories/008.0-DEV-AUTO-FIX.story.md REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE
93
- // @implements docs/stories/010.2-REQ-STORY-PATH-AUTOFIX.story.md REQ-AUTOFIX-FORMAT
91
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT
92
+ // @supports docs/stories/008.0-DEV-AUTO-FIX.story.md REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE
93
+ // @supports docs/stories/010.2-REQ-STORY-PATH-AUTOFIX.story.md REQ-AUTOFIX-FORMAT
94
94
  return original.replace(/\.md$/, ".story.md");
95
95
  }
96
96
  // @story docs/stories/010.2-REQ-STORY-PATH-AUTOFIX.story.md | REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE REQ-AUTOFIX-SAFE - For bare paths with no extension, append ".story.md" as a canonical story reference without touching the directory.
97
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces presence and correctness of the story identifier by supplying the standard ".story.md" suffix when no extension is provided.
98
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT
99
- // @implements docs/stories/008.0-DEV-AUTO-FIX.story.md REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE REQ-AUTOFIX-SAFE
100
- // @implements docs/stories/010.2-REQ-STORY-PATH-AUTOFIX.story.md REQ-AUTOFIX-FORMAT
97
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces presence and correctness of the story identifier by supplying the standard ".story.md" suffix when no extension is provided.
98
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT
99
+ // @supports docs/stories/008.0-DEV-AUTO-FIX.story.md REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE REQ-AUTOFIX-SAFE
100
+ // @supports docs/stories/010.2-REQ-STORY-PATH-AUTOFIX.story.md REQ-AUTOFIX-FORMAT
101
101
  return `${original}.story.md`;
102
102
  }
103
103
  /**
@@ -112,14 +112,14 @@ function getFixedStoryPath(original) {
112
112
  function buildStoryErrorMessage(kind, value, options) {
113
113
  const example = options.storyExample || exports.STORY_EXAMPLE_PATH;
114
114
  // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md | REQ-ERROR-SPECIFICITY - Use a dedicated message variant when the @story value is completely missing.
115
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces presence of the story identifier by emitting a targeted message when the @story value is absent.
115
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces presence of the story identifier by emitting a targeted message when the @story value is absent.
116
116
  if (kind === "missing") {
117
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-ERROR-SPECIFICITY
118
- // @implements docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-ERROR-SPECIFICITY
117
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-ERROR-SPECIFICITY
118
+ // @supports docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-ERROR-SPECIFICITY
119
119
  return `Missing story path for @story annotation. Expected a path like "${example}".`;
120
120
  }
121
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-ERROR-SPECIFICITY
122
- // @implements docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-ERROR-SPECIFICITY
121
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-ERROR-SPECIFICITY
122
+ // @supports docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-ERROR-SPECIFICITY
123
123
  return `Invalid story path "${value ?? ""}" for @story annotation. Expected a path like "${example}".`;
124
124
  }
125
125
  /**
@@ -134,13 +134,13 @@ function buildStoryErrorMessage(kind, value, options) {
134
134
  function buildReqErrorMessage(kind, value, options) {
135
135
  const example = options.reqExample || (0, valid_annotation_options_1.getDefaultReqExample)();
136
136
  // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md | REQ-ERROR-SPECIFICITY - Distinguish a completely missing @req from one that is present but malformed.
137
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces presence of the requirement identifier by emitting a specific message when the @req value is missing.
137
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces presence of the requirement identifier by emitting a specific message when the @req value is missing.
138
138
  if (kind === "missing") {
139
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-ERROR-SPECIFICITY
140
- // @implements docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-ERROR-SPECIFICITY
139
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-ERROR-SPECIFICITY
140
+ // @supports docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-ERROR-SPECIFICITY
141
141
  return `Missing requirement ID for @req annotation. Expected an identifier like "${example}".`;
142
142
  }
143
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-ERROR-SPECIFICITY
144
- // @implements docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-ERROR-SPECIFICITY
143
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-ERROR-SPECIFICITY
144
+ // @supports docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-ERROR-SPECIFICITY
145
145
  return `Invalid requirement ID "${value ?? ""}" for @req annotation. Expected an identifier like "${example}" (uppercase letters, numbers, and dashes only).`;
146
146
  }