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,
|
parseQuality,
|
||||||
type BaseServiceTransform,
|
type BaseServiceTransform,
|
||||||
type LocalImageService,
|
type LocalImageService,
|
||||||
type LocalImageTransform,
|
|
||||||
} from './service.js';
|
} from './service.js';
|
||||||
import { processBuffer } from './vendor/squoosh/image-pool.js';
|
import { processBuffer } from './vendor/squoosh/image-pool.js';
|
||||||
import type { Operation } from './vendor/squoosh/image.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
|
// Squoosh's PNG encoder does not support a quality setting, so we can skip that here
|
||||||
};
|
};
|
||||||
|
|
||||||
async function getRotationForEXIF(
|
async function getRotationForEXIF(inputBuffer: Buffer): Promise<Operation | undefined> {
|
||||||
transform: LocalImageTransform,
|
const meta = await imageMetadata(inputBuffer);
|
||||||
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);
|
|
||||||
|
|
||||||
if (!meta) return undefined;
|
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.
|
// 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 operations: Operation[] = [];
|
||||||
|
|
||||||
const rotation = await getRotationForEXIF(transform, inputBuffer);
|
const rotation = await getRotationForEXIF(inputBuffer);
|
||||||
|
|
||||||
if (rotation) {
|
if (rotation) {
|
||||||
operations.push(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 {
|
export interface ImageMetadata {
|
||||||
src: string;
|
src: string;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs/promises';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||||
import { prependForwardSlash, slash } from '../../core/path.js';
|
import { prependForwardSlash, slash } from '../../core/path.js';
|
||||||
|
@ -15,34 +15,46 @@ export async function emitESMImage(
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = pathToFileURL(id);
|
const url = pathToFileURL(id);
|
||||||
const meta = await imageMetadata(url);
|
let fileData: Buffer;
|
||||||
|
try {
|
||||||
if (!meta) {
|
fileData = await fs.readFile(url);
|
||||||
|
} catch (err) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fileMetadata = await imageMetadata(fileData);
|
||||||
|
|
||||||
|
if (!fileMetadata) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const emittedImage: ImageMetadata = {
|
||||||
|
src: '',
|
||||||
|
...fileMetadata,
|
||||||
|
};
|
||||||
|
|
||||||
// Build
|
// Build
|
||||||
if (!watchMode) {
|
if (!watchMode) {
|
||||||
const pathname = decodeURI(url.pathname);
|
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({
|
const handle = fileEmitter({
|
||||||
name: filename,
|
name: filename,
|
||||||
source: await fs.promises.readFile(url),
|
source: await fs.readFile(url),
|
||||||
type: 'asset',
|
type: 'asset',
|
||||||
});
|
});
|
||||||
|
|
||||||
meta.src = `__ASTRO_ASSET_IMAGE__${handle}__`;
|
emittedImage.src = `__ASTRO_ASSET_IMAGE__${handle}__`;
|
||||||
} else {
|
} else {
|
||||||
// Pass the original file information through query params so we don't have to load the file twice
|
// 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('origWidth', fileMetadata.width.toString());
|
||||||
url.searchParams.append('origHeight', meta.height.toString());
|
url.searchParams.append('origHeight', fileMetadata.height.toString());
|
||||||
url.searchParams.append('origFormat', meta.format);
|
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 {
|
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 type { ImageInputFormat, ImageMetadata } from '../types.js';
|
||||||
import imageSize from '../vendor/image-size/index.js';
|
import imageSize from '../vendor/image-size/index.js';
|
||||||
|
|
||||||
export async function imageMetadata(
|
export async function imageMetadata(data: Buffer): Promise<Omit<ImageMetadata, 'src'> | undefined> {
|
||||||
src: URL | string,
|
const { width, height, type, orientation } = imageSize(data);
|
||||||
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);
|
|
||||||
const isPortrait = (orientation || 0) >= 5;
|
const isPortrait = (orientation || 0) >= 5;
|
||||||
|
|
||||||
if (!width || !height || !type) {
|
if (!width || !height || !type) {
|
||||||
|
@ -24,7 +10,6 @@ export async function imageMetadata(
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
src: fileURLToPath(src),
|
|
||||||
width: isPortrait ? height : width,
|
width: isPortrait ? height : width,
|
||||||
height: isPortrait ? width : height,
|
height: isPortrait ? width : height,
|
||||||
format: type as ImageInputFormat,
|
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 }) => {
|
export const get = (async ({ params, request }) => {
|
||||||
const url = new URL(request.url);
|
const url = new URL(request.url);
|
||||||
|
|
Loading…
Reference in a new issue