96 lines
2.7 KiB
Go
96 lines
2.7 KiB
Go
package jstest
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"sync"
|
|
"testing"
|
|
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/repo"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
|
|
"github.com/go-json-experiment/json"
|
|
)
|
|
|
|
const loaderScript = `import script from "./script.mjs";
|
|
process.stdout.write(JSON.stringify(await script(...process.argv.slice(2))));`
|
|
|
|
var getNodeExeOnce = sync.OnceValue(func() string {
|
|
const exeName = "node"
|
|
exe, err := exec.LookPath(exeName)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return exe
|
|
})
|
|
|
|
// EvalNodeScript imports a Node.js script that default-exports a single function,
|
|
// calls it with the provided arguments, and unmarshals the JSON-stringified
|
|
// awaited return value into T.
|
|
func EvalNodeScript[T any](t testing.TB, script string, dir string, args ...string) (result T, err error) {
|
|
return evalNodeScript[T](t, script, loaderScript, dir, args...)
|
|
}
|
|
|
|
// EvalNodeScriptWithTS is like EvalNodeScript, but provides the TypeScript
|
|
// library to the script as the first argument.
|
|
func EvalNodeScriptWithTS[T any](t testing.TB, script string, dir string, args ...string) (result T, err error) {
|
|
if dir == "" {
|
|
dir = t.TempDir()
|
|
}
|
|
tsSrc := tspath.NormalizePath(filepath.Join(repo.RootPath, "node_modules/typescript/lib/typescript.js"))
|
|
if tsSrc[0] == '/' {
|
|
tsSrc = "file://" + tsSrc
|
|
} else {
|
|
tsSrc = "file:///" + tsSrc
|
|
}
|
|
tsLoaderScript := fmt.Sprintf(`import script from "./script.mjs";
|
|
import * as ts from "%s";
|
|
process.stdout.write(JSON.stringify(await script(ts, ...process.argv.slice(2))));`, tsSrc)
|
|
return evalNodeScript[T](t, script, tsLoaderScript, dir, args...)
|
|
}
|
|
|
|
func SkipIfNoNodeJS(t testing.TB) {
|
|
t.Helper()
|
|
if getNodeExeOnce() == "" {
|
|
t.Skip("Node.js not found")
|
|
}
|
|
}
|
|
|
|
func evalNodeScript[T any](t testing.TB, script string, loader string, dir string, args ...string) (result T, err error) {
|
|
t.Helper()
|
|
exe := getNodeExe(t)
|
|
scriptPath := dir + "/script.mjs"
|
|
if err = os.WriteFile(scriptPath, []byte(script), 0o644); err != nil {
|
|
return result, err
|
|
}
|
|
loaderPath := dir + "/loader.mjs"
|
|
if err = os.WriteFile(loaderPath, []byte(loader), 0o644); err != nil {
|
|
return result, err
|
|
}
|
|
|
|
execArgs := make([]string, 0, 1+len(args))
|
|
execArgs = append(execArgs, loaderPath)
|
|
execArgs = append(execArgs, args...)
|
|
execCmd := exec.Command(exe, execArgs...)
|
|
execCmd.Dir = dir
|
|
output, err := execCmd.CombinedOutput()
|
|
if err != nil {
|
|
return result, fmt.Errorf("failed to run node: %w\n%s", err, output)
|
|
}
|
|
|
|
if err = json.Unmarshal(output, &result); err != nil {
|
|
return result, fmt.Errorf("failed to unmarshal JSON output: %w", err)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func getNodeExe(t testing.TB) string {
|
|
if exe := getNodeExeOnce(); exe != "" {
|
|
return exe
|
|
}
|
|
t.Fatal("Node.js not found")
|
|
return ""
|
|
}
|