@uniweb/build 0.8.0 → 0.8.2
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 +4 -4
- package/src/runtime-schema.js +5 -0
- package/src/site/content-collector.js +206 -45
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/build",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
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.
|
|
53
|
+
"@uniweb/content-reader": "1.1.4",
|
|
54
54
|
"@uniweb/schemas": "0.2.1",
|
|
55
|
-
"@uniweb/runtime": "0.6.
|
|
55
|
+
"@uniweb/runtime": "0.6.2"
|
|
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.3"
|
|
65
65
|
},
|
|
66
66
|
"peerDependenciesMeta": {
|
|
67
67
|
"vite": {
|
package/src/runtime-schema.js
CHANGED
|
@@ -190,6 +190,11 @@ export function extractRuntimeSchema(fullMeta) {
|
|
|
190
190
|
|
|
191
191
|
const runtime = {}
|
|
192
192
|
|
|
193
|
+
// Inset flag: signals this component is available for inline @ references
|
|
194
|
+
if (fullMeta.inset) {
|
|
195
|
+
runtime.inset = true
|
|
196
|
+
}
|
|
197
|
+
|
|
193
198
|
// Background opt-out: 'self' means the component renders its own background
|
|
194
199
|
// layer (solid colors, insets, effects), so the runtime skips its Background.
|
|
195
200
|
if (fullMeta.background) {
|
|
@@ -178,6 +178,45 @@ async function readYamlFile(filePath) {
|
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
+
/**
|
|
182
|
+
* Extract inset references from a ProseMirror document.
|
|
183
|
+
*
|
|
184
|
+
* Walks top-level nodes for `inset_ref` (produced by content-reader
|
|
185
|
+
* for `{params}` syntax). Each ref is removed from the
|
|
186
|
+
* document and replaced with an `inset_placeholder` node carrying a
|
|
187
|
+
* unique refId. The extracted refs are returned as an array.
|
|
188
|
+
*
|
|
189
|
+
* @param {Object} doc - ProseMirror document (mutated in place)
|
|
190
|
+
* @returns {Array} Array of { refId, type, params, description }
|
|
191
|
+
*/
|
|
192
|
+
function extractInsets(doc) {
|
|
193
|
+
if (!doc?.content || !Array.isArray(doc.content)) return []
|
|
194
|
+
|
|
195
|
+
const insets = []
|
|
196
|
+
let refIndex = 0
|
|
197
|
+
|
|
198
|
+
for (let i = 0; i < doc.content.length; i++) {
|
|
199
|
+
const node = doc.content[i]
|
|
200
|
+
if (node.type === 'inset_ref') {
|
|
201
|
+
const { component, alt, ...params } = node.attrs || {}
|
|
202
|
+
const refId = `inset_${refIndex++}`
|
|
203
|
+
insets.push({
|
|
204
|
+
refId,
|
|
205
|
+
type: component,
|
|
206
|
+
params: Object.keys(params).length > 0 ? params : {},
|
|
207
|
+
description: alt || null,
|
|
208
|
+
})
|
|
209
|
+
// Replace in-place with placeholder
|
|
210
|
+
doc.content[i] = {
|
|
211
|
+
type: 'inset_placeholder',
|
|
212
|
+
attrs: { refId },
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return insets
|
|
218
|
+
}
|
|
219
|
+
|
|
181
220
|
/**
|
|
182
221
|
* Check if a file is a markdown file that should be processed.
|
|
183
222
|
* Excludes:
|
|
@@ -192,6 +231,23 @@ function isMarkdownFile(filename) {
|
|
|
192
231
|
return true
|
|
193
232
|
}
|
|
194
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
|
+
|
|
195
251
|
/**
|
|
196
252
|
* Check if a folder should be ignored.
|
|
197
253
|
* Excludes folders starting with _ (drafts/private).
|
|
@@ -330,11 +386,9 @@ function resolveMounts(pathsConfig, sitePath, pagesPath) {
|
|
|
330
386
|
* Supports:
|
|
331
387
|
* - Simple: "1", "2", "3"
|
|
332
388
|
* - Decimal ordering: "1.5" (between 1 and 2), "2.5" (between 2 and 3)
|
|
333
|
-
* - Hierarchy via comma: "1,1" (child of 1), "1,2" (second child of 1)
|
|
334
|
-
* - Mixed: "1.5,1" (child of section 1.5)
|
|
335
389
|
*/
|
|
336
390
|
function parseNumericPrefix(filename) {
|
|
337
|
-
const match = filename.match(/^(\d+(
|
|
391
|
+
const match = filename.match(/^(\d+(?:\.\d+)*)-?(.*)$/)
|
|
338
392
|
if (match) {
|
|
339
393
|
return { prefix: match[1], name: match[2] || match[1] }
|
|
340
394
|
}
|
|
@@ -343,19 +397,20 @@ function parseNumericPrefix(filename) {
|
|
|
343
397
|
|
|
344
398
|
/**
|
|
345
399
|
* Compare filenames for sorting by numeric prefix.
|
|
346
|
-
*
|
|
347
|
-
* 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.
|
|
348
401
|
*/
|
|
349
402
|
function compareFilenames(a, b) {
|
|
350
|
-
const
|
|
351
|
-
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)
|
|
352
407
|
|
|
353
408
|
if (!prefixA && !prefixB) return a.localeCompare(b)
|
|
354
409
|
if (!prefixA) return 1
|
|
355
410
|
if (!prefixB) return -1
|
|
356
411
|
|
|
357
|
-
const partsA = prefixA.split(
|
|
358
|
-
const partsB = prefixB.split(
|
|
412
|
+
const partsA = prefixA.split('.').map(Number)
|
|
413
|
+
const partsB = prefixB.split('.').map(Number)
|
|
359
414
|
|
|
360
415
|
for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
|
|
361
416
|
const numA = partsA[i] ?? 0
|
|
@@ -449,27 +504,37 @@ function applyWildcardOrder(items, parsed) {
|
|
|
449
504
|
}
|
|
450
505
|
|
|
451
506
|
/**
|
|
452
|
-
* Find the markdown file for a section name, handling numeric prefixes.
|
|
453
|
-
*
|
|
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
|
|
454
509
|
*
|
|
455
510
|
* @param {string} pagePath - Directory containing section files
|
|
456
|
-
* @param {string} sectionName - Logical section name (e.g., 'hero')
|
|
511
|
+
* @param {string} sectionName - Logical section name (e.g., 'hero', 'card-a')
|
|
457
512
|
* @param {string[]} [cachedFiles] - Pre-read directory listing (optimization)
|
|
458
|
-
* @returns {{ filePath: string, stableName: string, prefix: string|null }|null}
|
|
513
|
+
* @returns {{ filePath: string, stableName: string, prefix: string|null, isChild: boolean }|null}
|
|
459
514
|
*/
|
|
460
515
|
function findSectionFile(pagePath, sectionName, cachedFiles) {
|
|
516
|
+
// Try exact match: name.md
|
|
461
517
|
const exactPath = join(pagePath, `${sectionName}.md`)
|
|
462
518
|
if (existsSync(exactPath)) {
|
|
463
|
-
return { filePath: exactPath, stableName: sectionName, prefix: null }
|
|
519
|
+
return { filePath: exactPath, stableName: sectionName, prefix: null, isChild: false }
|
|
520
|
+
}
|
|
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 }
|
|
464
526
|
}
|
|
465
527
|
|
|
528
|
+
// Try prefix-based: N-name.md or @N-name.md
|
|
466
529
|
const files = cachedFiles || []
|
|
467
530
|
for (const file of files) {
|
|
468
531
|
if (!isMarkdownFile(file)) continue
|
|
469
532
|
const { name } = parse(file)
|
|
470
|
-
const
|
|
533
|
+
const isChild = isChildSection(name)
|
|
534
|
+
const stripped = isChild ? stripAtPrefix(name) : name
|
|
535
|
+
const { prefix, name: parsedName } = parseNumericPrefix(stripped)
|
|
471
536
|
if (parsedName === sectionName) {
|
|
472
|
-
return { filePath: join(pagePath, file), stableName: sectionName, prefix }
|
|
537
|
+
return { filePath: join(pagePath, file), stableName: sectionName, prefix, isChild }
|
|
473
538
|
}
|
|
474
539
|
}
|
|
475
540
|
|
|
@@ -579,6 +644,9 @@ async function processMarkdownFile(filePath, id, siteRoot, defaultStableId = nul
|
|
|
579
644
|
// Convert markdown to ProseMirror
|
|
580
645
|
const proseMirrorContent = markdownToProseMirror(markdown)
|
|
581
646
|
|
|
647
|
+
// Extract @ component references → insets (mutates doc)
|
|
648
|
+
const insets = extractInsets(proseMirrorContent)
|
|
649
|
+
|
|
582
650
|
// Support 'data:' shorthand for collection fetch
|
|
583
651
|
// data: team → fetch: { collection: team }
|
|
584
652
|
// data: [team, articles] → fetch: { collection: team } (first item, others via inheritData)
|
|
@@ -601,6 +669,7 @@ async function processMarkdownFile(filePath, id, siteRoot, defaultStableId = nul
|
|
|
601
669
|
params: { ...params, ...props },
|
|
602
670
|
content: proseMirrorContent,
|
|
603
671
|
fetch: parseFetchConfig(resolvedFetch),
|
|
672
|
+
...(insets.length > 0 ? { insets } : {}),
|
|
604
673
|
subsections: []
|
|
605
674
|
}
|
|
606
675
|
|
|
@@ -614,41 +683,80 @@ async function processMarkdownFile(filePath, id, siteRoot, defaultStableId = nul
|
|
|
614
683
|
}
|
|
615
684
|
|
|
616
685
|
/**
|
|
617
|
-
*
|
|
618
|
-
*
|
|
619
|
-
*
|
|
620
|
-
* -
|
|
621
|
-
* -
|
|
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 }
|
|
622
702
|
*/
|
|
623
|
-
function
|
|
624
|
-
const
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
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(),
|
|
630
709
|
}
|
|
631
710
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
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`)
|
|
636
720
|
continue
|
|
637
721
|
}
|
|
638
722
|
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
723
|
+
// Override any existing subsections (nest: wins over inline sections: nesting)
|
|
724
|
+
parent.subsections = []
|
|
725
|
+
|
|
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
|
+
}
|
|
642
741
|
|
|
643
|
-
|
|
742
|
+
const childId = `${parent.id}.${childIndex}`
|
|
743
|
+
const { section, assetCollection, iconCollection } =
|
|
744
|
+
await processMarkdownFile(found.filePath, childId, siteRoot, childName)
|
|
644
745
|
parent.subsections.push(section)
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
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++
|
|
648
756
|
}
|
|
649
757
|
}
|
|
650
758
|
|
|
651
|
-
return
|
|
759
|
+
return result
|
|
652
760
|
}
|
|
653
761
|
|
|
654
762
|
/**
|
|
@@ -785,11 +893,14 @@ async function processPage(pagePath, pageName, siteRoot, { isIndex = false, pare
|
|
|
785
893
|
|
|
786
894
|
if (sectionsConfig === undefined || sectionsConfig === '*' || sectionsParsed?.mode === 'all') {
|
|
787
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:)
|
|
788
897
|
const files = await readdir(pagePath)
|
|
789
898
|
const mdFiles = files.filter(isMarkdownFile).sort(compareFilenames)
|
|
790
899
|
|
|
791
900
|
const sections = []
|
|
792
901
|
for (const file of mdFiles) {
|
|
902
|
+
if (isChildSection(file)) continue // Skip @-prefixed child sections
|
|
903
|
+
|
|
793
904
|
const { name } = parse(file)
|
|
794
905
|
const { prefix, name: stableName } = parseNumericPrefix(name)
|
|
795
906
|
const id = prefix || name
|
|
@@ -809,17 +920,45 @@ async function processPage(pagePath, pageName, siteRoot, { isIndex = false, pare
|
|
|
809
920
|
}
|
|
810
921
|
}
|
|
811
922
|
|
|
812
|
-
//
|
|
813
|
-
|
|
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
|
|
814
950
|
|
|
815
951
|
} else if (sectionsParsed?.mode === 'inclusive') {
|
|
816
952
|
// Inclusive: pinned sections + auto-discovered rest via '...' wildcard
|
|
953
|
+
// @-prefixed files are excluded from auto-discovery (they're child sections for nest:)
|
|
817
954
|
const files = await readdir(pagePath)
|
|
818
955
|
const mdFiles = files.filter(isMarkdownFile).sort(compareFilenames)
|
|
819
956
|
|
|
820
|
-
// Build name → file info map from discovered files
|
|
957
|
+
// Build name → file info map from discovered files (excluding @ children)
|
|
821
958
|
const discoveredMap = new Map()
|
|
822
959
|
for (const file of mdFiles) {
|
|
960
|
+
if (isChildSection(file)) continue // Skip @-prefixed child sections
|
|
961
|
+
|
|
823
962
|
const { name } = parse(file)
|
|
824
963
|
const { prefix, name: stableName } = parseNumericPrefix(name)
|
|
825
964
|
const key = stableName || name
|
|
@@ -881,16 +1020,37 @@ async function processPage(pagePath, pageName, siteRoot, { isIndex = false, pare
|
|
|
881
1020
|
sectionIndex++
|
|
882
1021
|
}
|
|
883
1022
|
|
|
884
|
-
|
|
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
|
|
885
1034
|
|
|
886
1035
|
} else if (Array.isArray(sectionsConfig) && sectionsConfig.length > 0) {
|
|
887
1036
|
// Strict: explicit sections array (only listed sections processed)
|
|
1037
|
+
const cachedFiles = await readdir(pagePath)
|
|
888
1038
|
const result = await processExplicitSections(sectionsConfig, pagePath, siteRoot)
|
|
889
1039
|
hierarchicalSections = result.sections
|
|
890
1040
|
pageAssetCollection = result.assetCollection
|
|
891
1041
|
pageIconCollection = result.iconCollection
|
|
892
1042
|
lastModified = result.lastModified
|
|
893
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
|
+
|
|
894
1054
|
} else {
|
|
895
1055
|
// Empty sections (null, empty array, or invalid) = pure route with no content
|
|
896
1056
|
// hierarchicalSections stays empty, lastModified stays null
|
|
@@ -1822,7 +1982,8 @@ export {
|
|
|
1822
1982
|
extractItemName,
|
|
1823
1983
|
parseWildcardArray,
|
|
1824
1984
|
applyWildcardOrder,
|
|
1825
|
-
getDirectChildName
|
|
1985
|
+
getDirectChildName,
|
|
1986
|
+
extractInsets
|
|
1826
1987
|
}
|
|
1827
1988
|
|
|
1828
1989
|
export default collectSiteContent
|