eslint-plugin-traceability 1.6.0 → 1.6.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.
@@ -1,7 +1,12 @@
1
- /**
1
+ /****
2
+ * Rule to enforce @req annotation on functions
2
3
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
3
- * @req REQ-RULE-EXPORT - Export the rule object for ESLint
4
4
  * @req REQ-ANNOTATION-REQUIRED - Require @req annotation on functions
5
+ * @req REQ-FUNCTION-DETECTION - Detect function declarations, function expressions, and method definitions (including TypeScript declarations)
6
+ * @req REQ-TYPESCRIPT-SUPPORT - Support TypeScript-specific function syntax
7
+ * @req REQ-CONFIGURABLE-SCOPE - Allow configuration of which exports are checked
8
+ * @req REQ-EXPORT-PRIORITY - Allow configuration of export priority behavior
5
9
  */
6
- declare const _default: any;
7
- export default _default;
10
+ import type { Rule } from "eslint";
11
+ declare const rule: Rule.RuleModule;
12
+ export default rule;
@@ -1,37 +1,59 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- /****
4
- * Rule to enforce @req annotation on functions
5
- * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
6
- * @req REQ-ANNOTATION-REQUIRED - Require @req annotation on functions
7
- * @req REQ-FUNCTION-DETECTION - Detect function declarations, expressions, arrow functions, and methods
8
- * @req REQ-TYPESCRIPT-SUPPORT - Support TypeScript-specific function syntax
9
- */
3
+ const require_story_helpers_1 = require("./helpers/require-story-helpers");
10
4
  const annotation_checker_1 = require("../utils/annotation-checker");
