eslint-plugin-traceability 1.10.1 → 1.11.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 (70) hide show
  1. package/CHANGELOG.md +2 -2
  2. package/README.md +3 -2
  3. package/lib/src/maintenance/cli.js +12 -12
  4. package/lib/src/maintenance/detect.js +19 -19
  5. package/lib/src/maintenance/flags.js +111 -25
  6. package/lib/src/rules/helpers/require-story-core.d.ts +55 -9
  7. package/lib/src/rules/helpers/require-story-core.js +85 -62
  8. package/lib/src/rules/helpers/require-story-helpers.d.ts +27 -48
  9. package/lib/src/rules/helpers/require-story-helpers.js +154 -116
  10. package/lib/src/rules/helpers/require-story-io.js +51 -31
  11. package/lib/src/rules/helpers/require-story-visitors.js +47 -6
  12. package/lib/src/rules/helpers/valid-annotation-format-validators.js +5 -1
  13. package/lib/src/rules/helpers/valid-annotation-options.d.ts +9 -0
  14. package/lib/src/rules/helpers/valid-annotation-options.js +67 -20
  15. package/lib/src/rules/helpers/valid-annotation-utils.js +31 -31
  16. package/lib/src/rules/helpers/valid-story-reference-helpers.js +19 -19
  17. package/lib/src/rules/prefer-implements-annotation.js +29 -1
  18. package/lib/src/rules/require-story-annotation.js +15 -0
  19. package/lib/src/rules/require-test-traceability.js +1 -6
  20. package/lib/src/utils/annotation-checker.js +32 -8
  21. package/lib/src/utils/reqAnnotationDetection.js +36 -22
  22. package/lib/tests/cli-error-handling.test.js +1 -0
  23. package/lib/tests/config/eslint-config-validation.test.d.ts +8 -0
  24. package/lib/tests/config/eslint-config-validation.test.js +8 -0
  25. package/lib/tests/config/flat-config-presets-integration.test.js +1 -3
  26. package/lib/tests/config/require-story-annotation-config.test.d.ts +9 -0
  27. package/lib/tests/config/require-story-annotation-config.test.js +9 -0
  28. package/lib/tests/integration/cli-integration.test.js +9 -1
  29. package/lib/tests/maintenance/batch.test.js +1 -0
  30. package/lib/tests/maintenance/cli.test.js +1 -0
  31. package/lib/tests/maintenance/detect-isolated.test.js +1 -0
  32. package/lib/tests/maintenance/detect.test.js +1 -0
  33. package/lib/tests/maintenance/index.test.js +1 -0
  34. package/lib/tests/maintenance/report.test.js +1 -0
  35. package/lib/tests/maintenance/update-isolated.test.js +1 -0
  36. package/lib/tests/maintenance/update.test.js +1 -0
  37. package/lib/tests/perf/maintenance-cli-large-workspace.test.d.ts +1 -0
  38. package/lib/tests/perf/maintenance-cli-large-workspace.test.js +130 -0
  39. package/lib/tests/perf/maintenance-large-workspace.test.d.ts +1 -0
  40. package/lib/tests/perf/maintenance-large-workspace.test.js +149 -0
  41. package/lib/tests/plugin-default-export-and-configs.test.js +2 -0
  42. package/lib/tests/plugin-setup-error.test.d.ts +1 -0
  43. package/lib/tests/plugin-setup-error.test.js +1 -0
  44. package/lib/tests/plugin-setup.test.js +1 -1
  45. package/lib/tests/rules/auto-fix-behavior-008.test.js +39 -0
  46. package/lib/tests/rules/error-reporting.test.js +1 -0
  47. package/lib/tests/rules/prefer-implements-annotation.test.js +8 -0
  48. package/lib/tests/rules/require-branch-annotation.test.js +2 -0
  49. package/lib/tests/rules/require-story-core-edgecases.test.js +1 -0
  50. package/lib/tests/rules/require-story-core.autofix.test.js +10 -3
  51. package/lib/tests/rules/require-story-core.test.js +14 -7
  52. package/lib/tests/rules/require-story-helpers-edgecases.test.d.ts +1 -0
  53. package/lib/tests/rules/require-story-helpers-edgecases.test.js +2 -1
  54. package/lib/tests/rules/require-story-helpers.test.js +18 -11
  55. package/lib/tests/rules/require-story-io-behavior.test.d.ts +1 -0
  56. package/lib/tests/rules/require-story-io-behavior.test.js +1 -0
  57. package/lib/tests/rules/require-story-io.edgecases.test.d.ts +1 -0
  58. package/lib/tests/rules/require-story-io.edgecases.test.js +1 -0
  59. package/lib/tests/rules/require-story-visitors-edgecases.test.d.ts +1 -0
  60. package/lib/tests/rules/require-story-visitors-edgecases.test.js +1 -0
  61. package/lib/tests/rules/valid-story-reference.test.js +2 -0
  62. package/lib/tests/utils/annotation-checker.test.js +2 -1
  63. package/lib/tests/utils/branch-annotation-helpers.test.js +2 -1
  64. package/lib/tests/utils/require-story-core-test-helpers.d.ts +1 -1
  65. package/lib/tests/utils/require-story-core-test-helpers.js +16 -16
  66. package/lib/tests/utils/temp-dir-helpers.js +1 -1
  67. package/package.json +9 -2
  68. package/user-docs/api-reference.md +123 -12
  69. package/user-docs/examples.md +41 -0
  70. package/user-docs/migration-guide.md +36 -3
