eslint-plugin-traceability 1.8.1 → 1.8.3

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 (44) hide show
  1. package/CHANGELOG.md +4 -4
  2. package/README.md +4 -4
  3. package/SECURITY.md +40 -37
  4. package/lib/src/maintenance/cli.js +12 -16
  5. package/lib/src/maintenance/detect.js +28 -1
  6. package/lib/src/rules/helpers/require-story-io.d.ts +2 -2
  7. package/lib/src/rules/helpers/require-story-io.js +13 -13
  8. package/lib/src/rules/helpers/valid-annotation-format-internal.d.ts +2 -2
  9. package/lib/src/rules/helpers/valid-annotation-format-internal.js +3 -3
  10. package/lib/src/rules/helpers/valid-annotation-utils.d.ts +5 -0
  11. package/lib/src/rules/helpers/valid-annotation-utils.js +43 -5
  12. package/lib/src/rules/helpers/valid-implements-utils.d.ts +11 -11
  13. package/lib/src/rules/helpers/valid-implements-utils.js +11 -11
  14. package/lib/src/rules/helpers/valid-story-reference-helpers.js +19 -0
  15. package/lib/src/rules/prefer-implements-annotation.d.ts +7 -7
  16. package/lib/src/rules/prefer-implements-annotation.js +21 -21
  17. package/lib/src/rules/valid-annotation-format.js +50 -24
  18. package/lib/src/rules/valid-req-reference.js +9 -9
  19. package/lib/src/utils/annotation-checker.js +3 -1
  20. package/lib/src/utils/reqAnnotationDetection.d.ts +2 -2
  21. package/lib/src/utils/reqAnnotationDetection.js +28 -28
  22. package/lib/tests/maintenance/batch.test.js +11 -11
  23. package/lib/tests/maintenance/cli.test.js +34 -27
  24. package/lib/tests/maintenance/report.test.js +7 -7
  25. package/lib/tests/rules/prefer-implements-annotation.test.js +27 -22
  26. package/lib/tests/rules/require-branch-annotation.test.js +15 -36
  27. package/lib/tests/rules/require-req-annotation.test.js +31 -104
  28. package/lib/tests/rules/require-story-annotation.test.js +3 -3
  29. package/lib/tests/rules/require-story-io-behavior.test.js +2 -7
  30. package/lib/tests/rules/require-story-io.edgecases.test.js +2 -7
  31. package/lib/tests/rules/require-story-visitors-edgecases.test.js +8 -8
  32. package/lib/tests/rules/valid-annotation-format.test.js +23 -23
  33. package/lib/tests/rules/valid-req-reference.test.js +9 -9
  34. package/lib/tests/rules/valid-story-reference.test.js +4 -43
  35. package/lib/tests/utils/annotation-checker.test.js +2 -6
  36. package/lib/tests/utils/fsTestHelpers.d.ts +7 -0
  37. package/lib/tests/utils/fsTestHelpers.js +26 -0
  38. package/lib/tests/utils/ioTestHelpers.d.ts +7 -0
  39. package/lib/tests/utils/ioTestHelpers.js +24 -0
  40. package/lib/tests/utils/temp-dir-helpers.d.ts +14 -0
  41. package/lib/tests/utils/temp-dir-helpers.js +61 -0
  42. package/package.json +2 -2
  43. package/user-docs/api-reference.md +12 -15
  44. package/user-docs/migration-guide.md +21 -21
@@ -14,7 +14,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
14
14
  * @req REQ-TYPESCRIPT-SUPPORT - Verify TypeScript declarations are checked via shared annotation checker helper
15
15
  *
16
16
  * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
17
- * @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Verify @implements is accepted as satisfying requirement annotations
17
+ * @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Verify @supports is accepted as satisfying requirement annotations
18
18
  */
19
19
  const eslint_1 = require("eslint");
20
20
  const require_req_annotation_1 = __importDefault(require("../../src/rules/require-req-annotation"));
@@ -25,6 +25,18 @@ const ruleTester = new eslint_1.RuleTester({
25
25
  parserOptions: { ecmaVersion: 2022, sourceType: "module" },
26
26
  },
27
27
  });
