github.com/singlemusic/buffalo@v0.16.30/buffalo/cmd/fix/imports.go (about) 1 package fix 2 3 import ( 4 "fmt" 5 "go/ast" 6 "go/parser" 7 "go/token" 8 "os" 9 "path/filepath" 10 "strconv" 11 "strings" 12 13 "golang.org/x/tools/go/ast/astutil" 14 ) 15 16 // ImportConverter will changes imports from a -> b 17 type ImportConverter struct { 18 Data map[string]string 19 } 20 21 // Process will walk all the .go files in an application, excluding ./vendor. 22 // It will then attempt to convert any old import paths to any new import paths 23 // used by this version Buffalo. 24 func (c ImportConverter) Process(r *Runner) error { 25 fmt.Println("~~~ Rewriting Imports ~~~") 26 27 return filepath.Walk(".", c.processFile) 28 29 } 30 31 func (c ImportConverter) processFile(p string, info os.FileInfo, err error) error { 32 er := onlyRelevantFiles(p, info, err, func(p string) error { 33 return c.rewriteFile(p) 34 }) 35 36 return er 37 } 38 39 func (c ImportConverter) rewriteFile(name string) error { 40 41 // create an empty fileset. 42 fset := token.NewFileSet() 43 44 // parse the .go file. 45 // we are parsing the entire file with comments, so we don't lose anything 46 // if we need to write it back out. 47 f, err := parser.ParseFile(fset, name, nil, parser.ParseComments) 48 if err != nil { 49 e := err.Error() 50 msg := "expected 'package', found 'EOF'" 51 if e[len(e)-len(msg):] == msg { 52 return nil 53 } 54 return err 55 } 56 57 changed := false 58 for key, value := range c.Data { 59 if !astutil.DeleteImport(fset, f, key) { 60 continue 61 } 62 63 astutil.AddImport(fset, f, value) 64 changed = true 65 } 66 67 commentsChanged, err := c.handleFileComments(f) 68 if err != nil { 69 return err 70 } 71 72 changed = changed || commentsChanged 73 74 // if no change occurred, then we don't need to write to disk, just return. 75 if !changed { 76 return nil 77 } 78 79 // since the imports changed, resort them. 80 ast.SortImports(fset, f) 81 82 // create a temporary file, this easily avoids conflicts. 83 temp, err := writeTempResult(name, fset, f) 84 if err != nil { 85 return err 86 } 87 88 // rename the .temp to .go 89 return os.Rename(temp, name) 90 } 91 92 func (c ImportConverter) handleFileComments(f *ast.File) (bool, error) { 93 change := false 94 95 for _, cg := range f.Comments { 96 for _, cl := range cg.List { 97 if !strings.HasPrefix(cl.Text, "// import \"") { 98 continue 99 } 100 101 // trim off extra comment stuff 102 ctext := cl.Text 103 ctext = strings.TrimPrefix(ctext, "// import") 104 ctext = strings.TrimSpace(ctext) 105 106 // unquote the comment import path value 107 ctext, err := strconv.Unquote(ctext) 108 if err != nil { 109 return false, err 110 } 111 112 // match the comment import path with the given replacement map 113 if ctext, ok := c.match(ctext); ok { 114 cl.Text = "// import " + strconv.Quote(ctext) 115 change = true 116 } 117 118 } 119 } 120 121 return change, nil 122 } 123 124 // match takes an import path and replacement map. 125 func (c ImportConverter) match(importpath string) (string, bool) { 126 for key, value := range c.Data { 127 if !strings.HasPrefix(importpath, key) { 128 continue 129 } 130 131 result := strings.Replace(importpath, key, value, 1) 132 return result, true 133 } 134 135 return importpath, false 136 }