@aref-shojaei/router 1.0.2 → 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/src/route.js CHANGED
@@ -1,104 +1,104 @@
1
- import Router from "./router.js"
2
- import View from "./view.js";
3
- import { InvalidArgumentTypeError } from "./exception.js"
4
- import RouteDTO from "./dto/route.js"
5
-
6
-
7
- /**
8
- * @abstract
9
- */
10
- export default class Route extends Router {
11
- constructor() {
12
- throw new Error(`${new.target.name} class must not be called with \"new\" keyword!`)
13
- }
14
-
15
- /**
16
- * @param {string} route
17
- * @param {fucntion} callback
18
- * @returns {Route}
19
- */
20
- static add(route, callback) {
21
- if (typeof route !== "string") throw new InvalidArgumentTypeError("'route' must be a string!")
22
-
23
- if (typeof callback !== "function") throw new InvalidArgumentTypeError("'callback' must be a function!")
24
-
25
-
26
- this._routes[this._routePrefix + route] = new RouteDTO({ template : callback })
27
-
28
- this._currentRoute = route;
29
-
30
- return this;
31
- }
32
-
33
- /**
34
- * @param {string} prefix
35
- * @param {function} callback
36
- * @returns {Route}
37
- */
38
- static group(prefix, callback) {
39
- if (typeof prefix !== "string" || !prefix.startsWith("/")) throw new InvalidArgumentTypeError("'prefix' must be a string!")
40
-
41
- if (typeof callback !== "function") throw new InvalidArgumentTypeError("'callback' must be a function!")
42
-
43
-
44
- const previousPrefix = this._routePrefix
45
-
46
- this._routePrefix = prefix;
47
-
48
- callback();
49
-
50
- this._routePrefix = previousPrefix
51
-
52
- return this;
53
- }
54
-
55
- /**
56
- * @param {array} middlewares
57
- * @returns {void}
58
- */
59
- static middleware(middlewares) {
60
- if (!Array.isArray(middlewares)) throw new InvalidArgumentTypeError("'middlewares' must be an array!")
61
-
62
- const isDefinedRoutePrefix = this._routePrefix ? true : false
63
-
64
- // Add middlewares to single route
65
- if (!isDefinedRoutePrefix) {
66
- this._routes[this._routePrefix + this._currentRoute]["middlewares"].push(...middlewares);
67
-
68
- return;
69
- }
70
-
71
- // Add middlewares to the group of routes
72
- for (const route in this._routes) {
73
- if (!route.startsWith(this._routePrefix)) continue;
74
-
75
- this._routes[route]["middlewares"].push(...middlewares);
76
- }
77
- }
78
-
79
- /**
80
- * Set route page title
81
- * @param {string} value
82
- * @returns {void}
83
- */
84
- static title(value) {
85
- if (typeof value !== "string") throw new InvalidArgumentTypeError("'value' must be a string!")
86
-
87
- this._routes[this._routePrefix + this._currentRoute]["title"] = value
88
- }
89
-
90
- /**
91
- * @param {string} to - Route pointer
92
- * @returns {string}
93
- */
94
- static redirect(to) {
95
- if (typeof to !== "string" || !to.startsWith("/")) throw new InvalidArgumentTypeError("'to' must be a string starting route with \"/\"!")
96
-
97
-
98
- this._setRouteToURL(to)
99
-
100
- const { template, meta } = this._routes[to] ?? this._defaultRoute;
101
-
102
- return View.render(template, meta)
103
- }
1
+ import Router from "./router.js"
2
+ import View from "./view.js";
3
+ import { InvalidArgumentTypeError } from "./exception.js"
4
+ import RouteDTO from "./dto/route.js"
5
+
6
+
7
+ /**
8
+ * @abstract
9
+ */
10
+ export default class Route extends Router {
11
+ constructor() {
12
+ throw new Error(`${new.target.name} class must not be called with \"new\" keyword!`)
13
+ }
14
+
15
+ /**
16
+ * @param {string} route
17
+ * @param {fucntion} callback
18
+ * @returns {Route}
19
+ */
20
+ static add(route, callback) {
21
+ if (typeof route !== "string") throw new InvalidArgumentTypeError("'route' must be a string!")
22
+
23
+ if (typeof callback !== "function") throw new InvalidArgumentTypeError("'callback' must be a function!")
24
+
25
+
26
+ this._routes[this._routePrefix + route] = new RouteDTO({ template : callback })
27
+
28
+ this._currentRoute = route;
29
+
30
+ return this;
31
+ }
32
+
33
+ /**
34
+ * @param {string} prefix
35
+ * @param {function} callback
36
+ * @returns {Route}
37
+ */
38
+ static group(prefix, callback) {
39
+ if (typeof prefix !== "string" || !prefix.startsWith("/")) throw new InvalidArgumentTypeError("'prefix' must be a string!")
40
+
41
+ if (typeof callback !== "function") throw new InvalidArgumentTypeError("'callback' must be a function!")
42
+
43
+
44
+ const previousPrefix = this._routePrefix
45
+
46
+ this._routePrefix = prefix;
47
+
48
+ callback();
49
+
50
+ this._routePrefix = previousPrefix
51
+
52
+ return this;
53
+ }
54
+
55
+ /**
56
+ * @param {array} middlewares
57
+ * @returns {void}
58
+ */
59
+ static middleware(middlewares) {
60
+ if (!Array.isArray(middlewares)) throw new InvalidArgumentTypeError("'middlewares' must be an array!")
61
+
62
+ const isDefinedRoutePrefix = this._routePrefix ? true : false
63
+
64
+ // Add middlewares to single route
65
+ if (!isDefinedRoutePrefix) {
66
+ this._routes[this._routePrefix + this._currentRoute]["middlewares"].push(...middlewares);
67
+
68
+ return;
69
+ }
70
+
71
+ // Add middlewares to the group of routes
72
+ for (const route in this._routes) {
73
+ if (!route.startsWith(this._routePrefix)) continue;
74
+
75
+ this._routes[route]["middlewares"].push(...middlewares);
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Set route page title
81
+ * @param {string} value
82
+ * @returns {void}
83
+ */
84
+ static title(value) {
85
+ if (typeof value !== "string") throw new InvalidArgumentTypeError("'value' must be a string!")
86
+
87
+ this._routes[this._routePrefix + this._currentRoute]["title"] = value
88
+ }
89
+
90
+ /**
91
+ * @param {string} to - Route pointer
92
+ * @returns {string}
93
+ */
94
+ static redirect(to) {
95
+ if (typeof to !== "string" || !to.startsWith("/")) throw new InvalidArgumentTypeError("'to' must be a string starting route with \"/\"!")
96
+
97
+
98
+ this._setRouteToURL(to)
99
+
100
+ const { template, meta } = this._routes[to] ?? this._defaultRoute;
101
+
102
+ return View.render(template, meta)
103
+ }
104
104
  }
package/src/router.js CHANGED
@@ -1,208 +1,208 @@
1
- import Page from "./page.js"
2
- import View from "./view.js";
3
- import Selector from "./utils/selector.js";
4
- import Element from "./utils/element.js";
5
- import { InvalidArgumentTypeError } from "./exception.js";
6
- import RouteDTO from "./dto/route.js"
7
-
8
-
9
- /**
10
- * @abstract
11
- */
12
- export default class Router {
13
- static _window
14
-
15
- static _document
16
-
17
- static _rootElement = "#root"
18
-
19
- static _routes = {};
20
-
21
- static _currentRoute = "";
22
-
23
- static _routePrefix = "";
24
-
25
- static _defaultRoute = new RouteDTO({
26
- title : "404",
27
- template : () => "404 | Page not found!"
28
- })
29
-
30
-
31
-
32
-
33
- constructor() {
34
- throw new Error(`${new.target.name} class must not be called with \"new\" keyword!`)
35
- }
36
-
37
- /**
38
- * @param {object} params
39
- */
40
- static configure({ window, document, selector }) {
41
- if (typeof window !== "object") throw new InvalidArgumentTypeError("'window' must be a Window object!")
42
-
43
- if (typeof document !== "object") throw new InvalidArgumentTypeError("'document' must be a Document object!")
44
-
45
- if (selector && typeof selector !== "string") throw new InvalidArgumentTypeError("'selector' must be a string!")
46
-
47
-
48
- if (selector) this._rootElement = selector
49
-
50
-
51
- this._window = window
52
-
53
- this._document = document
54
- }
55
-
56
- /**
57
- * @param {string} target
58
- * @returns {object}
59
- */
60
- static #findRoute(target) {
61
- for (const route in this._routes) {
62
- const regex = new RegExp(`^${route.replace(/\{(\w+)\}/g, '(?<$1>[^/{}]+)')}$`);
63
-
64
- if (!regex.test(target)) continue
65
-
66
- const { groups } = regex.exec(target)
67
-
68
- this.#setRouteQuery(route)
69
-
70
- this.#setRouteParams(route, groups)
71
-
72
- return this._routes[route]
73
- }
74
- }
75
-
76
- /**
77
- * @param {string} route
78
- * @returns {boolean}
79
- */
80
- static #setRouteQuery(route) {
81
- const query = {}
82
-
83
- const { search } = location
84
-
85
- if (!search.length) return false
86
-
87
-
88
- search.slice(1).split("&").forEach(item => {
89
- const [key, value] = item.split("=")
90
-
91
- query[key] = value
92
- })
93
-
94
- this._routes[route]["meta"]["query"] = {...query}
95
- }
96
-
97
- /**
98
- * @param {string} route
99
- * @param {object} params
100
- * @returns {void}
101
- */
102
- static #setRouteParams(route, params) {
103
- this._routes[route]["meta"]["params"] = {...params}
104
- }
105
-
106
- /**
107
- * @param {string} route
108
- * @param {Window} window
109
- * @returns {void}
110
- */
111
- static _setRouteToURL(route) {
112
- if (typeof route !== "string" || !route.startsWith("/")) throw new InvalidArgumentTypeError("'route' must be a string starting with \"/\"!")
113
-
114
- this._window.history.pushState({}, "", route)
115
- }
116
-
117
- /**
118
- * @param {string} route
119
- * @returns {void}
120
- */
121
- static #injectTemplateToDOM(route) {
122
- try {
123
- const { title, template, middlewares, meta } = this.#findRoute(route) ?? this._defaultRoute
124
-
125
- Page.setTitle(title)
126
-
127
- this.#applyMiddlewares(middlewares)
128
-
129
- this._document.querySelector(this._rootElement).innerHTML = View.render(template, meta)
130
- } catch (error) {
131
- console.error("Error to inject route template:", route, error);
132
- }
133
- }
134
-
135
- /**
136
- * @returns {void}
137
- */
138
- static #activeHistroyNavigation() {
139
- this._window.addEventListener("popstate", event => {
140
- const route = event.target.location.pathname
141
-
142
- this.#injectTemplateToDOM(route)
143
- })
144
- }
145
-
146
- /**
147
- * @returns {void}
148
- */
149
- static #changeRoutebyRequest() {
150
- const { pathname } = this._window.location
151
-
152
- this.#injectTemplateToDOM(pathname)
153
- }
154
-
155
- /**
156
- * @returns {void}
157
- */
158
- static #changeRoutebyLink() {
159
- Selector.findAll("a", this._document).each(anchor => {
160
- Element.onClick(anchor, (event) => {
161
- if (event.target.hasAttribute("data-link")) return false
162
-
163
- event.preventDefault()
164
-
165
- const route = event.target.getAttribute("href")
166
-
167
- this._setRouteToURL(route)
168
-
169
- this.#injectTemplateToDOM(route)
170
- })
171
- })
172
- }
173
-
174
- /**
175
- * Executes middleware functions
176
- * @param {array} middlewares
177
- * @returns {void}
178
- */
179
- static #applyMiddlewares(middlewares) {
180
- try {
181
- middlewares.length && middlewares.forEach(middleware => middleware())
182
- } catch (error) {
183
- console.error("Error executing middleware:", error);
184
- }
185
- }
186
-
187
- /**
188
- * Initializes the router
189
- * @param {function} callback
190
- * @returns {void}
191
- */
192
- static run(callback = () => {}) {
193
- if (typeof callback !== "function") throw new InvalidArgumentTypeError("'callback' must be a function!")
194
-
195
-
196
- Page.setDocument(this._document)
197
-
198
- Page.setRootTitle(this._document.title)
199
-
200
- this.#activeHistroyNavigation()
201
-
202
- this.#changeRoutebyRequest()
203
-
204
- this.#changeRoutebyLink()
205
-
206
- callback()
207
- }
1
+ import Page from "./page.js"
2
+ import View from "./view.js";
3
+ import Selector from "./utils/selector.js";
4
+ import Element from "./utils/element.js";
5
+ import { InvalidArgumentTypeError } from "./exception.js";
6
+ import RouteDTO from "./dto/route.js"
7
+
8
+
9
+ /**
10
+ * @abstract
11
+ */
12
+ export default class Router {
13
+ static _window
14
+
15
+ static _document
16
+
17
+ static _rootElement = "#root"
18
+
19
+ static _routes = {};
20
+
21
+ static _currentRoute = "";
22
+
23
+ static _routePrefix = "";
24
+
25
+ static _defaultRoute = new RouteDTO({
26
+ title : "404",
27
+ template : () => "404 | Page not found!"
28
+ })
29
+
30
+
31
+
32
+
33
+ constructor() {
34
+ throw new Error(`${new.target.name} class must not be called with \"new\" keyword!`)
35
+ }
36
+
37
+ /**
38
+ * @param {object} params
39
+ */
40
+ static configure({ window, document, selector }) {
41
+ if (typeof window !== "object") throw new InvalidArgumentTypeError("'window' must be a Window object!")
42
+
43
+ if (typeof document !== "object") throw new InvalidArgumentTypeError("'document' must be a Document object!")
44
+
45
+ if (selector && typeof selector !== "string") throw new InvalidArgumentTypeError("'selector' must be a string!")
46
+
47
+
48
+ if (selector) this._rootElement = selector
49
+
50
+
51
+ this._window = window
52
+
53
+ this._document = document
54
+ }
55
+
56
+ /**
57
+ * @param {string} target
58
+ * @returns {object}
59
+ */
60
+ static #findRoute(target) {
61
+ for (const route in this._routes) {
62
+ const regex = new RegExp(`^${route.replace(/\{(\w+)\}/g, '(?<$1>[^/{}]+)')}$`);
63
+
64
+ if (!regex.test(target)) continue
65
+
66
+ const { groups } = regex.exec(target)
67
+
68
+ this.#setRouteQuery(route)
69
+
70
+ this.#setRouteParams(route, groups)
71
+
72
+ return this._routes[route]
73
+ }
74
+ }
75
+
76
+ /**
77
+ * @param {string} route
78
+ * @returns {boolean}
79
+ */
80
+ static #setRouteQuery(route) {
81
+ const query = {}
82
+
83
+ const { search } = location
84
+
85
+ if (!search.length) return false
86
+
87
+
88
+ search.slice(1).split("&").forEach(item => {
89
+ const [key, value] = item.split("=")
90
+
91
+ query[key] = value
92
+ })
93
+
94
+ this._routes[route]["meta"]["query"] = {...query}
95
+ }
96
+
97
+ /**
98
+ * @param {string} route
99
+ * @param {object} params
100
+ * @returns {void}
101
+ */
102
+ static #setRouteParams(route, params) {
103
+ this._routes[route]["meta"]["params"] = {...params}
104
+ }
105
+
106
+ /**
107
+ * @param {string} route
108
+ * @param {Window} window
109
+ * @returns {void}
110
+ */
111
+ static _setRouteToURL(route) {
112
+ if (typeof route !== "string" || !route.startsWith("/")) throw new InvalidArgumentTypeError("'route' must be a string starting with \"/\"!")
113
+
114
+ this._window.history.pushState({}, "", route)
115
+ }
116
+
117
+ /**
118
+ * @param {string} route
119
+ * @returns {void}
120
+ */
121
+ static #injectTemplateToDOM(route) {
122
+ try {
123
+ const { title, template, middlewares, meta } = this.#findRoute(route) ?? this._defaultRoute
124
+
125
+ Page.setTitle(title)
126
+
127
+ this.#applyMiddlewares(middlewares)
128
+
129
+ this._document.querySelector(this._rootElement).innerHTML = View.render(template, meta)
130
+ } catch (error) {
131
+ console.error("Error to inject route template:", route, error);
132
+ }
133
+ }
134
+
135
+ /**
136
+ * @returns {void}
137
+ */
138
+ static #activeHistroyNavigation() {
139
+ this._window.addEventListener("popstate", event => {
140
+ const route = event.target.location.pathname
141
+
142
+ this.#injectTemplateToDOM(route)
143
+ })
144
+ }
145
+
146
+ /**
147
+ * @returns {void}
148
+ */
149
+ static #changeRoutebyRequest() {
150
+ const { pathname } = this._window.location
151
+
152
+ this.#injectTemplateToDOM(pathname)
153
+ }
154
+
155
+ /**
156
+ * @returns {void}
157
+ */
158
+ static #changeRoutebyLink() {
159
+ Selector.findAll("a", this._document).each(anchor => {
160
+ Element.onClick(anchor, (event) => {
161
+ if (event.target.hasAttribute("data-link")) return false
162
+
163
+ event.preventDefault()
164
+
165
+ const route = event.target.getAttribute("href")
166
+
167
+ this._setRouteToURL(route)
168
+
169
+ this.#injectTemplateToDOM(route)
170
+ })
171
+ })
172
+ }
173
+
174
+ /**
175
+ * Executes middleware functions
176
+ * @param {array} middlewares
177
+ * @returns {void}
178
+ */
179
+ static #applyMiddlewares(middlewares) {
180
+ try {
181
+ middlewares.length && middlewares.forEach(middleware => middleware())
182
+ } catch (error) {
183
+ console.error("Error executing middleware:", error);
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Initializes the router
189
+ * @param {function} callback
190
+ * @returns {void}
191
+ */
192
+ static run(callback = () => {}) {
193
+ if (typeof callback !== "function") throw new InvalidArgumentTypeError("'callback' must be a function!")
194
+
195
+
196
+ Page.setDocument(this._document)
197
+
198
+ Page.setRootTitle(this._document.title)
199
+
200
+ this.#activeHistroyNavigation()
201
+
202
+ this.#changeRoutebyRequest()
203
+
204
+ this.#changeRoutebyLink()
205
+
206
+ callback()
207
+ }
208
208
  }
