An opinionated import organizer for Zig. Sorts, groups, and hoists @import
declarations, similar to isort for Python or
goimports for Go.
Installation
Note
Requires Zig 0.15.2 or newer (including 0.16) on Linux or macOS. On Windows, use WSL2.
Homebrew
brew tap mstdokumaci/zsort brew install mstdokumaci/zsort/zsort
Prebuilt binaries
Download from GitHub Releases,
unpack, and add to your PATH.
Zig package (build dependency)
Add to build.zig.zon:
.zsort = .{ .url = "https://github.com/mstdokumaci/zsort/archive/refs/tags/v0.6.0.tar.gz", // .hash .lazy = true, },
Wire up build steps in build.zig:
const zsort = b.lazyDependency("zsort", .{ .target = b.graph.host, .optimize = .ReleaseFast, }); if (zsort) |dep| { const zsort_exe = dep.artifact("zsort"); const check_imports = b.addRunArtifact(zsort_exe); check_imports.setCwd(b.path(".")); check_imports.addArgs(&.{ "check", "src", "--ban-prefix", "./", "--ban-prefix", "src/" }); b.step("check-imports", "Run zsort check on this project").dependOn(&check_imports.step); const run_fix = b.addRunArtifact(zsort_exe); run_fix.setCwd(b.path(".")); run_fix.addArgs(&.{ "fix", "src", "--ban-prefix", "./", "--ban-prefix", "src/" }); b.step("fix-imports", "Fix Zig import ordering in this project").dependOn(&run_fix.step); }
See test/consumer/ for a working example.
From source
zig build -Doptimize=ReleaseFast # → zig-out/bin/zsortUsage
Usage: zsort [check|fix] <dir|file>... [options]
Modes:
check Verify import ordering (exit 1 if changes needed)
fix Rewrite files in place
Options:
--ban-prefix <p> Reject imports starting with <p> (repeatable)
-h, --help Show help
--version Print version
zsort check src/ # verify a directory zsort fix . # fix everything zsort check src/ build.zig # mixed targets zsort check . --ban-prefix ./ --ban-prefix src/ # ban relative paths
checkprints unified diffs for files that need changes.// zsort: skipanywhere in a file excludes it from processing.- Directories are scanned recursively.
.gitignoreentries,.git,.zig-cache,zig-cache, andzig-outare skipped automatically.
Sorting rules
zsort groups imports into four bands, separated by blank lines:
- std / builtin — the
stdandbuiltinmodules - Third-party — other module names (
httpz,sqlite, …), including@cImport - Local — paths containing
/or ending in.zig, plus Zig's package-level modulesroot(the package's own root source file) andbuild_root(the build runner's root module) - Aliases —
const X = module.Member;wheremoduleresolves to an import above;const X = @This();sorts first in this band
Within each band, plain imports come before member imports, then both are sorted
by path (byte-wise). If two imports share the same path, the full line of code
breaks the tie (e.g. const Foo = @import("x.zig").Foo; before
const bar = @import("x.zig").bar;). The output is deterministic regardless
of input order.
Before zsort fix:
const std = @import("std"); const Config = auth.Config; const Router = @import("router.zig").Router; const httpz = @import("httpz"); // Handles request authentication. const auth = @import("auth.zig");
After:
const std = @import("std"); const httpz = @import("httpz"); // Handles request authentication. const auth = @import("auth.zig"); const Router = @import("router.zig").Router; const Config = auth.Config;
What fix does
- Sorts and groups top-of-file imports into the bands above
- Hoists stray imports and aliases from deeper in the file into their proper band
- Keeps preceding comments attached to their import
- Ensures a blank line between the import block and the following code
- Preserves
//!doc comments and original line endings (LF / CRLF)
Pre-commit
Add to .pre-commit-config.yaml:
repos: - repo: https://github.com/mstdokumaci/zsort rev: v0.6.0 hooks: - id: zsort # check mode (fail on unsorted) # - id: zsort-fix # fix mode (rewrite in place)
Requires zsort on $PATH (language: system). Pass extra flags via args,
e.g. args: [--ban-prefix, 'src/']. Only .zig files are checked.
See also
- CONTRIBUTING.md — development setup and lint gates
- CHANGELOG.md — release history
License
MIT