@uniweb/build 0.8.13 → 0.8.15

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniweb/build",
3
- "version": "0.8.13",
3
+ "version": "0.8.15",
4
4
  "description": "Build tooling for the Uniweb Component Web Platform",
5
5
  "type": "module",
6
6
  "exports": {
@@ -11,6 +11,7 @@
11
11
  "./vite-plugin": "./src/vite-foundation-plugin.js",
12
12
  "./foundation": "./src/foundation/index.js",
13
13
  "./foundation/config": "./src/foundation/config.js",
14
+ "./content": "./src/content/index.js",
14
15
  "./site": "./src/site/index.js",
15
16
  "./site/config": "./src/site/config.js",
16
17
  "./dev": "./src/dev/index.js",
@@ -47,12 +48,13 @@
47
48
  },
48
49
  "dependencies": {
49
50
  "js-yaml": "^4.1.0",
50
- "sharp": "^0.33.2"
51
+ "sharp": "^0.33.2",
52
+ "@uniweb/theming": "0.1.1"
51
53
  },
52
54
  "optionalDependencies": {
53
- "@uniweb/content-reader": "1.1.4",
54
- "@uniweb/runtime": "0.6.9",
55
- "@uniweb/schemas": "0.2.1"
55
+ "@uniweb/schemas": "0.2.1",
56
+ "@uniweb/runtime": "0.6.11",
57
+ "@uniweb/content-reader": "1.1.4"
56
58
  },
57
59
  "peerDependencies": {
58
60
  "vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Clean Content Entry Point
3
+ *
4
+ * Exposes only collectSiteContent and its clean dependency chain
5
+ * (Node builtins, js-yaml, @uniweb/theming). No Vite, sharp, or React.
6
+ *
7
+ * Used by Studio's sidecar (Bun-compiled binary) where those heavy
8
+ * peer/native dependencies aren't available.
9
+ *
10
+ * @module @uniweb/build/content
11
+ */
12
+
13
+ export { collectSiteContent } from '../site/content-collector.js'
@@ -37,13 +37,18 @@ export function extractTranslatableContent(siteContent) {
37
37
  }
38
38
  }
39
39
 
