This documentation is also published as Markdown for efficient machine reading: the whole site is indexed at /llms.txt, and every page has a clean Markdown copy under /_llms/. These are generated from the same source and cost far fewer tokens to read than this rendered HTML.

Skip to main content Skip to navigation
Reference

DI and middleware extension methods

Index of every AddPennington/UsePennington/Run* extension method across the referenced Pennington packages.

The list of public extension methods Pennington exposes for wiring the library into an ASP.NET Core host — Add* (DI registration), Use* (middleware and endpoints), and Run* (host entry points). Grouped below by receiver type; each method is declared in an *Extensions static class under its owning feature namespace.

IServiceCollection extensions

DI registration entry points. The three composition roots and the options record each configures:

The full set follows, each tagged with its owning package.

AddApiMetadataFromCompiledAssembly IServiceCollection AddApiMetadataFromCompiledAssembly(this IServiceCollection services, Action<CompiledAssemblyApiOptions> configure)

Package Pennington.ApiMetadata.Reflection

Convenience overload: registers under the "default" name for sites documenting a single library.
AddApiMetadataFromCompiledAssembly IServiceCollection AddApiMetadataFromCompiledAssembly(this IServiceCollection services, string name, Action<CompiledAssemblyApiOptions> configure)

Package Pennington.ApiMetadata.Reflection

Registers CompiledAssemblyApiMetadataProvider as a keyed IApiMetadataProvider under name. Call once per library you want to document — each call builds its own MetadataLoadContext and xmldoc index scoped to the supplied AssemblyDirectories. The shared IXmlDocParser / IXmlDocHtmlRenderer services are registered once (idempotent).
AddApiReference IServiceCollection AddApiReference(this IServiceCollection services, string name, Action<ApiReferenceRegistrationOptions> configure)

Package Pennington.DocSite.Api

Registers one named API-reference tree. Call once per library you want to document. Each call pairs with a matching AddApiMetadataFrom*(name, …) provider registration and publishes its type pages at the configured RoutePrefix.
AddBlogSite IServiceCollection AddBlogSite(this IServiceCollection services, Func<BlogSiteOptions> configureOptions)

Package Pennington.BlogSite

Registers BlogSite services with the provided options.
AddDataDirectory<TItem> IServiceCollection AddDataDirectory<TItem>(this IServiceCollection services, string name, string path)

Package Pennington

Registers every .yml, .yaml, and .json file in path as a single aggregated IReadOnlyList accessible through IDataFiles under the lookup key name. Each file contributes one record, or several when its root is an array; files are ordered by name. Edits, additions, and removals in the directory invalidate the cached value so the next read returns the fresh content.
AddDataFile<T> IServiceCollection AddDataFile<T>(this IServiceCollection services, string name, string path)

Package Pennington

Registers path as a data file accessible through IDataFiles under the lookup key name. Format is inferred from the file extension (.yml, .yaml, .json). Edits to the file invalidate the cached value so the next read returns the fresh content.
AddDocSite IServiceCollection AddDocSite(this IServiceCollection services, Func<DocSiteOptions> configureOptions)

Package Pennington.DocSite

Registers DocSite services with the provided options.
AddFileWatched<T> IServiceCollection AddFileWatched<T>(this IServiceCollection services)

Package Pennington

Register a concrete service whose instance is managed by FileWatchDependencyFactory.
AddFileWatched<TService, TImplementation> IServiceCollection AddFileWatched<TService, TImplementation>(this IServiceCollection services)

Package Pennington

Register a service whose instance is managed by FileWatchDependencyFactory. The factory (singleton) recreates the instance when the implementation's OnFileChanged returns Recreate. The service (transient) always returns the current instance from the factory.
AddHead IServiceCollection AddHead(this IServiceCollection services)

Package Pennington

Registers the head composition rewriter. Inert until at least one IHeadContributor is also registered, so adding this on its own leaves head output byte-identical.
AddHeadContributor<T> IServiceCollection AddHeadContributor<T>(this IServiceCollection services)

Package Pennington

Registers a single head contributor. Transient so contributors capturing a file-watched dependency (e.g. the content registry) pick up the current instance per request.
AddLlmsSubtree IServiceCollection AddLlmsSubtree(this IServiceCollection services, LlmsSubtree subtree)

Package Pennington

