Static Content Helper Markdown Directory Pipeline
This guide defines the integration model for generating static markdown section pages from directory content.
What This Solves
- Opinionated section index rendering from
.mdfiles in configured directories - Query-parameter pagination for section roots (
/blogs?page=2) - Detail page rendering with sticky heading TOC (
h2-h4) - Route lookup helper so applications can map request path/query to pre-rendered
Components
Design Rules
- Helper generation is stateless per call.
- Application owns cache/refresh lifecycle and HTTP routing.
- Frontmatter parsing is best-effort; invalid values become warnings.
- Markdown is escaped by default; unsafe mode is explicit per section.
Quick Start
StaticContentSite site = StaticContentSiteBuilder.create()
.addSection(ContentSectionConfig.create("blogs", Path.of("content/blogs"), "/blogs")
.withSectionTitle("Blog")
.withPageSize(10)
.withMaxDepth(4)
.withListItemComponentSupplier(DefaultContentListItemComponent::create))
.addSection(ContentSectionConfig.create("projects", Path.of("content/projects"), "/projects")
.withPageSize(12))
.build();
ContentSiteBundle bundle = site.generate();
Frontmatter DSL
---
title: Building the Parser
summary: Tradeoffs and internals
date: 2026-03-11T14:30
author: SimplyPages Team
tags: java,simplypages,rendering
slug: parser-internals
draft: false
---
Date formats accepted:
yyyy-MM-ddyyyy-MM-dd'T'HH:mm
Routing Pattern
- Section root page 1:
/blogs - Section root page n:
/blogs?page=n - Detail route:
/blogs/{slug}
Controller-side generic lookup pattern:
Optional<Component> resolved = bundle.routeIndex().resolveRequest(path, request.getParameter("page"));
Inspect generation warnings
Warnings are nonfatal: generation retains every generated page and its direct section lookup.
Inspect bundle.warnings() at startup and after each refresh, rather than treating successful
generation as evidence that request paths do not overlap. For example:
for (ContentWarning warning : bundle.warnings()) {
System.err.printf("Content section %s, source %s: %s%n",
warning.sectionKey(), warning.sourcePath(), warning.message());
}
Keep filesystem source paths in application diagnostics rather than automatically displaying them to visitors. See the API reference for warning fields and ordering.
A root section / containing foo.md publishes /foo. If another section uses /foo as its
base path, the detail shadows that section's index. The same overlap can occur at a nested
path, such as /blogs/foo. Detail-first resolution is unchanged: /foo, /foo/,
/foo?page=1 and /foo?page=2 all select the detail, masking index pagination. A nonfatal
collision warning names the route, both section keys, detail source file and index source
directory. Choose a non-overlapping section base path or published slug to make both reachable
through request lookup. resolveIndex(sectionKey, page) still retrieves the retained index
directly; neither route map loses its generated pages.
Diagnostics use final published routes after slug uniquification and exclude drafts. They emit one warning per conflicting detail/index owner pair, regardless of page count. Duplicate normalized section bases remain configuration errors; this diagnostic does not add a routing mode or new URL normalization.
Caching Pattern
- Build once at startup:
bundle = site.generate(). - Serve from
ContentRouteIndexlookups per request. - Rebuild bundle on deploy or app-defined refresh schedule.
Testing Checklist
- Frontmatter best-effort warnings for invalid values
- Draft exclusion and date-desc sort order
- Query pagination route generation and lookup
- Nonfatal detail/index collision warnings with both source owners, retained direct lookups and detail-first request resolution
- Draft exclusion, final uniquified routes and repeatable collision warning counts
- Sticky TOC heading extraction (
h2-h4) and heading anchor IDs - Markdown raw-HTML escaping in safe mode