@zvndev/darkmown 2.1.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 (74) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +65 -0
  3. package/docs/cli.md +8 -0
  4. package/docs/spec-alignment.md +18 -0
  5. package/grammar/wd-directives.gbnf +58 -0
  6. package/package.json +17 -10
  7. package/site/pages/docs/index.wd +29 -0
  8. package/site/pages/playground/index.js +47 -46
  9. package/src/builder.js +5 -0
  10. package/src/catalog.js +601 -0
  11. package/src/cli.js +8 -0
  12. package/src/compiler/actions.js +87 -34
  13. package/src/compiler/body.js +36 -14
  14. package/src/compiler/collections.js +16 -10
  15. package/src/compiler/context.js +70 -1
  16. package/src/compiler/fetch.js +51 -19
  17. package/src/compiler/format.js +5 -2
  18. package/src/compiler/forms.js +169 -50
  19. package/src/compiler/frontmatter.js +11 -2
  20. package/src/compiler/fs-reader.js +2 -1
  21. package/src/compiler/image-size.js +340 -0
  22. package/src/compiler/includes.js +8 -4
  23. package/src/compiler/interpolation.js +15 -7
  24. package/src/compiler/loops.js +78 -39
  25. package/src/compiler/markdown.js +4 -3
  26. package/src/compiler/media.js +38 -10
  27. package/src/compiler/page.js +8 -6
  28. package/src/compiler/predicates.js +74 -30
  29. package/src/compiler/reader.js +12 -2
  30. package/src/compiler/state.js +83 -18
  31. package/src/compiler/structure.js +57 -24
  32. package/src/deploy.js +8 -4
  33. package/src/errors.js +1156 -0
  34. package/src/grammar.js +131 -0
  35. package/src/index.js +1 -0
  36. package/src/router.js +7 -4
  37. package/src/scaffold.js +4 -2
  38. package/src/skin.js +17 -6
  39. package/types/api-runner.d.ts +94 -0
  40. package/types/builder.d.ts +141 -10
  41. package/types/catalog.d.ts +97 -0
  42. package/types/compiler/actions.d.ts +41 -0
  43. package/types/compiler/body.d.ts +11 -0
  44. package/types/compiler/collections.d.ts +76 -0
  45. package/types/compiler/context.d.ts +661 -0
  46. package/types/compiler/directives.d.ts +6 -0
  47. package/types/compiler/expr-ast.d.ts +20 -0
  48. package/types/compiler/fetch.d.ts +29 -0
  49. package/types/compiler/format.d.ts +49 -0
  50. package/types/compiler/forms.d.ts +94 -0
  51. package/types/compiler/frontmatter.d.ts +33 -0
  52. package/types/compiler/fs-reader.d.ts +7 -0
  53. package/types/compiler/image-size.d.ts +14 -0
  54. package/types/compiler/includes.d.ts +100 -0
  55. package/types/compiler/index.d.ts +5 -0
  56. package/types/compiler/interpolation.d.ts +115 -0
  57. package/types/compiler/loops.d.ts +36 -0
  58. package/types/compiler/markdown.d.ts +34 -0
  59. package/types/compiler/media.d.ts +32 -0
  60. package/types/compiler/page.d.ts +109 -0
  61. package/types/compiler/predicates.d.ts +50 -0
  62. package/types/compiler/reader.d.ts +56 -0
  63. package/types/compiler/state.d.ts +68 -0
  64. package/types/compiler/structure.d.ts +56 -0
  65. package/types/compiler.d.ts +35 -223
  66. package/types/dev.d.ts +47 -2
  67. package/types/errors.d.ts +100 -0
  68. package/types/feeds.d.ts +150 -0
  69. package/types/headers.d.ts +44 -0
  70. package/types/highlight.d.ts +24 -0
  71. package/types/router.d.ts +42 -3
  72. package/types/scaffold.d.ts +15 -3
  73. package/types/skin.d.ts +28 -1
  74. package/types/statics.d.ts +2 -1
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Compile a page source file into a full HTML document plus its assets.
3
+ * @param {string} file Absolute path to the source `.md`/`.wd` file.
4
+ * @param {Paths} context Resolved project paths.
5
+ * @param {{ feed?: { href: string, title: string }, collections?: Map<string, import("./collections.js").CollectionRow[]>, vars?: Record<string, unknown>, reader?: import("./reader.js").Reader }} [options]
6
+ * When a site-wide RSS feed is emitted (the home page set `site_url` and the
7
+ * site has dated posts), `feed` carries its absolute href + title so every page
8
+ * links it. `collections` is the build-time collection index a bare-name
9
+ * `@loop` resolves against; `vars` seeds the document scope (the pager `page`
10
+ * object on a paginated route). `reader` is the source reader (fs-backed by
11
+ * default via `src/compiler.js`; in-memory for `compileFromMemory`).
12
+ * @returns {CompiledPage}
13
+ */
14
+ export function compilePage(file: string, context: Paths, options?: {
15
+ feed?: {
16
+ href: string;
17
+ title: string;
18
+ };
19
+ collections?: Map<string, import("./collections.js").CollectionRow[]>;
20
+ vars?: Record<string, unknown>;
21
+ reader?: import("./reader.js").Reader;
22
+ }): CompiledPage;
23
+ /**
24
+ * Harden every `<img>` in the assembled page body — compile-time only, zero
25
+ * runtime JS. Bare markdown images reflow the page as they decode (cumulative
26
+ * layout shift — the literal "jump" on navigation) and load eagerly. This
27
+ * stamps intrinsic `width`/`height` (read from the file on disk so the browser
28
+ * reserves space), `decoding="async"` on all, and a load-priority split: the
29
+ * first image is the LCP candidate (eager + `fetchpriority="high"`), the rest
30
+ * lazy-load. Author-set attributes are never overwritten.
31
+ * @param {string} html Assembled page body HTML.
32
+ * @param {Paths} paths Resolved project paths.
33
+ * @param {import("./reader.js").Reader} reader Source reader for the image bytes.
34
+ * @returns {string}
35
+ */
36
+ export function enhanceImages(html: string, paths: Paths, reader: import("./reader.js").Reader): string;
37
+ /**
38
+ * Compile a source file into its body HTML, frontmatter, and assets (no page shell).
39
+ * @param {string} file Absolute path to the source file.
40
+ * @param {Paths} context Resolved project paths.
41
+ * @param {string[]} [stack] Include stack for cycle detection.
42
+ * @param {Record<string, unknown>} [vars] Initial static scope variables.
43
+ * @param {Map<string, import("./collections.js").CollectionRow[]>} [collections]
44
+ * Build-time collection index a bare-name `@loop` resolves against.
45
+ * @param {import("./reader.js").Reader} [reader] Source reader (fs-backed when
46
+ * omitted via `src/compiler.js`; in-memory for `compileFromMemory`).
47
+ * @returns {CompiledDocument}
48
+ */
49
+ export function compileDocument(file: string, context: Paths, stack?: string[], vars?: Record<string, unknown>, collections?: Map<string, import("./collections.js").CollectionRow[]>, reader?: import("./reader.js").Reader): CompiledDocument;
50
+ /**
51
+ * Compile a single file: parse frontmatter, collect assets, render body.
52
+ * @param {string} file
53
+ * @param {Paths} context
54
+ * @param {string[]} stack
55
+ * @param {Scope} scope
56
+ * @param {Compilation} comp
57
+ * @param {string[]} sections
58
+ * @param {string | null} loopItem
59
+ * @param {number} [reactiveDepth] Enclosing REACTIVE `@loop` levels, threaded in
60
+ * from the including page so an include's body can't bypass the two-level
61
+ * reactive-nesting limit at the `@include` boundary.
62
+ * @param {{ at: string, line: string } | null} [loopOpener] The enclosing `@loop`
63
+ * opener, threaded in so a depth error inside the include names the right opener.
64
+ * @returns {{ meta: Meta, html: string }}
65
+ */
66
+ export function compileFile(file: string, context: Paths, stack: string[], scope: Scope, comp: Compilation, sections: string[], loopItem: string | null, reactiveDepth?: number, loopOpener?: {
67
+ at: string;
68
+ line: string;
69
+ } | null): {
70
+ meta: Meta;
71
+ html: string;
72
+ };
73
+ /**
74
+ * Compile a page entirely from an in-memory file map — no filesystem, no
75
+ * `node:fs`. `files` maps PROJECT-RELATIVE POSIX paths (`site/pages/index.wd`,
76
+ * `site/_/nav.wd`) to their source strings; `entryPath` is the project-relative
77
+ * path of the page to compile. Includes, colocated `.skin`/`.js` detection, and
78
+ * `@loop` JSON-data reads all resolve against the same map (anything not in it
79
+ * is simply "absent"), so `@include`s work as long as their targets are present.
80
+ *
81
+ * This is the strategic fs-free entry point the browser playground and a planned
82
+ * mobile host use: the entire module graph reachable from here is free of
83
+ * `node:fs`, so it bundles for the browser. It returns the same
84
+ * {@link CompiledPage} shape as {@link compilePage} and throws the same
85
+ * `file:line` compile errors, so the error DX is identical in-browser.
86
+ * @param {Record<string, string> | Map<string, string>} files Project-relative
87
+ * path → source content.
88
+ * @param {string} entryPath Project-relative path of the page to compile.
89
+ * @param {{ feed?: { href: string, title: string }, collections?: Map<string, import("./collections.js").CollectionRow[]>, vars?: Record<string, unknown>, cwd?: string }} [options]
90
+ * Same shell options as {@link compilePage}; `cwd` overrides the virtual
91
+ * project root the relative keys resolve against (defaults to `/`).
92
+ * @returns {CompiledPage}
93
+ */
94
+ export function compileFromMemory(files: Record<string, string> | Map<string, string>, entryPath: string, options?: {
95
+ feed?: {
96
+ href: string;
97
+ title: string;
98
+ };
99
+ collections?: Map<string, import("./collections.js").CollectionRow[]>;
100
+ vars?: Record<string, unknown>;
101
+ cwd?: string;
102
+ }): CompiledPage;
103
+ export type Paths = import("./context.js").Paths;
104
+ export type Meta = import("./context.js").Meta;
105
+ export type Scope = import("./context.js").Scope;
106
+ export type Compilation = import("./context.js").Compilation;
107
+ export type Ctx = import("./context.js").Ctx;
108
+ export type CompiledDocument = import("./context.js").CompiledDocument;
109
+ export type CompiledPage = import("./context.js").CompiledPage;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * @param {string} raw
3
+ * @param {string} itemName
4
+ * @param {Ctx} ctx
5
+ * @returns {Predicate}
6
+ */
7
+ export function compilePredicate(raw: string, itemName: string, ctx: Ctx): Predicate;
8
+ /**
9
+ * Evaluate a compiled predicate against a row at build time by walking its AST —
10
+ * the same closed evaluator the runtime uses, so the fold matches. No eval.
11
+ * @param {string} body
12
+ * @param {unknown} item
13
+ * @param {Ctx} ctx
14
+ * @returns {boolean}
15
+ */
16
+ export function evalPredicate(body: string, item: unknown, ctx: Ctx): boolean;
17
+ /**
18
+ * Compile a `::: … .class when <predicate>`. Allows `:if`-style bare truthy
19
+ * paths and `where`-style `left <op> right` conditions joined by `and`/`or`.
20
+ * Folds to a static verdict when every operand is build-known, else returns a
21
+ * runtime body over I()/S()/C() plus whether it reads the reactive loop item
22
+ * (which decides data-wd-each-class vs the global data-wd-class).
23
+ * @param {string} raw
24
+ * @param {Ctx} ctx
25
+ * @param {string} [what] Directive label for error messages.
26
+ * @returns {{ static: true, value: boolean } | { static: false, body: string, item: boolean }}
27
+ */
28
+ export function compileWhen(raw: string, ctx: Ctx, what?: string): {
29
+ static: true;
30
+ value: boolean;
31
+ } | {
32
+ static: false;
33
+ body: string;
34
+ item: boolean;
35
+ };
36
+ /**
37
+ * Compile a `:computed` expression into safe JS over the `S(key, path)` reader.
38
+ * @param {string} raw
39
+ * @param {Ctx} ctx
40
+ * @returns {string}
41
+ */
42
+ export function compileComputedExpr(raw: string, ctx: Ctx): string;
43
+ /**
44
+ * @typedef {import("./context.js").Ctx} Ctx
45
+ * @typedef {import("./context.js").Predicate} Predicate
46
+ */
47
+ export const PREDICATE_OPS: string[];
48
+ export const PREDICATE_JOINERS: string[];
49
+ export type Ctx = import("./context.js").Ctx;
50
+ export type Predicate = import("./context.js").Predicate;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * The narrow filesystem surface the compiler needs. Every method takes an
3
+ * ABSOLUTE path (the compiler resolves includes/assets to absolute paths before
4
+ * reading), so a reader only has to map absolute paths to bytes/existence.
5
+ * @typedef {object} Reader
6
+ * @property {(absPath: string) => string} readText Read a UTF-8 text file. The
7
+ * result is ALWAYS LF-normalized: every parser downstream is line-structured
8
+ * and LF-shaped, so a CRLF source (Windows, or any `core.autocrlf` checkout)
9
+ * would otherwise fail to match delimiters like `---`. A custom reader must
10
+ * uphold this.
11
+ * @property {(absPath: string) => Uint8Array} readBinary Read raw bytes (images).
12
+ * @property {(absPath: string) => boolean} exists Whether the path exists.
13
+ * @property {(absPath: string) => string} realpath Canonical path (symlinks
14
+ * resolved on a real fs; a no-op normalize in memory) — used for cycle keys.
15
+ */
16
+ /**
17
+ * A reader backed by an in-memory map of PROJECT-RELATIVE path → content. Keys
18
+ * are POSIX, project-relative (e.g. `site/pages/index.wd`, `site/_/nav.wd`);
19
+ * lookups translate an absolute compile path back to its key via `cwd`. There
20
+ * are no symlinks in memory, so `realpath` just normalizes. Binary reads are
21
+ * unsupported (the playground has no image files, and image measuring degrades
22
+ * to "no dimensions" on a throw), so `readBinary` throws — callers already
23
+ * try/catch it.
24
+ * @param {Record<string, string> | Map<string, string>} files
25
+ * @param {string} [cwd] Virtual project root the keys are relative to.
26
+ * @returns {Reader}
27
+ */
28
+ export function memoryReader(files: Record<string, string> | Map<string, string>, cwd?: string): Reader;
29
+ /**
30
+ * The narrow filesystem surface the compiler needs. Every method takes an
31
+ * ABSOLUTE path (the compiler resolves includes/assets to absolute paths before
32
+ * reading), so a reader only has to map absolute paths to bytes/existence.
33
+ */
34
+ export type Reader = {
35
+ /**
36
+ * Read a UTF-8 text file. The
37
+ * result is ALWAYS LF-normalized: every parser downstream is line-structured
38
+ * and LF-shaped, so a CRLF source (Windows, or any `core.autocrlf` checkout)
39
+ * would otherwise fail to match delimiters like `---`. A custom reader must
40
+ * uphold this.
41
+ */
42
+ readText: (absPath: string) => string;
43
+ /**
44
+ * Read raw bytes (images).
45
+ */
46
+ readBinary: (absPath: string) => Uint8Array;
47
+ /**
48
+ * Whether the path exists.
49
+ */
50
+ exists: (absPath: string) => boolean;
51
+ /**
52
+ * Canonical path (symlinks
53
+ * resolved on a real fs; a no-op normalize in memory) — used for cycle keys.
54
+ */
55
+ realpath: (absPath: string) => string;
56
+ };
@@ -0,0 +1,68 @@
1
+ /**
2
+ * @param {string} line
3
+ * @param {Ctx} ctx
4
+ * @param {number} index 0-based line index for `file:line` errors.
5
+ * @returns {string}
6
+ */
7
+ export function handleState(line: string, ctx: Ctx, index: number): string;
8
+ /**
9
+ * Register a state key in the current section scope, enabling the runtime.
10
+ * @param {string} name
11
+ * @param {unknown} value
12
+ * @param {Ctx} ctx
13
+ * @returns {string} The fully-qualified state key.
14
+ */
15
+ export function declareState(name: string, value: unknown, ctx: Ctx): string;
16
+ /**
17
+ * @param {string} line
18
+ * @param {Ctx} ctx
19
+ * @param {number} index 0-based line index for `file:line` errors.
20
+ * @returns {string}
21
+ */
22
+ export function handleStore(line: string, ctx: Ctx, index: number): string;
23
+ /**
24
+ * Register a page-global store. The bare name is added to `comp.state` so every
25
+ * resolver (interpolation, :if, @loop, :computed, actions) sees it, and tracked
26
+ * in `comp.stores` for collision checks. Never section-scoped.
27
+ * @param {string} name
28
+ * @param {unknown} value
29
+ * @param {Ctx} ctx
30
+ * @returns {string} The store name (also its bare state key).
31
+ */
32
+ export function declareStore(name: string, value: unknown, ctx: Ctx): string;
33
+ /**
34
+ * Seed a `<name>_error` state key (null) if absent. Shared by :fetch and the
35
+ * round-trip :form so error fallbacks have a key to bind.
36
+ * @param {string} key
37
+ * @param {Ctx} ctx
38
+ * @returns {void}
39
+ */
40
+ export function declareErrorState(key: string, ctx: Ctx): void;
41
+ /**
42
+ * @param {string} line
43
+ * @param {Ctx} ctx
44
+ * @param {number} index 0-based line index for `file:line` errors.
45
+ * @returns {string}
46
+ */
47
+ export function handleComputed(line: string, ctx: Ctx, index: number): string;
48
+ /**
49
+ * `:theme [name] [= "auto"]` declares a durable `:store` (default name `theme`,
50
+ * seed `"auto"`) and reflects its value onto `<html data-theme>`. This layers a
51
+ * manual light/dark switch over the OS preference: `"auto"` follows the skin's
52
+ * `tokens dark` media query, while `"light"`/`"dark"` force the matching
53
+ * `:root[data-theme]` scope. Wire a switch with ordinary store actions —
54
+ * `:button "Dark" -> theme = "dark"`. Durable, so the choice survives reloads.
55
+ * @param {string} line
56
+ * @param {Ctx} ctx
57
+ * @param {number} index 0-based line index for `file:line` errors.
58
+ * @returns {string}
59
+ */
60
+ export function handleTheme(line: string, ctx: Ctx, index: number): string;
61
+ /**
62
+ * @typedef {import("./context.js").Ctx} Ctx
63
+ */
64
+ export const STATE_EXAMPLE: ":state count = 0";
65
+ export const STORE_EXAMPLE: ":store cart = []";
66
+ export const COMPUTED_EXAMPLE: ":computed total = items.length * 4";
67
+ export const THEME_EXAMPLE: ":theme";
68
+ export type Ctx = import("./context.js").Ctx;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @param {string} line
3
+ * @param {Ctx} ctx
4
+ * @param {number} index 0-based line index for `file:line` errors.
5
+ * @returns {string}
6
+ */
7
+ export function handleInclude(line: string, ctx: Ctx, index: number): string;
8
+ /**
9
+ * @param {string} header
10
+ * @param {string[]} bodyLines
11
+ * @param {Ctx} ctx
12
+ * @param {number} index 0-based line index for `file:line` errors.
13
+ * @returns {string}
14
+ */
15
+ export function handleContainer(header: string, bodyLines: string[], ctx: Ctx, index: number): string;
16
+ /**
17
+ * `:carousel [autoplay=N]` … `:endcarousel` — a horizontally scroll-snapping
18
+ * carousel. Contract: each DIRECT child element of the track is one slide, so put
19
+ * each slide in its own block (e.g. a `::: slide` container) and give that block
20
+ * the slide sizing in your skin — loose prose lines would each count as a slide.
21
+ * Registers the `carousel` behavior (prev/next, dot nav, optional autoplay, mouse
22
+ * drag); native CSS scroll-snap + the page skin handle layout and touch swipe.
23
+ * `autoplay` is suppressed under `prefers-reduced-motion`. No runtime required.
24
+ * @param {string} line
25
+ * @param {string[]} bodyLines
26
+ * @param {Ctx} ctx
27
+ * @param {number} index 0-based line index for `file:line` errors.
28
+ * @returns {string}
29
+ */
30
+ export function handleCarousel(line: string, bodyLines: string[], ctx: Ctx, index: number): string;
31
+ /**
32
+ * @param {string} line
33
+ * @param {string[]} truthyLines
34
+ * @param {string[]} falsyLines
35
+ * @param {Ctx} ctx
36
+ * @param {number} index 0-based line index for `file:line` errors.
37
+ * @param {number} [falsyStart] 0-based index the falsy body starts at in the
38
+ * current slice, so nested errors in that branch report the true file line.
39
+ * @returns {string}
40
+ */
41
+ export function handleIf(line: string, truthyLines: string[], falsyLines: string[], ctx: Ctx, index: number, falsyStart?: number): string;
42
+ /**
43
+ * Render the documentation-demo directives (`:try`, `:note`, `:sprint`).
44
+ * @param {string} line
45
+ * @param {Ctx} ctx
46
+ * @returns {string}
47
+ */
48
+ export function renderDemoDirective(line: string, ctx: Ctx): string;
49
+ /**
50
+ * @typedef {import("./context.js").Ctx} Ctx
51
+ */
52
+ export const INCLUDE_EXAMPLE: "@include /header.wd";
53
+ export const CONTAINER_EXAMPLE: "::: card .featured";
54
+ export const IF_EXAMPLE: ":if count > 0";
55
+ export const CAROUSEL_EXAMPLE: ":carousel autoplay=3000";
56
+ export type Ctx = import("./context.js").Ctx;
@@ -1,236 +1,48 @@
1
1
  /**
2
- * Compile a page source file into a full HTML document plus its assets.
2
+ * Compile a page source file into a full HTML document plus its assets, reading
3
+ * from the real filesystem by default. Pass `options.reader` to compile against
4
+ * a different source (see `compileFromMemory`).
3
5
  * @param {string} file Absolute path to the source `.md`/`.wd` file.
4
- * @param {Paths} context Resolved project paths.
6
+ * @param {import("./config.js").Paths} context Resolved project paths.
7
+ * @param {{ feed?: { href: string, title: string }, collections?: Map<string, import("./compiler/collections.js").CollectionRow[]>, vars?: Record<string, unknown>, reader?: import("./compiler/reader.js").Reader }} [options]
5
8
  * @returns {CompiledPage}
6
9
  */
