github.com/mariotoffia/goja_nodejs@v0.0.0-20221208130254-70b39c043ea9/require/resolve.go (about)

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