@sigil-dev/grimoire 0.5.0 → 0.6.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 (51) hide show
  1. package/README.md +174 -174
  2. package/index.ts +34 -16
  3. package/package.json +6 -6
  4. package/src/{enhance.ts → client/enhance.ts} +2 -1
  5. package/src/client/index.ts +5 -0
  6. package/src/{client-router.ts → client/router.ts} +1 -1
  7. package/src/{vite-plugin.ts → integrations/vite.ts} +4 -4
  8. package/src/{hydrate.ts → rendering/hydrate.ts} +2 -2
  9. package/src/{renderer.ts → rendering/index.ts} +29 -16
  10. package/src/{ssrPlugin.ts → rendering/ssrPlugin.ts} +3 -2
  11. package/src/{scanner.ts → routing/scanner.ts} +16 -4
  12. package/src/{transform-routes.ts → routing/transform-routes.ts} +3 -2
  13. package/src/{fail.ts → sentinels/fail.ts} +1 -1
  14. package/src/{build.ts → server/build.ts} +12 -10
  15. package/src/server/coordinator.ts +297 -0
  16. package/src/{hooks.ts → server/hooks.ts} +1 -1
  17. package/src/{server.ts → server/index.ts} +85 -37
  18. package/src/server/plugins.ts +119 -0
  19. package/src/server/worker.ts +59 -0
  20. package/src/{typegen.ts → typegen/index.ts} +5 -2
  21. package/src/types.ts +134 -3
  22. package/test/context.test.ts +1 -1
  23. package/test/fail.test.ts +1 -1
  24. package/test/headers.test.ts +6 -2
  25. package/test/hydration.test.ts +1 -1
  26. package/test/middleware.test.ts +8 -4
  27. package/test/preload.ts +1 -1
  28. package/test/redirect-error.test.ts +2 -2
  29. package/test/rendering.test.ts +15 -6
  30. package/test/routing.test.ts +2 -2
  31. package/test/scanning.test.ts +40 -11
  32. package/test/scope.test.ts +25 -10
  33. package/test/server.test.ts +39 -10
  34. package/test/streaming.test.ts +21 -8
  35. package/test/transform-routes.test.ts +2 -2
  36. package/test/typegen.test.ts +5 -3
  37. package/tsconfig.json +3 -1
  38. package/.grimoire/_routes.ts +0 -4
  39. package/public/__grimoire__/client.js +0 -55
  40. package/public/__grimoire__/hydrate.js +0 -63
  41. package/src/client.ts +0 -4
  42. package/src/plugins.ts +0 -39
  43. package/src/sync.ts +0 -18
  44. /package/src/{scope.ts → client/scope.ts} +0 -0
  45. /package/src/{head.ts → rendering/head.ts} +0 -0
  46. /package/src/{manifest-gen.ts → routing/manifest-gen.ts} +0 -0
  47. /package/src/{router.ts → routing/router.ts} +0 -0
  48. /package/src/{error.ts → sentinels/error.ts} +0 -0
  49. /package/src/{redirect.ts → sentinels/redirect.ts} +0 -0
  50. /package/src/{context.ts → server/context.ts} +0 -0
  51. /package/src/{cookie-utils.ts → server/cookie-utils.ts} +0 -0
