@uniweb/build 0.1.32 → 0.1.33
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/generate-entry.js +26 -5
- package/src/index.js +2 -1
- package/src/prerender.js +15 -1
- package/src/schema.js +78 -11
- package/src/site/config.js +2 -1
- package/src/site/content-collector.js +72 -8
- 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 +406 -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.1.
|
|
3
|
+
"version": "0.1.33",
|
|
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.19"
|
|
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.1.
|
|
63
|
+
"@uniweb/core": "0.1.16"
|
|
64
64
|
},
|
|
65
65
|
"peerDependenciesMeta": {
|
|
66
66
|
"vite": {
|
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
|
@@ -516,6 +516,14 @@ export async function prerenderSite(siteDir, options = {}) {
|
|
|
516
516
|
function injectContent(shell, renderedContent, page, siteContent) {
|
|
517
517
|
let html = shell
|
|
518
518
|
|
|
519
|
+
// Inject theme CSS if not already present
|
|
520
|
+
if (siteContent?.theme?.css && !html.includes('id="uniweb-theme"')) {
|
|
521
|
+
html = html.replace(
|
|
522
|
+
'</head>',
|
|
523
|
+
` <style id="uniweb-theme">\n${siteContent.theme.css}\n </style>\n </head>`
|
|
524
|
+
)
|
|
525
|
+
}
|
|
526
|
+
|
|
519
527
|
// Replace the empty root div with pre-rendered content
|
|
520
528
|
html = html.replace(
|
|
521
529
|
/<div id="root">[\s\S]*?<\/div>/,
|
|
@@ -548,7 +556,13 @@ function injectContent(shell, renderedContent, page, siteContent) {
|
|
|
548
556
|
|
|
549
557
|
// Inject site content as JSON for hydration
|
|
550
558
|
// Replace existing content if present, otherwise add it
|
|
551
|
-
|
|
559
|
+
// Strip CSS from theme (it's already in a <style> tag)
|
|
560
|
+
const contentForJson = { ...siteContent }
|
|
561
|
+
if (contentForJson.theme?.css) {
|
|
562
|
+
contentForJson.theme = { ...contentForJson.theme }
|
|
563
|
+
delete contentForJson.theme.css
|
|
564
|
+
}
|
|
565
|
+
const contentScript = `<script id="__SITE_CONTENT__" type="application/json">${JSON.stringify(contentForJson)}</script>`
|
|
552
566
|
if (html.includes('__SITE_CONTENT__')) {
|
|
553
567
|
// Replace existing site content with updated version (includes expanded dynamic routes)
|
|
554
568
|
// 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)
|
|
@@ -28,6 +28,7 @@ import { existsSync } from 'node:fs'
|
|
|
28
28
|
import yaml from 'js-yaml'
|
|
29
29
|
import { collectSectionAssets, mergeAssetCollections } from './assets.js'
|
|
30
30
|
import { parseFetchConfig, singularize } from './data-fetcher.js'
|
|
31
|
+
import { buildTheme, extractFoundationVars } from '../theme/index.js'
|
|
31
32
|
|
|
32
33
|
// Try to import content-reader, fall back to simplified parser
|
|
33
34
|
let markdownToProseMirror
|
|
@@ -161,11 +162,20 @@ async function processMarkdownFile(filePath, id, siteRoot) {
|
|
|
161
162
|
}
|
|
162
163
|
}
|
|
163
164
|
|
|
164
|
-
const { type, component, preset, input, props, fetch, ...params } = frontMatter
|
|
165
|
+
const { type, component, preset, input, props, fetch, data, ...params } = frontMatter
|
|
165
166
|
|
|
166
167
|
// Convert markdown to ProseMirror
|
|
167
168
|
const proseMirrorContent = markdownToProseMirror(markdown)
|
|
168
169
|
|
|
170
|
+
// Support 'data:' shorthand for collection fetch
|
|
171
|
+
// data: team → fetch: { collection: team }
|
|
172
|
+
// data: [team, articles] → fetch: { collection: team } (first item, others via inheritData)
|
|
173
|
+
let resolvedFetch = fetch
|
|
174
|
+
if (!fetch && data) {
|
|
175
|
+
const collectionName = Array.isArray(data) ? data[0] : data
|
|
176
|
+
resolvedFetch = { collection: collectionName }
|
|
177
|
+
}
|
|
178
|
+
|
|
169
179
|
const section = {
|
|
170
180
|
id,
|
|
171
181
|
component: type || component || 'Section',
|
|
@@ -173,7 +183,7 @@ async function processMarkdownFile(filePath, id, siteRoot) {
|
|
|
173
183
|
input,
|
|
174
184
|
params: { ...params, ...props },
|
|
175
185
|
content: proseMirrorContent,
|
|
176
|
-
fetch: parseFetchConfig(
|
|
186
|
+
fetch: parseFetchConfig(resolvedFetch),
|
|
177
187
|
subsections: []
|
|
178
188
|
}
|
|
179
189
|
|
|
@@ -438,7 +448,13 @@ async function processPage(pagePath, pageName, siteRoot, { isIndex = false, pare
|
|
|
438
448
|
},
|
|
439
449
|
|
|
440
450
|
// Data fetching
|
|
441
|
-
|
|
451
|
+
// Support 'data:' shorthand at page level
|
|
452
|
+
// data: team → fetch: { collection: team }
|
|
453
|
+
fetch: parseFetchConfig(
|
|
454
|
+
pageConfig.fetch || (pageConfig.data
|
|
455
|
+
? { collection: Array.isArray(pageConfig.data) ? pageConfig.data[0] : pageConfig.data }
|
|
456
|
+
: undefined)
|
|
457
|
+
),
|
|
442
458
|
|
|
443
459
|
sections: hierarchicalSections
|
|
444
460
|
},
|
|
@@ -581,24 +597,69 @@ async function collectPagesRecursive(dirPath, parentRoute, siteRoot, orderConfig
|
|
|
581
597
|
return { pages, assetCollection, header, footer, left, right, notFound }
|
|
582
598
|
}
|
|
583
599
|
|
|
600
|
+
/**
|
|
601
|
+
* Load foundation variables from schema.json
|
|
602
|
+
*
|
|
603
|
+
* @param {string} foundationPath - Path to foundation directory
|
|
604
|
+
* @returns {Promise<Object>} Foundation variables or empty object
|
|
605
|
+
*/
|
|
606
|
+
async function loadFoundationVars(foundationPath) {
|
|
607
|
+
if (!foundationPath) return {}
|
|
608
|
+
|
|
609
|
+
// Try dist/schema.json first (built foundation), then src/schema.json
|
|
610
|
+
const distSchemaPath = join(foundationPath, 'dist', 'schema.json')
|
|
611
|
+
const srcSchemaPath = join(foundationPath, 'schema.json')
|
|
612
|
+
|
|
613
|
+
const schemaPath = existsSync(distSchemaPath) ? distSchemaPath : srcSchemaPath
|
|
614
|
+
|
|
615
|
+
if (!existsSync(schemaPath)) {
|
|
616
|
+
return {}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
try {
|
|
620
|
+
const schemaContent = await readFile(schemaPath, 'utf8')
|
|
621
|
+
const schema = JSON.parse(schemaContent)
|
|
622
|
+
// Foundation config is in _self, support both 'vars' (new) and 'themeVars' (legacy)
|
|
623
|
+
return schema._self?.vars || schema._self?.themeVars || schema.themeVars || {}
|
|
624
|
+
} catch (err) {
|
|
625
|
+
console.warn('[content-collector] Failed to load foundation schema:', err.message)
|
|
626
|
+
return {}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
584
630
|
/**
|
|
585
631
|
* Collect all site content
|
|
586
632
|
*
|
|
587
633
|
* @param {string} sitePath - Path to site directory
|
|
634
|
+
* @param {Object} options - Collection options
|
|
635
|
+
* @param {string} options.foundationPath - Path to foundation directory (for theme vars)
|
|
588
636
|
* @returns {Promise<Object>} Site content object with assets manifest
|
|
589
637
|
*/
|
|
590
|
-
export async function collectSiteContent(sitePath) {
|
|
638
|
+
export async function collectSiteContent(sitePath, options = {}) {
|
|
639
|
+
const { foundationPath } = options
|
|
591
640
|
const pagesPath = join(sitePath, 'pages')
|
|
592
641
|
|
|
593
|
-
// Read site config
|
|
642
|
+
// Read site config and raw theme config
|
|
594
643
|
const siteConfig = await readYamlFile(join(sitePath, 'site.yml'))
|
|
595
|
-
const
|
|
644
|
+
const rawThemeConfig = await readYamlFile(join(sitePath, 'theme.yml'))
|
|
645
|
+
|
|
646
|
+
// Load foundation vars and process theme
|
|
647
|
+
const foundationVars = await loadFoundationVars(foundationPath)
|
|
648
|
+
const { config: processedTheme, css: themeCSS, warnings } = buildTheme(rawThemeConfig, { foundationVars })
|
|
649
|
+
|
|
650
|
+
// Log theme warnings
|
|
651
|
+
if (warnings?.length > 0) {
|
|
652
|
+
warnings.forEach(w => console.warn(`[theme] ${w}`))
|
|
653
|
+
}
|
|
596
654
|
|
|
597
655
|
// Check if pages directory exists
|
|
598
656
|
if (!existsSync(pagesPath)) {
|
|
599
657
|
return {
|
|
600
658
|
config: siteConfig,
|
|
601
|
-
theme:
|
|
659
|
+
theme: {
|
|
660
|
+
...processedTheme,
|
|
661
|
+
css: themeCSS
|
|
662
|
+
},
|
|
602
663
|
pages: [],
|
|
603
664
|
assets: {}
|
|
604
665
|
}
|
|
@@ -629,7 +690,10 @@ export async function collectSiteContent(sitePath) {
|
|
|
629
690
|
...siteConfig,
|
|
630
691
|
fetch: parseFetchConfig(siteConfig.fetch),
|
|
631
692
|
},
|
|
632
|
-
theme:
|
|
693
|
+
theme: {
|
|
694
|
+
...processedTheme,
|
|
695
|
+
css: themeCSS
|
|
696
|
+
},
|
|
633
697
|
pages,
|
|
634
698
|
header,
|
|
635
699
|
footer,
|
package/src/site/plugin.js
CHANGED
|
@@ -305,6 +305,7 @@ function escapeHtml(str) {
|
|
|
305
305
|
* @param {Object} [options.search] - Search index configuration
|
|
306
306
|
* @param {boolean} [options.search.enabled=true] - Generate search index (uses site.yml config by default)
|
|
307
307
|
* @param {string} [options.search.filename='search-index.json'] - Search index filename
|
|
308
|
+
* @param {string} [options.foundationPath] - Path to foundation directory (for loading theme vars)
|
|
308
309
|
*/
|
|
309
310
|
export function siteContentPlugin(options = {}) {
|
|
310
311
|
const {
|
|
@@ -316,7 +317,8 @@ export function siteContentPlugin(options = {}) {
|
|
|
316
317
|
watch: shouldWatch = true,
|
|
317
318
|
seo = {},
|
|
318
319
|
assets: assetsConfig = {},
|
|
319
|
-
search: searchPluginConfig = {}
|
|
320
|
+
search: searchPluginConfig = {},
|
|
321
|
+
foundationPath
|
|
320
322
|
} = options
|
|
321
323
|
|
|
322
324
|
// Extract asset processing options
|
|
@@ -416,7 +418,7 @@ export function siteContentPlugin(options = {}) {
|
|
|
416
418
|
if (!isProduction) {
|
|
417
419
|
try {
|
|
418
420
|
// Do an early content collection to get the collections config
|
|
419
|
-
const earlyContent = await collectSiteContent(resolvedSitePath)
|
|
421
|
+
const earlyContent = await collectSiteContent(resolvedSitePath, { foundationPath })
|
|
420
422
|
collectionsConfig = earlyContent.config?.collections
|
|
421
423
|
|
|
422
424
|
if (collectionsConfig) {
|
|
@@ -433,7 +435,7 @@ export function siteContentPlugin(options = {}) {
|
|
|
433
435
|
async buildStart() {
|
|
434
436
|
// Collect content at build start
|
|
435
437
|
try {
|
|
436
|
-
siteContent = await collectSiteContent(resolvedSitePath)
|
|
438
|
+
siteContent = await collectSiteContent(resolvedSitePath, { foundationPath })
|
|
437
439
|
console.log(`[site-content] Collected ${siteContent.pages?.length || 0} pages`)
|
|
438
440
|
|
|
439
441
|
// Process content collections if defined in site.yml
|
|
@@ -480,7 +482,7 @@ export function siteContentPlugin(options = {}) {
|
|
|
480
482
|
rebuildTimeout = setTimeout(async () => {
|
|
481
483
|
console.log('[site-content] Content changed, rebuilding...')
|
|
482
484
|
try {
|
|
483
|
-
siteContent = await collectSiteContent(resolvedSitePath)
|
|
485
|
+
siteContent = await collectSiteContent(resolvedSitePath, { foundationPath })
|
|
484
486
|
// Execute fetches for the updated content
|
|
485
487
|
await executeDevFetches(siteContent, resolvedSitePath)
|
|
486
488
|
console.log(`[site-content] Rebuilt ${siteContent.pages?.length || 0} pages`)
|
|
@@ -685,6 +687,11 @@ export function siteContentPlugin(options = {}) {
|
|
|
685
687
|
|
|
686
688
|
let headInjection = ''
|
|
687
689
|
|
|
690
|
+
// Inject theme CSS
|
|
691
|
+
if (contentToInject.theme?.css) {
|
|
692
|
+
headInjection += ` <style id="uniweb-theme">\n${contentToInject.theme.css}\n </style>\n`
|
|
693
|
+
}
|
|
694
|
+
|
|
688
695
|
// Inject SEO meta tags
|
|
689
696
|
if (seoEnabled) {
|
|
690
697
|
const metaTags = generateMetaTags(contentToInject, seoOptions)
|
|
@@ -791,6 +798,9 @@ export function siteContentPlugin(options = {}) {
|
|
|
791
798
|
delete finalContent.hasExplicitPoster
|
|
792
799
|
delete finalContent.hasExplicitPreview
|
|
793
800
|
|
|
801
|
+
// Note: theme.css is kept here so prerender can inject it into HTML
|
|
802
|
+
// Prerender will strip it from the JSON it injects into each page
|
|
803
|
+
|
|
794
804
|
// Emit content as JSON file in production build
|
|
795
805
|
this.emitFile({
|
|
796
806
|
type: 'asset',
|