honox 0.1.8 → 0.1.9
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
|
@@ -70,7 +70,7 @@ export default defineConfig({
|
|
|
70
70
|
|
|
71
71
|
A server entry file is required. The file is should be placed at `app/server.ts`. This file is first called by the Vite during the development or build phase.
|
|
72
72
|
|
|
73
|
-
In the entry file, simply initialize your app using the `createApp()` function. `app` will be an instance of Hono, so you can use Hono's middleware and the `showRoutes()`in`hono/dev`.
|
|
73
|
+
In the entry file, simply initialize your app using the `createApp()` function. `app` will be an instance of Hono, so you can use Hono's middleware and the `showRoutes()` in `hono/dev`.
|
|
74
74
|
|
|
75
75
|
```ts
|
|
76
76
|
// app/server.ts
|
|
@@ -545,6 +545,24 @@ export default createRoute(zValidator('form', schema), ...<more-middleware>)
|
|
|
545
545
|
|
|
546
546
|
Note that is some scenarios, auto-complete for the request body within the route may be lost depending on how the middleware was written.
|
|
547
547
|
|
|
548
|
+
### Trailing Slash
|
|
549
|
+
|
|
550
|
+
By default, trailing slashes are removed if the root file is an index file such as `index.tsx` or `index.mdx`.
|
|
551
|
+
However, if you set the `trailingSlash` option to `true` as the following, the trailing slash is not removed.
|
|
552
|
+
|
|
553
|
+
```ts
|
|
554
|
+
import { createApp } from 'honox/server'
|
|
555
|
+
|
|
556
|
+
const app = createApp({
|
|
557
|
+
trailingSlash: true,
|
|
558
|
+
})
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
Like the followings:
|
|
562
|
+
|
|
563
|
+
- `trailingSlash` is `false` (default): `app/routes/path/index.mdx` => `/path`
|
|
564
|
+
- `trailingSlash` is `true`: `app/routes/path/index.mdx` => `/path/`
|
|
565
|
+
|
|
548
566
|
### Using Tailwind CSS
|
|
549
567
|
|
|
550
568
|
Given that HonoX is Vite-centric, if you wish to utilize [Tailwind CSS](https://tailwindcss.com/), simply adhere to the official instructions.
|
package/dist/server/server.d.ts
CHANGED
|
@@ -4,12 +4,13 @@ import { Env, Hono, MiddlewareHandler, NotFoundHandler, ErrorHandler } from 'hon
|
|
|
4
4
|
import { IMPORTING_ISLANDS_ID } from '../constants.js';
|
|
5
5
|
|
|
6
6
|
declare const METHODS: readonly ["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"];
|
|
7
|
-
type
|
|
8
|
-
default: Hono;
|
|
9
|
-
};
|
|
10
|
-
type InnerMeta = {
|
|
7
|
+
type HasIslandFile = {
|
|
11
8
|
[key in typeof IMPORTING_ISLANDS_ID]?: boolean;
|
|
12
9
|
};
|
|
10
|
+
type InnerMeta = {} & HasIslandFile;
|
|
11
|
+
type AppFile = {
|
|
12
|
+
default: Hono;
|
|
13
|
+
} & InnerMeta;
|
|
13
14
|
type RouteFile = {
|
|
14
15
|
default?: Function;
|
|
15
16
|
} & {
|
|
@@ -17,13 +18,13 @@ type RouteFile = {
|
|
|
17
18
|
} & InnerMeta;
|
|
18
19
|
type RendererFile = {
|
|
19
20
|
default: MiddlewareHandler;
|
|
20
|
-
};
|
|
21
|
+
} & InnerMeta;
|
|
21
22
|
type NotFoundFile = {
|
|
22
23
|
default: NotFoundHandler;
|
|
23
|
-
};
|
|
24
|
+
} & InnerMeta;
|
|
24
25
|
type ErrorFile = {
|
|
25
26
|
default: ErrorHandler;
|
|
26
|
-
};
|
|
27
|
+
} & InnerMeta;
|
|
27
28
|
type MiddlewareFile = {
|
|
28
29
|
default: MiddlewareHandler[];
|
|
29
30
|
};
|
|
@@ -37,6 +38,11 @@ type BaseServerOptions<E extends Env = Env> = {
|
|
|
37
38
|
root: string;
|
|
38
39
|
app?: Hono<E>;
|
|
39
40
|
init?: InitFunction<E>;
|
|
41
|
+
/**
|
|
42
|
+
* Appends a trailing slash to URL if the route file is an index file, e.g., `index.tsx` or `index.mdx`.
|
|
43
|
+
* @default false
|
|
44
|
+
*/
|
|
45
|
+
trailingSlash?: boolean;
|
|
40
46
|
};
|
|
41
47
|
type ServerOptions<E extends Env = Env> = Partial<BaseServerOptions<E>>;
|
|
42
48
|
declare const createApp: <E extends Env>(options: BaseServerOptions<E>) => Hono<E, hono_types.BlankSchema, "/">;
|
package/dist/server/server.js
CHANGED
|
@@ -14,6 +14,7 @@ const createApp = (options) => {
|
|
|
14
14
|
const root = options.root;
|
|
15
15
|
const rootRegExp = new RegExp(`^${root}`);
|
|
16
16
|
const app = options.app ?? new Hono();
|
|
17
|
+
const trailingSlash = options.trailingSlash ?? false;
|
|
17
18
|
if (options.init) {
|
|
18
19
|
options.init(app);
|
|
19
20
|
}
|
|
@@ -47,9 +48,14 @@ const createApp = (options) => {
|
|
|
47
48
|
for (const map of routesMap) {
|
|
48
49
|
for (const [dir, content] of Object.entries(map)) {
|
|
49
50
|
const subApp = new Hono();
|
|
51
|
+
let hasIslandComponent = false;
|
|
50
52
|
const rendererPaths = getPaths(dir, rendererList);
|
|
51
53
|
rendererPaths.map((path) => {
|
|
52
54
|
const renderer = RENDERER_FILE[path];
|
|
55
|
+
const importingIslands = renderer[IMPORTING_ISLANDS_ID];
|
|
56
|
+
if (importingIslands) {
|
|
57
|
+
hasIslandComponent = true;
|
|
58
|
+
}
|
|
53
59
|
const rendererDefault = renderer.default;
|
|
54
60
|
if (rendererDefault) {
|
|
55
61
|
subApp.all("*", rendererDefault);
|
|
@@ -68,7 +74,7 @@ const createApp = (options) => {
|
|
|
68
74
|
for (const [filename, route] of Object.entries(content)) {
|
|
69
75
|
const importingIslands = route[IMPORTING_ISLANDS_ID];
|
|
70
76
|
const setInnerMeta = createMiddleware(async function innerMeta(c, next) {
|
|
71
|
-
c.set(IMPORTING_ISLANDS_ID, importingIslands);
|
|
77
|
+
c.set(IMPORTING_ISLANDS_ID, importingIslands ? true : hasIslandComponent);
|
|
72
78
|
await next();
|
|
73
79
|
});
|
|
74
80
|
const routeDefault = route.default;
|
|
@@ -97,6 +103,9 @@ const createApp = (options) => {
|
|
|
97
103
|
}
|
|
98
104
|
applyNotFound(subApp, dir, notFoundMap);
|
|
99
105
|
applyError(subApp, dir, errorMap);
|
|
106
|
+
if (trailingSlash) {
|
|
107
|
+
rootPath = /\/$/.test(rootPath) ? rootPath : rootPath + "/";
|
|
108
|
+
}
|
|
100
109
|
app.route(rootPath, subApp);
|
|
101
110
|
}
|
|
102
111
|
}
|
|
@@ -108,6 +117,13 @@ function applyNotFound(app, dir, map) {
|
|
|
108
117
|
const notFound = content[NOTFOUND_FILENAME];
|
|
109
118
|
if (notFound) {
|
|
110
119
|
const notFoundHandler = notFound.default;
|
|
120
|
+
const importingIslands = notFound[IMPORTING_ISLANDS_ID];
|
|
121
|
+
if (importingIslands) {
|
|
122
|
+
app.use("*", (c, next) => {
|
|
123
|
+
c.set(IMPORTING_ISLANDS_ID, true);
|
|
124
|
+
return next();
|
|
125
|
+
});
|
|
126
|
+
}
|
|
111
127
|
app.get("*", (c) => {
|
|
112
128
|
c.status(404);
|
|
113
129
|
return notFoundHandler(c);
|
|
@@ -119,12 +135,16 @@ function applyNotFound(app, dir, map) {
|
|
|
119
135
|
function applyError(app, dir, map) {
|
|
120
136
|
for (const [mapDir, content] of Object.entries(map)) {
|
|
121
137
|
if (dir === mapDir) {
|
|
122
|
-
const
|
|
123
|
-
if (
|
|
124
|
-
const errorHandler =
|
|
125
|
-
app.onError((
|
|
138
|
+
const errorFile = content[ERROR_FILENAME];
|
|
139
|
+
if (errorFile) {
|
|
140
|
+
const errorHandler = errorFile.default;
|
|
141
|
+
app.onError(async (error, c) => {
|
|
142
|
+
const importingIslands = errorFile[IMPORTING_ISLANDS_ID];
|
|
143
|
+
if (importingIslands) {
|
|
144
|
+
c.set(IMPORTING_ISLANDS_ID, importingIslands);
|
|
145
|
+
}
|
|
126
146
|
c.status(500);
|
|
127
|
-
return errorHandler(
|
|
147
|
+
return errorHandler(error, c);
|
|
128
148
|
});
|
|
129
149
|
}
|
|
130
150
|
}
|
|
@@ -14,17 +14,25 @@ declare const createApp: <E extends Env>(options?: Partial<{
|
|
|
14
14
|
PATCH?: hono_types.H[] | undefined;
|
|
15
15
|
} & {
|
|
16
16
|
__importing_islands?: boolean | undefined;
|
|
17
|
-
}) | {
|
|
17
|
+
}) | ({
|
|
18
18
|
default: hono.Hono<Env, hono_types.BlankSchema, "/">;
|
|
19
|
-
}
|
|
19
|
+
} & {
|
|
20
|
+
__importing_islands?: boolean | undefined;
|
|
21
|
+
})>;
|
|
20
22
|
RENDERER: Record<string, {
|
|
21
23
|
default: hono.MiddlewareHandler;
|
|
24
|
+
} & {
|
|
25
|
+
__importing_islands?: boolean | undefined;
|
|
22
26
|
}>;
|
|
23
27
|
NOT_FOUND: Record<string, {
|
|
24
28
|
default: hono.NotFoundHandler;
|
|
29
|
+
} & {
|
|
30
|
+
__importing_islands?: boolean | undefined;
|
|
25
31
|
}>;
|
|
26
32
|
ERROR: Record<string, {
|
|
27
33
|
default: hono.ErrorHandler;
|
|
34
|
+
} & {
|
|
35
|
+
__importing_islands?: boolean | undefined;
|
|
28
36
|
}>;
|
|
29
37
|
MIDDLEWARE: Record<string, {
|
|
30
38
|
default: hono.MiddlewareHandler[];
|
|
@@ -32,6 +40,7 @@ declare const createApp: <E extends Env>(options?: Partial<{
|
|
|
32
40
|
root: string;
|
|
33
41
|
app?: hono.Hono<E, hono_types.BlankSchema, "/"> | undefined;
|
|
34
42
|
init?: ((app: hono.Hono<E, hono_types.BlankSchema, "/">) => void) | undefined;
|
|
43
|
+
trailingSlash?: boolean | undefined;
|
|
35
44
|
}> | undefined) => hono.Hono<E, hono_types.BlankSchema, "/">;
|
|
36
45
|
|
|
37
46
|
export { createApp };
|
|
@@ -8,7 +8,7 @@ import { IMPORTING_ISLANDS_ID } from "../constants.js";
|
|
|
8
8
|
const generate = _generate.default ?? _generate;
|
|
9
9
|
async function injectImportingIslands() {
|
|
10
10
|
const isIslandRegex = new RegExp(/\/islands\//);
|
|
11
|
-
const
|
|
11
|
+
const fileRegex = new RegExp(/(routes|_renderer|_error|_404)\/.*\.[tj]sx$/);
|
|
12
12
|
const cache = {};
|
|
13
13
|
const walkDependencyTree = async (baseFile, dependencyFile) => {
|
|
14
14
|
const depPath = dependencyFile ? path.join(path.dirname(baseFile), dependencyFile) + ".tsx" : baseFile;
|
|
@@ -32,7 +32,7 @@ async function injectImportingIslands() {
|
|
|
32
32
|
return {
|
|
33
33
|
name: "inject-importing-islands",
|
|
34
34
|
async transform(sourceCode, id) {
|
|
35
|
-
if (!
|
|
35
|
+
if (!fileRegex.test(id)) {
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
38
38
|
const hasIslandsImport = (await walkDependencyTree(id)).flat().some((x) => isIslandRegex.test(normalizePath(x)));
|