mdeval 1.5.0 → 2.0.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.
@@ -1,110 +1,61 @@
1
1
  ---
2
2
  name: mdeval
3
- description: Evaluates JavaScript in markdown HTML comments and interpolates results in-place. Use when editing markdown files that contain mdeval script blocks or value markers, when the user wants computed/dynamic values in markdown, or when maintaining README badges, version numbers, or stats.
3
+ description: Editing Markdown that contains `<!--mdeval` script blocks or value markers; adding computed values to Markdown, such as versions, counts, file contents, command output, or generated tables; running the `mdeval` CLI or `mdeval/loader`, or debugging their output.
4
4
  ---
5
5
 
6
6
  # mdeval
7
7
 
8
- ## When to use
9
-
10
- - Markdown contains values derived from code, files, APIs, or shell commands
11
- - README stats, version numbers, dependency counts, or computed tables
12
- - Any value where accuracy matters — counts, sizes, dates, calculations
13
- - Auditable content — the expression proves the value is correct, not just asserted
8
+ This skill covers writing mdeval code in Markdown and running it. mdeval evaluates the code and writes each result into the file.
14
9
 
15
10
  ## Syntax
16
11
 
17
- Two types of HTML comments — invisible when rendered:
12
+ | Type | Syntax | Starts with |
13
+ | --- | --- | --- |
14
+ | Script block | `<!--mdeval\n...code...\n-->` | `<!--mdeval` + newline |
15
+ | Value marker | `<!--mdeval EXPR-->value<!--/mdeval-->` | `<!--mdeval` + space |
18
16
 
19
- | Type | Syntax | Purpose |
20
- |------|--------|---------|
21
- | Script block | `<!--mdeval\n...\n-->` | Define variables, imports, logic. Starts with `<!--mdeval` + newline |
22
- | Value marker | `<!--mdeval EXPR-->value<!--/mdeval-->` | Interpolate expression result. Starts with `<!--mdeval ` + space |
17
+ - All script blocks in a file form one ES module with full Node.js access and top-level `await`. Their imports and variables are visible to every marker in the file. Blocks are joined in order, so a block starting with `(`, `[`, or a template literal can continue the previous block's last statement unless it ends with a semicolon.
18
+ - `import.meta` refers to the Markdown file, so relative imports and paths resolve from it.
19
+ - Place script blocks at the top of the file. Their position relative to markers does not affect execution, but script blocks execute in their own order. Static imports from all blocks load before any block's code runs, so use dynamic `import()` when an earlier block must set up something an import reads at load time.
20
+ - Scripts and markers can use TypeScript syntax that Node.js strips: type annotations, `interface`, `type`, `as`, `satisfies`, and `!`. Do not use `enum`, `namespace` with values, or constructor parameter properties; they fail at runtime. Markdown under `node_modules` runs as plain JavaScript.
23
21
 
24
- Script blocks run as ESM with full Node.js access and top-level `await`. All blocks in a file merge into one module — imports and variables are shared across blocks and markers. `import.meta` points to the markdown file. Marker expressions are auto-awaited, so promises resolve automatically.
22
+ ## Markers
25
23
 
26
- ## Marker Expressions
24
+ A marker expression is anything valid after `const x =`. mdeval awaits it, converts the result to text, and replaces the value between `-->` and `<!--/mdeval-->`. An expression that appears in several markers is evaluated once.
27
25
 
28
- Any JavaScript expression valid on the right side of `const x =`:
26
+ Line breaks in values use the line ending most common outside marker values. Ties favor LF, then CRLF; if absent, LF is used.
29
27
 
30
- | Expression | Example |
31
- |------------|---------|
32
- | Variable | `<!--mdeval name-->value<!--/mdeval-->` |
33
- | Property access | `<!--mdeval data.version-->value<!--/mdeval-->` |
34
- | Computation | `<!--mdeval items.length + " items"-->value<!--/mdeval-->` |
35
- | IIFE | `<!--mdeval (() => { const x = 1 + 1; return x; })()-->value<!--/mdeval-->` |
28
+ | Result | Written as |
29
+ | --- | --- |
30
+ | `string` | As is, with line breaks converted to the file's line ending |
31
+ | Object with `Symbol.toPrimitive`, such as zx output | `String(value)` |
32
+ | Other object or array | `JSON.stringify(value)` |
33
+ | `undefined` or `null` | Error |
34
+ | Anything else | `String(value)` |
36
35
 
