atollic 0.0.2 → 0.0.3

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
@@ -126,7 +126,9 @@ const app = new Elysia()
126
126
  export default app.handle;
127
127
  ```
128
128
 
129
- The Elysia adapter intercepts responses via `mapResponse`, extracts HTML from Solid's SSR format, ensures DOCTYPE, and injects production assets.
129
+ The Elysia adapter intercepts responses via `mapResponse`, extracts HTML, ensures DOCTYPE, and injects production assets.
130
+
131
+ The HTML-extraction step is **registry-based** — each `FrameworkAdapter` contributes an extractor function (via the `extractHtml` field) that knows how to convert its framework's SSR output (e.g. Solid's `{ t: "..." }` shape) into a plain HTML string. The atollic Vite plugin wires these registrations up automatically before the first request, so the core stays framework-agnostic. Plain strings are always recognized as a fallback. See [Writing a framework adapter](#writing-a-framework-adapter) and the [`registerHtmlExtractor`](#atollic) API.
130
132
 
131
133
  ### Hono
132
134
 
@@ -280,9 +282,9 @@ Listen to lifecycle events on `document`:
280
282
  | `atollic:before-morph` | `{ newDoc, mountedIslands }` | Cancelable, before HMR page morph |
281
283
  | `atollic:after-morph` | `{ defaultSwap }` | After HMR page morph |
282
284
 
283
- ## htmx integration
285
+ ## htmx, Alpine, and other DOM-mutating libraries
284
286
 
285
- Islands hydrate automatically after htmx swaps. The client runtime listens for `htmx:afterSettle` and mounts any new `[data-island]` elements. A `MutationObserver` also catches dynamically added islands from any source.
287
+ Islands added to the DOM after the initial mount — by htmx swaps, Alpine templates, or any other source — are picked up automatically. The client runtime installs a `MutationObserver` on `document.body` and mounts any newly inserted `[data-island]` element. No library-specific event listener is required.
286
288
 
287
289
  ## CSS handling
288
290
 
@@ -348,10 +350,22 @@ export function myFramework(): FrameworkAdapter {
348
350
 
349
351
  // Optional: script tag for hydration bootstrap (e.g., Solid's _$HY)
350
352
  hydrationScript: `<script>/* bootstrap */</script>`,
353
+
354
+ // Optional: source code for an extractor function `(value) => string | null`.
355
+ // Atollic includes this in the server boot module so the runtime knows
356
+ // how to convert this framework's SSR output (e.g. Solid's `{ t: "..." }`
357
+ // shape) into a plain HTML string. Frameworks whose SSR output is already
358
+ // a string can omit this — plain strings are always recognized.
359
+ extractHtml: `(value) => {
360
+ if (value && typeof value === "object" && "t" in value) return value.t;
361
+ return null;
362
+ }`,
351
363
  };
352
364
  }
353
365
  ```
354
366
 
367
+ The `extractHtml` strings from every registered adapter are emitted into a generated server-boot module that runs once before the first request. Each one calls `registerHtmlExtractor()` on the atollic core, which `extractHtml` (used internally by the Elysia and Hono adapters) iterates in order. This is how the core stays decoupled from any specific UI framework's SSR output shape.
368
+
355
369
  ## API reference
356
370
 
357
371
  ### `atollic/vite`
@@ -368,9 +382,20 @@ atollic(options: AtollicOptions): Plugin[]
368
382
  ### `atollic`
369
383
 
370
384
  ```ts
371
- html(input: string): Response // Wrap HTML string in a Response with DOCTYPE
385
+ // Wrap an HTML string (or async JSX) in a Response with DOCTYPE.
386
+ // Returns a Promise<Response> when given a Promise<string>.
387
+ html(input: string | Promise<string>): Response | Promise<Response>
388
+
372
389
  setProductionAssets(assets: string): void // Set production asset tags
373
390
  getProductionAssets(): string | undefined // Get production asset tags
391
+
392
+ // Register a function that converts a framework-specific SSR output value
393
+ // into an HTML string (or returns null if it doesn't recognize the shape).
394
+ // Normally wired up automatically by the Vite plugin from each adapter's
395
+ // `extractHtml` field — call this directly only if you need a custom one.
396
+ // Returns a dispose function.
397
+ type HtmlExtractor = (value: unknown) => string | null
398
+ registerHtmlExtractor(fn: HtmlExtractor): () => void
374
399
  ```
