2024-06-24 09:39:38 +00:00
|
|
|
import { writeFile } from 'node:fs/promises'
|
2022-05-13 11:00:14 +00:00
|
|
|
import { pathToFileURL } from 'node:url'
|
2022-10-15 18:42:57 +00:00
|
|
|
import { createApp, createError, defineEventHandler, defineLazyEventHandler, eventHandler, toNodeListener } from 'h3'
|
2022-03-11 08:41:27 +00:00
|
|
|
import { ViteNodeServer } from 'vite-node/server'
|
2023-04-18 11:30:16 +00:00
|
|
|
import { isAbsolute, normalize, resolve } from 'pathe'
|
2022-09-03 10:02:14 +00:00
|
|
|
import { addDevServerHandler } from '@nuxt/kit'
|
2023-04-20 13:00:05 +00:00
|
|
|
import { isFileServingAllowed } from 'vite'
|
|
|
|
import type { ModuleNode, Plugin as VitePlugin } from 'vite'
|
2024-07-01 09:30:09 +00:00
|
|
|
import { getQuery } from 'ufo'
|
2022-08-07 09:52:34 +00:00
|
|
|
import { normalizeViteManifest } from 'vue-bundle-renderer'
|
2022-07-26 13:49:17 +00:00
|
|
|
import { resolve as resolveModule } from 'mlly'
|
2022-03-11 08:41:27 +00:00
|
|
|
import { distDir } from './dirs'
|
|
|
|
import type { ViteBuildContext } from './vite'
|
2022-04-12 10:04:55 +00:00
|
|
|
import { isCSS } from './utils'
|
2022-07-27 09:01:25 +00:00
|
|
|
import { createIsExternal } from './utils/external'
|
2023-01-19 10:56:34 +00:00
|
|
|
import { transpile } from './utils/transpile'
|
2022-03-11 08:41:27 +00:00
|
|
|
|
|
|
|
// TODO: Remove this in favor of registerViteNodeMiddleware
|
|
|
|
// after Nitropack or h3 fixed for adding middlewares after setup
|
|
|
|
export function viteNodePlugin (ctx: ViteBuildContext): VitePlugin {
|
2022-08-04 10:03:46 +00:00
|
|
|
// Store the invalidates for the next rendering
|
|
|
|
const invalidates = new Set<string>()
|
2023-01-25 12:24:58 +00:00
|
|
|
|
2022-10-24 08:32:49 +00:00
|
|
|
function markInvalidate (mod: ModuleNode) {
|
|
|
|
if (!mod.id) { return }
|
|
|
|
if (invalidates.has(mod.id)) { return }
|
|
|
|
invalidates.add(mod.id)
|
2023-01-25 12:24:58 +00:00
|
|
|
markInvalidates(mod.importers)
|
|
|
|
}
|
|
|
|
|
|
|
|
function markInvalidates (mods?: ModuleNode[] | Set<ModuleNode>) {
|
|
|
|
if (!mods) { return }
|
|
|
|
for (const mod of mods) {
|
|
|
|
markInvalidate(mod)
|
2022-10-24 08:32:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-11 08:41:27 +00:00
|
|
|
return {
|
|
|
|
name: 'nuxt:vite-node-server',
|
2022-08-04 10:03:46 +00:00
|
|
|
enforce: 'post',
|
2022-03-11 08:41:27 +00:00
|
|
|
configureServer (server) {
|
2023-01-25 12:24:58 +00:00
|
|
|
function invalidateVirtualModules () {
|
2022-10-24 08:32:49 +00:00
|
|
|
for (const [id, mod] of server.moduleGraph.idToModuleMap) {
|
|
|
|
if (id.startsWith('virtual:')) {
|
|
|
|
markInvalidate(mod)
|
|
|
|
}
|
|
|
|
}
|
2023-01-25 12:24:58 +00:00
|
|
|
for (const plugin of ctx.nuxt.options.plugins) {
|
|
|
|
markInvalidates(server.moduleGraph.getModulesByFile(typeof plugin === 'string' ? plugin : plugin.src))
|
|
|
|
}
|
|
|
|
for (const template of ctx.nuxt.options.build.templates) {
|
2023-06-10 17:14:58 +00:00
|
|
|
markInvalidates(server.moduleGraph.getModulesByFile(template.dst!))
|
2023-01-25 12:24:58 +00:00
|
|
|
}
|
2022-08-04 10:03:46 +00:00
|
|
|
}
|
2023-01-25 12:24:58 +00:00
|
|
|
|
|
|
|
server.middlewares.use('/__nuxt_vite_node__', toNodeListener(createViteNodeApp(ctx, invalidates)))
|
|
|
|
|
|
|
|
// Invalidate all virtual modules when templates are regenerated
|
|
|
|
ctx.nuxt.hook('app:templatesGenerated', () => {
|
|
|
|
invalidateVirtualModules()
|
|
|
|
})
|
|
|
|
|
|
|
|
server.watcher.on('all', (event, file) => {
|
2023-02-20 20:28:10 +00:00
|
|
|
markInvalidates(server.moduleGraph.getModulesByFile(normalize(file)))
|
2023-01-25 12:24:58 +00:00
|
|
|
// Invalidate all virtual modules when a file is added or removed
|
|
|
|
if (event === 'add' || event === 'unlink') {
|
|
|
|
invalidateVirtualModules()
|
|
|
|
}
|
|
|
|
})
|
2024-04-05 18:08:32 +00:00
|
|
|
},
|
2022-03-11 08:41:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function registerViteNodeMiddleware (ctx: ViteBuildContext) {
|
2022-09-03 10:02:14 +00:00
|
|
|
addDevServerHandler({
|
2022-03-11 08:41:27 +00:00
|
|
|
route: '/__nuxt_vite_node__/',
|
2024-04-05 18:08:32 +00:00
|
|
|
handler: createViteNodeApp(ctx).handler,
|
2022-03-11 08:41:27 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-08-04 15:24:35 +00:00
|
|
|
function getManifest (ctx: ViteBuildContext) {
|
2024-07-01 09:30:09 +00:00
|
|
|
const css = new Set<string>()
|
|
|
|
for (const key of ctx.ssrServer!.moduleGraph.urlToModuleMap.keys()) {
|
|
|
|
if (isCSS(key)) {
|
|
|
|
const query = getQuery(key)
|
|
|
|
if ('raw' in query) { continue }
|
|
|
|
const importers = ctx.ssrServer!.moduleGraph.urlToModuleMap.get(key)?.importers
|
|
|
|
if (importers && [...importers].every(i => i.id && 'raw' in getQuery(i.id))) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
css.add(key)
|
|
|
|
}
|
|
|
|
}
|
2022-03-11 08:41:27 +00:00
|
|
|
|
2022-08-07 09:52:34 +00:00
|
|
|
const manifest = normalizeViteManifest({
|
|
|
|
'@vite/client': {
|
|
|
|
file: '@vite/client',
|
2024-07-01 09:30:09 +00:00
|
|
|
css: [...css],
|
2022-08-09 07:59:08 +00:00
|
|
|
module: true,
|
2024-04-05 18:08:32 +00:00
|
|
|
isEntry: true,
|
2022-08-07 09:52:34 +00:00
|
|
|
},
|
|
|
|
[ctx.entry]: {
|
|
|
|
file: ctx.entry,
|
|
|
|
isEntry: true,
|
|
|
|
module: true,
|
2024-04-05 18:08:32 +00:00
|
|
|
resourceType: 'script',
|
|
|
|
},
|
2022-08-07 09:52:34 +00:00
|
|
|
})
|
2022-04-12 10:04:55 +00:00
|
|
|
|
2022-08-07 09:52:34 +00:00
|
|
|
return manifest
|
2022-03-11 08:41:27 +00:00
|
|
|
}
|
|
|
|
|
2022-10-15 18:42:57 +00:00
|
|
|
function createViteNodeApp (ctx: ViteBuildContext, invalidates: Set<string> = new Set()) {
|
2022-04-12 10:04:55 +00:00
|
|
|
const app = createApp()
|
|
|
|
|
2022-06-08 19:49:11 +00:00
|
|
|
app.use('/manifest', defineEventHandler(() => {
|
2022-08-04 15:24:35 +00:00
|
|
|
const manifest = getManifest(ctx)
|
2022-04-12 10:04:55 +00:00
|
|
|
return manifest
|
|
|
|
}))
|
|
|
|
|
2022-08-04 10:03:46 +00:00
|
|
|
app.use('/invalidates', defineEventHandler(() => {
|
|
|
|
const ids = Array.from(invalidates)
|
|
|
|
invalidates.clear()
|
|
|
|
return ids
|
|
|
|
}))
|
|
|
|
|
2022-04-12 10:04:55 +00:00
|
|
|
app.use('/module', defineLazyEventHandler(() => {
|
2022-08-15 13:40:06 +00:00
|
|
|
const viteServer = ctx.ssrServer!
|
2022-07-26 13:49:17 +00:00
|
|
|
const node: ViteNodeServer = new ViteNodeServer(viteServer, {
|
2022-04-12 10:04:55 +00:00
|
|
|
deps: {
|
|
|
|
inline: [
|
2023-10-12 14:17:38 +00:00
|
|
|
/\/node_modules\/(.*\/)?(nuxt|nuxt3|nuxt-nightly)\//,
|
2022-04-12 10:04:55 +00:00
|
|
|
/^#/,
|
2024-04-05 18:08:32 +00:00
|
|
|
...transpile({ isServer: true, isDev: ctx.nuxt.options.dev }),
|
|
|
|
],
|
2022-07-14 14:00:29 +00:00
|
|
|
},
|
|
|
|
transformMode: {
|
|
|
|
ssr: [/.*/],
|
2024-04-05 18:08:32 +00:00
|
|
|
web: [],
|
|
|
|
},
|
2022-04-12 10:04:55 +00:00
|
|
|
})
|
2023-07-06 13:22:31 +00:00
|
|
|
const isExternal = createIsExternal(viteServer, ctx.nuxt.options.rootDir, ctx.nuxt.options.modulesDir)
|
2022-07-26 13:49:17 +00:00
|
|
|
node.shouldExternalize = async (id: string) => {
|
2022-07-27 09:01:25 +00:00
|
|
|
const result = await isExternal(id)
|
2022-07-26 13:49:17 +00:00
|
|
|
if (result?.external) {
|
2023-09-04 12:49:26 +00:00
|
|
|
return resolveModule(result.id, { url: ctx.nuxt.options.modulesDir }).catch(() => false)
|
2022-07-26 13:49:17 +00:00
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2022-10-15 18:42:57 +00:00
|
|
|
return eventHandler(async (event) => {
|
2023-08-23 07:30:53 +00:00
|
|
|
const moduleId = decodeURI(event.path).substring(1)
|
2022-04-12 10:04:55 +00:00
|
|
|
if (moduleId === '/') {
|
|
|
|
throw createError({ statusCode: 400 })
|
|
|
|
}
|
2023-04-18 11:30:16 +00:00
|
|
|
if (isAbsolute(moduleId) && !isFileServingAllowed(moduleId, viteServer)) {
|
|
|
|
throw createError({ statusCode: 403 /* Restricted */ })
|
|
|
|
}
|
2022-09-03 10:02:14 +00:00
|
|
|
const module = await node.fetchModule(moduleId).catch((err) => {
|
2022-09-16 17:43:35 +00:00
|
|
|
const errorData = {
|
|
|
|
code: 'VITE_ERROR',
|
|
|
|
id: moduleId,
|
|
|
|
stack: '',
|
2024-04-05 18:08:32 +00:00
|
|
|
...err,
|
2022-09-16 17:43:35 +00:00
|
|
|
}
|
|
|
|
throw createError({ data: errorData })
|
2022-09-03 10:02:14 +00:00
|
|
|
})
|
2022-04-12 10:04:55 +00:00
|
|
|
return module
|
2022-10-15 18:42:57 +00:00
|
|
|
})
|
2022-04-12 10:04:55 +00:00
|
|
|
}))
|
|
|
|
|
2022-09-03 10:02:14 +00:00
|
|
|
return app
|
2022-04-12 10:04:55 +00:00
|
|
|
}
|
|
|
|
|
2023-06-28 21:19:51 +00:00
|
|
|
export type ViteNodeServerOptions = {
|
|
|
|
baseURL: string
|
|
|
|
root: string
|
|
|
|
entryPath: string
|
|
|
|
base: string
|
|
|
|
}
|
|
|
|
|
2022-07-27 09:01:25 +00:00
|
|
|
export async function initViteNodeServer (ctx: ViteBuildContext) {
|
2022-04-12 10:04:55 +00:00
|
|
|
// Serialize and pass vite-node runtime options
|
|
|
|
const viteNodeServerOptions = {
|
2022-10-27 10:36:37 +00:00
|
|
|
baseURL: `${ctx.nuxt.options.devServer.url}__nuxt_vite_node__`,
|
2022-08-12 09:11:09 +00:00
|
|
|
root: ctx.nuxt.options.srcDir,
|
2022-09-06 08:21:17 +00:00
|
|
|
entryPath: ctx.entry,
|
2024-04-05 18:08:32 +00:00
|
|
|
base: ctx.ssrServer!.config.base || '/_nuxt/',
|
2023-06-28 21:19:51 +00:00
|
|
|
} satisfies ViteNodeServerOptions
|
2022-04-12 10:04:55 +00:00
|
|
|
process.env.NUXT_VITE_NODE_OPTIONS = JSON.stringify(viteNodeServerOptions)
|
2022-03-30 11:34:23 +00:00
|
|
|
|
2022-05-13 11:00:14 +00:00
|
|
|
const serverResolvedPath = resolve(distDir, 'runtime/vite-node.mjs')
|
|
|
|
const manifestResolvedPath = resolve(distDir, 'runtime/client.manifest.mjs')
|
|
|
|
|
2024-06-24 09:39:38 +00:00
|
|
|
await writeFile(
|
2022-04-12 10:04:55 +00:00
|
|
|
resolve(ctx.nuxt.options.buildDir, 'dist/server/server.mjs'),
|
2024-04-05 18:08:32 +00:00
|
|
|
`export { default } from ${JSON.stringify(pathToFileURL(serverResolvedPath).href)}`,
|
2022-04-12 10:04:55 +00:00
|
|
|
)
|
2024-06-24 09:39:38 +00:00
|
|
|
await writeFile(
|
2022-04-12 10:04:55 +00:00
|
|
|
resolve(ctx.nuxt.options.buildDir, 'dist/server/client.manifest.mjs'),
|
2024-04-05 18:08:32 +00:00
|
|
|
`export { default } from ${JSON.stringify(pathToFileURL(manifestResolvedPath).href)}`,
|
2022-03-11 08:41:27 +00:00
|
|
|
)
|
|
|
|
}
|