Skip to content

Commit 2151135

Browse files
committed
fix #2129: handle corrupted binary executable
1 parent 36c070e commit 2151135

File tree

3 files changed

+22
-12
lines changed

3 files changed

+22
-12
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+
* Better handle errors where the esbuild binary executable is corrupted or missing ([#2129](https://github.com/evanw/esbuild/issues/2129))
6+
7+
If the esbuild binary executable is corrupted or missing, previously there was one situation where esbuild's JavaScript API could hang instead of generating an error. This release changes esbuild's library code to generate an error instead in this case.
8+
39
## 0.14.28
410

511
* Add support for some new CSS rules ([#2115](https://github.com/evanw/esbuild/issues/2115), [#2116](https://github.com/evanw/esbuild/issues/2116), [#2117](https://github.com/evanw/esbuild/issues/2117))

lib/npm/node.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ let ensureServiceIsRunning = (): Service => {
257257
writeToStdin(bytes) {
258258
child.stdin.write(bytes, err => {
259259
// Assume the service was stopped if we get an error writing to stdin
260-
if (err) afterClose();
260+
if (err) afterClose(err);
261261
});
262262
},
263263
readFileSync: fs.readFileSync,
@@ -269,6 +269,9 @@ let ensureServiceIsRunning = (): Service => {
269269
// Assume the service was stopped if we get an error writing to stdin
270270
child.stdin.on('error', afterClose);
271271

272+
// Propagate errors about failure to run the executable itself
273+
child.on('error', afterClose);
274+
272275
const stdin: typeof child.stdin & { unref?(): void } = child.stdin;
273276
const stdout: typeof child.stdout & { unref?(): void } = child.stdout;
274277

@@ -377,7 +380,7 @@ let runServiceSync = (callback: (service: common.StreamService) => void): void =
377380
maxBuffer: +process.env.ESBUILD_MAX_BUFFER! || 16 * 1024 * 1024,
378381
});
379382
readFromStdout(stdout);
380-
afterClose();
383+
afterClose(null);
381384
};
382385

383386
let randomFileName = () => {

lib/shared/common.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ export interface StreamIn {
411411

412412
export interface StreamOut {
413413
readFromStdout: (data: Uint8Array) => void;
414-
afterClose: () => void;
414+
afterClose: (error: Error | null) => void;
415415
service: StreamService;
416416
}
417417

@@ -481,7 +481,7 @@ export function createChannel(streamIn: StreamIn): StreamOut {
481481
let pluginCallbacks = new Map<number, PluginCallback>();
482482
let watchCallbacks = new Map<number, WatchCallback>();
483483
let serveCallbacks = new Map<number, ServeCallbacks>();
484-
let isClosed = false;
484+
let closeData: { reason: string } | null = null;
485485
let nextRequestID = 0;
486486
let nextBuildKey = 0;
487487

@@ -516,20 +516,21 @@ export function createChannel(streamIn: StreamIn): StreamOut {
516516
}
517517
};
518518

519-
let afterClose = () => {
519+
let afterClose = (error: Error | null) => {
520520
// When the process is closed, fail all pending requests
521-
isClosed = true;
521+
closeData = { reason: error ? ': ' + (error.message || error) : '' };
522+
const text = 'The service was stopped' + closeData.reason;
522523
for (let callback of responseCallbacks.values()) {
523-
callback('The service was stopped', null);
524+
callback(text, null);
524525
}
525526
responseCallbacks.clear();
526527
for (let callbacks of serveCallbacks.values()) {
527-
callbacks.onWait('The service was stopped');
528+
callbacks.onWait(text);
528529
}
529530
serveCallbacks.clear();
530531
for (let callback of watchCallbacks.values()) {
531532
try {
532-
callback(new Error('The service was stopped'), null);
533+
callback(new Error(text), null);
533534
} catch (e) {
534535
console.error(e)
535536
}
@@ -538,7 +539,7 @@ export function createChannel(streamIn: StreamIn): StreamOut {
538539
};
539540

540541
let sendRequest = <Req, Res>(refs: Refs | null, value: Req, callback: (error: string | null, response: Res | null) => void): void => {
541-
if (isClosed) return callback('The service is no longer running', null);
542+
if (closeData) return callback('The service is no longer running' + closeData.reason, null);
542543
let id = nextRequestID++;
543544
responseCallbacks.set(id, (error, response) => {
544545
try {
@@ -552,7 +553,7 @@ export function createChannel(streamIn: StreamIn): StreamOut {
552553
};
553554

554555
let sendResponse = (id: number, value: protocol.Value): void => {
555-
if (isClosed) throw new Error('The service is no longer running');
556+
if (closeData) throw new Error('The service is no longer running' + closeData.reason);
556557
streamIn.writeToStdin(protocol.encodePacket({ id, isRequest: false, value }));
557558
};
558559

@@ -1195,7 +1196,7 @@ export function createChannel(streamIn: StreamIn): StreamOut {
11951196
if (!rebuild) {
11961197
let isDisposed = false;
11971198
(rebuild as any) = () => new Promise<types.BuildResult>((resolve, reject) => {
1198-
if (isDisposed || isClosed) throw new Error('Cannot rebuild');
1199+
if (isDisposed || closeData) throw new Error('Cannot rebuild');
11991200
sendRequest<protocol.RebuildRequest, protocol.BuildResponse>(refs, { command: 'rebuild', key },
12001201
(error2, response2) => {
12011202
if (error2) {

0 commit comments

Comments
 (0)