Add live reload feature for development mode
This commit is contained in:
parent
143862059a
commit
edbdbe5146
25
README.md
25
README.md
|
|
@ -18,6 +18,7 @@
|
|||
- Page-based routing to `.tsx` files that export a `JSX` function in `./src/pages`
|
||||
- Transpile `.ts` files in `src/client/blah.ts` via `website.com/client/blah.ts`
|
||||
- Default, simple HTML5 layout with working frontend transpilation/bundling, or supply your own.
|
||||
- Live reload in development mode — pages automatically refresh when the server restarts.
|
||||
- Optional CSS reset.
|
||||
- Optional pico.css.
|
||||
|
||||
|
|
@ -203,6 +204,30 @@ Test with curl:
|
|||
curl -N http://localhost:3000/api/time
|
||||
```
|
||||
|
||||
### Live Reload
|
||||
|
||||
In development mode (`NODE_ENV !== 'production'`), `hype` automatically injects a live reload script into pages using the default layout. When the server restarts (e.g. via `bun --hot`), the browser will automatically reload the page.
|
||||
|
||||
If you're using a custom layout, you can add live reload by importing and rendering the `ReloadScript` component:
|
||||
|
||||
```tsx
|
||||
import { ReloadScript } from "hype"
|
||||
|
||||
export default ({ children, title }: any) => (
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{title ?? "hype"}</title>
|
||||
</head>
|
||||
<body>
|
||||
<main>{children}</main>
|
||||
<ReloadScript />
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
```
|
||||
|
||||
`ReloadScript` only renders in development mode — it's a no-op in production.
|
||||
|
||||
### utils
|
||||
|
||||
`hype` includes helpful utils for your webapp:
|
||||
|
|
|
|||
15
examples/live-reload/package.json
Normal file
15
examples/live-reload/package.json
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"name": "hype-live-reload-example",
|
||||
"module": "src/index.tsx",
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@because/hype": "*"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "bun run src/server/index.ts",
|
||||
"dev": "bun run --hot src/server/index.ts"
|
||||
}
|
||||
}
|
||||
1
examples/live-reload/src/client/main.ts
Normal file
1
examples/live-reload/src/client/main.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
console.log('Live reload is active in dev mode — no setup required.')
|
||||
29
examples/live-reload/src/css/main.css
Normal file
29
examples/live-reload/src/css/main.css
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding-top: 4rem;
|
||||
}
|
||||
|
||||
section {
|
||||
max-width: 500px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
code {
|
||||
background: #f0f0f0;
|
||||
padding: 0.2rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
code {
|
||||
background: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.hint {
|
||||
color: #888;
|
||||
font-style: italic;
|
||||
}
|
||||
8
examples/live-reload/src/pages/index.tsx
Normal file
8
examples/live-reload/src/pages/index.tsx
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
export default () => (
|
||||
<section>
|
||||
<h1>Live Reload Demo</h1>
|
||||
<p>Run this with <code>bun run dev</code>, then edit this file.</p>
|
||||
<p>The browser will automatically refresh — no manual reload needed.</p>
|
||||
<p class="hint">Try changing this text and saving!</p>
|
||||
</section>
|
||||
)
|
||||
5
examples/live-reload/src/server/index.ts
Normal file
5
examples/live-reload/src/server/index.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import { Hype } from '@because/hype'
|
||||
|
||||
const app = new Hype({})
|
||||
|
||||
export default app.defaults
|
||||
|
|
@ -13,6 +13,7 @@ const CSS_RESET = await Bun.file(join(import.meta.dir, '/css/reset.css')).text()
|
|||
const PICO_CSS = await Bun.file(join(import.meta.dir, '/css/pico.css')).text()
|
||||
|
||||
export * from './utils'
|
||||
export { ReloadScript } from './layout'
|
||||
export type { Context } from 'hono'
|
||||
|
||||
const pageCache = new Map()
|
||||
|
|
@ -142,6 +143,11 @@ export class Hype<
|
|||
if (this.routesRegistered) return
|
||||
this.routesRegistered = true
|
||||
|
||||
// live reload endpoint (dev mode only)
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
this.sse('/__hype/reload', () => {})
|
||||
}
|
||||
|
||||
// healthcheck
|
||||
if (this.props.ok)
|
||||
this.get('/ok', c => c.text('ok'))
|
||||
|
|
@ -246,3 +252,4 @@ function findAvailablePort(startPort: number, maxAttempts = 100): number {
|
|||
function render404(c: Context) {
|
||||
return c.text('File not found', 404)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { type FC } from 'hono/jsx'
|
||||
|
||||
const isDev = process.env.NODE_ENV !== 'production'
|
||||
|
||||
const Layout: FC = ({ children, title, props }) =>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
|
@ -17,7 +19,13 @@ const Layout: FC = ({ children, title, props }) =>
|
|||
<main>
|
||||
{children}
|
||||
</main>
|
||||
{isDev && <ReloadScript />}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
const RELOAD_SCRIPT = '{let c=false;const e=new EventSource("/__hype/reload");e.onopen=()=>{if(c)location.reload();c=true};e.onerror=()=>{e.close();let d=50;setTimeout(function r(){fetch("/__hype/reload").then(()=>location.reload()).catch(()=>{d=Math.min(d*1.5,2000);setTimeout(r,d)})},d)}}'
|
||||
|
||||
export const ReloadScript: FC = () =>
|
||||
isDev ? <script dangerouslySetInnerHTML={{ __html: RELOAD_SCRIPT }} /> : null
|
||||
|
||||
export default Layout
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user