Skip to content

Commit 793d7c0

Browse files
authored
Evaluator with amd module support (#5)
* Added AMD-based almond module loader and evaluator that can evaluate scripts and transpile them if needed * Allow transpiler to not use context cancellation (handle cancellation in the runtime) * Added transpiler option for custom typescript source * Removed examples directory and updated readme * Added support for Typescript v4.2.4 * Added support for pre-evaluation script modifier hooks and custom evaluation runtimes Co-authored-by: Clark McCauley <[email protected]>
1 parent a5193e1 commit 793d7c0

File tree

13 files changed

+444
-149
lines changed

13 files changed

+444
-149
lines changed

README.md

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
# Goja Typescript Transpiler
2-
This package provides a simple interface using [github.com/dop251/goja](github.com/dop251/goja) under the hood to allow you to transpile Typescript to Javascript in Go. This package has no direct dependencies besides testing utilities and has a 95% test coverage rate.
1+
# Goja Typescript Transpiler and Evaluator (with AMD module support)
2+
This package provides a simple interface using [github.com/dop251/goja](github.com/dop251/goja) under the hood to allow you to transpile Typescript to Javascript in Go. In addition it provides an evaluator with a built-in AMD module loader which allows you to run Typescript code against a compiled typescript bundle. This package has no direct dependencies besides testing utilities and has a 95% test coverage rate.
33

44
Feel free to contribute. This package is fresh and may experience some changes before it's first tagged release.
55

6-
## Example
7-
For more examples, see the `examples/` directory of this repository
6+
## Transpiling Examples
87
### Transpile Strings
98
```go
109
output, err := typescript.TranspileString("let a: number = 10;", nil)
@@ -19,47 +18,67 @@ output, err := typescript.Transpile(reader, nil)
1918
### Custom Typescript Compile Options
2019
You can optionally specify alternative compiler options that are used by Typescript. Any of the options [https://www.typescriptlang.org/docs/handbook/compiler-options.html](https://www.typescriptlang.org/docs/handbook/compiler-options.html) can be added.
2120
```go
22-
output, err = typescript.TranspileString(script, nil, typescript.WithCompileOptions(map[string]interface{}{
21+
output, err = typescript.TranspileString(script, typescript.WithCompileOptions(map[string]interface{}{
2322
"module": "none",
2423
"strict": true,
2524
}))
2625
```
2726

2827
### Custom Typescript Version
29-
#### Default Registry
3028
You can optionally specify which typescript version you want to compile using. These versions are based on the Git tags from the Typescript repository. If you're using a version that is supported in this package, you'll need to import the version package as a side-effect and will automatically be registered to the default registry.
3129
```go
3230
import _ "github.com/clarkmcc/go-typescript/versions/v4.2.2"
3331

3432
func main() {
35-
output, err := typescript.Transpile(reader, nil, typescript.WithVersion("v4.2.2"))
33+
output, err := typescript.Transpile(reader, typescript.WithVersion("v4.2.2"))
3634
}
3735
```
3836

39-
#### Custom Registry
40-
You may want to use a custom version registry rather than the default registry.
37+
### Custom Typescript Source
38+
You may want to use a custom typescript version.
4139

4240
```go
43-
import version "github.com/clarkmcc/go-typescript/versions/v4.2.2"
44-
4541
func main() {
46-
registry := versions.NewRegistry()
47-
registry.MustRegister("v4.2.3", version.Source)
48-
49-
output, err := typescript.TranspileString("let a:number = 10;", &typescript.Config{
50-
TypescriptSource: program,
51-
})
42+
output, err := typescript.TranspileString("let a:number = 10;",
43+
WithTypescriptSource("/* source code for typescript*/"))
5244
}
5345
```
5446

55-
#### Custom Version
56-
Need a different typescript version than the tags we support in this repo? No problem, you can load your own:
47+
## Evaluate Examples
48+
### Basic Evaluation
49+
You can evaluate pure Javascript code with:
50+
51+
```go
52+
result, err := Evaluate(strings.NewReader('var a = 10;')) // returns 10;
53+
```
54+
55+
### Transpile and Evaluate
56+
Or you can transpile first:
5757

5858
```go
59-
program, err := goja.Compile("typescript", "<typescript source code here>", true)
60-
output, err := typescript.Transpile(reader, &typescript.Config{
61-
CompileOptions: map[string]interface{}{},
62-
TypescriptSource: program,
63-
Runtime: goja.New(),
64-
})
59+
result, err := Evaluate(strings.NewReader('let a: number = 10;'), WithTranspile()) // returns 10;
60+
```
61+
62+
### Run Script with AMD Modules
63+
You can load in an AMD module bundle, then execute a Typescript script with access to the modules.
64+
65+
```go
66+
// This is the module we're going to import
67+
modules := strings.TrimSpace(`
68+
define("myModule", ["exports"], function (exports, core_1) {
69+
Object.defineProperty(exports, "__esModule", { value: true });
70+
exports.multiply = void 0;
71+
var multiply = function (a, b) { return a * b; };
72+
exports.multiply = multiply;
73+
});
74+
`)
75+
76+
// This is the script we're going to transpile and evaluate
77+
script := "import { multiply } from 'myModule'; multiply(5, 5)"
78+
79+
// Returns 25
80+
result, err := EvaluateCtx(context.Background(), strings.NewReader(script),
81+
WithAlmondModuleLoader(),
82+
WithTranspile(),
83+
WithEvaluateBefore(strings.NewReader(amdModuleScript)))
6584
```

config.go

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,29 @@ import (
55
"fmt"
66
"github.com/clarkmcc/go-typescript/utils"
77
"github.com/clarkmcc/go-typescript/versions"
8-
_ "github.com/clarkmcc/go-typescript/versions/v4.2.3"
8+
_ "github.com/clarkmcc/go-typescript/versions/v4.2.4"
99
"github.com/dop251/goja"
1010
)
1111

12-
// OptionFunc allows for easy chaining of pre-built config modifiers such as WithVersion.
13-
type OptionFunc func(*Config)
12+
// TranspileOptionFunc allows for easy chaining of pre-built config modifiers such as WithVersion.
13+
type TranspileOptionFunc func(*Config)
1414

1515
// Config defines the behavior of the typescript compiler.
1616
type Config struct {
1717
CompileOptions map[string]interface{}
1818
TypescriptSource *goja.Program
1919
Runtime *goja.Runtime
20+
21+
// If a module is exported by the typescript compiler, this is the name the module will be called
22+
ModuleName string
23+
24+
// Verbose enables built-in verbose logging for debugging purposes.
25+
Verbose bool
26+
27+
// PreventCancellation indicates that the transpiler should not handle context cancellation. This
28+
// should be used when external runtimes are configured AND cancellation is handled by those runtimes.
29+
PreventCancellation bool
30+
2031
// decoderName refers to a random generated string assigned to a function in the runtimes
2132
// global scope which is analogous to atob(), or a base64 decoding function. This function
2233
// is needed in the transpile process to ensure that we don't have any issues with string
@@ -49,34 +60,59 @@ func NewDefaultConfig() *Config {
4960
return &Config{
5061
Runtime: goja.New(),
5162
CompileOptions: nil,
52-
TypescriptSource: versions.DefaultRegistry.MustGet("v4.2.3"),
63+
TypescriptSource: versions.DefaultRegistry.MustGet("v4.2.4"),
64+
ModuleName: "default",
5365
}
5466
}
5567

5668
// WithVersion loads the provided tagged typescript source from the default registry
57-
func WithVersion(tag string) OptionFunc {
69+
func WithVersion(tag string) TranspileOptionFunc {
5870
return func(config *Config) {
5971
config.TypescriptSource = versions.DefaultRegistry.MustGet(tag)
6072
}
6173
}
6274

75+
// WithTypescriptSource configures a Typescript source from the provided typescript source string which
76+
// is compiled by goja when the config is initialized. This function will panic if the Typescript source
77+
// is invalid.
78+
func WithTypescriptSource(src string) TranspileOptionFunc {
79+
return func(config *Config) {
80+
config.TypescriptSource = goja.MustCompile("", src, true)
81+
}
82+
}
83+
6384
// WithCompileOptions sets the compile options that will be passed to the typescript compiler.
64-
func WithCompileOptions(options map[string]interface{}) OptionFunc {
85+
func WithCompileOptions(options map[string]interface{}) TranspileOptionFunc {
6586
return func(config *Config) {
6687
config.CompileOptions = options
6788
}
6889
}
6990

7091
// WithRuntime allows you to over-ride the default runtime
71-
func WithRuntime(runtime *goja.Runtime) OptionFunc {
92+
func WithRuntime(runtime *goja.Runtime) TranspileOptionFunc {
7293
return func(config *Config) {
7394
config.Runtime = runtime
7495
}
7596
}
7697

98+
// WithModuleName determines the module name applied to the typescript module if applicable. This is only needed to
99+
// customize the module name if the typescript module mode is AMD or SystemJS.
100+
func WithModuleName(name string) TranspileOptionFunc {
101+
return func(config *Config) {
102+
config.ModuleName = name
103+
}
104+
}
105+
106+
// WithPreventCancellation prevents the transpiler runtime from handling its own context cancellation.
107+
func WithPreventCancellation() TranspileOptionFunc {
108+
return func(config *Config) {
109+
config.PreventCancellation = true
110+
}
111+
}
112+
77113
// withFailOnInitialize used to test a config initialization failure. This is not exported because
78114
// it's used only for testing.
79-
func withFailOnInitialize() OptionFunc {
115+
func withFailOnInitialize() TranspileOptionFunc {
80116
return func(config *Config) {
81117
config.failOnInitialize = true
82118
}

config_test.go

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -25,58 +25,47 @@ func TestConfig_Initialize(t *testing.T) {
2525

2626
func TestVersionLoading(t *testing.T) {
2727
t.Run("v3.8.3", func(t *testing.T) {
28-
output, err := TranspileString("let a: number = 10;", &Config{
29-
TypescriptSource: versions.DefaultRegistry.MustGet("v3.8.3"),
30-
})
28+
output, err := TranspileString("let a: number = 10;", WithVersion("v3.8.3"))
3129
require.NoError(t, err)
3230
require.Equal(t, "var a = 10;", output)
3331
})
3432
t.Run("v3.9.9", func(t *testing.T) {
35-
output, err := TranspileString("let a: number = 10;", &Config{
36-
TypescriptSource: versions.DefaultRegistry.MustGet("v3.9.9"),
37-
})
33+
output, err := TranspileString("let a: number = 10;", WithVersion("v3.9.9"))
3834
require.NoError(t, err)
3935
require.Equal(t, "var a = 10;", output)
4036
})
4137
t.Run("v4.1.2", func(t *testing.T) {
42-
output, err := TranspileString("let a: number = 10;", &Config{
43-
TypescriptSource: versions.DefaultRegistry.MustGet("v4.1.2"),
44-
})
38+
output, err := TranspileString("let a: number = 10;", WithVersion("v4.1.2"))
4539
require.NoError(t, err)
4640
require.Equal(t, "var a = 10;", output)
4741
})
4842
t.Run("v4.1.3", func(t *testing.T) {
49-
output, err := TranspileString("let a: number = 10;", &Config{
50-
TypescriptSource: versions.DefaultRegistry.MustGet("v4.1.3"),
51-
})
43+
output, err := TranspileString("let a: number = 10;", WithVersion("v4.1.3"))
5244
require.NoError(t, err)
5345
require.Equal(t, "var a = 10;", output)
5446
})
5547
t.Run("v4.1.4", func(t *testing.T) {
56-
output, err := TranspileString("let a: number = 10;", &Config{
57-
TypescriptSource: versions.DefaultRegistry.MustGet("v4.1.4"),
58-
})
48+
output, err := TranspileString("let a: number = 10;", WithVersion("v4.1.4"))
5949
require.NoError(t, err)
6050
require.Equal(t, "var a = 10;", output)
6151
})
6252
t.Run("v4.1.5", func(t *testing.T) {
63-
output, err := TranspileString("let a: number = 10;", &Config{
64-
TypescriptSource: versions.DefaultRegistry.MustGet("v4.1.5"),
65-
})
53+
output, err := TranspileString("let a: number = 10;", WithVersion("v4.1.5"))
6654
require.NoError(t, err)
6755
require.Equal(t, "var a = 10;", output)
6856
})
6957
t.Run("v4.2.2", func(t *testing.T) {
70-
output, err := TranspileString("let a: number = 10;", &Config{
71-
TypescriptSource: versions.DefaultRegistry.MustGet("v4.2.2"),
72-
})
58+
output, err := TranspileString("let a: number = 10;", WithVersion("v4.2.2"))
7359
require.NoError(t, err)
7460
require.Equal(t, "var a = 10;", output)
7561
})
7662
t.Run("v4.2.3", func(t *testing.T) {
77-
output, err := TranspileString("let a: number = 10;", &Config{
78-
TypescriptSource: versions.DefaultRegistry.MustGet("v4.2.3"),
79-
})
63+
output, err := TranspileString("let a: number = 10;", WithVersion("v4.2.3"))
64+
require.NoError(t, err)
65+
require.Equal(t, "var a = 10;", output)
66+
})
67+
t.Run("v4.2.4", func(t *testing.T) {
68+
output, err := TranspileString("let a: number = 10;", WithVersion("v4.2.4"))
8069
require.NoError(t, err)
8170
require.Equal(t, "var a = 10;", output)
8271
})
@@ -86,9 +75,26 @@ func TestCustomRegistry(t *testing.T) {
8675
registry := versions.NewRegistry()
8776
registry.MustRegister("v4.2.3", v423.Source)
8877

89-
output, err := TranspileString("let a: number = 10;", &Config{
90-
TypescriptSource: registry.MustGet("v4.2.3"),
78+
output, err := TranspileString("let a: number = 10;", func(config *Config) {
79+
config.TypescriptSource = registry.MustGet("v4.2.3")
9180
})
9281
require.NoError(t, err)
9382
require.Equal(t, "var a = 10;", output)
9483
}
84+
85+
func TestWithModuleName(t *testing.T) {
86+
output, err := TranspileString("let a: number = 10;",
87+
WithModuleName("myModuleName"),
88+
WithCompileOptions(map[string]interface{}{
89+
"module": "amd",
90+
}))
91+
require.NoError(t, err)
92+
require.Contains(t, output, "define(\"myModuleName\"")
93+
}
94+
95+
func TestWithTypescriptSource(t *testing.T) {
96+
output, err := TranspileString("let a: number = 10;",
97+
WithTypescriptSource(v423.Source))
98+
require.NoError(t, err)
99+
require.Equal(t, "var a = 10;", output)
100+
}

0 commit comments

Comments
 (0)