Markdig is a fast, powerful, CommonMark compliant, extensible Markdown processor for .NET.
Paths/commands below are relative to this directory.
- Workspace:
src/markdig.slnx(solution/workspace listing the projects below) - Library:
src/Markdig/ - Tests:
src/Markdig.Tests/(NUnit; includes generated spec tests) - Signed library build:
src/Markdig.Signed/(strong-named build ofsrc/Markdig/sources) - Benchmarks:
src/Markdig.Benchmarks/(BenchmarkDotNet perf comparisons) - Fuzzing:
src/Markdig.Fuzzing/(SharpFuzz/libFuzzer harness for parser/renderers) - Web app:
src/Markdig.WebApp/(ASP.NET Core API for converting Markdown → HTML) - Tooling:
src/mdtoc/(CLI to generate a Markdown TOC from a local file or GitHub URL) - Tooling:
src/SpecFileGen/(regenerates*.generated.csspec tests fromsrc/Markdig.Tests/**.md) - Tooling:
src/UnicodeNormDApp/(generates the Unicode→ASCII mapping used byMarkdig.Helpers.CharNormalizer) - Docs to keep in sync with behavior:
readme.mdand the docs undersite/docs/(e.g.,site/docs/**/*.md) - Website:
site/— Lunet-based documentation site (https://xoofx.github.io/markdig)
# from the project root (this folder)
cd src
dotnet build -c Release
dotnet test -c ReleaseAll tests must pass and docs must be updated before submitting.
The project website lives in site/ and is built with Lunet, a static site generator.
# Prerequisites: install lunet as a .NET global tool
dotnet tool install -g lunet
# Build the site (from the project root)
cd site
lunet build # production build → .lunet/build/www/
lunet serve # dev server with live reload at http://localhost:4000Because the site is processed by Scriban via Lunet, any literal {{ or }} in Markdown documentation must be escaped as {{ "{{" }} and {{ "}}" }} respectively. Hand-written pages must be escaped manually.
- Keep diffs focused; avoid drive-by refactors/formatting and unnecessary dependencies.
- Follow existing patterns and naming; prefer clarity over cleverness.
- New/changed behavior requires tests; bug fix = regression test first, then fix.
- All public APIs require XML docs (avoid CS1591) and should document thrown exceptions.
- Naming:
PascalCasepublic/types/namespaces,camelCaselocals/params,_camelCaseprivate fields,I*interfaces. - Style: file-scoped namespaces;
usingoutside namespace (Systemfirst);varwhen the type is obvious. - Nullability: enabled — respect annotations; use
ArgumentNullException.ThrowIfNull(); preferis null/is not null; don't suppress warnings without a justification comment. - Exceptions: validate inputs early; throw specific exceptions (e.g.,
ArgumentException/ArgumentNullException) with meaningful messages. - Async:
Asyncsuffix; noasync void(except event handlers); useConfigureAwait(false)in library code; considerValueTask<T>on hot paths.
- Minimize allocations (
Span<T>,stackalloc,ArrayPool<T>,StringBuilderin loops). - Keep code AOT/trimmer-friendly: avoid reflection; prefer source generators; use
[DynamicallyAccessedMembers]when reflection is unavoidable. - Use
sealedfor non-inheritable classes; preferReadOnlySpan<char>for parsing.
- Follow .NET guidelines; keep APIs small and hard to misuse.
- Prefer overloads over optional parameters (binary compatibility); consider
Try*methods alongside throwing versions. - Mark APIs
[Obsolete("message", error: false)]before removal once stable (can be skipped while pre-release).
- Commits: commit after each self-contained logical step; imperative subject, < 72 chars; one logical change per commit; reference issues when relevant; don't delete unrelated local files.
- Checklist: each self-contained step is committed; build+tests pass; docs updated if behavior changed; public APIs have XML docs; changes covered by unit tests.