eslint-plugin-traceability 1.20.0 → 1.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/CHANGELOG.md +3 -3
  2. package/README.md +8 -7
  3. package/lib/src/maintenance/batch.js +1 -0
  4. package/lib/src/maintenance/cli.js +1 -0
  5. package/lib/src/maintenance/commands.js +1 -0
  6. package/lib/src/maintenance/detect.js +1 -0
  7. package/lib/src/maintenance/report.js +1 -0
  8. package/lib/src/maintenance/update.js +1 -0
  9. package/lib/src/rules/helpers/require-story-core.d.ts +2 -0
  10. package/lib/src/rules/helpers/require-story-core.js +7 -4
  11. package/lib/src/rules/helpers/require-story-helpers.d.ts +19 -1
  12. package/lib/src/rules/helpers/require-story-helpers.js +48 -4
  13. package/lib/src/rules/helpers/require-story-io.js +1 -0
  14. package/lib/src/rules/helpers/require-story-visitors.js +6 -0
  15. package/lib/src/rules/helpers/require-test-traceability-helpers.js +1 -0
  16. package/lib/src/rules/helpers/test-callback-exclusion.d.ts +1 -0
  17. package/lib/src/rules/helpers/valid-annotation-format-internal.d.ts +2 -2
  18. package/lib/src/rules/helpers/valid-annotation-format-internal.js +2 -2
  19. package/lib/src/rules/helpers/valid-annotation-format-validators.d.ts +14 -14
  20. package/lib/src/rules/helpers/valid-annotation-format-validators.js +31 -22
  21. package/lib/src/rules/helpers/valid-annotation-utils.js +1 -0
  22. package/lib/src/rules/helpers/valid-req-reference-helpers.js +1 -0
  23. package/lib/src/rules/require-req-annotation.d.ts +2 -3
  24. package/lib/src/rules/require-req-annotation.js +11 -2
  25. package/lib/src/rules/require-story-annotation.js +46 -20
  26. package/lib/src/rules/valid-annotation-format.js +14 -10
  27. package/lib/src/utils/annotation-checker.d.ts +1 -0
  28. package/lib/src/utils/annotation-checker.js +14 -0
  29. package/lib/src/utils/reqAnnotationDetection.js +1 -0
  30. package/lib/tests/config/eslint-config-validation.test.js +1 -0
  31. package/lib/tests/config/flat-config-presets-integration.test.js +1 -0
  32. package/lib/tests/config/require-story-annotation-config.test.js +2 -0
  33. package/lib/tests/fixtures/stale/example.js +1 -0
  34. package/lib/tests/fixtures/update/example.js +1 -0
  35. package/lib/tests/integration/annotation-placement-inside-prettier.integration.test.js +1 -0
  36. package/lib/tests/integration/catch-annotation-prettier.integration.test.js +1 -0
  37. package/lib/tests/integration/else-if-annotation-prettier.integration.test.js +1 -0
  38. package/lib/tests/integration/prettier-test-helpers.js +1 -0
  39. package/lib/tests/integration/require-traceability-aliases.integration.test.js +26 -0
  40. package/lib/tests/integration/require-traceability-test-callbacks.integration.test.js +1 -0
  41. package/lib/tests/maintenance/detect-isolated.test.js +1 -0
  42. package/lib/tests/perf/maintenance-large-workspace.test.js +1 -0
  43. package/lib/tests/perf/valid-annotation-format-large-file.test.js +1 -0
  44. package/lib/tests/plugin-setup.test.js +1 -0
  45. package/lib/tests/rules/error-reporting.test.js +1 -0
  46. package/lib/tests/rules/require-req-annotation.test.js +20 -0
  47. package/lib/tests/rules/require-story-annotation.test.js +26 -0
  48. package/lib/tests/rules/require-test-traceability.test.js +1 -0
  49. package/lib/tests/utils/branch-annotation-catch-insert-position.test.js +1 -0
  50. package/lib/tests/utils/branch-annotation-else-if-insert-position.test.js +1 -0
  51. package/lib/tests/utils/branch-annotation-helpers.test.js +1 -0
  52. package/package.json +1 -1
  53. package/user-docs/api-reference.md +4 -4
