eslint-plugin-traceability 1.4.12 → 1.5.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/lib/src/index.js +2 -2
- package/lib/src/rules/require-req-annotation.js +2 -17
- package/lib/src/rules/valid-annotation-format.d.ts +0 -8
- package/lib/src/rules/valid-annotation-format.js +181 -31
- package/lib/src/utils/annotation-checker.js +8 -0
- package/lib/tests/rules/require-req-annotation.test.js +6 -4
- package/lib/tests/rules/valid-annotation-format.test.js +153 -10
- package/package.json +1 -1
package/lib/src/index.js
CHANGED
|
@@ -77,7 +77,7 @@ const configs = {
|
|
|
77
77
|
"traceability/require-story-annotation": "error",
|
|
78
78
|
"traceability/require-req-annotation": "error",
|
|
79
79
|
"traceability/require-branch-annotation": "error",
|
|
80
|
-
"traceability/valid-annotation-format": "
|
|
80
|
+
"traceability/valid-annotation-format": "warn",
|
|
81
81
|
"traceability/valid-story-reference": "error",
|
|
82
82
|
"traceability/valid-req-reference": "error",
|
|
83
83
|
},
|
|
@@ -92,7 +92,7 @@ const configs = {
|
|
|
92
92
|
"traceability/require-story-annotation": "error",
|
|
93
93
|
"traceability/require-req-annotation": "error",
|
|
94
94
|
"traceability/require-branch-annotation": "error",
|
|
95
|
-
"traceability/valid-annotation-format": "
|
|
95
|
+
"traceability/valid-annotation-format": "warn",
|
|
96
96
|
"traceability/valid-story-reference": "error",
|
|
97
97
|
"traceability/valid-req-reference": "error",
|
|
98
98
|
},
|
|
@@ -22,7 +22,7 @@ exports.default = {
|
|
|
22
22
|
recommended: "error",
|
|
23
23
|
},
|
|
24
24
|
messages: {
|
|
25
|
-
missingReq: "Missing @req annotation",
|
|
25
|
+
missingReq: "Missing @req annotation for function '{{name}}' (REQ-ANNOTATION-REQUIRED)",
|
|
26
26
|
},
|
|
27
27
|
schema: [],
|
|
28
28
|
},
|
|
@@ -32,7 +32,6 @@ exports.default = {
|
|
|
32
32
|
* @req REQ-FUNCTION-DETECTION - Detect function declarations, expressions, arrow functions, and methods
|
|
33
33
|
*/
|
|
34
34
|
create(context) {
|
|
35
|
-
const sourceCode = context.getSourceCode();
|
|
36
35
|
return {
|
|
37
36
|
/**
|
|
38
37
|
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
@@ -40,21 +39,7 @@ exports.default = {
|
|
|
40
39
|
* @req REQ-ANNOTATION-REQUIRED - Enforce @req annotation on function declarations
|
|
41
40
|
*/
|
|
42
41
|
FunctionDeclaration(node) {
|
|
43
|
-
|
|
44
|
-
if (!jsdoc || !jsdoc.value.includes("@req")) {
|
|
45
|
-
context.report({
|
|
46
|
-
node,
|
|
47
|
-
messageId: "missingReq",
|
|
48
|
-
/**
|
|
49
|
-
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
50
|
-
* @req REQ-AUTOFIX - Provide automatic fix to insert @req annotation
|
|
51
|
-
* @req REQ-ANNOTATION-REQUIRED - Ensure inserted fix contains @req placeholder
|
|
52
|
-
*/
|
|
53
|
-
fix(fixer) {
|
|
54
|
-
return fixer.insertTextBefore(node, "/** @req <REQ-ID> */\n");
|
|
55
|
-
},
|
|
56
|
-
});
|
|
57
|
-
}
|
|
42
|
+
return (0, annotation_checker_1.checkReqAnnotation)(context, node);
|
|
58
43
|
},
|
|
59
44
|
/**
|
|
60
45
|
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
@@ -1,10 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Rule to validate @story and @req annotation format and syntax
|
|
3
|
-
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
4
|
-
* @req REQ-FORMAT-SPECIFICATION - Define clear format rules for @story and @req annotations
|
|
5
|
-
* @req REQ-SYNTAX-VALIDATION - Validate annotation syntax matches specification
|
|
6
|
-
* @req REQ-PATH-FORMAT - Validate @story paths follow expected patterns
|
|
7
|
-
* @req REQ-REQ-FORMAT - Validate @req identifiers follow expected patterns
|
|
8
|
-
*/
|
|
9
1
|
declare const _default: any;
|
|
10
2
|
export default _default;
|
|
@@ -1,13 +1,187 @@
|
|
|
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
4
|
/**
|
|
4
|
-
*
|
|
5
|
+
* Normalize a raw comment line to make annotation parsing more robust.
|
|
6
|
+
*
|
|
7
|
+
* This function trims whitespace, keeps any annotation tags that appear
|
|
8
|
+
* later in the line, and supports common JSDoc styles such as leading "*".
|
|
9
|
+
*
|
|
10
|
+
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
11
|
+
* @req REQ-FLEXIBLE-PARSING - Support reasonable variations in whitespace and formatting
|
|
12
|
+
*/
|
|
13
|
+
function normalizeCommentLine(rawLine) {
|
|
14
|
+
const trimmed = rawLine.trim();
|
|
15
|
+
if (!trimmed) {
|
|
16
|
+
return "";
|
|
17
|
+
}
|
|
18
|
+
const annotationMatch = trimmed.match(/@story\b|@req\b/);
|
|
19
|
+
if (!annotationMatch || annotationMatch.index === undefined) {
|
|
20
|
+
const withoutLeadingStar = trimmed.replace(/^\*\s?/, "");
|
|
21
|
+
return withoutLeadingStar;
|
|
22
|
+
}
|
|
23
|
+
return trimmed.slice(annotationMatch.index);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Collapse internal whitespace in an annotation value so that multi-line
|
|
27
|
+
* annotations are treated as a single logical value.
|
|
28
|
+
*
|
|
29
|
+
* Example:
|
|
30
|
+
* "docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md" across
|
|
31
|
+
* multiple lines will be collapsed before validation.
|
|
32
|
+
*
|
|
33
|
+
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
34
|
+
* @req REQ-MULTILINE-SUPPORT - Handle annotations split across multiple lines
|
|
35
|
+
*/
|
|
36
|
+
function collapseAnnotationValue(value) {
|
|
37
|
+
return value.replace(/\s+/g, "");
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Build a detailed error message for invalid @story annotations.
|
|
41
|
+
*
|
|
42
|
+
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
43
|
+
* @req REQ-ERROR-SPECIFICITY - Provide specific error messages for different format violations
|
|
44
|
+
*/
|
|
45
|
+
function buildStoryErrorMessage(kind, value) {
|
|
46
|
+
if (kind === "missing") {
|
|
47
|
+
return `Missing story path for @story annotation. Expected a path like "${STORY_EXAMPLE_PATH}".`;
|
|
48
|
+
}
|
|
49
|
+
return `Invalid story path "${value ?? ""}" for @story annotation. Expected a path like "${STORY_EXAMPLE_PATH}".`;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Build a detailed error message for invalid @req annotations.
|
|
53
|
+
*
|
|
54
|
+
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
55
|
+
* @req REQ-ERROR-SPECIFICITY - Provide specific error messages for different format violations
|
|
56
|
+
*/
|
|
57
|
+
function buildReqErrorMessage(kind, value) {
|
|
58
|
+
if (kind === "missing") {
|
|
59
|
+
return 'Missing requirement ID for @req annotation. Expected an identifier like "REQ-EXAMPLE".';
|
|
60
|
+
}
|
|
61
|
+
return `Invalid requirement ID "${value ?? ""}" for @req annotation. Expected an identifier like "REQ-EXAMPLE" (uppercase letters, numbers, and dashes only).`;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Validate a @story annotation value and report detailed errors when needed.
|
|
65
|
+
*
|
|
5
66
|
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
6
|
-
* @req REQ-FORMAT-SPECIFICATION - Define clear format rules for @story and @req annotations
|
|
7
|
-
* @req REQ-SYNTAX-VALIDATION - Validate annotation syntax matches specification
|
|
8
67
|
* @req REQ-PATH-FORMAT - Validate @story paths follow expected patterns
|
|
68
|
+
* @req REQ-ERROR-SPECIFICITY - Provide specific error messages for different format violations
|
|
69
|
+
*/
|
|
70
|
+
function validateStoryAnnotation(context, comment, rawValue) {
|
|
71
|
+
const trimmed = rawValue.trim();
|
|
72
|
+
if (!trimmed) {
|
|
73
|
+
context.report({
|
|
74
|
+
node: comment,
|
|
75
|
+
messageId: "invalidStoryFormat",
|
|
76
|
+
data: { details: buildStoryErrorMessage("missing", null) },
|
|
77
|
+
});
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const collapsed = collapseAnnotationValue(trimmed);
|
|
81
|
+
const pathPattern = /^docs\/stories\/[0-9]+\.[0-9]+-DEV-[\w-]+\.story\.md$/;
|
|
82
|
+
if (!pathPattern.test(collapsed)) {
|
|
83
|
+
context.report({
|
|
84
|
+
node: comment,
|
|
85
|
+
messageId: "invalidStoryFormat",
|
|
86
|
+
data: { details: buildStoryErrorMessage("invalid", collapsed) },
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Validate a @req annotation value and report detailed errors when needed.
|
|
92
|
+
*
|
|
93
|
+
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
9
94
|
* @req REQ-REQ-FORMAT - Validate @req identifiers follow expected patterns
|
|
95
|
+
* @req REQ-ERROR-SPECIFICITY - Provide specific error messages for different format violations
|
|
96
|
+
*/
|
|
97
|
+
function validateReqAnnotation(context, comment, rawValue) {
|
|
98
|
+
const trimmed = rawValue.trim();
|
|
99
|
+
if (!trimmed) {
|
|
100
|
+
context.report({
|
|
101
|
+
node: comment,
|
|
102
|
+
messageId: "invalidReqFormat",
|
|
103
|
+
data: { details: buildReqErrorMessage("missing", null) },
|
|
104
|
+
});
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const collapsed = collapseAnnotationValue(trimmed);
|
|
108
|
+
const reqPattern = /^REQ-[A-Z0-9-]+$/;
|
|
109
|
+
if (!reqPattern.test(collapsed)) {
|
|
110
|
+
context.report({
|
|
111
|
+
node: comment,
|
|
112
|
+
messageId: "invalidReqFormat",
|
|
113
|
+
data: { details: buildReqErrorMessage("invalid", collapsed) },
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Process a single comment node and validate any @story/@req annotations it contains.
|
|
119
|
+
*
|
|
120
|
+
* Supports annotations whose values span multiple lines within the same
|
|
121
|
+
* comment block, collapsing whitespace so that the logical value can be
|
|
122
|
+
* validated against the configured patterns.
|
|
123
|
+
*
|
|
124
|
+
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
125
|
+
* @req REQ-MULTILINE-SUPPORT - Handle annotations split across multiple lines
|
|
126
|
+
* @req REQ-FLEXIBLE-PARSING - Support reasonable variations in whitespace and formatting
|
|
10
127
|
*/
|
|
128
|
+
function processComment(context, comment) {
|
|
129
|
+
const rawLines = (comment.value || "").split(/\r?\n/);
|
|
130
|
+
let pending = null;
|
|
131
|
+
/**
|
|
132
|
+
* Finalize and validate the currently pending annotation, if any.
|
|
133
|
+
*
|
|
134
|
+
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
135
|
+
* @req REQ-SYNTAX-VALIDATION - Validate annotation syntax matches specification
|
|
136
|
+
*/
|
|
137
|
+
function finalizePending() {
|
|
138
|
+
if (!pending) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
142
|
+
// @req REQ-SYNTAX-VALIDATION - Dispatch validation based on annotation type
|
|
143
|
+
if (pending.type === "story") {
|
|
144
|
+
validateStoryAnnotation(context, comment, pending.value);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
validateReqAnnotation(context, comment, pending.value);
|
|
148
|
+
}
|
|
149
|
+
pending = null;
|
|
150
|
+
}
|
|
151
|
+
rawLines.forEach((rawLine) => {
|
|
152
|
+
const normalized = normalizeCommentLine(rawLine);
|
|
153
|
+
if (!normalized) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const isStory = /@story\b/.test(normalized);
|
|
157
|
+
const isReq = /@req\b/.test(normalized);
|
|
158
|
+
// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
159
|
+
// @req REQ-SYNTAX-VALIDATION - Start new pending annotation when a tag is found
|
|
160
|
+
if (isStory || isReq) {
|
|
161
|
+
finalizePending();
|
|
162
|
+
const value = normalized.replace(/^@story\b|^@req\b/, "").trim();
|
|
163
|
+
pending = {
|
|
164
|
+
type: isStory ? "story" : "req",
|
|
165
|
+
value,
|
|
166
|
+
hasValue: value.trim().length > 0,
|
|
167
|
+
};
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
171
|
+
// @req REQ-MULTILINE-SUPPORT - Treat subsequent lines as continuation for pending annotation
|
|
172
|
+
if (pending) {
|
|
173
|
+
const continuation = normalized.trim();
|
|
174
|
+
if (!continuation) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
pending.value = pending.value
|
|
178
|
+
? `${pending.value} ${continuation}`
|
|
179
|
+
: continuation;
|
|
180
|
+
pending.hasValue = pending.hasValue || continuation.length > 0;
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
finalizePending();
|
|
184
|
+
}
|
|
11
185
|
exports.default = {
|
|
12
186
|
meta: {
|
|
13
187
|
type: "problem",
|
|
@@ -16,8 +190,8 @@ exports.default = {
|
|
|
16
190
|
recommended: "error",
|
|
17
191
|
},
|
|
18
192
|
messages: {
|
|
19
|
-
invalidStoryFormat: "
|
|
20
|
-
invalidReqFormat: "
|
|
193
|
+
invalidStoryFormat: "{{details}}",
|
|
194
|
+
invalidReqFormat: "{{details}}",
|
|
21
195
|
},
|
|
22
196
|
schema: [],
|
|
23
197
|
},
|
|
@@ -31,6 +205,7 @@ exports.default = {
|
|
|
31
205
|
return {
|
|
32
206
|
/**
|
|
33
207
|
* Program-level handler that inspects all comments for @story and @req tags
|
|
208
|
+
*
|
|
34
209
|
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
35
210
|
* @req REQ-PATH-FORMAT - Validate @story paths follow expected patterns
|
|
36
211
|
* @req REQ-REQ-FORMAT - Validate @req identifiers follow expected patterns
|
|
@@ -38,32 +213,7 @@ exports.default = {
|
|
|
38
213
|
Program() {
|
|
39
214
|
const comments = sourceCode.getAllComments() || [];
|
|
40
215
|
comments.forEach((comment) => {
|
|
41
|
-
|
|
42
|
-
.split(/\r?\n/)
|
|
43
|
-
.map((l) => l.replace(/^[^@]*/, "").trim());
|
|
44
|
-
lines.forEach((line) => {
|
|
45
|
-
if (line.startsWith("@story")) {
|
|
46
|
-
const parts = line.split(/\s+/);
|
|
47
|
-
const storyPath = parts[1];
|
|
48
|
-
if (!storyPath ||
|
|
49
|
-
!/^docs\/stories\/[0-9]+\.[0-9]+-DEV-[\w-]+\.story\.md$/.test(storyPath)) {
|
|
50
|
-
context.report({
|
|
51
|
-
node: comment,
|
|
52
|
-
messageId: "invalidStoryFormat",
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
if (line.startsWith("@req")) {
|
|
57
|
-
const parts = line.split(/\s+/);
|
|
58
|
-
const reqId = parts[1];
|
|
59
|
-
if (!reqId || !/^REQ-[A-Z0-9-]+$/.test(reqId)) {
|
|
60
|
-
context.report({
|
|
61
|
-
node: comment,
|
|
62
|
-
messageId: "invalidReqFormat",
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
});
|
|
216
|
+
processComment(context, comment);
|
|
67
217
|
});
|
|
68
218
|
},
|
|
69
219
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.checkReqAnnotation = checkReqAnnotation;
|
|
4
|
+
const require_story_utils_1 = require("../rules/helpers/require-story-utils");
|
|
4
5
|
/**
|
|
5
6
|
* Helper to retrieve the JSDoc comment for a node.
|
|
6
7
|
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
@@ -65,13 +66,20 @@ function createMissingReqFix(node) {
|
|
|
65
66
|
}
|
|
66
67
|
/**
|
|
67
68
|
* Helper to report a missing @req annotation via the ESLint context API.
|
|
69
|
+
* Uses getNodeName to provide a readable name for the node.
|
|
68
70
|
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
71
|
+
* @story docs/stories/007.0-DEV-ERROR-REPORTING.story.md
|
|
69
72
|
* @req REQ-ANNOTATION-REPORTING - Report missing @req annotation to context
|
|
73
|
+
* @req REQ-ERROR-SPECIFIC - Provide specific error details including node name
|
|
74
|
+
* @req REQ-ERROR-LOCATION - Include contextual location information in errors
|
|
70
75
|
*/
|
|
71
76
|
function reportMissing(context, node) {
|
|
77
|
+
const rawName = (0, require_story_utils_1.getNodeName)(node);
|
|
78
|
+
const name = rawName ?? "(anonymous)";
|
|
72
79
|
context.report({
|
|
73
80
|
node,
|
|
74
81
|
messageId: "missingReq",
|
|
82
|
+
data: { name },
|
|
75
83
|
fix: createMissingReqFix(node),
|
|
76
84
|
});
|
|
77
85
|
}
|
|
@@ -7,6 +7,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
7
7
|
* Tests for: docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
8
8
|
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
9
9
|
* @req REQ-ANNOTATION-REQUIRED - Verify require-req-annotation rule enforces @req on functions
|
|
10
|
+
* @story docs/stories/007.0-DEV-ERROR-REPORTING.story.md
|
|
11
|
+
* @req REQ-ERROR-SPECIFIC - Verify enhanced, specific error messaging behavior
|
|
10
12
|
*/
|
|
11
13
|
const eslint_1 = require("eslint");
|
|
12
14
|
const require_req_annotation_1 = __importDefault(require("../../src/rules/require-req-annotation"));
|
|
@@ -48,19 +50,19 @@ describe("Require Req Annotation Rule (Story 003.0-DEV-FUNCTION-ANNOTATIONS)", (
|
|
|
48
50
|
name: "[REQ-ANNOTATION-REQUIRED] missing @req on function without JSDoc",
|
|
49
51
|
code: `function baz() {}`,
|
|
50
52
|
output: `/** @req <REQ-ID> */\nfunction baz() {}`,
|
|
51
|
-
errors: [{ messageId: "missingReq" }],
|
|
53
|
+
errors: [{ messageId: "missingReq", data: { name: "baz" } }],
|
|
52
54
|
},
|
|
53
55
|
{
|
|
54
56
|
name: "[REQ-ANNOTATION-REQUIRED] missing @req on function with only @story annotation",
|
|
55
57
|
code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n */\nfunction qux() {}`,
|
|
56
58
|
output: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n */\n/** @req <REQ-ID> */\nfunction qux() {}`,
|
|
57
|
-
errors: [{ messageId: "missingReq" }],
|
|
59
|
+
errors: [{ messageId: "missingReq", data: { name: "qux" } }],
|
|
58
60
|
},
|
|
59
61
|
{
|
|
60
62
|
name: "[REQ-TYPESCRIPT-SUPPORT] missing @req on TSDeclareFunction",
|
|
61
63
|
code: `declare function baz(): void;`,
|
|
62
64
|
output: `/** @req <REQ-ID> */\ndeclare function baz(): void;`,
|
|
63
|
-
errors: [{ messageId: "missingReq" }],
|
|
65
|
+
errors: [{ messageId: "missingReq", data: { name: "baz" } }],
|
|
64
66
|
languageOptions: {
|
|
65
67
|
parser: require("@typescript-eslint/parser"),
|
|
66
68
|
parserOptions: { ecmaVersion: 2022, sourceType: "module" },
|
|
@@ -70,7 +72,7 @@ describe("Require Req Annotation Rule (Story 003.0-DEV-FUNCTION-ANNOTATIONS)", (
|
|
|
70
72
|
name: "[REQ-TYPESCRIPT-SUPPORT] missing @req on TSMethodSignature",
|
|
71
73
|
code: `interface I { method(): void; }`,
|
|
72
74
|
output: `interface I { /** @req <REQ-ID> */\nmethod(): void; }`,
|
|
73
|
-
errors: [{ messageId: "missingReq" }],
|
|
75
|
+
errors: [{ messageId: "missingReq", data: { name: "method" } }],
|
|
74
76
|
languageOptions: {
|
|
75
77
|
parser: require("@typescript-eslint/parser"),
|
|
76
78
|
parserOptions: { ecmaVersion: 2022, sourceType: "module" },
|
|
@@ -17,41 +17,184 @@ describe("Valid Annotation Format Rule (Story 005.0-DEV-ANNOTATION-VALIDATION)",
|
|
|
17
17
|
ruleTester.run("valid-annotation-format", valid_annotation_format_1.default, {
|
|
18
18
|
valid: [
|
|
19
19
|
{
|
|
20
|
-
name: "[REQ-PATH-FORMAT] valid story annotation format",
|
|
20
|
+
name: "[REQ-PATH-FORMAT] valid story annotation format (single-line)",
|
|
21
21
|
code: `// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md`,
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
|
-
name: "[REQ-REQ-FORMAT] valid req annotation format",
|
|
24
|
+
name: "[REQ-REQ-FORMAT] valid req annotation format (single-line)",
|
|
25
25
|
code: `// @req REQ-EXAMPLE`,
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
|
-
name: "[REQ-FORMAT-SPECIFICATION] valid block annotations",
|
|
28
|
+
name: "[REQ-FORMAT-SPECIFICATION] valid block annotations (single-line values)",
|
|
29
29
|
code: `/**
|
|
30
30
|
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
31
31
|
* @req REQ-VALID-EXAMPLE
|
|
32
|
+
*/`,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "[REQ-MULTILINE-SUPPORT] valid multi-line @story annotation value in block comment",
|
|
36
|
+
code: `/**
|
|
37
|
+
* @story docs/stories/005.0-
|
|
38
|
+
* DEV-ANNOTATION-VALIDATION.story.md
|
|
39
|
+
*/`,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: "[REQ-MULTILINE-SUPPORT] valid multi-line @req annotation value in block comment",
|
|
43
|
+
code: `/**
|
|
44
|
+
* @req REQ-
|
|
45
|
+
* EXAMPLE
|
|
46
|
+
*/`,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "[REQ-FLEXIBLE-PARSING] valid JSDoc-style comment with leading stars and spacing",
|
|
50
|
+
code: `/**
|
|
51
|
+
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
52
|
+
* @req REQ-FLEXIBLE-PARSING
|
|
32
53
|
*/`,
|
|
33
54
|
},
|
|
34
55
|
],
|
|
35
56
|
invalid: [
|
|
36
57
|
{
|
|
37
|
-
name: "[REQ-PATH-FORMAT] missing story path",
|
|
58
|
+
name: "[REQ-PATH-FORMAT] missing story path (single line)",
|
|
38
59
|
code: `// @story`,
|
|
39
|
-
errors: [
|
|
60
|
+
errors: [
|
|
61
|
+
{
|
|
62
|
+
messageId: "invalidStoryFormat",
|
|
63
|
+
data: {
|
|
64
|
+
details: 'Missing story path for @story annotation. Expected a path like "docs/stories/005.0-DEV-EXAMPLE.story.md".',
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
],
|
|
40
68
|
},
|
|
41
69
|
{
|
|
42
70
|
name: "[REQ-PATH-FORMAT] invalid story file extension",
|
|
43
71
|
code: `// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story`,
|
|
44
|
-
errors: [
|
|
72
|
+
errors: [
|
|
73
|
+
{
|
|
74
|
+
messageId: "invalidStoryFormat",
|
|
75
|
+
data: {
|
|
76
|
+
details: 'Invalid story path "docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story" for @story annotation. Expected a path like "docs/stories/005.0-DEV-EXAMPLE.story.md".',
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: "[REQ-PATH-FORMAT] missing extension in story path",
|
|
83
|
+
code: `// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION`,
|
|
84
|
+
errors: [
|
|
85
|
+
{
|
|
86
|
+
messageId: "invalidStoryFormat",
|
|
87
|
+
data: {
|
|
88
|
+
details: 'Invalid story path "docs/stories/005.0-DEV-ANNOTATION-VALIDATION" for @story annotation. Expected a path like "docs/stories/005.0-DEV-EXAMPLE.story.md".',
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: "[REQ-PATH-FORMAT] story path must not use path traversal",
|
|
95
|
+
code: `// @story ../docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md`,
|
|
96
|
+
errors: [
|
|
97
|
+
{
|
|
98
|
+
messageId: "invalidStoryFormat",
|
|
99
|
+
data: {
|
|
100
|
+
details: 'Invalid story path "../docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md" for @story annotation. Expected a path like "docs/stories/005.0-DEV-EXAMPLE.story.md".',
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
],
|
|
45
104
|
},
|
|
46
105
|
{
|
|
47
|
-
name: "[REQ-REQ-FORMAT] missing req id",
|
|
106
|
+
name: "[REQ-REQ-FORMAT] missing req id (single line)",
|
|
48
107
|
code: `// @req`,
|
|
49
|
-
errors: [
|
|
108
|
+
errors: [
|
|
109
|
+
{
|
|
110
|
+
messageId: "invalidReqFormat",
|
|
111
|
+
data: {
|
|
112
|
+
details: 'Missing requirement ID for @req annotation. Expected an identifier like "REQ-EXAMPLE".',
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
],
|
|
50
116
|
},
|
|
51
117
|
{
|
|
52
|
-
name: "[REQ-REQ-FORMAT] invalid req id format",
|
|
118
|
+
name: "[REQ-REQ-FORMAT] invalid req id format (single line)",
|
|
53
119
|
code: `// @req invalid-format`,
|
|
54
|
-
errors: [
|
|
120
|
+
errors: [
|
|
121
|
+
{
|
|
122
|
+
messageId: "invalidReqFormat",
|
|
123
|
+
data: {
|
|
124
|
+
details: 'Invalid requirement ID "invalid-format" for @req annotation. Expected an identifier like "REQ-EXAMPLE" (uppercase letters, numbers, and dashes only).',
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: "[REQ-REQ-FORMAT] missing req identifier with trailing space",
|
|
131
|
+
code: `// @req `,
|
|
132
|
+
errors: [
|
|
133
|
+
{
|
|
134
|
+
messageId: "invalidReqFormat",
|
|
135
|
+
data: {
|
|
136
|
+
details: 'Missing requirement ID for @req annotation. Expected an identifier like "REQ-EXAMPLE".',
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: "[REQ-MULTILINE-SUPPORT] missing story path with multi-line block comment",
|
|
143
|
+
code: `/**
|
|
144
|
+
* @story
|
|
145
|
+
*/`,
|
|
146
|
+
errors: [
|
|
147
|
+
{
|
|
148
|
+
messageId: "invalidStoryFormat",
|
|
149
|
+
data: {
|
|
150
|
+
details: 'Missing story path for @story annotation. Expected a path like "docs/stories/005.0-DEV-EXAMPLE.story.md".',
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: "[REQ-MULTILINE-SUPPORT] invalid multi-line story path after collapsing whitespace",
|
|
157
|
+
code: `/**
|
|
158
|
+
* @story docs/stories/005.0-
|
|
159
|
+
* DEV-ANNOTATION-VALIDATION.story
|
|
160
|
+
*/`,
|
|
161
|
+
errors: [
|
|
162
|
+
{
|
|
163
|
+
messageId: "invalidStoryFormat",
|
|
164
|
+
data: {
|
|
165
|
+
details: 'Invalid story path "docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story" for @story annotation. Expected a path like "docs/stories/005.0-DEV-EXAMPLE.story.md".',
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
name: "[REQ-MULTILINE-SUPPORT] missing req id with multi-line block comment",
|
|
172
|
+
code: `/**
|
|
173
|
+
* @req
|
|
174
|
+
*/`,
|
|
175
|
+
errors: [
|
|
176
|
+
{
|
|
177
|
+
messageId: "invalidReqFormat",
|
|
178
|
+
data: {
|
|
179
|
+
details: 'Missing requirement ID for @req annotation. Expected an identifier like "REQ-EXAMPLE".',
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
name: "[REQ-MULTILINE-SUPPORT] invalid multi-line req id after collapsing whitespace",
|
|
186
|
+
code: `/**
|
|
187
|
+
* @req invalid-
|
|
188
|
+
* format
|
|
189
|
+
*/`,
|
|
190
|
+
errors: [
|
|
191
|
+
{
|
|
192
|
+
messageId: "invalidReqFormat",
|
|
193
|
+
data: {
|
|
194
|
+
details: 'Invalid requirement ID "invalid-format" for @req annotation. Expected an identifier like "REQ-EXAMPLE" (uppercase letters, numbers, and dashes only).',
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
],
|
|
55
198
|
},
|
|
56
199
|
],
|
|
57
200
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-traceability",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "A customizable ESLint plugin that enforces traceability annotations in your code, ensuring each implementation is linked to its requirement or test case.",
|
|
5
5
|
"main": "lib/src/index.js",
|
|
6
6
|
"types": "lib/src/index.d.ts",
|