@uniweb/build 0.1.28 → 0.1.29

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.1.28",
3
+ "version": "0.1.29",
4
4
  "description": "Build tooling for the Uniweb Component Web Platform",
5
5
  "type": "module",
6
6
  "exports": {
@@ -50,8 +50,8 @@
50
50
  "sharp": "^0.33.2"
51
51
  },
52
52
  "optionalDependencies": {
53
- "@uniweb/content-reader": "1.0.4",
54
- "@uniweb/runtime": "0.2.15"
53
+ "@uniweb/runtime": "0.2.16",
54
+ "@uniweb/content-reader": "1.0.4"
55
55
  },
56
56
  "peerDependencies": {
57
57
  "vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
@@ -21,8 +21,9 @@
21
21
  */
22
22
 
23
23
  import { existsSync, readFileSync } from 'node:fs'
24
- import { resolve, dirname } from 'node:path'
24
+ import { resolve, dirname, join } from 'node:path'
25
25
  import yaml from 'js-yaml'
26
+ import { generateEntryPoint } from '../generate-entry.js'
26
27
 
27
28
  /**
28
29
  * Detect foundation type from the foundation config value
@@ -177,8 +178,52 @@ export async function defineSiteConfig(options = {}) {
177
178
  foundationDevPlugin = modules[3].foundationDevPlugin
178
179
  }
179
180
 
181
+ // Plugin to ensure foundation entry file exists (for bundled mode with local foundation)
182
+ const ensureFoundationEntryPlugin = !isRuntimeMode && foundationInfo.type === 'local' ? {
183
+ name: 'uniweb:ensure-foundation-entry',
184
+ async config() {
185
+ const srcDir = join(foundationInfo.path, 'src')
186
+ const entryPath = join(srcDir, '_entry.generated.js')
187
+
188
+ // Always regenerate on dev start to ensure it's current
189
+ // This handles new components being added
190
+ if (existsSync(srcDir)) {
191
+ console.log('[site] Ensuring foundation entry is up to date...')
192
+ try {
193
+ await generateEntryPoint(srcDir, entryPath)
194
+ } catch (err) {
195
+ console.warn('[site] Failed to generate foundation entry:', err.message)
196
+ }
197
+ }
198
+ },
199
+
200
+ configureServer(server) {
201
+ // Watch foundation src for meta.js changes to regenerate entry
202
+ const srcDir = join(foundationInfo.path, 'src')
203
+ const entryPath = join(srcDir, '_entry.generated.js')
204
+
205
+ server.watcher.add(join(srcDir, '**', 'meta.js'))
206
+
207
+ server.watcher.on('all', async (event, path) => {
208
+ // Regenerate entry when meta.js files change (new/deleted components)
209
+ if (path.includes(srcDir) && path.endsWith('meta.js')) {
210
+ console.log(`[site] Foundation meta.js changed, regenerating entry...`)
211
+ try {
212
+ await generateEntryPoint(srcDir, entryPath)
213
+ server.ws.send({ type: 'full-reload' })
214
+ } catch (err) {
215
+ console.warn('[site] Failed to regenerate foundation entry:', err.message)
216
+ }
217
+ }
218
+ })
219
+ }
220
+ } : null
221
+
180
222
  // Build the plugins array
181
223
  const plugins = [
224
+ // Ensure foundation entry exists first (bundled mode only)
225
+ ensureFoundationEntryPlugin,
226
+
182
227
  // Standard plugins
183
228
  tailwind && tailwindcss(),
184
229
  react(),
@@ -274,6 +274,7 @@ export function siteContentPlugin(options = {}) {
274
274
  let server = null
275
275
  let localeTranslations = {} // Cache: { locale: translations }
276
276
  let localesDir = 'locales' // Default, updated from site config
277
+ let collectionsConfig = null // Cached for watcher setup
277
278
 
278
279
  /**
279
280
  * Load translations for a specific locale
@@ -332,10 +333,28 @@ export function siteContentPlugin(options = {}) {
332
333
  return {
333
334
  name: 'uniweb:site-content',
334
335
 
335
- configResolved(config) {
336
+ async configResolved(config) {
336
337
  resolvedSitePath = resolve(config.root, sitePath)
337
338
  resolvedOutDir = resolve(config.root, config.build.outDir)
338
339
  isProduction = config.command === 'build'
340
+
341
+ // In dev mode, process collections early so JSON files exist before server starts
342
+ // This runs before configureServer, ensuring data is available immediately
343
+ if (!isProduction) {
344
+ try {
345
+ // Do an early content collection to get the collections config
346
+ const earlyContent = await collectSiteContent(resolvedSitePath)
347
+ collectionsConfig = earlyContent.config?.collections
348
+
349
+ if (collectionsConfig) {
350
+ console.log('[site-content] Processing content collections...')
351
+ const collections = await processCollections(resolvedSitePath, collectionsConfig)
352
+ await writeCollectionFiles(resolvedSitePath, collections)
353
+ }
354
+ } catch (err) {
355
+ console.warn('[site-content] Early collection processing failed:', err.message)
356
+ }
357
+ }
339
358
  },
340
359
 
341
360
  async buildStart() {
@@ -345,8 +364,9 @@ export function siteContentPlugin(options = {}) {
345
364
  console.log(`[site-content] Collected ${siteContent.pages?.length || 0} pages`)
346
365
 
347
366
  // Process content collections if defined in site.yml
348
- // This generates JSON files in public/data/ BEFORE the Vite build
349
- if (siteContent.config?.collections) {
367
+ // In dev mode, this was already done in configResolved (before server starts)
368
+ // In production, do it here
369
+ if (isProduction && siteContent.config?.collections) {
350
370
  console.log('[site-content] Processing content collections...')
351
371
  const collections = await processCollections(resolvedSitePath, siteContent.config.collections)
352
372
  await writeCollectionFiles(resolvedSitePath, collections)
@@ -399,9 +419,11 @@ export function siteContentPlugin(options = {}) {
399
419
  collectionRebuildTimeout = setTimeout(async () => {
400
420
  console.log('[site-content] Collection content changed, regenerating JSON...')
401
421
  try {
402
- if (siteContent?.config?.collections) {
403
- const collections = await processCollections(resolvedSitePath, siteContent.config.collections)
404
- await writeCollectionFiles(resolvedSitePath, collections)
422
+ // Use collectionsConfig (cached from configResolved) or siteContent
423
+ const collections = collectionsConfig || siteContent?.config?.collections
424
+ if (collections) {
425
+ const processed = await processCollections(resolvedSitePath, collections)
426
+ await writeCollectionFiles(resolvedSitePath, processed)
405
427
  }
406
428
  // Send full reload to client
407
429
  server.ws.send({ type: 'full-reload' })
@@ -437,9 +459,10 @@ export function siteContentPlugin(options = {}) {
437
459
  }
438
460
 
439
461
  // Watch content/ folder for collection changes
440
- if (siteContent?.config?.collections) {
462
+ // Use collectionsConfig cached from configResolved (siteContent may be null here)
463
+ if (collectionsConfig) {
441
464
  const contentPaths = new Set()
442
- for (const config of Object.values(siteContent.config.collections)) {
465
+ for (const config of Object.values(collectionsConfig)) {
443
466
  const collectionPath = typeof config === 'string' ? config : config.path
444
467
  if (collectionPath) {
445
468
  contentPaths.add(resolve(resolvedSitePath, collectionPath))