@uniweb/build 0.54.0 → 0.54.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniweb/build",
3
- "version": "0.54.0",
3
+ "version": "0.54.1",
4
4
  "description": "Build tooling for the Uniweb Component Web Platform",
5
5
  "type": "module",
6
6
  "exports": {
@@ -58,14 +58,14 @@
58
58
  "sharp": "^0.35.3",
59
59
  "yaml": "^2.5.0",
60
60
  "@uniweb/content-reader": "^1.2.5",
61
+ "@uniweb/content-writer": "^0.3.4",
62
+ "@uniweb/projections": "^0.7.3",
61
63
  "@uniweb/semantic-parser": "^1.4.1",
62
64
  "@uniweb/schemas": "^0.3.3",
63
- "@uniweb/content-writer": "^0.3.4",
64
- "@uniweb/theming": "^0.1.15",
65
- "@uniweb/projections": "^0.7.3"
65
+ "@uniweb/theming": "^0.1.15"
66
66
  },
67
67
  "optionalDependencies": {
68
- "@uniweb/runtime": "^0.26.5"
68
+ "@uniweb/runtime": "^0.26.6"
69
69
  },
70
70
  "peerDependencies": {
71
71
  "vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
@@ -74,8 +74,8 @@
74
74
  "@tailwindcss/vite": "^4.0.0",
75
75
  "@vitejs/plugin-react": "^4.0.0 || ^5.0.0",
76
76
  "vite-plugin-svgr": "^4.0.0",
77
- "@uniweb/runtime": "^0.26.5",
78
- "@uniweb/core": "^0.29.4"
77
+ "@uniweb/core": "^0.29.5",
78
+ "@uniweb/runtime": "^0.26.6"
79
79
  },
80
80
  "peerDependenciesMeta": {
81
81
  "vite": {
package/src/prerender.js CHANGED
@@ -1095,9 +1095,10 @@ export async function prerenderSite(siteDir, options = {}) {
1095
1095
  // ⭐ `renderer.render` IS the sequence this loop used to spell out —
1096
1096
  // renderPage → classify → injectPageContent. It was moved into
1097
1097
  // `@uniweb/runtime/ssr` because an SSR isolate assembles the same three steps
1098
- // per request, and two hand-written copies of one sequence is how the two
1099
- // lanes drift. This lane passes the Page it already holds; the isolate passes
1100
- // a route. See `runtime/src/page-renderer.js` for what it leaves to the host.
1098
+ // for the page a visit starts on, and two hand-written copies of one sequence
1099
+ // is how the two lanes drift. This lane passes the Page it already holds; the
1100
+ // isolate passes a route. See `runtime/src/page-renderer.js` for what it leaves
1101
+ // to the host.
1101
1102
  const result = renderer.render(page)
1102
1103
 
1103
1104
  if (result.outcome === 'failed') {
@@ -28,7 +28,8 @@ import { existsSync } from 'node:fs'
28
28
  import { join, resolve, basename } from 'node:path'
29
29
  import yaml from 'js-yaml'
30
30
  import { YAML_OPTIONS } from './utils/yaml-schema.js'
31
- import { queryNameFromUrl, siteReaches, declaredKeys, fillDeclaredKeys } from '@uniweb/core'
31
+ import { queryNameFromUrl, declaredKeys, fillDeclaredKeys, fetchLevels, pageRouteQuery } from '@uniweb/core'
32
+ import { parentRouteOf } from '@uniweb/core/route-match'
32
33
 
33
34
  import { validateItem, isStaticallyCheckable, validateBound } from '@uniweb/schemas/conform'
34
35
  import { validateAndNormalizeSchema } from './resolve-data-schema.js'
@@ -105,7 +106,26 @@ export async function validateDataInputs({ siteRoot, foundationPath }) {
105
106
  const deferred = []
106
107
 
107
108
  const byRoute = new Map((site.pages || []).map((p) => [p.route, p]))
109
+ // ⭐ The parent by the ONE parent rule, not the declared field alone: a payload may omit
110
+ // `parent`, and reading it raw gives a page no parent here while every other lane infers one —
111
+ // so the site's fetch reached pages it does not reach, and a parent's reached none.
112
+ const parentOf = (page) => {
113
+ if (!page?.route) return null
114
+ const route = parentRouteOf(page.route, { declared: page.parent ?? null, has: (r) => byRoute.has(r) })
115
+ return route ? byRoute.get(route) ?? null : null
116
+ }
117
+ const access = {
118
+ routeOf: (p) => p?.route,
119
+ parentOf,
120
+ fetchOf: (p) => p?.fetch ?? null,
121
+ sectionsOf: (p) => p?.sections,
122
+ site: config.fetch,
123
+ }
108
124
  for (const page of site.pages || []) {
125
+ const parent = parentOf(page)
126
+ // Its route query, when it is on a parametric route — a page nested inside one included,
127
+ // which is a level this check did not have.
128
+ const route = pageRouteQuery(page, access)
109
129
  walkSections(page.sections || [], (section) => {
110
130
  const type = section.type
111
131
  if (!type) return
@@ -115,10 +135,11 @@ export async function validateDataInputs({ siteRoot, foundationPath }) {
115
135
  // same function the entity store delivers by — so a `post: '@std/article'` key
116
136
  // filled by an `articles` fetch is checked against `@std/article`, and a fetch that
117
137
  // fills no declared key is not checked at all, since no component receives it. The
118
- // levels are the section's own fetch, its page's, its parent page's and — for a
119
- // top-level page — the site's (`siteReaches`).
138
+ // levels are the rule's, so this check sees what the render sees (`fetchLevels`,
139
+ // `@uniweb/core/page-data`) — ⛔ it wrote its own list until 2026-09-19, which read only
140
+ // the declared parent and had no route binding for a nested page.
120
141
  const declared = declaredKeys(bindings)
121
- const levels = [section.fetch, page.fetch, byRoute.get(page.parent)?.fetch, siteReaches(page.parent) ? config.fetch : null]
142
+ const levels = fetchLevels({ own: section.fetch, page: page.fetch, parent, route, site: config.fetch })
122
143
  const inputs = collectInputs(levels)
123
144
  const held = nodesOfType(section.content, 'dataBlock').map((node) => node.attrs?.tag).filter(Boolean)
124
145
  const fills = fillDeclaredKeys(declared, levels, { queries: config.queries, held })