diff --git a/packages/transformers/image/package.json b/packages/transformers/image/package.json index 987b4800ac6..e9cefdb7381 100644 --- a/packages/transformers/image/package.json +++ b/packages/transformers/image/package.json @@ -17,6 +17,7 @@ }, "dependencies": { "@parcel/plugin": "2.0.0-rc.0", + "@parcel/workers": "2.0.0-rc.0", "nullthrows": "^1.1.1" }, "devDependencies": { diff --git a/packages/transformers/image/src/ImageTransformer.js b/packages/transformers/image/src/ImageTransformer.js index be04989e578..eac4100cf3a 100644 --- a/packages/transformers/image/src/ImageTransformer.js +++ b/packages/transformers/image/src/ImageTransformer.js @@ -2,6 +2,8 @@ import {validateConfig} from './validateConfig'; import {Transformer} from '@parcel/plugin'; import nullthrows from 'nullthrows'; +import WorkerFarm from '@parcel/workers'; +import loadSharp from './loadSharp'; // from https://github.com/lovell/sharp/blob/df7b8ba73808fc494be413e88cfb621b6279218c/lib/output.js#L6-L17 const FORMATS = new Map([ @@ -16,7 +18,7 @@ const FORMATS = new Map([ ['heif', 'heif'], ]); -const SHARP_RANGE = '^0.29.1'; +let isSharpLoadedOnMainThread = false; export default (new Transformer({ async loadConfig({config}) { @@ -61,14 +63,28 @@ export default (new Transformer({ const outputOptions = config[format]; if (width || height || quality || targetFormat || outputOptions) { + // Sharp must be required from the main thread as well to prevent errors when workers exit + // See https://sharp.pixelplumbing.com/install#worker-threads and https://github.com/lovell/sharp/issues/2263 + if (WorkerFarm.isWorker() && !isSharpLoadedOnMainThread) { + let api = WorkerFarm.getWorkerApi(); + await api.callMaster({ + location: __dirname + '/loadSharp.js', + args: [ + options.packageManager, + asset.filePath, + options.shouldAutoInstall, + ], + }); + + isSharpLoadedOnMainThread = true; + } + let inputBuffer = await asset.getBuffer(); - let sharp = await options.packageManager.require( - 'sharp', + let sharp = await loadSharp( + options.packageManager, asset.filePath, - { - range: SHARP_RANGE, - shouldAutoInstall: options.shouldAutoInstall, - }, + options.shouldAutoInstall, + true, ); let imagePipeline = sharp(inputBuffer); diff --git a/packages/transformers/image/src/loadSharp.js b/packages/transformers/image/src/loadSharp.js new file mode 100644 index 00000000000..52e598cbf26 --- /dev/null +++ b/packages/transformers/image/src/loadSharp.js @@ -0,0 +1,23 @@ +// @flow +import type {PackageManager} from '@parcel/package-manager'; +import type {FilePath} from '@parcel/types'; + +const SHARP_RANGE = '^0.29.1'; + +// This is used to load sharp on the main thread, which prevents errors when worker threads exit +// See https://sharp.pixelplumbing.com/install#worker-threads and https://github.com/lovell/sharp/issues/2263 +module.exports = async ( + packageManager: PackageManager, + filePath: FilePath, + shouldAutoInstall: boolean, + shouldReturn: boolean, +): Promise => { + let sharp = await packageManager.require('sharp', filePath, { + range: SHARP_RANGE, + shouldAutoInstall: shouldAutoInstall, + }); + + if (shouldReturn) { + return sharp; + } +};