Merge pull request #226 from acid-info/v1.1-lazylaoding

V1.1 lazylaoding
This commit is contained in:
Hossein Mehrabi 2024-01-22 17:29:53 +03:30 committed by GitHub
commit 25727c9e18
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 351 additions and 44 deletions

1
.gitignore vendored
View File

@ -38,3 +38,4 @@ next-env.d.ts
public/rss.xml
public/atom*.xml
public/images/placeholders/*

View File

@ -60,6 +60,7 @@
"react-player": "^2.12.0",
"react-quick-pinch-zoom": "^4.9.0",
"react-use": "^17.4.0",
"sharp": "^0.33.2",
"slugify": "^1.6.6",
"typescript": "5.0.4",
"use-query-params": "^2.2.1",

View File

@ -13,11 +13,13 @@ export type PostCardCoverProps = React.ComponentProps<typeof Link> & {
imageProps: ResponsiveImageProps
imageData: LPE.Image.Document
playIcon?: boolean
loadDelay?: number
}
export const PostCardCover: FC<PostCardCoverProps> = ({
imageProps,
imageData,
playIcon,
loadDelay = 0,
...props
}) => {
return (
@ -25,7 +27,7 @@ export const PostCardCover: FC<PostCardCoverProps> = ({
{...props}
className={`post-card__cover-image ${props.className}`}
>
<ResponsiveImage {...imageProps} data={imageData}>
<ResponsiveImage {...imageProps} data={imageData} loadDelay={loadDelay}>
{playIcon && (
<Icon size="small">
<UnfilledPlayIcon />

View File

@ -21,6 +21,7 @@ import { PostCardLabel } from './PostCard.Label'
export type PostAppearanceProps = {
imageProps?: ResponsiveImageProps
loadDelay?: number
}
export type PostDataProps = {
@ -47,7 +48,7 @@ export type PostCardProps = CommonProps &
export const PostCard = (_props: PostCardProps) => {
const {
appearance: { imageProps = {} } = {},
appearance: { imageProps = {}, loadDelay = 0 } = {},
data: {
coverImage = null,
date,
@ -77,6 +78,7 @@ export const PostCard = (_props: PostCardProps) => {
imageProps={imageProps}
imageData={coverImage}
playIcon={contentType === LPE.PostTypes.Podcast}
loadDelay={loadDelay}
/>
) : (
<div className="post-card__cover-image"></div>

View File

@ -4,7 +4,7 @@ import { SerializedStyles, css } from '@emotion/react'
import styled from '@emotion/styled'
import React, { useMemo } from 'react'
import { LPE } from '../../types/lpe.types'
import { chunkArray } from '../../utils/array.utils'
import { chunkArray, shuffleArray } from '../../utils/array.utils'
import { lsdUtils } from '../../utils/lsd.utils'
import { lcm } from '../../utils/math.utils'
import { PostCard, PostCardProps } from '../PostCard'
@ -54,6 +54,10 @@ export const PostsGrid: React.FC<PostsGridProps> = ({
[theme],
)
const loadingDelayEffectIndexes = useMemo(() => {
return shuffleArray(items.map((_, i) => i))
}, [items])
return (
<Container
{...props}
@ -64,7 +68,7 @@ export const PostsGrid: React.FC<PostsGridProps> = ({
postCardStyles={postCardStyles}
>
<div className="row">
{items.map(({ post, size }) => (
{items.map(({ post, size }, index) => (
<div key={post.id} className="post-card-wrapper">
<PostCard
size={size as any}
@ -74,6 +78,9 @@ export const PostsGrid: React.FC<PostsGridProps> = ({
displayYear={displayYear}
displayPodcastShow={displayPodcastShow}
data={PostCard.toData(post, shows)}
appearance={{
loadDelay: loadingDelayEffectIndexes[index] * 100,
}}
/>
</div>
))}

View File

@ -1,5 +1,5 @@
import styled from '@emotion/styled'
import Image, { ImageLoader, ImageProps } from 'next/image'
import Image, { ImageProps } from 'next/image'
import React, { useState } from 'react'
import { LPE } from '../../types/lpe.types'
@ -8,6 +8,7 @@ export type ResponsiveImageProps = {
nextImageProps?: Partial<ImageProps>
fill?: boolean
className?: string
loadDelay?: number
}
export type Props = {
@ -15,9 +16,6 @@ export type Props = {
alt?: string
} & ResponsiveImageProps
const unbodyImageLoader: ImageLoader = ({ src, width, quality }) =>
`${src}?w=${width}&q=${quality || 75}&auto=format`
export const ResponsiveImage = ({
data,
height,
@ -25,22 +23,22 @@ export const ResponsiveImage = ({
nextImageProps,
className,
children,
loadDelay = 0,
}: React.PropsWithChildren<Props>) => {
const [loaded, setLoaded] = useState(false)
const lazyUrl = `${data.url}?blur=200&px=16&auto=format`
const imageProps: ImageProps = {
src: `${data.url}`,
width: data.width,
height: data.height,
alt: data.alt,
className: loaded ? 'loaded' : '',
onLoad: () => setLoaded(true),
onLoad: () => {
setTimeout(() => {
setLoaded(true)
}, loadDelay)
},
loading: 'lazy',
...(data.url.startsWith('https://images.cdn.unbody.io')
? { loader: unbodyImageLoader }
: {}),
...(nextImageProps || {}),
style: {
width: '100%',
@ -57,7 +55,11 @@ export const ResponsiveImage = ({
}}
>
<div className="comment">
<img src={lazyUrl} alt={data.alt} title={data.alt} />
<img
src={data.placeholder?.replace('public/', '/')}
alt={data.alt}
title={data.alt}
/>
{children}
</div>
<div className={imageProps.className}>
@ -94,7 +96,7 @@ const Container = styled.div`
&:last-of-type {
opacity: 0;
transition: opacity 250ms;
transition: opacity 500ms;
&.loaded {
opacity: 1;

View File

@ -0,0 +1 @@
export const POSTS_IMAGE_PLACEHOLDER_DIR = `public/images/placeholders`

View File

@ -15,4 +15,10 @@ export const uiConfigs = {
numberOfImagesShowInTopResult: 3,
numberOfTotalBlocksInListView: 20,
},
imageRender: {
placeholder: {
pixelation: 0.5,
blur: 0,
},
},
}

View File

@ -0,0 +1,62 @@
import { POSTS_IMAGE_PLACEHOLDER_DIR } from '@/configs/consts.configs'
import { uiConfigs } from '@/configs/ui.configs'
import {
getStrapiImageUrlBySize,
transformStrapiImageUrl,
} from '@/services/strapi/transformers/utils'
import axios from 'axios'
import sharp from 'sharp'
export class PlaceholderService {
cache = new Map<string, string>()
constructor() {}
add(key: string, value: string) {
this.cache.set(key, value)
}
exists(key: string): boolean {
return this.cache.has(key)
}
emptyCache() {
this.cache.clear()
}
async pixelate(imagePath: string): Promise<string> {
if (imagePath.length === 0 || imagePath.endsWith('.svg')) return ''
const fileName = imagePath.split('/').pop() as string
if (this.exists(fileName)) return this.cache.get(fileName) as string
const filePath = `${POSTS_IMAGE_PLACEHOLDER_DIR}/${fileName}`
const thumbnailPath = getStrapiImageUrlBySize('thumbnail', imagePath)
const imageUrl = transformStrapiImageUrl(thumbnailPath)
const imageBuffer = (
await axios({ url: imageUrl, responseType: 'arraybuffer' })
).data as Buffer
try {
await sharp(
await sharp(imageBuffer)
.resize(uiConfigs.imageRender.placeholder.pixelation * 100, null, {
kernel: sharp.kernel.cubic,
})
.toBuffer(),
)
.resize(uiConfigs.imageRender.placeholder.pixelation * 400, null, {
kernel: sharp.kernel.nearest,
})
.toFile(filePath)
this.add(fileName, filePath)
return filePath
} catch (e) {
console.log(e)
}
return ''
}
}

View File

@ -19,9 +19,9 @@ export const episodeTransformer: Transformer<
transform: async (helpers, data, original, root, ctx) => {
return {
...data,
credits: transformStrapiHtmlContent({
credits: await transformStrapiHtmlContent({
html: original.attributes.credits || '',
}).blocks as LPE.Post.TextBlock[],
}).then((c) => c.blocks as LPE.Post.TextBlock[]),
channels: original.attributes.channel
? await transformChannels(original.attributes.channel)
: [],

View File

@ -15,7 +15,7 @@ export const podcastShowTransformer: Transformer<
classes: ['podcast'],
objectType: 'PodcastShow',
isMatch: (helpers, object) => object.__typename === 'PodcastShowEntity',
transform: (helpers, data, original, root, ctx) => {
transform: async (helpers, data, original, root, ctx) => {
const { id, attributes } = data
return {
@ -25,15 +25,15 @@ export const podcastShowTransformer: Transformer<
numberOfEpisodes: 0,
url: getPostLink('podcast', { showSlug: attributes.slug }),
description: attributes.description,
descriptionText: transformStrapiHtmlContent({
descriptionText: await transformStrapiHtmlContent({
html: attributes.description || '',
}).text,
}).then((h) => h.text),
hosts: attributes.hosts.data.map((host) => ({
id: '',
name: host.attributes.name,
emailAddress: host.attributes.email_address,
})),
logo: transformStrapiImageData(attributes.logo),
logo: await transformStrapiImageData(attributes.logo),
}
},
}

View File

@ -15,7 +15,7 @@ export const postTransformer: Transformer<
classes: ['post'],
objectType: 'Post',
isMatch: (helpers, object) => object.__typename === 'PostEntity',
transform: (helpers, data, original, root, ctx) => {
transform: async (helpers, data, original, root, ctx) => {
const { id, attributes } = data
const type = attributes.type
@ -26,7 +26,7 @@ export const postTransformer: Transformer<
const isHighlighted = attributes.featured
const isDraft = !attributes.publishedAt
const coverImage: LPE.Post.Document['coverImage'] =
transformStrapiImageData(attributes.cover_image)
await transformStrapiImageData(attributes.cover_image)
const tags: LPE.Tag.Document[] = attributes.tags.data.map((tag) => ({
id: tag.id,
name: tag.attributes.name,
@ -38,15 +38,15 @@ export const postTransformer: Transformer<
emailAddress: author.attributes.email_address,
}))
const summary = transformStrapiHtmlContent({
const summary = await transformStrapiHtmlContent({
html: attributes.summary || '',
}).text
}).then((h) => h.text)
const {
blocks: content,
toc,
text,
} = transformStrapiHtmlContent({
} = await transformStrapiHtmlContent({
html: attributes.body || '',
})

View File

@ -14,7 +14,7 @@ export const staticPageTransformer: Transformer<
classes: ['static_page'],
objectType: 'StaticPage',
isMatch: (helpers, object) => object.__typename === 'PageEntity',
transform: (helpers, data, original, root, ctx) => {
transform: async (helpers, data, original, root, ctx) => {
const { id, attributes } = data
const title = attributes.title
@ -22,7 +22,7 @@ export const staticPageTransformer: Transformer<
const slug = attributes.slug
const description = attributes.description
const { blocks: content } = transformStrapiHtmlContent({
const { blocks: content } = await transformStrapiHtmlContent({
html: attributes.body || '',
})

View File

@ -1,3 +1,5 @@
import { PlaceholderService } from '@/services/images.service'
import { isVercel } from '@/utils/env.utils'
import * as htmlParser from 'node-html-parser'
import slugify from 'slugify'
import { UploadFileEntity } from '../../../lib/strapi/strapi.generated'
@ -7,18 +9,27 @@ import { convertToIframe } from '../../../utils/string.utils'
let assetsBaseUrl = process.env.NEXT_PUBLIC_ASSETS_BASE_URL ?? ''
if (assetsBaseUrl.endsWith('/')) assetsBaseUrl = assetsBaseUrl.slice(1)
export const transformStrapiImageUrl = (url: string): string =>
assetsBaseUrl + url
const placeholderService = new PlaceholderService()
placeholderService.emptyCache()
export const transformStrapiImageData = (
image:
| Pick<UploadFileEntity, 'attributes'>
| {
data: {
attributes: Partial<UploadFileEntity['attributes']>
}
},
): LPE.Image.Document => {
// TODO: remove this and move it to a proper place
type StrapiImage =
| Pick<UploadFileEntity, 'attributes'>
| {
data: {
attributes: Partial<UploadFileEntity['attributes']>
}
}
export const getStrapiImageUrlBySize = (size: string, url: string): string =>
url.replace('/uploads/', `/uploads/${size}_`)
export const transformStrapiImageUrl = (filePath: string): string =>
assetsBaseUrl + filePath
export const transformStrapiImageData = async (
image: StrapiImage,
): Promise<LPE.Image.Document> => {
const attributes = 'data' in image ? image.data.attributes : image.attributes
return {
@ -27,19 +38,27 @@ export const transformStrapiImageData = (
caption: attributes.caption || '',
alt: attributes.caption || attributes.alternativeText || '',
url: attributes.url ? transformStrapiImageUrl(attributes.url) : '',
placeholder: attributes.url
? isVercel()
? getStrapiImageUrlBySize(
'thumbnail',
transformStrapiImageUrl(attributes.url),
)
: await placeholderService.pixelate(attributes.url)
: '',
}
}
export const transformStrapiHtmlContent = ({
export const transformStrapiHtmlContent = async ({
html,
}: {
html: string
}): {
}): Promise<{
toc: LPE.Post.TocItem[]
blocks: LPE.Post.ContentBlock[]
html: string
text: string
} => {
}> => {
const toc: LPE.Post.TocItem[] = []
const blocks: LPE.Post.ContentBlock[] = []

View File

@ -24,6 +24,7 @@ export namespace LPE {
height: number
caption?: string
captionHTML?: string
placeholder?: string
}
}

View File

@ -18,3 +18,17 @@ export const chunkArray = <T>(arr: T[], ...pattern: number[]): T[][] => {
return result
}
export const shuffleArray = <T>(arr: T[]): T[] => {
const result = [...arr]
for (let i = result.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1))
const temp = result[i]
result[i] = result[j]
result[j] = temp
}
return result
}

191
yarn.lock
View File

@ -658,6 +658,13 @@
tslib "^2.6.1"
ws "^8.13.0"
"@emnapi/runtime@^0.45.0":
version "0.45.0"
resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-0.45.0.tgz#e754de04c683263f34fd0c7f32adfe718bbe4ddd"
integrity sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==
dependencies:
tslib "^2.4.0"
"@emotion/babel-plugin@^11.10.6":
version "11.10.6"
resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.10.6.tgz#a68ee4b019d661d6f37dec4b8903255766925ead"
@ -1284,6 +1291,119 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
"@img/sharp-darwin-arm64@0.33.2":
version "0.33.2"
resolved "https://registry.yarnpkg.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.2.tgz#0a52a82c2169112794dac2c71bfba9e90f7c5bd1"
integrity sha512-itHBs1rPmsmGF9p4qRe++CzCgd+kFYktnsoR1sbIAfsRMrJZau0Tt1AH9KVnufc2/tU02Gf6Ibujx+15qRE03w==
optionalDependencies:
"@img/sharp-libvips-darwin-arm64" "1.0.1"
"@img/sharp-darwin-x64@0.33.2":
version "0.33.2"
resolved "https://registry.yarnpkg.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.2.tgz#982e26bb9d38a81f75915c4032539aed621d1c21"
integrity sha512-/rK/69Rrp9x5kaWBjVN07KixZanRr+W1OiyKdXcbjQD6KbW+obaTeBBtLUAtbBsnlTTmWthw99xqoOS7SsySDg==
optionalDependencies:
"@img/sharp-libvips-darwin-x64" "1.0.1"
"@img/sharp-libvips-darwin-arm64@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.1.tgz#81e83ffc2c497b3100e2f253766490f8fad479cd"
integrity sha512-kQyrSNd6lmBV7O0BUiyu/OEw9yeNGFbQhbxswS1i6rMDwBBSX+e+rPzu3S+MwAiGU3HdLze3PanQ4Xkfemgzcw==
"@img/sharp-libvips-darwin-x64@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.1.tgz#fc1fcd9d78a178819eefe2c1a1662067a83ab1d6"
integrity sha512-eVU/JYLPVjhhrd8Tk6gosl5pVlvsqiFlt50wotCvdkFGf+mDNBJxMh+bvav+Wt3EBnNZWq8Sp2I7XfSjm8siog==
"@img/sharp-libvips-linux-arm64@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.1.tgz#26eb8c556a9b0db95f343fc444abc3effb67ebcf"
integrity sha512-bnGG+MJjdX70mAQcSLxgeJco11G+MxTz+ebxlz8Y3dxyeb3Nkl7LgLI0mXupoO+u1wRNx/iRj5yHtzA4sde1yA==
"@img/sharp-libvips-linux-arm@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.1.tgz#2a377b959ff7dd6528deee486c25461296a4fa8b"
integrity sha512-FtdMvR4R99FTsD53IA3LxYGghQ82t3yt0ZQ93WMZ2xV3dqrb0E8zq4VHaTOuLEAuA83oDawHV3fd+BsAPadHIQ==
"@img/sharp-libvips-linux-s390x@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.1.tgz#af28ac9ba929204467ecdf843330d791e9421e10"
integrity sha512-3+rzfAR1YpMOeA2zZNp+aYEzGNWK4zF3+sdMxuCS3ey9HhDbJ66w6hDSHDMoap32DueFwhhs3vwooAB2MaK4XQ==
"@img/sharp-libvips-linux-x64@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.1.tgz#4273d182aa51912e655e1214ea47983d7c1f7f8d"
integrity sha512-3NR1mxFsaSgMMzz1bAnnKbSAI+lHXVTqAHgc1bgzjHuXjo4hlscpUxc0vFSAPKI3yuzdzcZOkq7nDPrP2F8Jgw==
"@img/sharp-libvips-linuxmusl-arm64@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.1.tgz#d150c92151cea2e8d120ad168b9c358d09c77ce8"
integrity sha512-5aBRcjHDG/T6jwC3Edl3lP8nl9U2Yo8+oTl5drd1dh9Z1EBfzUKAJFUDTDisDjUwc7N4AjnPGfCA3jl3hY8uDg==
"@img/sharp-libvips-linuxmusl-x64@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.1.tgz#e297c1a4252c670d93b0f9e51fca40a7a5b6acfd"
integrity sha512-dcT7inI9DBFK6ovfeWRe3hG30h51cBAP5JXlZfx6pzc/Mnf9HFCQDLtYf4MCBjxaaTfjCCjkBxcy3XzOAo5txw==
"@img/sharp-linux-arm64@0.33.2":
version "0.33.2"
resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.2.tgz#af3409f801a9bee1d11d0c7e971dcd6180f80022"
integrity sha512-pz0NNo882vVfqJ0yNInuG9YH71smP4gRSdeL09ukC2YLE6ZyZePAlWKEHgAzJGTiOh8Qkaov6mMIMlEhmLdKew==
optionalDependencies:
"@img/sharp-libvips-linux-arm64" "1.0.1"
"@img/sharp-linux-arm@0.33.2":
version "0.33.2"
resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.2.tgz#181f7466e6ac074042a38bfb679eb82505e17083"
integrity sha512-Fndk/4Zq3vAc4G/qyfXASbS3HBZbKrlnKZLEJzPLrXoJuipFNNwTes71+Ki1hwYW5lch26niRYoZFAtZVf3EGA==
optionalDependencies:
"@img/sharp-libvips-linux-arm" "1.0.1"
"@img/sharp-linux-s390x@0.33.2":
version "0.33.2"
resolved "https://registry.yarnpkg.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.2.tgz#9c171f49211f96fba84410b3e237b301286fa00f"
integrity sha512-MBoInDXDppMfhSzbMmOQtGfloVAflS2rP1qPcUIiITMi36Mm5YR7r0ASND99razjQUpHTzjrU1flO76hKvP5RA==
optionalDependencies:
"@img/sharp-libvips-linux-s390x" "1.0.1"
"@img/sharp-linux-x64@0.33.2":
version "0.33.2"
resolved "https://registry.yarnpkg.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.2.tgz#b956dfc092adc58c2bf0fae2077e6f01a8b2d5d7"
integrity sha512-xUT82H5IbXewKkeF5aiooajoO1tQV4PnKfS/OZtb5DDdxS/FCI/uXTVZ35GQ97RZXsycojz/AJ0asoz6p2/H/A==
optionalDependencies:
"@img/sharp-libvips-linux-x64" "1.0.1"
"@img/sharp-linuxmusl-arm64@0.33.2":
version "0.33.2"
resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.2.tgz#10e0ec5a79d1234c6a71df44c9f3b0bef0bc0f15"
integrity sha512-F+0z8JCu/UnMzg8IYW1TMeiViIWBVg7IWP6nE0p5S5EPQxlLd76c8jYemG21X99UzFwgkRo5yz2DS+zbrnxZeA==
optionalDependencies:
"@img/sharp-libvips-linuxmusl-arm64" "1.0.1"
"@img/sharp-linuxmusl-x64@0.33.2":
version "0.33.2"
resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.2.tgz#29e0030c24aa27c38201b1fc84e3d172899fcbe0"
integrity sha512-+ZLE3SQmSL+Fn1gmSaM8uFusW5Y3J9VOf+wMGNnTtJUMUxFhv+P4UPaYEYT8tqnyYVaOVGgMN/zsOxn9pSsO2A==
optionalDependencies:
"@img/sharp-libvips-linuxmusl-x64" "1.0.1"
"@img/sharp-wasm32@0.33.2":
version "0.33.2"
resolved "https://registry.yarnpkg.com/@img/sharp-wasm32/-/sharp-wasm32-0.33.2.tgz#38d7c740a22de83a60ad1e6bcfce17462b0d4230"
integrity sha512-fLbTaESVKuQcpm8ffgBD7jLb/CQLcATju/jxtTXR1XCLwbOQt+OL5zPHSDMmp2JZIeq82e18yE0Vv7zh6+6BfQ==
dependencies:
"@emnapi/runtime" "^0.45.0"
"@img/sharp-win32-ia32@0.33.2":
version "0.33.2"
resolved "https://registry.yarnpkg.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.2.tgz#09456314e223f68e5417c283b45c399635c16202"
integrity sha512-okBpql96hIGuZ4lN3+nsAjGeggxKm7hIRu9zyec0lnfB8E7Z6p95BuRZzDDXZOl2e8UmR4RhYt631i7mfmKU8g==
"@img/sharp-win32-x64@0.33.2":
version "0.33.2"
resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.2.tgz#148e96dfd6e68747da41a311b9ee4559bb1b1471"
integrity sha512-E4magOks77DK47FwHUIGH0RYWSgRBfGdK56kIHSVeB9uIS4pPFr4N2kIVsXdQQo4LzOsENKV5KAhRlRL7eMAdg==
"@imgix/js-core@^3.1.3":
version "3.8.0"
resolved "https://registry.yarnpkg.com/@imgix/js-core/-/js-core-3.8.0.tgz#630bc4fba8cb968d8c0e8298a2be71e39d75b8b8"
@ -2328,11 +2448,27 @@ color-name@1.1.3:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
color-name@^1.1.4, color-name@~1.1.4:
color-name@^1.0.0, color-name@^1.1.4, color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
color-string@^1.9.0:
version "1.9.1"
resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4"
integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==
dependencies:
color-name "^1.0.0"
simple-swizzle "^0.2.2"
color@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/color/-/color-4.2.3.tgz#d781ecb5e57224ee43ea9627560107c0e0c6463a"
integrity sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==
dependencies:
color-convert "^2.0.1"
color-string "^1.9.0"
colorette@^2.0.16, colorette@^2.0.19:
version "2.0.20"
resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a"
@ -2604,6 +2740,11 @@ detect-libc@^1.0.3:
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==
detect-libc@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.2.tgz#8ccf2ba9315350e1241b88d0ac3b0e1fbd99605d"
integrity sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==
dir-glob@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
@ -3738,6 +3879,11 @@ is-arrayish@^0.2.1:
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==
is-arrayish@^0.3.1:
version "0.3.2"
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03"
integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==
is-bigint@^1.0.1:
version "1.0.4"
resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3"
@ -5220,6 +5366,13 @@ semver@^7.3.7:
dependencies:
lru-cache "^6.0.0"
semver@^7.5.4:
version "7.5.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
dependencies:
lru-cache "^6.0.0"
sentence-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-3.0.4.tgz#3645a7b8c117c787fde8702056225bb62a45131f"
@ -5254,6 +5407,35 @@ shallowequal@^1.1.0:
resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8"
integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==
sharp@^0.33.2:
version "0.33.2"
resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.33.2.tgz#fcd52f2c70effa8a02160b1bfd989a3de55f2dfb"
integrity sha512-WlYOPyyPDiiM07j/UO+E720ju6gtNtHjEGg5vovUk1Lgxyjm2LFO+37Nt/UI3MMh2l6hxTWQWi7qk3cXJTutcQ==
dependencies:
color "^4.2.3"
detect-libc "^2.0.2"
semver "^7.5.4"
optionalDependencies:
"@img/sharp-darwin-arm64" "0.33.2"
"@img/sharp-darwin-x64" "0.33.2"
"@img/sharp-libvips-darwin-arm64" "1.0.1"
"@img/sharp-libvips-darwin-x64" "1.0.1"
"@img/sharp-libvips-linux-arm" "1.0.1"
"@img/sharp-libvips-linux-arm64" "1.0.1"
"@img/sharp-libvips-linux-s390x" "1.0.1"
"@img/sharp-libvips-linux-x64" "1.0.1"
"@img/sharp-libvips-linuxmusl-arm64" "1.0.1"
"@img/sharp-libvips-linuxmusl-x64" "1.0.1"
"@img/sharp-linux-arm" "0.33.2"
"@img/sharp-linux-arm64" "0.33.2"
"@img/sharp-linux-s390x" "0.33.2"
"@img/sharp-linux-x64" "0.33.2"
"@img/sharp-linuxmusl-arm64" "0.33.2"
"@img/sharp-linuxmusl-x64" "0.33.2"
"@img/sharp-wasm32" "0.33.2"
"@img/sharp-win32-ia32" "0.33.2"
"@img/sharp-win32-x64" "0.33.2"
shebang-command@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
@ -5290,6 +5472,13 @@ signedsource@^1.0.0:
resolved "https://registry.yarnpkg.com/signedsource/-/signedsource-1.0.0.tgz#1ddace4981798f93bd833973803d80d52e93ad6a"
integrity sha512-6+eerH9fEnNmi/hyM1DXcRK3pWdoMQtlkQ+ns0ntzunjKqp5i3sKCc80ym8Fib3iaYhdJUOPdhlJWj1tvge2Ww==
simple-swizzle@^0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a"
integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==
dependencies:
is-arrayish "^0.3.1"
slash@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"