diff --git a/README.md b/README.md index f43db6a..256fc0f 100644 --- a/README.md +++ b/README.md @@ -89,22 +89,29 @@ Mirrors Go's own export convention: ```gastro --- -ctx := gastro.Context() // lowercase -> private -err := doSomething() // lowercase -> private +slug := r.PathValue("slug") // lowercase -> private +err := doSomething() // lowercase -> private -Title := "Hello" // Uppercase -> {{ .Title }} -Items := fetchItems() // Uppercase -> {{ .Items }} +Title := "Hello" // Uppercase -> {{ .Title }} +Items := fetchItems() // Uppercase -> {{ .Items }} ---

{{ .Title }}

``` +Frontmatter has ambient `w http.ResponseWriter` and `r *http.Request`. +The page handler runs for every HTTP method; branch on `r.Method` to +handle non-GET requests in the same file. + ### File-Based Routing -| File | Route | -|----------------------------|-------------------| -| `pages/index.gastro` | `GET /` | -| `pages/about/index.gastro` | `GET /about` | -| `pages/blog/[slug].gastro` | `GET /blog/{slug}` | +| File | Route | +|----------------------------|----------------| +| `pages/index.gastro` | `/` | +| `pages/about/index.gastro` | `/about` | +| `pages/blog/[slug].gastro` | `/blog/{slug}` | + +Patterns are method-less; the page handles every method (GET, POST, +etc.) and branches on `r.Method`. ### Static Assets diff --git a/docs/architecture.md b/docs/architecture.md index 1c7771f..5f4903b 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -66,8 +66,10 @@ Three responsibilities, split across files: Wraps the frontmatter in a valid Go file and parses it with `go/ast`. Extracts: - All variable declarations (`:=` and `var`) - Classification: uppercase (exported to template) vs lowercase (private) -- Detection of `gastro.Context()` calls (marks file as a page) - Detection of `gastro.Props()` calls (marks file as a component) +- Marker-rewrite warnings (deprecation of `gastro.Context()`, unknown + `gastro.X` symbols) and missing-return findings from + `internal/analysis/respwrite.go` The frontmatter is wrapped in `package __gastro / func __handler() { ... }` so `go/parser` can handle it. Type declarations (`type Props struct{}`) are @@ -80,13 +82,33 @@ hoisted to package level since they can't live inside a function body. Produces Go source code for a page handler or component render function. Uses `text/template` to emit the generated code. The output includes: - Package declaration -- Imports (user's + gastro runtime) +- Imports (user's + gastro runtime); user paths that overlap the + auto-injected set (`net/http`, `log`, `html/template`, `bytes`) are + deduped - Template variable with parsed `html/template` -- Handler function with frontmatter code, data map, and template execution -- Panic recovery wrapper - -The `gastro.Context()` marker is rewritten to `gastroRuntime.NewContext(w, r)`. -The `gastro.Props()` marker is stripped (component generation is TODO). +- Handler function: wraps `w` in `gastroRuntime.NewPageWriter(w)` so + body writes are tracked, runs the rewritten frontmatter, and + conditionally executes the template only when the body is still + uncommitted (Track B) +- Panic recovery wrapper that consults the wrapped writer to avoid + double-writing after a partial response + +The consolidated marker rewriter (Track B §4.10) handles the implicit +`gastro.X` namespace via an allowlist: + +| Frontmatter call | Rewritten to | +|---------------------------|------------------------------------| +| `gastro.Props()` | `__props` | +| `gastro.Context()` | `gastroRuntime.NewContext(w, r)` (deprecated; emits warning) | +| `gastro.From[T](ctx)` | `gastroRuntime.FromContext[T](ctx)` | +| `gastro.FromOK[T](ctx)` | `gastroRuntime.FromContextOK[T](ctx)` | +| `gastro.FromContext[T]…` | identity (gastro → gastroRuntime) | +| `gastro.FromContextOK[T]…`| identity | +| `gastro.NewSSE(w, r)` | `gastroRuntime.NewSSE(w, r)` | +| `gastro.Render.X(…)` | `Render.X(…)` | + +Anything else under `gastro.X` produces an "unknown gastro runtime +symbol" warning, keeping the implicit namespace finite. #### `template.go` @@ -276,7 +298,7 @@ parser.Parse() | v parser.File { - Frontmatter: "ctx := gastro.Context()\nTitle := \"Hello\"" + Frontmatter: "slug := r.PathValue(\"slug\")\nTitle := \"Hello\"" TemplateBody: "

{{ .Title }}

" Imports: ["myapp/db"] ComponentImports: [{Name: "Card", Path: "components/card.gastro"}] @@ -287,8 +309,8 @@ parser.File { | v | FrontmatterInfo { | ExportedVars: [{Name: "Title"}] - | PrivateVars: [{Name: "ctx"}] - | IsPage: true + | PrivateVars: [{Name: "slug"}] + | Warnings: [] // Track B marker / missing-return findings | } | +---> codegen.ProcessMarkdownDirectives() diff --git a/docs/comparison.md b/docs/comparison.md index 9fa3ffc..1449943 100644 --- a/docs/comparison.md +++ b/docs/comparison.md @@ -152,7 +152,7 @@ All five approaches produce a single deployable binary. The differences are in h | Approach | What You Need to Know | New Concepts | |----------|----------------------|--------------| -| **Gastro** | Go + html/template syntax + HTML/CSS | `.gastro` file format, frontmatter conventions, `gastro.Props()` / `gastro.Context()` APIs | +| **Gastro** | Go + html/template syntax + HTML/CSS | `.gastro` file format, frontmatter conventions, ambient `(w, r)` and `gastro.Props()` | | **Templ** | Go + the Templ DSL | Templ-specific syntax (looks like Go but is not Go), `@component` calls, `templ` blocks | | **gomponents** | Go only | The `Node` interface and dot-import pattern. Otherwise it is standard Go. | | **htmgo** | Go + HTMX attributes | htmgo's element builder API, HTMX concepts (hypermedia-driven), partial rendering | diff --git a/docs/components.md b/docs/components.md index 1fad8c2..e686643 100644 --- a/docs/components.md +++ b/docs/components.md @@ -55,9 +55,9 @@ import ( PostCard "components/post-card.gastro" ) -ctx := gastro.Context() +Title := "Home" --- -{{ wrap Layout (dict "Title" "Home") }} +{{ wrap Layout (dict "Title" .Title) }} {{ PostCard (dict "Title" "My Post" "Slug" "my-post") }} {{ end }} ```