@kasperrt/smolter 0.2.0 → 0.3.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.
Files changed (264) hide show
  1. package/{src/internal → .generated}/client/index.ts +1 -0
  2. package/.generated/core/environment.ts +43 -0
  3. package/{src/internal → .generated}/core/errors.ts +1 -1
  4. package/{src/internal → .generated}/core/index.ts +1 -0
  5. package/.generated/core/navigation-router.ts +107 -0
  6. package/.generated/core/navigation.ts +147 -0
  7. package/.generated/core/presentation.ts +108 -0
  8. package/.generated/core/resource-types.ts +44 -0
  9. package/.generated/core/resources.ts +444 -0
  10. package/.generated/core/responses.ts +86 -0
  11. package/{src/internal → .generated}/core/router.ts +136 -63
  12. package/{src/internal → .generated}/core/serialize.ts +7 -2
  13. package/.generated/core/store.ts +25 -0
  14. package/.generated/core/submission.ts +116 -0
  15. package/{src/internal → .generated}/core/types.ts +18 -1
  16. package/.generated/core/url.ts +29 -0
  17. package/.generated/core/utils.ts +5 -0
  18. package/.generated/preact/await.tsx +41 -0
  19. package/.generated/preact/client.tsx +93 -0
  20. package/.generated/preact/context.ts +59 -0
  21. package/.generated/preact/document.tsx +198 -0
  22. package/{src/internal → .generated}/preact/fetcher.ts +43 -23
  23. package/.generated/preact/form.ts +58 -0
  24. package/{src/internal → .generated}/preact/hooks.ts +25 -28
  25. package/.generated/preact/index.ts +34 -0
  26. package/.generated/preact/lazy-route.ts +100 -0
  27. package/.generated/preact/navigation-state.ts +9 -0
  28. package/.generated/preact/navigation.tsx +147 -0
  29. package/.generated/preact/root-context.ts +21 -0
  30. package/.generated/preact/root-data.ts +45 -0
  31. package/.generated/preact/route-hooks.tsx +170 -0
  32. package/.generated/preact/route-types.ts +76 -0
  33. package/.generated/preact/router.tsx +107 -0
  34. package/{src/internal → .generated}/preact/routes.ts +17 -9
  35. package/.generated/react/context.ts +59 -0
  36. package/.generated/react/data.ts +25 -0
  37. package/{src/internal → .generated}/react/fetcher.ts +13 -11
  38. package/.generated/react/form.ts +58 -0
  39. package/{src/internal → .generated}/react/hooks.ts +9 -14
  40. package/{src/internal/preact → .generated/react}/index.ts +5 -1
  41. package/{src/internal → .generated}/react/link.ts +13 -21
  42. package/.generated/react/resources/components.tsx +404 -0
  43. package/{src/internal → .generated}/react/routes.ts +17 -9
  44. package/.generated/server/application.tsx +185 -0
  45. package/.generated/server/assets.ts +19 -0
  46. package/.generated/server/handler.ts +69 -0
  47. package/.generated/server/http.ts +55 -0
  48. package/{src/internal → .generated}/server/index.ts +3 -3
  49. package/.generated/server/node.ts +4 -0
  50. package/.generated/server/preact.ts +7 -0
  51. package/.generated/vite/application/browser-boundary.ts +42 -0
  52. package/.generated/vite/application/lazy-files.ts +136 -0
  53. package/.generated/vite/application/vite.ts +144 -0
  54. package/{src/preact-vite.ts → .generated/vite/preact.ts} +9 -3
  55. package/{src/react-vite.ts → .generated/vite/react.ts} +3 -3
  56. package/CHANGELOG.md +54 -0
  57. package/README.md +227 -35
  58. package/dist/client/deserialize.js +10 -0
  59. package/dist/{internal/client → client}/index.d.ts +1 -0
  60. package/dist/client/index.js +46 -0
  61. package/dist/core/environment.d.ts +12 -0
  62. package/dist/core/environment.js +23 -0
  63. package/dist/core/errors.js +7 -0
  64. package/dist/{internal/core → core}/index.d.ts +1 -0
  65. package/dist/core/match.js +33 -0
  66. package/dist/core/navigation-router.d.ts +15 -0
  67. package/dist/core/navigation-router.js +64 -0
  68. package/dist/core/navigation.d.ts +15 -0
  69. package/dist/core/navigation.js +92 -0
  70. package/dist/core/presentation.d.ts +5 -0
  71. package/dist/core/presentation.js +69 -0
  72. package/dist/core/resource-types.d.ts +37 -0
  73. package/dist/core/resources.d.ts +19 -0
  74. package/dist/core/resources.js +345 -0
  75. package/dist/core/responses.d.ts +15 -0
  76. package/dist/core/responses.js +48 -0
  77. package/dist/{internal/core → core}/router.d.ts +1 -1
  78. package/dist/core/router.js +204 -0
  79. package/dist/{internal/core → core}/serialize.d.ts +2 -0
  80. package/dist/core/serialize.js +17 -0
  81. package/dist/core/store.d.ts +6 -0
  82. package/dist/core/store.js +21 -0
  83. package/dist/core/submission.d.ts +30 -0
  84. package/dist/core/submission.js +41 -0
  85. package/dist/{internal/core → core}/types.d.ts +16 -1
  86. package/dist/core/url.d.ts +7 -0
  87. package/dist/core/url.js +12 -0
  88. package/dist/core/utils.d.ts +5 -0
  89. package/dist/core/utils.js +5 -0
  90. package/dist/files/index.js +64 -0
  91. package/dist/preact/await.d.ts +7 -0
  92. package/dist/preact/await.js +19 -0
  93. package/dist/preact/client.d.ts +7 -0
  94. package/dist/preact/client.js +67 -0
  95. package/dist/preact/context.d.ts +18 -0
  96. package/dist/preact/context.js +23 -0
  97. package/dist/preact/document.d.ts +15 -0
  98. package/dist/preact/document.js +155 -0
  99. package/dist/{internal/preact → preact}/fetcher.d.ts +9 -5
  100. package/dist/preact/fetcher.js +45 -0
  101. package/dist/{internal/preact → preact}/form.d.ts +5 -6
  102. package/dist/preact/form.js +21 -0
  103. package/dist/{internal/preact → preact}/hooks.d.ts +7 -6
  104. package/dist/preact/hooks.js +25 -0
  105. package/dist/preact/index.d.ts +18 -0
  106. package/dist/preact/index.js +15 -0
  107. package/dist/preact/lazy-route.d.ts +19 -0
  108. package/dist/preact/lazy-route.js +51 -0
  109. package/dist/preact/navigation-state.d.ts +2 -0
  110. package/dist/preact/navigation-state.js +9 -0
  111. package/dist/preact/navigation.d.ts +28 -0
  112. package/dist/preact/navigation.js +60 -0
  113. package/dist/preact/root-context.d.ts +13 -0
  114. package/dist/preact/root-context.js +9 -0
  115. package/dist/preact/root-data.d.ts +8 -0
  116. package/dist/preact/root-data.js +28 -0
  117. package/dist/preact/route-hooks.d.ts +32 -0
  118. package/dist/preact/route-hooks.js +94 -0
  119. package/dist/preact/route-types.d.ts +54 -0
  120. package/dist/preact/router.d.ts +16 -0
  121. package/dist/preact/router.js +63 -0
  122. package/dist/{internal/preact → preact}/routes.d.ts +1 -1
  123. package/dist/preact/routes.js +12 -0
  124. package/dist/{internal/preact → preact}/runtime.js +1 -1
  125. package/dist/react/context.d.ts +18 -0
  126. package/dist/react/context.js +23 -0
  127. package/dist/react/data.d.ts +5 -0
  128. package/dist/react/data.js +3 -0
  129. package/dist/{internal/react → react}/fetcher.d.ts +2 -2
  130. package/dist/react/fetcher.js +34 -0
  131. package/dist/{internal/react → react}/form.d.ts +5 -6
  132. package/dist/react/form.js +21 -0
  133. package/dist/react/hooks.js +25 -0
  134. package/dist/{internal/react → react}/index.d.ts +1 -1
  135. package/dist/react/index.js +10 -0
  136. package/dist/{internal/react → react}/link.d.ts +2 -2
  137. package/dist/react/link.js +27 -0
  138. package/dist/react/resources/components.d.ts +64 -0
  139. package/dist/react/resources/components.js +239 -0
  140. package/dist/{internal/react → react}/routes.d.ts +1 -1
  141. package/dist/react/routes.js +12 -0
  142. package/dist/react/runtime.js +1 -0
  143. package/dist/server/application.d.ts +4 -0
  144. package/dist/server/application.js +119 -0
  145. package/dist/server/assets.d.ts +5 -0
  146. package/dist/server/assets.js +7 -0
  147. package/dist/server/handler.d.ts +19 -0
  148. package/dist/server/handler.js +22 -0
  149. package/dist/server/http.d.ts +4 -0
  150. package/dist/server/http.js +27 -0
  151. package/dist/server/index.js +76 -0
  152. package/dist/server/node.d.ts +3 -0
  153. package/dist/server/node.js +3 -0
  154. package/dist/server/preact.d.ts +3 -0
  155. package/dist/server/preact.js +4 -0
  156. package/dist/vite/application/browser-boundary.d.ts +3 -0
  157. package/dist/vite/application/browser-boundary.js +35 -0
  158. package/dist/vite/application/lazy-files.d.ts +2 -0
  159. package/dist/vite/application/lazy-files.js +87 -0
  160. package/dist/vite/application/vite.d.ts +9 -0
  161. package/dist/vite/application/vite.js +101 -0
  162. package/dist/vite/index.js +104 -0
  163. package/dist/{preact-vite.d.ts → vite/preact.d.ts} +5 -3
  164. package/dist/vite/preact.js +15 -0
  165. package/dist/{react-vite.d.ts → vite/react.d.ts} +3 -3
  166. package/dist/vite/react.js +14 -0
  167. package/dist/vite/transport.js +35 -0
  168. package/jsr.json +28 -16
  169. package/package.json +116 -36
  170. package/dist/client.d.ts +0 -2
  171. package/dist/client.js +0 -2
  172. package/dist/core.d.ts +0 -2
  173. package/dist/core.js +0 -2
  174. package/dist/files.d.ts +0 -2
  175. package/dist/files.js +0 -2
  176. package/dist/internal/client/deserialize.js +0 -11
  177. package/dist/internal/client/index.js +0 -44
  178. package/dist/internal/core/errors.js +0 -6
  179. package/dist/internal/core/index.js +0 -7
  180. package/dist/internal/core/match.js +0 -27
  181. package/dist/internal/core/router.js +0 -189
  182. package/dist/internal/core/serialize.js +0 -13
  183. package/dist/internal/core/types.js +0 -5
  184. package/dist/internal/core/url.d.ts +0 -2
  185. package/dist/internal/core/url.js +0 -6
  186. package/dist/internal/files/index.js +0 -77
  187. package/dist/internal/preact/context.d.ts +0 -18
  188. package/dist/internal/preact/context.js +0 -20
  189. package/dist/internal/preact/fetcher.js +0 -28
  190. package/dist/internal/preact/form.js +0 -41
  191. package/dist/internal/preact/hooks.js +0 -35
  192. package/dist/internal/preact/index.d.ts +0 -9
  193. package/dist/internal/preact/index.js +0 -7
  194. package/dist/internal/preact/link.d.ts +0 -12
  195. package/dist/internal/preact/link.js +0 -28
  196. package/dist/internal/preact/routes.js +0 -22
  197. package/dist/internal/react/context.d.ts +0 -18
  198. package/dist/internal/react/context.js +0 -20
  199. package/dist/internal/react/fetcher.js +0 -28
  200. package/dist/internal/react/form.js +0 -41
  201. package/dist/internal/react/hooks.js +0 -35
  202. package/dist/internal/react/index.js +0 -7
  203. package/dist/internal/react/link.js +0 -28
  204. package/dist/internal/react/routes.js +0 -22
  205. package/dist/internal/react/runtime.js +0 -2
  206. package/dist/internal/server/index.js +0 -69
  207. package/dist/internal/vite/index.js +0 -132
  208. package/dist/internal/vite/transport.js +0 -42
  209. package/dist/preact-client.d.ts +0 -2
  210. package/dist/preact-client.js +0 -2
  211. package/dist/preact-server.d.ts +0 -2
  212. package/dist/preact-server.js +0 -2
  213. package/dist/preact-vite.js +0 -9
  214. package/dist/preact.d.ts +0 -2
  215. package/dist/preact.js +0 -2
  216. package/dist/react-client.d.ts +0 -2
  217. package/dist/react-client.js +0 -2
  218. package/dist/react-server.d.ts +0 -2
  219. package/dist/react-server.js +0 -2
  220. package/dist/react-vite.js +0 -9
  221. package/dist/react.d.ts +0 -2
  222. package/dist/react.js +0 -2
  223. package/dist/server.d.ts +0 -2
  224. package/dist/server.js +0 -2
  225. package/dist/utils.d.ts +0 -5
  226. package/dist/utils.js +0 -5
  227. package/dist/vite.d.ts +0 -2
  228. package/dist/vite.js +0 -2
  229. package/src/client.ts +0 -2
  230. package/src/core.ts +0 -2
  231. package/src/files.ts +0 -2
  232. package/src/internal/core/url.ts +0 -6
  233. package/src/internal/preact/context.ts +0 -46
  234. package/src/internal/preact/form.ts +0 -79
  235. package/src/internal/preact/link.ts +0 -46
  236. package/src/internal/react/context.ts +0 -46
  237. package/src/internal/react/form.ts +0 -79
  238. package/src/internal/react/index.ts +0 -13
  239. package/src/preact-client.ts +0 -2
  240. package/src/preact-server.ts +0 -2
  241. package/src/preact.ts +0 -2
  242. package/src/react-client.ts +0 -2
  243. package/src/react-server.ts +0 -2
  244. package/src/react.ts +0 -2
  245. package/src/server.ts +0 -2
  246. package/src/utils.ts +0 -5
  247. package/src/vite.ts +0 -2
  248. /package/{src/internal → .generated}/client/deserialize.ts +0 -0
  249. /package/{src/internal → .generated}/core/match.ts +0 -0
  250. /package/{src/internal → .generated}/files/index.ts +0 -0
  251. /package/{src/internal → .generated}/preact/runtime.ts +0 -0
  252. /package/{src/internal → .generated}/react/runtime.ts +0 -0
  253. /package/{src/internal → .generated}/vite/index.ts +0 -0
  254. /package/{src/internal → .generated}/vite/transport.ts +0 -0
  255. /package/dist/{internal/client → client}/deserialize.d.ts +0 -0
  256. /package/dist/{internal/core → core}/errors.d.ts +0 -0
  257. /package/dist/{internal/core → core}/match.d.ts +0 -0
  258. /package/dist/{internal/files → files}/index.d.ts +0 -0
  259. /package/dist/{internal/preact → preact}/runtime.d.ts +0 -0
  260. /package/dist/{internal/react → react}/hooks.d.ts +0 -0
  261. /package/dist/{internal/react → react}/runtime.d.ts +0 -0
  262. /package/dist/{internal/server → server}/index.d.ts +0 -0
  263. /package/dist/{internal/vite → vite}/index.d.ts +0 -0
  264. /package/dist/{internal/vite → vite}/transport.d.ts +0 -0
