0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-02-24 22:46:02 -05:00

Fix image integration crash on Netlify Functions due to import.meta.url (#5888)

* fix(image): Fix immediate crash on Netlify functions due to `import.meta.url`

* chore: changeset
This commit is contained in:
Erika 2023-01-19 14:04:15 +01:00 committed by GitHub
parent ce5c5dbd46
commit 35b26f377f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 130 additions and 102 deletions

View file

@ -0,0 +1,5 @@
---
'@astrojs/image': patch
---
Fix crash on Netlify Functions due to `import.meta.url`

View file

@ -1,8 +1,8 @@
/* eslint-disable */ /* eslint-disable */
// @ts-nocheck // @ts-nocheck
import { createRequire } from 'module'; import { createRequire } from 'module';
import { dirname } from '../emscripten-utils.js'; import { dirname, getModuleURL } from '../emscripten-utils.js';
const require = createRequire(import.meta.url); const require = createRequire(getModuleURL(import.meta.url));
var Module = (function () { var Module = (function () {
return function (Module) { return function (Module) {
@ -43,7 +43,7 @@ var Module = (function () {
if (ENVIRONMENT_IS_WORKER) { if (ENVIRONMENT_IS_WORKER) {
scriptDirectory = require('path').dirname(scriptDirectory) + '/' scriptDirectory = require('path').dirname(scriptDirectory) + '/'
} else { } else {
scriptDirectory = dirname(import.meta.url) + '/' scriptDirectory = dirname(getModuleURL(import.meta.url)) + '/'
} }
read_ = function shell_read(filename, binary) { read_ = function shell_read(filename, binary) {
if (!nodeFS) nodeFS = require('fs') if (!nodeFS) nodeFS = require('fs')

View file

@ -1,8 +1,8 @@
/* eslint-disable */ /* eslint-disable */
// @ts-nocheck // @ts-nocheck
import { createRequire } from 'module'; import { createRequire } from 'module';
import { dirname } from '../emscripten-utils.js'; import { dirname, getModuleURL } from '../emscripten-utils.js';
const require = createRequire(import.meta.url); const require = createRequire(getModuleURL(import.meta.url));
var Module = (function () { var Module = (function () {
return function (Module) { return function (Module) {
@ -43,7 +43,7 @@ var Module = (function () {
if (ENVIRONMENT_IS_WORKER) { if (ENVIRONMENT_IS_WORKER) {
scriptDirectory = require('path').dirname(scriptDirectory) + '/' scriptDirectory = require('path').dirname(scriptDirectory) + '/'
} else { } else {
scriptDirectory = dirname(import.meta.url) + '/' scriptDirectory = dirname(getModuleURL(import.meta.url)) + '/'
} }
read_ = function shell_read(filename, binary) { read_ = function shell_read(filename, binary) {
if (!nodeFS) nodeFS = require('fs') if (!nodeFS) nodeFS = require('fs')

View file

@ -1,5 +1,5 @@
import { promises as fsp } from 'node:fs' 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 { interface DecodeModule extends EmscriptenWasm.Module {
decode: (data: Uint8Array) => ImageData decode: (data: Uint8Array) => ImageData
@ -37,50 +37,50 @@ export interface RotateOptions {
import type { MozJPEGModule as MozJPEGEncodeModule } from './mozjpeg/mozjpeg_enc' import type { MozJPEGModule as MozJPEGEncodeModule } from './mozjpeg/mozjpeg_enc'
// @ts-ignore // @ts-ignore
import mozEnc from './mozjpeg/mozjpeg_node_enc.js' 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 // @ts-ignore
import mozDec from './mozjpeg/mozjpeg_node_dec.js' 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 // WebP
import type { WebPModule as WebPEncodeModule } from './webp/webp_enc' import type { WebPModule as WebPEncodeModule } from './webp/webp_enc'
// @ts-ignore // @ts-ignore
import webpEnc from './webp/webp_node_enc.js' 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 // @ts-ignore
import webpDec from './webp/webp_node_dec.js' 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 // AVIF
import type { AVIFModule as AVIFEncodeModule } from './avif/avif_enc' import type { AVIFModule as AVIFEncodeModule } from './avif/avif_enc'
// @ts-ignore // @ts-ignore
import avifEnc from './avif/avif_node_enc.js' 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 // @ts-ignore
import avifDec from './avif/avif_node_dec.js' 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 // PNG
// @ts-ignore // @ts-ignore
import * as pngEncDec from './png/squoosh_png.js' 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 = () => const pngEncDecInit = () =>
pngEncDec.default(fsp.readFile(pathify(pngEncDecWasm.toString()))) pngEncDec.default(fsp.readFile(pathify(pngEncDecWasm.toString())))
// OxiPNG // OxiPNG
// @ts-ignore // @ts-ignore
import * as oxipng from './png/squoosh_oxipng.js' 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()))) const oxipngInit = () => oxipng.default(fsp.readFile(pathify(oxipngWasm.toString())))
// Resize // Resize
// @ts-ignore // @ts-ignore
import * as resize from './resize/squoosh_resize.js' 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()))) const resizeInit = () => resize.default(fsp.readFile(pathify(resizeWasm.toString())))
// rotate // 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. // Our decoders currently rely on a `ImageData` global.
import ImageData from './image_data.js' import ImageData from './image_data.js'

View file

@ -1,5 +1,5 @@
// //
import { fileURLToPath } from 'node:url' import { fileURLToPath, pathToFileURL } from 'node:url'
export function pathify(path: string): string { export function pathify(path: string): string {
if (path.startsWith('file://')) { if (path.startsWith('file://')) {
@ -29,3 +29,16 @@ export function instantiateEmscriptenWasm<T extends EmscriptenWasm.Module>(
export function dirname(url: string) { export function dirname(url: string) {
return url.substring(0, url.lastIndexOf('/')) 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
}

View file

@ -4,72 +4,82 @@ import { fileURLToPath } from 'url';
import type { OutputFormat } from '../../loaders/index.js'; import type { OutputFormat } from '../../loaders/index.js';
import execOnce from '../../utils/execOnce.js'; import execOnce from '../../utils/execOnce.js';
import WorkerPool from '../../utils/workerPool.js'; import WorkerPool from '../../utils/workerPool.js';
import { getModuleURL } from './emscripten-utils.js';
import type { Operation } from './image.js'; import type { Operation } from './image.js';
import * as impl from './impl.js'; import * as impl from './impl.js';
const getWorker = execOnce( const getWorker = execOnce(() => {
() => {
return new WorkerPool( return new WorkerPool(
// There will be at most 7 workers needed since each worker will take // There will be at most 7 workers needed since each worker will take
// at least 1 operation type. // at least 1 operation type.
Math.max(1, Math.min(cpus().length - 1, 7)), Math.max(1, Math.min(cpus().length - 1, 7)),
fileURLToPath(import.meta.url) fileURLToPath(getModuleURL(import.meta.url))
); );
} });
)
type DecodeParams = { type DecodeParams = {
operation: 'decode', operation: 'decode';
buffer: Buffer buffer: Buffer;
}; };
type ResizeParams = { type ResizeParams = {
operation: 'resize', operation: 'resize';
imageData: ImageData, imageData: ImageData;
height?: number, height?: number;
width?: number width?: number;
}; };
type RotateParams = { type RotateParams = {
operation: 'rotate', operation: 'rotate';
imageData: ImageData, imageData: ImageData;
numRotations: number numRotations: number;
}; };
type EncodeAvifParams = { type EncodeAvifParams = {
operation: 'encodeavif', operation: 'encodeavif';
imageData: ImageData, imageData: ImageData;
quality: number quality: number;
} };
type EncodeJpegParams = { type EncodeJpegParams = {
operation: 'encodejpeg', operation: 'encodejpeg';
imageData: ImageData, imageData: ImageData;
quality: number quality: number;
} };
type EncodePngParams = { type EncodePngParams = {
operation: 'encodepng', operation: 'encodepng';
imageData: ImageData imageData: ImageData;
} };
type EncodeWebpParams = { type EncodeWebpParams = {
operation: 'encodewebp', operation: 'encodewebp';
imageData: ImageData, imageData: ImageData;
quality: number quality: number;
} };
type JobMessage = DecodeParams | ResizeParams | RotateParams | EncodeAvifParams | EncodeJpegParams | EncodePngParams | EncodeWebpParams type JobMessage =
| DecodeParams
| ResizeParams
| RotateParams
| EncodeAvifParams
| EncodeJpegParams
| EncodePngParams
| EncodeWebpParams;
function handleJob(params: JobMessage) { function handleJob(params: JobMessage) {
switch (params.operation) { switch (params.operation) {
case 'decode': case 'decode':
return impl.decodeBuffer(params.buffer) return impl.decodeBuffer(params.buffer);
case 'resize': 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': case 'rotate':
return impl.rotate(params.imageData as any, params.numRotations); return impl.rotate(params.imageData as any, params.numRotations);
case 'encodeavif': case 'encodeavif':
return impl.encodeAvif(params.imageData as any, { quality: params.quality }) return impl.encodeAvif(params.imageData as any, { quality: params.quality });
case 'encodejpeg': case 'encodejpeg':
return impl.encodeJpeg(params.imageData as any, { quality: params.quality }) return impl.encodeJpeg(params.imageData as any, { quality: params.quality });
case 'encodepng': case 'encodepng':
return impl.encodePng(params.imageData as any) return impl.encodePng(params.imageData as any);
case 'encodewebp': case 'encodewebp':
return impl.encodeWebp(params.imageData as any, { quality: params.quality }) return impl.encodeWebp(params.imageData as any, { quality: params.quality });
default: default:
throw Error(`Invalid job "${(params as any).operation}"`); throw Error(`Invalid job "${(params as any).operation}"`);
} }
@ -82,56 +92,56 @@ export async function processBuffer(
quality?: number quality?: number
): Promise<Uint8Array> { ): Promise<Uint8Array> {
// @ts-ignore // @ts-ignore
const worker = await getWorker() const worker = await getWorker();
let imageData = await worker.dispatchJob({ let imageData = await worker.dispatchJob({
operation: 'decode', operation: 'decode',
buffer, buffer,
}) });
for (const operation of operations) { for (const operation of operations) {
if (operation.type === 'rotate') { if (operation.type === 'rotate') {
imageData = await worker.dispatchJob({ imageData = await worker.dispatchJob({
operation: 'rotate', operation: 'rotate',
imageData, imageData,
numRotations: operation.numRotations numRotations: operation.numRotations,
}); });
} else if (operation.type === 'resize') { } else if (operation.type === 'resize') {
imageData = await worker.dispatchJob({ imageData = await worker.dispatchJob({
operation: 'resize', operation: 'resize',
imageData, imageData,
height: operation.height, height: operation.height,
width: operation.width width: operation.width,
}) });
} }
} }
switch (encoding) { switch (encoding) {
case 'avif': case 'avif':
return await worker.dispatchJob({ return (await worker.dispatchJob({
operation: 'encodeavif', operation: 'encodeavif',
imageData, imageData,
quality, quality,
}) as Uint8Array; })) as Uint8Array;
case 'jpeg': case 'jpeg':
case 'jpg': case 'jpg':
return await worker.dispatchJob({ return (await worker.dispatchJob({
operation: 'encodejpeg', operation: 'encodejpeg',
imageData, imageData,
quality, quality,
}) as Uint8Array; })) as Uint8Array;
case 'png': case 'png':
return await worker.dispatchJob({ return (await worker.dispatchJob({
operation: 'encodepng', operation: 'encodepng',
imageData, imageData,
}) as Uint8Array; })) as Uint8Array;
case 'webp': case 'webp':
return await worker.dispatchJob({ return (await worker.dispatchJob({
operation: 'encodewebp', operation: 'encodewebp',
imageData, imageData,
quality, quality,
}) as Uint8Array; })) as Uint8Array;
default: default:
throw Error(`Unsupported encoding format`) throw Error(`Unsupported encoding format`);
} }
} }

View file

@ -1,8 +1,8 @@
/* eslint-disable */ /* eslint-disable */
// @ts-nocheck // @ts-nocheck
import { createRequire } from 'module'; import { createRequire } from 'module';
import { dirname } from '../emscripten-utils.js'; import { dirname, getModuleURL } from '../emscripten-utils.js';
const require = createRequire(import.meta.url); const require = createRequire(getModuleURL(import.meta.url));
var Module = (function () { var Module = (function () {
return function (Module) { return function (Module) {
@ -43,7 +43,7 @@ var Module = (function () {
if (ENVIRONMENT_IS_WORKER) { if (ENVIRONMENT_IS_WORKER) {
scriptDirectory = require('path').dirname(scriptDirectory) + '/' scriptDirectory = require('path').dirname(scriptDirectory) + '/'
} else { } else {
scriptDirectory = dirname(import.meta.url) + '/' scriptDirectory = dirname(getModuleURL(import.meta.url)) + '/'
} }
read_ = function shell_read(filename, binary) { read_ = function shell_read(filename, binary) {
if (!nodeFS) nodeFS = require('fs') if (!nodeFS) nodeFS = require('fs')

View file

@ -1,8 +1,8 @@
/* eslint-disable */ /* eslint-disable */
// @ts-nocheck // @ts-nocheck
import { createRequire } from 'module'; import { createRequire } from 'module';
import { dirname } from '../emscripten-utils.js'; import { dirname, getModuleURL } from '../emscripten-utils.js';
const require = createRequire(import.meta.url); const require = createRequire(getModuleURL(import.meta.url));
var Module = (function () { var Module = (function () {
return function (Module) { return function (Module) {
@ -43,7 +43,7 @@ var Module = (function () {
if (ENVIRONMENT_IS_WORKER) { if (ENVIRONMENT_IS_WORKER) {
scriptDirectory = require('path').dirname(scriptDirectory) + '/' scriptDirectory = require('path').dirname(scriptDirectory) + '/'
} else { } else {
scriptDirectory = dirname(import.meta.url) + '/' scriptDirectory = dirname(getModuleURL(import.meta.url)) + '/'
} }
read_ = function shell_read(filename, binary) { read_ = function shell_read(filename, binary) {
if (!nodeFS) nodeFS = require('fs') if (!nodeFS) nodeFS = require('fs')

View file

@ -1,8 +1,8 @@
/* eslint-disable */ /* eslint-disable */
// @ts-nocheck // @ts-nocheck
import { createRequire } from 'module'; import { createRequire } from 'module';
import { dirname } from '../emscripten-utils.js'; import { dirname, getModuleURL } from '../emscripten-utils.js';
const require = createRequire(import.meta.url); const require = createRequire(getModuleURL(import.meta.url));
var Module = (function () { var Module = (function () {
return function (Module) { return function (Module) {
@ -43,7 +43,7 @@ var Module = (function () {
if (ENVIRONMENT_IS_WORKER) { if (ENVIRONMENT_IS_WORKER) {
scriptDirectory = require('path').dirname(scriptDirectory) + '/' scriptDirectory = require('path').dirname(scriptDirectory) + '/'
} else { } else {
scriptDirectory = dirname(import.meta.url) + '/' scriptDirectory = dirname(getModuleURL(import.meta.url)) + '/'
} }
read_ = function shell_read(filename, binary) { read_ = function shell_read(filename, binary) {
if (!nodeFS) nodeFS = require('fs') if (!nodeFS) nodeFS = require('fs')

View file

@ -1,8 +1,8 @@
/* eslint-disable */ /* eslint-disable */
// @ts-nocheck // @ts-nocheck
import { createRequire } from 'module'; import { createRequire } from 'module';
import { dirname } from '../emscripten-utils.js'; import { dirname, getModuleURL } from '../emscripten-utils.js';
const require = createRequire(import.meta.url); const require = createRequire(getModuleURL(import.meta.url));
var Module = (function () { var Module = (function () {
return function (Module) { return function (Module) {
@ -43,7 +43,7 @@ var Module = (function () {
if (ENVIRONMENT_IS_WORKER) { if (ENVIRONMENT_IS_WORKER) {
scriptDirectory = require('path').dirname(scriptDirectory) + '/' scriptDirectory = require('path').dirname(scriptDirectory) + '/'
} else { } else {
scriptDirectory = dirname(import.meta.url) + '/' scriptDirectory = dirname(getModuleURL(import.meta.url)) + '/'
} }
read_ = function shell_read(filename, binary) { read_ = function shell_read(filename, binary) {
if (!nodeFS) nodeFS = require('fs') if (!nodeFS) nodeFS = require('fs')