create-volt 0.23.0 → 0.25.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 CHANGED
@@ -4,6 +4,26 @@ All notable changes to `create-volt` are documented here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/), and this project adheres to
5
5
  [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [0.25.0] - 2026-06-29
8
+
9
+ ### Added
10
+ - **Per-page SEO on the `pages` add-on** — front-matter now drives the page head:
11
+ `description` (meta description + og:description), `image` (og:image), `type`
12
+ (og:type), `canonical`, and **`jsonld`** (a one-line JSON string rendered into a
13
+ validated `<script type="application/ld+json">` block, with `<` escaped to
14
+ prevent breakout). Open Graph + Twitter + JSON-LD per page — the Yoast-style SEO
15
+ a migrated WordPress site expects. `volt-addon-editor` 0.4.0 adds a SEO panel to
16
+ set these from the editor.
17
+
18
+ ## [0.24.0] - 2026-06-29
19
+
20
+ ### Added
21
+ - The `pages` add-on now supports **`format: html`** in front-matter — those
22
+ pages are served **verbatim** (no markdown processing), so rich/complex layouts
23
+ (e.g. from the WYSIWYG editor) are preserved losslessly. Plain markdown pages
24
+ are unchanged. (`volt-addon-editor` 0.2.0 now stores `getHTML()` with
25
+ `format: html` so editor layouts round-trip exactly.)
26
+
7
27
  ## [0.23.0] - 2026-06-29
8
28
 
9
29
  ### Added
@@ -334,6 +354,8 @@ All notable changes to `create-volt` are documented here. The format follows
334
354
  watching and full-page hot reload. Supports `--skip-install` and `--force`,
335
355
  and auto-detects npm / pnpm / yarn / bun for the install step.
336
356
 
357
+ [0.25.0]: https://github.com/MIR-2025/volt/releases/tag/v0.25.0
358
+ [0.24.0]: https://github.com/MIR-2025/volt/releases/tag/v0.24.0
337
359
  [0.23.0]: https://github.com/MIR-2025/volt/releases/tag/v0.23.0
338
360
  [0.22.0]: https://github.com/MIR-2025/volt/releases/tag/v0.22.0
339
361
  [0.21.0]: https://github.com/MIR-2025/volt/releases/tag/v0.21.0
@@ -50,10 +50,39 @@ export function parseFrontMatter(src) {
50
50
  // slugs are restricted to a safe charset — no dots/slashes → no path traversal
51
51
  export const isSafeSlug = (s) => /^[a-z0-9][a-z0-9-]*$/i.test(s);
52
52
 
53
- function shell(title, inner) {
53
+ // SEO/social head from front-matter: description, image, type, canonical, jsonld.
54
+ function metaHead(meta) {
55
+ const t = [];
56
+ const title = meta.title || "";
57
+ const desc = meta.description || "";
58
+ if (title) t.push(`<meta property="og:title" content="${esc(title)}" />`);
59
+ if (desc) {
60
+ t.push(`<meta name="description" content="${esc(desc)}" />`);
61
+ t.push(`<meta property="og:description" content="${esc(desc)}" />`);
62
+ }
63
+ t.push(`<meta property="og:type" content="${esc(meta.type || "website")}" />`);
64
+ if (meta.image) t.push(`<meta property="og:image" content="${esc(meta.image)}" />`);
65
+ if (meta.url || meta.canonical) t.push(`<meta property="og:url" content="${esc(meta.url || meta.canonical)}" />`);
66
+ if (meta.canonical) t.push(`<link rel="canonical" href="${esc(meta.canonical)}" />`);
67
+ t.push(`<meta name="twitter:card" content="${meta.image ? "summary_large_image" : "summary"}" />`);
68
+ if (meta.jsonld) {
69
+ let ok = false;
70
+ try {
71
+ JSON.parse(meta.jsonld);
72
+ ok = true;
73
+ } catch {
74
+ /* invalid JSON-LD → skip */
75
+ }
76
+ if (ok) t.push(`<script type="application/ld+json">${meta.jsonld.replace(/</g, "\\u003c")}</script>`);
77
+ }
78
+ return t.join("\n");
79
+ }
80
+
81
+ function shell(meta, inner) {
54
82
  return `<!doctype html><html lang="en"><head><meta charset="utf-8" />
55
83
  <meta name="viewport" content="width=device-width, initial-scale=1" />
56
- <title>${esc(title)}</title>
84
+ <title>${esc(meta.title || "")}</title>
85
+ ${metaHead(meta)}
57
86
  <style>
58
87
  :root { color-scheme: light dark }
59
88
  body { max-width: 720px; margin: 2.5rem auto; padding: 0 1.1rem; font: 17px/1.7 system-ui, -apple-system, sans-serif; }
@@ -78,7 +107,10 @@ export async function pagesRouter({ dir }) {
78
107
  const file = path.join(dir, slug + ".md");
79
108
  if (!fs.existsSync(file)) return next();
80
109
  const { meta, body } = parseFrontMatter(fs.readFileSync(file, "utf8"));
81
- res.type("html").send(shell(meta.title || slug, marked.parse(body)));
110
+ // `format: html` pages (e.g. from the WYSIWYG editor) are served verbatim to
111
+ // preserve complex layouts; everything else is markdown rendered with marked.
112
+ const inner = meta.format === "html" ? body : marked.parse(body);
113
+ res.type("html").send(shell({ ...meta, title: meta.title || slug }, inner));
82
114
  });
83
115
  return r;
84
116
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-volt",
3
- "version": "0.23.0",
3
+ "version": "0.25.0",
4
4
  "description": "Scaffold a new Volt app — no-build, signals-based UI with Socket.io hot reload.",
5
5
  "type": "module",
6
6
  "bin": {