11
- /**
12
- * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
13
- * @req REQ-RULE-EXPORT - Export the rule object for ESLint
14
- * @req REQ-ANNOTATION-REQUIRED - Require @req annotation on functions
15
- */
16
- exports.default = {
5
+ const rule = {
17
6
  meta: {
18
7
  type: "problem",
19
8
  fixable: "code",
20
9
  docs: {
21
- description: "Require @req annotations on functions",
10
+ description: "Require @req annotations on function-like exports (declarations, expressions, and methods, excluding arrow functions)",
22
11
  recommended: "error",
23
12
  },
24
13
  messages: {
25
14
  missingReq: "Missing @req annotation for function '{{name}}' (REQ-ANNOTATION-REQUIRED)",
26
15
  },
27
- schema: [],
16
+ schema: [
17
+ {
18
+ type: "object",
19
+ properties: {
20
+ scope: {
21
+ type: "array",
22
+ items: {
23
+ enum: require_story_helpers_1.DEFAULT_SCOPE,
24
+ },
25
+ uniqueItems: true,
26
+ },
27
+ exportPriority: {
28
+ enum: require_story_helpers_1.EXPORT_PRIORITY_VALUES,
29
+ },
30
+ },
31
+ additionalProperties: false,
32
+ },
33
+ ],
28
34
  },
29
35
  /**
30
36
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
31
37
  * @req REQ-CREATE-HOOK - Provide create(context) hook for rule behavior
32
- * @req REQ-FUNCTION-DETECTION - Detect function declarations, expressions, arrow functions, and methods
38
+ * @req REQ-FUNCTION-DETECTION - Detect function declarations, function expressions, and method definitions (including TS-specific nodes)
39
+ * @req REQ-CONFIGURABLE-SCOPE - Respect configurable scope of which exports are checked
40
+ * @req REQ-EXPORT-PRIORITY - Respect configurable export priority when determining which nodes to check
33
41
  */
34
42
  create(context) {
43
+ const options = context.options?.[0] ?? {};
44
+ const rawScope = options?.scope;
45
+ const scope = Array.isArray(rawScope) && rawScope.length > 0 ? rawScope : require_story_helpers_1.DEFAULT_SCOPE;
46
+ const exportPriority = options?.exportPriority ?? "all";
47
+ const shouldCheck = (node) => (0, require_story_helpers_1.shouldProcessNode)(node, scope, exportPriority);
48
+ /**
49
+ * Helper to conditionally run the annotation check only when the node
50
+ * should be processed according to scope/exportPriority.
51
+ */
52
+ const runCheck = (node) => {
53
+ if (!shouldCheck(node))
54
+ return;
55
+ (0, annotation_checker_1.checkReqAnnotation)(context, node, { enableFix: false });
56
+ };
35
57
  return {
36
58
  /**
37
59
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
@@ -39,18 +61,44 @@ exports.default = {
39
61
  * @req REQ-ANNOTATION-REQUIRED - Enforce @req annotation on function declarations
40
62
  */
41
63
  FunctionDeclaration(node) {
42
- return (0, annotation_checker_1.checkReqAnnotation)(context, node);
64
+ runCheck(node);
65
+ },
66
+ /**
67
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
68
+ * @req REQ-FUNCTION-DETECTION - Detect function expressions
69
+ * @req REQ-ANNOTATION-REQUIRED - Enforce @req annotation on function expressions
70
+ */
71
+ FunctionExpression(node) {
72
+ if (node.parent && node.parent.type === "MethodDefinition") {
73
+ return;
74
+ }
75
+ runCheck(node);
43
76
  },
44
77
  /**
45
78
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
46
- * @req REQ-TYPESCRIPT-SUPPORT - Support TypeScript-specific function syntax
79
+ * @req REQ-FUNCTION-DETECTION - Detect method definitions
80
+ * @req REQ-ANNOTATION-REQUIRED - Enforce @req annotation on method definitions
47
81
  */
48
- TSDeclareFunction: (node) => (0, annotation_checker_1.checkReqAnnotation)(context, node),
82
+ MethodDefinition(node) {
83
+ runCheck(node);
84
+ },
85
+ /**
86
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
87
+ * @req REQ-TYPESCRIPT-SUPPORT - Support TypeScript declare functions
88
+ * @req REQ-ANNOTATION-REQUIRED - Enforce @req annotation on TS declare functions
89
+ */
90
+ TSDeclareFunction(node) {
91
+ runCheck(node);
92
+ },
49
93
  /**
50
94
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
51
- * @req REQ-TYPESCRIPT-SUPPORT - Support TypeScript-specific function syntax
95
+ * @req REQ-TYPESCRIPT-SUPPORT - Support TypeScript method signatures
96
+ * @req REQ-ANNOTATION-REQUIRED - Enforce @req annotation on TS method signatures
52
97
  */
53
- TSMethodSignature: (node) => (0, annotation_checker_1.checkReqAnnotation)(context, node),
98
+ TSMethodSignature(node) {
99
+ runCheck(node);
100
+ },
54
101
  };
55
102
  },
56
103
  };
104
+ exports.default = rule;
@@ -7,6 +7,9 @@
7
7
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
8
8
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
9
9
  * @req REQ-ANNOTATION-REQUIRED
10
+ * @req REQ-AUTOFIX-MISSING - This rule supports auto-fixing missing @story annotations per Story 008.0 auto-fix behavior.
11
+ * @req REQ-AUTOFIX-SAFE - Auto-fix behavior only inserts @story annotation JSDoc comments and never changes executable or runtime code.
12
+ * @req REQ-AUTOFIX-PRESERVE - Auto-fix inserts a minimal placeholder JSDoc in a way that preserves existing surrounding formatting and structure.
10
13
  */
11
14
  import type { Rule } from "eslint";
12
15
  /**
@@ -15,6 +18,7 @@ import type { Rule } from "eslint";
15
18
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
16
19
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
17
20
  * @req REQ-ANNOTATION-REQUIRED
21
+ * @req REQ-AUTOFIX-MISSING - This rule participates in auto-fix for missing @story annotations.
18
22
  */
19
23
  declare const rule: Rule.RuleModule;
20
24
  export default rule;
@@ -8,15 +8,25 @@ const require_story_helpers_1 = require("./helpers/require-story-helpers");
8
8
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
9
9
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
10
10
  * @req REQ-ANNOTATION-REQUIRED
11
+ * @req REQ-AUTOFIX-MISSING - This rule participates in auto-fix for missing @story annotations.
11
12
  */
12
13
  const rule = {
13
14
  meta: {
14
15
  type: "problem",
15
16
  docs: {
16
- description: "Require @story annotations on functions",
17
+ description: "Require @story annotations on functions and auto-fix missing annotations where possible",
17
18
  recommended: "error",
18
19
  },
19
20
  hasSuggestions: true,
21
+ /**
22
+ * Auto-fix support for inserting @story annotations.
23
+ *
24
+ * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
25
+ * @req REQ-ANNOTATION-REQUIRED
26
+ * @req REQ-AUTOFIX-MISSING - `fixable: \"code\"` is used to implement REQ-AUTOFIX-MISSING for missing @story annotations.
27
+ * @req REQ-AUTOFIX-SAFE - Auto-fix is conservative and only adds a single-line JSDoc @story annotation without modifying existing runtime expressions.
28
+ * @req REQ-AUTOFIX-PRESERVE - Auto-fix behavior preserves surrounding code formatting and indentation when inserting the placeholder JSDoc.
29
+ */
20
30
  fixable: "code",
21
31
  messages: {
22
32
  missingStory: "Missing @story annotation for function '{{name}}' (REQ-ANNOTATION-REQUIRED)",
@@ -42,6 +52,7 @@ const rule = {
42
52
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
43
53
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
44
54
  * @req REQ-CREATE-HOOK
55
+ * @req REQ-AUTOFIX-MISSING - The create hook wires in visitors that are capable of providing auto-fix suggestions for missing @story annotations.
45
56
  */
46
57
  create(context) {
47
58
  const sourceCode = context.getSourceCode();
@@ -8,6 +8,7 @@ const STORY_EXAMPLE_PATH = "docs/stories/005.0-DEV-EXAMPLE.story.md";
8
8
  * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
9
9
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
10
10
  * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
11
+ * @req REQ-AUTOFIX-PRESERVE - Avoid risky text replacements when the annotation tag cannot be located
11
12
  */
12
13
  const TAG_NOT_FOUND_INDEX = -1;
13
14
  /**
@@ -89,6 +90,8 @@ function buildReqErrorMessage(kind, value) {
89
90
  *
90
91
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
91
92
  * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
93
+ * @req REQ-AUTOFIX-SAFE - Auto-fix must be conservative and never broaden the referenced path
94
+ * @req REQ-AUTOFIX-PRESERVE - Preserve surrounding formatting when normalizing story path suffixes
92
95
  */
93
96
  function getFixedStoryPath(original) {
94
97
  if (original.includes("..")) {
@@ -124,10 +127,18 @@ function reportInvalidStoryFormat(context, comment, collapsed) {
124
127
  * for common path suffix issues by locating and replacing the path text
125
128
  * within the original comment.
126
129
  *
130
+ * This helper:
131
+ * - only adjusts the story path suffix when a safe, well-understood
132
+ * transformation is available, satisfying REQ-AUTOFIX-SAFE.
133
+ * - preserves all surrounding comment formatting, spacing, and text
134
+ * outside the path substring, satisfying REQ-AUTOFIX-PRESERVE.
135
+ *
127
136
  * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
128
137
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
129
138
  * @req REQ-PATH-FORMAT - Validate @story paths follow expected patterns
130
139
  * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
140
+ * @req REQ-AUTOFIX-SAFE - Auto-fix must be conservative and avoid changing semantics
141
+ * @req REQ-AUTOFIX-PRESERVE - Auto-fix must preserve surrounding formatting and comments
131
142
  */
132
143
  function reportInvalidStoryFormatWithFix(context, comment, collapsed, fixed) {
133
144
  const sourceCode = context.getSourceCode();
@@ -317,6 +328,15 @@ exports.default = {
317
328
  invalidReqFormat: "{{details}}",
318
329
  },
319
330
  schema: [],
331
+ /**
332
+ * This rule's fixable support is limited to safe @story path suffix normalization per Story 008.0.
333
+ * Fixes are limited strictly to adjusting the suffix portion of the @story path (e.g., adding
334
+ * `.md` or `.story.md`), preserving all other comment text and whitespace exactly as written.
335
+ *
336
+ * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
337
+ * @req REQ-AUTOFIX-SAFE
338
+ * @req REQ-AUTOFIX-PRESERVE
339
+ */
320
340
  fixable: "code",
321
341
  },
322
342
  /**
@@ -1,6 +1,12 @@
1
1
  /**
2
2
  * Helper to check @req annotation presence on TS declare functions and method signatures.
3
+ * This helper is intentionally scope/exportPriority agnostic and focuses solely
4
+ * on detection and reporting of @req annotations for the given node.
3
5
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
4
6
  * @req REQ-TYPESCRIPT-SUPPORT - Support TypeScript-specific function syntax
7
+ * @req REQ-ANNOTATION-REQ-DETECTION - Determine presence of @req annotation
8
+ * @req REQ-ANNOTATION-REPORTING - Report missing @req annotation to context
5
9
  */
6
- export declare function checkReqAnnotation(context: any, node: any): void;
10
+ export declare function checkReqAnnotation(context: any, node: any, options?: {
11
+ enableFix?: boolean;
12
+ }): void;
@@ -48,11 +48,41 @@ function commentContainsReq(c) {
48
48
  * @req REQ-ANNOTATION-REQ-DETECTION - Determine presence of @req annotation
49
49
  */
50
50
  function hasReqAnnotation(jsdoc, comments) {
51
+ // BRANCH @req detection on JSDoc or comments
52
+ // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
53
+ // @req REQ-ANNOTATION-REQ-DETECTION
51
54
  return ((jsdoc &&
52
55
  typeof jsdoc.value === "string" &&
53
56
  jsdoc.value.includes("@req")) ||
54
57
  comments.some(commentContainsReq));
55
58
  }
59
+ /**
60
+ * Determine the most appropriate node to attach an inserted JSDoc to.
61
+ * Prefers outer function-like constructs such as methods, variable declarators,
62
+ * or wrapping expression statements for function expressions.
63
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
64
+ * @req REQ-ANNOTATION-AUTOFIX - Provide autofix for missing @req annotation
65
+ */
66
+ function getFixTargetNode(node) {
67
+ const parent = node && node.parent;
68
+ if (!parent) {
69
+ return node;
70
+ }
71
+ // If the node is part of a class/obj method definition, attach to the MethodDefinition
72
+ if (parent.type === "MethodDefinition") {
73
+ return parent;
74
+ }
75
+ // If the node is the init of a variable declarator, attach to the VariableDeclarator
76
+ if (parent.type === "VariableDeclarator" && parent.init === node) {
77
+ return parent;
78
+ }
79
+ // If the parent is an expression statement (e.g. IIFE or assigned via expression),
80
+ // attach to the outer ExpressionStatement.
81
+ if (parent.type === "ExpressionStatement") {
82
+ return parent;
83
+ }
84
+ return node;
85
+ }
56
86
  /**
57
87
  * Creates a fix function that inserts a missing @req JSDoc before the node.
58
88
  * Returned function is a proper named function so no inline arrow is used.
@@ -60,8 +90,9 @@ function hasReqAnnotation(jsdoc, comments) {
60
90
  * @req REQ-ANNOTATION-AUTOFIX - Provide autofix for missing @req annotation
61
91
  */
62
92
  function createMissingReqFix(node) {
93
+ const target = getFixTargetNode(node);
63
94
  return function missingReqFix(fixer) {
64
- return fixer.insertTextBefore(node, "/** @req <REQ-ID> */\n");
95
+ return fixer.insertTextBefore(target, "/** @req <REQ-ID> */\n");
65
96
  };
66
97
  }
67
98
  /**
@@ -73,29 +104,41 @@ function createMissingReqFix(node) {
73
104
  * @req REQ-ERROR-SPECIFIC - Provide specific error details including node name
74
105
  * @req REQ-ERROR-LOCATION - Include contextual location information in errors
75
106
  */
76
- function reportMissing(context, node) {
77
- const rawName = (0, require_story_utils_1.getNodeName)(node);
107
+ function reportMissing(context, node, enableFix = true) {
108
+ const rawName = (0, require_story_utils_1.getNodeName)(node) ?? (node && (0, require_story_utils_1.getNodeName)(node.parent));
78
109
  const name = rawName ?? "(anonymous)";
79
- context.report({
110
+ const reportOptions = {
80
111
  node,
81
112
  messageId: "missingReq",
82
113
  data: { name },
83
- fix: createMissingReqFix(node),
84
- });
114
+ };
115
+ if (enableFix) {
116
+ reportOptions.fix = createMissingReqFix(node);
117
+ }
118
+ context.report(reportOptions);
85
119
  }
86
120
  /**
87
121
  * Helper to check @req annotation presence on TS declare functions and method signatures.
122
+ * This helper is intentionally scope/exportPriority agnostic and focuses solely
123
+ * on detection and reporting of @req annotations for the given node.
88
124
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
89
125
  * @req REQ-TYPESCRIPT-SUPPORT - Support TypeScript-specific function syntax
126
+ * @req REQ-ANNOTATION-REQ-DETECTION - Determine presence of @req annotation
127
+ * @req REQ-ANNOTATION-REPORTING - Report missing @req annotation to context
90
128
  */
91
- function checkReqAnnotation(context, node) {
129
+ function checkReqAnnotation(context, node, options) {
130
+ const { enableFix = true } = options ?? {};
92
131
  const sourceCode = context.getSourceCode();
93
132
  const jsdoc = getJsdocComment(sourceCode, node);
94
133
  const leading = getLeadingComments(node);
95
134
  const comments = getCommentsBefore(sourceCode, node);
96
135
  const all = combineComments(leading, comments);
97
136
  const hasReq = hasReqAnnotation(jsdoc, all);
137
+ // BRANCH when a @req annotation is missing and must be reported
138
+ // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
139
+ // @req REQ-ANNOTATION-REQ-DETECTION
140
+ // @req REQ-ANNOTATION-REPORTING
98
141
  if (!hasReq) {
99
- reportMissing(context, node);
142
+ reportMissing(context, node, enableFix);
100
143
  }
101
144
  }
@@ -28,6 +28,10 @@ describe("Auto-fix behavior (Story 008.0-DEV-AUTO-FIX)", () => {
28
28
  name: "[REQ-AUTOFIX-MISSING] already annotated function is unchanged",
29
29
  code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n */\nfunction alreadyAnnotated() {}`,
30
30
  },
31
+ {
32
+ name: "[REQ-AUTOFIX-MISSING] already annotated class method is unchanged",
33
+ code: `class A {\n /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\n method() {}\n}`,
34
+ },
31
35
  ],
32
36
  invalid: [
33
37
  {
@@ -46,6 +50,78 @@ describe("Auto-fix behavior (Story 008.0-DEV-AUTO-FIX)", () => {
46
50
  },
47
51
  ],
48
52
  },
53
+ {
54
+ name: "[REQ-AUTOFIX-MISSING] adds @story before function expression when missing",
55
+ code: `const fnExpr = function() {};`,
56
+ output: `/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\nconst fnExpr = function() {};`,
57
+ errors: [
58
+ {
59
+ messageId: "missingStory",
60
+ suggestions: [
61
+ {
62
+ desc: "Add JSDoc @story annotation for function 'fnExpr', e.g., /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */",
63
+ output: `/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\nconst fnExpr = function() {};`,
64
+ },
65
+ ],
66
+ },
67
+ ],
68
+ },
69
+ {
70
+ name: "[REQ-AUTOFIX-MISSING] adds @story before class method when missing",
71
+ code: `class C {\n method() {}\n}`,
72
+ output: `class C {\n /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\n method() {}\n}`,
73
+ errors: [
74
+ {
75
+ messageId: "missingStory",
76
+ suggestions: [
77
+ {
78
+ desc: "Add JSDoc @story annotation for function 'method', e.g., /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */",
79
+ output: `class C {\n /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\n method() {}\n}`,
80
+ },
81
+ ],
82
+ },
83
+ ],
84
+ },
85
+ {
86
+ name: "[REQ-AUTOFIX-MISSING] adds @story before TS declare function when missing",
87
+ code: `declare function tsDecl(): void;`,
88
+ output: `/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\ndeclare function tsDecl(): void;`,
89
+ languageOptions: {
90
+ parser: require("@typescript-eslint/parser"),
91
+ parserOptions: { ecmaVersion: 2020, sourceType: "module" },
92
+ },
93
+ errors: [
94
+ {
95
+ messageId: "missingStory",
96
+ suggestions: [
97
+ {
98
+ desc: "Add JSDoc @story annotation for function 'tsDecl', e.g., /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */",
99
+ output: `/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\ndeclare function tsDecl(): void;`,
100
+ },
101
+ ],
102
+ },
103
+ ],
104
+ },
105
+ {
106
+ name: "[REQ-AUTOFIX-MISSING] adds @story before TS method signature when missing",
107
+ code: `interface D {\n method(): void;\n}`,
108
+ output: `/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\ninterface D {\n method(): void;\n}`,
109
+ languageOptions: {
110
+ parser: require("@typescript-eslint/parser"),
111
+ parserOptions: { ecmaVersion: 2020, sourceType: "module" },
112
+ },
113
+ errors: [
114
+ {
115
+ messageId: "missingStory",
116
+ suggestions: [
117
+ {
118
+ desc: "Add JSDoc @story annotation for function 'method', e.g., /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */",
119
+ output: `/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\ninterface D {\n method(): void;\n}`,
120
+ },
121
+ ],
122
+ },
123
+ ],
124
+ },
49
125
  ],
50
126
  });
51
127
  });
@@ -44,24 +44,80 @@ describe("Require Req Annotation Rule (Story 003.0-DEV-FUNCTION-ANNOTATIONS)", (
44
44
  parserOptions: { ecmaVersion: 2022, sourceType: "module" },
45
45
  },
46
46
  },
47
+ {
48
+ name: "[REQ-FUNCTION-DETECTION][Story 003.0] valid FunctionExpression with @req annotation",
49
+ code: `const fn = /**\n * @req REQ-EXAMPLE\n */\nfunction() {};`,
50
+ },
51
+ {
52
+ name: "[REQ-FUNCTION-DETECTION][Story 003.0] valid MethodDefinition with @req annotation",
53
+ code: `class C {\n /**\n * @req REQ-EXAMPLE\n */\n m() {}\n}`,
54
+ },
55
+ {
56
+ name: "[REQ-TYPESCRIPT-SUPPORT][REQ-FUNCTION-DETECTION][Story 003.0] valid TS FunctionExpression in variable declarator with @req",
57
+ code: `const fn = /**\n * @req REQ-EXAMPLE\n */\nfunction () {};`,
58
+ languageOptions: {
59
+ parser: require("@typescript-eslint/parser"),
60
+ parserOptions: { ecmaVersion: 2022, sourceType: "module" },
61
+ },
62
+ },
63
+ {
64
+ name: "[REQ-TYPESCRIPT-SUPPORT][REQ-FUNCTION-DETECTION][Story 003.0] valid exported TS FunctionExpression in variable declarator with @req",
65
+ code: `export const fn = /**\n * @req REQ-EXAMPLE\n */\nfunction () {};`,
66
+ languageOptions: {
67
+ parser: require("@typescript-eslint/parser"),
68
+ parserOptions: { ecmaVersion: 2022, sourceType: "module" },
69
+ },
70
+ },
71
+ {
72
+ name: "[REQ-CONFIGURABLE-SCOPE][Story 003.0] FunctionExpression ignored when scope only includes FunctionDeclaration",
73
+ code: `const fn = function () {};`,
74
+ options: [{ scope: ["FunctionDeclaration"] }],
75
+ },
76
+ {
77
+ name: "[REQ-EXPORT-PRIORITY][Story 003.0] non-exported function ignored when exportPriority is 'exported'",
78
+ code: `function nonExported() {}`,
79
+ options: [{ exportPriority: "exported" }],
80
+ },
81
+ {
82
+ name: "[REQ-EXPORT-PRIORITY][Story 003.0] exported function required when exportPriority is 'exported'",
83
+ code: `/** @req REQ-EXAMPLE */\nexport function exportedFn() {}`,
84
+ options: [{ exportPriority: "exported" }],
85
+ },
86
+ {
87
+ name: "[REQ-EXPORT-PRIORITY][Story 003.0] exported function ignored when exportPriority is 'non-exported'",
88
+ code: `export function exportedFn() {}`,
89
+ options: [{ exportPriority: "non-exported" }],
90
+ },
91
+ {
92
+ name: "[REQ-EXPORT-PRIORITY][Story 003.0] non-exported function required when exportPriority is 'non-exported'",
93
+ code: `/** @req REQ-EXAMPLE */\nfunction nonExported() {}`,
94
+ options: [{ exportPriority: "non-exported" }],
95
+ },
96
+ {
97
+ name: "[REQ-EXPORT-PRIORITY][Story 003.0] exported method ignored when exportPriority is 'non-exported'",
98
+ code: `export class C {\n m() {}\n}`,
99
+ options: [{ exportPriority: "non-exported" }],
100
+ },
101
+ {
102
+ name: "[REQ-EXPORT-PRIORITY][Story 003.0] non-exported method required when exportPriority is 'non-exported'",
103
+ code: `class C {\n /** @req REQ-EXAMPLE */\n m() {}\n}`,
104
+ options: [{ exportPriority: "non-exported" }],
105
+ },
47
106
  ],
48
107
  invalid: [
49
108
  {
50
109
  name: "[REQ-ANNOTATION-REQUIRED] missing @req on function without JSDoc",
51
110
  code: `function baz() {}`,
52
- output: `/** @req <REQ-ID> */\nfunction baz() {}`,
53
111
  errors: [{ messageId: "missingReq", data: { name: "baz" } }],
54
112
  },
55
113
  {
56
114
  name: "[REQ-ANNOTATION-REQUIRED] missing @req on function with only @story annotation",
57
115
  code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n */\nfunction qux() {}`,
58
- output: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n */\n/** @req <REQ-ID> */\nfunction qux() {}`,
59
116
  errors: [{ messageId: "missingReq", data: { name: "qux" } }],
60
117
  },
61
118
  {
62
119
  name: "[REQ-TYPESCRIPT-SUPPORT] missing @req on TSDeclareFunction",
63
120
  code: `declare function baz(): void;`,
64
- output: `/** @req <REQ-ID> */\ndeclare function baz(): void;`,
65
121
  errors: [{ messageId: "missingReq", data: { name: "baz" } }],
66
122
  languageOptions: {
67
123
  parser: require("@typescript-eslint/parser"),
@@ -71,13 +127,92 @@ describe("Require Req Annotation Rule (Story 003.0-DEV-FUNCTION-ANNOTATIONS)", (
71
127
  {
72
128
  name: "[REQ-TYPESCRIPT-SUPPORT] missing @req on TSMethodSignature",
73
129
  code: `interface I { method(): void; }`,
74
- output: `interface I { /** @req <REQ-ID> */\nmethod(): void; }`,
75
130
  errors: [{ messageId: "missingReq", data: { name: "method" } }],
76
131
  languageOptions: {
77
132
  parser: require("@typescript-eslint/parser"),
78
133
  parserOptions: { ecmaVersion: 2022, sourceType: "module" },
79
134
  },
80
135
  },
136
+ {
137
+ name: "[REQ-FUNCTION-DETECTION][Story 003.0] missing @req on FunctionExpression assigned to variable",
138
+ code: `const fn = function () {};`,
139
+ errors: [{ messageId: "missingReq", data: { name: "fn" } }],
140
+ },
141
+ {
142
+ name: "[REQ-FUNCTION-DETECTION][Story 003.0] missing @req on anonymous FunctionExpression (no variable name)",
143
+ code: `(function () {})();`,
144
+ errors: [{ messageId: "missingReq", data: { name: "(anonymous)" } }],
145
+ },
146
+ {
147
+ name: "[REQ-FUNCTION-DETECTION][Story 003.0] missing @req on MethodDefinition in class",
148
+ code: `class C {\n m() {}\n}`,
149
+ errors: [{ messageId: "missingReq" }],
150
+ },
151
+ {
152
+ name: "[REQ-FUNCTION-DETECTION][Story 003.0] missing @req on MethodDefinition in object literal",
153
+ code: `const o = { m() {} };`,
154
+ errors: [{ messageId: "missingReq" }],
155
+ },
156
+ {
157
+ name: "[REQ-TYPESCRIPT-SUPPORT][REQ-FUNCTION-DETECTION][Story 003.0] missing @req on TS FunctionExpression in variable declarator",
158
+ code: `const fn = function () {};`,
159
+ errors: [{ messageId: "missingReq", data: { name: "fn" } }],
160
+ languageOptions: {
161
+ parser: require("@typescript-eslint/parser"),
162
+ parserOptions: { ecmaVersion: 2022, sourceType: "module" },
163
+ },
164
+ },
165
+ {
166
+ name: "[REQ-TYPESCRIPT-SUPPORT][REQ-FUNCTION-DETECTION][Story 003.0] missing @req on exported TS FunctionExpression in variable declarator",
167
+ code: `export const fn = function () {};`,
168
+ errors: [{ messageId: "missingReq", data: { name: "fn" } }],
169
+ languageOptions: {
170
+ parser: require("@typescript-eslint/parser"),
171
+ parserOptions: { ecmaVersion: 2022, sourceType: "module" },
172
+ },
173
+ },
174
+ {
175
+ name: "[REQ-CONFIGURABLE-SCOPE][Story 003.0] FunctionDeclaration still reported when scope only includes FunctionDeclaration",
176
+ code: `function scoped() {}`,
177
+ options: [{ scope: ["FunctionDeclaration"] }],
178
+ errors: [{ messageId: "missingReq", data: { name: "scoped" } }],
179
+ },
180
+ {
181
+ name: "[REQ-EXPORT-PRIORITY][Story 003.0] exported function reported when exportPriority is 'exported'",
182
+ code: `export function exportedFn() {}`,
183
+ options: [{ exportPriority: "exported" }],
184
+ errors: [{ messageId: "missingReq", data: { name: "exportedFn" } }],
185
+ },
186
+ {
187
+ name: "[REQ-EXPORT-PRIORITY][Story 003.0] non-exported function reported when exportPriority is 'non-exported'",
188
+ code: `function nonExported() {}`,
189
+ options: [{ exportPriority: "non-exported" }],
190
+ errors: [{ messageId: "missingReq", data: { name: "nonExported" } }],
191
+ },
192
+ {
193
+ name: "[REQ-EXPORT-PRIORITY][Story 003.0] exported method reported when exportPriority is 'exported'",
194
+ code: `export class C {\n m() {}\n}`,
195
+ errors: [{ messageId: "missingReq" }],
196
+ options: [{ exportPriority: "exported" }],
197
+ },
198
+ {
199
+ name: "[REQ-EXPORT-PRIORITY][Story 003.0] non-exported method reported when exportPriority is 'non-exported'",
200
+ code: `class C {\n m() {}\n}`,
201
+ errors: [{ messageId: "missingReq" }],
202
+ options: [{ exportPriority: "non-exported" }],
203
+ },
204
+ {
205
+ name: "[REQ-EXPORT-PRIORITY][Story 003.0] exported FunctionExpression reported when exportPriority is 'exported'",
206
+ code: `export const fn = function () {};`,
207
+ options: [{ exportPriority: "exported" }],
208
+ errors: [{ messageId: "missingReq", data: { name: "fn" } }],
209
+ },
210
+ {
211
+ name: "[REQ-EXPORT-PRIORITY][Story 003.0] non-exported FunctionExpression reported when exportPriority is 'non-exported'",
212
+ code: `const fn = function () {};`,
213
+ options: [{ exportPriority: "non-exported" }],
214
+ errors: [{ messageId: "missingReq", data: { name: "fn" } }],
215
+ },
81
216
  ],
82
217
  });
83
218
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-traceability",
3
- "version": "1.6.0",
3
+ "version": "1.6.1",
4
4
  "description": "A customizable ESLint plugin that enforces traceability annotations in your code, ensuring each implementation is linked to its requirement or test case.",
5
5
  "main": "lib/src/index.js",
6
6
  "types": "lib/src/index.d.ts",