github.com/fortexxx/gqlgen@v0.10.3-0.20191216030626-ca5ea8b21ead/internal/code/imports.go (about) 1 package code 2 3 import ( 4 "errors" 5 "go/build" 6 "go/parser" 7 "go/token" 8 "io/ioutil" 9 "path/filepath" 10 "regexp" 11 "strings" 12 "sync" 13 14 "golang.org/x/tools/go/packages" 15 ) 16 17 var nameForPackageCache = sync.Map{} 18 19 var gopaths []string 20 21 func init() { 22 gopaths = filepath.SplitList(build.Default.GOPATH) 23 for i, p := range gopaths { 24 gopaths[i] = filepath.ToSlash(filepath.Join(p, "src")) 25 } 26 } 27 28 // NameForDir manually looks for package stanzas in files located in the given directory. This can be 29 // much faster than having to consult go list, because we already know exactly where to look. 30 func NameForDir(dir string) string { 31 dir, err := filepath.Abs(dir) 32 if err != nil { 33 return SanitizePackageName(filepath.Base(dir)) 34 } 35 files, err := ioutil.ReadDir(dir) 36 if err != nil { 37 return SanitizePackageName(filepath.Base(dir)) 38 } 39 fset := token.NewFileSet() 40 for _, file := range files { 41 if !strings.HasSuffix(strings.ToLower(file.Name()), ".go") { 42 continue 43 } 44 45 filename := filepath.Join(dir, file.Name()) 46 if src, err := parser.ParseFile(fset, filename, nil, parser.PackageClauseOnly); err == nil { 47 return src.Name.Name 48 } 49 } 50 51 return SanitizePackageName(filepath.Base(dir)) 52 } 53 54 // goModuleRoot returns the root of the current go module if there is a go.mod file in the directory tree 55 // If not, it returns false 56 func goModuleRoot(dir string) (string, bool) { 57 dir, err := filepath.Abs(dir) 58 if err != nil { 59 panic(err) 60 } 61 dir = filepath.ToSlash(dir) 62 modDir := dir 63 assumedPart := "" 64 for { 65 f, err := ioutil.ReadFile(filepath.Join(modDir, "go.mod")) 66 if err == nil { 67 // found it, stop searching 68 return string(modregex.FindSubmatch(f)[1]) + assumedPart, true 69 } 70 71 assumedPart = "/" + filepath.Base(modDir) + assumedPart 72 parentDir, err := filepath.Abs(filepath.Join(modDir, "..")) 73 if err != nil { 74 panic(err) 75 } 76 77 if parentDir == modDir { 78 // Walked all the way to the root and didnt find anything :'( 79 break 80 } 81 modDir = parentDir 82 } 83 return "", false 84 } 85 86 // ImportPathForDir takes a path and returns a golang import path for the package 87 func ImportPathForDir(dir string) (res string) { 88 dir, err := filepath.Abs(dir) 89 if err != nil { 90 panic(err) 91 } 92 dir = filepath.ToSlash(dir) 93 94 modDir, ok := goModuleRoot(dir) 95 if ok { 96 return modDir 97 } 98 99 for _, gopath := range gopaths { 100 if len(gopath) < len(dir) && strings.EqualFold(gopath, dir[0:len(gopath)]) { 101 return dir[len(gopath)+1:] 102 } 103 } 104 105 return "" 106 } 107 108 var modregex = regexp.MustCompile("module (.*)\n") 109 110 // NameForPackage returns the package name for a given import path. This can be really slow. 111 func NameForPackage(importPath string) string { 112 if importPath == "" { 113 panic(errors.New("import path can not be empty")) 114 } 115 if v, ok := nameForPackageCache.Load(importPath); ok { 116 return v.(string) 117 } 118 importPath = QualifyPackagePath(importPath) 119 p, _ := packages.Load(&packages.Config{ 120 Mode: packages.NeedName, 121 }, importPath) 122 123 if len(p) != 1 || p[0].Name == "" { 124 return SanitizePackageName(filepath.Base(importPath)) 125 } 126 127 nameForPackageCache.Store(importPath, p[0].Name) 128 129 return p[0].Name 130 }