forked from evanw/esbuild
-
Notifications
You must be signed in to change notification settings - Fork 1
/
esbuild
executable file
·133 lines (122 loc) · 5.08 KB
/
esbuild
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#!/usr/bin/env node
// Forward to the automatically-generated WebAssembly loader from the Go compiler
const module_ = require('module');
const crypto = require('crypto');
const path = require('path');
const zlib = require('zlib');
const fs = require('fs');
const os = require('os');
const wasm_exec_node = path.join(__dirname, '..', 'wasm_exec_node.js');
const esbuild_wasm = path.join(__dirname, '..', 'esbuild.wasm');
const code = fs.readFileSync(wasm_exec_node, 'utf8');
const wrapper = new Function('require', 'WebAssembly', code);
function instantiate(bytes, importObject) {
// Using this API causes "./esbuild --version" to run around 1 second faster
// than using the "WebAssembly.instantiate()" API when run in node (v12.16.2)
const module = new WebAssembly.Module(bytes);
const instance = new WebAssembly.Instance(module, importObject);
return Promise.resolve({ instance, module });
}
// Node has an unfortunate bug where the node process is unnecessarily kept open while a
// WebAssembly module is being optimized: https://github.com/nodejs/node/issues/36616.
// This means cases where running "esbuild" should take a few milliseconds can end up
// taking many seconds instead. To work around this bug, it is possible to force node to
// exit by calling the operating system's exit function. That's what this code does.
process.on('exit', code => {
// If it's a non-zero exit code, we can just kill our own process to stop. This will
// preserve the fact that there is a non-zero exit code although the exit code will
// be different. We cannot use this if the exit code is supposed to be zero.
if (code !== 0) {
try {
process.kill(process.pid, 'SIGINT');
} catch (e) {
}
return;
}
// Otherwise if the exit code is zero, try to fall back to a binary N-API module that
// calls the operating system's "exit(0)" function.
const nativeModule = `${process.platform}-${os.arch()}-${os.endianness()}.node`;
const base64 = require('../exit0')[nativeModule];
if (base64) {
try {
const data = zlib.inflateRawSync(Buffer.from(base64, 'base64'));
const hash = crypto.createHash('sha256').update(base64).digest().toString('hex').slice(0, 16);
const tempFile = path.join(os.tmpdir(), `${hash}-${nativeModule}`);
try {
if (fs.readFileSync(tempFile).equals(data)) {
require(tempFile);
}
} finally {
fs.writeFileSync(tempFile, data);
require(tempFile);
}
} catch (e) {
}
}
});
// Node has another bug where using "fs.read" to read from stdin reads
// everything successfully and then throws an error, but only on Windows. Go's
// WebAssembly support uses "fs.read" so it hits this problem. This is a patch
// to try to work around the bug in node. This bug has been reported to node
// at least twice in https://github.com/nodejs/node/issues/35997 and in
// https://github.com/nodejs/node/issues/19831. This issue has also been
// reported to the Go project: https://github.com/golang/go/issues/43913.
const read = fs.read;
fs.read = function () {
const callback = arguments[5];
arguments[5] = function (err, count) {
if (count === 0 && err && err.code === 'EOF') {
arguments[0] = null;
}
return callback.apply(this, arguments);
};
return read.apply(this, arguments);
};
// Hack around a Unicode bug in node: https://github.com/nodejs/node/issues/24550.
// See this for the matching Go issue: https://github.com/golang/go/issues/43917.
const write = fs.write;
fs.write = function (fd, buf, offset, length, position, callback) {
if (offset === 0 && length === buf.length && position === null) {
if (fd === process.stdout.fd) {
try {
process.stdout.write(buf, err => err ? callback(err, 0, null) : callback(null, length, buf));
} catch (err) {
callback(err, 0, null);
}
return;
}
if (fd === process.stderr.fd) {
try {
process.stderr.write(buf, err => err ? callback(err, 0, null) : callback(null, length, buf));
} catch (err) {
callback(err, 0, null);
}
return;
}
}
return write.apply(this, arguments);
};
const writeSync = fs.writeSync;
fs.writeSync = function (fd, buf) {
if (fd === process.stdout.fd) return process.stdout.write(buf), buf.length;
if (fd === process.stderr.fd) return process.stderr.write(buf), buf.length;
return writeSync.apply(this, arguments);
};
// WASM code generated with Go 1.17.2+ will crash when run in a situation with
// many environment variables: https://github.com/golang/go/issues/49011. An
// example of this situation is running a Go-compiled WASM executable in GitHub
// Actions. Work around this by filtering node's copy of environment variables
// down to only include the environment variables that esbuild currently uses.
const esbuildUsedEnvVars = [
'NO_COLOR',
'NODE_PATH',
'npm_config_user_agent',
'WT_SESSION',
]
for (let key in process.env) {
if (esbuildUsedEnvVars.indexOf(key) < 0) {
delete process.env[key]
}
}
process.argv.splice(2, 0, esbuild_wasm);
wrapper(module_.createRequire(wasm_exec_node), Object.assign(Object.create(WebAssembly), { instantiate }));