mdeval 1.1.0 → 1.3.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.
- package/README.md +10 -2
- package/dist/cli.mjs +4 -4
- package/dist/md-loader.mjs +4 -4
- package/dist/parse-markdown-BBzZcXPS.mjs +3 -0
- package/package.json +2 -1
- package/skills/mdeval/SKILL.md +38 -1
- package/dist/parse-markdown-ChGEvmW3.mjs +0 -3
package/README.md
CHANGED
|
@@ -7,6 +7,8 @@ Ever wanted dynamic values in your Markdown — package versions, star counts, c
|
|
|
7
7
|
|
|
8
8
|
mdeval embeds JavaScript directly in your Markdown using HTML comments. The logic lives right next to the content it produces, and the file renders normally everywhere — GitHub, editors, any Markdown viewer — because HTML comments are invisible.
|
|
9
9
|
|
|
10
|
+
Any value that matters — a dependency count, a version string, a benchmark result — is computed, not typed. Whether written by a person or an AI agent, the expression shows the work and anyone can verify it. Re-run `mdeval` and every value updates.
|
|
11
|
+
|
|
10
12
|
## Install
|
|
11
13
|
|
|
12
14
|
```bash
|
|
@@ -40,7 +42,9 @@ The `1.0.0` between `-->` and `<!--/mdeval-->` is the current value — it gets
|
|
|
40
42
|
mdeval README.md
|
|
41
43
|
```
|
|
42
44
|
|
|
43
|
-
The file is updated in-place. You can pass multiple files
|
|
45
|
+
The file is updated in-place. You can pass multiple files or glob patterns: `mdeval README.md "docs/**/*.md"`.
|
|
46
|
+
|
|
47
|
+
`node_modules` and hidden directories are automatically excluded from glob expansion.
|
|
44
48
|
|
|
45
49
|
## Notes
|
|
46
50
|
|
|
@@ -49,7 +53,11 @@ The file is updated in-place. You can pass multiple files at once: `mdeval READM
|
|
|
49
53
|
- Markers can be **self-contained** — no script block needed:
|
|
50
54
|
|
|
51
55
|
```markdown
|
|
52
|
-
<!--mdeval (() =>
|
|
56
|
+
<!--mdeval (() => {
|
|
57
|
+
const n = 3;
|
|
58
|
+
if (n % 2 === 0) { return 'even' }
|
|
59
|
+
return 'odd'
|
|
60
|
+
})()-->odd<!--/mdeval-->
|
|
53
61
|
```
|
|
54
62
|
|
|
55
63
|
In large documents, this IIFE pattern lets you keep logic next to the marker it serves instead of in a distant script block.
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var
|
|
3
|
-
`)||
|
|
4
|
-
${String(
|
|
5
|
-
`,globalThis.$=
|
|
2
|
+
var M=Object.defineProperty;var p=(o,r)=>M(o,"name",{value:r,configurable:!0});import m from"node:fs/promises";import $ from"node:path";import{cli as b}from"cleye";import{glob as x}from"tinyglobby";import{$ as S}from"zx";import{p as d,b as O,E as _,M as f,C as k,a as g,c as C,i as P}from"./parse-markdown-BBzZcXPS.mjs";import{register as R}from"node:module";import{pathToFileURL as T}from"node:url";import"md4x";const N=p(o=>{if(o==null)throw new Error(`Cannot interpolate ${o} \u2014 this is likely a bug in your script`);return typeof o=="string"?o:typeof o=="object"?Symbol.toPrimitive in o?String(o):JSON.stringify(o):String(o)},"coerceValue");R("#md-loader",import.meta.url);const v=p(async(o,r)=>{const{markers:t}=d(o);if(t.length===0)return o;const i=await import(T(r).href),n=O(t),w=await Promise.all(Array.from(n).map(async([e,l])=>{const s=N(await i[`${_}${l}`]);if(s.includes(f)||s.includes(`${k}
|
|
3
|
+
`)||s.includes(g))throw new Error(`Expression "${e}" produced a value containing mdeval comment syntax, which would corrupt the document on re-parse`);return s})),a=[];let c=0;for(const e of t){const l=f.length+e.expression.length+C.length,s=e.start+l,E=e.end-g.length,y=n.get(e.expression);a.push(o.slice(c,s),w[y]),c=E}return a.push(o.slice(c)),a.join("")},"processSource");globalThis.block=o=>`
|
|
4
|
+
${String(o)}
|
|
5
|
+
`,globalThis.$=S;const A=b({name:"mdeval",parameters:["<files...>"]}),u=A._.files,F=u.some(o=>o.includes("node_modules")),h=await x(u,{ignore:F?[]:["**/node_modules/**"]});h.length===0&&(console.error("No files matched the given patterns"),process.exit(1)),await Promise.all(h.map(async o=>{try{const r=$.resolve(o),t=await m.readFile(r,"utf8"),i=d(t);P(t,i)&&console.warn(`Warning: ${o} has no markdown content outside of mdeval blocks`);const n=await v(t,r);n!==t&&(await m.writeFile(r,n,"utf8"),console.log(`Updated: ${o}`))}catch(r){console.error(`Error processing ${o}:`,r instanceof Error?r.message:r),process.exitCode=1}}));
|
package/dist/md-loader.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var p=Object.defineProperty;var i=(r,o)=>p(r,"name",{value:o,configurable:!0});import c from"node:fs/promises";import{fileURLToPath as m}from"node:url";import{p as f,b as u,E as d}from"./parse-markdown-
|
|
3
|
-
`),s=u(o);if(s.size===0)return
|
|
4
|
-
${
|
|
5
|
-
`)}`},"generateModule"),$=i(async(r,o,
|
|
2
|
+
var p=Object.defineProperty;var i=(r,o)=>p(r,"name",{value:o,configurable:!0});import c from"node:fs/promises";import{fileURLToPath as m}from"node:url";import{p as f,b as u,E as d}from"./parse-markdown-BBzZcXPS.mjs";import"md4x";const l=i((r,o)=>{const e=r.map(t=>t.content).join(`
|
|
3
|
+
`),s=u(o);if(s.size===0)return e;const n=[...s].map(([t,a])=>`export const ${d}${a} = ${t};`);return`${e}
|
|
4
|
+
${n.join(`
|
|
5
|
+
`)}`},"generateModule"),$=i(async(r,o,e)=>{if(!r.endsWith(".md"))return e(r,o);const s=await c.readFile(m(r),"utf8"),{scriptBlocks:n,markers:t}=f(s);return n.length===0&&t.length===0?e(r,o):{format:"module",source:l(n,t),shortCircuit:!0}},"load");export{$ as load};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
var _=Object.defineProperty;var d=(n,t)=>_(n,"name",{value:t,configurable:!0});import{parseAST as R}from"md4x";const E="mdeval",O=`<!--${E}`,u=`${O} `,M=`<!--/${E}-->`,y=`__${E}_`,m="-->",k=`${E}
|
|
2
|
+
`,C=d(n=>{const t=new Map;for(const{expression:r}of n)t.has(r)||t.set(r,t.size);return t},"buildExpressionMap"),P=d((n,t)=>{const r=[],f=[];let l=0;const e=d(s=>{if(!Array.isArray(s))return;const[h,,...a]=s;if(h==="code"){const i=a[0];if(typeof i=="string"&&i.length>0){const c=t.indexOf(i,l);if(c!==-1){f.push([c,c+i.length]),l=c+i.length;return}const o=i.split(`
|
|
3
|
+
`).filter(g=>g.length>0);if(o.length>0){const g=t.indexOf(o[0],l);if(g!==-1){let p=g+o[0].length;for(let x=1;x<o.length;x+=1){const A=t.indexOf(o[x],p);A!==-1&&(p=A+o[x].length)}f.push([g,p]),l=p}}}return}if(h===null&&typeof a[0]=="string"){const i=a[0],c=`<!--${i}-->`,o=t.indexOf(c,l);i.startsWith(k)&&o!==-1&&r.push({content:i.slice(k.length),start:o,end:o+c.length}),o!==-1&&(l=o+c.length);return}for(const i of a)Array.isArray(i)&&e(i)},"walk");for(const s of n.nodes)e(s);return{scriptBlocks:r,codeRanges:f}},"walkAst"),S=d((n,t)=>{let r=0;const f=d(s=>{for(;r<t.length&&t[r][1]<=s;)r+=1;if(r>=t.length)return!1;const[h,a]=t[r];return s>=h&&s<a},"isInCode"),l=[];let e=0;for(;e<n.length;){const s=n.indexOf(u,e);if(s===-1)break;const h=s+u.length,a=n.indexOf(m,h);if(a===-1)break;const i=a+m.length,c=n.indexOf(M,i);if(c===-1){e=i;continue}const o=c+M.length;f(s)||l.push({expression:n.slice(h,a),start:s,end:o}),e=o}return l},"findMarkers"),w=d(n=>{if(!n.includes(O))return{scriptBlocks:[],markers:[]};const t=R(n),{scriptBlocks:r,codeRanges:f}=P(t,n);return{scriptBlocks:r,markers:S(n,f)}},"parseMarkdown"),I=d((n,{scriptBlocks:t,markers:r})=>{if(t.length===0&&r.length===0)return!1;const f=[...t.map(e=>[e.start,e.end]),...r.map(e=>[e.start,e.end])].sort((e,s)=>e[0]-s[0]);let l=0;for(const[e,s]of f){if(n.slice(l,e).trim()!=="")return!1;l=s}return n.slice(l).trim()===""},"isOnlyMdeval");export{O as C,y as E,u as M,M as a,C as b,m as c,I as i,w as p};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mdeval",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Evaluate JavaScript in markdown HTML comments and interpolate results in-place",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"markdown",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"cleye": "^2.3.0",
|
|
32
32
|
"md4x": "^0.0.25",
|
|
33
|
+
"tinyglobby": "^0.2.15",
|
|
33
34
|
"zx": "8.8.5-lite"
|
|
34
35
|
}
|
|
35
36
|
}
|
package/skills/mdeval/SKILL.md
CHANGED
|
@@ -5,6 +5,13 @@ description: Evaluates JavaScript in markdown HTML comments and interpolates res
|
|
|
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
|
|
14
|
+
|
|
8
15
|
## Syntax
|
|
9
16
|
|
|
10
17
|
Two types of HTML comments — invisible when rendered:
|
|
@@ -14,7 +21,7 @@ Two types of HTML comments — invisible when rendered:
|
|
|
14
21
|
| Script block | `<!--mdeval\n...\n-->` | Define variables, imports, logic. Starts with `<!--mdeval` + newline |
|
|
15
22
|
| Value marker | `<!--mdeval EXPR-->value<!--/mdeval-->` | Interpolate expression result. Starts with `<!--mdeval ` + space |
|
|
16
23
|
|
|
17
|
-
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.
|
|
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.
|
|
18
25
|
|
|
19
26
|
## Marker Expressions
|
|
20
27
|
|
|
@@ -36,10 +43,38 @@ Duplicate expressions across markers are evaluated once and reused.
|
|
|
36
43
|
| `string` | As-is |
|
|
37
44
|
| `number`, `boolean`, `bigint` | `String(value)` |
|
|
38
45
|
| `object`, `array` | `JSON.stringify(value)` |
|
|
46
|
+
| object with `Symbol.toPrimitive` | `String(value)` (e.g. zx `ProcessOutput`) |
|
|
47
|
+
| `Promise` | Auto-awaited, then coerced |
|
|
39
48
|
| `undefined`, `null` | Error |
|
|
40
49
|
|
|
50
|
+
## Globals
|
|
51
|
+
|
|
52
|
+
| Global | 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
|
+
## CLI
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
mdeval README.md # single file
|
|
61
|
+
mdeval README.md docs/guide.md # multiple files
|
|
62
|
+
mdeval "docs/**/*.md" # glob pattern
|
|
63
|
+
mdeval "**/*.md" # recursive — node_modules and dotdirs auto-excluded
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Supports full glob syntax including `**` recursive, `{a,b}` brace expansion, and `!` negation.
|
|
67
|
+
|
|
68
|
+
`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"`.
|
|
69
|
+
|
|
41
70
|
## Patterns
|
|
42
71
|
|
|
72
|
+
### Shell commands
|
|
73
|
+
|
|
74
|
+
````markdown
|
|
75
|
+
<!--mdeval $`git branch --show-current`-->main<!--/mdeval-->
|
|
76
|
+
````
|
|
77
|
+
|
|
43
78
|
### Read package.json
|
|
44
79
|
|
|
45
80
|
````markdown
|
|
@@ -116,3 +151,5 @@ const x = String.fromCharCode(45, 45, 62);
|
|
|
116
151
|
**Use IIFEs to co-locate logic.** In large docs, keep marker-specific computation inline instead of in a distant script block: `<!--mdeval (() => { ... })()-->`.
|
|
117
152
|
|
|
118
153
|
**Markers in code blocks are safe.** Fenced, indented, and inline code won't be touched — safe to document mdeval syntax in your own README.
|
|
154
|
+
|
|
155
|
+
**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.
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
var _=Object.defineProperty;var d=(s,t)=>_(s,"name",{value:t,configurable:!0});import{parseAST as R}from"md4x";const x="mdeval",k=`<!--${x}`,O=`${k} `,M=`<!--/${x}-->`,C=`__${x}_`,A="-->",m=`${x}
|
|
2
|
-
`,P=d(s=>{const t=new Map;for(const{expression:e}of s)t.has(e)||t.set(e,t.size);return t},"buildExpressionMap"),S=d((s,t)=>{const e=[],a=[];let l=0;const f=d(r=>{if(!Array.isArray(r))return;const[h,,...c]=r;if(h==="code"){const n=c[0];if(typeof n=="string"&&n.length>0){const o=t.indexOf(n,l);if(o!==-1){a.push([o,o+n.length]),l=o+n.length;return}const i=n.split(`
|
|
3
|
-
`).filter(p=>p.length>0);if(i.length>0){const p=t.indexOf(i[0],l);if(p!==-1){let g=p+i[0].length;for(let E=1;E<i.length;E+=1){const u=t.indexOf(i[E],g);u!==-1&&(g=u+i[E].length)}a.push([p,g]),l=g}}}return}if(h===null&&typeof c[0]=="string"){const n=c[0];n.startsWith(m)&&e.push(n.slice(m.length));const o=`<!--${n}-->`,i=t.indexOf(o,l);i!==-1&&(l=i+o.length);return}for(const n of c)Array.isArray(n)&&f(n)},"walk");for(const r of s.nodes)f(r);return{scriptBlocks:e,codeRanges:a}},"walkAst"),w=d((s,t)=>{let e=0;const a=d(r=>{for(;e<t.length&&t[e][1]<=r;)e+=1;if(e>=t.length)return!1;const[h,c]=t[e];return r>=h&&r<c},"isInCode"),l=[];let f=0;for(;f<s.length;){const r=s.indexOf(O,f);if(r===-1)break;const h=r+O.length,c=s.indexOf(A,h);if(c===-1)break;const n=c+A.length,o=s.indexOf(M,n);if(o===-1){f=n;continue}const i=o+M.length;a(r)||l.push({expression:s.slice(h,c),contentStart:n,contentEnd:o}),f=i}return l},"findMarkers"),y=d(s=>{if(!s.includes(k))return{scriptBlocks:[],markers:[]};const t=R(s),{scriptBlocks:e,codeRanges:a}=S(t,s);return{scriptBlocks:e,markers:w(s,a)}},"parseMarkdown");export{k as C,C as E,O as M,M as a,P as b,y as p};
|