@aref-shojaei/router 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,20 +14,20 @@ Router.configure({ window, document })
14
14
 
15
15
 
16
16
  // Single Route
17
- Route.addRoute("/", () => "Welcome Page")
17
+ Route.add("/", () => "Welcome Page")
18
18
 
19
19
  // Group Routes
20
20
  Route.group("/auth", () => {
21
- Route.addRoute("/login", () => "Login Page")
22
- Route.addRoute("/register", () => "Register Page")
21
+ Route.add("/login", () => "Login Page")
22
+ Route.add("/register", () => "Register Page")
23
23
  })
24
24
 
25
25
  // Dynamic Route
26
- Route.addRoute("/users/{id}", ({ params : { name } }) => `User#${id} Page`)
26
+ Route.add("/users/{id}", ({ params : { name } }) => `User#${id} Page`)
27
27
 
28
28
 
29
29
  // Redirect Routes
30
- Route.addRoute("/redirection", () => Route.redirect("/"))
30
+ Route.add("/redirection", () => Route.redirect("/"))
31
31
 
32
32
 
33
33
  // Initialize the router
@@ -124,7 +124,7 @@ yarn add @aref-shojaei/router
124
124
  ```js
125
125
  import { Router , Route } from "@aref-shojaei/router"
126
126
 
127
- Route.addRoute("/", () => "Welcome Page")
127
+ Route.add("/", () => "Welcome Page")
128
128
 
129
129
  Router.run()
130
130
  ```
@@ -143,18 +143,18 @@ Router.run()
143
143
  import { Route } from "@aref-shojaei/router"
144
144
 
145
145
  // Single route
146
- Route.addRoute("/", () => "Welcome Page")
146
+ Route.add("/", () => "Welcome Page")
147
147
 
148
148
  // Group routes
149
149
  Route.group("/auth", () => {
150
- Route.addRoute("/login", () => "Welcome Page")
151
- Route.addRoute("/register", () => "Welcome Page")
150
+ Route.add("/login", () => "Welcome Page")
151
+ Route.add("/register", () => "Welcome Page")
152
152
  })
153
153
 
154
154
  // Dynamic route with params
155
- Route.addRoute("/users/{id}", ({ params : { id } }) => `User #{id} Page`)
155
+ Route.add("/users/{id}", ({ params : { id } }) => `User #{id} Page`)
156
156
 
157
- Route.addRoute("/courses/{category}/{name}", ({ params : { category, name } }) => `Course Detail: '${category}/${name}' Page`)
157
+ Route.add("/courses/{category}/{name}", ({ params : { category, name } }) => `Course Detail: '${category}/${name}' Page`)
158
158
  ```
159
159
 
160
160
  <br/>
@@ -172,12 +172,12 @@ const logger = () => console.log("[LOG] my custom middleware")
172
172
 
173
173
 
174
174
  // Single route with middleware
175
- Route.addRoute("/", () => "Welcome Page").middleware([logger])
175
+ Route.add("/", () => "Welcome Page").middleware([logger])
176
176
 
177
177
  // Group routes with middleware
178
178
  Route.group("/auth", () => {
179
- Route.addRoute("/login", () => "Welcome Page")
180
- Route.addRoute("/register", () => "Welcome Page")
179
+ Route.add("/login", () => "Welcome Page")
180
+ Route.add("/register", () => "Welcome Page")
181
181
  }).middleware([logger])
182
182
  ```
183
183
 
