diff --git a/packages/vite/src/plugins/nitro-sourcemap.ts b/packages/vite/src/plugins/nitro-sourcemap.ts new file mode 100644 index 0000000000..01c2dc372b --- /dev/null +++ b/packages/vite/src/plugins/nitro-sourcemap.ts @@ -0,0 +1,63 @@ +import { mkdir, readFile, writeFile } from 'node:fs/promises' +import { dirname, resolve } from 'pathe' + +import type { Plugin as RollupPlugin } from 'rollup' +import type { Plugin as VitePlugin } from 'vite' + +export const createSourcemapPreserver = () => { + let outputDir: string + const ids = new Set() + + const vitePlugin = { + name: 'nuxt:sourcemap-export', + configResolved (config) { + outputDir = config.build.outDir + }, + async writeBundle (_options, bundle) { + for (const chunk of Object.values(bundle)) { + if (chunk.type !== 'chunk' || !chunk.map) { continue } + + const id = resolve(outputDir, chunk.fileName) + ids.add(id) + const dest = id + '.map.json' + await mkdir(dirname(dest), { recursive: true }) + await writeFile(dest, JSON.stringify({ + file: chunk.map.file, + mappings: chunk.map.mappings, + names: chunk.map.names, + sources: chunk.map.sources, + sourcesContent: chunk.map.sourcesContent, + version: chunk.map.version, + })) + } + }, + } satisfies VitePlugin + + const nitroPlugin = { + name: 'nuxt:sourcemap-import', + async load (id) { + id = resolve(id) + if (!ids.has(id)) { return } + + const [code, map] = await Promise.all([ + readFile(id, 'utf-8').catch(() => undefined), + readFile(id + '.map.json', 'utf-8').catch(() => undefined), + ]) + + if (!code) { + this.warn('Failed loading file') + return null + } + + return { + code, + map, + } + }, + } satisfies RollupPlugin + + return { + vitePlugin, + nitroPlugin, + } +} diff --git a/packages/vite/src/server.ts b/packages/vite/src/server.ts index f7023b3cb6..84f34e74ee 100644 --- a/packages/vite/src/server.ts +++ b/packages/vite/src/server.ts @@ -5,11 +5,13 @@ import viteJsxPlugin from '@vitejs/plugin-vue-jsx' import { logger, resolvePath, tryImportModule } from '@nuxt/kit' import { joinURL, withTrailingSlash, withoutLeadingSlash } from 'ufo' import type { ViteConfig } from '@nuxt/schema' +import defu from 'defu' import type { ViteBuildContext } from './vite' import { createViteLogger } from './utils/logger' import { initViteNodeServer } from './vite-node' import { writeManifest } from './manifest' import { transpile } from './utils/transpile' +import { createSourcemapPreserver } from './plugins/nitro-sourcemap' export async function buildServer (ctx: ViteBuildContext) { const helper = ctx.nuxt.options.nitro.imports !== false ? '' : 'globalThis.' @@ -121,6 +123,17 @@ export async function buildServer (ctx: ViteBuildContext) { } } + // tell rollup's nitro build about the original sources of the generated vite server build + if (ctx.nuxt.options.sourcemap.server && !ctx.nuxt.options.dev) { + const { vitePlugin, nitroPlugin } = createSourcemapPreserver() + serverConfig.plugins!.push(vitePlugin) + ctx.nuxt.hook('nitro:build:before', (nitro) => { + nitro.options.rollupConfig = defu(nitro.options.rollupConfig, { + plugins: [nitroPlugin], + }) + }) + } + serverConfig.customLogger = createViteLogger(serverConfig) await ctx.nuxt.callHook('vite:extendConfig', serverConfig, { isClient: false, isServer: true })