7
- export function compilePage(file: string, context: Paths): CompiledPage;
10
+ export function compilePage(file: string, context: import("./config.js").Paths, options?: {
11
+ feed?: {
12
+ href: string;
13
+ title: string;
14
+ };
15
+ collections?: Map<string, import("./compiler/collections.js").CollectionRow[]>;
16
+ vars?: Record<string, unknown>;
17
+ reader?: import("./compiler/reader.js").Reader;
18
+ }): CompiledPage;
8
19
  /**
9
- * Compile a source file into its body HTML, frontmatter, and assets (no page shell).
20
+ * Compile a source file into its body HTML, frontmatter, and assets (no page
21
+ * shell), reading from the real filesystem by default.
10
22
  * @param {string} file Absolute path to the source file.
11
- * @param {Paths} context Resolved project paths.
23
+ * @param {import("./config.js").Paths} context Resolved project paths.
12
24
  * @param {string[]} [stack] Include stack for cycle detection.
13
25
  * @param {Record<string, unknown>} [vars] Initial static scope variables.
26
+ * @param {Map<string, import("./compiler/collections.js").CollectionRow[]>} [collections]
27
+ * @param {import("./compiler/reader.js").Reader} [reader] Source reader.
14
28
  * @returns {CompiledDocument}
15
29
  */
