eslint-plugin-traceability 1.6.5 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +38 -1
  2. package/lib/src/index.d.ts +28 -25
  3. package/lib/src/index.js +49 -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-options.d.ts +118 -0
  11. package/lib/src/rules/helpers/valid-annotation-options.js +167 -0
  12. package/lib/src/rules/helpers/valid-annotation-utils.d.ts +68 -0
  13. package/lib/src/rules/helpers/valid-annotation-utils.js +103 -0
  14. package/lib/src/rules/helpers/valid-story-reference-helpers.d.ts +67 -0
  15. package/lib/src/rules/helpers/valid-story-reference-helpers.js +92 -0
  16. package/lib/src/rules/valid-annotation-format.js +168 -180
  17. package/lib/src/rules/valid-req-reference.js +139 -29
  18. package/lib/src/rules/valid-story-reference.d.ts +7 -0
  19. package/lib/src/rules/valid-story-reference.js +38 -80
  20. package/lib/src/utils/annotation-checker.js +2 -145
  21. package/lib/src/utils/branch-annotation-helpers.js +12 -3
  22. package/lib/src/utils/reqAnnotationDetection.d.ts +6 -0
  23. package/lib/src/utils/reqAnnotationDetection.js +152 -0
  24. package/lib/tests/maintenance/cli.test.d.ts +1 -0
  25. package/lib/tests/maintenance/cli.test.js +172 -0
  26. package/lib/tests/rules/require-branch-annotation.test.js +3 -2
  27. package/lib/tests/rules/require-req-annotation.test.js +57 -68
  28. package/lib/tests/rules/require-story-annotation.test.js +13 -28
  29. package/lib/tests/rules/require-story-core-edgecases.test.js +3 -58
  30. package/lib/tests/rules/require-story-core.autofix.test.js +5 -41
  31. package/lib/tests/rules/valid-annotation-format.test.js +328 -51
  32. package/lib/tests/utils/annotation-checker.test.d.ts +23 -0
  33. package/lib/tests/utils/annotation-checker.test.js +24 -17
  34. package/lib/tests/utils/require-story-core-test-helpers.d.ts +10 -0
  35. package/lib/tests/utils/require-story-core-test-helpers.js +75 -0
  36. package/lib/tests/utils/ts-language-options.d.ts +22 -0
  37. package/lib/tests/utils/ts-language-options.js +27 -0
  38. package/package.json +12 -3
@@ -56,45 +56,45 @@ function getAllFiles(dir) {
56
56
  if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
57
57
  return fileList;
58
58
  }
