eslint-plugin-traceability 1.21.0 → 1.22.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 (83) hide show
  1. package/CHANGELOG.md +7 -2
  2. package/README.md +6 -6
  3. package/lib/src/maintenance/batch.js +0 -1
  4. package/lib/src/maintenance/cli.js +8 -10
  5. package/lib/src/maintenance/commands.d.ts +2 -2
  6. package/lib/src/maintenance/commands.js +2 -2
  7. package/lib/src/maintenance/detect.js +7 -7
  8. package/lib/src/maintenance/report.js +2 -2
  9. package/lib/src/maintenance/storyParser.d.ts +16 -0
  10. package/lib/src/maintenance/storyParser.js +167 -0
  11. package/lib/src/rules/helpers/pattern-validators.d.ts +42 -0
  12. package/lib/src/rules/helpers/pattern-validators.js +65 -0
  13. package/lib/src/rules/helpers/prefer-implements-inline.d.ts +16 -0
  14. package/lib/src/rules/helpers/prefer-implements-inline.js +146 -0
  15. package/lib/src/rules/helpers/require-story-comment-detection.d.ts +47 -0
  16. package/lib/src/rules/helpers/require-story-comment-detection.js +141 -0
  17. package/lib/src/rules/helpers/require-story-core.d.ts +6 -6
  18. package/lib/src/rules/helpers/require-story-core.js +10 -10
  19. package/lib/src/rules/helpers/require-story-helpers.d.ts +5 -63
  20. package/lib/src/rules/helpers/require-story-helpers.js +29 -337
  21. package/lib/src/rules/helpers/require-story-io.js +1 -0
  22. package/lib/src/rules/helpers/require-story-name-extraction.d.ts +35 -0
  23. package/lib/src/rules/helpers/require-story-name-extraction.js +107 -0
  24. package/lib/src/rules/helpers/require-story-node-utils.d.ts +43 -0
  25. package/lib/src/rules/helpers/require-story-node-utils.js +115 -0
  26. package/lib/src/rules/helpers/require-test-traceability-helpers.js +1 -0
  27. package/lib/src/rules/helpers/valid-annotation-format-internal.d.ts +2 -2
  28. package/lib/src/rules/helpers/valid-annotation-format-internal.js +13 -5
  29. package/lib/src/rules/helpers/valid-annotation-format-validators.d.ts +14 -14
  30. package/lib/src/rules/helpers/valid-annotation-format-validators.js +31 -22
  31. package/lib/src/rules/helpers/valid-annotation-options.d.ts +0 -10
  32. package/lib/src/rules/helpers/valid-annotation-options.js +22 -92
  33. package/lib/src/rules/helpers/valid-annotation-utils.js +1 -0
  34. package/lib/src/rules/helpers/valid-req-reference-helpers.js +1 -1
  35. package/lib/src/rules/no-redundant-annotation.js +4 -238
  36. package/lib/src/rules/prefer-implements-annotation.d.ts +12 -0
  37. package/lib/src/rules/prefer-implements-annotation.js +9 -164
  38. package/lib/src/rules/require-traceability.d.ts +8 -0
  39. package/lib/src/rules/require-traceability.js +8 -0
  40. package/lib/src/rules/valid-annotation-format.js +14 -10
  41. package/lib/src/utils/annotation-checker.d.ts +3 -2
  42. package/lib/src/utils/annotation-checker.js +4 -2
  43. package/lib/src/utils/branch-annotation-catch-helpers.d.ts +22 -0
  44. package/lib/src/utils/branch-annotation-catch-helpers.js +70 -0
  45. package/lib/src/utils/branch-annotation-helpers.js +11 -187
  46. package/lib/src/utils/branch-annotation-if-helpers.d.ts +1 -0
  47. package/lib/src/utils/branch-annotation-if-helpers.js +59 -0
  48. package/lib/src/utils/branch-annotation-indent-helpers.d.ts +1 -1
  49. package/lib/src/utils/branch-annotation-switch-helpers.d.ts +8 -2
  50. package/lib/src/utils/branch-annotation-switch-helpers.js +10 -4
  51. package/lib/src/utils/branch-validation.d.ts +9 -0
  52. package/lib/src/utils/branch-validation.js +58 -0
  53. package/lib/src/utils/comment-text-helpers.d.ts +31 -0
  54. package/lib/src/utils/comment-text-helpers.js +54 -0
  55. package/lib/src/utils/redundancy-detector.d.ts +85 -0
  56. package/lib/src/utils/redundancy-detector.js +235 -0
  57. package/lib/src/utils/reqAnnotationDetection.js +1 -0
  58. package/lib/tests/config/eslint-config-validation.test.js +1 -0
  59. package/lib/tests/config/flat-config-presets-integration.test.js +1 -0
  60. package/lib/tests/config/require-story-annotation-config.test.js +1 -0
  61. package/lib/tests/fixtures/stale/example.js +1 -0
  62. package/lib/tests/fixtures/update/example.js +1 -0
  63. package/lib/tests/integration/annotation-placement-inside-prettier.integration.test.js +1 -0
  64. package/lib/tests/integration/catch-annotation-prettier.integration.test.js +1 -0
  65. package/lib/tests/integration/else-if-annotation-prettier.integration.test.js +1 -0
  66. package/lib/tests/integration/prettier-test-helpers.js +1 -0
  67. package/lib/tests/integration/require-traceability-test-callbacks.integration.test.js +1 -0
  68. package/lib/tests/maintenance/detect-isolated.test.js +1 -0
  69. package/lib/tests/maintenance/storyParser.test.d.ts +8 -0
  70. package/lib/tests/maintenance/storyParser.test.js +505 -0
  71. package/lib/tests/perf/maintenance-large-workspace.test.js +1 -0
  72. package/lib/tests/perf/valid-annotation-format-large-file.test.js +1 -0
  73. package/lib/tests/plugin-setup.test.js +1 -0
  74. package/lib/tests/rules/error-reporting.test.js +1 -0
  75. package/lib/tests/rules/no-redundant-annotation.test.js +1 -0
  76. package/lib/tests/rules/require-story-helpers.test.js +3 -2
  77. package/lib/tests/rules/require-test-traceability.test.js +1 -0
  78. package/lib/tests/rules/valid-req-reference.test.js +2 -0
  79. package/lib/tests/utils/branch-annotation-catch-insert-position.test.js +1 -0
  80. package/lib/tests/utils/branch-annotation-else-if-insert-position.test.js +1 -0
  81. package/lib/tests/utils/branch-annotation-helpers.test.js +1 -0
  82. package/package.json +18 -10
  83. package/user-docs/api-reference.md +2 -2
