Skip to content

Commit 563e39b

Browse files
authored
feat(wasm): export an autocomplete function (#3148)
1 parent 18bc286 commit 563e39b

File tree

22 files changed

+322
-71
lines changed

22 files changed

+322
-71
lines changed

.github/workflows/wasm.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,7 @@ jobs:
2828
run: ./scripts/build-wasm.sh
2929
- name: Run npm package tests
3030
run: ./scripts/run-tests-wasm.sh
31+
- name: Run custom tester
32+
run: ./scripts/run-tests-wasm.sh
33+
env:
34+
TESTER: true

cmd/scw-wasm-tester/args.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//go:build wasm && js
2+
3+
package main
4+
5+
import (
6+
"os"
7+
)
8+
9+
type Args struct {
10+
callback string
11+
targetObject string
12+
}
13+
14+
func getArgs() Args {
15+
args := Args{}
16+
if len(os.Args) > 0 {
17+
args.callback = os.Args[0]
18+
}
19+
if len(os.Args) > 1 {
20+
args.targetObject = os.Args[1]
21+
}
22+
23+
return args
24+
}

cmd/scw-wasm-tester/main.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//go:build wasm && js
2+
3+
package main
4+
5+
import (
6+
"syscall/js"
7+
8+
"github.com/scaleway/scaleway-cli/v2/internal/jshelpers"
9+
)
10+
11+
type jsFunction func(js.Value, []js.Value) any
12+
13+
var tests = map[string]jsFunction{
14+
"FromSlice": wasmTestFromSlice,
15+
}
16+
17+
func main() {
18+
stopChan := make(chan struct{})
19+
stop := func(_ js.Value, args []js.Value) (any, error) {
20+
stopChan <- struct{}{}
21+
return nil, nil
22+
}
23+
24+
args := getArgs()
25+
26+
if args.targetObject != "" {
27+
cliPackage := js.ValueOf(map[string]any{})
28+
for funcName, testFunc := range tests {
29+
cliPackage.Set(funcName, js.FuncOf(testFunc))
30+
}
31+
cliPackage.Set("stop", js.FuncOf(jshelpers.AsyncJsFunc(stop)))
32+
js.Global().Set(args.targetObject, cliPackage)
33+
}
34+
35+
if args.callback != "" {
36+
givenCallback := js.Global().Get(args.callback)
37+
if !givenCallback.IsUndefined() {
38+
givenCallback.Invoke()
39+
}
40+
}
41+
<-stopChan
42+
}

cmd/scw-wasm-tester/slices.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//go:build wasm && js
2+
3+
package main
4+
5+
import (
6+
"syscall/js"
7+
8+
"github.com/scaleway/scaleway-cli/v2/internal/jshelpers"
9+
)
10+
11+
func wasmTestFromSlice(_ js.Value, _ []js.Value) any {
12+
return jshelpers.FromSlice([]string{"1", "2", "3"})
13+
}

cmd/scw-wasm/main.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,19 @@ import (
1010
)
1111

1212
func main() {
13+
stopChan := make(chan struct{})
14+
stop := func(_ js.Value, args []js.Value) (any, error) {
15+
stopChan <- struct{}{}
16+
return nil, nil
17+
}
18+
1319
args := getArgs()
1420

1521
if args.targetObject != "" {
1622
cliPackage := js.ValueOf(map[string]any{})
17-
cliPackage.Set("run", asyncFunc(jshelpers.AsFunction(wasm.Run)))
23+
cliPackage.Set("run", js.FuncOf(jshelpers.AsPromise(wasm.Run)))
24+
cliPackage.Set("complete", js.FuncOf(jshelpers.AsPromise(wasm.Autocomplete)))
25+
cliPackage.Set("stop", js.FuncOf(jshelpers.AsyncJsFunc(stop)))
1826
js.Global().Set(args.targetObject, cliPackage)
1927
}
2028

@@ -24,5 +32,5 @@ func main() {
2432
givenCallback.Invoke()
2533
}
2634
}
27-
<-make(chan struct{})
35+
<-stopChan
2836
}

internal/core/wasm.go

Lines changed: 0 additions & 5 deletions
This file was deleted.

internal/jshelpers/arrays.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ var (
1313
)
1414

