Skip to content

Commit

Permalink
feat: Same domain images (#171)
Browse files Browse the repository at this point in the history
  • Loading branch information
tlgimenes authored Aug 4, 2023
1 parent 55c5df4 commit 12b6629
Show file tree
Hide file tree
Showing 26 changed files with 937 additions and 238 deletions.
99 changes: 1 addition & 98 deletions components/Image.tsx
Original file line number Diff line number Diff line change
@@ -1,98 +1 @@
import { Head } from "$fresh/runtime.ts";
import { forwardRef } from "preact/compat";
import ImageKit from "https://esm.sh/[email protected]";
import type { JSX } from "preact";

type Props =
& Omit<JSX.IntrinsicElements["img"], "width" | "height" | "preload">
& {
width: number;
height?: number;
src: string;
preload?: boolean;
fetchPriority?: "high" | "low" | "auto";
};

const imageKit = new ImageKit({
urlEndpoint: "https://ik.imagekit.io/decocx",
});

const FACTORS = [1, 1.5, 2];

export const getOptimizedMediaUrl = (
{ originalSrc, width, height, factor }: {
originalSrc: string;
width: number;
height?: number;
factor: number;
},
) => {
const w = `${Math.trunc(factor * width)}`;
const h = height ? `${Math.trunc(factor * height)}` : undefined;

if (
originalSrc.includes(".vteximg.com.br/arquivos/ids") ||
originalSrc.includes(".vtexassets.com/arquivos/ids")
) {
const url = new URL(originalSrc);

const [slash, arquivos, ids, id, ...rest] = url.pathname.split("/");
const [realId] = id.split("-");
url.protocol = "https:";
url.pathname = [slash, arquivos, ids, `${realId}-${w}-${h ?? w}`, ...rest]
.join("/");

return url.href;
}

return imageKit.url({
path: originalSrc,
transformation: [{ width: w, height: h }],
});
};

export const getSrcSet = (src: string, width: number, height?: number) =>
FACTORS
.map((factor) =>
`${getOptimizedMediaUrl({ originalSrc: src, width, height, factor })} ${
Math.trunc(factor * width)
}w`
)
.join(", ");

const Image = forwardRef<HTMLImageElement, Props>((props, ref) => {
const { preload, loading = "lazy" } = props;

const srcSet = getSrcSet(props.src, props.width, props.height);
const linkProps = {
imagesrcset: srcSet,
imagesizes: props.sizes,
fetchpriority: props.fetchPriority,
media: props.media,
};

return (
<>
{preload && (
<Head>
<link
as="image"
rel="preload"
href={props.src}
{...linkProps}
/>
</Head>
)}
<img
{...props}
preload={undefined}
src={props.src}
srcSet={srcSet}
loading={loading}
ref={ref}
/>
</>
);
});

export default Image;
export { default } from "deco-sites/std/packs/image/components/Image.tsx";
83 changes: 4 additions & 79 deletions components/Picture.tsx
Original file line number Diff line number Diff line change
@@ -1,79 +1,4 @@
import { useContext, useMemo } from "preact/hooks";
import { forwardRef } from "preact/compat";
import { ComponentChildren, createContext, JSX } from "preact";
import { Head } from "$fresh/runtime.ts";

import { getSrcSet } from "./Image.tsx";

interface Context {
preload?: boolean;
}

const Context = createContext<Context>({
preload: false,
});

type SourceProps =
& Omit<JSX.IntrinsicElements["source"], "width" | "height" | "preload">
& {
src: string;
width: number;
height?: number;
preload?: boolean;
fetchPriority?: "high" | "low" | "auto";
};

export const Source = forwardRef<HTMLSourceElement, SourceProps>(
(props, ref) => {
const { preload } = useContext(Context);

const srcSet = getSrcSet(props.src, props.width, props.height);
const linkProps = {
imagesrcset: srcSet,
imagesizes: props.sizes,
fetchpriority: props.fetchPriority,
media: props.media,
};

return (
<>
{preload && (
<Head>
<link
as="image"
rel="preload"
href={props.src}
{...linkProps}
/>
</Head>
)}
<source
{...props}
preload={undefined}
src={undefined} // Avoid deprecated api lighthouse warning
srcSet={srcSet}
ref={ref}
/>
</>
);
},
);

type Props = Omit<JSX.IntrinsicElements["picture"], "preload"> & {
children: ComponentChildren;
preload?: boolean;
};

export const Picture = forwardRef<HTMLPictureElement, Props>(
({ children, preload, ...props }, ref) => {
const value = useMemo(() => ({ preload }), [preload]);

return (
<Context.Provider value={value}>
<picture {...props} ref={ref}>
{children}
</picture>
</Context.Provider>
);
},
);
export {
Picture,
Source,
} from "deco-sites/std/packs/image/components/Picture.tsx";
45 changes: 1 addition & 44 deletions components/Video.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1 @@
/**
* TODO: Implement video preload with link[rel="preload"] tags once
* browsers support it. More info at: https://stackoverflow.com/a/68368601
*/
import { forwardRef } from "preact/compat";
import type { JSX } from "preact";

import { getOptimizedMediaUrl, getSrcSet } from "./Image.tsx";

type Props =
& Omit<JSX.IntrinsicElements["video"], "width" | "height" | "preload">
& {
width: number;
height: number;
src: string;
forceOptimizedSrc?: boolean;
};

const Video = forwardRef<HTMLVideoElement, Props>((props, ref) => {
const { loading = "lazy" } = props;
const srcSet = getSrcSet(props.src, props.width, props.height);

const src = props.forceOptimizedSrc
? getOptimizedMediaUrl({
originalSrc: props.src,
width: props.width,
height: props.height,
factor: 1,
})
: props.src;

return (
<video
{...props}
preload={undefined}
src={src}
srcSet={srcSet}
loading={loading}
ref={ref}
/>
);
});

export default Video;
export { default } from "deco-sites/std/packs/image/components/Video.tsx";
10 changes: 6 additions & 4 deletions live.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,9 @@ import * as $$$22 from "./loaders/vnda/cart.ts";
import * as $$$23 from "./loaders/nuvemShop/nuvemShopProductDetailsPage.ts";
import * as $$$24 from "./loaders/nuvemShop/nuvemShopProductList.ts";
import * as $$$25 from "./loaders/nuvemShop/nuvemShopProductListingPage.ts";
import * as $$$26 from "./loaders/x/redirectsFromCsv.ts";
import * as $$$27 from "./loaders/x/font.ts";
import * as $$$26 from "./loaders/x/image.ts";
import * as $$$27 from "./loaders/x/redirectsFromCsv.ts";
import * as $$$28 from "./loaders/x/font.ts";
import * as $$$$0 from "./routes/404.tsx";
import * as $$$$1 from "./routes/styles.css.ts";
import * as $$$$2 from "./routes/_app.tsx";
Expand Down Expand Up @@ -223,8 +224,9 @@ const manifest = {
"deco-sites/std/loaders/vtex/proxy.ts": $$$11,
"deco-sites/std/loaders/vtex/user.ts": $$$17,
"deco-sites/std/loaders/vtex/wishlist.ts": $$$9,
"deco-sites/std/loaders/x/font.ts": $$$27,
"deco-sites/std/loaders/x/redirectsFromCsv.ts": $$$26,
"deco-sites/std/loaders/x/font.ts": $$$28,
"deco-sites/std/loaders/x/image.ts": $$$26,
"deco-sites/std/loaders/x/redirectsFromCsv.ts": $$$27,
},
"routes": {
"./routes/_app.tsx": $$$$2,
Expand Down
1 change: 1 addition & 0 deletions loaders/x/image.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "deco-sites/std/packs/image/loaders/image.ts";
86 changes: 86 additions & 0 deletions packs/image/components/Image.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { Head } from "$fresh/runtime.ts";
import type { JSX } from "preact";
import { forwardRef } from "preact/compat";
import { KEY } from "../constants.ts";

type Props =
& Omit<JSX.IntrinsicElements["img"], "width" | "height" | "preload">
& {
src: string;
/** @description Improves Web Vitals (CLS|LCP) */
width: number;
/** @description Improves Web Vitals (CLS|LCP) */
height?: number;
/** @description Web Vitals (LCP). Adds a link[rel="preload"] tag in head. Use one preload per page for better performance */
preload?: boolean;
/** @description Improves Web Vitals (LCP). Use high for LCP image. Auto for other images */
fetchPriority?: "high" | "low" | "auto";
};

const FACTORS = [1, 1.5, 2];

export const getOptimizedMediaUrl = (
{ originalSrc, width, height, factor }: {
originalSrc: string;
width: number;
height?: number;
factor: number;
},
) => {
const params = new URLSearchParams();

params.set("src", originalSrc);
params.set("fit", "contain");
params.set("width", `${Math.trunc(factor * width)}`);
height && params.set("height", `${Math.trunc(factor * height)}`);

return `${KEY}?${params}`;
};

export const getSrcSet = (src: string, width: number, height?: number) =>
FACTORS
.map((factor) =>
`${getOptimizedMediaUrl({ originalSrc: src, width, height, factor })} ${
Math.trunc(factor * width)
}w`
)
.join(", ");

const Image = forwardRef<HTMLImageElement, Props>((props, ref) => {
const { preload, loading = "lazy" } = props;

if (!props.height) console.warn(`Missing height on image: ${props.src}`);

const srcSet = getSrcSet(props.src, props.width, props.height);
const linkProps = {
imagesrcset: srcSet,
imagesizes: props.sizes,
fetchpriority: props.fetchPriority,
media: props.media,
};

return (
<>
{preload && (
<Head>
<link
as="image"
rel="preload"
href={props.src}
{...linkProps}
/>
</Head>
)}
<img
{...props}
preload={undefined}
src={props.src}
srcSet={srcSet}
loading={loading}
ref={ref}
/>
</>
);
});

export default Image;
Loading

0 comments on commit 12b6629

Please sign in to comment.