@@ -1,16 +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.STORY_PATH = void 0;
3
+ exports.fallbackTextBeforeHasStory = exports.parentChainHasStory = exports.linesBeforeHasStory = exports.EXPORT_PRIORITY_VALUES = exports.DEFAULT_SCOPE = exports.resolveTargetNode = exports.extractName = exports.hasStoryAnnotationWithPlacement = exports.hasStoryAnnotation = exports.leadingCommentsHasStory = exports.commentsBeforeHasStory = exports.jsdocHasStory = exports.isExportedNode = exports.STORY_PATH = void 0;
4
4
  exports.getAnnotationTemplate = getAnnotationTemplate;
5
5
  exports.shouldApplyAutoFix = shouldApplyAutoFix;
6
- exports.isExportedNode = isExportedNode;
7
- exports.jsdocHasStory = jsdocHasStory;
8
- exports.commentsBeforeHasStory = commentsBeforeHasStory;
9
- exports.leadingCommentsHasStory = leadingCommentsHasStory;
10
- exports.hasStoryAnnotation = hasStoryAnnotation;
11
- exports.hasStoryAnnotationWithPlacement = hasStoryAnnotationWithPlacement;
12
- exports.extractName = extractName;
13
- exports.resolveTargetNode = resolveTargetNode;
14
6
  exports.shouldProcessNode = shouldProcessNode;
15
7
  exports.reportMissing = reportMissing;
16
8
  exports.reportMethod = reportMethod;
@@ -18,14 +10,22 @@ const require_story_io_1 = require("./require-story-io");
18
10
  Object.defineProperty(exports, "linesBeforeHasStory", { enumerable: true, get: function () { return require_story_io_1.linesBeforeHasStory; } });
19
11
  Object.defineProperty(exports, "parentChainHasStory", { enumerable: true, get: function () { return require_story_io_1.parentChainHasStory; } });
20
12
  Object.defineProperty(exports, "fallbackTextBeforeHasStory", { enumerable: true, get: function () { return require_story_io_1.fallbackTextBeforeHasStory; } });
