Skip to content

Commit

Permalink
refactor: use h3 instead of express (#6)
Browse files Browse the repository at this point in the history
* dfsdfdsfsfs

* fsdfsdfsd

* fsdfsdfsd

* todo: fix redirect

* export useH3Event
remove "custom" redirect implementation

* docs: update lincense year

* refactor: replace cheerio with node-html-parser (#5)

* update all packages

* remove unused counter, rearrange imports

* fdsfsfsdfsd

* also update peerDependencies

* ci(publish): update actions version, use node 20, add packageManager to package.json

* add ci workflow

run unit-tests

* setup pnpm before node

* fix(ci): setup pnpm before node in publish workflow

* update README

* add h3 playground

* fix(playgrounds): add missing build targets

this fixes the build error because of top level await

* refactor: use @unhead/ssr to render SSR head

* remove app.use(router) usage

* update deps

* update node to 22, update every deps

* remove console.log from client build

* remove build.target array from h3 vite config
  • Loading branch information
yooouuri authored Dec 12, 2024
1 parent 0a20253 commit 0700a73
Show file tree
Hide file tree
Showing 31 changed files with 1,511 additions and 1,336 deletions.
27 changes: 27 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: ci

on:
push:
branches:
- '**'
pull_request:
branches:
- main

jobs:
unit-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4

- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'

- run: pnpm install

- name: Run unit tests
run: pnpm run unit-test
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
20
22
68 changes: 60 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Vite plugin to develop Vue SSR apps
* State management
* Teleports
* [Unhead](https://unhead.unjs.io) support
* Based on [H3](https://h3.unjs.io)

## Quick Setup

Expand Down Expand Up @@ -80,6 +81,8 @@ const routes = [
export default vueSSR(App, { routes })
```

### State management

Pinia/Vuex is supported by using the `app` and `state` property inside the callback.

```typescript
Expand All @@ -96,11 +99,21 @@ export default vueSSR(App, { routes }, ({ app, state }) => {
})
```

> The state will be persisted on `window.__INITIAL_STATE__` property and serialized using `@nuxt/devalue`
> The state will be persisted on `window.__INITIAL_STATE__` property and serialized using `devalue`
### Router

It's possible to make changes to the router, use the `router` property in the callback.

```typescript
const routes = [
{
path: '/',
name: 'counter',
component: Counter,
},
]

export default vueSSR(App, { routes }, ({ router }) => {
router.beforeEach(async (to, from) => {
if (
Expand All @@ -113,28 +126,67 @@ export default vueSSR(App, { routes }, ({ router }) => {
})
```

The Express request and response objects are accessible from the callback. Make sure to wrap them in `import.meta.env.SSR`.
To customize the router, just return the router instance.

The `routes` parameter is omitted, because we create a fresh router instance in the method.

```typescript
export default vueSSR(App, {}, async ({ app }) => {
const router = createRouter({
history: import.meta.env.SSR ? createMemoryHistory('/') : createWebHistory('/'),
routes: [
{
path: '/',
name: 'counter',
component: Counter,
},
],
})

return {
router,
}
})
```

### H3

H3 is the underlaying server. During development it injects as an middleware.

The `event` param is used to access the H3 composables.

> NOTE: only works in SSR
```typescript
export default vueSSR(App, { routes }, ({ request, response }) => {
import { getRequestURL } from 'h3'

export default vueSSR(App, { routes }, ({ event }) => {
if (import.meta.env.SSR) {
console.log(request?.originalUrl)
console.log(getRequestURL(event)) // "https://example.com/path"
}

console.log(event) // undefined
})
```

Or use `useSSRContext`.
In a Vue component, use the `useH3Event()` composable

```typescript
const { request, response } = useSSRContext()
import { useH3Event } from 'vite-plugin-vue-ssr'
import { getRequestURL } from 'h3'

if (import.meta.env.SSR) {
console.log(request?.originalUrl)
const event = useH3Event()

console.log(getRequestURL(event)) // "https://example.com/path"
}
```

Using Teleport is supported, but requires a little bit of setup. Targeting `body` is not supported, use `#teleports` instead.
See [https://h3.unjs.io/utils](https://h3.unjs.io/utils) for more composables.

### Teleports

Using `Teleport` is supported, but requires a little bit of setup. Targeting `body` is not supported (in SSR), use `#teleports` instead.

```html
<template>
Expand Down
8 changes: 4 additions & 4 deletions build.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import { defineBuildConfig } from 'unbuild'
export default defineBuildConfig({
entries: [
{
input: './src/index.ts',
outDir: './dist',
input: 'src/index.ts',
outDir: 'dist',
},
{
input: 'src/plugin/index.ts',
outDir: 'dist/plugin',
}
},
],
externals: ['express', /@unhead/],
externals: [/@unhead/],
clean: true,
declaration: true,
rollup: {
Expand Down
45 changes: 22 additions & 23 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "vite-plugin-vue-ssr",
"version": "0.12.2",
"packageManager": "pnpm@9.6.0",
"packageManager": "pnpm@9.15.0",
"type": "module",
"license": "MIT",
"description": "Vite plugin to develop Vue SSR apps",
Expand All @@ -28,41 +28,40 @@
"scripts": {
"dev": "unbuild --stub",
"build": "unbuild",
"test": "vitest"
"unit-test": "vitest"
},
"keywords": [
"vue",
"ssr",
"vite"
],
"dependencies": {
"@babel/generator": "^7.25.0",
"@babel/parser": "^7.25.0",
"@babel/traverse": "^7.25.1",
"@babel/types": "^7.25.0",
"@types/express": "^4.17.21",
"@unhead/ssr": "^1.9.16",
"cookie": "^0.6.0",
"cookie-parser": "^1.4.6",
"@babel/generator": "^7.26.3",
"@babel/parser": "^7.26.3",
"@babel/traverse": "^7.26.4",
"@babel/types": "^7.26.3",
"@unhead/ssr": "^1.11.14",
"node-html-parser": "^6.1.13"
},
"devDependencies": {
"@types/node": "^20.14.13",
"@unhead/vue": "^1.9.16",
"devalue": "^5.0.0",
"typescript": "^5.5.0",
"@types/node": "^22.10.2",
"@unhead/vue": "^1.11.14",
"devalue": "^5.1.1",
"h3": "^1.13.0",
"typescript": "^5.7.2",
"unbuild": "^2.0.0",
"vite": "^5.3.5",
"vitest": "^1.6.0",
"vue": "^3.4.34",
"vue-router": "^4.4.0"
"vite": "^6.0.3",
"vitest": "^2.1.8",
"vue": "^3.5.13",
"vue-router": "^4.5.0"
},
"peerDependencies": {
"@unhead/vue": "^1.9.16",
"devalue": "^5.0.0",
"vite": "^5.3.5",
"vue": "^3.4.34",
"vue-router": "^4.4.0"
"@unhead/vue": "^1.11.14",
"devalue": "^5.1.1",
"h3": "^1.13.0",
"vite": "^6.0.3",
"vue": "^3.5.13",
"vue-router": "^4.5.0"
},
"peerDependenciesMeta": {
"devalue": {
Expand Down
1 change: 1 addition & 0 deletions playground/h3/env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="vite/client" />
13 changes: 13 additions & 0 deletions playground/h3/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
28 changes: 28 additions & 0 deletions playground/h3/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "h3-example",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "pnpm run build:client && pnpm run build:server",
"build:client": "vite build --ssrManifest --outDir dist/client",
"build:server": "vite build --ssr src/main.ts --outDir dist/server"
},
"dependencies": {
"@unhead/vue": "^1.11.14",
"h3": "^1.13.0",
"serve-static": "^1.16.2",
"vue": "^3.5.13",
"vue-router": "^4.5.0"
},
"devDependencies": {
"@tsconfig/node22": "^22.0.0",
"@types/node": "^22.10.2",
"@vitejs/plugin-vue": "^5.2.1",
"@vue/tsconfig": "^0.7.0",
"typescript": "^5.7.2",
"vite": "^6.0.3",
"vite-plugin-vue-ssr": "workspace:*"
}
}
Binary file added playground/h3/public/favicon.ico
Binary file not shown.
30 changes: 30 additions & 0 deletions playground/h3/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { readFileSync } from 'node:fs'
import { resolve, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
import { createServer } from 'node:http'
import { createApp, defineEventHandler, fromNodeMiddleware, toNodeListener } from 'h3'
import { generateTemplate } from 'vite-plugin-vue-ssr/plugin'
import serveStatic from 'serve-static'

const __dirname = dirname(fileURLToPath(import.meta.url))

const main = (await import(resolve(__dirname, './dist/server/main.js'))).default

const template = readFileSync(resolve('dist/client/index.html'), 'utf-8')

const manifest = JSON.parse(
readFileSync(resolve('dist/client/.vite/ssr-manifest.json'), 'utf-8')
)

const app = createApp()

app.use(fromNodeMiddleware(serveStatic(resolve('dist/client'), { index: false })))
app.use(defineEventHandler(async (event) => {
const url = event.node.req.originalUrl ?? '/'

const html = await generateTemplate(main, url, template, event, manifest)

return html
}))

createServer(toNodeListener(app)).listen(3000, () => console.log('server listening on 3000'))
30 changes: 30 additions & 0 deletions playground/h3/src/App.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<script setup lang="ts">
import { useHead } from '@unhead/vue'
useHead({
title: 'Example app, using vite-plugin-vue-ssr',
})
</script>

<template>
<div>H3 examples, see Cookie.vue and Redirect.vue</div>

<div>
<RouterView />
</div>

<div>
Navigating to the components won't do anything...
<ul>
<li><RouterLink to="/">Home</RouterLink></li>
<li><RouterLink to="cookie">Cookie</RouterLink></li>
<li><RouterLink to="redirect">Redirect</RouterLink></li>
</ul>
</div>
</template>

<style>
body {
background: lightblue;
}
</style>
14 changes: 14 additions & 0 deletions playground/h3/src/Cookie.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script setup lang="ts">
import { useH3Event } from 'vite-plugin-vue-ssr'
import { setCookie } from 'h3'
if (import.meta.env.SSR) {
const event = useH3Event()
setCookie(event, 'cookie-from', 'h3')
}
</script>

<template>
Me love cookies (check your cookies, they may be gone)
</template>
3 changes: 3 additions & 0 deletions playground/h3/src/Home.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
Hi from Home
</template>
25 changes: 25 additions & 0 deletions playground/h3/src/Redirect.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<script setup lang="ts">
import { onServerPrefetch } from 'vue'
import { useH3Event } from 'vite-plugin-vue-ssr'
import { getRequestURL, sendRedirect } from 'h3'
// if (import.meta.env.SSR) {
// const event = useH3Event()
//
// const { host } = getRequestURL(event)
//
// sendRedirect(event, `http://${host}/cookie`)
// }
onServerPrefetch(() => {
const event = useH3Event()
const { host } = getRequestURL(event)
sendRedirect(event, `http://${host}/cookie`)
})
</script>

<template>
Bye, try refreshing me (it even works in the onServerPrefetch hook)
</template>
Loading

0 comments on commit 0700a73

Please sign in to comment.