1515
func asSlice(typ reflect.Type, value js.Value) (any, error) {
16+
if !value.InstanceOf(jsArray) {
17+
return nil, fmt.Errorf("value type should be Array")
18+
}
19+
1620
l := value.Length()
1721

1822
slice := reflect.MakeSlice(reflect.SliceOf(typ), l, l)
@@ -32,14 +36,22 @@ func asSlice(typ reflect.Type, value js.Value) (any, error) {
3236
func AsSlice[T any](value js.Value) ([]T, error) {
3337
var t T
3438

35-
if !value.InstanceOf(jsArray) {
36-
return nil, fmt.Errorf("value type should be Array")
37-
}
38-
3939
slice, err := asSlice(reflect.TypeOf(t), value)
4040
if err != nil {
4141
return nil, err
4242
}
4343

4444
return slice.([]T), nil
4545
}
46+
47+
// FromSlice converts a Go slice to a JS Array
48+
func FromSlice(from any) js.Value {
49+
fromValue := reflect.ValueOf(from)
50+
51+
arrayItems := make([]any, fromValue.Len())
52+
for i := 0; i < len(arrayItems); i++ {
53+
arrayItems[i] = jsValue(fromValue.Index(i).Interface())
54+
}
55+
56+
return jsArray.New(arrayItems...)
57+
}
Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,30 @@
1-
//go:build wasm && js
1+
//go:build js
22

3-
package main
3+
package jshelpers
44

55
import (
66
"fmt"
77
"runtime/debug"
88
"syscall/js"
9-
10-
"github.com/scaleway/scaleway-cli/v2/internal/jshelpers"
11-
)
12-
13-
type fn func(this js.Value, args []js.Value) (any, error)
14-
15-
var (
16-
jsErr = js.Global().Get("Error")
17-
jsPromise = js.Global().Get("Promise")
189
)
1910

20-
func asyncFunc(innerFunc fn) js.Func {
21-
return js.FuncOf(func(this js.Value, args []js.Value) any {
11+
func AsyncJsFunc(innerFunc JsFuncWithError) JsFunc {
12+
return func(this js.Value, args []js.Value) any {
2213
handler := js.FuncOf(func(_ js.Value, promFn []js.Value) any {
2314
resolve, reject := promFn[0], promFn[1]
2415

2516
go func() {
2617
defer func() {
2718
if r := recover(); r != nil {
28-
reject.Invoke(jshelpers.NewError(
19+
reject.Invoke(NewError(
2920
fmt.Sprintf("panic: %v\n%s", r, string(debug.Stack())),
3021
))
3122
}
3223
}()
3324

3425
res, err := innerFunc(this, args)
3526
if err != nil {
36-
reject.Invoke(jshelpers.NewError(err.Error()))
27+
reject.Invoke(NewError(err.Error()))
3728
} else {
3829
resolve.Invoke(res)
3930
}
@@ -43,5 +34,5 @@ func asyncFunc(innerFunc fn) js.Func {
4334
})
4435

4536
return jsPromise.New(handler)
46-
})
37+
}
4738
}

internal/jshelpers/errors.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,6 @@ package jshelpers
44

55
import "syscall/js"
66

7-
var (
8-
jsObject = js.Global().Get("Object")
9-
jsErr = js.Global().Get("Error")
10-
)
11-
127
func NewError(msg any) js.Value {
138
return jsErr.New(msg)
149
}

internal/jshelpers/function.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ func jsValue(val any) js.Value {
2121
switch valType.Kind() {
2222
case reflect.Struct:
2323
return FromObject(val)
24+
case reflect.Slice:
25+
return FromSlice(val)
2426
}
27+
2528
return js.ValueOf(val)
2629
}
2730

@@ -32,10 +35,10 @@ func errValue(val any) error {
3235
return val.(error)
3336
}
3437

35-
// AsFunction convert a classic Go function to a function taking js arguments.
38+
// AsPromise convert a classic Go function to a function taking js arguments.
3639
// arguments and return types must be types handled by this package
3740
// function must return 2 variables, second one must be an error
38-
func AsFunction(goFunc any) func(this js.Value, args []js.Value) (any, error) {
41+
func AsPromise(goFunc any) JsFunc {
3942
goFuncValue := reflect.ValueOf(goFunc)
4043
goFuncType := goFuncValue.Type()
4144

@@ -51,7 +54,7 @@ func AsFunction(goFunc any) func(this js.Value, args []js.Value) (any, error) {
5154
panic("function must return an error")
5255
}
5356

54-
return func(this js.Value, args []js.Value) (any, error) {
57+
return AsyncJsFunc(func(this js.Value, args []js.Value) (any, error) {
5558
if len(args) != len(goFuncArgs) {
5659
return nil, fmt.Errorf("invalid number of arguments, expected %d, got %d", len(goFuncArgs), len(args))
5760
}
@@ -60,13 +63,13 @@ func AsFunction(goFunc any) func(this js.Value, args []js.Value) (any, error) {
6063
for i, argType := range goFuncArgs {
6164
arg, err := goValue(argType, args[i])
6265
if err != nil {
63-
return nil, fmt.Errorf("invalid argument at index %d with type %s: %w", i, argType.String(), err)
66+
return nil, fmt.Errorf("invalid argument at index %d, expected type %s: %w", i, argType.String(), err)
6467
}
6568
argValues[i] = reflect.ValueOf(arg)
6669
}
6770

6871
returnValues := goFuncValue.Call(argValues)
6972

7073
return jsValue(returnValues[0].Interface()), errValue(returnValues[1].Interface())
71-
}
74+
})
7275
}

0 commit comments

Comments
 (0)