Fix experimental.assets's Squoosh service not working on Netlify and Vercel SSR (#6765)
This commit is contained in:
parent
88d465a8d4
commit
6c09ac03bf
16 changed files with 52 additions and 113 deletions
5
.changeset/bright-laws-drum.md
Normal file
5
.changeset/bright-laws-drum.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Properly include the needed WASM files for the Squoosh service for Netlify and Vercel in SSR
|
1
packages/astro/src/assets/services/vendor/squoosh/avif/avif_node_dec.wasm.ts
vendored
Normal file
1
packages/astro/src/assets/services/vendor/squoosh/avif/avif_node_dec.wasm.ts
vendored
Normal file
File diff suppressed because one or more lines are too long
1
packages/astro/src/assets/services/vendor/squoosh/avif/avif_node_enc.wasm.ts
vendored
Normal file
1
packages/astro/src/assets/services/vendor/squoosh/avif/avif_node_enc.wasm.ts
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -1,5 +1,4 @@
|
|||
import { promises as fsp } from 'node:fs'
|
||||
import { getModuleURL, instantiateEmscriptenWasm, pathify } from './emscripten-utils.js'
|
||||
import { instantiateEmscriptenWasm } from './emscripten-utils.js'
|
||||
|
||||
interface DecodeModule extends EmscriptenWasm.Module {
|
||||
decode: (data: Uint8Array) => ImageData
|
||||
|
@ -35,52 +34,47 @@ export interface RotateOptions {
|
|||
|
||||
// MozJPEG
|
||||
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', getModuleURL(import.meta.url))
|
||||
// @ts-ignore
|
||||
import mozDec from './mozjpeg/mozjpeg_node_dec.js'
|
||||
const mozDecWasm = new URL('./mozjpeg/mozjpeg_node_dec.wasm', getModuleURL(import.meta.url))
|
||||
import mozDecWasm from './mozjpeg/mozjpeg_node_dec.wasm.js'
|
||||
|
||||
import mozEnc from './mozjpeg/mozjpeg_node_enc.js'
|
||||
import mozEncWasm from './mozjpeg/mozjpeg_node_enc.wasm.js'
|
||||
|
||||
// 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', getModuleURL(import.meta.url))
|
||||
// @ts-ignore
|
||||
import webpDec from './webp/webp_node_dec.js'
|
||||
const webpDecWasm = new URL('./webp/webp_node_dec.wasm', getModuleURL(import.meta.url))
|
||||
import webpDecWasm from './webp/webp_node_dec.wasm.js'
|
||||
|
||||
import webpEnc from './webp/webp_node_enc.js'
|
||||
import webpEncWasm from './webp/webp_node_enc.wasm.js'
|
||||
|
||||
// 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', getModuleURL(import.meta.url))
|
||||
// @ts-ignore
|
||||
import avifDec from './avif/avif_node_dec.js'
|
||||
const avifDecWasm = new URL('./avif/avif_node_dec.wasm', getModuleURL(import.meta.url))
|
||||
import avifDecWasm from './avif/avif_node_dec.wasm.js'
|
||||
|
||||
import avifEnc from './avif/avif_node_enc.js'
|
||||
import avifEncWasm from './avif/avif_node_enc.wasm.js'
|
||||
|
||||
// PNG
|
||||
// @ts-ignore
|
||||
import * as pngEncDec from './png/squoosh_png.js'
|
||||
const pngEncDecWasm = new URL('./png/squoosh_png_bg.wasm', getModuleURL(import.meta.url))
|
||||
import pngEncDecWasm from './png/squoosh_png_bg.wasm.js'
|
||||
|
||||
const pngEncDecInit = () =>
|
||||
pngEncDec.default(fsp.readFile(pathify(pngEncDecWasm.toString())))
|
||||
pngEncDec.default(pngEncDecWasm)
|
||||
|
||||
// OxiPNG
|
||||
// @ts-ignore
|
||||
import * as oxipng from './png/squoosh_oxipng.js'
|
||||
const oxipngWasm = new URL('./png/squoosh_oxipng_bg.wasm', getModuleURL(import.meta.url))
|
||||
const oxipngInit = () => oxipng.default(fsp.readFile(pathify(oxipngWasm.toString())))
|
||||
import oxipngWasm from './png/squoosh_oxipng_bg.wasm.js'
|
||||
const oxipngInit = () => oxipng.default(oxipngWasm)
|
||||
|
||||
// Resize
|
||||
// @ts-ignore
|
||||
import * as resize from './resize/squoosh_resize.js'
|
||||
const resizeWasm = new URL('./resize/squoosh_resize_bg.wasm', getModuleURL(import.meta.url))
|
||||
const resizeInit = () => resize.default(fsp.readFile(pathify(resizeWasm.toString())))
|
||||
import resizeWasm from './resize/squoosh_resize_bg.wasm.js'
|
||||
const resizeInit = () => resize.default(resizeWasm)
|
||||
|
||||
// rotate
|
||||
const rotateWasm = new URL('./rotate/rotate.wasm', getModuleURL(import.meta.url))
|
||||
import rotateWasm from './rotate/rotate.wasm.js'
|
||||
|
||||
// Our decoders currently rely on a `ImageData` global.
|
||||
import ImageData from './image_data.js'
|
||||
|
@ -187,7 +181,7 @@ export const preprocessors = {
|
|||
const sameDimensions = degrees === 0 || degrees === 180
|
||||
const size = width * height * 4
|
||||
const instance = (
|
||||
await WebAssembly.instantiate(await fsp.readFile(pathify(rotateWasm.toString())))
|
||||
await WebAssembly.instantiate(rotateWasm)
|
||||
).instance as RotateModuleInstance
|
||||
const { memory } = instance.exports
|
||||
const additionalPagesNeeded = Math.ceil(
|
||||
|
@ -218,11 +212,11 @@ export const codecs = {
|
|||
extension: 'jpg',
|
||||
detectors: [/^\xFF\xD8\xFF/],
|
||||
dec: () =>
|
||||
instantiateEmscriptenWasm(mozDec as DecodeModuleFactory, mozDecWasm.toString()),
|
||||
instantiateEmscriptenWasm(mozDec as DecodeModuleFactory, mozDecWasm),
|
||||
enc: () =>
|
||||
instantiateEmscriptenWasm(
|
||||
mozEnc as EmscriptenWasm.ModuleFactory<MozJPEGEncodeModule>,
|
||||
mozEncWasm.toString()
|
||||
mozEncWasm
|
||||
),
|
||||
defaultEncoderOptions: {
|
||||
quality: 75,
|
||||
|
@ -253,11 +247,11 @@ export const codecs = {
|
|||
extension: 'webp',
|
||||
detectors: [/^RIFF....WEBPVP8[LX ]/s],
|
||||
dec: () =>
|
||||
instantiateEmscriptenWasm(webpDec as DecodeModuleFactory, webpDecWasm.toString()),
|
||||
instantiateEmscriptenWasm(webpDec as DecodeModuleFactory, webpDecWasm),
|
||||
enc: () =>
|
||||
instantiateEmscriptenWasm(
|
||||
webpEnc as EmscriptenWasm.ModuleFactory<WebPEncodeModule>,
|
||||
webpEncWasm.toString()
|
||||
webpEncWasm
|
||||
),
|
||||
defaultEncoderOptions: {
|
||||
quality: 75,
|
||||
|
@ -300,11 +294,11 @@ export const codecs = {
|
|||
// eslint-disable-next-line no-control-regex
|
||||
detectors: [/^\x00\x00\x00 ftypavif\x00\x00\x00\x00/],
|
||||
dec: () =>
|
||||
instantiateEmscriptenWasm(avifDec as DecodeModuleFactory, avifDecWasm.toString()),
|
||||
instantiateEmscriptenWasm(avifDec as DecodeModuleFactory, avifDecWasm),
|
||||
enc: async () => {
|
||||
return instantiateEmscriptenWasm(
|
||||
avifEnc as EmscriptenWasm.ModuleFactory<AVIFEncodeModule>,
|
||||
avifEncWasm.toString()
|
||||
avifEncWasm
|
||||
)
|
||||
},
|
||||
defaultEncoderOptions: {
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
export async function copyWasmFiles(dir: URL) {
|
||||
const src = new URL('./', import.meta.url);
|
||||
const fileList = await listFiles(fileURLToPath(src), fileURLToPath(dir));
|
||||
|
||||
for (let file of fileList) {
|
||||
await fs.mkdir(path.dirname(file.dest), { recursive: true });
|
||||
await fs.copyFile(file.src, file.dest);
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteWasmFiles(dir: URL) {
|
||||
const src = new URL('./', import.meta.url);
|
||||
const fileList = await listFiles(fileURLToPath(src), fileURLToPath(dir));
|
||||
|
||||
for (let file of fileList) {
|
||||
await fs.rm(file.dest);
|
||||
}
|
||||
}
|
||||
|
||||
async function listFiles(src: string, dest: string) {
|
||||
const itemNames = await fs.readdir(src);
|
||||
const copiedFiles: {src: string, dest: string}[] = []
|
||||
await Promise.all(itemNames.map(async (srcName) => {
|
||||
const srcPath = path.join(src, srcName);
|
||||
const destPath = path.join(dest, srcName);
|
||||
const s = await fs.stat(srcPath);
|
||||
if (s.isFile() && /.wasm$/.test(srcPath)) {
|
||||
copiedFiles.push({src: srcPath, dest: destPath});
|
||||
}
|
||||
else if (s.isDirectory()) {
|
||||
copiedFiles.push(...await listFiles(srcPath, destPath));
|
||||
}
|
||||
}));
|
||||
|
||||
return copiedFiles;
|
||||
}
|
|
@ -10,19 +10,14 @@ export function pathify(path: string): string {
|
|||
|
||||
export function instantiateEmscriptenWasm<T extends EmscriptenWasm.Module>(
|
||||
factory: EmscriptenWasm.ModuleFactory<T>,
|
||||
path: string,
|
||||
workerJS = ''
|
||||
bytes: Uint8Array,
|
||||
): Promise<T> {
|
||||
return factory({
|
||||
locateFile(requestPath) {
|
||||
// The glue code generated by emscripten uses the original
|
||||
// file names of the worker file and the wasm binary.
|
||||
// These will have changed in the bundling process and
|
||||
// we need to inject them here.
|
||||
if (requestPath.endsWith('.wasm')) return pathify(path)
|
||||
if (requestPath.endsWith('.worker.js')) return pathify(workerJS)
|
||||
return requestPath
|
||||
},
|
||||
// @ts-expect-error This is a valid Emscripten option, but the type definitions don't know about it
|
||||
wasmBinary: bytes,
|
||||
locateFile(file: string) {
|
||||
return file
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -32,12 +27,12 @@ export function dirname(url: string) {
|
|||
|
||||
/**
|
||||
* 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
|
||||
* import.meta.url is undefined, so we'll fall back to __filename 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 pathToFileURL(__filename).toString();
|
||||
}
|
||||
|
||||
return url
|
||||
|
|
1
packages/astro/src/assets/services/vendor/squoosh/mozjpeg/mozjpeg_node_dec.wasm.ts
vendored
Normal file
1
packages/astro/src/assets/services/vendor/squoosh/mozjpeg/mozjpeg_node_dec.wasm.ts
vendored
Normal file
File diff suppressed because one or more lines are too long
1
packages/astro/src/assets/services/vendor/squoosh/mozjpeg/mozjpeg_node_enc.wasm.ts
vendored
Normal file
1
packages/astro/src/assets/services/vendor/squoosh/mozjpeg/mozjpeg_node_enc.wasm.ts
vendored
Normal file
File diff suppressed because one or more lines are too long
1
packages/astro/src/assets/services/vendor/squoosh/png/squoosh_oxipng_bg.wasm.ts
vendored
Normal file
1
packages/astro/src/assets/services/vendor/squoosh/png/squoosh_oxipng_bg.wasm.ts
vendored
Normal file
File diff suppressed because one or more lines are too long
1
packages/astro/src/assets/services/vendor/squoosh/png/squoosh_png_bg.wasm.ts
vendored
Normal file
1
packages/astro/src/assets/services/vendor/squoosh/png/squoosh_png_bg.wasm.ts
vendored
Normal file
File diff suppressed because one or more lines are too long
1
packages/astro/src/assets/services/vendor/squoosh/resize/squoosh_resize_bg.wasm.ts
vendored
Normal file
1
packages/astro/src/assets/services/vendor/squoosh/resize/squoosh_resize_bg.wasm.ts
vendored
Normal file
File diff suppressed because one or more lines are too long
1
packages/astro/src/assets/services/vendor/squoosh/rotate/rotate.wasm.ts
vendored
Normal file
1
packages/astro/src/assets/services/vendor/squoosh/rotate/rotate.wasm.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
export default Buffer.from("AGFzbQEAAAABDAJgAn9/AGADf39/AAMGBQAAAAABBQMBABAGEQJ/AEGAgMAAC38AQYCAwAALBy4EBm1lbW9yeQIABnJvdGF0ZQAECl9fZGF0YV9lbmQDAAtfX2hlYXBfYmFzZQMBCpsJBUkBAX8gACABbCIAQf////8DcSICBEBBCCEBIABBAnRBCGohAANAIAAgASgCADYCACABQQRqIQEgAEEEaiEAIAJBf2oiAg0ACwsLzQMBFH8gAUECdCERIAAgAWwiDEECdEEEaiESA0ACQAJAAkACQCAEQQFxRQRAIAMgAU8NAiADQQFqIQgMAQsgA0EPaiICIANJIggNASACIAFJIgVFDQEgASADQRBqIAgbIAEgBRshCCACIQMLIAEgA0EQaiICIAIgAUsbIQ0gA0F/cyETIBIgA0ECdGshFEEAIQVBACEOA0ACQAJAIA5FBEAgBSAASQ0BQQEhBAwGCyAAIAVBEGogBUEPaiICIAVJIgcbIAAgAiAASRshBUEBIQQgByACIABPcg0FDAELIAUiAkEBaiEFC0EBIQ4gAyANTw0AIAAgAkEQaiIPIAAgD0kbQQJ0IAJBAnRrIRUgEyABIAJsaiEHIBQgASACQQFqbEECdGohCSADIQoDQCAAIApsIgYgAmoiBEEQaiAAIAZqIA8gAEkbIgYgBEkgDCAGSXINAyAEIAZHBEAgBEECdEEIaiELIBUhBiAHIRAgCSEEA0AgDCABIBBqIhBNDQUgBCALKAIANgIAIAQgEWohBCALQQRqIQsgBkF8aiIGDQALCyAHQX9qIQcgCUF8aiEJIA0gCkEBaiIKRw0ACwwACwALDwsACyAIIQMMAAsAC1MBAX8CQCAAIAFsQQJ0IgJBCGoiAEEIRg0AIAAgAmpBfGohAEEAIQEDQCABIAJGDQEgACABQQhqKAIANgIAIABBfGohACACIAFBBGoiAUcNAAsLC9oDARN/IABBf2ohEEEAIAFBAnRrIREgACABbCIMQQJ0QQhqIRIDQAJAAkACQAJAIARBAXFFBEAgAyABTw0CIANBAWohCQwBCyADQQ9qIgIgA0kiCQ0BIAIgAUkiBUUNASABIANBEGogCRsgASAFGyEJIAIhAwsgASADQRBqIgIgAiABSxshDSASIANBAnRqIRNBACEFQQAhBgNAAkACQCAGQQFxRQRAIAUgAEkNAUEBIQQMBgsgACAFQRBqIAVBD2oiAiAFSSIIGyAAIAIgAEkbIQVBASEEIAggAiAAT3INBQwBCyAFIgJBAWohBQtBASEGIAMgDU8NACAAIAJBEGoiDiAAIA5JG0ECdCACQQJ0ayEUIAMgASAAIAJrbGohCCATIAEgECACa2xBAnRqIQogAyELA0AgACALbCIHIAJqIgRBEGogACAHaiAOIABJGyIHIARJIAwgB0lyDQMgBCAHRwRAIARBAnRBCGohBiAUIQcgCCEPIAohBANAIAwgDyABayIPTQ0FIAQgBigCADYCACAEIBFqIQQgBkEEaiEGIAdBfGoiBw0ACwtBASEGIAhBAWohCCAKQQRqIQogDSALQQFqIgtHDQALDAALAAsPCwALIAkhAwwACwALUAACQAJAAkACQCACQbMBTARAIAJFDQIgAkHaAEcNASAAIAEQAQ8LIAJBtAFGDQIgAkGOAkYNAwsACyAAIAEQAA8LIAAgARACDwsgACABEAMLAE0JcHJvZHVjZXJzAghsYW5ndWFnZQEEUnVzdAAMcHJvY2Vzc2VkLWJ5AQVydXN0Yx0xLjQ3LjAgKDE4YmY2YjRmMCAyMDIwLTEwLTA3KQ==", 'base64');
|
1
packages/astro/src/assets/services/vendor/squoosh/webp/webp_node_dec.wasm.ts
vendored
Normal file
1
packages/astro/src/assets/services/vendor/squoosh/webp/webp_node_dec.wasm.ts
vendored
Normal file
File diff suppressed because one or more lines are too long
1
packages/astro/src/assets/services/vendor/squoosh/webp/webp_node_enc.wasm.ts
vendored
Normal file
1
packages/astro/src/assets/services/vendor/squoosh/webp/webp_node_enc.wasm.ts
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -12,7 +12,6 @@ import { appendForwardSlash, joinPaths, prependForwardSlash } from '../core/path
|
|||
import { VIRTUAL_MODULE_ID, VIRTUAL_SERVICE_ID } from './consts.js';
|
||||
import { isESMImportedImage } from './internal.js';
|
||||
import { isLocalService } from './services/service.js';
|
||||
import { copyWasmFiles } from './services/vendor/squoosh/copy-wasm.js';
|
||||
import { emitESMImage } from './utils/emitAsset.js';
|
||||
import { imageMetadata } from './utils/metadata.js';
|
||||
import { getOrigQueryParams } from './utils/queryParams.js';
|
||||
|
@ -181,20 +180,6 @@ export default function assets({
|
|||
}
|
||||
};
|
||||
},
|
||||
async buildEnd() {
|
||||
if (mode != 'build') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (settings.config.image.service === 'astro/assets/services/squoosh') {
|
||||
const dir =
|
||||
settings.config.output === 'server'
|
||||
? settings.config.build.server
|
||||
: settings.config.outDir;
|
||||
|
||||
await copyWasmFiles(new URL('./chunks', dir));
|
||||
}
|
||||
},
|
||||
// In build, rewrite paths to ESM imported images in code to their final location
|
||||
async renderChunk(code) {
|
||||
const assetUrlRE = /__ASTRO_ASSET_IMAGE__([a-z\d]{8})__(?:_(.*?)__)?/g;
|
||||
|
@ -237,6 +222,6 @@ export default function assets({
|
|||
return `export default ${JSON.stringify(meta)}`;
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
];
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ import {
|
|||
generateImage as generateImageInternal,
|
||||
getStaticImageList,
|
||||
} from '../../assets/internal.js';
|
||||
import { deleteWasmFiles } from '../../assets/services/vendor/squoosh/copy-wasm.js';
|
||||
import { hasPrerenderedPages, type BuildInternals } from '../../core/build/internal.js';
|
||||
import {
|
||||
prependForwardSlash,
|
||||
|
@ -115,15 +114,6 @@ export async function generatePages(opts: StaticBuildOptions, internals: BuildIn
|
|||
await generateImage(opts, imageData[1].options, imageData[1].path);
|
||||
}
|
||||
|
||||
// Our Squoosh image service loads `.wasm` files relatively, so we need to copy the WASM files to the dist
|
||||
// for the image generation to work. In static output, we can remove those after the build is done.
|
||||
if (
|
||||
opts.settings.config.image.service === 'astro/assets/services/squoosh' &&
|
||||
opts.settings.config.output === 'static'
|
||||
) {
|
||||
await deleteWasmFiles(new URL('./chunks', opts.settings.config.outDir));
|
||||
}
|
||||
|
||||
delete globalThis.astroAsset.addStaticImage;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue