valyrian.js 7.1.0 → 7.1.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.
@@ -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;
@@ -28,8 +28,9 @@ interface RouterInterface {
28
28
  path: string;
29
29
  params: Record<string, string | number | any>;
30
30
  matches: string[];
31
+ pathPrefix: string;
31
32
  add(method: string, ...args: Middlewares): Router;
32
- use(...args: string[] | Middlewares | Router[]): Router;
33
+ use(...args: (string | Middleware | Router)[]): Router;
33
34
  routes(): string[];
34
35
  go(path: string, parentComponent?: Component | POJOComponent | VnodeComponentInterface): Promise<string | void>;
35
36
  }
@@ -44,13 +45,13 @@ export declare class Router implements RouterInterface {
44
45
  matches: string[];
45
46
  pathPrefix: string;
46
47
  constructor(pathPrefix?: string);
47
- add(path: string, ...args: Middlewares): Router;
48
- use(...args: Middlewares | Router[] | string[]): Router;
48
+ add(path: string, ...middlewares: Middlewares): Router;
49
+ use(...middlewares: Middlewares | Router[] | string[]): Router;
49
50
  routes(): string[];
50
- go(path: string, parentComponent?: Component): Promise<string | void>;
51
+ go(path: string, parentComponent?: Component, preventPushState?: boolean): Promise<string | void>;
51
52
  getOnClickHandler(url: string): (e: MouseEvent) => void;
52
53
  }
53
- export declare function redirect(url: string): any;
54
- export declare function mountRouter(elementContainer: any, router: any): void;
54
+ export declare function redirect(url: string, parentComponent?: Component, preventPushState?: boolean): string | void;
55
+ export declare function mountRouter(elementContainer: string | any, router: Router): void;
55
56
  export {};
56
57
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/router/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,SAAS,EACT,aAAa,EACb,uBAAuB,EAQxB,MAAM,aAAa,CAAC;AAErB,UAAU,IAAI;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,WAAW,CAAC;IACzB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,OAAO;IACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAElB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,SAAS,GAAG,aAAa,GAAG,uBAAuB,KAAK,KAAK,CAAC;CAC1G;AAED,UAAU,UAAU;IAElB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,GAAG,GACpB,OAAO,CAAC,GAAG,GAAG,SAAS,GAAG,aAAa,GAAG,uBAAuB,CAAC,GAClE,GAAG,GACH,SAAS,GACT,aAAa,GACb,uBAAuB,CAAC;CAC7B;AAED,UAAU,WAAY,SAAQ,KAAK,CAAC,UAAU,CAAC;CAAG;AAElD,UAAU,eAAe;IACvB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,SAAS,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAAC;IACnC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;IACvC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,GAAG,CAAC,CAAC;IAC9C,OAAO,EAAE,MAAM,EAAE,CAAC;IAElB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,WAAW,GAAG,MAAM,CAAC;IAElD,GAAG,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,WAAW,GAAG,MAAM,EAAE,GAAG,MAAM,CAAC;IAExD,MAAM,IAAI,MAAM,EAAE,CAAC;IAEnB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,SAAS,GAAG,aAAa,GAAG,uBAAuB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CACjH;AA2HD,qBAAa,MAAO,YAAW,eAAe;IAC5C,KAAK,EAAE,IAAI,EAAE,CAAM;IACnB,SAAS,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAAQ;IAC1C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAM;IAC5C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAM;IAClC,GAAG,EAAE,MAAM,CAAM;IACjB,IAAI,EAAE,MAAM,CAAM;IAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,GAAG,CAAC,CAAM;IACnD,OAAO,EAAE,MAAM,EAAE,CAAM;IACvB,UAAU,EAAE,MAAM,CAAM;gBAEZ,UAAU,GAAE,MAAW;IAInC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,WAAW,GAAG,MAAM;IAK/C,GAAG,CAAC,GAAG,IAAI,EAAE,WAAW,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM;IAwBvD,MAAM;IAUA,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IA2C3E,iBAAiB,CAAC,GAAG,EAAE,MAAM,OAChB,UAAU;CAOxB;AAGD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,OAKnC;AAED,wBAAgB,WAAW,CAAC,gBAAgB,KAAA,EAAE,MAAM,KAAA,QAiBnD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/router/index.ts"],"names":[],"mappings":"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"}
@@ -28,15 +28,27 @@ 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
+ function getPathWithoutPrefix(path, prefix) {
32
+ return getPathWithoutLastSlash(path.replace(new RegExp(`^${prefix}`), ""));
33
+ }
34
+ function getPathWithoutLastSlash(path) {
35
+ let pathWithoutLastSlash = path.replace(/\/$/, "");
36
+ if (pathWithoutLastSlash === "") {
37
+ pathWithoutLastSlash = "/";
34
38
  }
35
- 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
+ return pathWithoutLastSlash;
40
+ }
41
+ var addPath = ({
42
+ router,
43
+ method,
44
+ path,
45
+ middlewares
46
+ }) => {
47
+ if (!method || !path || !Array.isArray(middlewares) || middlewares.length === 0) {
48
+ throw new Error(`Invalid route input: ${method} ${path} ${middlewares}`);
39
49
  }
50
+ let realpath = path.replace(/(\S)(\/+)$/, "$1");
51
+ let params = (realpath.match(/:(\w+)?/gi) || []).map((param) => param.slice(1));
40
52
  let regexpPath = "^" + realpath.replace(/:(\w+)/gi, "([^\\/\\s]+)") + "$";
41
53
  router.paths.push({
42
54
  method,
@@ -47,43 +59,29 @@ var addPath = (router, method, path, middlewares) => {
47
59
  });
48
60
  };
49
61
  function parseQuery(queryParts) {
50
- let parts = queryParts ? queryParts.split("&", 20) : [];
62
+ let parts = queryParts ? queryParts.split("&") : [];
51
63
  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];
64
+ for (let nameValue of parts) {
65
+ let [name, value] = nameValue.split("=", 2);
66
+ query[name] = value || "";
57
67
  }
58
68
  return query;
59
69
  }
