valyrian.js 7.0.4 → 7.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,4 @@
1
1
  import { Component, POJOComponent, VnodeComponentInterface } from "valyrian.js";
2
- interface Path {
3
- method: string;
4
- path: string;
5
- middlewares: Middlewares;
6
- params: string[];
7
- regexp: RegExp;
8
- }
9
2
  interface Request {
10
3
  params: Record<string, any>;
11
4
  query: Record<string, any>;
@@ -19,6 +12,13 @@ interface Middleware {
19
12
  }
20
13
  interface Middlewares extends Array<Middleware> {
21
14
  }
15
+ interface Path {
16
+ method: string;
17
+ path: string;
18
+ middlewares: Middlewares;
19
+ params: string[];
20
+ regexp: RegExp;
21
+ }
22
22
  interface RouterInterface {
23
23
  paths: Path[];
24
24
  container: Element | string | null;
@@ -42,13 +42,15 @@ export declare class Router implements RouterInterface {
42
42
  path: string;
43
43
  params: Record<string, string | number | any>;
44
44
  matches: string[];
45
- add(path: string, ...args: Middlewares): Router;
46
- use(...args: Middlewares | Router[]): Router;
45
+ pathPrefix: string;
46
+ constructor(pathPrefix?: string);
47
+ add(path: string, ...middlewares: Middlewares): Router;
48
+ use(...middlewares: Middlewares | Router[] | string[]): Router;
47
49
  routes(): string[];
48
- go(path: string, parentComponent?: Component): Promise<string | void>;
50
+ go(path: string, parentComponent?: Component, preventPushState?: boolean): Promise<string | void>;
49
51
  getOnClickHandler(url: string): (e: MouseEvent) => void;
50
52
  }
51
- export declare function redirect(url: string): any;
52
- export declare function mountRouter(elementContainer: any, router: any): void;
53
+ export declare function redirect(url: string, parentComponent?: Component, preventPushState?: boolean): string | void;
54
+ export declare function mountRouter(elementContainer: string | any, router: Router): void;
53
55
  export {};
54
56
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/router/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/router/index.ts"],"names":[],"mappings":"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"}
@@ -28,15 +28,17 @@ var import_valyrian = require("valyrian.js");
28
28
  function flat(array) {
29
29
  return Array.isArray(array) ? array.flat(Infinity) : [array];
30
30
  }
31
- var addPath = (router, method, path, middlewares) => {
32
- if (middlewares.length === 0) {
33
- return;
31
+ var addPath = ({
32
+ router,
33
+ method,
34
+ path,
35
+ middlewares
36
+ }) => {
37
+ if (!method || !path || !Array.isArray(middlewares) || middlewares.length === 0) {
38
+ throw new Error(`Invalid route input: ${method} ${path} ${middlewares}`);
34
39
  }
35
40
  let realpath = path.replace(/(\S)(\/+)$/, "$1");
36
- let params = realpath.match(/:(\w+)?/gi) || [];
37
- for (let param in params) {
38
- params[param] = params[param].slice(1);
39
- }
41
+ let params = (realpath.match(/:(\w+)?/gi) || []).map((param) => param.slice(1));
40
42
  let regexpPath = "^" + realpath.replace(/:(\w+)/gi, "([^\\/\\s]+)") + "$";
41
43
  router.paths.push({
42
44
  method,
@@ -47,41 +49,27 @@ var addPath = (router, method, path, middlewares) => {
47
49
  });
48
50
  };
49
51
  function parseQuery(queryParts) {
50
- let parts = queryParts ? queryParts.split("&", 20) : [];
52
+ let parts = queryParts ? queryParts.split("&") : [];
51
53
  let query = {};
52
- let i = 0;
53
- let nameValue;
54
- for (; i < parts.length; i++) {
55
- nameValue = parts[i].split("=", 2);
56
- query[nameValue[0]] = nameValue[1];
54
+ for (let nameValue of parts) {
55
+ let [name, value] = nameValue.split("=", 2);
56
+ query[name] = value || "";
57
57
  }
58
58
  return query;
59
59
  }
60
60
  function searchMiddlewares(router, path) {
61
- let i;
62
- let k;
63
- let item;
64
- let match;
65
- let key;
66
61
  let middlewares = [];
67
62
  let params = {};
68
63
  let matches = [];
69
- router.params = {};
70
- router.path = "";
71
- router.matches = [];
72
- for (i = 0; i < router.paths.length; i++) {
73
- item = router.paths[i];
74
- match = item.regexp.exec(path);
64
+ for (let item of router.paths) {
65
+ let match = item.regexp.exec(path);
75
66
  if (Array.isArray(match)) {
76
67
  middlewares.push(...item.middlewares);
77
68
  match.shift();
78
- for (k = 0; k < item.params.length; k++) {
79
- key = item.params[k];
80
- params[key] = match.shift();
81
- }
82
- while (match.length) {
83
- matches.push(match.shift());
69
+ for (let [index, key] of item.params.entries()) {
70
+ params[key] = match[index];
84
71
  }
72
+ matches.push(...match);
85
73
  if (item.method === "add") {
86
74
  router.path = item.path;
87
75
  break;
@@ -93,8 +81,7 @@ function searchMiddlewares(router, path) {
93
81
  return middlewares;
94
82
  }
95
83
  async function searchComponent(router, middlewares) {
96
- let response;
97
- let req = {
84
+ const request = {
98
85
  params: router.params,
99
86
  query: router.query,
100
87
  url: router.url,
@@ -105,9 +92,9 @@ async function searchComponent(router, middlewares) {
105
92
  return false;
106
93
  }
107
94
  };
108
- let i = 0;
109
- for (; i < middlewares.length; i++) {
110
- response = await middlewares[i](req, response);
95
+ let response;
96
+ for (const middleware of middlewares) {
97
+ response = await middleware(request, response);
111
98
  if (response !== void 0 && ((0, import_valyrian.isComponent)(response) || (0, import_valyrian.isVnodeComponent)(response))) {
112
99
  return response;
113
100
  }
@@ -125,59 +112,56 @@ var Router = class {
125
112
  path = "";
126
113
  params = {};
127
114
  matches = [];
128
- add(path, ...args) {
129
- addPath(this, "add", path, args);
115
+ pathPrefix = "";
116
+ constructor(pathPrefix = "") {
117
+ this.pathPrefix = pathPrefix;
118
+ }
119
+ add(path, ...middlewares) {
120
+ addPath({ router: this, method: "add", path: `${this.pathPrefix}${path}`, middlewares });
130
121
  return this;
131
122
  }
132
- use(...args) {
133
- let path = typeof args[0] === "string" ? args.shift() : "/";
134
- let i;
135
- let k;
136
- let subrouter;
137
- let item;
138
- let subpath;
139
- for (i = 0; i < args.length; i++) {
140
- subrouter = args[i];
141
- if (typeof subrouter === "function") {
142
- addPath(this, "use", `${path}.*`, [subrouter]);
143
- } else if (subrouter.paths) {
144
- for (k = 0; k < subrouter.paths.length; k++) {
145
- item = subrouter.paths[k];
146
- subpath = `${path}${item.path}`.replace(/^\/\//, "/");
147
- addPath(this, item.method, subpath, item.middlewares);
123
+ use(...middlewares) {
124
+ let path = `${this.pathPrefix}${typeof middlewares[0] === "string" ? middlewares.shift() : "/"}`;
125
+ for (const item of middlewares) {
126
+ if (item instanceof Router) {
127
+ const subrouter = item;
128
+ for (const subpath of subrouter.paths) {
129
+ addPath({
130
+ router: this,
131
+ method: subpath.method,
132
+ path: `${path}${subpath.path}`.replace(/^\/\//, "/"),
133
+ middlewares: subpath.middlewares
134
+ });
148
135
  }
136
+ continue;
137
+ }
138
+ if (typeof item === "function") {
139
+ addPath({ router: this, method: "use", path: `${path}.*`, middlewares: [item] });
149
140
  }
150
141
  }
151
142
  return this;
152
143
  }
153
144
  routes() {
154
- let routes = [];
155
- this.paths.forEach((path) => {
156
- if (path.method === "add") {
157
- routes.push(path.path);
158
- }
159
- });
160
- return routes;
145
+ return this.paths.filter((path) => path.method === "add").map((path) => path.path);
161
146
  }
162
- async go(path, parentComponent) {
147
+ async go(path, parentComponent, preventPushState = false) {
163
148
  if (!path) {
164
149
  throw new Error("router.url.required");
165
150
  }
166
- let parts = path.split("?", 2);
167
- let urlParts = parts[0].replace(/(.+)\/$/, "$1");
168
- let queryParts = parts[1];
169
- this.url = path;
170
- this.query = parseQuery(queryParts);
171
- let middlewares = searchMiddlewares(this, urlParts);
151
+ const constructedPath = `${this.pathPrefix}${path}`;
152
+ const parts = constructedPath.split("?", 2);
153
+ this.url = constructedPath;
154
+ this.query = parseQuery(parts[1]);
155
+ const middlewares = searchMiddlewares(this, parts[0].replace(/(.+)\/$/, "$1"));
172
156
  let component = await searchComponent(this, middlewares);
173
157
  if (component === false) {
174
158
  return;
175
159
  }
176
160
  if (!component) {
177
- throw new Error(`The url ${path} requested wasn't found`);
161
+ throw new Error(`The url ${constructedPath} requested wasn't found`);
178
162
  }
179
163
  if ((0, import_valyrian.isComponent)(parentComponent) || (0, import_valyrian.isVnodeComponent)(parentComponent)) {
180
- let childComponent = (0, import_valyrian.isVnodeComponent)(component) ? component : (0, import_valyrian.v)(component, {});
164
+ const childComponent = (0, import_valyrian.isVnodeComponent)(component) ? component : (0, import_valyrian.v)(component, {});
181
165
  if ((0, import_valyrian.isVnodeComponent)(parentComponent)) {
182
166
  parentComponent.children.push(childComponent);
183
167
  component = parentComponent;
@@ -185,8 +169,8 @@ var Router = class {
185
169
  component = (0, import_valyrian.v)(parentComponent, {}, childComponent);
186
170
  }
187
171
  }
188
- if (!import_valyrian.isNodeJs) {
189
- window.history.pushState(null, "", path);
172
+ if (!import_valyrian.isNodeJs && !preventPushState) {
173
+ window.history.pushState(null, "", constructedPath);
190
174
  }
191
175
  if (this.container) {
192
176
  return (0, import_valyrian.mount)(this.container, component);
@@ -202,18 +186,19 @@ var Router = class {
202
186
  }
203
187
  };
204
188
  var localRedirect;
205
- function redirect(url) {
189
+ function redirect(url, parentComponent, preventPushState = false) {
206
190
  if (!localRedirect) {
207
191
  throw new Error("router.redirect.not.found");
208
192
  }
209
- return localRedirect(url);
193
+ return localRedirect(url, parentComponent, preventPushState);
210
194
  }
211
195
  function mountRouter(elementContainer, router) {
212
196
  router.container = elementContainer;
213
197
  localRedirect = router.go.bind(router);
214
198
  if (!import_valyrian.isNodeJs) {
215
199
  let onPopStateGoToRoute = function() {
216
- router.go(document.location.pathname);
200
+ let pathWithoutPrefix = document.location.pathname.replace(router.pathPrefix, "");
201
+ router.go(pathWithoutPrefix, void 0, true);
217
202
  };
218
203
  window.addEventListener("popstate", onPopStateGoToRoute, false);
219
204
  onPopStateGoToRoute();
@@ -11,15 +11,17 @@ import {
11
11
  function flat(array) {
12
12
  return Array.isArray(array) ? array.flat(Infinity) : [array];
13
13
  }
14
- var addPath = (router, method, path, middlewares) => {
15
- if (middlewares.length === 0) {
16
- return;
14
+ var addPath = ({
15
+ router,
16
+ method,
17
+ path,
18
+ middlewares
19
+ }) => {
20
+ if (!method || !path || !Array.isArray(middlewares) || middlewares.length === 0) {
21
+ throw new Error(`Invalid route input: ${method} ${path} ${middlewares}`);
17
22
  }
18
23
  let realpath = path.replace(/(\S)(\/+)$/, "$1");
19
- let params = realpath.match(/:(\w+)?/gi) || [];
20
- for (let param in params) {
21
- params[param] = params[param].slice(1);
22
- }
24
+ let params = (realpath.match(/:(\w+)?/gi) || []).map((param) => param.slice(1));
23
25
  let regexpPath = "^" + realpath.replace(/:(\w+)/gi, "([^\\/\\s]+)") + "$";
24
26
  router.paths.push({
25
27
  method,
@@ -30,41 +32,27 @@ var addPath = (router, method, path, middlewares) => {
30
32
  });
31
33
  };
32
34
  function parseQuery(queryParts) {
33
- let parts = queryParts ? queryParts.split("&", 20) : [];
35
+ let parts = queryParts ? queryParts.split("&") : [];
34
36
  let query = {};
35
- let i = 0;
36
- let nameValue;
37
- for (; i < parts.length; i++) {
38
- nameValue = parts[i].split("=", 2);
39
- query[nameValue[0]] = nameValue[1];
37
+ for (let nameValue of parts) {
38
+ let [name, value] = nameValue.split("=", 2);
39
+ query[name] = value || "";
40
40
  }
41
41
  return query;
42
42
  }
43
43
  function searchMiddlewares(router, path) {
44
- let i;
45
- let k;
46
- let item;
47
- let match;
48
- let key;
49
44
  let middlewares = [];
50
45
  let params = {};
51
46
  let matches = [];
52
- router.params = {};
53
- router.path = "";
54
- router.matches = [];
55
- for (i = 0; i < router.paths.length; i++) {
56
- item = router.paths[i];
57
- match = item.regexp.exec(path);
47
+ for (let item of router.paths) {
48
+ let match = item.regexp.exec(path);
58
49
  if (Array.isArray(match)) {
59
50
  middlewares.push(...item.middlewares);
60
51
  match.shift();
61
- for (k = 0; k < item.params.length; k++) {
62
- key = item.params[k];
63
- params[key] = match.shift();
64
- }
65
- while (match.length) {
66
- matches.push(match.shift());
52
+ for (let [index, key] of item.params.entries()) {
53
+ params[key] = match[index];
67
54
  }
55
+ matches.push(...match);
68
56
  if (item.method === "add") {
69
57
  router.path = item.path;
70
58
  break;
@@ -76,8 +64,7 @@ function searchMiddlewares(router, path) {
76
64
  return middlewares;
77
65
  }
78
66
  async function searchComponent(router, middlewares) {
79
- let response;
80
- let req = {
67
+ const request = {
81
68
  params: router.params,
82
69
  query: router.query,
83
70
  url: router.url,
@@ -88,9 +75,9 @@ async function searchComponent(router, middlewares) {
88
75
  return false;
89
76
  }
90
77
  };
91
- let i = 0;
92
- for (; i < middlewares.length; i++) {
93
- response = await middlewares[i](req, response);
78
+ let response;
79
+ for (const middleware of middlewares) {
80
+ response = await middleware(request, response);
94
81
  if (response !== void 0 && (isComponent(response) || isVnodeComponent(response))) {
95
82
  return response;
96
83
  }
@@ -108,59 +95,56 @@ var Router = class {
108
95
  path = "";
109
96
  params = {};
110
97
  matches = [];
111
- add(path, ...args) {
112
- addPath(this, "add", path, args);
98
+ pathPrefix = "";
99
+ constructor(pathPrefix = "") {
100
+ this.pathPrefix = pathPrefix;
101
+ }
102
+ add(path, ...middlewares) {
103
+ addPath({ router: this, method: "add", path: `${this.pathPrefix}${path}`, middlewares });
113
104
  return this;
114
105
  }
115
- use(...args) {
116
- let path = typeof args[0] === "string" ? args.shift() : "/";
117
- let i;
118
- let k;
119
- let subrouter;
120
- let item;
121
- let subpath;
122
- for (i = 0; i < args.length; i++) {
123
- subrouter = args[i];
124
- if (typeof subrouter === "function") {
125
- addPath(this, "use", `${path}.*`, [subrouter]);
126
- } else if (subrouter.paths) {
127
- for (k = 0; k < subrouter.paths.length; k++) {
128
- item = subrouter.paths[k];
129
- subpath = `${path}${item.path}`.replace(/^\/\//, "/");
130
- addPath(this, item.method, subpath, item.middlewares);
106
+ use(...middlewares) {
107
+ let path = `${this.pathPrefix}${typeof middlewares[0] === "string" ? middlewares.shift() : "/"}`;
108
+ for (const item of middlewares) {
109
+ if (item instanceof Router) {
110
+ const subrouter = item;
111
+ for (const subpath of subrouter.paths) {
112
+ addPath({
113
+ router: this,
114
+ method: subpath.method,
115
+ path: `${path}${subpath.path}`.replace(/^\/\//, "/"),
116
+ middlewares: subpath.middlewares
117
+ });
131
118
  }
119
+ continue;
120
+ }
121
+ if (typeof item === "function") {
122
+ addPath({ router: this, method: "use", path: `${path}.*`, middlewares: [item] });
132
123
  }
133
124
  }
134
125
  return this;
135
126
  }
136
127
  routes() {
137
- let routes = [];
138
- this.paths.forEach((path) => {
139
- if (path.method === "add") {
140
- routes.push(path.path);
141
- }
142
- });
143
- return routes;
128
+ return this.paths.filter((path) => path.method === "add").map((path) => path.path);
144
129
  }
145
- async go(path, parentComponent) {
130
+ async go(path, parentComponent, preventPushState = false) {
146
131
  if (!path) {
147
132
  throw new Error("router.url.required");
148
133
  }
149
- let parts = path.split("?", 2);
150
- let urlParts = parts[0].replace(/(.+)\/$/, "$1");
151
- let queryParts = parts[1];
152
- this.url = path;
153
- this.query = parseQuery(queryParts);
154
- let middlewares = searchMiddlewares(this, urlParts);
134
+ const constructedPath = `${this.pathPrefix}${path}`;
135
+ const parts = constructedPath.split("?", 2);
136
+ this.url = constructedPath;
137
+ this.query = parseQuery(parts[1]);
138
+ const middlewares = searchMiddlewares(this, parts[0].replace(/(.+)\/$/, "$1"));
155
139
  let component = await searchComponent(this, middlewares);
156
140
  if (component === false) {
157
141
  return;
158
142
  }
159
143
  if (!component) {
160
- throw new Error(`The url ${path} requested wasn't found`);
144
+ throw new Error(`The url ${constructedPath} requested wasn't found`);
161
145
  }
162
146
  if (isComponent(parentComponent) || isVnodeComponent(parentComponent)) {
163
- let childComponent = isVnodeComponent(component) ? component : v(component, {});
147
+ const childComponent = isVnodeComponent(component) ? component : v(component, {});
164
148
  if (isVnodeComponent(parentComponent)) {
165
149
  parentComponent.children.push(childComponent);
166
150
  component = parentComponent;
@@ -168,8 +152,8 @@ var Router = class {
168
152
  component = v(parentComponent, {}, childComponent);
169
153
  }
170
154
  }
171
- if (!isNodeJs) {
172
- window.history.pushState(null, "", path);
155
+ if (!isNodeJs && !preventPushState) {
156
+ window.history.pushState(null, "", constructedPath);
173
157
  }
174
158
  if (this.container) {
175
159
  return mount(this.container, component);
@@ -185,18 +169,19 @@ var Router = class {
185
169
  }
186
170
  };
187
171
  var localRedirect;
188
- function redirect(url) {
172
+ function redirect(url, parentComponent, preventPushState = false) {
189
173
  if (!localRedirect) {
190
174
  throw new Error("router.redirect.not.found");
191
175
  }
192
- return localRedirect(url);
176
+ return localRedirect(url, parentComponent, preventPushState);
193
177
  }
194
178
  function mountRouter(elementContainer, router) {
195
179
  router.container = elementContainer;
196
180
  localRedirect = router.go.bind(router);
197
181
  if (!isNodeJs) {
198
182
  let onPopStateGoToRoute = function() {
199
- router.go(document.location.pathname);
183
+ let pathWithoutPrefix = document.location.pathname.replace(router.pathPrefix, "");
184
+ router.go(pathWithoutPrefix, void 0, true);
200
185
  };
201
186
  window.addEventListener("popstate", onPopStateGoToRoute, false);
202
187
  onPopStateGoToRoute();
@@ -3,6 +3,7 @@ import {
3
3
  Component,
4
4
  POJOComponent,
5
5
  VnodeComponentInterface,
6
+ VnodeWithDom,
6
7
  directive,
7
8
  isComponent,
8
9
  isNodeJs,
@@ -12,14 +13,6 @@ import {
12
13
  v
13
14
  } from "valyrian.js";
14
15
 
15
- interface Path {
16
- method: string;
17
- path: string;
18
- middlewares: Middlewares;
19
- params: string[];
20
- regexp: RegExp;
21
- }
22
-
23
16
  interface Request {
24
17
  params: Record<string, any>;
25
18
  query: Record<string, any>;
@@ -42,6 +35,14 @@ interface Middleware {
42
35
 
43
36
  interface Middlewares extends Array<Middleware> {}
44
37
 
38
+ interface Path {
39
+ method: string;
40
+ path: string;
41
+ middlewares: Middlewares;
42
+ params: string[];
43
+ regexp: RegExp;
44
+ }
45
+
45
46
  interface RouterInterface {
46
47
  paths: Path[];
47
48
  container: Element | string | null;
@@ -61,25 +62,39 @@ interface RouterInterface {
61
62
  go(path: string, parentComponent?: Component | POJOComponent | VnodeComponentInterface): Promise<string | void>;
62
63
  }
63
64
 
65
+ interface RedirectFunction {
66
+ // eslint-disable-next-line no-unused-vars
67
+ (url: string, parentComponent?: Component, preventPushState?: boolean): string | void;
68
+ }
69
+
64
70
  function flat(array: any) {
65
71
  return Array.isArray(array) ? array.flat(Infinity) : [array];
66
72
  }
67
73
 
68
- let addPath = (router: Router, method: string, path: string, middlewares: any[]) => {
69
- if (middlewares.length === 0) {
70
- return;
74
+ const addPath = ({
75
+ router,
76
+ method,
77
+ path,
78
+ middlewares
79
+ }: {
80
+ router: Router;
81
+ method: string;
82
+ path: string;
83
+ middlewares: Middleware[];
84
+ }): void => {
85
+ if (!method || !path || !Array.isArray(middlewares) || middlewares.length === 0) {
86
+ throw new Error(`Invalid route input: ${method} ${path} ${middlewares}`);
71
87
  }
72
88
 
89
+ // Trim trailing slashes from the path
73
90
  let realpath = path.replace(/(\S)(\/+)$/, "$1");
74
91
 
75
- // Find the express like params
76
- let params = realpath.match(/:(\w+)?/gi) || [];
77
-
78
- // Set the names of the params found
79
- for (let param in params) {
80
- params[param] = params[param].slice(1);
81
- }
92
+ // Find the express-like params in the path
93
+ let params = (realpath.match(/:(\w+)?/gi) || [])
94
+ // Set the names of the params found
95
+ .map((param) => param.slice(1));
82
96
 
97
+ // Generate a regular expression to match the path
83
98
  let regexpPath = "^" + realpath.replace(/:(\w+)/gi, "([^\\/\\s]+)") + "$";
84
99
 
85
100
  router.paths.push({
@@ -91,52 +106,43 @@ let addPath = (router: Router, method: string, path: string, middlewares: any[])
91
106
  });
92
107
  };
93
108
 
94
- function parseQuery(queryParts?: string) {
95
- let parts = queryParts ? queryParts.split("&", 20) : [];
96
- let query: Record<string, any> = {};
97
- let i = 0;
98
- let nameValue;
109
+ // Parse a query string into an object
110
+ function parseQuery(queryParts?: string): Record<string, string> {
111
+ // Split the query string into an array of name-value pairs
112
+ let parts = queryParts ? queryParts.split("&") : [];
113
+ let query: Record<string, string> = {};
99
114
 
100
- for (; i < parts.length; i++) {
101
- nameValue = parts[i].split("=", 2);
102
- query[nameValue[0]] = nameValue[1];
115
+ // Iterate over the name-value pairs and add them to the query object
116
+ for (let nameValue of parts) {
117
+ let [name, value] = nameValue.split("=", 2);
118
+ query[name] = value || "";
103
119
  }
104
120
 
105
121
  return query;
106
122
  }
107
123
 
124
+ // Search for middlewares that match a given path
108
125
  function searchMiddlewares(router: RouterInterface, path: string): Middlewares {
109
- let i;
110
- let k;
111
- let item;
112
- let match;
113
- let key;
114
126
  let middlewares: Middlewares = [];
115
127
  let params: Record<string, any> = {};
116
128
  let matches = [];
117
- router.params = {};
118
- router.path = "";
119
- router.matches = [];
120
129
 
121
130
  // Search for middlewares
122
- for (i = 0; i < router.paths.length; i++) {
123
- item = router.paths[i];
131
+ for (let item of router.paths) {
132
+ let match = item.regexp.exec(path);
124
133
 
125
- match = item.regexp.exec(path);
126
134
  // If we found middlewares
127
135
  if (Array.isArray(match)) {
128
136
  middlewares.push(...item.middlewares);
129
137
  match.shift();
130
138
 
131
139
  // Parse params
132
- for (k = 0; k < item.params.length; k++) {
133
- key = item.params[k];
134
- params[key] = match.shift();
140
+ for (let [index, key] of item.params.entries()) {
141
+ params[key] = match[index];
135
142
  }
136
143
 
137
- while (match.length) {
138
- matches.push(match.shift() as string);
139
- }
144
+ // Add remaining matches to the array
145
+ matches.push(...match);
140
146
 
141
147
  if (item.method === "add") {
142
148
  router.path = item.path;
@@ -155,8 +161,8 @@ async function searchComponent(
155
161
  router: RouterInterface,
156
162
  middlewares: Middlewares
157
163
  ): Promise<Component | VnodeComponentInterface | false | void> {
158
- let response;
159
- let req: Request = {
164
+ // Define request object with default values
165
+ const request: Request = {
160
166
  params: router.params,
161
167
  query: router.query,
162
168
  url: router.url,
@@ -164,18 +170,25 @@ async function searchComponent(
164
170
  matches: router.matches,
165
171
  redirect: (path: string, parentComponent?: Component) => {
166
172
  router.go(path, parentComponent);
173
+ // Return false to stop the middleware chain
167
174
  return false;
168
175
  }
169
176
  };
170
- let i = 0;
171
177
 
172
- for (; i < middlewares.length; i++) {
173
- response = await middlewares[i](req, response);
178
+ // Initialize response variable
179
+ let response;
174
180
 
181
+ // Iterate through middlewares
182
+ for (const middleware of middlewares) {
183
+ // Invoke middleware and update response
184
+ response = await middleware(request, response);
185
+
186
+ // Return response if it's a valid component
175
187
  if (response !== undefined && (isComponent(response) || isVnodeComponent(response))) {
176
188
  return response;
177
189
  }
178
190
 
191
+ // Return false if response is explicitly false to stop the middleware chain
179
192
  if (response === false) {
180
193
  return false;
181
194
  }
@@ -191,59 +204,57 @@ export class Router implements RouterInterface {
191
204
  path: string = "";
192
205
  params: Record<string, string | number | any> = {};
193
206
  matches: string[] = [];
207
+ pathPrefix: string = "";
208
+
209
+ constructor(pathPrefix: string = "") {
210
+ this.pathPrefix = pathPrefix;
211
+ }
194
212
 
195
- add(path: string, ...args: Middlewares): Router {
196
- addPath(this, "add", path, args);
213
+ add(path: string, ...middlewares: Middlewares): Router {
214
+ addPath({ router: this, method: "add", path: `${this.pathPrefix}${path}`, middlewares });
197
215
  return this;
198
216
  }
199
217
 
200
- use(...args: Middlewares | Router[]): Router {
201
- let path = typeof args[0] === "string" ? args.shift() : "/";
202
- let i;
203
- let k;
204
- let subrouter;
205
- let item;
206
- let subpath;
207
-
208
- for (i = 0; i < args.length; i++) {
209
- subrouter = args[i];
210
- if (typeof subrouter === "function") {
211
- addPath(this, "use", `${path}.*`, [subrouter]);
212
- } else if (subrouter.paths) {
213
- for (k = 0; k < subrouter.paths.length; k++) {
214
- item = subrouter.paths[k];
215
- subpath = `${path}${item.path}`.replace(/^\/\//, "/");
216
- addPath(this, item.method, subpath, item.middlewares);
218
+ use(...middlewares: Middlewares | Router[] | string[]): Router {
219
+ let path = `${this.pathPrefix}${typeof middlewares[0] === "string" ? middlewares.shift() : "/"}`;
220
+
221
+ for (const item of middlewares) {
222
+ if (item instanceof Router) {
223
+ const subrouter = item as Router;
224
+ for (const subpath of subrouter.paths) {
225
+ addPath({
226
+ router: this,
227
+ method: subpath.method,
228
+ path: `${path}${subpath.path}`.replace(/^\/\//, "/"),
229
+ middlewares: subpath.middlewares
230
+ });
217
231
  }
232
+ continue;
233
+ }
234
+
235
+ if (typeof item === "function") {
236
+ addPath({ router: this, method: "use", path: `${path}.*`, middlewares: [item as Middleware] });
218
237
  }
219
238
  }
220
239
 
221
240
  return this;
222
241
  }
223
242
 
224
- routes() {
225
- let routes: string[] = [];
226
- this.paths.forEach((path) => {
227
- if (path.method === "add") {
228
- routes.push(path.path);
229
- }
230
- });
231
- return routes;
243
+ routes(): string[] {
244
+ return this.paths.filter((path) => path.method === "add").map((path) => path.path);
232
245
  }
233
246
 
234
- async go(path: string, parentComponent?: Component): Promise<string | void> {
247
+ async go(path: string, parentComponent?: Component, preventPushState = false): Promise<string | void> {
235
248
  if (!path) {
236
249
  throw new Error("router.url.required");
237
250
  }
238
251
 
239
- let parts = path.split("?", 2);
240
- let urlParts = parts[0].replace(/(.+)\/$/, "$1");
241
- let queryParts = parts[1];
242
- this.url = path;
243
- this.query = parseQuery(queryParts);
244
-
245
- let middlewares = searchMiddlewares(this as RouterInterface, urlParts);
252
+ const constructedPath = `${this.pathPrefix}${path}`;
253
+ const parts = constructedPath.split("?", 2);
254
+ this.url = constructedPath;
255
+ this.query = parseQuery(parts[1]);
246
256
 
257
+ const middlewares = searchMiddlewares(this as RouterInterface, parts[0].replace(/(.+)\/$/, "$1"));
247
258
  let component = await searchComponent(this as RouterInterface, middlewares);
248
259
 
249
260
  if (component === false) {
@@ -251,11 +262,11 @@ export class Router implements RouterInterface {
251
262
  }
252
263
 
253
264
  if (!component) {
254
- throw new Error(`The url ${path} requested wasn't found`);
265
+ throw new Error(`The url ${constructedPath} requested wasn't found`);
255
266
  }
256
267
 
257
268
  if (isComponent(parentComponent) || isVnodeComponent(parentComponent)) {
258
- let childComponent = isVnodeComponent(component) ? component : v(component as Component, {});
269
+ const childComponent = isVnodeComponent(component) ? component : v(component as Component, {});
259
270
  if (isVnodeComponent(parentComponent)) {
260
271
  parentComponent.children.push(childComponent);
261
272
  component = parentComponent;
@@ -264,8 +275,8 @@ export class Router implements RouterInterface {
264
275
  }
265
276
  }
266
277
 
267
- if (!isNodeJs) {
268
- window.history.pushState(null, "", path);
278
+ if (!isNodeJs && !preventPushState) {
279
+ window.history.pushState(null, "", constructedPath);
269
280
  }
270
281
 
271
282
  if (this.container) {
@@ -283,28 +294,29 @@ export class Router implements RouterInterface {
283
294
  }
284
295
  }
285
296
 
286
- let localRedirect;
287
- export function redirect(url: string) {
297
+ let localRedirect: RedirectFunction;
298
+
299
+ export function redirect(url: string, parentComponent?: Component, preventPushState = false): string | void {
288
300
  if (!localRedirect) {
289
301
  throw new Error("router.redirect.not.found");
290
302
  }
291
- return localRedirect(url);
303
+ return localRedirect(url, parentComponent, preventPushState);
292
304
  }
293
305
 
294
- export function mountRouter(elementContainer, router) {
306
+ export function mountRouter(elementContainer: string | any, router: Router): void {
295
307
  router.container = elementContainer;
296
308
  localRedirect = router.go.bind(router);
297
309
 
298
- // Activate the use of the router
299
310
  if (!isNodeJs) {
300
- function onPopStateGoToRoute() {
301
- (router as unknown as Router).go(document.location.pathname);
311
+ function onPopStateGoToRoute(): void {
312
+ let pathWithoutPrefix = document.location.pathname.replace(router.pathPrefix, "");
313
+ (router as unknown as Router).go(pathWithoutPrefix, undefined, true);
302
314
  }
303
315
  window.addEventListener("popstate", onPopStateGoToRoute, false);
304
316
  onPopStateGoToRoute();
305
317
  }
306
318
 
307
- directive("route", (url, vnode, oldnode) => {
319
+ directive("route", (url: string, vnode: VnodeWithDom, oldnode?: VnodeWithDom): void => {
308
320
  setAttribute("href", url, vnode, oldnode);
309
321
  setAttribute("onclick", router.getOnClickHandler(url), vnode, oldnode);
310
322
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "valyrian.js",
3
- "version": "7.0.4",
3
+ "version": "7.1.1",
4
4
  "description": "Lightweight steel to forge PWAs. (Minimal Frontend Framework with server side rendering and other capabilities)",
5
5
  "repository": "git@github.com:Masquerade-Circus/valyrian.js.git",
6
6
  "author": "Masquerade <christian@masquerade-circus.net>",