package/README.md CHANGED
@@ -1,174 +1,174 @@
1
- # @sigil-dev/grimoire
2
-
3
- Full-stack server framework for [Sigil](https://git.cane1712.dev/sigil/sigil).
4
- Heavily inspired by SvelteKit. Most of [its documentation](https://svelte.dev/docs/kit/introduction) applies here
5
-
6
- ## Quick start
7
-
8
- ```sh
9
- bun i -g @sigil-dev/cli
10
- sigil create
11
- ```
12
-
13
- ## Route files
14
-
15
- Place files in /src/routes.
16
-
17
- | File | Purpose |
18
- |---|---|
19
- | `+page.tsx | index.tsx | routeName.tsx` | Page |
20
- | `+page.server.ts` | Server data passed to page / form actions |
21
- | `+layout.tsx` | Page layout |
22
- | `+layout.server.ts` | Layout-level `load` |
23
- | `+server.ts` | API route (raw `Request → Response`) |
24
- | `+error.tsx` | Error boundary page |
25
- | `hooks.server.ts` | Global request middleware (project root) |
26
-
27
- `/routes/about/index.tsx`, `/routes/about/+page.tsx` and `/routes/about.tsx` all work and all do the same thing.
28
- For path parameters use square brackets. `src/routes/posts/[slug]/+page.tsx` → `/posts/:slug`.
29
-
30
- ## Loading data
31
-
32
- ```ts
33
- // src/routes/posts/[slug]/+page.server.ts
34
- import { error } from "@sigil-dev/grimoire";
35
- import type { TypedLoadContext } from "@sigil-dev/grimoire";
36
-
37
- export async function load({ params }: TypedLoadContext<{ slug: string }>) {
38
- const post = await db.posts.findBySlug(params.slug);
39
- if (!post) throw error(404, "Post not found");
40
- return { post }; // → page receives { data: { post }, params }
41
- }
42
- ```
43
-
44
- The return value is passed as the `data` prop to the matching `+page.tsx` component.
45
-
46
- ## Form actions
47
-
48
- Export HTTP method handlers alongside `load`:
49
-
50
- ```ts
51
- // src/routes/login/+page.server.ts
52
- import { fail, redirect } from "@sigil-dev/grimoire";
53
-
54
- export async function POST({ request }) {
55
- const data = await request.formData();
56
- const user = await auth.login(data.get("email"), data.get("password"));
57
- if (!user) return fail(400, { error: "Invalid credentials" });
58
- throw redirect(303, "/dashboard");
59
- }
60
- ```
61
-
62
- Use `enhance` with the `use` prop on the client for fetch-based submission without a full page reload:
63
- > This is jank and will likely be reworked.
64
-
65
- ```tsx
66
- // src/routes/login/+page.tsx
67
- import { enhance } from "@sigil-dev/grimoire/client";
68
-
69
- export default function LoginPage() {
70
- const errors = $state({});
71
-
72
- return (
73
- <form use={[enhance, { onFail: (data) => errors.set(data) }]}>
74
- <input name="email" />
75
- <input name="password" type="password" />
76
- <button>Log in</button>
77
- </form>
78
- );
79
- }
80
- ```
81
-
82
- ## Error handling
83
-
84
- `error(status, message)` can be thrown from any `load` function or action. Grimoire renders the closest `+error.tsx` if one exists, otherwise falls back to a plain-text response.
85
-
86
- ```tsx
87
- // src/routes/+error.tsx
88
- export default function ErrorPage({ status, message }: { status: number; message: string }) {
89
- return (
90
- <html>
91
- <body>
92
- <h1>{status}</h1>
93
- <p>{message}</p>
94
- </body>
95
- </html>
96
- );
97
- }
98
- ```
99
-
100
- ## API routes
101
-
102
- ```ts
103
- // src/routes/api/items/+server.ts
104
- export async function GET({ url }) {
105
- const items = await db.items.list();
106
- return Response.json(items);
107
- }
108
-
109
- export async function POST({ request }) {
110
- const body = await request.json();
111
- const item = await db.items.create(body);
112
- return Response.json(item, { status: 201 });
113
- }
114
- ```
115
-
116
- ## Layouts
117
-
118
- ```tsx
119
- // src/routes/+layout.tsx
120
- import { Head } from "@sigil-dev/grimoire";
121
-
122
- export default function Layout({ children }) {
123
- return (
124
- <html>
125
- <Head><title>My App</title></Head>
126
- <body>{children}</body>
127
- </html>
128
- );
129
- }
130
- ```
131
-
132
- ## Hooks (middleware)
133
-
134
- ```ts
135
- // hooks.server.ts (project root)
136
- import type { Handle } from "@sigil-dev/grimoire/hooks";
137
-
138
- export const handle: Handle = async ({ event, resolve }) => {
139
- event.locals.user = await getUser(event.request);
140
- return resolve(event);
141
- };
142
- ```
143
-
144
- ## Configuration
145
-
146
- ```ts
147
- import { defineConfig } from "@sigil-dev/grimoire";
148
-
149
- export default defineConfig({
150
- port: 3000,
151
- host: "localhost",
152
- routes: "src/routes", // default
153
- plugins: [],
154
- });
155
- ```
156
-
157
- ## Helpers
158
-
159
- | Import | Description |
160
- |---|---|
161
- | `error(status, message)` | Throw an HTTP error from `load` or an action |
162
- | `fail(status, data)` | Return validation errors from an action |
163
- | `redirect(status, location)` | Throw a redirect from `load` or an action |
164
- | `sequence(...handles)` | Compose multiple `Handle` middleware functions |
165
-
166
- ## Security headers
167
-
168
- ```ts
169
- import { securityHeaders } from "@sigil-dev/grimoire/headers";
170
-
171
- // use as a Handle in hooks.server.ts, or compose with sequence()
172
- ```
173
-
174
- `securityHeaders()` returns a `Handle` that sets CSP, HSTS, X-Frame-Options, and related headers on every response.
1
+ # @sigil-dev/grimoire
2
+
3
+ Full-stack server framework for [Sigil](https://git.cane1712.dev/sigil/sigil).
4
+ Heavily inspired by SvelteKit. Most of [its documentation](https://svelte.dev/docs/kit/introduction) applies here
5
+
6
+ ## Quick start
7
+
8
+ ```sh
9
+ bun i -g @sigil-dev/cli
10
+ sigil create
11
+ ```
12
+
13
+ ## Route files
14
+
15
+ Place files in /src/routes.
16
+
17
+ | File | Purpose |
18
+ |---|---|
19
+ | `+page.tsx | index.tsx | routeName.tsx` | Page |
20
+ | `+page.server.ts` | Server data passed to page / form actions |
21
+ | `+layout.tsx` | Page layout |
22
+ | `+layout.server.ts` | Layout-level `load` |
23
+ | `+server.ts` | API route (raw `Request → Response`) |
24
+ | `+error.tsx` | Error boundary page |
25
+ | `hooks.index.ts` | Global request middleware (project root) |
26
+
27
+ `/routes/about/index.tsx`, `/routes/about/+page.tsx` and `/routes/about.tsx` all work and all do the same thing.
28
+ For path parameters use square brackets. `src/routes/posts/[slug]/+page.tsx` → `/posts/:slug`.
29
+
30
+ ## Loading data
31
+
32
+ ```ts
33
+ // src/routes/posts/[slug]/+page.server.ts
34
+ import { error } from "@sigil-dev/grimoire";
35
+ import type { TypedLoadContext } from "@sigil-dev/grimoire";
36
+
37
+ export async function load({ params }: TypedLoadContext<{ slug: string }>) {
38
+ const post = await db.posts.findBySlug(params.slug);
39
+ if (!post) throw error(404, "Post not found");
40
+ return { post }; // → page receives { data: { post }, params }
41
+ }
42
+ ```
43
+
44
+ The return value is passed as the `data` prop to the matching `+page.tsx` component.
45
+
46
+ ## Form actions
47
+
48
+ Export HTTP method handlers alongside `load`:
49
+
50
+ ```ts
51
+ // src/routes/login/+page.server.ts
52
+ import { fail, redirect } from "@sigil-dev/grimoire";
53
+
54
+ export async function POST({ request }) {
55
+ const data = await request.formData();
56
+ const user = await auth.login(data.get("email"), data.get("password"));
57
+ if (!user) return fail(400, { error: "Invalid credentials" });
58
+ throw redirect(303, "/dashboard");
59
+ }
60
+ ```
61
+
62
+ Use `enhance` with the `use` prop on the client for fetch-based submission without a full page reload:
63
+ > This is jank and will likely be reworked.
64
+
65
+ ```tsx
66
+ // src/routes/login/+page.tsx
67
+ import { enhance } from "@sigil-dev/grimoire/client";
68
+
69
+ export default function LoginPage() {
70
+ const errors = $state({});
71
+
72
+ return (
73
+ <form use={[enhance, { onFail: (data) => errors.set(data) }]}>
74
+ <input name="email" />
75
+ <input name="password" type="password" />
76
+ <button>Log in</button>
77
+ </form>
78
+ );
79
+ }
80
+ ```
81
+
82
+ ## Error handling
83
+
84
+ `error(status, message)` can be thrown from any `load` function or action. Grimoire renders the closest `+error.tsx` if one exists, otherwise falls back to a plain-text response.
85
+
86
+ ```tsx
87
+ // src/routes/+error.tsx
88
+ export default function ErrorPage({ status, message }: { status: number; message: string }) {
89
+ return (
90
+ <html>
91
+ <body>
92
+ <h1>{status}</h1>
93
+ <p>{message}</p>
94
+ </body>
95
+ </html>
96
+ );
97
+ }
98
+ ```
99
+
100
+ ## API routes
101
+
102
+ ```ts
103
+ // src/routes/api/items/+server.ts
104
+ export async function GET({ url }) {
105
+ const items = await db.items.list();
106
+ return Response.json(items);
107
+ }
108
+
109
+ export async function POST({ request }) {
110
+ const body = await request.json();
111
+ const item = await db.items.create(body);
112
+ return Response.json(item, { status: 201 });
113
+ }
114
+ ```
115
+
116
+ ## Layouts
117
+
118
+ ```tsx
119
+ // src/routes/+layout.tsx
120
+ import { Head } from "@sigil-dev/grimoire";
121
+
122
+ export default function Layout({ children }) {
123
+ return (
124
+ <html>
125
+ <Head><title>My App</title></Head>
126
+ <body>{children}</body>
127
+ </html>
128
+ );
129
+ }
130
+ ```
131
+
132
+ ## Hooks (middleware)
133
+
134
+ ```ts
135
+ // hooks.index.ts (project root)
136
+ import type { Handle } from "@sigil-dev/grimoire/hooks";
137
+
138
+ export const handle: Handle = async ({ event, resolve }) => {
139
+ event.locals.user = await getUser(event.request);
140
+ return resolve(event);
141
+ };
142
+ ```
143
+
144
+ ## Configuration
145
+
146
+ ```ts
147
+ import { defineConfig } from "@sigil-dev/grimoire";
148
+
149
+ export default defineConfig({
150
+ port: 3000,
151
+ host: "localhost",
152
+ routes: "src/routes", // default
153
+ plugins: [],
154
+ });
155
+ ```
156
+
157
+ ## Helpers
158
+
159
+ | Import | Description |
160
+ |---|---|
161
+ | `error(status, message)` | Throw an HTTP error from `load` or an action |
162
+ | `fail(status, data)` | Return validation errors from an action |
163
+ | `redirect(status, location)` | Throw a redirect from `load` or an action |
164
+ | `sequence(...handles)` | Compose multiple `Handle` middleware functions |
165
+
166
+ ## Security headers
167
+
168
+ ```ts
169
+ import { securityHeaders } from "@sigil-dev/grimoire/headers";
170
+
171
+ // use as a Handle in hooks.index.ts, or compose with sequence()
172
+ ```
173
+
174
+ `securityHeaders()` returns a `Handle` that sets CSP, HSTS, X-Frame-Options, and related headers on every response.
package/index.ts CHANGED
@@ -1,29 +1,47 @@
1
- export type { RouteFile, RouteTree } from "./src/scanner";
2
- export { scanRoutes } from "./src/scanner";
3
- export { buildProject } from "./src/build";
1
+ export { Head } from "./src/rendering/head";
2
+ export type { RouteFile, RouteTree } from "./src/routing/scanner";
3
+ export { scanRoutes } from "./src/routing/scanner";
4
+ export type { ErrorResult } from "./src/sentinels/error.ts";
5
+ export { error, isErrorResult } from "./src/sentinels/error.ts";
6
+ export type { FailResult } from "./src/sentinels/fail.ts";
7
+ export { fail, isFailResult } from "./src/sentinels/fail.ts";
8
+ export type { RedirectResult } from "./src/sentinels/redirect.ts";
9
+ export { isRedirectResult, redirect } from "./src/sentinels/redirect.ts";
4
10
  export { createServer } from "./src/server";
5
- export { fail, isFailResult } from "./src/fail";
6
- export type { FailResult } from "./src/fail";
7
- export { redirect, isRedirectResult } from "./src/redirect";
8
- export type { RedirectResult } from "./src/redirect";
9
- export { error, isErrorResult } from "./src/error";
10
- export type { ErrorResult } from "./src/error";
11
- export type { Handle, RequestEvent, Cookies, CookieOptions, ResolveFunction } from "./src/hooks";
12
- export { parseCookies } from "./src/cookie-utils";
13
- export { sequence, createHooks } from "./src/hooks";
11
+ export { buildProject } from "./src/server/build";
12
+ export { parseCookies } from "./src/server/cookie-utils";
13
+ export { startCoordinator } from "./src/server/coordinator.ts";
14
+ export type {
15
+ CookieOptions,
16
+ Cookies,
17
+ Handle,
18
+ RequestEvent,
19
+ ResolveFunction,
20
+ } from "./src/server/hooks";
21
+ export { createHooks, sequence } from "./src/server/hooks";
22
+ export {
23
+ runDeserializeLocals,
24
+ runRouteRequest,
25
+ runSerializeLocals,
26
+ runWorkerSpawn,
27
+ } from "./src/server/plugins";
14
28
  export type { TypegenConfig } from "./src/typegen";
15
29
  export { generateTypes } from "./src/typegen";
16
30
  export type {
31
+ BuiltinWorkerMode,
32
+ CoordinatorContext,
17
33
  GrimoireConfig,
18
34
  GrimoirePlugin,
35
+ LayoutServerLoad,
19
36
  LoadContext,
37
+ PageServerLoad,
20
38
  RenderContext,
39
+ RequestHandler,
21
40
  RouteInfo,
22
41
  TypedLoadContext,
23
- PageServerLoad,
24
- LayoutServerLoad,
25
- RequestHandler,
42
+ WorkerDescriptor,
43
+ WorkerEnv,
44
+ WorkerMode,
26
45
  WsRouteHandler,
27
46
  } from "./src/types";
28
47
  export { defineConfig } from "./src/types";
29
- export { Head } from "./src/head";
package/package.json CHANGED
@@ -3,16 +3,16 @@
3
3
  "module": "index.ts",
4
4
  "type": "module",
5
5
  "private": false,
6
- "version": "0.5.0",
6
+ "version": "0.6.0",
7
7
  "devDependencies": {
8
8
  "@types/bun": "latest"
9
9
  },
10
10
  "exports": {
11
11
  ".": "./index.ts",
12
- "./client": "./src/enhance.ts",
12
+ "./client": "./src/client/enhance.ts",
13
13
  "./headers": "./src/headers.ts",
14
- "./hooks": "./src/hooks.ts",
15
- "./vite": "./src/vite-plugin.ts"
14
+ "./hooks": "./src/server/hooks.ts",
15
+ "./vite": "./src/integrations/vite.ts"
16
16
  },
17
17
  "bin": {
18
18
  "grimoire": "src/sync.ts"
@@ -26,8 +26,8 @@
26
26
  "@babel/plugin-syntax-jsx": "^8.0.0-rc.6",
27
27
  "@babel/plugin-syntax-typescript": "^8.0.0-rc.6",
28
28
  "@babel/types": "^8.0.0-rc.6",
29
- "@sigil-dev/compiler": "0.3.0",
30
- "@sigil-dev/runtime": "0.3.0",
29
+ "@sigil-dev/compiler": "0.5.0",
30
+ "@sigil-dev/runtime": "0.5.0",
31
31
  "vite": "^8.0.16"
32
32
  },
33
33
  "peerDependencies": {
@@ -42,7 +42,8 @@ export function enhance(
42
42
  e.preventDefault();
43
43
 
44
44
  const formData = new FormData(form);
45
- const url = action ?? form.getAttribute("action") ?? window.location.pathname;
45
+ const url =
46
+ action ?? form.getAttribute("action") ?? window.location.pathname;
46
47
 
47
48
  try {
48
49
  const res = await fetch(url, {
@@ -0,0 +1,5 @@
1
+ // @ts-expect-error Compiler generated/linked
2
+ import { routes } from "#grimoire-routes";
3
+ import { initRouter } from "./router.ts";
4
+
5
+ initRouter(routes);
@@ -1,4 +1,4 @@
1
- import { withEffectScope } from "./scope.ts";
1
+ import { withEffectScope } from "./scope";
2
2
 
3
3
  let routeMap: Record<string, (props: any) => any> = {};
4
4
  let disposeCurrentPage: (() => void) | null = null;
@@ -1,10 +1,10 @@
1
1
  import { isAbsolute, join, resolve } from "node:path";
2
2
  import type { Plugin } from "vite";
3
- import { renderRoute } from "./renderer";
4
- import { matchRoute } from "./router";
5
- import { scanRoutes } from "./scanner";
3
+ import { renderRoute } from "../rendering";
4
+ import { matchRoute } from "../routing/router.ts";
5
+ import { scanRoutes } from "../routing/scanner.ts";
6
6
 
7
- const CLIENT_ENTRY = resolve(import.meta.dir, "./client.ts");
7
+ const CLIENT_ENTRY = resolve(import.meta.dir, "./index.ts");
8
8
 
9
9
  export function grimoire(options: { routes?: string } = {}): Plugin {
10
10
  let isBuild = false;
@@ -1,6 +1,6 @@
1
1
  import { routes } from "#grimoire-routes";
2
- import { withEffectScope } from "./scope.ts";
3
- import { initRouter } from "./client-router.ts";
2
+ import { initRouter } from "../client/router.ts";
3
+ import { withEffectScope } from "../client/scope.ts";
4
4
 
5
5
  const stateEl = document.getElementById("__grimoire_state__");
6
6
  let initialDispose: (() => void) | undefined;
@@ -1,12 +1,12 @@
1
1
  import { SafeHtml } from "@sigil-dev/runtime";
2
- import { isErrorResult } from "./error";
3
- import { runWithContext } from "./context";
2
+ import { findClosestError, type MatchedRoute } from "../routing/router";
3
+ import type { RouteFile } from "../routing/scanner";
4
+ import { isErrorResult } from "../sentinels/error.ts";
5
+ import { isRedirectResult } from "../sentinels/redirect.ts";
6
+ import { runWithContext } from "../server/context";
7
+ import { runHook } from "../server/plugins";
8
+ import type { GrimoirePlugin, LoadContext, Route } from "../types";
4
9
  import { collectHead, initHead } from "./head";
5
- import { runHook } from "./plugins";
6
- import { isRedirectResult } from "./redirect";
7
- import { findClosestError, type MatchedRoute } from "./router";
8
- import type { RouteFile } from "./scanner";
9
- import type { GrimoirePlugin, LoadContext, Route } from "./types";
10
10
 
11
11
  export type ModuleLoader = (path: string) => Promise<any>;
12
12
 
@@ -20,7 +20,10 @@ async function renderErrorPage(
20
20
  if (!errorPage) return null;
21
21
  const mod = await import(errorPage.filePath);
22
22
  const html = mod.default({ status, message });
23
- return new Response(html, { status, headers: { "Content-Type": "text/html" } });
23
+ return new Response(html, {
24
+ status,
25
+ headers: { "Content-Type": "text/html" },
26
+ });
24
27
  }
25
28
 
26
29
  export async function renderRoute(
@@ -64,8 +67,12 @@ export async function renderRoute(
64
67
  }
65
68
  if (isErrorResult(e)) {
66
69
  return (
67
- (await renderErrorPage(errorRoutes, context.url.pathname, e.status, e.message)) ??
68
- new Response(e.message, { status: e.status })
70
+ (await renderErrorPage(
71
+ errorRoutes,
72
+ context.url.pathname,
73
+ e.status,
74
+ e.message,
75
+ )) ?? new Response(e.message, { status: e.status })
69
76
  );
70
77
  }
71
78
  throw e;
@@ -86,8 +93,12 @@ export async function renderRoute(
86
93
  }
87
94
  if (isErrorResult(e)) {
88
95
  return (
89
- (await renderErrorPage(errorRoutes, context.url.pathname, e.status, e.message)) ??
90
- new Response(e.message, { status: e.status })
96
+ (await renderErrorPage(
97
+ errorRoutes,
98
+ context.url.pathname,
99
+ e.status,
100
+ e.message,
101
+ )) ?? new Response(e.message, { status: e.status })
91
102
  );
92
103
  }
93
104
  throw e;
@@ -118,10 +129,12 @@ export async function renderRoute(
118
129
  let bodyHtml: string = wrappedPage;
119
130
  if (matched.layout) {
120
131
  const layoutMod = await import(matched.layout.filePath);
121
- bodyHtml = String(layoutMod.default({
122
- data: layoutData,
123
- children: new SafeHtml(wrappedPage),
124
- }));
132
+ bodyHtml = String(
133
+ layoutMod.default({
134
+ data: layoutData,
135
+ children: new SafeHtml(wrappedPage),
136
+ }),
137
+ );
125
138
  }
126
139
 
127
140
  const stateJson = JSON.stringify({
@@ -1,7 +1,7 @@
1
1
  // packages/grimoire/src/ssr-plugin.ts
2
2
  import { transformSync } from "@babel/core";
3
3
  import sigilPlugin from "@sigil-dev/compiler/babel";
4
- import type { GrimoirePlugin } from "./types";
4
+ import type { GrimoirePlugin } from "../types";
5
5
 
6
6
  export function registerSSRPlugin(plugins: GrimoirePlugin[] = []) {
7
7
  Bun.plugin({
@@ -19,7 +19,8 @@ export function registerSSRPlugin(plugins: GrimoirePlugin[] = []) {
19
19
  });
20
20
  let contents = transpiler.transformSync(result?.code ?? "");
21
21
  for (const plugin of plugins) {
22
- if (plugin.transform) contents = (await plugin.transform(contents, path)) ?? contents;
22
+ if (plugin.transform)
23
+ contents = (await plugin.transform(contents, path)) ?? contents;
23
24
  }
24
25
  return {
25
26
  contents,
@@ -93,10 +93,22 @@ export async function scanRoutes(
93
93
  return aScore - bScore;
94
94
  });
95
95
 
96
- assertNoDuplicatePaths(routes.filter(r => r.type === "page" || r.type === "simple"), "page");
97
- assertNoDuplicatePaths(routes.filter(r => r.type === "pageServer"), "page server");
98
- assertNoDuplicatePaths(layouts.filter(l => l.type === "layout"), "layout");
99
- assertNoDuplicatePaths(layouts.filter(l => l.type === "layoutServer"), "layout server");
96
+ assertNoDuplicatePaths(
97
+ routes.filter((r) => r.type === "page" || r.type === "simple"),
98
+ "page",
99
+ );
100
+ assertNoDuplicatePaths(
101
+ routes.filter((r) => r.type === "pageServer"),
102
+ "page server",
103
+ );
104
+ assertNoDuplicatePaths(
105
+ layouts.filter((l) => l.type === "layout"),
106
+ "layout",
107
+ );
108
+ assertNoDuplicatePaths(
109
+ layouts.filter((l) => l.type === "layoutServer"),
110
+ "layout server",
111
+ );
100
112
  assertNoDuplicatePaths(servers, "API route");
101
113
  assertNoDuplicatePaths(errors, "error page");
102
114
 
@@ -1,8 +1,8 @@
1
1
  import { transformSync } from "@babel/core";
2
2
  import sigilPlugin from "@sigil-dev/compiler/babel";
3
3
  import { basename, dirname, join, relative, resolve } from "path";
4
+ import type { GrimoirePlugin } from "../types";
4
5
  import type { RouteFile } from "./scanner";
5
- import type { GrimoirePlugin } from "./types";
6
6
 
7
7
  const STYLE_RE = /<style[^>]*>([\s\S]*?)<\/style>/gi;
8
8
 
@@ -80,7 +80,8 @@ export async function transformRoutes(
80
80
  });
81
81
  let out = transpiler.transformSync(res?.code ?? "");
82
82
  for (const plugin of plugins) {
83
- if (plugin.transform) out = (await plugin.transform(out, route.filePath)) ?? out;
83
+ if (plugin.transform)
84
+ out = (await plugin.transform(out, route.filePath)) ?? out;
84
85
  }
85
86
  const rel = relative(process.cwd(), route.filePath)
86
87
  .replace(/\.[cm]?[jt]sx?$/, "")
@@ -35,7 +35,7 @@ export function isFailResult(value: unknown): value is FailResult {
35
35
  return (
36
36
  typeof value === "object" &&
37
37
  value !== null &&
38
- "__fail" in value &&
38
+ "__fail" in value &&
39
39
  (value as any).__fail === true
40
40
  );
41
41
  }