zero-query 0.7.5 → 0.8.6
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/README.md +37 -27
- package/cli/commands/build.js +110 -1
- package/cli/commands/bundle.js +107 -22
- package/cli/commands/create.js +1 -1
- package/cli/commands/dev/devtools/index.js +56 -0
- package/cli/commands/dev/devtools/js/components.js +49 -0
- package/cli/commands/dev/devtools/js/core.js +409 -0
- package/cli/commands/dev/devtools/js/elements.js +413 -0
- package/cli/commands/dev/devtools/js/network.js +166 -0
- package/cli/commands/dev/devtools/js/performance.js +73 -0
- package/cli/commands/dev/devtools/js/router.js +105 -0
- package/cli/commands/dev/devtools/js/source.js +132 -0
- package/cli/commands/dev/devtools/js/stats.js +35 -0
- package/cli/commands/dev/devtools/js/tabs.js +79 -0
- package/cli/commands/dev/devtools/panel.html +95 -0
- package/cli/commands/dev/devtools/styles.css +244 -0
- package/cli/commands/dev/index.js +28 -3
- package/cli/commands/dev/logger.js +6 -1
- package/cli/commands/dev/overlay.js +377 -0
- package/cli/commands/dev/server.js +8 -0
- package/cli/commands/dev/watcher.js +26 -1
- package/cli/help.js +8 -5
- package/cli/scaffold/{scripts → app}/app.js +1 -1
- package/cli/scaffold/{scripts → app}/components/about.js +4 -4
- package/cli/scaffold/{scripts → app}/components/api-demo.js +1 -1
- package/cli/scaffold/app/components/home.js +137 -0
- package/cli/scaffold/{scripts → app}/routes.js +1 -1
- package/cli/scaffold/{scripts → app}/store.js +6 -6
- package/cli/scaffold/assets/.gitkeep +0 -0
- package/cli/scaffold/{styles/styles.css → global.css} +3 -2
- package/cli/scaffold/index.html +11 -11
- package/dist/zquery.dist.zip +0 -0
- package/dist/zquery.js +746 -134
- package/dist/zquery.min.js +2 -2
- package/index.d.ts +11 -9
- package/index.js +15 -10
- package/package.json +3 -2
- package/src/component.js +161 -48
- package/src/core.js +57 -11
- package/src/diff.js +256 -58
- package/src/expression.js +33 -3
- package/src/reactive.js +37 -5
- package/src/router.js +195 -6
- package/tests/component.test.js +582 -0
- package/tests/core.test.js +251 -0
- package/tests/diff.test.js +333 -2
- package/tests/expression.test.js +148 -0
- package/tests/http.test.js +108 -0
- package/tests/reactive.test.js +148 -0
- package/tests/router.test.js +317 -0
- package/tests/store.test.js +126 -0
- package/tests/utils.test.js +161 -2
- package/types/collection.d.ts +17 -2
- package/types/component.d.ts +7 -0
- package/types/misc.d.ts +13 -0
- package/types/router.d.ts +30 -1
- package/cli/commands/dev.old.js +0 -520
- package/cli/scaffold/scripts/components/home.js +0 -137
- /package/cli/scaffold/{scripts → app}/components/contacts/contacts.css +0 -0
- /package/cli/scaffold/{scripts → app}/components/contacts/contacts.html +0 -0
- /package/cli/scaffold/{scripts → app}/components/contacts/contacts.js +0 -0
- /package/cli/scaffold/{scripts → app}/components/counter.js +0 -0
- /package/cli/scaffold/{scripts → app}/components/not-found.js +0 -0
- /package/cli/scaffold/{scripts → app}/components/todos.js +0 -0
|
@@ -360,6 +360,383 @@ const OVERLAY_SCRIPT = `<script>
|
|
|
360
360
|
}
|
|
361
361
|
|
|
362
362
|
connect();
|
|
363
|
+
|
|
364
|
+
// =====================================================================
|
|
365
|
+
// Fetch / $.http Interceptor — pretty console logging
|
|
366
|
+
// =====================================================================
|
|
367
|
+
var __zqChannel;
|
|
368
|
+
try { __zqChannel = new BroadcastChannel('__zq_devtools'); } catch(e) {}
|
|
369
|
+
|
|
370
|
+
var __zqRequests = [];
|
|
371
|
+
var __zqMorphEvents = [];
|
|
372
|
+
var __zqMorphCount = 0;
|
|
373
|
+
var __zqRenderCount = 0;
|
|
374
|
+
var __zqReqId = 0;
|
|
375
|
+
var _origFetch = window.fetch;
|
|
376
|
+
|
|
377
|
+
window.fetch = function(input, init) {
|
|
378
|
+
var url = typeof input === 'string' ? input : (input && input.url ? input.url : String(input));
|
|
379
|
+
var method = ((init && init.method) || (input && input.method) || 'GET').toUpperCase();
|
|
380
|
+
var id = ++__zqReqId;
|
|
381
|
+
var start = performance.now();
|
|
382
|
+
|
|
383
|
+
// Skip internal dev-server requests
|
|
384
|
+
if (url.indexOf('__zq_') !== -1 || url.indexOf('/_devtools') !== -1) {
|
|
385
|
+
return _origFetch.apply(this, arguments);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return _origFetch.apply(this, arguments).then(function(response) {
|
|
389
|
+
var elapsed = Math.round(performance.now() - start);
|
|
390
|
+
var status = response.status;
|
|
391
|
+
var cloned = response.clone();
|
|
392
|
+
|
|
393
|
+
cloned.text().then(function(bodyText) {
|
|
394
|
+
var entry = {
|
|
395
|
+
id: id, method: method, url: url, status: status,
|
|
396
|
+
elapsed: elapsed, bodyPreview: bodyText.slice(0, 5000),
|
|
397
|
+
timestamp: Date.now()
|
|
398
|
+
};
|
|
399
|
+
__zqRequests.push(entry);
|
|
400
|
+
if (__zqRequests.length > 500) __zqRequests.shift();
|
|
401
|
+
updateDevBar();
|
|
402
|
+
|
|
403
|
+
// Pretty console log
|
|
404
|
+
var isOk = status >= 200 && status < 300;
|
|
405
|
+
var color = isOk ? '#2ecc71' : status < 400 ? '#f39c12' : '#e74c3c';
|
|
406
|
+
|
|
407
|
+
console.groupCollapsed(
|
|
408
|
+
'%c ' + method + ' %c' + status + '%c ' + url + ' %c' + elapsed + 'ms',
|
|
409
|
+
'background:' + color + ';color:#fff;padding:2px 6px;border-radius:3px;font-weight:700;font-size:11px',
|
|
410
|
+
'color:' + color + ';font-weight:700;margin-left:8px',
|
|
411
|
+
'color:inherit;margin-left:4px',
|
|
412
|
+
'color:#888;margin-left:8px;font-size:11px'
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
// Response body
|
|
416
|
+
try {
|
|
417
|
+
var parsed = JSON.parse(bodyText);
|
|
418
|
+
console.log('%c Response ', 'background:#1e1e2e;color:#8be9fd;padding:2px 6px;border-radius:2px;font-weight:600', parsed);
|
|
419
|
+
} catch(pe) {
|
|
420
|
+
if (bodyText.length > 0) {
|
|
421
|
+
console.log('%c Response ', 'background:#1e1e2e;color:#8be9fd;padding:2px 6px;border-radius:2px;font-weight:600',
|
|
422
|
+
bodyText.length > 500 ? bodyText.slice(0, 500) + '... (' + bodyText.length + ' chars)' : bodyText);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Headers
|
|
427
|
+
try {
|
|
428
|
+
console.log('%c Headers ', 'background:#1e1e2e;color:#bd93f9;padding:2px 6px;border-radius:2px;font-weight:600',
|
|
429
|
+
Object.fromEntries(response.headers.entries()));
|
|
430
|
+
} catch(he) {}
|
|
431
|
+
|
|
432
|
+
// Request body (if sent)
|
|
433
|
+
if (init && init.body) {
|
|
434
|
+
try {
|
|
435
|
+
console.log('%c Request ', 'background:#1e1e2e;color:#f1fa8c;padding:2px 6px;border-radius:2px;font-weight:600',
|
|
436
|
+
JSON.parse(init.body));
|
|
437
|
+
} catch(re) {
|
|
438
|
+
console.log('%c Request ', 'background:#1e1e2e;color:#f1fa8c;padding:2px 6px;border-radius:2px;font-weight:600',
|
|
439
|
+
String(init.body).slice(0, 500));
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
console.groupEnd();
|
|
444
|
+
|
|
445
|
+
// Broadcast to devtools
|
|
446
|
+
if (__zqChannel) {
|
|
447
|
+
try { __zqChannel.postMessage({ type: 'http', data: entry }); } catch(ce) {}
|
|
448
|
+
}
|
|
449
|
+
}).catch(function() {});
|
|
450
|
+
|
|
451
|
+
return response;
|
|
452
|
+
}, function(err) {
|
|
453
|
+
var elapsed = Math.round(performance.now() - start);
|
|
454
|
+
console.groupCollapsed(
|
|
455
|
+
'%c ' + method + ' %cERR%c ' + url + ' %c' + elapsed + 'ms',
|
|
456
|
+
'background:#e74c3c;color:#fff;padding:2px 6px;border-radius:3px;font-weight:700;font-size:11px',
|
|
457
|
+
'color:#e74c3c;font-weight:700;margin-left:8px',
|
|
458
|
+
'color:inherit;margin-left:4px',
|
|
459
|
+
'color:#888;margin-left:8px;font-size:11px'
|
|
460
|
+
);
|
|
461
|
+
console.error(err);
|
|
462
|
+
console.groupEnd();
|
|
463
|
+
|
|
464
|
+
var entry = { id: id, method: method, url: url, status: 0, elapsed: elapsed, bodyPreview: err.message, timestamp: Date.now() };
|
|
465
|
+
__zqRequests.push(entry);
|
|
466
|
+
updateDevBar();
|
|
467
|
+
if (__zqChannel) {
|
|
468
|
+
try { __zqChannel.postMessage({ type: 'http', data: entry }); } catch(ce) {}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
throw err;
|
|
472
|
+
});
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
// =====================================================================
|
|
476
|
+
// Morph instrumentation — hook via window.__zqMorphHook (set by diff.js)
|
|
477
|
+
// =====================================================================
|
|
478
|
+
window.__zqMorphHook = function(el, elapsed) {
|
|
479
|
+
__zqMorphCount++;
|
|
480
|
+
updateDevBar();
|
|
481
|
+
|
|
482
|
+
var evt = { target: el.id || el.tagName.toLowerCase(), elapsed: elapsed, kind: 'morph', timestamp: Date.now() };
|
|
483
|
+
__zqMorphEvents.push(evt);
|
|
484
|
+
if (__zqMorphEvents.length > 200) __zqMorphEvents.shift();
|
|
485
|
+
|
|
486
|
+
// Console timing for slow morphs (> 4ms)
|
|
487
|
+
if (elapsed > 4) {
|
|
488
|
+
console.log(
|
|
489
|
+
'%c morph %c' + elapsed.toFixed(2) + 'ms%c ' + (el.id || el.tagName.toLowerCase()),
|
|
490
|
+
'background:#9b59b6;color:#fff;padding:2px 6px;border-radius:3px;font-weight:700;font-size:11px',
|
|
491
|
+
'color:' + (elapsed > 16 ? '#e74c3c' : '#f39c12') + ';font-weight:700;margin-left:8px',
|
|
492
|
+
'color:#888;margin-left:4px'
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Broadcast to devtools
|
|
497
|
+
if (__zqChannel) {
|
|
498
|
+
try {
|
|
499
|
+
__zqChannel.postMessage({
|
|
500
|
+
type: 'morph-detail',
|
|
501
|
+
data: evt
|
|
502
|
+
});
|
|
503
|
+
} catch(ce) {}
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
// =====================================================================
|
|
508
|
+
// Render instrumentation — hook for first-renders & route swaps
|
|
509
|
+
// =====================================================================
|
|
510
|
+
window.__zqRenderHook = function(el, elapsed, kind, name) {
|
|
511
|
+
__zqRenderCount++;
|
|
512
|
+
__zqMorphCount++; // count renders in the morph total for the toolbar
|
|
513
|
+
updateDevBar();
|
|
514
|
+
|
|
515
|
+
var evt = { target: name || el.id || el.tagName.toLowerCase(), elapsed: elapsed, kind: kind, timestamp: Date.now() };
|
|
516
|
+
__zqMorphEvents.push(evt);
|
|
517
|
+
if (__zqMorphEvents.length > 200) __zqMorphEvents.shift();
|
|
518
|
+
|
|
519
|
+
// Console log for route/mount renders
|
|
520
|
+
var label = kind === 'route' ? ' route ' : ' mount ';
|
|
521
|
+
var bg = kind === 'route' ? '#d29922' : '#3fb950';
|
|
522
|
+
console.log(
|
|
523
|
+
'%c' + label + '%c' + elapsed.toFixed(2) + 'ms%c ' + (name || el.id || el.tagName.toLowerCase()),
|
|
524
|
+
'background:' + bg + ';color:#fff;padding:2px 6px;border-radius:3px;font-weight:700;font-size:11px',
|
|
525
|
+
'color:' + (elapsed > 16 ? '#e74c3c' : '#888') + ';font-weight:700;margin-left:8px',
|
|
526
|
+
'color:#888;margin-left:4px'
|
|
527
|
+
);
|
|
528
|
+
|
|
529
|
+
// Broadcast to devtools
|
|
530
|
+
if (__zqChannel) {
|
|
531
|
+
try {
|
|
532
|
+
__zqChannel.postMessage({
|
|
533
|
+
type: 'render-detail',
|
|
534
|
+
data: evt
|
|
535
|
+
});
|
|
536
|
+
} catch(ce) {}
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
// =====================================================================
|
|
541
|
+
// Router instrumentation — history state tracking for devtools
|
|
542
|
+
// =====================================================================
|
|
543
|
+
var __zqRouterEvents = [];
|
|
544
|
+
|
|
545
|
+
var _origPushState = history.pushState;
|
|
546
|
+
history.pushState = function(state, title, url) {
|
|
547
|
+
_origPushState.apply(this, arguments);
|
|
548
|
+
var isSubstate = state && state.__zq === 'substate';
|
|
549
|
+
var evt = {
|
|
550
|
+
action: isSubstate ? 'substate' : 'navigate',
|
|
551
|
+
url: String(url || location.href).replace(location.origin, ''),
|
|
552
|
+
key: isSubstate ? state.key : null,
|
|
553
|
+
data: isSubstate ? state.data : null,
|
|
554
|
+
timestamp: Date.now()
|
|
555
|
+
};
|
|
556
|
+
__zqRouterEvents.push(evt);
|
|
557
|
+
if (__zqRouterEvents.length > 200) __zqRouterEvents.shift();
|
|
558
|
+
if (__zqChannel) {
|
|
559
|
+
try { __zqChannel.postMessage({ type: 'router', data: evt }); } catch(e) {}
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
var _origReplaceState = history.replaceState;
|
|
564
|
+
history.replaceState = function(state, title, url) {
|
|
565
|
+
_origReplaceState.apply(this, arguments);
|
|
566
|
+
var isSubstate = state && state.__zq === 'substate';
|
|
567
|
+
var evt = {
|
|
568
|
+
action: 'replace',
|
|
569
|
+
url: String(url || location.href).replace(location.origin, ''),
|
|
570
|
+
key: isSubstate ? state.key : null,
|
|
571
|
+
data: isSubstate ? state.data : null,
|
|
572
|
+
timestamp: Date.now()
|
|
573
|
+
};
|
|
574
|
+
__zqRouterEvents.push(evt);
|
|
575
|
+
if (__zqRouterEvents.length > 200) __zqRouterEvents.shift();
|
|
576
|
+
if (__zqChannel) {
|
|
577
|
+
try { __zqChannel.postMessage({ type: 'router', data: evt }); } catch(e) {}
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
window.addEventListener('popstate', function(e) {
|
|
582
|
+
var state = e.state;
|
|
583
|
+
var isSubstate = state && state.__zq === 'substate';
|
|
584
|
+
var evt = {
|
|
585
|
+
action: isSubstate ? 'pop-substate' : 'pop',
|
|
586
|
+
url: location.pathname + location.hash,
|
|
587
|
+
key: isSubstate ? state.key : null,
|
|
588
|
+
data: isSubstate ? state.data : null,
|
|
589
|
+
timestamp: Date.now()
|
|
590
|
+
};
|
|
591
|
+
__zqRouterEvents.push(evt);
|
|
592
|
+
if (__zqRouterEvents.length > 200) __zqRouterEvents.shift();
|
|
593
|
+
if (__zqChannel) {
|
|
594
|
+
try { __zqChannel.postMessage({ type: 'router', data: evt }); } catch(e) {}
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
window.addEventListener('hashchange', function() {
|
|
599
|
+
var evt = {
|
|
600
|
+
action: 'hashchange',
|
|
601
|
+
url: location.hash,
|
|
602
|
+
timestamp: Date.now()
|
|
603
|
+
};
|
|
604
|
+
__zqRouterEvents.push(evt);
|
|
605
|
+
if (__zqRouterEvents.length > 200) __zqRouterEvents.shift();
|
|
606
|
+
if (__zqChannel) {
|
|
607
|
+
try { __zqChannel.postMessage({ type: 'router', data: evt }); } catch(e) {}
|
|
608
|
+
}
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
// =====================================================================
|
|
612
|
+
// Dev Toolbar — floating bar with DOM viewer button & request counter
|
|
613
|
+
// =====================================================================
|
|
614
|
+
var devBar;
|
|
615
|
+
|
|
616
|
+
function createDevBar() {
|
|
617
|
+
devBar = document.createElement('div');
|
|
618
|
+
devBar.id = '__zq_devbar';
|
|
619
|
+
devBar.setAttribute('style',
|
|
620
|
+
'position:fixed;bottom:12px;right:12px;z-index:2147483646;' +
|
|
621
|
+
'display:flex;align-items:center;gap:6px;' +
|
|
622
|
+
'background:rgba(22,27,34,0.92);border:1px solid rgba(48,54,61,0.8);' +
|
|
623
|
+
'border-radius:8px;padding:4px 6px;backdrop-filter:blur(8px);' +
|
|
624
|
+
'font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;' +
|
|
625
|
+
'font-size:11px;color:#8b949e;user-select:none;cursor:default;' +
|
|
626
|
+
'box-shadow:0 4px 12px rgba(0,0,0,0.4);'
|
|
627
|
+
);
|
|
628
|
+
devBar.innerHTML =
|
|
629
|
+
'<span style="color:#58a6ff;font-weight:700;padding:0 4px;font-size:10px;letter-spacing:.5px">zQ</span>' +
|
|
630
|
+
'<span id="__zq_bar_reqs" title="Network requests" style="padding:2px 6px;border-radius:4px;' +
|
|
631
|
+
'background:rgba(88,166,255,0.1);color:#58a6ff;cursor:pointer;font-size:10px;font-weight:600;">0 req</span>' +
|
|
632
|
+
'<span id="__zq_bar_morphs" title="Render operations" style="padding:2px 6px;border-radius:4px;' +
|
|
633
|
+
'background:rgba(188,140,255,0.1);color:#bc8cff;cursor:pointer;font-size:10px;font-weight:600;">0 render</span>' +
|
|
634
|
+
'<button id="__zq_bar_dom" title="Open DevTools (/_devtools)" style="' +
|
|
635
|
+
'padding:3px 8px;border-radius:4px;font-size:10px;font-weight:700;' +
|
|
636
|
+
'background:rgba(63,185,80,0.15);color:#3fb950;border:1px solid rgba(63,185,80,0.3);' +
|
|
637
|
+
'cursor:pointer;font-family:inherit;transition:all .15s;' +
|
|
638
|
+
'">DOM</button>' +
|
|
639
|
+
'<button id="__zq_bar_close" title="Close toolbar" style="' +
|
|
640
|
+
'padding:0 4px;color:#484f58;cursor:pointer;font-size:14px;border:none;' +
|
|
641
|
+
'background:none;font-family:inherit;line-height:1;' +
|
|
642
|
+
'">×</button>';
|
|
643
|
+
|
|
644
|
+
document.body.appendChild(devBar);
|
|
645
|
+
updateDevBar();
|
|
646
|
+
|
|
647
|
+
// Check if we're inside a devtools split-view iframe
|
|
648
|
+
function isInSplitFrame() {
|
|
649
|
+
try { return window.parent !== window && window.parent.document.getElementById('app-frame'); }
|
|
650
|
+
catch(e) { return false; }
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// Switch tab in devtools (works for both split iframe and popup)
|
|
654
|
+
function switchDevTab(tab) {
|
|
655
|
+
if (__zqChannel) {
|
|
656
|
+
__zqChannel.postMessage({ type: 'switch-tab', tab: tab });
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// Req counter → Network tab
|
|
661
|
+
document.getElementById('__zq_bar_reqs').addEventListener('click', function() {
|
|
662
|
+
if (isInSplitFrame()) {
|
|
663
|
+
switchDevTab('network');
|
|
664
|
+
} else {
|
|
665
|
+
openDevToolsPopup('network');
|
|
666
|
+
}
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
// Render counter → Performance tab
|
|
670
|
+
document.getElementById('__zq_bar_morphs').addEventListener('click', function() {
|
|
671
|
+
if (isInSplitFrame()) {
|
|
672
|
+
switchDevTab('perf');
|
|
673
|
+
} else {
|
|
674
|
+
openDevToolsPopup('perf');
|
|
675
|
+
}
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
// DOM button → Elements tab (in split) or open popup
|
|
679
|
+
var __zqPopup = null;
|
|
680
|
+
function openDevToolsPopup(tab) {
|
|
681
|
+
// If popup is already open, just switch the tab via BroadcastChannel
|
|
682
|
+
if (__zqPopup && !__zqPopup.closed) {
|
|
683
|
+
switchDevTab(tab);
|
|
684
|
+
__zqPopup.focus();
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
var w = 1080, h = 800;
|
|
688
|
+
var left = window.screenX + window.outerWidth - w - 20;
|
|
689
|
+
var top = window.screenY + 60;
|
|
690
|
+
var url = '/_devtools' + (tab ? '#' + tab : '');
|
|
691
|
+
__zqPopup = window.open(url, '__zq_devtools',
|
|
692
|
+
'width=' + w + ',height=' + h + ',left=' + left + ',top=' + top +
|
|
693
|
+
',resizable=yes,scrollbars=yes');
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
document.getElementById('__zq_bar_dom').addEventListener('click', function() {
|
|
697
|
+
if (isInSplitFrame()) {
|
|
698
|
+
switchDevTab('dom');
|
|
699
|
+
} else {
|
|
700
|
+
openDevToolsPopup('dom');
|
|
701
|
+
}
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
// Close button
|
|
705
|
+
document.getElementById('__zq_bar_close').addEventListener('click', function() {
|
|
706
|
+
devBar.style.display = 'none';
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
// Hover effects
|
|
710
|
+
document.getElementById('__zq_bar_dom').addEventListener('mouseover', function() {
|
|
711
|
+
this.style.background = 'rgba(63,185,80,0.3)';
|
|
712
|
+
});
|
|
713
|
+
document.getElementById('__zq_bar_dom').addEventListener('mouseout', function() {
|
|
714
|
+
this.style.background = 'rgba(63,185,80,0.15)';
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
function updateDevBar() {
|
|
719
|
+
if (!devBar) return;
|
|
720
|
+
var reqEl = document.getElementById('__zq_bar_reqs');
|
|
721
|
+
var morphEl = document.getElementById('__zq_bar_morphs');
|
|
722
|
+
if (reqEl) reqEl.textContent = __zqRequests.length + ' req';
|
|
723
|
+
if (morphEl) morphEl.textContent = __zqMorphCount + ' render';
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// Expose for devtools popup
|
|
727
|
+
window.__zqDevTools = {
|
|
728
|
+
get requests() { return __zqRequests; },
|
|
729
|
+
get morphEvents() { return __zqMorphEvents; },
|
|
730
|
+
get morphCount() { return __zqMorphCount; },
|
|
731
|
+
get renderCount() { return __zqRenderCount; },
|
|
732
|
+
get routerEvents() { return __zqRouterEvents; }
|
|
733
|
+
};
|
|
734
|
+
|
|
735
|
+
if (document.readyState === 'loading') {
|
|
736
|
+
document.addEventListener('DOMContentLoaded', createDevBar);
|
|
737
|
+
} else {
|
|
738
|
+
createDevBar();
|
|
739
|
+
}
|
|
363
740
|
})();
|
|
364
741
|
</script>`;
|
|
365
742
|
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
const fs = require('fs');
|
|
12
12
|
const path = require('path');
|
|
13
13
|
const OVERLAY_SCRIPT = require('./overlay');
|
|
14
|
+
const DEVTOOLS_HTML = require('./devtools');
|
|
14
15
|
|
|
15
16
|
// ---------------------------------------------------------------------------
|
|
16
17
|
// SSE client pool
|
|
@@ -102,6 +103,13 @@ async function createServer({ root, htmlEntry, port, noIntercept }) {
|
|
|
102
103
|
pool.add(sse);
|
|
103
104
|
});
|
|
104
105
|
|
|
106
|
+
// ---- DevTools panel ----
|
|
107
|
+
app.get('/_devtools', (req, res) => {
|
|
108
|
+
res.set('Content-Type', 'text/html; charset=utf-8');
|
|
109
|
+
res.set('Cache-Control', 'no-cache');
|
|
110
|
+
res.send(DEVTOOLS_HTML);
|
|
111
|
+
});
|
|
112
|
+
|
|
105
113
|
// ---- Auto-resolve zquery.min.js ----
|
|
106
114
|
const pkgRoot = path.resolve(__dirname, '..', '..', '..');
|
|
107
115
|
|
|
@@ -60,12 +60,13 @@ function collectWatchDirs(dir) {
|
|
|
60
60
|
* @param {SSEPool} opts.pool — SSE broadcast pool
|
|
61
61
|
* @returns {{ dirs: string[], destroy: Function }}
|
|
62
62
|
*/
|
|
63
|
-
function startWatcher({ root, pool }) {
|
|
63
|
+
function startWatcher({ root, pool, bundleMode, serveRoot }) {
|
|
64
64
|
const watchDirs = collectWatchDirs(root);
|
|
65
65
|
const watchers = [];
|
|
66
66
|
|
|
67
67
|
let debounceTimer;
|
|
68
68
|
let currentError = null; // track which file has an active error
|
|
69
|
+
let bundleTimer = null; // debounce for bundle rebuilds
|
|
69
70
|
|
|
70
71
|
// Track file mtimes so we only react to genuine writes.
|
|
71
72
|
// On Windows, fs.watch fires on reads/access too, which causes
|
|
@@ -128,6 +129,30 @@ function startWatcher({ root, pool }) {
|
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
// ---- Full reload ----
|
|
132
|
+
if (bundleMode) {
|
|
133
|
+
// Debounce bundle rebuilds (500ms) so rapid saves don't spam
|
|
134
|
+
clearTimeout(bundleTimer);
|
|
135
|
+
bundleTimer = setTimeout(() => {
|
|
136
|
+
try {
|
|
137
|
+
const bundleFn = require('../bundle');
|
|
138
|
+
const { args: cliArgs } = require('../../args');
|
|
139
|
+
const savedArgs = cliArgs.slice();
|
|
140
|
+
cliArgs.length = 0;
|
|
141
|
+
cliArgs.push('bundle');
|
|
142
|
+
const prevCwd = process.cwd();
|
|
143
|
+
process.chdir(root);
|
|
144
|
+
bundleFn();
|
|
145
|
+
process.chdir(prevCwd);
|
|
146
|
+
cliArgs.length = 0;
|
|
147
|
+
savedArgs.forEach(a => cliArgs.push(a));
|
|
148
|
+
logReload(rel + ' (rebuilt)');
|
|
149
|
+
pool.broadcast('reload', rel);
|
|
150
|
+
} catch (err) {
|
|
151
|
+
console.error(' Bundle rebuild failed:', err.message);
|
|
152
|
+
}
|
|
153
|
+
}, 500);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
131
156
|
logReload(rel);
|
|
132
157
|
pool.broadcast('reload', rel);
|
|
133
158
|
}, 100);
|
package/cli/help.js
CHANGED
|
@@ -7,7 +7,7 @@ function showHelp() {
|
|
|
7
7
|
COMMANDS
|
|
8
8
|
|
|
9
9
|
create [dir] Scaffold a new zQuery project
|
|
10
|
-
Creates index.html,
|
|
10
|
+
Creates index.html, global.css, app/, assets/ in the target directory
|
|
11
11
|
(defaults to the current directory)
|
|
12
12
|
|
|
13
13
|
dev [root] Start a dev server with live-reload
|
|
@@ -15,6 +15,8 @@ function showHelp() {
|
|
|
15
15
|
--index, -i <file> Index HTML file (default: index.html)
|
|
16
16
|
--no-intercept Disable auto-resolution of zquery.min.js
|
|
17
17
|
(serve the on-disk vendor copy instead)
|
|
18
|
+
--bundle, -b Serve the bundled build (runs bundler first,
|
|
19
|
+
serves from dist/server/, auto-rebuilds on save)
|
|
18
20
|
|
|
19
21
|
Includes error overlay: syntax errors are
|
|
20
22
|
caught on save and shown as a full-screen
|
|
@@ -24,7 +26,8 @@ function showHelp() {
|
|
|
24
26
|
bundle [dir|file] Bundle app ES modules into a single file
|
|
25
27
|
--out, -o <path> Output directory (default: dist/ next to HTML file)
|
|
26
28
|
--index, -i <file> Index HTML file (default: auto-detected)
|
|
27
|
-
--minimal, -m Only output HTML
|
|
29
|
+
--minimal, -m Only output HTML, bundled JS, and global CSS (skip static assets)
|
|
30
|
+
--global-css <path> Override global CSS input (default: first <link> in HTML)
|
|
28
31
|
|
|
29
32
|
build Build the zQuery library \u2192 dist/ --watch, -w Watch src/ and rebuild on changes (must be run from the project root where src/ lives)
|
|
30
33
|
|
|
@@ -35,7 +38,7 @@ function showHelp() {
|
|
|
35
38
|
1. index.html first, then other .html files
|
|
36
39
|
2. Within HTML: module script pointing to app.js, else first module script
|
|
37
40
|
3. JS scan: $.router( first (entry point), then $.mount( / $.store(
|
|
38
|
-
4. Convention fallbacks (scripts/app.js, app.js, etc.)
|
|
41
|
+
4. Convention fallbacks (app/app.js, scripts/app.js, app.js, etc.)
|
|
39
42
|
• Passing a directory auto-detects the entry; passing a file uses it directly
|
|
40
43
|
• zquery.min.js is always embedded (auto-built from source if not found)
|
|
41
44
|
• HTML file is auto-detected (any .html, not just index.html)
|
|
@@ -83,12 +86,12 @@ function showHelp() {
|
|
|
83
86
|
zquery bundle my-app/
|
|
84
87
|
|
|
85
88
|
# Pass a direct entry file (skip auto-detection)
|
|
86
|
-
zquery bundle my-app/
|
|
89
|
+
zquery bundle my-app/app/main.js
|
|
87
90
|
|
|
88
91
|
# Custom output directory
|
|
89
92
|
zquery bundle my-app/ -o build/
|
|
90
93
|
|
|
91
|
-
# Minimal build (
|
|
94
|
+
# Minimal build (HTML + JS + global CSS, no static asset copying)
|
|
92
95
|
zquery bundle my-app/ --minimal
|
|
93
96
|
|
|
94
97
|
# Dev server with a custom index page
|
|
@@ -31,16 +31,16 @@ $.component('about-page', {
|
|
|
31
31
|
</div>
|
|
32
32
|
|
|
33
33
|
<div class="card">
|
|
34
|
-
<h3
|
|
34
|
+
<h3><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="var(--accent)" style="width:20px;height:20px;vertical-align:-4px;margin-right:0.25rem;"><path stroke-linecap="round" stroke-linejoin="round" d="M9.53 16.122a3 3 0 0 0-5.78 1.128 2.25 2.25 0 0 1-2.4 2.245 4.5 4.5 0 0 0 8.4-2.245c0-.399-.078-.78-.22-1.128Zm0 0a15.998 15.998 0 0 0 3.388-1.62m-5.043-.025a15.994 15.994 0 0 1 1.622-3.395m3.42 3.42a15.995 15.995 0 0 0 4.764-4.648l3.876-5.814a1.151 1.151 0 0 0-1.597-1.597L14.146 6.32a15.996 15.996 0 0 0-4.649 4.763m3.42 3.42a6.776 6.776 0 0 0-3.42-3.42"/></svg> Theme</h3>
|
|
35
35
|
<p>Toggle between dark and light mode. Persisted to <code>localStorage</code> via <code>$.storage</code>.</p>
|
|
36
36
|
<div class="theme-toggle">
|
|
37
37
|
<span>Current: <strong>${this.state.theme}</strong></span>
|
|
38
|
-
<button class="btn btn-outline" @click="toggleTheme">${this.state.theme === 'dark' ? '
|
|
38
|
+
<button class="btn btn-outline" @click="toggleTheme">${this.state.theme === 'dark' ? '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" style="width:16px;height:16px;vertical-align:-3px;margin-right:0.15rem;"><path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2.25m6.364.386-1.591 1.591M21 12h-2.25m-.386 6.364-1.591-1.591M12 18.75V21m-4.773-4.227-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0Z"/></svg> Light Mode' : '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" style="width:16px;height:16px;vertical-align:-3px;margin-right:0.15rem;"><path stroke-linecap="round" stroke-linejoin="round" d="M21.752 15.002A9.72 9.72 0 0 1 18 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 0 0 3 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 0 0 9.002-5.998Z"/></svg> Dark Mode'}</button>
|
|
39
39
|
</div>
|
|
40
40
|
</div>
|
|
41
41
|
|
|
42
42
|
<div class="card">
|
|
43
|
-
<h3
|
|
43
|
+
<h3><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="var(--accent)" style="width:20px;height:20px;vertical-align:-4px;margin-right:0.25rem;"><path stroke-linecap="round" stroke-linejoin="round" d="M21.75 6.75a4.5 4.5 0 0 1-4.884 4.484c-1.076-.091-2.264.071-2.95.904l-7.152 8.684a2.548 2.548 0 1 1-3.586-3.586l8.684-7.152c.833-.686.995-1.874.904-2.95a4.5 4.5 0 0 1 6.336-4.486l-3.276 3.276a3.004 3.004 0 0 0 2.25 2.25l3.276-3.276c.256.565.398 1.192.398 1.852Z"/></svg> Features Used in This App</h3>
|
|
44
44
|
<div class="feature-grid">
|
|
45
45
|
<div class="feature-item">
|
|
46
46
|
<strong>$.component()</strong>
|
|
@@ -118,7 +118,7 @@ $.component('about-page', {
|
|
|
118
118
|
</div>
|
|
119
119
|
|
|
120
120
|
<div class="card card-muted">
|
|
121
|
-
<h3
|
|
121
|
+
<h3><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="var(--accent)" style="width:20px;height:20px;vertical-align:-4px;margin-right:0.25rem;"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6.042A8.967 8.967 0 0 0 6 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 0 1 6 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 0 1 6-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0 0 18 18a8.967 8.967 0 0 0-6 2.292m0-14.25v14.25"/></svg> Next Steps</h3>
|
|
122
122
|
<ul class="next-steps">
|
|
123
123
|
<li>Read the <a href="https://z-query.com/docs" target="_blank" rel="noopener">full documentation</a></li>
|
|
124
124
|
<li>Explore the <a href="https://github.com/tonywied17/zero-query" target="_blank" rel="noopener">source on GitHub</a></li>
|
|
@@ -58,7 +58,7 @@ $.component('api-demo', {
|
|
|
58
58
|
<p class="subtitle">Fetching data with <code>$.get()</code>. Directives: <code>z-if</code>, <code>z-show</code>, <code>z-for</code>, <code>z-text</code>.</p>
|
|
59
59
|
</div>
|
|
60
60
|
|
|
61
|
-
<div class="card card-error" z-show="error"><p
|
|
61
|
+
<div class="card card-error" z-show="error"><p><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" style="width:16px;height:16px;vertical-align:-3px;margin-right:0.15rem;"><path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"/></svg> <span z-text="error"></span></p></div>
|
|
62
62
|
<div class="loading-bar" z-show="loading"></div>
|
|
63
63
|
|
|
64
64
|
<div z-if="!selectedUser">
|