This is the F# counterpart to part one’s C# walkthrough — same demo, same template, same idea: you don’t have to hand-write Excel styling code. If someone already built the spreadsheet you need to reproduce, you can reverse-engineer it into real source code, then wire in your own data. The full project is on GitHub: Kookerella.Demo.DecompileToSource.

What we’re starting from

The same plain Excel file used in the C# demo: a bold, 16pt “INVOICE” title, a white-on-navy header row with a bottom border, and two sample line-item rows with an Amount column computed as Qty * Unit Price.

Step 1: Install the tool

dotnet tool install -g Kookerella.FsOpenXmlDsl.Mcp

Step 2: Reverse-engineer the template

fsopenxmldsl-mcp convert templates/InvoiceTemplate.xlsx --lang fsharp -o decompiled/InvoiceTemplate.g.fsx

This prints out F# code that reproduces the workbook exactly — an F# script, meant to be run with dotnet fsi, not a .csproj-based project. Its first line is:

#r "nuget: Kookerella.FsOpenXmlDsl, 0.3.1"

#r "nuget: <package>, <version>" is F# Interactive’s own directive for pulling a NuGet package directly into a script — the closest F# equivalent to the C# demo’s #:package line, and just as portable: it pins the exact version the tool itself was built against, so it works on any machine with the .NET 10 SDK, not just the one that generated it. You can run it immediately:

cd decompiled
dotnet fsi InvoiceTemplate.g.fsx

That produces output.xlsx, pixel-for-pixel the same as the original template — title, header styling, borders, currency formatting, and the Qty * Unit Price formulas all expressed as plain F# values (cell (Text "...", style = ...), Formula(...), and so on).

Step 3: Adding the package to a real project

The script above pulls the package in via #r "nuget: ...". In a compiled project you add it the usual way:

dotnet add package Kookerella.FsOpenXmlDsl

Step 4: Turning the decompiled snippet into real code

The decompiled script is disposable — it’s the starting point, not the deliverable. The diff to a maintained version is deliberately small:

  1. Wrap the expression in a build function that takes OrderLine list instead of running standalone.

  2. Replace the two hardcoded item rows with a projection over that data:

    let dataRows =
        orders
        |> List.mapi (fun i order ->
            let r = 4 + i
            let amount = order.Quantity * order.UnitPrice
            row [ cell (Text order.Item)
                  cell (Number order.Quantity)
                  cell (Number order.UnitPrice, style = { CellStyle.Default with NumberFormat = Some Currency })
                  cell (Formula(sprintf "B%d*C%d" r r, Some amount), style = { CellStyle.Default with NumberFormat = Some Currency }) ])
    
  3. Splice the generated rows into the sheet with yield!, instead of listing two hardcoded rows:

    sheet "Invoice" [
        yield row [ (* title row, exactly as decompiled *) ]
        yield row ([ (* header row, exactly as decompiled *) ], index = 2)
        yield! dataRows
    ]
    

    This is the F# mirror of the C# demo’s collection-expression spread (.. dataRows) — same idea, splice a computed sequence into a literal, different syntax.

Everything else — every style value, every color, every border — is copied verbatim from the decompiled script. The styling was never hand-written by a developer in the first place, so there’s nothing to “port” — only the two data rows needed to become data-driven.

Step 5: Prove it stays correct

The test suite checks the same three things the C# demo’s tests do:

  • Schema validity — runs a real OpenXmlValidator over the output.
  • Correctness — feeds in 0, 1, and 3 orders and checks the row count and the Amount formula for each one.
  • Styling preservation — pins the exact colors, bold, font size, and border style that came from the original template.

Recap

StepCommandOutput
Install the tooldotnet tool install -g Kookerella.FsOpenXmlDsl.Mcpfsopenxmldsl-mcp on your PATH
Reverse-engineerfsopenxmldsl-mcp convert InvoiceTemplate.xlsx --lang fsharp -o InvoiceTemplate.g.fsxA portable F# script that reproduces the original .xlsx exactly
Add the packagedotnet add package Kookerella.FsOpenXmlDslUsed directly — there’s no separate wrapper package in F#
Make it data-drivenReplace hardcoded rows with a projection over your own data model, spliced in with yield!A maintained generator
Verifydotnet testSchema validity, formula correctness, and styling all pinned by real tests

Next up: the same demo with no code at all, just an XSLT transform.