uniweb 0.12.19 → 0.12.21

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/README.md CHANGED
@@ -351,7 +351,7 @@ A future version will let markdown in a git repo and content in the Uniweb apps
351
351
  | `uniweb export` | Produce a self-contained `dist/` for any static host. You upload it yourself. `--host=<adapter>` adds host-specific helper files. |
352
352
  | `uniweb publish @org/name` | Publish a foundation to the registry (path 2). |
353
353
  | `uniweb build` | Inspect a build locally. For shipping, use `deploy` or `export`. |
354
- | `uniweb update` | Reconcile workspace state with the CLI: self-update the global install, align `@uniweb/*` deps in every `package.json` to the CLI's matrix, refresh `AGENTS.md`. Refuses to outpace declared deps with a stale doc. |
354
+ | `uniweb update` | Align this project with the CLI you're running: bump `@uniweb/*` deps in every `package.json` to the CLI's matrix (then install), and refresh `AGENTS.md`. Pins to *this* CLI's matrix run `npx uniweb@latest update` to align to the latest release. Updating the CLI itself is your package manager's job (`npm i -g uniweb@latest`). |
355
355
 
356
356
  `--host=<adapter>` is the same option across `deploy`, `export`, and `add ci`. Built-in adapters: `cloudflare-pages`, `netlify`, `github-pages`, `vercel`, `s3-cloudfront`, `generic-static`. Each adapter implements only the operations it supports — `add ci` is currently `github-pages`-only because it's the only one that needs a workflow file in the repo.
357
357
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uniweb",
3
- "version": "0.12.19",
3
+ "version": "0.12.21",
4
4
  "description": "Create structured Vite + React sites with content/code separation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -41,13 +41,13 @@
41
41
  "js-yaml": "^4.1.0",
42
42
  "prompts": "^2.4.2",
43
43
  "tar": "^7.0.0",
44
+ "@uniweb/runtime": "0.8.14",
44
45
  "@uniweb/core": "0.7.11",
45
- "@uniweb/kit": "0.9.11",
46
- "@uniweb/runtime": "0.8.13"
46
+ "@uniweb/kit": "0.9.13"
47
47
  },
48
48
  "peerDependencies": {
49
- "@uniweb/build": "0.14.4",
50
- "@uniweb/content-reader": "1.1.10",
49
+ "@uniweb/build": "0.14.5",
50
+ "@uniweb/content-reader": "1.1.11",
51
51
  "@uniweb/semantic-parser": "1.1.17"
52
52
  },
