@torpor/build 1.0.0 → 1.0.2
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/{flattenHeaders-C_YYLdOq.mjs → Server-BnjLJleZ.mjs} +2 -12
- package/dist/Server-BnjLJleZ.mjs.map +1 -0
- package/dist/{Server-C_IKav_e.d.mts → Server-Cler2w5O.d.mts} +3 -48
- package/dist/Server-Cler2w5O.d.mts.map +1 -0
- package/dist/ServerEvent-CTx3Q-hk.d.mts +48 -0
- package/dist/ServerEvent-CTx3Q-hk.d.mts.map +1 -0
- package/dist/{Site-CMa153FA.d.mts → Site-CE0_o5Ci.d.mts} +3 -2
- package/dist/Site-CE0_o5Ci.d.mts.map +1 -0
- package/dist/bin/index.js +1 -1
- package/dist/clientEntry.d.mts +1 -0
- package/dist/clientEntry.mjs +59 -0
- package/dist/clientEntry.mjs.map +1 -0
- package/dist/clientEntryDev.d.mts +1 -0
- package/dist/clientEntryDev.mjs +7 -0
- package/dist/clientEntryDev.mjs.map +1 -0
- package/dist/dev.d.mts +3 -0
- package/dist/dev.mjs +3 -0
- package/dist/endpoint-EULRGCKA.mjs +108 -0
- package/dist/endpoint-EULRGCKA.mjs.map +1 -0
- package/dist/flattenHeaders-DM5qsSqO.mjs +13 -0
- package/dist/flattenHeaders-DM5qsSqO.mjs.map +1 -0
- package/dist/index.d.mts +1 -1
- package/dist/load-BntzvmwL.mjs +228 -0
- package/dist/load-BntzvmwL.mjs.map +1 -0
- package/dist/nav.mjs +1 -225
- 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-BUi1RH1l.mjs → runOpenApi-C8eI2Fh0.mjs} +54 -16
- package/dist/runOpenApi-C8eI2Fh0.mjs.map +1 -0
- package/dist/server/Server.d.mts +2 -0
- package/dist/server/Server.mjs +2 -0
- package/dist/server.d.mts +2 -1
- package/dist/server.d.mts.map +1 -1
- package/dist/server.mjs +2 -1
- package/dist/server.mjs.map +1 -1
- package/dist/serverEntry-1qY3wZig.mjs +351 -0
- package/dist/serverEntry-1qY3wZig.mjs.map +1 -0
- package/dist/serverEntry-BAO8m03V.d.mts +6 -0
- package/dist/serverEntry-BAO8m03V.d.mts.map +1 -0
- package/dist/serverEntry.d.mts +2 -0
- package/dist/serverEntry.mjs +2 -0
- package/dist/test.d.mts +2 -2
- package/dist/test.mjs +2 -104
- package/dist/test.mjs.map +1 -1
- package/package.json +9 -8
- package/dist/Server-C_IKav_e.d.mts.map +0 -1
- package/dist/Site-CMa153FA.d.mts.map +0 -1
- package/dist/flattenHeaders-C_YYLdOq.mjs.map +0 -1
- package/dist/runOpenApi-BUi1RH1l.mjs.map +0 -1
- package/src/bin/index.ts +0 -22
- package/src/dev.ts +0 -10
- package/src/form/formDataToRecord.ts +0 -23
- package/src/form/readForm.ts +0 -71
- package/src/form.ts +0 -4
- package/src/index.ts +0 -82
- package/src/nav/api.test-d.ts +0 -79
- package/src/nav/api.ts +0 -104
- package/src/nav/formSubmit.ts +0 -70
- package/src/nav/load.ts +0 -9
- package/src/nav/loadData.ts +0 -134
- package/src/nav/navigate.ts +0 -189
- package/src/nav/reload.ts +0 -9
- package/src/nav/route.ts +0 -38
- package/src/nav.ts +0 -6
- package/src/openapi/docsHtml.ts +0 -25
- package/src/openapi/document.ts +0 -154
- package/src/openapi/plugin.ts +0 -78
- package/src/openapi/types.ts +0 -82
- package/src/openapi.ts +0 -14
- package/src/response/TypedResponse.ts +0 -17
- package/src/response/badRequest.ts +0 -31
- package/src/response/created.ts +0 -31
- package/src/response/forbidden.ts +0 -21
- package/src/response/found.ts +0 -30
- package/src/response/methodNotAllowed.ts +0 -18
- package/src/response/movedPermanently.ts +0 -25
- package/src/response/notFound.ts +0 -24
- package/src/response/notModified.ts +0 -34
- package/src/response/ok.ts +0 -38
- package/src/response/permanentRedirect.ts +0 -30
- package/src/response/response.ts +0 -22
- package/src/response/seeOther.ts +0 -19
- package/src/response/serverError.ts +0 -24
- package/src/response/temporaryRedirect.ts +0 -31
- package/src/response/transfer.ts +0 -14
- package/src/response/unauthorized.ts +0 -21
- package/src/response/unprocessable.ts +0 -30
- package/src/response.ts +0 -37
- package/src/run/depCache.ts +0 -175
- package/src/run/devPlugin.ts +0 -128
- package/src/run/prepareTemplate.ts +0 -42
- package/src/run/run.ts +0 -86
- package/src/run/runBuild.ts +0 -128
- package/src/run/runDev.ts +0 -177
- package/src/run/runOpenApi.ts +0 -52
- package/src/run/runPreview.ts +0 -84
- package/src/run/staleTorpCopies.ts +0 -100
- package/src/run.ts +0 -7
- package/src/schema.ts +0 -7
- package/src/server/CookieHelper.ts +0 -46
- package/src/server/HeaderHelper.ts +0 -25
- package/src/server/Server.ts +0 -168
- package/src/server/ServerEvent.ts +0 -62
- package/src/server/connect/connectMiddleware.ts +0 -85
- package/src/server/connect/flattenHeaders.ts +0 -22
- package/src/server/connect/nodeMessageToNodeResponse.ts +0 -89
- package/src/server/connect/requestToNodeMessage.ts +0 -29
- package/src/server/contentType.ts +0 -85
- package/src/server/types/HttpMethod.ts +0 -12
- package/src/server/types/MiddlewareFunction.ts +0 -22
- package/src/server/types/ServerFunction.ts +0 -7
- package/src/server.ts +0 -7
- package/src/site/Router.ts +0 -175
- package/src/site/Site.ts +0 -388
- package/src/site/checkLayoutSlots.ts +0 -114
- package/src/site/checkRoutes.ts +0 -435
- package/src/site/clientEntry.ts +0 -121
- package/src/site/clientEntryDev.ts +0 -3
- package/src/site/defaultAdapter.ts +0 -11
- package/src/site/layoutSlots.ts +0 -53
- package/src/site/manifest.ts +0 -173
- package/src/site/serverEntry.ts +0 -648
- package/src/state/$page.ts +0 -25
- package/src/state/$serverPage.ts +0 -22
- package/src/state/client.ts +0 -15
- package/src/state.ts +0 -3
- package/src/test/runTest.ts +0 -538
- package/src/test.ts +0 -3
- package/src/types/Adapter.ts +0 -20
- package/src/types/ClientState.ts +0 -8
- package/src/types/InternalState.ts +0 -7
- package/src/types/Jsonify.ts +0 -20
- package/src/types/LayoutHandler.ts +0 -5
- package/src/types/LayoutPath.ts +0 -10
- package/src/types/ManifestRoute.ts +0 -6
- package/src/types/PageData.test-d.ts +0 -136
- package/src/types/PageData.ts +0 -35
- package/src/types/PageEndPoint.ts +0 -55
- package/src/types/PageForm.test-d.ts +0 -78
- package/src/types/PageForm.ts +0 -29
- package/src/types/PageLoadEvent.ts +0 -26
- package/src/types/PageLoadReturn.ts +0 -13
- package/src/types/PageProps.ts +0 -14
- package/src/types/PageServerAction.ts +0 -13
- package/src/types/PageServerEndPoint.test-d.ts +0 -105
- package/src/types/PageServerEndPoint.ts +0 -110
- package/src/types/PageServerLoad.ts +0 -15
- package/src/types/PageState.ts +0 -8
- package/src/types/ParseRouteParams.test-d.ts +0 -84
- package/src/types/ParseRouteParams.ts +0 -64
- package/src/types/Route.ts +0 -30
- package/src/types/RouteHandler.ts +0 -23
- package/src/types/RouteLayoutHandler.ts +0 -5
- package/src/types/RouteMatchResult.ts +0 -7
- package/src/types/RouteType.ts +0 -19
- package/src/types/ServerEndPoint.test-d.ts +0 -77
- package/src/types/ServerEndPoint.ts +0 -169
- package/src/types/ServerHook.ts +0 -18
- package/src/types/ServerLoadEvent.ts +0 -82
- package/src/types/ServerRequest.ts +0 -15
- package/src/types/SitePlugin.ts +0 -24
- package/src/types/StandardSchema.ts +0 -109
- package/src/utils/pathToRegex.ts +0 -17
- package/src/utils/pathTrie.ts +0 -182
- package/src/utils/searchParamsToRecord.ts +0 -18
- package/src/utils/torporPackages.ts +0 -150
- package/src/utils/tsconfigAliases.ts +0 -90
- package/src/validation/ValidationError.ts +0 -18
- package/src/validation/endpoint.ts +0 -83
- package/src/validation/validate.ts +0 -26
- package/src/vite-env.d.ts +0 -2
package/src/site/Site.ts
DELETED
|
@@ -1,388 +0,0 @@
|
|
|
1
|
-
import { promises as fs } from "node:fs";
|
|
2
|
-
import fpath from "node:path";
|
|
3
|
-
import { type Plugin, type UserConfig } from "vite";
|
|
4
|
-
import type Adapter from "../types/Adapter";
|
|
5
|
-
import type SitePlugin from "../types/SitePlugin";
|
|
6
|
-
import type { InlineEndPoint } from "../types/Route";
|
|
7
|
-
import type PageServerEndPoint from "../types/PageServerEndPoint";
|
|
8
|
-
import type Route from "../types/Route";
|
|
9
|
-
import type ServerEndPoint from "../types/ServerEndPoint";
|
|
10
|
-
import type ServerHook from "../types/ServerHook";
|
|
11
|
-
import {
|
|
12
|
-
ERROR_ROUTE,
|
|
13
|
-
HOOK_ROUTE,
|
|
14
|
-
HOOK_SERVER_ROUTE,
|
|
15
|
-
LAYOUT_ROUTE,
|
|
16
|
-
LAYOUT_SERVER_ROUTE,
|
|
17
|
-
PAGE_ROUTE,
|
|
18
|
-
PAGE_SERVER_ROUTE,
|
|
19
|
-
RouteType,
|
|
20
|
-
SERVER_ROUTE,
|
|
21
|
-
} from "../types/RouteType";
|
|
22
|
-
import defaultAdapter from "./defaultAdapter";
|
|
23
|
-
|
|
24
|
-
// 1. The user sets this up
|
|
25
|
-
// 2. It is passed into serverEntry and clientEntry through a Vite plugin
|
|
26
|
-
// (defined in manifest.ts)
|
|
27
|
-
// 3. In those files, it is used to build a Router
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* The Site class contains the information necessary to setup a @torpor/build
|
|
31
|
-
* site, including routes, plugins, and options.
|
|
32
|
-
*/
|
|
33
|
-
export default class Site {
|
|
34
|
-
root: string;
|
|
35
|
-
routes: Route[] = [];
|
|
36
|
-
/**
|
|
37
|
-
* Torpor site plugins, run when the site config is loaded (for dev and
|
|
38
|
-
* build) and when the server starts up. Use these to register extra
|
|
39
|
-
* routes (e.g. `openApi()` from `@torpor/build/openapi`) or add other
|
|
40
|
-
* framework-level functionality
|
|
41
|
-
*/
|
|
42
|
-
plugins: SitePlugin[] = [];
|
|
43
|
-
/**
|
|
44
|
-
* Vite plugins to add to the client and server builds
|
|
45
|
-
*/
|
|
46
|
-
vitePlugins: Plugin[] = [];
|
|
47
|
-
// Is default adapter a bad idea?
|
|
48
|
-
adapter: Adapter = defaultAdapter;
|
|
49
|
-
/**
|
|
50
|
-
* Extra inputs, as absolute file paths, that will be compiled with the
|
|
51
|
-
* output. What you do with them after that is up to you. File paths ending
|
|
52
|
-
* with server.ts/js will only be compiled with the server output
|
|
53
|
-
*/
|
|
54
|
-
inputs: string[] = [];
|
|
55
|
-
/**
|
|
56
|
-
* Vite config options to merge with the standard Torpor build options.
|
|
57
|
-
* `plugins` and `build.rollupOptions.input` will be overridden, so you
|
|
58
|
-
* should set `Site.vitePlugins` and `Site.inputs` instead. Other options may be
|
|
59
|
-
* overridden or incompatible, so experimentation may be required
|
|
60
|
-
*/
|
|
61
|
-
viteConfig?: UserConfig;
|
|
62
|
-
/**
|
|
63
|
-
* The path to the site.config file. Set automatically by the CLI; used by
|
|
64
|
-
* the manifest plugin to import inline endpoints for server builds.
|
|
65
|
-
*/
|
|
66
|
-
configFile?: string;
|
|
67
|
-
/**
|
|
68
|
-
* A state map for plugins to store their configuration and state on the
|
|
69
|
-
* site, so that framework internals (e.g. the manifest plugin) and other
|
|
70
|
-
* plugins can read them back:
|
|
71
|
-
*
|
|
72
|
-
* ```ts
|
|
73
|
-
* const myKey = Symbol.for("my-plugin");
|
|
74
|
-
* site.pluginState.set(myKey, options);
|
|
75
|
-
* ```
|
|
76
|
-
*
|
|
77
|
-
* Prefer `Symbol.for("...")` keys (or plain strings) over `Symbol()`,
|
|
78
|
-
* because the user's config file and framework internals can be loaded as
|
|
79
|
-
* separate module instances -- registry symbols are shared across
|
|
80
|
-
* instances, `Symbol()` ones are not.
|
|
81
|
-
*/
|
|
82
|
-
pluginState: Map<PropertyKey, unknown> = new Map();
|
|
83
|
-
/**
|
|
84
|
-
* Inline endpoints keyed by `"path:type"`. Populated by `addRoute` when
|
|
85
|
-
* the user passes an inline endpoint object instead of a file path.
|
|
86
|
-
*/
|
|
87
|
-
inlineEndPoints: Record<string, InlineEndPoint> = {};
|
|
88
|
-
|
|
89
|
-
constructor() {
|
|
90
|
-
this.root = process.cwd();
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Adds a folder of routes, based on the names of the files in the folder
|
|
95
|
-
* @param folder The folder containing the routes
|
|
96
|
-
* @param subFolder If a subFolder is set, all routes in the folder will be
|
|
97
|
-
* placed under the subFolder, and only hooks, layouts etc will be used if
|
|
98
|
-
* they have the same subFolder. This can be used to create e.g. an `api`
|
|
99
|
-
* set of routes with their own hook for authorization from various clients
|
|
100
|
-
*/
|
|
101
|
-
async addRouteFolder(folder: string, subFolder?: string): Promise<void> {
|
|
102
|
-
const routeFolder = fpath.join(this.root, folder);
|
|
103
|
-
const routeFiles = await fs.readdir(routeFolder, { recursive: true });
|
|
104
|
-
if (subFolder !== undefined && !subFolder.startsWith("/")) {
|
|
105
|
-
subFolder = "/" + subFolder;
|
|
106
|
-
}
|
|
107
|
-
for (let file of routeFiles) {
|
|
108
|
-
// The file must start with `+` or `_` and end with `.js` or `.ts`
|
|
109
|
-
if (/^(\+|_).+(\.js|\.ts)$/.test(fpath.basename(file))) {
|
|
110
|
-
let routePath = this.#routePath(file);
|
|
111
|
-
if (subFolder !== undefined) {
|
|
112
|
-
routePath = subFolder + routePath;
|
|
113
|
-
}
|
|
114
|
-
let type = this.#routeType(file);
|
|
115
|
-
file = fpath.relative(this.root, fpath.resolve(routeFolder, file));
|
|
116
|
-
this.routes.push({
|
|
117
|
-
path: routePath,
|
|
118
|
-
file,
|
|
119
|
-
type,
|
|
120
|
-
subFolder: subFolder,
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
this.#sortRoutes();
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Adds a file route, which can have a standard name (e.g. +page.ts) or any
|
|
129
|
-
* name if you pass the type
|
|
130
|
-
* @param path The route path
|
|
131
|
-
* @param file The file
|
|
132
|
-
* @param type The file type (if the file name is non-standard)
|
|
133
|
-
*/
|
|
134
|
-
addRouteFile(
|
|
135
|
-
path: string,
|
|
136
|
-
file: string,
|
|
137
|
-
type?:
|
|
138
|
-
| "+page"
|
|
139
|
-
| "+page.server"
|
|
140
|
-
| "+server"
|
|
141
|
-
| "_layout"
|
|
142
|
-
| "_layout.server"
|
|
143
|
-
| "_hook"
|
|
144
|
-
| "_hook.server"
|
|
145
|
-
| "_error",
|
|
146
|
-
subFolder?: string,
|
|
147
|
-
): void {
|
|
148
|
-
let routePath = this.#routePath(fpath.join(path, type ?? fpath.basename(file)));
|
|
149
|
-
let routeType = type ? this.#routeTypeFromString(type) : this.#routeType(file);
|
|
150
|
-
if (subFolder !== undefined && !subFolder.startsWith("/")) {
|
|
151
|
-
subFolder = "/" + subFolder;
|
|
152
|
-
}
|
|
153
|
-
this.routes.push({
|
|
154
|
-
path: routePath,
|
|
155
|
-
file,
|
|
156
|
-
type: routeType,
|
|
157
|
-
subFolder: subFolder,
|
|
158
|
-
});
|
|
159
|
-
this.#sortRoutes();
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
#routePath(file: string): string {
|
|
163
|
-
let routePath = file
|
|
164
|
-
.replace(/^\//, "")
|
|
165
|
-
.replace(/(\.ts|\.js)$/, "")
|
|
166
|
-
.replace(/_hook.server$/, "_hook/~server")
|
|
167
|
-
.replace(/_layout.server$/, "_layout/~server")
|
|
168
|
-
.replace(/\+page.server$/, "~server")
|
|
169
|
-
.replace(/\+server$/, "")
|
|
170
|
-
.replace(/\+page$/, "")
|
|
171
|
-
.replace(/\/$/, "");
|
|
172
|
-
return routePath.length > 0 ? `/${routePath}` : "/";
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
#routeType(file: string): RouteType {
|
|
176
|
-
let routePath = file.replace(/^\//, "").replace(/(\.ts|\.js)$/, "");
|
|
177
|
-
if (routePath.endsWith("+page")) {
|
|
178
|
-
return PAGE_ROUTE;
|
|
179
|
-
} else if (routePath.endsWith("+page.server")) {
|
|
180
|
-
return PAGE_SERVER_ROUTE;
|
|
181
|
-
} else if (routePath.endsWith("+server")) {
|
|
182
|
-
return SERVER_ROUTE;
|
|
183
|
-
} else if (routePath.endsWith("_layout")) {
|
|
184
|
-
return LAYOUT_ROUTE;
|
|
185
|
-
} else if (routePath.endsWith("_layout.server")) {
|
|
186
|
-
return LAYOUT_SERVER_ROUTE;
|
|
187
|
-
} else if (routePath.endsWith("_hook")) {
|
|
188
|
-
return HOOK_ROUTE;
|
|
189
|
-
} else if (routePath.endsWith("_hook.server")) {
|
|
190
|
-
return HOOK_SERVER_ROUTE;
|
|
191
|
-
} else if (routePath.endsWith("_error")) {
|
|
192
|
-
return ERROR_ROUTE;
|
|
193
|
-
}
|
|
194
|
-
return -1;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
#routeTypeFromString(
|
|
198
|
-
type:
|
|
199
|
-
| "+page"
|
|
200
|
-
| "+page.server"
|
|
201
|
-
| "+server"
|
|
202
|
-
| "_layout"
|
|
203
|
-
| "_layout.server"
|
|
204
|
-
| "_hook"
|
|
205
|
-
| "_hook.server"
|
|
206
|
-
| "_error",
|
|
207
|
-
) {
|
|
208
|
-
switch (type) {
|
|
209
|
-
case "+page":
|
|
210
|
-
return PAGE_ROUTE;
|
|
211
|
-
case "+page.server":
|
|
212
|
-
return PAGE_SERVER_ROUTE;
|
|
213
|
-
case "+server":
|
|
214
|
-
return SERVER_ROUTE;
|
|
215
|
-
case "_layout":
|
|
216
|
-
return LAYOUT_ROUTE;
|
|
217
|
-
case "_layout.server":
|
|
218
|
-
return LAYOUT_SERVER_ROUTE;
|
|
219
|
-
case "_hook":
|
|
220
|
-
return HOOK_ROUTE;
|
|
221
|
-
case "_hook.server":
|
|
222
|
-
return HOOK_SERVER_ROUTE;
|
|
223
|
-
case "_error":
|
|
224
|
-
return ERROR_ROUTE;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Adds a route in code, without needing the standard file naming
|
|
230
|
-
* conventions (+page.ts, +page.server.ts, etc). This is useful for small
|
|
231
|
-
* sites with one or two pages.
|
|
232
|
-
*
|
|
233
|
-
* Any combination of options may be provided; each maps to the
|
|
234
|
-
* corresponding file-route type:
|
|
235
|
-
*
|
|
236
|
-
* - `page` — a `+page` (`.torp` component or `.ts`/`.js` `PageEndPoint`)
|
|
237
|
-
* - `pageServer` — a `+page.server` (`PageServerEndPoint`, file or inline)
|
|
238
|
-
* - `server` — a `+server` HTTP endpoint (`ServerEndPoint`, file or inline)
|
|
239
|
-
* - `layout` — a `_layout` (`.torp` component or `.ts`/`.js` `PageEndPoint`)
|
|
240
|
-
* - `layoutServer` — a `_layout.server` (`PageServerEndPoint`, file or inline)
|
|
241
|
-
* - `hookServer` — a `_hook.server` (`ServerHook`, file or inline)
|
|
242
|
-
* - `error` — an `_error` page (`.torp` component or `.ts`/`.js` `PageEndPoint`)
|
|
243
|
-
*
|
|
244
|
-
* Options that need a component (`page`, `layout`, `error`) must be file
|
|
245
|
-
* paths so the component is included in the client bundle. Server-only
|
|
246
|
-
* options (`pageServer`, `server`, `layoutServer`, `hookServer`) accept
|
|
247
|
-
* either a file path or an inline object; inline server code is kept out
|
|
248
|
-
* of the client bundle automatically.
|
|
249
|
-
*
|
|
250
|
-
* @example
|
|
251
|
-
* // A page with an inline server action
|
|
252
|
-
* site.addRoute("/", {
|
|
253
|
-
* page: "./src/Counter.torp",
|
|
254
|
-
* pageServer: { actions: { set: async ({ request }) => ok() } },
|
|
255
|
-
* });
|
|
256
|
-
* @example
|
|
257
|
-
* // A JSON API endpoint
|
|
258
|
-
* site.addRoute("/api/time", {
|
|
259
|
-
* server: { get: async () => ok({ time: Date.now() }) },
|
|
260
|
-
* });
|
|
261
|
-
* @example
|
|
262
|
-
* // A root layout, server hook, and error page
|
|
263
|
-
* site.addRoute("/", {
|
|
264
|
-
* layout: "./src/Layout.torp",
|
|
265
|
-
* hookServer: { enter: async (event) => {} },
|
|
266
|
-
* error: "./src/ErrorPage.torp",
|
|
267
|
-
* });
|
|
268
|
-
*
|
|
269
|
-
* @param path The route path, e.g. `/` or `/about`
|
|
270
|
-
* @param options File paths and/or inline endpoints for the route
|
|
271
|
-
* @param subFolder An optional subFolder for grouping routes
|
|
272
|
-
*/
|
|
273
|
-
addRoute(
|
|
274
|
-
path: string,
|
|
275
|
-
options: {
|
|
276
|
-
page?: string;
|
|
277
|
-
pageServer?: string | PageServerEndPoint;
|
|
278
|
-
server?: string | ServerEndPoint;
|
|
279
|
-
layout?: string;
|
|
280
|
-
layoutServer?: string | PageServerEndPoint;
|
|
281
|
-
hookServer?: string | ServerHook;
|
|
282
|
-
error?: string;
|
|
283
|
-
},
|
|
284
|
-
subFolder?: string,
|
|
285
|
-
): void {
|
|
286
|
-
let normalizedSubFolder = subFolder;
|
|
287
|
-
if (normalizedSubFolder !== undefined && !normalizedSubFolder.startsWith("/")) {
|
|
288
|
-
normalizedSubFolder = "/" + normalizedSubFolder;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// The base path with any trailing slash stripped, so that we can
|
|
292
|
-
// append "/_layout", "/~server" etc. For the root "/" this is "".
|
|
293
|
-
let base = path.replace(/\/$/, "");
|
|
294
|
-
|
|
295
|
-
if (options.page !== undefined) {
|
|
296
|
-
this.#pushRoute(path, PAGE_ROUTE, options.page, normalizedSubFolder);
|
|
297
|
-
}
|
|
298
|
-
if (options.pageServer !== undefined) {
|
|
299
|
-
this.#pushRoute(
|
|
300
|
-
`${base}/~server`,
|
|
301
|
-
PAGE_SERVER_ROUTE,
|
|
302
|
-
options.pageServer,
|
|
303
|
-
normalizedSubFolder,
|
|
304
|
-
);
|
|
305
|
-
}
|
|
306
|
-
if (options.server !== undefined) {
|
|
307
|
-
this.#pushRoute(path, SERVER_ROUTE, options.server, normalizedSubFolder);
|
|
308
|
-
}
|
|
309
|
-
if (options.layout !== undefined) {
|
|
310
|
-
this.#pushRoute(`${base}/_layout`, LAYOUT_ROUTE, options.layout, normalizedSubFolder);
|
|
311
|
-
}
|
|
312
|
-
if (options.layoutServer !== undefined) {
|
|
313
|
-
this.#pushRoute(
|
|
314
|
-
`${base}/_layout/~server`,
|
|
315
|
-
LAYOUT_SERVER_ROUTE,
|
|
316
|
-
options.layoutServer,
|
|
317
|
-
normalizedSubFolder,
|
|
318
|
-
);
|
|
319
|
-
}
|
|
320
|
-
if (options.hookServer !== undefined) {
|
|
321
|
-
this.#pushRoute(
|
|
322
|
-
`${base}/_hook/~server`,
|
|
323
|
-
HOOK_SERVER_ROUTE,
|
|
324
|
-
options.hookServer,
|
|
325
|
-
normalizedSubFolder,
|
|
326
|
-
);
|
|
327
|
-
}
|
|
328
|
-
if (options.error !== undefined) {
|
|
329
|
-
this.#pushRoute(`${base}/_error`, ERROR_ROUTE, options.error, normalizedSubFolder);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
this.#sortRoutes();
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* Pushes a single route entry, handling both file-path and inline-endpoint
|
|
337
|
-
* targets. File paths are resolved relative to the site root; inline
|
|
338
|
-
* endpoints are also stored in `inlineEndPoints` keyed by `"path:type"`
|
|
339
|
-
* so the manifest plugin can resolve them at build time.
|
|
340
|
-
*
|
|
341
|
-
* If a route with the same path and type was already added, it is
|
|
342
|
-
* replaced, so that running a plugin twice (config load and server
|
|
343
|
-
* startup) doesn't add duplicate routes.
|
|
344
|
-
*/
|
|
345
|
-
#pushRoute(
|
|
346
|
-
routePath: string,
|
|
347
|
-
type: RouteType,
|
|
348
|
-
target: string | InlineEndPoint,
|
|
349
|
-
subFolder?: string,
|
|
350
|
-
): void {
|
|
351
|
-
const index = this.routes.findIndex((r) => r.path === routePath && r.type === type);
|
|
352
|
-
if (typeof target === "string") {
|
|
353
|
-
let file = fpath.relative(this.root, fpath.resolve(this.root, target));
|
|
354
|
-
if (index >= 0) {
|
|
355
|
-
this.routes[index] = { path: routePath, file, type, subFolder };
|
|
356
|
-
} else {
|
|
357
|
-
this.routes.push({ path: routePath, file, type, subFolder });
|
|
358
|
-
}
|
|
359
|
-
} else {
|
|
360
|
-
let key = `${routePath}:${type}`;
|
|
361
|
-
this.inlineEndPoints[key] = target;
|
|
362
|
-
if (index >= 0) {
|
|
363
|
-
this.routes[index] = { path: routePath, endPoint: target, type, subFolder };
|
|
364
|
-
} else {
|
|
365
|
-
this.routes.push({ path: routePath, endPoint: target, type, subFolder });
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
#sortRoutes() {
|
|
371
|
-
this.routes = this.routes.sort((a, b) => {
|
|
372
|
-
// Sort [param]s after paths
|
|
373
|
-
// There might be a quicker/easier way to do this
|
|
374
|
-
for (let i = 0; i < Math.min(a.path.length, b.path.length); i++) {
|
|
375
|
-
if (a.path[i] === "[" && b.path[i] !== "[") {
|
|
376
|
-
return 1;
|
|
377
|
-
} else if (b.path[i] === "[" && a.path[i] !== "[") {
|
|
378
|
-
return -1;
|
|
379
|
-
} else if (a.path[i] === b.path[i]) {
|
|
380
|
-
// Keep going...
|
|
381
|
-
} else {
|
|
382
|
-
return a.path[i].localeCompare(b.path[i]);
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
return a.path.length - b.path.length;
|
|
386
|
-
});
|
|
387
|
-
}
|
|
388
|
-
}
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { parse } from "@torpor/view/compile";
|
|
4
|
-
import type Route from "../types/Route";
|
|
5
|
-
import { LAYOUT_ROUTE } from "../types/RouteType";
|
|
6
|
-
import type Site from "./Site";
|
|
7
|
-
import { createApiCallCheck, resolveSpecifier, type RouteCheckIssue } from "./checkRoutes";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* The default import of a `.torp` component from a `.ts`/`.js` route file,
|
|
11
|
-
* e.g. `import component from "@/views/Layout.torp"`.
|
|
12
|
-
*/
|
|
13
|
-
const DEFAULT_TORP_IMPORT_RE = /import\s+[A-Za-z_$][\w$]*\s+from\s*["']([^"']+\.torp)["']/;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Checks that every `_layout` route renders its default `<slot />`.
|
|
17
|
-
*
|
|
18
|
-
* The layout engine composes pages by passing each layout (and finally the
|
|
19
|
-
* page) as the default slot of its parent, so a layout that never renders
|
|
20
|
-
* `<slot />` drops its entire content: the server renders the layout's own
|
|
21
|
-
* markup with an empty content area (no error), and the first client-side
|
|
22
|
-
* navigation that reuses the layout fails on a null slot region.
|
|
23
|
-
*
|
|
24
|
-
* Runs at build time (failing the build) and at dev startup. Best-effort and
|
|
25
|
-
* static: a layout that renders `<slot />` conditionally (inside `@if`)
|
|
26
|
-
* passes here, and is caught at request time by the server render warning
|
|
27
|
-
* (see site/layoutSlots.ts).
|
|
28
|
-
*/
|
|
29
|
-
export function checkLayoutSlots(site: Site): RouteCheckIssue[] {
|
|
30
|
-
const issues: RouteCheckIssue[] = [];
|
|
31
|
-
for (const route of site.routes) {
|
|
32
|
-
if (route.type === LAYOUT_ROUTE) {
|
|
33
|
-
issues.push(...checkLayoutSlot(site, route));
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return issues;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Checks a single layout route's component (the `.torp` route file itself,
|
|
41
|
-
* or the `.torp` file its `.ts`/`.js` endpoint re-exports as its default
|
|
42
|
-
* import). Routes without a file (inline endpoints) can't hold a component,
|
|
43
|
-
* so they are skipped.
|
|
44
|
-
*/
|
|
45
|
-
export function checkLayoutSlot(site: Site, route: Route): RouteCheckIssue[] {
|
|
46
|
-
if (route.type !== LAYOUT_ROUTE || !route.file) return [];
|
|
47
|
-
|
|
48
|
-
const check = createApiCallCheck(site);
|
|
49
|
-
|
|
50
|
-
// The component is either the route file itself (.torp) or a .ts/.js
|
|
51
|
-
// endpoint re-exporting a .torp component as its default export
|
|
52
|
-
let torpFile = path.join(site.root, route.file);
|
|
53
|
-
if (!route.file.endsWith(".torp")) {
|
|
54
|
-
const source = readIfExists(torpFile);
|
|
55
|
-
if (source === undefined) return [];
|
|
56
|
-
const match = DEFAULT_TORP_IMPORT_RE.exec(source);
|
|
57
|
-
if (!match) return [];
|
|
58
|
-
const resolved = resolveSpecifier(match[1], torpFile, check);
|
|
59
|
-
if (!resolved) return [];
|
|
60
|
-
torpFile = resolved;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const source = readIfExists(torpFile);
|
|
64
|
-
if (source === undefined) return [];
|
|
65
|
-
|
|
66
|
-
// Parse errors are reported when the file is compiled, not here
|
|
67
|
-
const parsed = parse(source);
|
|
68
|
-
if (!parsed.ok || !parsed.template) return [];
|
|
69
|
-
if (rendersDefaultSlot(parsed.template)) return [];
|
|
70
|
-
|
|
71
|
-
return [
|
|
72
|
-
{
|
|
73
|
-
file: route.file,
|
|
74
|
-
line: 1,
|
|
75
|
-
severity: "error",
|
|
76
|
-
message:
|
|
77
|
-
"The layout never renders <slot />, so pages under it render without their content. " +
|
|
78
|
-
"Every _layout component must render <slot />, or pass it down to a child component " +
|
|
79
|
-
"it renders.",
|
|
80
|
-
},
|
|
81
|
-
];
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function readIfExists(file: string): string | undefined {
|
|
85
|
-
try {
|
|
86
|
-
if (!existsSync(file) || !statSync(file).isFile()) return undefined;
|
|
87
|
-
return readFileSync(file, "utf8");
|
|
88
|
-
} catch {
|
|
89
|
-
return undefined;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Walks a parsed template looking for a default `<slot />` anywhere in the
|
|
95
|
-
* render tree — including nested inside a child component element, which is
|
|
96
|
-
* how a layout passes the page slot down (e.g. `<Shell><slot /></Shell>`).
|
|
97
|
-
*/
|
|
98
|
-
// HACK: no types on the internal parse tree nodes (same as the prettier plugin)
|
|
99
|
-
function rendersDefaultSlot(template: any): boolean {
|
|
100
|
-
const component = template.components?.[0];
|
|
101
|
-
if (!component) return false;
|
|
102
|
-
return walk(component.markup);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function walk(node: any): boolean {
|
|
106
|
-
if (
|
|
107
|
-
(node.type === "element" || node.type === "special") &&
|
|
108
|
-
node.tagName === "slot" &&
|
|
109
|
-
!node.attributes?.some((a: any) => a.name === "name")
|
|
110
|
-
) {
|
|
111
|
-
return true;
|
|
112
|
-
}
|
|
113
|
-
return (node.children ?? []).some(walk);
|
|
114
|
-
}
|