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.
- package/LICENSE +201 -0
- package/README.md +532 -3
- package/package.json +60 -11
- package/partials/adding-pages.hbs +77 -0
- package/partials/agents.md +3000 -0
- package/partials/ai-assistance.hbs +28 -0
- package/partials/components-docs.hbs +13 -0
- package/partials/config-reference.hbs +66 -0
- package/partials/deployment.hbs +13 -0
- package/partials/learn-more.hbs +6 -0
- package/partials/quick-start.hbs +8 -0
- package/partials/search-docs.hbs +110 -0
- package/src/backend/asset-download.js +222 -0
- package/src/backend/client.js +986 -0
- package/src/backend/foundation-bring-along.js +692 -0
- package/src/backend/payment-handoff.js +362 -0
- package/src/backend/service-request.js +210 -0
- package/src/backend/site-media.js +255 -0
- package/src/backend/site-preview.js +77 -0
- package/src/backend/site-sync.js +2515 -0
- package/src/backend/workspace.js +234 -0
- package/src/commands/add.js +1900 -0
- package/src/commands/build.js +1214 -0
- package/src/commands/clone.js +466 -0
- package/src/commands/content.js +202 -0
- package/src/commands/deploy.js +537 -0
- package/src/commands/dev.js +202 -0
- package/src/commands/docs.js +530 -0
- package/src/commands/doctor.js +1982 -0
- package/src/commands/export.js +116 -0
- package/src/commands/families.js +82 -0
- package/src/commands/forget.js +197 -0
- package/src/commands/handoff.js +21 -0
- package/src/commands/i18n.js +1707 -0
- package/src/commands/inspect.js +375 -0
- package/src/commands/invite.js +24 -0
- package/src/commands/org.js +96 -0
- package/src/commands/publish.js +1087 -0
- package/src/commands/pull.js +1257 -0
- package/src/commands/push.js +663 -0
- package/src/commands/refresh.js +212 -0
- package/src/commands/register.js +1092 -0
- package/src/commands/rename.js +647 -0
- package/src/commands/site.js +358 -0
- package/src/commands/snapshot.js +461 -0
- package/src/commands/starter.js +206 -0
- package/src/commands/status.js +302 -0
- package/src/commands/sync.js +93 -0
- package/src/commands/template.js +107 -0
- package/src/commands/update.js +887 -0
- package/src/commands/validate.js +376 -0
- package/src/framework-index.json +248 -0
- package/src/index.js +2241 -0
- package/src/templates/fetchers/github.js +113 -0
- package/src/templates/fetchers/npm.js +85 -0
- package/src/templates/fetchers/release.js +92 -0
- package/src/templates/index.js +190 -0
- package/src/templates/processor.js +333 -0
- package/src/templates/resolver.js +232 -0
- package/src/templates/validator.js +155 -0
- package/src/utils/agents-stamp.js +110 -0
- package/src/utils/args.js +126 -0
- package/src/utils/asset-upload.js +270 -0
- package/src/utils/bytes.js +30 -0
- package/src/utils/code-upload.js +394 -0
- package/src/utils/config.js +417 -0
- package/src/utils/conformance.js +274 -0
- package/src/utils/dep-survey.js +176 -0
- package/src/utils/destination-prompt.js +146 -0
- package/src/utils/discover.js +75 -0
- package/src/utils/env.js +22 -0
- package/src/utils/exit.js +30 -0
- package/src/utils/fetch-retry.js +109 -0
- package/src/utils/flag-guard.js +246 -0
- package/src/utils/foundation-name.js +179 -0
- package/src/utils/git.js +342 -0
- package/src/utils/host-prompt.js +51 -0
- package/src/utils/install-integrity.js +210 -0
- package/src/utils/interactive.js +118 -0
- package/src/utils/json-file.js +72 -0
- package/src/utils/names.js +91 -0
- package/src/utils/placement.js +151 -0
- package/src/utils/pm.js +165 -0
- package/src/utils/preview.js +20 -0
- package/src/utils/pull-written.js +132 -0
- package/src/utils/records-guard.js +88 -0
- package/src/utils/registry-auth.js +994 -0
- package/src/utils/registry-orgs.js +290 -0
- package/src/utils/scaffold.js +483 -0
- package/src/utils/schemaless-report.js +30 -0
- package/src/utils/semver-precedence.js +80 -0
- package/src/utils/session-file.js +95 -0
- package/src/utils/site-content-refs.js +23 -0
- package/src/utils/site-copies.js +123 -0
- package/src/utils/site-data-upload.js +302 -0
- package/src/utils/site-identity.js +137 -0
- package/src/utils/snapshot-settings.js +112 -0
- package/src/utils/update-check.js +302 -0
- package/src/utils/uwx-read.js +157 -0
- package/src/utils/workspace.js +223 -0
- package/src/versions.js +467 -0
- package/starter/foundation/main.js +50 -0
- package/starter/foundation/sections/Section/index.jsx +95 -0
- package/starter/foundation/sections/Section/meta.js +44 -0
- package/starter/site/pages/about/1-about.md.hbs +13 -0
- package/starter/site/pages/about/page.yml +3 -0
- package/starter/site/pages/home/1-welcome.md.hbs +14 -0
- package/starter/site/pages/home/page.yml +3 -0
- package/templates/foundation/_gitignore +5 -0
- package/templates/foundation/components/.gitkeep +0 -0
- package/templates/foundation/main.js.hbs +33 -0
- package/templates/foundation/package.json.hbs +51 -0
- package/templates/foundation/sections/.gitkeep +0 -0
- package/templates/foundation/styles.css +6 -0
- package/templates/foundation/vite.config.js +3 -0
- package/templates/site/_gitignore +12 -0
- package/templates/site/entry.js +7 -0
- package/templates/site/index.html.hbs +13 -0
- package/templates/site/package.json.hbs +27 -0
- package/templates/site/public/favicon.svg +7 -0
- package/templates/site/site.yml.hbs +91 -0
- package/templates/site/theme.yml +99 -0
- package/templates/site/vite.config.js +7 -0
- package/templates/workspace/AGENTS.md.hbs +2 -0
- package/templates/workspace/README.md.hbs +87 -0
- package/templates/workspace/_gitignore +29 -0
- package/templates/workspace/_vscode/settings.json +6 -0
- package/templates/workspace/package.json.hbs +20 -0
- 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
|
+
}
|