uniweb 0.13.17 → 0.14.1
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 +1 -1
- package/package.json +7 -7
- package/partials/agents.md +51 -0
- package/src/backend/client.js +173 -40
- package/src/backend/data-bundle.js +15 -3
- package/src/backend/foundation-bring-along.js +76 -21
- package/src/backend/payment-handoff.js +28 -9
- package/src/backend/site-media.js +147 -19
- package/src/backend/site-sync.js +362 -40
- package/src/commands/add.js +575 -273
- package/src/commands/build.js +160 -57
- package/src/commands/clone.js +79 -26
- package/src/commands/content.js +11 -7
- package/src/commands/deploy.js +80 -32
- package/src/commands/dev.js +39 -17
- package/src/commands/docs.js +44 -16
- package/src/commands/doctor.js +338 -82
- package/src/commands/export.js +15 -7
- package/src/commands/handoff.js +6 -2
- package/src/commands/i18n.js +248 -99
- package/src/commands/inspect.js +48 -19
- package/src/commands/invite.js +9 -3
- package/src/commands/org.js +19 -5
- package/src/commands/publish.js +229 -60
- package/src/commands/pull.js +157 -48
- package/src/commands/push.js +144 -42
- package/src/commands/refresh.js +37 -10
- package/src/commands/register.js +235 -64
- package/src/commands/rename.js +126 -46
- package/src/commands/runtime.js +74 -24
- package/src/commands/status.js +48 -15
- package/src/commands/sync.js +7 -2
- package/src/commands/template.js +12 -4
- package/src/commands/update.js +263 -87
- package/src/commands/validate.js +115 -32
- package/src/framework-index.json +15 -13
- package/src/index.js +298 -145
- package/src/templates/fetchers/github.js +7 -7
- package/src/templates/fetchers/npm.js +3 -3
- package/src/templates/fetchers/release.js +21 -18
- package/src/templates/index.js +34 -12
- package/src/templates/processor.js +44 -12
- package/src/templates/resolver.js +42 -15
- package/src/templates/validator.js +14 -7
- package/src/utils/asset-upload.js +90 -22
- package/src/utils/code-upload.js +62 -37
- package/src/utils/config.js +31 -10
- package/src/utils/dep-survey.js +33 -7
- package/src/utils/destination-prompt.js +27 -17
- package/src/utils/git.js +66 -22
- package/src/utils/host-prompt.js +4 -3
- package/src/utils/install-integrity.js +8 -4
- package/src/utils/interactive.js +3 -3
- package/src/utils/json-file.js +6 -2
- package/src/utils/names.js +15 -6
- package/src/utils/placement.js +16 -8
- package/src/utils/registry-auth.js +185 -57
- package/src/utils/registry-orgs.js +142 -53
- package/src/utils/runtime-upload.js +52 -14
- package/src/utils/scaffold.js +55 -14
- package/src/utils/site-content-refs.js +3 -1
- package/src/utils/update-check.js +43 -17
- package/src/utils/workspace.js +13 -7
- package/src/versions.js +18 -7
- package/templates/site/_gitignore +5 -0
package/src/commands/update.js
CHANGED
|
@@ -45,13 +45,24 @@ import { spawn } from 'node:child_process'
|
|
|
45
45
|
import prompts from 'prompts'
|
|
46
46
|
|
|
47
47
|
import { findWorkspaceRoot } from '../utils/workspace.js'
|
|
48
|
-
import {
|
|
48
|
+
import {
|
|
49
|
+
readAgentsVersion,
|
|
50
|
+
generateAgentsContent
|
|
51
|
+
} from '../utils/agents-stamp.js'
|
|
49
52
|
import { getCliVersion } from '../versions.js'
|
|
50
53
|
import { isNonInteractive } from '../utils/interactive.js'
|
|
51
|
-
import {
|
|
54
|
+
import {
|
|
55
|
+
detectWorkspacePm,
|
|
56
|
+
installCmd,
|
|
57
|
+
detectGlobalCliPm,
|
|
58
|
+
globalCliUpdateCmd
|
|
59
|
+
} from '../utils/pm.js'
|
|
52
60
|
import { writeJsonPreservingStyle } from '../utils/json-file.js'
|
|
53
61
|
import { surveyWorkspaceDeps, compareSemver } from '../utils/dep-survey.js'
|
|
54
|
-
import {
|
|
62
|
+
import {
|
|
63
|
+
checkWorkspaceInstall,
|
|
64
|
+
readDeclaredFoundation
|
|
65
|
+
} from '../utils/install-integrity.js'
|
|
55
66
|
import { getLatestVersion } from '../utils/update-check.js'
|
|
56
67
|
|
|
57
68
|
const colors = {
|
|
@@ -61,7 +72,7 @@ const colors = {
|
|
|
61
72
|
red: '\x1b[31m',
|
|
62
73
|
green: '\x1b[32m',
|
|
63
74
|
yellow: '\x1b[33m',
|
|
64
|
-
cyan: '\x1b[36m'
|
|
75
|
+
cyan: '\x1b[36m'
|
|
65
76
|
}
|
|
66
77
|
|
|
67
78
|
const success = (msg) => console.log(`${colors.green}✓${colors.reset} ${msg}`)
|
|
@@ -78,8 +89,10 @@ const log = console.log
|
|
|
78
89
|
function isGlobalInstall() {
|
|
79
90
|
const scriptPath = process.argv[1]
|
|
80
91
|
if (!scriptPath) return false
|
|
81
|
-
return
|
|
82
|
-
|
|
92
|
+
return (
|
|
93
|
+
!scriptPath.split('/').includes('node_modules') &&
|
|
94
|
+
!scriptPath.split('\\').includes('node_modules')
|
|
95
|
+
)
|
|
83
96
|
}
|
|
84
97
|
|
|
85
98
|
/**
|
|
@@ -106,7 +119,9 @@ function findUniwebWorkspace(cwd) {
|
|
|
106
119
|
if (!existsSync(pkgPath)) return null
|
|
107
120
|
try {
|
|
108
121
|
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
|
|
109
|
-
const hasUniwebDep = !!(
|
|
122
|
+
const hasUniwebDep = !!(
|
|
123
|
+
pkg.devDependencies?.uniweb || pkg.dependencies?.uniweb
|
|
124
|
+
)
|
|
110
125
|
return hasUniwebDep ? workspaceDir : null
|
|
111
126
|
} catch {
|
|
112
127
|
return null
|
|
@@ -137,18 +152,34 @@ function findUniwebWorkspace(cwd) {
|
|
|
137
152
|
*
|
|
138
153
|
* @returns {boolean} true if a notice was printed.
|
|
139
154
|
*/
|
|
140
|
-
export function printStaleCliNotice({
|
|
155
|
+
export function printStaleCliNotice({
|
|
156
|
+
cliVersion,
|
|
157
|
+
latest,
|
|
158
|
+
isNpx,
|
|
159
|
+
isGlobal,
|
|
160
|
+
globalPm
|
|
161
|
+
}) {
|
|
141
162
|
if (isNpx) return false
|
|
142
163
|
if (!latest || compareSemver(latest, cliVersion) <= 0) return false
|
|
143
164
|
|
|
144
165
|
log('')
|
|
145
|
-
log(
|
|
166
|
+
log(
|
|
167
|
+
`${colors.yellow}⚠${colors.reset} ${colors.bright}A newer uniweb is available:${colors.reset} ${colors.dim}v${cliVersion}${colors.reset} → ${colors.cyan}v${latest}${colors.reset}`
|
|
168
|
+
)
|
|
146
169
|
if (isGlobal) {
|
|
147
|
-
log(
|
|
148
|
-
|
|
170
|
+
log(
|
|
171
|
+
`${colors.dim}This run aligned the project to v${cliVersion}'s matrix. To update the CLI:${colors.reset} ${colors.cyan}${globalCliUpdateCmd(globalPm)}${colors.reset}`
|
|
172
|
+
)
|
|
173
|
+
log(
|
|
174
|
+
`${colors.dim}Or align to the latest release without a global install:${colors.reset} ${colors.cyan}npx uniweb@latest update${colors.reset}`
|
|
175
|
+
)
|
|
149
176
|
} else {
|
|
150
|
-
log(
|
|
151
|
-
|
|
177
|
+
log(
|
|
178
|
+
`${colors.dim}This run aligned the project to v${cliVersion}'s matrix — the version your project pins.${colors.reset}`
|
|
179
|
+
)
|
|
180
|
+
log(
|
|
181
|
+
`${colors.dim}To move to v${latest}:${colors.reset} ${colors.cyan}npx uniweb@latest update${colors.reset} ${colors.dim}(updates the pin too).${colors.reset}`
|
|
182
|
+
)
|
|
152
183
|
}
|
|
153
184
|
log('')
|
|
154
185
|
return true
|
|
@@ -159,7 +190,7 @@ function runCommand(cmd, cwd) {
|
|
|
159
190
|
return new Promise((resolve) => {
|
|
160
191
|
const [bin, ...rest] = cmd.split(' ')
|
|
161
192
|
const child = spawn(bin, rest, { stdio: 'inherit', cwd })
|
|
162
|
-
child.on('close', code => resolve(code ?? 0))
|
|
193
|
+
child.on('close', (code) => resolve(code ?? 0))
|
|
163
194
|
child.on('error', () => resolve(1))
|
|
164
195
|
})
|
|
165
196
|
}
|
|
@@ -179,19 +210,28 @@ function runCommand(cmd, cwd) {
|
|
|
179
210
|
* regression here is invisible — the command still works, it just stops
|
|
180
211
|
* being readable.
|
|
181
212
|
*/
|
|
182
|
-
export function printSurvey(
|
|
213
|
+
export function printSurvey(
|
|
214
|
+
report,
|
|
215
|
+
cliVersion,
|
|
216
|
+
agentsVersion,
|
|
217
|
+
{ verbose = false } = {}
|
|
218
|
+
) {
|
|
183
219
|
log('')
|
|
184
220
|
log(`${colors.bright}uniweb CLI:${colors.reset} v${cliVersion}`)
|
|
185
|
-
log(
|
|
221
|
+
log(
|
|
222
|
+
`${colors.bright}AGENTS.md stamp:${colors.reset} ${agentsVersion ? 'v' + agentsVersion : colors.dim + '(none)' + colors.reset}`
|
|
223
|
+
)
|
|
186
224
|
log('')
|
|
187
225
|
|
|
188
226
|
if (report.rows.length === 0) {
|
|
189
|
-
log(
|
|
227
|
+
log(
|
|
228
|
+
`${colors.dim}No @uniweb/* deps found in workspace package.json files.${colors.reset}`
|
|
229
|
+
)
|
|
190
230
|
log('')
|
|
191
231
|
return
|
|
192
232
|
}
|
|
193
233
|
|
|
194
|
-
const needsAttention = report.rows.filter(r => r.status !== 'aligned')
|
|
234
|
+
const needsAttention = report.rows.filter((r) => r.status !== 'aligned')
|
|
195
235
|
const shown = verbose ? report.rows : needsAttention
|
|
196
236
|
const alignedCount = report.rows.length - needsAttention.length
|
|
197
237
|
|
|
@@ -208,7 +248,7 @@ export function printSurvey(report, cliVersion, agentsVersion, { verbose = false
|
|
|
208
248
|
log(`${colors.bright}Workspace deps (declared):${colors.reset}`)
|
|
209
249
|
for (const [dir, dirRows] of Object.entries(byDir)) {
|
|
210
250
|
log(` ${colors.dim}${dir}/${colors.reset}`)
|
|
211
|
-
const maxName = Math.max(...dirRows.map(r => r.name.length))
|
|
251
|
+
const maxName = Math.max(...dirRows.map((r) => r.name.length))
|
|
212
252
|
for (const row of dirRows) {
|
|
213
253
|
const padding = ' '.repeat(maxName - row.name.length)
|
|
214
254
|
let icon, statusText
|
|
@@ -222,11 +262,15 @@ export function printSurvey(report, cliVersion, agentsVersion, { verbose = false
|
|
|
222
262
|
icon = `${colors.cyan}↑${colors.reset}`
|
|
223
263
|
statusText = `${colors.cyan}ahead of CLI${colors.reset}`
|
|
224
264
|
}
|
|
225
|
-
log(
|
|
265
|
+
log(
|
|
266
|
+
` ${icon} ${row.name}${padding} ${row.current.padEnd(10)} → ${row.target.padEnd(10)} ${statusText}`
|
|
267
|
+
)
|
|
226
268
|
}
|
|
227
269
|
}
|
|
228
270
|
if (!verbose && alignedCount > 0) {
|
|
229
|
-
log(
|
|
271
|
+
log(
|
|
272
|
+
` ${colors.dim}(${alignedCount} other${alignedCount === 1 ? '' : 's'} already aligned — ${colors.reset}${colors.cyan}--verbose${colors.reset}${colors.dim} to list)${colors.reset}`
|
|
273
|
+
)
|
|
230
274
|
}
|
|
231
275
|
log('')
|
|
232
276
|
}
|
|
@@ -239,7 +283,7 @@ export function printSurvey(report, cliVersion, agentsVersion, { verbose = false
|
|
|
239
283
|
* reflow the whole file). Returns the list of paths that actually changed.
|
|
240
284
|
*/
|
|
241
285
|
function applyDepUpdates(workspaceDir, surveyRows, dryRun) {
|
|
242
|
-
const behind = surveyRows.filter(r => r.status === 'behind')
|
|
286
|
+
const behind = surveyRows.filter((r) => r.status === 'behind')
|
|
243
287
|
const byDir = {}
|
|
244
288
|
for (const row of behind) {
|
|
245
289
|
const dir = row.relDir === '(root)' ? '' : row.relDir
|
|
@@ -254,12 +298,20 @@ function applyDepUpdates(workspaceDir, surveyRows, dryRun) {
|
|
|
254
298
|
if (!existsSync(pkgPath)) continue
|
|
255
299
|
const original = readFileSync(pkgPath, 'utf8')
|
|
256
300
|
let pkg
|
|
257
|
-
try {
|
|
301
|
+
try {
|
|
302
|
+
pkg = JSON.parse(original)
|
|
303
|
+
} catch {
|
|
304
|
+
continue
|
|
305
|
+
}
|
|
258
306
|
|
|
259
307
|
let changed = false
|
|
260
308
|
for (const row of dirRows) {
|
|
261
309
|
const section = pkg[row.section]
|
|
262
|
-
if (
|
|
310
|
+
if (
|
|
311
|
+
section &&
|
|
312
|
+
section[row.name] !== undefined &&
|
|
313
|
+
section[row.name] !== row.target
|
|
314
|
+
) {
|
|
263
315
|
section[row.name] = row.target
|
|
264
316
|
changed = true
|
|
265
317
|
}
|
|
@@ -295,8 +347,12 @@ export async function update(args = []) {
|
|
|
295
347
|
const cliVersion = getCliVersion()
|
|
296
348
|
|
|
297
349
|
if ((agentsOnly || depsOnly) && !inProject) {
|
|
298
|
-
error(
|
|
299
|
-
|
|
350
|
+
error(
|
|
351
|
+
`${agentsOnly ? '--agents-only' : '--deps-only'} requires a Uniweb project (no \`uniweb\` dep in the workspace root).`
|
|
352
|
+
)
|
|
353
|
+
log(
|
|
354
|
+
`${colors.dim}Run this command from inside a project created by${colors.reset} ${colors.cyan}uniweb create${colors.reset}${colors.dim}.${colors.reset}`
|
|
355
|
+
)
|
|
300
356
|
process.exit(1)
|
|
301
357
|
}
|
|
302
358
|
|
|
@@ -328,45 +384,64 @@ export async function update(args = []) {
|
|
|
328
384
|
// same convention `--version` follows in index.js. Skipped entirely when
|
|
329
385
|
// there is nothing to reconcile — the notice is never reached from those
|
|
330
386
|
// paths, and looking it up anyway would spend a network call on nothing.
|
|
331
|
-
const latestCli =
|
|
332
|
-
|
|
333
|
-
|
|
387
|
+
const latestCli =
|
|
388
|
+
isNpx || !inProject
|
|
389
|
+
? null
|
|
390
|
+
: await getLatestVersion({ allowNetwork: !!process.stdout.isTTY })
|
|
334
391
|
|
|
335
392
|
if (isNpx) {
|
|
336
|
-
log(
|
|
337
|
-
|
|
393
|
+
log(
|
|
394
|
+
`${colors.dim}Running${colors.reset} ${colors.cyan}uniweb@${cliVersion}${colors.reset} ${colors.dim}via npx — aligning this project to v${cliVersion}'s matrix.${colors.reset}`
|
|
395
|
+
)
|
|
396
|
+
log(
|
|
397
|
+
`${colors.dim}(To install the CLI:${colors.reset} ${colors.cyan}npm i -g uniweb${colors.reset}${colors.dim}.)${colors.reset}`
|
|
398
|
+
)
|
|
338
399
|
log('')
|
|
339
400
|
} else if (!isGlobal) {
|
|
340
401
|
// Project-local copy (lives in this project's node_modules).
|
|
341
|
-
log(
|
|
402
|
+
log(
|
|
403
|
+
`${colors.dim}Running the project-local CLI (v${cliVersion}) — pinned by your project's${colors.reset} ${colors.cyan}package.json${colors.reset}${colors.dim}.${colors.reset}`
|
|
404
|
+
)
|
|
342
405
|
log('')
|
|
343
406
|
}
|
|
344
407
|
|
|
345
408
|
if (!inProject) {
|
|
346
|
-
log(
|
|
347
|
-
|
|
409
|
+
log(
|
|
410
|
+
`${colors.dim}Not inside a Uniweb project — nothing to reconcile.${colors.reset}`
|
|
411
|
+
)
|
|
412
|
+
log(
|
|
413
|
+
`${colors.dim}Run this from a project created by${colors.reset} ${colors.cyan}uniweb create${colors.reset}${colors.dim}.${colors.reset}`
|
|
414
|
+
)
|
|
348
415
|
log('')
|
|
349
416
|
return
|
|
350
417
|
}
|
|
351
418
|
|
|
352
419
|
// ── Step 1: Deps alignment ───────────────────────────────────────
|
|
353
|
-
let depsEdited = false
|
|
354
|
-
let installRan = false
|
|
420
|
+
let depsEdited = false // package.json files were rewritten
|
|
421
|
+
let installRan = false // `<pm> install` ran and succeeded
|
|
355
422
|
let editedPaths = []
|
|
356
423
|
|
|
357
424
|
if (!skipDeps && survey) {
|
|
358
425
|
if (!survey.anyDrift) {
|
|
359
426
|
// The count carries the work the collapsed table no longer shows.
|
|
360
|
-
success(
|
|
427
|
+
success(
|
|
428
|
+
`Workspace deps are aligned with the CLI (${survey.rows.length} checked).`
|
|
429
|
+
)
|
|
361
430
|
if (survey.anyAhead) {
|
|
362
|
-
log(
|
|
431
|
+
log(
|
|
432
|
+
`${colors.dim}(Some deps are ahead of the CLI's bundled matrix — left untouched.)${colors.reset}`
|
|
433
|
+
)
|
|
363
434
|
}
|
|
364
435
|
if (!existsSync(join(workspaceDir, 'node_modules'))) {
|
|
365
|
-
warn(
|
|
436
|
+
warn(
|
|
437
|
+
`No ${colors.bright}node_modules${colors.reset} in the workspace — run ${colors.cyan}${installCmd(installPm || 'pnpm')}${colors.reset} to install.`
|
|
438
|
+
)
|
|
366
439
|
}
|
|
367
440
|
log('')
|
|
368
441
|
} else {
|
|
369
|
-
log(
|
|
442
|
+
log(
|
|
443
|
+
`${colors.yellow}⚠${colors.reset} Some workspace deps lag the CLI's bundled matrix.`
|
|
444
|
+
)
|
|
370
445
|
log('')
|
|
371
446
|
|
|
372
447
|
// Decide whether to write the package.json edits.
|
|
@@ -377,8 +452,12 @@ export async function update(args = []) {
|
|
|
377
452
|
proceed = true
|
|
378
453
|
} else if (nonInteractive) {
|
|
379
454
|
proceed = false
|
|
380
|
-
info(
|
|
381
|
-
|
|
455
|
+
info(
|
|
456
|
+
`${colors.dim}Non-interactive — printing the alignment plan; not editing files.${colors.reset}`
|
|
457
|
+
)
|
|
458
|
+
log(
|
|
459
|
+
`${colors.dim}To apply, re-run with${colors.reset} ${colors.cyan}--yes${colors.reset}${colors.dim}, or align manually:${colors.reset}`
|
|
460
|
+
)
|
|
382
461
|
log(` ${colors.cyan}pnpm update "@uniweb/*" uniweb -r${colors.reset}`)
|
|
383
462
|
log('')
|
|
384
463
|
} else {
|
|
@@ -386,7 +465,7 @@ export async function update(args = []) {
|
|
|
386
465
|
type: 'confirm',
|
|
387
466
|
name: 'go',
|
|
388
467
|
message: `Edit workspace package.json files to align with v${cliVersion}?`,
|
|
389
|
-
initial: true
|
|
468
|
+
initial: true
|
|
390
469
|
})
|
|
391
470
|
proceed = !!go
|
|
392
471
|
}
|
|
@@ -395,11 +474,18 @@ export async function update(args = []) {
|
|
|
395
474
|
const wouldEdit = applyDepUpdates(workspaceDir, survey.rows, true)
|
|
396
475
|
if (wouldEdit.length > 0) {
|
|
397
476
|
info('Dry-run: would update package.json in:')
|
|
398
|
-
for (const path of wouldEdit)
|
|
477
|
+
for (const path of wouldEdit)
|
|
478
|
+
log(
|
|
479
|
+
` ${colors.dim}- ${relativize(path, workspaceDir)}${colors.reset}`
|
|
480
|
+
)
|
|
399
481
|
if (installPm) {
|
|
400
|
-
log(
|
|
482
|
+
log(
|
|
483
|
+
`${colors.dim}Then would run:${colors.reset} ${colors.cyan}${installCmd(installPm)}${colors.reset}`
|
|
484
|
+
)
|
|
401
485
|
} else {
|
|
402
|
-
log(
|
|
486
|
+
log(
|
|
487
|
+
`${colors.dim}Then would prompt for an install command (no lockfile detected).${colors.reset}`
|
|
488
|
+
)
|
|
403
489
|
}
|
|
404
490
|
log('')
|
|
405
491
|
}
|
|
@@ -410,26 +496,32 @@ export async function update(args = []) {
|
|
|
410
496
|
info('No package.json files needed changes.')
|
|
411
497
|
log('')
|
|
412
498
|
} else {
|
|
413
|
-
for (const path of editedPaths)
|
|
499
|
+
for (const path of editedPaths)
|
|
500
|
+
success(`Updated ${relativize(path, workspaceDir)}`)
|
|
414
501
|
log('')
|
|
415
502
|
|
|
416
503
|
// Resolve the workspace PM (lockfile-driven). If absent, ask.
|
|
417
504
|
if (!installPm) {
|
|
418
505
|
if (nonInteractive) {
|
|
419
|
-
warn(
|
|
420
|
-
|
|
506
|
+
warn(
|
|
507
|
+
'No lockfile in workspace root — cannot pick an install command for you.'
|
|
508
|
+
)
|
|
509
|
+
log(
|
|
510
|
+
`${colors.dim}Run one of:${colors.reset} ${colors.cyan}pnpm install${colors.reset} ${colors.dim}/${colors.reset} ${colors.cyan}yarn install${colors.reset} ${colors.dim}/${colors.reset} ${colors.cyan}npm install${colors.reset}`
|
|
511
|
+
)
|
|
421
512
|
log('')
|
|
422
513
|
} else {
|
|
423
514
|
const { picked } = await prompts({
|
|
424
515
|
type: 'select',
|
|
425
516
|
name: 'picked',
|
|
426
|
-
message:
|
|
517
|
+
message:
|
|
518
|
+
'No lockfile found. Which package manager does this workspace use?',
|
|
427
519
|
choices: [
|
|
428
520
|
{ title: 'pnpm', value: 'pnpm' },
|
|
429
521
|
{ title: 'yarn', value: 'yarn' },
|
|
430
522
|
{ title: 'npm', value: 'npm' },
|
|
431
|
-
{ title:
|
|
432
|
-
]
|
|
523
|
+
{ title: "skip — I'll install manually", value: null }
|
|
524
|
+
]
|
|
433
525
|
})
|
|
434
526
|
installPm = picked || null
|
|
435
527
|
}
|
|
@@ -442,14 +534,16 @@ export async function update(args = []) {
|
|
|
442
534
|
runInstall = true
|
|
443
535
|
} else if (nonInteractive) {
|
|
444
536
|
runInstall = false
|
|
445
|
-
info(
|
|
537
|
+
info(
|
|
538
|
+
`${colors.dim}Non-interactive — run the install yourself:${colors.reset} ${colors.cyan}${cmd}${colors.reset}`
|
|
539
|
+
)
|
|
446
540
|
log('')
|
|
447
541
|
} else {
|
|
448
542
|
const { go } = await prompts({
|
|
449
543
|
type: 'confirm',
|
|
450
544
|
name: 'go',
|
|
451
545
|
message: `Run \`${cmd}\` now?`,
|
|
452
|
-
initial: true
|
|
546
|
+
initial: true
|
|
453
547
|
})
|
|
454
548
|
runInstall = !!go
|
|
455
549
|
}
|
|
@@ -461,15 +555,25 @@ export async function update(args = []) {
|
|
|
461
555
|
success('Install complete.')
|
|
462
556
|
log('')
|
|
463
557
|
} else {
|
|
464
|
-
error(
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
558
|
+
error(
|
|
559
|
+
`Install failed (exit ${code}). package.json edits are intact.`
|
|
560
|
+
)
|
|
561
|
+
const editedRel = editedPaths
|
|
562
|
+
.map((p) => relativize(p, workspaceDir))
|
|
563
|
+
.join(' ')
|
|
564
|
+
log(
|
|
565
|
+
`${colors.dim}To revert:${colors.reset} ${colors.cyan}git checkout -- ${editedRel}${colors.reset}`
|
|
566
|
+
)
|
|
567
|
+
log(
|
|
568
|
+
`${colors.dim}To retry: ${colors.reset} ${colors.cyan}${cmd}${colors.reset}`
|
|
569
|
+
)
|
|
468
570
|
log('')
|
|
469
571
|
process.exit(code)
|
|
470
572
|
}
|
|
471
573
|
} else {
|
|
472
|
-
log(
|
|
574
|
+
log(
|
|
575
|
+
`${colors.dim}Skipped install. Edits saved; run${colors.reset} ${colors.cyan}${cmd}${colors.reset} ${colors.dim}to apply them.${colors.reset}`
|
|
576
|
+
)
|
|
473
577
|
log('')
|
|
474
578
|
}
|
|
475
579
|
}
|
|
@@ -482,12 +586,20 @@ export async function update(args = []) {
|
|
|
482
586
|
}
|
|
483
587
|
|
|
484
588
|
// ── Step 2: AGENTS.md ────────────────────────────────────────────
|
|
485
|
-
let agentsResult = null
|
|
589
|
+
let agentsResult = null // 'created' | 'updated' | 'current' | 'skipped'
|
|
486
590
|
|
|
487
591
|
if (!skipAgents) {
|
|
488
592
|
agentsResult = await refreshAgents({
|
|
489
|
-
workspaceDir,
|
|
490
|
-
|
|
593
|
+
workspaceDir,
|
|
594
|
+
cliVersion,
|
|
595
|
+
allowMismatch,
|
|
596
|
+
dryRun,
|
|
597
|
+
hasYes,
|
|
598
|
+
nonInteractive,
|
|
599
|
+
agentsOnly,
|
|
600
|
+
depsEdited,
|
|
601
|
+
installRan,
|
|
602
|
+
installPm
|
|
491
603
|
})
|
|
492
604
|
}
|
|
493
605
|
|
|
@@ -503,14 +615,30 @@ export async function update(args = []) {
|
|
|
503
615
|
}
|
|
504
616
|
|
|
505
617
|
// ── Closing summary ──────────────────────────────────────────────
|
|
506
|
-
if (
|
|
507
|
-
|
|
618
|
+
if (
|
|
619
|
+
!dryRun &&
|
|
620
|
+
(depsEdited || agentsResult === 'created' || agentsResult === 'updated')
|
|
621
|
+
) {
|
|
622
|
+
printSummary({
|
|
623
|
+
editedPaths,
|
|
624
|
+
depsEdited,
|
|
625
|
+
installRan,
|
|
626
|
+
installPm,
|
|
627
|
+
agentsResult,
|
|
628
|
+
cliVersion
|
|
629
|
+
})
|
|
508
630
|
}
|
|
509
631
|
|
|
510
632
|
// Last, deliberately — see printStaleCliNotice. Everything above reports
|
|
511
633
|
// against THIS CLI's matrix; if the CLI itself is behind, that is the note
|
|
512
634
|
// the reader should leave with.
|
|
513
|
-
printStaleCliNotice({
|
|
635
|
+
printStaleCliNotice({
|
|
636
|
+
cliVersion,
|
|
637
|
+
latest: latestCli,
|
|
638
|
+
isNpx,
|
|
639
|
+
isGlobal,
|
|
640
|
+
globalPm
|
|
641
|
+
})
|
|
514
642
|
}
|
|
515
643
|
|
|
516
644
|
/**
|
|
@@ -521,16 +649,17 @@ async function reportInstallIntegrity(workspaceDir) {
|
|
|
521
649
|
// Imported here, not at the top: discover.js reaches @uniweb/build, an
|
|
522
650
|
// optional peer that must stay off the CLI's startup path so `npx uniweb
|
|
523
651
|
// create` works in an empty directory. update.js is loaded at startup.
|
|
524
|
-
const { discoverSites, discoverFoundations } =
|
|
652
|
+
const { discoverSites, discoverFoundations } =
|
|
653
|
+
await import('../utils/discover.js')
|
|
525
654
|
|
|
526
655
|
const sites = (await discoverSites(workspaceDir)).map((s) => ({
|
|
527
656
|
name: s.name,
|
|
528
657
|
path: join(workspaceDir, s.path),
|
|
529
|
-
foundation: readDeclaredFoundation(join(workspaceDir, s.path))
|
|
658
|
+
foundation: readDeclaredFoundation(join(workspaceDir, s.path))
|
|
530
659
|
}))
|
|
531
660
|
const foundations = (await discoverFoundations(workspaceDir)).map((f) => ({
|
|
532
661
|
name: f.name,
|
|
533
|
-
path: join(workspaceDir, f.path)
|
|
662
|
+
path: join(workspaceDir, f.path)
|
|
534
663
|
}))
|
|
535
664
|
|
|
536
665
|
const findings = checkWorkspaceInstall(sites, foundations, workspaceDir)
|
|
@@ -541,7 +670,9 @@ async function reportInstallIntegrity(workspaceDir) {
|
|
|
541
670
|
for (const finding of findings) {
|
|
542
671
|
log(` ${finding.message}`)
|
|
543
672
|
}
|
|
544
|
-
log(
|
|
673
|
+
log(
|
|
674
|
+
` ${colors.dim}${findings[0].remedy}. \`uniweb doctor\` explains in full.${colors.reset}`
|
|
675
|
+
)
|
|
545
676
|
} catch {
|
|
546
677
|
// A post-flight check must never turn a successful update into a failure.
|
|
547
678
|
}
|
|
@@ -557,8 +688,16 @@ async function reportInstallIntegrity(workspaceDir) {
|
|
|
557
688
|
*/
|
|
558
689
|
async function refreshAgents(ctx) {
|
|
559
690
|
const {
|
|
560
|
-
workspaceDir,
|
|
561
|
-
|
|
691
|
+
workspaceDir,
|
|
692
|
+
cliVersion,
|
|
693
|
+
allowMismatch,
|
|
694
|
+
dryRun,
|
|
695
|
+
hasYes,
|
|
696
|
+
nonInteractive,
|
|
697
|
+
agentsOnly,
|
|
698
|
+
depsEdited,
|
|
699
|
+
installRan,
|
|
700
|
+
installPm
|
|
562
701
|
} = ctx
|
|
563
702
|
|
|
564
703
|
// Deps were rewritten but not installed → node_modules is now behind
|
|
@@ -566,8 +705,12 @@ async function refreshAgents(ctx) {
|
|
|
566
705
|
// installed code doesn't have. No override — run the install first.
|
|
567
706
|
if (depsEdited && !installRan) {
|
|
568
707
|
const cmd = installCmd(installPm || 'pnpm')
|
|
569
|
-
warn(
|
|
570
|
-
|
|
708
|
+
warn(
|
|
709
|
+
'AGENTS.md refresh skipped: package.json was updated but not installed.'
|
|
710
|
+
)
|
|
711
|
+
log(
|
|
712
|
+
`${colors.dim}Your${colors.reset} ${colors.bright}node_modules${colors.reset} ${colors.dim}is behind your${colors.reset} ${colors.bright}package.json${colors.reset}${colors.dim}. Run${colors.reset} ${colors.cyan}${cmd}${colors.reset}${colors.dim}, then re-run${colors.reset} ${colors.cyan}uniweb update${colors.reset}${colors.dim}.${colors.reset}`
|
|
713
|
+
)
|
|
571
714
|
log('')
|
|
572
715
|
return 'skipped'
|
|
573
716
|
}
|
|
@@ -577,8 +720,12 @@ async function refreshAgents(ctx) {
|
|
|
577
720
|
const finalSurvey = await surveyWorkspaceDeps(workspaceDir)
|
|
578
721
|
if (finalSurvey.anyDrift && !allowMismatch) {
|
|
579
722
|
warn('AGENTS.md refresh skipped: workspace deps still lag the CLI.')
|
|
580
|
-
log(
|
|
581
|
-
|
|
723
|
+
log(
|
|
724
|
+
`${colors.dim}AGENTS.md from v${cliVersion} would document features not in your installed packages.${colors.reset}`
|
|
725
|
+
)
|
|
726
|
+
log(
|
|
727
|
+
`${colors.dim}Re-run without ${colors.reset}${colors.cyan}--no-deps${colors.reset}${colors.dim}, or pass ${colors.reset}${colors.cyan}--allow-mismatch${colors.reset}${colors.dim} to override.${colors.reset}`
|
|
728
|
+
)
|
|
582
729
|
log('')
|
|
583
730
|
if (agentsOnly) process.exit(1)
|
|
584
731
|
return 'skipped'
|
|
@@ -592,7 +739,9 @@ async function refreshAgents(ctx) {
|
|
|
592
739
|
}
|
|
593
740
|
|
|
594
741
|
if (dryRun) {
|
|
595
|
-
info(
|
|
742
|
+
info(
|
|
743
|
+
`Dry-run: would ${currentAgentsVersion ? `update AGENTS.md (v${currentAgentsVersion} → v${cliVersion})` : `create AGENTS.md (v${cliVersion})`}.`
|
|
744
|
+
)
|
|
596
745
|
return 'skipped'
|
|
597
746
|
}
|
|
598
747
|
|
|
@@ -600,7 +749,12 @@ async function refreshAgents(ctx) {
|
|
|
600
749
|
const action = currentAgentsVersion
|
|
601
750
|
? `Update AGENTS.md (v${currentAgentsVersion} → v${cliVersion})?`
|
|
602
751
|
: `Create AGENTS.md (v${cliVersion})?`
|
|
603
|
-
const { yes } = await prompts({
|
|
752
|
+
const { yes } = await prompts({
|
|
753
|
+
type: 'confirm',
|
|
754
|
+
name: 'yes',
|
|
755
|
+
message: action,
|
|
756
|
+
initial: true
|
|
757
|
+
})
|
|
604
758
|
if (!yes) {
|
|
605
759
|
log(`${colors.dim}Skipped AGENTS.md.${colors.reset}`)
|
|
606
760
|
return 'skipped'
|
|
@@ -623,20 +777,38 @@ async function refreshAgents(ctx) {
|
|
|
623
777
|
* installed packages changed, and stay quiet otherwise (this runs on every
|
|
624
778
|
* `uniweb update`, including no-op ones).
|
|
625
779
|
*/
|
|
626
|
-
export function printSummary({
|
|
780
|
+
export function printSummary({
|
|
781
|
+
editedPaths,
|
|
782
|
+
depsEdited,
|
|
783
|
+
installRan,
|
|
784
|
+
installPm,
|
|
785
|
+
agentsResult,
|
|
786
|
+
cliVersion
|
|
787
|
+
}) {
|
|
627
788
|
log(`${colors.bright}Summary${colors.reset}`)
|
|
628
789
|
if (depsEdited) {
|
|
629
|
-
log(
|
|
790
|
+
log(
|
|
791
|
+
` ${colors.green}✓${colors.reset} package.json updated in ${editedPaths.length} file${editedPaths.length === 1 ? '' : 's'}`
|
|
792
|
+
)
|
|
630
793
|
if (installRan) {
|
|
631
|
-
log(
|
|
794
|
+
log(
|
|
795
|
+
` ${colors.green}✓${colors.reset} ${installCmd(installPm || 'pnpm')} completed`
|
|
796
|
+
)
|
|
632
797
|
} else {
|
|
633
|
-
log(
|
|
798
|
+
log(
|
|
799
|
+
` ${colors.yellow}⚠${colors.reset} install NOT run — run ${colors.cyan}${installCmd(installPm || 'pnpm')}${colors.reset} to apply`
|
|
800
|
+
)
|
|
634
801
|
}
|
|
635
802
|
}
|
|
636
|
-
if (agentsResult === 'created')
|
|
637
|
-
|
|
638
|
-
else if (agentsResult === '
|
|
639
|
-
|
|
803
|
+
if (agentsResult === 'created')
|
|
804
|
+
log(` ${colors.green}✓${colors.reset} AGENTS.md created (v${cliVersion})`)
|
|
805
|
+
else if (agentsResult === 'updated')
|
|
806
|
+
log(` ${colors.green}✓${colors.reset} AGENTS.md updated (v${cliVersion})`)
|
|
807
|
+
else if (agentsResult === 'skipped' && depsEdited)
|
|
808
|
+
log(` ${colors.dim}·${colors.reset} AGENTS.md not refreshed (see above)`)
|
|
809
|
+
log(
|
|
810
|
+
`${colors.dim}Review changes with${colors.reset} ${colors.cyan}git diff${colors.reset}${colors.dim}, then commit.${colors.reset}`
|
|
811
|
+
)
|
|
640
812
|
|
|
641
813
|
// Only when the installed packages actually changed under a process that may
|
|
642
814
|
// still be running. This failure is nastier than it sounds: hot reload picks
|
|
@@ -646,8 +818,12 @@ export function printSummary({ editedPaths, depsEdited, installRan, installPm, a
|
|
|
646
818
|
// thinks to look is the server that has been running fine all along.
|
|
647
819
|
if (depsEdited && installRan) {
|
|
648
820
|
log('')
|
|
649
|
-
log(
|
|
650
|
-
|
|
821
|
+
log(
|
|
822
|
+
`${colors.yellow}↻${colors.reset} ${colors.bright}Restart any running dev server.${colors.reset}`
|
|
823
|
+
)
|
|
824
|
+
log(
|
|
825
|
+
`${colors.dim}Hot reload picks up your source, not swapped dependencies — a running${colors.reset} ${colors.cyan}uniweb dev${colors.reset} ${colors.dim}keeps using the framework version it started with.${colors.reset}`
|
|
826
|
+
)
|
|
651
827
|
}
|
|
652
828
|
|
|
653
829
|
log('')
|