Skip to content

chore: add repo-wide consistent-formatting setup#261

Merged
damyanpetev merged 5 commits into
masterfrom
dpetev/formatting-setup
Jul 22, 2026
Merged

chore: add repo-wide consistent-formatting setup#261
damyanpetev merged 5 commits into
masterfrom
dpetev/formatting-setup

Conversation

@damyanpetev

@damyanpetev damyanpetev commented Jul 21, 2026

Copy link
Copy Markdown
Member

Do not squash
Steps:

Current implementation

Layer Tool What it covers Where it runs
C# whitespace dotnet format whitespace --folder Indentation, braces, spacing, new-lines (IDE0055) CI gate (--verify-no-changes)
C# style analysis EnforceCodeStyleInBuild + .editorconfig IDE0055 / IDE2000 / using placement as build warnings Every dotnet build, IDE squiggles
JS/TS/JSON/YAML/CSS Prettier Formatting (single quotes; doubles for YAML via override) pre-commit via lint-staged, CI gate (npm run format:check)
Pre-commit native git hooks + lint-staged Auto-formats staged JS-side files .githooks/pre-commitnpx --no-install lint-staged

Git hooks use the native core.hooksPath mechanism (git ≥ 2.9) instead of husky — the committed .githooks/ directory is activated by the npm prepare script (git config core.hooksPath .githooks), which runs automatically on npm install/npm ci. Same pattern as igniteui-angular (cec370f). Caveats: on a fresh clone hooks only work after npm install (both to set the config and because npx --no-install needs node_modules), and the hook file must keep its executable bit for Linux/macOS clones. Hooks can be tested without committing via git hook run pre-commit (git ≥ 2.36).

The one-time repo-wide normalization was applied as a single mechanical commit (dotnet format whitespace . --folder --exclude templates node_modules + npm run format — reproducible by rerunning the commands). Its SHA is listed in .git-blame-ignore-revs so git blame and GitHub's blame view skip it; each clone needs a one-time git config blame.ignoreRevsFile .git-blame-ignore-revs. This only affects blame — in diffs use git diff -w / GitHub's "Hide whitespace" (?w=1).

Key files: .editorconfig, Directory.Build.props (EnforceCodeStyleInBuild), ci.yml (both verify steps), package.json (format, format:check, prepare, lint-staged), .prettierrc, .prettierignore, .githooks/, .git-blame-ignore-revs.

.editorconfig structure

The file is Visual Studio's generated .editorconfig used as a neutral base, with repo-specific deviations marked REPO: inline:

  • var preferences → true (VS default false; codebase uses var heavily, matches aspnetcore)
  • csharp_style_namespace_declarations = block_scoped (repo majority incl. generated components; aspnetcore prefers file_scoped via IDE0161)
  • dotnet_style_allow_multiple_blank_lines_experimental = false + IDE2000 warning (VS default allows them; aspnetcore also disallows)
  • csharp_preserve_single_line_statements = false (VS default true; matches aspnetcore)
  • end_of_line / charset deliberately unset (VS default crlf) — needs a .gitattributes first, see open items
  • insert_final_newline = true under [*] (VS default false)

Enforced severities live in one block near the bottom. Note aspnetcore's .editorconfig keeps IDE0055 at suggestion and relies on a CI dotnet format check alone; we use warning for IDE/build visibility. aspnetcore's larger CA/IDE analyzer set (correctness, perf, hygiene — mostly without auto-fix) is captured as a commented "Future candidates" section at the end of our .editorconfig, including their pattern of relaxing rules to suggestion for test projects.

Why not plain dotnet format (the important caveat)

Full dotnet format (or its style/analyzers verbs) corrupts this repo. The library multi-targets net8.0/net9.0/net10.0; fixers run once per TFM and Roslyn merges disagreeing edits by writing literal conflict markers into sources:

<<<<<<< TODO: Unmerged change from project 'IgniteUI.Blazor.Lite(net9.0)', Before:
...
>>>>>>> After

Verified empirically in a throwaway worktree: a full dotnet format IgniteUI.Blazor.Lite.slnx run left ~7.7k conflict-marker errors and did not converge on re-verify. Known upstream issues:

  • dotnet/format#1634 — "Unmerged change from project" after running dotnet format on multi-targeted projects (our exact symptom)
  • dotnet/format#1574 — files should be left untouched when per-project changes cannot be merged, instead of being written out in a non-building state
  • dotnet/roslyn#74366 — the underlying Roslyn behavior: cross-target refactoring conflicts are emitted as "Unmerged change" comment blocks

Why dotnet format also doesn't fit pre-commit / format-on-save

Separate from the corruption bug, dotnet format fundamentally cannot act as a Prettier-style per-file formatter:

  • dotnet/sdk#43056 — feature request for dotnet format <file>; today it demands a project/solution even with --include, so "format the staged files" is not expressible (folder mode being the partial workaround)
  • dotnet/sdk#49910 — ~5s to format a fresh 2-file dotnet new console (slower than building it); MSBuild workspace loading dominates, making per-commit runs painful — the discussion points to CSharpier as the practical alternative