@@ -1,17 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.EXPORT_PRIORITY_VALUES = exports.DEFAULT_SCOPE = void 0;
3
+ exports.EXPORT_PRIORITY_VALUES = exports.STORY_PATH = 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");
6
+ exports.coreReportMissing = coreReportMissing;
7
+ exports.coreReportMethod = coreReportMethod;
9
8
  /**
10
9
  * Create a fixer function that inserts a @story annotation before the target node.
11
10
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
12
11
  * @req REQ-AUTOFIX - Provide automatic fix function for missing @story annotations
13
12
  */
14
- function createAddStoryFix(target) {
13
+ function createAddStoryFix(target, annotationTemplate) {
15
14
  /**
16
15
  * Fixer that inserts a @story annotation before the target node.
17
16
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
@@ -29,7 +28,7 @@ function createAddStoryFix(target) {
29
28
  ? target.range[0]
30
29
  : 0
31
30
  : 0;
32
- return fixer.insertTextBeforeRange([start, start], `${require_story_helpers_1.ANNOTATION}\n`);
31
+ return fixer.insertTextBeforeRange([start, start], `${annotationTemplate}\n`);
33
32
  }
34
33
  return addStoryFixer;
35
34
  }
@@ -38,7 +37,7 @@ function createAddStoryFix(target) {
38
37
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
39
38
  * @req REQ-AUTOFIX - Provide automatic fix for class method annotations
40
39
  */
41
- function createMethodFix(node) {
40
+ function createMethodFix(node, annotationTemplate) {
42
41
  /**
43
42
  * Fixer that inserts a @story annotation before a method node.
44
43
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
@@ -56,7 +55,7 @@ function createMethodFix(node) {
56
55
  ? node.range[0]
57
56
  : 0
58
57
  : 0;
59
- return fixer.insertTextBeforeRange([start, start], `${require_story_helpers_1.ANNOTATION}\n `);
58
+ return fixer.insertTextBeforeRange([start, start], `${annotationTemplate}\n `);
60
59
  }
61
60
  return methodFixer;
62
61
  }
@@ -72,71 +71,95 @@ exports.DEFAULT_SCOPE = [
72
71
  "TSMethodSignature",
73
72
  "TSDeclareFunction",
74
73
  ];
74
+ /**
75
+ * Path to the story file for function-annotation helpers.
76
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
77
+ * @req REQ-ANNOTATION-REQUIRED - Provide a single source of truth for the canonical story path used by helper modules
78
+ */
79
+ exports.STORY_PATH = "docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md";
75
80
  /**
76
81
  * Allowed values for export priority option.
77
82
  */
78
83
  exports.EXPORT_PRIORITY_VALUES = ["all", "exported", "non-exported"];
79
84
  /**
80
- * Report a missing @story annotation for a general function-like node.
85
+ * Core helper to report a missing @story annotation for a function-like node.
86
+ * Delegates actual behavior to injected dependencies so higher-level helpers
87
+ * can remain small while sharing error-reporting behavior.
81
88
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
82
- * @req REQ-AUTOFIX - Report missing annotation and provide autofix using createAddStoryFix
89
+ * @story docs/stories/007.0-DEV-ERROR-REPORTING.story.md
90
+ * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
91
+ * @req REQ-ANNOTATION-REQUIRED
92
+ * @req REQ-AUTOFIX-MISSING
93
+ * @req REQ-ERROR-SPECIFIC
83
94
  */
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"))
95
+ function coreReportMissing(deps, context, sourceCode, config) {
96
+ const { node, target: passedTarget, options = {} } = config;
97
+ try {
98
+ if (deps.hasStoryAnnotation(sourceCode, node)) {
99
99
  return;
100
+ }
101
+ const functionName = deps.getReportedFunctionName(node);
102
+ const resolvedTarget = deps.resolveAnnotationTargetNode(sourceCode, node, passedTarget);
103
+ const nameNode = deps.getNameNodeForReport(node);
104
+ const { effectiveTemplate, allowFix } = deps.buildTemplateConfig(options);
105
+ const name = functionName;
106
+ context.report({
107
+ node: nameNode,
108
+ messageId: "missingStory",
109
+ data: { name, functionName: name },
110
+ fix: allowFix
111
+ ? deps.createAddStoryFix(resolvedTarget, effectiveTemplate)
112
+ : undefined,
113
+ suggest: [
114
+ {
115
+ desc: `Add JSDoc @story annotation for function '${name}', e.g., ${effectiveTemplate}`,
116
+ fix: deps.createAddStoryFix(resolvedTarget, effectiveTemplate),
117
+ },
118
+ ],
119
+ });
120
+ }
121
+ catch {
122
+ /* noop */
100
123
  }
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
124
  }
122
125
  /**
123
- * Report missing @story annotation for methods
126
+ * Core helper to report a missing @story annotation for a method-like node.
127
+ * Delegates actual behavior to injected dependencies while keeping this
128
+ * module focused on core error-reporting behavior.
124
129
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
125
- * @req REQ-AUTOFIX - Provide automatic fix for class method annotations
130
+ * @story docs/stories/007.0-DEV-ERROR-REPORTING.story.md
131
+ * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
132
+ * @req REQ-ANNOTATION-REQUIRED
133
+ * @req REQ-AUTOFIX-MISSING
134
+ * @req REQ-ERROR-SPECIFIC
126
135
  */
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
- });
136
+ function coreReportMethod(deps, context, sourceCode, config) {
137
+ const { node, target: passedTarget, options = {} } = config;
138
+ try {
139
+ if (deps.hasStoryAnnotation(sourceCode, node)) {
140
+ return;
141
+ }
142
+ const resolvedTarget = passedTarget ?? deps.resolveAnnotationTargetNode(sourceCode, node, null);
143
+ const name = deps.extractName(node);
144
+ const nameNode = (node.key && node.key.type === "Identifier" && node.key) || node;
145
+ const effectiveTemplate = deps.getAnnotationTemplate(options.annotationTemplateOverride);
146
+ const allowFix = deps.shouldApplyAutoFix(options.autoFixToggle);
147
+ context.report({
148
+ node: nameNode,
149
+ messageId: "missingStory",
150
+ data: { name, functionName: name },
151
+ fix: allowFix
152
+ ? deps.createMethodFix(resolvedTarget, effectiveTemplate)
153
+ : undefined,
154
+ suggest: [
155
+ {
156
+ desc: `Add JSDoc @story annotation for function '${name}', e.g., ${effectiveTemplate}`,
157
+ fix: deps.createMethodFix(resolvedTarget, effectiveTemplate),
158
+ },
159
+ ],
160
+ });
161
+ }
162
+ catch {
163
+ /* noop */
164
+ }
142
165
  }
@@ -1,29 +1,27 @@
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";
8
12
  import { getNodeName } from "./require-story-utils";
9
- import { DEFAULT_SCOPE, EXPORT_PRIORITY_VALUES } from "./require-story-core";
10
- /**
11
- * Path to the story file for annotations
12
- */
13
- 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 */";
13
+ import { DEFAULT_SCOPE, EXPORT_PRIORITY_VALUES, STORY_PATH } from "./require-story-core";
15
14
  /**
16
- * Number of physical source lines to inspect before a node when searching for @story text
17
- * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
18
- * @req REQ-ANNOTATION-REQUIRED - Replace magic number for lookback lines with named constant
15
+ * Derive the annotation template, optionally using an override.
16
+ * When override is a non-empty string, its trimmed value is used.
17
+ * Otherwise, the default template is returned.
19
18
  */
20
- declare const LOOKBACK_LINES = 4;
19
+ declare function getAnnotationTemplate(override?: string): string;
21
20
  /**
22
- * Window (in characters) to inspect before a node as a fallback when searching for @story text
23
- * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
24
- * @req REQ-ANNOTATION-REQUIRED - Replace magic number for fallback text window with named constant
21
+ * Determine whether auto-fix should be applied.
22
+ * Explicit false disables auto-fix; all other values enable it.
25
23
  */
26
- declare const FALLBACK_WINDOW = 800;
24
+ declare function shouldApplyAutoFix(autoFix: boolean | undefined): boolean;
27
25
  /**
28
26
  * Determine if a node is in an export declaration
29
27
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
@@ -96,42 +94,23 @@ declare function extractName(node: any): string;
96
94
  * @returns {boolean} whether node should be processed
97
95
  */
98
96
  declare function shouldProcessNode(node: any, scope: string[], exportPriority?: string): boolean;
99
- /**
100
- * Report a missing @story annotation for a function-like node
101
- * Provides a suggestion to add the annotation.
102
- * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
103
- * @story docs/stories/007.0-DEV-ERROR-REPORTING.story.md
104
- * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
105
- * @req REQ-ANNOTATION-REQUIRED - Implement reporting for missing annotations with suggestion
106
- * @req REQ-AUTOFIX-MISSING - Provide autofix for missing annotations while preserving suggestions
107
- * @req REQ-ERROR-SPECIFIC - Error reports must include both name and functionName in the data payload for specific function context
108
- * @param {Rule.RuleContext} context - ESLint rule context used to report
109
- * @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
112
- */
113
- declare function reportMissing(context: Rule.RuleContext, sourceCode: any, node: any, passedTarget?: any): void;
114
- /**
115
- * Report a missing @story annotation for a method-like node
116
- * Provides a suggestion to update the method/interface with the annotation.
117
- * The error data payload uses both name and functionName for consistent, specific error context.
118
- * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
119
- * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
120
- * @story docs/stories/007.0-DEV-ERROR-REPORTING.story.md
121
- * @req REQ-ANNOTATION-REQUIRED - Implement reporting for missing method/interface annotations with suggestion
122
- * @req REQ-AUTOFIX-MISSING - Provide autofix for missing method/interface annotations while preserving suggestions
123
- * @req REQ-ERROR-SPECIFIC - Method error reports must include both name and functionName in the data payload for specific function context
124
- * @req REQ-ERROR-LOCATION - Method error reports must use the method name node to anchor error location
125
- * @req REQ-ERROR-CONTEXT - Method error reports must include functionName data for consistent error context
126
- * @param {Rule.RuleContext} context - ESLint rule context to report
127
- * @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
130
- */
131
- declare function reportMethod(context: Rule.RuleContext, sourceCode: any, node: any, passedTarget?: any): void;
97
+ interface ReportOptions {
98
+ annotationTemplateOverride?: string;
99
+ autoFixToggle?: boolean;
100
+ }
101
+ declare function reportMissing(context: Rule.RuleContext, sourceCode: any, config: {
102
+ node: any;
103
+ target?: any;
104
+ options?: ReportOptions;
105
+ }): void;
106
+ declare function reportMethod(context: Rule.RuleContext, sourceCode: any, config: {
107
+ node: any;
108
+ target?: any;
109
+ options?: ReportOptions;
110
+ }): void;
132
111
  /**
133
112
  * Explicit exports for require-story-annotation consumers
134
113
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
135
114
  * @req REQ-ANNOTATION-REQUIRED - Explicitly export helper functions and constants used by requiring modules
136
115
  */
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, };
116
+ export { STORY_PATH, getAnnotationTemplate, shouldApplyAutoFix, isExportedNode, jsdocHasStory, commentsBeforeHasStory, leadingCommentsHasStory, hasStoryAnnotation, getNodeName, extractName, resolveTargetNode, shouldProcessNode, DEFAULT_SCOPE, EXPORT_PRIORITY_VALUES, linesBeforeHasStory, parentChainHasStory, fallbackTextBeforeHasStory, reportMissing, reportMethod, };
@@ -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.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;
@@ -20,27 +22,28 @@ Object.defineProperty(exports, "getNodeName", { enumerable: true, get: function
20
22
  const require_story_core_1 = require("./require-story-core");
21
23
  Object.defineProperty(exports, "DEFAULT_SCOPE", { enumerable: true, get: function () { return require_story_core_1.DEFAULT_SCOPE; } });
22
24
  Object.defineProperty(exports, "EXPORT_PRIORITY_VALUES", { enumerable: true, get: function () { return require_story_core_1.EXPORT_PRIORITY_VALUES; } });
25
+ Object.defineProperty(exports, "STORY_PATH", { enumerable: true, get: function () { return require_story_core_1.STORY_PATH; } });
23
26
  /**
24
- * Path to the story file for annotations
27
+ * Derive the annotation template, optionally using an override.
28
+ * When override is a non-empty string, its trimmed value is used.
29
+ * Otherwise, the default template is returned.
25
30
  */
26
- const STORY_PATH = "docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md";
27
- exports.STORY_PATH = STORY_PATH;
28
- const ANNOTATION = `/** @story ${STORY_PATH} */`;
29
- exports.ANNOTATION = ANNOTATION;
30
- /**
31
- * Number of physical source lines to inspect before a node when searching for @story text
32
- * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
33
- * @req REQ-ANNOTATION-REQUIRED - Replace magic number for lookback lines with named constant
34
- */
35
- const LOOKBACK_LINES = 4;
36
- exports.LOOKBACK_LINES = LOOKBACK_LINES;
31
+ function getAnnotationTemplate(override) {
32
+ if (typeof override === "string" && override.trim().length > 0) {
33
+ return override.trim();
34
+ }
35
+ return `/** @story ${require_story_core_1.STORY_PATH} */`;
36
+ }
37
37
  /**
38
- * Window (in characters) to inspect before a node as a fallback when searching for @story text
39
- * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
40
- * @req REQ-ANNOTATION-REQUIRED - Replace magic number for fallback text window with named constant
38
+ * Determine whether auto-fix should be applied.
39
+ * Explicit false disables auto-fix; all other values enable it.
41
40
  */
42
- const FALLBACK_WINDOW = 800;
43
- exports.FALLBACK_WINDOW = FALLBACK_WINDOW;
41
+ function shouldApplyAutoFix(autoFix) {
42
+ if (autoFix === false) {
43
+ return false;
44
+ }
45
+ return true;
46
+ }
44
47
  /**
45
48
  * Determine if a node is in an export declaration
46
49
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
@@ -126,7 +129,7 @@ function hasStoryAnnotation(sourceCode, node) {
126
129
  if (leadingCommentsHasStory(node)) {
127
130
  return true;
128
131
  }
129
- if ((0, require_story_io_1.linesBeforeHasStory)(sourceCode, node, LOOKBACK_LINES)) {
132
+ if ((0, require_story_io_1.linesBeforeHasStory)(sourceCode, node)) {
130
133
  return true;
131
134
  }
132
135
  if ((0, require_story_io_1.parentChainHasStory)(sourceCode, node)) {
@@ -173,6 +176,54 @@ function resolveTargetNode(sourceCode, node) {
173
176
  }
174
177
  return node;
175
178
  }
179
+ /**
180
+ * Extract a direct Identifier name when available on the given node.
181
+ * This focuses only on plain Identifier nodes and ignores container shapes.
182
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
183
+ * @req REQ-ANNOTATION-REQUIRED - Extract direct Identifier-based names from nodes
184
+ * @param {any} node - AST node to inspect
185
+ * @returns {string | null} identifier name or null when not applicable
186
+ */
187
+ function getDirectIdentifierName(node) {
188
+ if (node &&
189
+ node.type === "Identifier" &&
190
+ typeof node.name === "string" &&
191
+ node.name.length > 0) {
192
+ return node.name;
193
+ }
194
+ return null;
195
+ }
196
+ /**
197
+ * Normalize container nodes that expose names via id/key properties.
198
+ * Supports common function and method containers, including literal keys.
199
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
200
+ * @req REQ-ANNOTATION-REQUIRED - Normalize container id/key-based names into a single helper
201
+ * @param {any} node - AST node that may contain id/key name information
202
+ * @returns {string | null} resolved container name or null when unavailable
203
+ */
204
+ function getContainerKeyOrIdName(node) {
205
+ if (!node) {
206
+ return null;
207
+ }
208
+ if (node.id) {
209
+ const idName = (0, require_story_utils_1.getNodeName)(node.id);
210
+ if (typeof idName === "string" && idName.length > 0) {
211
+ return idName;
212
+ }
213
+ }
214
+ if (node.key) {
215
+ const keyName = (0, require_story_utils_1.getNodeName)(node.key);
216
+ if (typeof keyName === "string" && keyName.length > 0) {
217
+ return keyName;
218
+ }
219
+ if (node.key.type === "Literal" &&
220
+ typeof node.key.value === "string" &&
221
+ node.key.value.length > 0) {
222
+ return node.key.value;
223
+ }
224
+ }
225
+ return null;
226
+ }
176
227
  /**
177
228
  * Small utility to walk the node and its parents to extract an Identifier or key name.
178
229
  * Walks up the parent chain and inspects common properties (id, key, name, Identifier nodes).
@@ -182,33 +233,21 @@ function resolveTargetNode(sourceCode, node) {
182
233
  * @returns {string} extracted name or "(anonymous)" when no name found
183
234
  */
184
235
  function extractName(node) {
185
- let n = node;
186
- while (n) {
187
- // Direct Identifier node
188
- if (n.type === "Identifier" && typeof n.name === "string") {
189
- return n.name;
190
- }
191
- // id property (FunctionDeclaration, etc.)
192
- if (n.id && n.id.type === "Identifier" && typeof n.id.name === "string") {
193
- return n.id.name;
236
+ let current = node;
237
+ while (current) {
238
+ const directIdentifierName = getDirectIdentifierName(current);
239
+ if (directIdentifierName) {
240
+ return directIdentifierName;
194
241
  }
195
- // key property (Property, MethodDefinition, etc.)
196
- if (n.key &&
197
- n.key.type === "Identifier" &&
198
- typeof n.key.name === "string") {
199
- return n.key.name;
242
+ const containerName = getContainerKeyOrIdName(current);
243
+ if (containerName) {
244
+ return containerName;
200
245
  }
201
- // name property (some nodes may have a 'name' string directly)
202
- if (typeof n.name === "string" && n.name.length > 0) {
203
- return n.name;
246
+ const directName = current.name;
247
+ if (typeof directName === "string" && directName.length > 0) {
248
+ return directName;
204
249
  }
205
- // computed keys may have an Identifier inside
206
- if (n.key &&
207
- n.key.type === "Literal" &&
208
- typeof n.key.value === "string") {
209
- return n.key.value;
210
- }
211
- n = n.parent;
250
+ current = current.parent;
212
251
  }
213
252
  return "(anonymous)";
214
253
  }
@@ -235,86 +274,85 @@ function shouldProcessNode(node, scope, exportPriority = "all") {
235
274
  return true;
236
275
  }
237
276
  /**
238
- * Report a missing @story annotation for a function-like node
239
- * Provides a suggestion to add the annotation.
277
+ * Resolve the effective function name to report for a node.
278
+ * Normalizes id/key handling before delegating to extractName.
240
279
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
241
- * @story docs/stories/007.0-DEV-ERROR-REPORTING.story.md
242
- * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
243
- * @req REQ-ANNOTATION-REQUIRED - Implement reporting for missing annotations with suggestion
244
- * @req REQ-AUTOFIX-MISSING - Provide autofix for missing annotations while preserving suggestions
245
- * @req REQ-ERROR-SPECIFIC - Error reports must include both name and functionName in the data payload for specific function context
246
- * @param {Rule.RuleContext} context - ESLint rule context used to report
247
- * @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
280
+ * @req REQ-ANNOTATION-REQUIRED - Centralize reported function name resolution
281
+ * @param {any} node - AST node used to derive the function name
282
+ * @returns {string} resolved function name
250
283
  */
251
- function reportMissing(context, sourceCode, node, passedTarget) {
252
- try {
253
- const functionName = extractName(node && (node.id || node.key) ? node.id || node.key : node);
254
- if (hasStoryAnnotation(sourceCode, node)) {
255
- return;
256
- }
257
- const resolvedTarget = passedTarget ?? resolveTargetNode(sourceCode, node);
258
- const name = functionName;
259
- const nameNode = (node.id && node.id.type === "Identifier" && node.id) ||
260
- (node.key && node.key.type === "Identifier" && node.key) ||
261
- node;
262
- context.report({
263
- node: nameNode,
264
- messageId: "missingStory",
265
- data: { name, functionName: name },
266
- fix: (0, require_story_core_1.createAddStoryFix)(resolvedTarget),
267
- suggest: [
268
- {
269
- desc: `Add JSDoc @story annotation for function '${name}', e.g., ${ANNOTATION}`,
270
- fix: (0, require_story_core_1.createAddStoryFix)(resolvedTarget),
271
- },
272
- ],
273
- });
284
+ function getReportedFunctionName(node) {
285
+ const candidate = node && (node.id || node.key) ? node.id || node.key : node;
286
+ return extractName(candidate);
287
+ }
288
+ /**
289
+ * Determine the most appropriate AST node to anchor error location for a report.
290
+ * Prefers Identifier nodes from id/key properties when available.
291
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
292
+ * @req REQ-ANNOTATION-REQUIRED - Normalize name node selection for error reporting
293
+ * @param {any} node - AST node used for error anchoring
294
+ * @returns {any} node to use as the report location
295
+ */
296
+ function getNameNodeForReport(node) {
297
+ if (node?.id?.type === "Identifier") {
298
+ return node.id;
274
299
  }
275
- catch {
276
- /* noop */
300
+ if (node?.key?.type === "Identifier") {
301
+ return node.key;
277
302
  }
303
+ return node;
278
304
  }
279
305
  /**
280
- * Report a missing @story annotation for a method-like node
281
- * Provides a suggestion to update the method/interface with the annotation.
282
- * The error data payload uses both name and functionName for consistent, specific error context.
306
+ * Resolve the node that should receive the @story annotation,
307
+ * respecting an explicitly passed target when provided.
283
308
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
284
- * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
285
- * @story docs/stories/007.0-DEV-ERROR-REPORTING.story.md
286
- * @req REQ-ANNOTATION-REQUIRED - Implement reporting for missing method/interface annotations with suggestion
287
- * @req REQ-AUTOFIX-MISSING - Provide autofix for missing method/interface annotations while preserving suggestions
288
- * @req REQ-ERROR-SPECIFIC - Method error reports must include both name and functionName in the data payload for specific function context
289
- * @req REQ-ERROR-LOCATION - Method error reports must use the method name node to anchor error location
290
- * @req REQ-ERROR-CONTEXT - Method error reports must include functionName data for consistent error context
291
- * @param {Rule.RuleContext} context - ESLint rule context to report
309
+ * @req REQ-ANNOTATION-REQUIRED - Centralize annotation target node resolution
292
310
  * @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
311
+ * @param {any} node - original function-like AST node
312
+ * @param {any} passedTarget - optional explicit annotation target
313
+ * @returns {any} node that should receive the annotation
295
314
  */
296
- function reportMethod(context, sourceCode, node, passedTarget) {
297
- try {
298
- if (hasStoryAnnotation(sourceCode, node)) {
299
- return;
300
- }
301
- const resolvedTarget = passedTarget ?? resolveTargetNode(sourceCode, node);
302
- const name = extractName(node);
303
- const nameNode = (node.key && node.key.type === "Identifier" && node.key) || node;
304
- context.report({
305
- node: nameNode,
306
- messageId: "missingStory",
307
- data: { name, functionName: name },
308
- fix: (0, require_story_core_1.createMethodFix)(resolvedTarget),
309
- suggest: [
310
- {
311
- desc: `Add JSDoc @story annotation for function '${name}', e.g., ${ANNOTATION}`,
312
- fix: (0, require_story_core_1.createMethodFix)(resolvedTarget),
313
- },
314
- ],
315
- });
316
- }
317
- catch {
318
- /* noop */
319
- }
315
+ function resolveAnnotationTargetNode(sourceCode, node, passedTarget) {
316
+ return passedTarget ?? resolveTargetNode(sourceCode, node);
317
+ }
318
+ /**
319
+ * Build the effective annotation template and autofix toggle
320
+ * from the provided report options.
321
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
322
+ * @req REQ-ANNOTATION-REQUIRED - Normalize template and autofix configuration
323
+ * @param {ReportOptions} [options] - optional report configuration
324
+ * @returns {{ effectiveTemplate: string; allowFix: boolean }} template and autofix flags
325
+ */
326
+ function buildTemplateConfig(options) {
327
+ const effectiveTemplate = getAnnotationTemplate(options?.annotationTemplateOverride);
328
+ const allowFix = shouldApplyAutoFix(options?.autoFixToggle);
329
+ return { effectiveTemplate, allowFix };
330
+ }
331
+ function reportMissing(context, sourceCode, config) {
332
+ (0, require_story_core_1.coreReportMissing)({
333
+ hasStoryAnnotation,
334
+ getReportedFunctionName,
335
+ resolveAnnotationTargetNode,
336
+ getNameNodeForReport,
337
+ buildTemplateConfig,
338
+ extractName,
339
+ getAnnotationTemplate,
340
+ shouldApplyAutoFix,
341
+ createAddStoryFix: require_story_core_1.createAddStoryFix,
342
+ createMethodFix: require_story_core_1.createMethodFix,
343
+ }, context, sourceCode, config);
344
+ }
345
+ function reportMethod(context, sourceCode, config) {
346
+ (0, require_story_core_1.coreReportMethod)({
347
+ hasStoryAnnotation,
348
+ getReportedFunctionName,
349
+ resolveAnnotationTargetNode,
350
+ getNameNodeForReport,
351
+ buildTemplateConfig,
352
+ extractName,
353
+ getAnnotationTemplate,
354
+ shouldApplyAutoFix,
355
+ createAddStoryFix: require_story_core_1.createAddStoryFix,
356
+ createMethodFix: require_story_core_1.createMethodFix,
357
+ }, context, sourceCode, config);
320
358
  }