16
- export function compileDocument(file: string, context: Paths, stack?: string[], vars?: Record<string, unknown>): CompiledDocument;
17
- /**
18
- * Compile a page from an in-memory map of project-relative path → source, with
19
- * no filesystem access. `entryPath` is the project-relative path of the page to
20
- * compile; includes and `@loop` data resolve against the same map.
21
- * @param {Record<string, string> | Map<string, string>} files
22
- * @param {string} entryPath
23
- * @param {{ vars?: Record<string, unknown>, cwd?: string }} [options]
24
- * @returns {CompiledPage}
25
- */
26
- export function compileFromMemory(files: Record<string, string> | Map<string, string>, entryPath: string, options?: {
27
- vars?: Record<string, unknown>;
28
- cwd?: string;
29
- }): CompiledPage;
30
- /**
31
- * Split a raw file into its frontmatter `meta` and `body`.
32
- * @param {string} raw Full file contents.
33
- * @param {string} [file] Source path, used only for error messages.
34
- * @returns {{ meta: Meta, body: string }}
35
- */
36
- export function parseFrontmatter(raw: string, file?: string): {
37
- meta: Meta;
38
- body: string;
39
- };
30
+ export function compileDocument(file: string, context: import("./config.js").Paths, stack?: string[], vars?: Record<string, unknown>, collections?: Map<string, import("./compiler/collections.js").CollectionRow[]>, reader?: import("./compiler/reader.js").Reader): CompiledDocument;
40
31
  /**
41
- * Stable per-render key for a loop row, disambiguating duplicates with `#n`.
42
- * @param {unknown} item
43
- * @param {Map<string, number>} counts Mutable seen-count accumulator.
32
+ * Harden every `<img>` in an assembled page body, reading intrinsic dimensions
33
+ * from the real filesystem by default.
34
+ * @param {string} html Assembled page body HTML.
35
+ * @param {import("./config.js").Paths} paths Resolved project paths.
36
+ * @param {import("./compiler/reader.js").Reader} [reader] Source reader for image bytes.
44
37
  * @returns {string}
45
38
  */
