@redocly/recheck 0.1.0

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 (182) hide show
  1. package/README.md +544 -0
  2. package/dist/assertions/bullet-style.d.ts +3 -0
  3. package/dist/assertions/bullet-style.d.ts.map +1 -0
  4. package/dist/assertions/bullet-style.js +60 -0
  5. package/dist/assertions/bullet-style.js.map +1 -0
  6. package/dist/assertions/index.d.ts +21 -0
  7. package/dist/assertions/index.d.ts.map +1 -0
  8. package/dist/assertions/index.js +30 -0
  9. package/dist/assertions/index.js.map +1 -0
  10. package/dist/assertions/max-image-size.d.ts +3 -0
  11. package/dist/assertions/max-image-size.d.ts.map +1 -0
  12. package/dist/assertions/max-image-size.js +73 -0
  13. package/dist/assertions/max-image-size.js.map +1 -0
  14. package/dist/assertions/max-line-length.d.ts +3 -0
  15. package/dist/assertions/max-line-length.d.ts.map +1 -0
  16. package/dist/assertions/max-line-length.js +68 -0
  17. package/dist/assertions/max-line-length.js.map +1 -0
  18. package/dist/assertions/no-broken-fragment-links.d.ts +3 -0
  19. package/dist/assertions/no-broken-fragment-links.d.ts.map +1 -0
  20. package/dist/assertions/no-broken-fragment-links.js +79 -0
  21. package/dist/assertions/no-broken-fragment-links.js.map +1 -0
  22. package/dist/assertions/no-duplicate-headings.d.ts +3 -0
  23. package/dist/assertions/no-duplicate-headings.d.ts.map +1 -0
  24. package/dist/assertions/no-duplicate-headings.js +66 -0
  25. package/dist/assertions/no-duplicate-headings.js.map +1 -0
  26. package/dist/assertions/no-hard-tabs.d.ts +3 -0
  27. package/dist/assertions/no-hard-tabs.d.ts.map +1 -0
  28. package/dist/assertions/no-hard-tabs.js +63 -0
  29. package/dist/assertions/no-hard-tabs.js.map +1 -0
  30. package/dist/assertions/no-trailing-spaces.d.ts +3 -0
  31. package/dist/assertions/no-trailing-spaces.d.ts.map +1 -0
  32. package/dist/assertions/no-trailing-spaces.js +72 -0
  33. package/dist/assertions/no-trailing-spaces.js.map +1 -0
  34. package/dist/assertions/pattern.d.ts +3 -0
  35. package/dist/assertions/pattern.d.ts.map +1 -0
  36. package/dist/assertions/pattern.js +39 -0
  37. package/dist/assertions/pattern.js.map +1 -0
  38. package/dist/assertions/semantic-line-breaks.d.ts +3 -0
  39. package/dist/assertions/semantic-line-breaks.d.ts.map +1 -0
  40. package/dist/assertions/semantic-line-breaks.js +152 -0
  41. package/dist/assertions/semantic-line-breaks.js.map +1 -0
  42. package/dist/assertions/swap.d.ts +3 -0
  43. package/dist/assertions/swap.d.ts.map +1 -0
  44. package/dist/assertions/swap.js +39 -0
  45. package/dist/assertions/swap.js.map +1 -0
  46. package/dist/assertions/utils.d.ts +8 -0
  47. package/dist/assertions/utils.d.ts.map +1 -0
  48. package/dist/assertions/utils.js +57 -0
  49. package/dist/assertions/utils.js.map +1 -0
  50. package/dist/cli.d.ts +3 -0
  51. package/dist/cli.d.ts.map +1 -0
  52. package/dist/cli.js +125 -0
  53. package/dist/cli.js.map +1 -0
  54. package/dist/commands/run.d.ts +20 -0
  55. package/dist/commands/run.d.ts.map +1 -0
  56. package/dist/commands/run.js +115 -0
  57. package/dist/commands/run.js.map +1 -0
  58. package/dist/commands/validate.d.ts +8 -0
  59. package/dist/commands/validate.d.ts.map +1 -0
  60. package/dist/commands/validate.js +43 -0
  61. package/dist/commands/validate.js.map +1 -0
  62. package/dist/config/load.d.ts +20 -0
  63. package/dist/config/load.d.ts.map +1 -0
  64. package/dist/config/load.js +85 -0
  65. package/dist/config/load.js.map +1 -0
  66. package/dist/config/schema.d.ts +109 -0
  67. package/dist/config/schema.d.ts.map +1 -0
  68. package/dist/config/schema.js +103 -0
  69. package/dist/config/schema.js.map +1 -0
  70. package/dist/config/validate.d.ts +10 -0
  71. package/dist/config/validate.d.ts.map +1 -0
  72. package/dist/config/validate.js +129 -0
  73. package/dist/config/validate.js.map +1 -0
  74. package/dist/core/auto-fix.d.ts +16 -0
  75. package/dist/core/auto-fix.d.ts.map +1 -0
  76. package/dist/core/auto-fix.js +82 -0
  77. package/dist/core/auto-fix.js.map +1 -0
  78. package/dist/core/files.d.ts +6 -0
  79. package/dist/core/files.d.ts.map +1 -0
  80. package/dist/core/files.js +75 -0
  81. package/dist/core/files.js.map +1 -0
  82. package/dist/core/rule-filters.d.ts +28 -0
  83. package/dist/core/rule-filters.d.ts.map +1 -0
  84. package/dist/core/rule-filters.js +54 -0
  85. package/dist/core/rule-filters.js.map +1 -0
  86. package/dist/core/runner.d.ts +4 -0
  87. package/dist/core/runner.d.ts.map +1 -0
  88. package/dist/core/runner.js +135 -0
  89. package/dist/core/runner.js.map +1 -0
  90. package/dist/core/scope-parser.d.ts +26 -0
  91. package/dist/core/scope-parser.d.ts.map +1 -0
  92. package/dist/core/scope-parser.js +110 -0
  93. package/dist/core/scope-parser.js.map +1 -0
  94. package/dist/core/timing.d.ts +24 -0
  95. package/dist/core/timing.d.ts.map +1 -0
  96. package/dist/core/timing.js +39 -0
  97. package/dist/core/timing.js.map +1 -0
  98. package/dist/files.d.ts +2 -0
  99. package/dist/files.d.ts.map +1 -0
  100. package/dist/files.js +39 -0
  101. package/dist/files.js.map +1 -0
  102. package/dist/load-config.d.ts +25 -0
  103. package/dist/load-config.d.ts.map +1 -0
  104. package/dist/load-config.js +104 -0
  105. package/dist/load-config.js.map +1 -0
  106. package/dist/load.d.ts +25 -0
  107. package/dist/load.d.ts.map +1 -0
  108. package/dist/load.js +112 -0
  109. package/dist/load.js.map +1 -0
  110. package/dist/reporter/fixes.d.ts +3 -0
  111. package/dist/reporter/fixes.d.ts.map +1 -0
  112. package/dist/reporter/fixes.js +20 -0
  113. package/dist/reporter/fixes.js.map +1 -0
  114. package/dist/reporter/formats/github-actions.d.ts +6 -0
  115. package/dist/reporter/formats/github-actions.d.ts.map +1 -0
  116. package/dist/reporter/formats/github-actions.js +20 -0
  117. package/dist/reporter/formats/github-actions.js.map +1 -0
  118. package/dist/reporter/formats/json.d.ts +6 -0
  119. package/dist/reporter/formats/json.d.ts.map +1 -0
  120. package/dist/reporter/formats/json.js +24 -0
  121. package/dist/reporter/formats/json.js.map +1 -0
  122. package/dist/reporter/formats/sarif.d.ts +10 -0
  123. package/dist/reporter/formats/sarif.d.ts.map +1 -0
  124. package/dist/reporter/formats/sarif.js +66 -0
  125. package/dist/reporter/formats/sarif.js.map +1 -0
  126. package/dist/reporter/formats/table.d.ts +6 -0
  127. package/dist/reporter/formats/table.d.ts.map +1 -0
  128. package/dist/reporter/formats/table.js +38 -0
  129. package/dist/reporter/formats/table.js.map +1 -0
  130. package/dist/reporter/index.d.ts +10 -0
  131. package/dist/reporter/index.d.ts.map +1 -0
  132. package/dist/reporter/index.js +42 -0
  133. package/dist/reporter/index.js.map +1 -0
  134. package/dist/reporter/prioritize-problems.d.ts +7 -0
  135. package/dist/reporter/prioritize-problems.d.ts.map +1 -0
  136. package/dist/reporter/prioritize-problems.js +29 -0
  137. package/dist/reporter/prioritize-problems.js.map +1 -0
  138. package/dist/reporter/statistics.d.ts +17 -0
  139. package/dist/reporter/statistics.d.ts.map +1 -0
  140. package/dist/reporter/statistics.js +52 -0
  141. package/dist/reporter/statistics.js.map +1 -0
  142. package/dist/reporter/summary.d.ts +10 -0
  143. package/dist/reporter/summary.d.ts.map +1 -0
  144. package/dist/reporter/summary.js +48 -0
  145. package/dist/reporter/summary.js.map +1 -0
  146. package/dist/scope.d.ts +26 -0
  147. package/dist/scope.d.ts.map +1 -0
  148. package/dist/scope.js +110 -0
  149. package/dist/scope.js.map +1 -0
  150. package/dist/types/assertions.d.ts +56 -0
  151. package/dist/types/assertions.d.ts.map +1 -0
  152. package/dist/types/assertions.js +2 -0
  153. package/dist/types/assertions.js.map +1 -0
  154. package/dist/types/index.d.ts +6 -0
  155. package/dist/types/index.d.ts.map +1 -0
  156. package/dist/types/index.js +6 -0
  157. package/dist/types/index.js.map +1 -0
  158. package/dist/types/problems.d.ts +21 -0
  159. package/dist/types/problems.d.ts.map +1 -0
  160. package/dist/types/problems.js +2 -0
  161. package/dist/types/problems.js.map +1 -0
  162. package/dist/types/reporting.d.ts +23 -0
  163. package/dist/types/reporting.d.ts.map +1 -0
  164. package/dist/types/reporting.js +2 -0
  165. package/dist/types/reporting.js.map +1 -0
  166. package/dist/types/rules.d.ts +43 -0
  167. package/dist/types/rules.d.ts.map +1 -0
  168. package/dist/types/rules.js +2 -0
  169. package/dist/types/rules.js.map +1 -0
  170. package/dist/types/validation.d.ts +6 -0
  171. package/dist/types/validation.d.ts.map +1 -0
  172. package/dist/types/validation.js +2 -0
  173. package/dist/types/validation.js.map +1 -0
  174. package/dist/types.d.ts +109 -0
  175. package/dist/types.d.ts.map +1 -0
  176. package/dist/types.js +2 -0
  177. package/dist/types.js.map +1 -0
  178. package/dist/validate.d.ts +31 -0
  179. package/dist/validate.d.ts.map +1 -0
  180. package/dist/validate.js +154 -0
  181. package/dist/validate.js.map +1 -0
  182. package/package.json +43 -0
