eslint-plugin-traceability 1.8.0 → 1.8.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 +5 -5
- package/README.md +28 -29
- package/SECURITY.md +135 -0
- package/lib/src/index.d.ts +6 -35
- package/lib/src/index.js +8 -5
- package/lib/src/maintenance/cli.js +12 -16
- package/lib/src/maintenance/detect.js +28 -1
- package/lib/src/rules/helpers/require-story-io.d.ts +2 -2
- package/lib/src/rules/helpers/require-story-io.js +13 -13
- package/lib/src/rules/helpers/valid-annotation-format-internal.d.ts +2 -2
- package/lib/src/rules/helpers/valid-annotation-format-internal.js +3 -3
- package/lib/src/rules/helpers/valid-annotation-utils.d.ts +5 -0
- package/lib/src/rules/helpers/valid-annotation-utils.js +43 -5
- package/lib/src/rules/helpers/valid-implements-utils.d.ts +11 -11
- package/lib/src/rules/helpers/valid-implements-utils.js +11 -11
- package/lib/src/rules/helpers/valid-story-reference-helpers.js +19 -0
- package/lib/src/rules/prefer-implements-annotation.d.ts +7 -7
- package/lib/src/rules/prefer-implements-annotation.js +21 -21
- package/lib/src/rules/valid-annotation-format.js +50 -24
- package/lib/src/rules/valid-req-reference.js +9 -9
- package/lib/src/utils/annotation-checker.js +3 -1
- package/lib/src/utils/reqAnnotationDetection.d.ts +2 -2
- package/lib/src/utils/reqAnnotationDetection.js +28 -28
- package/lib/tests/config/flat-config-presets-integration.test.d.ts +1 -0
- package/lib/tests/config/flat-config-presets-integration.test.js +75 -0
- package/lib/tests/maintenance/batch.test.js +11 -11
- package/lib/tests/maintenance/cli.test.js +34 -27
- package/lib/tests/maintenance/report.test.js +7 -7
- package/lib/tests/plugin-default-export-and-configs.test.js +0 -2
- package/lib/tests/rules/prefer-implements-annotation.test.js +48 -15
- package/lib/tests/rules/require-branch-annotation.test.js +15 -36
- package/lib/tests/rules/require-req-annotation.test.js +31 -104
- package/lib/tests/rules/require-story-annotation.test.js +3 -3
- package/lib/tests/rules/require-story-io-behavior.test.js +2 -7
- package/lib/tests/rules/require-story-io.edgecases.test.js +2 -7
- package/lib/tests/rules/require-story-visitors-edgecases.test.js +8 -8
- package/lib/tests/rules/valid-annotation-format.test.js +23 -23
- package/lib/tests/rules/valid-req-reference.test.js +9 -9
- package/lib/tests/rules/valid-story-reference.test.js +4 -43
- package/lib/tests/utils/annotation-checker.test.js +2 -6
- package/lib/tests/utils/fsTestHelpers.d.ts +7 -0
- package/lib/tests/utils/fsTestHelpers.js +26 -0
- package/lib/tests/utils/ioTestHelpers.d.ts +7 -0
- package/lib/tests/utils/ioTestHelpers.js +24 -0
- package/lib/tests/utils/temp-dir-helpers.d.ts +14 -0
- package/lib/tests/utils/temp-dir-helpers.js +61 -0
- package/package.json +8 -7
- package/user-docs/api-reference.md +37 -20
- package/user-docs/eslint-9-setup-guide.md +89 -6
- package/user-docs/migration-guide.md +37 -21
- package/docs/ci-cd-pipeline.md +0 -224
- package/docs/cli-integration.md +0 -22
- package/docs/code-quality-refactor-opportunities-2025-12-03.md +0 -78
- package/docs/config-presets.md +0 -38
- package/docs/conventional-commits-guide.md +0 -185
- package/docs/custom-rules-development-guide.md +0 -659
- package/docs/decisions/0001-allow-dynamic-require-for-built-plugins.md +0 -26
- package/docs/decisions/001-typescript-for-eslint-plugin.accepted.md +0 -111
- package/docs/decisions/002-jest-for-eslint-testing.accepted.md +0 -137
- package/docs/decisions/003-code-quality-ratcheting-plan.md +0 -48
- package/docs/decisions/004-automated-version-bumping-for-ci-cd.md +0 -196
- package/docs/decisions/005-github-actions-validation-tooling.accepted.md +0 -144
- package/docs/decisions/006-semantic-release-for-automated-publishing.accepted.md +0 -227
- package/docs/decisions/007-github-releases-over-changelog.accepted.md +0 -216
- package/docs/decisions/008-ci-audit-flags.accepted.md +0 -60
- package/docs/decisions/009-security-focused-lint-rules.accepted.md +0 -64
- package/docs/decisions/010-implements-annotation-for-multi-story-requirements.proposed.md +0 -184
- package/docs/decisions/adr-0001-console-usage-for-cli-guards.md +0 -190
- package/docs/decisions/adr-accept-dev-dep-risk-glob.md +0 -40
- package/docs/decisions/adr-commit-branch-tests.md +0 -54
- package/docs/decisions/adr-maintenance-cli-interface.md +0 -140
- package/docs/decisions/adr-pre-push-parity.md +0 -112
- package/docs/decisions/code-quality-ratcheting-plan.md +0 -53
- package/docs/dependency-health.md +0 -238
- package/docs/eslint-9-setup-guide.md +0 -517
- package/docs/eslint-plugin-development-guide.md +0 -487
- package/docs/functionality-coverage-2025-12-03.md +0 -250
- package/docs/jest-testing-guide.md +0 -100
- package/docs/rules/prefer-implements-annotation.md +0 -219
- package/docs/rules/require-branch-annotation.md +0 -71
- package/docs/rules/require-req-annotation.md +0 -203
- package/docs/rules/require-story-annotation.md +0 -159
- package/docs/rules/valid-annotation-format.md +0 -418
- package/docs/rules/valid-req-reference.md +0 -153
- package/docs/rules/valid-story-reference.md +0 -120
- package/docs/security-incidents/2025-11-17-glob-cli-incident.md +0 -45
- package/docs/security-incidents/2025-11-18-brace-expansion-redos.md +0 -45
- package/docs/security-incidents/2025-11-18-bundled-dev-deps-accepted-risk.md +0 -93
- package/docs/security-incidents/2025-11-18-tar-race-condition.md +0 -43
- package/docs/security-incidents/2025-12-03-dependency-health-review.md +0 -58
- package/docs/security-incidents/SECURITY-INCIDENT-2025-11-18-semantic-release-bundled-npm.known-error.md +0 -104
- package/docs/security-incidents/SECURITY-INCIDENT-TEMPLATE.md +0 -37
- package/docs/security-incidents/dependency-override-rationale.md +0 -57
- package/docs/security-incidents/dev-deps-high.json +0 -116
- package/docs/security-incidents/handling-procedure.md +0 -54
- package/docs/stories/001.0-DEV-PLUGIN-SETUP.story.md +0 -92
- package/docs/stories/002.0-DEV-ESLINT-CONFIG.story.md +0 -82
- package/docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md +0 -112
- package/docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md +0 -153
- package/docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md +0 -138
- package/docs/stories/006.0-DEV-FILE-VALIDATION.story.md +0 -144
- package/docs/stories/007.0-DEV-ERROR-REPORTING.story.md +0 -163
- package/docs/stories/008.0-DEV-AUTO-FIX.story.md +0 -150
- package/docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md +0 -117
- package/docs/stories/010.0-DEV-DEEP-VALIDATION.story.md +0 -124
- package/docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md +0 -149
- package/docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md +0 -216
- package/docs/stories/010.3-DEV-MIGRATE-TO-IMPLEMENTS.story.md +0 -236
- package/docs/stories/developer-story.map.md +0 -120
- package/docs/ts-jest-presets-guide.md +0 -548
|
@@ -1,418 +0,0 @@
|
|
|
1
|
-
# valid-annotation-format
|
|
2
|
-
|
|
3
|
-
Validates that `@story`, `@req`, and `@implements` annotations follow the correct format and syntax rules to ensure traceability annotations are parseable and standardized.
|
|
4
|
-
|
|
5
|
-
@story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
6
|
-
@story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
|
|
7
|
-
@story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
8
|
-
@req REQ-FORMAT-SPECIFICATION - Define clear format rules for @story and @req annotations
|
|
9
|
-
@req REQ-SYNTAX-VALIDATION - Validate annotation syntax matches specification
|
|
10
|
-
@req REQ-PATH-FORMAT - Validate @story paths follow expected patterns
|
|
11
|
-
@req REQ-REQ-FORMAT - Validate @req identifiers follow expected patterns
|
|
12
|
-
|
|
13
|
-
## Rule Details
|
|
14
|
-
|
|
15
|
-
This rule scans all comments in the source code and validates any lines that contain `@story`, `@req`, or `@implements` annotations. It is designed to be flexible in how it discovers annotations while still enforcing a strict, machine-parseable format.
|
|
16
|
-
|
|
17
|
-
Key behaviors:
|
|
18
|
-
|
|
19
|
-
- **Flexible parsing**
|
|
20
|
-
- Works in line (`// ...`), block (`/* ... */`), and JSDoc (`/** ... */`) comments.
|
|
21
|
-
- Annotations can appear anywhere in the comment text, not only at the beginning of the line.
|
|
22
|
-
- Multiple annotations can appear in the same comment block or on the same line.
|
|
23
|
-
|
|
24
|
-
- **Multiline annotation support**
|
|
25
|
-
- Annotation values may be split across multiple lines within the same block or JSDoc comment.
|
|
26
|
-
- The rule concatenates all lines that belong to the same annotation, removing all internal whitespace characters (spaces, tabs, and newlines) before validating the final value.
|
|
27
|
-
- This allows patterns such as:
|
|
28
|
-
```js
|
|
29
|
-
/**
|
|
30
|
-
* @story docs/stories/005.0-
|
|
31
|
-
* DEV-ANNOTATION-VALIDATION.story.md
|
|
32
|
-
*/
|
|
33
|
-
```
|
|
34
|
-
which will be normalized and validated as
|
|
35
|
-
`@story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md`.
|
|
36
|
-
|
|
37
|
-
- **`@implements` format support**
|
|
38
|
-
- The rule validates `@implements` annotations that associate code with one or more stories and requirements, such as:
|
|
39
|
-
```js
|
|
40
|
-
/**
|
|
41
|
-
* @implements docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md REQ-FOO REQ-BAR
|
|
42
|
-
*/
|
|
43
|
-
```
|
|
44
|
-
- The story path that appears first in an `@implements` annotation is validated using the same story pattern as `@story`.
|
|
45
|
-
- All requirement IDs that follow in the same `@implements` annotation are validated using the same requirement pattern as `@req`.
|
|
46
|
-
- This ensures that multi-story / multi-requirement traceability annotations share the same standardized, machine-parseable formats as standalone `@story` and `@req` annotations.
|
|
47
|
-
|
|
48
|
-
- **Validated patterns (configurable)**
|
|
49
|
-
- The rule validates:
|
|
50
|
-
- **Story identifiers**: the value that follows `@story` (and the story path segment of `@implements`)
|
|
51
|
-
- **Requirement identifiers**: the value that follows `@req` (and each requirement ID segment of `@implements`)
|
|
52
|
-
- Both patterns are configurable via rule options so that teams can align validation with their own conventions while still keeping annotations machine-parseable.
|
|
53
|
-
|
|
54
|
-
## Options
|
|
55
|
-
|
|
56
|
-
The rule supports an optional configuration object. The **primary** configuration shape uses nested `story` and `req` objects, which allow you to configure both the validation pattern and the example used in error messages:
|
|
57
|
-
|
|
58
|
-
```jsonc
|
|
59
|
-
"valid-annotation-format": [
|
|
60
|
-
"error",
|
|
61
|
-
{
|
|
62
|
-
"story": {
|
|
63
|
-
"pattern": "^docs/stories/[0-9]+\\.[0-9]+-DEV-[\\w-]+\\.story\\.md$",
|
|
64
|
-
"example": "docs/stories/005.0-DEV-EXAMPLE.story.md"
|
|
65
|
-
},
|
|
66
|
-
"req": {
|
|
67
|
-
"pattern": "^REQ-[A-Z0-9-]+$",
|
|
68
|
-
"example": "REQ-EXAMPLE"
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
]
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
All configuration fields are optional; when omitted, the rule falls back to the built-in defaults described below.
|
|
75
|
-
|
|
76
|
-
The canonical, nested configuration fields are:
|
|
77
|
-
|
|
78
|
-
- `story.pattern` / `story.example`
|
|
79
|
-
- `req.pattern` / `req.example`
|
|
80
|
-
|
|
81
|
-
For convenience, a **flat shorthand** form is also supported using:
|
|
82
|
-
|
|
83
|
-
- `storyPathPattern`
|
|
84
|
-
- `storyPathExample`
|
|
85
|
-
- `requirementIdPattern`
|
|
86
|
-
- `requirementIdExample`
|
|
87
|
-
|
|
88
|
-
These flat fields map directly onto the canonical nested fields. When both nested (`story` / `req`) and flat shorthand fields are provided for the same value, the **nested configuration takes precedence**.
|
|
89
|
-
|
|
90
|
-
### Nested configuration
|
|
91
|
-
|
|
92
|
-
Nested configuration is the recommended form and mirrors how the rule internally organizes its helpers.
|
|
93
|
-
|
|
94
|
-
#### `story`
|
|
95
|
-
|
|
96
|
-
```jsonc
|
|
97
|
-
{
|
|
98
|
-
"story": {
|
|
99
|
-
"pattern": "^docs/stories/[0-9]+\\.[0-9]+-DEV-[\\w-]+\\.story\\.md$",
|
|
100
|
-
"example": "docs/stories/005.0-DEV-EXAMPLE.story.md",
|
|
101
|
-
},
|
|
102
|
-
}
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
- `story.pattern`
|
|
106
|
-
- **Type:** `string` (a JavaScript regular expression source, without surrounding slashes)
|
|
107
|
-
- **Purpose:** Defines the allowed format for `@story` values.
|
|
108
|
-
- **Default:** (from `getDefaultStoryPattern()`)
|
|
109
|
-
```txt
|
|
110
|
-
^docs/stories/[0-9]+\.[0-9]+-DEV-[\w-]+\.story\.md$
|
|
111
|
-
```
|
|
112
|
-
- **Default expectation:**
|
|
113
|
-
- Path starts with `docs/stories/`
|
|
114
|
-
- Followed by `<major>.<minor>` numeric version segments (e.g. `005.0`)
|
|
115
|
-
- Followed by `-DEV-`
|
|
116
|
-
- Followed by an uppercase, word-character-and-dash name segment
|
|
117
|
-
- Ends with `.story.md`
|
|
118
|
-
- You can change this to match your own story path / traceability file layout, for example:
|
|
119
|
-
```jsonc
|
|
120
|
-
{
|
|
121
|
-
"story": {
|
|
122
|
-
"pattern": "^trace/stories/STORY-[0-9]+\\.md$",
|
|
123
|
-
"example": "trace/stories/STORY-123.md",
|
|
124
|
-
},
|
|
125
|
-
}
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
- `story.example`
|
|
129
|
-
- **Type:** `string`
|
|
130
|
-
- **Purpose:** Provides a human-readable example that appears in error messages when a `@story` value does not match `story.pattern`.
|
|
131
|
-
- **Default:** (from `getDefaultStoryExample()`)
|
|
132
|
-
`docs/stories/005.0-DEV-EXAMPLE.story.md`
|
|
133
|
-
- **Behavior:**
|
|
134
|
-
- Not used for validation; only for constructing clearer diagnostics.
|
|
135
|
-
- Should be a single example that matches `story.pattern`.
|
|
136
|
-
|
|
137
|
-
#### `req`
|
|
138
|
-
|
|
139
|
-
```jsonc
|
|
140
|
-
{
|
|
141
|
-
"req": {
|
|
142
|
-
"pattern": "^REQ-[A-Z0-9-]+$",
|
|
143
|
-
"example": "REQ-EXAMPLE",
|
|
144
|
-
},
|
|
145
|
-
}
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
- `req.pattern`
|
|
149
|
-
- **Type:** `string` (a JavaScript regular expression source, without surrounding slashes)
|
|
150
|
-
- **Purpose:** Defines the allowed format for `@req` values.
|
|
151
|
-
- **Default:** (from `getDefaultReqPattern()`)
|
|
152
|
-
```txt
|
|
153
|
-
^REQ-[A-Z0-9-]+$
|
|
154
|
-
```
|
|
155
|
-
- **Default expectation:**
|
|
156
|
-
- Identifier starts with `REQ-`
|
|
157
|
-
- Contains only uppercase letters, digits, and dashes.
|
|
158
|
-
- You can adapt this to your requirement ID scheme, e.g.:
|
|
159
|
-
```jsonc
|
|
160
|
-
{
|
|
161
|
-
"req": {
|
|
162
|
-
"pattern": "^SYS-[0-9]{4}$",
|
|
163
|
-
"example": "SYS-0001",
|
|
164
|
-
},
|
|
165
|
-
}
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
- `req.example`
|
|
169
|
-
- **Type:** `string`
|
|
170
|
-
- **Purpose:** Provides a human-readable example that appears in error messages when a `@req` value does not match `req.pattern`.
|
|
171
|
-
- **Default:** (from `getDefaultReqExample()`)
|
|
172
|
-
`REQ-EXAMPLE`
|
|
173
|
-
- **Behavior:**
|
|
174
|
-
- Not used for validation; only for diagnostics.
|
|
175
|
-
- Should be a single example that matches `req.pattern`.
|
|
176
|
-
|
|
177
|
-
### Flat shorthand configuration
|
|
178
|
-
|
|
179
|
-
Instead of nested `story` / `req` objects, you can configure the same options directly on the rule options object:
|
|
180
|
-
|
|
181
|
-
```jsonc
|
|
182
|
-
"valid-annotation-format": [
|
|
183
|
-
"error",
|
|
184
|
-
{
|
|
185
|
-
"storyPathPattern": "^trace/stories/STORY-[0-9]+\\.md$",
|
|
186
|
-
"storyPathExample": "trace/stories/STORY-123.md",
|
|
187
|
-
"requirementIdPattern": "^SYS-[0-9]{4}$",
|
|
188
|
-
"requirementIdExample": "SYS-0001"
|
|
189
|
-
}
|
|
190
|
-
]
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
Flat fields map directly to the canonical nested ones:
|
|
194
|
-
|
|
195
|
-
- `storyPathPattern` → `story.pattern`
|
|
196
|
-
- `storyPathExample` → `story.example`
|
|
197
|
-
- `requirementIdPattern` → `req.pattern`
|
|
198
|
-
- `requirementIdExample` → `req.example`
|
|
199
|
-
|
|
200
|
-
If you specify both nested and flat values for the same option, the **nested `story` / `req` values take precedence** over the flat shorthand.
|
|
201
|
-
|
|
202
|
-
### Invalid configuration
|
|
203
|
-
|
|
204
|
-
If any of the pattern fields contain an invalid regular expression source—whether provided via nested `story.pattern` / `req.pattern` or via flat shorthand `storyPathPattern` / `requirementIdPattern`—the rule:
|
|
205
|
-
|
|
206
|
-
- Reports an ESLint configuration diagnostic with `messageId: "invalidRuleConfiguration"`.
|
|
207
|
-
- Falls back to the **built-in default** story and/or requirement patterns (`getDefaultStoryPattern()` / `getDefaultReqPattern()`) for actual annotation validation, so that rule execution still proceeds with known-good patterns.
|
|
208
|
-
|
|
209
|
-
This behavior ensures configuration errors are visible without breaking validation of annotations in your codebase.
|
|
210
|
-
|
|
211
|
-
## Error messages
|
|
212
|
-
|
|
213
|
-
The rule reports targeted, specific messages depending on what failed, using the configured patterns and examples where appropriate. Messages are produced via `meta.messages.invalidStoryFormat` / `invalidReqFormat` and the helpers `buildStoryErrorMessage` / `buildReqErrorMessage`.
|
|
214
|
-
|
|
215
|
-
Examples of the actual runtime messages:
|
|
216
|
-
|
|
217
|
-
- **Missing story value**
|
|
218
|
-
- When `@story` is present but no path value is provided:
|
|
219
|
-
- `Invalid annotation format: Missing story path for @story annotation. Expected a path like "docs/stories/005.0-DEV-EXAMPLE.story.md".`
|
|
220
|
-
|
|
221
|
-
- **Invalid story identifier format**
|
|
222
|
-
- When a `@story` value is present but does not match `story.pattern`:
|
|
223
|
-
- `Invalid annotation format: Invalid story path "foo/bar.story.md" for @story annotation. Expected a path like "docs/stories/005.0-DEV-EXAMPLE.story.md".`
|
|
224
|
-
- If you configure `story.pattern` / `story.example` (or the corresponding flat shorthand fields), the example in the message will change accordingly.
|
|
225
|
-
|
|
226
|
-
- **Missing requirement value**
|
|
227
|
-
- When `@req` is present but no identifier value is provided:
|
|
228
|
-
- `Invalid annotation format: Missing requirement ID for @req annotation. Expected an identifier like "REQ-EXAMPLE".`
|
|
229
|
-
|
|
230
|
-
- **Invalid requirement identifier format**
|
|
231
|
-
- When a `@req` value is present but does not match `req.pattern`:
|
|
232
|
-
- `Invalid annotation format: Invalid requirement ID "Req-foo" for @req annotation. Expected an identifier like "REQ-EXAMPLE" (uppercase letters, numbers, and dashes only).`
|
|
233
|
-
- If you configure `req.pattern` / `req.example` (or the corresponding flat shorthand fields), the example in the message will change accordingly.
|
|
234
|
-
|
|
235
|
-
Violations always include:
|
|
236
|
-
|
|
237
|
-
- The exact line (and, where possible, column) of the offending annotation.
|
|
238
|
-
- Whether the problem is with a `@story` value or a `@req` value.
|
|
239
|
-
- A short description of what part of the expected format was not met, to make fixes straightforward.
|
|
240
|
-
|
|
241
|
-
## Examples
|
|
242
|
-
|
|
243
|
-
#### Correct (default configuration)
|
|
244
|
-
|
|
245
|
-
```js
|
|
246
|
-
/**
|
|
247
|
-
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
248
|
-
* @req REQ-FORMAT-SPECIFICATION
|
|
249
|
-
*/
|
|
250
|
-
function example() {}
|
|
251
|
-
|
|
252
|
-
// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
253
|
-
// @req REQ-SYNTAX-VALIDATION
|
|
254
|
-
if (condition) {
|
|
255
|
-
/* ... */
|
|
256
|
-
}
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
#### Correct (custom configuration, nested)
|
|
260
|
-
|
|
261
|
-
```js
|
|
262
|
-
/* eslint valid-annotation-format: [
|
|
263
|
-
"error",
|
|
264
|
-
{
|
|
265
|
-
story: {
|
|
266
|
-
pattern: "^trace/stories/STORY-[0-9]+\\.md$",
|
|
267
|
-
example: "trace/stories/STORY-123.md"
|
|
268
|
-
},
|
|
269
|
-
req: {
|
|
270
|
-
pattern: "^SYS-[0-9]{4}$",
|
|
271
|
-
example: "SYS-0001"
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
] */
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* @story trace/stories/STORY-123.md
|
|
278
|
-
* @req SYS-0001
|
|
279
|
-
*/
|
|
280
|
-
function configured() {}
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
#### Correct (custom configuration, flat shorthand)
|
|
284
|
-
|
|
285
|
-
```js
|
|
286
|
-
/* eslint valid-annotation-format: [
|
|
287
|
-
"error",
|
|
288
|
-
{
|
|
289
|
-
storyPathPattern: "^trace/stories/STORY-[0-9]+\\.md$",
|
|
290
|
-
storyPathExample: "trace/stories/STORY-123.md",
|
|
291
|
-
requirementIdPattern: "^SYS-[0-9]{4}$",
|
|
292
|
-
requirementIdExample: "SYS-0001"
|
|
293
|
-
}
|
|
294
|
-
] */
|
|
295
|
-
|
|
296
|
-
/**
|
|
297
|
-
* @story trace/stories/STORY-123.md
|
|
298
|
-
* @req SYS-0001
|
|
299
|
-
*/
|
|
300
|
-
function flatConfigured() {}
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
#### Incorrect
|
|
304
|
-
|
|
305
|
-
```js
|
|
306
|
-
// @story invalid-path
|
|
307
|
-
// @req REQ-1234
|
|
308
|
-
|
|
309
|
-
// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story
|
|
310
|
-
// @req invalid-req-id
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* @story
|
|
314
|
-
* @req REQ-EXAMPLE
|
|
315
|
-
*/
|
|
316
|
-
function badExample() {}
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
## Migration to `@implements`
|
|
320
|
-
|
|
321
|
-
The `@implements` annotation is designed to make multi-story integration functions easier to annotate and validate without breaking existing projects. You do **not** need to migrate existing single-story code that already uses `@story` and `@req` correctly, but you can opt in to `@implements` where it adds clarity.
|
|
322
|
-
|
|
323
|
-
### When you can stay with `@story` + `@req`
|
|
324
|
-
|
|
325
|
-
Keep using only `@story` + `@req` when:
|
|
326
|
-
|
|
327
|
-
- A function or class is tied to a **single** story file.
|
|
328
|
-
- All of its requirements live in that same story file.
|
|
329
|
-
- You are happy to treat that story as the single source of truth for that code path.
|
|
330
|
-
|
|
331
|
-
Example (no migration required):
|
|
332
|
-
|
|
333
|
-
```js
|
|
334
|
-
/**
|
|
335
|
-
* Calculate age in days since publish date.
|
|
336
|
-
* @story docs/stories/002.0-DEV-FETCH-AVAILABLE-VERSIONS.story.md
|
|
337
|
-
* @req REQ-AGE-CALC
|
|
338
|
-
*/
|
|
339
|
-
export function calculateAgeInDays(publishDate) {
|
|
340
|
-
// ...
|
|
341
|
-
}
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
### When to adopt `@implements`
|
|
345
|
-
|
|
346
|
-
Use `@implements` when:
|
|
347
|
-
|
|
348
|
-
- A function or class **implements requirements from multiple stories**.
|
|
349
|
-
- Requirements with the **same ID** are reused in more than one story.
|
|
350
|
-
- You want each requirement to be explicitly associated with the story file it comes from.
|
|
351
|
-
|
|
352
|
-
With only `@story` + `@req`, multi-story integration code either:
|
|
353
|
-
|
|
354
|
-
- Cannot be expressed cleanly at all, or
|
|
355
|
-
- Leads to confusing or incorrect deep-validation results when checked by `valid-req-reference`.
|
|
356
|
-
|
|
357
|
-
`@implements` solves this by letting you list, on a single line, the story file followed by all requirement IDs from that story that the code implements.
|
|
358
|
-
|
|
359
|
-
### Before: single-story annotations only
|
|
360
|
-
|
|
361
|
-
A typical "before" example for integration code might try to overload a single story, or avoid deep validation entirely:
|
|
362
|
-
|
|
363
|
-
```js
|
|
364
|
-
/**
|
|
365
|
-
* Apply age and security filters to rows.
|
|
366
|
-
* @story docs/stories/003.0-DEV-IDENTIFY-OUTDATED.story.md
|
|
367
|
-
* @req REQ-AGE-THRESHOLD
|
|
368
|
-
* @req REQ-OUTPUT
|
|
369
|
-
*
|
|
370
|
-
* // Implicitly also implements security checks from another story,
|
|
371
|
-
* // but there is no clear or validated link here.
|
|
372
|
-
*/
|
|
373
|
-
export async function applyFilters(rows, options) {
|
|
374
|
-
// combined behavior
|
|
375
|
-
}
|
|
376
|
-
```
|
|
377
|
-
|
|
378
|
-
This passes format validation but does **not** clearly show that some behavior comes from a second story, and deep requirement validation cannot reliably tell which story each requirement belongs to.
|
|
379
|
-
|
|
380
|
-
### After: multi-story `@implements`
|
|
381
|
-
|
|
382
|
-
With `@implements`, you keep `@story` + `@req` for the primary story (if you want), and add explicit, multi-story mappings:
|
|
383
|
-
|
|
384
|
-
```js
|
|
385
|
-
/**
|
|
386
|
-
* Apply age and security filters to rows.
|
|
387
|
-
* @story docs/stories/003.0-DEV-IDENTIFY-OUTDATED.story.md
|
|
388
|
-
* @req REQ-AGE-THRESHOLD
|
|
389
|
-
* @req REQ-OUTPUT
|
|
390
|
-
*
|
|
391
|
-
* @implements docs/stories/003.0-DEV-IDENTIFY-OUTDATED.story.md REQ-AGE-THRESHOLD REQ-OUTPUT
|
|
392
|
-
* @implements docs/stories/004.0-DEV-FILTER-VULNERABLE-VERSIONS.story.md REQ-AUDIT-CHECK REQ-SAFE-ONLY
|
|
393
|
-
*/
|
|
394
|
-
export async function applyFilters(rows, options) {
|
|
395
|
-
// combined behavior
|
|
396
|
-
}
|
|
397
|
-
```
|
|
398
|
-
|
|
399
|
-
In this form:
|
|
400
|
-
|
|
401
|
-
- `valid-annotation-format` checks that each `@implements` line uses a valid story path and requirement ID format.
|
|
402
|
-
- `valid-req-reference` (see its rule documentation) performs deep validation that every requirement ID listed after `@implements` actually exists in the referenced story file.
|
|
403
|
-
|
|
404
|
-
### Mixed usage during migration
|
|
405
|
-
|
|
406
|
-
You can introduce `@implements` **incrementally**:
|
|
407
|
-
|
|
408
|
-
1. Start from working code that already uses `@story` + `@req`.
|
|
409
|
-
2. Add `@implements` lines that group requirements by story file, without removing the original annotations.
|
|
410
|
-
3. Run ESLint with `traceability/valid-annotation-format` and `traceability/valid-req-reference` enabled to confirm there are no new violations.
|
|
411
|
-
4. Optionally, once your team is comfortable, standardize on always using `@implements` for multi-story integration functions.
|
|
412
|
-
|
|
413
|
-
Both annotation styles are fully supported:
|
|
414
|
-
|
|
415
|
-
- Single-story code can continue to use only `@story` + `@req`.
|
|
416
|
-
- Multi-story integration code should prefer `@implements` to get precise, per-story validation.
|
|
417
|
-
|
|
418
|
-
For more details on how `@implements` participates in deep requirement checking, including multi-story scenarios and requirement ID scoping, see the `valid-req-reference` rule documentation and Story `docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md`.
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
# valid-req-reference
|
|
2
|
-
|
|
3
|
-
Enforces that `@req` and `@implements` annotations reference existing requirements in story files and protects against invalid paths.
|
|
4
|
-
|
|
5
|
-
@story docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
|
|
6
|
-
@req REQ-DEEP-PARSE - Parse story files to extract requirement identifiers
|
|
7
|
-
@req REQ-DEEP-MATCH - Validate `@req` references against story file content
|
|
8
|
-
@req REQ-DEEP-PATH - Protect against path traversal in story paths
|
|
9
|
-
@story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
10
|
-
@req REQ-MULTI-IMPLEMENTS - Support `@implements` annotations that point to multiple story files
|
|
11
|
-
@req REQ-MULTI-PATHS - Allow each `@implements` to specify its own story path and requirement list
|
|
12
|
-
@req REQ-MULTI-UNIQUENESS - Only require requirement IDs to be unique within a single story file
|
|
13
|
-
|
|
14
|
-
## Rule Details
|
|
15
|
-
|
|
16
|
-
This rule performs deep validation of `@req` and `@implements` annotations by:
|
|
17
|
-
|
|
18
|
-
- Verifying each referenced story file exists and is within the project directory
|
|
19
|
-
- Parsing every referenced story file to extract requirement IDs (e.g., `REQ-XXX-YYY`)
|
|
20
|
-
- Ensuring each `@req` and `@implements` requirement ID matches an extracted requirement ID in the corresponding story file
|
|
21
|
-
- Reporting an `invalidPath` error for paths containing `..` or absolute paths
|
|
22
|
-
- Reporting a `reqMissing` error when the requirement ID is not found in the story file
|
|
23
|
-
- Treating requirement IDs as scoped to a story file: the same requirement ID may appear in multiple story files without error
|
|
24
|
-
|
|
25
|
-
### Interaction of `@story`/`@req` and `@implements`
|
|
26
|
-
|
|
27
|
-
- `@story` sets a default story file path for all subsequent `@req` lines in the same file (until another `@story` is encountered).
|
|
28
|
-
- Each `@req` line uses the most recent `@story` path; the rule validates that the requirement exists in that story file.
|
|
29
|
-
- Each `@implements` line is self-contained:
|
|
30
|
-
- It specifies its own story path (independent of any preceding `@story`).
|
|
31
|
-
- It may list one or more requirement IDs that are expected to exist in that specific story file.
|
|
32
|
-
- The rule validates each requirement ID from `@implements` against the requirements found in the referenced story file.
|
|
33
|
-
- Requirement IDs only need to be unique within a single story file; duplicates across different story files are allowed and validated independently.
|
|
34
|
-
|
|
35
|
-
### Options
|
|
36
|
-
|
|
37
|
-
This rule does not accept any options (schema is `[]`).
|
|
38
|
-
|
|
39
|
-
## Examples
|
|
40
|
-
|
|
41
|
-
### Correct: Single Story with `@story` and `@req`
|
|
42
|
-
|
|
43
|
-
```js
|
|
44
|
-
// @story docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
|
|
45
|
-
// @req REQ-PLUGIN-STRUCTURE
|
|
46
|
-
function initPlugin() {}
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### Correct: Multiple Stories with `@implements`
|
|
50
|
-
|
|
51
|
-
```js
|
|
52
|
-
// @implements docs/stories/001.0-DEV-PLUGIN-SETUP.story.md REQ-PLUGIN-STRUCTURE REQ-PLUGIN-INIT
|
|
53
|
-
// @implements docs/stories/002.0-DEV-PLUGIN-RUNTIME.story.md REQ-PLUGIN-RUNTIME-START REQ-PLUGIN-RUNTIME-STOP
|
|
54
|
-
function initPlugin() {}
|
|
55
|
-
|
|
56
|
-
// Same requirement ID reused in another story file is allowed:
|
|
57
|
-
// @implements docs/stories/003.0-DEV-ALT-SETUP.story.md REQ-PLUGIN-STRUCTURE
|
|
58
|
-
function altInitPlugin() {}
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### Incorrect: Missing Requirement with `@req`
|
|
62
|
-
|
|
63
|
-
```js
|
|
64
|
-
// @story docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
|
|
65
|
-
// @req REQ-NON-EXISTENT
|
|
66
|
-
function initPlugin() {}
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
### Incorrect: Missing Requirement with `@implements`
|
|
70
|
-
|
|
71
|
-
```js
|
|
72
|
-
// @implements docs/stories/001.0-DEV-PLUGIN-SETUP.story.md REQ-NON-EXISTENT
|
|
73
|
-
function initPlugin() {}
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
### Incorrect: Path Traversal
|
|
77
|
-
|
|
78
|
-
```js
|
|
79
|
-
// @story ../docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
|
|
80
|
-
// @req REQ-PLUGIN-STRUCTURE
|
|
81
|
-
function initPlugin() {}
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
```js
|
|
85
|
-
// @implements ../docs/stories/001.0-DEV-PLUGIN-SETUP.story.md REQ-PLUGIN-STRUCTURE
|
|
86
|
-
function initPlugin() {}
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
### Incorrect: Absolute Path
|
|
90
|
-
|
|
91
|
-
```js
|
|
92
|
-
// @story /absolute/path/docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
|
|
93
|
-
// @req REQ-PLUGIN-STRUCTURE
|
|
94
|
-
function initPlugin() {}
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
```js
|
|
98
|
-
// @implements /absolute/path/docs/stories/001.0-DEV-PLUGIN-SETUP.story.md REQ-PLUGIN-STRUCTURE
|
|
99
|
-
function initPlugin() {}
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
### Migration and multi-story usage
|
|
103
|
-
|
|
104
|
-
The `valid-req-reference` rule is fully backward compatible with projects that only use `@story` and `@req`. You can keep your existing deep-validation configuration and gradually adopt `@implements` where it adds clarity.
|
|
105
|
-
|
|
106
|
-
#### Before: deep validation with a single story
|
|
107
|
-
|
|
108
|
-
In many codebases, deep requirement validation starts with a single story per function:
|
|
109
|
-
|
|
110
|
-
```js
|
|
111
|
-
// @story docs/stories/003.0-DEV-IDENTIFY-OUTDATED.story.md
|
|
112
|
-
// @req REQ-AGE-THRESHOLD
|
|
113
|
-
// @req REQ-OUTPUT
|
|
114
|
-
export async function applyFilters(rows, options) {
|
|
115
|
-
// combined behavior
|
|
116
|
-
}
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
`valid-req-reference` resolves the story file, parses its requirement IDs, and verifies that both `REQ-AGE-THRESHOLD` and `REQ-OUTPUT` exist in that file.
|
|
120
|
-
|
|
121
|
-
#### After: multi-story deep validation with `@implements`
|
|
122
|
-
|
|
123
|
-
When the same function genuinely implements requirements from multiple stories, prefer `@implements` to make that relationship explicit:
|
|
124
|
-
|
|
125
|
-
```js
|
|
126
|
-
/**
|
|
127
|
-
* Apply age and security filters to rows.
|
|
128
|
-
* @story docs/stories/003.0-DEV-IDENTIFY-OUTDATED.story.md
|
|
129
|
-
* @req REQ-AGE-THRESHOLD
|
|
130
|
-
* @req REQ-OUTPUT
|
|
131
|
-
*
|
|
132
|
-
* @implements docs/stories/003.0-DEV-IDENTIFY-OUTDATED.story.md REQ-AGE-THRESHOLD REQ-OUTPUT
|
|
133
|
-
* @implements docs/stories/004.0-DEV-FILTER-VULNERABLE-VERSIONS.story.md REQ-AUDIT-CHECK REQ-SAFE-ONLY
|
|
134
|
-
*/
|
|
135
|
-
export async function applyFilters(rows, options) {
|
|
136
|
-
// combined behavior
|
|
137
|
-
}
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
In this form:
|
|
141
|
-
|
|
142
|
-
- Each `@implements` line is self-contained: it specifies the story file and the list of requirements implemented from that story.
|
|
143
|
-
- `valid-req-reference` validates every requirement ID listed after `@implements` against the corresponding story file, using the same parsing and caching logic as for `@req`.
|
|
144
|
-
- Requirement IDs only need to be unique within a single story file; you can safely reuse IDs like `REQ-SHARED-ID` in multiple stories and reference each one via its own `@implements` line.
|
|
145
|
-
|
|
146
|
-
You can mix `@story`/`@req` and `@implements` in the same file during migration. Start from working `@story`/`@req` annotations, add `@implements` lines for multi-story integration functions, and run ESLint with both `traceability/valid-annotation-format` and `traceability/valid-req-reference` enabled to confirm there are no new violations.
|
|
147
|
-
|
|
148
|
-
For more background and examples, see Story `docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md`.
|
|
149
|
-
|
|
150
|
-
For more details, see the stories:
|
|
151
|
-
|
|
152
|
-
- docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
|
|
153
|
-
- docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
# valid-story-reference
|
|
2
|
-
|
|
3
|
-
Validates that `@story` annotation references refer to existing `.story.md` files within the project and prevents invalid path usage.
|
|
4
|
-
|
|
5
|
-
@story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
|
|
6
|
-
@req REQ-FILE-EXISTENCE - Verify that `@story` file paths reference existing files
|
|
7
|
-
@req REQ-PATH-RESOLUTION - Resolve relative paths correctly and enforce configuration
|
|
8
|
-
@req REQ-SECURITY-VALIDATION - Prevent path traversal and absolute path usage
|
|
9
|
-
|
|
10
|
-
## Rule Details
|
|
11
|
-
|
|
12
|
-
This rule inspects all comment nodes for lines starting with `@story`. It then:
|
|
13
|
-
|
|
14
|
-
- Prevents absolute paths unless explicitly allowed
|
|
15
|
-
- Prevents path traversal outside the project directory
|
|
16
|
-
- Ensures `.story.md` extension when required
|
|
17
|
-
- Resolves candidate file locations in configured story directories
|
|
18
|
-
- Reports a `fileMissing`, `invalidExtension`, or `invalidPath` error for violations
|
|
19
|
-
|
|
20
|
-
### Boundary & Configuration
|
|
21
|
-
|
|
22
|
-
The rule enforces a strict “project boundary” for story references in line with story `006.0-DEV-FILE-VALIDATION`:
|
|
23
|
-
|
|
24
|
-
- All candidate filesystem paths are built (from `@story` text, `storyDirectories`, and allowed absolute paths) and then checked against the project root using a `ProjectBoundaryCheckResult`.
|
|
25
|
-
- A candidate that the boundary checker classifies as outside the project root is treated as out-of-bounds and is not used for existence/access decisions.
|
|
26
|
-
- If **all** resolved candidates are out-of-bounds, the rule reports `invalidPath` even if filesystem queries (were they to be run) would indicate that a file exists at one or more of those locations.
|
|
27
|
-
- When **at least one** candidate path is within the project boundary:
|
|
28
|
-
- Normal extension rules are applied first (e.g. `.story.md` when `requireStoryExtension` is `true`), and mismatches are reported as `invalidExtension`.
|
|
29
|
-
- For in-bounds candidates with acceptable extensions, the rule performs filesystem checks and distinguishes between:
|
|
30
|
-
- `fileMissing` – the candidate is within the boundary and has a valid extension, but no file exists at that path.
|
|
31
|
-
- `fileAccessError` – the candidate is within the boundary, but the filesystem check fails for reasons other than “file not found” (e.g. permission errors).
|
|
32
|
-
- Success – the file exists and is accessible within the project boundary.
|
|
33
|
-
- By default, absolute paths are considered outside the project boundary and are rejected as `invalidPath` by the boundary checker unless explicitly allowed by configuration; when allowed, they are still subject to the same boundary classification and existence/access logic as relative candidates.
|
|
34
|
-
- Resolution is constrained to candidates that remain within the project root according to `ProjectBoundaryCheckResult`; candidates that would escape the project (e.g. via `..` traversal) are classified as out-of-bounds and contribute to `invalidPath` when no in-bounds alternatives exist.
|
|
35
|
-
|
|
36
|
-
These constraints ensure that story references are deterministic, prevent accidental coupling to files outside the intended documentation area, and avoid security issues from arbitrary filesystem traversal.
|
|
37
|
-
|
|
38
|
-
### Options
|
|
39
|
-
|
|
40
|
-
Configure rule behavior using an options object that controls how paths are resolved and validated against the project boundary:
|
|
41
|
-
|
|
42
|
-
```json
|
|
43
|
-
{
|
|
44
|
-
"storyDirectories": ["docs/stories", "stories"],
|
|
45
|
-
"allowAbsolutePaths": false,
|
|
46
|
-
"requireStoryExtension": true
|
|
47
|
-
}
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
#### `storyDirectories`
|
|
51
|
-
|
|
52
|
-
- Type: `string[]`
|
|
53
|
-
- Default: `["docs/stories", "stories"]` (may vary by project)
|
|
54
|
-
|
|
55
|
-
Defines the set of directories (relative to the project root) that are considered valid locations for `.story.md` files.
|
|
56
|
-
|
|
57
|
-
Behavior and interaction with boundaries:
|
|
58
|
-
|
|
59
|
-
- All relative `@story` paths are first normalized and then resolved **within** these directories.
|
|
60
|
-
- The rule will only look for matching files under these directories; files with the same name outside them are ignored.
|
|
61
|
-
- A path that cannot be mapped into any configured `storyDirectories` without leaving the project boundary is reported as `invalidPath`.
|
|
62
|
-
- If a normalized path is inside a `storyDirectories` entry but the target file does not exist, the rule reports `fileMissing`.
|
|
63
|
-
|
|
64
|
-
Use this to centrally control where story files are allowed to live (e.g. `docs/stories`, `stories/api`).
|
|
65
|
-
|
|
66
|
-
#### `allowAbsolutePaths`
|
|
67
|
-
|
|
68
|
-
- Type: `boolean`
|
|
69
|
-
- Default: `false`
|
|
70
|
-
|
|
71
|
-
Controls whether `@story` can use absolute filesystem paths (e.g. `/full/path/to/story.story.md`).
|
|
72
|
-
|
|
73
|
-
Boundary and security implications:
|
|
74
|
-
|
|
75
|
-
- When `false`:
|
|
76
|
-
- Any absolute path is reported as `invalidPath`.
|
|
77
|
-
- All `@story` references must be relative to the project and are resolved only within `storyDirectories`.
|
|
78
|
-
- This is the recommended setting to prevent references to files outside the repository or project boundary.
|
|
79
|
-
- When `true`:
|
|
80
|
-
- Absolute paths are permitted and resolved directly on the filesystem.
|
|
81
|
-
- Other checks still apply:
|
|
82
|
-
- Path traversal checks still apply (e.g. disallowing obvious escape patterns if the project sets such constraints).
|
|
83
|
-
- `requireStoryExtension` still controls which extensions are accepted.
|
|
84
|
-
- Use with caution, as it weakens project-boundary guarantees and may introduce portability issues across machines.
|
|
85
|
-
|
|
86
|
-
#### `requireStoryExtension`
|
|
87
|
-
|
|
88
|
-
- Type: `boolean`
|
|
89
|
-
- Default: `true`
|
|
90
|
-
|
|
91
|
-
Controls whether `@story` references must end with the `.story.md` extension.
|
|
92
|
-
|
|
93
|
-
Extension and boundary interaction:
|
|
94
|
-
|
|
95
|
-
- When `true`:
|
|
96
|
-
- Only files with a `.story.md` suffix are considered valid story files.
|
|
97
|
-
- If a referenced path resolves to a file with the wrong extension (e.g. `.md` without `.story`), the rule reports `invalidExtension`.
|
|
98
|
-
- This applies regardless of whether the file exists within the project boundary; a mismatched extension is always flagged as `invalidExtension` (even if the underlying file exists).
|
|
99
|
-
- When `false`:
|
|
100
|
-
- Other markdown extensions (e.g. `.md`, `.markdown`) may be allowed, subject to the rule’s implementation.
|
|
101
|
-
- Path and boundary checks still apply: the file must still be within `storyDirectories` (or be a permitted absolute path), and traversal outside the project boundary is still rejected as `invalidPath`.
|
|
102
|
-
|
|
103
|
-
Use `requireStoryExtension: true` to enforce a clear, consistent naming convention for story files and to make it unambiguous which markdown files are intended to be executable stories.
|
|
104
|
-
|
|
105
|
-
### Examples
|
|
106
|
-
|
|
107
|
-
#### Correct
|
|
108
|
-
|
|
109
|
-
```js
|
|
110
|
-
// @story docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
#### Incorrect
|
|
114
|
-
|
|
115
|
-
```js
|
|
116
|
-
// @story docs/stories/nonexistent.story.md // @story fileMissing
|
|
117
|
-
// @story docs/stories/001.0-DEV-PLUGIN-SETUP.md // @story invalidExtension
|
|
118
|
-
// @story ../outside.story.md // @story invalidPath
|
|
119
|
-
// @story /etc/passwd.story.md // @story invalidPath
|
|
120
|
-
```
|