honox 0.1.1 → 0.1.2

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
@@ -157,7 +157,7 @@ export default app
157
157
  Or simply, you can just return JSX.
158
158
 
159
159
  ```tsx
160
- export default function Home() {
160
+ export default function Home(_c: Context) {
161
161
  return <h1>Welcome!</h1>
162
162
  }
163
163
  ```
@@ -1,8 +1,12 @@
1
- import { Hydrate, CreateElement } from '../types.js';
1
+ import { Hydrate, CreateElement, CreateChildren } from '../types.js';
2
2
 
3
3
  type ClientOptions = {
4
4
  hydrate?: Hydrate;
5
5
  createElement?: CreateElement;
6
+ /**
7
+ * Create "children" attribute of a component from a list of child nodes
8
+ */
9
+ createChildren?: CreateChildren;
6
10
  ISLAND_FILES?: Record<string, () => Promise<unknown>>;
7
11
  island_root?: string;
8
12
  };
@@ -1,6 +1,6 @@
1
1
  import { render } from "hono/jsx/dom";
2
2
  import { jsx as jsxFn } from "hono/jsx/dom/jsx-runtime";
3
- import { COMPONENT_NAME, DATA_SERIALIZED_PROPS } from "../constants.js";
3
+ import { COMPONENT_NAME, DATA_HONO_TEMPLATE, DATA_SERIALIZED_PROPS } from "../constants.js";
4
4
  const createClient = async (options) => {
5
5
  const FILES = options?.ISLAND_FILES ?? import.meta.glob("/app/islands/**/[a-zA-Z0-9[-]+.(tsx|ts)");
6
6
  const root = options?.island_root ?? "/app/islands/";
@@ -17,6 +17,17 @@ const createClient = async (options) => {
17
17
  const props = JSON.parse(serializedProps ?? "{}");
18
18
  const hydrate = options?.hydrate ?? render;
19
19
  const createElement = options?.createElement ?? jsxFn;
20
+ const maybeTemplate = element.childNodes[element.childNodes.length - 1];
21
+ if (maybeTemplate?.nodeName === "TEMPLATE" && maybeTemplate?.attributes.getNamedItem(DATA_HONO_TEMPLATE) !== null) {
22
+ let createChildren = options?.createChildren;
23
+ if (!createChildren) {
24
+ const { buildCreateChildrenFn } = await import("./runtime");
25
+ createChildren = buildCreateChildrenFn(createElement);
26
+ }
27
+ props.children = await createChildren(
28
+ maybeTemplate.content.childNodes
29
+ );
30
+ }
20
31
  const newElem = await createElement(Component, props);
21
32
  await hydrate(newElem, element);
22
33
  });
