@uniweb/build 0.1.32 → 0.2.0
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 +30 -1
- package/package.json +3 -3
- package/src/docs.js +2 -2
- package/src/generate-entry.js +26 -5
- package/src/index.js +2 -1
- package/src/prerender.js +18 -2
- package/src/schema.js +78 -11
- package/src/site/config.js +2 -1
- package/src/site/content-collector.js +265 -20
- package/src/site/plugin.js +14 -4
- package/src/theme/css-generator.js +341 -0
- package/src/theme/index.js +65 -0
- package/src/theme/processor.js +422 -0
- package/src/theme/shade-generator.js +666 -0
package/README.md
CHANGED
|
@@ -333,6 +333,34 @@ dist/
|
|
|
333
333
|
└── default.webp
|
|
334
334
|
```
|
|
335
335
|
|
|
336
|
+
### Schema.json Structure
|
|
337
|
+
|
|
338
|
+
The generated `schema.json` contains:
|
|
339
|
+
|
|
340
|
+
```json
|
|
341
|
+
{
|
|
342
|
+
"_self": {
|
|
343
|
+
"name": "foundation",
|
|
344
|
+
"version": "0.1.0",
|
|
345
|
+
"description": "My foundation description",
|
|
346
|
+
"vars": { ... }
|
|
347
|
+
},
|
|
348
|
+
"Hero": { ... },
|
|
349
|
+
"Features": { ... }
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
The `_self` object contains foundation-level metadata:
|
|
354
|
+
|
|
355
|
+
| Field | Source | Description |
|
|
356
|
+
|-------|--------|-------------|
|
|
357
|
+
| `name` | `package.json` | Foundation package name |
|
|
358
|
+
| `version` | `package.json` | Foundation version |
|
|
359
|
+
| `description` | `package.json` | Foundation description |
|
|
360
|
+
| `vars` | `foundation.js` | CSS custom properties sites can override |
|
|
361
|
+
|
|
362
|
+
Identity fields (`name`, `version`, `description`) come from the foundation's `package.json`. Configuration fields (`vars`, etc.) come from `src/foundation.js`.
|
|
363
|
+
|
|
336
364
|
## API Reference
|
|
337
365
|
|
|
338
366
|
### Schema Functions
|
|
@@ -341,7 +369,8 @@ dist/
|
|
|
341
369
|
|----------|-------------|
|
|
342
370
|
| `discoverComponents(srcDir)` | Discover all exposed components |
|
|
343
371
|
| `loadComponentMeta(componentDir)` | Load meta file for a component |
|
|
344
|
-
| `
|
|
372
|
+
| `loadPackageJson(srcDir)` | Load identity from package.json |
|
|
373
|
+
| `loadFoundationConfig(srcDir)` | Load foundation.js configuration |
|
|
345
374
|
| `buildSchema(srcDir)` | Build complete schema object |
|
|
346
375
|
|
|
347
376
|
### Entry Generation
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/build",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Build tooling for the Uniweb Component Web Platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
},
|
|
52
52
|
"optionalDependencies": {
|
|
53
53
|
"@uniweb/content-reader": "1.0.4",
|
|
54
|
-
"@uniweb/runtime": "0.2.
|
|
54
|
+
"@uniweb/runtime": "0.2.20"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"@tailwindcss/vite": "^4.0.0",
|
|
61
61
|
"@vitejs/plugin-react": "^4.0.0 || ^5.0.0",
|
|
62
62
|
"vite-plugin-svgr": "^4.0.0",
|
|
63
|
-
"@uniweb/core": "0.
|
|
63
|
+
"@uniweb/core": "0.2.0"
|
|
64
64
|
},
|
|
65
65
|
"peerDependenciesMeta": {
|
|
66
66
|
"vite": {
|
package/src/docs.js
CHANGED
|
@@ -165,8 +165,8 @@ export async function generateDocs(foundationDir, options = {}) {
|
|
|
165
165
|
|
|
166
166
|
let schema
|
|
167
167
|
|
|
168
|
-
// Try to load schema.json from dist
|
|
169
|
-
const schemaPath = join(foundationDir, 'dist', 'schema.json')
|
|
168
|
+
// Try to load schema.json from dist/meta (where foundation build outputs it)
|
|
169
|
+
const schemaPath = join(foundationDir, 'dist', 'meta', 'schema.json')
|
|
170
170
|
|
|
171
171
|
if (!fromSource && existsSync(schemaPath)) {
|
|
172
172
|
// Load from existing schema.json
|
package/src/generate-entry.js
CHANGED
|
@@ -26,20 +26,41 @@ import { discoverComponents } from './schema.js'
|
|
|
26
26
|
import { extractAllRuntimeSchemas } from './runtime-schema.js'
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
|
-
* Detect foundation exports file (for custom Layout, props, etc.)
|
|
30
|
-
*
|
|
29
|
+
* Detect foundation config/exports file (for custom Layout, props, vars, etc.)
|
|
30
|
+
*
|
|
31
|
+
* Looks for (in order of preference):
|
|
32
|
+
* 1. foundation.js - New consolidated format
|
|
33
|
+
* 2. exports.js - Legacy format (for backward compatibility)
|
|
34
|
+
*
|
|
35
|
+
* The file should export:
|
|
36
|
+
* - Layout (optional) - Custom page layout component
|
|
37
|
+
* - props (optional) - Foundation-wide props
|
|
38
|
+
* - vars (optional) - CSS custom properties (also read by schema builder)
|
|
31
39
|
*/
|
|
32
40
|
function detectFoundationExports(srcDir) {
|
|
33
|
-
|
|
41
|
+
// Prefer foundation.js (new consolidated format)
|
|
42
|
+
const foundationCandidates = [
|
|
43
|
+
{ path: 'foundation.js', ext: 'js' },
|
|
44
|
+
{ path: 'foundation.jsx', ext: 'jsx' },
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
for (const { path, ext } of foundationCandidates) {
|
|
48
|
+
if (existsSync(join(srcDir, path))) {
|
|
49
|
+
return { path: `./${path}`, ext, isFoundationJs: true }
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Fall back to exports.js (legacy format)
|
|
54
|
+
const legacyCandidates = [
|
|
34
55
|
{ path: 'exports.js', ext: 'js' },
|
|
35
56
|
{ path: 'exports.jsx', ext: 'jsx' },
|
|
36
57
|
{ path: 'exports/index.js', ext: 'js' },
|
|
37
58
|
{ path: 'exports/index.jsx', ext: 'jsx' },
|
|
38
59
|
]
|
|
39
60
|
|
|
40
|
-
for (const { path, ext } of
|
|
61
|
+
for (const { path, ext } of legacyCandidates) {
|
|
41
62
|
if (existsSync(join(srcDir, path))) {
|
|
42
|
-
return { path: `./${path.replace(/\/index\.(js|jsx)$/, '')}`, ext }
|
|
63
|
+
return { path: `./${path.replace(/\/index\.(js|jsx)$/, '')}`, ext, isFoundationJs: false }
|
|
43
64
|
}
|
|
44
65
|
}
|
|
45
66
|
return null
|
package/src/index.js
CHANGED
package/src/prerender.js
CHANGED
|
@@ -306,9 +306,11 @@ function renderBlock(block) {
|
|
|
306
306
|
}
|
|
307
307
|
|
|
308
308
|
// Wrapper props
|
|
309
|
+
// Use stableId for DOM ID if available (stable across reordering)
|
|
309
310
|
const theme = block.themeName
|
|
311
|
+
const sectionId = block.stableId || block.id
|
|
310
312
|
const wrapperProps = {
|
|
311
|
-
id: `
|
|
313
|
+
id: `section-${sectionId}`,
|
|
312
314
|
className: theme || ''
|
|
313
315
|
}
|
|
314
316
|
|
|
@@ -516,6 +518,14 @@ export async function prerenderSite(siteDir, options = {}) {
|
|
|
516
518
|
function injectContent(shell, renderedContent, page, siteContent) {
|
|
517
519
|
let html = shell
|
|
518
520
|
|
|
521
|
+
// Inject theme CSS if not already present
|
|
522
|
+
if (siteContent?.theme?.css && !html.includes('id="uniweb-theme"')) {
|
|
523
|
+
html = html.replace(
|
|
524
|
+
'</head>',
|
|
525
|
+
` <style id="uniweb-theme">\n${siteContent.theme.css}\n </style>\n </head>`
|
|
526
|
+
)
|
|
527
|
+
}
|
|
528
|
+
|
|
519
529
|
// Replace the empty root div with pre-rendered content
|
|
520
530
|
html = html.replace(
|
|
521
531
|
/<div id="root">[\s\S]*?<\/div>/,
|
|
@@ -548,7 +558,13 @@ function injectContent(shell, renderedContent, page, siteContent) {
|
|
|
548
558
|
|
|
549
559
|
// Inject site content as JSON for hydration
|
|
550
560
|
// Replace existing content if present, otherwise add it
|
|
551
|
-
|
|
561
|
+
// Strip CSS from theme (it's already in a <style> tag)
|
|
562
|
+
const contentForJson = { ...siteContent }
|
|
563
|
+
if (contentForJson.theme?.css) {
|
|
564
|
+
contentForJson.theme = { ...contentForJson.theme }
|
|
565
|
+
delete contentForJson.theme.css
|
|
566
|
+
}
|
|
567
|
+
const contentScript = `<script id="__SITE_CONTENT__" type="application/json">${JSON.stringify(contentForJson)}</script>`
|
|
552
568
|
if (html.includes('__SITE_CONTENT__')) {
|
|
553
569
|
// Replace existing site content with updated version (includes expanded dynamic routes)
|
|
554
570
|
// Match script tag with attributes in any order
|
package/src/schema.js
CHANGED
|
@@ -5,14 +5,17 @@
|
|
|
5
5
|
* Schema data is for editor-time only, not runtime.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { readdir } from 'node:fs/promises'
|
|
8
|
+
import { readdir, readFile } from 'node:fs/promises'
|
|
9
9
|
import { existsSync } from 'node:fs'
|
|
10
|
-
import { join,
|
|
10
|
+
import { join, dirname } from 'node:path'
|
|
11
11
|
import { pathToFileURL } from 'node:url'
|
|
12
12
|
|
|
13
|
-
//
|
|
13
|
+
// Component meta file name
|
|
14
14
|
const META_FILE_NAME = 'meta.js'
|
|
15
15
|
|
|
16
|
+
// Foundation config file name
|
|
17
|
+
const FOUNDATION_FILE_NAME = 'foundation.js'
|
|
18
|
+
|
|
16
19
|
// Default component paths (relative to srcDir)
|
|
17
20
|
const DEFAULT_COMPONENT_PATHS = ['components']
|
|
18
21
|
|
|
@@ -44,21 +47,71 @@ export async function loadComponentMeta(componentDir) {
|
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
/**
|
|
47
|
-
* Load foundation
|
|
50
|
+
* Load package.json from foundation root
|
|
51
|
+
* Extracts identity fields: name, version, description
|
|
52
|
+
*
|
|
53
|
+
* @param {string} srcDir - Source directory (e.g., 'src')
|
|
54
|
+
* @returns {Object} Identity fields from package.json
|
|
48
55
|
*/
|
|
49
|
-
export async function
|
|
50
|
-
|
|
56
|
+
export async function loadPackageJson(srcDir) {
|
|
57
|
+
// package.json is in the foundation root (parent of srcDir)
|
|
58
|
+
const foundationRoot = dirname(srcDir)
|
|
59
|
+
const packagePath = join(foundationRoot, 'package.json')
|
|
60
|
+
|
|
61
|
+
if (!existsSync(packagePath)) {
|
|
62
|
+
return {}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const content = await readFile(packagePath, 'utf-8')
|
|
67
|
+
const pkg = JSON.parse(content)
|
|
68
|
+
|
|
69
|
+
// Extract only identity fields for schema
|
|
70
|
+
return {
|
|
71
|
+
name: pkg.name,
|
|
72
|
+
version: pkg.version,
|
|
73
|
+
description: pkg.description,
|
|
74
|
+
}
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.warn(`Warning: Failed to load package.json:`, error.message)
|
|
77
|
+
return {}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Load foundation-level config file (foundation.js)
|
|
83
|
+
*
|
|
84
|
+
* Contains foundation-wide configuration:
|
|
85
|
+
* - vars: CSS custom properties sites can override
|
|
86
|
+
* - Layout: Custom layout component
|
|
87
|
+
* - Future: providers, middleware, etc.
|
|
88
|
+
*/
|
|
89
|
+
export async function loadFoundationConfig(srcDir) {
|
|
90
|
+
const filePath = join(srcDir, FOUNDATION_FILE_NAME)
|
|
51
91
|
if (!existsSync(filePath)) {
|
|
52
92
|
return {}
|
|
53
93
|
}
|
|
54
94
|
try {
|
|
55
|
-
|
|
95
|
+
const module = await import(pathToFileURL(filePath).href)
|
|
96
|
+
// Support both default export and named exports
|
|
97
|
+
return {
|
|
98
|
+
...module.default,
|
|
99
|
+
vars: module.vars || module.default?.vars,
|
|
100
|
+
Layout: module.Layout || module.default?.Layout,
|
|
101
|
+
}
|
|
56
102
|
} catch (error) {
|
|
57
|
-
console.warn(`Warning: Failed to load foundation
|
|
103
|
+
console.warn(`Warning: Failed to load foundation config ${filePath}:`, error.message)
|
|
58
104
|
return {}
|
|
59
105
|
}
|
|
60
106
|
}
|
|
61
107
|
|
|
108
|
+
/**
|
|
109
|
+
* @deprecated Use loadFoundationConfig instead
|
|
110
|
+
*/
|
|
111
|
+
export async function loadFoundationMeta(srcDir) {
|
|
112
|
+
return loadFoundationConfig(srcDir)
|
|
113
|
+
}
|
|
114
|
+
|
|
62
115
|
/**
|
|
63
116
|
* Discover components in a single path
|
|
64
117
|
* @param {string} srcDir - Source directory (e.g., 'src')
|
|
@@ -127,17 +180,31 @@ export async function discoverComponents(srcDir, componentPaths = DEFAULT_COMPON
|
|
|
127
180
|
|
|
128
181
|
/**
|
|
129
182
|
* Build complete schema for a foundation
|
|
130
|
-
* Returns { _self:
|
|
183
|
+
* Returns { _self: { identity + config }, ComponentName: componentMeta, ... }
|
|
184
|
+
*
|
|
185
|
+
* The _self object contains:
|
|
186
|
+
* - Identity from package.json (name, version, description)
|
|
187
|
+
* - Configuration from foundation.js (vars, Layout, etc.)
|
|
131
188
|
*
|
|
132
189
|
* @param {string} srcDir - Source directory
|
|
133
190
|
* @param {string[]} [componentPaths] - Paths to search for components
|
|
134
191
|
*/
|
|
135
192
|
export async function buildSchema(srcDir, componentPaths) {
|
|
136
|
-
|
|
193
|
+
// Load identity from package.json
|
|
194
|
+
const identity = await loadPackageJson(srcDir)
|
|
195
|
+
|
|
196
|
+
// Load configuration from foundation.js
|
|
197
|
+
const foundationConfig = await loadFoundationConfig(srcDir)
|
|
198
|
+
|
|
199
|
+
// Discover components
|
|
137
200
|
const components = await discoverComponents(srcDir, componentPaths)
|
|
138
201
|
|
|
139
202
|
return {
|
|
140
|
-
|
|
203
|
+
// Merge identity and config - identity fields take precedence
|
|
204
|
+
_self: {
|
|
205
|
+
...foundationConfig,
|
|
206
|
+
...identity,
|
|
207
|
+
},
|
|
141
208
|
...components,
|
|
142
209
|
}
|
|
143
210
|
}
|
package/src/site/config.js
CHANGED
|
@@ -235,7 +235,8 @@ export async function defineSiteConfig(options = {}) {
|
|
|
235
235
|
inject: true,
|
|
236
236
|
seo,
|
|
237
237
|
assets,
|
|
238
|
-
search
|
|
238
|
+
search,
|
|
239
|
+
foundationPath: foundationInfo.path // For loading foundation theme vars
|
|
239
240
|
}),
|
|
240
241
|
|
|
241
242
|
// Foundation dev server (only in runtime mode with local foundation)
|