28
+ /**
29
+ * Build a standard missingReq error object for a given function name.
30
+ *
31
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
32
+ * @req REQ-ANNOTATION-REQUIRED - Standardize missingReq error shape in tests
33
+ */
34
+ function missingReq(functionName) {
35
+ return {
36
+ messageId: "missingReq",
37
+ data: { name: functionName, functionName },
38
+ };
39
+ }
28
40
  /**
29
41
  * @trace Story 003.0-DEV-FUNCTION-ANNOTATIONS / REQ-TYPESCRIPT-SUPPORT
30
42
  * Exercise the require-req-annotation rule's behavior on TSDeclareFunction and
@@ -50,22 +62,12 @@ const ruleTester = new eslint_1.RuleTester({
50
62
  {
51
63
  name: "[REQ-TYPESCRIPT-SUPPORT] missing @req on TSDeclareFunction",
52
64
  code: `declare function baz(): void;`,
53
- errors: [
54
- {
55
- messageId: "missingReq",
56
- data: { name: "baz", functionName: "baz" },
57
- },
58
- ],
65
+ errors: [missingReq("baz")],
59
66
  },
60
67
  {
61
68
  name: "[REQ-TYPESCRIPT-SUPPORT] missing @req on TSMethodSignature",
62
69
  code: `interface I { method(): void; }`,
63
- errors: [
64
- {
65
- messageId: "missingReq",
66
- data: { name: "method", functionName: "method" },
67
- },
68
- ],
70
+ errors: [missingReq("method")],
69
71
  },
70
72
  ],
71
73
  });
@@ -78,7 +80,7 @@ describe("Require Req Annotation Rule (Story 003.0-DEV-FUNCTION-ANNOTATIONS)", (
78
80
  },
79
81
  {
80
82
  name: "[REQ-REQUIRE-ACCEPTS-IMPLEMENTS] valid with only @implements annotation",
81
- code: `/**\n * @implements docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ANNOTATION-REQUIRED\n */\nfunction implOnly() {}`,
83
+ code: `/**\n * @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ANNOTATION-REQUIRED\n */\nfunction implOnly() {}`,
82
84
  },
83
85
  {
84
86
  name: "[REQ-ANNOTATION-REQUIRED] valid with @story and @req annotations",
@@ -140,159 +142,84 @@ describe("Require Req Annotation Rule (Story 003.0-DEV-FUNCTION-ANNOTATIONS)", (
140
142
  {
141
143
  name: "[REQ-ANNOTATION-REQUIRED][REQ-REQUIRE-ACCEPTS-IMPLEMENTS] missing @req on function without JSDoc remains invalid under multi-story support",
142
144
  code: `function baz() {}`,
143
- errors: [
144
- {
145
- messageId: "missingReq",
146
- data: { name: "baz", functionName: "baz" },
147
- },
148
- ],
145
+ errors: [missingReq("baz")],
149
146
  },
150
147
  {
151
148
  name: "[REQ-ANNOTATION-REQUIRED] missing @req on function with only @story annotation",
152
149
  code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n */\nfunction qux() {}`,
153
- errors: [
154
- {
155
- messageId: "missingReq",
156
- data: { name: "qux", functionName: "qux" },
157
- },
158
- ],
150
+ errors: [missingReq("qux")],
159
151
  },
160
152
  {
161
153
  name: "[REQ-FUNCTION-DETECTION][Story 003.0] missing @req on FunctionExpression assigned to variable",
162
154
  code: `const fn = function () {};`,
163
- errors: [
164
- {
165
- messageId: "missingReq",
166
- data: { name: "fn", functionName: "fn" },
167
- },
168
- ],
155
+ errors: [missingReq("fn")],
169
156
  },
170
157
  {
171
158
  name: "[REQ-FUNCTION-DETECTION][Story 003.0] missing @req on anonymous FunctionExpression (no variable name)",
172
159
  code: `(function () {})();`,
173
- errors: [
174
- {
175
- messageId: "missingReq",
176
- data: { name: "(anonymous)", functionName: "(anonymous)" },
177
- },
178
- ],
160
+ errors: [missingReq("(anonymous)")],
179
161
  },
180
162
  {
181
163
  name: "[REQ-FUNCTION-DETECTION][Story 003.0] missing @req on MethodDefinition in class",
182
164
  code: `class C {\n m() {}\n}`,
183
- errors: [
184
- {
185
- messageId: "missingReq",
186
- data: { name: "m", functionName: "m" },
187
- },
188
- ],
165
+ errors: [missingReq("m")],
189
166
  },
190
167
  {
191
168
  name: "[REQ-FUNCTION-DETECTION][Story 003.0] missing @req on MethodDefinition in object literal",
192
169
  code: `const o = { m() {} };`,
193
- errors: [
194
- {
195
- messageId: "missingReq",
196
- data: { name: "m", functionName: "m" },
197
- },
198
- ],
170
+ errors: [missingReq("m")],
199
171
  },
200
172
  (0, ts_language_options_1.withTsLanguageOptions)({
201
173
  name: "[REQ-TYPESCRIPT-SUPPORT][REQ-FUNCTION-DETECTION][Story 003.0] missing @req on TS FunctionExpression in variable declarator",
202
174
  code: `const fn = function () {};`,
203
- errors: [
204
- {
205
- messageId: "missingReq",
206
- data: { name: "fn", functionName: "fn" },
207
- },
208
- ],
175
+ errors: [missingReq("fn")],
209
176
  }),
