-
Notifications
You must be signed in to change notification settings - Fork 0
Contributing
This guide covers building, testing, architecture, and coding conventions for FlexRender contributors.
dotnet build FlexRender.slnx # Build entire solution
dotnet test FlexRender.slnx # Run all tests
dotnet test --filter "ClassName" # Filter by test class
dotnet test --filter "MethodName" # Filter by test method
UPDATE_SNAPSHOTS=true dotnet test # Regenerate golden snapshot imagesRequirements: .NET 10 SDK
src/FlexRender.Core/ # Core library (0 external dependencies)
Abstractions/ # IFlexRender, IResourceLoader, ILayoutRenderer<T>
Configuration/ # FlexRenderBuilder, FlexRenderOptions, ResourceLimits
Layout/ # Two-pass flexbox layout engine (LayoutEngine facade + strategy classes)
Units/ # Unit, UnitParser, PaddingValues, PaddingParser
Loaders/ # FileResourceLoader, Base64ResourceLoader, EmbeddedResourceLoader
Parsing/Ast/ # Template, CanvasSettings, TemplateElement tree
TemplateEngine/ # TemplateProcessor, ExpressionLexer, ExpressionEvaluator
Values/ # TemplateValue hierarchy
src/FlexRender.Yaml/ # YAML template parser (-> Core + YamlDotNet)
Parsing/ # TemplateParser facade, ElementParsers, YamlPropertyHelpers
src/FlexRender.Http/ # HTTP resource loader (-> Core)
src/FlexRender.Skia.Render/ # SkiaSharp renderer (-> Core + SkiaSharp)
Rendering/ # SkiaRenderer facade, RenderingEngine, TemplatePreprocessor, TextRenderer, FontManager, BmpEncoder
Providers/ # ImageProvider, IContentProvider<T>
src/FlexRender.Skia/ # Skia backend meta-package (renderer + providers)
src/FlexRender.QrCode.Skia.Render/ # QR provider for Skia (-> Skia + QRCoder)
src/FlexRender.QrCode.Svg.Render/ # QR provider for SVG (-> Svg)
src/FlexRender.QrCode.ImageSharp.Render/ # QR provider for ImageSharp (-> ImageSharp + QRCoder)
src/FlexRender.QrCode/ # QR meta-package (references all renderers)
src/FlexRender.Barcode.Skia.Render/ # Barcode provider for Skia
src/FlexRender.Barcode.Svg.Render/ # Barcode provider for SVG
src/FlexRender.Barcode.ImageSharp.Render/ # Barcode provider for ImageSharp
src/FlexRender.Barcode/ # Barcode meta-package (references all renderers)
src/FlexRender.SvgElement.Skia.Render/ # SvgElement provider for Skia (-> Svg.Skia)
src/FlexRender.SvgElement.Svg.Render/ # SvgElement provider for SVG (native)
src/FlexRender.SvgElement/ # SvgElement meta-package (references all renderers)
src/FlexRender.ImageSharp.Render/ # ImageSharp renderer (-> Core + SixLabors.ImageSharp)
Rendering/ # ImageSharpRenderingEngine, ImageSharpTextRenderer, ImageSharpFontManager
src/FlexRender.ImageSharp/ # ImageSharp backend meta-package (renderer + providers)
src/FlexRender.Svg.Render/ # SVG output renderer (-> Core)
src/FlexRender.Svg/ # SVG backend meta-package (renderer + providers)
src/FlexRender.DependencyInjection/ # Microsoft DI integration
src/FlexRender.MetaPackage/ # Meta-package (core + all backends + DI)
src/FlexRender.Cli/ # CLI tool (System.CommandLine)
tests/FlexRender.Tests/ # Unit + snapshot tests
tests/FlexRender.Cli.Tests/ # CLI integration tests
tests/FlexRender.ImageSharp.Tests/ # ImageSharp visual snapshot tests
examples/ # Example YAML templates
YAML Template
-> TemplateParser (YAML -> AST: Template with CanvasSettings + TemplateElement tree)
-> TemplateExpander (expand EachElement/IfElement to concrete elements based on data)
-> TemplateProcessor (resolve {{variable}} expressions in element properties)
-> LayoutEngine (two-pass: MeasureAllIntrinsics -> ComputeLayout -> LayoutNode tree)
-> SkiaRenderer (traverse LayoutNode tree -> draw to SKBitmap via SkiaSharp)
| Stage | Key Classes |
|---|---|
| Configuration |
FlexRenderBuilder, SkiaBuilder, FlexRenderOptions, ResourceLimits
|
| Abstractions |
IFlexRender, IResourceLoader
|
| Parsing |
TemplateParser (facade), ElementParsers, YamlPropertyHelpers, Template, CanvasSettings, TextElement, FlexElement, QrElement, BarcodeElement, ImageElement, SeparatorElement, EachElement, IfElement
|
| Template Engine |
TemplateExpander, TemplateProcessor, ExpressionLexer, ExpressionEvaluator
|
| Layout |
LayoutEngine (facade), IntrinsicMeasurer, ColumnFlexLayoutStrategy, RowFlexLayoutStrategy, WrappedFlexLayoutStrategy, LayoutHelpers, LayoutNode, LayoutContext, LayoutSize, IntrinsicSize
|
| Rendering (Skia) |
SkiaRender (IFlexRender impl), SkiaRenderer (facade), RenderingEngine, TemplatePreprocessor, TextRenderer, FontManager, BmpEncoder
|
| Rendering (ImageSharp) |
ImageSharpRender (IFlexRender impl), ImageSharpRenderingEngine, ImageSharpTextRenderer, ImageSharpFontManager
|
| Providers |
IContentProvider<T>, ISvgContentProvider<T>, IImageSharpContentProvider<T>, QrProvider, BarcodeProvider, ImageProvider
|
| Values |
TemplateValue, StringValue, NumberValue, BoolValue, NullValue, ArrayValue, ObjectValue
|
-
TextMeasurer delegate --
Func<TextElement, float, float, LayoutSize>?onLayoutEngine, wired up bySkiaRendererfor content-based text sizing -
Content providers --
IContentProvider<TElement>returns rendered content for QR, barcode, image elements -
Resource loader chain --
IResourceLoaderwithCanHandle(),Load(),Priority(chain of responsibility) -
Switch-based dispatch -- element type dispatch uses
switchon concrete types, not base class properties -
Template processing layers -- AST-level (
TemplateExpander) and inline (TemplateProcessor)
-
.NET 10, C# latest,
Nullable=enable,TreatWarningsAsErrors=true -
File-scoped namespaces --
namespace Foo.Bar;
-
IsAotCompatible=trueon all library and CLI projects -
No reflection anywhere -- no
Type.GetType(), nodynamic -
GeneratedRegexfor all regex patterns - Pattern matching (
switchon concrete types) for type dispatch
-
sealedclasses -- all leaf/value/concrete classes must besealed -
sealed record-- for token types and small immutable data -
readonly record struct-- for small value types (IntrinsicSize,LayoutRect,PaddingValues) -
Immutability -- value types are
readonly, collections exposed asIReadOnlyList<T> -
Thread safety --
ConcurrentDictionarywhere concurrent access is expected
-
XML documentation --
<summary>,<param>,<returns>,<exception>on all public APIs -
Guard clauses --
ArgumentNullException.ThrowIfNull(),ArgumentException.ThrowIfNullOrWhiteSpace() -
String comparison --
StringComparer.OrdinalIgnoreCasefor dictionaries keyed by names
All security limits are in ResourceLimits. Never remove or weaken them without explicit justification.
-
Framework: xUnit with
[Fact]and[Theory]/[InlineData] -
Assertions:
Assert.*from xUnit;FluentAssertionsin some tests -
Naming:
MethodUnderTest_Scenario_ExpectedResult -
Class naming:
{ClassName}Tests - Organization: mirrors source structure
- Pattern: Arrange-Act-Assert
SnapshotTestBase with golden images in golden/. Pixel-by-pixel comparison with configurable colorThreshold and maxDifferencePercent. ImageSharp snapshot tests use ImageSharpSnapshotTestBase and ImageSharpSnapshotComparer with golden images prefixed is_ (macOS-only due to platform rendering differences).
# Regenerate golden snapshot images
UPDATE_SNAPSHOTS=true dotnet test- All features and non-trivial changes in separate branches
-
Branch naming:
type/short-description(e.g.,feat/qr-provider,fix/layout-overflow) -
Types:
feat,fix,refactor,build,test,docs,chore - Do NOT merge into
main-- leave feature branches for PR review
Conventional Commits: type(scope): description
-
Types:
feat,fix,refactor,build,test,docs,chore -
Scopes (optional):
parser,layout,renderer,ast,examples,cli - Description: lowercase, imperative mood
Examples:
feat(parser): add barcode element support
fix(layout): correct flex-basis resolution for percentage values
refactor(renderer): extract text measurement into delegate
test(layout): add wrap-reverse alignment tests
- Create a
sealedAST class inParsing/Ast/extendingTemplateElement - Add to
ElementTypeenum - Add parser function in
TemplateParser.cs-- register in_elementParsersdictionary - Add flex-item property support via
switchpattern matching in the layout engine - Add rendering in
SkiaRenderer.RenderNode()or create a provider implementingIContentProvider<T> - Write tests for each step
- Add token type as
sealed recordinExpressionToken.cs - Add lexer support in
ExpressionLexer.cs - Add evaluation in
TemplateProcessor.cs - Write tests
- Add format to
BarcodeFormatenum inParsing/Ast/BarcodeElement.cs - Add parsing case in
TemplateParser.ParseBarcodeElement() - Add generation in
BarcodeProvider - Write tests
When reviewing code changes, verify:
- AOT safe -- no reflection, no
dynamic, noType.GetType(). UseGeneratedRegexfor regex - Classes are
sealedunless designed for inheritance - Null checks via
ArgumentNullException.ThrowIfNull()-- not manualif (x == null) - Resource limits preserved -- never remove or weaken
MaxFileSize,MaxNestingDepth,MaxRenderDepth - New element types follow switch-based dispatch pattern
- XML docs on all public API surface
- Snapshot tests added/updated for visual changes
- No LINQ in hot paths (
foreachinstead of.Where()/.Select()/.ToList()) - Collections have capacity hints where size is predictable
- Large temporary arrays use
ArrayPool<T>.Shared
SkiaSharp and HarfBuzzSharp require native libraries on Linux. Add to executable projects (not libraries):
| Package | Use Case |
|---|---|
SkiaSharp.NativeAssets.Linux |
Standard Linux with fontconfig/freetype |
SkiaSharp.NativeAssets.Linux.NoDependencies |
Minimal/Docker containers |
HarfBuzzSharp.NativeAssets.Linux |
Required when using FlexRender.HarfBuzz
|
If DllNotFoundException: libSkiaSharp or libHarfBuzzSharp occurs, verify with ldd /path/to/lib*.so.
- API-Reference -- full API documentation
- Template-Syntax -- YAML template reference
- AGENTS.md -- additional contributor guidelines