46
- export function loopKeyOf(item: unknown, counts: Map<string, number>): string;
47
- /**
48
- * Escape a value for safe inclusion in HTML text and attribute contexts.
49
- * @param {unknown} value
50
- * @returns {string}
51
- */
52
- export function escapeHtml(value: unknown): string;
53
- /**
54
- * A loop offset/limit argument: a non-negative integer literal or a state key.
55
- */
56
- export type NumArg = {
57
- kind: "literal";
58
- value: number;
59
- } | {
60
- kind: "key";
61
- value: string;
62
- };
63
- /**
64
- * A compiled `@loop … where` predicate.
65
- */
66
- export type Predicate = {
67
- /**
68
- * Safe JS boolean expression over `I()`/`S()`/`C()`.
69
- */
70
- body: string;
71
- /**
72
- * Whether the predicate reads declared state.
73
- */
74
- refsState: boolean;
75
- };
76
- /**
77
- * Loop clause configuration shared by the static and reactive paths.
78
- */
79
- export type LoopOpts = {
80
- where: Predicate | null;
81
- sort: {
82
- key: string;
83
- dir: string;
84
- } | null;
85
- reverse: boolean;
86
- offset: NumArg | null;
87
- limit: NumArg | null;
88
- /**
89
- * Empty-branch body lines, if any.
90
- */
91
- empty: string[] | null;
92
- /**
93
- * Whether offset/limit reference state.
94
- */
95
- clauseRefsState: boolean;
96
- };
97
- export type Paths = import("./config.js").Paths;
98
- /**
99
- * Frontmatter values are scalars or inline arrays of scalars.
100
- */
101
- export type FrontmatterValue = string | number | boolean | null | Array<string | number | boolean | null>;
102
- /**
103
- * Parsed page frontmatter, keyed by field name.
104
- */
105
- export type Meta = Record<string, FrontmatterValue>;
106
- /**
107
- * Collected output assets for a compiled document.
108
- */
109
- export type Assets = {
110
- /**
111
- * Public hrefs of compiled skin stylesheets.
112
- */
113
- skins: Set<string>;
114
- /**
115
- * Public hrefs of colocated page scripts.
116
- */
117
- scripts: Set<string>;
118
- /**
119
- * Source path → public href for emitted assets.
120
- */
121
- files: Map<string, string>;
122
- /**
123
- * Whether the reactive runtime is required.
124
- */
125
- runtime: boolean;
126
- };
127
- /**
128
- * Per-document compilation accumulator shared across includes/sections.
129
- */
130
- export type Compilation = {
131
- assets: Assets;
132
- /**
133
- * Declared state keys → initial values.
134
- */
135
- state: Map<string, unknown>;
136
- /**
137
- * Page-global store names (a subset of state keys).
138
- */
139
- stores: Set<string>;
140
- /**
141
- * Non-fatal authoring hints.
142
- */
143
- warnings: string[];
144
- /**
145
- * Counter for auto-generated section ids.
146
- */
147
- sectionCounter: number;
148
- };
149
- /**
150
- * A lexical scope chain for static interpolation values (include args, loop vars).
151
- */
152
- export type Scope = {
153
- parent: Scope | null;
154
- vars: Record<string, unknown>;
155
- };
156
- /**
157
- * Per-file compile context threaded through the directive handlers.
158
- */
159
- export type Ctx = {
160
- /**
161
- * Absolute path to the file being compiled.
162
- */
163
- file: string;
164
- /**
165
- * Resolved project paths.
166
- */
167
- context: Paths;
168
- /**
169
- * Include stack (real paths) for cycle detection.
170
- */
171
- stack: string[];
172
- /**
173
- * Static value scope chain.
174
- */
175
- scope: Scope;
176
- /**
177
- * Shared compilation accumulator.
178
- */
179
- comp: Compilation;
180
- /**
181
- * Active section-id scope chain.
182
- */
183
- sections: string[];
184
- /**
185
- * Name of the current reactive loop item, if any.
186
- */
187
- loopItem: string | null;
188
- /**
189
- * State key of the list being looped, if any.
190
- */
191
- loopKey?: string | undefined;
192
- /**
193
- * Inside a loop body, so `$index`/`$first`/… are valid.
194
- */
195
- loopMeta?: boolean | undefined;
196
- /**
197
- * Markdown-it instance selected for this file.
198
- */
199
- md?: MarkdownIt | undefined;
200
- };
201
- /**
202
- * A compiled page document.
203
- */
204
- export type CompiledDocument = {
205
- meta: Meta;
206
- html: string;
207
- assets: Assets;
208
- warnings: string[];
209
- };
210
- /**
211
- * A compiled page: the document plus its full HTML shell.
212
- */
213
- export type CompiledPage = {
214
- meta: Meta;
215
- html: string;
216
- assets: Assets;
217
- warnings: string[];
218
- };
219
- /**
220
- * A parsed `:button` action.
221
- */
222
- export type Action = {
223
- /**
224
- * Runtime operation (inc/dec/add/sub/append/prepend/set/toggle/member-toggle/remove/remove-value/clear/merge/delete/reset/append-row).
225
- */
226
- op: string;
227
- /**
228
- * State key (possibly dotted) the action mutates.
229
- */
230
- target: string;
231
- /**
232
- * Literal value for value-carrying ops.
233
- */
234
- value?: unknown;
235
- };
236
- import MarkdownIt from "markdown-it";
39
+ export function enhanceImages(html: string, paths: import("./config.js").Paths, reader?: import("./compiler/reader.js").Reader): string;
40
+ export type Meta = import("./compiler/context.js").Meta;
41
+ export type FrontmatterValue = import("./compiler/context.js").FrontmatterValue;
42
+ export type Assets = import("./compiler/context.js").Assets;
43
+ export type Compilation = import("./compiler/context.js").Compilation;
44
+ export type Scope = import("./compiler/context.js").Scope;
45
+ export type Ctx = import("./compiler/context.js").Ctx;
46
+ export type CompiledDocument = import("./compiler/context.js").CompiledDocument;
47
+ export type CompiledPage = import("./compiler/context.js").CompiledPage;
48
+ export { compileFromMemory, escapeHtml, loopKeyOf, parseFrontmatter, stampScope } from "./compiler/index.js";
package/types/dev.d.ts CHANGED
@@ -1,9 +1,54 @@
1
1
  /**
2
- * Inject the dev-client `<script>` before `</body>` (or append it if absent).
2
+ * Serialize the dev server's rebuilds: at most ONE child build runs at a time.
3
+ * Every build read-modify-writes the dependency map (`dist/.wd-dev-deps.json`),
4
+ * so overlapping children could persist a stale map missing deps a concurrent
5
+ * build just recorded — after which the affected routes silently stop
6
+ * rebuilding. Changes debounce into a batch; changes arriving MID-build
7
+ * accumulate into the next batch (duplicate paths coalesce via the Set, and a
8
+ * `null` — a `src/` change or an unattributable event, forcing a full rebuild —
9
+ * swallows the whole batch into one full build).
10
+ * @param {(changed: string[]) => Promise<void> | void} runBuild Runs one child
11
+ * build over the changed `site/` paths (`[]` = full rebuild). Build failures
12
+ * are its own concern (report + resolve); a rejection still frees the queue.
13
+ * @param {number} [debounceMs] Debounce window for batching change events.
14
+ * @returns {{ change: (path: string | null) => void, close: () => void }}
15
+ */
16
+ export function createRebuildQueue(runBuild: (changed: string[]) => Promise<void> | void, debounceMs?: number): {
17
+ change: (path: string | null) => void;
18
+ close: () => void;
19
+ };
20
+ /**
21
+ * Inject the dev-client `<script>` before `</body>` (or append it if absent),
22
+ * plus — for a `draft: true` page — an inline draft-banner script. Both are
23
+ * injected at dev-SERVE time only, so neither ever touches the shipped HTML on
24
+ * disk (a production build excludes drafts entirely; a `build --drafts` staging
25
+ * build emits clean HTML with no banner).
3
26
  * @param {string} html Page HTML to augment.
27
+ * @param {{ draft?: boolean }} [options] `draft: true` adds the visible banner.
28
+ * @returns {string}
29
+ */
30
+ export function injectDevClient(html: string, options?: {
31
+ draft?: boolean;
32
+ }): string;
33
+ /**
34
+ * The inline dev-only draft banner: a fixed bar marking the page as an unpublished
35
+ * draft, so it's unmistakable in `darkmown dev` that this content is excluded from
36
+ * a production build. Inert (`pointer-events:none`) and high-z so it never blocks
37
+ * the page; dev-serve-injected, so it's never in shipped output.
38
+ * @returns {string}
39
+ */
40
+ export function draftBannerScript(): string;
41
+ /**
42
+ * The page the dev server serves for a route with no dist output while the last
43
+ * build is FAILED — the honest alternative to the production 404 copy, which
44
+ * would claim the route "is hidden or has not been created" when the real cause
45
+ * is a compile error. The message renders inline (works without JS) and the
46
+ * injected dev client replays the `builderror` overlay on connect, then reloads
47
+ * the real page on the next successful build.
48
+ * @param {string} message The recorded build failure.
4
49
  * @returns {string}
5
50
  */
6
- export function injectDevClient(html: string): string;
51
+ export function buildFailedPage(message: string): string;
7
52
  /**
8
53
  * The dev-client browser script that opens the SSE channel for live reload.
9
54
  * @returns {string}