eslint-plugin-traceability 1.9.0 → 1.10.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/CHANGELOG.md +3 -3
- package/lib/src/rules/helpers/require-test-traceability-helpers.d.ts +34 -0
- package/lib/src/rules/helpers/require-test-traceability-helpers.js +258 -0
- package/lib/src/rules/helpers/valid-annotation-format-internal.d.ts +11 -0
- package/lib/src/rules/helpers/valid-annotation-format-internal.js +21 -0
- package/lib/src/rules/helpers/valid-annotation-format-validators.d.ts +125 -0
- package/lib/src/rules/helpers/valid-annotation-format-validators.js +270 -0
- package/lib/src/rules/require-test-traceability.d.ts +3 -0
- package/lib/src/rules/require-test-traceability.js +33 -90
- package/lib/src/rules/valid-annotation-format.js +10 -243
- package/lib/tests/rules/require-test-traceability.test.js +43 -6
- package/lib/tests/rules/valid-annotation-format.test.js +71 -0
- package/package.json +1 -1
- package/user-docs/api-reference.md +6 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
## [1.10.1](https://github.com/voder-ai/eslint-plugin-traceability/compare/v1.10.0...v1.10.1) (2025-12-05)
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
###
|
|
4
|
+
### Bug Fixes
|
|
5
5
|
|
|
6
|
-
*
|
|
6
|
+
* support JSDoc tag coexistence for annotation parsing ([31e9416](https://github.com/voder-ai/eslint-plugin-traceability/commit/31e9416d201fd347051bc44521d3d3b840a244a1))
|
|
7
7
|
|
|
8
8
|
# Changelog
|
|
9
9
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type TestTraceabilityAutoFixOptions = {
|
|
2
|
+
autoFixTestTemplate: boolean;
|
|
3
|
+
testSupportsTemplate?: string;
|
|
4
|
+
};
|
|
5
|
+
export type CallExpressionOptions = {
|
|
6
|
+
sourceCode: any;
|
|
7
|
+
describeRegex: RegExp;
|
|
8
|
+
requireDescribeStory: boolean;
|
|
9
|
+
requireTestReqPrefix: boolean;
|
|
10
|
+
autoFixTestPrefixFormat: boolean;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Determine if a file should be treated as a test file based on patterns.
|
|
14
|
+
*
|
|
15
|
+
* @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-PATTERN-DETECT
|
|
16
|
+
*/
|
|
17
|
+
export declare function determineIsTestFile(filename: string, rawPatterns?: string[]): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Ensure the file has a @supports annotation listing tested requirements.
|
|
20
|
+
*
|
|
21
|
+
* When auto-fix is enabled, a placeholder @supports JSDoc is inserted at the
|
|
22
|
+
* top of the file (after any shebang) using a safe, non-semantic template.
|
|
23
|
+
*
|
|
24
|
+
* @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-FILE-SUPPORTS REQ-TEST-SUPPORTS-VALID
|
|
25
|
+
* @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-TEMPLATE REQ-TEST-FIX-PLACEHOLDER REQ-TEST-FIX-SAFE REQ-TEST-FIX-PRESERVE REQ-TEST-FIX-NO-INFERENCE
|
|
26
|
+
*/
|
|
27
|
+
export declare function ensureFileSupportsAnnotation(context: any, sourceCode: any, autoFixOptions: TestTraceabilityAutoFixOptions): void;
|
|
28
|
+
/**
|
|
29
|
+
* Build a CallExpression visitor for the main rule create() function.
|
|
30
|
+
*
|
|
31
|
+
* @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-DESCRIBE-STORY REQ-TEST-IT-REQ-PREFIX REQ-TEST-NESTED-DESCRIBE REQ-TEST-ERROR-CONTEXT
|
|
32
|
+
* @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-PREFIX-FORMAT REQ-TEST-FIX-SAFE REQ-TEST-FIX-PRESERVE REQ-TEST-FIX-NO-INFERENCE
|
|
33
|
+
*/
|
|
34
|
+
export declare function handleCallExpression(context: any, options: CallExpressionOptions): (node: any) => void;
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.determineIsTestFile = determineIsTestFile;
|
|
4
|
+
exports.ensureFileSupportsAnnotation = ensureFileSupportsAnnotation;
|
|
5
|
+
exports.handleCallExpression = handleCallExpression;
|
|
6
|
+
/**
|
|
7
|
+
* Helper utilities for the require-test-traceability rule.
|
|
8
|
+
*
|
|
9
|
+
* @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-PATTERN-DETECT REQ-TEST-FRAMEWORK-COMPAT
|
|
10
|
+
* @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-TEMPLATE REQ-TEST-FIX-PREFIX-FORMAT REQ-TEST-FIX-SAFE REQ-TEST-FIX-PRESERVE REQ-TEST-FIX-PLACEHOLDER REQ-TEST-FIX-NO-INFERENCE
|
|
11
|
+
*/
|
|
12
|
+
const NOT_FOUND = -1;
|
|
13
|
+
const REQ_PREFIX_LENGTH = 3;
|
|
14
|
+
const QUOTES = ["'", '"', "`"];
|
|
15
|
+
/**
|
|
16
|
+
* Determine if a file should be treated as a test file based on patterns.
|
|
17
|
+
*
|
|
18
|
+
* @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-PATTERN-DETECT
|
|
19
|
+
*/
|
|
20
|
+
function determineIsTestFile(filename, rawPatterns = [
|
|
21
|
+
"/tests/",
|
|
22
|
+
"/test/",
|
|
23
|
+
"/__tests__",
|
|
24
|
+
".test.",
|
|
25
|
+
".spec.",
|
|
26
|
+
]) {
|
|
27
|
+
return rawPatterns.some((pattern) => filename.includes(pattern.replace("**", "")));
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Build the placeholder @supports template comment for a test file.
|
|
31
|
+
*
|
|
32
|
+
* @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-TEMPLATE REQ-TEST-FIX-PLACEHOLDER
|
|
33
|
+
*/
|
|
34
|
+
function buildSupportsTemplateComment(customTemplate) {
|
|
35
|
+
const baseTemplate = (customTemplate && customTemplate.trim()) ||
|
|
36
|
+
"@supports docs/stories/XXX.X-STORY-NAME.story.md REQ-XXX-YYY REQ-XXX-ZZZ";
|
|
37
|
+
const lines = [
|
|
38
|
+
"/**",
|
|
39
|
+
` * ${baseTemplate}`,
|
|
40
|
+
" * TODO: Replace the placeholder story path and REQ-IDs with real values for this test file.",
|
|
41
|
+
" */",
|
|
42
|
+
"",
|
|
43
|
+
];
|
|
44
|
+
return lines.join("\n");
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Insert the file-level @supports template comment at a safe location.
|
|
48
|
+
*
|
|
49
|
+
* The template is inserted after a shebang line if present, otherwise at the
|
|
50
|
+
* very start of the file. This preserves executable semantics while adding
|
|
51
|
+
* only comment text.
|
|
52
|
+
*
|
|
53
|
+
* @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-TEMPLATE REQ-TEST-FIX-SAFE REQ-TEST-FIX-PRESERVE REQ-TEST-FIX-NO-INFERENCE
|
|
54
|
+
*/
|
|
55
|
+
function insertSupportsTemplate(fixer, sourceCode, customTemplate) {
|
|
56
|
+
const text = sourceCode.text || "";
|
|
57
|
+
let insertIndex = 0;
|
|
58
|
+
// Preserve shebang: it must remain the very first characters in the file.
|
|
59
|
+
if (text.startsWith("#!")) {
|
|
60
|
+
const firstNewline = text.indexOf("\n");
|
|
61
|
+
insertIndex = firstNewline === NOT_FOUND ? text.length : firstNewline + 1;
|
|
62
|
+
}
|
|
63
|
+
const templateComment = buildSupportsTemplateComment(customTemplate);
|
|
64
|
+
return fixer.insertTextBeforeRange([insertIndex, insertIndex], templateComment);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Ensure the file has a @supports annotation listing tested requirements.
|
|
68
|
+
*
|
|
69
|
+
* When auto-fix is enabled, a placeholder @supports JSDoc is inserted at the
|
|
70
|
+
* top of the file (after any shebang) using a safe, non-semantic template.
|
|
71
|
+
*
|
|
72
|
+
* @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-FILE-SUPPORTS REQ-TEST-SUPPORTS-VALID
|
|
73
|
+
* @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-TEMPLATE REQ-TEST-FIX-PLACEHOLDER REQ-TEST-FIX-SAFE REQ-TEST-FIX-PRESERVE REQ-TEST-FIX-NO-INFERENCE
|
|
74
|
+
*/
|
|
75
|
+
function ensureFileSupportsAnnotation(context, sourceCode, autoFixOptions) {
|
|
76
|
+
const fileComments = sourceCode.getAllComments() || [];
|
|
77
|
+
const fileHasSupports = fileComments.some((comment) => /@supports\b/.test(comment.value || ""));
|
|
78
|
+
if (!fileHasSupports) {
|
|
79
|
+
const node = fileComments[0] || (sourceCode.ast && sourceCode.ast);
|
|
80
|
+
context.report({
|
|
81
|
+
node: node,
|
|
82
|
+
messageId: "missingFileSupports",
|
|
83
|
+
fix: autoFixOptions.autoFixTestTemplate === false
|
|
84
|
+
? undefined
|
|
85
|
+
: (fixer) => insertSupportsTemplate(fixer, sourceCode, autoFixOptions.testSupportsTemplate),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Check if a callee name corresponds to a test framework function.
|
|
91
|
+
*
|
|
92
|
+
* @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-FRAMEWORK-COMPAT
|
|
93
|
+
*/
|
|
94
|
+
function isTestCallName(name) {
|
|
95
|
+
return ["describe", "it", "test", "context"].includes(name);
|
|
96
|
+
}
|
|
97
|
+
function getCalleeName(node) {
|
|
98
|
+
if (node.callee.type === "Identifier") {
|
|
99
|
+
return node.callee.name;
|
|
100
|
+
}
|
|
101
|
+
if (node.callee.type === "MemberExpression" &&
|
|
102
|
+
node.callee.object.type === "Identifier") {
|
|
103
|
+
return node.callee.object.name;
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
function getFirstArgumentLiteral(node) {
|
|
108
|
+
const arg = node.arguments && node.arguments[0];
|
|
109
|
+
if (!arg)
|
|
110
|
+
return null;
|
|
111
|
+
if (arg.type === "Literal" && typeof arg.value === "string") {
|
|
112
|
+
return arg.value;
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Normalize a raw REQ identifier string to canonical REQ-XXX format.
|
|
118
|
+
*
|
|
119
|
+
* This helper performs only local, format-level normalization without
|
|
120
|
+
* inferring new requirement IDs.
|
|
121
|
+
*
|
|
122
|
+
* @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-PREFIX-FORMAT REQ-TEST-FIX-NO-INFERENCE
|
|
123
|
+
*/
|
|
124
|
+
function normalizeReqId(raw) {
|
|
125
|
+
let id = raw.trim().toUpperCase();
|
|
126
|
+
if (!id.startsWith("REQ")) {
|
|
127
|
+
return id;
|
|
128
|
+
}
|
|
129
|
+
let rest = id.slice(REQ_PREFIX_LENGTH);
|
|
130
|
+
// Drop leading separators after "REQ"
|
|
131
|
+
rest = rest.replace(/^[-_\s:]+/, "");
|
|
132
|
+
// Convert internal whitespace/underscores to hyphens
|
|
133
|
+
rest = rest.replace(/[\s_]+/g, "-");
|
|
134
|
+
// Collapse multiple hyphens
|
|
135
|
+
rest = rest.replace(/-+/g, "-");
|
|
136
|
+
return rest ? `REQ-${rest}` : "REQ-";
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Normalize malformed [REQ-XXX] prefixes in test names.
|
|
140
|
+
*
|
|
141
|
+
* Handles cases such as:
|
|
142
|
+
* - "[ REQ-XXX ] ..." -> "[REQ-XXX] ..."
|
|
143
|
+
* - "[REQ_XXX] ..." -> "[REQ-XXX] ..."
|
|
144
|
+
* - "(REQ-XXX) ..." -> "[REQ-XXX] ..."
|
|
145
|
+
* - "[req-xxx] ..." -> "[REQ-XXX] ..."
|
|
146
|
+
*
|
|
147
|
+
* Only operates when a REQ identifier is already present at the start of the
|
|
148
|
+
* string; it never invents new IDs.
|
|
149
|
+
*
|
|
150
|
+
* @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-PREFIX-FORMAT REQ-TEST-FIX-SAFE REQ-TEST-FIX-PRESERVE REQ-TEST-FIX-NO-INFERENCE
|
|
151
|
+
*/
|
|
152
|
+
function normalizeReqPrefixInDescription(description) {
|
|
153
|
+
const canonicalPattern = /^\[REQ-[^\]]+]/;
|
|
154
|
+
if (canonicalPattern.test(description)) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
// Leading square brackets with optional spacing.
|
|
158
|
+
const squareMatch = description.match(/^\[\s*(REQ[^\]]*?)\s*](.*)$/i);
|
|
159
|
+
if (squareMatch) {
|
|
160
|
+
const normalizedId = normalizeReqId(squareMatch[1]);
|
|
161
|
+
return `[${normalizedId}]${squareMatch[2] ?? ""}`;
|
|
162
|
+
}
|
|
163
|
+
// Leading parentheses with optional spacing.
|
|
164
|
+
const parenMatch = description.match(/^\(\s*(REQ[^)]*?)\s*\)(.*)$/i);
|
|
165
|
+
if (parenMatch) {
|
|
166
|
+
const normalizedId = normalizeReqId(parenMatch[1]);
|
|
167
|
+
return `[${normalizedId}]${parenMatch[2] ?? ""}`;
|
|
168
|
+
}
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Create a string literal with the same quote style as the original node.
|
|
173
|
+
*
|
|
174
|
+
* This helper rewrites only the literal value while preserving the original
|
|
175
|
+
* quoting character (`'`, `"`, or `` ` ``) and escaping rules.
|
|
176
|
+
*
|
|
177
|
+
* @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-PRESERVE
|
|
178
|
+
*/
|
|
179
|
+
function createUpdatedStringLiteralRaw(originalNode, newValue, sourceCode) {
|
|
180
|
+
const raw = sourceCode.getText(originalNode);
|
|
181
|
+
const firstChar = raw[0];
|
|
182
|
+
if (QUOTES.includes(firstChar)) {
|
|
183
|
+
const quote = firstChar;
|
|
184
|
+
const escaped = newValue
|
|
185
|
+
.replace(/\\/g, "\\\\")
|
|
186
|
+
.replace(new RegExp(`\\${quote}`, "g"), `\\${quote}`);
|
|
187
|
+
return `${quote}${escaped}${quote}`;
|
|
188
|
+
}
|
|
189
|
+
// Fallback: let JSON.stringify choose a safe representation.
|
|
190
|
+
return JSON.stringify(newValue);
|
|
191
|
+
}
|
|
192
|
+
function handleDescribeCall(context, node, description, options) {
|
|
193
|
+
const { describeRegex, requireDescribeStory } = options;
|
|
194
|
+
if (!requireDescribeStory)
|
|
195
|
+
return;
|
|
196
|
+
if (!describeRegex.test(description)) {
|
|
197
|
+
context.report({
|
|
198
|
+
node: node,
|
|
199
|
+
messageId: "missingDescribeStory",
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function handleItOrTestCall(context, node, description, options) {
|
|
204
|
+
const { sourceCode, requireTestReqPrefix, autoFixTestPrefixFormat } = options;
|
|
205
|
+
if (!requireTestReqPrefix)
|
|
206
|
+
return;
|
|
207
|
+
if (!/^\[REQ-[^\]]+]/.test(description)) {
|
|
208
|
+
const normalizedDescription = autoFixTestPrefixFormat !== false
|
|
209
|
+
? normalizeReqPrefixInDescription(description)
|
|
210
|
+
: null;
|
|
211
|
+
context.report({
|
|
212
|
+
node: node,
|
|
213
|
+
messageId: "missingReqPrefix",
|
|
214
|
+
...(autoFixTestPrefixFormat !== false &&
|
|
215
|
+
normalizedDescription !== null &&
|
|
216
|
+
node.arguments &&
|
|
217
|
+
node.arguments[0] &&
|
|
218
|
+
node.arguments[0].type === "Literal" &&
|
|
219
|
+
typeof node.arguments[0].value === "string"
|
|
220
|
+
? {
|
|
221
|
+
fix(fixer) {
|
|
222
|
+
const literalNode = node.arguments[0];
|
|
223
|
+
const newRaw = createUpdatedStringLiteralRaw(literalNode, normalizedDescription, sourceCode);
|
|
224
|
+
return fixer.replaceText(literalNode, newRaw);
|
|
225
|
+
},
|
|
226
|
+
}
|
|
227
|
+
: {}),
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Build a CallExpression visitor for the main rule create() function.
|
|
233
|
+
*
|
|
234
|
+
* @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-DESCRIBE-STORY REQ-TEST-IT-REQ-PREFIX REQ-TEST-NESTED-DESCRIBE REQ-TEST-ERROR-CONTEXT
|
|
235
|
+
* @supports docs/stories/021.0-DEV-TEST-ANNOTATION-AUTO-FIX.story.md REQ-TEST-FIX-PREFIX-FORMAT REQ-TEST-FIX-SAFE REQ-TEST-FIX-PRESERVE REQ-TEST-FIX-NO-INFERENCE
|
|
236
|
+
*/
|
|
237
|
+
function handleCallExpression(context, options) {
|
|
238
|
+
const { describeRegex, requireDescribeStory } = options;
|
|
239
|
+
return (node) => {
|
|
240
|
+
const calleeName = getCalleeName(node);
|
|
241
|
+
if (!calleeName || !isTestCallName(calleeName)) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const description = getFirstArgumentLiteral(node);
|
|
245
|
+
if (!description)
|
|
246
|
+
return;
|
|
247
|
+
if (calleeName === "describe") {
|
|
248
|
+
handleDescribeCall(context, node, description, {
|
|
249
|
+
describeRegex,
|
|
250
|
+
requireDescribeStory,
|
|
251
|
+
});
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
if (calleeName === "it" || calleeName === "test") {
|
|
255
|
+
handleItOrTestCall(context, node, description, options);
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
}
|
|
@@ -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;
|