@sigil-dev/grimoire 0.7.3 → 0.7.5

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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "module": "index.ts",
4
4
  "type": "module",
5
5
  "private": false,
6
- "version": "0.7.3",
6
+ "version": "0.7.5",
7
7
  "devDependencies": {
8
8
  "@types/bun": "latest"
9
9
  },
@@ -28,8 +28,8 @@
28
28
  "@babel/plugin-syntax-jsx": "^8.0.0-rc.6",
29
29
  "@babel/plugin-syntax-typescript": "^8.0.0-rc.6",
30
30
  "@babel/types": "^8.0.0-rc.6",
31
- "@sigil-dev/compiler": "0.7.3",
32
- "@sigil-dev/runtime": "0.7.3",
31
+ "@sigil-dev/compiler": "0.7.5",
32
+ "@sigil-dev/runtime": "0.7.5",
33
33
  "vite": "^8.0.16"
34
34
  },
35
35
  "peerDependencies": {
@@ -1,4 +1,5 @@
1
1
  import { routes } from "#grimoire-routes";
2
+ import { popHydrationNodes, pushHydrationNodes } from "@sigil-dev/runtime";
2
3
  import { initRouter } from "../client/router.ts";
3
4
  import { withEffectScope } from "../client/scope.ts";
4
5
 
@@ -1,32 +1,47 @@
1
- // packages/grimoire/src/ssr-plugin.ts
2
1
  import { transformSync } from "@babel/core";
3
2
  import sigilPlugin from "@sigil-dev/compiler/babel";
3
+ import { createHash } from "node:crypto";
4
4
  import type { GrimoirePlugin } from "../types";
5
5
 
6
+ let registered = false;
7
+
6
8
  export function registerSSRPlugin(plugins: GrimoirePlugin[] = []) {
7
- Bun.plugin({
8
- name: "sigil-ssr",
9
- setup(build) {
10
- const transpiler = new Bun.Transpiler({ loader: "tsx", target: "bun" });
11
- build.onLoad({ filter: /\.[jt]sx$/ }, async ({ path }) => {
12
- const source = await Bun.file(path).text();
13
- const result = transformSync(source, {
14
- parserOpts: {
15
- plugins: [["typescript", { isTSX: true }], "jsx"],
16
- },
17
- plugins: [[sigilPlugin, { mode: "ssr" }]],
18
- filename: path,
19
- });
20
- let contents = transpiler.transformSync(result?.code ?? "");
21
- for (const plugin of plugins) {
22
- if (plugin.transform)
23
- contents = (await plugin.transform(contents, path)) ?? contents;
24
- }
25
- return {
26
- contents,
27
- loader: "js" as const,
28
- };
29
- });
30
- },
31
- });
9
+ if (registered) return;
10
+ registered = true;
11
+
12
+ Bun.plugin({
13
+ name: "sigil-ssr",
14
+ setup(build) {
15
+ // loader: "ts" not "tsx" — Babel already consumed the JSX,
16
+ // Bun only needs to strip remaining TypeScript types
17
+ const transpiler = new Bun.Transpiler({ loader: "ts", target: "bun" });
18
+
19
+ build.onLoad({ filter: /\.tsx?$/ }, async ({ path }) => {
20
+ if (path.includes(".grimoire") || path.includes("node_modules")) return;
21
+ const source = await Bun.file(path).text();
22
+ const hash = createHash("md5").update(path).digest("hex").slice(0, 8);
23
+ // console.log("[sigil-ssr] transforming", path);
24
+ const result = transformSync(source, {
25
+ configFile: false,
26
+ babelrc: false,
27
+ parserOpts: {
28
+ plugins: ["typescript", "jsx"],
29
+ },
30
+ plugins: [[sigilPlugin, { mode: "ssr", hash }]],
31
+ filename: path,
32
+ });
33
+ const output = result?.code ?? "";
34
+ const lines = output.split("\n");
35
+
36
+ let contents = transpiler.transformSync(result?.code ?? "");
37
+
38
+ for (const plugin of plugins) {
39
+ if (plugin.transform)
40
+ contents = (await plugin.transform(contents, path)) ?? contents;
41
+ }
42
+
43
+ return { contents, loader: "js" as const };
44
+ });
45
+ },
46
+ });
32
47
  }
@@ -9,39 +9,39 @@ const STYLE_RE = /<style[^>]*>([\s\S]*?)<\/style>/gi;
9
9
  // Inline the same hash helper used in bun-plugin.ts to avoid importing
10
10
  // unexported internals from @sigil-dev/compiler.
11
11
  function computeHash(filePath: string): string {
12
- let h = 0x811c9dc5;
13
- for (let i = 0; i < filePath.length; i++) {
14
- h ^= filePath.charCodeAt(i);
15
- h = (h * 0x01000193) >>> 0;
16
- }
17
- return h.toString(36);
12
+ let h = 0x811c9dc5;
13
+ for (let i = 0; i < filePath.length; i++) {
14
+ h ^= filePath.charCodeAt(i);
15
+ h = (h * 0x01000193) >>> 0;
16
+ }
17
+ return h.toString(36);
18
18
  }
19
19
 
20
20
  function rewriteRelativeImports(filePath: string) {
21
- const basedir = dirname(filePath);
22
- const rewrite = (source?: { value: string }) => {
23
- if (!source?.value.startsWith(".")) return;
24
- source.value = resolve(basedir, source.value);
25
- };
21
+ const basedir = dirname(filePath);
22
+ const rewrite = (source?: { value: string }) => {
23
+ if (!source?.value.startsWith(".")) return;
24
+ source.value = resolve(basedir, source.value);
25
+ };
26
26
 
27
- return {
28
- name: "sigil-rewrite-relative-route-imports",
29
- visitor: {
30
- ImportDeclaration(path: any) {
31
- rewrite(path.node.source);
32
- },
33
- ExportNamedDeclaration(path: any) {
34
- rewrite(path.node.source);
35
- },
36
- ExportAllDeclaration(path: any) {
37
- rewrite(path.node.source);
38
- },
39
- CallExpression(path: any) {
40
- if (path.node.callee.type !== "Import") return;
41
- rewrite(path.node.arguments[0]);
42
- },
43
- },
44
- };
27
+ return {
28
+ name: "sigil-rewrite-relative-route-imports",
29
+ visitor: {
30
+ ImportDeclaration(path: any) {
31
+ rewrite(path.node.source);
32
+ },
33
+ ExportNamedDeclaration(path: any) {
34
+ rewrite(path.node.source);
35
+ },
36
+ ExportAllDeclaration(path: any) {
37
+ rewrite(path.node.source);
38
+ },
39
+ CallExpression(path: any) {
40
+ if (path.node.callee.type !== "Import") return;
41
+ rewrite(path.node.arguments[0]);
42
+ },
43
+ },
44
+ };
45
45
  }
46
46
 
47
47
  /**
@@ -54,43 +54,48 @@ function rewriteRelativeImports(filePath: string) {
54
54
  * Bun.build only ever sees plain JavaScript.
55
55
  */
56
56
  export async function transformRoutes(
57
- routes: RouteFile[],
58
- outDir: string,
59
- mode: "hydrate" | "dom",
60
- plugins: GrimoirePlugin[] = [],
57
+ routes: RouteFile[],
58
+ outDir: string,
59
+ mode: "hydrate" | "dom",
60
+ plugins: GrimoirePlugin[] = [],
61
61
  ): Promise<Map<string, string>> {
62
- const map = new Map<string, string>();
63
- const transpiler = new Bun.Transpiler({ loader: "tsx", target: "browser" });
64
- await Promise.all(
65
- routes.map(async (route, index) => {
66
- let code = await Bun.file(route.filePath).text();
67
- const hash = computeHash(route.filePath);
68
- // Strip <style> blocks — CSS injection is handled by the sigil plugin
69
- // inside bun-plugin.ts at runtime; we just need clean JS here.
70
- code = code.replace(STYLE_RE, "");
71
- const res = transformSync(code, {
72
- parserOpts: {
73
- plugins: [["typescript", { isTSX: true }], "jsx"],
74
- },
75
- plugins: [
76
- [sigilPlugin, { hash, mode }],
77
- rewriteRelativeImports(route.filePath),
78
- ],
79
- filename: route.filePath,
80
- });
81
- let out = transpiler.transformSync(res?.code ?? "");
82
- for (const plugin of plugins) {
83
- if (plugin.transform)
84
- out = (await plugin.transform(out, route.filePath)) ?? out;
85
- }
86
- const rel = relative(process.cwd(), route.filePath)
87
- .replace(/\.[cm]?[jt]sx?$/, "")
88
- .replace(/[^a-zA-Z0-9._-]/g, "_");
89
- const nameBase = rel || basename(route.filePath).replace(/\.[jt]sx$/, "");
90
- const outPath = join(outDir, `${index}-${nameBase}.${mode}.js`);
91
- await Bun.write(outPath, out);
92
- map.set(route.filePath, outPath);
93
- }),
94
- );
95
- return map;
62
+ const map = new Map<string, string>();
63
+ // loader: "ts" Babel already consumed JSX, Bun only strips remaining types
64
+ const transpiler = new Bun.Transpiler({ loader: "ts", target: "browser" });
65
+
66
+ await Promise.all(
67
+ routes.map(async (route, index) => {
68
+ let code = await Bun.file(route.filePath).text();
69
+ const hash = computeHash(route.filePath);
70
+ code = code.replace(STYLE_RE, "");
71
+
72
+ const res = transformSync(code, {
73
+ configFile: false,
74
+ babelrc: false,
75
+ parserOpts: {
76
+ plugins: ["typescript", "jsx"], // no isTSX
77
+ },
78
+ plugins: [
79
+ [sigilPlugin, { hash, mode }],
80
+ rewriteRelativeImports(route.filePath),
81
+ ],
82
+ filename: route.filePath,
83
+ });
84
+ let out = transpiler.transformSync(res?.code ?? "");
85
+ for (const plugin of plugins) {
86
+ if (plugin.transform)
87
+ out = (await plugin.transform(out, route.filePath)) ?? out;
88
+ }
89
+
90
+ const rel = relative(process.cwd(), route.filePath)
91
+ .replace(/\.tsx?$/, "")
92
+ .replace(/[^a-zA-Z0-9._-]/g, "_");
93
+ const nameBase = rel || basename(route.filePath).replace(/\.[jt]sx$/, "");
94
+ const outPath = join(outDir, `${index}-${nameBase}.${mode}.js`);
95
+
96
+ await Bun.write(outPath, out);
97
+ map.set(route.filePath, outPath);
98
+ }),
99
+ );
100
+ return map;
96
101
  }
@@ -10,81 +10,81 @@ import type { BuildResult, GrimoireConfig, GrimoirePlugin } from "../types";
10
10
  import { runHook } from "./plugins";
11
11
 
12
12
  export async function buildProject(
13
- config: GrimoireConfig,
14
- plugins: GrimoirePlugin[] = [],
13
+ config: GrimoireConfig,
14
+ plugins: GrimoirePlugin[] = [],
15
15
  ): Promise<{ result: BuildResult; tree: RouteTree }> {
16
- await runHook(plugins, "onBuildStart");
16
+ await runHook(plugins, "onBuildStart");
17
17
 
18
- const { routes = "src/routes" } = config;
18
+ const { routes = "src/routes" } = config;
19
19
 
20
- const routesDir = isAbsolute(routes)
21
- ? routes.replace(/\0/g, "")
22
- : join(process.cwd(), routes).replace(/\0/g, "");
23
- const tree = await scanRoutes(routesDir, process.cwd());
20
+ const routesDir = isAbsolute(routes)
21
+ ? routes.replace(/\0/g, "")
22
+ : join(process.cwd(), routes).replace(/\0/g, "");
23
+ const tree = await scanRoutes(routesDir, process.cwd());
24
24
 
25
- await mkdir(join(process.cwd(), ".grimoire"), { recursive: true });
25
+ await mkdir(join(process.cwd(), ".grimoire"), { recursive: true });
26
26
 
27
- await generateTypes(tree, {
28
- projectRoot: process.cwd(),
29
- routesDir,
30
- outDir: join(process.cwd(), ".grimoire/types"),
31
- });
27
+ await generateTypes(tree, {
28
+ projectRoot: process.cwd(),
29
+ routesDir,
30
+ outDir: join(process.cwd(), ".grimoire/types"),
31
+ });
32
32
 
33
- const compiledDir = join(process.cwd(), ".grimoire/compiled");
34
- await mkdir(compiledDir, { recursive: true });
33
+ const compiledDir = join(process.cwd(), ".grimoire/compiled");
34
+ await mkdir(compiledDir, { recursive: true });
35
35
 
36
- const pageRoutes = tree.routes.filter(
37
- (r) => r.type === "page" || r.type === "simple",
38
- );
39
- const [hydrateFiles, domFiles] = await Promise.all([
40
- transformRoutes(pageRoutes, compiledDir, "hydrate", plugins),
41
- transformRoutes(pageRoutes, compiledDir, "dom", plugins),
42
- ]);
36
+ const pageRoutes = tree.routes.filter(
37
+ (r) => r.type === "page" || r.type === "simple",
38
+ );
39
+ const [hydrateFiles, domFiles] = await Promise.all([
40
+ transformRoutes(pageRoutes, compiledDir, "hydrate", plugins),
41
+ transformRoutes(pageRoutes, compiledDir, "dom", plugins),
42
+ ]);
43
43
 
44
- const hydrateManifest = join(process.cwd(), ".grimoire/_routes.hydrate.js");
45
- const domManifest = join(process.cwd(), ".grimoire/_routes.dom.js");
46
- await Promise.all([
47
- Bun.write(hydrateManifest, generateManifest(pageRoutes, hydrateFiles)),
48
- Bun.write(domManifest, generateManifest(pageRoutes, domFiles)),
49
- ]);
44
+ const hydrateManifest = join(process.cwd(), ".grimoire/_routes.hydrate.js");
45
+ const domManifest = join(process.cwd(), ".grimoire/_routes.dom.js");
46
+ await Promise.all([
47
+ Bun.write(hydrateManifest, generateManifest(pageRoutes, hydrateFiles)),
48
+ Bun.write(domManifest, generateManifest(pageRoutes, domFiles)),
49
+ ]);
50
50
 
51
- const makeRoutesPlugin = (manifestPath: string) => ({
52
- name: "grimoire-routes",
53
- setup(build: any) {
54
- build.onResolve({ filter: /^#grimoire-routes$/ }, () => ({
55
- path: manifestPath,
56
- }));
57
- },
58
- });
51
+ const makeRoutesPlugin = (manifestPath: string) => ({
52
+ name: "grimoire-routes",
53
+ setup(build: any) {
54
+ build.onResolve({ filter: /^#grimoire-routes$/ }, () => ({
55
+ path: manifestPath,
56
+ }));
57
+ },
58
+ });
59
59
 
