diff --git a/.editorconfig b/.editorconfig index 711fa53..29bab96 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,10 +6,16 @@ trim_trailing_whitespace=true [makefile] indent_style=tab -[*.{gradle,java,kt,kts,sh,xml,yaml,yml}] +[*.{cs,gradle,java,kt,kts,sh,xml,yaml,yml}] indent_size=2 max_line_length=100 +; https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ +; https://google.github.io/styleguide/csharp-style.html#whitespace-rules +[*.cs] +csharp_new_line_before_open_brace = none +dotnet_diagnostic.IDE0011.severity = error + [*.{js,jsx,ts,tsx}] indent_size=2 max_line_length=80 diff --git a/Dockerfile b/Dockerfile index bb66920..9e6a019 100644 --- a/Dockerfile +++ b/Dockerfile @@ -40,6 +40,7 @@ apk add --no-cache --virtual .build-deps \ py3-pip \ python3-dev apk add --no-cache \ + dotnet8-sdk \ libxslt \ nodejs \ python3 diff --git a/Makefile b/Makefile index 7c918af..21f83c1 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,7 @@ shell: # Runs tests .PHONY: test test: - docker run --rm -v "$${PWD}/test:/test" "$$(docker build --network=host -q .)" sh -c \ + docker run --rm --init -v "$${PWD}/test:/test" "$$(docker build --network=host -q .)" sh -c \ 'cd /tmp \ && cp -r /test/before actual \ && cp -r /test/after expected \ diff --git a/README.md b/README.md index 1d5622c..e437377 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ This repo currently contains a single [pre-commit](https://pre-commit.com/) hook - [xsltproc](http://www.xmlsoft.org/xslt/xsltproc.html) from libxslt v10139 for XML - [terraform fmt](https://github.com/hashicorp/terraform) v1.9.8 for Terraform - [ClangFormat](https://clang.llvm.org/docs/ClangFormat.html) v18.1.8 for C++, Protobuf +- [dotnet format](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-format) v8.0.111 for C# - [SVGO](https://github.com/svg/svgo) v3.3.2 for SVG - [Taplo](https://taplo.tamasfe.dev/) v0.9.3 for TOML - Custom regex transformations (basically [sed](https://en.wikipedia.org/wiki/Sed)), for example: diff --git a/entry.ts b/entry.ts index 7d0c5e4..dd48d4e 100644 --- a/entry.ts +++ b/entry.ts @@ -1,7 +1,8 @@ #!/usr/bin/env node import { exec } from "child_process"; -import { createReadStream, readFile, writeFile } from "fs"; +import { createReadStream } from "fs"; +import { readFile, unlink, writeFile } from "fs/promises"; import { createInterface } from "readline"; /** @@ -49,37 +50,32 @@ const run = (...args: string[]) => export const isTruthy = (value: T): value is NonNullable => !!value; /** Reads a file, transforms its contents, and writes the result if different */ -const transformFile = (path: string, transform: (before: string) => string) => - new Promise((resolve, reject) => { - readFile(path, "utf8", (err, data) => { - // File unreadable - if (err) { - reject(err); - return; - } - - // File empty - if (data === "") { - resolve(); - return; - } +const transformFile = async ( + path: string, + transform: (before: string) => string, +) => { + const data = await readFile(path, "utf8"); + + // File empty + if (data === "") { + return; + } - // File unmodified - const after = transform(data); - if (data === after) { - resolve(); - return; - } + // File unmodified + const after = transform(data); + if (data === after) { + return; + } - // File modified - writeFile(path, after, "utf8", err => (err ? reject(err) : resolve())); - }); - }); + // File modified + await writeFile(path, after, "utf8"); +}; const enum HookName { Autoflake = "autoflake", Black = "Black", ClangFormat = "ClangFormat", + DotnetFormat = "dotnet format", EsLint = "ESLint", Gofmt = "gofmt", GoogleJavaFormat = "google-java-format", @@ -191,6 +187,46 @@ const HOOKS: Record = { include: /\.(cpp|proto$)/, runAfter: [HookName.Sed], }), + [HookName.DotnetFormat]: createLockableHook({ + action: async sources => { + // Create project file + const projectFile = `dotnet-format-${Math.random()}.csproj`; + await writeFile( + projectFile, + ` + + false + net8.0 + + + ${sources.map(s => ``).join("")} + + `, + ); + try { + await run( + "dotnet", + "format", + "style", + projectFile, + "--verbosity", + "quiet", + ); + await run( + "dotnet", + "format", + "whitespace", + projectFile, + "--verbosity", + "quiet", + ); + } finally { + await unlink(projectFile); + } + }, + include: /\.cs$/, + runAfter: [HookName.Sed], + }), [HookName.EsLint]: createLockableHook({ action: async sources => { try { diff --git a/test/after/hello.cs b/test/after/hello.cs new file mode 100644 index 0000000..fa00de6 --- /dev/null +++ b/test/after/hello.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; + +namespace HelloWorld { + public class Program { + public static void Main(string[] args) { + Console.WriteLine("Hello, World!"); + var numbers = new List { 1, 2, 3 }; + foreach (var number in numbers) { + Console.WriteLine($"Number: {number}"); + } + } + } +} diff --git a/test/before/hello.cs b/test/before/hello.cs new file mode 100644 index 0000000..c5a09d7 --- /dev/null +++ b/test/before/hello.cs @@ -0,0 +1,14 @@ + using System ; +using System.Collections.Generic; + +namespace HelloWorld{ + public class Program +{public static void Main(string[]args) +{ +Console.WriteLine("Hello, World!"); +var numbers=new List{1,2,3}; +foreach (var number in numbers) +Console.WriteLine($"Number: {number}"); +} +} +}