@kasperrt/smolter 0.0.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # smolter _
2
2
 
3
3
  <!-- bundlejs:start -->
4
- [![react gzip size](https://deno.bundlejs.com/?q=%40kasperrt%2Fsmolter%400.0.1%2Freact&treeshake=%5B*%5D&config=%7B%22compression%22%3A%22gzip%22%2C%22esbuild%22%3A%7B%22external%22%3A%5B%22react%22%2C%22react%2F*%22%5D%7D%7D&badge)](https://bundlejs.com/?q=%40kasperrt%2Fsmolter%400.0.1%2Freact&treeshake=%5B*%5D&config=%7B%22compression%22%3A%22gzip%22%2C%22esbuild%22%3A%7B%22external%22%3A%5B%22react%22%2C%22react%2F*%22%5D%7D%7D&bundle)
5
- [![preact gzip size](https://deno.bundlejs.com/?q=%40kasperrt%2Fsmolter%400.0.1%2Fpreact&treeshake=%5B*%5D&config=%7B%22compression%22%3A%22gzip%22%2C%22esbuild%22%3A%7B%22external%22%3A%5B%22preact%22%2C%22preact%2F*%22%5D%7D%7D&badge)](https://bundlejs.com/?q=%40kasperrt%2Fsmolter%400.0.1%2Fpreact&treeshake=%5B*%5D&config=%7B%22compression%22%3A%22gzip%22%2C%22esbuild%22%3A%7B%22external%22%3A%5B%22preact%22%2C%22preact%2F*%22%5D%7D%7D&bundle)
4
+ [![react gzip size](https://deno.bundlejs.com/?q=%40kasperrt%2Fsmolter%400.2.0%2Freact&treeshake=%5B*%5D&config=%7B%22compression%22%3A%22gzip%22%2C%22esbuild%22%3A%7B%22external%22%3A%5B%22react%22%2C%22react%2F*%22%5D%7D%7D&badge)](https://bundlejs.com/?q=%40kasperrt%2Fsmolter%400.2.0%2Freact&treeshake=%5B*%5D&config=%7B%22compression%22%3A%22gzip%22%2C%22esbuild%22%3A%7B%22external%22%3A%5B%22react%22%2C%22react%2F*%22%5D%7D%7D&bundle)
5
+ [![preact gzip size](https://deno.bundlejs.com/?q=%40kasperrt%2Fsmolter%400.2.0%2Fpreact&treeshake=%5B*%5D&config=%7B%22compression%22%3A%22gzip%22%2C%22esbuild%22%3A%7B%22external%22%3A%5B%22preact%22%2C%22preact%2F*%22%5D%7D%7D&badge)](https://bundlejs.com/?q=%40kasperrt%2Fsmolter%400.2.0%2Fpreact&treeshake=%5B*%5D&config=%7B%22compression%22%3A%22gzip%22%2C%22esbuild%22%3A%7B%22external%22%3A%5B%22preact%22%2C%22preact%2F*%22%5D%7D%7D&bundle)
6
6
  <!-- bundlejs:end -->
7
7
 
8
8
  A typed data router for React and Preact. **Zero runtime dependencies.**
@@ -14,6 +14,10 @@ loader; `shouldRevalidate` decides when that work is needed.
14
14
  Inspired by [wouter](https://github.com/molefrog/wouter)
15
15
  and [React Router](https://reactrouter.com/).
16
16
 
17
+ [Read the documentation](https://kasperrt.github.io/smolter/react/docs/) for guides and the complete API reference. The site itself uses smolter routes.
18
+
19
+ Start with [loaders](https://kasperrt.github.io/smolter/react/docs/loaders/), [actions](https://kasperrt.github.io/smolter/react/docs/actions/), [forms and validation](https://kasperrt.github.io/smolter/react/docs/forms/), or [fetcher reads and mutations](https://kasperrt.github.io/smolter/react/docs/fetchers/). Each guide connects the handler to an actual route URL and its consuming view.
20
+
17
21
  ## Contents
18
22
 
19
23
  - [Install](#install)
@@ -49,10 +53,10 @@ pnpm dev
49
53
 
50
54
  | App | Open | Data |
51
55
  | --- | --- | --- |
52
- | [React SPA](https://github.com/kasperrt/smolter/tree/main/apps/react) | http://127.0.0.1:5173 | Client loaders/actions, localStorage |
53
- | [Preact SPA](https://github.com/kasperrt/smolter/tree/main/apps/preact) | http://127.0.0.1:5174 | Client loaders/actions, localStorage |
54
- | [React SSR](https://github.com/kasperrt/smolter/tree/main/apps/react-ssr) | http://127.0.0.1:5175 | Server loaders/actions, hydration |
55
- | [Preact SSR](https://github.com/kasperrt/smolter/tree/main/apps/preact-ssr) | http://127.0.0.1:5176 | Server loaders/actions, hydration |
56
+ | [React SPA](./apps/react) | http://127.0.0.1:5173 | Client loaders/actions, localStorage |
57
+ | [Preact SPA](./apps/preact) | http://127.0.0.1:5174 | Client loaders/actions, localStorage |
58
+ | [React SSR](./apps/react-ssr) | http://127.0.0.1:5175 | Server loaders/actions, hydration |
59
+ | [Preact SSR](./apps/preact-ssr) | http://127.0.0.1:5176 | Server loaders/actions, hydration |
56
60
 
57
61
  Each app has its own entrypoints, routes, and data code. Only UI components are
58
62
  shared. Preact examples do not import React. Each includes:
@@ -81,7 +85,7 @@ or filename parser enters the browser bundle.
81
85
  ```ts
82
86
  // vite.config.ts
83
87
  import { defineConfig } from "vite";
84
- import { fileRouter } from "@kasperrt/smolter/vite";
88
+ import { fileRouter } from "@kasperrt/smolter/react/vite";
85
89
 
86
90
  export default defineConfig({
87
91
  plugins: [fileRouter({ directory: "src/routes" })],
@@ -121,12 +125,12 @@ For the virtual module, add this declaration to your app:
121
125
  // env.d.ts
122
126
  // Include vite/client in your tsconfig types as usual.
123
127
  declare module "virtual:smolter/routes" {
124
- const routes: import("@kasperrt/smolter/core").Route[];
128
+ const routes: import("@kasperrt/smolter/react").Route[];
125
129
  export default routes;
126
130
  }
127
131
  ```
128
132
 
129
- Outside Vite, `flatRoutes(directory)` from `@kasperrt/smolter/files` returns
133
+ Outside Vite, `flatRoutes(directory)` from `@kasperrt/smolter/react/vite` returns
130
134
  `{ path, file }[] | Error` for your build tool to turn into imports.
131
135
 
132
136
  ### Manual routes
@@ -184,7 +188,7 @@ With the optional server integration:
184
188
 
185
189
  ```ts
186
190
  // client-loader.ts
187
- import type { ClientLoaderArgs } from "@kasperrt/smolter/client";
191
+ import type { ClientLoaderArgs } from "@kasperrt/smolter/react/client";
188
192
  import type { loader } from "./loader";
189
193
 
190
194
  export async function clientLoader({ serverLoader }: ClientLoaderArgs<typeof loader>) {
@@ -194,7 +198,7 @@ export async function clientLoader({ serverLoader }: ClientLoaderArgs<typeof loa
194
198
 
195
199
  ```ts
196
200
  // client-action.ts
197
- import type { ClientActionArgs } from "@kasperrt/smolter/client";
201
+ import type { ClientActionArgs } from "@kasperrt/smolter/react/client";
198
202
  import type { action } from "./action";
199
203
 
200
204
  export async function clientAction({ serverAction }: ClientActionArgs<typeof action>) {
@@ -234,17 +238,17 @@ in a client bundle. Type-only imports from server handlers are safe. Do not
234
238
  import server code directly into client views or client handlers.
235
239
 
236
240
  The Vite dev plugin mounts `/_smolter?url=...`. Production hosts dispatch that
237
- endpoint through `handleRequest(routes, request)` from `@kasperrt/smolter/server`, using
241
+ endpoint through `handleRequest(routes, request)` from `@kasperrt/smolter/react/server`, using
238
242
  the requested route URL. Both SSR examples include a working host. The host
239
243
  owns authentication, session cookies, HTTP policy, and deployment.
240
244
 
241
245
  Manual server routes use `serverRoute<typeof import("./route")>(path, clientModule)`
242
- from `@kasperrt/smolter/client` in the browser and `defineRoute` on the server. This keeps
246
+ from `@kasperrt/smolter/react/client` in the browser and `defineRoute` on the server. This keeps
243
247
  the type connection without importing server code at runtime. To use manual
244
248
  tables with the dev transport:
245
249
 
246
250
  ```ts
247
- import { routerPlugin } from "@kasperrt/smolter/vite";
251
+ import { routerPlugin } from "@kasperrt/smolter/react/vite";
248
252
 
249
253
  routerPlugin({
250
254
  clientRoutes: "src/manual-browser-routes.ts",
@@ -366,7 +370,7 @@ remount the fallback during revalidation. Return a `Response` with a `Location`
366
370
  ## Server rendering
367
371
 
368
372
  ```tsx
369
- import { loadRouter, serialize } from "@kasperrt/smolter/server";
373
+ import { loadRouter, serialize } from "@kasperrt/smolter/react/server";
370
374
 
371
375
  const router = await loadRouter(routes, incomingRequest);
372
376
  const html = renderToString(
@@ -376,13 +380,13 @@ const hydrationJSON = serialize(router.getSnapshot());
376
380
  ```
377
381
 
378
382
  Create a fresh router per request. Embed the serialized snapshot in a JSON
379
- script, restore it with `deserialize` from `@kasperrt/smolter/client`, and pass it as
383
+ script, restore it with `deserialize` from `@kasperrt/smolter/react/client`, and pass it as
380
384
  `hydrationData` to `createRouter`. This preserves loader errors through hydration
381
385
  without exposing server stacks or causes. Error boundaries should display the
382
386
  error message, not its stack.
383
387
 
384
388
  ```ts
385
- import { deserialize } from "@kasperrt/smolter/client";
389
+ import { deserialize } from "@kasperrt/smolter/react/client";
386
390
  import { createRouter } from "@kasperrt/smolter/react";
387
391
  import routes from "virtual:smolter/routes";
388
392
 
@@ -403,15 +407,31 @@ The SSR examples use the same server route definitions for HTML and data request
403
407
  ## Package entries
404
408
 
405
409
  Use `@kasperrt/smolter/react` for React 18+ and `@kasperrt/smolter/preact` for Preact 10.25+.
406
- The root `@kasperrt/smolter` entry (also available as `@kasperrt/smolter/core`) is framework-independent.
407
- Server helpers live in `@kasperrt/smolter/server`; optional browser transport in `@kasperrt/smolter/client`;
408
- flat file routing in `@kasperrt/smolter/vite` and `@kasperrt/smolter/files`.
410
+ Choose one renderer for your application. Both expose the same routing API:
411
+
412
+ | Purpose | React | Preact |
413
+ | --- | --- | --- |
414
+ | Components, hooks, router, data types | `@kasperrt/smolter/react` | `@kasperrt/smolter/preact` |
415
+ | Optional browser transport | `@kasperrt/smolter/react/client` | `@kasperrt/smolter/preact/client` |
416
+ | SSR and HTTP data dispatch | `@kasperrt/smolter/react/server` | `@kasperrt/smolter/preact/server` |
417
+ | Vite plugin and flat-file discovery | `@kasperrt/smolter/react/vite` | `@kasperrt/smolter/preact/vite` |
418
+
419
+ `@kasperrt/smolter/utils` contains browser-safe matching and error helpers.
420
+ The main renderer entries never import server or build modules. Named imports
421
+ are tree-shaken; optional transport is included only when you import `/client`.
422
+ Server entries do not import either UI renderer. Vite entries are build-time only.
423
+
424
+ **Migrating to 0.2.0:** replace `/core` imports for types, `createRouter`, and
425
+ `defineRoute` with your renderer entry. Use `/utils` for `matchRoutes`,
426
+ `internalURL` and `safeWrapAsync`. Move `/client`, `/server`,
427
+ `/files`, and `/vite` imports to the corresponding renderer-prefixed path above.
428
+ The bare package root and old top-level subpaths are no longer exported.
409
429
 
410
430
  On JSR, install `@kasperrt/smolter` using the
411
431
  [JSR package instructions](https://jsr.io/docs/npm-compatibility) and import
412
432
  `@kasperrt/smolter/react` or `@kasperrt/smolter/preact`. Its `core`, `client`,
413
433
  `server`, and `files` subpaths match npm. The Vite plugin is npm-only:
414
- use `@kasperrt/smolter/vite` from the npm package when adding file routing.
434
+ use `@kasperrt/smolter/react/vite` from the npm package when adding file routing.
415
435
 
416
436
  ## Size and checks
417
437
 
@@ -426,8 +446,8 @@ example labels. File discovery is build-only.
426
446
  | --- | ---: |
427
447
  | `@kasperrt/smolter/react` | 2445 |
428
448
  | `@kasperrt/smolter/preact` | 2445 |
429
- | `@kasperrt/smolter/react + @kasperrt/smolter/client` | 2782 |
430
- | `@kasperrt/smolter/preact + @kasperrt/smolter/client` | 2781 |
449
+ | `@kasperrt/smolter/react + @kasperrt/smolter/react/client` | 2782 |
450
+ | `@kasperrt/smolter/preact + @kasperrt/smolter/preact/client` | 2781 |
431
451
  | `wouter@3.13.0` | 3091 |
432
452
  | `wouter-preact@3.13.0` | 2450 |
433
453
  | `react-router@8.4.0` | 60994 |
@@ -438,11 +458,11 @@ using production ESM bundles, esbuild, Terser, and gzip level 9. React, React DO
438
458
  and Preact are external in every bundle. Transitive library dependencies are
439
459
  included. React Router provides a broader API; this compares complete entry
440
460
  sizes, not equivalent feature sets or the minimum code for one route.
441
- `@kasperrt/smolter/client` adds browser transport for server loaders/actions; server-only
461
+ `@kasperrt/smolter/react/client` adds browser transport for server loaders/actions; server-only
442
462
  rendering code and the Vite plugin are not shipped to the browser.
443
463
 
444
464
  Exact current measurements are generated in
445
- [`packages/ui/src/size.json`](https://github.com/kasperrt/smolter/blob/main/packages/ui/src/size.json).
465
+ [`packages/ui/src/size.json`](./packages/ui/src/size.json).
446
466
 
447
467
  ```sh
448
468
  pnpm fix # Formatting
@@ -469,3 +489,5 @@ To test a local package build in another project:
469
489
  pnpm --dir packages/smolter pack --pack-destination ../..
470
490
  pnpm add /absolute/path/to/kasperrt-smolter-0.0.1.tgz
471
491
  ```
492
+
493
+ Workspace structure and rendering checks are documented in [CONTRIBUTING.md](./CONTRIBUTING.md).
@@ -2,5 +2,6 @@
2
2
  export * from "./errors.ts";
3
3
  export * from "./match.ts";
4
4
  export * from "./router.ts";
5
+ export * from "./serialize.ts";
5
6
  export * from "./types.ts";
6
7
  export * from "./url.ts";
@@ -2,5 +2,6 @@
2
2
  export * from "./errors.js";
3
3
  export * from "./match.js";
4
4
  export * from "./router.js";
5
+ export * from "./serialize.js";
5
6
  export * from "./types.js";
6
7
  export * from "./url.js";
@@ -0,0 +1,3 @@
1
+ import type { Snapshot } from "./types.ts";
2
+ /** Safe inside a script element. Data must be JSON serializable; error stacks and causes are omitted. */
3
+ export declare function serialize(snapshot: Snapshot): string;
@@ -0,0 +1,13 @@
1
+ /** Safe inside a script element. Data must be JSON serializable; error stacks and causes are omitted. */
2
+ export function serialize(snapshot) {
3
+ return JSON.stringify({
4
+ ...snapshot,
5
+ error: snapshot.error && {
6
+ name: snapshot.error.name,
7
+ message: snapshot.error.message,
8
+ },
9
+ })
10
+ .replace(/</g, "\\u003c")
11
+ .replace(/\u2028/g, "\\u2028")
12
+ .replace(/\u2029/g, "\\u2029");
13
+ }
@@ -2,10 +2,9 @@
2
2
  * Request-scoped server rendering and HTTP data dispatch.
3
3
  * @module
4
4
  */
5
- import { type Route, type Router, type Snapshot } from "../core/index.ts";
5
+ import { type Route, type Router } from "../core/index.ts";
6
6
  /** Create one router per server request, await this, then render the provider. */
7
7
  export declare function loadRouter(routes: readonly Route[], input: string | Request): Promise<Router>;
8
- /** Safe inside a script element. Data must be JSON serializable; error stacks and causes are omitted. */
9
- export declare function serialize(snapshot: Snapshot): string;
8
+ export { serialize } from "../core/index.ts";
10
9
  /** Dispatch an HTTP data request to a registered server loader or action. */
11
10
  export declare function handleRequest(routes: readonly Route[], request: Request): Promise<Response>;
@@ -39,19 +39,7 @@ export async function loadRouter(routes, input) {
39
39
  }
40
40
  return router;
41
41
  }
42
- /** Safe inside a script element. Data must be JSON serializable; error stacks and causes are omitted. */
43
- export function serialize(snapshot) {
44
- return JSON.stringify({
45
- ...snapshot,
46
- error: snapshot.error && {
47
- name: snapshot.error.name,
48
- message: snapshot.error.message,
49
- },
50
- })
51
- .replace(/</g, "\\u003c")
52
- .replace(/\u2028/g, "\\u2028")
53
- .replace(/\u2029/g, "\\u2029");
54
- }
42
+ export { serialize } from "../core/index.js";
55
43
  /** Dispatch an HTTP data request to a registered server loader or action. */
56
44
  export async function handleRequest(routes, request) {
57
45
  const match = matchRoutes(routes, new URL(request.url).pathname);
@@ -3,8 +3,9 @@ export interface FileRouterOptions {
3
3
  directory?: string;
4
4
  clientRoutes?: string;
5
5
  serverRoutes?: string;
6
+ framework?: "react" | "preact";
6
7
  }
7
8
  /** Build-time flat route discovery and the optional server data transport. */
8
- export declare function routerPlugin({ directory, clientRoutes, serverRoutes, }?: FileRouterOptions): Plugin;
9
+ export declare function routerPlugin({ directory, clientRoutes, serverRoutes, framework, }?: FileRouterOptions): Plugin;
9
10
  /** File routing is the default; this alias keeps configuration explicit. */
10
11
  export declare const fileRouter: typeof routerPlugin;
@@ -4,7 +4,7 @@ import { flatRoutes } from "../files/index.js";
4
4
  import { transformWithOxc } from "vite";
5
5
  import { configureTransport } from "./transport.js";
6
6
  /** Build-time flat route discovery and the optional server data transport. */
7
- export function routerPlugin({ directory = "src/routes", clientRoutes, serverRoutes, } = {}) {
7
+ export function routerPlugin({ directory = "src/routes", clientRoutes, serverRoutes, framework = "react", } = {}) {
8
8
  let root;
9
9
  let projectRoot;
10
10
  const publicId = "virtual:smolter/routes";
@@ -103,7 +103,7 @@ export function routerPlugin({ directory = "src/routes", clientRoutes, serverRou
103
103
  }
104
104
  if (!options?.ssr &&
105
105
  definitions.some((definition) => definition.startsWith("serverRoute(")))
106
- imports.unshift('import { serverRoute } from "@kasperrt/smolter/client";');
106
+ imports.unshift(`import { serverRoute } from "@kasperrt/smolter/${framework}/client";`);
107
107
  return `${imports.join("\n")}\nexport default [${definitions.join(",\n")}];\n`;
108
108
  },
109
109
  configureServer(server) {
@@ -124,7 +124,7 @@ export function routerPlugin({ directory = "src/routes", clientRoutes, serverRou
124
124
  server.watcher.off("change", refresh);
125
125
  server.watcher.off("unlink", refresh);
126
126
  });
127
- configureTransport(server, publicId);
127
+ configureTransport(server, publicId, framework);
128
128
  },
129
129
  };
130
130
  }
@@ -1,2 +1,2 @@
1
1
  import type { ViteDevServer } from "vite";
2
- export declare function configureTransport(server: ViteDevServer, publicId: string): void;
2
+ export declare function configureTransport(server: ViteDevServer, publicId: string, framework?: "react" | "preact"): void;
@@ -1,4 +1,4 @@
1
- export function configureTransport(server, publicId) {
1
+ export function configureTransport(server, publicId, framework = "react") {
2
2
  server.middlewares.use((incoming, outgoing, next) => {
3
3
  if (!incoming.url?.startsWith("/_smolter?"))
4
4
  return next();
@@ -30,7 +30,7 @@ export function configureTransport(server, publicId) {
30
30
  });
31
31
  const [{ default: routes }, { handleRequest }] = await Promise.all([
32
32
  server.ssrLoadModule(publicId),
33
- server.ssrLoadModule("@kasperrt/smolter/server"),
33
+ server.ssrLoadModule(`@kasperrt/smolter/${framework}/server`),
34
34
  ]);
35
35
  const response = await handleRequest(routes, request);
36
36
  outgoing.statusCode = response.status;
@@ -0,0 +1,2 @@
1
+ /** Optional browser transport for server handlers. @module */
2
+ export * from "./internal/client/index.ts";
@@ -0,0 +1,2 @@
1
+ /** Optional browser transport for server handlers. @module */
2
+ export * from "./internal/client/index.js";
@@ -0,0 +1,2 @@
1
+ /** Preact server rendering and HTTP data dispatch. @module */
2
+ export * from "./internal/server/index.ts";
@@ -0,0 +1,2 @@
1
+ /** Preact server rendering and HTTP data dispatch. @module */
2
+ export * from "./internal/server/index.js";
@@ -0,0 +1,9 @@
1
+ /** Optional flat-file routing for Preact builds. @module */
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";
6
+ /** Configure flat routes and server transport for Preact. */
7
+ export declare function routerPlugin(options?: FileRouterOptions): Plugin;
8
+ /** File routing is the default; this alias keeps configuration explicit. */
9
+ export declare const fileRouter: typeof routerPlugin;
@@ -0,0 +1,9 @@
1
+ /** Optional flat-file routing for Preact builds. @module */
2
+ import { routerPlugin as createPlugin, } from "./internal/vite/index.js";
3
+ export * from "./internal/files/index.js";
4
+ /** Configure flat routes and server transport for Preact. */
5
+ export function routerPlugin(options = {}) {
6
+ return createPlugin({ ...options, framework: "preact" });
7
+ }
8
+ /** File routing is the default; this alias keeps configuration explicit. */
9
+ export const fileRouter = routerPlugin;
@@ -0,0 +1,2 @@
1
+ /** Optional browser transport for server handlers. @module */
2
+ export * from "./internal/client/index.ts";
@@ -0,0 +1,2 @@
1
+ /** Optional browser transport for server handlers. @module */
2
+ export * from "./internal/client/index.js";
@@ -0,0 +1,2 @@
1
+ /** React server rendering and HTTP data dispatch. @module */
2
+ export * from "./internal/server/index.ts";
@@ -0,0 +1,2 @@
1
+ /** React server rendering and HTTP data dispatch. @module */
2
+ export * from "./internal/server/index.js";
@@ -0,0 +1,9 @@
1
+ /** Optional flat-file routing for React builds. @module */
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";
6
+ /** Configure flat routes and server transport for React. */
7
+ export declare function routerPlugin(options?: FileRouterOptions): Plugin;
8
+ /** File routing is the default; this alias keeps configuration explicit. */
9
+ export declare const fileRouter: typeof routerPlugin;
@@ -0,0 +1,9 @@
1
+ /** Optional flat-file routing for React builds. @module */
2
+ import { routerPlugin as createPlugin, } from "./internal/vite/index.js";
3
+ export * from "./internal/files/index.js";
4
+ /** Configure flat routes and server transport for React. */
5
+ export function routerPlugin(options = {}) {
6
+ return createPlugin({ ...options, framework: "react" });
7
+ }
8
+ /** File routing is the default; this alias keeps configuration explicit. */
9
+ export const fileRouter = routerPlugin;
@@ -0,0 +1,5 @@
1
+ /** Framework-independent, browser-safe utilities. @module */
2
+ export * from "./internal/core/errors.ts";
3
+ export * from "./internal/core/match.ts";
4
+ export * from "./internal/core/serialize.ts";
5
+ export * from "./internal/core/url.ts";
package/dist/utils.js ADDED
@@ -0,0 +1,5 @@
1
+ /** Framework-independent, browser-safe utilities. @module */
2
+ export * from "./internal/core/errors.js";
3
+ export * from "./internal/core/match.js";
4
+ export * from "./internal/core/serialize.js";
5
+ export * from "./internal/core/url.js";
package/jsr.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@kasperrt/smolter",
3
- "version": "0.0.1",
3
+ "version": "0.2.0",
4
4
  "exports": {
5
- ".": "./src/core.ts",
5
+ "./react": "./src/react.ts",
6
+ "./react/server": "./src/react-server.ts",
6
7
  "./preact": "./src/preact.ts",
7
- "./core": "./src/core.ts",
8
- "./server": "./src/server.ts",
9
- "./client": "./src/client.ts",
10
- "./files": "./src/files.ts",
11
- "./react": "./src/react.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"
12
12
  },
13
13
  "imports": {
14
14
  "react": "npm:react@19.3.1",
@@ -16,7 +16,12 @@
16
16
  },
17
17
  "publish": {
18
18
  "include": ["src", "README.md", "LICENSE", "jsr.json"],
19
- "exclude": ["src/vite.ts", "src/internal/vite"]
19
+ "exclude": [
20
+ "src/vite.ts",
21
+ "src/react-vite.ts",
22
+ "src/preact-vite.ts",
23
+ "src/internal/vite"
24
+ ]
20
25
  },
21
26
  "license": "MIT",
22
27
  "compatibility": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kasperrt/smolter",
3
- "version": "0.0.1",
3
+ "version": "0.2.0",
4
4
  "description": "A tiny typed data router for React and Preact.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -11,38 +11,41 @@
11
11
  "directory": "packages/smolter"
12
12
  },
13
13
  "exports": {
14
- ".": {
15
- "types": "./dist/core.d.ts",
16
- "import": "./dist/core.js"
14
+ "./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"
17
25
  },
18
26
  "./preact": {
19
27
  "types": "./dist/preact.d.ts",
20
28
  "import": "./dist/preact.js"
21
29
  },
22
- "./core": {
23
- "types": "./dist/core.d.ts",
24
- "import": "./dist/core.js"
30
+ "./preact/server": {
31
+ "types": "./dist/preact-server.d.ts",
32
+ "import": "./dist/preact-server.js"
25
33
  },
26
- "./server": {
27
- "types": "./dist/server.d.ts",
28
- "import": "./dist/server.js"
34
+ "./preact/vite": {
35
+ "types": "./dist/preact-vite.d.ts",
36
+ "import": "./dist/preact-vite.js"
29
37
  },
30
- "./files": {
31
- "types": "./dist/files.d.ts",
32
- "import": "./dist/files.js"
38
+ "./react/client": {
39
+ "types": "./dist/react-client.d.ts",
40
+ "import": "./dist/react-client.js"
33
41
  },
34
- "./vite": {
35
- "types": "./dist/vite.d.ts",
36
- "import": "./dist/vite.js"
42
+ "./preact/client": {
43
+ "types": "./dist/preact-client.d.ts",
44
+ "import": "./dist/preact-client.js"
37
45
  },
38
- "./client": {
39
- "types": "./dist/client.d.ts",
40
- "import": "./dist/client.js"
41
- },
42
- "./package.json": "./package.json",
43
- "./react": {
44
- "types": "./dist/react.d.ts",
45
- "import": "./dist/react.js"
46
+ "./utils": {
47
+ "types": "./dist/utils.d.ts",
48
+ "import": "./dist/utils.js"
46
49
  }
47
50
  },
48
51
  "files": [
@@ -87,8 +90,6 @@
87
90
  "ssr",
88
91
  "file-routing"
89
92
  ],
90
- "main": "./dist/core.js",
91
- "types": "./dist/core.d.ts",
92
93
  "publishConfig": {
93
94
  "access": "public",
94
95
  "provenance": true
@@ -3,5 +3,6 @@
3
3
  export * from "./errors.ts";
4
4
  export * from "./match.ts";
5
5
  export * from "./router.ts";
6
+ export * from "./serialize.ts";
6
7
  export * from "./types.ts";
7
8
  export * from "./url.ts";
@@ -0,0 +1,15 @@
1
+ import type { Snapshot } from "./types.ts";
2
+
3
+ /** Safe inside a script element. Data must be JSON serializable; error stacks and causes are omitted. */
4
+ export function serialize(snapshot: Snapshot): string {
5
+ return JSON.stringify({
6
+ ...snapshot,
7
+ error: snapshot.error && {
8
+ name: snapshot.error.name,
9
+ message: snapshot.error.message,
10
+ },
11
+ })
12
+ .replace(/</g, "\\u003c")
13
+ .replace(/\u2028/g, "\\u2028")
14
+ .replace(/\u2029/g, "\\u2029");
15
+ }
@@ -9,7 +9,6 @@ import {
9
9
  matchRoutes,
10
10
  type Route,
11
11
  type Router,
12
- type Snapshot,
13
12
  safeWrapAsync,
14
13
  } from "../core/index.ts";
15
14
  /** Create one router per server request, await this, then render the provider. */
@@ -61,19 +60,7 @@ export async function loadRouter(
61
60
 
62
61
  return router;
63
62
  }
64
- /** Safe inside a script element. Data must be JSON serializable; error stacks and causes are omitted. */
65
- export function serialize(snapshot: Snapshot): string {
66
- return JSON.stringify({
67
- ...snapshot,
68
- error: snapshot.error && {
69
- name: snapshot.error.name,
70
- message: snapshot.error.message,
71
- },
72
- })
73
- .replace(/</g, "\\u003c")
74
- .replace(/\u2028/g, "\\u2028")
75
- .replace(/\u2029/g, "\\u2029");
76
- }
63
+ export { serialize } from "../core/index.ts";
77
64
 
78
65
  /** Dispatch an HTTP data request to a registered server loader or action. */
79
66
  export async function handleRequest(
@@ -8,6 +8,7 @@ export interface FileRouterOptions {
8
8
  directory?: string;
9
9
  clientRoutes?: string;
10
10
  serverRoutes?: string;
11
+ framework?: "react" | "preact";
11
12
  }
12
13
 
13
14
  /** Build-time flat route discovery and the optional server data transport. */
@@ -15,6 +16,7 @@ export function routerPlugin({
15
16
  directory = "src/routes",
16
17
  clientRoutes,
17
18
  serverRoutes,
19
+ framework = "react",
18
20
  }: FileRouterOptions = {}): Plugin {
19
21
  let root: string;
20
22
  let projectRoot: string;
@@ -173,7 +175,7 @@ export function routerPlugin({
173
175
  definitions.some((definition) => definition.startsWith("serverRoute("))
174
176
  )
175
177
  imports.unshift(
176
- 'import { serverRoute } from "@kasperrt/smolter/client";',
178
+ `import { serverRoute } from "@kasperrt/smolter/${framework}/client";`,
177
179
  );
178
180
 
179
181
  return `${imports.join("\n")}\nexport default [${definitions.join(",\n")}];\n`;
@@ -201,7 +203,7 @@ export function routerPlugin({
201
203
  server.watcher.off("unlink", refresh);
202
204
  });
203
205
 
204
- configureTransport(server, publicId);
206
+ configureTransport(server, publicId, framework);
205
207
  },
206
208
  };
207
209
  }
@@ -3,6 +3,7 @@ import type { ViteDevServer } from "vite";
3
3
  export function configureTransport(
4
4
  server: ViteDevServer,
5
5
  publicId: string,
6
+ framework: "react" | "preact" = "react",
6
7
  ): void {
7
8
  server.middlewares.use((incoming, outgoing, next) => {
8
9
  if (!incoming.url?.startsWith("/_smolter?")) return next();
@@ -43,7 +44,7 @@ export function configureTransport(
43
44
  });
44
45
  const [{ default: routes }, { handleRequest }] = await Promise.all([
45
46
  server.ssrLoadModule(publicId),
46
- server.ssrLoadModule("@kasperrt/smolter/server"),
47
+ server.ssrLoadModule(`@kasperrt/smolter/${framework}/server`),
47
48
  ]);
48
49
  const response: Response = await handleRequest(routes, request);
49
50
 
@@ -0,0 +1,2 @@
1
+ /** Optional browser transport for server handlers. @module */
2
+ export * from "./internal/client/index.ts";
@@ -0,0 +1,2 @@
1
+ /** Preact server rendering and HTTP data dispatch. @module */
2
+ export * from "./internal/server/index.ts";
@@ -0,0 +1,18 @@
1
+ /** Optional flat-file routing for Preact builds. @module */
2
+
3
+ import type { Plugin } from "vite";
4
+ import {
5
+ routerPlugin as createPlugin,
6
+ type FileRouterOptions,
7
+ } from "./internal/vite/index.ts";
8
+
9
+ export * from "./internal/files/index.ts";
10
+ export type { FileRouterOptions } from "./internal/vite/index.ts";
11
+
12
+ /** Configure flat routes and server transport for Preact. */
13
+ export function routerPlugin(options: FileRouterOptions = {}): Plugin {
14
+ return createPlugin({ ...options, framework: "preact" });
15
+ }
16
+
17
+ /** File routing is the default; this alias keeps configuration explicit. */
18
+ export const fileRouter = routerPlugin;
@@ -0,0 +1,2 @@
1
+ /** Optional browser transport for server handlers. @module */
2
+ export * from "./internal/client/index.ts";
@@ -0,0 +1,2 @@
1
+ /** React server rendering and HTTP data dispatch. @module */
2
+ export * from "./internal/server/index.ts";
@@ -0,0 +1,18 @@
1
+ /** Optional flat-file routing for React builds. @module */
2
+
3
+ import type { Plugin } from "vite";
4
+ import {
5
+ routerPlugin as createPlugin,
6
+ type FileRouterOptions,
7
+ } from "./internal/vite/index.ts";
8
+
9
+ export * from "./internal/files/index.ts";
10
+ export type { FileRouterOptions } from "./internal/vite/index.ts";
11
+
12
+ /** Configure flat routes and server transport for React. */
13
+ export function routerPlugin(options: FileRouterOptions = {}): Plugin {
14
+ return createPlugin({ ...options, framework: "react" });
15
+ }
16
+
17
+ /** File routing is the default; this alias keeps configuration explicit. */
18
+ export const fileRouter = routerPlugin;
package/src/utils.ts ADDED
@@ -0,0 +1,5 @@
1
+ /** Framework-independent, browser-safe utilities. @module */
2
+ export * from "./internal/core/errors.ts";
3
+ export * from "./internal/core/match.ts";
4
+ export * from "./internal/core/serialize.ts";
5
+ export * from "./internal/core/url.ts";