60
70
  function searchMiddlewares(router, path) {
61
- let i;
62
- let k;
63
- let item;
64
- let match;
65
- let key;
66
71
  let middlewares = [];
67
72
  let params = {};
68
73
  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);
74
+ for (let item of router.paths) {
75
+ let match = item.regexp.exec(path);
75
76
  if (Array.isArray(match)) {
76
77
  middlewares.push(...item.middlewares);
77
78
  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());
79
+ for (let [index, key] of item.params.entries()) {
80
+ params[key] = match[index];
84
81
  }
82
+ matches.push(...match);
85
83
  if (item.method === "add") {
86
- router.path = item.path;
84
+ router.path = getPathWithoutPrefix(item.path, router.pathPrefix);
87
85
  break;
88
86
  }
89
87
  }
@@ -93,8 +91,7 @@ function searchMiddlewares(router, path) {
93
91
  return middlewares;
94
92
  }
95
93
  async function searchComponent(router, middlewares) {
96
- let response;
97
- let req = {
94
+ const request = {
98
95
  params: router.params,
99
96
  query: router.query,
100
97
  url: router.url,
@@ -105,9 +102,9 @@ async function searchComponent(router, middlewares) {
105
102
  return false;
106
103
  }
107
104
  };
108
- let i = 0;
109
- for (; i < middlewares.length; i++) {
110
- response = await middlewares[i](req, response);
105
+ let response;
106
+ for (const middleware of middlewares) {
107
+ response = await middleware(request, response);
111
108
  if (response !== void 0 && ((0, import_valyrian.isComponent)(response) || (0, import_valyrian.isVnodeComponent)(response))) {
112
109
  return response;
113
110
  }
@@ -129,50 +126,46 @@ var Router = class {
129
126
  constructor(pathPrefix = "") {
130
127
  this.pathPrefix = pathPrefix;
131
128
  }
132
- add(path, ...args) {
133
- addPath(this, "add", `${this.pathPrefix}${path}`, args);
129
+ add(path, ...middlewares) {
130
+ let pathWithoutLastSlash = getPathWithoutLastSlash(`${this.pathPrefix}${path}`);
131
+ addPath({ router: this, method: "add", path: pathWithoutLastSlash, middlewares });
134
132
  return this;
135
133
  }
136
- use(...args) {
137
- let path = `${this.pathPrefix}${typeof args[0] === "string" ? args.shift() : "/"}`;
138
- let item;
139
- let subpath;
140
- for (let i = 0; i < args.length; i++) {
141
- if (args[i] instanceof Router) {
142
- let subrouter = args[i];
143
- for (let k = 0; k < subrouter.paths.length; k++) {
144
- item = subrouter.paths[k];
145
- subpath = `${path}${item.path}`.replace(/^\/\//, "/");
146
- addPath(this, item.method, subpath, item.middlewares);
134
+ use(...middlewares) {
135
+ let path = getPathWithoutLastSlash(
136
+ `${this.pathPrefix}${typeof middlewares[0] === "string" ? middlewares.shift() : "/"}`
137
+ );
138
+ for (const item of middlewares) {
139
+ if (item instanceof Router) {
140
+ const subrouter = item;
141
+ for (const subpath of subrouter.paths) {
142
+ addPath({
143
+ router: this,
144
+ method: subpath.method,
145
+ path: `${path}${subpath.path}`.replace(/^\/\//, "/"),
146
+ middlewares: subpath.middlewares
147
+ });
147
148
  }
148
149
  continue;
149
150
  }
150
- if (typeof args[i] === "function") {
151
- addPath(this, "use", `${path}.*`, [args[i]]);
151
+ if (typeof item === "function") {
152
+ addPath({ router: this, method: "use", path: `${path}.*`, middlewares: [item] });
152
153
  }
153
154
  }
154
155
  return this;
155
156
  }
156
157
  routes() {
157
- let routes = [];
158
- this.paths.forEach((path) => {
159
- if (path.method === "add") {
160
- routes.push(path.path);
161
- }
162
- });
163
- return routes;
158
+ return this.paths.filter((path) => path.method === "add").map((path) => path.path);
164
159
  }
165
- async go(path, parentComponent) {
160
+ async go(path, parentComponent, preventPushState = false) {
166
161
  if (!path) {
167
162
  throw new Error("router.url.required");
168
163
  }
169
- let constructedPath = `${this.pathPrefix}${path}`;
170
- let parts = constructedPath.split("?", 2);
171
- let urlParts = parts[0].replace(/(.+)\/$/, "$1");
172
- let queryParts = parts[1];
164
+ let constructedPath = getPathWithoutLastSlash(`${this.pathPrefix}${path}`);
165
+ const parts = constructedPath.split("?", 2);
173
166
  this.url = constructedPath;
174
- this.query = parseQuery(queryParts);
175
- let middlewares = searchMiddlewares(this, urlParts);
167
+ this.query = parseQuery(parts[1]);
168
+ const middlewares = searchMiddlewares(this, parts[0].replace(/(.+)\/$/, "$1"));
176
169
  let component = await searchComponent(this, middlewares);
177
170
  if (component === false) {
178
171
  return;
@@ -181,7 +174,7 @@ var Router = class {
181
174
  throw new Error(`The url ${constructedPath} requested wasn't found`);
182
175
  }
183
176
  if ((0, import_valyrian.isComponent)(parentComponent) || (0, import_valyrian.isVnodeComponent)(parentComponent)) {
184
- let childComponent = (0, import_valyrian.isVnodeComponent)(component) ? component : (0, import_valyrian.v)(component, {});
177
+ const childComponent = (0, import_valyrian.isVnodeComponent)(component) ? component : (0, import_valyrian.v)(component, {});
185
178
  if ((0, import_valyrian.isVnodeComponent)(parentComponent)) {
186
179
  parentComponent.children.push(childComponent);
187
180
  component = parentComponent;
@@ -189,7 +182,7 @@ var Router = class {
189
182
  component = (0, import_valyrian.v)(parentComponent, {}, childComponent);
190
183
  }
191
184
  }
192
- if (!import_valyrian.isNodeJs) {
185
+ if (!import_valyrian.isNodeJs && !preventPushState) {
193
186
  window.history.pushState(null, "", constructedPath);
194
187
  }
195
188
  if (this.container) {
@@ -206,18 +199,19 @@ var Router = class {
206
199
  }
207
200
  };
208
201
  var localRedirect;
209
- function redirect(url) {
202
+ function redirect(url, parentComponent, preventPushState = false) {
210
203
  if (!localRedirect) {
211
204
  throw new Error("router.redirect.not.found");
212
205
  }
213
- return localRedirect(url);
206
+ return localRedirect(url, parentComponent, preventPushState);
214
207
  }
215
208
  function mountRouter(elementContainer, router) {
216
209
  router.container = elementContainer;
217
210
  localRedirect = router.go.bind(router);
218
211
  if (!import_valyrian.isNodeJs) {
219
212
  let onPopStateGoToRoute = function() {
220
- router.go(document.location.pathname);
213
+ let pathWithoutPrefix = getPathWithoutPrefix(document.location.pathname, router.pathPrefix);
214
+ router.go(pathWithoutPrefix, void 0, true);
221
215
  };
222
216
  window.addEventListener("popstate", onPopStateGoToRoute, false);
223
217
  onPopStateGoToRoute();
@@ -11,15 +11,27 @@ 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
+ function getPathWithoutPrefix(path, prefix) {
15
+ return getPathWithoutLastSlash(path.replace(new RegExp(`^${prefix}`), ""));
16
+ }
17
+ function getPathWithoutLastSlash(path) {
18
+ let pathWithoutLastSlash = path.replace(/\/$/, "");
19
+ if (pathWithoutLastSlash === "") {
20
+ pathWithoutLastSlash = "/";
17
21
  }
18
- 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
+ return pathWithoutLastSlash;
23
+ }
24
+ var addPath = ({
25
+ router,
26
+ method,
27
+ path,
28
+ middlewares
29
+ }) => {
30
+ if (!method || !path || !Array.isArray(middlewares) || middlewares.length === 0) {
31
+ throw new Error(`Invalid route input: ${method} ${path} ${middlewares}`);
22
32
  }
33
+ let realpath = path.replace(/(\S)(\/+)$/, "$1");
34
+ let params = (realpath.match(/:(\w+)?/gi) || []).map((param) => param.slice(1));
23
35
  let regexpPath = "^" + realpath.replace(/:(\w+)/gi, "([^\\/\\s]+)") + "$";
24
36
  router.paths.push({
25
37
  method,
@@ -30,43 +42,29 @@ var addPath = (router, method, path, middlewares) => {
30
42
  });
31
43
  };
32
44
  function parseQuery(queryParts) {
33
- let parts = queryParts ? queryParts.split("&", 20) : [];
45
+ let parts = queryParts ? queryParts.split("&") : [];
34
46
  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];
47
+ for (let nameValue of parts) {
48
+ let [name, value] = nameValue.split("=", 2);
49
+ query[name] = value || "";
40
50
  }
41
51
  return query;
42
52
  }
43
53
  function searchMiddlewares(router, path) {
44
- let i;
45
- let k;
46
- let item;
47
- let match;
48
- let key;
49
54
  let middlewares = [];
50
55
  let params = {};
51
56
  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);
57
+ for (let item of router.paths) {
58
+ let match = item.regexp.exec(path);
58
59
  if (Array.isArray(match)) {
59
60
  middlewares.push(...item.middlewares);
60
61
  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());
62
+ for (let [index, key] of item.params.entries()) {
63
+ params[key] = match[index];
67
64
  }
65
+ matches.push(...match);
68
66
  if (item.method === "add") {
69
- router.path = item.path;
67
+ router.path = getPathWithoutPrefix(item.path, router.pathPrefix);
70
68
  break;
71
69
  }
72
70
  }
@@ -76,8 +74,7 @@ function searchMiddlewares(router, path) {
76
74
  return middlewares;
77
75
  }
78
76
  async function searchComponent(router, middlewares) {
79
- let response;
80
- let req = {
77
+ const request = {
81
78
  params: router.params,
82
79
  query: router.query,
83
80
  url: router.url,
@@ -88,9 +85,9 @@ async function searchComponent(router, middlewares) {
88
85
  return false;
89
86
  }
90
87
  };
91
- let i = 0;
92
- for (; i < middlewares.length; i++) {
93
- response = await middlewares[i](req, response);
88
+ let response;
89
+ for (const middleware of middlewares) {
90
+ response = await middleware(request, response);
94
91
  if (response !== void 0 && (isComponent(response) || isVnodeComponent(response))) {
95
92
  return response;
96
93
  }
@@ -112,50 +109,46 @@ var Router = class {
112
109
  constructor(pathPrefix = "") {
113
110
  this.pathPrefix = pathPrefix;
114
111
  }
115
- add(path, ...args) {
116
- addPath(this, "add", `${this.pathPrefix}${path}`, args);
112
+ add(path, ...middlewares) {
113
+ let pathWithoutLastSlash = getPathWithoutLastSlash(`${this.pathPrefix}${path}`);
114
+ addPath({ router: this, method: "add", path: pathWithoutLastSlash, middlewares });
117
115
  return this;
118
116
  }
119
- use(...args) {
120
- let path = `${this.pathPrefix}${typeof args[0] === "string" ? args.shift() : "/"}`;
121
- let item;
122
- let subpath;
123
- for (let i = 0; i < args.length; i++) {
124
- if (args[i] instanceof Router) {
125
- let subrouter = args[i];
126
- for (let k = 0; k < subrouter.paths.length; k++) {
127
- item = subrouter.paths[k];
128
- subpath = `${path}${item.path}`.replace(/^\/\//, "/");
129
- addPath(this, item.method, subpath, item.middlewares);
117
+ use(...middlewares) {
118
+ let path = getPathWithoutLastSlash(
119
+ `${this.pathPrefix}${typeof middlewares[0] === "string" ? middlewares.shift() : "/"}`
120
+ );
121
+ for (const item of middlewares) {
122
+ if (item instanceof Router) {
123
+ const subrouter = item;
124
+ for (const subpath of subrouter.paths) {
125
+ addPath({
126
+ router: this,
127
+ method: subpath.method,
128
+ path: `${path}${subpath.path}`.replace(/^\/\//, "/"),
129
+ middlewares: subpath.middlewares
130
+ });
130
131
  }
131
132
  continue;
132
133
  }
133
- if (typeof args[i] === "function") {
134
- addPath(this, "use", `${path}.*`, [args[i]]);
134
+ if (typeof item === "function") {
135
+ addPath({ router: this, method: "use", path: `${path}.*`, middlewares: [item] });
135
136
  }
136
137
  }
137
138
  return this;
138
139
  }
139
140
  routes() {
140
- let routes = [];
141
- this.paths.forEach((path) => {
142
- if (path.method === "add") {
143
- routes.push(path.path);
144
- }
145
- });
146
- return routes;
141
+ return this.paths.filter((path) => path.method === "add").map((path) => path.path);
147
142
  }
148
- async go(path, parentComponent) {
143
+ async go(path, parentComponent, preventPushState = false) {
149
144
  if (!path) {
150
145
  throw new Error("router.url.required");
151
146
  }
152
- let constructedPath = `${this.pathPrefix}${path}`;
153
- let parts = constructedPath.split("?", 2);
154
- let urlParts = parts[0].replace(/(.+)\/$/, "$1");
155
- let queryParts = parts[1];
147
+ let constructedPath = getPathWithoutLastSlash(`${this.pathPrefix}${path}`);
148
+ const parts = constructedPath.split("?", 2);
156
149
  this.url = constructedPath;
157
- this.query = parseQuery(queryParts);
158
- let middlewares = searchMiddlewares(this, urlParts);
150
+ this.query = parseQuery(parts[1]);
151
+ const middlewares = searchMiddlewares(this, parts[0].replace(/(.+)\/$/, "$1"));
159
152
  let component = await searchComponent(this, middlewares);
160
153
  if (component === false) {
161
154
  return;
@@ -164,7 +157,7 @@ var Router = class {
164
157
  throw new Error(`The url ${constructedPath} requested wasn't found`);
165
158
  }
166
159
  if (isComponent(parentComponent) || isVnodeComponent(parentComponent)) {
167
- let childComponent = isVnodeComponent(component) ? component : v(component, {});
160
+ const childComponent = isVnodeComponent(component) ? component : v(component, {});
168
161
  if (isVnodeComponent(parentComponent)) {
169
162
  parentComponent.children.push(childComponent);
170
163
  component = parentComponent;
@@ -172,7 +165,7 @@ var Router = class {
172
165
  component = v(parentComponent, {}, childComponent);
173
166
  }
174
167
  }
175
- if (!isNodeJs) {
168
+ if (!isNodeJs && !preventPushState) {
176
169
  window.history.pushState(null, "", constructedPath);
177
170
  }
178
171
  if (this.container) {
@@ -189,18 +182,19 @@ var Router = class {
189
182
  }
190
183
  };
191
184
  var localRedirect;
192
- function redirect(url) {
185
+ function redirect(url, parentComponent, preventPushState = false) {
193
186
  if (!localRedirect) {
194
187
  throw new Error("router.redirect.not.found");
195
188
  }
196
- return localRedirect(url);
189
+ return localRedirect(url, parentComponent, preventPushState);
197
190
  }
198
191
  function mountRouter(elementContainer, router) {
199
192
  router.container = elementContainer;
200
193
  localRedirect = router.go.bind(router);
201
194
  if (!isNodeJs) {
202
195
  let onPopStateGoToRoute = function() {
203
- router.go(document.location.pathname);
196
+ let pathWithoutPrefix = getPathWithoutPrefix(document.location.pathname, router.pathPrefix);
197
+ router.go(pathWithoutPrefix, void 0, true);
204
198
  };
205
199
  window.addEventListener("popstate", onPopStateGoToRoute, false);
206
200
  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;
@@ -51,35 +52,62 @@ interface RouterInterface {
51
52
  path: string;
52
53
  params: Record<string, string | number | any>;
53
54
  matches: string[];
55
+ pathPrefix: string;
54
56
  // eslint-disable-next-line no-unused-vars
55
57
  add(method: string, ...args: Middlewares): Router;
56
58
  // eslint-disable-next-line no-unused-vars
57
- use(...args: string[] | Middlewares | Router[]): Router;
59
+ use(...args: (string | Middleware | Router)[]): Router;
58
60
 
59
61
  routes(): string[];
60
62
  // eslint-disable-next-line no-unused-vars
61
63
  go(path: string, parentComponent?: Component | POJOComponent | VnodeComponentInterface): Promise<string | void>;
62
64
  }
63
65
 
66
+ interface RedirectFunction {
67
+ // eslint-disable-next-line no-unused-vars
68
+ (url: string, parentComponent?: Component, preventPushState?: boolean): string | void;
69
+ }
70
+
64
71
  function flat(array: any) {
65
72
  return Array.isArray(array) ? array.flat(Infinity) : [array];
66
73
  }
67
74
 
68
- let addPath = (router: Router, method: string, path: string, middlewares: any[]) => {
69
- if (middlewares.length === 0) {
70
- return;
75
+ function getPathWithoutPrefix(path: string, prefix: string) {
76
+ return getPathWithoutLastSlash(path.replace(new RegExp(`^${prefix}`), ""));
77
+ }
78
+
79
+ function getPathWithoutLastSlash(path: string) {
80
+ let pathWithoutLastSlash = path.replace(/\/$/, "");
81
+ if (pathWithoutLastSlash === "") {
82
+ pathWithoutLastSlash = "/";
71
83
  }
84
+ return pathWithoutLastSlash;
85
+ }
72
86
 
87
+ const addPath = ({
88
+ router,
89
+ method,
90
+ path,
91
+ middlewares
92
+ }: {
93
+ router: Router;
94
+ method: string;
95
+ path: string;
96
+ middlewares: Middleware[];
97
+ }): void => {
98
+ if (!method || !path || !Array.isArray(middlewares) || middlewares.length === 0) {
99
+ throw new Error(`Invalid route input: ${method} ${path} ${middlewares}`);
100
+ }
101
+
102
+ // Trim trailing slashes from the path
73
103
  let realpath = path.replace(/(\S)(\/+)$/, "$1");
74
104
 
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
- }
105
+ // Find the express-like params in the path
106
+ let params = (realpath.match(/:(\w+)?/gi) || [])
107
+ // Set the names of the params found
108
+ .map((param) => param.slice(1));
82
109
 
110
+ // Generate a regular expression to match the path
83
111
  let regexpPath = "^" + realpath.replace(/:(\w+)/gi, "([^\\/\\s]+)") + "$";
84
112
 
85
113
  router.paths.push({
@@ -91,55 +119,46 @@ let addPath = (router: Router, method: string, path: string, middlewares: any[])
91
119
  });
92
120
  };
93
121
 
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;
122
+ // Parse a query string into an object
123
+ function parseQuery(queryParts?: string): Record<string, string> {
124
+ // Split the query string into an array of name-value pairs
125
+ let parts = queryParts ? queryParts.split("&") : [];
126
+ let query: Record<string, string> = {};
99
127
 
100
- for (; i < parts.length; i++) {
101
- nameValue = parts[i].split("=", 2);
102
- query[nameValue[0]] = nameValue[1];
128
+ // Iterate over the name-value pairs and add them to the query object
129
+ for (let nameValue of parts) {
130
+ let [name, value] = nameValue.split("=", 2);
131
+ query[name] = value || "";
103
132
  }
104
133
 
105
134
  return query;
106
135
  }
107
136
 
137
+ // Search for middlewares that match a given path
108
138
  function searchMiddlewares(router: RouterInterface, path: string): Middlewares {
109
- let i;
110
- let k;
111
- let item;
112
- let match;
113
- let key;
114
139
  let middlewares: Middlewares = [];
115
140
  let params: Record<string, any> = {};
116
141
  let matches = [];
117
- router.params = {};
118
- router.path = "";
119
- router.matches = [];
120
142
 
121
143
  // Search for middlewares
122
- for (i = 0; i < router.paths.length; i++) {
123
- item = router.paths[i];
144
+ for (let item of router.paths) {
145
+ let match = item.regexp.exec(path);
124
146
 
125
- match = item.regexp.exec(path);
126
147
  // If we found middlewares
127
148
  if (Array.isArray(match)) {
128
149
  middlewares.push(...item.middlewares);
129
150
  match.shift();
130
151
 
131
152
  // Parse params
132
- for (k = 0; k < item.params.length; k++) {
133
- key = item.params[k];
134
- params[key] = match.shift();
153
+ for (let [index, key] of item.params.entries()) {
154
+ params[key] = match[index];
135
155
  }
136
156
 
137
- while (match.length) {
138
- matches.push(match.shift() as string);
139
- }
157
+ // Add remaining matches to the array
158
+ matches.push(...match);
140
159
 
141
160
  if (item.method === "add") {
142
- router.path = item.path;
161
+ router.path = getPathWithoutPrefix(item.path, router.pathPrefix);
143
162
  break;
144
163
  }
145
164
  }
@@ -155,8 +174,8 @@ async function searchComponent(
155
174
  router: RouterInterface,
156
175
  middlewares: Middlewares
157
176
  ): Promise<Component | VnodeComponentInterface | false | void> {
158
- let response;
159
- let req: Request = {
177
+ // Define request object with default values
178
+ const request: Request = {
160
179
  params: router.params,
161
180
  query: router.query,
162
181
  url: router.url,
@@ -164,18 +183,25 @@ async function searchComponent(
164
183
  matches: router.matches,
165
184
  redirect: (path: string, parentComponent?: Component) => {
166
185
  router.go(path, parentComponent);
186
+ // Return false to stop the middleware chain
167
187
  return false;
168
188
  }
169
189
  };
170
- let i = 0;
171
190
 
172
- for (; i < middlewares.length; i++) {
173
- response = await middlewares[i](req, response);
191
+ // Initialize response variable
192
+ let response;
193
+
194
+ // Iterate through middlewares
195
+ for (const middleware of middlewares) {
196
+ // Invoke middleware and update response
197
+ response = await middleware(request, response);
174
198
 
199
+ // Return response if it's a valid component
175
200
  if (response !== undefined && (isComponent(response) || isVnodeComponent(response))) {
176
201
  return response;
177
202
  }
178
203
 
204
+ // Return false if response is explicitly false to stop the middleware chain
179
205
  if (response === false) {
180
206
  return false;
181
207
  }
@@ -197,59 +223,54 @@ export class Router implements RouterInterface {
197
223
  this.pathPrefix = pathPrefix;
198
224
  }
199
225
 
200
- add(path: string, ...args: Middlewares): Router {
201
- addPath(this, "add", `${this.pathPrefix}${path}`, args);
226
+ add(path: string, ...middlewares: Middlewares): Router {
227
+ let pathWithoutLastSlash = getPathWithoutLastSlash(`${this.pathPrefix}${path}`);
228
+ addPath({ router: this, method: "add", path: pathWithoutLastSlash, middlewares });
202
229
  return this;
203
230
  }
204
231
 
205
- use(...args: Middlewares | Router[] | string[]): Router {
206
- let path = `${this.pathPrefix}${typeof args[0] === "string" ? args.shift() : "/"}`;
207
- let item;
208
- let subpath;
209
-
210
- for (let i = 0; i < args.length; i++) {
211
- if (args[i] instanceof Router) {
212
- let subrouter = args[i] as Router;
213
- for (let 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);
232
+ use(...middlewares: Middlewares | Router[] | string[]): Router {
233
+ let path = getPathWithoutLastSlash(
234
+ `${this.pathPrefix}${typeof middlewares[0] === "string" ? middlewares.shift() : "/"}`
235
+ );
236
+
237
+ for (const item of middlewares) {
238
+ if (item instanceof Router) {
239
+ const subrouter = item as Router;
240
+ for (const subpath of subrouter.paths) {
241
+ addPath({
242
+ router: this,
243
+ method: subpath.method,
244
+ path: `${path}${subpath.path}`.replace(/^\/\//, "/"),
245
+ middlewares: subpath.middlewares
246
+ });
217
247
  }
218
248
  continue;
219
249
  }
220
250
 
221
- if (typeof args[i] === "function") {
222
- addPath(this, "use", `${path}.*`, [args[i]]);
251
+ if (typeof item === "function") {
252
+ addPath({ router: this, method: "use", path: `${path}.*`, middlewares: [item as Middleware] });
223
253
  }
224
254
  }
225
255
 
226
256
  return this;
227
257
  }
228
258
 
229
- routes() {
230
- let routes: string[] = [];
231
- this.paths.forEach((path) => {
232
- if (path.method === "add") {
233
- routes.push(path.path);
234
- }
235
- });
236
- return routes;
259
+ routes(): string[] {
260
+ return this.paths.filter((path) => path.method === "add").map((path) => path.path);
237
261
  }
238
262
 
239
- async go(path: string, parentComponent?: Component): Promise<string | void> {
263
+ async go(path: string, parentComponent?: Component, preventPushState = false): Promise<string | void> {
240
264
  if (!path) {
241
265
  throw new Error("router.url.required");
242
266
  }
243
267
 
244
- let constructedPath = `${this.pathPrefix}${path}`;
245
- let parts = constructedPath.split("?", 2);
246
- let urlParts = parts[0].replace(/(.+)\/$/, "$1");
247
- let queryParts = parts[1];
268
+ let constructedPath = getPathWithoutLastSlash(`${this.pathPrefix}${path}`);
269
+ const parts = constructedPath.split("?", 2);
248
270
  this.url = constructedPath;
249
- this.query = parseQuery(queryParts);
250
-
251
- let middlewares = searchMiddlewares(this as RouterInterface, urlParts);
271
+ this.query = parseQuery(parts[1]);
252
272
 
273
+ const middlewares = searchMiddlewares(this as RouterInterface, parts[0].replace(/(.+)\/$/, "$1"));
253
274
  let component = await searchComponent(this as RouterInterface, middlewares);
254
275
 
255
276
  if (component === false) {
@@ -261,7 +282,7 @@ export class Router implements RouterInterface {
261
282
  }
262
283
 
263
284
  if (isComponent(parentComponent) || isVnodeComponent(parentComponent)) {
264
- let childComponent = isVnodeComponent(component) ? component : v(component as Component, {});
285
+ const childComponent = isVnodeComponent(component) ? component : v(component as Component, {});
265
286
  if (isVnodeComponent(parentComponent)) {
266
287
  parentComponent.children.push(childComponent);
267
288
  component = parentComponent;
@@ -270,7 +291,7 @@ export class Router implements RouterInterface {
270
291
  }
271
292
  }
272
293
 
273
- if (!isNodeJs) {
294
+ if (!isNodeJs && !preventPushState) {
274
295
  window.history.pushState(null, "", constructedPath);
275
296
  }
276
297
 
@@ -289,28 +310,29 @@ export class Router implements RouterInterface {
289
310
  }
290
311
  }
291
312
 
292
- let localRedirect;
293
- export function redirect(url: string) {
313
+ let localRedirect: RedirectFunction;
314
+
315
+ export function redirect(url: string, parentComponent?: Component, preventPushState = false): string | void {
294
316
  if (!localRedirect) {
295
317
  throw new Error("router.redirect.not.found");
296
318
  }
297
- return localRedirect(url);
319
+ return localRedirect(url, parentComponent, preventPushState);
298
320
  }
299
321
 
300
- export function mountRouter(elementContainer, router) {
322
+ export function mountRouter(elementContainer: string | any, router: Router): void {
301
323
  router.container = elementContainer;
302
324
  localRedirect = router.go.bind(router);
303
325
 
304
- // Activate the use of the router
305
326
  if (!isNodeJs) {
306
- function onPopStateGoToRoute() {
307
- (router as unknown as Router).go(document.location.pathname);
327
+ function onPopStateGoToRoute(): void {
328
+ let pathWithoutPrefix = getPathWithoutPrefix(document.location.pathname, router.pathPrefix);
329
+ (router as unknown as Router).go(pathWithoutPrefix, undefined, true);
308
330
  }
309
331
  window.addEventListener("popstate", onPopStateGoToRoute, false);
310
332
  onPopStateGoToRoute();
311
333
  }
312
334
 
313
- directive("route", (url, vnode, oldnode) => {
335
+ directive("route", (url: string, vnode: VnodeWithDom, oldnode?: VnodeWithDom): void => {
314
336
  setAttribute("href", url, vnode, oldnode);
315
337
  setAttribute("onclick", router.getOnClickHandler(url), vnode, oldnode);
316
338
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "valyrian.js",
3
- "version": "7.1.0",
3
+ "version": "7.1.2",
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>",