60
- const [hydrateResult, domResult] = await Promise.all([
61
- Bun.build({
62
- entrypoints: [join(import.meta.dir, "../rendering/hydrate.ts")],
63
- outdir: join(process.cwd(), "public/__grimoire__"),
64
- plugins: [sigil({ mode: "hydrate" }), makeRoutesPlugin(hydrateManifest)],
65
- }),
66
- Bun.build({
67
- entrypoints: [join(import.meta.dir, "../client/index.ts")],
68
- outdir: join(process.cwd(), "public/__grimoire__"),
69
- plugins: [sigil({ mode: "dom" }), makeRoutesPlugin(domManifest)],
70
- }),
71
- ]);
60
+ const [hydrateResult, domResult] = await Promise.all([
61
+ Bun.build({
62
+ entrypoints: [join(import.meta.dir, "../rendering/hydrate.ts")],
63
+ outdir: join(process.cwd(), "public/__grimoire__"),
64
+ plugins: [sigil({ mode: "hydrate" }), makeRoutesPlugin(hydrateManifest)],
65
+ }),
66
+ Bun.build({
67
+ entrypoints: [join(import.meta.dir, "../client/index.ts")],
68
+ outdir: join(process.cwd(), "public/__grimoire__"),
69
+ plugins: [sigil({ mode: "dom" }), makeRoutesPlugin(domManifest)],
70
+ }),
71
+ ]);
72
72
 
