eslint-plugin-traceability 1.11.0 → 1.11.2
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.
- package/CHANGELOG.md +3 -4
- package/README.md +1 -1
- package/lib/src/index.d.ts +12 -1
- package/lib/src/index.js +43 -6
- package/lib/src/maintenance/commands.js +2 -3
- package/lib/src/maintenance/flags.js +111 -25
- package/lib/src/maintenance/update.js +1 -14
- package/lib/src/rules/helpers/require-story-core.d.ts +67 -0
- package/lib/src/rules/helpers/require-story-core.js +142 -23
- package/lib/src/rules/helpers/require-story-helpers.d.ts +9 -88
- package/lib/src/rules/helpers/require-story-helpers.js +118 -166
- package/lib/src/rules/helpers/require-story-io.js +51 -31
- package/lib/src/rules/helpers/valid-annotation-format-internal.d.ts +12 -13
- package/lib/src/rules/helpers/valid-annotation-format-internal.js +21 -16
- package/lib/src/rules/helpers/valid-annotation-format-validators.d.ts +29 -3
- package/lib/src/rules/helpers/valid-annotation-format-validators.js +29 -3
- package/lib/src/rules/helpers/valid-annotation-options.d.ts +3 -0
- package/lib/src/rules/helpers/valid-annotation-options.js +64 -21
- package/lib/src/rules/helpers/valid-annotation-utils.d.ts +3 -3
- package/lib/src/rules/helpers/valid-annotation-utils.js +10 -10
- package/lib/src/rules/helpers/valid-req-reference-helpers.d.ts +11 -0
- package/lib/src/rules/helpers/valid-req-reference-helpers.js +362 -0
- package/lib/src/rules/prefer-implements-annotation.js +7 -7
- package/lib/src/rules/require-story-annotation.d.ts +2 -0
- package/lib/src/rules/require-story-annotation.js +1 -1
- package/lib/src/rules/valid-req-reference.d.ts +4 -0
- package/lib/src/rules/valid-req-reference.js +5 -349
- package/lib/src/rules/valid-story-reference.d.ts +1 -1
- package/lib/src/rules/valid-story-reference.js +17 -10
- package/lib/src/utils/annotation-checker.js +31 -7
- package/lib/src/utils/branch-annotation-helpers.d.ts +2 -2
- package/lib/src/utils/branch-annotation-helpers.js +4 -4
- package/lib/src/utils/reqAnnotationDetection.js +36 -22
- package/lib/tests/cli-error-handling.test.js +2 -1
- package/lib/tests/config/eslint-config-validation.test.d.ts +8 -0
- package/lib/tests/config/eslint-config-validation.test.js +81 -0
- package/lib/tests/config/flat-config-presets-integration.test.js +1 -3
- package/lib/tests/config/require-story-annotation-config.test.d.ts +9 -0
- package/lib/tests/config/require-story-annotation-config.test.js +9 -0
- package/lib/tests/fixtures/stale/example.js +1 -1
- package/lib/tests/fixtures/update/example.js +1 -1
- package/lib/tests/integration/cli-integration.test.js +9 -1
- package/lib/tests/integration/dogfooding-validation.test.d.ts +1 -0
- package/lib/tests/integration/dogfooding-validation.test.js +94 -0
- package/lib/tests/maintenance/batch.test.js +1 -0
- package/lib/tests/maintenance/cli.test.js +38 -0
- package/lib/tests/maintenance/detect-isolated.test.js +6 -5
- package/lib/tests/maintenance/detect.test.js +1 -0
- package/lib/tests/maintenance/index.test.js +1 -0
- package/lib/tests/maintenance/report.test.js +1 -0
- package/lib/tests/maintenance/update-isolated.test.js +1 -0
- package/lib/tests/maintenance/update.test.js +1 -0
- package/lib/tests/perf/maintenance-cli-large-workspace.test.js +18 -0
- package/lib/tests/perf/require-branch-annotation-large-file.test.d.ts +1 -0
- package/lib/tests/perf/require-branch-annotation-large-file.test.js +67 -0
- package/lib/tests/plugin-default-export-and-configs.test.js +2 -0
- package/lib/tests/plugin-setup-error.test.d.ts +1 -0
- package/lib/tests/plugin-setup-error.test.js +1 -0
- package/lib/tests/plugin-setup.test.js +12 -1
- package/lib/tests/rules/auto-fix-behavior-008.test.js +16 -0
- package/lib/tests/rules/error-reporting.test.js +1 -0
- package/lib/tests/rules/prefer-implements-annotation.test.js +8 -0
- package/lib/tests/rules/require-branch-annotation.test.js +34 -0
- package/lib/tests/rules/require-story-core-edgecases.test.js +1 -0
- package/lib/tests/rules/require-story-core.autofix.test.js +1 -0
- package/lib/tests/rules/require-story-core.test.js +1 -0
- package/lib/tests/rules/require-story-helpers-edgecases.test.d.ts +1 -0
- package/lib/tests/rules/require-story-helpers-edgecases.test.js +1 -0
- package/lib/tests/rules/require-story-helpers.test.js +4 -3
- package/lib/tests/rules/require-story-io-behavior.test.d.ts +1 -0
- package/lib/tests/rules/require-story-io-behavior.test.js +1 -0
- package/lib/tests/rules/require-story-io.edgecases.test.d.ts +1 -0
- package/lib/tests/rules/require-story-io.edgecases.test.js +1 -0
- package/lib/tests/rules/require-story-visitors-edgecases.test.d.ts +1 -0
- package/lib/tests/rules/require-story-visitors-edgecases.test.js +1 -0
- package/lib/tests/rules/valid-annotation-format-internal.test.d.ts +8 -0
- package/lib/tests/rules/valid-annotation-format-internal.test.js +47 -0
- package/lib/tests/rules/valid-story-reference.test.js +2 -0
- package/lib/tests/utils/annotation-checker.test.js +2 -1
- package/lib/tests/utils/branch-annotation-helpers.test.js +2 -1
- package/package.json +2 -2
- package/user-docs/api-reference.md +115 -8
- package/user-docs/examples.md +1 -1
- package/user-docs/migration-guide.md +35 -2
|
@@ -0,0 +1,362 @@
|
|
|
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
|
+
exports.createValidReqReferenceProgramVisitor = createValidReqReferenceProgramVisitor;
|
|
7
|
+
/* eslint-env node */
|
|
8
|
+
/**
|
|
9
|
+
* Helper utilities for the "valid-req-reference" rule.
|
|
10
|
+
*
|
|
11
|
+
* These helpers encapsulate the deep-validation logic for @req and
|
|
12
|
+
* @supports annotations so that the rule module can remain focused on
|
|
13
|
+
* wiring into ESLint. They are intentionally structured as a set of
|
|
14
|
+
* small, single-responsibility functions that can be reused and tested
|
|
15
|
+
* in isolation if needed.
|
|
16
|
+
*
|
|
17
|
+
* @supports docs/stories/010.0-DEV-DEEP-VALIDATION.story.md REQ-DEEP-PARSE REQ-DEEP-MATCH REQ-DEEP-CACHE
|
|
18
|
+
* @supports docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md REQ-SUPPORTS-VALIDATE REQ-MIXED-SUPPORT REQ-SCOPED-IDS
|
|
19
|
+
*/
|
|
20
|
+
const fs_1 = __importDefault(require("fs"));
|
|
21
|
+
const path_1 = __importDefault(require("path"));
|
|
22
|
+
/**
|
|
23
|
+
* Token index configuration for @supports annotations.
|
|
24
|
+
* This clarifies the expected positions of the story path and first requirement ID
|
|
25
|
+
* and avoids hard-coded "magic number" indices in parsing logic.
|
|
26
|
+
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
27
|
+
*/
|
|
28
|
+
const IMPLEMENTS_TOKENS = {
|
|
29
|
+
STORY_INDEX: 1,
|
|
30
|
+
FIRST_REQ_INDEX: 2,
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Extract the story path from a JSDoc comment.
|
|
34
|
+
* @story docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
|
|
35
|
+
* @req REQ-DEEP-PARSE - Parse JSDoc comment lines to locate @story annotations
|
|
36
|
+
*/
|
|
37
|
+
function extractStoryPath(comment) {
|
|
38
|
+
const rawLines = comment.value.split(/\r?\n/);
|
|
39
|
+
for (const rawLine of rawLines) {
|
|
40
|
+
const line = rawLine.trim().replace(/^\*+\s*/, "");
|
|
41
|
+
if (line.startsWith("@story")) {
|
|
42
|
+
const parts = line.split(/\s+/);
|
|
43
|
+
return parts[1] || null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Validate and resolve the referenced story path.
|
|
50
|
+
* Performs traversal/absolute checks and resolves to a disk path.
|
|
51
|
+
* @story docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
|
|
52
|
+
* @req REQ-DEEP-CACHE - Validate and resolve referenced story file paths
|
|
53
|
+
*/
|
|
54
|
+
function validateAndResolveStoryPath(opts) {
|
|
55
|
+
const { comment, context, storyPath, cwd } = opts;
|
|
56
|
+
if (storyPath.includes("..") || path_1.default.isAbsolute(storyPath)) {
|
|
57
|
+
context.report({
|
|
58
|
+
node: comment,
|
|
59
|
+
messageId: "invalidPath",
|
|
60
|
+
data: { storyPath },
|
|
61
|
+
});
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const resolvedStoryPath = path_1.default.resolve(cwd, storyPath);
|
|
65
|
+
if (!resolvedStoryPath.startsWith(cwd + path_1.default.sep) &&
|
|
66
|
+
resolvedStoryPath !== cwd) {
|
|
67
|
+
context.report({
|
|
68
|
+
node: comment,
|
|
69
|
+
messageId: "invalidPath",
|
|
70
|
+
data: { storyPath },
|
|
71
|
+
});
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
return resolvedStoryPath;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Load and cache requirement IDs from a story file.
|
|
78
|
+
* Reads the story file, extracts requirement IDs, and updates the cache.
|
|
79
|
+
* @story docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
|
|
80
|
+
* @req REQ-DEEP-CACHE - Cache requirement IDs discovered in story files
|
|
81
|
+
* @req REQ-DEEP-PARSE - Parse story file contents to extract requirement identifiers
|
|
82
|
+
*/
|
|
83
|
+
function loadAndCacheRequirements(opts) {
|
|
84
|
+
const { resolvedStoryPath, reqCache } = opts;
|
|
85
|
+
if (!reqCache.has(resolvedStoryPath)) {
|
|
86
|
+
try {
|
|
87
|
+
const content = fs_1.default.readFileSync(resolvedStoryPath, "utf8");
|
|
88
|
+
const found = new Set();
|
|
89
|
+
const regex = /REQ-[A-Z0-9-]+/g;
|
|
90
|
+
let match;
|
|
91
|
+
while ((match = regex.exec(content)) !== null) {
|
|
92
|
+
found.add(match[0]);
|
|
93
|
+
}
|
|
94
|
+
reqCache.set(resolvedStoryPath, found);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
reqCache.set(resolvedStoryPath, new Set());
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return reqCache.get(resolvedStoryPath);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Perform the final requirement existence check and report if missing.
|
|
104
|
+
* @story docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
|
|
105
|
+
* @req REQ-DEEP-MATCH - Verify that a referenced requirement ID exists in the story
|
|
106
|
+
*/
|
|
107
|
+
function checkRequirementExists(opts) {
|
|
108
|
+
const { comment, context, reqId, storyPath, reqSet } = opts;
|
|
109
|
+
if (!reqSet.has(reqId)) {
|
|
110
|
+
context.report({
|
|
111
|
+
node: comment,
|
|
112
|
+
messageId: "reqMissing",
|
|
113
|
+
data: { reqId, storyPath },
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Extract requirement ID from a @req line.
|
|
119
|
+
* @story docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
|
|
120
|
+
* @req REQ-DEEP-PARSE - Parse annotation lines to extract requirement IDs
|
|
121
|
+
*/
|
|
122
|
+
function extractReqIdFromLine(line) {
|
|
123
|
+
const parts = line.split(/\s+/);
|
|
124
|
+
return parts[1];
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Resolve story path and load requirements set for validation.
|
|
128
|
+
* @story docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
|
|
129
|
+
* @req REQ-DEEP-CACHE - Validate and resolve referenced story file paths
|
|
130
|
+
* @req REQ-DEEP-CACHE - Cache requirement IDs discovered in story files
|
|
131
|
+
*/
|
|
132
|
+
function resolveStoryAndRequirements(opts) {
|
|
133
|
+
const { comment, context, storyPath, cwd, reqCache } = opts;
|
|
134
|
+
const resolvedStoryPath = validateAndResolveStoryPath({
|
|
135
|
+
comment,
|
|
136
|
+
context,
|
|
137
|
+
storyPath,
|
|
138
|
+
cwd,
|
|
139
|
+
});
|
|
140
|
+
if (!resolvedStoryPath) {
|
|
141
|
+
return { resolvedStoryPath: null, reqSet: null };
|
|
142
|
+
}
|
|
143
|
+
const reqSet = loadAndCacheRequirements({
|
|
144
|
+
resolvedStoryPath,
|
|
145
|
+
reqCache,
|
|
146
|
+
});
|
|
147
|
+
return { resolvedStoryPath, reqSet };
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Validate a @req annotation line against the extracted story content.
|
|
151
|
+
* Performs path validation, file reading, caching, and requirement existence checks.
|
|
152
|
+
* @story docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
|
|
153
|
+
* @req REQ-DEEP-CACHE - Validate and resolve referenced story file paths
|
|
154
|
+
* @req REQ-DEEP-CACHE - Cache requirement IDs discovered in story files
|
|
155
|
+
* @req REQ-DEEP-MATCH - Verify that a referenced requirement ID exists in the story
|
|
156
|
+
* @req REQ-DEEP-PARSE - Parse story file contents to extract requirement identifiers
|
|
157
|
+
*/
|
|
158
|
+
function validateReqLine(opts) {
|
|
159
|
+
const { comment, context, line, storyPath, cwd, reqCache } = opts;
|
|
160
|
+
const reqId = extractReqIdFromLine(line);
|
|
161
|
+
if (!reqId || !storyPath) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const { reqSet } = resolveStoryAndRequirements({
|
|
165
|
+
comment,
|
|
166
|
+
context,
|
|
167
|
+
storyPath,
|
|
168
|
+
cwd,
|
|
169
|
+
reqCache,
|
|
170
|
+
});
|
|
171
|
+
if (!reqSet) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
checkRequirementExists({
|
|
175
|
+
comment,
|
|
176
|
+
context,
|
|
177
|
+
reqId,
|
|
178
|
+
storyPath,
|
|
179
|
+
reqSet,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Parse a @supports annotation line into its story path and requirement IDs.
|
|
184
|
+
* Expects the format: "@supports <storyPath> <REQ-ID-1> <REQ-ID-2> ..."
|
|
185
|
+
* Invalid formats (missing storyPath or reqIds) are ignored by this deep rule.
|
|
186
|
+
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
187
|
+
* @req REQ-SUPPORTS-VALIDATE - Support validation of @supports annotations
|
|
188
|
+
* @req REQ-MIXED-SUPPORT - Allow mixed @story/@req/@implements usage in the same comment
|
|
189
|
+
* @req REQ-SCOPED-IDS - Treat requirement IDs as scoped to the referenced story file
|
|
190
|
+
*/
|
|
191
|
+
function parseImplementsLine(line) {
|
|
192
|
+
const parts = line.split(/\s+/);
|
|
193
|
+
const storyPath = parts[IMPLEMENTS_TOKENS.STORY_INDEX];
|
|
194
|
+
const reqIds = parts.slice(IMPLEMENTS_TOKENS.FIRST_REQ_INDEX);
|
|
195
|
+
if (!storyPath || reqIds.length === 0) {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
return { storyPath, reqIds };
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Validate an @supports annotation line against the referenced story content.
|
|
202
|
+
* Performs path validation, file reading, caching, and requirement existence checks
|
|
203
|
+
* for each requirement ID listed on the line.
|
|
204
|
+
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
205
|
+
* @req REQ-SUPPORTS-VALIDATE - Validate that all @supports requirement IDs exist
|
|
206
|
+
* @req REQ-MIXED-SUPPORT - Ensure @supports can coexist with @story/@req annotations
|
|
207
|
+
* @req REQ-SCOPED-IDS - Validate requirement IDs in the scope of their explicit story
|
|
208
|
+
*/
|
|
209
|
+
function validateImplementsLine(opts) {
|
|
210
|
+
const { comment, context, line, cwd, reqCache } = opts;
|
|
211
|
+
const parsed = parseImplementsLine(line);
|
|
212
|
+
if (!parsed) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const { storyPath, reqIds } = parsed;
|
|
216
|
+
const { reqSet } = resolveStoryAndRequirements({
|
|
217
|
+
comment,
|
|
218
|
+
context,
|
|
219
|
+
storyPath,
|
|
220
|
+
cwd,
|
|
221
|
+
reqCache,
|
|
222
|
+
});
|
|
223
|
+
if (!reqSet) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
for (const reqId of reqIds) {
|
|
227
|
+
checkRequirementExists({
|
|
228
|
+
comment,
|
|
229
|
+
context,
|
|
230
|
+
reqId,
|
|
231
|
+
storyPath,
|
|
232
|
+
reqSet,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Handle a single annotation line for story or requirement metadata.
|
|
238
|
+
* @story docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
|
|
239
|
+
* @req REQ-DEEP-PARSE - Parse annotation lines for @story, @req, and @supports tags
|
|
240
|
+
* @req REQ-DEEP-MATCH - Dispatch @req lines for validation against story requirements
|
|
241
|
+
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
242
|
+
* @req REQ-SUPPORTS-VALIDATE - Dispatch @supports lines for validation
|
|
243
|
+
* @req REQ-MIXED-SUPPORT - Support mixed annotation types without interfering with each other
|
|
244
|
+
*/
|
|
245
|
+
function handleAnnotationLine(opts) {
|
|
246
|
+
const { line, comment, context, cwd, reqCache, storyPath } = opts;
|
|
247
|
+
if (line.startsWith("@story")) {
|
|
248
|
+
const newPath = extractStoryPath(comment);
|
|
249
|
+
return newPath || storyPath;
|
|
250
|
+
}
|
|
251
|
+
else if (line.startsWith("@req")) {
|
|
252
|
+
validateReqLine({ comment, context, line, storyPath, cwd, reqCache });
|
|
253
|
+
return storyPath;
|
|
254
|
+
}
|
|
255
|
+
else if (line.startsWith("@supports")) {
|
|
256
|
+
validateImplementsLine({ comment, context, line, cwd, reqCache });
|
|
257
|
+
return storyPath;
|
|
258
|
+
}
|
|
259
|
+
return storyPath;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Iterate over all raw lines in a comment and update storyPath as needed.
|
|
263
|
+
* @story docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
|
|
264
|
+
* @req REQ-DEEP-PARSE - Iterate comment lines to process @story/@req annotations
|
|
265
|
+
* @req REQ-DEEP-MATCH - Coordinate annotation handling across a comment block
|
|
266
|
+
*/
|
|
267
|
+
function processCommentLines(opts) {
|
|
268
|
+
const { comment, context, cwd, reqCache, initialStoryPath } = opts;
|
|
269
|
+
let storyPath = initialStoryPath;
|
|
270
|
+
const rawLines = comment.value.split(/\r?\n/);
|
|
271
|
+
for (const rawLine of rawLines) {
|
|
272
|
+
const line = rawLine.trim().replace(/^\*+\s*/, "");
|
|
273
|
+
storyPath = handleAnnotationLine({
|
|
274
|
+
line,
|
|
275
|
+
comment,
|
|
276
|
+
context,
|
|
277
|
+
cwd,
|
|
278
|
+
reqCache,
|
|
279
|
+
storyPath,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
return storyPath;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Handle JSDoc story and req annotations for a single comment block.
|
|
286
|
+
* @story docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
|
|
287
|
+
* @req REQ-DEEP-PARSE - Iterate comment lines to process @story/@req annotations
|
|
288
|
+
* @req REQ-DEEP-MATCH - Coordinate annotation handling across a comment block
|
|
289
|
+
* @req REQ-DEEP-CACHE - Maintain and reuse discovered story path across comments
|
|
290
|
+
*/
|
|
291
|
+
function handleComment(opts) {
|
|
292
|
+
const { comment, context, cwd, reqCache, rawStoryPath } = opts;
|
|
293
|
+
return processCommentLines({
|
|
294
|
+
comment,
|
|
295
|
+
context,
|
|
296
|
+
cwd,
|
|
297
|
+
reqCache,
|
|
298
|
+
initialStoryPath: rawStoryPath,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Get all comments from source and drive comment-level handling.
|
|
303
|
+
* @story docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
|
|
304
|
+
* @req REQ-DEEP-PARSE - Collect all comments from the source code
|
|
305
|
+
* @req REQ-DEEP-MATCH - Drive comment-level handling for traceability checks
|
|
306
|
+
* @req REQ-DEEP-CACHE - Reuse story path and requirement cache across comments
|
|
307
|
+
*/
|
|
308
|
+
function processAllComments(opts) {
|
|
309
|
+
const { sourceCode, context, cwd, reqCache } = opts;
|
|
310
|
+
let rawStoryPath = opts.initialStoryPath;
|
|
311
|
+
const comments = sourceCode.getAllComments() || [];
|
|
312
|
+
comments.forEach((comment) => {
|
|
313
|
+
rawStoryPath = handleComment({
|
|
314
|
+
comment,
|
|
315
|
+
context,
|
|
316
|
+
cwd,
|
|
317
|
+
reqCache,
|
|
318
|
+
rawStoryPath,
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Create a Program listener that iterates comments and validates annotations.
|
|
324
|
+
* @story docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
|
|
325
|
+
* @req REQ-DEEP-CACHE - Initialize and share a requirement cache for the program
|
|
326
|
+
* @req REQ-DEEP-CACHE - Derive the working directory context for path resolution
|
|
327
|
+
*/
|
|
328
|
+
function programListener(context) {
|
|
329
|
+
const sourceCode = context.getSourceCode();
|
|
330
|
+
const cwd = process.cwd();
|
|
331
|
+
const reqCache = new Map();
|
|
332
|
+
let rawStoryPath = null;
|
|
333
|
+
/**
|
|
334
|
+
* Program visitor that walks all comments to validate story/requirement references.
|
|
335
|
+
* @story docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
|
|
336
|
+
* @req REQ-DEEP-PARSE - Collect all comments from the source code
|
|
337
|
+
* @req REQ-DEEP-MATCH - Drive comment-level handling for traceability checks
|
|
338
|
+
* @req REQ-DEEP-CACHE - Reuse story path and requirement cache across comments
|
|
339
|
+
* @req REQ-DEEP-CACHE - Ensure validation respects project-relative paths
|
|
340
|
+
*/
|
|
341
|
+
return function Program() {
|
|
342
|
+
processAllComments({
|
|
343
|
+
sourceCode,
|
|
344
|
+
context,
|
|
345
|
+
cwd,
|
|
346
|
+
reqCache,
|
|
347
|
+
initialStoryPath: rawStoryPath,
|
|
348
|
+
});
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Factory used by the valid-req-reference rule to construct its Program
|
|
353
|
+
* visitor. Keeping this in a helper module allows the rule entrypoint
|
|
354
|
+
* itself to remain small and focused on meta configuration while the
|
|
355
|
+
* heavier deep-validation logic is encapsulated here.
|
|
356
|
+
*
|
|
357
|
+
* @supports docs/stories/010.0-DEV-DEEP-VALIDATION.story.md REQ-DEEP-PARSE REQ-DEEP-MATCH REQ-DEEP-CACHE
|
|
358
|
+
* @supports docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md REQ-SUPPORTS-VALIDATE REQ-MIXED-SUPPORT REQ-SCOPED-IDS
|
|
359
|
+
*/
|
|
360
|
+
function createValidReqReferenceProgramVisitor(context) {
|
|
361
|
+
return programListener(context);
|
|
362
|
+
}
|
|
@@ -100,15 +100,15 @@ function applyImplementsReplacement(context, comment, details) {
|
|
|
100
100
|
* preserving the original comment formatting.
|
|
101
101
|
*
|
|
102
102
|
* The fixer is intentionally conservative and only activates when:
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
103
|
+
* This rule requires that there is exactly one distinct `@story` path.
|
|
104
|
+
* It also requires that exactly one `@story` line is present.
|
|
105
|
+
* It requires that at least one `@req` line is present.
|
|
106
|
+
* It also requires that each `@req` line has the simple form `@req <REQ-ID>` (no extra tokens).
|
|
107
107
|
*
|
|
108
108
|
* When applicable, the fix:
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
109
|
+
* It removes the original `@story` and `@req` lines.
|
|
110
|
+
* It then inserts a single `@supports` line in their place, preserving the
|
|
111
|
+
* original leading comment prefix (indentation and `*` markers).
|
|
112
112
|
*
|
|
113
113
|
* More complex patterns remain diagnostics-only with no fix to avoid
|
|
114
114
|
* producing invalid or ambiguous output.
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* This file implements the ESLint rule that requires @story annotations
|
|
5
5
|
* on functions and methods according to configured scope and export priority.
|
|
6
|
+
* Example: see docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md for function annotations,
|
|
7
|
+
* and docs/stories/008.0-DEV-AUTO-FIX.story.md for auto-fix behavior.
|
|
6
8
|
*
|
|
7
9
|
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
8
10
|
* @story docs/stories/008.0-DEV-AUTO-FIX.story.md
|
|
@@ -33,7 +33,7 @@ const rule = {
|
|
|
33
33
|
*/
|
|
34
34
|
fixable: "code",
|
|
35
35
|
messages: {
|
|
36
|
-
missingStory: "Function '{{name}}' must have an explicit @story annotation. Add a JSDoc or line comment with @story that points to the implementing story file
|
|
36
|
+
missingStory: "Function '{{name}}' must have an explicit @story annotation. Add a JSDoc or line comment with @story that points to the implementing story file, such as docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md.",
|
|
37
37
|
},
|
|
38
38
|
schema: [
|
|
39
39
|
{
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule to validate @req annotation references refer to existing requirements in story files.
|
|
3
|
+
* Uses shared helpers from the valid-req-reference-helpers module.
|
|
4
|
+
*/
|
|
1
5
|
import type { Rule } from "eslint";
|
|
2
6
|
declare const _default: Rule.RuleModule;
|
|
3
7
|
export default _default;
|