github.com/jacobsoderblom/buffalo@v0.11.0/buffalo/cmd/updater/imports.go (about) 1 package updater 2 3 import ( 4 "bytes" 5 "fmt" 6 "go/ast" 7 "go/parser" 8 "go/printer" 9 "go/token" 10 "io/ioutil" 11 "os" 12 "path" 13 "path/filepath" 14 "strconv" 15 "strings" 16 17 "github.com/pkg/errors" 18 ) 19 20 // ImportConverter will changes imports from a -> b 21 type ImportConverter struct { 22 Data map[string]string 23 } 24 25 // Process will walk all the .go files in an application, excluding ./vendor. 26 // It will then attempt to convert any old import paths to any new import paths 27 // used by this version Buffalo. 28 func (c ImportConverter) Process(r *Runner) error { 29 fmt.Println("~~~ Rewriting Imports ~~~") 30 err := filepath.Walk(".", func(p string, info os.FileInfo, err error) error { 31 for _, n := range []string{"vendor", "node_modules", ".git"} { 32 if strings.HasPrefix(p, n+string(filepath.Separator)) { 33 return nil 34 } 35 } 36 if info.IsDir() { 37 return nil 38 } 39 ext := filepath.Ext(p) 40 if ext == ".go" { 41 if err := c.rewriteFile(p); err != nil { 42 return errors.WithStack(err) 43 } 44 } 45 return nil 46 }) 47 if err != nil { 48 return errors.WithStack(err) 49 } 50 51 if r.App.WithDep { 52 b, err := ioutil.ReadFile("Gopkg.toml") 53 if err != nil { 54 return errors.WithStack(err) 55 } 56 for k := range c.Data { 57 if bytes.Contains(b, []byte(k)) { 58 r.Warnings = append(r.Warnings, fmt.Sprintf("Your Gopkg.toml contains the following import that need to be changed MANUALLY: %s", k)) 59 } 60 } 61 } 62 return nil 63 } 64 65 // TAKEN FROM https://gist.github.com/jackspirou/61ce33574e9f411b8b4a 66 // rewriteFile rewrites import statments in the named file 67 // according to the rules supplied by the map of strings. 68 func (c ImportConverter) rewriteFile(name string) error { 69 70 // create an empty fileset. 71 fset := token.NewFileSet() 72 73 // parse the .go file. 74 // we are parsing the entire file with comments, so we don't lose anything 75 // if we need to write it back out. 76 f, err := parser.ParseFile(fset, name, nil, parser.ParseComments) 77 if err != nil { 78 e := err.Error() 79 msg := "expected 'package', found 'EOF'" 80 if e[len(e)-len(msg):] == msg { 81 return nil 82 } 83 return err 84 } 85 86 // iterate through the import paths. if a change occurs update bool. 87 change := false 88 for _, i := range f.Imports { 89 90 // unquote the import path value. 91 path, err := strconv.Unquote(i.Path.Value) 92 if err != nil { 93 return err 94 } 95 96 // match import path with the given replacement map 97 if rpath, ok := c.match(path); ok { 98 fmt.Printf("[IMPORT] %s: %s -> %s\n", name, path, rpath) 99 i.Path.Value = strconv.Quote(rpath) 100 change = true 101 } 102 } 103 104 for _, cg := range f.Comments { 105 for _, cl := range cg.List { 106 if strings.HasPrefix(cl.Text, "// import \"") { 107 108 // trim off extra comment stuff 109 ctext := cl.Text 110 ctext = strings.TrimPrefix(ctext, "// import") 111 ctext = strings.TrimSpace(ctext) 112 113 // unquote the comment import path value 114 ctext, err := strconv.Unquote(ctext) 115 if err != nil { 116 return err 117 } 118 119 // match the comment import path with the given replacement map 120 if ctext, ok := c.match(ctext); ok { 121 cl.Text = "// import " + strconv.Quote(ctext) 122 change = true 123 } 124 } 125 } 126 } 127 128 // if no change occured, then we don't need to write to disk, just return. 129 if !change { 130 return nil 131 } 132 133 // since the imports changed, resort them. 134 ast.SortImports(fset, f) 135 136 // create a temporary file, this easily avoids conflicts. 137 temp := name + ".temp" 138 w, err := os.Create(temp) 139 if err != nil { 140 return err 141 } 142 143 // write changes to .temp file, and include proper formatting. 144 err = (&printer.Config{Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8}).Fprint(w, fset, f) 145 if err != nil { 146 return err 147 } 148 149 // close the writer 150 err = w.Close() 151 if err != nil { 152 return err 153 } 154 155 // rename the .temp to .go 156 return os.Rename(temp, name) 157 } 158 159 // match takes an import path and replacement map. 160 func (c ImportConverter) match(importpath string) (string, bool) { 161 for key, value := range c.Data { 162 if len(importpath) >= len(key) { 163 if importpath[:len(key)] == key { 164 result := path.Join(value, importpath[len(key):]) 165 return result, true 166 } 167 } 168 } 169 return importpath, false 170 }