diff --git a/packages/vite/src/node/server/__tests__/send.spec.ts b/packages/vite/src/node/server/__tests__/send.spec.ts new file mode 100644 index 00000000000000..c672887ebda43e --- /dev/null +++ b/packages/vite/src/node/server/__tests__/send.spec.ts @@ -0,0 +1,48 @@ +import type { IncomingMessage, ServerResponse } from 'node:http' +import { describe, expect, test } from 'vitest' +import { send } from '../send' + +function createReq(url = '/foo.js'): IncomingMessage { + return { url, method: 'GET', headers: {} } as IncomingMessage +} + +function createRes(): ServerResponse & { body?: string | Buffer } { + const res = { + writableEnded: false, + statusCode: 0, + headers: {} as Record, + setHeader(name: string, value: unknown) { + res.headers[name] = value + return res + }, + end(chunk?: string | Buffer) { + res.body = chunk + res.writableEnded = true + }, + } + return res as unknown as ServerResponse & { body?: string | Buffer } +} + +describe('send fallback sourcemap', () => { + test('injects a fallback sourcemap for js without a map', () => { + const req = createReq() + const res = createRes() + send(req, res, 'const a = 1', 'js', {}) + expect(res.body!.toString()).toContain('//# sourceMappingURL=') + }) + + test('skips the fallback sourcemap for very large js', () => { + const req = createReq() + const res = createRes() + const code = `export default ${JSON.stringify('x\n'.repeat(5_000_000))}` + send(req, res, code, 'js', {}) + expect(res.body!.toString()).not.toContain('//# sourceMappingURL=') + }) + + test('does not inject a fallback sourcemap for non-js', () => { + const req = createReq('/foo.css') + const res = createRes() + send(req, res, '.a { color: red }', 'css', {}) + expect(res.body!.toString()).not.toContain('sourceMappingURL=') + }) +}) diff --git a/packages/vite/src/node/server/send.ts b/packages/vite/src/node/server/send.ts index 780ab8984a6441..e9b5e79fac8848 100644 --- a/packages/vite/src/node/server/send.ts +++ b/packages/vite/src/node/server/send.ts @@ -29,6 +29,9 @@ export interface SendOptions { map?: SourceMap | { mappings: '' } | null } +// skip the fallback sourcemap for large code to avoid OOM (#22132) +const MAX_FALLBACK_SOURCEMAP_CODE_LENGTH = 4 * 1024 * 1024 + export function send( req: IncomingMessage, res: ServerResponse, @@ -76,6 +79,8 @@ export function send( // if the code has existing inline sourcemap, assume it's correct and skip if (convertSourceMap.mapFileCommentRegex.test(code)) { debug?.(`Skipped injecting fallback sourcemap for ${req.url}`) + } else if (code.length > MAX_FALLBACK_SOURCEMAP_CODE_LENGTH) { + debug?.(`Skipped injecting fallback sourcemap for large ${req.url}`) } else { const urlWithoutTimestamp = removeTimestampQuery(req.url!) const ms = new MagicString(code)