Skip to content

Commit cc1c5b7

Browse files
committed
Rework disable in dev and fix path not based on __webpack_public_path__
1 parent 0c9932b commit cc1c5b7

File tree

2 files changed

+81
-47
lines changed

2 files changed

+81
-47
lines changed

src/loader.ts

Lines changed: 68 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -66,33 +66,50 @@ export type NativeIdealImageData = {
6666
export const raw = true
6767

6868
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]!]
7572
}
7673

77-
export default async function loader(this: LoaderContext<LoaderOptions>, content: Buffer) {
74+
export default async function loader(this: LoaderContext<LoaderOptions>, buffer: Buffer) {
7875
const callback = this.async()
79-
const options = this.getOptions()
8076

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)
9078
const metadata = await image.metadata()
9179
const orginalWidth = metadata.width
9280
if (!orginalWidth) {
9381
throw `Can't get the width of this image (${this.resourcePath})`
9482
}
9583

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)
96113
const preset = options.presets[queryOptions.get('preset') || 'default']
97114

98115
const sizes: number[] = []
@@ -139,7 +156,7 @@ export default async function loader(this: LoaderContext<LoaderOptions>, content
139156
// src: files[0]![files.length - 1]!,
140157
lqip,
141158
} satisfies NativeIdealImageData
142-
callback(null, `export default ${JSON.stringify(output)}`)
159+
callback(null, generateOutput(output))
143160
}
144161

145162
async function createFiles(
@@ -177,16 +194,32 @@ async function processImage(
177194
output = resized.avif({ quality: 50 })
178195
break
179196
}
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+
) {
182214
const options = context.getOptions()
183215
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
190223
}
191224

192225
async function toBase64Lqip(image: sharp.Sharp, imageType: SupportedOutputTypes) {
@@ -207,3 +240,12 @@ async function toBase64Lqip(image: sharp.Sharp, imageType: SupportedOutputTypes)
207240
const data = await output.toBuffer()
208241
return `data:${mimeType};base64,${data.toString('base64')}`
209242
}
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+
}

src/theme/NativeIdealImage.tsx

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import './NativeIdealImage.css'
77

88
export type NativeIdealImageProps = Omit<ComponentProps<'img'>, 'ref'> & {
99
/** The output of `import('ideal-img!./some-image.jpeg')` */
10-
readonly img: { default: string | NativeIdealImageData } | string | NativeIdealImageData
10+
readonly img: { default: NativeIdealImageData } | NativeIdealImageData
1111
/**
1212
* Swap (fade in) the actual image after it's fully loaded
1313
*
@@ -19,35 +19,27 @@ export type NativeIdealImageProps = Omit<ComponentProps<'img'>, 'ref'> & {
1919
swapOnLoad?: boolean
2020
}
2121

22-
// This is kinda messy handling all those posibilities at a single place >.<
2322
export default function NativeIdealImage(props: NativeIdealImageProps): JSX.Element {
2423
const { img, swapOnLoad, src, srcSet, width, height, sizes, loading, ...propsRest } = props
2524

26-
// When disableInDev in true, img will be a string or a { default: string } pointing to the image
27-
const data = typeof img === 'object' && 'default' in img ? img.default : img
28-
const enabled = typeof data === 'object'
29-
const formats = enabled ? data.formats : []
30-
const lqip = enabled && data.lqip
25+
const data = 'default' in img ? img.default : img
26+
const { lqip, formats } = data
27+
28+
const sizesAttr = sizes ?? 'auto'
3129

3230
// Put the last source on the img element and the others on source elements
3331
const sources = formats.slice(0, -1)
34-
const lastFormat = formats[formats.length - 1]
32+
const lastFormat = formats[formats.length - 1]!
3533

36-
const sizesAttr = (sizes ?? enabled) ? 'auto' : undefined
37-
const isSingleImage = formats[0]?.srcSet.length === 1
38-
const largestImage = formats[0]?.srcSet[formats[0]?.srcSet.length - 1]
34+
const isSingleImage = lastFormat?.srcSet.length === 1
35+
const largestImage = lastFormat.srcSet[lastFormat.srcSet.length - 1]!
3936

4037
let imgSrc = src
4138
let imgSrcSet = srcSet
42-
if (enabled) {
43-
// lastFormat much exist when loader is enabled
44-
if (isSingleImage) {
45-
imgSrc ??= getSource(lastFormat!.srcSet)
46-
} else {
47-
imgSrcSet ??= getSource(lastFormat!.srcSet)
48-
}
39+
if (isSingleImage) {
40+
imgSrc ??= getSource(lastFormat.srcSet)
4941
} else {
50-
imgSrc ??= data
42+
imgSrcSet ??= getSource(lastFormat.srcSet)
5143
}
5244

5345
const [placeHolderOnTop, setPlaceHolderOnTop] = useState(false)
@@ -84,9 +76,9 @@ export default function NativeIdealImage(props: NativeIdealImageProps): JSX.Elem
8476
src={imgSrc}
8577
srcSet={imgSrcSet}
8678
sizes={sizesAttr}
87-
width={(width ?? (isSingleImage || sizesAttr === 'auto')) ? largestImage?.width : undefined}
79+
width={(width ?? (isSingleImage || sizesAttr === 'auto')) ? largestImage.width : undefined}
8880
height={
89-
(height ?? (isSingleImage || sizesAttr === 'auto')) ? largestImage?.height : undefined
81+
(height ?? (isSingleImage || sizesAttr === 'auto')) ? largestImage.height : undefined
9082
}
9183
ref={imageEl}
9284
{...propsRest}

0 commit comments

Comments
 (0)