@johnhenry/sitefile 0.0.0-stage → 0.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.
Files changed (174) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/LICENSE +21 -0
  3. package/README.md +475 -2
  4. package/lib/src/archive-writers.d.ts +31 -0
  5. package/lib/src/archive-writers.js +197 -0
  6. package/lib/src/attest.d.ts +126 -0
  7. package/lib/src/attest.js +220 -0
  8. package/lib/src/bake.d.ts +74 -0
  9. package/lib/src/bake.js +372 -0
  10. package/lib/src/base-image.d.ts +76 -0
  11. package/lib/src/base-image.js +138 -0
  12. package/lib/src/build-args.d.ts +26 -0
  13. package/lib/src/build-args.js +164 -0
  14. package/lib/src/cache-limit.d.ts +54 -0
  15. package/lib/src/cache-limit.js +239 -0
  16. package/lib/src/casefold-data.d.ts +2 -0
  17. package/lib/src/casefold-data.js +217 -0
  18. package/lib/src/cbor.d.ts +39 -0
  19. package/lib/src/cbor.js +174 -0
  20. package/lib/src/checklinks.d.ts +42 -0
  21. package/lib/src/checklinks.js +118 -0
  22. package/lib/src/cli/bake.d.ts +2 -0
  23. package/lib/src/cli/bake.js +179 -0
  24. package/lib/src/cli/build-image.d.ts +21 -0
  25. package/lib/src/cli/build-image.js +42 -0
  26. package/lib/src/cli/cache.d.ts +8 -0
  27. package/lib/src/cli/cache.js +109 -0
  28. package/lib/src/cli/common.d.ts +29 -0
  29. package/lib/src/cli/common.js +100 -0
  30. package/lib/src/cli/git-context.d.ts +18 -0
  31. package/lib/src/cli/git-context.js +48 -0
  32. package/lib/src/cli/images.d.ts +6 -0
  33. package/lib/src/cli/images.js +285 -0
  34. package/lib/src/cli/index.d.ts +10 -0
  35. package/lib/src/cli/index.js +95 -0
  36. package/lib/src/cli/outdated.d.ts +5 -0
  37. package/lib/src/cli/outdated.js +94 -0
  38. package/lib/src/cli/registry.d.ts +2 -0
  39. package/lib/src/cli/registry.js +122 -0
  40. package/lib/src/cli/serve-worker.d.ts +2 -0
  41. package/lib/src/cli/serve-worker.js +42 -0
  42. package/lib/src/cli/servers.d.ts +15 -0
  43. package/lib/src/cli/servers.js +451 -0
  44. package/lib/src/cli/vendor.d.ts +2 -0
  45. package/lib/src/cli/vendor.js +92 -0
  46. package/lib/src/cli/watch.d.ts +26 -0
  47. package/lib/src/cli/watch.js +81 -0
  48. package/lib/src/cli.d.ts +12 -0
  49. package/lib/src/cli.js +486 -0
  50. package/lib/src/csv.d.ts +27 -0
  51. package/lib/src/csv.js +117 -0
  52. package/lib/src/deflate.d.ts +76 -0
  53. package/lib/src/deflate.js +604 -0
  54. package/lib/src/digest.d.ts +3 -0
  55. package/lib/src/digest.js +7 -0
  56. package/lib/src/errors.d.ts +34 -0
  57. package/lib/src/errors.js +20 -0
  58. package/lib/src/executor.d.ts +386 -0
  59. package/lib/src/executor.js +2193 -0
  60. package/lib/src/explain.d.ts +64 -0
  61. package/lib/src/explain.js +445 -0
  62. package/lib/src/export-formats.d.ts +16 -0
  63. package/lib/src/export-formats.js +52 -0
  64. package/lib/src/exporter.d.ts +103 -0
  65. package/lib/src/exporter.js +676 -0
  66. package/lib/src/fingerprint.d.ts +40 -0
  67. package/lib/src/fingerprint.js +168 -0
  68. package/lib/src/frontmatter.d.ts +22 -0
  69. package/lib/src/frontmatter.js +45 -0
  70. package/lib/src/index.d.ts +12 -0
  71. package/lib/src/index.js +12 -0
  72. package/lib/src/language/ast.d.ts +353 -0
  73. package/lib/src/language/ast.js +57 -0
  74. package/lib/src/language/parser.d.ts +3 -0
  75. package/lib/src/language/parser.js +612 -0
  76. package/lib/src/language/scanner.d.ts +110 -0
  77. package/lib/src/language/scanner.js +449 -0
  78. package/lib/src/language/validate.d.ts +13 -0
  79. package/lib/src/language/validate.js +367 -0
  80. package/lib/src/links.d.ts +79 -0
  81. package/lib/src/links.js +583 -0
  82. package/lib/src/lint.d.ts +10 -0
  83. package/lib/src/lint.js +223 -0
  84. package/lib/src/markdown.d.ts +29 -0
  85. package/lib/src/markdown.js +612 -0
  86. package/lib/src/media.d.ts +82 -0
  87. package/lib/src/media.js +650 -0
  88. package/lib/src/memo.d.ts +57 -0
  89. package/lib/src/memo.js +93 -0
  90. package/lib/src/oci.d.ts +178 -0
  91. package/lib/src/oci.js +380 -0
  92. package/lib/src/outdated.d.ts +46 -0
  93. package/lib/src/outdated.js +195 -0
  94. package/lib/src/pages.d.ts +10 -0
  95. package/lib/src/pages.js +54 -0
  96. package/lib/src/paths.d.ts +39 -0
  97. package/lib/src/paths.js +101 -0
  98. package/lib/src/planner.d.ts +297 -0
  99. package/lib/src/planner.js +410 -0
  100. package/lib/src/plugins/cache.d.ts +70 -0
  101. package/lib/src/plugins/cache.js +226 -0
  102. package/lib/src/plugins/exec.d.ts +51 -0
  103. package/lib/src/plugins/exec.js +144 -0
  104. package/lib/src/plugins/host.d.ts +75 -0
  105. package/lib/src/plugins/host.js +277 -0
  106. package/lib/src/plugins/wasm.d.ts +23 -0
  107. package/lib/src/plugins/wasm.js +249 -0
  108. package/lib/src/profile.d.ts +42 -0
  109. package/lib/src/profile.js +68 -0
  110. package/lib/src/ref.d.ts +40 -0
  111. package/lib/src/ref.js +79 -0
  112. package/lib/src/registry-auth.d.ts +32 -0
  113. package/lib/src/registry-auth.js +151 -0
  114. package/lib/src/registry-cache.d.ts +71 -0
  115. package/lib/src/registry-cache.js +333 -0
  116. package/lib/src/registry-ops.d.ts +51 -0
  117. package/lib/src/registry-ops.js +248 -0
  118. package/lib/src/registry.d.ts +109 -0
  119. package/lib/src/registry.js +505 -0
  120. package/lib/src/render.d.ts +134 -0
  121. package/lib/src/render.js +576 -0
  122. package/lib/src/resources/archive.d.ts +11 -0
  123. package/lib/src/resources/archive.js +306 -0
  124. package/lib/src/resources/bundle-data.d.ts +16 -0
  125. package/lib/src/resources/bundle-data.js +50 -0
  126. package/lib/src/resources/convert.d.ts +12 -0
  127. package/lib/src/resources/convert.js +63 -0
  128. package/lib/src/resources/documents.d.ts +35 -0
  129. package/lib/src/resources/documents.js +122 -0
  130. package/lib/src/resources/git.d.ts +103 -0
  131. package/lib/src/resources/git.js +720 -0
  132. package/lib/src/resources/https.d.ts +40 -0
  133. package/lib/src/resources/https.js +256 -0
  134. package/lib/src/resources/ignore.d.ts +33 -0
  135. package/lib/src/resources/ignore.js +119 -0
  136. package/lib/src/resources/local.d.ts +64 -0
  137. package/lib/src/resources/local.js +248 -0
  138. package/lib/src/resources/lockfile.d.ts +52 -0
  139. package/lib/src/resources/lockfile.js +172 -0
  140. package/lib/src/resources/netpolicy.d.ts +6 -0
  141. package/lib/src/resources/netpolicy.js +82 -0
  142. package/lib/src/resources/types.d.ts +63 -0
  143. package/lib/src/resources/types.js +13 -0
  144. package/lib/src/resources/vendor.d.ts +94 -0
  145. package/lib/src/resources/vendor.js +296 -0
  146. package/lib/src/resources/wbn-reader.d.ts +44 -0
  147. package/lib/src/resources/wbn-reader.js +302 -0
  148. package/lib/src/schema.d.ts +25 -0
  149. package/lib/src/schema.js +169 -0
  150. package/lib/src/secrets.d.ts +21 -0
  151. package/lib/src/secrets.js +103 -0
  152. package/lib/src/serve-rules.d.ts +29 -0
  153. package/lib/src/serve-rules.js +122 -0
  154. package/lib/src/server.d.ts +45 -0
  155. package/lib/src/server.js +246 -0
  156. package/lib/src/servers.d.ts +71 -0
  157. package/lib/src/servers.js +139 -0
  158. package/lib/src/site.d.ts +60 -0
  159. package/lib/src/site.js +169 -0
  160. package/lib/src/store.d.ts +151 -0
  161. package/lib/src/store.js +596 -0
  162. package/lib/src/tree.d.ts +50 -0
  163. package/lib/src/tree.js +134 -0
  164. package/lib/src/values.d.ts +7 -0
  165. package/lib/src/values.js +37 -0
  166. package/lib/src/vendor.d.ts +19 -0
  167. package/lib/src/vendor.js +145 -0
  168. package/lib/src/watch.d.ts +44 -0
  169. package/lib/src/watch.js +137 -0
  170. package/lib/src/wbn.d.ts +5 -0
  171. package/lib/src/wbn.js +147 -0
  172. package/lib/src/yaml.d.ts +34 -0
  173. package/lib/src/yaml.js +562 -0
  174. package/package.json +58 -4
