eslint-plugin-traceability 1.5.1 → 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.
@@ -7,21 +7,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  /**
8
8
  * Rule to validate @req annotation references refer to existing requirements in story files
9
9
  * @story docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
10
- * @req REQ-DEEP-PARSE - Parse story files to extract requirement identifiers
11
- * @req REQ-DEEP-MATCH - Validate @req references against story file content
12
- * @req REQ-DEEP-CACHE - Cache parsed story content for performance
13
- * @req REQ-DEEP-PATH - Protect against path traversal in story paths
10
+ * @req REQ-DEEP-PARSE - Parse comments and extract story/requirement metadata
11
+ * @req REQ-DEEP-MATCH - Match @req annotations to story file requirements
12
+ * @req REQ-DEEP-CACHE - Cache requirement IDs per story file for efficient validation
13
+ * @req REQ-DEEP-PATH - Validate and resolve story file paths safely
14
14
  */
15
15
  const fs_1 = __importDefault(require("fs"));
16
16
  const path_1 = __importDefault(require("path"));
17
17
  /**
18
18
  * Extract the story path from a JSDoc comment.
19
- * Parses comment.value lines for @story annotation.
20
- * @param comment any JSDoc comment node
21
- * @returns story path or null if not found
22
- *
23
19
  * @story docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
24
- * @req REQ-DEEP-PARSE - Extracts @story annotation from comment content
20
+ * @req REQ-DEEP-PARSE - Parse JSDoc comment lines to locate @story annotations
25
21
  */
