#[Solved] How to handle side-effects in Astro component frontmatter correctly?

8 messages · Page 1 of 1 (latest)

obtuse mulch
#

One of my Astro components that generates <head /> tags also generates an Open Graph image as a part of it's frontmatter. To be specific, I use the (satori)[https://github.com/vercel/satori] to generate an image based on the supplied props, and then write it to a file with common Node utilities.

Although the process of figuring out the correct paths during the building stage (I don't use SSR) was a hassle (maybe this part needs documentation and/or API improvement?), I managed to successfully do it, until I tried to create a dynamic Astro component page: I found out that the transpiled component executes twice, and the second time it executes without props (this is how I figured out the problem in the first place, the generated image had generic text as if no page-specific props were provided) it overwrites the original generated file. Here's an example of a frontmatter:

// /src/layouts/BaseHead/ImageTags.astro
import satori from 'satori';
import { join } from 'node:path';
import { writeFile } from 'node:fs/promises';

const { PROD, ROOT_DIR } = import.meta.env;
const { slug, text } = Astro.props; // <- 'text' is empty when Astro component is called the 2nd time

if (PROD) {
  const path = join(ROOT_DIR, '/dist/og/', `${slug}.svg`);
  const file = Buffer.from(await satori(/* ...options */));
  await writeFile(path, file, { flag: 'wx' }); // <- This throws 'EEXIST: file already exists' error
}

How can I avoid generating the file twice? Maybe there is more idiomatic way to handle side-effects?

pearl condorBOT
#
No-one around right now?

It looks like no-one has responded to your question yet. People might not be available right now or don’t know how to answer your question. Want an answer while you wait? Try asking our experimental bot in #1095492539085230272.

brave orchid
#

Instead of generating the image as part of your component, move your generation into an endpoint:
https://docs.astro.build/en/core-concepts/endpoints/

Your component then should just be responsible for the tags containing the URLs pointing to that endpoint, and not the generation of the image itself.

Astro Documentation

Learn how to create endpoints that serve any kind of data

obtuse mulch
brave orchid
obtuse mulch
# brave orchid In SSG mode, your endpoints are called at build time.

Oh, I see. My understanding is that I should be using getStaticPaths, but this way I can programmatically pull props only from collection routes, while needing to hard-code non-collection routes (specifically I have to copy-paste title and path/slug from index and 404). Is there a way to get props from those, as well?

brave orchid
obtuse mulch