Unlike JS, F# projects require all sources to be listed in compilation order in an .fsproj file. This may look quite restrictive at first, but it does have some advantages. Since an F# project takes its roots from the .NET ecosystem, we need to follow a few obligatory steps in order to add a file to an F# project.

Many F# IDEs already provide commands to perform operations like creating a project or adding/removing a file. The steps below are only necessary if you want to do this manually.

  • Create an .fsproj file

    .fsproj files are in XML format. This may look a bit old-fashioned, but luckily the basic schema for F# projects has become much simpler in recent versions. Now the skeleton for most projects looks like this:

    <Project Sdk="Microsoft.NET.Sdk">
        <PropertyGroup>
            <TargetFramework>netstandard2.0</TargetFramework>
        </PropertyGroup>
        <ItemGroup>
            <!-- List your source files here -->
        </ItemGroup>
        <ItemGroup>
            <!-- List your package references here if you don't use Paket -->
        </ItemGroup>
    </Project>
  • Add an .fs file to the project

    F# source files end with the .fs extension. To include a new one in the project, you only need to add it to the .fsproj using a relative path with the following tag:

    <ItemGroup>
        <Compile Include="path/to/my/File.fs" />
    <ItemGroup>

    For example, if we have have an app with two files, named MyAwesomeFeature.fs and App.fs (the last one contains the entry point), it will look like this

    <ItemGroup>
        <Compile Include="MyAwesomeFeature.fs" />
        <Compile Include="App.fs" />
    <ItemGroup>

    Please be aware that in F#, file order is important. For instance, if App.fs calls MyAwesomeFeature.fs, then you must place MyAwesomeFeature.fs above App.fs.

    Let's add another file, Authentication.fs, located in another folder Shared which is at the same depth as our src folder (this can happen, for example, if the file is shared with another project, like a server). Let's see the current state of our project tree:

    myproject
        |_ src
            |_ MyAwesomeFeature.fs
            |_ App.fs
            |_ App.fsproj
        |_ Shared
            |_ Authentication.fs

    This can be expressed in the project file as:

    <ItemGroup>
        <Compile Include="../Shared/Authentication.fs" />
        <Compile Include="MyAwesomeFeature.fs" />
        <Compile Include="App.fs" />
    <ItemGroup>

For JavaScript/TypeScript target

An important thing to note is Fable will translate F# source files to ES2015 modules using JS import. This means that files which are not referenced by any other will be ignored (except the last file) including side effects. This is different behavior from .NET, where all files are compiled and executed, regardless of their relationship with the rest of the project.