fix: squoosh not working on Windows in certain situations (#7826)
This commit is contained in:
parent
54fb03b444
commit
31c4031ba7
6 changed files with 36 additions and 42 deletions
5
.changeset/calm-balloons-study.md
Normal file
5
.changeset/calm-balloons-study.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Fix `astro:assets` not working on Windows in build when using Squoosh
|
|
@ -7,7 +7,6 @@ import {
|
|||
parseQuality,
|
||||
type BaseServiceTransform,
|
||||
type LocalImageService,
|
||||
type LocalImageTransform,
|
||||
} from './service.js';
|
||||
import { processBuffer } from './vendor/squoosh/image-pool.js';
|
||||
import type { Operation } from './vendor/squoosh/image.js';
|
||||
|
@ -30,15 +29,8 @@ const qualityTable: Record<
|
|||
// Squoosh's PNG encoder does not support a quality setting, so we can skip that here
|
||||
};
|
||||
|
||||
async function getRotationForEXIF(
|
||||
transform: LocalImageTransform,
|
||||
inputBuffer: Buffer
|
||||
): Promise<Operation | undefined> {
|
||||
// check EXIF orientation data and rotate the image if needed
|
||||
const filePath = transform.src.slice('/@fs'.length);
|
||||
const filePathURL = new URL('.' + filePath, 'file:');
|
||||
const meta = await imageMetadata(filePathURL, inputBuffer);
|
||||
|
||||
async function getRotationForEXIF(inputBuffer: Buffer): Promise<Operation | undefined> {
|
||||
const meta = await imageMetadata(inputBuffer);
|
||||
if (!meta) return undefined;
|
||||
|
||||
// EXIF orientations are a bit hard to read, but the numbers are actually standard. See https://exiftool.org/TagNames/EXIF.html for a list.
|
||||
|
@ -71,7 +63,7 @@ const service: LocalImageService = {
|
|||
|
||||
const operations: Operation[] = [];
|
||||
|
||||
const rotation = await getRotationForEXIF(transform, inputBuffer);
|
||||
const rotation = await getRotationForEXIF(inputBuffer);
|
||||
|
||||
if (rotation) {
|
||||
operations.push(rotation);
|
||||
|
|
|
@ -17,7 +17,7 @@ declare global {
|
|||
}
|
||||
|
||||
/**
|
||||
* Type returned by ESM imports of images and direct calls to imageMetadata
|
||||
* Type returned by ESM imports of images
|
||||
*/
|
||||
export interface ImageMetadata {
|
||||
src: string;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import fs from 'node:fs';
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
import { prependForwardSlash, slash } from '../../core/path.js';
|
||||
|
@ -15,34 +15,46 @@ export async function emitESMImage(
|
|||
}
|
||||
|
||||
const url = pathToFileURL(id);
|
||||
const meta = await imageMetadata(url);
|
||||
|
||||
if (!meta) {
|
||||
let fileData: Buffer;
|
||||
try {
|
||||
fileData = await fs.readFile(url);
|
||||
} catch (err) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const fileMetadata = await imageMetadata(fileData);
|
||||
|
||||
if (!fileMetadata) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const emittedImage: ImageMetadata = {
|
||||
src: '',
|
||||
...fileMetadata,
|
||||
};
|
||||
|
||||
// Build
|
||||
if (!watchMode) {
|
||||
const pathname = decodeURI(url.pathname);
|
||||
const filename = path.basename(pathname, path.extname(pathname) + `.${meta.format}`);
|
||||
const filename = path.basename(pathname, path.extname(pathname) + `.${fileMetadata.format}`);
|
||||
|
||||
const handle = fileEmitter({
|
||||
name: filename,
|
||||
source: await fs.promises.readFile(url),
|
||||
source: await fs.readFile(url),
|
||||
type: 'asset',
|
||||
});
|
||||
|
||||
meta.src = `__ASTRO_ASSET_IMAGE__${handle}__`;
|
||||
emittedImage.src = `__ASTRO_ASSET_IMAGE__${handle}__`;
|
||||
} else {
|
||||
// Pass the original file information through query params so we don't have to load the file twice
|
||||
url.searchParams.append('origWidth', meta.width.toString());
|
||||
url.searchParams.append('origHeight', meta.height.toString());
|
||||
url.searchParams.append('origFormat', meta.format);
|
||||
url.searchParams.append('origWidth', fileMetadata.width.toString());
|
||||
url.searchParams.append('origHeight', fileMetadata.height.toString());
|
||||
url.searchParams.append('origFormat', fileMetadata.format);
|
||||
|
||||
meta.src = `/@fs` + prependForwardSlash(fileURLToNormalizedPath(url));
|
||||
emittedImage.src = `/@fs` + prependForwardSlash(fileURLToNormalizedPath(url));
|
||||
}
|
||||
|
||||
return meta;
|
||||
return emittedImage;
|
||||
}
|
||||
|
||||
function fileURLToNormalizedPath(filePath: URL): string {
|
||||
|
|
|
@ -1,22 +1,8 @@
|
|||
import fs from 'node:fs/promises';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import type { ImageInputFormat, ImageMetadata } from '../types.js';
|
||||
import imageSize from '../vendor/image-size/index.js';
|
||||
|
||||
export async function imageMetadata(
|
||||
src: URL | string,
|
||||
data?: Buffer
|
||||
): Promise<ImageMetadata | undefined> {
|
||||
let file = data;
|
||||
if (!file) {
|
||||
try {
|
||||
file = await fs.readFile(src);
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
const { width, height, type, orientation } = imageSize(file);
|
||||
export async function imageMetadata(data: Buffer): Promise<Omit<ImageMetadata, 'src'> | undefined> {
|
||||
const { width, height, type, orientation } = imageSize(data);
|
||||
const isPortrait = (orientation || 0) >= 5;
|
||||
|
||||
if (!width || !height || !type) {
|
||||
|
@ -24,7 +10,6 @@ export async function imageMetadata(
|
|||
}
|
||||
|
||||
return {
|
||||
src: fileURLToPath(src),
|
||||
width: isPortrait ? height : width,
|
||||
height: isPortrait ? width : height,
|
||||
format: type as ImageInputFormat,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { APIRoute } from "../../../../../src/@types/astro";
|
||||
import type { APIRoute } from "../../../../../src/@types/astro";
|
||||
|
||||
export const get = (async ({ params, request }) => {
|
||||
const url = new URL(request.url);
|
||||
|
|
Loading…
Reference in a new issue