@torpor/build 1.0.0 → 1.0.1

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.
Files changed (174) hide show
  1. package/dist/{flattenHeaders-C_YYLdOq.mjs → Server-BnjLJleZ.mjs} +2 -12
  2. package/dist/Server-BnjLJleZ.mjs.map +1 -0
  3. package/dist/{Server-C_IKav_e.d.mts → Server-Cler2w5O.d.mts} +3 -48
  4. package/dist/Server-Cler2w5O.d.mts.map +1 -0
  5. package/dist/ServerEvent-CTx3Q-hk.d.mts +48 -0
  6. package/dist/ServerEvent-CTx3Q-hk.d.mts.map +1 -0
  7. package/dist/{Site-CMa153FA.d.mts → Site-CE0_o5Ci.d.mts} +3 -2
  8. package/dist/Site-CE0_o5Ci.d.mts.map +1 -0
  9. package/dist/bin/index.js +1 -1
  10. package/dist/clientEntry.d.mts +1 -0
  11. package/dist/clientEntry.mjs +59 -0
  12. package/dist/clientEntry.mjs.map +1 -0
  13. package/dist/clientEntryDev.d.mts +1 -0
  14. package/dist/clientEntryDev.mjs +7 -0
  15. package/dist/clientEntryDev.mjs.map +1 -0
  16. package/dist/dev.d.mts +3 -0
  17. package/dist/dev.mjs +3 -0
  18. package/dist/endpoint-EULRGCKA.mjs +108 -0
  19. package/dist/endpoint-EULRGCKA.mjs.map +1 -0
  20. package/dist/flattenHeaders-DM5qsSqO.mjs +13 -0
  21. package/dist/flattenHeaders-DM5qsSqO.mjs.map +1 -0
  22. package/dist/index.d.mts +1 -1
  23. package/dist/load-BntzvmwL.mjs +228 -0
  24. package/dist/load-BntzvmwL.mjs.map +1 -0
  25. package/dist/nav.mjs +1 -225
  26. package/dist/nav.mjs.map +1 -1
  27. package/dist/openapi.d.mts +1 -1
  28. package/dist/run.d.mts +1 -1
  29. package/dist/run.d.mts.map +1 -1
  30. package/dist/run.mjs +1 -1
  31. package/dist/{runOpenApi-BUi1RH1l.mjs → runOpenApi-DlOgF2Il.mjs} +54 -16
  32. package/dist/runOpenApi-DlOgF2Il.mjs.map +1 -0
  33. package/dist/server/Server.d.mts +2 -0
  34. package/dist/server/Server.mjs +2 -0
  35. package/dist/server.d.mts +2 -1
  36. package/dist/server.d.mts.map +1 -1
  37. package/dist/server.mjs +2 -1
  38. package/dist/server.mjs.map +1 -1
  39. package/dist/serverEntry-1qY3wZig.mjs +351 -0
  40. package/dist/serverEntry-1qY3wZig.mjs.map +1 -0
  41. package/dist/serverEntry-BAO8m03V.d.mts +6 -0
  42. package/dist/serverEntry-BAO8m03V.d.mts.map +1 -0
  43. package/dist/serverEntry.d.mts +2 -0
  44. package/dist/serverEntry.mjs +2 -0
  45. package/dist/test.d.mts +2 -2
  46. package/dist/test.mjs +2 -104
  47. package/dist/test.mjs.map +1 -1
  48. package/package.json +9 -8
  49. package/dist/Server-C_IKav_e.d.mts.map +0 -1
  50. package/dist/Site-CMa153FA.d.mts.map +0 -1
  51. package/dist/flattenHeaders-C_YYLdOq.mjs.map +0 -1
  52. package/dist/runOpenApi-BUi1RH1l.mjs.map +0 -1
  53. package/src/bin/index.ts +0 -22
  54. package/src/dev.ts +0 -10
  55. package/src/form/formDataToRecord.ts +0 -23
  56. package/src/form/readForm.ts +0 -71
  57. package/src/form.ts +0 -4
  58. package/src/index.ts +0 -82
  59. package/src/nav/api.test-d.ts +0 -79
  60. package/src/nav/api.ts +0 -104
  61. package/src/nav/formSubmit.ts +0 -70
  62. package/src/nav/load.ts +0 -9
  63. package/src/nav/loadData.ts +0 -134
  64. package/src/nav/navigate.ts +0 -189
  65. package/src/nav/reload.ts +0 -9
  66. package/src/nav/route.ts +0 -38
  67. package/src/nav.ts +0 -6
  68. package/src/openapi/docsHtml.ts +0 -25
  69. package/src/openapi/document.ts +0 -154
  70. package/src/openapi/plugin.ts +0 -78
  71. package/src/openapi/types.ts +0 -82
  72. package/src/openapi.ts +0 -14
  73. package/src/response/TypedResponse.ts +0 -17
  74. package/src/response/badRequest.ts +0 -31
  75. package/src/response/created.ts +0 -31
  76. package/src/response/forbidden.ts +0 -21
  77. package/src/response/found.ts +0 -30
  78. package/src/response/methodNotAllowed.ts +0 -18
  79. package/src/response/movedPermanently.ts +0 -25
  80. package/src/response/notFound.ts +0 -24
  81. package/src/response/notModified.ts +0 -34
  82. package/src/response/ok.ts +0 -38
  83. package/src/response/permanentRedirect.ts +0 -30
  84. package/src/response/response.ts +0 -22
  85. package/src/response/seeOther.ts +0 -19
  86. package/src/response/serverError.ts +0 -24
  87. package/src/response/temporaryRedirect.ts +0 -31
  88. package/src/response/transfer.ts +0 -14
  89. package/src/response/unauthorized.ts +0 -21
  90. package/src/response/unprocessable.ts +0 -30
  91. package/src/response.ts +0 -37
  92. package/src/run/depCache.ts +0 -175
  93. package/src/run/devPlugin.ts +0 -128
  94. package/src/run/prepareTemplate.ts +0 -42
  95. package/src/run/run.ts +0 -86
  96. package/src/run/runBuild.ts +0 -128
  97. package/src/run/runDev.ts +0 -177
  98. package/src/run/runOpenApi.ts +0 -52
  99. package/src/run/runPreview.ts +0 -84
  100. package/src/run/staleTorpCopies.ts +0 -100
  101. package/src/run.ts +0 -7
  102. package/src/schema.ts +0 -7
  103. package/src/server/CookieHelper.ts +0 -46
  104. package/src/server/HeaderHelper.ts +0 -25
  105. package/src/server/Server.ts +0 -168
  106. package/src/server/ServerEvent.ts +0 -62
  107. package/src/server/connect/connectMiddleware.ts +0 -85
  108. package/src/server/connect/flattenHeaders.ts +0 -22
  109. package/src/server/connect/nodeMessageToNodeResponse.ts +0 -89
  110. package/src/server/connect/requestToNodeMessage.ts +0 -29
  111. package/src/server/contentType.ts +0 -85
  112. package/src/server/types/HttpMethod.ts +0 -12
  113. package/src/server/types/MiddlewareFunction.ts +0 -22
  114. package/src/server/types/ServerFunction.ts +0 -7
  115. package/src/server.ts +0 -7
  116. package/src/site/Router.ts +0 -175
  117. package/src/site/Site.ts +0 -388
  118. package/src/site/checkLayoutSlots.ts +0 -114
  119. package/src/site/checkRoutes.ts +0 -435
  120. package/src/site/clientEntry.ts +0 -121
  121. package/src/site/clientEntryDev.ts +0 -3
  122. package/src/site/defaultAdapter.ts +0 -11
  123. package/src/site/layoutSlots.ts +0 -53
  124. package/src/site/manifest.ts +0 -173
  125. package/src/site/serverEntry.ts +0 -648
  126. package/src/state/$page.ts +0 -25
  127. package/src/state/$serverPage.ts +0 -22
  128. package/src/state/client.ts +0 -15
  129. package/src/state.ts +0 -3
  130. package/src/test/runTest.ts +0 -538
  131. package/src/test.ts +0 -3
  132. package/src/types/Adapter.ts +0 -20
  133. package/src/types/ClientState.ts +0 -8
  134. package/src/types/InternalState.ts +0 -7
  135. package/src/types/Jsonify.ts +0 -20
  136. package/src/types/LayoutHandler.ts +0 -5
  137. package/src/types/LayoutPath.ts +0 -10
  138. package/src/types/ManifestRoute.ts +0 -6
  139. package/src/types/PageData.test-d.ts +0 -136
  140. package/src/types/PageData.ts +0 -35
  141. package/src/types/PageEndPoint.ts +0 -55
  142. package/src/types/PageForm.test-d.ts +0 -78
  143. package/src/types/PageForm.ts +0 -29
  144. package/src/types/PageLoadEvent.ts +0 -26
  145. package/src/types/PageLoadReturn.ts +0 -13
  146. package/src/types/PageProps.ts +0 -14
  147. package/src/types/PageServerAction.ts +0 -13
  148. package/src/types/PageServerEndPoint.test-d.ts +0 -105
  149. package/src/types/PageServerEndPoint.ts +0 -110
  150. package/src/types/PageServerLoad.ts +0 -15
  151. package/src/types/PageState.ts +0 -8
  152. package/src/types/ParseRouteParams.test-d.ts +0 -84
  153. package/src/types/ParseRouteParams.ts +0 -64
  154. package/src/types/Route.ts +0 -30
  155. package/src/types/RouteHandler.ts +0 -23
  156. package/src/types/RouteLayoutHandler.ts +0 -5
  157. package/src/types/RouteMatchResult.ts +0 -7
  158. package/src/types/RouteType.ts +0 -19
  159. package/src/types/ServerEndPoint.test-d.ts +0 -77
  160. package/src/types/ServerEndPoint.ts +0 -169
  161. package/src/types/ServerHook.ts +0 -18
  162. package/src/types/ServerLoadEvent.ts +0 -82
  163. package/src/types/ServerRequest.ts +0 -15
  164. package/src/types/SitePlugin.ts +0 -24
  165. package/src/types/StandardSchema.ts +0 -109
  166. package/src/utils/pathToRegex.ts +0 -17
  167. package/src/utils/pathTrie.ts +0 -182
  168. package/src/utils/searchParamsToRecord.ts +0 -18
  169. package/src/utils/torporPackages.ts +0 -150
  170. package/src/utils/tsconfigAliases.ts +0 -90
  171. package/src/validation/ValidationError.ts +0 -18
  172. package/src/validation/endpoint.ts +0 -83
  173. package/src/validation/validate.ts +0 -26
  174. package/src/vite-env.d.ts +0 -2