21
- const require_story_utils_1 = require("./require-story-utils");
22
- Object.defineProperty(exports, "getNodeName", { enumerable: true, get: function () { return require_story_utils_1.getNodeName; } });
23
- const function_annotation_helpers_1 = require("../../utils/function-annotation-helpers");
24
13
  const require_story_core_1 = require("./require-story-core");
25
14
  Object.defineProperty(exports, "DEFAULT_SCOPE", { enumerable: true, get: function () { return require_story_core_1.DEFAULT_SCOPE; } });
26
15
  Object.defineProperty(exports, "EXPORT_PRIORITY_VALUES", { enumerable: true, get: function () { return require_story_core_1.EXPORT_PRIORITY_VALUES; } });
27
16
  Object.defineProperty(exports, "STORY_PATH", { enumerable: true, get: function () { return require_story_core_1.STORY_PATH; } });
28
17
  const test_callback_exclusion_1 = require("./test-callback-exclusion");
18
+ const require_story_name_extraction_1 = require("./require-story-name-extraction");
19
+ Object.defineProperty(exports, "extractName", { enumerable: true, get: function () { return require_story_name_extraction_1.extractName; } });
20
+ const require_story_node_utils_1 = require("./require-story-node-utils");
21
+ Object.defineProperty(exports, "isExportedNode", { enumerable: true, get: function () { return require_story_node_utils_1.isExportedNode; } });
22
+ Object.defineProperty(exports, "resolveTargetNode", { enumerable: true, get: function () { return require_story_node_utils_1.resolveTargetNode; } });
23
+ const require_story_comment_detection_1 = require("./require-story-comment-detection");
24
+ Object.defineProperty(exports, "jsdocHasStory", { enumerable: true, get: function () { return require_story_comment_detection_1.jsdocHasStory; } });
25
+ Object.defineProperty(exports, "commentsBeforeHasStory", { enumerable: true, get: function () { return require_story_comment_detection_1.commentsBeforeHasStory; } });
26
+ Object.defineProperty(exports, "leadingCommentsHasStory", { enumerable: true, get: function () { return require_story_comment_detection_1.leadingCommentsHasStory; } });
27
+ Object.defineProperty(exports, "hasStoryAnnotation", { enumerable: true, get: function () { return require_story_comment_detection_1.hasStoryAnnotation; } });
28
+ Object.defineProperty(exports, "hasStoryAnnotationWithPlacement", { enumerable: true, get: function () { return require_story_comment_detection_1.hasStoryAnnotationWithPlacement; } });
29
29
  /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
30
30
  function getAnnotationTemplate(override, _options) {
31
31
  if (typeof override === "string" && override.trim().length > 0) {
@@ -55,49 +55,6 @@ function buildTemplateConfig(options) {
55
55
  });
56
56
  return { effectiveTemplate, allowFix };
57
57
  }