@@ -0,0 +1,5 @@
1
+ import { CreateElement, CreateChildren } from '../types.js';
2
+
3
+ declare const buildCreateChildrenFn: (createElement: CreateElement) => CreateChildren;
4
+
5
+ export { buildCreateChildrenFn };
@@ -0,0 +1,81 @@
1
+ import { Suspense, use } from "hono/jsx/dom";
2
+ const buildCreateChildrenFn = (createElement) => {
3
+ const createChildren = async (childNodes) => {
4
+ const children = [];
5
+ for (let i = 0; i < childNodes.length; i++) {
6
+ const child = childNodes[i];
7
+ if (child.nodeType === 8) {
8
+ continue;
9
+ } else if (child.nodeType === 3) {
10
+ children.push(child.textContent);
11
+ } else if (child.nodeName === "TEMPLATE" && child.id.match(/(?:H|E):\d+/)) {
12
+ const placeholderElement = document.createElement("hono-placeholder");
13
+ placeholderElement.style.display = "none";
14
+ let resolve;
15
+ const promise = new Promise((r) => resolve = r);
16
+ child.replaceWith = (node) => {
17
+ createChildren(node.childNodes).then(resolve);
18
+ placeholderElement.remove();
19
+ };
20
+ let fallback = [];
21
+ for (
22
+ // equivalent to i++
23
+ placeholderElement.appendChild(child);
24
+ i < childNodes.length;
25
+ i++
26
+ ) {
27
+ const child2 = childNodes[i];
28
+ if (child2.nodeType === 8) {
29
+ placeholderElement.appendChild(child2);
30
+ i--;
31
+ break;
32
+ } else if (child2.nodeType === 3) {
33
+ fallback.push(child2.textContent);
34
+ } else {
35
+ fallback.push(
36
+ await createElement(child2.nodeName, {
37
+ children: await createChildren(child2.childNodes)
38
+ })
39
+ );
40
+ }
41
+ }
42
+ const fallbackTemplates = document.querySelectorAll(
43
+ `[data-hono-target="${child.id}"]`
44
+ );
45
+ if (fallbackTemplates.length > 0) {
46
+ const fallbackTemplate = fallbackTemplates[fallbackTemplates.length - 1];
47
+ fallback = await createChildren(fallbackTemplate.content.childNodes);
48
+ }
49
+ if (fallback.length === 0 && child.id.startsWith("E:")) {
50
+ let resolve2;
51
+ const promise2 = new Promise((r) => resolve2 = r);
52
+ fallback = await createElement(Suspense, {
53
+ fallback: [],
54
+ children: [await createElement(() => use(promise2), {})]
55
+ });
56
+ placeholderElement.insertBefore = (node) => {
57
+ createChildren(node.childNodes).then(resolve2);
58
+ };
59
+ }
60
+ document.body.appendChild(placeholderElement);
61
+ children.push(
62
+ await createElement(Suspense, {
63
+ fallback,
64
+ children: [await createElement(() => use(promise), {})]
65
+ })
66
+ );
67
+ } else {
68
+ children.push(
69
+ await createElement(child.nodeName, {
70
+ children: await createChildren(child.childNodes)
71
+ })
72
+ );
73
+ }
74
+ }
75
+ return children;
76
+ };
77
+ return createChildren;
78
+ };
79
+ export {
80
+ buildCreateChildrenFn
81
+ };
@@ -1,5 +1,6 @@
1
1
  declare const COMPONENT_NAME = "component-name";
2
2
  declare const DATA_SERIALIZED_PROPS = "data-serialized-props";
3
+ declare const DATA_HONO_TEMPLATE = "data-hono-template";
3
4
  declare const IMPORTING_ISLANDS_ID: "__importing_islands";
4
5
 
5
- export { COMPONENT_NAME, DATA_SERIALIZED_PROPS, IMPORTING_ISLANDS_ID };
6
+ export { COMPONENT_NAME, DATA_HONO_TEMPLATE, DATA_SERIALIZED_PROPS, IMPORTING_ISLANDS_ID };
package/dist/constants.js CHANGED
@@ -1,8 +1,10 @@
1
1
  const COMPONENT_NAME = "component-name";
2
2
  const DATA_SERIALIZED_PROPS = "data-serialized-props";
3
+ const DATA_HONO_TEMPLATE = "data-hono-template";
3
4
  const IMPORTING_ISLANDS_ID = "__importing_islands";
4
5
  export {
5
6
  COMPONENT_NAME,
7
+ DATA_HONO_TEMPLATE,
6
8
  DATA_SERIALIZED_PROPS,
7
9
  IMPORTING_ISLANDS_ID
8
10
  };
@@ -3,6 +3,7 @@ import { Manifest } from 'vite';
3
3
 
4
4
  type Options = {
5
5
  src: string;
6
+ async?: boolean;
6
7
  prod?: boolean;
7
8
  manifest?: Manifest;
8
9
  };
