Skip to content

Latest commit

 

History

History
561 lines (424 loc) · 15.8 KB

File metadata and controls

561 lines (424 loc) · 15.8 KB

Contributing to LiveTemplate Core Library

Thank you for your interest in contributing to the LiveTemplate core library! This guide covers contributions to the Go server-side library only.

Contributing to Other Components

LiveTemplate is distributed across multiple repositories. Please use the appropriate contribution guide:


This guide will help you get started with core library contributions.

Table of Contents

Prerequisites

Before you begin, ensure you have the following installed:

  • Go 1.26+ - Required for building and testing the core library
  • golangci-lint - Required for linting (pre-commit hook)
    # macOS
    brew install golangci-lint
    
    # Linux/WSL
    curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin
  • Chrome/Chromium - Required for E2E browser tests (chromedp)

Note: For client library development (TypeScript), see the client repository.

Setup

  1. Fork and clone the repository

    git clone https://github.com/yourusername/livetemplate.git
    cd livetemplate
  2. Install dependencies

    # Go dependencies (automatically handled by Go modules)
    go mod download
  3. Install pre-commit hook (automatically validates before each commit)

    cp scripts/pre-commit.sh .git/hooks/pre-commit
    chmod +x .git/hooks/pre-commit
  4. Verify setup

    # Run all tests
    go test -v ./... -timeout=30s
    
    # Run linter
    golangci-lint run

Development Workflow

Making Changes

  1. Create a feature branch

    git checkout -b feature/your-feature-name
    # or
    git checkout -b fix/your-bug-fix
  2. Make your changes

    • Follow existing code patterns and conventions
    • Add tests for new functionality
    • Update documentation if needed
  3. Run tests frequently

    # Quick feedback loop
    go test -v ./...
    
    # Or test specific packages
    go test -v -run TestYourSpecificTest
  4. Commit your changes

    git add .
    git commit -m "your commit message"
    # Pre-commit hook will automatically run validation

Testing Core Changes with LVT/Examples

When making changes to the core library, you may want to test how they affect LVT or examples before releasing.

Recommended: Go Workspace (Automatic)

The easiest way is to use Go workspaces (Go 1.18+). This automatically uses local checkouts without modifying any go.mod files.

Directory structure:

parent/
├── livetemplate/  (core library - this repo)
├── lvt/           (CLI tool)
├── examples/      (example apps)
├── client/        (TypeScript client - optional)
└── setup-workspace.sh  (run this once)

One-time setup:

# Clone sibling repositories
cd ..  # Go to parent directory
git clone https://github.com/livetemplate/lvt.git
git clone https://github.com/livetemplate/examples.git

# Create workspace (run once)
./setup-workspace.sh

That's it! Now all go commands automatically use your local versions:

# Test LVT with your core changes
cd lvt
go test ./...  # Automatically uses ../livetemplate

# Test examples
cd ../examples
./test-all.sh  # Automatically uses ../livetemplate and ../lvt

# Build an example
cd counter
go build  # Uses local livetemplate

To remove workspace:

cd /path/to/parent
./setup-workspace.sh --clean

How it works:

  • Creates a go.work file in the parent directory
  • Go automatically finds it and uses listed modules
  • No go.mod changes needed
  • go.work is gitignored (never committed)

Alternative: Manual Replace Directives

If you prefer manual control or can't use workspaces, use the helper scripts in each repo:

cd ../lvt
./scripts/setup-local-dev.sh

cd ../examples
./scripts/setup-local-dev.sh

Revert with --undo flag when done.

Automated CI Checks

The core library has automated CI checks that test LVT and examples against your PR. These checks will catch breaking changes before merge.

Directory Structure (Core Library)

