imply/loader.go

101 lines
2.4 KiB
Go

package imply
import (
"fmt"
"os"
js "github.com/dop251/goja"
)
func moduleTemplate(c string) string {
return "(function(module, exports) {\n" + c + "\n})"
}
func (c *Core) createPack() (*js.Object, error) {
m := c.NewObject()
e := c.NewObject()
err := m.Set("exports", e)
return m, err
}
func load(name string, code []byte) (*js.Program, error) {
text := moduleTemplate(string(code))
return js.Compile(name, text, false)
}
func (c *Core) validate(prg *js.Program) (jsExports js.Value, err error) {
var f js.Value
f, err = c.RunProgram(prg)
if err != nil {
return
}
g, ok := js.AssertFunction(f)
if !ok {
err = fmt.Errorf("[%T] %v is not a function", f, f)
return
}
pkg, err := c.createPack()
if err != nil {
return
}
jsExports = pkg.Get("exports")
_, err = g(jsExports, pkg, jsExports)
if err != nil {
return
}
// fmt.Printf("VALIDATED: [%T] %v\n", jsExports, spew.Sprint(jsExports))
return
}
func (c *Core) checkExisting(p string) (js.Value, bool) {
c.RLock()
defer c.RUnlock()
val, ok := c.Pkg[p]
return val, ok
}
func (c *Core) registerModule(p string, jsExports js.Value) {
c.Lock()
c.Pkg[p] = jsExports
c.Unlock()
}
func (c *Core) validateAndRegister(name string, prg *js.Program) (jsExports js.Value, err error) {
if jsExports, err = c.validate(prg); err != nil {
return nil, err
}
if jsExports == nil {
return nil, fmt.Errorf("module %s does not export anything", name)
}
c.registerModule(name, jsExports)
return jsExports, nil
}
// LoadJSPackageFile loads a javascript package from a file, validates it, and registers it.
// The given path will be used as the name of the module.
//
// Warning: If name is already registered, the existing module will be returned.
func (c *Core) LoadJSPackageFile(path string) (js.Value, error) {
code, err := os.ReadFile(path)
if err != nil {
// println("!!ERR!! " + err.Error())
return nil, err
}
// println("new: " + path)
return c.LoadJSPackage(path, code)
}
// LoadJSPackage loads a javascript package from code, validates it, and registers it by name.
//
// Warning: If name is already registered, the existing module will be returned.
func (c *Core) LoadJSPackage(name string, code []byte) (js.Value, error) {
if mod, ok := c.checkExisting(name); ok {
return mod, nil
}
prg, err := load(name, code)
if err != nil {
return nil, err
}
return c.validateAndRegister(name, prg)
}