@uniweb/build 0.3.0 → 0.3.1
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 +3 -3
- package/src/prerender.js +136 -63
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/build",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
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.7",
|
|
54
|
-
"@uniweb/runtime": "0.
|
|
54
|
+
"@uniweb/runtime": "0.4.2"
|
|
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.2.
|
|
63
|
+
"@uniweb/core": "0.2.4"
|
|
64
64
|
},
|
|
65
65
|
"peerDependenciesMeta": {
|
|
66
66
|
"vite": {
|
package/src/prerender.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { readFile, writeFile, mkdir } from 'node:fs/promises'
|
|
12
|
-
import { existsSync } from 'node:fs'
|
|
12
|
+
import { existsSync, readdirSync, statSync } from 'node:fs'
|
|
13
13
|
import { join, dirname, resolve } from 'node:path'
|
|
14
14
|
import { pathToFileURL } from 'node:url'
|
|
15
15
|
import { createRequire } from 'node:module'
|
|
@@ -381,6 +381,62 @@ function createPageElement(page, website) {
|
|
|
381
381
|
)
|
|
382
382
|
}
|
|
383
383
|
|
|
384
|
+
/**
|
|
385
|
+
* Discover all locale content files in the dist directory
|
|
386
|
+
* Returns an array of { locale, contentPath, htmlPath, isDefault }
|
|
387
|
+
*
|
|
388
|
+
* @param {string} distDir - Path to dist directory
|
|
389
|
+
* @param {Object} defaultContent - Default site content (to get default locale)
|
|
390
|
+
* @returns {Array} Locale configurations
|
|
391
|
+
*/
|
|
392
|
+
async function discoverLocaleContents(distDir, defaultContent) {
|
|
393
|
+
const locales = []
|
|
394
|
+
const defaultLocale = defaultContent.config?.defaultLanguage || 'en'
|
|
395
|
+
|
|
396
|
+
// Add the default locale (root level)
|
|
397
|
+
locales.push({
|
|
398
|
+
locale: defaultLocale,
|
|
399
|
+
contentPath: join(distDir, 'site-content.json'),
|
|
400
|
+
htmlPath: join(distDir, 'index.html'),
|
|
401
|
+
isDefault: true,
|
|
402
|
+
routePrefix: ''
|
|
403
|
+
})
|
|
404
|
+
|
|
405
|
+
// Check for locale subdirectories with site-content.json
|
|
406
|
+
try {
|
|
407
|
+
const entries = readdirSync(distDir)
|
|
408
|
+
for (const entry of entries) {
|
|
409
|
+
const entryPath = join(distDir, entry)
|
|
410
|
+
// Skip if not a directory
|
|
411
|
+
if (!statSync(entryPath).isDirectory()) continue
|
|
412
|
+
|
|
413
|
+
// Check if this looks like a locale code (2-3 letter code)
|
|
414
|
+
if (!/^[a-z]{2,3}(-[A-Z]{2})?$/.test(entry)) continue
|
|
415
|
+
|
|
416
|
+
// Check if it has a site-content.json
|
|
417
|
+
const localeContentPath = join(entryPath, 'site-content.json')
|
|
418
|
+
const localeHtmlPath = join(entryPath, 'index.html')
|
|
419
|
+
|
|
420
|
+
if (existsSync(localeContentPath)) {
|
|
421
|
+
locales.push({
|
|
422
|
+
locale: entry,
|
|
423
|
+
contentPath: localeContentPath,
|
|
424
|
+
htmlPath: localeHtmlPath,
|
|
425
|
+
isDefault: false,
|
|
426
|
+
routePrefix: `/${entry}`
|
|
427
|
+
})
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
} catch (err) {
|
|
431
|
+
// Ignore errors reading directory
|
|
432
|
+
if (process.env.DEBUG) {
|
|
433
|
+
console.error('Error discovering locale contents:', err.message)
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
return locales
|
|
438
|
+
}
|
|
439
|
+
|
|
384
440
|
/**
|
|
385
441
|
* Pre-render all pages in a built site to static HTML
|
|
386
442
|
*
|
|
@@ -407,33 +463,21 @@ export async function prerenderSite(siteDir, options = {}) {
|
|
|
407
463
|
onProgress('Loading dependencies...')
|
|
408
464
|
await loadDependencies(siteDir)
|
|
409
465
|
|
|
410
|
-
// Load site content
|
|
466
|
+
// Load default site content
|
|
411
467
|
onProgress('Loading site content...')
|
|
412
468
|
const contentPath = join(distDir, 'site-content.json')
|
|
413
469
|
if (!existsSync(contentPath)) {
|
|
414
470
|
throw new Error(`site-content.json not found at: ${contentPath}`)
|
|
415
471
|
}
|
|
416
|
-
const
|
|
417
|
-
|
|
418
|
-
// Execute data fetches (site, page, section levels)
|
|
419
|
-
onProgress('Executing data fetches...')
|
|
420
|
-
const { siteCascadedData, pageFetchedData } = await executeAllFetches(siteContent, siteDir, onProgress)
|
|
421
|
-
|
|
422
|
-
// Expand dynamic pages (e.g., /blog/:slug → /blog/post-1, /blog/post-2)
|
|
423
|
-
if (siteContent.pages?.some(p => p.isDynamic)) {
|
|
424
|
-
onProgress('Expanding dynamic routes...')
|
|
425
|
-
siteContent.pages = expandDynamicPages(siteContent.pages, pageFetchedData, onProgress)
|
|
426
|
-
}
|
|
472
|
+
const defaultSiteContent = JSON.parse(await readFile(contentPath, 'utf8'))
|
|
427
473
|
|
|
428
|
-
//
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
throw new Error(`index.html not found at: ${shellPath}`)
|
|
474
|
+
// Discover all locale content files
|
|
475
|
+
const localeConfigs = await discoverLocaleContents(distDir, defaultSiteContent)
|
|
476
|
+
if (localeConfigs.length > 1) {
|
|
477
|
+
onProgress(`Found ${localeConfigs.length} locales: ${localeConfigs.map(l => l.locale).join(', ')}`)
|
|
433
478
|
}
|
|
434
|
-
const htmlShell = await readFile(shellPath, 'utf8')
|
|
435
479
|
|
|
436
|
-
// Load the foundation module
|
|
480
|
+
// Load the foundation module (shared across all locales)
|
|
437
481
|
onProgress('Loading foundation...')
|
|
438
482
|
const foundationPath = join(foundationDir, 'dist', 'foundation.js')
|
|
439
483
|
if (!existsSync(foundationPath)) {
|
|
@@ -442,58 +486,87 @@ export async function prerenderSite(siteDir, options = {}) {
|
|
|
442
486
|
const foundationUrl = pathToFileURL(foundationPath).href
|
|
443
487
|
const foundation = await import(foundationUrl)
|
|
444
488
|
|
|
445
|
-
//
|
|
446
|
-
|
|
447
|
-
const uniweb = createUniweb(siteContent)
|
|
448
|
-
uniweb.setFoundation(foundation)
|
|
489
|
+
// Pre-render each locale
|
|
490
|
+
const renderedFiles = []
|
|
449
491
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
uniweb.setFoundationConfig(foundation.capabilities)
|
|
453
|
-
}
|
|
492
|
+
for (const localeConfig of localeConfigs) {
|
|
493
|
+
const { locale, contentPath: localeContentPath, htmlPath, isDefault, routePrefix } = localeConfig
|
|
454
494
|
|
|
455
|
-
|
|
456
|
-
const renderedFiles = []
|
|
457
|
-
const pages = uniweb.activeWebsite.pages
|
|
458
|
-
const website = uniweb.activeWebsite
|
|
495
|
+
onProgress(`\nRendering ${isDefault ? 'default' : locale} locale...`)
|
|
459
496
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
497
|
+
// Load locale-specific content
|
|
498
|
+
const siteContent = JSON.parse(await readFile(localeContentPath, 'utf8'))
|
|
499
|
+
|
|
500
|
+
// Set the active locale in the content
|
|
501
|
+
siteContent.config = siteContent.config || {}
|
|
502
|
+
siteContent.config.activeLocale = locale
|
|
503
|
+
|
|
504
|
+
// Execute data fetches (site, page, section levels)
|
|
505
|
+
onProgress('Executing data fetches...')
|
|
506
|
+
const { siteCascadedData, pageFetchedData } = await executeAllFetches(siteContent, siteDir, onProgress)
|
|
507
|
+
|
|
508
|
+
// Expand dynamic pages (e.g., /blog/:slug → /blog/post-1, /blog/post-2)
|
|
509
|
+
if (siteContent.pages?.some(p => p.isDynamic)) {
|
|
510
|
+
onProgress('Expanding dynamic routes...')
|
|
511
|
+
siteContent.pages = expandDynamicPages(siteContent.pages, pageFetchedData, onProgress)
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Load the HTML shell for this locale
|
|
515
|
+
const shellPath = existsSync(htmlPath) ? htmlPath : join(distDir, 'index.html')
|
|
516
|
+
const htmlShell = await readFile(shellPath, 'utf8')
|
|
517
|
+
|
|
518
|
+
// Initialize the Uniweb runtime for this locale
|
|
519
|
+
onProgress('Initializing runtime...')
|
|
520
|
+
const uniweb = createUniweb(siteContent)
|
|
521
|
+
uniweb.setFoundation(foundation)
|
|
522
|
+
|
|
523
|
+
// Set foundation capabilities (Layout, props, etc.)
|
|
524
|
+
if (foundation.capabilities) {
|
|
525
|
+
uniweb.setFoundationConfig(foundation.capabilities)
|
|
482
526
|
}
|
|
483
527
|
|
|
484
|
-
//
|
|
485
|
-
const
|
|
528
|
+
// Pre-render each page
|
|
529
|
+
const pages = uniweb.activeWebsite.pages
|
|
530
|
+
const website = uniweb.activeWebsite
|
|
486
531
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
await writeFile(outputPath, html)
|
|
532
|
+
for (const page of pages) {
|
|
533
|
+
// Build the output route with locale prefix
|
|
534
|
+
const outputRoute = routePrefix + page.route
|
|
491
535
|
|
|
492
|
-
|
|
493
|
-
|
|
536
|
+
onProgress(`Rendering ${outputRoute}...`)
|
|
537
|
+
|
|
538
|
+
// Set this as the active page
|
|
539
|
+
uniweb.activeWebsite.setActivePage(page.route)
|
|
540
|
+
|
|
541
|
+
// Create the page element using inline SSR rendering
|
|
542
|
+
const element = createPageElement(page, website)
|
|
543
|
+
|
|
544
|
+
// Render to HTML string
|
|
545
|
+
let renderedContent
|
|
546
|
+
try {
|
|
547
|
+
renderedContent = renderToString(element)
|
|
548
|
+
} catch (err) {
|
|
549
|
+
console.warn(`Warning: Failed to render ${outputRoute}: ${err.message}`)
|
|
550
|
+
if (process.env.DEBUG) {
|
|
551
|
+
console.error(err.stack)
|
|
552
|
+
}
|
|
553
|
+
continue
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// Inject into shell
|
|
557
|
+
const html = injectContent(htmlShell, renderedContent, page, siteContent)
|
|
558
|
+
|
|
559
|
+
// Output to the locale-prefixed route
|
|
560
|
+
const outputPath = getOutputPath(distDir, outputRoute)
|
|
561
|
+
await mkdir(dirname(outputPath), { recursive: true })
|
|
562
|
+
await writeFile(outputPath, html)
|
|
563
|
+
|
|
564
|
+
renderedFiles.push(outputPath)
|
|
565
|
+
onProgress(` → ${outputPath.replace(distDir, 'dist')}`)
|
|
566
|
+
}
|
|
494
567
|
}
|
|
495
568
|
|
|
496
|
-
onProgress(
|
|
569
|
+
onProgress(`\nPre-rendered ${renderedFiles.length} pages across ${localeConfigs.length} locale(s)`)
|
|
497
570
|
|
|
498
571
|
return {
|
|
499
572
|
pages: renderedFiles.length,
|