mirror of
https://github.com/nuxt/nuxt.git
synced 2025-02-16 13:48:13 +00:00
feat(vite-node): on-demand manifest generation (#3968)
Co-authored-by: pooya parsa <pyapar@gmail.com>
This commit is contained in:
parent
d2b4e60963
commit
e31c604ac4
26
examples/experimental/vite-node/app.vue
Normal file
26
examples/experimental/vite-node/app.vue
Normal file
@ -0,0 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
const count = ref(0)
|
||||
|
||||
function inc () {
|
||||
count.value++
|
||||
}
|
||||
function dec () {
|
||||
count.value--
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NuxtExampleLayout example="experimental/vite-node">
|
||||
<div>
|
||||
{{ count }}
|
||||
<div class="flex gap-1 justify-center">
|
||||
<NButton @click="inc()">
|
||||
Inc
|
||||
</NButton>
|
||||
<NButton @click="dec()">
|
||||
Dec
|
||||
</NButton>
|
||||
</div>
|
||||
</div>
|
||||
</NuxtExampleLayout>
|
||||
</template>
|
10
examples/experimental/vite-node/nuxt.config.ts
Normal file
10
examples/experimental/vite-node/nuxt.config.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { defineNuxtConfig } from 'nuxt3'
|
||||
|
||||
export default defineNuxtConfig({
|
||||
modules: [
|
||||
'@nuxt/ui'
|
||||
],
|
||||
experimental: {
|
||||
viteNode: true
|
||||
}
|
||||
})
|
13
examples/experimental/vite-node/package.json
Normal file
13
examples/experimental/vite-node/package.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "example-vite-node",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "nuxi build",
|
||||
"dev": "nuxi dev",
|
||||
"start": "nuxi preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxt/ui": "npm:@nuxt/ui-edge@latest",
|
||||
"nuxt3": "latest"
|
||||
}
|
||||
}
|
3
examples/experimental/vite-node/tsconfig.json
Normal file
3
examples/experimental/vite-node/tsconfig.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "./.nuxt/tsconfig.json"
|
||||
}
|
8
packages/vite/src/runtime/client.manifest.mjs
Normal file
8
packages/vite/src/runtime/client.manifest.mjs
Normal file
@ -0,0 +1,8 @@
|
||||
import { $fetch } from 'ohmyfetch'
|
||||
import { getViteNodeOptions } from './vite-node-shared.mjs'
|
||||
|
||||
const viteNodeOptions = getViteNodeOptions()
|
||||
|
||||
const manifest = await $fetch('/manifest', { baseURL: viteNodeOptions.baseURL })
|
||||
|
||||
export default manifest
|
@ -1,60 +0,0 @@
|
||||
import { promises as fs } from 'fs'
|
||||
import { fileURLToPath } from 'url'
|
||||
import { ViteNodeRunner } from 'vite-node/client'
|
||||
import { dirname, join } from 'pathe'
|
||||
|
||||
const url = process.env.NUXT_VITE_SERVER_FETCH
|
||||
const entry = process.env.NUXT_VITE_SERVER_ENTRY
|
||||
const base = process.env.NUXT_VITE_SERVER_BASE
|
||||
const root = process.env.NUXT_VITE_SERVER_ROOT
|
||||
|
||||
const runner = new ViteNodeRunner({
|
||||
root,
|
||||
base,
|
||||
async fetchModule (id) {
|
||||
return await $fetch(url, {
|
||||
method: 'POST',
|
||||
body: { id }
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const IS_CSS_RE = /\.(css|postcss|sass|scss|less|stylus|styl)(\?[^.]+)?$/
|
||||
function isCSS (file) {
|
||||
return IS_CSS_RE.test(file)
|
||||
}
|
||||
|
||||
async function writeManifest () {
|
||||
const dir = dirname(fileURLToPath(import.meta.url))
|
||||
|
||||
const entries = [
|
||||
'@vite/client',
|
||||
'entry.mjs',
|
||||
...Array.from(runner.moduleCache.keys())
|
||||
.filter(i => runner.moduleCache.get(i).exports && isCSS(i))
|
||||
.map(i => i.slice(1))
|
||||
]
|
||||
|
||||
const clientManifest = {
|
||||
publicPath: '',
|
||||
all: entries,
|
||||
initial: entries,
|
||||
async: [],
|
||||
modules: {}
|
||||
}
|
||||
|
||||
await fs.writeFile(join(dir, 'client.manifest.json'), JSON.stringify(clientManifest, null, 2), 'utf8')
|
||||
await fs.writeFile(join(dir, 'client.manifest.mjs'), 'export default ' + JSON.stringify(clientManifest, null, 2), 'utf8')
|
||||
}
|
||||
|
||||
let render
|
||||
|
||||
export default async (ssrContext) => {
|
||||
// Workaround for stub mode
|
||||
// https://github.com/nuxt/framework/pull/3983
|
||||
process.server = true
|
||||
render = render || (await runner.executeFile(entry)).default
|
||||
const result = await render(ssrContext)
|
||||
await writeManifest()
|
||||
return result
|
||||
}
|
8
packages/vite/src/runtime/vite-node-shared.d.ts
vendored
Normal file
8
packages/vite/src/runtime/vite-node-shared.d.ts
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
export interface ViteNodeRuntimeOptions {
|
||||
baseURL: string,
|
||||
rootDir: string,
|
||||
entryPath: string,
|
||||
base: string
|
||||
}
|
||||
|
||||
export function getViteNodeOptions (): ViteNodeRuntimeOptions
|
4
packages/vite/src/runtime/vite-node-shared.mjs
Normal file
4
packages/vite/src/runtime/vite-node-shared.mjs
Normal file
@ -0,0 +1,4 @@
|
||||
/** @type {import('./vite-node-shared').getViteNodeOptions} */
|
||||
export function getViteNodeOptions () {
|
||||
return JSON.parse(process.env.NUXT_VITE_NODE_OPTIONS || '{}')
|
||||
}
|
26
packages/vite/src/runtime/vite-node.mjs
Normal file
26
packages/vite/src/runtime/vite-node.mjs
Normal file
@ -0,0 +1,26 @@
|
||||
import { ViteNodeRunner } from 'vite-node/client'
|
||||
import { $fetch } from 'ohmyfetch'
|
||||
import { getViteNodeOptions } from './vite-node-shared.mjs'
|
||||
|
||||
const viteNodeOptions = getViteNodeOptions()
|
||||
|
||||
const runner = new ViteNodeRunner({
|
||||
root: viteNodeOptions.rootDir,
|
||||
base: viteNodeOptions.base,
|
||||
async fetchModule (id) {
|
||||
return await $fetch('/module/' + encodeURI(id), {
|
||||
baseURL: viteNodeOptions.baseURL
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
let render
|
||||
|
||||
export default async (ssrContext) => {
|
||||
// Workaround for stub mode
|
||||
// https://github.com/nuxt/framework/pull/3983
|
||||
process.server = true
|
||||
render = render || (await runner.executeFile(viteNodeOptions.entryPath)).default
|
||||
const result = await render(ssrContext)
|
||||
return result
|
||||
}
|
@ -1,11 +1,12 @@
|
||||
import { IncomingMessage } from 'http'
|
||||
import { createApp, createError, defineEventHandler, defineLazyEventHandler } from 'h3'
|
||||
import { ViteNodeServer } from 'vite-node/server'
|
||||
import fse from 'fs-extra'
|
||||
import { resolve } from 'pathe'
|
||||
import { addServerMiddleware } from '@nuxt/kit'
|
||||
import type { Connect, Plugin as VitePlugin } from 'vite'
|
||||
import type { Plugin as VitePlugin, ViteDevServer } from 'vite'
|
||||
import { distDir } from './dirs'
|
||||
import type { ViteBuildContext } from './vite'
|
||||
import { isCSS } from './utils'
|
||||
|
||||
// TODO: Remove this in favor of registerViteNodeMiddleware
|
||||
// after Nitropack or h3 fixed for adding middlewares after setup
|
||||
@ -26,68 +27,82 @@ export function registerViteNodeMiddleware (ctx: ViteBuildContext) {
|
||||
})
|
||||
}
|
||||
|
||||
function createViteNodeMiddleware (ctx: ViteBuildContext): Connect.NextHandleFunction {
|
||||
let node: ViteNodeServer | undefined
|
||||
return async (req, res, next) => {
|
||||
if (!node && ctx.ssrServer) {
|
||||
node = new ViteNodeServer(ctx.ssrServer, {
|
||||
deps: {
|
||||
inline: [
|
||||
/\/nuxt3\//,
|
||||
/^#/,
|
||||
...ctx.nuxt.options.build.transpile as string[]
|
||||
]
|
||||
}
|
||||
})
|
||||
}
|
||||
if (!node) {
|
||||
return next()
|
||||
}
|
||||
function getManifest (server: ViteDevServer) {
|
||||
const ids = Array.from(server.moduleGraph.urlToModuleMap.keys())
|
||||
.filter(i => isCSS(i))
|
||||
|
||||
const body = await getBodyJson(req) || {}
|
||||
const { id } = body
|
||||
if (!id) {
|
||||
res.statusCode = 400
|
||||
res.end()
|
||||
} else {
|
||||
res.write(JSON.stringify(await node.fetchModule(id)))
|
||||
res.end()
|
||||
}
|
||||
const entries = [
|
||||
'@vite/client',
|
||||
'entry.mjs',
|
||||
...ids.map(i => i.slice(1))
|
||||
]
|
||||
|
||||
return {
|
||||
publicPath: '',
|
||||
all: entries,
|
||||
initial: entries,
|
||||
async: [],
|
||||
modules: {}
|
||||
}
|
||||
}
|
||||
|
||||
function createViteNodeMiddleware (ctx: ViteBuildContext) {
|
||||
const app = createApp()
|
||||
|
||||
app.use('/manifest', defineEventHandler(async () => {
|
||||
const manifest = await getManifest(ctx.ssrServer)
|
||||
return manifest
|
||||
}))
|
||||
|
||||
app.use('/module', defineLazyEventHandler(() => {
|
||||
const node: ViteNodeServer = new ViteNodeServer(ctx.ssrServer, {
|
||||
deps: {
|
||||
inline: [
|
||||
/\/nuxt3\//,
|
||||
/^#/,
|
||||
...ctx.nuxt.options.build.transpile as string[]
|
||||
]
|
||||
}
|
||||
})
|
||||
return async (event) => {
|
||||
const moduleId = decodeURI(event.req.url).substring(1)
|
||||
if (moduleId === '/') {
|
||||
throw createError({ statusCode: 400 })
|
||||
}
|
||||
const module = await node.fetchModule(moduleId) as any
|
||||
return module
|
||||
}
|
||||
}))
|
||||
|
||||
return app.nodeHandler
|
||||
}
|
||||
|
||||
export async function prepareDevServerEntry (ctx: ViteBuildContext) {
|
||||
let entryPath = resolve(ctx.nuxt.options.appDir, 'entry.async.mjs')
|
||||
if (!fse.existsSync(entryPath)) {
|
||||
entryPath = resolve(ctx.nuxt.options.appDir, 'entry.async')
|
||||
}
|
||||
|
||||
// TODO: Update me
|
||||
const host = ctx.nuxt.options.server.host || 'localhost'
|
||||
const port = ctx.nuxt.options.server.port || '3000'
|
||||
const protocol = ctx.nuxt.options.server.https ? 'https' : 'http'
|
||||
|
||||
process.env.NUXT_VITE_SERVER_FETCH = `${protocol}://${host}:${port}/__nuxt_vite_node__/`
|
||||
process.env.NUXT_VITE_SERVER_ENTRY = entryPath
|
||||
process.env.NUXT_VITE_SERVER_BASE = ctx.ssrServer.config.base || '/_nuxt/'
|
||||
process.env.NUXT_VITE_SERVER_ROOT = ctx.nuxt.options.rootDir
|
||||
// Serialize and pass vite-node runtime options
|
||||
const viteNodeServerOptions = {
|
||||
baseURL: `${protocol}://${host}:${port}/__nuxt_vite_node__`,
|
||||
rootDir: ctx.nuxt.options.rootDir,
|
||||
entryPath,
|
||||
base: ctx.ssrServer.config.base || '/_nuxt/'
|
||||
}
|
||||
process.env.NUXT_VITE_NODE_OPTIONS = JSON.stringify(viteNodeServerOptions)
|
||||
|
||||
await fse.copyFile(
|
||||
resolve(distDir, 'runtime/server.mjs'),
|
||||
resolve(ctx.nuxt.options.buildDir, 'dist/server/server.mjs')
|
||||
await fse.writeFile(
|
||||
resolve(ctx.nuxt.options.buildDir, 'dist/server/server.mjs'),
|
||||
`export { default } from ${JSON.stringify(resolve(distDir, 'runtime/vite-node.mjs'))}`
|
||||
)
|
||||
await fse.writeFile(
|
||||
resolve(ctx.nuxt.options.buildDir, 'dist/server/client.manifest.mjs'),
|
||||
`export { default } from ${JSON.stringify(resolve(distDir, 'runtime/client.manifest.mjs'))}`
|
||||
)
|
||||
}
|
||||
|
||||
function getBodyJson (req: IncomingMessage) {
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
let body = ''
|
||||
req.on('data', (chunk) => { body += chunk })
|
||||
req.on('error', reject)
|
||||
req.on('end', () => {
|
||||
try {
|
||||
resolve(JSON.parse(body) || {})
|
||||
} catch (e) {
|
||||
reject(e)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -10755,6 +10755,15 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"example-vite-node@workspace:examples/experimental/vite-node":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "example-vite-node@workspace:examples/experimental/vite-node"
|
||||
dependencies:
|
||||
"@nuxt/ui": "npm:@nuxt/ui-edge@latest"
|
||||
nuxt3: latest
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"example-wasm@workspace:examples/experimental/wasm":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "example-wasm@workspace:examples/experimental/wasm"
|
||||
|
Loading…
Reference in New Issue
Block a user