diff --git a/tools/isobuild/compiler-plugin.js b/tools/isobuild/compiler-plugin.js index 0b2199518c..cbdaa2431a 100644 --- a/tools/isobuild/compiler-plugin.js +++ b/tools/isobuild/compiler-plugin.js @@ -884,12 +884,29 @@ class OutputResource { } else if (this._lazyFinalizer) { const finalize = this._lazyFinalizer; this._lazyFinalizer = null; - (this._finalizerPromise = new Promise( - resolve => resolve(finalize()) - ).then(result => { - Object.assign(this._initialOptions, result); - this._finalizerPromise = null; - })).await(); + (this._finalizerPromise = + // It's important to initialize this._finalizerPromise to the new + // Promise before calling finalize(), so there's no possibility of + // finalize() triggering code that reenters this function before we + // have the final version of this._finalizerPromise. If this code + // used `new Promise(resolve => resolve(finalize()))` instead of + // `Promise.resolve().then(finalize)`, the finalize() call would + // begin before this._finalizerPromise was fully initialized. + Promise.resolve().then(finalize).then(result => { + if (result) { + Object.assign(this._initialOptions, result); + } else if (this._errors.length === 0) { + // In case the finalize() call failed without reporting any + // errors, create at least one generic error that can be + // reported when reportPendingErrors is called. + const error = new Error("lazyFinalizer failed"); + error.info = { resource: this, finalize } + this._errors.push(error); + } + // The this._finalizerPromise object only survives for the + // duration of the initial finalization. + this._finalizerPromise = null; + })).await(); } }