github.com/jinhy/goja_nodejs@v0.0.0-20220718154249-8c961d4ddad3/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 // 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 }