@uniweb/build 0.9.3 → 0.9.5
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 +3 -3
- package/src/prerender.js +20 -16
- package/src/runtime-schema.js +73 -38
- package/src/site/data-fetcher.js +18 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/build",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.5",
|
|
4
4
|
"description": "Build tooling for the Uniweb Component Web Platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"@uniweb/theming": "0.1.3"
|
|
55
55
|
},
|
|
56
56
|
"optionalDependencies": {
|
|
57
|
-
"@uniweb/runtime": "0.7.
|
|
57
|
+
"@uniweb/runtime": "0.7.5",
|
|
58
58
|
"@uniweb/content-reader": "1.1.4",
|
|
59
59
|
"@uniweb/schemas": "0.2.1"
|
|
60
60
|
},
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"@tailwindcss/vite": "^4.0.0",
|
|
66
66
|
"@vitejs/plugin-react": "^4.0.0 || ^5.0.0",
|
|
67
67
|
"vite-plugin-svgr": "^4.0.0",
|
|
68
|
-
"@uniweb/core": "0.6.
|
|
68
|
+
"@uniweb/core": "0.6.2"
|
|
69
69
|
},
|
|
70
70
|
"peerDependenciesMeta": {
|
|
71
71
|
"vite": {
|
package/src/prerender.js
CHANGED
|
@@ -11,6 +11,7 @@ import { readFile, writeFile, mkdir } from 'node:fs/promises'
|
|
|
11
11
|
import { existsSync, readdirSync, statSync } from 'node:fs'
|
|
12
12
|
import { join, dirname, resolve } from 'node:path'
|
|
13
13
|
import { pathToFileURL } from 'node:url'
|
|
14
|
+
import { deriveCacheKey } from '@uniweb/core'
|
|
14
15
|
import { executeFetch, mergeDataIntoContent, singularize } from './site/data-fetcher.js'
|
|
15
16
|
import { shouldSplitContent } from './site/split-content.js'
|
|
16
17
|
|
|
@@ -509,27 +510,19 @@ export async function prerenderSite(siteDir, options = {}) {
|
|
|
509
510
|
const shellPath = existsSync(htmlPath) ? htmlPath : join(distDir, 'index.html')
|
|
510
511
|
const htmlShell = await readFile(shellPath, 'utf8')
|
|
511
512
|
|
|
512
|
-
//
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
if (
|
|
517
|
-
|
|
518
|
-
uniweb.activeWebsite.dataStore.set(entry.config, entry.data)
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
// Build-specific: load extensions (secondary foundations via URL)
|
|
523
|
-
const extensions = siteContent.config?.extensions
|
|
524
|
-
if (extensions?.length) {
|
|
525
|
-
onProgress(`Loading ${extensions.length} extension(s)...`)
|
|
513
|
+
// Build-specific: load extensions (secondary foundations via URL) BEFORE
|
|
514
|
+
// initPrerender so the Website's FetcherDispatcher sees their routes.
|
|
515
|
+
const extensionSources = siteContent.config?.extensions
|
|
516
|
+
const loadedExtensions = []
|
|
517
|
+
if (extensionSources?.length) {
|
|
518
|
+
onProgress(`Loading ${extensionSources.length} extension(s)...`)
|
|
526
519
|
const projectRoot = join(siteDir, '..')
|
|
527
|
-
for (const ext of
|
|
520
|
+
for (const ext of extensionSources) {
|
|
528
521
|
try {
|
|
529
522
|
const url = typeof ext === 'string' ? ext : ext.url
|
|
530
523
|
const extPath = resolveExtensionPath(url, distDir, projectRoot)
|
|
531
524
|
const extModule = await import(pathToFileURL(extPath).href)
|
|
532
|
-
|
|
525
|
+
loadedExtensions.push(extModule)
|
|
533
526
|
onProgress(` Extension loaded: ${url}`)
|
|
534
527
|
} catch (err) {
|
|
535
528
|
onProgress(` Warning: Extension failed to load: ${ext} (${err.message})`)
|
|
@@ -537,6 +530,17 @@ export async function prerenderSite(siteDir, options = {}) {
|
|
|
537
530
|
}
|
|
538
531
|
}
|
|
539
532
|
|
|
533
|
+
// Initialize the Uniweb runtime using the shared SSR module
|
|
534
|
+
const uniweb = initPrerender(siteContent, foundation, loadedExtensions, { onProgress })
|
|
535
|
+
|
|
536
|
+
// Build-specific: pre-populate DataStore so EntityStore can resolve data during prerender.
|
|
537
|
+
// Use the framework's default cache key so runtime probes hit the same entries.
|
|
538
|
+
if (fetchedData.length > 0 && uniweb.activeWebsite?.dataStore) {
|
|
539
|
+
for (const entry of fetchedData) {
|
|
540
|
+
uniweb.activeWebsite.dataStore.set(deriveCacheKey(entry.config), { data: entry.data })
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
540
544
|
// Pre-fetch icons for SSR embedding
|
|
541
545
|
await prefetchIcons(siteContent, uniweb, onProgress)
|
|
542
546
|
|
package/src/runtime-schema.js
CHANGED
|
@@ -10,7 +10,18 @@
|
|
|
10
10
|
* - defaults: param default values
|
|
11
11
|
* - context: static capabilities for cross-block coordination
|
|
12
12
|
* - initialState: initial values for mutable block state
|
|
13
|
-
* - inheritData:
|
|
13
|
+
* - inheritData: internal flag for cascaded data delivery
|
|
14
|
+
* true → deliver all data available at ancestor levels (default)
|
|
15
|
+
* false → deliver nothing (component opted out with `data: false`)
|
|
16
|
+
*
|
|
17
|
+
* Data delivery is default-on: a component without any `data:` field
|
|
18
|
+
* receives all data cascaded from its ancestor levels (block → page →
|
|
19
|
+
* parent page → site) via `content.data.{schema}`. A component that
|
|
20
|
+
* genuinely cannot tolerate ambient data declares `data: false`.
|
|
21
|
+
*
|
|
22
|
+
* `data: { entity: 'articles' }` is a **declaration**, not a gate. It
|
|
23
|
+
* tells the editor and prepare-props what shape the component expects,
|
|
24
|
+
* but does not restrict delivery.
|
|
14
25
|
*
|
|
15
26
|
* Full metadata (titles, descriptions, hints, etc.) stays in schema.json
|
|
16
27
|
* for the visual editor.
|
|
@@ -202,39 +213,64 @@ export function extractRuntimeSchema(fullMeta) {
|
|
|
202
213
|
}
|
|
203
214
|
|
|
204
215
|
// Data binding (CMS entities)
|
|
205
|
-
//
|
|
206
|
-
//
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
216
|
+
//
|
|
217
|
+
// Supported forms:
|
|
218
|
+
// data: false → explicit opt-out
|
|
219
|
+
// data: { entity: 'person:6' } → declaration + shape hints
|
|
220
|
+
// data: { schemas: {...} } → validation / default shapes
|
|
221
|
+
// data: 'person:6' → legacy string form (declaration only)
|
|
222
|
+
//
|
|
223
|
+
// Deprecated forms (accepted with dev-mode warning, removed in next release):
|
|
224
|
+
// data: { inherit: true | false | [...] } — component-side gating is gone;
|
|
225
|
+
// delivery is default-on
|
|
226
|
+
// data: { detail, limit } — moved to block-level fetch
|
|
227
|
+
if (fullMeta.data === false) {
|
|
228
|
+
// Explicit opt-out — deliver nothing to this component.
|
|
229
|
+
runtime.inheritData = false
|
|
230
|
+
} else if (typeof fullMeta.data === 'string') {
|
|
231
|
+
// Legacy string form: data: 'person:6'
|
|
232
|
+
const parsed = parseDataString(fullMeta.data)
|
|
233
|
+
if (parsed) {
|
|
234
|
+
runtime.data = parsed
|
|
235
|
+
}
|
|
236
|
+
} else if (fullMeta.data && typeof fullMeta.data === 'object') {
|
|
237
|
+
if (fullMeta.data.entity) {
|
|
238
|
+
const parsed = parseDataString(fullMeta.data.entity)
|
|
211
239
|
if (parsed) {
|
|
212
240
|
runtime.data = parsed
|
|
213
241
|
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
runtime.data = parsed
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
if (fullMeta.data.schemas) {
|
|
223
|
-
const schemas = extractSchemas(fullMeta.data.schemas)
|
|
224
|
-
if (schemas) {
|
|
225
|
-
runtime.schemas = schemas
|
|
226
|
-
}
|
|
242
|
+
}
|
|
243
|
+
if (fullMeta.data.schemas) {
|
|
244
|
+
const schemas = extractSchemas(fullMeta.data.schemas)
|
|
245
|
+
if (schemas) {
|
|
246
|
+
runtime.schemas = schemas
|
|
227
247
|
}
|
|
228
|
-
|
|
229
|
-
|
|
248
|
+
}
|
|
249
|
+
// Deprecated: data.inherit is a no-op under default-on delivery. The
|
|
250
|
+
// only behavior we still honor is `inherit: false` → treat as opt-out,
|
|
251
|
+
// so existing foundations that wrote "don't deliver" still don't.
|
|
252
|
+
// Array and `true` forms are ignored — delivery happens regardless.
|
|
253
|
+
if (fullMeta.data.inherit === false) {
|
|
254
|
+
if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {
|
|
255
|
+
console.warn(
|
|
256
|
+
'[uniweb] `data: { inherit: false }` is deprecated; use `data: false` instead.'
|
|
257
|
+
)
|
|
230
258
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
if (
|
|
234
|
-
|
|
259
|
+
runtime.inheritData = false
|
|
260
|
+
} else if (fullMeta.data.inherit !== undefined) {
|
|
261
|
+
if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {
|
|
262
|
+
console.warn(
|
|
263
|
+
'[uniweb] `data: { inherit: ... }` is deprecated; delivery is default-on. Remove the `inherit` field.'
|
|
264
|
+
)
|
|
235
265
|
}
|
|
236
|
-
|
|
237
|
-
|
|
266
|
+
}
|
|
267
|
+
// Deprecated: detail/limit on the component side. Block-level
|
|
268
|
+
// `fetch: { inherit: true, detail, limit }` is where these belong now.
|
|
269
|
+
if (fullMeta.data.detail !== undefined || fullMeta.data.limit !== undefined) {
|
|
270
|
+
if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {
|
|
271
|
+
console.warn(
|
|
272
|
+
'[uniweb] `data: { detail, limit }` on the component side is deprecated; set these on a block-level `fetch: { inherit: true, ... }` instead.'
|
|
273
|
+
)
|
|
238
274
|
}
|
|
239
275
|
}
|
|
240
276
|
}
|
|
@@ -267,18 +303,17 @@ export function extractRuntimeSchema(fullMeta) {
|
|
|
267
303
|
}
|
|
268
304
|
}
|
|
269
305
|
|
|
270
|
-
//
|
|
271
|
-
//
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
runtime.inheritData = fullMeta.inheritData
|
|
306
|
+
// Top-level inheritData (legacy, pre-`data.*` format) — honored only as opt-out.
|
|
307
|
+
// Truthy values and arrays are ignored; delivery is default-on.
|
|
308
|
+
if (fullMeta.inheritData === false && runtime.inheritData === undefined) {
|
|
309
|
+
runtime.inheritData = false
|
|
275
310
|
}
|
|
276
311
|
|
|
277
|
-
//
|
|
278
|
-
//
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
312
|
+
// Data delivery is default-on. `runtime.inheritData` stays undefined unless
|
|
313
|
+
// the component explicitly opts out (runtime.inheritData === false), in
|
|
314
|
+
// which case EntityStore delivers nothing. The declaration `data.entity`
|
|
315
|
+
// no longer implies a gate — it's a hint consumed by prepare-props and
|
|
316
|
+
// the editor.
|
|
282
317
|
|
|
283
318
|
return Object.keys(runtime).length > 0 ? runtime : null
|
|
284
319
|
}
|
package/src/site/data-fetcher.js
CHANGED
|
@@ -225,11 +225,25 @@ export function parseFetchConfig(fetch) {
|
|
|
225
225
|
// Full config object
|
|
226
226
|
if (typeof fetch !== 'object') return null
|
|
227
227
|
|
|
228
|
-
//
|
|
229
|
-
// No URL — merges with the parent fetch config at runtime; only carries
|
|
230
|
-
|
|
228
|
+
// Refine config: { refine: true, detail: false, limit: 3 }
|
|
229
|
+
// No URL — merges with the parent fetch config at runtime; only carries
|
|
230
|
+
// override props. The legacy spelling `inherit: true` is accepted for one
|
|
231
|
+
// release with a warning, then removed.
|
|
232
|
+
//
|
|
233
|
+
// Note on build-vs-runtime scope: this parser passes `sort` and `filter`
|
|
234
|
+
// through on refine configs, but the runtime EntityStore only applies
|
|
235
|
+
// `detail`, `limit`, and `order` overrides. `sort` / `filter` on a refine
|
|
236
|
+
// block are currently accepted by the parser but not honored at runtime.
|
|
237
|
+
// Preserved as-is in this rename commit; revisit separately if needed.
|
|
238
|
+
if (fetch.refine === true || fetch.inherit === true) {
|
|
239
|
+
if (fetch.inherit === true && fetch.refine !== true) {
|
|
240
|
+
console.warn(
|
|
241
|
+
"[uniweb] 'fetch: { inherit: true }' is deprecated; rename to 'fetch: { refine: true }'. " +
|
|
242
|
+
'Accepted for one release; will be removed in the next minor.'
|
|
243
|
+
)
|
|
244
|
+
}
|
|
231
245
|
return {
|
|
232
|
-
|
|
246
|
+
refine: true,
|
|
233
247
|
...(fetch.detail !== undefined ? { detail: fetch.detail } : {}),
|
|
234
248
|
...(fetch.limit !== undefined ? { limit: fetch.limit } : {}),
|
|
235
249
|
...(fetch.sort !== undefined ? { sort: fetch.sort } : {}),
|