livetemplate/
├── template.go          # Main API — Template type and orchestrator
├── mount.go             # Controller+State pattern, HTTP/WebSocket handlers
├── context.go           # Unified Context type for action handlers
├── state.go             # State interface and AsState wrapper
├── action.go            # Action data binding (ActionData, FieldError, MultiError)
├── dispatch.go          # Reflection-based action method dispatch
├── lifecycle.go         # Controller lifecycle method detection
├── config.go            # Template and handler configuration
├── auth.go              # Authenticator interface and implementations
├── session_stores.go    # MemorySessionStore and RedisSessionStore
├── health.go            # Kubernetes health check endpoints
├── formvalidation.go    # Form schema extraction and validation
├── ws.go                # WebSocket interface abstraction
├── ws_gorilla.go        # Gorilla WebSocket implementation
├── upload.go            # File upload public API
├── testing.go           # AssertPureState test helper
├── internal/            # Internal packages (5-phase architecture)
│   ├── parse/           # Phase 1: Template parsing (AST evaluation)
│   ├── build/           # Phase 2: Tree types, fingerprinting, wrapper injection
│   ├── diff/            # Phase 3: Tree comparison and update generation
│   ├── render/          # Phase 4: HTML rendering and minification
│   ├── send/            # Phase 5: Message parsing and serialization
│   ├── session/         # WebSocket connection registry
│   ├── observe/         # Metrics and Prometheus export
│   ├── keys/            # Range item key generation
│   ├── upload/          # Upload infrastructure
│   └── fuzz/            # Fuzz testing framework
├── pubsub/              # Redis pub/sub for distributed broadcasting
├── testdata/            # Test fixtures, golden files, fuzz corpus
├── docs/                # Documentation
└── scripts/             # Development scripts

For the complete file-by-file map with line counts and dependencies, see docs/design/CODE_STRUCTURE.md.

Note: The client library, CLI tool, and examples are now in separate repositories:

Pre-commit Hook

The pre-commit hook is CRITICAL for maintaining code quality. It automatically:

  1. Auto-formats Go code using go fmt
  2. Runs golangci-lint to catch common issues
  3. Runs all Go tests with timeout

Important Rules

  1. NEVER skip the pre-commit hook using --no-verify

    • The hook is there to catch issues early
    • Skipping it will break CI and block your PR
  2. Fix failures before committing

    • Linting errors: Fix the code issues
    • Test failures: Ensure all tests pass
    • If stuck, ask for help (see Getting Help)
  3. Formatted files are auto-added

    • The hook runs go fmt and stages formatted files automatically
    • No need to manually format before committing

Example Hook Output

🔄 Running pre-commit validation...
📝 Auto-formatting Go code...
✅ Code formatting completed
🔍 Running golangci-lint...
✅ Linting passed
🧪 Running Go tests...
✅ All Go tests passed
✅ Pre-commit validation completed successfully

Testing

Test Categories

  1. Unit Tests - Fast tests for individual functions

    go test -v ./... -short
  2. E2E Tests - End-to-end tests with template rendering

    go test -run TestTemplate_E2E -v
  3. Browser Tests - Chromedp tests for real browser interactions

    go test -run TestE2E -v
  4. Fuzz Tests - Randomized input testing

    go test -fuzz=FuzzTree -fuzztime=30s

Note: For client library tests, see the client repository.

Golden Files

Many E2E tests use golden files in testdata/e2e/:

  • *.html - Expected rendered HTML output
  • *.json - Expected tree updates

To update golden files after intentional changes:

UPDATE_GOLDEN=1 go test -run TestTemplate_E2E -v

Writing Tests

Follow these patterns:

Unit test example:

func TestNewFeature(t *testing.T) {
    t.Run("description of test case", func(t *testing.T) {
        // Arrange
        input := "test input"

        // Act
        result := YourFunction(input)

        // Assert
        if result != expected {
            t.Errorf("expected %v, got %v", expected, result)
        }
    })
}

E2E browser test example:

func TestFeature(t *testing.T) {
    // Setup server and browser context
    ctx, cancel := chromedp.NewContext(context.Background())
    defer cancel()

    // Run test actions
    err := chromedp.Run(ctx,
        chromedp.Navigate("http://localhost:8080"),
        chromedp.Click("button#submit"),
        chromedp.WaitVisible("#result"),
    )

    if err != nil {
        t.Fatal(err)
    }
}

Code Style

General Principles

  1. No unnecessary comments - Code should be self-documenting
  2. Follow existing patterns - Check neighboring code for conventions
  3. Use existing utilities - Don't reinvent the wheel
  4. Maintain idiomatic Go - Follow Go best practices

