honox 0.0.1 → 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
@@ -1,15 +1,15 @@
1
1
  # HonoX
2
2
 
3
- **HonoX** is a simple and fast meta framework for creating websites and Web APIs with Server-Side Rendering - (formerly _[Sonik](https://github.com/sonikjs/sonik)_). It stands on the shoulders of giants; built on [Hono](https://hono.dev/), [Vite](https://hono.dev/), and UI libraries.
3
+ **HonoX** is a simple and fast - _supersonic_ - meta framework for creating full-stack websites or Web APIs - (formerly _[Sonik](https://github.com/sonikjs/sonik)_). It stands on the shoulders of giants; built on [Hono](https://hono.dev/), [Vite](https://hono.dev/), and UI libraries.
4
4
 
5
- **Note**: _HonoX is currently in a "beta stage". There will be breaking changes without any announcement. Don't use it in production. However, feel free to try it in your hobby project and give us your feedback!_
5
+ **Note**: _HonoX is currently in a "beta stage". Breaking changes are introduced without following semantic versioning._
6
6
 
7
7
  ## Features
8
8
 
9
- - **File-based routing** - You can create a large app by separating concerns.
9
+ - **File-based routing** - You can create a large application like Next.js.
10
10
  - **Fast SSR** - Rendering is ultra-fast thanks to Hono.
11
11
  - **BYOR** - You can bring your own renderer, not only one using hono/jsx.
12
- - **Island hydration** - If you want interactions, create an island. JavaScript is hydrated only for it.
12
+ - **Islands hydration** - If you want interactions, create an island. JavaScript is hydrated only for it.
13
13
  - **Middleware** - It works as Hono, so you can use a lot of Hono's middleware.
14
14
 
15
15
  ## Get Started - Basic
@@ -154,7 +154,7 @@ Before writing `_renderer.tsx`, write the Renderer type definition in `global.d.
154
154
 
155
155
  ```ts
156
156
  // app/global.d.ts
157
- import 'hono'
157
+ import type {} from 'hono'
158
158
 
159
159
  type Head = {
160
160
  title?: string
@@ -179,7 +179,7 @@ export default jsxRenderer(({ children, title }) => {
179
179
  <head>
180
180
  <meta charset='UTF-8' />
181
181
  <meta name='viewport' content='width=device-width, initial-scale=1.0' />
182
- {title ? <title>{title}</title> : ''}
182
+ {title ? <title>{title}</title> : <></>}
183
183
  </head>
184
184
  <body>{children}</body>
185
185
  </html>
@@ -245,7 +245,7 @@ The below is the project structure of a minimal application including a client s
245
245
 
246
246
  ### Renderer
247
247
 
248
- This is a `_renderer.tsx` which will load the `/app/client.ts` entry file for the client. It can also load the JavaScript file for the production according to the variable `import.meta.env.PROD`.
248
+ This is a `_renderer.tsx`, which will load the `/app/client.ts` entry file for the client. It will load the JavaScript file for the production according to the variable `import.meta.env.PROD`. And renders the inside of `HasIslands` if there are islands on that page.
249
249
 
250
250
  ```tsx
251
251
  // app/routes/_renderer.tsx
@@ -258,7 +258,9 @@ export default jsxRenderer(({ children }) => {
258
258
  <meta charset='UTF-8' />
259
259
  <meta name='viewport' content='width=device-width, initial-scale=1.0' />
260
260
  {import.meta.env.PROD ? (
261
- <script type='module' src='/static/client.js'></script>
261
+ <HasIslands>
262
+ <script type='module' src='/static/client.js'></script>
263
+ </HasIslands>
262
264
  ) : (
263
265
  <script type='module' src='/app/client.ts'></script>
264
266
  )}
@@ -269,6 +271,27 @@ export default jsxRenderer(({ children }) => {
269
271
  })
270
272
  ```
271
273
 
274
+ If you have a manifest file in `dist/.vite/manifest.json`, you can easily write it using `<Script />`.
275
+
276
+ ```tsx
277
+ // app/routes/_renderer.tsx
278
+ import { jsxRenderer } from 'hono/jsx-renderer'
279
+ import { Script } from 'honox/server'
280
+
281
+ export default jsxRenderer(({ children }) => {
282
+ return (
283
+ <html lang='en'>
284
+ <head>
285
+ <meta charset='UTF-8' />
286
+ <meta name='viewport' content='width=device-width, initial-scale=1.0' />
287
+ <Script src='/app/client.ts' />
288
+ </head>
289
+ <body>{children}</body>
290
+ </html>
291
+ )
292
+ })
293
+ ```
294
+
272
295
  ### Client Entry File
273
296
 
274
297
  A client side entry file should be in `app/client.ts`. Simply, write `createClient()`.
@@ -320,7 +343,7 @@ export default createRoute((c) => {
320
343
 
321
344
  You can bring your own renderer using a UI library like React, Preact, Solid, or others.
322
345
 
323
- **Note**: We cannot provide technical support for the renderer you bring.
346
+ **Note**: We may not provide supports for the renderer you bring.
324
347
 
325
348
  ### React case
326
349
 
@@ -389,6 +412,25 @@ createClient({
389
412
 
390
413
  ## Guides
391
414
 
415
+ ### Nested Layouts
416
+
417
+ If you are using the JSX Renderer middleware, you can nest layouts using ` <Layout />`.
418
+
419
+ ```tsx
420
+ // app/routes/posts/_renderer.tsx
421
+
422
+ import { jsxRenderer } from 'hono/jsx-renderer'
423
+
424
+ export default jsxRenderer(({ children, Layout }) => {
425
+ return (
426
+ <Layout>
427
+ <nav>Posts Menu</nav>
428
+ <div>{children}</div>
429
+ </Layout>
430
+ )
431
+ })
432
+ ```
433
+
392
434
  ### Using Middleware
393
435
 
394
436
  You can use Hono's Middleware in each root file with the same syntax as Hono. For example, to validate a value with the [Zod Validator](https://github.com/honojs/middleware/tree/main/packages/zod-validator), do the following:
@@ -533,6 +575,7 @@ Since a HonoX instance is essentially a Hono instance, it can be deployed on any
533
575
  Setup the `vite.config.ts`:
534
576
 
535
577
  ```ts
578
+ // vite.config.ts
536
579
  import { defineConfig } from 'vite'
537
580
  import honox from 'honox/vite'
538
581
  import pages from '@hono/vite-cloudflare-pages'
@@ -546,25 +589,15 @@ If you want to include client side scripts and assets:
546
589
 
547
590
  ```ts
548
591
  // vite.config.ts
549
- import { defineConfig } from 'vite'
550
- import honox from 'honox/vite'
551
592
  import pages from '@hono/vite-cloudflare-pages'
593
+ import honox from 'honox/vite'
594
+ import client from 'honox/vite/client'
595
+ import { defineConfig } from 'vite'
552
596
 
553
597
  export default defineConfig(({ mode }) => {
554
598
  if (mode === 'client') {
555
599
  return {
556
- build: {
557
- rollupOptions: {
558
- input: ['./app/client.ts'],
559
- output: {
560
- entryFileNames: 'static/client.js',
561
- chunkFileNames: 'static/assets/[name]-[hash].js',
562
- assetFileNames: 'static/assets/[name].[ext]',
563
- },
564
- },
565
- emptyOutDir: false,
566
- copyPublicDir: false,
567
- },
600
+ plugins: [client()],
568
601
  }
569
602
  } else {
570
603
  return {
@@ -577,7 +610,7 @@ export default defineConfig(({ mode }) => {
577
610
  Build command (including a client):
578
611
 
579
612
  ```txt
580
- vite build && vite build --mode client
613
+ vite build --mode client && vite build
581
614
  ```
582
615
 
583
616
  Deploy with the following commands after build. Ensure you have [Wrangler](https://developers.cloudflare.com/workers/wrangler/) installed:
@@ -1,4 +1,5 @@
1
1
  declare const COMPONENT_NAME = "component-name";
2
2
  declare const DATA_SERIALIZED_PROPS = "data-serialized-props";
3
+ declare const IMPORTING_ISLANDS_ID: "__importing_islands";
3
4
 
4
- export { COMPONENT_NAME, DATA_SERIALIZED_PROPS };
5
+ export { COMPONENT_NAME, DATA_SERIALIZED_PROPS, IMPORTING_ISLANDS_ID };
package/dist/constants.js CHANGED
@@ -1,6 +1,8 @@
1
1
  const COMPONENT_NAME = "component-name";
2
2
  const DATA_SERIALIZED_PROPS = "data-serialized-props";
3
+ const IMPORTING_ISLANDS_ID = "__importing_islands";
3
4
  export {
4
5
  COMPONENT_NAME,
5
- DATA_SERIALIZED_PROPS
6
+ DATA_SERIALIZED_PROPS,
7
+ IMPORTING_ISLANDS_ID
6
8
  };
@@ -0,0 +1,5 @@
1
+ import { FC } from 'hono/jsx';
2
+
3
+ declare const HasIslands: FC;
4
+
5
+ export { HasIslands };
@@ -0,0 +1,10 @@
1
+ import { Fragment, jsx } from "hono/jsx/jsx-runtime";
2
+ import { useRequestContext } from "hono/jsx-renderer";
3
+ import { IMPORTING_ISLANDS_ID } from "../../constants.js";
4
+ const HasIslands = ({ children }) => {
5
+ const c = useRequestContext();
6
+ return /* @__PURE__ */ jsx(Fragment, { children: c.get(IMPORTING_ISLANDS_ID) ? children : /* @__PURE__ */ jsx(Fragment, {}) });
7
+ };
8
+ export {
9
+ HasIslands
10
+ };
@@ -0,0 +1,4 @@
1
+ export { HasIslands } from './has-islands.js';
2
+ export { Script } from './script.js';
3
+ import 'hono/jsx';
4
+ import 'vite';
@@ -0,0 +1,6 @@
1
+ import { HasIslands } from "./has-islands.js";
2
+ import { Script } from "./script.js";
3
+ export {
4
+ HasIslands,
5
+ Script
6
+ };
@@ -0,0 +1,11 @@
1
+ import { FC } from 'hono/jsx';
2
+ import { Manifest } from 'vite';
3
+
4
+ type Options = {
5
+ src: string;
6
+ prod?: boolean;
7
+ manifest?: Manifest;
8
+ };
9
+ declare const Script: FC<Options>;
10
+
11
+ export { Script };
@@ -0,0 +1,31 @@
1
+ import { Fragment, jsx } from "hono/jsx/jsx-runtime";
2
+ import { HasIslands } from "./has-islands.js";
3
+ const Script = async (options) => {
4
+ const src = options.src;
5
+ if (options.prod ?? import.meta.env.PROD) {
6
+ let manifest = options.manifest;
7
+ if (!manifest) {
8
+ const MANIFEST = import.meta.glob("/dist/.vite/manifest.json", {
9
+ eager: true
10
+ });
11
+ for (const [, manifestFile] of Object.entries(MANIFEST)) {
12
+ if (manifestFile["default"]) {
13
+ manifest = manifestFile["default"];
14
+ break;
15
+ }
16
+ }
17
+ }
18
+ if (manifest) {
19
+ const scriptInManifest = manifest[src.replace(/^\//, "")];
20
+ if (scriptInManifest) {
21
+ return /* @__PURE__ */ jsx(HasIslands, { children: /* @__PURE__ */ jsx("script", { type: "module", src: `/${scriptInManifest.file}` }) });
22
+ }
23
+ }
24
+ return /* @__PURE__ */ jsx(Fragment, {});
25
+ } else {
26
+ return /* @__PURE__ */ jsx("script", { type: "module", src });
27
+ }
28
+ };
29
+ export {
30
+ Script
31
+ };
@@ -0,0 +1,19 @@
1
+ import * as hono_types from 'hono/types';
2
+ import * as hono from 'hono';
3
+ import { Env, Hono } from 'hono';
4
+
5
+ declare const createRoute: {
6
+ <I extends hono.Input = {}>(handler1: hono_types.H<Env, any, I>): [hono_types.H<Env, any, I>];
7
+ <I_1 extends hono.Input = {}, I2 extends hono.Input = I_1, R extends hono_types.HandlerResponse<any> = any>(handler1: hono_types.H<Env, any, I_1, R>, handler2: hono_types.H<Env, any, I2, R>): [hono_types.H<Env, any, I_1, R>, hono_types.H<Env, any, I2, R>];
8
+ <I_2 extends hono.Input = {}, I2_1 extends hono.Input = I_2, I3 extends hono.Input = I_2 & I2_1, R_1 extends hono_types.HandlerResponse<any> = any>(handler1: hono_types.H<Env, any, I_2, R_1>, handler2: hono_types.H<Env, any, I2_1, R_1>, handler3: hono_types.H<Env, any, I3, R_1>): [hono_types.H<Env, any, I_2, R_1>, hono_types.H<Env, any, I2_1, R_1>, hono_types.H<Env, any, I3, R_1>];
9
+ <I_3 extends hono.Input = {}, I2_2 extends hono.Input = I_3, I3_1 extends hono.Input = I_3 & I2_2, I4 extends hono.Input = I_3 & I2_2 & I3_1, R_2 extends hono_types.HandlerResponse<any> = any>(handler1: hono_types.H<Env, any, I_3, R_2>, handler2: hono_types.H<Env, any, I2_2, R_2>, handler3: hono_types.H<Env, any, I3_1, R_2>, handler4: hono_types.H<Env, any, I4, R_2>): [hono_types.H<Env, any, I_3, R_2>, hono_types.H<Env, any, I2_2, R_2>, hono_types.H<Env, any, I3_1, R_2>, hono_types.H<Env, any, I4, R_2>];
10
+ <I_4 extends hono.Input = {}, I2_3 extends hono.Input = I_4, I3_2 extends hono.Input = I_4 & I2_3, I4_1 extends hono.Input = I_4 & I2_3 & I3_2, I5 extends hono.Input = I_4 & I2_3 & I3_2 & I4_1, R_3 extends hono_types.HandlerResponse<any> = any>(handler1: hono_types.H<Env, any, I_4, R_3>, handler2: hono_types.H<Env, any, I2_3, R_3>, handler3: hono_types.H<Env, any, I3_2, R_3>, handler4: hono_types.H<Env, any, I4_1, R_3>, handler5: hono_types.H<Env, any, I5, R_3>): [hono_types.H<Env, any, I_4, R_3>, hono_types.H<Env, any, I2_3, R_3>, hono_types.H<Env, any, I3_2, R_3>, hono_types.H<Env, any, I4_1, R_3>, hono_types.H<Env, any, I5, R_3>];
11
+ <I_5 extends hono.Input = {}, I2_4 extends hono.Input = I_5, I3_3 extends hono.Input = I_5 & I2_4, I4_2 extends hono.Input = I_5 & I2_4 & I3_3, I5_1 extends hono.Input = I_5 & I2_4 & I3_3 & I4_2, I6 extends hono.Input = I_5 & I2_4 & I3_3 & I4_2 & I5_1, R_4 extends hono_types.HandlerResponse<any> = any>(handler1: hono_types.H<Env, any, I_5, R_4>, handler2: hono_types.H<Env, any, I2_4, R_4>, handler3: hono_types.H<Env, any, I3_3, R_4>, handler4: hono_types.H<Env, any, I4_2, R_4>, handler5: hono_types.H<Env, any, I5_1, R_4>, handler6: hono_types.H<Env, any, I6, R_4>): [hono_types.H<Env, any, I_5, R_4>, hono_types.H<Env, any, I2_4, R_4>, hono_types.H<Env, any, I3_3, R_4>, hono_types.H<Env, any, I4_2, R_4>, hono_types.H<Env, any, I5_1, R_4>, hono_types.H<Env, any, I6, R_4>];
12
+ <I_6 extends hono.Input = {}, I2_5 extends hono.Input = I_6, I3_4 extends hono.Input = I_6 & I2_5, I4_3 extends hono.Input = I_6 & I2_5 & I3_4, I5_2 extends hono.Input = I_6 & I2_5 & I3_4 & I4_3, I6_1 extends hono.Input = I_6 & I2_5 & I3_4 & I4_3 & I5_2, I7 extends hono.Input = I_6 & I2_5 & I3_4 & I4_3 & I5_2 & I6_1, R_5 extends hono_types.HandlerResponse<any> = any>(handler1: hono_types.H<Env, any, I_6, R_5>, handler2: hono_types.H<Env, any, I2_5, R_5>, handler3: hono_types.H<Env, any, I3_4, R_5>, handler4: hono_types.H<Env, any, I4_3, R_5>, handler5: hono_types.H<Env, any, I5_2, R_5>, handler6: hono_types.H<Env, any, I6_1, R_5>, handler7: hono_types.H<Env, any, I7, R_5>): [hono_types.H<Env, any, I_6, R_5>, hono_types.H<Env, any, I2_5, R_5>, hono_types.H<Env, any, I3_4, R_5>, hono_types.H<Env, any, I4_3, R_5>, hono_types.H<Env, any, I5_2, R_5>, hono_types.H<Env, any, I6_1, R_5>, hono_types.H<Env, any, I7, R_5>];
13
+ <I_7 extends hono.Input = {}, I2_6 extends hono.Input = I_7, I3_5 extends hono.Input = I_7 & I2_6, I4_4 extends hono.Input = I_7 & I2_6 & I3_5, I5_3 extends hono.Input = I_7 & I2_6 & I3_5 & I4_4, I6_2 extends hono.Input = I_7 & I2_6 & I3_5 & I4_4 & I5_3, I7_1 extends hono.Input = I_7 & I2_6 & I3_5 & I4_4 & I5_3 & I6_2, I8 extends hono.Input = I_7 & I2_6 & I3_5 & I4_4 & I5_3 & I6_2 & I7_1, R_6 extends hono_types.HandlerResponse<any> = any>(handler1: hono_types.H<Env, any, I_7, R_6>, handler2: hono_types.H<Env, any, I2_6, R_6>, handler3: hono_types.H<Env, any, I3_5, R_6>, handler4: hono_types.H<Env, any, I4_4, R_6>, handler5: hono_types.H<Env, any, I5_3, R_6>, handler6: hono_types.H<Env, any, I6_2, R_6>, handler7: hono_types.H<Env, any, I7_1, R_6>, handler8: hono_types.H<Env, any, I8, R_6>): [hono_types.H<Env, any, I_7, R_6>, hono_types.H<Env, any, I2_6, R_6>, hono_types.H<Env, any, I3_5, R_6>, hono_types.H<Env, any, I4_4, R_6>, hono_types.H<Env, any, I5_3, R_6>, hono_types.H<Env, any, I6_2, R_6>, hono_types.H<Env, any, I7_1, R_6>, hono_types.H<Env, any, I8, R_6>];
14
+ <I_8 extends hono.Input = {}, I2_7 extends hono.Input = I_8, I3_6 extends hono.Input = I_8 & I2_7, I4_5 extends hono.Input = I_8 & I2_7 & I3_6, I5_4 extends hono.Input = I_8 & I2_7 & I3_6 & I4_5, I6_3 extends hono.Input = I_8 & I2_7 & I3_6 & I4_5 & I5_4, I7_2 extends hono.Input = I_8 & I2_7 & I3_6 & I4_5 & I5_4 & I6_3, I8_1 extends hono.Input = I_8 & I2_7 & I3_6 & I4_5 & I5_4 & I6_3 & I7_2, I9 extends hono.Input = I_8 & I2_7 & I3_6 & I4_5 & I5_4 & I6_3 & I7_2 & I8_1, R_7 extends hono_types.HandlerResponse<any> = any>(handler1: hono_types.H<Env, any, I_8, R_7>, handler2: hono_types.H<Env, any, I2_7, R_7>, handler3: hono_types.H<Env, any, I3_6, R_7>, handler4: hono_types.H<Env, any, I4_5, R_7>, handler5: hono_types.H<Env, any, I5_4, R_7>, handler6: hono_types.H<Env, any, I6_3, R_7>, handler7: hono_types.H<Env, any, I7_2, R_7>, handler8: hono_types.H<Env, any, I8_1, R_7>, handler9: hono_types.H<Env, any, I9, R_7>): [hono_types.H<Env, any, I_8, R_7>, hono_types.H<Env, any, I2_7, R_7>, hono_types.H<Env, any, I3_6, R_7>, hono_types.H<Env, any, I4_5, R_7>, hono_types.H<Env, any, I5_4, R_7>, hono_types.H<Env, any, I6_3, R_7>, hono_types.H<Env, any, I7_2, R_7>, hono_types.H<Env, any, I8_1, R_7>, hono_types.H<Env, any, I9, R_7>];
15
+ <I_9 extends hono.Input = {}, I2_8 extends hono.Input = I_9, I3_7 extends hono.Input = I_9 & I2_8, I4_6 extends hono.Input = I_9 & I2_8 & I3_7, I5_5 extends hono.Input = I_9 & I2_8 & I3_7 & I4_6, I6_4 extends hono.Input = I_9 & I2_8 & I3_7 & I4_6 & I5_5, I7_3 extends hono.Input = I_9 & I2_8 & I3_7 & I4_6 & I5_5 & I6_4, I8_2 extends hono.Input = I_9 & I2_8 & I3_7 & I4_6 & I5_5 & I6_4 & I7_3, I9_1 extends hono.Input = I_9 & I2_8 & I3_7 & I4_6 & I5_5 & I6_4 & I7_3 & I8_2, I10 extends hono.Input = I_9 & I2_8 & I3_7 & I4_6 & I5_5 & I6_4 & I7_3 & I8_2 & I9_1, R_8 extends hono_types.HandlerResponse<any> = any>(handler1: hono_types.H<Env, any, I_9, R_8>, handler2: hono_types.H<Env, any, I2_8, R_8>, handler3: hono_types.H<Env, any, I3_7, R_8>, handler4: hono_types.H<Env, any, I4_6, R_8>, handler5: hono_types.H<Env, any, I5_5, R_8>, handler6: hono_types.H<Env, any, I6_4, R_8>, handler7: hono_types.H<Env, any, I7_3, R_8>, handler8: hono_types.H<Env, any, I8_2, R_8>, handler9: hono_types.H<Env, any, I9_1, R_8>, handler10: hono_types.H<Env, any, I10, R_8>): [hono_types.H<Env, any, I_9, R_8>, hono_types.H<Env, any, I2_8, R_8>, hono_types.H<Env, any, I3_7, R_8>, hono_types.H<Env, any, I4_6, R_8>, hono_types.H<Env, any, I5_5, R_8>, hono_types.H<Env, any, I6_4, R_8>, hono_types.H<Env, any, I7_3, R_8>, hono_types.H<Env, any, I8_2, R_8>, hono_types.H<Env, any, I9_1, R_8>, hono_types.H<Env, any, I10, R_8>];
16
+ };
17
+ declare const createHono: () => Hono<Env, hono_types.BlankSchema, "/">;
18
+
19
+ export { createHono, createRoute };
@@ -0,0 +1,11 @@
1
+ import { Hono } from "hono";
2
+ import { createFactory } from "hono/factory";
3
+ const factory = createFactory();
4
+ const createRoute = factory.createHandlers;
5
+ const createHono = () => {
6
+ return new Hono();
7
+ };
8
+ export {
9
+ createHono,
10
+ createRoute
11
+ };
@@ -0,0 +1,3 @@
1
+ export { createHono, createRoute } from './factory.js';
2
+ import 'hono/types';
3
+ import 'hono';
@@ -0,0 +1,5 @@
1
+ import { createRoute, createHono } from "./factory.js";
2
+ export {
3
+ createHono,
4
+ createRoute
5
+ };
@@ -1,3 +1,8 @@
1
1
  export { ServerOptions, createApp } from './server.js';
2
+ export { HasIslands } from './components/has-islands.js';
3
+ export { Script } from './components/script.js';
2
4
  import 'hono/types';
3
5
  import 'hono';
6
+ import '../constants.js';
7
+ import 'hono/jsx';
8
+ import 'vite';
@@ -1,4 +1,5 @@
1
1
  import { createApp } from "./server.js";
2
+ export * from "./components/index.js";
2
3
  export {
3
4
  createApp
4
5
  };
@@ -1,13 +1,17 @@
1
1
  import * as hono_types from 'hono/types';
2
2
  import { H } from 'hono/types';
3
3
  import { Env, Hono, MiddlewareHandler, NotFoundHandler, ErrorHandler } from 'hono';
4
+ import { IMPORTING_ISLANDS_ID } from '../constants.js';
4
5
 
5
6
  declare const METHODS: readonly ["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"];
7
+ type InnerMeta = {
8
+ [key in typeof IMPORTING_ISLANDS_ID]?: boolean;
9
+ };
6
10
  type RouteFile = {
7
11
  default?: Function;
8
12
  } & {
9
13
  [M in (typeof METHODS)[number]]?: H[];
10
- };
14
+ } & InnerMeta;
11
15
  type RendererFile = {
12
16
  default: MiddlewareHandler;
13
17
  };
@@ -1,9 +1,10 @@
1
1
  import { Hono } from "hono";
2
+ import { createMiddleware } from "hono/factory";
3
+ import { IMPORTING_ISLANDS_ID } from "../constants.js";
2
4
  import {
3
5
  filePathToPath,
4
6
  groupByDirectory,
5
7
  listByDirectory,
6
- pathToDirectoryPath,
7
8
  sortDirectoriesByDepth
8
9
  } from "../utils/file.js";
9
10
  const NOTFOUND_FILENAME = "_404.tsx";
@@ -28,10 +29,12 @@ const createApp = (options) => {
28
29
  eager: true
29
30
  });
30
31
  const rendererList = listByDirectory(RENDERER_FILE);
31
- const applyRenderer = (rendererFile) => {
32
+ const applyRenderer = (app2, rendererFile) => {
32
33
  const renderer = RENDERER_FILE[rendererFile];
33
- const path = pathToDirectoryPath(rendererFile).replace(rootRegExp, "");
34
- app.all(`${filePathToPath(path)}*`, renderer.default);
34
+ const rendererDefault = renderer["default"];
35
+ if (rendererDefault) {
36
+ app2.all("*", rendererDefault);
37
+ }
35
38
  };
36
39
  const ROUTES_FILE = options?.ROUTES ?? import.meta.glob("/app/routes/**/[!_]*.(ts|tsx|mdx)", {
37
40
  eager: true
@@ -40,30 +43,31 @@ const createApp = (options) => {
40
43
  for (const map of routesMap) {
41
44
  for (const [dir, content] of Object.entries(map)) {
42
45
  const subApp = new Hono();
43
- let rendererFiles = rendererList[dir];
44
- if (rendererFiles) {
45
- applyRenderer(rendererFiles[0]);
46
- }
47
- if (!rendererFiles) {
48
- const dirPaths = dir.split("/");
49
- const getRendererPaths = (paths) => {
50
- rendererFiles = rendererList[paths.join("/")];
51
- if (!rendererFiles) {
52
- paths.pop();
53
- if (paths.length) {
54
- getRendererPaths(paths);
55
- }
46
+ let rendererPaths = rendererList[dir] ?? [];
47
+ const getRendererPaths = (paths) => {
48
+ rendererPaths = rendererList[paths.join("/")];
49
+ if (!rendererPaths) {
50
+ paths.pop();
51
+ if (paths.length) {
52
+ getRendererPaths(paths);
56
53
  }
57
- return rendererFiles;
58
- };
59
- rendererFiles = getRendererPaths(dirPaths);
60
- if (rendererFiles) {
61
- applyRenderer(rendererFiles[0]);
62
54
  }
63
- }
55
+ return rendererPaths ?? [];
56
+ };
57
+ const dirPaths = dir.split("/");
58
+ rendererPaths = getRendererPaths(dirPaths);
59
+ rendererPaths.sort((a, b) => a.split("/").length - b.split("/").length);
60
+ rendererPaths.map((path) => {
61
+ applyRenderer(subApp, path);
62
+ });
64
63
  let rootPath = dir.replace(rootRegExp, "");
65
64
  rootPath = filePathToPath(rootPath);
66
65
  for (const [filename, route] of Object.entries(content)) {
66
+ const importingIslands = route[IMPORTING_ISLANDS_ID];
67
+ const setInnerMeta = createMiddleware(async function innerMeta(c, next) {
68
+ c.set(IMPORTING_ISLANDS_ID, importingIslands);
69
+ await next();
70
+ });
67
71
  const routeDefault = route.default;
68
72
  const path = filePathToPath(filename);
69
73
  if (routeDefault && "fetch" in routeDefault) {
@@ -72,13 +76,16 @@ const createApp = (options) => {
72
76
  for (const m of METHODS) {
73
77
  const handlers = route[m];
74
78
  if (handlers) {
79
+ subApp.on(m, path, setInnerMeta);
75
80
  subApp.on(m, path, ...handlers);
76
81
  }
77
82
  }
78
83
  if (routeDefault && Array.isArray(routeDefault)) {
84
+ subApp.get(path, setInnerMeta);
79
85
  subApp.get(path, ...routeDefault);
80
86
  }
81
87
  if (typeof routeDefault === "function") {
88
+ subApp.get(path, setInnerMeta);
82
89
  subApp.get(path, (c) => {
83
90
  return c.render(routeDefault(), route);
84
91
  });
@@ -0,0 +1,10 @@
1
+ import { Plugin } from 'vite';
2
+
3
+ type Options = {
4
+ jsxImportSource?: string;
5
+ assetsDir?: string;
6
+ };
7
+ declare const defaultOptions: Options;
8
+ declare function client(options?: Options): Plugin;
9
+
10
+ export { client as default, defaultOptions };
@@ -0,0 +1,28 @@
1
+ const defaultOptions = {
2
+ jsxImportSource: "hono/jsx/dom",
3
+ assetsDir: "static"
4
+ };
5
+ function client(options) {
6
+ return {
7
+ name: "honox-vite-client",
8
+ config: () => {
9
+ return {
10
+ build: {
11
+ rollupOptions: {
12
+ input: ["/app/client.ts"]
13
+ },
14
+ assetsDir: options?.assetsDir ?? defaultOptions.assetsDir,
15
+ manifest: true
16
+ },
17
+ esbuild: {
18
+ jsxImportSource: options?.jsxImportSource ?? defaultOptions.jsxImportSource
19
+ }
20
+ };
21
+ }
22
+ };
23
+ }
24
+ var client_default = client;
25
+ export {
26
+ client_default as default,
27
+ defaultOptions
28
+ };
@@ -3,13 +3,13 @@ export { defaultOptions as devServerDefaultOptions } from '@hono/vite-dev-server
3
3
  import { PluginOption } from 'vite';
4
4
  export { islandComponents } from './island-components.js';
5
5
 
6
- type HonoXOptions = {
6
+ type Options = {
7
7
  islands?: boolean;
8
8
  entry?: string;
9
9
  devServer?: DevServerOptions;
10
10
  external?: string[];
11
11
  };
12
- declare const defaultOptions: HonoXOptions;
13
- declare function honox(options?: HonoXOptions): PluginOption[];
12
+ declare const defaultOptions: Options;
13
+ declare function honox(options?: Options): PluginOption[];
14
14
 
15
15
  export { honox as default, defaultOptions };
@@ -1,5 +1,6 @@
1
1
  import path from "path";
2
2
  import devServer, { defaultOptions as devServerDefaultOptions } from "@hono/vite-dev-server";
3
+ import { injectImportingIslands } from "./inject-importing-islands.js";
3
4
  import { islandComponents } from "./island-components.js";
4
5
  const defaultOptions = {
5
6
  islands: true,
@@ -23,9 +24,17 @@ function honox(options) {
23
24
  if (options?.islands !== false) {
24
25
  plugins.push(islandComponents());
25
26
  }
27
+ plugins.push(injectImportingIslands());
26
28
  return [
27
29
  {
28
- name: "honox-vite-config"
30
+ name: "honox-vite-config",
31
+ config: () => {
32
+ return {
33
+ ssr: {
34
+ noExternal: true
35
+ }
36
+ };
37
+ }
29
38
  },
30
39
  ...plugins
31
40
  ];
@@ -0,0 +1,5 @@
1
+ import { Plugin } from 'vite';
2
+
3
+ declare function injectImportingIslands(): Plugin;
4
+
5
+ export { injectImportingIslands };
@@ -0,0 +1,52 @@
1
+ import _generate from "@babel/generator";
2
+ import { parse } from "@babel/parser";
3
+ import _traverse from "@babel/traverse";
4
+ import { IMPORTING_ISLANDS_ID } from "../constants.js";
5
+ const traverse = _traverse.default ?? _traverse;
6
+ const generate = _generate.default ?? _generate;
7
+ function injectImportingIslands() {
8
+ return {
9
+ name: "inject-importing-islands",
10
+ transform(code, id) {
11
+ if (id.endsWith(".tsx") || id.endsWith(".jsx")) {
12
+ let hasIslandsImport = false;
13
+ const ast = parse(code, {
14
+ sourceType: "module",
15
+ plugins: ["jsx"]
16
+ });
17
+ traverse(ast, {
18
+ ImportDeclaration(path) {
19
+ if (path.node.source.value.includes("islands/")) {
20
+ hasIslandsImport = true;
21
+ }
22
+ }
23
+ });
24
+ if (hasIslandsImport) {
25
+ const hasIslandsNode = {
26
+ type: "ExportNamedDeclaration",
27
+ declaration: {
28
+ type: "VariableDeclaration",
29
+ declarations: [
30
+ {
31
+ type: "VariableDeclarator",
32
+ id: { type: "Identifier", name: IMPORTING_ISLANDS_ID },
33
+ init: { type: "BooleanLiteral", value: true }
34
+ }
35
+ ],
36
+ kind: "const"
37
+ }
38
+ };
39
+ ast.program.body.push(hasIslandsNode);
40
+ }
41
+ const output = generate(ast, {}, code);
42
+ return {
43
+ code: output.code,
44
+ map: output.map
45
+ };
46
+ }
47
+ }
48
+ };
49
+ }
50
+ export {
51
+ injectImportingIslands
52
+ };
@@ -75,8 +75,9 @@ const transformJsxTags = (contents, componentName) => {
75
75
  ExportDefaultDeclaration(path) {
76
76
  if (path.node.declaration.type === "FunctionDeclaration") {
77
77
  const functionId = path.node.declaration.id;
78
- if (!functionId)
78
+ if (!functionId) {
79
79
  return;
80
+ }
80
81
  const isAsync = path.node.declaration.async;
81
82
  const originalFunctionId = identifier(functionId.name + "Original");
82
83
  const originalFunction = functionExpression(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "honox",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "main": "dist/index.js",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -8,15 +8,18 @@
8
8
  "test:unit": "vitest --run test/unit",
9
9
  "test:integration": "bun test:integration:api && bun test:integration:hono-jsx",
10
10
  "test:integration:hono-jsx": "vitest run -c ./test/hono-jsx/vitest.config.ts ./test/hono-jsx/integration.test.ts",
11
+ "test:integration:hono-jsx:watch": "vitest -c ./test/hono-jsx/vitest.config.ts ./test/hono-jsx/integration.test.ts",
11
12
  "test:integration:api": "vitest run -c ./test/api/vitest.config.ts ./test/api/integration.test.ts",
12
13
  "test:e2e": "playwright test -c ./test/hono-jsx/playwright.config.ts ./test/hono-jsx/e2e.test.ts",
13
14
  "typecheck": "tsc --noEmit",
14
15
  "build": "tsup && publint",
15
16
  "watch": "tsup --watch",
16
- "lint": "eslint src/**.ts",
17
- "lint:fix": "eslint src/**.ts --fix",
18
- "prerelease": "yarn test && yarn build",
19
- "release": "np --no-yarn"
17
+ "lint": "eslint --ext js,ts src test",
18
+ "lint:fix": "eslint --ext js,ts src test --fix",
19
+ "format": "prettier --check \"src/**/*.{js,ts}\" \"test/**/*.{js,ts}\"",
20
+ "format:fix": "prettier --write \"src/**/*.{js,ts}\" \"test/**/*.{js,ts}\"",
21
+ "prerelease": "bun run test && bun run build",
22
+ "release": "np"
20
23
  },
21
24
  "files": [
22
25
  "dist"
@@ -49,6 +52,10 @@
49
52
  "./vite": {
50
53
  "types": "./dist/vite/index.d.ts",
51
54
  "import": "./dist/vite/index.js"
55
+ },
56
+ "./vite/client": {
57
+ "types": "./dist/vite/client.d.ts",
58
+ "import": "./dist/vite/client.js"
52
59
  }
53
60
  },
54
61
  "typesVersions": {
@@ -70,6 +77,9 @@
70
77
  ],
71
78
  "vite": [
72
79
  "./dist/vite"
80
+ ],
81
+ "vite/client": [
82
+ "./dist/vite/client"
73
83
  ]
74
84
  }
75
85
  },
@@ -89,10 +99,10 @@
89
99
  "@babel/parser": "^7.23.6",
90
100
  "@babel/traverse": "^7.23.6",
91
101
  "@babel/types": "^7.23.6",
92
- "@hono/vite-dev-server": "^0.4.1"
102
+ "@hono/vite-dev-server": "^0.5.0"
93
103
  },
94
104
  "devDependencies": {
95
- "@hono/eslint-config": "^0.0.3",
105
+ "@hono/eslint-config": "^0.0.4",
96
106
  "@mdx-js/rollup": "^3.0.0",
97
107
  "@playwright/test": "^1.41.0",
98
108
  "@types/babel__generator": "^7",
@@ -100,8 +110,8 @@
100
110
  "@types/node": "^20.10.5",
101
111
  "eslint": "^8.56.0",
102
112
  "glob": "^10.3.10",
103
- "hono": "4.0.0-rc.2",
104
- "np": "^9.2.0",
113
+ "hono": "4.0.0-rc.4",
114
+ "np": "7.7.0",
105
115
  "prettier": "^3.1.1",
106
116
  "publint": "^0.2.7",
107
117
  "tsup": "^8.0.1",
@@ -109,8 +119,10 @@
109
119
  "vite": "^5.0.12",
110
120
  "vitest": "^1.2.1"
111
121
  },
112
- "packageManager": "yarn@4.0.2",
113
122
  "engines": {
114
123
  "node": ">=18.14.1"
124
+ },
125
+ "optionalDependencies": {
126
+ "@rollup/rollup-linux-x64-gnu": "^4.9.6"
115
127
  }
116
128
  }