@@ -0,0 +1,87 @@
1
+ import { flatRoutes } from "../../files/index.js";
2
+ import { readFile } from "node:fs/promises";
3
+ import { resolve } from "node:path";
4
+ import { transformWithOxc } from "vite";
5
+ //#region .generated/vite/application/lazy-files.ts
6
+ function lazyFiles(directory) {
7
+ let root;
8
+ const modules = /* @__PURE__ */ new Map();
9
+ return {
10
+ name: "smolter-lazy-routes",
11
+ enforce: "pre",
12
+ configResolved(config) {
13
+ root = resolve(config.root, directory);
14
+ },
15
+ generateBundle(_options, bundle) {
16
+ if (!modules.size) return;
17
+ const routes = [];
18
+ for (const chunk of Object.values(bundle)) {
19
+ if (chunk.type !== "chunk" || !chunk.facadeModuleId) continue;
20
+ const path = modules.get(chunk.facadeModuleId);
21
+ if (!path) continue;
22
+ const files = /* @__PURE__ */ new Set();
23
+ function visit(file) {
24
+ if (files.has(file)) return;
25
+ files.add(file);
26
+ const dependency = bundle[file];
27
+ if (dependency?.type !== "chunk") return;
28
+ for (const imported of dependency.imports) visit(imported);
29
+ }
30
+ visit(chunk.fileName);
31
+ routes.push({
32
+ path,
33
+ files: [...files]
34
+ });
35
+ }
36
+ const paths = [...modules.values()];
37
+ routes.sort((left, right) => paths.indexOf(left.path) - paths.indexOf(right.path));
38
+ this.emitFile({
39
+ type: "asset",
40
+ fileName: ".vite/route-assets.json",
41
+ source: JSON.stringify(routes)
42
+ });
43
+ },
44
+ async load(id, options) {
45
+ if (id !== "\0virtual:smolter/routes" || options?.ssr) return;
46
+ const routes = await flatRoutes(root);
47
+ if (routes instanceof Error) return this.error(routes.message);
48
+ const definitions = [];
49
+ for (const route of routes) {
50
+ this.addWatchFile(route.file);
51
+ const source = await readFile(route.file, "utf8");
52
+ const transformed = await transformWithOxc(source, route.file);
53
+ const ast = this.parse(transformed.code);
54
+ const fields = { path: route.path };
55
+ for (const statement of ast.body) {
56
+ if (statement.type === "ExportDefaultDeclaration") {
57
+ fields.default = true;
58
+ continue;
59
+ }
60
+ if (statement.type !== "ExportNamedDeclaration") continue;
61
+ if (statement.declaration?.type === "FunctionDeclaration") {
62
+ const name = statement.declaration.id?.name;
63
+ if (name) fields[name] = true;
64
+ }
65
+ for (const specifier of statement.specifiers) {
66
+ const name = specifier.exported.type === "Identifier" ? specifier.exported.name : specifier.exported.value;
67
+ fields[name] = true;
68
+ }
69
+ if (statement.declaration?.type !== "VariableDeclaration") continue;
70
+ for (const declaration of statement.declaration.declarations) {
71
+ if (declaration.id.type === "Identifier") fields[declaration.id.name] = true;
72
+ if (declaration.id.type !== "Identifier" || declaration.init?.type !== "Literal" || typeof declaration.init.value !== "string") continue;
73
+ const name = declaration.id.name;
74
+ if (name === "id") fields.id = declaration.init.value;
75
+ if (name === "routePath") fields.path = declaration.init.value;
76
+ }
77
+ }
78
+ const file = `${route.file.replaceAll("\\", "/")}?smolter-client`;
79
+ if (fields.default) modules.set(file, String(fields.path));
80
+ definitions.push(`lazyRoute(${JSON.stringify(fields)}, () => import(${JSON.stringify(file)}))`);
81
+ }
82
+ return ["import { lazyRoute } from \"@kasperrt/smolter/preact/lazy-route\";", `export default [${definitions.join(",\n")}];`].join("\n");
83
+ }
84
+ };
85
+ }
86
+ //#endregion
87
+ export { lazyFiles };
@@ -0,0 +1,9 @@
1
+ import type { Plugin, UserConfig } from "vite";
2
+ export interface ApplicationOptions {
3
+ directory?: string;
4
+ ssr?: boolean;
5
+ splitRoutes?: boolean;
6
+ resourceRequest?: (url: string) => boolean;
7
+ }
8
+ export declare const preactConfig: UserConfig;
9
+ export declare function applicationPlugins({ directory, ssr, splitRoutes, resourceRequest, }?: ApplicationOptions): Plugin[];
@@ -0,0 +1,101 @@
1
+ import { routerPlugin } from "../index.js";
2
+ import { send, webRequest } from "../../server/http.js";
3
+ import { browserBoundary } from "./browser-boundary.js";
4
+ import { lazyFiles } from "./lazy-files.js";
5
+ //#region .generated/vite/application/vite.ts
6
+ var preactConfig = {
7
+ resolve: {
8
+ alias: [
9
+ {
10
+ find: /^react\/jsx-dev-runtime$/,
11
+ replacement: "preact/jsx-dev-runtime"
12
+ },
13
+ {
14
+ find: /^react\/jsx-runtime$/,
15
+ replacement: "preact/jsx-runtime"
16
+ },
17
+ {
18
+ find: /^react-dom\/client$/,
19
+ replacement: "preact/compat/client"
20
+ },
21
+ {
22
+ find: /^react-dom\/server$/,
23
+ replacement: "preact-render-to-string"
24
+ },
25
+ {
26
+ find: /^react-dom$/,
27
+ replacement: "preact/compat"
28
+ },
29
+ {
30
+ find: /^react$/,
31
+ replacement: "preact/compat"
32
+ }
33
+ ],
34
+ dedupe: ["preact", "@kasperrt/smolter"]
35
+ },
36
+ ssr: { noExternal: ["@kasperrt/smolter"] },
37
+ optimizeDeps: { include: [
38
+ "preact",
39
+ "preact/hooks",
40
+ "preact/compat",
41
+ "preact/jsx-runtime",
42
+ "@kasperrt/smolter/preact",
43
+ "@kasperrt/smolter/preact/lazy-route"
44
+ ] }
45
+ };
46
+ function applicationPlugins({ directory = "src/routes", ssr = true, splitRoutes = true, resourceRequest } = {}) {
47
+ return [
48
+ {
49
+ name: "smolter-application",
50
+ enforce: "pre",
51
+ config: () => preactConfig,
52
+ transformIndexHtml: {
53
+ order: "post",
54
+ handler(html, context) {
55
+ if (!context.server || !ssr) return html;
56
+ const client = html.match(/\s*<script\b[^>]*src="[^"]*\/@vite\/client"[^>]*><\/script>\s*/)?.[0];
57
+ if (!client) return html;
58
+ return html.replace(client, "").replace("</body>", `${client.trim()}</body>`);
59
+ }
60
+ },
61
+ configureServer(server) {
62
+ server.middlewares.use((incoming, outgoing, next) => {
63
+ if (!incoming.url?.startsWith("/_smolter?")) return next();
64
+ const run = async () => {
65
+ const target = new URL(incoming.url, "http://local").searchParams.get("url");
66
+ if (!target?.startsWith("/") || target.startsWith("//")) return send(new Response("Invalid route URL", { status: 400 }), outgoing);
67
+ const request = await webRequest(incoming, outgoing, target);
68
+ const [{ default: routes }, { dispatchRoute }] = await Promise.all([server.ssrLoadModule("virtual:smolter/routes"), server.ssrLoadModule("@kasperrt/smolter/preact/server")]);
69
+ await send(await dispatchRoute(request, routes), outgoing);
70
+ };
71
+ run().catch(next);
72
+ });
73
+ if (!ssr) return;
74
+ return () => {
75
+ server.middlewares.use((incoming, outgoing, next) => {
76
+ if (!incoming.headers.accept?.includes("text/html") && !resourceRequest?.(incoming.originalUrl ?? incoming.url ?? "")) return next();
77
+ const run = async () => {
78
+ const request = await webRequest(incoming, outgoing);
79
+ const response = await (await server.ssrLoadModule("/src/entry.server.tsx")).default(request, "/src/entry.client.tsx");
80
+ if (!response.headers.get("Content-Type")?.includes("text/html")) return send(response, outgoing);
81
+ const html = await server.transformIndexHtml(incoming.url, await response.text());
82
+ await send(new Response(html, {
83
+ status: response.status,
84
+ headers: response.headers
85
+ }), outgoing);
86
+ };
87
+ run().catch(next);
88
+ });
89
+ };
90
+ }
91
+ },
92
+ browserBoundary(),
93
+ ...splitRoutes ? [lazyFiles(directory)] : [],
94
+ routerPlugin({
95
+ directory,
96
+ framework: "preact"
97
+ })
98
+ ];
99
+ }
100
+ //#endregion
101
+ export { applicationPlugins, preactConfig };
@@ -0,0 +1,104 @@
1
+ import { flatRoutes } from "../files/index.js";
2
+ import { configureTransport } from "./transport.js";
3
+ import { readFile } from "node:fs/promises";
4
+ import { resolve } from "node:path";
5
+ import { transformWithOxc } from "vite";
6
+ //#region .generated/vite/index.ts
7
+ /** Build-time flat route discovery and the optional server data transport. */
8
+ function routerPlugin({ directory = "src/routes", clientRoutes, serverRoutes, framework = "react" } = {}) {
9
+ let root;
10
+ let projectRoot;
11
+ const publicId = "virtual:smolter/routes";
12
+ const virtualId = `\0${publicId}`;
13
+ const suffix = "?smolter-client";
14
+ return {
15
+ name: "smolter-flat-routes",
16
+ enforce: "pre",
17
+ configResolved(config) {
18
+ projectRoot = config.root;
19
+ root = resolve(config.root, directory);
20
+ },
21
+ resolveId(id) {
22
+ if (id === publicId) return virtualId;
23
+ },
24
+ async load(id, options) {
25
+ if (id.endsWith(suffix)) {
26
+ const file = id.slice(0, -15);
27
+ const transformed = await transformWithOxc(await readFile(file, "utf8"), file);
28
+ const ast = this.parse(transformed.code);
29
+ const edits = [];
30
+ for (const statement of ast.body) {
31
+ if (statement.type === "ExportAllDeclaration") return this.error(`Use explicit named exports in ${file}; export * cannot be safely split between server and browser.`);
32
+ if (statement.type !== "ExportNamedDeclaration") continue;
33
+ const server = statement.specifiers.filter((specifier) => {
34
+ const name = specifier.exported.type === "Identifier" ? specifier.exported.name : specifier.exported.value;
35
+ return name === "loader" || name === "action";
36
+ });
37
+ if (!server.length) {
38
+ const declaration = statement.declaration;
39
+ const name = declaration?.type === "FunctionDeclaration" ? declaration.id?.name : void 0;
40
+ const variables = declaration?.type === "VariableDeclaration" ? declaration.declarations : [];
41
+ if (name === "loader" || name === "action" || variables.some((item) => item.id.type === "Identifier" && ["loader", "action"].includes(item.id.name))) return this.error(`Re-export loader/action from separate files in ${file}; inline server handlers cannot enter a browser module.`);
42
+ continue;
43
+ }
44
+ if (!statement.source || server.length !== statement.specifiers.length) return this.error(`Use separate export { loader } / export { action } statements in ${file}.`);
45
+ edits.push([statement.start, statement.end]);
46
+ }
47
+ let code = transformed.code;
48
+ for (const [start, end] of edits.reverse()) code = code.slice(0, start) + code.slice(end);
49
+ return {
50
+ code,
51
+ map: null
52
+ };
53
+ }
54
+ if (id !== virtualId) return;
55
+ if (clientRoutes || serverRoutes) {
56
+ if (!clientRoutes || !serverRoutes) return this.error("Specify both clientRoutes and serverRoutes for manual routing.");
57
+ const entry = resolve(projectRoot, options?.ssr ? serverRoutes : clientRoutes).replaceAll("\\", "/");
58
+ return `export { routes as default } from ${JSON.stringify(entry)};`;
59
+ }
60
+ const routes = await flatRoutes(root);
61
+ if (routes instanceof Error) return this.error(routes.message);
62
+ const imports = [];
63
+ const definitions = [];
64
+ for (const [index, route] of routes.entries()) {
65
+ this.addWatchFile(route.file);
66
+ const file = route.file.replaceAll("\\", "/");
67
+ const transformed = await transformWithOxc(await readFile(file, "utf8"), file);
68
+ const ast = this.parse(transformed.code);
69
+ const handlers = {};
70
+ for (const statement of ast.body) {
71
+ if (statement.type !== "ExportNamedDeclaration") continue;
72
+ for (const specifier of statement.specifiers) {
73
+ const name = specifier.exported.type === "Identifier" ? specifier.exported.name : specifier.exported.value;
74
+ if (name === "loader" || name === "action") handlers[name] = true;
75
+ }
76
+ }
77
+ imports.push(`import * as route${index} from ${JSON.stringify(file + (options?.ssr ? "" : suffix))};`);
78
+ definitions.push(options?.ssr || !Object.keys(handlers).length ? `{ ...route${index}, path: ${JSON.stringify(route.path)} }` : `serverRoute(${JSON.stringify(route.path)}, route${index}, ${JSON.stringify(handlers)})`);
79
+ }
80
+ if (!options?.ssr && definitions.some((definition) => definition.startsWith("serverRoute("))) imports.unshift(`import { serverRoute } from "@kasperrt/smolter/${framework}/client";`);
81
+ return `${imports.join("\n")}\nexport default [${definitions.join(",\n")}];\n`;
82
+ },
83
+ configureServer(server) {
84
+ server.watcher.add(root);
85
+ const refresh = (file) => {
86
+ if (!file.startsWith(`${root}/`)) return;
87
+ const module = server.moduleGraph.getModuleById(virtualId);
88
+ if (module) server.moduleGraph.invalidateModule(module);
89
+ server.ws.send({ type: "full-reload" });
90
+ };
91
+ server.watcher.on("add", refresh);
92
+ server.watcher.on("change", refresh);
93
+ server.watcher.on("unlink", refresh);
94
+ server.httpServer?.once("close", () => {
95
+ server.watcher.off("add", refresh);
96
+ server.watcher.off("change", refresh);
97
+ server.watcher.off("unlink", refresh);
98
+ });
99
+ configureTransport(server, publicId, framework);
100
+ }
101
+ };
102
+ }
103
+ //#endregion
104
+ export { routerPlugin };
@@ -1,9 +1,11 @@
1
1
  /** Optional flat-file routing for Preact builds. @module */
