@uniweb/build 1.3.3 → 1.3.4
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": "1.3.
|
|
3
|
+
"version": "1.3.4",
|
|
4
4
|
"description": "Build tooling for the Uniweb Component Web Platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -56,12 +56,12 @@
|
|
|
56
56
|
"js-yaml": "^4.1.0",
|
|
57
57
|
"sharp": "^0.35.3",
|
|
58
58
|
"yaml": "^2.5.0",
|
|
59
|
-
"@uniweb/
|
|
60
|
-
"@uniweb/theming": "~1.2.0",
|
|
61
|
-
"@uniweb/semantic-parser": "~1.8.0",
|
|
59
|
+
"@uniweb/content-reader": "~1.3.2",
|
|
62
60
|
"@uniweb/content-writer": "~1.1.2",
|
|
61
|
+
"@uniweb/semantic-parser": "~1.8.0",
|
|
63
62
|
"@uniweb/schemas": "~1.3.0",
|
|
64
|
-
"@uniweb/
|
|
63
|
+
"@uniweb/projections": "~1.1.1",
|
|
64
|
+
"@uniweb/theming": "~1.2.0"
|
|
65
65
|
},
|
|
66
66
|
"optionalDependencies": {
|
|
67
67
|
"@uniweb/runtime": "~1.1.3"
|
|
@@ -1321,7 +1321,9 @@ function normalizeLayoutConfig(raw) {
|
|
|
1321
1321
|
const out = {}
|
|
1322
1322
|
if (typeof raw.name === 'string' && raw.name) out.name = raw.name
|
|
1323
1323
|
if (Array.isArray(raw.hide)) out.hide = raw.hide
|
|
1324
|
-
|
|
1324
|
+
// A map, never an array: `params: []` passed a bare `typeof` check and replaced the inherited
|
|
1325
|
+
// params with a list nothing reads.
|
|
1326
|
+
if (raw.params && typeof raw.params === 'object' && !Array.isArray(raw.params)) out.params = raw.params
|
|
1325
1327
|
return out
|
|
1326
1328
|
}
|
|
1327
1329
|
|
|
@@ -23,27 +23,23 @@ import { renderEntityDocument } from './backfill.js'
|
|
|
23
23
|
import { queriesYmlPath } from './queries-config.js'
|
|
24
24
|
import { folderYmlPath } from '../site/records-config.js'
|
|
25
25
|
import { editYamlText, YAML_DUMP_OPTS } from './yaml-edit.js'
|
|
26
|
-
import {
|
|
26
|
+
import { isSectionKey } from '@uniweb/schemas/section'
|
|
27
|
+
import { DECLARATION_KEYS, authorableDeclaration } from '../site/fetch-shapes.js'
|
|
27
28
|
|
|
28
|
-
// Frontmatter keys
|
|
29
|
-
// authoring, not to externally-editable params. On a section write an existing
|
|
30
|
-
// reserved key is preserved and never overwritten by incoming params, so a
|
|
31
|
-
// projection doesn't churn fields it didn't author (the surgical-update bar).
|
|
29
|
+
// Frontmatter keys a section write keeps from the file when the file has them.
|
|
32
30
|
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
31
|
+
// ⛔ Not `type`, `query`, `fetch` or the retired `data`, kept here as the developer's own from
|
|
32
|
+
// the start (2026-05-30) until 2026-10-16. A pull is a checkout: a teammate's push changes
|
|
33
|
+
// them, and an editor changes a section's data declaration. A pull that kept the file's
|
|
34
|
+
// value still recorded the backend's version as taken, so the next edit to the section
|
|
35
|
+
// pushed the file's value back over the backend's, with no refusal (measured with a
|
|
36
|
+
// section's `fetch:`). The declaration is written by `writeSectionFile`'s `declaration`.
|
|
37
|
+
// ⛔ Not `hidden` either: an editor toggles it, so a pull must carry the store's value into
|
|
38
|
+
// the file (2026-09-28).
|
|
37
39
|
export const DEFAULT_RESERVED_FRONTMATTER = new Set([
|
|
38
|
-
'type',
|
|
39
40
|
'preset',
|
|
40
41
|
'input',
|
|
41
|
-
'query',
|
|
42
|
-
'fetch',
|
|
43
|
-
'data',
|
|
44
42
|
'nest',
|
|
45
|
-
// ⛔ Not `hidden`, which was here from the start (2026-05-30) with no reason recorded: an
|
|
46
|
-
// editor toggles it, so a pull must carry the store's value into the file (2026-09-28).
|
|
47
43
|
])
|
|
48
44
|
|
|
49
45
|
/**
|
|
@@ -154,22 +150,30 @@ function assembleSection(frontmatter, body) {
|
|
|
154
150
|
}
|
|
155
151
|
|
|
156
152
|
/**
|
|
157
|
-
* Write a section's `.md` file:
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
* existing frontmatter/body to preserve. When `content` is omitted, the existing
|
|
153
|
+
* Write a section's `.md` file as the backend holds the section: its `params` and its
|
|
154
|
+
* data `declaration` replace the file's, `content` (ProseMirror) becomes the markdown
|
|
155
|
+
* body, and the file is written only when that changes it. When the file is new there
|
|
156
|
+
* is no existing frontmatter/body to preserve. When `content` is omitted, the existing
|
|
161
157
|
* body is kept (a params-only update).
|
|
162
158
|
*
|
|
159
|
+
* ⭐ `params` is the section's whole set: a param the file holds and `params` does not —
|
|
160
|
+
* one a push would send (`isSectionKey`, the collector's own test) — is removed. ⛔ Until
|
|
161
|
+
* 2026-10-16 incoming params were merged into the file's, so a param removed elsewhere
|
|
162
|
+
* stayed in the file, and the next edit to the section pushed it back.
|
|
163
|
+
*
|
|
163
164
|
* @param {object} opts
|
|
164
165
|
* @param {string} opts.filePath
|
|
165
166
|
* @param {object} [opts.content] - ProseMirror document for the body
|
|
166
|
-
* @param {object} [opts.params] -
|
|
167
|
+
* @param {object} [opts.params] - the section's frontmatter params as the backend holds them
|
|
168
|
+
* @param {object|object[]|null} [opts.declaration] - the section's `fetch` off the wire, written
|
|
169
|
+
* under the declaration key the file uses (`authorableDeclaration`); `null` when the
|
|
170
|
+
* section declares none, which removes the file's. Left out, the file's declaration stays
|
|
167
171
|
* @param {Set<string>} [opts.reserved] - keys preserved when already present
|
|
168
172
|
* @param {object} [opts.implied] - `{ key: value }` a file means by leaving `key` out; an incoming
|
|
169
173
|
* value equal to it is not written into an existing file that leaves the key out
|
|
170
174
|
* @returns {'updated'|'unchanged'}
|
|
171
175
|
*/
|
|
172
|
-
export function writeSectionFile({ filePath, content, params, reserved = DEFAULT_RESERVED_FRONTMATTER, implied = null }) {
|
|
176
|
+
export function writeSectionFile({ filePath, content, params, declaration, reserved = DEFAULT_RESERVED_FRONTMATTER, implied = null }) {
|
|
173
177
|
let existing = ''
|
|
174
178
|
try {
|
|
175
179
|
existing = readFileSync(filePath, 'utf8')
|
|
@@ -179,21 +183,29 @@ export function writeSectionFile({ filePath, content, params, reserved = DEFAULT
|
|
|
179
183
|
const { frontmatter, body: existingBody } = parseFrontmatter(existing, filePath)
|
|
180
184
|
|
|
181
185
|
const nextFrontmatter = { ...frontmatter }
|
|
182
|
-
const declaresLocally = DECLARATION_KEYS.some((k) => k in frontmatter)
|
|
183
186
|
if (params) {
|
|
184
187
|
for (const [key, value] of Object.entries(params)) {
|
|
185
|
-
// A reserved key is preserved only when it already exists locally
|
|
186
|
-
//
|
|
187
|
-
// there is nothing to protect, so the incoming value fills it — that's
|
|
188
|
-
// how a newly-projected section gets its `type`/`nest`/etc.
|
|
188
|
+
// A reserved key is preserved only when it already exists locally. On a new file there is
|
|
189
|
+
// nothing to protect, so the incoming value fills it.
|
|
189
190
|
if (reserved.has(key) && key in frontmatter) continue
|
|
190
|
-
if (reserved.has(key) && DECLARATION_KEYS.includes(key) && declaresLocally) continue
|
|
191
191
|
// A value the file implies by leaving the key out (`implied`) is not written into a file that
|
|
192
192
|
// exists — a new file has nothing to imply it with.
|
|
193
193
|
if (implied && existing && !(key in frontmatter) && implied[key] === value) continue
|
|
194
194
|
if (value === null || value === undefined) delete nextFrontmatter[key]
|
|
195
195
|
else nextFrontmatter[key] = value
|
|
196
196
|
}
|
|
197
|
+
for (const key of Object.keys(frontmatter)) {
|
|
198
|
+
if (key in params || reserved.has(key) || key === 'fetch' || isSectionKey(key)) continue
|
|
199
|
+
delete nextFrontmatter[key]
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// ⭐ The declaration keys — `query`, `fetch`, and the retired `data` — are one group: exactly one
|
|
203
|
+
// is written, under the key the file already uses where it can say the declaration, and the
|
|
204
|
+
// others are removed (a file holding two is refused by the build).
|
|
205
|
+
if (declaration !== undefined) {
|
|
206
|
+
const incoming = declaration === null ? null : authorableDeclaration(declaration, frontmatter)
|
|
207
|
+
for (const key of DECLARATION_KEYS) if (key !== incoming?.key) delete nextFrontmatter[key]
|
|
208
|
+
if (incoming) nextFrontmatter[incoming.key] = incoming.value
|
|
197
209
|
}
|
|
198
210
|
|
|
199
211
|
// ⭐ AN UNCHANGED BODY KEEPS THE AUTHOR'S OWN TEXT. A body that parses to the document the pull brought
|
package/src/uwx/site-project.js
CHANGED
|
@@ -405,20 +405,23 @@ export function sectionRecordToFile({ filePath, record, sourceLocale = LOCALIZED
|
|
|
405
405
|
// `params`, so one stored there counts when the field is absent.
|
|
406
406
|
frontmatter.hidden = (typeof hidden === 'boolean' ? hidden : paramHidden === true) ? true : undefined
|
|
407
407
|
Object.assign(frontmatter, paramsRest)
|
|
408
|
-
// Invert the build's resolution rather than copy it — see fetch-shapes.js. A new
|
|
409
|
-
// file gets `query:` for a declaration of names alone; a section that declares
|
|
410
|
-
// its data locally keeps it (`writeSectionFile`, the declaration keys).
|
|
411
|
-
// ⭐ From `params.fetch` — where a section carries its data since 2026-09-28.
|
|
412
|
-
if (paramFetch !== undefined) {
|
|
413
|
-
const { key, value } = authorableDeclaration(paramFetch)
|
|
414
|
-
frontmatter[key] = value
|
|
415
|
-
}
|
|
416
408
|
// `id:` only where the file's name does not already give it (`writeId`, `pageSectionsToFiles`).
|
|
417
409
|
if (stable_id !== undefined && writeId) frontmatter.id = stable_id
|
|
418
410
|
|
|
411
|
+
// ⭐ The data declaration, from `params.fetch` — where a section carries its data since
|
|
412
|
+
// 2026-09-28 — is the backend's: written under the key the file already uses, else `query:`
|
|
413
|
+
// for names alone (`authorableDeclaration`, the inverse of the build's resolution), and
|
|
414
|
+
// removed from the file of a section that declares none. ⛔ Until 2026-10-16 a file's own
|
|
415
|
+
// declaration was kept over the backend's, and the next edit to the section pushed it back.
|
|
419
416
|
// ⛔ Not `type: Content` into a file that names no type: it is what the push sent for it, and
|
|
420
417
|
// absent, the foundation's `defaultSection` renders it. Until 2026-09-26 a pull wrote it back.
|
|
421
|
-
return writeSectionFile({
|
|
418
|
+
return writeSectionFile({
|
|
419
|
+
filePath,
|
|
420
|
+
content: sourceContent,
|
|
421
|
+
params: frontmatter,
|
|
422
|
+
declaration: paramFetch ?? null,
|
|
423
|
+
implied: { type: FILLED_SECTION_TYPE },
|
|
424
|
+
})
|
|
422
425
|
}
|
|
423
426
|
|
|
424
427
|
// ---------------------------------------------------------------------------
|