resolve.go 5.9 KB

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