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.
Files changed (48) hide show
  1. package/README.md +39 -1
  2. package/lib/src/index.d.ts +30 -27
  3. package/lib/src/index.js +51 -31
  4. package/lib/src/maintenance/cli.d.ts +12 -0
  5. package/lib/src/maintenance/cli.js +279 -0
  6. package/lib/src/maintenance/detect.js +27 -12
  7. package/lib/src/maintenance/update.js +42 -34
  8. package/lib/src/maintenance/utils.js +30 -30
  9. package/lib/src/rules/helpers/require-story-io.js +51 -15
  10. package/lib/src/rules/helpers/valid-annotation-format-internal.d.ts +30 -0
  11. package/lib/src/rules/helpers/valid-annotation-format-internal.js +36 -0
  12. package/lib/src/rules/helpers/valid-annotation-options.d.ts +118 -0
  13. package/lib/src/rules/helpers/valid-annotation-options.js +167 -0
  14. package/lib/src/rules/helpers/valid-annotation-utils.d.ts +68 -0
  15. package/lib/src/rules/helpers/valid-annotation-utils.js +103 -0
  16. package/lib/src/rules/helpers/valid-implements-utils.d.ts +75 -0
  17. package/lib/src/rules/helpers/valid-implements-utils.js +149 -0
  18. package/lib/src/rules/helpers/valid-story-reference-helpers.d.ts +67 -0
  19. package/lib/src/rules/helpers/valid-story-reference-helpers.js +92 -0
  20. package/lib/src/rules/prefer-implements-annotation.d.ts +39 -0
  21. package/lib/src/rules/prefer-implements-annotation.js +276 -0
  22. package/lib/src/rules/valid-annotation-format.js +255 -208
  23. package/lib/src/rules/valid-req-reference.js +210 -29
  24. package/lib/src/rules/valid-story-reference.d.ts +7 -0
  25. package/lib/src/rules/valid-story-reference.js +38 -80
  26. package/lib/src/utils/annotation-checker.js +2 -145
  27. package/lib/src/utils/branch-annotation-helpers.js +12 -3
  28. package/lib/src/utils/reqAnnotationDetection.d.ts +6 -0
  29. package/lib/src/utils/reqAnnotationDetection.js +152 -0
  30. package/lib/tests/maintenance/cli.test.d.ts +1 -0
  31. package/lib/tests/maintenance/cli.test.js +172 -0
  32. package/lib/tests/plugin-default-export-and-configs.test.js +3 -0
  33. package/lib/tests/rules/prefer-implements-annotation.test.d.ts +1 -0
  34. package/lib/tests/rules/prefer-implements-annotation.test.js +84 -0
  35. package/lib/tests/rules/require-branch-annotation.test.js +3 -2
  36. package/lib/tests/rules/require-req-annotation.test.js +57 -68
  37. package/lib/tests/rules/require-story-annotation.test.js +13 -28
  38. package/lib/tests/rules/require-story-core-edgecases.test.js +3 -58
  39. package/lib/tests/rules/require-story-core.autofix.test.js +5 -41
  40. package/lib/tests/rules/valid-annotation-format.test.js +395 -40
  41. package/lib/tests/rules/valid-req-reference.test.js +34 -0
  42. package/lib/tests/utils/annotation-checker.test.d.ts +23 -0
  43. package/lib/tests/utils/annotation-checker.test.js +24 -17
  44. package/lib/tests/utils/require-story-core-test-helpers.d.ts +10 -0
  45. package/lib/tests/utils/require-story-core-test-helpers.js +75 -0
  46. package/lib/tests/utils/ts-language-options.d.ts +22 -0
  47. package/lib/tests/utils/ts-language-options.js +27 -0
  48. 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;