eslint-plugin-traceability 1.13.1 → 1.14.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 +2 -2
- package/lib/src/rules/helpers/require-story-core.js +1 -0
- package/lib/src/rules/helpers/require-story-helpers.d.ts +4 -0
- package/lib/src/rules/helpers/require-story-helpers.js +92 -3
- package/lib/src/rules/helpers/require-test-traceability-helpers.js +24 -0
- package/lib/src/rules/helpers/valid-annotation-options.d.ts +32 -1
- package/lib/src/rules/helpers/valid-annotation-options.js +144 -1
- package/lib/src/rules/helpers/valid-implements-utils.js +13 -5
- package/lib/src/rules/no-redundant-annotation.js +12 -0
- package/lib/src/rules/prefer-implements-annotation.js +176 -5
- package/lib/src/rules/require-branch-annotation.js +73 -1
- package/lib/src/rules/require-test-traceability.js +8 -0
- package/lib/src/rules/valid-req-reference.js +4 -0
- package/lib/src/rules/valid-story-reference.d.ts +9 -0
- package/lib/src/rules/valid-story-reference.js +9 -0
- package/lib/src/utils/branch-annotation-helpers.d.ts +12 -10
- package/lib/src/utils/branch-annotation-helpers.js +31 -140
- package/lib/src/utils/branch-annotation-loop-helpers.d.ts +9 -0
- package/lib/src/utils/branch-annotation-loop-helpers.js +36 -0
- package/lib/src/utils/branch-annotation-report-helpers.d.ts +11 -0
- package/lib/src/utils/branch-annotation-report-helpers.js +111 -0
- package/lib/tests/integration/dogfooding-validation.test.js +5 -2
- package/lib/tests/rules/prefer-implements-annotation.test.js +23 -0
- package/lib/tests/rules/require-branch-annotation.test.js +88 -19
- package/lib/tests/rules/require-story-annotation.test.js +56 -8
- package/lib/tests/utils/temp-dir-helpers.d.ts +6 -1
- package/lib/tests/utils/temp-dir-helpers.js +2 -1
- package/package.json +1 -1
- package/user-docs/api-reference.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
## [1.
|
|
1
|
+
## [1.14.1](https://github.com/voder-ai/eslint-plugin-traceability/compare/v1.14.0...v1.14.1) (2025-12-08)
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
### Bug Fixes
|
|
5
5
|
|
|
6
|
-
*
|
|
6
|
+
* implement branch and function behaviors for branch annotations story ([00f9655](https://github.com/voder-ai/eslint-plugin-traceability/commit/00f9655c8a284278d69c7d09d106a340ada57827))
|
|
7
7
|
|
|
8
8
|
# Changelog
|
|
9
9
|
|
|
@@ -18,6 +18,7 @@ interface ReportOptions {
|
|
|
18
18
|
annotationTemplateOverride?: string;
|
|
19
19
|
autoFixToggle?: boolean;
|
|
20
20
|
}
|
|
21
|
+
/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
|
|
21
22
|
declare function getAnnotationTemplate(override?: string): string;
|
|
22
23
|
declare function shouldApplyAutoFix(autoFix: boolean | undefined): boolean;
|
|
23
24
|
/**
|
|
@@ -63,12 +64,15 @@ declare function resolveTargetNode(sourceCode: any, node: any): any;
|
|
|
63
64
|
* @req REQ-ANNOTATION-REQUIRED - Walk node and parents to find Identifier/Key name
|
|
64
65
|
*/
|
|
65
66
|
declare function extractName(node: any): string;
|
|
67
|
+
/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
|
|
66
68
|
declare function shouldProcessNode(node: any, scope: string[], exportPriority?: string): boolean;
|
|
69
|
+
/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
|
|
67
70
|
declare function reportMissing(context: Rule.RuleContext, sourceCode: any, config: {
|
|
68
71
|
node: any;
|
|
69
72
|
target?: any;
|
|
70
73
|
options?: ReportOptions;
|
|
71
74
|
}): void;
|
|
75
|
+
/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
|
|
72
76
|
declare function reportMethod(context: Rule.RuleContext, sourceCode: any, config: {
|
|
73
77
|
node: any;
|
|
74
78
|
target?: any;
|
|
@@ -23,6 +23,7 @@ const require_story_core_1 = require("./require-story-core");
|
|
|
23
23
|
Object.defineProperty(exports, "DEFAULT_SCOPE", { enumerable: true, get: function () { return require_story_core_1.DEFAULT_SCOPE; } });
|
|
24
24
|
Object.defineProperty(exports, "EXPORT_PRIORITY_VALUES", { enumerable: true, get: function () { return require_story_core_1.EXPORT_PRIORITY_VALUES; } });
|
|
25
25
|
Object.defineProperty(exports, "STORY_PATH", { enumerable: true, get: function () { return require_story_core_1.STORY_PATH; } });
|
|
26
|
+
/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
|
|
26
27
|
function getAnnotationTemplate(override) {
|
|
27
28
|
if (typeof override === "string" && override.trim().length > 0) {
|
|
28
29
|
return override.trim();
|
|
@@ -39,11 +40,81 @@ function shouldApplyAutoFix(autoFix) {
|
|
|
39
40
|
* Build the effective annotation template and autofix toggle
|
|
40
41
|
* from the provided report options.
|
|
41
42
|
*/
|
|
43
|
+
/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
|
|
42
44
|
function buildTemplateConfig(options) {
|
|
43
45
|
const effectiveTemplate = getAnnotationTemplate(options?.annotationTemplateOverride);
|
|
44
46
|
const allowFix = shouldApplyAutoFix(options?.autoFixToggle);
|
|
45
47
|
return { effectiveTemplate, allowFix };
|
|
46
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Determine whether a node represents an anonymous arrow function expression
|
|
51
|
+
* where the parent variable declarator has no explicit Identifier name.
|
|
52
|
+
*
|
|
53
|
+
* @supports docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md REQ-ARROW-FUNCTION-EXCLUDED
|
|
54
|
+
*/
|
|
55
|
+
function isAnonymousArrowFunction(node) {
|
|
56
|
+
return !!node && node.type === "ArrowFunctionExpression";
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Determine whether a function-like node is nested within another function.
|
|
60
|
+
*
|
|
61
|
+
* @supports docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md REQ-NESTED-FUNCTION-INHERITANCE
|
|
62
|
+
*/
|
|
63
|
+
function isNestedFunction(node) {
|
|
64
|
+
let current = node?.parent;
|
|
65
|
+
while (current) {
|
|
66
|
+
if (current.type === "FunctionDeclaration" ||
|
|
67
|
+
current.type === "FunctionExpression" ||
|
|
68
|
+
current.type === "ArrowFunctionExpression" ||
|
|
69
|
+
current.type === "MethodDefinition" ||
|
|
70
|
+
current.type === "TSDeclareFunction" ||
|
|
71
|
+
current.type === "TSMethodSignature") {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
current = current.parent;
|
|
75
|
+
}
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Determine whether a function-like node is effectively anonymous for the
|
|
80
|
+
* purposes of nested-function inheritance. Named functions must always carry
|
|
81
|
+
* their own annotations, while anonymous nested functions may inherit.
|
|
82
|
+
*
|
|
83
|
+
* @supports docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md REQ-NESTED-FUNCTION-INHERITANCE
|
|
84
|
+
*/
|
|
85
|
+
function isEffectivelyAnonymousFunction(node) {
|
|
86
|
+
const name = getContainerKeyOrIdName(node) ?? getDirectIdentifierName(node);
|
|
87
|
+
if (typeof name === "string" && name.length > 0 && name !== "(anonymous)") {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Determine whether a function node is required to carry its own annotation
|
|
94
|
+
* according to Story 004.0-DEV-BRANCH-ANNOTATIONS rules.
|
|
95
|
+
*
|
|
96
|
+
* - Anonymous arrow functions used as callbacks are excluded from
|
|
97
|
+
* function-level annotation requirements.
|
|
98
|
+
* - Named arrow functions must be annotated.
|
|
99
|
+
* - Nested anonymous functions may inherit their parent function's
|
|
100
|
+
* annotation and therefore are not required to be annotated directly.
|
|
101
|
+
* - Named nested functions must always carry their own explicit annotations.
|
|
102
|
+
*
|
|
103
|
+
* @supports docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md REQ-ARROW-FUNCTION-EXCLUDED REQ-NESTED-FUNCTION-INHERITANCE
|
|
104
|
+
*/
|
|
105
|
+
function requiresOwnFunctionAnnotation(node) {
|
|
106
|
+
// Anonymous arrow functions used as callbacks are excluded from function-level
|
|
107
|
+
// requirements when they are nested inside another function or method.
|
|
108
|
+
if (isAnonymousArrowFunction(node) &&
|
|
109
|
+
isNestedFunction(node) &&
|
|
110
|
+
isEffectivelyAnonymousFunction(node)) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
if (isNestedFunction(node) && isEffectivelyAnonymousFunction(node)) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
47
118
|
/**
|
|
48
119
|
* Determine if a node is in an export declaration
|
|
49
120
|
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
@@ -105,6 +176,7 @@ function leadingCommentsHasStory(node) {
|
|
|
105
176
|
*/
|
|
106
177
|
function hasStoryAnnotation(sourceCode, node) {
|
|
107
178
|
try {
|
|
179
|
+
// Direct, node-local checks always apply first.
|
|
108
180
|
if (jsdocHasStory(sourceCode, node)) {
|
|
109
181
|
return true;
|
|
110
182
|
}
|
|
@@ -114,13 +186,20 @@ function hasStoryAnnotation(sourceCode, node) {
|
|
|
114
186
|
if (leadingCommentsHasStory(node)) {
|
|
115
187
|
return true;
|
|
116
188
|
}
|
|
117
|
-
if ((0, require_story_io_1.linesBeforeHasStory)(sourceCode, node)) {
|
|
189
|
+
if (!isNestedFunction(node) && (0, require_story_io_1.linesBeforeHasStory)(sourceCode, node)) {
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
const canInherit = isNestedFunction(node) && isEffectivelyAnonymousFunction(node);
|
|
193
|
+
// Only nodes that are allowed to inherit annotations (e.g., nested anonymous
|
|
194
|
+
// callbacks) may treat parent-chain comments or broad fallback text as
|
|
195
|
+
// satisfying the annotation requirement.
|
|
196
|
+
if (canInherit && (0, require_story_io_1.parentChainHasStory)(sourceCode, node)) {
|
|
118
197
|
return true;
|
|
119
198
|
}
|
|
120
|
-
if ((0, require_story_io_1.
|
|
199
|
+
if (canInherit && (0, require_story_io_1.fallbackTextBeforeHasStory)(sourceCode, node)) {
|
|
121
200
|
return true;
|
|
122
201
|
}
|
|
123
|
-
if (
|
|
202
|
+
if (canInherit) {
|
|
124
203
|
return true;
|
|
125
204
|
}
|
|
126
205
|
}
|
|
@@ -226,7 +305,15 @@ function extractName(node) {
|
|
|
226
305
|
}
|
|
227
306
|
return "(anonymous)";
|
|
228
307
|
}
|
|
308
|
+
/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
|
|
229
309
|
function shouldProcessNode(node, scope, exportPriority = "all") {
|
|
310
|
+
if (node &&
|
|
311
|
+
(node.type === "FunctionDeclaration" ||
|
|
312
|
+
node.type === "FunctionExpression" ||
|
|
313
|
+
node.type === "ArrowFunctionExpression") &&
|
|
314
|
+
!requiresOwnFunctionAnnotation(node)) {
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
230
317
|
if (!scope.includes(node.type)) {
|
|
231
318
|
return false;
|
|
232
319
|
}
|
|
@@ -271,6 +358,7 @@ function getNameNodeForReport(node) {
|
|
|
271
358
|
function resolveAnnotationTargetNode(sourceCode, node, passedTarget) {
|
|
272
359
|
return passedTarget ?? resolveTargetNode(sourceCode, node);
|
|
273
360
|
}
|
|
361
|
+
/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
|
|
274
362
|
function reportMissing(context, sourceCode, config) {
|
|
275
363
|
(0, require_story_core_1.coreReportMissing)({
|
|
276
364
|
hasStoryAnnotation,
|
|
@@ -285,6 +373,7 @@ function reportMissing(context, sourceCode, config) {
|
|
|
285
373
|
createMethodFix: require_story_core_1.createMethodFix,
|
|
286
374
|
}, context, sourceCode, config);
|
|
287
375
|
}
|
|
376
|
+
/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
|
|
288
377
|
function reportMethod(context, sourceCode, config) {
|
|
289
378
|
(0, require_story_core_1.coreReportMethod)({
|
|
290
379
|
hasStoryAnnotation,
|
|
@@ -94,6 +94,11 @@ function ensureFileSupportsAnnotation(context, sourceCode, autoFixOptions) {
|
|
|
94
94
|
function isTestCallName(name) {
|
|
95
95
|
return ["describe", "it", "test", "context"].includes(name);
|
|
96
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Extract the test framework call name from a CallExpression callee.
|
|
99
|
+
*
|
|
100
|
+
* @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-FRAMEWORK-COMPAT
|
|
101
|
+
*/
|
|
97
102
|
function getCalleeName(node) {
|
|
98
103
|
if (node.callee.type === "Identifier") {
|
|
99
104
|
return node.callee.name;
|
|
@@ -104,6 +109,11 @@ function getCalleeName(node) {
|
|
|
104
109
|
}
|
|
105
110
|
return null;
|
|
106
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Extract the first string literal argument from a CallExpression, if present.
|
|
114
|
+
*
|
|
115
|
+
* @supports docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md REQ-TEST-DESCRIBE-STORY REQ-TEST-IT-REQ-PREFIX
|
|
116
|
+
*/
|
|
107
117
|
function getFirstArgumentLiteral(node) {
|
|
108
118
|
const arg = node.arguments && node.arguments[0];
|
|
109
119
|
if (!arg)
|
|
@@ -189,6 +199,13 @@ function createUpdatedStringLiteralRaw(originalNode, newValue, sourceCode) {
|
|
|
189
199
|
// Fallback: let JSON.stringify choose a safe representation.
|
|
190
200
|
return JSON.stringify(newValue);
|
|
191
201
|
}
|
|
202
|
+
/**
|
|
203
|
+
* Validate describe() calls to ensure they include a story reference
|
|
204
|
+
* matching the configured describeRegex when required.
|
|
205
|
+
*
|
|
206
|
+
* @story docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md
|
|
207
|
+
* @req REQ-TEST-DESCRIBE-STORY
|
|
208
|
+
*/
|
|
192
209
|
function handleDescribeCall(context, node, description, options) {
|
|
193
210
|
const { describeRegex, requireDescribeStory } = options;
|
|
194
211
|
if (!requireDescribeStory)
|
|
@@ -200,6 +217,13 @@ function handleDescribeCall(context, node, description, options) {
|
|
|
200
217
|
});
|
|
201
218
|
}
|
|
202
219
|
}
|
|
220
|
+
/**
|
|
221
|
+
* Validate it() and test() calls to ensure their descriptions start with a
|
|
222
|
+
* [REQ-XXX] prefix, optionally normalizing malformed prefixes when enabled.
|
|
223
|
+
*
|
|
224
|
+
* @story docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md
|
|
225
|
+
* @req REQ-TEST-IT-REQ-PREFIX
|
|
226
|
+
*/
|
|
203
227
|
function handleItOrTestCall(context, node, description, options) {
|
|
204
228
|
const { sourceCode, requireTestReqPrefix, autoFixTestPrefixFormat } = options;
|
|
205
229
|
if (!requireTestReqPrefix)
|
|
@@ -69,16 +69,47 @@ export interface ResolvedAnnotationOptions {
|
|
|
69
69
|
reqExample: string;
|
|
70
70
|
autoFix: boolean;
|
|
71
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Get the default requirement ID example string used in error messages.
|
|
74
|
+
*
|
|
75
|
+
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
76
|
+
* @req REQ-PATTERN-CONFIG - Provide a default requirement ID example value
|
|
77
|
+
*/
|
|
72
78
|
export declare function getDefaultReqExample(): string;
|
|
79
|
+
/**
|
|
80
|
+
* Expose the most recently resolved options so other helpers can reuse
|
|
81
|
+
* the same defaults without re-resolving configuration.
|
|
82
|
+
*
|
|
83
|
+
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
84
|
+
* @req REQ-PATTERN-CONFIG - Share resolved default patterns across helpers
|
|
85
|
+
* @req REQ-BACKWARD-COMPAT - Maintain a stable default configuration
|
|
86
|
+
*/
|
|
73
87
|
export declare function getResolvedDefaults(): ResolvedAnnotationOptions;
|
|
88
|
+
/**
|
|
89
|
+
* Retrieve an array of configuration error messages collected during
|
|
90
|
+
* option resolution, typically regex validation failures.
|
|
91
|
+
*
|
|
92
|
+
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
93
|
+
* @req REQ-PATTERN-CONFIG - Surface configuration problems to callers
|
|
94
|
+
*/
|
|
74
95
|
export declare function getOptionErrors(): string[];
|
|
75
96
|
/**
|
|
76
97
|
* Resolve user options into concrete, validated configuration.
|
|
77
98
|
* Falls back to existing defaults when options are not provided or invalid.
|
|
99
|
+
*
|
|
100
|
+
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
101
|
+
* @req REQ-PATTERN-CONFIG - Drive rule behavior from configurable patterns
|
|
102
|
+
* @req REQ-REGEX-VALIDATION - Validate all configured regex patterns
|
|
103
|
+
* @req REQ-BACKWARD-COMPAT - Maintain behavior when no custom options set
|
|
78
104
|
*/
|
|
79
105
|
export declare function resolveOptions(rawOptions: unknown[]): ResolvedAnnotationOptions;
|
|
80
106
|
/**
|
|
81
|
-
* Build the JSON
|
|
107
|
+
* Build the JSON Schema definition that validates rule configuration
|
|
108
|
+
* passed to ESLint, ensuring option shapes and types are correct.
|
|
109
|
+
*
|
|
110
|
+
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
111
|
+
* @req REQ-SCHEMA-VALIDATION - Define a JSON Schema for rule options
|
|
112
|
+
* @req REQ-PATTERN-CONFIG - Describe configurable pattern and example fields
|
|
82
113
|
*/
|
|
83
114
|
export declare function getRuleSchema(): {
|
|
84
115
|
type: string;
|
|
@@ -5,15 +5,39 @@ exports.getResolvedDefaults = getResolvedDefaults;
|
|
|
5
5
|
exports.getOptionErrors = getOptionErrors;
|
|
6
6
|
exports.resolveOptions = resolveOptions;
|
|
7
7
|
exports.getRuleSchema = getRuleSchema;
|
|
8
|
+
/**
|
|
9
|
+
* Get the default regular expression used to validate story paths.
|
|
10
|
+
*
|
|
11
|
+
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
12
|
+
* @req REQ-PATTERN-CONFIG - Provide a default story path pattern
|
|
13
|
+
*/
|
|
8
14
|
function getDefaultStoryPattern() {
|
|
9
15
|
return /^docs\/stories\/[0-9]+\.[0-9]+-DEV-[\w-]+\.story\.md$/;
|
|
10
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Get the default story example string used in error messages.
|
|
19
|
+
*
|
|
20
|
+
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
21
|
+
* @req REQ-PATTERN-CONFIG - Provide a default story example value
|
|
22
|
+
*/
|
|
11
23
|
function getDefaultStoryExample() {
|
|
12
24
|
return "docs/stories/005.0-DEV-EXAMPLE.story.md";
|
|
13
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Get the default regular expression used to validate requirement IDs.
|
|
28
|
+
*
|
|
29
|
+
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
30
|
+
* @req REQ-PATTERN-CONFIG - Provide a default requirement ID pattern
|
|
31
|
+
*/
|
|
14
32
|
function getDefaultReqPattern() {
|
|
15
33
|
return /^REQ-[A-Z0-9-]+$/;
|
|
16
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Get the default requirement ID example string used in error messages.
|
|
37
|
+
*
|
|
38
|
+
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
39
|
+
* @req REQ-PATTERN-CONFIG - Provide a default requirement ID example value
|
|
40
|
+
*/
|
|
17
41
|
function getDefaultReqExample() {
|
|
18
42
|
return "REQ-EXAMPLE";
|
|
19
43
|
}
|
|
@@ -32,15 +56,33 @@ let resolvedDefaults = {
|
|
|
32
56
|
* Collected configuration errors encountered while resolving options.
|
|
33
57
|
*/
|
|
34
58
|
let optionErrors = [];
|
|
59
|
+
/**
|
|
60
|
+
* Expose the most recently resolved options so other helpers can reuse
|
|
61
|
+
* the same defaults without re-resolving configuration.
|
|
62
|
+
*
|
|
63
|
+
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
64
|
+
* @req REQ-PATTERN-CONFIG - Share resolved default patterns across helpers
|
|
65
|
+
* @req REQ-BACKWARD-COMPAT - Maintain a stable default configuration
|
|
66
|
+
*/
|
|
35
67
|
function getResolvedDefaults() {
|
|
36
68
|
return resolvedDefaults;
|
|
37
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Retrieve an array of configuration error messages collected during
|
|
72
|
+
* option resolution, typically regex validation failures.
|
|
73
|
+
*
|
|
74
|
+
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
75
|
+
* @req REQ-PATTERN-CONFIG - Surface configuration problems to callers
|
|
76
|
+
*/
|
|
38
77
|
function getOptionErrors() {
|
|
39
78
|
return optionErrors;
|
|
40
79
|
}
|
|
41
80
|
/**
|
|
42
81
|
* Build a stable, engine-independent configuration error message
|
|
43
82
|
* for invalid regex options.
|
|
83
|
+
*
|
|
84
|
+
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
85
|
+
* @req REQ-PATTERN-CONFIG - Provide consistent regex validation diagnostics
|
|
44
86
|
*/
|
|
45
87
|
function buildInvalidRegexError(field, pattern) {
|
|
46
88
|
return `Invalid regular expression for option "${field}": "${pattern}"`;
|
|
@@ -105,13 +147,38 @@ function resolveExample(nestedExample, flatExample, defaultExample) {
|
|
|
105
147
|
}
|
|
106
148
|
return defaultExample;
|
|
107
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* Extract and normalize user-provided options from the raw ESLint
|
|
152
|
+
* options array into an AnnotationRuleOptions object.
|
|
153
|
+
*
|
|
154
|
+
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
155
|
+
* @req REQ-PATTERN-CONFIG - Accept structured configuration for patterns
|
|
156
|
+
* @req REQ-BACKWARD-COMPAT - Tolerate missing or malformed options
|
|
157
|
+
*/
|
|
108
158
|
function getUserOptions(rawOptions) {
|
|
109
159
|
return normalizeUserOptions(rawOptions);
|
|
110
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* Resolve the auto-fix flag, defaulting to true when the option
|
|
163
|
+
* is not explicitly provided by the user.
|
|
164
|
+
*
|
|
165
|
+
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
166
|
+
* @req REQ-PATTERN-CONFIG - Support configuration of fix behavior
|
|
167
|
+
* @req REQ-BACKWARD-COMPAT - Preserve default auto-fix behavior
|
|
168
|
+
*/
|
|
111
169
|
function resolveAutoFixFlag(user) {
|
|
112
170
|
const autoFixFlag = user?.autoFix;
|
|
113
171
|
return typeof autoFixFlag === "boolean" ? autoFixFlag : true;
|
|
114
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Resolve the story path pattern from nested or flat configuration
|
|
175
|
+
* fields, validating and falling back to the default as needed.
|
|
176
|
+
*
|
|
177
|
+
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
178
|
+
* @req REQ-PATTERN-CONFIG - Allow configurable story path patterns
|
|
179
|
+
* @req REQ-REGEX-VALIDATION - Validate story path regex options
|
|
180
|
+
* @req REQ-BACKWARD-COMPAT - Use a default when no pattern is provided
|
|
181
|
+
*/
|
|
115
182
|
function resolveStoryPattern(nestedStoryPattern, flatStoryPattern) {
|
|
116
183
|
return resolvePattern({
|
|
117
184
|
nestedPattern: nestedStoryPattern,
|
|
@@ -121,6 +188,15 @@ function resolveStoryPattern(nestedStoryPattern, flatStoryPattern) {
|
|
|
121
188
|
defaultPattern: getDefaultStoryPattern(),
|
|
122
189
|
});
|
|
123
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* Resolve the requirement ID pattern from nested or flat configuration
|
|
193
|
+
* fields, validating and falling back to the default as needed.
|
|
194
|
+
*
|
|
195
|
+
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
196
|
+
* @req REQ-PATTERN-CONFIG - Allow configurable requirement ID patterns
|
|
197
|
+
* @req REQ-REGEX-VALIDATION - Validate requirement ID regex options
|
|
198
|
+
* @req REQ-BACKWARD-COMPAT - Use a default when no pattern is provided
|
|
199
|
+
*/
|
|
124
200
|
function resolveReqPattern(nestedReqPattern, flatReqPattern) {
|
|
125
201
|
return resolvePattern({
|
|
126
202
|
nestedPattern: nestedReqPattern,
|
|
@@ -130,36 +206,93 @@ function resolveReqPattern(nestedReqPattern, flatReqPattern) {
|
|
|
130
206
|
defaultPattern: getDefaultReqPattern(),
|
|
131
207
|
});
|
|
132
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* Resolve the story example string from nested or flat configuration
|
|
211
|
+
* fields, preferring user-provided values and falling back to the default.
|
|
212
|
+
*
|
|
213
|
+
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
214
|
+
* @req REQ-EXAMPLE-MESSAGES - Allow custom story examples in messages
|
|
215
|
+
* @req REQ-BACKWARD-COMPAT - Use a default story example when omitted
|
|
216
|
+
*/
|
|
133
217
|
function resolveStoryExample(nestedStoryExample, flatStoryExample) {
|
|
134
218
|
return resolveExample(nestedStoryExample, flatStoryExample, getDefaultStoryExample());
|
|
135
219
|
}
|
|
220
|
+
/**
|
|
221
|
+
* Resolve the requirement ID example string from nested or flat configuration
|
|
222
|
+
* fields, preferring user-provided values and falling back to the default.
|
|
223
|
+
*
|
|
224
|
+
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
225
|
+
* @req REQ-EXAMPLE-MESSAGES - Allow custom requirement ID examples in messages
|
|
226
|
+
* @req REQ-BACKWARD-COMPAT - Use a default requirement ID example when omitted
|
|
227
|
+
*/
|
|
136
228
|
function resolveReqExample(nestedReqExample, flatReqExample) {
|
|
137
229
|
return resolveExample(nestedReqExample, flatReqExample, getDefaultReqExample());
|
|
138
230
|
}
|
|
231
|
+
/**
|
|
232
|
+
* Collect user-provided story pattern inputs from both nested and flat
|
|
233
|
+
* configuration fields to support backward-compatible shapes.
|
|
234
|
+
*
|
|
235
|
+
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
236
|
+
* @req REQ-PATTERN-CONFIG - Read story patterns from multiple option shapes
|
|
237
|
+
* @req REQ-BACKWARD-COMPAT - Support legacy flat storyPathPattern
|
|
238
|
+
*/
|
|
139
239
|
function getStoryPatternInputs(user) {
|
|
140
240
|
return {
|
|
141
241
|
nestedStoryPattern: user?.story?.pattern,
|
|
142
242
|
flatStoryPattern: user?.storyPathPattern,
|
|
143
243
|
};
|
|
144
244
|
}
|
|
245
|
+
/**
|
|
246
|
+
* Collect user-provided story example inputs from both nested and flat
|
|
247
|
+
* configuration fields to support backward-compatible shapes.
|
|
248
|
+
*
|
|
249
|
+
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
250
|
+
* @req REQ-EXAMPLE-MESSAGES - Read story examples from multiple option shapes
|
|
251
|
+
* @req REQ-BACKWARD-COMPAT - Support legacy flat storyPathExample
|
|
252
|
+
*/
|
|
145
253
|
function getStoryExampleInputs(user) {
|
|
146
254
|
return {
|
|
147
255
|
nestedStoryExample: user?.story?.example,
|
|
148
256
|
flatStoryExample: user?.storyPathExample,
|
|
149
257
|
};
|
|
150
258
|
}
|
|
259
|
+
/**
|
|
260
|
+
* Collect user-provided requirement ID pattern inputs from both nested
|
|
261
|
+
* and flat configuration fields to support backward-compatible shapes.
|
|
262
|
+
*
|
|
263
|
+
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
264
|
+
* @req REQ-PATTERN-CONFIG - Read requirement ID patterns from multiple shapes
|
|
265
|
+
* @req REQ-BACKWARD-COMPAT - Support legacy flat requirementIdPattern
|
|
266
|
+
*/
|
|
151
267
|
function getReqPatternInputs(user) {
|
|
152
268
|
return {
|
|
153
269
|
nestedReqPattern: user?.req?.pattern,
|
|
154
270
|
flatReqPattern: user?.requirementIdPattern,
|
|
155
271
|
};
|
|
156
272
|
}
|
|
273
|
+
/**
|
|
274
|
+
* Collect user-provided requirement ID example inputs from both nested
|
|
275
|
+
* and flat configuration fields to support backward-compatible shapes.
|
|
276
|
+
*
|
|
277
|
+
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
278
|
+
* @req REQ-EXAMPLE-MESSAGES - Read requirement ID examples from multiple shapes
|
|
279
|
+
* @req REQ-BACKWARD-COMPAT - Support legacy flat requirementIdExample
|
|
280
|
+
*/
|
|
157
281
|
function getReqExampleInputs(user) {
|
|
158
282
|
return {
|
|
159
283
|
nestedReqExample: user?.req?.example,
|
|
160
284
|
flatReqExample: user?.requirementIdExample,
|
|
161
285
|
};
|
|
162
286
|
}
|
|
287
|
+
/**
|
|
288
|
+
* Internal helper to resolve all rule options into a concrete, validated
|
|
289
|
+
* ResolvedAnnotationOptions structure, applying defaults and validation.
|
|
290
|
+
*
|
|
291
|
+
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
292
|
+
* @req REQ-PATTERN-CONFIG - Combine pattern and example configuration
|
|
293
|
+
* @req REQ-REGEX-VALIDATION - Enforce regex validity during resolution
|
|
294
|
+
* @req REQ-BACKWARD-COMPAT - Respect defaults when options are missing
|
|
295
|
+
*/
|
|
163
296
|
function resolveOptionsInternal(user) {
|
|
164
297
|
const { nestedStoryPattern, flatStoryPattern } = getStoryPatternInputs(user);
|
|
165
298
|
const { nestedStoryExample, flatStoryExample } = getStoryExampleInputs(user);
|
|
@@ -181,6 +314,11 @@ function resolveOptionsInternal(user) {
|
|
|
181
314
|
/**
|
|
182
315
|
* Resolve user options into concrete, validated configuration.
|
|
183
316
|
* Falls back to existing defaults when options are not provided or invalid.
|
|
317
|
+
*
|
|
318
|
+
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
319
|
+
* @req REQ-PATTERN-CONFIG - Drive rule behavior from configurable patterns
|
|
320
|
+
* @req REQ-REGEX-VALIDATION - Validate all configured regex patterns
|
|
321
|
+
* @req REQ-BACKWARD-COMPAT - Maintain behavior when no custom options set
|
|
184
322
|
*/
|
|
185
323
|
function resolveOptions(rawOptions) {
|
|
186
324
|
optionErrors = [];
|
|
@@ -190,7 +328,12 @@ function resolveOptions(rawOptions) {
|
|
|
190
328
|
return resolvedDefaults;
|
|
191
329
|
}
|
|
192
330
|
/**
|
|
193
|
-
* Build the JSON
|
|
331
|
+
* Build the JSON Schema definition that validates rule configuration
|
|
332
|
+
* passed to ESLint, ensuring option shapes and types are correct.
|
|
333
|
+
*
|
|
334
|
+
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
335
|
+
* @req REQ-SCHEMA-VALIDATION - Define a JSON Schema for rule options
|
|
336
|
+
* @req REQ-PATTERN-CONFIG - Describe configurable pattern and example fields
|
|
194
337
|
*/
|
|
195
338
|
function getRuleSchema() {
|
|
196
339
|
return [
|
|
@@ -81,10 +81,14 @@ function reportInvalidImplementsReqId(context, comment, reqId, options) {
|
|
|
81
81
|
});
|
|
82
82
|
}
|
|
83
83
|
/**
|
|
84
|
-
*
|
|
84
|
+
* Parse the raw token stream for an @implements annotation into a structured
|
|
85
|
+
* representation with a single storyPath and an array of requirement IDs.
|
|
85
86
|
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
87
|
+
* Handles trimming, token splitting, and basic structural checks, and reports
|
|
88
|
+
* missing-value conditions via the provided dependency helpers.
|
|
89
|
+
*
|
|
90
|
+
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
91
|
+
* @req REQ-IMPLEMENTS-TOKENS
|
|
88
92
|
*/
|
|
89
93
|
function parseImplementsTokens(deps, context, comment, rest) {
|
|
90
94
|
const { MIN_IMPLEMENTS_TOKENS, reportMissingImplementsValue, reportMissingImplementsReqIds, } = deps;
|
|
@@ -103,8 +107,12 @@ function parseImplementsTokens(deps, context, comment, rest) {
|
|
|
103
107
|
return { storyPath, reqIds };
|
|
104
108
|
}
|
|
105
109
|
/**
|
|
106
|
-
* Validate
|
|
107
|
-
*
|
|
110
|
+
* Validate a previously parsed @implements token structure against configured
|
|
111
|
+
* story and requirement patterns, reporting any configuration or format errors
|
|
112
|
+
* via the supplied dependency helpers.
|
|
113
|
+
*
|
|
114
|
+
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
115
|
+
* @req REQ-IMPLEMENTS-TOKENS
|
|
108
116
|
*/
|
|
109
117
|
function validateImplementsTokens(deps, context, comment, rest) {
|
|
110
118
|
const { reportInvalidImplementsStoryPath, reportInvalidImplementsReqId } = deps;
|
|
@@ -21,6 +21,12 @@ const DEFAULT_ALWAYS_COVERED_STATEMENTS = [
|
|
|
21
21
|
const DEFAULT_STRICTNESS = "moderate";
|
|
22
22
|
const DEFAULT_ALLOW_EMPHASIS_DUPLICATION = false;
|
|
23
23
|
const DEFAULT_MAX_SCOPE_DEPTH = 3;
|
|
24
|
+
/**
|
|
25
|
+
* Normalize and apply defaults to rule options for the redundancy detector.
|
|
26
|
+
*
|
|
27
|
+
* @story docs/stories/027.0-DEV-REDUNDANT-ANNOTATION-DETECTION.story.md
|
|
28
|
+
* @req REQ-REDUNDANT-OPTIONS
|
|
29
|
+
*/
|
|
24
30
|
function normalizeOptions(raw) {
|
|
25
31
|
const strictness = raw && typeof raw.strictness === "string"
|
|
26
32
|
? raw.strictness
|
|
@@ -287,6 +293,12 @@ const rule = {
|
|
|
287
293
|
redundantAnnotation: "Annotation on this statement is redundant; it is already covered by its containing scope.",
|
|
288
294
|
},
|
|
289
295
|
},
|
|
296
|
+
/**
|
|
297
|
+
* Wire up the ESLint visitors that detect and fix redundant annotations.
|
|
298
|
+
*
|
|
299
|
+
* @story docs/stories/027.0-DEV-REDUNDANT-ANNOTATION-DETECTION.story.md
|
|
300
|
+
* @req REQ-REDUNDANT-DETECTION
|
|
301
|
+
*/
|
|
290
302
|
create(context) {
|
|
291
303
|
const options = normalizeOptions(context.options[0]);
|
|
292
304
|
return {
|