@zvndev/darkmown 2.2.0 → 2.3.1-next.1.g3a4a8dc
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +34 -2
- package/docs/spec-alignment.md +10 -0
- package/package.json +16 -11
- package/site/pages/docs/index.wd +14 -0
- package/site/pages/playground/index.js +47 -46
- package/src/catalog.js +16 -1
- package/src/compiler/actions.js +42 -22
- package/src/compiler/body.js +36 -14
- package/src/compiler/collections.js +16 -10
- package/src/compiler/context.js +39 -10
- package/src/compiler/fetch.js +39 -15
- package/src/compiler/format.js +5 -2
- package/src/compiler/forms.js +91 -26
- package/src/compiler/frontmatter.js +11 -2
- package/src/compiler/fs-reader.js +2 -1
- package/src/compiler/image-size.js +340 -0
- package/src/compiler/includes.js +8 -4
- package/src/compiler/interpolation.js +15 -7
- package/src/compiler/loops.js +63 -35
- package/src/compiler/markdown.js +4 -3
- package/src/compiler/media.js +17 -3
- package/src/compiler/page.js +8 -6
- package/src/compiler/predicates.js +49 -24
- package/src/compiler/reader.js +12 -2
- package/src/compiler/state.js +16 -2
- package/src/compiler/structure.js +24 -14
- package/src/deploy.js +8 -4
- package/src/errors.js +1156 -0
- package/src/router.js +7 -4
- package/src/scaffold.js +4 -2
- package/src/skin.js +17 -6
- package/types/api-runner.d.ts +94 -0
- package/types/builder.d.ts +141 -10
- package/types/catalog.d.ts +97 -0
- package/types/compiler/actions.d.ts +41 -0
- package/types/compiler/body.d.ts +11 -0
- package/types/compiler/collections.d.ts +76 -0
- package/types/compiler/context.d.ts +647 -0
- package/types/compiler/directives.d.ts +6 -0
- package/types/compiler/expr-ast.d.ts +20 -0
- package/types/compiler/fetch.d.ts +29 -0
- package/types/compiler/format.d.ts +49 -0
- package/types/compiler/forms.d.ts +94 -0
- package/types/compiler/frontmatter.d.ts +33 -0
- package/types/compiler/fs-reader.d.ts +7 -0
- package/types/compiler/image-size.d.ts +14 -0
- package/types/compiler/includes.d.ts +100 -0
- package/types/compiler/index.d.ts +5 -0
- package/types/compiler/interpolation.d.ts +115 -0
- package/types/compiler/loops.d.ts +36 -0
- package/types/compiler/markdown.d.ts +34 -0
- package/types/compiler/media.d.ts +32 -0
- package/types/compiler/page.d.ts +109 -0
- package/types/compiler/predicates.d.ts +50 -0
- package/types/compiler/reader.d.ts +48 -0
- package/types/compiler/state.d.ts +68 -0
- package/types/compiler/structure.d.ts +56 -0
- package/types/compiler.d.ts +35 -223
- package/types/dev.d.ts +47 -2
- package/types/errors.d.ts +100 -0
- package/types/feeds.d.ts +150 -0
- package/types/headers.d.ts +44 -0
- package/types/highlight.d.ts +24 -0
- package/types/router.d.ts +42 -3
- package/types/scaffold.d.ts +15 -3
- package/types/skin.d.ts +28 -1
- package/types/src/api-runner.d.ts +94 -0
- package/types/src/builder.d.ts +170 -0
- package/types/src/catalog.d.ts +97 -0
- package/types/src/compiler/actions.d.ts +41 -0
- package/types/src/compiler/body.d.ts +11 -0
- package/types/src/compiler/collections.d.ts +76 -0
- package/types/src/compiler/context.d.ts +647 -0
- package/types/src/compiler/directives.d.ts +6 -0
- package/types/src/compiler/expr-ast.d.ts +20 -0
- package/types/src/compiler/fetch.d.ts +29 -0
- package/types/src/compiler/format.d.ts +49 -0
- package/types/src/compiler/forms.d.ts +94 -0
- package/types/src/compiler/frontmatter.d.ts +33 -0
- package/types/src/compiler/fs-reader.d.ts +7 -0
- package/types/src/compiler/image-size.d.ts +14 -0
- package/types/src/compiler/includes.d.ts +100 -0
- package/types/src/compiler/index.d.ts +5 -0
- package/types/src/compiler/interpolation.d.ts +115 -0
- package/types/src/compiler/loops.d.ts +36 -0
- package/types/src/compiler/markdown.d.ts +34 -0
- package/types/src/compiler/media.d.ts +32 -0
- package/types/src/compiler/page.d.ts +109 -0
- package/types/src/compiler/predicates.d.ts +50 -0
- package/types/src/compiler/reader.d.ts +48 -0
- package/types/src/compiler/state.d.ts +68 -0
- package/types/src/compiler/structure.d.ts +56 -0
- package/types/src/compiler.d.ts +48 -0
- package/types/src/config.d.ts +45 -0
- package/types/src/dev.d.ts +58 -0
- package/types/src/errors.d.ts +100 -0
- package/types/src/feeds.d.ts +150 -0
- package/types/src/headers.d.ts +44 -0
- package/types/src/highlight.d.ts +24 -0
- package/types/src/router.d.ts +78 -0
- package/types/src/scaffold.d.ts +20 -0
- package/types/src/skin.d.ts +48 -0
- package/types/src/statics.d.ts +24 -0
- package/types/statics.d.ts +2 -1
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import("./router.js").Route} Route
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* A route paired with its source file's last-modified date (ISO yyyy-mm-dd),
|
|
6
|
+
* ready for a sitemap `<lastmod>`.
|
|
7
|
+
* @typedef {object} DatedRoute
|
|
8
|
+
* @property {string} loc Absolute URL (`site_url` + route path).
|
|
9
|
+
* @property {string} lastmod ISO date (`yyyy-mm-dd`).
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* XML-escape a text value for use inside an element or attribute. Covers the
|
|
13
|
+
* five predefined XML entities — the same set RSS/sitemap consumers expect.
|
|
14
|
+
* @param {string} value
|
|
15
|
+
* @returns {string}
|
|
16
|
+
*/
|
|
17
|
+
export function escapeXml(value: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Join the site origin and a route path into one absolute URL, collapsing the
|
|
20
|
+
* boundary slash so `https://x.com` + `/about/` → `https://x.com/about/` (the
|
|
21
|
+
* home route `/` → `https://x.com/`). `siteUrl` is expected without a trailing
|
|
22
|
+
* slash (the documented contract); a stray one is tolerated.
|
|
23
|
+
* @param {string} siteUrl Absolute origin, e.g. `https://example.com`.
|
|
24
|
+
* @param {string} route Route path (always trailing-slashed, e.g. `/about/`).
|
|
25
|
+
* @returns {string}
|
|
26
|
+
*/
|
|
27
|
+
export function absoluteUrl(siteUrl: string, route: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* The last-modified date for a source file, as an ISO `yyyy-mm-dd` string.
|
|
30
|
+
* Resolution order: the file's frontmatter `date:` is handled by the caller
|
|
31
|
+
* (it wins); this helper covers the fallback chain — the git last-commit date
|
|
32
|
+
* of the file, else the filesystem mtime. Git is wrapped in try/catch so a
|
|
33
|
+
* scaffolded app or an extracted tarball (no `.git`) degrades to mtime instead
|
|
34
|
+
* of crashing the build.
|
|
35
|
+
* @param {string} file Absolute path to the source file.
|
|
36
|
+
* @returns {string} ISO date (`yyyy-mm-dd`).
|
|
37
|
+
*/
|
|
38
|
+
export function lastmodFor(file: string): string;
|
|
39
|
+
/**
|
|
40
|
+
* Format a `Date` as an ISO `yyyy-mm-dd` string (sitemap `<lastmod>` form).
|
|
41
|
+
* @param {Date} date
|
|
42
|
+
* @returns {string}
|
|
43
|
+
*/
|
|
44
|
+
export function isoDate(date: Date): string;
|
|
45
|
+
/**
|
|
46
|
+
* Format a frontmatter `date:` value as an RFC-822 date for an RSS `<pubDate>`
|
|
47
|
+
* (e.g. `Sat, 28 Jun 2026 00:00:00 GMT`). Accepts anything `new Date` parses
|
|
48
|
+
* (an ISO `yyyy-mm-dd`, a full timestamp); an unparseable value returns "" so
|
|
49
|
+
* the caller can omit the element rather than emit `Invalid Date`.
|
|
50
|
+
* @param {string} value Raw frontmatter date string.
|
|
51
|
+
* @returns {string} RFC-822 date in GMT, or "" when unparseable.
|
|
52
|
+
*/
|
|
53
|
+
export function rfc822(value: string): string;
|
|
54
|
+
/**
|
|
55
|
+
* Extract the first paragraph of a plain-`.md` body as a single-line snippet —
|
|
56
|
+
* the RSS description fallback when a `.md` post has neither `excerpt:` nor
|
|
57
|
+
* `description:`. Deliberately dumb: skip leading blank/frontmatter-less lines,
|
|
58
|
+
* take the first run of non-blank prose, strip the most common inline markdown
|
|
59
|
+
* (heading marks, emphasis, inline-code, links → their text), and collapse
|
|
60
|
+
* whitespace. Returns "" when the body is empty or starts with a non-prose
|
|
61
|
+
* block (a fence/heading-only doc) — the caller then falls back further.
|
|
62
|
+
* @param {string} body The post-frontmatter markdown body.
|
|
63
|
+
* @returns {string}
|
|
64
|
+
*/
|
|
65
|
+
export function firstParagraph(body: string): string;
|
|
66
|
+
/**
|
|
67
|
+
* The RSS `<description>` for a post: explicit `excerpt:` wins, else the source
|
|
68
|
+
* `description:`, else (plain `.md` only) the first rendered paragraph. `.wd`
|
|
69
|
+
* bodies are NOT scanned for a first paragraph — stripping reactive/directive
|
|
70
|
+
* markup back to clean prose is not worth the fragility, so a `.wd` post without
|
|
71
|
+
* `excerpt`/`description` simply gets no description.
|
|
72
|
+
* @param {import("./compiler.js").Meta} meta
|
|
73
|
+
* @param {string} file Absolute source path (its extension gates `.md` scanning).
|
|
74
|
+
* @param {string} body The post-frontmatter body.
|
|
75
|
+
* @returns {string}
|
|
76
|
+
*/
|
|
77
|
+
export function rssDescription(meta: import("./compiler.js").Meta, file: string, body: string): string;
|
|
78
|
+
/**
|
|
79
|
+
* Build `sitemap.xml` from the emitted (post-draft-filter) routes. One `<url>`
|
|
80
|
+
* per route — reactive pages included (they're indexable HTML) — with `<loc>` =
|
|
81
|
+
* `site_url` + path and `<lastmod>` from the supplied date. The `/404/` route is
|
|
82
|
+
* excluded by the caller before this point. No `<priority>`/`<changefreq>` —
|
|
83
|
+
* Google ignores them and they invite bit-rot.
|
|
84
|
+
* @param {DatedRoute[]} entries Absolute loc + ISO lastmod, in route order.
|
|
85
|
+
* @returns {string} The full XML document (trailing newline).
|
|
86
|
+
*/
|
|
87
|
+
export function buildSitemap(entries: DatedRoute[]): string;
|
|
88
|
+
/**
|
|
89
|
+
* An RSS item — a dated post in feed order.
|
|
90
|
+
* @typedef {object} RssItem
|
|
91
|
+
* @property {string} title
|
|
92
|
+
* @property {string} link Absolute URL (also used as the `<guid>`).
|
|
93
|
+
* @property {string} pubDate RFC-822 date, or "" to omit.
|
|
94
|
+
* @property {string} description
|
|
95
|
+
*/
|
|
96
|
+
/**
|
|
97
|
+
* Build `rss.xml` (RSS 2.0) for the dated posts. The caller selects, sorts
|
|
98
|
+
* (date desc), and caps the items; this builder just renders. Channel `<title>`
|
|
99
|
+
* / `<description>` come from the home frontmatter; `<link>` is the site origin
|
|
100
|
+
* and the `<atom:link rel="self">` points at the feed itself (RSS best practice).
|
|
101
|
+
* @param {{ title: string, description: string, siteUrl: string, feedUrl: string }} channel
|
|
102
|
+
* @param {RssItem[]} items
|
|
103
|
+
* @returns {string} The full XML document (trailing newline).
|
|
104
|
+
*/
|
|
105
|
+
export function buildRss(channel: {
|
|
106
|
+
title: string;
|
|
107
|
+
description: string;
|
|
108
|
+
siteUrl: string;
|
|
109
|
+
feedUrl: string;
|
|
110
|
+
}, items: RssItem[]): string;
|
|
111
|
+
/**
|
|
112
|
+
* Build `robots.txt`. Always allows everything (hidden/draft routes are never
|
|
113
|
+
* built, so there's nothing to `Disallow`). The `Sitemap:` line is included
|
|
114
|
+
* ONLY when `siteUrl` is set — a sitemap URL is only meaningful with an origin.
|
|
115
|
+
* @param {string} [siteUrl] Absolute origin, or "" / undefined to omit the line.
|
|
116
|
+
* @returns {string} The robots.txt body (trailing newline).
|
|
117
|
+
*/
|
|
118
|
+
export function buildRobots(siteUrl?: string): string;
|
|
119
|
+
/** Most-recent-first RSS post cap — a feed is a recent window, not an archive. */
|
|
120
|
+
export const RSS_ITEM_LIMIT: 20;
|
|
121
|
+
export type Route = import("./router.js").Route;
|
|
122
|
+
/**
|
|
123
|
+
* A route paired with its source file's last-modified date (ISO yyyy-mm-dd),
|
|
124
|
+
* ready for a sitemap `<lastmod>`.
|
|
125
|
+
*/
|
|
126
|
+
export type DatedRoute = {
|
|
127
|
+
/**
|
|
128
|
+
* Absolute URL (`site_url` + route path).
|
|
129
|
+
*/
|
|
130
|
+
loc: string;
|
|
131
|
+
/**
|
|
132
|
+
* ISO date (`yyyy-mm-dd`).
|
|
133
|
+
*/
|
|
134
|
+
lastmod: string;
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* An RSS item — a dated post in feed order.
|
|
138
|
+
*/
|
|
139
|
+
export type RssItem = {
|
|
140
|
+
title: string;
|
|
141
|
+
/**
|
|
142
|
+
* Absolute URL (also used as the `<guid>`).
|
|
143
|
+
*/
|
|
144
|
+
link: string;
|
|
145
|
+
/**
|
|
146
|
+
* RFC-822 date, or "" to omit.
|
|
147
|
+
*/
|
|
148
|
+
pubDate: string;
|
|
149
|
+
description: string;
|
|
150
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The full security-header set for one response, including the CSP variant.
|
|
3
|
+
* @param {boolean} reactive Whether the route ships the reactive runtime.
|
|
4
|
+
* @returns {Record<string, string>}
|
|
5
|
+
*/
|
|
6
|
+
export function securityHeaders(reactive: boolean): Record<string, string>;
|
|
7
|
+
/**
|
|
8
|
+
* The exact body of the inline `<script type="speculationrules">` block that
|
|
9
|
+
* `compilePage` emits for `transitions: true` pages. Kept in sync with
|
|
10
|
+
* `src/compiler/page.js` by a drift-guard test that hashes the block out of a
|
|
11
|
+
* real build — if the emitted rules change, that test fails before the CSP
|
|
12
|
+
* silently starts blocking prerender.
|
|
13
|
+
* @type {string}
|
|
14
|
+
*/
|
|
15
|
+
export const SPECULATION_RULES_JSON: string;
|
|
16
|
+
/**
|
|
17
|
+
* CSP hash source for the speculationrules block — computed from the constant
|
|
18
|
+
* above at module load, never hand-maintained.
|
|
19
|
+
* @type {string}
|
|
20
|
+
*/
|
|
21
|
+
export const SPECULATION_RULES_HASH: string;
|
|
22
|
+
/**
|
|
23
|
+
* CSP for reactive pages. Since 2.1 the runtime WALKS a compile-time-validated
|
|
24
|
+
* expression AST instead of building a `new Function`, so reactive pages no longer
|
|
25
|
+
* need `'unsafe-eval'` — this policy is byte-identical to the static one. The only
|
|
26
|
+
* script source is same-origin `/__wd/runtime.js` (plus the hashed speculationrules
|
|
27
|
+
* block); no `'unsafe-inline'`, no `'unsafe-eval'`.
|
|
28
|
+
* @type {string}
|
|
29
|
+
*/
|
|
30
|
+
export const REACTIVE_CSP: string;
|
|
31
|
+
/**
|
|
32
|
+
* CSP for static pages (zero framework JS). Identical to the reactive policy now
|
|
33
|
+
* that neither variant evals — kept as a distinct export so the per-route emit and
|
|
34
|
+
* the `securityHeaders()` callers stay explicit about intent. Inline `<style>`
|
|
35
|
+
* (view transitions) keeps `'unsafe-inline'` on style-src only.
|
|
36
|
+
* @type {string}
|
|
37
|
+
*/
|
|
38
|
+
export const STATIC_CSP: string;
|
|
39
|
+
/**
|
|
40
|
+
* Baseline security headers applied to every HTML response, independent of the
|
|
41
|
+
* CSP. CSP is added per-route (static vs reactive) by the helpers below.
|
|
42
|
+
* @type {Record<string, string>}
|
|
43
|
+
*/
|
|
44
|
+
export const BASE_SECURITY_HEADERS: Record<string, string>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* markdown-it `highlight` callback. Returns highlight.js's escaped token HTML for
|
|
3
|
+
* a known language (wrapped so the `hljs` marker is always present), or `""` to
|
|
4
|
+
* defer to markdown-it's own escaping for an unknown/absent language — the
|
|
5
|
+
* graceful-degradation path that renders a plain `<code>` with no highlighting.
|
|
6
|
+
*
|
|
7
|
+
* markdown-it does NOT escape a non-empty return value (it is trusted HTML), so
|
|
8
|
+
* we must return already-escaped content — `hljs.highlight` escapes its output.
|
|
9
|
+
* @param {string} code The fenced block's raw source.
|
|
10
|
+
* @param {string} lang The info string (language) after the opening fence, if any.
|
|
11
|
+
* @returns {string} Highlighted, escaped HTML, or `""` to defer to markdown-it.
|
|
12
|
+
*/
|
|
13
|
+
export function highlightCode(code: string, lang: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Detect whether a rendered HTML fragment contains a highlighted code block, so
|
|
16
|
+
* the page-level `assets.hasCode` flag (and thus the highlight stylesheet) is set
|
|
17
|
+
* only when highlighting actually happened. Matches the `hljs` marker stamped by
|
|
18
|
+
* {@link highlightCode}; a page with only plain/unknown-language code never trips it.
|
|
19
|
+
* @param {string} html Rendered body HTML.
|
|
20
|
+
* @returns {boolean}
|
|
21
|
+
*/
|
|
22
|
+
export function htmlHasHighlight(html: string): boolean;
|
|
23
|
+
export const HLJS_MARKER: "hljs";
|
|
24
|
+
export const HIGHLIGHT_CSS: "/* Darkmown syntax highlighting \u2014 highlight.js classes mapped to $code-* skin\n tokens. Dark mode is automatic: the tokens swap under tokens dark / :theme,\n so this stylesheet ships zero JavaScript. */\n.hljs {\n color: var(--code-fg, #e9efe7);\n background: var(--code-bg, #1b2420);\n}\n.hljs-comment,\n.hljs-quote {\n color: var(--code-comment, #7d8a82);\n font-style: italic;\n}\n.hljs-keyword,\n.hljs-selector-tag,\n.hljs-literal,\n.hljs-section,\n.hljs-doctag,\n.hljs-name {\n color: var(--code-keyword, #d39bda);\n}\n.hljs-string,\n.hljs-regexp,\n.hljs-meta .hljs-string,\n.hljs-attr,\n.hljs-selector-attr,\n.hljs-selector-pseudo,\n.hljs-addition {\n color: var(--code-string, #8fd28a);\n}\n.hljs-title,\n.hljs-title.function_,\n.hljs-built_in,\n.hljs-class .hljs-title,\n.hljs-selector-class,\n.hljs-selector-id {\n color: var(--code-function, #82c0e9);\n}\n.hljs-number,\n.hljs-symbol,\n.hljs-bullet,\n.hljs-link,\n.hljs-meta {\n color: var(--code-number, #e3a76f);\n}\n.hljs-variable,\n.hljs-template-variable,\n.hljs-type,\n.hljs-attribute,\n.hljs-params,\n.hljs-tag,\n.hljs-deletion,\n.hljs-punctuation,\n.hljs-operator {\n color: var(--code-punctuation, #b9c4be);\n}\n.hljs-emphasis {\n font-style: italic;\n}\n.hljs-strong {\n font-weight: 600;\n}\n";
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import("./compiler.js").Meta} Meta
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* A discovered page route: the source file, its public route path, and its
|
|
6
|
+
* parsed frontmatter. `meta` is read ONCE here — the single source of truth for
|
|
7
|
+
* "is this a draft" and for the downstream sitemap/RSS feeds (which never see a
|
|
8
|
+
* draft, since drafts are filtered out below).
|
|
9
|
+
* @typedef {object} Route
|
|
10
|
+
* @property {string} file Absolute path to the source `.md`/`.wd` file.
|
|
11
|
+
* @property {string} route Public route path (always trailing-slashed, e.g. `/about/`).
|
|
12
|
+
* @property {Meta} meta Parsed frontmatter for the file.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Walk `routesRoot` and collect page routes, sorted and de-duplicated.
|
|
16
|
+
*
|
|
17
|
+
* Drafts are filtered HERE, at the one route-discovery chokepoint, so every
|
|
18
|
+
* downstream consumer (the build, `routes.json`, the sitemap, the RSS feed)
|
|
19
|
+
* only ever sees the post-filter list — a `draft: true` page can never leak. By
|
|
20
|
+
* default drafts are excluded; pass `{ includeDrafts: true }` (`darkmown dev`
|
|
21
|
+
* and `darkmown build --drafts`) to keep them. This is distinct from, and
|
|
22
|
+
* orthogonal to, the permanent `.`/`-`/`_` filename hiding above: hidden names
|
|
23
|
+
* are private forever; `draft:` is a toggleable, publish-when-ready gate.
|
|
24
|
+
* @param {string} routesRoot Absolute path to `site/pages`.
|
|
25
|
+
* @param {{ includeDrafts?: boolean }} [options]
|
|
26
|
+
* @returns {Route[]}
|
|
27
|
+
*/
|
|
28
|
+
export function discoverRoutes(routesRoot: string, options?: {
|
|
29
|
+
includeDrafts?: boolean;
|
|
30
|
+
}): Route[];
|
|
31
|
+
/**
|
|
32
|
+
* Whether a page's frontmatter marks it a draft (`draft: true`). The scalar
|
|
33
|
+
* frontmatter parser yields a boolean `true` or the string `"true"`, so accept
|
|
34
|
+
* both; any other value (absent, `false`, `"false"`) is not a draft.
|
|
35
|
+
* @param {Meta} meta
|
|
36
|
+
* @returns {boolean}
|
|
37
|
+
*/
|
|
38
|
+
export function isDraft(meta: Meta): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Derive a public route path from a source file path.
|
|
41
|
+
* @param {string} routesRoot Absolute path to `site/pages`.
|
|
42
|
+
* @param {string} file Absolute path to the source file.
|
|
43
|
+
* @returns {string}
|
|
44
|
+
*/
|
|
45
|
+
export function routeFromFile(routesRoot: string, file: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Map a public route to the `index.html` output path under `distRoot`.
|
|
48
|
+
*
|
|
49
|
+
* Defense in depth: routes come from `routeFromFile` walking `site/pages`, so
|
|
50
|
+
* they can't normally carry traversal segments — but any future caller passing
|
|
51
|
+
* an unvetted route must not be able to write outside the build output. The
|
|
52
|
+
* resolved path is asserted to stay inside `distRoot`.
|
|
53
|
+
* @param {string} distRoot Absolute path to the build output directory.
|
|
54
|
+
* @param {string} route Public route path.
|
|
55
|
+
* @returns {string}
|
|
56
|
+
*/
|
|
57
|
+
export function outputPathForRoute(distRoot: string, route: string): string;
|
|
58
|
+
export type Meta = import("./compiler.js").Meta;
|
|
59
|
+
/**
|
|
60
|
+
* A discovered page route: the source file, its public route path, and its
|
|
61
|
+
* parsed frontmatter. `meta` is read ONCE here — the single source of truth for
|
|
62
|
+
* "is this a draft" and for the downstream sitemap/RSS feeds (which never see a
|
|
63
|
+
* draft, since drafts are filtered out below).
|
|
64
|
+
*/
|
|
65
|
+
export type Route = {
|
|
66
|
+
/**
|
|
67
|
+
* Absolute path to the source `.md`/`.wd` file.
|
|
68
|
+
*/
|
|
69
|
+
file: string;
|
|
70
|
+
/**
|
|
71
|
+
* Public route path (always trailing-slashed, e.g. `/about/`).
|
|
72
|
+
*/
|
|
73
|
+
route: string;
|
|
74
|
+
/**
|
|
75
|
+
* Parsed frontmatter for the file.
|
|
76
|
+
*/
|
|
77
|
+
meta: Meta;
|
|
78
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* List the available scaffold templates (directory names under `src/templates`).
|
|
3
|
+
* @returns {string[]}
|
|
4
|
+
*/
|
|
5
|
+
export function availableTemplates(): string[];
|
|
6
|
+
/**
|
|
7
|
+
* Scaffold a new Darkmown project into `root` from a template, creating files
|
|
8
|
+
* that don't already exist (never overwriting). `package.json` is generated (so
|
|
9
|
+
* the name tracks the directory and the `@zvndev/darkmown` dep pins the installed
|
|
10
|
+
* version); every other file is copied verbatim from the chosen template.
|
|
11
|
+
* @param {string} root Absolute path to the target project directory.
|
|
12
|
+
* @param {{ template?: string }} [options] `template` defaults to `"starter"`.
|
|
13
|
+
* @returns {{ root: string, template: string }}
|
|
14
|
+
*/
|
|
15
|
+
export function initProject(root: string, options?: {
|
|
16
|
+
template?: string;
|
|
17
|
+
}): {
|
|
18
|
+
root: string;
|
|
19
|
+
template: string;
|
|
20
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derive a short, deterministic scope id from a skin's PROJECT-RELATIVE PATH.
|
|
3
|
+
* Path-based (stable across content edits, unique per component file) and short
|
|
4
|
+
* enough to read in the emitted CSS (`wd-7c21`). The same path always yields the
|
|
5
|
+
* same id, so the CSS rewrite (builder) and the HTML stamp (page/include) agree
|
|
6
|
+
* without sharing state — they just hash the same relative path. The path is
|
|
7
|
+
* normalized to POSIX separators first so a build is identical on Windows.
|
|
8
|
+
* @param {string} relPath Skin path relative to the project root (cwd).
|
|
9
|
+
* @returns {string} The scope id, e.g. `wd-7c21`.
|
|
10
|
+
*/
|
|
11
|
+
export function scopeIdFor(relPath: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* A nesting frame in the indentation stack.
|
|
14
|
+
* @typedef {object} SkinFrame
|
|
15
|
+
* @property {number} indent
|
|
16
|
+
* @property {string | null} selector
|
|
17
|
+
* @property {string} [media]
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Compile indentation-based `.skin` source into CSS.
|
|
21
|
+
*
|
|
22
|
+
* Scoping (opt-in): when `opts.scope` is set the skin is compiled in scoped
|
|
23
|
+
* mode — every selector RULE gets `[data-wd-scope="<id>"]` appended to the
|
|
24
|
+
* subject of each compound selector, so the stylesheet only ever matches inside
|
|
25
|
+
* the stamped subtree. Design tokens (`tokens` / `tokens dark` / `tokens [attr]`)
|
|
26
|
+
* stay GLOBAL on `:root` regardless, so `var(--x)` and dark mode keep working
|
|
27
|
+
* site-wide. A whole-selector `:global(.x)` escapes scoping. Page-level selectors
|
|
28
|
+
* (`page`/`*`/`html`/`body`/`::selection`) are a compile error in scoped mode.
|
|
29
|
+
* Without `opts.scope`, output is byte-identical to before this feature existed.
|
|
30
|
+
* @param {string} source
|
|
31
|
+
* @param {{ scope?: string, subjects?: Set<string> }} [opts] `scope`: the scope
|
|
32
|
+
* id enabling scoped mode. `subjects`: an optional set the compiler fills with
|
|
33
|
+
* every scoped subject token (`.card`, `h2`, `#id`) for the unused-selector
|
|
34
|
+
* warning, so the caller need not re-parse the source.
|
|
35
|
+
* @returns {string}
|
|
36
|
+
*/
|
|
37
|
+
export function compileSkin(source: string, opts?: {
|
|
38
|
+
scope?: string;
|
|
39
|
+
subjects?: Set<string>;
|
|
40
|
+
}): string;
|
|
41
|
+
/**
|
|
42
|
+
* A nesting frame in the indentation stack.
|
|
43
|
+
*/
|
|
44
|
+
export type SkinFrame = {
|
|
45
|
+
indent: number;
|
|
46
|
+
selector: string | null;
|
|
47
|
+
media?: string | undefined;
|
|
48
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serve a built file for a request URL out of `distRoot`, or a 404 page.
|
|
3
|
+
* @param {string} distRoot Absolute path to the build output directory.
|
|
4
|
+
* @param {string} url Request URL (may include a query string).
|
|
5
|
+
* @param {http.ServerResponse} res Response to write to.
|
|
6
|
+
* @returns {void}
|
|
7
|
+
*/
|
|
8
|
+
export function serve(distRoot: string, url: string, res: http.ServerResponse): void;
|
|
9
|
+
/**
|
|
10
|
+
* Resolve a request URL to an absolute file inside `distRoot`, guarding against
|
|
11
|
+
* path traversal. Returns null when the resolved path escapes the root.
|
|
12
|
+
* @param {string} distRoot Absolute path to the build output directory.
|
|
13
|
+
* @param {string} url Request URL (may include a query string).
|
|
14
|
+
* @returns {string | null}
|
|
15
|
+
*/
|
|
16
|
+
export function resolvePublicFile(distRoot: string, url: string): string | null;
|
|
17
|
+
/**
|
|
18
|
+
* Map a file path to a response content-type by extension, defaulting to
|
|
19
|
+
* `application/octet-stream` for unknown types.
|
|
20
|
+
* @param {string} file File path or name.
|
|
21
|
+
* @returns {string}
|
|
22
|
+
*/
|
|
23
|
+
export function contentType(file: string): string;
|
|
24
|
+
import http from "node:http";
|
package/types/statics.d.ts
CHANGED
|
@@ -15,7 +15,8 @@ export function serve(distRoot: string, url: string, res: http.ServerResponse):
|
|
|
15
15
|
*/
|
|
16
16
|
export function resolvePublicFile(distRoot: string, url: string): string | null;
|
|
17
17
|
/**
|
|
18
|
-
* Map a file path to a response content-type, defaulting to
|
|
18
|
+
* Map a file path to a response content-type by extension, defaulting to
|
|
19
|
+
* `application/octet-stream` for unknown types.
|
|
19
20
|
* @param {string} file File path or name.
|
|
20
21
|
* @returns {string}
|
|
21
22
|
*/
|