eslint-plugin-traceability 1.10.1 → 1.11.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 (33) hide show
  1. package/CHANGELOG.md +4 -3
  2. package/README.md +1 -0
  3. package/lib/src/maintenance/cli.js +12 -12
  4. package/lib/src/maintenance/detect.js +19 -19
  5. package/lib/src/rules/helpers/require-story-core.d.ts +2 -15
  6. package/lib/src/rules/helpers/require-story-core.js +4 -71
  7. package/lib/src/rules/helpers/require-story-helpers.d.ts +32 -8
  8. package/lib/src/rules/helpers/require-story-helpers.js +44 -15
  9. package/lib/src/rules/helpers/require-story-visitors.js +47 -6
  10. package/lib/src/rules/helpers/valid-annotation-format-validators.js +5 -1
  11. package/lib/src/rules/helpers/valid-annotation-options.d.ts +6 -0
  12. package/lib/src/rules/helpers/valid-annotation-options.js +4 -0
  13. package/lib/src/rules/helpers/valid-annotation-utils.js +31 -31
  14. package/lib/src/rules/helpers/valid-story-reference-helpers.js +19 -19
  15. package/lib/src/rules/prefer-implements-annotation.js +29 -1
  16. package/lib/src/rules/require-story-annotation.js +15 -0
  17. package/lib/src/rules/require-test-traceability.js +1 -6
  18. package/lib/src/utils/annotation-checker.js +1 -1
  19. package/lib/tests/perf/maintenance-cli-large-workspace.test.d.ts +1 -0
  20. package/lib/tests/perf/maintenance-cli-large-workspace.test.js +130 -0
  21. package/lib/tests/perf/maintenance-large-workspace.test.d.ts +1 -0
  22. package/lib/tests/perf/maintenance-large-workspace.test.js +149 -0
  23. package/lib/tests/rules/auto-fix-behavior-008.test.js +23 -0
  24. package/lib/tests/rules/require-story-core.autofix.test.js +9 -3
  25. package/lib/tests/rules/require-story-core.test.js +13 -7
  26. package/lib/tests/rules/require-story-helpers-edgecases.test.js +1 -1
  27. package/lib/tests/rules/require-story-helpers.test.js +14 -8
  28. package/lib/tests/utils/require-story-core-test-helpers.d.ts +1 -1
  29. package/lib/tests/utils/require-story-core-test-helpers.js +16 -16
  30. package/lib/tests/utils/temp-dir-helpers.js +1 -1
  31. package/package.json +9 -2
  32. package/user-docs/api-reference.md +8 -4
  33. package/user-docs/examples.md +42 -0
package/CHANGELOG.md CHANGED
@@ -1,9 +1,10 @@
1
- ## [1.10.1](https://github.com/voder-ai/eslint-plugin-traceability/compare/v1.10.0...v1.10.1) (2025-12-05)
1
+ # [1.11.0](https://github.com/voder-ai/eslint-plugin-traceability/compare/v1.10.1...v1.11.0) (2025-12-05)
2
2
 
3
3
 
4
- ### Bug Fixes
4
+ ### Features
5
5
 
