github.com/kosmosJS/engine-node@v0.0.0-20220426040216-d53e2a72192e/require/resolve.go (about)

     1  package require
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"path"
     7  	"strings"
     8  
     9  	js "github.com/kosmosJS/engine"
    10  )
    11  
    12  // NodeJS module search algorithm described by
    13  // https://nodejs.org/api/modules.html#modules_all_together
    14  func (r *RequireModule) resolve(modpath string) (module *js.Object, err error) {
    15  	origPath, modpath := modpath, filepathClean(modpath)
    16  	if modpath == "" {
    17  		return nil, IllegalModuleNameError
    18  	}
    19  
    20  	module, err = r.loadNative(modpath)
    21  	if err == nil {
    22  		return
    23  	}
    24  
    25  	var start string
    26  	err = nil
    27  	if path.IsAbs(origPath) {
    28  		start = "/"
    29  	} else {
    30  		start = r.getCurrentModulePath()
    31  	}
    32  
    33  	p := path.Join(start, modpath)
    34  	if strings.HasPrefix(origPath, "./") ||
    35  		strings.HasPrefix(origPath, "/") || strings.HasPrefix(origPath, "../") ||
    36  		origPath == "." || origPath == ".." {
    37  		if module = r.modules[p]; module != nil {
    38  			return
    39  		}
    40  		module, err = r.loadAsFileOrDirectory(p)
    41  		if err == nil && module != nil {
    42  			r.modules[p] = module
    43  		}
    44  	} else {
    45  		if module = r.nodeModules[p]; module != nil {
    46  			return
    47  		}
    48  		module, err = r.loadNodeModules(modpath, start)
    49  		if err == nil && module != nil {
    50  			r.nodeModules[p] = module
    51  		}
    52  	}
    53  
    54  	if module == nil && err == nil {
    55  		err = InvalidModuleError
    56  	}
    57  	return
    58  }
    59  
    60  func (r *RequireModule) loadNative(path string) (*js.Object, error) {
    61  	module := r.modules[path]
    62  	if module != nil {
    63  		return module, nil
    64  	}
    65  
    66  	var ldr ModuleLoader
    67  	if ldr = r.r.native[path]; ldr == nil {
    68  		ldr = native[path]
    69  	}
    70  
    71  	if ldr != nil {
    72  		module = r.createModuleObject()
    73  		r.modules[path] = module
    74  		ldr(r.runtime, module)
    75  		return module, nil
    76  	}
    77  
    78  	return nil, InvalidModuleError
    79  }
    80  
    81  func (r *RequireModule) loadAsFileOrDirectory(path string) (module *js.Object, err error) {
    82  	if module, err = r.loadAsFile(path); module != nil || err != nil {
    83  		return
    84  	}
    85  
    86  	return r.loadAsDirectory(path)
    87  }
    88  
    89  func (r *RequireModule) loadAsFile(path string) (module *js.Object, err error) {
    90  	if module, err = r.loadModule(path); module != nil || err != nil {
    91  		return
    92  	}
    93  
    94  	p := path + ".js"
    95  	if module, err = r.loadModule(p); module != nil || err != nil {
    96  		return
    97  	}
    98  
    99  	p = path + ".json"
   100  	return r.loadModule(p)
   101  }
   102  
   103  func (r *RequireModule) loadIndex(modpath string) (module *js.Object, err error) {
   104  	p := path.Join(modpath, "index.js")
   105  	if module, err = r.loadModule(p); module != nil || err != nil {
   106  		return
   107  	}
   108  
   109  	p = path.Join(modpath, "index.json")
   110  	return r.loadModule(p)
   111  }
   112  
   113  func (r *RequireModule) loadAsDirectory(modpath string) (module *js.Object, err error) {
   114  	p := path.Join(modpath, "package.json")
   115  	buf, err := r.r.getSource(p)
   116  	if err != nil {
   117  		return r.loadIndex(modpath)
   118  	}
   119  	var pkg struct {
   120  		Main string
   121  	}
   122  	err = json.Unmarshal(buf, &pkg)
   123  	if err != nil || len(pkg.Main) == 0 {
   124  		return r.loadIndex(modpath)
   125  	}
   126  
   127  	m := path.Join(modpath, pkg.Main)
   128  	if module, err = r.loadAsFile(m); module != nil || err != nil {
   129  		return
   130  	}
   131  
   132  	return r.loadIndex(m)
   133  }
   134  
   135  func (r *RequireModule) loadNodeModule(modpath, start string) (*js.Object, error) {
   136  	return r.loadAsFileOrDirectory(path.Join(start, modpath))
   137  }
   138  
   139  func (r *RequireModule) loadNodeModules(modpath, start string) (module *js.Object, err error) {
   140  	for _, dir := range r.r.globalFolders {
   141  		if module, err = r.loadNodeModule(modpath, dir); module != nil || err != nil {
   142  			return
   143  		}
   144  	}
   145  	for {
   146  		var p string
   147  		if path.Base(start) != "node_modules" {
   148  			p = path.Join(start, "node_modules")
   149  		} else {
   150  			p = start
   151  		}
   152  		if module, err = r.loadNodeModule(modpath, p); module != nil || err != nil {
   153  			return
   154  		}
   155  		if start == ".." { // Dir('..') is '.'
   156  			break
   157  		}
   158  		parent := path.Dir(start)
   159  		if parent == start {
   160  			break
   161  		}
   162  		start = parent
   163  	}
   164  
   165  	return nil, InvalidModuleError
   166  }
   167  
   168  func (r *RequireModule) getCurrentModulePath() string {
   169  	var buf [2]js.StackFrame
   170  	frames := r.runtime.CaptureCallStack(2, buf[:0])
   171  	if len(frames) < 2 {
   172  		return "."
   173  	}
   174  	return path.Dir(frames[1].SrcName())
   175  }
   176  
   177  func (r *RequireModule) createModuleObject() *js.Object {
   178  	module := r.runtime.NewObject()
   179  	module.Set("exports", r.runtime.NewObject())
   180  	return module
   181  }
   182  
   183  func (r *RequireModule) loadModule(path string) (*js.Object, error) {
   184  	module := r.modules[path]
   185  	if module == nil {
   186  		module = r.createModuleObject()
   187  		r.modules[path] = module
   188  		err := r.loadModuleFile(path, module)
   189  		if err != nil {
   190  			module = nil
   191  			delete(r.modules, path)
   192  			if errors.Is(err, ModuleFileDoesNotExistError) {
   193  				err = nil
   194  			}
   195  		}
   196  		return module, err
   197  	}
   198  	return module, nil
   199  }
   200  
   201  func (r *RequireModule) loadModuleFile(path string, jsModule *js.Object) error {
   202  
   203  	prg, err := r.r.getCompiledSource(path)
   204  
   205  	if err != nil {
   206  		return err
   207  	}
   208  
   209  	f, err := r.runtime.RunProgram(prg)
   210  	if err != nil {
   211  		return err
   212  	}
   213  
   214  	if call, ok := js.AssertFunction(f); ok {
   215  		jsExports := jsModule.Get("exports")
   216  		jsRequire := r.runtime.Get("require")
   217  
   218  		// Run the module source, with "jsExports" as "this",
   219  		// "jsExports" as the "exports" variable, "jsRequire"
   220  		// as the "require" variable and "jsModule" as the
   221  		// "module" variable (Nodejs capable).
   222  		_, err = call(jsExports, jsExports, jsRequire, jsModule)
   223  		if err != nil {
   224  			return err
   225  		}
   226  	} else {
   227  		return InvalidModuleError
   228  	}
   229  
   230  	return nil
   231  }