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,530 @@
1
+ /**
2
+ * Docs Command
3
+ *
4
+ * Generates markdown documentation from foundation schema and
5
+ * provides configuration references.
6
+ *
7
+ * Usage:
8
+ * uniweb docs # Generate COMPONENTS.md
9
+ * uniweb docs site # Show site.yml reference
10
+ * uniweb docs page # Show page.yml reference
11
+ * uniweb docs meta # Show component meta.js reference
12
+ * uniweb docs --output README.md # Custom output filename
13
+ * uniweb docs --from-source # Build schema from source (no build required)
14
+ * uniweb docs --target <path> # Specify foundation directory explicitly
15
+ *
16
+ * When run from a site directory, automatically finds and documents the
17
+ * linked foundation, placing COMPONENTS.md in the site folder.
18
+ *
19
+ * When run from workspace root, auto-detects foundations. If multiple exist,
20
+ * prompts for selection.
21
+ */
22
+
23
+ import { existsSync } from 'node:fs'
24
+ import { readFile } from 'node:fs/promises'
25
+ import { resolve, join, dirname } from 'node:path'
26
+ import {
27
+ generateDocs,
28
+ resolveFoundationSrcPath,
29
+ classifyPackage
30
+ } from '@uniweb/build'
31
+ import {
32
+ isWorkspaceRoot,
33
+ findFoundations,
34
+ promptSelect
35
+ } from '../utils/workspace.js'
36
+ import {
37
+ isNonInteractive,
38
+ getCliPrefix,
39
+ formatOptions
40
+ } from '../utils/interactive.js'
41
+
42
+ // Colors for terminal output
43
+ const colors = {
44
+ reset: '\x1b[0m',
45
+ bright: '\x1b[1m',
46
+ dim: '\x1b[2m',
47
+ cyan: '\x1b[36m',
48
+ green: '\x1b[32m',
49
+ yellow: '\x1b[33m',
50
+ red: '\x1b[31m'
51
+ }
52
+
53
+ function log(message) {
54
+ console.log(message)
55
+ }
56
+
57
+ function success(message) {
58
+ console.log(`${colors.green}✓${colors.reset} ${message}`)
59
+ }
60
+
61
+ function error(message) {
62
+ console.error(`${colors.red}✗${colors.reset} ${message}`)
63
+ }
64
+
65
+ function info(message) {
66
+ console.log(`${colors.cyan}→${colors.reset} ${message}`)
67
+ }
68
+
69
+ // =============================================================================
70
+ // Reference Content
71
+ // =============================================================================
72
+
73
+ const SITE_REFERENCE = `
74
+ ${colors.cyan}${colors.bright}site.yml Reference${colors.reset}
75
+
76
+ Site-wide configuration for your Uniweb project.
77
+
78
+ ${colors.bright}Core Settings:${colors.reset}
79
+ ${colors.cyan}name${colors.reset} Site name (used in metadata and browser tab)
80
+ ${colors.cyan}defaultLanguage${colors.reset} Default locale code (default: "en")
81
+ ${colors.cyan}foundation${colors.reset} Foundation package name ("foundation" for local)
82
+
83
+ ${colors.bright}Page Ordering:${colors.reset}
84
+ ${colors.cyan}pages${colors.reset} Array of page folder names
85
+ First item becomes homepage at /
86
+ Example: [home, about, docs, pricing]
87
+ ${colors.cyan}index${colors.reset} Alternative: just name the homepage
88
+ Rest are auto-discovered
89
+
90
+ ${colors.bright}Internationalization:${colors.reset}
91
+ ${colors.cyan}languages${colors.reset} Languages to build (array or "*" for all)
92
+ Example: [es, fr, de]
93
+ ${colors.cyan}i18n.localesDir${colors.reset} Translation files directory (default: "locales")
94
+
95
+ ${colors.bright}Build Options:${colors.reset}
96
+ ${colors.cyan}build.prerender${colors.reset} Enable static HTML generation (default: false)
97
+ Generates SEO-friendly static pages
98
+
99
+ ${colors.bright}Example:${colors.reset}
100
+ ${colors.dim}name: My Site
101
+ defaultLanguage: en
102
+ foundation: foundation
103
+ pages: [home, about, docs]
104
+ i18n:
105
+ locales: [es, fr]
106
+ build:
107
+ prerender: true${colors.reset}
108
+
109
+ ${colors.dim}Schema: https://raw.githubusercontent.com/uniweb/cli/main/schemas/site.schema.json${colors.reset}
110
+ `
111
+
112
+ const PAGE_REFERENCE = `
113
+ ${colors.cyan}${colors.bright}page.yml Reference${colors.reset}
114
+
115
+ Page-level configuration in site/pages/[page]/page.yml
116
+
117
+ ${colors.bright}Basic Metadata:${colors.reset}
118
+ ${colors.cyan}title${colors.reset} Page title (browser tab, nav, SEO)
119
+ ${colors.cyan}description${colors.reset} Meta description for SEO
120
+ ${colors.cyan}label${colors.reset} Short nav label (defaults to title)
121
+ ${colors.cyan}order${colors.reset} Navigation sort order (lower = first)
122
+
123
+ ${colors.bright}Child Page Ordering:${colors.reset}
124
+ ${colors.cyan}pages${colors.reset} Array of child page names
125
+ First becomes index for this route
126
+ ${colors.cyan}index${colors.reset} Alternative: just name the index page
127
+
128
+ ${colors.bright}Navigation Visibility:${colors.reset}
129
+ ${colors.cyan}hidden${colors.reset} Hide from all navigation (page still exists)
130
+ ${colors.cyan}hideIn${colors.reset} Hide from named nav areas, e.g. [header] or [footer, sidebar]
131
+
132
+ ${colors.bright}Layout Options:${colors.reset}
133
+ ${colors.cyan}layout${colors.reset} Layout name or object with name, hide, params
134
+ String: layout name (e.g., MarketingLayout)
135
+ Object: { name, hide: [areas], params: {...} }
136
+ ${colors.cyan}layout.hide${colors.reset} Array of area names to suppress on this page
137
+ Example: [header, footer] or [left, right]
138
+ ${colors.cyan}layout.params${colors.reset} Layout-specific parameters (merged with meta.js defaults)
139
+
140
+ ${colors.bright}Section Control:${colors.reset}
141
+ ${colors.cyan}sections${colors.reset} "*" for auto-discover, or explicit array
142
+ Nested arrays create subsections:
143
+ - hero
144
+ - features:
145
+ - logocloud
146
+ - stats
147
+
148
+ ${colors.bright}SEO Options:${colors.reset}
149
+ ${colors.cyan}seo.noindex${colors.reset} Prevent search engine indexing
150
+ ${colors.cyan}seo.image${colors.reset} Open Graph / social-card image URL
151
+ ${colors.cyan}seo.ogTitle${colors.reset} Social-card title (defaults to page title)
152
+ ${colors.cyan}seo.ogDescription${colors.reset} Social-card description
153
+ ${colors.cyan}seo.canonical${colors.reset} Canonical URL for this page
154
+ ${colors.cyan}seo.changefreq${colors.reset} Sitemap hint (daily, weekly, monthly...)
155
+ ${colors.cyan}seo.priority${colors.reset} Sitemap priority (0.0 to 1.0)
156
+
157
+ ${colors.bright}Example:${colors.reset}
158
+ ${colors.dim}title: About Us
159
+ description: Learn about our company
160
+ order: 2
161
+ hideIn: [footer]
162
+ layout:
163
+ hide: [right]
164
+ seo:
165
+ image: /about-og.png${colors.reset}
166
+
167
+ ${colors.dim}Schema: https://raw.githubusercontent.com/uniweb/cli/main/schemas/page.schema.json${colors.reset}
168
+ `
169
+
170
+ const META_REFERENCE = `
171
+ ${colors.cyan}${colors.bright}Component meta.js Reference${colors.reset}
172
+
173
+ A section type's metadata, in <foundation>/sections/[Name]/meta.js
174
+
175
+ ${colors.bright}Identity:${colors.reset}
176
+ ${colors.cyan}title${colors.reset} Display name in editor
177
+ ${colors.cyan}description${colors.reset} What the component does
178
+ ${colors.cyan}family${colors.reset} The standard section family this is — "hero", "pricing",
179
+ "faq". Picks the editor's illustration and a translated
180
+ label. Only needed when the component NAME is not already
181
+ one: \`Hero\` resolves on its own, \`ProfileHero\` does not.
182
+ An unrecognized value is legal and falls back.
183
+ ${colors.dim}uniweb families${colors.reset}
184
+ ${colors.cyan}hidden${colors.reset} If true, not selectable in frontmatter
185
+
186
+ ${colors.bright}Content Expectations:${colors.reset}
187
+ ${colors.cyan}content${colors.reset} Object describing expected markdown structure
188
+ Keys: title, pretitle, subtitle, paragraphs, links, items
189
+ Values: "Description [count]"
190
+ Count: [1], [1-3], [2+]
191
+
192
+ ${colors.bright}Parameters:${colors.reset}
193
+ ${colors.cyan}params${colors.reset} Object of configurable parameters
194
+ Each param has: type, label, options, default
195
+ Types: "select", "boolean", "string", "number"
196
+
197
+ ${colors.bright}Presets:${colors.reset}
198
+ ${colors.cyan}presets${colors.reset} Named parameter combinations
199
+ Each preset: { label, params: {...} }
200
+
201
+ ${colors.bright}Special:${colors.reset}
202
+ ${colors.cyan}background${colors.reset} true if component supports background images
203
+ ${colors.cyan}data${colors.reset} Dynamic data binding ("articles", "events:5")
204
+
205
+ ${colors.bright}Example:${colors.reset}
206
+ ${colors.dim}export default {
207
+ title: 'Hero Banner',
208
+ description: 'Bold hero section with headline and CTA',
209
+ family: 'hero',
210
+ background: true,
211
+
212
+ content: {
213
+ pretitle: 'Eyebrow text',
214
+ title: 'Headline',
215
+ paragraphs: 'Description [1-2]',
216
+ links: 'CTA buttons [1-2]',
217
+ },
218
+
219
+ params: {
220
+ theme: {
221
+ type: 'select',
222
+ options: ['gradient', 'glass', 'dark'],
223
+ default: 'gradient',
224
+ },
225
+ layout: {
226
+ type: 'select',
227
+ options: ['center', 'left', 'split'],
228
+ default: 'center',
229
+ },
230
+ },
231
+
232
+ presets: {
233
+ default: { label: 'Centered', params: { theme: 'gradient', layout: 'center' } },
234
+ minimal: { label: 'Minimal', params: { theme: 'light', layout: 'left' } },
235
+ },
236
+ }${colors.reset}
237
+
238
+ ${colors.bright}Runtime Benefits:${colors.reset}
239
+ - Content structure is guaranteed (no null checks needed)
240
+ - Param defaults are applied automatically
241
+ - Components receive clean { content, params } props
242
+
243
+ ${colors.dim}Full guide: https://github.com/uniweb/cli/blob/main/docs/component-metadata.md${colors.reset}
244
+ `
245
+
246
+ // =============================================================================
247
+ // Subcommand Handlers
248
+ // =============================================================================
249
+
250
+ function showSiteReference() {
251
+ log(SITE_REFERENCE)
252
+ }
253
+
254
+ function showPageReference() {
255
+ log(PAGE_REFERENCE)
256
+ }
257
+
258
+ function showMetaReference() {
259
+ log(META_REFERENCE)
260
+ }
261
+
262
+ // =============================================================================
263
+ // Original Docs Functionality
264
+ // =============================================================================
265
+
266
+ /**
267
+ * Parse command line arguments
268
+ */
269
+ function parseArgs(args) {
270
+ const options = {
271
+ output: 'COMPONENTS.md',
272
+ fromSource: false,
273
+ target: null
274
+ }
275
+
276
+ for (let i = 0; i < args.length; i++) {
277
+ const arg = args[i]
278
+
279
+ if (arg === '--output' || arg === '-o') {
280
+ options.output = args[++i]
281
+ } else if (arg === '--from-source' || arg === '-s') {
282
+ options.fromSource = true
283
+ } else if (arg === '--target' || arg === '-t') {
284
+ options.target = args[++i]
285
+ } else if (arg === '--help' || arg === '-h') {
286
+ options.help = true
287
+ }
288
+ }
289
+
290
+ return options
291
+ }
292
+
293
+ /**
294
+ * Show help message
295
+ */
296
+ function showHelp() {
297
+ log(`
298
+ ${colors.bright}uniweb docs${colors.reset} - Documentation and configuration references
299
+
300
+ ${colors.dim}Usage:${colors.reset}
301
+ uniweb docs Generate COMPONENTS.md
302
+ uniweb docs site Show site.yml configuration reference
303
+ uniweb docs page Show page.yml configuration reference
304
+ uniweb docs meta Show component meta.js reference
305
+
306
+ ${colors.dim}Generation Options:${colors.reset}
307
+ -o, --output <file> Output filename (default: COMPONENTS.md)
308
+ -s, --from-source Read meta.js files directly instead of schema.json
309
+ -t, --target <path> Foundation directory (auto-detected if not specified)
310
+ -h, --help Show this help message
311
+
312
+ ${colors.dim}Examples:${colors.reset}
313
+ uniweb docs Generate component docs
314
+ uniweb docs site Quick reference for site.yml options
315
+ uniweb docs page Quick reference for page.yml options
316
+ uniweb docs meta Learn about component meta.js schema
317
+ uniweb docs --output DOCS.md Custom output filename
318
+ uniweb docs --from-source Generate without building first
319
+
320
+ ${colors.dim}Notes:${colors.reset}
321
+ Run from a foundation directory to generate docs there.
322
+ Run from a site directory to auto-detect the linked foundation.
323
+ Run from workspace root to auto-detect foundations.
324
+ `)
325
+ }
326
+
327
+ /**
328
+ * Detect if current directory is a foundation
329
+ */
330
+ function isFoundation(dir) {
331
+ return classifyPackage(dir) === 'foundation'
332
+ }
333
+
334
+ /**
335
+ * Detect if current directory is a site
336
+ */
337
+ function isSite(dir) {
338
+ // Use the canonical classifier; also accept legacy `site.yaml`.
339
+ return classifyPackage(dir) === 'site' || existsSync(join(dir, 'site.yaml'))
340
+ }
341
+
342
+ /**
343
+ * Resolve foundation path from a site directory
344
+ * Reads package.json to find foundation dependency with file: protocol
345
+ */
346
+ async function resolveFoundationFromSite(siteDir) {
347
+ const pkgPath = join(siteDir, 'package.json')
348
+ if (!existsSync(pkgPath)) {
349
+ return null
350
+ }
351
+
352
+ try {
353
+ const pkg = JSON.parse(await readFile(pkgPath, 'utf-8'))
354
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies }
355
+
356
+ // Look for foundation dependency with file: protocol
357
+ // Common names: "foundation", or the foundation name from site.yml
358
+ for (const [name, version] of Object.entries(deps)) {
359
+ if (typeof version === 'string' && version.startsWith('file:')) {
360
+ const relativePath = version.slice(5) // Remove 'file:' prefix
361
+ const absolutePath = resolve(siteDir, relativePath)
362
+
363
+ // Check if this is actually a foundation
364
+ if (isFoundation(absolutePath)) {
365
+ return { name, path: absolutePath }
366
+ }
367
+ }
368
+ }
369
+ } catch {
370
+ // Ignore parse errors
371
+ }
372
+
373
+ return null
374
+ }
375
+
376
+ /**
377
+ * Generate component documentation (original functionality)
378
+ */
379
+ async function generateComponentDocs(args) {
380
+ const options = parseArgs(args)
381
+
382
+ const projectDir = resolve(process.cwd())
383
+ let foundationDir = projectDir
384
+ let outputDir = projectDir
385
+
386
+ // If --target specified, use it directly
387
+ if (options.target) {
388
+ foundationDir = resolve(projectDir, options.target)
389
+ outputDir = foundationDir
390
+ if (!isFoundation(foundationDir)) {
391
+ error(
392
+ `Target directory does not appear to be a foundation: ${options.target}`
393
+ )
394
+ log(
395
+ `${colors.dim}Foundations have a src/components/ directory.${colors.reset}`
396
+ )
397
+ process.exit(1)
398
+ }
399
+ info(`Using foundation: ${options.target}`)
400
+ }
401
+ // Check if we're at workspace root
402
+ else if (isWorkspaceRoot(projectDir)) {
403
+ const foundations = await findFoundations(projectDir)
404
+
405
+ if (foundations.length === 0) {
406
+ error('No foundations found in this workspace.')
407
+ log(
408
+ `${colors.dim}Foundations have @uniweb/build in devDependencies.${colors.reset}`
409
+ )
410
+ process.exit(1)
411
+ }
412
+
413
+ let targetFoundation
414
+ if (foundations.length === 1) {
415
+ targetFoundation = foundations[0]
416
+ info(`Found foundation: ${targetFoundation}`)
417
+ } else if (isNonInteractive(process.argv)) {
418
+ error(`Multiple foundations found. Specify which to target:\n`)
419
+ log(
420
+ formatOptions(foundations.map((f) => ({ label: f, description: '' })))
421
+ )
422
+ log('')
423
+ log(`Usage: ${getCliPrefix()} docs --target <path>`)
424
+ process.exit(1)
425
+ } else {
426
+ log(
427
+ `${colors.dim}Multiple foundations found in workspace.${colors.reset}\n`
428
+ )
429
+ targetFoundation = await promptSelect('Select foundation:', foundations)
430
+ if (!targetFoundation) {
431
+ log('Cancelled.')
432
+ process.exit(0)
433
+ }
434
+ }
435
+
436
+ foundationDir = resolve(projectDir, targetFoundation)
437
+ outputDir = foundationDir
438
+ }
439
+ // Check if we're in a site directory
440
+ else if (isSite(projectDir)) {
441
+ const foundation = await resolveFoundationFromSite(projectDir)
442
+ if (!foundation) {
443
+ error('Could not find a linked foundation in this site.')
444
+ log(
445
+ `${colors.dim}Make sure package.json has a foundation dependency like:${colors.reset}`
446
+ )
447
+ log(`${colors.dim} "foundation": "file:../foundation"${colors.reset}`)
448
+ process.exit(1)
449
+ }
450
+
451
+ foundationDir = foundation.path
452
+ outputDir = projectDir
453
+ info(`Found foundation: ${foundation.name} (${foundation.path})`)
454
+ } else if (!isFoundation(projectDir)) {
455
+ error('This directory does not appear to be a foundation or site.')
456
+ log(
457
+ `${colors.dim}Foundations have a src/components/ directory.${colors.reset}`
458
+ )
459
+ log(`${colors.dim}Sites have a site.yml file.${colors.reset}`)
460
+ process.exit(1)
461
+ }
462
+
463
+ // Check if schema.json exists (if not using --from-source)
464
+ const schemaPath = join(foundationDir, 'dist', 'schema.json')
465
+ if (!options.fromSource && !existsSync(schemaPath)) {
466
+ log(
467
+ `${colors.yellow}⚠${colors.reset} No dist/schema.json found in foundation.`
468
+ )
469
+ log(
470
+ `${colors.dim}Run 'uniweb build' in the foundation first, or use '--from-source'.${colors.reset}`
471
+ )
472
+ log('')
473
+ info('Falling back to --from-source mode')
474
+ options.fromSource = true
475
+ }
476
+
477
+ try {
478
+ info('Generating documentation...')
479
+
480
+ const result = await generateDocs(foundationDir, {
481
+ output: join(outputDir, options.output),
482
+ fromSource: options.fromSource
483
+ })
484
+
485
+ success(`Generated ${result.outputPath}`)
486
+ log(
487
+ `${colors.dim}Documented ${result.componentCount} components${colors.reset}`
488
+ )
489
+ } catch (err) {
490
+ error(`Failed to generate docs: ${err.message}`)
491
+ process.exit(1)
492
+ }
493
+ }
494
+
495
+ // =============================================================================
496
+ // Main Command Entry Point
497
+ // =============================================================================
498
+
499
+ /**
500
+ * Main docs command - handles subcommands and generates documentation
501
+ */
502
+ export async function docs(args) {
503
+ // Check for subcommand as first argument
504
+ const firstArg = args[0]
505
+
506
+ // Handle reference subcommands
507
+ if (firstArg === 'site') {
508
+ showSiteReference()
509
+ return
510
+ }
511
+
512
+ if (firstArg === 'page') {
513
+ showPageReference()
514
+ return
515
+ }
516
+
517
+ if (firstArg === 'meta') {
518
+ showMetaReference()
519
+ return
520
+ }
521
+
522
+ // Handle help
523
+ if (firstArg === '--help' || firstArg === '-h') {
524
+ showHelp()
525
+ return
526
+ }
527
+
528
+ // Default: generate component documentation
529
+ await generateComponentDocs(args)
530
+ }