@@ -59,7 +59,13 @@ const rule = {
59
59
  const rawScope = options?.scope;
60
60
  const scope = Array.isArray(rawScope) && rawScope.length > 0 ? rawScope : require_story_helpers_1.DEFAULT_SCOPE;
61
61
  const exportPriority = options?.exportPriority ?? "all";
62
- const shouldCheck = (node) => (0, require_story_helpers_1.shouldProcessNode)(node, scope, exportPriority);
62
+ const rawAnnotationPlacement = options?.annotationPlacement;
63
+ const annotationPlacement = rawAnnotationPlacement === "inside" || rawAnnotationPlacement === "before"
64
+ ? rawAnnotationPlacement
65
+ : "before";
66
+ const shouldCheck = (node) => (0, require_story_helpers_1.shouldProcessNode)(node, scope, exportPriority, {
67
+ annotationPlacement,
68
+ });
63
69
  /**
64
70
  * Helper to conditionally run the annotation check only when the node
65
71
  * should be processed according to scope/exportPriority.
@@ -67,7 +73,10 @@ const rule = {
67
73
  const runCheck = (node) => {
68
74
  if (!shouldCheck(node))
69
75
  return;
70
- (0, annotation_checker_1.checkReqAnnotation)(context, node, { enableFix: false });
76
+ (0, annotation_checker_1.checkReqAnnotation)(context, node, {
77
+ enableFix: false,
78
+ annotationPlacement,
79
+ });
71
80
  };
72
81
  return {
73
82
  /**
@@ -2,6 +2,43 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const require_story_visitors_1 = require("./helpers/require-story-visitors");
4
4
  const require_story_helpers_1 = require("./helpers/require-story-helpers");
5
+ function getNormalizedOptions(context) {
6
+ const sourceCode = context.getSourceCode();
7
+ const opts = (context.options && context.options[0]) || {};
8
+ const scope = opts.scope || require_story_helpers_1.DEFAULT_SCOPE;
9
+ const exportPriority = opts.exportPriority || "all";
10
+ const annotationTemplate = typeof opts.annotationTemplate === "string" &&
11
+ opts.annotationTemplate.trim().length > 0
12
+ ? opts.annotationTemplate.trim()
13
+ : undefined;
14
+ const methodAnnotationTemplate = typeof opts.methodAnnotationTemplate === "string" &&
15
+ opts.methodAnnotationTemplate.trim().length > 0
16
+ ? opts.methodAnnotationTemplate.trim()
17
+ : undefined;
18
+ const autoFix = typeof opts.autoFix === "boolean" ? opts.autoFix : true;
19
+ const excludeTestCallbacks = typeof opts.excludeTestCallbacks === "boolean"
20
+ ? opts.excludeTestCallbacks
21
+ : true;
22
+ const additionalTestHelperNames = Array.isArray(opts.additionalTestHelperNames) &&
23
+ opts.additionalTestHelperNames.every((name) => typeof name === "string")
24
+ ? opts.additionalTestHelperNames
25
+ : undefined;
26
+ const rawAnnotationPlacement = opts.annotationPlacement;
27
+ const annotationPlacement = rawAnnotationPlacement === "inside" || rawAnnotationPlacement === "before"
28
+ ? rawAnnotationPlacement
29
+ : "before";
30
+ return {
31
+ sourceCode,
32
+ scope,
33
+ exportPriority,
34
+ annotationTemplate,
35
+ methodAnnotationTemplate,
36
+ autoFix,
37
+ excludeTestCallbacks,
38
+ additionalTestHelperNames,
39
+ annotationPlacement,
40
+ };
41
+ }
5
42
  /**
6
43
  * ESLint rule to require @story annotations on functions/methods.
7
44
  *
@@ -58,6 +95,12 @@ const rule = {
58
95
  items: { type: "string" },
59
96
  uniqueItems: true,
60
97
  },
98
+ /**
99
+ * @supports docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md REQ-PLACEMENT-CONFIG REQ-DEFAULT-BACKWARD-COMPAT REQ-ALL-BLOCK-TYPES
100
+ */
101
+ annotationPlacement: {
102
+ enum: ["before", "inside"],
103
+ },
61
104
  },
62
105
  additionalProperties: false,
63
106
  },
