uniweb 1.19.2 → 1.20.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 +10 -10
- package/partials/agents.md +2 -0
- package/src/commands/build.js +48 -99
- package/src/framework-index.json +8 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uniweb",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.20.1",
|
|
4
4
|
"description": "Create structured Vite + React sites with content/code separation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -37,19 +37,19 @@
|
|
|
37
37
|
"node": ">=22.12"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"handlebars": "^4.7.
|
|
41
|
-
"js-yaml": "^4.
|
|
40
|
+
"handlebars": "^4.7.10",
|
|
41
|
+
"js-yaml": "^4.3.2",
|
|
42
42
|
"prompts": "^2.4.2",
|
|
43
|
-
"tar": "^7.
|
|
44
|
-
"@uniweb/core": "~1.4.
|
|
45
|
-
"@uniweb/content-writer": "~1.3.0",
|
|
43
|
+
"tar": "^7.5.21",
|
|
44
|
+
"@uniweb/core": "~1.4.7",
|
|
46
45
|
"@uniweb/schemas": "~1.6.5",
|
|
47
|
-
"@uniweb/semantic-parser": "~1.13.0"
|
|
46
|
+
"@uniweb/semantic-parser": "~1.13.0",
|
|
47
|
+
"@uniweb/content-writer": "~1.3.1"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"@uniweb/build": "~1.
|
|
51
|
-
"@uniweb/
|
|
52
|
-
"@uniweb/
|
|
50
|
+
"@uniweb/build": "~1.10.1",
|
|
51
|
+
"@uniweb/semantic-parser": "~1.13.0",
|
|
52
|
+
"@uniweb/content-reader": "~1.5.7"
|
|
53
53
|
},
|
|
54
54
|
"peerDependenciesMeta": {
|
|
55
55
|
"@uniweb/build": {
|
package/partials/agents.md
CHANGED
|
@@ -801,6 +801,8 @@ The point is the pair. An agent fetches the index, *reads* what each page is abo
|
|
|
801
801
|
|
|
802
802
|
**Large sites also get per-branch indexes.** A branch with at least 5 pages gets its own — `/docs/llms.txt` lists just the docs, titled by that folder. The site index still lists everything; a branch index is an extra entry point, not a replacement, so an agent that starts at `/llms.txt` still reaches any page in two hops.
|
|
803
803
|
|
|
804
|
+
**Each language gets its own.** A site in several languages has `/fr/llms.txt` beside `/llms.txt`, listing the French pages, record pages included, and linking each at its French address (`/fr/a-propos.md`). Its search index is per language the same way (`/fr/search-index.json`).
|
|
805
|
+
|
|
804
806
|
```yaml
|
|
805
807
|
# site.yml — all optional; these are the defaults
|
|
806
808
|
agents:
|
package/src/commands/build.js
CHANGED
|
@@ -12,8 +12,9 @@
|
|
|
12
12
|
* --bundle (internal; called by `uniweb deploy --host` / `uniweb export`)
|
|
13
13
|
* Full vite + post-vite pipeline. Produces a static-host JS bundle
|
|
14
14
|
* (`dist/index.html`, `dist/entry.js`, `_importmap/*`, `_pages/*` for
|
|
15
|
-
* split mode, sitemap/robots
|
|
16
|
-
* configured). The foundation is
|
|
15
|
+
* split mode, sitemap/robots, each language's search index, llms.txt and
|
|
16
|
+
* page markdown, prerendered HTML when configured). The foundation is
|
|
17
|
+
* statically inlined when site.yml names a
|
|
17
18
|
* workspace-local one (the self-contained case), and loaded by URL when
|
|
18
19
|
* it names a URL, or under VITE_FOUNDATION_MODE=runtime. A catalog ref
|
|
19
20
|
* (`@org/name@version`) is refused (`detectFoundationType`).
|
|
@@ -461,19 +462,38 @@ async function loadI18nConfig(projectDir, siteConfig = null) {
|
|
|
461
462
|
}
|
|
462
463
|
|
|
463
464
|
/**
|
|
464
|
-
* Build
|
|
465
|
+
* Build every configured language but the default: its records, its content, and — on the bundle
|
|
466
|
+
* lane — its search index, llms.txt and page markdown, in that order (`buildLocalizedSite`).
|
|
467
|
+
*
|
|
468
|
+
* ⛔ Until 2026-10-25 each lane ran content, then records, here in two copies, and a language's
|
|
469
|
+
* search index was written in the content step, before its records were translated.
|
|
465
470
|
*/
|
|
466
|
-
async function
|
|
467
|
-
const {
|
|
468
|
-
|
|
469
|
-
const outputs = await buildLocalizedContent(projectDir, {
|
|
470
|
-
localesDir: i18nConfig.localesDir,
|
|
471
|
+
async function buildLocalizedSite(projectDir, i18nConfig, { projections = false } = {}) {
|
|
472
|
+
const { buildLocalizedSite } = await import('@uniweb/build/i18n')
|
|
473
|
+
return buildLocalizedSite(projectDir, {
|
|
471
474
|
locales: i18nConfig.locales,
|
|
475
|
+
localesDir: i18nConfig.localesDir,
|
|
472
476
|
outputDir: join(projectDir, 'dist'),
|
|
473
|
-
|
|
477
|
+
projections
|
|
474
478
|
})
|
|
479
|
+
}
|
|
475
480
|
|
|
476
|
-
|
|
481
|
+
/** What `buildLocalizedSite` wrote, per language. */
|
|
482
|
+
function reportLocalizedSite(built) {
|
|
483
|
+
const locales = Object.keys(built.content)
|
|
484
|
+
success(`Generated ${locales.length} locale(s)`)
|
|
485
|
+
for (const locale of locales) {
|
|
486
|
+
log(` ${colors.dim}dist/${locale}/site-content.json${colors.reset}`)
|
|
487
|
+
const files = built.projections[locale] || []
|
|
488
|
+
for (const file of files) {
|
|
489
|
+
if (file.kind === 'search') log(` ${colors.dim}dist/${file.fileName} (${file.count} entries)${colors.reset}`)
|
|
490
|
+
if (file.kind === 'index') log(` ${colors.dim}dist/${file.fileName}${colors.reset}`)
|
|
491
|
+
}
|
|
492
|
+
const markdown = files.filter((file) => file.kind === 'markdown').length
|
|
493
|
+
if (markdown) log(` ${colors.dim}dist/${locale}/ — ${markdown} page markdown projections${colors.reset}`)
|
|
494
|
+
}
|
|
495
|
+
const translated = Object.values(built.records).filter((outputs) => Object.keys(outputs).length > 0).length
|
|
496
|
+
if (translated > 0) success(`Translated records for ${translated} locale(s)`)
|
|
477
497
|
}
|
|
478
498
|
|
|
479
499
|
/**
|
|
@@ -586,16 +606,16 @@ function resolveFoundationDir(projectDir, siteConfig) {
|
|
|
586
606
|
*
|
|
587
607
|
* Does NOT emit HTML, JS, CSS, source maps, _importmap chunks, processed media
|
|
588
608
|
* (dist/assets/), or static-host extras (sitemap, robots, search-index,
|
|
589
|
-
* _pages/*) — `publish` reads none
|
|
590
|
-
* dist/assets/ — WebP copies, video
|
|
591
|
-
*
|
|
592
|
-
*
|
|
593
|
-
*
|
|
609
|
+
* llms.txt, page markdown, _pages/*), in any language — `publish` reads none
|
|
610
|
+
* of them. ⛔ *It processed media into dist/assets/ — WebP copies, video
|
|
611
|
+
* posters, PDF thumbnails — until 2026-10-19, and until 2026-10-25 wrote the
|
|
612
|
+
* default language's llms.txt and page markdown, and each other language's
|
|
613
|
+
* search index.*
|
|
594
614
|
*
|
|
595
|
-
* The `
|
|
596
|
-
*
|
|
597
|
-
* either mode
|
|
598
|
-
*
|
|
615
|
+
* The `buildLocalizedSite` step is the call bundle mode makes post-vite,
|
|
616
|
+
* without the projections, so multi-locale sites get identical per-locale
|
|
617
|
+
* outputs in either mode: each language's query files (dist/<lang>/data/),
|
|
618
|
+
* then its content (dist/<lang>/site-content.json).
|
|
599
619
|
*
|
|
600
620
|
* History: when `deploy` uploaded `dist/site-content.json` to a backend, it
|
|
601
621
|
* read the bundle pipeline's copy, which prerender rewrites into a lightweight
|
|
@@ -634,49 +654,16 @@ async function buildSiteLink(projectDir, options = {}) {
|
|
|
634
654
|
})
|
|
635
655
|
success(`Wrote ${join('dist', 'site-content.json')}`)
|
|
636
656
|
|
|
637
|
-
// Per-locale variants —
|
|
638
|
-
//
|
|
639
|
-
// `publish` reads which
|
|
657
|
+
// Per-locale variants — the call bundle mode makes post-vite, without the
|
|
658
|
+
// projections: a host derives its own. Both modes produce identical
|
|
659
|
+
// `dist/<lang>/site-content.json` outputs, and `publish` reads which
|
|
660
|
+
// languages were built from them.
|
|
640
661
|
const i18nConfig = await loadI18nConfig(projectDir, siteConfig)
|
|
641
662
|
if (i18nConfig && i18nConfig.locales.length > 0) {
|
|
642
663
|
log('')
|
|
643
664
|
info(`Building localized content for: ${i18nConfig.locales.join(', ')}`)
|
|
644
665
|
try {
|
|
645
|
-
|
|
646
|
-
success(`Generated ${Object.keys(outputs).length} locale(s)`)
|
|
647
|
-
for (const [locale] of Object.entries(outputs)) {
|
|
648
|
-
log(` ${colors.dim}dist/${locale}/site-content.json${colors.reset}`)
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
// Collection translations — optional; don't fail the build if
|
|
652
|
-
// missing. Bundle mode does the same.
|
|
653
|
-
try {
|
|
654
|
-
const { buildLocalizedRecords } = await import('@uniweb/build/i18n')
|
|
655
|
-
const recordOutputs = await buildLocalizedRecords(projectDir, {
|
|
656
|
-
locales: i18nConfig.locales,
|
|
657
|
-
outputDir: distDir,
|
|
658
|
-
recordLocalesDir: join(
|
|
659
|
-
projectDir,
|
|
660
|
-
i18nConfig.localesDir,
|
|
661
|
-
'records'
|
|
662
|
-
),
|
|
663
|
-
// Free-form record translations live under it — the default was `locales/`
|
|
664
|
-
// whatever the site said.
|
|
665
|
-
localesDir: join(projectDir, i18nConfig.localesDir)
|
|
666
|
-
})
|
|
667
|
-
const recordCount = Object.values(recordOutputs).reduce(
|
|
668
|
-
(sum, localeOutputs) => sum + Object.keys(localeOutputs).length,
|
|
669
|
-
0
|
|
670
|
-
)
|
|
671
|
-
if (recordCount > 0) {
|
|
672
|
-
success(
|
|
673
|
-
`Translated records for ${Object.keys(recordOutputs).length} locale(s)`
|
|
674
|
-
)
|
|
675
|
-
}
|
|
676
|
-
} catch (err) {
|
|
677
|
-
if (process.env.UNIWEB_DEBUG)
|
|
678
|
-
console.error('Record translation:', err.message)
|
|
679
|
-
}
|
|
666
|
+
reportLocalizedSite(await buildLocalizedSite(projectDir, i18nConfig))
|
|
680
667
|
} catch (err) {
|
|
681
668
|
error(`i18n build failed: ${err.message}`)
|
|
682
669
|
if (process.env.UNIWEB_DEBUG) console.error(err.stack)
|
|
@@ -722,52 +709,14 @@ async function buildSite(projectDir, options = {}) {
|
|
|
722
709
|
info(`Building localized content for: ${i18nConfig.locales.join(', ')}`)
|
|
723
710
|
|
|
724
711
|
try {
|
|
725
|
-
//
|
|
726
|
-
|
|
712
|
+
// Each language's records, then its content, then its search index, llms.txt and page
|
|
713
|
+
// markdown, made from both.
|
|
714
|
+
const built = await buildLocalizedSite(projectDir, i18nConfig, { projections: true })
|
|
727
715
|
|
|
728
716
|
// Generate locale-specific index.html files
|
|
729
717
|
await generateLocalizedHtml(projectDir, i18nConfig)
|
|
730
718
|
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
for (const [locale, path] of Object.entries(outputs)) {
|
|
734
|
-
log(` ${colors.dim}dist/${locale}/site-content.json${colors.reset}`)
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
// Translate collections if they exist
|
|
738
|
-
try {
|
|
739
|
-
const { buildLocalizedRecords } = await import('@uniweb/build/i18n')
|
|
740
|
-
|
|
741
|
-
const recordOutputs = await buildLocalizedRecords(projectDir, {
|
|
742
|
-
locales: i18nConfig.locales,
|
|
743
|
-
outputDir: join(projectDir, 'dist'),
|
|
744
|
-
recordLocalesDir: join(
|
|
745
|
-
projectDir,
|
|
746
|
-
i18nConfig.localesDir,
|
|
747
|
-
'records'
|
|
748
|
-
),
|
|
749
|
-
// Free-form record translations live under it — the default was `locales/`
|
|
750
|
-
// whatever the site said.
|
|
751
|
-
localesDir: join(projectDir, i18nConfig.localesDir)
|
|
752
|
-
})
|
|
753
|
-
|
|
754
|
-
// Count collections translated
|
|
755
|
-
const recordCount = Object.values(recordOutputs).reduce(
|
|
756
|
-
(sum, localeOutputs) => sum + Object.keys(localeOutputs).length,
|
|
757
|
-
0
|
|
758
|
-
)
|
|
759
|
-
|
|
760
|
-
if (recordCount > 0) {
|
|
761
|
-
success(
|
|
762
|
-
`Translated records for ${Object.keys(recordOutputs).length} locale(s)`
|
|
763
|
-
)
|
|
764
|
-
}
|
|
765
|
-
} catch (err) {
|
|
766
|
-
// Collection translation is optional, don't fail build
|
|
767
|
-
if (process.env.UNIWEB_DEBUG) {
|
|
768
|
-
console.error('Record translation:', err.message)
|
|
769
|
-
}
|
|
770
|
-
}
|
|
719
|
+
reportLocalizedSite(built)
|
|
771
720
|
} catch (err) {
|
|
772
721
|
error(`i18n build failed: ${err.message}`)
|
|
773
722
|
if (process.env.UNIWEB_DEBUG) {
|
package/src/framework-index.json
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"generatedAt": "2026-10-
|
|
3
|
+
"generatedAt": "2026-10-25T21:41:31.474Z",
|
|
4
4
|
"packages": {
|
|
5
5
|
"@uniweb/api": {
|
|
6
6
|
"version": "1.6.4"
|
|
7
7
|
},
|
|
8
8
|
"@uniweb/build": {
|
|
9
|
-
"version": "1.
|
|
9
|
+
"version": "1.10.1"
|
|
10
10
|
},
|
|
11
11
|
"@uniweb/content-reader": {
|
|
12
|
-
"version": "1.5.
|
|
12
|
+
"version": "1.5.7"
|
|
13
13
|
},
|
|
14
14
|
"@uniweb/content-writer": {
|
|
15
|
-
"version": "1.3.
|
|
15
|
+
"version": "1.3.1"
|
|
16
16
|
},
|
|
17
17
|
"@uniweb/core": {
|
|
18
|
-
"version": "1.4.
|
|
18
|
+
"version": "1.4.7"
|
|
19
19
|
},
|
|
20
20
|
"@uniweb/frame-bridge": {
|
|
21
21
|
"version": "1.0.2"
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"version": "1.2.0"
|
|
37
37
|
},
|
|
38
38
|
"@uniweb/runtime": {
|
|
39
|
-
"version": "1.2.
|
|
39
|
+
"version": "1.2.3"
|
|
40
40
|
},
|
|
41
41
|
"@uniweb/scene": {
|
|
42
42
|
"version": "1.0.1"
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"version": "1.13.0"
|
|
52
52
|
},
|
|
53
53
|
"@uniweb/snapshot": {
|
|
54
|
-
"version": "1.1.
|
|
54
|
+
"version": "1.1.1"
|
|
55
55
|
},
|
|
56
56
|
"@uniweb/templates": {
|
|
57
57
|
"version": "1.1.5"
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"version": "1.2.1"
|
|
61
61
|
},
|
|
62
62
|
"@uniweb/unipress": {
|
|
63
|
-
"version": "1.1.
|
|
63
|
+
"version": "1.1.16"
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
66
|
"templates": {
|