@zvndev/darkmown 2.2.0 → 2.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.
Files changed (68) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +34 -2
  3. package/docs/spec-alignment.md +10 -0
  4. package/package.json +16 -11
  5. package/site/pages/docs/index.wd +14 -0
  6. package/site/pages/playground/index.js +47 -46
  7. package/src/catalog.js +16 -1
  8. package/src/compiler/actions.js +42 -22
  9. package/src/compiler/body.js +36 -14
  10. package/src/compiler/collections.js +16 -10
  11. package/src/compiler/context.js +39 -10
  12. package/src/compiler/fetch.js +39 -15
  13. package/src/compiler/format.js +5 -2
  14. package/src/compiler/forms.js +91 -26
  15. package/src/compiler/frontmatter.js +11 -2
  16. package/src/compiler/fs-reader.js +2 -1
  17. package/src/compiler/image-size.js +340 -0
  18. package/src/compiler/includes.js +8 -4
  19. package/src/compiler/interpolation.js +15 -7
  20. package/src/compiler/loops.js +63 -35
  21. package/src/compiler/markdown.js +4 -3
  22. package/src/compiler/media.js +17 -3
  23. package/src/compiler/page.js +8 -6
  24. package/src/compiler/predicates.js +49 -24
  25. package/src/compiler/reader.js +12 -2
  26. package/src/compiler/state.js +16 -2
  27. package/src/compiler/structure.js +24 -14
  28. package/src/deploy.js +8 -4
  29. package/src/errors.js +1156 -0
  30. package/src/router.js +7 -4
  31. package/src/scaffold.js +4 -2
  32. package/src/skin.js +17 -6
  33. package/types/api-runner.d.ts +94 -0
  34. package/types/builder.d.ts +141 -10
  35. package/types/catalog.d.ts +97 -0
  36. package/types/compiler/actions.d.ts +41 -0
  37. package/types/compiler/body.d.ts +11 -0
  38. package/types/compiler/collections.d.ts +76 -0
  39. package/types/compiler/context.d.ts +661 -0
  40. package/types/compiler/directives.d.ts +6 -0
  41. package/types/compiler/expr-ast.d.ts +20 -0
  42. package/types/compiler/fetch.d.ts +29 -0
  43. package/types/compiler/format.d.ts +49 -0
  44. package/types/compiler/forms.d.ts +94 -0
  45. package/types/compiler/frontmatter.d.ts +33 -0
  46. package/types/compiler/fs-reader.d.ts +7 -0
  47. package/types/compiler/image-size.d.ts +14 -0
  48. package/types/compiler/includes.d.ts +100 -0
  49. package/types/compiler/index.d.ts +5 -0
  50. package/types/compiler/interpolation.d.ts +115 -0
  51. package/types/compiler/loops.d.ts +36 -0
  52. package/types/compiler/markdown.d.ts +34 -0
  53. package/types/compiler/media.d.ts +32 -0
  54. package/types/compiler/page.d.ts +109 -0
  55. package/types/compiler/predicates.d.ts +50 -0
  56. package/types/compiler/reader.d.ts +56 -0
  57. package/types/compiler/state.d.ts +68 -0
  58. package/types/compiler/structure.d.ts +56 -0
  59. package/types/compiler.d.ts +35 -223
  60. package/types/dev.d.ts +47 -2
  61. package/types/errors.d.ts +100 -0
  62. package/types/feeds.d.ts +150 -0
  63. package/types/headers.d.ts +44 -0
  64. package/types/highlight.d.ts +24 -0
  65. package/types/router.d.ts +42 -3
  66. package/types/scaffold.d.ts +15 -3
  67. package/types/skin.d.ts +28 -1
  68. package/types/statics.d.ts +2 -1
