@torpor/build 1.2.0 → 1.4.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/dist/MiddlewareFunction-BZilnEei.d.mts +50 -0
- package/dist/MiddlewareFunction-BZilnEei.d.mts.map +1 -0
- package/dist/{Router-COdQuVUi.d.mts → Router-dAu_5PlG.d.mts} +2 -2
- package/dist/{Router-COdQuVUi.d.mts.map → Router-dAu_5PlG.d.mts.map} +1 -1
- package/dist/{Server-Dr6yqVa5.mjs → Server-CSti9cvM.mjs} +2 -2
- package/dist/Server-CSti9cvM.mjs.map +1 -0
- package/dist/{Server-Cler2w5O.d.mts → Server-qn5Ws_d0.d.mts} +3 -23
- package/dist/Server-qn5Ws_d0.d.mts.map +1 -0
- package/dist/ServerEvent-Bh9cEpro.mjs +335 -0
- package/dist/ServerEvent-Bh9cEpro.mjs.map +1 -0
- package/dist/ServerHook-BTSGfLiJ.d.mts +21 -0
- package/dist/ServerHook-BTSGfLiJ.d.mts.map +1 -0
- package/dist/{ServerHook-BlLArueW.d.mts → ServerLoadEvent-DzbxxkF8.d.mts} +13 -21
- package/dist/ServerLoadEvent-DzbxxkF8.d.mts.map +1 -0
- package/dist/SessionHelper-MaodMaLU.d.mts +148 -0
- package/dist/SessionHelper-MaodMaLU.d.mts.map +1 -0
- package/dist/{Site-CTADVh7d.d.mts → Site-CQ29Opvm.d.mts} +96 -75
- package/dist/Site-CQ29Opvm.d.mts.map +1 -0
- package/dist/{_page-4cQKs_Dn.mjs → _page-CW6SLuT4.mjs} +2 -1
- package/dist/{_page-4cQKs_Dn.mjs.map → _page-CW6SLuT4.mjs.map} +1 -1
- package/dist/basePath-DQnbHc7o.mjs +99 -0
- package/dist/basePath-DQnbHc7o.mjs.map +1 -0
- package/dist/bin/index.js +1 -1
- package/dist/clientEntry.mjs +17 -5
- package/dist/clientEntry.mjs.map +1 -1
- package/dist/dev.d.mts +2 -2
- package/dist/dev.mjs +2 -2
- package/dist/discovery.d.mts +43 -0
- package/dist/discovery.d.mts.map +1 -0
- package/dist/discovery.mjs +72 -0
- package/dist/discovery.mjs.map +1 -0
- package/dist/env-C2SUAwAq.mjs +49 -0
- package/dist/env-C2SUAwAq.mjs.map +1 -0
- package/dist/env.d.mts +84 -0
- package/dist/env.d.mts.map +1 -0
- package/dist/env.mjs +2 -0
- package/dist/form.d.mts +14 -5
- package/dist/form.d.mts.map +1 -1
- package/dist/form.mjs +14 -4
- package/dist/form.mjs.map +1 -1
- package/dist/index.d.mts +5 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +61 -0
- package/dist/index.mjs.map +1 -1
- package/dist/{load-C9RpS_4j.mjs → load-Uyj9I60p.mjs} +33 -21
- package/dist/load-Uyj9I60p.mjs.map +1 -0
- package/dist/nav.d.mts.map +1 -1
- package/dist/nav.mjs +4 -2
- package/dist/nav.mjs.map +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/run.d.mts +1 -1
- package/dist/run.d.mts.map +1 -1
- package/dist/run.mjs +1 -1
- package/dist/{runOpenApi-yIzYasNN.mjs → runOpenApi-DWfPiAv0.mjs} +239 -30
- package/dist/runOpenApi-DWfPiAv0.mjs.map +1 -0
- package/dist/server/Server.d.mts +1 -1
- package/dist/server/Server.mjs +1 -1
- package/dist/server.d.mts +19 -3
- package/dist/server.d.mts.map +1 -1
- package/dist/server.mjs +18 -3
- package/dist/server.mjs.map +1 -1
- package/dist/serverEntry-CnCVNNjp.mjs +13 -0
- package/dist/serverEntry-CnCVNNjp.mjs.map +1 -0
- package/dist/{serverEntry-DVv9YI3I.d.mts → serverEntry-fj0XfTs4.d.mts} +5 -4
- package/dist/serverEntry-fj0XfTs4.d.mts.map +1 -0
- package/dist/serverEntry.d.mts +2 -2
- package/dist/serverEntry.mjs +2 -2
- package/dist/{serverHandlers-fzIlmvX-.mjs → serverHandlers-B6EcNfBP.mjs} +95 -23
- package/dist/serverHandlers-B6EcNfBP.mjs.map +1 -0
- package/dist/sitemap-4imxJVrG.mjs +30 -0
- package/dist/sitemap-4imxJVrG.mjs.map +1 -0
- package/dist/sitemap-BjqN4d4W.d.mts +112 -0
- package/dist/sitemap-BjqN4d4W.d.mts.map +1 -0
- package/dist/state.d.mts +6 -0
- package/dist/state.d.mts.map +1 -1
- package/dist/state.mjs +1 -1
- package/dist/test.d.mts +2 -2
- package/dist/test.mjs +2 -2
- package/package.json +9 -1
- package/src/types/manifest.d.ts +4 -0
- package/dist/Server-Cler2w5O.d.mts.map +0 -1
- package/dist/Server-Dr6yqVa5.mjs.map +0 -1
- package/dist/ServerEvent-CTx3Q-hk.d.mts +0 -48
- package/dist/ServerEvent-CTx3Q-hk.d.mts.map +0 -1
- package/dist/ServerEvent-gmOvC_Yf.mjs +0 -100
- package/dist/ServerEvent-gmOvC_Yf.mjs.map +0 -1
- package/dist/ServerHook-BlLArueW.d.mts.map +0 -1
- package/dist/Site-CTADVh7d.d.mts.map +0 -1
- package/dist/load-C9RpS_4j.mjs.map +0 -1
- package/dist/runOpenApi-yIzYasNN.mjs.map +0 -1
- package/dist/serverEntry-CUXFQ07g.mjs +0 -11
- package/dist/serverEntry-CUXFQ07g.mjs.map +0 -1
- package/dist/serverEntry-DVv9YI3I.d.mts.map +0 -1
- package/dist/serverHandlers-fzIlmvX-.mjs.map +0 -1
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as getBasePath, s as stripBaseFromUrl } from "./basePath-DQnbHc7o.mjs";
|
|
2
|
+
import { t as $page } from "./_page-CW6SLuT4.mjs";
|
|
2
3
|
import { clearLayoutSlot, fillLayoutSlot, hydrate, mount, unmount } from "@torpor/view";
|
|
3
4
|
//#region src/state/client.ts
|
|
4
5
|
const CLIENT_SYMBOL = Symbol.for("torp.Client");
|
|
5
6
|
const client = globalThis[CLIENT_SYMBOL] ??= {
|
|
6
7
|
router: null,
|
|
7
8
|
layoutStack: [],
|
|
8
|
-
prefetchedData: {}
|
|
9
|
+
prefetchedData: {},
|
|
10
|
+
viewTransitions: false
|
|
9
11
|
};
|
|
10
12
|
//#endregion
|
|
11
13
|
//#region src/nav/reload.ts
|
|
@@ -69,13 +71,13 @@ async function loadData(handler, params, path, query, newLayoutStack, clientEndP
|
|
|
69
71
|
};
|
|
70
72
|
const layoutEndPoint = (await layout.endPoint())?.default;
|
|
71
73
|
const layoutServerEndPoint = layout.serverEndPoint && (await layout.serverEndPoint())?.default;
|
|
72
|
-
if ((await loadClientAndServerData(stackLayout.data, document.location.origin + layoutPath, query, params, layoutEndPoint, layoutServerEndPoint))?.ok === false) return;
|
|
74
|
+
if ((await loadClientAndServerData(stackLayout.data, document.location.origin + getBasePath() + layoutPath, query, params, layoutEndPoint, layoutServerEndPoint))?.ok === false) return;
|
|
73
75
|
Object.assign(data, stackLayout.data);
|
|
74
76
|
newLayoutStack.push(stackLayout);
|
|
75
77
|
}
|
|
76
78
|
}
|
|
77
79
|
}
|
|
78
|
-
if ((await loadClientAndServerData(data, document.location.origin + path, query, params, clientEndPoint, serverEndPoint))?.ok === false) return;
|
|
80
|
+
if ((await loadClientAndServerData(data, document.location.origin + getBasePath() + path, query, params, clientEndPoint, serverEndPoint))?.ok === false) return;
|
|
79
81
|
return data;
|
|
80
82
|
}
|
|
81
83
|
async function loadClientAndServerData(data, location, query, params, clientEndPoint, serverEndPoint) {
|
|
@@ -128,12 +130,13 @@ function buildClientParams(url, params, data) {
|
|
|
128
130
|
}
|
|
129
131
|
//#endregion
|
|
130
132
|
//#region src/nav/navigate.ts
|
|
131
|
-
async function navigate(
|
|
133
|
+
async function navigate(rawUrl, withHydration = false) {
|
|
132
134
|
let parent = document.getElementById("app");
|
|
133
135
|
if (!parent) {
|
|
134
136
|
console.log("500");
|
|
135
137
|
return false;
|
|
136
138
|
}
|
|
139
|
+
const url = stripBaseFromUrl(rawUrl, getBasePath()) ?? rawUrl;
|
|
137
140
|
const path = url.pathname;
|
|
138
141
|
const query = url.searchParams;
|
|
139
142
|
const route = client.router.match(path, query);
|
|
@@ -145,7 +148,7 @@ async function navigate(url, withHydration = false) {
|
|
|
145
148
|
if (path.endsWith("/_error")) {
|
|
146
149
|
$page.status = parseInt(query.get("status") ?? "404");
|
|
147
150
|
$page.error = { message: query.get("message") ?? "" };
|
|
148
|
-
window.history.replaceState({}, "",
|
|
151
|
+
window.history.replaceState({}, "", rawUrl.toString().split("?")[0]);
|
|
149
152
|
} else $page.status = 200;
|
|
150
153
|
const handler = route.handler;
|
|
151
154
|
const params = route.params || {};
|
|
@@ -157,6 +160,7 @@ async function navigate(url, withHydration = false) {
|
|
|
157
160
|
const serverEndPoint = handler.serverEndPoint && (await handler.serverEndPoint())?.default;
|
|
158
161
|
let newLayoutStack = [];
|
|
159
162
|
const data = await loadData(handler, params, path, query, newLayoutStack, clientEndPoint, serverEndPoint);
|
|
163
|
+
if (data === void 0) return false;
|
|
160
164
|
let formInput = document.getElementById("t-form-data");
|
|
161
165
|
let form;
|
|
162
166
|
if (formInput) {
|
|
@@ -202,21 +206,29 @@ async function navigate(url, withHydration = false) {
|
|
|
202
206
|
}
|
|
203
207
|
}
|
|
204
208
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
209
|
+
let app = parent;
|
|
210
|
+
const render = () => {
|
|
211
|
+
try {
|
|
212
|
+
if (reused) {
|
|
213
|
+
const slotRegion = layoutStack[reusedIndex].slotRegion;
|
|
214
|
+
app = slotRegion.startNode.parentNode;
|
|
215
|
+
clearLayoutSlot(slotRegion);
|
|
216
|
+
component(app, null);
|
|
217
|
+
} else if (withHydration) hydrate(app, component, $props, slots);
|
|
218
|
+
else {
|
|
219
|
+
unmount(app);
|
|
220
|
+
mount(app, component, $props, slots);
|
|
221
|
+
}
|
|
222
|
+
} catch (error) {
|
|
223
|
+
app.innerHTML = "<span style=\"color: red\">Script syntax error</span><p>" + error + "</p>";
|
|
224
|
+
console.log(error);
|
|
215
225
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
226
|
+
if (!withHydration) {
|
|
227
|
+
if (stripBaseFromUrl(new URL(document.location.href), getBasePath())?.pathname !== path) window.scrollTo(0, 0);
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
if (!withHydration && client.viewTransitions && typeof document.startViewTransition === "function") document.startViewTransition(render);
|
|
231
|
+
else render();
|
|
220
232
|
client.prefetchedData = {};
|
|
221
233
|
return true;
|
|
222
234
|
}
|
|
@@ -231,4 +243,4 @@ async function load(path) {
|
|
|
231
243
|
//#endregion
|
|
232
244
|
export { client as a, reload as i, navigate as n, loadData as r, load as t };
|
|
233
245
|
|
|
234
|
-
//# sourceMappingURL=load-
|
|
246
|
+
//# sourceMappingURL=load-Uyj9I60p.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-Uyj9I60p.mjs","names":[],"sources":["../src/state/client.ts","../src/nav/reload.ts","../src/nav/formSubmit.ts","../src/nav/loadData.ts","../src/nav/navigate.ts","../src/nav/load.ts"],"sourcesContent":["import type ClientState from \"../types/ClientState\";\n\nconst CLIENT_SYMBOL: unique symbol = Symbol.for(\"torp.Client\");\n\nconst client: ClientState =\n\t// @ts-ignore\n\t(globalThis[CLIENT_SYMBOL] ??= {\n\t\t// @ts-ignore This will definitely, 100% get set\n\t\trouter: null,\n\t\tlayoutStack: [],\n\t\t// TODO: Probably expire after 30 seconds or something?\n\t\tprefetchedData: {},\n\t\tviewTransitions: false,\n\t});\n\nexport default client;\n","import navigate from \"./navigate\";\n\n/**\n * Reloads data for the currently loaded page and rerenders it.\n */\nexport default async function reload(): Promise<boolean> {\n\tconst url = new URL(document.location.href);\n\treturn await navigate(url);\n}\n","import $page from \"../state/$page\";\nimport load from \"./load\";\nimport reload from \"./reload\";\n\nexport default async function formSubmit(e: SubmitEvent): Promise<void> {\n\t// We'll take it from here\n\te.preventDefault();\n\n\t// Reset $page on submit and link navigation\n\t// TODO: Should we put data in $page.data??\n\t$page.form = undefined;\n\n\t// Perform a POST fetch to the form's action location\n\tconst form = e.target as HTMLFormElement;\n\tconst button = e.submitter as HTMLButtonElement;\n\t// HACK: submit buttons have their formAction set to the current page if not\n\t// set, so we can't just check whether it's empty and fallback to form action\n\t//const action = button?.formAction ?? form.action;\n\tlet action = form.action || document.URL;\n\tif (button && button.formAction && button.formAction !== document.URL) {\n\t\taction = button.formAction;\n\t}\n\tconst options = {\n\t\tbody: new FormData(form),\n\t\tmethod: \"POST\",\n\t\tmode: \"cors\",\n\t\tcredentials: \"same-origin\",\n\t\t// Add a special header which tells the server that we want the raw\n\t\t// error response (if applicable) rather than the reloaded page that you\n\t\t// would get if submitting a page without javascript\n\t\theaders: {\n\t\t\t\"X-Torpor-Form-Submit\": \"\",\n\t\t},\n\t};\n\tconst response = await fetch(\n\t\taction,\n\t\t// @ts-ignore -- don't know what it's complaining about here\n\t\toptions,\n\t);\n\n\tif (\n\t\tresponse.status === 200 &&\n\t\tresponse.headers.has(\"Content-Disposition\") &&\n\t\t/attachment;\\s*filename=\"(.+?)\"/.test(response.headers.get(\"Content-Disposition\")!)\n\t) {\n\t\t// TODO: Is this enough to check to see if we have a file that should download?\n\t\t// Download the file by creating a dummy <a> and clicking it\n\t\tconst downloadFileName =\n\t\t\tresponse.headers.get(\"Content-Disposition\")?.match(/attachment;\\s*filename=\"(.+?)\"/)![1] ??\n\t\t\t\"file\";\n\t\tconst downloadLink = document.createElement(\"a\");\n\t\tdownloadLink.href = window.URL.createObjectURL(await response.blob());\n\t\tdownloadLink.download = downloadFileName;\n\t\tdownloadLink.click();\n\t} else if (response.status === 200 && response.headers.has(\"X-Torpor-Form-Redirect\")) {\n\t\t// TODO: Should do the correct type of redirect e.g. seeOther etc\n\t\tconst redirect = response.headers.get(\"Location\")!;\n\t\tawait load(redirect);\n\t\twindow.location.href = redirect;\n\t} else if (\n\t\t(response.status >= 200 && response.status <= 299) ||\n\t\t(response.status >= 400 && response.status <= 499)\n\t) {\n\t\t// Put the response into `$page.form`, and it will be put into `$props` in reload\n\t\tif (response.headers.get(\"Content-Type\") === \"application/json\") {\n\t\t\t$page.form = await response.json();\n\t\t}\n\t\tawait reload();\n\t}\n}\n","import client from \"../state/client\";\nimport { getBasePath } from \"../site/basePath\";\nimport type LayoutPath from \"../types/LayoutPath\";\nimport type PageEndPoint from \"../types/PageEndPoint\";\nimport type PageServerEndPoint from \"../types/PageServerEndPoint\";\nimport type RouteHandler from \"../types/RouteHandler\";\n\nexport default async function loadData(\n\thandler: RouteHandler,\n\tparams: Record<string, string>,\n\tpath: string,\n\tquery: URLSearchParams,\n\tnewLayoutStack: LayoutPath[],\n\tclientEndPoint: PageEndPoint | undefined,\n\tserverEndPoint: PageServerEndPoint | undefined,\n\tprefetch = false,\n): Promise<Record<string, string> | void> {\n\tlet data = {};\n\tif (handler.layouts) {\n\t\tlet layoutStack = client.layoutStack;\n\t\tfor (let [i, layout] of handler.layouts.entries()) {\n\t\t\tlet layoutPath = layout.path;\n\t\t\tfor (let key in params) {\n\t\t\t\tlayoutPath = layoutPath.replace(`[${key}]`, params[key]);\n\t\t\t}\n\t\t\tif (layoutStack.at(i)?.path === layoutPath) {\n\t\t\t\t// We've already loaded this layout, we can just re-use its data and UI\n\t\t\t\tif (!prefetch) {\n\t\t\t\t\tlayoutStack[i].reuse = true;\n\t\t\t\t}\n\t\t\t\tnewLayoutStack[i] = layoutStack[i];\n\t\t\t\tObject.assign(data, layoutStack[i].data);\n\t\t\t} else {\n\t\t\t\tconst stackLayout = { path: layoutPath, data: {}, reuse: false, slotRegion: null };\n\t\t\t\tconst layoutEndPoint: PageEndPoint | undefined = (await layout.endPoint())?.default;\n\t\t\t\tconst layoutServerEndPoint: PageServerEndPoint | undefined =\n\t\t\t\t\tlayout.serverEndPoint && (await layout.serverEndPoint())?.default;\n\t\t\t\tconst layoutResponse = await loadClientAndServerData(\n\t\t\t\t\tstackLayout.data,\n\t\t\t\t\tdocument.location.origin + getBasePath() + layoutPath,\n\t\t\t\t\tquery,\n\t\t\t\t\tparams,\n\t\t\t\t\tlayoutEndPoint,\n\t\t\t\t\tlayoutServerEndPoint,\n\t\t\t\t);\n\t\t\t\tif (layoutResponse?.ok === false) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tObject.assign(data, stackLayout.data);\n\t\t\t\tnewLayoutStack.push(stackLayout);\n\t\t\t}\n\t\t}\n\t}\n\tlet endPointResponse = await loadClientAndServerData(\n\t\tdata,\n\t\tdocument.location.origin + getBasePath() + path,\n\t\tquery,\n\t\tparams,\n\t\tclientEndPoint,\n\t\tserverEndPoint,\n\t);\n\tif (endPointResponse?.ok === false) {\n\t\treturn;\n\t}\n\treturn data;\n}\n\nasync function loadClientAndServerData(\n\tdata: Record<string, any>,\n\tlocation: string,\n\tquery: URLSearchParams,\n\tparams: Record<string, string>,\n\tclientEndPoint?: PageEndPoint,\n\tserverEndPoint?: PageServerEndPoint,\n): Promise<Response | undefined | void> {\n\tlet prefetchedData = client.prefetchedData;\n\n\tif (clientEndPoint?.load) {\n\t\tconst clientKey = location + (query.size > 0 ? `?${query}` : \"\");\n\t\tif (prefetchedData[clientKey]) {\n\t\t\tObject.assign(data, prefetchedData[clientKey]);\n\t\t} else {\n\t\t\tconst clientUrl = new URL(document.location.href);\n\t\t\tfor (let [name, value] of query) {\n\t\t\t\tclientUrl.searchParams.append(name, value);\n\t\t\t}\n\t\t\tconst clientParams = buildClientParams(clientUrl, params, data);\n\t\t\tconst clientResponse = await clientEndPoint.load(clientParams);\n\t\t\tif (clientResponse) {\n\t\t\t\tif (clientResponse.ok) {\n\t\t\t\t\tif (clientResponse.headers.get(\"Content-Type\")?.includes(\"application/json\")) {\n\t\t\t\t\t\tconst clientData = await clientResponse.json();\n\t\t\t\t\t\tObject.assign(data, clientData);\n\t\t\t\t\t\tprefetchedData[clientKey] = clientData;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\treturn clientResponse;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (serverEndPoint?.load) {\n\t\tconst serverLocation = location.replace(/\\/$/, \"\") + \"/~server\";\n\t\tconst serverKey = serverLocation + (query.size > 0 ? `?${query}` : \"\");\n\t\tif (prefetchedData[serverKey]) {\n\t\t\tObject.assign(data, prefetchedData[serverKey]);\n\t\t} else {\n\t\t\tconst serverUrl = new URL(serverLocation);\n\t\t\tfor (let [name, value] of query) {\n\t\t\t\tserverUrl.searchParams.append(name, value);\n\t\t\t}\n\t\t\tconst serverResponse = await fetch(serverUrl);\n\t\t\tif (serverResponse) {\n\t\t\t\tif (serverResponse.ok) {\n\t\t\t\t\tif (serverResponse.headers.get(\"Content-Type\")?.includes(\"application/json\")) {\n\t\t\t\t\t\tconst serverData = await serverResponse.json();\n\t\t\t\t\t\tObject.assign(data, serverData);\n\t\t\t\t\t\tprefetchedData[serverKey] = serverData;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\treturn serverResponse;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction buildClientParams(url: URL, params: Record<string, string>, data: Record<string, any>) {\n\treturn {\n\t\turl,\n\t\tparams,\n\t\tdata,\n\t};\n}\n","import { clearLayoutSlot, fillLayoutSlot, hydrate } from \"@torpor/view\";\nimport { type Component, type SlotRender } from \"@torpor/view\";\nimport { mount, unmount } from \"@torpor/view\";\nimport $page from \"../state/$page\";\nimport client from \"../state/client\";\nimport { getBasePath, stripBaseFromUrl } from \"../site/basePath\";\nimport type LayoutPath from \"../types/LayoutPath\";\nimport type PageEndPoint from \"../types/PageEndPoint\";\nimport type PageServerEndPoint from \"../types/PageServerEndPoint\";\nimport formSubmit from \"./formSubmit\";\nimport loadData from \"./loadData\";\n\n// @ts-ignore\nexport default async function navigate(rawUrl: URL, withHydration = false): Promise<boolean> {\n\tlet parent = document.getElementById(\"app\");\n\tif (!parent) {\n\t\t// TODO: 500\n\t\tconsole.log(\"500\");\n\t\treturn false;\n\t}\n\n\t// The base path is stripped before matching (routes are base-free), but\n\t// kept in browser state: the caller pushes the original URL. When the\n\t// url doesn't carry the base there will be no matching route anyway\n\tconst url = stripBaseFromUrl(rawUrl, getBasePath()) ?? rawUrl;\n\tconst path = url.pathname;\n\tconst query = url.searchParams;\n\n\t//console.log(`navigating to '${path}'${query.size ? ` with ${query}` : \"\"}`);\n\n\tconst route = client.router.match(path, query);\n\tif (!route) {\n\t\t// TODO: 404\n\t\tconsole.log(\"404\");\n\t\treturn false;\n\t}\n\n\t// Update $page before building the components\n\t$page.url = url;\n\tif (path.endsWith(\"/_error\")) {\n\t\t$page.status = parseInt(query.get(\"status\") ?? \"404\");\n\t\t$page.error = { message: query.get(\"message\") ?? \"\" };\n\t\t// Make it look a bit classier by removing the query -- keeping the\n\t\t// original URL, so the base path stays in the address bar\n\t\twindow.history.replaceState({}, \"\", rawUrl.toString().split(\"?\")[0]);\n\t} else {\n\t\t$page.status = 200;\n\t}\n\tconst handler = route.handler;\n\tconst params = route.params || {};\n\n\t// There must be a client endpoint with a component\n\tconst clientEndPoint: PageEndPoint | undefined = (await handler.endPoint()).default;\n\tif (!clientEndPoint?.component) {\n\t\t// TODO: 404\n\t\tconsole.log(\"404\");\n\t\treturn false;\n\t}\n\n\t// There may be a server endpoint\n\tconst serverEndPoint: PageServerEndPoint | undefined =\n\t\thandler.serverEndPoint && (await handler.serverEndPoint())?.default;\n\n\tlet newLayoutStack: LayoutPath[] = [];\n\n\t// Pass the data into $props\n\t// TODO: Don't load if this is the first time -- it should have been passed\n\t// to us, somehow...\n\tconst data = await loadData(\n\t\thandler,\n\t\tparams,\n\t\tpath,\n\t\tquery,\n\t\tnewLayoutStack,\n\t\tclientEndPoint,\n\t\tserverEndPoint,\n\t);\n\tif (data === undefined) {\n\t\t// The data couldn't be loaded -- a load function returned an error\n\t\t// response, or (on a prerendered site with no server) the ~server\n\t\t// request got a 404. Leave the current page alone; the caller\n\t\t// (e.g. the client entry) falls back to a full page load\n\t\treturn false;\n\t}\n\t// We may have form data in a hidden input -- not sure if this is the best\n\t// way to do it\n\tlet formInput = document.getElementById(\"t-form-data\") as HTMLInputElement;\n\tlet form: Record<string, string> | undefined;\n\tif (formInput) {\n\t\tform = JSON.parse(formInput.value);\n\t\t$page.form = form;\n\t\tformInput.remove();\n\t}\n\tlet $props: Record<string, any> = { data };\n\n\t// We may have form data in $page.form, either from the hidden input\n\t// (above), or added via onformsubmit (below)\n\t$props.form = $page.form;\n\n\t// Add some special context for submitting Forms client-side\n\tconst $context = {\n\t\tTorporBuildContext: { onformsubmit: formSubmit },\n\t};\n\n\tclient.layoutStack.push({ path: route.handler.path, data: {}, reuse: false, slotRegion: null });\n\tclient.layoutStack = newLayoutStack;\n\tlet layoutStack = client.layoutStack;\n\n\t// If there are layouts, work our way upwards, pushing each component into\n\t// the default slot of its parent\n\t// TODO: There's probably a nicer way to do this with reducers or something\n\tlet component = clientEndPoint.component as Component;\n\tlet slots: Record<string, SlotRender> | undefined = undefined;\n\tlet reused = false;\n\t// The index of the innermost reused layout. Its slotRegion is the region\n\t// that must be cleared and refilled (it contains the next layout's or the\n\t// page's content). This is usually the last entry of the stack, but not\n\t// when an outer layout is reused while an inner one is new — e.g.\n\t// navigating between sections that share the root layout.\n\tlet reusedIndex = -1;\n\tif (handler.layouts) {\n\t\tlet slotFunctions: SlotRender[] = [];\n\t\t// The last slot function will render the client component\n\t\tslotFunctions[handler.layouts.length] = function clientComponent(parent, anchor) {\n\t\t\tlet i = layoutStack.length - 1;\n\t\t\tlayoutStack[i].slotRegion = fillLayoutSlot(\n\t\t\t\tclientEndPoint.component!,\n\t\t\t\tslotFunctions[i + 1],\n\t\t\t\tparent,\n\t\t\t\tanchor,\n\t\t\t\t$props,\n\t\t\t\t$context,\n\t\t\t);\n\t\t};\n\n\t\t// Each earlier slot function will render a layout\n\t\tfor (let i = handler.layouts.length - 1; i >= 0; i--) {\n\t\t\tconst layoutEndPoint: PageEndPoint | undefined = (await handler.layouts[i].endPoint())\n\t\t\t\t?.default;\n\t\t\tif (layoutEndPoint?.component) {\n\t\t\t\tif (layoutStack[i].reuse) {\n\t\t\t\t\t// Reuse this layout — clear and refill its slot (done in\n\t\t\t\t\t// the try block below so a failure doesn't leave the slot\n\t\t\t\t\t// half-cleared)\n\t\t\t\t\tcomponent = slotFunctions[i + 1] as Component;\n\t\t\t\t\treusedIndex = i;\n\t\t\t\t\treused = true;\n\t\t\t\t\tbreak;\n\t\t\t\t} else if (i === 0) {\n\t\t\t\t\tcomponent = layoutEndPoint.component as Component;\n\t\t\t\t\tslots = { _: slotFunctions[i + 1] };\n\t\t\t\t} else {\n\t\t\t\t\tslotFunctions[i] = function layoutComponent(parent, anchor, _, $context) {\n\t\t\t\t\t\tlayoutStack[i - 1].slotRegion = fillLayoutSlot(\n\t\t\t\t\t\t\tlayoutEndPoint.component!,\n\t\t\t\t\t\t\tslotFunctions[i + 1],\n\t\t\t\t\t\t\tparent,\n\t\t\t\t\t\t\tanchor,\n\t\t\t\t\t\t\t$props,\n\t\t\t\t\t\t\t$context,\n\t\t\t\t\t\t);\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// A definite alias, so that the render closure's type checks hold\n\tlet app: HTMLElement = parent;\n\t// The DOM update, wrapped by the view transitions API when navigating\n\t// (but not on the initial hydration)\n\tconst render = () => {\n\t\ttry {\n\t\t\tif (reused) {\n\t\t\t\t// The layout is being reused — clear the old slot content, then\n\t\t\t\t// call the slot function directly to fill it with the new page.\n\t\t\t\t// We must not go through `mount` here: the slot's container still\n\t\t\t\t// holds the layout's own children (e.g. a header), which `mount`\n\t\t\t\t// refuses to mount into. Both the clear and the fill are inside\n\t\t\t\t// the try so that a failure doesn't leave the slot half-cleared.\n\t\t\t\tconst slotRegion = layoutStack[reusedIndex].slotRegion;\n\t\t\t\tapp = slotRegion.startNode!.parentNode as HTMLElement;\n\t\t\t\tclearLayoutSlot(slotRegion);\n\t\t\t\tcomponent(app, null);\n\t\t\t} else if (withHydration) {\n\t\t\t\thydrate(app, component, $props, slots);\n\t\t\t} else {\n\t\t\t\t// The layout chain changed (or there was no previous layout to\n\t\t\t\t// reuse): tear down the previous UI entirely — disposing its\n\t\t\t\t// region tree and clearing `#app` — so `mount` starts fresh.\n\t\t\t\t// Without this, `mount` throws because `#app` still holds the\n\t\t\t\t// previous render's children, and it would reuse a stale root\n\t\t\t\t// region.\n\t\t\t\tunmount(app);\n\t\t\t\tmount(app, component, $props, slots);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// TODO: Show a proper Error component\n\t\t\tapp.innerHTML = '<span style=\"color: red\">Script syntax error</span><p>' + error + \"</p>\";\n\t\t\tconsole.log(error);\n\t\t}\n\n\t\tif (!withHydration) {\n\t\t\t// Scroll back to the top when moving to a different page; keep the\n\t\t\t// scroll position for same-page navigations (query changes,\n\t\t\t// re-renders after form actions)\n\t\t\tconst current = stripBaseFromUrl(new URL(document.location.href), getBasePath())?.pathname;\n\t\t\tif (current !== path) {\n\t\t\t\twindow.scrollTo(0, 0);\n\t\t\t}\n\t\t}\n\t};\n\n\tif (\n\t\t!withHydration &&\n\t\tclient.viewTransitions &&\n\t\ttypeof document.startViewTransition === \"function\"\n\t) {\n\t\tdocument.startViewTransition(render);\n\t} else {\n\t\trender();\n\t}\n\n\t// Reset prefetched data on each navigation\n\tclient.prefetchedData = {};\n\n\treturn true;\n}\n","import navigate from \"./navigate\";\n\n/**\n * Loads and renders the page at the supplied path.\n */\nexport default async function load(path: string): Promise<boolean> {\n\tconst url = new URL(path, document.location.href);\n\treturn await navigate(url);\n}\n"],"mappings":"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"}
|
package/dist/nav.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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,8BAErB,yBAAyB,YAAY,YAAY,SAAS,mBAAkB,sBACzE,kBAGH,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;;;;;;iBCpEgB,KAAK,eAAe;;;;;;iBCApB,UAAU;;;;;;;;;;;;;;
|
|
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,8BAErB,yBAAyB,YAAY,YAAY,SAAS,mBAAkB,sBACzE,kBAGH,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;;;;;;iBCpEgB,KAAK,eAAe;;;;;;iBCApB,UAAU;;;;;;;;;;;;;;iBCShB,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,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as getBasePath, t as addBaseToPath } from "./basePath-DQnbHc7o.mjs";
|
|
2
|
+
import { i as reload, t as load } from "./load-Uyj9I60p.mjs";
|
|
2
3
|
//#region src/nav/route.ts
|
|
3
4
|
/**
|
|
4
5
|
* Builds a route path in a type-safe manner, filling in dynamic segments, e.g.
|
|
@@ -13,11 +14,12 @@ import { i as reload, t as load } from "./load-C9RpS_4j.mjs";
|
|
|
13
14
|
*/
|
|
14
15
|
function route(path, ...args) {
|
|
15
16
|
const params = args[0] ?? {};
|
|
16
|
-
|
|
17
|
+
const filled = path.replace(/\[(\.\.\.)?([^\]]+)\]/g, (_, splat, name) => {
|
|
17
18
|
const value = params[name];
|
|
18
19
|
if (value === void 0) throw new Error(`Missing param '${name}' for route '${path}'`);
|
|
19
20
|
return splat ? value.split("/").map((segment) => encodeURIComponent(segment)).join("/") : encodeURIComponent(String(value));
|
|
20
21
|
});
|
|
22
|
+
return addBaseToPath(filled, getBasePath());
|
|
21
23
|
}
|
|
22
24
|
//#endregion
|
|
23
25
|
//#region src/nav/api.ts
|
package/dist/nav.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nav.mjs","names":[],"sources":["../src/nav/route.ts","../src/nav/api.ts"],"sourcesContent":["import type { ExactRouteParams, ParseRouteParams } from \"../types/ParseRouteParams\";\n\n/**\n * Builds a route path in a type-safe manner, filling in dynamic segments, e.g.\n * `route(\"/posts/[id]\", { id: 5 })` returns `/posts/5`. The params object is\n * checked for exact keys: missing or unknown params error at compile time.\n *\n * NOTE: The rest args conditional must stay INLINE — routing it through a type\n * alias defeats `Params` inference, and excess keys stop being checked\n * @param path The route path\n * @param params The route params, required if the path has dynamic segments\n * @returns The path with the params filled in, URI-encoded\n */\nexport default function route<\n\tRoute extends string,\n\tParams extends ParseRouteParams<Route> = ParseRouteParams<Route>,\n>(\n\tpath: Route,\n\t...args: string extends Route\n\t\t? [params?: Record<string, string>]\n\t\t: keyof ParseRouteParams<Route> extends never\n\t\t\t? []\n\t\t\t: [params: Params & ExactRouteParams<Params, ParseRouteParams<Route>>]\n): string {\n\tconst params = ((args as unknown[])[0] ?? {}) as Record<string, string>;\n\
|
|
1
|
+
{"version":3,"file":"nav.mjs","names":[],"sources":["../src/nav/route.ts","../src/nav/api.ts"],"sourcesContent":["import { addBaseToPath, getBasePath } from \"../site/basePath\";\nimport type { ExactRouteParams, ParseRouteParams } from \"../types/ParseRouteParams\";\n\n/**\n * Builds a route path in a type-safe manner, filling in dynamic segments, e.g.\n * `route(\"/posts/[id]\", { id: 5 })` returns `/posts/5`. The params object is\n * checked for exact keys: missing or unknown params error at compile time.\n *\n * NOTE: The rest args conditional must stay INLINE — routing it through a type\n * alias defeats `Params` inference, and excess keys stop being checked\n * @param path The route path\n * @param params The route params, required if the path has dynamic segments\n * @returns The path with the params filled in, URI-encoded\n */\nexport default function route<\n\tRoute extends string,\n\tParams extends ParseRouteParams<Route> = ParseRouteParams<Route>,\n>(\n\tpath: Route,\n\t...args: string extends Route\n\t\t? [params?: Record<string, string>]\n\t\t: keyof ParseRouteParams<Route> extends never\n\t\t\t? []\n\t\t\t: [params: Params & ExactRouteParams<Params, ParseRouteParams<Route>>]\n): string {\n\tconst params = ((args as unknown[])[0] ?? {}) as Record<string, string>;\n\tconst filled = path.replace(\n\t\t/\\[(\\.\\.\\.)?([^\\]]+)\\]/g,\n\t\t(_, splat: string | undefined, name: string) => {\n\t\t\tconst value = params[name];\n\t\t\tif (value === undefined) {\n\t\t\t\tthrow new Error(`Missing param '${name}' for route '${path}'`);\n\t\t\t}\n\t\t\treturn splat\n\t\t\t\t? value\n\t\t\t\t\t\t.split(\"/\")\n\t\t\t\t\t\t.map((segment) => encodeURIComponent(segment))\n\t\t\t\t\t\t.join(\"/\")\n\t\t\t\t: encodeURIComponent(String(value));\n\t\t},\n\t);\n\t// Links are written base-free; the router strips the base before matching\n\treturn addBaseToPath(filled, getBasePath());\n}\n","import type { ExactRouteParams, ParseRouteParams } from \"../types/ParseRouteParams\";\nimport type TypedResponse from \"../response/TypedResponse\";\nimport route from \"./route\";\n\n/**\n * The value a client call resolves to: the endpoint's typed JSON body when it\n * returns a typed response, or the raw Response when the body isn't JSON.\n * Untyped endpoints resolve to `unknown`.\n */\ntype ApiResult<Handler> = Handler extends (event: any) => any\n\t? Awaited<ReturnType<Handler>> extends TypedResponse<infer Body>\n\t\t? Body | Response\n\t\t: unknown\n\t: never;\n\n/**\n * The body a client call accepts: the endpoint event's annotated JSON body\n * type (from `ServerLoadEvent<Route, Body>`), or a loose value when the\n * handler doesn't annotate one.\n */\ntype ApiRequestBody<Handler> = Handler extends (event: infer Event) => any\n\t? Event extends { json: () => Promise<infer Body> }\n\t\t? unknown extends Body\n\t\t\t? BodyInit | object\n\t\t\t: Body\n\t\t: BodyInit | object\n\t: BodyInit | object;\n\n/** The callable methods of an endpoint, one per handler it defines. */\nexport type ApiMethods<Endpoint extends object> = {\n\t[\n\t\tMethod in string & keyof Endpoint as NonNullable<Endpoint[Method]> extends (event: any) => any\n\t\t\t? Method\n\t\t\t: never\n\t]: (\n\t\tbody?: ApiRequestBody<NonNullable<Endpoint[Method]>>,\n\t\tinit?: RequestInit,\n\t) => Promise<ApiResult<NonNullable<Endpoint[Method]>>>;\n};\n\nconst METHODS = [\"get\", \"post\", \"patch\", \"put\", \"del\", \"options\", \"head\"] as const;\n\n/**\n * Creates typed client callers for a `+server` endpoint, e.g.\n *\n * ```ts\n * import type endpoint from \"@/routes/api/posts/[id]/+server\";\n * const post = makeApi<\"/api/posts/[id]\", typeof endpoint>(\"/api/posts/[id]\");\n * const data = await post.get(); // typed from the endpoint's responses\n * ```\n *\n * Method names match the endpoint's handlers (`del` for DELETE). Params are\n * filled in and URI-encoded from the route path, and JSON responses are\n * parsed; non-JSON responses resolve to the raw Response. Failed requests\n * (non-2xx) throw.\n *\n * @param path The endpoint's route path\n * @param params The route params, required if the path has dynamic segments\n * @returns An object with one caller per handler defined by the endpoint\n */\nexport default function makeApi<\n\tRoute extends string,\n\tEndpoint extends object,\n\tParams extends ParseRouteParams<Route> = ParseRouteParams<Route>,\n>(\n\tpath: Route,\n\t// NOTE: The rest args conditional must stay INLINE — routing it through a\n\t// type alias defeats `Params` inference, and excess keys stop being checked\n\t...args: string extends Route\n\t\t? [params?: Record<string, string>]\n\t\t: keyof ParseRouteParams<Route> extends never\n\t\t\t? []\n\t\t\t: [params: Params & ExactRouteParams<Params, ParseRouteParams<Route>>]\n): ApiMethods<Endpoint> {\n\tconst url = route(path as string, ...(args as [params?: Record<string, string>]));\n\tconst api: Record<string, (body?: unknown, init?: RequestInit) => Promise<unknown>> = {};\n\tfor (const method of METHODS) {\n\t\tapi[method] = (body, init) => request(method, url, body, init);\n\t}\n\treturn api as ApiMethods<Endpoint>;\n}\n\nasync function request(\n\tmethod: string,\n\turl: string,\n\tbody: unknown,\n\tinit?: RequestInit,\n): Promise<unknown> {\n\t// `del` is the endpoint handler name for DELETE requests\n\tconst httpMethod = method === \"del\" ? \"delete\" : method;\n\tconst options: RequestInit = { method: httpMethod.toUpperCase(), ...init };\n\tif (body !== undefined && options.body === undefined && method !== \"get\" && method !== \"head\") {\n\t\tif (typeof body === \"object\") {\n\t\t\toptions.headers ??= { \"Content-Type\": \"application/json\" };\n\t\t\toptions.body = JSON.stringify(body);\n\t\t} else {\n\t\t\toptions.body = body as BodyInit;\n\t\t}\n\t}\n\tconst response = await fetch(url, options);\n\tif (!response.ok) {\n\t\tthrow new Error(`API ${method.toUpperCase()} ${url} failed: ${response.status}`);\n\t}\n\tconst contentType = response.headers.get(\"Content-Type\") ?? \"\";\n\treturn contentType.includes(\"application/json\") ? await response.json() : response;\n}\n"],"mappings":";;;;;;;;;;;;;;AAcA,SAAwB,MAIvB,MACA,GAAG,MAKM;CACT,MAAM,SAAW,KAAmB,MAAM,CAAC;CAC3C,MAAM,SAAS,KAAK,QACnB,2BACC,GAAG,OAA2B,SAAiB;EAC/C,MAAM,QAAQ,OAAO;EACrB,IAAI,UAAU,KAAA,GACb,MAAM,IAAI,MAAM,kBAAkB,KAAK,eAAe,KAAK,EAAE;EAE9D,OAAO,QACJ,MACC,MAAM,GAAG,CAAC,CACV,KAAK,YAAY,mBAAmB,OAAO,CAAC,CAAC,CAC7C,KAAK,GAAG,IACT,mBAAmB,OAAO,KAAK,CAAC;CACpC,CACD;CAEA,OAAO,cAAc,QAAQ,YAAY,CAAC;AAC3C;;;ACHA,MAAM,UAAU;CAAC;CAAO;CAAQ;CAAS;CAAO;CAAO;CAAW;AAAM;;;;;;;;;;;;;;;;;;;AAoBxE,SAAwB,QAKvB,MAGA,GAAG,MAKoB;CACvB,MAAM,MAAM,MAAM,MAAgB,GAAI,IAA0C;CAChF,MAAM,MAAgF,CAAC;CACvF,KAAK,MAAM,UAAU,SACpB,IAAI,WAAW,MAAM,SAAS,QAAQ,QAAQ,KAAK,MAAM,IAAI;CAE9D,OAAO;AACR;AAEA,eAAe,QACd,QACA,KACA,MACA,MACmB;CAGnB,MAAM,UAAuB;EAAE,SADZ,WAAW,QAAQ,WAAW,OAAA,CACC,YAAY;EAAG,GAAG;CAAK;CACzE,IAAI,SAAS,KAAA,KAAa,QAAQ,SAAS,KAAA,KAAa,WAAW,SAAS,WAAW,QAAQ;EAC9F,IAAI,OAAO,SAAS,UAAU;GAC7B,QAAQ,YAAY,EAAE,gBAAgB,mBAAmB;GACzD,QAAQ,OAAO,KAAK,UAAU,IAAI;EACnC,OACC,QAAQ,OAAO;CAEjB;CACA,MAAM,WAAW,MAAM,MAAM,KAAK,OAAO;CACzC,IAAI,CAAC,SAAS,IACb,MAAM,IAAI,MAAM,OAAO,OAAO,YAAY,EAAE,GAAG,IAAI,WAAW,SAAS,QAAQ;CAGhF,QADoB,SAAS,QAAQ,IAAI,cAAc,KAAK,GAAA,CACzC,SAAS,kBAAkB,IAAI,MAAM,SAAS,KAAK,IAAI;AAC3E"}
|
package/dist/openapi.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { v as SitePlugin } from "./Site-CTADVh7d.mjs";
|
|
2
1
|
import { t as StandardSchemaV1 } from "./StandardSchema-D28L8_zZ.mjs";
|
|
2
|
+
import { h as SitePlugin } from "./Site-CQ29Opvm.mjs";
|
|
3
3
|
//#region src/openapi/types.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* A JSON Schema object, as produced by a schema library's converter (e.g.
|
package/dist/run.d.mts
CHANGED
package/dist/run.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.mts","names":[],"sources":["../src/run/run.ts","../src/run/runBuild.ts","../src/run/runDev.ts","../src/run/runOpenApi.ts","../src/run/runPreview.ts"],"mappings":";;;iBAyD8B,IAC7B,gBACA,oCACE;;;
|
|
1
|
+
{"version":3,"file":"run.d.mts","names":[],"sources":["../src/run/run.ts","../src/run/runBuild.ts","../src/run/runDev.ts","../src/run/runOpenApi.ts","../src/run/runPreview.ts"],"mappings":";;;iBAyD8B,IAC7B,gBACA,oCACE;;;iBC1C2B,SAAS,MAAM,OAAO;;;iBCYtB,OAAO,MAAM,OAAO;;;;;;;;iBChBpB,WAC7B,MAAM,MACN,MAAM,eACN,mBACE;;;iBCR2B,WAAW,MAAM,OAAO"}
|
package/dist/run.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as runDev, i as runPreview, o as runBuild, r as run, t as runOpenApi } from "./runOpenApi-
|
|
1
|
+
import { a as runDev, i as runPreview, o as runBuild, r as run, t as runOpenApi } from "./runOpenApi-DWfPiAv0.mjs";
|
|
2
2
|
export { run, runBuild, runDev, runOpenApi, runPreview };
|
|
@@ -1,15 +1,18 @@
|
|
|
1
|
+
import { i as normalizeOrigin, r as normalizeBasePath } from "./basePath-DQnbHc7o.mjs";
|
|
1
2
|
import { i as buildOpenApiDocument, t as OPEN_API_STATE_KEY } from "./plugin-D8Www11B.mjs";
|
|
2
3
|
import { r as serverError } from "./response-CuNPcxsY.mjs";
|
|
3
|
-
import { t as
|
|
4
|
+
import { t as ServerEvent } from "./ServerEvent-Bh9cEpro.mjs";
|
|
5
|
+
import { n as sitemapXml } from "./sitemap-4imxJVrG.mjs";
|
|
6
|
+
import { t as Server } from "./Server-CSti9cvM.mjs";
|
|
4
7
|
import { t as flattenHeaders } from "./flattenHeaders-DM5qsSqO.mjs";
|
|
5
8
|
import { createRequire, isBuiltin } from "node:module";
|
|
6
9
|
import fs, { existsSync, promises, readFileSync, readdirSync, realpathSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
7
10
|
import fpath from "node:path";
|
|
8
11
|
import { build, createServer, defineConfig } from "vite";
|
|
9
12
|
import torpor from "@torpor/unplugin/vite";
|
|
10
|
-
import { parse } from "@torpor/view/compile";
|
|
11
|
-
import estorpor from "@torpor/unplugin/esbuild";
|
|
12
13
|
import { configDotenv } from "dotenv";
|
|
14
|
+
import { parse } from "@torpor/view/compile";
|
|
15
|
+
import { pathToFileURL } from "node:url";
|
|
13
16
|
import { Readable } from "node:stream";
|
|
14
17
|
import { createHash } from "node:crypto";
|
|
15
18
|
import { writeFile } from "node:fs/promises";
|
|
@@ -526,20 +529,44 @@ function manifest(site, server = false) {
|
|
|
526
529
|
load(id, viteOptions) {
|
|
527
530
|
if (id === moduleId) {
|
|
528
531
|
let serverRequest = server && !!viteOptions?.ssr;
|
|
532
|
+
const base = normalizeBasePath(site.basePath);
|
|
533
|
+
if (site.sitemap && !site.origin) throw new Error(`site.sitemap requires site.origin to be set (it is the host of the urls)`);
|
|
534
|
+
if (site.origin) normalizeOrigin(site.origin);
|
|
535
|
+
if (typeof site.sitemap === "object" && site.sitemap !== null) {
|
|
536
|
+
if (site.sitemap.path !== void 0 && !site.sitemap.path.startsWith("/")) throw new Error(`The sitemap path must be an absolute path (e.g. "/sitemap.xml"), got "${site.sitemap.path}"`);
|
|
537
|
+
} else if (typeof site.sitemap === "string" && !site.sitemap.startsWith("/")) throw new Error(`The sitemap path must be an absolute path (e.g. "/sitemap.xml"), got "${site.sitemap}"`);
|
|
529
538
|
const hasInline = site.routes.some((r) => r.endPoint && !r.file);
|
|
530
539
|
const hasPlugins = site.plugins.length > 0;
|
|
531
540
|
const hasOpenApi = site.pluginState.has(OPEN_API_STATE_KEY);
|
|
532
|
-
const
|
|
541
|
+
const hasEnv = !!site.env;
|
|
542
|
+
const hasMiddleware = site.middleware.length > 0;
|
|
543
|
+
const hasRuntimeSitemap = typeof site.sitemap === "object" && site.sitemap !== null;
|
|
544
|
+
const configImport = hasInline || hasPlugins || hasOpenApi || hasEnv || hasRuntimeSitemap || hasMiddleware ? serverRequest && site.configFile ? `import __site from ${JSON.stringify(site.configFile)};` : "" : "";
|
|
545
|
+
const envGlue = serverRequest && hasEnv && site.configFile ? `import { setEnvSchema } from "@torpor/build/env";
|
|
546
|
+
setEnvSchema(__site.env);` : "";
|
|
533
547
|
const pluginLoop = serverRequest && hasPlugins ? `for (const __plugin of __site.plugins ?? []) { await __plugin(__site); }` : "";
|
|
534
548
|
const openApiOptions = site.pluginState.get(OPEN_API_STATE_KEY);
|
|
535
549
|
const openApiGlue = serverRequest && openApiOptions && site.configFile ? openApiGlueCode(site, openApiOptions) : "";
|
|
536
550
|
const openApiRoute = serverRequest && openApiOptions ? `{ path: ${JSON.stringify(openApiOptions.path)}, type: 3, endPoint: () => Promise.resolve({ default: { get: __openApiGet } }), subFolder: undefined },` : "";
|
|
551
|
+
const sitemapOptions = typeof site.sitemap === "object" && site.sitemap !== null ? site.sitemap : void 0;
|
|
552
|
+
const sitemapGlue = serverRequest && sitemapOptions && site.configFile ? `
|
|
553
|
+
import { sitemapResponse } from "@torpor/build/discovery";
|
|
554
|
+
async function __sitemapGet(event) {
|
|
555
|
+
return await sitemapResponse(__site.sitemap, ${JSON.stringify(base)}, event);
|
|
556
|
+
}
|
|
557
|
+
` : "";
|
|
558
|
+
const sitemapRoute = serverRequest && sitemapOptions ? `{ path: ${JSON.stringify(sitemapOptions.path ?? "/sitemap.xml")}, type: 3, endPoint: () => Promise.resolve({ default: { get: __sitemapGet } }), subFolder: undefined },` : "";
|
|
537
559
|
return `
|
|
538
560
|
${!serverRequest ? "const load = { default: { load: true } };" : ""}
|
|
539
561
|
${configImport}
|
|
562
|
+
${envGlue}
|
|
540
563
|
${pluginLoop}
|
|
541
564
|
${openApiGlue}
|
|
565
|
+
${sitemapGlue}
|
|
542
566
|
export default {
|
|
567
|
+
base: ${JSON.stringify(base)},
|
|
568
|
+
viewTransitions: ${!!site.viewTransitions},
|
|
569
|
+
${serverRequest && hasMiddleware ? "middleware: __site.middleware ?? []," : ""}
|
|
543
570
|
routes: [
|
|
544
571
|
${site.routes.map((r) => {
|
|
545
572
|
let endPoint;
|
|
@@ -556,6 +583,7 @@ export default {
|
|
|
556
583
|
return `{ path: "${r.path}", type: ${r.type}, endPoint: ${endPoint}, subFolder: ${sub} },`;
|
|
557
584
|
}).join("\n ")}
|
|
558
585
|
${openApiRoute}
|
|
586
|
+
${sitemapRoute}
|
|
559
587
|
],
|
|
560
588
|
};
|
|
561
589
|
`;
|
|
@@ -607,8 +635,10 @@ async function __openApiGet() {
|
|
|
607
635
|
* leave raw `.torp` imports that neither Node nor workerd can load.
|
|
608
636
|
*
|
|
609
637
|
* A package is a torpor package when it declares a `torpor` entry in its
|
|
610
|
-
* package.json
|
|
611
|
-
*
|
|
638
|
+
* package.json, or when any of its export targets is a `.torp` file. The
|
|
639
|
+
* entry is usually a path (`"torpor": "./lib/index.js"`, mirroring the
|
|
640
|
+
* `svelte` field convention), but packages that ship compiled output can
|
|
641
|
+
* just mark themselves with `"torpor": true`.
|
|
612
642
|
*
|
|
613
643
|
* @param siteRoot The root folder of the site
|
|
614
644
|
* @returns The names of the torpor packages found (may be empty)
|
|
@@ -662,7 +692,7 @@ function collectTorporPackage(name, require, packages, visited) {
|
|
|
662
692
|
for (const depName of dependencyNames$1(pkg)) collectTorporPackage(depName, depRequire, packages, visited);
|
|
663
693
|
}
|
|
664
694
|
function isTorporPackage(pkg) {
|
|
665
|
-
if (typeof pkg.torpor === "string") return true;
|
|
695
|
+
if (pkg.torpor === true || typeof pkg.torpor === "string") return true;
|
|
666
696
|
return exportsIncludeTorp(pkg.exports);
|
|
667
697
|
}
|
|
668
698
|
function exportsIncludeTorp(exports) {
|
|
@@ -731,8 +761,202 @@ function siteEntryPaths(siteRoot, sourceMode) {
|
|
|
731
761
|
};
|
|
732
762
|
}
|
|
733
763
|
//#endregion
|
|
764
|
+
//#region src/run/prepareTemplate.ts
|
|
765
|
+
function prepareTemplate(template, clientScript, clientDevScript) {
|
|
766
|
+
let result = template;
|
|
767
|
+
let headStart = result.indexOf("</head>");
|
|
768
|
+
if (headStart === -1) throw new Error(`Couldn't find <head> end tag`);
|
|
769
|
+
result = result.substring(0, headStart) + "%COMPONENT_HEAD%" + result.substring(headStart);
|
|
770
|
+
let divMatchStart = regexIndexOf(result, /<div\s+id=("app"|'app'|app)[\s>]/);
|
|
771
|
+
if (divMatchStart === -1) throw new Error(`Couldn't find <div id="app"></div>`);
|
|
772
|
+
let bodyStart = result.indexOf(">", divMatchStart) + 1;
|
|
773
|
+
let bodyEnd = result.indexOf("</div>", bodyStart);
|
|
774
|
+
if (bodyEnd === -1) throw new Error(`Couldn't find <div id="app"></div>`);
|
|
775
|
+
result = result.substring(0, bodyStart) + "%COMPONENT_BODY%" + result.substring(bodyEnd);
|
|
776
|
+
if (clientDevScript) result += `<script type="module" src="${clientDevScript}"><\/script>`;
|
|
777
|
+
result += `<script type="module" src="${clientScript}"><\/script>`;
|
|
778
|
+
return result;
|
|
779
|
+
}
|
|
780
|
+
function regexIndexOf(string, regex, position) {
|
|
781
|
+
var indexOf = string.substring(position || 0).search(regex);
|
|
782
|
+
return indexOf >= 0 ? indexOf + (position || 0) : indexOf;
|
|
783
|
+
}
|
|
784
|
+
//#endregion
|
|
785
|
+
//#region src/run/runPrerender.ts
|
|
786
|
+
/**
|
|
787
|
+
* Resolves which page paths should be prerendered, from the routes of a
|
|
788
|
+
* built site, testing in order:
|
|
789
|
+
*
|
|
790
|
+
* 1. the page endpoint's own `prerender` flag, or the inferred-params one
|
|
791
|
+
* on the sibling `+page.server` endpoint
|
|
792
|
+
* 2. the nearest ancestor `_layout` endpoint's flag
|
|
793
|
+
* 3. the site-level `site.prerender` setting: a boolean default, or a
|
|
794
|
+
* longest-match map of exact paths (`/about`) and prefixes (`/blog/**`)
|
|
795
|
+
*
|
|
796
|
+
* @param routes The site's routes, including layouts and page server
|
|
797
|
+
* endpoints, with lazy endpoint loaders
|
|
798
|
+
* @param siteFlag The `site.prerender` value
|
|
799
|
+
* @returns The paths to prerender, with route params for dynamic ones
|
|
800
|
+
*/
|
|
801
|
+
async function resolvePrerenderPaths(routes, siteFlag) {
|
|
802
|
+
const result = [];
|
|
803
|
+
for (const route of routes.filter((r) => r.type === 0)) {
|
|
804
|
+
const base = route.path.replace(/\/$/, "");
|
|
805
|
+
const pageFlag = await endpointFlag(route.endPoint);
|
|
806
|
+
const serverRoute = routes.find((r) => r.type === 1 && r.path === `${base}/~server`);
|
|
807
|
+
const serverFlag = serverRoute ? await endpointFlag(serverRoute.endPoint) : void 0;
|
|
808
|
+
const entries = paramsEntriesOf(pageFlag) ?? paramsEntriesOf(serverFlag);
|
|
809
|
+
if (!((pageFlag === void 0 ? serverFlag === void 0 ? void 0 : Boolean(serverFlag) : Boolean(pageFlag)) ?? await nearestLayoutFlag(routes, route) ?? siteResolve(route.path, siteFlag))) continue;
|
|
810
|
+
if (/\[[^\]]+\]/.test(route.path)) {
|
|
811
|
+
if (!entries?.length) throw new Error(`The route "${route.path}" is marked for prerendering, but it has dynamic segments and no params entries were declared. Set \`prerender: { params: [...] }\` on its endpoint.`);
|
|
812
|
+
for (const entry of entries) result.push({
|
|
813
|
+
path: expandPath(route.path, entry),
|
|
814
|
+
params: toStringParams(entry)
|
|
815
|
+
});
|
|
816
|
+
} else result.push({
|
|
817
|
+
path: route.path || "/",
|
|
818
|
+
params: void 0
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
return result;
|
|
822
|
+
}
|
|
823
|
+
function paramsEntriesOf(flag) {
|
|
824
|
+
return typeof flag === "object" && flag !== null ? flag.params : void 0;
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Renders the prerenderable routes to static HTML files in the client
|
|
828
|
+
* output, plus the `_error` page as `404.html` when the site has one.
|
|
829
|
+
*
|
|
830
|
+
* Called by `tb build` after the client and server builds, when the built
|
|
831
|
+
* server entry (with its routes) can be imported directly, and before the
|
|
832
|
+
* adapter's postbuild -- so a prerendered site ships as plain files and
|
|
833
|
+
* any static host can serve it.
|
|
834
|
+
*
|
|
835
|
+
* @param site The loaded site config
|
|
836
|
+
* @returns The number of HTML files written
|
|
837
|
+
*/
|
|
838
|
+
async function runPrerender(site) {
|
|
839
|
+
const serverFolder = fpath.join(site.root, "dist", "server");
|
|
840
|
+
const clientFolder = fpath.join(site.root, "dist", "client");
|
|
841
|
+
const serverEntryPath = fpath.join(serverFolder, "serverEntry.js");
|
|
842
|
+
const serverEntry = await import(pathToFileURL(serverEntryPath).href);
|
|
843
|
+
const routes = serverEntry.router.routes.map((r) => r.handler);
|
|
844
|
+
let template;
|
|
845
|
+
const siteHtml = fpath.join(clientFolder, "site.html");
|
|
846
|
+
if (existsSync(siteHtml)) {
|
|
847
|
+
const assets = fpath.join(clientFolder, "assets");
|
|
848
|
+
let clientScript = (await promises.readdir(assets)).find((f) => f.startsWith("clientEntry-"));
|
|
849
|
+
if (!clientScript) throw new Error("clientEntry.js not found");
|
|
850
|
+
template = prepareTemplate(await promises.readFile(siteHtml, "utf-8"), `/assets/${clientScript}`);
|
|
851
|
+
}
|
|
852
|
+
const entries = await resolvePrerenderPaths(routes, site.prerender);
|
|
853
|
+
globalThis.adapter ??= { env: process.env };
|
|
854
|
+
const failures = [];
|
|
855
|
+
let written = 0;
|
|
856
|
+
for (const entry of entries) {
|
|
857
|
+
const url = `http://torpor.build${entry.path}`;
|
|
858
|
+
const request = new Request(url);
|
|
859
|
+
const serverEvent = new ServerEvent(request, entry.params, new URL(url));
|
|
860
|
+
const response = await serverEntry.load(serverEvent, template);
|
|
861
|
+
if (response.status < 200 || response.status > 299) {
|
|
862
|
+
failures.push(`${entry.path} (status ${await responseStatus(response)})`);
|
|
863
|
+
continue;
|
|
864
|
+
}
|
|
865
|
+
if (!response.headers.get("Content-Type")?.includes("text/html")) {
|
|
866
|
+
failures.push(`${entry.path} (not an HTML response)`);
|
|
867
|
+
continue;
|
|
868
|
+
}
|
|
869
|
+
await writeFileForPath(clientFolder, entry.path, await response.text());
|
|
870
|
+
written++;
|
|
871
|
+
}
|
|
872
|
+
if (routes.find((r) => r.type === 8) && template !== void 0) {
|
|
873
|
+
const url = "http://torpor.build/_error?status=404";
|
|
874
|
+
const serverEvent = new ServerEvent(new Request(url), void 0, new URL(url));
|
|
875
|
+
const response = await serverEntry.load(serverEvent, template);
|
|
876
|
+
if (response.ok && response.headers.get("Content-Type")?.includes("text/html")) await promises.writeFile(fpath.join(clientFolder, "404.html"), await response.text());
|
|
877
|
+
else failures.push(`/_error (status ${await responseStatus(response)})`);
|
|
878
|
+
}
|
|
879
|
+
if (failures.length) throw new Error(`Prerendering failed for ${failures.length} route${failures.length === 1 ? "" : "s"}:\n` + failures.map((f) => ` - ${f}`).join("\n"));
|
|
880
|
+
if (site.sitemap && site.origin && entries.length > 0) {
|
|
881
|
+
const base = site.basePath;
|
|
882
|
+
const sitemap = sitemapXml(site.origin, base, entries.map((entry) => entry.path));
|
|
883
|
+
await promises.writeFile(fpath.join(clientFolder, typeof site.sitemap === "string" ? site.sitemap.slice(1) : "sitemap.xml"), sitemap);
|
|
884
|
+
}
|
|
885
|
+
return written;
|
|
886
|
+
}
|
|
887
|
+
/**
|
|
888
|
+
* Writes an HTML file for a route path, e.g. `/` into `index.html` and
|
|
889
|
+
* `/about` into `about/index.html`. Guards against route param values
|
|
890
|
+
* escaping the client folder.
|
|
891
|
+
*/
|
|
892
|
+
async function writeFileForPath(clientFolder, routePath, html) {
|
|
893
|
+
const rel = routePath === "/" ? "index.html" : routePath.slice(1) + "/index.html";
|
|
894
|
+
const out = fpath.resolve(clientFolder, rel);
|
|
895
|
+
const relSafe = fpath.relative(clientFolder, out);
|
|
896
|
+
if (relSafe.startsWith("..") || fpath.isAbsolute(relSafe)) throw new Error(`The prerendered path "${routePath}" escaped ${clientFolder}`);
|
|
897
|
+
await promises.mkdir(fpath.dirname(out), { recursive: true });
|
|
898
|
+
await promises.writeFile(out, html);
|
|
899
|
+
}
|
|
900
|
+
async function endpointFlag(loader) {
|
|
901
|
+
return ((await loader())?.default)?.prerender;
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* The nearest ancestor `_layout`'s flag for a page path, or undefined when
|
|
905
|
+
* no layout sets one. Layouts apply within the same subFolder.
|
|
906
|
+
*/
|
|
907
|
+
async function nearestLayoutFlag(routes, route) {
|
|
908
|
+
let flag;
|
|
909
|
+
let flagLength = -1;
|
|
910
|
+
for (const candidate of routes) {
|
|
911
|
+
if (candidate.type !== 4 || candidate.subFolder !== route.subFolder) continue;
|
|
912
|
+
const base = candidate.path.replace(/\/_layout$/, "");
|
|
913
|
+
if (!(base === "" || route.path === base || route.path.startsWith(base + "/"))) continue;
|
|
914
|
+
const flagOfLayout = await endpointFlag(candidate.endPoint);
|
|
915
|
+
if (flagOfLayout !== void 0 && base.length > flagLength) {
|
|
916
|
+
flag = flagOfLayout;
|
|
917
|
+
flagLength = base.length;
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
return flag;
|
|
921
|
+
}
|
|
922
|
+
function siteResolve(routePath, siteFlag) {
|
|
923
|
+
if (siteFlag === void 0) return void 0;
|
|
924
|
+
if (typeof siteFlag === "boolean") return siteFlag;
|
|
925
|
+
let flag;
|
|
926
|
+
let flagLength = -1;
|
|
927
|
+
for (const [pattern, value] of Object.entries(siteFlag)) {
|
|
928
|
+
const base = pattern.replace(/\/\*\*$/, "");
|
|
929
|
+
const isPrefixMatch = base !== pattern && (routePath === base || routePath.startsWith(base + "/"));
|
|
930
|
+
if ((pattern === routePath || isPrefixMatch) && pattern.length > flagLength) {
|
|
931
|
+
flag = value;
|
|
932
|
+
flagLength = pattern.length;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
return flag;
|
|
936
|
+
}
|
|
937
|
+
function toStringParams(entry) {
|
|
938
|
+
const result = {};
|
|
939
|
+
for (const [key, value] of Object.entries(entry)) result[key] = String(value);
|
|
940
|
+
return result;
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
943
|
+
* Fills a route path's `[param]` and `[...rest]` segments with the values
|
|
944
|
+
* from a params entry.
|
|
945
|
+
*/
|
|
946
|
+
function expandPath(routePath, entry) {
|
|
947
|
+
return routePath.replace(/\[([^\]]+)\]/g, (_match, name) => {
|
|
948
|
+
const key = name.replace(/^\.\.\./, "");
|
|
949
|
+
if (!(key in entry)) throw new Error(`Missing param "${key}" for the route "${routePath}" (prerendering)`);
|
|
950
|
+
return String(entry[key]);
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
async function responseStatus(response) {
|
|
954
|
+
return `${response.status}${response.status === 303 ? " redirect" : ""}`;
|
|
955
|
+
}
|
|
956
|
+
//#endregion
|
|
734
957
|
//#region src/run/runBuild.ts
|
|
735
958
|
async function runBuild(site) {
|
|
959
|
+
configDotenv();
|
|
736
960
|
let errorCount = reportRouteIssues(checkRoutes(site));
|
|
737
961
|
errorCount += reportRouteIssues(checkApiCalls(site));
|
|
738
962
|
errorCount += reportRouteIssues(checkLayoutSlots(site));
|
|
@@ -758,6 +982,8 @@ async function runBuild(site) {
|
|
|
758
982
|
];
|
|
759
983
|
clientConfig.build ??= {};
|
|
760
984
|
clientConfig.build.outDir = clientFolder;
|
|
985
|
+
const publicDir = fpath.resolve(site.root, "src/public");
|
|
986
|
+
if (existsSync(publicDir)) clientConfig.publicDir = publicDir;
|
|
761
987
|
clientConfig.build.rollupOptions ??= {};
|
|
762
988
|
clientConfig.build.rollupOptions.input = [
|
|
763
989
|
...hasSiteHtml ? [siteHtml] : [],
|
|
@@ -791,6 +1017,8 @@ async function runBuild(site) {
|
|
|
791
1017
|
await promises.rename(fpath.join(clientFolder, "src", "site.html"), fpath.join(clientFolder, "site.html"));
|
|
792
1018
|
await promises.rm(fpath.join(clientFolder, "src"), { recursive: true });
|
|
793
1019
|
}
|
|
1020
|
+
const prerendered = await runPrerender(site);
|
|
1021
|
+
if (prerendered > 0) console.log(`[torpor] Prerendered ${prerendered} route${prerendered === 1 ? "" : "s"}`);
|
|
794
1022
|
if (site.adapter.postbuild) await site.adapter.postbuild(site);
|
|
795
1023
|
}
|
|
796
1024
|
function asAliasArray$1(alias) {
|
|
@@ -802,27 +1030,6 @@ function asAliasArray$1(alias) {
|
|
|
802
1030
|
}));
|
|
803
1031
|
}
|
|
804
1032
|
//#endregion
|
|
805
|
-
//#region src/run/prepareTemplate.ts
|
|
806
|
-
function prepareTemplate(template, clientScript, clientDevScript) {
|
|
807
|
-
let result = template;
|
|
808
|
-
let headStart = result.indexOf("</head>");
|
|
809
|
-
if (headStart === -1) throw new Error(`Couldn't find <head> end tag`);
|
|
810
|
-
result = result.substring(0, headStart) + "%COMPONENT_HEAD%" + result.substring(headStart);
|
|
811
|
-
let divMatchStart = regexIndexOf(result, /<div\s+id=("app"|'app'|app)[\s>]/);
|
|
812
|
-
if (divMatchStart === -1) throw new Error(`Couldn't find <div id="app"></div>`);
|
|
813
|
-
let bodyStart = result.indexOf(">", divMatchStart) + 1;
|
|
814
|
-
let bodyEnd = result.indexOf("</div>", bodyStart);
|
|
815
|
-
if (bodyEnd === -1) throw new Error(`Couldn't find <div id="app"></div>`);
|
|
816
|
-
result = result.substring(0, bodyStart) + "%COMPONENT_BODY%" + result.substring(bodyEnd);
|
|
817
|
-
if (clientDevScript) result += `<script type="module" src="${clientDevScript}"><\/script>`;
|
|
818
|
-
result += `<script type="module" src="${clientScript}"><\/script>`;
|
|
819
|
-
return result;
|
|
820
|
-
}
|
|
821
|
-
function regexIndexOf(string, regex, position) {
|
|
822
|
-
var indexOf = string.substring(position || 0).search(regex);
|
|
823
|
-
return indexOf >= 0 ? indexOf + (position || 0) : indexOf;
|
|
824
|
-
}
|
|
825
|
-
//#endregion
|
|
826
1033
|
//#region src/run/devPlugin.ts
|
|
827
1034
|
/**
|
|
828
1035
|
* The default Node-runtime dev plugin. Reads `src/site.html`, sets up a torpor
|
|
@@ -1111,6 +1318,8 @@ async function runDev(site) {
|
|
|
1111
1318
|
config.server ??= {};
|
|
1112
1319
|
config.server.host ??= "localhost";
|
|
1113
1320
|
config.server.port ??= 7059;
|
|
1321
|
+
const publicDir = fpath.resolve(site.root, "src/public");
|
|
1322
|
+
if (existsSync(publicDir)) config.publicDir = publicDir;
|
|
1114
1323
|
config.resolve ??= {};
|
|
1115
1324
|
config.resolve.tsconfigPaths ??= true;
|
|
1116
1325
|
const sourceMode = detectSourceMode(site.root);
|
|
@@ -1131,7 +1340,7 @@ async function runDev(site) {
|
|
|
1131
1340
|
config.optimizeDeps.extensions ??= [];
|
|
1132
1341
|
config.optimizeDeps.extensions.push(".torp");
|
|
1133
1342
|
config.optimizeDeps.rolldownOptions ??= {};
|
|
1134
|
-
config.optimizeDeps.rolldownOptions.plugins ??= [
|
|
1343
|
+
config.optimizeDeps.rolldownOptions.plugins ??= [torpor()];
|
|
1135
1344
|
addTorporPackageConfig(site.root, config);
|
|
1136
1345
|
clearStaleDepCache(site.root, (message) => console.log(`\n${message}`));
|
|
1137
1346
|
reportStaleTorpCopies(site.root, (message) => console.log(`\n${message}`));
|
|
@@ -1416,4 +1625,4 @@ async function runOpenApi(site, vite, outFile = "openapi.json") {
|
|
|
1416
1625
|
//#endregion
|
|
1417
1626
|
export { runDev as a, runPreview as i, loadSite as n, runBuild as o, run as r, runOpenApi as t };
|
|
1418
1627
|
|
|
1419
|
-
//# sourceMappingURL=runOpenApi-
|
|
1628
|
+
//# sourceMappingURL=runOpenApi-DWfPiAv0.mjs.map
|