Contact Us

Build & Request Pipeline

DOC-00084 reference implementor, developer, editor

The project has two pipeline layers that transform requests and content: request middleware (Astro onRequest) for URL normalization, and build-time rehype plugins for markdown link resolution. This page documents both.

Who this is for

  • Implementors and editors linking between content files using relative markdown paths
  • Developers adding middleware functions, extending the link rewriting plugin, or troubleshooting URL behavior

Request Middleware

The project uses Astro middleware (src/middleware.ts — CORE-OWNED) for request preprocessing.

Trailing-Slash Redirect

When platformConfig.trailingSlash is true, all non-extension, non-API paths without a trailing slash receive a 301 redirect to the same path with a trailing slash appended.

  • File extensions are detected via regex /\.\w{1,10}$/ (skips .js, .css, .png, etc.).
  • API routes (/api/) are always skipped.
  • Query parameters are preserved in redirects.

Controlled by the trailingSlash boolean in build-safe site configuration (src/site/config/platform.config.ts — SITE-OWNED).

Adding New Middleware

Additional middleware functions can be chained using Astro’s sequence() helper. Each function receives context and next, returning a Response.

// src/middleware.ts — CORE-OWNED
import { sequence } from "astro:middleware";

export const onRequest = sequence(trailingSlashMiddleware, newMiddleware);

Note: Keep middleware functions small and focused. Each function should handle a single concern.


The rehype-resolve-internal-links plugin rewrites file-relative markdown links targeting .md or .mdx files into their canonical route URLs during build-time markdown compilation. This lets authors use natural markdown inter-document linking (e.g., [see this](./other-post.md)) without worrying about how content files map to generated routes.

The plugin runs in the rehype pipeline (HAST tree) and is registered in astro.config.mjs before the rehype-external-links plugin.

What gets rewritten

The plugin rewrites links that meet all of these criteria:

  • The href targets a .md or .mdx file
  • The href uses file-relative resolution: explicit ./ or ../ prefixes, or a bare filename like other-post.md
  • The target file exists in one of the configured content collection directories

What does not get rewritten

  • External links (https://..., mailto:...) — handled by rehype-external-links
  • Root-relative links (/articles/post/) — already in route form
  • Absolute filesystem paths (/home/.../file.md)
  • Non-markdown targets (./image.png, ./data.json)
  • Unresolvable links — if the target .md file does not exist in any configured collection, the link is left unchanged. The PLN-4 broken-link linter (DOC-00082) catches these during validation.
  • Links in .astro templates — only markdown/MDX content is processed by rehype

How resolution works

At build time, the plugin scans all configured collection directories and builds a resolution map from absolute file paths to canonical route URLs. For each content entry:

  1. If the entry has a linkedRoute frontmatter field, the plugin uses that value as the canonical URL. This applies to metadata stubs for static .astro routes in the pages and theme-docs collections.
  2. Otherwise, the plugin calls normalizeEntrySlug() and prepends the collection’s route prefix. Frontmatter slug overrides are respected (supported on pages and articles).
  3. For the pages collection, slugs whose first segment matches a reserved prefix (articles, docs, tests, api) are excluded — those routes belong to other collections.

All rewritten URLs include a trailing slash when platformConfig.trailingSlash enables trailing slash behavior.

Fragments (#heading) and query strings (?param=value) are preserved on the rewritten URL.

Draft entries are included in the resolution map so that inter-draft links work during development.

Covered collections

The plugin ships with four default collection mappings:

CollectionContent directoryRoute prefixExample route
pagessrc/content/pages(none)/about/
articlessrc/content/articles/articles/articles/my-post/
theme-docssrc/core/content/theme-docs/theme-docs/theme-docs/platform/overview/
site-docssrc/content/site-docs/site-docs/site-docs/getting-started/

Adding a collection

Sites can register additional collections by importing the core defaults and appending their own mappings in astro.config.mjs:

import {
  rehypeResolveInternalLinks,
  coreCollections,
} from "./src/core/lib/rehype/rehype-resolve-internal-links.ts";

// In the markdown config:
rehypePlugins: [
  [
    rehypeResolveInternalLinks,
    {
      collections: [
        ...coreCollections,
        {
          name: "guides",
          base: "src/content/guides",
          routePrefix: "/guides",
        },
      ],
    },
  ],
  // ... other plugins
];

Each collection mapping requires three fields:

  • name — collection name (used for diagnostics)
  • base — content directory relative to the project root
  • routePrefix — route prefix prepended to the slug (use "" for collections that route to /{slug}/ with no prefix)

The broken-link static rule (DOC-00082) validates internal links in source content before build. It operates on the raw authored markdown, not on rendered HTML, so the two systems do not interact at runtime. The linter catches broken links; the plugin rewrites valid ones.

If a .md link targets a file that does not exist, the plugin leaves it unchanged and the linter reports it during npm run lint:static-rules.

REQ-00145 implemented In production-aligned outputs (build/preview/deployed), requests for URLs missing the required trailing slash shall automatically redirect to the trailing-slash canonical URL.
REQ-00200 implemented Trailing slash enforcement behavior shall be controllable from a single configuration value such that enabling or disabling it does not require manual edits in multiple files.
REQ-00212 implemented The theme shall rewrite file-relative markdown links targeting .md/.mdx files to their canonical route URLs at build time, so authors can use natural inter-document linking across routed content collections.
REQ-00213 implemented YouTube embeds shall use a click-to-load facade pattern with build-time cached thumbnail images to prevent third-party data transfer before user interaction.
REQ-00225 implemented The lightbox shall integrate with markdown content via a build-time rehype plugin that wraps eligible images in lightbox-enabled links, enabling lightbox behavior without manual markup in content files.
REQ-00262 implemented External link indicators (icon and screen-reader announcement) shall be automatically applied only to links rendered through the markdown/MDX content pipeline. Component-rendered and navigation links shall use the ExternalLink component for explicit opt-in.

Search

Search across pages and articles. Use arrow keys to navigate results.

Search across pages and articles.

Loading search...

Search is unavailable. Please try again later.

    No results for ""

    Try different keywords or fewer words.