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 }