Skip to content

Commit 7e8e004

Browse files
authored
fix(esbuild): inject esbuild helper functions with minified $ variables correctly (#20761)
1 parent a679a64 commit 7e8e004

File tree

2 files changed

+60
-24
lines changed

2 files changed

+60
-24
lines changed

packages/vite/src/node/__tests__/plugins/esbuild.spec.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import path from 'node:path'
22
import { describe, expect, test } from 'vitest'
33
import type { ResolvedConfig, UserConfig } from '../../config'
44
import {
5+
injectEsbuildHelpers,
56
resolveEsbuildTranspileOptions,
67
transformWithEsbuild,
78
} from '../../plugins/esbuild'
@@ -397,6 +398,33 @@ describe('transformWithEsbuild', () => {
397398
})
398399
})
399400

401+
describe('injectEsbuildHelpers', () => {
402+
test('injects helpers in IIFE format', () => {
403+
const esbuildCode =
404+
'var $=function(){};var MyLib=function(){"use strict";return 42;}'
405+
const result = injectEsbuildHelpers(esbuildCode, 'iife')
406+
expect(result).toBe(
407+
'var MyLib=function(){"use strict";var $=function(){};return 42;}',
408+
)
409+
})
410+
411+
test('injects helpers in UMD format', () => {
412+
const esbuildCode =
413+
'var $=function(){};(function(global){"use strict";return 42;})'
414+
const result = injectEsbuildHelpers(esbuildCode, 'umd')
415+
expect(result).toBe(
416+
'(function(global){"use strict";var $=function(){};return 42;})',
417+
)
418+
})
419+
420+
test('handles helpers with special characters', () => {
421+
const esbuildCode =
422+
'var $$=function(){};var MyLib=function(){"use strict";return 42;}'
423+
const result = injectEsbuildHelpers(esbuildCode, 'iife')
424+
expect(result).toContain('"use strict";var $$=function(){};')
425+
})
426+
})
427+
400428
/**
401429
* Helper for `resolveEsbuildTranspileOptions` to created resolved config with types.
402430
* Note: The function only uses `build.target`, `build.minify` and `esbuild` options.

packages/vite/src/node/plugins/esbuild.ts

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,37 @@ const rollupToEsbuildFormatMap: Record<
318318
iife: undefined,
319319
}
320320

321+
// #7188, esbuild adds helpers out of the UMD and IIFE wrappers, and the
322+
// names are minified potentially causing collision with other globals.
323+
// We inject the helpers inside the wrappers.
324+
// e.g. turn:
325+
// <esbuild helpers> (function(){ /*actual content/* })()
326+
// into:
327+
// (function(){ <esbuild helpers> /*actual content/* })()
328+
// Not using regex because it's too hard to rule out performance issues like #8738 #8099 #10900 #14065
329+
// Instead, using plain string index manipulation (indexOf, slice) which is simple and performant
330+
// We don't need to create a MagicString here because both the helpers and
331+
// the headers don't modify the sourcemap
332+
export const injectEsbuildHelpers = (
333+
esbuildCode: string,
334+
format: string,
335+
): string => {
336+
const contentIndex =
337+
format === 'iife'
338+
? Math.max(esbuildCode.search(IIFE_BEGIN_RE), 0)
339+
: format === 'umd'
340+
? esbuildCode.indexOf(`(function(`) // same for minified or not
341+
: 0
342+
343+
if (contentIndex > 0) {
344+
const esbuildHelpers = esbuildCode.slice(0, contentIndex)
345+
return esbuildCode
346+
.slice(contentIndex)
347+
.replace('"use strict";', (m: string) => m + esbuildHelpers)
348+
}
349+
return esbuildCode
350+
}
351+
321352
export const buildEsbuildPlugin = (): Plugin => {
322353
return {
323354
name: 'vite:esbuild-transpile',
@@ -346,30 +377,7 @@ export const buildEsbuildPlugin = (): Plugin => {
346377
)
347378

348379
if (config.build.lib) {
349-
// #7188, esbuild adds helpers out of the UMD and IIFE wrappers, and the
350-
// names are minified potentially causing collision with other globals.
351-
// We inject the helpers inside the wrappers.
352-
// e.g. turn:
353-
// <esbuild helpers> (function(){ /*actual content/* })()
354-
// into:
355-
// (function(){ <esbuild helpers> /*actual content/* })()
356-
// Not using regex because it's too hard to rule out performance issues like #8738 #8099 #10900 #14065
357-
// Instead, using plain string index manipulation (indexOf, slice) which is simple and performant
358-
// We don't need to create a MagicString here because both the helpers and
359-
// the headers don't modify the sourcemap
360-
const esbuildCode = res.code
361-
const contentIndex =
362-
opts.format === 'iife'
363-
? Math.max(esbuildCode.search(IIFE_BEGIN_RE), 0)
364-
: opts.format === 'umd'
365-
? esbuildCode.indexOf(`(function(`) // same for minified or not
366-
: 0
367-
if (contentIndex > 0) {
368-
const esbuildHelpers = esbuildCode.slice(0, contentIndex)
369-
res.code = esbuildCode
370-
.slice(contentIndex)
371-
.replace(`"use strict";`, `"use strict";` + esbuildHelpers)
372-
}
380+
res.code = injectEsbuildHelpers(res.code, opts.format)
373381
}
374382

375383
return res

0 commit comments

Comments
 (0)