From 6482a64b91ac5ce51e7396836f0089e3d6003d35 Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Thu, 20 May 2021 13:57:11 -0400 Subject: [PATCH] fix(@angular-devkit/build-angular): ensure Sass worker implementation supports Node.js 12.14 The Worker constructor option for a transfer list is unfortunately not supported until Node.js 12.17. For Node.js versions prior to 12.17, a manual message post is now used to transfer the necessary initialization data to the Sass workers. (cherry picked from commit 4dc7cf952961183abcd201db6a5747a7b22e5953) --- .../build_angular/src/sass/sass-service.ts | 17 ++++++++-- .../build_angular/src/sass/worker.ts | 31 +++++++++++++++---- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/packages/angular_devkit/build_angular/src/sass/sass-service.ts b/packages/angular_devkit/build_angular/src/sass/sass-service.ts index 737b8e216b1c..d1336726983f 100644 --- a/packages/angular_devkit/build_angular/src/sass/sass-service.ts +++ b/packages/angular_devkit/build_angular/src/sass/sass-service.ts @@ -39,6 +39,15 @@ interface RenderResponseMessage { result?: Result; } +/** + * Workaround required for lack of new Worker transfer list support in Node.js prior to 12.17 + */ +let transferListWorkaround = false; +const version = process.versions.node.split('.').map((part) => Number(part)); +if (version[0] === 12 && version[1] < 17) { + transferListWorkaround = true; +} + /** * A Sass renderer implementation that provides an interface that can be used by Webpack's * `sass-loader`. The implementation uses a Worker thread to perform the Sass rendering @@ -126,10 +135,14 @@ export class SassWorkerImplementation { const workerPath = require.resolve('./worker'); const worker = new Worker(workerPath, { - workerData: { workerImporterPort, importerSignal }, - transferList: [workerImporterPort], + workerData: transferListWorkaround ? undefined : { workerImporterPort, importerSignal }, + transferList: transferListWorkaround ? undefined : [workerImporterPort], }); + if (transferListWorkaround) { + worker.postMessage({ init: true, workerImporterPort, importerSignal }, [workerImporterPort]); + } + worker.on('message', (response: RenderResponseMessage) => { const request = this.requests.get(response.id); if (!request) { diff --git a/packages/angular_devkit/build_angular/src/sass/worker.ts b/packages/angular_devkit/build_angular/src/sass/worker.ts index 137b9e323ebc..ee44149065e1 100644 --- a/packages/angular_devkit/build_angular/src/sass/worker.ts +++ b/packages/angular_devkit/build_angular/src/sass/worker.ts @@ -18,27 +18,46 @@ interface RenderRequestMessage { * importer on the main thread. */ id: number; + /** * The Sass options to provide to the `dart-sass` render function. */ options: Options; + /** * Indicates the request has a custom importer function on the main thread. */ hasImporter: boolean; + + /** + * Indicates this is not an init message. + */ + init: undefined; +} + +interface InitMessage { + init: true; + workerImporterPort: MessagePort; + importerSignal: Int32Array; } -if (!parentPort || !workerData) { +if (!parentPort) { throw new Error('Sass worker must be executed as a Worker.'); } // The importer variables are used to proxy import requests to the main thread -const { workerImporterPort, importerSignal } = workerData as { - workerImporterPort: MessagePort; - importerSignal: Int32Array; -}; +let { workerImporterPort, importerSignal } = (workerData || {}) as InitMessage; + +parentPort.on('message', (message: RenderRequestMessage | InitMessage) => { + // The init message is only needed to support Node.js < 12.17 and can be removed once support is dropped + if (message.init) { + workerImporterPort = message.workerImporterPort; + importerSignal = message.importerSignal; + + return; + } -parentPort.on('message', ({ id, hasImporter, options }: RenderRequestMessage) => { + const { id, hasImporter, options } = message; try { if (hasImporter) { // When a custom importer function is present, the importer request must be proxied