Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
"isRoot": true,
"tools": {
"fable": {
"version": "5.0.0",
"version": "5.13.0",
"commands": [
"fable"
],
"rollForward": false
},
"fantomas": {
"version": "7.0.3",
"version": "7.0.5",
"commands": [
"fantomas"
],
Expand Down
53 changes: 53 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
version: 2

updates:
- package-ecosystem: "nuget"
directories:
- "/"
- "/Fable.Literate"
- "/Fable.Literate.Tests"
schedule:
interval: "weekly"
groups:
nuget-minor-and-patch:
patterns:
- "*"
update-types:
- "minor"
- "patch"

- package-ecosystem: "uv"
directory: "/"
schedule:
interval: "weekly"
groups:
python-minor-and-patch:
patterns:
- "*"
update-types:
- "minor"
- "patch"

- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
groups:
npm-minor-and-patch:
patterns:
- "*"
update-types:
- "minor"
- "patch"

- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
groups:
actions-minor-and-patch:
patterns:
- "*"
update-types:
- "minor"
- "patch"
6 changes: 3 additions & 3 deletions .github/workflows/build-blogpost.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@ jobs:
- name: Set up .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
dotnet-version: '10.0.x'

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
python-version: '3.14'

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
node-version: '22'

- name: Install uv
run: pipx install uv
Expand Down
32 changes: 32 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Repository Guidelines

## Project Structure & Module Organization

This repository is a self-documenting Fable.Python guide. Author chapter content as literate F# in `chapters/*.fs`; each file mixes executable examples with Markdown in `(** ... *)` comments. Keep chapter order synchronized between `fable-python.fsproj` and the `chapters` variable in `justfile`.

`Fable.Literate/` contains the F# parser and Markdown generator. Its cross-target tests live in `Fable.Literate.Tests/`. Fable writes generated Python to `output/`, while documentation is generated under `docs/`; do not hand-edit generated files. `docs/blogpost.md` is the published, tracked aggregate.

## Build, Test, and Development Commands

Use the repository `justfile` as the command entry point:

- `just setup` restores local .NET tools and synchronizes Python dependencies with `uv`.
- `just restore` restores NuGet and npm dependencies.
- `just build` transpiles the chapters and converter to Python.
- `just generate` rebuilds individual chapter Markdown; `just blogpost` rebuilds `docs/blogpost.md`.
- `just watch` recompiles chapter sources during development.
- `just test-all` runs the Expecto suite on .NET and the Pyxpecto build on Python. Use `just test` or `just test-python` for one target.
- `just format` applies Fantomas to F# and Ruff to generated Python; `just lint` checks generated Markdown.
- `just all` runs the complete restore, generation, formatting, and lint pipeline, but not tests.

## Coding Style & Naming Conventions

Use four-space indentation and let Fantomas format `.fs` files according to `.editorconfig`. Follow F# conventions: PascalCase for modules, types, and chapter filenames; camelCase for values and functions. Keep examples compatible with both .NET and Fable.Python unless a chapter explicitly demonstrates target-specific interop. Use Ruff for generated Python and markdownlint for generated documentation.

## Testing Guidelines

Tests use Expecto on .NET and Fable.Pyxpecto on Python. Add focused cases to `Fable.Literate.Tests/Tests.fs`, group them in descriptive `testList` values, and name cases as behaviors, such as `"parses simple markdown block"`. Run `just test-all` before submitting changes to the converter or generation pipeline.

## Commit & Pull Request Guidelines

Recent history uses short, imperative Conventional Commit prefixes such as `feat:`, `fix:`, `docs:`, and `chore:`. Keep each commit scoped. Pull requests should summarize the source change, note generated documentation updates, and list validation commands. Include the rebuilt `docs/blogpost.md` when chapter output changes; link relevant issues and add screenshots only when rendered output needs visual review.
99 changes: 2 additions & 97 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,98 +1,3 @@
# CLAUDE.md
# Claude Code Guidance

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

**F# Advent 2025 blog post project** demonstrating Fable.Python capabilities.

**Fable.Literate** is a literate programming converter (inspired by jupytext) written in F# that transpiles to Python via Fable.Python. It processes `.fs` files with embedded Markdown comments (FSharp.Formatting conventions) and outputs GitHub-flavored Markdown suitable for publishing on platforms like Hashnode.

**Key concept:** The project is self-documenting - the chapters and converter generate the blog post that documents how they work.

## Build Commands (using just)

```bash
just setup # Install Fable and Python dependencies
just restore # Restore NuGet packages
just build # Build all chapters and tools to Python
just generate # Generate individual markdown docs from chapters
just blogpost # Generate concatenated blogpost.md for publishing
just format # Format Python with ruff
just lint # Lint Markdown (markdownlint)
just watch # Watch mode for development
just clean # Clean generated files
just all # Full pipeline: restore, build, generate, format, lint
```

## Architecture

### Fable.Literate AST Pipeline

The converter follows a compiler-like architecture with three phases:

1. **Parse**: Convert source lines into a Block AST
2. **Transform**: Filter hidden blocks, resolve Python includes
3. **Print**: Render the AST as Markdown

### Literate Directives

| Directive | Purpose |
|-----------|---------|
| `(** content *)` | Raw markdown content |
| `(*** hide ***)` | Hide following code from output |
| `(*** include-python: symbol ***)` | Include generated Python for symbol |
| Regular F# code | Wrapped in ```fsharp fenced blocks |

### File Structure

```text
chapters/
├── Introduction.fs # What is Fable.Python, why use it
├── Python.fs # F# concepts for Python developers
├── GettingStarted.fs # Setup, first project, hello world
├── Interop.fs # Using existing Python libraries
├── Bindings.fs # Creating Python bindings
├── Compatibility.fs # F# features supported, limitations
├── AsyncProgramming.fs # async vs task, Python asyncio mapping
├── Testing.fs # Testing F# code with Python test runners
├── FableV5.fs # Fable v5 features, Rust core, PyPI
├── Pydantic.fs # Pydantic models, DTOs, validation
├── FastAPI.fs # Type-safe web APIs with FastAPI
├── UnitsOfMeasure.fs # Compile-time dimensional analysis
├── FableLiterate.fs # Symlink → ../Fable.Literate/App.fs
└── Summary.fs # Wrap-up, resources, repo link
Fable.Literate/
├── App.fs # Fable.Literate converter source (F#)
└── Fable.Literate.fsproj
output/
├── chapters/ # Generated Python from chapters
└── Fable.Literate/
└── app.py # Generated converter (Python)
docs/
├── *.md # Individual chapter markdown
└── blogpost.md # Concatenated for Hashnode
```

## Chapter Writing Guidelines

- Each chapter is a literate F# file with embedded markdown
- Use `(** ... *)` for markdown content
- Use `(*** hide ***)` to hide setup code (module declarations, imports)
- Use `(*** include-python: symbolName ***)` to show generated Python
- Tables are auto-formatted by markdownlint - don't fight it
- Keep code examples self-contained and buildable

## Fable.Python Considerations

- Use `Fable.Core` attributes (`[<Emit>]`, `[<Import>]`, etc.)
- Use `Fable.Python.Pydantic` for Pydantic interop
- Stick to Fable-compatible F# subset
- `task { }` compiles to native Python `async def` (Fable v5)
- `async { }` for multi-target code (Python, .NET, JS)

## Resources

- [Fable.Python docs](https://fable.io/docs/getting-started/python.html)
- [Fable.Python GitHub](https://github.com/fable-compiler/Fable.Python/)
- [Content Plan](CONTENT-PLAN.md) - Chapter structure and TODO items (important!)
Follow the repository-wide contributor and agent instructions in [AGENTS.md](AGENTS.md).
4 changes: 2 additions & 2 deletions Fable.Literate.Tests/Fable.Literate.Tests.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
<PackageReference Include="Fable.Pyxpecto" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="YoloDev.Expecto.TestSdk" Version="0.14.3" />
<PackageReference Include="Fable.Core" Version="5.0.0" />
<PackageReference Include="Fable.Python" Version="5.1.0" />
<PackageReference Include="Fable.Core" Version="5.2.0" />
<PackageReference Include="Fable.Python" Version="5.4.0" />
</ItemGroup>

<ItemGroup>
Expand Down
4 changes: 2 additions & 2 deletions Fable.Literate/Fable.Literate.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Fable.Core" Version="5.0.0" />
<PackageReference Include="Fable.Python" Version="5.1.0" />
<PackageReference Include="Fable.Core" Version="5.2.0" />
<PackageReference Include="Fable.Python" Version="5.4.0" />
</ItemGroup>

<ItemGroup>
Expand Down
4 changes: 2 additions & 2 deletions fable-python.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Fable.Core" Version="5.0.0" />
<PackageReference Include="Fable.Python" Version="5.1.0" />
<PackageReference Include="Fable.Core" Version="5.2.0" />
<PackageReference Include="Fable.Python" Version="5.4.0" />
</ItemGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion justfile
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ blogpost: build format-python
"chapters/${name}.fs" > docs/blogpost.md
# Insert version banner after the first heading using awk
awk -v ts="$timestamp" -v fv="$fable_version" \
'NR==1 {print; print ""; print "*Generated on " ts " using Fable v" fv "*"; next} {print}' \
'NR==1 {print; print ""; print "*Generated on " ts " using Fable v" fv "*."; next} {print}' \
docs/blogpost.md > docs/blogpost.tmp && mv docs/blogpost.tmp docs/blogpost.md
first=false
else
Expand Down
Loading
Loading