Registers a LlmsSubtree so all leaves under RoutePrefix get split out into a dedicated {RoutePrefix}llms.txt. Multiple registrations are allowed; programmatic registrations override _meta.yml-discovered subtrees with the same prefix.
AddMonorailCss IServiceCollection AddMonorailCss(this IServiceCollection services, Func<IServiceProvider, MonorailCssOptions> optionFactory)

Package Pennington.MonorailCss

Registers MonorailCSS services and the runtime class-discovery pipeline. With no configuration, the discovery pipeline force-loads every non-BCL assembly the app references, scans each one's IL, watches the project's source files in development, and loads wwwroot/app.css as the source CSS prefix when present. The CSS endpoint served by UseMonorailCss regenerates whenever the class set changes.
AddPennington IServiceCollection AddPennington(this IServiceCollection services, Action<PenningtonOptions> configure)

Package Pennington

Register all Pennington services.
AddPenningtonBook IServiceCollection AddPenningtonBook(this IServiceCollection services, Action<BookOptions> configure)

Package Pennington.Book

Adds PDF book generation: a per-locale book per BookDefinition (or one whole-site book when none are configured), served on demand at /pdf/{slug}.pdf in dev and emitted into the static build. Registers an IDownloadLinkProvider a host's chrome can advertise.
AddPenningtonStyles IServiceCollection AddPenningtonStyles(this IServiceCollection services, IReadOnlyDictionary<string, string> templateSkin, Func<IReadOnlyDictionary<string, string>> styleOverrides, Func<string, string, string> classMerger)

Package Pennington.UI

Registers the StyleRegistry. Site templates call this internally with their skin; bare hosts composing Pennington.UI components directly can call it themselves (the components fall back to the built-in defaults when nothing is registered). When called more than once the last registration wins; the diag command is registered once.
AddTaxonomy<TFrontMatter, TKey> IServiceCollection AddTaxonomy<TFrontMatter, TKey>(this IServiceCollection services, Action<TaxonomyOptions<TFrontMatter, TKey>> configure)

Package Pennington

Registers a TaxonomyContentService configured by configure. Multiple AddTaxonomy calls with the same TFrontMatter/TKey pair coexist as long as each uses a distinct BaseUrl.
AddTranslationAudit IServiceCollection AddTranslationAudit(this IServiceCollection services, Action<TranslationAuditOptions> configure)

Package Pennington.TranslationAudit

Register TranslationAuditor as an IBuildAuditor. Diagnostics land in the dev overlay (per-page) and in the build report (site-wide) automatically.
AddTreeSitter IServiceCollection AddTreeSitter(this IServiceCollection services, Action<TreeSitterOptions> configure)

Package Pennington.TreeSitter

Adds tree-sitter based multi-language code-fragment extraction — the :symbol fence modifier. Services are registered only when ContentRoot is configured.
AddWordBreak IServiceCollection AddWordBreak(this IServiceCollection services, Action<WordBreakOptions> configure)

Package Pennington

Registers WordBreakHtmlRewriter in the shared HTML rewriting pipeline, so long identifiers in the configured elements get <wbr> break opportunities without an extra DOM parse.
AddYamlContext IServiceCollection AddYamlContext(this IServiceCollection services, YamlSerializerContext context)

Package Pennington

Register a source-generated YamlSerializerContext so the types it covers deserialize without reflection (NativeAOT/trim-friendly). Types not covered by any registered context fall back to reflection. Satellite templates call this for their own front-matter records; end users call it for theirs.
ReplaceContentRenderer<TOld, TNew> IServiceCollection ReplaceContentRenderer<TOld, TNew>(this IServiceCollection services)

Package Pennington

Replaces every registered IContentRenderer with TNew, resolved through DI as a transient. The TOld type parameter documents the renderer being swapped out — it is informational and unused at runtime, but lets the call site read as "replace TOld with TNew".
ReplaceContentRenderer<TOld, TNew> IServiceCollection ReplaceContentRenderer<TOld, TNew>(this IServiceCollection services, Func<IServiceProvider, TNew> factory)

Package Pennington

Replaces every registered IContentRenderer with one produced by factory. Use this overload when the new renderer takes ctor arguments DI cannot resolve (e.g. a version string or per-site constant).

WebApplication extensions

Middleware and endpoint wiring. The template Use* methods each wrap a fixed sequence, listed below.

RunBlogSiteAsync Task RunBlogSiteAsync(this WebApplication app, string[] args)

Package Pennington.BlogSite

