Zap Studio

Code Quality

Linting, formatting, testing, and monorepo task orchestration in Local.ts.

Local.ts comes with a complete code quality setup using modern, fast tooling. This guide covers linting, formatting, testing, and the monorepo task runner.

Ultracite

Ultracite is a zero-configuration linter and formatter preset designed to enforce high code quality standards.

It can unify linting and formatting using different engines, such as Biome, Prettier with ESLint, or the oxlint and oxfmt tools.

In this project, we use the oxlint + oxfmt combo for fast, modern linting and formatting.

Why Ultracite?

  • Fast — Rust-based engine runs in milliseconds
  • Zero-config — Sensible defaults out of the box
  • Unified — Linting and formatting in one tool
  • Auto-fix — Most issues are automatically fixable

Commands

# Format and fix all issues
pnpm dlx ultracite fix

# Check for issues without fixing
pnpm dlx ultracite check

# Diagnose setup issues
pnpm dlx ultracite doctor

Or using Turborepo:

turbo format   # Fix issues
turbo lint     # Check issues

Configuration

Ultracite works with oxlint under the hood. The configuration is in .oxlintrc.json:

{
  "$schema": "./node_modules/oxlint/configuration_schema.json",
  "extends": ["ultracite/oxlint/core", "ultracite/oxlint/react"]
}

The presets include rules for:

  • TypeScript best practices
  • React hooks and accessibility
  • Modern JavaScript patterns
  • Security and performance

Pre-commit Hooks

Local.ts uses Lefthook to run Ultracite on staged files before each commit:

# lefthook.yml
pre-commit:
  jobs:
    - run: pnpm dlx ultracite fix
      glob:
        - "*.js"
        - "*.jsx"
        - "*.ts"
        - "*.tsx"
        - "*.json"
        - "*.jsonc"
        - "*.css"
      stage_fixed: true

This ensures all committed code passes linting and formatting checks.

Testing with Vitest

Local.ts uses Vitest for testing. Vitest is a fast, Vite-native test runner with a Jest-compatible API.

Running Tests

# Run tests once
turbo test

# Watch mode for development
turbo test:watch

# Generate coverage report
turbo test:coverage

# Open the UI test runner
turbo test:ui

Configuration

Testing is configured in vite.config.ts:

export default defineConfig({
  // ... other config
  test: {
    coverage: {
      provider: "v8",
      reporter: ["text", "html"],
    },
  },
});

Writing Tests

Create test files with the .test.ts or .test.tsx extension:

import { describe, it, expect } from "vitest";

describe("myFunction", () => {
  it("should return the correct value", () => {
    expect(myFunction(2)).toBe(4);
  });
});

Coverage Reports

Coverage reports are generated in the coverage/ directory:

  • coverage/index.html — Interactive HTML report
  • Terminal output shows summary

Turborepo

Turborepo orchestrates tasks across the monorepo, handling both the frontend (Vite) and backend (Tauri/Rust) with unified commands and intelligent caching.

Monorepo Structure

Local.ts is structured as a pnpm workspace with two packages:

# pnpm-workspace.yaml
packages:
  - .           # Frontend (React/Vite)
  - ./src-tauri # Backend (Rust/Tauri)

This allows Turborepo to run tasks across both packages with a single command.

Task Configuration

Tasks are defined in turbo.json:

{
  "$schema": "https://turborepo.com/schema.json",
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [
        "dist/**",
        "src-tauri/target/release/**",
        "src-tauri/target/debug/**"
      ]
    },
    "check": {
      "dependsOn": ["^check"]
    },
    "dev": {
      "persistent": true,
      "cache": false
    },
    "lint": {
      "dependsOn": ["^lint"]
    },
    "test": {
      "dependsOn": ["^build"]
    },
    "validate": {
      "dependsOn": ["build", "check", "lint", "test"]
    }
  }
}

Key Concepts

