Skip to content

Commit 536ebd3

Browse files
committed
fix #2081: avoid "new Function" in "esbuild-wasm"
1 parent fdf31de commit 536ebd3

File tree

3 files changed

+33
-34
lines changed

3 files changed

+33
-34
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
* Avoid `new Function` in esbuild's library code ([#2081](https://github.com/evanw/esbuild/issues/2081))
6+
7+
Some JavaScript environments such as Cloudflare Workers or Deno Deploy don't allow `new Function` because they disallow dynamic JavaScript evaluation. Previously esbuild's WebAssembly-based library used this to construct the WebAssembly worker function. With this release, the code is now inlined without using `new Function` so it will be able to run even when this restriction is in place.
8+
39
## 0.14.25
410

511
* Reduce minification of CSS transforms to avoid Safari bugs ([#2057](https://github.com/evanw/esbuild/issues/2057))

lib/npm/browser.ts

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import * as common from "../shared/common"
33
import * as ourselves from "./browser"
44

55
declare const ESBUILD_VERSION: string;
6-
declare let WEB_WORKER_SOURCE_CODE: string
6+
declare let WEB_WORKER_FUNCTION: (postMessage: (data: Uint8Array) => void) => (event: { data: Uint8Array | ArrayBuffer }) => void
7+
let webWorkerFunction = WEB_WORKER_FUNCTION
78

89
export let version = ESBUILD_VERSION;
910

@@ -74,14 +75,6 @@ const startRunningService = async (wasmURL: string, useWorker: boolean): Promise
7475
let res = await fetch(wasmURL);
7576
if (!res.ok) throw new Error(`Failed to download ${JSON.stringify(wasmURL)}`);
7677
let wasm = await res.arrayBuffer();
77-
let code = `{` +
78-
`let global={};` +
79-
`for(let o=self;o;o=Object.getPrototypeOf(o))` +
80-
`for(let k of Object.getOwnPropertyNames(o))` +
81-
`if(!(k in global))` +
82-
`Object.defineProperty(global,k,{get:()=>self[k]});` +
83-
WEB_WORKER_SOURCE_CODE +
84-
`}`
8578
let worker: {
8679
onmessage: ((event: any) => void) | null
8780
postMessage: (data: Uint8Array | ArrayBuffer) => void
@@ -90,12 +83,11 @@ const startRunningService = async (wasmURL: string, useWorker: boolean): Promise
9083

9184
if (useWorker) {
9285
// Run esbuild off the main thread
93-
let blob = new Blob([code], { type: 'text/javascript' })
86+
let blob = new Blob([`onmessage=(${webWorkerFunction})(postMessage)`], { type: 'text/javascript' })
9487
worker = new Worker(URL.createObjectURL(blob))
9588
} else {
9689
// Run esbuild on the main thread
97-
let fn = new Function('postMessage', code + `var onmessage; return m => onmessage(m)`)
98-
let onmessage = fn((data: Uint8Array) => worker.onmessage!({ data }))
90+
let onmessage = webWorkerFunction((data: Uint8Array) => worker.onmessage!({ data }))
9991
worker = {
10092
onmessage: null,
10193
postMessage: data => onmessage({ data }),

scripts/esbuild.js

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -167,26 +167,29 @@ exports.buildWasmLib = async (esbuildPath) => {
167167
let wasmWorkerCode = {}
168168

169169
for (const [format, target] of Object.entries({ umd: umdBrowserTarget, esm: esmBrowserTarget })) {
170-
// Process "npm/esbuild-wasm/wasm_exec.js"
171-
let wasmExecCode = wasm_exec_js;
172-
if (minify) {
173-
const wasmExecMin = childProcess.execFileSync(esbuildPath, [
174-
'--target=' + target,
175-
].concat(minifyFlags), { cwd: repoDir, input: wasmExecCode }).toString()
176-
const commentLines = wasmExecCode.split('\n')
177-
const firstNonComment = commentLines.findIndex(line => !line.startsWith('//'))
178-
wasmExecCode = '\n' + commentLines.slice(0, firstNonComment).concat(wasmExecMin).join('\n')
179-
}
180-
181-
// Process "lib/worker.ts"
182-
const workerCode = childProcess.execFileSync(esbuildPath, [
183-
path.join(repoDir, 'lib', 'npm', 'worker.ts'),
170+
// Process "npm/esbuild-wasm/wasm_exec.js" and "lib/worker.ts"
171+
const input = `
172+
let onmessage;
173+
let global = {};
174+
for (let o = self; o; o = Object.getPrototypeOf(o))
175+
for (let k of Object.getOwnPropertyNames(o))
176+
if (!(k in global))
177+
Object.defineProperty(global, k, { get: () => self[k] });
178+
${wasm_exec_js}
179+
${fs.readFileSync(path.join(repoDir, 'lib', 'npm', 'worker.ts'), 'utf8')}
180+
return m => onmessage(m)
181+
`;
182+
const wasmExecAndWorker = childProcess.execFileSync(esbuildPath, [
183+
'--loader=ts',
184184
'--target=' + target,
185185
'--define:ESBUILD_VERSION=' + JSON.stringify(version),
186-
'--log-level=warning',
187-
].concat(minifyFlags), { cwd: repoDir }).toString().trim()
188-
189-
wasmWorkerCode[format] = wasmExecCode + workerCode
186+
].concat(minifyFlags), { cwd: repoDir, input }).toString().trim()
187+
const commentLines = wasm_exec_js.split('\n')
188+
const firstNonComment = commentLines.findIndex(line => !line.startsWith('//'))
189+
const commentPrefix = '\n' + commentLines.slice(0, firstNonComment).join('\n') + '\n'
190+
wasmWorkerCode[format] = minify
191+
? `postMessage=>{${commentPrefix}${wasmExecAndWorker}}`
192+
: `(postMessage) => {${(commentPrefix + wasmExecAndWorker).replace(/\n/g, '\n ')}\n}`
190193
}
191194

192195
// Generate "npm/esbuild-wasm/lib/browser.*"
@@ -198,11 +201,10 @@ exports.buildWasmLib = async (esbuildPath) => {
198201
'--target=' + umdBrowserTarget,
199202
'--format=cjs',
200203
'--define:ESBUILD_VERSION=' + JSON.stringify(version),
201-
'--define:WEB_WORKER_SOURCE_CODE=' + JSON.stringify(wasmWorkerCode.umd),
202204
'--banner:js=' + umdPrefix,
203205
'--footer:js=' + umdSuffix,
204206
'--log-level=warning',
205-
].concat(minifyFlags), { cwd: repoDir }).toString()
207+
].concat(minifyFlags), { cwd: repoDir }).toString().replace('WEB_WORKER_FUNCTION', wasmWorkerCode.umd)
206208
fs.writeFileSync(path.join(libDir, minify ? 'browser.min.js' : 'browser.js'), browserCJS)
207209

208210
// Generate "npm/esbuild-wasm/esm/browser.min.js"
@@ -212,9 +214,8 @@ exports.buildWasmLib = async (esbuildPath) => {
212214
'--target=' + esmBrowserTarget,
213215
'--format=esm',
214216
'--define:ESBUILD_VERSION=' + JSON.stringify(version),
215-
'--define:WEB_WORKER_SOURCE_CODE=' + JSON.stringify(wasmWorkerCode.esm),
216217
'--log-level=warning',
217-
].concat(minifyFlags), { cwd: repoDir }).toString()
218+
].concat(minifyFlags), { cwd: repoDir }).toString().replace('WEB_WORKER_FUNCTION', wasmWorkerCode.esm)
218219
fs.writeFileSync(path.join(esmDir, minify ? 'browser.min.js' : 'browser.js'), browserESM)
219220
}
220221

0 commit comments

Comments
 (0)