@uniweb/build 0.70.0 → 0.70.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.70.0",
3
+ "version": "0.70.1",
4
4
  "description": "Build tooling for the Uniweb Component Web Platform",
5
5
  "type": "module",
6
6
  "exports": {
@@ -57,11 +57,11 @@
57
57
  "js-yaml": "^4.1.0",
58
58
  "sharp": "^0.35.3",
59
59
  "yaml": "^2.5.0",
60
- "@uniweb/content-reader": "^1.2.7",
61
60
  "@uniweb/content-writer": "^0.3.4",
62
- "@uniweb/semantic-parser": "^1.4.1",
63
- "@uniweb/schemas": "^0.9.0",
64
61
  "@uniweb/projections": "^0.7.9",
62
+ "@uniweb/content-reader": "^1.2.7",
63
+ "@uniweb/schemas": "^0.9.0",
64
+ "@uniweb/semantic-parser": "^1.4.1",
65
65
  "@uniweb/theming": "^0.1.16"
66
66
  },
67
67
  "optionalDependencies": {
@@ -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.33.0",
78
- "@uniweb/runtime": "^0.28.0"
77
+ "@uniweb/runtime": "^0.28.0",
78
+ "@uniweb/core": "^0.33.0"
79
79
  },
80
80
  "peerDependenciesMeta": {
81
81
  "vite": {
@@ -749,8 +749,13 @@ function prettifySlug(slug) {
749
749
  *
750
750
  * A node with no sections of its own (a group/container node, a redirect stub)
751
751
  * has nothing to humanize from, so it shows the identifier the author actually
752
- * wrote — `v1` stays `v1`. A node with content falls back to its opening H1,
753
- * and only then to a prettified slug.
752
+ * wrote — `v1` stays `v1`. A node with content falls back to the opening H1 of
753
+ * its first section that publishes, and only then to a prettified slug.
754
+ *
755
+ * ⭐ "That publishes": a `hidden: true` section is a draft, so its heading never
756
+ * titles the page — in dev as in a published build, so the title does not change
757
+ * between them, and as a backend's publish does [2026-09-28]. A node whose sections
758
+ * are all hidden takes the prettified slug.
754
759
  *
755
760
  * The runtime has no fallback of its own: an empty title renders empty. The
756
761
  * fill belongs here, at the producer, because `title` also feeds `<title>`,
@@ -765,7 +770,8 @@ function prettifySlug(slug) {
765
770
  function resolveDisplayTitle(declaredTitle, segment, sections) {
766
771
  if (declaredTitle) return declaredTitle
767
772
  if (!sections?.length) return segment
768
- return extractH1(sections[0]?.content) || prettifySlug(segment)
773
+ const first = sections.find((section) => section?.hidden !== true)
774
+ return extractH1(first?.content) || prettifySlug(segment)
769
775
  }
770
776
 
771
777
  // ⭐ The rule now lives in `utils/numeric-prefix.js` — `records/folder.yml` needs the
@@ -957,7 +963,9 @@ async function processFileAsPage(filePath, fileName, siteRoot, parentRoute) {
957
963
  version: null,
958
964
  versionMeta: null,
959
965
  versionScope: null,
960
- hidden: false,
966
+ // The file IS the page, so its `hidden: true` drafts the page — left out of a published
967
+ // build, kept in dev — rather than leaving a page with no section (2026-09-28).
968
+ hidden: section.hidden === true,
961
969
  hideIn: [],
962
970
  layout: {},
963
971
  seo: {
@@ -2806,7 +2814,7 @@ export async function collectSiteContent(sitePath, options = {}) {
2806
2814
  // Dev keeps them so drafts stay previewable.
2807
2815
  pages: dropUnpublished ? dropUnpublishedPages(pages).map(withoutHiddenSections) : pages,
2808
2816
  // Layout area sets: { default: { header: page, footer: page, ... }, marketing: { ... } }
2809
- layouts: dropUnpublished ? mapLayoutAreas(layouts, withoutHiddenSections) : layouts,
2817
+ layouts: dropUnpublished ? withoutHiddenAreas(layouts) : layouts,
2810
2818
  // ⭐ THE SAME REACHABILITY AXIS, for the 404 slot. `hidden: true` means DRAFT —
2811
2819
  // "excluded from the published site" (`docs/reference/page-configuration.md`);
2812
2820
  // `hideIn: ['*']` is the control for "routed but in no nav".
@@ -2836,16 +2844,23 @@ function withoutHiddenSections(page) {
2836
2844
  return { ...page, sections: dropHiddenSections(page.sections) }
2837
2845
  }
2838
2846
 
2839
- // Apply `fn` to every area of every layout: `{ default: { header: page, … }, … }`.
2840
- function mapLayoutAreas(layouts, fn) {
2847
+ // Every layout's areas without their hidden sections — and without an area whose sections
2848
+ // were all hidden: drafting every section of an area drafts the area, so a layout gets no area
2849
+ // there rather than an empty one (a backend's publish does the same).
2850
+ function withoutHiddenAreas(layouts) {
2841
2851
  if (!layouts || typeof layouts !== 'object') return layouts
2842
2852
  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
- ])
2853
+ Object.entries(layouts).map(([name, areas]) => {
2854
+ if (!areas || typeof areas !== 'object') return [name, areas]
2855
+ const kept = {}
2856
+ for (const [area, page] of Object.entries(areas)) {
2857
+ const shown = withoutHiddenSections(page)
2858
+ const hadSections = Array.isArray(page?.sections) && page.sections.length > 0
2859
+ if (hadSections && shown.sections.length === 0) continue
2860
+ kept[area] = shown
2861
+ }
2862
+ return [name, kept]
2863
+ })
2849
2864
  )
2850
2865
  }
2851
2866
 
package/src/uwx/site.js CHANGED
@@ -307,15 +307,16 @@ function mapSectionData(section) {
307
307
  // ⭐ A draft section — the store's own `hidden`, which an editor also writes [2026-09-28].
308
308
  if (section.hidden === true) data.hidden = true
309
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.
310
+ // ⭐ `background` and `theme` ride IN `params` [Diego, 2026-09-28] — where the runtime reads
311
+ // them, and where a section stores them. ⛔ Not also as the `background` / `theme_override`
312
+ // fields, as build 0.69 sent them: every reader of a stored section reads `params` — a publish
313
+ // carries `params` whole, and the app reads a stored field first, then `params` — so nothing
314
+ // needs them, and a push that stops sending them lets a backend retire them without refusing
315
+ // it. A pull still prefers a stored field while one exists (`sectionRecordToFile`).
316
+ // uwx-format.md § A page section's fields, Order.
314
317
  if (Object.keys(params).length > 0) data.params = params
315
318
  data.content = section.content // leaf insets in it as written — no `insets` field since 2026-09-27
316
319
  setIf(data, 'fetch', section.fetch ?? undefined)
317
- setIf(data, 'background', params.background)
318
- setIf(data, 'theme_override', params.theme)
319
320
  return data
320
321
  }
321
322