@uniweb/build 1.6.1 → 1.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/package.json +8 -8
- package/src/runtime-schema.js +36 -0
- package/src/schema.js +18 -0
- package/src/site/content-collector.js +8 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/build",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Build tooling for the Uniweb Component Web Platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -56,15 +56,15 @@
|
|
|
56
56
|
"js-yaml": "^4.1.0",
|
|
57
57
|
"sharp": "^0.35.3",
|
|
58
58
|
"yaml": "^2.5.0",
|
|
59
|
-
"@uniweb/
|
|
59
|
+
"@uniweb/content-reader": "~1.5.4",
|
|
60
60
|
"@uniweb/content-writer": "~1.3.0",
|
|
61
|
+
"@uniweb/projections": "~1.1.6",
|
|
62
|
+
"@uniweb/schemas": "~1.6.0",
|
|
61
63
|
"@uniweb/semantic-parser": "~1.12.2",
|
|
62
|
-
"@uniweb/theming": "~1.2.0"
|
|
63
|
-
"@uniweb/content-reader": "~1.5.4",
|
|
64
|
-
"@uniweb/projections": "~1.1.4"
|
|
64
|
+
"@uniweb/theming": "~1.2.0"
|
|
65
65
|
},
|
|
66
66
|
"optionalDependencies": {
|
|
67
|
-
"@uniweb/runtime": "~1.1.
|
|
67
|
+
"@uniweb/runtime": "~1.1.13"
|
|
68
68
|
},
|
|
69
69
|
"peerDependencies": {
|
|
70
70
|
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
|
|
@@ -73,8 +73,8 @@
|
|
|
73
73
|
"@tailwindcss/vite": "^4.0.0",
|
|
74
74
|
"@vitejs/plugin-react": "^4.0.0 || ^5.0.0",
|
|
75
75
|
"vite-plugin-svgr": "^4.0.0",
|
|
76
|
-
"@uniweb/
|
|
77
|
-
"@uniweb/
|
|
76
|
+
"@uniweb/core": "~1.4.0",
|
|
77
|
+
"@uniweb/runtime": "~1.1.13"
|
|
78
78
|
},
|
|
79
79
|
"peerDependenciesMeta": {
|
|
80
80
|
"vite": {
|
package/src/runtime-schema.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* reads to render, and nothing else:
|
|
6
6
|
*
|
|
7
7
|
* - background: 'self' when component handles its own background
|
|
8
|
+
* - sticky: { default, top? } — whether the section stays in view unless its author says
|
|
8
9
|
* - data: { <key>: <schema ref> | { schema, single?, whole? } | null } — every `content.data`
|
|
9
10
|
* key the component declares, in order, with its schema ref and the flags set on it (null for
|
|
10
11
|
* an inline shape)
|
|
@@ -37,6 +38,35 @@
|
|
|
37
38
|
import { lowerData } from '@uniweb/schemas/content'
|
|
38
39
|
import { schemaDeclarationOf } from '@uniweb/schemas/foundation'
|
|
39
40
|
|
|
41
|
+
/**
|
|
42
|
+
* A section type's `sticky`, checked: `true` or `false`, or `{ default, top }`. `top` is how far
|
|
43
|
+
* below the top the section stays — one CSS value, `4rem` or `var(--header-height)` — and is
|
|
44
|
+
* written into the style of the box that sticks, so a declaration (`;`, a brace) is refused.
|
|
45
|
+
*
|
|
46
|
+
* @param {*} declared - `meta.js`'s `sticky`
|
|
47
|
+
* @returns {{ default: boolean, top?: string }}
|
|
48
|
+
* @throws {Error} for any other shape
|
|
49
|
+
*/
|
|
50
|
+
export function stickyDeclaration(declared) {
|
|
51
|
+
if (typeof declared === 'boolean') return { default: declared }
|
|
52
|
+
if (!declared || typeof declared !== 'object' || Array.isArray(declared)) {
|
|
53
|
+
throw new Error(`[uniweb] Invalid 'sticky': write true, false or { default, top } — got ${JSON.stringify(declared)}`)
|
|
54
|
+
}
|
|
55
|
+
const unknown = Object.keys(declared).filter((key) => key !== 'default' && key !== 'top')
|
|
56
|
+
if (unknown.length) throw new Error(`[uniweb] Invalid 'sticky': ${unknown.join(', ')} — it takes default and top`)
|
|
57
|
+
if (declared.default !== undefined && typeof declared.default !== 'boolean') {
|
|
58
|
+
throw new Error(`[uniweb] Invalid 'sticky.default': write true or false`)
|
|
59
|
+
}
|
|
60
|
+
const out = { default: declared.default === true }
|
|
61
|
+
if (declared.top !== undefined) {
|
|
62
|
+
if (typeof declared.top !== 'string' || !declared.top.trim() || /[;{}]/.test(declared.top)) {
|
|
63
|
+
throw new Error(`[uniweb] Invalid 'sticky.top': one CSS length, such as '4rem' or 'var(--header-height)'`)
|
|
64
|
+
}
|
|
65
|
+
out.top = declared.top.trim()
|
|
66
|
+
}
|
|
67
|
+
return out
|
|
68
|
+
}
|
|
69
|
+
|
|
40
70
|
/**
|
|
41
71
|
* A `data:` entry as the runtime reads it: its schema ref, with the flags set on it — or null for an
|
|
42
72
|
* inline shape, which names no schema and is filled only under its own key.
|
|
@@ -132,6 +162,12 @@ export function extractRuntimeSchema(fullMeta) {
|
|
|
132
162
|
runtime.background = fullMeta.background
|
|
133
163
|
}
|
|
134
164
|
|
|
165
|
+
// Whether the section stays in view by default: core reads it, and the author's
|
|
166
|
+
// `sticky:` wins (`Block#initComponent`). The runtime makes sticky the box that can.
|
|
167
|
+
if (fullMeta.sticky !== undefined) {
|
|
168
|
+
runtime.sticky = stickyDeclaration(fullMeta.sticky)
|
|
169
|
+
}
|
|
170
|
+
|
|
135
171
|
// Data. `data:` is the single declaration surface for a section's structured
|
|
136
172
|
// data: it maps each `content.data` key to its schema. A value is a named ref
|
|
137
173
|
// (`'@/member'`), an inline field map, or an inline rich-form (`{ fields: [...] }`,
|
package/src/schema.js
CHANGED
|
@@ -22,6 +22,7 @@ import { describeContent, lowerData } from '@uniweb/schemas/content'
|
|
|
22
22
|
import { normalizeData, schemaDeclarationOf } from '@uniweb/schemas/foundation'
|
|
23
23
|
import { rootListSection } from '@uniweb/schemas/conform'
|
|
24
24
|
import { SECTION_PARAMS, SECTION_KEYS } from '@uniweb/schemas/section'
|
|
25
|
+
import { stickyDeclaration } from './runtime-schema.js'
|
|
25
26
|
import { collectSchemaRefs, buildDataSchemaMap, ownSchemaRefs } from './resolve-data-schema.js'
|
|
26
27
|
import {
|
|
27
28
|
composeSupports,
|
|
@@ -652,12 +653,29 @@ function buildComponentEntry(name, relativePath, meta) {
|
|
|
652
653
|
|
|
653
654
|
if (meta.data !== undefined) entry.data = lowerData(meta.data)
|
|
654
655
|
refuseDataDeclarations(`${name} (meta.js)`, entry.data)
|
|
656
|
+
refuseStickyDeclaration(`${name} (meta.js)`, meta.sticky)
|
|
655
657
|
|
|
656
658
|
const problems = [...content.problems, ...describeChildren(meta).problems]
|
|
657
659
|
if (problems.length > 0) loweringProblems.set(entry, problems)
|
|
658
660
|
return entry
|
|
659
661
|
}
|
|
660
662
|
|
|
663
|
+
/**
|
|
664
|
+
* Refuse a `sticky` that is not `true`, `false` or `{ default, top }` (`stickyDeclaration`), naming
|
|
665
|
+
* the component — the lean runtime schema checks it too, without the name.
|
|
666
|
+
*
|
|
667
|
+
* @param {string} owner - who declares it, for the message
|
|
668
|
+
* @param {*} sticky - `meta.js`'s `sticky`
|
|
669
|
+
*/
|
|
670
|
+
function refuseStickyDeclaration(owner, sticky) {
|
|
671
|
+
if (sticky === undefined) return
|
|
672
|
+
try {
|
|
673
|
+
stickyDeclaration(sticky)
|
|
674
|
+
} catch (err) {
|
|
675
|
+
throw new Error(`[uniweb] ${owner}: ${err.message.replace(/^\[uniweb\] /, '')}`)
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
661
679
|
/**
|
|
662
680
|
* Refuse a `data:` key typed by a schema that is not written as one: the retired `/*` suffix, a
|
|
663
681
|
* property of the long form other than `schema`, `single` and `whole`, or a flag that is not a
|
|
@@ -39,6 +39,7 @@ import { resolveExtensionUrls } from './extension-urls.js'
|
|
|
39
39
|
import { buildTheme, extractFoundationVars } from '../theme/index.js'
|
|
40
40
|
import { resolveDefaultLocale, resolvePublishableLocales, validateLanguageConfig, pageRouteQuery, currentFor } from '@uniweb/core'
|
|
41
41
|
import { authoredRedirectTarget } from '@uniweb/core/resolve-route'
|
|
42
|
+
import { parentRouteOf } from '@uniweb/core/route-match'
|
|
42
43
|
import { parseFrontmatter } from '../utils/frontmatter.js'
|
|
43
44
|
import { parseGrid } from '@uniweb/schemas/grid'
|
|
44
45
|
import { runtimeServicesConfig, refuseRetiredServiceKeys, refuseUnreadableServices } from '../uwx/services-request.js'
|
|
@@ -2719,8 +2720,10 @@ export async function collectSiteContent(sitePath, options = {}) {
|
|
|
2719
2720
|
}
|
|
2720
2721
|
}
|
|
2721
2722
|
|
|
2722
|
-
// Compute parent route for each page (hierarchy declaration)
|
|
2723
|
-
//
|
|
2723
|
+
// Compute parent route for each page (hierarchy declaration), by the one parent rule
|
|
2724
|
+
// (`parentRouteOf`) — so the parent declared here is the one every lane derives, past a
|
|
2725
|
+
// version folder included. This runs once at build time so runtime doesn't need to
|
|
2726
|
+
// re-derive hierarchy.
|
|
2724
2727
|
const pageRouteMap = new Map()
|
|
2725
2728
|
for (const page of pages) {
|
|
2726
2729
|
pageRouteMap.set(page.route, page)
|
|
@@ -2728,15 +2731,10 @@ export async function collectSiteContent(sitePath, options = {}) {
|
|
|
2728
2731
|
pageRouteMap.set(page.sourcePath, page)
|
|
2729
2732
|
}
|
|
2730
2733
|
}
|
|
2734
|
+
const hasPage = (route) => pageRouteMap.has(route)
|
|
2731
2735
|
for (const page of pages) {
|
|
2732
|
-
const
|
|
2733
|
-
|
|
2734
|
-
page.parent = null
|
|
2735
|
-
continue
|
|
2736
|
-
}
|
|
2737
|
-
const parentRoute = '/' + segments.slice(0, -1).join('/')
|
|
2738
|
-
const parentPage = pageRouteMap.get(parentRoute)
|
|
2739
|
-
page.parent = parentPage ? parentPage.route : null
|
|
2736
|
+
const parentRoute = parentRouteOf(page.route, { has: hasPage })
|
|
2737
|
+
page.parent = parentRoute ? pageRouteMap.get(parentRoute).route : null
|
|
2740
2738
|
}
|
|
2741
2739
|
|
|
2742
2740
|
const siteFetch = parseFetchConfig(...fetchIn(siteConfig, 'site.yml', 'site'))
|