@zeropress/build-core 0.6.12 → 0.7.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/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  ![license](https://img.shields.io/npm/l/%40zeropress%2Fbuild-core)
5
5
  ![node](https://img.shields.io/node/v/%40zeropress%2Fbuild-core)
6
6
 
7
- Shared deterministic rendering core for ZeroPress static output v0.6.
7
+ Shared deterministic rendering core for Preview Data v0.7 and Theme Runtime v0.7.
8
8
 
9
9
  This package is the canonical rendering core for preview-data and theme packages consumed directly by:
10
10
 
@@ -13,10 +13,10 @@ This package is the canonical rendering core for preview-data and theme packages
13
13
 
14
14
  Public contract references:
15
15
 
16
- - [Preview Data v0.6 Spec](https://zeropress.dev/spec/preview-data-v0.6.html)
17
- - [Preview Data v0.6 Schema](https://schemas.zeropress.dev/preview-data/v0.6/schema.json)
18
- - [Theme Runtime v0.6 Spec](https://zeropress.dev/spec/theme-runtime-v0.6.html)
19
- - [Theme Runtime v0.6 Schema](https://schemas.zeropress.dev/theme-runtime/v0.6/schema.json)
16
+ - [Preview Data v0.7 Spec](https://zeropress.dev/reference/preview-data/specs/v0.7/)
17
+ - [Preview Data v0.7 Schema](https://schemas.zeropress.dev/preview-data/v0.7/schema.json)
18
+ - [Theme Runtime v0.7 Spec](https://zeropress.dev/reference/theme-runtime/specs/v0.7/)
19
+ - [Theme Runtime v0.7 Schema](https://schemas.zeropress.dev/theme-runtime/v0.7/schema.json)
20
20
 
21
21
  It accepts canonical preview-data plus a validated theme package and produces static HTML artifacts through a writer interface.
22
22
 
@@ -83,9 +83,6 @@ const result = await buildSite({
83
83
  previewData,
84
84
  themePackage,
85
85
  writer,
86
- options: {
87
- writeManifest: true,
88
- },
89
86
  });
90
87
  ```
91
88
 
@@ -100,18 +97,7 @@ Returns:
100
97
  size: 1234,
101
98
  sha256: '...'
102
99
  }
103
- ],
104
- manifest: {
105
- generatedAt: '2026-04-02T00:00:00Z',
106
- files: [
107
- {
108
- path: 'index.html',
109
- contentType: 'text/html',
110
- size: 1234,
111
- sha256: '...'
112
- }
113
- ]
114
- }
100
+ ]
115
101
  }
116
102
  ```
117
103
 
@@ -121,11 +107,13 @@ Notes:
121
107
  - `previewData` must already satisfy the canonical preview-data contract
122
108
  - `themePackage` must already be a validated in-memory theme package
123
109
  - `sitemap.xml` is emitted only when `site.url` is a non-empty canonical URL
124
- - `feed.xml` is emitted only when `site.url` is a non-empty canonical URL and `generateFeed` is not `false`
110
+ - `feed.xml` is emitted only when `site.feed.enabled` is not `false`, `site.url` is a non-empty canonical URL, and `generateFeed` is not `false`
125
111
  - callers may pass `sitemapStylesheetHref` to add an XML stylesheet processing instruction to generated `sitemap.xml`
126
- - fallback `robots.txt` is emitted when `generateSpecialFiles` is enabled and `generateRobotsTxt` is not `false`
127
- - fallback `robots.txt` uses `site.indexing`; `false` emits `Disallow: /`, while missing or `true` emits `Allow: /`
112
+ - fallback `robots.txt` is emitted when `generateRobotsTxt` is not `false`
113
+ - fallback `robots.txt` uses the effective `site.robots.allow_indexing` policy; omitted Preview Data defaults to `true`, `false` emits `Disallow: /`, and `true` emits `Allow: /`
128
114
  - callers that disable fallback robots because a public `robots.txt` exists should copy that file as-is; sitemap directives in custom robots files are caller/user responsibility
115
+ - dotted slug segments such as `v0.6` remain literal in route URLs, canonical URLs, and output filenames
116
+ - output planning rejects duplicate public URLs, including clean-host aliases (`page.html` → `/page` and `page/index.html` → `/page/`), content routes shadowed by generated special files, and file/directory path hierarchy conflicts before writing
129
117
 
130
118
  ### `buildSiteFromThemeDir(input)`
131
119
 
@@ -179,7 +167,7 @@ Theme validation is enforced through:
179
167
 
180
168
  JavaScript theme assets are emitted as provided. Build-core may hash output filenames, but it does not rewrite or minify JavaScript content.
181
169
 
182
- In `preview-data v0.6`, the payload does not carry `routes` arrays or raw HTML fragments such as `categories_html`.
170
+ User-defined scalar metadata under `site.meta`, `post.meta`, `page.meta`, and item `meta` is always HTML-escaped during template interpolation. Names ending in `_html` or `_url` do not opt metadata into raw rendering; use the explicit content and custom HTML contracts for trusted raw markup.
183
171
 
184
172
  Build-core derives:
185
173
 
@@ -187,31 +175,138 @@ Build-core derives:
187
175
  - post list HTML blocks
188
176
  - pagination HTML
189
177
  - taxonomy link HTML
190
- - formatted `published_at` / `updated_at`
178
+ - localized fallback `published_at` / `updated_at` values plus unconditional `published_at_iso` / `updated_at_iso` values
191
179
  - datetime formatting from `site.locale`, `site.timezone`, `site.date_style`, and `site.time_style`
192
180
  - `reading_time`
193
- - `comments_enabled`
181
+ - a route-root `comments` discriminated context
182
+
183
+ Every ordinary rendered route receives effective feature state on `site`, regardless of whether the corresponding Preview Data preference was omitted:
184
+
185
+ ```js
186
+ {
187
+ search: { enabled: true },
188
+ feed: { enabled: true, url: '/feed.xml' },
189
+ archive: { enabled: true, url: '/archive/' },
190
+ comments: {
191
+ enabled: true,
192
+ provider: 'zeropress',
193
+ api_base_url: 'https://comments.example.com',
194
+ per_page: 50,
195
+ order: 'desc',
196
+ threading: { enabled: true, max_depth: 2 }
197
+ }
198
+ }
199
+ ```
200
+
201
+ Disabled states are exactly `{ enabled: false }`. Search combines the Preview Data request with `theme.json.features.search`. Feed additionally requires a canonical `site.url` and is hard-disabled by `generateFeed: false`. Archive additionally requires `archive.html`; its URL is `/archive/` for directory output and `/archive` for html-extension output. Comments additionally require `theme.json.features.comments`. `site.post_index.enabled` and `site.post_index.paginate` are also effective values after applying `theme.json.features.post_index` and route generation.
202
+
203
+ Build Core always emits localized fallback datetime strings together with canonical ISO timestamps. Themes may progressively enhance explicitly marked `<time datetime="...">` elements for the visitor's browser locale and timezone, but must preserve the fallback when JavaScript, `Intl`, or ISO parsing is unavailable. Themes that want canonical site-local display should use the fallback without client enhancement.
204
+
205
+ When `site.comments` is present, Build Core materializes the following defaults before rendering:
206
+
207
+ - `provider: "zeropress"`
208
+ - `per_page: 50`
209
+ - `order: "desc"`
210
+ - `threading.enabled: true`
211
+ - `threading.max_depth: 2`
212
+
213
+ Trailing slashes are removed from `site.comments.api_base_url`, except when the value is the same-origin root `/`. The normalized configuration remains available as `site.comments`.
194
214
 
195
- Comment availability is derived from preview-data policy:
215
+ Every rendered route receives a top-level `comments` object. Inactive and non-detail routes receive exactly:
196
216
 
197
- - `site.disallow_comments`
198
- - `content.posts[].allow_comments`
217
+ ```js
218
+ {
219
+ comments: {
220
+ enabled: false
221
+ }
222
+ }
223
+ ```
224
+
225
+ An active post or page detail route receives:
226
+
227
+ ```js
228
+ {
229
+ comments: {
230
+ enabled: true,
231
+ target_type: 'post', // or 'page'
232
+ target_public_id: 101,
233
+ provider: 'zeropress', // or 'wordpress'
234
+ api_base_url: 'https://comments.example.com',
235
+ per_page: 50,
236
+ order: 'desc',
237
+ threading: {
238
+ enabled: true,
239
+ max_depth: 2
240
+ },
241
+ request_token: '...'
242
+ }
243
+ }
244
+ ```
199
245
 
200
- The canonical `preview-data v0.6` site contract uses:
246
+ The active state requires all of the following:
201
247
 
202
- - `site.media_base_url`
248
+ - the theme declares `features.comments: true`
249
+ - `site.comments` is configured with `enabled: true`
250
+ - the post or page has `allow_comments: true` and a positive `public_id`
251
+ - the ZeroPress provider has a non-empty item `comments.request_token`
252
+
253
+ The theme-facing `request_token` key exists only for the `zeropress` provider. Preview-data items may retain ignored token metadata for `wordpress` or inactive comment states, but Build Core drops it. An active `wordpress` context omits that key entirely. Post/page objects, structured list items, collection items and cursors, adjacent-item summaries, search data, feeds, and non-detail route roots do not receive a copy of the item token. Themes must use the route-root `comments.enabled` discriminator.
254
+
255
+ The canonical `preview-data v0.7` site contract uses:
256
+
257
+ - `site.media_origin`
203
258
  - `site.media_delivery_mode`
204
259
  - `site.locale`
205
260
  - `site.timezone`
206
- - `site.datetime_display`
207
261
  - `site.date_style`
208
262
  - `site.time_style`
209
- - `site.disallow_comments`
210
- - `site.indexing`
263
+ - `site.feed`
264
+ - `site.archive`
265
+ - `site.comments`
266
+ - `site.robots`
211
267
  - `site.expose_generator`
212
268
  - `site.search`
213
269
 
214
- Native search artifacts are emitted only when preview-data does not set `site.search: false` and the active theme declares `features.search: true`. `search_pagefind.js` is a Pagefind adapter that can replace `search.js` after a post-build Pagefind step.
270
+ Post objects use `public_id` as their public numeric identity. The removed internal `content.posts[].id` field is neither required nor exposed to the theme runtime.
271
+
272
+ `site.url` is either an empty string or a credential-free HTTP(S) origin. A trailing root slash is accepted and normalized to `URL.origin`; credentials, paths, queries, and fragments are invalid. Menu and newsletter navigation URLs must be safe single-slash root-relative paths or credential-free HTTP(S) URLs. Query and fragment are allowed, while bare/dot-relative paths, protocol-relative URLs, path dot segments, backslashes, whitespace/control characters, and malformed percent escapes are invalid.
273
+
274
+ `site.media_origin` is either an empty string or an absolute HTTP(S) origin. Media fields accept only safe single-slash root-relative paths with a real path or credential-free HTTP(S) URLs with a path; `/`, bare paths, and dot-relative paths are invalid. Build Core resolves root-relative site favicon/logo, profile-widget avatar, author avatar, Post/Page featured-image, and `content.media[].src` values against that origin, while preserving already-absolute external URLs. SEO image metadata receives the normalized featured image. Responsive `srcset` variants require `media_delivery_mode: "media_domain"`, a non-empty origin, and an exact origin match; paths, credentials, query strings, and fragments are not part of the media-origin contract.
275
+
276
+ Locale and time zone are canonicalized once at build start. Locale uses canonical BCP 47. Time zone accepts `UTC`, canonical IANA identifiers, or canonical fixed offsets within `±14:00`; zero offset becomes `UTC`. The same values drive fallback date strings, archives, date-based permalinks, feeds, and rendered metadata.
277
+
278
+ When routing fields are omitted, Build Core uses directory output, `/posts/:slug/`, `/:slug/`, `/categories/:slug/`, and `/tags/:slug/`; `front_page` defaults to `theme_index`; and `post_index` defaults to `{ enabled: true, path: "/", paginate: true }`. Page objects retain a leaf `slug`, but external identity uses the NFC-normalized effective route path. Page front-page references use `page_path`, Page collection references use `path`, and different Page paths may share the same leaf slug.
279
+
280
+ `site.favicon.icon`, `svg`, and `png` form the default favicon set. `icon_dark` is the optional dark color-scheme icon. When both sets exist, Build Core emits the dark icon first with `media="(prefers-color-scheme: dark)"` and marks every default icon link with the light media query. A single available set is emitted without a media condition, and `apple_touch_icon` is always unconditional. An explicit preview-data `site.favicon` object replaces the complete auto-discovered build option instead of merging field by field.
281
+
282
+ ### Custom HTML
283
+
284
+ Preview Data v0.7 carries optional trusted site-level HTML as flat raw strings:
285
+
286
+ ```json
287
+ {
288
+ "custom_html": {
289
+ "head_end": "<meta name=\"example\" content=\"value\">",
290
+ "body_end": "<script src=\"/assets/example.js\"></script>"
291
+ }
292
+ }
293
+ ```
294
+
295
+ Each configured slot must be nonblank and is limited to 65,536 Unicode code points. Build Core preserves the string without trimming, escaping, sanitizing, or interpreting it.
296
+
297
+ For ordinary theme-rendered HTML routes, site customization order is deterministic:
298
+
299
+ 1. favicon links
300
+ 2. ZeroPress generator metadata, unless disabled
301
+ 3. the generated custom CSS link
302
+ 4. `custom_html.head_end`
303
+ 5. `custom_html.body_end`
304
+
305
+ Closing `</head>` and `</body>` tags are matched case-insensitively. If a configured custom HTML slot has no matching closing tag, the build fails with the route, output path, missing tag, and remediation guidance instead of silently dropping the configured content. Themes that consume these slots must render the corresponding closing tag. A `site.front_page.type: "standalone_html"` document remains byte-for-byte independent and does not receive favicon, generator, custom CSS, or custom HTML injection.
306
+
307
+ Native search artifacts are emitted only when preview-data does not set `site.search.enabled: false` and the active theme declares `features.search: true`. When that effective search state is disabled, search widgets are omitted from resolved widget items while their widget areas and non-search siblings remain available. `search_pagefind.js` is a Pagefind adapter that can replace `search.js` after a post-build Pagefind step.
308
+
309
+ When archive is disabled or `archive.html` is missing, archive routes and archive widget items are omitted while authored menu items, widget areas, and non-archive siblings remain unchanged. No archive output path is claimed in that state, so content may use `/archive`. Feed behaves the same way for `feed.xml`: a disabled feed neither emits nor claims the file. Enabled feeds add one RSS autodiscovery link to `meta.head_tags` on ordinary theme-rendered routes; `404.html` and standalone front-page HTML do not receive it.
215
310
 
216
311
  Optional route templates behave as rendering capabilities, not guaranteed outputs:
217
312
 
@@ -220,7 +315,9 @@ Optional route templates behave as rendering capabilities, not guaranteed output
220
315
  - `tag.html`
221
316
  - `404.html`
222
317
 
223
- If preview-data includes content that could produce archive/category/tag pages but the theme omits the matching optional template, build-core skips those outputs. Special files are derived from emitted outputs rather than raw preview-data alone.
318
+ If preview-data includes content that could produce archive/category/tag pages but the theme omits the matching optional template, build-core skips those outputs. `404.html` is emitted only when the theme provides its matching template. Special files are derived from emitted outputs rather than raw preview-data alone.
319
+
320
+ Generated `404.html` uses the document title `Page Not Found - <site title>` and always includes `robots: noindex`. It omits description, canonical, Open Graph, and article metadata because a static error document is not a canonical or shareable content route. Build-core writes the artifact only; the development server or deployment host remains responsible for serving it with HTTP status `404` for missing URLs.
224
321
 
225
322
  ## Build Options
226
323
 
@@ -229,20 +326,17 @@ Supported options:
229
326
  - `assetHashing`
230
327
  - `favicon`
231
328
  - `sitemapStylesheetHref`
232
- - `generateSpecialFiles`
233
329
  - `generateFeed`
234
330
  - `generateRobotsTxt`
235
- - `writeManifest`
236
-
237
- These options apply to both full builds and partial renders where relevant.
331
+ - `reservedOutputPaths`
238
332
 
239
333
  Defaults:
240
334
 
241
335
  - `assetHashing: true`
242
- - `generateSpecialFiles: true`
243
336
  - `generateFeed: true`
244
337
  - `generateRobotsTxt: true`
245
- - `writeManifest: false`
338
+
339
+ `reservedOutputPaths` is an internal orchestration boundary for callers that already own files in the final output tree, such as a copied public directory. Reserved files are safety-validated and participate in clean-URL alias, exact-path, and file/directory hierarchy collision checks, but Build Core does not write them or include them in the returned file summaries.
246
340
 
247
341
  ## License
248
342
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeropress/build-core",
3
- "version": "0.6.12",
3
+ "version": "0.7.0",
4
4
  "description": "Shared ZeroPress build core",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -30,14 +30,14 @@
30
30
  "test": "node --test"
31
31
  },
32
32
  "engines": {
33
- "node": ">=18.18.0"
33
+ "node": ">=22.12.0"
34
34
  },
35
35
  "dependencies": {
36
- "@zeropress/slug-policy": "0.1.0",
37
- "@zeropress/preview-data-validator": "0.6.12",
38
- "@zeropress/theme-validator": "0.6.5",
36
+ "@zeropress/slug-policy": "0.2.1",
37
+ "@zeropress/preview-data-validator": "0.7.0",
38
+ "@zeropress/theme-validator": "0.7.0",
39
39
  "highlight.js": "11.11.1",
40
- "markdown-it": "14.2.0",
41
- "markdown-it-anchor": "9.2.0"
40
+ "markdown-it": "14.3.0",
41
+ "markdown-it-anchor": "9.2.1"
42
42
  }
43
43
  }