From f99cdde742429fbf9e9714e6fc0a3561b8778494 Mon Sep 17 00:00:00 2001 From: Clark McCauley Date: Fri, 25 Oct 2024 14:58:36 -0600 Subject: [PATCH] Optimize memory usage (#16) * 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 --- README.md | 1 + config.go | 29 +++--- config_test.go | 116 ++++++++---------------- evaluate_test.go | 9 ++ examples/typescript_amd_modules_test.go | 11 ++- examples/typescript_context_test.go | 10 +- examples/typescript_evaluate_test.go | 10 +- examples/typescript_test.go | 7 +- transpiler.go | 6 +- transpiler_test.go | 12 ++- versions/expiring_registry.go | 100 ++++++++++++++++++++ versions/registry.go | 74 ++++++++------- versions/registry_test.go | 27 ++---- versions/v3.8.3/loader.go | 5 - versions/v3.8.3/loader_test.go | 10 ++ versions/v3.9.9/loader.go | 5 - versions/v3.9.9/loader_test.go | 10 ++ versions/v4.1.2/loader.go | 5 - versions/v4.1.2/loader_test.go | 10 ++ versions/v4.1.3/loader.go | 5 - versions/v4.1.3/loader_test.go | 10 ++ versions/v4.1.4/loader.go | 5 - versions/v4.1.4/loader_test.go | 10 ++ versions/v4.1.5/loader.go | 5 - versions/v4.1.5/loader_test.go | 10 ++ versions/v4.2.2/loader.go | 5 - versions/v4.2.2/loader_test.go | 10 ++ versions/v4.2.3/loader.go | 5 - versions/v4.2.3/loader_test.go | 10 ++ versions/v4.2.4/loader.go | 5 - versions/v4.2.4/loader_test.go | 10 ++ versions/v4.7.2/loader.go | 5 - versions/v4.7.2/loader_test.go | 10 ++ versions/v4.9.3/loader.go | 5 - versions/v4.9.3/loader_test.go | 10 ++ 35 files changed, 371 insertions(+), 206 deletions(-) create mode 100644 versions/expiring_registry.go create mode 100644 versions/v3.8.3/loader_test.go create mode 100644 versions/v3.9.9/loader_test.go create mode 100644 versions/v4.1.2/loader_test.go create mode 100644 versions/v4.1.3/loader_test.go create mode 100644 versions/v4.1.4/loader_test.go create mode 100644 versions/v4.1.5/loader_test.go create mode 100644 versions/v4.2.2/loader_test.go create mode 100644 versions/v4.2.3/loader_test.go create mode 100644 versions/v4.2.4/loader_test.go create mode 100644 versions/v4.7.2/loader_test.go create mode 100644 versions/v4.9.3/loader_test.go diff --git a/README.md b/README.md index 01b8f61..666ee77 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/config.go b/config.go index eee00b9..a42b636 100644 --- a/config.go +++ b/config.go @@ -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 @@ -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 } } diff --git a/config_test.go b/config_test.go index 8cd921f..e2ae08e 100644 --- a/config_test.go +++ b/config_test.go @@ -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" ) @@ -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) -} diff --git a/evaluate_test.go b/evaluate_test.go index dcb0e9e..f9104ce 100644 --- a/evaluate_test.go +++ b/evaluate_test.go @@ -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" @@ -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 @@ -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 @@ -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 diff --git a/examples/typescript_amd_modules_test.go b/examples/typescript_amd_modules_test.go index 2174dbf..112b83d 100644 --- a/examples/typescript_amd_modules_test.go +++ b/examples/typescript_amd_modules_test.go @@ -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" ) @@ -11,10 +13,17 @@ import ( 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) } diff --git a/examples/typescript_context_test.go b/examples/typescript_context_test.go index 1fe8aea..e3e00f9 100644 --- a/examples/typescript_context_test.go +++ b/examples/typescript_context_test.go @@ -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" ) @@ -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") } diff --git a/examples/typescript_evaluate_test.go b/examples/typescript_evaluate_test.go index 75dd267..c36e164 100644 --- a/examples/typescript_evaluate_test.go +++ b/examples/typescript_evaluate_test.go @@ -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" ) @@ -11,8 +13,14 @@ import ( 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) } diff --git a/examples/typescript_test.go b/examples/typescript_test.go index edc62f4..6c1f085 100644 --- a/examples/typescript_test.go +++ b/examples/typescript_test.go @@ -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" ) @@ -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) } diff --git a/transpiler.go b/transpiler.go index f454e57..6fd836f 100644 --- a/transpiler.go +++ b/transpiler.go @@ -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) } diff --git a/transpiler_test.go b/transpiler_test.go index cafabcd..2b446c5 100644 --- a/transpiler_test.go +++ b/transpiler_test.go @@ -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" @@ -10,11 +12,13 @@ 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) }) @@ -22,7 +26,7 @@ func TestCompileVariousScripts(t *testing.T) { 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) }) @@ -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) } diff --git a/versions/expiring_registry.go b/versions/expiring_registry.go new file mode 100644 index 0000000..852be1a --- /dev/null +++ b/versions/expiring_registry.go @@ -0,0 +1,100 @@ +package versions + +import ( + "fmt" + "github.com/dop251/goja" + "sync" + "time" +) + +// ExpiringRegistry is a thread-safe registry for storing Typescript programs +// that are garbage collected after a certain amount of inactivity. Retrieving +// a program from the registry will reset its expiration time allowing the +// compiled program to stay cached for longer. +type ExpiringRegistry struct { + lock sync.Mutex + versions map[string]string + compiled map[string]entry + + // A struct is sent on this channel every time the registry is cleaned up. + Freed chan struct{} + + ttl time.Duration +} + +type entry struct { + value *goja.Program + exp time.Time +} + +func (r *ExpiringRegistry) Register(tag string, source string) { + r.lock.Lock() + defer r.lock.Unlock() + r.versions[tag] = source +} + +func (r *ExpiringRegistry) Get(tag string) (*goja.Program, error) { + r.lock.Lock() + defer r.lock.Unlock() + + e, ok := r.compiled[tag] + if ok && e.exp.After(time.Now()) { + e.exp = time.Now().Add(r.ttl) + r.compiled[tag] = e + return e.value, nil + } + delete(r.compiled, tag) + + src, ok := r.versions[tag] + if !ok { + return nil, fmt.Errorf("unsupported version tag '%s', must be one of %v", tag, r.RegisteredVersions()) + } + prg, err := goja.Compile("", src, true) + if err != nil { + return nil, fmt.Errorf("compiling registered source for tag '%s': %w", tag, err) + } + r.compiled[tag] = entry{value: prg, exp: time.Now().Add(r.ttl)} + return prg, nil +} + +func (r *ExpiringRegistry) RegisteredVersions() (out []string) { + for k := range r.versions { + out = append(out, k) + } + return +} + +func NewExpiringRegistry(ttl time.Duration) *ExpiringRegistry { + r := &ExpiringRegistry{ + versions: make(map[string]string), + compiled: make(map[string]entry), + Freed: make(chan struct{}), + ttl: ttl, + } + + go func() { + for { + time.Sleep(r.ttl) + r.lock.Lock() + var deleted bool + for k, e := range r.compiled { + if e.exp.Before(time.Now()) { + deleted = true + delete(r.compiled, k) + } + } + r.lock.Unlock() + + // Once we've cleaned up, notify any waiting goroutines + // that we're done. This is useful if callers want to + // run a manual GC cycle after this. + if deleted { + select { + case r.Freed <- struct{}{}: + } + } + } + }() + + return r +} diff --git a/versions/registry.go b/versions/registry.go index 79bc41f..7108a54 100644 --- a/versions/registry.go +++ b/versions/registry.go @@ -3,59 +3,55 @@ package versions import ( "fmt" "github.com/dop251/goja" + "github.com/stretchr/testify/assert" "sync" + "testing" ) -// DefaultRegistry is the default instance of the typescript tagged version registry. -var DefaultRegistry = NewRegistry() +type Registry interface { + Register(tag string, source string) + Get(tag string) (*goja.Program, error) +} -// Registry is a thread-safe registry for storing tagged versions of the typescript source code. -type Registry struct { +// CachingRegistry is a thread-safe registry for storing tagged versions of the typescript source code. +type CachingRegistry struct { lock sync.Mutex - versions map[string]*goja.Program + versions map[string]string + compiled map[string]*goja.Program } // Register registers the provided source to the specified tag in the registry. -func (r *Registry) Register(tag string, source string) error { +func (r *CachingRegistry) Register(tag string, source string) { r.lock.Lock() defer r.lock.Unlock() - program, err := goja.Compile("", source, true) - if err != nil { - return fmt.Errorf("compiling registered source for tag '%s': %w", tag, err) - } - r.versions[tag] = program - return nil -} - -// MustRegister calls Register and panics if we're unable to register the version. -func (r *Registry) MustRegister(tag string, source string) { - err := r.Register(tag, source) - if err != nil { - panic(err) - } + r.versions[tag] = source + delete(r.compiled, tag) } // Get attempts to return the typescript source for the specified tag if it exists, otherwise // it returns an error with a list of typescript versions that are supported by this registry. -func (r *Registry) Get(tag string) (*goja.Program, error) { +func (r *CachingRegistry) Get(tag string) (*goja.Program, error) { + r.lock.Lock() + defer r.lock.Unlock() + + prg, ok := r.compiled[tag] + if ok { + return prg, nil + } src, ok := r.versions[tag] if !ok { return nil, fmt.Errorf("unsupported version tag '%s', must be one of %v", tag, r.supportedVersionsLocked()) } - return src, nil -} - -// MustGet calls Get with the specified tag, but panics if the tag cannot be found. -func (r *Registry) MustGet(tag string) *goja.Program { - source, err := r.Get(tag) + prg, err := goja.Compile("", src, true) if err != nil { - panic(err) + return nil, fmt.Errorf("compiling registered source for tag '%s': %w", tag, err) } - return source + r.compiled[tag] = prg + return prg, nil } // RegisteredVersions returns an unordered list of the versions that are registered in this registry -func (r *Registry) RegisteredVersions() (out []string) { +func (r *CachingRegistry) RegisteredVersions() (out []string) { for k, _ := range r.versions { out = append(out, k) } @@ -65,7 +61,7 @@ func (r *Registry) RegisteredVersions() (out []string) { // supportedVersionsLocked returns a slice of supported version tags that are registered // to this registry and can be accessed by calling Get. This function should only be called // by a caller who has already acquired a lock on the registry. -func (r *Registry) supportedVersionsLocked() (out []string) { +func (r *CachingRegistry) supportedVersionsLocked() (out []string) { for k, _ := range r.versions { out = append(out, k) } @@ -73,8 +69,18 @@ func (r *Registry) supportedVersionsLocked() (out []string) { } // NewRegistry creates a new instances of a version registry -func NewRegistry() *Registry { - return &Registry{ - versions: map[string]*goja.Program{}, +func NewRegistry() *CachingRegistry { + return &CachingRegistry{ + versions: make(map[string]string), + compiled: make(map[string]*goja.Program), } } + +// TestSource is a helper function for testing that versions of the Typescript compiler can +// properly be registered. +func TestSource(t *testing.T, version, source string) { + r := NewRegistry() + r.Register(version, source) + _, err := r.Get(version) + assert.NoErrorf(t, err, "failed to register %v", version) +} diff --git a/versions/registry_test.go b/versions/registry_test.go index 3a3404f..94b4f80 100644 --- a/versions/registry_test.go +++ b/versions/registry_test.go @@ -1,46 +1,37 @@ package versions import ( - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "testing" ) func TestRegistry_Get(t *testing.T) { + r := NewRegistry() t.Run("KnownTag", func(t *testing.T) { - DefaultRegistry.MustRegister("v4.2.3", "") - _, err := DefaultRegistry.Get("v4.2.3") + r.Register("v4.2.3", "") + _, err := r.Get("v4.2.3") require.NoError(t, err) }) t.Run("UnknownTag", func(t *testing.T) { - _, err := DefaultRegistry.Get("abc") + _, err := r.Get("abc") require.Error(t, err) }) - t.Run("MustGet", func(t *testing.T) { - assert.NotPanics(t, func() { - DefaultRegistry.MustGet("v4.2.3") - }) - }) } func TestRegistry_Register(t *testing.T) { r := NewRegistry() t.Run("ValidJavascript", func(t *testing.T) { - err := r.Register("a", "var a = 10;") + r.Register("a", "var a = 10;") + _, err := r.Get("a") require.NoError(t, err) }) t.Run("InvalidJavascript", func(t *testing.T) { - err := r.Register("a", "type a struct{}") + r.Register("a", "type a struct{}") + prg, err := r.Get("a") + require.Nil(t, prg) require.Error(t, err) }) t.Run("RegisteredVersions", func(t *testing.T) { require.Len(t, r.RegisteredVersions(), 1) }) } - -func TestRegistry_MustGet(t *testing.T) { - r := NewRegistry() - require.Panics(t, func() { - r.MustGet("a") - }) -} diff --git a/versions/v3.8.3/loader.go b/versions/v3.8.3/loader.go index 3fcb945..de69e01 100644 --- a/versions/v3.8.3/loader.go +++ b/versions/v3.8.3/loader.go @@ -2,12 +2,7 @@ package v3_8_3 import ( _ "embed" - "github.com/clarkmcc/go-typescript/versions" ) //go:embed v3.8.3.js var Source string - -func init() { - versions.DefaultRegistry.MustRegister("v3.8.3", Source) -} diff --git a/versions/v3.8.3/loader_test.go b/versions/v3.8.3/loader_test.go new file mode 100644 index 0000000..fa36909 --- /dev/null +++ b/versions/v3.8.3/loader_test.go @@ -0,0 +1,10 @@ +package v3_8_3 + +import ( + "github.com/clarkmcc/go-typescript/versions" + "testing" +) + +func TestRegister(t *testing.T) { + versions.TestSource(t, "v3.8.3", Source) +} diff --git a/versions/v3.9.9/loader.go b/versions/v3.9.9/loader.go index f703ace..bf35fd7 100644 --- a/versions/v3.9.9/loader.go +++ b/versions/v3.9.9/loader.go @@ -2,12 +2,7 @@ package v3_9_9 import ( _ "embed" - "github.com/clarkmcc/go-typescript/versions" ) //go:embed v3.9.9.js var Source string - -func init() { - versions.DefaultRegistry.MustRegister("v3.9.9", Source) -} diff --git a/versions/v3.9.9/loader_test.go b/versions/v3.9.9/loader_test.go new file mode 100644 index 0000000..d2af866 --- /dev/null +++ b/versions/v3.9.9/loader_test.go @@ -0,0 +1,10 @@ +package v3_9_9 + +import ( + "github.com/clarkmcc/go-typescript/versions" + "testing" +) + +func TestRegister(t *testing.T) { + versions.TestSource(t, "v3.9.9", Source) +} diff --git a/versions/v4.1.2/loader.go b/versions/v4.1.2/loader.go index 3c04dab..f20e114 100644 --- a/versions/v4.1.2/loader.go +++ b/versions/v4.1.2/loader.go @@ -2,12 +2,7 @@ package v4_1_2 import ( _ "embed" - "github.com/clarkmcc/go-typescript/versions" ) //go:embed v4.1.2.js var Source string - -func init() { - versions.DefaultRegistry.MustRegister("v4.1.2", Source) -} diff --git a/versions/v4.1.2/loader_test.go b/versions/v4.1.2/loader_test.go new file mode 100644 index 0000000..3bceb7a --- /dev/null +++ b/versions/v4.1.2/loader_test.go @@ -0,0 +1,10 @@ +package v4_1_2 + +import ( + "github.com/clarkmcc/go-typescript/versions" + "testing" +) + +func TestRegister(t *testing.T) { + versions.TestSource(t, "v4.1.2", Source) +} diff --git a/versions/v4.1.3/loader.go b/versions/v4.1.3/loader.go index 9a731e6..eb60c5e 100644 --- a/versions/v4.1.3/loader.go +++ b/versions/v4.1.3/loader.go @@ -2,12 +2,7 @@ package v4_1_3 import ( _ "embed" - "github.com/clarkmcc/go-typescript/versions" ) //go:embed v4.1.3.js var Source string - -func init() { - versions.DefaultRegistry.MustRegister("v4.1.3", Source) -} diff --git a/versions/v4.1.3/loader_test.go b/versions/v4.1.3/loader_test.go new file mode 100644 index 0000000..a510094 --- /dev/null +++ b/versions/v4.1.3/loader_test.go @@ -0,0 +1,10 @@ +package v4_1_3 + +import ( + "github.com/clarkmcc/go-typescript/versions" + "testing" +) + +func TestRegister(t *testing.T) { + versions.TestSource(t, "v4.1.3", Source) +} diff --git a/versions/v4.1.4/loader.go b/versions/v4.1.4/loader.go index 594e9cd..046a3e7 100644 --- a/versions/v4.1.4/loader.go +++ b/versions/v4.1.4/loader.go @@ -2,12 +2,7 @@ package v4_1_4 import ( _ "embed" - "github.com/clarkmcc/go-typescript/versions" ) //go:embed v4.1.4.js var Source string - -func init() { - versions.DefaultRegistry.MustRegister("v4.1.4", Source) -} diff --git a/versions/v4.1.4/loader_test.go b/versions/v4.1.4/loader_test.go new file mode 100644 index 0000000..491f9b0 --- /dev/null +++ b/versions/v4.1.4/loader_test.go @@ -0,0 +1,10 @@ +package v4_1_4 + +import ( + "github.com/clarkmcc/go-typescript/versions" + "testing" +) + +func TestRegister(t *testing.T) { + versions.TestSource(t, "v4.1.4", Source) +} diff --git a/versions/v4.1.5/loader.go b/versions/v4.1.5/loader.go index 5aa77df..3016d76 100644 --- a/versions/v4.1.5/loader.go +++ b/versions/v4.1.5/loader.go @@ -2,12 +2,7 @@ package v4_1_5 import ( _ "embed" - "github.com/clarkmcc/go-typescript/versions" ) //go:embed v4.1.5.js var Source string - -func init() { - versions.DefaultRegistry.MustRegister("v4.1.5", Source) -} diff --git a/versions/v4.1.5/loader_test.go b/versions/v4.1.5/loader_test.go new file mode 100644 index 0000000..66d1b98 --- /dev/null +++ b/versions/v4.1.5/loader_test.go @@ -0,0 +1,10 @@ +package v4_1_5 + +import ( + "github.com/clarkmcc/go-typescript/versions" + "testing" +) + +func TestRegister(t *testing.T) { + versions.TestSource(t, "v4.1.5", Source) +} diff --git a/versions/v4.2.2/loader.go b/versions/v4.2.2/loader.go index 2b1cd5b..6855128 100644 --- a/versions/v4.2.2/loader.go +++ b/versions/v4.2.2/loader.go @@ -2,12 +2,7 @@ package v4_2_2 import ( _ "embed" - "github.com/clarkmcc/go-typescript/versions" ) //go:embed v4.2.2.js var Source string - -func init() { - versions.DefaultRegistry.MustRegister("v4.2.2", Source) -} diff --git a/versions/v4.2.2/loader_test.go b/versions/v4.2.2/loader_test.go new file mode 100644 index 0000000..b2c1011 --- /dev/null +++ b/versions/v4.2.2/loader_test.go @@ -0,0 +1,10 @@ +package v4_2_2 + +import ( + "github.com/clarkmcc/go-typescript/versions" + "testing" +) + +func TestRegister(t *testing.T) { + versions.TestSource(t, "v4.2.2", Source) +} diff --git a/versions/v4.2.3/loader.go b/versions/v4.2.3/loader.go index 8474446..e2873e1 100644 --- a/versions/v4.2.3/loader.go +++ b/versions/v4.2.3/loader.go @@ -2,12 +2,7 @@ package v4_2_3 import ( _ "embed" - "github.com/clarkmcc/go-typescript/versions" ) //go:embed v4.2.3.js var Source string - -func init() { - versions.DefaultRegistry.MustRegister("v4.2.3", Source) -} diff --git a/versions/v4.2.3/loader_test.go b/versions/v4.2.3/loader_test.go new file mode 100644 index 0000000..af38d13 --- /dev/null +++ b/versions/v4.2.3/loader_test.go @@ -0,0 +1,10 @@ +package v4_2_3 + +import ( + "github.com/clarkmcc/go-typescript/versions" + "testing" +) + +func TestRegister(t *testing.T) { + versions.TestSource(t, "v4.2.3", Source) +} diff --git a/versions/v4.2.4/loader.go b/versions/v4.2.4/loader.go index 9fc0b80..8ed3e94 100644 --- a/versions/v4.2.4/loader.go +++ b/versions/v4.2.4/loader.go @@ -2,12 +2,7 @@ package v4_2_4 import ( _ "embed" - "github.com/clarkmcc/go-typescript/versions" ) //go:embed v4.2.4.js var Source string - -func init() { - versions.DefaultRegistry.MustRegister("v4.2.4", Source) -} diff --git a/versions/v4.2.4/loader_test.go b/versions/v4.2.4/loader_test.go new file mode 100644 index 0000000..bb54301 --- /dev/null +++ b/versions/v4.2.4/loader_test.go @@ -0,0 +1,10 @@ +package v4_2_4 + +import ( + "github.com/clarkmcc/go-typescript/versions" + "testing" +) + +func TestRegister(t *testing.T) { + versions.TestSource(t, "v4.2.4", Source) +} diff --git a/versions/v4.7.2/loader.go b/versions/v4.7.2/loader.go index 847f57b..80ea096 100644 --- a/versions/v4.7.2/loader.go +++ b/versions/v4.7.2/loader.go @@ -2,12 +2,7 @@ package v4_7_2 import ( _ "embed" - "github.com/clarkmcc/go-typescript/versions" ) //go:embed v4.7.2.js var Source string - -func init() { - versions.DefaultRegistry.MustRegister("v4.7.2", Source) -} diff --git a/versions/v4.7.2/loader_test.go b/versions/v4.7.2/loader_test.go new file mode 100644 index 0000000..9f39303 --- /dev/null +++ b/versions/v4.7.2/loader_test.go @@ -0,0 +1,10 @@ +package v4_7_2 + +import ( + "github.com/clarkmcc/go-typescript/versions" + "testing" +) + +func TestRegister(t *testing.T) { + versions.TestSource(t, "v4.7.2", Source) +} diff --git a/versions/v4.9.3/loader.go b/versions/v4.9.3/loader.go index 6e0446b..b0f0426 100644 --- a/versions/v4.9.3/loader.go +++ b/versions/v4.9.3/loader.go @@ -2,12 +2,7 @@ package v4_9_3 import ( _ "embed" - "github.com/clarkmcc/go-typescript/versions" ) //go:embed v4.9.3.js var Source string - -func init() { - versions.DefaultRegistry.MustRegister("v4.9.3", Source) -} diff --git a/versions/v4.9.3/loader_test.go b/versions/v4.9.3/loader_test.go new file mode 100644 index 0000000..07e15c6 --- /dev/null +++ b/versions/v4.9.3/loader_test.go @@ -0,0 +1,10 @@ +package v4_9_3 + +import ( + "github.com/clarkmcc/go-typescript/versions" + "testing" +) + +func TestRegister(t *testing.T) { + versions.TestSource(t, "v4.9.3", Source) +}