github.com/senomas/gqlgen@v0.17.11-0.20220626120754-9aee61b0716a/internal/code/imports.go (about) 1 package code 2 3 import ( 4 "go/build" 5 "go/parser" 6 "go/token" 7 "os" 8 "path/filepath" 9 "regexp" 10 "strings" 11 ) 12 13 var gopaths []string 14 15 func init() { 16 gopaths = filepath.SplitList(build.Default.GOPATH) 17 for i, p := range gopaths { 18 gopaths[i] = filepath.ToSlash(filepath.Join(p, "src")) 19 } 20 } 21 22 // NameForDir manually looks for package stanzas in files located in the given directory. This can be 23 // much faster than having to consult go list, because we already know exactly where to look. 24 func NameForDir(dir string) string { 25 dir, err := filepath.Abs(dir) 26 if err != nil { 27 return SanitizePackageName(filepath.Base(dir)) 28 } 29 files, err := os.ReadDir(dir) 30 if err != nil { 31 return SanitizePackageName(filepath.Base(dir)) 32 } 33 fset := token.NewFileSet() 34 for _, file := range files { 35 if !strings.HasSuffix(strings.ToLower(file.Name()), ".go") { 36 continue 37 } 38 39 filename := filepath.Join(dir, file.Name()) 40 if src, err := parser.ParseFile(fset, filename, nil, parser.PackageClauseOnly); err == nil { 41 return src.Name.Name 42 } 43 } 44 45 return SanitizePackageName(filepath.Base(dir)) 46 } 47 48 type goModuleSearchResult struct { 49 path string 50 goModPath string 51 moduleName string 52 } 53 54 var goModuleRootCache = map[string]goModuleSearchResult{} 55 56 // goModuleRoot returns the root of the current go module if there is a go.mod file in the directory tree 57 // If not, it returns false 58 func goModuleRoot(dir string) (string, bool) { 59 dir, err := filepath.Abs(dir) 60 if err != nil { 61 panic(err) 62 } 63 dir = filepath.ToSlash(dir) 64 65 dirs := []string{dir} 66 result := goModuleSearchResult{} 67 68 for { 69 modDir := dirs[len(dirs)-1] 70 71 if val, ok := goModuleRootCache[dir]; ok { 72 result = val 73 break 74 } 75 76 if content, err := os.ReadFile(filepath.Join(modDir, "go.mod")); err == nil { 77 moduleName := string(modregex.FindSubmatch(content)[1]) 78 result = goModuleSearchResult{ 79 path: moduleName, 80 goModPath: modDir, 81 moduleName: moduleName, 82 } 83 goModuleRootCache[modDir] = result 84 break 85 } 86 87 if modDir == "" || modDir == "." || modDir == "/" || strings.HasSuffix(modDir, "\\") { 88 // Reached the top of the file tree which means go.mod file is not found 89 // Set root folder with a sentinel cache value 90 goModuleRootCache[modDir] = result 91 break 92 } 93 94 dirs = append(dirs, filepath.Dir(modDir)) 95 } 96 97 // create a cache for each path in a tree traversed, except the top one as it is already cached 98 for _, d := range dirs[:len(dirs)-1] { 99 if result.moduleName == "" { 100 // go.mod is not found in the tree, so the same sentinel value fits all the directories in a tree 101 goModuleRootCache[d] = result 102 } else { 103 if relPath, err := filepath.Rel(result.goModPath, d); err != nil { 104 panic(err) 105 } else { 106 path := result.moduleName 107 relPath := filepath.ToSlash(relPath) 108 if !strings.HasSuffix(relPath, "/") { 109 path += "/" 110 } 111 path += relPath 112 113 goModuleRootCache[d] = goModuleSearchResult{ 114 path: path, 115 goModPath: result.goModPath, 116 moduleName: result.moduleName, 117 } 118 } 119 } 120 } 121 122 res := goModuleRootCache[dir] 123 if res.moduleName == "" { 124 return "", false 125 } 126 return res.path, true 127 } 128 129 // ImportPathForDir takes a path and returns a golang import path for the package 130 func ImportPathForDir(dir string) (res string) { 131 dir, err := filepath.Abs(dir) 132 if err != nil { 133 panic(err) 134 } 135 dir = filepath.ToSlash(dir) 136 137 modDir, ok := goModuleRoot(dir) 138 if ok { 139 return modDir 140 } 141 142 for _, gopath := range gopaths { 143 if len(gopath) < len(dir) && strings.EqualFold(gopath, dir[0:len(gopath)]) { 144 return dir[len(gopath)+1:] 145 } 146 } 147 148 return "" 149 } 150 151 var modregex = regexp.MustCompile(`module ([^\s]*)`)