lahama 5.0.0 → 6.0.0
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/dist/lahama.js +302 -38
- package/package.json +1 -1
package/dist/lahama.js
CHANGED
|
@@ -384,6 +384,7 @@ function createComponentNode(vdom, parentEl, index, hostComponent) {
|
|
|
384
384
|
const { props, events } = extractPropsAndEvents(vdom);
|
|
385
385
|
const component = new Component(props, events, hostComponent);
|
|
386
386
|
component.setExternalContent(children);
|
|
387
|
+
component.setAppContext(hostComponent?.appContext ?? {});
|
|
387
388
|
component.mount(parentEl, index);
|
|
388
389
|
vdom.component = component;
|
|
389
390
|
vdom.el = component.firstElement;
|
|
@@ -433,10 +434,253 @@ function removeFragmentNode(vdom) {
|
|
|
433
434
|
children.forEach(destroyDom);
|
|
434
435
|
}
|
|
435
436
|
|
|
436
|
-
function
|
|
437
|
+
function makeRouteMatcher(route) {
|
|
438
|
+
return routeHasParams(route)
|
|
439
|
+
? makeMatcherWithParams(route)
|
|
440
|
+
: makeMatcherWithoutParams(route)
|
|
441
|
+
}
|
|
442
|
+
function routeHasParams({ path }) {
|
|
443
|
+
return path.includes(':')
|
|
444
|
+
}
|
|
445
|
+
const CATCH_ALL_ROUTE = '*';
|
|
446
|
+
function makeRouteWithoutParamsRegex( { path }) {
|
|
447
|
+
if (path === CATCH_ALL_ROUTE) {
|
|
448
|
+
return new RegExp('^.*$')
|
|
449
|
+
}
|
|
450
|
+
return new RegExp(`^${path}`)
|
|
451
|
+
}
|
|
452
|
+
function makeMatcherWithoutParams(route) {
|
|
453
|
+
const regex = makeRouteWithoutParamsRegex(route);
|
|
454
|
+
const isRedirect = typeof route.redirect === 'string';
|
|
455
|
+
return {
|
|
456
|
+
route,
|
|
457
|
+
isRedirect,
|
|
458
|
+
checkMatch(path) {
|
|
459
|
+
return regex.test(path)
|
|
460
|
+
},
|
|
461
|
+
extractParams() {
|
|
462
|
+
return {}
|
|
463
|
+
},
|
|
464
|
+
extractQuery,
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
function extractQuery(path) {
|
|
468
|
+
const queryIndex = path.indexOf('?');
|
|
469
|
+
if (queryIndex === -1) {
|
|
470
|
+
return {}
|
|
471
|
+
}
|
|
472
|
+
const search = new URLSearchParams(path.slice(queryIndex + 1));
|
|
473
|
+
return Object.fromEntries(search.entries())
|
|
474
|
+
}
|
|
475
|
+
function makeRouteWithParamsRegex({ path }) {
|
|
476
|
+
const regex = path.replace(
|
|
477
|
+
/:([^/]+)/g,
|
|
478
|
+
(_, paramName) => `(?<${paramName}>[^/]+)`
|
|
479
|
+
);
|
|
480
|
+
return new RegExp(`^${regex}$`)
|
|
481
|
+
}
|
|
482
|
+
function makeMatcherWithParams(route) {
|
|
483
|
+
const regex = makeRouteWithParamsRegex(route);
|
|
484
|
+
const isRedirect = typeof route.redirect === 'string';
|
|
485
|
+
return {
|
|
486
|
+
route,
|
|
487
|
+
isRedirect,
|
|
488
|
+
checkMatch(path) {
|
|
489
|
+
return regex.test(path)
|
|
490
|
+
},
|
|
491
|
+
extractParams(path) {
|
|
492
|
+
const { groups } = regex.exec(path);
|
|
493
|
+
return groups
|
|
494
|
+
},
|
|
495
|
+
extractQuery,
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
class Dispatcher {
|
|
500
|
+
#subs = new Map()
|
|
501
|
+
#afterHandlers = []
|
|
502
|
+
subscribe(commandName, handler) {
|
|
503
|
+
if (!this.#subs.has(commandName)) {
|
|
504
|
+
this.#subs.set(commandName, []);
|
|
505
|
+
}
|
|
506
|
+
const handlersArray = this.#subs.get(commandName);
|
|
507
|
+
if (handlersArray.includes(handler)) {
|
|
508
|
+
return () => {
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
handlersArray.push(handler);
|
|
512
|
+
return () => {
|
|
513
|
+
const idx = handlersArray.indexOf(handler);
|
|
514
|
+
handlersArray.splice(idx, 1);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
afterEveryCommand(handler) {
|
|
518
|
+
this.#afterHandlers.push(handler);
|
|
519
|
+
return () => {
|
|
520
|
+
const idx = this.#afterHandlers.indexOf(handler);
|
|
521
|
+
this.#afterHandlers.splice(idx, 1);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
dispatch(commandName, payload) {
|
|
525
|
+
if (this.#subs.has(commandName)) {
|
|
526
|
+
this.#subs.get(commandName).forEach((handler) => handler(payload));
|
|
527
|
+
} else {
|
|
528
|
+
console.warn(`No handlers for command : ${commandName}`);
|
|
529
|
+
}
|
|
530
|
+
this.#afterHandlers.forEach((handler) => handler());
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
const ROUTER_EVENT = 'router-event';
|
|
535
|
+
class HashRouter {
|
|
536
|
+
#matchers = []
|
|
537
|
+
#isInitialized = false
|
|
538
|
+
#onPopState = () => this.#matchCurrentRoute()
|
|
539
|
+
#params = {}
|
|
540
|
+
#query = {}
|
|
541
|
+
#matchedRoute = null
|
|
542
|
+
#dispatcher = new Dispatcher()
|
|
543
|
+
#subscriptions = new WeakMap()
|
|
544
|
+
#subscriberFns = new Set()
|
|
545
|
+
get params() {
|
|
546
|
+
return this.#params
|
|
547
|
+
}
|
|
548
|
+
get query() {
|
|
549
|
+
return this.#query
|
|
550
|
+
}
|
|
551
|
+
get matchedRoute() {
|
|
552
|
+
return this.#matchedRoute
|
|
553
|
+
}
|
|
554
|
+
constructor(routes = []) {
|
|
555
|
+
this.#matchers = routes.map(makeRouteMatcher);
|
|
556
|
+
}
|
|
557
|
+
get #currentRouteHash() {
|
|
558
|
+
const hash = document.location.hash;
|
|
559
|
+
if (hash === '') {
|
|
560
|
+
return '/'
|
|
561
|
+
}
|
|
562
|
+
return hash.slice(1)
|
|
563
|
+
}
|
|
564
|
+
async init() {
|
|
565
|
+
if (this.#isInitialized) {
|
|
566
|
+
return
|
|
567
|
+
}
|
|
568
|
+
this.#isInitialized = true;
|
|
569
|
+
if (document.location.hash === '') {
|
|
570
|
+
window.history.replaceState({}, '', '#/');
|
|
571
|
+
}
|
|
572
|
+
window.addEventListener('popstate', this.#onPopState);
|
|
573
|
+
}
|
|
574
|
+
destroy() {
|
|
575
|
+
if (!this.#isInitialized) {
|
|
576
|
+
return
|
|
577
|
+
}
|
|
578
|
+
window.removeEventListener('popstate', this.#onPopState);
|
|
579
|
+
Array.from(this.#subscriberFns).forEach(this.unsubscribe, this);
|
|
580
|
+
this.#isInitialized = false;
|
|
581
|
+
}
|
|
582
|
+
#matchCurrentRoute() {
|
|
583
|
+
return this.navigateTo(this.#currentRouteHash)
|
|
584
|
+
}
|
|
585
|
+
async navigateTo(path) {
|
|
586
|
+
const matcher = this.#matchers.find((matcher) =>
|
|
587
|
+
matcher.checkMatch(path)
|
|
588
|
+
);
|
|
589
|
+
if (matcher == null) {
|
|
590
|
+
console.warn(`[Router] No route matches path "${path}"`);
|
|
591
|
+
this.#matchedRoute = null;
|
|
592
|
+
this.#params = {};
|
|
593
|
+
this.#query = {};
|
|
594
|
+
return
|
|
595
|
+
}
|
|
596
|
+
if (matcher.isRedirect) {
|
|
597
|
+
return this.navigateTo(matcher.route.redirect)
|
|
598
|
+
}
|
|
599
|
+
const from = this.#matchedRoute;
|
|
600
|
+
const to = matcher.route;
|
|
601
|
+
const { shouldNavigate, shouldRedirect, redirectPath } =
|
|
602
|
+
await this.#canChangeRoute(from, to);
|
|
603
|
+
if (shouldRedirect) {
|
|
604
|
+
return this.navigateTo(redirectPath)
|
|
605
|
+
}
|
|
606
|
+
if (shouldNavigate) {
|
|
607
|
+
this.#matchedRoute = matcher.route;
|
|
608
|
+
this.#params = matcher.extractParams(path);
|
|
609
|
+
this.#query = matcher.extractQuery(path);
|
|
610
|
+
this.#pushState(path);
|
|
611
|
+
this.#dispatcher.dispatch(ROUTER_EVENT, {from, to, router: this});
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
#pushState(path) {
|
|
615
|
+
window.history.pushState({}, '', `#${path}`);
|
|
616
|
+
}
|
|
617
|
+
back() {
|
|
618
|
+
window.history.back();
|
|
619
|
+
}
|
|
620
|
+
forward() {
|
|
621
|
+
window.history.forward();
|
|
622
|
+
}
|
|
623
|
+
subscribe(handler) {
|
|
624
|
+
const unsubscribe = this.#dispatcher.subscribe(ROUTER_EVENT, handler);
|
|
625
|
+
this.#subscriptions.set(handler, unsubscribe);
|
|
626
|
+
this.#subscriberFns.add(handler);
|
|
627
|
+
}
|
|
628
|
+
unsubscribe(handler) {
|
|
629
|
+
const unsubscribe = this.#subscriptions.get(handler);
|
|
630
|
+
if (unsubscribe) {
|
|
631
|
+
unsubscribe();
|
|
632
|
+
this.#subscriptions.delete(handler);
|
|
633
|
+
this.#subscriberFns.delete(handler);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
async #canChangeRoute(from, to) {
|
|
637
|
+
const guard = to.beforeEnter;
|
|
638
|
+
if (typeof guard !== 'function') {
|
|
639
|
+
return {
|
|
640
|
+
shouldRedirect : false,
|
|
641
|
+
shouldNavigate : true,
|
|
642
|
+
redirectPath : null,
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
const result = await guard(from?.path, to?.path);
|
|
646
|
+
if (result === false) {
|
|
647
|
+
return {
|
|
648
|
+
shouldRedirect : false,
|
|
649
|
+
shouldNavigate : false,
|
|
650
|
+
redirectPath: null,
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
if (typeof result === 'string') {
|
|
654
|
+
return {
|
|
655
|
+
shouldRedirect : true,
|
|
656
|
+
shouldNavigate : true,
|
|
657
|
+
redirectPath: result,
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
return {
|
|
661
|
+
shouldRedirect : false,
|
|
662
|
+
shouldNavigate : true,
|
|
663
|
+
redirectPath : null,
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
class NoopRouter {
|
|
668
|
+
init() {}
|
|
669
|
+
destroy() {}
|
|
670
|
+
navigateTo() {}
|
|
671
|
+
back() {}
|
|
672
|
+
forward() {}
|
|
673
|
+
subscribe() {}
|
|
674
|
+
unsubscribe() {}
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
function createApp(RootComponent, props = {}, options = {}) {
|
|
437
678
|
let parentEl = null;
|
|
438
679
|
let isMounted = false;
|
|
439
680
|
let vdom = null;
|
|
681
|
+
const context = {
|
|
682
|
+
router : options.router || new NoopRouter(),
|
|
683
|
+
};
|
|
440
684
|
function reset() {
|
|
441
685
|
parentEl = null;
|
|
442
686
|
isMounted = false;
|
|
@@ -449,7 +693,8 @@ function createApp(RootComponent, props = {}) {
|
|
|
449
693
|
}
|
|
450
694
|
parentEl = _parentEl;
|
|
451
695
|
vdom = h(RootComponent, props);
|
|
452
|
-
mountDom(vdom, parentEl);
|
|
696
|
+
mountDom(vdom, parentEl, null, { appContext : context});
|
|
697
|
+
context.router.init();
|
|
453
698
|
isMounted = true;
|
|
454
699
|
},
|
|
455
700
|
unmount() {
|
|
@@ -457,6 +702,7 @@ function createApp(RootComponent, props = {}) {
|
|
|
457
702
|
throw new Error(`The application is not mounted`)
|
|
458
703
|
}
|
|
459
704
|
destroyDom(vdom);
|
|
705
|
+
context.router.destroy();
|
|
460
706
|
reset();
|
|
461
707
|
}
|
|
462
708
|
}
|
|
@@ -739,41 +985,6 @@ function requireFastDeepEqual () {
|
|
|
739
985
|
var fastDeepEqualExports = requireFastDeepEqual();
|
|
740
986
|
var equal = /*@__PURE__*/getDefaultExportFromCjs(fastDeepEqualExports);
|
|
741
987
|
|
|
742
|
-
class Dispatcher {
|
|
743
|
-
#subs = new Map()
|
|
744
|
-
#afterHandlers = []
|
|
745
|
-
subscribe(commandName, handler) {
|
|
746
|
-
if (!this.#subs.has(commandName)) {
|
|
747
|
-
this.#subs.set(commandName, []);
|
|
748
|
-
}
|
|
749
|
-
const handlersArray = this.#subs.get(commandName);
|
|
750
|
-
if (handlersArray.includes(handler)) {
|
|
751
|
-
return () => {
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
handlersArray.push(handler);
|
|
755
|
-
return () => {
|
|
756
|
-
const idx = handlersArray.indexOf(handler);
|
|
757
|
-
handlersArray.splice(idx, 1);
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
afterEveryCommand(handler) {
|
|
761
|
-
this.#afterHandlers.push(handler);
|
|
762
|
-
return () => {
|
|
763
|
-
const idx = this.#afterHandlers.indexOf(handler);
|
|
764
|
-
this.#afterHandlers.splice(idx, 1);
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
dispatch(commandName, payload) {
|
|
768
|
-
if (this.#subs.has(commandName)) {
|
|
769
|
-
this.#subs.get(commandName).forEach((handler) => handler(payload));
|
|
770
|
-
} else {
|
|
771
|
-
console.warn(`No handlers for command : ${commandName}`);
|
|
772
|
-
}
|
|
773
|
-
this.#afterHandlers.forEach((handler) => handler());
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
|
|
777
988
|
function traverseDFS(
|
|
778
989
|
vdom,
|
|
779
990
|
processNode,
|
|
@@ -827,6 +1038,7 @@ function defineComponent({
|
|
|
827
1038
|
#parentComponent = null
|
|
828
1039
|
#dispatcher = new Dispatcher()
|
|
829
1040
|
#subscriptions = []
|
|
1041
|
+
#appContext = null
|
|
830
1042
|
#children = []
|
|
831
1043
|
setExternalContent(children) {
|
|
832
1044
|
this.#children = children;
|
|
@@ -847,6 +1059,12 @@ function defineComponent({
|
|
|
847
1059
|
onUnMounted() {
|
|
848
1060
|
return Promise.resolve(onUnmounted.call(this))
|
|
849
1061
|
}
|
|
1062
|
+
setAppContext(appContext) {
|
|
1063
|
+
this.#appContext = appContext;
|
|
1064
|
+
}
|
|
1065
|
+
get appContext() {
|
|
1066
|
+
return this.#appContext
|
|
1067
|
+
}
|
|
850
1068
|
get elements() {
|
|
851
1069
|
if (this.#vdom == null) {
|
|
852
1070
|
return []
|
|
@@ -948,4 +1166,50 @@ function defineComponent({
|
|
|
948
1166
|
return Component;
|
|
949
1167
|
}
|
|
950
1168
|
|
|
951
|
-
|
|
1169
|
+
const RouterLink = defineComponent({
|
|
1170
|
+
render() {
|
|
1171
|
+
const { to } = this.props;
|
|
1172
|
+
return h(
|
|
1173
|
+
'a',
|
|
1174
|
+
{
|
|
1175
|
+
href : to,
|
|
1176
|
+
on : {
|
|
1177
|
+
click : (e) => {
|
|
1178
|
+
e.preventDefault();
|
|
1179
|
+
this.appContext.router.navigateTo(to);
|
|
1180
|
+
},
|
|
1181
|
+
},
|
|
1182
|
+
},
|
|
1183
|
+
[hSlot()]
|
|
1184
|
+
)
|
|
1185
|
+
},
|
|
1186
|
+
});
|
|
1187
|
+
const RouterOutlet = defineComponent({
|
|
1188
|
+
state() {
|
|
1189
|
+
return {
|
|
1190
|
+
matchedRoute: null,
|
|
1191
|
+
subscription : null,
|
|
1192
|
+
}
|
|
1193
|
+
},
|
|
1194
|
+
onMounted() {
|
|
1195
|
+
const subscription = this.appContext.router.subscribe(({ to }) => {
|
|
1196
|
+
this.handleRouteChange(to);
|
|
1197
|
+
});
|
|
1198
|
+
this.updateState({ subscription });
|
|
1199
|
+
},
|
|
1200
|
+
onUnmounted() {
|
|
1201
|
+
const { subscription } = this.state();
|
|
1202
|
+
this.appContext.router.unsubscribe(subscription);
|
|
1203
|
+
},
|
|
1204
|
+
handleRouteChange(matchedRoute) {
|
|
1205
|
+
this.updateState({ matchedRoute});
|
|
1206
|
+
},
|
|
1207
|
+
render() {
|
|
1208
|
+
const { matchedRoute } = this.state;
|
|
1209
|
+
return h('div', {id : 'router-outlet'}, [
|
|
1210
|
+
matchedRoute ? h(matchedRoute.component) : null,
|
|
1211
|
+
])
|
|
1212
|
+
}
|
|
1213
|
+
});
|
|
1214
|
+
|
|
1215
|
+
export { DOM_TYPES, HashRouter, RouterLink, RouterOutlet, createApp, defineComponent, h, hFragment, hSlot, hString, nextTick };
|