eslint-plugin-traceability 1.6.5 → 1.7.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/README.md +39 -1
- package/lib/src/index.d.ts +30 -27
- package/lib/src/index.js +51 -31
- package/lib/src/maintenance/cli.d.ts +12 -0
- package/lib/src/maintenance/cli.js +279 -0
- package/lib/src/maintenance/detect.js +27 -12
- package/lib/src/maintenance/update.js +42 -34
- package/lib/src/maintenance/utils.js +30 -30
- package/lib/src/rules/helpers/require-story-io.js +51 -15
- package/lib/src/rules/helpers/valid-annotation-format-internal.d.ts +30 -0
- package/lib/src/rules/helpers/valid-annotation-format-internal.js +36 -0
- package/lib/src/rules/helpers/valid-annotation-options.d.ts +118 -0
- package/lib/src/rules/helpers/valid-annotation-options.js +167 -0
- package/lib/src/rules/helpers/valid-annotation-utils.d.ts +68 -0
- package/lib/src/rules/helpers/valid-annotation-utils.js +103 -0
- package/lib/src/rules/helpers/valid-implements-utils.d.ts +75 -0
- package/lib/src/rules/helpers/valid-implements-utils.js +149 -0
- package/lib/src/rules/helpers/valid-story-reference-helpers.d.ts +67 -0
- package/lib/src/rules/helpers/valid-story-reference-helpers.js +92 -0
- package/lib/src/rules/prefer-implements-annotation.d.ts +39 -0
- package/lib/src/rules/prefer-implements-annotation.js +276 -0
- package/lib/src/rules/valid-annotation-format.js +255 -208
- package/lib/src/rules/valid-req-reference.js +210 -29
- package/lib/src/rules/valid-story-reference.d.ts +7 -0
- package/lib/src/rules/valid-story-reference.js +38 -80
- package/lib/src/utils/annotation-checker.js +2 -145
- package/lib/src/utils/branch-annotation-helpers.js +12 -3
- package/lib/src/utils/reqAnnotationDetection.d.ts +6 -0
- package/lib/src/utils/reqAnnotationDetection.js +152 -0
- package/lib/tests/maintenance/cli.test.d.ts +1 -0
- package/lib/tests/maintenance/cli.test.js +172 -0
- package/lib/tests/plugin-default-export-and-configs.test.js +3 -0
- package/lib/tests/rules/prefer-implements-annotation.test.d.ts +1 -0
- package/lib/tests/rules/prefer-implements-annotation.test.js +84 -0
- package/lib/tests/rules/require-branch-annotation.test.js +3 -2
- package/lib/tests/rules/require-req-annotation.test.js +57 -68
- package/lib/tests/rules/require-story-annotation.test.js +13 -28
- package/lib/tests/rules/require-story-core-edgecases.test.js +3 -58
- package/lib/tests/rules/require-story-core.autofix.test.js +5 -41
- package/lib/tests/rules/valid-annotation-format.test.js +395 -40
- package/lib/tests/rules/valid-req-reference.test.js +34 -0
- package/lib/tests/utils/annotation-checker.test.d.ts +23 -0
- package/lib/tests/utils/annotation-checker.test.js +24 -17
- package/lib/tests/utils/require-story-core-test-helpers.d.ts +10 -0
- package/lib/tests/utils/require-story-core-test-helpers.js +75 -0
- package/lib/tests/utils/ts-language-options.d.ts +22 -0
- package/lib/tests/utils/ts-language-options.js +27 -0
- package/package.json +12 -3
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { ResolvedAnnotationOptions } from "./valid-annotation-options";
|
|
2
|
+
/**
|
|
3
|
+
* Shared constants and helpers for annotation-format validation.
|
|
4
|
+
*
|
|
5
|
+
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
6
|
+
* @story docs/stories/008.0-DEV-AUTO-FIX.story.md
|
|
7
|
+
* @req REQ-MULTILINE-SUPPORT - Handle annotations split across multiple lines
|
|
8
|
+
* @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Constant to represent the "tag not found" index when searching
|
|
12
|
+
* for @story or @req within a comment.
|
|
13
|
+
*
|
|
14
|
+
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
15
|
+
* @story docs/stories/008.0-DEV-AUTO-FIX.story.md
|
|
16
|
+
* @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
|
|
17
|
+
* @req REQ-AUTOFIX-PRESERVE - Avoid risky text replacements when the annotation tag cannot be located
|
|
18
|
+
*/
|
|
19
|
+
export declare const TAG_NOT_FOUND_INDEX = -1;
|
|
20
|
+
export declare const STORY_EXAMPLE_PATH = "docs/stories/005.0-DEV-EXAMPLE.story.md";
|
|
21
|
+
/**
|
|
22
|
+
* Collapse internal whitespace in an annotation value so that multi-line
|
|
23
|
+
* annotations are treated as a single logical value.
|
|
24
|
+
*
|
|
25
|
+
* Example:
|
|
26
|
+
* "docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md" across
|
|
27
|
+
* multiple lines will be collapsed before validation.
|
|
28
|
+
*
|
|
29
|
+
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
30
|
+
* @story docs/stories/008.0-DEV-AUTO-FIX.story.md
|
|
31
|
+
* @req REQ-MULTILINE-SUPPORT - Handle annotations split across multiple lines
|
|
32
|
+
* @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
|
|
33
|
+
*/
|
|
34
|
+
export declare function collapseAnnotationValue(value: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Attempt a minimal, safe auto-fix for common @story path suffix issues.
|
|
37
|
+
*
|
|
38
|
+
* Only handles:
|
|
39
|
+
* - missing ".md"
|
|
40
|
+
* - missing ".story.md"
|
|
41
|
+
* and skips any paths with traversal segments (e.g. "..").
|
|
42
|
+
*
|
|
43
|
+
* Returns the fixed path when safe, or null if no fix should be applied.
|
|
44
|
+
*
|
|
45
|
+
* @story docs/stories/008.0-DEV-AUTO-FIX.story.md
|
|
46
|
+
* @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
|
|
47
|
+
* @req REQ-AUTOFIX-SAFE - Auto-fix must be conservative and never broaden the referenced path
|
|
48
|
+
* @req REQ-AUTOFIX-PRESERVE - Preserve surrounding formatting when normalizing story path suffixes
|
|
49
|
+
*/
|
|
50
|
+
export declare function getFixedStoryPath(original: string): string | null;
|
|
51
|
+
/**
|
|
52
|
+
* Build a detailed error message for invalid @story annotations.
|
|
53
|
+
*
|
|
54
|
+
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
55
|
+
* @story docs/stories/008.0-DEV-AUTO-FIX.story.md
|
|
56
|
+
* @req REQ-ERROR-SPECIFICITY - Provide specific error messages for different format violations
|
|
57
|
+
* @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
|
|
58
|
+
*/
|
|
59
|
+
export declare function buildStoryErrorMessage(kind: "missing" | "invalid", value: string | null, options: ResolvedAnnotationOptions): string;
|
|
60
|
+
/**
|
|
61
|
+
* Build a detailed error message for invalid @req annotations.
|
|
62
|
+
*
|
|
63
|
+
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
64
|
+
* @story docs/stories/008.0-DEV-AUTO-FIX.story.md
|
|
65
|
+
* @req REQ-ERROR-SPECIFICITY - Provide specific error messages for different format violations
|
|
66
|
+
* @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
|
|
67
|
+
*/
|
|
68
|
+
export declare function buildReqErrorMessage(kind: "missing" | "invalid", value: string | null, options: ResolvedAnnotationOptions): string;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.STORY_EXAMPLE_PATH = exports.TAG_NOT_FOUND_INDEX = void 0;
|
|
4
|
+
exports.collapseAnnotationValue = collapseAnnotationValue;
|
|
5
|
+
exports.getFixedStoryPath = getFixedStoryPath;
|
|
6
|
+
exports.buildStoryErrorMessage = buildStoryErrorMessage;
|
|
7
|
+
exports.buildReqErrorMessage = buildReqErrorMessage;
|
|
8
|
+
const valid_annotation_options_1 = require("./valid-annotation-options");
|
|
9
|
+
/**
|
|
10
|
+
* Shared constants and helpers for annotation-format validation.
|
|
11
|
+
*
|
|
12
|
+
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
13
|
+
* @story docs/stories/008.0-DEV-AUTO-FIX.story.md
|
|
14
|
+
* @req REQ-MULTILINE-SUPPORT - Handle annotations split across multiple lines
|
|
15
|
+
* @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Constant to represent the "tag not found" index when searching
|
|
19
|
+
* for @story or @req within a comment.
|
|
20
|
+
*
|
|
21
|
+
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
22
|
+
* @story docs/stories/008.0-DEV-AUTO-FIX.story.md
|
|
23
|
+
* @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
|
|
24
|
+
* @req REQ-AUTOFIX-PRESERVE - Avoid risky text replacements when the annotation tag cannot be located
|
|
25
|
+
*/
|
|
26
|
+
exports.TAG_NOT_FOUND_INDEX = -1;
|
|
27
|
+
exports.STORY_EXAMPLE_PATH = "docs/stories/005.0-DEV-EXAMPLE.story.md";
|
|
28
|
+
/**
|
|
29
|
+
* Collapse internal whitespace in an annotation value so that multi-line
|
|
30
|
+
* annotations are treated as a single logical value.
|
|
31
|
+
*
|
|
32
|
+
* Example:
|
|
33
|
+
* "docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md" across
|
|
34
|
+
* multiple lines will be collapsed before validation.
|
|
35
|
+
*
|
|
36
|
+
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
37
|
+
* @story docs/stories/008.0-DEV-AUTO-FIX.story.md
|
|
38
|
+
* @req REQ-MULTILINE-SUPPORT - Handle annotations split across multiple lines
|
|
39
|
+
* @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
|
|
40
|
+
*/
|
|
41
|
+
function collapseAnnotationValue(value) {
|
|
42
|
+
return value.replace(/\s+/g, "");
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Attempt a minimal, safe auto-fix for common @story path suffix issues.
|
|
46
|
+
*
|
|
47
|
+
* Only handles:
|
|
48
|
+
* - missing ".md"
|
|
49
|
+
* - missing ".story.md"
|
|
50
|
+
* and skips any paths with traversal segments (e.g. "..").
|
|
51
|
+
*
|
|
52
|
+
* Returns the fixed path when safe, or null if no fix should be applied.
|
|
53
|
+
*
|
|
54
|
+
* @story docs/stories/008.0-DEV-AUTO-FIX.story.md
|
|
55
|
+
* @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
|
|
56
|
+
* @req REQ-AUTOFIX-SAFE - Auto-fix must be conservative and never broaden the referenced path
|
|
57
|
+
* @req REQ-AUTOFIX-PRESERVE - Preserve surrounding formatting when normalizing story path suffixes
|
|
58
|
+
*/
|
|
59
|
+
function getFixedStoryPath(original) {
|
|
60
|
+
if (original.includes("..")) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
if (/\.story\.md$/.test(original)) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
if (/\.story$/.test(original)) {
|
|
67
|
+
return `${original}.md`;
|
|
68
|
+
}
|
|
69
|
+
if (/\.md$/.test(original)) {
|
|
70
|
+
return original.replace(/\.md$/, ".story.md");
|
|
71
|
+
}
|
|
72
|
+
return `${original}.story.md`;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Build a detailed error message for invalid @story annotations.
|
|
76
|
+
*
|
|
77
|
+
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
78
|
+
* @story docs/stories/008.0-DEV-AUTO-FIX.story.md
|
|
79
|
+
* @req REQ-ERROR-SPECIFICITY - Provide specific error messages for different format violations
|
|
80
|
+
* @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
|
|
81
|
+
*/
|
|
82
|
+
function buildStoryErrorMessage(kind, value, options) {
|
|
83
|
+
const example = options.storyExample || exports.STORY_EXAMPLE_PATH;
|
|
84
|
+
if (kind === "missing") {
|
|
85
|
+
return `Missing story path for @story annotation. Expected a path like "${example}".`;
|
|
86
|
+
}
|
|
87
|
+
return `Invalid story path "${value ?? ""}" for @story annotation. Expected a path like "${example}".`;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Build a detailed error message for invalid @req annotations.
|
|
91
|
+
*
|
|
92
|
+
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
93
|
+
* @story docs/stories/008.0-DEV-AUTO-FIX.story.md
|
|
94
|
+
* @req REQ-ERROR-SPECIFICITY - Provide specific error messages for different format violations
|
|
95
|
+
* @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
|
|
96
|
+
*/
|
|
97
|
+
function buildReqErrorMessage(kind, value, options) {
|
|
98
|
+
const example = options.reqExample || (0, valid_annotation_options_1.getDefaultReqExample)();
|
|
99
|
+
if (kind === "missing") {
|
|
100
|
+
return `Missing requirement ID for @req annotation. Expected an identifier like "${example}".`;
|
|
101
|
+
}
|
|
102
|
+
return `Invalid requirement ID "${value ?? ""}" for @req annotation. Expected an identifier like "${example}" (uppercase letters, numbers, and dashes only).`;
|
|
103
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for @implements annotation validation used by valid-annotation-format.
|
|
3
|
+
*
|
|
4
|
+
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
5
|
+
* @req REQ-IMPLEMENTS-PARSE - Parse @implements annotations without affecting @story/@req
|
|
6
|
+
* @req REQ-FORMAT-VALIDATION - Validate @implements story path and requirement IDs
|
|
7
|
+
* @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
|
|
8
|
+
*/
|
|
9
|
+
import type { ResolvedAnnotationOptions } from "./valid-annotation-options";
|
|
10
|
+
/**
|
|
11
|
+
* Minimum number of tokens required for a valid @implements value:
|
|
12
|
+
* - one story path
|
|
13
|
+
* - at least one requirement ID
|
|
14
|
+
*
|
|
15
|
+
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
16
|
+
* @req REQ-IMPLEMENTS-PARSE
|
|
17
|
+
*/
|
|
18
|
+
export declare const MIN_IMPLEMENTS_TOKENS = 2;
|
|
19
|
+
/**
|
|
20
|
+
* Report a completely missing @implements value (no story path or req IDs).
|
|
21
|
+
*
|
|
22
|
+
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
23
|
+
* @req REQ-FORMAT-VALIDATION
|
|
24
|
+
*/
|
|
25
|
+
export declare function reportMissingImplementsValue(context: any, comment: any, options: ResolvedAnnotationOptions): void;
|
|
26
|
+
/**
|
|
27
|
+
* Report a value that has only a story path and no requirement IDs.
|
|
28
|
+
*
|
|
29
|
+
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
30
|
+
* @req REQ-FORMAT-VALIDATION
|
|
31
|
+
*/
|
|
32
|
+
export declare function reportMissingImplementsReqIds(context: any, comment: any, options: ResolvedAnnotationOptions): void;
|
|
33
|
+
/**
|
|
34
|
+
* Report an invalid story path inside @implements.
|
|
35
|
+
*
|
|
36
|
+
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
37
|
+
* @req REQ-FORMAT-VALIDATION
|
|
38
|
+
*/
|
|
39
|
+
export declare function reportInvalidImplementsStoryPath(context: any, comment: any, storyPath: string, options: ResolvedAnnotationOptions): void;
|
|
40
|
+
/**
|
|
41
|
+
* Report an invalid requirement ID token inside @implements.
|
|
42
|
+
*
|
|
43
|
+
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
44
|
+
* @req REQ-FORMAT-VALIDATION
|
|
45
|
+
* @req REQ-MIXED-SUPPORT
|
|
46
|
+
*/
|
|
47
|
+
export declare function reportInvalidImplementsReqId(context: any, comment: any, reqId: string, options: ResolvedAnnotationOptions): void;
|
|
48
|
+
type ImplementsDeps = {
|
|
49
|
+
MIN_IMPLEMENTS_TOKENS: number;
|
|
50
|
+
reportMissingImplementsValue: typeof reportMissingImplementsValue;
|
|
51
|
+
reportMissingImplementsReqIds: typeof reportMissingImplementsReqIds;
|
|
52
|
+
reportInvalidImplementsStoryPath: typeof reportInvalidImplementsStoryPath;
|
|
53
|
+
reportInvalidImplementsReqId: typeof reportInvalidImplementsReqId;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Validate an @implements annotation value.
|
|
57
|
+
*
|
|
58
|
+
* This helper encapsulates the logic previously in valid-annotation-format.ts:
|
|
59
|
+
* - trims the raw value
|
|
60
|
+
* - splits into tokens
|
|
61
|
+
* - enforces MIN_IMPLEMENTS_TOKENS
|
|
62
|
+
* - validates the story path using options.storyPattern
|
|
63
|
+
* - validates each requirement ID using options.reqPattern
|
|
64
|
+
* - delegates reporting to the provided helpers
|
|
65
|
+
*
|
|
66
|
+
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
67
|
+
* @req REQ-IMPLEMENTS-PARSE
|
|
68
|
+
* @req REQ-FORMAT-VALIDATION
|
|
69
|
+
* @req REQ-MIXED-SUPPORT
|
|
70
|
+
*/
|
|
71
|
+
export declare function validateImplementsAnnotationHelper(deps: ImplementsDeps, context: any, comment: any, args: {
|
|
72
|
+
rawValue: string | null | undefined;
|
|
73
|
+
options: ResolvedAnnotationOptions;
|
|
74
|
+
}): void;
|
|
75
|
+
export {};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MIN_IMPLEMENTS_TOKENS = void 0;
|
|
4
|
+
exports.reportMissingImplementsValue = reportMissingImplementsValue;
|
|
5
|
+
exports.reportMissingImplementsReqIds = reportMissingImplementsReqIds;
|
|
6
|
+
exports.reportInvalidImplementsStoryPath = reportInvalidImplementsStoryPath;
|
|
7
|
+
exports.reportInvalidImplementsReqId = reportInvalidImplementsReqId;
|
|
8
|
+
exports.validateImplementsAnnotationHelper = validateImplementsAnnotationHelper;
|
|
9
|
+
const valid_annotation_utils_1 = require("./valid-annotation-utils");
|
|
10
|
+
/**
|
|
11
|
+
* Minimum number of tokens required for a valid @implements value:
|
|
12
|
+
* - one story path
|
|
13
|
+
* - at least one requirement ID
|
|
14
|
+
*
|
|
15
|
+
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
16
|
+
* @req REQ-IMPLEMENTS-PARSE
|
|
17
|
+
*/
|
|
18
|
+
exports.MIN_IMPLEMENTS_TOKENS = 2;
|
|
19
|
+
/**
|
|
20
|
+
* Report a completely missing @implements value (no story path or req IDs).
|
|
21
|
+
*
|
|
22
|
+
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
23
|
+
* @req REQ-FORMAT-VALIDATION
|
|
24
|
+
*/
|
|
25
|
+
function reportMissingImplementsValue(context, comment, options) {
|
|
26
|
+
const { storyExample, reqExample } = options;
|
|
27
|
+
context.report({
|
|
28
|
+
node: comment,
|
|
29
|
+
messageId: "invalidImplementsFormat",
|
|
30
|
+
data: {
|
|
31
|
+
details: `Missing story path and requirement IDs for @implements annotation. Expected a value like "${storyExample} ${reqExample}".`,
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Report a value that has only a story path and no requirement IDs.
|
|
37
|
+
*
|
|
38
|
+
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
39
|
+
* @req REQ-FORMAT-VALIDATION
|
|
40
|
+
*/
|
|
41
|
+
function reportMissingImplementsReqIds(context, comment, options) {
|
|
42
|
+
const { storyExample, reqExample } = options;
|
|
43
|
+
context.report({
|
|
44
|
+
node: comment,
|
|
45
|
+
messageId: "invalidImplementsFormat",
|
|
46
|
+
data: {
|
|
47
|
+
details: `Missing requirement IDs for @implements annotation. Expected a value like "${storyExample} ${reqExample}".`,
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Report an invalid story path inside @implements.
|
|
53
|
+
*
|
|
54
|
+
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
55
|
+
* @req REQ-FORMAT-VALIDATION
|
|
56
|
+
*/
|
|
57
|
+
function reportInvalidImplementsStoryPath(context, comment, storyPath, options) {
|
|
58
|
+
const { storyExample } = options;
|
|
59
|
+
context.report({
|
|
60
|
+
node: comment,
|
|
61
|
+
messageId: "invalidImplementsFormat",
|
|
62
|
+
data: {
|
|
63
|
+
details: `Invalid story path "${storyPath}" for @implements annotation. Expected a path like "${storyExample}".`,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Report an invalid requirement ID token inside @implements.
|
|
69
|
+
*
|
|
70
|
+
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
71
|
+
* @req REQ-FORMAT-VALIDATION
|
|
72
|
+
* @req REQ-MIXED-SUPPORT
|
|
73
|
+
*/
|
|
74
|
+
function reportInvalidImplementsReqId(context, comment, reqId, options) {
|
|
75
|
+
context.report({
|
|
76
|
+
node: comment,
|
|
77
|
+
messageId: "invalidReqFormat",
|
|
78
|
+
data: {
|
|
79
|
+
details: (0, valid_annotation_utils_1.buildReqErrorMessage)("invalid", reqId, options),
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Prepare and validate the token array for an @implements value.
|
|
85
|
+
*
|
|
86
|
+
* Returns { storyPath, reqIds } when tokens are present and structurally valid,
|
|
87
|
+
* or null when a missing-value condition has been reported.
|
|
88
|
+
*/
|
|
89
|
+
function parseImplementsTokens(deps, context, comment, rest) {
|
|
90
|
+
const { MIN_IMPLEMENTS_TOKENS, reportMissingImplementsValue, reportMissingImplementsReqIds, } = deps;
|
|
91
|
+
const { rawValue, options } = rest;
|
|
92
|
+
const value = rawValue?.trim() ?? "";
|
|
93
|
+
if (!value) {
|
|
94
|
+
reportMissingImplementsValue(context, comment, options);
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
const tokens = value.split(/\s+/);
|
|
98
|
+
if (tokens.length < MIN_IMPLEMENTS_TOKENS) {
|
|
99
|
+
reportMissingImplementsReqIds(context, comment, options);
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
const [storyPath, ...reqIds] = tokens;
|
|
103
|
+
return { storyPath, reqIds };
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Validate the parsed storyPath and reqIds against the provided patterns and
|
|
107
|
+
* delegate reporting of any invalid tokens.
|
|
108
|
+
*/
|
|
109
|
+
function validateImplementsTokens(deps, context, comment, rest) {
|
|
110
|
+
const { reportInvalidImplementsStoryPath, reportInvalidImplementsReqId } = deps;
|
|
111
|
+
const { parsed, options } = rest;
|
|
112
|
+
const { storyPath, reqIds } = parsed;
|
|
113
|
+
if (!options.storyPattern.test(storyPath)) {
|
|
114
|
+
reportInvalidImplementsStoryPath(context, comment, storyPath, options);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
for (const reqId of reqIds) {
|
|
118
|
+
if (!options.reqPattern.test(reqId)) {
|
|
119
|
+
reportInvalidImplementsReqId(context, comment, reqId, options);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Validate an @implements annotation value.
|
|
125
|
+
*
|
|
126
|
+
* This helper encapsulates the logic previously in valid-annotation-format.ts:
|
|
127
|
+
* - trims the raw value
|
|
128
|
+
* - splits into tokens
|
|
129
|
+
* - enforces MIN_IMPLEMENTS_TOKENS
|
|
130
|
+
* - validates the story path using options.storyPattern
|
|
131
|
+
* - validates each requirement ID using options.reqPattern
|
|
132
|
+
* - delegates reporting to the provided helpers
|
|
133
|
+
*
|
|
134
|
+
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
135
|
+
* @req REQ-IMPLEMENTS-PARSE
|
|
136
|
+
* @req REQ-FORMAT-VALIDATION
|
|
137
|
+
* @req REQ-MIXED-SUPPORT
|
|
138
|
+
*/
|
|
139
|
+
function validateImplementsAnnotationHelper(deps, context, comment, args) {
|
|
140
|
+
const { rawValue, options } = args;
|
|
141
|
+
const parsed = parseImplementsTokens(deps, context, comment, {
|
|
142
|
+
rawValue,
|
|
143
|
+
options,
|
|
144
|
+
});
|
|
145
|
+
if (!parsed) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
validateImplementsTokens(deps, context, comment, { parsed, options });
|
|
149
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper utilities for valid-story-reference rule.
|
|
3
|
+
*
|
|
4
|
+
* @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
5
|
+
* @req REQ-PROJECT-BOUNDARY - Ensure resolved candidate paths remain within the project root
|
|
6
|
+
* @req REQ-SECURITY-VALIDATION - Prevent path traversal and absolute path usage
|
|
7
|
+
*/
|
|
8
|
+
export interface ReportInvalidPathArgs {
|
|
9
|
+
storyPath: string;
|
|
10
|
+
commentNode: any;
|
|
11
|
+
context: any;
|
|
12
|
+
}
|
|
13
|
+
export type ReportInvalidPathFn = (args: ReportInvalidPathArgs) => void;
|
|
14
|
+
export interface HandleBoundaryOptions {
|
|
15
|
+
storyPath: string;
|
|
16
|
+
commentNode: any;
|
|
17
|
+
context: any;
|
|
18
|
+
cwd: string;
|
|
19
|
+
candidates: string[];
|
|
20
|
+
existenceResult: {
|
|
21
|
+
status: "exists" | "missing" | "fs-error" | null;
|
|
22
|
+
matchedPath?: string | null;
|
|
23
|
+
} | null;
|
|
24
|
+
reportInvalidPath: ReportInvalidPathFn;
|
|
25
|
+
}
|
|
26
|
+
export interface SecurityValidationOptions {
|
|
27
|
+
storyPath: string;
|
|
28
|
+
commentNode: any;
|
|
29
|
+
context: any;
|
|
30
|
+
cwd: string;
|
|
31
|
+
allowAbsolute: boolean;
|
|
32
|
+
reportInvalidPath: ReportInvalidPathFn;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Analyze candidate paths against the project boundary, returning whether any
|
|
36
|
+
* are within the project and whether any are outside.
|
|
37
|
+
*
|
|
38
|
+
* @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
39
|
+
* @req REQ-PROJECT-BOUNDARY - Validate files are within project boundaries
|
|
40
|
+
* @req REQ-CONFIGURABLE-PATHS - Respect configured storyDirectories while enforcing project boundaries
|
|
41
|
+
*/
|
|
42
|
+
export declare function analyzeCandidateBoundaries(candidates: string[], cwd: string): {
|
|
43
|
+
hasInProjectCandidate: boolean;
|
|
44
|
+
hasOutOfProjectCandidate: boolean;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Determine whether any candidate or matched path crosses the project
|
|
48
|
+
* boundary, and report an invalid path if so.
|
|
49
|
+
*
|
|
50
|
+
* This centralizes project-boundary invalidation logic used during
|
|
51
|
+
* existence checks, so the decision of *when* to call the invalid-path
|
|
52
|
+
* reporter is not duplicated.
|
|
53
|
+
*
|
|
54
|
+
* @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
55
|
+
* @req REQ-PROJECT-BOUNDARY - Ensure resolved candidate paths remain within the project root
|
|
56
|
+
* @req REQ-CONFIGURABLE-PATHS - Respect configured storyDirectories while enforcing project boundaries
|
|
57
|
+
*/
|
|
58
|
+
export declare function handleProjectBoundaryForExistence({ storyPath, commentNode, context, cwd, candidates, existenceResult, reportInvalidPath, }: HandleBoundaryOptions): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Perform security-related validations on the story path, including
|
|
61
|
+
* absolute-path usage and path traversal checks. Report invalid paths
|
|
62
|
+
* when necessary and indicate whether further processing should continue.
|
|
63
|
+
*
|
|
64
|
+
* @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
65
|
+
* @req REQ-SECURITY-VALIDATION - Prevent path traversal and absolute path usage
|
|
66
|
+
*/
|
|
67
|
+
export declare function performSecurityValidations({ storyPath, commentNode, context, cwd, allowAbsolute, reportInvalidPath, }: SecurityValidationOptions): boolean;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.analyzeCandidateBoundaries = analyzeCandidateBoundaries;
|
|
7
|
+
exports.handleProjectBoundaryForExistence = handleProjectBoundaryForExistence;
|
|
8
|
+
exports.performSecurityValidations = performSecurityValidations;
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const storyReferenceUtils_1 = require("../../utils/storyReferenceUtils");
|
|
11
|
+
/**
|
|
12
|
+
* Analyze candidate paths against the project boundary, returning whether any
|
|
13
|
+
* are within the project and whether any are outside.
|
|
14
|
+
*
|
|
15
|
+
* @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
16
|
+
* @req REQ-PROJECT-BOUNDARY - Validate files are within project boundaries
|
|
17
|
+
* @req REQ-CONFIGURABLE-PATHS - Respect configured storyDirectories while enforcing project boundaries
|
|
18
|
+
*/
|
|
19
|
+
function analyzeCandidateBoundaries(candidates, cwd) {
|
|
20
|
+
let hasInProjectCandidate = false;
|
|
21
|
+
let hasOutOfProjectCandidate = false;
|
|
22
|
+
for (const candidate of candidates) {
|
|
23
|
+
const boundary = (0, storyReferenceUtils_1.enforceProjectBoundary)(candidate, cwd);
|
|
24
|
+
if (boundary.isWithinProject) {
|
|
25
|
+
hasInProjectCandidate = true;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
hasOutOfProjectCandidate = true;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return { hasInProjectCandidate, hasOutOfProjectCandidate };
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Determine whether any candidate or matched path crosses the project
|
|
35
|
+
* boundary, and report an invalid path if so.
|
|
36
|
+
*
|
|
37
|
+
* This centralizes project-boundary invalidation logic used during
|
|
38
|
+
* existence checks, so the decision of *when* to call the invalid-path
|
|
39
|
+
* reporter is not duplicated.
|
|
40
|
+
*
|
|
41
|
+
* @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
42
|
+
* @req REQ-PROJECT-BOUNDARY - Ensure resolved candidate paths remain within the project root
|
|
43
|
+
* @req REQ-CONFIGURABLE-PATHS - Respect configured storyDirectories while enforcing project boundaries
|
|
44
|
+
*/
|
|
45
|
+
function handleProjectBoundaryForExistence({ storyPath, commentNode, context, cwd, candidates, existenceResult, reportInvalidPath, }) {
|
|
46
|
+
if (candidates.length > 0) {
|
|
47
|
+
const { hasInProjectCandidate, hasOutOfProjectCandidate } = analyzeCandidateBoundaries(candidates, cwd);
|
|
48
|
+
if (hasOutOfProjectCandidate && !hasInProjectCandidate) {
|
|
49
|
+
reportInvalidPath({ storyPath, commentNode, context });
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (existenceResult &&
|
|
54
|
+
existenceResult.status === "exists" &&
|
|
55
|
+
existenceResult.matchedPath) {
|
|
56
|
+
const boundary = (0, storyReferenceUtils_1.enforceProjectBoundary)(existenceResult.matchedPath, cwd);
|
|
57
|
+
if (!boundary.isWithinProject) {
|
|
58
|
+
reportInvalidPath({ storyPath, commentNode, context });
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Perform security-related validations on the story path, including
|
|
66
|
+
* absolute-path usage and path traversal checks. Report invalid paths
|
|
67
|
+
* when necessary and indicate whether further processing should continue.
|
|
68
|
+
*
|
|
69
|
+
* @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
70
|
+
* @req REQ-SECURITY-VALIDATION - Prevent path traversal and absolute path usage
|
|
71
|
+
*/
|
|
72
|
+
function performSecurityValidations({ storyPath, commentNode, context, cwd, allowAbsolute, reportInvalidPath, }) {
|
|
73
|
+
// Absolute path check
|
|
74
|
+
if (path_1.default.isAbsolute(storyPath)) {
|
|
75
|
+
if (!allowAbsolute) {
|
|
76
|
+
reportInvalidPath({ storyPath, commentNode, context });
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
// When absolute paths are allowed, we still enforce extension and
|
|
80
|
+
// project-boundary checks via the existence phase.
|
|
81
|
+
}
|
|
82
|
+
// Path traversal check
|
|
83
|
+
const containsTraversal = storyPath.includes("..") || /\\|\//.test(storyPath);
|
|
84
|
+
if (containsTraversal) {
|
|
85
|
+
const full = path_1.default.resolve(cwd, path_1.default.normalize(storyPath));
|
|
86
|
+
if (!full.startsWith(cwd + path_1.default.sep)) {
|
|
87
|
+
reportInvalidPath({ storyPath, commentNode, context });
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESLint rule implementation for preferring the consolidated `@implements`
|
|
3
|
+
* annotation over legacy combinations of `@story` and `@req` within JSDoc
|
|
4
|
+
* block comments. This module provides:
|
|
5
|
+
*
|
|
6
|
+
* - Detection of legacy `@story` + `@req` patterns.
|
|
7
|
+
* - Identification of multi-story comment blocks that are not safely
|
|
8
|
+
* auto-fixable.
|
|
9
|
+
* - A conservative auto-fix that rewrites simple, single-story patterns into
|
|
10
|
+
* a single `@implements` annotation while preserving formatting.
|
|
11
|
+
*
|
|
12
|
+
* The rule is intended as an **optional migration aid** to help projects
|
|
13
|
+
* gradually move to the newer `@implements` format without breaking existing
|
|
14
|
+
* traceability links.
|
|
15
|
+
*
|
|
16
|
+
* @story docs/stories/010.3-DEV-MIGRATE-TO-IMPLEMENTS.story.md
|
|
17
|
+
* @req REQ-OPTIONAL-WARNING - Emit configurable recommendation diagnostics for legacy @story/@req usage
|
|
18
|
+
* @req REQ-MULTI-STORY-DETECT - Detect multi-story patterns that cannot be auto-fixed
|
|
19
|
+
* @req REQ-SINGLE-STORY-FIX - Restrict auto-fix to single-story, single-path cases
|
|
20
|
+
* @req REQ-PRESERVE-FORMAT - Preserve original JSDoc indentation and prefix formatting
|
|
21
|
+
* @req REQ-VALID-OUTPUT - Avoid emitting auto-fixes for complex or ambiguous patterns
|
|
22
|
+
* @req REQ-BACKWARD-COMP-VALIDATION - Keep legacy @story/@req annotations valid when the rule is disabled
|
|
23
|
+
* @req REQ-AUTO-FIX - Provide safe, opt-in auto-fix for simple legacy patterns
|
|
24
|
+
*/
|
|
25
|
+
import type { Rule } from "eslint";
|
|
26
|
+
/**
|
|
27
|
+
* ESLint rule: prefer-implements-annotation
|
|
28
|
+
*
|
|
29
|
+
* Recommend migrating from legacy `@story` + `@req` annotations to the
|
|
30
|
+
* newer `@implements` format. This rule is **disabled by default** and
|
|
31
|
+
* is intended as an optional, opt-in migration aid.
|
|
32
|
+
*
|
|
33
|
+
* @story docs/stories/010.3-DEV-MIGRATE-TO-IMPLEMENTS.story.md
|
|
34
|
+
* @req REQ-OPTIONAL-WARNING - Emit configurable recommendation diagnostics for legacy @story/@req usage
|
|
35
|
+
* @req REQ-MULTI-STORY-DETECT - Detect multi-story patterns that cannot be auto-fixed
|
|
36
|
+
* @req REQ-BACKWARD-COMP-VALIDATION - Keep legacy @story/@req annotations valid when the rule is disabled
|
|
37
|
+
*/
|
|
38
|
+
declare const preferImplementsAnnotationRule: Rule.RuleModule;
|
|
39
|
+
export default preferImplementsAnnotationRule;
|