73
- if (!hydrateResult.success) {
74
- for (const log of hydrateResult.logs) console.error(log);
75
- }
76
- if (!domResult.success) {
77
- for (const log of domResult.logs) console.error(log);
78
- }
73
+ if (!hydrateResult.success) {
74
+ for (const log of hydrateResult.logs) console.error(log);
75
+ }
76
+ if (!domResult.success) {
77
+ for (const log of domResult.logs) console.error(log);
78
+ }
79
79
 
80
- const result: BuildResult = {
81
- success: hydrateResult.success && domResult.success,
82
- outputs: [...hydrateResult.outputs, ...domResult.outputs].map(
83
- (o) => o.path,
84
- ),
85
- errors: [...hydrateResult.logs, ...domResult.logs].map(String),
86
- };
80
+ const result: BuildResult = {
81
+ success: hydrateResult.success && domResult.success,
82
+ outputs: [...hydrateResult.outputs, ...domResult.outputs].map(
83
+ (o) => o.path,
84
+ ),
85
+ errors: [...hydrateResult.logs, ...domResult.logs].map(String),
86
+ };
87
87
 
88
- await runHook(plugins, "onBuildEnd", result);
89
- return { result, tree };
88
+ await runHook(plugins, "onBuildEnd", result);
89
+ return { result, tree };
90
90
  }
@@ -57,7 +57,6 @@ export async function createServer(config: GrimoireConfig = {}) {
57
57
  _skipBuild = false,
58
58
  } = finalConfig;
59
59
 
60
- registerSSRPlugin(plugins);
61
60
  let tree: any;
62
61
  if (!config._skipBuild) {
63
62
  const { result, tree: _tree } = await buildProject(finalConfig, plugins);
@@ -71,6 +70,10 @@ export async function createServer(config: GrimoireConfig = {}) {
71
70
  tree = await scanRoutes(routesDir, process.cwd());
72
71
  }
73
72
 
73
+ // SSR plugin should NOT INTNERCEPT FILES BUNDLEDFOR CLIENT
74
+ // WHAT ARE WE DOINGGGGGGGGG
75
+ registerSSRPlugin(plugins);
76
+
74
77
  // Load hooks.index.ts
75
78
  const { handle: hooksHandle, init: hooksInit } = await loadHooks(
76
79
  process.cwd(),