diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index fe4dd39..a355cb3 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -12,16 +12,19 @@ jobs: - name: Setup dotnet uses: actions/setup-dotnet@v5 with: - dotnet-version: 10.0.x + dotnet-version: | + 8.0.x + 9.0.x + 10.0.x - name: Install Tools run: dotnet tool restore - working-directory: ./fsharp-view-engine + working-directory: ./lib - name: Install Packages run: dotnet paket install - working-directory: ./fsharp-view-engine + working-directory: ./lib - name: Test run: ./fake.sh Test - working-directory: ./fsharp-view-engine + working-directory: ./lib preview: name: Preview runs-on: ubuntu-latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8a5a16e..1d0478e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,16 +14,19 @@ jobs: - name: Setup dotnet uses: actions/setup-dotnet@v5 with: - dotnet-version: 10.0.x + dotnet-version: | + 8.0.x + 9.0.x + 10.0.x - name: Restore Tools run: dotnet tool restore - working-directory: ./fsharp-view-engine + working-directory: ./lib - name: Install Packages run: dotnet paket install - working-directory: ./fsharp-view-engine + working-directory: ./lib - name: Publish - run: ./fake.sh Publish - working-directory: ./fsharp-view-engine + run: ./fake.sh PushNugets + working-directory: ./lib env: NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} deploy: diff --git a/.gitignore b/.gitignore index 5eec986..4e52848 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ +.idea +.vscode .claude diff --git a/fsharp-view-engine/AGENTS.md b/AGENTS.md similarity index 100% rename from fsharp-view-engine/AGENTS.md rename to AGENTS.md diff --git a/CLAUDE.md b/CLAUDE.md index 3dd6247..c89bd6c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -3,21 +3,24 @@ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview -FSharp.ViewEngine is a view engine for F# web applications. It provides a functional approach to generating HTML with type-safe F# code, including integrated support for HTMX, Alpine.js, Tailwind CSS, and SVG elements. +FSharp.ViewEngine is a view engine for F# web applications. It provides a functional approach to generating HTML with type-safe F# code, including integrated support for HTMX, Alpine.js, Datastar, Tailwind CSS, and SVG elements. ## Repository Structure -- `fsharp-view-engine/` — F# library, app, tests, and build system -- `pulumi/` — Pulumi infrastructure (TypeScript) for deploying the demo app -- `.github/` — CI workflows (at repo root, with `working-directory: fsharp-view-engine`) +- `lib/` — Core F# library, tests, benchmarks, and build system +- `docs/` — Documentation Giraffe web app with Markdown pages and build system +- `etc/` — Assets (logos) +- `pulumi/` — Pulumi infrastructure (TypeScript) for deploying the docs app +- `.github/` — CI workflows - `.claude/` — Claude Code settings ## Common Development Commands **Important:** When running in a bash shell (including Claude Code), always use `./fake.sh` instead of `fake.cmd`. +### Library (`lib/`) + ```bash -# All dotnet/fake commands run from fsharp-view-engine/ -cd fsharp-view-engine +cd lib # Restore tools and packages dotnet tool restore @@ -30,13 +33,28 @@ dotnet run --project src/Tests/Tests.fsproj # Direct # Run a single test by name dotnet run --project src/Tests/Tests.fsproj -- --filter "Should render html document" -# Run demo app with Tailwind watch -./fake.sh Watch - # Create NuGet package (needs GITHUB_REF_NAME env var) ./fake.sh Pack +``` + +### Docs app (`docs/`) + +```bash +cd docs -# Pulumi deployment +# Run docs app with Tailwind watch (hot reload) +./fake.sh Watch + +# Build CSS for production +./fake.sh BuildCss + +# Publish docs app +./fake.sh Publish +``` + +### Pulumi deployment + +```bash cd pulumi npm install pulumi up -s prod @@ -44,17 +62,18 @@ pulumi up -s prod ## Architecture -### Core Type System (`fsharp-view-engine/src/FSharp.ViewEngine/Core.fs`) +### Core Type System (`lib/src/FSharp.ViewEngine/Core.fs`) Two discriminated unions form the foundation: - **Element**: `Text | Tag | Void | Fragment | Raw | Noop` — represents DOM nodes - **Attribute**: `KeyValue | Boolean | Children | Noop` — represents HTML attributes Rendering uses `StringBuilder` with recursive pattern matching. `Text` is HTML-encoded; `Raw` is not. `Void` elements (e.g., `br`, `img`) are self-closing and reject children. -### Module Organization +### Module Organization (`lib/src/FSharp.ViewEngine/`) - `Html.fs` — Standard HTML elements and attributes as static members on `Html` type - `Htmx.fs` — HTMX attributes (`_hxGet`, `_hxPost`, etc.) on `Htmx` type - `Alpine.fs` — Alpine.js directives (`_xData`, `_xShow`, etc.) on `Alpine` type +- `Datastar.fs` — Datastar attributes (`_dsSignals`, `_dsOn`, etc.) on `Datastar` type - `Tailwind.fs` — Tailwind UI custom elements on `Tailwind` type - `Svg.fs` — SVG elements and attributes on `Svg` type @@ -64,31 +83,38 @@ open FSharp.ViewEngine open type Html open type Htmx -div [ _class "container"; _hxGet "/api"; _children [ h1 "Hello" ] ] -|> Element.render +div { + _class "container" + _hxGet "/api" + h1 { "Hello" } +} +|> Render.toHtmlDocString ``` ### Project Structure -- `fsharp-view-engine/src/FSharp.ViewEngine/` — Core library (NuGet package) -- `fsharp-view-engine/src/Tests/` — xUnit tests -- `fsharp-view-engine/src/Build/` — FAKE build system (`Program.fs` defines targets) -- `fsharp-view-engine/src/App/` — Demo Giraffe web app with Markdown docs +- `lib/src/FSharp.ViewEngine/` — Core library (NuGet package) +- `lib/src/Tests/` — Expecto tests +- `lib/src/Benchmarks/` — BenchmarkDotNet benchmarks +- `lib/src/Build/` — FAKE build system (Test, Pack, PushNugets targets) +- `docs/src/Docs/` — Documentation Giraffe web app with Markdown pages +- `docs/src/Build/` — FAKE build system (Watch, BuildCss, Publish targets) - `pulumi/` — Infrastructure as code (Pulumi + TypeScript) ## Development Patterns - **New HTML elements** in `Html.fs`: use `Tag` for normal elements, `Void` for self-closing. Add convenience overloads (e.g., `p (text:string)`). -- **Framework attributes**: HTMX → `Htmx.fs` with `_hx` prefix; Alpine → `Alpine.fs` with `_x` prefix. +- **Framework attributes**: HTMX → `Htmx.fs` with `_hx` prefix; Alpine → `Alpine.fs` with `_x` prefix; Datastar → `Datastar.fs` with `_ds` prefix. +- **New doc pages**: Add markdown in `docs/src/Docs/docs/`, handler in `Handlers.fs`, route in `Program.fs`, nav link in `Views.fs`. - **Tests** compare rendered HTML strings using `String.clean` for whitespace normalization. Use `// language=HTML` comment for IDE syntax highlighting in expected strings. ## Build System -- FAKE build scripts invoked via `fake.cmd`/`fake.sh` -- Paket for package management (`paket.dependencies` at root of `fsharp-view-engine/`) +- FAKE build scripts invoked via `fake.cmd`/`fake.sh` (separate in `lib/` and `docs/`) +- Paket for package management (`paket.dependencies` in `lib/` and `docs/`) - .NET 10.0 SDK (`global.json`) - CI: GitHub Actions — tests on PRs, NuGet publish on release tags (`v*.*.*`) ## Infrastructure (Pulumi) - TypeScript Pulumi project in `pulumi/` -- Deploys demo app to Kubernetes via AWS ECR + Cloudflare Tunnel +- Deploys docs app to Kubernetes via AWS ECR - Domain: `fsharpviewengine.meiermade.com` - Stack references: `identity`, `infrastructure`, `fsharp-view-engine-identity` diff --git a/fsharp-view-engine/LICENSE b/LICENSE similarity index 100% rename from fsharp-view-engine/LICENSE rename to LICENSE diff --git a/README.md b/README.md index 2214ce8..312700e 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,15 @@ [](https://github.com/meiermade/FSharp.ViewEngine/actions/workflows/release.yml) +
+
+