@@ -66,33 +66,50 @@ export type NativeIdealImageData = {
66
66
export const raw = true
67
67
68
68
export function pitch ( this : LoaderContext < LoaderOptions > ) {
69
- const options = this . getOptions ( )
70
- if ( ! options . disabled ) {
71
- // Remove all other loaders,
72
- // used for preventing the default url/file loader from generating extra images
73
- this . loaders = [ this . loaders [ this . loaderIndex ] ! ]
74
- }
69
+ // Remove all other loaders,
70
+ // used for preventing the default url/file loader from generating extra images
71
+ this . loaders = [ this . loaders [ this . loaderIndex ] ! ]
75
72
}
76
73
77
- export default async function loader ( this : LoaderContext < LoaderOptions > , content : Buffer ) {
74
+ export default async function loader ( this : LoaderContext < LoaderOptions > , buffer : Buffer ) {
78
75
const callback = this . async ( )
79
- const options = this . getOptions ( )
80
76
81
- if ( options . disabled ) {
82
- // Return the value from default asset loader
83
- this . callback ( null , content )
84
- return
85
- }
86
-
87
- const queryOptions = new URLSearchParams ( this . resourceQuery )
88
-
89
- const image = sharp ( content )
77
+ const image = sharp ( buffer )
90
78
const metadata = await image . metadata ( )
91
79
const orginalWidth = metadata . width
92
80
if ( ! orginalWidth ) {
93
81
throw `Can't get the width of this image (${ this . resourcePath } )`
94
82
}
95
83
84
+ const options = this . getOptions ( )
85
+ if ( options . disabled ) {
86
+ const height = metadata . height !
87
+ const format = metadata . format !
88
+ if ( ! ( format && format in MIMES ) ) {
89
+ this . getLogger ( ) . warn (
90
+ `Can't get the format of this image or this image type is not supported (${ this . resourcePath } ), fallback to jpeg` ,
91
+ )
92
+ }
93
+ const path = emitFile ( this , {
94
+ width : orginalWidth ,
95
+ height,
96
+ buffer,
97
+ format,
98
+ } )
99
+ const output = {
100
+ formats : [
101
+ {
102
+ mime : MIMES [ format as SupportedOutputTypes ] ?? 'image/jpeg' ,
103
+ srcSet : [ { path, width : orginalWidth , height } ] ,
104
+ } ,
105
+ ] ,
106
+ } satisfies NativeIdealImageData
107
+ callback ( null , generateOutput ( output ) )
108
+ generateOutput
109
+ return
110
+ }
111
+
112
+ const queryOptions = new URLSearchParams ( this . resourceQuery )
96
113
const preset = options . presets [ queryOptions . get ( 'preset' ) || 'default' ]
97
114
98
115
const sizes : number [ ] = [ ]
@@ -139,7 +156,7 @@ export default async function loader(this: LoaderContext<LoaderOptions>, content
139
156
// src: files[0]![files.length - 1]!,
140
157
lqip,
141
158
} satisfies NativeIdealImageData
142
- callback ( null , `export default ${ JSON . stringify ( output ) } ` )
159
+ callback ( null , generateOutput ( output ) )
143
160
}
144
161
145
162
async function createFiles (
@@ -177,16 +194,32 @@ async function processImage(
177
194
output = resized . avif ( { quality : 50 } )
178
195
break
179
196
}
180
- const data = await output . toBuffer ( )
181
- const metadata = await sharp ( data ) . metadata ( )
197
+ const buffer = await output . toBuffer ( )
198
+ const metadata = await sharp ( buffer ) . metadata ( )
199
+ const width = metadata . width !
200
+ const height = metadata . height !
201
+ const path = emitFile ( context , { width, height, buffer, format } )
202
+ return { path, width, height }
203
+ }
204
+
205
+ function emitFile (
206
+ context : LoaderContext < LoaderOptions > ,
207
+ data : {
208
+ buffer : Buffer
209
+ width : number
210
+ height : number
211
+ format : string
212
+ } ,
213
+ ) {
182
214
const options = context . getOptions ( )
183
215
const path = loaderUtils
184
- . interpolateName ( context , options . fileNameTemplate , { content : data } )
185
- . replaceAll ( '[width]' , String ( metadata . width ) )
186
- . replaceAll ( '[height]' , String ( metadata . height ) )
187
- . replaceAll ( '[format]' , format )
188
- context . emitFile ( path , data )
189
- return { path, width : metadata . width ! , height : metadata . height ! }
216
+ . interpolateName ( context , options . fileNameTemplate , { content : data . buffer } )
217
+ . replaceAll ( '[width]' , String ( data . width ) )
218
+ . replaceAll ( '[height]' , String ( data . height ) )
219
+ . replaceAll ( '[format]' , data . format )
220
+ context . emitFile ( path , data . buffer )
221
+ /** __webpack_public_path__ is handled by {@link generateOutput} */
222
+ return '__webpack_public_path__' + path
190
223
}
191
224
192
225
async function toBase64Lqip ( image : sharp . Sharp , imageType : SupportedOutputTypes ) {
@@ -207,3 +240,12 @@ async function toBase64Lqip(image: sharp.Sharp, imageType: SupportedOutputTypes)
207
240
const data = await output . toBuffer ( )
208
241
return `data:${ mimeType } ;base64,${ data . toString ( 'base64' ) } `
209
242
}
243
+
244
+ function generateOutput ( output : NativeIdealImageData ) {
245
+ const out = JSON . stringify ( output )
246
+ // Replace __webpack_public_path__ literal with __webpack_public_path__ variable
247
+ // { "path": "__webpack_public_path__/assets/img.jpeg" }
248
+ // to
249
+ // { "path": __webpack_public_path__ + "/assets/img.jpeg" }
250
+ return `export default ${ out . replaceAll ( `"__webpack_public_path__` , `__webpack_public_path__ + "` ) } `
251
+ }
0 commit comments