fantomas 8.0.0-alpha-016
See the version list below for details.
dotnet tool install --global fantomas --version 8.0.0-alpha-016
dotnet new tool-manifest
dotnet tool install --local fantomas --version 8.0.0-alpha-016
#tool dotnet:?package=fantomas&version=8.0.0-alpha-016&prerelease
nuke :add-package fantomas --version 8.0.0-alpha-016
Fantomas

An opinionated F# source code formatter.
dotnet tool install fantomas
Documentation is available at https://fsprojects.github.io/fantomas/docs/index.html
If you point a coding agent at Fantomas, give it llms.txt for an index of the documentation, or llms-full.txt for all of it in one file.
Contributing Guidelines
See the Contribution Guidelines and our contributors documentation
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.
| Version | Downloads | Last Updated |
|---|---|---|
| 8.0.0-alpha-020 | 0 | 8/27/2026 |
| 8.0.0-alpha-019 | 19 | 8/26/2026 |
| 8.0.0-alpha-018 | 23 | 8/26/2026 |
| 8.0.0-alpha-017 | 147 | 8/25/2026 |
| 8.0.0-alpha-016 | 391 | 8/25/2026 |
| 8.0.0-alpha-015 | 211 | 8/24/2026 |
| 8.0.0-alpha-014 | 107 | 8/20/2026 |
| 8.0.0-alpha-013 | 105 | 8/19/2026 |
| 8.0.0-alpha-012 | 6,877 | 4/16/2026 |
| 8.0.0-alpha-011 | 172 | 4/15/2026 |
| 8.0.0-alpha-010 | 169 | 4/15/2026 |
| 8.0.0-alpha-009 | 334 | 4/3/2026 |
| 8.0.0-alpha-008 | 177 | 3/25/2026 |
| 8.0.0-alpha-007 | 185 | 3/10/2026 |
| 8.0.0-alpha-006 | 150 | 3/9/2026 |
| 8.0.0-alpha-005 | 137 | 3/9/2026 |
| 8.0.0-alpha-004 | 131 | 3/7/2026 |
| 8.0.0-alpha-003 | 148 | 3/3/2026 |
| 8.0.0-alpha-002 | 389 | 12/15/2025 |
| 7.0.6 | 3,117 | 8/19/2026 |
### Added
- `fantomas check <paths>` and `fantomas daemon`, beside the `--check` and `--daemon` flags that named them. Both flags keep working and are not deprecated, so no pipeline and no editor integration has anything to change; each means exactly what its command means. The commands exist because a command can have a `--help` page of its own and a flag cannot: `fantomas check --help` lists the flags checking has any use for and leaves out the ones it would refuse. When standard error is a terminal, the older spelling prints a one line note saying how it is spelled now; a redirected stream never sees it, so it stays out of build logs and out of the daemon an editor starts. [#3416](https://github.com/fsprojects/fantomas/pull/3416)
- `fantomas profile <paths>`, replacing the `--profile` flag. It formats one file at a time so the timings can be compared, writes nothing, reports every file slowest first with its line count and the number of define combinations it had to format, and ends with a total. `--profile` measured files formatted in parallel, so each stopwatch recorded wall clock under contention: a six line signature file and a file of several thousand lines came back with the same figure. [#3416](https://github.com/fsprojects/fantomas/pull/3416)
- A `--help` page for each command. `fantomas --help` is the overview, with the commands, the flags, worked examples and links; `fantomas check --help`, `fantomas profile --help` and `fantomas daemon --help` are about one command each. Which flags a command's page lists is asked of the same rule that refuses them at run time, so the page cannot come to disagree with the tool. Every page names the tool the way this run was started, so a local tool install is shown `dotnet fantomas` rather than a command it does not have. [#3416](https://github.com/fsprojects/fantomas/pull/3416)
- `--flag=value` alongside `--flag value`, so `--out=build` and `--verbosity=d` work. Only the space separated form was accepted before, and `--out=build` was read as an input path that does not exist. [#3416](https://github.com/fsprojects/fantomas/pull/3416)
- `--` ends the flags, so a path beginning with a dash can be named after it. It used to be read as an input path itself, which meant no such path could be given at all. [#3416](https://github.com/fsprojects/fantomas/pull/3416)
- `--json` for `fantomas profile`, carrying the same files as the text report with each file's line count, define combinations and milliseconds, and the run's own total as `elapsedMilliseconds`. Ordered by path rather than slowest first, since a reader that wants them by time can sort them. [#3416](https://github.com/fsprojects/fantomas/pull/3416)
- Running with no path at all formats the folder you are in, so `fantomas` is `fantomas .`, and `check` and `profile` read a bare invocation the same way. Formatting and checking used to refuse with `No input path provided.` This is what `ruff format` and `dotnet format` do. Note that it walks every F# file below the working directory, and that a `.fantomasignore` in a repository below it governs only that repository's files. [#3416](https://github.com/fsprojects/fantomas/pull/3416)
### Changed
- Breaking: `Fantomas.Core.ParseException` now derives from `FormatException`, as the other exceptions the library raises already did, so `:? FormatException` catches every way formatting can fail. It could not before: it was declared with F#'s `exception` keyword, which cannot name a base class. Its `Message` names the first error by position instead of dumping every diagnostic record through `%A`, and the records are reachable as `.Diagnostics` rather than only by matching the exception pattern. Code matching ` ParseException diagnostics ->` becomes ` :? ParseException as e -> e.Diagnostics`; raising and constructing it are unchanged. [#3415](https://github.com/fsprojects/fantomas/pull/3415)
- A construct Fantomas cannot model is now reported through the same mechanism as a parse failure, instead of as a `%A` dump of a syntax tree node in the middle of the message. The report says in words what could not be modelled, names the union case responsible, positions it in the file being formatted rather than in the `tmp.fsx` the parser was handed, and draws a snippet of the source with a caret under the construct, so the enclosing declaration is visible without having to bisect the file by hand. The syntax tree node is still reported, at `--verbosity d`, where it serves whoever triages the issue rather than whoever files it. This covers every such report, from a type the transformer has no Oak node for to the chain and leading-keyword invariants behind it. `--check` and the daemon position them the same way, so an editor shows its user the snippet rather than a bare line and column. [#3415](https://github.com/fsprojects/fantomas/pull/3415)
- Breaking: everything a format or check run prints was rewritten. Each file gets a sentence of its own opening with a status character, `+ src/A.fs was formatted.`, and the run ends with a line of counts, `2 files formatted, 30 unchanged.` This replaces the bordered table of headings that a folder run printed, which bypassed the logger and so carried no timestamp at `--verbosity d` while everything around it did. A run over a single named file is answered on its own terms and has no summary added to it. Any script reading this output needs updating; `--json` exists for a caller that has to act on the result rather than read it. [#3416](https://github.com/fsprojects/fantomas/pull/3416)
- Breaking: `.fantomasignore` is resolved per file, the nearest one at or above it, which is what the daemon has always done. The command line used to resolve one file for the whole run from the directory it started in, so an ignore file in a subfolder was honoured by an editor and invisible to a pipeline: the same file was skipped in one and formatted in the other. A nested ignore file that used to have no effect on a command line run now has one. [#3416](https://github.com/fsprojects/fantomas/pull/3416)
- Breaking: a folder that `.fantomasignore` names is no longer opened. Up to now every file inside it was found and then rejected one at a time; the folder is now passed over, so nothing inside it is read, counted or reported. A folder pattern spelled with a trailing separator, `vendor/`, closes the folder as `vendor` always did. Nothing is formatted that was not formatted before. [#3416](https://github.com/fsprojects/fantomas/pull/3416)
- Breaking: a run that skipped files no longer says how many. The count could not be honest: an ignore pattern naming a file can be counted, and one naming a folder cannot, because the folder is never opened and what is inside it is unknown by design. A number right about the first and blind to the second reads as though it covered both. A file named on the command line still gets a line of its own, since a count is the only other place a path could be accounted for and no count carries this one; a file found by walking a folder, and the folder itself, are named at `--verbosity d`. [#3416](https://github.com/fsprojects/fantomas/pull/3416)
- Breaking: `--json` no longer names a file that `.fantomasignore` matched, and `ignored` is no longer a status a file can carry. Nothing counts them in its place, for the reason above: `files` is what the run looked at. [#3416](https://github.com/fsprojects/fantomas/pull/3416)
- Breaking: a token beginning with a dash that is not a flag Fantomas has is reported as an unknown flag, with the nearest flag it could be a misspelling of. `fantomas --chek src` said `Input path '--chek' not found.` and now says `'--chek' is not a Fantomas flag. Did you mean '--check'?` Every unrecognised token used to be taken for an input path, so a misspelled flag was reported as a file that is not there. [#3416](https://github.com/fsprojects/fantomas/pull/3416)
- Breaking: a malformed command line exits 1 rather than 2. The documented exit codes have only ever been 0, 99 and 1; 2 came from the argument parser and was never one Fantomas chose. [#3416](https://github.com/fsprojects/fantomas/pull/3416)
- Breaking: repeating a flag is allowed and the last one wins, where it used to be refused outright with `argument '--check' has been specified more than once`. This is the Unix norm and stops a script that builds its arguments up from failing on a duplicate. `--out` is the exception and is still refused, because it decides where files are written and choosing between two of them quietly is choosing where the work lands. [#3416](https://github.com/fsprojects/fantomas/pull/3416)
- Breaking: a file that could not be parsed is reported as `src/A.fs could not be parsed by Fantomas:` rather than `Fantomas could not parse src/A.fs:`, and a construct that could not be modelled as `src/A.fs could not be formatted by Fantomas:`. Every line of a report now begins with the path it is about, so the column can be read down. A script matching on the old text needs updating. [#3416](https://github.com/fsprojects/fantomas/pull/3416)
- Breaking: `--check` no longer reports a file that will not parse twice, once as an error and once as needing formatting. It is an error and nothing else, which is also what its exit code has always said. [#3416](https://github.com/fsprojects/fantomas/pull/3416)
- Breaking: `--force` announces invalid output as a warning on standard error rather than on standard out, and says what happened: `src/A.fs was formatted, but the result is not valid F# code. It was written because --force was given.` It says Fantomas wrote F# it believes is not valid, which is the opposite of the ordinary run of things standard out carries. [#3416](https://github.com/fsprojects/fantomas/pull/3416)
- `--version` prints the commit hash trimmed to the short form the `--help` page has always shown, and the whole of it at `--verbosity d`. `Fantomas.Client` is unaffected: it cuts the version at `+` and drops the build metadata whatever it holds. [#3416](https://github.com/fsprojects/fantomas/pull/3416)
- A failure says what went wrong at any verbosity. The exception's message used to be kept for `--verbosity d` and nothing printed in its place at normal, so an unreadable file reported only `A.fs could not be formatted.` and left `Access to the path is denied` behind a flag nobody knew to pass. Detailed verbosity now adds the type and the stack trace below that line rather than instead of it. [#3416](https://github.com/fsprojects/fantomas/pull/3416)
- Colour is used where the terminal takes it and dropped where it does not, decided for standard out and standard error separately, so a run whose output is piped keeps its colour on the diagnostics still going to the terminal. The status character is `+`, `=`, `-`, `!` and `x` where the output is redirected or the console is not UTF-8, and `✔`, `=`, `○`, `!` and `✘` where it is; both carry the same five states, and the words beside them say the same thing either way. `NO_COLOR` is honoured. [#3416](https://github.com/fsprojects/fantomas/pull/3416)
- Files are reported in path order rather than in whichever order the file system returned them, so two machines running one command print the same thing. [#3416](https://github.com/fsprojects/fantomas/pull/3416)
- Breaking: a run writing to `--out` reports what was written rather than what changed, `32 files written to build, 2 reformatted.`, since under `--out` every input produces an output file whether or not its content changed. A single file named on the command line says where it went, `src/A.fs was formatted and written to build/A.fs.`, where it used to say only that it was formatted or unchanged and leave the destination unmentioned. [#3416](https://github.com/fsprojects/fantomas/pull/3416)
- A run that looked at no file at all says so on standard error and exits 0, which a check used to pass over in silence: `fantomas check` on an empty folder, or on one an ignore file emptied, printed nothing and read as a green build. Formatting and checking now report this the same way, as they do everything else they both have to say. [#3416](https://github.com/fsprojects/fantomas/pull/3416)
### Removed
- Breaking: the `--profile` flag. `fantomas profile <paths>` replaces it, and a run that names the flag is refused with `'--profile' is not a Fantomas flag. 'profile' is a command: try 'fantomas profile <paths>'.` and exits 1. It was not kept working as an alias, alone among the flags that became commands: the flag wrote formatted files to disk as a side effect of measuring them and the command deliberately writes nothing, so an alias would have gone on being accepted and silently stopped writing, which is worse than a run that stops and says what to type. `--profile --check`, which was accepted and did nothing at all, no longer has anything to combine. [#3416](https://github.com/fsprojects/fantomas/pull/3416)
### Fixed
- Getting a 'no Oak node is defined for this type' error when formatting a p/invoke signature with nullability annotations. [#3414](https://github.com/fsprojects/fantomas/issues/3414)
- An `extern` declaration returning an array, such as `extern byte[] f(int options)`, lost the element type of the array and was written as ``extern `[]` f(int options)``. [#3415](https://github.com/fsprojects/fantomas/pull/3415)
- A file whose formatted output is not valid F# named that file twice in one line, `Failed to format file: A.fs : Formatting A.fs leads to invalid F# code`, with a spaced colon in the middle. [#3416](https://github.com/fsprojects/fantomas/pull/3416)
- At `--verbosity d`, a single unchanged file was reported twice in two different spellings, once as `'A.fs' was unchanged` and once as `A.fs was unchanged.` [#3416](https://github.com/fsprojects/fantomas/pull/3416)