Skip to content

Commit

Permalink
fix: always apply vite ssr source maps (#2433)
Browse files Browse the repository at this point in the history
* fix: always apply vite ssr source maps

* chore: remove non-vite-node source maps from code

* test: source map test

* chore: force esbuild to not generate inline source map

* chore: fix failing text
  • Loading branch information
sheremet-va committed Dec 5, 2022
1 parent 5098b21 commit cbf91ba
Show file tree
Hide file tree
Showing 7 changed files with 44 additions and 21 deletions.
9 changes: 4 additions & 5 deletions packages/vite-node/src/client.ts
Expand Up @@ -6,6 +6,7 @@ import { isNodeBuiltin } from 'mlly'
import createDebug from 'debug'
import { isPrimitive, mergeSlashes, normalizeModuleId, normalizeRequestId, slash, toFilePath } from './utils'
import type { HotContext, ModuleCache, ViteNodeRunnerOptions } from './types'
import { extractSourceMap } from './source-map'

const debugExecute = createDebug('vite-node:client:execute')
const debugNative = createDebug('vite-node:client:native')
Expand Down Expand Up @@ -114,13 +115,11 @@ export class ModuleCacheMap extends Map<string, ModuleCache> {
* Return parsed source map based on inlined source map of the module
*/
getSourceMap(id: string) {
const fsPath = this.normalizePath(id)
const cache = this.get(fsPath)
const cache = this.get(id)
if (cache.map)
return cache.map
const mapString = cache?.code?.match(/\/\/# sourceMappingURL=data:application\/json;charset=utf-8;base64,(.+)/)?.[1]
if (mapString) {
const map = JSON.parse(Buffer.from(mapString, 'base64').toString('utf-8'))
const map = cache.code && extractSourceMap(cache.code)
if (map) {
cache.map = map
return map
}
Expand Down
3 changes: 2 additions & 1 deletion packages/vite-node/src/server.ts
Expand Up @@ -4,8 +4,9 @@ import type { TransformResult, ViteDevServer } from 'vite'
import createDebug from 'debug'
import type { DebuggerOptions, FetchResult, RawSourceMap, ViteNodeResolveId, ViteNodeServerOptions } from './types'
import { shouldExternalize } from './externalize'
import { toArray, toFilePath, withInlineSourcemap } from './utils'
import { toArray, toFilePath } from './utils'
import { Debugger } from './debug'
import { withInlineSourcemap } from './source-map'

export * from './externalize'

Expand Down
30 changes: 30 additions & 0 deletions packages/vite-node/src/source-map.ts
@@ -1,10 +1,40 @@
import { install } from 'source-map-support'
import type { TransformResult } from 'vite'
import type { RawSourceMap } from './types'

interface InstallSourceMapSupportOptions {
getSourceMap: (source: string) => RawSourceMap | null | undefined
}

let SOURCEMAPPING_URL = 'sourceMa'
SOURCEMAPPING_URL += 'ppingURL'

const VITE_NODE_SOURCEMAPPING_URL = `${SOURCEMAPPING_URL}=data:application/json;charset=utf-8;source=vite-node`
const VITE_NODE_SOURCEMAPPING_REGEXP = new RegExp(`//# ${VITE_NODE_SOURCEMAPPING_URL};base64,(.+)`)
const OTHER_SOURCE_MAP_REGEXP = new RegExp(`//# ${SOURCEMAPPING_URL}=data:application/json[^,]+base64,(.+)`)

export async function withInlineSourcemap(result: TransformResult) {
const { code, map } = result

if (!map || code.includes(VITE_NODE_SOURCEMAPPING_URL))
return result

// to reduce the payload size, we only inline vite node source map, because it's also the only one we use
if (OTHER_SOURCE_MAP_REGEXP.test(code))
result.code = code.replace(OTHER_SOURCE_MAP_REGEXP, '')

result.code = `${code}\n\n//# ${VITE_NODE_SOURCEMAPPING_URL};base64,${Buffer.from(JSON.stringify(map), 'utf-8').toString('base64')}\n`

return result
}

export function extractSourceMap(code: string): RawSourceMap | null {
const mapString = code.match(VITE_NODE_SOURCEMAPPING_REGEXP)?.[1]
if (mapString)
return JSON.parse(Buffer.from(mapString, 'base64').toString('utf-8'))
return null
}

export function installSourcemapsSupport(options: InstallSourceMapSupportOptions) {
install({
environment: 'node',
Expand Down
15 changes: 0 additions & 15 deletions packages/vite-node/src/utils.ts
@@ -1,7 +1,6 @@
import { fileURLToPath, pathToFileURL } from 'url'
import { existsSync } from 'fs'
import { relative, resolve } from 'pathe'
import type { TransformResult } from 'vite'
import { isNodeBuiltin } from 'mlly'
import type { Arrayable, Nullable } from './types'

Expand Down Expand Up @@ -88,20 +87,6 @@ export function toFilePath(id: string, root: string): string {
: absolute
}

let SOURCEMAPPING_URL = 'sourceMa'
SOURCEMAPPING_URL += 'ppingURL'

export async function withInlineSourcemap(result: TransformResult) {
const { code, map } = result

if (code.includes(`${SOURCEMAPPING_URL}=`))
return result
if (map)
result.code = `${code}\n\n//# ${SOURCEMAPPING_URL}=data:application/json;charset=utf-8;base64,${Buffer.from(JSON.stringify(map), 'utf-8').toString('base64')}\n`

return result
}

/**
* Convert `Arrayable<T>` to `Array<T>`
*
Expand Down
3 changes: 3 additions & 0 deletions packages/vitest/src/node/plugins/index.ts
Expand Up @@ -90,6 +90,9 @@ export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest('t
open = '/'

const config: ViteConfig = {
esbuild: {
sourcemap: 'external',
},
resolve: {
// by default Vite resolves `module` field, which not always a native ESM module
// setting this option can bypass that and fallback to cjs version
Expand Down
2 changes: 2 additions & 0 deletions packages/vitest/src/utils/source-map.ts
Expand Up @@ -6,6 +6,8 @@ export const lineSplitRE = /\r?\n/
const stackIgnorePatterns = [
'node:internal',
'/vitest/dist/',
'/vite-node/dist',
'/vite-node/src',
'/vitest/src/',
'/node_modules/chai/',
'/node_modules/tinypool/',
Expand Down
3 changes: 3 additions & 0 deletions test/stacktraces/vite.config.ts
@@ -1,6 +1,9 @@
import { defineConfig } from 'vite'

export default defineConfig({
esbuild: {
sourcemap: 'both',
},
test: {
include: ['test/*.test.ts'],
},
Expand Down

0 comments on commit cbf91ba

Please sign in to comment.