@ramstack/alpinegear-router 1.1.0-preview.6 → 1.1.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/README.md CHANGED
@@ -56,7 +56,7 @@ Alpine.start();
56
56
 
57
57
  <nav>
58
58
  <a x-router:link href="/">Home</a>
59
- <a x-router:link href="/about">About</a>
59
+ <a x-router:link.replace href="/about">About</a>
60
60
  </nav>
61
61
 
62
62
  <!-- Render the matching route -->
@@ -172,6 +172,27 @@ In this case:
172
172
  * `x-router:link` will locate the nested `<a>` element within the parent and use its `href` for routing.
173
173
  * The `$active` state will correctly reflect whether the nested link's `href` matches the current route.
174
174
 
175
+ ### Modifier `replace`
176
+
177
+ The `replace` modifier changes the default navigation behavior of the router. When a link with this modifier is clicked,
178
+ it triggers `$router.navigate(href, /* replace */ true)`.
179
+
180
+ Unlike the default behavior, which adds a new entry to the browser's history stack, this option replaces
181
+ the current history entry with the new URL. As a result, the user's navigation history remains unchanged,
182
+ and pressing the **"Back"** button will skip over the replaced entry.
183
+
184
+ ```html
185
+ <div x-router:hash>
186
+ ...
187
+ <nav>
188
+ <a x-router:link href="/">Home</a>
189
+ <!-- The "replace" modifier ensures that clicking this link
190
+ replaces the current history entry instead of adding a new one -->
191
+ <a x-router:link.replace href="/about">About</a>
192
+ </nav>
193
+ </div>
194
+ ```
195
+
175
196
  ## Inline and External templates
176
197
  Routes can be defined using either **inline templates** or **external templates**:
177
198
 
@@ -4,6 +4,7 @@ const is_nullish = value => value === null || value === undefined;
4
4
  const is_template = el => el instanceof HTMLTemplateElement;
5
5
  const is_function = value => typeof value === "function";
6
6
  const as_array = value => is_array(value) ? value : [value];
7
+ const has_modifier = (modifiers, modifier) => modifiers.includes(modifier);
7
8
 