40
- // Extract from shared layout pages (header, footer, left, right panels)
41
- for (const layoutKey of ['header', 'footer', 'left', 'right']) {
42
- const layoutPage = siteContent[layoutKey]
43
- if (layoutPage?.sections) {
44
- const pageRoute = layoutPage.route || `/layout/${layoutKey}`
45
- for (const section of layoutPage.sections) {
46
- extractFromSection(section, pageRoute, units)
40
+ // Extract from layout areas (header, footer, left, right panels)
41
+ // Layouts are nested under siteContent.layouts: { default: { header, footer, ... }, marketing: { ... } }
42
+ if (siteContent.layouts) {
43
+ for (const [layoutName, areas] of Object.entries(siteContent.layouts)) {
44
+ if (!areas || typeof areas !== 'object') continue
45
+ for (const [areaKey, layoutPage] of Object.entries(areas)) {
46
+ if (layoutPage?.sections) {
47
+ const pageRoute = layoutPage.route || `/layout/${layoutName === 'default' ? '' : layoutName + '/'}${areaKey}`
48
+ for (const section of layoutPage.sections) {
49
+ extractFromSection(section, pageRoute, units)
50
+ }
51
+ }
47
52
  }
48
53
  }
49
54
  }
package/src/i18n/merge.js CHANGED
@@ -76,13 +76,18 @@ function mergeTranslationsSync(siteContent, translations, fallbackToSource) {
76
76
  }
77
77
  }
78
78
 
79
- // Translate shared layout sections (header, footer, sidebars)
80
- for (const layoutKey of ['header', 'footer', 'left', 'right']) {
81
- const layoutPage = translated[layoutKey]
82
- if (layoutPage?.sections) {
83
- const pageRoute = layoutPage.route || `/layout/${layoutKey}`
84
- for (const section of layoutPage.sections) {
85
- translateSectionSync(section, pageRoute, translations, fallbackToSource)
79
+ // Translate layout sections (header, footer, sidebars)
80
+ // Layouts are nested under translated.layouts: { default: { header, footer, ... }, marketing: { ... } }
81
+ if (translated.layouts) {
82
+ for (const [layoutName, areas] of Object.entries(translated.layouts)) {
83
+ if (!areas || typeof areas !== 'object') continue
84
+ for (const [areaKey, layoutPage] of Object.entries(areas)) {
85
+ if (layoutPage?.sections) {
86
+ const pageRoute = layoutPage.route || `/layout/${layoutName === 'default' ? '' : layoutName + '/'}${areaKey}`
87
+ for (const section of layoutPage.sections) {
88
+ translateSectionSync(section, pageRoute, translations, fallbackToSource)
89
+ }
90
+ }
86
91
  }
87
92
  }
88
93
  }
@@ -128,18 +133,22 @@ async function mergeTranslationsAsync(siteContent, translations, options) {
128
133
  }
129
134
  }
130
135
 
131
- // Translate shared layout sections (header, footer, sidebars)
132
- for (const layoutKey of ['header', 'footer', 'left', 'right']) {
133
- const layoutPage = translated[layoutKey]
134
- if (layoutPage?.sections) {
135
- // Ensure route is set for context matching
136
- if (!layoutPage.route) layoutPage.route = `/layout/${layoutKey}`
137
- for (const section of layoutPage.sections) {
138
- await translateSectionAsync(section, layoutPage, translations, {
139
- fallbackToSource,
140
- locale,
141
- localesDir
142
- })
136
+ // Translate layout sections (header, footer, sidebars)
137
+ // Layouts are nested under translated.layouts: { default: { header, footer, ... }, marketing: { ... } }
138
+ if (translated.layouts) {
139
+ for (const [layoutName, areas] of Object.entries(translated.layouts)) {
140
+ if (!areas || typeof areas !== 'object') continue
141
+ for (const [areaKey, layoutPage] of Object.entries(areas)) {
142
+ if (layoutPage?.sections) {
143
+ if (!layoutPage.route) layoutPage.route = `/layout/${layoutName === 'default' ? '' : layoutName + '/'}${areaKey}`
144
+ for (const section of layoutPage.sections) {
145
+ await translateSectionAsync(section, layoutPage, translations, {
146
+ fallbackToSource,
147
+ locale,
148
+ localesDir
149
+ })
150
+ }
151
+ }
143
152
  }
144
153
  }
145
154
  }
@@ -1,65 +1,10 @@
1
1
  /**
2
- * Theme Module
2
+ * Theme Module — re-export shim
3
3
  *
4
- * Exports all theme-related utilities for the build process.
4
+ * All theming logic has moved to @uniweb/theming.
5
+ * This shim preserves backward compatibility for internal imports.
5
6
  *
6
7
  * @module @uniweb/build/theme
7
8
  */
8
9
 
9
- // Shade generation
10
- export {
11
- parseColor,
12
- formatOklch,
13
- formatHex,
14
- generateShades,
15
- generatePalettes,
16
- isValidColor,
17
- getShadeLevels,
18
- } from './shade-generator.js'
19
-
20
- // CSS generation
21
- export {
22
- generateThemeCSS,
23
- generateContextCSS,
24
- generatePaletteVars,
25
- getDefaultContextTokens,
26
- getDefaultColors,
27
- } from './css-generator.js'
28
-
29
- // Theme processing
30
- export {
31
- validateThemeConfig,
32
- processTheme,
33
- extractFoundationVars,
34
- foundationHasVars,
35
- } from './processor.js'
36
-
37
- // Default export for convenience
38
- import { processTheme } from './processor.js'
39
- import { generateThemeCSS } from './css-generator.js'
40
-
41
- /**
42
- * Process theme configuration and generate CSS in one step
43
- *
44
- * @param {Object} themeYml - Raw theme.yml content
45
- * @param {Object} options - Processing options
46
- * @param {Object} options.foundationVars - Foundation variables
47
- * @returns {{ css: string, config: Object, errors: string[], warnings: string[] }}
48
- */
49
- export function buildTheme(themeYml = {}, options = {}) {
50
- const { config, errors, warnings } = processTheme(themeYml, options)
51
- const css = generateThemeCSS(config)
52
-
53
- return {
54
- css,
55
- config,
56
- errors,
57
- warnings,
58
- }
59
- }
60
-
61
- export default {
62
- buildTheme,
63
- processTheme,
64
- generateThemeCSS,
65
- }
10
+ export * from '@uniweb/theming'
@@ -1,400 +0,0 @@
1
- /**
2
- * Theme CSS Generator
3
- *
4
- * Generates complete CSS for site theming including:
5
- * - Color palettes as CSS custom properties
6
- * - Context classes (light/medium/dark) with semantic tokens
7
- * - Foundation-specific variables
8
- * - Optional dark scheme support
9
- *
10
- * @module @uniweb/build/theme/css-generator
11
- */
12
-
13
- import { generatePalettes, formatOklch } from './shade-generator.js'
14
-
15
- // Default semantic tokens for each context
16
- // These map abstract concepts to specific palette values
17
- const DEFAULT_CONTEXT_TOKENS = {
18
- light: {
19
- 'section': 'var(--neutral-50)',
20
- 'card': 'var(--neutral-100)',
21
- 'muted': 'var(--neutral-200)',
22
- 'body': 'var(--neutral-950)',
23
- 'heading': 'var(--neutral-900)',
24
- 'subtle': 'var(--neutral-600)',
25
- 'border': 'var(--neutral-200)',
26
- 'ring': 'var(--primary-500)',
27
- 'link': 'var(--primary-600)',
28
- 'link-hover': 'var(--primary-700)',
29
- 'primary': 'var(--primary-600)',
30
- 'primary-foreground': 'white',
31
- 'primary-hover': 'var(--primary-700)',
32
- 'primary-border': 'transparent',
33
- 'secondary': 'white',
34
- 'secondary-foreground': 'var(--neutral-900)',
35
- 'secondary-hover': 'var(--neutral-100)',
36
- 'secondary-border': 'var(--neutral-300)',
37
- 'success': '#16a34a',
38
- 'success-subtle': '#f0fdf4',
39
- 'warning': '#d97706',
40
- 'warning-subtle': '#fffbeb',
41
- 'error': '#dc2626',
42
- 'error-subtle': '#fef2f2',
43
- 'info': '#2563eb',
44
- 'info-subtle': '#eff6ff',
45
- },
46
- medium: {
47
- 'section': 'var(--neutral-100)',
48
- 'card': 'var(--neutral-200)',
49
- 'muted': 'var(--neutral-300)',
50
- 'body': 'var(--neutral-950)',
51
- 'heading': 'var(--neutral-900)',
52
- 'subtle': 'var(--neutral-700)',
53
- 'border': 'var(--neutral-300)',
54
- 'ring': 'var(--primary-500)',
55
- 'link': 'var(--primary-600)',
56
- 'link-hover': 'var(--primary-700)',
57
- 'primary': 'var(--primary-600)',
58
- 'primary-foreground': 'white',
59
- 'primary-hover': 'var(--primary-700)',
60
- 'primary-border': 'transparent',
61
- 'secondary': 'white',
62
- 'secondary-foreground': 'var(--neutral-900)',
63
- 'secondary-hover': 'var(--neutral-100)',
64
- 'secondary-border': 'var(--neutral-300)',
65
- 'success': '#16a34a',
66
- 'success-subtle': '#f0fdf4',
67
- 'warning': '#d97706',
68
- 'warning-subtle': '#fffbeb',
69
- 'error': '#dc2626',
70
- 'error-subtle': '#fef2f2',
71
- 'info': '#2563eb',
72
- 'info-subtle': '#eff6ff',
73
- },
74
- dark: {
75
- 'section': 'var(--neutral-900)',
76
- 'card': 'var(--neutral-800)',
77
- 'muted': 'var(--neutral-700)',
78
- 'body': 'var(--neutral-50)',
79
- 'heading': 'white',
80
- 'subtle': 'var(--neutral-400)',
81
- 'border': 'var(--neutral-700)',
82
- 'ring': 'var(--primary-500)',
83
- 'link': 'var(--primary-400)',
84
- 'link-hover': 'var(--primary-300)',
85
- 'primary': 'var(--primary-500)',
86
- 'primary-foreground': 'white',
87
- 'primary-hover': 'var(--primary-400)',
88
- 'primary-border': 'transparent',
89
- 'secondary': 'var(--neutral-800)',
90
- 'secondary-foreground': 'var(--neutral-100)',
91
- 'secondary-hover': 'var(--neutral-700)',
92
- 'secondary-border': 'var(--neutral-600)',
93
- 'success': '#4ade80',
94
- 'success-subtle': '#052e16',
95
- 'warning': '#fbbf24',
96
- 'warning-subtle': '#451a03',
97
- 'error': '#f87171',
98
- 'error-subtle': '#450a0a',
99
- 'info': '#60a5fa',
100
- 'info-subtle': '#172554',
101
- },
102
- }
103
-
104
- // Default color palette configuration
105
- const DEFAULT_COLORS = {
106
- primary: '#3b82f6', // Blue
107
- secondary: '#64748b', // Slate
108
- accent: '#8b5cf6', // Purple
109
- neutral: '#78716c', // Stone
110
- }
111
-
112
- // Shade levels for CSS variable generation
113
- const SHADE_LEVELS = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950]
114
-
115
- /**
116
- * Generate CSS variable declarations from an object
117
- *
118
- * @param {Object} vars - Object with variable names as keys
119
- * @param {string} indent - Indentation string
120
- * @returns {string} CSS variable declarations
121
- */
122
- function generateVarDeclarations(vars, indent = ' ') {
123
- return Object.entries(vars)
124
- .map(([name, value]) => `${indent}--${name}: ${value};`)
125
- .join('\n')
126
- }
127
-
128
- /**
129
- * Generate color palette CSS variables
130
- *
131
- * @param {Object} palettes - Object with palette name → shades
132
- * @returns {string} CSS variable declarations for all palettes
133
- */
134
- function generatePaletteVars(palettes) {
135
- const lines = []
136
-
137
- for (const [name, shades] of Object.entries(palettes)) {
138
- for (const level of SHADE_LEVELS) {
139
- if (shades[level]) {
140
- lines.push(` --${name}-${level}: ${shades[level]};`)
141
- }
142
- }
143
- }
144
-
145
- return lines.join('\n')
146
- }
147
-
148
- /**
149
- * Generate context class CSS
150
- *
151
- * @param {string} context - Context name (light, medium, dark)
152
- * @param {Object} tokens - Token overrides
153
- * @returns {string} CSS for context class
154
- */
155
- function generateContextCSS(context, tokens = {}) {
156
- const defaultTokens = DEFAULT_CONTEXT_TOKENS[context] || DEFAULT_CONTEXT_TOKENS.light
157
- const mergedTokens = { ...defaultTokens, ...tokens }
158
-
159
- const vars = generateVarDeclarations(mergedTokens)
160
-
161
- return `.context-${context} {\n${vars}\n background-color: var(--section);\n}`
162
- }
163
-
164
- /**
165
- * Generate dark scheme CSS (for site-wide dark mode toggle)
166
- *
167
- * @param {Object} config - Appearance configuration
168
- * @returns {string} CSS for dark scheme support
169
- */
170
- function generateDarkSchemeCSS(config = {}) {
171
- const { respectSystemPreference = true } = config
172
-
173
- // Dark scheme tokens - similar to dark context but at root level
174
- const darkTokens = {
175
- 'section': 'var(--neutral-950)',
176
- 'card': 'var(--neutral-900)',
177
- 'muted': 'var(--neutral-800)',
178
- 'body': 'var(--neutral-50)',
179
- 'heading': 'white',
180
- 'subtle': 'var(--neutral-400)',
181
- 'border': 'var(--neutral-800)',
182
- 'ring': 'var(--primary-500)',
183
- 'link': 'var(--primary-400)',
184
- 'link-hover': 'var(--primary-300)',
185
- 'primary': 'var(--primary-500)',
186
- 'primary-foreground': 'white',
187
- 'primary-hover': 'var(--primary-400)',
188
- 'primary-border': 'transparent',
189
- 'secondary': 'var(--neutral-800)',
190
- 'secondary-foreground': 'var(--neutral-100)',
191
- 'secondary-hover': 'var(--neutral-700)',
192
- 'secondary-border': 'var(--neutral-600)',
193
- 'success': '#4ade80',
194
- 'success-subtle': '#052e16',
195
- 'warning': '#fbbf24',
196
- 'warning-subtle': '#451a03',
197
- 'error': '#f87171',
198
- 'error-subtle': '#450a0a',
199
- 'info': '#60a5fa',
200
- 'info-subtle': '#172554',
201
- }
202
-
203
- const vars = generateVarDeclarations(darkTokens)
204
-
205
- let css = `/* Dark scheme (user preference) */\n`
206
- css += `.scheme-dark {\n${vars}\n}\n`
207
-
208
- if (respectSystemPreference) {
209
- css += `\n@media (prefers-color-scheme: dark) {\n`
210
- css += ` :root:not(.scheme-light) {\n`
211
- for (const [name, value] of Object.entries(darkTokens)) {
212
- css += ` --${name}: ${value};\n`
213
- }
214
- css += ` }\n`
215
- css += `}\n`
216
- }
217
-
218
- return css
219
- }
220
-
221
- /**
222
- * Generate font CSS
223
- *
224
- * @param {Object} fonts - Font configuration
225
- * @returns {string} CSS for fonts
226
- */
227
- function generateFontCSS(fonts = {}) {
228
- const lines = []
229
-
230
- // Font imports
231
- if (fonts.import && Array.isArray(fonts.import)) {
232
- for (const font of fonts.import) {
233
- if (font.url) {
234
- lines.push(`@import url('${font.url}');`)
235
- }
236
- }
237
- if (lines.length > 0) {
238
- lines.push('') // Empty line after imports
239
- }
240
- }
241
-
242
- // Font family variables
243
- const fontVars = []
244
- if (fonts.body) {
245
- fontVars.push(` --font-body: ${fonts.body};`)
246
- }
247
- if (fonts.heading) {
248
- fontVars.push(` --font-heading: ${fonts.heading};`)
249
- }
250
- if (fonts.mono) {
251
- fontVars.push(` --font-mono: ${fonts.mono};`)
252
- }
253
-
254
- if (fontVars.length > 0) {
255
- lines.push(':root {')
256
- lines.push(...fontVars)
257
- lines.push('}')
258
- }
259
-
260
- return lines.join('\n')
261
- }
262
-
263
- /**
264
- * Generate foundation-specific CSS variables
265
- *
266
- * @param {Object} vars - Foundation variables from vars.js
267
- * @returns {string} CSS variable declarations
268
- */
269
- function generateFoundationVars(vars = {}) {
270
- if (!vars || Object.keys(vars).length === 0) {
271
- return ''
272
- }
273
-
274
- const declarations = []
275
-
276
- for (const [name, config] of Object.entries(vars)) {
277
- const value = typeof config === 'object' ? config.default : config
278
- if (value !== undefined) {
279
- declarations.push(` --${name}: ${value};`)
280
- }
281
- }
282
-
283
- if (declarations.length === 0) {
284
- return ''
285
- }
286
-
287
- return `:root {\n${declarations.join('\n')}\n}`
288
- }
289
-
290
- /**
291
- * Generate complete theme CSS
292
- *
293
- * @param {Object} config - Processed theme configuration
294
- * @param {Object} config.colors - Color palette configuration
295
- * @param {Object} config.contexts - Context token overrides
296
- * @param {Object} config.fonts - Font configuration
297
- * @param {Object} config.appearance - Appearance settings (dark mode, etc.)
298
- * @param {Object} config.foundationVars - Foundation-specific variables
299
- * @returns {string} Complete CSS string
300
- */
301
- export function generateThemeCSS(config = {}) {
302
- const {
303
- colors = DEFAULT_COLORS,
304
- contexts = {},
305
- fonts = {},
306
- appearance = {},
307
- foundationVars = {},
308
- } = config
309
-
310
- const sections = []
311
-
312
- // 1. Font imports and variables
313
- const fontCSS = generateFontCSS(fonts)
314
- if (fontCSS) {
315
- sections.push('/* Typography */\n' + fontCSS)
316
- }
317
-
318
- // 2. Color palettes
319
- const palettes = generatePalettes(colors)
320
- const paletteVars = generatePaletteVars(palettes)
321
- sections.push(`/* Color Palettes */\n:root {\n${paletteVars}\n}`)
322
-
323
- // 3. Default semantic tokens (applied to :root for global defaults)
324
- const defaultTokens = { ...DEFAULT_CONTEXT_TOKENS.light, ...(contexts.light || {}) }
325
- const defaultVars = generateVarDeclarations(defaultTokens)
326
- sections.push(`/* Default Semantic Tokens */\n:root {\n${defaultVars}\n}`)
327
-
328
- // 4. Context classes
329
- const contextCSS = [
330
- generateContextCSS('light', contexts.light),
331
- generateContextCSS('medium', contexts.medium),
332
- generateContextCSS('dark', contexts.dark),
333
- ]
334
- sections.push('/* Color Contexts */\n' + contextCSS.join('\n\n'))
335
-
336
- // 5. Foundation variables
337
- const foundationCSS = generateFoundationVars(foundationVars)
338
- if (foundationCSS) {
339
- sections.push('/* Foundation Variables */\n' + foundationCSS)
340
- }
341
-
342
- // 6. Dark scheme support (if enabled)
343
- if (appearance.allowToggle || appearance.schemes?.includes('dark')) {
344
- sections.push(generateDarkSchemeCSS(appearance))
345
- }
346
-
347
- // 7. Site background (if specified in theme.yml)
348
- if (config.background) {
349
- sections.push(`/* Site Background */\nbody {\n background: ${config.background};\n}`)
350
- }
351
-
352
- // 8. Inline text styles (if specified in theme.yml)
353
- if (config.inline && typeof config.inline === 'object') {
354
- const rules = Object.entries(config.inline)
355
- .filter(([, styles]) => styles && typeof styles === 'object')
356
- .map(([name, styles]) => {
357
- const declarations = Object.entries(styles)
358
- .map(([prop, value]) => ` ${prop}: ${value};`)
359
- .join('\n')
360
- return `span[${name}] {\n${declarations}\n}`
361
- })
362
- if (rules.length > 0) {
363
- sections.push('/* Inline Text Styles */\n' + rules.join('\n\n'))
364
- }
365
- }
366
-
367
- return sections.join('\n\n')
368
- }
369
-
370
- /**
371
- * Generate CSS for a single context (useful for testing)
372
- */
373
- export { generateContextCSS }
374
-
375
- /**
376
- * Generate palette CSS variables (useful for testing)
377
- */
378
- export { generatePaletteVars }
379
-
380
- /**
381
- * Get default context tokens
382
- */
383
- export function getDefaultContextTokens() {
384
- return JSON.parse(JSON.stringify(DEFAULT_CONTEXT_TOKENS))
385
- }
386
-
387
- /**
388
- * Get default colors
389
- */
390
- export function getDefaultColors() {
391
- return { ...DEFAULT_COLORS }
392
- }
393
-
394
- export default {
395
- generateThemeCSS,
396
- generateContextCSS,
397
- generatePaletteVars,
398
- getDefaultContextTokens,
399
- getDefaultColors,
400
- }