@zenithbuild/core 0.4.1 → 0.4.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/cli/commands/dev.ts +29 -1
- package/package.json +1 -1
- package/runtime/bundle-generator.ts +123 -0
package/cli/commands/dev.ts
CHANGED
|
@@ -244,16 +244,44 @@ export async function dev(options: DevOptions = {}): Promise<void> {
|
|
|
244
244
|
}
|
|
245
245
|
|
|
246
246
|
function findPageForRoute(route: string, pagesDir: string): string | null {
|
|
247
|
+
// 1. Try exact match first (e.g., /about -> about.zen)
|
|
247
248
|
const exactPath = path.join(pagesDir, route === '/' ? 'index.zen' : `${route.slice(1)}.zen`)
|
|
248
249
|
if (fs.existsSync(exactPath)) return exactPath
|
|
250
|
+
|
|
251
|
+
// 2. Try index.zen in directory (e.g., /about -> about/index.zen)
|
|
249
252
|
const indexPath = path.join(pagesDir, route === '/' ? 'index.zen' : `${route.slice(1)}/index.zen`)
|
|
250
253
|
if (fs.existsSync(indexPath)) return indexPath
|
|
254
|
+
|
|
255
|
+
// 3. Try dynamic routes [slug].zen, [...slug].zen
|
|
256
|
+
// Walk up the path looking for dynamic segments
|
|
257
|
+
const segments = route === '/' ? [] : route.slice(1).split('/').filter(Boolean)
|
|
258
|
+
|
|
259
|
+
// Try matching with dynamic [slug].zen at each level
|
|
260
|
+
for (let i = segments.length - 1; i >= 0; i--) {
|
|
261
|
+
const staticPart = segments.slice(0, i).join('/')
|
|
262
|
+
const baseDir = staticPart ? path.join(pagesDir, staticPart) : pagesDir
|
|
263
|
+
|
|
264
|
+
// Check for [slug].zen (single segment catch)
|
|
265
|
+
const singleDynamicPath = path.join(baseDir, '[slug].zen')
|
|
266
|
+
if (fs.existsSync(singleDynamicPath)) return singleDynamicPath
|
|
267
|
+
|
|
268
|
+
// Check for [...slug].zen (catch-all)
|
|
269
|
+
const catchAllPath = path.join(baseDir, '[...slug].zen')
|
|
270
|
+
if (fs.existsSync(catchAllPath)) return catchAllPath
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// 4. Check for catch-all at root
|
|
274
|
+
const rootCatchAll = path.join(pagesDir, '[...slug].zen')
|
|
275
|
+
if (fs.existsSync(rootCatchAll)) return rootCatchAll
|
|
276
|
+
|
|
251
277
|
return null
|
|
252
278
|
}
|
|
253
279
|
|
|
254
280
|
function generateDevHTML(page: CompiledPage, contentData: any = {}): string {
|
|
255
281
|
const runtimeTag = `<script src="/runtime.js"></script>`
|
|
256
|
-
|
|
282
|
+
// Escape </script> sequences in JSON content to prevent breaking the script tag
|
|
283
|
+
const contentJson = JSON.stringify(contentData).replace(/<\//g, '<\\/')
|
|
284
|
+
const contentTag = `<script>window.__ZENITH_CONTENT__ = ${contentJson};</script>`
|
|
257
285
|
const scriptTag = `<script>\n${page.script}\n</script>`
|
|
258
286
|
const allScripts = `${runtimeTag}\n${contentTag}\n${scriptTag}`
|
|
259
287
|
return page.html.includes('</body>')
|
package/package.json
CHANGED
|
@@ -506,6 +506,120 @@ export function generateBundleJS(): string {
|
|
|
506
506
|
return new ZenCollection(data);
|
|
507
507
|
}
|
|
508
508
|
|
|
509
|
+
// ============================================
|
|
510
|
+
// useZenOrder - Documentation ordering & navigation
|
|
511
|
+
// ============================================
|
|
512
|
+
|
|
513
|
+
function slugify(text) {
|
|
514
|
+
return String(text || '')
|
|
515
|
+
.toLowerCase()
|
|
516
|
+
.replace(/[^\\w\\s-]/g, '')
|
|
517
|
+
.replace(/\\s+/g, '-')
|
|
518
|
+
.replace(/-+/g, '-')
|
|
519
|
+
.trim();
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
function getDocSlug(doc) {
|
|
523
|
+
const slugOrId = String(doc.slug || doc.id || '');
|
|
524
|
+
const parts = slugOrId.split('/');
|
|
525
|
+
const filename = parts[parts.length - 1];
|
|
526
|
+
return filename ? slugify(filename) : slugify(doc.title || 'untitled');
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function processRawSections(rawSections) {
|
|
530
|
+
const sections = (rawSections || []).map(function(rawSection) {
|
|
531
|
+
const sectionSlug = slugify(rawSection.title || rawSection.id || 'section');
|
|
532
|
+
const items = (rawSection.items || []).map(function(item) {
|
|
533
|
+
return Object.assign({}, item, {
|
|
534
|
+
slug: getDocSlug(item),
|
|
535
|
+
sectionSlug: sectionSlug,
|
|
536
|
+
isIntro: item.intro === true || (item.tags && item.tags.includes && item.tags.includes('intro'))
|
|
537
|
+
});
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
// Sort items: intro first, then order, then alphabetical
|
|
541
|
+
items.sort(function(a, b) {
|
|
542
|
+
if (a.isIntro && !b.isIntro) return -1;
|
|
543
|
+
if (!a.isIntro && b.isIntro) return 1;
|
|
544
|
+
if (a.order !== undefined && b.order !== undefined) return a.order - b.order;
|
|
545
|
+
if (a.order !== undefined) return -1;
|
|
546
|
+
if (b.order !== undefined) return 1;
|
|
547
|
+
return (a.title || '').localeCompare(b.title || '');
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
return {
|
|
551
|
+
id: rawSection.id || sectionSlug,
|
|
552
|
+
title: rawSection.title || 'Untitled',
|
|
553
|
+
slug: sectionSlug,
|
|
554
|
+
order: rawSection.order !== undefined ? rawSection.order : (rawSection.meta && rawSection.meta.order),
|
|
555
|
+
hasIntro: items.some(function(i) { return i.isIntro; }),
|
|
556
|
+
items: items
|
|
557
|
+
};
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
// Sort sections: order → hasIntro → alphabetical
|
|
561
|
+
sections.sort(function(a, b) {
|
|
562
|
+
if (a.order !== undefined && b.order !== undefined) return a.order - b.order;
|
|
563
|
+
if (a.order !== undefined) return -1;
|
|
564
|
+
if (b.order !== undefined) return 1;
|
|
565
|
+
if (a.hasIntro && !b.hasIntro) return -1;
|
|
566
|
+
if (!a.hasIntro && b.hasIntro) return 1;
|
|
567
|
+
return a.title.localeCompare(b.title);
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
return sections;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
function createZenOrder(rawSections) {
|
|
574
|
+
const sections = processRawSections(rawSections);
|
|
575
|
+
|
|
576
|
+
return {
|
|
577
|
+
sections: sections,
|
|
578
|
+
selectedSection: sections[0] || null,
|
|
579
|
+
selectedDoc: sections[0] && sections[0].items[0] || null,
|
|
580
|
+
|
|
581
|
+
getSectionBySlug: function(sectionSlug) {
|
|
582
|
+
return sections.find(function(s) { return s.slug === sectionSlug; }) || null;
|
|
583
|
+
},
|
|
584
|
+
|
|
585
|
+
getDocBySlug: function(sectionSlug, docSlug) {
|
|
586
|
+
var section = sections.find(function(s) { return s.slug === sectionSlug; });
|
|
587
|
+
if (!section) return null;
|
|
588
|
+
return section.items.find(function(d) { return d.slug === docSlug; }) || null;
|
|
589
|
+
},
|
|
590
|
+
|
|
591
|
+
getNextDoc: function(currentDoc) {
|
|
592
|
+
if (!currentDoc) return null;
|
|
593
|
+
var currentSection = sections.find(function(s) { return s.slug === currentDoc.sectionSlug; });
|
|
594
|
+
if (!currentSection) return null;
|
|
595
|
+
var idx = currentSection.items.findIndex(function(d) { return d.slug === currentDoc.slug; });
|
|
596
|
+
if (idx < currentSection.items.length - 1) return currentSection.items[idx + 1];
|
|
597
|
+
var secIdx = sections.findIndex(function(s) { return s.slug === currentSection.slug; });
|
|
598
|
+
if (secIdx < sections.length - 1) return sections[secIdx + 1].items[0] || null;
|
|
599
|
+
return null;
|
|
600
|
+
},
|
|
601
|
+
|
|
602
|
+
getPrevDoc: function(currentDoc) {
|
|
603
|
+
if (!currentDoc) return null;
|
|
604
|
+
var currentSection = sections.find(function(s) { return s.slug === currentDoc.sectionSlug; });
|
|
605
|
+
if (!currentSection) return null;
|
|
606
|
+
var idx = currentSection.items.findIndex(function(d) { return d.slug === currentDoc.slug; });
|
|
607
|
+
if (idx > 0) return currentSection.items[idx - 1];
|
|
608
|
+
var secIdx = sections.findIndex(function(s) { return s.slug === currentSection.slug; });
|
|
609
|
+
if (secIdx > 0) {
|
|
610
|
+
var prev = sections[secIdx - 1];
|
|
611
|
+
return prev.items[prev.items.length - 1] || null;
|
|
612
|
+
}
|
|
613
|
+
return null;
|
|
614
|
+
},
|
|
615
|
+
|
|
616
|
+
buildDocUrl: function(sectionSlug, docSlug) {
|
|
617
|
+
if (!docSlug || docSlug === 'index') return '/documentation/' + sectionSlug;
|
|
618
|
+
return '/documentation/' + sectionSlug + '/' + docSlug;
|
|
619
|
+
}
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
|
|
509
623
|
// Virtual DOM Helper for JSX-style expressions
|
|
510
624
|
function h(tag, props, children) {
|
|
511
625
|
const el = document.createElement(tag);
|
|
@@ -573,6 +687,10 @@ export function generateBundleJS(): string {
|
|
|
573
687
|
// zenith:content
|
|
574
688
|
defineSchema: defineSchema,
|
|
575
689
|
zenCollection: zenCollection,
|
|
690
|
+
// useZenOrder hook
|
|
691
|
+
createZenOrder: createZenOrder,
|
|
692
|
+
processRawSections: processRawSections,
|
|
693
|
+
slugify: slugify,
|
|
576
694
|
// Virtual DOM helper for JSX
|
|
577
695
|
h: h,
|
|
578
696
|
// Lifecycle
|
|
@@ -610,6 +728,11 @@ export function generateBundleJS(): string {
|
|
|
610
728
|
global.onMount = zenOnMount;
|
|
611
729
|
global.onUnmount = zenOnUnmount;
|
|
612
730
|
|
|
731
|
+
// useZenOrder hook exports
|
|
732
|
+
global.createZenOrder = createZenOrder;
|
|
733
|
+
global.processRawSections = processRawSections;
|
|
734
|
+
global.slugify = slugify;
|
|
735
|
+
|
|
613
736
|
// ============================================
|
|
614
737
|
// HMR Client (Development Only)
|
|
615
738
|
// ============================================
|