53
53
  "peerDependenciesMeta": {
@@ -10,6 +10,8 @@ import { loadDeployYml } from '@uniweb/build/site'
10
10
  import { listAdapters } from '@uniweb/build/hosts'
11
11
  import { getCliVersion } from '../versions.js'
12
12
  import { readAgentsVersion } from '../utils/agents-stamp.js'
13
+ import { writeJsonPreservingStyle } from '../utils/json-file.js'
14
+ import { surveyWorkspaceDeps } from '../utils/dep-survey.js'
13
15
  import { discoverFoundations, discoverSites } from '../utils/discover.js'
14
16
  import { findWorkspaceRoot } from '../utils/workspace.js'
15
17
 
@@ -204,9 +206,10 @@ export async function doctor(args = []) {
204
206
  const union = Array.from(new Set([...ymlPackages, ...pkgWorkspaces])).sort()
205
207
  writeFileSync(ymlPath, yaml.dump({ packages: union }, { flowLevel: -1, quotingType: '"' }))
206
208
  const rootPkgPath = join(workspaceDir, 'package.json')
207
- const rootPkg = JSON.parse(readFileSync(rootPkgPath, 'utf-8'))
209
+ const rootPkgSrc = readFileSync(rootPkgPath, 'utf-8')
210
+ const rootPkg = JSON.parse(rootPkgSrc)
208
211
  rootPkg.workspaces = union
209
- writeFileSync(rootPkgPath, JSON.stringify(rootPkg, null, 2) + '\n')
212
+ writeJsonPreservingStyle(rootPkgPath, rootPkg, rootPkgSrc)
210
213
  issue.fixed = true
211
214
  fixed(`wrote union [${union.join(', ')}] to both manifests`)
212
215
  } else {
@@ -364,7 +367,7 @@ export async function doctor(args = []) {
364
367
  const sitePkgPath = join(sitePath, 'package.json')
365
368
  const updatedPkg = { ...sitePkg }
366
369
  updatedPkg.dependencies = { ...(updatedPkg.dependencies || {}), [foundationName]: expectedPath }
367
- writeFileSync(sitePkgPath, JSON.stringify(updatedPkg, null, 2) + '\n')
370
+ writeJsonPreservingStyle(sitePkgPath, updatedPkg)
368
371
  issue.fixed = true
369
372
  fixed(`added "${foundationName}": "${expectedPath}" to ${relative(workspaceDir, sitePkgPath)}`)
370
373
  } else {
@@ -391,7 +394,7 @@ export async function doctor(args = []) {
391
394
  const sitePkgPath = join(sitePath, 'package.json')
392
395
  const updatedPkg = { ...sitePkg }
393
396
  updatedPkg.dependencies = { ...(updatedPkg.dependencies || {}), [foundationName]: expectedPath }
394
- writeFileSync(sitePkgPath, JSON.stringify(updatedPkg, null, 2) + '\n')
397
+ writeJsonPreservingStyle(sitePkgPath, updatedPkg)
395
398
  issue.fixed = true
396
399
  fixed(`updated "${foundationName}" to "${expectedPath}" in ${relative(workspaceDir, sitePkgPath)}`)
397
400
  } else {
@@ -672,11 +675,27 @@ export async function doctor(args = []) {
672
675
  }
673
676
  }
674
677
 
678
+ const cliVersion = getCliVersion()
679
+
680
+ // Check @uniweb/* dep alignment with the running CLI
681
+ log('')
682
+ const depSurvey = await surveyWorkspaceDeps(workspaceDir)
683
+ const behindDeps = depSurvey.rows.filter(r => r.status === 'behind')
684
+ if (behindDeps.length > 0) {
685
+ const names = [...new Set(behindDeps.map(r => r.name))].sort()
686
+ warn(`${behindDeps.length} workspace dep declaration${behindDeps.length === 1 ? '' : 's'} lag the CLI (v${cliVersion}): ${names.join(', ')}`)
687
+ info(`Run: uniweb update`)
688
+ issues.push({ type: 'warn', message: `${behindDeps.length} @uniweb/* dep declaration(s) behind CLI v${cliVersion}` })
689
+ } else if (depSurvey.anyAhead) {
690
+ success(`@uniweb/* deps are aligned or ahead of the CLI (v${cliVersion})`)
691
+ } else {
692
+ success(`@uniweb/* deps are aligned with the CLI (v${cliVersion})`)
693
+ }
694
+
675
695
  // Check AGENTS.md freshness
676
696
  log('')
677
697
  const agentsPath = join(workspaceDir, 'AGENTS.md')
678
698
  const agentsVersion = readAgentsVersion(agentsPath)
679
- const cliVersion = getCliVersion()
680
699
 
681
700
  if (!existsSync(agentsPath)) {
682
701
  warn('AGENTS.md not found')
@@ -34,7 +34,7 @@
34
34
  */
35
35
 
36
36
  import { existsSync } from 'node:fs'
37
- import { readFile, writeFile, mkdir } from 'node:fs/promises'
37
+ import { readFile, mkdir } from 'node:fs/promises'
38
38
  import { resolve, join } from 'node:path'
39
39
  import { execSync } from 'node:child_process'
40
40
 
@@ -42,6 +42,7 @@ import { resolveFoundationSrcPath, classifyPackage } from '@uniweb/build'
42
42
  import { createLocalRegistry, RemoteRegistry } from '../utils/registry.js'
43
43
  import { ensureAuth, readAuth, writeAuth, decodeJwtPayload } from '../utils/auth.js'
44
44
  import { getRegistryUrl, getBackendUrl } from '../utils/config.js'
45
+ import { writeJsonPreservingStyleAsync } from '../utils/json-file.js'
45
46
  import { findWorkspaceRoot, findFoundations, findSites, promptSelect } from '../utils/workspace.js'
46
47
  import { isNonInteractive, getCliPrefix } from '../utils/interactive.js'
47
48
 
@@ -225,7 +226,12 @@ export async function publish(args = []) {
225
226
  // intends the NEW one. Without rebuilding we'd ship inconsistent
226
227
  // bytes (schema says one version, registry record says another).
227
228
  const distDir = join(foundationDir, 'dist')
229
+ // @uniweb/build@0.14.0+ emits dist/entry.js (Phase 5 of CDN migration);
230
+ // older builds emitted dist/foundation.js. Accept either so a single CLI
231
+ // works against both old and new foundations during the rollout window.
232
+ const entryJs = join(distDir, 'entry.js')
228
233
  const foundationJs = join(distDir, 'foundation.js')
234
+ const hasMainArtifact = () => existsSync(entryJs) || existsSync(foundationJs)
229
235
  const schemaJson = join(distDir, 'meta', 'schema.json')
230
236
 
231
237
  // Pre-read package.json so we can compare its version against the
@@ -335,7 +341,7 @@ export async function publish(args = []) {
335
341
  }
336
342
  }
337
343
 
338
- let needsBuild = !existsSync(foundationJs) || !existsSync(schemaJson)
344
+ let needsBuild = !hasMainArtifact() || !existsSync(schemaJson)
339
345
  let buildReason = needsBuild ? 'no dist/ found' : null
340
346
 
341
347
  if (!needsBuild) {
@@ -446,8 +452,8 @@ export async function publish(args = []) {
446
452
  })
447
453
  console.log('')
448
454
 
449
- if (!existsSync(foundationJs) || !existsSync(schemaJson)) {
450
- error('Build did not produce dist/foundation.js and dist/meta/schema.json')
455
+ if (!hasMainArtifact() || !existsSync(schemaJson)) {
456
+ error('Build did not produce dist/entry.js (or legacy dist/foundation.js) and dist/meta/schema.json')
451
457
  process.exit(1)
452
458
  }
453
459
  }
@@ -676,7 +682,7 @@ export async function publish(args = []) {
676
682
  if (writeBackId) {
677
683
  pkg.uniweb = pkg.uniweb || {}
678
684
  pkg.uniweb.id = foundationName
679
- await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n')
685
+ await writeJsonPreservingStyleAsync(pkgPath, pkg)
680
686
  info(`Wrote ${colors.cyan}uniweb.id: "${foundationName}"${colors.reset} to ${colors.dim}package.json${colors.reset}`)
681
687
  }
682
688
 
@@ -846,7 +852,7 @@ export async function publish(args = []) {
846
852
  if (writeBackId) {
847
853
  pkg.uniweb = pkg.uniweb || {}
848
854
  pkg.uniweb.id = foundationName
849
- await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n')
855
+ await writeJsonPreservingStyleAsync(pkgPath, pkg)
850
856
  info(`Wrote ${colors.cyan}uniweb.id: "${foundationName}"${colors.reset} to ${colors.dim}package.json${colors.reset}`)
851
857
  }
852
858
  console.log('')
@@ -54,6 +54,7 @@ import {
54
54
  updateRootScripts,
55
55
  } from '../utils/config.js'
56
56
  import { discoverFoundations, discoverSites } from '../utils/discover.js'
57
+ import { writeJsonPreservingStyleAsync } from '../utils/json-file.js'
57
58
  import { getExistingPackageNames, validatePackageName } from '../utils/names.js'
58
59
  import { detectPackageManager, installCmd } from '../utils/pm.js'
59
60
  import { getCliPrefix } from '../utils/interactive.js'
@@ -184,9 +185,10 @@ async function validateRenameName(rootDir, newName) {
184
185
  * Rewrite the `name` field in a package.json file.
185
186
  */
186
187
  async function rewritePackageJsonName(pkgPath, newName) {
187
- const pkg = JSON.parse(await readFile(pkgPath, 'utf-8'))
188
+ const src = await readFile(pkgPath, 'utf-8')
189
+ const pkg = JSON.parse(src)
188
190
  pkg.name = newName
189
- await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n')
191
+ await writeJsonPreservingStyleAsync(pkgPath, pkg, src)
190
192
  }
191
193
 
192
194
  // ─── Foundation rename ───────────────────────────────────────────
@@ -234,11 +236,13 @@ async function renameFoundation(rootDir, oldName, newName, prefix) {
234
236
  for (const site of sites) {
235
237
  const sitePkgPath = join(rootDir, site.path, 'package.json')
236
238
  const siteYmlPath = join(rootDir, site.path, 'site.yml')
237
- let pkg, ymlData
239
+ let pkg, pkgSrc, ymlData
238
240
  try {
239
- pkg = JSON.parse(await readFile(sitePkgPath, 'utf-8'))
241
+ pkgSrc = await readFile(sitePkgPath, 'utf-8')
242
+ pkg = JSON.parse(pkgSrc)
240
243
  } catch {
241
244
  pkg = null
245
+ pkgSrc = null
242
246
  }
243
247
  try {
244
248
  ymlData = yaml.load(await readFile(siteYmlPath, 'utf-8')) || {}
@@ -252,6 +256,7 @@ async function renameFoundation(rootDir, oldName, newName, prefix) {
252
256
  path: site.path,
253
257
  name: site.name,
254
258
  pkg,
259
+ pkgSrc,
255
260
  sitePkgPath,
256
261
  siteYmlPath,
257
262
  ymlData,
@@ -295,7 +300,7 @@ async function renameFoundation(rootDir, oldName, newName, prefix) {
295
300
  s.pkg.dependencies[newName] = oldValue.startsWith('file:')
296
301
  ? `file:${newRel}`
297
302
  : oldValue // npm-pinned, leave it; rename-then-republish is out of scope.
298
- await writeFile(s.sitePkgPath, JSON.stringify(s.pkg, null, 2) + '\n')
303
+ await writeJsonPreservingStyleAsync(s.sitePkgPath, s.pkg, s.pkgSrc)
299
304
  }
300
305
  if (s.ymlMatches) {
301
306
  const newYmlData = { ...s.ymlData, foundation: newName }