210
177
  (0, ts_language_options_1.withTsLanguageOptions)({
211
178
  name: "[REQ-TYPESCRIPT-SUPPORT][REQ-FUNCTION-DETECTION][Story 003.0] missing @req on exported TS FunctionExpression in variable declarator",
212
179
  code: `export const fn = function () {};`,
213
- errors: [
214
- {
215
- messageId: "missingReq",
216
- data: { name: "fn", functionName: "fn" },
217
- },
218
- ],
180
+ errors: [missingReq("fn")],
219
181
  }),
220
182
  {
221
183
  name: "[REQ-CONFIGURABLE-SCOPE][Story 003.0] FunctionDeclaration still reported when scope only includes FunctionDeclaration",
222
184
  code: `function scoped() {}`,
223
185
  options: [{ scope: ["FunctionDeclaration"] }],
224
- errors: [
225
- {
226
- messageId: "missingReq",
227
- data: { name: "scoped", functionName: "scoped" },
228
- },
229
- ],
186
+ errors: [missingReq("scoped")],
230
187
  },
231
188
  {
232
189
  name: "[REQ-EXPORT-PRIORITY][Story 003.0] exported function reported when exportPriority is 'exported'",
233
190
  code: `export function exportedFn() {}`,
234
191
  options: [{ exportPriority: "exported" }],
235
- errors: [
236
- {
237
- messageId: "missingReq",
238
- data: { name: "exportedFn", functionName: "exportedFn" },
239
- },
240
- ],
192
+ errors: [missingReq("exportedFn")],
241
193
  },
242
194
  {
243
195
  name: "[REQ-EXPORT-PRIORITY][Story 003.0] non-exported function reported when exportPriority is 'non-exported'",
244
196
  code: `function nonExported() {}`,
245
197
  options: [{ exportPriority: "non-exported" }],
246
- errors: [
247
- {
248
- messageId: "missingReq",
249
- data: { name: "nonExported", functionName: "nonExported" },
250
- },
251
- ],
198
+ errors: [missingReq("nonExported")],
252
199
  },
253
200
  {
254
201
  name: "[REQ-EXPORT-PRIORITY][Story 003.0] exported method reported when exportPriority is 'exported'",
255
202
  code: `export class C {\n m() {}\n}`,
256
- errors: [
257
- {
258
- messageId: "missingReq",
259
- data: { name: "m", functionName: "m" },
260
- },
261
- ],
203
+ errors: [missingReq("m")],
262
204
  options: [{ exportPriority: "exported" }],
263
205
  },
264
206
  {
265
207
  name: "[REQ-EXPORT-PRIORITY][Story 003.0] non-exported method reported when exportPriority is 'non-exported'",
266
208
  code: `class C {\n m() {}\n}`,
267
- errors: [
268
- {
269
- messageId: "missingReq",
270
- data: { name: "m", functionName: "m" },
271
- },
272
- ],
209
+ errors: [missingReq("m")],
273
210
  options: [{ exportPriority: "non-exported" }],
274
211
  },
275
212
  {
276
213
  name: "[REQ-EXPORT-PRIORITY][Story 003.0] exported FunctionExpression reported when exportPriority is 'exported'",
277
214
  code: `export const fn = function () {};`,
278
215
  options: [{ exportPriority: "exported" }],
279
- errors: [
280
- {
281
- messageId: "missingReq",
282
- data: { name: "fn", functionName: "fn" },
283
- },
284
- ],
216
+ errors: [missingReq("fn")],
285
217
  },
286
218
  {
287
219
  name: "[REQ-EXPORT-PRIORITY][Story 003.0] non-exported FunctionExpression reported when exportPriority is 'non-exported'",
288
220
  code: `const fn = function () {};`,
289
221
  options: [{ exportPriority: "non-exported" }],
290
- errors: [
291
- {
292
- messageId: "missingReq",
293
- data: { name: "fn", functionName: "fn" },
294
- },
295
- ],
222
+ errors: [missingReq("fn")],
296
223
  },
297
224
  ],
298
225
  });
@@ -8,7 +8,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
8
8
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
9
9
  * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
10
10
  * @req REQ-ANNOTATION-REQUIRED - Verify require-story-annotation rule enforces @story annotation on functions
11
- * @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Verify @implements annotation is accepted as satisfying story requirements
11
+ * @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Verify @supports annotation is accepted as satisfying story requirements
12
12
  */
13
13
  const eslint_1 = require("eslint");
14
14
  const require_story_annotation_1 = __importDefault(require("../../src/rules/require-story-annotation"));
@@ -25,7 +25,7 @@ describe("Require Story Annotation Rule (Story 003.0-DEV-FUNCTION-ANNOTATIONS)",
25
25
  },
26
26
  {
27
27
  name: "[REQ-REQUIRE-ACCEPTS-IMPLEMENTS] valid with only @implements annotation",
28
- code: `/**\n * @implements docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ANNOTATION-REQUIRED\n */\nfunction implOnly() {}`,
28
+ code: `/**\n * @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ANNOTATION-REQUIRED\n */\nfunction implOnly() {}`,
29
29
  },
30
30
  {
31
31
  name: "[REQ-ANNOTATION-REQUIRED] valid with line comment @story annotation",
@@ -65,7 +65,7 @@ declare function tsDecl(): void;`,
65
65
  invalid: [
66
66
  {
67
67
  // Backward compatibility: plain unannotated functions remain invalid under multi-story support
68
- name: "[REQ-ANNOTATION-REQUIRED][BACKCOMPAT] missing @story annotation on function with no @implements",
68
+ name: "[REQ-ANNOTATION-REQUIRED][BACKCOMPAT] missing @story annotation on function with no @supports",
69
69
  code: `function bar() {}`,
70
70
  output: `/** @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md */\nfunction bar() {}`,
71
71
  errors: [
@@ -6,6 +6,7 @@
6
6
  */
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  const require_story_io_1 = require("../../src/rules/helpers/require-story-io");
9
+ const ioTestHelpers_1 = require("../utils/ioTestHelpers");
9
10
  describe("Require Story IO helpers - additional behavior (Story 003.0)", () => {
10
11
  test("parentChainHasStory returns false when sourceCode.getCommentsBefore is not a function", () => {
11
12
  const fakeSource = {}; // no getCommentsBefore function
@@ -39,12 +40,6 @@ describe("Require Story IO helpers - additional behavior (Story 003.0)", () => {
39
40
  expect((0, require_story_io_1.fallbackTextBeforeHasStory)(fakeSource, node)).toBe(false);
40
41
  });
41
42
  test("fallbackTextBeforeHasStory detects @story in text before node.range", () => {
42
- const story = "@story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md";
43
- const pre = `/* ${story} */\n`;
44
- const rest = "function y() {}";
45
- const full = pre + rest;
46
- const fakeSource = { getText: () => full };
47
- const node = { range: [full.indexOf("function"), full.length] };
48
- expect((0, require_story_io_1.fallbackTextBeforeHasStory)(fakeSource, node)).toBe(true);
43
+ (0, ioTestHelpers_1.runFallbackTextBeforeHasStoryDetectsStoryTest)("@story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md", require_story_io_1.fallbackTextBeforeHasStory);
49
44
  });
50
45
  });
@@ -6,6 +6,7 @@
6
6
  */
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  const require_story_io_1 = require("../../src/rules/helpers/require-story-io");
9
+ const ioTestHelpers_1 = require("../utils/ioTestHelpers");
9
10
  describe("Require Story IO helpers - edge cases (Story 003.0)", () => {
10
11
  test("linesBeforeHasStory returns false when source.lines missing or node.loc missing", () => {
11
12
  const fakeSource = {};
@@ -24,13 +25,7 @@ describe("Require Story IO helpers - edge cases (Story 003.0)", () => {
24
25
  expect((0, require_story_io_1.fallbackTextBeforeHasStory)(fakeSource2, node2)).toBe(false);
25
26
  });
26
27
  test("fallbackTextBeforeHasStory detects @story in text before node.range", () => {
27
- const story = "@story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md";
28
- const pre = `/* ${story} */\\n`;
29
- const rest = "function x() {}";
30
- const full = pre + rest;
31
- const fakeSource = { getText: () => full };
32
- const node = { range: [full.indexOf("function"), full.length] };
33
- expect((0, require_story_io_1.fallbackTextBeforeHasStory)(fakeSource, node)).toBe(true);
28
+ (0, ioTestHelpers_1.runFallbackTextBeforeHasStoryDetectsStoryTest)(require_story_io_1.fallbackTextBeforeHasStory);
34
29
  });
35
30
  test("parentChainHasStory returns true when ancestor comments contain @story", () => {
36
31
  const fakeSource = {
@@ -6,20 +6,20 @@
6
6
  */
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  const require_story_visitors_1 = require("../../src/rules/helpers/require-story-visitors");
9
+ const makeVisitors = () => {
10
+ const fakeContext = { getFilename: () => "file.ts" };
11
+ const fakeSource = { getText: () => "" };
12
+ const options = { shouldProcessNode: () => true };
13
+ return (0, require_story_visitors_1.buildVisitors)(fakeContext, fakeSource, options);
14
+ };
9
15
  describe("Require Story Visitors - behavior (Story 003.0)", () => {
10
16
  test("build visitors returns handlers for FunctionDeclaration and ArrowFunctionExpression", () => {
11
- const fakeContext = { getFilename: () => "file.ts" };
12
- const fakeSource = { getText: () => "" };
13
- const options = { shouldProcessNode: () => true };
14
- const visitors = (0, require_story_visitors_1.buildVisitors)(fakeContext, fakeSource, options);
17
+ const visitors = makeVisitors();
15
18
  expect(typeof visitors.FunctionDeclaration).toBe("function");
16
19
  expect(typeof visitors.ArrowFunctionExpression).toBe("function");
17
20
  });
18
21
  test("FunctionDeclaration handler uses context.getFilename and doesn't throw when node lacks id", () => {
19
- const fakeContext = { getFilename: () => "file.ts" };
20
- const fakeSource = { getText: () => "" };
21
- const options = { shouldProcessNode: () => true };
22
- const visitors = (0, require_story_visitors_1.buildVisitors)(fakeContext, fakeSource, options);
22
+ const visitors = makeVisitors();
23
23
  const handler = visitors.FunctionDeclaration;
24
24
  expect(() => handler({})).not.toThrow();
25
25
  });
@@ -20,9 +20,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
20
20
  * @req REQ-CONFIGURABLE-PATTERNS-FALLBACK - Invalid regex patterns fall back to default behavior without crashing
21
21
  * Tests for: docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
22
22
  * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
23
- * @req REQ-IMPLEMENTS-PARSE - Rule parses @implements annotations with story and requirement references
24
- * @req REQ-FORMAT-VALIDATION - Rule validates story and requirement formats inside @implements annotations
25
- * @req REQ-MIXED-SUPPORT - Rule supports mixed @story/@req/@implements usage in the same comment
23
+ * @req REQ-SUPPORTS-PARSE - Rule parses @supports annotations with story and requirement references
24
+ * @req REQ-FORMAT-VALIDATION - Rule validates story and requirement formats inside @supports annotations
25
+ * @req REQ-MIXED-SUPPORT - Rule supports mixed @story/@req/@supports usage in the same comment
26
26
  */
27
27
  const eslint_1 = require("eslint");
28
28
  const valid_annotation_format_1 = __importDefault(require("../../src/rules/valid-annotation-format"));
@@ -174,24 +174,24 @@ describe("Valid Annotation Format Rule (Story 005.0-DEV-ANNOTATION-VALIDATION)",
174
174
  ],
175
175
  },
176
176
  {
177
- name: "[REQ-IMPLEMENTS-PARSE] valid single @implements with one story and one requirement (default patterns)",
177
+ name: "[REQ-IMPLEMENTS-PARSE] valid single @supports with one story and one requirement (default patterns)",
178
178
  code: `/**
179
- * @implements docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md REQ-IMPLEMENTS-PARSE
179
+ * @supports docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md REQ-IMPLEMENTS-PARSE
180
180
  */`,
181
181
  },
182
182
  {
183
- name: "[REQ-IMPLEMENTS-PARSE] valid multiple @implements lines with different stories and requirements",
183
+ name: "[REQ-IMPLEMENTS-PARSE] valid multiple @supports lines with different stories and requirements",
184
184
  code: `/**
185
- * @implements docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md REQ-IMPLEMENTS-PARSE REQ-FORMAT-VALIDATION
186
- * @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-FORMAT-SPECIFICATION
185
+ * @supports docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md REQ-IMPLEMENTS-PARSE REQ-FORMAT-VALIDATION
186
+ * @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-FORMAT-SPECIFICATION
187
187
  */`,
188
188
  },
189
189
  {
190
- name: "[REQ-MIXED-SUPPORT] valid mixed @story/@req/@implements usage in same block comment",
190
+ name: "[REQ-MIXED-SUPPORT] valid mixed @story/@req/@supports usage in same block comment",
191
191
  code: `/**
192
192
  * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
193
193
  * @req REQ-MIXED-SUPPORT
194
- * @implements docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md REQ-IMPLEMENTS-PARSE REQ-FORMAT-VALIDATION REQ-MIXED-SUPPORT
194
+ * @supports docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md REQ-IMPLEMENTS-PARSE REQ-FORMAT-VALIDATION REQ-MIXED-SUPPORT
195
195
  */`,
196
196
  },
197
197
  ],
@@ -507,33 +507,33 @@ describe("Valid Annotation Format Rule (Story 005.0-DEV-ANNOTATION-VALIDATION)",
507
507
  ],
508
508
  },
509
509
  makeInvalid({
510
- name: "[REQ-IMPLEMENTS-PARSE] @implements with no value is invalid",
510
+ name: "[REQ-SUPPORTS-PARSE] @supports with no value is invalid",
511
511
  code: `/**
512
- * @implements
512
+ * @supports
513
513
  */`,
514
514
  messageId: "invalidImplementsFormat",
515
- details: 'Missing story path and requirement IDs for @implements annotation. Expected a value like "docs/stories/005.0-DEV-EXAMPLE.story.md REQ-EXAMPLE".',
515
+ details: 'Missing story path and requirement IDs for @supports annotation. Expected a value like "docs/stories/005.0-DEV-EXAMPLE.story.md REQ-EXAMPLE".',
516
516
  }),
517
517
  makeInvalid({
518
- name: "[REQ-IMPLEMENTS-PARSE] @implements with only story path and no requirement IDs is invalid",
518
+ name: "[REQ-SUPPORTS-PARSE] @supports with only story path and no requirement IDs is invalid",
519
519
  code: `/**
520
- * @implements docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
520
+ * @supports docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
521
521
  */`,
522
522
  messageId: "invalidImplementsFormat",
523
- details: 'Missing requirement IDs for @implements annotation. Expected a value like "docs/stories/005.0-DEV-EXAMPLE.story.md REQ-EXAMPLE".',
523
+ details: 'Missing requirement IDs for @supports annotation. Expected a value like "docs/stories/005.0-DEV-EXAMPLE.story.md REQ-EXAMPLE".',
524
524
  }),
525
525
  makeInvalid({
526
- name: "[REQ-FORMAT-VALIDATION] @implements with invalid story path format",
526
+ name: "[REQ-FORMAT-VALIDATION] @supports with invalid story path format",
527
527
  code: `/**
528
- * @implements invalid/path.txt REQ-IMPLEMENTS-PARSE
528
+ * @supports invalid/path.txt REQ-IMPLEMENTS-PARSE
529
529
  */`,
530
530
  messageId: "invalidImplementsFormat",
531
- details: 'Invalid story path "invalid/path.txt" for @implements annotation. Expected a path like "docs/stories/005.0-DEV-EXAMPLE.story.md".',
531
+ details: 'Invalid story path "invalid/path.txt" for @supports annotation. Expected a path like "docs/stories/005.0-DEV-EXAMPLE.story.md".',
532
532
  }),
533
533
  {
534
- name: "[REQ-FORMAT-VALIDATION] @implements with invalid requirement ID format",
534
+ name: "[REQ-FORMAT-VALIDATION] @supports with invalid requirement ID format",
535
535
  code: `/**
536
- * @implements docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md REQ-VALID invalid-format
536
+ * @supports docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md REQ-VALID invalid-format
537
537
  */`,
538
538
  errors: [
539
539
  {
@@ -545,9 +545,9 @@ describe("Valid Annotation Format Rule (Story 005.0-DEV-ANNOTATION-VALIDATION)",
545
545
  ],
546
546
  },
547
547
  {
548
- name: "[REQ-FORMAT-VALIDATION] @implements with multiple requirement IDs where one is invalid",
548
+ name: "[REQ-FORMAT-VALIDATION] @supports with multiple requirement IDs where one is invalid",
549
549
  code: `/**
550
- * @implements docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md REQ-VALID-1 REQ-VALID-2 bad-id
550
+ * @supports docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md REQ-VALID-1 REQ-VALID-2 bad-id
551
551
  */`,
552
552
  errors: [
553
553
  {
@@ -33,13 +33,13 @@ describe("Valid Req Reference Rule (Story 010.0-DEV-DEEP-VALIDATION)", () => {
33
33
  // @req REQ-BULLET-LIST`,
34
34
  },
