Skip to content

Commit

Permalink
Optimize memory usage (#16)
Browse files Browse the repository at this point in the history
* Fixed bug causing 4.7.2 to be loaded in addition to 4.9.3

* Overhauled how the registry works, and added more memory efficient expiring registry
  • Loading branch information
clarkmcc authored Oct 25, 2024
1 parent 492c8de commit f99cdde
Show file tree
Hide file tree
Showing 35 changed files with 371 additions and 206 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ This package provides a thin wrapper around [goja](https://github.com/dop251/goj
* AMD-style modules using the built-in [Almond module loader](https://github.com/requirejs/almond).
* Custom Typescript version registration with built-in support for versions 3.8.3, 3.9.9, 4.1.2, 4.1.3, 4.1.4, 4.1.5, 4.2.2, 4.2.3, 4.2.4, and 4.7.2.
* 90%+ test coverage
* Used in production world-wide (sponsoring company has evaluated over 1 billion scripts using this runtime)

## Installation

Expand Down
29 changes: 14 additions & 15 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ type TranspileOptionFunc func(*Config)

// Config defines the behavior of the typescript compiler.
type Config struct {
CompileOptions map[string]interface{}
TypescriptSource *goja.Program
Runtime *goja.Runtime
CompileOptions map[string]interface{}
TypescriptVersion string
Registry versions.Registry
Runtime *goja.Runtime

// If a module is exported by the typescript compiler, this is the name the module will be called
ModuleName string
Expand Down Expand Up @@ -58,26 +59,24 @@ func (c *Config) Initialize() error {
// typescript source code.s
func NewDefaultConfig() *Config {
return &Config{
Runtime: goja.New(),
CompileOptions: nil,
TypescriptSource: versions.DefaultRegistry.MustGet("v4.9.3"),
ModuleName: "default",
Runtime: goja.New(),
CompileOptions: nil,
TypescriptVersion: "v4.9.3",
Registry: versions.NewRegistry(),
ModuleName: "default",
}
}

// WithVersion loads the provided tagged typescript source from the default registry
func WithVersion(tag string) TranspileOptionFunc {
func WithRegistry(registry versions.Registry) TranspileOptionFunc {
return func(config *Config) {
config.TypescriptSource = versions.DefaultRegistry.MustGet(tag)
config.Registry = registry
}
}

// WithTypescriptSource configures a Typescript source from the provided typescript source string which
// is compiled by goja when the config is initialized. This function will panic if the Typescript source
// is invalid.
func WithTypescriptSource(src string) TranspileOptionFunc {
// WithVersion loads the provided tagged typescript source from the default registry
func WithVersion(tag string) TranspileOptionFunc {
return func(config *Config) {
config.TypescriptSource = goja.MustCompile("", src, true)
config.TypescriptVersion = tag
}
}

Expand Down
116 changes: 40 additions & 76 deletions config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@ package typescript
import (
"fmt"
"github.com/clarkmcc/go-typescript/versions"
_ "github.com/clarkmcc/go-typescript/versions/v3.8.3"
_ "github.com/clarkmcc/go-typescript/versions/v3.9.9"
_ "github.com/clarkmcc/go-typescript/versions/v4.1.2"
_ "github.com/clarkmcc/go-typescript/versions/v4.1.3"
_ "github.com/clarkmcc/go-typescript/versions/v4.1.4"
_ "github.com/clarkmcc/go-typescript/versions/v4.1.5"
_ "github.com/clarkmcc/go-typescript/versions/v4.2.2"
v423 "github.com/clarkmcc/go-typescript/versions/v4.2.3"
_ "github.com/clarkmcc/go-typescript/versions/v4.2.4"
_ "github.com/clarkmcc/go-typescript/versions/v4.7.2"
v3_8_3 "github.com/clarkmcc/go-typescript/versions/v3.8.3"
v3_9_9 "github.com/clarkmcc/go-typescript/versions/v3.9.9"
v4_1_2 "github.com/clarkmcc/go-typescript/versions/v4.1.2"
v4_1_3 "github.com/clarkmcc/go-typescript/versions/v4.1.3"
v4_1_4 "github.com/clarkmcc/go-typescript/versions/v4.1.4"
v4_1_5 "github.com/clarkmcc/go-typescript/versions/v4.1.5"
v4_2_2 "github.com/clarkmcc/go-typescript/versions/v4.2.2"
v4_2_3 "github.com/clarkmcc/go-typescript/versions/v4.2.3"
v4_2_4 "github.com/clarkmcc/go-typescript/versions/v4.2.4"
v4_7_2 "github.com/clarkmcc/go-typescript/versions/v4.7.2"
v4_9_3 "github.com/clarkmcc/go-typescript/versions/v4.9.3"
"github.com/stretchr/testify/require"
"testing"
)
Expand All @@ -26,82 +27,45 @@ func TestConfig_Initialize(t *testing.T) {
}

func TestVersionLoading(t *testing.T) {
t.Run("v3.8.3", func(t *testing.T) {
output, err := TranspileString("let a: number = 10;", WithVersion("v3.8.3"))
require.NoError(t, err)
require.Equal(t, "var a = 10;", output)
})
t.Run("v3.9.9", func(t *testing.T) {
output, err := TranspileString("let a: number = 10;", WithVersion("v3.9.9"))
require.NoError(t, err)
require.Equal(t, "var a = 10;", output)
})
t.Run("v4.1.2", func(t *testing.T) {
output, err := TranspileString("let a: number = 10;", WithVersion("v4.1.2"))
require.NoError(t, err)
require.Equal(t, "var a = 10;", output)
})
t.Run("v4.1.3", func(t *testing.T) {
output, err := TranspileString("let a: number = 10;", WithVersion("v4.1.3"))
require.NoError(t, err)
require.Equal(t, "var a = 10;", output)
})
t.Run("v4.1.4", func(t *testing.T) {
output, err := TranspileString("let a: number = 10;", WithVersion("v4.1.4"))
require.NoError(t, err)
require.Equal(t, "var a = 10;", output)
})
t.Run("v4.1.5", func(t *testing.T) {
output, err := TranspileString("let a: number = 10;", WithVersion("v4.1.5"))
require.NoError(t, err)
require.Equal(t, "var a = 10;", output)
})
t.Run("v4.2.2", func(t *testing.T) {
output, err := TranspileString("let a: number = 10;", WithVersion("v4.2.2"))
require.NoError(t, err)
require.Equal(t, "var a = 10;", output)
})
t.Run("v4.2.3", func(t *testing.T) {
output, err := TranspileString("let a: number = 10;", WithVersion("v4.2.3"))
require.NoError(t, err)
require.Equal(t, "var a = 10;", output)
})
t.Run("v4.2.4", func(t *testing.T) {
output, err := TranspileString("let a: number = 10;", WithVersion("v4.2.4"))
require.NoError(t, err)
require.Equal(t, "var a = 10;", output)
})
t.Run("v4.7.2", func(t *testing.T) {
output, err := TranspileString("let a: number = 10;", WithVersion("v4.7.2"))
require.NoError(t, err)
require.Equal(t, "var a = 10;", output)
})
}

func TestCustomRegistry(t *testing.T) {
registry := versions.NewRegistry()
registry.MustRegister("v4.2.3", v423.Source)

output, err := TranspileString("let a: number = 10;", func(config *Config) {
config.TypescriptSource = registry.MustGet("v4.2.3")
})
require.NoError(t, err)
require.Equal(t, "var a = 10;", output)
sources := map[string]string{
"v3.8.3": v3_8_3.Source,
"v3.9.9": v3_9_9.Source,
"v4.1.2": v4_1_2.Source,
"v4.1.3": v4_1_3.Source,
"v4.1.4": v4_1_4.Source,
"v4.1.5": v4_1_5.Source,
"v4.2.2": v4_2_2.Source,
"v4.2.3": v4_2_3.Source,
"v4.2.4": v4_2_4.Source,
"v4.7.2": v4_7_2.Source,
"v4.9.3": v4_9_3.Source,
}

for tag, source := range sources {
registry.Register(tag, source)
}

for tag, _ := range sources {
t.Run(tag, func(t *testing.T) {
output, err := TranspileString("let a: number = 10;", WithRegistry(registry), WithVersion(tag))
require.NoError(t, err)
require.Equal(t, "var a = 10;", output)
})
}
}

func TestWithModuleName(t *testing.T) {
registry := versions.NewRegistry()
registry.Register("v4.9.3", v4_9_3.Source)
output, err := TranspileString("let a: number = 10;",
WithModuleName("myModuleName"),
WithRegistry(registry),
WithVersion("v4.9.3"),
WithCompileOptions(map[string]interface{}{
"module": "amd",
}))
require.NoError(t, err)
require.Contains(t, output, "define(\"myModuleName\"")
}

func TestWithTypescriptSource(t *testing.T) {
output, err := TranspileString("let a: number = 10;",
WithTypescriptSource(v423.Source))
require.NoError(t, err)
require.Equal(t, "var a = 10;", output)
}
9 changes: 9 additions & 0 deletions evaluate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"context"
"errors"
"fmt"
"github.com/clarkmcc/go-typescript/versions"
v4_9_3 "github.com/clarkmcc/go-typescript/versions/v4.9.3"
"github.com/dop251/goja"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand All @@ -24,6 +26,9 @@ var (
)

func TestEvaluateCtx(t *testing.T) {
registry := versions.NewRegistry()
registry.Register("v4.9.3", v4_9_3.Source)

// This test hits a lot of things:
// #1 - We test that we can load the almond AMD module loader
// #2 - We test that we can load our own 'evaluate before' script that declares an AMD module
Expand All @@ -33,6 +38,8 @@ func TestEvaluateCtx(t *testing.T) {
result, err := EvaluateCtx(context.Background(), strings.NewReader(script),
WithAlmondModuleLoader(),
WithTranspile(),
WithTranspileOptions(WithRegistry(registry),
WithVersion("v4.9.3")),
WithEvaluateBefore(strings.NewReader(amdModuleScript)),
WithTranspileOptions(func(config *Config) {
config.Verbose = true
Expand Down Expand Up @@ -100,6 +107,8 @@ func TestEvaluateCtx(t *testing.T) {
s1 := "let a: number = 10"
_, err := Evaluate(strings.NewReader(s1),
WithTranspile(),
WithTranspileOptions(WithRegistry(registry),
WithVersion("v4.9.3")),
WithScriptPreTranspileHook(func(s2 string) (string, error) {
assert.Equal(t, s1, s2)
return s2, nil
Expand Down
11 changes: 10 additions & 1 deletion examples/typescript_amd_modules_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,26 @@ import (
_ "embed"
"fmt"
"github.com/clarkmcc/go-typescript"
"github.com/clarkmcc/go-typescript/versions"
v4_9_3 "github.com/clarkmcc/go-typescript/versions/v4.9.3"
"strings"
)

//go:embed javascript-example-module.js
var module string

func ExampleTypescriptAMDModule() {
registry := versions.NewRegistry()
registry.Register("v4.9.3", v4_9_3.Source)

result, err := typescript.Evaluate(strings.NewReader(`import { multiply } from 'myModule'; multiply(5, 5)`),
typescript.WithTranspile(),
typescript.WithAlmondModuleLoader(),
typescript.WithEvaluateBefore(strings.NewReader(module)))
typescript.WithEvaluateBefore(strings.NewReader(module)),
typescript.WithTranspileOptions(
typescript.WithRegistry(registry),
typescript.WithVersion("v4.9.3"),
))
if err != nil {
panic(err)
}
Expand Down
10 changes: 9 additions & 1 deletion examples/typescript_context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
_ "embed"
"fmt"
"github.com/clarkmcc/go-typescript"
"github.com/clarkmcc/go-typescript/versions"
v4_9_3 "github.com/clarkmcc/go-typescript/versions/v4.9.3"
"strings"
)

Expand All @@ -15,7 +17,13 @@ func ExampleContext() {
ctx, cancel := context.WithCancel(context.Background())
cancel()

_, err := typescript.TranspileCtx(ctx, strings.NewReader(script3))
registry := versions.NewRegistry()
registry.Register("v4.9.3", v4_9_3.Source)

_, err := typescript.TranspileCtx(ctx,
strings.NewReader(script3),
typescript.WithRegistry(registry),
typescript.WithVersion("v4.9.3"))
if err == nil {
panic("expected error")
}
Expand Down
10 changes: 9 additions & 1 deletion examples/typescript_evaluate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,23 @@ import (
_ "embed"
"fmt"
"github.com/clarkmcc/go-typescript"
"github.com/clarkmcc/go-typescript/versions"
v4_9_3 "github.com/clarkmcc/go-typescript/versions/v4.9.3"
"strings"
)

//go:embed typescript-example.ts
var script2 string

func ExampleTypescriptEvaluate() {
registry := versions.NewRegistry()
registry.Register("v4.9.3", v4_9_3.Source)

// Transpile the typescript and return evaluated result
result, err := typescript.Evaluate(strings.NewReader(script2), typescript.WithTranspile())
result, err := typescript.Evaluate(strings.NewReader(script2), typescript.WithTranspile(), typescript.WithTranspileOptions(
typescript.WithRegistry(registry),
typescript.WithVersion("v4.9.3"),
))
if err != nil {
panic(err)
}
Expand Down
7 changes: 6 additions & 1 deletion examples/typescript_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
_ "embed"
"fmt"
"github.com/clarkmcc/go-typescript"
"github.com/clarkmcc/go-typescript/versions"
v4_9_3 "github.com/clarkmcc/go-typescript/versions/v4.9.3"
"strings"
)

Expand All @@ -24,8 +26,11 @@ var me = new Person("John Doe");
me.greet();`

func ExampleTranspile() {
registry := versions.NewRegistry()
registry.Register("v4.9.3", v4_9_3.Source)

// Only transpile the typescript and return transpiled Javascript, don't evaluate
transpiled, err := typescript.TranspileString(script1)
transpiled, err := typescript.TranspileString(script1, typescript.WithRegistry(registry), typescript.WithVersion("v4.9.3"))
if err != nil {
panic(err)
}
Expand Down
6 changes: 5 additions & 1 deletion transpiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ func TranspileCtx(ctx context.Context, script io.Reader, opts ...TranspileOption
if err != nil {
return "", fmt.Errorf("initializing config: %w", err)
}
_, err = cfg.Runtime.RunProgram(cfg.TypescriptSource)
src, err := cfg.Registry.Get(cfg.TypescriptVersion)
if err != nil {
return "", fmt.Errorf("getting typescript source: %w", err)
}
_, err = cfg.Runtime.RunProgram(src)
if err != nil {
return "", fmt.Errorf("running typescript compiler: %w", err)
}
Expand Down
12 changes: 9 additions & 3 deletions transpiler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package typescript

import (
"context"
"github.com/clarkmcc/go-typescript/versions"
v4_2_3 "github.com/clarkmcc/go-typescript/versions/v4.2.3"
"github.com/dop251/goja"
"github.com/stretchr/testify/require"
"strings"
Expand All @@ -10,19 +12,21 @@ import (

func TestCompileVariousScripts(t *testing.T) {
runtime := goja.New()
registry := versions.NewRegistry()
registry.Register("v4.2.3", v4_2_3.Source)

t.Run("let", func(t *testing.T) {
compiled, err := TranspileString("let a: number = 10;", WithCompileOptions(map[string]interface{}{
"module": "none",
}), WithVersion("v4.2.3"), WithRuntime(runtime))
}), WithVersion("v4.2.3"), WithRegistry(registry), WithRuntime(runtime))
require.NoError(t, err)
require.Equal(t, "var a = 10;", compiled)
})

t.Run("arrow function", func(t *testing.T) {
compiled, err := TranspileString("((): number => 10)()", WithCompileOptions(map[string]interface{}{
"module": "none",
}), WithVersion("v4.2.3"), WithRuntime(runtime))
}), WithVersion("v4.2.3"), WithRegistry(registry), WithRuntime(runtime))
require.NoError(t, err)
require.Equal(t, "(function () { return 10; })();", compiled)
})
Expand All @@ -49,7 +53,9 @@ func TestBadConfig(t *testing.T) {
}

func TestTranspile(t *testing.T) {
output, err := Transpile(strings.NewReader("let a: number = 10;"))
registry := versions.NewRegistry()
registry.Register("v4.2.3", v4_2_3.Source)
output, err := Transpile(strings.NewReader("let a: number = 10;"), WithRegistry(registry), WithVersion("v4.2.3"))
require.NoError(t, err)
require.Equal(t, "var a = 10;", output)
}
Loading

0 comments on commit f99cdde

Please sign in to comment.