wip2
This commit is contained in:
39
AGENTS.md
39
AGENTS.md
@@ -1,28 +1,46 @@
|
||||
# Poiesis
|
||||
|
||||
## Module Name
|
||||
|
||||
`reichard.io/poiesis`
|
||||
|
||||
## Overview
|
||||
|
||||
Go tool that transpiles TypeScript to JavaScript using esbuild API and executes it with goja.
|
||||
Go tool that transpiles TypeScript to JavaScript using esbuild API and executes it with goja. Features a flexible builtin system for exposing Go functions to TypeScript.
|
||||
|
||||
## Build & Test
|
||||
|
||||
```bash
|
||||
go build
|
||||
go test
|
||||
go build ./cmd/poiesis
|
||||
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
|
||||
```
|
||||
reichard.io/poiesis/
|
||||
├── cmd/poiesis/ # CLI entry point
|
||||
│ └── main.go
|
||||
├── internal/
|
||||
│ ├── builtin/ # Builtin function framework
|
||||
│ │ ├── builtin.go
|
||||
│ │ └── builtin_test.go
|
||||
│ └── runtime/ # TypeScript transpilation + execution
|
||||
│ ├── runtime.go
|
||||
│ └── runtime_test.go
|
||||
└── test_data/ # Test TypeScript files
|
||||
```
|
||||
|
||||
## Key Packages
|
||||
|
||||
- `reichard.io/poiesis/internal/runtime` - Runtime management, TypeScript transpilation, execution
|
||||
- `reichard.io/poiesis/internal/builtin` - Builtin registration and type conversion
|
||||
|
||||
## Testing Patterns
|
||||
|
||||
- **Test framework**: Go's built-in `testing` package
|
||||
- **Assertions**: `github.com/stretchr/testify/assert`
|
||||
- **Assertions**: `github.com/stretchr/testify/assert` and `require`
|
||||
- **Linting**: `golangci-lint run` - must pass before committing
|
||||
- **Test organization**: Test files use `_test.go` suffix, test functions prefixed with `Test`
|
||||
|
||||
@@ -32,12 +50,9 @@ golangci-lint run
|
||||
- `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
|
||||
- Package structure follows standard Go project layout with internal packages
|
||||
|
||||
89
README.md
Normal file
89
README.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# Poiesis
|
||||
|
||||
A Go tool that transpiles TypeScript to JavaScript using esbuild and executes it with goja, with an extensible builtin system.
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
reichard.io/poiesis/
|
||||
├── cmd/
|
||||
│ └── poiesis/ # CLI application entry point
|
||||
│ └── main.go
|
||||
├── internal/
|
||||
│ ├── builtin/ # Builtin function framework
|
||||
│ │ ├── builtin.go
|
||||
│ │ └── builtin_test.go
|
||||
│ └── runtime/ # TypeScript transpilation and execution
|
||||
│ ├── runtime.go
|
||||
│ └── runtime_test.go
|
||||
└── examples/ # Example TypeScript files
|
||||
```
|
||||
|
||||
## Installation & Build
|
||||
|
||||
```bash
|
||||
go build ./cmd/poiesis
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
go test ./...
|
||||
golangci-lint run
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
poiesis <typescript-file>
|
||||
```
|
||||
|
||||
## Builtin System
|
||||
|
||||
The builtin system allows you to easily expose Go functions to TypeScript/JavaScript.
|
||||
|
||||
### Adding a Builtin
|
||||
|
||||
Just write a Go function and register it:
|
||||
|
||||
```go
|
||||
// Your function
|
||||
func add(a, b int) int {
|
||||
return a + b
|
||||
}
|
||||
|
||||
// Register it
|
||||
func init() {
|
||||
builtin.RegisterBuiltin("add", add)
|
||||
}
|
||||
```
|
||||
|
||||
That's it! The framework automatically:
|
||||
- Converts TypeScript values to Go types
|
||||
- Handles errors (panics as JS errors)
|
||||
- Generates TypeScript definitions
|
||||
- Manages the goja integration
|
||||
|
||||
### Example
|
||||
|
||||
```typescript
|
||||
// TypeScript code
|
||||
console.log("5 + 10 =", add(5, 10));
|
||||
|
||||
const response = fetch("https://httpbin.org/get");
|
||||
console.log("OK:", response.ok);
|
||||
console.log("Status:", response.status);
|
||||
console.log("Body:", response.text());
|
||||
```
|
||||
|
||||
### Built-in Functions
|
||||
|
||||
- `fetch(url, options?)` - HTTP requests
|
||||
- `add(a, b)` - Simple arithmetic example
|
||||
- `greet(name)` - String manipulation example
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `github.com/evanw/esbuild/pkg/api` - TypeScript transpilation
|
||||
- `github.com/dop251/goja` - JavaScript execution
|
||||
- `github.com/stretchr/testify/assert` - Test assertions
|
||||
4
go.mod
4
go.mod
@@ -1,10 +1,11 @@
|
||||
module poiesis
|
||||
module reichard.io/poiesis
|
||||
|
||||
go 1.25.5
|
||||
|
||||
require (
|
||||
github.com/dop251/goja v0.0.0-20260106131823-651366fbe6e3
|
||||
github.com/evanw/esbuild v0.27.2
|
||||
github.com/stretchr/testify v1.11.1
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -13,7 +14,6 @@ require (
|
||||
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
|
||||
|
||||
1
go.sum
1
go.sum
@@ -21,6 +21,7 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9w
|
||||
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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
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=
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package builtin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -1,9 +1,8 @@
|
||||
package main
|
||||
package builtin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -53,36 +52,3 @@ func TestFetchWithInvalidURL(t *testing.T) {
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to fetch")
|
||||
}
|
||||
|
||||
func TestFetchBuiltinIntegration(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.Header().Set("X-Custom", "custom-value")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("Hello, World!"))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
var stdout, stderr strings.Builder
|
||||
tsContent := `
|
||||
const response = fetch("${URL}");
|
||||
console.log("OK:", response.ok);
|
||||
console.log("Status:", response.status);
|
||||
console.log("Body:", response.text());
|
||||
|
||||
console.log("Content-Type:", response.headers.get("content-type") || "undefined");
|
||||
console.log("Content-Type (case sensitive):", response.headers.get("Content-Type") || "undefined");
|
||||
console.log("X-Custom:", response.headers.get("x-custom") || "undefined");
|
||||
console.log("X-Custom (case sensitive):", response.headers.get("X-Custom") || "undefined");
|
||||
`
|
||||
tsContent = strings.Replace(tsContent, "${URL}", server.URL, 1)
|
||||
|
||||
err := executeTypeScriptContent(tsContent, &stdout, &stderr)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, stderr.String(), "Expected no error output")
|
||||
|
||||
output := stdout.String()
|
||||
assert.Contains(t, output, "OK: true")
|
||||
assert.Contains(t, output, "Status: 200")
|
||||
assert.Contains(t, output, "Body: Hello, World!")
|
||||
}
|
||||
137
internal/runtime/runtime.go
Normal file
137
internal/runtime/runtime.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/dop251/goja"
|
||||
"github.com/evanw/esbuild/pkg/api"
|
||||
"reichard.io/poiesis/internal/builtin"
|
||||
)
|
||||
|
||||
type Runtime struct {
|
||||
vm *goja.Runtime
|
||||
stdout io.Writer
|
||||
stderr io.Writer
|
||||
}
|
||||
|
||||
func New() *Runtime {
|
||||
vm := goja.New()
|
||||
|
||||
r := &Runtime{vm: vm, stdout: os.Stdout, stderr: os.Stderr}
|
||||
r.setupConsole()
|
||||
builtin.RegisterBuiltins(vm)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Runtime) setupConsole() {
|
||||
console := r.vm.NewObject()
|
||||
_ = r.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(r.stdout, " ")
|
||||
}
|
||||
_, _ = fmt.Fprint(r.stdout, arg.String())
|
||||
}
|
||||
_, _ = fmt.Fprintln(r.stdout)
|
||||
return goja.Undefined()
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Runtime) SetOutput(stdout, stderr io.Writer) {
|
||||
r.stdout = stdout
|
||||
r.stderr = stderr
|
||||
consoleObj := r.vm.Get("console")
|
||||
if consoleObj != nil {
|
||||
console := consoleObj.ToObject(r.vm)
|
||||
if console != nil {
|
||||
r.setupConsole()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Runtime) RunFile(filePath string, stdout, stderr io.Writer) error {
|
||||
r.stdout = stdout
|
||||
r.stderr = stderr
|
||||
r.setupConsole()
|
||||
|
||||
content, err := r.transformFile(filePath)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(stderr, "Error: %v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if len(content.errors) > 0 {
|
||||
_, _ = fmt.Fprintf(stderr, "Transpilation errors:\n")
|
||||
for _, err := range content.errors {
|
||||
_, _ = fmt.Fprintf(stderr, " %s\n", err.Text)
|
||||
}
|
||||
return fmt.Errorf("transpilation failed")
|
||||
}
|
||||
|
||||
_, err = r.vm.RunString(content.code)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(stderr, "Execution error: %v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Runtime) RunCode(tsCode string, stdout, stderr io.Writer) error {
|
||||
r.stdout = stdout
|
||||
r.stderr = stderr
|
||||
r.setupConsole()
|
||||
|
||||
content := r.transformCode(tsCode)
|
||||
|
||||
if len(content.errors) > 0 {
|
||||
_, _ = fmt.Fprintf(stderr, "Transpilation errors:\n")
|
||||
for _, err := range content.errors {
|
||||
_, _ = fmt.Fprintf(stderr, " %s\n", err.Text)
|
||||
}
|
||||
return fmt.Errorf("transpilation failed")
|
||||
}
|
||||
|
||||
_, err := r.vm.RunString(content.code)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(stderr, "Execution error: %v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type transformResult struct {
|
||||
code string
|
||||
errors []api.Message
|
||||
}
|
||||
|
||||
func (r *Runtime) transformFile(filePath string) (*transformResult, error) {
|
||||
tsFileContent, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading file: %w", err)
|
||||
}
|
||||
|
||||
return r.transformCode(string(tsFileContent)), nil
|
||||
}
|
||||
|
||||
func (r *Runtime) transformCode(tsCode string) *transformResult {
|
||||
result := api.Transform(tsCode, api.TransformOptions{
|
||||
Loader: api.LoaderTS,
|
||||
Target: api.ES2020,
|
||||
Format: api.FormatIIFE,
|
||||
Sourcemap: api.SourceMapNone,
|
||||
TreeShaking: api.TreeShakingFalse,
|
||||
})
|
||||
|
||||
return &transformResult{
|
||||
code: string(result.Code),
|
||||
errors: result.Errors,
|
||||
}
|
||||
}
|
||||
44
internal/runtime/runtime_test.go
Normal file
44
internal/runtime/runtime_test.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestExecuteTypeScript(t *testing.T) {
|
||||
var stdout, stderr bytes.Buffer
|
||||
|
||||
rt := New()
|
||||
err := rt.RunFile("../../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 TestFetchBuiltinIntegration(t *testing.T) {
|
||||
rt := New()
|
||||
|
||||
tsContent := `
|
||||
const result = add(5, 10);
|
||||
console.log("Result:", result);
|
||||
`
|
||||
|
||||
var stdout, stderr bytes.Buffer
|
||||
err := rt.RunCode(tsContent, &stdout, &stderr)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, stdout.String(), "Result: 15")
|
||||
}
|
||||
119
main.go
119
main.go
@@ -1,119 +0,0 @@
|
||||
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()
|
||||
|
||||
RegisterBuiltins(vm)
|
||||
|
||||
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 executeTypeScriptContent(tsContent string, stdout, stderr io.Writer) error {
|
||||
result := api.Transform(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()
|
||||
|
||||
RegisterBuiltins(vm)
|
||||
|
||||
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
69
main_test.go
@@ -1,69 +0,0 @@
|
||||
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")
|
||||
}
|
||||
Reference in New Issue
Block a user