@uniweb/build 0.74.1 → 0.74.3
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 +7 -7
- package/src/prerender.js +9 -7
- package/src/uwx/data-schema.js +9 -5
- package/src/uwx/site-project.js +15 -1
- package/src/uwx/site.js +15 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/build",
|
|
3
|
-
"version": "0.74.
|
|
3
|
+
"version": "0.74.3",
|
|
4
4
|
"description": "Build tooling for the Uniweb Component Web Platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -59,13 +59,13 @@
|
|
|
59
59
|
"yaml": "^2.5.0",
|
|
60
60
|
"@uniweb/content-reader": "^1.2.8",
|
|
61
61
|
"@uniweb/content-writer": "^0.3.4",
|
|
62
|
+
"@uniweb/schemas": "^0.10.1",
|
|
62
63
|
"@uniweb/projections": "^0.7.11",
|
|
63
|
-
"@uniweb/
|
|
64
|
-
"@uniweb/
|
|
65
|
-
"@uniweb/theming": "^0.2.0"
|
|
64
|
+
"@uniweb/theming": "^0.2.0",
|
|
65
|
+
"@uniweb/semantic-parser": "^1.5.1"
|
|
66
66
|
},
|
|
67
67
|
"optionalDependencies": {
|
|
68
|
-
"@uniweb/runtime": "^0.28.
|
|
68
|
+
"@uniweb/runtime": "^0.28.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/core": "^0.35.
|
|
78
|
-
"@uniweb/runtime": "^0.28.
|
|
77
|
+
"@uniweb/core": "^0.35.1",
|
|
78
|
+
"@uniweb/runtime": "^0.28.6"
|
|
79
79
|
},
|
|
80
80
|
"peerDependenciesMeta": {
|
|
81
81
|
"vite": {
|
package/src/prerender.js
CHANGED
|
@@ -279,10 +279,11 @@ export async function executeAllFetches(siteContent, siteDir, onProgress, locale
|
|
|
279
279
|
*
|
|
280
280
|
* Emitting the canonical route raw sent a reader who asked for `/fr/<container>`
|
|
281
281
|
* to the DEFAULT-locale page — a language switch they never asked for, from a
|
|
282
|
-
* URL that was correct. The
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
*
|
|
282
|
+
* URL that was correct. The in-app redirect had the identical bug and was fixed
|
|
283
|
+
* first. ⭐ Since 2026-09-30 that one is `resolveRoute`'s (`@uniweb/core/resolve-route`,
|
|
284
|
+
* `localeUrl`); this is the static twin of its destination — the same prefix and
|
|
285
|
+
* translated slug, with the deployment base this pass writes under — and the two
|
|
286
|
+
* must agree or a container behaves differently on a cold load than in the app.
|
|
286
287
|
*
|
|
287
288
|
* The DECISION to redirect stays canonical (`target !== page.route`) — only the
|
|
288
289
|
* destination is localized. Comparing a localized destination against a
|
|
@@ -1024,9 +1025,10 @@ export async function prerenderSite(siteDir, options = {}) {
|
|
|
1024
1025
|
continue
|
|
1025
1026
|
}
|
|
1026
1027
|
|
|
1027
|
-
// Content-less containers: auto-redirect to first descendant with content
|
|
1028
|
-
//
|
|
1029
|
-
//
|
|
1028
|
+
// Content-less containers: auto-redirect to first descendant with content —
|
|
1029
|
+
// `getNavigableRoute()`, which is `resolveRoute`'s landing rule
|
|
1030
|
+
// (`@uniweb/core/resolve-route`), so the stub goes where the app would.
|
|
1031
|
+
// A stub so the redirect works without JS (via <meta http-equiv="refresh">).
|
|
1030
1032
|
if (!page.hasContent()) {
|
|
1031
1033
|
const target = page.getNavigableRoute()
|
|
1032
1034
|
if (target && target !== page.route) {
|
package/src/uwx/data-schema.js
CHANGED
|
@@ -112,8 +112,8 @@ export function isContentBodyField(field) {
|
|
|
112
112
|
* @param {(ref: string) => string} [opts.resolveOptions] - maps an `options`
|
|
113
113
|
* (item_ref) ref to its full `@org/model/<section>` path. Falls back to
|
|
114
114
|
* `resolveName` (model only) when not supplied.
|
|
115
|
-
* @returns {Object} the declaration (`{ name, label?,
|
|
116
|
-
* linkable?, sections }`).
|
|
115
|
+
* @returns {Object} the declaration (`{ name, label?, plural?, description?,
|
|
116
|
+
* source_locale?, linkable?, sections }`).
|
|
117
117
|
*/
|
|
118
118
|
export function toDataSchemaDeclaration(normalized, { name, resolveName, resolveOptions } = {}) {
|
|
119
119
|
if (!name) throw new Error('toDataSchemaDeclaration: a registry name is required')
|
|
@@ -136,19 +136,23 @@ export function toDataSchemaDeclaration(normalized, { name, resolveName, resolve
|
|
|
136
136
|
|
|
137
137
|
const decl = { name }
|
|
138
138
|
if (normalized.label) decl.label = normalized.label
|
|
139
|
+
if (normalized.plural) decl.plural = normalized.plural
|
|
139
140
|
if (normalized.description) decl.description = normalized.description
|
|
140
141
|
if (normalized.sourceLocale) decl.source_locale = normalized.sourceLocale
|
|
141
142
|
// ⛔ No `creatable_by`: retired 2026-09-23 — Models are open, and `@uniweb/schemas`
|
|
142
143
|
// refuses the key. (It was emitted here when authored.)
|
|
143
144
|
|
|
144
145
|
// `linkable` — AUTHORED ONLY (2026-09-27 [Diego]). A model is linkable unless it says
|
|
145
|
-
// `linkable: false` (omitted ⇒ true), with or without a brief
|
|
146
|
-
//
|
|
147
|
-
//
|
|
146
|
+
// `linkable: false` (omitted ⇒ true), with or without a brief. `linkable: false` keeps
|
|
147
|
+
// its entries out of references and out of a site's folder, and nothing else keeps them
|
|
148
|
+
// out of a folder. Whether a reference field may point at a model with no brief — no
|
|
149
|
+
// card to show — is the host's to decide, and nothing here derives it.
|
|
148
150
|
//
|
|
149
151
|
// ⛔ Until then a brief-less model was emitted `linkable: false` and `linkable: true`
|
|
150
152
|
// on one was refused, on the reasoning that a reference would have no card to show —
|
|
151
153
|
// which kept its records out of every site's folder.
|
|
154
|
+
// ⛔ And until 2026-09-30 this said a missing card "is not a reason to refuse" a
|
|
155
|
+
// reference, which read as a promise that one is accepted. A backend refuses it.
|
|
152
156
|
if (normalized.linkable === false) decl.linkable = false
|
|
153
157
|
|
|
154
158
|
decl.sections = sections
|
package/src/uwx/site-project.js
CHANGED
|
@@ -839,7 +839,7 @@ function writePagesTree(pages, pagesDir, sourceLocale, report, ctx, routePrefix
|
|
|
839
839
|
// ⚖️ A pull adds and changes these, and never removes one: a page pushed by a CLI from before carries
|
|
840
840
|
// none, and reading that as "none" would delete what the author wrote.
|
|
841
841
|
function pageSlugToYml(record, canon, existing, sourceLocale, ctx) {
|
|
842
|
-
if (record.is_dynamic) return
|
|
842
|
+
if (record.is_dynamic) return dynamicOwnSlug(record, existing, sourceLocale)
|
|
843
843
|
const targets = localizedTargets(record.slug, sourceLocale)
|
|
844
844
|
const own = isPlainRecord(existing?.slug)
|
|
845
845
|
if (!own && ctx.routes?.siteMap && !record.is_index) {
|
|
@@ -851,6 +851,20 @@ function pageSlugToYml(record, canon, existing, sourceLocale, ctx) {
|
|
|
851
851
|
return own && stableJson(next) === stableJson(existing.slug) ? null : next
|
|
852
852
|
}
|
|
853
853
|
|
|
854
|
+
// A parametric page's folder is named by its param (`pageDirName`), so its OWN slug — where a page
|
|
855
|
+
// made in an app gives it one of its own (`slug: detail`, `param_name: id`) — has no other place on
|
|
856
|
+
// disk. It is kept in `page.yml` as a plain string, which the build never reads for a route (only a
|
|
857
|
+
// per-language map names URL segments) and the push sends back as the page's slug. A string written
|
|
858
|
+
// here before is rewritten to the stored slug, so it is never left stale.
|
|
859
|
+
// ⛔ Until 2026-09-30 a pull dropped it, and the next push sent the param in its place: an authored
|
|
860
|
+
// value lost on a round trip (`uwx-format.md` § THE ROUND-TRIP LAW).
|
|
861
|
+
function dynamicOwnSlug(record, existing, sourceLocale) {
|
|
862
|
+
const own = unwrapLocalized(record.slug, sourceLocale)
|
|
863
|
+
if (typeof own !== 'string' || !own || own === '...path') return null
|
|
864
|
+
if (own !== (record.param_name || own)) return own
|
|
865
|
+
return typeof existing?.slug === 'string' ? own : null
|
|
866
|
+
}
|
|
867
|
+
|
|
854
868
|
// The locales of a pulled localized `slug` other than the source one.
|
|
855
869
|
function localizedTargets(slug, sourceLocale) {
|
|
856
870
|
if (!isPlainRecord(slug)) return {}
|
package/src/uwx/site.js
CHANGED
|
@@ -224,7 +224,15 @@ function localizeRouteSlugs(pages, siteYml, sourceLocale) {
|
|
|
224
224
|
export function buildRouteOf(record, parentRoute, sourceLocale) {
|
|
225
225
|
if (record?.is_index) return parentRoute
|
|
226
226
|
const slug = unwrapLocalized(record?.slug, sourceLocale)
|
|
227
|
-
|
|
227
|
+
// A parametric page's segment is its PARAM — the name the pull gives its folder, `[param_name]`
|
|
228
|
+
// (`site-project.js::pageDirName`), which is the route the build then reads. A page pushed from
|
|
229
|
+
// files has one word for both; a page made in an app may not (`slug: detail`, `param_name: id`).
|
|
230
|
+
// ⛔ Until 2026-09-30 this used the slug, so such a page was addressed `/…/:detail` while its
|
|
231
|
+
// folder built `/…/:id`, and its translated title and description were filed under a route no
|
|
232
|
+
// build reads.
|
|
233
|
+
const segment = record?.is_dynamic
|
|
234
|
+
? (slug === CATCH_ALL_MARKER ? ':path*' : `:${record.param_name || slug}`)
|
|
235
|
+
: slug
|
|
228
236
|
return parentRoute === '/' ? `/${segment}` : `${parentRoute}/${segment}`
|
|
229
237
|
}
|
|
230
238
|
|
|
@@ -783,6 +791,12 @@ async function walkPagesNested(ctx, dirPath, parentSlugPath, inheritedMode, pare
|
|
|
783
791
|
// A page's own localized URL segments, for `localizeRouteSlugs` — under a symbol, so they
|
|
784
792
|
// are never serialized as a field of the record.
|
|
785
793
|
if (!dyn && isPlainRecord(f.config?.slug)) record[AUTHORED_SLUG] = f.config.slug
|
|
794
|
+
// A parametric page's OWN slug, where it is not its param — a page made in an app — is kept in
|
|
795
|
+
// its page.yml by the pull as a plain string (`site-project.js::dynamicOwnSlug`), since its folder
|
|
796
|
+
// is named by its param. The route, the folder and the page's identity stay the param's.
|
|
797
|
+
if (dyn && dyn[1] !== CATCH_ALL_MARKER && typeof f.config?.slug === 'string' && f.config.slug) {
|
|
798
|
+
record.slug = { ...record.slug, [sourceLocale]: f.config.slug }
|
|
799
|
+
}
|
|
786
800
|
|
|
787
801
|
if (mode === 'page') {
|
|
788
802
|
const sections = await collectPageSectionsNested(f.path, siteRoot, f.config)
|