Naming Conventions

  • Public API (exported): PascalCase
    • Template, Context, State, Session, AsState
  • Internal implementation (unexported): camelCase
    • treeNode, keyGenerator, parseAction
  • Test functions: TestFeatureName
  • Benchmark functions: BenchmarkFeatureName

Public API Guidelines

The public API surface is minimal by design. Only export:

  • Types that users directly interact with
  • Functions that users must call
  • Interfaces that users implement

Keep implementation details private.

Documentation

  • Add godoc comments for all public types and functions
  • Document non-obvious behavior and edge cases
  • Include examples in godoc when helpful
// Template represents a parsed template that can generate updates.
// It maintains state between renders to produce minimal diffs.
type Template struct {
    // ...
}

// ExecuteToUpdate renders the template and returns a JSON update.
// This is more efficient than ExecuteToHTML for subsequent renders.
func (t *Template) ExecuteToUpdate(data interface{}) (*UpdateResponse, error) {
    // ...
}

Commit Messages

Use conventional commit format:

<type>(<scope>): <subject>

<body>

<footer>

Types

  • feat: New feature
  • fix: Bug fix
  • refactor: Code restructuring without behavior change
  • test: Adding or updating tests
  • docs: Documentation changes
  • perf: Performance improvements
  • chore: Build process or tooling changes

Examples

feat(template): add support for nested template invokes

Implements recursive template invocation to support complex
component hierarchies. Updates tree parser to handle nested
{{template}} calls correctly.

Closes #123
fix(client): prevent duplicate WebSocket connections

Adds connection state tracking to prevent race condition where
multiple connections could be established during reconnection.

Fixes #456
refactor: minimize public API surface

BREAKING CHANGE: Internal types like TreeNode and KeyGenerator
are now private. Users should only interact with Template,
Store, and ActionContext interfaces.

Pull Requests

Before Submitting

  1. Ensure all tests pass locally
  2. Update documentation if needed
  3. Add tests for new features
  4. Rebase on latest main branch
  5. Run the pre-commit hook manually if needed:
    .git/hooks/pre-commit

PR Description Template

## Description
Brief description of changes

## Motivation
Why is this change needed?

## Changes
- List of specific changes
- One per line

## Testing
How was this tested?

## Checklist
- [ ] Tests added/updated
- [ ] Documentation updated
- [ ] Pre-commit hook passes
- [ ] No breaking changes (or documented if necessary)

Review Process

  1. PRs require at least one approval
  2. CI must pass (tests, linting, formatting)
  3. Address reviewer feedback
  4. Maintainer will merge when ready

Where to Start

Good First Issues

Look for issues labeled good first issue - these are:

  • Well-defined and scoped
  • Don't require deep system knowledge
  • Good for getting familiar with the codebase

Areas to Explore

  1. Core template engine (template.go, tree.go, internal/)

    • Template parsing improvements
    • Tree diffing optimizations
    • New Go template constructs
  2. Documentation (docs/)

    • Improve existing docs
    • Add examples
    • Fix typos or unclear sections
  3. Testing (various *_test.go files)

    • Add test coverage
    • Improve E2E tests
    • Add edge case tests
  4. HTTP/WebSocket handling (mount.go, session.go, context.go)

    • Controller+State pattern implementation
    • Session management improvements
    • Performance optimizations

For other components:

Learning the Codebase

  1. Start with the Contributor Walkthrough

  2. Read the architecture docs

    • CLAUDE.md - Development guidelines
    • docs/design/ARCHITECTURE.md - System architecture and design decisions
  3. Run the examples

    # Clone the examples repository
    git clone https://github.com/livetemplate/examples.git
    cd examples/counter
    go run main.go
    # Open http://localhost:8080
  4. Read the tests

    • Tests are excellent documentation
    • Start with e2e_test.go for high-level flow
    • Check template_test.go for core functionality
  5. Experiment

    • Make small changes
    • Run tests to see what breaks
    • Use debugger to step through code

Getting Help

  • Questions: Open a discussion on GitHub
  • Bugs: Open an issue with reproduction steps
  • Features: Open an issue to discuss before implementing
  • Real-time help: Check if there's a Discord/Slack (if available)

License

By contributing, you agree that your contributions will be licensed under the same license as the project (check LICENSE file).


Thank you for contributing to LiveTemplate!