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.
- package/README.md +39 -1
- package/lib/src/index.d.ts +30 -27
- package/lib/src/index.js +51 -31
- package/lib/src/maintenance/cli.d.ts +12 -0
- package/lib/src/maintenance/cli.js +279 -0
- package/lib/src/maintenance/detect.js +27 -12
- package/lib/src/maintenance/update.js +42 -34
- package/lib/src/maintenance/utils.js +30 -30
- package/lib/src/rules/helpers/require-story-io.js +51 -15
- package/lib/src/rules/helpers/valid-annotation-format-internal.d.ts +30 -0
- package/lib/src/rules/helpers/valid-annotation-format-internal.js +36 -0
- package/lib/src/rules/helpers/valid-annotation-options.d.ts +118 -0
- package/lib/src/rules/helpers/valid-annotation-options.js +167 -0
- package/lib/src/rules/helpers/valid-annotation-utils.d.ts +68 -0
- package/lib/src/rules/helpers/valid-annotation-utils.js +103 -0
- package/lib/src/rules/helpers/valid-implements-utils.d.ts +75 -0
- package/lib/src/rules/helpers/valid-implements-utils.js +149 -0
- package/lib/src/rules/helpers/valid-story-reference-helpers.d.ts +67 -0
- package/lib/src/rules/helpers/valid-story-reference-helpers.js +92 -0
- package/lib/src/rules/prefer-implements-annotation.d.ts +39 -0
- package/lib/src/rules/prefer-implements-annotation.js +276 -0
- package/lib/src/rules/valid-annotation-format.js +255 -208
- package/lib/src/rules/valid-req-reference.js +210 -29
- package/lib/src/rules/valid-story-reference.d.ts +7 -0
- package/lib/src/rules/valid-story-reference.js +38 -80
- package/lib/src/utils/annotation-checker.js +2 -145
- package/lib/src/utils/branch-annotation-helpers.js +12 -3
- package/lib/src/utils/reqAnnotationDetection.d.ts +6 -0
- package/lib/src/utils/reqAnnotationDetection.js +152 -0
- package/lib/tests/maintenance/cli.test.d.ts +1 -0
- package/lib/tests/maintenance/cli.test.js +172 -0
- package/lib/tests/plugin-default-export-and-configs.test.js +3 -0
- package/lib/tests/rules/prefer-implements-annotation.test.d.ts +1 -0
- package/lib/tests/rules/prefer-implements-annotation.test.js +84 -0
- package/lib/tests/rules/require-branch-annotation.test.js +3 -2
- package/lib/tests/rules/require-req-annotation.test.js +57 -68
- package/lib/tests/rules/require-story-annotation.test.js +13 -28
- package/lib/tests/rules/require-story-core-edgecases.test.js +3 -58
- package/lib/tests/rules/require-story-core.autofix.test.js +5 -41
- package/lib/tests/rules/valid-annotation-format.test.js +395 -40
- package/lib/tests/rules/valid-req-reference.test.js +34 -0
- package/lib/tests/utils/annotation-checker.test.d.ts +23 -0
- package/lib/tests/utils/annotation-checker.test.js +24 -17
- package/lib/tests/utils/require-story-core-test-helpers.d.ts +10 -0
- package/lib/tests/utils/require-story-core-test-helpers.js +75 -0
- package/lib/tests/utils/ts-language-options.d.ts +22 -0
- package/lib/tests/utils/ts-language-options.js +27 -0
- package/package.json +12 -3
|
@@ -1,126 +1,62 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const
|
|
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
|
-
*
|
|
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
|
-
* @
|
|
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
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
*
|
|
23
|
+
* Compute the text replacement for an invalid @story annotation within a comment.
|
|
83
24
|
*
|
|
84
|
-
*
|
|
85
|
-
* -
|
|
86
|
-
* -
|
|
87
|
-
* and
|
|
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
|
|
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-
|
|
93
|
-
* @req REQ-AUTOFIX-
|
|
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
|
|
97
|
-
|
|
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
|
-
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
|
145
|
-
|
|
146
|
-
|
|
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: {
|
|
172
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
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
|
-
|
|
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 @
|
|
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 @
|
|
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
|
};
|