@uniweb/build 0.1.19 → 0.1.21
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 +2 -2
- package/src/site/content-collector.js +120 -33
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/build",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.21",
|
|
4
4
|
"description": "Build tooling for the Uniweb Component Web Platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"@tailwindcss/vite": "^4.0.0",
|
|
60
60
|
"@vitejs/plugin-react": "^4.0.0 || ^5.0.0",
|
|
61
61
|
"vite-plugin-svgr": "^4.0.0",
|
|
62
|
-
"@uniweb/core": "0.1.
|
|
62
|
+
"@uniweb/core": "0.1.9"
|
|
63
63
|
},
|
|
64
64
|
"peerDependenciesMeta": {
|
|
65
65
|
"vite": {
|
|
@@ -79,9 +79,14 @@ function isMarkdownFile(filename) {
|
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
81
|
* Parse numeric prefix from filename (e.g., "1-hero.md" → { prefix: "1", name: "hero" })
|
|
82
|
+
* Supports:
|
|
83
|
+
* - Simple: "1", "2", "3"
|
|
84
|
+
* - Decimal ordering: "1.5" (between 1 and 2), "2.5" (between 2 and 3)
|
|
85
|
+
* - Hierarchy via comma: "1,1" (child of 1), "1,2" (second child of 1)
|
|
86
|
+
* - Mixed: "1.5,1" (child of section 1.5)
|
|
82
87
|
*/
|
|
83
88
|
function parseNumericPrefix(filename) {
|
|
84
|
-
const match = filename.match(/^(\d+(
|
|
89
|
+
const match = filename.match(/^(\d+(?:[.,]\d+)*)-?(.*)$/)
|
|
85
90
|
if (match) {
|
|
86
91
|
return { prefix: match[1], name: match[2] || match[1] }
|
|
87
92
|
}
|
|
@@ -89,7 +94,9 @@ function parseNumericPrefix(filename) {
|
|
|
89
94
|
}
|
|
90
95
|
|
|
91
96
|
/**
|
|
92
|
-
* Compare filenames for sorting by numeric prefix
|
|
97
|
+
* Compare filenames for sorting by numeric prefix.
|
|
98
|
+
* Both . and , are treated as separators for sorting purposes.
|
|
99
|
+
* This ensures correct ordering: 1, 1,1, 1.5, 2, 2,1, etc.
|
|
93
100
|
*/
|
|
94
101
|
function compareFilenames(a, b) {
|
|
95
102
|
const { prefix: prefixA } = parseNumericPrefix(parse(a).name)
|
|
@@ -99,8 +106,8 @@ function compareFilenames(a, b) {
|
|
|
99
106
|
if (!prefixA) return 1
|
|
100
107
|
if (!prefixB) return -1
|
|
101
108
|
|
|
102
|
-
const partsA = prefixA.split(
|
|
103
|
-
const partsB = prefixB.split(
|
|
109
|
+
const partsA = prefixA.split(/[.,]/).map(Number)
|
|
110
|
+
const partsB = prefixB.split(/[.,]/).map(Number)
|
|
104
111
|
|
|
105
112
|
for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
|
|
106
113
|
const numA = partsA[i] ?? 0
|
|
@@ -155,7 +162,11 @@ async function processMarkdownFile(filePath, id, siteRoot) {
|
|
|
155
162
|
}
|
|
156
163
|
|
|
157
164
|
/**
|
|
158
|
-
* Build section hierarchy from flat list
|
|
165
|
+
* Build section hierarchy from flat list.
|
|
166
|
+
* Hierarchy is determined by comma separators:
|
|
167
|
+
* - "1", "1.5", "2" → all top-level (dots are for ordering)
|
|
168
|
+
* - "1,1", "1,2" → children of section "1"
|
|
169
|
+
* - "1.5,1" → child of section "1.5"
|
|
159
170
|
*/
|
|
160
171
|
function buildSectionHierarchy(sections) {
|
|
161
172
|
const sectionMap = new Map()
|
|
@@ -166,15 +177,15 @@ function buildSectionHierarchy(sections) {
|
|
|
166
177
|
sectionMap.set(section.id, section)
|
|
167
178
|
}
|
|
168
179
|
|
|
169
|
-
// Second pass: build hierarchy
|
|
180
|
+
// Second pass: build hierarchy (comma = hierarchy)
|
|
170
181
|
for (const section of sections) {
|
|
171
|
-
if (!section.id.includes('
|
|
182
|
+
if (!section.id.includes(',')) {
|
|
172
183
|
topLevel.push(section)
|
|
173
184
|
continue
|
|
174
185
|
}
|
|
175
186
|
|
|
176
|
-
const parts = section.id.split('
|
|
177
|
-
const parentId = parts.slice(0, -1).join('
|
|
187
|
+
const parts = section.id.split(',')
|
|
188
|
+
const parentId = parts.slice(0, -1).join(',')
|
|
178
189
|
const parent = sectionMap.get(parentId)
|
|
179
190
|
|
|
180
191
|
if (parent) {
|
|
@@ -277,11 +288,14 @@ async function processExplicitSections(sectionsConfig, pagePath, siteRoot, paren
|
|
|
277
288
|
* Process a page directory
|
|
278
289
|
*
|
|
279
290
|
* @param {string} pagePath - Path to page directory
|
|
280
|
-
* @param {string} pageName - Name of the page
|
|
291
|
+
* @param {string} pageName - Name of the page (folder name, not full path)
|
|
281
292
|
* @param {string} siteRoot - Site root directory for asset resolution
|
|
293
|
+
* @param {Object} options - Route options
|
|
294
|
+
* @param {boolean} options.isIndex - Whether this page is the index for its parent route
|
|
295
|
+
* @param {string} options.parentRoute - The parent route (e.g., '/' or '/docs')
|
|
282
296
|
* @returns {Object} Page data with assets manifest
|
|
283
297
|
*/
|
|
284
|
-
async function processPage(pagePath, pageName, siteRoot) {
|
|
298
|
+
async function processPage(pagePath, pageName, siteRoot, { isIndex = false, parentRoute = '/' } = {}) {
|
|
285
299
|
const pageConfig = await readYamlFile(join(pagePath, 'page.yml'))
|
|
286
300
|
|
|
287
301
|
// Note: We no longer skip hidden pages here - they still exist as valid pages,
|
|
@@ -337,11 +351,16 @@ async function processPage(pagePath, pageName, siteRoot) {
|
|
|
337
351
|
}
|
|
338
352
|
|
|
339
353
|
// Determine route
|
|
340
|
-
let route
|
|
341
|
-
if (
|
|
342
|
-
|
|
354
|
+
let route
|
|
355
|
+
if (isIndex) {
|
|
356
|
+
// Index page gets the parent route
|
|
357
|
+
route = parentRoute
|
|
343
358
|
} else if (pageName.startsWith('@')) {
|
|
344
|
-
|
|
359
|
+
// Special pages (layout areas) keep their @ prefix
|
|
360
|
+
route = parentRoute === '/' ? `/@${pageName.slice(1)}` : `${parentRoute}/@${pageName.slice(1)}`
|
|
361
|
+
} else {
|
|
362
|
+
// Normal pages get parent + their name
|
|
363
|
+
route = parentRoute === '/' ? `/${pageName}` : `${parentRoute}/${pageName}`
|
|
345
364
|
}
|
|
346
365
|
|
|
347
366
|
// Extract configuration
|
|
@@ -381,15 +400,50 @@ async function processPage(pagePath, pageName, siteRoot) {
|
|
|
381
400
|
}
|
|
382
401
|
}
|
|
383
402
|
|
|
403
|
+
/**
|
|
404
|
+
* Determine the index page name from ordering config
|
|
405
|
+
*
|
|
406
|
+
* @param {Object} orderConfig - { pages: [...], index: 'name' } from parent
|
|
407
|
+
* @param {Array} availableFolders - Array of { name, order } for folders at this level
|
|
408
|
+
* @returns {string|null} The folder name that should be the index, or null
|
|
409
|
+
*/
|
|
410
|
+
function determineIndexPage(orderConfig, availableFolders) {
|
|
411
|
+
const { pages: pagesArray, index: indexName } = orderConfig || {}
|
|
412
|
+
|
|
413
|
+
// 1. Explicit pages array - first item is index
|
|
414
|
+
if (Array.isArray(pagesArray) && pagesArray.length > 0) {
|
|
415
|
+
return pagesArray[0]
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// 2. Explicit index property
|
|
419
|
+
if (indexName) {
|
|
420
|
+
return indexName
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// 3. Fallback: lowest order value, or first alphabetically
|
|
424
|
+
if (availableFolders.length === 0) return null
|
|
425
|
+
|
|
426
|
+
const sorted = [...availableFolders].sort((a, b) => {
|
|
427
|
+
// Sort by order (lower first), then alphabetically
|
|
428
|
+
const orderA = a.order ?? 999
|
|
429
|
+
const orderB = b.order ?? 999
|
|
430
|
+
if (orderA !== orderB) return orderA - orderB
|
|
431
|
+
return a.name.localeCompare(b.name)
|
|
432
|
+
})
|
|
433
|
+
|
|
434
|
+
return sorted[0].name
|
|
435
|
+
}
|
|
436
|
+
|
|
384
437
|
/**
|
|
385
438
|
* Recursively collect pages from a directory
|
|
386
439
|
*
|
|
387
440
|
* @param {string} dirPath - Directory to scan
|
|
388
|
-
* @param {string}
|
|
441
|
+
* @param {string} parentRoute - Parent route (e.g., '/' or '/docs')
|
|
389
442
|
* @param {string} siteRoot - Site root directory for asset resolution
|
|
443
|
+
* @param {Object} orderConfig - { pages: [...], index: 'name' } from parent's config
|
|
390
444
|
* @returns {Promise<Object>} { pages, assetCollection, header, footer, left, right }
|
|
391
445
|
*/
|
|
392
|
-
async function collectPagesRecursive(dirPath,
|
|
446
|
+
async function collectPagesRecursive(dirPath, parentRoute, siteRoot, orderConfig = {}) {
|
|
393
447
|
const entries = await readdir(dirPath)
|
|
394
448
|
const pages = []
|
|
395
449
|
let assetCollection = {
|
|
@@ -402,30 +456,55 @@ async function collectPagesRecursive(dirPath, routePrefix, siteRoot) {
|
|
|
402
456
|
let left = null
|
|
403
457
|
let right = null
|
|
404
458
|
|
|
459
|
+
// First pass: discover all page folders and read their order values
|
|
460
|
+
const pageFolders = []
|
|
405
461
|
for (const entry of entries) {
|
|
406
462
|
const entryPath = join(dirPath, entry)
|
|
407
463
|
const stats = await stat(entryPath)
|
|
408
|
-
|
|
409
464
|
if (!stats.isDirectory()) continue
|
|
410
465
|
|
|
411
|
-
//
|
|
412
|
-
const
|
|
466
|
+
// Read page.yml to get order and child page config
|
|
467
|
+
const pageConfig = await readYamlFile(join(entryPath, 'page.yml'))
|
|
468
|
+
pageFolders.push({
|
|
469
|
+
name: entry,
|
|
470
|
+
path: entryPath,
|
|
471
|
+
order: pageConfig.order,
|
|
472
|
+
childOrderConfig: {
|
|
473
|
+
pages: pageConfig.pages,
|
|
474
|
+
index: pageConfig.index
|
|
475
|
+
}
|
|
476
|
+
})
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Determine which page is the index for this level
|
|
480
|
+
const regularFolders = pageFolders.filter(f => !f.name.startsWith('@'))
|
|
481
|
+
const indexPageName = determineIndexPage(orderConfig, regularFolders)
|
|
482
|
+
|
|
483
|
+
// Second pass: process each page folder
|
|
484
|
+
for (const folder of pageFolders) {
|
|
485
|
+
const { name: entry, path: entryPath, childOrderConfig } = folder
|
|
486
|
+
const isIndex = entry === indexPageName
|
|
487
|
+
const isSpecial = entry.startsWith('@')
|
|
413
488
|
|
|
414
489
|
// Process this directory as a page
|
|
415
|
-
const result = await processPage(entryPath,
|
|
490
|
+
const result = await processPage(entryPath, entry, siteRoot, {
|
|
491
|
+
isIndex: isIndex && !isSpecial,
|
|
492
|
+
parentRoute
|
|
493
|
+
})
|
|
494
|
+
|
|
416
495
|
if (result) {
|
|
417
496
|
const { page, assetCollection: pageAssets } = result
|
|
418
497
|
assetCollection = mergeAssetCollections(assetCollection, pageAssets)
|
|
419
498
|
|
|
420
499
|
// Handle special pages (layout areas) - only at root level
|
|
421
|
-
if (
|
|
422
|
-
if (entry === '@header'
|
|
500
|
+
if (parentRoute === '/') {
|
|
501
|
+
if (entry === '@header') {
|
|
423
502
|
header = page
|
|
424
|
-
} else if (entry === '@footer'
|
|
503
|
+
} else if (entry === '@footer') {
|
|
425
504
|
footer = page
|
|
426
|
-
} else if (entry === '@left'
|
|
505
|
+
} else if (entry === '@left') {
|
|
427
506
|
left = page
|
|
428
|
-
} else if (entry === '@right'
|
|
507
|
+
} else if (entry === '@right') {
|
|
429
508
|
right = page
|
|
430
509
|
} else {
|
|
431
510
|
pages.push(page)
|
|
@@ -433,13 +512,15 @@ async function collectPagesRecursive(dirPath, routePrefix, siteRoot) {
|
|
|
433
512
|
} else {
|
|
434
513
|
pages.push(page)
|
|
435
514
|
}
|
|
436
|
-
}
|
|
437
515
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
516
|
+
// Recursively process subdirectories (but not special @ directories)
|
|
517
|
+
if (!isSpecial) {
|
|
518
|
+
// The child route depends on whether this page is the index
|
|
519
|
+
const childParentRoute = isIndex ? parentRoute : page.route
|
|
520
|
+
const subResult = await collectPagesRecursive(entryPath, childParentRoute, siteRoot, childOrderConfig)
|
|
521
|
+
pages.push(...subResult.pages)
|
|
522
|
+
assetCollection = mergeAssetCollections(assetCollection, subResult.assetCollection)
|
|
523
|
+
}
|
|
443
524
|
}
|
|
444
525
|
}
|
|
445
526
|
|
|
@@ -469,9 +550,15 @@ export async function collectSiteContent(sitePath) {
|
|
|
469
550
|
}
|
|
470
551
|
}
|
|
471
552
|
|
|
553
|
+
// Extract page ordering config from site.yml
|
|
554
|
+
const siteOrderConfig = {
|
|
555
|
+
pages: siteConfig.pages,
|
|
556
|
+
index: siteConfig.index
|
|
557
|
+
}
|
|
558
|
+
|
|
472
559
|
// Recursively collect all pages
|
|
473
560
|
const { pages, assetCollection, header, footer, left, right } =
|
|
474
|
-
await collectPagesRecursive(pagesPath, '', sitePath)
|
|
561
|
+
await collectPagesRecursive(pagesPath, '/', sitePath, siteOrderConfig)
|
|
475
562
|
|
|
476
563
|
// Sort pages by order
|
|
477
564
|
pages.sort((a, b) => (a.order ?? 999) - (b.order ?? 999))
|