@@ -18,12 +18,19 @@ const Script = async (options) => {
18
18
  if (manifest) {
19
19
  const scriptInManifest = manifest[src.replace(/^\//, "")];
20
20
  if (scriptInManifest) {
21
- return /* @__PURE__ */ jsx(HasIslands, { children: /* @__PURE__ */ jsx("script", { type: "module", src: `/${scriptInManifest.file}` }) });
21
+ return /* @__PURE__ */ jsx(HasIslands, { children: /* @__PURE__ */ jsx(
22
+ "script",
23
+ {
24
+ type: "module",
25
+ async: !!options.async,
26
+ src: `/${scriptInManifest.file}`
27
+ }
28
+ ) });
22
29
  }
23
30
  }
24
31
  return /* @__PURE__ */ jsx(Fragment, {});
25
32
  } else {
26
- return /* @__PURE__ */ jsx("script", { type: "module", src });
33
+ return /* @__PURE__ */ jsx("script", { type: "module", async: !!options.async, src });
27
34
  }
28
35
  };
29
36
  export {
@@ -87,7 +87,7 @@ const createApp = (options) => {
87
87
  if (typeof routeDefault === "function") {
88
88
  subApp.get(path, setInnerMeta);
89
89
  subApp.get(path, (c) => {
90
- return c.render(routeDefault(), route);
90
+ return c.render(routeDefault(c), route);
91
91
  });
92
92
  }
93
93
  }
package/dist/types.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  /** JSX */
2
2
  type CreateElement = (type: any, props: any) => Node | Promise<Node>;
3
3
  type Hydrate = (children: Node, parent: Element) => void | Promise<void>;
4
+ type CreateChildren = (childNodes: NodeListOf<ChildNode>) => Node[] | Promise<Node[]>;
4
5
 
5
- export type { CreateElement, Hydrate };
6
+ export type { CreateChildren, CreateElement, Hydrate };
@@ -1,5 +1,5 @@
1
1
  const filePathToPath = (filePath) => {
2
- filePath = filePath.replace(/\.tsx?$/g, "").replace(/\.mdx$/g, "").replace(/^\/?index/, "/").replace(/\/index/, "").replace(/\[\.{3}.+\]/, "*").replace(/\[(.+)\]/, ":$1");
2
+ filePath = filePath.replace(/\.tsx?$/g, "").replace(/\.mdx$/g, "").replace(/^\/?index$/, "/").replace(/\/index$/, "").replace(/\[\.{3}.+\]/, "*").replace(/\[(.+?)\]/g, ":$1");
3
3
  return /^\//.test(filePath) ? filePath : "/" + filePath;
4
4
  };
5
5
  const groupByDirectory = (files) => {
@@ -24,13 +24,25 @@ import {
24
24
  conditionalExpression,
25
25
  memberExpression
26
26
  } from "@babel/types";
27
- import { COMPONENT_NAME, DATA_SERIALIZED_PROPS } from "../constants.js";
27
+ import { COMPONENT_NAME, DATA_HONO_TEMPLATE, DATA_SERIALIZED_PROPS } from "../constants.js";
28
28
  function addSSRCheck(funcName, componentName, isAsync = false) {
29
29
  const isSSR = memberExpression(
30
30
  memberExpression(identifier("import"), identifier("meta")),
31
31
  identifier("env.SSR")
32
32
  );
33
- const serializedProps = callExpression(identifier("JSON.stringify"), [identifier("props")]);
33
+ const serializedProps = callExpression(identifier("JSON.stringify"), [
34
+ callExpression(memberExpression(identifier("Object"), identifier("fromEntries")), [
35
+ callExpression(
36
+ memberExpression(
37
+ callExpression(memberExpression(identifier("Object"), identifier("entries")), [
38
+ identifier("props")
39
+ ]),
40
+ identifier("filter")
41
+ ),
42
+ [identifier('([key]) => key !== "children"')]
43
+ )
44
+ ])
45
+ ]);
34
46
  const ssrElement = jsxElement(
35
47
  jsxOpeningElement(
36
48
  jsxIdentifier("honox-island"),
@@ -50,6 +62,21 @@ function addSSRCheck(funcName, componentName, isAsync = false) {
50
62
  ),
51
63
  jsxClosingElement(jsxIdentifier(funcName)),
52
64
  []
65
+ ),
66
+ jsxExpressionContainer(
67
+ conditionalExpression(
68
+ memberExpression(identifier("props"), identifier("children")),
69
+ jsxElement(
70
+ jsxOpeningElement(
71
+ jsxIdentifier("template"),
72
+ [jsxAttribute(jsxIdentifier(DATA_HONO_TEMPLATE), stringLiteral(""))],
73
+ false
74
+ ),
75
+ jsxClosingElement(jsxIdentifier("template")),
76
+ [jsxExpressionContainer(memberExpression(identifier("props"), identifier("children")))]
77
+ ),
78
+ identifier("null")
79
+ )
53
80
  )
54
81
  ]
55
82
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "honox",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "main": "dist/index.js",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -113,7 +113,7 @@
113
113
  "@types/node": "^20.10.5",
114
114
  "eslint": "^8.56.0",
115
115
  "glob": "^10.3.10",
116
- "hono": "^4.0.1",
116
+ "hono": "^4.0.3",
117
117
  "np": "7.7.0",
118
118
  "prettier": "^3.1.1",
119
119
  "publint": "^0.2.7",