Skip to content

Commit

Permalink
feat: add trailingSlash option
Browse files Browse the repository at this point in the history
  • Loading branch information
yusukebe committed Mar 17, 2024
1 parent 65ed1a7 commit beb3b2a
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 1 deletion.
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export default defineConfig({

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.

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`.
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`.

```ts
// app/server.ts
Expand Down Expand Up @@ -545,6 +545,24 @@ export default createRoute(zValidator('form', schema), ...<more-middleware>)

Note that is some scenarios, auto-complete for the request body within the route may be lost depending on how the middleware was written.

### Trailing Slash

By default, trailing slashes are removed if the root file is an index file such as `index.tsx` or `index.mdx`.
However, if you set the `trailingSlash` option to `true` as the following, the trailing slash is not removed.

```ts
import { createApp } from 'honox/server'
const app = createApp({
trailingSlash: true,
})
```

Like the followings:

- `trailingSlash` is `false` (default): `app/routes/path/index.mdx` => `/path`
- `trailingSlash` is `true`: `app/routes/path/index.mdx` => `/path/`

### Using Tailwind CSS

Given that HonoX is Vite-centric, if you wish to utilize [Tailwind CSS](https://tailwindcss.com/), simply adhere to the official instructions.
Expand Down
11 changes: 11 additions & 0 deletions src/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ type BaseServerOptions<E extends Env = Env> = {
root: string
app?: Hono<E>
init?: InitFunction<E>
/**
* Appends a trailing slash to URL if the route file is an index file, e.g., `index.tsx` or `index.mdx`.
* @default false
*/
trailingSlash?: boolean
}

export type ServerOptions<E extends Env = Env> = Partial<BaseServerOptions<E>>
Expand All @@ -53,6 +58,7 @@ export const createApp = <E extends Env>(options: BaseServerOptions<E>): Hono<E>
const root = options.root
const rootRegExp = new RegExp(`^${root}`)
const app = options.app ?? new Hono()
const trailingSlash = options.trailingSlash ?? false

if (options.init) {
options.init(app)
Expand Down Expand Up @@ -178,6 +184,11 @@ export const createApp = <E extends Env>(options: BaseServerOptions<E>): Hono<E>
applyNotFound(subApp, dir, notFoundMap)
// Error
applyError(subApp, dir, errorMap)

if (trailingSlash) {
rootPath = /\/$/.test(rootPath) ? rootPath : rootPath + '/'
}

app.route(rootPath, subApp)
}
}
Expand Down
1 change: 1 addition & 0 deletions src/server/with-defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const createApp = <E extends Env>(options?: ServerOptions<E>) => {
root: options?.root ?? '/app/routes',
app: options?.app,
init: options?.init,
trailingSlash: options?.trailingSlash,
NOT_FOUND:
options?.NOT_FOUND ??
import.meta.glob('/app/routes/**/_404.(ts|tsx)', {
Expand Down
3 changes: 3 additions & 0 deletions test/hono-jsx/app-nested/routes/top.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Top() {
return <h1>Top</h1>
}
33 changes: 33 additions & 0 deletions test/hono-jsx/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -579,3 +579,36 @@ describe('Island Components with Preserved Files', () => {
)
})
})

describe('Trailing Slash', () => {
const ROUTES = import.meta.glob('./app-nested/routes/**/[a-z[-][a-z-_[]*.(tsx|ts)', {
eager: true,
})

const app = createApp({
root: './app-nested/routes',
ROUTES: ROUTES as any,
trailingSlash: true,
})

const paths = ['/nested', '/nested/foo', '/nested/foo/bar', '/nested/foo/bar/baz']
for (const path of paths) {
it(`Should return 404 response - ${path}`, async () => {
const res = await app.request(path)
expect(res.status).toBe(404)
})
it(`Should return 200 response - ${path}/`, async () => {
const res = await app.request(`${path}/`)
expect(res.status).toBe(200)
})
}

it('Should return 200 response - /top', async () => {
const res = await app.request('/top')
expect(res.status).toBe(200)
})
it('Should return 404 response - /top/', async () => {
const res = await app.request('/top/')
expect(res.status).toBe(404)
})
})

0 comments on commit beb3b2a

Please sign in to comment.