@@ -1,24 +1,24 @@
1
- import { InvalidArgumentTypeError } from "../exception.js"
2
-
3
- /**
4
- * @abstract
5
- */
6
- export default class Element {
7
- constructor() {
8
- throw new Error(`${new.target.name} class must not be called with \"new\" keyword!`)
9
- }
10
-
11
- /**
12
- * @param {HTMLElement} element
13
- * @param {function} callback
14
- * @returns {void}
15
- */
16
- static onClick(element, callback) {
17
- if (typeof element !== "object") throw new InvalidArgumentTypeError("'element' must be an HTMLElement object!")
18
-
19
- if (typeof callback !== "function") throw new InvalidArgumentTypeError("'callback' must be a function!")
20
-
21
-
22
- element.addEventListener("click", callback)
23
- }
1
+ import { InvalidArgumentTypeError } from "../exception.js"
2
+
3
+ /**
4
+ * @abstract
5
+ */
6
+ export default class Element {
7
+ constructor() {
8
+ throw new Error(`${new.target.name} class must not be called with \"new\" keyword!`)
9
+ }
10
+
11
+ /**
12
+ * @param {HTMLElement} element
13
+ * @param {function} callback
14
+ * @returns {void}
15
+ */
16
+ static onClick(element, callback) {
17
+ if (typeof element !== "object") throw new InvalidArgumentTypeError("'element' must be an HTMLElement object!")
18
+
19
+ if (typeof callback !== "function") throw new InvalidArgumentTypeError("'callback' must be a function!")
20
+
21
+
22
+ element.addEventListener("click", callback)
23
+ }
24
24
  }