Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support SSR in Vite dev mode #2014

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9,737 changes: 3,887 additions & 5,850 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/core/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const builds = [
{ entryPoints: ['src/index.ts'], format: 'cjs', outfile: 'dist/index.js', platform: 'browser' },
{ entryPoints: ['src/server.ts'], format: 'esm', outfile: 'dist/server.esm.js', platform: 'node' },
{ entryPoints: ['src/server.ts'], format: 'cjs', outfile: 'dist/server.js', platform: 'node' },
{ entryPoints: ['src/vite.ts'], format: 'esm', outfile: 'dist/vite.js', platform: 'node' },
]

builds.forEach((build) => {
Expand Down
13 changes: 13 additions & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
"types": "./types/server.d.ts",
"import": "./dist/server.esm.js",
"require": "./dist/server.js"
},
"./vite": {
"types": "./types/vite.d.ts",
"default": "./dist/vite.js"
}
},
"typesVersions": {
Expand All @@ -56,6 +60,14 @@
"nprogress": "^0.2.0",
"qs": "^6.9.0"
},
"peerDependencies": {
"vite": "^5.4.0 | ^6.0.0-beta.1"
},
"peerDependenciesMeta": {
"vite": {
"optional": true
}
},
"devDependencies": {
"@types/deepmerge": "^2.2.0",
"@types/node": "^14.0",
Expand All @@ -65,6 +77,7 @@
"esbuild-node-externals": "^1.6.0",
"happy-dom": "^14.12.3",
"typescript": "^4.9.4",
"vite": "^5.4.8",
"vitest": "^1.6.0"
}
}
14 changes: 4 additions & 10 deletions packages/core/src/server.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
import { createServer, IncomingMessage } from 'http'
import * as process from 'process'
import { InertiaAppResponse, Page } from './types'
import { readableToString } from './serverUtils'
import type { InertiaAppResponse, Page } from './types'

type AppCallback = (page: Page) => InertiaAppResponse
type RouteHandler = (request: IncomingMessage) => Promise<unknown>
export type AppCallback = (page: Page) => InertiaAppResponse

const readableToString: (readable: IncomingMessage) => Promise<string> = (readable) =>
new Promise((resolve, reject) => {
let data = ''
readable.on('data', (chunk) => (data += chunk))
readable.on('end', () => resolve(data))
readable.on('error', (err) => reject(err))
})
type RouteHandler = (request: IncomingMessage) => Promise<unknown>

export default (render: AppCallback, port?: number): void => {
const _port = port || 13714
Expand Down
9 changes: 9 additions & 0 deletions packages/core/src/serverUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { IncomingMessage } from 'http'

export const readableToString: (readable: IncomingMessage) => Promise<string> = (readable) =>
new Promise((resolve, reject) => {
let data = ''
readable.on('data', (chunk) => (data += chunk))
readable.on('end', () => resolve(data))
readable.on('error', (err) => reject(err))
})
43 changes: 43 additions & 0 deletions packages/core/src/vite.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { Plugin } from 'vite'
import { readableToString } from './serverUtils'

interface PluginConfig {
renderer: string
}

export default function inertia(config: string | PluginConfig): Plugin {
const resolvedConfig = resolveConfig(config)

return {
name: '@inertiajs/core/vite',
async configureServer(server) {
return () =>
server.middlewares.use(async (req, res, next) => {
if (req.url !== '/render') {
next()
}

const { default: render } = await server.ssrLoadModule(resolvedConfig.renderer)
const response = await render(JSON.parse(await readableToString(req)))
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify(response))
})
},
}
}

function resolveConfig(config: string | PluginConfig) {
if (typeof config === 'undefined') {
throw new Error('@inertiajs/core/vite: missing configuration.')
}

if (typeof config === 'string') {
return { renderer: config }
}

if (typeof config.renderer === 'undefined') {
throw new Error('@inertiajs/core/vite: missing configuration for "renderer".')
}

return config
}
1 change: 1 addition & 0 deletions packages/react/src/server.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from '@inertiajs/core/server'
export { default as default } from '@inertiajs/core/server'
1 change: 1 addition & 0 deletions packages/svelte/src/server.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from '@inertiajs/core/server'
export { default as default } from '@inertiajs/core/server'
1 change: 1 addition & 0 deletions packages/vue3/src/server.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from '@inertiajs/core/server'
export { default as default } from '@inertiajs/core/server'
14 changes: 14 additions & 0 deletions playgrounds/react/resources/js/viteSsr.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { createInertiaApp } from '@inertiajs/react'
import type { AppCallback } from '@inertiajs/react/server'
import * as ReactDOMServer from 'react-dom/server'

