This is how I automatically update the updated property of content on my site, such as articles, projects, and notes. It finds the last Git commit that touched the current file. Every build this is checked, meaning there are no manual steps to setting the value.

  • If there are no commits found then most likely this is a new file.
  • This example makes a few simplifications for brevity, it would be much cleaner to keep Git functionality in it’s own module and import it here.
  • This does not currently allow you to manually override the updated date, but it would be a straightforward change.
  • This uses Astro 5 Content collections which uses id instead of slug in previous versions.
  • This might not look like other examples of Astro based sites, but the key point here is that I’m augmenting the data returned from Astro.
  • I prefer this kind of approach to the various “rehype/remark plugins” as in my opinion these are fragile and too far removed from the content files.
---
export const getStaticPaths = async () => {
const getLastModifiedTime = (path?: string) => {
const updated = execSync(`git log -1 --pretty="format:%cI" ${path}`).toString();
return !updated ? new Date() : new Date(updated);
};
const noteEntries = await getCollection('note');
const notes = noteEntries
.sort((a, b) => b.data.dates.published.valueOf() - a.data.dates.published.valueOf())
.map((entry) => ({
params: { id: entry.id },
props: {
...entry,
data: {
...entry.data,
dates: {
...entry.data.dates,
updated: getLastModifiedTime(entry.filePath),
},
number: Number.parseInt(entry.id),
},
},
}));
return notes;
};
const note = Astro.props.data as Note;
const { Content } = await render(Astro.props);
---
<Layout
title={`Note #${note.number!}`}
description="A collection of notes and ideas"
dates={note.dates}>
<div>
<NoteComponent {...note} number={note.number!}>
<Content />
</NoteComponent>
<p>
Read the rest of my <a href="/notes/">notes</a>
</p>
</div>
</Layout>

I use the following types and schemas to define my Note type.

export const noteSchema = z.object({
number: z.number().optional(),
dates: datesSchema,
related: relatedSchema.optional(),
});
export type Note = Readonly<z.infer<typeof noteSchema>> & {
preview?: string;
};
export const datesSchema = z.object({
published: z.coerce.date(),
updated: z.coerce.date().optional()
});
export type Dates = Readonly<z.infer<typeof datesSchema>>;

Read the rest of my notes