From e8e17884b94ba148d4d2f7af71e9dcd55d240e79 Mon Sep 17 00:00:00 2001 From: logelog <194732487+logelog@users.noreply.github.com> Date: Thu, 23 Jul 2026 17:50:48 +0200 Subject: [PATCH 1/2] fix(core): compute taxonomy counts on demand --- .changeset/nervous-jars-shave.md | 5 + docs/src/content/docs/guides/taxonomies.mdx | 8 + packages/core/src/astro/prefetch.ts | 9 +- .../src/components/widgets/Categories.astro | 2 +- .../core/src/components/widgets/Tags.astro | 2 +- packages/core/src/taxonomies/index.ts | 87 ++++++--- .../unit/taxonomies/term-count-demand.test.ts | 168 ++++++++++++++++++ scripts/query-counts.queries.d1.json | 4 +- scripts/query-counts.queries.sqlite.json | 4 +- scripts/query-counts.snapshot.d1.json | 4 +- scripts/query-counts.snapshot.sqlite.json | 4 +- .../src/pages/work/index.astro | 3 +- .../portfolio/src/pages/work/index.astro | 3 +- 13 files changed, 266 insertions(+), 37 deletions(-) create mode 100644 .changeset/nervous-jars-shave.md create mode 100644 packages/core/tests/unit/taxonomies/term-count-demand.test.ts diff --git a/.changeset/nervous-jars-shave.md b/.changeset/nervous-jars-shave.md new file mode 100644 index 0000000000..e1a4e34f70 --- /dev/null +++ b/.changeset/nervous-jars-shave.md @@ -0,0 +1,5 @@ +--- +"emdash": patch +--- + +Fixes taxonomy term counts being recomputed on every page render even when nothing displays them. Counting term usage aggregates the whole content–term assignment table for each taxonomy, and the layout prefetch ran it for every taxonomy on every HTML response — on Cloudflare D1 this could read millions of rows per page view. Counts are now computed only when a consumer asks for them: the prefetch never does, and the Tags and Categories widgets only when their `showCount` prop is on. `getTaxonomyTerms()` takes a new `includeCounts` option (default `true`) to opt out explicitly, and terms are cached separately from their counts so both callers share one term lookup. diff --git a/docs/src/content/docs/guides/taxonomies.mdx b/docs/src/content/docs/guides/taxonomies.mdx index 42fdc53686..39aa36466f 100644 --- a/docs/src/content/docs/guides/taxonomies.mdx +++ b/docs/src/content/docs/guides/taxonomies.mdx @@ -126,6 +126,14 @@ interface TaxonomyTerm { } ``` +Computing `count` aggregates every content–term assignment in the taxonomy's +collections, which is the most expensive part of the call. If you only need +labels and slugs, skip it — `count` is then omitted from the returned terms: + +```ts +const tags = await getTaxonomyTerms("tag", { includeCounts: false }); +``` + ### Get a Single Term The following example fetches one term by taxonomy and slug: diff --git a/packages/core/src/astro/prefetch.ts b/packages/core/src/astro/prefetch.ts index 19cc2c46ac..88265b2993 100644 --- a/packages/core/src/astro/prefetch.ts +++ b/packages/core/src/astro/prefetch.ts @@ -40,10 +40,15 @@ async function prefetchWidgetAreas(): Promise { } } -/** Warm every taxonomy's term list via the real helper (primes per-name keys). */ +/** + * Warm every taxonomy's term list via the real helper (primes per-name keys). + * Counts are left out: they cost an aggregate over the whole assignment pivot + * per taxonomy, and only a consumer that renders one can say it's needed. A + * consumer that does asks for it and reuses the term list warmed here. + */ async function prefetchTaxonomyTerms(): Promise { const defs = await getTaxonomyDefs(); - await Promise.allSettled(defs.map((def) => getTaxonomyTerms(def.name))); + await Promise.allSettled(defs.map((def) => getTaxonomyTerms(def.name, { includeCounts: false }))); } /** Warm every menu via the real helper (primes `menu:${name}:${locale}`). */ diff --git a/packages/core/src/components/widgets/Categories.astro b/packages/core/src/components/widgets/Categories.astro index 3f226e0b78..9d7cf682cb 100644 --- a/packages/core/src/components/widgets/Categories.astro +++ b/packages/core/src/components/widgets/Categories.astro @@ -12,7 +12,7 @@ interface Props { const { showCount = true, hierarchical = true } = Astro.props; -const categories = await getTaxonomyTerms("category"); +const categories = await getTaxonomyTerms("category", { includeCounts: showCount }); ---