htmx-router 0.0.4 → 0.0.6
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/bin/404-route.d.ts +2 -0
- package/bin/404-route.js +8 -0
- package/bin/cli/dynamic.js +1 -1
- package/bin/cli/static.js +1 -1
- package/bin/components.d.ts +5 -0
- package/bin/components.js +31 -0
- package/bin/index.d.ts +4 -2
- package/bin/index.js +8 -5
- package/bin/render-args.d.ts +33 -0
- package/bin/render-args.js +105 -0
- package/bin/router.d.ts +5 -5
- package/bin/router.js +156 -62
- package/bin/shared.d.ts +5 -18
- package/bin/shared.js +1 -65
- package/package.json +3 -2
package/bin/404-route.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Render = void 0;
|
|
4
|
+
const shared_1 = require("./shared");
|
|
5
|
+
async function Render(rn, { req }) {
|
|
6
|
+
throw new shared_1.ErrorResponse(404, "Resource Not Found", req.url || "/");
|
|
7
|
+
}
|
|
8
|
+
exports.Render = Render;
|
package/bin/cli/dynamic.js
CHANGED
|
@@ -39,7 +39,7 @@ function readDirRecursively(dir: string) {
|
|
|
39
39
|
script += "\tconst ext = extname(file);\n";
|
|
40
40
|
script += "\tif (!IsAllowedExt(ext)) continue;\n";
|
|
41
41
|
script += "\tconst url = relative(ctx, file.slice(0, file.lastIndexOf(\".\")).replace(/\\\\/g, \"/\"));\n";
|
|
42
|
-
script += `\timport(file).then((mod) => Router.ingest(url, mod, []));\n`;
|
|
42
|
+
script += `\timport(file).then((mod) => Router.ingest(url, mod, [false]));\n`;
|
|
43
43
|
script += "}\n";
|
|
44
44
|
(0, fs_1.writeFileSync)(`${cwd}/router.ts`, script);
|
|
45
45
|
console.log(`Finished Building`);
|
package/bin/cli/static.js
CHANGED
|
@@ -40,7 +40,7 @@ function BuildStatic(cwd) {
|
|
|
40
40
|
script += `\nexport const Router = new RouteTree();\n`;
|
|
41
41
|
for (let i = 0; i < files.length; i++) {
|
|
42
42
|
const file = files[i];
|
|
43
|
-
script += `Router.ingest("${file.slice(DIR.length - 1)}", Route${i}, []);\n`;
|
|
43
|
+
script += `Router.ingest("${file.slice(DIR.length - 1)}", Route${i}, [false]);\n`;
|
|
44
44
|
}
|
|
45
45
|
script += `Router.assignRoot(RootRoute);\n`;
|
|
46
46
|
(0, fs_1.writeFileSync)(`${cwd}/router.ts`, script);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.Link = void 0;
|
|
27
|
+
const elements = __importStar(require("typed-html"));
|
|
28
|
+
function Link(props, contents) {
|
|
29
|
+
return elements.createElement("a", { target: props.target || "", style: props.style || "", href: props.to, "hx-get": props.to, "hx-headers": '{"hx-headless": "true"}' }, contents);
|
|
30
|
+
}
|
|
31
|
+
exports.Link = Link;
|
package/bin/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { ErrorResponse, Redirect, Outlet, Override } from "./shared";
|
|
1
2
|
import { RouteTree, IsAllowedExt } from "./router";
|
|
2
|
-
import {
|
|
3
|
+
import { RenderArgs } from "./render-args";
|
|
4
|
+
import { Link } from "./components";
|
|
3
5
|
import { StyleCSS } from "./helper";
|
|
4
|
-
export { IsAllowedExt, RouteTree, ErrorResponse, Redirect, Override, RenderArgs, Outlet, StyleCSS };
|
|
6
|
+
export { IsAllowedExt, RouteTree, ErrorResponse, Redirect, Override, RenderArgs, Outlet, StyleCSS, Link };
|
package/bin/index.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.StyleCSS = exports.RenderArgs = exports.Override = exports.Redirect = exports.ErrorResponse = exports.RouteTree = exports.IsAllowedExt = void 0;
|
|
4
|
-
const router_1 = require("./router");
|
|
5
|
-
Object.defineProperty(exports, "RouteTree", { enumerable: true, get: function () { return router_1.RouteTree; } });
|
|
6
|
-
Object.defineProperty(exports, "IsAllowedExt", { enumerable: true, get: function () { return router_1.IsAllowedExt; } });
|
|
3
|
+
exports.Link = exports.StyleCSS = exports.RenderArgs = exports.Override = exports.Redirect = exports.ErrorResponse = exports.RouteTree = exports.IsAllowedExt = void 0;
|
|
7
4
|
const shared_1 = require("./shared");
|
|
8
5
|
Object.defineProperty(exports, "ErrorResponse", { enumerable: true, get: function () { return shared_1.ErrorResponse; } });
|
|
9
6
|
Object.defineProperty(exports, "Redirect", { enumerable: true, get: function () { return shared_1.Redirect; } });
|
|
10
7
|
Object.defineProperty(exports, "Override", { enumerable: true, get: function () { return shared_1.Override; } });
|
|
11
|
-
|
|
8
|
+
const router_1 = require("./router");
|
|
9
|
+
Object.defineProperty(exports, "RouteTree", { enumerable: true, get: function () { return router_1.RouteTree; } });
|
|
10
|
+
Object.defineProperty(exports, "IsAllowedExt", { enumerable: true, get: function () { return router_1.IsAllowedExt; } });
|
|
11
|
+
const render_args_1 = require("./render-args");
|
|
12
|
+
Object.defineProperty(exports, "RenderArgs", { enumerable: true, get: function () { return render_args_1.RenderArgs; } });
|
|
13
|
+
const components_1 = require("./components");
|
|
14
|
+
Object.defineProperty(exports, "Link", { enumerable: true, get: function () { return components_1.Link; } });
|
|
12
15
|
const helper_1 = require("./helper");
|
|
13
16
|
Object.defineProperty(exports, "StyleCSS", { enumerable: true, get: function () { return helper_1.StyleCSS; } });
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import type http from "node:http";
|
|
3
|
+
import { RouteLeaf } from "./router";
|
|
4
|
+
type MetaHTML = {
|
|
5
|
+
[key: string]: string;
|
|
6
|
+
};
|
|
7
|
+
export declare enum MaskType {
|
|
8
|
+
show = 0,
|
|
9
|
+
headless = 1,
|
|
10
|
+
hide = 2
|
|
11
|
+
}
|
|
12
|
+
export declare class RenderArgs {
|
|
13
|
+
req: http.IncomingMessage;
|
|
14
|
+
res: http.ServerResponse;
|
|
15
|
+
params: MetaHTML;
|
|
16
|
+
url: URL;
|
|
17
|
+
shared: {
|
|
18
|
+
[key: string]: any;
|
|
19
|
+
};
|
|
20
|
+
links: MetaHTML[];
|
|
21
|
+
meta: MetaHTML[];
|
|
22
|
+
_outletChain: RouteLeaf[];
|
|
23
|
+
_maskChain: MaskType[];
|
|
24
|
+
_maxChain: number;
|
|
25
|
+
constructor(req: http.IncomingMessage, res: http.ServerResponse, url: URL);
|
|
26
|
+
addLinks(links: MetaHTML[], override?: boolean): void;
|
|
27
|
+
addMeta(links: MetaHTML[], override?: boolean): void;
|
|
28
|
+
Outlet: () => Promise<string>;
|
|
29
|
+
_addOutlet(route: RouteLeaf): void;
|
|
30
|
+
_applyMask(mask: boolean[], depth: number): void;
|
|
31
|
+
renderHeadHTML(): string;
|
|
32
|
+
}
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RenderArgs = exports.MaskType = void 0;
|
|
4
|
+
const attrRegex = /^[A-z][A-z\-0-9]+$/;
|
|
5
|
+
function ValidateMetaHTML(val) {
|
|
6
|
+
for (const key in val) {
|
|
7
|
+
if (!attrRegex.test(key))
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
function ValidateMetaHTMLs(val) {
|
|
13
|
+
for (const meta of val) {
|
|
14
|
+
if (!ValidateMetaHTML(meta))
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
var MaskType;
|
|
20
|
+
(function (MaskType) {
|
|
21
|
+
MaskType[MaskType["show"] = 0] = "show";
|
|
22
|
+
MaskType[MaskType["headless"] = 1] = "headless";
|
|
23
|
+
MaskType[MaskType["hide"] = 2] = "hide";
|
|
24
|
+
})(MaskType || (exports.MaskType = MaskType = {}));
|
|
25
|
+
class RenderArgs {
|
|
26
|
+
constructor(req, res, url) {
|
|
27
|
+
// unpacking Outlet caused this to be undefined
|
|
28
|
+
// hence the weird def
|
|
29
|
+
this.Outlet = () => {
|
|
30
|
+
const depth = this._maxChain - this._outletChain.length;
|
|
31
|
+
const route = this._outletChain.pop();
|
|
32
|
+
let mask = this._maskChain.pop();
|
|
33
|
+
if (mask === undefined) {
|
|
34
|
+
mask = MaskType.show;
|
|
35
|
+
}
|
|
36
|
+
if (!route)
|
|
37
|
+
return new Promise((res) => res(""));
|
|
38
|
+
const routeName = `hx-route-${depth.toString(16)}`;
|
|
39
|
+
return route.render(this, mask, routeName);
|
|
40
|
+
};
|
|
41
|
+
this.req = req;
|
|
42
|
+
this.res = res;
|
|
43
|
+
this.url = url;
|
|
44
|
+
this.params = {};
|
|
45
|
+
this.shared = {};
|
|
46
|
+
this.links = [];
|
|
47
|
+
this.meta = [];
|
|
48
|
+
this._outletChain = [];
|
|
49
|
+
this._maskChain = [];
|
|
50
|
+
this._maxChain = 0;
|
|
51
|
+
}
|
|
52
|
+
addLinks(links, override = false) {
|
|
53
|
+
if (!ValidateMetaHTMLs(links))
|
|
54
|
+
throw new Error(`Provided links have invalid attribute`);
|
|
55
|
+
if (override) {
|
|
56
|
+
this.links = links;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
this.links.push(...links);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
addMeta(links, override = false) {
|
|
63
|
+
if (!ValidateMetaHTMLs(links))
|
|
64
|
+
throw new Error(`Provided links have invalid attribute`);
|
|
65
|
+
if (override) {
|
|
66
|
+
this.meta = links;
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
this.meta.push(...links);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
_addOutlet(route) {
|
|
73
|
+
this._outletChain.push(route);
|
|
74
|
+
}
|
|
75
|
+
_applyMask(mask, depth) {
|
|
76
|
+
const padded = new Array(this._outletChain.length - mask.length).fill(false);
|
|
77
|
+
padded.push(...mask);
|
|
78
|
+
this._maskChain = padded
|
|
79
|
+
.map((x, i) => x === true ?
|
|
80
|
+
MaskType.hide :
|
|
81
|
+
padded.length - i > depth ?
|
|
82
|
+
MaskType.show :
|
|
83
|
+
MaskType.headless);
|
|
84
|
+
this._maxChain = this._maskChain.length;
|
|
85
|
+
}
|
|
86
|
+
renderHeadHTML() {
|
|
87
|
+
let out = "";
|
|
88
|
+
for (const elm of this.links) {
|
|
89
|
+
out += "<link";
|
|
90
|
+
for (const attr in elm) {
|
|
91
|
+
out += ` ${attr}="${elm[attr].replace(/"/g, "\\\"")}"`;
|
|
92
|
+
}
|
|
93
|
+
out += "></link>";
|
|
94
|
+
}
|
|
95
|
+
for (const elm of this.meta) {
|
|
96
|
+
out += "<meta";
|
|
97
|
+
for (const attr in elm) {
|
|
98
|
+
out += ` ${attr}="${elm[attr].replace(/"/g, "\\\"")}"`;
|
|
99
|
+
}
|
|
100
|
+
out += "></meta>";
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
exports.RenderArgs = RenderArgs;
|
package/bin/router.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import type http from "node:http";
|
|
3
|
-
import {
|
|
3
|
+
import { Override, Redirect, RouteModule } from "./shared";
|
|
4
|
+
import { MaskType, RenderArgs } from "./render-args";
|
|
4
5
|
export declare function IsAllowedExt(ext: string): boolean;
|
|
5
|
-
declare class RouteLeaf {
|
|
6
|
+
export declare class RouteLeaf {
|
|
6
7
|
module: RouteModule;
|
|
7
8
|
mask: boolean[];
|
|
8
9
|
constructor(module: RouteModule, mask: boolean[]);
|
|
9
|
-
|
|
10
|
+
render(args: RenderArgs, mask: MaskType, routeName: string): Promise<string>;
|
|
10
11
|
}
|
|
11
12
|
export declare class RouteTree {
|
|
12
13
|
nested: Map<string, RouteTree>;
|
|
@@ -17,7 +18,6 @@ export declare class RouteTree {
|
|
|
17
18
|
constructor();
|
|
18
19
|
assignRoot(module: RouteModule): void;
|
|
19
20
|
ingest(path: string | string[], module: RouteModule, override: boolean[]): void;
|
|
21
|
+
calculateDepth(from: string[], to: string[]): number;
|
|
20
22
|
render(req: http.IncomingMessage, res: http.ServerResponse, url: URL): Promise<string | Redirect | Override>;
|
|
21
|
-
private _recursiveRender;
|
|
22
23
|
}
|
|
23
|
-
export {};
|
package/bin/router.js
CHANGED
|
@@ -1,7 +1,32 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
2
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RouteTree = exports.IsAllowedExt = void 0;
|
|
26
|
+
exports.RouteTree = exports.RouteLeaf = exports.IsAllowedExt = void 0;
|
|
4
27
|
const shared_1 = require("./shared");
|
|
28
|
+
const render_args_1 = require("./render-args");
|
|
29
|
+
const BlankRoute = __importStar(require("./404-route"));
|
|
5
30
|
function IsAllowedExt(ext) {
|
|
6
31
|
if (ext[0] !== ".")
|
|
7
32
|
return false;
|
|
@@ -19,33 +44,37 @@ function IsAllowedExt(ext) {
|
|
|
19
44
|
return true;
|
|
20
45
|
}
|
|
21
46
|
exports.IsAllowedExt = IsAllowedExt;
|
|
22
|
-
async function blankOutlet() {
|
|
23
|
-
return "";
|
|
24
|
-
}
|
|
25
47
|
class RouteLeaf {
|
|
26
48
|
constructor(module, mask) {
|
|
27
49
|
this.module = module;
|
|
28
50
|
this.mask = mask;
|
|
29
51
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
52
|
+
async render(args, mask, routeName) {
|
|
53
|
+
try {
|
|
54
|
+
if (this.module.Auth)
|
|
55
|
+
return await this.module.Auth(args);
|
|
56
|
+
if (mask === render_args_1.MaskType.show) {
|
|
57
|
+
if (this.module.Render)
|
|
58
|
+
return await this.module.Render(routeName, args);
|
|
36
59
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
throw e;
|
|
40
|
-
const err = (e instanceof shared_1.ErrorResponse) ? e :
|
|
41
|
-
new shared_1.ErrorResponse(500, "Runtime Error", e);
|
|
42
|
-
if (catcher)
|
|
43
|
-
return await catcher(args, err);
|
|
44
|
-
throw err;
|
|
60
|
+
else {
|
|
61
|
+
return await args.Outlet();
|
|
45
62
|
}
|
|
46
|
-
}
|
|
63
|
+
}
|
|
64
|
+
catch (e) {
|
|
65
|
+
if (e instanceof shared_1.Redirect || e instanceof shared_1.Override)
|
|
66
|
+
throw e;
|
|
67
|
+
const err = (e instanceof shared_1.ErrorResponse) ? e :
|
|
68
|
+
new shared_1.ErrorResponse(500, "Runtime Error", e);
|
|
69
|
+
if (this.module.CatchError)
|
|
70
|
+
return await this.module.CatchError(routeName, args, err);
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
return "";
|
|
47
74
|
}
|
|
48
75
|
}
|
|
76
|
+
exports.RouteLeaf = RouteLeaf;
|
|
77
|
+
const blankLeaf = new RouteLeaf(BlankRoute, []);
|
|
49
78
|
class RouteTree {
|
|
50
79
|
constructor() {
|
|
51
80
|
this.nested = new Map();
|
|
@@ -102,67 +131,132 @@ class RouteTree {
|
|
|
102
131
|
path.splice(0, 1);
|
|
103
132
|
next.ingest(path, module, override);
|
|
104
133
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if (
|
|
108
|
-
|
|
134
|
+
calculateDepth(from, to) {
|
|
135
|
+
let depth = 0;
|
|
136
|
+
if (from.length == 0 || to.length == 0) {
|
|
137
|
+
depth = 1;
|
|
109
138
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
139
|
+
else {
|
|
140
|
+
const segmentA = from.splice(0, 1)[0];
|
|
141
|
+
const segmentB = to.splice(0, 1)[0];
|
|
142
|
+
const subRoute = this.nested.get(segmentA);
|
|
143
|
+
if (subRoute && segmentA === segmentB) {
|
|
144
|
+
depth = subRoute.calculateDepth(from, to);
|
|
145
|
+
}
|
|
146
|
+
else if (this.wild) {
|
|
147
|
+
depth = this.wild.calculateDepth(from, to);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
return 1;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
depth++;
|
|
154
|
+
return depth;
|
|
155
|
+
}
|
|
156
|
+
async render(req, res, url) {
|
|
157
|
+
var _a;
|
|
158
|
+
if (url.pathname.length != 1 && url.pathname.endsWith("/")) {
|
|
159
|
+
return new shared_1.Redirect(url.pathname.slice(0, -1) + url.search);
|
|
113
160
|
}
|
|
161
|
+
const args = new render_args_1.RenderArgs(req, res, url);
|
|
162
|
+
const from = req.headers['hx-headless'] ?
|
|
163
|
+
new URL(((_a = req.headers['hx-current-url']) === null || _a === void 0 ? void 0 : _a.toString()) || "/").pathname :
|
|
164
|
+
"";
|
|
114
165
|
try {
|
|
115
|
-
const
|
|
116
|
-
|
|
166
|
+
const depth = BuildOutlet(this, args, from);
|
|
167
|
+
if (from) {
|
|
168
|
+
res.setHeader('HX-Replace-Url', req.url || "/");
|
|
169
|
+
if (depth > 0) {
|
|
170
|
+
res.setHeader('HX-Retarget', `#hx-route-${depth.toString(16)}`);
|
|
171
|
+
}
|
|
172
|
+
res.setHeader('HX-Reswap', "outerHTML");
|
|
173
|
+
}
|
|
174
|
+
return await args.Outlet();
|
|
117
175
|
}
|
|
118
176
|
catch (e) {
|
|
119
177
|
if (e instanceof shared_1.Redirect)
|
|
120
178
|
return e;
|
|
121
179
|
if (e instanceof shared_1.Override)
|
|
122
180
|
return e;
|
|
181
|
+
console.error(e);
|
|
123
182
|
throw new Error(`Unhandled boil up type ${typeof (e)}: ${e}`);
|
|
124
183
|
}
|
|
125
184
|
;
|
|
126
185
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
186
|
+
}
|
|
187
|
+
exports.RouteTree = RouteTree;
|
|
188
|
+
function BuildOutlet(start, args, fromPath) {
|
|
189
|
+
const frags = args.url.pathname.split('/').slice(1);
|
|
190
|
+
if (frags.length === 1 && frags[0] === "") {
|
|
191
|
+
frags.splice(0, 1);
|
|
192
|
+
}
|
|
193
|
+
const from = fromPath.split('/').slice(1);
|
|
194
|
+
if (from.length === 1 && from[0] === "") {
|
|
195
|
+
from.splice(0, 1);
|
|
196
|
+
}
|
|
197
|
+
let matching = fromPath.length > 0;
|
|
198
|
+
let depth = -1;
|
|
199
|
+
const stack = [start];
|
|
200
|
+
let mask = null;
|
|
201
|
+
while (stack.length > 0) {
|
|
202
|
+
const cursor = stack.pop();
|
|
203
|
+
if (!mask) {
|
|
204
|
+
stack.push(cursor);
|
|
205
|
+
if (frags.length === 0) {
|
|
206
|
+
if (matching && from.length !== 0) {
|
|
207
|
+
depth = args._outletChain.length + stack.length;
|
|
208
|
+
matching = false;
|
|
209
|
+
}
|
|
210
|
+
;
|
|
211
|
+
if (cursor.default) {
|
|
212
|
+
args._addOutlet(cursor.default);
|
|
213
|
+
mask = cursor.default.mask;
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
args._addOutlet(blankLeaf);
|
|
217
|
+
mask = [];
|
|
218
|
+
}
|
|
138
219
|
}
|
|
139
|
-
else
|
|
140
|
-
|
|
141
|
-
|
|
220
|
+
else {
|
|
221
|
+
if (matching && from.length === 0) {
|
|
222
|
+
depth = args._outletChain.length + stack.length;
|
|
223
|
+
matching = false;
|
|
224
|
+
}
|
|
225
|
+
const segment = frags.splice(0, 1)[0];
|
|
226
|
+
const other = from.splice(0, 1)[0];
|
|
227
|
+
const subRoute = cursor.nested.get(segment);
|
|
228
|
+
if (subRoute) {
|
|
229
|
+
if (matching && segment !== other) {
|
|
230
|
+
depth = args._outletChain.length + stack.length;
|
|
231
|
+
matching = false;
|
|
232
|
+
}
|
|
233
|
+
;
|
|
234
|
+
stack.push(subRoute);
|
|
235
|
+
}
|
|
236
|
+
else if (cursor.wild) {
|
|
237
|
+
if (matching && cursor.nested.has(other)) {
|
|
238
|
+
depth = args._outletChain.length + stack.length;
|
|
239
|
+
matching = false;
|
|
240
|
+
}
|
|
241
|
+
;
|
|
242
|
+
args.params[cursor.wildCard] = segment;
|
|
243
|
+
stack.push(cursor.wild);
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
args._addOutlet(blankLeaf);
|
|
247
|
+
mask = [];
|
|
248
|
+
}
|
|
142
249
|
}
|
|
143
250
|
}
|
|
144
251
|
else {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (subRoute) {
|
|
148
|
-
out = subRoute._recursiveRender(args, frags);
|
|
149
|
-
}
|
|
150
|
-
else if (this.wild) {
|
|
151
|
-
args.params[this.wildCard] = segment;
|
|
152
|
-
out = this.wild._recursiveRender(args, frags);
|
|
153
|
-
}
|
|
154
|
-
else {
|
|
155
|
-
out.outlet = () => {
|
|
156
|
-
throw new shared_1.ErrorResponse(404, "Resource Not Found", `Unable to find ${args.url.pathname}`);
|
|
157
|
-
};
|
|
252
|
+
if (cursor.route) {
|
|
253
|
+
args._addOutlet(cursor.route);
|
|
158
254
|
}
|
|
159
255
|
}
|
|
160
|
-
// Is this route masked out?
|
|
161
|
-
const ignored = out.mask.splice(0, 1)[0] === true;
|
|
162
|
-
if (!ignored && this.route) {
|
|
163
|
-
out.outlet = this.route.makeOutlet(args, out.outlet);
|
|
164
|
-
}
|
|
165
|
-
return out;
|
|
166
256
|
}
|
|
257
|
+
if (matching) {
|
|
258
|
+
depth = args._outletChain.length;
|
|
259
|
+
}
|
|
260
|
+
args._applyMask(mask, depth);
|
|
261
|
+
return depth;
|
|
167
262
|
}
|
|
168
|
-
exports.RouteTree = RouteTree;
|
package/bin/shared.d.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
/// <reference types="node" />
|
|
3
3
|
import type http from "node:http";
|
|
4
|
+
import { RenderArgs } from "./render-args";
|
|
4
5
|
export type Outlet = () => Promise<string>;
|
|
5
|
-
export type
|
|
6
|
-
export type
|
|
6
|
+
export type CatchFunction = (routeName: string, args: RenderArgs, err: ErrorResponse) => Promise<string>;
|
|
7
|
+
export type RenderFunction = (routeName: string, args: RenderArgs) => Promise<string>;
|
|
8
|
+
export type AuthFunction = (args: RenderArgs) => Promise<string>;
|
|
7
9
|
export type RouteModule = {
|
|
8
10
|
Render?: RenderFunction;
|
|
9
11
|
CatchError?: CatchFunction;
|
|
12
|
+
Auth?: AuthFunction;
|
|
10
13
|
};
|
|
11
14
|
export declare class ErrorResponse {
|
|
12
15
|
code: number;
|
|
@@ -23,19 +26,3 @@ export declare class Override {
|
|
|
23
26
|
data: string | Buffer | Uint8Array;
|
|
24
27
|
constructor(data: string | Buffer | Uint8Array);
|
|
25
28
|
}
|
|
26
|
-
type MetaHTML = {
|
|
27
|
-
[key: string]: string;
|
|
28
|
-
};
|
|
29
|
-
export declare class RenderArgs {
|
|
30
|
-
req: http.IncomingMessage;
|
|
31
|
-
res: http.ServerResponse;
|
|
32
|
-
params: MetaHTML;
|
|
33
|
-
url: URL;
|
|
34
|
-
links: MetaHTML[];
|
|
35
|
-
meta: MetaHTML[];
|
|
36
|
-
constructor(req: http.IncomingMessage, res: http.ServerResponse, url: URL);
|
|
37
|
-
addLinks(links: MetaHTML[], override?: boolean): void;
|
|
38
|
-
addMeta(links: MetaHTML[], override?: boolean): void;
|
|
39
|
-
renderHeadHTML(): string;
|
|
40
|
-
}
|
|
41
|
-
export {};
|
package/bin/shared.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.Override = exports.Redirect = exports.ErrorResponse = void 0;
|
|
4
4
|
class ErrorResponse {
|
|
5
5
|
constructor(statusCode, statusMessage, data) {
|
|
6
6
|
this.code = statusCode;
|
|
@@ -26,67 +26,3 @@ class Override {
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
exports.Override = Override;
|
|
29
|
-
const attrRegex = /^[A-z][A-z\-0-9]+$/;
|
|
30
|
-
function ValidateMetaHTML(val) {
|
|
31
|
-
for (const key in val) {
|
|
32
|
-
if (!attrRegex.test(key))
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
return true;
|
|
36
|
-
}
|
|
37
|
-
function ValidateMetaHTMLs(val) {
|
|
38
|
-
for (const meta of val) {
|
|
39
|
-
if (!ValidateMetaHTML(meta))
|
|
40
|
-
return false;
|
|
41
|
-
}
|
|
42
|
-
return true;
|
|
43
|
-
}
|
|
44
|
-
class RenderArgs {
|
|
45
|
-
constructor(req, res, url) {
|
|
46
|
-
this.req = req;
|
|
47
|
-
this.res = res;
|
|
48
|
-
this.url = url;
|
|
49
|
-
this.params = {};
|
|
50
|
-
this.links = [];
|
|
51
|
-
this.meta = [];
|
|
52
|
-
}
|
|
53
|
-
addLinks(links, override = false) {
|
|
54
|
-
if (!ValidateMetaHTMLs(links))
|
|
55
|
-
throw new Error(`Provided links have invalid attribute`);
|
|
56
|
-
if (override) {
|
|
57
|
-
this.links = links;
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
this.links.push(...links);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
addMeta(links, override = false) {
|
|
64
|
-
if (!ValidateMetaHTMLs(links))
|
|
65
|
-
throw new Error(`Provided links have invalid attribute`);
|
|
66
|
-
if (override) {
|
|
67
|
-
this.meta = links;
|
|
68
|
-
}
|
|
69
|
-
else {
|
|
70
|
-
this.meta.push(...links);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
renderHeadHTML() {
|
|
74
|
-
let out = "";
|
|
75
|
-
for (const elm of this.links) {
|
|
76
|
-
out += "<link";
|
|
77
|
-
for (const attr in elm) {
|
|
78
|
-
out += ` ${attr}="${elm[attr].replace(/"/g, "\\\"")}"`;
|
|
79
|
-
}
|
|
80
|
-
out += "></link>";
|
|
81
|
-
}
|
|
82
|
-
for (const elm of this.meta) {
|
|
83
|
-
out += "<meta";
|
|
84
|
-
for (const attr in elm) {
|
|
85
|
-
out += ` ${attr}="${elm[attr].replace(/"/g, "\\\"")}"`;
|
|
86
|
-
}
|
|
87
|
-
out += "></meta>";
|
|
88
|
-
}
|
|
89
|
-
return out;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
exports.RenderArgs = RenderArgs;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "htmx-router",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "A remix.js style file path router for htmX websites",
|
|
5
5
|
"main": "./bin/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"typescript": "^5.1.6"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"csstype": "^3.1.2"
|
|
28
|
+
"csstype": "^3.1.2",
|
|
29
|
+
"typed-html": "^3.0.1"
|
|
29
30
|
}
|
|
30
31
|
}
|