@uniweb/build 0.6.4 → 0.6.5
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/i18n/extract.js +1 -1
- package/src/i18n/merge.js +3 -3
- package/src/search/extract.js +0 -5
- package/src/site/collection-processor.js +6 -5
- package/src/site/config.js +15 -1
- package/src/site/content-collector.js +65 -36
- package/src/site/plugin.js +54 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/build",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.5",
|
|
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/schemas": "0.2.1",
|
|
54
|
-
"@uniweb/runtime": "0.5.
|
|
54
|
+
"@uniweb/runtime": "0.5.14",
|
|
55
55
|
"@uniweb/content-reader": "1.1.2"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"@tailwindcss/vite": "^4.0.0",
|
|
62
62
|
"@vitejs/plugin-react": "^4.0.0 || ^5.0.0",
|
|
63
63
|
"vite-plugin-svgr": "^4.0.0",
|
|
64
|
-
"@uniweb/core": "0.4.
|
|
64
|
+
"@uniweb/core": "0.4.3"
|
|
65
65
|
},
|
|
66
66
|
"peerDependenciesMeta": {
|
|
67
67
|
"vite": {
|
package/src/i18n/extract.js
CHANGED
|
@@ -41,7 +41,7 @@ export function extractTranslatableContent(siteContent) {
|
|
|
41
41
|
for (const layoutKey of ['header', 'footer', 'left', 'right']) {
|
|
42
42
|
const layoutPage = siteContent[layoutKey]
|
|
43
43
|
if (layoutPage?.sections) {
|
|
44
|
-
const pageRoute = layoutPage.route ||
|
|
44
|
+
const pageRoute = layoutPage.route || `/layout/${layoutKey}`
|
|
45
45
|
for (const section of layoutPage.sections) {
|
|
46
46
|
extractFromSection(section, pageRoute, units)
|
|
47
47
|
}
|
package/src/i18n/merge.js
CHANGED
|
@@ -80,7 +80,7 @@ function mergeTranslationsSync(siteContent, translations, fallbackToSource) {
|
|
|
80
80
|
for (const layoutKey of ['header', 'footer', 'left', 'right']) {
|
|
81
81
|
const layoutPage = translated[layoutKey]
|
|
82
82
|
if (layoutPage?.sections) {
|
|
83
|
-
const pageRoute = layoutPage.route ||
|
|
83
|
+
const pageRoute = layoutPage.route || `/layout/${layoutKey}`
|
|
84
84
|
for (const section of layoutPage.sections) {
|
|
85
85
|
translateSectionSync(section, pageRoute, translations, fallbackToSource)
|
|
86
86
|
}
|
|
@@ -132,8 +132,8 @@ async function mergeTranslationsAsync(siteContent, translations, options) {
|
|
|
132
132
|
for (const layoutKey of ['header', 'footer', 'left', 'right']) {
|
|
133
133
|
const layoutPage = translated[layoutKey]
|
|
134
134
|
if (layoutPage?.sections) {
|
|
135
|
-
// Ensure route is set for context matching
|
|
136
|
-
if (!layoutPage.route) layoutPage.route =
|
|
135
|
+
// Ensure route is set for context matching
|
|
136
|
+
if (!layoutPage.route) layoutPage.route = `/layout/${layoutKey}`
|
|
137
137
|
for (const section of layoutPage.sections) {
|
|
138
138
|
await translateSectionAsync(section, layoutPage, translations, {
|
|
139
139
|
fallbackToSource,
|
package/src/search/extract.js
CHANGED
|
@@ -41,11 +41,6 @@ export function extractSearchContent(siteContent, options = {}) {
|
|
|
41
41
|
continue
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
// Skip special pages (header, footer, etc.)
|
|
45
|
-
if (pageRoute.startsWith('/@')) {
|
|
46
|
-
continue
|
|
47
|
-
}
|
|
48
|
-
|
|
49
44
|
// Skip pages marked as noindex
|
|
50
45
|
if (page.seo?.noindex) {
|
|
51
46
|
continue
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
28
|
import { readFile, readdir, stat, writeFile, mkdir, copyFile } from 'node:fs/promises'
|
|
29
|
-
import { join, basename, extname, dirname, relative } from 'node:path'
|
|
29
|
+
import { join, basename, extname, dirname, relative, resolve } from 'node:path'
|
|
30
30
|
import { existsSync } from 'node:fs'
|
|
31
31
|
import yaml from 'js-yaml'
|
|
32
32
|
import { applyFilter, applySort } from './data-fetcher.js'
|
|
@@ -421,8 +421,9 @@ async function processContentItem(dir, filename, config, siteRoot) {
|
|
|
421
421
|
* @param {Object} config - Parsed collection config
|
|
422
422
|
* @returns {Promise<Array>} Array of processed items
|
|
423
423
|
*/
|
|
424
|
-
async function collectItems(siteDir, config) {
|
|
425
|
-
const
|
|
424
|
+
async function collectItems(siteDir, config, collectionsBase) {
|
|
425
|
+
const base = collectionsBase || siteDir
|
|
426
|
+
const collectionDir = resolve(base, config.path)
|
|
426
427
|
|
|
427
428
|
// Check if collection directory exists
|
|
428
429
|
if (!existsSync(collectionDir)) {
|
|
@@ -496,7 +497,7 @@ async function collectItems(siteDir, config) {
|
|
|
496
497
|
* })
|
|
497
498
|
* // { articles: [...], products: [...] }
|
|
498
499
|
*/
|
|
499
|
-
export async function processCollections(siteDir, collectionsConfig) {
|
|
500
|
+
export async function processCollections(siteDir, collectionsConfig, collectionsBase) {
|
|
500
501
|
if (!collectionsConfig || typeof collectionsConfig !== 'object') {
|
|
501
502
|
return {}
|
|
502
503
|
}
|
|
@@ -505,7 +506,7 @@ export async function processCollections(siteDir, collectionsConfig) {
|
|
|
505
506
|
|
|
506
507
|
for (const [name, config] of Object.entries(collectionsConfig)) {
|
|
507
508
|
const parsed = parseCollectionConfig(name, config)
|
|
508
|
-
const items = await collectItems(siteDir, parsed)
|
|
509
|
+
const items = await collectItems(siteDir, parsed, collectionsBase)
|
|
509
510
|
results[name] = items
|
|
510
511
|
console.log(`[collection-processor] Processed ${name}: ${items.length} items`)
|
|
511
512
|
}
|
package/src/site/config.js
CHANGED
|
@@ -328,7 +328,21 @@ export async function defineSiteConfig(options = {}) {
|
|
|
328
328
|
server: {
|
|
329
329
|
fs: {
|
|
330
330
|
// Allow parent directory for foundation sibling access
|
|
331
|
-
|
|
331
|
+
// Plus any external content paths from site.yml paths: group
|
|
332
|
+
allow: (() => {
|
|
333
|
+
const allowed = ['..']
|
|
334
|
+
const parentDir = resolve(siteRoot, '..')
|
|
335
|
+
const paths = siteConfig.paths || {}
|
|
336
|
+
for (const key of ['pages', 'layout', 'collections']) {
|
|
337
|
+
if (paths[key]) {
|
|
338
|
+
const resolved = resolve(siteRoot, paths[key])
|
|
339
|
+
if (!resolved.startsWith(parentDir)) {
|
|
340
|
+
allowed.push(resolved)
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return allowed
|
|
345
|
+
})()
|
|
332
346
|
},
|
|
333
347
|
...(siteConfig.build?.port && { port: siteConfig.build.port }),
|
|
334
348
|
...serverOverrides
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Content Collector
|
|
3
3
|
*
|
|
4
|
-
* Collects site content from a
|
|
4
|
+
* Collects site content from a site directory structure:
|
|
5
5
|
* - site.yml: Site configuration
|
|
6
6
|
* - pages/: Directory of page folders
|
|
7
7
|
* - page.yml: Page metadata
|
|
8
8
|
* - *.md: Section content with YAML frontmatter
|
|
9
|
+
* - layout/: Layout panel folders (header, footer, left, right)
|
|
9
10
|
*
|
|
10
11
|
* Section frontmatter reserved properties:
|
|
11
12
|
* - type: Component type (e.g., "Hero", "Features")
|
|
@@ -23,7 +24,7 @@
|
|
|
23
24
|
*/
|
|
24
25
|
|
|
25
26
|
import { readFile, readdir, stat } from 'node:fs/promises'
|
|
26
|
-
import { join, parse } from 'node:path'
|
|
27
|
+
import { join, parse, resolve } from 'node:path'
|
|
27
28
|
import { existsSync } from 'node:fs'
|
|
28
29
|
import yaml from 'js-yaml'
|
|
29
30
|
import { collectSectionAssets, mergeAssetCollections } from './assets.js'
|
|
@@ -502,10 +503,7 @@ async function processPage(pagePath, pageName, siteRoot, { isIndex = false, pare
|
|
|
502
503
|
|
|
503
504
|
// First, calculate the folder-based route (what the route would be without index handling)
|
|
504
505
|
let folderRoute
|
|
505
|
-
if (
|
|
506
|
-
// Special pages (layout areas) keep their @ prefix
|
|
507
|
-
folderRoute = parentRoute === '/' ? `/@${pageName.slice(1)}` : `${parentRoute}/@${pageName.slice(1)}`
|
|
508
|
-
} else if (isDynamic) {
|
|
506
|
+
if (isDynamic) {
|
|
509
507
|
// Dynamic routes: /blog/[slug] → /blog/:slug (for route matching)
|
|
510
508
|
folderRoute = parentRoute === '/' ? `/:${paramName}` : `${parentRoute}/:${paramName}`
|
|
511
509
|
} else {
|
|
@@ -648,10 +646,6 @@ async function collectPagesRecursive(dirPath, parentRoute, siteRoot, orderConfig
|
|
|
648
646
|
icons: new Set(),
|
|
649
647
|
bySource: new Map()
|
|
650
648
|
}
|
|
651
|
-
let header = null
|
|
652
|
-
let footer = null
|
|
653
|
-
let left = null
|
|
654
|
-
let right = null
|
|
655
649
|
let notFound = null
|
|
656
650
|
const versionedScopes = new Map() // scope route → versionMeta
|
|
657
651
|
|
|
@@ -737,8 +731,8 @@ async function collectPagesRecursive(dirPath, parentRoute, siteRoot, orderConfig
|
|
|
737
731
|
for (const [scope, meta] of subResult.versionedScopes) {
|
|
738
732
|
versionedScopes.set(scope, meta)
|
|
739
733
|
}
|
|
740
|
-
} else
|
|
741
|
-
// Non-version
|
|
734
|
+
} else {
|
|
735
|
+
// Non-version folders in a versioned section
|
|
742
736
|
// These could be shared across versions - process normally
|
|
743
737
|
const result = await processPage(entryPath, entry, siteRoot, {
|
|
744
738
|
isIndex: false,
|
|
@@ -755,14 +749,14 @@ async function collectPagesRecursive(dirPath, parentRoute, siteRoot, orderConfig
|
|
|
755
749
|
}
|
|
756
750
|
|
|
757
751
|
// Return early - we've handled all children
|
|
758
|
-
return { pages, assetCollection, iconCollection,
|
|
752
|
+
return { pages, assetCollection, iconCollection, notFound, versionedScopes }
|
|
759
753
|
}
|
|
760
754
|
|
|
761
755
|
// Determine which page is the index for this level
|
|
762
756
|
// A directory with its own .md content is a real page, not a container —
|
|
763
757
|
// never promote a child as index, even if explicit config says so
|
|
764
758
|
// (that config is likely a leftover from before the directory had content)
|
|
765
|
-
const regularFolders = pageFolders
|
|
759
|
+
const regularFolders = pageFolders
|
|
766
760
|
const hasExplicitOrder = orderConfig?.index || (Array.isArray(orderConfig?.pages) && orderConfig.pages.length > 0)
|
|
767
761
|
const hasMdContent = entries.some(e => isMarkdownFile(e))
|
|
768
762
|
const indexPageName = hasMdContent ? null : determineIndexPage(orderConfig, regularFolders)
|
|
@@ -771,12 +765,11 @@ async function collectPagesRecursive(dirPath, parentRoute, siteRoot, orderConfig
|
|
|
771
765
|
for (const folder of pageFolders) {
|
|
772
766
|
const { name: entry, path: entryPath, childOrderConfig } = folder
|
|
773
767
|
const isIndex = entry === indexPageName
|
|
774
|
-
const isSpecial = entry.startsWith('@')
|
|
775
768
|
|
|
776
769
|
// Process this directory as a page
|
|
777
770
|
// Pass parentFetch so dynamic routes can inherit parent's data schema
|
|
778
771
|
const result = await processPage(entryPath, entry, siteRoot, {
|
|
779
|
-
isIndex
|
|
772
|
+
isIndex,
|
|
780
773
|
parentRoute,
|
|
781
774
|
parentFetch,
|
|
782
775
|
versionContext
|
|
@@ -787,27 +780,15 @@ async function collectPagesRecursive(dirPath, parentRoute, siteRoot, orderConfig
|
|
|
787
780
|
assetCollection = mergeAssetCollections(assetCollection, pageAssets)
|
|
788
781
|
iconCollection = mergeIconCollections(iconCollection, pageIcons)
|
|
789
782
|
|
|
790
|
-
// Handle
|
|
791
|
-
if (parentRoute === '/') {
|
|
792
|
-
|
|
793
|
-
header = page
|
|
794
|
-
} else if (entry === '@footer') {
|
|
795
|
-
footer = page
|
|
796
|
-
} else if (entry === '@left') {
|
|
797
|
-
left = page
|
|
798
|
-
} else if (entry === '@right') {
|
|
799
|
-
right = page
|
|
800
|
-
} else if (entry === '404') {
|
|
801
|
-
notFound = page
|
|
802
|
-
} else {
|
|
803
|
-
pages.push(page)
|
|
804
|
-
}
|
|
783
|
+
// Handle 404 page - only at root level
|
|
784
|
+
if (parentRoute === '/' && entry === '404') {
|
|
785
|
+
notFound = page
|
|
805
786
|
} else {
|
|
806
787
|
pages.push(page)
|
|
807
788
|
}
|
|
808
789
|
|
|
809
|
-
// Recursively process subdirectories
|
|
810
|
-
|
|
790
|
+
// Recursively process subdirectories
|
|
791
|
+
{
|
|
811
792
|
// The child route depends on whether this page is the index
|
|
812
793
|
// For explicit index (from site.yml `index:` or `pages:`), children use parentRoute
|
|
813
794
|
// since that's a true structural promotion. For auto-detected index, children use
|
|
@@ -830,7 +811,7 @@ async function collectPagesRecursive(dirPath, parentRoute, siteRoot, orderConfig
|
|
|
830
811
|
}
|
|
831
812
|
}
|
|
832
813
|
|
|
833
|
-
return { pages, assetCollection, iconCollection,
|
|
814
|
+
return { pages, assetCollection, iconCollection, notFound, versionedScopes }
|
|
834
815
|
}
|
|
835
816
|
|
|
836
817
|
/**
|
|
@@ -863,6 +844,43 @@ async function loadFoundationVars(foundationPath) {
|
|
|
863
844
|
}
|
|
864
845
|
}
|
|
865
846
|
|
|
847
|
+
/**
|
|
848
|
+
* Collect layout panels from the layout/ directory
|
|
849
|
+
*
|
|
850
|
+
* Layout panels (header, footer, left, right) are persistent regions
|
|
851
|
+
* that appear on every page. They live in layout/ parallel to pages/.
|
|
852
|
+
*
|
|
853
|
+
* @param {string} layoutDir - Path to layout directory
|
|
854
|
+
* @param {string} siteRoot - Path to site root
|
|
855
|
+
* @returns {Promise<Object>} { header, footer, left, right }
|
|
856
|
+
*/
|
|
857
|
+
async function collectLayoutPanels(layoutDir, siteRoot) {
|
|
858
|
+
const result = { header: null, footer: null, left: null, right: null }
|
|
859
|
+
|
|
860
|
+
if (!existsSync(layoutDir)) return result
|
|
861
|
+
|
|
862
|
+
const knownPanels = ['header', 'footer', 'left', 'right']
|
|
863
|
+
const entries = await readdir(layoutDir)
|
|
864
|
+
|
|
865
|
+
for (const entry of entries) {
|
|
866
|
+
if (!knownPanels.includes(entry)) continue
|
|
867
|
+
const entryPath = join(layoutDir, entry)
|
|
868
|
+
const stats = await stat(entryPath)
|
|
869
|
+
if (!stats.isDirectory()) continue
|
|
870
|
+
|
|
871
|
+
const pageResult = await processPage(entryPath, entry, siteRoot, {
|
|
872
|
+
isIndex: false,
|
|
873
|
+
parentRoute: '/layout'
|
|
874
|
+
})
|
|
875
|
+
|
|
876
|
+
if (pageResult) {
|
|
877
|
+
result[entry] = pageResult.page
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
return result
|
|
882
|
+
}
|
|
883
|
+
|
|
866
884
|
/**
|
|
867
885
|
* Collect all site content
|
|
868
886
|
*
|
|
@@ -873,10 +891,18 @@ async function loadFoundationVars(foundationPath) {
|
|
|
873
891
|
*/
|
|
874
892
|
export async function collectSiteContent(sitePath, options = {}) {
|
|
875
893
|
const { foundationPath } = options
|
|
876
|
-
const pagesPath = join(sitePath, 'pages')
|
|
877
894
|
|
|
878
895
|
// Read site config and raw theme config
|
|
879
896
|
const siteConfig = await readYamlFile(join(sitePath, 'site.yml'))
|
|
897
|
+
|
|
898
|
+
// Resolve content paths from site.yml paths: group, defaulting to standard locations
|
|
899
|
+
const pagesPath = siteConfig.paths?.pages
|
|
900
|
+
? resolve(sitePath, siteConfig.paths.pages)
|
|
901
|
+
: join(sitePath, 'pages')
|
|
902
|
+
|
|
903
|
+
const layoutPath = siteConfig.paths?.layout
|
|
904
|
+
? resolve(sitePath, siteConfig.paths.layout)
|
|
905
|
+
: join(sitePath, 'layout')
|
|
880
906
|
const rawThemeConfig = await readYamlFile(join(sitePath, 'theme.yml'))
|
|
881
907
|
|
|
882
908
|
// Load foundation vars and process theme
|
|
@@ -907,8 +933,11 @@ export async function collectSiteContent(sitePath, options = {}) {
|
|
|
907
933
|
index: siteConfig.index
|
|
908
934
|
}
|
|
909
935
|
|
|
936
|
+
// Collect layout panels from layout/ directory
|
|
937
|
+
const { header, footer, left, right } = await collectLayoutPanels(layoutPath, sitePath)
|
|
938
|
+
|
|
910
939
|
// Recursively collect all pages
|
|
911
|
-
const { pages, assetCollection, iconCollection,
|
|
940
|
+
const { pages, assetCollection, iconCollection, notFound, versionedScopes } =
|
|
912
941
|
await collectPagesRecursive(pagesPath, '/', sitePath, siteOrderConfig)
|
|
913
942
|
|
|
914
943
|
// Deduplicate: remove content-less container pages whose route duplicates
|
package/src/site/plugin.js
CHANGED
|
@@ -376,6 +376,9 @@ export function siteContentPlugin(options = {}) {
|
|
|
376
376
|
let collectionTranslations = {} // Cache: { locale: collection translations }
|
|
377
377
|
let localesDir = 'locales' // Default, updated from site config
|
|
378
378
|
let collectionsConfig = null // Cached for watcher setup
|
|
379
|
+
let resolvedPagesPath = null // Resolved from site.yml pagesDir or default
|
|
380
|
+
let resolvedLayoutPath = null // Resolved from site.yml layoutDir or default
|
|
381
|
+
let resolvedCollectionsBase = null // Resolved from site.yml collectionsDir
|
|
379
382
|
|
|
380
383
|
/**
|
|
381
384
|
* Load translations for a specific locale
|
|
@@ -486,15 +489,43 @@ export function siteContentPlugin(options = {}) {
|
|
|
486
489
|
const earlyContent = await collectSiteContent(resolvedSitePath, { foundationPath })
|
|
487
490
|
collectionsConfig = earlyContent.config?.collections
|
|
488
491
|
|
|
492
|
+
// Resolve content directory paths from site.yml paths: group
|
|
493
|
+
const paths = earlyContent?.config?.paths || {}
|
|
494
|
+
resolvedPagesPath = paths.pages
|
|
495
|
+
? resolve(resolvedSitePath, paths.pages)
|
|
496
|
+
: resolve(resolvedSitePath, pagesDir)
|
|
497
|
+
resolvedLayoutPath = paths.layout
|
|
498
|
+
? resolve(resolvedSitePath, paths.layout)
|
|
499
|
+
: resolve(resolvedSitePath, 'layout')
|
|
500
|
+
resolvedCollectionsBase = paths.collections
|
|
501
|
+
? resolve(resolvedSitePath, paths.collections)
|
|
502
|
+
: null
|
|
503
|
+
|
|
489
504
|
if (collectionsConfig) {
|
|
490
505
|
console.log('[site-content] Processing content collections...')
|
|
491
|
-
const collections = await processCollections(resolvedSitePath, collectionsConfig)
|
|
506
|
+
const collections = await processCollections(resolvedSitePath, collectionsConfig, resolvedCollectionsBase)
|
|
492
507
|
await writeCollectionFiles(resolvedSitePath, collections)
|
|
493
508
|
}
|
|
494
509
|
} catch (err) {
|
|
495
510
|
console.warn('[site-content] Early collection processing failed:', err.message)
|
|
496
511
|
}
|
|
497
512
|
}
|
|
513
|
+
|
|
514
|
+
// In production, resolve content paths from site.yml directly
|
|
515
|
+
if (isProduction || !resolvedPagesPath) {
|
|
516
|
+
const { readSiteConfig } = await import('./config.js')
|
|
517
|
+
const cfg = readSiteConfig(resolvedSitePath)
|
|
518
|
+
const paths = cfg.paths || {}
|
|
519
|
+
resolvedPagesPath = paths.pages
|
|
520
|
+
? resolve(resolvedSitePath, paths.pages)
|
|
521
|
+
: resolve(resolvedSitePath, pagesDir)
|
|
522
|
+
resolvedLayoutPath = paths.layout
|
|
523
|
+
? resolve(resolvedSitePath, paths.layout)
|
|
524
|
+
: resolve(resolvedSitePath, 'layout')
|
|
525
|
+
resolvedCollectionsBase = paths.collections
|
|
526
|
+
? resolve(resolvedSitePath, paths.collections)
|
|
527
|
+
: null
|
|
528
|
+
}
|
|
498
529
|
},
|
|
499
530
|
|
|
500
531
|
async buildStart() {
|
|
@@ -508,7 +539,7 @@ export function siteContentPlugin(options = {}) {
|
|
|
508
539
|
// In production, do it here
|
|
509
540
|
if (isProduction && siteContent.config?.collections) {
|
|
510
541
|
console.log('[site-content] Processing content collections...')
|
|
511
|
-
const collections = await processCollections(resolvedSitePath, siteContent.config.collections)
|
|
542
|
+
const collections = await processCollections(resolvedSitePath, siteContent.config.collections, resolvedCollectionsBase)
|
|
512
543
|
await writeCollectionFiles(resolvedSitePath, collections)
|
|
513
544
|
}
|
|
514
545
|
|
|
@@ -537,7 +568,6 @@ export function siteContentPlugin(options = {}) {
|
|
|
537
568
|
|
|
538
569
|
// Watch for content changes in dev mode
|
|
539
570
|
if (shouldWatch) {
|
|
540
|
-
const pagesPath = resolve(resolvedSitePath, pagesDir)
|
|
541
571
|
const siteYmlPath = resolve(resolvedSitePath, 'site.yml')
|
|
542
572
|
const themeYmlPath = resolve(resolvedSitePath, 'theme.yml')
|
|
543
573
|
|
|
@@ -571,7 +601,7 @@ export function siteContentPlugin(options = {}) {
|
|
|
571
601
|
// Use collectionsConfig (cached from configResolved) or siteContent
|
|
572
602
|
const collections = collectionsConfig || siteContent?.config?.collections
|
|
573
603
|
if (collections) {
|
|
574
|
-
const processed = await processCollections(resolvedSitePath, collections)
|
|
604
|
+
const processed = await processCollections(resolvedSitePath, collections, resolvedCollectionsBase)
|
|
575
605
|
await writeCollectionFiles(resolvedSitePath, processed)
|
|
576
606
|
}
|
|
577
607
|
// Send full reload to client
|
|
@@ -585,12 +615,24 @@ export function siteContentPlugin(options = {}) {
|
|
|
585
615
|
// Track all watchers for cleanup
|
|
586
616
|
const watchers = []
|
|
587
617
|
|
|
588
|
-
// Watch pages directory
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
618
|
+
// Watch pages directory (resolved from site.yml pagesDir or default)
|
|
619
|
+
if (existsSync(resolvedPagesPath)) {
|
|
620
|
+
try {
|
|
621
|
+
watchers.push(watch(resolvedPagesPath, { recursive: true }, scheduleRebuild))
|
|
622
|
+
console.log(`[site-content] Watching ${resolvedPagesPath}`)
|
|
623
|
+
} catch (err) {
|
|
624
|
+
console.warn('[site-content] Could not watch pages directory:', err.message)
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// Watch layout directory (resolved from site.yml layoutDir or default)
|
|
629
|
+
if (existsSync(resolvedLayoutPath)) {
|
|
630
|
+
try {
|
|
631
|
+
watchers.push(watch(resolvedLayoutPath, { recursive: true }, scheduleRebuild))
|
|
632
|
+
console.log(`[site-content] Watching ${resolvedLayoutPath}`)
|
|
633
|
+
} catch (err) {
|
|
634
|
+
console.warn('[site-content] Could not watch layout directory:', err.message)
|
|
635
|
+
}
|
|
594
636
|
}
|
|
595
637
|
|
|
596
638
|
// Watch site.yml
|
|
@@ -611,10 +653,11 @@ export function siteContentPlugin(options = {}) {
|
|
|
611
653
|
// Use collectionsConfig cached from configResolved (siteContent may be null here)
|
|
612
654
|
if (collectionsConfig) {
|
|
613
655
|
const contentPaths = new Set()
|
|
656
|
+
const collectionBase = resolvedCollectionsBase || resolvedSitePath
|
|
614
657
|
for (const config of Object.values(collectionsConfig)) {
|
|
615
658
|
const collectionPath = typeof config === 'string' ? config : config.path
|
|
616
659
|
if (collectionPath) {
|
|
617
|
-
contentPaths.add(resolve(
|
|
660
|
+
contentPaths.add(resolve(collectionBase, collectionPath))
|
|
618
661
|
}
|
|
619
662
|
}
|
|
620
663
|
|