@@ -1,128 +0,0 @@
1
- import { type IncomingMessage, type ServerResponse } from "node:http";
2
- import { existsSync, promises as fs } from "node:fs";
3
- import path from "node:path";
4
- import { Readable } from "node:stream";
5
- import { type Plugin, type ViteDevServer } from "vite";
6
- import { serverError } from "../response.ts";
7
- import Server from "../server/Server.ts";
8
- import flattenHeaders from "../server/connect/flattenHeaders.ts";
9
- import type Site from "../site/Site.ts";
10
- import prepareTemplate from "./prepareTemplate.ts";
11
-
12
- type ReqHandler = (req: IncomingMessage, res: ServerResponse) => Promise<void>;
13
-
14
- /**
15
- * The default Node-runtime dev plugin. Reads `src/site.html`, sets up a torpor
16
- * `Server` whose catch-all route renders via `vite.ssrLoadModule`, and registers
17
- * a Connect middleware (after Vite's own middlewares) that bridges Node
18
- * req/res to the torpor Server's fetch handler.
19
- *
20
- * Adapters that need a different runtime (e.g. workerd) provide their own
21
- * `dev()` plugin instead.
22
- */
23
- export default function devPlugin(site: Site): Plugin {
24
- return {
25
- name: "torpor-dev",
26
- configureServer(vite) {
27
- // Kick off async setup immediately; the middleware awaits it so we
28
- // don't depend on whether Vite awaits configureServer itself.
29
- const handlerPromise = createDevHandler(vite, site);
30
- // Return a post-hook so our middleware runs AFTER Vite's internal
31
- // middlewares (static serving, transforms, HMR), acting as the SSR
32
- // fallback.
33
- return () => {
34
- vite.middlewares.use(async (req, res, next) => {
35
- try {
36
- const handler = await handlerPromise;
37
- await handler(req, res);
38
- } catch (e) {
39
- next(e as Error);
40
- }
41
- });
42
- };
43
- },
44
- };
45
- }
46
-
47
- async function createDevHandler(vite: ViteDevServer, site: Site): Promise<ReqHandler> {
48
- const siteFolder = path.resolve(site.root, "./node_modules/@torpor/build/src/site/");
49
- const serverScript = path.join(siteFolder, "serverEntry.ts");
50
-
51
- // Read site.html if present
52
- // It's called site.html because @torpor/build builds the site (html, routes
53
- // etc) while the user builds the app (components etc). An endpoints-only
54
- // site has no site.html, and no template is needed
55
- let template: string | undefined;
56
- const templateFile = path.resolve(site.root, "src/site.html");
57
- if (existsSync(templateFile)) {
58
- template = await fs.readFile(templateFile, "utf-8");
59
-
60
- // Apply Vite HTML transforms. This injects the Vite HMR client, and also
61
- // applies HTML transforms from Vite plugins. Note that we only support a
62
- // universal transform, not individual transforms for each route
63
- template = await vite.transformIndexHtml("", template);
64
-
65
- // Prepare site.html so that we can just splice components into it
66
- const clientScript = path.join(siteFolder, "clientEntry.ts");
67
- const clientDevScript = path.join(siteFolder, "clientEntryDev.ts");
68
- template = prepareTemplate(template, clientScript, clientDevScript);
69
- }
70
-
71
- const server = new Server();
72
-
73
- // Every request (GET, POST, etc) goes through loadEndPoint
74
- server.add("*", async (ev) => {
75
- try {
76
- // Load the server entry. ssrLoadModule automatically transforms ESM
77
- // source code to be usable in Node.js. No bundling is required, and
78
- // it provides efficient invalidation similar to HMR
79
- const { load } = await vite.ssrLoadModule(serverScript);
80
-
81
- // Render the app HTML (or fetch server data etc) via serverEntry's
82
- // exported `load` function
83
- return await load(ev, template);
84
- } catch (e: any) {
85
- // If an error is caught, let Vite fix the stack trace so it maps
86
- // back to your actual source code
87
- vite.ssrFixStacktrace(e);
88
- return serverError(e);
89
- }
90
- });
91
-
92
- // Put adapter-specific functionality in the `adapter` property of
93
- // globalThis for now
94
- // @ts-ignore
95
- globalThis.adapter = { env: process.env };
96
-
97
- return async (req, res) => {
98
- const request = await nodeReqToRequest(req);
99
- const response = await server.fetch(request);
100
- await sendResponse(response, res);
101
- };
102
- }
103
-
104
- async function nodeReqToRequest(req: IncomingMessage): Promise<Request> {
105
- const url = req.url ?? "/";
106
- const host = req.headers.host ?? "localhost";
107
- const protocol = "encrypted" in req.socket && req.socket.encrypted ? "https" : "http";
108
- const init: RequestInit = {
109
- method: req.method,
110
- headers: flattenHeaders(req.headers),
111
- };
112
- if (req.method !== "GET" && req.method !== "HEAD") {
113
- init.body = Readable.toWeb(req) as ReadableStream;
114
- // @ts-ignore duplex is required when streaming a body
115
- init.duplex = "half";
116
- }
117
- return new Request(`${protocol}://${host}${url}`, init);
118
- }
119
-
120
- async function sendResponse(response: Response, res: ServerResponse): Promise<void> {
121
- res.statusCode = response.status;
122
- response.headers.forEach((value, key) => res.setHeader(key, value));
123
- if (!response.body) {
124
- res.end();
125
- return;
126
- }
127
- Readable.fromWeb(response.body as unknown as import("node:stream/web").ReadableStream).pipe(res);
128
- }
@@ -1,42 +0,0 @@
1
- export default function prepareTemplate(
2
- template: string,
3
- clientScript: string,
4
- clientDevScript?: string,
5
- ): string {
6
- let result = template;
7
-
8
- // Put %COMPONENT_HEAD% before </head>
9
- // This is where styles will go
10
- let headStart = result.indexOf("</head>");
11
- if (headStart === -1) {
12
- throw new Error(`Couldn't find <head> end tag`);
13
- }
14
- result = result.substring(0, headStart) + "%COMPONENT_HEAD%" + result.substring(headStart);
15
-
16
- // Put %COMPONENT_BODY% inside <div id="app"></div>
17
- // This is where the component's HTML will go
18
- let divMatchStart = regexIndexOf(result, /<div\s+id=("app"|'app'|app)[\s>]/);
19
- if (divMatchStart === -1) {
20
- throw new Error(`Couldn't find <div id="app"></div>`);
21
- }
22
- let bodyStart = result.indexOf(">", divMatchStart) + 1;
23
- let bodyEnd = result.indexOf("</div>", bodyStart);
24
- if (bodyEnd === -1) {
25
- throw new Error(`Couldn't find <div id="app"></div>`);
26
- }
27
- result = result.substring(0, bodyStart) + "%COMPONENT_BODY%" + result.substring(bodyEnd);
28
-
29
- // Add the clientEntry script at the very end, for hydrating and navigating
30
- if (clientDevScript) {
31
- result += `<script type="module" src="${clientDevScript}"></script>`;
32
- }
33
- result += `<script type="module" src="${clientScript}"></script>`;
34
-
35
- return result;
36
- }
37
-
38
- // From https://stackoverflow.com/a/274094
39
- function regexIndexOf(string: string, regex: RegExp, position?: number) {
40
- var indexOf = string.substring(position || 0).search(regex);
41
- return indexOf >= 0 ? indexOf + (position || 0) : indexOf;
42
- }
package/src/run/run.ts DELETED
@@ -1,86 +0,0 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import { createServer as createViteServer, type ViteDevServer } from "vite";
4
- import Site from "../site/Site";
5
- import runBuild from "./runBuild";
6
- import runDev from "./runDev";
7
- import runPreview from "./runPreview";
8
-
9
- /**
10
- * Looks for and loads a site.config.js/ts file in the working directory,
11
- * running the site's plugins. The Vite server used to load the config is
12
- * returned as well, so that (for example) route files can be loaded through
13
- * it; callers are responsible for closing it.
14
- */
15
- export async function loadSite(folder: string): Promise<{ site: Site; vite: ViteDevServer }> {
16
- // Look for and load a site.config.js/ts file in the working directory
17
- const jsConfigFile = path.join(folder, "site.config.js");
18
- const tsConfigFile = path.join(folder, "site.config.ts");
19
- let configFile = "";
20
- if (fs.existsSync(jsConfigFile)) {
21
- configFile = jsConfigFile;
22
- } else if (fs.existsSync(tsConfigFile)) {
23
- configFile = tsConfigFile;
24
- } else {
25
- throw new Error("site.config file not found");
26
- }
27
-
28
- // Use a temporary Vite server to load the config, so that local TS imports
29
- // (e.g. a routes.ts file) and path aliases are resolved correctly
30
- const vite = await createViteServer({
31
- server: { middlewareMode: true },
32
- appType: "custom",
33
- resolve: { tsconfigPaths: true },
34
- optimizeDeps: { noDiscovery: true },
35
- });
36
- try {
37
- const site = (await vite.ssrLoadModule(configFile)).default as Site;
38
- site.configFile = configFile;
39
-
40
- if (!site || !site.root || !site.routes) {
41
- throw new Error("Invalid site in config file");
42
- }
43
-
44
- // Run the site's plugins. The manifest plugin also runs them when the
45
- // server starts up (dev SSR and production), so that plugin routes are
46
- // registered in the runtime as well
47
- for (const plugin of site.plugins) {
48
- await plugin(site);
49
- }
50
-
51
- return { site, vite };
52
- } catch (error) {
53
- await vite.close();
54
- throw error;
55
- }
56
- }
57
-
58
- export default async function run(
59
- folder: string,
60
- mode: "dev" | "build" | "preview",
61
- ): Promise<void> {
62
- const { site, vite } = await loadSite(folder);
63
- try {
64
- switch (mode) {
65
- case "dev": {
66
- await runDev(site);
67
- break;
68
- }
69
- case "build": {
70
- await runBuild(site);
71
- break;
72
- }
73
- case "preview": {
74
- await runPreview(site);
75
- break;
76
- }
77
- default: {
78
- console.log("No mode passed, running in dev");
79
- await runDev(site);
80
- break;
81
- }
82
- }
83
- } finally {
84
- await vite.close();
85
- }
86
- }
@@ -1,128 +0,0 @@
1
- import torpor from "@torpor/unplugin/vite";
2
- import { existsSync, promises as fs } from "node:fs";
3
- import path from "node:path";
4
- import { build, defineConfig, type AliasOptions } from "vite";
5
- import Site from "../site/Site";
6
- import { checkApiCalls, checkRoutes, reportRouteIssues } from "../site/checkRoutes";
7
- import { checkLayoutSlots } from "../site/checkLayoutSlots";
8
- import manifest from "../site/manifest.ts";
9
- import tsconfigAliases, { type AliasEntry } from "../utils/tsconfigAliases";
10
- import { addTorporPackageConfig } from "../utils/torporPackages";
11
-
12
- // TODO: Don't cache index.html in dev?
13
- // TODO: Don't reload layouts during client routing
14
- // TODO: Call the correct +page and +server routes when in the same folder
15
-
16
- export default async function runBuild(site: Site): Promise<void> {
17
- // Check route type annotations and makeApi calls against the routes
18
- // derived from file locations, and check that layouts render their slot;
19
- // errors fail the build before anything is written
20
- let errorCount = reportRouteIssues(checkRoutes(site));
21
- errorCount += reportRouteIssues(checkApiCalls(site));
22
- errorCount += reportRouteIssues(checkLayoutSlots(site));
23
- if (errorCount > 0) {
24
- throw new Error(
25
- `Route type check failed with ${errorCount} error${errorCount === 1 ? "" : "s"} (see above)`,
26
- );
27
- }
28
-
29
- // Delete the dist folder if it exists
30
- const distFolder = path.resolve(site.root, "dist");
31
- if (existsSync(distFolder)) {
32
- await fs.rm(distFolder, { recursive: true });
33
- }
34
- const clientFolder = path.join(distFolder, "client");
35
- const serverFolder = path.join(distFolder, "server");
36
-
37
- // HOOK: Prebuild
38
- if (site.adapter.prebuild) {
39
- await site.adapter.prebuild(site);
40
- }
41
-
42
- // TODO: From a setting
43
- // An endpoints-only site has no site.html, so everything template-related
44
- // is optional
45
- const siteHtml = path.resolve(site.root, "src/site.html");
46
- const hasSiteHtml = existsSync(siteHtml);
47
-
48
- const siteFolder = path.resolve(site.root, "./node_modules/@torpor/build/src/site/");
49
- let clientScript = path.join(siteFolder, "clientEntry.ts");
50
- let serverScript = path.join(siteFolder, "serverEntry.ts");
51
-
52
- // Build the client assets, including site.html and the route files
53
- // EXCLUDING anything with `server.js` in the name
54
- const clientConfig = structuredClone(site.viteConfig ?? {});
55
- // Resolve tsconfig path aliases (e.g. `@/*`). Previously provided by the
56
- // default `vite-tsconfig-paths` plugin on Site; vite-plus handles it inline
57
- // for the client, but hardcodes tsconfigPaths:false on the SSR
58
- // externalization path, so mirror tsconfig `compilerOptions.paths` as Vite
59
- // `resolve.alias` to resolve them during transform for both client and SSR
60
- clientConfig.resolve ??= {};
61
- clientConfig.resolve.tsconfigPaths ??= true;
62
- clientConfig.resolve.alias = [
63
- ...asAliasArray(clientConfig.resolve.alias),
64
- ...tsconfigAliases(site.root),
65
- ];
66
- clientConfig.plugins = [manifest(site), torpor(), ...site.vitePlugins];
67
- clientConfig.build ??= {};
68
- clientConfig.build.outDir = clientFolder;
69
- clientConfig.build.rollupOptions ??= {};
70
- clientConfig.build.rollupOptions.input = [
71
- ...(hasSiteHtml ? [siteHtml] : []),
72
- clientScript,
73
- ...site.routes
74
- .filter((r) => r.file && !/server\.(ts|js)$/.test(r.file))
75
- .map((r) => path.resolve(site.root, r.file!)),
76
- ...site.inputs.filter((f) => !/server\.(ts|js)$/.test(f)),
77
- ];
78
- clientConfig.build.ssrManifest = true;
79
- await build(defineConfig(clientConfig));
80
-
81
- // Build the server assets, including the server entry script and the route
82
- // files
83
- const serverConfig = structuredClone(site.viteConfig ?? {});
84
- // Resolve tsconfig path aliases (e.g. `@/*`), as above
85
- serverConfig.resolve ??= {};
86
- serverConfig.resolve.tsconfigPaths ??= true;
87
- serverConfig.resolve.alias = [
88
- ...asAliasArray(serverConfig.resolve.alias),
89
- ...tsconfigAliases(site.root),
90
- ];
91
- serverConfig.plugins = [manifest(site, true), torpor(), ...site.vitePlugins];
92
- serverConfig.build ??= {};
93
- serverConfig.build.outDir = serverFolder;
94
- // Bundle packages that ship `.torp` files into the server build, as they
95
- // need the torpor compiler (Node/workerd can't load them externalized)
96
- addTorporPackageConfig(site.root, serverConfig);
97
- serverConfig.build.rollupOptions ??= {};
98
- serverConfig.build.rollupOptions.input = [
99
- serverScript,
100
- ...site.routes.filter((r) => r.file).map((r) => path.resolve(site.root, r.file!)),
101
- ...site.inputs,
102
- ];
103
- serverConfig.build.ssr = serverScript;
104
- await build(defineConfig(serverConfig));
105
-
106
- // Move the site.html file into /client
107
- // HACK: Should do this in Rollup if possible?
108
- if (hasSiteHtml) {
109
- await fs.rename(
110
- path.join(clientFolder, "src", "site.html"),
111
- path.join(clientFolder, "site.html"),
112
- );
113
- await fs.rm(path.join(clientFolder, "src"), { recursive: true });
114
- }
115
-
116
- // HOOK: Postbuild
117
- if (site.adapter.postbuild) {
118
- await site.adapter.postbuild(site);
119
- }
120
- }
121
-
122
- function asAliasArray(alias: AliasOptions | undefined): AliasEntry[] {
123
- // Normalize an existing `resolve.alias` value (array | object | undefined)
124
- // into an array so we can concatenate our tsconfig-derived aliases.
125
- if (!alias) return [];
126
- if (Array.isArray(alias)) return alias;
127
- return Object.entries(alias).map(([find, replacement]) => ({ find, replacement }));
128
- }
package/src/run/runDev.ts DELETED
@@ -1,177 +0,0 @@
1
- import estorpor from "@torpor/unplugin/esbuild";
2
- import torpor from "@torpor/unplugin/vite";
3
- import { configDotenv } from "dotenv";
4
- import { readFileSync } from "node:fs";
5
- import path from "node:path";
6
- import {
7
- type AliasOptions,
8
- createServer as createViteServer,
9
- type Plugin,
10
- type ViteDevServer,
11
- } from "vite";
12
- import Site from "../site/Site.ts";
13
- import {
14
- checkApiCallSource,
15
- checkApiCalls,
16
- checkRoute,
17
- checkRoutes,
18
- createApiCallCheck,
19
- type RouteCheckIssue,
20
- reportRouteIssues,
21
- } from "../site/checkRoutes";
22
- import manifest from "../site/manifest.ts";
23
- import { checkLayoutSlot, checkLayoutSlots } from "../site/checkLayoutSlots";
24
- import { LAYOUT_ROUTE } from "../types/RouteType";
25
- import tsconfigAliases, { type AliasEntry } from "../utils/tsconfigAliases";
26
- import { addTorporPackageConfig } from "../utils/torporPackages";
27
- import devPlugin from "./devPlugin.ts";
28
- import { clearStaleDepCache } from "./depCache";
29
- import { reportStaleTorpCopies } from "./staleTorpCopies";
30
-
31
- export default async function runDev(site: Site): Promise<void> {
32
- // Create the Vite dev server. Unlike the previous middleware-mode setup, we
33
- // let Vite own the HTTP server (so HMR/websockets work natively) and let the
34
- // adapter's `dev()` plugin(s) handle SSR via configureServer.
35
- const config = structuredClone(site.viteConfig ?? {});
36
- config.appType = "custom";
37
- config.server ??= {};
38
- config.server.host ??= "localhost";
39
- config.server.port ??= 7059;
40
- // Resolve tsconfig path aliases (e.g. `@/*`). Previously provided by the
41
- // default `vite-tsconfig-paths` plugin on Site; vite-plus handles it inline
42
- config.resolve ??= {};
43
- config.resolve.tsconfigPaths ??= true;
44
- // Resolve `@torpor/view` (runtime, compiler, SSR) from source in dev mode
45
- // so changes to the compiler take effect without rebuilding dist.
46
- config.resolve.conditions ??= [];
47
- config.resolve.conditions.push("development");
48
- // vite-plus' `tsconfigPaths` isn't honored by the SSR module-runner's
49
- // externalization path (it hardcodes tsconfigPaths:false), so dev SSR can't
50
- // resolve path aliases. Mirror tsconfig `compilerOptions.paths` as Vite
51
- // `resolve.alias` so they resolve during transform for both client and SSR.
52
- config.resolve.alias = [...asAliasArray(config.resolve.alias), ...tsconfigAliases(site.root)];
53
-
54
- // The adapter provides the dev-runtime plugin(s); fall back to the
55
- // framework's Node-runtime plugin when it doesn't.
56
- const adapterDev = site.adapter.dev?.(site);
57
- const devPlugins = normalizePlugins(adapterDev ?? devPlugin(site));
58
-
59
- config.plugins = [
60
- manifest(site, true),
61
- torpor({ dev: true }),
62
- ...devPlugins,
63
- ...site.vitePlugins,
64
- ];
65
-
66
- // HACK: To be able to import `.torp` files from barrel files in
67
- // node_modules, we need to add their libraries to `ssr.noExternal` in
68
- // site.config.ts, and let esbuild know how to compile them here
69
- config.optimizeDeps ??= {};
70
- config.optimizeDeps.extensions ??= [];
71
- config.optimizeDeps.extensions.push(".torp");
72
- config.optimizeDeps.rolldownOptions ??= {};
73
- config.optimizeDeps.rolldownOptions.plugins ??= [estorpor()];
74
- // TODO: config.optimizeDeps.rolldownOptions.plugins.push(estorpor());
75
-
76
- // Packages that ship `.torp` files (e.g. icon libraries) need the torpor
77
- // compiler: dep optimization would parse them as plain JavaScript and
78
- // fail, and SSR externalization would leave raw `.torp` imports that
79
- // Node/workerd can't load. Exclude them from optimization and mark them
80
- // for SSR bundling so they go through the torpor plugin
81
- addTorporPackageConfig(site.root, config);
82
-
83
- // Workspace packages may have been rebuilt since the last dev run; drop
84
- // Vite's dep cache so SSR never executes stale prebundled modules
85
- clearStaleDepCache(site.root, (message) => console.log(`\n${message}`));
86
-
87
- // A workspace package with dist .torp copies (e.g. @torpor/ui) whose src
88
- // changed without a rebuild serves OLD components to the site, silently;
89
- // report the drift so it can be fixed with a rebuild
90
- reportStaleTorpCopies(site.root, (message) => console.log(`\n${message}`));
91
-
92
- // Load environment variables from a `.env` file, with defaults if not set
93
- configDotenv();
94
-
95
- process.env.PROTOCOL ??= "http:";
96
- process.env.HOST ??= "localhost";
97
- process.env.PORT ??= "7059";
98
-
99
- const connectingUrl = `${process.env.PROTOCOL}//${process.env.HOST}:${process.env.PORT}`;
100
- console.log(`\nConnecting to ${connectingUrl}`);
101
-
102
- const vite = await createViteServer(config);
103
- await vite.listen();
104
-
105
- watchRouteTypes(site, vite);
106
-
107
- const listeningUrl = vite.resolvedUrls?.local?.[0] ?? connectingUrl;
108
- console.log(`Listening on ${listeningUrl}\n`);
109
- }
110
-
111
- /**
112
- * Reports route type issues at startup, then re-checks route files and
113
- * `makeApi` calls as they change so annotation problems surface during
114
- * development without failing the dev server.
115
- */
116
- function watchRouteTypes(site: Site, vite: ViteDevServer): void {
117
- reportRouteIssues(checkRoutes(site));
118
- reportRouteIssues(checkApiCalls(site));
119
- reportRouteIssues(checkLayoutSlots(site));
120
-
121
- // Map absolute route file paths to their manifest entries, so changed
122
- // files can be re-checked against their derived route. Layout route files
123
- // are included for their slot check (a .torp layout is its own component)
124
- const routeFiles = new Map(
125
- site.routes
126
- .filter(
127
- (r) => r.file?.endsWith(".ts") || (r.file?.endsWith(".torp") && r.type === LAYOUT_ROUTE),
128
- )
129
- .map((r) => [path.resolve(site.root, r.file!), r]),
130
- );
131
- const apiCheck = createApiCallCheck(site);
132
- // Only report when a file's issues change, to avoid repeating the same
133
- // warnings on every save
134
- const reported = new Map<string, string>();
135
-
136
- const report = (file: string, issues: RouteCheckIssue[]) => {
137
- const key = JSON.stringify(issues);
138
- if (reported.get(file) === key) return;
139
- reported.set(file, key);
140
- reportRouteIssues(issues);
141
- };
142
-
143
- const recheck = (file: string): void => {
144
- const route = routeFiles.get(file);
145
- if (route) {
146
- report(
147
- file,
148
- route.type === LAYOUT_ROUTE ? checkLayoutSlot(site, route) : checkRoute(site, route),
149
- );
150
- return;
151
- }
152
- if (!/\.(ts|js|torp)$/.test(file)) return;
153
- let source: string;
154
- try {
155
- source = readFileSync(file, "utf8");
156
- } catch {
157
- return;
158
- }
159
- report(file, checkApiCallSource(source, file, apiCheck));
160
- };
161
-
162
- vite.watcher.on("change", recheck);
163
- vite.watcher.on("add", recheck);
164
- }
165
-
166
- function normalizePlugins(plugins: Plugin | Plugin[] | void): Plugin[] {
167
- if (!plugins) return [];
168
- return Array.isArray(plugins) ? plugins : [plugins];
169
- }
170
-
171
- function asAliasArray(alias: AliasOptions | undefined): AliasEntry[] {
172
- // Normalize an existing `resolve.alias` value (array | object | undefined)
173
- // into an array so we can concatenate our tsconfig-derived aliases.
174
- if (!alias) return [];
175
- if (Array.isArray(alias)) return alias;
176
- return Object.entries(alias).map(([find, replacement]) => ({ find, replacement }));
177
- }
@@ -1,52 +0,0 @@
1
- import { writeFile } from "node:fs/promises";
2
- import path from "node:path";
3
- import type { ViteDevServer } from "vite";
4
- import buildOpenApiDocument from "../openapi/document";
5
- import { OPEN_API_STATE_KEY } from "../openapi/plugin";
6
- import type { OpenApiEndPoint, OpenApiRouteEntry, ResolvedOpenApiOptions } from "../openapi/types";
7
- import type Site from "../site/Site";
8
- import { SERVER_ROUTE } from "../types/RouteType";
9
-
10
- /**
11
- * Writes an OpenAPI document for the site's +server endpoints to a file.
12
- * Route files are loaded through the supplied Vite server, so this must be
13
- * called while the server from `loadSite` is still open.
14
- */
15
- export default async function runOpenApi(
16
- site: Site,
17
- vite: ViteDevServer,
18
- outFile = "openapi.json",
19
- ): Promise<void> {
20
- const options = site.pluginState.get(OPEN_API_STATE_KEY) as ResolvedOpenApiOptions | undefined;
21
- if (!options) {
22
- throw new Error(
23
- 'No OpenAPI options found. Add the openApi() plugin to site.plugins to use "tb --openapi"',
24
- );
25
- }
26
-
27
- const entries: OpenApiRouteEntry[] = [];
28
- for (const route of site.routes) {
29
- if (route.type !== SERVER_ROUTE) {
30
- continue;
31
- }
32
- // Don't document the docs page itself
33
- if (options.docs && route.path === options.docs) {
34
- continue;
35
- }
36
- if (route.endPoint && !route.file) {
37
- // Inline endpoint
38
- entries.push({ path: route.path, endPoint: route.endPoint as OpenApiEndPoint });
39
- } else if (route.file) {
40
- const file = path.resolve(site.root, route.file);
41
- const mod = await vite.ssrLoadModule(file);
42
- entries.push({ path: route.path, endPoint: mod.default });
43
- }
44
- }
45
-
46
- const doc = buildOpenApiDocument(entries, options);
47
- await writeFile(path.resolve(site.root, outFile), JSON.stringify(doc, null, 2) + "\n");
48
-
49
- console.log(
50
- `OpenAPI document written to ${outFile} (${Object.keys(doc.paths ?? {}).length} paths)`,
51
- );
52
- }