Evan Reichard 23730868b8 feat(cli): add cobra-based CLI with subcommands
Replace simple argument parsing with cobra to provide better CLI experience:

- Add 'execute' subcommand for running TypeScript files
- Add 'types' subcommand for printing TypeScript type declarations
- Add help text and error handling
- Update documentation with new usage examples

Add dependencies: github.com/spf13/cobra, github.com/spf13/pflag
2026-01-29 21:32:00 -05:00
2026-01-29 21:31:49 -05:00
2026-01-29 21:31:49 -05:00
2026-01-29 21:31:49 -05:00
2026-01-29 21:31:49 -05:00

Poiesis

A Go tool that transpiles TypeScript to JavaScript using esbuild and executes it with qjs, with an extensible function system.

Project Structure

reichard.io/poiesis/
├── cmd/
│   └── poiesis/                    # CLI application entry point
│       └── main.go
├── internal/
│   ├── runtime/                    # Runtime management, transpilation, execution
│   │   ├── runtime.go              # Core runtime, transpilation, execution
│   │   └── runtime_test.go         # Runtime tests
│   ├── functions/                  # Function registration framework
│   │   ├── registry.go             # Registration system
│   │   ├── types.go                # Core interfaces and types
│   │   ├── typescript.go           # TypeScript definition generation
│   │   ├── collector.go            # Type collection utilities
│   │   └── typescript_test.go      # Type system tests
│   └── stdlib/                     # Standard library implementations
│       ├── fetch.go                # HTTP fetch implementation
│       └── fetch_test.go           # Fetch tests

Architecture

The project is cleanly separated into three packages:

  1. internal/runtime - Runtime management

    • TypeScript transpilation with esbuild
    • JavaScript execution with qjs
    • Automatic function registration and execution
  2. internal/functions - Generic function registration framework

    • Type-safe registration with generics
    • Bidirectional Go ↔ JavaScript type conversion
    • Automatic TypeScript declaration generation
  3. internal/stdlib - Standard library implementations

    • fetch - HTTP requests
    • Extensible for additional standard functions

Installation & Build

go build ./cmd/poiesis

CLI Options

  • [file] - Path to TypeScript file to execute (optional)
  • --print-types - Print TypeScript type declarations for all registered functions
  • --help - Show help information

Testing

go test ./...
golangci-lint run

Usage

poiesis execute [file]      # Run TypeScript file
poiesis types               # Print TypeScript type declarations
poiesis --help              # Show help

Function System

The function system allows you to easily expose Go functions to TypeScript/JavaScript.

Adding a Function

Just write a Go function and register it:

package mystdlib

import (
    "context"
    "reichard.io/poiesis/internal/functions"
)

type AddArgs struct {
    A int `json:"a"`
    B int `json:"b"`
}

func (a AddArgs) Validate() error {
    return nil
}

func Add(_ context.Context, args AddArgs) (int, error) {
    return args.A + args.B, nil
}

func init() {
    functions.RegisterFunction[AddArgs, int]("add", Add)
}

That's it! The framework automatically:

  • Converts JavaScript values to Go types
  • Handles errors (panics as JS errors)
  • Generates TypeScript definitions
  • Manages the qjs integration

Calling Convention

Important: There's an important difference between how functions are defined in Go versus how they're called in JavaScript:

  • Go side: The function receives a single argument struct containing all parameters
  • JavaScript side: The function is called with the struct fields as individual arguments (in the order they appear in the struct)
// Go: Single struct argument
type AddArgs struct {
    A int `json:"a"`
    B int `json:"b"`
}

func Add(_ context.Context, args AddArgs) (int, error) {
    return args.A + args.B, nil
}
// JavaScript: Individual arguments (not an object!)
const result = add(5, 10);  // NOT add({ a: 5, b: 10 })

The framework extracts the JSON tags from the struct fields and uses them to generate the correct TypeScript function signature.

Example

// TypeScript code - call with individual arguments matching struct fields
const response = fetch("https://httpbin.org/get");
console.log("OK:", response.ok);
console.log("Status:", response.status);
console.log("Body:", response.body);

Built-in Functions

  • fetch(options) - HTTP requests
    • options.input (string) - URL to fetch
    • options.init (object) - Optional init object with method, headers, body

Dependencies

  • github.com/evanw/esbuild/pkg/api - TypeScript transpilation
  • github.com/fastschema/qjs - JavaScript execution (QuickJS)
  • github.com/stretchr/testify/assert - Test assertions

Development

  • Test framework: Go's built-in testing package
  • Code style: Follow standard Go conventions
  • Linting: golangci-lint run - must pass before committing
  • TypeScript test files: Tests that require TypeScript files should create them inline using os.CreateTemp() instead of relying on external test files
Description
No description provided
Readme 107 KiB
Languages
Go 98.6%
Nix 1.4%