37
- Duplicate expressions across markers are evaluated once and reused.
36
+ Keep logic that one marker needs inside that marker. Markers can span lines, and an IIFE holds statements:
38
37
 
39
- ## Value Coercion
38
+ ````markdown
39
+ <!--mdeval (() => {
40
+ const n = 3;
41
+ return n % 2 === 0 ? 'even' : 'odd';
42
+ })()-->odd<!--/mdeval-->
43
+ ````
40
44
 
41
- | Type | Result |
42
- |------|--------|
43
- | `string` | As-is |
44
- | `number`, `boolean`, `bigint` | `String(value)` |
45
- | `object`, `array` | `JSON.stringify(value)` |
46
- | object with `Symbol.toPrimitive` | `String(value)` (e.g. zx `ProcessOutput`) |
47
- | `Promise` | Auto-awaited, then coerced |
48
- | `undefined`, `null` | Error |
45
+ Use script blocks for imports and for values that several markers share.
49
46
 
50
47
  ## Helpers
51
48
 
52
- | Helper | Description |
53
- |--------|-------------|
54
- | `block(value)` | Wraps value with newlines for block-level rendering |
55
- | `$` | [zx](https://google.github.io/zx/) shell — run commands via tagged templates: `` $`git branch` `` |
56
-
57
- | File | Access |
58
- |------|--------|
59
- | `.md` | Globals — call directly: `block(x)`, `` $`cmd` `` |
60
- | `.js`, `.ts`, anything else | `import { block, $ } from 'mdeval'` |
61
-
62
- CLI (and `--import mdeval/loader`) seeds the helpers on `globalThis` at startup. Modules imported transitively from a `.md` see them too, but in non-`.md` files prefer the explicit import. `import { block, $ } from 'mdeval'` is side-effect-free.
63
-
64
- ## Importing `.md` exports from a script
65
-
66
- Use when a Node script (validation, migration, agent tooling) needs to read exports from a `.md`. Run the script with `--import mdeval/loader`:
67
-
68
- ```bash
69
- node --import mdeval/loader ./consumer.js
70
- ```
71
-
72
- `consumer.js` can use ordinary static imports against `.md` files:
73
-
74
- ```js
75
- import { todos } from './TODOS.md';
76
-
77
- console.log(todos);
78
- ```
79
-
80
- - `--import mdeval/loader` is a side-effect-only entry — seeds `block`/`$` on `globalThis` and registers the Node ESM loader before any of the script's imports link.
81
- - Without it, static `import` of a `.md` fails — Node can't resolve `.md` until the loader is registered.
82
- - The plain `mdeval` import (`import { block, $ } from 'mdeval'`) stays pure — no globals, no loader. Use it when you only want the helpers.
83
- - Works on `.md` directly too: `node --import mdeval/loader ./TODOS.md`.
84
- - Runtime stack traces from a `.md` point at original lines and columns automatically.
85
-
86
- ## CLI
87
-
88
- ```bash
89
- mdeval README.md # single file
90
- mdeval README.md docs/guide.md # multiple files
91
- mdeval "docs/**/*.md" # glob pattern
92
- mdeval "**/*.md" # recursive — node_modules and dotdirs auto-excluded
93
- ```
94
-
95
- Supports full glob syntax including `**` recursive, `{a,b}` brace expansion, and `!` negation.
49
+ | Helper | Use |
50
+ | --- | --- |
51
+ | `block(value)` | Puts the value on its own lines. Required for headings, lists, tables, and other block Markdown. |
52
+ | `$` | Runs a shell command with [zx](https://google.github.io/zx/): `` $`git branch --show-current` `` |
96
53
 
97
- `node_modules` and hidden directories (`.git`, `.next`, etc.) are automatically excluded from glob expansion. No need to manually negate them. To explicitly include `node_modules`, reference it in the pattern: `mdeval "node_modules/pkg/*.md"`.
54
+ Both are globals in `.md` files at runtime and when type-checked, without imports. Their types are visible throughout the TypeScript program; if another global conflicts, use a separate tsconfig for Markdown that excludes the conflicting declaration from all loaded files, including root files, imports, and `compilerOptions.types` inherited through `extends`. In `.js` and `.ts` files, import the helpers used: `import { block, $ } from 'mdeval'`.
98
55
 
99
56
  ## Patterns
100
57
 
101
- ### Shell commands
102
-
103
- ````markdown
104
- <!--mdeval $`git branch --show-current`-->main<!--/mdeval-->
105
- ````
106
-
107
- ### Read package.json
58
+ Read `package.json`:
108
59
 
109
60
  ````markdown
110
61
  <!--mdeval
@@ -112,165 +63,123 @@ import fs from 'node:fs/promises';
112
63
  const pkg = JSON.parse(await fs.readFile('package.json', 'utf8'));
113
64
  -->
114
65
 
115
- Version: <!--mdeval pkg.version-->0.0.0<!--/mdeval-->
66
+ Version: <!--mdeval pkg.version-->1.0.0<!--/mdeval-->
116
67
  ````
117
68
 
118
- ### Import from other .md files
119
-
120
- Only script blocks are executed — markers are not processed:
69
+ Shell output:
121
70
 
122
71
  ````markdown
123
- <!--mdeval
124
- import { version } from './data.md';
125
- -->
126
-
127
- <!--mdeval version-->1.0.0<!--/mdeval-->
72
+ Branch: <!--mdeval $`git branch --show-current`-->main<!--/mdeval-->
128
73
  ````
129
74
 
130
- If the imported `.md` may not yet have mdeval content (stubs filled in over time), use a **namespace import** — named imports against an empty module are rejected by Node's ESM linker, but missing properties on a namespace resolve to `undefined`:
75
+ Typed data, which `tsc` can check:
131
76
 
132
77
  ````markdown
133
78
  <!--mdeval
134
- import * as data from './stub.md';
135
- const version = data.version ?? 'tbd';
79
+ interface Release {
80
+ version: string;
81
+ date: string;
82
+ }
83
+
84
+ const releases = [
85
+ { version: '1.1.0', date: '2026-11-01' },
86
+ { version: '1.0.0', date: '2026-10-01' },
87
+ ] satisfies Release[];
136
88
  -->
137
- ````
138
89
 
139
- ### Generate Markdown with md-pen
90
+ Latest: <!--mdeval releases[0].version-->1.1.0<!--/mdeval-->
91
+ ````
140
92
 
141
- Use [md-pen](https://github.com/privatenumber/md-pen) for formatted output (tables, lists, headings):
93
+ Generated Markdown with [md-pen](https://github.com/privatenumber/md-pen). It must be installed in the project:
142
94
 
143
95
  ````markdown
144
96
  <!--mdeval
145
- import { table, bold, link } from 'md-pen';
146
- const deps = [['cleye', '^2.3.0'], ['md-pen', '^0.0.2']];
147
- const depsTable = table(deps.map(([name, v]) => [link(`https://npm.im/${name}`, bold(name)), v]));
97
+ import { table } from 'md-pen';
148
98
  -->
149
99
 
150
- <!--mdeval block(depsTable)-->
100
+ <!--mdeval block(table([
101
+ { Package: 'cleye', Version: '^2.3.0' },
102
+ { Package: 'md-pen', Version: '^0.0.2' },
103
+ ]))-->
151
104
  | Package | Version |
152
105
  | - | - |
153
- | [__cleye__](https://npm.im/cleye) | ^2.3.0 |
106
+ | cleye | ^2.3.0 |
107
+ | md-pen | ^0.0.2 |
154
108
  <!--/mdeval-->
155
109
  ````
156
110
 
157
- ## Gotchas
158
-
159
- **Block-level values need `block()`.** Without it, block elements don't render:
111
+ Data from another `.md` file. Importing a `.md` file runs only its script blocks:
160
112
 
161
113
  ````markdown
162
- <!-- ❌ Heading stays on same line as comment, won't render -->
163
- <!--mdeval heading-->### Title<!--/mdeval-->
114
+ <!--mdeval
115
+ import { version } from './data.md';
116
+ -->
164
117
 
165
- <!-- ✅ block() adds newlines so the heading renders correctly -->
166
- <!--mdeval block(heading)-->
167
- ### Title
168
- <!--/mdeval-->
118
+ Version: <!--mdeval version-->1.0.0<!--/mdeval-->
169
119
  ````
170
120
 
171
- **Script code cannot contain `-->`** — it closes the HTML comment:
121
+ If the imported file may not have mdeval code yet, use a namespace import. A named import of a missing export fails, but a missing namespace property is `undefined`:
172
122
 
173
123
  ````markdown
174
- <!-- ❌ --> in the string literal closes the comment prematurely -->
175
124
  <!--mdeval
176
- const x = "<!--/mdeval-->";
177
- -->
178
-
179
- <!-- ✅ Build the string without --> -->
180
- <!--mdeval
181
- const x = String.fromCharCode(45, 45, 62);
125
+ import * as data from './stub.md';
126
+ const version = data.version ?? 'tbd';
182
127
  -->
183
128
  ````
184
129
 
185
- **Values cannot contain mdeval syntax.** Producing `<!--mdeval ` or `<!--/mdeval-->` in a value throws an error to prevent document corruption on re-parse.
186
-
187
- **Place scripts at the top.** Order doesn't affect execution — scripts can appear after the markers that reference them — but top placement signals the file contains generated content.
188
-
189
- **Markers can span multiple lines — use them to inline logic.** Don't cram expressions into one line. Co-locate marker-specific computation with its output:
190
-
191
- ````markdown
192
- <!--mdeval block(table([
193
- { name: 'cleye', version: '^2.3.0' },
194
- { name: 'md-pen', version: '^0.0.2' },
195
- ]))-->
196
- | Package | Version |
197
- | - | - |
198
- | cleye | ^2.3.0 |
199
- | md-pen | ^0.0.2 |
200
- <!--/mdeval-->
201
- ````
130
+ ## Gotchas
202
131
 
203
- For statements or control flow, wrap in an IIFE: `<!--mdeval (() => { ... })()-->`. Reserve script blocks for shared imports or values referenced by multiple markers.
132
+ **Block Markdown needs `block()`.** Without it, a heading, list, or table stays on the marker's line and does not render.
204
133
 
205
- **Standalone markers suppress inline markdown in the value.** When a marker opens a line — standalone paragraph, list item, or blockquote — GitHub treats the entire line as a raw HTML block. Inline markdown in the value (`` `code` ``, `**bold**`, `[link](url)`) is not processed and appears literally.
134
+ **Script code cannot contain `-->` or `<!--mdeval` followed by a space or newline.** The first ends the HTML comment; the second makes mdeval report an unclosed script, even inside a string. Build the text instead, such as `'--' + '>'` or `'<!--' + 'mdeval '`. Rewrite `x-- > y` as `x -= 1; x > y`.
206
135
 
207
- ````markdown
208
- <!-- ❌ backtick shows literally — line is an HTML block, not inline markdown -->
209
- <!--mdeval expr-->`value`<!--/mdeval-->
210
- ````
136
+ **Values cannot contain `<!--mdeval` followed by a space, a newline, or CRLF, or `<!--/mdeval-->`.** mdeval throws instead of writing them.
211
137
 
212
- Use a multi-line marker so the value occupies its own lines, which are processed as normal markdown. Use [md-pen](https://github.com/privatenumber/md-pen) to generate formatted values:
138
+ **A marker that starts a line turns off inline Markdown in its value.** When a marker opens a paragraph, list item, or blockquote, GitHub treats the line as raw HTML, so `` `code` ``, `**bold**`, and links in the value appear literally. Wrap the value in `block()` so it renders on its own lines:
213
139
 
214
140
  ````markdown
215
141
  <!--mdeval
216
- import { code, bold, link } from 'md-pen';
142
+ import { code } from 'md-pen';
217
143
  -->
218
144
 
219
- <!--mdeval code('asdf')-->
220
- `asdf`
145
+ <!--mdeval block(code('value'))-->
146
+ `value`
221
147
  <!--/mdeval-->
222
148
  ````
223
149
 
224
- Markers inside headings are always inline — markdown in the value always renders there.
150
+ Markers inside headings, or after other text on a line, render inline Markdown normally.
225
151
 
226
- **Comments don't work in link URLs or image alt text.** GitHub escapes comment syntax in these positions:
152
+ **Markers do not work in link URLs or image alt text.** GitHub shows or encodes the comment there instead of hiding it. mdeval warns about markers in alt text.
227
153
 
228
- ````markdown
229
- <!-- ❌ comment is URL-encoded as href -->
230
- [text](<!-- comment -->)
154
+ **`<details>` needs blank lines around `</summary>`.** Without them, block Markdown inside it renders as text. md-pen's `details(summary, content)` writes the correct shape.
231
155
 
232
- <!-- ❌ comment appears as visible alt text -->
233
- ![<!-- comment -->](image.png)
234
- ````
156
+ **Examples in code are safe.** mdeval ignores markers in fenced, indented, and inline code, so Markdown can document mdeval syntax.
235
157
 
236
- **`<details>` requires blank lines around the `</summary>` close.** Block-level Markdown inside a `<details>` only renders when blank lines surround the closing `</summary>` tag — otherwise lists, code fences, tables, and other block elements render as literal text. md-pen's `details(summary, content)` emits the correct shape:
158
+ **Do not create a `.md` file that only holds code.** mdeval warns about it. Put shared logic in a `.js` or `.ts` file and import it.
237
159
 
238
- ````markdown
239
- <!--mdeval
240
- import { details, ul } from 'md-pen';
241
- -->
160
+ ## CLI
242
161
 
243
- <!--mdeval block(details(
244
- 'Click me',
245
- ul(['item one', 'item two']),
246
- ))-->
247
- <details>
248
- <summary>Click me</summary>
162
+ ```bash
163
+ mdeval README.md "docs/**/*.md"
164
+ ```
249
165
 
250
- - item one
251
- - item two
166
+ - mdeval updates each file in place and prints the path of each file it changed.
167
+ - For CI, run `npx mdeval --check "**/*.md"` to report outdated files and exit 1 without rewriting; evaluation can still have side effects.
168
+ - For a Lefthook pre-commit hook, use a job with `run: npx mdeval --check "**/*.md"` and no `glob`, so changes to non-Markdown inputs are checked too. When it fails, run `npx mdeval "**/*.md"`, review and stage the updates, and commit again. Do not rewrite files in the hook (with `stage_fixed` or `fail_on_changes`) or parse mdeval's stdout to stage files: Lefthook's rollback can discard unstaged edits, `stage_fixed` misses dependent files, and scripts can print to stdout.
169
+ - Globs skip `node_modules` and hidden directories unless the pattern names them, such as `"node_modules/pkg/*.md"`.
170
+ - Warnings go to stderr. A `marker leaks into rendered ...` warning means a marker will show as text on GitHub. Fix it unless the marker is an intentional example.
171
+ - After editing mdeval code, run `mdeval` on the file and check the written values.
252
172
 
253
- </details>
254
- <!--/mdeval-->
255
- ````
173
+ ## References
256
174
 
257
- **Set up a git hook so values never go stale.** Use [Lefthook](https://github.com/evilmartians/lefthook) with this config:
258
-
259
- ```yaml
260
- # lefthook.yml
261
- pre-commit:
262
- jobs:
263
- - name: mdeval
264
- # lefthook's default `gobwas` matcher requires `**` to span 1+ dirs,
265
- # so `**/*.md` alone misses root-level files like README.md
266
- glob: ["*.md", "**/*.md"]
267
- run: |
268
- files=$(npx mdeval "**/*.md")
269
- [ -n "$files" ] && git add $files
270
- ```
175
+ - `references/type-checking.md`: setting up or running `tsc` on Markdown, or fixing a TypeScript error reported at a `.md` line. If the project's `tsconfig.json` lists mdeval in `contentMappers`, read it and run `tsc` after editing mdeval code.
176
+ - `references/node-loader.md`: a Node.js script, test, or tool imports a `.md` file, or runs one with `node`.
271
177
 
272
- `git add $files` re-stages only the files mdeval actually rewrote. Assumes `.md` paths without spaces.
178
+ ## Reporting problems
273
179
 
274
- **Markers in code blocks are safe.** Fenced, indented, and inline code won't be touched — safe to document mdeval syntax in your own README.
180
+ Report bugs in mdeval or in this skill to [privatenumber/mdeval](https://github.com/privatenumber/mdeval/issues) without waiting to be asked. This includes wrong behavior, confusing errors or warnings, wrong or missing guidance in this skill, and friction that cost time.
275
181
 
276
- **Never create a .md file with only a script block and no real content.** Markdown files must contain actual prose/documentation. For shared logic or utilities, create a `.js` or `.ts` file and import it from your markdown instead.
182
+ 1. Search open and closed issues: `gh issue list -R privatenumber/mdeval --search "<terms>" --state all`.
183
+ 2. Confirm the behavior on the latest mdeval release.
184
+ 3. Write a minimal Markdown reproduction without the user's private content.
185
+ 4. If your instructions allow filing issues for the user, run `gh issue create -R privatenumber/mdeval`. Otherwise, show the user the draft and file it only after they approve it.
@@ -0,0 +1,25 @@
1
+ # Importing `.md` files from Node.js
2
+
3
+ `.md` files with mdeval script blocks are ES modules. Run a script with the loader to import their exports:
4
+
5
+ ```bash
6
+ node --import mdeval/loader ./script.js
7
+ ```
8
+
9
+ ```js
10
+ // script.js
11
+ import { todos } from './TODOS.md'
12
+
13
+ console.log(todos)
14
+ ```
15
+
16
+ - `--import mdeval/loader` must come before the script, so the loader is registered before `.md` imports resolve. Without it, importing a `.md` file fails.
17
+ - The loader also defines the `block` and `$` globals that `.md` code uses.
18
+ - Importing a `.md` file runs only its script blocks. It does not evaluate markers or change the file.
19
+ - TypeScript scripts work the same way: `node --import mdeval/loader ./script.ts`.
20
+ - Run a `.md` file's script blocks directly: `node --import mdeval/loader ./TODOS.md`.
21
+ - Stack traces from `.md` code report the original Markdown lines.
22
+
23
+ In `.js` and `.ts` files, import the helpers instead of relying on globals: `import { block, $ } from 'mdeval'`. This import has no side effects. It does not define globals or register the loader.
24
+
25
+ In tests that spawn the script, pass the flag to the child process, for example `spawn(process.execPath, ['--import', 'mdeval/loader', scriptPath])`.
@@ -0,0 +1,70 @@
1
+ # Type checking mdeval code
2
+
3
+ TypeScript 7.1 checks the code in script blocks and markers through a content mapper, and reports errors at the original Markdown lines. Checking does not run the code.
4
+
5
+ ## Set up
6
+
7
+ Install the TypeScript 7.1 beta:
8
+
9
+ ```bash
10
+ npm install --save-dev typescript@7.1.0-beta
11
+ ```
12
+
13
+ Register mdeval for `.md` files in `tsconfig.json`, and include the Markdown files to check:
14
+
15
+ ```json
16
+ {
17
+ "compilerOptions": {
18
+ "module": "nodenext",
19
+ "target": "esnext",
20
+ "strict": true,
21
+ "erasableSyntaxOnly": true,
22
+ "noEmit": true
23
+ },
24
+ "contentMappers": [
25
+ {
26
+ "package": "mdeval",
27
+ "extensions": [
28
+ ".md"
29
+ ]
30
+ }
31
+ ],
32
+ "include": [
33
+ "README.md",
34
+ "docs/**/*.md"
35
+ ]
36
+ }
37
+ ```
38
+
39
+ Ask the user before adding this to a project. It requires a beta compiler.
40
+
41
+ ## Run
42
+
43
+ ```bash
44
+ npx tsc --noEmit --runExternalCode
45
+ ```
46
+
47
+ `--runExternalCode` lets `tsc` start mdeval's mapper. Without it, `tsc` fails with `TS18068: Content mappers require the '--runExternalCode' command line flag to be enabled.`
48
+
49
+ ## What is checked
50
+
51
+ - Each `.md` file outside `node_modules` is a TypeScript module. Annotate with TypeScript syntax, such as `const count: number = 42`. JSDoc types are not checked there.
52
+ - Markdown under `node_modules` is checked as JavaScript because Node runs it as JavaScript.
53
+ - `erasableSyntaxOnly` reports syntax that fails at runtime, such as `enum`, at its Markdown line.
54
+ - `block` and `$` are available without imports; explicit imports also work. Their global types are visible throughout the TypeScript program, including `.ts` and `.js` files, which matches runtime: the loader puts them on `globalThis` for every module in the process. Code that also runs without the loader should import them, or Markdown should be checked under its own tsconfig.
55
+ - For conflicting globals, including full zx's `$`, use a separate tsconfig for Markdown that excludes the conflicting declaration from all loaded files, including root files, the import graph, and `compilerOptions.types` inherited through `extends` (for example, `zx/globals`). With `skipLibCheck`, the first declaration TypeScript checks determines the type, and a conflicting later `.d.ts` declaration can be skipped. For `a.md`, `globals.d.ts`, then `b.md`, the first Markdown declaration establishes the type and TS2403 can be absent.
56
+ - A repeated marker expression is checked once, and its errors point to the first occurrence.
57
+ - Fenced code, inline code, and rendered marker values are not checked.
58
+ - A `.ts` file that imports a `.md` file is checked against the `.md` exports' types when the `.ts` file is in `include`.
59
+
60
+ ## Fix errors
61
+
62
+ Fix a reported error in the script block or marker at that Markdown line, then run `tsc` again.
63
+
64
+ A namespace import of a `.md` file without exports, such as `import * as data from './stub.md'`, reports `Property 'version' does not exist` for `data.version`, even though the value is `undefined` at runtime. Give the imported file the export, or cast the namespace to the expected shape:
65
+
66
+ ```ts
67
+ import * as stub from './stub.md'
68
+
69
+ const data = stub as { version?: string }
70
+ ```
@@ -1,12 +0,0 @@
1
- #!/usr/bin/env node
2
- var x=Object.defineProperty;var c=(e,a)=>x(e,"name",{value:a,configurable:!0});import b from"node:fs/promises";import{pathToFileURL as E,fileURLToPath as T}from"node:url";import{p as $,E as B,M as F}from"./parse-markdown-wnFVEAED.mjs";import{GenMapping as P,toEncodedMap as W,addSegment as U}from"@jridgewell/gen-mapping";import"micromark";const M=10,v=13,N=c(e=>e===9||e===M||e===11||e===12||e===v||e===32,"isWhitespaceCode"),j=c(e=>e>=48&&e<=57||e>=65&&e<=90||e===95||e>=97&&e<=122||e===36,"isIdentifierCode"),A=c((e,a)=>{const h=E(e).href,p=new P({file:e}),s=[];let i="";const o=[];for(let n=a.indexOf(`
3
- `);n!==-1;n=a.indexOf(`
4
- `,n+1))o.push(n);const y=c(n=>{let t=0,r=o.length;for(;t<r;){const f=Math.floor((t+r)/2);o[f]<n?t=f+1:r=f}const d=t>0?o[t-1]:-1;return{line:t,column:n-d-1}},"positionFromOffset"),u=c(n=>{i+=n},"appendLineChunk"),S=c(()=>{s.push(i),i=""},"flushLine"),O=c((n,t)=>{const r=y(t);let d=r.line,f=r.column,L=!1,R=!1,m=0;const w=c(l=>i.length+l-m,"generatedColumn");for(let l=0;l<n.length;l+=1){const g=n.codePointAt(l),C=N(g),I=j(g);!C&&(l===0||R||!L||!I)&&U(p,s.length,w(l),h,d,f),g===M?(d+=1,f=0):f+=1,L=I,R=C,g===M&&(u(n.slice(m,l)),S(),m=l+1)}m<n.length&&u(n.slice(m))},"appendSourceText"),k=c(n=>{let t=0;for(let r=n.indexOf(`
5
- `);r!==-1;r=n.indexOf(`
6
- `,t))u(n.slice(t,r)),S(),t=r+1;t<n.length&&u(n.slice(t))},"appendSyntheticText");return{appendSource(n,t){O(n,t)},appendSynthetic(n){k(n)},toModuleSource:c(()=>{const t=`${(i===""?s:[...s,i]).join(`
7
- `)}
8
- `,r=W(p),d=`//# sourceMappingURL=data:application/json;base64,${Buffer.from(JSON.stringify(r)).toString("base64")}`;return`${t}${d}
9
- `},"toModuleSource")}},"createSourceBuilder"),X=c((e,a,h,p)=>{const s=A(a,e);for(const o of h)s.appendSource(o.content,o.contentStart),o.content.endsWith(`
10
- `)||s.appendSynthetic(`
11
- `);const i=new Map;for(const o of p)i.has(o.expression)||i.set(o.expression,{index:i.size,marker:o});for(const[o,{index:y,marker:u}]of i){const S=`export const ${B}${y} = `;s.appendSynthetic(S),s.appendSource(o,u.start+F.length),s.appendSynthetic(`;
12
- `)}return s.toModuleSource()},"generateModule"),_=c(async(e,a,h)=>{if(!e.endsWith(".md"))return h(e,a);const p=T(e),s=await b.readFile(p,"utf8"),{scriptBlocks:i,markers:o}=$(s);return i.length===0&&o.length===0?{format:"module",source:"",shortCircuit:!0}:{format:"module",source:X(s,p,i,o),shortCircuit:!0}},"load");export{_ as load};
@@ -1,6 +0,0 @@
1
- var M=Object.defineProperty;var l=(n,e)=>M(n,"name",{value:e,configurable:!0});import{postprocess as v,parse as w,preprocess as k}from"micromark";const x="mdeval",c=`<!--${x}`,C=`${c} `,E=`<!--/${x}-->`,b=`__${x}_`,g="-->",_=l(n=>{const e=new Map;for(const{expression:s}of n)e.has(s)||e.set(s,e.size);return e},"buildExpressionMap"),$=l(n=>{const e=n.codePointAt(0)===65279?1:0,s=v(w().document().write(k()(n,void 0,!0))),r=[];for(const[o,t]of s)o==="enter"&&(t.type==="codeFenced"||t.type==="codeIndented"||t.type==="codeText")&&r.push([t.start.offset+e,t.end.offset+e]);return r},"findCodeRanges"),R=l(n=>{let e=null,s=0;return r=>{for(e===null&&(e=$(n));s<e.length&&e[s][1]<=r;)s+=1;return s<e.length&&r>=e[s][0]}},"createIsInCode"),u=l((n,e)=>{let s=e;for(;s<n.length;){const r=n.indexOf(c,s);if(r===-1)return-1;const o=n[r+c.length];if(o===" "||o===`
2
- `||o==="\r"&&n[r+c.length+1]===`
3
- `)return r;s=r+c.length}return-1},"findNextMdevalOpening"),S=l(n=>{if(!n.includes(c))return{scriptBlocks:[],markers:[]};const e=R(n),s=[],r=[];let o=0;for(;o<n.length;){const t=n.indexOf(c,o);if(t===-1)break;const i=t+c.length,m=n[i];if(m===`
4
- `||m==="\r"&&n[i+1]===`
5
- `){const f=m===`
6
- `?i+1:i+2,a=n.indexOf(g,f),d=u(n,f),p=d!==-1&&(a===-1||d<a);if(a===-1||p){if(!e(t))throw new Error(p?`mdeval: unclosed script block at offset ${t} (another mdeval opening found before \`-->\`)`:`mdeval: unclosed script block at offset ${t} (missing \`-->\`)`);if(a===-1)break;o=f;continue}const h=a+g.length;e(t)||s.push({content:n.slice(f,a),contentStart:f,start:t,end:h}),o=h;continue}if(m===" "){const f=i+1,a=n.indexOf(g,f);if(a===-1){if(e(t))break;throw new Error(`mdeval: unclosed marker open at offset ${t} (missing \`-->\` on the marker open)`)}const d=a+g.length,p=n.indexOf(E,d),h=u(n,d);if(p===-1||h!==-1&&h<p){if(e(t)){o=d;continue}throw new Error(`mdeval: unclosed marker open at offset ${t} (missing \`<!--/mdeval-->\`)`)}const O=p+E.length;e(t)||r.push({expression:n.slice(f,a),start:t,end:O}),o=O;continue}o=i}return{scriptBlocks:s,markers:r}},"parseMarkdown"),I=l((n,{scriptBlocks:e,markers:s})=>{if(e.length===0&&s.length===0)return!1;const r=[...e.map(t=>[t.start,t.end]),...s.map(t=>[t.start,t.end])].sort((t,i)=>t[0]-i[0]);let o=0;for(const[t,i]of r){if(n.slice(o,t).trim()!=="")return!1;o=i}return n.slice(o).trim()===""},"isOnlyMdeval");export{c as C,b as E,C as M,E as a,_ as b,g as c,I as i,S as p};