diff --git a/.changeset/twenty-boxes-know.md b/.changeset/twenty-boxes-know.md new file mode 100644 index 000000000000..7c8b5097cc8e --- /dev/null +++ b/.changeset/twenty-boxes-know.md @@ -0,0 +1,5 @@ +--- +'@astrojs/image': patch +--- + +Fix crash on Netlify Functions due to `import.meta.url` diff --git a/packages/integrations/image/src/vendor/squoosh/avif/avif_node_dec.ts b/packages/integrations/image/src/vendor/squoosh/avif/avif_node_dec.ts index 8f2bbbbf8c15..b432feefda28 100644 --- a/packages/integrations/image/src/vendor/squoosh/avif/avif_node_dec.ts +++ b/packages/integrations/image/src/vendor/squoosh/avif/avif_node_dec.ts @@ -1,8 +1,8 @@ /* eslint-disable */ // @ts-nocheck import { createRequire } from 'module'; -import { dirname } from '../emscripten-utils.js'; -const require = createRequire(import.meta.url); +import { dirname, getModuleURL } from '../emscripten-utils.js'; +const require = createRequire(getModuleURL(import.meta.url)); var Module = (function () { return function (Module) { @@ -43,7 +43,7 @@ var Module = (function () { if (ENVIRONMENT_IS_WORKER) { scriptDirectory = require('path').dirname(scriptDirectory) + '/' } else { - scriptDirectory = dirname(import.meta.url) + '/' + scriptDirectory = dirname(getModuleURL(import.meta.url)) + '/' } read_ = function shell_read(filename, binary) { if (!nodeFS) nodeFS = require('fs') diff --git a/packages/integrations/image/src/vendor/squoosh/avif/avif_node_enc.ts b/packages/integrations/image/src/vendor/squoosh/avif/avif_node_enc.ts index cc1a3df22efd..933d1a7c90b7 100644 --- a/packages/integrations/image/src/vendor/squoosh/avif/avif_node_enc.ts +++ b/packages/integrations/image/src/vendor/squoosh/avif/avif_node_enc.ts @@ -1,8 +1,8 @@ /* eslint-disable */ // @ts-nocheck import { createRequire } from 'module'; -import { dirname } from '../emscripten-utils.js'; -const require = createRequire(import.meta.url); +import { dirname, getModuleURL } from '../emscripten-utils.js'; +const require = createRequire(getModuleURL(import.meta.url)); var Module = (function () { return function (Module) { @@ -43,7 +43,7 @@ var Module = (function () { if (ENVIRONMENT_IS_WORKER) { scriptDirectory = require('path').dirname(scriptDirectory) + '/' } else { - scriptDirectory = dirname(import.meta.url) + '/' + scriptDirectory = dirname(getModuleURL(import.meta.url)) + '/' } read_ = function shell_read(filename, binary) { if (!nodeFS) nodeFS = require('fs') diff --git a/packages/integrations/image/src/vendor/squoosh/codecs.ts b/packages/integrations/image/src/vendor/squoosh/codecs.ts index 48deae27ac75..85ccb51a77d9 100644 --- a/packages/integrations/image/src/vendor/squoosh/codecs.ts +++ b/packages/integrations/image/src/vendor/squoosh/codecs.ts @@ -1,5 +1,5 @@ import { promises as fsp } from 'node:fs' -import { instantiateEmscriptenWasm, pathify } from './emscripten-utils.js' +import { getModuleURL, instantiateEmscriptenWasm, pathify } from './emscripten-utils.js' interface DecodeModule extends EmscriptenWasm.Module { decode: (data: Uint8Array) => ImageData @@ -37,50 +37,50 @@ export interface RotateOptions { import type { MozJPEGModule as MozJPEGEncodeModule } from './mozjpeg/mozjpeg_enc' // @ts-ignore import mozEnc from './mozjpeg/mozjpeg_node_enc.js' -const mozEncWasm = new URL('./mozjpeg/mozjpeg_node_enc.wasm', import.meta.url) +const mozEncWasm = new URL('./mozjpeg/mozjpeg_node_enc.wasm', getModuleURL(import.meta.url)) // @ts-ignore import mozDec from './mozjpeg/mozjpeg_node_dec.js' -const mozDecWasm = new URL('./mozjpeg/mozjpeg_node_dec.wasm', import.meta.url) +const mozDecWasm = new URL('./mozjpeg/mozjpeg_node_dec.wasm', getModuleURL(import.meta.url)) // WebP import type { WebPModule as WebPEncodeModule } from './webp/webp_enc' // @ts-ignore import webpEnc from './webp/webp_node_enc.js' -const webpEncWasm = new URL('./webp/webp_node_enc.wasm', import.meta.url) +const webpEncWasm = new URL('./webp/webp_node_enc.wasm', getModuleURL(import.meta.url)) // @ts-ignore import webpDec from './webp/webp_node_dec.js' -const webpDecWasm = new URL('./webp/webp_node_dec.wasm', import.meta.url) +const webpDecWasm = new URL('./webp/webp_node_dec.wasm', getModuleURL(import.meta.url)) // AVIF import type { AVIFModule as AVIFEncodeModule } from './avif/avif_enc' // @ts-ignore import avifEnc from './avif/avif_node_enc.js' -const avifEncWasm = new URL('./avif/avif_node_enc.wasm', import.meta.url) +const avifEncWasm = new URL('./avif/avif_node_enc.wasm', getModuleURL(import.meta.url)) // @ts-ignore import avifDec from './avif/avif_node_dec.js' -const avifDecWasm = new URL('./avif/avif_node_dec.wasm', import.meta.url) +const avifDecWasm = new URL('./avif/avif_node_dec.wasm', getModuleURL(import.meta.url)) // PNG // @ts-ignore import * as pngEncDec from './png/squoosh_png.js' -const pngEncDecWasm = new URL('./png/squoosh_png_bg.wasm', import.meta.url) +const pngEncDecWasm = new URL('./png/squoosh_png_bg.wasm', getModuleURL(import.meta.url)) const pngEncDecInit = () => pngEncDec.default(fsp.readFile(pathify(pngEncDecWasm.toString()))) // OxiPNG // @ts-ignore import * as oxipng from './png/squoosh_oxipng.js' -const oxipngWasm = new URL('./png/squoosh_oxipng_bg.wasm', import.meta.url) +const oxipngWasm = new URL('./png/squoosh_oxipng_bg.wasm', getModuleURL(import.meta.url)) const oxipngInit = () => oxipng.default(fsp.readFile(pathify(oxipngWasm.toString()))) // Resize // @ts-ignore import * as resize from './resize/squoosh_resize.js' -const resizeWasm = new URL('./resize/squoosh_resize_bg.wasm', import.meta.url) +const resizeWasm = new URL('./resize/squoosh_resize_bg.wasm', getModuleURL(import.meta.url)) const resizeInit = () => resize.default(fsp.readFile(pathify(resizeWasm.toString()))) // rotate -const rotateWasm = new URL('./rotate/rotate.wasm', import.meta.url) +const rotateWasm = new URL('./rotate/rotate.wasm', getModuleURL(import.meta.url)) // Our decoders currently rely on a `ImageData` global. import ImageData from './image_data.js' diff --git a/packages/integrations/image/src/vendor/squoosh/emscripten-utils.ts b/packages/integrations/image/src/vendor/squoosh/emscripten-utils.ts index 2e335fd2decb..e661fae9253d 100644 --- a/packages/integrations/image/src/vendor/squoosh/emscripten-utils.ts +++ b/packages/integrations/image/src/vendor/squoosh/emscripten-utils.ts @@ -1,5 +1,5 @@ -// -import { fileURLToPath } from 'node:url' +// +import { fileURLToPath, pathToFileURL } from 'node:url' export function pathify(path: string): string { if (path.startsWith('file://')) { @@ -29,3 +29,16 @@ export function instantiateEmscriptenWasm( export function dirname(url: string) { return url.substring(0, url.lastIndexOf('/')) } + +/** + * On certain serverless hosts, our ESM bundle is transpiled to CJS before being run, which means + * import.meta.url is undefined, so we'll fall back to __dirname in those cases + * We should be able to remove this once https://github.com/netlify/zip-it-and-ship-it/issues/750 is fixed + */ +export function getModuleURL(url: string | undefined): string { + if (!url) { + return pathToFileURL(__dirname).toString(); + } + + return url +} diff --git a/packages/integrations/image/src/vendor/squoosh/image-pool.ts b/packages/integrations/image/src/vendor/squoosh/image-pool.ts index e19215397c89..4275aa8ebc9a 100644 --- a/packages/integrations/image/src/vendor/squoosh/image-pool.ts +++ b/packages/integrations/image/src/vendor/squoosh/image-pool.ts @@ -4,137 +4,147 @@ import { fileURLToPath } from 'url'; import type { OutputFormat } from '../../loaders/index.js'; import execOnce from '../../utils/execOnce.js'; import WorkerPool from '../../utils/workerPool.js'; +import { getModuleURL } from './emscripten-utils.js'; import type { Operation } from './image.js'; import * as impl from './impl.js'; -const getWorker = execOnce( - () => { - return new WorkerPool( - // There will be at most 7 workers needed since each worker will take - // at least 1 operation type. - Math.max(1, Math.min(cpus().length - 1, 7)), - fileURLToPath(import.meta.url) - ); - } -) +const getWorker = execOnce(() => { + return new WorkerPool( + // There will be at most 7 workers needed since each worker will take + // at least 1 operation type. + Math.max(1, Math.min(cpus().length - 1, 7)), + fileURLToPath(getModuleURL(import.meta.url)) + ); +}); type DecodeParams = { - operation: 'decode', - buffer: Buffer + operation: 'decode'; + buffer: Buffer; }; type ResizeParams = { - operation: 'resize', - imageData: ImageData, - height?: number, - width?: number + operation: 'resize'; + imageData: ImageData; + height?: number; + width?: number; }; type RotateParams = { - operation: 'rotate', - imageData: ImageData, - numRotations: number + operation: 'rotate'; + imageData: ImageData; + numRotations: number; }; type EncodeAvifParams = { - operation: 'encodeavif', - imageData: ImageData, - quality: number -} + operation: 'encodeavif'; + imageData: ImageData; + quality: number; +}; type EncodeJpegParams = { - operation: 'encodejpeg', - imageData: ImageData, - quality: number -} + operation: 'encodejpeg'; + imageData: ImageData; + quality: number; +}; type EncodePngParams = { - operation: 'encodepng', - imageData: ImageData -} + operation: 'encodepng'; + imageData: ImageData; +}; type EncodeWebpParams = { - operation: 'encodewebp', - imageData: ImageData, - quality: number -} -type JobMessage = DecodeParams | ResizeParams | RotateParams | EncodeAvifParams | EncodeJpegParams | EncodePngParams | EncodeWebpParams + operation: 'encodewebp'; + imageData: ImageData; + quality: number; +}; +type JobMessage = + | DecodeParams + | ResizeParams + | RotateParams + | EncodeAvifParams + | EncodeJpegParams + | EncodePngParams + | EncodeWebpParams; function handleJob(params: JobMessage) { - switch (params.operation) { - case 'decode': - return impl.decodeBuffer(params.buffer) + switch (params.operation) { + case 'decode': + return impl.decodeBuffer(params.buffer); case 'resize': - return impl.resize({ image: params.imageData as any, width: params.width, height: params.height }) + return impl.resize({ + image: params.imageData as any, + width: params.width, + height: params.height, + }); case 'rotate': return impl.rotate(params.imageData as any, params.numRotations); case 'encodeavif': - return impl.encodeAvif(params.imageData as any, { quality: params.quality }) + return impl.encodeAvif(params.imageData as any, { quality: params.quality }); case 'encodejpeg': - return impl.encodeJpeg(params.imageData as any, { quality: params.quality }) + return impl.encodeJpeg(params.imageData as any, { quality: params.quality }); case 'encodepng': - return impl.encodePng(params.imageData as any) + return impl.encodePng(params.imageData as any); case 'encodewebp': - return impl.encodeWebp(params.imageData as any, { quality: params.quality }) - default: - throw Error(`Invalid job "${(params as any).operation}"`); - } + return impl.encodeWebp(params.imageData as any, { quality: params.quality }); + default: + throw Error(`Invalid job "${(params as any).operation}"`); + } } export async function processBuffer( - buffer: Buffer, - operations: Operation[], - encoding: OutputFormat, - quality?: number + buffer: Buffer, + operations: Operation[], + encoding: OutputFormat, + quality?: number ): Promise { // @ts-ignore - const worker = await getWorker() + const worker = await getWorker(); - let imageData = await worker.dispatchJob({ + let imageData = await worker.dispatchJob({ operation: 'decode', buffer, - }) - for (const operation of operations) { - if (operation.type === 'rotate') { + }); + for (const operation of operations) { + if (operation.type === 'rotate') { imageData = await worker.dispatchJob({ operation: 'rotate', imageData, - numRotations: operation.numRotations + numRotations: operation.numRotations, }); - } else if (operation.type === 'resize') { + } else if (operation.type === 'resize') { imageData = await worker.dispatchJob({ operation: 'resize', imageData, height: operation.height, - width: operation.width - }) - } - } + width: operation.width, + }); + } + } switch (encoding) { case 'avif': - return await worker.dispatchJob({ + return (await worker.dispatchJob({ operation: 'encodeavif', imageData, quality, - }) as Uint8Array; + })) as Uint8Array; case 'jpeg': case 'jpg': - return await worker.dispatchJob({ + return (await worker.dispatchJob({ operation: 'encodejpeg', imageData, quality, - }) as Uint8Array; + })) as Uint8Array; case 'png': - return await worker.dispatchJob({ + return (await worker.dispatchJob({ operation: 'encodepng', imageData, - }) as Uint8Array; + })) as Uint8Array; case 'webp': - return await worker.dispatchJob({ + return (await worker.dispatchJob({ operation: 'encodewebp', imageData, quality, - }) as Uint8Array; + })) as Uint8Array; default: - throw Error(`Unsupported encoding format`) + throw Error(`Unsupported encoding format`); } } if (!isMainThread) { - WorkerPool.useThisThreadAsWorker(handleJob); + WorkerPool.useThisThreadAsWorker(handleJob); } diff --git a/packages/integrations/image/src/vendor/squoosh/mozjpeg/mozjpeg_node_dec.ts b/packages/integrations/image/src/vendor/squoosh/mozjpeg/mozjpeg_node_dec.ts index 7e8e62c3bfcb..720508a42e0b 100644 --- a/packages/integrations/image/src/vendor/squoosh/mozjpeg/mozjpeg_node_dec.ts +++ b/packages/integrations/image/src/vendor/squoosh/mozjpeg/mozjpeg_node_dec.ts @@ -1,8 +1,8 @@ /* eslint-disable */ // @ts-nocheck import { createRequire } from 'module'; -import { dirname } from '../emscripten-utils.js'; -const require = createRequire(import.meta.url); +import { dirname, getModuleURL } from '../emscripten-utils.js'; +const require = createRequire(getModuleURL(import.meta.url)); var Module = (function () { return function (Module) { @@ -43,7 +43,7 @@ var Module = (function () { if (ENVIRONMENT_IS_WORKER) { scriptDirectory = require('path').dirname(scriptDirectory) + '/' } else { - scriptDirectory = dirname(import.meta.url) + '/' + scriptDirectory = dirname(getModuleURL(import.meta.url)) + '/' } read_ = function shell_read(filename, binary) { if (!nodeFS) nodeFS = require('fs') diff --git a/packages/integrations/image/src/vendor/squoosh/mozjpeg/mozjpeg_node_enc.ts b/packages/integrations/image/src/vendor/squoosh/mozjpeg/mozjpeg_node_enc.ts index 82f274648256..b5fee7365cca 100644 --- a/packages/integrations/image/src/vendor/squoosh/mozjpeg/mozjpeg_node_enc.ts +++ b/packages/integrations/image/src/vendor/squoosh/mozjpeg/mozjpeg_node_enc.ts @@ -1,8 +1,8 @@ /* eslint-disable */ // @ts-nocheck import { createRequire } from 'module'; -import { dirname } from '../emscripten-utils.js'; -const require = createRequire(import.meta.url); +import { dirname, getModuleURL } from '../emscripten-utils.js'; +const require = createRequire(getModuleURL(import.meta.url)); var Module = (function () { return function (Module) { @@ -43,7 +43,7 @@ var Module = (function () { if (ENVIRONMENT_IS_WORKER) { scriptDirectory = require('path').dirname(scriptDirectory) + '/' } else { - scriptDirectory = dirname(import.meta.url) + '/' + scriptDirectory = dirname(getModuleURL(import.meta.url)) + '/' } read_ = function shell_read(filename, binary) { if (!nodeFS) nodeFS = require('fs') diff --git a/packages/integrations/image/src/vendor/squoosh/webp/webp_node_dec.ts b/packages/integrations/image/src/vendor/squoosh/webp/webp_node_dec.ts index 12c9f39216a5..cdb1a9837380 100644 --- a/packages/integrations/image/src/vendor/squoosh/webp/webp_node_dec.ts +++ b/packages/integrations/image/src/vendor/squoosh/webp/webp_node_dec.ts @@ -1,8 +1,8 @@ /* eslint-disable */ // @ts-nocheck import { createRequire } from 'module'; -import { dirname } from '../emscripten-utils.js'; -const require = createRequire(import.meta.url); +import { dirname, getModuleURL } from '../emscripten-utils.js'; +const require = createRequire(getModuleURL(import.meta.url)); var Module = (function () { return function (Module) { @@ -43,7 +43,7 @@ var Module = (function () { if (ENVIRONMENT_IS_WORKER) { scriptDirectory = require('path').dirname(scriptDirectory) + '/' } else { - scriptDirectory = dirname(import.meta.url) + '/' + scriptDirectory = dirname(getModuleURL(import.meta.url)) + '/' } read_ = function shell_read(filename, binary) { if (!nodeFS) nodeFS = require('fs') diff --git a/packages/integrations/image/src/vendor/squoosh/webp/webp_node_enc.ts b/packages/integrations/image/src/vendor/squoosh/webp/webp_node_enc.ts index aa4a139eb465..d1c350d2b952 100644 --- a/packages/integrations/image/src/vendor/squoosh/webp/webp_node_enc.ts +++ b/packages/integrations/image/src/vendor/squoosh/webp/webp_node_enc.ts @@ -1,8 +1,8 @@ /* eslint-disable */ // @ts-nocheck import { createRequire } from 'module'; -import { dirname } from '../emscripten-utils.js'; -const require = createRequire(import.meta.url); +import { dirname, getModuleURL } from '../emscripten-utils.js'; +const require = createRequire(getModuleURL(import.meta.url)); var Module = (function () { return function (Module) { @@ -43,7 +43,7 @@ var Module = (function () { if (ENVIRONMENT_IS_WORKER) { scriptDirectory = require('path').dirname(scriptDirectory) + '/' } else { - scriptDirectory = dirname(import.meta.url) + '/' + scriptDirectory = dirname(getModuleURL(import.meta.url)) + '/' } read_ = function shell_read(filename, binary) { if (!nodeFS) nodeFS = require('fs')