ConceptDescription
dependsOnRun dependencies first (e.g., ^build runs build in all deps)
outputsFiles to cache for faster subsequent runs
persistentKeep running (for dev servers)
cacheWhether to cache task outputs

Caching

Turborepo caches task outputs based on file inputs. When you run a task:

  1. Turborepo hashes relevant source files
  2. If the hash matches a previous run, it replays cached output
  3. If not, it runs the task and caches the result

This dramatically speeds up CI pipelines and repeated local builds.

Available Commands

CommandDescription
turbo devStart Vite dev server
turbo tauri -- devStart full Tauri app with hot reload
turbo tauri -- buildBuild production app
turbo buildBuild frontend
turbo checkTypeScript type checking
turbo lintRun Ultracite linter
turbo formatFormat code with Ultracite
turbo testRun Vitest tests
turbo test:coverageGenerate coverage report
turbo validateRun all checks (build, check, lint, test)

The Validate Command

The validate task runs all quality checks in the correct order:

{
  "validate": {
    "dependsOn": ["build", "check", "lint", "test"]
  }
}

Use it before committing or in CI:

turbo validate

This ensures your code:

  • Builds successfully
  • Passes TypeScript checks
  • Passes linting rules
  • Passes all tests

Cargo

Local.ts includes a Rust backend in src-tauri/ with comprehensive tooling for code quality.

The Rust toolchain provides native tools for formatting, linting, type checking, testing, and coverage.

Available Cargo Commands

CommandDescription
cargo fmtFormat Rust code with rustfmt
cargo clippyLint Rust code with Clippy
cargo checkType check without building
cargo testRun Rust unit and integration tests
cargo tarpaulinGenerate code coverage reports

Turborepo Integration

The src-tauri/ directory includes a package.json that bridges Cargo commands to the Turborepo workflow. This allows unified commands across both TypeScript and Rust codebases.

Here is a simplified example:

{
  "scripts": {
    "format": "cargo fmt",
    "lint": "cargo clippy",
    "check": "cargo check",
    "test": "cargo test"
  }
}

Now you can run Cargo commands through Turborepo alongside your TypeScript tasks:

# Format both TypeScript and Rust code
turbo format

# Lint both codebases
turbo lint

# Type check TypeScript and Rust
turbo check

# Run tests for both frontend and backend
turbo test

Cargo-specific Workflows

For Rust-specific tasks, navigate to src-tauri/ and use Cargo directly:

cd src-tauri

# Watch tests during development
cargo watch -x test

# Run tests with output
cargo test -- --nocapture

# Generate coverage report
cargo tarpaulin --out Html

# Fix clippy warnings automatically
cargo clippy --fix

Tarpaulin Coverage

Cargo Tarpaulin generates code coverage for Rust tests:

# Install tarpaulin
cargo install cargo-tarpaulin

# Generate HTML coverage report
cargo tarpaulin --out Html

# View in browser
open tarpaulin-report.html

Coverage reports help identify untested code paths in your Rust backend.

CI Integration

Local.ts includes GitHub Actions workflows that run on every pull request to ensure code quality across the monorepo.

Existing Workflows

WorkflowFileDescription
Checkcheck.ymlType checks TypeScript and Rust code
Lintlint.ymlRuns oxlint linter on the monorepo
Testtest.ymlExecutes all test suites
Buildbuild.ymlBuilds the entire monorepo

Workflow Triggers

All workflows:

  • Run on pull requests
  • Execute in parallel for faster feedback
  • Use pnpm for dependency management

Leveraging Turborepo in CI

Turborepo caches build artifacts between runs, significantly reducing CI times after the first build:

# Your CI pipeline benefits from Turborepo's cache
- name: Install dependencies
  run: pnpm install --frozen-lockfile

- name: Validate
  run: pnpm turbo run validate

Remote Caching

For team environments, enable Turborepo Remote Caching to share cache across machines:

turbo login
turbo link

This allows team members and CI runners to benefit from each other's cached builds, dramatically speeding up workflows.

Edit on GitHub

Last updated on

On this page