@@ -72,26 +115,7 @@ const rule = {
72
115
  * @req REQ-AUTOFIX-MISSING - The create hook wires in visitors that are capable of providing auto-fix suggestions for missing @story annotations.
73
116
  */
74
117
  create(context) {
75
- const sourceCode = context.getSourceCode();
76
- const opts = (context.options && context.options[0]) || {};
77
- const scope = opts.scope || require_story_helpers_1.DEFAULT_SCOPE;
78
- const exportPriority = opts.exportPriority || "all";
79
- const annotationTemplate = typeof opts.annotationTemplate === "string" &&
80
- opts.annotationTemplate.trim().length > 0
81
- ? opts.annotationTemplate.trim()
82
- : undefined;
83
- const methodAnnotationTemplate = typeof opts.methodAnnotationTemplate === "string" &&
84
- opts.methodAnnotationTemplate.trim().length > 0
85
- ? opts.methodAnnotationTemplate.trim()
86
- : undefined;
87
- const autoFix = typeof opts.autoFix === "boolean" ? opts.autoFix : true;
88
- const excludeTestCallbacks = typeof opts.excludeTestCallbacks === "boolean"
89
- ? opts.excludeTestCallbacks
90
- : true;
91
- const additionalTestHelperNames = Array.isArray(opts.additionalTestHelperNames) &&
92
- opts.additionalTestHelperNames.every((name) => typeof name === "string")
93
- ? opts.additionalTestHelperNames
94
- : undefined;
118
+ const { sourceCode, scope, exportPriority, annotationTemplate, methodAnnotationTemplate, autoFix, excludeTestCallbacks, additionalTestHelperNames, annotationPlacement, } = getNormalizedOptions(context);
95
119
  /**
96
120
  * Optional debug logging for troubleshooting this rule.
97
121
  * Developers can temporarily uncomment the block below to log when the rule
@@ -110,6 +134,7 @@ const rule = {
110
134
  const should = (node) => (0, require_story_helpers_1.shouldProcessNode)(node, scope, exportPriority, {
111
135
  excludeTestCallbacks,
112
136
  additionalTestHelperNames,
137
+ annotationPlacement,
113
138
  });
114
139
  // Delegate visitor construction to helper to keep this file concise.
115
140
  return (0, require_story_visitors_1.buildVisitors)(context, sourceCode, {
@@ -121,6 +146,7 @@ const rule = {
121
146
  autoFix,
122
147
  excludeTestCallbacks,
123
148
  additionalTestHelperNames,
149
+ annotationPlacement,
124
150
  });
125
151
  },
126
152
  };
@@ -5,7 +5,9 @@ const valid_annotation_format_internal_1 = require("./helpers/valid-annotation-f
5
5
  const valid_annotation_format_validators_1 = require("./helpers/valid-annotation-format-validators");
6
6
  function handleImplementsLine(normalized, pending, deps) {
7
7
  const { context, comment, options } = deps;
8
- const isImplements = /@supports\b/.test(normalized);
8
+ // Only match `@supports` at the START of the normalized line to avoid
9
+ // false matches when this keyword appears in prose
10
+ const isImplements = /^@supports\b/.test(normalized);
9
11
  if (!isImplements) {
10
12
  return pending;
11
13
  }
@@ -15,8 +17,10 @@ function handleImplementsLine(normalized, pending, deps) {
15
17
  }
16
18
  function handleStoryOrReqLine(normalized, pending, deps) {
17
19
  const { context, comment, options } = deps;
18
- const isStory = /@story\b/.test(normalized);
19
- const isReq = /@req\b/.test(normalized);
20
+ // Only match `@story`/`@req` at the START of the normalized line to avoid
21
+ // false matches when these keywords appear in prose (e.g., "@returns ... `@story` annotations")
22
+ const isStory = /^@story\b/.test(normalized);
23
+ const isReq = /^@req\b/.test(normalized);
20
24
  if (!isStory && !isReq) {
21
25
  return pending;
22
26
  }
@@ -81,7 +85,7 @@ function processCommentLine({ normalized, pending, context, comment, options, })
81
85
  if (afterStoryOrReq !== pending) {
82
86
  return afterStoryOrReq;
83
87
  }
84
- // Implement JSDoc tag coexistence behavior: terminate @story/@req values when a new non-traceability JSDoc tag line (e.g., @param, @returns) is encountered.
88
+ // Implement JSDoc tag coexistence behavior: terminate `@story`/`@req` values when a new non-traceability JSDoc tag line (e.g., @param, @returns) is encountered.
85
89
  // @supports docs/stories/022.0-DEV-JSDOC-COEXISTENCE.story.md REQ-ANNOTATION-TERMINATION REQ-CONTINUATION-LOGIC
86
90
  if ((0, valid_annotation_format_internal_1.isNonTraceabilityJSDocTagLine)(normalized)) {
87
91
  (0, valid_annotation_format_validators_1.finalizePendingAnnotation)(context, comment, options, pending);
@@ -96,13 +100,13 @@ function processCommentLine({ normalized, pending, context, comment, options, })
96
100
  return extendPendingAnnotation(normalized, pending);
97
101
  }
98
102
  /**
99
- * Process a single comment node and validate any @story/@req/@supports annotations it contains.
103
+ * Process a single comment node and validate any `@story`/`@req`/`@supports` annotations it contains.
100
104
  *
101
- * Supports @story and @req annotations whose values span multiple lines within the same
105
+ * Supports `@story` and `@req` annotations whose values span multiple lines within the same
102
106
  * comment block, collapsing whitespace so that the logical value can be
103
107
  * validated against the configured patterns.
104
108
  *
105
- * @supports annotations are validated immediately per-line and are not
109
+ * `@supports` annotations are validated immediately per-line and are not
106
110
  * accumulated into pending multi-line state.
107
111
  *
108
112
  * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
@@ -172,8 +176,8 @@ exports.default = {
172
176
  },
173
177
  schema: (0, valid_annotation_options_1.getRuleSchema)(),
174
178
  /**
175
- * This rule's fixable support is limited to safe @story path suffix normalization per Story 008.0.
176
- * Fixes are limited strictly to adjusting the suffix portion of the @story path (e.g., adding
179
+ * This rule's fixable support is limited to safe `@story` path suffix normalization per Story 008.0.
180
+ * Fixes are limited strictly to adjusting the suffix portion of the `@story` path (e.g., adding
177
181
  * `.md` or `.story.md`), preserving all other comment text and whitespace exactly as written.
178
182
  *
179
183
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
@@ -204,7 +208,7 @@ exports.default = {
204
208
  const optionErrors = (0, valid_annotation_options_1.getOptionErrors)();
205
209
  return {
206
210
  /**
207
- * Program-level handler that inspects all comments for @story, @req, and @supports tags
211
+ * Program-level handler that inspects all comments for `@story`, `@req`, and `@supports` tags
208
212
  *
209
213
  * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
210
214
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
@@ -13,4 +13,5 @@
13
13
  */
14
14
  export declare function checkReqAnnotation(context: any, node: any, options?: {
15
15
  enableFix?: boolean;
16
+ annotationPlacement?: "before" | "inside";
16
17
  }): void;
@@ -1,8 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.checkReqAnnotation = checkReqAnnotation;
4
+ /* eslint-disable traceability/valid-annotation-format */
4
5
  const require_story_utils_1 = require("../rules/helpers/require-story-utils");
5
6
  const reqAnnotationDetection_1 = require("./reqAnnotationDetection");
7
+ const function_annotation_helpers_1 = require("./function-annotation-helpers");
6
8
  /**
7
9
  * Helper to retrieve the JSDoc comment for a node.
8
10
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
@@ -180,7 +182,19 @@ function reportMissing(context, node, enableFix = true) {
180
182
  */
181
183
  function checkReqAnnotation(context, node, options) {
182
184
  const { enableFix = true } = options ?? {};
185
+ const annotationPlacement = options?.annotationPlacement === "inside" ||
186
+ options?.annotationPlacement === "before"
187
+ ? options.annotationPlacement
188
+ : "before";
183
189
  const sourceCode = context.getSourceCode();
190
+ if (annotationPlacement === "inside" &&
191
+ (0, function_annotation_helpers_1.supportsInsidePlacementForFunction)(node)) {
192
+ const insideText = (0, function_annotation_helpers_1.getFunctionInsideBodyCommentText)(sourceCode, node);
193
+ if (typeof insideText === "string" &&
194
+ (insideText.includes("@req") || insideText.includes("@supports"))) {
195
+ return;
196
+ }
197
+ }
184
198
  const jsdoc = getJsdocComment(sourceCode, node);
185
199
  const leading = getLeadingComments(node);
186
200
  const comments = getCommentsBefore(sourceCode, node);
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.hasReqAnnotation = hasReqAnnotation;
4
+ /* eslint-disable traceability/valid-annotation-format */
4
5
  /**
5
6
  * Shared @req detection helpers used by annotation-checker utilities.
6
7
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
+ /* eslint-disable traceability/valid-annotation-format */
2
3
  /**
3
4
  * Tests for ESLint config rule schemas.
4
5
  *
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ /* eslint-disable traceability/valid-annotation-format */
36
37
  /**
37
38
  * Tests for: docs/stories/002.0-DEV-ESLINT-CONFIG.story.md
38
39
  * @story docs/stories/002.0-DEV-ESLINT-CONFIG.story.md
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
+ /* eslint-disable traceability/valid-annotation-format */
2
3
  /**
3
4
  * Tests for the require-story-annotation rule schema configuration.
4
5
  *
@@ -19,6 +20,7 @@ describe("ESLint Configuration Rule Options (Story 002.0-DEV-ESLINT-CONFIG)", ()
19
20
  const schema = require_story_annotation_1.default.meta.schema[0];
20
21
  expect(schema.properties).toHaveProperty("scope");
21
22
  expect(schema.properties).toHaveProperty("exportPriority");
23
+ expect(schema.properties).toHaveProperty("annotationPlacement");
22
24
  expect(schema.additionalProperties).toBe(false);
23
25
  });
24
26
  });
@@ -1,3 +1,4 @@
1
1
  "use strict";
2
+ /* eslint-disable traceability/valid-annotation-format */
2
3
  // Sample code with stale annotation
3
4
  // @story docs/stories/non-existent.story.md
@@ -1,3 +1,4 @@
1
1
  "use strict";
2
+ /* eslint-disable traceability/valid-annotation-format */
2
3
  // Sample code with annotation to update
3
4
  // @story docs/stories/old.story.md
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ /* eslint-disable traceability/valid-annotation-format */
6
7
  /**
7
8
  * Prettier integration tests for annotationPlacement: "inside" across multiple branch types.
8
9
  * @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ /* eslint-disable traceability/valid-annotation-format */
6
7
  /**
7
8
  * Prettier integration tests for CatchClause annotation positions.
8
9
  * @story docs/stories/025.0-DEV-CATCH-ANNOTATION-POSITION.story.md
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ /* eslint-disable traceability/valid-annotation-format */
6
7
  /**
7
8
  * Prettier integration tests for else-if annotation positions.
8
9
  * @story docs/stories/026.0-DEV-ELSE-IF-ANNOTATION-POSITION.story.md
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.formatWithPrettier = formatWithPrettier;
7
+ /* eslint-disable traceability/valid-annotation-format */
7
8
  /**
8
9
  * Shared helpers for Prettier-based integration tests.
9
10
  * @story docs/stories/025.0-DEV-CATCH-ANNOTATION-POSITION.story.md
@@ -109,6 +109,32 @@ describe("Unified require-traceability and aliases integration (Story 010.4-DEV-
109
109
  expect(messages).toHaveLength(0);
110
110
  });
111
111
  });
112
+ it("[REQ-INSIDE-BRACE-PLACEMENT][REQ-ALL-BLOCK-TYPES] unified rule and aliases accept inside-brace annotations when annotationPlacement is 'inside'", async () => {
113
+ const codeWithInsideAnnotations = `function foo() {\n // @supports docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md REQ-FN-INSIDE\n return 1;\n}`;
114
+ const config = [
115
+ {
116
+ rules: {
117
+ "traceability/require-traceability": "error",
118
+ "traceability/require-story-annotation": [
119
+ "error",
120
+ {
121
+ annotationPlacement: "inside",
122
+ },
123
+ ],
124
+ "traceability/require-req-annotation": [
125
+ "error",
126
+ {
127
+ annotationPlacement: "inside",
128
+ },
129
+ ],
130
+ },
131
+ },
132
+ ];
133
+ const result = await lintTextWithConfig(codeWithInsideAnnotations, "example.js", config);
134
+ const ruleIds = result.messages.map((m) => m.ruleId);
135
+ expect(ruleIds).not.toContain("traceability/require-story-annotation");
136
+ expect(ruleIds).not.toContain("traceability/require-req-annotation");
137
+ });
112
138
  it("[REQ-PRESETS-CANONICAL-RULE] recommended preset surfaces unified and legacy diagnostics together for missing annotations", async () => {
113
139
  const result = await lintTextWithConfig(codeMissingAll, "example.js", index_1.configs.recommended);
114
140
  const ruleIds = result.messages.map((m) => m.ruleId).sort();
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ /* eslint-disable traceability/valid-annotation-format */
6
7
  /**
7
8
  * Integration tests for require-traceability with configurable test callback exclusion.
8
9
  *
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ /* eslint-disable traceability/valid-annotation-format */
36
37
  /**
37
38
  * Tests for: docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
38
39
  * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ /* eslint-disable traceability/valid-annotation-format */
36
37
  /**
37
38
  * Performance and stress tests for maintenance tools on large workspaces.
38
39
  * @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT REQ-MAINT-VERIFY REQ-MAINT-REPORT REQ-MAINT-UPDATE REQ-MAINT-BATCH
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ /* eslint-disable traceability/valid-annotation-format */
6
7
  /**
7
8
  * Performance tests for valid-annotation-format on large annotated files.
8
9
  *
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ /* eslint-disable traceability/valid-annotation-format */
36
37
  /**
37
38
  * Tests for: docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
38
39
  * @story docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ /* eslint-disable traceability/valid-annotation-format */
6
7
  /**
7
8
  * Tests for: docs/stories/007.0-DEV-ERROR-REPORTING.story.md
8
9
  * @story docs/stories/007.0-DEV-ERROR-REPORTING.story.md
@@ -137,6 +137,26 @@ describe("Require Req Annotation Rule (Story 003.0-DEV-FUNCTION-ANNOTATIONS)", (
137
137
  code: `class C {\n /** @req REQ-EXAMPLE */\n m() {}\n}`,
138
138
  options: [{ exportPriority: "non-exported" }],
139
139
  },
140
+ {
141
+ name: "[REQ-INSIDE-BRACE-PLACEMENT][REQ-ALL-BLOCK-TYPES] function-level @supports requirement inside body is valid when annotationPlacement is 'inside'",
142
+ code: `function withInsideReq() {\n // @supports docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md REQ-FN-INSIDE\n return 1;\n}`,
143
+ options: [{ annotationPlacement: "inside" }],
144
+ },
145
+ (0, ts_language_options_1.withTsLanguageOptions)({
146
+ name: "[REQ-INSIDE-BRACE-PLACEMENT][REQ-ALL-BLOCK-TYPES] method-level @req inside body is valid when annotationPlacement is 'inside'",
147
+ code: `class C {\n method() {\n // @req REQ-METHOD-INSIDE\n return 1;\n }\n}`,
148
+ options: [{ annotationPlacement: "inside" }],
149
+ }),
150
+ {
151
+ name: "[REQ-INSIDE-BRACE-PLACEMENT] before-function @req remains valid when annotationPlacement is 'inside' (REQ-REQ-PLACEMENT-BC)",
152
+ code: `/**\n * @req REQ-BEFORE-FN\n */\nfunction beforeReqOnly() {\n return 1;\n}`,
153
+ options: [{ annotationPlacement: "inside" }],
154
+ },
155
+ (0, ts_language_options_1.withTsLanguageOptions)({
156
+ name: "[REQ-INSIDE-BRACE-PLACEMENT] before-method @req remains valid when annotationPlacement is 'inside' (REQ-REQ-PLACEMENT-BC)",
157
+ code: `class C {\n /**\n * @req REQ-BEFORE-METHOD\n */\n method() {\n return 1;\n }\n}`,
158
+ options: [{ annotationPlacement: "inside" }],
159
+ }),
140
160
  ],
141
161
  invalid: [
142
162
  {
@@ -105,6 +105,21 @@ describe('Vitest suite', () => {
105
105
  code: `withTestCase("does something", () => {});`,
106
106
  options: [{ additionalTestHelperNames: ["withTestCase"] }],
107
107
  },
108
+ {
109
+ name: "[REQ-INSIDE-BRACE-PLACEMENT][REQ-ALL-BLOCK-TYPES] function-level @supports annotation inside body is valid when annotationPlacement is 'inside'",
110
+ code: `function insideAnnotated() {\n // @supports docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md REQ-INSIDE-FN\n return 1;\n}`,
111
+ options: [{ annotationPlacement: "inside" }],
112
+ },
113
+ (0, ts_language_options_1.withTsLanguageOptions)({
114
+ name: "[REQ-INSIDE-BRACE-PLACEMENT][REQ-ALL-BLOCK-TYPES] function-level @story annotation inside body is valid when annotationPlacement is 'inside' (TS)",
115
+ code: `function insideAnnotatedTs() {\n // @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md\n return 1;\n}`,
116
+ options: [{ annotationPlacement: "inside" }],
117
+ }),
118
+ (0, ts_language_options_1.withTsLanguageOptions)({
119
+ name: "[REQ-INSIDE-BRACE-PLACEMENT] before-method @story remains valid when annotationPlacement is 'inside' (placement BC)",
120
+ code: `class C {\n /** @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md */\n method() {\n return 1;\n }\n}`,
121
+ options: [{ annotationPlacement: "inside" }],
122
+ }),
108
123
  ],
109
124
  invalid: [
110
125
  {
@@ -237,6 +252,17 @@ describe('Vitest suite', () => {
237
252
  },
238
253
  ],
239
254
  },
255
+ {
256
+ name: "[REQ-BEFORE-BRACE-ERROR][REQ-INSIDE-BRACE-PLACEMENT] before-function annotation is ignored when annotationPlacement is 'inside'",
257
+ code: `// @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md\nfunction beforeOnly() {\n return 1;\n}`,
258
+ options: [{ annotationPlacement: "inside", autoFix: false }],
259
+ errors: [
260
+ {
261
+ messageId: "missingStory",
262
+ suggestions: 1, // satisfy TypeScript's SuggestionOutput[] typing while asserting suggestion count
263
+ },
264
+ ],
265
+ },
240
266
  ],
241
267
  });
242
268
  ruleTester.run("require-story-annotation with exportPriority option", require_story_annotation_1.default, {
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ /* eslint-disable traceability/valid-annotation-format */
6
7
  /**
7
8
  * Tests for:
8
9
  * - docs/stories/020.0-DEV-TEST-ANNOTATION-VALIDATION.story.md
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ /* eslint-disable traceability/valid-annotation-format */
3
4
  /**
4
5
  * Unit tests for CatchClause insert position calculation.
5
6
  * @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ /* eslint-disable traceability/valid-annotation-format */
3
4
  /**
4
5
  * Unit tests for else-if insert position calculation.
5
6
  * @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ /* eslint-disable traceability/valid-annotation-format */
3
4
  /**
4
5
  * Unit tests for branch annotation helpers
5
6
  * Tests for: docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-traceability",
3
- "version": "1.20.0",
3
+ "version": "1.21.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",
@@ -26,7 +26,7 @@ For function-level traceability, the plugin exposes a unified rule and two legac
26
26
  - `traceability/require-traceability` is the **canonical function-level rule** for new configurations. It ensures functions and methods have both story coverage and requirement coverage, and it accepts either `@supports` (preferred) or legacy `@story` / `@req` annotations.
27
27
  - `traceability/require-story-annotation` and `traceability/require-req-annotation` are **backward-compatible aliases** that focus on the story and requirement aspects separately. They are retained for existing configurations and share the same underlying implementation model as the unified rule, but new ESLint configs should normally rely on `traceability/require-traceability` rather than enabling these legacy keys directly.
28
28
 
29
- All three rule keys can still be configured individually if you need fine-grained control (for example, to tune severities separately), but the recommended and strict presets enable `traceability/require-traceability` by default and keep the legacy keys primarily for projects that adopted them before the unified rule existed. When the underlying function rules are configured with `annotationPlacement: "inside"`, the unified `require-traceability` rule honours inside-brace placement for function and method bodies in the same formatter-aware way that `require-branch-annotation` handles branches.
29
+ All three rule keys can still be configured individually if you need fine-grained control (for example, to tune severities separately), but the recommended and strict presets enable `traceability/require-traceability` by default and keep the legacy keys primarily for projects that adopted them before the unified rule existed. Current releases support `annotationPlacement` on both the branch-level rule and the function-level rules, so you can standardize on inside-brace placement for if/else/try/catch/switch/function/loop blocks. When you leave `annotationPlacement` unspecified, all rules default to the historical before-function/before-branch behavior for backward compatibility.
30
30
 
31
31
  ### traceability/require-traceability
32
32
 
@@ -44,7 +44,7 @@ Options:
44
44
  - `methodAnnotationTemplate` (string, optional) – Overrides the default placeholder JSDoc used when inserting missing `@story` annotations for class methods and TypeScript method signatures. When omitted or blank, falls back to `annotationTemplate` if provided, otherwise the built-in template.
45
45
  - `autoFix` (boolean, optional) – When set to `false`, disables all automatic fix behavior for this rule while retaining its suggestions and diagnostics. When omitted or `true`, the rule behaves as before, inserting placeholder annotations in `--fix` mode.
46
46
  - `excludeTestCallbacks` (boolean, optional) – When `true` (default), excludes anonymous arrow functions that are direct callbacks to common test framework functions (for example, Jest/Mocha/Vitest `describe`/`it`/`test`/`beforeEach`/`afterEach`/`beforeAll`/`afterAll`, plus focused/skipped/concurrent variants such as `fdescribe`, `xdescribe`, `fit`, `xit`, `test.concurrent`, `describe.concurrent`) from function-level annotation requirements. This assumes those test files are already covered by file-level `@supports` annotations and `traceability/require-test-traceability`. When set to `false`, these callbacks are treated like any other arrow function and must be annotated when in-scope.
47
- - `annotationPlacement` ("before" | "inside", optional) Controls whether the rule looks for annotations immediately before functions (`"before"`, the default and backward-compatible behaviour) or allows annotations as the first comment-only lines inside function and method bodies (`"inside"`). In inside mode, the rule continues to treat TypeScript declarations and signature-only nodes (such as `TSDeclareFunction` and `TSMethodSignature`) as before-function only, since they have no executable body.
47
+ - `annotationPlacement` ("before" | "inside", optional)  Controls whether the rule treats before-function comments as the source of annotations (`"before"`, the default and backward-compatible behavior) or instead requires annotations as the first comment-only lines inside function and method bodies (`"inside"`). In `"inside"` mode, JSDoc and before-function comments are ignored for block-bodied functions and methods, and only inside-body comments are considered; declaration-only nodes (e.g., TS signatures) continue to use before-node annotations.
48
48
 
49
49
  Default Severity: `error`
50
50
  Example:
@@ -71,7 +71,7 @@ Options:
71
71
 
72
72
  - `scope` (string[], optional) – Controls which function-like node types are required to have @req annotations. Allowed values: "FunctionDeclaration", "FunctionExpression", "MethodDefinition", "TSDeclareFunction", "TSMethodSignature". Default: ["FunctionDeclaration", "FunctionExpression", "MethodDefinition", "TSDeclareFunction", "TSMethodSignature"].
73
73
  - `exportPriority` ("all" | "exported" | "non-exported", optional) – Controls whether the rule checks all functions, only exported ones, or only non-exported ones. Default: "all".
74
- - `annotationPlacement` ("before" | "inside", optional) Accepted for configuration parity with `require-story-annotation`; requirement annotations are still evaluated using before-function comments and JSDoc today, so `"inside"` does not change behaviour yet.
74
+ - `annotationPlacement` ("before" | "inside", optional)  Controls whether the rule treats before-function comments as the source of annotations (`"before"`, the default and backward-compatible behavior) or instead requires annotations as the first comment-only lines inside function and method bodies (`"inside"`). In `"inside"` mode, JSDoc and before-function comments are ignored for block-bodied functions and methods, and only inside-body comments are considered; declaration-only nodes (e.g., TS signatures) continue to use before-node annotations.
75
75
 
76
76
  Default Severity: `error`
77
77
  Example (with both `@story` and `@req`, as typically used when both rules are enabled):
@@ -115,7 +115,7 @@ Behavior notes:
115
115
  Placement modes:
116
116
 
117
117
  - `"before"` mode preserves the existing semantics described above, including the dual-position behavior for `catch` and `else if` branches where comments immediately before the branch and the first comment-only lines inside the block are both acceptable and validated according to their existing precedence rules.
118
- - `"inside"` mode standardizes on the first comment-only lines inside supported branch blocks (`if`/`else if`, loops, `catch`, and `try`) for validation and auto-fix insertion. This placement is designed to work well with Prettier and other formatters, while the current implementation still treats many before-branch annotations as needing migration and may, in corner cases where it cannot confidently rewrite placement, insert new placeholder comments above the branch rather than moving existing ones. Story `028.0-DEV-FUNCTION-PLACEMENT` also extends this inside-brace standard to function and method bodies via `require-story-annotation` when configured, so projects can apply a single inside-brace rule consistently to both branches and functions.
118
+ - `"inside"` mode standardizes on the first comment-only lines inside supported branch blocks (`if`/`else if`, loops, `catch`, and `try`) for validation and auto-fix insertion. This placement is designed to work well with Prettier and other formatters, while the current implementation still treats many before-branch annotations as needing migration and may, in corner cases where it cannot confidently rewrite placement, insert new placeholder comments above the branch rather than moving existing ones. Story `028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION` standardizes inside-brace placement for supported branch constructs; function-level rules continue to use before-function annotations until a future story extends the same standard to functions.
119
119
 
120
120
  For a concrete illustration of how these rules interact with Prettier, see the formatter-aware if/else/else-if example in [user-docs/examples.md](examples.md) (section **6. Branch annotations with if/else/else-if and Prettier**), which shows both the hand-written and formatted code that the rule considers valid.
121
121