6
- * support JSDoc tag coexistence for annotation parsing ([31e9416](https://github.com/voder-ai/eslint-plugin-traceability/commit/31e9416d201fd347051bc44521d3d3b840a244a1))
6
+ * add configurable auto-fix templates and toggles ([5e0e6e7](https://github.com/voder-ai/eslint-plugin-traceability/commit/5e0e6e782812aebb128c4922931bb9703265bfb1))
7
+ * add configurable auto-fix templates and toggles ([21c3a79](https://github.com/voder-ai/eslint-plugin-traceability/commit/21c3a79ce2e2e7fd95a422a963735d67940c8bd8))
7
8
 
8
9
  # Changelog
9
10
 
package/README.md CHANGED
@@ -54,6 +54,7 @@ export default [
54
54
  - `traceability/valid-annotation-format` Enforces correct format of traceability annotations. (See the rule documentation in the plugin's user guide.)
55
55
  - `traceability/valid-story-reference` Validates that `@story` references point to existing story files. (See the rule documentation in the plugin's user guide.)
56
56
  - `traceability/valid-req-reference` Validates that `@req` references point to existing requirement IDs. (See the rule documentation in the plugin's user guide.)
57
+ - `traceability/require-test-traceability` Enforces traceability conventions in test files by requiring file-level `@supports` annotations, story references in `describe` blocks, and `[REQ-...]` prefixes in `it`/`test` names. (See the rule documentation in the plugin's user guide.)
57
58
  - `traceability/prefer-implements-annotation` Recommends migration from legacy `@story`/`@req` annotations to `@supports` (opt-in; disabled by default in the presets and must be explicitly enabled). (See the rule documentation in the plugin's user guide.)
58
59
 
59
60
  Configuration options: For detailed per-rule options (such as scopes, branch types, and story directory settings), see the individual rule docs in the plugin's user guide and the consolidated [API Reference](user-docs/api-reference.md).
@@ -18,8 +18,8 @@ function runMaintenanceCli(rawArgv) {
18
18
  const initialNormalized = (0, flags_1.normalizeCliArgs)(rawArgv);
19
19
  const { subcommand: command } = initialNormalized;
20
20
  if (!command || command === "-h" || command === "--help") {
21
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE
22
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Branch to show usage when no command or help flag is provided; handle help requests safely and provide discoverable usage output
21
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE
22
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Branch to show usage when no command or help flag is provided; handle help requests safely and provide discoverable usage output
23
23
  printHelp();
24
24
  return commands_1.EXIT_OK;
25
25
  }
@@ -27,36 +27,36 @@ function runMaintenanceCli(rawArgv) {
27
27
  // receive the subcommand name and its raw argument vector unchanged.
28
28
  const normalized = initialNormalized;
29
29
  try {
30
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE
31
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Catch unexpected errors and surface concise diagnostics without crashing
30
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE
31
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Catch unexpected errors and surface concise diagnostics without crashing
32
32
  switch (command) {
33
33
  case "detect":
34
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT - Branch to dispatch to detection handler when 'detect' is requested; dispatch to detection handler
34
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT - Branch to dispatch to detection handler when 'detect' is requested; dispatch to detection handler
35
35
  return (0, commands_1.handleDetect)(normalized);
36
36
  case "verify":
37
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-VERIFY - Branch to dispatch to verification handler when 'verify' is requested; dispatch to verification handler
37
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-VERIFY - Branch to dispatch to verification handler when 'verify' is requested; dispatch to verification handler
38
38
  return (0, commands_1.handleVerify)(normalized);
39
39
  case "report":
40
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-REPORT - Branch to dispatch to reporting handler when 'report' is requested; dispatch to reporting handler
40
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-REPORT - Branch to dispatch to reporting handler when 'report' is requested; dispatch to reporting handler
41
41
  return (0, commands_1.handleReport)(normalized);
42
42
  case "update": {
43
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-UPDATE - Branch to dispatch to update handler when 'update' is requested; dispatch to update handler
43
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-UPDATE - Branch to dispatch to update handler when 'update' is requested; dispatch to update handler
44
44
  const result = (0, commands_1.handleUpdate)(normalized);
45
45
  if (result === commands_1.EXIT_USAGE) {
46
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Print help on usage errors from update; help branch for update usage errors
46
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Print help on usage errors from update; help branch for update usage errors
47
47
  printHelp();
48
48
  }
49
49
  return result;
50
50
  }
51
51
  default:
52
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Branch for unknown commands to emit diagnostics and safe usage guidance; handle unknown commands safely with diagnostics
52
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Branch for unknown commands to emit diagnostics and safe usage guidance; handle unknown commands safely with diagnostics
53
53
  console.error(`Unknown command: ${command}`);
54
54
  printHelp();
55
55
  return commands_1.EXIT_USAGE;
56
56
  }
57
57
  }
58
58
  catch (error) {
59
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Catch-all error branch to prevent crashes and provide concise diagnostics; catch unexpected errors and surface concise diagnostics without crashing
59
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Catch-all error branch to prevent crashes and provide concise diagnostics; catch unexpected errors and surface concise diagnostics without crashing
60
60
  const message = error instanceof Error
61
61
  ? error.message
62
62
  : "Unknown error in maintenance CLI";
@@ -70,7 +70,7 @@ function runMaintenanceCli(rawArgv) {
70
70
  * @req REQ-MAINT-SAFE - Provide discoverable CLI usage information
71
71
  */
72
72
  function printHelp() {
73
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Help branch ensures users can discover maintenance CLI usage safely; provide discoverable CLI usage information
73
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Help branch ensures users can discover maintenance CLI usage safely; provide discoverable CLI usage information
74
74
  console.log(`traceability-maint - Traceability annotation maintenance tools
75
75
 
76
76
  Usage:
@@ -54,9 +54,9 @@ function detectStaleAnnotations(codebasePath) {
54
54
  // @req REQ-MAINT-DETECT - Return empty result if workspaceRoot does not exist or is not a directory
55
55
  if (!fs.existsSync(workspaceRoot) ||
56
56
  !fs.statSync(workspaceRoot).isDirectory()) {
57
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
58
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE
59
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
57
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
58
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE
59
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
60
60
  return [];
61
61
  }
62
62
  const stale = new Set();
@@ -79,12 +79,12 @@ function processFileForStaleAnnotations(file, workspaceRoot, cwd, stale) {
79
79
  // @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
80
80
  // @req REQ-MAINT-DETECT - Handle file read errors gracefully
81
81
  try {
82
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
82
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
83
83
  content = fs.readFileSync(file, "utf8");
84
84
  }
85
85
  catch {
86
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
87
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Swallow file read failures without aborting detection
86
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
87
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Swallow file read failures without aborting detection
88
88
  return;
89
89
  }
90
90
  const regex = /@story\s+([^\s]+)/g;
@@ -103,7 +103,7 @@ function handleStoryMatch(storyPath, workspaceRoot, cwd, stale) {
103
103
  // @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
104
104
  // @req REQ-MAINT-DETECT REQ-SECURITY-VALIDATION - Skip traversal/absolute-unsafe or invalid-extension story paths before any filesystem or boundary checks
105
105
  if ((0, storyReferenceUtils_1.isUnsafeStoryPath)(storyPath)) {
106
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
106
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
107
107
  return;
108
108
  }
109
109
  // @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
@@ -115,7 +115,7 @@ function handleStoryMatch(storyPath, workspaceRoot, cwd, stale) {
115
115
  const inProjectCandidates = getInProjectCandidates(storyProjectCandidate, storyCodebaseCandidate, workspaceRoot);
116
116
  // If both candidates are out-of-project, do not mark as stale and skip FS checks
117
117
  if (inProjectCandidates.length === 0) {
118
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT - No in-project candidates means nothing to check or mark stale
118
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT - No in-project candidates means nothing to check or mark stale
119
119
  return;
120
120
  }
121
121
  // @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
@@ -124,7 +124,7 @@ function handleStoryMatch(storyPath, workspaceRoot, cwd, stale) {
124
124
  // @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
125
125
  // @req REQ-MAINT-DETECT - Mark story as stale if any in-project candidate exists conceptually but none exist on disk
126
126
  if (!anyExists) {
127
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
127
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
128
128
  stale.add(storyPath);
129
129
  }
130
130
  }
@@ -136,24 +136,24 @@ function getInProjectCandidates(storyProjectCandidate, storyCodebaseCandidate, w
136
136
  let projectBoundary;
137
137
  let codebaseBoundary;
138
138
  try {
139
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
139
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
140
140
  projectBoundary = (0, storyReferenceUtils_1.enforceProjectBoundary)(storyProjectCandidate, workspaceRoot);
141
141
  }
142
142
  catch {
143
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
144
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Treat boundary enforcement failures as out-of-project
143
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
144
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Treat boundary enforcement failures as out-of-project
145
145
  projectBoundary = {
146
146
  isWithinProject: false,
147
147
  candidate: storyProjectCandidate,
148
148
  };
149
149
  }
150
150
  try {
151
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
151
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
152
152
  codebaseBoundary = (0, storyReferenceUtils_1.enforceProjectBoundary)(storyCodebaseCandidate, workspaceRoot);
153
153
  }
154
154
  catch {
155
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
156
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Treat boundary enforcement failures as out-of-project
155
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
156
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Treat boundary enforcement failures as out-of-project
157
157
  codebaseBoundary = {
158
158
  isWithinProject: false,
159
159
  candidate: storyCodebaseCandidate,
@@ -161,11 +161,11 @@ function getInProjectCandidates(storyProjectCandidate, storyCodebaseCandidate, w
161
161
  }
162
162
  const inProjectCandidates = [];
163
163
  if (projectBoundary.isWithinProject) {
164
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT - Collect project-relative in-project candidate
164
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT - Collect project-relative in-project candidate
165
165
  inProjectCandidates.push(projectBoundary.candidate);
166
166
  }
167
167
  if (codebaseBoundary.isWithinProject) {
168
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT - Collect workspace-root-relative in-project candidate
168
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT - Collect workspace-root-relative in-project candidate
169
169
  inProjectCandidates.push(codebaseBoundary.candidate);
170
170
  }
171
171
  return inProjectCandidates;
@@ -177,12 +177,12 @@ function getInProjectCandidates(storyProjectCandidate, storyCodebaseCandidate, w
177
177
  function anyInProjectCandidateExists(inProjectCandidates) {
178
178
  return inProjectCandidates.some(
179
179
  /**
180
- * @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT REQ-MAINT-SAFE
180
+ * @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT REQ-MAINT-SAFE
181
181
  */
182
182
  (p) => {
183
183
  const exists = fs.existsSync(p);
184
184
  if (!exists) {
185
- // @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Safely handle non-existent candidate without throwing
185
+ // @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Safely handle non-existent candidate without throwing
186
186
  }
187
187
  return exists;
188
188
  });
@@ -1,16 +1,15 @@
1
- import type { Rule } from "eslint";
2
1
  /**
3
2
  * Create a fixer function that inserts a @story annotation before the target node.
4
3
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
5
4
  * @req REQ-AUTOFIX - Provide automatic fix function for missing @story annotations
6
5
  */
7
- export declare function createAddStoryFix(target: any): (fixer: any) => any;
6
+ export declare function createAddStoryFix(target: any, annotationTemplate: string): (fixer: any) => any;
8
7
  /**
9
8
  * Create a fixer function for class method annotations.
10
9
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
11
10
  * @req REQ-AUTOFIX - Provide automatic fix for class method annotations
12
11
  */
13
- export declare function createMethodFix(node: any): (fixer: any) => any;
12
+ export declare function createMethodFix(node: any, annotationTemplate: string): (fixer: any) => any;
14
13
  /**
15
14
  * Default set of node types to check for missing @story annotations.
16
15
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
@@ -21,15 +20,3 @@ export declare const DEFAULT_SCOPE: string[];
21
20
  * Allowed values for export priority option.
22
21
  */
23
22
  export declare const EXPORT_PRIORITY_VALUES: string[];
24
- /**
25
- * Report a missing @story annotation for a general function-like node.
26
- * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
27
- * @req REQ-AUTOFIX - Report missing annotation and provide autofix using createAddStoryFix
28
- */
29
- export declare function reportMissing(context: Rule.RuleContext, sourceCode: any, node: any, target?: any): void;
30
- /**
31
- * Report missing @story annotation for methods
32
- * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
33
- * @req REQ-AUTOFIX - Provide automatic fix for class method annotations
34
- */
35
- export declare function reportMethod(context: Rule.RuleContext, sourceCode: any, node: any, target?: any): void;
@@ -3,15 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.EXPORT_PRIORITY_VALUES = exports.DEFAULT_SCOPE = void 0;
4
4
  exports.createAddStoryFix = createAddStoryFix;
5
5
  exports.createMethodFix = createMethodFix;
6
- exports.reportMissing = reportMissing;
7
- exports.reportMethod = reportMethod;
8
- const require_story_helpers_1 = require("./require-story-helpers");
9
6
  /**
10
7
  * Create a fixer function that inserts a @story annotation before the target node.
11
8
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
12
9
  * @req REQ-AUTOFIX - Provide automatic fix function for missing @story annotations
13
10
  */
14
- function createAddStoryFix(target) {
11
+ function createAddStoryFix(target, annotationTemplate) {
15
12
  /**
16
13
  * Fixer that inserts a @story annotation before the target node.
17
14
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
@@ -29,7 +26,7 @@ function createAddStoryFix(target) {
29
26
  ? target.range[0]
30
27
  : 0
31
28
  : 0;
32
- return fixer.insertTextBeforeRange([start, start], `${require_story_helpers_1.ANNOTATION}\n`);
29
+ return fixer.insertTextBeforeRange([start, start], `${annotationTemplate}\n`);
33
30
  }
34
31
  return addStoryFixer;
35
32
  }
@@ -38,7 +35,7 @@ function createAddStoryFix(target) {
38
35
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
39
36
  * @req REQ-AUTOFIX - Provide automatic fix for class method annotations
40
37
  */
41
- function createMethodFix(node) {
38
+ function createMethodFix(node, annotationTemplate) {
42
39
  /**
43
40
  * Fixer that inserts a @story annotation before a method node.
44
41
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
@@ -56,7 +53,7 @@ function createMethodFix(node) {
56
53
  ? node.range[0]
57
54
  : 0
58
55
  : 0;
59
- return fixer.insertTextBeforeRange([start, start], `${require_story_helpers_1.ANNOTATION}\n `);
56
+ return fixer.insertTextBeforeRange([start, start], `${annotationTemplate}\n `);
60
57
  }
61
58
  return methodFixer;
62
59
  }
@@ -76,67 +73,3 @@ exports.DEFAULT_SCOPE = [
76
73
  * Allowed values for export priority option.
77
74
  */
78
75
  exports.EXPORT_PRIORITY_VALUES = ["all", "exported", "non-exported"];
79
- /**
80
- * Report a missing @story annotation for a general function-like node.
81
- * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
82
- * @req REQ-AUTOFIX - Report missing annotation and provide autofix using createAddStoryFix
83
- */
84
- function reportMissing(context, sourceCode, node, target) {
85
- // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
86
- // @req REQ-AUTOFIX - Prefer provided sourceCode, fallback to context.getSourceCode()
87
- const sc = sourceCode || context.getSourceCode();
88
- /**
89
- * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
90
- * @req REQ-AUTOFIX - Only attempt to read JSDoc comment if source supports it
91
- */
92
- if (typeof sc?.getJSDocComment === "function") {
93
- // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
94
- // @req REQ-ANNOTATION-REQUIRED - Skip reporting when JSDoc already contains @story
95
- const js = sc.getJSDocComment(node);
96
- // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
97
- // @req REQ-ANNOTATION-REQUIRED - If @story present in JSDoc, do not report
98
- if (js && typeof js.value === "string" && js.value.includes("@story"))
99
- return;
100
- }
101
- // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
102
- // @req REQ-ANNOTATION-REQUIRED - Resolve function name for reporting, default to <unknown>
103
- const name = node && node.id && node.id.name ? node.id.name : "<unknown>";
104
- const resolvedTarget = target ?? node;
105
- const nameNode = node && node.id && node.id.type === "Identifier"
106
- ? node.id
107
- : node && node.key && node.key.type === "Identifier"
108
- ? node.key
109
- : node;
110
- context.report({
111
- node: nameNode,
112
- messageId: "missingStory",
113
- data: { name },
114
- suggest: [
115
- {
116
- desc: `Add JSDoc @story annotation for function '${name}', e.g., ${require_story_helpers_1.ANNOTATION}`,
117
- fix: createAddStoryFix(resolvedTarget),
118
- },
119
- ],
120
- });
121
- }
122
- /**
123
- * Report missing @story annotation for methods
124
- * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
125
- * @req REQ-AUTOFIX - Provide automatic fix for class method annotations
126
- */
127
- function reportMethod(context, sourceCode, node, target) {
128
- // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
129
- // @req REQ-ANNOTATION-REQUIRED - Resolve method name for reporting, default to <unknown>
130
- const name = node && node.key && node.key.name ? node.key.name : "<unknown>";
131
- context.report({
132
- node,
133
- messageId: "missingStory",
134
- data: { name },
135
- suggest: [
136
- {
137
- desc: `Add JSDoc @story annotation for function '${name}', e.g., ${require_story_helpers_1.ANNOTATION}`,
138
- fix: createMethodFix(target ?? node),
139
- },
140
- ],
141
- });
142
- }
@@ -1,7 +1,11 @@
1
1
  /**
2
2
  * Helpers for the "require-story" rule
3
3
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
4
+ * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
4
5
  * @req REQ-ANNOTATION-REQUIRED - File-level header for rule helper utilities
6
+ * @req REQ-AUTOFIX-MISSING
7
+ * @req REQ-AUTOFIX-TEMPLATE
8
+ * @req REQ-AUTOFIX-SELECTIVE
5
9
  */
6
10
  import type { Rule } from "eslint";
7
11
  import { linesBeforeHasStory, parentChainHasStory, fallbackTextBeforeHasStory } from "./require-story-io";
@@ -11,7 +15,17 @@ import { DEFAULT_SCOPE, EXPORT_PRIORITY_VALUES } from "./require-story-core";
11
15
  * Path to the story file for annotations
12
16
  */
13
17
  declare const STORY_PATH = "docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md";
14
- declare const ANNOTATION = "/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */";
18
+ /**
19
+ * Derive the annotation template, optionally using an override.
20
+ * When override is a non-empty string, its trimmed value is used.
21
+ * Otherwise, the default template is returned.
22
+ */
23
+ declare function getAnnotationTemplate(override?: string): string;
24
+ /**
25
+ * Determine whether auto-fix should be applied.
26
+ * Explicit false disables auto-fix; all other values enable it.
27
+ */
28
+ declare function shouldApplyAutoFix(autoFix: boolean | undefined): boolean;
15
29
  /**
16
30
  * Number of physical source lines to inspect before a node when searching for @story text
17
31
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
@@ -96,6 +110,10 @@ declare function extractName(node: any): string;
96
110
  * @returns {boolean} whether node should be processed
97
111
  */
98
112
  declare function shouldProcessNode(node: any, scope: string[], exportPriority?: string): boolean;
113
+ interface ReportOptions {
114
+ annotationTemplateOverride?: string;
115
+ autoFixToggle?: boolean;
116
+ }
99
117
  /**
100
118
  * Report a missing @story annotation for a function-like node
101
119
  * Provides a suggestion to add the annotation.
@@ -107,10 +125,13 @@ declare function shouldProcessNode(node: any, scope: string[], exportPriority?:
107
125
  * @req REQ-ERROR-SPECIFIC - Error reports must include both name and functionName in the data payload for specific function context
108
126
  * @param {Rule.RuleContext} context - ESLint rule context used to report
109
127
  * @param {any} sourceCode - ESLint sourceCode object
110
- * @param {any} node - AST node that is missing the annotation
111
- * @param {any} [passedTarget] - optional AST node to use as insertion target instead of resolving from node
128
+ * @param {{ node: any; target?: any; options?: ReportOptions }} config - configuration containing the node to report, optional insertion target, and optional report options
112
129
  */
113
- declare function reportMissing(context: Rule.RuleContext, sourceCode: any, node: any, passedTarget?: any): void;
130
+ declare function reportMissing(context: Rule.RuleContext, sourceCode: any, config: {
131
+ node: any;
132
+ target?: any;
133
+ options?: ReportOptions;
134
+ }): void;
114
135
  /**
115
136
  * Report a missing @story annotation for a method-like node
116
137
  * Provides a suggestion to update the method/interface with the annotation.
@@ -125,13 +146,16 @@ declare function reportMissing(context: Rule.RuleContext, sourceCode: any, node:
125
146
  * @req REQ-ERROR-CONTEXT - Method error reports must include functionName data for consistent error context
126
147
  * @param {Rule.RuleContext} context - ESLint rule context to report
127
148
  * @param {any} sourceCode - ESLint sourceCode object
128
- * @param {any} node - AST node that is missing the annotation
129
- * @param {any} [passedTarget] - optional AST node to use as insertion target instead of resolving from node
149
+ * @param {{ node: any; target?: any; options?: ReportOptions }} config - configuration containing the node to report, optional insertion target, and optional report options
130
150
  */
131
- declare function reportMethod(context: Rule.RuleContext, sourceCode: any, node: any, passedTarget?: any): void;
151
+ declare function reportMethod(context: Rule.RuleContext, sourceCode: any, config: {
152
+ node: any;
153
+ target?: any;
154
+ options?: ReportOptions;
155
+ }): void;
132
156
  /**
133
157
  * Explicit exports for require-story-annotation consumers
134
158
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
135
159
  * @req REQ-ANNOTATION-REQUIRED - Explicitly export helper functions and constants used by requiring modules
136
160
  */
137
- export { STORY_PATH, ANNOTATION, LOOKBACK_LINES, FALLBACK_WINDOW, isExportedNode, jsdocHasStory, commentsBeforeHasStory, leadingCommentsHasStory, hasStoryAnnotation, getNodeName, extractName, resolveTargetNode, shouldProcessNode, reportMissing, reportMethod, DEFAULT_SCOPE, EXPORT_PRIORITY_VALUES, linesBeforeHasStory, parentChainHasStory, fallbackTextBeforeHasStory, };
161
+ export { STORY_PATH, getAnnotationTemplate, shouldApplyAutoFix, LOOKBACK_LINES, FALLBACK_WINDOW, isExportedNode, jsdocHasStory, commentsBeforeHasStory, leadingCommentsHasStory, hasStoryAnnotation, getNodeName, extractName, resolveTargetNode, shouldProcessNode, reportMissing, reportMethod, DEFAULT_SCOPE, EXPORT_PRIORITY_VALUES, linesBeforeHasStory, parentChainHasStory, fallbackTextBeforeHasStory, };
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.fallbackTextBeforeHasStory = exports.parentChainHasStory = exports.linesBeforeHasStory = exports.EXPORT_PRIORITY_VALUES = exports.DEFAULT_SCOPE = exports.getNodeName = exports.FALLBACK_WINDOW = exports.LOOKBACK_LINES = exports.ANNOTATION = exports.STORY_PATH = void 0;
3
+ exports.fallbackTextBeforeHasStory = exports.parentChainHasStory = exports.linesBeforeHasStory = exports.EXPORT_PRIORITY_VALUES = exports.DEFAULT_SCOPE = exports.getNodeName = exports.FALLBACK_WINDOW = exports.LOOKBACK_LINES = exports.STORY_PATH = void 0;
4
+ exports.getAnnotationTemplate = getAnnotationTemplate;
5
+ exports.shouldApplyAutoFix = shouldApplyAutoFix;
4
6
  exports.isExportedNode = isExportedNode;
5
7
  exports.jsdocHasStory = jsdocHasStory;
6
8
  exports.commentsBeforeHasStory = commentsBeforeHasStory;
@@ -25,8 +27,27 @@ Object.defineProperty(exports, "EXPORT_PRIORITY_VALUES", { enumerable: true, get
25
27
  */
26
28
  const STORY_PATH = "docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md";
27
29
  exports.STORY_PATH = STORY_PATH;
28
- const ANNOTATION = `/** @story ${STORY_PATH} */`;
29
- exports.ANNOTATION = ANNOTATION;
30
+ /**
31
+ * Derive the annotation template, optionally using an override.
32
+ * When override is a non-empty string, its trimmed value is used.
33
+ * Otherwise, the default template is returned.
34
+ */
35
+ function getAnnotationTemplate(override) {
36
+ if (typeof override === "string" && override.trim().length > 0) {
37
+ return override.trim();
38
+ }
39
+ return `/** @story ${STORY_PATH} */`;
40
+ }
41
+ /**
42
+ * Determine whether auto-fix should be applied.
43
+ * Explicit false disables auto-fix; all other values enable it.
44
+ */
45
+ function shouldApplyAutoFix(autoFix) {
46
+ if (autoFix === false) {
47
+ return false;
48
+ }
49
+ return true;
50
+ }
30
51
  /**
31
52
  * Number of physical source lines to inspect before a node when searching for @story text
32
53
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
@@ -245,10 +266,10 @@ function shouldProcessNode(node, scope, exportPriority = "all") {
245
266
  * @req REQ-ERROR-SPECIFIC - Error reports must include both name and functionName in the data payload for specific function context
246
267
  * @param {Rule.RuleContext} context - ESLint rule context used to report
247
268
  * @param {any} sourceCode - ESLint sourceCode object
248
- * @param {any} node - AST node that is missing the annotation
249
- * @param {any} [passedTarget] - optional AST node to use as insertion target instead of resolving from node
269
+ * @param {{ node: any; target?: any; options?: ReportOptions }} config - configuration containing the node to report, optional insertion target, and optional report options
250
270
  */
251
- function reportMissing(context, sourceCode, node, passedTarget) {
271
+ function reportMissing(context, sourceCode, config) {
272
+ const { node, target: passedTarget, options = {} } = config;
252
273
  try {
253
274
  const functionName = extractName(node && (node.id || node.key) ? node.id || node.key : node);
254
275
  if (hasStoryAnnotation(sourceCode, node)) {
@@ -259,15 +280,19 @@ function reportMissing(context, sourceCode, node, passedTarget) {
259
280
  const nameNode = (node.id && node.id.type === "Identifier" && node.id) ||
260
281
  (node.key && node.key.type === "Identifier" && node.key) ||
261
282
  node;
283
+ const effectiveTemplate = getAnnotationTemplate(options.annotationTemplateOverride);
284
+ const allowFix = shouldApplyAutoFix(options.autoFixToggle);
262
285
  context.report({
263
286
  node: nameNode,
264
287
  messageId: "missingStory",
265
288
  data: { name, functionName: name },
266
- fix: (0, require_story_core_1.createAddStoryFix)(resolvedTarget),
289
+ fix: allowFix
290
+ ? (0, require_story_core_1.createAddStoryFix)(resolvedTarget, effectiveTemplate)
291
+ : undefined,
267
292
  suggest: [
268
293
  {
269
- desc: `Add JSDoc @story annotation for function '${name}', e.g., ${ANNOTATION}`,
270
- fix: (0, require_story_core_1.createAddStoryFix)(resolvedTarget),
294
+ desc: `Add JSDoc @story annotation for function '${name}', e.g., ${effectiveTemplate}`,
295
+ fix: (0, require_story_core_1.createAddStoryFix)(resolvedTarget, effectiveTemplate),
271
296
  },
272
297
  ],
273
298
  });
@@ -290,10 +315,10 @@ function reportMissing(context, sourceCode, node, passedTarget) {
290
315
  * @req REQ-ERROR-CONTEXT - Method error reports must include functionName data for consistent error context
291
316
  * @param {Rule.RuleContext} context - ESLint rule context to report
292
317
  * @param {any} sourceCode - ESLint sourceCode object
293
- * @param {any} node - AST node that is missing the annotation
294
- * @param {any} [passedTarget] - optional AST node to use as insertion target instead of resolving from node
318
+ * @param {{ node: any; target?: any; options?: ReportOptions }} config - configuration containing the node to report, optional insertion target, and optional report options
295
319
  */
296
- function reportMethod(context, sourceCode, node, passedTarget) {
320
+ function reportMethod(context, sourceCode, config) {
321
+ const { node, target: passedTarget, options = {} } = config;
297
322
  try {
298
323
  if (hasStoryAnnotation(sourceCode, node)) {
299
324
  return;
@@ -301,15 +326,19 @@ function reportMethod(context, sourceCode, node, passedTarget) {
301
326
  const resolvedTarget = passedTarget ?? resolveTargetNode(sourceCode, node);
302
327
  const name = extractName(node);
303
328
  const nameNode = (node.key && node.key.type === "Identifier" && node.key) || node;
329
+ const effectiveTemplate = getAnnotationTemplate(options.annotationTemplateOverride);
330
+ const allowFix = shouldApplyAutoFix(options.autoFixToggle);
304
331
  context.report({
305
332
  node: nameNode,
306
333
  messageId: "missingStory",
307
334
  data: { name, functionName: name },
308
- fix: (0, require_story_core_1.createMethodFix)(resolvedTarget),
335
+ fix: allowFix
336
+ ? (0, require_story_core_1.createMethodFix)(resolvedTarget, effectiveTemplate)
337
+ : undefined,
309
338
  suggest: [
310
339
  {
311
- desc: `Add JSDoc @story annotation for function '${name}', e.g., ${ANNOTATION}`,
312
- fix: (0, require_story_core_1.createMethodFix)(resolvedTarget),
340
+ desc: `Add JSDoc @story annotation for function '${name}', e.g., ${effectiveTemplate}`,
341
+ fix: (0, require_story_core_1.createMethodFix)(resolvedTarget, effectiveTemplate),
313
342
  },
314
343
  ],
315
344
  });
@@ -27,7 +27,14 @@ function buildFunctionDeclarationVisitor(context, sourceCode, options) {
27
27
  if (!options.shouldProcessNode(node))
28
28
  return;
29
29
  const target = (0, require_story_helpers_1.resolveTargetNode)(sourceCode, node);
30
- (0, require_story_helpers_1.reportMissing)(context, sourceCode, node, target);
30
+ (0, require_story_helpers_1.reportMissing)(context, sourceCode, {
31
+ node,
32
+ target,
33
+ options: {
34
+ annotationTemplateOverride: options.annotationTemplate,
35
+ autoFixToggle: options.autoFix,
36
+ },
37
+ });
31
38
  }
32
39
  return {
33
40
  FunctionDeclaration: handleFunctionDeclaration,
@@ -55,7 +62,14 @@ function buildFunctionExpressionVisitor(context, sourceCode, options) {
55
62
  if (node.parent && node.parent.type === "MethodDefinition")
56
63
  return;
57
64
  const target = (0, require_story_helpers_1.resolveTargetNode)(sourceCode, node);
58
- (0, require_story_helpers_1.reportMissing)(context, sourceCode, node, target);
65
+ (0, require_story_helpers_1.reportMissing)(context, sourceCode, {
66
+ node,
67
+ target,
68
+ options: {
69
+ annotationTemplateOverride: options.annotationTemplate,
70
+ autoFixToggle: options.autoFix,
71
+ },
72
+ });
59
73
  }
60
74
  return {
61
75
  FunctionExpression: handleFunctionExpression,
@@ -76,7 +90,14 @@ function buildArrowFunctionVisitor(context, sourceCode, options) {
76
90
  if (!options.shouldProcessNode(node))
77
91
  return;
78
92
  const target = (0, require_story_helpers_1.resolveTargetNode)(sourceCode, node);
79
- (0, require_story_helpers_1.reportMissing)(context, sourceCode, node, target);
93
+ (0, require_story_helpers_1.reportMissing)(context, sourceCode, {
94
+ node,
95
+ target,
96
+ options: {
97
+ annotationTemplateOverride: options.annotationTemplate,
98
+ autoFixToggle: options.autoFix,
99
+ },
100
+ });
80
101
  }
81
102
  return {
82
103
  ArrowFunctionExpression: handleArrowFunctionExpression,
@@ -96,7 +117,14 @@ function buildTSDeclareFunctionVisitor(context, sourceCode, options) {
96
117
  function handleTSDeclareFunction(node) {
97
118
  if (!options.shouldProcessNode(node))
98
119
  return;
99
- (0, require_story_helpers_1.reportMissing)(context, sourceCode, node, node);
120
+ (0, require_story_helpers_1.reportMissing)(context, sourceCode, {
121
+ node,
122
+ target: node,
123
+ options: {
124
+ annotationTemplateOverride: options.annotationTemplate,
125
+ autoFixToggle: options.autoFix,
126
+ },
127
+ });
100
128
  }
101
129
  return {
102
130
  TSDeclareFunction: handleTSDeclareFunction,
@@ -117,7 +145,14 @@ function buildTSMethodSignatureVisitor(context, sourceCode, options) {
117
145
  if (!options.shouldProcessNode(node))
118
146
  return;
119
147
  const target = (0, require_story_helpers_1.resolveTargetNode)(sourceCode, node);
120
- (0, require_story_helpers_1.reportMissing)(context, sourceCode, node, target);
148
+ (0, require_story_helpers_1.reportMissing)(context, sourceCode, {
149
+ node,
150
+ target,
151
+ options: {
152
+ annotationTemplateOverride: options.methodAnnotationTemplate ?? options.annotationTemplate,
153
+ autoFixToggle: options.autoFix,
154
+ },
155
+ });
121
156
  }
122
157
  return {
123
158
  TSMethodSignature: handleTSMethodSignature,
@@ -137,7 +172,13 @@ function buildMethodDefinitionVisitor(context, sourceCode, options) {
137
172
  function handleMethodDefinition(node) {
138
173
  if (!options.shouldProcessNode(node))
139
174
  return;
140
- (0, require_story_helpers_1.reportMethod)(context, sourceCode, node);
175
+ (0, require_story_helpers_1.reportMethod)(context, sourceCode, {
176
+ node,
177
+ options: {
178
+ annotationTemplateOverride: options.methodAnnotationTemplate ?? options.annotationTemplate,
179
+ autoFixToggle: options.autoFix,
180
+ },
181
+ });
141
182
  }
142
183
  return {
143
184
  MethodDefinition: handleMethodDefinition,