eslint-plugin-traceability 1.8.1 → 1.8.2
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 +4 -4
- package/README.md +4 -4
- package/SECURITY.md +40 -37
- package/lib/src/maintenance/cli.js +12 -16
- package/lib/src/maintenance/detect.js +28 -1
- package/lib/src/rules/helpers/require-story-io.d.ts +2 -2
- package/lib/src/rules/helpers/require-story-io.js +13 -13
- package/lib/src/rules/helpers/valid-annotation-format-internal.d.ts +2 -2
- package/lib/src/rules/helpers/valid-annotation-format-internal.js +3 -3
- package/lib/src/rules/helpers/valid-annotation-utils.d.ts +5 -0
- package/lib/src/rules/helpers/valid-annotation-utils.js +43 -5
- package/lib/src/rules/helpers/valid-implements-utils.d.ts +11 -11
- package/lib/src/rules/helpers/valid-implements-utils.js +11 -11
- package/lib/src/rules/helpers/valid-story-reference-helpers.js +19 -0
- package/lib/src/rules/prefer-implements-annotation.d.ts +7 -7
- package/lib/src/rules/prefer-implements-annotation.js +21 -21
- package/lib/src/rules/valid-annotation-format.js +50 -24
- package/lib/src/rules/valid-req-reference.js +9 -9
- package/lib/src/utils/annotation-checker.js +3 -1
- package/lib/src/utils/reqAnnotationDetection.d.ts +2 -2
- package/lib/src/utils/reqAnnotationDetection.js +28 -28
- package/lib/tests/maintenance/batch.test.js +11 -11
- package/lib/tests/maintenance/cli.test.js +34 -27
- package/lib/tests/maintenance/report.test.js +7 -7
- package/lib/tests/rules/prefer-implements-annotation.test.js +27 -22
- package/lib/tests/rules/require-branch-annotation.test.js +15 -36
- package/lib/tests/rules/require-req-annotation.test.js +31 -104
- package/lib/tests/rules/require-story-annotation.test.js +3 -3
- package/lib/tests/rules/require-story-io-behavior.test.js +2 -7
- package/lib/tests/rules/require-story-io.edgecases.test.js +2 -7
- package/lib/tests/rules/require-story-visitors-edgecases.test.js +8 -8
- package/lib/tests/rules/valid-annotation-format.test.js +23 -23
- package/lib/tests/rules/valid-req-reference.test.js +9 -9
- package/lib/tests/rules/valid-story-reference.test.js +4 -43
- package/lib/tests/utils/annotation-checker.test.js +2 -6
- package/lib/tests/utils/fsTestHelpers.d.ts +7 -0
- package/lib/tests/utils/fsTestHelpers.js +26 -0
- package/lib/tests/utils/ioTestHelpers.d.ts +7 -0
- package/lib/tests/utils/ioTestHelpers.js +24 -0
- package/lib/tests/utils/temp-dir-helpers.d.ts +14 -0
- package/lib/tests/utils/temp-dir-helpers.js +61 -0
- package/package.json +3 -2
- package/user-docs/api-reference.md +12 -15
- package/user-docs/migration-guide.md +21 -21
|
@@ -39,6 +39,7 @@ exports.STORY_EXAMPLE_PATH = "docs/stories/005.0-DEV-EXAMPLE.story.md";
|
|
|
39
39
|
* @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
|
|
40
40
|
*/
|
|
41
41
|
function collapseAnnotationValue(value) {
|
|
42
|
+
// @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-MULTILINE-SUPPORT
|
|
42
43
|
return value.replace(/\s+/g, "");
|
|
43
44
|
}
|
|
44
45
|
/**
|
|
@@ -51,29 +52,52 @@ function collapseAnnotationValue(value) {
|
|
|
51
52
|
*
|
|
52
53
|
* Returns the fixed path when safe, or null if no fix should be applied.
|
|
53
54
|
*
|
|
55
|
+
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
54
56
|
* @story docs/stories/008.0-DEV-AUTO-FIX.story.md
|
|
57
|
+
* @story docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md
|
|
58
|
+
* @story docs/stories/010.2-REQ-STORY-PATH-AUTOFIX.story.md
|
|
55
59
|
* @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
|
|
56
60
|
* @req REQ-AUTOFIX-SAFE - Auto-fix must be conservative and never broaden the referenced path
|
|
57
61
|
* @req REQ-AUTOFIX-PRESERVE - Preserve surrounding formatting when normalizing story path suffixes
|
|
58
62
|
*/
|
|
59
63
|
function getFixedStoryPath(original) {
|
|
60
|
-
// @story docs/stories/
|
|
64
|
+
// @story docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md | REQ-AUTOFIX-SAFE - Reject auto-fix when the path contains ".." traversal segments to avoid broadening the reference.
|
|
65
|
+
// @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces correctness of the story identifier by rejecting paths that use unsafe traversal segments.
|
|
61
66
|
if (original.includes("..")) {
|
|
67
|
+
// @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT
|
|
68
|
+
// @implements docs/stories/008.0-DEV-AUTO-FIX.story.md REQ-AUTOFIX-SAFE
|
|
69
|
+
// @implements docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-AUTOFIX-SAFE
|
|
62
70
|
return null;
|
|
63
71
|
}
|
|
64
|
-
// @story docs/stories/
|
|
72
|
+
// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md | REQ-AUTOFIX-FORMAT - Leave correctly formatted ".story.md" paths unchanged so diagnostics are not hidden by redundant fixes.
|
|
73
|
+
// @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces correctness of the story identifier by recognizing already valid ".story.md" paths without altering them.
|
|
65
74
|
if (/\.story\.md$/.test(original)) {
|
|
75
|
+
// @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT
|
|
76
|
+
// @implements docs/stories/008.0-DEV-AUTO-FIX.story.md REQ-AUTOFIX-FORMAT
|
|
77
|
+
// @implements docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-AUTOFIX-FORMAT
|
|
66
78
|
return null;
|
|
67
79
|
}
|
|
68
|
-
// @story docs/stories/008.0-DEV-AUTO-FIX.story.md | REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE -
|
|
80
|
+
// @story docs/stories/008.0-DEV-AUTO-FIX.story.md | REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE - When ".story" is present but ".md" is missing, append only the extension without altering the base path.
|
|
81
|
+
// @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces correctness of the story identifier by completing a partially correct ".story" suffix to the canonical ".story.md".
|
|
69
82
|
if (/\.story$/.test(original)) {
|
|
83
|
+
// @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT
|
|
84
|
+
// @implements docs/stories/008.0-DEV-AUTO-FIX.story.md REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE
|
|
85
|
+
// @implements docs/stories/010.2-REQ-STORY-PATH-AUTOFIX.story.md REQ-AUTOFIX-FORMAT
|
|
70
86
|
return `${original}.md`;
|
|
71
87
|
}
|
|
72
|
-
// @story docs/stories/
|
|
88
|
+
// @story docs/stories/010.2-REQ-STORY-PATH-AUTOFIX.story.md | REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE - Normalize plain ".md" doc paths to ".story.md" while keeping the rest of the path intact.
|
|
89
|
+
// @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces correctness of the story identifier by transforming generic ".md" references into canonical ".story.md" story paths.
|
|
73
90
|
if (/\.md$/.test(original)) {
|
|
91
|
+
// @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT
|
|
92
|
+
// @implements docs/stories/008.0-DEV-AUTO-FIX.story.md REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE
|
|
93
|
+
// @implements docs/stories/010.2-REQ-STORY-PATH-AUTOFIX.story.md REQ-AUTOFIX-FORMAT
|
|
74
94
|
return original.replace(/\.md$/, ".story.md");
|
|
75
95
|
}
|
|
76
|
-
// @story docs/stories/
|
|
96
|
+
// @story docs/stories/010.2-REQ-STORY-PATH-AUTOFIX.story.md | REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE REQ-AUTOFIX-SAFE - For bare paths with no extension, append ".story.md" as a canonical story reference without touching the directory.
|
|
97
|
+
// @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces presence and correctness of the story identifier by supplying the standard ".story.md" suffix when no extension is provided.
|
|
98
|
+
// @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT
|
|
99
|
+
// @implements docs/stories/008.0-DEV-AUTO-FIX.story.md REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE REQ-AUTOFIX-SAFE
|
|
100
|
+
// @implements docs/stories/010.2-REQ-STORY-PATH-AUTOFIX.story.md REQ-AUTOFIX-FORMAT
|
|
77
101
|
return `${original}.story.md`;
|
|
78
102
|
}
|
|
79
103
|
/**
|
|
@@ -81,14 +105,21 @@ function getFixedStoryPath(original) {
|
|
|
81
105
|
*
|
|
82
106
|
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
83
107
|
* @story docs/stories/008.0-DEV-AUTO-FIX.story.md
|
|
108
|
+
* @story docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md
|
|
84
109
|
* @req REQ-ERROR-SPECIFICITY - Provide specific error messages for different format violations
|
|
85
110
|
* @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
|
|
86
111
|
*/
|
|
87
112
|
function buildStoryErrorMessage(kind, value, options) {
|
|
88
113
|
const example = options.storyExample || exports.STORY_EXAMPLE_PATH;
|
|
114
|
+
// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md | REQ-ERROR-SPECIFICITY - Use a dedicated message variant when the @story value is completely missing.
|
|
115
|
+
// @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces presence of the story identifier by emitting a targeted message when the @story value is absent.
|
|
89
116
|
if (kind === "missing") {
|
|
117
|
+
// @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-ERROR-SPECIFICITY
|
|
118
|
+
// @implements docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-ERROR-SPECIFICITY
|
|
90
119
|
return `Missing story path for @story annotation. Expected a path like "${example}".`;
|
|
91
120
|
}
|
|
121
|
+
// @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-ERROR-SPECIFICITY
|
|
122
|
+
// @implements docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-ERROR-SPECIFICITY
|
|
92
123
|
return `Invalid story path "${value ?? ""}" for @story annotation. Expected a path like "${example}".`;
|
|
93
124
|
}
|
|
94
125
|
/**
|
|
@@ -96,13 +127,20 @@ function buildStoryErrorMessage(kind, value, options) {
|
|
|
96
127
|
*
|
|
97
128
|
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
98
129
|
* @story docs/stories/008.0-DEV-AUTO-FIX.story.md
|
|
130
|
+
* @story docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md
|
|
99
131
|
* @req REQ-ERROR-SPECIFICITY - Provide specific error messages for different format violations
|
|
100
132
|
* @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
|
|
101
133
|
*/
|
|
102
134
|
function buildReqErrorMessage(kind, value, options) {
|
|
103
135
|
const example = options.reqExample || (0, valid_annotation_options_1.getDefaultReqExample)();
|
|
136
|
+
// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md | REQ-ERROR-SPECIFICITY - Distinguish a completely missing @req from one that is present but malformed.
|
|
137
|
+
// @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces presence of the requirement identifier by emitting a specific message when the @req value is missing.
|
|
104
138
|
if (kind === "missing") {
|
|
139
|
+
// @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-ERROR-SPECIFICITY
|
|
140
|
+
// @implements docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-ERROR-SPECIFICITY
|
|
105
141
|
return `Missing requirement ID for @req annotation. Expected an identifier like "${example}".`;
|
|
106
142
|
}
|
|
143
|
+
// @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-ERROR-SPECIFICITY
|
|
144
|
+
// @implements docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-ERROR-SPECIFICITY
|
|
107
145
|
return `Invalid requirement ID "${value ?? ""}" for @req annotation. Expected an identifier like "${example}" (uppercase letters, numbers, and dashes only).`;
|
|
108
146
|
}
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Helpers for @
|
|
2
|
+
* Helpers for @supports annotation validation used by valid-annotation-format.
|
|
3
3
|
*
|
|
4
4
|
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
5
|
-
* @req REQ-
|
|
6
|
-
* @req REQ-FORMAT-VALIDATION - Validate @
|
|
7
|
-
* @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@
|
|
5
|
+
* @req REQ-SUPPORTS-PARSE - Parse @supports annotations without affecting @story/@req
|
|
6
|
+
* @req REQ-FORMAT-VALIDATION - Validate @supports story path and requirement IDs
|
|
7
|
+
* @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@supports usage in comments
|
|
8
8
|
*/
|
|
9
9
|
import type { ResolvedAnnotationOptions } from "./valid-annotation-options";
|
|
10
10
|
/**
|
|
11
|
-
* Minimum number of tokens required for a valid @
|
|
11
|
+
* Minimum number of tokens required for a valid @supports value:
|
|
12
12
|
* - one story path
|
|
13
13
|
* - at least one requirement ID
|
|
14
14
|
*
|
|
15
15
|
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
16
|
-
* @req REQ-
|
|
16
|
+
* @req REQ-SUPPORTS-PARSE
|
|
17
17
|
*/
|
|
18
18
|
export declare const MIN_IMPLEMENTS_TOKENS = 2;
|
|
19
19
|
/**
|
|
20
|
-
* Report a completely missing @
|
|
20
|
+
* Report a completely missing @supports value (no story path or req IDs).
|
|
21
21
|
*
|
|
22
22
|
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
23
23
|
* @req REQ-FORMAT-VALIDATION
|
|
@@ -31,14 +31,14 @@ export declare function reportMissingImplementsValue(context: any, comment: any,
|
|
|
31
31
|
*/
|
|
32
32
|
export declare function reportMissingImplementsReqIds(context: any, comment: any, options: ResolvedAnnotationOptions): void;
|
|
33
33
|
/**
|
|
34
|
-
* Report an invalid story path inside @
|
|
34
|
+
* Report an invalid story path inside @supports.
|
|
35
35
|
*
|
|
36
36
|
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
37
37
|
* @req REQ-FORMAT-VALIDATION
|
|
38
38
|
*/
|
|
39
39
|
export declare function reportInvalidImplementsStoryPath(context: any, comment: any, storyPath: string, options: ResolvedAnnotationOptions): void;
|
|
40
40
|
/**
|
|
41
|
-
* Report an invalid requirement ID token inside @
|
|
41
|
+
* Report an invalid requirement ID token inside @supports.
|
|
42
42
|
*
|
|
43
43
|
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
44
44
|
* @req REQ-FORMAT-VALIDATION
|
|
@@ -53,7 +53,7 @@ type ImplementsDeps = {
|
|
|
53
53
|
reportInvalidImplementsReqId: typeof reportInvalidImplementsReqId;
|
|
54
54
|
};
|
|
55
55
|
/**
|
|
56
|
-
* Validate an @
|
|
56
|
+
* Validate an @supports annotation value.
|
|
57
57
|
*
|
|
58
58
|
* This helper encapsulates the logic previously in valid-annotation-format.ts:
|
|
59
59
|
* - trims the raw value
|
|
@@ -64,7 +64,7 @@ type ImplementsDeps = {
|
|
|
64
64
|
* - delegates reporting to the provided helpers
|
|
65
65
|
*
|
|
66
66
|
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
67
|
-
* @req REQ-
|
|
67
|
+
* @req REQ-SUPPORTS-PARSE
|
|
68
68
|
* @req REQ-FORMAT-VALIDATION
|
|
69
69
|
* @req REQ-MIXED-SUPPORT
|
|
70
70
|
*/
|
|
@@ -8,16 +8,16 @@ exports.reportInvalidImplementsReqId = reportInvalidImplementsReqId;
|
|
|
8
8
|
exports.validateImplementsAnnotationHelper = validateImplementsAnnotationHelper;
|
|
9
9
|
const valid_annotation_utils_1 = require("./valid-annotation-utils");
|
|
10
10
|
/**
|
|
11
|
-
* Minimum number of tokens required for a valid @
|
|
11
|
+
* Minimum number of tokens required for a valid @supports value:
|
|
12
12
|
* - one story path
|
|
13
13
|
* - at least one requirement ID
|
|
14
14
|
*
|
|
15
15
|
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
16
|
-
* @req REQ-
|
|
16
|
+
* @req REQ-SUPPORTS-PARSE
|
|
17
17
|
*/
|
|
18
18
|
exports.MIN_IMPLEMENTS_TOKENS = 2;
|
|
19
19
|
/**
|
|
20
|
-
* Report a completely missing @
|
|
20
|
+
* Report a completely missing @supports value (no story path or req IDs).
|
|
21
21
|
*
|
|
22
22
|
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
23
23
|
* @req REQ-FORMAT-VALIDATION
|
|
@@ -28,7 +28,7 @@ function reportMissingImplementsValue(context, comment, options) {
|
|
|
28
28
|
node: comment,
|
|
29
29
|
messageId: "invalidImplementsFormat",
|
|
30
30
|
data: {
|
|
31
|
-
details: `Missing story path and requirement IDs for @
|
|
31
|
+
details: `Missing story path and requirement IDs for @supports annotation. Expected a value like "${storyExample} ${reqExample}".`,
|
|
32
32
|
},
|
|
33
33
|
});
|
|
34
34
|
}
|
|
@@ -44,12 +44,12 @@ function reportMissingImplementsReqIds(context, comment, options) {
|
|
|
44
44
|
node: comment,
|
|
45
45
|
messageId: "invalidImplementsFormat",
|
|
46
46
|
data: {
|
|
47
|
-
details: `Missing requirement IDs for @
|
|
47
|
+
details: `Missing requirement IDs for @supports annotation. Expected a value like "${storyExample} ${reqExample}".`,
|
|
48
48
|
},
|
|
49
49
|
});
|
|
50
50
|
}
|
|
51
51
|
/**
|
|
52
|
-
* Report an invalid story path inside @
|
|
52
|
+
* Report an invalid story path inside @supports.
|
|
53
53
|
*
|
|
54
54
|
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
55
55
|
* @req REQ-FORMAT-VALIDATION
|
|
@@ -60,12 +60,12 @@ function reportInvalidImplementsStoryPath(context, comment, storyPath, options)
|
|
|
60
60
|
node: comment,
|
|
61
61
|
messageId: "invalidImplementsFormat",
|
|
62
62
|
data: {
|
|
63
|
-
details: `Invalid story path "${storyPath}" for @
|
|
63
|
+
details: `Invalid story path "${storyPath}" for @supports annotation. Expected a path like "${storyExample}".`,
|
|
64
64
|
},
|
|
65
65
|
});
|
|
66
66
|
}
|
|
67
67
|
/**
|
|
68
|
-
* Report an invalid requirement ID token inside @
|
|
68
|
+
* Report an invalid requirement ID token inside @supports.
|
|
69
69
|
*
|
|
70
70
|
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
71
71
|
* @req REQ-FORMAT-VALIDATION
|
|
@@ -81,7 +81,7 @@ function reportInvalidImplementsReqId(context, comment, reqId, options) {
|
|
|
81
81
|
});
|
|
82
82
|
}
|
|
83
83
|
/**
|
|
84
|
-
* Prepare and validate the token array for an @
|
|
84
|
+
* Prepare and validate the token array for an @supports value.
|
|
85
85
|
*
|
|
86
86
|
* Returns { storyPath, reqIds } when tokens are present and structurally valid,
|
|
87
87
|
* or null when a missing-value condition has been reported.
|
|
@@ -121,7 +121,7 @@ function validateImplementsTokens(deps, context, comment, rest) {
|
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
123
|
/**
|
|
124
|
-
* Validate an @
|
|
124
|
+
* Validate an @supports annotation value.
|
|
125
125
|
*
|
|
126
126
|
* This helper encapsulates the logic previously in valid-annotation-format.ts:
|
|
127
127
|
* - trims the raw value
|
|
@@ -132,7 +132,7 @@ function validateImplementsTokens(deps, context, comment, rest) {
|
|
|
132
132
|
* - delegates reporting to the provided helpers
|
|
133
133
|
*
|
|
134
134
|
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
135
|
-
* @req REQ-
|
|
135
|
+
* @req REQ-SUPPORTS-PARSE
|
|
136
136
|
* @req REQ-FORMAT-VALIDATION
|
|
137
137
|
* @req REQ-MIXED-SUPPORT
|
|
138
138
|
*/
|
|
@@ -20,11 +20,14 @@ function analyzeCandidateBoundaries(candidates, cwd) {
|
|
|
20
20
|
let hasInProjectCandidate = false;
|
|
21
21
|
let hasOutOfProjectCandidate = false;
|
|
22
22
|
for (const candidate of candidates) {
|
|
23
|
+
// @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
|
|
23
24
|
const boundary = (0, storyReferenceUtils_1.enforceProjectBoundary)(candidate, cwd);
|
|
24
25
|
if (boundary.isWithinProject) {
|
|
26
|
+
// @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY
|
|
25
27
|
hasInProjectCandidate = true;
|
|
26
28
|
}
|
|
27
29
|
else {
|
|
30
|
+
// @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY
|
|
28
31
|
hasOutOfProjectCandidate = true;
|
|
29
32
|
}
|
|
30
33
|
}
|
|
@@ -44,8 +47,12 @@ function analyzeCandidateBoundaries(candidates, cwd) {
|
|
|
44
47
|
*/
|
|
45
48
|
function handleProjectBoundaryForExistence({ storyPath, commentNode, context, cwd, candidates, existenceResult, reportInvalidPath, }) {
|
|
46
49
|
if (candidates.length > 0) {
|
|
50
|
+
// @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
|
|
51
|
+
// @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
|
|
47
52
|
const { hasInProjectCandidate, hasOutOfProjectCandidate } = analyzeCandidateBoundaries(candidates, cwd);
|
|
48
53
|
if (hasOutOfProjectCandidate && !hasInProjectCandidate) {
|
|
54
|
+
// @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
|
|
55
|
+
// @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY
|
|
49
56
|
reportInvalidPath({ storyPath, commentNode, context });
|
|
50
57
|
return true;
|
|
51
58
|
}
|
|
@@ -53,8 +60,12 @@ function handleProjectBoundaryForExistence({ storyPath, commentNode, context, cw
|
|
|
53
60
|
if (existenceResult &&
|
|
54
61
|
existenceResult.status === "exists" &&
|
|
55
62
|
existenceResult.matchedPath) {
|
|
63
|
+
// @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
|
|
64
|
+
// @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY
|
|
56
65
|
const boundary = (0, storyReferenceUtils_1.enforceProjectBoundary)(existenceResult.matchedPath, cwd);
|
|
57
66
|
if (!boundary.isWithinProject) {
|
|
67
|
+
// @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
|
|
68
|
+
// @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY
|
|
58
69
|
reportInvalidPath({ storyPath, commentNode, context });
|
|
59
70
|
return true;
|
|
60
71
|
}
|
|
@@ -72,7 +83,11 @@ function handleProjectBoundaryForExistence({ storyPath, commentNode, context, cw
|
|
|
72
83
|
function performSecurityValidations({ storyPath, commentNode, context, cwd, allowAbsolute, reportInvalidPath, }) {
|
|
73
84
|
// Absolute path check
|
|
74
85
|
if (path_1.default.isAbsolute(storyPath)) {
|
|
86
|
+
// @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
|
|
87
|
+
// @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-SECURITY-VALIDATION
|
|
75
88
|
if (!allowAbsolute) {
|
|
89
|
+
// @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
|
|
90
|
+
// @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-SECURITY-VALIDATION
|
|
76
91
|
reportInvalidPath({ storyPath, commentNode, context });
|
|
77
92
|
return false;
|
|
78
93
|
}
|
|
@@ -82,8 +97,12 @@ function performSecurityValidations({ storyPath, commentNode, context, cwd, allo
|
|
|
82
97
|
// Path traversal check
|
|
83
98
|
const containsTraversal = storyPath.includes("..") || /\\|\//.test(storyPath);
|
|
84
99
|
if (containsTraversal) {
|
|
100
|
+
// @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
|
|
101
|
+
// @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-SECURITY-VALIDATION
|
|
85
102
|
const full = path_1.default.resolve(cwd, path_1.default.normalize(storyPath));
|
|
86
103
|
if (!full.startsWith(cwd + path_1.default.sep)) {
|
|
104
|
+
// @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
|
|
105
|
+
// @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-SECURITY-VALIDATION
|
|
87
106
|
reportInvalidPath({ storyPath, commentNode, context });
|
|
88
107
|
return false;
|
|
89
108
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ESLint rule implementation for preferring the consolidated `@
|
|
2
|
+
* ESLint rule implementation for preferring the consolidated `@supports`
|
|
3
3
|
* annotation over legacy combinations of `@story` and `@req` within JSDoc
|
|
4
4
|
* block comments. This module provides:
|
|
5
5
|
*
|
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
* - Identification of multi-story comment blocks that are not safely
|
|
8
8
|
* auto-fixable.
|
|
9
9
|
* - A conservative auto-fix that rewrites simple, single-story patterns into
|
|
10
|
-
* a single `@
|
|
10
|
+
* a single `@supports` annotation while preserving formatting.
|
|
11
11
|
*
|
|
12
12
|
* The rule is intended as an **optional migration aid** to help projects
|
|
13
|
-
* gradually move to the newer `@
|
|
13
|
+
* gradually move to the newer `@supports` format without breaking existing
|
|
14
14
|
* traceability links.
|
|
15
15
|
*
|
|
16
|
-
* @story docs/stories/010.3-DEV-MIGRATE-TO-
|
|
17
|
-
* @req REQ-OPTIONAL-WARNING - Emit configurable recommendation diagnostics for legacy @story/@req usage
|
|
16
|
+
* @story docs/stories/010.3-DEV-MIGRATE-TO-SUPPORTS.story.md
|
|
17
|
+
* @req REQ-OPTIONAL-WARNING - Emit configurable recommendation diagnostics for legacy @story/@req usage in favor of @supports
|
|
18
18
|
* @req REQ-MULTI-STORY-DETECT - Detect multi-story patterns that cannot be auto-fixed
|
|
19
19
|
* @req REQ-SINGLE-STORY-FIX - Restrict auto-fix to single-story, single-path cases
|
|
20
20
|
* @req REQ-PRESERVE-FORMAT - Preserve original JSDoc indentation and prefix formatting
|
|
@@ -27,10 +27,10 @@ import type { Rule } from "eslint";
|
|
|
27
27
|
* ESLint rule: prefer-implements-annotation
|
|
28
28
|
*
|
|
29
29
|
* Recommend migrating from legacy `@story` + `@req` annotations to the
|
|
30
|
-
* newer `@
|
|
30
|
+
* newer `@supports` format. This rule is **disabled by default** and
|
|
31
31
|
* is intended as an optional, opt-in migration aid.
|
|
32
32
|
*
|
|
33
|
-
* @story docs/stories/010.3-DEV-MIGRATE-TO-
|
|
33
|
+
* @story docs/stories/010.3-DEV-MIGRATE-TO-SUPPORTS.story.md
|
|
34
34
|
* @req REQ-OPTIONAL-WARNING - Emit configurable recommendation diagnostics for legacy @story/@req usage
|
|
35
35
|
* @req REQ-MULTI-STORY-DETECT - Detect multi-story patterns that cannot be auto-fixed
|
|
36
36
|
* @req REQ-BACKWARD-COMP-VALIDATION - Keep legacy @story/@req annotations valid when the rule is disabled
|
|
@@ -5,7 +5,7 @@ const valid_annotation_format_internal_1 = require("./helpers/valid-annotation-f
|
|
|
5
5
|
// @req REQ-MULTI-STORY-DETECT - Centralized threshold constant for detecting multi-story patterns
|
|
6
6
|
const MULTI_STORY_THRESHOLD = 1;
|
|
7
7
|
// Minimum number of tokens required for a valid @story annotation line.
|
|
8
|
-
// @story docs/stories/010.3-DEV-MIGRATE-TO-
|
|
8
|
+
// @story docs/stories/010.3-DEV-MIGRATE-TO-SUPPORTS.story.md
|
|
9
9
|
// @req REQ-MULTI-STORY-DETECT
|
|
10
10
|
const MIN_STORY_TOKENS = 2;
|
|
11
11
|
// Minimum number of tokens required for a valid @req annotation line, aligned with story tokens.
|
|
@@ -23,8 +23,8 @@ function collectStoryAndReqMetadata(comment) {
|
|
|
23
23
|
const normalized = (0, valid_annotation_format_internal_1.normalizeCommentLine)(rawLine);
|
|
24
24
|
if (!normalized)
|
|
25
25
|
return;
|
|
26
|
-
if (/^@
|
|
27
|
-
// Mixed @
|
|
26
|
+
if (/^@supports\b/.test(normalized)) {
|
|
27
|
+
// Mixed @supports usage should have been filtered out earlier
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
30
|
if (/^@story\b/.test(normalized)) {
|
|
@@ -56,7 +56,7 @@ function applyImplementsReplacement(context, comment, details) {
|
|
|
56
56
|
const { storyIdx, allIndicesToRemove, storyPath, reqIds } = details;
|
|
57
57
|
const rawValue = comment.value || "";
|
|
58
58
|
const rawLines = rawValue.split(/\r?\n/);
|
|
59
|
-
const implAnnotation = `@
|
|
59
|
+
const implAnnotation = `@supports ${storyPath} ${reqIds.join(" ")}`;
|
|
60
60
|
// Determine the leading prefix (indentation and `*`) from the original @story line
|
|
61
61
|
const storyRawLine = rawLines[storyIdx];
|
|
62
62
|
const prefixMatch = storyRawLine.match(/^(\s*\*?\s*)/);
|
|
@@ -81,7 +81,7 @@ function applyImplementsReplacement(context, comment, details) {
|
|
|
81
81
|
}
|
|
82
82
|
/**
|
|
83
83
|
* Build an ESLint auto-fix for simple single-story `@story` + `@req` JSDoc
|
|
84
|
-
* blocks, converting them to a single `@
|
|
84
|
+
* blocks, converting them to a single `@supports` annotation while
|
|
85
85
|
* preserving the original comment formatting.
|
|
86
86
|
*
|
|
87
87
|
* The fixer is intentionally conservative and only activates when:
|
|
@@ -92,13 +92,13 @@ function applyImplementsReplacement(context, comment, details) {
|
|
|
92
92
|
*
|
|
93
93
|
* When applicable, the fix:
|
|
94
94
|
* - Removes the original `@story` and `@req` lines.
|
|
95
|
-
* - Inserts a single `@
|
|
95
|
+
* - Inserts a single `@supports` line in their place, preserving the
|
|
96
96
|
* original leading comment prefix (indentation and `*` markers).
|
|
97
97
|
*
|
|
98
98
|
* More complex patterns remain diagnostics-only with no fix to avoid
|
|
99
99
|
* producing invalid or ambiguous output.
|
|
100
100
|
*
|
|
101
|
-
* @implements docs/stories/010.3-DEV-MIGRATE-TO-
|
|
101
|
+
* @implements docs/stories/010.3-DEV-MIGRATE-TO-SUPPORTS.story.md
|
|
102
102
|
* @req REQ-AUTO-FIX - Provide safe, opt-in auto-fix for simple legacy patterns
|
|
103
103
|
* @req REQ-SINGLE-STORY-FIX - Restrict auto-fix to single-story, single-path cases
|
|
104
104
|
* @req REQ-PRESERVE-FORMAT - Preserve original JSDoc indentation and prefix formatting
|
|
@@ -136,7 +136,7 @@ function analyzeComment(comment) {
|
|
|
136
136
|
const normalized = (0, valid_annotation_format_internal_1.normalizeCommentLine)(rawLine);
|
|
137
137
|
if (!normalized)
|
|
138
138
|
return;
|
|
139
|
-
if (/^@
|
|
139
|
+
if (/^@supports\b/.test(normalized)) {
|
|
140
140
|
hasImplements = true;
|
|
141
141
|
return;
|
|
142
142
|
}
|
|
@@ -168,7 +168,7 @@ function processComment(comment, context) {
|
|
|
168
168
|
node: comment,
|
|
169
169
|
messageId: "cannotAutoFix",
|
|
170
170
|
data: {
|
|
171
|
-
reason: "comment mixes @story/@req with existing @
|
|
171
|
+
reason: "comment mixes @story/@req with existing @supports annotations",
|
|
172
172
|
},
|
|
173
173
|
});
|
|
174
174
|
return;
|
|
@@ -191,10 +191,10 @@ function processComment(comment, context) {
|
|
|
191
191
|
* ESLint rule: prefer-implements-annotation
|
|
192
192
|
*
|
|
193
193
|
* Recommend migrating from legacy `@story` + `@req` annotations to the
|
|
194
|
-
* newer `@
|
|
194
|
+
* newer `@supports` format. This rule is **disabled by default** and
|
|
195
195
|
* is intended as an optional, opt-in migration aid.
|
|
196
196
|
*
|
|
197
|
-
* @story docs/stories/010.3-DEV-MIGRATE-TO-
|
|
197
|
+
* @story docs/stories/010.3-DEV-MIGRATE-TO-SUPPORTS.story.md
|
|
198
198
|
* @req REQ-OPTIONAL-WARNING - Emit configurable recommendation diagnostics for legacy @story/@req usage
|
|
199
199
|
* @req REQ-MULTI-STORY-DETECT - Detect multi-story patterns that cannot be auto-fixed
|
|
200
200
|
* @req REQ-BACKWARD-COMP-VALIDATION - Keep legacy @story/@req annotations valid when the rule is disabled
|
|
@@ -203,7 +203,7 @@ const preferImplementsAnnotationRule = {
|
|
|
203
203
|
meta: {
|
|
204
204
|
type: "suggestion",
|
|
205
205
|
docs: {
|
|
206
|
-
description: "Recommend using @
|
|
206
|
+
description: "Recommend using @supports instead of legacy @story + @req annotations (optional migration rule)",
|
|
207
207
|
recommended: false,
|
|
208
208
|
},
|
|
209
209
|
// Auto-fix support will be wired in a later iteration; the rule starts as
|
|
@@ -212,31 +212,31 @@ const preferImplementsAnnotationRule = {
|
|
|
212
212
|
messages: {
|
|
213
213
|
/**
|
|
214
214
|
* Recommend migrating simple, single-story @story + @req blocks to a
|
|
215
|
-
* single @
|
|
215
|
+
* single @supports line. Auto-fix is provided where safe in a
|
|
216
216
|
* follow-up iteration.
|
|
217
217
|
*
|
|
218
|
-
* @story docs/stories/010.3-DEV-MIGRATE-TO-
|
|
218
|
+
* @story docs/stories/010.3-DEV-MIGRATE-TO-SUPPORTS.story.md
|
|
219
219
|
* @req REQ-OPTIONAL-WARNING
|
|
220
220
|
*/
|
|
221
|
-
preferImplements: "Consider using @
|
|
221
|
+
preferImplements: "Consider using @supports instead of @story + @req for clearer traceability. Run ESLint with --fix to auto-convert.",
|
|
222
222
|
/**
|
|
223
223
|
* Report situations where the rule detects a legacy annotation pattern
|
|
224
224
|
* but cannot safely provide an automatic fix. The `reason` field gives
|
|
225
225
|
* a short, human-readable explanation to guide manual migration.
|
|
226
226
|
*
|
|
227
|
-
* @story docs/stories/010.3-DEV-MIGRATE-TO-
|
|
227
|
+
* @story docs/stories/010.3-DEV-MIGRATE-TO-SUPPORTS.story.md
|
|
228
228
|
* @req REQ-MULTI-STORY-DETECT
|
|
229
229
|
*/
|
|
230
|
-
cannotAutoFix: "Cannot auto-fix: {{reason}}. Manual migration to @
|
|
230
|
+
cannotAutoFix: "Cannot auto-fix: {{reason}}. Manual migration to @supports required.",
|
|
231
231
|
/**
|
|
232
232
|
* Specialized message for the most common non-fixable case where more
|
|
233
233
|
* than one @story annotation appears in the same block, indicating a
|
|
234
234
|
* likely multi-story integration that must be converted manually.
|
|
235
235
|
*
|
|
236
|
-
* @story docs/stories/010.3-DEV-MIGRATE-TO-
|
|
236
|
+
* @story docs/stories/010.3-DEV-MIGRATE-TO-SUPPORTS.story.md
|
|
237
237
|
* @req REQ-MULTI-STORY-DETECT
|
|
238
238
|
*/
|
|
239
|
-
multiStoryDetected: "Multiple @story annotations detected in the same comment block. Manually convert to separate @
|
|
239
|
+
multiStoryDetected: "Multiple @story annotations detected in the same comment block. Manually convert to separate @supports lines.",
|
|
240
240
|
},
|
|
241
241
|
schema: [],
|
|
242
242
|
},
|
|
@@ -247,7 +247,7 @@ const preferImplementsAnnotationRule = {
|
|
|
247
247
|
* it surfaces recommendations when legacy `@story` + `@req` combinations are
|
|
248
248
|
* present but does not yet perform automatic code modifications.
|
|
249
249
|
*
|
|
250
|
-
* @story docs/stories/010.3-DEV-MIGRATE-TO-
|
|
250
|
+
* @story docs/stories/010.3-DEV-MIGRATE-TO-SUPPORTS.story.md
|
|
251
251
|
* @req REQ-OPTIONAL-WARNING
|
|
252
252
|
* @req REQ-MULTI-STORY-DETECT
|
|
253
253
|
*/
|
|
@@ -258,7 +258,7 @@ const preferImplementsAnnotationRule = {
|
|
|
258
258
|
* Program-level visitor that scans all comments for legacy
|
|
259
259
|
* `@story` + `@req` usage and emits recommendation diagnostics.
|
|
260
260
|
*
|
|
261
|
-
* @story docs/stories/010.3-DEV-MIGRATE-TO-
|
|
261
|
+
* @story docs/stories/010.3-DEV-MIGRATE-TO-SUPPORTS.story.md
|
|
262
262
|
* @req REQ-OPTIONAL-WARNING - Emit recommendations when legacy annotations are detected
|
|
263
263
|
* @req REQ-MULTI-STORY-DETECT - Detect multi-story and mixed annotation patterns
|
|
264
264
|
*/
|