nufmt is a formatter for Nushell scripts, built entirely on Nushell's own parsing infrastructure (nu-parser, nu-protocol). It provides:
- AST-based formatting: Uses Nushell's actual parser for accurate code understanding
- Idempotent output: Running the formatter twice produces the same result
- Comment preservation: Comments are preserved in their original positions
- Configurable: Supports configuration via NUON files
- Fast: Parallel file processing with Rayon
cargo install --git https://github.com/nushell/nufmtcargo install nufmtnix run github:nushell/nufmtnufmt [OPTIONS] [FILES]...
Format one or more Nushell files:
# Format a single file
nufmt script.nu
# Format multiple files
nufmt file1.nu file2.nu file3.nu
# Format all .nu files in a directory
nufmt src/| Option | Short | Description |
|---|---|---|
--dry-run |
Check files without modifying them. Returns exit code 1 if files would be reformatted. | |
--stdin |
Read from stdin and write formatted output to stdout. Cannot be combined with file arguments. | |
--config |
-c |
Path to a configuration file (NUON format). |
--help |
-h |
Show help and exit. |
--version |
-v |
Print version and exit. |
# Format files in place
nufmt *.nu
# Check if files need formatting (CI mode)
nufmt --dry-run src/
# Format stdin
echo 'let x=1' | nufmt --stdin
# Use custom config
nufmt --config nufmt.nuon src/Create a nufmt.nuon file in your project root:
{
indent: 4
line_length: 80
margin: 1
exclude: ["vendor/**", "target/**"]
}
Configuration options:
| Option | Type | Default | Description |
|---|---|---|---|
indent |
int | 4 | Number of spaces per indentation level |
line_length |
int | 80 | Maximum line length (advisory) |
margin |
int | 1 | Number of blank lines between top-level items |
exclude |
list<string> | [] | Glob patterns for files to exclude |
| Code | Description |
|---|---|
| 0 | Success (files formatted or already formatted) |
| 1 | Dry-run mode: at least one file would be reformatted |
| 2 | Error: invalid configuration, CLI options, or parse error |
nufmt properly formats the following Nushell constructs:
- ✅ Variable declarations (
let,mut,const) - ✅ Function definitions (
def,def-env,export def) - ✅ Control flow (
if/else,match,for,while,loop) - ✅ Pipelines with proper spacing around
| - ✅ Lists and records
- ✅ Closures with parameters (
{|x| ... }) - ✅ String interpolation (
$"Hello ($name)") - ✅ Modules (
module,use,export) - ✅ Error handling (
try/catch) - ✅ Comments (preserved in output)
- ✅ Ranges (
1..10,1..2..10) - ✅ Binary operations with proper spacing
Unlike tree-sitter based formatters, nufmt uses Nushell's own nu-parser crate to parse scripts into an AST. This ensures:
- Accuracy: The same parser that runs your scripts formats them
- Compatibility: Always in sync with Nushell's syntax
- Error detection: Invalid syntax is detected before formatting
The formatter walks the AST and emits properly formatted code with consistent:
- Indentation (configurable)
- Spacing around operators and keywords
- Brace placement for blocks
- Comment placement
nufmt has a comprehensive ground truth testing system that verifies the formatter produces correct output for all supported Nushell constructs.
The testing system uses two sets of fixture files:
- Input files (
tests/fixtures/input/): Contain valid Nushell code with intentional formatting issues (extra spaces, inconsistent indentation, etc.) - Expected files (
tests/fixtures/expected/): Contain the correctly formatted version of each input file
When tests run, the formatter processes each input file and compares the output against the corresponding expected file. This ensures:
- The formatter produces consistent, correct output
- Formatting changes are intentional and reviewed
- Regressions are caught immediately
Important: Input files should always differ from expected files. Input files represent "what not to do" - poorly formatted but valid Nushell code that the formatter should fix.
Tests are organized by Nushell construct category:
| Category | Constructs |
|---|---|
core |
let_statement, mut_statement, const_statement, def_statement |
control_flow |
if_else, for_loop, while_loop, loop_statement, match_expr, try_catch, break_continue, return_statement |
data_structures |
list, record, table, nested_structures |
pipelines_expressions |
pipeline, multiline_pipeline, closure, subexpression, binary_ops, range, cell_path, spread |
strings_interpolation |
string_interpolation, comment |
types_values |
value_with_unit, datetime, nothing, glob_pattern |
modules_imports |
module, use_statement, export, source, hide, overlay |
commands_definitions |
alias, extern, external_call |
special_constructs |
do_block, where_clause, error_make |
# Run all tests (unit + ground truth + idempotency)
cargo test
# Run only ground truth tests
cargo test --test ground_truth
# Run with verbose output
cargo test -- --nocaptureA Nushell script (tests/run_ground_truth_tests.nu) provides a more interactive testing experience:
# Build the release binary first
cargo build --release
# Run all tests
nu tests/run_ground_truth_tests.nu
# Run with verbose output (show diffs on failure)
nu tests/run_ground_truth_tests.nu --verbose
# Run only ground truth tests (skip idempotency)
nu tests/run_ground_truth_tests.nu --ground-truth
# Run only idempotency tests
nu tests/run_ground_truth_tests.nu --idempotency
# Run tests for a specific category
nu tests/run_ground_truth_tests.nu --category control_flow
# Run a single test
nu tests/run_ground_truth_tests.nu --test let_statement
# List all available tests
nu tests/run_ground_truth_tests.nu --list
# List available categories
nu tests/run_ground_truth_tests.nu --list-categories
# Check which test files exist
nu tests/run_ground_truth_tests.nu --check-filesTo add a test for a new construct:
- Create an input file at
tests/fixtures/input/<construct_name>.nuwith poorly-formatted but valid Nushell code - Create an expected file at
tests/fixtures/expected/<construct_name>.nuwith the correctly formatted version - Add the construct name to the appropriate category in
tests/run_ground_truth_tests.nu - Run the tests to verify
Example input file (tests/fixtures/input/my_construct.nu):
let x = 1
let y = 2Example expected file (tests/fixtures/expected/my_construct.nu):
let x = 1
let y = 2Contributions are welcome! Please see our contribution guide.
If you encounter formatting issues, please:
- Check if the script is valid Nushell syntax
- Provide a minimal reproduction case
- Include your
nufmtversion and Nushell version
MIT License - see LICENSE for details.