@uniweb/build 0.1.21 → 0.1.23
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/package.json +2 -2
- package/src/docs.js +44 -56
- package/src/prerender.js +22 -10
- package/src/site/content-collector.js +4 -4
- package/src/site/plugin.js +26 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/build",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.23",
|
|
4
4
|
"description": "Build tooling for the Uniweb Component Web Platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"@tailwindcss/vite": "^4.0.0",
|
|
60
60
|
"@vitejs/plugin-react": "^4.0.0 || ^5.0.0",
|
|
61
61
|
"vite-plugin-svgr": "^4.0.0",
|
|
62
|
-
"@uniweb/core": "0.1.
|
|
62
|
+
"@uniweb/core": "0.1.10"
|
|
63
63
|
},
|
|
64
64
|
"peerDependenciesMeta": {
|
|
65
65
|
"vite": {
|
package/src/docs.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { readFile, writeFile } from 'node:fs/promises'
|
|
9
9
|
import { existsSync } from 'node:fs'
|
|
10
|
-
import { join } from 'node:path'
|
|
10
|
+
import { join, isAbsolute } from 'node:path'
|
|
11
11
|
import { buildSchema } from './schema.js'
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -30,51 +30,39 @@ function generateComponentDocs(name, meta) {
|
|
|
30
30
|
lines.push('')
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
//
|
|
34
|
-
if (meta.category) {
|
|
35
|
-
lines.push(`**Category:** ${meta.category}`)
|
|
36
|
-
lines.push('')
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Content Elements
|
|
40
|
-
if (meta.elements && Object.keys(meta.elements).length > 0) {
|
|
41
|
-
lines.push('### Content Elements')
|
|
42
|
-
lines.push('')
|
|
43
|
-
lines.push('| Element | Label | Required | Description |')
|
|
44
|
-
lines.push('|---------|-------|----------|-------------|')
|
|
45
|
-
|
|
46
|
-
for (const [key, element] of Object.entries(meta.elements)) {
|
|
47
|
-
const label = element.label || key
|
|
48
|
-
const required = element.required ? 'Yes' : ''
|
|
49
|
-
const description = element.description || ''
|
|
50
|
-
lines.push(`| \`${key}\` | ${label} | ${required} | ${description} |`)
|
|
51
|
-
}
|
|
52
|
-
lines.push('')
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Parameters/Properties
|
|
33
|
+
// Parameters/Properties (shown first - most important for content authors)
|
|
56
34
|
if (meta.properties && Object.keys(meta.properties).length > 0) {
|
|
57
35
|
lines.push('### Parameters')
|
|
58
36
|
lines.push('')
|
|
59
|
-
lines.push('| Parameter | Type | Default | Description |')
|
|
60
|
-
lines.push('|-----------|------|---------|-------------|')
|
|
61
37
|
|
|
62
38
|
for (const [key, prop] of Object.entries(meta.properties)) {
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
39
|
+
const defaultVal = prop.default !== undefined ? prop.default : ''
|
|
40
|
+
|
|
41
|
+
// Parameter name with default
|
|
42
|
+
if (defaultVal !== '') {
|
|
43
|
+
lines.push(`**${key}** = \`${defaultVal}\``)
|
|
44
|
+
} else {
|
|
45
|
+
lines.push(`**${key}**`)
|
|
46
|
+
}
|
|
66
47
|
|
|
67
|
-
//
|
|
48
|
+
// For select type, show options on next line
|
|
68
49
|
if (prop.type === 'select' && prop.options) {
|
|
69
50
|
const optionValues = prop.options.map(o =>
|
|
70
51
|
typeof o === 'object' ? o.value : o
|
|
71
|
-
).join('
|
|
72
|
-
|
|
52
|
+
).join(' | ')
|
|
53
|
+
lines.push(` ${optionValues}`)
|
|
54
|
+
} else if (prop.type === 'boolean') {
|
|
55
|
+
// For boolean, show the label as description
|
|
56
|
+
if (prop.label) {
|
|
57
|
+
lines.push(` ${prop.label}`)
|
|
58
|
+
}
|
|
59
|
+
} else if (prop.label) {
|
|
60
|
+
// For other types, show label
|
|
61
|
+
lines.push(` ${prop.label}`)
|
|
73
62
|
}
|
|
74
63
|
|
|
75
|
-
lines.push(
|
|
64
|
+
lines.push('')
|
|
76
65
|
}
|
|
77
|
-
lines.push('')
|
|
78
66
|
}
|
|
79
67
|
|
|
80
68
|
// Presets
|
|
@@ -88,11 +76,24 @@ function generateComponentDocs(name, meta) {
|
|
|
88
76
|
.map(([k, v]) => `${k}: ${v}`)
|
|
89
77
|
.join(', ')
|
|
90
78
|
: ''
|
|
91
|
-
lines.push(`- **${preset.name}
|
|
79
|
+
lines.push(`- **${preset.name}**${settings ? ` — ${settings}` : ''}`)
|
|
92
80
|
}
|
|
93
81
|
lines.push('')
|
|
94
82
|
}
|
|
95
83
|
|
|
84
|
+
// Content Elements (condensed - less important)
|
|
85
|
+
if (meta.elements && Object.keys(meta.elements).length > 0) {
|
|
86
|
+
const elements = Object.entries(meta.elements)
|
|
87
|
+
const elementList = elements.map(([key, el]) => {
|
|
88
|
+
return el.required ? `${key} (required)` : key
|
|
89
|
+
}).join(', ')
|
|
90
|
+
|
|
91
|
+
lines.push('### Content')
|
|
92
|
+
lines.push('')
|
|
93
|
+
lines.push(elementList)
|
|
94
|
+
lines.push('')
|
|
95
|
+
}
|
|
96
|
+
|
|
96
97
|
return lines.join('\n')
|
|
97
98
|
}
|
|
98
99
|
|
|
@@ -112,17 +113,11 @@ export function generateDocsFromSchema(schema, options = {}) {
|
|
|
112
113
|
lines.push(`# ${title}`)
|
|
113
114
|
lines.push('')
|
|
114
115
|
|
|
115
|
-
// Foundation
|
|
116
|
+
// Foundation description
|
|
116
117
|
const foundationMeta = schema._self
|
|
117
|
-
if (foundationMeta) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
lines.push('')
|
|
121
|
-
}
|
|
122
|
-
if (foundationMeta.description) {
|
|
123
|
-
lines.push(foundationMeta.description)
|
|
124
|
-
lines.push('')
|
|
125
|
-
}
|
|
118
|
+
if (foundationMeta?.description) {
|
|
119
|
+
lines.push(foundationMeta.description)
|
|
120
|
+
lines.push('')
|
|
126
121
|
}
|
|
127
122
|
|
|
128
123
|
lines.push('---')
|
|
@@ -134,11 +129,7 @@ export function generateDocsFromSchema(schema, options = {}) {
|
|
|
134
129
|
if (componentNames.length > 0) {
|
|
135
130
|
lines.push('## Components')
|
|
136
131
|
lines.push('')
|
|
137
|
-
|
|
138
|
-
const meta = schema[name]
|
|
139
|
-
const title = meta.title || name
|
|
140
|
-
lines.push(`- [${title}](#${name.toLowerCase()}) - ${meta.description || ''}`)
|
|
141
|
-
}
|
|
132
|
+
lines.push(componentNames.map(name => `[${name}](#${name.toLowerCase()})`).join(' · '))
|
|
142
133
|
lines.push('')
|
|
143
134
|
lines.push('---')
|
|
144
135
|
lines.push('')
|
|
@@ -152,10 +143,7 @@ export function generateDocsFromSchema(schema, options = {}) {
|
|
|
152
143
|
lines.push('')
|
|
153
144
|
}
|
|
154
145
|
|
|
155
|
-
|
|
156
|
-
lines.push('*Generated from foundation schema*')
|
|
157
|
-
|
|
158
|
-
return lines.join('\n')
|
|
146
|
+
return lines.join('\n').trim() + '\n'
|
|
159
147
|
}
|
|
160
148
|
|
|
161
149
|
/**
|
|
@@ -206,8 +194,8 @@ export async function generateDocs(foundationDir, options = {}) {
|
|
|
206
194
|
// Generate markdown
|
|
207
195
|
const markdown = generateDocsFromSchema(schema, { title })
|
|
208
196
|
|
|
209
|
-
// Write output
|
|
210
|
-
const outputPath = join(foundationDir, output)
|
|
197
|
+
// Write output (support absolute paths for site-based generation)
|
|
198
|
+
const outputPath = isAbsolute(output) ? output : join(foundationDir, output)
|
|
211
199
|
await writeFile(outputPath, markdown)
|
|
212
200
|
|
|
213
201
|
// Count components
|
package/src/prerender.js
CHANGED
|
@@ -118,11 +118,21 @@ export async function prerenderSite(siteDir, options = {}) {
|
|
|
118
118
|
const pages = uniweb.activeWebsite.pages
|
|
119
119
|
|
|
120
120
|
for (const page of pages) {
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
// Determine which routes to render this page at
|
|
122
|
+
// Index pages are rendered at both their actual route and their nav route
|
|
123
|
+
const routesToRender = [page.route]
|
|
124
|
+
if (page.isIndex) {
|
|
125
|
+
const navRoute = page.getNavRoute()
|
|
126
|
+
if (navRoute !== page.route) {
|
|
127
|
+
routesToRender.push(navRoute)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Render once, output to multiple paths
|
|
132
|
+
onProgress(`Rendering ${routesToRender[0]}...`)
|
|
123
133
|
|
|
124
134
|
// Set this as the active page
|
|
125
|
-
uniweb.activeWebsite.setActivePage(route)
|
|
135
|
+
uniweb.activeWebsite.setActivePage(page.route)
|
|
126
136
|
|
|
127
137
|
// Create the page element
|
|
128
138
|
// Note: We don't need StaticRouter for SSG since we're just rendering
|
|
@@ -134,7 +144,7 @@ export async function prerenderSite(siteDir, options = {}) {
|
|
|
134
144
|
try {
|
|
135
145
|
renderedContent = renderToString(element)
|
|
136
146
|
} catch (err) {
|
|
137
|
-
console.warn(`Warning: Failed to render ${route}: ${err.message}`)
|
|
147
|
+
console.warn(`Warning: Failed to render ${page.route}: ${err.message}`)
|
|
138
148
|
if (process.env.DEBUG) {
|
|
139
149
|
console.error(err.stack)
|
|
140
150
|
}
|
|
@@ -144,13 +154,15 @@ export async function prerenderSite(siteDir, options = {}) {
|
|
|
144
154
|
// Inject into shell
|
|
145
155
|
const html = injectContent(htmlShell, renderedContent, page, siteContent)
|
|
146
156
|
|
|
147
|
-
//
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
157
|
+
// Output to all routes for this page
|
|
158
|
+
for (const route of routesToRender) {
|
|
159
|
+
const outputPath = getOutputPath(distDir, route)
|
|
160
|
+
await mkdir(dirname(outputPath), { recursive: true })
|
|
161
|
+
await writeFile(outputPath, html)
|
|
151
162
|
|
|
152
|
-
|
|
153
|
-
|
|
163
|
+
renderedFiles.push(outputPath)
|
|
164
|
+
onProgress(` → ${outputPath.replace(distDir, 'dist')}`)
|
|
165
|
+
}
|
|
154
166
|
}
|
|
155
167
|
|
|
156
168
|
onProgress(`Pre-rendered ${renderedFiles.length} pages`)
|
|
@@ -351,11 +351,10 @@ async function processPage(pagePath, pageName, siteRoot, { isIndex = false, pare
|
|
|
351
351
|
}
|
|
352
352
|
|
|
353
353
|
// Determine route
|
|
354
|
+
// All pages get their actual folder-based route (no special treatment for index)
|
|
355
|
+
// The isIndex flag marks which page should also be accessible at the parent route
|
|
354
356
|
let route
|
|
355
|
-
if (
|
|
356
|
-
// Index page gets the parent route
|
|
357
|
-
route = parentRoute
|
|
358
|
-
} else if (pageName.startsWith('@')) {
|
|
357
|
+
if (pageName.startsWith('@')) {
|
|
359
358
|
// Special pages (layout areas) keep their @ prefix
|
|
360
359
|
route = parentRoute === '/' ? `/@${pageName.slice(1)}` : `${parentRoute}/@${pageName.slice(1)}`
|
|
361
360
|
} else {
|
|
@@ -369,6 +368,7 @@ async function processPage(pagePath, pageName, siteRoot, { isIndex = false, pare
|
|
|
369
368
|
return {
|
|
370
369
|
page: {
|
|
371
370
|
route,
|
|
371
|
+
isIndex, // Marks this page as the index for its parent route (accessible at parentRoute)
|
|
372
372
|
title: pageConfig.title || pageName,
|
|
373
373
|
description: pageConfig.description || '',
|
|
374
374
|
label: pageConfig.label || null, // Short label for navigation (defaults to title)
|
package/src/site/plugin.js
CHANGED
|
@@ -295,7 +295,9 @@ export function siteContentPlugin(options = {}) {
|
|
|
295
295
|
|
|
296
296
|
// Watch for content changes in dev mode
|
|
297
297
|
if (shouldWatch) {
|
|
298
|
-
const
|
|
298
|
+
const pagesPath = resolve(resolvedSitePath, pagesDir)
|
|
299
|
+
const siteYmlPath = resolve(resolvedSitePath, 'site.yml')
|
|
300
|
+
const themeYmlPath = resolve(resolvedSitePath, 'theme.yml')
|
|
299
301
|
|
|
300
302
|
// Debounce rebuilds
|
|
301
303
|
let rebuildTimeout = null
|
|
@@ -315,12 +317,33 @@ export function siteContentPlugin(options = {}) {
|
|
|
315
317
|
}, 100)
|
|
316
318
|
}
|
|
317
319
|
|
|
320
|
+
// Track all watchers for cleanup
|
|
321
|
+
const watchers = []
|
|
322
|
+
|
|
323
|
+
// Watch pages directory
|
|
318
324
|
try {
|
|
319
|
-
|
|
320
|
-
console.log(`[site-content] Watching ${
|
|
325
|
+
watchers.push(watch(pagesPath, { recursive: true }, scheduleRebuild))
|
|
326
|
+
console.log(`[site-content] Watching ${pagesPath}`)
|
|
321
327
|
} catch (err) {
|
|
322
328
|
console.warn('[site-content] Could not watch pages directory:', err.message)
|
|
323
329
|
}
|
|
330
|
+
|
|
331
|
+
// Watch site.yml
|
|
332
|
+
try {
|
|
333
|
+
watchers.push(watch(siteYmlPath, scheduleRebuild))
|
|
334
|
+
} catch (err) {
|
|
335
|
+
// site.yml may not exist, that's ok
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Watch theme.yml
|
|
339
|
+
try {
|
|
340
|
+
watchers.push(watch(themeYmlPath, scheduleRebuild))
|
|
341
|
+
} catch (err) {
|
|
342
|
+
// theme.yml may not exist, that's ok
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Store watchers for cleanup
|
|
346
|
+
watcher = { close: () => watchers.forEach(w => w.close()) }
|
|
324
347
|
}
|
|
325
348
|
|
|
326
349
|
// Serve content and SEO files
|