eslint-plugin-traceability 1.4.3 → 1.4.4
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/maintenance/batch.js +1 -0
- package/lib/src/maintenance/detect.js +4 -0
- package/lib/src/maintenance/report.js +2 -0
- package/lib/src/maintenance/update.js +18 -0
- package/lib/src/maintenance/utils.js +33 -2
- package/lib/src/rules/helpers/require-story-core.d.ts +35 -0
- package/lib/src/rules/helpers/require-story-core.js +139 -0
- package/lib/src/rules/helpers/require-story-helpers.d.ts +124 -0
- package/lib/src/rules/helpers/require-story-helpers.js +293 -0
- package/lib/src/rules/helpers/require-story-io.d.ts +35 -0
- package/lib/src/rules/helpers/require-story-io.js +97 -0
- package/lib/src/rules/helpers/require-story-visitors.d.ts +13 -0
- package/lib/src/rules/helpers/require-story-visitors.js +163 -0
- package/lib/src/rules/require-story-annotation.d.ts +15 -0
- package/lib/src/rules/require-story-annotation.js +27 -268
- package/lib/src/utils/branch-annotation-helpers.js +91 -31
- package/lib/tests/rules/require-story-helpers.test.d.ts +1 -0
- package/lib/tests/rules/require-story-helpers.test.js +173 -0
- package/lib/tests/rules/require-story-io.edgecases.test.d.ts +6 -0
- package/lib/tests/rules/require-story-io.edgecases.test.js +50 -0
- package/package.json +3 -1
|
@@ -1,198 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
"FunctionDeclaration",
|
|
6
|
-
"FunctionExpression",
|
|
7
|
-
"ArrowFunctionExpression",
|
|
8
|
-
"MethodDefinition",
|
|
9
|
-
"TSDeclareFunction",
|
|
10
|
-
"TSMethodSignature",
|
|
11
|
-
];
|
|
12
|
-
const EXPORT_PRIORITY_VALUES = ["all", "exported", "non-exported"];
|
|
3
|
+
const require_story_visitors_1 = require("./helpers/require-story-visitors");
|
|
4
|
+
const require_story_helpers_1 = require("./helpers/require-story-helpers");
|
|
13
5
|
/**
|
|
14
|
-
*
|
|
6
|
+
* ESLint rule to require @story annotations on functions/methods.
|
|
15
7
|
*
|
|
16
8
|
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
17
9
|
* @req REQ-ANNOTATION-REQUIRED
|
|
18
|
-
* @param {any} node - AST node to check for export ancestry
|
|
19
|
-
* @returns {boolean} true if node is within an export declaration
|
|
20
10
|
*/
|
|
21
|
-
function isExportedNode(node) {
|
|
22
|
-
let p = node.parent;
|
|
23
|
-
while (p) {
|
|
24
|
-
if (p.type === "ExportNamedDeclaration" ||
|
|
25
|
-
p.type === "ExportDefaultDeclaration") {
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
p = p.parent;
|
|
29
|
-
}
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
// Path to the story file for annotations
|
|
33
|
-
const STORY_PATH = "docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md";
|
|
34
|
-
const ANNOTATION = `/** @story ${STORY_PATH} */`;
|
|
35
|
-
/**
|
|
36
|
-
* Check if @story annotation already present in JSDoc or preceding comments
|
|
37
|
-
*
|
|
38
|
-
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
39
|
-
* @req REQ-ANNOTATION-REQUIRED
|
|
40
|
-
* @param {any} sourceCode - ESLint sourceCode object
|
|
41
|
-
* @param {any} node - AST node to inspect for existing annotations
|
|
42
|
-
* @returns {boolean} true if @story annotation already present
|
|
43
|
-
*/
|
|
44
|
-
function hasStoryAnnotation(sourceCode, node) {
|
|
45
|
-
const jsdoc = sourceCode.getJSDocComment(node);
|
|
46
|
-
if (jsdoc?.value.includes("@story")) {
|
|
47
|
-
return true;
|
|
48
|
-
}
|
|
49
|
-
const comments = sourceCode.getCommentsBefore(node) || [];
|
|
50
|
-
return comments.some((c) => c.value.includes("@story"));
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Get the name of the function-like node
|
|
54
|
-
*
|
|
55
|
-
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
56
|
-
* @req REQ-ANNOTATION-REQUIRED
|
|
57
|
-
* @param {any} node - AST node representing a function-like construct
|
|
58
|
-
* @returns {string} the resolved name or "<unknown>"
|
|
59
|
-
*/
|
|
60
|
-
function getNodeName(node) {
|
|
61
|
-
let current = node;
|
|
62
|
-
while (current) {
|
|
63
|
-
if (current.type === "VariableDeclarator" &&
|
|
64
|
-
current.id &&
|
|
65
|
-
typeof current.id.name === "string") {
|
|
66
|
-
return current.id.name;
|
|
67
|
-
}
|
|
68
|
-
if ((current.type === "FunctionDeclaration" ||
|
|
69
|
-
current.type === "TSDeclareFunction") &&
|
|
70
|
-
current.id &&
|
|
71
|
-
typeof current.id.name === "string") {
|
|
72
|
-
return current.id.name;
|
|
73
|
-
}
|
|
74
|
-
if ((current.type === "MethodDefinition" ||
|
|
75
|
-
current.type === "TSMethodSignature") &&
|
|
76
|
-
current.key &&
|
|
77
|
-
typeof current.key.name === "string") {
|
|
78
|
-
return current.key.name;
|
|
79
|
-
}
|
|
80
|
-
current = current.parent;
|
|
81
|
-
}
|
|
82
|
-
return "<unknown>";
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Determine AST node where annotation should be inserted
|
|
86
|
-
*
|
|
87
|
-
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
88
|
-
* @req REQ-ANNOTATION-REQUIRED
|
|
89
|
-
* @param {any} sourceCode - ESLint sourceCode object (unused but kept for parity)
|
|
90
|
-
* @param {any} node - function-like AST node to resolve target for
|
|
91
|
-
* @returns {any} AST node that should receive the annotation
|
|
92
|
-
*/
|
|
93
|
-
function resolveTargetNode(sourceCode, node) {
|
|
94
|
-
if (node.type === "TSMethodSignature") {
|
|
95
|
-
// Interface method signature -> insert on interface
|
|
96
|
-
return node.parent.parent;
|
|
97
|
-
}
|
|
98
|
-
if (node.type === "FunctionExpression" ||
|
|
99
|
-
node.type === "ArrowFunctionExpression") {
|
|
100
|
-
const parent = node.parent;
|
|
101
|
-
if (parent.type === "VariableDeclarator") {
|
|
102
|
-
const varDecl = parent.parent;
|
|
103
|
-
if (varDecl.parent && varDecl.parent.type === "ExportNamedDeclaration") {
|
|
104
|
-
return varDecl.parent;
|
|
105
|
-
}
|
|
106
|
-
return varDecl;
|
|
107
|
-
}
|
|
108
|
-
if (parent.type === "ExportNamedDeclaration") {
|
|
109
|
-
return parent;
|
|
110
|
-
}
|
|
111
|
-
if (parent.type === "ExpressionStatement") {
|
|
112
|
-
return parent;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
return node;
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Report missing @story annotation on function or method
|
|
119
|
-
*
|
|
120
|
-
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
121
|
-
* @req REQ-ANNOTATION-REQUIRED
|
|
122
|
-
* @param {Rule.RuleContext} context - ESLint rule context
|
|
123
|
-
* @param {any} sourceCode - ESLint sourceCode object
|
|
124
|
-
* @param {any} node - function AST node missing annotation
|
|
125
|
-
* @param {any} target - AST node where annotation should be inserted
|
|
126
|
-
*/
|
|
127
|
-
function reportMissing(context, sourceCode, node, target) {
|
|
128
|
-
if (hasStoryAnnotation(sourceCode, node) ||
|
|
129
|
-
hasStoryAnnotation(sourceCode, target)) {
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
let name = getNodeName(node);
|
|
133
|
-
if (node.type === "TSDeclareFunction" && node.id && node.id.name) {
|
|
134
|
-
name = node.id.name;
|
|
135
|
-
}
|
|
136
|
-
context.report({
|
|
137
|
-
node,
|
|
138
|
-
messageId: "missingStory",
|
|
139
|
-
data: { name },
|
|
140
|
-
suggest: [
|
|
141
|
-
{
|
|
142
|
-
desc: `Add JSDoc @story annotation for function '${name}', e.g., ${ANNOTATION}`,
|
|
143
|
-
fix: (fixer) => fixer.insertTextBefore(target, `${ANNOTATION}\n`),
|
|
144
|
-
},
|
|
145
|
-
],
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Report missing @story annotation on class methods
|
|
150
|
-
*
|
|
151
|
-
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
152
|
-
* @req REQ-ANNOTATION-REQUIRED
|
|
153
|
-
* @param {Rule.RuleContext} context - ESLint rule context
|
|
154
|
-
* @param {any} sourceCode - ESLint sourceCode object
|
|
155
|
-
* @param {any} node - MethodDefinition AST node
|
|
156
|
-
*/
|
|
157
|
-
function reportMethod(context, sourceCode, node) {
|
|
158
|
-
if (hasStoryAnnotation(sourceCode, node)) {
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
context.report({
|
|
162
|
-
node,
|
|
163
|
-
messageId: "missingStory",
|
|
164
|
-
data: { name: getNodeName(node) },
|
|
165
|
-
suggest: [
|
|
166
|
-
{
|
|
167
|
-
desc: `Add JSDoc @story annotation for function '${getNodeName(node)}', e.g., ${ANNOTATION}`,
|
|
168
|
-
fix: (fixer) => fixer.insertTextBefore(node, `${ANNOTATION}\n `),
|
|
169
|
-
},
|
|
170
|
-
],
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Check if this node is within scope and matches exportPriority
|
|
175
|
-
*
|
|
176
|
-
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
177
|
-
* @req REQ-ANNOTATION-REQUIRED
|
|
178
|
-
* @param {any} node - AST node to evaluate
|
|
179
|
-
* @param {string[]} scope - allowed node types
|
|
180
|
-
* @param {string} exportPriority - 'all' | 'exported' | 'non-exported'
|
|
181
|
-
* @returns {boolean} whether node should be processed
|
|
182
|
-
*/
|
|
183
|
-
function shouldProcessNode(node, scope, exportPriority) {
|
|
184
|
-
if (!scope.includes(node.type)) {
|
|
185
|
-
return false;
|
|
186
|
-
}
|
|
187
|
-
const exported = isExportedNode(node);
|
|
188
|
-
if (exportPriority === "exported" && !exported) {
|
|
189
|
-
return false;
|
|
190
|
-
}
|
|
191
|
-
if (exportPriority === "non-exported" && exported) {
|
|
192
|
-
return false;
|
|
193
|
-
}
|
|
194
|
-
return true;
|
|
195
|
-
}
|
|
196
11
|
const rule = {
|
|
197
12
|
meta: {
|
|
198
13
|
type: "problem",
|
|
@@ -210,99 +25,43 @@ const rule = {
|
|
|
210
25
|
properties: {
|
|
211
26
|
scope: {
|
|
212
27
|
type: "array",
|
|
213
|
-
items: { type: "string", enum: DEFAULT_SCOPE },
|
|
28
|
+
items: { type: "string", enum: require_story_helpers_1.DEFAULT_SCOPE },
|
|
214
29
|
uniqueItems: true,
|
|
215
30
|
},
|
|
216
|
-
exportPriority: { type: "string", enum: EXPORT_PRIORITY_VALUES },
|
|
31
|
+
exportPriority: { type: "string", enum: require_story_helpers_1.EXPORT_PRIORITY_VALUES },
|
|
217
32
|
},
|
|
218
33
|
additionalProperties: false,
|
|
219
34
|
},
|
|
220
35
|
],
|
|
221
36
|
},
|
|
222
37
|
/**
|
|
223
|
-
* Create the rule visitor functions
|
|
38
|
+
* Create the rule visitor functions.
|
|
39
|
+
*
|
|
224
40
|
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
225
|
-
* @req REQ-CREATE-HOOK
|
|
41
|
+
* @req REQ-CREATE-HOOK
|
|
226
42
|
*/
|
|
227
43
|
create(context) {
|
|
228
44
|
const sourceCode = context.getSourceCode();
|
|
229
|
-
const opts = context.options[0] ||
|
|
230
|
-
|
|
231
|
-
const scope = opts.scope || DEFAULT_SCOPE;
|
|
45
|
+
const opts = (context.options && context.options[0]) || {};
|
|
46
|
+
const scope = opts.scope || require_story_helpers_1.DEFAULT_SCOPE;
|
|
232
47
|
const exportPriority = opts.exportPriority || "all";
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
*/
|
|
251
|
-
FunctionExpression(node) {
|
|
252
|
-
if (!shouldProcessNode(node, scope, exportPriority))
|
|
253
|
-
return;
|
|
254
|
-
if (node.parent && node.parent.type === "MethodDefinition")
|
|
255
|
-
return;
|
|
256
|
-
const target = resolveTargetNode(sourceCode, node);
|
|
257
|
-
reportMissing(context, sourceCode, node, target);
|
|
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
|
-
*/
|
|
265
|
-
ArrowFunctionExpression(node) {
|
|
266
|
-
if (!shouldProcessNode(node, scope, exportPriority))
|
|
267
|
-
return;
|
|
268
|
-
const target = resolveTargetNode(sourceCode, node);
|
|
269
|
-
reportMissing(context, sourceCode, node, target);
|
|
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
|
-
*/
|
|
277
|
-
TSDeclareFunction(node) {
|
|
278
|
-
if (!shouldProcessNode(node, scope, exportPriority))
|
|
279
|
-
return;
|
|
280
|
-
reportMissing(context, sourceCode, node, node);
|
|
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
|
-
*/
|
|
288
|
-
TSMethodSignature(node) {
|
|
289
|
-
if (!shouldProcessNode(node, scope, exportPriority))
|
|
290
|
-
return;
|
|
291
|
-
const target = resolveTargetNode(sourceCode, node);
|
|
292
|
-
reportMissing(context, sourceCode, node, target);
|
|
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
|
-
*/
|
|
300
|
-
MethodDefinition(node) {
|
|
301
|
-
if (!shouldProcessNode(node, scope, exportPriority))
|
|
302
|
-
return;
|
|
303
|
-
reportMethod(context, sourceCode, node);
|
|
304
|
-
},
|
|
305
|
-
};
|
|
48
|
+
/**
|
|
49
|
+
* Debug log at the start of create to help diagnose rule activation in tests.
|
|
50
|
+
*
|
|
51
|
+
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
52
|
+
* @req REQ-DEBUG-LOG
|
|
53
|
+
*/
|
|
54
|
+
console.debug("require-story-annotation:create", typeof context.getFilename === "function"
|
|
55
|
+
? context.getFilename()
|
|
56
|
+
: "<unknown>");
|
|
57
|
+
// Local closure that binds configured scope and export priority to the helper.
|
|
58
|
+
const should = (node) => (0, require_story_helpers_1.shouldProcessNode)(node, scope, exportPriority);
|
|
59
|
+
// Delegate visitor construction to helper to keep this file concise.
|
|
60
|
+
return (0, require_story_visitors_1.buildVisitors)(context, sourceCode, {
|
|
61
|
+
shouldProcessNode: should,
|
|
62
|
+
scope,
|
|
63
|
+
exportPriority,
|
|
64
|
+
});
|
|
306
65
|
},
|
|
307
66
|
};
|
|
308
67
|
exports.default = rule;
|
|
@@ -31,28 +31,47 @@ exports.DEFAULT_BRANCH_TYPES = [
|
|
|
31
31
|
*/
|
|
32
32
|
function validateBranchTypes(context) {
|
|
33
33
|
const options = context.options[0] || {};
|
|
34
|
+
/**
|
|
35
|
+
* Conditional branch checking whether branchTypes option was provided.
|
|
36
|
+
* @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
37
|
+
* @req REQ-TRACEABILITY-CONDITIONAL - Trace configuration branch existence check
|
|
38
|
+
*/
|
|
34
39
|
if (Array.isArray(options.branchTypes)) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Predicate to determine whether a provided branch type is invalid.
|
|
42
|
+
* @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
43
|
+
* @req REQ-TRACEABILITY-FILTER-CALLBACK - Trace filter callback for invalid branch type detection
|
|
44
|
+
*/
|
|
45
|
+
function isInvalidType(t) {
|
|
46
|
+
return !exports.DEFAULT_BRANCH_TYPES.includes(t);
|
|
47
|
+
}
|
|
48
|
+
const invalidTypes = options.branchTypes.filter(isInvalidType);
|
|
49
|
+
/**
|
|
50
|
+
* Conditional branch checking whether any invalid types were found.
|
|
51
|
+
* @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
52
|
+
* @req REQ-TRACEABILITY-INVALID-DETECTION - Trace handling when invalid types are detected
|
|
53
|
+
*/
|
|
38
54
|
if (invalidTypes.length > 0) {
|
|
39
55
|
/**
|
|
40
56
|
* Program listener produced when configuration is invalid.
|
|
41
57
|
* @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
42
58
|
* @req REQ-TRACEABILITY-PROGRAM-LISTENER - Trace Program listener reporting invalid config values
|
|
43
59
|
*/
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
60
|
+
function ProgramHandler(node) {
|
|
61
|
+
/**
|
|
62
|
+
* Report a single invalid type for the given Program node.
|
|
63
|
+
* @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
64
|
+
* @req REQ-TRACEABILITY-FOR-EACH-CALLBACK - Trace reporting for each invalid type
|
|
65
|
+
*/
|
|
66
|
+
function reportInvalidType(t) {
|
|
67
|
+
context.report({
|
|
68
|
+
node,
|
|
69
|
+
message: `Value "${t}" should be equal to one of the allowed values: ${exports.DEFAULT_BRANCH_TYPES.join(", ")}`,
|
|
53
70
|
});
|
|
54
|
-
}
|
|
55
|
-
|
|
71
|
+
}
|
|
72
|
+
invalidTypes.forEach(reportInvalidType);
|
|
73
|
+
}
|
|
74
|
+
return { Program: ProgramHandler };
|
|
56
75
|
}
|
|
57
76
|
}
|
|
58
77
|
return Array.isArray(options.branchTypes)
|
|
@@ -65,6 +84,11 @@ function validateBranchTypes(context) {
|
|
|
65
84
|
* @req REQ-COMMENT-ASSOCIATION - Associate inline comments with their corresponding code branches
|
|
66
85
|
*/
|
|
67
86
|
function gatherBranchCommentText(sourceCode, node) {
|
|
87
|
+
/**
|
|
88
|
+
* Conditional branch for SwitchCase nodes that may include inline comments.
|
|
89
|
+
* @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
90
|
+
* @req REQ-TRACEABILITY-SWITCHCASE-COMMENTS - Trace collection of preceding comments for SwitchCase
|
|
91
|
+
*/
|
|
68
92
|
if (node.type === "SwitchCase") {
|
|
69
93
|
const lines = sourceCode.lines;
|
|
70
94
|
const startLine = node.loc.start.line;
|
|
@@ -79,9 +103,15 @@ function gatherBranchCommentText(sourceCode, node) {
|
|
|
79
103
|
return comments.join(" ");
|
|
80
104
|
}
|
|
81
105
|
const comments = sourceCode.getCommentsBefore(node) || [];
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
106
|
+
/**
|
|
107
|
+
* Mapper to extract the text value from a comment node.
|
|
108
|
+
* @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
109
|
+
* @req REQ-TRACEABILITY-MAP-CALLBACK - Trace mapping of comment nodes to their text values
|
|
110
|
+
*/
|
|
111
|
+
function commentToValue(c) {
|
|
112
|
+
return c.value;
|
|
113
|
+
}
|
|
114
|
+
return comments.map(commentToValue).join(" ");
|
|
85
115
|
}
|
|
86
116
|
/**
|
|
87
117
|
* Report missing @story annotation on a branch node.
|
|
@@ -90,15 +120,25 @@ function gatherBranchCommentText(sourceCode, node) {
|
|
|
90
120
|
*/
|
|
91
121
|
function reportMissingStory(context, node, options) {
|
|
92
122
|
const { indent, insertPos, storyFixCountRef } = options;
|
|
123
|
+
/**
|
|
124
|
+
* Conditional branch deciding whether to offer an auto-fix for the missing story.
|
|
125
|
+
* @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
126
|
+
* @req REQ-TRACEABILITY-FIX-DECISION - Trace decision to provide fixer for missing @story
|
|
127
|
+
*/
|
|
93
128
|
if (storyFixCountRef.count === 0) {
|
|
129
|
+
/**
|
|
130
|
+
* Fixer that inserts a default @story annotation above the branch.
|
|
131
|
+
* @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
132
|
+
* @req REQ-TRACEABILITY-FIX-ARROW - Trace fixer function used to insert missing @story
|
|
133
|
+
*/
|
|
134
|
+
function insertStoryFixer(fixer) {
|
|
135
|
+
return fixer.insertTextBeforeRange([insertPos, insertPos], `${indent}// @story <story-file>.story.md\n`);
|
|
136
|
+
}
|
|
94
137
|
context.report({
|
|
95
138
|
node,
|
|
96
139
|
messageId: "missingAnnotation",
|
|
97
140
|
data: { missing: "@story" },
|
|
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`),
|
|
141
|
+
fix: insertStoryFixer,
|
|
102
142
|
});
|
|
103
143
|
storyFixCountRef.count++;
|
|
104
144
|
}
|
|
@@ -117,15 +157,25 @@ function reportMissingStory(context, node, options) {
|
|
|
117
157
|
*/
|
|
118
158
|
function reportMissingReq(context, node, options) {
|
|
119
159
|
const { indent, insertPos, missingStory } = options;
|
|
160
|
+
/**
|
|
161
|
+
* Conditional branch deciding whether to offer an auto-fix for the missing req.
|
|
162
|
+
* @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
163
|
+
* @req REQ-TRACEABILITY-FIX-DECISION - Trace decision to provide fixer for missing @req
|
|
164
|
+
*/
|
|
120
165
|
if (!missingStory) {
|
|
166
|
+
/**
|
|
167
|
+
* Fixer that inserts a default @req annotation above the branch.
|
|
168
|
+
* @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
169
|
+
* @req REQ-TRACEABILITY-FIX-ARROW - Trace fixer function used to insert missing @req
|
|
170
|
+
*/
|
|
171
|
+
function insertReqFixer(fixer) {
|
|
172
|
+
return fixer.insertTextBeforeRange([insertPos, insertPos], `${indent}// @req <REQ-ID>\n`);
|
|
173
|
+
}
|
|
121
174
|
context.report({
|
|
122
175
|
node,
|
|
123
176
|
messageId: "missingAnnotation",
|
|
124
177
|
data: { missing: "@req" },
|
|
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`),
|
|
178
|
+
fix: insertReqFixer,
|
|
129
179
|
});
|
|
130
180
|
}
|
|
131
181
|
else {
|
|
@@ -163,10 +213,20 @@ function reportMissingAnnotations(context, node, storyFixCountRef) {
|
|
|
163
213
|
args: [context, node, { indent, insertPos, missingStory }],
|
|
164
214
|
},
|
|
165
215
|
];
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
216
|
+
/**
|
|
217
|
+
* Process a single action from the actions array.
|
|
218
|
+
* @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
219
|
+
* @req REQ-TRACEABILITY-ACTIONS-FOREACH - Trace processing of actions array to report missing annotations
|
|
220
|
+
*/
|
|
221
|
+
function processAction(item) {
|
|
222
|
+
/**
|
|
223
|
+
* Callback invoked for each action to decide and execute reporting.
|
|
224
|
+
* @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
225
|
+
* @req REQ-TRACEABILITY-FOR-EACH-CALLBACK - Trace callback handling for each action item
|
|
226
|
+
*/
|
|
227
|
+
if (item.missing) {
|
|
228
|
+
item.fn(...item.args);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
actions.forEach(processAction);
|
|
172
232
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|