59
+ traverseDirectory(dir, fileList);
60
+ return fileList;
61
+ }
62
+ /**
63
+ * Recursively traverse a directory and collect file paths.
64
+ * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
65
+ * @req REQ-MAINT-UTILS-TRAVERSE - Helper traversal function used by getAllFiles
66
+ */
67
+ function traverseDirectory(currentDir, fileList) {
68
+ const entries = fs.readdirSync(currentDir);
59
69
  /**
60
- * Recursively traverse a directory and collect file paths.
70
+ * Iterate over directory entries using a for-of loop.
61
71
  * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
62
- * @req REQ-MAINT-UTILS-TRAVERSE - Helper traversal function used by getAllFiles
72
+ * @req REQ-MAINT-UTILS-TRAVERSE-FOROF - Traceability for ForOfStatement branch handling entries
63
73
  */
64
- function traverse(currentDir) {
65
- const entries = fs.readdirSync(currentDir);
74
+ for (const entry of entries) {
75
+ const fullPath = path.join(currentDir, entry);
76
+ const stat = fs.statSync(fullPath);
66
77
  /**
67
- * Iterate over directory entries using a for-of loop.
78
+ * Recurse into directories to continue traversal.
68
79
  * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
69
- * @req REQ-MAINT-UTILS-TRAVERSE-FOROF - Traceability for ForOfStatement branch handling entries
80
+ * @req REQ-MAINT-UTILS-TRAVERSE-DIR - Handle directory entries during traversal
70
81
  */
71
- for (const entry of entries) {
72
- const fullPath = path.join(currentDir, entry);
73
- const stat = fs.statSync(fullPath);
82
+ if (stat.isDirectory()) {
83
+ traverseDirectory(fullPath, fileList);
74
84
  /**
75
- * Recurse into directories to continue traversal.
85
+ * Collect regular file entries during traversal.
76
86
  * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
77
- * @req REQ-MAINT-UTILS-TRAVERSE-DIR - Handle directory entries during traversal
87
+ * @req REQ-MAINT-UTILS-TRAVERSE-FILE - Handle file entries during traversal
78
88
  */
79
- if (stat.isDirectory()) {
80
- traverse(fullPath);
81
- /**
82
- * Collect regular file entries during traversal.
83
- * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
84
- * @req REQ-MAINT-UTILS-TRAVERSE-FILE - Handle file entries during traversal
85
- */
86
- }
87
- /**
88
- * Skip non-file entries encountered during traversal.
89
- * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
90
- * @req REQ-MAINT-UTILS-TRAVERSE-SKIP-NONFILE - Traceability for skipping non-file entries
91
- */
92
- if (!stat.isFile()) {
93
- continue;
94
- }
95
- fileList.push(fullPath);
96
89
  }
90
+ /**
91
+ * Skip non-file entries encountered during traversal.
92
+ * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
93
+ * @req REQ-MAINT-UTILS-TRAVERSE-SKIP-NONFILE - Traceability for skipping non-file entries
94
+ */
95
+ if (!stat.isFile()) {
96
+ continue;
97
+ }
98
+ fileList.push(fullPath);
97
99
  }
98
- traverse(dir);
99
- return fileList;
100
100
  }
@@ -22,23 +22,41 @@ exports.LOOKBACK_LINES = 4;
22
22
  */
23
23
  exports.FALLBACK_WINDOW = 800;
24
24
  /**
25
- * Inspect a fixed number of physical source lines before the node for @story text
25
+ * Shared predicate to determine if a given comment node contains an @story marker.
26
26
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
27
- * @req REQ-ANNOTATION-REQUIRED - Extract line-based detection into helper
27
+ * @req REQ-ANNOTATION-REQUIRED - Centralize @story detection logic for comment value inspection
28
28
  */
29
- function linesBeforeHasStory(sourceCode, node, lookback = exports.LOOKBACK_LINES) {
29
+ function commentContainsStory(comment) {
30
+ return typeof comment?.value === "string" && comment.value.includes("@story");
31
+ }
32
+ /**
33
+ * Safely extract the physical source lines array from sourceCode for scanning.
34
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
35
+ * @req REQ-ANNOTATION-REQUIRED - Centralize guards for safe access to source lines
36
+ */
37
+ function getSourceLines(sourceCode) {
30
38
  const lines = sourceCode && sourceCode.lines;
31
- const startLine = node && node.loc && typeof node.loc.start?.line === "number"
32
- ? node.loc.start.line
33
- : null;
34
- // Guard against missing or non-array source lines or an invalid start line before scanning.
35
- // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
36
- // @req REQ-ANNOTATION-REQUIRED - Fail gracefully when source lines or locations are unavailable
37
- if (!Array.isArray(lines) || typeof startLine !== "number") {
38
- return false;
39
+ return Array.isArray(lines) ? lines : null;
40
+ }
41
+ /**
42
+ * Safely resolve the starting line number of a node for use in lookback scans.
43
+ * Returns null when the node does not provide a valid numeric start line.
44
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
45
+ * @req REQ-ANNOTATION-REQUIRED - Centralize guards for safe access to node location metadata
46
+ */
47
+ function getNodeStartLine(node) {
48
+ if (!node || !node.loc) {
49
+ return null;
39
50
  }
40
- const from = Math.max(0, startLine - 1 - lookback);
41
- const to = Math.max(0, startLine - 1);
51
+ const line = node.loc.start?.line;
52
+ return typeof line === "number" ? line : null;
53
+ }
54
+ /**
55
+ * Generic helper to scan a range of physical source lines for the presence of an @story marker.
56
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
57
+ * @req REQ-ANNOTATION-REQUIRED - Reuse line scanning logic for story annotations across helpers
58
+ */
59
+ function scanLinesForMarker(lines, from, to) {
42
60
  // Walk each physical line in the configured lookback window to search for an inline @story marker.
43
61
  // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
44
62
  // @req REQ-ANNOTATION-REQUIRED - Scan preceding lines for existing story annotations
@@ -53,6 +71,24 @@ function linesBeforeHasStory(sourceCode, node, lookback = exports.LOOKBACK_LINES
53
71
  }
54
72
  return false;
55
73
  }
74
+ /**
75
+ * Inspect a fixed number of physical source lines before the node for @story text
76
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
77
+ * @req REQ-ANNOTATION-REQUIRED - Extract line-based detection into helper
78
+ */
79
+ function linesBeforeHasStory(sourceCode, node, lookback = exports.LOOKBACK_LINES) {
80
+ const lines = getSourceLines(sourceCode);
81
+ const startLine = getNodeStartLine(node);
82
+ // Guard against missing or non-array source lines or an invalid start line before scanning.
83
+ // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
84
+ // @req REQ-ANNOTATION-REQUIRED - Fail gracefully when source lines or locations are unavailable
85
+ if (!lines || typeof startLine !== "number") {
86
+ return false;
87
+ }
88
+ const from = Math.max(0, startLine - 1 - lookback);
89
+ const to = Math.max(0, startLine - 1);
90
+ return scanLinesForMarker(lines, from, to);
91
+ }
56
92
  /**
57
93
  * Walk parent chain and check comments before each parent and their leadingComments
58
94
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
@@ -70,7 +106,7 @@ function parentChainHasStory(sourceCode, node) {
70
106
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
71
107
  * @req REQ-ANNOTATION-REQUIRED - Detect @story in parent comments via value inspection
72
108
  */
73
- (c) => typeof c.value === "string" && c.value.includes("@story"))) {
109
+ (c) => commentContainsStory(c))) {
74
110
  return true;
75
111
  }
76
112
  const pLeading = p.leadingComments || [];
@@ -80,7 +116,7 @@ function parentChainHasStory(sourceCode, node) {
80
116
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
81
117
  * @req REQ-ANNOTATION-REQUIRED - Detect @story in parent leadingComments via value inspection
82
118
  */
83
- (c) => typeof c.value === "string" && c.value.includes("@story"))) {
119
+ (c) => commentContainsStory(c))) {
84
120
  return true;
85
121
  }
