@ramstack/alpinegear-router 1.1.0-preview.1

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.
@@ -0,0 +1,888 @@
1
+ (function () {
2
+ 'use strict';
3
+
4
+ const warn = (...args) => console.warn("alpine-gear.js:", ...args);
5
+ const is_array = Array.isArray;
6
+ const is_nullish = value => value === null || value === undefined;
7
+ const is_template = el => el instanceof HTMLTemplateElement;
8
+ const is_function = value => typeof value === "function";
9
+ const as_array = value => is_array(value) ? value : [value];
10
+
11
+ function assert(value, message) {
12
+ if (!value) {
13
+ throw new Error(message);
14
+ }
15
+ }
16
+
17
+ const asyncify = fn => {
18
+ if (is_function(fn) && fn.constructor?.name === "AsyncFunction") {
19
+ return fn;
20
+ }
21
+
22
+ return function(...args) {
23
+ const result = fn.apply(this, args);
24
+ return is_function(result?.then) ? result : Promise.resolve(result);
25
+ }
26
+ };
27
+
28
+ const listen = (target, type, listener, options) => {
29
+ target.addEventListener(type, listener, options);
30
+ return () => target.removeEventListener(type, listener, options);
31
+ };
32
+
33
+ const closest = (el, callback) => {
34
+ while (el && !callback(el)) {
35
+ el = (el._x_teleportBack ?? el).parentElement;
36
+ }
37
+
38
+ return el;
39
+ };
40
+
41
+ const default_constraints = Object.freeze({
42
+ "regex"(value) {
43
+ const regexp = new RegExp(value);
44
+ return {
45
+ test: v => regexp.test(v)
46
+ };
47
+ },
48
+ "bool"() {
49
+ return {
50
+ test: v => /^(?:true|false)$/i.test(v),
51
+ transform: v => v.length === 4
52
+ };
53
+ },
54
+ "int"() {
55
+ return {
56
+ test: v => /^\d+$/.test(v),
57
+ transform: v => +v
58
+ };
59
+ },
60
+ "number"() {
61
+ return {
62
+ test: v => /^[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?$/.test(v) && isFinite(parseFloat(v)),
63
+ transform: v => parseFloat(v)
64
+ };
65
+ },
66
+ "alpha"() {
67
+ return {
68
+ test: v => /^[a-z]+$/i.test(v)
69
+ };
70
+ },
71
+ "min"(value) {
72
+ return {
73
+ test: v => v >= +value
74
+ };
75
+ },
76
+ "max"(value) {
77
+ return {
78
+ test: v => v <= +value
79
+ };
80
+ },
81
+ "range"(value) {
82
+ let [a, b] = value.split(",", 2).map(v => v.trim());
83
+ return {
84
+ test: v => v >= +a && v <= +b
85
+ };
86
+ },
87
+ "length"(value) {
88
+ return {
89
+ test: v => v.length === +value
90
+ };
91
+ },
92
+ "minlength"(value) {
93
+ return {
94
+ test: v => v.length >= +value
95
+ };
96
+ },
97
+ "maxlength"(value) {
98
+ return {
99
+ test: v => v.length <= +value
100
+ };
101
+ },
102
+ });
103
+
104
+ class RoutePattern {
105
+ #regex;
106
+ #template;
107
+ #segments;
108
+ #parameters;
109
+ #constraints;
110
+
111
+ get template() {
112
+ return this.#template;
113
+ }
114
+
115
+ get regex() {
116
+ return this.#regex;
117
+ }
118
+
119
+ get constraints() {
120
+ return this.#constraints;
121
+ }
122
+
123
+ constructor(template, constraints = null) {
124
+ this.#template = template;
125
+ this.#regex = build(
126
+ template,
127
+ this.#segments = [],
128
+ this.#parameters = new Map(),
129
+ this.#constraints = constraints ?? {}
130
+ );
131
+ }
132
+
133
+ match(path) {
134
+ let result = this.#regex.exec(path);
135
+
136
+ if (is_nullish(result)) {
137
+ return null;
138
+ }
139
+
140
+ result = result.groups ?? {};
141
+
142
+ for (let [name, parameter] of this.#parameters.entries()) {
143
+ let value = result[name];
144
+
145
+ if (is_nullish(value) && is_nullish(parameter.default)) {
146
+ continue;
147
+ }
148
+
149
+ if (!value && !is_nullish(parameter.default)) {
150
+ value = parameter.default;
151
+ }
152
+
153
+ const values = parameter.catch_all
154
+ ? value.split("/").filter(v => v.length)
155
+ : [value];
156
+
157
+ for (let i = 0; i < values.length; i++) {
158
+ for (let constraint of parameter.constraints) {
159
+ if (constraint.test && !constraint.test(values[i])) {
160
+ return null;
161
+ }
162
+
163
+ if (constraint.transform) {
164
+ values[i] = constraint.transform(values[i]);
165
+ }
166
+ }
167
+ }
168
+
169
+ result[name] = parameter.catch_all ? values : values[0];
170
+ }
171
+
172
+ return result;
173
+ }
174
+
175
+ resolve(values) {
176
+ values = new Map(Object.entries(values));
177
+ const segments = [];
178
+
179
+ for (let segment of this.#segments) {
180
+ const parts = [];
181
+
182
+ for (let part of segment.parts) {
183
+ if (part.kind === "literal") {
184
+ parts.push(part.value);
185
+ }
186
+ else {
187
+ let value = values.get(part.name);
188
+ values.delete(part.name);
189
+
190
+ if (is_nullish(value) || value === "") {
191
+ value = this.#parameters.get(part.name)?.default;
192
+ if (part.catch_all && value) {
193
+ value = value.split("/");
194
+ }
195
+ }
196
+
197
+ if (is_nullish(value) || value === "") {
198
+ if (part.required) {
199
+ return null;
200
+ }
201
+
202
+ // TODO check twice this statement
203
+ if (part.optional && part.default === value) {
204
+ continue;
205
+ }
206
+ }
207
+
208
+ if (part.catch_all) {
209
+ value = as_array(value);
210
+ parts.push(...value.map(v => encodeURIComponent(v)).join("/"));
211
+ }
212
+ else {
213
+ parts.push(encodeURIComponent(value));
214
+ }
215
+ }
216
+ }
217
+
218
+ parts.length && segments.push(parts.join(""));
219
+ }
220
+
221
+ let queries = [...values.entries()].map(([k, v]) => encodeURIComponent(k) + "=" + encodeURIComponent(v)).join("&");
222
+ queries && (queries = "?" + queries);
223
+
224
+ const result = segments.join("/") + queries;
225
+ return result[0] !== "/"
226
+ ? "/" + result
227
+ : result;
228
+ }
229
+ }
230
+
231
+ function build(pattern, segments, parameters, constraints) {
232
+ segments.push(...parse(pattern, constraints));
233
+
234
+ let expression = segments.map(segment => {
235
+ return segment.parts.map((part, index) => {
236
+ if (part.kind === "literal") {
237
+ return index ? part.value : `/${ part.value }`;
238
+ }
239
+
240
+ parameters.set(part.name, part);
241
+
242
+ if (segment.parts.length === 1 && part.quantifier === "?") {
243
+ return `(?:/(?<${ part.name }>[^/]+?))?`;
244
+ }
245
+
246
+ if (part.catch_all) {
247
+ let expr = `(?<${ part.name }>.${ part.quantifier })`;
248
+ index || (expr = `(?:/${ expr })`);
249
+ part.quantifier === "*" && (expr += "?");
250
+ return part.quantifier === "*" ? expr + "?" : expr;
251
+ }
252
+ else {
253
+ const expr = `(?<${ part.name }>[^/]+?)${ part.quantifier }`;
254
+ return index ? expr : `/${ expr }`;
255
+ }
256
+ }).join("");
257
+ }).join("") || "/";
258
+
259
+ expression !== "/" && (expression += "/?");
260
+ return new RegExp(`^${ expression }$`);
261
+ }
262
+
263
+ function parse(pattern, factories) {
264
+ return preprocess(segments());
265
+
266
+ function preprocess(segments) {
267
+ segments.find(s => s.parts.length > 1 && s.parts.every(p => p.optional))
268
+ && throw_error("Using all segment parameters as optional is not permitted");
269
+
270
+ const parameters = new Map;
271
+
272
+ segments.flatMap(s => s.parts).forEach((part, index, parts) => {
273
+ if (part.kind === "literal" && part.value.indexOf("?") >= 0) {
274
+ throw_error("Literal segments cannot contain the '?' character");
275
+ }
276
+
277
+ if (part.kind === "parameter") {
278
+ if (part.catch_all && index !== parts.length - 1) {
279
+ throw_error("A catch-all parameter can only appear as the last segment");
280
+ }
281
+
282
+ if (parameters.has(part.name)) {
283
+ throw_error(`The route parameter name '${part.name}' appears more than one time`);
284
+ }
285
+
286
+ part.quantifier === "*"
287
+ && is_nullish(part.default)
288
+ && (part.default = "");
289
+
290
+ part.default === ""
291
+ && part.quantifier !== "*"
292
+ && (part.default = null);
293
+
294
+ parameters.set(part.name, true);
295
+
296
+ for (let constraint of part.constraints) {
297
+ const factory = factories?.[name] ?? default_constraints[constraint.name];
298
+ is_nullish(factory) && throw_error(`Unknown constraint '${ constraint.name }'`);
299
+ Object.assign(constraint, factory(constraint.argument));
300
+ }
301
+ }
302
+ });
303
+
304
+ return segments;
305
+ }
306
+
307
+ function segments() {
308
+ const segments = [];
309
+
310
+ for (let i = 0; i < pattern.length;) {
311
+ const r = segment(i);
312
+ r.template && segments.push(r);
313
+ i += r.template.length + 1;
314
+ }
315
+
316
+ return segments;
317
+ }
318
+
319
+ function segment(p) {
320
+ let parts = [];
321
+ let index = p;
322
+
323
+ while (index < pattern.length && pattern[index] !== "/") {
324
+ const part = literal(index) || parameter(index);
325
+ parts.push(part);
326
+
327
+ index += part.template.length;
328
+ }
329
+
330
+ return {
331
+ template: pattern.slice(p, index),
332
+ parts: parts
333
+ }
334
+ }
335
+
336
+ function constraints(text, p) {
337
+ const array = [];
338
+
339
+ for (let i = p; i < text.length;) {
340
+ if (text[i] !== ":") {
341
+ throw_error();
342
+ }
343
+
344
+ const name = constraint_name(text.slice(i + 1));
345
+ i += name.length + 1;
346
+
347
+ const argument = text[i] === "("
348
+ ? extract(i, text)
349
+ : null;
350
+
351
+ is_nullish(argument) || (i += argument.length + 2);
352
+
353
+ if (!name && !argument) {
354
+ throw_error();
355
+ }
356
+
357
+ array.push({
358
+ name: name === ""
359
+ ? "regex"
360
+ : name === "="
361
+ ? "default"
362
+ : name,
363
+ argument: argument ?? ""
364
+ });
365
+
366
+ }
367
+
368
+ return array;
369
+ }
370
+
371
+ function parameter(p) {
372
+ if (pattern[p] !== "{") {
373
+ return null;
374
+ }
375
+
376
+ const value = extract(p);
377
+ const param_name = parameter_name(value);
378
+ const template = pattern.slice(p, p + value.length + 2);
379
+ const quantifier = (() => {
380
+ const q = value[param_name.length];
381
+ return q === "*"
382
+ || q === "+"
383
+ || q === "?" ? q : "";
384
+ })();
385
+ const list = constraints(value, param_name.length + quantifier.length);
386
+
387
+ return {
388
+ kind: "parameter",
389
+ template: template,
390
+ name: param_name,
391
+ quantifier: quantifier,
392
+ constraints: list.filter(c => c.name !== "default"),
393
+ default: list.find(c => c.name === "default")?.argument,
394
+ required: quantifier === "+" || quantifier === "",
395
+ optional: quantifier === "?" || quantifier === "*",
396
+ catch_all: quantifier === "+" || quantifier === "*"
397
+ };
398
+ }
399
+
400
+ function literal(p) {
401
+ for (let i = p;; i++) {
402
+ if (i >= pattern.length
403
+ || pattern[i] === "/"
404
+ || pattern[i] === "{") {
405
+ if (i === p) {
406
+ return null;
407
+ }
408
+
409
+ const template = pattern.slice(p, i);
410
+ return {
411
+ kind: "literal",
412
+ template: template,
413
+ value: template
414
+ };
415
+ }
416
+ }
417
+ }
418
+
419
+ function extract(p, s) {
420
+ s ??= pattern;
421
+ const stack = [];
422
+
423
+ loop: for (let i = p; i < s.length; i++) {
424
+ switch (s[i]) {
425
+ case "{": stack.push("}"); break;
426
+ case "(": stack.push(")"); break;
427
+ case "}":
428
+ case ")":
429
+ if (stack.pop() !== s[i]) break loop;
430
+ break;
431
+ }
432
+
433
+ if (stack.length === 0) {
434
+ return s.slice(p + 1, i);
435
+ }
436
+ }
437
+
438
+ throw_error();
439
+ }
440
+
441
+ function parameter_name(value) {
442
+ const r = value.match(/^(?<name>[a-z_$][a-z0-9_$-]*?)(?:[:?+*]|$)/i)?.groups?.name;
443
+ if ((r?.length ?? -1) < 0) {
444
+ throw_error("Invalid parameter name");
445
+ }
446
+ return r;
447
+ }
448
+
449
+ function constraint_name(value) {
450
+ const r = value.match(/^(?<name>=|[a-z0-9_$]*)(?=[/:(]|$)/i)?.groups?.name;
451
+ if ((r?.length ?? -1) < 0) {
452
+ throw_error("Invalid constraint name");
453
+ }
454
+
455
+ return r;
456
+ }
457
+
458
+ function throw_error(message = "Invalid pattern") {
459
+ throw new Error(`${ message }: ${ pattern }`);
460
+ }
461
+ }
462
+
463
+ async function load_template(path) {
464
+ let result;
465
+ try {
466
+ result = await fetch(path);
467
+ }
468
+ catch {
469
+
470
+ }
471
+
472
+ if (!result?.ok) {
473
+ warn(`Failed to load template from ${ path }`);
474
+ return new DocumentFragment();
475
+ }
476
+
477
+ const fragment = new DocumentFragment();
478
+ const document = new DOMParser().parseFromString(await result.text(), "text/html");
479
+
480
+ fragment.append(...document.body.childNodes);
481
+ return fragment;
482
+ }
483
+
484
+ function route({ directive, magic, $data }) {
485
+ directive("route", (el, { expression, value, modifiers }, { cleanup, evaluate }) => {
486
+ if (!is_template(el)) {
487
+ warn("x-route can only be used on a 'template' tag");
488
+ return;
489
+ }
490
+
491
+ const route = closest(el, n => n._b_route)?._b_route;
492
+
493
+ if (is_nullish(route) && (value === "view" || value === "handler")) {
494
+ warn(`no x-route directive found`);
495
+ return;
496
+ }
497
+
498
+ switch (value) {
499
+ case "view":
500
+ process_view();
501
+ break;
502
+
503
+ case "handler":
504
+ process_handler();
505
+ break;
506
+
507
+ default:
508
+ process_route();
509
+ break;
510
+ }
511
+
512
+ function process_route() {
513
+ const router = closest(el, n => n._b_router)?._b_router;
514
+ if (is_nullish(router)) {
515
+ warn(`no x-router directive found`);
516
+ return;
517
+ }
518
+
519
+ const view = () => new Promise(resolve => resolve(el.content));
520
+
521
+ el._b_route = Object.assign(new RoutePattern(expression), { el, view, handler: () => Promise.resolve() });
522
+ router.routes.push(el._b_route);
523
+
524
+ cleanup(() => {
525
+ router.routes = router.routes.filter(r => r !== el._b_route);
526
+ });
527
+ }
528
+
529
+ function process_handler() {
530
+ expression || (expression = "[]");
531
+ expression.startsWith("[") || (expression = `[${ expression }]`);
532
+
533
+ const handlers = evaluate(expression).map(asyncify);
534
+ const self = $data(el);
535
+
536
+ route.handler = async context => {
537
+ for (let handler of handlers) {
538
+ const r = await handler.call(self, context);
539
+ if (!is_nullish(r)) {
540
+ return r;
541
+ }
542
+ }
543
+ };
544
+
545
+ cleanup(() => route.handler = null);
546
+ }
547
+
548
+ function process_view() {
549
+ route.view = () => load_template(expression);
550
+
551
+ cleanup(() => {
552
+ route.view = () => new Promise(resolve => resolve(new DocumentFragment()));
553
+ });
554
+ }
555
+ });
556
+
557
+ magic("route", el => closest(el, n => n._b_router)?._b_router.values);
558
+ }
559
+
560
+ let data;
561
+
562
+ function use_location() {
563
+ assert(Alpine, "Alpine is not defined");
564
+
565
+ if (!data) {
566
+ data = Alpine.reactive({
567
+ hash: "",
568
+ host: "",
569
+ hostname: "",
570
+ href: "",
571
+ origin: "",
572
+ pathname: "",
573
+ port: 0,
574
+ protocol: "",
575
+ search: "",
576
+ refresh() {
577
+ populate();
578
+ }
579
+ });
580
+
581
+ populate();
582
+
583
+ listen(window, "hashchange", populate);
584
+ listen(window, "popstate", populate);
585
+ }
586
+
587
+ return data;
588
+ }
589
+
590
+ function populate() {
591
+ for (let name in data) {
592
+ if (name in location) {
593
+ data[name] = location[name];
594
+ }
595
+ }
596
+ }
597
+
598
+ let location$1;
599
+
600
+ const hash_api = {
601
+ get path() {
602
+ return location$1.hash.slice(1) || "/";
603
+ },
604
+ get "location"() {
605
+ return location$1;
606
+ },
607
+ resolve(path) {
608
+ let url = new URL(path);
609
+ return url.hash ? (url.hash.slice(1) || "/") : url.pathname;
610
+ },
611
+ navigate(path, replace = false) {
612
+ path.indexOf("#") < 0 && (path = "#" + path);
613
+ navigate(path, replace);
614
+ }
615
+ };
616
+
617
+ const html5_api = {
618
+ get path() {
619
+ return location$1.pathname;
620
+ },
621
+ get "location"() {
622
+ return location$1;
623
+ },
624
+ resolve(path) {
625
+ return new URL(path).pathname;
626
+ },
627
+ navigate(path, replace = false) {
628
+ navigate(path, replace);
629
+ }
630
+ };
631
+
632
+ function navigate(path, replace) {
633
+ history[replace ? "replaceState" : "pushState"]({}, "", path);
634
+ location$1.refresh();
635
+ }
636
+
637
+ const known_api = {
638
+ html5: html5_api,
639
+ hash: hash_api,
640
+ };
641
+
642
+ function create_history(name) {
643
+ location$1 ??= use_location();
644
+
645
+ name ||= "html5";
646
+ let api = known_api[name];
647
+
648
+ if (!api) {
649
+ warn(`Unknown history API: '${ name }'`);
650
+ api = html5_api;
651
+ }
652
+
653
+ return api;
654
+ }
655
+
656
+ function create_getter(evaluate_later, ...args) {
657
+ const evaluate = evaluate_later(...args);
658
+ return () => {
659
+ let result;
660
+ evaluate(v => result = v);
661
+ return has_getter(result) ? result.get() : result;
662
+ };
663
+ }
664
+
665
+ function has_getter(value) {
666
+ return typeof value?.get === "function";
667
+ }
668
+
669
+ function watch(get_value, callback, options = null) {
670
+ assert(Alpine, "Alpine is not defined");
671
+
672
+ const {
673
+ effect,
674
+ release
675
+ } = Alpine;
676
+
677
+ let new_value;
678
+ let old_value;
679
+ let initialized = false;
680
+
681
+ const handle = effect(() => {
682
+ new_value = get_value();
683
+
684
+ if (!initialized) {
685
+ options?.deep && JSON.stringify(new_value);
686
+ old_value = new_value;
687
+ }
688
+
689
+ if (initialized || (options?.immediate ?? true)) {
690
+
691
+ setTimeout(() => {
692
+ callback(new_value, old_value);
693
+ old_value = new_value;
694
+ }, 0);
695
+ }
696
+
697
+ initialized = true;
698
+ });
699
+
700
+ return () => release(handle);
701
+ }
702
+
703
+ function router({ directive, magic, reactive }) {
704
+ directive("router", (el, { expression, value }, { cleanup, effect, evaluate, evaluateLater: evaluate_later }) => {
705
+ value || (value = "html5");
706
+
707
+ const router = closest(el, node => node._b_router)?._b_router;
708
+
709
+ if (is_nullish(router) && (value === "outlet" || value === "link")) {
710
+ warn(`no x-router directive found`);
711
+ return;
712
+ }
713
+
714
+ switch (value) {
715
+ case "outlet":
716
+ process_outlet();
717
+ break;
718
+
719
+ case "link":
720
+ process_link();
721
+ break;
722
+
723
+ default:
724
+ process_router();
725
+ break;
726
+ }
727
+
728
+ function process_router() {
729
+ if (is_template(el)) {
730
+ warn("x-router cannot be used on a 'template' tag");
731
+ return;
732
+ }
733
+
734
+ const values = reactive({
735
+ pattern: "",
736
+ path: "",
737
+ params: ""
738
+ });
739
+
740
+ const api = is_nullish(value) && expression
741
+ ? evaluate(expression)
742
+ : create_history(value);
743
+
744
+ const router = {
745
+ routes: [],
746
+ outlet: null,
747
+ active: null,
748
+ history: api,
749
+ values: values,
750
+ async match(path) {
751
+ for (let route of this.routes) {
752
+ const params = route.match(path);
753
+ if (params) {
754
+ const context = { router, route, params, path };
755
+ if (await route.handler(context) !== false) {
756
+ return context;
757
+ }
758
+ }
759
+ }
760
+ },
761
+ navigate(path, replace = false) {
762
+ api.navigate(path, replace);
763
+ return true;
764
+ }
765
+ };
766
+
767
+ el._b_router = router;
768
+
769
+ function activate(route, path, params) {
770
+ if (route.nodes?.length && values.path === path) {
771
+ return;
772
+ }
773
+
774
+ clear();
775
+
776
+ values.path = path;
777
+ values.pattern = route.template;
778
+ values.params = params;
779
+
780
+ router.active = route;
781
+
782
+ const outlet = router.outlet;
783
+ if (outlet) {
784
+ route.view().then(html => {
785
+ if (values.path !== path
786
+ || values.pattern !== route.template
787
+ || JSON.stringify(values.params) !== JSON.stringify(params)) {
788
+ return;
789
+ }
790
+
791
+ route.nodes = [...html.cloneNode(true).childNodes];
792
+ is_template(outlet)
793
+ ? route.nodes.forEach(node => outlet.parentElement.insertBefore(node, outlet))
794
+ : route.nodes.forEach(node => outlet.append(node));
795
+ });
796
+ }
797
+ }
798
+
799
+ function clear() {
800
+ if (router.active) {
801
+ for (let n of router.active.nodes ?? []) {
802
+ n.remove();
803
+ }
804
+ router.active.nodes = null;
805
+ router.active = null;
806
+ }
807
+ }
808
+
809
+ const dispose = watch(() => api.path, async path => {
810
+ const result = await router.match(path);
811
+
812
+ if (result) {
813
+ if (path === api.path) {
814
+ activate(result.route, result.path, result.params);
815
+ }
816
+ }
817
+ else {
818
+ clear();
819
+ }
820
+ });
821
+
822
+ cleanup(dispose);
823
+ cleanup(clear);
824
+ }
825
+
826
+ function process_link() {
827
+ const is_blank = (el.getAttribute("target") ?? "").indexOf("_blank") >= 0;
828
+ const unsubscribe = listen(el, "click", e => {
829
+ if (e.metaKey
830
+ || e.altKey
831
+ || e.ctrlKey
832
+ || e.shiftKey
833
+ || e.defaultPrevented
834
+ || e.button > 0
835
+ || is_blank) {
836
+ return;
837
+ }
838
+
839
+ e.preventDefault();
840
+
841
+ router.navigate(`${ el.pathname }${ el.search }${ el.hash }`);
842
+ });
843
+
844
+ if (expression) {
845
+ const is_active = create_getter(evaluate_later, "$active");
846
+ const list = as_array(evaluate(expression));
847
+
848
+ effect(() => {
849
+ const active = is_active();
850
+ for (let name of list) {
851
+ el.classList.toggle(name, active);
852
+ }
853
+ });
854
+
855
+ cleanup(() => el.classList.remove(...list));
856
+ }
857
+
858
+ cleanup(unsubscribe);
859
+ }
860
+
861
+ function process_outlet() {
862
+ router.outlet && warn("x-router:outlet already specified", router.outlet);
863
+ router.outlet || (router.outlet = el);
864
+ cleanup(() => router.outlet = null);
865
+ }
866
+ });
867
+
868
+ magic("router", el => closest(el, n => n._b_router)?._b_router);
869
+
870
+ magic("active", el => {
871
+ const router = closest(el, node => node._b_router)?._b_router;
872
+ if (is_nullish(router)) {
873
+ warn("No x-router directive found");
874
+ return;
875
+ }
876
+
877
+ return router.history.resolve(el.href) === router.values.path;
878
+ });
879
+ }
880
+
881
+ function plugin(alpine) {
882
+ window.RoutePattern = RoutePattern;
883
+ alpine.plugin([router, route]);
884
+ }
885
+
886
+ document.addEventListener("alpine:init", () => { Alpine.plugin(plugin); });
887
+
888
+ })();