58
- /**
59
- * Determine whether a node represents an anonymous arrow function expression
60
- * where the parent variable declarator has no explicit Identifier name.
61
- *
62
- * @supports docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md REQ-ARROW-FUNCTION-EXCLUDED
63
- */
64
- function isAnonymousArrowFunction(node) {
65
- return !!node && node.type === "ArrowFunctionExpression";
66
- }
67
- /**
68
- * Determine whether a function-like node is nested within another function.
69
- *
70
- * @supports docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md REQ-NESTED-FUNCTION-INHERITANCE
71
- */
72
- function isNestedFunction(node) {
73
- let current = node?.parent;
74
- while (current) {
75
- if (current.type === "FunctionDeclaration" ||
76
- current.type === "FunctionExpression" ||
77
- current.type === "ArrowFunctionExpression" ||
78
- current.type === "MethodDefinition" ||
79
- current.type === "TSDeclareFunction" ||
80
- current.type === "TSMethodSignature") {
81
- return true;
82
- }
83
- current = current.parent;
84
- }
85
- return false;
86
- }
87
- /**
88
- * Determine whether a function-like node is effectively anonymous for the
89
- * purposes of nested-function inheritance. Named functions must always carry
90
- * their own annotations, while anonymous nested functions may inherit.
91
- *
92
- * @supports docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md REQ-NESTED-FUNCTION-INHERITANCE
93
- */
94
- function isEffectivelyAnonymousFunction(node) {
95
- const name = getContainerKeyOrIdName(node) ?? getDirectIdentifierName(node);
96
- if (typeof name === "string" && name.length > 0 && name !== "(anonymous)") {
97
- return false;
98
- }
99
- return true;
100
- }
101
58
  /**
102
59
  * Determine whether a function node is required to carry its own annotation
103
60
  * according to Story 004.0-DEV-BRANCH-ANNOTATIONS rules.
@@ -117,249 +74,16 @@ function requiresOwnFunctionAnnotation(node, options) {
117
74
  }
118
75
  // Anonymous arrow functions used as callbacks are excluded from function-level
119
76
  // requirements when they are nested inside another function or method.
120
- if (isAnonymousArrowFunction(node) &&
121
- isNestedFunction(node) &&
122
- isEffectivelyAnonymousFunction(node)) {
77
+ if ((0, require_story_node_utils_1.isAnonymousArrowFunction)(node) &&
78
+ (0, require_story_node_utils_1.isNestedFunction)(node) &&
79
+ (0, require_story_node_utils_1.isEffectivelyAnonymousFunction)(node)) {
123
80
  return false;
124
81
  }
125
- if (isNestedFunction(node) && isEffectivelyAnonymousFunction(node)) {
82
+ if ((0, require_story_node_utils_1.isNestedFunction)(node) && (0, require_story_node_utils_1.isEffectivelyAnonymousFunction)(node)) {
126
83
  return false;
127
84
  }
128
85
  return true;
129
86
  }
130
- /**
131
- * Determine if a node is in an export declaration
132
- * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
133
- * @req REQ-ANNOTATION-REQUIRED - Check node ancestry to find export declarations
134
- */
135
- function isExportedNode(node) {
136
- let p = node.parent;
137
- while (p) {
138
- if (p.type === "ExportNamedDeclaration" ||
139
- p.type === "ExportDefaultDeclaration") {
140
- return true;
141
- }
142
- p = p.parent;
143
- }
144
- return false;
145
- }
146
- /**
147
- * Check whether the JSDoc associated with node contains @story
148
- * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
149
- * @req REQ-ANNOTATION-REQUIRED - Extract JSDoc based detection into helper
150
- */
151
- function jsdocHasStory(sourceCode, node) {
152
- if (typeof sourceCode?.getJSDocComment !== "function") {
153
- return false;
154
- }
155
- const jsdoc = sourceCode.getJSDocComment(node);
156
- return !!(jsdoc &&
157
- typeof jsdoc.value === "string" &&
158
- jsdoc.value.includes("@story"));
159
- }
160
- /**
161
- * Check whether comments returned by sourceCode.getCommentsBefore contain @story
162
- * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
163
- * @req REQ-ANNOTATION-REQUIRED - Extract comment-before detection into helper
164
- */
165
- function commentsBeforeHasStory(sourceCode, node) {
166
- if (typeof sourceCode?.getCommentsBefore !== "function") {
167
- return false;
168
- }
169
- const commentsBefore = sourceCode.getCommentsBefore(node) || [];
170
- return (Array.isArray(commentsBefore) &&
171
- commentsBefore.some((c) => typeof c.value === "string" && c.value.includes("@story")));
172
- }
173
- /**
174
- * Check whether leadingComments attached to the node contain @story
175
- * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
176
- * @req REQ-ANNOTATION-REQUIRED - Extract leadingComments detection into helper
177
- */
178
- function leadingCommentsHasStory(node) {
179
- const leadingComments = (node && node.leadingComments) || [];
180
- return (Array.isArray(leadingComments) &&
181
- leadingComments.some((c) => typeof c.value === "string" && c.value.includes("@story")));
182
- }
183
- /**
184
- * Check if @story annotation already present in JSDoc or preceding comments
185
- * Consolidates a variety of heuristics through smaller helpers.
186
- * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
187
- * @req REQ-ANNOTATION-REQUIRED - Detect existing story annotations in JSDoc or comments
188
- */
189
- function hasStoryAnnotation(sourceCode, node) {
190
- try {
191
- // Direct, node-local checks always apply first.
192
- if (jsdocHasStory(sourceCode, node)) {
193
- return true;
194
- }
195
- if (commentsBeforeHasStory(sourceCode, node)) {
196
- return true;
197
- }
198
- if (leadingCommentsHasStory(node)) {
199
- return true;
200
- }
201
- if (!isNestedFunction(node) && (0, require_story_io_1.linesBeforeHasStory)(sourceCode, node)) {
202
- return true;
203
- }
204
- const canInherit = isNestedFunction(node) && isEffectivelyAnonymousFunction(node);
205
- // Only nodes that are allowed to inherit annotations (e.g., nested anonymous
206
- // callbacks) may treat parent-chain comments or broad fallback text as
207
- // satisfying the annotation requirement.
208
- if (canInherit && (0, require_story_io_1.parentChainHasStory)(sourceCode, node)) {
209
- return true;
210
- }
211
- if (canInherit && (0, require_story_io_1.fallbackTextBeforeHasStory)(sourceCode, node)) {
212
- return true;
213
- }
214
- if (canInherit) {
215
- return true;
216
- }
217
- }
218
- catch (error) {
219
- if (process.env.TRACEABILITY_DEBUG === "1") {
220
- console.error("[traceability] hasStoryAnnotation failed for node", error?.message ?? error);
221
- }
222
- }
223
- return false;
224
- }
225
- /**
226
- * Placement-aware story detection helper used by core reporting.
227
- *
228
- * When annotationPlacement is "inside" and the node supports inside-brace
229
- * semantics, this helper only treats annotations found on the first
230
- * comment-only lines inside the function or method body as satisfying the
231
- * requirement. JSDoc and before-function comments are intentionally ignored so
232
- * that misplaced annotations are reported as violations under the inside
233
- * standard.
234
- *
235
- * For nodes that do not support inside placement (such as TS declarations,
236
- * signature-only nodes, or functions without block bodies), this helper
237
- * delegates to the existing hasStoryAnnotation heuristics so that they
238
- * continue to rely on before-function placement.
239
- *
240
- * @supports docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md REQ-ALL-BLOCK-TYPES REQ-INSIDE-BRACE-PLACEMENT REQ-PLACEMENT-CONFIG
241
- */
242
- function hasStoryAnnotationWithPlacement(sourceCode, node, annotationPlacement) {
243
- // Backward-compatible default: use existing heuristics when placement is
244
- // "before" or when the function does not support inside-brace semantics.
245
- if (annotationPlacement !== "inside" ||
246
- !(0, function_annotation_helpers_1.supportsInsidePlacementForFunction)(node)) {
247
- return hasStoryAnnotation(sourceCode, node);
248
- }
249
- try {
250
- const insideText = (0, function_annotation_helpers_1.getFunctionInsideBodyCommentText)(sourceCode, node);
251
- if (typeof insideText === "string" &&
252
- (insideText.includes("@story") || insideText.includes("@supports"))) {
253
- return true;
254
- }
255
- }
256
- catch (error) {
257
- if (process.env.TRACEABILITY_DEBUG === "1") {
258
- // Debug logging only when explicitly enabled for troubleshooting helper failures.
259
- console.error("[traceability] hasStoryAnnotationWithPlacement failed for node", error?.message ?? error);
260
- }
261
- }
262
- // In inside-placement mode for block-bodied functions and methods we
263
- // intentionally do not fall back to before-function heuristics; callers
264
- // should treat this as a missing annotation so that misplaced comments are
265
- // reported as violations.
266
- return false;
267
- }
268
- /**
269
- * Determine AST node where annotation should be inserted
270
- * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
271
- * @req REQ-ANNOTATION-REQUIRED - Determine correct insertion target for annotation
272
- */
273
- function resolveTargetNode(sourceCode, node) {
274
- if (node.type === "TSMethodSignature") {
275
- // Interface method signature -> insert on interface
276
- return node.parent.parent;
277
- }
278
- if (node.type === "FunctionExpression" ||
279
- node.type === "ArrowFunctionExpression") {
280
- const parent = node.parent;
281
- if (parent.type === "VariableDeclarator") {
282
- const varDecl = parent.parent;
283
- if (varDecl.parent && varDecl.parent.type === "ExportNamedDeclaration") {
284
- return varDecl.parent;
285
- }
286
- return varDecl;
287
- }
288
- if (parent.type === "ExportNamedDeclaration") {
289
- return parent;
290
- }
291
- if (parent.type === "ExpressionStatement") {
292
- return parent;
293
- }
294
- }
295
- return node;
296
- }
297
- /**
298
- * Extract a direct Identifier name when available on the given node.
299
- * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
300
- * @req REQ-ANNOTATION-REQUIRED - Extract direct Identifier-based names from nodes
301
- */
302
- function getDirectIdentifierName(node) {
303
- if (node &&
304
- node.type === "Identifier" &&
305
- typeof node.name === "string" &&
306
- node.name.length > 0) {
307
- return node.name;
308
- }
309
- return null;
310
- }
311
- /**
312
- * Normalize container nodes that expose names via id/key properties.
313
- * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
314
- * @req REQ-ANNOTATION-REQUIRED - Normalize container id/key-based names into a single helper
315
- */
316
- function getContainerKeyOrIdName(node) {
317
- if (!node) {
318
- return null;
319
- }
320
- if (node.id) {
321
- const idName = (0, require_story_utils_1.getNodeName)(node.id);
322
- if (typeof idName === "string" && idName.length > 0) {
323
- return idName;
324
- }
325
- }
326
- if (node.key) {
327
- const keyName = (0, require_story_utils_1.getNodeName)(node.key);
328
- if (typeof keyName === "string" && keyName.length > 0) {
329
- return keyName;
330
- }
331
- if (node.key.type === "Literal" &&
332
- typeof node.key.value === "string" &&
333
- node.key.value.length > 0) {
334
- return node.key.value;
335
- }
336
- }
337
- return null;
338
- }
339
- /**
340
- * Small utility to walk the node and its parents to extract an Identifier or key name.
341
- * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
342
- * @req REQ-ANNOTATION-REQUIRED - Walk node and parents to find Identifier/Key name
343
- */
344
- function extractName(node) {
345
- let current = node;
346
- while (current) {
347
- const directIdentifierName = getDirectIdentifierName(current);
348
- if (directIdentifierName) {
349
- return directIdentifierName;
350
- }
351
- const containerName = getContainerKeyOrIdName(current);
352
- if (containerName) {
353
- return containerName;
354
- }
355
- const directName = current.name;
356
- if (typeof directName === "string" && directName.length > 0) {
357
- return directName;
358
- }
359
- current = current.parent;
360
- }
361
- return "(anonymous)";
362
- }
363
87
  /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
