dotnet-format: Prettier your C# with lint-staged & husky
John Reilly
December 22, 2020
Consistent formatting in a codebase is a good thing. We can achieve this in dotnet using dotnet format, used in combination with the npm packages husky and lint-staged. This post shows how.
If you're interested in formatting, you might be interested in linting. Whilst we use ESLint in JavaScript, there's Roslyn Analyzers for Cand you can read about it here.
Why format?
Consistent formatting makes code less confusing to newcomers and it allows whoever is working on the codebase to reliably focus on the task at hand. Not "fixing curly braces because Janice messed them up with her last commit". (A git commit message that would be tragic in so many ways.)
Once we've agreed that we want to have consistent formatting, we want it to be enforced. Enter, stage left, Prettier, the fantastic tool for formatting code. It rocks; I've been using on my JavaScript / TypeScript for the longest time. But what about C#? Well, there is a Prettier plugin for C#.... Sort of. It appears to be abandoned and contains the worrying message in the README/index.md:
> Please note that this plugin is under active development, and might not be ready to run on production code yet. It will break your code.
Not a ringing endorsement.
dotnet-format: a new hope
Margarida Pereira recently pointed me in the direction of dotnet-format which is a formatter for .NET. It's a .NET tool which:
> is a code formatter for dotnet that applies style preferences to a project or solution. Preferences will be read from an .editorconfig file, if present, otherwise a default set of preferences will be used.
It can be installed with:
The VS Code Cextension will make use of this formatter, we just need to set the following in our settings.json:
Customising our formatting
If we'd like to deviate from the default formatting options then create ourselves an .editorconfig file in the root of our project. Let's say we prefer more of the K & R style approach to braces instead of the Cdefault of Allman style. To make dotnet-format use that we'd set the following:
With this in place it's K & R all the way baby!
lint-staged / husky integration
It's become somewhat standard to use the marvellous husky and lint-staged to enforce code quality. To quote the docs:
> Run linters against staged git files and don't let 💩 slip into our code base!
To add this to our (otherwise Ccodebase), we're going to need a package.json file:
We'll install husky and lint-staged:
We should have a new file living at .husky/pre-commit which is our pre-commit hook.
Within that file we should replace npm test with npx lint-staged --relative. This is the command that will be run on commit. lint-staged will be run and we're specifying relative so that relative file paths will be used. This is important as dotnet format's --include accepts "a list of relative file or folder paths to include in formatting". Absolute paths (the default) won't work - and if we pass them to dotnet format, it will not format the files.
Finally we add the following entry to the package.json:
This is the task that will be invoked by lint-staged against files with a .cs suffix on commit. When lint-staged runs, it will pass a list of relative file paths to dotnet format. So if we'd staged two files it might end up executing a command like this:
dotnet format --include src/server-app/Server/Controllers/UserController.cs src/server-app/Server/Controllers/WeatherForecastController.cs
We should end up with a package.json that looks something like this:
By and large we don't have to think about this; the important take home is that we're now enforcing standardised formatting for all Cfiles upon commit. Everything that goes into the codebase will be formatted in a consistent fashion.
CSharpier - update 16/05/2021
There is an alternative to the CSharp Prettier project. It's being worked on by
Bela VanderVoort and it goes by the name of csharpier. When comparing CSharpier and dotnet-format, Bela put it like this:
> I could see CSharpier being the non-configurable super opinionated formatter and dotnet-format being for the people that do want to have options.
Check it out!
Discussion in the ATmosphere