eslint-plugin-traceability 1.7.1 → 1.8.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 (39) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/README.md +76 -37
  3. package/SECURITY.md +132 -0
  4. package/lib/src/index.d.ts +6 -35
  5. package/lib/src/index.js +8 -5
  6. package/lib/src/maintenance/batch.d.ts +5 -0
  7. package/lib/src/maintenance/batch.js +5 -0
  8. package/lib/src/maintenance/cli.js +34 -212
  9. package/lib/src/maintenance/commands.d.ts +32 -0
  10. package/lib/src/maintenance/commands.js +139 -0
  11. package/lib/src/maintenance/detect.d.ts +2 -0
  12. package/lib/src/maintenance/detect.js +4 -0
  13. package/lib/src/maintenance/flags.d.ts +99 -0
  14. package/lib/src/maintenance/flags.js +121 -0
  15. package/lib/src/maintenance/report.d.ts +2 -0
  16. package/lib/src/maintenance/report.js +2 -0
  17. package/lib/src/maintenance/update.d.ts +4 -0
  18. package/lib/src/maintenance/update.js +4 -0
  19. package/lib/src/rules/helpers/require-story-io.d.ts +3 -0
  20. package/lib/src/rules/helpers/require-story-io.js +20 -6
  21. package/lib/src/rules/helpers/valid-annotation-options.js +15 -4
  22. package/lib/src/rules/helpers/valid-annotation-utils.js +5 -0
  23. package/lib/src/rules/helpers/valid-story-reference-helpers.d.ts +3 -4
  24. package/lib/src/utils/reqAnnotationDetection.d.ts +4 -1
  25. package/lib/src/utils/reqAnnotationDetection.js +43 -15
  26. package/lib/tests/config/flat-config-presets-integration.test.d.ts +1 -0
  27. package/lib/tests/config/flat-config-presets-integration.test.js +75 -0
  28. package/lib/tests/maintenance/cli.test.js +89 -0
  29. package/lib/tests/plugin-default-export-and-configs.test.js +0 -2
  30. package/lib/tests/rules/prefer-implements-annotation.test.js +28 -0
  31. package/lib/tests/rules/require-req-annotation.test.js +8 -1
  32. package/lib/tests/rules/require-story-annotation.test.js +9 -4
  33. package/lib/tests/utils/ts-language-options.d.ts +1 -7
  34. package/lib/tests/utils/ts-language-options.js +8 -5
  35. package/package.json +11 -7
  36. package/user-docs/api-reference.md +527 -0
  37. package/user-docs/eslint-9-setup-guide.md +722 -0
  38. package/user-docs/examples.md +74 -0
  39. package/user-docs/migration-guide.md +174 -0
