@zvndev/darkmown 2.3.1-next.5.g6f7d03c → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/README.md +1 -1
- package/docs/cli.md +2 -0
- package/docs/spec-alignment.md +2 -0
- package/package.json +1 -1
- package/site/pages/docs/index.wd +1 -1
- package/src/scaffold.js +34 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to Darkmown are documented here. Versions follow [semver](https://semver.org); pre-1.0 minor versions may contain breaking changes.
|
|
4
4
|
|
|
5
|
+
## 2.4.0 (2026-08-09)
|
|
6
|
+
|
|
7
|
+
A discoverability release, in both senses. Search engines can now index the site correctly, and coding agents working in a Darkmown project finally get the directive reference where they will actually read it. No runtime change (7654 B gzipped, unchanged) and no breaking changes.
|
|
8
|
+
|
|
9
|
+
- **`darkmown init` now writes agent context into the project.** Darkmown has always shipped a complete build-with-Darkmown guide, but only at the *package* root, so an installed copy landed in `node_modules/@zvndev/darkmown/AGENTS.md` where no coding agent reads it. Every scaffolded project therefore began each session with the agent guessing at directive syntax. `init` now writes `AGENTS.md` at the project root (copied byte-for-byte from the package, so a scaffolded project and the framework can never teach different syntax), a short `CLAUDE.md` pointing at it, and a `.gitignore` covering `node_modules/` and `dist/`. All three respect the existing never-overwrite rule. This matters because Darkmown's directive vocabulary is small and closed: an agent that has it in context writes valid `.wd` on the first attempt, and an agent that does not reaches for JSX and JavaScript idioms the compiler rejects.
|
|
10
|
+
- **Canonical URLs and `og:url`.** Neither existed. Every page with a `site_url` now emits `<link rel="canonical">` and `og:url`, both derived from the same route string the sitemap is built from, so the three can never disagree. A paginated page canonicalizes to itself, never to page one. No `site_url` means no canonical tag, never an invented origin. Two related bugs fell out: `og:title` was gated on `description || image`, so a titled page with neither shared as a bare URL, and `og:type` was hardcoded to `website`, so every blog post claimed to be one.
|
|
11
|
+
- **Opt-in JSON-LD structured data.** A new `schema:` frontmatter key emits `Article`, `BlogPosting`, `TechArticle`, `WebSite`, or `Organization`, plus an automatic `BreadcrumbList` built only from routes that actually exist. Driven by frontmatter rather than a directive, so a page with `html: false` can describe itself without a line of raw HTML. There is deliberately **no code path** to `aggregateRating`, `review`, or `offers`: fabricated ratings are a manual-action risk and the framework cannot know them. A key you did not write produces no property at all, never a blank one and never a guess. Verified to emit no CSP violation under `script-src 'self'` with no `unsafe-inline`.
|
|
12
|
+
- **`llms-full.txt`.** `llms.txt` becomes a true index (cheatsheet, one line per page, a pointer onward) and `llms-full.txt` carries the corpus: the full directive reference, every error code with its cause and fix, and the complete source of every page. Both come from one generator, so neither can drift from the compiler.
|
|
13
|
+
- **Explicit AI-crawler rules in `robots.txt`.** Fifteen user-agent tokens across OpenAI, Anthropic, Google, Apple, Perplexity and others, each verified against the operator's own published documentation, with the docs URL and a purpose comment written into the generated file. The distinctions are recorded rather than flattened: `OAI-SearchBot` governs ChatGPT Search crawling and citations while `GPTBot` governs training access, and `Google-Extended` is a control token that never affects Search ranking. A new `ai_crawlers: deny` config flips them all, and a typo fails the build (`WD907`) instead of failing open.
|
|
14
|
+
- **BUGFIX: `darkmown deploy vercel` no longer makes every internal link redirect.** The generated Vercel config set `trailingSlash: false` while Darkmown's route model is trailing-slashed end to end (`routeFromFile`, `routes.json`, `_headers`, the sitemap, the dev server, and the `dist/docs/index.html` layout the build writes). The result was a `308` on every single internal navigation. Now `true`, so the deployed config agrees with the routes the framework actually emits. Note that this also applies to extensionless `/api/*` paths: point `:fetch` and `:form action=` at the slashed form to avoid a redirect hop. The redirect is a `308`, so a form POST still arrives intact either way, and this is documented in the backends section.
|
|
15
|
+
|
|
5
16
|
## 2.3.0 — 2026-08-09
|
|
6
17
|
|
|
7
18
|
A trust release. Every compile error now carries a stable, searchable code; the dependency tree carries zero known advisories; and CI proves the framework on three operating systems and three browser engines instead of one of each. The new coverage found real bugs, including one that had silently broken every Windows user since the project started. No runtime change (7654 B gzipped, unchanged) and no breaking changes.
|
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ npm run dev # live demo site — the same site that runs darkmown.com
|
|
|
58
58
|
|
|
59
59
|
## Commands
|
|
60
60
|
|
|
61
|
-
- `darkmown init [dir] [--template <name>]` scaffolds a new site from a template (`starter`, `blog`, `store`, `dashboard`, `landing`).
|
|
61
|
+
- `darkmown init [dir] [--template <name>]` scaffolds a new site from a template (`starter`, `blog`, `store`, `dashboard`, `landing`). It also drops `AGENTS.md` (the full directive reference), a `CLAUDE.md` pointing at it, and a `.gitignore` into the project root, so a coding agent working in your new project has the directive vocabulary in context from the first prompt.
|
|
62
62
|
- `darkmown dev` starts the live compiler with browser reload, an in-browser error overlay, and a local runner for `api/*` functions. Rebuilds are incremental: each dev build records a per-route dependency map (page file, includes, colocated assets, loop data, collections), so editing one file recompiles only the routes that depend on it — with a full rebuild on any uncertainty (a new/deleted/renamed file, or a framework `src/` change). Rebuilds run one at a time; edits landing mid-build batch into the next one.
|
|
63
63
|
- `darkmown build [--target cloudflare] [--drafts]` writes static output to `dist` (plus `sitemap.xml`/`rss.xml`/`robots.txt`); `--drafts` includes `draft: true` pages for staging.
|
|
64
64
|
- `darkmown deploy <vercel|cloudflare> [--prod]` builds and deploys via the platform CLI.
|
package/docs/cli.md
CHANGED
|
@@ -34,6 +34,8 @@ Scaffolds a Darkmown site from a template (default: `starter`). Available templa
|
|
|
34
34
|
darkmown init my-site --template store
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
Every scaffold also writes agent context at the project root: `AGENTS.md` (the full directive reference, copied byte-for-byte from the installed package so it can never teach different syntax than the compiler accepts), a short `CLAUDE.md` pointing at it, and a `.gitignore` covering `node_modules/` and `dist/`. Coding agents read instruction files from the project root, so without this a new project starts every session with the agent guessing at directive syntax.
|
|
38
|
+
|
|
37
39
|
Existing files are never overwritten. The generated `package.json` is private, sets `"type": "module"` (so `api/*.js` import as ESM), includes `dev`/`build`/`preview` scripts, and names the app after the target directory.
|
|
38
40
|
|
|
39
41
|
After scaffolding:
|
package/docs/spec-alignment.md
CHANGED
|
@@ -21,6 +21,8 @@ The 1.3.0 cycle shipped the crawler *files* (sitemap, rss, robots). This closes
|
|
|
21
21
|
- **Zero known advisories, and one fewer runtime dependency.** `image-size` carried a high-severity advisory affecting every published version with no upgrade path, so its single build-time call site is now served by an inlined header reader (PNG/JPEG/GIF/WebP/SVG) with explicitly bounded parse loops. This keeps the project's "few runtime dependencies" posture honest rather than deferring to an unfixable upstream.
|
|
22
22
|
- **BUGFIX — CRLF sources lost their frontmatter.** `parseFrontmatter` tested an LF-shaped `---\n` opener, so any file authored on Windows or checked out with `core.autocrlf` had its whole frontmatter block silently reclassified as body text: no error, but the page lost its title, layout, `draft` flag, and collection schema fields. Newline normalization now happens at the reader boundary and in `parseFrontmatter`. This had been latent since the first commit; it surfaced within minutes of adding a Windows CI job.
|
|
23
23
|
- **Platform coverage is now part of the contract.** CI runs the suite on Linux, Windows, and macOS, and the browser e2e suite against Chromium, Firefox, and WebKit (sharded, so three engines cost roughly one engine's wall-clock). The reactive runtime had only ever been proven in one engine; the CLI's path handling had never executed on Windows at all.
|
|
24
|
+
- **A scaffolded project now carries its own agent context.** The package has always shipped a full build-with-Darkmown guide, but only at the package root, so a consumer's copy landed in `node_modules/@zvndev/darkmown/AGENTS.md` where no coding agent reads it. `darkmown init` now copies that same file to the project root (byte-identical, so a scaffolded project and this repo can never teach different syntax), plus a short `CLAUDE.md` pointing at it and a `.gitignore`. This matters because the directive vocabulary is small and closed: an agent that has it in context writes valid `.wd` on the first try, and an agent that does not reaches for JSX and JavaScript idioms the compiler rejects. A pack test keeps `AGENTS.md` in the tarball, since dropping it would make `init` silently scaffold a project with no agent context and no error.
|
|
25
|
+
|
|
24
26
|
- **Types resolve for modern TypeScript consumers.** The `exports` map lacked `types` conditions, so `moduleResolution: node16`/`nodenext`/`bundler` consumers got no types from either entrypoint. Fixed, `types/catalog.d.ts` authored, and the declaration build itself repaired (it had been failing silently under TypeScript 6, leaving the committed `.d.ts` stale).
|
|
25
27
|
- **Property tests extended to the skin compiler and the router,** in the existing seeded/reproducible style. They found a real defect on the first run: `.skin` token modifiers quoted the attribute value but never validated the attribute *name*, letting `tokens [{=v]` emit an unbalanced `:root[{="v"]` selector that a CSS parser would use to swallow every following rule.
|
|
26
28
|
- **Release channels:** merges to `master` publish a prerelease to the `next` dist-tag; the tag-triggered stable path is unchanged and the canary job verifies after publishing that it did not take `latest`.
|
package/package.json
CHANGED
package/site/pages/docs/index.wd
CHANGED
|
@@ -694,7 +694,7 @@ A shipped code is a public contract: never renumbered, never reused. The full re
|
|
|
694
694
|
|
|
695
695
|
The `darkmown` command (installed by `npx @zvndev/darkmown`) is the whole tool — every command is one word:
|
|
696
696
|
|
|
697
|
-
- `darkmown init [dir] [--template <name>]`
|
|
697
|
+
- `darkmown init [dir] [--template <name>]` scaffolds a new site from a template (`starter`, `blog`, `store`, `dashboard`, `landing`). It also writes agent context at the project root: `AGENTS.md` (the full directive reference, copied from the installed package so it can never teach syntax the compiler rejects), a `CLAUDE.md` pointing at it, and a `.gitignore`. Existing files are never overwritten.
|
|
698
698
|
- `darkmown dev` — the live compiler: browser reload, an in-browser error overlay, incremental rebuilds, and a local runner for `api/*` functions.
|
|
699
699
|
- `darkmown build [--target cloudflare] [--drafts]` — write 100% static output to `dist/` (plus `sitemap.xml` / `rss.xml` / `robots.txt`); `--drafts` includes `draft: true` pages for a staging deploy.
|
|
700
700
|
- `darkmown serve` — preview the built `dist/` locally (static only; use `dev` to exercise `api/*`).
|
package/src/scaffold.js
CHANGED
|
@@ -27,7 +27,9 @@ export function availableTemplates() {
|
|
|
27
27
|
* Scaffold a new Darkmown project into `root` from a template, creating files
|
|
28
28
|
* that don't already exist (never overwriting). `package.json` is generated (so
|
|
29
29
|
* the name tracks the directory and the `@zvndev/darkmown` dep pins the installed
|
|
30
|
-
* version);
|
|
30
|
+
* version); `AGENTS.md` is copied from the package so coding agents find the
|
|
31
|
+
* directive reference at the project root; `CLAUDE.md` and `.gitignore` are
|
|
32
|
+
* generated; every other file is copied verbatim from the chosen template.
|
|
31
33
|
* @param {string} root Absolute path to the target project directory.
|
|
32
34
|
* @param {{ template?: string }} [options] `template` defaults to `"starter"`.
|
|
33
35
|
* @returns {{ root: string, template: string }}
|
|
@@ -71,6 +73,37 @@ export function initProject(root, options = {}) {
|
|
|
71
73
|
)}\n`
|
|
72
74
|
);
|
|
73
75
|
|
|
76
|
+
// Agent context. The package already carries a full build-with-Darkmown
|
|
77
|
+
// guide, but at the PACKAGE root — so a consumer's copy lands in
|
|
78
|
+
// node_modules/@zvndev/darkmown/AGENTS.md, where no coding agent looks. Agents
|
|
79
|
+
// read instruction files from the PROJECT root, so the guide is copied there
|
|
80
|
+
// and the scaffolded project starts with the directive vocabulary in context
|
|
81
|
+
// instead of the agent guessing at it.
|
|
82
|
+
//
|
|
83
|
+
// Copied rather than generated: it is one maintained file, so a scaffolded
|
|
84
|
+
// project and this repo can never teach different syntax.
|
|
85
|
+
copyNew(path.join(moduleDir, "..", "AGENTS.md"), path.join(root, "AGENTS.md"));
|
|
86
|
+
|
|
87
|
+
// Claude Code reads CLAUDE.md; most other agents read AGENTS.md. A pointer,
|
|
88
|
+
// not a second copy, so there is exactly one file to keep true.
|
|
89
|
+
writeNew(
|
|
90
|
+
root,
|
|
91
|
+
"CLAUDE.md",
|
|
92
|
+
"# Project instructions\n\n" +
|
|
93
|
+
"This project is built with [Darkmown](https://darkmown.com). " +
|
|
94
|
+
"The directive reference, project layout, and build rules are in " +
|
|
95
|
+
"[AGENTS.md](./AGENTS.md). Read it before writing or editing any " +
|
|
96
|
+
"`.md`/`.wd` file.\n\n" +
|
|
97
|
+
"Refresh the directive reference at any time with " +
|
|
98
|
+
"`npx darkmown catalog --llms` (cheatsheet) or " +
|
|
99
|
+
"`npx darkmown catalog --llms-full` (full reference). Both are generated " +
|
|
100
|
+
"from the compiler's own tables, so they cannot drift from what compiles.\n"
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
// Without this a first `git init` stages the build output and the whole
|
|
104
|
+
// dependency tree, which is the first thing anyone does after `init`.
|
|
105
|
+
writeNew(root, ".gitignore", "node_modules/\ndist/\n.DS_Store\n");
|
|
106
|
+
|
|
74
107
|
for (const rel of walk(templateDir)) {
|
|
75
108
|
copyNew(path.join(templateDir, rel), path.join(root, rel));
|
|
76
109
|
}
|