@uniweb/build 1.10.5 → 1.10.7

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.10.5",
3
+ "version": "1.10.7",
4
4
  "description": "Build tooling for the Uniweb Component Web Platform",
5
5
  "type": "module",
6
6
  "exports": {
@@ -57,14 +57,14 @@
57
57
  "sharp": "^0.35.5",
58
58
  "yaml": "^2.5.0",
59
59
  "@uniweb/content-reader": "~1.5.7",
60
- "@uniweb/schemas": "~1.6.5",
61
60
  "@uniweb/semantic-parser": "~1.13.0",
62
- "@uniweb/projections": "~1.2.1",
61
+ "@uniweb/schemas": "~1.6.5",
62
+ "@uniweb/theming": "~1.2.1",
63
63
  "@uniweb/content-writer": "~1.3.1",
64
- "@uniweb/theming": "~1.2.1"
64
+ "@uniweb/projections": "~1.2.3"
65
65
  },
66
66
  "optionalDependencies": {
67
- "@uniweb/runtime": "~1.2.3"
67
+ "@uniweb/runtime": "~1.2.5"
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/runtime": "~1.2.3",
77
- "@uniweb/core": "~1.4.7"
76
+ "@uniweb/core": "~1.4.9",
77
+ "@uniweb/runtime": "~1.2.5"
78
78
  },
