@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 +17 -17
- package/build/router.min.js +1 -1
- package/example/assets/js/app.js +4 -4
- package/example/assets/js/router.min.js +1 -1
- package/example/index.html +1 -1
- package/example/package.json +18 -18
- package/package.json +43 -43
- package/src/dto/route.js +0 -4
- package/src/route.js +1 -1
- package/src/utils/element.js +1 -0
- package/src/utils/selector.js +2 -0
- package/tests/unit/route.test.js +7 -7
package/README.md
CHANGED
|
@@ -14,20 +14,20 @@ Router.configure({ window, document })
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
// Single Route
|
|
17
|
-
Route.
|
|
17
|
+
Route.add("/", () => "Welcome Page")
|
|
18
18
|
|
|
19
19
|
// Group Routes
|
|
20
20
|
Route.group("/auth", () => {
|
|
21
|
-
Route.
|
|
22
|
-
Route.
|
|
21
|
+
Route.add("/login", () => "Login Page")
|
|
22
|
+
Route.add("/register", () => "Register Page")
|
|
23
23
|
})
|
|
24
24
|
|
|
25
25
|
// Dynamic Route
|
|
26
|
-
Route.
|
|
26
|
+
Route.add("/users/{id}", ({ params : { name } }) => `User#${id} Page`)
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
// Redirect Routes
|
|
30
|
-
Route.
|
|
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.
|
|
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.
|
|
146
|
+
Route.add("/", () => "Welcome Page")
|
|
147
147
|
|
|
148
148
|
// Group routes
|
|
149
149
|
Route.group("/auth", () => {
|
|
150
|
-
Route.
|
|
151
|
-
Route.
|
|
150
|
+
Route.add("/login", () => "Welcome Page")
|
|
151
|
+
Route.add("/register", () => "Welcome Page")
|
|
152
152
|
})
|
|
153
153
|
|
|
154
154
|
// Dynamic route with params
|
|
155
|
-
Route.
|
|
155
|
+
Route.add("/users/{id}", ({ params : { id } }) => `User #{id} Page`)
|
|
156
156
|
|
|
157
|
-
Route.
|
|
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.
|
|
175
|
+
Route.add("/", () => "Welcome Page").middleware([logger])
|
|
176
176
|
|
|
177
177
|
// Group routes with middleware
|
|
178
178
|
Route.group("/auth", () => {
|
|
179
|
-
Route.
|
|
180
|
-
Route.
|
|
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.
|
|
192
|
-
Route.
|
|
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.
|
|
204
|
+
Route.add("/", () => "Welcome Page").title("Custom Page Title | SPA")
|
|
205
205
|
```
|
package/build/router.min.js
CHANGED
|
@@ -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
|
|
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)}}})();
|
package/example/assets/js/app.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Router.configure({ window, document })
|
|
2
2
|
|
|
3
|
-
Route.
|
|
4
|
-
Route.
|
|
5
|
-
Route.
|
|
6
|
-
Route.
|
|
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
|
|
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)}}})();
|
package/example/index.html
CHANGED
|
@@ -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
|
|
18
|
+
<li><a href="https://github.com/ArefShojaei" data-link>Github</a></li>
|
|
19
19
|
</ul>
|
|
20
20
|
|
|
21
21
|
<div id="root"></div>
|
package/example/package.json
CHANGED
|
@@ -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.
|
|
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
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
|
|
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!")
|
package/src/utils/element.js
CHANGED
|
@@ -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!")
|
package/src/utils/selector.js
CHANGED
|
@@ -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)
|
package/tests/unit/route.test.js
CHANGED
|
@@ -6,7 +6,7 @@ describe("Route tests", () => {
|
|
|
6
6
|
const route = "/"
|
|
7
7
|
const template = () => "Welcome Page"
|
|
8
8
|
|
|
9
|
-
Route.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
74
|
-
Route.
|
|
73
|
+
Route.add("/", () => "Welcome Page")
|
|
74
|
+
Route.add("/redirection", () => Route.redirect(distRoute))
|
|
75
75
|
|
|
76
76
|
const routes = Route._routes
|
|
77
77
|
|