eslint-plugin-traceability 1.4.2 → 1.4.3
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.
|
@@ -50,15 +50,36 @@ function updateAnnotationReferences(codebasePath, oldPath, newPath) {
|
|
|
50
50
|
const escapedOldPath = oldPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
51
51
|
const regex = new RegExp(`(@story\\s*)${escapedOldPath}`, "g");
|
|
52
52
|
const files = (0, utils_1.getAllFiles)(codebasePath);
|
|
53
|
+
/**
|
|
54
|
+
* Iterate over all files and replace annotation references
|
|
55
|
+
* @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
|
|
56
|
+
* @req REQ-MAINT-UPDATE
|
|
57
|
+
*/
|
|
53
58
|
for (const fullPath of files) {
|
|
54
59
|
const stat = fs.statSync(fullPath);
|
|
60
|
+
/**
|
|
61
|
+
* Skip non-files in iteration
|
|
62
|
+
* @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
|
|
63
|
+
* @req REQ-MAINT-UPDATE
|
|
64
|
+
*/
|
|
55
65
|
if (!stat.isFile())
|
|
56
66
|
continue;
|
|
57
67
|
const content = fs.readFileSync(fullPath, "utf8");
|
|
58
|
-
const newContent = content.replace(regex,
|
|
68
|
+
const newContent = content.replace(regex,
|
|
69
|
+
/**
|
|
70
|
+
* Replacement callback to update annotation references
|
|
71
|
+
* @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
|
|
72
|
+
* @req REQ-MAINT-UPDATE
|
|
73
|
+
*/
|
|
74
|
+
(match, p1) => {
|
|
59
75
|
replacementCount++;
|
|
60
76
|
return `${p1}${newPath}`;
|
|
61
77
|
});
|
|
78
|
+
/**
|
|
79
|
+
* Write file only if content changed
|
|
80
|
+
* @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
|
|
81
|
+
* @req REQ-MAINT-UPDATE
|
|
82
|
+
*/
|
|
62
83
|
if (newContent !== content) {
|
|
63
84
|
fs.writeFileSync(fullPath, newContent, "utf8");
|
|
64
85
|
}
|
|
@@ -242,6 +242,12 @@ const rule = {
|
|
|
242
242
|
}
|
|
243
243
|
reportMissing(context, sourceCode, node, target);
|
|
244
244
|
},
|
|
245
|
+
/**
|
|
246
|
+
* Handle FunctionExpression nodes
|
|
247
|
+
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
248
|
+
* @req REQ-ANNOTATION-REQUIRED
|
|
249
|
+
* @param {any} node - FunctionExpression AST node
|
|
250
|
+
*/
|
|
245
251
|
FunctionExpression(node) {
|
|
246
252
|
if (!shouldProcessNode(node, scope, exportPriority))
|
|
247
253
|
return;
|
|
@@ -250,23 +256,47 @@ const rule = {
|
|
|
250
256
|
const target = resolveTargetNode(sourceCode, node);
|
|
251
257
|
reportMissing(context, sourceCode, node, target);
|
|
252
258
|
},
|
|
259
|
+
/**
|
|
260
|
+
* Handle ArrowFunctionExpression nodes
|
|
261
|
+
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
262
|
+
* @req REQ-ANNOTATION-REQUIRED
|
|
263
|
+
* @param {any} node - ArrowFunctionExpression AST node
|
|
264
|
+
*/
|
|
253
265
|
ArrowFunctionExpression(node) {
|
|
254
266
|
if (!shouldProcessNode(node, scope, exportPriority))
|
|
255
267
|
return;
|
|
256
268
|
const target = resolveTargetNode(sourceCode, node);
|
|
257
269
|
reportMissing(context, sourceCode, node, target);
|
|
258
270
|
},
|
|
271
|
+
/**
|
|
272
|
+
* Handle TSDeclareFunction nodes
|
|
273
|
+
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
274
|
+
* @req REQ-ANNOTATION-REQUIRED
|
|
275
|
+
* @param {any} node - TSDeclareFunction AST node
|
|
276
|
+
*/
|
|
259
277
|
TSDeclareFunction(node) {
|
|
260
278
|
if (!shouldProcessNode(node, scope, exportPriority))
|
|
261
279
|
return;
|
|
262
280
|
reportMissing(context, sourceCode, node, node);
|
|
263
281
|
},
|
|
282
|
+
/**
|
|
283
|
+
* Handle TSMethodSignature nodes
|
|
284
|
+
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
285
|
+
* @req REQ-ANNOTATION-REQUIRED
|
|
286
|
+
* @param {any} node - TSMethodSignature AST node
|
|
287
|
+
*/
|
|
264
288
|
TSMethodSignature(node) {
|
|
265
289
|
if (!shouldProcessNode(node, scope, exportPriority))
|
|
266
290
|
return;
|
|
267
291
|
const target = resolveTargetNode(sourceCode, node);
|
|
268
292
|
reportMissing(context, sourceCode, node, target);
|
|
269
293
|
},
|
|
294
|
+
/**
|
|
295
|
+
* Handle MethodDefinition nodes
|
|
296
|
+
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
297
|
+
* @req REQ-ANNOTATION-REQUIRED
|
|
298
|
+
* @param {any} node - MethodDefinition AST node
|
|
299
|
+
*/
|
|
270
300
|
MethodDefinition(node) {
|
|
271
301
|
if (!shouldProcessNode(node, scope, exportPriority))
|
|
272
302
|
return;
|
|
@@ -32,10 +32,19 @@ exports.DEFAULT_BRANCH_TYPES = [
|
|
|
32
32
|
function validateBranchTypes(context) {
|
|
33
33
|
const options = context.options[0] || {};
|
|
34
34
|
if (Array.isArray(options.branchTypes)) {
|
|
35
|
+
// @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
36
|
+
// @req REQ-TRACEABILITY-FILTER-CALLBACK - Trace filter callback for invalid branch type detection
|
|
35
37
|
const invalidTypes = options.branchTypes.filter((t) => !exports.DEFAULT_BRANCH_TYPES.includes(t));
|
|
36
38
|
if (invalidTypes.length > 0) {
|
|
39
|
+
/**
|
|
40
|
+
* Program listener produced when configuration is invalid.
|
|
41
|
+
* @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
42
|
+
* @req REQ-TRACEABILITY-PROGRAM-LISTENER - Trace Program listener reporting invalid config values
|
|
43
|
+
*/
|
|
37
44
|
return {
|
|
38
45
|
Program(node) {
|
|
46
|
+
// @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
47
|
+
// @req REQ-TRACEABILITY-FOR-EACH-CALLBACK - Trace reporting for each invalid type
|
|
39
48
|
invalidTypes.forEach((t) => {
|
|
40
49
|
context.report({
|
|
41
50
|
node,
|
|
@@ -61,6 +70,8 @@ function gatherBranchCommentText(sourceCode, node) {
|
|
|
61
70
|
const startLine = node.loc.start.line;
|
|
62
71
|
let i = startLine - PRE_COMMENT_OFFSET;
|
|
63
72
|
const comments = [];
|
|
73
|
+
// @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
74
|
+
// @req REQ-TRACEABILITY-WHILE - Trace while loop that collects preceding comments for SwitchCase
|
|
64
75
|
while (i >= 0 && /^\s*(\/\/|\/\*)/.test(lines[i])) {
|
|
65
76
|
comments.unshift(lines[i].trim());
|
|
66
77
|
i--;
|
|
@@ -68,6 +79,8 @@ function gatherBranchCommentText(sourceCode, node) {
|
|
|
68
79
|
return comments.join(" ");
|
|
69
80
|
}
|
|
70
81
|
const comments = sourceCode.getCommentsBefore(node) || [];
|
|
82
|
+
// @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
83
|
+
// @req REQ-TRACEABILITY-MAP-CALLBACK - Trace mapping of comment nodes to their text values
|
|
71
84
|
return comments.map((c) => c.value).join(" ");
|
|
72
85
|
}
|
|
73
86
|
/**
|
|
@@ -82,7 +95,10 @@ function reportMissingStory(context, node, options) {
|
|
|
82
95
|
node,
|
|
83
96
|
messageId: "missingAnnotation",
|
|
84
97
|
data: { missing: "@story" },
|
|
85
|
-
fix:
|
|
98
|
+
fix:
|
|
99
|
+
// @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
100
|
+
// @req REQ-TRACEABILITY-FIX-ARROW - Trace fixer arrow function used to insert missing @story
|
|
101
|
+
(fixer) => fixer.insertTextBeforeRange([insertPos, insertPos], `${indent}// @story <story-file>.story.md\n`),
|
|
86
102
|
});
|
|
87
103
|
storyFixCountRef.count++;
|
|
88
104
|
}
|
|
@@ -106,7 +122,10 @@ function reportMissingReq(context, node, options) {
|
|
|
106
122
|
node,
|
|
107
123
|
messageId: "missingAnnotation",
|
|
108
124
|
data: { missing: "@req" },
|
|
109
|
-
fix:
|
|
125
|
+
fix:
|
|
126
|
+
// @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
127
|
+
// @req REQ-TRACEABILITY-FIX-ARROW - Trace fixer arrow function used to insert missing @req
|
|
128
|
+
(fixer) => fixer.insertTextBeforeRange([insertPos, insertPos], `${indent}// @req <REQ-ID>\n`),
|
|
110
129
|
});
|
|
111
130
|
}
|
|
112
131
|
else {
|
|
@@ -144,5 +163,10 @@ function reportMissingAnnotations(context, node, storyFixCountRef) {
|
|
|
144
163
|
args: [context, node, { indent, insertPos, missingStory }],
|
|
145
164
|
},
|
|
146
165
|
];
|
|
147
|
-
|
|
166
|
+
// @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
167
|
+
// @req REQ-TRACEABILITY-ACTIONS-FOREACH - Trace processing of actions array to report missing annotations
|
|
168
|
+
actions.forEach(({ missing, fn, args }) =>
|
|
169
|
+
// @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
170
|
+
// @req REQ-TRACEABILITY-FOR-EACH-CALLBACK - Trace callback handling for each action item
|
|
171
|
+
missing && fn(...args));
|
|
148
172
|
}
|
|
@@ -87,11 +87,24 @@ describe("detectStaleAnnotations isolated (Story 009.0-DEV-MAINTENANCE-TOOLS)",
|
|
|
87
87
|
`;
|
|
88
88
|
fs.writeFileSync(filePath, content, "utf8");
|
|
89
89
|
// Remove read permission
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
90
|
+
try {
|
|
91
|
+
fs.chmodSync(dir, 0o000);
|
|
92
|
+
expect(() => (0, detect_1.detectStaleAnnotations)(tmpDir2)).toThrow();
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
// Restore permissions and cleanup temporary directory, ignoring errors during cleanup
|
|
96
|
+
try {
|
|
97
|
+
fs.chmodSync(dir, 0o700);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// ignore
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
fs.rmSync(tmpDir2, { recursive: true, force: true });
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// ignore
|
|
107
|
+
}
|
|
108
|
+
}
|
|
96
109
|
});
|
|
97
110
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-traceability",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.3",
|
|
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",
|