eslint-plugin-traceability 1.0.3 → 1.0.5

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 (94) hide show
  1. package/.github/workflows/ci-cd.yml +41 -35
  2. package/.husky/pre-commit +1 -1
  3. package/.husky/pre-push +1 -1
  4. package/.prettierignore +5 -1
  5. package/.releaserc.json +15 -0
  6. package/.voder/history.md +138 -264
  7. package/.voder/implementation-progress.md +110 -112
  8. package/.voder/last-action.md +62 -204
  9. package/.voder/plan.md +10 -10
  10. package/.voder/progress-chart.png +0 -0
  11. package/.voder/progress-log-areas.csv +4 -0
  12. package/.voder/progress-log.csv +4 -0
  13. package/CHANGELOG.md +20 -1
  14. package/CONTRIBUTING.md +15 -13
  15. package/README.md +2 -2
  16. package/cli-integration.js +61 -115
  17. package/docs/cli-integration.md +3 -1
  18. package/docs/conventional-commits-guide.md +185 -0
  19. package/docs/decisions/004-automated-version-bumping-for-ci-cd.md +3 -1
  20. package/docs/decisions/005-github-actions-validation-tooling.accepted.md +144 -0
  21. package/docs/decisions/006-semantic-release-for-automated-publishing.accepted.md +227 -0
  22. package/eslint.config.js +4 -4
  23. package/package.json +7 -2
  24. package/tests/{basic.test.ts → plugin-setup.test.ts} +1 -1
  25. package/user-docs/migration-guide.md +71 -0
  26. package/lib/index.d.ts +0 -26
  27. package/lib/index.js +0 -11
  28. package/lib/src/index.d.ts +0 -80
  29. package/lib/src/index.js +0 -58
  30. package/lib/src/maintenance/batch.d.ts +0 -16
  31. package/lib/src/maintenance/batch.js +0 -28
  32. package/lib/src/maintenance/detect.d.ts +0 -6
  33. package/lib/src/maintenance/detect.js +0 -69
  34. package/lib/src/maintenance/index.d.ts +0 -14
  35. package/lib/src/maintenance/index.js +0 -22
  36. package/lib/src/maintenance/report.d.ts +0 -7
  37. package/lib/src/maintenance/report.js +0 -17
  38. package/lib/src/maintenance/update.d.ts +0 -6
  39. package/lib/src/maintenance/update.js +0 -67
  40. package/lib/src/maintenance/utils.d.ts +0 -6
  41. package/lib/src/maintenance/utils.js +0 -64
  42. package/lib/src/rules/require-branch-annotation.d.ts +0 -7
  43. package/lib/src/rules/require-branch-annotation.js +0 -111
  44. package/lib/src/rules/require-req-annotation.d.ts +0 -7
  45. package/lib/src/rules/require-req-annotation.js +0 -38
  46. package/lib/src/rules/require-story-annotation.d.ts +0 -7
  47. package/lib/src/rules/require-story-annotation.js +0 -50
  48. package/lib/src/rules/valid-annotation-format.d.ts +0 -10
  49. package/lib/src/rules/valid-annotation-format.js +0 -60
  50. package/lib/src/rules/valid-req-reference.d.ts +0 -3
  51. package/lib/src/rules/valid-req-reference.js +0 -104
  52. package/lib/src/rules/valid-story-reference.d.ts +0 -3
  53. package/lib/src/rules/valid-story-reference.js +0 -168
  54. package/lib/tests/basic.test.d.ts +0 -1
  55. package/lib/tests/basic.test.js +0 -51
  56. package/lib/tests/fixtures/stale/example.d.ts +0 -0
  57. package/lib/tests/fixtures/stale/example.js +0 -3
  58. package/lib/tests/fixtures/update/example.d.ts +0 -0
  59. package/lib/tests/fixtures/update/example.js +0 -3
  60. package/lib/tests/fixtures/valid-annotations/example.d.ts +0 -0
  61. package/lib/tests/fixtures/valid-annotations/example.js +0 -3
  62. package/lib/tests/index.test.d.ts +0 -1
  63. package/lib/tests/index.test.js +0 -72
  64. package/lib/tests/integration/file-validation.test.d.ts +0 -1
  65. package/lib/tests/integration/file-validation.test.js +0 -71
  66. package/lib/tests/integration/plugin-validation.test.d.ts +0 -1
  67. package/lib/tests/integration/plugin-validation.test.js +0 -83
  68. package/lib/tests/maintenance/batch.test.d.ts +0 -1
  69. package/lib/tests/maintenance/batch.test.js +0 -79
  70. package/lib/tests/maintenance/detect-isolated.test.d.ts +0 -1
  71. package/lib/tests/maintenance/detect-isolated.test.js +0 -95
  72. package/lib/tests/maintenance/detect.test.d.ts +0 -1
  73. package/lib/tests/maintenance/detect.test.js +0 -23
  74. package/lib/tests/maintenance/report.test.d.ts +0 -1
  75. package/lib/tests/maintenance/report.test.js +0 -67
  76. package/lib/tests/maintenance/update-isolated.test.d.ts +0 -1
  77. package/lib/tests/maintenance/update-isolated.test.js +0 -66
  78. package/lib/tests/maintenance/update.test.d.ts +0 -1
  79. package/lib/tests/maintenance/update.test.js +0 -26
  80. package/lib/tests/rules/require-branch-annotation.test.d.ts +0 -1
  81. package/lib/tests/rules/require-branch-annotation.test.js +0 -253
  82. package/lib/tests/rules/require-req-annotation.test.d.ts +0 -1
  83. package/lib/tests/rules/require-req-annotation.test.js +0 -41
  84. package/lib/tests/rules/require-story-annotation.test.d.ts +0 -1
  85. package/lib/tests/rules/require-story-annotation.test.js +0 -36
  86. package/lib/tests/rules/valid-annotation-format.test.d.ts +0 -1
  87. package/lib/tests/rules/valid-annotation-format.test.js +0 -58
  88. package/lib/tests/rules/valid-req-reference.test.d.ts +0 -1
  89. package/lib/tests/rules/valid-req-reference.test.js +0 -87
  90. package/lib/tests/rules/valid-story-reference.test.d.ts +0 -1
  91. package/lib/tests/rules/valid-story-reference.test.js +0 -69
  92. package/tests/integration/file-validation.test.ts +0 -78
  93. package/tests/integration/plugin-validation.test.ts +0 -84
  94. /package/tests/{index.test.ts → plugin-default-export-and-configs.test.ts} +0 -0
