npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@scalar/csharp-fmt

v0.2.0

Published

C# formatter (CSharpier on wasm) - runs on plain Node, no .NET

Readme

Scalar C# Formatter

Version Downloads License Discord

C# formatter that runs on plain Node. No .NET SDK, no dotnet on PATH, no postinstall download.


Scalar is an open-source API platform for teams who want beautiful developer interfaces without vendor lock-in.

  • API References — Interactive API documentation from OpenAPI and AsyncAPI specs.
  • Developer Docs — Write in Markdown/MDX, generate API references, sync with two-way Git.
  • SDK Generator — Type-safe SDKs and CLIs in TypeScript, Python, Go, PHP, Java, and Ruby.
  • API Client — Open-source, offline-first Postman alternative built on OpenAPI.

20M+ monthly npm installs · 15,500+ GitHub stars · MIT licensed · scalar.com


npm i @scalar/csharp-fmt
import { format } from '@scalar/csharp-fmt'

await format('using B;using A;class A{int x  =  1;void F(){G( "hi" );}}')
// using A;
// using B;
//
// class A
// {
//     int x = 1;
//
//     void F()
//     {
//         G("hi");
//     }
// }

Async because the first call decompresses the archive and boots the runtime — about 0.8s. That work is cached, so every later call is ~5ms.

Options: { printWidth } (100), { useTabs } (false), { indentSize } (4) and { endOfLine } ('auto', or 'lf'/'crlf'). Every default is CSharpier's own, so format(source) means what csharpier format <file> means for a file with no .csharpierrc beside it.

Source that does not parse throws, carrying the diagnostics CSharpier produced: (1,9): error CS1513: } expected.

Node 22 or newer.

Formatting without awaiting

formatSync is for callers with no await to give — a code generator that formats each file inside the synchronous builder that emits it, a template renderer, a plugin hook that has to return a string.

import { formatSync, init } from '@scalar/csharp-fmt'

await init()
const formatted = formatSync(source)

Booting is the one thing that cannot be made synchronous, so init covers it once and formatSync throws until it has. Everything after that already was synchronous — format was only ever awaiting the boot, and both produce the same bytes.

Prefer format where you can await: it needs no setup call and cannot throw that error.

It runs in the browser too

The import does not change — bundlers and browsers pick the browser export condition on their own, and format has the same signature and returns the same bytes. Only the assets' route in differs: fetched rather than read from disk.

import { format, init } from '@scalar/csharp-fmt'

// Optional. Both the archive and the runtime's four JavaScript files resolve
// next to the module by default, which Vite, Rollup, webpack and a plain CDN
// handle unaided. esbuild does not rewrite `new URL(..., import.meta.url)`, so
// there they need naming.
await init({ url: '/assets/csharp_fmt.br', runtimeBaseUrl: '/assets/dotnet' })

await format(source)

Run it in a worker. Booting is ~1s of decompression, module loading and Roslyn warm-up, which is a visibly frozen tab if it happens on the main thread.

This is the one package whose assets are not all bytes. The assemblies, the runtime wasm and the ICU data all come out of the archive, but dotnet.js, dotnet.boot.js, dotnet.runtime.js and dotnet.native.js are ES modules the .NET runtime imports by URL, so they have to exist at one. That is what runtimeBaseUrl is for; the default needs no configuration, and a real Vite production build emits all four under hashed names and still boots.

The browser reads the same brotli archive as Node (4.2 MB over the wire, 21 MB expanded) and expands it with DecompressionStream('brotli') where the engine has it, or a 208 KB wasm decoder where it does not — Chrome, today. Serving the archive with Content-Encoding: br, or serving it uncompressed, skips the decoder entirely:

await init({ url: '/assets/csharp_fmt', encoding: 'none' })

This is the real CSharpier, and the output is exact

This is actual CSharpier 1.3.0 — Roslyn's own C# parser included — compiled to WebAssembly by the .NET 10 browser-wasm toolchain. It is not a reimplementation, so it does not drift.

test/native-conformance.test.ts asserts byte-identical output against the same version running on .NET. That test asserts rather than reports: any divergence is a real bug. It skips cleanly when no native csharpier is around, so a toolchain-free checkout still passes.

Beyond the samples in that test, the build was checked against 613 real C# files — CSharpier's own syntax-coverage test corpus plus its source — formatted by both the wasm build and the native CLI. All 613 came out identical.

format() is a string in and a string out

For a .cs file the CLI dispatches straight to the same CSharpFormatter.Format that runs inside the module here, so unlike this repo's Java package there is no extra CLI pipeline to replicate. What the CLI adds around it is a caller's job, because none of it exists inside a module with no filesystem:

  • finding files, and honouring .csharpierignore
  • resolving .csharpierrc and .editorconfig into options — pass them yourself
  • detecting a file's encoding

The one place that boundary is papered over is the byte-order mark. The library drops a leading mark and the CLI re-attaches it from the encoding it detected, so format() re-attaches it too — otherwise formatting a BOM-prefixed file would silently strip it.

XML is not included

CSharpier also formats .csproj and other XML through a separate XmlFormatter. This package exposes only the C# formatter. That is a scope choice, not a limitation of the build.

One artifact, and how to get it

build/csharp_fmt/build.sh produces two things, both committed:

  • csharp_fmt.br — the assemblies, the runtime wasm and the ICU data, packed into one brotli archive. 21MB raw, 4.2MB compressed.
  • runtime/ — the four JavaScript files the .NET runtime imports as ES modules. They resolve by URL, so they have to stay real files.

Everything in the archive is fed to the runtime through dotnet.withResourceLoader, which is why 21MB of assets can ship as one compressed file instead of twenty loose ones.

The build needs the .NET 10 SDK and its wasm-tools workload, which the script downloads itself. Neither the tests nor consumers ever need them.

build/csharp_fmt/NOTES.md records what was measured and why the build is configured the way it is — including two settings that silently change output, which is worth reading before touching a flag.

Community

We are API nerds. You too? Let's chat on Discord: https://discord.gg/scalar

Licensing

Everything embedded here is permissively licensed: CSharpier, Roslyn, the .NET runtime and class library, and Emscripten are MIT, and the ICU data is Unicode-3.0. Unlike this repo's Java package there is no restriction on who may redistribute a copy or what they may charge. See licenses/NOTICE.md.