@zenithbuild/core 0.3.3 → 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/bin/zen-build.ts +0 -0
- package/bin/zen-dev.ts +0 -0
- package/bin/zen-preview.ts +0 -0
- package/cli/commands/dev.ts +184 -91
- package/cli/utils/branding.ts +25 -0
- package/cli/utils/content.ts +112 -0
- package/cli/utils/logger.ts +22 -16
- package/compiler/parse/parseTemplate.ts +120 -49
- package/compiler/parse/scriptAnalysis.ts +6 -0
- package/compiler/runtime/dataExposure.ts +12 -4
- package/compiler/runtime/generateHydrationBundle.ts +20 -15
- package/compiler/runtime/transformIR.ts +4 -8
- package/compiler/runtime/wrapExpression.ts +23 -12
- package/compiler/runtime/wrapExpressionWithLoop.ts +8 -2
- package/compiler/ssg-build.ts +7 -3
- package/compiler/transform/expressionTransformer.ts +385 -0
- package/compiler/transform/transformNode.ts +1 -1
- package/core/config/index.ts +16 -0
- package/core/config/loader.ts +69 -0
- package/core/config/types.ts +89 -0
- package/core/plugins/index.ts +7 -0
- package/core/plugins/registry.ts +81 -0
- package/dist/cli.js +1 -0
- package/dist/zen-build.js +568 -292
- package/dist/zen-dev.js +568 -292
- package/dist/zen-preview.js +568 -292
- package/dist/zenith.js +568 -292
- package/package.json +4 -1
- package/runtime/bundle-generator.ts +421 -37
- package/runtime/client-runtime.ts +17 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zenithbuild/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "Core library for the Zenith framework",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"exports": {
|
|
27
27
|
".": "./index.ts",
|
|
28
28
|
"./compiler": "./compiler/index.ts",
|
|
29
|
+
"./config": "./core/config/index.ts",
|
|
29
30
|
"./core": "./core/index.ts",
|
|
30
31
|
"./router": "./router/index.ts",
|
|
31
32
|
"./runtime": "./runtime/index.ts"
|
|
@@ -58,7 +59,9 @@
|
|
|
58
59
|
"typescript": "^5"
|
|
59
60
|
},
|
|
60
61
|
"dependencies": {
|
|
62
|
+
"@types/marked": "^6.0.0",
|
|
61
63
|
"@types/parse5": "^7.0.0",
|
|
64
|
+
"marked": "^17.0.1",
|
|
62
65
|
"parse5": "^8.0.0"
|
|
63
66
|
}
|
|
64
67
|
}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* This is served as an external JS file, not inlined
|
|
18
18
|
*/
|
|
19
19
|
export function generateBundleJS(): string {
|
|
20
|
-
|
|
20
|
+
return `/*!
|
|
21
21
|
* Zenith Runtime v0.1.0
|
|
22
22
|
* Shared client-side runtime for hydration and reactivity
|
|
23
23
|
*/
|
|
@@ -296,6 +296,57 @@ export function generateBundleJS(): string {
|
|
|
296
296
|
return expressionRegistry.get(id);
|
|
297
297
|
}
|
|
298
298
|
|
|
299
|
+
function updateNode(node, exprId, pageState) {
|
|
300
|
+
const expr = getExpression(exprId);
|
|
301
|
+
if (!expr) return;
|
|
302
|
+
|
|
303
|
+
zenEffect(function() {
|
|
304
|
+
const result = expr(pageState);
|
|
305
|
+
|
|
306
|
+
if (node.hasAttribute('data-zen-text')) {
|
|
307
|
+
// Handle complex text/children results
|
|
308
|
+
if (result === null || result === undefined || result === false) {
|
|
309
|
+
node.textContent = '';
|
|
310
|
+
} else if (typeof result === 'string') {
|
|
311
|
+
if (result.trim().startsWith('<') && result.trim().endsWith('>')) {
|
|
312
|
+
node.innerHTML = result;
|
|
313
|
+
} else {
|
|
314
|
+
node.textContent = result;
|
|
315
|
+
}
|
|
316
|
+
} else if (result instanceof Node) {
|
|
317
|
+
node.innerHTML = '';
|
|
318
|
+
node.appendChild(result);
|
|
319
|
+
} else if (Array.isArray(result)) {
|
|
320
|
+
node.innerHTML = '';
|
|
321
|
+
const fragment = document.createDocumentFragment();
|
|
322
|
+
result.flat(Infinity).forEach(item => {
|
|
323
|
+
if (item instanceof Node) fragment.appendChild(item);
|
|
324
|
+
else if (item != null && item !== false) fragment.appendChild(document.createTextNode(String(item)));
|
|
325
|
+
});
|
|
326
|
+
node.appendChild(fragment);
|
|
327
|
+
} else {
|
|
328
|
+
node.textContent = String(result);
|
|
329
|
+
}
|
|
330
|
+
} else {
|
|
331
|
+
// Attribute update
|
|
332
|
+
const attrNames = ['class', 'style', 'src', 'href', 'disabled', 'checked'];
|
|
333
|
+
for (const attr of attrNames) {
|
|
334
|
+
if (node.hasAttribute('data-zen-attr-' + attr)) {
|
|
335
|
+
if (attr === 'class' || attr === 'className') {
|
|
336
|
+
node.className = String(result || '');
|
|
337
|
+
} else if (attr === 'disabled' || attr === 'checked') {
|
|
338
|
+
if (result) node.setAttribute(attr, '');
|
|
339
|
+
else node.removeAttribute(attr);
|
|
340
|
+
} else {
|
|
341
|
+
if (result != null && result !== false) node.setAttribute(attr, String(result));
|
|
342
|
+
else node.removeAttribute(attr);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
|
|
299
350
|
/**
|
|
300
351
|
* Hydrate a page with reactive bindings
|
|
301
352
|
* Called after page HTML is in DOM
|
|
@@ -303,49 +354,323 @@ export function generateBundleJS(): string {
|
|
|
303
354
|
function zenithHydrate(pageState, container) {
|
|
304
355
|
container = container || document;
|
|
305
356
|
|
|
306
|
-
// Find all
|
|
307
|
-
const
|
|
357
|
+
// Find all text expression placeholders
|
|
358
|
+
const textNodes = container.querySelectorAll('[data-zen-text]');
|
|
359
|
+
textNodes.forEach(el => updateNode(el, el.getAttribute('data-zen-text'), pageState));
|
|
308
360
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
if (
|
|
314
|
-
const expr = getExpression(exprId);
|
|
315
|
-
if (expr) {
|
|
316
|
-
zenEffect(function() {
|
|
317
|
-
el.textContent = expr(pageState);
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
} else if (bindType === 'attr') {
|
|
321
|
-
const attrName = el.getAttribute('data-zen-attr');
|
|
322
|
-
const expr = getExpression(exprId);
|
|
323
|
-
if (expr && attrName) {
|
|
324
|
-
zenEffect(function() {
|
|
325
|
-
el.setAttribute(attrName, expr(pageState));
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
}
|
|
361
|
+
// Find all attribute expression placeholders
|
|
362
|
+
const attrNodes = container.querySelectorAll('[data-zen-attr-class], [data-zen-attr-style], [data-zen-attr-src], [data-zen-attr-href]');
|
|
363
|
+
attrNodes.forEach(el => {
|
|
364
|
+
const attrMatch = Array.from(el.attributes).find(a => a.name.startsWith('data-zen-attr-'));
|
|
365
|
+
if (attrMatch) updateNode(el, attrMatch.value, pageState);
|
|
329
366
|
});
|
|
330
367
|
|
|
331
368
|
// Wire up event handlers
|
|
332
|
-
const
|
|
333
|
-
|
|
334
|
-
const
|
|
335
|
-
|
|
336
|
-
const
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
369
|
+
const eventTypes = ['click', 'change', 'input', 'submit', 'focus', 'blur', 'keyup', 'keydown'];
|
|
370
|
+
eventTypes.forEach(eventType => {
|
|
371
|
+
const elements = container.querySelectorAll('[data-zen-' + eventType + ']');
|
|
372
|
+
elements.forEach(el => {
|
|
373
|
+
const handlerName = el.getAttribute('data-zen-' + eventType);
|
|
374
|
+
if (handlerName && (global[handlerName] || getExpression(handlerName))) {
|
|
375
|
+
el.addEventListener(eventType, function(e) {
|
|
376
|
+
const handler = global[handlerName] || getExpression(handlerName);
|
|
377
|
+
if (typeof handler === 'function') handler(e, el);
|
|
378
|
+
});
|
|
341
379
|
}
|
|
342
|
-
}
|
|
380
|
+
});
|
|
343
381
|
});
|
|
344
382
|
|
|
345
383
|
// Trigger mount
|
|
346
384
|
triggerMount();
|
|
347
385
|
}
|
|
348
386
|
|
|
387
|
+
// ============================================
|
|
388
|
+
// zenith:content - Content Engine
|
|
389
|
+
// ============================================
|
|
390
|
+
|
|
391
|
+
const schemaRegistry = new Map();
|
|
392
|
+
const builtInEnhancers = {
|
|
393
|
+
readTime: (item) => {
|
|
394
|
+
const wordsPerMinute = 200;
|
|
395
|
+
const text = item.content || '';
|
|
396
|
+
const wordCount = text.split(/\\s+/).length;
|
|
397
|
+
const minutes = Math.ceil(wordCount / wordsPerMinute);
|
|
398
|
+
return Object.assign({}, item, { readTime: minutes + ' min' });
|
|
399
|
+
},
|
|
400
|
+
wordCount: (item) => {
|
|
401
|
+
const text = item.content || '';
|
|
402
|
+
const wordCount = text.split(/\\s+/).length;
|
|
403
|
+
return Object.assign({}, item, { wordCount: wordCount });
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
async function applyEnhancers(item, enhancers) {
|
|
408
|
+
let enrichedItem = Object.assign({}, item);
|
|
409
|
+
for (const enhancer of enhancers) {
|
|
410
|
+
if (typeof enhancer === 'string') {
|
|
411
|
+
const fn = builtInEnhancers[enhancer];
|
|
412
|
+
if (fn) enrichedItem = await fn(enrichedItem);
|
|
413
|
+
} else if (typeof enhancer === 'function') {
|
|
414
|
+
enrichedItem = await enhancer(enrichedItem);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return enrichedItem;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
class ZenCollection {
|
|
421
|
+
constructor(items) {
|
|
422
|
+
this.items = [...items];
|
|
423
|
+
this.filters = [];
|
|
424
|
+
this.sortField = null;
|
|
425
|
+
this.sortOrder = 'desc';
|
|
426
|
+
this.limitCount = null;
|
|
427
|
+
this.selectedFields = null;
|
|
428
|
+
this.enhancers = [];
|
|
429
|
+
this._groupByFolder = false;
|
|
430
|
+
}
|
|
431
|
+
where(fn) { this.filters.push(fn); return this; }
|
|
432
|
+
sortBy(field, order = 'desc') { this.sortField = field; this.sortOrder = order; return this; }
|
|
433
|
+
limit(n) { this.limitCount = n; return this; }
|
|
434
|
+
fields(f) { this.selectedFields = f; return this; }
|
|
435
|
+
enhanceWith(e) { this.enhancers.push(e); return this; }
|
|
436
|
+
groupByFolder() { this._groupByFolder = true; return this; }
|
|
437
|
+
get() {
|
|
438
|
+
let results = [...this.items];
|
|
439
|
+
for (const filter of this.filters) results = results.filter(filter);
|
|
440
|
+
if (this.sortField) {
|
|
441
|
+
results.sort((a, b) => {
|
|
442
|
+
const valA = a[this.sortField];
|
|
443
|
+
const valB = b[this.sortField];
|
|
444
|
+
if (valA < valB) return this.sortOrder === 'asc' ? -1 : 1;
|
|
445
|
+
if (valA > valB) return this.sortOrder === 'asc' ? 1 : -1;
|
|
446
|
+
return 0;
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
if (this.limitCount !== null) results = results.slice(0, this.limitCount);
|
|
450
|
+
|
|
451
|
+
// Apply enhancers synchronously if possible
|
|
452
|
+
if (this.enhancers.length > 0) {
|
|
453
|
+
results = results.map(item => {
|
|
454
|
+
let enrichedItem = Object.assign({}, item);
|
|
455
|
+
for (const enhancer of this.enhancers) {
|
|
456
|
+
if (typeof enhancer === 'string') {
|
|
457
|
+
const fn = builtInEnhancers[enhancer];
|
|
458
|
+
if (fn) enrichedItem = fn(enrichedItem);
|
|
459
|
+
} else if (typeof enhancer === 'function') {
|
|
460
|
+
enrichedItem = enhancer(enrichedItem);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
return enrichedItem;
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if (this.selectedFields) {
|
|
468
|
+
results = results.map(item => {
|
|
469
|
+
const newItem = {};
|
|
470
|
+
this.selectedFields.forEach(f => { newItem[f] = item[f]; });
|
|
471
|
+
return newItem;
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Group by folder if requested
|
|
476
|
+
if (this._groupByFolder) {
|
|
477
|
+
const groups = {};
|
|
478
|
+
const groupOrder = [];
|
|
479
|
+
for (const item of results) {
|
|
480
|
+
// Extract folder from slug (e.g., "getting-started/installation" -> "getting-started")
|
|
481
|
+
const slug = item.slug || item.id || '';
|
|
482
|
+
const parts = slug.split('/');
|
|
483
|
+
const folder = parts.length > 1 ? parts[0] : 'root';
|
|
484
|
+
|
|
485
|
+
if (!groups[folder]) {
|
|
486
|
+
groups[folder] = {
|
|
487
|
+
id: folder,
|
|
488
|
+
title: folder.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' '),
|
|
489
|
+
items: []
|
|
490
|
+
};
|
|
491
|
+
groupOrder.push(folder);
|
|
492
|
+
}
|
|
493
|
+
groups[folder].items.push(item);
|
|
494
|
+
}
|
|
495
|
+
return groupOrder.map(f => groups[f]);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
return results;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function defineSchema(name, schema) { schemaRegistry.set(name, schema); }
|
|
503
|
+
|
|
504
|
+
function zenCollection(collectionName) {
|
|
505
|
+
const data = (global.__ZENITH_CONTENT__ && global.__ZENITH_CONTENT__[collectionName]) || [];
|
|
506
|
+
return new ZenCollection(data);
|
|
507
|
+
}
|
|
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
|
+
|
|
623
|
+
// Virtual DOM Helper for JSX-style expressions
|
|
624
|
+
function h(tag, props, children) {
|
|
625
|
+
const el = document.createElement(tag);
|
|
626
|
+
if (props) {
|
|
627
|
+
for (const [key, value] of Object.entries(props)) {
|
|
628
|
+
if (key.startsWith('on') && typeof value === 'function') {
|
|
629
|
+
el.addEventListener(key.slice(2).toLowerCase(), value);
|
|
630
|
+
} else if (key === 'class' || key === 'className') {
|
|
631
|
+
el.className = String(value || '');
|
|
632
|
+
} else if (key === 'style' && typeof value === 'object') {
|
|
633
|
+
Object.assign(el.style, value);
|
|
634
|
+
} else if (value != null && value !== false) {
|
|
635
|
+
el.setAttribute(key, String(value));
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
if (children != null && children !== false) {
|
|
640
|
+
// Flatten nested arrays (from .map() calls)
|
|
641
|
+
const childrenArray = Array.isArray(children) ? children.flat(Infinity) : [children];
|
|
642
|
+
for (const child of childrenArray) {
|
|
643
|
+
// Skip null, undefined, and false
|
|
644
|
+
if (child == null || child === false) continue;
|
|
645
|
+
|
|
646
|
+
if (typeof child === 'string') {
|
|
647
|
+
// Check if string looks like HTML
|
|
648
|
+
if (child.trim().startsWith('<') && child.trim().endsWith('>')) {
|
|
649
|
+
// Render as HTML
|
|
650
|
+
const wrapper = document.createElement('div');
|
|
651
|
+
wrapper.innerHTML = child;
|
|
652
|
+
while (wrapper.firstChild) {
|
|
653
|
+
el.appendChild(wrapper.firstChild);
|
|
654
|
+
}
|
|
655
|
+
} else {
|
|
656
|
+
el.appendChild(document.createTextNode(child));
|
|
657
|
+
}
|
|
658
|
+
} else if (typeof child === 'number') {
|
|
659
|
+
el.appendChild(document.createTextNode(String(child)));
|
|
660
|
+
} else if (child instanceof Node) {
|
|
661
|
+
el.appendChild(child);
|
|
662
|
+
} else if (Array.isArray(child)) {
|
|
663
|
+
// Handle nested arrays (shouldn't happen after flat() but just in case)
|
|
664
|
+
for (const c of child) {
|
|
665
|
+
if (c instanceof Node) el.appendChild(c);
|
|
666
|
+
else if (c != null && c !== false) el.appendChild(document.createTextNode(String(c)));
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
return el;
|
|
672
|
+
}
|
|
673
|
+
|
|
349
674
|
// ============================================
|
|
350
675
|
// Export to window.__zenith
|
|
351
676
|
// ============================================
|
|
@@ -359,6 +684,15 @@ export function generateBundleJS(): string {
|
|
|
359
684
|
ref: zenRef,
|
|
360
685
|
batch: zenBatch,
|
|
361
686
|
untrack: zenUntrack,
|
|
687
|
+
// zenith:content
|
|
688
|
+
defineSchema: defineSchema,
|
|
689
|
+
zenCollection: zenCollection,
|
|
690
|
+
// useZenOrder hook
|
|
691
|
+
createZenOrder: createZenOrder,
|
|
692
|
+
processRawSections: processRawSections,
|
|
693
|
+
slugify: slugify,
|
|
694
|
+
// Virtual DOM helper for JSX
|
|
695
|
+
h: h,
|
|
362
696
|
// Lifecycle
|
|
363
697
|
onMount: zenOnMount,
|
|
364
698
|
onUnmount: zenOnUnmount,
|
|
@@ -394,6 +728,56 @@ export function generateBundleJS(): string {
|
|
|
394
728
|
global.onMount = zenOnMount;
|
|
395
729
|
global.onUnmount = zenOnUnmount;
|
|
396
730
|
|
|
731
|
+
// useZenOrder hook exports
|
|
732
|
+
global.createZenOrder = createZenOrder;
|
|
733
|
+
global.processRawSections = processRawSections;
|
|
734
|
+
global.slugify = slugify;
|
|
735
|
+
|
|
736
|
+
// ============================================
|
|
737
|
+
// HMR Client (Development Only)
|
|
738
|
+
// ============================================
|
|
739
|
+
|
|
740
|
+
if (typeof window !== 'undefined' && (location.hostname === 'localhost' || location.hostname === '127.0.0.1')) {
|
|
741
|
+
let socket;
|
|
742
|
+
function connectHMR() {
|
|
743
|
+
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
744
|
+
socket = new WebSocket(protocol + '//' + location.host + '/hmr');
|
|
745
|
+
|
|
746
|
+
socket.onmessage = function(event) {
|
|
747
|
+
try {
|
|
748
|
+
const data = JSON.parse(event.data);
|
|
749
|
+
if (data.type === 'reload') {
|
|
750
|
+
console.log('[Zenith] HMR: Reloading page...');
|
|
751
|
+
location.reload();
|
|
752
|
+
} else if (data.type === 'style-update') {
|
|
753
|
+
console.log('[Zenith] HMR: Updating style ' + data.url);
|
|
754
|
+
const links = document.querySelectorAll('link[rel="stylesheet"]');
|
|
755
|
+
for (let i = 0; i < links.length; i++) {
|
|
756
|
+
const link = links[i];
|
|
757
|
+
const url = new URL(link.href);
|
|
758
|
+
if (url.pathname === data.url) {
|
|
759
|
+
link.href = data.url + '?t=' + Date.now();
|
|
760
|
+
break;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
} catch (e) {
|
|
765
|
+
console.error('[Zenith] HMR Error:', e);
|
|
766
|
+
}
|
|
767
|
+
};
|
|
768
|
+
|
|
769
|
+
socket.onclose = function() {
|
|
770
|
+
console.log('[Zenith] HMR: Connection closed. Retrying in 2s...');
|
|
771
|
+
setTimeout(connectHMR, 2000);
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// Connect unless explicitly disabled
|
|
776
|
+
if (!window.__ZENITH_NO_HMR__) {
|
|
777
|
+
connectHMR();
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
|
|
397
781
|
})(typeof window !== 'undefined' ? window : this);
|
|
398
782
|
`
|
|
399
783
|
}
|
|
@@ -403,14 +787,14 @@ export function generateBundleJS(): string {
|
|
|
403
787
|
* For production builds
|
|
404
788
|
*/
|
|
405
789
|
export function generateMinifiedBundleJS(): string {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
790
|
+
// For now, return non-minified
|
|
791
|
+
// TODO: Add minification via terser or similar
|
|
792
|
+
return generateBundleJS()
|
|
409
793
|
}
|
|
410
794
|
|
|
411
795
|
/**
|
|
412
796
|
* Get bundle version for cache busting
|
|
413
797
|
*/
|
|
414
798
|
export function getBundleVersion(): string {
|
|
415
|
-
|
|
799
|
+
return '0.1.0'
|
|
416
800
|
}
|
|
@@ -358,6 +358,23 @@ function updateTextBinding(node: Element, expressionId: string, state: any): voi
|
|
|
358
358
|
const result = expression(state);
|
|
359
359
|
if (result === null || result === undefined || result === false) {
|
|
360
360
|
node.textContent = '';
|
|
361
|
+
} else if (typeof result === 'string') {
|
|
362
|
+
if (result.trim().startsWith('<') && result.trim().endsWith('>')) {
|
|
363
|
+
node.innerHTML = result;
|
|
364
|
+
} else {
|
|
365
|
+
node.textContent = result;
|
|
366
|
+
}
|
|
367
|
+
} else if (result instanceof Node) {
|
|
368
|
+
node.innerHTML = '';
|
|
369
|
+
node.appendChild(result);
|
|
370
|
+
} else if (Array.isArray(result)) {
|
|
371
|
+
node.innerHTML = '';
|
|
372
|
+
const fragment = document.createDocumentFragment();
|
|
373
|
+
result.flat(Infinity).forEach(item => {
|
|
374
|
+
if (item instanceof Node) fragment.appendChild(item);
|
|
375
|
+
else if (item != null && item !== false) fragment.appendChild(document.createTextNode(String(item)));
|
|
376
|
+
});
|
|
377
|
+
node.appendChild(fragment);
|
|
361
378
|
} else {
|
|
362
379
|
node.textContent = String(result);
|
|
363
380
|
}
|