uniweb 0.56.8 → 0.56.9

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",
3
- "version": "0.56.8",
3
+ "version": "0.56.9",
4
4
  "description": "Create structured Vite + React sites with content/code separation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -42,11 +42,11 @@
42
42
  "prompts": "^2.4.2",
43
43
  "tar": "^7.0.0",
44
44
  "@uniweb/core": "^0.29.4",
45
- "@uniweb/kit": "^0.19.3",
46
- "@uniweb/schemas": "^0.3.1",
47
45
  "@uniweb/runtime": "^0.26.4",
48
46
  "@uniweb/semantic-parser": "^1.4.1",
49
- "@uniweb/content-writer": "^0.3.4"
47
+ "@uniweb/schemas": "^0.3.2",
48
+ "@uniweb/content-writer": "^0.3.4",
49
+ "@uniweb/kit": "^0.19.3"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "@uniweb/build": "^0.52.6",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-09-16T23:53:23.238Z",
3
+ "generatedAt": "2026-09-17T03:24:18.741Z",
4
4
  "packages": {
5
5
  "@uniweb/api": {
6
6
  "version": "0.3.6",
@@ -96,7 +96,7 @@
96
96
  "deps": []
97
97
  },
98
98
  "@uniweb/schemas": {
99
- "version": "0.3.1",
99
+ "version": "0.3.2",
100
100
  "path": "framework/schemas",
101
101
  "deps": []
102
102
  },
@@ -111,12 +111,12 @@
111
111
  "deps": []
112
112
  },
113
113
  "@uniweb/snapshot": {
114
- "version": "0.1.0",
114
+ "version": "0.1.1",
115
115
  "path": "framework/snapshot",
116
116
  "deps": []
117
117
  },
118
118
  "@uniweb/templates": {
119
- "version": "0.14.6",
119
+ "version": "0.14.7",
120
120
  "path": "framework/templates",
121
121
  "deps": []
122
122
  },
@@ -178,10 +178,22 @@ export async function applyContent(
178
178
  '.gitignore'
179
179
  ])
180
180
 
181
- // Config files that should be merged, not overwritten.
182
- // Keys listed here are preserved from the scaffolded version.
181
+ // Config files that should be merged, not overwritten: for each listed
182
+ // key, when the scaffolded version's value is used.
183
+ //
184
+ // foundation ALWAYS — the CLI resolved it for this project, and a
185
+ // content template cannot know it.
186
+ // name ONLY WHEN THE TEMPLATE SETS NONE. A template's own name is
187
+ // the default name of a site made from it — the same thing
188
+ // cloning a template in an app gives — so a literal
189
+ // `name: Product Launch` survives, and the project name fills
190
+ // in for a template with no name, or only the
191
+ // `{{projectName}}` placeholder.
183
192
  const MERGE_FILES = {
184
- 'site.yml': ['name', 'foundation']
193
+ 'site.yml': [
194
+ ['name', 'when-template-has-none'],
195
+ ['foundation', 'always']
196
+ ]
185
197
  }
186
198
 
187
199
  await copyContentRecursive(
@@ -267,8 +279,9 @@ async function copyContentRecursive(
267
279
  // educational structure of the content template survive) and
268
280
  // override only the specific top-level keys listed in
269
281
  // preserveKeys with the values from the already-scaffolded base
270
- // file (so the user's chosen project name and foundation ref
271
- // don't get replaced by whatever the content template hardcoded).
282
+ // file (so the resolved foundation ref is never replaced by whatever
283
+ // the content template hardcoded, and a project name fills in for a
284
+ // template that names nothing).
272
285
  //
273
286
  // Earlier versions of this code parsed both files through
274
287
  // js-yaml, merged the objects, and re-emitted the result via
@@ -283,17 +296,21 @@ async function copyContentRecursive(
283
296
  const existing = yaml.load(existingContent) || {}
284
297
  let merged = newContent ?? (await fs.readFile(sourcePath, 'utf-8'))
285
298
 
286
- for (const key of preserveKeys) {
299
+ for (const [key, when] of preserveKeys) {
287
300
  if (existing[key] === undefined) continue
288
301
  const baseLine = matchTopLevelLine(existingContent, key)
289
302
  if (!baseLine) continue
290
303
  // If the new content carries the key, replace its line with
291
- // the scaffolded value (preserving the user's project/foundation
292
- // choice). Otherwise insert the line — older content templates
293
- // (notably `docs/site/site.yml.hbs`) omit `foundation:` entirely,
294
- // and dropping it leaves the site without a foundation ref so
295
- // the entry's `import '#foundation/styles'` fails at build time.
304
+ // the scaffolded value unless the key only fills in, and the
305
+ // template set a value of its own. Otherwise insert the line —
306
+ // older content templates (notably `docs/site/site.yml.hbs`) omit
307
+ // `foundation:` entirely, and dropping it leaves the site without a
308
+ // foundation ref so the entry's `import '#foundation/styles'` fails
309
+ // at build time.
296
310
  if (matchTopLevelLine(merged, key)) {
311
+ if (when === 'when-template-has-none' && hasOwnValue(merged, key)) {
312
+ continue
313
+ }
297
314
  merged = replaceTopLevelLine(merged, key, baseLine)
298
315
  } else {
299
316
  merged = insertTopLevelLine(merged, baseLine)
@@ -358,6 +375,22 @@ function escapeRegex(s) {
358
375
  return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
359
376
  }
360
377
 
378
+ /**
379
+ * Whether a content template sets `key` to a value of its own: a non-empty
380
+ * string that is not an unrendered `{{placeholder}}` (a plain `site.yml`, not
381
+ * `.hbs`, is copied without rendering). Content that does not parse counts as
382
+ * setting nothing, so the scaffolded value fills in, as it always did.
383
+ */
384
+ function hasOwnValue(content, key) {
385
+ let value
386
+ try {
387
+ value = (yaml.load(content) || {})[key]
388
+ } catch {
389
+ return false
390
+ }
391
+ return typeof value === 'string' && value.trim() !== '' && !value.includes('{{')
392
+ }
393
+
361
394
  /**
362
395
  * Find the verbatim text of a single-line top-level YAML entry like
363
396
  * `name: foo bar` or `foundation: my-foundation`. Returns the matched