375
400
 
376
401
  ### `atollic/elysia`
@@ -402,15 +427,26 @@ solid(): FrameworkAdapter // Solid.js framework adapter
402
427
  Types for server-side JSX:
403
428
 
404
429
  ```ts
405
- type Component<P = {}> = (props: P & { children?: Children }) => string
406
- type Children = string | string[] | Promise<string>
430
+ type Children =
431
+ | string
432
+ | number
433
+ | bigint
434
+ | boolean
435
+ | null
436
+ | undefined
437
+ | Promise<Children>
438
+ | Children[]
439
+
440
+ type Component<T = {}> = (
441
+ props: T & { children?: Children },
442
+ ) => string | Promise<string>
407
443
  ```
408
444
 
409
445
  ## Exports
410
446
 
411
447
  | Export | Description |
412
448
  |---|---|
413
- | `atollic` | Core — `html()`, `setProductionAssets()`, `getProductionAssets()` |
449
+ | `atollic` | Core — `html()`, `setProductionAssets()`, `getProductionAssets()`, `registerHtmlExtractor()` |
414
450
  | `atollic/vite` | Vite plugin |
415
451
  | `atollic/client` | Client runtime (auto-imported) |
416
452
  | `atollic/adapter` | `FrameworkAdapter` type |
package/dist/adapter.d.ts CHANGED
@@ -36,4 +36,20 @@ export interface FrameworkAdapter {
36
36
  * can omit this.
37
37
  */
38
38
  hydrationScript?: string;
39
+ /**
40
+ * Source code for an extractor function with the signature
41
+ * `(value: unknown) => string | null`.
42
+ *
43
+ * The atollic Vite plugin includes this in the server boot module so the
44
+ * runtime knows how to convert this framework's SSR output (e.g. Solid's
45
+ * `{ t: "..." }` shape) into a plain HTML string. Frameworks whose SSR
46
+ * output is already a string can omit this — the core falls back to
47
+ * recognizing plain strings on its own.
48
+ *
49
+ * @example
50
+ * extractHtml: `(value) => value && typeof value === "object" && "t" in value
51
+ * ? value.t
52
+ * : null`
53
+ */
54
+ extractHtml?: string;
39
55
  }
@@ -30,7 +30,8 @@ function n() {
30
30
  return a;
31
31
  },
32
32
  clientRuntime: "\nimport { hydrate as _hydrate, render as _render } from \"solid-js/web\";\nimport { $DEVCOMP } from \"solid-js\";\n\nexport function hydrateIsland(el, Component, props, id) {\n if ($DEVCOMP && !($DEVCOMP in Component)) {\n Component[$DEVCOMP] = true;\n }\n return _hydrate(() => Component(props), el, { renderId: id });\n}\n\nexport function renderIsland(el, Component, props) {\n if ($DEVCOMP && !($DEVCOMP in Component)) {\n Component[$DEVCOMP] = true;\n }\n el.textContent = \"\";\n return _render(() => Component(props), el);\n}\n",
33
- hydrationScript: "<script>(self._$HY||(self._$HY={events:[],completed:new WeakSet,r:{}}))<\/script>"
33
+ hydrationScript: "<script>(self._$HY||(self._$HY={events:[],completed:new WeakSet,r:{}}))<\/script>",
34
+ extractHtml: "(value) => {\n if (value && typeof value === \"object\" && \"t\" in value && typeof value.t === \"string\") {\n return value.t;\n }\n if (Array.isArray(value)) {\n const html = value.flat(Infinity).map((item) => {\n if (typeof item === \"string\") return item;\n if (item && typeof item === \"object\" && \"t\" in item) return item.t;\n return \"\";\n }).join(\"\");\n return html || null;\n }\n return null;\n}"
34
35
  };
35
36
  }
36
37
  //#endregion