@@ -188,8 +188,8 @@ Route.group("/auth", () => {
188
188
  ```js
189
189
  import { Route } from "@aref-shojaei/router"
190
190
 
191
- Route.addRoute("/auth/login", () => "Welcome Page")
192
- Route.addRoute("/dashboard", () => Route.redirect("/auth/login"))
191
+ Route.add("/auth/login", () => "Welcome Page")
192
+ Route.add("/dashboard", () => Route.redirect("/auth/login"))
193
193
  ```
194
194
 
195
195
  <br/>
@@ -201,5 +201,5 @@ Route.addRoute("/dashboard", () => Route.redirect("/auth/login"))
201
201
  ```js
202
202
  import { Route } from "@aref-shojaei/router"
203
203
 
204
- Route.addRoute("/", () => "Welcome Page").title("Custom Page Title | SPA")
204
+ Route.add("/", () => "Welcome Page").title("Custom Page Title | SPA")
205
205
  ```
@@ -1 +1 @@
1
- (()=>{"use strict";class t extends Error{constructor(t){super(t),this.name="Invalid argument error"}}class e{static#t="";static#e="";static#s;constructor(){throw new Error(`${new.target.name} class must not be called with "new" keyword!`)}static setDocument(e){if("object"!=typeof e)throw new t("'document' must be a Document object!");this.#s=e}static _getDocument(){return this.#s}static setTitle(e){if("string"!=typeof e)throw new t("'title' must be a string!");this.#e=e,this.#i()}static getTitle(){return this.#e||this.#t}static setRootTitle(e){if("string"!=typeof e)throw new t("'value' must be a string!");this.#t=e,this.#i()}static getRootTitle(){return this.#t}static#i(){const t=this._getDocument();this.getTitle()?t.title=this.getTitle():t.title=this.getRootTitle()}}class s{constructor(){throw new Error(`${new.target.name} class must not be called with "new" keyword!`)}static render(e,s={}){if("function"!=typeof e)throw new t("'template' must be a function!");try{return e(s)}catch(t){console.error("Error during template rendering: ",t)}}}class i{static#o=[];constructor(){throw new Error(`${new.target.name} class must not be called with "new" keyword!`)}static findAll(e,s){if("string"!=typeof e)throw new t("'element' must be an HTMLElement object!");if("object"!=typeof s)throw new t("'document' must be a Document object!");const i=s.querySelectorAll(e);return this._setElements(i),this}static each(e){if("function"!=typeof e)throw new t("'callback' must be a function!");const s=this._getElements();this.#o.length&&s.forEach(e)}static _setElements(t){this.#o.push(...t)}static _getElements(){return this.#o}}class o{constructor(){throw new Error(`${new.target.name} class must not be called with "new" keyword!`)}static onClick(e,s){if("object"!=typeof e)throw new t("'element' must be an HTMLElement object!");if("function"!=typeof s)throw new t("'callback' must be a function!");e.addEventListener("click",s)}}class r{title="";template;middlewares=[];meta={params:{},query:{}};constructor({title:t,template:e}){this.title=t??this.title,this.template=e}}class n{static _window;static _document;static _rootElement="#root";static _routes={};static _currentRoute="";static _routePrefix="";static _defaultRoute=new r({title:"404",template:()=>"404 | Page not found!"});constructor(){throw new Error(`${new.target.name} class must not be called with "new" keyword!`)}static configure({window:e,document:s,selector:i}){if("object"!=typeof e)throw new t("'window' must be a Window object!");if("object"!=typeof s)throw new t("'document' must be a Document object!");if(i&&"string"!=typeof i)throw new t("'selector' must be a string!");i&&(this._rootElement=i),this._window=e,this._document=s}static#r(t){for(const e in this._routes){const s=new RegExp(`^${e.replace(/\{(\w+)\}/g,"(?<$1>[^/{}]+)")}$`);if(!s.test(t))continue;const{groups:i}=s.exec(t);return this.#n(e),this.#a(e,i),this._routes[e]}}static#n(t){const e={},{search:s}=location;if(!s.length)return!1;s.slice(1).split("&").forEach((t=>{const[s,i]=t.split("=");e[s]=i})),this._routes[t].meta.query={...e}}static#a(t,e){this._routes[t].meta.params={...e}}static _setRouteToURL(e){if("string"!=typeof e||!e.startsWith("/"))throw new t("'route' must be a string starting with \"/\"!");this._window.history.pushState({},"",e)}static#c(t){try{const{title:i,template:o,middlewares:r,meta:n}=this.#r(t)??this._defaultRoute;e.setTitle(i),this.#u(r),this._document.querySelector(this._rootElement).innerHTML=s.render(o,n)}catch(e){console.error("Error to inject route template:",t,e)}}static#l(){this._window.addEventListener("popstate",(t=>{const e=t.target.location.pathname;this.#c(e)}))}static#h(){const{pathname:t}=this._window.location;this.#c(t)}static#w(){i.findAll("a",this._document).each((t=>{o.onClick(t,(t=>{if(t.target.hasAttribute("data-link"))return!1;t.preventDefault();const e=t.target.getAttribute("href");this._setRouteToURL(e),this.#c(e)}))}))}static#u(t){try{t.length&&t.forEach((t=>t()))}catch(t){console.error("Error executing middleware:",t)}}static run(s=(()=>{})){if("function"!=typeof s)throw new t("'callback' must be a function!");e.setDocument(this._document),e.setRootTitle(this._document.title),this.#l(),this.#h(),this.#w(),s()}}window.Router=n,window.Route=class extends n{constructor(){throw new Error(`${new.target.name} class must not be called with "new" keyword!`)}static addRoute(e,s){if("string"!=typeof e)throw new t("'route' must be a string!");if("function"!=typeof s)throw new t("'callback' must be a function!");return this._routes[this._routePrefix+e]=new r({template:s}),this._currentRoute=e,this}static group(e,s){if("string"!=typeof e||!e.startsWith("/"))throw new t("'prefix' must be a string!");if("function"!=typeof s)throw new t("'callback' must be a function!");const i=this._routePrefix;return this._routePrefix=e,s(),this._routePrefix=i,this}static middleware(e){if(!Array.isArray(e))throw new t("'middlewares' must be an array!");if(this._routePrefix)for(const t in this._routes)t.startsWith(this._routePrefix)&&this._routes[t].middlewares.push(...e);else this._routes[this._routePrefix+this._currentRoute].middlewares.push(...e)}static title(e){if("string"!=typeof e)throw new t("'value' must be a string!");this._routes[this._routePrefix+this._currentRoute].title=e}static redirect(e){if("string"!=typeof e||!e.startsWith("/"))throw new t("'to' must be a string starting route with \"/\"!");this._setRouteToURL(e);const{template:i,meta:o}=this._routes[e]??this._defaultRoute;return s.render(i,o)}}})();
1
+ (()=>{"use strict";class t extends Error{constructor(t){super(t),this.name="Invalid argument error"}}class e{static#t="";static#e="";static#s;constructor(){throw new Error(`${new.target.name} class must not be called with "new" keyword!`)}static setDocument(e){if("object"!=typeof e)throw new t("'document' must be a Document object!");this.#s=e}static _getDocument(){return this.#s}static setTitle(e){if("string"!=typeof e)throw new t("'title' must be a string!");this.#e=e,this.#i()}static getTitle(){return this.#e||this.#t}static setRootTitle(e){if("string"!=typeof e)throw new t("'value' must be a string!");this.#t=e,this.#i()}static getRootTitle(){return this.#t}static#i(){const t=this._getDocument();this.getTitle()?t.title=this.getTitle():t.title=this.getRootTitle()}}class s{constructor(){throw new Error(`${new.target.name} class must not be called with "new" keyword!`)}static render(e,s={}){if("function"!=typeof e)throw new t("'template' must be a function!");try{return e(s)}catch(t){console.error("Error during template rendering: ",t)}}}class i{static#o=[];constructor(){throw new Error(`${new.target.name} class must not be called with "new" keyword!`)}static findAll(e,s){if("string"!=typeof e)throw new t("'element' must be an HTMLElement object!");if("object"!=typeof s)throw new t("'document' must be a Document object!");const i=s.querySelectorAll(e);return this._setElements(i),this}static each(e){if("function"!=typeof e)throw new t("'callback' must be a function!");const s=this._getElements();this.#o.length&&s.forEach(e)}static _setElements(t){this.#o.push(...t)}static _getElements(){return this.#o}}class o{constructor(){throw new Error(`${new.target.name} class must not be called with "new" keyword!`)}static onClick(e,s){if("object"!=typeof e)throw new t("'element' must be an HTMLElement object!");if("function"!=typeof s)throw new t("'callback' must be a function!");e.addEventListener("click",s)}}class r{title="";template;middlewares=[];meta={params:{},query:{}};constructor({title:t,template:e}){this.title=t??this.title,this.template=e}}class n{static _window;static _document;static _rootElement="#root";static _routes={};static _currentRoute="";static _routePrefix="";static _defaultRoute=new r({title:"404",template:()=>"404 | Page not found!"});constructor(){throw new Error(`${new.target.name} class must not be called with "new" keyword!`)}static configure({window:e,document:s,selector:i}){if("object"!=typeof e)throw new t("'window' must be a Window object!");if("object"!=typeof s)throw new t("'document' must be a Document object!");if(i&&"string"!=typeof i)throw new t("'selector' must be a string!");i&&(this._rootElement=i),this._window=e,this._document=s}static#r(t){for(const e in this._routes){const s=new RegExp(`^${e.replace(/\{(\w+)\}/g,"(?<$1>[^/{}]+)")}$`);if(!s.test(t))continue;const{groups:i}=s.exec(t);return this.#n(e),this.#a(e,i),this._routes[e]}}static#n(t){const e={},{search:s}=location;if(!s.length)return!1;s.slice(1).split("&").forEach((t=>{const[s,i]=t.split("=");e[s]=i})),this._routes[t].meta.query={...e}}static#a(t,e){this._routes[t].meta.params={...e}}static _setRouteToURL(e){if("string"!=typeof e||!e.startsWith("/"))throw new t("'route' must be a string starting with \"/\"!");this._window.history.pushState({},"",e)}static#c(t){try{const{title:i,template:o,middlewares:r,meta:n}=this.#r(t)??this._defaultRoute;e.setTitle(i),this.#u(r),this._document.querySelector(this._rootElement).innerHTML=s.render(o,n)}catch(e){console.error("Error to inject route template:",t,e)}}static#l(){this._window.addEventListener("popstate",(t=>{const e=t.target.location.pathname;this.#c(e)}))}static#h(){const{pathname:t}=this._window.location;this.#c(t)}static#w(){i.findAll("a",this._document).each((t=>{o.onClick(t,(t=>{if(t.target.hasAttribute("data-link"))return!1;t.preventDefault();const e=t.target.getAttribute("href");this._setRouteToURL(e),this.#c(e)}))}))}static#u(t){try{t.length&&t.forEach((t=>t()))}catch(t){console.error("Error executing middleware:",t)}}static run(s=(()=>{})){if("function"!=typeof s)throw new t("'callback' must be a function!");e.setDocument(this._document),e.setRootTitle(this._document.title),this.#l(),this.#h(),this.#w(),s()}}window.Router=n,window.Route=class extends n{constructor(){throw new Error(`${new.target.name} class must not be called with "new" keyword!`)}static add(e,s){if("string"!=typeof e)throw new t("'route' must be a string!");if("function"!=typeof s)throw new t("'callback' must be a function!");return this._routes[this._routePrefix+e]=new r({template:s}),this._currentRoute=e,this}static group(e,s){if("string"!=typeof e||!e.startsWith("/"))throw new t("'prefix' must be a string!");if("function"!=typeof s)throw new t("'callback' must be a function!");const i=this._routePrefix;return this._routePrefix=e,s(),this._routePrefix=i,this}static middleware(e){if(!Array.isArray(e))throw new t("'middlewares' must be an array!");if(this._routePrefix)for(const t in this._routes)t.startsWith(this._routePrefix)&&this._routes[t].middlewares.push(...e);else this._routes[this._routePrefix+this._currentRoute].middlewares.push(...e)}static title(e){if("string"!=typeof e)throw new t("'value' must be a string!");this._routes[this._routePrefix+this._currentRoute].title=e}static redirect(e){if("string"!=typeof e||!e.startsWith("/"))throw new t("'to' must be a string starting route with \"/\"!");this._setRouteToURL(e);const{template:i,meta:o}=this._routes[e]??this._defaultRoute;return s.render(i,o)}}})();
@@ -1,8 +1,8 @@
1
1
  Router.configure({ window, document })
2
2
 
3
- Route.addRoute("/", () => "Welcome Page")
4
- Route.addRoute("/users", () => "Users Page")
5
- Route.addRoute("/users/{name}", ({ params : { name } }) => `User #${name} Page`)
6
- Route.addRoute("/redirection", () => Route.redirect("/"))
3
+ Route.add("/", () => "Welcome Page")
4
+ Route.add("/users", () => "Users Page")
5
+ Route.add("/users/{name}", ({ params : { name } }) => `User #${name} Page`)
6
+ Route.add("/redirection", () => Route.redirect("/"))
7
7
 
8
8
  Router.run()
@@ -1 +1 @@
1
- (()=>{"use strict";class t extends Error{constructor(t){super(t),this.name="Invalid argument error"}}class e{static#t="";static#e="";static#s;constructor(){throw new Error(`${new.target.name} class must not be called with "new" keyword!`)}static setDocument(e){if("object"!=typeof e)throw new t("'document' must be a Document object!");this.#s=e}static _getDocument(){return this.#s}static setTitle(e){if("string"!=typeof e)throw new t("'title' must be a string!");this.#e=e,this.#i()}static getTitle(){return this.#e||this.#t}static setRootTitle(e){if("string"!=typeof e)throw new t("'value' must be a string!");this.#t=e,this.#i()}static getRootTitle(){return this.#t}static#i(){const t=this._getDocument();this.getTitle()?t.title=this.getTitle():t.title=this.getRootTitle()}}class s{constructor(){throw new Error(`${new.target.name} class must not be called with "new" keyword!`)}static render(e,s={}){if("function"!=typeof e)throw new t("'template' must be a function!");try{return e(s)}catch(t){console.error("Error during template rendering: ",t)}}}class i{static#o=[];constructor(){throw new Error(`${new.target.name} class must not be called with "new" keyword!`)}static findAll(e,s){if("string"!=typeof e)throw new t("'element' must be an HTMLElement object!");if("object"!=typeof s)throw new t("'document' must be a Document object!");const i=s.querySelectorAll(e);return this._setElements(i),this}static each(e){if("function"!=typeof e)throw new t("'callback' must be a function!");const s=this._getElements();this.#o.length&&s.forEach(e)}static _setElements(t){this.#o.push(...t)}static _getElements(){return this.#o}}class o{constructor(){throw new Error(`${new.target.name} class must not be called with "new" keyword!`)}static onClick(e,s){if("object"!=typeof e)throw new t("'element' must be an HTMLElement object!");if("function"!=typeof s)throw new t("'callback' must be a function!");e.addEventListener("click",s)}}class r{title="";template;middlewares=[];meta={params:{},query:{}};constructor({title:t,template:e}){this.title=t??this.title,this.template=e}}class n{static _window;static _document;static _rootElement="#root";static _routes={};static _currentRoute="";static _routePrefix="";static _defaultRoute=new r({title:"404",template:()=>"404 | Page not found!"});constructor(){throw new Error(`${new.target.name} class must not be called with "new" keyword!`)}static configure({window:e,document:s,selector:i}){if("object"!=typeof e)throw new t("'window' must be a Window object!");if("object"!=typeof s)throw new t("'document' must be a Document object!");if(i&&"string"!=typeof i)throw new t("'selector' must be a string!");i&&(this._rootElement=i),this._window=e,this._document=s}static#r(t){for(const e in this._routes){const s=new RegExp(`^${e.replace(/\{(\w+)\}/g,"(?<$1>[^/{}]+)")}$`);if(!s.test(t))continue;const{groups:i}=s.exec(t);return this.#n(e),this.#a(e,i),this._routes[e]}}static#n(t){const e={},{search:s}=location;if(!s.length)return!1;s.slice(1).split("&").forEach((t=>{const[s,i]=t.split("=");e[s]=i})),this._routes[t].meta.query={...e}}static#a(t,e){this._routes[t].meta.params={...e}}static _setRouteToURL(e){if("string"!=typeof e||!e.startsWith("/"))throw new t("'route' must be a string starting with \"/\"!");this._window.history.pushState({},"",e)}static#c(t){try{const{title:i,template:o,middlewares:r,meta:n}=this.#r(t)??this._defaultRoute;e.setTitle(i),this.#u(r),this._document.querySelector(this._rootElement).innerHTML=s.render(o,n)}catch(e){console.error("Error to inject route template:",t,e)}}static#l(){this._window.addEventListener("popstate",(t=>{const e=t.target.location.pathname;this.#c(e)}))}static#h(){const{pathname:t}=this._window.location;this.#c(t)}static#w(){i.findAll("a",this._document).each((t=>{o.onClick(t,(t=>{if(t.target.hasAttribute("data-link"))return!1;t.preventDefault();const e=t.target.getAttribute("href");this._setRouteToURL(e),this.#c(e)}))}))}static#u(t){try{t.length&&t.forEach((t=>t()))}catch(t){console.error("Error executing middleware:",t)}}static run(s=(()=>{})){if("function"!=typeof s)throw new t("'callback' must be a function!");e.setDocument(this._document),e.setRootTitle(this._document.title),this.#l(),this.#h(),this.#w(),s()}}window.Router=n,window.Route=class extends n{constructor(){throw new Error(`${new.target.name} class must not be called with "new" keyword!`)}static addRoute(e,s){if("string"!=typeof e)throw new t("'route' must be a string!");if("function"!=typeof s)throw new t("'callback' must be a function!");return this._routes[this._routePrefix+e]=new r({template:s}),this._currentRoute=e,this}static group(e,s){if("string"!=typeof e||!e.startsWith("/"))throw new t("'prefix' must be a string!");if("function"!=typeof s)throw new t("'callback' must be a function!");const i=this._routePrefix;return this._routePrefix=e,s(),this._routePrefix=i,this}static middleware(e){if(!Array.isArray(e))throw new t("'middlewares' must be an array!");if(this._routePrefix)for(const t in this._routes)t.startsWith(this._routePrefix)&&this._routes[t].middlewares.push(...e);else this._routes[this._routePrefix+this._currentRoute].middlewares.push(...e)}static title(e){if("string"!=typeof e)throw new t("'value' must be a string!");this._routes[this._routePrefix+this._currentRoute].title=e}static redirect(e){if("string"!=typeof e||!e.startsWith("/"))throw new t("'to' must be a string starting route with \"/\"!");this._setRouteToURL(e);const{template:i,meta:o}=this._routes[e]??this._defaultRoute;return s.render(i,o)}}})();
1
+ (()=>{"use strict";class t extends Error{constructor(t){super(t),this.name="Invalid argument error"}}class e{static#t="";static#e="";static#s;constructor(){throw new Error(`${new.target.name} class must not be called with "new" keyword!`)}static setDocument(e){if("object"!=typeof e)throw new t("'document' must be a Document object!");this.#s=e}static _getDocument(){return this.#s}static setTitle(e){if("string"!=typeof e)throw new t("'title' must be a string!");this.#e=e,this.#i()}static getTitle(){return this.#e||this.#t}static setRootTitle(e){if("string"!=typeof e)throw new t("'value' must be a string!");this.#t=e,this.#i()}static getRootTitle(){return this.#t}static#i(){const t=this._getDocument();this.getTitle()?t.title=this.getTitle():t.title=this.getRootTitle()}}class s{constructor(){throw new Error(`${new.target.name} class must not be called with "new" keyword!`)}static render(e,s={}){if("function"!=typeof e)throw new t("'template' must be a function!");try{return e(s)}catch(t){console.error("Error during template rendering: ",t)}}}class i{static#o=[];constructor(){throw new Error(`${new.target.name} class must not be called with "new" keyword!`)}static findAll(e,s){if("string"!=typeof e)throw new t("'element' must be an HTMLElement object!");if("object"!=typeof s)throw new t("'document' must be a Document object!");const i=s.querySelectorAll(e);return this._setElements(i),this}static each(e){if("function"!=typeof e)throw new t("'callback' must be a function!");const s=this._getElements();this.#o.length&&s.forEach(e)}static _setElements(t){this.#o.push(...t)}static _getElements(){return this.#o}}class o{constructor(){throw new Error(`${new.target.name} class must not be called with "new" keyword!`)}static onClick(e,s){if("object"!=typeof e)throw new t("'element' must be an HTMLElement object!");if("function"!=typeof s)throw new t("'callback' must be a function!");e.addEventListener("click",s)}}class r{title="";template;middlewares=[];meta={params:{},query:{}};constructor({title:t,template:e}){this.title=t??this.title,this.template=e}}class n{static _window;static _document;static _rootElement="#root";static _routes={};static _currentRoute="";static _routePrefix="";static _defaultRoute=new r({title:"404",template:()=>"404 | Page not found!"});constructor(){throw new Error(`${new.target.name} class must not be called with "new" keyword!`)}static configure({window:e,document:s,selector:i}){if("object"!=typeof e)throw new t("'window' must be a Window object!");if("object"!=typeof s)throw new t("'document' must be a Document object!");if(i&&"string"!=typeof i)throw new t("'selector' must be a string!");i&&(this._rootElement=i),this._window=e,this._document=s}static#r(t){for(const e in this._routes){const s=new RegExp(`^${e.replace(/\{(\w+)\}/g,"(?<$1>[^/{}]+)")}$`);if(!s.test(t))continue;const{groups:i}=s.exec(t);return this.#n(e),this.#a(e,i),this._routes[e]}}static#n(t){const e={},{search:s}=location;if(!s.length)return!1;s.slice(1).split("&").forEach((t=>{const[s,i]=t.split("=");e[s]=i})),this._routes[t].meta.query={...e}}static#a(t,e){this._routes[t].meta.params={...e}}static _setRouteToURL(e){if("string"!=typeof e||!e.startsWith("/"))throw new t("'route' must be a string starting with \"/\"!");this._window.history.pushState({},"",e)}static#c(t){try{const{title:i,template:o,middlewares:r,meta:n}=this.#r(t)??this._defaultRoute;e.setTitle(i),this.#u(r),this._document.querySelector(this._rootElement).innerHTML=s.render(o,n)}catch(e){console.error("Error to inject route template:",t,e)}}static#l(){this._window.addEventListener("popstate",(t=>{const e=t.target.location.pathname;this.#c(e)}))}static#h(){const{pathname:t}=this._window.location;this.#c(t)}static#w(){i.findAll("a",this._document).each((t=>{o.onClick(t,(t=>{if(t.target.hasAttribute("data-link"))return!1;t.preventDefault();const e=t.target.getAttribute("href");this._setRouteToURL(e),this.#c(e)}))}))}static#u(t){try{t.length&&t.forEach((t=>t()))}catch(t){console.error("Error executing middleware:",t)}}static run(s=(()=>{})){if("function"!=typeof s)throw new t("'callback' must be a function!");e.setDocument(this._document),e.setRootTitle(this._document.title),this.#l(),this.#h(),this.#w(),s()}}window.Router=n,window.Route=class extends n{constructor(){throw new Error(`${new.target.name} class must not be called with "new" keyword!`)}static add(e,s){if("string"!=typeof e)throw new t("'route' must be a string!");if("function"!=typeof s)throw new t("'callback' must be a function!");return this._routes[this._routePrefix+e]=new r({template:s}),this._currentRoute=e,this}static group(e,s){if("string"!=typeof e||!e.startsWith("/"))throw new t("'prefix' must be a string!");if("function"!=typeof s)throw new t("'callback' must be a function!");const i=this._routePrefix;return this._routePrefix=e,s(),this._routePrefix=i,this}static middleware(e){if(!Array.isArray(e))throw new t("'middlewares' must be an array!");if(this._routePrefix)for(const t in this._routes)t.startsWith(this._routePrefix)&&this._routes[t].middlewares.push(...e);else this._routes[this._routePrefix+this._currentRoute].middlewares.push(...e)}static title(e){if("string"!=typeof e)throw new t("'value' must be a string!");this._routes[this._routePrefix+this._currentRoute].title=e}static redirect(e){if("string"!=typeof e||!e.startsWith("/"))throw new t("'to' must be a string starting route with \"/\"!");this._setRouteToURL(e);const{template:i,meta:o}=this._routes[e]??this._defaultRoute;return s.render(i,o)}}})();
@@ -15,7 +15,7 @@
15
15
  <li><a href="/blog">Blog</a></li>
16
16
  <li><a href="/test">404 | Not Found</a></li>
17
17
  <li><a href="/redirection">Redirection</a></li>
18
- <li><a href="https://github.com/ArefShojaei/Lite-PHP/" data-link>Github</a></li>
18
+ <li><a href="https://github.com/ArefShojaei" data-link>Github</a></li>
19
19
  </ul>
20
20
 
21
21
  <div id="root"></div>
@@ -1,18 +1,18 @@
1
- {
2
- "name": "example",
3
- "version": "1.0.0",
4
- "description": "",
5
- "main": "server.js",
6
- "scripts": {
7
- "start": "nodemon server.js"
8
- },
9
- "keywords": [],
10
- "author": "",
11
- "license": "ISC",
12
- "dependencies": {
13
- "express": "^4.21.2"
14
- },
15
- "devDependencies": {
16
- "nodemon": "^3.1.9"
17
- }
18
- }
1
+ {
2
+ "name": "example",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "server.js",
6
+ "scripts": {
7
+ "start": "nodemon server.js"
8
+ },
9
+ "keywords": [],
10
+ "author": "",
11
+ "license": "ISC",
12
+ "dependencies": {
13
+ "express": "^4.21.2"
14
+ },
15
+ "devDependencies": {
16
+ "nodemon": "^3.1.9"
17
+ }
18
+ }
package/package.json CHANGED
@@ -1,43 +1,43 @@
1
- {
2
- "name": "@aref-shojaei/router",
3
- "version": "1.0.1",
4
- "description": "JavaScript Router (SPA)",
5
- "main": "index.js",
6
- "directories": {
7
- "test": "tests",
8
- "src": "src",
9
- "build": "build",
10
- "example": "example"
11
- },
12
- "scripts": {
13
- "test": "jest"
14
- },
15
- "jest": {
16
- "transform": {
17
- "^.+\\.js$": "babel-jest"
18
- }
19
- },
20
- "repository": {
21
- "type": "git",
22
- "url": "git+https://github.com/ArefShojaei/Router.git"
23
- },
24
- "keywords": [
25
- "router",
26
- "routing",
27
- "route",
28
- "spa"
29
- ],
30
- "author": "Aref Shojaei",
31
- "license": "MIT",
32
- "bugs": {
33
- "url": "https://github.com/ArefShojaei/Router/issues"
34
- },
35
- "homepage": "https://github.com/ArefShojaei/Router#readme",
36
- "devDependencies": {
37
- "@babel/core": "^7.26.0",
38
- "@babel/preset-env": "^7.26.0",
39
- "babel-jest": "^29.7.0",
40
- "jest": "^29.7.0",
41
- "jsdom": "^25.0.1"
42
- }
43
- }
1
+ {
2
+ "name": "@aref-shojaei/router",
3
+ "version": "1.0.2",
4
+ "description": "JavaScript Router (SPA)",
5
+ "main": "index.js",
6
+ "directories": {
7
+ "test": "tests",
8
+ "src": "src",
9
+ "build": "build",
10
+ "example": "example"
11
+ },
12
+ "scripts": {
13
+ "test": "jest"
14
+ },
15
+ "jest": {
16
+ "transform": {
17
+ "^.+\\.js$": "babel-jest"
18
+ }
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/ArefShojaei/Router.git"
23
+ },
24
+ "keywords": [
25
+ "router",
26
+ "routing",
27
+ "route",
28
+ "spa"
29
+ ],
30
+ "author": "Aref Shojaei",
31
+ "license": "MIT",
32
+ "bugs": {
33
+ "url": "https://github.com/ArefShojaei/Router/issues"
34
+ },
35
+ "homepage": "https://github.com/ArefShojaei/Router#readme",
36
+ "devDependencies": {
37
+ "@babel/core": "^7.26.0",
38
+ "@babel/preset-env": "^7.26.0",
39
+ "babel-jest": "^29.7.0",
40
+ "jest": "^29.7.0",
41
+ "jsdom": "^25.0.1"
42
+ }
43
+ }
package/src/dto/route.js CHANGED
@@ -1,16 +1,12 @@
1
1
  export default class Route {
2
2
  title = ""
3
-
4
3
  template
5
-
6
4
  middlewares = []
7
-
8
5
  meta = {
9
6
  params : {},
10
7
  query : {},
11
8
  }
12
9
 
13
-
14
10
  constructor({ title, template }) {
15
11
  this.title = title ?? this.title
16
12
  this.template = template
package/src/route.js CHANGED
@@ -17,7 +17,7 @@ export default class Route extends Router {
17
17
  * @param {fucntion} callback
18
18
  * @returns {Route}
19
19
  */
20
- static addRoute(route, callback) {
20
+ static add(route, callback) {
21
21
  if (typeof route !== "string") throw new InvalidArgumentTypeError("'route' must be a string!")
22
22
 
23
23
  if (typeof callback !== "function") throw new InvalidArgumentTypeError("'callback' must be a function!")
@@ -11,6 +11,7 @@ export default class Element {
11
11
  /**
12
12
  * @param {HTMLElement} element
13
13
  * @param {function} callback
14
+ * @returns {void}
14
15
  */
15
16
  static onClick(element, callback) {
16
17
  if (typeof element !== "object") throw new InvalidArgumentTypeError("'element' must be an HTMLElement object!")
@@ -32,6 +32,7 @@ export default class Selector {
32
32
  /**
33
33
  * @param {function} callback
34
34
  * @returns {void}
35
+ * @returns {void}
35
36
  */
36
37
  static each(callback) {
37
38
  if (typeof callback !== "function") throw new InvalidArgumentTypeError("'callback' must be a function!")
@@ -44,6 +45,7 @@ export default class Selector {
44
45
 
45
46
  /**
46
47
  * @param {array} elements
48
+ * @returns {void}
47
49
  */
48
50
  static _setElements(elements) {
49
51
  this.#elements.push(...elements)
@@ -6,7 +6,7 @@ describe("Route tests", () => {
6
6
  const route = "/"
7
7
  const template = () => "Welcome Page"
8
8
 
9
- Route.addRoute(route, template)
9
+ Route.add(route, template)
10
10
 
11
11
  const routes = Route._routes
12
12
 
@@ -19,7 +19,7 @@ describe("Route tests", () => {
19
19
  const template = () => "Login Page"
20
20
 
21
21
  Route.group(routePrefix, () => {
22
- Route.addRoute(route, template)
22
+ Route.add(route, template)
23
23
  })
24
24
 
25
25
  const routes = Route._routes
@@ -32,7 +32,7 @@ describe("Route tests", () => {
32
32
  const route = "/users/{id}"
33
33
  const template = ({ params: { id } }) => `User #${id}`
34
34
 
35
- Route.addRoute(route, template)
35
+ Route.add(route, template)
36
36
 
37
37
  const routes = Route._routes
38
38
 
@@ -45,7 +45,7 @@ describe("Route tests", () => {
45
45
  const template = () => "Admin Page"
46
46
  const loggerMiddleware = () => { message : "Custom Log Message!" }
47
47
 
48
- Route.addRoute(route, template).middleware([loggerMiddleware])
48
+ Route.add(route, template).middleware([loggerMiddleware])
49
49
 
50
50
  const routes = Route._routes
51
51
 
@@ -57,7 +57,7 @@ describe("Route tests", () => {
57
57
  const route = "/product"
58
58
  const template = () => "SPA Page"
59
59
 
60
- Route.addRoute(route, template).title("Custom page title (SPA)")
60
+ Route.add(route, template).title("Custom page title (SPA)")
61
61
 
62
62
  const routes = Route._routes
63
63
 
@@ -70,8 +70,8 @@ describe("Route tests", () => {
70
70
  const redirectionRoute = "/redirection"
71
71
  const distRoute = welcomeRoute
72
72
 
73
- Route.addRoute("/", () => "Welcome Page")
74
- Route.addRoute("/redirection", () => Route.redirect(distRoute))
73
+ Route.add("/", () => "Welcome Page")
74
+ Route.add("/redirection", () => Route.redirect(distRoute))
75
75
 
76
76
  const routes = Route._routes
77
77