2
2
  import type { Plugin } from "vite";
3
- import { type FileRouterOptions } from "./internal/vite/index.ts";
4
- export * from "./internal/files/index.ts";
5
- export type { FileRouterOptions } from "./internal/vite/index.ts";
3
+ import { type FileRouterOptions } from "./index.ts";
4
+ export * from "../files/index.ts";
5
+ export type { FileRouterOptions } from "./index.ts";
6
6
  /** Configure flat routes and server transport for Preact. */
7
7
  export declare function routerPlugin(options?: FileRouterOptions): Plugin;
8
8
  /** File routing is the default; this alias keeps configuration explicit. */
9
9
  export declare const fileRouter: typeof routerPlugin;
10
+ export type { ApplicationOptions } from "./application/vite.ts";
11
+ export { applicationPlugins, preactConfig, } from "./application/vite.ts";
@@ -0,0 +1,15 @@
1
+ import { flatRoutes } from "../files/index.js";
2
+ import { routerPlugin as routerPlugin$1 } from "./index.js";
3
+ import { applicationPlugins, preactConfig } from "./application/vite.js";
4
+ //#region .generated/vite/preact.ts
5
+ /** Configure flat routes and server transport for Preact. */
6
+ function routerPlugin(options = {}) {
7
+ return routerPlugin$1({
8
+ ...options,
9
+ framework: "preact"
10
+ });
11
+ }
12
+ /** File routing is the default; this alias keeps configuration explicit. */
13
+ var fileRouter = routerPlugin;
14
+ //#endregion
15
+ export { applicationPlugins, fileRouter, flatRoutes, preactConfig, routerPlugin };
@@ -1,8 +1,8 @@
1
1
  /** Optional flat-file routing for React builds. @module */