package/dist/index.d.ts CHANGED
@@ -1 +1,2 @@
1
- export { getProductionAssets, html, setProductionAssets } from "./server.js";
1
+ export type { HtmlExtractor } from "./server.js";
2
+ export { getProductionAssets, html, registerHtmlExtractor, setProductionAssets, } from "./server.js";
package/dist/index.js CHANGED
@@ -1,2 +1,3 @@
1
- import { n as e, r as t, t as n } from "./server-DhToIUB0.js";
2
- export { n as getProductionAssets, e as html, t as setProductionAssets };
1
+ import { o as e } from "./shared-Bv39_NsU.js";
2
+ import { n as t, r as n, t as r } from "./server-DAuZqGuP.js";
3
+ export { r as getProductionAssets, t as html, e as registerHtmlExtractor, n as setProductionAssets };
@@ -1,4 +1,4 @@
1
- import { t as e } from "./shared-CfRCLggd.js";
1
+ import { t as e } from "./shared-Bv39_NsU.js";
2
2
  //#region src/server.ts
3
3
  var t = "__atollicProdAssets";
4
4
  function n(e) {
package/dist/server.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  /**
2
2
  * Server-side helpers for atollic.
3
3
  */
4
+ export type { HtmlExtractor } from "./shared.js";
5
+ export { registerHtmlExtractor } from "./shared.js";
4
6
  /**
5
7
  * Set the production asset tags (scripts + CSS) to inject into HTML responses.
6
8
  * Called by the generated production entry before the server starts.
@@ -1,5 +1,5 @@
1
- import { a as e, i as t, n } from "../shared-CfRCLggd.js";
2
- import { t as r } from "../server-DhToIUB0.js";
1
+ import { a as e, i as t, n } from "../shared-Bv39_NsU.js";
2
+ import { t as r } from "../server-DAuZqGuP.js";
3
3
  import { Elysia as i } from "elysia";
4
4
  //#region src/servers/elysia.ts
5
5
  function a() {
@@ -1,5 +1,5 @@
1
- import { a as e, i as t } from "../shared-CfRCLggd.js";
2
- import { t as n } from "../server-DhToIUB0.js";
1
+ import { a as e, i as t } from "../shared-Bv39_NsU.js";
2
+ import { t as n } from "../server-DAuZqGuP.js";
3
3
  //#region src/servers/hono.ts
4
4
  function r() {
5
5
  return async (r, i) => {
@@ -0,0 +1,39 @@
1
+ //#region src/shared.ts
2
+ var e = /<meta\s+name="atollic-head"\s*\/?>/;
3
+ function t(t, n) {
4
+ let r = t.replace(e, n);
5
+ return r === t ? t.includes("</head>") ? t.replace("</head>", ` ${n}\n</head>`) : t : r;
6
+ }
7
+ var n = "__atollicHtmlExtractors";
8
+ function r() {
9
+ let e = globalThis;
10
+ return Array.isArray(e[n]) || (e[n] = []), e[n];
11
+ }
12
+ function i(e) {
13
+ let t = r();
14
+ return t.push(e), () => {
15
+ let n = t.indexOf(e);
16
+ n >= 0 && t.splice(n, 1);
17
+ };
18
+ }
19
+ function a(e) {
20
+ for (let t of r()) {
21
+ let n = t(e);
22
+ if (n !== null) return n;
23
+ }
24
+ return typeof e == "string" ? e : null;
25
+ }
26
+ function o(e) {
27
+ let t = e.trimStart();
28
+ return t.startsWith("<html") && !t.startsWith("<!DOCTYPE") ? `<!DOCTYPE html>\n${e}` : e;
29
+ }
30
+ function s(e) {
31
+ let t = e.trimStart();
32
+ return t.startsWith("<!DOCTYPE") || t.startsWith("<html");
33
+ }
34
+ function c(e, n) {
35
+ let r = o(e);
36
+ return n && (r = t(r, n)), r;
37
+ }
38
+ //#endregion
39
+ export { c as a, s as i, a as n, i as o, t as r, o as t };
package/dist/shared.d.ts CHANGED
@@ -1,8 +1,23 @@
1
1
  /** Replace <Head /> marker with tags, or fall back to </head> injection. */
2
2
  export declare function injectHead(html: string, tags: string): string;
3
3
  /**
4
- * Extract HTML from Solid's SSR output format.
5
- * Handles `{t: "<html>"}` objects and arrays of fragments.
4
+ * A function that converts a framework-specific SSR output value into an HTML
5
+ * string, or returns `null` if it doesn't recognize the shape.
6
+ */
7
+ export type HtmlExtractor = (value: unknown) => string | null;
8
+ /**
9
+ * Register a function that extracts an HTML string from a framework-specific
10
+ * SSR output. The atollic Vite plugin wires this up automatically — every
11
+ * `FrameworkAdapter` with an `extractHtml` source string gets registered
12
+ * before the first request is served.
13
+ *
14
+ * @returns A dispose function that removes the extractor.
15
+ */
16
+ export declare function registerHtmlExtractor(fn: HtmlExtractor): () => void;
17
+ /**
18
+ * Convert a server response value into an HTML string by trying each
19
+ * registered framework extractor, then falling back to a plain string.
20
+ * Returns `null` when nothing recognizes the shape.
6
21
  */
7
22
  export declare function extractHtml(value: unknown): string | null;
8
23
  /** Prepend DOCTYPE if the string looks like HTML but is missing it. */
package/dist/vite.js CHANGED
@@ -1,23 +1,23 @@
1
- import { i as e, n as t, r as n, t as r } from "./shared-CfRCLggd.js";
1
+ import { i as e, n as t, r as n, t as r } from "./shared-Bv39_NsU.js";
2
2
  import { readFileSync as i } from "node:fs";
3
3
  import { glob as a, readFile as o } from "node:fs/promises";
4
4
  import { basename as s, dirname as c, extname as l, relative as u, resolve as d } from "node:path";
5
5
  import { fileURLToPath as f } from "node:url";
6
6
  import { transformWithOxc as p } from "vite";
7
7
  //#region src/vite.ts
8
- var m = c(f(import.meta.url)), h = "virtual:atollic/client", g = `\0${h}`, _ = "virtual:atollic/prod-entry", v = `\0${_}`, y = "virtual:atollic/fw-", b = "atollic:reload", x = "?raw-island", S = /\.[jt]sx?$/, C = /\.[jt]sx$/, w = /\.css($|\?)/;
9
- function T(e) {
8
+ var m = c(f(import.meta.url)), h = "virtual:atollic/client", g = `\0${h}`, _ = "virtual:atollic/prod-entry", v = `\0${_}`, y = "virtual:atollic/server-boot", b = `\0${y}`, x = "virtual:atollic/fw-", S = "atollic:reload", C = "?raw-island", w = /\.[jt]sx?$/, T = /\.[jt]sx$/, E = /\.css($|\?)/;
9
+ function D(e) {
10
10
  let t = e.replace(/^\s*(\/\*[\s\S]*?\*\/\s*|\/\/[^\n]*\n\s*)*/g, "").match(/^["']use client(?::(\w+))?["']/);
11
11
  return t ? {
12
12
  hasDirective: !0,
13
13
  framework: t[1]
14
14
  } : { hasDirective: !1 };
15
15
  }
16
- function E(e) {
16
+ function O(e) {
17
17
  return e.replace(/^["']use client(?::\w+)?["'];?\s*/m, "");
18
18
  }
19
- function D(e, t) {
20
- let n = [], r = E(e), i = r.match(/export\s+default\s+(?:function|class)\s+([A-Z]\w*)/);
19
+ function k(e, t) {
20
+ let n = [], r = O(e), i = r.match(/export\s+default\s+(?:function|class)\s+([A-Z]\w*)/);
21
21
  i ? n.push({
22
22
  exportName: "default",
23
23
  islandName: i[1]
@@ -31,7 +31,10 @@ function D(e, t) {
31
31
  });
32
32
  return n;
33
33
  }
34
- function O(e, t, n, r) {
34
+ function A(e) {
35
+ return e.map((e) => e.extractHtml).filter((e) => !!e).map((e) => `registerHtmlExtractor(${e});`).join("\n");
36
+ }
37
+ function j(e, t, n, r) {
35
38
  let i = [...n].map((e) => `import "${e}";`).join("\n"), a = [...t].map((e) => `import "/${u(r, e)}";`).join("\n"), o = /* @__PURE__ */ new Map();
36
39
  for (let [t, n] of Object.entries(e)) {
37
40
  let e = n.adapter.name;
@@ -40,7 +43,7 @@ function O(e, t, n, r) {
40
43
  let s = "import { registerIsland, registerFramework, initRuntime } from \"atollic/client\";\n";
41
44
  s += `${i}\n`, s += `${a}\n\n`;
42
45
  for (let [e, t] of o) {
43
- let n = `${y}${e}`;
46
+ let n = `${x}${e}`;
44
47
  s += `import { hydrateIsland as __hydrate_${e}, renderIsland as __render_${e} } from "${n}";\n`, s += `registerFramework("${e}", __hydrate_${e}, __render_${e});\n\n`;
45
48
  for (let [n, i] of t) {
46
49
  let t = `/${u(r, i.absPath)}`;
@@ -49,7 +52,7 @@ function O(e, t, n, r) {
49
52
  }
50
53
  return s += "\ninitRuntime();\n", s;
51
54
  }
52
- function k(e) {
55
+ function M(e) {
53
56
  let t = new URL(e.url ?? "/", `http://${e.headers.host || "localhost"}`), n = new Headers();
54
57
  for (let [t, r] of Object.entries(e.headers)) if (r) if (Array.isArray(r)) for (let e of r) n.append(t, e);
55
58
  else n.set(t, r);
@@ -61,48 +64,48 @@ function k(e) {
61
64
  e.on("data", (e) => t.enqueue(e)), e.on("end", () => t.close()), e.on("error", (e) => t.error(e));
62
65
  } }), r.duplex = "half"), new Request(t, r);
63
66
  }
64
- function A(e, t) {
67
+ function N(e, t) {
65
68
  let n = [];
66
69
  if (t) for (let e of t) n.push(`<link rel="stylesheet" href="${e}">`);
67
70
  for (let t of e) t.hydrationScript && n.push(t.hydrationScript);
68
71
  return n.push(`<script type="module">import "${h}";<\/script>`), n.join("\n ");
69
72
  }
70
- function j(e, t) {
73
+ function P(e, t) {
71
74
  let n = /* @__PURE__ */ new Set(), r = /* @__PURE__ */ new Set();
72
75
  function i(t) {
73
76
  if (r.has(t)) return;
74
77
  r.add(t);
75
78
  let a = e.moduleGraph.getModuleById(t);
76
79
  if (a) {
77
- w.test(a.url) && n.add(a.url);
80
+ E.test(a.url) && n.add(a.url);
78
81
  for (let e of a.ssrImportedModules) i(e.id ?? e.url);
79
82
  }
80
83
  }
81
84
  let a = e.moduleGraph.getModuleById(t);
82
85
  return a && i(a.id ?? a.url), n;
83
86
  }
84
- function M(u) {
85
- let f = u.frameworks ?? [], w = f[0], M = "", N = {}, P = /* @__PURE__ */ new Set(), F = /* @__PURE__ */ new Set(), I = "", L = 3e3, R = null, z = "", B = !1, V = null;
86
- function H(e) {
87
- return e ? f.find((t) => t.name === e) : w;
87
+ function F(u) {
88
+ let f = u.frameworks ?? [], E = f[0], F = "", I = {}, L = /* @__PURE__ */ new Set(), R = /* @__PURE__ */ new Set(), z = "", B = 3e3, V = null, H = "", U = !1, W = null;
89
+ function G(e) {
90
+ return e ? f.find((t) => t.name === e) : E;
88
91
  }
89
- function U() {
90
- if (!R) return;
91
- let e = R.moduleGraph.getModuleById(g);
92
- e && R.moduleGraph.invalidateModule(e);
92
+ function K() {
93
+ if (!V) return;
94
+ let e = V.moduleGraph.getModuleById(g);
95
+ e && V.moduleGraph.invalidateModule(e);
93
96
  }
94
- function W(e, t) {
95
- N[t] || (N[t] = e, U());
97
+ function q(e, t) {
98
+ I[t] || (I[t] = e, K());
96
99
  }
97
- function G(e, t, n) {
98
- let r = D(t, s(e, l(e)));
99
- for (let t of r) W({
100
+ function J(e, t, n) {
101
+ let r = k(t, s(e, l(e)));
102
+ for (let t of r) q({
100
103
  absPath: e,
101
104
  exportName: t.exportName,
102
105
  adapter: n
103
106
  }, t.islandName);
104
107
  }
105
- let K = {
108
+ let Y = {
106
109
  name: "atollic",
107
110
  enforce: "pre",
108
111
  sharedDuringBuild: !0,
@@ -129,31 +132,31 @@ function M(u) {
129
132
  },
130
133
  async buildApp(e) {
131
134
  await e.build(e.environments.client);
132
- let t = d(M, "dist/client/.vite/manifest.json"), n = JSON.parse(await o(t, "utf-8")), r = n.client ?? n["virtual:atollic/client"], i = [];
135
+ let t = d(F, "dist/client/.vite/manifest.json"), n = JSON.parse(await o(t, "utf-8")), r = n.client ?? n["virtual:atollic/client"], i = [];
133
136
  for (let e of Object.values(n)) if (e.css) for (let t of e.css) i.push(`<link rel="stylesheet" href="/${t}">`);
134
137
  for (let e of f) e.hydrationScript && i.push(e.hydrationScript);
135
- r && i.push(`<script type="module" src="/${r.file}"><\/script>`), z = i.join("\n "), await e.build(e.environments.ssr);
138
+ r && i.push(`<script type="module" src="/${r.file}"><\/script>`), H = i.join("\n "), await e.build(e.environments.ssr);
136
139
  },
137
140
  async configResolved(e) {
138
- M = e.root, I = d(M, u.entry), L = e.server.port ?? 3e3;
141
+ F = e.root, z = d(F, u.entry), B = e.server.port ?? 3e3;
139
142
  },
140
143
  async buildStart() {
141
- if (B) return;
142
- B = !0;
144
+ if (U) return;
145
+ U = !0;
143
146
  let e = /import\s+['"]([^'"]+\.css)['"]/g, t = /import\s.*?from\s+['"](\.\.?\/[^'"]+)['"]/g, n = /* @__PURE__ */ new Set();
144
147
  function r(t, n) {
145
- let r = T(n);
146
- if (r.hasDirective) if (C.test(t)) {
147
- let e = H(r.framework);
148
- e ? G(t, n, e) : r.framework && console.warn(`[atollic] Unknown framework "${r.framework}" in ${t}`);
149
- } else P.add(t);
150
- for (let t of n.matchAll(e)) F.add(t[1]);
148
+ let r = D(n);
149
+ if (r.hasDirective) if (T.test(t)) {
150
+ let e = G(r.framework);
151
+ e ? J(t, n, e) : r.framework && console.warn(`[atollic] Unknown framework "${r.framework}" in ${t}`);
152
+ } else L.add(t);
153
+ for (let t of n.matchAll(e)) R.add(t[1]);
151
154
  }
152
155
  for await (let e of a("**/*.{ts,tsx,js,jsx}", {
153
- cwd: M,
156
+ cwd: F,
154
157
  exclude: ["node_modules/**", "dist/**"]
155
158
  })) {
156
- let t = d(M, e);
159
+ let t = d(F, e);
157
160
  n.add(t);
158
161
  try {
159
162
  r(t, await o(t, "utf-8"));
@@ -183,23 +186,31 @@ function M(u) {
183
186
  } catch {}
184
187
  }
185
188
  }
186
- await i(I);
189
+ await i(z);
187
190
  },
188
191
  resolveId(e) {
189
192
  if (e === h) return g;
190
193
  if (e === _) return v;
191
- if (e.startsWith(y)) return `\0${e}`;
192
- if (e.endsWith(x)) return `${e.slice(0, -11)}${x}`;
194
+ if (e === y) return b;
195
+ if (e.startsWith(x)) return `\0${e}`;
196
+ if (e.endsWith(C)) return `${e.slice(0, -11)}${C}`;
193
197
  },
194
198
  load(e, t) {
195
- if (e === g) return O(N, P, F, M);
196
- if (e === v) return `import { resolve, join } from "node:path";
197
- import { setProductionAssets } from "${d(m, "index.js")}";
198
- setProductionAssets(${JSON.stringify(z)});
199
- import handler from "${I}";
199
+ if (e === g) return j(I, L, R, F);
200
+ if (e === b) {
201
+ let e = d(m, "index.js"), t = A(f);
202
+ return t ? `import { registerHtmlExtractor } from "${e}";\n${t}\n` : "// no html extractors registered\n";
203
+ }
204
+ if (e === v) {
205
+ let e = d(m, "index.js"), t = A(f);
206
+ return `import { resolve, join } from "node:path";
207
+ import { setProductionAssets, registerHtmlExtractor } from "${e}";
208
+ setProductionAssets(${JSON.stringify(H)});
209
+ ${t}
210
+ import handler from "${z}";
200
211
 
201
212
  const clientDir = resolve(import.meta.dirname, "..", "client");
202
- const port = process.env.PORT || ${L};
213
+ const port = process.env.PORT || ${B};
203
214
 
204
215
  Bun.serve({
205
216
  port,
@@ -215,45 +226,48 @@ Bun.serve({
215
226
  });
216
227
  console.log(\`Listening on http://localhost:\${port}\`);
217
228
  `;
218
- if (e.startsWith("\0" + y)) {
219
- let t = e.slice(("\0" + y).length), n = f.find((e) => e.name === t);
229
+ }
230
+ if (e.startsWith("\0" + x)) {
231
+ let t = e.slice(("\0" + x).length), n = f.find((e) => e.name === t);
220
232
  if (n) return n.clientRuntime;
221
233
  }
222
- if (e.endsWith(x)) {
234
+ if (e.endsWith(C)) {
223
235
  let t = e.slice(0, -11);
224
236
  try {
225
- return E(i(t, "utf-8"));
237
+ return O(i(t, "utf-8"));
226
238
  } catch {}
227
239
  return;
228
240
  }
229
- if (!(!t?.ssr || !S.test(e))) try {
230
- let t = i(e, "utf-8"), n = T(t);
241
+ if (!(!t?.ssr || !w.test(e))) try {
242
+ let t = i(e, "utf-8"), n = D(t);
231
243
  if (n.hasDirective) {
232
- if (!C.test(e)) return P.add(e), U(), "export default {};";
233
- let r = H(n.framework);
244
+ if (!T.test(e)) return L.add(e), K(), "export default {};";
245
+ let r = G(n.framework);
234
246
  if (!r) {
235
247
  n.framework && console.warn(`[atollic] Unknown framework "${n.framework}" in ${e}`);
236
248
  return;
237
249
  }
238
- let i = D(t, s(e, l(e)));
239
- for (let t of i) W({
250
+ let i = k(t, s(e, l(e)));
251
+ for (let t of i) q({
240
252
  absPath: e,
241
253
  exportName: t.exportName,
242
254
  adapter: r
243
255
  }, t.islandName);
244
- let a = `${e}${x}`;
256
+ let a = `${e}${C}`;
245
257
  return r.ssrStub(a, i);
246
258
  }
247
259
  } catch {}
248
260
  },
249
261
  configureServer(i) {
250
- R = i, i.middlewares.use(async (a, o, s) => {
262
+ V = i, i.ssrLoadModule(y).catch((e) => {
263
+ console.error("[atollic] Failed to load server boot module:", e);
264
+ }), i.middlewares.use(async (a, o, s) => {
251
265
  let c = a.url || "";
252
266
  if (c.startsWith("/@") || c.startsWith("/node_modules") || c.startsWith("/__") || /\.\w+($|\?)/.test(c)) return s();
253
267
  try {
254
- let l = await i.ssrLoadModule(I), u = l.default ?? l.handler;
268
+ let l = await i.ssrLoadModule(z), u = l.default ?? l.handler;
255
269
  if (typeof u != "function") return console.error("[atollic] Entry must default-export a fetch function: (req: Request) => Response"), s();
256
- let d = await u(k(a));
270
+ let d = await u(M(a));
257
271
  if (d.status === 404) return s();
258
272
  let p = await d.text(), m = d.headers.get("content-type") || "";
259
273
  if (m.includes("application/json")) try {
@@ -261,7 +275,7 @@ console.log(\`Listening on http://localhost:\${port}\`);
261
275
  e && (p = e, m = "");
262
276
  } catch {}
263
277
  let h = e(p);
264
- !m.includes("text/html") && h && (m = "text/html; charset=utf-8"), h && (p = r(p), V ||= j(i, I), p = n(p, A(f, V)), p = await i.transformIndexHtml(c, p));
278
+ !m.includes("text/html") && h && (m = "text/html; charset=utf-8"), h && (p = r(p), W ||= P(i, z), p = n(p, N(f, W)), p = await i.transformIndexHtml(c, p));
265
279
  let g = {};
266
280
  for (let [e, t] of d.headers) g[e] = t;
267
281
  m ? g["content-type"] = m : g["content-type"] ||= "text/html; charset=utf-8", o.writeHead(d.status, g), o.end(p);
@@ -271,23 +285,23 @@ console.log(\`Listening on http://localhost:\${port}\`);
271
285
  });
272
286
  },
273
287
  handleHotUpdate({ file: e, server: t }) {
274
- if (V = null, !e.endsWith(".ts") && !e.endsWith(".tsx")) return;
288
+ if (W = null, !e.endsWith(".ts") && !e.endsWith(".tsx")) return;
275
289
  try {
276
- if (T(i(e, "utf-8")).hasDirective) return;
290
+ if (D(i(e, "utf-8")).hasDirective) return;
277
291
  } catch {}
278
292
  let n = t.moduleGraph.getModulesByFile(e);
279
293
  if (n) for (let e of n) t.moduleGraph.invalidateModule(e);
280
294
  return t.ws.send({
281
295
  type: "custom",
282
- event: b
296
+ event: S
283
297
  }), [];
284
298
  }
285
- }, q = /@jsxImportSource\s+(\S+)/, J = {
299
+ }, X = /@jsxImportSource\s+(\S+)/, Z = {
286
300
  name: "atollic:jsx-pre-transform",
287
301
  enforce: "pre",
288
302
  async transform(e, t) {
289
303
  if (!/\.[jt]sx$/.test(t)) return;
290
- let n = e.match(q);
304
+ let n = e.match(X);
291
305
  if (n) {
292
306
  let e = n[1];
293
307
  for (let t of f) if (e.includes(t.name)) return;
@@ -301,13 +315,13 @@ console.log(\`Listening on http://localhost:\${port}\`);
301
315
  map: r.map
302
316
  };
303
317
  }
304
- }, Y = [];
305
- for (let e of f) e.plugins && Y.push(...e.plugins());
318
+ }, Q = [];
319
+ for (let e of f) e.plugins && Q.push(...e.plugins());
306
320
  return [
307
- J,
308
- ...Y,
309
- K
321
+ Z,
322
+ ...Q,
323
+ Y
310
324
  ];
311
325
  }
312
326
  //#endregion
313
- export { M as atollic };
327
+ export { F as atollic };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "atollic",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "Island architecture for WinterCG-compatible runtimes. Bring-your-own server (Elysia, Hono, …) and UI framework, powered by Vite.",
5
5
  "keywords": [
6
6
  "wintercg",
@@ -1,23 +0,0 @@
1
- //#region src/shared.ts
2
- var e = /<meta\s+name="atollic-head"\s*\/?>/;
3
- function t(t, n) {
4
- let r = t.replace(e, n);
5
- return r === t ? t.includes("</head>") ? t.replace("</head>", ` ${n}\n</head>`) : t : r;
6
- }
7
- function n(e) {
8
- return e && typeof e == "object" && "t" in e && typeof e.t == "string" ? e.t : Array.isArray(e) ? e.flat(Infinity).map((e) => typeof e == "string" ? e : e && typeof e == "object" && "t" in e ? e.t : "").join("") || null : typeof e == "string" ? e : null;
9
- }
10
- function r(e) {
11
- let t = e.trimStart();
12
- return t.startsWith("<html") && !t.startsWith("<!DOCTYPE") ? `<!DOCTYPE html>\n${e}` : e;
13
- }
14
- function i(e) {
15
- let t = e.trimStart();
16
- return t.startsWith("<!DOCTYPE") || t.startsWith("<html");
17
- }
18
- function a(e, n) {
19
- let i = r(e);
20
- return n && (i = t(i, n)), i;
21
- }
22
- //#endregion
23
- export { a, i, n, t as r, r as t };