eslint-plugin-traceability 1.11.2 → 1.11.4
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 +2 -2
- package/README.md +2 -2
- package/lib/src/index.d.ts +1 -7
- package/lib/src/index.js +28 -0
- package/lib/src/utils/branch-annotation-helpers.d.ts +5 -1
- package/lib/src/utils/branch-annotation-helpers.js +194 -18
- package/lib/tests/integration/catch-annotation-prettier.integration.test.d.ts +1 -0
- package/lib/tests/integration/catch-annotation-prettier.integration.test.js +131 -0
- package/lib/tests/integration/else-if-annotation-prettier.integration.test.d.ts +1 -0
- package/lib/tests/integration/else-if-annotation-prettier.integration.test.js +116 -0
- package/lib/tests/perf/valid-annotation-format-large-file.test.d.ts +1 -0
- package/lib/tests/perf/valid-annotation-format-large-file.test.js +74 -0
- package/lib/tests/plugin-default-export-and-configs.test.js +1 -0
- package/lib/tests/rules/prefer-implements-annotation.test.js +84 -70
- package/lib/tests/rules/require-branch-annotation.test.js +18 -1
- package/lib/tests/utils/branch-annotation-catch-insert-position.test.d.ts +1 -0
- package/lib/tests/utils/branch-annotation-catch-insert-position.test.js +68 -0
- package/lib/tests/utils/branch-annotation-catch-position.test.d.ts +1 -0
- package/lib/tests/utils/branch-annotation-catch-position.test.js +115 -0
- package/lib/tests/utils/req-annotation-detection.test.d.ts +1 -0
- package/lib/tests/utils/req-annotation-detection.test.js +247 -0
- package/package.json +3 -3
- package/user-docs/api-reference.md +17 -10
- package/user-docs/migration-guide.md +9 -5
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const reqAnnotationDetection_1 = require("../../src/utils/reqAnnotationDetection");
|
|
4
|
+
// Small helper to construct a minimal SourceCode-like object for the detection helpers.
|
|
5
|
+
function createMockSourceCode(options = {}) {
|
|
6
|
+
const { lines = null, text = "", commentsBefore = [] } = options;
|
|
7
|
+
return {
|
|
8
|
+
lines: lines ?? undefined,
|
|
9
|
+
getText() {
|
|
10
|
+
return text;
|
|
11
|
+
},
|
|
12
|
+
getCommentsBefore() {
|
|
13
|
+
return commentsBefore;
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
describe("reqAnnotationDetection advanced heuristics (Story 003.0-DEV-FUNCTION-ANNOTATIONS)", () => {
|
|
18
|
+
it("[REQ-ANNOTATION-REQ-DETECTION] returns false when sourceCode is missing", () => {
|
|
19
|
+
const has = (0, reqAnnotationDetection_1.hasReqAnnotation)(null, [], undefined, {
|
|
20
|
+
loc: null,
|
|
21
|
+
});
|
|
22
|
+
expect(has).toBe(false);
|
|
23
|
+
});
|
|
24
|
+
it("[REQ-ANNOTATION-REQ-DETECTION] returns false when node is missing", () => {
|
|
25
|
+
const context = {
|
|
26
|
+
getSourceCode() {
|
|
27
|
+
return createMockSourceCode({ lines: ["/** @req REQ-TEST */"] });
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
const has = (0, reqAnnotationDetection_1.hasReqAnnotation)(null, [], context, undefined);
|
|
31
|
+
expect(has).toBe(false);
|
|
32
|
+
});
|
|
33
|
+
it("[REQ-ANNOTATION-REQ-DETECTION] inspects jsdoc and comments when advanced heuristics throw", () => {
|
|
34
|
+
const context = {
|
|
35
|
+
getSourceCode() {
|
|
36
|
+
// This object intentionally causes hasReqInAdvancedHeuristics to throw by
|
|
37
|
+
// providing a getCommentsBefore implementation that throws on access.
|
|
38
|
+
return {
|
|
39
|
+
getCommentsBefore() {
|
|
40
|
+
throw new Error("boom");
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
const jsdoc = { value: "/** @req REQ-FROM-JSDOC */" };
|
|
46
|
+
const has = (0, reqAnnotationDetection_1.hasReqAnnotation)(jsdoc, [], context, {
|
|
47
|
+
// Minimal shape – the helper will call into the mock sourceCode and trigger the throw
|
|
48
|
+
parent: {},
|
|
49
|
+
});
|
|
50
|
+
expect(has).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
it("[REQ-ANNOTATION-REQ-DETECTION] treats @supports in comments as satisfying requirement", () => {
|
|
53
|
+
const context = {
|
|
54
|
+
getSourceCode() {
|
|
55
|
+
return createMockSourceCode();
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
const comments = [{ value: "// @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-X" }];
|
|
59
|
+
const has = (0, reqAnnotationDetection_1.hasReqAnnotation)(null, comments, context, {
|
|
60
|
+
parent: {},
|
|
61
|
+
});
|
|
62
|
+
expect(has).toBe(true);
|
|
63
|
+
});
|
|
64
|
+
it("[REQ-ANNOTATION-REQ-DETECTION] linesBeforeHasReq returns false when lines is not an array", () => {
|
|
65
|
+
const context = {
|
|
66
|
+
getSourceCode() {
|
|
67
|
+
// lines is null here, causing the helper to see a non-array and return false
|
|
68
|
+
return createMockSourceCode({ lines: null });
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
const has = (0, reqAnnotationDetection_1.hasReqAnnotation)(null, [], context, {
|
|
72
|
+
// Provide a minimal location so advanced heuristics try to use line info
|
|
73
|
+
loc: { start: { line: 5 } },
|
|
74
|
+
parent: {},
|
|
75
|
+
});
|
|
76
|
+
expect(has).toBe(false);
|
|
77
|
+
});
|
|
78
|
+
it("[REQ-ANNOTATION-REQ-DETECTION] linesBeforeHasReq returns false when startLine is not a number", () => {
|
|
79
|
+
const sourceCode = createMockSourceCode({ lines: ["// @req REQ-SHOULD-NOT-BE-SEEN"] });
|
|
80
|
+
const has = (0, reqAnnotationDetection_1.hasReqAnnotation)(null, [], { getSourceCode: () => sourceCode }, {
|
|
81
|
+
// loc is missing/undefined; startLine will not be a valid number
|
|
82
|
+
loc: undefined,
|
|
83
|
+
parent: {},
|
|
84
|
+
});
|
|
85
|
+
expect(has).toBe(false);
|
|
86
|
+
});
|
|
87
|
+
it("[REQ-ANNOTATION-REQ-DETECTION] parentChainHasReq returns false when getCommentsBefore is not a function and no leadingComments/parents have req", () => {
|
|
88
|
+
const context = {
|
|
89
|
+
getSourceCode() {
|
|
90
|
+
return {
|
|
91
|
+
// getCommentsBefore is not a function here
|
|
92
|
+
getCommentsBefore: 123,
|
|
93
|
+
};
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
const node = {
|
|
97
|
+
parent: {
|
|
98
|
+
leadingComments: [{ value: "no req here" }],
|
|
99
|
+
parent: {
|
|
100
|
+
leadingComments: [{ value: "still nothing" }],
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
const has = (0, reqAnnotationDetection_1.hasReqAnnotation)(null, [], context, node);
|
|
105
|
+
expect(has).toBe(false);
|
|
106
|
+
});
|
|
107
|
+
it("[REQ-ANNOTATION-REQ-DETECTION] parentChainHasReq returns true when getCommentsBefore returns comments containing @req", () => {
|
|
108
|
+
const sourceCode = {
|
|
109
|
+
getCommentsBefore(n) {
|
|
110
|
+
if (n && n.isTargetParent) {
|
|
111
|
+
return [{ value: "/* @req REQ-FROM-PARENT */" }];
|
|
112
|
+
}
|
|
113
|
+
return [];
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
const context = {
|
|
117
|
+
getSourceCode() {
|
|
118
|
+
return sourceCode;
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
const node = {
|
|
122
|
+
parent: {
|
|
123
|
+
isTargetParent: true,
|
|
124
|
+
parent: {},
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
const has = (0, reqAnnotationDetection_1.hasReqAnnotation)(null, [], context, node);
|
|
128
|
+
expect(has).toBe(true);
|
|
129
|
+
});
|
|
130
|
+
it("[REQ-ANNOTATION-REQ-DETECTION] fallbackTextBeforeHasReq returns false when getText is not a function", () => {
|
|
131
|
+
const context = {
|
|
132
|
+
getSourceCode() {
|
|
133
|
+
return {
|
|
134
|
+
// getText is not a function
|
|
135
|
+
getText: "not-a-function",
|
|
136
|
+
};
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
const node = {
|
|
140
|
+
range: [0, 10],
|
|
141
|
+
parent: {},
|
|
142
|
+
};
|
|
143
|
+
const has = (0, reqAnnotationDetection_1.hasReqAnnotation)(null, [], context, node);
|
|
144
|
+
expect(has).toBe(false);
|
|
145
|
+
});
|
|
146
|
+
it("[REQ-ANNOTATION-REQ-DETECTION] fallbackTextBeforeHasReq returns false when node.range is not an array", () => {
|
|
147
|
+
const context = {
|
|
148
|
+
getSourceCode() {
|
|
149
|
+
return createMockSourceCode({ text: "/* @req REQ-IN-TEXT */" });
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
const node = {
|
|
153
|
+
// range is missing; helper should see non-array range and return false
|
|
154
|
+
range: null,
|
|
155
|
+
parent: {},
|
|
156
|
+
};
|
|
157
|
+
const has = (0, reqAnnotationDetection_1.hasReqAnnotation)(null, [], context, node);
|
|
158
|
+
expect(has).toBe(false);
|
|
159
|
+
});
|
|
160
|
+
it("[REQ-ANNOTATION-REQ-DETECTION] fallbackTextBeforeHasReq returns true when text window contains @req", () => {
|
|
161
|
+
const fullText = `
|
|
162
|
+
// some header
|
|
163
|
+
/** @req REQ-IN-TEXT-WINDOW */
|
|
164
|
+
function foo() {}
|
|
165
|
+
`;
|
|
166
|
+
const context = {
|
|
167
|
+
getSourceCode() {
|
|
168
|
+
return createMockSourceCode({ text: fullText });
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
// Choose a range that starts after the @req comment so the "text before"
|
|
172
|
+
// window that the helper inspects includes the annotation.
|
|
173
|
+
const startIndex = fullText.indexOf("function foo");
|
|
174
|
+
const node = {
|
|
175
|
+
range: [startIndex, startIndex + 5],
|
|
176
|
+
parent: {},
|
|
177
|
+
};
|
|
178
|
+
const has = (0, reqAnnotationDetection_1.hasReqAnnotation)(null, [], context, node);
|
|
179
|
+
expect(has).toBe(true);
|
|
180
|
+
});
|
|
181
|
+
it("[REQ-ANNOTATION-REQ-DETECTION] fallbackTextBeforeHasReq returns false when getText throws", () => {
|
|
182
|
+
const context = {
|
|
183
|
+
getSourceCode() {
|
|
184
|
+
return {
|
|
185
|
+
getText() {
|
|
186
|
+
throw new Error("boom from getText");
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
const node = {
|
|
192
|
+
range: [0, 10],
|
|
193
|
+
parent: {},
|
|
194
|
+
};
|
|
195
|
+
const has = (0, reqAnnotationDetection_1.hasReqAnnotation)(null, [], context, node);
|
|
196
|
+
expect(has).toBe(false);
|
|
197
|
+
});
|
|
198
|
+
it("[REQ-ANNOTATION-REQ-DETECTION] hasReqInAdvancedHeuristics short-circuits and returns false when sourceCode is missing", () => {
|
|
199
|
+
const context = {
|
|
200
|
+
// No getSourceCode method at all – internal advanced heuristics
|
|
201
|
+
// should immediately return false and not throw.
|
|
202
|
+
};
|
|
203
|
+
const node = {
|
|
204
|
+
loc: { start: { line: 3 } },
|
|
205
|
+
range: [0, 10],
|
|
206
|
+
parent: {},
|
|
207
|
+
};
|
|
208
|
+
const has = (0, reqAnnotationDetection_1.hasReqAnnotation)(null, [], context, node);
|
|
209
|
+
expect(has).toBe(false);
|
|
210
|
+
});
|
|
211
|
+
it("[REQ-ANNOTATION-REQ-DETECTION] hasReqInAdvancedHeuristics short-circuits and returns false when node is missing", () => {
|
|
212
|
+
const context = {
|
|
213
|
+
getSourceCode() {
|
|
214
|
+
return createMockSourceCode({ text: "@req REQ-SHOULD-NOT-BE-SEEN" });
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
const has = (0, reqAnnotationDetection_1.hasReqAnnotation)(null, [], context, undefined);
|
|
218
|
+
expect(has).toBe(false);
|
|
219
|
+
});
|
|
220
|
+
it("[REQ-ANNOTATION-REQ-DETECTION] hasReqAnnotation returns true when jsdoc contains @supports and advanced heuristics are false", () => {
|
|
221
|
+
const context = {
|
|
222
|
+
getSourceCode() {
|
|
223
|
+
// Returning a sourceCode that will not satisfy any advanced heuristic
|
|
224
|
+
// (no lines, no comments, empty text).
|
|
225
|
+
return createMockSourceCode({ lines: [], text: "" });
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
const jsdoc = {
|
|
229
|
+
value: "/** @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-JSDOC-SUPPORTS */",
|
|
230
|
+
};
|
|
231
|
+
const node = {
|
|
232
|
+
parent: {},
|
|
233
|
+
};
|
|
234
|
+
const has = (0, reqAnnotationDetection_1.hasReqAnnotation)(jsdoc, [], context, node);
|
|
235
|
+
expect(has).toBe(true);
|
|
236
|
+
});
|
|
237
|
+
it("[REQ-ANNOTATION-REQ-DETECTION] falls back to jsdoc/comments when context.getSourceCode throws", () => {
|
|
238
|
+
const context = {
|
|
239
|
+
getSourceCode() {
|
|
240
|
+
throw new Error("boom from getSourceCode");
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
const jsdoc = { value: "/** @req REQ-FROM-GETSOURCECODE */" };
|
|
244
|
+
const has = (0, reqAnnotationDetection_1.hasReqAnnotation)(jsdoc, [], context, { parent: {} });
|
|
245
|
+
expect(has).toBe(true);
|
|
246
|
+
});
|
|
247
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-traceability",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.4",
|
|
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",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"safety:deps": "node scripts/ci-safety-deps.js",
|
|
42
42
|
"audit:ci": "node scripts/ci-audit.js",
|
|
43
43
|
"check:ci-artifacts": "node scripts/check-no-tracked-ci-artifacts.js",
|
|
44
|
-
"security:secrets": "secretlint \"**/*\"
|
|
44
|
+
"security:secrets": "secretlint \"**/*\"",
|
|
45
45
|
"smoke-test": "./scripts/smoke-test.sh",
|
|
46
46
|
"debug:cli": "node scripts/cli-debug.js",
|
|
47
47
|
"debug:require-story": "node scripts/debug-require-story.js",
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
"eslint": "^9.0.0"
|
|
100
100
|
},
|
|
101
101
|
"engines": {
|
|
102
|
-
"node": "
|
|
102
|
+
"node": "^18.18.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
|
|
103
103
|
},
|
|
104
104
|
"overrides": {
|
|
105
105
|
"glob": "12.0.0",
|
|
@@ -15,7 +15,7 @@ In addition to the core `@story` and `@req` annotations, the plugin also underst
|
|
|
15
15
|
`@supports docs/stories/010.0-PAYMENTS.story.md#REQ-PAYMENTS-REFUND`
|
|
16
16
|
to indicate that a given function supports a particular requirement from a payments story document within that project’s own `docs/stories` tree. For a detailed explanation of `@supports` behavior and validation, see [Migration Guide](migration-guide.md) (section **3.1 Multi-story @supports annotations**). Additional background on multi-story semantics is available in the project’s internal rule documentation, which is intended for maintainers rather than end users.
|
|
17
17
|
|
|
18
|
-
The `prefer-
|
|
18
|
+
The `prefer-supports-annotation` rule is an **opt-in migration helper** that is disabled by default and **not** part of any built-in preset. It can be enabled and given a severity like `"warn"` or `"error"` using normal ESLint rule configuration when you want to gradually encourage multi-story `@supports` usage. The legacy rule key `traceability/prefer-implements-annotation` remains available as a **deprecated alias** for backward compatibility, but new configurations should prefer `traceability/prefer-supports-annotation`. Detailed behavior and migration guidance are documented in the project’s internal rule documentation, which is targeted at maintainers; typical end users can rely on the high-level guidance in this API reference and the [Migration Guide](migration-guide.md).
|
|
19
19
|
|
|
20
20
|
### traceability/require-story-annotation
|
|
21
21
|
|
|
@@ -68,11 +68,17 @@ function initAuth() {
|
|
|
68
68
|
|
|
69
69
|
### traceability/require-branch-annotation
|
|
70
70
|
|
|
71
|
-
Description: Ensures significant code branches (if/else, loops, switch cases, try/catch) have both `@story` and `@req` annotations in preceding comments.
|
|
71
|
+
Description: Ensures significant code branches (if/else, loops, switch cases, try/catch) have both `@story` and `@req` annotations in preceding comments. For `catch` clauses specifically, the rule accepts annotations either immediately before the `catch` keyword or as the first comment-only lines inside the catch block; for `else if` branches, the rule accepts annotations either immediately before the `else if` keyword or on comment-only lines between the `else if (condition)` and the first statement of the consequent body, matching Prettier’s wrapped style.
|
|
72
|
+
|
|
72
73
|
Options:
|
|
73
74
|
|
|
74
75
|
- `branchTypes` (string[], optional) – AST node types that are treated as significant branches for annotation enforcement. Allowed values: "IfStatement", "SwitchCase", "TryStatement", "CatchClause", "ForStatement", "ForOfStatement", "ForInStatement", "WhileStatement", "DoWhileStatement". Default: ["IfStatement", "SwitchCase", "TryStatement", "CatchClause", "ForStatement", "ForOfStatement", "ForInStatement", "WhileStatement", "DoWhileStatement"]. If an invalid branch type is provided, the rule reports a configuration error for each invalid value.
|
|
75
76
|
|
|
77
|
+
Behavior notes:
|
|
78
|
+
|
|
79
|
+
- When both before-`catch` and inside-block annotations are present for the same catch clause, the comments immediately before `catch` take precedence for validation and reporting.
|
|
80
|
+
- When auto-fixing missing annotations on a catch clause, the rule inserts placeholder comments inside the catch body so that formatters like Prettier preserve them and do not move them to unexpected locations.
|
|
81
|
+
|
|
76
82
|
Default Severity: `error`
|
|
77
83
|
Example:
|
|
78
84
|
|
|
@@ -246,9 +252,9 @@ describe("Refunds flow docs/stories/010.0-PAYMENTS.story.md", () => {
|
|
|
246
252
|
});
|
|
247
253
|
```
|
|
248
254
|
|
|
249
|
-
### traceability/prefer-
|
|
255
|
+
### traceability/prefer-supports-annotation
|
|
250
256
|
|
|
251
|
-
Description: An optional, opt-in migration helper that encourages converting legacy single‑story `@story` + `@req` JSDoc blocks into the newer multi‑story `@supports` format. The rule is **disabled by default** and is **not included in any built‑in preset**; you enable it explicitly and control its behavior entirely via ESLint severity (`"off" | "warn" | "error"`). It does not change what the core rules consider valid—it only adds migration recommendations and safe auto‑fixes on top of existing validation.
|
|
257
|
+
Description: An optional, opt-in migration helper that encourages converting legacy single‑story `@story` + `@req` JSDoc blocks into the newer multi‑story `@supports` format. The rule is **disabled by default** and is **not included in any built‑in preset**; you enable it explicitly and control its behavior entirely via ESLint severity (`"off" | "warn" | "error"`). It does not change what the core rules consider valid—it only adds migration recommendations and safe auto‑fixes on top of existing validation. The legacy rule key `traceability/prefer-implements-annotation` is still recognized as a **deprecated alias** for `traceability/prefer-supports-annotation` so that existing configurations continue to work unchanged.
|
|
252
258
|
|
|
253
259
|
Options: None – this rule does not accept a configuration object. All tuning is done via the ESLint rule level (`"off"`, `"warn"`, `"error"`).
|
|
254
260
|
|
|
@@ -318,7 +324,7 @@ function issueRefund() {
|
|
|
318
324
|
}
|
|
319
325
|
```
|
|
320
326
|
|
|
321
|
-
With `traceability/prefer-
|
|
327
|
+
With `traceability/prefer-supports-annotation` enabled (or its deprecated alias `traceability/prefer-implements-annotation`) and ESLint run with `--fix`, the rule rewrites it to:
|
|
322
328
|
|
|
323
329
|
```js
|
|
324
330
|
/**
|
|
@@ -343,7 +349,9 @@ export default [
|
|
|
343
349
|
traceability.configs.recommended,
|
|
344
350
|
{
|
|
345
351
|
rules: {
|
|
346
|
-
"traceability/prefer-
|
|
352
|
+
"traceability/prefer-supports-annotation": "warn",
|
|
353
|
+
// The deprecated alias is still honored if you prefer:
|
|
354
|
+
// "traceability/prefer-implements-annotation": "warn",
|
|
347
355
|
},
|
|
348
356
|
},
|
|
349
357
|
];
|
|
@@ -358,7 +366,7 @@ The plugin provides two built-in presets for easy configuration:
|
|
|
358
366
|
Enables the **six core traceability rules** with severities tuned for common usage (most at `error`, with
|
|
359
367
|
`traceability/valid-annotation-format` at `warn` to reduce noise). This `warn` level for `traceability/valid-annotation-format` is intentional to keep early adoption noise low, but you can safely raise it to `error` in projects that want strict enforcement of annotation formatting.
|
|
360
368
|
|
|
361
|
-
The `prefer-
|
|
369
|
+
The `prefer-supports-annotation` migration rule (and its deprecated alias key `traceability/prefer-implements-annotation`) is **not included** in this (or any) preset and remains disabled by default. If you want to encourage or enforce multi-story `@supports` annotations, you must enable `traceability/prefer-supports-annotation` explicitly in your ESLint configuration and choose an appropriate severity (for example, `"warn"` during migration or `"error"` once fully adopted).
|
|
362
370
|
|
|
363
371
|
Core rules enabled by the `recommended` preset:
|
|
364
372
|
|
|
@@ -381,7 +389,7 @@ export default [js.configs.recommended, traceability.configs.recommended];
|
|
|
381
389
|
|
|
382
390
|
### strict
|
|
383
391
|
|
|
384
|
-
Currently mirrors the **recommended** preset, reserved for future stricter policies. As with the `recommended` preset, the `traceability/prefer-
|
|
392
|
+
Currently mirrors the **recommended** preset, reserved for future stricter policies. As with the `recommended` preset, the `traceability/prefer-supports-annotation` rule is **not** enabled here by default, and the deprecated alias key `traceability/prefer-implements-annotation` continues to be honored only when you opt into it manually in your own configuration.
|
|
385
393
|
|
|
386
394
|
Usage:
|
|
387
395
|
|
|
@@ -681,5 +689,4 @@ If `--from` or `--to` is missing, the CLI prints an error, shows the help text,
|
|
|
681
689
|
In CI:
|
|
682
690
|
|
|
683
691
|
```bash
|
|
684
|
-
npm run traceability:verify
|
|
685
|
-
```
|
|
692
|
+
npm run traceability:verify
|
|
@@ -57,18 +57,22 @@ function integrate() {}
|
|
|
57
57
|
|
|
58
58
|
You **do not** need to change existing, single-story annotations that already use `@story` and `@req`. Migration to `@supports` is only recommended when a function or module genuinely implements requirements from more than one story file.
|
|
59
59
|
|
|
60
|
-
#### Optional `prefer-
|
|
60
|
+
#### Optional `prefer-supports-annotation` migration rule
|
|
61
61
|
|
|
62
|
-
For teams that want to gradually migrate from `@story` + `@req` to `@supports`, the plugin provides an optional rule: `traceability/prefer-
|
|
62
|
+
For teams that want to gradually migrate from `@story` + `@req` to `@supports`, the plugin provides an optional rule: `traceability/prefer-supports-annotation`.
|
|
63
63
|
|
|
64
|
-
- This
|
|
64
|
+
- This is the canonical rule name starting in v1.x.
|
|
65
|
+
- The legacy key `traceability/prefer-implements-annotation` remains supported as a **deprecated alias** for backward compatibility, but should not be used in new configurations.
|
|
66
|
+
|
|
67
|
+
- This rule is **disabled by default** and is **not** included in any built-in presets (the deprecated alias is also not enabled by any presets).
|
|
65
68
|
- You can enable it with any standard ESLint severity (`"off"`, `"warn"`, or `"error"`) in your config, for example:
|
|
66
69
|
|
|
67
70
|
```js
|
|
68
71
|
// excerpt from eslint.config.js
|
|
69
72
|
{
|
|
70
73
|
rules: {
|
|
71
|
-
"traceability/prefer-
|
|
74
|
+
"traceability/prefer-supports-annotation": "warn",
|
|
75
|
+
// "traceability/prefer-implements-annotation": "warn", // deprecated alias
|
|
72
76
|
},
|
|
73
77
|
}
|
|
74
78
|
```
|
|
@@ -101,7 +105,7 @@ Aligned with the internal rule behavior, the key cases are:
|
|
|
101
105
|
- Comments that contain only `@supports` lines, and
|
|
102
106
|
- Line comments such as `// @story ...`.
|
|
103
107
|
|
|
104
|
-
These forms are still supported by the plugin and are not modified by `traceability/prefer-
|
|
108
|
+
These forms are still supported by the plugin and are not modified by `traceability/prefer-supports-annotation`.
|
|
105
109
|
|
|
106
110
|
A typical migration path is:
|
|
107
111
|
|