@@ -1,168 +0,0 @@
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
- /* eslint-env node */
7
- /**
8
- * Rule to validate @story annotation references refer to existing story files
9
- * @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
10
- * @req REQ-FILE-EXISTENCE - Validate that story file paths reference existing files
11
- * @req REQ-PATH-RESOLUTION - Resolve relative paths correctly and enforce configuration
12
- * @req REQ-SECURITY-VALIDATION - Prevent path traversal and absolute path usage
13
- */
14
- const fs_1 = __importDefault(require("fs"));
15
- const path_1 = __importDefault(require("path"));
16
- const defaultStoryDirs = ["docs/stories", "stories"];
17
- const fileExistCache = new Map();
18
- /**
19
- * Build possible file paths for a given storyPath.
20
- * @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
21
- * @req REQ-PATH-RESOLUTION - Resolve relative paths correctly and enforce configuration
22
- */
23
- function buildCandidates(storyPath, cwd, storyDirs) {
24
- const candidates = [];
25
- if (storyPath.startsWith("./") || storyPath.startsWith("../")) {
26
- candidates.push(path_1.default.resolve(cwd, storyPath));
27
- }
28
- else {
29
- candidates.push(path_1.default.resolve(cwd, storyPath));
30
- for (const dir of storyDirs) {
31
- candidates.push(path_1.default.resolve(cwd, dir, path_1.default.basename(storyPath)));
32
- }
33
- }
34
- return candidates;
35
- }
36
- /**
37
- * Check if any of the candidate files exist.
38
- * @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
39
- * @req REQ-FILE-EXISTENCE - Validate that story file paths reference existing files
40
- */
41
- function existsAny(paths) {
42
- for (const candidate of paths) {
43
- let ok = fileExistCache.get(candidate);
44
- if (ok === undefined) {
45
- ok = fs_1.default.existsSync(candidate) && fs_1.default.statSync(candidate).isFile();
46
- fileExistCache.set(candidate, ok);
47
- }
48
- if (ok) {
49
- return true;
50
- }
51
- }
52
- return false;
53
- }
54
- /**
55
- * Validate a single @story annotation line.
56
- * @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
57
- * @req REQ-FILE-EXISTENCE - Validate that story file paths reference existing files
58
- * @req REQ-PATH-RESOLUTION - Resolve relative paths correctly and enforce configuration
59
- * @req REQ-SECURITY-VALIDATION - Prevent path traversal and absolute path usage
60
- */
61
- function validateStoryPath(line, commentNode, context, cwd, storyDirs, allowAbsolute, requireExt) {
62
- const parts = line.split(/\s+/);
63
- const storyPath = parts[1];
64
- if (!storyPath) {
65
- return;
66
- }
67
- // Absolute path check
68
- if (path_1.default.isAbsolute(storyPath)) {
69
- if (!allowAbsolute) {
70
- context.report({
71
- node: commentNode,
72
- messageId: "invalidPath",
73
- data: { path: storyPath },
74
- });
75
- }
76
- return;
77
- }
78
- // Path traversal prevention
79
- if (storyPath.includes("..")) {
80
- const normalized = path_1.default.normalize(storyPath);
81
- const full = path_1.default.resolve(cwd, normalized);
82
- if (!full.startsWith(cwd + path_1.default.sep)) {
83
- context.report({
84
- node: commentNode,
85
- messageId: "invalidPath",
86
- data: { path: storyPath },
87
- });
88
- return;
89
- }
90
- }
91
- // Extension check
92
- if (requireExt && !storyPath.endsWith(".story.md")) {
93
- context.report({
94
- node: commentNode,
95
- messageId: "invalidExtension",
96
- data: { path: storyPath },
97
- });
98
- return;
99
- }
100
- // Build candidate paths and check existence
101
- const candidates = buildCandidates(storyPath, cwd, storyDirs);
102
- if (!existsAny(candidates)) {
103
- context.report({
104
- node: commentNode,
105
- messageId: "fileMissing",
106
- data: { path: storyPath },
107
- });
108
- }
109
- }
110
- /**
111
- * Handle a single comment node by processing its lines.
112
- * @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
113
- * @req REQ-ANNOTATION-VALIDATION - Ensure each annotation line is parsed
114
- */
115
- function handleComment(commentNode, context, sourceCode, cwd, storyDirs, allowAbsolute, requireExt) {
116
- const lines = commentNode.value
117
- .split(/\r?\n/)
118
- .map((l) => l.replace(/^[^@]*/, "").trim());
119
- for (const line of lines) {
120
- if (line.startsWith("@story")) {
121
- validateStoryPath(line, commentNode, context, cwd, storyDirs, allowAbsolute, requireExt);
122
- }
123
- }
124
- }
125
- exports.default = {
126
- meta: {
127
- type: "problem",
128
- docs: {
129
- description: "Validate that @story annotations reference existing .story.md files",
130
- recommended: "error",
131
- },
132
- messages: {
133
- fileMissing: "Story file '{{path}}' not found",
134
- invalidExtension: "Invalid story file extension for '{{path}}', expected '.story.md'",
135
- invalidPath: "Invalid story path '{{path}}'",
136
- },
137
- schema: [
138
- {
139
- type: "object",
140
- properties: {
141
- storyDirectories: {
142
- type: "array",
143
- items: { type: "string" },
144
- },
145
- allowAbsolutePaths: { type: "boolean" },
146
- requireStoryExtension: { type: "boolean" },
147
- },
148
- additionalProperties: false,
149
- },
150
- ],
151
- },
152
- create(context) {
153
- const sourceCode = context.getSourceCode();
154
- const cwd = process.cwd();
155
- const opts = context.options[0];
156
- const storyDirs = opts?.storyDirectories || defaultStoryDirs;
157
- const allowAbsolute = opts?.allowAbsolutePaths || false;
158
- const requireExt = opts?.requireStoryExtension !== false;
159
- return {
160
- Program() {
161
- const comments = sourceCode.getAllComments() || [];
162
- for (const comment of comments) {
163
- handleComment(comment, context, sourceCode, cwd, storyDirs, allowAbsolute, requireExt);
164
- }
165
- },
166
- };
167
- },
168
- };
@@ -1 +0,0 @@
1
- export {};
@@ -1,51 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- /**
37
- * Tests for: docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
38
- * @story docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
39
- * @req REQ-PLUGIN-STRUCTURE - plugin exports rules and configs
40
- */
41
- const index_1 = __importStar(require("../lib/index"));
42
- describe("Traceability ESLint Plugin (Story 001.0-DEV-PLUGIN-SETUP)", () => {
43
- it("[REQ-PLUGIN-STRUCTURE] plugin exports rules and configs", () => {
44
- expect(index_1.rules).toBeDefined();
45
- expect(index_1.configs).toBeDefined();
46
- expect(typeof index_1.rules).toBe("object");
47
- expect(typeof index_1.configs).toBe("object");
48
- expect(index_1.default.rules).toBe(index_1.rules);
49
- expect(index_1.default.configs).toBe(index_1.configs);
50
- });
51
- });
File without changes
@@ -1,3 +0,0 @@
1
- "use strict";
2
- // Sample code with stale annotation
3
- // @story non-existent.md
File without changes
@@ -1,3 +0,0 @@
1
- "use strict";
2
- // Sample code with annotation to update
3
- // @story old.md
File without changes
@@ -1,3 +0,0 @@
1
- "use strict";
2
- // Valid annotation
3
- // @story docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
@@ -1 +0,0 @@
1
- export {};
@@ -1,72 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- /**
37
- * Tests for: docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
38
- * @story docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
39
- * @req REQ-PLUGIN-STRUCTURE - Validate plugin default export and configs in src/index.ts
40
- */
41
- const index_1 = __importStar(require("../src/index"));
42
- describe("Plugin Default Export and Configs (Story 001.0-DEV-PLUGIN-SETUP)", () => {
43
- it("[REQ-PLUGIN-STRUCTURE] default export includes rules and configs", () => {
44
- expect(index_1.default.rules).toBe(index_1.rules);
45
- expect(index_1.default.configs).toBe(index_1.configs);
46
- });
47
- it("[REQ-PLUGIN-STRUCTURE] rules object has correct rule names", () => {
48
- // Arrange: expected rule names in insertion order
49
- const expected = [
50
- "require-story-annotation",
51
- "require-req-annotation",
52
- "require-branch-annotation",
53
- "valid-annotation-format",
54
- "valid-story-reference",
55
- "valid-req-reference",
56
- ];
57
- // Act: get actual rule names from plugin
58
- const actual = Object.keys(index_1.rules);
59
- // Assert: actual matches expected
60
- expect(actual).toEqual(expected);
61
- });
62
- it("[REQ-RULE-REGISTRY] configs.recommended contains correct rule configuration", () => {
63
- const recommendedRules = index_1.configs.recommended[0].rules;
64
- expect(recommendedRules).toHaveProperty("traceability/require-story-annotation", "error");
65
- expect(recommendedRules).toHaveProperty("traceability/require-req-annotation", "error");
66
- expect(recommendedRules).toHaveProperty("traceability/require-branch-annotation", "error");
67
- });
68
- it("[REQ-CONFIG-SYSTEM] configs.strict contains same rules as recommended", () => {
69
- const strictRules = index_1.configs.strict[0].rules;
70
- expect(strictRules).toEqual(index_1.configs.recommended[0].rules);
71
- });
72
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,71 +0,0 @@
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
- * Integration tests for file-validation rules via ESLint CLI
8
- * @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
9
- * @req REQ-PERFORMANCE-OPTIMIZATION - Verify CLI integration of valid-story-reference and valid-req-reference rules
10
- */
11
- const child_process_1 = require("child_process");
12
- const path_1 = __importDefault(require("path"));
13
- // Ensure ESLint CLI uses built plugin
14
- const eslintBin = path_1.default.resolve(__dirname, "../../node_modules/.bin/eslint");
15
- const configPath = path_1.default.resolve(__dirname, "../../eslint.config.js");
16
- describe("File and Req Validation CLI Integration (Story 006.0-DEV-FILE-VALIDATION)", () => {
17
- function runLint(code, rules) {
18
- const ruleArgs = ["--rule", "no-unused-vars:off"];
19
- for (const r of rules) {
20
- ruleArgs.push("--rule", r);
21
- }
22
- return (0, child_process_1.spawnSync)("node", [
23
- eslintBin,
24
- "--no-config-lookup",
25
- "--config",
26
- configPath,
27
- "--stdin",
28
- "--stdin-filename",
29
- "foo.js",
30
- ...ruleArgs,
31
- ], {
32
- encoding: "utf-8",
33
- input: code,
34
- });
35
- }
36
- it("[REQ-FILE-EXISTENCE] reports missing story file via CLI", () => {
37
- // Arrange
38
- const code = "// @story docs/stories/missing-file.story.md";
39
- // Act
40
- const res = runLint(code, ["traceability/valid-story-reference:error"]);
41
- // Assert
42
- expect(res.status).toBe(1);
43
- expect(res.stdout).toContain("Story file");
44
- });
45
- it("[REQ-EXTENSION] reports invalid extension via CLI", () => {
46
- // Arrange
47
- const code = "// @story docs/stories/001.0-DEV-PLUGIN-SETUP.md";
48
- // Act
49
- const res = runLint(code, ["traceability/valid-story-reference:error"]);
50
- // Assert
51
- expect(res.status).toBe(1);
52
- expect(res.stdout).toContain("Invalid story file extension");
53
- });
54
- it("[REQ-DEEP-PARSE] reports missing requirement via CLI", () => {
55
- // Arrange
56
- const code = "// @story docs/stories/001.0-DEV-PLUGIN-SETUP.story.md\n// @req REQ-UNKNOWN";
57
- // Act
58
- const res = runLint(code, ["traceability/valid-req-reference:error"]);
59
- // Assert
60
- expect(res.status).toBe(1);
61
- expect(res.stdout).toContain("Requirement 'REQ-UNKNOWN' not found");
62
- });
63
- it("[REQ-DEEP-MATCH] valid story and requirement via CLI", () => {
64
- // Arrange
65
- const code = "// @story docs/stories/001.0-DEV-PLUGIN-SETUP.story.md\n// @req REQ-PLUGIN-STRUCTURE";
66
- // Act
67
- const res = runLint(code, ["traceability/valid-req-reference:error"]);
68
- // Assert
69
- expect(res.status).toBe(0);
70
- });
71
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,83 +0,0 @@
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
- * Integration tests for ESLint plugin via CLI
8
- * @story docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
9
- * @req REQ-PLUGIN-STRUCTURE - Validate plugin registers via CLI
10
- */
11
- /* eslint-env node, jest */
12
- const child_process_1 = require("child_process");
13
- const path_1 = __importDefault(require("path"));
14
- // Ensure ESLint CLI uses built plugin
15
- const eslintBin = path_1.default.resolve(__dirname, "../../node_modules/.bin/eslint");
16
- const configPath = path_1.default.resolve(__dirname, "../../eslint.config.js");
17
- describe("ESLint CLI Integration (Story 001.0-DEV-PLUGIN-SETUP)", () => {
18
- /**
19
- * Helper to run ESLint CLI with the traceability plugin and custom rule
20
- * @story docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
21
- * @req REQ-PLUGIN-STRUCTURE - Invoke ESLint CLI for integration testing
22
- */
23
- function runEslint(code, rule) {
24
- const args = [
25
- "--no-config-lookup",
26
- "--config",
27
- configPath,
28
- "--stdin",
29
- "--stdin-filename",
30
- "foo.js",
31
- "--rule",
32
- "no-unused-vars:off",
33
- "--rule",
34
- rule,
35
- ];
36
- return (0, child_process_1.spawnSync)("node", [eslintBin, ...args], {
37
- encoding: "utf-8",
38
- input: code,
39
- });
40
- }
41
- it("[REQ-PLUGIN-STRUCTURE] reports error when @story annotation is missing", () => {
42
- // Arrange
43
- const code = "function foo() {}";
44
- const rule = "traceability/require-story-annotation:error";
45
- // Act
46
- const result = runEslint(code, rule);
47
- // Assert
48
- expect(result.status).toBe(1);
49
- expect(result.stdout).toMatch(/require-story-annotation/);
50
- });
51
- it("[REQ-PLUGIN-STRUCTURE] does not report error when @story annotation is present", () => {
52
- // Arrange
53
- const code = `/**
54
- * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
55
- */
56
- function foo() {}`;
57
- const rule = "traceability/require-story-annotation:error";
58
- // Act
59
- const result = runEslint(code, rule);
60
- // Assert
61
- expect(result.status).toBe(0);
62
- });
63
- it("[REQ-REQ-CLI] reports error when @req annotation is missing", () => {
64
- // Arrange
65
- const code = "function foo() {}";
66
- const rule = "traceability/require-req-annotation:error";
67
- // Act
68
- const result = runEslint(code, rule);
69
- // Assert
70
- expect(result.status).toBe(1);
71
- expect(result.stdout).toMatch(/require-req-annotation/);
72
- });
73
- it("[REQ-BRANCH-CLI] reports error when branch annotations missing", () => {
74
- // Arrange
75
- const code = "if (true) {}";
76
- const rule = "traceability/require-branch-annotation:error";
77
- // Act
78
- const result = runEslint(code, rule);
79
- // Assert
80
- expect(result.status).toBe(1);
81
- expect(result.stdout).toMatch(/require-branch-annotation/);
82
- });
83
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,79 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- /**
37
- * Tests for: docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
38
- * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
39
- * @req REQ-MAINT-BATCH - Perform batch updates
40
- * @req REQ-MAINT-VERIFY - Verify annotation references
41
- */
42
- const fs = __importStar(require("fs"));
43
- const path = __importStar(require("path"));
44
- const os = __importStar(require("os"));
45
- const batch_1 = require("../../src/maintenance/batch");
46
- describe("batchUpdateAnnotations (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
47
- let tmpDir;
48
- beforeAll(() => {
49
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "batch-test-"));
50
- });
51
- afterAll(() => {
52
- fs.rmSync(tmpDir, { recursive: true, force: true });
53
- });
54
- it("[REQ-MAINT-BATCH] should return 0 when no mappings applied", () => {
55
- const count = (0, batch_1.batchUpdateAnnotations)(tmpDir, []);
56
- expect(count).toBe(0);
57
- });
58
- });
59
- describe("verifyAnnotations (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
60
- let tmpDir;
61
- beforeAll(() => {
62
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "verify-test-"));
63
- const tsContent = `
64
- /**
65
- * Tests for: my-story.story.md
66
- * @story my-story.story.md
67
- */
68
- `;
69
- fs.writeFileSync(path.join(tmpDir, "test.ts"), tsContent);
70
- fs.writeFileSync(path.join(tmpDir, "my-story.story.md"), "# Dummy Story");
71
- });
72
- afterAll(() => {
73
- fs.rmSync(tmpDir, { recursive: true, force: true });
74
- });
75
- it("[REQ-MAINT-VERIFY] should return true when annotations are valid", () => {
76
- const valid = (0, batch_1.verifyAnnotations)(tmpDir);
77
- expect(valid).toBe(true);
78
- });
79
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,95 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- /**
37
- * Tests for: docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
38
- * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
39
- * @req REQ-MAINT-DETECT - Detect stale annotation references
40
- */
41
- const fs = __importStar(require("fs"));
42
- const path = __importStar(require("path"));
43
- const os = __importStar(require("os"));
44
- const detect_1 = require("../../src/maintenance/detect");
45
- describe("detectStaleAnnotations isolated (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
46
- it("[REQ-MAINT-DETECT] returns empty array when directory does not exist", () => {
47
- const result = (0, detect_1.detectStaleAnnotations)("non-existent-dir");
48
- expect(result).toEqual([]);
49
- });
50
- it("[REQ-MAINT-DETECT] detects stale annotations in nested directories", () => {
51
- // Arrange
52
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "tmp-nested-"));
53
- const nestedDir = path.join(tmpDir, "nested");
54
- fs.mkdirSync(nestedDir);
55
- const filePath1 = path.join(tmpDir, "file1.ts");
56
- const filePath2 = path.join(nestedDir, "file2.ts");
57
- const content1 = `
58
- /**
59
- * @story stale1.story.md
60
- */
61
- `;
62
- fs.writeFileSync(filePath1, content1, "utf8");
63
- const content2 = `
64
- /**
65
- * @story stale2.story.md
66
- */
67
- `;
68
- fs.writeFileSync(filePath2, content2, "utf8");
69
- // Act
70
- const result = (0, detect_1.detectStaleAnnotations)(tmpDir);
71
- // Assert
72
- expect(result).toHaveLength(2);
73
- expect(result).toContain("stale1.story.md");
74
- expect(result).toContain("stale2.story.md");
75
- });
76
- it("[REQ-MAINT-DETECT] throws error on permission denied", () => {
77
- const tmpDir2 = fs.mkdtempSync(path.join(os.tmpdir(), "tmp-perm-"));
78
- const dir = path.join(tmpDir2, "subdir");
79
- fs.mkdirSync(dir);
80
- const filePath = path.join(dir, "file.ts");
81
- const content = `
82
- /**
83
- * @story none.story.md
84
- */
85
- `;
86
- fs.writeFileSync(filePath, content, "utf8");
87
- // Remove read permission
88
- fs.chmodSync(dir, 0o000);
89
- expect(() => (0, detect_1.detectStaleAnnotations)(tmpDir2)).toThrow();
90
- // Restore permissions
91
- fs.chmodSync(dir, 0o700);
92
- // Cleanup temporary directory
93
- fs.rmSync(tmpDir2, { recursive: true, force: true });
94
- });
95
- });
@@ -1 +0,0 @@
1
- export {};