@@ -0,0 +1,121 @@
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.parseCliInput = parseCliInput;
7
+ exports.normalizeCliArgs = normalizeCliArgs;
8
+ exports.parseFlags = parseFlags;
9
+ /**
10
+ * Flag parsing and normalization logic for the traceability-maint CLI.
11
+ *
12
+ * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
13
+ * @req REQ-MAINT-SAFE - Provide predictable, minimal argument parsing
14
+ */
15
+ const path_1 = __importDefault(require("path"));
16
+ /**
17
+ * Parse raw Node.js argv into a structured CLI input for the maintenance tools.
18
+ *
19
+ * This performs only minimal, predictable splitting to support higher-level
20
+ * flag parsing in a safe and testable way.
21
+ *
22
+ * @param argv - Raw argv array, usually process.argv.
23
+ * @returns ParsedCliInput with node, script, subcommand, and remaining args.
24
+ *
25
+ * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
26
+ * @req REQ-MAINT-SAFE - Provide predictable, minimal argument parsing
27
+ */
28
+ function parseCliInput(argv) {
29
+ const [node = "", script = "", ...rest] = argv;
30
+ const [subcommand, ...args] = rest;
31
+ return { argv, node, script, subcommand, args };
32
+ }
33
+ /**
34
+ * Normalize raw argv into a subcommand-centric view for flag parsing.
35
+ *
36
+ * Uses parseCliInput to safely separate Node/V8 internals from the
37
+ * subcommand and its arguments, then exposes only the pieces relevant
38
+ * to subcommand-specific flag parsing.
39
+ *
40
+ * @param rawArgv - Raw argv array, usually process.argv.
41
+ * @returns NormalizedCliArgs with subcommand and remaining args.
42
+ *
43
+ * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
44
+ * @req REQ-MAINT-SAFE - Provide predictable, minimal argument parsing
45
+ */
46
+ function normalizeCliArgs(rawArgv) {
47
+ const { subcommand, args } = parseCliInput(rawArgv);
48
+ return { subcommand, args };
49
+ }
50
+ /**
51
+ * Initialize default flags for the maintenance CLI.
52
+ *
53
+ * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
54
+ * @req REQ-MAINT-SAFE - Provide predictable, minimal argument parsing
55
+ */
56
+ function createDefaultFlags() {
57
+ return {
58
+ root: process.cwd(),
59
+ json: false,
60
+ };
61
+ }
62
+ /**
63
+ * Handle a single CLI argument and update the flags accordingly.
64
+ *
65
+ * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
66
+ * @req REQ-MAINT-SAFE - Provide predictable, minimal argument parsing
67
+ */
68
+ function applyFlag(flags, args, index) {
69
+ const arg = args[index];
70
+ if (arg === "--root" && typeof args[index + 1] === "string") {
71
+ flags.root = path_1.default.resolve(args[index + 1]);
72
+ return index + 1;
73
+ }
74
+ if (arg === "--json") {
75
+ flags.json = true;
76
+ return index;
77
+ }
78
+ if (arg === "--format" && typeof args[index + 1] === "string") {
79
+ const value = args[index + 1];
80
+ if (value === "text" || value === "json") {
81
+ flags.format = value;
82
+ }
83
+ else {
84
+ throw new Error(`Invalid format: ${value}. Expected 'text' or 'json'.`);
85
+ }
86
+ return index + 1;
87
+ }
88
+ if (arg === "--from" && typeof args[index + 1] === "string") {
89
+ flags.from = args[index + 1];
90
+ return index + 1;
91
+ }
92
+ if (arg === "--to" && typeof args[index + 1] === "string") {
93
+ flags.to = args[index + 1];
94
+ return index + 1;
95
+ }
96
+ if (arg === "--dry-run") {
97
+ flags.dryRun = true;
98
+ return index;
99
+ }
100
+ return index;
101
+ }
102
+ /**
103
+ * Basic flag parser for maintenance CLI subcommands.
104
+ *
105
+ * Consumes already-normalized CLI arguments (subcommand and its raw args)
106
+ * and produces a ParsedFlags structure with minimal, predictable behavior.
107
+ *
108
+ * @param normalized - Normalized CLI arguments for a maintenance subcommand.
109
+ * @returns ParsedFlags with defaults applied and any recognized flags set.
110
+ *
111
+ * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
112
+ * @req REQ-MAINT-SAFE - Provide predictable, minimal argument parsing
113
+ */
114
+ function parseFlags(normalized) {
115
+ const flags = createDefaultFlags();
116
+ const { args } = normalized;
117
+ for (let i = 0; i < args.length; i += 1) {
118
+ i = applyFlag(flags, args, i);
119
+ }
120
+ return flags;
121
+ }
@@ -3,5 +3,7 @@
3
3
  * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
4
4
  * @req REQ-MAINT-REPORT - Generate maintenance report
5
5
  * @req REQ-MAINT-SAFE - Ensure operations are safe and reversible
6
+ * @param codebasePath The workspace root to scan for stale maintenance annotations.
7
+ * @returns An empty string when no stale annotations are found, or a newline-separated list of stale `@story` paths.
6
8
  */
7
9
  export declare function generateMaintenanceReport(codebasePath: string): string;
