htmx-router 1.0.0-alpha.6 → 1.0.0-pre1
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/package.json +6 -8
- package/readme.md +20 -3
- package/cookies.d.ts +0 -25
- package/cookies.js +0 -60
- package/css.d.ts +0 -13
- package/css.js +0 -55
- package/dynamic.d.ts +0 -5
- package/dynamic.js +0 -42
- package/endpoint.d.ts +0 -13
- package/endpoint.js +0 -34
- package/event-source.d.ts +0 -26
- package/event-source.js +0 -123
- package/index.d.ts +0 -19
- package/index.js +0 -2
- package/internal/cli/config.d.ts +0 -13
- package/internal/cli/config.js +0 -11
- package/internal/cli/index.d.ts +0 -2
- package/internal/cli/index.js +0 -15
- package/internal/client.d.ts +0 -1
- package/internal/client.js +0 -14
- package/internal/compile/manifest.d.ts +0 -1
- package/internal/compile/manifest.js +0 -178
- package/internal/compile/router.d.ts +0 -1
- package/internal/compile/router.js +0 -51
- package/internal/component/dynamic.d.ts +0 -4
- package/internal/component/dynamic.js +0 -18
- package/internal/component/head.d.ts +0 -5
- package/internal/component/head.js +0 -22
- package/internal/component/scripts.d.ts +0 -4
- package/internal/component/scripts.js +0 -23
- package/internal/hash.d.ts +0 -4
- package/internal/hash.js +0 -10
- package/internal/mount.d.ts +0 -2
- package/internal/mount.js +0 -81
- package/internal/request/http.d.ts +0 -10
- package/internal/request/http.js +0 -61
- package/internal/request/index.d.ts +0 -16
- package/internal/request/index.js +0 -8
- package/internal/request/native.d.ts +0 -9
- package/internal/request/native.js +0 -48
- package/internal/util.d.ts +0 -4
- package/internal/util.js +0 -49
- package/request/http.d.ts +0 -10
- package/request/http.js +0 -59
- package/request/index.d.ts +0 -13
- package/request/index.js +0 -3
- package/request/native.d.ts +0 -9
- package/request/native.js +0 -46
- package/response.d.ts +0 -13
- package/response.js +0 -46
- package/router.d.ts +0 -51
- package/router.js +0 -231
- package/shell.d.ts +0 -120
- package/shell.js +0 -253
- package/util/parameters.d.ts +0 -7
- package/util/parameters.js +0 -14
- package/util/path-builder.d.ts +0 -1
- package/util/path-builder.js +0 -45
- package/util/route.d.ts +0 -2
- package/util/route.js +0 -58
- package/vite/bundle-splitter.d.ts +0 -4
- package/vite/bundle-splitter.js +0 -26
- package/vite/client-island.d.ts +0 -4
- package/vite/client-island.js +0 -14
- package/vite/code-splitting.d.ts +0 -4
- package/vite/code-splitting.js +0 -14
- package/vite/index.d.ts +0 -3
- package/vite/index.js +0 -3
- package/vite/router.d.ts +0 -2
- package/vite/router.js +0 -29
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
import { init, parse } from "es-module-lexer";
|
|
2
|
-
import { CutString, ServerOnlyWarning } from "../util.js";
|
|
3
|
-
ServerOnlyWarning("manifest-compiler");
|
|
4
|
-
export function CompileManifest(adapter, source, ssr) {
|
|
5
|
-
const imported = ParseImports(source);
|
|
6
|
-
if (ssr)
|
|
7
|
-
return BuildServerManifest(adapter, imported);
|
|
8
|
-
return BuildClientManifest(adapter, imported);
|
|
9
|
-
;
|
|
10
|
-
}
|
|
11
|
-
await init; // ensure the webassembly module is ready
|
|
12
|
-
function ParseImports(source) {
|
|
13
|
-
const parsed = parse(source)[0];
|
|
14
|
-
const out = [];
|
|
15
|
-
for (const imported of parsed) {
|
|
16
|
-
if (imported.a !== -1)
|
|
17
|
-
continue;
|
|
18
|
-
if (imported.t !== 1)
|
|
19
|
-
continue;
|
|
20
|
-
const href = source.slice(imported.s, imported.e);
|
|
21
|
-
if (href === "htmx-router")
|
|
22
|
-
continue;
|
|
23
|
-
const front = source.slice(imported.ss, imported.s);
|
|
24
|
-
const start = front.indexOf("{");
|
|
25
|
-
if (start === -1) {
|
|
26
|
-
const middle = CutString(CutString(front, "import")[1], "from", -1)[0];
|
|
27
|
-
out.push({ mapping: ExtractName(middle), href });
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
const end = front.lastIndexOf("}");
|
|
31
|
-
const middle = front.slice(start + 1, end);
|
|
32
|
-
const segments = middle.split(",");
|
|
33
|
-
out.push({ mapping: segments.map(ExtractName), href });
|
|
34
|
-
}
|
|
35
|
-
return out;
|
|
36
|
-
}
|
|
37
|
-
function ExtractName(str) {
|
|
38
|
-
const parts = CutString(str, " as ");
|
|
39
|
-
if (parts[1].length !== 0)
|
|
40
|
-
return { name: parts[1].trim(), original: parts[0].trim() };
|
|
41
|
-
const name = parts[0].trim();
|
|
42
|
-
return { name, original: name };
|
|
43
|
-
}
|
|
44
|
-
function BuildServerManifest(type, imported) {
|
|
45
|
-
const names = new Array();
|
|
46
|
-
for (const imp of imported) {
|
|
47
|
-
if (Array.isArray(imp.mapping))
|
|
48
|
-
names.push(...imp.mapping.map(x => x.name));
|
|
49
|
-
else
|
|
50
|
-
names.push(imp.mapping.name);
|
|
51
|
-
}
|
|
52
|
-
let out = "";
|
|
53
|
-
for (const imp of imported) {
|
|
54
|
-
out += "import ";
|
|
55
|
-
if (!Array.isArray(imp.mapping)) {
|
|
56
|
-
out += ImportNameSource(imp.mapping) + " ";
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
let first = true;
|
|
60
|
-
out += "{ ";
|
|
61
|
-
for (const name of imp.mapping) {
|
|
62
|
-
if (first)
|
|
63
|
-
first = false;
|
|
64
|
-
else
|
|
65
|
-
out += ", ";
|
|
66
|
-
out += ImportNameSource(name);
|
|
67
|
-
}
|
|
68
|
-
out += " } ";
|
|
69
|
-
}
|
|
70
|
-
out += `"${imp.href}";\n\n`;
|
|
71
|
-
}
|
|
72
|
-
out = `import { StyleClass } from "htmx-router/css";\n`
|
|
73
|
-
+ `const island = new StyleClass("i", ".this{display:contents;}\\n").name;\n\n`
|
|
74
|
-
+ "type FirstArg<T> = T extends (arg: infer U, ...args: any[]) => any ? U : never;\n"
|
|
75
|
-
+ "function mount(name: string, data: string, ssr?: JSX.Element) {\n"
|
|
76
|
-
+ "\treturn (<>\n"
|
|
77
|
-
+ `\t\t<div className={island}>{ssr}</div>\n`
|
|
78
|
-
+ `\t\t${SafeScript(type, "`Router.mountAboveWith('${name}', ${data})`")}\n`
|
|
79
|
-
+ "\t</>);\n"
|
|
80
|
-
+ "}\n"
|
|
81
|
-
+ "\n"
|
|
82
|
-
+ "const Client = {\n";
|
|
83
|
-
for (const name of names) {
|
|
84
|
-
out += `\t${name}: function(props: FirstArg<typeof ${name}> & { children?: JSX.Element }) {\n`
|
|
85
|
-
+ `\t\tconst { children, ...rest } = props;\n`
|
|
86
|
-
+ `\t\treturn mount("${name}", JSON.stringify(rest), children);\n`
|
|
87
|
-
+ `\t},\n`;
|
|
88
|
-
}
|
|
89
|
-
out += "}\nexport default Client;";
|
|
90
|
-
return out;
|
|
91
|
-
}
|
|
92
|
-
function ImportNameSource(name) {
|
|
93
|
-
if (name.original === name.name)
|
|
94
|
-
return name;
|
|
95
|
-
return `${name.original} as ${name.name}`;
|
|
96
|
-
}
|
|
97
|
-
function SafeScript(type, script) {
|
|
98
|
-
switch (type) {
|
|
99
|
-
case "react": return `<script dangerouslySetInnerHTML={{__html: ${script}}}></script>`;
|
|
100
|
-
default: return `<script>${script}</script>`;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
function BuildClientManifest(type, imports) {
|
|
104
|
-
const bind = binding[type];
|
|
105
|
-
if (!bind)
|
|
106
|
-
throw new Error(`Unsupported client adapter ${type}`);
|
|
107
|
-
let out = "const client = {\n";
|
|
108
|
-
for (const imported of imports) {
|
|
109
|
-
if (Array.isArray(imported.mapping)) {
|
|
110
|
-
for (const map of imported.mapping) {
|
|
111
|
-
out += `\t${map.name}: async (element: HTMLElement, props: any) => {\n`
|
|
112
|
-
+ `\t\tconst C = (await import("${imported.href}")).${map.original};\n`
|
|
113
|
-
+ bind.mount
|
|
114
|
-
+ `\n\t},\n`;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
out += `\t${imported.mapping.name}: async (element: HTMLElement, props: any) => {\n`
|
|
119
|
-
+ `\t\tconst C = (await import("${imported.href}")).default;\n`
|
|
120
|
-
+ bind.mount
|
|
121
|
-
+ `\n\t},\n`;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
out += "}\nexport default client;\n"
|
|
125
|
-
+ "(window as any).CLIENT = client;\n\n";
|
|
126
|
-
out += bind.unmount;
|
|
127
|
-
out += cleanup;
|
|
128
|
-
return out;
|
|
129
|
-
}
|
|
130
|
-
const binding = {
|
|
131
|
-
react: {
|
|
132
|
-
mount: '\t\tconst d = await import("react-dom/client");\n'
|
|
133
|
-
+ '\t\tconst r = d.createRoot(element);\n'
|
|
134
|
-
+ '\t\tr.render(<C {...props} />);\n'
|
|
135
|
-
+ '\t\tmounted.set(element, r);',
|
|
136
|
-
unmount: `
|
|
137
|
-
import type { Root } from "react-dom/client";
|
|
138
|
-
const mounted = new Map<HTMLElement, Root>();
|
|
139
|
-
function Unmount(node: HTMLElement, root: Root) {
|
|
140
|
-
mounted.delete(node);
|
|
141
|
-
root.unmount();
|
|
142
|
-
}`
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
const cleanup = `
|
|
146
|
-
|
|
147
|
-
const limbo = new Set<Node>();
|
|
148
|
-
let queued = false;
|
|
149
|
-
const observer = new MutationObserver((mutations) => {
|
|
150
|
-
for (const mut of mutations) {
|
|
151
|
-
for (const node of mut.removedNodes) limbo.add(node);
|
|
152
|
-
for (const node of mut.addedNodes) limbo.delete(node);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (!queued) {
|
|
156
|
-
queueMicrotask(Cleanup);
|
|
157
|
-
queued = true;
|
|
158
|
-
}
|
|
159
|
-
});
|
|
160
|
-
observer.observe(document.body, { childList: true, subtree: true });
|
|
161
|
-
|
|
162
|
-
function Cleanup() {
|
|
163
|
-
queued = false;
|
|
164
|
-
for (const elm of limbo) CleanNode(elm);
|
|
165
|
-
limbo.clear();
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
function CleanNode(node: Node) {
|
|
169
|
-
if (node instanceof HTMLElement) {
|
|
170
|
-
const root = mounted.get(node);
|
|
171
|
-
if (root) {
|
|
172
|
-
console.info("unmounting", node);
|
|
173
|
-
Unmount(node, root);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
for (const child of node.childNodes) CleanNode(child);
|
|
178
|
-
}`;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function CompileRouter(folder: string): string;
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
export function CompileRouter(folder) {
|
|
2
|
-
return `import { GenericContext, RouteTree } from "htmx-router/bin/router";
|
|
3
|
-
import { GetClientEntryURL } from 'htmx-router/bin/client/entry';
|
|
4
|
-
import { DynamicReference } from "htmx-router/bin/util/dynamic";
|
|
5
|
-
import { GetMountUrl } from 'htmx-router/bin/client/mount';
|
|
6
|
-
import { GetSheetUrl } from 'htmx-router/bin/util/css';
|
|
7
|
-
import { resolve } from "path";
|
|
8
|
-
|
|
9
|
-
const modules = import.meta.glob('/${folder}/**/*.{ts,tsx}', { eager: true });
|
|
10
|
-
console.log("${folder}");
|
|
11
|
-
console.log(modules);
|
|
12
|
-
|
|
13
|
-
export const tree = new RouteTree();
|
|
14
|
-
for (const path in modules) {
|
|
15
|
-
const mod = modules[path];
|
|
16
|
-
const tail = path.lastIndexOf(".");
|
|
17
|
-
const url = path.slice(${folder.length + 1}, tail);
|
|
18
|
-
tree.ingest(url, mod);
|
|
19
|
-
}`;
|
|
20
|
-
}
|
|
21
|
-
/*
|
|
22
|
-
export function Dynamic<T extends Record<string, string>>(props: {
|
|
23
|
-
params?: T,
|
|
24
|
-
loader: (ctx: GenericContext, params?: T) => Promise<JSX.Element>
|
|
25
|
-
children?: JSX.Element
|
|
26
|
-
}): JSX.Element {
|
|
27
|
-
return <div
|
|
28
|
-
hx-get={DynamicReference(props.loader, props.params)}
|
|
29
|
-
hx-trigger="load"
|
|
30
|
-
hx-swap="outerHTML transition:true"
|
|
31
|
-
style={{ display: "contents" }}
|
|
32
|
-
>{props.children ? props.children : ""}</div>
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
let headCache: JSX.Element | null = null;
|
|
36
|
-
const isProduction = process.env.NODE_ENV === "production";
|
|
37
|
-
const clientEntry = await GetClientEntryURL();
|
|
38
|
-
export function Scripts() {
|
|
39
|
-
if (headCache) return headCache;
|
|
40
|
-
|
|
41
|
-
const res = <>
|
|
42
|
-
<link href={GetSheetUrl()} rel="stylesheet"></link>
|
|
43
|
-
{ isProduction ? "" : <script type="module" src="/@vite/client"></script> }
|
|
44
|
-
<script type="module" src={clientEntry}></script>
|
|
45
|
-
<script src={GetMountUrl()}></script>
|
|
46
|
-
</>;
|
|
47
|
-
|
|
48
|
-
if (isProduction) headCache = res;
|
|
49
|
-
return res;
|
|
50
|
-
}
|
|
51
|
-
*/
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
const generic = `import { DynamicReference } from "htmx-router/dynamic";
|
|
2
|
-
import { GenericContext } from "htmx-router/router";
|
|
3
|
-
|
|
4
|
-
export function Dynamic<T extends Record<string, string>>(props: {
|
|
5
|
-
params?: T,
|
|
6
|
-
loader: (ctx: GenericContext, params?: T) => Promise<JSX.Element>
|
|
7
|
-
children?: JSX.Element
|
|
8
|
-
}): JSX.Element {
|
|
9
|
-
return <div
|
|
10
|
-
hx-get={DynamicReference(props.loader, props.params)}
|
|
11
|
-
hx-trigger="load"
|
|
12
|
-
hx-swap="outerHTML transition:true"
|
|
13
|
-
style={{ display: "contents" }}
|
|
14
|
-
>{props.children ? props.children : ""}</div>
|
|
15
|
-
}`;
|
|
16
|
-
export default {
|
|
17
|
-
"*": generic
|
|
18
|
-
};
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
const generic = `import { RenderMetaDescriptor, ShellOptions } from "htmx-router/shell";
|
|
2
|
-
|
|
3
|
-
export function Head<T>(props: { options: ShellOptions<T>, children: JSX.Element }) {
|
|
4
|
-
return <head>
|
|
5
|
-
{ RenderMetaDescriptor(props.options) as "safe" }
|
|
6
|
-
{ props.children as "safe" }
|
|
7
|
-
</head>;
|
|
8
|
-
}`;
|
|
9
|
-
const react = `import { RenderMetaDescriptor, ShellOptions } from "htmx-router/shell";
|
|
10
|
-
import { renderToString } from 'react-dom/server';
|
|
11
|
-
import { ReactNode } from "react";
|
|
12
|
-
|
|
13
|
-
export function Head<T>(props: { options: ShellOptions<T>, children: ReactNode }) {
|
|
14
|
-
const body = RenderMetaDescriptor(props.options)
|
|
15
|
-
+ renderToString(props.children);
|
|
16
|
-
|
|
17
|
-
return <head dangerouslySetInnerHTML={{ __html: body }}></head>;
|
|
18
|
-
}`;
|
|
19
|
-
export default {
|
|
20
|
-
"*": generic,
|
|
21
|
-
react
|
|
22
|
-
};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
const generic = `import { GetClientEntryURL } from 'htmx-router/internal/client';
|
|
2
|
-
import { GetMountUrl } from 'htmx-router/internal/mount';
|
|
3
|
-
import { GetSheetUrl } from 'htmx-router/css';
|
|
4
|
-
|
|
5
|
-
let cache: JSX.Element | null = null;
|
|
6
|
-
const isProduction = process.env.NODE_ENV === "production";
|
|
7
|
-
const clientEntry = await GetClientEntryURL();
|
|
8
|
-
export function Scripts() {
|
|
9
|
-
if (cache) return cache;
|
|
10
|
-
|
|
11
|
-
const res = <>
|
|
12
|
-
<link href={GetSheetUrl()} rel="stylesheet"></link>
|
|
13
|
-
{ isProduction ? "" : <script type="module" src="/@vite/client"></script> }
|
|
14
|
-
<script type="module" src={clientEntry}></script>
|
|
15
|
-
<script src={GetMountUrl()}></script>
|
|
16
|
-
</>;
|
|
17
|
-
|
|
18
|
-
if (isProduction) cache = res;
|
|
19
|
-
return res;
|
|
20
|
-
}`;
|
|
21
|
-
export default {
|
|
22
|
-
"*": generic
|
|
23
|
-
};
|
package/internal/hash.d.ts
DELETED
package/internal/hash.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Not the best hash in the world, but it's something really fast that will work on all JS runtimes
|
|
3
|
-
*/
|
|
4
|
-
export function QuickHash(input) {
|
|
5
|
-
let hash = 0;
|
|
6
|
-
for (let i = 0; i < input.length; i++) {
|
|
7
|
-
hash = (hash * 31 + input.charCodeAt(i)) >>> 0;
|
|
8
|
-
}
|
|
9
|
-
return hash.toString(36).slice(0, 5);
|
|
10
|
-
}
|
package/internal/mount.d.ts
DELETED
package/internal/mount.js
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { ServerOnlyWarning } from "./util.js";
|
|
2
|
-
ServerOnlyWarning("client-mounter");
|
|
3
|
-
import { CutString, QuickHash } from "./util.js";
|
|
4
|
-
// this function simply exists so it can be stringified and written into the client js bundle
|
|
5
|
-
function ClientMounter() {
|
|
6
|
-
const theme = {
|
|
7
|
-
get: () => {
|
|
8
|
-
return (localStorage.getItem("theme") || theme.infer());
|
|
9
|
-
},
|
|
10
|
-
infer: () => {
|
|
11
|
-
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
12
|
-
const current = prefersDark ? 'dark' : 'light';
|
|
13
|
-
localStorage.setItem("theme", current);
|
|
14
|
-
return current;
|
|
15
|
-
},
|
|
16
|
-
apply: () => {
|
|
17
|
-
document.documentElement.setAttribute('data-theme', theme.get());
|
|
18
|
-
},
|
|
19
|
-
toggle: () => {
|
|
20
|
-
if (theme.get() === "dark")
|
|
21
|
-
localStorage.setItem("theme", "light");
|
|
22
|
-
else
|
|
23
|
-
localStorage.setItem("theme", "dark");
|
|
24
|
-
theme.apply();
|
|
25
|
-
return localStorage.getItem("theme");
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
|
29
|
-
theme.infer();
|
|
30
|
-
theme.apply();
|
|
31
|
-
});
|
|
32
|
-
theme.apply();
|
|
33
|
-
const global = window;
|
|
34
|
-
const mountRequests = new Array();
|
|
35
|
-
function RequestMount(funcName, json) {
|
|
36
|
-
const elm = document.currentScript.previousElementSibling;
|
|
37
|
-
if (elm.hasAttribute("mounted"))
|
|
38
|
-
return;
|
|
39
|
-
mountRequests.push([funcName, elm, json]);
|
|
40
|
-
}
|
|
41
|
-
function Mount() {
|
|
42
|
-
if (mountRequests.length < 1)
|
|
43
|
-
return;
|
|
44
|
-
if (!global.CLIENT)
|
|
45
|
-
throw new Error("Client manifest missing");
|
|
46
|
-
for (const [funcName, element, json] of mountRequests) {
|
|
47
|
-
console.info("hydrating", funcName, "into", element);
|
|
48
|
-
const func = global.CLIENT[funcName];
|
|
49
|
-
if (!func)
|
|
50
|
-
throw new Error(`Component ${funcName} is missing from client manifest`);
|
|
51
|
-
func(element, json);
|
|
52
|
-
element.setAttribute("mounted", "yes");
|
|
53
|
-
}
|
|
54
|
-
mountRequests.length = 0;
|
|
55
|
-
}
|
|
56
|
-
document.addEventListener("DOMContentLoaded", Mount);
|
|
57
|
-
document.addEventListener("htmx:load", Mount);
|
|
58
|
-
return {
|
|
59
|
-
mountAboveWith: RequestMount,
|
|
60
|
-
theme
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
;
|
|
64
|
-
const script = "window.Router = (function () {"
|
|
65
|
-
+ CutString(ClientMounter.toString(), "{")[1]
|
|
66
|
-
+ ")();";
|
|
67
|
-
const hash = QuickHash(script);
|
|
68
|
-
export function _resolve(fragments) {
|
|
69
|
-
if (!fragments[2])
|
|
70
|
-
return null;
|
|
71
|
-
// const build = GetSheet();
|
|
72
|
-
if (!fragments[2].startsWith(hash))
|
|
73
|
-
return null;
|
|
74
|
-
const headers = new Headers();
|
|
75
|
-
headers.set("Content-Type", "text/javascript");
|
|
76
|
-
headers.set("Cache-Control", "public, max-age=604800");
|
|
77
|
-
return new Response(script, { headers });
|
|
78
|
-
}
|
|
79
|
-
export function GetMountUrl() {
|
|
80
|
-
return `/_/mount/${hash}.js`;
|
|
81
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { IncomingMessage, ServerResponse } from "http";
|
|
2
|
-
import type { ViteDevServer } from "vite";
|
|
3
|
-
import type { GenericContext } from "../../router.js";
|
|
4
|
-
type Config = {
|
|
5
|
-
build: Promise<any> | (() => Promise<Record<string, any>>);
|
|
6
|
-
viteDevServer: ViteDevServer | null;
|
|
7
|
-
render: GenericContext["render"];
|
|
8
|
-
};
|
|
9
|
-
export declare function createRequestHandler(config: Config): (req: IncomingMessage, res: ServerResponse) => Promise<void>;
|
|
10
|
-
export {};
|
package/internal/request/http.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { ServerOnlyWarning } from "../util.js";
|
|
2
|
-
ServerOnlyWarning("http-request");
|
|
3
|
-
import { Resolve } from "./native.js";
|
|
4
|
-
export function createRequestHandler(config) {
|
|
5
|
-
return async (req, res) => {
|
|
6
|
-
try {
|
|
7
|
-
const mod = typeof config.build === "function" ? await config.build() : await config.build;
|
|
8
|
-
const request = NativeRequest(req);
|
|
9
|
-
let { response, headers } = await Resolve(request, mod.tree, config);
|
|
10
|
-
res.writeHead(response.status, headers);
|
|
11
|
-
if (response.body instanceof ReadableStream) {
|
|
12
|
-
const reader = response.body.getReader();
|
|
13
|
-
while (true) {
|
|
14
|
-
const { done, value } = await reader.read();
|
|
15
|
-
if (done)
|
|
16
|
-
break;
|
|
17
|
-
res.write(value); // `value` is a Uint8Array.
|
|
18
|
-
}
|
|
19
|
-
res.end();
|
|
20
|
-
}
|
|
21
|
-
else {
|
|
22
|
-
const rendered = await response.text();
|
|
23
|
-
res.end(rendered);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
catch (e) {
|
|
27
|
-
res.statusCode = 500;
|
|
28
|
-
if (e instanceof Error) {
|
|
29
|
-
console.error(e.stack);
|
|
30
|
-
config.viteDevServer?.ssrFixStacktrace(e);
|
|
31
|
-
res.end(e.stack);
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
console.error(e);
|
|
35
|
-
res.end(String(e));
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
function NativeRequest(req) {
|
|
41
|
-
const ctrl = new AbortController();
|
|
42
|
-
const headers = new Headers(req.headers);
|
|
43
|
-
const url = new URL(`http://${headers.get('host')}${req.originalUrl || req.url}`);
|
|
44
|
-
req.once('aborted', () => ctrl.abort());
|
|
45
|
-
const bodied = req.method !== "GET" && req.method !== "HEAD";
|
|
46
|
-
const request = new Request(url, {
|
|
47
|
-
headers,
|
|
48
|
-
method: req.method,
|
|
49
|
-
body: bodied ? req : undefined,
|
|
50
|
-
signal: ctrl.signal,
|
|
51
|
-
referrer: headers.get("referrer") || undefined,
|
|
52
|
-
// @ts-ignore
|
|
53
|
-
duplex: bodied ? 'half' : undefined
|
|
54
|
-
});
|
|
55
|
-
if (!request.headers.has("X-Real-IP")) {
|
|
56
|
-
const info = req.socket.address();
|
|
57
|
-
if ("address" in info)
|
|
58
|
-
request.headers.set("X-Real-IP", info.address);
|
|
59
|
-
}
|
|
60
|
-
return request;
|
|
61
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { ViteDevServer } from "vite";
|
|
2
|
-
import type { GenericContext, RouteTree } from '../../router.js';
|
|
3
|
-
import * as native from "./native.js";
|
|
4
|
-
import * as http from "./http.js";
|
|
5
|
-
export type Config = {
|
|
6
|
-
build: Promise<any> | (() => Promise<Record<string, any>>);
|
|
7
|
-
viteDevServer: ViteDevServer | null;
|
|
8
|
-
render: GenericContext["render"];
|
|
9
|
-
};
|
|
10
|
-
export type RouterModule = {
|
|
11
|
-
tree: RouteTree;
|
|
12
|
-
};
|
|
13
|
-
export declare const createRequestHandler: {
|
|
14
|
-
http: typeof http.createRequestHandler;
|
|
15
|
-
native: typeof native.createRequestHandler;
|
|
16
|
-
};
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { ServerOnlyWarning } from "../util.js";
|
|
2
|
-
ServerOnlyWarning("request");
|
|
3
|
-
import * as native from "./native.js";
|
|
4
|
-
import * as http from "./http.js";
|
|
5
|
-
export const createRequestHandler = {
|
|
6
|
-
http: http.createRequestHandler,
|
|
7
|
-
native: native.createRequestHandler
|
|
8
|
-
};
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { type RouteTree } from '../../router.js';
|
|
2
|
-
import type { Config } from './index.js';
|
|
3
|
-
export declare function createRequestHandler(config: Config): (req: Request) => Promise<Response>;
|
|
4
|
-
export declare function Resolve(request: Request, tree: RouteTree, config: Config): Promise<{
|
|
5
|
-
response: Response;
|
|
6
|
-
headers: {
|
|
7
|
-
[key: string]: string | string[];
|
|
8
|
-
};
|
|
9
|
-
}>;
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { ServerOnlyWarning } from "../util.js";
|
|
2
|
-
ServerOnlyWarning("native-request");
|
|
3
|
-
import { GenericContext } from '../../router.js';
|
|
4
|
-
export function createRequestHandler(config) {
|
|
5
|
-
return async (req) => {
|
|
6
|
-
try {
|
|
7
|
-
const mod = typeof config.build === "function" ? await config.build() : await config.build;
|
|
8
|
-
let { response } = await Resolve(req, mod.tree, config);
|
|
9
|
-
return response;
|
|
10
|
-
}
|
|
11
|
-
catch (e) {
|
|
12
|
-
if (e instanceof Error) {
|
|
13
|
-
console.error(e.stack);
|
|
14
|
-
config.viteDevServer?.ssrFixStacktrace(e);
|
|
15
|
-
return new Response(e.message + "\n" + e.stack, { status: 500, statusText: "Internal Server Error" });
|
|
16
|
-
}
|
|
17
|
-
else {
|
|
18
|
-
console.error(e);
|
|
19
|
-
return new Response(String(e), { status: 500, statusText: "Internal Server Error" });
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
export async function Resolve(request, tree, config) {
|
|
25
|
-
const url = new URL(request.url);
|
|
26
|
-
const ctx = new GenericContext(request, url, config.render);
|
|
27
|
-
const x = ctx.url.pathname.endsWith("/") ? ctx.url.pathname.slice(0, -1) : ctx.url.pathname;
|
|
28
|
-
const fragments = x.split("/").slice(1);
|
|
29
|
-
let response = await tree.resolve(fragments, ctx);
|
|
30
|
-
if (response === null)
|
|
31
|
-
response = new Response("No Route Found", { status: 404, statusText: "Not Found", headers: ctx.headers });
|
|
32
|
-
// Override with context headers
|
|
33
|
-
if (response.headers !== ctx.headers) {
|
|
34
|
-
for (const [key, value] of ctx.headers) {
|
|
35
|
-
if (response.headers.has(key))
|
|
36
|
-
continue;
|
|
37
|
-
response.headers.set(key, value);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
// Merge cookie changes
|
|
41
|
-
const headers = Object.fromEntries(response.headers);
|
|
42
|
-
const cookies = ctx.cookie.export();
|
|
43
|
-
if (cookies.length > 0) {
|
|
44
|
-
headers['set-cookie'] = cookies;
|
|
45
|
-
response.headers.set("Set-Cookie", cookies[0]); // Response object doesn't support multi-header..[]
|
|
46
|
-
}
|
|
47
|
-
return { response, headers };
|
|
48
|
-
}
|
package/internal/util.d.ts
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export declare function QuickHash(input: string): string;
|
|
2
|
-
export declare function CutString(str: string, pivot: string, offset?: number): [string, string];
|
|
3
|
-
export declare function Singleton<T>(name: string, cb: () => T): T;
|
|
4
|
-
export declare function ServerOnlyWarning(context: string): void;
|
package/internal/util.js
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
export function QuickHash(input) {
|
|
2
|
-
let hash = 0;
|
|
3
|
-
for (let i = 0; i < input.length; i++) {
|
|
4
|
-
hash = (hash * 31 + input.charCodeAt(i)) >>> 0;
|
|
5
|
-
}
|
|
6
|
-
return hash.toString(36).slice(0, 5);
|
|
7
|
-
}
|
|
8
|
-
export function CutString(str, pivot, offset = 1) {
|
|
9
|
-
if (offset > 0) {
|
|
10
|
-
let cursor = 0;
|
|
11
|
-
while (offset !== 0) {
|
|
12
|
-
const i = str.indexOf(pivot, cursor);
|
|
13
|
-
if (i === -1)
|
|
14
|
-
return [str, ""];
|
|
15
|
-
cursor = i + 1;
|
|
16
|
-
offset--;
|
|
17
|
-
}
|
|
18
|
-
cursor--;
|
|
19
|
-
return [str.slice(0, cursor), str.slice(cursor + pivot.length)];
|
|
20
|
-
}
|
|
21
|
-
if (offset < 0) {
|
|
22
|
-
let cursor = str.length;
|
|
23
|
-
while (offset !== 0) {
|
|
24
|
-
const i = str.lastIndexOf(pivot, cursor);
|
|
25
|
-
if (i === -1)
|
|
26
|
-
return [str, ""];
|
|
27
|
-
cursor = i - 1;
|
|
28
|
-
offset++;
|
|
29
|
-
}
|
|
30
|
-
cursor++;
|
|
31
|
-
return [str.slice(0, cursor), str.slice(cursor + pivot.length)];
|
|
32
|
-
}
|
|
33
|
-
return [str, ""];
|
|
34
|
-
}
|
|
35
|
-
export function Singleton(name, cb) {
|
|
36
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
37
|
-
const g = globalThis;
|
|
38
|
-
g.__singletons ??= {};
|
|
39
|
-
g.__singletons[name] ??= cb();
|
|
40
|
-
return g.__singletons[name];
|
|
41
|
-
}
|
|
42
|
-
export function ServerOnlyWarning(context) {
|
|
43
|
-
if (typeof process !== "undefined")
|
|
44
|
-
return;
|
|
45
|
-
if (typeof document == "undefined")
|
|
46
|
-
return;
|
|
47
|
-
console.warn(`Warn: Server-side only htmx-router feature ${context} has leaked to client code`);
|
|
48
|
-
console.log(typeof document, typeof process);
|
|
49
|
-
}
|
package/request/http.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { IncomingMessage, ServerResponse } from "http";
|
|
2
|
-
import type { ViteDevServer } from "vite";
|
|
3
|
-
import type { GenericContext } from "~/router.js";
|
|
4
|
-
type Config = {
|
|
5
|
-
build: Promise<any> | (() => Promise<Record<string, any>>);
|
|
6
|
-
viteDevServer: ViteDevServer | null;
|
|
7
|
-
render: GenericContext["render"];
|
|
8
|
-
};
|
|
9
|
-
export declare function createRequestHandler(config: Config): (req: IncomingMessage, res: ServerResponse) => Promise<void>;
|
|
10
|
-
export {};
|