@@ -0,0 +1,661 @@
1
+ /**
2
+ * @typedef {import("../config.js").Paths} Paths
3
+ */
4
+ /**
5
+ * Frontmatter values are scalars or inline arrays of scalars.
6
+ * @typedef {string | number | boolean | null | Array<string | number | boolean | null>} FrontmatterValue
7
+ */
8
+ /**
9
+ * Parsed page frontmatter, keyed by field name.
10
+ * @typedef {Record<string, FrontmatterValue>} Meta
11
+ */
12
+ /**
13
+ * Collected output assets for a compiled document.
14
+ * @typedef {object} Assets
15
+ * @property {Set<string>} skins Public hrefs of compiled skin stylesheets.
16
+ * @property {Set<string>} scripts Public hrefs of colocated page scripts.
17
+ * @property {Map<string, string>} files Source path → public href for emitted assets.
18
+ * @property {boolean} runtime Whether the reactive runtime is required.
19
+ * @property {Set<string>} behaviors Names of framework behavior modules this page
20
+ * needs (`slider` is compile-time only and never appears here; `sortable`,
21
+ * `carousel` each emit a pay-for-what-you-use `/__wd/behaviors/<name>.js`).
22
+ * @property {boolean} hasCode Whether the page has a build-time-highlighted code
23
+ * block, so the framework highlight stylesheet (`/__wd/highlight.css`) is
24
+ * emitted and linked — pay-for-what-you-use, zero JS.
25
+ * @property {Set<string>} scopedSkins Source paths of colocated `.skin` files
26
+ * that opted into scoping (`scoped` first line). The builder emits these with
27
+ * the scope attribute and runs the unused-selector warning against the stamped
28
+ * subtree; the HTML stamp uses them to scope the matching page/include subtree.
29
+ */
30
+ /**
31
+ * The pagination intent a `@loop … paginate N` records during compile, so the
32
+ * builder can multiply routes (page 1 at the listing route, 2+ at
33
+ * `/<route>/page/<n>/`). `total` is the post-`where`/`sort` page count for the
34
+ * collection the loop drew from.
35
+ * @typedef {object} Pagination
36
+ * @property {number} perPage Rows per page (the `paginate N` value).
37
+ * @property {number} total Total page count (≥ 1).
38
+ * @property {string} collection Collection name the loop paginated.
39
+ */
40
+ /**
41
+ * Per-document compilation accumulator shared across includes/sections.
42
+ * @typedef {object} Compilation
43
+ * @property {Assets} assets
44
+ * @property {Map<string, unknown>} state Declared state keys → initial values.
45
+ * @property {Set<string>} stores Page-global store names (a subset of state keys).
46
+ * @property {Set<string>} fetchKeys Base state keys declared by `:fetch`, so
47
+ * bare `:if <key>_loading` / `:if <key>_error` regions get compile-time
48
+ * `role`/`aria-live` announcements.
49
+ * @property {string[]} warnings Non-fatal authoring hints.
50
+ * @property {number} sectionCounter Counter for auto-generated section ids.
51
+ * @property {Map<string, number>} headingSlugs Heading-slug occurrence counts,
52
+ * shared across prose chunks and includes so anchor ids dedupe document-wide.
53
+ * @property {Map<string, import("./collections.js").CollectionRow[]>} collections
54
+ * Collection name → entry rows, built from the router's routes and threaded in
55
+ * so a bare-name `@loop blog into post` resolves to its entries at build time.
56
+ * @property {import("./context.js").Pagination | null} pagination The pagination
57
+ * intent a `@loop … paginate N` recorded this compile, or null. The builder
58
+ * reads it to multiply routes; one paginated loop per page is supported.
59
+ * @property {Set<string>} deps Absolute paths of every source file this compile
60
+ * read: the page itself, every `@include` target, and every `@loop` JSON data
61
+ * file. The dev builder unions these (plus colocated assets) into the per-route
62
+ * dependency map that drives incremental rebuilds.
63
+ * @property {Set<string>} collectionsUsed Names of the collections this compile
64
+ * looped, so a change to any entry of a collection rebuilds its consumers.
65
+ * @property {import("./reader.js").Reader} reader The file reader every source
66
+ * read threads through — the real filesystem by default, or an in-memory map
67
+ * for `compileFromMemory`. Set once by `createCompilation` and reached via
68
+ * `ctx.comp.reader` in the directive handlers (includes, loop data).
69
+ */
70
+ /**
71
+ * A lexical scope chain for static interpolation values (include args, loop vars).
72
+ * @typedef {object} Scope
73
+ * @property {Scope | null} parent
74
+ * @property {Record<string, unknown>} vars
75
+ */
76
+ /**
77
+ * Per-file compile context threaded through the directive handlers.
78
+ * @typedef {object} Ctx
79
+ * @property {string} file Absolute path to the file being compiled.
80
+ * @property {number} [bodyLine] 0-based file line the compiled body starts on
81
+ * (the frontmatter offset), so `at` reports true file line numbers.
82
+ * @property {number} [lineOffset] 0-based body line the current `compileBody`
83
+ * slice starts on. Block handlers recurse with {@link nestedCtx}, so a line
84
+ * index inside a nested block body still maps to the true file line.
85
+ * @property {Paths} context Resolved project paths.
86
+ * @property {string[]} stack Include stack (real paths) for cycle detection.
87
+ * @property {Scope} scope Static value scope chain.
88
+ * @property {Compilation} comp Shared compilation accumulator.
89
+ * @property {string[]} sections Active section-id scope chain.
90
+ * @property {string | null} loopItem Name of the current reactive loop item, if any.
91
+ * @property {string} [loopKey] State key of the list being looped, if any.
92
+ * @property {number} [reactiveDepth] Number of enclosing REACTIVE `@loop` levels
93
+ * (0 at top level). The runtime reconciles at most two nested `data-wd-loop`
94
+ * levels — a third paints empty — so a third reactive level is rejected at
95
+ * compile time. Static (build-unrolled) loops flatten to markup and don't add
96
+ * a level, so they carry the count through without incrementing it.
97
+ * @property {{ at: string, line: string }} [loopOpener] The current `@loop`
98
+ * opener's `file:line` + raw text, set when descending into a loop body so the
99
+ * reactive-nesting-depth error can point at the offending opener.
100
+ * @property {boolean} [loopMeta] Inside a loop body, so `$index`/`$first`/… are valid.
101
+ * @property {import("markdown-it").default} [md] Markdown-it instance selected for this file.
102
+ * @property {(lines: string[], ctx: Ctx) => string} compileBody The body
103
+ * dispatcher, threaded through the context so block handlers (`:if`, `:::`,
104
+ * `:form`, `@loop`, …) recurse into nested bodies without importing the
105
+ * dispatcher back — the module graph stays an import-cycle-free DAG.
106
+ * @property {(file: string, context: Paths, stack: string[], scope: Scope, comp: Compilation, sections: string[], loopItem: string | null, reactiveDepth?: number, loopOpener?: { at: string, line: string } | null) => { meta: Meta, html: string }} compileFile
107
+ * The per-file compile, threaded for the same reason so `@include` can
108
+ * compile its target file. `reactiveDepth`/`loopOpener` carry the enclosing
109
+ * reactive-loop nesting across the include boundary so the depth guard holds.
110
+ */
111
+ /**
112
+ * A compiled page document.
113
+ * @typedef {object} CompiledDocument
114
+ * @property {Meta} meta
115
+ * @property {string} html
116
+ * @property {Assets} assets
117
+ * @property {string[]} warnings
118
+ * @property {Pagination | null} pagination The pagination intent a paginated
119
+ * `@loop` recorded, or null. The builder reads it to multiply routes.
120
+ * @property {Set<string>} deps Absolute source-file dependencies of this compile.
121
+ * @property {Set<string>} collectionsUsed Collection names this compile looped.
122
+ */
123
+ /**
124
+ * A compiled page: the document plus its full HTML shell.
125
+ * @typedef {object} CompiledPage
126
+ * @property {Meta} meta
127
+ * @property {string} html
128
+ * @property {Assets} assets
129
+ * @property {string[]} warnings
130
+ * @property {Pagination | null} pagination The pagination intent a paginated
131
+ * `@loop` recorded, or null. The builder reads it to multiply routes.
132
+ * @property {Set<string>} deps Absolute source-file dependencies of this compile.
133
+ * @property {Set<string>} collectionsUsed Collection names this compile looped.
134
+ */
135
+ /**
136
+ * Structured, machine-readable companion to a compile error's string message.
137
+ * The string `message` contract is unchanged; `wd` gives an AI edit-loop (or an
138
+ * editor) the same facts without re-parsing the prose: which code, which
139
+ * file/line, the corrective `Use:` template, and one concrete compilable
140
+ * example line.
141
+ * @typedef {object} WdErrorInfo
142
+ * @property {string} code Stable error code (`WD` + three digits, e.g. `WD201`).
143
+ * Every code is registered in `src/errors.js` with a title, cause, and fix,
144
+ * and documented in `docs/errors.md`. {@link wdError} prefixes the string
145
+ * message with `[<code>] `, so the code is greppable from the message too.
146
+ * @property {string} [file] Absolute path of the source file the error is in,
147
+ * when the throwing layer knows one (`compileSkin` is a pure string→string
148
+ * pass and a collection-row check names the entry route instead).
149
+ * @property {number} [line] 1-based file line the error points at, when known
150
+ * (some errors — e.g. a bad `where` operand — are file-scoped only, matching
151
+ * the string message, which likewise omits the line there).
152
+ * @property {string} [hint] The corrective `Use:` template (without the `Use: ` prefix).
153
+ * @property {string} [example] One concrete, compilable directive line.
154
+ */
155
+ /**
156
+ * A compiled `@loop … where` predicate.
157
+ * @typedef {object} Predicate
158
+ * @property {string} body Safe JS boolean expression over `I()`/`S()`/`C()`.
159
+ * @property {boolean} refsState Whether the predicate reads declared state.
160
+ */
161
+ /**
162
+ * A loop offset/limit argument: a non-negative integer literal or a state key.
163
+ * @typedef {{ kind: "literal", value: number } | { kind: "key", value: string }} NumArg
164
+ */
165
+ /**
166
+ * Loop clause configuration shared by the static and reactive paths.
167
+ * @typedef {object} LoopOpts
168
+ * @property {Predicate | null} where
169
+ * @property {{ key: string, keyKind: "literal" | "key", dir: string, dirKind: "literal" | "key" } | null} sort A literal item field + asc/desc, or a `:state`/`:store` key for either (reactive, clickable-header sort).
170
+ * @property {boolean} reverse
171
+ * @property {NumArg | null} offset
172
+ * @property {NumArg | null} limit
173
+ * @property {string[] | null} empty Empty-branch body lines, if any.
174
+ * @property {number} [emptyStart] 0-based index the empty branch starts at
175
+ * within the loop body, so its nested errors report the true file line.
176
+ * @property {boolean} clauseRefsState Whether offset/limit reference state.
177
+ * @property {boolean} sortable Drag-to-reorder the underlying :state/:store list
178
+ * (the `sortable` clause). Only valid on a plain reactive state-key loop.
179
+ * @property {number | null} paginate Rows-per-page for a `paginate N` clause, or
180
+ * null. Only valid on a collection loop; multiplies the listing into static
181
+ * pages (page 1 at the route, 2+ at `/<route>/page/<n>/`) and exposes a `page`
182
+ * pager to the template scope.
183
+ */
184
+ /**
185
+ * A parsed `:button` action.
186
+ * @typedef {object} Action
187
+ * @property {string} op Runtime operation (inc/dec/add/sub/append/prepend/set/toggle/member-toggle/remove/remove-value/clear/merge/delete/reset/append-row).
188
+ * @property {string} target State key (possibly dotted) the action mutates.
189
+ * @property {unknown} [value] Literal value for value-carrying ops.
190
+ */
191
+ /**
192
+ * Normalize CRLF/CR line endings to LF.
193
+ *
194
+ * Every line-structured parser in the project (frontmatter delimiters, the `.wd`
195
+ * directive dispatcher, `.skin` indentation) tests LF-shaped text. Source files
196
+ * authored on Windows — or checked out anywhere with git's `core.autocrlf`, the
197
+ * default on Windows — arrive CRLF-terminated, and an un-normalized `\r` turns
198
+ * `---` into `---\r` and silently defeats those tests. Applied at the reader
199
+ * boundary and again at the two entry points that accept raw strings from
200
+ * callers that bypass the reader; it is idempotent, so doubling up is free.
201
+ * @param {string} text
202
+ * @returns {string}
203
+ */
204
+ export function normalizeNewlines(text: string): string;
205
+ /**
206
+ * @param {import("./reader.js").Reader} reader The source reader for this
207
+ * compile (fs-backed by default; in-memory for `compileFromMemory`).
208
+ * @returns {Compilation}
209
+ */
210
+ export function createCompilation(reader: import("./reader.js").Reader): Compilation;
211
+ /**
212
+ * @param {Scope | null} parent
213
+ * @param {Record<string, unknown>} [vars]
214
+ * @returns {Scope}
215
+ */
216
+ export function createScope(parent: Scope | null, vars?: Record<string, unknown>): Scope;
217
+ /**
218
+ * Format a source location as `file:line` (1-based) for compile errors. Keeps the
219
+ * file path intact so existing message matchers still pass, while pointing at the
220
+ * directive's (or unclosed opener's) line. `index` is 0-based into the current
221
+ * `compileBody` slice; `ctx.lineOffset` (where that slice starts in the body,
222
+ * accumulated by {@link nestedCtx} as block handlers recurse) and `ctx.bodyLine`
223
+ * (the frontmatter offset) shift it back to the true file line.
224
+ * @param {Pick<Ctx, "file" | "bodyLine" | "lineOffset">} ctx
225
+ * @param {number} index 0-based line index into the current body slice.
226
+ * @returns {string}
227
+ */
228
+ export function at(ctx: Pick<Ctx, "file" | "bodyLine" | "lineOffset">, index: number): string;
229
+ /**
230
+ * The 1-based true file line for a body-slice `index` — the numeric half of
231
+ * {@link at}, exposed so {@link wdError} can populate the structured `wd.line`.
232
+ * @param {Pick<Ctx, "bodyLine" | "lineOffset">} ctx
233
+ * @param {number} index 0-based line index into the current body slice.
234
+ * @returns {number}
235
+ */
236
+ export function lineOf(ctx: Pick<Ctx, "bodyLine" | "lineOffset">, index: number): number;
237
+ /**
238
+ * Build a compile `Error` carrying a structured {@link WdErrorInfo} on `err.wd`
239
+ * — so an AI edit-loop (or an editor) gets code/file/line/hint/example without
240
+ * re-parsing the prose. The string message is the caller's text prefixed with
241
+ * the stable `[WDxxx]` code, so a user can search the code straight out of the
242
+ * terminal; everything after the prefix (the `file:line`, the corrective `Use:`
243
+ * template, the concrete `e.g.` example) is unchanged.
244
+ *
245
+ * Every author-facing compile error is built here, so no error can ship without
246
+ * a code. See `src/errors.js` for the registry and the numbering scheme.
247
+ * @param {string} message The user-facing error text, without the code prefix.
248
+ * @param {WdErrorInfo} wd The structured mirror (its `code` becomes the prefix).
249
+ * @returns {Error & { wd: WdErrorInfo }}
250
+ */
251
+ export function wdError(message: string, wd: WdErrorInfo): Error & {
252
+ wd: WdErrorInfo;
253
+ };
254
+ /**
255
+ * The compile context for a nested block body that starts `start` lines into the
256
+ * current slice. The offset accumulates, so errors inside arbitrarily nested
257
+ * blocks (`::: container`, `:form`, `@loop`, `:if`, `:carousel`) still report
258
+ * the true file line through {@link at}.
259
+ * @template {Pick<Ctx, "lineOffset">} T
260
+ * @param {T} ctx
261
+ * @param {number} start 0-based index the nested body starts at in the current slice.
262
+ * @returns {T}
263
+ */
264
+ export function nestedCtx<T extends Pick<Ctx, "lineOffset">>(ctx: T, start: number): T;
265
+ export const pageIncludeExtensions: string[];
266
+ /** @type {Record<string, string>} */
267
+ export const LOOP_META: Record<string, string>;
268
+ export type Paths = import("../config.js").Paths;
269
+ /**
270
+ * Frontmatter values are scalars or inline arrays of scalars.
271
+ */
272
+ export type FrontmatterValue = string | number | boolean | null | Array<string | number | boolean | null>;
273
+ /**
274
+ * Parsed page frontmatter, keyed by field name.
275
+ */
276
+ export type Meta = Record<string, FrontmatterValue>;
277
+ /**
278
+ * Collected output assets for a compiled document.
279
+ */
280
+ export type Assets = {
281
+ /**
282
+ * Public hrefs of compiled skin stylesheets.
283
+ */
284
+ skins: Set<string>;
285
+ /**
286
+ * Public hrefs of colocated page scripts.
287
+ */
288
+ scripts: Set<string>;
289
+ /**
290
+ * Source path → public href for emitted assets.
291
+ */
292
+ files: Map<string, string>;
293
+ /**
294
+ * Whether the reactive runtime is required.
295
+ */
296
+ runtime: boolean;
297
+ /**
298
+ * Names of framework behavior modules this page
299
+ * needs (`slider` is compile-time only and never appears here; `sortable`,
300
+ * `carousel` each emit a pay-for-what-you-use `/__wd/behaviors/<name>.js`).
301
+ */
302
+ behaviors: Set<string>;
303
+ /**
304
+ * Whether the page has a build-time-highlighted code
305
+ * block, so the framework highlight stylesheet (`/__wd/highlight.css`) is
306
+ * emitted and linked — pay-for-what-you-use, zero JS.
307
+ */
308
+ hasCode: boolean;
309
+ /**
310
+ * Source paths of colocated `.skin` files
311
+ * that opted into scoping (`scoped` first line). The builder emits these with
312
+ * the scope attribute and runs the unused-selector warning against the stamped
313
+ * subtree; the HTML stamp uses them to scope the matching page/include subtree.
314
+ */
315
+ scopedSkins: Set<string>;
316
+ };
317
+ /**
318
+ * The pagination intent a `@loop … paginate N` records during compile, so the
319
+ * builder can multiply routes (page 1 at the listing route, 2+ at
320
+ * `/<route>/page/<n>/`). `total` is the post-`where`/`sort` page count for the
321
+ * collection the loop drew from.
322
+ */
323
+ export type Pagination = {
324
+ /**
325
+ * Rows per page (the `paginate N` value).
326
+ */
327
+ perPage: number;
328
+ /**
329
+ * Total page count (≥ 1).
330
+ */
331
+ total: number;
332
+ /**
333
+ * Collection name the loop paginated.
334
+ */
335
+ collection: string;
336
+ };
337
+ /**
338
+ * Per-document compilation accumulator shared across includes/sections.
339
+ */
340
+ export type Compilation = {
341
+ assets: Assets;
342
+ /**
343
+ * Declared state keys → initial values.
344
+ */
345
+ state: Map<string, unknown>;
346
+ /**
347
+ * Page-global store names (a subset of state keys).
348
+ */
349
+ stores: Set<string>;
350
+ /**
351
+ * Base state keys declared by `:fetch`, so
352
+ * bare `:if <key>_loading` / `:if <key>_error` regions get compile-time
353
+ * `role`/`aria-live` announcements.
354
+ */
355
+ fetchKeys: Set<string>;
356
+ /**
357
+ * Non-fatal authoring hints.
358
+ */
359
+ warnings: string[];
360
+ /**
361
+ * Counter for auto-generated section ids.
362
+ */
363
+ sectionCounter: number;
364
+ /**
365
+ * Heading-slug occurrence counts,
366
+ * shared across prose chunks and includes so anchor ids dedupe document-wide.
367
+ */
368
+ headingSlugs: Map<string, number>;
369
+ /**
370
+ * Collection name → entry rows, built from the router's routes and threaded in
371
+ * so a bare-name `@loop blog into post` resolves to its entries at build time.
372
+ */
373
+ collections: Map<string, import("./collections.js").CollectionRow[]>;
374
+ /**
375
+ * The pagination
376
+ * intent a `@loop … paginate N` recorded this compile, or null. The builder
377
+ * reads it to multiply routes; one paginated loop per page is supported.
378
+ */
379
+ pagination: import("./context.js").Pagination | null;
380
+ /**
381
+ * Absolute paths of every source file this compile
382
+ * read: the page itself, every `@include` target, and every `@loop` JSON data
383
+ * file. The dev builder unions these (plus colocated assets) into the per-route
384
+ * dependency map that drives incremental rebuilds.
385
+ */
386
+ deps: Set<string>;
387
+ /**
388
+ * Names of the collections this compile
389
+ * looped, so a change to any entry of a collection rebuilds its consumers.
390
+ */
391
+ collectionsUsed: Set<string>;
392
+ /**
393
+ * The file reader every source
394
+ * read threads through — the real filesystem by default, or an in-memory map
395
+ * for `compileFromMemory`. Set once by `createCompilation` and reached via
396
+ * `ctx.comp.reader` in the directive handlers (includes, loop data).
397
+ */
398
+ reader: import("./reader.js").Reader;
399
+ };
400
+ /**
401
+ * A lexical scope chain for static interpolation values (include args, loop vars).
402
+ */
403
+ export type Scope = {
404
+ parent: Scope | null;
405
+ vars: Record<string, unknown>;
406
+ };
407
+ /**
408
+ * Per-file compile context threaded through the directive handlers.
409
+ */
410
+ export type Ctx = {
411
+ /**
412
+ * Absolute path to the file being compiled.
413
+ */
414
+ file: string;
415
+ /**
416
+ * 0-based file line the compiled body starts on
417
+ * (the frontmatter offset), so `at` reports true file line numbers.
418
+ */
419
+ bodyLine?: number | undefined;
420
+ /**
421
+ * 0-based body line the current `compileBody`
422
+ * slice starts on. Block handlers recurse with {@link nestedCtx}, so a line
423
+ * index inside a nested block body still maps to the true file line.
424
+ */
425
+ lineOffset?: number | undefined;
426
+ /**
427
+ * Resolved project paths.
428
+ */
429
+ context: Paths;
430
+ /**
431
+ * Include stack (real paths) for cycle detection.
432
+ */
433
+ stack: string[];
434
+ /**
435
+ * Static value scope chain.
436
+ */
437
+ scope: Scope;
438
+ /**
439
+ * Shared compilation accumulator.
440
+ */
441
+ comp: Compilation;
442
+ /**
443
+ * Active section-id scope chain.
444
+ */
445
+ sections: string[];
446
+ /**
447
+ * Name of the current reactive loop item, if any.
448
+ */
449
+ loopItem: string | null;
450
+ /**
451
+ * State key of the list being looped, if any.
452
+ */
453
+ loopKey?: string | undefined;
454
+ /**
455
+ * Number of enclosing REACTIVE `@loop` levels
456
+ * (0 at top level). The runtime reconciles at most two nested `data-wd-loop`
457
+ * levels — a third paints empty — so a third reactive level is rejected at
458
+ * compile time. Static (build-unrolled) loops flatten to markup and don't add
459
+ * a level, so they carry the count through without incrementing it.
460
+ */
461
+ reactiveDepth?: number | undefined;
462
+ /**
463
+ * The current `@loop`
464
+ * opener's `file:line` + raw text, set when descending into a loop body so the
465
+ * reactive-nesting-depth error can point at the offending opener.
466
+ */
467
+ loopOpener?: {
468
+ at: string;
469
+ line: string;
470
+ } | undefined;
471
+ /**
472
+ * Inside a loop body, so `$index`/`$first`/… are valid.
473
+ */
474
+ loopMeta?: boolean | undefined;
475
+ /**
476
+ * Markdown-it instance selected for this file.
477
+ */
478
+ md?: import("markdown-it").default | undefined;
479
+ /**
480
+ * The body
481
+ * dispatcher, threaded through the context so block handlers (`:if`, `:::`,
482
+ * `:form`, `@loop`, …) recurse into nested bodies without importing the
483
+ * dispatcher back — the module graph stays an import-cycle-free DAG.
484
+ */
485
+ compileBody: (lines: string[], ctx: Ctx) => string;
486
+ /**
487
+ * The per-file compile, threaded for the same reason so `@include` can
488
+ * compile its target file. `reactiveDepth`/`loopOpener` carry the enclosing
489
+ * reactive-loop nesting across the include boundary so the depth guard holds.
490
+ */
491
+ compileFile: (file: string, context: Paths, stack: string[], scope: Scope, comp: Compilation, sections: string[], loopItem: string | null, reactiveDepth?: number, loopOpener?: {
492
+ at: string;
493
+ line: string;
494
+ } | null) => {
495
+ meta: Meta;
496
+ html: string;
497
+ };
498
+ };
499
+ /**
500
+ * A compiled page document.
501
+ */
502
+ export type CompiledDocument = {
503
+ meta: Meta;
504
+ html: string;
505
+ assets: Assets;
506
+ warnings: string[];
507
+ /**
508
+ * The pagination intent a paginated
509
+ * `@loop` recorded, or null. The builder reads it to multiply routes.
510
+ */
511
+ pagination: Pagination | null;
512
+ /**
513
+ * Absolute source-file dependencies of this compile.
514
+ */
515
+ deps: Set<string>;
516
+ /**
517
+ * Collection names this compile looped.
518
+ */
519
+ collectionsUsed: Set<string>;
520
+ };
521
+ /**
522
+ * A compiled page: the document plus its full HTML shell.
523
+ */
524
+ export type CompiledPage = {
525
+ meta: Meta;
526
+ html: string;
527
+ assets: Assets;
528
+ warnings: string[];
529
+ /**
530
+ * The pagination intent a paginated
531
+ * `@loop` recorded, or null. The builder reads it to multiply routes.
532
+ */
533
+ pagination: Pagination | null;
534
+ /**
535
+ * Absolute source-file dependencies of this compile.
536
+ */
537
+ deps: Set<string>;
538
+ /**
539
+ * Collection names this compile looped.
540
+ */
541
+ collectionsUsed: Set<string>;
542
+ };
543
+ /**
544
+ * Structured, machine-readable companion to a compile error's string message.
545
+ * The string `message` contract is unchanged; `wd` gives an AI edit-loop (or an
546
+ * editor) the same facts without re-parsing the prose: which code, which
547
+ * file/line, the corrective `Use:` template, and one concrete compilable
548
+ * example line.
549
+ */
550
+ export type WdErrorInfo = {
551
+ /**
552
+ * Stable error code (`WD` + three digits, e.g. `WD201`).
553
+ * Every code is registered in `src/errors.js` with a title, cause, and fix,
554
+ * and documented in `docs/errors.md`. {@link wdError} prefixes the string
555
+ * message with `[<code>] `, so the code is greppable from the message too.
556
+ */
557
+ code: string;
558
+ /**
559
+ * Absolute path of the source file the error is in,
560
+ * when the throwing layer knows one (`compileSkin` is a pure string→string
561
+ * pass and a collection-row check names the entry route instead).
562
+ */
563
+ file?: string | undefined;
564
+ /**
565
+ * 1-based file line the error points at, when known
566
+ * (some errors — e.g. a bad `where` operand — are file-scoped only, matching
567
+ * the string message, which likewise omits the line there).
568
+ */
569
+ line?: number | undefined;
570
+ /**
571
+ * The corrective `Use:` template (without the `Use: ` prefix).
572
+ */
573
+ hint?: string | undefined;
574
+ /**
575
+ * One concrete, compilable directive line.
576
+ */
577
+ example?: string | undefined;
578
+ };
579
+ /**
580
+ * A compiled `@loop … where` predicate.
581
+ */
582
+ export type Predicate = {
583
+ /**
584
+ * Safe JS boolean expression over `I()`/`S()`/`C()`.
585
+ */
586
+ body: string;
587
+ /**
588
+ * Whether the predicate reads declared state.
589
+ */
590
+ refsState: boolean;
591
+ };
592
+ /**
593
+ * A loop offset/limit argument: a non-negative integer literal or a state key.
594
+ */
595
+ export type NumArg = {
596
+ kind: "literal";
597
+ value: number;
598
+ } | {
599
+ kind: "key";
600
+ value: string;
601
+ };
602
+ /**
603
+ * Loop clause configuration shared by the static and reactive paths.
604
+ */
605
+ export type LoopOpts = {
606
+ where: Predicate | null;
607
+ /**
608
+ * A literal item field + asc/desc, or a `:state`/`:store` key for either (reactive, clickable-header sort).
609
+ */
610
+ sort: {
611
+ key: string;
612
+ keyKind: "literal" | "key";
613
+ dir: string;
614
+ dirKind: "literal" | "key";
615
+ } | null;
616
+ reverse: boolean;
617
+ offset: NumArg | null;
618
+ limit: NumArg | null;
619
+ /**
620
+ * Empty-branch body lines, if any.
621
+ */
622
+ empty: string[] | null;
623
+ /**
624
+ * 0-based index the empty branch starts at
625
+ * within the loop body, so its nested errors report the true file line.
626
+ */
627
+ emptyStart?: number | undefined;
628
+ /**
629
+ * Whether offset/limit reference state.
630
+ */
631
+ clauseRefsState: boolean;
632
+ /**
633
+ * Drag-to-reorder the underlying :state/:store list
634
+ * (the `sortable` clause). Only valid on a plain reactive state-key loop.
635
+ */
636
+ sortable: boolean;
637
+ /**
638
+ * Rows-per-page for a `paginate N` clause, or
639
+ * null. Only valid on a collection loop; multiplies the listing into static
640
+ * pages (page 1 at the route, 2+ at `/<route>/page/<n>/`) and exposes a `page`
641
+ * pager to the template scope.
642
+ */
643
+ paginate: number | null;
644
+ };
645
+ /**
646
+ * A parsed `:button` action.
647
+ */
648
+ export type Action = {
649
+ /**
650
+ * Runtime operation (inc/dec/add/sub/append/prepend/set/toggle/member-toggle/remove/remove-value/clear/merge/delete/reset/append-row).
651
+ */
652
+ op: string;
653
+ /**
654
+ * State key (possibly dotted) the action mutates.
655
+ */
656
+ target: string;
657
+ /**
658
+ * Literal value for value-carrying ops.
659
+ */
660
+ value?: unknown;
661
+ };
@@ -0,0 +1,6 @@
1
+ export { handleFetch } from "./fetch.js";
2
+ export { handleButton, handleEffect, handleEvery } from "./actions.js";
3
+ export { handleBind, handleChoiceGroup, handleForm, handleInput, handleSelect, handleSlider, handleSubmit, handleTextarea } from "./forms.js";
4
+ export { handleEmbed, handleMedia } from "./media.js";
5
+ export { handleComputed, handleState, handleStore, handleTheme } from "./state.js";
6
+ export { handleCarousel, handleContainer, handleIf, handleInclude, renderDemoDirective } from "./structure.js";
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Re-parse a validated compiled expression (JS fragment over I()/S()/C()/A())
3
+ * into a compact AST. Throws on anything outside the closed grammar.
4
+ * @param {string} code
5
+ * @returns {any[]}
6
+ */
7
+ export function astOf(code: string): any[];
8
+ /**
9
+ * Build-time mirror of the runtime AST walker — same operators, so a `:computed`
10
+ * or `where`/`when` predicate folds to the SAME value the runtime will recompute.
11
+ * `state` reads come from the compilation's declared-state map. No eval.
12
+ * @param {any[]} node
13
+ * @param {unknown} item Loop row for I(); undefined otherwise.
14
+ * @param {{ state: Map<string, unknown> }} comp
15
+ * @returns {any}
16
+ */
17
+ export function evalAst(node: any[], item: unknown, comp: {
18
+ state: Map<string, unknown>;
19
+ }): any;
20
+ export function serializeExpr(code: string): string;