dotnet format whitespace . --folder avoids MSBuild entirely (formats files as text+syntax), so it is immune — and ~50x faster (seconds vs minutes). The trade-off: folder mode only runs the whitespace formatter.

What each rule can and cannot do here

Rule Build warning Auto-fixed by our format command Notes
IDE0055 (formatting) yes yes (whitespace --folder)
IDE2000 (multiple blank lines) yes no style-verb rule; only dotnet format style (unusable, see above) or CSharpier fixes it
IDE0005 (unused usings) yes¹ no analyzers-verb rule; same limitation
using placement/sorting yes no style-verb rule
var, namespaces, etc. suggestion only no deliberately non-gating

¹ IDE0005 only surfaces at build when the project produces an XML docs file.

Editor gotchas (learned the hard way)

  • .editorconfig keys are last-one-wins: a duplicated dotnet_diagnostic.IDE0055.severity line silently overrides the earlier one.
  • suggestion severity renders as a near-invisible hint in VS Code and does not appear in the Problems panel — use warning for anything that matters.
  • VS Code only analyzes files whose project is loaded. TestBed / IntegrationTests / Stories are only in IgniteUI.Blazor.Lite.slnx, not in IgniteUI.Blazor.Lite.Library.slnf — pick the solution accordingly (C# Dev Kit: "Open Solution" / status-bar picker). Same applies to builds: dotnet build <slnf> never compiles TestBed, so no warnings from its files.

CSharpier evaluation (v1.3.0)

Opinionated Prettier-style formatter for C# (belav/csharpier), evaluated as a local dotnet tool. Test results on this repo:

  • Speed: checked 384 files in ~1s (vs ~60s for dotnet format project mode). Works per-file with no MSBuild → does fit lint-staged, unlike dotnet format.
  • Correctness: no multi-TFM issue (pure syntax-tree formatting). On the IDE2000/IDE0055 test file it fixed indentation and collapsed blank lines.
  • Scope: since v1 it also formats XML (.csproj, .props, .targets) — a full run would touch ~379 files including project files.
  • Opinionated: rewraps long lines (default printWidth 100), normalizes expression layout. Little to configure (width, tabs, endOfLine) — that is the point, but the diff goes beyond pure whitespace. Reads .editorconfig for indent_size/max_line_length.
  • Opinions you cannot turn off (all verified, all by-design with no config option): constructor initializers always break to a new line even when short (public A() : base() { }public A()\n : base() { }); brace collapsing/expansion normalized to its own rules (e.g. empty/short bodies hugged onto the member line); long-line rewrapping and expression layout normalization throughout.
  • Ecosystem: VS Code / VS / Rider extensions (format-on-save), CSharpier.MsBuild package (fail build if unformatted), .csharpierignore.

Verdict: evaluated and rejected. Technically it is the strongest option — fast, per-file, immune to the multi-TFM bug, and the only tool that auto-fixes blank lines (IDE2000) — but its fixed style opinions (constructor initializer placement, brace collapsing, aggressive rewrapping) diverge from the preferred style of this codebase, and none of them are configurable. Consequence: IDE2000 and other style-verb rules stay report-only — build warnings with manual or IDE-code-fix cleanup, no bulk auto-fix. Should the preference shift (or CSharpier grow the relevant options), adoption would be: one-time dotnet csharpier format ., swap the CI whitespace gate for dotnet csharpier check ., add "*.cs": "dotnet csharpier format" to lint-staged, and keep .editorconfig + EnforceCodeStyleInBuild for the non-formatting style rules.

Other deliberate choices / open items

  • Generated TS (src/src/ig/) is fully excluded: from Prettier via .prettierignore and from editor churn via the [src/src/ig/**] unset-section at the end of .editorconfig (kept last so it overrides the [*] defaults).
  • Line endings / charset are not enforced (no .gitattributes); adding one plus end_of_line in .editorconfig is a possible follow-up.
  • Markdown is excluded from Prettier for now (*.md in .prettierignore).
  • ESLint is not wired into lint-staged — there is no eslint flat config at the repo root (only the legacy tslint-loader webpack setup); lint-staged would fail without one. Add eslint.config.js first if lint gating is wanted.

@damyanpetev
damyanpetev force-pushed the dpetev/setup-update branch 2 times, most recently from 61cc657 to fdbe764 Compare July 21, 2026 14:21
Base automatically changed from dpetev/setup-update to master July 21, 2026 14:42
@damyanpetev
damyanpetev force-pushed the dpetev/formatting-setup branch from 17c8218 to 138097f Compare July 21, 2026 17:23
@damyanpetev
damyanpetev requested review from dkamburov and kdinev July 21, 2026 17:24
@damyanpetev
damyanpetev marked this pull request as ready for review July 21, 2026 17:32
@damyanpetev
damyanpetev enabled auto-merge July 21, 2026 17:32
Comment on lines +7 to +9
Top

}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Top
}
}
Top
}

@damyanpetev
damyanpetev merged commit 4ca7a50 into master Jul 22, 2026
6 checks passed
@damyanpetev
damyanpetev deleted the dpetev/formatting-setup branch July 22, 2026 12:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants