eslint-plugin-traceability 1.4.8 → 1.4.9

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.
@@ -37,10 +37,14 @@ function validateStoryPath(opts) {
37
37
  }
38
38
  /**
39
39
  * Process and validate the story path for security, extension, and existence.
40
+ * Filesystem and I/O errors are handled inside the underlying utilities
41
+ * (e.g. storyExists) and surfaced as missing-file diagnostics where appropriate.
42
+ *
40
43
  * @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
41
44
  * @req REQ-FILE-EXISTENCE - Validate that story file paths reference existing files
42
45
  * @req REQ-PATH-RESOLUTION - Resolve relative paths correctly and enforce configuration
43
46
  * @req REQ-SECURITY-VALIDATION - Prevent path traversal and absolute path usage
47
+ * @req REQ-ERROR-HANDLING - Delegate filesystem and I/O error handling to utilities
44
48
  */
45
49
  function processStoryPath(opts) {
46
50
  const { storyPath, commentNode, context, cwd, storyDirs, allowAbsolute, requireExt, } = opts;
@@ -76,8 +80,10 @@ function processStoryPath(opts) {
76
80
  });
77
81
  return;
78
82
  }
79
- // Existence check
80
- if (!(0, storyReferenceUtils_1.normalizeStoryPath)(storyPath, cwd, storyDirs).exists) {
83
+ // Existence check (filesystem and I/O errors are swallowed by utilities
84
+ // and treated as non-existent files)
85
+ const result = (0, storyReferenceUtils_1.normalizeStoryPath)(storyPath, cwd, storyDirs);
86
+ if (!result.exists) {
81
87
  context.report({
82
88
  node: commentNode,
83
89
  messageId: "fileMissing",
@@ -146,10 +152,14 @@ exports.default = {
146
152
  return {
147
153
  /**
148
154
  * Program-level handler: iterate comments and validate @story annotations.
155
+ * Filesystem and I/O errors are handled by underlying utilities and
156
+ * surfaced as missing-file diagnostics where appropriate.
157
+ *
149
158
  * @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
150
159
  * @req REQ-ANNOTATION-VALIDATION - Discover and dispatch @story annotations for validation
151
160
  * @req REQ-FILE-EXISTENCE - Ensure referenced files exist
152
161
  * @req REQ-PATH-RESOLUTION - Resolve using cwd and configured story directories
162
+ * @req REQ-ERROR-HANDLING - Delegate filesystem and I/O error handling to utilities
153
163
  */
154
164
  Program() {
155
165
  const comments = context.getSourceCode().getAllComments() || [];
@@ -7,9 +7,12 @@ export declare function buildStoryCandidates(storyPath: string, cwd: string, sto
7
7
  export declare function storyExists(paths: string[]): boolean;
8
8
  /**
9
9
  * Normalize a story path to candidate absolute paths and check existence.
10
+ * Filesystem errors are handled via `storyExists`, which suppresses exceptions
11
+ * and treats such cases as non-existent.
10
12
  * @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
11
13
  * @req REQ-PATH-RESOLUTION - Resolve relative paths correctly and enforce configuration
12
14
  * @req REQ-FILE-EXISTENCE - Validate that story file paths reference existing files
15
+ * @req REQ-ERROR-HANDLING - Handle filesystem errors gracefully without throwing
13
16
  */
14
17
  export declare function normalizeStoryPath(storyPath: string, cwd: string, storyDirs: string[]): {
15
18
  candidates: string[];
@@ -17,6 +17,7 @@ exports.isUnsafeStoryPath = isUnsafeStoryPath;
17
17
  * @req REQ-PATH-RESOLUTION - Resolve relative paths correctly and enforce configuration
18
18
  * @req REQ-FILE-EXISTENCE - Validate that story file paths reference existing files
19
19
  * @req REQ-SECURITY-VALIDATION - Prevent path traversal and absolute path usage
20
+ * @req REQ-ERROR-HANDLING - Handle filesystem errors gracefully without throwing
20
21
  */
21
22
  const fs_1 = __importDefault(require("fs"));
22
23
  const path_1 = __importDefault(require("path"));
@@ -40,15 +41,23 @@ function buildStoryCandidates(storyPath, cwd, storyDirs) {
40
41
  }
41
42
  /**
42
43
  * Check if any of the provided file paths exist.
44
+ * Handles filesystem errors (e.g., EACCES) gracefully by treating them as non-existent
45
+ * and never throwing.
43
46
  * @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
44
47
  * @req REQ-FILE-EXISTENCE - Validate that story file paths reference existing files
48
+ * @req REQ-ERROR-HANDLING - Handle filesystem errors gracefully without throwing
45
49
  */
46
50
  const fileExistCache = new Map();
47
51
  function storyExists(paths) {
48
52
  for (const candidate of paths) {
49
53
  let ok = fileExistCache.get(candidate);
50
54
  if (ok === undefined) {
51
- ok = fs_1.default.existsSync(candidate) && fs_1.default.statSync(candidate).isFile();
55
+ try {
56
+ ok = fs_1.default.existsSync(candidate) && fs_1.default.statSync(candidate).isFile();
57
+ }
58
+ catch {
59
+ ok = false;
60
+ }
52
61
  fileExistCache.set(candidate, ok);
53
62
  }
54
63
  if (ok) {
@@ -59,9 +68,12 @@ function storyExists(paths) {
59
68
  }
60
69
  /**
61
70
  * Normalize a story path to candidate absolute paths and check existence.
71
+ * Filesystem errors are handled via `storyExists`, which suppresses exceptions
72
+ * and treats such cases as non-existent.
62
73
  * @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
63
74
  * @req REQ-PATH-RESOLUTION - Resolve relative paths correctly and enforce configuration
64
75
  * @req REQ-FILE-EXISTENCE - Validate that story file paths reference existing files
76
+ * @req REQ-ERROR-HANDLING - Handle filesystem errors gracefully without throwing
65
77
  */
66
78
  function normalizeStoryPath(storyPath, cwd, storyDirs) {
67
79
  const candidates = buildStoryCandidates(storyPath, cwd, storyDirs);
@@ -1,13 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  /**
4
- * Branch tests for: docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
4
+ * Edge-case tests for: docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
5
5
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
6
6
  * @req REQ-AUTOFIX - Cover additional branch cases in require-story-core (addStoryFixer/reportMissing)
7
7
  */
8
8
  const require_story_core_1 = require("../../src/rules/helpers/require-story-core");
9
9
  const require_story_helpers_1 = require("../../src/rules/helpers/require-story-helpers");
10
- describe("Require Story Core (Story 003.0)", () => {
10
+ describe("Require Story Core - edge cases (Story 003.0)", () => {
11
11
  test("createAddStoryFix falls back to 0 when target is falsy", () => {
12
12
  const fixer = {
13
13
  insertTextBeforeRange: jest.fn((r, t) => ({ r, t })),
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Tests for: docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
3
3
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
4
- * @req REQ-COVERAGE-IO - Additional tests to exercise uncovered branches in require-story-io.ts
4
+ * @req REQ-HELPERS-EDGE-CASES - Edge-case behavior tests for helpers in require-story-helpers.ts
5
5
  */
6
6
  export {};
@@ -2,11 +2,11 @@
2
2
  /**
3
3
  * Tests for: docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
4
4
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
5
- * @req REQ-COVERAGE-HELPERS - Additional tests to exercise edge branches in require-story-helpers.ts
5
+ * @req REQ-HELPERS-EDGE-CASES - Edge-case behavior tests for helpers in require-story-helpers.ts
6
6
  */
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  const require_story_helpers_1 = require("../../src/rules/helpers/require-story-helpers");
9
- describe("Require Story Helpers - additional branch coverage (Story 003.0)", () => {
9
+ describe("Require Story Helpers - edge cases (Story 003.0)", () => {
10
10
  test("jsdocHasStory returns false when JSDoc exists but value is not a string", () => {
11
11
  const fakeSource = { getJSDocComment: () => ({ value: 123 }) };
12
12
  const res = (0, require_story_helpers_1.jsdocHasStory)(fakeSource, {});
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Tests for: docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
3
3
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
4
- * @req REQ-COVERAGE-HELPERS - Additional tests to exercise edge branches in require-story-helpers.ts
4
+ * @req REQ-IO-BEHAVIOR-EDGE-CASES - Edge-case behavior tests for IO helpers in require-story-io.ts
5
5
  */
6
6
  export {};
@@ -2,11 +2,11 @@
2
2
  /**
3
3
  * Tests for: docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
4
4
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
5
- * @req REQ-COVERAGE-IO - Additional tests to exercise uncovered branches in require-story-io.ts
5
+ * @req REQ-IO-BEHAVIOR-EDGE-CASES - Edge-case behavior tests for IO helpers in require-story-io.ts
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
- describe("Require Story IO helpers - branch coverage (Story 003.0)", () => {
9
+ describe("Require Story IO helpers - additional behavior (Story 003.0)", () => {
10
10
  test("parentChainHasStory returns false when sourceCode.getCommentsBefore is not a function", () => {
11
11
  const fakeSource = {}; // no getCommentsBefore function
12
12
  const node = { parent: { parent: null } };
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Tests for: docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
3
3
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
4
- * @req REQ-COVERAGE-VISITORS - Tests to cover visitors branches in require-story-visitors.ts
4
+ * @req REQ-VISITORS-BEHAVIOR - Behavior tests for visitors in require-story-visitors.ts
5
5
  */
6
6
  export {};
@@ -2,11 +2,11 @@
2
2
  /**
3
3
  * Tests for: docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
4
4
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
5
- * @req REQ-COVERAGE-VISITORS - Tests to cover visitors branches in require-story-visitors.ts
5
+ * @req REQ-VISITORS-BEHAVIOR - Behavior tests for visitors in require-story-visitors.ts
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
- describe("Require Story Visitors - branch coverage (Story 003.0)", () => {
9
+ describe("Require Story Visitors - behavior (Story 003.0)", () => {
10
10
  test("build visitors returns handlers for FunctionDeclaration and ArrowFunctionExpression", () => {
11
11
  const fakeContext = { getFilename: () => "file.ts" };
12
12
  const fakeSource = { getText: () => "" };
@@ -10,6 +10,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  */
11
11
  const eslint_1 = require("eslint");
12
12
  const valid_story_reference_1 = __importDefault(require("../../src/rules/valid-story-reference"));
13
+ const storyReferenceUtils_1 = require("../../src/utils/storyReferenceUtils");
13
14
  const ruleTester = new eslint_1.RuleTester({
14
15
  languageOptions: { parserOptions: { ecmaVersion: 2020 } },
15
16
  });
@@ -67,3 +68,28 @@ describe("Valid Story Reference Rule (Story 006.0-DEV-FILE-VALIDATION)", () => {
67
68
  ],
68
69
  });
69
70
  });
71
+ describe("Valid Story Reference Rule Error Handling (Story 006.0-DEV-FILE-VALIDATION)", () => {
72
+ /**
73
+ * @req REQ-ERROR-HANDLING - Verify storyExists swallows fs errors and returns false
74
+ * instead of throwing when filesystem operations fail.
75
+ * @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
76
+ */
77
+ const fs = require("fs");
78
+ afterEach(() => {
79
+ jest.restoreAllMocks();
80
+ });
81
+ it("[REQ-ERROR-HANDLING] storyExists returns false when fs throws", () => {
82
+ jest.spyOn(fs, "existsSync").mockImplementation(() => {
83
+ const err = new Error("EACCES: permission denied");
84
+ err.code = "EACCES";
85
+ throw err;
86
+ });
87
+ jest.spyOn(fs, "statSync").mockImplementation(() => {
88
+ const err = new Error("EACCES: permission denied");
89
+ err.code = "EACCES";
90
+ throw err;
91
+ });
92
+ expect(() => (0, storyReferenceUtils_1.storyExists)(["docs/stories/permission-denied.story.md"])).not.toThrow();
93
+ expect((0, storyReferenceUtils_1.storyExists)(["docs/stories/permission-denied.story.md"])).toBe(false);
94
+ });
95
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-traceability",
3
- "version": "1.4.8",
3
+ "version": "1.4.9",
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",
@@ -22,6 +22,7 @@
22
22
  "lint": "eslint --config eslint.config.js \"src/**/*.{js,ts}\" \"tests/**/*.{js,ts}\" --max-warnings=0",
23
23
  "test": "jest --ci --bail",
24
24
  "ci-verify": "npm run type-check && npm run lint && npm run format:check && npm run duplication && npm run check:traceability && npm test && npm run audit:ci && npm run safety:deps",
25
+ "ci-verify:full": "npm run check:traceability && npm run safety:deps && npm run audit:ci && npm run build && npm run type-check && npm run lint-plugin-check && npm run lint -- --max-warnings=0 && npm run duplication && npm run test -- --coverage && npm run format:check && npm audit --production --audit-level=high && npm run audit:dev-high",
25
26
  "ci-verify:fast": "npm run type-check && npm run check:traceability && npm run duplication && jest --ci --bail --passWithNoTests --testPathPatterns 'tests/(unit|fast)'",
26
27
  "format": "prettier --write .",
27
28
  "format:check": "prettier --check \"src/**/*.ts\" \"tests/**/*.ts\"",