From 6576968141f007e771b1925cfc0d3edd0148e39e Mon Sep 17 00:00:00 2001 From: Steven Date: Mon, 11 Aug 2025 14:45:19 -0400 Subject: [PATCH 1/6] fix(next/image): use image-size as format detector --- .../src/compiled/image-detector/detector.js | 1 + packages/next/src/server/image-optimizer.ts | 19 ++++++++++++++----- packages/next/taskfile.js | 10 ++++++++++ packages/next/types/$$compiled.internal.d.ts | 4 ++++ 4 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 packages/next/src/compiled/image-detector/detector.js diff --git a/packages/next/src/compiled/image-detector/detector.js b/packages/next/src/compiled/image-detector/detector.js new file mode 100644 index 0000000000000..19317a740eb84 --- /dev/null +++ b/packages/next/src/compiled/image-detector/detector.js @@ -0,0 +1 @@ +(()=>{"use strict";var t={436:(t,e,n)=>{Object.defineProperty(e,"__esModule",{value:true});e.BMP=void 0;const r=n(779);e.BMP={validate:t=>(0,r.toUTF8String)(t,0,2)==="BM",calculate:t=>({height:Math.abs((0,r.readInt32LE)(t,22)),width:(0,r.readUInt32LE)(t,18)})}},67:(t,e,n)=>{Object.defineProperty(e,"__esModule",{value:true});e.CUR=void 0;const r=n(845);const i=n(779);const o=2;e.CUR={validate(t){const e=(0,i.readUInt16LE)(t,0);const n=(0,i.readUInt16LE)(t,4);if(e!==0||n===0)return false;const r=(0,i.readUInt16LE)(t,2);return r===o},calculate:t=>r.ICO.calculate(t)}},400:(t,e,n)=>{Object.defineProperty(e,"__esModule",{value:true});e.DDS=void 0;const r=n(779);e.DDS={validate:t=>(0,r.readUInt32LE)(t,0)===542327876,calculate:t=>({height:(0,r.readUInt32LE)(t,12),width:(0,r.readUInt32LE)(t,16)})}},197:(t,e,n)=>{Object.defineProperty(e,"__esModule",{value:true});e.GIF=void 0;const r=n(779);const i=/^GIF8[79]a/;e.GIF={validate:t=>i.test((0,r.toUTF8String)(t,0,6)),calculate:t=>({height:(0,r.readUInt16LE)(t,8),width:(0,r.readUInt16LE)(t,6)})}},974:(t,e,n)=>{Object.defineProperty(e,"__esModule",{value:true});e.HEIF=void 0;const r=n(779);const i={avif:"avif",mif1:"heif",msf1:"heif",heic:"heic",heix:"heic",hevc:"heic",hevx:"heic"};e.HEIF={validate(t){const e=(0,r.toUTF8String)(t,4,8);if(e!=="ftyp")return false;const n=(0,r.findBox)(t,"ftyp",0);if(!n)return false;const o=(0,r.toUTF8String)(t,n.offset+8,n.offset+12);return o in i},calculate(t){const e=(0,r.findBox)(t,"meta",0);const n=e&&(0,r.findBox)(t,"iprp",e.offset+12);const i=n&&(0,r.findBox)(t,"ipco",n.offset+8);const o=i&&(0,r.findBox)(t,"ispe",i.offset+8);if(o){return{height:(0,r.readUInt32BE)(t,o.offset+16),width:(0,r.readUInt32BE)(t,o.offset+12),type:(0,r.toUTF8String)(t,8,12)}}throw new TypeError("Invalid HEIF, no size found")}}},512:(t,e,n)=>{Object.defineProperty(e,"__esModule",{value:true});e.ICNS=void 0;const r=n(779);const i=4+4;const o=4;const s=4;const c={ICON:32,"ICN#":32,"icm#":16,icm4:16,icm8:16,"ics#":16,ics4:16,ics8:16,is32:16,s8mk:16,icp4:16,icl4:32,icl8:32,il32:32,l8mk:32,icp5:32,ic11:32,ich4:48,ich8:48,ih32:48,h8mk:48,icp6:64,ic12:32,it32:128,t8mk:128,ic07:128,ic08:256,ic13:256,ic09:512,ic14:512,ic10:1024};function readImageHeader(t,e){const n=e+s;return[(0,r.toUTF8String)(t,e,n),(0,r.readUInt32BE)(t,n)]}function getImageSize(t){const e=c[t];return{width:e,height:e,type:t}}e.ICNS={validate:t=>(0,r.toUTF8String)(t,0,4)==="icns",calculate(t){const e=t.length;const n=(0,r.readUInt32BE)(t,o);let s=i;let c=readImageHeader(t,s);let a=getImageSize(c[0]);s+=c[1];if(s===n)return a;const d={height:a.height,images:[a],width:a.width};while(s{Object.defineProperty(e,"__esModule",{value:true});e.ICO=void 0;const r=n(779);const i=1;const o=2+2+2;const s=1+1+1+1+2+2+4+4;function getSizeFromOffset(t,e){const n=t[e];return n===0?256:n}function getImageSize(t,e){const n=o+e*s;return{height:getSizeFromOffset(t,n+1),width:getSizeFromOffset(t,n)}}e.ICO={validate(t){const e=(0,r.readUInt16LE)(t,0);const n=(0,r.readUInt16LE)(t,4);if(e!==0||n===0)return false;const o=(0,r.readUInt16LE)(t,2);return o===i},calculate(t){const e=(0,r.readUInt16LE)(t,4);const n=getImageSize(t,0);if(e===1)return n;const i=[n];for(let n=1;n{Object.defineProperty(e,"__esModule",{value:true});e.typeHandlers=void 0;const r=n(436);const i=n(67);const o=n(400);const s=n(197);const c=n(974);const a=n(512);const d=n(845);const f=n(885);const u=n(530);const l=n(934);const h=n(245);const g=n(33);const I=n(105);const p=n(210);const U=n(448);const w=n(136);const v=n(414);const E=n(753);const m=n(940);const B=n(226);e.typeHandlers={bmp:r.BMP,cur:i.CUR,dds:o.DDS,gif:s.GIF,heif:c.HEIF,icns:a.ICNS,ico:d.ICO,j2c:f.J2C,jp2:u.JP2,jpg:l.JPG,jxl:h.JXL,"jxl-stream":g.JXLStream,ktx:I.KTX,png:p.PNG,pnm:U.PNM,psd:w.PSD,svg:v.SVG,tga:E.TGA,tiff:m.TIFF,webp:B.WEBP}},885:(t,e,n)=>{Object.defineProperty(e,"__esModule",{value:true});e.J2C=void 0;const r=n(779);e.J2C={validate:t=>(0,r.readUInt32BE)(t,0)===4283432785,calculate:t=>({height:(0,r.readUInt32BE)(t,12),width:(0,r.readUInt32BE)(t,8)})}},530:(t,e,n)=>{Object.defineProperty(e,"__esModule",{value:true});e.JP2=void 0;const r=n(779);e.JP2={validate(t){const e=(0,r.toUTF8String)(t,4,8);if(e!=="jP ")return false;const n=(0,r.findBox)(t,"ftyp",0);if(!n)return false;const i=(0,r.toUTF8String)(t,n.offset+8,n.offset+12);return i==="jp2 "},calculate(t){const e=(0,r.findBox)(t,"jp2h",0);const n=e&&(0,r.findBox)(t,"ihdr",e.offset+8);if(n){return{height:(0,r.readUInt32BE)(t,n.offset+8),width:(0,r.readUInt32BE)(t,n.offset+12)}}throw new TypeError("Unsupported JPEG 2000 format")}}},934:(t,e,n)=>{Object.defineProperty(e,"__esModule",{value:true});e.JPG=void 0;const r=n(779);const i="45786966";const o=2;const s=6;const c=2;const a="4d4d";const d="4949";const f=12;const u=2;function isEXIF(t){return(0,r.toHexString)(t,2,6)===i}function extractSize(t,e){return{height:(0,r.readUInt16BE)(t,e),width:(0,r.readUInt16BE)(t,e+2)}}function extractOrientation(t,e){const n=8;const i=s+n;const o=(0,r.readUInt)(t,16,i,e);for(let n=0;nt.length){return}const c=t.slice(o,s);const a=(0,r.readUInt)(c,16,0,e);if(a===274){const t=(0,r.readUInt)(c,16,2,e);if(t!==3){return}const n=(0,r.readUInt)(c,32,4,e);if(n!==1){return}return(0,r.readUInt)(c,16,8,e)}}}function validateExifBlock(t,e){const n=t.slice(o,e);const i=(0,r.toHexString)(n,s,s+c);const f=i===a;const u=i===d;if(f||u){return extractOrientation(n,f)}}function validateInput(t,e){if(e>t.length){throw new TypeError("Corrupt JPG, exceeded buffer limits")}}e.JPG={validate:t=>(0,r.toHexString)(t,0,2)==="ffd8",calculate(t){t=t.slice(4);let e;let n;while(t.length){const i=(0,r.readUInt16BE)(t,0);if(t[i]!==255){t=t.slice(1);continue}if(isEXIF(t)){e=validateExifBlock(t,i)}validateInput(t,i);n=t[i+1];if(n===192||n===193||n===194){const n=extractSize(t,i+5);if(!e){return n}return{height:n.height,orientation:e,width:n.width}}t=t.slice(i+2)}throw new TypeError("Invalid JPG, no size found")}}},33:(t,e,n)=>{Object.defineProperty(e,"__esModule",{value:true});e.JXLStream=void 0;const r=n(779);const i=n(112);function calculateImageDimension(t,e){if(e){return 8*(1+t.getBits(5))}else{const e=t.getBits(2);const n=[9,13,18,30][e];return 1+t.getBits(n)}}function calculateImageWidth(t,e,n,r){if(e&&n===0){return 8*(1+t.getBits(5))}else if(n===0){return calculateImageDimension(t,false)}else{const t=[1,1.2,4/3,1.5,16/9,5/4,2];return Math.floor(r*t[n-1])}}e.JXLStream={validate:t=>(0,r.toHexString)(t,0,2)==="ff0a",calculate(t){const e=new i.BitReader(t,"little-endian");const n=e.getBits(1)===1;const r=calculateImageDimension(e,n);const o=e.getBits(3);const s=calculateImageWidth(e,n,o,r);return{width:s,height:r}}}},245:(t,e,n)=>{Object.defineProperty(e,"__esModule",{value:true});e.JXL=void 0;const r=n(779);const i=n(33);function extractCodestream(t){const e=(0,r.findBox)(t,"jxlc",0);if(e){return t.slice(e.offset+8,e.offset+e.size)}const n=extractPartialStreams(t);if(n.length>0){return concatenateCodestreams(n)}return undefined}function extractPartialStreams(t){const e=[];let n=0;while(nt+e.length),0);const n=new Uint8Array(e);let r=0;for(const e of t){n.set(e,r);r+=e.length}return n}e.JXL={validate:t=>{const e=(0,r.toUTF8String)(t,4,8);if(e!=="JXL ")return false;const n=(0,r.findBox)(t,"ftyp",0);if(!n)return false;const i=(0,r.toUTF8String)(t,n.offset+8,n.offset+12);return i==="jxl "},calculate(t){const e=extractCodestream(t);if(e)return i.JXLStream.calculate(e);throw new Error("No codestream found in JXL container")}}},105:(t,e,n)=>{Object.defineProperty(e,"__esModule",{value:true});e.KTX=void 0;const r=n(779);e.KTX={validate:t=>{const e=(0,r.toUTF8String)(t,1,7);return["KTX 11","KTX 20"].includes(e)},calculate:t=>{const e=t[5]===49?"ktx":"ktx2";const n=e==="ktx"?36:20;return{height:(0,r.readUInt32LE)(t,n+4),width:(0,r.readUInt32LE)(t,n),type:e}}}},210:(t,e,n)=>{Object.defineProperty(e,"__esModule",{value:true});e.PNG=void 0;const r=n(779);const i="PNG\r\n\n";const o="IHDR";const s="CgBI";e.PNG={validate(t){if(i===(0,r.toUTF8String)(t,1,8)){let e=(0,r.toUTF8String)(t,12,16);if(e===s){e=(0,r.toUTF8String)(t,28,32)}if(e!==o){throw new TypeError("Invalid PNG")}return true}return false},calculate(t){if((0,r.toUTF8String)(t,12,16)===s){return{height:(0,r.readUInt32BE)(t,36),width:(0,r.readUInt32BE)(t,32)}}return{height:(0,r.readUInt32BE)(t,20),width:(0,r.readUInt32BE)(t,16)}}}},448:(t,e,n)=>{Object.defineProperty(e,"__esModule",{value:true});e.PNM=void 0;const r=n(779);const i={P1:"pbm/ascii",P2:"pgm/ascii",P3:"ppm/ascii",P4:"pbm",P5:"pgm",P6:"ppm",P7:"pam",PF:"pfm"};const o={default:t=>{let e=[];while(t.length>0){const n=t.shift();if(n[0]==="#"){continue}e=n.split(" ");break}if(e.length===2){return{height:parseInt(e[1],10),width:parseInt(e[0],10)}}else{throw new TypeError("Invalid PNM")}},pam:t=>{const e={};while(t.length>0){const n=t.shift();if(n.length>16||n.charCodeAt(0)>128){continue}const[r,i]=n.split(" ");if(r&&i){e[r.toLowerCase()]=parseInt(i,10)}if(e.height&&e.width){break}}if(e.height&&e.width){return{height:e.height,width:e.width}}else{throw new TypeError("Invalid PAM")}}};e.PNM={validate:t=>(0,r.toUTF8String)(t,0,2)in i,calculate(t){const e=(0,r.toUTF8String)(t,0,2);const n=i[e];const s=(0,r.toUTF8String)(t,3).split(/[\r\n]+/);const c=o[n]||o.default;return c(s)}}},136:(t,e,n)=>{Object.defineProperty(e,"__esModule",{value:true});e.PSD=void 0;const r=n(779);e.PSD={validate:t=>(0,r.toUTF8String)(t,0,4)==="8BPS",calculate:t=>({height:(0,r.readUInt32BE)(t,14),width:(0,r.readUInt32BE)(t,18)})}},414:(t,e,n)=>{Object.defineProperty(e,"__esModule",{value:true});e.SVG=void 0;const r=n(779);const i=/"']|"[^"]*"|'[^']*')*>/;const o={height:/\sheight=(['"])([^%]+?)\1/,root:i,viewbox:/\sviewBox=(['"])(.+?)\1/i,width:/\swidth=(['"])([^%]+?)\1/};const s=2.54;const c={in:96,cm:96/s,em:16,ex:8,m:96/s*100,mm:96/s/10,pc:96/72/12,pt:96/72,px:1};const a=new RegExp(`^([0-9.]+(?:e\\d+)?)(${Object.keys(c).join("|")})?$`);function parseLength(t){const e=a.exec(t);if(!e){return undefined}return Math.round(Number(e[1])*(c[e[2]]||1))}function parseViewbox(t){const e=t.split(" ");return{height:parseLength(e[3]),width:parseLength(e[2])}}function parseAttributes(t){const e=t.match(o.width);const n=t.match(o.height);const r=t.match(o.viewbox);return{height:n&&parseLength(n[2]),viewbox:r&&parseViewbox(r[2]),width:e&&parseLength(e[2])}}function calculateByDimensions(t){return{height:t.height,width:t.width}}function calculateByViewbox(t,e){const n=e.width/e.height;if(t.width){return{height:Math.floor(t.width/n),width:t.width}}if(t.height){return{height:t.height,width:Math.floor(t.height*n)}}return{height:e.height,width:e.width}}e.SVG={validate:t=>i.test((0,r.toUTF8String)(t,0,1e3)),calculate(t){const e=(0,r.toUTF8String)(t).match(o.root);if(e){const t=parseAttributes(e[0]);if(t.width&&t.height){return calculateByDimensions(t)}if(t.viewbox){return calculateByViewbox(t,t.viewbox)}}throw new TypeError("Invalid SVG")}}},753:(t,e,n)=>{Object.defineProperty(e,"__esModule",{value:true});e.TGA=void 0;const r=n(779);e.TGA={validate(t){return(0,r.readUInt16LE)(t,0)===0&&(0,r.readUInt16LE)(t,4)===0},calculate(t){return{height:(0,r.readUInt16LE)(t,14),width:(0,r.readUInt16LE)(t,12)}}}},940:(t,e,n)=>{Object.defineProperty(e,"__esModule",{value:true});e.TIFF=void 0;const r=n(147);const i=n(779);function readIFD(t,e,n){const o=(0,i.readUInt)(t,32,4,n);let s=1024;const c=r.statSync(e).size;if(o+s>c){s=c-o-10}const a=new Uint8Array(s);const d=r.openSync(e,"r");r.readSync(d,a,0,s,o);r.closeSync(d);return a.slice(2)}function readValue(t,e){const n=(0,i.readUInt)(t,16,8,e);const r=(0,i.readUInt)(t,16,10,e);return(r<<16)+n}function nextTag(t){if(t.length>24){return t.slice(12)}}function extractTags(t,e){const n={};let r=t;while(r&&r.length){const t=(0,i.readUInt)(r,16,0,e);const o=(0,i.readUInt)(r,16,2,e);const s=(0,i.readUInt)(r,32,4,e);if(t===0){break}else{if(s===1&&(o===3||o===4)){n[t]=readValue(r,e)}r=nextTag(r)}}return n}function determineEndianness(t){const e=(0,i.toUTF8String)(t,0,2);if("II"===e){return"LE"}else if("MM"===e){return"BE"}}const o=["49492a00","4d4d002a"];e.TIFF={validate:t=>o.includes((0,i.toHexString)(t,0,4)),calculate(t,e){if(!e){throw new TypeError("Tiff doesn't support buffer")}const n=determineEndianness(t)==="BE";const r=readIFD(t,e,n);const i=extractTags(r,n);const o=i[256];const s=i[257];if(!o||!s){throw new TypeError("Invalid Tiff. Missing tags")}return{height:s,width:o}}}},779:(t,e)=>{Object.defineProperty(e,"__esModule",{value:true});e.findBox=e.readUInt=e.readUInt32LE=e.readUInt32BE=e.readInt32LE=e.readUInt24LE=e.readUInt16LE=e.readUInt16BE=e.readInt16LE=e.toHexString=e.toUTF8String=void 0;const n=new TextDecoder;const toUTF8String=(t,e=0,r=t.length)=>n.decode(t.slice(e,r));e.toUTF8String=toUTF8String;const toHexString=(t,e=0,n=t.length)=>t.slice(e,n).reduce(((t,e)=>t+("0"+e.toString(16)).slice(-2)),"");e.toHexString=toHexString;const readInt16LE=(t,e=0)=>{const n=t[e]+t[e+1]*2**8;return n|(n&2**15)*131070};e.readInt16LE=readInt16LE;const readUInt16BE=(t,e=0)=>t[e]*2**8+t[e+1];e.readUInt16BE=readUInt16BE;const readUInt16LE=(t,e=0)=>t[e]+t[e+1]*2**8;e.readUInt16LE=readUInt16LE;const readUInt24LE=(t,e=0)=>t[e]+t[e+1]*2**8+t[e+2]*2**16;e.readUInt24LE=readUInt24LE;const readInt32LE=(t,e=0)=>t[e]+t[e+1]*2**8+t[e+2]*2**16+(t[e+3]<<24);e.readInt32LE=readInt32LE;const readUInt32BE=(t,e=0)=>t[e]*2**24+t[e+1]*2**16+t[e+2]*2**8+t[e+3];e.readUInt32BE=readUInt32BE;const readUInt32LE=(t,e=0)=>t[e]+t[e+1]*2**8+t[e+2]*2**16+t[e+3]*2**24;e.readUInt32LE=readUInt32LE;const r={readUInt16BE:e.readUInt16BE,readUInt16LE:e.readUInt16LE,readUInt32BE:e.readUInt32BE,readUInt32LE:e.readUInt32LE};function readUInt(t,e,n,i){n=n||0;const o=i?"BE":"LE";const s="readUInt"+e+o;return r[s](t,n)}e.readUInt=readUInt;function readBox(t,n){if(t.length-n<4)return;const r=(0,e.readUInt32BE)(t,n);if(t.length-n0?r.size:8}}e.findBox=findBox},226:(t,e,n)=>{Object.defineProperty(e,"__esModule",{value:true});e.WEBP=void 0;const r=n(779);function calculateExtended(t){return{height:1+(0,r.readUInt24LE)(t,7),width:1+(0,r.readUInt24LE)(t,4)}}function calculateLossless(t){return{height:1+((t[4]&15)<<10|t[3]<<2|(t[2]&192)>>6),width:1+((t[2]&63)<<8|t[1])}}function calculateLossy(t){return{height:(0,r.readInt16LE)(t,8)&16383,width:(0,r.readInt16LE)(t,6)&16383}}e.WEBP={validate(t){const e="RIFF"===(0,r.toUTF8String)(t,0,4);const n="WEBP"===(0,r.toUTF8String)(t,8,12);const i="VP8"===(0,r.toUTF8String)(t,12,15);return e&&n&&i},calculate(t){const e=(0,r.toUTF8String)(t,12,16);t=t.slice(20,30);if(e==="VP8X"){const e=t[0];const n=(e&192)===0;const r=(e&1)===0;if(n&&r){return calculateExtended(t)}else{throw new TypeError("Invalid WebP")}}if(e==="VP8 "&&t[0]!==47){return calculateLossy(t)}const n=(0,r.toHexString)(t,3,6);if(e==="VP8L"&&n!=="9d012a"){return calculateLossless(t)}throw new TypeError("Invalid WebP")}}},112:(t,e)=>{Object.defineProperty(e,"__esModule",{value:true});e.BitReader=void 0;class BitReader{constructor(t,e){this.input=t;this.endianness=e;this.byteOffset=2;this.bitOffset=0}getBits(t=1){let e=0;let n=0;while(n=this.input.length){throw new Error("Reached end of input")}const r=this.input[this.byteOffset];const i=8-this.bitOffset;const o=Math.min(t-n,i);if(this.endianness==="little-endian"){const t=(1<>this.bitOffset&t;e|=i<>8-this.bitOffset-o;e=e<{t.exports=require("fs")}};var e={};function __nccwpck_require__(n){var r=e[n];if(r!==undefined){return r.exports}var i=e[n]={exports:{}};var o=true;try{t[n](i,i.exports,__nccwpck_require__);o=false}finally{if(o)delete e[n]}return i.exports}if(typeof __nccwpck_require__!=="undefined")__nccwpck_require__.ab=__dirname+"/";var n={};(()=>{var t=n;Object.defineProperty(t,"__esModule",{value:true});t.detector=void 0;const e=__nccwpck_require__(229);const r=Object.keys(e.typeHandlers);const i={56:"psd",66:"bmp",68:"dds",71:"gif",73:"tiff",77:"tiff",82:"webp",105:"icns",137:"png",255:"jpg"};function detector(t){const n=t[0];if(n in i){const r=i[n];if(r&&e.typeHandlers[r].validate(t)){return r}}const finder=n=>e.typeHandlers[n].validate(t);return r.find(finder)}t.detector=detector})();module.exports=n})(); \ No newline at end of file diff --git a/packages/next/src/server/image-optimizer.ts b/packages/next/src/server/image-optimizer.ts index 125849d9c0e81..39412ccee4a6f 100644 --- a/packages/next/src/server/image-optimizer.ts +++ b/packages/next/src/server/image-optimizer.ts @@ -4,6 +4,7 @@ import type { IncomingMessage, ServerResponse } from 'http' import { mediaType } from 'next/dist/compiled/@hapi/accept' import contentDisposition from 'next/dist/compiled/content-disposition' import imageSizeOf from 'next/dist/compiled/image-size' +import imageDetector from 'next/dist/compiled/image-detector' import isAnimated from 'next/dist/compiled/is-animated' import { join } from 'path' import nodeUrl, { type UrlWithParsedQuery } from 'url' @@ -232,11 +233,19 @@ export async function detectContentType( return JP2 } - const sharp = getSharp(null) - const meta = await sharp(buffer) - .metadata() - .catch((_) => null) - switch (meta?.format) { + let format: import('sharp').Metadata['format'] | undefined + format = imageDetector.detector(buffer) + + console.log('found format', format) + if (!format) { + const sharp = getSharp(null) + const meta = await sharp(buffer) + .metadata() + .catch((_) => null) + format = meta?.format + } + + switch (format) { case 'avif': return AVIF case 'webp': diff --git a/packages/next/taskfile.js b/packages/next/taskfile.js index 01f4937f0b736..640858d7cfc7b 100644 --- a/packages/next/taskfile.js +++ b/packages/next/taskfile.js @@ -660,6 +660,15 @@ export async function ncc_image_size(task, opts) { .target('src/compiled/image-size') } +// eslint-disable-next-line camelcase +externals['image-detector'] = 'next/dist/compiled/image-detector' +export async function ncc_image_detector(task, opts) { + await task + .source(relative(__dirname, require.resolve('image-size/dist/detector.js'))) + .ncc({ packageName: 'image-size', externals }) + .target('src/compiled/image-detector') +} + // eslint-disable-next-line camelcase externals['@hapi/accept'] = 'next/dist/compiled/@hapi/accept' export async function ncc_hapi_accept(task, opts) { @@ -2281,6 +2290,7 @@ export async function ncc(task, opts) { 'ncc_p_queue', 'ncc_raw_body', 'ncc_image_size', + 'ncc_image_detector', 'ncc_hapi_accept', 'ncc_commander', 'ncc_node_anser', diff --git a/packages/next/types/$$compiled.internal.d.ts b/packages/next/types/$$compiled.internal.d.ts index 946fda28e5001..c7055b9efb67d 100644 --- a/packages/next/types/$$compiled.internal.d.ts +++ b/packages/next/types/$$compiled.internal.d.ts @@ -464,6 +464,10 @@ declare module 'next/dist/compiled/image-size' { export = m } +declare module 'next/dist/compiled/image-detector' { + export function detector(arr: Uint8Array): import('sharp').Metadata['format'] +} + declare module 'next/dist/compiled/@hapi/accept' { import m from '@hapi/accept' export = m From 4e7bee46677060f4c1c25d987cb0ffc297b2caad Mon Sep 17 00:00:00 2001 From: Steven Date: Mon, 11 Aug 2025 14:50:47 -0400 Subject: [PATCH 2/6] fix --- packages/next/src/server/image-optimizer.ts | 4 ++-- packages/next/taskfile.js | 2 ++ packages/next/types/$$compiled.internal.d.ts | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/next/src/server/image-optimizer.ts b/packages/next/src/server/image-optimizer.ts index 39412ccee4a6f..52dada9c6197b 100644 --- a/packages/next/src/server/image-optimizer.ts +++ b/packages/next/src/server/image-optimizer.ts @@ -4,7 +4,7 @@ import type { IncomingMessage, ServerResponse } from 'http' import { mediaType } from 'next/dist/compiled/@hapi/accept' import contentDisposition from 'next/dist/compiled/content-disposition' import imageSizeOf from 'next/dist/compiled/image-size' -import imageDetector from 'next/dist/compiled/image-detector' +import { detector } from 'next/dist/compiled/image-detector/detector.js' import isAnimated from 'next/dist/compiled/is-animated' import { join } from 'path' import nodeUrl, { type UrlWithParsedQuery } from 'url' @@ -234,7 +234,7 @@ export async function detectContentType( } let format: import('sharp').Metadata['format'] | undefined - format = imageDetector.detector(buffer) + format = detector(buffer) console.log('found format', format) if (!format) { diff --git a/packages/next/taskfile.js b/packages/next/taskfile.js index 640858d7cfc7b..0ca17dabda499 100644 --- a/packages/next/taskfile.js +++ b/packages/next/taskfile.js @@ -663,6 +663,8 @@ export async function ncc_image_size(task, opts) { // eslint-disable-next-line camelcase externals['image-detector'] = 'next/dist/compiled/image-detector' export async function ncc_image_detector(task, opts) { + // NOTE: remove this special compile step if the upstream PR lands + // https://github.com/image-size/image-size/pull/451 await task .source(relative(__dirname, require.resolve('image-size/dist/detector.js'))) .ncc({ packageName: 'image-size', externals }) diff --git a/packages/next/types/$$compiled.internal.d.ts b/packages/next/types/$$compiled.internal.d.ts index c7055b9efb67d..936c67962f80c 100644 --- a/packages/next/types/$$compiled.internal.d.ts +++ b/packages/next/types/$$compiled.internal.d.ts @@ -464,7 +464,7 @@ declare module 'next/dist/compiled/image-size' { export = m } -declare module 'next/dist/compiled/image-detector' { +declare module 'next/dist/compiled/image-detector/detector.js' { export function detector(arr: Uint8Array): import('sharp').Metadata['format'] } From 8a0d1b1ef083dca1bfff8074554d0d13557ca5a9 Mon Sep 17 00:00:00 2001 From: Steven Date: Mon, 11 Aug 2025 14:51:30 -0400 Subject: [PATCH 3/6] remove log --- packages/next/src/server/image-optimizer.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/next/src/server/image-optimizer.ts b/packages/next/src/server/image-optimizer.ts index 52dada9c6197b..e559eef00eb88 100644 --- a/packages/next/src/server/image-optimizer.ts +++ b/packages/next/src/server/image-optimizer.ts @@ -236,7 +236,6 @@ export async function detectContentType( let format: import('sharp').Metadata['format'] | undefined format = detector(buffer) - console.log('found format', format) if (!format) { const sharp = getSharp(null) const meta = await sharp(buffer) From 7748155c44999e50f48bdba6ad8210bb0c254a81 Mon Sep 17 00:00:00 2001 From: Steven Date: Mon, 11 Aug 2025 15:16:17 -0400 Subject: [PATCH 4/6] fix types --- packages/next/src/server/image-optimizer.ts | 12 +++++++++- packages/next/types/$$compiled.internal.d.ts | 24 +++++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/packages/next/src/server/image-optimizer.ts b/packages/next/src/server/image-optimizer.ts index e559eef00eb88..71df46956615f 100644 --- a/packages/next/src/server/image-optimizer.ts +++ b/packages/next/src/server/image-optimizer.ts @@ -233,7 +233,10 @@ export async function detectContentType( return JP2 } - let format: import('sharp').Metadata['format'] | undefined + let format: + | import('sharp').Metadata['format'] + | ReturnType + | undefined format = detector(buffer) if (!format) { @@ -259,6 +262,7 @@ export async function detectContentType( case 'svg': return SVG case 'jxl': + case 'jxl-stream': return JXL case 'jp2': return JP2 @@ -267,6 +271,12 @@ export async function detectContentType( return TIFF case 'pdf': return PDF + case 'bmp': + return BMP + case 'ico': + return ICO + case 'icns': + return ICNS case 'dcraw': case 'dz': case 'exr': diff --git a/packages/next/types/$$compiled.internal.d.ts b/packages/next/types/$$compiled.internal.d.ts index 936c67962f80c..dd282b3518065 100644 --- a/packages/next/types/$$compiled.internal.d.ts +++ b/packages/next/types/$$compiled.internal.d.ts @@ -465,7 +465,29 @@ declare module 'next/dist/compiled/image-size' { } declare module 'next/dist/compiled/image-detector/detector.js' { - export function detector(arr: Uint8Array): import('sharp').Metadata['format'] + export function detector( + arr: Uint8Array + ): + | 'bmp' + | 'cur' + | 'dds' + | 'gif' + | 'heif' + | 'icns' + | 'ico' + | 'j2c' + | 'jp2' + | 'jpg' + | 'jxl' + | 'jxl-stream' + | 'ktx' + | 'png' + | 'pnm' + | 'psd' + | 'svg' + | 'tga' + | 'tiff' + | 'webp' } declare module 'next/dist/compiled/@hapi/accept' { From 38f935cdd32b850a244a9a9c2c3b74625a857133 Mon Sep 17 00:00:00 2001 From: Steven Date: Mon, 11 Aug 2025 15:19:19 -0400 Subject: [PATCH 5/6] add exhaustive check --- packages/next/src/server/image-optimizer.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/next/src/server/image-optimizer.ts b/packages/next/src/server/image-optimizer.ts index 71df46956615f..d70c9f45b3467 100644 --- a/packages/next/src/server/image-optimizer.ts +++ b/packages/next/src/server/image-optimizer.ts @@ -289,6 +289,13 @@ export async function detectContentType( case 'rad': case 'raw': case 'v': + case 'cur': + case 'dds': + case 'j2c': + case 'ktx': + case 'pnm': + case 'psd': + case 'tga': case undefined: default: return null From 684b7fdcfdcad58a76597caa306d8d3a2dfb6040 Mon Sep 17 00:00:00 2001 From: Steven Date: Mon, 11 Aug 2025 15:33:34 -0400 Subject: [PATCH 6/6] could be undefined --- packages/next/types/$$compiled.internal.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/next/types/$$compiled.internal.d.ts b/packages/next/types/$$compiled.internal.d.ts index dd282b3518065..a4a3defe6c0fe 100644 --- a/packages/next/types/$$compiled.internal.d.ts +++ b/packages/next/types/$$compiled.internal.d.ts @@ -488,6 +488,7 @@ declare module 'next/dist/compiled/image-detector/detector.js' { | 'tga' | 'tiff' | 'webp' + | undefined } declare module 'next/dist/compiled/@hapi/accept' {