@torpor/build 0.4.4 → 0.4.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/CHANGELOG.md +19 -0
- package/README.md +2 -2
- package/dist/{RouteType-VOBjKJ8I.js → RouteType-B6BMOyYw.mjs} +13 -12
- package/dist/RouteType-B6BMOyYw.mjs.map +1 -0
- package/dist/{Server-DL9neaq0.d.ts → Server-CF5wDJp6.d.mts} +1 -1
- package/dist/Server-CF5wDJp6.d.mts.map +1 -0
- package/dist/{ServerEvent-Dnk2FIKQ.js → ServerEvent-CxPwA4lH.mjs} +1 -1
- package/dist/ServerEvent-CxPwA4lH.mjs.map +1 -0
- package/dist/{Site-B8TxSzPk.d.ts → Site-DgC6WWn1.d.mts} +20 -9
- package/dist/Site-DgC6WWn1.d.mts.map +1 -0
- package/dist/{_page-ef72MjgG.js → _page-DBSsnHFG.mjs} +1 -1
- package/dist/_page-DBSsnHFG.mjs.map +1 -0
- package/dist/bin/index.js +7 -7
- package/dist/bin/index.js.map +1 -1
- package/dist/{connectMiddleware-PBcWWRr-.js → connectMiddleware-D7nehjj3.mjs} +3 -3
- package/dist/connectMiddleware-D7nehjj3.mjs.map +1 -0
- package/dist/{index.d.ts → index.d.mts} +11 -6
- package/dist/index.d.mts.map +1 -0
- package/dist/{index.js → index.mjs} +21 -12
- package/dist/index.mjs.map +1 -0
- package/dist/{nav.d.ts → nav.d.mts} +1 -1
- package/dist/nav.d.mts.map +1 -0
- package/dist/{nav.js → nav.mjs} +17 -12
- package/dist/nav.mjs.map +1 -0
- package/dist/{pathToRegex-BTPkBIYJ.js → pathToRegex-TUAMc3zS.mjs} +1 -1
- package/dist/pathToRegex-TUAMc3zS.mjs.map +1 -0
- package/dist/{response-KnPcsvtU.js → response-C5TtAsh1.mjs} +2 -2
- package/dist/response-C5TtAsh1.mjs.map +1 -0
- package/dist/{response.d.ts → response.d.mts} +1 -1
- package/dist/response.d.mts.map +1 -0
- package/dist/{response.js → response.mjs} +2 -2
- package/dist/{run-CaFHeN6K.js → run-DiZgkMnG.mjs} +37 -37
- package/dist/run-DiZgkMnG.mjs.map +1 -0
- package/dist/{run.d.ts → run.d.mts} +3 -3
- package/dist/run.d.mts.map +1 -0
- package/dist/run.mjs +8 -0
- package/dist/{seeOther-C-36lhS5.js → seeOther-B4Yhu9iq.mjs} +1 -1
- package/dist/seeOther-B4Yhu9iq.mjs.map +1 -0
- package/dist/{server.d.ts → server.d.mts} +2 -2
- package/dist/server.d.mts.map +1 -0
- package/dist/{server.js → server.mjs} +3 -3
- package/dist/{state.d.ts → state.d.mts} +1 -1
- package/dist/state.d.mts.map +1 -0
- package/dist/state.mjs +3 -0
- package/dist/{test.d.ts → test.d.mts} +3 -3
- package/dist/test.d.mts.map +1 -0
- package/dist/{test.js → test.mjs} +25 -14
- package/dist/test.mjs.map +1 -0
- package/package.json +26 -24
- package/scripts/postbuild.ts +12 -0
- package/src/nav/formSubmit.ts +2 -1
- package/src/nav/loadData.ts +16 -5
- package/src/nav/navigate.ts +1 -0
- package/src/site/Router.ts +12 -13
- package/src/site/Site.ts +29 -4
- package/src/site/clientEntry.ts +1 -1
- package/src/site/manifest.ts +5 -2
- package/src/test/runTest.ts +15 -6
- package/src/types/ManifestRoute.ts +1 -0
- package/src/types/Route.ts +11 -0
- package/src/types/RouteHandler.ts +4 -0
- package/test/api/_hook.server.ts +3 -0
- package/test/api/posts/+page.server.ts +3 -0
- package/test/api/posts/[id]/+page.server.ts +3 -0
- package/test/router.test.ts +78 -0
- package/test/routes/+page.server.ts +3 -0
- package/test/routes/+page.ts +3 -0
- package/test/routes/_error.ts +3 -0
- package/test/routes/_hook.server.ts +3 -0
- package/test/routes/_layout.server.ts +3 -0
- package/test/routes/_layout.ts +3 -0
- package/test/routes/posts/+page.ts +3 -0
- package/test/routes/posts/[id]/+page.ts +3 -0
- package/test/site.test.ts +40 -0
- package/tsdown.config.ts +3 -10
- package/vitest.config.ts +2 -2
- package/dist/RouteType-VOBjKJ8I.js.map +0 -1
- package/dist/Server-DL9neaq0.d.ts.map +0 -1
- package/dist/ServerEvent-Dnk2FIKQ.js.map +0 -1
- package/dist/Site-B8TxSzPk.d.ts.map +0 -1
- package/dist/_page-ef72MjgG.js.map +0 -1
- package/dist/connectMiddleware-PBcWWRr-.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/nav.d.ts.map +0 -1
- package/dist/nav.js.map +0 -1
- package/dist/pathToRegex-BTPkBIYJ.js.map +0 -1
- package/dist/response-KnPcsvtU.js.map +0 -1
- package/dist/response.d.ts.map +0 -1
- package/dist/run-CaFHeN6K.js.map +0 -1
- package/dist/run.d.ts.map +0 -1
- package/dist/run.js +0 -8
- package/dist/seeOther-C-36lhS5.js.map +0 -1
- package/dist/server.d.ts.map +0 -1
- package/dist/state.d.ts.map +0 -1
- package/dist/state.js +0 -3
- package/dist/test.d.ts.map +0 -1
- package/dist/test.js.map +0 -1
- /package/dist/bin/{index.d.ts → index.d.mts} +0 -0
package/package.json
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@torpor/build",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.6",
|
|
4
4
|
"description": "A framework for building sites and apps with Torpor",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": {
|
|
8
|
-
"types": "./dist/index.d.
|
|
9
|
-
"import": "./dist/index.
|
|
8
|
+
"types": "./dist/index.d.mts",
|
|
9
|
+
"import": "./dist/index.mjs"
|
|
10
10
|
},
|
|
11
11
|
"./nav": {
|
|
12
|
-
"types": "./dist/nav.d.
|
|
13
|
-
"import": "./dist/nav.
|
|
12
|
+
"types": "./dist/nav.d.mts",
|
|
13
|
+
"import": "./dist/nav.mjs"
|
|
14
14
|
},
|
|
15
15
|
"./response": {
|
|
16
|
-
"types": "./dist/response.d.
|
|
17
|
-
"import": "./dist/response.
|
|
16
|
+
"types": "./dist/response.d.mts",
|
|
17
|
+
"import": "./dist/response.mjs"
|
|
18
18
|
},
|
|
19
19
|
"./run": {
|
|
20
|
-
"types": "./dist/run.d.
|
|
21
|
-
"import": "./dist/run.
|
|
20
|
+
"types": "./dist/run.d.mts",
|
|
21
|
+
"import": "./dist/run.mjs"
|
|
22
22
|
},
|
|
23
23
|
"./server": {
|
|
24
|
-
"types": "./dist/server.d.
|
|
25
|
-
"import": "./dist/server.
|
|
24
|
+
"types": "./dist/server.d.mts",
|
|
25
|
+
"import": "./dist/server.mjs"
|
|
26
26
|
},
|
|
27
27
|
"./state": {
|
|
28
|
-
"types": "./dist/state.d.
|
|
29
|
-
"import": "./dist/state.
|
|
28
|
+
"types": "./dist/state.d.mts",
|
|
29
|
+
"import": "./dist/state.mjs"
|
|
30
30
|
},
|
|
31
31
|
"./test": {
|
|
32
|
-
"types": "./dist/test.d.
|
|
33
|
-
"import": "./dist/test.
|
|
32
|
+
"types": "./dist/test.d.mts",
|
|
33
|
+
"import": "./dist/test.mjs"
|
|
34
34
|
},
|
|
35
35
|
"./manifest": {
|
|
36
36
|
"types": "./src/types/manifest.d.ts"
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"access": "public"
|
|
41
41
|
},
|
|
42
42
|
"bin": {
|
|
43
|
-
"tb": "./dist/bin/index.
|
|
43
|
+
"tb": "./dist/bin/index.mjs"
|
|
44
44
|
},
|
|
45
45
|
"keywords": [],
|
|
46
46
|
"author": "",
|
|
@@ -48,23 +48,25 @@
|
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"cookie": "^1.0.2",
|
|
50
50
|
"dotenv": "^17.2.3",
|
|
51
|
-
"vite": "^7.
|
|
51
|
+
"vite": "^7.2.2",
|
|
52
52
|
"vite-tsconfig-paths": "^5.1.4",
|
|
53
53
|
"@torpor/unplugin": "^0.2.1",
|
|
54
|
-
"@torpor/view": "^0.4.
|
|
54
|
+
"@torpor/view": "^0.4.14"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@trivago/prettier-plugin-sort-imports": "^
|
|
58
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
59
|
-
"
|
|
60
|
-
"
|
|
57
|
+
"@trivago/prettier-plugin-sort-imports": "^6.0.0",
|
|
58
|
+
"@typescript/native-preview": "7.0.0-dev.20251112.1",
|
|
59
|
+
"esno": "^4.8.0",
|
|
60
|
+
"oxlint-tsgolint": "^0.6.0",
|
|
61
|
+
"tsdown": "^0.16.4",
|
|
61
62
|
"typescript": "^5.9.3",
|
|
62
|
-
"vitest": "^4.0.
|
|
63
|
+
"vitest": "^4.0.8"
|
|
63
64
|
},
|
|
64
65
|
"scripts": {
|
|
66
|
+
"check": "tsgo --noEmit && pnpm dlx oxlint --type-aware",
|
|
65
67
|
"build": "tsgo --noEmit && tsdown",
|
|
68
|
+
"build:fix": "esno scripts/postbuild.ts",
|
|
66
69
|
"build:watch": "tsdown --watch",
|
|
67
|
-
"check": "tsgo --noEmit && pnpm dlx oxlint --type-aware",
|
|
68
70
|
"test": "vitest"
|
|
69
71
|
}
|
|
70
72
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { renameSync } from "node:fs";
|
|
2
|
+
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
// HACK: tsdown only creates *.mjs files now (post v0.16), so we need to rename
|
|
6
|
+
// the bin/index.mjs file to bin/index.js for it to be run by node
|
|
7
|
+
|
|
8
|
+
const scriptsFolder = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const distFolder = resolve(scriptsFolder, "../dist");
|
|
10
|
+
|
|
11
|
+
renameSync(join(distFolder, "bin", "index.mjs"), join(distFolder, "bin", "index.js"));
|
|
12
|
+
renameSync(join(distFolder, "bin", "index.mjs.map"), join(distFolder, "bin", "index.js.map"));
|
package/src/nav/formSubmit.ts
CHANGED
|
@@ -12,6 +12,7 @@ export default async function formSubmit(e: SubmitEvent): Promise<void> {
|
|
|
12
12
|
|
|
13
13
|
// Perform a POST fetch to the form's action location
|
|
14
14
|
const form = e.target as HTMLFormElement;
|
|
15
|
+
const action = (e.submitter as HTMLButtonElement)?.formAction ?? form.action;
|
|
15
16
|
const options = {
|
|
16
17
|
body: new FormData(form),
|
|
17
18
|
method: "POST",
|
|
@@ -25,7 +26,7 @@ export default async function formSubmit(e: SubmitEvent): Promise<void> {
|
|
|
25
26
|
},
|
|
26
27
|
};
|
|
27
28
|
const response = await fetch(
|
|
28
|
-
|
|
29
|
+
action,
|
|
29
30
|
// @ts-ignore -- don't know what it's complaining about here
|
|
30
31
|
options,
|
|
31
32
|
);
|
package/src/nav/loadData.ts
CHANGED
|
@@ -8,6 +8,7 @@ export default async function loadData(
|
|
|
8
8
|
handler: RouteHandler,
|
|
9
9
|
params: Record<string, string>,
|
|
10
10
|
path: string,
|
|
11
|
+
query: URLSearchParams,
|
|
11
12
|
newLayoutStack: LayoutPath[],
|
|
12
13
|
clientEndPoint: PageEndPoint | undefined,
|
|
13
14
|
serverEndPoint: PageServerEndPoint | undefined,
|
|
@@ -36,6 +37,7 @@ export default async function loadData(
|
|
|
36
37
|
const layoutResponse = await loadClientAndServerData(
|
|
37
38
|
stackLayout.data,
|
|
38
39
|
document.location.origin + layoutPath,
|
|
40
|
+
query,
|
|
39
41
|
params,
|
|
40
42
|
layoutEndPoint,
|
|
41
43
|
layoutServerEndPoint,
|
|
@@ -51,6 +53,7 @@ export default async function loadData(
|
|
|
51
53
|
let endPointResponse = await loadClientAndServerData(
|
|
52
54
|
data,
|
|
53
55
|
document.location.origin + path,
|
|
56
|
+
query,
|
|
54
57
|
params,
|
|
55
58
|
clientEndPoint,
|
|
56
59
|
serverEndPoint,
|
|
@@ -64,6 +67,7 @@ export default async function loadData(
|
|
|
64
67
|
async function loadClientAndServerData(
|
|
65
68
|
data: Record<string, any>,
|
|
66
69
|
location: string,
|
|
70
|
+
query: URLSearchParams,
|
|
67
71
|
params: Record<string, string>,
|
|
68
72
|
clientEndPoint?: PageEndPoint,
|
|
69
73
|
serverEndPoint?: PageServerEndPoint,
|
|
@@ -75,6 +79,9 @@ async function loadClientAndServerData(
|
|
|
75
79
|
Object.assign(data, prefetchedData[location]);
|
|
76
80
|
} else {
|
|
77
81
|
const clientUrl = new URL(document.location.href);
|
|
82
|
+
for (let [name, value] of query) {
|
|
83
|
+
clientUrl.searchParams.append(name, value);
|
|
84
|
+
}
|
|
78
85
|
const clientParams = buildClientParams(clientUrl, params, data);
|
|
79
86
|
const clientResponse = await clientEndPoint.load(clientParams);
|
|
80
87
|
if (clientResponse) {
|
|
@@ -90,19 +97,23 @@ async function loadClientAndServerData(
|
|
|
90
97
|
}
|
|
91
98
|
}
|
|
92
99
|
}
|
|
93
|
-
if (serverEndPoint?.load) {
|
|
94
|
-
const serverUrl = location.replace(/\/$/, "") + "/~server";
|
|
95
100
|
|
|
96
|
-
|
|
97
|
-
|
|
101
|
+
if (serverEndPoint?.load) {
|
|
102
|
+
const serverLocation = location.replace(/\/$/, "") + "/~server";
|
|
103
|
+
if (prefetchedData[serverLocation]) {
|
|
104
|
+
Object.assign(data, prefetchedData[serverLocation]);
|
|
98
105
|
} else {
|
|
106
|
+
const serverUrl = new URL(serverLocation);
|
|
107
|
+
for (let [name, value] of query) {
|
|
108
|
+
serverUrl.searchParams.append(name, value);
|
|
109
|
+
}
|
|
99
110
|
const serverResponse = await fetch(serverUrl);
|
|
100
111
|
if (serverResponse) {
|
|
101
112
|
if (serverResponse.ok) {
|
|
102
113
|
if (serverResponse.headers.get("Content-Type")?.includes("application/json")) {
|
|
103
114
|
const serverData = await serverResponse.json();
|
|
104
115
|
Object.assign(data, serverData);
|
|
105
|
-
prefetchedData[
|
|
116
|
+
prefetchedData[serverLocation] = serverData;
|
|
106
117
|
}
|
|
107
118
|
} else {
|
|
108
119
|
return serverResponse;
|
package/src/nav/navigate.ts
CHANGED
package/src/site/Router.ts
CHANGED
|
@@ -23,6 +23,7 @@ export default class Router {
|
|
|
23
23
|
path: r.path,
|
|
24
24
|
type: r.type,
|
|
25
25
|
endPoint: r.endPoint,
|
|
26
|
+
subFolder: r.subFolder,
|
|
26
27
|
}),
|
|
27
28
|
);
|
|
28
29
|
}
|
|
@@ -31,12 +32,13 @@ export default class Router {
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
// TODO: Allow calling with the endpoint itself, so that you can setup an app with no scaffold
|
|
34
|
-
addPage(path: string, type: number, endPoint: () => Promise<any
|
|
35
|
+
addPage(path: string, type: number, endPoint: () => Promise<any>, subFolder?: string): this {
|
|
35
36
|
this.routes.push(
|
|
36
37
|
new Route(path, {
|
|
37
38
|
path,
|
|
38
39
|
type,
|
|
39
40
|
endPoint,
|
|
41
|
+
subFolder,
|
|
40
42
|
}),
|
|
41
43
|
);
|
|
42
44
|
this.#sortRoutes();
|
|
@@ -53,7 +55,7 @@ export default class Router {
|
|
|
53
55
|
}
|
|
54
56
|
*/
|
|
55
57
|
|
|
56
|
-
match(path: string, query: URLSearchParams):
|
|
58
|
+
match(path: string, query: URLSearchParams): RouteMatch | undefined {
|
|
57
59
|
for (let route of this.routes) {
|
|
58
60
|
let match = path.match(route.regex);
|
|
59
61
|
if (match) {
|
|
@@ -69,17 +71,15 @@ export default class Router {
|
|
|
69
71
|
};
|
|
70
72
|
}
|
|
71
73
|
}
|
|
72
|
-
|
|
73
|
-
console.log(`not found: '${path}'`);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
#loadHandler(handler: RouteHandler, path: string) {
|
|
77
|
-
handler.layouts = this.#findLayouts(path);
|
|
77
|
+
handler.layouts = this.#findLayouts(path, handler);
|
|
78
78
|
handler.serverEndPoint = this.#findServer(path);
|
|
79
|
-
handler.serverHook = this.#findServerHook(
|
|
79
|
+
handler.serverHook = this.#findServerHook(handler);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
#findLayouts(path: string): LayoutHandler[] | undefined {
|
|
82
|
+
#findLayouts(path: string, handler: RouteHandler): LayoutHandler[] | undefined {
|
|
83
83
|
let layouts: LayoutHandler[] = [];
|
|
84
84
|
let parts = path
|
|
85
85
|
// Strip the trailing`~/server` (so we don't look for `~/server/_layout`
|
|
@@ -96,7 +96,7 @@ export default class Router {
|
|
|
96
96
|
}
|
|
97
97
|
const layoutPath = basePath + "/_layout";
|
|
98
98
|
const layoutRoute = this.routes.find((r) => r.path === layoutPath);
|
|
99
|
-
if (layoutRoute) {
|
|
99
|
+
if (layoutRoute && layoutRoute.handler.subFolder === handler.subFolder) {
|
|
100
100
|
layouts.push({
|
|
101
101
|
path: layoutPath,
|
|
102
102
|
endPoint: layoutRoute.handler.endPoint,
|
|
@@ -113,12 +113,11 @@ export default class Router {
|
|
|
113
113
|
return serverRoute && serverRoute.handler.endPoint;
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
#findServerHook(
|
|
116
|
+
#findServerHook(handler: RouteHandler): (() => Promise<any>) | undefined {
|
|
117
117
|
// TODO: Should this be a collection, like layouts?
|
|
118
118
|
let serverHookPath = "/_hook/~server";
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
serverHookPath = "/api" + serverHookPath;
|
|
119
|
+
if (handler.subFolder !== undefined) {
|
|
120
|
+
serverHookPath = handler.subFolder + serverHookPath;
|
|
122
121
|
}
|
|
123
122
|
const serverHookRoute = this.routes.find((r) => r.path === serverHookPath);
|
|
124
123
|
return serverHookRoute && serverHookRoute.handler.endPoint;
|
|
@@ -158,7 +157,7 @@ class Route {
|
|
|
158
157
|
}
|
|
159
158
|
}
|
|
160
159
|
|
|
161
|
-
interface
|
|
160
|
+
interface RouteMatch {
|
|
162
161
|
handler: RouteHandler;
|
|
163
162
|
params?: Record<string, string>;
|
|
164
163
|
query: URLSearchParams;
|
package/src/site/Site.ts
CHANGED
|
@@ -3,6 +3,7 @@ import fpath from "node:path";
|
|
|
3
3
|
import { type Plugin, type UserConfig } from "vite";
|
|
4
4
|
import tsconfigPaths from "vite-tsconfig-paths";
|
|
5
5
|
import type Adapter from "../types/Adapter";
|
|
6
|
+
import type Route from "../types/Route";
|
|
6
7
|
import {
|
|
7
8
|
ERROR_ROUTE,
|
|
8
9
|
HOOK_ROUTE,
|
|
@@ -27,7 +28,7 @@ import defaultAdapter from "./defaultAdapter";
|
|
|
27
28
|
*/
|
|
28
29
|
export default class Site {
|
|
29
30
|
root: string;
|
|
30
|
-
routes:
|
|
31
|
+
routes: Route[] = [];
|
|
31
32
|
// Is default plugins a bad idea?
|
|
32
33
|
// HACK: Set loose because otherwise it uses a list of allowed extensions
|
|
33
34
|
// TODO: Should probably just do this ourselves and let the user pass in
|
|
@@ -56,17 +57,32 @@ export default class Site {
|
|
|
56
57
|
/**
|
|
57
58
|
* Adds a folder of routes, based on the names of the files in the folder
|
|
58
59
|
* @param folder The folder containing the routes
|
|
60
|
+
* @param subFolder If a subFolder is set, all routes in the folder will be
|
|
61
|
+
* placed under the subFolder, and only hooks, layouts etc will be used if
|
|
62
|
+
* they have the same subFolder. This can be used to create e.g. an `api`
|
|
63
|
+
* set of routes with their own hook for authorization from various clients
|
|
59
64
|
*/
|
|
60
|
-
async addRouteFolder(folder: string): Promise<void> {
|
|
65
|
+
async addRouteFolder(folder: string, subFolder?: string): Promise<void> {
|
|
61
66
|
const routeFolder = fpath.join(this.root, folder);
|
|
62
67
|
const routeFiles = await fs.readdir(routeFolder, { recursive: true });
|
|
68
|
+
if (subFolder !== undefined && !subFolder.startsWith("/")) {
|
|
69
|
+
subFolder = "/" + subFolder;
|
|
70
|
+
}
|
|
63
71
|
for (let file of routeFiles) {
|
|
64
72
|
// The file must start with `+` or `_` and end with `.js` or `.ts`
|
|
65
73
|
if (/^(\+|_).+(\.js|\.ts)$/.test(fpath.basename(file))) {
|
|
66
74
|
let routePath = this.#routePath(file);
|
|
75
|
+
if (subFolder !== undefined) {
|
|
76
|
+
routePath = subFolder + routePath;
|
|
77
|
+
}
|
|
67
78
|
let type = this.#routeType(file);
|
|
68
79
|
file = fpath.relative(this.root, fpath.resolve(routeFolder, file));
|
|
69
|
-
this.routes.push({
|
|
80
|
+
this.routes.push({
|
|
81
|
+
path: routePath,
|
|
82
|
+
file,
|
|
83
|
+
type,
|
|
84
|
+
subFolder: subFolder,
|
|
85
|
+
});
|
|
70
86
|
}
|
|
71
87
|
}
|
|
72
88
|
this.#sortRoutes();
|
|
@@ -91,10 +107,19 @@ export default class Site {
|
|
|
91
107
|
| "_hook"
|
|
92
108
|
| "_hook.server"
|
|
93
109
|
| "_error",
|
|
110
|
+
subFolder?: string,
|
|
94
111
|
): void {
|
|
95
112
|
let routePath = this.#routePath(fpath.join(path, type ?? fpath.basename(file)));
|
|
96
113
|
let routeType = type ? this.#routeTypeFromString(type) : this.#routeType(file);
|
|
97
|
-
|
|
114
|
+
if (subFolder !== undefined && !subFolder.startsWith("/")) {
|
|
115
|
+
subFolder = "/" + subFolder;
|
|
116
|
+
}
|
|
117
|
+
this.routes.push({
|
|
118
|
+
path: routePath,
|
|
119
|
+
file,
|
|
120
|
+
type: routeType,
|
|
121
|
+
subFolder: subFolder,
|
|
122
|
+
});
|
|
98
123
|
this.#sortRoutes();
|
|
99
124
|
}
|
|
100
125
|
|
package/src/site/clientEntry.ts
CHANGED
|
@@ -72,7 +72,7 @@ async function maybePrefetch(e: MouseEvent | TouchEvent) {
|
|
|
72
72
|
// Just dummy this up
|
|
73
73
|
let newStack: LayoutPath[] = [];
|
|
74
74
|
|
|
75
|
-
await loadData(handler, params, path, newStack, clientEndPoint, serverEndPoint, true);
|
|
75
|
+
await loadData(handler, params, path, query, newStack, clientEndPoint, serverEndPoint, true);
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
}
|
package/src/site/manifest.ts
CHANGED
|
@@ -30,16 +30,19 @@ export default {
|
|
|
30
30
|
routes: [
|
|
31
31
|
${site.routes
|
|
32
32
|
.map((r) => {
|
|
33
|
+
let endPoint: string;
|
|
33
34
|
if (serverRequest || !/server\.(ts|js)$/.test(r.file)) {
|
|
34
|
-
|
|
35
|
+
endPoint = `() => import(/* @vite-ignore */ "${path.join(site.root, r.file)}")`;
|
|
35
36
|
} else {
|
|
36
37
|
// On the client, for a server route, we need to check
|
|
37
38
|
// whether there's a load function and set a dummy endPoint
|
|
38
39
|
// if so. The correct endpoint will be hit in clientEntry
|
|
39
40
|
// HACK: Should do this better than checking for `load:` in source...
|
|
40
41
|
const haveLoad = readFileSync(path.join(site.root, r.file)).includes("load:");
|
|
41
|
-
|
|
42
|
+
endPoint = haveLoad ? "() => load" : "undefined";
|
|
42
43
|
}
|
|
44
|
+
const sub = r.subFolder ? `"${r.subFolder}"` : "undefined";
|
|
45
|
+
return `{ path: "${r.path}", type: ${r.type}, endPoint: ${endPoint}, subFolder: ${sub} }`;
|
|
43
46
|
})
|
|
44
47
|
.join(",\n ")}
|
|
45
48
|
],
|
package/src/test/runTest.ts
CHANGED
|
@@ -48,6 +48,7 @@ export default async function runTest(
|
|
|
48
48
|
path: r.path,
|
|
49
49
|
type: r.type,
|
|
50
50
|
endPoint: () => import(/* @vite-ignore */ path.join(site.root, r.file)),
|
|
51
|
+
subFolder: r.subFolder,
|
|
51
52
|
})),
|
|
52
53
|
);
|
|
53
54
|
|
|
@@ -201,17 +202,21 @@ async function loadView(
|
|
|
201
202
|
}
|
|
202
203
|
let $props: Record<string, any> = { data };
|
|
203
204
|
|
|
205
|
+
let styles = "";
|
|
206
|
+
|
|
204
207
|
// If there are layouts, work our way upwards, pushing each component into
|
|
205
208
|
// the default slot of its parent
|
|
206
209
|
// TODO: Also handle layout server data
|
|
207
210
|
// TODO: There's probably a nicer way to do this with reducers or something
|
|
208
|
-
// TODO: Re-use parent layouts
|
|
209
211
|
let component = clientEndPoint.component as ServerComponent;
|
|
210
212
|
let slots: Record<string, ServerSlotRender> | undefined = undefined;
|
|
211
213
|
if (handler.layouts) {
|
|
212
214
|
let slotFunctions: ServerSlotRender[] = [];
|
|
213
|
-
slotFunctions[handler.layouts.length] = (_, context) =>
|
|
214
|
-
(clientEndPoint.component as ServerComponent)($props, context);
|
|
215
|
+
slotFunctions[handler.layouts.length] = (_, $context) => {
|
|
216
|
+
let { body, head } = (clientEndPoint.component as ServerComponent)($props, $context);
|
|
217
|
+
styles += head;
|
|
218
|
+
return body;
|
|
219
|
+
};
|
|
215
220
|
for (let i = handler.layouts.length - 1; i >= 0; i--) {
|
|
216
221
|
const layoutEndPoint: PageEndPoint | undefined = (await handler.layouts[i].endPoint())
|
|
217
222
|
?.default;
|
|
@@ -220,10 +225,13 @@ async function loadView(
|
|
|
220
225
|
component = layoutEndPoint.component as ServerComponent;
|
|
221
226
|
slots = { _: slotFunctions[i + 1] };
|
|
222
227
|
} else {
|
|
223
|
-
slotFunctions[i] = (_, context) =>
|
|
224
|
-
(layoutEndPoint.component as ServerComponent)($props, context, {
|
|
228
|
+
slotFunctions[i] = (_, $context) => {
|
|
229
|
+
let { body, head } = (layoutEndPoint.component as ServerComponent)($props, $context, {
|
|
225
230
|
_: slotFunctions[i + 1],
|
|
226
231
|
});
|
|
232
|
+
styles += head;
|
|
233
|
+
return body;
|
|
234
|
+
};
|
|
227
235
|
}
|
|
228
236
|
}
|
|
229
237
|
}
|
|
@@ -232,7 +240,8 @@ async function loadView(
|
|
|
232
240
|
let html;
|
|
233
241
|
try {
|
|
234
242
|
let { body, head } = component($props, undefined, slots);
|
|
235
|
-
|
|
243
|
+
styles += head;
|
|
244
|
+
html = template.replace("%COMPONENT_BODY%", body).replace("%COMPONENT_HEAD", styles);
|
|
236
245
|
} catch (error) {
|
|
237
246
|
// TODO: Show a proper Error component
|
|
238
247
|
html = '<span style="color: red">Script syntax error</span><p>' + error + "</p>";
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import type RouteLayoutHandler from "./RouteLayoutHandler";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* A handler for a route in the Router.
|
|
5
|
+
*/
|
|
3
6
|
export default interface RouteHandler {
|
|
4
7
|
path: string;
|
|
5
8
|
type: number;
|
|
6
9
|
endPoint: () => Promise<any>;
|
|
10
|
+
subFolder?: string;
|
|
7
11
|
|
|
8
12
|
loaded?: boolean;
|
|
9
13
|
layouts?: RouteLayoutHandler[];
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { assert, expect, test } from "vitest";
|
|
2
|
+
import Router from "../src/site/Router";
|
|
3
|
+
import { HOOK_SERVER_ROUTE, LAYOUT_ROUTE, PAGE_ROUTE } from "../src/types/RouteType";
|
|
4
|
+
|
|
5
|
+
test("router method sorting", () => {
|
|
6
|
+
let r = new Router();
|
|
7
|
+
r.addPage("/posts/[id]", PAGE_ROUTE, async () => {});
|
|
8
|
+
r.addPage("/posts/drafts/[id]", PAGE_ROUTE, async () => {});
|
|
9
|
+
expect(r.routes.map((r) => r.path).join(",")).toBe("/posts/drafts/[id],/posts/[id]");
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test("router matching", () => {
|
|
13
|
+
let r = new Router();
|
|
14
|
+
r.addPage("/posts", PAGE_ROUTE, async () => {});
|
|
15
|
+
const match = r.match("/posts", new URLSearchParams());
|
|
16
|
+
assert(match, "no match");
|
|
17
|
+
expect(match.params).toBeUndefined();
|
|
18
|
+
assert(match.handler, "no handler");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("router matching with param", () => {
|
|
22
|
+
let r = new Router();
|
|
23
|
+
r.addPage("/posts/[id]", PAGE_ROUTE, async () => {});
|
|
24
|
+
r.addPage("/posts/drafts/[id]", PAGE_ROUTE, async () => {});
|
|
25
|
+
const match = r.match("/posts/5", new URLSearchParams());
|
|
26
|
+
assert(match, "no match");
|
|
27
|
+
assert(match.params, "no params");
|
|
28
|
+
expect(match.params["id"]).toBe("5");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("router matching not found", () => {
|
|
32
|
+
let r = new Router();
|
|
33
|
+
r.addPage("/posts/[id]", PAGE_ROUTE, async () => {});
|
|
34
|
+
r.addPage("/posts/drafts/[id]", PAGE_ROUTE, async () => {});
|
|
35
|
+
const match = r.match("/postss/5", new URLSearchParams());
|
|
36
|
+
expect(match).toBeUndefined();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("router matching layouts", () => {
|
|
40
|
+
let r = new Router();
|
|
41
|
+
r.addPage("/_layout", LAYOUT_ROUTE, async () => {});
|
|
42
|
+
r.addPage("/posts/_layout", LAYOUT_ROUTE, async () => {});
|
|
43
|
+
r.addPage("/posts/[id]", PAGE_ROUTE, async () => {});
|
|
44
|
+
r.addPage("/posts/drafts/[id]", PAGE_ROUTE, async () => {});
|
|
45
|
+
const match = r.match("/posts/5", new URLSearchParams());
|
|
46
|
+
assert(match, "no match");
|
|
47
|
+
assert(match.handler.layouts, "no layouts");
|
|
48
|
+
expect(match.handler.layouts.length).toBe(2);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("router matching hook", () => {
|
|
52
|
+
let r = new Router();
|
|
53
|
+
r.addPage("/_hook/~server", HOOK_SERVER_ROUTE, async () => {});
|
|
54
|
+
r.addPage("/posts/[id]", PAGE_ROUTE, async () => {});
|
|
55
|
+
const match = r.match("/posts/5", new URLSearchParams());
|
|
56
|
+
assert(match, "no match");
|
|
57
|
+
assert(match.handler.serverHook, "no hook");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("router matching base folder", async () => {
|
|
61
|
+
let r = new Router();
|
|
62
|
+
r.addPage("/_hook/~server", HOOK_SERVER_ROUTE, async () => {});
|
|
63
|
+
r.addPage("/_layout", LAYOUT_ROUTE, async () => {});
|
|
64
|
+
r.addPage("/posts/_layout", LAYOUT_ROUTE, async () => {});
|
|
65
|
+
r.addPage("/posts/[id]", PAGE_ROUTE, async () => {});
|
|
66
|
+
r.addPage("/api/posts/[id]", PAGE_ROUTE, async () => {}, "/api");
|
|
67
|
+
r.addPage("/api/_hook/~server", HOOK_SERVER_ROUTE, async () => "api hook", "/api");
|
|
68
|
+
const match = r.match("/api/posts/5", new URLSearchParams());
|
|
69
|
+
assert(match, "no match");
|
|
70
|
+
assert(match.params, "no params");
|
|
71
|
+
expect(match.params["id"]).toBe("5");
|
|
72
|
+
|
|
73
|
+
expect(match.handler.path).toBe("/api/posts/[id]");
|
|
74
|
+
assert(match.handler.serverHook, "no hook");
|
|
75
|
+
expect(await match.handler.serverHook()).toBe("api hook");
|
|
76
|
+
|
|
77
|
+
expect(match.handler.layouts).toBeUndefined();
|
|
78
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { expect, test } from "vitest";
|
|
2
|
+
import Site from "../src/site/Site";
|
|
3
|
+
|
|
4
|
+
test("site routes", async () => {
|
|
5
|
+
let site = new Site();
|
|
6
|
+
await site.addRouteFolder("./test/routes");
|
|
7
|
+
expect(site.routes.map((r) => r.path).join("\n")).toBe(
|
|
8
|
+
`
|
|
9
|
+
/
|
|
10
|
+
/_error
|
|
11
|
+
/_hook/~server
|
|
12
|
+
/_layout
|
|
13
|
+
/_layout/~server
|
|
14
|
+
/~server
|
|
15
|
+
/posts
|
|
16
|
+
/posts/[id]
|
|
17
|
+
`.trim(),
|
|
18
|
+
);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("site routes with base folder", async () => {
|
|
22
|
+
let site = new Site();
|
|
23
|
+
await site.addRouteFolder("./test/routes");
|
|
24
|
+
await site.addRouteFolder("./test/api", "api");
|
|
25
|
+
expect(site.routes.map((r) => r.path).join("\n")).toBe(
|
|
26
|
+
`
|
|
27
|
+
/
|
|
28
|
+
/_error
|
|
29
|
+
/_hook/~server
|
|
30
|
+
/_layout
|
|
31
|
+
/_layout/~server
|
|
32
|
+
/~server
|
|
33
|
+
/api/_hook/~server
|
|
34
|
+
/api/posts/~server
|
|
35
|
+
/api/posts/[id]/~server
|
|
36
|
+
/posts
|
|
37
|
+
/posts/[id]
|
|
38
|
+
`.trim(),
|
|
39
|
+
);
|
|
40
|
+
});
|