@uniweb/build 0.68.0 → 0.69.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/site/content-collector.js +38 -6
- package/src/site/nav-visibility.js +22 -1
- package/src/uwx/project-writer.js +2 -1
- package/src/uwx/site-project.js +11 -2
- package/src/uwx/site.js +10 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/build",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.69.0",
|
|
4
4
|
"description": "Build tooling for the Uniweb Component Web Platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -57,12 +57,12 @@
|
|
|
57
57
|
"js-yaml": "^4.1.0",
|
|
58
58
|
"sharp": "^0.35.3",
|
|
59
59
|
"yaml": "^2.5.0",
|
|
60
|
-
"@uniweb/
|
|
61
|
-
"@uniweb/semantic-parser": "^1.4.1",
|
|
62
|
-
"@uniweb/schemas": "^0.8.2",
|
|
60
|
+
"@uniweb/schemas": "^0.8.3",
|
|
63
61
|
"@uniweb/content-writer": "^0.3.4",
|
|
64
|
-
"@uniweb/
|
|
65
|
-
"@uniweb/
|
|
62
|
+
"@uniweb/theming": "^0.1.15",
|
|
63
|
+
"@uniweb/content-reader": "^1.2.7",
|
|
64
|
+
"@uniweb/projections": "^0.7.8",
|
|
65
|
+
"@uniweb/semantic-parser": "^1.4.1"
|
|
66
66
|
},
|
|
67
67
|
"optionalDependencies": {
|
|
68
68
|
"@uniweb/runtime": "^0.27.10"
|
|
@@ -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/
|
|
78
|
-
"@uniweb/
|
|
77
|
+
"@uniweb/core": "^0.32.2",
|
|
78
|
+
"@uniweb/runtime": "^0.27.10"
|
|
79
79
|
},
|
|
80
80
|
"peerDependenciesMeta": {
|
|
81
81
|
"vite": {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Section frontmatter reserved properties:
|
|
12
12
|
* - type: Component type (e.g., "Hero", "Features")
|
|
13
|
+
* - hidden: `true` makes the section a draft — left out of a published build, kept in dev
|
|
13
14
|
* - props: Additional component props (merged with other params)
|
|
14
15
|
* - fetch: The section's binding — names a query (query, as, where, sort, limit, current,
|
|
15
16
|
* prerender, detailPage); `query:` is its shorthand
|
|
@@ -31,7 +32,7 @@ import yaml from 'js-yaml'
|
|
|
31
32
|
import { YAML_OPTIONS } from '../utils/yaml-schema.js'
|
|
32
33
|
import { collectSectionAssets, mergeAssetCollections, collectConfigAssets } from './assets.js'
|
|
33
34
|
import { collectSectionIcons, mergeIconCollections, buildIconManifest } from './icons.js'
|
|
34
|
-
import { normalizeHideIn, dropUnpublishedPages } from './nav-visibility.js'
|
|
35
|
+
import { normalizeHideIn, dropUnpublishedPages, dropHiddenSections } from './nav-visibility.js'
|
|
35
36
|
import { parseFetchConfig, toFetchList } from './data-fetcher.js'
|
|
36
37
|
import { resolveExtensionUrls } from './extension-urls.js'
|
|
37
38
|
import { buildTheme, extractFoundationVars } from '../theme/index.js'
|
|
@@ -1009,7 +1010,17 @@ async function processMarkdownFile(filePath, id, siteRoot, defaultStableId = nul
|
|
|
1009
1010
|
|
|
1010
1011
|
// `query`, `fetch` and `data` are never params: `query:` / `fetch:` declare the
|
|
1011
1012
|
// section's own data, and a leftover `data:` is refused (`declaredFetch`).
|
|
1012
|
-
const { type, preset, input, props, fetch, query, data, id: frontmatterId, ...params } = frontMatter
|
|
1013
|
+
const { type, hidden, preset, input, props, fetch, query, data, id: frontmatterId, ...params } = frontMatter
|
|
1014
|
+
// ⭐ `hidden: true` — a draft section, the page's meaning one level down: left out of a
|
|
1015
|
+
// published build, kept by `uniweb dev`, and synced as the section's own `hidden`
|
|
1016
|
+
// [Diego, 2026-09-28]. ⛔ Until then it was an ordinary param: the component received it
|
|
1017
|
+
// and the section rendered everywhere.
|
|
1018
|
+
if (hidden !== undefined && typeof hidden !== 'boolean') {
|
|
1019
|
+
console.warn(
|
|
1020
|
+
`[content-collector] ${relative(siteRoot, filePath)}: hidden: ${JSON.stringify(hidden)} is not true or false — ` +
|
|
1021
|
+
`the section stays visible.`
|
|
1022
|
+
)
|
|
1023
|
+
}
|
|
1013
1024
|
// ⛔ `preset:` and `input:` do nothing, so they are dropped with a warning rather than
|
|
1014
1025
|
// handed to the component as params. Nothing ever read either [2026-09-27]: a preset is
|
|
1015
1026
|
// a named set of params a section type's `meta.js` offers an editor, and a file writes
|
|
@@ -1058,6 +1069,7 @@ async function processMarkdownFile(filePath, id, siteRoot, defaultStableId = nul
|
|
|
1058
1069
|
id,
|
|
1059
1070
|
stableId,
|
|
1060
1071
|
type: type || null,
|
|
1072
|
+
...(hidden === true ? { hidden: true } : {}),
|
|
1061
1073
|
params: { ...params, ...props },
|
|
1062
1074
|
content: proseMirrorContent,
|
|
1063
1075
|
fetch: parseFetchConfig(resolvedFetch, relative(siteRoot, filePath), { level: 'section' }),
|
|
@@ -2790,10 +2802,11 @@ export async function collectSiteContent(sitePath, options = {}) {
|
|
|
2790
2802
|
links: themeLinks
|
|
2791
2803
|
},
|
|
2792
2804
|
// Reachability axis: on the published build paths, drop `hidden` pages and
|
|
2793
|
-
// their whole subtree (cascade)
|
|
2794
|
-
|
|
2805
|
+
// their whole subtree (cascade), and every `hidden` section with its children.
|
|
2806
|
+
// Dev keeps them so drafts stay previewable.
|
|
2807
|
+
pages: dropUnpublished ? dropUnpublishedPages(pages).map(withoutHiddenSections) : pages,
|
|
2795
2808
|
// Layout area sets: { default: { header: page, footer: page, ... }, marketing: { ... } }
|
|
2796
|
-
layouts,
|
|
2809
|
+
layouts: dropUnpublished ? mapLayoutAreas(layouts, withoutHiddenSections) : layouts,
|
|
2797
2810
|
// ⭐ THE SAME REACHABILITY AXIS, for the 404 slot. `hidden: true` means DRAFT —
|
|
2798
2811
|
// "excluded from the published site" (`docs/reference/page-configuration.md`);
|
|
2799
2812
|
// `hideIn: ['*']` is the control for "routed but in no nav".
|
|
@@ -2806,7 +2819,7 @@ export async function collectSiteContent(sitePath, options = {}) {
|
|
|
2806
2819
|
// shell), and `uniweb dev` keeps the page previewable like any other draft.
|
|
2807
2820
|
// No cascade to resolve: this slot is root-level only, so its own flag is
|
|
2808
2821
|
// the only one that can apply.
|
|
2809
|
-
notFound: dropUnpublished
|
|
2822
|
+
notFound: dropUnpublished ? (notFound?.hidden ? null : withoutHiddenSections(notFound)) : notFound,
|
|
2810
2823
|
// Versioned scopes: route → { versions, latestId }
|
|
2811
2824
|
versionedScopes: versionedScopesObj,
|
|
2812
2825
|
assets: assetCollection.assets,
|
|
@@ -2817,6 +2830,25 @@ export async function collectSiteContent(sitePath, options = {}) {
|
|
|
2817
2830
|
}
|
|
2818
2831
|
}
|
|
2819
2832
|
|
|
2833
|
+
// A page (or a layout area) without its hidden sections — `dropHiddenSections`.
|
|
2834
|
+
function withoutHiddenSections(page) {
|
|
2835
|
+
if (!page || !Array.isArray(page.sections)) return page
|
|
2836
|
+
return { ...page, sections: dropHiddenSections(page.sections) }
|
|
2837
|
+
}
|
|
2838
|
+
|
|
2839
|
+
// Apply `fn` to every area of every layout: `{ default: { header: page, … }, … }`.
|
|
2840
|
+
function mapLayoutAreas(layouts, fn) {
|
|
2841
|
+
if (!layouts || typeof layouts !== 'object') return layouts
|
|
2842
|
+
return Object.fromEntries(
|
|
2843
|
+
Object.entries(layouts).map(([name, areas]) => [
|
|
2844
|
+
name,
|
|
2845
|
+
areas && typeof areas === 'object'
|
|
2846
|
+
? Object.fromEntries(Object.entries(areas).map(([area, page]) => [area, fn(page)]))
|
|
2847
|
+
: areas,
|
|
2848
|
+
])
|
|
2849
|
+
)
|
|
2850
|
+
}
|
|
2851
|
+
|
|
2820
2852
|
// Shared pure / IO helpers. Exported for testing AND reused by
|
|
2821
2853
|
// @uniweb/build/uwx, the .uwx exporter, which re-walks the source rather
|
|
2822
2854
|
// than the flattened collector output but reuses these primitives so
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Page-visibility helpers — the two orthogonal axes a page carries:
|
|
2
2
|
// • Reachability — `hidden: bool`. `true` = excluded from the PUBLISHED output
|
|
3
|
-
// entirely (see `dropUnpublishedPages
|
|
3
|
+
// entirely (see `dropUnpublishedPages`, and `dropHiddenSections` for a section).
|
|
4
|
+
// Still previewable in `uniweb dev`.
|
|
4
5
|
// • Nav placement — `hideIn: string[]`. Which nav areas suppress the page while
|
|
5
6
|
// it IS routed (see `normalizeHideIn`). `['*']` = suppressed from every area.
|
|
6
7
|
|
|
@@ -67,3 +68,23 @@ export function dropUnpublishedPages(pages) {
|
|
|
67
68
|
}
|
|
68
69
|
return pages.filter((p) => !isUnpublished(p))
|
|
69
70
|
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Drop hidden sections — the same reachability axis, one level down. A section with
|
|
74
|
+
* `hidden: true` in its frontmatter is a draft: left out of the PUBLISHED output together
|
|
75
|
+
* with its child sections, and kept by `uniweb dev` so it stays previewable. Applied where
|
|
76
|
+
* `dropUnpublishedPages` is, to a page's sections and a layout area's alike.
|
|
77
|
+
*
|
|
78
|
+
* @param {Array<object>} sections - collected sections (each may hold `subsections`)
|
|
79
|
+
* @returns {Array<object>} the sections without the hidden ones, at every depth
|
|
80
|
+
*/
|
|
81
|
+
export function dropHiddenSections(sections) {
|
|
82
|
+
if (!Array.isArray(sections)) return sections
|
|
83
|
+
return sections
|
|
84
|
+
.filter((section) => section?.hidden !== true)
|
|
85
|
+
.map((section) =>
|
|
86
|
+
Array.isArray(section.subsections) && section.subsections.length > 0
|
|
87
|
+
? { ...section, subsections: dropHiddenSections(section.subsections) }
|
|
88
|
+
: section
|
|
89
|
+
)
|
|
90
|
+
}
|
|
@@ -41,7 +41,8 @@ export const DEFAULT_RESERVED_FRONTMATTER = new Set([
|
|
|
41
41
|
'fetch',
|
|
42
42
|
'data',
|
|
43
43
|
'nest',
|
|
44
|
-
|
|
44
|
+
// ⛔ Not `hidden`, which was here from the start (2026-05-30) with no reason recorded: an
|
|
45
|
+
// editor toggles it, so a pull must carry the store's value into the file (2026-09-28).
|
|
45
46
|
])
|
|
46
47
|
|
|
47
48
|
// js-yaml dump options shared by every config write, so output is byte-stable.
|
package/src/uwx/site-project.js
CHANGED
|
@@ -444,7 +444,7 @@ function reinlineInsets(content, insets) {
|
|
|
444
444
|
export function sectionRecordToFile({ filePath, record, sourceLocale = LOCALIZED_FIELD_ASSUMPTION.defaultSourceLocale, collector, freeformRelPath, freeformCandidates = null, writeId = true, context = null }) {
|
|
445
445
|
// ⛔ A stored `preset` or `input` is not written back: neither has an effect, and the build
|
|
446
446
|
// would only warn about it (2026-09-27).
|
|
447
|
-
const { type, stable_id, params, content, insets, fetch, background, theme_override } = record || {}
|
|
447
|
+
const { type, stable_id, hidden, params, content, insets, fetch, background, theme_override } = record || {}
|
|
448
448
|
|
|
449
449
|
// A localized `content` field unwraps to the source-locale doc for the body; its
|
|
450
450
|
// target-locale structural maps are captured into the locales/ collector, and any
|
|
@@ -452,9 +452,18 @@ export function sectionRecordToFile({ filePath, record, sourceLocale = LOCALIZED
|
|
|
452
452
|
// locales/freeform/. A bare doc (source-only / pre-localization) passes through.
|
|
453
453
|
const sourceContent = unwrapLocalizedContent(content, sourceLocale, collector, freeformRelPath, freeformCandidates, context)
|
|
454
454
|
|
|
455
|
+
const { hidden: paramHidden, ...paramsRest } = params && typeof params === 'object' ? params : {}
|
|
455
456
|
const frontmatter = {}
|
|
456
457
|
if (type !== undefined) frontmatter.type = type
|
|
457
|
-
|
|
458
|
+
// ⭐ `hidden:` says what the store says: written for a hidden section, and removed from the
|
|
459
|
+
// file of a visible one — an editor toggles it [2026-09-28]. ⛔ Until then a pull dropped it,
|
|
460
|
+
// and the next push un-hid the section. A push before that day sent a file's `hidden:` inside
|
|
461
|
+
// `params`, so one stored there counts when the field is absent.
|
|
462
|
+
frontmatter.hidden = (typeof hidden === 'boolean' ? hidden : paramHidden === true) ? true : undefined
|
|
463
|
+
Object.assign(frontmatter, paramsRest)
|
|
464
|
+
// ⚠️ A stored `background` / `theme_override` field wins over the same key in `params` while
|
|
465
|
+
// one is stored: an editor may still write the fields rather than `params` (2026-09-28;
|
|
466
|
+
// uwx-format.md § A page section's fields, Order).
|
|
458
467
|
if (background !== undefined) frontmatter.background = background
|
|
459
468
|
if (theme_override !== undefined) frontmatter.theme = theme_override
|
|
460
469
|
// Invert the build's resolution rather than copy it — see fetch-shapes.js. A new
|
package/src/uwx/site.js
CHANGED
|
@@ -142,9 +142,9 @@ function stripCredentials(block, label) {
|
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
// page_sections and layout_sections share this content shape.
|
|
145
|
-
// processMarkdownFile takes only type/props/fetch/query/data/id out of the
|
|
145
|
+
// processMarkdownFile takes only type/hidden/props/fetch/query/data/id out of the
|
|
146
146
|
// frontmatter (and drops preset/input), so `background:` and `theme:` stay
|
|
147
|
-
// inside section.params —
|
|
147
|
+
// inside section.params — and ride there on the wire too (`mapSectionData`).
|
|
148
148
|
// The `type` a push sends for a section whose file names none — the Model requires one. A pull does
|
|
149
149
|
// not write it back into a file that names none (`sectionRecordToFile`): absent, the foundation's
|
|
150
150
|
// `defaultSection` renders it, which is not always `Content`.
|
|
@@ -302,19 +302,20 @@ function localizePageMeta(page, route, sourceLocale, translations) {
|
|
|
302
302
|
|
|
303
303
|
function mapSectionData(section) {
|
|
304
304
|
const params = { ...section.params }
|
|
305
|
-
const background = params.background
|
|
306
|
-
const themeOverride = params.theme
|
|
307
|
-
delete params.background
|
|
308
|
-
delete params.theme
|
|
309
|
-
|
|
310
305
|
const data = { type: section.type || FILLED_SECTION_TYPE } // entity type requires `type`
|
|
311
306
|
setIf(data, 'stable_id', section.stableId ?? undefined)
|
|
307
|
+
// ⭐ A draft section — the store's own `hidden`, which an editor also writes [2026-09-28].
|
|
308
|
+
if (section.hidden === true) data.hidden = true
|
|
312
309
|
// No `preset` or `input`: nothing read either, and the collector drops them (2026-09-27).
|
|
310
|
+
// ⭐ `background` and `theme` stay IN `params` [Diego, 2026-09-28] — where the runtime reads
|
|
311
|
+
// them, and where a section stores them from now on. ⚠️ Also sent as the two fields they had
|
|
312
|
+
// until today, until every reader of a stored section reads `params`; a pull prefers a
|
|
313
|
+
// stored field meanwhile (`sectionRecordToFile`). uwx-format.md § A page section's fields.
|
|
313
314
|
if (Object.keys(params).length > 0) data.params = params
|
|
314
315
|
data.content = section.content // leaf insets in it as written — no `insets` field since 2026-09-27
|
|
315
316
|
setIf(data, 'fetch', section.fetch ?? undefined)
|
|
316
|
-
setIf(data, 'background', background)
|
|
317
|
-
setIf(data, 'theme_override',
|
|
317
|
+
setIf(data, 'background', params.background)
|
|
318
|
+
setIf(data, 'theme_override', params.theme)
|
|
318
319
|
return data
|
|
319
320
|
}
|
|
320
321
|
|