Skip to content

Latest commit

 

History

History
147 lines (113 loc) · 4.31 KB

File metadata and controls

147 lines (113 loc) · 4.31 KB

AGENTS.md

Project Scope

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)

Zig Development

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.Window

Common Zig Patterns

These 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,
    }),
});

Zig Code Style

Naming:

  • camelCase for functions and methods
  • snake_case for variables and parameters
  • PascalCase for types, structs, and enums
  • SCREAMING_SNAKE_CASE for constants

Struct initialization: Prefer explicit type annotation with anonymous literals:

const foo: Type = .{ .field = value };  // Good
const foo = Type{ .field = value };     // Avoid

File structure:

  1. //! doc comment describing the module
  2. const Self = @This(); (for self-referential types)
  3. Imports: stdbuiltin → project modules
  4. const log = std.log.scoped(.module_name);

Functions: Order methods as initdeinit → 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.

Regression Testing with Fixtures

When encountering a bug or false positive/negative compared to svelte-check:

  1. Create a minimal reproduction in test-fixtures/ with a .svelte file that isolates the issue
  2. Run both tools to confirm the discrepancy:
    ./zig-out/bin/zvelte-check --workspace test-fixtures
    cd test-fixtures && pnpm exec svelte-check
  3. Fix the bug in the transformer or parser
  4. Verify parity - both tools should now report the same errors (or lack thereof)

This ensures we catch regressions and maintain compatibility with svelte-check.

Safety Conventions

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