364
88
  function shouldProcessNode(node, scope, exportPriority = "all", options) {
365
89
  if (node &&
@@ -372,7 +96,7 @@ function shouldProcessNode(node, scope, exportPriority = "all", options) {
372
96
  if (!scope.includes(node.type)) {
373
97
  return false;
374
98
  }
375
- const exported = isExportedNode(node);
99
+ const exported = (0, require_story_node_utils_1.isExportedNode)(node);
376
100
  if (exportPriority === "exported" && !exported) {
377
101
  return false;
378
102
  }
@@ -381,48 +105,16 @@ function shouldProcessNode(node, scope, exportPriority = "all", options) {
381
105
  }
382
106
  return true;
383
107
  }
384
- /**
385
- * Resolve the effective function name to report for a node.
386
- * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
387
- * @req REQ-ANNOTATION-REQUIRED - Centralize reported function name resolution
388
- */
389
- function getReportedFunctionName(node) {
390
- const candidate = node && (node.id || node.key) ? node.id || node.key : node;
391
- return extractName(candidate);
392
- }
393
- /**
394
- * Determine the most appropriate AST node to anchor error location for a report.
395
- * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
396
- * @req REQ-ANNOTATION-REQUIRED - Normalize name node selection for error reporting
397
- */
398
- function getNameNodeForReport(node) {
399
- if (node?.id?.type === "Identifier") {
400
- return node.id;
401
- }
402
- if (node?.key?.type === "Identifier") {
403
- return node.key;
404
- }
405
- return node;
406
- }
407
- /**
408
- * Resolve the node that should receive the @story annotation,
409
- * respecting an explicitly passed target when provided.
410
- * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
411
- * @req REQ-ANNOTATION-REQUIRED - Centralize annotation target node resolution
412
- */
413
- function resolveAnnotationTargetNode(sourceCode, node, passedTarget) {
414
- return passedTarget ?? resolveTargetNode(sourceCode, node);
415
- }
416
108
  /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
417
109
  function reportMissing(context, sourceCode, config) {
418
110
  (0, require_story_core_1.coreReportMissing)({
419
- hasStoryAnnotation,
420
- hasStoryAnnotationWithPlacement,
421
- getReportedFunctionName,
422
- resolveAnnotationTargetNode,
423
- getNameNodeForReport,
111
+ hasStoryAnnotation: require_story_comment_detection_1.hasStoryAnnotation,
112
+ hasStoryAnnotationWithPlacement: require_story_comment_detection_1.hasStoryAnnotationWithPlacement,
113
+ getReportedFunctionName: require_story_name_extraction_1.getReportedFunctionName,
114
+ resolveAnnotationTargetNode: require_story_node_utils_1.resolveAnnotationTargetNode,
115
+ getNameNodeForReport: require_story_name_extraction_1.getNameNodeForReport,
424
116
  buildTemplateConfig,
425
- extractName,
117
+ extractName: require_story_name_extraction_1.extractName,
426
118
  getAnnotationTemplate,
427
119
  shouldApplyAutoFix,
428
120
  createAddStoryFix: require_story_core_1.createAddStoryFix,
@@ -432,13 +124,13 @@ function reportMissing(context, sourceCode, config) {
432
124
  /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */
433
125
  function reportMethod(context, sourceCode, config) {
434
126
  (0, require_story_core_1.coreReportMethod)({
435
- hasStoryAnnotation,
436
- hasStoryAnnotationWithPlacement,
437
- getReportedFunctionName,
438
- resolveAnnotationTargetNode,
439
- getNameNodeForReport,
127
+ hasStoryAnnotation: require_story_comment_detection_1.hasStoryAnnotation,
128
+ hasStoryAnnotationWithPlacement: require_story_comment_detection_1.hasStoryAnnotationWithPlacement,
129
+ getReportedFunctionName: require_story_name_extraction_1.getReportedFunctionName,
130
+ resolveAnnotationTargetNode: require_story_node_utils_1.resolveAnnotationTargetNode,
131
+ getNameNodeForReport: require_story_name_extraction_1.getNameNodeForReport,
440
132
  buildTemplateConfig,
441
- extractName,
133
+ extractName: require_story_name_extraction_1.extractName,
442
134
  getAnnotationTemplate,
443
135
  shouldApplyAutoFix,
444
136
  createAddStoryFix: require_story_core_1.createAddStoryFix,
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
+ /* eslint-disable traceability/valid-annotation-format */
2
3
  /**
3
4
  * IO helpers for require-story detection moved to reduce helper module size
4
5
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Extract a direct Identifier name when available on the given node.
3
+ *
4
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
5
+ * @req REQ-ANNOTATION-REQUIRED - Extract direct Identifier-based names from nodes
6
+ */
7
+ export declare function getDirectIdentifierName(node: any): string | null;
8
+ /**
9
+ * Normalize container nodes that expose names via id/key properties.
10
+ *
11
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
12
+ * @req REQ-ANNOTATION-REQUIRED - Normalize container id/key-based names into a single helper
13
+ */
14
+ export declare function getContainerKeyOrIdName(node: any): string | null;
15
+ /**
16
+ * Small utility to walk the node and its parents to extract an Identifier or key name.
17
+ *
18
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
19
+ * @req REQ-ANNOTATION-REQUIRED - Walk node and parents to find Identifier/Key name
20
+ */
21
+ export declare function extractName(node: any): string;
22
+ /**
23
+ * Resolve the effective function name to report for a node.
24
+ *
25
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
26
+ * @req REQ-ANNOTATION-REQUIRED - Centralize reported function name resolution
27
+ */
28
+ export declare function getReportedFunctionName(node: any): string;
29
+ /**
30
+ * Determine the most appropriate AST node to anchor error location for a report.
31
+ *
32
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
33
+ * @req REQ-ANNOTATION-REQUIRED - Normalize name node selection for error reporting
34
+ */
35
+ export declare function getNameNodeForReport(node: any): any;
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getDirectIdentifierName = getDirectIdentifierName;
4
+ exports.getContainerKeyOrIdName = getContainerKeyOrIdName;
5
+ exports.extractName = extractName;
6
+ exports.getReportedFunctionName = getReportedFunctionName;
7
+ exports.getNameNodeForReport = getNameNodeForReport;
8
+ /**
9
+ * Name extraction utilities for require-story rule
10
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
11
+ * @req REQ-ANNOTATION-REQUIRED - File-level header for name extraction utilities
12
+ */
13
+ const require_story_utils_1 = require("./require-story-utils");
14
+ /**
15
+ * Extract a direct Identifier name when available on the given node.
16
+ *
17
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
18
+ * @req REQ-ANNOTATION-REQUIRED - Extract direct Identifier-based names from nodes
19
+ */
20
+ function getDirectIdentifierName(node) {
21
+ if (node &&
22
+ node.type === "Identifier" &&
23
+ typeof node.name === "string" &&
24
+ node.name.length > 0) {
25
+ return node.name;
26
+ }
27
+ return null;
28
+ }
29
+ /**
30
+ * Normalize container nodes that expose names via id/key properties.
31
+ *
32
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
33
+ * @req REQ-ANNOTATION-REQUIRED - Normalize container id/key-based names into a single helper
34
+ */
35
+ function getContainerKeyOrIdName(node) {
36
+ if (!node) {
37
+ return null;
38
+ }
39
+ if (node.id) {
40
+ const idName = (0, require_story_utils_1.getNodeName)(node.id);
41
+ if (typeof idName === "string" && idName.length > 0) {
42
+ return idName;
43
+ }
44
+ }
45
+ if (node.key) {
46
+ const keyName = (0, require_story_utils_1.getNodeName)(node.key);
47
+ if (typeof keyName === "string" && keyName.length > 0) {
48
+ return keyName;
49
+ }
50
+ if (node.key.type === "Literal" &&
51
+ typeof node.key.value === "string" &&
52
+ node.key.value.length > 0) {
53
+ return node.key.value;
54
+ }
55
+ }
56
+ return null;
57
+ }
58
+ /**
59
+ * Small utility to walk the node and its parents to extract an Identifier or key name.
60
+ *
61
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
62
+ * @req REQ-ANNOTATION-REQUIRED - Walk node and parents to find Identifier/Key name
63
+ */
64
+ function extractName(node) {
65
+ let current = node;
66
+ while (current) {
67
+ const directIdentifierName = getDirectIdentifierName(current);
68
+ if (directIdentifierName) {
69
+ return directIdentifierName;
70
+ }
71
+ const containerName = getContainerKeyOrIdName(current);
72
+ if (containerName) {
73
+ return containerName;
74
+ }
75
+ const directName = current.name;
76
+ if (typeof directName === "string" && directName.length > 0) {
77
+ return directName;
78
+ }
79
+ current = current.parent;
80
+ }
81
+ return "(anonymous)";
82
+ }
83
+ /**
84
+ * Resolve the effective function name to report for a node.
85
+ *
86
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
87
+ * @req REQ-ANNOTATION-REQUIRED - Centralize reported function name resolution
88
+ */
89
+ function getReportedFunctionName(node) {
90
+ const candidate = node && (node.id || node.key) ? node.id || node.key : node;
91
+ return extractName(candidate);
92
+ }
93
+ /**
94
+ * Determine the most appropriate AST node to anchor error location for a report.
95
+ *
96
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
97
+ * @req REQ-ANNOTATION-REQUIRED - Normalize name node selection for error reporting
98
+ */
99
+ function getNameNodeForReport(node) {
100
+ if (node?.id?.type === "Identifier") {
101
+ return node.id;
102
+ }
103
+ if (node?.key?.type === "Identifier") {
104
+ return node.key;
105
+ }
106
+ return node;
107
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Determine whether a node represents an anonymous arrow function expression
3
+ * where the parent variable declarator has no explicit Identifier name.
4
+ *
5
+ * @supports docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md REQ-ARROW-FUNCTION-EXCLUDED
6
+ */
7
+ export declare function isAnonymousArrowFunction(node: any): boolean;
8
+ /**
9
+ * Determine whether a function-like node is nested within another function.
10
+ *
11
+ * @supports docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md REQ-NESTED-FUNCTION-INHERITANCE
12
+ */
13
+ export declare function isNestedFunction(node: any): boolean;
14
+ /**
15
+ * Determine whether a function-like node is effectively anonymous for the
16
+ * purposes of nested-function inheritance. Named functions must always carry
17
+ * their own annotations, while anonymous nested functions may inherit.
18
+ *
19
+ * @supports docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md REQ-NESTED-FUNCTION-INHERITANCE
20
+ */
21
+ export declare function isEffectivelyAnonymousFunction(node: any): boolean;
22
+ /**
23
+ * Determine if a node is in an export declaration
24
+ *
25
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
26
+ * @req REQ-ANNOTATION-REQUIRED - Check node ancestry to find export declarations
27
+ */
28
+ export declare function isExportedNode(node: any): boolean;
29
+ /**
30
+ * Determine AST node where annotation should be inserted
31
+ *
32
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
33
+ * @req REQ-ANNOTATION-REQUIRED - Determine correct insertion target for annotation
34
+ */
35
+ export declare function resolveTargetNode(sourceCode: any, node: any): any;
36
+ /**
37
+ * Resolve the node that should receive the `@story` annotation,
38
+ * respecting an explicitly passed target when provided.
39
+ *
40
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
41
+ * @req REQ-ANNOTATION-REQUIRED - Centralize annotation target node resolution
42
+ */
43
+ export declare function resolveAnnotationTargetNode(sourceCode: any, node: any, passedTarget: any): any;