86
122
  p = p.parent;
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Shared option handling for the valid-annotation-format rule.
3
+ *
4
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
5
+ * @req REQ-PATTERN-CONFIG - Support configuration of custom story path and requirement ID patterns
6
+ * @req REQ-REGEX-VALIDATION - Validate that configured patterns are valid regular expressions
7
+ * @req REQ-BACKWARD-COMPAT - Maintain current behavior when no custom patterns configured
8
+ * @req REQ-EXAMPLE-MESSAGES - Support optional example strings in error messages
9
+ * @req REQ-SCHEMA-VALIDATION - Use JSON Schema to validate configuration options
10
+ */
11
+ export interface AnnotationRuleOptions {
12
+ story?: {
13
+ /**
14
+ * Regex (string) the collapsed story path must match.
15
+ * Default: /^docs\/stories\/[0-9]+\.[0-9]+-DEV-[\w-]+\.story\.md$/
16
+ */
17
+ pattern?: string;
18
+ /**
19
+ * Human-readable example path used in error messages.
20
+ * Default: "docs/stories/005.0-DEV-EXAMPLE.story.md"
21
+ */
22
+ example?: string;
23
+ };
24
+ req?: {
25
+ /**
26
+ * Regex (string) the collapsed requirement ID must match.
27
+ * Default: /^REQ-[A-Z0-9-]+$/
28
+ */
29
+ pattern?: string;
30
+ /**
31
+ * Human-readable example requirement ID used in error messages.
32
+ * Default: "REQ-EXAMPLE"
33
+ */
34
+ example?: string;
35
+ };
36
+ /**
37
+ * Shorthand for story.pattern.
38
+ * Regex (string) the collapsed story path must match.
39
+ */
40
+ storyPathPattern?: string;
41
+ /**
42
+ * Shorthand for story.example.
43
+ * Human-readable example story path used in error messages.
44
+ */
45
+ storyPathExample?: string;
46
+ /**
47
+ * Shorthand for req.pattern.
48
+ * Regex (string) the collapsed requirement ID must match.
49
+ */
50
+ requirementIdPattern?: string;
51
+ /**
52
+ * Shorthand for req.example.
53
+ * Human-readable example requirement ID used in error messages.
54
+ */
55
+ requirementIdExample?: string;
56
+ }
57
+ /**
58
+ * Resolved, runtime-ready options for the rule.
59
+ */
60
+ export interface ResolvedAnnotationOptions {
61
+ storyPattern: RegExp;
62
+ storyExample: string;
63
+ reqPattern: RegExp;
64
+ reqExample: string;
65
+ }
66
+ export declare function getDefaultReqExample(): string;
67
+ export declare function getResolvedDefaults(): ResolvedAnnotationOptions;
68
+ export declare function getOptionErrors(): string[];
69
+ /**
70
+ * Resolve user options into concrete, validated configuration.
71
+ * Falls back to existing defaults when options are not provided or invalid.
72
+ */
73
+ export declare function resolveOptions(rawOptions: unknown[]): ResolvedAnnotationOptions;
74
+ /**
75
+ * Build the JSON schema for rule options.
76
+ */
77
+ export declare function getRuleSchema(): {
78
+ type: string;
79
+ properties: {
80
+ story: {
81
+ type: string;
82
+ properties: {
83
+ pattern: {
84
+ type: string;
85
+ };
86
+ example: {
87
+ type: string;
88
+ };
89
+ };
90
+ additionalProperties: boolean;
91
+ };
92
+ req: {
93
+ type: string;
94
+ properties: {
95
+ pattern: {
96
+ type: string;
97
+ };
98
+ example: {
99
+ type: string;
100
+ };
101
+ };
102
+ additionalProperties: boolean;
103
+ };
104
+ storyPathPattern: {
105
+ type: string;
106
+ };
107
+ storyPathExample: {
108
+ type: string;
109
+ };
110
+ requirementIdPattern: {
111
+ type: string;
112
+ };
113
+ requirementIdExample: {
114
+ type: string;
115
+ };
116
+ };
117
+ additionalProperties: boolean;
118
+ }[];
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getDefaultReqExample = getDefaultReqExample;
4
+ exports.getResolvedDefaults = getResolvedDefaults;
5
+ exports.getOptionErrors = getOptionErrors;
6
+ exports.resolveOptions = resolveOptions;
7
+ exports.getRuleSchema = getRuleSchema;
8
+ function getDefaultStoryPattern() {
9
+ return /^docs\/stories\/[0-9]+\.[0-9]+-DEV-[\w-]+\.story\.md$/;
10
+ }
11
+ function getDefaultStoryExample() {
12
+ return "docs/stories/005.0-DEV-EXAMPLE.story.md";
13
+ }
14
+ function getDefaultReqPattern() {
15
+ return /^REQ-[A-Z0-9-]+$/;
16
+ }
17
+ function getDefaultReqExample() {
18
+ return "REQ-EXAMPLE";
19
+ }
20
+ /**
21
+ * Global cache of the last resolved options for helpers that need access
22
+ * without having options explicitly passed in.
23
+ */
24
+ let resolvedDefaults = {
25
+ storyPattern: getDefaultStoryPattern(),
26
+ storyExample: getDefaultStoryExample(),
27
+ reqPattern: getDefaultReqPattern(),
28
+ reqExample: getDefaultReqExample(),
29
+ };
30
+ /**
31
+ * Collected configuration errors encountered while resolving options.
32
+ */
33
+ let optionErrors = [];
34
+ function getResolvedDefaults() {
35
+ return resolvedDefaults;
36
+ }
37
+ function getOptionErrors() {
38
+ return optionErrors;
39
+ }
40
+ /**
41
+ * Build a stable, engine-independent configuration error message
42
+ * for invalid regex options.
43
+ */
44
+ function buildInvalidRegexError(field, pattern) {
45
+ return `Invalid regular expression for option "${field}": "${pattern}"`;
46
+ }
47
+ /**
48
+ * Normalize raw rule options into a single AnnotationRuleOptions object.
49
+ *
50
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
51
+ * @req REQ-PATTERN-CONFIG
52
+ * @req REQ-BACKWARD-COMPAT
53
+ */
54
+ function normalizeUserOptions(rawOptions) {
55
+ if (!rawOptions || rawOptions.length === 0) {
56
+ return undefined;
57
+ }
58
+ const first = rawOptions[0];
59
+ if (!first || typeof first !== "object") {
60
+ return undefined;
61
+ }
62
+ return first;
63
+ }
64
+ /**
65
+ * Resolve a user-configured regex pattern, handling both nested and flat
66
+ * configuration shapes and accumulating validation errors.
67
+ *
68
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
69
+ * @req REQ-PATTERN-CONFIG
70
+ * @req REQ-REGEX-VALIDATION
71
+ * @req REQ-BACKWARD-COMPAT
72
+ */
73
+ // eslint-disable-next-line max-params -- Small, centralized helper; keeping parameters explicit is clearer than introducing an options object here.
74
+ function resolvePattern(nestedPattern, nestedFieldName, flatPattern, flatFieldName, defaultPattern) {
75
+ const effective = typeof nestedPattern === "string"
76
+ ? { value: nestedPattern, field: nestedFieldName }
77
+ : typeof flatPattern === "string"
78
+ ? { value: flatPattern, field: flatFieldName }
79
+ : null;
80
+ if (!effective) {
81
+ return defaultPattern;
82
+ }
83
+ try {
84
+ return new RegExp(effective.value);
85
+ }
86
+ catch {
87
+ optionErrors.push(buildInvalidRegexError(effective.field, effective.value));
88
+ return defaultPattern;
89
+ }
90
+ }
91
+ /**
92
+ * Resolve an example string, preferring nested over flat configuration,
93
+ * and falling back to the provided default when necessary.
94
+ *
95
+ * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
96
+ * @req REQ-EXAMPLE-MESSAGES
97
+ * @req REQ-BACKWARD-COMPAT
98
+ */
99
+ function resolveExample(nestedExample, flatExample, defaultExample) {
100
+ if (typeof nestedExample === "string" && nestedExample.trim()) {
101
+ return nestedExample;
102
+ }
103
+ if (typeof flatExample === "string" && flatExample.trim()) {
104
+ return flatExample;
105
+ }
106
+ return defaultExample;
107
+ }
108
+ /**
109
+ * Resolve user options into concrete, validated configuration.
110
+ * Falls back to existing defaults when options are not provided or invalid.
111
+ */
112
+ function resolveOptions(rawOptions) {
113
+ optionErrors = [];
114
+ const user = normalizeUserOptions(rawOptions);
115
+ const nestedStoryPattern = user?.story?.pattern;
116
+ const flatStoryPattern = user?.storyPathPattern;
117
+ const nestedStoryExample = user?.story?.example;
118
+ const flatStoryExample = user?.storyPathExample;
119
+ const nestedReqPattern = user?.req?.pattern;
120
+ const flatReqPattern = user?.requirementIdPattern;
121
+ const nestedReqExample = user?.req?.example;
122
+ const flatReqExample = user?.requirementIdExample;
123
+ const storyPattern = resolvePattern(nestedStoryPattern, "story.pattern", flatStoryPattern, "storyPathPattern", getDefaultStoryPattern());
124
+ const reqPattern = resolvePattern(nestedReqPattern, "req.pattern", flatReqPattern, "requirementIdPattern", getDefaultReqPattern());
125
+ const storyExample = resolveExample(nestedStoryExample, flatStoryExample, getDefaultStoryExample());
126
+ const reqExample = resolveExample(nestedReqExample, flatReqExample, getDefaultReqExample());
127
+ resolvedDefaults = {
128
+ storyPattern,
129
+ storyExample,
130
+ reqPattern,
131
+ reqExample,
132
+ };
133
+ return resolvedDefaults;
134
+ }
135
+ /**
136
+ * Build the JSON schema for rule options.
137
+ */
138
+ function getRuleSchema() {
139
+ return [
140
+ {
141
+ type: "object",
142
+ properties: {
143
+ story: {
144
+ type: "object",
145
+ properties: {
146
+ pattern: { type: "string" },
147
+ example: { type: "string" },
148
+ },
149
+ additionalProperties: false,
150
+ },
151
+ req: {
152
+ type: "object",
153
+ properties: {
154
+ pattern: { type: "string" },
155
+ example: { type: "string" },
156
+ },
157
+ additionalProperties: false,
158
+ },
159
+ storyPathPattern: { type: "string" },
160
+ storyPathExample: { type: "string" },
161
+ requirementIdPattern: { type: "string" },
162
+ requirementIdExample: { type: "string" },
163
+ },
164
+ additionalProperties: false,
165
+ },
166
+ ];
167
+ }
@@ -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
+ }