Skip to content

Commit

Permalink
feat: introduce <Style /> (#28)
Browse files Browse the repository at this point in the history
* feat: introduce `<Style />`

* add tests
  • Loading branch information
yusukebe authored Feb 4, 2024
1 parent 0c8d0e7 commit ef9fd5b
Show file tree
Hide file tree
Showing 23 changed files with 178 additions and 78 deletions.
42 changes: 27 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,27 @@ export default jsxRenderer(({ children }) => {
})
```

If you have a manifest file in `dist/.vite/manifest.json`, you can easily write it using `<Script />`.

```tsx
// app/routes/_renderer.tsx
import { Style } from 'hono/css'
import { jsxRenderer } from 'hono/jsx-renderer'

export default jsxRenderer(({ children }) => {
return (
<html lang='en'>
<head>
<meta charset='UTF-8' />
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
<Script src='/app/client.ts' />
</head>
<body>{children}</body>
</html>
)
})
```

### Client Entry File

A client side entry file should be in `app/client.ts`. Simply, write `createClient()`.
Expand Down Expand Up @@ -554,6 +575,7 @@ Since a HonoX instance is essentially a Hono instance, it can be deployed on any
Setup the `vite.config.ts`:

```ts
// vite.config.ts
import { defineConfig } from 'vite'
import honox from 'honox/vite'
import pages from '@hono/vite-cloudflare-pages'
Expand All @@ -567,25 +589,15 @@ If you want to include client side scripts and assets:

```ts
// vite.config.ts
import { defineConfig } from 'vite'
import honox from 'honox/vite'
import pages from '@hono/vite-cloudflare-pages'
import honox from 'honox/vite'
import client from 'honox/vite/client'
import { defineConfig } from 'vite'

export default defineConfig(({ mode }) => {
if (mode === 'client') {
return {
build: {
rollupOptions: {
input: ['./app/client.ts'],
output: {
entryFileNames: 'static/client.js',
chunkFileNames: 'static/assets/[name]-[hash].js',
assetFileNames: 'static/assets/[name].[ext]',
},
},
emptyOutDir: false,
copyPublicDir: false,
},
plugins: [client()],
}
} else {
return {
Expand All @@ -598,7 +610,7 @@ export default defineConfig(({ mode }) => {
Build command (including a client):

```txt
vite build && vite build --mode client
vite build --mode client && vite build
```

Deploy with the following commands after build. Ensure you have [Wrangler](https://developers.cloudflare.com/workers/wrangler/) installed:
Expand Down
12 changes: 4 additions & 8 deletions examples/basic/app/routes/_renderer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Style } from 'hono/css'
import { jsxRenderer } from 'hono/jsx-renderer'
import { HasIslands } from 'honox/server'
import { Script } from 'honox/server'

export default jsxRenderer(({ children, title }) => {
return (
Expand All @@ -8,13 +9,8 @@ export default jsxRenderer(({ children, title }) => {
<meta charset='UTF-8' />
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
{title ? <title>{title}</title> : <></>}
{import.meta.env.PROD ? (
<HasIslands>
<script type='module' src='/static/client.js'></script>
</HasIslands>
) : (
<script type='module' src='/app/client.ts'></script>
)}
<Script src='/app/client.ts' />
<Style />
</head>
<body>{children}</body>
</html>
Expand Down
11 changes: 7 additions & 4 deletions examples/basic/app/routes/index.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { css } from 'hono/css'
import { createRoute } from 'honox/factory'
import Counter from '../islands/counter'

const className = css`
font-family: sans-serif;
`

export default createRoute((c) => {
const name = c.req.query('name') ?? 'Hono'
return c.render(
<div>
<div class={className}>
<h1>Hello, {name}!</h1>
<Counter />
</div>,
{
title: name,
}
{ title: name }
)
})
Binary file modified examples/basic/bun.lockb
Binary file not shown.
6 changes: 3 additions & 3 deletions examples/basic/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build && vite build --mode client",
"build": "vite build --mode client && vite build",
"preview": "wrangler pages dev ./dist",
"deploy": "$npm_execpath build && wrangler pages deploy ./dist"
},
"private": true,
"devDependencies": {
"@hono/vite-cloudflare-pages": "^0.2.2",
"@hono/vite-cloudflare-pages": "^0.2.3",
"vite": "^5.0.12",
"wrangler": "^3.23.0"
},
"dependencies": {
"hono": "4.0.0-rc.3"
"hono": "4.0.0-rc.4"
}
}
4 changes: 0 additions & 4 deletions examples/basic/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,7 @@
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler",
"esModuleInterop": true,
"strict": true,
"lib": [
"esnext"
],
"types": [
"vite/client"
],
Expand Down
18 changes: 4 additions & 14 deletions examples/basic/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,16 @@
import build from '@hono/vite-cloudflare-pages'
import pages from '@hono/vite-cloudflare-pages'
import honox from 'honox/vite'
import client from 'honox/vite/client'
import { defineConfig } from 'vite'

export default defineConfig(({ mode }) => {
if (mode === 'client') {
return {
build: {
rollupOptions: {
input: ['./app/client.ts'],
output: {
entryFileNames: 'static/client.js',
chunkFileNames: 'static/assets/[name]-[hash].js',
assetFileNames: 'static/assets/[name].[ext]',
},
},
emptyOutDir: false,
copyPublicDir: false,
},
plugins: [client()],
}
} else {
return {
plugins: [honox(), build()],
plugins: [honox(), pages()],
}
}
})
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@
"./vite": {
"types": "./dist/vite/index.d.ts",
"import": "./dist/vite/index.js"
},
"./vite/client": {
"types": "./dist/vite/client.d.ts",
"import": "./dist/vite/client.js"
}
},
"typesVersions": {
Expand All @@ -73,6 +77,9 @@
],
"vite": [
"./dist/vite"
],
"vite/client": [
"./dist/vite/client"
]
}
},
Expand Down Expand Up @@ -118,4 +125,4 @@
"optionalDependencies": {
"@rollup/rollup-linux-x64-gnu": "^4.9.6"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { FC } from 'hono/jsx'
import { useRequestContext } from 'hono/jsx-renderer'
import { IMPORTING_ISLANDS_ID } from '../constants.js'
import { IMPORTING_ISLANDS_ID } from '../../constants.js'

export const HasIslands: FC = ({ children }) => {
const c = useRequestContext()
Expand Down
2 changes: 2 additions & 0 deletions src/server/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { HasIslands } from './has-islands.js'
export { Script } from './script.js'
29 changes: 29 additions & 0 deletions src/server/components/script.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { FC } from 'hono/jsx'
import type { Manifest } from 'vite'

type Options = {
src: string
prod?: boolean
manifest?: Manifest
}

export const Script: FC<Options> = async (options) => {
const src = options.src
if (options.prod ?? import.meta.env.PROD) {
let manifest: Manifest | undefined = options.manifest
if (!manifest) {
// @ts-expect-error not typed
const manifestFile = await import('/dist/.vite/manifest.json')
manifest = manifestFile['default']
}
if (manifest) {
const scriptInManifest = manifest[src.replace(/^\//, '')]
if (scriptInManifest) {
return <script type='module' src={`/${scriptInManifest.file}`}></script>
}
}
return <></>
} else {
return <script type='module' src={src}></script>
}
}
2 changes: 1 addition & 1 deletion src/server/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { createApp } from './server.js'
export type { ServerOptions } from './server.js'
export { HasIslands } from './components.js'
export * from './components/index.js'
1 change: 0 additions & 1 deletion src/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
filePathToPath,
groupByDirectory,
listByDirectory,
pathToDirectoryPath,
sortDirectoriesByDepth,
} from '../utils/file.js'

Expand Down
33 changes: 33 additions & 0 deletions src/vite/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { Plugin } from 'vite'

type Options = {
jsxImportSource?: string
assetsDir?: string
}

export const defaultOptions: Options = {
jsxImportSource: 'hono/jsx/dom',
assetsDir: 'static',
}

function client(options?: Options): Plugin {
return {
name: 'honox-vite-client',
config: () => {
return {
build: {
rollupOptions: {
input: ['/app/client.ts'],
},
assetsDir: options?.assetsDir ?? defaultOptions.assetsDir,
manifest: true,
},
esbuild: {
jsxImportSource: options?.jsxImportSource ?? defaultOptions.jsxImportSource,
},
}
},
}
}

export default client
6 changes: 3 additions & 3 deletions src/vite/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ import type { PluginOption } from 'vite'
import { injectImportingIslands } from './inject-importing-islands.js'
import { islandComponents } from './island-components.js'

type HonoXOptions = {
type Options = {
islands?: boolean
entry?: string
devServer?: DevServerOptions
external?: string[]
}

export const defaultOptions: HonoXOptions = {
export const defaultOptions: Options = {
islands: true,
entry: path.join(process.cwd(), './app/server.ts'),
}

function honox(options?: HonoXOptions): PluginOption[] {
function honox(options?: Options): PluginOption[] {
const plugins: PluginOption[] = []

const entry = options?.entry ?? defaultOptions.entry
Expand Down
19 changes: 19 additions & 0 deletions test/hono-jsx/app-script/routes/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Script } from '../../../../src/server'

export default function Hello() {
return (
<html>
<head>
<Script
src='/app/client.ts'
prod={true}
manifest={{
'app/client.ts': {
file: 'static/client-abc.js',
},
}}
/>
</head>
</html>
)
}
4 changes: 0 additions & 4 deletions test/hono-jsx/app/factory.ts

This file was deleted.

6 changes: 3 additions & 3 deletions test/hono-jsx/app/routes/about/[name].tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { createRoute } from '../../../../../src/factory'
import Badge from '../../components/Badge'
import { createHandlers } from '../../factory'

export const POST = createHandlers((c) => {
export const POST = createRoute((c) => {
return c.text('Created!', 201)
})

export default createHandlers((c) => {
export default createRoute((c) => {
const { name } = c.req.param()
return c.render(
<>
Expand Down
4 changes: 2 additions & 2 deletions test/hono-jsx/app/routes/about/[name]/address.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createHandlers } from '../../../factory'
import { createRoute } from '../../../../../../src/factory'

export default createHandlers((c) => {
export default createRoute((c) => {
const { name } = c.req.param()
return c.render(<b>{name}'s address</b>, {
title: `${name}'s address`,
Expand Down
6 changes: 3 additions & 3 deletions test/hono-jsx/app/routes/api.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createHandlers } from '../factory'
import { createRoute } from '../../../../src/factory'

export const POST = createHandlers((c) => {
export const POST = createRoute((c) => {
return c.json(
{
message: 'created',
Expand All @@ -10,7 +10,7 @@ export const POST = createHandlers((c) => {
)
})

export default createHandlers((c) => {
export default createRoute((c) => {
c.header('X-Custom', 'Hello')
return c.json({
foo: 'bar',
Expand Down
Loading

0 comments on commit ef9fd5b

Please sign in to comment.