8
9
  function assert(value, message) {
9
10
  if (!value) {
@@ -674,7 +675,7 @@ function watch(get_value, callback, options = null) {
674
675
  }
675
676
 
676
677
  function router({ $data, addScopeToNode, directive, magic, reactive }) {
677
- directive("router", (el, { value }, { cleanup }) => {
678
+ directive("router", (el, { modifiers, value }, { cleanup }) => {
678
679
  value || (value = "html5");
679
680
 
680
681
  const router = $data(el).$router;
@@ -814,7 +815,7 @@ function router({ $data, addScopeToNode, directive, magic, reactive }) {
814
815
 
815
816
  e.preventDefault();
816
817
 
817
- router.navigate(`${ link.pathname }${ link.search }${ link.hash }`);
818
+ router.navigate(`${ link.pathname }${ link.search }${ link.hash }`, has_modifier(modifiers, "replace"));
818
819
  });
819
820
 
820
821
  cleanup(unsubscribe);
@@ -839,7 +840,7 @@ function router({ $data, addScopeToNode, directive, magic, reactive }) {
839
840
 
840
841
  magic("active", el => {
841
842
  const router = $data(el).$router;
842
- if (is_nullish(router)) {
843
+ if (!router) {
843
844
  warn("No x-router directive found");
844
845
  return false;
845
846
  }
@@ -1 +1 @@
1
- const e=(...e)=>console.warn("alpine-gear.js:",...e),t=Array.isArray,n=e=>null==e,r=e=>e instanceof HTMLTemplateElement,a=e=>"function"==typeof e,o=e=>t(e)?e:[e],i=e=>a(e)&&"AsyncFunction"===e.constructor?.name?e:function(...t){const n=e.apply(this,t);return a(n?.then)?n:Promise.resolve(n)},l=(e,t,n,r)=>(e.addEventListener(t,n,r),()=>e.removeEventListener(t,n,r)),s=Object.freeze({regex(e){const t=new RegExp(e);return{test:e=>t.test(e)}},bool(){return{test:e=>/^(?:true|false)$/i.test(e),transform:e=>4===e.length}},int(){return{test:e=>/^-?\d+$/.test(e),transform:e=>+e}},number(){return{test:e=>/^[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?$/.test(e)&&isFinite(parseFloat(e)),transform:e=>parseFloat(e)}},alpha(){return{test:e=>/^[a-z]+$/i.test(e)}},min(e){return{test:t=>t>=+e}},max(e){return{test:t=>+e>=t}},range(e){let[t,n]=e.split(",",2).map((e=>e.trim()));return{test:e=>e>=+t&&+n>=e}},length(e){let t=e.split(",").map((e=>e.trim()));return{test:2==t.length?e=>e.length>=+t[0]&&+t[1]>=e.length:e=>e.length===+t[0]}},minlength(e){return{test:t=>t.length>=+e}},maxlength(e){return{test:t=>+e>=t.length}}});class u{#e;#t;#n;#r;#a;get template(){return this.#t}get regex(){return this.#e}get constraints(){return this.#a}constructor(e,t=null){this.#t=e,this.#e=function(e,t,r,a){t.push(...function(e,t){return function(e){e.find((e=>e.parts.length>1&&e.parts.every((e=>e.optional))))&&u("Using all segment parameters as optional is not permitted");const r=new Map;return e.flatMap((e=>e.parts)).forEach(((e,a,o)=>{if("literal"!==e.kind||0>e.value.indexOf("?")||u("Literal segments cannot contain the '?' character"),"parameter"===e.kind){e.catch_all&&a!==o.length-1&&u("A catch-all parameter can only appear as the last segment"),r.has(e.name)&&u(`The route parameter name '${e.name}' appears more than one time`),"*"===e.quantifier&&n(e.default)&&(e.default=""),""===e.default&&"*"!==e.quantifier&&(e.default=null),r.set(e.name,!0);for(let r of e.constraints){const e=t?.[r.name]??s[r.name];n(e)&&u(`Unknown constraint '${r.name}'`),Object.assign(r,e(r.argument))}}})),e}(function(){const t=[];for(let n=0;e.length>n;){const e=r(n);e.template&&t.push(e),n+=e.template.length+1}return t}());function r(t){let n=[],r=t;for(;e.length>r&&"/"!==e[r];){const e=o(r)||a(r);n.push(e),r+=e.template.length}return{template:e.slice(t,r),parts:n}}function a(t){if("{"!==e[t])return null;const r=i(t),a=e.slice(t,t+r.length+2),o=function(e){const t=e.match(/^(?<name>[a-z_$][a-z0-9_$-]*?)(?:[:?+*]|$)/i)?.groups?.name;return n(t)&&u("Invalid parameter name"),t}(r),s=/[*+?]/.exec(r[o.length])?.[0]??"",c=function(e,t){const r=[];for(let a=t;e.length>a;){":"!==e[a]&&u();const t=l(e.slice(a+1));a+=t.length+1;const o="("===e[a]?i(a,e):null;n(o)||(a+=o.length+2),t||o||u(),r.push({name:"="===t?"default":t||"regex",argument:o??""})}return r}(r,o.length+s.length);return{name:o,kind:"parameter",template:a,quantifier:s,constraints:c.filter((e=>"default"!==e.name)),default:c.find((e=>"default"===e.name))?.argument,required:"+"===s||""===s,optional:"?"===s||"*"===s,catch_all:"+"===s||"*"===s}}function o(t){for(let n=t;;n++)if(n>=e.length||"/"===e[n]||"{"===e[n]){if(n===t)return null;const r=e.slice(t,n);return{kind:"literal",template:r,value:r}}}function i(t,n){n??=e;const r=[];e:for(let e=t;n.length>e;e++){switch(n[e]){case"{":r.push("}");break;case"(":r.push(")");break;case"}":case")":if(r.pop()!==n[e])break e}if(0===r.length)return n.slice(t+1,e)}u()}function l(e){const t=e.match(/^(?<name>=|[a-z0-9_$]*)(?=[/:(]|$)/i)?.groups?.name;return n(t)&&u("Invalid constraint name"),t}function u(t="Invalid pattern"){throw new Error(`${t}: ${e}`)}}(e,a));let o=t.map((e=>e.parts.map(((t,n)=>{if("literal"===t.kind)return n?t.value:`/${t.value}`;if(r.set(t.name,t),1===e.parts.length&&"?"===t.quantifier)return`(?:/(?<${t.name}>[^/]+?))?`;if(t.catch_all){let e=`(?<${t.name}>.${t.quantifier})`;return n||(e=`(?:/${e})`),"*"===t.quantifier&&(e+="?"),"*"===t.quantifier?e+"?":e}{const e=`(?<${t.name}>[^/]+?)${t.quantifier}`;return n?e:`/${e}`}})).join(""))).join("")||"/";return"/"!==o&&(o+="/?"),new RegExp(`^${o}$`)}(e,this.#n=[],this.#r=new Map,this.#a=t??{})}match(e){let t=this.#e.exec(e);if(n(t))return null;t=t.groups??{};for(let[e,r]of this.#r.entries()){let a=t[e];if(n(a)&&n(r.default))continue;a||n(r.default)||(a=r.default);const o=r.catch_all?a.split("/").filter((e=>e.length)):[a];for(let e=0;o.length>e;e++)for(let t of r.constraints){if(t.test&&!t.test(o[e]))return null;t.transform&&(o[e]=t.transform(o[e]))}t[e]=r.catch_all?o:o[0]}return t}resolve(e){e=new Map(Object.entries(e));const t=[];for(let r of this.#n){const a=[];for(let t of r.parts)if("literal"===t.kind)a.push(t.value);else{let r=e.get(t.name);if(e.delete(t.name),(n(r)||""===r)&&(r=this.#r.get(t.name)?.default,t.catch_all&&r&&(r=r.split("/"))),n(r)||""===r){if(t.required)return null;if(t.optional&&t.default===r)continue}t.catch_all?(r=o(r),a.push(...r.map((e=>encodeURIComponent(e))).join("/"))):a.push(encodeURIComponent(r))}a.length&&t.push(a.join(""))}let r=[...e.entries()].map((([e,t])=>encodeURIComponent(e)+"="+encodeURIComponent(t))).join("&");r&&(r="?"+r);const a=t.join("/")+r;return"/"!==a[0]?"/"+a:a}}function c({directive:t,$data:a}){t("route",((t,{expression:o,value:l},{cleanup:s,evaluate:c})=>{if(!r(t))return void e("x-route can only be used on a 'template' tag");const f=t._r_route;if(f||"view"!==l&&"handler"!==l)switch(l){case"view":f.view=()=>async function(t){let n;try{n=await fetch(t)}catch{}if(!n?.ok)return e(`Failed to load template from ${t}`),new DocumentFragment;const r=new DocumentFragment,a=(new DOMParser).parseFromString(await n.text(),"text/html");return r.append(...a.body.childNodes),r}(o),s((()=>{f.view=()=>new Promise((e=>e(new DocumentFragment)))}));break;case"handler":!function(){o||(o="[]"),o.startsWith("[")||(o=`[${o}]`);const e=c(o).map(i),r=a(t);f.handler=async t=>{for(let a of e){const e=await a.call(r,t);if(!n(e))return e}},s((()=>f.handler=null))}();break;default:!function(){const n=a(t)?.$router;if(n){const e=()=>new Promise((e=>e(t.content)));t._r_route=Object.assign(new u(o),{el:t,view:e,handler:()=>Promise.resolve()}),n.routes.push(t._r_route),s((()=>n.routes=n.routes.filter((e=>e!==t._r_route))))}else e("no x-router directive found")}()}else e("no x-route directive found")}))}let f,h;function p(){for(let e in f)e in location&&(f[e]=location[e])}const m={get path(){return h.hash.slice(1)||"/"},get location(){return h},resolve(e){let t=new URL(e);return t.hash?t.hash.slice(1)||"/":t.pathname},navigate(e,t=!1){0>e.indexOf("#")&&(e="#"+e),g(e,t)}},d={get path(){return h.pathname},get location(){return h},resolve(e){return new URL(e).pathname},navigate(e,t=!1){g(e,t)}};function g(e,t){history[t?"replaceState":"pushState"]({},"",e),h.refresh()}const v={html5:d,hash:m};function w({$data:t,addScopeToNode:a,directive:o,magic:i,reactive:s}){o("router",((n,{value:o},{cleanup:i})=>{o||(o="html5");const u=t(n).$router;if(u||"outlet"!==o&&"link"!==o)switch(o){case"outlet":u.outlet?e("x-router:outlet already specified",u.outlet,n):(u.outlet=n,i((()=>u.outlet=null)));break;case"link":!function(){let t=function(t){if($(t))return t;const n=t.querySelectorAll("a");return 1!==n.length&&e(`Expected exactly one link, but found ${n.length}`),n[0]}(n);if(t){n._r_routerlink=t;const e=(t.getAttribute("target")??"").indexOf("_blank")>=0,r=l(t,"click",(n=>{n.metaKey||n.altKey||n.ctrlKey||n.shiftKey||n.defaultPrevented||n.button>0||e||(n.preventDefault(),u.navigate(`${t.pathname}${t.search}${t.hash}`))}));i(r)}}();break;default:!function(){if(r(n))return void e("x-router cannot be used on a 'template' tag");const t=s({pattern:"",path:"",params:{}}),u=function(t){h??=(f||(f=Alpine.reactive({hash:"",host:"",hostname:"",href:"",origin:"",pathname:"",port:0,protocol:"",search:"",refresh(){p()}}),p(),l(window,"hashchange",p),l(window,"popstate",p)),f),t||="html5";let n=v[t];return n||(e(`Unknown history API: ${t}`),n=d),n}(o),c={routes:[],outlet:null,active:null,history:u,values:t,async match(e){for(let t of this.routes){const n=t.match(e);if(n){const r={router:c,route:t,params:n,path:e};if(!1!==await t.handler(r))return r}}},navigate(e,t=!1){return u.navigate(e,t),!0}};function m(){if(c.active){for(let e of c.active.nodes??[])e.remove();c.active.nodes=null,c.active=null}}a(n,{$route:t,$router:c});const g=function(e,t,n=null){const{effect:r,release:a}=Alpine;let o,i,l=!1;const s=r((()=>{o=e(),l||(n?.deep&&JSON.stringify(o),i=o),(l||(n?.immediate??1))&&setTimeout((()=>{t(o,i),i=o}),0),l=!0}));return()=>a(s)}((()=>u.path),(async e=>{const n=await c.match(e);n?e===u.path&&function(e,n,a){if(e.nodes?.length&&t.path===n)return;m(),t.path=n,t.pattern=e.template,t.params=a??{},c.active=e;const o=c.outlet;o&&e.view().then((i=>{t.path===n&&t.pattern===e.template&&JSON.stringify(t.params)===JSON.stringify(a)&&(e.nodes=[...i.cloneNode(!0).childNodes],r(o)?e.nodes.forEach((e=>o.parentElement.insertBefore(e,o))):e.nodes.forEach((e=>o.append(e))))}))}(n.route,n.path,n.params):m()}));i(g),i(m)}()}else e("no x-router directive found")})),i("active",(r=>{const a=t(r).$router;if(n(a))return e("No x-router directive found"),!1;JSON.stringify(a.values);const o=$(r)?r:(e=>{for(;e&&!e._r_routerlink;)e=(e._x_teleportBack??e).parentElement;return e})(r)?._r_routerlink;return o?a.history.resolve(o.href)===a.values.path:(r._r_routerlink_init?e("x-router:link directive not found",r):queueMicrotask((()=>{r._r_routerlink_init=!0,a.values.path=a.values.path})),!1)}))}function $(e){return"A"===e.tagName.toUpperCase()}function _(e){window.RoutePattern=u,e.plugin([w,c])}export{u as RoutePattern,_ as router};
1
+ const e=(...e)=>console.warn("alpine-gear.js:",...e),t=Array.isArray,n=e=>null==e,r=e=>e instanceof HTMLTemplateElement,a=e=>"function"==typeof e,o=e=>t(e)?e:[e],i=e=>a(e)&&"AsyncFunction"===e.constructor?.name?e:function(...t){const n=e.apply(this,t);return a(n?.then)?n:Promise.resolve(n)},l=(e,t,n,r)=>(e.addEventListener(t,n,r),()=>e.removeEventListener(t,n,r)),s=Object.freeze({regex(e){const t=new RegExp(e);return{test:e=>t.test(e)}},bool(){return{test:e=>/^(?:true|false)$/i.test(e),transform:e=>4===e.length}},int(){return{test:e=>/^-?\d+$/.test(e),transform:e=>+e}},number(){return{test:e=>/^[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?$/.test(e)&&isFinite(parseFloat(e)),transform:e=>parseFloat(e)}},alpha(){return{test:e=>/^[a-z]+$/i.test(e)}},min(e){return{test:t=>t>=+e}},max(e){return{test:t=>+e>=t}},range(e){let[t,n]=e.split(",",2).map((e=>e.trim()));return{test:e=>e>=+t&&+n>=e}},length(e){let t=e.split(",").map((e=>e.trim()));return{test:2==t.length?e=>e.length>=+t[0]&&+t[1]>=e.length:e=>e.length===+t[0]}},minlength(e){return{test:t=>t.length>=+e}},maxlength(e){return{test:t=>+e>=t.length}}});class u{#e;#t;#n;#r;#a;get template(){return this.#t}get regex(){return this.#e}get constraints(){return this.#a}constructor(e,t=null){this.#t=e,this.#e=function(e,t,r,a){t.push(...function(e,t){return function(e){e.find((e=>e.parts.length>1&&e.parts.every((e=>e.optional))))&&u("Using all segment parameters as optional is not permitted");const r=new Map;return e.flatMap((e=>e.parts)).forEach(((e,a,o)=>{if("literal"!==e.kind||0>e.value.indexOf("?")||u("Literal segments cannot contain the '?' character"),"parameter"===e.kind){e.catch_all&&a!==o.length-1&&u("A catch-all parameter can only appear as the last segment"),r.has(e.name)&&u(`The route parameter name '${e.name}' appears more than one time`),"*"===e.quantifier&&n(e.default)&&(e.default=""),""===e.default&&"*"!==e.quantifier&&(e.default=null),r.set(e.name,!0);for(let r of e.constraints){const e=t?.[r.name]??s[r.name];n(e)&&u(`Unknown constraint '${r.name}'`),Object.assign(r,e(r.argument))}}})),e}(function(){const t=[];for(let n=0;e.length>n;){const e=r(n);e.template&&t.push(e),n+=e.template.length+1}return t}());function r(t){let n=[],r=t;for(;e.length>r&&"/"!==e[r];){const e=o(r)||a(r);n.push(e),r+=e.template.length}return{template:e.slice(t,r),parts:n}}function a(t){if("{"!==e[t])return null;const r=i(t),a=e.slice(t,t+r.length+2),o=function(e){const t=e.match(/^(?<name>[a-z_$][a-z0-9_$-]*?)(?:[:?+*]|$)/i)?.groups?.name;return n(t)&&u("Invalid parameter name"),t}(r),s=/[*+?]/.exec(r[o.length])?.[0]??"",c=function(e,t){const r=[];for(let a=t;e.length>a;){":"!==e[a]&&u();const t=l(e.slice(a+1));a+=t.length+1;const o="("===e[a]?i(a,e):null;n(o)||(a+=o.length+2),t||o||u(),r.push({name:"="===t?"default":t||"regex",argument:o??""})}return r}(r,o.length+s.length);return{name:o,kind:"parameter",template:a,quantifier:s,constraints:c.filter((e=>"default"!==e.name)),default:c.find((e=>"default"===e.name))?.argument,required:"+"===s||""===s,optional:"?"===s||"*"===s,catch_all:"+"===s||"*"===s}}function o(t){for(let n=t;;n++)if(n>=e.length||"/"===e[n]||"{"===e[n]){if(n===t)return null;const r=e.slice(t,n);return{kind:"literal",template:r,value:r}}}function i(t,n){n??=e;const r=[];e:for(let e=t;n.length>e;e++){switch(n[e]){case"{":r.push("}");break;case"(":r.push(")");break;case"}":case")":if(r.pop()!==n[e])break e}if(0===r.length)return n.slice(t+1,e)}u()}function l(e){const t=e.match(/^(?<name>=|[a-z0-9_$]*)(?=[/:(]|$)/i)?.groups?.name;return n(t)&&u("Invalid constraint name"),t}function u(t="Invalid pattern"){throw new Error(`${t}: ${e}`)}}(e,a));let o=t.map((e=>e.parts.map(((t,n)=>{if("literal"===t.kind)return n?t.value:`/${t.value}`;if(r.set(t.name,t),1===e.parts.length&&"?"===t.quantifier)return`(?:/(?<${t.name}>[^/]+?))?`;if(t.catch_all){let e=`(?<${t.name}>.${t.quantifier})`;return n||(e=`(?:/${e})`),"*"===t.quantifier&&(e+="?"),"*"===t.quantifier?e+"?":e}{const e=`(?<${t.name}>[^/]+?)${t.quantifier}`;return n?e:`/${e}`}})).join(""))).join("")||"/";return"/"!==o&&(o+="/?"),new RegExp(`^${o}$`)}(e,this.#n=[],this.#r=new Map,this.#a=t??{})}match(e){let t=this.#e.exec(e);if(n(t))return null;t=t.groups??{};for(let[e,r]of this.#r.entries()){let a=t[e];if(n(a)&&n(r.default))continue;a||n(r.default)||(a=r.default);const o=r.catch_all?a.split("/").filter((e=>e.length)):[a];for(let e=0;o.length>e;e++)for(let t of r.constraints){if(t.test&&!t.test(o[e]))return null;t.transform&&(o[e]=t.transform(o[e]))}t[e]=r.catch_all?o:o[0]}return t}resolve(e){e=new Map(Object.entries(e));const t=[];for(let r of this.#n){const a=[];for(let t of r.parts)if("literal"===t.kind)a.push(t.value);else{let r=e.get(t.name);if(e.delete(t.name),(n(r)||""===r)&&(r=this.#r.get(t.name)?.default,t.catch_all&&r&&(r=r.split("/"))),n(r)||""===r){if(t.required)return null;if(t.optional&&t.default===r)continue}t.catch_all?(r=o(r),a.push(...r.map((e=>encodeURIComponent(e))).join("/"))):a.push(encodeURIComponent(r))}a.length&&t.push(a.join(""))}let r=[...e.entries()].map((([e,t])=>encodeURIComponent(e)+"="+encodeURIComponent(t))).join("&");r&&(r="?"+r);const a=t.join("/")+r;return"/"!==a[0]?"/"+a:a}}function c({directive:t,$data:a}){t("route",((t,{expression:o,value:l},{cleanup:s,evaluate:c})=>{if(!r(t))return void e("x-route can only be used on a 'template' tag");const f=t._r_route;if(f||"view"!==l&&"handler"!==l)switch(l){case"view":f.view=()=>async function(t){let n;try{n=await fetch(t)}catch{}if(!n?.ok)return e(`Failed to load template from ${t}`),new DocumentFragment;const r=new DocumentFragment,a=(new DOMParser).parseFromString(await n.text(),"text/html");return r.append(...a.body.childNodes),r}(o),s((()=>{f.view=()=>new Promise((e=>e(new DocumentFragment)))}));break;case"handler":!function(){o||(o="[]"),o.startsWith("[")||(o=`[${o}]`);const e=c(o).map(i),r=a(t);f.handler=async t=>{for(let a of e){const e=await a.call(r,t);if(!n(e))return e}},s((()=>f.handler=null))}();break;default:!function(){const n=a(t)?.$router;if(n){const e=()=>new Promise((e=>e(t.content)));t._r_route=Object.assign(new u(o),{el:t,view:e,handler:()=>Promise.resolve()}),n.routes.push(t._r_route),s((()=>n.routes=n.routes.filter((e=>e!==t._r_route))))}else e("no x-router directive found")}()}else e("no x-route directive found")}))}let f,h;function p(){for(let e in f)e in location&&(f[e]=location[e])}const m={get path(){return h.hash.slice(1)||"/"},get location(){return h},resolve(e){let t=new URL(e);return t.hash?t.hash.slice(1)||"/":t.pathname},navigate(e,t=!1){0>e.indexOf("#")&&(e="#"+e),g(e,t)}},d={get path(){return h.pathname},get location(){return h},resolve(e){return new URL(e).pathname},navigate(e,t=!1){g(e,t)}};function g(e,t){history[t?"replaceState":"pushState"]({},"",e),h.refresh()}const v={html5:d,hash:m};function w({$data:t,addScopeToNode:n,directive:a,magic:o,reactive:i}){a("router",((a,{modifiers:o,value:s},{cleanup:u})=>{s||(s="html5");const c=t(a).$router;if(c||"outlet"!==s&&"link"!==s)switch(s){case"outlet":c.outlet?e("x-router:outlet already specified",c.outlet,a):(c.outlet=a,u((()=>c.outlet=null)));break;case"link":!function(){let t=function(t){if($(t))return t;const n=t.querySelectorAll("a");return 1!==n.length&&e(`Expected exactly one link, but found ${n.length}`),n[0]}(a);if(t){a._r_routerlink=t;const e=(t.getAttribute("target")??"").indexOf("_blank")>=0,n=l(t,"click",(n=>{n.metaKey||n.altKey||n.ctrlKey||n.shiftKey||n.defaultPrevented||n.button>0||e||(n.preventDefault(),c.navigate(`${t.pathname}${t.search}${t.hash}`,(e=>e.includes("replace"))(o)))}));u(n)}}();break;default:!function(){if(r(a))return void e("x-router cannot be used on a 'template' tag");const t=i({pattern:"",path:"",params:{}}),o=function(t){h??=(f||(f=Alpine.reactive({hash:"",host:"",hostname:"",href:"",origin:"",pathname:"",port:0,protocol:"",search:"",refresh(){p()}}),p(),l(window,"hashchange",p),l(window,"popstate",p)),f),t||="html5";let n=v[t];return n||(e(`Unknown history API: ${t}`),n=d),n}(s),c={routes:[],outlet:null,active:null,history:o,values:t,async match(e){for(let t of this.routes){const n=t.match(e);if(n){const r={router:c,route:t,params:n,path:e};if(!1!==await t.handler(r))return r}}},navigate(e,t=!1){return o.navigate(e,t),!0}};function m(){if(c.active){for(let e of c.active.nodes??[])e.remove();c.active.nodes=null,c.active=null}}n(a,{$route:t,$router:c});const g=function(e,t,n=null){const{effect:r,release:a}=Alpine;let o,i,l=!1;const s=r((()=>{o=e(),l||(n?.deep&&JSON.stringify(o),i=o),(l||(n?.immediate??1))&&setTimeout((()=>{t(o,i),i=o}),0),l=!0}));return()=>a(s)}((()=>o.path),(async e=>{const n=await c.match(e);n?e===o.path&&function(e,n,a){if(e.nodes?.length&&t.path===n)return;m(),t.path=n,t.pattern=e.template,t.params=a??{},c.active=e;const o=c.outlet;o&&e.view().then((i=>{t.path===n&&t.pattern===e.template&&JSON.stringify(t.params)===JSON.stringify(a)&&(e.nodes=[...i.cloneNode(!0).childNodes],r(o)?e.nodes.forEach((e=>o.parentElement.insertBefore(e,o))):e.nodes.forEach((e=>o.append(e))))}))}(n.route,n.path,n.params):m()}));u(g),u(m)}()}else e("no x-router directive found")})),o("active",(n=>{const r=t(n).$router;if(!r)return e("No x-router directive found"),!1;JSON.stringify(r.values);const a=$(n)?n:(e=>{for(;e&&!e._r_routerlink;)e=(e._x_teleportBack??e).parentElement;return e})(n)?._r_routerlink;return a?r.history.resolve(a.href)===r.values.path:(n._r_routerlink_init?e("x-router:link directive not found",n):queueMicrotask((()=>{n._r_routerlink_init=!0,r.values.path=r.values.path})),!1)}))}function $(e){return"A"===e.tagName.toUpperCase()}function _(e){window.RoutePattern=u,e.plugin([w,c])}export{u as RoutePattern,_ as router};
@@ -7,6 +7,7 @@
7
7
  const is_template = el => el instanceof HTMLTemplateElement;
8
8
  const is_function = value => typeof value === "function";
9
9
  const as_array = value => is_array(value) ? value : [value];
10
+ const has_modifier = (modifiers, modifier) => modifiers.includes(modifier);
10
11
 
11
12
  function assert(value, message) {
12
13
  if (!value) {
@@ -677,7 +678,7 @@
677
678
  }
678
679
 
679
680
  function router({ $data, addScopeToNode, directive, magic, reactive }) {
680
- directive("router", (el, { value }, { cleanup }) => {
681
+ directive("router", (el, { modifiers, value }, { cleanup }) => {
681
682
  value || (value = "html5");
682
683
 
683
684
  const router = $data(el).$router;
@@ -817,7 +818,7 @@
817
818
 
818
819
  e.preventDefault();
819
820
 
820
- router.navigate(`${ link.pathname }${ link.search }${ link.hash }`);
821
+ router.navigate(`${ link.pathname }${ link.search }${ link.hash }`, has_modifier(modifiers, "replace"));
821
822
  });
822
823
 
823
824
  cleanup(unsubscribe);
@@ -842,7 +843,7 @@
842
843
 
843
844
  magic("active", el => {
844
845
  const router = $data(el).$router;
845
- if (is_nullish(router)) {
846
+ if (!router) {
846
847
  warn("No x-router directive found");
847
848
  return false;
848
849
  }
@@ -1 +1 @@
1
- !function(){"use strict";const e=(...e)=>console.warn("alpine-gear.js:",...e),t=Array.isArray,n=e=>null==e,r=e=>e instanceof HTMLTemplateElement,a=e=>"function"==typeof e,o=e=>t(e)?e:[e],i=e=>a(e)&&"AsyncFunction"===e.constructor?.name?e:function(...t){const n=e.apply(this,t);return a(n?.then)?n:Promise.resolve(n)},l=(e,t,n,r)=>(e.addEventListener(t,n,r),()=>e.removeEventListener(t,n,r)),s=Object.freeze({regex(e){const t=new RegExp(e);return{test:e=>t.test(e)}},bool(){return{test:e=>/^(?:true|false)$/i.test(e),transform:e=>4===e.length}},int(){return{test:e=>/^-?\d+$/.test(e),transform:e=>+e}},number(){return{test:e=>/^[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?$/.test(e)&&isFinite(parseFloat(e)),transform:e=>parseFloat(e)}},alpha(){return{test:e=>/^[a-z]+$/i.test(e)}},min(e){return{test:t=>t>=+e}},max(e){return{test:t=>+e>=t}},range(e){let[t,n]=e.split(",",2).map((e=>e.trim()));return{test:e=>e>=+t&&+n>=e}},length(e){let t=e.split(",").map((e=>e.trim()));return{test:2==t.length?e=>e.length>=+t[0]&&+t[1]>=e.length:e=>e.length===+t[0]}},minlength(e){return{test:t=>t.length>=+e}},maxlength(e){return{test:t=>+e>=t.length}}});class u{#e;#t;#n;#r;#a;get template(){return this.#t}get regex(){return this.#e}get constraints(){return this.#a}constructor(e,t=null){this.#t=e,this.#e=function(e,t,r,a){t.push(...function(e,t){return function(e){e.find((e=>e.parts.length>1&&e.parts.every((e=>e.optional))))&&u("Using all segment parameters as optional is not permitted");const r=new Map;return e.flatMap((e=>e.parts)).forEach(((e,a,o)=>{if("literal"!==e.kind||0>e.value.indexOf("?")||u("Literal segments cannot contain the '?' character"),"parameter"===e.kind){e.catch_all&&a!==o.length-1&&u("A catch-all parameter can only appear as the last segment"),r.has(e.name)&&u(`The route parameter name '${e.name}' appears more than one time`),"*"===e.quantifier&&n(e.default)&&(e.default=""),""===e.default&&"*"!==e.quantifier&&(e.default=null),r.set(e.name,!0);for(let r of e.constraints){const e=t?.[r.name]??s[r.name];n(e)&&u(`Unknown constraint '${r.name}'`),Object.assign(r,e(r.argument))}}})),e}(function(){const t=[];for(let n=0;e.length>n;){const e=r(n);e.template&&t.push(e),n+=e.template.length+1}return t}());function r(t){let n=[],r=t;for(;e.length>r&&"/"!==e[r];){const e=o(r)||a(r);n.push(e),r+=e.template.length}return{template:e.slice(t,r),parts:n}}function a(t){if("{"!==e[t])return null;const r=i(t),a=e.slice(t,t+r.length+2),o=function(e){const t=e.match(/^(?<name>[a-z_$][a-z0-9_$-]*?)(?:[:?+*]|$)/i)?.groups?.name;return n(t)&&u("Invalid parameter name"),t}(r),s=/[*+?]/.exec(r[o.length])?.[0]??"",c=function(e,t){const r=[];for(let a=t;e.length>a;){":"!==e[a]&&u();const t=l(e.slice(a+1));a+=t.length+1;const o="("===e[a]?i(a,e):null;n(o)||(a+=o.length+2),t||o||u(),r.push({name:"="===t?"default":t||"regex",argument:o??""})}return r}(r,o.length+s.length);return{name:o,kind:"parameter",template:a,quantifier:s,constraints:c.filter((e=>"default"!==e.name)),default:c.find((e=>"default"===e.name))?.argument,required:"+"===s||""===s,optional:"?"===s||"*"===s,catch_all:"+"===s||"*"===s}}function o(t){for(let n=t;;n++)if(n>=e.length||"/"===e[n]||"{"===e[n]){if(n===t)return null;const r=e.slice(t,n);return{kind:"literal",template:r,value:r}}}function i(t,n){n??=e;const r=[];e:for(let e=t;n.length>e;e++){switch(n[e]){case"{":r.push("}");break;case"(":r.push(")");break;case"}":case")":if(r.pop()!==n[e])break e}if(0===r.length)return n.slice(t+1,e)}u()}function l(e){const t=e.match(/^(?<name>=|[a-z0-9_$]*)(?=[/:(]|$)/i)?.groups?.name;return n(t)&&u("Invalid constraint name"),t}function u(t="Invalid pattern"){throw new Error(`${t}: ${e}`)}}(e,a));let o=t.map((e=>e.parts.map(((t,n)=>{if("literal"===t.kind)return n?t.value:`/${t.value}`;if(r.set(t.name,t),1===e.parts.length&&"?"===t.quantifier)return`(?:/(?<${t.name}>[^/]+?))?`;if(t.catch_all){let e=`(?<${t.name}>.${t.quantifier})`;return n||(e=`(?:/${e})`),"*"===t.quantifier&&(e+="?"),"*"===t.quantifier?e+"?":e}{const e=`(?<${t.name}>[^/]+?)${t.quantifier}`;return n?e:`/${e}`}})).join(""))).join("")||"/";return"/"!==o&&(o+="/?"),new RegExp(`^${o}$`)}(e,this.#n=[],this.#r=new Map,this.#a=t??{})}match(e){let t=this.#e.exec(e);if(n(t))return null;t=t.groups??{};for(let[e,r]of this.#r.entries()){let a=t[e];if(n(a)&&n(r.default))continue;a||n(r.default)||(a=r.default);const o=r.catch_all?a.split("/").filter((e=>e.length)):[a];for(let e=0;o.length>e;e++)for(let t of r.constraints){if(t.test&&!t.test(o[e]))return null;t.transform&&(o[e]=t.transform(o[e]))}t[e]=r.catch_all?o:o[0]}return t}resolve(e){e=new Map(Object.entries(e));const t=[];for(let r of this.#n){const a=[];for(let t of r.parts)if("literal"===t.kind)a.push(t.value);else{let r=e.get(t.name);if(e.delete(t.name),(n(r)||""===r)&&(r=this.#r.get(t.name)?.default,t.catch_all&&r&&(r=r.split("/"))),n(r)||""===r){if(t.required)return null;if(t.optional&&t.default===r)continue}t.catch_all?(r=o(r),a.push(...r.map((e=>encodeURIComponent(e))).join("/"))):a.push(encodeURIComponent(r))}a.length&&t.push(a.join(""))}let r=[...e.entries()].map((([e,t])=>encodeURIComponent(e)+"="+encodeURIComponent(t))).join("&");r&&(r="?"+r);const a=t.join("/")+r;return"/"!==a[0]?"/"+a:a}}function c({directive:t,$data:a}){t("route",((t,{expression:o,value:l},{cleanup:s,evaluate:c})=>{if(!r(t))return void e("x-route can only be used on a 'template' tag");const f=t._r_route;if(f||"view"!==l&&"handler"!==l)switch(l){case"view":f.view=()=>async function(t){let n;try{n=await fetch(t)}catch{}if(!n?.ok)return e(`Failed to load template from ${t}`),new DocumentFragment;const r=new DocumentFragment,a=(new DOMParser).parseFromString(await n.text(),"text/html");return r.append(...a.body.childNodes),r}(o),s((()=>{f.view=()=>new Promise((e=>e(new DocumentFragment)))}));break;case"handler":!function(){o||(o="[]"),o.startsWith("[")||(o=`[${o}]`);const e=c(o).map(i),r=a(t);f.handler=async t=>{for(let a of e){const e=await a.call(r,t);if(!n(e))return e}},s((()=>f.handler=null))}();break;default:!function(){const n=a(t)?.$router;if(n){const e=()=>new Promise((e=>e(t.content)));t._r_route=Object.assign(new u(o),{el:t,view:e,handler:()=>Promise.resolve()}),n.routes.push(t._r_route),s((()=>n.routes=n.routes.filter((e=>e!==t._r_route))))}else e("no x-router directive found")}()}else e("no x-route directive found")}))}let f,h;function p(){for(let e in f)e in location&&(f[e]=location[e])}const m={get path(){return h.hash.slice(1)||"/"},get location(){return h},resolve(e){let t=new URL(e);return t.hash?t.hash.slice(1)||"/":t.pathname},navigate(e,t=!1){0>e.indexOf("#")&&(e="#"+e),g(e,t)}},d={get path(){return h.pathname},get location(){return h},resolve(e){return new URL(e).pathname},navigate(e,t=!1){g(e,t)}};function g(e,t){history[t?"replaceState":"pushState"]({},"",e),h.refresh()}const v={html5:d,hash:m};function w({$data:t,addScopeToNode:a,directive:o,magic:i,reactive:s}){o("router",((n,{value:o},{cleanup:i})=>{o||(o="html5");const u=t(n).$router;if(u||"outlet"!==o&&"link"!==o)switch(o){case"outlet":u.outlet?e("x-router:outlet already specified",u.outlet,n):(u.outlet=n,i((()=>u.outlet=null)));break;case"link":!function(){let t=function(t){if($(t))return t;const n=t.querySelectorAll("a");return 1!==n.length&&e(`Expected exactly one link, but found ${n.length}`),n[0]}(n);if(t){n._r_routerlink=t;const e=(t.getAttribute("target")??"").indexOf("_blank")>=0,r=l(t,"click",(n=>{n.metaKey||n.altKey||n.ctrlKey||n.shiftKey||n.defaultPrevented||n.button>0||e||(n.preventDefault(),u.navigate(`${t.pathname}${t.search}${t.hash}`))}));i(r)}}();break;default:!function(){if(r(n))return void e("x-router cannot be used on a 'template' tag");const t=s({pattern:"",path:"",params:{}}),u=function(t){h??=(f||(f=Alpine.reactive({hash:"",host:"",hostname:"",href:"",origin:"",pathname:"",port:0,protocol:"",search:"",refresh(){p()}}),p(),l(window,"hashchange",p),l(window,"popstate",p)),f),t||="html5";let n=v[t];return n||(e(`Unknown history API: ${t}`),n=d),n}(o),c={routes:[],outlet:null,active:null,history:u,values:t,async match(e){for(let t of this.routes){const n=t.match(e);if(n){const r={router:c,route:t,params:n,path:e};if(!1!==await t.handler(r))return r}}},navigate(e,t=!1){return u.navigate(e,t),!0}};function m(){if(c.active){for(let e of c.active.nodes??[])e.remove();c.active.nodes=null,c.active=null}}a(n,{$route:t,$router:c});const g=function(e,t,n=null){const{effect:r,release:a}=Alpine;let o,i,l=!1;const s=r((()=>{o=e(),l||(n?.deep&&JSON.stringify(o),i=o),(l||(n?.immediate??1))&&setTimeout((()=>{t(o,i),i=o}),0),l=!0}));return()=>a(s)}((()=>u.path),(async e=>{const n=await c.match(e);n?e===u.path&&function(e,n,a){if(e.nodes?.length&&t.path===n)return;m(),t.path=n,t.pattern=e.template,t.params=a??{},c.active=e;const o=c.outlet;o&&e.view().then((i=>{t.path===n&&t.pattern===e.template&&JSON.stringify(t.params)===JSON.stringify(a)&&(e.nodes=[...i.cloneNode(!0).childNodes],r(o)?e.nodes.forEach((e=>o.parentElement.insertBefore(e,o))):e.nodes.forEach((e=>o.append(e))))}))}(n.route,n.path,n.params):m()}));i(g),i(m)}()}else e("no x-router directive found")})),i("active",(r=>{const a=t(r).$router;if(n(a))return e("No x-router directive found"),!1;JSON.stringify(a.values);const o=$(r)?r:(e=>{for(;e&&!e._r_routerlink;)e=(e._x_teleportBack??e).parentElement;return e})(r)?._r_routerlink;return o?a.history.resolve(o.href)===a.values.path:(r._r_routerlink_init?e("x-router:link directive not found",r):queueMicrotask((()=>{r._r_routerlink_init=!0,a.values.path=a.values.path})),!1)}))}function $(e){return"A"===e.tagName.toUpperCase()}function _(e){window.RoutePattern=u,e.plugin([w,c])}document.addEventListener("alpine:init",(()=>{Alpine.plugin(_)}))}();
1
+ !function(){"use strict";const e=(...e)=>console.warn("alpine-gear.js:",...e),t=Array.isArray,n=e=>null==e,r=e=>e instanceof HTMLTemplateElement,a=e=>"function"==typeof e,o=e=>t(e)?e:[e],i=e=>a(e)&&"AsyncFunction"===e.constructor?.name?e:function(...t){const n=e.apply(this,t);return a(n?.then)?n:Promise.resolve(n)},l=(e,t,n,r)=>(e.addEventListener(t,n,r),()=>e.removeEventListener(t,n,r)),s=Object.freeze({regex(e){const t=new RegExp(e);return{test:e=>t.test(e)}},bool(){return{test:e=>/^(?:true|false)$/i.test(e),transform:e=>4===e.length}},int(){return{test:e=>/^-?\d+$/.test(e),transform:e=>+e}},number(){return{test:e=>/^[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?$/.test(e)&&isFinite(parseFloat(e)),transform:e=>parseFloat(e)}},alpha(){return{test:e=>/^[a-z]+$/i.test(e)}},min(e){return{test:t=>t>=+e}},max(e){return{test:t=>+e>=t}},range(e){let[t,n]=e.split(",",2).map((e=>e.trim()));return{test:e=>e>=+t&&+n>=e}},length(e){let t=e.split(",").map((e=>e.trim()));return{test:2==t.length?e=>e.length>=+t[0]&&+t[1]>=e.length:e=>e.length===+t[0]}},minlength(e){return{test:t=>t.length>=+e}},maxlength(e){return{test:t=>+e>=t.length}}});class u{#e;#t;#n;#r;#a;get template(){return this.#t}get regex(){return this.#e}get constraints(){return this.#a}constructor(e,t=null){this.#t=e,this.#e=function(e,t,r,a){t.push(...function(e,t){return function(e){e.find((e=>e.parts.length>1&&e.parts.every((e=>e.optional))))&&u("Using all segment parameters as optional is not permitted");const r=new Map;return e.flatMap((e=>e.parts)).forEach(((e,a,o)=>{if("literal"!==e.kind||0>e.value.indexOf("?")||u("Literal segments cannot contain the '?' character"),"parameter"===e.kind){e.catch_all&&a!==o.length-1&&u("A catch-all parameter can only appear as the last segment"),r.has(e.name)&&u(`The route parameter name '${e.name}' appears more than one time`),"*"===e.quantifier&&n(e.default)&&(e.default=""),""===e.default&&"*"!==e.quantifier&&(e.default=null),r.set(e.name,!0);for(let r of e.constraints){const e=t?.[r.name]??s[r.name];n(e)&&u(`Unknown constraint '${r.name}'`),Object.assign(r,e(r.argument))}}})),e}(function(){const t=[];for(let n=0;e.length>n;){const e=r(n);e.template&&t.push(e),n+=e.template.length+1}return t}());function r(t){let n=[],r=t;for(;e.length>r&&"/"!==e[r];){const e=o(r)||a(r);n.push(e),r+=e.template.length}return{template:e.slice(t,r),parts:n}}function a(t){if("{"!==e[t])return null;const r=i(t),a=e.slice(t,t+r.length+2),o=function(e){const t=e.match(/^(?<name>[a-z_$][a-z0-9_$-]*?)(?:[:?+*]|$)/i)?.groups?.name;return n(t)&&u("Invalid parameter name"),t}(r),s=/[*+?]/.exec(r[o.length])?.[0]??"",c=function(e,t){const r=[];for(let a=t;e.length>a;){":"!==e[a]&&u();const t=l(e.slice(a+1));a+=t.length+1;const o="("===e[a]?i(a,e):null;n(o)||(a+=o.length+2),t||o||u(),r.push({name:"="===t?"default":t||"regex",argument:o??""})}return r}(r,o.length+s.length);return{name:o,kind:"parameter",template:a,quantifier:s,constraints:c.filter((e=>"default"!==e.name)),default:c.find((e=>"default"===e.name))?.argument,required:"+"===s||""===s,optional:"?"===s||"*"===s,catch_all:"+"===s||"*"===s}}function o(t){for(let n=t;;n++)if(n>=e.length||"/"===e[n]||"{"===e[n]){if(n===t)return null;const r=e.slice(t,n);return{kind:"literal",template:r,value:r}}}function i(t,n){n??=e;const r=[];e:for(let e=t;n.length>e;e++){switch(n[e]){case"{":r.push("}");break;case"(":r.push(")");break;case"}":case")":if(r.pop()!==n[e])break e}if(0===r.length)return n.slice(t+1,e)}u()}function l(e){const t=e.match(/^(?<name>=|[a-z0-9_$]*)(?=[/:(]|$)/i)?.groups?.name;return n(t)&&u("Invalid constraint name"),t}function u(t="Invalid pattern"){throw new Error(`${t}: ${e}`)}}(e,a));let o=t.map((e=>e.parts.map(((t,n)=>{if("literal"===t.kind)return n?t.value:`/${t.value}`;if(r.set(t.name,t),1===e.parts.length&&"?"===t.quantifier)return`(?:/(?<${t.name}>[^/]+?))?`;if(t.catch_all){let e=`(?<${t.name}>.${t.quantifier})`;return n||(e=`(?:/${e})`),"*"===t.quantifier&&(e+="?"),"*"===t.quantifier?e+"?":e}{const e=`(?<${t.name}>[^/]+?)${t.quantifier}`;return n?e:`/${e}`}})).join(""))).join("")||"/";return"/"!==o&&(o+="/?"),new RegExp(`^${o}$`)}(e,this.#n=[],this.#r=new Map,this.#a=t??{})}match(e){let t=this.#e.exec(e);if(n(t))return null;t=t.groups??{};for(let[e,r]of this.#r.entries()){let a=t[e];if(n(a)&&n(r.default))continue;a||n(r.default)||(a=r.default);const o=r.catch_all?a.split("/").filter((e=>e.length)):[a];for(let e=0;o.length>e;e++)for(let t of r.constraints){if(t.test&&!t.test(o[e]))return null;t.transform&&(o[e]=t.transform(o[e]))}t[e]=r.catch_all?o:o[0]}return t}resolve(e){e=new Map(Object.entries(e));const t=[];for(let r of this.#n){const a=[];for(let t of r.parts)if("literal"===t.kind)a.push(t.value);else{let r=e.get(t.name);if(e.delete(t.name),(n(r)||""===r)&&(r=this.#r.get(t.name)?.default,t.catch_all&&r&&(r=r.split("/"))),n(r)||""===r){if(t.required)return null;if(t.optional&&t.default===r)continue}t.catch_all?(r=o(r),a.push(...r.map((e=>encodeURIComponent(e))).join("/"))):a.push(encodeURIComponent(r))}a.length&&t.push(a.join(""))}let r=[...e.entries()].map((([e,t])=>encodeURIComponent(e)+"="+encodeURIComponent(t))).join("&");r&&(r="?"+r);const a=t.join("/")+r;return"/"!==a[0]?"/"+a:a}}function c({directive:t,$data:a}){t("route",((t,{expression:o,value:l},{cleanup:s,evaluate:c})=>{if(!r(t))return void e("x-route can only be used on a 'template' tag");const f=t._r_route;if(f||"view"!==l&&"handler"!==l)switch(l){case"view":f.view=()=>async function(t){let n;try{n=await fetch(t)}catch{}if(!n?.ok)return e(`Failed to load template from ${t}`),new DocumentFragment;const r=new DocumentFragment,a=(new DOMParser).parseFromString(await n.text(),"text/html");return r.append(...a.body.childNodes),r}(o),s((()=>{f.view=()=>new Promise((e=>e(new DocumentFragment)))}));break;case"handler":!function(){o||(o="[]"),o.startsWith("[")||(o=`[${o}]`);const e=c(o).map(i),r=a(t);f.handler=async t=>{for(let a of e){const e=await a.call(r,t);if(!n(e))return e}},s((()=>f.handler=null))}();break;default:!function(){const n=a(t)?.$router;if(n){const e=()=>new Promise((e=>e(t.content)));t._r_route=Object.assign(new u(o),{el:t,view:e,handler:()=>Promise.resolve()}),n.routes.push(t._r_route),s((()=>n.routes=n.routes.filter((e=>e!==t._r_route))))}else e("no x-router directive found")}()}else e("no x-route directive found")}))}let f,h;function p(){for(let e in f)e in location&&(f[e]=location[e])}const m={get path(){return h.hash.slice(1)||"/"},get location(){return h},resolve(e){let t=new URL(e);return t.hash?t.hash.slice(1)||"/":t.pathname},navigate(e,t=!1){0>e.indexOf("#")&&(e="#"+e),g(e,t)}},d={get path(){return h.pathname},get location(){return h},resolve(e){return new URL(e).pathname},navigate(e,t=!1){g(e,t)}};function g(e,t){history[t?"replaceState":"pushState"]({},"",e),h.refresh()}const v={html5:d,hash:m};function w({$data:t,addScopeToNode:n,directive:a,magic:o,reactive:i}){a("router",((a,{modifiers:o,value:s},{cleanup:u})=>{s||(s="html5");const c=t(a).$router;if(c||"outlet"!==s&&"link"!==s)switch(s){case"outlet":c.outlet?e("x-router:outlet already specified",c.outlet,a):(c.outlet=a,u((()=>c.outlet=null)));break;case"link":!function(){let t=function(t){if($(t))return t;const n=t.querySelectorAll("a");return 1!==n.length&&e(`Expected exactly one link, but found ${n.length}`),n[0]}(a);if(t){a._r_routerlink=t;const e=(t.getAttribute("target")??"").indexOf("_blank")>=0,n=l(t,"click",(n=>{n.metaKey||n.altKey||n.ctrlKey||n.shiftKey||n.defaultPrevented||n.button>0||e||(n.preventDefault(),c.navigate(`${t.pathname}${t.search}${t.hash}`,(e=>e.includes("replace"))(o)))}));u(n)}}();break;default:!function(){if(r(a))return void e("x-router cannot be used on a 'template' tag");const t=i({pattern:"",path:"",params:{}}),o=function(t){h??=(f||(f=Alpine.reactive({hash:"",host:"",hostname:"",href:"",origin:"",pathname:"",port:0,protocol:"",search:"",refresh(){p()}}),p(),l(window,"hashchange",p),l(window,"popstate",p)),f),t||="html5";let n=v[t];return n||(e(`Unknown history API: ${t}`),n=d),n}(s),c={routes:[],outlet:null,active:null,history:o,values:t,async match(e){for(let t of this.routes){const n=t.match(e);if(n){const r={router:c,route:t,params:n,path:e};if(!1!==await t.handler(r))return r}}},navigate(e,t=!1){return o.navigate(e,t),!0}};function m(){if(c.active){for(let e of c.active.nodes??[])e.remove();c.active.nodes=null,c.active=null}}n(a,{$route:t,$router:c});const g=function(e,t,n=null){const{effect:r,release:a}=Alpine;let o,i,l=!1;const s=r((()=>{o=e(),l||(n?.deep&&JSON.stringify(o),i=o),(l||(n?.immediate??1))&&setTimeout((()=>{t(o,i),i=o}),0),l=!0}));return()=>a(s)}((()=>o.path),(async e=>{const n=await c.match(e);n?e===o.path&&function(e,n,a){if(e.nodes?.length&&t.path===n)return;m(),t.path=n,t.pattern=e.template,t.params=a??{},c.active=e;const o=c.outlet;o&&e.view().then((i=>{t.path===n&&t.pattern===e.template&&JSON.stringify(t.params)===JSON.stringify(a)&&(e.nodes=[...i.cloneNode(!0).childNodes],r(o)?e.nodes.forEach((e=>o.parentElement.insertBefore(e,o))):e.nodes.forEach((e=>o.append(e))))}))}(n.route,n.path,n.params):m()}));u(g),u(m)}()}else e("no x-router directive found")})),o("active",(n=>{const r=t(n).$router;if(!r)return e("No x-router directive found"),!1;JSON.stringify(r.values);const a=$(n)?n:(e=>{for(;e&&!e._r_routerlink;)e=(e._x_teleportBack??e).parentElement;return e})(n)?._r_routerlink;return a?r.history.resolve(a.href)===r.values.path:(n._r_routerlink_init?e("x-router:link directive not found",n):queueMicrotask((()=>{n._r_routerlink_init=!0,r.values.path=r.values.path})),!1)}))}function $(e){return"A"===e.tagName.toUpperCase()}function _(e){window.RoutePattern=u,e.plugin([w,c])}document.addEventListener("alpine:init",(()=>{Alpine.plugin(_)}))}();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ramstack/alpinegear-router",
3
- "version": "1.1.0-preview.6",
3
+ "version": "1.1.0",
4
4
  "description": "@ramstack/alpinegear-router provides routing-related directives for Alpine.js, enabling client-side navigation and routing functionality.",
5
5
  "author": "Rameel Burhan",
6
6
  "license": "MIT",