Skip to content

Commit 481a37b

Browse files
committed
updated readme
1 parent e74f10e commit 481a37b

1 file changed

Lines changed: 246 additions & 5 deletions

File tree

README.md

Lines changed: 246 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,252 @@
1-
# Bigode
1+
# Bigode 🧔
22

3-
Bigode is a subset of mustache written in C# to be fast and compatible with AOT.
3+
Bigode (Portuguese for Mustache) is a highly-performant (no regex), Ahead-Of-Time (AOT) compatible template parser for C#. It implements a focused subset of the original Mustache specification, tailored specifically for AOT compilation scenarios. By requiring explicitly typed models (`RenderModel`), Bigode ensures maximum performance and seamless native compilation without relying on reflection.
44

55
## Status
66

7-
Bigode is under heavy development so expect a lot of changes in a short window.
7+
Bigode is mostly feature complete, as it currently fits all my personal needs. But its development will continue focusing on better performance and ease of use. We do accept PRs.
88

9-
## Expectations
9+
## Features
1010

11-
I'm mostly doing this for my own projects and I'll mantain this and add features as long as theres is interest on it. But the main factor to create this was the fact that Microsoft doesnt yet support Razor on AOT, when they do I might archive this library, lets see.
11+
- AOT Compatible: Built from the ground up to support native AOT compilation in C#.
12+
- High Performance: Designed to be incredibly fast with built-in AST tree caching.
13+
- Relative Partials: Resolves partial files relative to the current document's location, keeping your folder structure clean.
14+
- Custom file extension: Easily change the expected template file extensions.
15+
16+
## Installation
17+
18+
Bigode is published on NuGet.
19+
20+
For the core parser, you can install it via the .NET CLI:
21+
22+
```bash
23+
dotnet add package Bigode
24+
```
25+
26+
For ASP.NET Core Minimal API integration, install the accompanying ASP.NET package:
27+
28+
```bash
29+
dotnet add package Bigode.AspNet
30+
```
31+
32+
## Quick Start
33+
34+
To use Bigode, initialize an instance of the parser (optionally defining the target file extension) and provide a RenderModel with your data.
35+
36+
```cs
37+
38+
using Bigode;
39+
using Bigode.Models;
40+
41+
var bigode = new Bigode("html"); // "html" is the default extension
42+
43+
```
44+
45+
## ASP.NET Core Integration (Minimal APIs)
46+
47+
Bigode is perfectly suited for high-performance, AOT-compiled ASP.NET Core Minimal APIs. The `Bigode.AspNet` package provides built-in dependency injection extensions to seamlessly use the parser inside your endpoints.
48+
49+
Ensure you have the `Bigode.AspNet` package installed.
50+
51+
Register the service using `AddBigode()`.
52+
53+
Inject `BigodeService` directly into your Minimal API handlers.
54+
55+
```cs
56+
57+
using Bigode.AspNet;
58+
using Bigode.AspNet.Services;
59+
using Bigode.Models;
60+
61+
var builder = WebApplication.CreateBuilder(args);
62+
63+
// Register the BigodeService with DI
64+
// You can optionally configure BigodeServiceOptions here
65+
builder.Services.AddBigode(options => {
66+
options.ViewsPath = "./Views"; // default value os "./Views"
67+
options.ViewFileExtension = "html"; // default value os "html"
68+
69+
#if DEBUG
70+
options.DisableFileCache = true; // default value is "false"
71+
#endif
72+
});
73+
74+
var app = builder.Build();
75+
76+
app.MapGet("/", async (BigodeService bigode) =>
77+
{
78+
// Assuming your view is at "Views/Home.html"
79+
var page = await bigodeService.RenderViewAsync("Home", []);
80+
81+
// Then render your page inside the "Views/Template.html" template/layout:
82+
return await bigodeService.RenderViewResultAsync("Template", new RenderModel
83+
{
84+
{ "title", new ("Bigode Example Page") },
85+
{ "content", new (page) }
86+
});
87+
});
88+
89+
app.Run();
90+
```
91+
92+
Also dont forget to include your `Views` folder in the `.csproj` so that they get copied over to the publish folder when project is published:
93+
94+
```xml
95+
<ItemGroup>
96+
<Content Include="Views\**">
97+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
98+
</Content>
99+
</ItemGroup>
100+
```
101+
102+
103+
## Supported Tags & Examples
104+
105+
Below is a comprehensive list of supported tags and scenarios, documented via examples that correspond directly to the unit tests included in the repository.
106+
107+
### 1. Variables
108+
109+
Replaces `{{name}}` with the provided string value.
110+
111+
```cs
112+
var templatePath = "basic.html"; // Content: "Hello {{name}}!"
113+
var model = new RenderModel { { "name", new("World") } };
114+
115+
var result = await bigode.ParseAsync(templatePath, model);
116+
// Output: "Hello World!"
117+
```
118+
119+
### 2. Comments
120+
121+
Comments start with a `!` and are completely ignored during rendering.
122+
123+
```cs
124+
var templatePath = "ignore.html"; // Content: "Visible {{! This is hidden }} Content"
125+
var model = new RenderModel(); // Empty model
126+
127+
var result = await bigode.ParseAsync(templatePath, model);
128+
// Output: "Visible Content"
129+
```
130+
131+
### 3. Lambdas / Delegates
132+
133+
You can pass functions (lambdas) to manipulate the inner content of a section.
134+
135+
```cs
136+
var templatePath = "lambda.html"; // Content: "{{#wrapped}}Inner Content{{/wrapped}}"
137+
var model = new RenderModel {
138+
{ "wrapped", new(async (text) => $"<b>{text}</b>") }
139+
};
140+
141+
var result = await bigode.ParseAsync(templatePath, model);
142+
// Output: "<b>Inner Content</b>"
143+
```
144+
145+
146+
### 4. Conditional Sections (Booleans)
147+
148+
Sections starting with `#` will only render if the provided boolean is true.
149+
150+
```cs
151+
var templatePath = "condition.html"; // Content: "{{#isAdmin}}Welcome Admin{{/isAdmin}}"
152+
var model = new RenderModel { { "isAdmin", new(true) } };
153+
154+
var result = await bigode.ParseAsync(templatePath, model);
155+
// Output: "Welcome Admin"
156+
```
157+
158+
159+
### 5. Loop Sections (Arrays)
160+
161+
Sections evaluate to a loop when the provided model data is an array of `RenderModel` items.
162+
163+
```cs
164+
var templatePath = "loop.html"; // Content: "{{#users}}Hello {{name}}{{/users}}"
165+
var model = new RenderModel {
166+
{ "users", new([
167+
new RenderModel { { "name", new("Alice") } },
168+
new RenderModel { { "name", new("Bob") } }
169+
])}
170+
};
171+
172+
var result = await bigode.ParseAsync(templatePath, model);
173+
// Output: "Hello AliceHello Bob"
174+
```
175+
176+
177+
### 6. Inverted Sections
178+
179+
Sections starting with `^` will render only if the value is missing, false, or an empty array.
180+
181+
```cs
182+
var templatePath = "inverted.html"; // Content: "{{^isLogged}}Please log in{{/isLogged}}"
183+
var model = new RenderModel { { "isLogged", new(false) } };
184+
185+
var result = await bigode.ParseAsync(templatePath, model);
186+
// Output: "Please log in"
187+
```
188+
189+
### 7. Missing Variables
190+
191+
If a variable or section is referenced but missing from the `RenderModel`, Bigode gracefully fails silently by outputting nothing.
192+
193+
```cs
194+
var templatePath = "missing.html"; // Content: "Value: {{missingVar}}"
195+
var model = new RenderModel();
196+
197+
var result = await bigode.ParseAsync(templatePath, model);
198+
// Output: "Value: "
199+
```
200+
201+
202+
### 8. Partials
203+
204+
Partials allow you to include other templates. They are denoted by `{{> partial_name }}`. Paths are resolved relative to the calling document.
205+
206+
```cs
207+
// user.html content: "<div>User: {{name}}</div>"
208+
var templatePath = "main.html"; // Content: "Profile: {{> user }}"
209+
var model = new RenderModel { { "name", new("Alice") } };
210+
211+
var result = await bigode.ParseAsync(templatePath, model);
212+
// Output: "Profile: <div>User: Alice</div>"
213+
```
214+
215+
216+
### 9. Loop Partials
217+
218+
You can combine loops and partials. The partial will be rendered for each item in the array context.
219+
220+
```cs
221+
// item.html content: "<li>{{ name }}</li>"
222+
var templatePath = "list.html"; // Content: "<ul>{{#items}}{{> item }}{{/items}}</ul>"
223+
var model = new RenderModel {
224+
{ "items", new([
225+
new RenderModel { { "name", new("Apples") } },
226+
new RenderModel { { "name", new("Bananas") } }
227+
])}
228+
};
229+
230+
var result = await bigode.ParseAsync(templatePath, model);
231+
// Output: "<ul><li>Apples</li><li>Bananas</li></ul>"
232+
```
233+
234+
235+
### 10. Nested Partials
236+
237+
Partials can call other partials recursively.
238+
239+
```cs
240+
// leaf.html content: "I am the {{name}}."
241+
// branch.html content: "Branch including: {{> leaf }}"
242+
var templatePath = "root.html"; // Content: "Root starts: {{> branch }}"
243+
var model = new RenderModel { { "name", new("leaf") } };
244+
245+
var result = await bigode.ParseAsync(templatePath, model);
246+
// Output: "Root starts: Branch including: I am the leaf."
247+
```
248+
249+
250+
## Contributing
251+
252+
Contributions are welcome! Please feel free to submit a pull request or open an issue if you encounter any problems or have feature requests.

0 commit comments

Comments
 (0)