initial commit

This commit is contained in:
2026-01-27 09:55:09 -05:00
commit c2f08e9140
9 changed files with 353 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
poiesis

43
AGENTS.md Normal file
View File

@@ -0,0 +1,43 @@
# Poiesis
## Overview
Go tool that transpiles TypeScript to JavaScript using esbuild API and executes it with goja.
## Build & Test
```bash
go build
go test
golangci-lint run
```
## Project Structure
- `main.go` - Entry point with `executeTypeScript()` function
- `main_test.go` - Test suite using testify assertions
- `test_data/` - Test TypeScript files
- `go.mod` - Dependencies
## Testing Patterns
- **Test framework**: Go's built-in `testing` package
- **Assertions**: `github.com/stretchr/testify/assert`
- **Linting**: `golangci-lint run` - must pass before committing
- **Test organization**: Test files use `_test.go` suffix, test functions prefixed with `Test`
## Dependencies
- `github.com/evanw/esbuild/pkg/api` - TypeScript transpilation
- `github.com/dop251/goja` - JavaScript execution
- `github.com/stretchr/testify/assert` - Test assertions
## Key Functions
- `executeTypeScript(filePath string, stdout, stderr io.Writer) error` - Main transpilation and execution logic
## Code Conventions
- Handle all return values from external functions (enforced by golangci-lint)
- Use `os` package instead of deprecated `io/ioutil`
- Error logging uses `_, _ = fmt.Fprintf(stderr, ...)` pattern

61
flake.lock generated Normal file
View File

@@ -0,0 +1,61 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1769318308,
"narHash": "sha256-Mjx6p96Pkefks3+aA+72lu1xVehb6mv2yTUUqmSet6Q=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1cd347bf3355fce6c64ab37d3967b4a2cb4b878c",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-25.11",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

35
flake.nix Normal file
View File

@@ -0,0 +1,35 @@
{
description = "Development Environment";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
flake-utils.url = "github:numtide/flake-utils";
};
outputs =
{ self
, nixpkgs
, flake-utils
,
}:
flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = (
import nixpkgs {
system = system;
}
);
in
{
devShells.default = pkgs.mkShell {
packages = with pkgs; [
# Backend
go
gopls
golangci-lint
];
};
}
);
}

20
go.mod Normal file
View File

@@ -0,0 +1,20 @@
module poiesis
go 1.25.5
require (
github.com/dop251/goja v0.0.0-20260106131823-651366fbe6e3
github.com/evanw/esbuild v0.27.2
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dlclark/regexp2 v1.11.4 // indirect
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.11.1 // indirect
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
golang.org/x/text v0.3.8 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

28
go.sum Normal file
View File

@@ -0,0 +1,28 @@
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dop251/goja v0.0.0-20260106131823-651366fbe6e3 h1:bVp3yUzvSAJzu9GqID+Z96P+eu5TKnIMJSV4QaZMauM=
github.com/dop251/goja v0.0.0-20260106131823-651366fbe6e3/go.mod h1:MxLav0peU43GgvwVgNbLAj1s/bSGboKkhuULvq/7hx4=
github.com/evanw/esbuild v0.27.2 h1:3xBEws9y/JosfewXMM2qIyHAi+xRo8hVx475hVkJfNg=
github.com/evanw/esbuild v0.27.2/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U=
github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

72
main.go Normal file
View File

@@ -0,0 +1,72 @@
package main
import (
"fmt"
"io"
"os"
"github.com/dop251/goja"
"github.com/evanw/esbuild/pkg/api"
)
func executeTypeScript(filePath string, stdout, stderr io.Writer) error {
tsContent, err := os.ReadFile(filePath)
if err != nil {
_, _ = fmt.Fprintf(stderr, "Error reading file: %v\n", err)
return err
}
result := api.Transform(string(tsContent), api.TransformOptions{
Loader: api.LoaderTS,
Target: api.ES2020,
Format: api.FormatIIFE,
Sourcemap: api.SourceMapNone,
TreeShaking: api.TreeShakingFalse,
})
if len(result.Errors) > 0 {
_, _ = fmt.Fprintf(stderr, "Transpilation errors:\n")
for _, err := range result.Errors {
_, _ = fmt.Fprintf(stderr, " %s\n", err.Text)
}
return fmt.Errorf("transpilation failed")
}
vm := goja.New()
console := vm.NewObject()
_ = vm.Set("console", console)
_ = console.Set("log", func(call goja.FunctionCall) goja.Value {
args := call.Arguments
for i, arg := range args {
if i > 0 {
_, _ = fmt.Fprint(stdout, " ")
}
_, _ = fmt.Fprint(stdout, arg.String())
}
_, _ = fmt.Fprintln(stdout)
return goja.Undefined()
})
_, err = vm.RunString(string(result.Code))
if err != nil {
_, _ = fmt.Fprintf(stderr, "Execution error: %v\n", err)
return err
}
return nil
}
func main() {
if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, "Usage: program <typescript-file>")
os.Exit(1)
}
filePath := os.Args[1]
if err := executeTypeScript(filePath, os.Stdout, os.Stderr); err != nil {
os.Exit(1)
}
}

69
main_test.go Normal file
View File

@@ -0,0 +1,69 @@
package main
import (
"bytes"
"os"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestExecuteTypeScript(t *testing.T) {
var stdout, stderr bytes.Buffer
err := executeTypeScript("test_data/test.ts", &stdout, &stderr)
assert.NoError(t, err, "Expected no error")
assert.Empty(t, stderr.String(), "Expected no error output")
output := stdout.String()
assert.Contains(t, output, "Hello, Alice!", "Should greet Alice")
assert.Contains(t, output, "You are 30 years old", "Should show age")
assert.Contains(t, output, "Email: alice@example.com", "Should show email")
assert.Contains(t, output, "Sum of 5 and 10 is: 15", "Should calculate sum correctly")
lines := strings.Split(strings.TrimSpace(output), "\n")
assert.GreaterOrEqual(t, len(lines), 3, "Should have at least 3 output lines")
}
func TestExecuteTypeScriptWithNonExistentFile(t *testing.T) {
var stdout, stderr bytes.Buffer
err := executeTypeScript("non_existent_file.ts", &stdout, &stderr)
assert.Error(t, err, "Expected error for non-existent file")
assert.Contains(t, stderr.String(), "Error reading file", "Should show read error")
}
func TestExecuteTypeScriptWithSyntaxError(t *testing.T) {
var stdout, stderr bytes.Buffer
tsContent := `
interface Person {
name: string;
age: number;
}
const user: Person = {
name: "Bob",
age: 25,
};
console.log(user.name)
console.log(user.age +);
`
err := os.WriteFile("test_data/invalid.ts", []byte(tsContent), 0644)
if err != nil {
t.Fatalf("Failed to write test file: %v", err)
}
defer func() {
_ = os.Remove("test_data/invalid.ts")
}()
err = executeTypeScript("test_data/invalid.ts", &stdout, &stderr)
assert.Error(t, err, "Expected error for invalid TypeScript")
}

24
test_data/test.ts Normal file
View File

@@ -0,0 +1,24 @@
interface Person {
name: string;
age: number;
email: string;
}
function greet(person: Person): string {
return `Hello, ${person.name}! You are ${person.age} years old.`;
}
const user: Person = {
name: "Alice",
age: 30,
email: "alice@example.com",
};
console.log(greet(user));
console.log(`Email: ${user.email}`);
function calculateSum(a: number, b: number): number {
return a + b;
}
console.log(`Sum of 5 and 10 is: ${calculateSum(5, 10)}`);