Runs the BlogSite: either serves the app or performs a static build, based on command-line args.
RunDocSiteAsync Task RunDocSiteAsync(this WebApplication app, string[] args)

Package Pennington.DocSite

Runs the DocSite: either serves the app or performs a static build, based on command-line args.
RunOrBuildAsync Task RunOrBuildAsync(this WebApplication app, string[] args)

Package Pennington

Runs the host: serves live (no verb), builds the static site (build), or runs a diagnostic command (diag <sub>). Everything flows through one System.CommandLine pipeline, so --help / --version work at the root and every subcommand. Build and diag run one-shot against a started in-memory host that is disposed afterward; serve hands off to RunAsync.
UseBlogSite WebApplication UseBlogSite(this WebApplication app)

Package Pennington.BlogSite

Wires BlogSite middleware, Razor components, and RSS endpoint into the request pipeline.
UseDocSite WebApplication UseDocSite(this WebApplication app)

Package Pennington.DocSite

Wires DocSite middleware and Razor components into the request pipeline.
UseLiveReload WebApplication UseLiveReload(this WebApplication app)

Package Pennington

Adds live reload WebSocket support for development. Skipped during static build (see PenningtonCli).
UseLocaleRouting WebApplication UseLocaleRouting(this WebApplication app)

Package Pennington

Adds locale detection and URL path rewriting middleware. Must be called MapRazorComponents so that Blazor routing sees the locale-stripped path (e.g., /gen-z/schedule becomes /schedule). Called automatically by UsePennington when it hasn't been called yet, but at that point it is too late for Blazor endpoint routing. Sites that use @page directives with locale prefixes must call this explicitly.
UseMonorailCss WebApplication UseMonorailCss(this WebApplication app, string path)

Package Pennington.MonorailCss

Maps the MonorailCSS stylesheet endpoint. The endpoint pulls the current class set from the discovery pipeline registered in AddMonorailCss, generates CSS, and serves it.
UsePennington WebApplication UsePennington(this WebApplication app)

Package Pennington

Configure the Pennington middleware pipeline.

UseDocSite middleware order

UseDocSite registers this sequence before mapping the Razor component endpoint:

  1. UseLocaleRouting
  2. UseAntiforgery
  3. UseStaticFiles
  4. UseMonorailCss
  5. UsePennington
  6. MapRazorComponents<App>()

UseBlogSite middleware order

UseBlogSite registers the same sequence minus locale routing, which BlogSite does not wire:

  1. UseAntiforgery
  2. UseStaticFiles
  3. UseMonorailCss
  4. UsePennington
  5. MapRazorComponents<App>()

For why each step lands where it does, see Dev mode and build mode share one code path.

Run* host entry points

Host entry points that run one System.CommandLine pipeline: serve live with no verb, build the static site with build, or run a read-only inspection with diag <sub>. Build and diag run one-shot against a started in-memory host that is disposed afterward; serve hands off to RunAsync.

Example

A complete DocSite host wiring all three layers — AddDocSite, UseDocSite, RunDocSiteAsync — in call order.

csharp
using Pennington.DocSite;
  
var builder = WebApplication.CreateBuilder(args);
  
// Swap the bare `AddPennington` host for the DocSite template. `AddDocSite`
// wires the full documentation experience on top of Pennington core — a
// Blazor-rendered layout with sidebar navigation, header, search surface,
// outline nav, dark-mode toggle — driven entirely from `DocSiteOptions`.
builder.Services.AddDocSite(() => new DocSiteOptions
{
    SiteTitle = "Scaffold Docs",
    SiteDescription = "A minimal DocSite scaffold built on AddDocSite.",
    GitHubUrl = "https://github.com/usepennington/pennington",
    HeaderContent = """<a href="/">Scaffold Docs</a>""",
    FooterContent = """<footer class="mt-16 py-8 text-center text-sm text-base-500">Built with Pennington DocSite.</footer>""",
});
  
var app = builder.Build();
  
// `UseDocSite` mounts locale routing, antiforgery, static files, Razor
// component routing (`Pages.razor` owns `/{*fileName:nonfile}`), MonorailCSS,
// SPA navigation, and the core Pennington middleware in the right order.
app.UseDocSite();
  
// `RunDocSiteAsync` delegates to `RunOrBuildAsync`, so `dotnet run` serves live
// and `dotnet run -- build <baseUrl> <outputDir>` generates static HTML. Both
// positional args are optional (defaults: `/` and `output`).
await app.RunDocSiteAsync(args);

See also