package/README.md ADDED
@@ -0,0 +1,544 @@
1
+ # Recheck - Content Linting Tool
2
+
3
+ A modern, production-ready content linter for markdown files with configurable rules and built-in content quality checks.
4
+
5
+ ## Features
6
+
7
+ ✅ **Modern Scope-Based Architecture**
8
+ - File-first processing with semantic scope parsing
9
+ - Support for `sentence`, `paragraph`, `heading`, `code`, `default`, `raw`, and `all` scopes
10
+ - Vale-compatible scope notation (e.g., `heading.h1`, `heading.h2`)
11
+ - Efficient rule indexing for fast processing at scale
12
+
13
+ ✅ **Flexible Configuration Format**
14
+ - Modern `assertions`-based rule definitions
15
+ - `severity` levels: `off`, `info`, `warn`, `error`
16
+ - Array-based scope targeting for precise control
17
+ - Comprehensive JSON Schema validation
18
+
19
+ ✅ **Production-Ready Engine**
20
+ - High-performance JavaScript engine optimized for large repositories
21
+ - Successfully processes 300+ files with 1,000+ issues efficiently
22
+ - Built-in rules for common content quality checks
23
+ - Safe auto-fix capabilities for appropriate rules
24
+
25
+ ✅ **Developer-Friendly CLI**
26
+ - Table, JSON, SARIF, and GitHub Actions output formats for CI/CD integration
27
+ - Universal output-path option for file export
28
+ - Inline PR annotations with GitHub Actions format
29
+ - Severity filtering and detailed statistics
30
+ - Auto-fix with granular control
31
+ - Comprehensive error reporting
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ # Navigate to the recheck package
37
+ cd packages/recheck
38
+
39
+ # Install dependencies
40
+ pnpm install
41
+
42
+ # Build the project
43
+ pnpm build
44
+ ```
45
+
46
+ ## Usage
47
+
48
+ ### Validate Configuration
49
+
50
+ ```bash
51
+ # Validate with explicit config file
52
+ node dist/cli.js validate --config recheck.example.yaml
53
+
54
+ # Auto-discover config file in current directory
55
+ node dist/cli.js validate
56
+ ```
57
+
58
+ ### Run content linting
59
+
60
+ ```bash
61
+ # Run on current directory
62
+ node dist/cli.js run . --config recheck.example.yaml
63
+
64
+ # Run on specific file
65
+ node dist/cli.js run README.md --config recheck.example.yaml
66
+
67
+ # Filter by severity (only show errors)
68
+ node dist/cli.js run . --severity error
69
+
70
+ # Show all enabled rules (info and above)
71
+ node dist/cli.js run . --severity info
72
+
73
+ # Output formats (table is default)
74
+ node dist/cli.js run . --output table # Human-readable table (default)
75
+ node dist/cli.js run . --output json # Structured JSON for CI
76
+ node dist/cli.js run . --output sarif # SARIF format for security tools
77
+ node dist/cli.js run . --output github-actions # GitHub Actions annotations (inline PR comments)
78
+
79
+ # Show detailed statistics
80
+ node dist/cli.js run . --stats
81
+
82
+ # Auto-fix safe issues (trailing spaces, bullet style, hard tabs)
83
+ node dist/cli.js run . --fix
84
+
85
+ # Combine auto-fix with statistics
86
+ node dist/cli.js run . --fix --stats
87
+
88
+ # Limit annotations for CI (applies to file output, default: 20)
89
+ node dist/cli.js run . --annotations-limit 50
90
+
91
+ # Output to file (works with all formats)
92
+ node dist/cli.js run . --output json --output-path report.json
93
+ node dist/cli.js run . --output sarif --output-path recheck.sarif
94
+ node dist/cli.js run . --output json --output-path limited.json --annotations-limit 50
95
+
96
+ # Emit run summary to a file (json or text)
97
+ node dist/cli.js run . --summary json --summary-path recheck-summary.json
98
+
99
+ # Scan only changed files (via file list or stdin)
100
+ # From file:
101
+ node dist/cli.js run . --changed-only --changed-list changed.txt
102
+ # Or with stdin:
103
+ git diff --name-only origin/main... | node dist/cli.js run . --changed-only
104
+ ```
105
+
106
+ ## Configuration Format
107
+
108
+ Configuration uses a modern `assertions`-based format with `severity` levels and flexible scope targeting:
109
+
110
+ ```yaml
111
+ recheck/us-spelling:
112
+ scope: all # default
113
+ severity: error
114
+ message: 'Use the US spelling "%s" instead of British "%s".'
115
+ link: https://docs.microsoft.com/en-us/style-guide/word-choice/use-us-spelling-avoid-non-english-words
116
+ appliesTo:
117
+ - "docs/**" # Only apply to documentation
118
+ assertions:
119
+ swap:
120
+ ignoreCase: true
121
+ wordBoundary: true
122
+ pairs:
123
+ color: colour
124
+ behavior: behaviour
125
+ organize: organise
126
+ exceptions:
127
+ files: [docs/style-guide.md]
128
+ lines:
129
+ - "British spellings such as 'color'"
130
+
131
+ recheck/no-gerund-headings:
132
+ severity: error
133
+ scope:
134
+ - heading.h1
135
+ - heading.h2
136
+ - heading.h3
137
+ message: 'Do not start headings with a gerund.'
138
+ excludes:
139
+ - "**/drafts/**" # Exclude draft documents
140
+ assertions:
141
+ pattern:
142
+ ignoreCase: true
143
+ tokens:
144
+ - '^\\w*ing.*'
145
+
146
+ recheck/config-line-length:
147
+ severity: error
148
+ message: 'Config docs: keep lines under %s characters.'
149
+ appliesTo:
150
+ - "docs/config/**" # Only apply to config documentation
151
+ assertions:
152
+ max-line-length:
153
+ max: 100
154
+ ignoreCodeBlocks: true
155
+
156
+ recheck/bullet-style-dash:
157
+ severity: error
158
+ message: "Use '-' for unordered list bullets."
159
+ autoFixable: true # Safe for auto-fix
160
+ excludes:
161
+ - "**/examples/**" # Allow mixed styles in examples
162
+ assertions:
163
+ bullet-style:
164
+ style: '-'
165
+ normalizeNested: true
166
+ ```
167
+
168
+ ## Exceptions
169
+
170
+ Rules can be configured with exceptions to skip specific files or lines:
171
+
172
+ ### File Exceptions
173
+
174
+ Skip entire files using glob patterns or exact matches:
175
+
176
+ ```yaml
177
+ recheck/us-spelling:
178
+ # ... other config
179
+ exceptions:
180
+ files:
181
+ - "docs/style-guide.md" # Exact filename
182
+ - "docs/api/*.md" # Glob pattern
183
+ - "**/CHANGELOG.md" # Recursive glob
184
+ ```
185
+
186
+ **File matching supports:**
187
+ - **Basename matching**: `style-guide.md` matches any file with that name
188
+ - **Relative path matching**: `docs/style-guide.md` matches the specific path
189
+ - **Glob patterns**: `docs/*.md` matches all markdown files in docs directory
190
+
191
+ ### Line Exceptions
192
+
193
+ Skip specific lines using fragment matching:
194
+
195
+ ```yaml
196
+ recheck/no-trailing-spaces:
197
+ # ... other config
198
+ exceptions:
199
+ lines:
200
+ - "British spellings such as" # Fragment match
201
+ - "Code example:" # Beginning of line
202
+ - "// ignore-lint" # Comment-based exception
203
+ ```
204
+
205
+ **Line matching behavior:**
206
+ - **Fragment matching**: If the line contains the exception text anywhere, it's skipped
207
+ - **Case-sensitive**: `"Code Example"` does not match `"code example"`
208
+ - **Multiple patterns**: Any matching pattern will skip the line
209
+
210
+ ### Exception Examples
211
+
212
+ ```yaml
213
+ # Skip documentation style guides for spelling rules
214
+ recheck/us-spelling:
215
+ exceptions:
216
+ files: ["docs/style-guide.md", "**/*style*"]
217
+ lines: ["British spellings such as 'colour'"]
218
+
219
+ # Skip auto-generated files and code blocks
220
+ recheck/no-trailing-spaces:
221
+ exceptions:
222
+ files: ["**/generated/**", "CHANGELOG.md"]
223
+ lines: ["```", "Code example:", "// prettier-ignore"]
224
+ ```
225
+
226
+ ## Rule Types and Assertions
227
+
228
+ ### Assertion Types
229
+
230
+ Rules are defined using `assertions` that specify their behavior:
231
+
232
+ #### Swap Assertions (`swap`)
233
+ Text replacement with configurable options:
234
+ ```yaml
235
+ assertions:
236
+ swap:
237
+ ignoreCase: true
238
+ wordBoundary: true
239
+ pairs:
240
+ color: colour
241
+ behavior: behaviour
242
+ ```
243
+
244
+ #### Pattern Assertions (`pattern`)
245
+ Regex-based pattern matching:
246
+ ```yaml
247
+ assertions:
248
+ pattern:
249
+ ignoreCase: true
250
+ tokens:
251
+ - '^\\w*ing.*'
252
+ ```
253
+
254
+ #### Built-in Assertions
255
+ Predefined content checks with typed options:
256
+ - `max-line-length` - Line length enforcement
257
+ - `no-trailing-spaces` - Trailing whitespace detection ✅ **Auto-fixable**
258
+ - `bullet-style` - Unordered list bullet consistency ✅ **Auto-fixable**
259
+ - `semantic-line-breaks` - Semantic line break validation
260
+ - `no-hard-tabs` - Hard tab detection ✅ **Auto-fixable**
261
+ - `no-duplicate-headings` - Duplicate heading detection
262
+ - `no-broken-fragment-links` - Broken link fragment detection
263
+
264
+ ### Enhanced Scope Support
265
+
266
+ The `scope` field supports arrays and specific targeting:
267
+ ```yaml
268
+ scope: all # Apply to all content
269
+ scope: heading # Apply to all headings
270
+ scope: sentence # Apply to sentences only
271
+ scope: paragraph # Apply to paragraphs only
272
+ scope: code # Apply to code blocks only
273
+ scope: default # Apply to default content (excludes code/comments)
274
+ scope: raw # Apply to raw file content
275
+ scope: # Apply to specific heading levels
276
+ - heading.h1
277
+ - heading.h2
278
+ - heading.h3
279
+ ```
280
+
281
+ ### Rule Severity Levels
282
+
283
+ Rules can be configured with different severity levels:
284
+
285
+ - **`off`**: Disable the rule completely
286
+ - **`info`**: Informational messages (exit code 0)
287
+ - **`warn`**: Warning messages (exit code 0)
288
+ - **`error`**: Error messages (exit code 1)
289
+
290
+ ### Auto-Fix Safety
291
+
292
+ Only rules marked with `autoFixable: true` can be automatically corrected:
293
+
294
+ - ✅ **Safe Rules**: `no-trailing-spaces`, `bullet-style`, `no-hard-tabs`
295
+ - ❌ **Unsafe Rules**: `max-line-length`, `semantic-line-breaks`, `no-duplicate-headings`
296
+
297
+ ## GitHub Actions Integration
298
+
299
+ Recheck provides seamless GitHub Actions integration for automated content quality checking on pull requests.
300
+
301
+ ### Output Format: `github-actions`
302
+
303
+ Use the `--output github-actions` format to generate inline file annotations that appear directly on pull request files:
304
+
305
+ ```bash
306
+ # Basic GitHub Actions output
307
+ node dist/cli.js run docs --output github-actions
308
+
309
+ # With annotation limits (recommended for PR workflows)
310
+ node dist/cli.js run docs --output github-actions --annotations-limit 20
311
+ ```
312
+
313
+ ### Annotation Output
314
+
315
+ The GitHub Actions format produces annotations that GitHub automatically displays as inline comments:
316
+
317
+ ```
318
+ ::error title=recheck/no-trailing-spaces,file=docs/guide.md,line=42,col=15,endColumn=18::Remove trailing spaces.
319
+ ::warning title=recheck/bullet-style-dash,file=docs/api.md,line=23,col=1,endColumn=2::Use '-' for unordered list bullets.
320
+ ```
321
+
322
+ ### GitHub Actions Limits
323
+
324
+ GitHub Actions has strict limits on annotations:
325
+ - **10 error** and **10 warning** annotations per step
326
+ - **50 total** annotations per job
327
+
328
+ **Recommendation**: Use `--annotations-limit 20` (the default) to stay well within these limits while prioritizing the most critical issues.
329
+
330
+ ### Workflow Example
331
+
332
+ ```yaml
333
+ - name: Run recheck with inline annotations
334
+ run: |
335
+ node packages/recheck/dist/cli.js run docs \
336
+ --config recheck.yaml \
337
+ --output github-actions \
338
+ --changed-only < changed-files.txt
339
+ ```
340
+
341
+ This creates both inline file annotations AND summary comments when combined with the PR comment workflow.
342
+
343
+ ## Performance
344
+
345
+ The new architecture delivers excellent performance:
346
+
347
+ - ✅ **Small Projects**: ~15ms for single files
348
+ - ✅ **Medium Projects**: ~50-100ms for 10-50 files
349
+ - ✅ **Large Projects**: Successfully processes 300+ files with 1,000+ issues
350
+ - ✅ **Scalable**: File-first architecture with rule indexing optimizes for large repositories
351
+
352
+ ## Dependencies
353
+
354
+ ### Runtime Dependencies
355
+ - `ajv` + `ajv-formats` - JSON Schema validation
356
+ - `js-yaml` - YAML configuration parsing
357
+ - `yargs` - CLI interface
358
+ - `colorette` - Terminal colors
359
+ - `picomatch` - File pattern matching
360
+
361
+ ### Development Dependencies
362
+ - `vitest` - Modern testing framework
363
+ - `typescript` - Type checking and compilation
364
+ - `@types/*` - TypeScript definitions
365
+
366
+ ## Example Output
367
+
368
+ ### Standard Run
369
+ ```
370
+ 🏃 Running recheck on: docs/
371
+ ✅ Configuration loaded successfully!
372
+ Config file: recheck.yaml
373
+ Loaded 8 rule(s)
374
+ Disabled 1 rule(s) (severity: off)
375
+
376
+ 🔧 Running 8 rule(s)...
377
+ Found 311 markdown file(s)
378
+ Checking rule: us-spelling...
379
+ Checking rule: no-gerund-headings...
380
+ Checking rule: oxford-comma...
381
+ Checking rule: no-trailing-spaces...
382
+ Checking rule: bullet-style-dash...
383
+ Checking rule: semantic-line-breaks...
384
+ Checking rule: no-hard-tabs...
385
+ Checking rule: no-duplicate-headings...
386
+
387
+ 📋 Found 1086 issue(s):
388
+
389
+ us-spelling README.md:68:5 Use the US spelling "color" instead of British "colour".
390
+ no-trailing-spaces README.md:15:42 Remove trailing spaces.
391
+ bullet-style-dash docs/guide.md:22:1 Use '-' for unordered list bullets.
392
+
393
+ 65 error(s)
394
+ 1021 warning(s)
395
+
396
+ ❌ Found 65 error(s). Exiting with code 1.
397
+ Completed in 156ms
398
+ ```
399
+
400
+ ### JSON Output
401
+ ```json
402
+ {
403
+ "summary": {
404
+ "filesScanned": 311,
405
+ "totalIssues": 1086,
406
+ "breakdown": {
407
+ "recheck/no-trailing-spaces": {
408
+ "errors": 65,
409
+ "warnings": 0,
410
+ "info": 0,
411
+ "total": 65
412
+ },
413
+ "recheck/bullet-style-dash": {
414
+ "errors": 1021,
415
+ "warnings": 0,
416
+ "info": 0,
417
+ "total": 1021
418
+ }
419
+ }
420
+ },
421
+ "issues": [
422
+ {
423
+ "file": "../../docs/public/branding/index.md",
424
+ "line": 13,
425
+ "column": 22,
426
+ "text": "Use the brand guidelines when applying Redocly brand",
427
+ "match": " ",
428
+ "ruleName": "recheck/no-trailing-spaces",
429
+ "severity": "error",
430
+ "message": "Remove trailing spaces."
431
+ }
432
+ ]
433
+ }
434
+ ```
435
+
436
+ ## What's Working Now
437
+
438
+ - ✅ **Modern Architecture**: File-first processing with scope-based rule application
439
+ - ✅ **Vale Compatibility**: Scope notation compatible with Vale linter
440
+ - ✅ **Swap Rules**: Find and replace text patterns with word boundaries and case sensitivity
441
+ - ✅ **Pattern Rules**: Regex matching with precise scope filtering
442
+ - ✅ **Built-in Rules**: All 6 built-in rules fully implemented with comprehensive tests
443
+ - ✅ **File Discovery**: Recursive markdown file finding with common directory exclusions
444
+ - ✅ **Multiple Output Formats**: Human-readable table, structured JSON, SARIF, and GitHub Actions
445
+ - ✅ **Severity Filtering**: Show only errors, warnings, or all issues
446
+ - ✅ **Exit Codes**: Non-zero exit when errors found (perfect for CI)
447
+ - ✅ **Exception Handling**: Skip lines and files that match exception patterns
448
+ - ✅ **Auto-Fix**: Safe automatic correction for appropriate rules
449
+ - ✅ **Production Scale**: Successfully handles large documentation repositories
450
+ - ✅ **Comprehensive Testing**: Full test suite with 161 passing tests
451
+
452
+
453
+ ### Key Design Principles
454
+
455
+ - **File-First Processing**: Iterate by files, then by semantic scopes within files
456
+ - **Scope-Based Rules**: Apply rules only to relevant content scopes
457
+ - **Type Safety**: Full TypeScript coverage with strict typing
458
+ - **Scope parsing**: Efficient per-file segmentation for applying rules by scope
459
+ - **Modular Rules**: Each built-in rule in its own file with dedicated tests
460
+ - **Safe Auto-Fix**: Granular control over which rules can auto-fix
461
+ - **Vale Compatibility**: Scope notation compatible with existing Vale configurations
462
+
463
+ ## File Targeting
464
+
465
+ Rules can target specific files using path patterns with `appliesTo` and `excludes`:
466
+
467
+ ### Apply Rules to Specific Files
468
+
469
+ ```yaml
470
+ recheck/config-docs-only:
471
+ severity: error
472
+ message: "Config docs must follow specific patterns"
473
+ appliesTo:
474
+ - "docs/config/**" # All files in docs/config directory
475
+ - "**/api/*.md" # All .md files in any api directory
476
+ - "*.config.md" # Files ending with .config.md
477
+ assertions:
478
+ pattern:
479
+ tokens: ["TODO"]
480
+
481
+ recheck/api-standards:
482
+ severity: warn
483
+ message: "API docs need review"
484
+ appliesTo:
485
+ - "docs/api/**" # Target API documentation
486
+ - "**/endpoints/*.md" # Target endpoint documentation
487
+ assertions:
488
+ pattern:
489
+ tokens: ["DRAFT", "TBD"]
490
+ ```
491
+
492
+ ### Exclude Files from Rules
493
+
494
+ ```yaml
495
+ recheck/no-todos:
496
+ severity: error
497
+ message: "No TODOs allowed"
498
+ excludes:
499
+ - "docs/drafts/**" # Exclude all draft documents
500
+ - "**/temp*.md" # Exclude temporary files
501
+ - "README.md" # Exclude specific file
502
+ assertions:
503
+ pattern:
504
+ tokens: ["TODO"]
505
+ ```
506
+
507
+ ### Path Pattern Support
508
+
509
+ File targeting supports multiple pattern types:
510
+
511
+ - **Full path patterns**: `docs/config/**`, `src/components/*.md`
512
+ - **Recursive patterns**: `**/api/*.md`, `**/README.md`
513
+ - **Basename patterns**: `*.config.md`, `temp*.md` (backward compatible)
514
+ - **Exact matches**: `README.md`, `docs/guide.md`
515
+
516
+ ### Pattern Matching Strategy
517
+
518
+ The enhanced pattern matching checks patterns against:
519
+
520
+ 1. **Filename** (`config.md`) - for simple patterns
521
+ 2. **Relative path** (`docs/config/settings.md`) - for full path patterns
522
+ 3. **Path segments** (`config/settings.md`) - for partial path patterns
523
+
524
+ This allows flexible targeting while maintaining backward compatibility.
525
+
526
+ ## Contributing
527
+
528
+ Want to add new assertions or improve existing ones? Check out our **[Contributing Guide](src/assertions/CONTRIBUTING.md)** for:
529
+
530
+ - 🏗️ **Architecture overview** - How the centralized scoping system works
531
+ - 📝 **Step-by-step guide** - Create new assertions following best practices
532
+ - 🧪 **Testing guidelines** - Comprehensive test coverage examples
533
+ - ✅ **Code standards** - Follow our established conventions
534
+ - 🚀 **Quick examples** - Get started with working code templates
535
+
536
+ The guide covers our modern architecture where the **runner handles scoping automatically**, so your assertions can focus on their core logic without worrying about segment filtering or line number adjustments.
537
+
538
+ ## Future Enhancements
539
+
540
+ 1. **Plugin System**: Custom rule loading from external files/packages
541
+ 2. **Configuration Inheritance**: Config file discovery and inheritance
542
+ 3. **Watch Mode**: Real-time linting as files change
543
+ 4. **IDE Integration**: Language server protocol support
544
+ 5. **Additional Scopes**: Support for more Vale-compatible scopes
@@ -0,0 +1,3 @@
1
+ import type { RuleDefinition } from './index.js';
2
+ export declare const bulletStyle: RuleDefinition;
3
+ //# sourceMappingURL=bullet-style.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bullet-style.d.ts","sourceRoot":"","sources":["../../src/assertions/bullet-style.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAW,MAAM,YAAY,CAAC;AAmE1D,eAAO,MAAM,WAAW,EAAE,cAIzB,CAAC"}
@@ -0,0 +1,60 @@
1
+ async function execute(rule, file, { content }) {
2
+ const problems = [];
3
+ const options = rule.assertions['bullet-style'];
4
+ const expectedStyle = options.style;
5
+ const lines = content.split('\n');
6
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
7
+ const lineText = lines[lineIndex];
8
+ const lineNumber = lineIndex + 1;
9
+ const bulletMatch = lineText.match(/^(\s*)([*-])\s+/);
10
+ if (bulletMatch) {
11
+ const actualStyle = bulletMatch[2];
12
+ if (actualStyle !== expectedStyle) {
13
+ problems.push({
14
+ file,
15
+ line: lineNumber,
16
+ column: bulletMatch[1].length + 1,
17
+ text: lineText,
18
+ match: actualStyle,
19
+ ruleName: rule.name,
20
+ severity: rule.severity,
21
+ message: rule.message,
22
+ });
23
+ }
24
+ }
25
+ }
26
+ return problems;
27
+ }
28
+ async function fix(rule, file, { content }) {
29
+ const fixes = [];
30
+ const options = rule.assertions['bullet-style'];
31
+ const expectedStyle = options.style;
32
+ const lines = content.split('\n');
33
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
34
+ const lineText = lines[lineIndex];
35
+ const lineNumber = lineIndex + 1;
36
+ const bulletMatch = lineText.match(/^(\s*)([*-])(\s+)/);
37
+ if (bulletMatch) {
38
+ const [fullMatch, indentation, actualStyle, spacing] = bulletMatch;
39
+ if (actualStyle !== expectedStyle) {
40
+ const newText = indentation + expectedStyle + spacing + lineText.substring(fullMatch.length);
41
+ fixes.push({
42
+ file,
43
+ line: lineNumber,
44
+ column: 1,
45
+ oldText: lineText,
46
+ newText,
47
+ ruleName: rule.name,
48
+ description: `Change bullet style from '${actualStyle}' to '${expectedStyle}'`,
49
+ });
50
+ }
51
+ }
52
+ }
53
+ return fixes;
54
+ }
55
+ export const bulletStyle = {
56
+ id: 'bullet-style',
57
+ execute,
58
+ fix,
59
+ };
60
+ //# sourceMappingURL=bullet-style.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bullet-style.js","sourceRoot":"","sources":["../../src/assertions/bullet-style.ts"],"names":[],"mappings":"AAGA,KAAK,UAAU,OAAO,CACpB,IAAoB,EACpB,IAAY,EACZ,EAAE,OAAO,EAAW;IAEpB,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,cAAc,CAAyB,CAAC;IACxE,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC;IAEpC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,KAAK,IAAI,SAAS,GAAG,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,CAAC;QAC9D,MAAM,QAAQ,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC;QAClC,MAAM,UAAU,GAAG,SAAS,GAAG,CAAC,CAAC;QACjC,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACtD,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,WAAW,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;YACnC,IAAI,WAAW,KAAK,aAAa,EAAE,CAAC;gBAClC,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI;oBACJ,IAAI,EAAE,UAAU;oBAChB,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC;oBACjC,IAAI,EAAE,QAAQ;oBACd,KAAK,EAAE,WAAW;oBAClB,QAAQ,EAAE,IAAI,CAAC,IAAI;oBACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,OAAO,EAAE,IAAI,CAAC,OAAO;iBACtB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,KAAK,UAAU,GAAG,CAAC,IAAoB,EAAE,IAAY,EAAE,EAAE,OAAO,EAAW;IACzE,MAAM,KAAK,GAAU,EAAE,CAAC;IACxB,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,cAAc,CAAyB,CAAC;IACxE,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC;IAEpC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,KAAK,IAAI,SAAS,GAAG,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,CAAC;QAC9D,MAAM,QAAQ,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC;QAClC,MAAM,UAAU,GAAG,SAAS,GAAG,CAAC,CAAC;QACjC,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACxD,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,CAAC,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,CAAC,GAAG,WAAW,CAAC;YACnE,IAAI,WAAW,KAAK,aAAa,EAAE,CAAC;gBAClC,MAAM,OAAO,GACX,WAAW,GAAG,aAAa,GAAG,OAAO,GAAG,QAAQ,CAAC,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;gBAC/E,KAAK,CAAC,IAAI,CAAC;oBACT,IAAI;oBACJ,IAAI,EAAE,UAAU;oBAChB,MAAM,EAAE,CAAC;oBACT,OAAO,EAAE,QAAQ;oBACjB,OAAO;oBACP,QAAQ,EAAE,IAAI,CAAC,IAAI;oBACnB,WAAW,EAAE,6BAA6B,WAAW,SAAS,aAAa,GAAG;iBAC/E,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAmB;IACzC,EAAE,EAAE,cAAc;IAClB,OAAO;IACP,GAAG;CACJ,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { NormalizedRule, Problem, Fix } from '../types/index.js';
2
+ import type { ScopedSegment } from '../core/scope-parser.js';
3
+ export interface Context {
4
+ content: string;
5
+ segments: ScopedSegment[];
6
+ fileMetadata?: {
7
+ images: Map<string, {
8
+ path: string;
9
+ size: number;
10
+ exists: boolean;
11
+ }>;
12
+ };
13
+ }
14
+ export interface RuleDefinition {
15
+ id: string;
16
+ execute: (rule: NormalizedRule, file: string, { content, segments }: Context) => Promise<Problem[]>;
17
+ fix?: (rule: NormalizedRule, file: string, { content, segments }: Context) => Promise<Fix[]>;
18
+ }
19
+ export declare const assertionRules: Record<string, RuleDefinition>;
20
+ export declare function getAssertion(id: string): RuleDefinition;
21
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/assertions/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAa7D,MAAM,WAAW,OAAO;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,YAAY,CAAC,EAAE;QACb,MAAM,EAAE,GAAG,CACT,MAAM,EACN;YACE,IAAI,EAAE,MAAM,CAAC;YACb,IAAI,EAAE,MAAM,CAAC;YACb,MAAM,EAAE,OAAO,CAAC;SACjB,CACF,CAAC;KACH,CAAC;CACH;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,CACP,IAAI,EAAE,cAAc,EACpB,IAAI,EAAE,MAAM,EACZ,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,OAAO,KAC3B,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACxB,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,OAAO,KAAK,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;CAC9F;AAED,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAWzD,CAAC;AAEF,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,CAMvD"}
@@ -0,0 +1,30 @@
1
+ import { bulletStyle } from './bullet-style.js';
2
+ import { maxImageSize } from './max-image-size.js';
3
+ import { maxLineLength } from './max-line-length.js';
4
+ import { noDuplicateHeadings } from './no-duplicate-headings.js';
5
+ import { noHardTabs } from './no-hard-tabs.js';
6
+ import { noTrailingSpaces } from './no-trailing-spaces.js';
7
+ import { noBrokenFragmentLinks } from './no-broken-fragment-links.js';
8
+ import { pattern } from './pattern.js';
9
+ import { semanticLineBreaks } from './semantic-line-breaks.js';
10
+ import { swap } from './swap.js';
11
+ export const assertionRules = {
12
+ 'max-image-size': maxImageSize,
13
+ 'max-line-length': maxLineLength,
14
+ 'no-trailing-spaces': noTrailingSpaces,
15
+ 'bullet-style': bulletStyle,
16
+ 'semantic-line-breaks': semanticLineBreaks,
17
+ 'no-hard-tabs': noHardTabs,
18
+ 'no-duplicate-headings': noDuplicateHeadings,
19
+ 'no-broken-fragment-links': noBrokenFragmentLinks,
20
+ swap,
21
+ pattern,
22
+ };
23
+ export function getAssertion(id) {
24
+ const assertion = assertionRules[id];
25
+ if (!assertion) {
26
+ throw new Error(`Assertion not found: ${id}`);
27
+ }
28
+ return assertion;
29
+ }
30
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/assertions/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AA2BjC,MAAM,CAAC,MAAM,cAAc,GAAmC;IAC5D,gBAAgB,EAAE,YAAY;IAC9B,iBAAiB,EAAE,aAAa;IAChC,oBAAoB,EAAE,gBAAgB;IACtC,cAAc,EAAE,WAAW;IAC3B,sBAAsB,EAAE,kBAAkB;IAC1C,cAAc,EAAE,UAAU;IAC1B,uBAAuB,EAAE,mBAAmB;IAC5C,0BAA0B,EAAE,qBAAqB;IACjD,IAAI;IACJ,OAAO;CACR,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,EAAU;IACrC,MAAM,SAAS,GAAG,cAAc,CAAC,EAAE,CAAC,CAAC;IACrC,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,wBAAwB,EAAE,EAAE,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { RuleDefinition } from './index.js';
2
+ export declare const maxImageSize: RuleDefinition;
3
+ //# sourceMappingURL=max-image-size.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"max-image-size.d.ts","sourceRoot":"","sources":["../../src/assertions/max-image-size.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAW,MAAM,YAAY,CAAC;AAoG1D,eAAO,MAAM,YAAY,EAAE,cAI1B,CAAC"}