const render: AppCallback = (page) =>
createInertiaApp({
page,
render: ReactDOMServer.renderToString,
title: (title) => `${title} - React Playground`,
resolve: (name) => import(`./Pages/${name}.tsx`),
setup: ({ App, props }) => <App {...props} />,
})

export default render
2 changes: 2 additions & 0 deletions playgrounds/react/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import inertia from '@inertiajs/core/vite'
import react from '@vitejs/plugin-react'
import laravel from 'laravel-vite-plugin'
import { defineConfig } from 'vite'
Expand All @@ -9,6 +10,7 @@ export default defineConfig({
ssr: 'resources/js/ssr.tsx',
refresh: true,
}),
inertia('resources/js/viteSsr.tsx'),
react({}),
],
})
10 changes: 10 additions & 0 deletions playgrounds/svelte4/resources/js/viteSsr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { createInertiaApp } from '@inertiajs/svelte'
import type { AppCallback } from '@inertiajs/svelte/server'

const render: AppCallback = (page) =>
createInertiaApp({
page,
resolve: (name) => import(`./Pages/${name}.svelte`),
})

export default render
2 changes: 2 additions & 0 deletions playgrounds/svelte4/vite.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import inertia from '@inertiajs/core/vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
import laravel from 'laravel-vite-plugin'
import { defineConfig } from 'vite'
Expand All @@ -9,6 +10,7 @@ export default defineConfig({
ssr: 'resources/js/ssr.ts',
refresh: true,
}),
inertia('resources/js/viteSsr.ts'),
svelte({
compilerOptions: {
hydratable: true,
Expand Down
10 changes: 10 additions & 0 deletions playgrounds/svelte5/resources/js/viteSsr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { createInertiaApp } from '@inertiajs/svelte'
import type { AppCallback } from '@inertiajs/svelte/server'

const render: AppCallback = (page) =>
createInertiaApp({
page,
resolve: (name) => import(`./Pages/${name}.svelte`),
})

export default render
2 changes: 2 additions & 0 deletions playgrounds/svelte5/vite.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { svelte } from '@sveltejs/vite-plugin-svelte'
import laravel from 'laravel-vite-plugin'
import { defineConfig } from 'vite'
import inertia from '@inertiajs/core/vite';

export default defineConfig({
plugins: [
Expand All @@ -9,6 +10,7 @@ export default defineConfig({
ssr: 'resources/js/ssr.ts',
refresh: true,
}),
inertia('resources/js/viteSsr.ts'),
svelte(),
],
})
19 changes: 19 additions & 0 deletions playgrounds/vue3/resources/js/viteSsr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { createInertiaApp } from '@inertiajs/vue3'
import type { AppCallback } from '@inertiajs/vue3/server'
import { renderToString } from '@vue/server-renderer'
import { createSSRApp, h } from 'vue'

const render: AppCallback = (page) =>
createInertiaApp({
page,
render: renderToString,
title: (title) => `${title} - Vue 3 Playground`,
resolve: (name) => import(`./Pages/${name}.vue`),
setup({ App, props, plugin }) {
return createSSRApp({
render: () => h(App, props),
}).use(plugin)
},
})

export default render
2 changes: 2 additions & 0 deletions playgrounds/vue3/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import inertia from '@inertiajs/core/vite'
import vue from '@vitejs/plugin-vue'
import laravel from 'laravel-vite-plugin'
import { defineConfig } from 'vite'
Expand All @@ -9,6 +10,7 @@ export default defineConfig({
ssr: 'resources/js/ssr.ts',
refresh: true,
}),
inertia('resources/js/viteSsr.ts'),
vue({
template: {
transformAssetUrls: {
Expand Down
Loading