@uniweb/build 0.8.1 → 0.8.3
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 +5 -5
- package/src/runtime-schema.js +3 -3
- package/src/site/config.js +2 -1
- package/src/site/content-collector.js +177 -60
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/build",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.3",
|
|
4
4
|
"description": "Build tooling for the Uniweb Component Web Platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -50,9 +50,9 @@
|
|
|
50
50
|
"sharp": "^0.33.2"
|
|
51
51
|
},
|
|
52
52
|
"optionalDependencies": {
|
|
53
|
-
"@uniweb/content-reader": "1.1.
|
|
54
|
-
"@uniweb/
|
|
55
|
-
"@uniweb/
|
|
53
|
+
"@uniweb/content-reader": "1.1.4",
|
|
54
|
+
"@uniweb/schemas": "0.2.1",
|
|
55
|
+
"@uniweb/runtime": "0.6.3"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
58
|
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
|
|
@@ -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.5.
|
|
64
|
+
"@uniweb/core": "0.5.4"
|
|
65
65
|
},
|
|
66
66
|
"peerDependenciesMeta": {
|
|
67
67
|
"vite": {
|
package/src/runtime-schema.js
CHANGED
|
@@ -190,9 +190,9 @@ export function extractRuntimeSchema(fullMeta) {
|
|
|
190
190
|
|
|
191
191
|
const runtime = {}
|
|
192
192
|
|
|
193
|
-
//
|
|
194
|
-
if (fullMeta.
|
|
195
|
-
runtime.
|
|
193
|
+
// Inset flag: signals this component is available for inline @ references
|
|
194
|
+
if (fullMeta.inset) {
|
|
195
|
+
runtime.inset = true
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
// Background opt-out: 'self' means the component renders its own background
|
package/src/site/config.js
CHANGED
|
@@ -483,7 +483,8 @@ export async function defineSiteConfig(options = {}) {
|
|
|
483
483
|
},
|
|
484
484
|
|
|
485
485
|
optimizeDeps: {
|
|
486
|
-
include: ['react', 'react-dom', 'react-dom/client', 'react-router-dom']
|
|
486
|
+
include: ['react', 'react-dom', 'react-dom/client', 'react-router-dom'],
|
|
487
|
+
exclude: ['#foundation']
|
|
487
488
|
},
|
|
488
489
|
|
|
489
490
|
...restOptions
|
|
@@ -179,42 +179,42 @@ async function readYamlFile(filePath) {
|
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
/**
|
|
182
|
-
* Extract
|
|
182
|
+
* Extract inset references from a ProseMirror document.
|
|
183
183
|
*
|
|
184
|
-
* Walks top-level nodes for `
|
|
184
|
+
* Walks top-level nodes for `inset_ref` (produced by content-reader
|
|
185
185
|
* for `{params}` syntax). Each ref is removed from the
|
|
186
|
-
* document and replaced with an `
|
|
186
|
+
* document and replaced with an `inset_placeholder` node carrying a
|
|
187
187
|
* unique refId. The extracted refs are returned as an array.
|
|
188
188
|
*
|
|
189
189
|
* @param {Object} doc - ProseMirror document (mutated in place)
|
|
190
|
-
* @returns {Array} Array of { refId, type, params,
|
|
190
|
+
* @returns {Array} Array of { refId, type, params, description }
|
|
191
191
|
*/
|
|
192
|
-
function
|
|
192
|
+
function extractInsets(doc) {
|
|
193
193
|
if (!doc?.content || !Array.isArray(doc.content)) return []
|
|
194
194
|
|
|
195
|
-
const
|
|
195
|
+
const insets = []
|
|
196
196
|
let refIndex = 0
|
|
197
197
|
|
|
198
198
|
for (let i = 0; i < doc.content.length; i++) {
|
|
199
199
|
const node = doc.content[i]
|
|
200
|
-
if (node.type === '
|
|
200
|
+
if (node.type === 'inset_ref') {
|
|
201
201
|
const { component, alt, ...params } = node.attrs || {}
|
|
202
|
-
const refId = `
|
|
203
|
-
|
|
202
|
+
const refId = `inset_${refIndex++}`
|
|
203
|
+
insets.push({
|
|
204
204
|
refId,
|
|
205
205
|
type: component,
|
|
206
206
|
params: Object.keys(params).length > 0 ? params : {},
|
|
207
|
-
|
|
207
|
+
description: alt || null,
|
|
208
208
|
})
|
|
209
209
|
// Replace in-place with placeholder
|
|
210
210
|
doc.content[i] = {
|
|
211
|
-
type: '
|
|
211
|
+
type: 'inset_placeholder',
|
|
212
212
|
attrs: { refId },
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
-
return
|
|
217
|
+
return insets
|
|
218
218
|
}
|
|
219
219
|
|
|
220
220
|
/**
|
|
@@ -231,6 +231,23 @@ function isMarkdownFile(filename) {
|
|
|
231
231
|
return true
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
+
/**
|
|
235
|
+
* Check if a filename uses the @ prefix (child section convention).
|
|
236
|
+
* @-prefixed files are excluded from auto-discovered top-level sections —
|
|
237
|
+
* they exist to be nested under a parent via `nest:` in page.yml.
|
|
238
|
+
*/
|
|
239
|
+
function isChildSection(filename) {
|
|
240
|
+
return filename.startsWith('@')
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Strip leading @ characters from a filename to get the section name.
|
|
245
|
+
* @card-a → card-a, @@sub-item → sub-item
|
|
246
|
+
*/
|
|
247
|
+
function stripAtPrefix(filename) {
|
|
248
|
+
return filename.replace(/^@+/, '')
|
|
249
|
+
}
|
|
250
|
+
|
|
234
251
|
/**
|
|
235
252
|
* Check if a folder should be ignored.
|
|
236
253
|
* Excludes folders starting with _ (drafts/private).
|
|
@@ -369,11 +386,9 @@ function resolveMounts(pathsConfig, sitePath, pagesPath) {
|
|
|
369
386
|
* Supports:
|
|
370
387
|
* - Simple: "1", "2", "3"
|
|
371
388
|
* - Decimal ordering: "1.5" (between 1 and 2), "2.5" (between 2 and 3)
|
|
372
|
-
* - Hierarchy via comma: "1,1" (child of 1), "1,2" (second child of 1)
|
|
373
|
-
* - Mixed: "1.5,1" (child of section 1.5)
|
|
374
389
|
*/
|
|
375
390
|
function parseNumericPrefix(filename) {
|
|
376
|
-
const match = filename.match(/^(\d+(
|
|
391
|
+
const match = filename.match(/^(\d+(?:\.\d+)*)-?(.*)$/)
|
|
377
392
|
if (match) {
|
|
378
393
|
return { prefix: match[1], name: match[2] || match[1] }
|
|
379
394
|
}
|
|
@@ -382,19 +397,20 @@ function parseNumericPrefix(filename) {
|
|
|
382
397
|
|
|
383
398
|
/**
|
|
384
399
|
* Compare filenames for sorting by numeric prefix.
|
|
385
|
-
*
|
|
386
|
-
* This ensures correct ordering: 1, 1,1, 1.5, 2, 2,1, etc.
|
|
400
|
+
* Dots are treated as sub-level separators: 1, 1.5, 2, 2.5, etc.
|
|
387
401
|
*/
|
|
388
402
|
function compareFilenames(a, b) {
|
|
389
|
-
const
|
|
390
|
-
const
|
|
403
|
+
const nameA = isChildSection(parse(a).name) ? stripAtPrefix(parse(a).name) : parse(a).name
|
|
404
|
+
const nameB = isChildSection(parse(b).name) ? stripAtPrefix(parse(b).name) : parse(b).name
|
|
405
|
+
const { prefix: prefixA } = parseNumericPrefix(nameA)
|
|
406
|
+
const { prefix: prefixB } = parseNumericPrefix(nameB)
|
|
391
407
|
|
|
392
408
|
if (!prefixA && !prefixB) return a.localeCompare(b)
|
|
393
409
|
if (!prefixA) return 1
|
|
394
410
|
if (!prefixB) return -1
|
|
395
411
|
|
|
396
|
-
const partsA = prefixA.split(
|
|
397
|
-
const partsB = prefixB.split(
|
|
412
|
+
const partsA = prefixA.split('.').map(Number)
|
|
413
|
+
const partsB = prefixB.split('.').map(Number)
|
|
398
414
|
|
|
399
415
|
for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
|
|
400
416
|
const numA = partsA[i] ?? 0
|
|
@@ -488,27 +504,37 @@ function applyWildcardOrder(items, parsed) {
|
|
|
488
504
|
}
|
|
489
505
|
|
|
490
506
|
/**
|
|
491
|
-
* Find the markdown file for a section name, handling numeric prefixes.
|
|
492
|
-
*
|
|
507
|
+
* Find the markdown file for a section name, handling numeric prefixes and @ prefix.
|
|
508
|
+
* Search order: name.md → @name.md → N-name.md → @N-name.md
|
|
493
509
|
*
|
|
494
510
|
* @param {string} pagePath - Directory containing section files
|
|
495
|
-
* @param {string} sectionName - Logical section name (e.g., 'hero')
|
|
511
|
+
* @param {string} sectionName - Logical section name (e.g., 'hero', 'card-a')
|
|
496
512
|
* @param {string[]} [cachedFiles] - Pre-read directory listing (optimization)
|
|
497
|
-
* @returns {{ filePath: string, stableName: string, prefix: string|null }|null}
|
|
513
|
+
* @returns {{ filePath: string, stableName: string, prefix: string|null, isChild: boolean }|null}
|
|
498
514
|
*/
|
|
499
515
|
function findSectionFile(pagePath, sectionName, cachedFiles) {
|
|
516
|
+
// Try exact match: name.md
|
|
500
517
|
const exactPath = join(pagePath, `${sectionName}.md`)
|
|
501
518
|
if (existsSync(exactPath)) {
|
|
502
|
-
return { filePath: exactPath, stableName: sectionName, prefix: null }
|
|
519
|
+
return { filePath: exactPath, stableName: sectionName, prefix: null, isChild: false }
|
|
503
520
|
}
|
|
504
521
|
|
|
522
|
+
// Try @-prefixed exact match: @name.md
|
|
523
|
+
const atPath = join(pagePath, `@${sectionName}.md`)
|
|
524
|
+
if (existsSync(atPath)) {
|
|
525
|
+
return { filePath: atPath, stableName: sectionName, prefix: null, isChild: true }
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Try prefix-based: N-name.md or @N-name.md
|
|
505
529
|
const files = cachedFiles || []
|
|
506
530
|
for (const file of files) {
|
|
507
531
|
if (!isMarkdownFile(file)) continue
|
|
508
532
|
const { name } = parse(file)
|
|
509
|
-
const
|
|
533
|
+
const isChild = isChildSection(name)
|
|
534
|
+
const stripped = isChild ? stripAtPrefix(name) : name
|
|
535
|
+
const { prefix, name: parsedName } = parseNumericPrefix(stripped)
|
|
510
536
|
if (parsedName === sectionName) {
|
|
511
|
-
return { filePath: join(pagePath, file), stableName: sectionName, prefix }
|
|
537
|
+
return { filePath: join(pagePath, file), stableName: sectionName, prefix, isChild }
|
|
512
538
|
}
|
|
513
539
|
}
|
|
514
540
|
|
|
@@ -618,8 +644,8 @@ async function processMarkdownFile(filePath, id, siteRoot, defaultStableId = nul
|
|
|
618
644
|
// Convert markdown to ProseMirror
|
|
619
645
|
const proseMirrorContent = markdownToProseMirror(markdown)
|
|
620
646
|
|
|
621
|
-
// Extract @ component references →
|
|
622
|
-
const
|
|
647
|
+
// Extract @ component references → insets (mutates doc)
|
|
648
|
+
const insets = extractInsets(proseMirrorContent)
|
|
623
649
|
|
|
624
650
|
// Support 'data:' shorthand for collection fetch
|
|
625
651
|
// data: team → fetch: { collection: team }
|
|
@@ -643,7 +669,7 @@ async function processMarkdownFile(filePath, id, siteRoot, defaultStableId = nul
|
|
|
643
669
|
params: { ...params, ...props },
|
|
644
670
|
content: proseMirrorContent,
|
|
645
671
|
fetch: parseFetchConfig(resolvedFetch),
|
|
646
|
-
...(
|
|
672
|
+
...(insets.length > 0 ? { insets } : {}),
|
|
647
673
|
subsections: []
|
|
648
674
|
}
|
|
649
675
|
|
|
@@ -657,41 +683,80 @@ async function processMarkdownFile(filePath, id, siteRoot, defaultStableId = nul
|
|
|
657
683
|
}
|
|
658
684
|
|
|
659
685
|
/**
|
|
660
|
-
*
|
|
661
|
-
*
|
|
662
|
-
*
|
|
663
|
-
* -
|
|
664
|
-
* -
|
|
686
|
+
* Process `nest:` config from page.yml to attach child sections to parents.
|
|
687
|
+
*
|
|
688
|
+
* nest:
|
|
689
|
+
* features: [card-a, card-b]
|
|
690
|
+
* card-a: [sub-1, sub-2]
|
|
691
|
+
*
|
|
692
|
+
* For each parent→children mapping, finds the parent section in the list,
|
|
693
|
+
* processes child files (expected to use @ prefix), and attaches as subsections.
|
|
694
|
+
* nest: overrides any inline nesting from sections: config.
|
|
695
|
+
*
|
|
696
|
+
* @param {Array} sections - Built top-level sections
|
|
697
|
+
* @param {Object} nestConfig - The nest: object from page.yml
|
|
698
|
+
* @param {string} pagePath - Path to page directory
|
|
699
|
+
* @param {string} siteRoot - Site root for asset resolution
|
|
700
|
+
* @param {string[]} cachedFiles - Pre-read directory listing
|
|
701
|
+
* @returns {Object} { assetCollection, iconCollection, lastModified, attachedChildren }
|
|
665
702
|
*/
|
|
666
|
-
function
|
|
667
|
-
const
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
sectionMap.set(section.id, section)
|
|
703
|
+
async function processNesting(sections, nestConfig, pagePath, siteRoot, cachedFiles) {
|
|
704
|
+
const result = {
|
|
705
|
+
assetCollection: { assets: {}, hasExplicitPoster: new Set(), hasExplicitPreview: new Set() },
|
|
706
|
+
iconCollection: { icons: new Set(), bySource: new Map() },
|
|
707
|
+
lastModified: null,
|
|
708
|
+
attachedChildren: new Set(),
|
|
673
709
|
}
|
|
674
710
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
711
|
+
if (!nestConfig || typeof nestConfig !== 'object') return result
|
|
712
|
+
|
|
713
|
+
for (const [parentName, childNames] of Object.entries(nestConfig)) {
|
|
714
|
+
if (!Array.isArray(childNames)) continue
|
|
715
|
+
|
|
716
|
+
// Find parent section by stableId
|
|
717
|
+
const parent = sections.find(s => s.stableId === parentName)
|
|
718
|
+
if (!parent) {
|
|
719
|
+
console.warn(`[content-collector] nest: parent section '${parentName}' not found`)
|
|
679
720
|
continue
|
|
680
721
|
}
|
|
681
722
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
const parent = sectionMap.get(parentId)
|
|
723
|
+
// Override any existing subsections (nest: wins over inline sections: nesting)
|
|
724
|
+
parent.subsections = []
|
|
685
725
|
|
|
686
|
-
|
|
726
|
+
let childIndex = 1
|
|
727
|
+
for (const childName of childNames) {
|
|
728
|
+
const found = findSectionFile(pagePath, childName, cachedFiles)
|
|
729
|
+
if (!found) {
|
|
730
|
+
console.warn(`[content-collector] nest: child section '${childName}' not found`)
|
|
731
|
+
continue
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// Validate @ prefix
|
|
735
|
+
if (!found.isChild) {
|
|
736
|
+
console.warn(
|
|
737
|
+
`[content-collector] Section '${childName}' is declared as a child of '${parentName}' ` +
|
|
738
|
+
`but the file doesn't use the @ prefix`
|
|
739
|
+
)
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
const childId = `${parent.id}.${childIndex}`
|
|
743
|
+
const { section, assetCollection, iconCollection } =
|
|
744
|
+
await processMarkdownFile(found.filePath, childId, siteRoot, childName)
|
|
687
745
|
parent.subsections.push(section)
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
746
|
+
result.assetCollection = mergeAssetCollections(result.assetCollection, assetCollection)
|
|
747
|
+
result.iconCollection = mergeIconCollections(result.iconCollection, iconCollection)
|
|
748
|
+
result.attachedChildren.add(childName)
|
|
749
|
+
|
|
750
|
+
const fileStat = await stat(found.filePath)
|
|
751
|
+
if (!result.lastModified || fileStat.mtime > result.lastModified) {
|
|
752
|
+
result.lastModified = fileStat.mtime
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
childIndex++
|
|
691
756
|
}
|
|
692
757
|
}
|
|
693
758
|
|
|
694
|
-
return
|
|
759
|
+
return result
|
|
695
760
|
}
|
|
696
761
|
|
|
697
762
|
/**
|
|
@@ -828,11 +893,14 @@ async function processPage(pagePath, pageName, siteRoot, { isIndex = false, pare
|
|
|
828
893
|
|
|
829
894
|
if (sectionsConfig === undefined || sectionsConfig === '*' || sectionsParsed?.mode === 'all') {
|
|
830
895
|
// Default behavior: discover all .md files, sort by numeric prefix
|
|
896
|
+
// @-prefixed files are excluded from top-level (they're child sections for nest:)
|
|
831
897
|
const files = await readdir(pagePath)
|
|
832
898
|
const mdFiles = files.filter(isMarkdownFile).sort(compareFilenames)
|
|
833
899
|
|
|
834
900
|
const sections = []
|
|
835
901
|
for (const file of mdFiles) {
|
|
902
|
+
if (isChildSection(file)) continue // Skip @-prefixed child sections
|
|
903
|
+
|
|
836
904
|
const { name } = parse(file)
|
|
837
905
|
const { prefix, name: stableName } = parseNumericPrefix(name)
|
|
838
906
|
const id = prefix || name
|
|
@@ -852,17 +920,45 @@ async function processPage(pagePath, pageName, siteRoot, { isIndex = false, pare
|
|
|
852
920
|
}
|
|
853
921
|
}
|
|
854
922
|
|
|
855
|
-
//
|
|
856
|
-
|
|
923
|
+
// Process nest: config to attach child sections to parents
|
|
924
|
+
const nestResult = await processNesting(sections, pageConfig.nest, pagePath, siteRoot, files)
|
|
925
|
+
pageAssetCollection = mergeAssetCollections(pageAssetCollection, nestResult.assetCollection)
|
|
926
|
+
pageIconCollection = mergeIconCollections(pageIconCollection, nestResult.iconCollection)
|
|
927
|
+
if (nestResult.lastModified && (!lastModified || nestResult.lastModified > lastModified)) {
|
|
928
|
+
lastModified = nestResult.lastModified
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
// Warn about orphaned @-prefixed files (no parent in nest: or sections:)
|
|
932
|
+
const childFiles = mdFiles.filter(isChildSection)
|
|
933
|
+
for (const file of childFiles) {
|
|
934
|
+
const { name } = parse(file)
|
|
935
|
+
const stripped = stripAtPrefix(name)
|
|
936
|
+
const { name: childName } = parseNumericPrefix(stripped)
|
|
937
|
+
if (!nestResult.attachedChildren.has(childName)) {
|
|
938
|
+
console.warn(`[content-collector] Orphaned child section: ${file} (no parent declared in nest:)`)
|
|
939
|
+
// Graceful degradation: add to top level to avoid silent data loss
|
|
940
|
+
const id = String(sections.length + 1)
|
|
941
|
+
const { section, assetCollection, iconCollection } =
|
|
942
|
+
await processMarkdownFile(join(pagePath, file), id, siteRoot, childName)
|
|
943
|
+
sections.push(section)
|
|
944
|
+
pageAssetCollection = mergeAssetCollections(pageAssetCollection, assetCollection)
|
|
945
|
+
pageIconCollection = mergeIconCollections(pageIconCollection, iconCollection)
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
hierarchicalSections = sections
|
|
857
950
|
|
|
858
951
|
} else if (sectionsParsed?.mode === 'inclusive') {
|
|
859
952
|
// Inclusive: pinned sections + auto-discovered rest via '...' wildcard
|
|
953
|
+
// @-prefixed files are excluded from auto-discovery (they're child sections for nest:)
|
|
860
954
|
const files = await readdir(pagePath)
|
|
861
955
|
const mdFiles = files.filter(isMarkdownFile).sort(compareFilenames)
|
|
862
956
|
|
|
863
|
-
// Build name → file info map from discovered files
|
|
957
|
+
// Build name → file info map from discovered files (excluding @ children)
|
|
864
958
|
const discoveredMap = new Map()
|
|
865
959
|
for (const file of mdFiles) {
|
|
960
|
+
if (isChildSection(file)) continue // Skip @-prefixed child sections
|
|
961
|
+
|
|
866
962
|
const { name } = parse(file)
|
|
867
963
|
const { prefix, name: stableName } = parseNumericPrefix(name)
|
|
868
964
|
const key = stableName || name
|
|
@@ -924,16 +1020,37 @@ async function processPage(pagePath, pageName, siteRoot, { isIndex = false, pare
|
|
|
924
1020
|
sectionIndex++
|
|
925
1021
|
}
|
|
926
1022
|
|
|
927
|
-
|
|
1023
|
+
// Process nest: config to attach child sections (overrides inline nesting)
|
|
1024
|
+
if (pageConfig.nest) {
|
|
1025
|
+
const nestResult = await processNesting(sections, pageConfig.nest, pagePath, siteRoot, files)
|
|
1026
|
+
pageAssetCollection = mergeAssetCollections(pageAssetCollection, nestResult.assetCollection)
|
|
1027
|
+
pageIconCollection = mergeIconCollections(pageIconCollection, nestResult.iconCollection)
|
|
1028
|
+
if (nestResult.lastModified && (!lastModified || nestResult.lastModified > lastModified)) {
|
|
1029
|
+
lastModified = nestResult.lastModified
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
hierarchicalSections = sections
|
|
928
1034
|
|
|
929
1035
|
} else if (Array.isArray(sectionsConfig) && sectionsConfig.length > 0) {
|
|
930
1036
|
// Strict: explicit sections array (only listed sections processed)
|
|
1037
|
+
const cachedFiles = await readdir(pagePath)
|
|
931
1038
|
const result = await processExplicitSections(sectionsConfig, pagePath, siteRoot)
|
|
932
1039
|
hierarchicalSections = result.sections
|
|
933
1040
|
pageAssetCollection = result.assetCollection
|
|
934
1041
|
pageIconCollection = result.iconCollection
|
|
935
1042
|
lastModified = result.lastModified
|
|
936
1043
|
|
|
1044
|
+
// Process nest: config (overrides inline nesting from sections:)
|
|
1045
|
+
if (pageConfig.nest) {
|
|
1046
|
+
const nestResult = await processNesting(hierarchicalSections, pageConfig.nest, pagePath, siteRoot, cachedFiles)
|
|
1047
|
+
pageAssetCollection = mergeAssetCollections(pageAssetCollection, nestResult.assetCollection)
|
|
1048
|
+
pageIconCollection = mergeIconCollections(pageIconCollection, nestResult.iconCollection)
|
|
1049
|
+
if (nestResult.lastModified && (!lastModified || nestResult.lastModified > lastModified)) {
|
|
1050
|
+
lastModified = nestResult.lastModified
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
|
|
937
1054
|
} else {
|
|
938
1055
|
// Empty sections (null, empty array, or invalid) = pure route with no content
|
|
939
1056
|
// hierarchicalSections stays empty, lastModified stays null
|
|
@@ -1866,7 +1983,7 @@ export {
|
|
|
1866
1983
|
parseWildcardArray,
|
|
1867
1984
|
applyWildcardOrder,
|
|
1868
1985
|
getDirectChildName,
|
|
1869
|
-
|
|
1986
|
+
extractInsets
|
|
1870
1987
|
}
|
|
1871
1988
|
|
|
1872
1989
|
export default collectSiteContent
|