35
35
  {
36
- name: "[REQ-DEEP-IMPLEMENTS] single implements line with multiple requirements in multi-story fixture (see 010.2-DEV-MULTI-STORY-SUPPORT)",
37
- code: `// @implements tests/fixtures/story_multi_a.md REQ-SHARED-ID REQ-ONLY-A`,
36
+ name: "[REQ-DEEP-IMPLEMENTS] single supports line with multiple requirements in multi-story fixture (see 010.2-DEV-MULTI-STORY-SUPPORT)",
37
+ code: `// @supports tests/fixtures/story_multi_a.md REQ-SHARED-ID REQ-ONLY-A`,
38
38
  },
39
39
  {
40
- name: "[REQ-DEEP-IMPLEMENTS] multi-story implements with shared requirement IDs (see 010.2-DEV-MULTI-STORY-SUPPORT)",
41
- code: `// @implements tests/fixtures/story_multi_a.md REQ-SHARED-ID REQ-ONLY-A
42
- // @implements tests/fixtures/story_multi_b.md REQ-SHARED-ID REQ-ONLY-B`,
40
+ name: "[REQ-DEEP-IMPLEMENTS] multi-story supports with shared requirement IDs (see 010.2-DEV-MULTI-STORY-SUPPORT)",
41
+ code: `// @supports tests/fixtures/story_multi_a.md REQ-SHARED-ID REQ-ONLY-A
42
+ // @supports tests/fixtures/story_multi_b.md REQ-SHARED-ID REQ-ONLY-B`,
43
43
  },
