@uniweb/build 0.14.14 → 0.14.16

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.14.14",
3
+ "version": "0.14.16",
4
4
  "description": "Build tooling for the Uniweb Component Web Platform",
5
5
  "type": "module",
6
6
  "exports": {
@@ -64,7 +64,7 @@
64
64
  },
65
65
  "optionalDependencies": {
66
66
  "@uniweb/content-reader": "1.1.12",
67
- "@uniweb/runtime": "0.8.18",
67
+ "@uniweb/runtime": "0.8.20",
68
68
  "@uniweb/schemas": "0.2.3"
69
69
  },
70
70
  "peerDependencies": {
@@ -74,7 +74,7 @@
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.7.13"
77
+ "@uniweb/core": "0.7.14"
78
78
  },
79
79
  "peerDependenciesMeta": {
80
80
  "vite": {
@@ -1269,6 +1269,9 @@ async function processPage(pagePath, pageName, siteRoot, { isIndex = false, pare
1269
1269
  seo: {
1270
1270
  noindex: seo.noindex || false,
1271
1271
  image: seo.image || null,
1272
+ ogTitle: seo.ogTitle || null,
1273
+ ogDescription: seo.ogDescription || null,
1274
+ canonical: seo.canonical || null,
1272
1275
  changefreq: seo.changefreq || null,
1273
1276
  priority: seo.priority || null
1274
1277
  },
@@ -100,6 +100,7 @@ const INFO_TO_SITE_YML = {
100
100
  paths: 'paths',
101
101
  data: 'data',
102
102
  template: 'template',
103
+ seo: 'seo',
103
104
  }
104
105
 
105
106
  /**
@@ -128,7 +129,13 @@ export function siteInfoToConfig({ document, siteRoot, sourceLocale = LOCALIZED_
128
129
  const description = unwrapLocalized(info.description, sourceLocale)
129
130
  if (description !== undefined) siteChanges.description = description
130
131
 
131
- // Verbatim fields.
132
+ // `keywords` is a localized list (mirrors page keywords) → unwrap to the
133
+ // source locale; the target locales are captured into the locales/ collector.
134
+ if (Array.isArray(info.keywords)) info.keywords.forEach((kw) => collector?.add(kw))
135
+ const keywords = unwrapLocalizedList(info.keywords, sourceLocale)
136
+ if (keywords !== undefined) siteChanges.keywords = keywords
137
+
138
+ // Verbatim fields (includes `seo` — the site-level social/SEO block).
132
139
  for (const [infoKey, ymlKey] of Object.entries(INFO_TO_SITE_YML)) {
133
140
  if (info[infoKey] !== undefined) siteChanges[ymlKey] = info[infoKey]
134
141
  }
package/src/uwx/site.js CHANGED
@@ -558,6 +558,13 @@ export async function siteProjectToDocument(siteRoot, opts = {}) {
558
558
  // refs). `assets` is a build-DERIVED upload manifest, not authored config, so it
559
559
  // is never produced from / projected to the site files.
560
560
  setIf(info, 'favicon', siteYml.favicon)
561
+ // Site-level SEO/social metadata — the same shape as page.yml's `seo:` + the
562
+ // top-level `keywords`, hoisted to the site root so the homepage social card
563
+ // and default keywords exist for any share/SSR/crawler. `seo` rides verbatim
564
+ // as authored config (round-trips like favicon); `keywords` is a localized
565
+ // list (like page keywords).
566
+ setIf(info, 'seo', siteYml.seo)
567
+ setIf(info, 'keywords', localizeScalarList(siteYml.keywords, sourceLocale, translations))
561
568
  setIf(info, 'head_html', headHtml)
562
569
  setIf(info, 'fetcher', siteYml.fetcher)
563
570
  setIf(info, 'build', siteYml.build)
package/src/uwx/zip.js CHANGED
@@ -1,11 +1,12 @@
1
1
  // Minimal, zero-dependency ZIP writer/reader for .uwx containers.
2
2
  //
3
- // DESIGN DECISION: Stored only (compression method 0), no Deflate. A .uwx
4
- // container is a ZIP; compression is an optimization, not part of the
5
- // contract, and every standard ZIP reader handles Stored entries. Staying
6
- // Stored-only removes a class of cross-tool byte asymmetry and needs no
7
- // zlib. If package size ever matters, a Deflate path can be added later via
8
- // Node's built-in `zlib.deflateRawSync` without changing callers.
3
+ // DESIGN DECISION: our WRITER (`createZip`) emits Stored only (compression
4
+ // method 0), no Deflate. A .uwx container is a ZIP; compression is an
5
+ // optimization, not part of the contract, and every standard ZIP reader
6
+ // handles Stored entries. Staying Stored-only on write removes a class of
7
+ // cross-tool byte asymmetry. The READER (`readZip`) additionally inflates
8
+ // Deflate (method 8) entries — the backend's pull `.uwx` Deflates larger
9
+ // entities, and the framework must read what the backend produces.
9
10
  //
10
11
  // No Zip64: per-record JSON files are far below 4 GiB and entry counts far
11
12
  // below 65535. The format is otherwise the classic APPNOTE layout, all
@@ -17,6 +18,7 @@
17
18
  // makes byte output reproducible for a given input.
18
19
 
19
20
  import { crc32 } from './crc32.js'
21
+ import { inflateRawSync } from 'node:zlib'
20
22
 
21
23
  const LOCAL_SIG = 0x04034b50
22
24
  const CENTRAL_SIG = 0x02014b50
@@ -89,11 +91,12 @@ export function createZip(files) {
89
91
  }
90
92
 
91
93
  /**
92
- * Reader for our own Stored archivesused by tests and by inspection /
93
- * `--dry-run`. Not a general-purpose unzip.
94
+ * Reader for `.uwx` containers. Handles Stored (method 0 what our writer emits)
95
+ * and Deflate (method 8 — what the backend's pull `.uwx` uses); any other method
96
+ * throws. Not otherwise a general-purpose unzip (no Zip64, no encryption).
94
97
  *
95
98
  * @param {Buffer} buf
96
- * @returns {Map<string, Buffer>} name -> data
99
+ * @returns {Map<string, Buffer>} name -> uncompressed data
97
100
  */
98
101
  export function readZip(buf) {
99
102
  const out = new Map()
@@ -115,6 +118,7 @@ export function readZip(buf) {
115
118
  if (buf.readUInt32LE(p) !== CENTRAL_SIG) {
116
119
  throw new Error('uwx/zip: bad central directory signature')
117
120
  }
121
+ const method = buf.readUInt16LE(p + 10)
118
122
  const compSize = buf.readUInt32LE(p + 20)
119
123
  const nameLen = buf.readUInt16LE(p + 28)
120
124
  const extraLen = buf.readUInt16LE(p + 30)
@@ -127,7 +131,11 @@ export function readZip(buf) {
127
131
  const lNameLen = buf.readUInt16LE(localOff + 26)
128
132
  const lExtraLen = buf.readUInt16LE(localOff + 28)
129
133
  const dataStart = localOff + 30 + lNameLen + lExtraLen
130
- out.set(name, buf.subarray(dataStart, dataStart + compSize))
134
+ const raw = buf.subarray(dataStart, dataStart + compSize)
135
+ // Stored (0) → verbatim; Deflate (8) → inflate the raw deflate stream.
136
+ if (method === 0) out.set(name, raw)
137
+ else if (method === 8) out.set(name, inflateRawSync(raw))
138
+ else throw new Error(`uwx/zip: unsupported compression method ${method} for ${name}`)
131
139
 
132
140
  p += 46 + nameLen + extraLen + commentLen
133
141
  }