@@ -7,6 +7,8 @@ const detect_1 = require("./detect");
7
7
  * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
8
8
  * @req REQ-MAINT-REPORT - Generate maintenance report
9
9
  * @req REQ-MAINT-SAFE - Ensure operations are safe and reversible
10
+ * @param codebasePath The workspace root to scan for stale maintenance annotations.
11
+ * @returns An empty string when no stale annotations are found, or a newline-separated list of stale `@story` paths.
10
12
  */
11
13
  function generateMaintenanceReport(codebasePath) {
12
14
  const staleAnnotations = (0, detect_1.detectStaleAnnotations)(codebasePath);
@@ -2,5 +2,9 @@
2
2
  * Update annotation references when story files are moved or renamed
3
3
  * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
4
4
  * @req REQ-MAINT-UPDATE - Update annotation references
5
+ * @param codebasePath Absolute or workspace-root path whose files will be updated in-place.
6
+ * @param oldPath The original @story path to search for in annotation comments.
7
+ * @param newPath The replacement @story path that will replace occurrences of oldPath.
8
+ * @returns The number of @story annotations that were updated across the codebase.
5
9
  */
6
10
  export declare function updateAnnotationReferences(codebasePath: string, oldPath: string, newPath: string): number;
@@ -79,6 +79,10 @@ function processFileForAnnotationUpdates(fullPath, regex, newPath, replacementCo
79
79
  * Update annotation references when story files are moved or renamed
80
80
  * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
81
81
  * @req REQ-MAINT-UPDATE - Update annotation references
82
+ * @param codebasePath Absolute or workspace-root path whose files will be updated in-place.
83
+ * @param oldPath The original @story path to search for in annotation comments.
84
+ * @param newPath The replacement @story path that will replace occurrences of oldPath.
85
+ * @returns The number of @story annotations that were updated across the codebase.
82
86
  */
83
87
  function updateAnnotationReferences(codebasePath, oldPath, newPath) {
84
88
  /**
@@ -29,7 +29,10 @@ export declare function linesBeforeHasStory(sourceCode: any, node: any, lookback
29
29
  export declare function parentChainHasStory(sourceCode: any, node: any): boolean;
30
30
  /**
31
31
  * Fallback: inspect text immediately preceding the node in sourceCode.getText to find @story
32
+ * Also accepts @implements annotations as satisfying story presence for this rule.
32
33
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
34
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
33
35
  * @req REQ-ANNOTATION-REQUIRED - Provide fallback textual inspection when other heuristics fail
36
+ * @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Treat @implements annotations as satisfying story presence in fallback checks
34
37
  */
35
38
  export declare function fallbackTextBeforeHasStory(sourceCode: any, node: any): boolean;
@@ -23,11 +23,17 @@ exports.LOOKBACK_LINES = 4;
23
23
  exports.FALLBACK_WINDOW = 800;
24
24
  /**
25
25
  * Shared predicate to determine if a given comment node contains an @story marker.
26
+ * Also treats @implements annotations as satisfying story presence checks.
26
27
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
28
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
27
29
  * @req REQ-ANNOTATION-REQUIRED - Centralize @story detection logic for comment value inspection
30
+ * @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Treat @implements annotations as satisfying story presence checks
28
31
  */
29
32
  function commentContainsStory(comment) {
30
- return typeof comment?.value === "string" && comment.value.includes("@story");
33
+ if (typeof comment?.value !== "string") {
34
+ return false;
35
+ }
36
+ return (comment.value.includes("@story") || comment.value.includes("@implements"));
31
37
  }
32
38
  /**
33
39
  * Safely extract the physical source lines array from sourceCode for scanning.
@@ -55,17 +61,20 @@ function getNodeStartLine(node) {
55
61
  * Generic helper to scan a range of physical source lines for the presence of an @story marker.
56
62
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
57
63
  * @req REQ-ANNOTATION-REQUIRED - Reuse line scanning logic for story annotations across helpers
64
+ * @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Accept @implements annotations as valid markers during line scanning
58
65
  */
59
66
  function scanLinesForMarker(lines, from, to) {
60
- // Walk each physical line in the configured lookback window to search for an inline @story marker.
67
+ // Walk each physical line in the configured lookback window to search for an inline @story or @implements marker.
61
68
  // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
62
69
  // @req REQ-ANNOTATION-REQUIRED - Scan preceding lines for existing story annotations
63
70
  for (let i = from; i < to; i++) {
64
71
  const text = lines[i];
65
- // Treat any line containing "@story" as evidence that the function is already annotated.
72
+ // Treat any line containing "@story" or "@implements" as evidence that the function is already annotated.
66
73
  // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
67
74
  // @req REQ-ANNOTATION-REQUIRED - Detect explicit @story markers in raw source text
68
- if (typeof text === "string" && text.includes("@story")) {
75
+ // @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Detect explicit @implements markers in raw source text
76
+ if (typeof text === "string" &&
77
+ (text.includes("@story") || text.includes("@implements"))) {
69
78
  return true;
70
79
  }
71
80
  }
@@ -125,8 +134,11 @@ function parentChainHasStory(sourceCode, node) {
125
134
  }
126
135
  /**
127
136
  * Fallback: inspect text immediately preceding the node in sourceCode.getText to find @story
137
+ * Also accepts @implements annotations as satisfying story presence for this rule.
128
138
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
139
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
129
140
  * @req REQ-ANNOTATION-REQUIRED - Provide fallback textual inspection when other heuristics fail
141
+ * @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Treat @implements annotations as satisfying story presence in fallback checks
130
142
  */
131
143
  function fallbackTextBeforeHasStory(sourceCode, node) {
132
144
  // Skip fallback text inspection when the sourceCode API or node range information is not available.
@@ -149,10 +161,12 @@ function fallbackTextBeforeHasStory(sourceCode, node) {
149
161
  // @req REQ-ANNOTATION-REQUIRED - Restrict fallback text scanning to a safe, fixed-size window
150
162
  const start = Math.max(0, range[0] - exports.FALLBACK_WINDOW);
151
163
  const textBefore = sourceCode.getText().slice(start, range[0]);
152
- // Detect any @story marker that appears within the bounded fallback window.
164
+ // Detect any @story or @implements marker that appears within the bounded fallback window.
153
165
  // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
154
166
  // @req REQ-ANNOTATION-REQUIRED - Recognize story annotations discovered via fallback text scanning
155
- if (typeof textBefore === "string" && textBefore.includes("@story")) {
167
+ // @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Recognize @implements annotations discovered via fallback text scanning
168
+ if (typeof textBefore === "string" &&
169
+ (textBefore.includes("@story") || textBefore.includes("@implements"))) {
156
170
  return true;
157
171
  }
158
172
  }
@@ -70,8 +70,7 @@ function normalizeUserOptions(rawOptions) {
70
70
  * @req REQ-REGEX-VALIDATION
71
71
  * @req REQ-BACKWARD-COMPAT
72
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) {
73
+ function resolvePattern({ nestedPattern, nestedFieldName, flatPattern, flatFieldName, defaultPattern, }) {
75
74
  const effective = typeof nestedPattern === "string"
76
75
  ? { value: nestedPattern, field: nestedFieldName }
77
76
  : typeof flatPattern === "string"
@@ -120,8 +119,20 @@ function resolveOptions(rawOptions) {
120
119
  const flatReqPattern = user?.requirementIdPattern;
121
120
  const nestedReqExample = user?.req?.example;
122
121
  const flatReqExample = user?.requirementIdExample;
123
- const storyPattern = resolvePattern(nestedStoryPattern, "story.pattern", flatStoryPattern, "storyPathPattern", getDefaultStoryPattern());
124
- const reqPattern = resolvePattern(nestedReqPattern, "req.pattern", flatReqPattern, "requirementIdPattern", getDefaultReqPattern());
122
+ const storyPattern = resolvePattern({
123
+ nestedPattern: nestedStoryPattern,
124
+ nestedFieldName: "story.pattern",
125
+ flatPattern: flatStoryPattern,
126
+ flatFieldName: "storyPathPattern",
127
+ defaultPattern: getDefaultStoryPattern(),
128
+ });
129
+ const reqPattern = resolvePattern({
130
+ nestedPattern: nestedReqPattern,
131
+ nestedFieldName: "req.pattern",
132
+ flatPattern: flatReqPattern,
133
+ flatFieldName: "requirementIdPattern",
134
+ defaultPattern: getDefaultReqPattern(),
135
+ });
125
136
  const storyExample = resolveExample(nestedStoryExample, flatStoryExample, getDefaultStoryExample());
126
137
  const reqExample = resolveExample(nestedReqExample, flatReqExample, getDefaultReqExample());
127
138
  resolvedDefaults = {
@@ -57,18 +57,23 @@ function collapseAnnotationValue(value) {
57
57
  * @req REQ-AUTOFIX-PRESERVE - Preserve surrounding formatting when normalizing story path suffixes
58
58
  */
59
59
  function getFixedStoryPath(original) {
60
+ // @story docs/stories/008.0-DEV-AUTO-FIX.story.md | REQ-AUTOFIX-SAFE - Skip auto-fix entirely for paths containing directory traversal segments ("..").
60
61
  if (original.includes("..")) {
61
62
  return null;
62
63
  }
64
+ // @story docs/stories/008.0-DEV-AUTO-FIX.story.md | REQ-AUTOFIX-FORMAT - Do not modify paths that already end with ".story.md" since they are already in the correct format.
63
65
  if (/\.story\.md$/.test(original)) {
64
66
  return null;
65
67
  }
68
+ // @story docs/stories/008.0-DEV-AUTO-FIX.story.md | REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE - Append the missing ".md" extension when the path already ends with ".story", preserving the existing base path.
66
69
  if (/\.story$/.test(original)) {
67
70
  return `${original}.md`;
68
71
  }
72
+ // @story docs/stories/008.0-DEV-AUTO-FIX.story.md | REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE - Upgrade plain ".md" paths to ".story.md" while preserving the rest of the path unchanged.
69
73
  if (/\.md$/.test(original)) {
70
74
  return original.replace(/\.md$/, ".story.md");
71
75
  }
76
+ // @story docs/stories/008.0-DEV-AUTO-FIX.story.md | REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE REQ-AUTOFIX-SAFE - For paths with no extension, conservatively append ".story.md" to create a canonical story file path.
72
77
  return `${original}.story.md`;
73
78
  }
74
79
  /**
@@ -5,12 +5,11 @@
5
5
  * @req REQ-PROJECT-BOUNDARY - Ensure resolved candidate paths remain within the project root
6
6
  * @req REQ-SECURITY-VALIDATION - Prevent path traversal and absolute path usage
7
7
  */
8
- export interface ReportInvalidPathArgs {
8
+ export interface _ReportInvalidPathArgs {
9
9
  storyPath: string;
10
10
  commentNode: any;
11
11
  context: any;
12
12
  }
13
- export type ReportInvalidPathFn = (args: ReportInvalidPathArgs) => void;
14
13
  export interface HandleBoundaryOptions {
15
14
  storyPath: string;
16
15
  commentNode: any;
@@ -21,7 +20,7 @@ export interface HandleBoundaryOptions {
21
20
  status: "exists" | "missing" | "fs-error" | null;
22
21
  matchedPath?: string | null;
23
22
  } | null;
24
- reportInvalidPath: ReportInvalidPathFn;
23
+ reportInvalidPath: (_args: _ReportInvalidPathArgs) => void;
25
24
  }
26
25
  export interface SecurityValidationOptions {
27
26
  storyPath: string;
@@ -29,7 +28,7 @@ export interface SecurityValidationOptions {
29
28
  context: any;
30
29
  cwd: string;
31
30
  allowAbsolute: boolean;
32
- reportInvalidPath: ReportInvalidPathFn;
31
+ reportInvalidPath: (_args: _ReportInvalidPathArgs) => void;
33
32
  }
34
33
  /**
35
34
  * Analyze candidate paths against the project boundary, returning whether any
@@ -1,6 +1,9 @@
1
1
  /**
2
- * Helper to determine whether a JSDoc or any nearby comments contain a @req annotation.
2
+ * Helper to determine whether a JSDoc or any nearby comments contain a requirement annotation.
3
+ * Treats both @req and @implements annotations as evidence of requirement coverage.
3
4
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
5
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
4
6
  * @req REQ-ANNOTATION-REQ-DETECTION - Determine presence of @req annotation
7
+ * @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Accept @implements as requirement coverage
5
8
  */
6
9
  export declare function hasReqAnnotation(jsdoc: any, comments: any[], context?: any, node?: any): boolean;
@@ -8,17 +8,24 @@ exports.hasReqAnnotation = hasReqAnnotation;
8
8
  */
9
9
  const require_story_io_1 = require("../rules/helpers/require-story-io");
10
10
  /**
11
- * Predicate helper to check whether a comment contains a @req annotation.
11
+ * Predicate helper to check whether a comment contains a requirement annotation.
12
+ * Treats both @req and @implements annotations as satisfying requirement presence checks.
12
13
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
14
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
13
15
  * @req REQ-ANNOTATION-REQ-DETECTION - Detect @req tag inside a comment
16
+ * @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Accept @implements as requirement annotation
14
17
  */
15
18
  function commentContainsReq(c) {
16
- return c && typeof c.value === "string" && c.value.includes("@req");
19
+ return (c &&
20
+ typeof c.value === "string" &&
21
+ (c.value.includes("@req") || c.value.includes("@implements")));
17
22
  }
18
23
  /**
19
- * Line-based helper adapted from linesBeforeHasStory to detect @req.
24
+ * Line-based helper adapted from linesBeforeHasStory to detect requirement annotations.
25
+ * Lines containing either @req or @implements are treated as annotated.
20
26
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
21
27
  * @req REQ-ANNOTATION-REQ-DETECTION - Detect @req in preceding source lines
28
+ * @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Accept @implements in preceding source lines
22
29
  */
23
30
  function linesBeforeHasReq(sourceCode, node) {
24
31
  const lines = sourceCode && sourceCode.lines;
@@ -33,44 +40,53 @@ function linesBeforeHasReq(sourceCode, node) {
33
40
  }
34
41
  const from = Math.max(0, startLine - 1 - require_story_io_1.LOOKBACK_LINES);
35
42
  const to = Math.max(0, startLine - 1);
36
- // Scan each physical line in the configured lookback window for an @req marker.
43
+ // Scan each physical line in the configured lookback window for @req or @implements markers.
37
44
  // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
38
45
  // @req REQ-ANNOTATION-REQ-DETECTION - Search preceding lines for @req text
46
+ // @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Search preceding lines for @implements text
39
47
  for (let i = from; i < to; i++) {
40
48
  const text = lines[i];
41
- // When a line contains @req we treat the function as already annotated.
49
+ // When a line contains @req or @implements we treat the function as already annotated.
42
50
  // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
43
51
  // @req REQ-ANNOTATION-REQ-DETECTION - Detect @req marker in raw source lines
44
- if (typeof text === "string" && text.includes("@req")) {
52
+ // @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Detect @implements marker in raw source lines
53
+ if (typeof text === "string" &&
54
+ (text.includes("@req") || text.includes("@implements"))) {
45
55
  return true;
46
56
  }
47
57
  }
48
58
  return false;
49
59
  }
50
60
  /**
51
- * Parent-chain helper adapted from parentChainHasStory to detect @req.
61
+ * Parent-chain helper adapted from parentChainHasStory to detect requirement annotations.
62
+ * Accepts both @req and @implements in parent-chain comments as satisfying requirement presence.
52
63
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
53
64
  * @req REQ-ANNOTATION-REQ-DETECTION - Detect @req in parent-chain comments
65
+ * @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Accept @implements in parent-chain comments
54
66
  */
55
67
  function parentChainHasReq(sourceCode, node) {
56
68
  let p = node && node.parent;
57
69
  // Walk up the parent chain and inspect comments attached to each ancestor.
70
+ // Accept both @req and @implements markers when local comments are absent.
58
71
  // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
59
72
  // @req REQ-ANNOTATION-REQ-DETECTION - Traverse parent nodes when local comments are absent
73
+ // @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Allow @implements to satisfy requirement on parents
60
74
  while (p) {
61
75
  const pComments = typeof sourceCode?.getCommentsBefore === "function"
62
76
  ? sourceCode.getCommentsBefore(p) || []
63
77
  : [];
64
- // Look for @req in comments immediately preceding each parent node.
78
+ // Look for @req or @implements in comments immediately preceding each parent node.
65
79
  // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
66
80
  // @req REQ-ANNOTATION-REQ-DETECTION - Detect @req markers in parent comments
81
+ // @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Detect @implements markers in parent comments
67
82
  if (Array.isArray(pComments) && pComments.some(commentContainsReq)) {
68
83
  return true;
69
84
  }
70
85
  const pLeading = p.leadingComments || [];
71
- // Also inspect leadingComments attached directly to the parent node.
86
+ // Also inspect leadingComments attached directly to the parent node, accepting @req or @implements.
72
87
  // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
73
88
  // @req REQ-ANNOTATION-REQ-DETECTION - Detect @req markers in parent leadingComments
89
+ // @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Detect @implements markers in parent leadingComments
74
90
  if (Array.isArray(pLeading) && pLeading.some(commentContainsReq)) {
75
91
  return true;
76
92
  }
@@ -79,9 +95,12 @@ function parentChainHasReq(sourceCode, node) {
79
95
  return false;
80
96
  }
81
97
  /**
82
- * Fallback text window helper adapted from fallbackTextBeforeHasStory to detect @req.
98
+ * Fallback text window helper adapted from fallbackTextBeforeHasStory to detect requirement annotations.
99
+ * Treats both @req and @implements in the fallback text window as requirement presence.
83
100
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
101
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
84
102
  * @req REQ-ANNOTATION-REQ-DETECTION - Detect @req in fallback text window before node
103
+ * @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Accept @implements in fallback text window before node
85
104
  */
86
105
  function fallbackTextBeforeHasReq(sourceCode, node) {
87
106
  // Guard against unsupported sourceCode or nodes without a usable range.
@@ -101,10 +120,13 @@ function fallbackTextBeforeHasReq(sourceCode, node) {
101
120
  try {
102
121
  const start = Math.max(0, range[0] - require_story_io_1.FALLBACK_WINDOW);
103
122
  const textBefore = sourceCode.getText().slice(start, range[0]);
104
- // Detect @req in the bounded text window immediately preceding the node.
123
+ // Detect @req or @implements in the bounded text window immediately preceding the node.
105
124
  // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
125
+ // @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
106
126
  // @req REQ-ANNOTATION-REQ-DETECTION - Detect @req marker in fallback text window
107
- if (typeof textBefore === "string" && textBefore.includes("@req")) {
127
+ // @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Detect @implements marker in fallback text window
128
+ if (typeof textBefore === "string" &&
129
+ (textBefore.includes("@req") || textBefore.includes("@implements"))) {
108
130
  return true;
109
131
  }
110
132
  }
@@ -117,9 +139,12 @@ function fallbackTextBeforeHasReq(sourceCode, node) {
117
139
  return false;
118
140
  }
119
141
  /**
120
- * Helper to determine whether a JSDoc or any nearby comments contain a @req annotation.
142
+ * Helper to determine whether a JSDoc or any nearby comments contain a requirement annotation.
143
+ * Treats both @req and @implements annotations as evidence of requirement coverage.
121
144
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
145
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
122
146
  * @req REQ-ANNOTATION-REQ-DETECTION - Determine presence of @req annotation
147
+ * @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Accept @implements as requirement coverage
123
148
  */
124
149
  function hasReqAnnotation(jsdoc, comments, context, node) {
125
150
  try {
@@ -129,6 +154,7 @@ function hasReqAnnotation(jsdoc, comments, context, node) {
129
154
  // Prefer robust, location-based heuristics when sourceCode and node are available.
130
155
  // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
131
156
  // @req REQ-ANNOTATION-REQ-DETECTION - Use multiple heuristics to detect @req markers around the node
157
+ // @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Use multiple heuristics to detect @implements markers around the node
132
158
  if (sourceCode && node) {
133
159
  if (linesBeforeHasReq(sourceCode, node) ||
134
160
  parentChainHasReq(sourceCode, node) ||
@@ -142,11 +168,13 @@ function hasReqAnnotation(jsdoc, comments, context, node) {
142
168
  // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
143
169
  // @req REQ-ANNOTATION-REQ-DETECTION - Fail gracefully when advanced detection heuristics throw
144
170
  }
145
- // BRANCH @req detection on JSDoc or comments
171
+ // BRANCH requirement detection on JSDoc or comments, accepting both @req and @implements.
146
172
  // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
173
+ // @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
147
174
  // @req REQ-ANNOTATION-REQ-DETECTION
175
+ // @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS
148
176
  return ((jsdoc &&
149
177
  typeof jsdoc.value === "string" &&
150
- jsdoc.value.includes("@req")) ||
178
+ (jsdoc.value.includes("@req") || jsdoc.value.includes("@implements"))) ||
151
179
  comments.some(commentContainsReq));
152
180
  }
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ /**
37
+ * Tests for: docs/stories/002.0-DEV-ESLINT-CONFIG.story.md
38
+ * @story docs/stories/002.0-DEV-ESLINT-CONFIG.story.md
39
+ * @req REQ-CONFIG-PRESETS - Validate flat-config presets register traceability plugin and rules
40
+ * @req REQ-FLAT-CONFIG - Ensure presets work with ESLint v9 flat config
41
+ * @req REQ-PROJECT-INTEGRATION - Support seamless integration via documented preset usage
42
+ */
43
+ const use_at_your_own_risk_1 = require("eslint/use-at-your-own-risk");
44
+ const index_1 = __importStar(require("../../src/index"));
45
+ const baseConfig = {
46
+ plugins: {
47
+ traceability: index_1.default,
48
+ },
49
+ rules: {},
50
+ };
51
+ async function lintTextWithConfig(text, config) {
52
+ const eslint = new use_at_your_own_risk_1.FlatESLint({
53
+ overrideConfig: config,
54
+ overrideConfigFile: true,
55
+ ignore: false,
56
+ });
57
+ const [result] = await eslint.lintText(text, { filePath: "example.js" });
58
+ return result;
59
+ }
60
+ describe("Flat config presets integration (Story 002.0-DEV-ESLINT-CONFIG)", () => {
61
+ it("[REQ-CONFIG-PRESETS] recommended preset enables traceability rules via documented usage", async () => {
62
+ const config = [baseConfig, ...index_1.configs.recommended];
63
+ const code = "function foo() {}";
64
+ const result = await lintTextWithConfig(code, config);
65
+ const ruleIds = result.messages.map((m) => m.ruleId).sort();
66
+ expect(ruleIds).toContain("traceability/require-story-annotation");
67
+ });
68
+ it("[REQ-CONFIG-PRESETS] strict preset also enables traceability rules via documented usage", async () => {
69
+ const config = [baseConfig, ...index_1.configs.strict];
70
+ const code = "function bar() {}";
71
+ const result = await lintTextWithConfig(code, config);
72
+ const ruleIds = result.messages.map((m) => m.ruleId).sort();
73
+ expect(ruleIds).toContain("traceability/require-story-annotation");
74
+ });
75
+ });