add core view extension with JTE and Thymeleaf support

This commit is contained in:
Relism
2026-04-21 00:32:09 +02:00
parent 34fd74068a
commit 9e19f439be
93 changed files with 2200 additions and 1404 deletions
@@ -0,0 +1,29 @@
# Architecture
`flash-ext-view-jte` has two layers:
1. **Boot-time**
- `JteExtension` installs runtime + annotation processor.
- `JteTargetResolver` validates handlers and resolves `@Template`.
- Resolved targets are cached per handler class.
2. **Request-time**
- Handler builds local `ViewModel`.
- Runtime injects globals under reserved `global` namespace and merges local model.
- jte renders template into `StringOutput`.
## Handler Contract
- Must extend `JteHandler`.
- Must have route annotation (`@Route`, `@GET`, `@POST`, ...).
- Must declare exactly one view annotation: `@Template`.
Invalid configurations fail fast at startup.
## Defaults
- `templateRoot`: `/templates`
- `contentType`: `gg.jte.ContentType.Html`
- `developmentMode`: `Flash.DEV`
- `usePrecompiled`: derived from `!developmentMode` unless explicitly set
- `binaryStaticContent`: `false`
@@ -0,0 +1,34 @@
# Handlers
Use `JteHandler` for class-based jte routes.
## Lifecycle
- `onViewInit()` runs once at boot.
- `render(...)` runs per request.
Use `onViewInit()` to cache dependencies via `require(...)`.
## Example
```java
@GET("/dashboard")
@Template("pages/dashboard.jte")
public final class DashboardPage extends JteHandler {
private DashboardService service;
@Override
protected void onViewInit() {
service = require(DashboardService.class);
}
@Override
public ViewModel render(dev.relism.models.Request req) {
return ViewModel.empty()
.with("page", service.page(req));
}
}
```
Use `render(Request, Response)` when you need response access while building model variables.
@@ -0,0 +1,33 @@
# Model And Globals
`ViewModel` is the per-request data container.
## POJO-First Pattern
Keep page state in a POJO and pass it as a single key:
```java
return ViewModel.empty()
.with("page", new HomePage("Flash + jte", "elorc"))
.with("build", "dev");
```
Then type it in template:
```jte
@import com.example.HomePage
@param HomePage page
@param String build
```
## Globals
Register globals in extension setup:
```java
new JteExtension().addGlobal("appName", req -> "Flash")
```
Globals are available under `global` namespace in templates.
`global` is reserved and cannot be used as local model key.
@@ -0,0 +1,18 @@
# Performance Notes
- Runtime caches resolved targets by handler class.
- Request path allocates only what jte rendering requires.
- No reflection on hot path after target cache is warm.
- `TemplateEngine` is created once and shared.
## Dev vs Prod
- Dev mode (`developmentMode=true`) uses code resolver rendering.
- Prod mode (`usePrecompiled=true`) uses precompiled classes for startup and render speed.
## Content Type
`gg.jte.ContentType` drives both escaping mode and HTTP response content type mirror:
- `Html` -> context-aware HTML escaping + `text/html`
- `Plain` -> plain output + `text/plain`