@doccident/doccident 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +195 -3
- package/README.md +556 -6
- package/bin/cmd.js +12 -1
- package/dist/doctest.js +229 -20
- package/dist/languages/basic.js +51 -0
- package/dist/languages/c.js +169 -0
- package/dist/languages/cobol.js +55 -0
- package/dist/languages/csharp.js +77 -0
- package/dist/languages/fortran.js +165 -0
- package/dist/languages/go.js +159 -0
- package/dist/languages/interface.js +2 -0
- package/dist/languages/java.js +92 -0
- package/dist/languages/javascript.js +51 -0
- package/dist/languages/pascal.js +88 -0
- package/dist/languages/perl.js +36 -0
- package/dist/languages/python.js +48 -0
- package/dist/languages/r.js +37 -0
- package/dist/languages/rust.js +104 -0
- package/dist/languages/shell.js +46 -0
- package/dist/parse-code-snippets-from-markdown.js +137 -7
- package/dist/reporter.js +39 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -1,17 +1,42 @@
|
|
|
1
1
|
# doccident
|
|
2
2
|
|
|
3
3
|
[](http://badge.fury.io/js/doccident)
|
|
4
|
+
[](README.md)
|
|
4
5
|
|
|
5
6
|
**Test the code examples in your Markdown documentation.**
|
|
6
7
|
|
|
7
8
|
## Overview
|
|
8
9
|
|
|
9
|
-
As an open source developer, few things are more frustrating for users than encountering broken examples in a README. `doccident` ensures your documentation remains accurate by treating your examples as testable code. It parses your Markdown files, extracts
|
|
10
|
+
As an open source developer, few things are more frustrating for users than encountering broken examples in a README. `doccident` ensures your documentation remains accurate by treating your examples as testable code. It parses your Markdown files, extracts code blocks for multiple languages, and executes them in a sandboxed environment to verify they run without errors.
|
|
11
|
+
|
|
12
|
+
### The "Why"
|
|
13
|
+
|
|
14
|
+
This project was born out of frustration. The author once wrote a massive technical book, only to be haunted by the realization that subtle bugs had crept into the code examples and output listings during the editing process.
|
|
15
|
+
|
|
16
|
+
`doccident` solves this by automating the verification of your documentation. It doesn't just check if your code compiles; it can **execute** your snippets and **verify their output** matches what you claim in your docs. This "living documentation" approach guarantees that your readers always see correct, working code and accurate output.
|
|
17
|
+
|
|
18
|
+
**Supported Languages:**
|
|
19
|
+
* JavaScript (Node.js)
|
|
20
|
+
* TypeScript
|
|
21
|
+
* Python
|
|
22
|
+
* Shell (Bash, Sh, Zsh)
|
|
23
|
+
* Go
|
|
24
|
+
* Rust
|
|
25
|
+
* Fortran
|
|
26
|
+
* COBOL
|
|
27
|
+
* C
|
|
28
|
+
* BASIC
|
|
29
|
+
* Java
|
|
30
|
+
* Perl
|
|
31
|
+
* C#
|
|
32
|
+
* R
|
|
33
|
+
* Pascal
|
|
10
34
|
|
|
11
35
|
> **Note**: `doccident` primarily verifies that your code *runs* without throwing exceptions. While you can add assertions to your examples to test correctness, its main goal is to ensure your documentation examples are valid and runnable.
|
|
12
36
|
|
|
13
37
|
## Installation
|
|
14
38
|
|
|
39
|
+
<!-- skip-example -->
|
|
15
40
|
```bash
|
|
16
41
|
npm install --save-dev @doccident/doccident
|
|
17
42
|
```
|
|
@@ -20,24 +45,59 @@ npm install --save-dev @doccident/doccident
|
|
|
20
45
|
|
|
21
46
|
Run `doccident` in your project root. By default, it recursively checks all `.md` and `.markdown` files (excluding `node_modules`).
|
|
22
47
|
|
|
48
|
+
<!-- skip-example -->
|
|
23
49
|
```bash
|
|
24
50
|
npx doccident
|
|
25
51
|
```
|
|
26
52
|
|
|
27
53
|
You can also target specific files or directories:
|
|
28
54
|
|
|
55
|
+
<!-- skip-example -->
|
|
29
56
|
```bash
|
|
30
57
|
npx doccident docs/**/*.md
|
|
31
58
|
```
|
|
32
59
|
|
|
60
|
+
### Timeout
|
|
61
|
+
|
|
62
|
+
By default, each code snippet has a 5-minute (300,000ms) timeout to prevent infinite loops or hangs. You can customize this with the `--timeout` flag (in milliseconds):
|
|
63
|
+
|
|
64
|
+
<!-- skip-example -->
|
|
65
|
+
```bash
|
|
66
|
+
npx doccident --timeout 60000 # 60 second timeout
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
If a snippet exceeds the timeout, it will fail with an error message indicating the timeout was reached.
|
|
70
|
+
|
|
33
71
|
### Language Support & Recipes
|
|
34
72
|
|
|
35
|
-
`doccident` executes code inside
|
|
73
|
+
`doccident` executes code inside fenced code blocks for the following languages:
|
|
74
|
+
|
|
75
|
+
* **JavaScript**: `js`, `javascript`, `es6`
|
|
76
|
+
* **TypeScript**: `ts`, `typescript`
|
|
77
|
+
* **Python**: `py`, `python`
|
|
78
|
+
* **Shell**: `sh`, `bash`, `zsh`, `shell`
|
|
79
|
+
* **Go**: `go`
|
|
80
|
+
* **Rust**: `rust`, `rs`
|
|
81
|
+
* **Fortran**: `fortran`, `f90`, `f95`
|
|
82
|
+
* **COBOL**: `cobol`, `cob`
|
|
83
|
+
* **C**: `c`
|
|
84
|
+
* **BASIC**: `basic`
|
|
85
|
+
* **Java**: `java`
|
|
86
|
+
* **Perl**: `perl`, `pl`
|
|
87
|
+
* **C#**: `csharp`, `cs`
|
|
88
|
+
* **R**: `r`
|
|
89
|
+
* **Pascal**: `pascal`, `pas`
|
|
90
|
+
* **Text/Output**: `text`, `txt`, `output` (for output verification)
|
|
91
|
+
|
|
92
|
+
It automatically transforms modern JavaScript and TypeScript using **esbuild** before execution.
|
|
36
93
|
|
|
37
94
|
#### JavaScript
|
|
38
95
|
|
|
39
96
|
Use `js`, `javascript`, or `es6` for JavaScript examples.
|
|
40
97
|
|
|
98
|
+
**Execution Model:**
|
|
99
|
+
Runs directly in a Node.js `vm` sandbox.
|
|
100
|
+
|
|
41
101
|
**Recipe:**
|
|
42
102
|
|
|
43
103
|
1. Use `js` fenced code blocks.
|
|
@@ -53,6 +113,9 @@ Use `js`, `javascript`, or `es6` for JavaScript examples.
|
|
|
53
113
|
|
|
54
114
|
Use `ts` or `typescript` for TypeScript examples.
|
|
55
115
|
|
|
116
|
+
**Execution Model:**
|
|
117
|
+
Transpiled via `esbuild`, then runs in a Node.js `vm` sandbox.
|
|
118
|
+
|
|
56
119
|
**Recipe:**
|
|
57
120
|
|
|
58
121
|
1. Use `ts` fenced code blocks.
|
|
@@ -69,6 +132,356 @@ Use `ts` or `typescript` for TypeScript examples.
|
|
|
69
132
|
console.log(user.name);
|
|
70
133
|
```
|
|
71
134
|
|
|
135
|
+
#### Python
|
|
136
|
+
|
|
137
|
+
Use `py` or `python` for Python examples.
|
|
138
|
+
|
|
139
|
+
**Prerequisites:**
|
|
140
|
+
* `python3` must be installed and available in your system path.
|
|
141
|
+
* **macOS**: `brew install python`
|
|
142
|
+
* **Ubuntu/Debian**: `sudo apt-get install python3`
|
|
143
|
+
|
|
144
|
+
**Execution Model:**
|
|
145
|
+
Spawns a `python3` subprocess with the code piped to stdin.
|
|
146
|
+
|
|
147
|
+
**Recipe:**
|
|
148
|
+
|
|
149
|
+
1. Use `python` fenced code blocks.
|
|
150
|
+
2. Standard library imports work out of the box.
|
|
151
|
+
3. State can be shared between blocks using `<!-- share-code-between-examples -->`.
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
import json
|
|
155
|
+
data = {"key": "value"}
|
|
156
|
+
assert json.loads('{"key": "value"}') == data
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
#### Shell Scripts
|
|
160
|
+
|
|
161
|
+
Use `sh`, `bash`, `zsh` or `shell` (defaults to bash) for shell examples.
|
|
162
|
+
|
|
163
|
+
**Prerequisites:**
|
|
164
|
+
* The specified shell (`bash`, `sh`, or `zsh`) must be available in your system path.
|
|
165
|
+
|
|
166
|
+
**Execution Model:**
|
|
167
|
+
Spawns a subprocess using the specified shell, with the code piped to stdin.
|
|
168
|
+
|
|
169
|
+
**Recipe:**
|
|
170
|
+
|
|
171
|
+
1. Use specific shell tags like `bash` or `zsh` if your script relies on shell-specific syntax.
|
|
172
|
+
2. Use `sh` for POSIX-compliant scripts.
|
|
173
|
+
3. State (variables) can be shared between blocks using `<!-- share-code-between-examples -->`.
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
export MY_VAR="hello"
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
if [ "$MY_VAR" != "hello" ]; then exit 1; fi
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
#### Go
|
|
184
|
+
|
|
185
|
+
Use `go` for Go examples.
|
|
186
|
+
|
|
187
|
+
**Prerequisites:**
|
|
188
|
+
* `go` must be installed and available in your system path.
|
|
189
|
+
* **macOS**: `brew install go`
|
|
190
|
+
* **Ubuntu/Debian**: `sudo apt-get install golang-go`
|
|
191
|
+
|
|
192
|
+
**Execution Model:**
|
|
193
|
+
Writes code to a temporary file and executes it via `go run`.
|
|
194
|
+
|
|
195
|
+
**Recipe:**
|
|
196
|
+
|
|
197
|
+
1. Use `go` fenced code blocks.
|
|
198
|
+
2. You can provide a full program (including `package main`) OR a simple snippet.
|
|
199
|
+
3. Simple snippets (without `package main`) are automatically wrapped in a `main` function and include `import "fmt"`.
|
|
200
|
+
4. **Note**: `<!-- share-code-between-examples -->` is supported for Go snippets (auto-wrapped). Declarations (`func`, `type`, `import`) are extracted to top-level.
|
|
201
|
+
|
|
202
|
+
**Simple Snippet:**
|
|
203
|
+
<!-- skip-example -->
|
|
204
|
+
```go
|
|
205
|
+
fmt.Println("Hello Go")
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
#### Rust
|
|
209
|
+
|
|
210
|
+
Use `rust` or `rs` for Rust examples.
|
|
211
|
+
|
|
212
|
+
**Prerequisites:**
|
|
213
|
+
* `rustc` (Rust compiler) must be installed and available in your system path.
|
|
214
|
+
* **macOS**: `brew install rust`
|
|
215
|
+
* **Ubuntu/Debian**: `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh` or `sudo apt-get install rustc`
|
|
216
|
+
|
|
217
|
+
**Execution Model:**
|
|
218
|
+
Compiles the code using `rustc` into a temporary binary, then executes the binary.
|
|
219
|
+
|
|
220
|
+
**Recipe:**
|
|
221
|
+
|
|
222
|
+
1. Use `rust` fenced code blocks.
|
|
223
|
+
2. You can provide a full program (including `fn main()`) OR a simple snippet.
|
|
224
|
+
3. Simple snippets (without `fn main`) are automatically wrapped in a `main` function.
|
|
225
|
+
4. **Note**: `<!-- share-code-between-examples -->` is supported for Rust. Attributes like `#![...]` and `extern crate` are hoisted to the crate root. Other code is wrapped in `fn main()`.
|
|
226
|
+
|
|
227
|
+
**Simple Snippet:**
|
|
228
|
+
<!-- skip-example -->
|
|
229
|
+
```rust
|
|
230
|
+
println!("Hello Rust");
|
|
231
|
+
let x = 5;
|
|
232
|
+
assert_eq!(x, 5);
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
#### Fortran
|
|
236
|
+
|
|
237
|
+
Use `fortran`, `f90`, or `f95` for Fortran examples.
|
|
238
|
+
|
|
239
|
+
**Prerequisites:**
|
|
240
|
+
* `gfortran` must be installed and available in your system path.
|
|
241
|
+
* **macOS**: `brew install gcc` (includes gfortran)
|
|
242
|
+
* **Ubuntu/Debian**: `sudo apt-get install gfortran`
|
|
243
|
+
|
|
244
|
+
**Execution Model:**
|
|
245
|
+
Compiles the code using `gfortran` into a temporary binary, then executes the binary.
|
|
246
|
+
|
|
247
|
+
**Recipe:**
|
|
248
|
+
|
|
249
|
+
1. Use `fortran` fenced code blocks.
|
|
250
|
+
2. You can provide a full program (starting with `program name`) OR a simple snippet.
|
|
251
|
+
3. Simple snippets are automatically wrapped in a `program main ... end program main` block.
|
|
252
|
+
4. **Note**: `<!-- share-code-between-examples -->` is supported for Fortran. Modules are extracted to the top level; `use` statements are moved to the top of `program main`.
|
|
253
|
+
|
|
254
|
+
**Simple Snippet:**
|
|
255
|
+
<!-- skip-example -->
|
|
256
|
+
```fortran
|
|
257
|
+
print *, "Hello Fortran"
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
#### COBOL
|
|
261
|
+
|
|
262
|
+
Use `cobol` or `cob` for COBOL examples.
|
|
263
|
+
|
|
264
|
+
**Prerequisites:**
|
|
265
|
+
* `cobc` (GnuCOBOL) must be installed and available in your system path.
|
|
266
|
+
* **macOS**: `brew install gnucobol`
|
|
267
|
+
* **Ubuntu/Debian**: `sudo apt-get install gnucobol`
|
|
268
|
+
|
|
269
|
+
**Execution Model:**
|
|
270
|
+
Compiles the code using `cobc -x -free` into a temporary executable, then runs it.
|
|
271
|
+
|
|
272
|
+
**Recipe:**
|
|
273
|
+
|
|
274
|
+
1. Use `cobol` fenced code blocks.
|
|
275
|
+
2. Provide a full COBOL program (including `IDENTIFICATION DIVISION`).
|
|
276
|
+
3. The compiler is run in free-format mode (`-free`).
|
|
277
|
+
4. **Note**: `<!-- share-code-between-examples -->` is **not** supported for COBOL.
|
|
278
|
+
|
|
279
|
+
<!-- skip-example -->
|
|
280
|
+
```cobol
|
|
281
|
+
IDENTIFICATION DIVISION.
|
|
282
|
+
PROGRAM-ID. HELLO.
|
|
283
|
+
PROCEDURE DIVISION.
|
|
284
|
+
DISPLAY 'Hello COBOL'.
|
|
285
|
+
STOP RUN.
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
#### C
|
|
289
|
+
|
|
290
|
+
Use `c` for C examples.
|
|
291
|
+
|
|
292
|
+
**Prerequisites:**
|
|
293
|
+
* `gcc` must be installed and available in your system path.
|
|
294
|
+
* **macOS**: `xcode-select --install` or `brew install gcc`
|
|
295
|
+
* **Ubuntu/Debian**: `sudo apt-get install build-essential`
|
|
296
|
+
|
|
297
|
+
**Execution Model:**
|
|
298
|
+
Compiles the code using `gcc` into a temporary binary, then executes the binary.
|
|
299
|
+
|
|
300
|
+
**Recipe:**
|
|
301
|
+
|
|
302
|
+
1. Use `c` fenced code blocks.
|
|
303
|
+
2. You can provide a full program (including `main()`) OR a simple snippet.
|
|
304
|
+
3. Simple snippets are automatically wrapped in a `main` function and include `stdio.h`.
|
|
305
|
+
4. **Note**: `<!-- share-code-between-examples -->` is supported for C. Includes and definitions (`struct`, `func`) are hoisted; statements are placed in `main()`.
|
|
306
|
+
|
|
307
|
+
**Simple Snippet:**
|
|
308
|
+
```c
|
|
309
|
+
printf("Hello C\n");
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
#### BASIC
|
|
313
|
+
|
|
314
|
+
Use `basic` for BASIC examples.
|
|
315
|
+
|
|
316
|
+
**Prerequisites:**
|
|
317
|
+
* `cbmbasic` must be installed and available in your system path.
|
|
318
|
+
* **macOS**: `brew install cbmbasic`
|
|
319
|
+
* **Ubuntu/Debian**: Build from source (see `test.yml` for example).
|
|
320
|
+
|
|
321
|
+
**Execution Model:**
|
|
322
|
+
Spawns a `cbmbasic` subprocess with the code piped to stdin.
|
|
323
|
+
|
|
324
|
+
**Recipe:**
|
|
325
|
+
|
|
326
|
+
1. Use `basic` fenced code blocks.
|
|
327
|
+
2. Write standard Commodore BASIC V2 code.
|
|
328
|
+
3. State can be shared between blocks using `<!-- share-code-between-examples -->`.
|
|
329
|
+
|
|
330
|
+
<!-- skip-example -->
|
|
331
|
+
```basic
|
|
332
|
+
10 PRINT "HELLO BASIC"
|
|
333
|
+
20 END
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
#### Java
|
|
337
|
+
|
|
338
|
+
Use `java` for Java examples.
|
|
339
|
+
|
|
340
|
+
**Prerequisites:**
|
|
341
|
+
* `javac` and `java` must be installed and available in your system path.
|
|
342
|
+
* **macOS**: `brew install openjdk`
|
|
343
|
+
* **Ubuntu/Debian**: `sudo apt-get install default-jdk`
|
|
344
|
+
|
|
345
|
+
**Execution Model:**
|
|
346
|
+
Compiles the code using `javac` into a temporary class file in a unique directory, then executes it with `java`.
|
|
347
|
+
|
|
348
|
+
**Recipe:**
|
|
349
|
+
|
|
350
|
+
1. Use `java` fenced code blocks.
|
|
351
|
+
2. You can provide a full class (e.g. `public class MyClass { ... }`) OR a simple snippet.
|
|
352
|
+
3. Simple snippets are automatically wrapped in a `public class Main { public static void main(String[] args) { ... } }`.
|
|
353
|
+
4. **Note**: `<!-- share-code-between-examples -->` is **not** supported for Java.
|
|
354
|
+
|
|
355
|
+
**Simple Snippet:**
|
|
356
|
+
<!-- skip-example -->
|
|
357
|
+
```java
|
|
358
|
+
System.out.println("Hello Java");
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
**Full Class:**
|
|
362
|
+
<!-- skip-example -->
|
|
363
|
+
```java
|
|
364
|
+
public class Greeting {
|
|
365
|
+
public static void main(String[] args) {
|
|
366
|
+
System.out.println("Hello from Greeting class");
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
#### Perl
|
|
372
|
+
|
|
373
|
+
Use `perl` or `pl` for Perl examples.
|
|
374
|
+
|
|
375
|
+
**Prerequisites:**
|
|
376
|
+
* `perl` must be installed and available in your system path.
|
|
377
|
+
* **macOS**: Pre-installed or `brew install perl`
|
|
378
|
+
* **Ubuntu/Debian**: Pre-installed or `sudo apt-get install perl`
|
|
379
|
+
|
|
380
|
+
**Execution Model:**
|
|
381
|
+
Spawns a `perl` subprocess with the code piped to stdin.
|
|
382
|
+
|
|
383
|
+
**Recipe:**
|
|
384
|
+
|
|
385
|
+
1. Use `perl` fenced code blocks.
|
|
386
|
+
2. Standard library imports work out of the box.
|
|
387
|
+
3. State can be shared between blocks using `<!-- share-code-between-examples -->`.
|
|
388
|
+
|
|
389
|
+
```perl
|
|
390
|
+
print "Hello Perl\n";
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
#### C#
|
|
394
|
+
|
|
395
|
+
Use `csharp` or `cs` for C# examples.
|
|
396
|
+
|
|
397
|
+
**Prerequisites:**
|
|
398
|
+
* `mono` (includes `mcs` compiler) must be installed and available in your system path.
|
|
399
|
+
* **macOS**: `brew install mono`
|
|
400
|
+
* **Ubuntu/Debian**: `sudo apt-get install mono-devel`
|
|
401
|
+
|
|
402
|
+
**Execution Model:**
|
|
403
|
+
Compiles the code using `mcs` into a temporary executable, then runs it with `mono`.
|
|
404
|
+
|
|
405
|
+
**Recipe:**
|
|
406
|
+
|
|
407
|
+
1. Use `csharp` fenced code blocks.
|
|
408
|
+
2. You can provide a full class (e.g. `public class Program { ... }`) OR a simple snippet.
|
|
409
|
+
3. Simple snippets are automatically wrapped in a `public class Program { public static void Main(string[] args) { ... } }` and include `using System;`.
|
|
410
|
+
4. **Note**: `<!-- share-code-between-examples -->` is **not** supported for C#.
|
|
411
|
+
|
|
412
|
+
**Simple Snippet:**
|
|
413
|
+
<!-- skip-example -->
|
|
414
|
+
```csharp
|
|
415
|
+
Console.WriteLine("Hello C#");
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
**Full Class:**
|
|
419
|
+
<!-- skip-example -->
|
|
420
|
+
```csharp
|
|
421
|
+
using System;
|
|
422
|
+
public class Test {
|
|
423
|
+
public static void Main(string[] args) {
|
|
424
|
+
Console.WriteLine("Hello from Test class");
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
#### R
|
|
430
|
+
|
|
431
|
+
Use `r` for R examples.
|
|
432
|
+
|
|
433
|
+
**Prerequisites:**
|
|
434
|
+
* `Rscript` must be installed and available in your system path.
|
|
435
|
+
* **macOS**: `brew install r`
|
|
436
|
+
* **Ubuntu/Debian**: `sudo apt-get install r-base`
|
|
437
|
+
|
|
438
|
+
**Execution Model:**
|
|
439
|
+
Spawns an `Rscript` subprocess with the code piped to stdin.
|
|
440
|
+
|
|
441
|
+
**Recipe:**
|
|
442
|
+
|
|
443
|
+
1. Use `r` fenced code blocks.
|
|
444
|
+
2. Standard R syntax works out of the box.
|
|
445
|
+
3. State can be shared between blocks using `<!-- share-code-between-examples -->`.
|
|
446
|
+
|
|
447
|
+
```r
|
|
448
|
+
print("Hello R")
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
#### Pascal
|
|
452
|
+
|
|
453
|
+
Use `pascal` or `pas` for Pascal examples.
|
|
454
|
+
|
|
455
|
+
**Prerequisites:**
|
|
456
|
+
* `fpc` (Free Pascal Compiler) must be installed and available in your system path.
|
|
457
|
+
* **macOS**: `brew install fpc`
|
|
458
|
+
* **Ubuntu/Debian**: `sudo apt-get install fpc`
|
|
459
|
+
|
|
460
|
+
**Execution Model:**
|
|
461
|
+
Compiles the code using `fpc` into a temporary executable, then runs it.
|
|
462
|
+
|
|
463
|
+
**Recipe:**
|
|
464
|
+
|
|
465
|
+
1. Use `pascal` fenced code blocks.
|
|
466
|
+
2. You can provide a full program (including `program Name;`) OR a simple snippet.
|
|
467
|
+
3. Simple snippets are automatically wrapped in a `program TestProgram; begin ... end.` block.
|
|
468
|
+
4. **Note**: `<!-- share-code-between-examples -->` is **not** supported for Pascal.
|
|
469
|
+
|
|
470
|
+
**Simple Snippet:**
|
|
471
|
+
<!-- skip-example -->
|
|
472
|
+
```pascal
|
|
473
|
+
writeln('Hello Pascal');
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
**Full Program:**
|
|
477
|
+
<!-- skip-example -->
|
|
478
|
+
```pascal
|
|
479
|
+
program Hello;
|
|
480
|
+
begin
|
|
481
|
+
writeln('Hello from Pascal program');
|
|
482
|
+
end.
|
|
483
|
+
```
|
|
484
|
+
|
|
72
485
|
### Skipping Examples
|
|
73
486
|
|
|
74
487
|
To skip a specific code block, add the `<!-- skip-example -->` comment immediately before it:
|
|
@@ -79,6 +492,110 @@ To skip a specific code block, add the `<!-- skip-example -->` comment immediate
|
|
|
79
492
|
fetch('https://example.com');
|
|
80
493
|
```
|
|
81
494
|
|
|
495
|
+
### Sharing Code Between Examples
|
|
496
|
+
|
|
497
|
+
By default, each code block is executed in isolation. To share state (variables, functions, classes) between multiple code blocks in the same file, add the `<!-- share-code-between-examples -->` comment. This applies to the entire file.
|
|
498
|
+
|
|
499
|
+
> **Note**: This feature is currently supported for JavaScript, TypeScript, Python, Shell, BASIC, Perl, R, Go, Rust, Fortran, and C, but **not** compiled languages like COBOL, Java, C#, and Pascal.
|
|
500
|
+
|
|
501
|
+
<!-- share-code-between-examples -->
|
|
502
|
+
|
|
503
|
+
```python
|
|
504
|
+
x = 10
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
```python
|
|
508
|
+
# x is still available here
|
|
509
|
+
assert x == 10
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
### Configuration Comments
|
|
513
|
+
|
|
514
|
+
You can configure execution behavior for specific snippets using comments immediately preceding the code block.
|
|
515
|
+
|
|
516
|
+
**Arguments:** Pass arguments to the compiler or interpreter.
|
|
517
|
+
|
|
518
|
+
<!-- args: -v -->
|
|
519
|
+
```python
|
|
520
|
+
import sys
|
|
521
|
+
print("Running with verbose output")
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
**Environment Variables:** Set environment variables for the execution.
|
|
525
|
+
|
|
526
|
+
<!-- env: API_KEY=secret123 MODE=test -->
|
|
527
|
+
```bash
|
|
528
|
+
echo "Key: $API_KEY"
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
### Output Verification
|
|
532
|
+
|
|
533
|
+
You can verify that a code snippet produces specific output by assigning it an ID and then referencing that ID in a subsequent block.
|
|
534
|
+
|
|
535
|
+
1. Assign an ID to the code snippet using `<!-- id: my-snippet-name -->`.
|
|
536
|
+
2. Create an output block (usually `text` or `json`) and reference the ID using `<!-- output: my-snippet-name -->`.
|
|
537
|
+
|
|
538
|
+
`doccident` will execute the first snippet, capture its stdout, and verify that it matches the content of the output block.
|
|
539
|
+
|
|
540
|
+
**Example:**
|
|
541
|
+
|
|
542
|
+
<!-- id: hello-world -->
|
|
543
|
+
```js
|
|
544
|
+
console.log("Hello Output");
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
<!-- output: hello-world -->
|
|
548
|
+
```text
|
|
549
|
+
Hello Output
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
This is useful for ensuring your documentation's "Output:" sections stay in sync with the actual code behavior.
|
|
553
|
+
|
|
554
|
+
### Output Matching Modes
|
|
555
|
+
|
|
556
|
+
When verifying output, you can optionally specify a matching mode to handle whitespace or dynamic content.
|
|
557
|
+
|
|
558
|
+
* `exact` (default): Exact string match (trailing whitespace is trimmed).
|
|
559
|
+
* `ignore-whitespace`: Collapses all whitespace sequences to a single space and trims ends before comparing.
|
|
560
|
+
* `match:regex`: Treats the output block content as a regular expression.
|
|
561
|
+
|
|
562
|
+
**Examples:**
|
|
563
|
+
|
|
564
|
+
**Ignore Whitespace:**
|
|
565
|
+
|
|
566
|
+
<!-- id: fuzzy-output -->
|
|
567
|
+
```js
|
|
568
|
+
console.log(" A B \n C ");
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
<!-- output: fuzzy-output ignore-whitespace -->
|
|
572
|
+
```text
|
|
573
|
+
A B C
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
**Regex Matching:**
|
|
577
|
+
|
|
578
|
+
<!-- id: dynamic-output -->
|
|
579
|
+
```js
|
|
580
|
+
console.log("Timestamp: " + Date.now());
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
<!-- output: dynamic-output match:regex -->
|
|
584
|
+
```text
|
|
585
|
+
^Timestamp: \d+$
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
### Updating Output (Snapshots)
|
|
589
|
+
|
|
590
|
+
If you want `doccident` to automatically update your output blocks with the actual output from execution, run with the `--update-output` flag.
|
|
591
|
+
|
|
592
|
+
<!-- skip-example -->
|
|
593
|
+
```bash
|
|
594
|
+
npx doccident --update-output
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
This will replace the content of any `<!-- output: ... -->` block with the latest captured output from the corresponding ID. This is extremely useful when writing documentation: you can write the code example, add a placeholder output block, and let `doccident` fill it in for you.
|
|
598
|
+
|
|
82
599
|
## Configuration
|
|
83
600
|
|
|
84
601
|
Create a `.doccident-setup.js` file in your project root to configure the test environment.
|
|
@@ -87,6 +604,7 @@ Create a `.doccident-setup.js` file in your project root to configure the test e
|
|
|
87
604
|
|
|
88
605
|
If your examples use external libraries, provide them here. This allows your examples to `require` modules just like users would:
|
|
89
606
|
|
|
607
|
+
<!-- skip-example -->
|
|
90
608
|
```javascript
|
|
91
609
|
// .doccident-setup.js
|
|
92
610
|
module.exports = {
|
|
@@ -94,7 +612,7 @@ module.exports = {
|
|
|
94
612
|
// Make 'my-library' available when examples call require('my-library')
|
|
95
613
|
'my-library': require('./index.js'),
|
|
96
614
|
'lodash': require('lodash')
|
|
97
|
-
|
|
615
|
+
}
|
|
98
616
|
};
|
|
99
617
|
```
|
|
100
618
|
|
|
@@ -102,6 +620,7 @@ module.exports = {
|
|
|
102
620
|
|
|
103
621
|
Define global variables available to all snippets:
|
|
104
622
|
|
|
623
|
+
<!-- skip-example -->
|
|
105
624
|
```javascript
|
|
106
625
|
module.exports = {
|
|
107
626
|
globals: {
|
|
@@ -127,13 +646,14 @@ module.exports = {
|
|
|
127
646
|
* Reads Markdown content line-by-line.
|
|
128
647
|
* Uses a robust state machine to identify code fences and control comments (like `skip-example`).
|
|
129
648
|
* Extracts valid snippets into structured objects containing code, file paths, and line numbers.
|
|
130
|
-
* **
|
|
649
|
+
* **Multi-Language Support**: Recognizes `js`, `ts`, `python`, `shell`, `go`, `rust`, `fortran`, `cobol`, `c`, `basic`, `java`, `perl`, `csharp`, `r`, and `pascal` blocks.
|
|
131
650
|
|
|
132
651
|
2. **Test Runner (`src/doctest.ts`)**
|
|
133
652
|
* The orchestrator of the application.
|
|
134
653
|
* Iterates through parsed snippets and manages the execution lifecycle.
|
|
135
|
-
* **Sandboxing**: Uses Node.js's `vm` module (`runInNewContext`) to execute code in isolation.
|
|
136
|
-
* **
|
|
654
|
+
* **Sandboxing**: Uses Node.js's `vm` module (`runInNewContext`) to execute JS/TS code in isolation.
|
|
655
|
+
* **Subprocess Execution**: Spawns `python3`, `go`, `rustc`, `gfortran`, `cobc`, `gcc`, `cbmbasic`, `javac`/`java`, `perl`, `mcs`/`mono`, `Rscript`, `fpc`, or shell subprocesses for non-JS languages.
|
|
656
|
+
* **Transformation**: Uses **esbuild** to compile modern JavaScript and TypeScript code down to a compatible format before execution.
|
|
137
657
|
|
|
138
658
|
3. **Reporter (`src/reporter.ts`)**
|
|
139
659
|
* Collects execution results (pass, fail, skip).
|
|
@@ -145,3 +665,33 @@ module.exports = {
|
|
|
145
665
|
* Utility functions (`src/utils.ts`) provide common helpers.
|
|
146
666
|
|
|
147
667
|
This separation of concerns allows `doccident` to be easily extended—for example, by adding new parsers for different documentation formats or custom reporters for CI environments.
|
|
668
|
+
|
|
669
|
+
## License & History
|
|
670
|
+
|
|
671
|
+
This project is licensed under the Apache License, Version 2.0.
|
|
672
|
+
|
|
673
|
+
Copyright (c) 2025 Billaud Cipher
|
|
674
|
+
|
|
675
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
676
|
+
you may not use this file except in compliance with the License.
|
|
677
|
+
You may obtain a copy of the License at
|
|
678
|
+
|
|
679
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
680
|
+
|
|
681
|
+
Unless required by applicable law or agreed to in writing, software
|
|
682
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
683
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
684
|
+
See the License for the specific language governing permissions and
|
|
685
|
+
limitations under the License.
|
|
686
|
+
|
|
687
|
+
---
|
|
688
|
+
|
|
689
|
+
**Note**: This project was originally forked from [markdown-doctest](https://github.com/nick-johnstone/markdown-doctest) by Nick Johnstone. While the original inspiration and some core concepts remain, the **entire codebase has been replaced** and significantly expanded by Billaud Cipher to support a wide range of compiled and interpreted languages, shared state mechanisms, and output verification. All subsequent additions and modifications are covered by the Apache 2.0 Software License.
|
|
690
|
+
|
|
691
|
+
## Agentic AI Guide
|
|
692
|
+
|
|
693
|
+
This repository contains a dedicated guide for AI agents to understand how to use `doccident`. If you are an AI model tasked with maintaining this project or using it to verify documentation, please refer to:
|
|
694
|
+
|
|
695
|
+
[**doccident/guide/index.md**](./guide/index.md)
|
|
696
|
+
|
|
697
|
+
This directory contains instructions on capabilities, verification workflows, and configuration specifically structured for machine consumption.
|
package/bin/cmd.js
CHANGED
|
@@ -22,7 +22,8 @@ const config = {
|
|
|
22
22
|
require: {},
|
|
23
23
|
globals: {},
|
|
24
24
|
ignore: [],
|
|
25
|
-
testOutput: false
|
|
25
|
+
testOutput: false,
|
|
26
|
+
timeout: 300000 // Default 5 minutes
|
|
26
27
|
};
|
|
27
28
|
|
|
28
29
|
// Setup commander
|
|
@@ -34,6 +35,8 @@ program
|
|
|
34
35
|
.helpOption('-h, --help', 'output usage informations')
|
|
35
36
|
.option('-c, --config <path>', 'custom config location', path.join(process.cwd(), '/.doccident-setup.js'))
|
|
36
37
|
.option('--test-output', 'output the test results to the console')
|
|
38
|
+
.option('--update-output', 'update the output blocks in markdown files')
|
|
39
|
+
.option('--timeout <ms>', 'timeout for each snippet execution in milliseconds', '300000')
|
|
37
40
|
.parse(process.argv);
|
|
38
41
|
|
|
39
42
|
const options = program.opts();
|
|
@@ -57,6 +60,14 @@ const options = program.opts();
|
|
|
57
60
|
if (options.testOutput) {
|
|
58
61
|
config.testOutput = true;
|
|
59
62
|
}
|
|
63
|
+
|
|
64
|
+
if (options.updateOutput) {
|
|
65
|
+
config.updateOutput = true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (options.timeout) {
|
|
69
|
+
config.timeout = parseInt(options.timeout, 10);
|
|
70
|
+
}
|
|
60
71
|
|
|
61
72
|
// Resolve files
|
|
62
73
|
try {
|