Skip to content

Commit c85b591

Browse files
fix: optimize generated images dx (#215)
* fix: optimize generated images dx * fix: better debug page * feat: add vercel adapter * wip: not working * feat: add download route * fix: download * fix: talk square image * fix: add server in getAstroImagePath * quickfix: ignore not generated images * fix: improve debug page * feat: add sponsors, partners and coOrganisers images --------- Co-authored-by: Yoann Fleury <yoann.fleury@yahoo.com>
1 parent 697f0dc commit c85b591

File tree

16 files changed

+880
-120
lines changed

16 files changed

+880
-120
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# build output
22
dist/
3+
.vercel
4+
35
# generated types
46
.astro/
57

astro.config.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import mdx from "@astrojs/mdx";
55
import sitemap from "@astrojs/sitemap";
66
import robotsTxt from "astro-robots-txt";
77

8+
import vercel from "@astrojs/vercel";
9+
810
// https://astro.build/config
911
export default defineConfig({
1012
site: import.meta.env.PROD
@@ -36,4 +38,6 @@ export default defineConfig({
3638
sitemap(),
3739
robotsTxt(),
3840
],
41+
42+
adapter: vercel(),
3943
});

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"@astrojs/rss": "4.0.11",
2222
"@astrojs/sitemap": "3.2.1",
2323
"@astrojs/tailwind": "6.0.0",
24+
"@astrojs/vercel": "8.0.5",
2425
"@fontsource-variable/inter": "5.0.20",
2526
"@fontsource/tomorrow": "5.0.20",
2627
"@radix-ui/react-accordion": "1.2.0",
@@ -32,6 +33,7 @@
3233
"@types/react-dom": "18.3.0",
3334
"@uidotdev/usehooks": "2.4.1",
3435
"@vercel/analytics": "1.4.1",
36+
"adm-zip": "0.5.16",
3537
"astro": "5.2.3",
3638
"astro-robots-txt": "1.0.0",
3739
"astro-seo": "0.8.4",
@@ -57,6 +59,7 @@
5759
},
5860
"devDependencies": {
5961
"@tailwindcss/typography": "0.5.14",
62+
"@types/adm-zip": "0.5.7",
6063
"husky": "9.1.6",
6164
"lint-staged": "15.2.10",
6265
"prettier": "3.3.3",

pnpm-lock.yaml

Lines changed: 387 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/dynamic-images/utils.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ export async function generateImageResponse(
162162
function getAstroImagePath(image: ImageMetadata) {
163163
return import.meta.env.DEV
164164
? path.resolve(image.src.replace(/\?.*/, "").replace("/@fs", ""))
165-
: path.resolve(image.src.replace("/", "dist/"));
165+
: path.resolve(image.src.replace("/", "dist/server/"));
166166
}
167167

168168
async function getAstroImageBuffer(image: ImageMetadata) {
@@ -198,6 +198,7 @@ type ExtraProps = { isDebug: boolean; width: number; height: number };
198198
type PropsForRender<T> = T & { dynamicImage: ExtraProps };
199199
export function generateImageMethods<Props>(
200200
params: ImageParams & {
201+
shouldBuild?: (props: Props) => boolean;
201202
getStaticPaths: (
202203
options: GetStaticPathsOptions,
203204
) => Promise<Array<GetStaticPathItemWithGeneric<Props>>>;
@@ -208,8 +209,15 @@ export function generateImageMethods<Props>(
208209
) {
209210
return (p: { __image: string }) => ({
210211
getStaticPaths: async (options: GetStaticPathsOptions) => {
212+
const entries = await params.getStaticPaths(options);
213+
211214
return withType({
212-
entries: await params.getStaticPaths(options),
215+
entries:
216+
import.meta.env.PROD && params.shouldBuild
217+
? entries.filter(
218+
(entry) => !!entry.props && params.shouldBuild?.(entry.props),
219+
)
220+
: entries,
213221
params: p,
214222
});
215223
},

src/pages/events/[id]/dynamic-images/_save-the-date.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
getAstroImageBase64,
66
} from "@/dynamic-images/utils";
77

8-
import { getEventStaticPaths } from "./_utils";
8+
import { getEventStaticPaths, shouldBuildEventImage } from "./_utils";
99
import { Frame } from "@/dynamic-images/components/Frame";
1010

1111
import { LogoWithFriends } from "@/dynamic-images/components/LogoWithFriends";
@@ -15,6 +15,7 @@ export const saveTheDate = (config: { width: number; height: number }) =>
1515
generateImageMethods({
1616
width: config.width,
1717
height: config.height,
18+
shouldBuild: shouldBuildEventImage,
1819
getStaticPaths: getEventStaticPaths,
1920
render: async (props) => {
2021
const postCover = await getAstroImageBase64(props.event.data.image.media);

src/pages/events/[id]/dynamic-images/_tickets-available.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
getAstroImageBase64,
66
} from "@/dynamic-images/utils";
77

8-
import { getEventStaticPaths } from "./_utils";
8+
import { getEventStaticPaths, shouldBuildEventImage } from "./_utils";
99
import { Frame } from "@/dynamic-images/components/Frame";
1010

1111
import { LogoWithFriends } from "@/dynamic-images/components/LogoWithFriends";
@@ -19,6 +19,7 @@ export const ticketsAvailable = (config: {
1919
generateImageMethods({
2020
width: config.width,
2121
height: config.height,
22+
shouldBuild: shouldBuildEventImage,
2223
getStaticPaths: getEventStaticPaths,
2324
render: async (props) => {
2425
const postCover = await getAstroImageBase64(props.event.data.image.media);

src/pages/events/[id]/dynamic-images/_utils.ts

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import { getCollection, getEntries } from "astro:content";
1+
import { getCollection, getEntries, type CollectionEntry } from "astro:content";
2+
import dayjs from "dayjs";
3+
4+
export const shouldBuildEventImage = (props: {
5+
event: CollectionEntry<"events">;
6+
}) => {
7+
return dayjs().isBefore(dayjs(props.event.data.date).add(1, "day"), "day");
8+
};
29

310
export const getEventStaticPaths = async () => {
411
const events = await getCollection("events");
@@ -23,3 +30,51 @@ export const getEventStaticPaths = async () => {
2330
}),
2431
);
2532
};
33+
34+
const cleanFileName = (path: string) =>
35+
path
36+
.split("/")
37+
.at(-1)
38+
?.replace(/\.tsx$/, "")
39+
.replace(/^_/, "");
40+
41+
export const getEventAssetsSources = (event: CollectionEntry<"events">) => {
42+
const eventFilesNames = Object.keys(
43+
import.meta.glob("./_*.tsx", { eager: true }),
44+
).map(cleanFileName);
45+
const talkFilesNames = Object.keys(
46+
import.meta.glob("../talks/[talkId]/dynamic-images/_*.tsx", {
47+
eager: true,
48+
}),
49+
).map(cleanFileName);
50+
const partnersFilesNames = Object.keys(
51+
import.meta.glob("../partners/[partnerId]/dynamic-images/_*.tsx", {
52+
eager: true,
53+
}),
54+
).map(cleanFileName);
55+
56+
const sponsors = event.data.sponsors?.map((s) => s.slug) ?? [];
57+
const partners = event.data.partners ?? [];
58+
const coOrganizers = event.data.coOrganizers ?? [];
59+
60+
return [
61+
eventFilesNames.map(
62+
(fileName) => `/events/${event.id}/dynamic-images/${fileName}.jpg`,
63+
),
64+
event.data.schedule?.flatMap((talk) =>
65+
talkFilesNames.map((fileName) =>
66+
!talk.slug
67+
? null
68+
: `/events/${event.id}/talks/${talk.slug.id}/dynamic-images/${fileName}.jpg`,
69+
),
70+
),
71+
[...coOrganizers, ...sponsors, ...partners].flatMap((partner) =>
72+
partnersFilesNames.map(
73+
(fileName) =>
74+
`/events/${event.id}/partners/${partner.id}/dynamic-images/${fileName}.jpg`,
75+
),
76+
),
77+
]
78+
.flat()
79+
.filter((x) => x !== undefined && x !== null);
80+
};

src/pages/events/[id]/dynamic-images/debug.astro

Lines changed: 0 additions & 55 deletions
This file was deleted.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { getEventAssetsSources } from "@/pages/events/[id]/dynamic-images/_utils";
2+
import type { APIRoute } from "astro";
3+
import { getEntry } from "astro:content";
4+
import dayjs from "dayjs";
5+
import AdmZip from "adm-zip";
6+
7+
export const prerender = false;
8+
9+
export const GET: APIRoute = async ({ params, site }) => {
10+
const event = await getEntry("events", params.id!);
11+
if (!event) {
12+
return new Response("", { status: 404 });
13+
}
14+
15+
const zip = new AdmZip();
16+
const imagesSrc = getEventAssetsSources(event);
17+
18+
await Promise.all(
19+
imagesSrc.map(async (src) => {
20+
const url = new URL(src, site).toString();
21+
const response = await fetch(url);
22+
if (!response.ok) {
23+
// Ignore not generated images
24+
return;
25+
}
26+
const blob = await response.blob();
27+
zip.addFile(
28+
`${src.replaceAll("/events/", "").replaceAll("/dynamic-images", "").replaceAll("/", "_")}`,
29+
Buffer.from(await blob.arrayBuffer()),
30+
);
31+
}),
32+
);
33+
34+
return new Response(zip.toBuffer(), {
35+
headers: {
36+
"Content-Type": "application/zip",
37+
"Content-Disposition": `attachment; filename="${event.data.date.getFullYear()}-${event.data.country.toLowerCase()}-${event.data.city.toLowerCase()}-assets-${dayjs().format("YYYYMMDDHHmmss")}.zip"`,
38+
},
39+
});
40+
};

0 commit comments

Comments
 (0)