app/routes/blog.series.$series.tsx
71 lines and 4 functions in DrDustinEdwards/dustinedwards-info, at commit 1590ba4 on 2026-10-24. This file on GitHub.
Functions
| Function | Lines | Complexity | Covered | CRAP | Mutation |
|---|---|---|---|---|---|
| loader | 30 to 49 | 2 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| headers | 51 to 55 | 2 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| meta | 57 to 68 | 2 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| BlogSeries | 70 to 81 | 2 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
Imports
It imports app/components/taxonomy-listing.tsx, app/db/index.ts, app/lib/blog-listing.mjs, app/lib/context.ts, app/lib/seo.ts, app/lib/series-path.mjs. No module of this repo imports it.
Source
import { data } from "react-router";
import {
TaxonomyListing,
listingHref,
redirectPastLastPage,
} from "~/components/taxonomy-listing";
import { getBlogSeries, listSeriesPosts, nextScheduledPublishAt } from "~/db";
import { POSTS_PER_PAGE, readPage } from "~/lib/blog-listing.mjs";
import { getEnv } from "~/lib/context";
import {
EDGE_CACHE_HEADER,
SITE,
pageMeta,
cacheTags,
publicHtmlHeaders,
scheduledEdgeCacheControl,
} from "~/lib/seo";
import { seriesPath } from "~/lib/series-path.mjs";
import type { Route } from "./+types/blog.series.$series";
import "~/styles/evidence-row.css";
import "~/styles/listing.css";
import "~/styles/entry-list.css";
/**
* Ordered by part, the one listing that is not newest first. 404 rather than an empty page, which
* would be a soft 404 and leak a series only a draft carries.
*/
export async function loader({ params, request, context }: Route.LoaderArgs) {
const env = getEnv(context);
const url = new URL(request.url);
const page = readPage(url.searchParams.get("page"));
const series = await getBlogSeries(env, params.series);
if (!series) throw data("Not found", { status: 404 });
const [listing, nextPublishAt] = await Promise.all([
listSeriesPosts(env, series.name, { page, perPage: POSTS_PER_PAGE }),
nextScheduledPublishAt(env),
]);
redirectPastLastPage(page, listing.pageCount, seriesPath(series.name));
return data(
{ ...listing, series },
{ headers: { [EDGE_CACHE_HEADER]: scheduledEdgeCacheControl(new Date(), nextPublishAt) } },
);
}
export function headers({ loaderHeaders }: Route.HeadersArgs) {
return new Headers(
publicHtmlHeaders(cacheTags(), loaderHeaders.get(EDGE_CACHE_HEADER) ?? undefined),
);
}
export function meta({ loaderData }: Route.MetaArgs) {
if (!loaderData) {
return [{ title: `Not found | ${SITE.name}` }];
}
const { series, page } = loaderData;
const path = listingHref(seriesPath(series.name), page);
return pageMeta({
title: `${series.name} | ${SITE.name}`,
description: `Every part of ${series.name}, a series on ${SITE.name}'s blog.`,
path,
});
}
export default function BlogSeries({ loaderData }: Route.ComponentProps) {
const { series, ...listing } = loaderData;
return (
<TaxonomyListing
{...listing}
base={seriesPath(series.name)}
name={series.name}
heading={series.name}
dek={`A series in ${series.total} part${series.total === 1 ? "" : "s"}, in order.`}
/>
);
}