-
Notifications
You must be signed in to change notification settings - Fork 86
Add fsharp-project-structure skill #248
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
T-Gro
wants to merge
4
commits into
dotnet:main
Choose a base branch
from
T-Gro:feature/fsharp-projects-mgmt
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 3 commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
0f3d7b3
Add fsharp-project-structure skill for .fsproj file ordering and .fsi…
T-Gro 194579d
Add fsharp-project-structure skill for .fsproj file ordering and .fsi…
T-Gro ddfcf42
Merge branch 'feature/fsharp-projects-mgmt' of https://github.com/T-G…
T-Gro da175b1
Update plugins/dotnet/skills/fsharp-project-structure/SKILL.md
T-Gro File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| --- | ||
| name: fsharp-project-structure | ||
| description: "F# .fsproj file ordering, adding .fs/.fsi files, fixing FS0039 FS0010 FS0034 compilation order errors, signature files." | ||
| --- | ||
|
|
||
| # F# Project File Structure | ||
|
|
||
| F# compiles `<Compile Include>` items sequentially, top to bottom. A file can only reference types/modules from files listed **above** it in the .fsproj. | ||
|
|
||
| ## Inputs | ||
|
|
||
| | Input | Required | Description | | ||
| |-------|----------|-------------| | ||
| | .fsproj file | Yes | The F# project file to modify | | ||
|
|
||
T-Gro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ## File compilation order | ||
|
|
||
| - File B can use types from file A only if A is listed BEFORE B in the .fsproj | ||
| - Entry point (`Program.fs` or `[<EntryPoint>]`) must be LAST | ||
| - When adding a file: check its `open` declarations, insert AFTER the last dependency but BEFORE any consumer | ||
| - Wrong order symptom: `FS0039 "The type/value/namespace 'X' is not defined"` where X exists in another project file | ||
|
|
||
| Example ordering: | ||
| ```xml | ||
| <ItemGroup> | ||
| <Compile Include="Domain.fs" /> | ||
| <Compile Include="Services.fs" /> | ||
| <Compile Include="Program.fs" /> | ||
| </ItemGroup> | ||
| ``` | ||
|
|
||
| ## Signature files (.fsi) | ||
|
|
||
| - A `.fsi` defines the public API contract for its companion `.fs` file | ||
| - Contains type signatures and `val` declarations — no implementation | ||
| - `.fsi` MUST appear immediately BEFORE its `.fs` in the Compile list | ||
| - If `.fsi` exists, public members in `.fs` not declared in `.fsi` become internal | ||
| - `FS0034` (ValueNotContained): signature and implementation don't match | ||
|
|
||
| Example with signatures: | ||
| ```xml | ||
| <ItemGroup> | ||
| <Compile Include="Domain.fsi" /> | ||
| <Compile Include="Domain.fs" /> | ||
| <Compile Include="Services.fsi" /> | ||
| <Compile Include="Services.fs" /> | ||
| <Compile Include="Program.fs" /> | ||
| </ItemGroup> | ||
| ``` | ||
|
|
||
| ## Workflow | ||
|
|
||
| 1. Identify where the new file fits in the dependency chain | ||
| 2. Add `<Compile Include>` at the correct position in .fsproj | ||
| 3. Run `dotnet build` to verify | ||
|
|
||
| ## Pitfalls | ||
|
|
||
| | Pitfall | Fix | | ||
| |---------|-----| | ||
| | New file appended after Program.fs | Insert before Program.fs, after its dependencies | | ||
| | `.fsi` placed AFTER its `.fs` | Must be immediately BEFORE | | ||
| | Deleting `.fsi` to "fix" FS0034 | Update the `.fsi` to match the new API instead | | ||
T-Gro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| | Circular dependency between files | Split one file into two | | ||
| | Modifying source to work around order | Reorder `<Compile>` items in .fsproj instead | | ||
11 changes: 11 additions & 0 deletions
11
tests/dotnet/fsharp-project-structure/BrokenOrder/BrokenOrder.fsproj
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
| <PropertyGroup> | ||
| <OutputType>Exe</OutputType> | ||
| <TargetFramework>net8.0</TargetFramework> | ||
| </PropertyGroup> | ||
| <ItemGroup> | ||
| <Compile Include="Services.fs" /> | ||
| <Compile Include="Domain.fs" /> | ||
| <Compile Include="Program.fs" /> | ||
| </ItemGroup> | ||
| </Project> |
15 changes: 15 additions & 0 deletions
15
tests/dotnet/fsharp-project-structure/BrokenOrder/Domain.fs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| module BrokenOrder.Domain | ||
|
|
||
| type OrderId = OrderId of int | ||
|
|
||
| type OrderItem = { | ||
| Name: string | ||
| Quantity: int | ||
| Price: decimal | ||
| } | ||
|
|
||
| type Order = { | ||
| Id: OrderId | ||
| CustomerName: string | ||
| Items: OrderItem list | ||
| } |
14 changes: 14 additions & 0 deletions
14
tests/dotnet/fsharp-project-structure/BrokenOrder/Program.fs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| open BrokenOrder.Domain | ||
| open BrokenOrder.Services | ||
|
|
||
| let order = { | ||
| Id = OrderId 1 | ||
| CustomerName = "Alice" | ||
| Items = [ | ||
| { Name = "Widget"; Quantity = 2; Price = 9.99m } | ||
| { Name = "Gadget"; Quantity = 1; Price = 24.99m } | ||
| ] | ||
| } | ||
|
|
||
| let total = processOrder order | ||
| printfn "Order total: $%M" total |
12 changes: 12 additions & 0 deletions
12
tests/dotnet/fsharp-project-structure/BrokenOrder/Services.fs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| module BrokenOrder.Services | ||
|
|
||
| open BrokenOrder.Domain | ||
|
|
||
| let calculateTotal (order: Order) = | ||
| order.Items | ||
| |> List.sumBy (fun item -> item.Price * decimal item.Quantity) | ||
|
|
||
| let processOrder (order: Order) = | ||
| let total = calculateTotal order | ||
| printfn "Processing order for %s: $%M" order.CustomerName total | ||
| total |
15 changes: 15 additions & 0 deletions
15
tests/dotnet/fsharp-project-structure/OrderService/Domain.fs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| module OrderService.Domain | ||
|
|
||
| type OrderId = OrderId of int | ||
|
|
||
| type OrderItem = { | ||
| Name: string | ||
| Quantity: int | ||
| Price: decimal | ||
| } | ||
|
|
||
| type Order = { | ||
| Id: OrderId | ||
| CustomerName: string | ||
| Items: OrderItem list | ||
| } |
11 changes: 11 additions & 0 deletions
11
tests/dotnet/fsharp-project-structure/OrderService/OrderService.fsproj
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
| <PropertyGroup> | ||
| <OutputType>Exe</OutputType> | ||
| <TargetFramework>net8.0</TargetFramework> | ||
| </PropertyGroup> | ||
| <ItemGroup> | ||
| <Compile Include="Domain.fs" /> | ||
| <Compile Include="Services.fs" /> | ||
| <Compile Include="Program.fs" /> | ||
| </ItemGroup> | ||
| </Project> |
14 changes: 14 additions & 0 deletions
14
tests/dotnet/fsharp-project-structure/OrderService/Program.fs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| open OrderService.Domain | ||
| open OrderService.Services | ||
|
|
||
| let order = { | ||
| Id = OrderId 1 | ||
| CustomerName = "Alice" | ||
| Items = [ | ||
| { Name = "Widget"; Quantity = 2; Price = 9.99m } | ||
| { Name = "Gadget"; Quantity = 1; Price = 24.99m } | ||
| ] | ||
| } | ||
|
|
||
| let total = processOrder order | ||
| printfn "Order total: $%M" total |
12 changes: 12 additions & 0 deletions
12
tests/dotnet/fsharp-project-structure/OrderService/Services.fs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| module OrderService.Services | ||
|
|
||
| open OrderService.Domain | ||
|
|
||
| let calculateTotal (order: Order) = | ||
| order.Items | ||
| |> List.sumBy (fun item -> item.Price * decimal item.Quantity) | ||
|
|
||
| let processOrder (order: Order) = | ||
| let total = calculateTotal order | ||
| printfn "Processing order for %s: $%M" order.CustomerName total | ||
| total |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| scenarios: | ||
| - name: "Add a module to an F# project" | ||
| prompt: | | ||
| Add a Validation.fs module to the F# project at OrderService/ that validates | ||
| orders before processing. It should check that the order has at least one item | ||
| and that the customer name is not empty. Return a Result<Order, string> with | ||
| a descriptive error message on failure. Then update Program.fs to validate the | ||
| order before processing it, printing the error if validation fails. | ||
| setup: | ||
| copy_test_files: true | ||
| assertions: | ||
| - type: "exit_success" | ||
| - type: "file_contains" | ||
| path: "OrderService/OrderService.fsproj" | ||
| value: "Validation.fs" | ||
| rubric: | ||
| - "Created Validation.fs with a validation function returning Result<Order, string>" | ||
| - "Inserted Compile Include=\"Validation.fs\" AFTER Domain.fs and BEFORE Program.fs in the .fsproj" | ||
| - "Did NOT place Validation.fs after Program.fs in the Compile item list" | ||
| - "Updated Program.fs to call the validation function before processing" | ||
| - "Ran dotnet build and it succeeded" | ||
| timeout: 120 | ||
|
|
||
| - name: "Fix broken file order causing FS0039" | ||
| prompt: | | ||
| The F# project at BrokenOrder/ fails to build. Diagnose and fix the issue. | ||
| setup: | ||
| copy_test_files: true | ||
| assertions: | ||
| - type: "exit_success" | ||
| - type: "file_contains" | ||
| path: "BrokenOrder/BrokenOrder.fsproj" | ||
| value: "Domain.fs" | ||
T-Gro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| rubric: | ||
| - "Ran dotnet build and observed FS0039 or similar 'not defined' errors" | ||
| - "Identified that the failure is caused by wrong file order in the .fsproj — Services.fs is listed before Domain.fs" | ||
| - "Reordered Compile items so Domain.fs appears before Services.fs in the .fsproj" | ||
| - "Did NOT modify any .fs source files to work around the ordering issue" | ||
| - "Ran dotnet build after the fix and confirmed it succeeds" | ||
| timeout: 120 | ||
|
|
||
| - name: "Add a signature file to define public API" | ||
| prompt: | | ||
| Add a signature file (Domain.fsi) for the Domain module in the F# project at | ||
| OrderService/ to explicitly define its public API surface. The signature should | ||
| expose all types and keep the module's public contract clear. Make sure the | ||
| project still builds after adding the signature file. | ||
| setup: | ||
| copy_test_files: true | ||
| assertions: | ||
| - type: "exit_success" | ||
| - type: "file_contains" | ||
| path: "OrderService/OrderService.fsproj" | ||
| value: "Domain.fsi" | ||
| rubric: | ||
| - "Created Domain.fsi with type signatures matching the types in Domain.fs" | ||
| - "Inserted Compile Include=\"Domain.fsi\" immediately BEFORE Domain.fs in the .fsproj" | ||
| - "Did NOT place Domain.fsi after Domain.fs in the Compile item list" | ||
| - "The .fsi file contains type declarations (not implementation code)" | ||
| - "The project builds successfully with the signature file present" | ||
| timeout: 120 | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.