This is zvelte-check, a fast Svelte diagnostic tool (svelte-check replacement) written in Zig.
In scope:
- All svelte-check diagnostics (a11y, CSS, TypeScript/JavaScript)
- Svelte 5+ support (runes, snippets, etc.)
- Performance (parallel processing, fast startup)
Out of scope:
- Watch mode (users should use their editor's LSP or run on-demand)
- Svelte 4 and earlier support
- Output format compatibility (we use our own simpler formats)
Always use zigdoc to discover APIs for the Zig standard library and any third-party dependencies.
Examples:
zigdoc std.fs
zigdoc std.posix.getuid
zigdoc ghostty-vt.Terminal
zigdoc vaxis.WindowThese patterns reflect current Zig APIs and may differ from older documentation.
ArrayList:
var list: std.ArrayList(u32) = .empty;
defer list.deinit(allocator);
try list.append(allocator, 42);HashMap/StringHashMap (unmanaged):
var map: std.StringHashMapUnmanaged(u32) = .empty;
defer map.deinit(allocator);
try map.put(allocator, "key", 42);HashMap/StringHashMap (managed):
var map: std.StringHashMap(u32) = std.StringHashMap(u32).init(allocator);
defer map.deinit();
try map.put("key", 42);stdout/stderr Writer:
var buf: [4096]u8 = undefined;
const writer = std.fs.File.stdout().writer(&buf);
defer writer.flush() catch {};
try writer.print("hello {s}\n", .{"world"});File Writer (buffered):
const file = try dir.createFile("output.txt", .{});
defer file.close();
var buf: [4096]u8 = undefined;
var writer = file.writer(&buf);
defer writer.end() catch {};
try writer.interface.writeAll("content");build.zig executable/test:
b.addExecutable(.{
.name = "foo",
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
}),
});Naming:
camelCasefor functions and methodssnake_casefor variables and parametersPascalCasefor types, structs, and enumsSCREAMING_SNAKE_CASEfor constants
Struct initialization: Prefer explicit type annotation with anonymous literals:
const foo: Type = .{ .field = value }; // Good
const foo = Type{ .field = value }; // AvoidFile structure:
//!doc comment describing the moduleconst Self = @This();(for self-referential types)- Imports:
std→builtin→ project modules const log = std.log.scoped(.module_name);
Functions: Order methods as init → deinit → public API → private helpers
Memory: Pass allocators explicitly, use errdefer for cleanup on error
Documentation: Use /// for public API, // for implementation notes. Always explain why, not just what.
Tests: Inline in the same file, register in src/main.zig test block
Bug fixes: Always add a regression test when fixing a bug. The test should reproduce the original failure and verify the fix.
When encountering a bug or false positive/negative compared to svelte-check:
- Create a minimal reproduction in
test-fixtures/with a.sveltefile that isolates the issue - Run both tools to confirm the discrepancy:
./zig-out/bin/zvelte-check --workspace test-fixtures cd test-fixtures && pnpm exec svelte-check
- Fix the bug in the transformer or parser
- Verify parity - both tools should now report the same errors (or lack thereof)
This ensures we catch regressions and maintain compatibility with svelte-check.
Inspired by TigerStyle.
Assertions:
- Add assertions that catch real bugs, not trivially true statements
- Focus on API boundaries and state transitions where invariants matter
- Good: bounds checks, null checks before dereference, state machine transitions
- Avoid: asserting something immediately after setting it, checking internal function arguments
Function size:
- Soft limit of 70 lines per function
- Centralize control flow (switch/if) in parent functions
- Push pure computation to helper functions
Comments:
- Explain why the code exists, not what it does
- Document non-obvious thresholds, timing values, protocol details