@torpor/build 0.4.14 → 1.0.0
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/README.md +20 -2
- package/dist/Jsonify-Dg6gChd3.d.mts +10 -0
- package/dist/Jsonify-Dg6gChd3.d.mts.map +1 -0
- package/dist/ParseRouteParams-DBmqlmLh.d.mts +42 -0
- package/dist/ParseRouteParams-DBmqlmLh.d.mts.map +1 -0
- package/dist/{RouteType-B6BMOyYw.mjs → Router-C4ADUfhv.mjs} +30 -42
- package/dist/Router-C4ADUfhv.mjs.map +1 -0
- package/dist/{Server-CF5wDJp6.d.mts → Server-C_IKav_e.d.mts} +44 -16
- package/dist/Server-C_IKav_e.d.mts.map +1 -0
- package/dist/{ServerEvent-CxPwA4lH.mjs → ServerEvent-Dljn5i7A.mjs} +37 -11
- package/dist/ServerEvent-Dljn5i7A.mjs.map +1 -0
- package/dist/Site-CMa153FA.d.mts +565 -0
- package/dist/Site-CMa153FA.d.mts.map +1 -0
- package/dist/StandardSchema-D28L8_zZ.d.mts +100 -0
- package/dist/StandardSchema-D28L8_zZ.d.mts.map +1 -0
- package/dist/TypedResponse-6lznqN4U.d.mts +22 -0
- package/dist/TypedResponse-6lznqN4U.d.mts.map +1 -0
- package/dist/{_page-BQs4WvCO.mjs → _page-4cQKs_Dn.mjs} +3 -5
- package/dist/_page-4cQKs_Dn.mjs.map +1 -0
- package/dist/bin/index.d.mts +1 -1
- package/dist/bin/index.js +15 -11
- package/dist/bin/index.js.map +1 -1
- package/dist/flattenHeaders-C_YYLdOq.mjs +114 -0
- package/dist/flattenHeaders-C_YYLdOq.mjs.map +1 -0
- package/dist/form.d.mts +35 -0
- package/dist/form.d.mts.map +1 -0
- package/dist/form.mjs +48 -0
- package/dist/form.mjs.map +1 -0
- package/dist/index.d.mts +72 -156
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +158 -26
- package/dist/index.mjs.map +1 -1
- package/dist/nav.d.mts +58 -5
- package/dist/nav.d.mts.map +1 -1
- package/dist/nav.mjs +116 -38
- package/dist/nav.mjs.map +1 -1
- package/dist/openapi.d.mts +144 -0
- package/dist/openapi.d.mts.map +1 -0
- package/dist/openapi.mjs +2 -0
- package/dist/pathTrie-D4Ax2Mu9.mjs +160 -0
- package/dist/pathTrie-D4Ax2Mu9.mjs.map +1 -0
- package/dist/plugin-D8Www11B.mjs +202 -0
- package/dist/plugin-D8Www11B.mjs.map +1 -0
- package/dist/{response-C5TtAsh1.mjs → response-Bg4w2S1q.mjs} +16 -39
- package/dist/response-Bg4w2S1q.mjs.map +1 -0
- package/dist/response.d.mts +309 -289
- package/dist/response.d.mts.map +1 -1
- package/dist/response.mjs +3 -4
- package/dist/run.d.mts +11 -4
- package/dist/run.d.mts.map +1 -1
- package/dist/run.mjs +2 -8
- package/dist/runOpenApi-BUi1RH1l.mjs +1381 -0
- package/dist/runOpenApi-BUi1RH1l.mjs.map +1 -0
- package/dist/schema.d.mts +32 -0
- package/dist/schema.d.mts.map +1 -0
- package/dist/schema.mjs +2 -0
- package/dist/server.d.mts +10 -8
- package/dist/server.d.mts.map +1 -1
- package/dist/server.mjs +132 -4
- package/dist/server.mjs.map +1 -0
- package/dist/state.d.mts.map +1 -1
- package/dist/state.mjs +2 -3
- package/dist/test.d.mts +8 -10
- package/dist/test.d.mts.map +1 -1
- package/dist/test.mjs +283 -79
- package/dist/test.mjs.map +1 -1
- package/dist/{seeOther-B4Yhu9iq.mjs → unprocessable-DAZbrDeR.mjs} +34 -13
- package/dist/unprocessable-DAZbrDeR.mjs.map +1 -0
- package/dist/validate-Ok4krlkX.mjs +40 -0
- package/dist/validate-Ok4krlkX.mjs.map +1 -0
- package/package.json +28 -17
- package/src/bin/index.ts +13 -2
- package/src/dev.ts +10 -0
- package/src/form/formDataToRecord.ts +23 -0
- package/src/form/readForm.ts +71 -0
- package/src/form.ts +4 -0
- package/src/index.ts +53 -1
- package/src/nav/api.test-d.ts +79 -0
- package/src/nav/api.ts +104 -0
- package/src/nav/formSubmit.ts +18 -4
- package/src/nav/navigate.ts +40 -15
- package/src/nav/route.ts +38 -0
- package/src/nav.ts +3 -1
- package/src/openapi/docsHtml.ts +25 -0
- package/src/openapi/document.ts +154 -0
- package/src/openapi/plugin.ts +78 -0
- package/src/openapi/types.ts +82 -0
- package/src/openapi.ts +14 -0
- package/src/response/TypedResponse.ts +17 -0
- package/src/response/badRequest.ts +14 -2
- package/src/response/created.ts +13 -2
- package/src/response/found.ts +2 -2
- package/src/response/movedPermanently.ts +2 -2
- package/src/response/notModified.ts +2 -2
- package/src/response/ok.ts +13 -4
- package/src/response/response.ts +4 -4
- package/src/response/unprocessable.ts +14 -2
- package/src/run/depCache.ts +175 -0
- package/src/run/devPlugin.ts +128 -0
- package/src/run/prepareTemplate.ts +6 -3
- package/src/run/run.ts +63 -39
- package/src/run/runBuild.ts +63 -12
- package/src/run/runDev.ts +146 -58
- package/src/run/runOpenApi.ts +52 -0
- package/src/run/runPreview.ts +17 -26
- package/src/run/staleTorpCopies.ts +100 -0
- package/src/run.ts +2 -1
- package/src/schema.ts +7 -0
- package/src/server/CookieHelper.ts +17 -7
- package/src/server/Server.ts +54 -30
- package/src/server/ServerEvent.ts +24 -1
- package/src/server/connect/connectMiddleware.ts +44 -40
- package/src/server/connect/flattenHeaders.ts +1 -23
- package/src/server/connect/requestToNodeMessage.ts +6 -3
- package/src/server/types/MiddlewareFunction.ts +18 -4
- package/src/site/Router.ts +52 -41
- package/src/site/Site.ts +184 -10
- package/src/site/checkLayoutSlots.ts +114 -0
- package/src/site/checkRoutes.ts +435 -0
- package/src/site/clientEntry.ts +22 -9
- package/src/site/layoutSlots.ts +53 -0
- package/src/site/manifest.ts +125 -6
- package/src/site/serverEntry.ts +341 -132
- package/src/state/$page.ts +2 -1
- package/src/state/$serverPage.ts +22 -0
- package/src/test/runTest.ts +306 -118
- package/src/types/Adapter.ts +12 -0
- package/src/types/Jsonify.ts +20 -0
- package/src/types/PageData.test-d.ts +136 -0
- package/src/types/PageData.ts +35 -0
- package/src/types/PageEndPoint.ts +25 -9
- package/src/types/PageForm.test-d.ts +78 -0
- package/src/types/PageForm.ts +29 -0
- package/src/types/PageLoadEvent.ts +16 -4
- package/src/types/PageLoadReturn.ts +13 -0
- package/src/types/PageProps.ts +14 -0
- package/src/types/PageServerAction.ts +8 -2
- package/src/types/PageServerEndPoint.test-d.ts +105 -0
- package/src/types/PageServerEndPoint.ts +99 -5
- package/src/types/PageServerLoad.ts +11 -3
- package/src/types/ParseRouteParams.test-d.ts +84 -0
- package/src/types/ParseRouteParams.ts +64 -0
- package/src/types/Route.ts +20 -1
- package/src/types/RouteHandler.ts +8 -1
- package/src/types/ServerEndPoint.test-d.ts +77 -0
- package/src/types/ServerEndPoint.ts +148 -16
- package/src/types/ServerHook.ts +10 -3
- package/src/types/ServerLoadEvent.ts +48 -3
- package/src/types/ServerRequest.ts +10 -2
- package/src/types/SitePlugin.ts +24 -0
- package/src/types/StandardSchema.ts +109 -0
- package/src/utils/pathToRegex.ts +4 -2
- package/src/utils/pathTrie.ts +182 -0
- package/src/utils/searchParamsToRecord.ts +18 -0
- package/src/utils/torporPackages.ts +150 -0
- package/src/utils/tsconfigAliases.ts +90 -0
- package/src/validation/ValidationError.ts +18 -0
- package/src/validation/endpoint.ts +83 -0
- package/src/validation/validate.ts +26 -0
- package/dist/RouteType-B6BMOyYw.mjs.map +0 -1
- package/dist/Server-CF5wDJp6.d.mts.map +0 -1
- package/dist/ServerEvent-CxPwA4lH.mjs.map +0 -1
- package/dist/Site-DgC6WWn1.d.mts +0 -78
- package/dist/Site-DgC6WWn1.d.mts.map +0 -1
- package/dist/_page-BQs4WvCO.mjs.map +0 -1
- package/dist/connectMiddleware-D7nehjj3.mjs +0 -252
- package/dist/connectMiddleware-D7nehjj3.mjs.map +0 -1
- package/dist/pathToRegex-TUAMc3zS.mjs +0 -11
- package/dist/pathToRegex-TUAMc3zS.mjs.map +0 -1
- package/dist/response-C5TtAsh1.mjs.map +0 -1
- package/dist/run-BPFtSYA3.mjs +0 -332
- package/dist/run-BPFtSYA3.mjs.map +0 -1
- package/dist/seeOther-B4Yhu9iq.mjs.map +0 -1
- package/src/server/Routerx.ts +0 -72
- package/src/server/connect/bufferToArrayBuffer.ts +0 -8
- package/src/server/connect/readableToBuffer.ts +0 -16
package/dist/index.mjs
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
|
-
import "./
|
|
2
|
-
import { a as LAYOUT_SERVER_ROUTE, c as SERVER_ROUTE, i as LAYOUT_ROUTE, l as Router, n as HOOK_ROUTE, o as PAGE_ROUTE, r as HOOK_SERVER_ROUTE, s as PAGE_SERVER_ROUTE, t as ERROR_ROUTE } from "./RouteType-B6BMOyYw.mjs";
|
|
1
|
+
import { t as Router } from "./Router-C4ADUfhv.mjs";
|
|
3
2
|
import { promises } from "node:fs";
|
|
4
3
|
import fpath from "node:path";
|
|
5
|
-
import tsconfigPaths from "vite-tsconfig-paths";
|
|
6
|
-
|
|
7
4
|
//#region src/site/defaultAdapter.ts
|
|
8
5
|
const defaultAdapter = { serve: () => {
|
|
9
6
|
console.log("An adapter needs to be set in site.config");
|
|
10
7
|
} };
|
|
11
|
-
var defaultAdapter_default = defaultAdapter;
|
|
12
|
-
|
|
13
8
|
//#endregion
|
|
14
9
|
//#region src/site/Site.ts
|
|
15
10
|
/**
|
|
@@ -19,8 +14,18 @@ var defaultAdapter_default = defaultAdapter;
|
|
|
19
14
|
var Site = class {
|
|
20
15
|
root;
|
|
21
16
|
routes = [];
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Torpor site plugins, run when the site config is loaded (for dev and
|
|
19
|
+
* build) and when the server starts up. Use these to register extra
|
|
20
|
+
* routes (e.g. `openApi()` from `@torpor/build/openapi`) or add other
|
|
21
|
+
* framework-level functionality
|
|
22
|
+
*/
|
|
23
|
+
plugins = [];
|
|
24
|
+
/**
|
|
25
|
+
* Vite plugins to add to the client and server builds
|
|
26
|
+
*/
|
|
27
|
+
vitePlugins = [];
|
|
28
|
+
adapter = defaultAdapter;
|
|
24
29
|
/**
|
|
25
30
|
* Extra inputs, as absolute file paths, that will be compiled with the
|
|
26
31
|
* output. What you do with them after that is up to you. File paths ending
|
|
@@ -30,10 +35,36 @@ var Site = class {
|
|
|
30
35
|
/**
|
|
31
36
|
* Vite config options to merge with the standard Torpor build options.
|
|
32
37
|
* `plugins` and `build.rollupOptions.input` will be overridden, so you
|
|
33
|
-
* should set `Site.
|
|
38
|
+
* should set `Site.vitePlugins` and `Site.inputs` instead. Other options may be
|
|
34
39
|
* overridden or incompatible, so experimentation may be required
|
|
35
40
|
*/
|
|
36
41
|
viteConfig;
|
|
42
|
+
/**
|
|
43
|
+
* The path to the site.config file. Set automatically by the CLI; used by
|
|
44
|
+
* the manifest plugin to import inline endpoints for server builds.
|
|
45
|
+
*/
|
|
46
|
+
configFile;
|
|
47
|
+
/**
|
|
48
|
+
* A state map for plugins to store their configuration and state on the
|
|
49
|
+
* site, so that framework internals (e.g. the manifest plugin) and other
|
|
50
|
+
* plugins can read them back:
|
|
51
|
+
*
|
|
52
|
+
* ```ts
|
|
53
|
+
* const myKey = Symbol.for("my-plugin");
|
|
54
|
+
* site.pluginState.set(myKey, options);
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* Prefer `Symbol.for("...")` keys (or plain strings) over `Symbol()`,
|
|
58
|
+
* because the user's config file and framework internals can be loaded as
|
|
59
|
+
* separate module instances -- registry symbols are shared across
|
|
60
|
+
* instances, `Symbol()` ones are not.
|
|
61
|
+
*/
|
|
62
|
+
pluginState = /* @__PURE__ */ new Map();
|
|
63
|
+
/**
|
|
64
|
+
* Inline endpoints keyed by `"path:type"`. Populated by `addRoute` when
|
|
65
|
+
* the user passes an inline endpoint object instead of a file path.
|
|
66
|
+
*/
|
|
67
|
+
inlineEndPoints = {};
|
|
37
68
|
constructor() {
|
|
38
69
|
this.root = process.cwd();
|
|
39
70
|
}
|
|
@@ -88,26 +119,127 @@ var Site = class {
|
|
|
88
119
|
}
|
|
89
120
|
#routeType(file) {
|
|
90
121
|
let routePath = file.replace(/^\//, "").replace(/(\.ts|\.js)$/, "");
|
|
91
|
-
if (routePath.endsWith("+page")) return
|
|
92
|
-
else if (routePath.endsWith("+page.server")) return
|
|
93
|
-
else if (routePath.endsWith("+server")) return
|
|
94
|
-
else if (routePath.endsWith("_layout")) return
|
|
95
|
-
else if (routePath.endsWith("_layout.server")) return
|
|
96
|
-
else if (routePath.endsWith("_hook")) return
|
|
97
|
-
else if (routePath.endsWith("_hook.server")) return
|
|
98
|
-
else if (routePath.endsWith("_error")) return
|
|
122
|
+
if (routePath.endsWith("+page")) return 0;
|
|
123
|
+
else if (routePath.endsWith("+page.server")) return 1;
|
|
124
|
+
else if (routePath.endsWith("+server")) return 3;
|
|
125
|
+
else if (routePath.endsWith("_layout")) return 4;
|
|
126
|
+
else if (routePath.endsWith("_layout.server")) return 5;
|
|
127
|
+
else if (routePath.endsWith("_hook")) return 6;
|
|
128
|
+
else if (routePath.endsWith("_hook.server")) return 7;
|
|
129
|
+
else if (routePath.endsWith("_error")) return 8;
|
|
99
130
|
return -1;
|
|
100
131
|
}
|
|
101
132
|
#routeTypeFromString(type) {
|
|
102
133
|
switch (type) {
|
|
103
|
-
case "+page": return
|
|
104
|
-
case "+page.server": return
|
|
105
|
-
case "+server": return
|
|
106
|
-
case "_layout": return
|
|
107
|
-
case "_layout.server": return
|
|
108
|
-
case "_hook": return
|
|
109
|
-
case "_hook.server": return
|
|
110
|
-
case "_error": return
|
|
134
|
+
case "+page": return 0;
|
|
135
|
+
case "+page.server": return 1;
|
|
136
|
+
case "+server": return 3;
|
|
137
|
+
case "_layout": return 4;
|
|
138
|
+
case "_layout.server": return 5;
|
|
139
|
+
case "_hook": return 6;
|
|
140
|
+
case "_hook.server": return 7;
|
|
141
|
+
case "_error": return 8;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Adds a route in code, without needing the standard file naming
|
|
146
|
+
* conventions (+page.ts, +page.server.ts, etc). This is useful for small
|
|
147
|
+
* sites with one or two pages.
|
|
148
|
+
*
|
|
149
|
+
* Any combination of options may be provided; each maps to the
|
|
150
|
+
* corresponding file-route type:
|
|
151
|
+
*
|
|
152
|
+
* - `page` — a `+page` (`.torp` component or `.ts`/`.js` `PageEndPoint`)
|
|
153
|
+
* - `pageServer` — a `+page.server` (`PageServerEndPoint`, file or inline)
|
|
154
|
+
* - `server` — a `+server` HTTP endpoint (`ServerEndPoint`, file or inline)
|
|
155
|
+
* - `layout` — a `_layout` (`.torp` component or `.ts`/`.js` `PageEndPoint`)
|
|
156
|
+
* - `layoutServer` — a `_layout.server` (`PageServerEndPoint`, file or inline)
|
|
157
|
+
* - `hookServer` — a `_hook.server` (`ServerHook`, file or inline)
|
|
158
|
+
* - `error` — an `_error` page (`.torp` component or `.ts`/`.js` `PageEndPoint`)
|
|
159
|
+
*
|
|
160
|
+
* Options that need a component (`page`, `layout`, `error`) must be file
|
|
161
|
+
* paths so the component is included in the client bundle. Server-only
|
|
162
|
+
* options (`pageServer`, `server`, `layoutServer`, `hookServer`) accept
|
|
163
|
+
* either a file path or an inline object; inline server code is kept out
|
|
164
|
+
* of the client bundle automatically.
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* // A page with an inline server action
|
|
168
|
+
* site.addRoute("/", {
|
|
169
|
+
* page: "./src/Counter.torp",
|
|
170
|
+
* pageServer: { actions: { set: async ({ request }) => ok() } },
|
|
171
|
+
* });
|
|
172
|
+
* @example
|
|
173
|
+
* // A JSON API endpoint
|
|
174
|
+
* site.addRoute("/api/time", {
|
|
175
|
+
* server: { get: async () => ok({ time: Date.now() }) },
|
|
176
|
+
* });
|
|
177
|
+
* @example
|
|
178
|
+
* // A root layout, server hook, and error page
|
|
179
|
+
* site.addRoute("/", {
|
|
180
|
+
* layout: "./src/Layout.torp",
|
|
181
|
+
* hookServer: { enter: async (event) => {} },
|
|
182
|
+
* error: "./src/ErrorPage.torp",
|
|
183
|
+
* });
|
|
184
|
+
*
|
|
185
|
+
* @param path The route path, e.g. `/` or `/about`
|
|
186
|
+
* @param options File paths and/or inline endpoints for the route
|
|
187
|
+
* @param subFolder An optional subFolder for grouping routes
|
|
188
|
+
*/
|
|
189
|
+
addRoute(path, options, subFolder) {
|
|
190
|
+
let normalizedSubFolder = subFolder;
|
|
191
|
+
if (normalizedSubFolder !== void 0 && !normalizedSubFolder.startsWith("/")) normalizedSubFolder = "/" + normalizedSubFolder;
|
|
192
|
+
let base = path.replace(/\/$/, "");
|
|
193
|
+
if (options.page !== void 0) this.#pushRoute(path, 0, options.page, normalizedSubFolder);
|
|
194
|
+
if (options.pageServer !== void 0) this.#pushRoute(`${base}/~server`, 1, options.pageServer, normalizedSubFolder);
|
|
195
|
+
if (options.server !== void 0) this.#pushRoute(path, 3, options.server, normalizedSubFolder);
|
|
196
|
+
if (options.layout !== void 0) this.#pushRoute(`${base}/_layout`, 4, options.layout, normalizedSubFolder);
|
|
197
|
+
if (options.layoutServer !== void 0) this.#pushRoute(`${base}/_layout/~server`, 5, options.layoutServer, normalizedSubFolder);
|
|
198
|
+
if (options.hookServer !== void 0) this.#pushRoute(`${base}/_hook/~server`, 7, options.hookServer, normalizedSubFolder);
|
|
199
|
+
if (options.error !== void 0) this.#pushRoute(`${base}/_error`, 8, options.error, normalizedSubFolder);
|
|
200
|
+
this.#sortRoutes();
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Pushes a single route entry, handling both file-path and inline-endpoint
|
|
204
|
+
* targets. File paths are resolved relative to the site root; inline
|
|
205
|
+
* endpoints are also stored in `inlineEndPoints` keyed by `"path:type"`
|
|
206
|
+
* so the manifest plugin can resolve them at build time.
|
|
207
|
+
*
|
|
208
|
+
* If a route with the same path and type was already added, it is
|
|
209
|
+
* replaced, so that running a plugin twice (config load and server
|
|
210
|
+
* startup) doesn't add duplicate routes.
|
|
211
|
+
*/
|
|
212
|
+
#pushRoute(routePath, type, target, subFolder) {
|
|
213
|
+
const index = this.routes.findIndex((r) => r.path === routePath && r.type === type);
|
|
214
|
+
if (typeof target === "string") {
|
|
215
|
+
let file = fpath.relative(this.root, fpath.resolve(this.root, target));
|
|
216
|
+
if (index >= 0) this.routes[index] = {
|
|
217
|
+
path: routePath,
|
|
218
|
+
file,
|
|
219
|
+
type,
|
|
220
|
+
subFolder
|
|
221
|
+
};
|
|
222
|
+
else this.routes.push({
|
|
223
|
+
path: routePath,
|
|
224
|
+
file,
|
|
225
|
+
type,
|
|
226
|
+
subFolder
|
|
227
|
+
});
|
|
228
|
+
} else {
|
|
229
|
+
let key = `${routePath}:${type}`;
|
|
230
|
+
this.inlineEndPoints[key] = target;
|
|
231
|
+
if (index >= 0) this.routes[index] = {
|
|
232
|
+
path: routePath,
|
|
233
|
+
endPoint: target,
|
|
234
|
+
type,
|
|
235
|
+
subFolder
|
|
236
|
+
};
|
|
237
|
+
else this.routes.push({
|
|
238
|
+
path: routePath,
|
|
239
|
+
endPoint: target,
|
|
240
|
+
type,
|
|
241
|
+
subFolder
|
|
242
|
+
});
|
|
111
243
|
}
|
|
112
244
|
}
|
|
113
245
|
#sortRoutes() {
|
|
@@ -119,7 +251,7 @@ var Site = class {
|
|
|
119
251
|
});
|
|
120
252
|
}
|
|
121
253
|
};
|
|
122
|
-
|
|
123
254
|
//#endregion
|
|
124
255
|
export { Router, Site };
|
|
256
|
+
|
|
125
257
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["defaultAdapter: Adapter","defaultAdapter","fs","#routePath","#routeType","#sortRoutes","#routeTypeFromString"],"sources":["../src/site/defaultAdapter.ts","../src/site/Site.ts"],"sourcesContent":["import type Adapter from \"../types/Adapter\";\n\n// TODO: This should be an adapter-auto package that conditionally loads other packages...\n\nconst defaultAdapter: Adapter = {\n\tserve: () => {\n\t\tconsole.log(\"An adapter needs to be set in site.config\");\n\t},\n};\n\nexport default defaultAdapter;\n","import { promises as fs } from \"node:fs\";\nimport fpath from \"node:path\";\nimport { type Plugin, type UserConfig } from \"vite\";\nimport tsconfigPaths from \"vite-tsconfig-paths\";\nimport type Adapter from \"../types/Adapter\";\nimport type Route from \"../types/Route\";\nimport {\n\tERROR_ROUTE,\n\tHOOK_ROUTE,\n\tHOOK_SERVER_ROUTE,\n\tLAYOUT_ROUTE,\n\tLAYOUT_SERVER_ROUTE,\n\tPAGE_ROUTE,\n\tPAGE_SERVER_ROUTE,\n\tRouteType,\n\tSERVER_ROUTE,\n} from \"../types/RouteType\";\nimport defaultAdapter from \"./defaultAdapter\";\n\n// 1. The user sets this up\n// 2. It is passed into serverEntry and clientEntry through a Vite plugin\n// (defined in manifest.ts)\n// 3. In those files, it is used to build a Router\n\n/**\n * The Site class contains the information necessary to setup a @torpor/build\n * site, including routes, plugins, and options.\n */\nexport default class Site {\n\troot: string;\n\troutes: Route[] = [];\n\t// Is default plugins a bad idea?\n\t// HACK: Set loose because otherwise it uses a list of allowed extensions\n\t// TODO: Should probably just do this ourselves and let the user pass in\n\t// tsconfigPaths themselves if they want to do something funky\n\tplugins: Plugin[] = [tsconfigPaths({ loose: true })];\n\t// Is default adapter a bad idea?\n\tadapter: Adapter = defaultAdapter;\n\t/**\n\t * Extra inputs, as absolute file paths, that will be compiled with the\n\t * output. What you do with them after that is up to you. File paths ending\n\t * with server.ts/js will only be compiled with the server output\n\t */\n\tinputs: string[] = [];\n\t/**\n\t * Vite config options to merge with the standard Torpor build options.\n\t * `plugins` and `build.rollupOptions.input` will be overridden, so you\n\t * should set `Site.plugins` and `Site.inputs` instead. Other options may be\n\t * overridden or incompatible, so experimentation may be required\n\t */\n\tviteConfig?: UserConfig;\n\n\tconstructor() {\n\t\tthis.root = process.cwd();\n\t}\n\n\t/**\n\t * Adds a folder of routes, based on the names of the files in the folder\n\t * @param folder The folder containing the routes\n\t * @param subFolder If a subFolder is set, all routes in the folder will be\n\t * placed under the subFolder, and only hooks, layouts etc will be used if\n\t * they have the same subFolder. This can be used to create e.g. an `api`\n\t * set of routes with their own hook for authorization from various clients\n\t */\n\tasync addRouteFolder(folder: string, subFolder?: string): Promise<void> {\n\t\tconst routeFolder = fpath.join(this.root, folder);\n\t\tconst routeFiles = await fs.readdir(routeFolder, { recursive: true });\n\t\tif (subFolder !== undefined && !subFolder.startsWith(\"/\")) {\n\t\t\tsubFolder = \"/\" + subFolder;\n\t\t}\n\t\tfor (let file of routeFiles) {\n\t\t\t// The file must start with `+` or `_` and end with `.js` or `.ts`\n\t\t\tif (/^(\\+|_).+(\\.js|\\.ts)$/.test(fpath.basename(file))) {\n\t\t\t\tlet routePath = this.#routePath(file);\n\t\t\t\tif (subFolder !== undefined) {\n\t\t\t\t\troutePath = subFolder + routePath;\n\t\t\t\t}\n\t\t\t\tlet type = this.#routeType(file);\n\t\t\t\tfile = fpath.relative(this.root, fpath.resolve(routeFolder, file));\n\t\t\t\tthis.routes.push({\n\t\t\t\t\tpath: routePath,\n\t\t\t\t\tfile,\n\t\t\t\t\ttype,\n\t\t\t\t\tsubFolder: subFolder,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tthis.#sortRoutes();\n\t}\n\n\t/**\n\t * Adds a file route, which can have a standard name (e.g. +page.ts) or any\n\t * name if you pass the type\n\t * @param path The route path\n\t * @param file The file\n\t * @param type The file type (if the file name is non-standard)\n\t */\n\taddRouteFile(\n\t\tpath: string,\n\t\tfile: string,\n\t\ttype?:\n\t\t\t| \"+page\"\n\t\t\t| \"+page.server\"\n\t\t\t| \"+server\"\n\t\t\t| \"_layout\"\n\t\t\t| \"_layout.server\"\n\t\t\t| \"_hook\"\n\t\t\t| \"_hook.server\"\n\t\t\t| \"_error\",\n\t\tsubFolder?: string,\n\t): void {\n\t\tlet routePath = this.#routePath(fpath.join(path, type ?? fpath.basename(file)));\n\t\tlet routeType = type ? this.#routeTypeFromString(type) : this.#routeType(file);\n\t\tif (subFolder !== undefined && !subFolder.startsWith(\"/\")) {\n\t\t\tsubFolder = \"/\" + subFolder;\n\t\t}\n\t\tthis.routes.push({\n\t\t\tpath: routePath,\n\t\t\tfile,\n\t\t\ttype: routeType,\n\t\t\tsubFolder: subFolder,\n\t\t});\n\t\tthis.#sortRoutes();\n\t}\n\n\t#routePath(file: string): string {\n\t\tlet routePath = file\n\t\t\t.replace(/^\\//, \"\")\n\t\t\t.replace(/(\\.ts|\\.js)$/, \"\")\n\t\t\t.replace(/_hook.server$/, \"_hook/~server\")\n\t\t\t.replace(/_layout.server$/, \"_layout/~server\")\n\t\t\t.replace(/\\+page.server$/, \"~server\")\n\t\t\t.replace(/\\+server$/, \"\")\n\t\t\t.replace(/\\+page$/, \"\")\n\t\t\t.replace(/\\/$/, \"\");\n\t\treturn routePath.length > 0 ? `/${routePath}` : \"/\";\n\t}\n\n\t#routeType(file: string): RouteType {\n\t\tlet routePath = file.replace(/^\\//, \"\").replace(/(\\.ts|\\.js)$/, \"\");\n\t\tif (routePath.endsWith(\"+page\")) {\n\t\t\treturn PAGE_ROUTE;\n\t\t} else if (routePath.endsWith(\"+page.server\")) {\n\t\t\treturn PAGE_SERVER_ROUTE;\n\t\t} else if (routePath.endsWith(\"+server\")) {\n\t\t\treturn SERVER_ROUTE;\n\t\t} else if (routePath.endsWith(\"_layout\")) {\n\t\t\treturn LAYOUT_ROUTE;\n\t\t} else if (routePath.endsWith(\"_layout.server\")) {\n\t\t\treturn LAYOUT_SERVER_ROUTE;\n\t\t} else if (routePath.endsWith(\"_hook\")) {\n\t\t\treturn HOOK_ROUTE;\n\t\t} else if (routePath.endsWith(\"_hook.server\")) {\n\t\t\treturn HOOK_SERVER_ROUTE;\n\t\t} else if (routePath.endsWith(\"_error\")) {\n\t\t\treturn ERROR_ROUTE;\n\t\t}\n\t\treturn -1;\n\t}\n\n\t#routeTypeFromString(\n\t\ttype:\n\t\t\t| \"+page\"\n\t\t\t| \"+page.server\"\n\t\t\t| \"+server\"\n\t\t\t| \"_layout\"\n\t\t\t| \"_layout.server\"\n\t\t\t| \"_hook\"\n\t\t\t| \"_hook.server\"\n\t\t\t| \"_error\",\n\t) {\n\t\tswitch (type) {\n\t\t\tcase \"+page\":\n\t\t\t\treturn PAGE_ROUTE;\n\t\t\tcase \"+page.server\":\n\t\t\t\treturn PAGE_SERVER_ROUTE;\n\t\t\tcase \"+server\":\n\t\t\t\treturn SERVER_ROUTE;\n\t\t\tcase \"_layout\":\n\t\t\t\treturn LAYOUT_ROUTE;\n\t\t\tcase \"_layout.server\":\n\t\t\t\treturn LAYOUT_SERVER_ROUTE;\n\t\t\tcase \"_hook\":\n\t\t\t\treturn HOOK_ROUTE;\n\t\t\tcase \"_hook.server\":\n\t\t\t\treturn HOOK_SERVER_ROUTE;\n\t\t\tcase \"_error\":\n\t\t\t\treturn ERROR_ROUTE;\n\t\t}\n\t}\n\n\t// TODO: You could make your site in a single file, like e.g. Hono...\n\t//addPage(path: string, endPoint: PageEndPoint, serverEndPoint?: PageServerEndPoint) {}\n\t//addEndPoint(path: string, endPoint: ServerEndPoint) {}\n\n\t#sortRoutes() {\n\t\tthis.routes = this.routes.sort((a, b) => {\n\t\t\t// Sort [param]s after paths\n\t\t\t// There might be a quicker/easier way to do this\n\t\t\tfor (let i = 0; i < Math.min(a.path.length, b.path.length); i++) {\n\t\t\t\tif (a.path[i] === \"[\" && b.path[i] !== \"[\") {\n\t\t\t\t\treturn 1;\n\t\t\t\t} else if (b.path[i] === \"[\" && a.path[i] !== \"[\") {\n\t\t\t\t\treturn -1;\n\t\t\t\t} else if (a.path[i] === b.path[i]) {\n\t\t\t\t\t// Keep going...\n\t\t\t\t} else {\n\t\t\t\t\treturn a.path[i].localeCompare(b.path[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn a.path.length - b.path.length;\n\t\t});\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["fs","#routePath","#routeType","#sortRoutes","#routeTypeFromString","#pushRoute"],"sources":["../src/site/defaultAdapter.ts","../src/site/Site.ts"],"sourcesContent":["import type Adapter from \"../types/Adapter\";\n\n// TODO: This should be an adapter-auto package that conditionally loads other packages...\n\nconst defaultAdapter: Adapter = {\n\tserve: () => {\n\t\tconsole.log(\"An adapter needs to be set in site.config\");\n\t},\n};\n\nexport default defaultAdapter;\n","import { promises as fs } from \"node:fs\";\nimport fpath from \"node:path\";\nimport { type Plugin, type UserConfig } from \"vite\";\nimport type Adapter from \"../types/Adapter\";\nimport type SitePlugin from \"../types/SitePlugin\";\nimport type { InlineEndPoint } from \"../types/Route\";\nimport type PageServerEndPoint from \"../types/PageServerEndPoint\";\nimport type Route from \"../types/Route\";\nimport type ServerEndPoint from \"../types/ServerEndPoint\";\nimport type ServerHook from \"../types/ServerHook\";\nimport {\n\tERROR_ROUTE,\n\tHOOK_ROUTE,\n\tHOOK_SERVER_ROUTE,\n\tLAYOUT_ROUTE,\n\tLAYOUT_SERVER_ROUTE,\n\tPAGE_ROUTE,\n\tPAGE_SERVER_ROUTE,\n\tRouteType,\n\tSERVER_ROUTE,\n} from \"../types/RouteType\";\nimport defaultAdapter from \"./defaultAdapter\";\n\n// 1. The user sets this up\n// 2. It is passed into serverEntry and clientEntry through a Vite plugin\n// (defined in manifest.ts)\n// 3. In those files, it is used to build a Router\n\n/**\n * The Site class contains the information necessary to setup a @torpor/build\n * site, including routes, plugins, and options.\n */\nexport default class Site {\n\troot: string;\n\troutes: Route[] = [];\n\t/**\n\t * Torpor site plugins, run when the site config is loaded (for dev and\n\t * build) and when the server starts up. Use these to register extra\n\t * routes (e.g. `openApi()` from `@torpor/build/openapi`) or add other\n\t * framework-level functionality\n\t */\n\tplugins: SitePlugin[] = [];\n\t/**\n\t * Vite plugins to add to the client and server builds\n\t */\n\tvitePlugins: Plugin[] = [];\n\t// Is default adapter a bad idea?\n\tadapter: Adapter = defaultAdapter;\n\t/**\n\t * Extra inputs, as absolute file paths, that will be compiled with the\n\t * output. What you do with them after that is up to you. File paths ending\n\t * with server.ts/js will only be compiled with the server output\n\t */\n\tinputs: string[] = [];\n\t/**\n\t * Vite config options to merge with the standard Torpor build options.\n\t * `plugins` and `build.rollupOptions.input` will be overridden, so you\n\t * should set `Site.vitePlugins` and `Site.inputs` instead. Other options may be\n\t * overridden or incompatible, so experimentation may be required\n\t */\n\tviteConfig?: UserConfig;\n\t/**\n\t * The path to the site.config file. Set automatically by the CLI; used by\n\t * the manifest plugin to import inline endpoints for server builds.\n\t */\n\tconfigFile?: string;\n\t/**\n\t * A state map for plugins to store their configuration and state on the\n\t * site, so that framework internals (e.g. the manifest plugin) and other\n\t * plugins can read them back:\n\t *\n\t * ```ts\n\t * const myKey = Symbol.for(\"my-plugin\");\n\t * site.pluginState.set(myKey, options);\n\t * ```\n\t *\n\t * Prefer `Symbol.for(\"...\")` keys (or plain strings) over `Symbol()`,\n\t * because the user's config file and framework internals can be loaded as\n\t * separate module instances -- registry symbols are shared across\n\t * instances, `Symbol()` ones are not.\n\t */\n\tpluginState: Map<PropertyKey, unknown> = new Map();\n\t/**\n\t * Inline endpoints keyed by `\"path:type\"`. Populated by `addRoute` when\n\t * the user passes an inline endpoint object instead of a file path.\n\t */\n\tinlineEndPoints: Record<string, InlineEndPoint> = {};\n\n\tconstructor() {\n\t\tthis.root = process.cwd();\n\t}\n\n\t/**\n\t * Adds a folder of routes, based on the names of the files in the folder\n\t * @param folder The folder containing the routes\n\t * @param subFolder If a subFolder is set, all routes in the folder will be\n\t * placed under the subFolder, and only hooks, layouts etc will be used if\n\t * they have the same subFolder. This can be used to create e.g. an `api`\n\t * set of routes with their own hook for authorization from various clients\n\t */\n\tasync addRouteFolder(folder: string, subFolder?: string): Promise<void> {\n\t\tconst routeFolder = fpath.join(this.root, folder);\n\t\tconst routeFiles = await fs.readdir(routeFolder, { recursive: true });\n\t\tif (subFolder !== undefined && !subFolder.startsWith(\"/\")) {\n\t\t\tsubFolder = \"/\" + subFolder;\n\t\t}\n\t\tfor (let file of routeFiles) {\n\t\t\t// The file must start with `+` or `_` and end with `.js` or `.ts`\n\t\t\tif (/^(\\+|_).+(\\.js|\\.ts)$/.test(fpath.basename(file))) {\n\t\t\t\tlet routePath = this.#routePath(file);\n\t\t\t\tif (subFolder !== undefined) {\n\t\t\t\t\troutePath = subFolder + routePath;\n\t\t\t\t}\n\t\t\t\tlet type = this.#routeType(file);\n\t\t\t\tfile = fpath.relative(this.root, fpath.resolve(routeFolder, file));\n\t\t\t\tthis.routes.push({\n\t\t\t\t\tpath: routePath,\n\t\t\t\t\tfile,\n\t\t\t\t\ttype,\n\t\t\t\t\tsubFolder: subFolder,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tthis.#sortRoutes();\n\t}\n\n\t/**\n\t * Adds a file route, which can have a standard name (e.g. +page.ts) or any\n\t * name if you pass the type\n\t * @param path The route path\n\t * @param file The file\n\t * @param type The file type (if the file name is non-standard)\n\t */\n\taddRouteFile(\n\t\tpath: string,\n\t\tfile: string,\n\t\ttype?:\n\t\t\t| \"+page\"\n\t\t\t| \"+page.server\"\n\t\t\t| \"+server\"\n\t\t\t| \"_layout\"\n\t\t\t| \"_layout.server\"\n\t\t\t| \"_hook\"\n\t\t\t| \"_hook.server\"\n\t\t\t| \"_error\",\n\t\tsubFolder?: string,\n\t): void {\n\t\tlet routePath = this.#routePath(fpath.join(path, type ?? fpath.basename(file)));\n\t\tlet routeType = type ? this.#routeTypeFromString(type) : this.#routeType(file);\n\t\tif (subFolder !== undefined && !subFolder.startsWith(\"/\")) {\n\t\t\tsubFolder = \"/\" + subFolder;\n\t\t}\n\t\tthis.routes.push({\n\t\t\tpath: routePath,\n\t\t\tfile,\n\t\t\ttype: routeType,\n\t\t\tsubFolder: subFolder,\n\t\t});\n\t\tthis.#sortRoutes();\n\t}\n\n\t#routePath(file: string): string {\n\t\tlet routePath = file\n\t\t\t.replace(/^\\//, \"\")\n\t\t\t.replace(/(\\.ts|\\.js)$/, \"\")\n\t\t\t.replace(/_hook.server$/, \"_hook/~server\")\n\t\t\t.replace(/_layout.server$/, \"_layout/~server\")\n\t\t\t.replace(/\\+page.server$/, \"~server\")\n\t\t\t.replace(/\\+server$/, \"\")\n\t\t\t.replace(/\\+page$/, \"\")\n\t\t\t.replace(/\\/$/, \"\");\n\t\treturn routePath.length > 0 ? `/${routePath}` : \"/\";\n\t}\n\n\t#routeType(file: string): RouteType {\n\t\tlet routePath = file.replace(/^\\//, \"\").replace(/(\\.ts|\\.js)$/, \"\");\n\t\tif (routePath.endsWith(\"+page\")) {\n\t\t\treturn PAGE_ROUTE;\n\t\t} else if (routePath.endsWith(\"+page.server\")) {\n\t\t\treturn PAGE_SERVER_ROUTE;\n\t\t} else if (routePath.endsWith(\"+server\")) {\n\t\t\treturn SERVER_ROUTE;\n\t\t} else if (routePath.endsWith(\"_layout\")) {\n\t\t\treturn LAYOUT_ROUTE;\n\t\t} else if (routePath.endsWith(\"_layout.server\")) {\n\t\t\treturn LAYOUT_SERVER_ROUTE;\n\t\t} else if (routePath.endsWith(\"_hook\")) {\n\t\t\treturn HOOK_ROUTE;\n\t\t} else if (routePath.endsWith(\"_hook.server\")) {\n\t\t\treturn HOOK_SERVER_ROUTE;\n\t\t} else if (routePath.endsWith(\"_error\")) {\n\t\t\treturn ERROR_ROUTE;\n\t\t}\n\t\treturn -1;\n\t}\n\n\t#routeTypeFromString(\n\t\ttype:\n\t\t\t| \"+page\"\n\t\t\t| \"+page.server\"\n\t\t\t| \"+server\"\n\t\t\t| \"_layout\"\n\t\t\t| \"_layout.server\"\n\t\t\t| \"_hook\"\n\t\t\t| \"_hook.server\"\n\t\t\t| \"_error\",\n\t) {\n\t\tswitch (type) {\n\t\t\tcase \"+page\":\n\t\t\t\treturn PAGE_ROUTE;\n\t\t\tcase \"+page.server\":\n\t\t\t\treturn PAGE_SERVER_ROUTE;\n\t\t\tcase \"+server\":\n\t\t\t\treturn SERVER_ROUTE;\n\t\t\tcase \"_layout\":\n\t\t\t\treturn LAYOUT_ROUTE;\n\t\t\tcase \"_layout.server\":\n\t\t\t\treturn LAYOUT_SERVER_ROUTE;\n\t\t\tcase \"_hook\":\n\t\t\t\treturn HOOK_ROUTE;\n\t\t\tcase \"_hook.server\":\n\t\t\t\treturn HOOK_SERVER_ROUTE;\n\t\t\tcase \"_error\":\n\t\t\t\treturn ERROR_ROUTE;\n\t\t}\n\t}\n\n\t/**\n\t * Adds a route in code, without needing the standard file naming\n\t * conventions (+page.ts, +page.server.ts, etc). This is useful for small\n\t * sites with one or two pages.\n\t *\n\t * Any combination of options may be provided; each maps to the\n\t * corresponding file-route type:\n\t *\n\t * - `page` — a `+page` (`.torp` component or `.ts`/`.js` `PageEndPoint`)\n\t * - `pageServer` — a `+page.server` (`PageServerEndPoint`, file or inline)\n\t * - `server` — a `+server` HTTP endpoint (`ServerEndPoint`, file or inline)\n\t * - `layout` — a `_layout` (`.torp` component or `.ts`/`.js` `PageEndPoint`)\n\t * - `layoutServer` — a `_layout.server` (`PageServerEndPoint`, file or inline)\n\t * - `hookServer` — a `_hook.server` (`ServerHook`, file or inline)\n\t * - `error` — an `_error` page (`.torp` component or `.ts`/`.js` `PageEndPoint`)\n\t *\n\t * Options that need a component (`page`, `layout`, `error`) must be file\n\t * paths so the component is included in the client bundle. Server-only\n\t * options (`pageServer`, `server`, `layoutServer`, `hookServer`) accept\n\t * either a file path or an inline object; inline server code is kept out\n\t * of the client bundle automatically.\n\t *\n\t * @example\n\t * // A page with an inline server action\n\t * site.addRoute(\"/\", {\n\t * page: \"./src/Counter.torp\",\n\t * pageServer: { actions: { set: async ({ request }) => ok() } },\n\t * });\n\t * @example\n\t * // A JSON API endpoint\n\t * site.addRoute(\"/api/time\", {\n\t * server: { get: async () => ok({ time: Date.now() }) },\n\t * });\n\t * @example\n\t * // A root layout, server hook, and error page\n\t * site.addRoute(\"/\", {\n\t * layout: \"./src/Layout.torp\",\n\t * hookServer: { enter: async (event) => {} },\n\t * error: \"./src/ErrorPage.torp\",\n\t * });\n\t *\n\t * @param path The route path, e.g. `/` or `/about`\n\t * @param options File paths and/or inline endpoints for the route\n\t * @param subFolder An optional subFolder for grouping routes\n\t */\n\taddRoute(\n\t\tpath: string,\n\t\toptions: {\n\t\t\tpage?: string;\n\t\t\tpageServer?: string | PageServerEndPoint;\n\t\t\tserver?: string | ServerEndPoint;\n\t\t\tlayout?: string;\n\t\t\tlayoutServer?: string | PageServerEndPoint;\n\t\t\thookServer?: string | ServerHook;\n\t\t\terror?: string;\n\t\t},\n\t\tsubFolder?: string,\n\t): void {\n\t\tlet normalizedSubFolder = subFolder;\n\t\tif (normalizedSubFolder !== undefined && !normalizedSubFolder.startsWith(\"/\")) {\n\t\t\tnormalizedSubFolder = \"/\" + normalizedSubFolder;\n\t\t}\n\n\t\t// The base path with any trailing slash stripped, so that we can\n\t\t// append \"/_layout\", \"/~server\" etc. For the root \"/\" this is \"\".\n\t\tlet base = path.replace(/\\/$/, \"\");\n\n\t\tif (options.page !== undefined) {\n\t\t\tthis.#pushRoute(path, PAGE_ROUTE, options.page, normalizedSubFolder);\n\t\t}\n\t\tif (options.pageServer !== undefined) {\n\t\t\tthis.#pushRoute(\n\t\t\t\t`${base}/~server`,\n\t\t\t\tPAGE_SERVER_ROUTE,\n\t\t\t\toptions.pageServer,\n\t\t\t\tnormalizedSubFolder,\n\t\t\t);\n\t\t}\n\t\tif (options.server !== undefined) {\n\t\t\tthis.#pushRoute(path, SERVER_ROUTE, options.server, normalizedSubFolder);\n\t\t}\n\t\tif (options.layout !== undefined) {\n\t\t\tthis.#pushRoute(`${base}/_layout`, LAYOUT_ROUTE, options.layout, normalizedSubFolder);\n\t\t}\n\t\tif (options.layoutServer !== undefined) {\n\t\t\tthis.#pushRoute(\n\t\t\t\t`${base}/_layout/~server`,\n\t\t\t\tLAYOUT_SERVER_ROUTE,\n\t\t\t\toptions.layoutServer,\n\t\t\t\tnormalizedSubFolder,\n\t\t\t);\n\t\t}\n\t\tif (options.hookServer !== undefined) {\n\t\t\tthis.#pushRoute(\n\t\t\t\t`${base}/_hook/~server`,\n\t\t\t\tHOOK_SERVER_ROUTE,\n\t\t\t\toptions.hookServer,\n\t\t\t\tnormalizedSubFolder,\n\t\t\t);\n\t\t}\n\t\tif (options.error !== undefined) {\n\t\t\tthis.#pushRoute(`${base}/_error`, ERROR_ROUTE, options.error, normalizedSubFolder);\n\t\t}\n\n\t\tthis.#sortRoutes();\n\t}\n\n\t/**\n\t * Pushes a single route entry, handling both file-path and inline-endpoint\n\t * targets. File paths are resolved relative to the site root; inline\n\t * endpoints are also stored in `inlineEndPoints` keyed by `\"path:type\"`\n\t * so the manifest plugin can resolve them at build time.\n\t *\n\t * If a route with the same path and type was already added, it is\n\t * replaced, so that running a plugin twice (config load and server\n\t * startup) doesn't add duplicate routes.\n\t */\n\t#pushRoute(\n\t\troutePath: string,\n\t\ttype: RouteType,\n\t\ttarget: string | InlineEndPoint,\n\t\tsubFolder?: string,\n\t): void {\n\t\tconst index = this.routes.findIndex((r) => r.path === routePath && r.type === type);\n\t\tif (typeof target === \"string\") {\n\t\t\tlet file = fpath.relative(this.root, fpath.resolve(this.root, target));\n\t\t\tif (index >= 0) {\n\t\t\t\tthis.routes[index] = { path: routePath, file, type, subFolder };\n\t\t\t} else {\n\t\t\t\tthis.routes.push({ path: routePath, file, type, subFolder });\n\t\t\t}\n\t\t} else {\n\t\t\tlet key = `${routePath}:${type}`;\n\t\t\tthis.inlineEndPoints[key] = target;\n\t\t\tif (index >= 0) {\n\t\t\t\tthis.routes[index] = { path: routePath, endPoint: target, type, subFolder };\n\t\t\t} else {\n\t\t\t\tthis.routes.push({ path: routePath, endPoint: target, type, subFolder });\n\t\t\t}\n\t\t}\n\t}\n\n\t#sortRoutes() {\n\t\tthis.routes = this.routes.sort((a, b) => {\n\t\t\t// Sort [param]s after paths\n\t\t\t// There might be a quicker/easier way to do this\n\t\t\tfor (let i = 0; i < Math.min(a.path.length, b.path.length); i++) {\n\t\t\t\tif (a.path[i] === \"[\" && b.path[i] !== \"[\") {\n\t\t\t\t\treturn 1;\n\t\t\t\t} else if (b.path[i] === \"[\" && a.path[i] !== \"[\") {\n\t\t\t\t\treturn -1;\n\t\t\t\t} else if (a.path[i] === b.path[i]) {\n\t\t\t\t\t// Keep going...\n\t\t\t\t} else {\n\t\t\t\t\treturn a.path[i].localeCompare(b.path[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn a.path.length - b.path.length;\n\t\t});\n\t}\n}\n"],"mappings":"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"}
|
package/dist/nav.d.mts
CHANGED
|
@@ -1,14 +1,67 @@
|
|
|
1
|
+
import { n as ParseRouteParams, t as ExactRouteParams } from "./ParseRouteParams-DBmqlmLh.mjs";
|
|
2
|
+
import { t as TypedResponse } from "./TypedResponse-6lznqN4U.mjs";
|
|
3
|
+
//#region src/nav/api.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* The value a client call resolves to: the endpoint's typed JSON body when it
|
|
6
|
+
* returns a typed response, or the raw Response when the body isn't JSON.
|
|
7
|
+
* Untyped endpoints resolve to `unknown`.
|
|
8
|
+
*/
|
|
9
|
+
type ApiResult<Handler> = Handler extends ((event: any) => any) ? Awaited<ReturnType<Handler>> extends TypedResponse<infer Body> ? Body | Response : unknown : never;
|
|
10
|
+
/**
|
|
11
|
+
* The body a client call accepts: the endpoint event's annotated JSON body
|
|
12
|
+
* type (from `ServerLoadEvent<Route, Body>`), or a loose value when the
|
|
13
|
+
* handler doesn't annotate one.
|
|
14
|
+
*/
|
|
15
|
+
type ApiRequestBody<Handler> = Handler extends ((event: infer Event) => any) ? Event extends {
|
|
16
|
+
json: () => Promise<infer Body>;
|
|
17
|
+
} ? unknown extends Body ? BodyInit | object : Body : BodyInit | object : BodyInit | object;
|
|
18
|
+
/** The callable methods of an endpoint, one per handler it defines. */
|
|
19
|
+
type ApiMethods<Endpoint extends object> = { [Method in string & keyof Endpoint as NonNullable<Endpoint[Method]> extends ((event: any) => any) ? Method : never]: (body?: ApiRequestBody<NonNullable<Endpoint[Method]>>, init?: RequestInit) => Promise<ApiResult<NonNullable<Endpoint[Method]>>>; };
|
|
20
|
+
/**
|
|
21
|
+
* Creates typed client callers for a `+server` endpoint, e.g.
|
|
22
|
+
*
|
|
23
|
+
* ```ts
|
|
24
|
+
* import type endpoint from "@/routes/api/posts/[id]/+server";
|
|
25
|
+
* const post = makeApi<"/api/posts/[id]", typeof endpoint>("/api/posts/[id]");
|
|
26
|
+
* const data = await post.get(); // typed from the endpoint's responses
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* Method names match the endpoint's handlers (`del` for DELETE). Params are
|
|
30
|
+
* filled in and URI-encoded from the route path, and JSON responses are
|
|
31
|
+
* parsed; non-JSON responses resolve to the raw Response. Failed requests
|
|
32
|
+
* (non-2xx) throw.
|
|
33
|
+
*
|
|
34
|
+
* @param path The endpoint's route path
|
|
35
|
+
* @param params The route params, required if the path has dynamic segments
|
|
36
|
+
* @returns An object with one caller per handler defined by the endpoint
|
|
37
|
+
*/
|
|
38
|
+
declare function makeApi<Route extends string, Endpoint extends object, Params extends ParseRouteParams<Route> = ParseRouteParams<Route>>(path: Route, ...args: string extends Route ? [params?: Record<string, string>] : keyof ParseRouteParams<Route> extends never ? [] : [params: Params & ExactRouteParams<Params, ParseRouteParams<Route>>]): ApiMethods<Endpoint>;
|
|
39
|
+
//#endregion
|
|
1
40
|
//#region src/nav/load.d.ts
|
|
2
41
|
/**
|
|
3
|
-
* Loads and renders the page at the supplied path.
|
|
4
|
-
*/
|
|
42
|
+
* Loads and renders the page at the supplied path.
|
|
43
|
+
*/
|
|
5
44
|
declare function load(path: string): Promise<boolean>;
|
|
6
45
|
//#endregion
|
|
7
46
|
//#region src/nav/reload.d.ts
|
|
8
47
|
/**
|
|
9
|
-
* Reloads data for the currently loaded page and rerenders it.
|
|
10
|
-
*/
|
|
48
|
+
* Reloads data for the currently loaded page and rerenders it.
|
|
49
|
+
*/
|
|
11
50
|
declare function reload(): Promise<boolean>;
|
|
12
51
|
//#endregion
|
|
13
|
-
|
|
52
|
+
//#region src/nav/route.d.ts
|
|
53
|
+
/**
|
|
54
|
+
* Builds a route path in a type-safe manner, filling in dynamic segments, e.g.
|
|
55
|
+
* `route("/posts/[id]", { id: 5 })` returns `/posts/5`. The params object is
|
|
56
|
+
* checked for exact keys: missing or unknown params error at compile time.
|
|
57
|
+
*
|
|
58
|
+
* NOTE: The rest args conditional must stay INLINE — routing it through a type
|
|
59
|
+
* alias defeats `Params` inference, and excess keys stop being checked
|
|
60
|
+
* @param path The route path
|
|
61
|
+
* @param params The route params, required if the path has dynamic segments
|
|
62
|
+
* @returns The path with the params filled in, URI-encoded
|
|
63
|
+
*/
|
|
64
|
+
declare function route<Route extends string, Params extends ParseRouteParams<Route> = ParseRouteParams<Route>>(path: Route, ...args: string extends Route ? [params?: Record<string, string>] : keyof ParseRouteParams<Route> extends never ? [] : [params: Params & ExactRouteParams<Params, ParseRouteParams<Route>>]): string;
|
|
65
|
+
//#endregion
|
|
66
|
+
export { load, makeApi, reload, route };
|
|
14
67
|
//# sourceMappingURL=nav.d.mts.map
|
package/dist/nav.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nav.d.mts","names":[],"sources":["../src/nav/load.ts","../src/nav/reload.ts"
|
|
1
|
+
{"version":3,"file":"nav.d.mts","names":[],"sources":["../src/nav/api.ts","../src/nav/load.ts","../src/nav/reload.ts","../src/nav/route.ts"],"mappings":";;;;;;;;KASK,UAAU,WAAW,kBAAiB,sBACxC,QAAQ,WAAW,kBAAkB,oBAAoB,QACxD,OAAO;;;;;;KASN,eAAe,WAAW,kBAAiB,aAAa,iBAC1D;EAAgB,YAAY,cAAc;oBACzB,OACf,oBACA,OACD,oBACD;;KAGS,WAAW,8BACrB,yBAAyB,YAAY,YAAY,SAAS,mBAAkB,sBAC1E,kBAEF,OAAO,eAAe,YAAY,SAAS,WAC3C,OAAO,gBACH,QAAQ,UAAU,YAAY,SAAS;;;;;;;;;;;;;;;;;;;iBAuBrB,QACvB,sBACA,yBACA,eAAe,iBAAiB,SAAS,iBAAiB,QAE1D,MAAM,UAGH,qBAAqB,SACpB,SAAS,gCACJ,iBAAiB,6BAErB,QAAQ,SAAS,iBAAiB,QAAQ,iBAAiB,WAC9D,WAAW;;;;;;iBClEgB,KAAK,eAAe;;;;;;iBCApB,UAAU;;;;;;;;;;;;;;iBCQhB,MACvB,sBACA,eAAe,iBAAiB,SAAS,iBAAiB,QAE1D,MAAM,UACH,qBAAqB,SACpB,SAAS,gCACJ,iBAAiB,6BAErB,QAAQ,SAAS,iBAAiB,QAAQ,iBAAiB"}
|
package/dist/nav.mjs
CHANGED
|
@@ -1,6 +1,74 @@
|
|
|
1
|
-
import { t as $
|
|
2
|
-
import { clearLayoutSlot, fillLayoutSlot, hydrate, mount } from "@torpor/view";
|
|
3
|
-
|
|
1
|
+
import { t as $page } from "./_page-4cQKs_Dn.mjs";
|
|
2
|
+
import { clearLayoutSlot, fillLayoutSlot, hydrate, mount, unmount } from "@torpor/view";
|
|
3
|
+
//#region src/nav/route.ts
|
|
4
|
+
/**
|
|
5
|
+
* Builds a route path in a type-safe manner, filling in dynamic segments, e.g.
|
|
6
|
+
* `route("/posts/[id]", { id: 5 })` returns `/posts/5`. The params object is
|
|
7
|
+
* checked for exact keys: missing or unknown params error at compile time.
|
|
8
|
+
*
|
|
9
|
+
* NOTE: The rest args conditional must stay INLINE — routing it through a type
|
|
10
|
+
* alias defeats `Params` inference, and excess keys stop being checked
|
|
11
|
+
* @param path The route path
|
|
12
|
+
* @param params The route params, required if the path has dynamic segments
|
|
13
|
+
* @returns The path with the params filled in, URI-encoded
|
|
14
|
+
*/
|
|
15
|
+
function route(path, ...args) {
|
|
16
|
+
const params = args[0] ?? {};
|
|
17
|
+
return path.replace(/\[(\.\.\.)?([^\]]+)\]/g, (_, splat, name) => {
|
|
18
|
+
const value = params[name];
|
|
19
|
+
if (value === void 0) throw new Error(`Missing param '${name}' for route '${path}'`);
|
|
20
|
+
return splat ? value.split("/").map((segment) => encodeURIComponent(segment)).join("/") : encodeURIComponent(String(value));
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/nav/api.ts
|
|
25
|
+
const METHODS = [
|
|
26
|
+
"get",
|
|
27
|
+
"post",
|
|
28
|
+
"patch",
|
|
29
|
+
"put",
|
|
30
|
+
"del",
|
|
31
|
+
"options",
|
|
32
|
+
"head"
|
|
33
|
+
];
|
|
34
|
+
/**
|
|
35
|
+
* Creates typed client callers for a `+server` endpoint, e.g.
|
|
36
|
+
*
|
|
37
|
+
* ```ts
|
|
38
|
+
* import type endpoint from "@/routes/api/posts/[id]/+server";
|
|
39
|
+
* const post = makeApi<"/api/posts/[id]", typeof endpoint>("/api/posts/[id]");
|
|
40
|
+
* const data = await post.get(); // typed from the endpoint's responses
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* Method names match the endpoint's handlers (`del` for DELETE). Params are
|
|
44
|
+
* filled in and URI-encoded from the route path, and JSON responses are
|
|
45
|
+
* parsed; non-JSON responses resolve to the raw Response. Failed requests
|
|
46
|
+
* (non-2xx) throw.
|
|
47
|
+
*
|
|
48
|
+
* @param path The endpoint's route path
|
|
49
|
+
* @param params The route params, required if the path has dynamic segments
|
|
50
|
+
* @returns An object with one caller per handler defined by the endpoint
|
|
51
|
+
*/
|
|
52
|
+
function makeApi(path, ...args) {
|
|
53
|
+
const url = route(path, ...args);
|
|
54
|
+
const api = {};
|
|
55
|
+
for (const method of METHODS) api[method] = (body, init) => request(method, url, body, init);
|
|
56
|
+
return api;
|
|
57
|
+
}
|
|
58
|
+
async function request(method, url, body, init) {
|
|
59
|
+
const options = {
|
|
60
|
+
method: (method === "del" ? "delete" : method).toUpperCase(),
|
|
61
|
+
...init
|
|
62
|
+
};
|
|
63
|
+
if (body !== void 0 && options.body === void 0 && method !== "get" && method !== "head") if (typeof body === "object") {
|
|
64
|
+
options.headers ??= { "Content-Type": "application/json" };
|
|
65
|
+
options.body = JSON.stringify(body);
|
|
66
|
+
} else options.body = body;
|
|
67
|
+
const response = await fetch(url, options);
|
|
68
|
+
if (!response.ok) throw new Error(`API ${method.toUpperCase()} ${url} failed: ${response.status}`);
|
|
69
|
+
return (response.headers.get("Content-Type") ?? "").includes("application/json") ? await response.json() : response;
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
4
72
|
//#region src/state/client.ts
|
|
5
73
|
const CLIENT_SYMBOL = Symbol.for("torp.Client");
|
|
6
74
|
const client = globalThis[CLIENT_SYMBOL] ??= {
|
|
@@ -8,8 +76,6 @@ const client = globalThis[CLIENT_SYMBOL] ??= {
|
|
|
8
76
|
layoutStack: [],
|
|
9
77
|
prefetchedData: {}
|
|
10
78
|
};
|
|
11
|
-
var client_default = client;
|
|
12
|
-
|
|
13
79
|
//#endregion
|
|
14
80
|
//#region src/nav/reload.ts
|
|
15
81
|
/**
|
|
@@ -18,12 +84,11 @@ var client_default = client;
|
|
|
18
84
|
async function reload() {
|
|
19
85
|
return await navigate(new URL(document.location.href));
|
|
20
86
|
}
|
|
21
|
-
|
|
22
87
|
//#endregion
|
|
23
88
|
//#region src/nav/formSubmit.ts
|
|
24
89
|
async function formSubmit(e) {
|
|
25
90
|
e.preventDefault();
|
|
26
|
-
$
|
|
91
|
+
$page.form = void 0;
|
|
27
92
|
const form = e.target;
|
|
28
93
|
const button = e.submitter;
|
|
29
94
|
let action = form.action || document.URL;
|
|
@@ -33,25 +98,30 @@ async function formSubmit(e) {
|
|
|
33
98
|
method: "POST",
|
|
34
99
|
mode: "cors",
|
|
35
100
|
credentials: "same-origin",
|
|
36
|
-
headers: { "
|
|
101
|
+
headers: { "X-Torpor-Form-Submit": "" }
|
|
37
102
|
};
|
|
38
103
|
const response = await fetch(action, options);
|
|
39
|
-
if (response.status === 200 && response.headers.has("
|
|
40
|
-
const
|
|
104
|
+
if (response.status === 200 && response.headers.has("Content-Disposition") && /attachment;\s*filename="(.+?)"/.test(response.headers.get("Content-Disposition"))) {
|
|
105
|
+
const downloadFileName = response.headers.get("Content-Disposition")?.match(/attachment;\s*filename="(.+?)"/)[1] ?? "file";
|
|
106
|
+
const downloadLink = document.createElement("a");
|
|
107
|
+
downloadLink.href = window.URL.createObjectURL(await response.blob());
|
|
108
|
+
downloadLink.download = downloadFileName;
|
|
109
|
+
downloadLink.click();
|
|
110
|
+
} else if (response.status === 200 && response.headers.has("X-Torpor-Form-Redirect")) {
|
|
111
|
+
const redirect = response.headers.get("Location");
|
|
41
112
|
await load(redirect);
|
|
42
113
|
window.location.href = redirect;
|
|
43
114
|
} else if (response.status >= 200 && response.status <= 299 || response.status >= 400 && response.status <= 499) {
|
|
44
|
-
if (response.headers.get("
|
|
115
|
+
if (response.headers.get("Content-Type") === "application/json") $page.form = await response.json();
|
|
45
116
|
await reload();
|
|
46
117
|
}
|
|
47
118
|
}
|
|
48
|
-
|
|
49
119
|
//#endregion
|
|
50
120
|
//#region src/nav/loadData.ts
|
|
51
121
|
async function loadData(handler, params, path, query, newLayoutStack, clientEndPoint, serverEndPoint, prefetch = false) {
|
|
52
122
|
let data = {};
|
|
53
123
|
if (handler.layouts) {
|
|
54
|
-
let layoutStack =
|
|
124
|
+
let layoutStack = client.layoutStack;
|
|
55
125
|
for (let [i, layout] of handler.layouts.entries()) {
|
|
56
126
|
let layoutPath = layout.path;
|
|
57
127
|
for (let key in params) layoutPath = layoutPath.replace(`[${key}]`, params[key]);
|
|
@@ -78,7 +148,7 @@ async function loadData(handler, params, path, query, newLayoutStack, clientEndP
|
|
|
78
148
|
return data;
|
|
79
149
|
}
|
|
80
150
|
async function loadClientAndServerData(data, location, query, params, clientEndPoint, serverEndPoint) {
|
|
81
|
-
let prefetchedData =
|
|
151
|
+
let prefetchedData = client.prefetchedData;
|
|
82
152
|
if (clientEndPoint?.load) {
|
|
83
153
|
const clientKey = location + (query.size > 0 ? `?${query}` : "");
|
|
84
154
|
if (prefetchedData[clientKey]) Object.assign(data, prefetchedData[clientKey]);
|
|
@@ -121,7 +191,6 @@ function buildClientParams(url, params, data) {
|
|
|
121
191
|
data
|
|
122
192
|
};
|
|
123
193
|
}
|
|
124
|
-
|
|
125
194
|
//#endregion
|
|
126
195
|
//#region src/nav/navigate.ts
|
|
127
196
|
async function navigate(url, withHydration = false) {
|
|
@@ -132,17 +201,17 @@ async function navigate(url, withHydration = false) {
|
|
|
132
201
|
}
|
|
133
202
|
const path = url.pathname;
|
|
134
203
|
const query = url.searchParams;
|
|
135
|
-
const route =
|
|
204
|
+
const route = client.router.match(path, query);
|
|
136
205
|
if (!route) {
|
|
137
206
|
console.log("404");
|
|
138
207
|
return false;
|
|
139
208
|
}
|
|
140
|
-
$
|
|
209
|
+
$page.url = url;
|
|
141
210
|
if (path.endsWith("/_error")) {
|
|
142
|
-
$
|
|
143
|
-
$
|
|
211
|
+
$page.status = parseInt(query.get("status") ?? "404");
|
|
212
|
+
$page.error = { message: query.get("message") ?? "" };
|
|
144
213
|
window.history.replaceState({}, "", url.toString().split("?")[0]);
|
|
145
|
-
} else $
|
|
214
|
+
} else $page.status = 200;
|
|
146
215
|
const handler = route.handler;
|
|
147
216
|
const params = route.params || {};
|
|
148
217
|
const clientEndPoint = (await handler.endPoint()).default;
|
|
@@ -157,54 +226,63 @@ async function navigate(url, withHydration = false) {
|
|
|
157
226
|
let form;
|
|
158
227
|
if (formInput) {
|
|
159
228
|
form = JSON.parse(formInput.value);
|
|
160
|
-
$
|
|
229
|
+
$page.form = form;
|
|
161
230
|
formInput.remove();
|
|
162
231
|
}
|
|
163
232
|
let $props = { data };
|
|
164
|
-
$props.form = $
|
|
233
|
+
$props.form = $page.form;
|
|
165
234
|
const $context = { TorporBuildContext: { onformsubmit: formSubmit } };
|
|
166
|
-
|
|
235
|
+
client.layoutStack.push({
|
|
167
236
|
path: route.handler.path,
|
|
168
237
|
data: {},
|
|
169
238
|
reuse: false,
|
|
170
239
|
slotRegion: null
|
|
171
240
|
});
|
|
172
|
-
|
|
173
|
-
let layoutStack =
|
|
241
|
+
client.layoutStack = newLayoutStack;
|
|
242
|
+
let layoutStack = client.layoutStack;
|
|
174
243
|
let component = clientEndPoint.component;
|
|
175
244
|
let slots = void 0;
|
|
245
|
+
let reused = false;
|
|
246
|
+
let reusedIndex = -1;
|
|
176
247
|
if (handler.layouts) {
|
|
177
248
|
let slotFunctions = [];
|
|
178
|
-
slotFunctions[handler.layouts.length] = function clientComponent(parent
|
|
249
|
+
slotFunctions[handler.layouts.length] = function clientComponent(parent, anchor) {
|
|
179
250
|
let i = layoutStack.length - 1;
|
|
180
|
-
layoutStack[i].slotRegion = fillLayoutSlot(clientEndPoint.component, slotFunctions[i + 1], parent
|
|
251
|
+
layoutStack[i].slotRegion = fillLayoutSlot(clientEndPoint.component, slotFunctions[i + 1], parent, anchor, $props, $context);
|
|
181
252
|
};
|
|
182
253
|
for (let i = handler.layouts.length - 1; i >= 0; i--) {
|
|
183
254
|
const layoutEndPoint = (await handler.layouts[i].endPoint())?.default;
|
|
184
255
|
if (layoutEndPoint?.component) if (layoutStack[i].reuse) {
|
|
185
|
-
parent = layoutStack[i].slotRegion.startNode.parentNode;
|
|
186
|
-
clearLayoutSlot(layoutStack[i].slotRegion);
|
|
187
256
|
component = slotFunctions[i + 1];
|
|
257
|
+
reusedIndex = i;
|
|
258
|
+
reused = true;
|
|
188
259
|
break;
|
|
189
260
|
} else if (i === 0) {
|
|
190
261
|
component = layoutEndPoint.component;
|
|
191
262
|
slots = { _: slotFunctions[i + 1] };
|
|
192
|
-
} else slotFunctions[i] = function layoutComponent(parent
|
|
193
|
-
layoutStack[i - 1].slotRegion = fillLayoutSlot(layoutEndPoint.component, slotFunctions[i + 1], parent
|
|
263
|
+
} else slotFunctions[i] = function layoutComponent(parent, anchor, _, $context) {
|
|
264
|
+
layoutStack[i - 1].slotRegion = fillLayoutSlot(layoutEndPoint.component, slotFunctions[i + 1], parent, anchor, $props, $context);
|
|
194
265
|
};
|
|
195
266
|
}
|
|
196
267
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
268
|
+
try {
|
|
269
|
+
if (reused) {
|
|
270
|
+
const slotRegion = layoutStack[reusedIndex].slotRegion;
|
|
271
|
+
parent = slotRegion.startNode.parentNode;
|
|
272
|
+
clearLayoutSlot(slotRegion);
|
|
273
|
+
component(parent, null);
|
|
274
|
+
} else if (withHydration) hydrate(parent, component, $props, slots);
|
|
275
|
+
else {
|
|
276
|
+
unmount(parent);
|
|
277
|
+
mount(parent, component, $props, slots);
|
|
278
|
+
}
|
|
200
279
|
} catch (error) {
|
|
201
280
|
parent.innerHTML = "<span style=\"color: red\">Script syntax error</span><p>" + error + "</p>";
|
|
202
281
|
console.log(error);
|
|
203
282
|
}
|
|
204
|
-
|
|
283
|
+
client.prefetchedData = {};
|
|
205
284
|
return true;
|
|
206
285
|
}
|
|
207
|
-
|
|
208
286
|
//#endregion
|
|
209
287
|
//#region src/nav/load.ts
|
|
210
288
|
/**
|
|
@@ -213,7 +291,7 @@ async function navigate(url, withHydration = false) {
|
|
|
213
291
|
async function load(path) {
|
|
214
292
|
return await navigate(new URL(path, document.location.href));
|
|
215
293
|
}
|
|
216
|
-
|
|
217
294
|
//#endregion
|
|
218
|
-
export { load, reload };
|
|
295
|
+
export { load, makeApi, reload, route };
|
|
296
|
+
|
|
219
297
|
//# sourceMappingURL=nav.mjs.map
|