Skip to content

Commit 97a0007

Browse files
feat(website): set Early Hints at Image/Source components when preload is set (#1414)
* Add setEarlyHint to image when preload * fix properties which contains comma * bump @deco/deco@^1.127.0 * fix task check to not write files
1 parent 584a6cf commit 97a0007

3 files changed

Lines changed: 78 additions & 12 deletions

File tree

deno.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@
3434
"fast-json-patch": "npm:fast-json-patch@^3.1.1",
3535
"simple-git": "npm:simple-git@^3.25.0",
3636
"https://esm.sh/*preact-render-to-string@6.3.1": "npm:preact-render-to-string@6.4.2",
37-
"@deco/deco": "jsr:@deco/deco@^1.98.3"
37+
"@deco/deco": "jsr:@deco/deco@^1.127.0"
3838
},
3939
"lock": false,
4040
"tasks": {
41-
"check": "deno fmt && deno lint && deno check **/mod.ts",
41+
"check": "deno fmt --check && deno lint && deno check **/mod.ts",
4242
"release": "deno eval 'import \"deco/scripts/release.ts\"'",
4343
"start": "deno run -A ./scripts/start.ts",
4444
"bundle": "deno run -A jsr:@deco/deco/scripts/bundle",

website/components/Image.tsx

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Manifest } from "../manifest.gen.ts";
66
export const PATH: `/live/invoke/${keyof Manifest["loaders"]}` =
77
"/live/invoke/website/loaders/image.ts";
88

9+
export type SetEarlyHint = (hint: string) => void;
910
export type Props =
1011
& Omit<
1112
JSX.IntrinsicElements["img"],
@@ -23,6 +24,7 @@ export type Props =
2324
fetchPriority?: "high" | "low" | "auto";
2425
/** @description Object-fit */
2526
fit?: FitOptions;
27+
setEarlyHint?: SetEarlyHint;
2628
};
2729

2830
const FACTORS = [1, 2];
@@ -80,8 +82,13 @@ const optimizeVTEX = (opts: OptimizationOptions) => {
8082
const [slash, arquivos, ids, rawId, ...rest] = src.pathname.split("/");
8183
const [trueId, _w, _h] = rawId.split("-");
8284

83-
src.pathname = [slash, arquivos, ids, `${trueId}-${width}-${height}`, ...rest]
84-
.join("/");
85+
src.pathname = [
86+
slash,
87+
arquivos,
88+
ids,
89+
`${trueId}-${width}-${height}`,
90+
...rest,
91+
].join("/");
8592

8693
return src.href;
8794
};
@@ -161,6 +168,34 @@ export const getSrcSet = (
161168
return srcSet.length > 0 ? srcSet.join(", ") : undefined;
162169
};
163170

171+
export const getEarlyHintFromSrcProps = (srcProps: {
172+
imagesrcset: string | undefined;
173+
imagesizes: string | undefined;
174+
fetchpriority: "high" | "low" | "auto" | undefined;
175+
media: string | undefined;
176+
src: string;
177+
}) => {
178+
const earlyHintParts = [`<${srcProps.src}>; rel=preload; as=image`];
179+
180+
if (srcProps?.imagesrcset) {
181+
earlyHintParts.push(`; imagesrcset="${srcProps.imagesrcset}"`);
182+
}
183+
184+
if (srcProps?.imagesizes) {
185+
earlyHintParts.push(`; imagesizes="${srcProps.imagesizes}"`);
186+
}
187+
188+
if (srcProps?.fetchpriority) {
189+
earlyHintParts.push(`; fetchpriority=${srcProps.fetchpriority}`);
190+
}
191+
192+
if (srcProps?.media) {
193+
earlyHintParts.push(`; media=${srcProps.media}`);
194+
}
195+
196+
return earlyHintParts.join("");
197+
};
198+
164199
const Image = forwardRef<HTMLImageElement, Props>((props, ref) => {
165200
const { preload, loading = "lazy" } = props;
166201

@@ -173,12 +208,27 @@ const Image = forwardRef<HTMLImageElement, Props>((props, ref) => {
173208
const srcSet = props.srcSet ??
174209
getSrcSet(props.src, props.width, props.height, props.fit);
175210

176-
const linkProps = srcSet && {
177-
imagesrcset: srcSet,
178-
imagesizes: props.sizes,
179-
fetchpriority: props.fetchPriority,
180-
media: props.media,
181-
};
211+
const linkProps = srcSet &&
212+
({
213+
imagesrcset: srcSet,
214+
imagesizes: props.sizes,
215+
fetchpriority: props.fetchPriority,
216+
media: props.media,
217+
} as
218+
| ""
219+
| undefined
220+
| {
221+
imagesrcset: string;
222+
imagesizes: string | undefined;
223+
fetchpriority: "high" | "low" | "auto" | undefined;
224+
media: string | undefined;
225+
});
226+
227+
if (!IS_BROWSER && props.setEarlyHint && preload && linkProps) {
228+
props.setEarlyHint(
229+
getEarlyHintFromSrcProps({ ...linkProps, src: props.src }),
230+
);
231+
}
182232

183233
return (
184234
<>

website/components/Picture.tsx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import { useContext, useMemo } from "preact/hooks";
22
import { forwardRef } from "preact/compat";
33
import { ComponentChildren, createContext, JSX } from "preact";
4-
import { Head } from "$fresh/runtime.ts";
4+
import { Head, IS_BROWSER } from "$fresh/runtime.ts";
55

6-
import { getSrcSet } from "./Image.tsx";
6+
import {
7+
getEarlyHintFromSrcProps,
8+
getSrcSet,
9+
type SetEarlyHint,
10+
} from "./Image.tsx";
711

812
interface Context {
913
preload?: boolean;
@@ -25,6 +29,7 @@ type SourceProps =
2529
preload?: boolean;
2630
/** @description Improves Web Vitals (LCP). Use high for LCP image. Auto for other images */
2731
fetchPriority?: "high" | "low" | "auto";
32+
setEarlyHint?: SetEarlyHint;
2833
};
2934

3035
export const Source = forwardRef<HTMLSourceElement, SourceProps>(
@@ -37,8 +42,19 @@ export const Source = forwardRef<HTMLSourceElement, SourceProps>(
3742
imagesizes: props.sizes,
3843
fetchpriority: props.fetchPriority,
3944
media: props.media,
45+
} as {
46+
imagesrcset: string | undefined;
47+
imagesizes: string | undefined;
48+
fetchpriority: "high" | "low" | "auto" | undefined;
49+
media: string | undefined;
4050
};
4151

52+
if (!IS_BROWSER && preload && linkProps && props.setEarlyHint) {
53+
props.setEarlyHint(
54+
getEarlyHintFromSrcProps({ ...linkProps, src: props.src }),
55+
);
56+
}
57+
4258
return (
4359
<>
4460
{preload && (

0 commit comments

Comments
 (0)