79
79
  "peerDependenciesMeta": {
80
80
  "vite": {
@@ -308,11 +308,14 @@ export async function forgetDeployYml(siteDir) {
308
308
  *
309
309
  * A target's record moves to `deploys:` when that target has none there; when it has, the
310
310
  * one in `deploys:` is newer, and the old is dropped. `autoSave: off` becomes
311
- * `saveDeploys: false`; any other value is the default, and goes.
311
+ * `saveDeploys: false`; any other value is the default, and goes. And `saveDeploys:` holding
312
+ * one of `autoSave:`'s values — the key renamed by hand, its value kept — is read the same way:
313
+ * `off` is `false`, `lastDeploy` the default.
312
314
  *
313
315
  * @param {string} siteDir
314
- * @returns {Promise<{ moved: string[], dropped: string[], saveDeploys: boolean|null } | null>}
315
- * null when the file has neither key, or there is no file
316
+ * @returns {Promise<{ moved: string[], dropped: string[], saveDeploys: boolean|null, renamed: string|null } | null>}
317
+ * `renamed` is the old value found under `saveDeploys:`; null when the file has none of
318
+ * these, or there is no file
316
319
  */
317
320
  export async function retireDeployYmlKeys(siteDir) {
318
321
  const path = join(siteDir, 'deploy.yml')
@@ -321,7 +324,9 @@ export async function retireDeployYmlKeys(siteDir) {
321
324
  if (doc.errors.length) throw new Error(`${path}: ${doc.errors[0].message}`)
322
325
  const old = doc.get('lastDeploy')
323
326
  const autoSave = doc.get('autoSave')
324
- if (old === undefined && autoSave === undefined) return null
327
+ const renamed = doc.get('saveDeploys')
328
+ const kept = renamed === 'off' || renamed === 'lastDeploy' ? renamed : undefined
329
+ if (old === undefined && autoSave === undefined && kept === undefined) return null
325
330
  const moved = []
326
331
  const dropped = []
327
332
  if (isMap(old)) {
@@ -338,6 +343,14 @@ export async function retireDeployYmlKeys(siteDir) {
338
343
  }
339
344
  doc.delete('lastDeploy')
340
345
  let saveDeploys = null
346
+ if (kept !== undefined) {
347
+ if (kept === 'off') {
348
+ doc.set('saveDeploys', false)
349
+ saveDeploys = false
350
+ } else {
351
+ doc.delete('saveDeploys')
352
+ }
353
+ }
341
354
  if (autoSave !== undefined) {
342
355
  if (String(autoSave) === 'off' && !doc.has('saveDeploys')) {
343
356
  doc.set('saveDeploys', false)
@@ -346,5 +359,5 @@ export async function retireDeployYmlKeys(siteDir) {
346
359
  doc.delete('autoSave')
347
360
  }
348
361
  await writeFile(path, String(doc))
349
- return { moved, dropped, saveDeploys }
362
+ return { moved, dropped, saveDeploys, renamed: kept ?? null }
350
363
  }
@@ -186,7 +186,15 @@ function validate(doc, path) {
186
186
  // now: two targets naming DIFFERENT backends are two sites, and two naming the SAME
187
187
  // backend are one site shipped twice. The reason is gone, so the rejection is too.
188
188
  if (doc.saveDeploys !== undefined && typeof doc.saveDeploys !== 'boolean') {
189
- throw new Error(`${path}: \`saveDeploys\` must be true or false.`)
189
+ // `lastDeploy` and `off` were the values of `autoSave:`, which `saveDeploys:` replaced: the
190
+ // key renamed by hand, its value kept (measured 2026-10-26 on a copied project).
191
+ const retired = doc.saveDeploys === 'lastDeploy' || doc.saveDeploys === 'off'
192
+ throw new Error(
193
+ `${path}: \`saveDeploys\` must be true or false` +
194
+ (retired
195
+ ? ` — \`${doc.saveDeploys}\` is a value of the retired \`autoSave:\`. \`uniweb doctor --fix deploy-yml-retired-key\` writes what it meant.`
196
+ : '.')
197
+ )
190
198
  }
191
199
  if (doc.default !== undefined && typeof doc.default !== 'string') {
192
200
  throw new Error(`${path}: \`default\` must be a string.`)
@@ -225,11 +225,16 @@ export function backfillBibFile(filePath, uuidBySlug) {
225
225
 
226
226
  // Unwrap a localized wire value `{ <locale>: v }` back to the authored bare value
227
227
  // (the source locale, else the first present). Non-localized values pass through.
228
+ // ⛔ One with no locale at all, `{}`, is no value: undefined, and the field is not written.
229
+ // Until 2026-10-26 it passed through as the empty map, so a record's emptied caption was
230
+ // written `caption: {}` — an object where its schema says string, which `uniweb validate`
231
+ // refuses, and a publish stopped on fifteen of them.
228
232
  export function unwrapLocalized(value, sourceLocale) {
229
233
  if (value && typeof value === 'object' && !Array.isArray(value)) {
230
234
  if (Object.prototype.hasOwnProperty.call(value, sourceLocale)) return value[sourceLocale]
231
235
  const keys = Object.keys(value)
232
236
  if (keys.length) return value[keys[0]]
237
+ return undefined
233
238
  }
234
239
  return value
235
240
  }
@@ -238,7 +243,8 @@ export function unwrapLocalized(value, sourceLocale) {
238
243
  // field element-wise. An array unwraps each element; a bare localized map unwraps to
239
244
  // one value. The container shape is preserved (mirrors how the producer built it).
240
245
  export function unwrapLocalizedList(value, sourceLocale) {
241
- if (Array.isArray(value)) return value.map((v) => unwrapLocalized(v, sourceLocale))
246
+ // An element with no locale at all is no element (`unwrapLocalized`).
247
+ if (Array.isArray(value)) return value.map((v) => unwrapLocalized(v, sourceLocale)).filter((v) => v !== undefined)
242
248
  return unwrapLocalized(value, sourceLocale)
243
249
  }
244
250
 
@@ -347,7 +353,8 @@ function decodeRecord(fields, value, dec, sectionName) {
347
353
  const isBody = Boolean(dec.bodyAt) && sectionName === dec.bodyAt.section && key === dec.bodyAt.key
348
354
  const decoded = decodeLeaf(raw, field, dec, isBody)
349
355
  if (isBody) dec.body = typeof decoded === 'string' ? decoded : String(decoded ?? '')
350
- else out[key] = decoded
356
+ // A translatable value with no text in any language is no value: the field is not written.
357
+ else if (decoded !== undefined) out[key] = decoded
351
358
  }
352
359
  return out
353
360
  }