generated from bleumoon/template
A library for rendering HTML via ServerSide Rendering
- Luau 96.6%
- Nix 2%
- Shell 1.4%
BleuMoon's init.luau parent-directory context resolution causes @src
aliases in root init.luau to resolve against the parent directory's
.luaurc instead of the package's own .luaurc. This makes @src point to
the consumer project's src/ instead of the package's src/.
Fix: move all entry logic to src/init.luau where the parent-directory
context resolves to the package root (which has the correct .luaurc).
Root init.luau becomes a thin proxy: require("./bleurender/src").
|
||
|---|---|---|
| .forgejo/workflows | ||
| .github | ||
| .vscode | ||
| docs | ||
| scripts | ||
| src | ||
| tests | ||
| .editorconfig | ||
| .gitignore | ||
| .luaurc | ||
| bleumoon.lock | ||
| bleumoon.toml | ||
| CHANGELOG.md | ||
| flake.lock | ||
| flake.nix | ||
| init.luau | ||
| LICENSE | ||
| README.md | ||
| stylua.toml | ||
BleuRender
A Server-Side Rendered HTML framework for BleuMoon. Includes an HTML builder, template engine with layout inheritance, component system, HTTP server with middleware, and static file serving.
Prerequisites
Tip: If you have Nix with flakes enabled, both tools are provided automatically via
nix develop.
Getting Started
With Nix (recommended)
git clone https://git.ds.reinitialized.net/bleupigs.club/bleurender.git
cd bleurender
nix develop
Without Nix
-
Clone and install dependencies:
git clone https://git.ds.reinitialized.net/bleupigs.club/bleurender.git cd bleurender bleumoon pkg install
Quick Start
HTML Builder
Build HTML programmatically with h():
local html = require("@pkg/bleurender/src").html
local page = html.h("div", { class = "container" }, {
html.h("h1", {}, { "Hello, World!" }),
html.h("p", {}, { "Built with BleuRender." }),
})
print(html.renderToString(page))
-- <div class="container"><h1>Hello, World!</h1><p>Built with BleuRender.</p></div>
Templates
Write templates with a Luau-native syntax:
{@ extends "layouts/base" }
{@ block "content" }
<h1>Welcome, {= user.name }</h1>
{# if user.isAdmin }
<p>Admin panel</p>
{# else }
<p>Regular dashboard</p>
{/# if }
<ul>
{# each items as item, idx }
<li>{= idx }. {= item.name }</li>
{/# each }
</ul>
{/@ block }
Components
Register reusable function components:
local bleurender = require("@pkg/bleurender/src")
bleurender.components.registerComponent("Button", function(props)
return bleurender.html.renderToString(
bleurender.html.h("button", { class = props.class or "btn" }, { props.label or "Click" })
)
end)
Use in templates:
{@ component "Button" label="Submit" class="btn-primary" }
Server
Create an HTTP server with routing and middleware:
local bleurender = require("@pkg/bleurender/src")
local Server = bleurender.server
local app = Server.new({
port = 3000,
templateDir = "./templates",
})
-- Built-in middleware
app:use(Server.logger())
app:use(Server.errorHandler())
-- Routes
app:get("/", function(_req, res)
res:render("index", { title = "Home", message = "Hello!" })
end)
app:get("/api/data", function(_req, res)
res:json({ status = "ok", data = { 1, 2, 3 } })
end)
-- Static files
app:use(Server.static("./public"))
app:listen()
Template Syntax Reference
| Syntax | Description |
|---|---|
{= expr } |
Escaped output (HTML-safe) |
{! expr } |
Raw output (no escaping) |
{# if cond }...{/# if } |
Conditional |
{# elseif cond } |
Else-if branch |
{# else } |
Else branch |
{# each list as item, idx }...{/# each } |
Loop with optional index |
{@ extends "layout" } |
Inherit from layout |
{@ block "name" }...{/@ block } |
Named block |
{@ include "partial" } |
Include partial |
{@ component "Name" k=v } |
Invoke component |
{-- comment --} |
Template comment (stripped) |
Condition expressions
- Simple:
{# if loggedIn } - Negated:
{# if not loggedIn } - Comparison:
{# if user.role == "admin" }(supports==,~=,>,<,>=,<=)
Dot-path resolution
Expressions like {= user.address.city } are resolved through the scope chain, walking through nested table keys.
API Overview
local bleurender = require("@pkg/bleurender/src")
-- HTML builder
bleurender.html.h(tag, attrs?, children?)
bleurender.html.renderToString(vnode)
bleurender.html.renderFragment(vnodes)
bleurender.html.rawHtml(str)
bleurender.html.isVNode(value)
-- Templates
bleurender.template.compile(source, options?)
bleurender.template.render(source, data, options?)
bleurender.template.renderFile(filePath, data, options?)
bleurender.template.createCache(options?)
-- Components
bleurender.components.registerComponent(name, fn)
bleurender.components.getComponent(name)
bleurender.components.hasComponent(name)
bleurender.components.unregisterComponent(name)
bleurender.components.clearRegistry()
bleurender.components.listComponents()
-- Server
bleurender.server.new(options)
bleurender.server.static(root, options?)
bleurender.server.logger()
bleurender.server.compression()
bleurender.server.errorHandler()
bleurender.server.cookieParser()
Running Tests
bleumoon run tests/init
Formatting
stylua --check . # check
stylua . # apply
Project Structure
├── bleumoon.toml # Project manifest & dependencies
├── flake.nix # Nix flake for reproducible dev env
├── CHANGELOG.md # Version history
├── docs/
│ └── architecture/
│ └── bleurender-design.md # Architecture design document
├── src/
│ ├── init.luau # Public API entry point
│ ├── main.luau # Demo application
│ ├── html/
│ │ ├── init.luau # h(), renderToString(), renderFragment()
│ │ ├── elements.luau # VNode type, void elements, boolean attrs
│ │ └── attributes.luau # Attribute serialization
│ ├── template/
│ │ ├── init.luau # compile(), render(), renderFile()
│ │ ├── parser.luau # Template tokenizer / AST
│ │ ├── compiler.luau # AST → render function
│ │ ├── context.luau # Scope chain for variables
│ │ └── cache.luau # Template cache with mtime invalidation
│ ├── components/
│ │ └── init.luau # Component registry
│ ├── server/
│ │ ├── init.luau # Server.new(), middleware factories
│ │ ├── router.luau # Route matching (:param, *wildcard)
│ │ ├── request.luau # Request wrapper
│ │ ├── response.luau # Response builder
│ │ ├── middleware.luau # Pipeline composition
│ │ └── static.luau # Static file serving
│ └── utils/
│ └── init.luau # MIME types, path helpers, parsing
└── tests/
├── init.luau # Test runner
├── html_test.luau # HTML builder tests
├── template_test.luau # Parser & render tests
├── context_test.luau # Scope chain tests
├── components_test.luau # Component registry tests
└── server_test.luau # Router, middleware, request, response tests
License
This project is licensed under the MIT License.