2
2
  import type { Plugin } from "vite";
3
- import { type FileRouterOptions } from "./internal/vite/index.ts";
4
- export * from "./internal/files/index.ts";
5
- export type { FileRouterOptions } from "./internal/vite/index.ts";
3
+ import { type FileRouterOptions } from "./index.ts";
4
+ export * from "../files/index.ts";
5
+ export type { FileRouterOptions } from "./index.ts";
6
6
  /** Configure flat routes and server transport for React. */
7
7
  export declare function routerPlugin(options?: FileRouterOptions): Plugin;
8
8
  /** File routing is the default; this alias keeps configuration explicit. */
@@ -0,0 +1,14 @@
1
+ import { flatRoutes } from "../files/index.js";
2
+ import { routerPlugin as routerPlugin$1 } from "./index.js";
3
+ //#region .generated/vite/react.ts
4
+ /** Configure flat routes and server transport for React. */
5
+ function routerPlugin(options = {}) {
6
+ return routerPlugin$1({
7
+ ...options,
8
+ framework: "react"
9
+ });
10
+ }
11
+ /** File routing is the default; this alias keeps configuration explicit. */
12
+ var fileRouter = routerPlugin;
13
+ //#endregion
14
+ export { fileRouter, flatRoutes, routerPlugin };
@@ -0,0 +1,35 @@
1
+ //#region .generated/vite/transport.ts
2
+ function configureTransport(server, publicId, framework = "react") {
3
+ server.middlewares.use((incoming, outgoing, next) => {
4
+ if (!incoming.url?.startsWith("/_smolter?")) return next();
5
+ const dispatch = async () => {
6
+ const origin = `http://${incoming.headers.host}`;
7
+ const target = new URL(incoming.url, origin).searchParams.get("url");
8
+ if (!target?.startsWith("/") || target.startsWith("//")) {
9
+ outgoing.statusCode = 400;
10
+ outgoing.end("Invalid route URL");
11
+ return;
12
+ }
13
+ const headers = new Headers();
14
+ for (const [name, value] of Object.entries(incoming.headers)) if (value) headers.set(name, Array.isArray(value) ? value.join(", ") : value);
15
+ const chunks = [];
16
+ for await (const chunk of incoming) chunks.push(chunk);
17
+ const controller = new AbortController();
18
+ outgoing.once("close", () => controller.abort());
19
+ const request = new Request(new URL(target, origin), {
20
+ signal: controller.signal,
21
+ method: incoming.method,
22
+ headers,
23
+ ...incoming.method !== "GET" && { body: new Uint8Array(Buffer.concat(chunks)) }
24
+ });
25
+ const [{ default: routes }, { handleRequest }] = await Promise.all([server.ssrLoadModule(publicId), server.ssrLoadModule(`@kasperrt/smolter/${framework}/server`)]);
26
+ const response = await handleRequest(routes, request);
27
+ outgoing.statusCode = response.status;
28
+ response.headers.forEach((value, name) => outgoing.setHeader(name, value));
29
+ outgoing.end(new Uint8Array(await response.arrayBuffer()));
30
+ };
31
+ dispatch().catch(next);
32
+ });
33
+ }
34
+ //#endregion
35
+ export { configureTransport };
package/jsr.json CHANGED
@@ -1,27 +1,39 @@
1
1
  {
2
2
  "name": "@kasperrt/smolter",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "exports": {
5
- "./react": "./src/react.ts",
6
- "./react/server": "./src/react-server.ts",
7
- "./preact": "./src/preact.ts",
8
- "./preact/server": "./src/preact-server.ts",
9
- "./react/client": "./src/react-client.ts",
10
- "./preact/client": "./src/preact-client.ts",
11
- "./utils": "./src/utils.ts"
5
+ "./react": "./.generated/react/index.ts",
6
+ "./preact": "./.generated/preact/index.ts",
7
+ "./react/server": "./.generated/server/index.ts",
8
+ "./preact/server": "./.generated/server/preact.ts",
9
+ "./react/client": "./.generated/client/index.ts",
10
+ "./preact/client": "./.generated/preact/client.tsx",
11
+ "./utils": "./.generated/core/utils.ts",
12
+ "./utils/http": "./.generated/server/node.ts",
13
+ "./utils/assets": "./.generated/server/assets.ts",
14
+ "./utils/responses": "./.generated/core/responses.ts",
15
+ "./react/data": "./.generated/react/data.ts",
16
+ "./preact/lazy-route": "./.generated/preact/lazy-route.ts",
17
+ "./preact/document": "./.generated/preact/document.tsx"
12
18
  },
13
19
  "imports": {
14
20
  "react": "npm:react@19.3.1",
15
- "preact/compat": "npm:preact@11.0.1/compat"
21
+ "preact/compat": "npm:preact@11.0.1/compat",
22
+ "preact": "npm:preact@11.0.1",
23
+ "preact/hooks": "npm:preact@11.0.1/hooks",
24
+ "preact/jsx-runtime": "npm:preact@11.0.1/jsx-runtime",
25
+ "preact-render-to-string": "npm:preact-render-to-string@6.8.1",
26
+ "react/jsx-runtime": "npm:react@19.3.1/jsx-runtime"
16
27
  },
17
28
  "publish": {
18
- "include": ["src", "README.md", "LICENSE", "jsr.json"],
19
- "exclude": [
20
- "src/vite.ts",
21
- "src/react-vite.ts",
22
- "src/preact-vite.ts",
23
- "src/internal/vite"
24
- ]
29
+ "include": [
30
+ ".generated",
31
+ "README.md",
32
+ "LICENSE",
33
+ "jsr.json",
34
+ "CHANGELOG.md"
35
+ ],
36
+ "exclude": [".generated/vite"]
25
37
  },
26
38
  "license": "MIT",
27
39
  "compatibility": {
package/package.json CHANGED
@@ -1,67 +1,144 @@
1
1
  {
2
2
  "name": "@kasperrt/smolter",
3
- "version": "0.2.0",
4
- "description": "A tiny typed data router for React and Preact.",
3
+ "packageManager": "pnpm@11.8.0",
5
4
  "type": "module",
5
+ "scripts": {
6
+ "build": "vite build && tsc -p tsconfig.build.json",
7
+ "dev": "pnpm build && pnpm --parallel --filter \"@smolter/example-*\" dev",
8
+ "fix": "biome check --write .",
9
+ "lint": "biome check .",
10
+ "typecheck": "pnpm build && tsc --noEmit",
11
+ "test": "pnpm build && vitest run --project unit",
12
+ "coverage": "pnpm build && vitest run --coverage",
13
+ "smoke": "playwright test",
14
+ "size": "pnpm build && node scripts/size.mjs",
15
+ "check": "pnpm lint && pnpm typecheck && pnpm test && pnpm size",
16
+ "version:patch": "node scripts/version.mjs patch",
17
+ "version:minor": "node scripts/version.mjs minor",
18
+ "version:release": "node scripts/version.mjs release",
19
+ "pack:smoke": "node scripts/pack-smoke.mjs",
20
+ "build:examples": "pnpm --filter \"@smolter/example-*\" build",
21
+ "smoke:production": "node scripts/production-smoke.mjs",
22
+ "jsr:check": "node scripts/jsr.mjs",
23
+ "build:site": "vite build --config apps/site/vite.config.ts && pnpm --filter @smolter/example-react build --mode hosted && pnpm --filter @smolter/example-preact build --mode hosted",
24
+ "smoke:site": "node scripts/site-smoke.mjs",
25
+ "docs:badges": "node scripts/readme-badges.mjs",
26
+ "docs:reference": "pnpm build && node scripts/docs-reference.mjs",
27
+ "docs:check": "pnpm build && node scripts/docs-reference.mjs --check"
28
+ },
29
+ "devDependencies": {
30
+ "@biomejs/biome": "2.5.15",
31
+ "@playwright/test": "1.64.1",
32
+ "@smolter/client": "workspace:*",
33
+ "@smolter/core": "workspace:*",
34
+ "@smolter/files": "workspace:*",
35
+ "@smolter/preact": "workspace:*",
36
+ "@smolter/react": "workspace:*",
37
+ "@smolter/server": "workspace:*",
38
+ "@smolter/vite": "workspace:*",
39
+ "@types/node": "24.10.1",
40
+ "@types/react": "19.3.0",
41
+ "@types/react-dom": "19.3.0",
42
+ "@vitejs/plugin-react": "6.1.2",
43
+ "@vitest/browser-playwright": "5.0.5",
44
+ "@vitest/coverage-v8": "5.0.5",
45
+ "esbuild": "0.28.2",
46
+ "oxc-transform-react": "0.152.0",
47
+ "preact": "11.0.1",
48
+ "preact-render-to-string": "6.8.1",
49
+ "react": "19.3.1",
50
+ "react-dom": "19.3.1",
51
+ "react-router": "8.4.0",
52
+ "terser": "5.44.1",
53
+ "typescript": "6.0.3",
54
+ "vite": "8.3.4",
55
+ "vitest": "5.0.5",
56
+ "wouter": "3.13.0",
57
+ "wouter-preact": "3.13.0"
58
+ },
59
+ "version": "0.3.0",
60
+ "description": "A tiny typed data router for React and Preact.",
6
61
  "sideEffects": false,
7
62
  "license": "MIT",
8
63
  "repository": {
9
64
  "type": "git",
10
- "url": "git+https://github.com/kasperrt/smolter.git",
11
- "directory": "packages/smolter"
65
+ "url": "git+https://github.com/kasperrt/smolter.git"
12
66
  },
13
67
  "exports": {
14
68
  "./react": {
15
- "types": "./dist/react.d.ts",
16
- "import": "./dist/react.js"
17
- },
18
- "./react/server": {
19
- "types": "./dist/react-server.d.ts",
20
- "import": "./dist/react-server.js"
21
- },
22
- "./react/vite": {
23
- "types": "./dist/react-vite.d.ts",
24
- "import": "./dist/react-vite.js"
69
+ "types": "./dist/react/index.d.ts",
70
+ "import": "./dist/react/index.js"
25
71
  },
26
72
  "./preact": {
27
- "types": "./dist/preact.d.ts",
28
- "import": "./dist/preact.js"
73
+ "types": "./dist/preact/index.d.ts",
74
+ "import": "./dist/preact/index.js"
29
75
  },
30
- "./preact/server": {
31
- "types": "./dist/preact-server.d.ts",
32
- "import": "./dist/preact-server.js"
76
+ "./react/server": {
77
+ "types": "./dist/server/index.d.ts",
78
+ "import": "./dist/server/index.js"
33
79
  },
34
- "./preact/vite": {
35
- "types": "./dist/preact-vite.d.ts",
36
- "import": "./dist/preact-vite.js"
80
+ "./preact/server": {
81
+ "types": "./dist/server/preact.d.ts",
82
+ "import": "./dist/server/preact.js"
37
83
  },
38
84
  "./react/client": {
39
- "types": "./dist/react-client.d.ts",
40
- "import": "./dist/react-client.js"
85
+ "types": "./dist/client/index.d.ts",
86
+ "import": "./dist/client/index.js"
41
87
  },
42
88
  "./preact/client": {
43
- "types": "./dist/preact-client.d.ts",
44
- "import": "./dist/preact-client.js"
89
+ "types": "./dist/preact/client.d.ts",
90
+ "import": "./dist/preact/client.js"
91
+ },
92
+ "./react/vite": {
93
+ "types": "./dist/vite/react.d.ts",
94
+ "import": "./dist/vite/react.js"
95
+ },
96
+ "./preact/vite": {
97
+ "types": "./dist/vite/preact.d.ts",
98
+ "import": "./dist/vite/preact.js"
45
99
  },
46
100
  "./utils": {
47
- "types": "./dist/utils.d.ts",
48
- "import": "./dist/utils.js"
101
+ "types": "./dist/core/utils.d.ts",
102
+ "import": "./dist/core/utils.js"
103
+ },
104
+ "./utils/http": {
105
+ "types": "./dist/server/node.d.ts",
106
+ "import": "./dist/server/node.js"
107
+ },
108
+ "./utils/assets": {
109
+ "types": "./dist/server/assets.d.ts",
110
+ "import": "./dist/server/assets.js"
111
+ },
112
+ "./utils/responses": {
113
+ "types": "./dist/core/responses.d.ts",
114
+ "import": "./dist/core/responses.js"
115
+ },
116
+ "./react/data": {
117
+ "types": "./dist/react/data.d.ts",
118
+ "import": "./dist/react/data.js"
119
+ },
120
+ "./preact/lazy-route": {
121
+ "types": "./dist/preact/lazy-route.d.ts",
122
+ "import": "./dist/preact/lazy-route.js"
123
+ },
124
+ "./preact/document": {
125
+ "types": "./dist/preact/document.d.ts",
126
+ "import": "./dist/preact/document.js"
49
127
  }
50
128
  },
51
129
  "files": [
52
130
  "dist",
53
- "src",
131
+ ".generated",
54
132
  "README.md",
55
133
  "LICENSE",
56
- "jsr.json"
134
+ "jsr.json",
135
+ "CHANGELOG.md"
57
136
  ],
58
- "scripts": {
59
- "build": "node ../../scripts/build.mjs"
60
- },
61
137
  "peerDependencies": {
62
138
  "react": ">=18",
63
- "preact": ">=10.25.0",
64
- "vite": ">=8"
139
+ "preact": ">=11.0.1",
140
+ "vite": ">=8",
141
+ "preact-render-to-string": ">=6"
65
142
  },
66
143
  "peerDependenciesMeta": {
67
144
  "react": {
@@ -72,9 +149,12 @@
72
149
  },
73
150
  "vite": {
74
151
  "optional": true
152
+ },
153
+ "preact-render-to-string": {
154
+ "optional": true
75
155
  }
76
156
  },
77
- "author": "Kasper Rynning-Tønnesen <kasperrt@users.noreply.github.com> (https://github.com/kasperrt)",
157
+ "author": "Kasper Rynning-T\u00f8nnesen <kasperrt@users.noreply.github.com> (https://github.com/kasperrt)",
78
158
  "homepage": "https://kasperrt.github.io/smolter/",
79
159
  "bugs": {
80
160
  "url": "https://github.com/kasperrt/smolter/issues"
package/dist/client.d.ts DELETED
@@ -1,2 +0,0 @@
1
- /** Client entry point. @module */
2
- export * from "./internal/client/index.ts";
package/dist/client.js DELETED
@@ -1,2 +0,0 @@
1
- /** Client entry point. @module */
2
- export * from "./internal/client/index.js";