eslint-plugin-traceability 1.21.0 → 1.22.0
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 +7 -2
- package/README.md +6 -6
- package/lib/src/maintenance/batch.js +0 -1
- package/lib/src/maintenance/cli.js +8 -10
- package/lib/src/maintenance/commands.d.ts +2 -2
- package/lib/src/maintenance/commands.js +2 -2
- package/lib/src/maintenance/detect.js +7 -7
- package/lib/src/maintenance/report.js +2 -2
- package/lib/src/maintenance/storyParser.d.ts +16 -0
- package/lib/src/maintenance/storyParser.js +167 -0
- package/lib/src/rules/helpers/pattern-validators.d.ts +42 -0
- package/lib/src/rules/helpers/pattern-validators.js +65 -0
- package/lib/src/rules/helpers/prefer-implements-inline.d.ts +16 -0
- package/lib/src/rules/helpers/prefer-implements-inline.js +146 -0
- package/lib/src/rules/helpers/require-story-comment-detection.d.ts +47 -0
- package/lib/src/rules/helpers/require-story-comment-detection.js +141 -0
- package/lib/src/rules/helpers/require-story-core.d.ts +6 -6
- package/lib/src/rules/helpers/require-story-core.js +10 -10
- package/lib/src/rules/helpers/require-story-helpers.d.ts +5 -63
- package/lib/src/rules/helpers/require-story-helpers.js +29 -337
- package/lib/src/rules/helpers/require-story-io.js +1 -0
- package/lib/src/rules/helpers/require-story-name-extraction.d.ts +35 -0
- package/lib/src/rules/helpers/require-story-name-extraction.js +107 -0
- package/lib/src/rules/helpers/require-story-node-utils.d.ts +43 -0
- package/lib/src/rules/helpers/require-story-node-utils.js +115 -0
- package/lib/src/rules/helpers/require-test-traceability-helpers.js +1 -0
- package/lib/src/rules/helpers/valid-annotation-format-internal.d.ts +2 -2
- package/lib/src/rules/helpers/valid-annotation-format-internal.js +13 -5
- package/lib/src/rules/helpers/valid-annotation-format-validators.d.ts +14 -14
- package/lib/src/rules/helpers/valid-annotation-format-validators.js +31 -22
- package/lib/src/rules/helpers/valid-annotation-options.d.ts +0 -10
- package/lib/src/rules/helpers/valid-annotation-options.js +22 -92
- package/lib/src/rules/helpers/valid-annotation-utils.js +1 -0
- package/lib/src/rules/helpers/valid-req-reference-helpers.js +1 -1
- package/lib/src/rules/no-redundant-annotation.js +4 -238
- package/lib/src/rules/prefer-implements-annotation.d.ts +12 -0
- package/lib/src/rules/prefer-implements-annotation.js +9 -164
- package/lib/src/rules/require-traceability.d.ts +8 -0
- package/lib/src/rules/require-traceability.js +8 -0
- package/lib/src/rules/valid-annotation-format.js +14 -10
- package/lib/src/utils/annotation-checker.d.ts +3 -2
- package/lib/src/utils/annotation-checker.js +4 -2
- package/lib/src/utils/branch-annotation-catch-helpers.d.ts +22 -0
- package/lib/src/utils/branch-annotation-catch-helpers.js +70 -0
- package/lib/src/utils/branch-annotation-helpers.js +11 -187
- package/lib/src/utils/branch-annotation-if-helpers.d.ts +1 -0
- package/lib/src/utils/branch-annotation-if-helpers.js +59 -0
- package/lib/src/utils/branch-annotation-indent-helpers.d.ts +1 -1
- package/lib/src/utils/branch-annotation-switch-helpers.d.ts +8 -2
- package/lib/src/utils/branch-annotation-switch-helpers.js +10 -4
- package/lib/src/utils/branch-validation.d.ts +9 -0
- package/lib/src/utils/branch-validation.js +58 -0
- package/lib/src/utils/comment-text-helpers.d.ts +31 -0
- package/lib/src/utils/comment-text-helpers.js +54 -0
- package/lib/src/utils/redundancy-detector.d.ts +85 -0
- package/lib/src/utils/redundancy-detector.js +235 -0
- package/lib/src/utils/reqAnnotationDetection.js +1 -0
- package/lib/tests/config/eslint-config-validation.test.js +1 -0
- package/lib/tests/config/flat-config-presets-integration.test.js +1 -0
- package/lib/tests/config/require-story-annotation-config.test.js +1 -0
- package/lib/tests/fixtures/stale/example.js +1 -0
- package/lib/tests/fixtures/update/example.js +1 -0
- package/lib/tests/integration/annotation-placement-inside-prettier.integration.test.js +1 -0
- package/lib/tests/integration/catch-annotation-prettier.integration.test.js +1 -0
- package/lib/tests/integration/else-if-annotation-prettier.integration.test.js +1 -0
- package/lib/tests/integration/prettier-test-helpers.js +1 -0
- package/lib/tests/integration/require-traceability-test-callbacks.integration.test.js +1 -0
- package/lib/tests/maintenance/detect-isolated.test.js +1 -0
- package/lib/tests/maintenance/storyParser.test.d.ts +8 -0
- package/lib/tests/maintenance/storyParser.test.js +505 -0
- package/lib/tests/perf/maintenance-large-workspace.test.js +1 -0
- package/lib/tests/perf/valid-annotation-format-large-file.test.js +1 -0
- package/lib/tests/plugin-setup.test.js +1 -0
- package/lib/tests/rules/error-reporting.test.js +1 -0
- package/lib/tests/rules/no-redundant-annotation.test.js +1 -0
- package/lib/tests/rules/require-story-helpers.test.js +3 -2
- package/lib/tests/rules/require-test-traceability.test.js +1 -0
- package/lib/tests/rules/valid-req-reference.test.js +2 -0
- package/lib/tests/utils/branch-annotation-catch-insert-position.test.js +1 -0
- package/lib/tests/utils/branch-annotation-else-if-insert-position.test.js +1 -0
- package/lib/tests/utils/branch-annotation-helpers.test.js +1 -0
- package/package.json +18 -10
- package/user-docs/api-reference.md +2 -2
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isAnonymousArrowFunction = isAnonymousArrowFunction;
|
|
4
|
+
exports.isNestedFunction = isNestedFunction;
|
|
5
|
+
exports.isEffectivelyAnonymousFunction = isEffectivelyAnonymousFunction;
|
|
6
|
+
exports.isExportedNode = isExportedNode;
|
|
7
|
+
exports.resolveTargetNode = resolveTargetNode;
|
|
8
|
+
exports.resolveAnnotationTargetNode = resolveAnnotationTargetNode;
|
|
9
|
+
/**
|
|
10
|
+
* Node classification utilities for require-story rule
|
|
11
|
+
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
12
|
+
* @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
13
|
+
* @req REQ-ANNOTATION-REQUIRED - File-level header for node utility functions
|
|
14
|
+
*/
|
|
15
|
+
const require_story_name_extraction_1 = require("./require-story-name-extraction");
|
|
16
|
+
/**
|
|
17
|
+
* Determine whether a node represents an anonymous arrow function expression
|
|
18
|
+
* where the parent variable declarator has no explicit Identifier name.
|
|
19
|
+
*
|
|
20
|
+
* @supports docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md REQ-ARROW-FUNCTION-EXCLUDED
|
|
21
|
+
*/
|
|
22
|
+
function isAnonymousArrowFunction(node) {
|
|
23
|
+
return !!node && node.type === "ArrowFunctionExpression";
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Determine whether a function-like node is nested within another function.
|
|
27
|
+
*
|
|
28
|
+
* @supports docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md REQ-NESTED-FUNCTION-INHERITANCE
|
|
29
|
+
*/
|
|
30
|
+
function isNestedFunction(node) {
|
|
31
|
+
let current = node?.parent;
|
|
32
|
+
while (current) {
|
|
33
|
+
if (current.type === "FunctionDeclaration" ||
|
|
34
|
+
current.type === "FunctionExpression" ||
|
|
35
|
+
current.type === "ArrowFunctionExpression" ||
|
|
36
|
+
current.type === "MethodDefinition" ||
|
|
37
|
+
current.type === "TSDeclareFunction" ||
|
|
38
|
+
current.type === "TSMethodSignature") {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
current = current.parent;
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Determine whether a function-like node is effectively anonymous for the
|
|
47
|
+
* purposes of nested-function inheritance. Named functions must always carry
|
|
48
|
+
* their own annotations, while anonymous nested functions may inherit.
|
|
49
|
+
*
|
|
50
|
+
* @supports docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md REQ-NESTED-FUNCTION-INHERITANCE
|
|
51
|
+
*/
|
|
52
|
+
function isEffectivelyAnonymousFunction(node) {
|
|
53
|
+
const name = (0, require_story_name_extraction_1.getContainerKeyOrIdName)(node) ?? (0, require_story_name_extraction_1.getDirectIdentifierName)(node);
|
|
54
|
+
if (typeof name === "string" && name.length > 0 && name !== "(anonymous)") {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Determine if a node is in an export declaration
|
|
61
|
+
*
|
|
62
|
+
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
63
|
+
* @req REQ-ANNOTATION-REQUIRED - Check node ancestry to find export declarations
|
|
64
|
+
*/
|
|
65
|
+
function isExportedNode(node) {
|
|
66
|
+
let p = node.parent;
|
|
67
|
+
while (p) {
|
|
68
|
+
if (p.type === "ExportNamedDeclaration" ||
|
|
69
|
+
p.type === "ExportDefaultDeclaration") {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
p = p.parent;
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Determine AST node where annotation should be inserted
|
|
78
|
+
*
|
|
79
|
+
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
80
|
+
* @req REQ-ANNOTATION-REQUIRED - Determine correct insertion target for annotation
|
|
81
|
+
*/
|
|
82
|
+
function resolveTargetNode(sourceCode, node) {
|
|
83
|
+
if (node.type === "TSMethodSignature") {
|
|
84
|
+
// Interface method signature -> insert on interface
|
|
85
|
+
return node.parent.parent;
|
|
86
|
+
}
|
|
87
|
+
if (node.type === "FunctionExpression" ||
|
|
88
|
+
node.type === "ArrowFunctionExpression") {
|
|
89
|
+
const parent = node.parent;
|
|
90
|
+
if (parent.type === "VariableDeclarator") {
|
|
91
|
+
const varDecl = parent.parent;
|
|
92
|
+
if (varDecl.parent && varDecl.parent.type === "ExportNamedDeclaration") {
|
|
93
|
+
return varDecl.parent;
|
|
94
|
+
}
|
|
95
|
+
return varDecl;
|
|
96
|
+
}
|
|
97
|
+
if (parent.type === "ExportNamedDeclaration") {
|
|
98
|
+
return parent;
|
|
99
|
+
}
|
|
100
|
+
if (parent.type === "ExpressionStatement") {
|
|
101
|
+
return parent;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return node;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Resolve the node that should receive the `@story` annotation,
|
|
108
|
+
* respecting an explicitly passed target when provided.
|
|
109
|
+
*
|
|
110
|
+
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
111
|
+
* @req REQ-ANNOTATION-REQUIRED - Centralize annotation target node resolution
|
|
112
|
+
*/
|
|
113
|
+
function resolveAnnotationTargetNode(sourceCode, node, passedTarget) {
|
|
114
|
+
return passedTarget ?? resolveTargetNode(sourceCode, node);
|
|
115
|
+
}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.determineIsTestFile = determineIsTestFile;
|
|
4
4
|
exports.ensureFileSupportsAnnotation = ensureFileSupportsAnnotation;
|
|
5
5
|
exports.handleCallExpression = handleCallExpression;
|
|
6
|
+
/* eslint-disable traceability/valid-annotation-format */
|
|
6
7
|
/**
|
|
7
8
|
* Helper utilities for the require-test-traceability rule.
|
|
8
9
|
*
|
|
@@ -19,7 +19,7 @@ export interface PendingAnnotation {
|
|
|
19
19
|
* boundaries), keeps any annotation tags that appear later in the line, and
|
|
20
20
|
* supports common JSDoc styles such as leading "*".
|
|
21
21
|
*
|
|
22
|
-
* It detects
|
|
22
|
+
* It detects `@story`, `@req`, and `@supports` tags while preserving the rest
|
|
23
23
|
* of the line for downstream logic.
|
|
24
24
|
*/
|
|
25
25
|
export declare function normalizeCommentLine(rawLine: string): string;
|
|
@@ -27,7 +27,7 @@ export declare function normalizeCommentLine(rawLine: string): string;
|
|
|
27
27
|
* Detect whether a normalized comment line starts with a non-traceability JSDoc tag.
|
|
28
28
|
*
|
|
29
29
|
* This is used to distinguish regular JSDoc tags (e.g. @param, @returns) from
|
|
30
|
-
* traceability-related annotations such as
|
|
30
|
+
* traceability-related annotations such as `@story`, `@req`, and `@supports`.
|
|
31
31
|
*
|
|
32
32
|
* Supports coexistence with JSDoc by:
|
|
33
33
|
* - Detecting boundaries between traceability tags and other tags
|
|
@@ -15,7 +15,7 @@ exports.isNonTraceabilityJSDocTagLine = isNonTraceabilityJSDocTagLine;
|
|
|
15
15
|
* boundaries), keeps any annotation tags that appear later in the line, and
|
|
16
16
|
* supports common JSDoc styles such as leading "*".
|
|
17
17
|
*
|
|
18
|
-
* It detects
|
|
18
|
+
* It detects `@story`, `@req`, and `@supports` tags while preserving the rest
|
|
19
19
|
* of the line for downstream logic.
|
|
20
20
|
*/
|
|
21
21
|
function normalizeCommentLine(rawLine) {
|
|
@@ -29,18 +29,26 @@ function normalizeCommentLine(rawLine) {
|
|
|
29
29
|
// This ensures annotations that appear outside code spans are still
|
|
30
30
|
// detected at their original indices.
|
|
31
31
|
const filtered = trimmed.replace(/`[^`]*`/g, (match) => " ".repeat(match.length));
|
|
32
|
-
|
|
32
|
+
// Remove leading star first to normalize JSDoc format
|
|
33
|
+
const withoutLeadingStar = filtered.replace(/^\*\s?/, "");
|
|
34
|
+
// Check if the line starts with a non-traceability JSDoc tag (e.g., @param, @returns)
|
|
35
|
+
// If so, return the whole line as-is to avoid false positives where annotation
|
|
36
|
+
// keywords appear in the tag's description (e.g., "`@returns` ... `@story` annotations")
|
|
37
|
+
if (/^@(?!story\b|req\b|supports\b)/.test(withoutLeadingStar)) {
|
|
38
|
+
return withoutLeadingStar;
|
|
39
|
+
}
|
|
40
|
+
// Otherwise, check for traceability annotations and slice to them if found
|
|
41
|
+
const annotationMatch = withoutLeadingStar.match(/@story\b|@req\b|@supports\b/);
|
|
33
42
|
if (!annotationMatch || annotationMatch.index === undefined) {
|
|
34
|
-
const withoutLeadingStar = filtered.replace(/^\*\s?/, "");
|
|
35
43
|
return withoutLeadingStar;
|
|
36
44
|
}
|
|
37
|
-
return
|
|
45
|
+
return withoutLeadingStar.slice(annotationMatch.index);
|
|
38
46
|
}
|
|
39
47
|
/**
|
|
40
48
|
* Detect whether a normalized comment line starts with a non-traceability JSDoc tag.
|
|
41
49
|
*
|
|
42
50
|
* This is used to distinguish regular JSDoc tags (e.g. @param, @returns) from
|
|
43
|
-
* traceability-related annotations such as
|
|
51
|
+
* traceability-related annotations such as `@story`, `@req`, and `@supports`.
|
|
44
52
|
*
|
|
45
53
|
* Supports coexistence with JSDoc by:
|
|
46
54
|
* - Detecting boundaries between traceability tags and other tags
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
* to read while still preserving all existing behavior.
|
|
8
8
|
*
|
|
9
9
|
* The implementation in this module supports:
|
|
10
|
-
* - validation of
|
|
11
|
-
* - validation of
|
|
12
|
-
* - validation of
|
|
10
|
+
* - validation of `@story` annotations
|
|
11
|
+
* - validation of `@req` annotations
|
|
12
|
+
* - validation of `@implements`/`@supports`-style annotations
|
|
13
13
|
* - safe, minimal auto-fixes for certain invalid formats
|
|
14
14
|
*
|
|
15
15
|
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
import type { ResolvedAnnotationOptions } from "./valid-annotation-options";
|
|
34
34
|
import type { PendingAnnotation } from "./valid-annotation-format-internal";
|
|
35
35
|
/**
|
|
36
|
-
* Report an invalid
|
|
36
|
+
* Report an invalid `@story` annotation without applying a fix.
|
|
37
37
|
*
|
|
38
|
-
* The invalid
|
|
38
|
+
* The invalid `@story` annotation is detected and reported but left unchanged.
|
|
39
39
|
*
|
|
40
40
|
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
41
41
|
* @story docs/stories/008.0-DEV-AUTO-FIX.story.md
|
|
@@ -44,10 +44,10 @@ import type { PendingAnnotation } from "./valid-annotation-format-internal";
|
|
|
44
44
|
*/
|
|
45
45
|
export declare function reportInvalidStoryFormat(context: any, comment: any, collapsed: string, options: ResolvedAnnotationOptions): void;
|
|
46
46
|
/**
|
|
47
|
-
* Compute the text replacement for an invalid
|
|
47
|
+
* Compute the text replacement for an invalid `@story` annotation within a comment.
|
|
48
48
|
*
|
|
49
49
|
* This helper:
|
|
50
|
-
* - finds the
|
|
50
|
+
* - finds the `@story` tag in the raw comment text,
|
|
51
51
|
* - computes the character range of its value,
|
|
52
52
|
* - and returns an ESLint fix that replaces only that range.
|
|
53
53
|
*
|
|
@@ -59,7 +59,7 @@ export declare function reportInvalidStoryFormat(context: any, comment: any, col
|
|
|
59
59
|
*/
|
|
60
60
|
export declare function createStoryFix(context: any, comment: any, fixed: string): null | (() => any);
|
|
61
61
|
/**
|
|
62
|
-
* Report an invalid
|
|
62
|
+
* Report an invalid `@story` annotation and attempt a minimal, safe auto-fix
|
|
63
63
|
* for common path suffix issues by locating and replacing the path text
|
|
64
64
|
* within the original comment.
|
|
65
65
|
*
|
|
@@ -76,10 +76,10 @@ export declare function createStoryFix(context: any, comment: any, fixed: string
|
|
|
76
76
|
*/
|
|
77
77
|
export declare function reportInvalidStoryFormatWithFix(context: any, comment: any, collapsed: string, fixed: string): void;
|
|
78
78
|
/**
|
|
79
|
-
* Validate a
|
|
79
|
+
* Validate a `@story` annotation value and report detailed errors when needed.
|
|
80
80
|
* Where safe and unambiguous, apply an automatic fix for missing suffixes.
|
|
81
81
|
*
|
|
82
|
-
* Processing of
|
|
82
|
+
* Processing of `@story` values includes:
|
|
83
83
|
* - trimming whitespace,
|
|
84
84
|
* - collapsing multi-line text,
|
|
85
85
|
* - matching against the configured story regex,
|
|
@@ -97,7 +97,7 @@ export declare function reportInvalidStoryFormatWithFix(context: any, comment: a
|
|
|
97
97
|
*/
|
|
98
98
|
export declare function validateStoryAnnotation(context: any, comment: any, rawValue: string, options: ResolvedAnnotationOptions): void;
|
|
99
99
|
/**
|
|
100
|
-
* Validate a
|
|
100
|
+
* Validate a `@req` annotation value and report detailed errors when needed.
|
|
101
101
|
*
|
|
102
102
|
* This behavior covers:
|
|
103
103
|
* - detecting missing identifiers,
|
|
@@ -115,14 +115,14 @@ export declare function validateStoryAnnotation(context: any, comment: any, rawV
|
|
|
115
115
|
*/
|
|
116
116
|
export declare function validateReqAnnotation(context: any, comment: any, rawValue: string, options: ResolvedAnnotationOptions): void;
|
|
117
117
|
/**
|
|
118
|
-
* Validate an
|
|
118
|
+
* Validate an `@supports` annotation value and report detailed errors when needed.
|
|
119
119
|
*
|
|
120
120
|
* Expected format:
|
|
121
|
-
*
|
|
121
|
+
* `@supports <storyPath> <REQ-ID> [<REQ-ID> ...]`
|
|
122
122
|
*
|
|
123
123
|
* Validation rules:
|
|
124
124
|
* - Value must include at least a story path and one requirement ID.
|
|
125
|
-
* - Story path must match the same storyPattern used for
|
|
125
|
+
* - Story path must match the same storyPattern used for `@story` (no auto-fix).
|
|
126
126
|
* - Each subsequent token must match reqPattern and is validated individually.
|
|
127
127
|
*
|
|
128
128
|
* Story path issues are reported with "invalidImplementsFormat" and
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
* to read while still preserving all existing behavior.
|
|
9
9
|
*
|
|
10
10
|
* The implementation in this module supports:
|
|
11
|
-
* - validation of
|
|
12
|
-
* - validation of
|
|
13
|
-
* - validation of
|
|
11
|
+
* - validation of `@story` annotations
|
|
12
|
+
* - validation of `@req` annotations
|
|
13
|
+
* - validation of `@implements`/`@supports`-style annotations
|
|
14
14
|
* - safe, minimal auto-fixes for certain invalid formats
|
|
15
15
|
*
|
|
16
16
|
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
@@ -43,9 +43,9 @@ const valid_annotation_utils_1 = require("./valid-annotation-utils");
|
|
|
43
43
|
const valid_implements_utils_1 = require("./valid-implements-utils");
|
|
44
44
|
const valid_annotation_options_1 = require("./valid-annotation-options");
|
|
45
45
|
/**
|
|
46
|
-
* Report an invalid
|
|
46
|
+
* Report an invalid `@story` annotation without applying a fix.
|
|
47
47
|
*
|
|
48
|
-
* The invalid
|
|
48
|
+
* The invalid `@story` annotation is detected and reported but left unchanged.
|
|
49
49
|
*
|
|
50
50
|
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
51
51
|
* @story docs/stories/008.0-DEV-AUTO-FIX.story.md
|
|
@@ -60,10 +60,10 @@ function reportInvalidStoryFormat(context, comment, collapsed, options) {
|
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
62
|
/**
|
|
63
|
-
* Compute the text replacement for an invalid
|
|
63
|
+
* Compute the text replacement for an invalid `@story` annotation within a comment.
|
|
64
64
|
*
|
|
65
65
|
* This helper:
|
|
66
|
-
* - finds the
|
|
66
|
+
* - finds the `@story` tag in the raw comment text,
|
|
67
67
|
* - computes the character range of its value,
|
|
68
68
|
* - and returns an ESLint fix that replaces only that range.
|
|
69
69
|
*
|
|
@@ -103,7 +103,7 @@ function createStoryFix(context, comment, fixed) {
|
|
|
103
103
|
return () => (fixer) => fixer.replaceTextRange(fixRange, fixed);
|
|
104
104
|
}
|
|
105
105
|
/**
|
|
106
|
-
* Report an invalid
|
|
106
|
+
* Report an invalid `@story` annotation and attempt a minimal, safe auto-fix
|
|
107
107
|
* for common path suffix issues by locating and replacing the path text
|
|
108
108
|
* within the original comment.
|
|
109
109
|
*
|
|
@@ -136,10 +136,10 @@ function reportInvalidStoryFormatWithFix(context, comment, collapsed, fixed) {
|
|
|
136
136
|
});
|
|
137
137
|
}
|
|
138
138
|
/**
|
|
139
|
-
* Validate a
|
|
139
|
+
* Validate a `@story` annotation value and report detailed errors when needed.
|
|
140
140
|
* Where safe and unambiguous, apply an automatic fix for missing suffixes.
|
|
141
141
|
*
|
|
142
|
-
* Processing of
|
|
142
|
+
* Processing of `@story` values includes:
|
|
143
143
|
* - trimming whitespace,
|
|
144
144
|
* - collapsing multi-line text,
|
|
145
145
|
* - matching against the configured story regex,
|
|
@@ -194,7 +194,7 @@ function validateStoryAnnotation(context, comment, rawValue, options) {
|
|
|
194
194
|
reportInvalidStoryFormat(context, comment, collapsed, options);
|
|
195
195
|
}
|
|
196
196
|
/**
|
|
197
|
-
* Validate a
|
|
197
|
+
* Validate a `@req` annotation value and report detailed errors when needed.
|
|
198
198
|
*
|
|
199
199
|
* This behavior covers:
|
|
200
200
|
* - detecting missing identifiers,
|
|
@@ -222,33 +222,42 @@ function validateReqAnnotation(context, comment, rawValue, options) {
|
|
|
222
222
|
});
|
|
223
223
|
return;
|
|
224
224
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
// while still validating simple multi-line @req identifiers that collapse
|
|
228
|
-
// to a single token.
|
|
229
|
-
if (collapsed.includes("@supports")) {
|
|
225
|
+
// Allow mixed `@req`/`@supports` lines to pass without additional `@req` validation.
|
|
226
|
+
if (trimmed.includes("@supports")) {
|
|
230
227
|
return;
|
|
231
228
|
}
|
|
232
|
-
const
|
|
229
|
+
const tokens = trimmed.split(/\s+/).filter(Boolean);
|
|
230
|
+
let reqId = tokens[0] || trimmed;
|
|
231
|
+
for (let index = 1; index < tokens.length; index += 1) {
|
|
232
|
+
const token = tokens[index];
|
|
233
|
+
if (token === "-" || token === "–" || token === "—")
|
|
234
|
+
break;
|
|
235
|
+
const candidate = `${reqId}${token}`;
|
|
236
|
+
if (reqId.endsWith("-") || options.reqPattern.test(candidate)) {
|
|
237
|
+
reqId = candidate;
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
233
242
|
// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
234
243
|
// @req REQ-REQ-FORMAT - Flag @req identifiers that do not match the configured pattern
|
|
235
|
-
if (!reqPattern.test(
|
|
244
|
+
if (!options.reqPattern.test(reqId)) {
|
|
236
245
|
context.report({
|
|
237
246
|
node: comment,
|
|
238
247
|
messageId: "invalidReqFormat",
|
|
239
|
-
data: { details: (0, valid_annotation_utils_1.buildReqErrorMessage)("invalid",
|
|
248
|
+
data: { details: (0, valid_annotation_utils_1.buildReqErrorMessage)("invalid", reqId, options) },
|
|
240
249
|
});
|
|
241
250
|
}
|
|
242
251
|
}
|
|
243
252
|
/**
|
|
244
|
-
* Validate an
|
|
253
|
+
* Validate an `@supports` annotation value and report detailed errors when needed.
|
|
245
254
|
*
|
|
246
255
|
* Expected format:
|
|
247
|
-
*
|
|
256
|
+
* `@supports <storyPath> <REQ-ID> [<REQ-ID> ...]`
|
|
248
257
|
*
|
|
249
258
|
* Validation rules:
|
|
250
259
|
* - Value must include at least a story path and one requirement ID.
|
|
251
|
-
* - Story path must match the same storyPattern used for
|
|
260
|
+
* - Story path must match the same storyPattern used for `@story` (no auto-fix).
|
|
252
261
|
* - Each subsequent token must match reqPattern and is validated individually.
|
|
253
262
|
*
|
|
254
263
|
* Story path issues are reported with "invalidImplementsFormat" and
|
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared option handling for the valid-annotation-format rule.
|
|
3
|
-
*
|
|
4
|
-
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
5
|
-
* @req REQ-PATTERN-CONFIG - Support configuration of custom story path and requirement ID patterns
|
|
6
|
-
* @req REQ-REGEX-VALIDATION - Validate that configured patterns are valid regular expressions
|
|
7
|
-
* @req REQ-BACKWARD-COMPAT - Maintain current behavior when no custom patterns configured
|
|
8
|
-
* @req REQ-EXAMPLE-MESSAGES - Support optional example strings in error messages
|
|
9
|
-
* @req REQ-SCHEMA-VALIDATION - Use JSON Schema to validate configuration options
|
|
10
|
-
*/
|
|
11
1
|
export interface AnnotationRuleOptions {
|
|
12
2
|
story?: {
|
|
13
3
|
/**
|
|
@@ -5,6 +5,17 @@ exports.getResolvedDefaults = getResolvedDefaults;
|
|
|
5
5
|
exports.getOptionErrors = getOptionErrors;
|
|
6
6
|
exports.resolveOptions = resolveOptions;
|
|
7
7
|
exports.getRuleSchema = getRuleSchema;
|
|
8
|
+
/**
|
|
9
|
+
* Shared option handling for the valid-annotation-format rule.
|
|
10
|
+
*
|
|
11
|
+
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
12
|
+
* @req REQ-PATTERN-CONFIG - Support configuration of custom story path and requirement ID patterns
|
|
13
|
+
* @req REQ-REGEX-VALIDATION - Validate that configured patterns are valid regular expressions
|
|
14
|
+
* @req REQ-BACKWARD-COMPAT - Maintain current behavior when no custom patterns configured
|
|
15
|
+
* @req REQ-EXAMPLE-MESSAGES - Support optional example strings in error messages
|
|
16
|
+
* @req REQ-SCHEMA-VALIDATION - Use JSON Schema to validate configuration options
|
|
17
|
+
*/
|
|
18
|
+
const pattern_validators_1 = require("./pattern-validators");
|
|
8
19
|
/**
|
|
9
20
|
* Get the default regular expression used to validate story paths.
|
|
10
21
|
*
|
|
@@ -84,92 +95,6 @@ function getOptionErrors() {
|
|
|
84
95
|
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
85
96
|
* @req REQ-PATTERN-CONFIG - Provide consistent regex validation diagnostics
|
|
86
97
|
*/
|
|
87
|
-
function buildInvalidRegexError(field, pattern) {
|
|
88
|
-
return `Invalid regular expression for option "${field}": "${pattern}"`;
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Normalize raw rule options into a single AnnotationRuleOptions object.
|
|
92
|
-
*
|
|
93
|
-
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
94
|
-
* @req REQ-PATTERN-CONFIG
|
|
95
|
-
* @req REQ-BACKWARD-COMPAT
|
|
96
|
-
*/
|
|
97
|
-
function normalizeUserOptions(rawOptions) {
|
|
98
|
-
if (!rawOptions || rawOptions.length === 0) {
|
|
99
|
-
return undefined;
|
|
100
|
-
}
|
|
101
|
-
const first = rawOptions[0];
|
|
102
|
-
if (!first || typeof first !== "object") {
|
|
103
|
-
return undefined;
|
|
104
|
-
}
|
|
105
|
-
return first;
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Resolve a user-configured regex pattern, handling both nested and flat
|
|
109
|
-
* configuration shapes and accumulating validation errors.
|
|
110
|
-
*
|
|
111
|
-
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
112
|
-
* @req REQ-PATTERN-CONFIG
|
|
113
|
-
* @req REQ-REGEX-VALIDATION
|
|
114
|
-
* @req REQ-BACKWARD-COMPAT
|
|
115
|
-
*/
|
|
116
|
-
function resolvePattern({ nestedPattern, nestedFieldName, flatPattern, flatFieldName, defaultPattern, }) {
|
|
117
|
-
const effective = typeof nestedPattern === "string"
|
|
118
|
-
? { value: nestedPattern, field: nestedFieldName }
|
|
119
|
-
: typeof flatPattern === "string"
|
|
120
|
-
? { value: flatPattern, field: flatFieldName }
|
|
121
|
-
: null;
|
|
122
|
-
if (!effective) {
|
|
123
|
-
return defaultPattern;
|
|
124
|
-
}
|
|
125
|
-
try {
|
|
126
|
-
return new RegExp(effective.value);
|
|
127
|
-
}
|
|
128
|
-
catch {
|
|
129
|
-
optionErrors.push(buildInvalidRegexError(effective.field, effective.value));
|
|
130
|
-
return defaultPattern;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Resolve an example string, preferring nested over flat configuration,
|
|
135
|
-
* and falling back to the provided default when necessary.
|
|
136
|
-
*
|
|
137
|
-
* @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
138
|
-
* @req REQ-EXAMPLE-MESSAGES
|
|
139
|
-
* @req REQ-BACKWARD-COMPAT
|
|
140
|
-
*/
|
|
141
|
-
function resolveExample(nestedExample, flatExample, defaultExample) {
|
|
142
|
-
if (typeof nestedExample === "string" && nestedExample.trim()) {
|
|
143
|
-
return nestedExample;
|
|
144
|
-
}
|
|
145
|
-
if (typeof flatExample === "string" && flatExample.trim()) {
|
|
146
|
-
return flatExample;
|
|
147
|
-
}
|
|
148
|
-
return defaultExample;
|
|
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
|
-
*/
|
|
158
|
-
function getUserOptions(rawOptions) {
|
|
159
|
-
return normalizeUserOptions(rawOptions);
|
|
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
|
-
*/
|
|
169
|
-
function resolveAutoFixFlag(user) {
|
|
170
|
-
const autoFixFlag = user?.autoFix;
|
|
171
|
-
return typeof autoFixFlag === "boolean" ? autoFixFlag : true;
|
|
172
|
-
}
|
|
173
98
|
/**
|
|
174
99
|
* Resolve the story path pattern from nested or flat configuration
|
|
175
100
|
* fields, validating and falling back to the default as needed.
|
|
@@ -180,12 +105,13 @@ function resolveAutoFixFlag(user) {
|
|
|
180
105
|
* @req REQ-BACKWARD-COMPAT - Use a default when no pattern is provided
|
|
181
106
|
*/
|
|
182
107
|
function resolveStoryPattern(nestedStoryPattern, flatStoryPattern) {
|
|
183
|
-
return resolvePattern({
|
|
108
|
+
return (0, pattern_validators_1.resolvePattern)({
|
|
184
109
|
nestedPattern: nestedStoryPattern,
|
|
185
110
|
nestedFieldName: "story.pattern",
|
|
186
111
|
flatPattern: flatStoryPattern,
|
|
187
112
|
flatFieldName: "storyPathPattern",
|
|
188
113
|
defaultPattern: getDefaultStoryPattern(),
|
|
114
|
+
errors: optionErrors,
|
|
189
115
|
});
|
|
190
116
|
}
|
|
191
117
|
/**
|
|
@@ -198,12 +124,13 @@ function resolveStoryPattern(nestedStoryPattern, flatStoryPattern) {
|
|
|
198
124
|
* @req REQ-BACKWARD-COMPAT - Use a default when no pattern is provided
|
|
199
125
|
*/
|
|
200
126
|
function resolveReqPattern(nestedReqPattern, flatReqPattern) {
|
|
201
|
-
return resolvePattern({
|
|
127
|
+
return (0, pattern_validators_1.resolvePattern)({
|
|
202
128
|
nestedPattern: nestedReqPattern,
|
|
203
129
|
nestedFieldName: "req.pattern",
|
|
204
130
|
flatPattern: flatReqPattern,
|
|
205
131
|
flatFieldName: "requirementIdPattern",
|
|
206
132
|
defaultPattern: getDefaultReqPattern(),
|
|
133
|
+
errors: optionErrors,
|
|
207
134
|
});
|
|
208
135
|
}
|
|
209
136
|
/**
|
|
@@ -215,7 +142,7 @@ function resolveReqPattern(nestedReqPattern, flatReqPattern) {
|
|
|
215
142
|
* @req REQ-BACKWARD-COMPAT - Use a default story example when omitted
|
|
216
143
|
*/
|
|
217
144
|
function resolveStoryExample(nestedStoryExample, flatStoryExample) {
|
|
218
|
-
return resolveExample(nestedStoryExample, flatStoryExample, getDefaultStoryExample());
|
|
145
|
+
return (0, pattern_validators_1.resolveExample)(nestedStoryExample, flatStoryExample, getDefaultStoryExample());
|
|
219
146
|
}
|
|
220
147
|
/**
|
|
221
148
|
* Resolve the requirement ID example string from nested or flat configuration
|
|
@@ -226,7 +153,7 @@ function resolveStoryExample(nestedStoryExample, flatStoryExample) {
|
|
|
226
153
|
* @req REQ-BACKWARD-COMPAT - Use a default requirement ID example when omitted
|
|
227
154
|
*/
|
|
228
155
|
function resolveReqExample(nestedReqExample, flatReqExample) {
|
|
229
|
-
return resolveExample(nestedReqExample, flatReqExample, getDefaultReqExample());
|
|
156
|
+
return (0, pattern_validators_1.resolveExample)(nestedReqExample, flatReqExample, getDefaultReqExample());
|
|
230
157
|
}
|
|
231
158
|
/**
|
|
232
159
|
* Collect user-provided story pattern inputs from both nested and flat
|
|
@@ -298,7 +225,8 @@ function resolveOptionsInternal(user) {
|
|
|
298
225
|
const { nestedStoryExample, flatStoryExample } = getStoryExampleInputs(user);
|
|
299
226
|
const { nestedReqPattern, flatReqPattern } = getReqPatternInputs(user);
|
|
300
227
|
const { nestedReqExample, flatReqExample } = getReqExampleInputs(user);
|
|
301
|
-
const
|
|
228
|
+
const autoFixFlag = user?.autoFix;
|
|
229
|
+
const autoFix = typeof autoFixFlag === "boolean" ? autoFixFlag : true;
|
|
302
230
|
const storyPattern = resolveStoryPattern(nestedStoryPattern, flatStoryPattern);
|
|
303
231
|
const reqPattern = resolveReqPattern(nestedReqPattern, flatReqPattern);
|
|
304
232
|
const storyExample = resolveStoryExample(nestedStoryExample, flatStoryExample);
|
|
@@ -322,7 +250,9 @@ function resolveOptionsInternal(user) {
|
|
|
322
250
|
*/
|
|
323
251
|
function resolveOptions(rawOptions) {
|
|
324
252
|
optionErrors = [];
|
|
325
|
-
const user =
|
|
253
|
+
const user = rawOptions && rawOptions.length > 0 && typeof rawOptions[0] === "object"
|
|
254
|
+
? rawOptions[0]
|
|
255
|
+
: undefined;
|
|
326
256
|
const resolved = resolveOptionsInternal(user);
|
|
327
257
|
resolvedDefaults = resolved;
|
|
328
258
|
return resolvedDefaults;
|
|
@@ -5,6 +5,7 @@ exports.collapseAnnotationValue = collapseAnnotationValue;
|
|
|
5
5
|
exports.getFixedStoryPath = getFixedStoryPath;
|
|
6
6
|
exports.buildStoryErrorMessage = buildStoryErrorMessage;
|
|
7
7
|
exports.buildReqErrorMessage = buildReqErrorMessage;
|
|
8
|
+
/* eslint-disable traceability/valid-annotation-format */
|
|
8
9
|
const valid_annotation_options_1 = require("./valid-annotation-options");
|
|
9
10
|
/**
|
|
10
11
|
* Shared constants and helpers for annotation-format validation.
|
|
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.createValidReqReferenceProgramVisitor = createValidReqReferenceProgramVisitor;
|
|
7
|
-
/* eslint-
|
|
7
|
+
/* eslint-disable traceability/valid-annotation-format */
|
|
8
8
|
/**
|
|
9
9
|
* Helper utilities for the "valid-req-reference" rule.
|
|
10
10
|
*
|