44
44
  ],
45
45
  invalid: [
@@ -98,8 +98,8 @@ describe("Valid Req Reference Rule (Story 010.0-DEV-DEEP-VALIDATION)", () => {
98
98
  ],
99
99
  },
100
100
  {
101
- name: "[REQ-DEEP-IMPLEMENTS] missing implements requirement in multi-story fixture (see 010.2-DEV-MULTI-STORY-SUPPORT)",
102
- code: `// @implements tests/fixtures/story_multi_a.md REQ-NOT-IN-A`,
101
+ name: "[REQ-DEEP-IMPLEMENTS] missing supports requirement in multi-story fixture (see 010.2-DEV-MULTI-STORY-SUPPORT)",
102
+ code: `// @supports tests/fixtures/story_multi_a.md REQ-NOT-IN-A`,
103
103
  errors: [
104
104
  {
105
105
  messageId: "reqMissing",
@@ -111,8 +111,8 @@ describe("Valid Req Reference Rule (Story 010.0-DEV-DEEP-VALIDATION)", () => {
111
111
  ],
112
112
  },
113
113
  {
114
- name: "[REQ-DEEP-IMPLEMENTS] disallow path traversal in implements story path (see 010.2-DEV-MULTI-STORY-SUPPORT)",
115
- code: `// @implements ../tests/fixtures/story_multi_a.md REQ-SHARED-ID`,
114
+ name: "[REQ-DEEP-IMPLEMENTS] disallow path traversal in supports story path (see 010.2-DEV-MULTI-STORY-SUPPORT)",
115
+ code: `// @supports ../tests/fixtures/story_multi_a.md REQ-SHARED-ID`,
116
116
  errors: [
117
117
  {
118
118
  messageId: "invalidPath",
@@ -50,6 +50,7 @@ const eslint_1 = require("eslint");
50
50
  const valid_story_reference_1 = __importDefault(require("../../src/rules/valid-story-reference"));
51
51
  const storyReferenceUtils_1 = require("../../src/utils/storyReferenceUtils");
52
52
  const path = __importStar(require("path"));
53
+ const fsTestHelpers_1 = require("../utils/fsTestHelpers");
53
54
  const ruleTester = new eslint_1.RuleTester({
54
55
  languageOptions: { parserOptions: { ecmaVersion: 2020 } },
55
56
  });
@@ -223,19 +224,7 @@ describe("Valid Story Reference Rule Configuration and Boundaries (Story 006.0-D
223
224
  });
224
225
  it("[REQ-CONFIGURABLE-PATHS] uses storyDirectories when resolving relative paths (Story 006.0-DEV-FILE-VALIDATION)", () => {
225
226
  const storyPath = pathModule.join(process.cwd(), "docs/stories/001.0-DEV-PLUGIN-SETUP.story.md");
226
- jest.spyOn(fs, "existsSync").mockImplementation((...args) => {
227
- const p = args[0];
228
- return p === storyPath;
229
- });
230
- jest.spyOn(fs, "statSync").mockImplementation((...args) => {
231
- const p = args[0];
232
- if (p === storyPath) {
233
- return {
234
- isFile: () => true,
235
- };
236
- }
237
- throw Object.assign(new Error("ENOENT"), { code: "ENOENT" });
238
- });
227
+ (0, fsTestHelpers_1.mockFsForExistingFile)(fs, storyPath);
239
228
  const diagnostics = runRuleOnCode(`// @story 001.0-DEV-PLUGIN-SETUP.story.md`, [{ storyDirectories: ["docs/stories"] }]);
240
229
  // When storyDirectories is configured, the underlying resolution should
241
230
  // treat the path as valid; absence of errors is asserted via RuleTester
@@ -274,21 +263,7 @@ describe("Valid Story Reference Rule Configuration and Boundaries (Story 006.0-D
274
263
  const pathModule = require("path");
275
264
  const outsideDir = pathModule.resolve(pathModule.sep, "tmp", "outside");
276
265
  const outsideFile = pathModule.join(outsideDir, "external-story.story.md");
277
- jest.spyOn(fs, "existsSync").mockImplementation((...args) => {
278
- const p = args[0];
279
- return p === outsideFile;
280
- });
281
- jest.spyOn(fs, "statSync").mockImplementation((...args) => {
282
- const p = args[0];
283
- if (p === outsideFile) {
284
- return {
285
- isFile: () => true,
286
- };
287
- }
288
- const err = new Error("ENOENT");
289
- err.code = "ENOENT";
290
- throw err;
291
- });
266
+ (0, fsTestHelpers_1.mockFsForExistingFile)(fs, outsideFile);
292
267
  const diagnostics = runRuleOnCode(`// @story ${outsideFile}\n// @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md`, [
293
268
  {
294
269
  allowAbsolutePaths: true,
@@ -308,21 +283,7 @@ describe("Valid Story Reference Rule Configuration and Boundaries (Story 006.0-D
308
283
  const fs = require("fs");
309
284
  const pathModule = require("path");
310
285
  const storyPath = pathModule.join(process.cwd(), "docs/stories/developer-story.map.md");
311
- jest.spyOn(fs, "existsSync").mockImplementation((...args) => {
312
- const p = args[0];
313
- return p === storyPath;
314
- });
315
- jest.spyOn(fs, "statSync").mockImplementation((...args) => {
316
- const p = args[0];
317
- if (p === storyPath) {
318
- return {
319
- isFile: () => true,
320
- };
321
- }
322
- const err = new Error("ENOENT");
323
- err.code = "ENOENT";
324
- throw err;
325
- });
286
+ (0, fsTestHelpers_1.mockFsForExistingFile)(fs, storyPath);
326
287
  const diagnostics = runRuleOnCode(`// @story docs/stories/developer-story.map.md\n// @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md`, [
327
288
  {
328
289
  storyDirectories: ["docs/stories"],
@@ -11,10 +11,6 @@ const eslint_1 = require("eslint");
11
11
  const annotation_checker_1 = require("../../src/utils/annotation-checker");
12
12
  const ts_language_options_1 = require("./ts-language-options");
13
13
  const ruleTester = new eslint_1.RuleTester();
14
- const withTsAnnotationCheckerOptions = (test) => ({
15
- ...test,
16
- languageOptions: ts_language_options_1.tsRuleTesterLanguageOptions,
17
- });
18
14
  /**
19
15
  * Shared helper for running tests that exercise the annotation-checker logic
20
16
  * for TypeScript constructs.
@@ -26,8 +22,8 @@ const withTsAnnotationCheckerOptions = (test) => ({
26
22
  function runAnnotationCheckerTests(ruleName, config) {
27
23
  const { rule, valid, invalid } = config;
28
24
  ruleTester.run(ruleName, rule, {
29
- valid: valid.map(withTsAnnotationCheckerOptions),
30
- invalid: invalid.map(withTsAnnotationCheckerOptions),
25
+ valid: valid.map(ts_language_options_1.withTsLanguageOptions),
26
+ invalid: invalid.map(ts_language_options_1.withTsLanguageOptions),
31
27
  });
32
28
  }
33
29
  const rule = {
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Shared filesystem mocking utilities for rule tests.
3
+ *
4
+ * @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
5
+ * @req REQ-TEST-UTILS-FS - Provide helpers to reduce duplication in fs-related tests
6
+ */
7
+ export declare function mockFsForExistingFile(fs: typeof import("fs"), filePath: string): void;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mockFsForExistingFile = mockFsForExistingFile;
4
+ /**
5
+ * Shared filesystem mocking utilities for rule tests.
6
+ *
7
+ * @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
8
+ * @req REQ-TEST-UTILS-FS - Provide helpers to reduce duplication in fs-related tests
9
+ */
10
+ function mockFsForExistingFile(fs, filePath) {
11
+ jest.spyOn(fs, "existsSync").mockImplementation((...args) => {
12
+ const p = args[0];
13
+ return p === filePath;
14
+ });
15
+ jest.spyOn(fs, "statSync").mockImplementation((...args) => {
16
+ const p = args[0];
17
+ if (p === filePath) {
18
+ return {
19
+ isFile: () => true,
20
+ };
21
+ }
22
+ const err = new Error("ENOENT");
23
+ err.code = "ENOENT";
24
+ throw err;
25
+ });
26
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Shared IO helper tests for require-story-io behavior.
3
+ *
4
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
5
+ * @req REQ-TEST-UTILS-IO - Provide reusable helpers for IO-related edge case tests
6
+ */
7
+ export declare function runFallbackTextBeforeHasStoryDetectsStoryTest(storyAnnotationOrFallbackFn?: string | ((_source: any, _node: any) => boolean), maybeFallbackFn?: (_source: any, _node: any) => boolean): void;