uniweb 1.0.0 → 1.0.2

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.
Files changed (129) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +532 -3
  3. package/package.json +60 -11
  4. package/partials/adding-pages.hbs +77 -0
  5. package/partials/agents.md +3000 -0
  6. package/partials/ai-assistance.hbs +28 -0
  7. package/partials/components-docs.hbs +13 -0
  8. package/partials/config-reference.hbs +66 -0
  9. package/partials/deployment.hbs +13 -0
  10. package/partials/learn-more.hbs +6 -0
  11. package/partials/quick-start.hbs +8 -0
  12. package/partials/search-docs.hbs +110 -0
  13. package/src/backend/asset-download.js +222 -0
  14. package/src/backend/client.js +986 -0
  15. package/src/backend/foundation-bring-along.js +692 -0
  16. package/src/backend/payment-handoff.js +362 -0
  17. package/src/backend/service-request.js +210 -0
  18. package/src/backend/site-media.js +255 -0
  19. package/src/backend/site-preview.js +77 -0
  20. package/src/backend/site-sync.js +2515 -0
  21. package/src/backend/workspace.js +234 -0
  22. package/src/commands/add.js +1900 -0
  23. package/src/commands/build.js +1214 -0
  24. package/src/commands/clone.js +466 -0
  25. package/src/commands/content.js +202 -0
  26. package/src/commands/deploy.js +537 -0
  27. package/src/commands/dev.js +202 -0
  28. package/src/commands/docs.js +530 -0
  29. package/src/commands/doctor.js +1982 -0
  30. package/src/commands/export.js +116 -0
  31. package/src/commands/families.js +82 -0
  32. package/src/commands/forget.js +197 -0
  33. package/src/commands/handoff.js +21 -0
  34. package/src/commands/i18n.js +1707 -0
  35. package/src/commands/inspect.js +375 -0
  36. package/src/commands/invite.js +24 -0
  37. package/src/commands/org.js +96 -0
  38. package/src/commands/publish.js +1087 -0
  39. package/src/commands/pull.js +1257 -0
  40. package/src/commands/push.js +663 -0
  41. package/src/commands/refresh.js +212 -0
  42. package/src/commands/register.js +1092 -0
  43. package/src/commands/rename.js +647 -0
  44. package/src/commands/site.js +358 -0
  45. package/src/commands/snapshot.js +461 -0
  46. package/src/commands/starter.js +206 -0
  47. package/src/commands/status.js +302 -0
  48. package/src/commands/sync.js +93 -0
  49. package/src/commands/template.js +107 -0
  50. package/src/commands/update.js +887 -0
  51. package/src/commands/validate.js +376 -0
  52. package/src/framework-index.json +248 -0
  53. package/src/index.js +2241 -0
  54. package/src/templates/fetchers/github.js +113 -0
  55. package/src/templates/fetchers/npm.js +85 -0
  56. package/src/templates/fetchers/release.js +92 -0
  57. package/src/templates/index.js +190 -0
  58. package/src/templates/processor.js +333 -0
  59. package/src/templates/resolver.js +232 -0
  60. package/src/templates/validator.js +155 -0
  61. package/src/utils/agents-stamp.js +110 -0
  62. package/src/utils/args.js +126 -0
  63. package/src/utils/asset-upload.js +270 -0
  64. package/src/utils/bytes.js +30 -0
  65. package/src/utils/code-upload.js +394 -0
  66. package/src/utils/config.js +417 -0
  67. package/src/utils/conformance.js +274 -0
  68. package/src/utils/dep-survey.js +176 -0
  69. package/src/utils/destination-prompt.js +146 -0
  70. package/src/utils/discover.js +75 -0
  71. package/src/utils/env.js +22 -0
  72. package/src/utils/exit.js +30 -0
  73. package/src/utils/fetch-retry.js +109 -0
  74. package/src/utils/flag-guard.js +246 -0
  75. package/src/utils/foundation-name.js +179 -0
  76. package/src/utils/git.js +342 -0
  77. package/src/utils/host-prompt.js +51 -0
  78. package/src/utils/install-integrity.js +210 -0
  79. package/src/utils/interactive.js +118 -0
  80. package/src/utils/json-file.js +72 -0
  81. package/src/utils/names.js +91 -0
  82. package/src/utils/placement.js +151 -0
  83. package/src/utils/pm.js +165 -0
  84. package/src/utils/preview.js +20 -0
  85. package/src/utils/pull-written.js +132 -0
  86. package/src/utils/records-guard.js +88 -0
  87. package/src/utils/registry-auth.js +994 -0
  88. package/src/utils/registry-orgs.js +290 -0
  89. package/src/utils/scaffold.js +483 -0
  90. package/src/utils/schemaless-report.js +30 -0
  91. package/src/utils/semver-precedence.js +80 -0
  92. package/src/utils/session-file.js +95 -0
  93. package/src/utils/site-content-refs.js +23 -0
  94. package/src/utils/site-copies.js +123 -0
  95. package/src/utils/site-data-upload.js +302 -0
  96. package/src/utils/site-identity.js +137 -0
  97. package/src/utils/snapshot-settings.js +112 -0
  98. package/src/utils/update-check.js +302 -0
  99. package/src/utils/uwx-read.js +157 -0
  100. package/src/utils/workspace.js +223 -0
  101. package/src/versions.js +467 -0
  102. package/starter/foundation/main.js +50 -0
  103. package/starter/foundation/sections/Section/index.jsx +95 -0
  104. package/starter/foundation/sections/Section/meta.js +44 -0
  105. package/starter/site/pages/about/1-about.md.hbs +13 -0
  106. package/starter/site/pages/about/page.yml +3 -0
  107. package/starter/site/pages/home/1-welcome.md.hbs +14 -0
  108. package/starter/site/pages/home/page.yml +3 -0
  109. package/templates/foundation/_gitignore +5 -0
  110. package/templates/foundation/components/.gitkeep +0 -0
  111. package/templates/foundation/main.js.hbs +33 -0
  112. package/templates/foundation/package.json.hbs +51 -0
  113. package/templates/foundation/sections/.gitkeep +0 -0
  114. package/templates/foundation/styles.css +6 -0
  115. package/templates/foundation/vite.config.js +3 -0
  116. package/templates/site/_gitignore +12 -0
  117. package/templates/site/entry.js +7 -0
  118. package/templates/site/index.html.hbs +13 -0
  119. package/templates/site/package.json.hbs +27 -0
  120. package/templates/site/public/favicon.svg +7 -0
  121. package/templates/site/site.yml.hbs +91 -0
  122. package/templates/site/theme.yml +99 -0
  123. package/templates/site/vite.config.js +7 -0
  124. package/templates/workspace/AGENTS.md.hbs +2 -0
  125. package/templates/workspace/README.md.hbs +87 -0
  126. package/templates/workspace/_gitignore +29 -0
  127. package/templates/workspace/_vscode/settings.json +6 -0
  128. package/templates/workspace/package.json.hbs +20 -0
  129. package/templates/workspace/pnpm-workspace.yaml.hbs +26 -0
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Dev Command
3
+ *
4
+ * Starts a dev server for a site in the current workspace. Wraps the
5
+ * project's `dev` script (set up by `uniweb create` to filter to the
6
+ * appropriate site package). Provides discoverability and consistency
7
+ * with `uniweb build` / `uniweb deploy` — users shouldn't have to know
8
+ * whether to type `pnpm dev` or `npm run dev` when the rest of the CLI
9
+ * is verb-shaped.
10
+ *
11
+ * Usage:
12
+ * uniweb dev Start dev server for the (single) site
13
+ * uniweb dev <site> Start dev server for a specific site
14
+ * uniweb dev --site <name> Same, with explicit flag form
15
+ *
16
+ * Resolution order for which site to launch:
17
+ * 1. --site <name> (if passed)
18
+ * 2. Positional <site> arg
19
+ * 3. The single site in the workspace (if exactly one)
20
+ * 4. The first site in the workspace, with a "multiple sites" notice
21
+ * pointing at --site for explicit selection
22
+ *
23
+ * Multi-site workspaces with no positional / flag will run the first
24
+ * site by default (mirrors the `pnpm dev` shortcut `uniweb create` writes).
25
+ * Use `--site` to pick a different one without editing the root scripts.
26
+ *
27
+ * A site whose foundation is a catalog ref — every clone — has no build of it, and
28
+ * only the backend the site is on can say where that version is served. So `dev`
29
+ * asks it, through the site, and hands the answer to the dev server it starts in its
30
+ * environment (`UNIWEB_PREVIEW`), with the backend it asked, which is also where the
31
+ * site's kept media URLs are fetched from. Nothing is written: `site.yml` keeps the
32
+ * ref and the round trip never sees the URL. A backend that does not say is an
33
+ * ordinary answer — `dev` says so and starts nothing (`backend/site-preview.js`).
34
+ *
35
+ * Implementation: shells out to the package manager that invoked the CLI
36
+ * (detected via npm_config_user_agent), running the workspace-filtered
37
+ * dev command (`pnpm --filter <name> dev` or `npm -w <name> run dev`).
38
+ * No special handling of vite directly — the site package already owns
39
+ * its dev script, and shelling through pnpm/npm respects whatever the
40
+ * site has configured (Vite plugins, env vars, port overrides, etc.).
41
+ */
42
+
43
+ import { spawn } from 'node:child_process'
44
+ import { join } from 'node:path'
45
+
46
+ import { detectPackageManager, filterCmd } from '../utils/pm.js'
47
+ import { readWorkspaceConfig } from '../utils/config.js'
48
+ import { discoverSites } from '../utils/discover.js'
49
+ import { findWorkspaceRoot } from '../utils/workspace.js'
50
+ import { readFlagValue } from '../utils/args.js'
51
+ import {
52
+ checkSiteInstall,
53
+ readDeclaredFoundation
54
+ } from '../utils/install-integrity.js'
55
+ import { discoverFoundations } from '../utils/discover.js'
56
+
57
+ const RED = '\x1b[31m'
58
+ const YELLOW = '\x1b[33m'
59
+ const DIM = '\x1b[2m'
60
+ const CYAN = '\x1b[36m'
61
+ const RESET = '\x1b[0m'
62
+
63
+ export async function dev(args = []) {
64
+ const cwd = process.cwd()
65
+ const rootDir = findWorkspaceRoot(cwd) || cwd
66
+
67
+ // Verify we're in a Uniweb workspace (has pnpm-workspace.yaml or
68
+ // package.json::workspaces). discoverSites already handles both.
69
+ let workspaceConfig
70
+ try {
71
+ workspaceConfig = await readWorkspaceConfig(rootDir)
72
+ } catch {
73
+ workspaceConfig = { packages: [] }
74
+ }
75
+ if (workspaceConfig.packages.length === 0) {
76
+ console.error(
77
+ `${RED}✗${RESET} Not in a Uniweb workspace (no pnpm-workspace.yaml or package.json::workspaces).`
78
+ )
79
+ console.error(
80
+ ` Run \`uniweb create <name>\` to scaffold a project, or cd into an existing one.`
81
+ )
82
+ process.exit(1)
83
+ }
84
+
85
+ const sites = await discoverSites(rootDir)
86
+ if (sites.length === 0) {
87
+ console.error(`${RED}✗${RESET} No sites found in this workspace.`)
88
+ console.error(` Add one with \`uniweb add site <name>\`.`)
89
+ process.exit(1)
90
+ }
91
+
92
+ // Pick the site
93
+ const siteFlag = readFlagValue(args, '--site')
94
+ const positional = args.find((a) => !a.startsWith('-'))
95
+ const requested =
96
+ (typeof siteFlag === 'string' ? siteFlag : null) || positional || null
97
+
98
+ let site
99
+ if (requested) {
100
+ site =
101
+ sites.find((s) => s.name === requested) ||
102
+ sites.find((s) => s.path === requested)
103
+ if (!site) {
104
+ console.error(`${RED}✗${RESET} Site "${requested}" not found.`)
105
+ console.error(` Available: ${sites.map((s) => s.name).join(', ')}`)
106
+ process.exit(1)
107
+ }
108
+ } else if (sites.length === 1) {
109
+ site = sites[0]
110
+ } else {
111
+ site = sites[0]
112
+ console.error(
113
+ `${YELLOW}⚠${RESET} Multiple sites found; using ${CYAN}${site.name}${RESET}.`
114
+ )
115
+ console.error(` Pick a different one with \`uniweb dev --site <name>\`.`)
116
+ console.error(` Available: ${sites.map((s) => s.name).join(', ')}`)
117
+ console.error('')
118
+ }
119
+
120
+ // A `file:` foundation dependency can be satisfied by a link or by a copy,
121
+ // and a copy is a snapshot: the dev server serves it while builds read the
122
+ // workspace source, so the two disagree and only the browser shows it. The
123
+ // check is a few stats and package.json reads, so it runs every time rather
124
+ // than waiting for someone to suspect it.
125
+ await warnOnStaleInstall(rootDir, site)
126
+
127
+ const pm = detectPackageManager()
128
+ const command = filterCmd(pm, site.name, 'dev')
129
+ const [bin, ...rest] = command.split(' ')
130
+ const sitePath = join(rootDir, site.path)
131
+
132
+ // ⭐ A SITE WHOSE FOUNDATION IS A CATALOG REF — a clone — is previewed with what the backend
133
+ // it is on says: where that version is served. Asked here, for this dev server alone, and
134
+ // handed to it in its environment; nothing is written (`backend/site-preview.js`). Any other
135
+ // site needs nothing from a backend, and this asks nothing.
136
+ const { readSitePreview } = await import('../backend/site-preview.js')
137
+ const asked = await readSitePreview({ siteDir: sitePath, args })
138
+ if (asked?.refused) {
139
+ console.error(`${RED}✗${RESET} ${asked.refused[0]}`)
140
+ for (const line of asked.refused.slice(1)) console.error(` ${line}`)
141
+ process.exit(1)
142
+ }
143
+ let env = process.env
144
+ if (asked?.preview) {
145
+ const { PREVIEW_ENV, encodePreview } = await import('@uniweb/build/site')
146
+ env = { ...process.env, [PREVIEW_ENV]: encodePreview(asked.preview) }
147
+ console.error(
148
+ `${DIM}→ Previewing with ${asked.preview.foundation.ref}, served by ${asked.preview.backend}${RESET}`
149
+ )
150
+ }
151
+
152
+ console.error(
153
+ `${DIM}→ ${command}${RESET} ${DIM}(site: ${site.name}, dir: ${sitePath})${RESET}`
154
+ )
155
+ console.error('')
156
+
157
+ const child = spawn(bin, rest, { cwd: rootDir, stdio: 'inherit', env })
158
+ child.on('close', (code) => process.exit(code ?? 0))
159
+ child.on('error', (err) => {
160
+ console.error(`${RED}✗${RESET} Failed to start dev server: ${err.message}`)
161
+ process.exit(1)
162
+ })
163
+ }
164
+
165
+ /**
166
+ * Say so if the site is about to be served from an install that no longer
167
+ * matches the workspace. Warns rather than exits — the server may still be
168
+ * useful, and the author is the one who knows whether it is.
169
+ */
170
+ async function warnOnStaleInstall(rootDir, site) {
171
+ try {
172
+ const sitePath = join(rootDir, site.path)
173
+ const foundationName = readDeclaredFoundation(sitePath)
174
+ if (!foundationName) return
175
+
176
+ const foundations = await discoverFoundations(rootDir)
177
+ const foundation = foundations
178
+ .map((f) => ({ ...f, path: join(rootDir, f.path) }))
179
+ .find((f) => f.name === foundationName)
180
+ if (!foundation) return
181
+
182
+ const findings = checkSiteInstall(
183
+ { name: site.name, path: sitePath },
184
+ foundation,
185
+ rootDir
186
+ )
187
+ if (!findings.length) return
188
+
189
+ console.error(
190
+ `${YELLOW}⚠${RESET} Dev will not build against your workspace foundation:`
191
+ )
192
+ for (const finding of findings) {
193
+ console.error(` ${finding.message}`)
194
+ }
195
+ console.error(
196
+ ` ${findings[0].remedy}. \`uniweb doctor\` explains in full.`
197
+ )
198
+ console.error('')
199
+ } catch {
200
+ // A diagnostic must never be the reason dev fails to start.
201
+ }
202
+ }