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

fix: sourcemapIgnoreList for optimizedDeps #12633

Merged
merged 3 commits into from Mar 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 16 additions & 4 deletions packages/vite/src/node/server/middlewares/transform.ts
Expand Up @@ -2,6 +2,7 @@ import path from 'node:path'
import fsp from 'node:fs/promises'
import type { Connect } from 'dep-types/connect'
import colors from 'picocolors'
import type { ExistingRawSourceMap } from 'rollup'
import type { ViteDevServer } from '..'
import {
cleanUrl,
Expand All @@ -19,6 +20,7 @@ import {
} from '../../utils'
import { send } from '../send'
import { ERR_LOAD_URL, transformRequest } from '../transformRequest'
import { applySourcemapIgnoreList } from '../sourcemap'
import { isHTMLProxy } from '../../plugins/html'
import {
DEP_VERSION_RE,
Expand Down Expand Up @@ -75,14 +77,24 @@ export function transformMiddleware(
if (depsOptimizer?.isOptimizedDepUrl(url)) {
// If the browser is requesting a source map for an optimized dep, it
// means that the dependency has already been pre-bundled and loaded
const mapFile = url.startsWith(FS_PREFIX)
const sourcemapPath = url.startsWith(FS_PREFIX)
? fsPathFromId(url)
: normalizePath(
ensureVolumeInPath(path.resolve(root, url.slice(1))),
)
try {
const map = await fsp.readFile(mapFile, 'utf-8')
return send(req, res, map, 'json', {
const map = JSON.parse(
await fsp.readFile(sourcemapPath, 'utf-8'),
) as ExistingRawSourceMap

applySourcemapIgnoreList(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of rewriting the sourcemap when serving it, wouldn't it make sense to consider the ignore list predicate when initially generating the optimized dep?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about it, but we don't have the .map in memory to modify it. See #12622, we needed to go back to let esbuild write the files to disk directly because there was a perf regression.
We would also need to start using sourcemapIgnoreList.toString() in the hash key for optimization given our current architecture. That could work though.

If we want to do it, I think we should still do it lazily to avoid blocking giving the optimized deps to the browser. But that means that if the process is stopped, we may end up with inconsistent results.

So I see three options:
a. Current approach in this PR, and we accept the JSON.parse/JSON.stringify cost (I think this could be fine)
b. Current approach, but we don't use JSON.parse/JSON.stringify and instead use regexes to grab the sources array and then inject after it the ignoreSourcemapList.
c. We add sourcemapIgnoreList to the deps cache key, and when we do the first JSON.parse, we also add a marker like "__vite": "sourcemapIgnorList_done",. So next time we load it, we can use a regex to know it has been processed.

Leaning towards b. if there is a regression perf after this PR. cc @bluwy @sapphi-red

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My opinion is to go with "a." for now because I guess this is not a hot path.

One more option would be to create a feature request / PR to esbuild for adding sourcemap ignore list feature.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation. I'd also heavily lean towards "a." unless there's a serious performance problem.

map,
sourcemapPath,
server.config.server.sourcemapIgnoreList,
logger,
)

return send(req, res, JSON.stringify(map), 'json', {
headers: server.config.server.headers,
})
} catch (e) {
Expand All @@ -91,7 +103,7 @@ export function transformMiddleware(
// Send back an empty source map so the browser doesn't issue warnings
const dummySourceMap = {
version: 3,
file: mapFile.replace(/\.map$/, ''),
file: sourcemapPath.replace(/\.map$/, ''),
sources: [],
sourcesContent: [],
names: [],
Expand Down
40 changes: 39 additions & 1 deletion packages/vite/src/node/server/sourcemap.ts
@@ -1,6 +1,6 @@
import path from 'node:path'
import { promises as fs } from 'node:fs'
import type { SourceMap } from 'rollup'
import type { ExistingRawSourceMap, SourceMap } from 'rollup'
import type { Logger } from '../logger'
import { createDebugger } from '../utils'

Expand Down Expand Up @@ -83,3 +83,41 @@ export function getCodeWithSourcemap(

return code
}

export function applySourcemapIgnoreList(
map: ExistingRawSourceMap,
sourcemapPath: string,
sourcemapIgnoreList: (sourcePath: string, sourcemapPath: string) => boolean,
logger?: Logger,
): void {
let { x_google_ignoreList } = map
if (x_google_ignoreList === undefined) {
x_google_ignoreList = []
}
for (
let sourcesIndex = 0;
sourcesIndex < map.sources.length;
++sourcesIndex
) {
const sourcePath = map.sources[sourcesIndex]
if (!sourcePath) continue

const ignoreList = sourcemapIgnoreList(
path.isAbsolute(sourcePath)
? sourcePath
: path.resolve(path.dirname(sourcemapPath), sourcePath),
sourcemapPath,
)
if (logger && typeof ignoreList !== 'boolean') {
logger.warn('sourcemapIgnoreList function must return a boolean.')
}

if (ignoreList && !x_google_ignoreList.includes(sourcesIndex)) {
x_google_ignoreList.push(sourcesIndex)
}
}

if (x_google_ignoreList.length > 0) {
if (!map.x_google_ignoreList) map.x_google_ignoreList = x_google_ignoreList
}
}
62 changes: 27 additions & 35 deletions packages/vite/src/node/server/transformRequest.ts
Expand Up @@ -18,7 +18,7 @@ import {
} from '../utils'
import { checkPublicFile } from '../plugins/asset'
import { getDepsOptimizer } from '../optimizer'
import { injectSourcesContent } from './sourcemap'
import { applySourcemapIgnoreList, injectSourcesContent } from './sourcemap'
import { isFileServingAllowed } from './middlewares/static'

export const ERR_LOAD_URL = 'ERR_LOAD_URL'
Expand Down Expand Up @@ -271,41 +271,33 @@ async function loadAndTransform(
if (map.mappings && !map.sourcesContent) {
await injectSourcesContent(map, mod.file, logger)
}
for (
let sourcesIndex = 0;
sourcesIndex < map.sources.length;
++sourcesIndex
) {
const sourcePath = map.sources[sourcesIndex]
if (!sourcePath) continue

const sourcemapPath = `${mod.file}.map`
const ignoreList = config.server.sourcemapIgnoreList(
path.isAbsolute(sourcePath)
? sourcePath
: path.resolve(path.dirname(sourcemapPath), sourcePath),
sourcemapPath,
)
if (typeof ignoreList !== 'boolean') {
logger.warn('sourcemapIgnoreList function must return a boolean.')
}
if (ignoreList) {
if (map.x_google_ignoreList === undefined) {
map.x_google_ignoreList = []
}
if (!map.x_google_ignoreList.includes(sourcesIndex)) {
map.x_google_ignoreList.push(sourcesIndex)
}
}

// Rewrite sources to relative paths to give debuggers the chance
// to resolve and display them in a meaningful way (rather than
// with absolute paths).
if (path.isAbsolute(sourcePath) && path.isAbsolute(mod.file)) {
map.sources[sourcesIndex] = path.relative(
path.dirname(mod.file),
sourcePath,
)
const sourcemapPath = `${mod.file}.map`
applySourcemapIgnoreList(
map,
sourcemapPath,
config.server.sourcemapIgnoreList,
logger,
)

if (path.isAbsolute(mod.file)) {
for (
let sourcesIndex = 0;
sourcesIndex < map.sources.length;
++sourcesIndex
) {
const sourcePath = map.sources[sourcesIndex]
if (sourcePath) {
// Rewrite sources to relative paths to give debuggers the chance
// to resolve and display them in a meaningful way (rather than
// with absolute paths).
if (path.isAbsolute(sourcePath)) {
map.sources[sourcesIndex] = path.relative(
path.dirname(mod.file),
sourcePath,
)
}
}
}
}
}
Expand Down