26
22
  function extractStoryPath(comment) {
27
23
  const rawLines = comment.value.split(/\r?\n/);
@@ -37,14 +33,11 @@ function extractStoryPath(comment) {
37
33
  /**
38
34
  * Validate a @req annotation line against the extracted story content.
39
35
  * Performs path validation, file reading, caching, and requirement existence checks.
40
- *
41
- * @param opts options bag
42
- *
43
36
  * @story docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
44
- * @req REQ-DEEP-PATH - Validates and protects against path traversal and absolute paths
45
- * @req REQ-DEEP-CACHE - Caches parsed story files to avoid repeated IO
46
- * @req REQ-DEEP-MATCH - Ensures referenced requirement IDs exist in the story file
47
- * @req REQ-DEEP-PARSE - Parses story file content to find REQ- identifiers
37
+ * @req REQ-DEEP-PATH - Validate and resolve referenced story file paths
38
+ * @req REQ-DEEP-CACHE - Cache requirement IDs discovered in story files
39
+ * @req REQ-DEEP-MATCH - Verify that a referenced requirement ID exists in the story
40
+ * @req REQ-DEEP-PARSE - Parse story file contents to extract requirement identifiers
48
41
  */
49
42
  function validateReqLine(opts) {
50
43
  const { comment, context, line, storyPath, cwd, reqCache } = opts;
@@ -96,15 +89,10 @@ function validateReqLine(opts) {
96
89
  }
97
90
  }
98
91
  /**
99
- * Handle a single annotation line.
100
- * @story Updates the current story path when encountering an @story annotation
101
- * @req Validates the requirement reference against the current story content
102
- *
103
- * @param opts handler options
104
- *
92
+ * Handle a single annotation line for story or requirement metadata.
105
93
  * @story docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
106
- * @req REQ-DEEP-PARSE - Recognizes @story and @req annotation lines
107
- * @req REQ-DEEP-MATCH - Delegates @req validation to validateReqLine
94
+ * @req REQ-DEEP-PARSE - Parse annotation lines for @story and @req tags
95
+ * @req REQ-DEEP-MATCH - Dispatch @req lines for validation against story requirements
108
96
  */
109
97
  function handleAnnotationLine(opts) {
110
98
  const { line, comment, context, cwd, reqCache, storyPath } = opts;
@@ -119,14 +107,11 @@ function handleAnnotationLine(opts) {
119
107
  return storyPath;
120
108
  }
121
109
  /**
122
- * Handle JSDoc story and req annotations.
123
- *
124
- * @param opts options for comment handling
125
- *
110
+ * Handle JSDoc story and req annotations for a single comment block.
126
111
  * @story docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
127
- * @req REQ-DEEP-PARSE - Parses comment blocks to extract annotation lines
128
- * @req REQ-DEEP-MATCH - Uses handleAnnotationLine to validate @req entries
129
- * @req REQ-DEEP-CACHE - Passes shared cache for parsed story files
112
+ * @req REQ-DEEP-PARSE - Iterate comment lines to process @story/@req annotations
113
+ * @req REQ-DEEP-MATCH - Coordinate annotation handling across a comment block
114
+ * @req REQ-DEEP-CACHE - Maintain and reuse discovered story path across comments
130
115
  */
131
116
  function handleComment(opts) {
132
117
  const { comment, context, cwd, reqCache, rawStoryPath } = opts;
@@ -147,30 +132,25 @@ function handleComment(opts) {
147
132
  }
148
133
  /**
149
134
  * Create a Program listener that iterates comments and validates annotations.
150
- *
151
- * @param context ESLint rule context
152
- * @returns Program visitor function
153
- *
154
135
  * @story docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
155
- * @req REQ-DEEP-CACHE - Maintains a cache across comment processing
156
- * @req REQ-DEEP-PATH - Resolves and protects story paths against traversal
136
+ * @req REQ-DEEP-CACHE - Initialize and share a requirement cache for the program
137
+ * @req REQ-DEEP-PATH - Derive the working directory context for path resolution
157
138
  */
158
139
  function programListener(context) {
159
140
  const sourceCode = context.getSourceCode();
160
141
  const cwd = process.cwd();
161
142
  const reqCache = new Map();
162
143
  let rawStoryPath = null;
144
+ /**
145
+ * Program visitor that walks all comments to validate story/requirement references.
146
+ * @story docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
147
+ * @req REQ-DEEP-PARSE - Collect all comments from the source code
148
+ * @req REQ-DEEP-MATCH - Drive comment-level handling for traceability checks
149
+ * @req REQ-DEEP-CACHE - Reuse story path and requirement cache across comments
150
+ * @req REQ-DEEP-PATH - Ensure validation respects project-relative paths
151
+ */
163
152
  return function Program() {
164
153
  const comments = sourceCode.getAllComments() || [];
165
- /**
166
- * Process each comment to handle story and requirement annotations.
167
- *
168
- * @story docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
169
- * @req REQ-DEEP-PARSE - Parse annotations from comment blocks
170
- * @req REQ-DEEP-MATCH - Validate @req references found in comments
171
- * @req REQ-DEEP-CACHE - Use cache for parsed story files to avoid repeated IO
172
- * @req REQ-DEEP-PATH - Enforce path validation when resolving story files
173
- */
174
154
  comments.forEach((comment) => {
175
155
  rawStoryPath = handleComment({
176
156
  comment,
@@ -197,12 +177,11 @@ exports.default = {
197
177
  },
198
178
  /**
199
179
  * Rule create entrypoint that returns the Program visitor.
200
- *
201
180
  * @story docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
202
- * @req REQ-DEEP-MATCH - Entrypoint orchestrates validation of @req annotations
203
- * @req REQ-DEEP-PARSE - Uses parsing helpers to extract annotations and story paths
204
- * @req REQ-DEEP-CACHE - Establishes cache used during validation
205
- * @req REQ-DEEP-PATH - Ensures path validation is applied during checks
181
+ * @req REQ-DEEP-MATCH - Register the Program visitor with ESLint
182
+ * @req REQ-DEEP-PARSE - Integrate comment parsing into the ESLint rule lifecycle
183
+ * @req REQ-DEEP-CACHE - Ensure cache and context are wired into the listener
184
+ * @req REQ-DEEP-PATH - Propagate path context into the program listener
206
185
  */
207
186
  create(context) {
208
187
  return { Program: programListener(context) };
@@ -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
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ /**
7
+ * Tests for: docs/stories/008.0-DEV-AUTO-FIX.story.md
8
+ * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
9
+ * @req REQ-AUTOFIX-MISSING - Verify ESLint --fix automatically adds missing @story annotations to functions
10
+ * @req REQ-AUTOFIX-FORMAT - Verify ESLint --fix corrects simple annotation format issues for @story annotations
11
+ */
12
+ const eslint_1 = require("eslint");
13
+ const require_story_annotation_1 = __importDefault(require("../../src/rules/require-story-annotation"));
14
+ const valid_annotation_format_1 = __importDefault(require("../../src/rules/valid-annotation-format"));
15
+ const functionRuleTester = new eslint_1.RuleTester({
16
+ languageOptions: {
17
+ parserOptions: { ecmaVersion: 2020, sourceType: "module" },
18
+ },
19
+ });
20
+ const formatRuleTester = new eslint_1.RuleTester({
21
+ languageOptions: { parserOptions: { ecmaVersion: 2020 } },
22
+ });
23
+ describe("Auto-fix behavior (Story 008.0-DEV-AUTO-FIX)", () => {
24
+ describe("[REQ-AUTOFIX-MISSING] require-story-annotation auto-fix", () => {
25
+ functionRuleTester.run("require-story-annotation --fix", require_story_annotation_1.default, {
26
+ valid: [
27
+ {
28
+ name: "[REQ-AUTOFIX-MISSING] already annotated function is unchanged",
29
+ code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n */\nfunction alreadyAnnotated() {}`,
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
+ },
35
+ ],
36
+ invalid: [
37
+ {
38
+ name: "[REQ-AUTOFIX-MISSING] adds @story before function declaration when missing",
39
+ code: `function autoFixMe() {}`,
40
+ output: `/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\nfunction autoFixMe() {}`,
41
+ errors: [
42
+ {
43
+ messageId: "missingStory",
44
+ suggestions: [
45
+ {
46
+ desc: "Add JSDoc @story annotation for function 'autoFixMe', e.g., /** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */",
47
+ output: `/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\nfunction autoFixMe() {}`,
48
+ },
49
+ ],
50
+ },
51
+ ],
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
+ },
125
+ ],
126
+ });
127
+ });
128
+ describe("[REQ-AUTOFIX-FORMAT] valid-annotation-format auto-fix", () => {
129
+ formatRuleTester.run("valid-annotation-format --fix simple @story extension issues", valid_annotation_format_1.default, {
130
+ valid: [
131
+ {
132
+ name: "[REQ-AUTOFIX-FORMAT] already-correct story path is unchanged",
133
+ code: `// @story docs/stories/005.0-DEV-EXAMPLE.story.md`,
134
+ },
135
+ ],
136
+ invalid: [
137
+ {
138
+ name: "[REQ-AUTOFIX-FORMAT] adds .md extension for .story path",
139
+ code: `// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story`,
140
+ output: `// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md`,
141
+ errors: [
142
+ {
143
+ messageId: "invalidStoryFormat",
144
+ },
145
+ ],
146
+ },
147
+ {
148
+ name: "[REQ-AUTOFIX-FORMAT] adds .story.md extension when missing entirely",
149
+ code: `// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION`,
150
+ output: `// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md`,
151
+ errors: [
152
+ {
153
+ messageId: "invalidStoryFormat",
154
+ },
155
+ ],
156
+ },
157
+ ],
158
+ });
159
+ });
160
+ });
@@ -29,6 +29,7 @@ describe("Error Reporting Enhancements for require-story-annotation (Story 007.0
29
29
  {
30
30
  name: "[REQ-ERROR-SPECIFIC] missing @story annotation should report specific details and suggestion",
31
31
  code: `function bar() {}`,
32
+ output: "/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\nfunction bar() {}",
32
33
  errors: [
33
34
  {
34
35
  messageId: "missingStory",
@@ -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
  });