@@ -0,0 +1,612 @@
1
+ /**
2
+ * A small, dependency-free, deterministic Markdown renderer (CommonMark-ish).
3
+ *
4
+ * Blocks: ATX headings (#..######, optional closing #s), setext headings,
5
+ * paragraphs (hard break = two trailing spaces or a trailing backslash),
6
+ * fenced code (``` and ~~~, info word becomes class="language-x" when it is
7
+ * [A-Za-z0-9_+-]+), indented code, blockquotes (recursive, lazy continuation),
8
+ * bullet lists (- * +) and ordered lists (1. / 1), `start` kept when not 1),
9
+ * nesting by indentation, tight vs loose lists, thematic breaks (--- *** ___).
10
+ *
11
+ * Inlines: backslash escapes, code spans, **strong** / __strong__, *em* /
12
+ * _em_ (intraword `_` is literal), ~~del~~, [text](url "title"),
13
+ * ![alt](url "title"), <autolinks>. Emphasis that does not close stays literal.
14
+ *
15
+ * Not supported: reference links, HTML entities (`&` is always escaped, so
16
+ * `&amp;` shows literally), tables, footnotes, task lists.
17
+ *
18
+ * Safety: raw HTML is never passed through; every `<` in the source is
19
+ * escaped. Link and image URLs must be http, https, mailto, relative,
20
+ * `#fragment` or `/absolute`; anything else (javascript:, data:, ...) is
21
+ * rendered as plain text without an <a> / <img>.
22
+ */
23
+ const MAX_DEPTH = 32;
24
+ const EXCERPT_MAX = 200;
25
+ // ---------------------------------------------------------------- escaping
26
+ export function escapeHtml(s) {
27
+ return s.replace(/[&<>"']/g, (c) => (c === '&' ? '&amp;' : c === '<' ? '&lt;' : c === '>' ? '&gt;' : c === '"' ? '&quot;' : '&#39;'));
28
+ }
29
+ /** Scheme check on a URL stripped of whitespace/control characters. */
30
+ function isSafeUrl(url) {
31
+ const stripped = url.replace(/[\u0000-\u0020\u007f-\u009f\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, '');
32
+ const m = /^([^:/?#]*):/.exec(stripped);
33
+ return m === null || /^(https?|mailto)$/i.test(m[1] ?? '');
34
+ }
35
+ function encodeUrl(url) {
36
+ return escapeHtml(url.replace(/[\u0000-\u001f\u007f]/g, '').replace(/ /g, '%20'));
37
+ }
38
+ const unescapeMd = (s) => s.replace(/\\([!-/:-@[-`{-~])/g, '$1');
39
+ const isPunct = (c) => c !== undefined && /[\p{P}\p{S}]/u.test(c);
40
+ const isSpace = (c) => c === undefined || /\s/.test(c);
41
+ /** Index of the `]` matching an already-opened `[` (scan starts at `from`), or -1. */
42
+ function findCloseBracket(s, from) {
43
+ let depth = 1;
44
+ for (let j = from; j < s.length; j++) {
45
+ const c = s[j];
46
+ if (c === '\\')
47
+ j++;
48
+ else if (c === '`') {
49
+ const run = /^`+/.exec(s.slice(j))?.[0] ?? '`';
50
+ const end = s.indexOf(run, j + run.length);
51
+ j = end === -1 ? j + run.length - 1 : end + run.length - 1;
52
+ }
53
+ else if (c === '[')
54
+ depth++;
55
+ else if (c === ']' && --depth === 0)
56
+ return j;
57
+ }
58
+ return -1;
59
+ }
60
+ /** Parse `(dest "title")` starting at the `(` at s[k]. */
61
+ function parseDestination(s, k) {
62
+ const ws = (i) => {
63
+ while (i < s.length && /[ \t\n]/.test(s[i]))
64
+ i++;
65
+ return i;
66
+ };
67
+ let i = ws(k + 1);
68
+ let href;
69
+ if (s[i] === '<') {
70
+ let j = i + 1;
71
+ while (j < s.length && s[j] !== '>' && s[j] !== '<' && s[j] !== '\n')
72
+ j++;
73
+ if (s[j] !== '>')
74
+ return null;
75
+ href = unescapeMd(s.slice(i + 1, j));
76
+ i = j + 1;
77
+ }
78
+ else {
79
+ const start = i;
80
+ let depth = 0;
81
+ while (i < s.length) {
82
+ const c = s[i];
83
+ if (c === '\\' && i + 1 < s.length) {
84
+ i += 2;
85
+ continue;
86
+ }
87
+ if (/[\s\u0000-\u001f]/.test(c))
88
+ break;
89
+ if (c === '(')
90
+ depth++;
91
+ else if (c === ')') {
92
+ if (depth === 0)
93
+ break;
94
+ depth--;
95
+ }
96
+ i++;
97
+ }
98
+ if (depth !== 0)
99
+ return null;
100
+ href = unescapeMd(s.slice(start, i));
101
+ }
102
+ const afterDest = i;
103
+ i = ws(i);
104
+ if (s[i] === ')')
105
+ return { href, title: null, end: i + 1 };
106
+ const open = s[i];
107
+ const close = open === '"' ? '"' : open === "'" ? "'" : open === '(' ? ')' : null;
108
+ if (close === null || i === afterDest)
109
+ return null;
110
+ let j = i + 1;
111
+ while (j < s.length && s[j] !== close)
112
+ j += s[j] === '\\' ? 2 : 1;
113
+ if (j >= s.length)
114
+ return null;
115
+ const title = unescapeMd(s.slice(i + 1, j));
116
+ j = ws(j + 1);
117
+ return s[j] === ')' ? { href, title, end: j + 1 } : null;
118
+ }
119
+ function parseInline(s, depth) {
120
+ const items = [];
121
+ let buf = '';
122
+ const flush = () => {
123
+ if (buf !== '')
124
+ items.push({ t: 'text', v: buf });
125
+ buf = '';
126
+ };
127
+ let i = 0;
128
+ while (i < s.length) {
129
+ const c = s[i];
130
+ const next = s[i + 1];
131
+ if (c === '\\') {
132
+ if (next === '\n') {
133
+ flush();
134
+ items.push({ t: 'br' });
135
+ i += 2;
136
+ }
137
+ else if (next !== undefined && /[!-/:-@[-`{-~]/.test(next)) {
138
+ buf += next;
139
+ i += 2;
140
+ }
141
+ else {
142
+ buf += c;
143
+ i++;
144
+ }
145
+ }
146
+ else if (c === '\n') {
147
+ const spaces = /( *)$/.exec(buf)?.[1]?.length ?? 0;
148
+ buf = buf.slice(0, buf.length - spaces);
149
+ if (spaces >= 2) {
150
+ flush();
151
+ items.push({ t: 'br' });
152
+ }
153
+ else {
154
+ buf += '\n';
155
+ }
156
+ i++;
157
+ }
158
+ else if (c === '`') {
159
+ const run = /^`+/.exec(s.slice(i))?.[0] ?? '`';
160
+ let end = -1;
161
+ for (let j = s.indexOf('`', i + run.length); j !== -1;) {
162
+ const r = /^`+/.exec(s.slice(j))?.[0] ?? '`';
163
+ if (r.length === run.length) {
164
+ end = j;
165
+ break;
166
+ }
167
+ j = s.indexOf('`', j + r.length);
168
+ }
169
+ if (end === -1) {
170
+ buf += run;
171
+ i += run.length;
172
+ }
173
+ else {
174
+ let code = s.slice(i + run.length, end).replace(/\n/g, ' ');
175
+ if (code.length > 2 && code.startsWith(' ') && code.endsWith(' ') && code.trim() !== '')
176
+ code = code.slice(1, -1);
177
+ flush();
178
+ items.push({ t: 'code', v: code });
179
+ i = end + run.length;
180
+ }
181
+ }
182
+ else if (c === '*' || c === '_' || c === '~') {
183
+ let n = 1;
184
+ while (s[i + n] === c)
185
+ n++;
186
+ if (c === '~' && n !== 2) {
187
+ buf += c.repeat(n);
188
+ i += n;
189
+ continue;
190
+ }
191
+ const before = s[i - 1];
192
+ const after = s[i + n];
193
+ const left = !isSpace(after) && (!isPunct(after) || isSpace(before) || isPunct(before));
194
+ const right = !isSpace(before) && (!isPunct(before) || isSpace(after) || isPunct(after));
195
+ const open = c === '_' ? left && (!right || isPunct(before)) : left;
196
+ const close = c === '_' ? right && (!left || isPunct(after)) : right;
197
+ flush();
198
+ items.push({ t: 'delim', ch: c, n, orig: n, open, close });
199
+ i += n;
200
+ }
201
+ else if (c === '[' || (c === '!' && next === '[')) {
202
+ const isImg = c === '!';
203
+ const labelStart = i + (isImg ? 2 : 1);
204
+ const closeIdx = depth < MAX_DEPTH ? findCloseBracket(s, labelStart) : -1;
205
+ const dest = closeIdx !== -1 && s[closeIdx + 1] === '(' ? parseDestination(s, closeIdx + 1) : null;
206
+ if (closeIdx === -1 || dest === null) {
207
+ buf += isImg ? '![' : '[';
208
+ i = labelStart;
209
+ continue;
210
+ }
211
+ const children = parseInline(s.slice(labelStart, closeIdx), depth + 1);
212
+ flush();
213
+ if (isImg) {
214
+ const alt = nodesToText(children);
215
+ items.push(isSafeUrl(dest.href) ? { t: 'img', src: dest.href, alt, title: dest.title } : { t: 'text', v: alt });
216
+ }
217
+ else if (isSafeUrl(dest.href)) {
218
+ items.push({ t: 'link', href: dest.href, title: dest.title, c: children });
219
+ }
220
+ else {
221
+ items.push(...children);
222
+ }
223
+ i = dest.end;
224
+ }
225
+ else if (c === '<') {
226
+ const auto = /^<([A-Za-z][A-Za-z0-9+.-]{1,31}:[^\s<>]*)>/.exec(s.slice(i));
227
+ const mail = /^<([A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+)>/.exec(s.slice(i));
228
+ if (auto && isSafeUrl(auto[1])) {
229
+ flush();
230
+ items.push({ t: 'link', href: auto[1], title: null, c: [{ t: 'text', v: auto[1] }] });
231
+ i += auto[0].length;
232
+ }
233
+ else if (mail) {
234
+ flush();
235
+ items.push({ t: 'link', href: `mailto:${mail[1]}`, title: null, c: [{ t: 'text', v: mail[1] }] });
236
+ i += mail[0].length;
237
+ }
238
+ else {
239
+ buf += c;
240
+ i++;
241
+ }
242
+ }
243
+ else {
244
+ buf += c;
245
+ i++;
246
+ }
247
+ }
248
+ flush();
249
+ return resolveEmphasis(items);
250
+ }
251
+ const delimToText = (it) => (it.t === 'delim' ? { t: 'text', v: it.ch.repeat(it.n) } : it);
252
+ /** CommonMark-style delimiter matching, simplified. */
253
+ function resolveEmphasis(items) {
254
+ for (let ci = 0; ci < items.length; ci++) {
255
+ const closer = items[ci];
256
+ if (closer?.t !== 'delim' || !closer.close)
257
+ continue;
258
+ while (closer.n > 0) {
259
+ let oi = -1;
260
+ for (let k = ci - 1; k >= 0; k--) {
261
+ const o = items[k];
262
+ if (o?.t !== 'delim' || o.ch !== closer.ch || !o.open || o.n === 0)
263
+ continue;
264
+ const rule3 = (o.close || closer.open) && (o.orig + closer.orig) % 3 === 0 && !(o.orig % 3 === 0 && closer.orig % 3 === 0);
265
+ if (rule3)
266
+ continue;
267
+ oi = k;
268
+ break;
269
+ }
270
+ if (oi === -1)
271
+ break;
272
+ const opener = items[oi];
273
+ const use = closer.ch === '~' ? 2 : opener.n >= 2 && closer.n >= 2 ? 2 : 1;
274
+ const tag = closer.ch === '~' ? 'del' : use === 2 ? 'strong' : 'em';
275
+ const inner = items.slice(oi + 1, ci).map(delimToText);
276
+ opener.n -= use;
277
+ closer.n -= use;
278
+ items.splice(oi + 1, ci - oi - 1, { t: 'wrap', tag, c: inner });
279
+ ci = oi + 2;
280
+ if (opener.n === 0) {
281
+ items.splice(oi, 1);
282
+ ci--;
283
+ }
284
+ if (closer.n === 0) {
285
+ items.splice(ci, 1);
286
+ ci--;
287
+ break;
288
+ }
289
+ }
290
+ }
291
+ return items.map(delimToText);
292
+ }
293
+ function nodesToText(nodes) {
294
+ return nodes
295
+ .map((n) => {
296
+ switch (n.t) {
297
+ case 'text':
298
+ case 'code':
299
+ return n.v;
300
+ case 'br':
301
+ return ' ';
302
+ case 'img':
303
+ return n.alt;
304
+ default:
305
+ return nodesToText(n.c);
306
+ }
307
+ })
308
+ .join('');
309
+ }
310
+ function nodesToHtml(nodes) {
311
+ return nodes
312
+ .map((n) => {
313
+ switch (n.t) {
314
+ case 'text':
315
+ return escapeHtml(n.v);
316
+ case 'code':
317
+ return `<code>${escapeHtml(n.v)}</code>`;
318
+ case 'br':
319
+ return '<br>\n';
320
+ case 'wrap':
321
+ return `<${n.tag}>${nodesToHtml(n.c)}</${n.tag}>`;
322
+ case 'link':
323
+ return `<a href="${encodeUrl(n.href)}"${n.title === null ? '' : ` title="${escapeHtml(n.title)}"`}>${nodesToHtml(n.c)}</a>`;
324
+ case 'img':
325
+ return `<img src="${encodeUrl(n.src)}" alt="${escapeHtml(n.alt)}"${n.title === null ? '' : ` title="${escapeHtml(n.title)}"`}>`;
326
+ }
327
+ })
328
+ .join('');
329
+ }
330
+ const FENCE_RE = /^( {0,3})(`{3,}|~{3,})(.*)$/;
331
+ const ATX_RE = /^ {0,3}(#{1,6})(?:[ \t]+(.*)|[ \t]*)$/;
332
+ const HR_RE = /^ {0,3}([-*_])(?:[ \t]*\1){2,}[ \t]*$/;
333
+ const LIST_RE = /^( {0,3})([-*+]|\d{1,9}[.)])( +|$)(.*)$/;
334
+ const QUOTE_RE = /^ {0,3}>/;
335
+ const isBlank = (l) => l.trim() === '';
336
+ const indentOf = (l) => /^ */.exec(l)?.[0].length ?? 0;
337
+ const isHr = (l) => HR_RE.test(l);
338
+ const listKey = (m) => m[2].slice(-1);
339
+ /** Can this line interrupt a paragraph? */
340
+ function interruptsParagraph(l) {
341
+ const fence = FENCE_RE.exec(l);
342
+ if (fence && !(fence[2]?.startsWith('`') && (fence[3] ?? '').includes('`')))
343
+ return true;
344
+ if (ATX_RE.test(l) || isHr(l) || QUOTE_RE.test(l))
345
+ return true;
346
+ const m = LIST_RE.exec(l);
347
+ return m !== null && (m[4] ?? '') !== '' && (/^[-*+]/.test(m[2]) || parseInt(m[2], 10) === 1);
348
+ }
349
+ function makeParagraph(lines, st) {
350
+ const text = lines.map((l) => l.trimStart()).join('\n').trimEnd();
351
+ const nodes = parseInline(text, 0);
352
+ if (st.excerpt === null) {
353
+ const plain = nodesToText(nodes).replace(/\s+/g, ' ').trim();
354
+ if (plain !== '')
355
+ st.excerpt = plain;
356
+ }
357
+ const inner = nodesToHtml(nodes);
358
+ return { html: `<p>${inner}</p>`, para: inner, blankBefore: false };
359
+ }
360
+ function parseBlocks(lines, st, depth) {
361
+ if (depth > MAX_DEPTH) {
362
+ const text = lines.filter((l) => !isBlank(l));
363
+ return text.length ? [makeParagraph(text, st)] : [];
364
+ }
365
+ const out = [];
366
+ let blank = false;
367
+ const push = (b) => {
368
+ out.push({ ...b, blankBefore: blank && out.length > 0 });
369
+ blank = false;
370
+ };
371
+ let i = 0;
372
+ while (i < lines.length) {
373
+ const line = lines[i];
374
+ if (isBlank(line)) {
375
+ blank = true;
376
+ i++;
377
+ continue;
378
+ }
379
+ // fenced code
380
+ const fence = FENCE_RE.exec(line);
381
+ if (fence && !(fence[2]?.startsWith('`') && (fence[3] ?? '').includes('`'))) {
382
+ const indent = fence[1]?.length ?? 0;
383
+ const marker = fence[2];
384
+ const closeRe = new RegExp(`^ {0,3}${marker[0] === '`' ? '`' : '~'}{${marker.length},}[ \\t]*$`);
385
+ const info = (fence[3] ?? '').trim().split(/\s+/)[0] ?? '';
386
+ const body = [];
387
+ i++;
388
+ while (i < lines.length && !closeRe.test(lines[i])) {
389
+ const l = lines[i];
390
+ body.push(l.slice(Math.min(indent, indentOf(l))));
391
+ i++;
392
+ }
393
+ i++; // closing fence (or past the end for an unterminated one)
394
+ push(codeBlock(body, info));
395
+ continue;
396
+ }
397
+ // ATX heading
398
+ const atx = ATX_RE.exec(line);
399
+ if (atx) {
400
+ const level = atx[1].length;
401
+ const content = (atx[2] ?? '').replace(/(^|[ \t]+)#+[ \t]*$/, '').trim();
402
+ push(heading(level, content));
403
+ i++;
404
+ continue;
405
+ }
406
+ if (isHr(line)) {
407
+ push({ html: '<hr>', blankBefore: false });
408
+ i++;
409
+ continue;
410
+ }
411
+ // blockquote
412
+ if (QUOTE_RE.test(line)) {
413
+ const inner = [];
414
+ while (i < lines.length) {
415
+ const l = lines[i];
416
+ if (QUOTE_RE.test(l)) {
417
+ inner.push(l.replace(/^ {0,3}> ?/, ''));
418
+ }
419
+ else if (!isBlank(l) && inner.length > 0 && !isBlank(inner[inner.length - 1]) && !interruptsParagraph(l)) {
420
+ inner.push(l); // lazy continuation
421
+ }
422
+ else
423
+ break;
424
+ i++;
425
+ }
426
+ const blocks = parseBlocks(inner, st, depth + 1);
427
+ push({ html: `<blockquote>\n${blocks.map((b) => b.html + '\n').join('')}</blockquote>`, blankBefore: false });
428
+ continue;
429
+ }
430
+ // list
431
+ const lm = LIST_RE.exec(line);
432
+ if (lm) {
433
+ const res = parseList(lines, i, st, depth);
434
+ push(res.block);
435
+ i = res.next;
436
+ continue;
437
+ }
438
+ // indented code
439
+ if (indentOf(line) >= 4) {
440
+ const body = [];
441
+ while (i < lines.length && (isBlank(lines[i]) || indentOf(lines[i]) >= 4)) {
442
+ body.push(lines[i].slice(4));
443
+ i++;
444
+ }
445
+ while (body.length && isBlank(body[body.length - 1]))
446
+ body.pop();
447
+ push(codeBlock(body, ''));
448
+ continue;
449
+ }
450
+ // paragraph / setext heading
451
+ const para = [line];
452
+ i++;
453
+ let setext = 0;
454
+ while (i < lines.length) {
455
+ const l = lines[i];
456
+ if (isBlank(l))
457
+ break;
458
+ if (/^ {0,3}=+[ \t]*$/.test(l))
459
+ setext = 1;
460
+ else if (/^ {0,3}-+[ \t]*$/.test(l))
461
+ setext = 2;
462
+ if (setext) {
463
+ i++;
464
+ break;
465
+ }
466
+ if (interruptsParagraph(l))
467
+ break;
468
+ para.push(l);
469
+ i++;
470
+ }
471
+ if (setext) {
472
+ const text = para.map((l) => l.trim()).join('\n');
473
+ push(heading(setext, text, para));
474
+ }
475
+ else {
476
+ push(makeParagraph(para, st));
477
+ }
478
+ }
479
+ return out;
480
+ function heading(level, content, src) {
481
+ if (src) {
482
+ const p = makeParagraph(src, st); // records the excerpt like a paragraph
483
+ return { html: `<h${level}>${p.para}</h${level}>`, blankBefore: false };
484
+ }
485
+ return { html: `<h${level}>${nodesToHtml(parseInline(content, 0))}</h${level}>`, blankBefore: false };
486
+ }
487
+ }
488
+ function codeBlock(body, info) {
489
+ const cls = /^[A-Za-z0-9_+-]+$/.test(info) ? ` class="language-${info}"` : '';
490
+ const text = body.length ? escapeHtml(body.join('\n')) + '\n' : '';
491
+ return { html: `<pre><code${cls}>${text}</code></pre>`, blankBefore: false };
492
+ }
493
+ function parseList(lines, start, st, depth) {
494
+ const first = LIST_RE.exec(lines[start]);
495
+ const key = listKey(first);
496
+ const ordered = /\d/.test(first[2]);
497
+ const startNum = ordered ? parseInt(first[2], 10) : 1;
498
+ const items = [];
499
+ let loose = false;
500
+ let i = start;
501
+ const siblingAt = (idx) => {
502
+ const l = lines[idx];
503
+ if (l === undefined || isHr(l))
504
+ return null;
505
+ const m = LIST_RE.exec(l);
506
+ return m && listKey(m) === key ? m : null;
507
+ };
508
+ while (i < lines.length) {
509
+ const m = siblingAt(i);
510
+ if (!m)
511
+ break;
512
+ const base = m[1].length;
513
+ const markerLen = m[2].length;
514
+ const spaces = m[3].length;
515
+ let rest = m[4];
516
+ let contentIndent;
517
+ if (rest === '')
518
+ contentIndent = base + markerLen + 1;
519
+ else if (spaces > 4) {
520
+ contentIndent = base + markerLen + 1;
521
+ rest = ' '.repeat(spaces - 1) + rest;
522
+ }
523
+ else
524
+ contentIndent = base + markerLen + spaces;
525
+ const body = [rest];
526
+ i++;
527
+ while (i < lines.length) {
528
+ const l = lines[i];
529
+ if (isBlank(l)) {
530
+ let j = i;
531
+ while (j < lines.length && isBlank(lines[j]))
532
+ j++;
533
+ if (j < lines.length && indentOf(lines[j]) >= contentIndent) {
534
+ for (; i < j; i++)
535
+ body.push('');
536
+ continue;
537
+ }
538
+ break;
539
+ }
540
+ if (indentOf(l) >= contentIndent) {
541
+ body.push(l.slice(contentIndent));
542
+ }
543
+ else if (siblingAt(i) || LIST_RE.test(l) || isHr(l) || interruptsParagraph(l) || isBlank(body[body.length - 1])) {
544
+ break;
545
+ }
546
+ else {
547
+ body.push(l.trimStart()); // lazy continuation
548
+ }
549
+ i++;
550
+ }
551
+ items.push(body);
552
+ let j = i;
553
+ while (j < lines.length && isBlank(lines[j]))
554
+ j++;
555
+ if (j > i && siblingAt(j)) {
556
+ loose = true;
557
+ i = j;
558
+ }
559
+ else if (j === i)
560
+ continue;
561
+ else
562
+ break;
563
+ }
564
+ const rendered = items.map((body) => parseBlocks(body, st, depth + 1));
565
+ if (rendered.some((blocks) => blocks.some((b, k) => k > 0 && b.blankBefore)))
566
+ loose = true;
567
+ const lis = rendered.map((blocks) => {
568
+ if (blocks.length === 0)
569
+ return '<li></li>';
570
+ let inner = '';
571
+ blocks.forEach((b, k) => {
572
+ if (!loose && b.para !== undefined)
573
+ inner += (k > 0 ? '\n' : '') + b.para;
574
+ else
575
+ inner += '\n' + b.html;
576
+ });
577
+ const last = blocks[blocks.length - 1];
578
+ if (loose || last.para === undefined)
579
+ inner += '\n';
580
+ return `<li>${inner}</li>`;
581
+ });
582
+ const tag = ordered ? 'ol' : 'ul';
583
+ const attr = ordered && startNum !== 1 ? ` start="${startNum}"` : '';
584
+ return { block: { html: `<${tag}${attr}>\n${lis.join('\n')}\n</${tag}>`, blankBefore: false }, next: i };
585
+ }
586
+ // ------------------------------------------------------------------ excerpt
587
+ function truncate(text) {
588
+ if (text.length <= EXCERPT_MAX)
589
+ return text;
590
+ let cut = text.slice(0, EXCERPT_MAX - 1);
591
+ if (text[EXCERPT_MAX - 1] !== ' ') {
592
+ const sp = cut.lastIndexOf(' ');
593
+ if (sp > 0)
594
+ cut = cut.slice(0, sp);
595
+ }
596
+ cut = cut.trimEnd();
597
+ if (/[\ud800-\udbff]$/.test(cut))
598
+ cut = cut.slice(0, -1);
599
+ return cut + '…';
600
+ }
601
+ export function renderMarkdown(src) {
602
+ const normalized = src
603
+ .replace(/\r\n?/g, '\n')
604
+ .replace(/\u0000/g, '�')
605
+ .split('\n')
606
+ .map((l) => l.replace(/^[ \t]+/, (ws) => ws.replace(/\t/g, ' ')));
607
+ const st = { excerpt: null };
608
+ const blocks = parseBlocks(normalized, st, 0);
609
+ const html = blocks.map((b) => b.html + '\n').join('');
610
+ return { html, excerpt: truncate(st.excerpt ?? '') };
611
+ }
612
+ //# sourceMappingURL=markdown.js.map
@@ -0,0 +1,82 @@
1
+ import { type SourceSpan } from './errors.js';
2
+ /** Helpers that produce a value (escaped like any substituted value). */
3
+ export declare const VALUE_HELPERS: readonly ['url', 'width', 'height', 'type', 'size', 'integrity', 'datauri', 'srcset'];
4
+ /** Helpers that write a tag (trusted markup: inserted as-is; every attribute value is escaped). */
5
+ export declare const TAG_HELPERS: readonly ['img', 'video', 'audio', 'source', 'script', 'stylesheet', 'preload', 'modulepreload', 'pre', 'svg'];
6
+ export declare const FILE_HELPERS: readonly string[];
7
+ /** The largest file `datauri` inlines. */
8
+ export declare const DATAURI_MAX = 32768;
9
+ /** The lowercase extension of a path's last segment ('' if none). */
10
+ export declare function extensionOf(path: string): string;
11
+ /** The media type of a path, by extension (`application/octet-stream` when unknown). */
12
+ export declare function mediaType(path: string): string;
13
+ export declare function isImagePath(path: string): boolean;
14
+ /** The URL of a tree path: each segment percent-encoded like `encodeURIComponent`. */
15
+ export declare function fileUrl(path: string): string;
16
+ /** `sha384-<base64>`, the subresource integrity of `bytes`. */
17
+ export declare function integrityOf(bytes: Uint8Array): string;
18
+ export interface ImageSize {
19
+ readonly width: number;
20
+ readonly height: number;
21
+ }
22
+ /**
23
+ * The width and height of an image file, from its header (PNG, JPEG, GIF, WebP, AVIF): bounded reads,
24
+ * no decoding. undefined for an SVG (its size is in its markup, often relative). E_DATA when the
25
+ * bytes are no supported image, or the header is truncated or malformed.
26
+ */
27
+ export declare function imageSize(bytes: Uint8Array, path: string): ImageSize | undefined;
28
+ /** A file of the tree a helper writes markup for. */
29
+ export interface TreeFile {
30
+ /** Its normalized absolute tree path. */
31
+ readonly path: string;
32
+ readonly bytes: Uint8Array;
33
+ }
34
+ /** An attribute written in the template: `alt="…"` (value) or `controls` (bare). */
35
+ export interface HelperAttr {
36
+ readonly name: string;
37
+ readonly value?: string;
38
+ }
39
+ /** What a helper produced: markup (inserted as-is) or a value (escaped like any `${…}`). */
40
+ export type HelperOutput = {
41
+ readonly markup: string;
42
+ } | {
43
+ readonly value: string;
44
+ };
45
+ /** The files of the stage's tree at a RENDER, as `${file …}` sees them. */
46
+ export interface TemplateTree {
47
+ get(path: string): Uint8Array | undefined;
48
+ isDirectory(path: string): boolean;
49
+ /** Where a FINGERPRINT earlier in the stage moved `path` (its content-hashed name), if it did. */
50
+ renamed?(path: string): string | undefined;
51
+ }
52
+ export interface HelperOptions {
53
+ /** RENDER --inline-svg: the svg helper may paste SVG markup into the page. */
54
+ readonly inlineSvg: boolean;
55
+ }
56
+ /** The file `${file "<raw>"}` names: an absolute path in the tree (E_PATH, E_TYPE, E_NOT_FOUND otherwise). */
57
+ export declare function resolveFile(raw: string, tree: TemplateTree, span: SourceSpan): TreeFile;
58
+ /**
59
+ * What `${file "<raw>" | helper attrs…}` writes (`helper` undefined: the file's URL). A value is
60
+ * escaped by the caller like any substituted value; markup is inserted as it is.
61
+ */
62
+ export declare function renderFile(raw: string, helper: string | undefined, attrs: readonly HelperAttr[], tree: TemplateTree, opts: HelperOptions, span: SourceSpan): HelperOutput;
63
+ /** What `helper` writes for file `f` (`undefined`: no helper, the file's URL). */
64
+ export declare function renderFileHelper(helper: string | undefined, f: TreeFile, attrs: readonly HelperAttr[], opts: HelperOptions, span: SourceSpan): HelperOutput;
65
+ /** The image plugin's `<name>.json` (docs/PLUGINS.md, "The metadata file"), version 1: what the helpers read. */
66
+ export interface ImageMetadata {
67
+ /** The metadata file's tree path (for messages). */
68
+ readonly path: string;
69
+ readonly src: string;
70
+ readonly type: string;
71
+ readonly width: number;
72
+ readonly height: number;
73
+ readonly srcset: string;
74
+ readonly sources: ReadonlyArray<{
75
+ readonly type: string;
76
+ readonly srcset: string;
77
+ }>;
78
+ }
79
+ /** The metadata version this sitefile reads. */
80
+ export declare const IMAGE_METADATA_VERSION = 1;
81
+ /** `/dir/name.ext` -> `/dir/name.json` ('' for a path without an extension). */
82
+ export declare function metadataPathOf(path: string): string;