├── .overcommit.yml ├── LICENSE ├── README.md ├── TODO ├── importer.go_bak ├── main.go └── rewrite └── rewrite.go /.overcommit.yml: -------------------------------------------------------------------------------- 1 | # Use this file to configure the Overcommit hooks you wish to use. This will 2 | # extend the default configuration defined in: 3 | # https://github.com/brigade/overcommit/blob/master/config/default.yml 4 | # 5 | # At the topmost level of this YAML file is a key representing type of hook 6 | # being run (e.g. pre-commit, commit-msg, etc.). Within each type you can 7 | # customize each hook, such as whether to only run it on certain files (via 8 | # `include`), whether to only display output if it fails (via `quiet`), etc. 9 | # 10 | # For a complete list of hooks, see: 11 | # https://github.com/brigade/overcommit/tree/master/lib/overcommit/hook 12 | # 13 | # For a complete list of options that you can use to customize hooks, see: 14 | # https://github.com/brigade/overcommit#configuration 15 | # 16 | # Uncomment the following lines to make the configuration take effect. 17 | 18 | #PreCommit: 19 | # RuboCop: 20 | # enabled: true 21 | # on_warn: fail # Treat all warnings as failures 22 | # 23 | # TrailingWhitespace: 24 | # exclude: 25 | # - '**/db/structure.sql' # Ignore trailing whitespace in generated files 26 | # 27 | #PostCheckout: 28 | # ALL: # Special hook name that customizes all hooks of this type 29 | # quiet: true # Change all post-checkout hooks to only display output on failure 30 | # 31 | # IndexTags: 32 | # enabled: true # Generate a tags file with `ctags` each time HEAD changes 33 | PreCommit: 34 | GoLint: 35 | enabled: true 36 | GoVet: 37 | enabled: true 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Jeromy Johnson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gx-js-tool 2 | 3 | A tool to use with the gx package manager for packages written in javascript. 4 | 5 | ## Usage: 6 | ``` 7 | NAME: 8 | gx-js - gx extensions for javascript 9 | 10 | USAGE: 11 | gx-js [global options] command [command options] [arguments...] 12 | 13 | VERSION: 14 | 0.2.0 15 | 16 | AUTHOR(S): 17 | whyrusleeping 18 | sterpe 19 | 20 | COMMANDS: 21 | update update a packages imports to a new path 22 | import import a javascript package and all its depencies into gx 23 | path prints the import path of the current package within GOPATH 24 | hook javascript specific hooks to be called by the gx tool 25 | help, h Shows a list of commands or help for one command 26 | 27 | GLOBAL OPTIONS: 28 | --help, -h show help 29 | --version, -v print the version 30 | ``` 31 | 32 | ## Intro 33 | Using gx as a javascript vendoring tool and package manager is (or at least, should be) a 34 | very simple process. 35 | 36 | ### Creating a new package 37 | In the directory of your javascript package, just run: 38 | ``` 39 | gx init --lang=js 40 | ``` 41 | 42 | And gx will create a new `package.json` for you with some basic information 43 | filled out. From there, all you *have* to do is run `gx publish` (ensure you 44 | have a running ipfs daemon) and gx will give you a package hash. That works 45 | fine for the base case, but to work even more nicely with javascript, we recommend 46 | setting the import path of your package in your `package.json`, like so: 47 | 48 | package.json 49 | ```json 50 | { 51 | ... 52 | "gx":{ 53 | "dvcsimport":"github.com/sterpe/gx-js" 54 | } 55 | } 56 | ``` 57 | 58 | ### Importing an existing package 59 | Importing an existing javascript package from gx is easy, just grab its hash from 60 | somewhere, and run: 61 | ``` 62 | gx import 63 | ``` 64 | 65 | If the package you are importing has its dvcs import path set as shown above, 66 | gx will ask if you want to rewrite your import paths with the new gx path. 67 | If you say no to this (as is the default), you can rewrite the paths at any time 68 | by running `gx-js rewrite`. 69 | 70 | ### Some notes on publishing 71 | It is recommended that when you publish, your import paths are *not* rewritten. 72 | The gx-js post install hook will fix that after the install, but for 'same package' 73 | imports, it works best to have gx rewrite things after the fact (Its also sometimes 74 | nicer for development). You can change paths back from their gx paths with: 75 | ``` 76 | gx-js rewrite --undo 77 | ``` 78 | 79 | ## NOTE: 80 | It is highly recommended that you set your `GOPATH` to a temporary directory when running import. 81 | This ensures that your current javascript packages are not affected, and also that fresh versions of 82 | the packages in question are pulled down. 83 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | - 'working set' command to copy deps from global root to somewhere local (`vendor` dir) 2 | - this way we can easily hack on our deps without disturbing any other packages 3 | -------------------------------------------------------------------------------- /importer.go_bak: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "go/build" 6 | "io/ioutil" 7 | "os" 8 | "os/exec" 9 | "path" 10 | "path/filepath" 11 | "strings" 12 | 13 | rw "github.com/whyrusleeping/gx-go/rewrite" 14 | gx "github.com/whyrusleeping/gx/gxutil" 15 | . "github.com/whyrusleeping/stump" 16 | ) 17 | 18 | func doUpdate(dir, oldimp, newimp string) error { 19 | rwf := func(in string) string { 20 | if in == oldimp { 21 | return newimp 22 | } 23 | 24 | if strings.HasPrefix(in, oldimp+"/") { 25 | return strings.Replace(in, oldimp, newimp, 1) 26 | } 27 | 28 | return in 29 | } 30 | 31 | filter := func(in string) bool { 32 | return strings.HasSuffix(in, ".go") && !strings.HasPrefix(in, "vendor") 33 | } 34 | 35 | return rw.RewriteImports(dir, rwf, filter) 36 | } 37 | 38 | func pathIsNotStdlib(path string) bool { 39 | first := strings.Split(path, "/")[0] 40 | 41 | if len(strings.Split(first, ".")) > 1 { 42 | return true 43 | } 44 | return false 45 | } 46 | 47 | type Importer struct { 48 | pkgs map[string]*gx.Dependency 49 | gopath string 50 | pm *gx.PM 51 | rewrite bool 52 | yesall bool 53 | preMap map[string]string 54 | 55 | bctx build.Context 56 | } 57 | 58 | func NewImporter(rw bool, gopath string, premap map[string]string) (*Importer, error) { 59 | cfg, err := gx.LoadConfig() 60 | if err != nil { 61 | return nil, err 62 | } 63 | 64 | pm, err := gx.NewPM(cfg) 65 | if err != nil { 66 | return nil, err 67 | } 68 | 69 | if premap == nil { 70 | premap = make(map[string]string) 71 | } 72 | 73 | bctx := build.Default 74 | bctx.GOPATH = gopath 75 | 76 | return &Importer{ 77 | pkgs: make(map[string]*gx.Dependency), 78 | gopath: gopath, 79 | pm: pm, 80 | rewrite: rw, 81 | preMap: premap, 82 | bctx: bctx, 83 | }, nil 84 | } 85 | 86 | // this function is an attempt to keep subdirectories of a package as part of 87 | // the same logical gx package. It has a special case for golang.org/x/ packages 88 | func getBaseDVCS(path string) string { 89 | parts := strings.Split(path, "/") 90 | depth := 3 91 | /* 92 | if parts[0] == "golang.org" && parts[1] == "x" { 93 | depth = 4 94 | } 95 | */ 96 | 97 | if len(parts) > depth { 98 | return strings.Join(parts[:3], "/") 99 | } 100 | return path 101 | } 102 | 103 | func (i *Importer) GxPublishGoPackage(imppath string) (*gx.Dependency, error) { 104 | imppath = getBaseDVCS(imppath) 105 | if d, ok := i.pkgs[imppath]; ok { 106 | return d, nil 107 | } 108 | 109 | if hash, ok := i.preMap[imppath]; ok { 110 | pkg, err := i.pm.GetPackageTo(hash, filepath.Join(vendorDir, hash)) 111 | if err != nil { 112 | return nil, err 113 | } 114 | 115 | dep := &gx.Dependency{ 116 | Hash: hash, 117 | Name: pkg.Name, 118 | Version: pkg.Version, 119 | } 120 | i.pkgs[imppath] = dep 121 | return dep, nil 122 | } 123 | 124 | // make sure its local 125 | err := i.GoGet(imppath) 126 | if err != nil { 127 | if !strings.Contains(err.Error(), "no buildable Go source files") { 128 | return nil, err 129 | } 130 | } 131 | 132 | pkgpath := path.Join(i.gopath, "src", imppath) 133 | pkgFilePath := path.Join(pkgpath, gx.PkgFileName) 134 | pkg, err := LoadPackageFile(pkgFilePath) 135 | if err != nil { 136 | if !os.IsNotExist(err) { 137 | return nil, err 138 | } 139 | 140 | // init as gx package 141 | parts := strings.Split(imppath, "/") 142 | pkgname := parts[len(parts)-1] 143 | if !i.yesall { 144 | p := fmt.Sprintf("enter name for import '%s'", imppath) 145 | nname, err := prompt(p, pkgname) 146 | if err != nil { 147 | return nil, err 148 | } 149 | 150 | pkgname = nname 151 | } 152 | 153 | err = i.pm.InitPkg(pkgpath, pkgname, "go", nil) 154 | if err != nil { 155 | return nil, err 156 | } 157 | 158 | pkg, err = LoadPackageFile(pkgFilePath) 159 | if err != nil { 160 | return nil, err 161 | } 162 | } 163 | 164 | // wipe out existing dependencies 165 | pkg.Dependencies = nil 166 | 167 | // recurse! 168 | depsToVendor, err := i.depsToVendorForPackage(imppath) 169 | if err != nil { 170 | return nil, err 171 | } 172 | 173 | for n, child := range depsToVendor { 174 | Log("- processing dep %s for %s [%d / %d]", child, imppath, n+1, len(depsToVendor)) 175 | if strings.HasPrefix(child, imppath) { 176 | continue 177 | } 178 | childdep, err := i.GxPublishGoPackage(child) 179 | if err != nil { 180 | return nil, err 181 | } 182 | 183 | pkg.Dependencies = append(pkg.Dependencies, childdep) 184 | } 185 | 186 | err = gx.SavePackageFile(pkg, pkgFilePath) 187 | if err != nil { 188 | return nil, err 189 | } 190 | 191 | fullpkgpath, err := filepath.Abs(pkgpath) 192 | if err != nil { 193 | return nil, err 194 | } 195 | 196 | err = i.rewriteImports(fullpkgpath) 197 | if err != nil { 198 | return nil, err 199 | } 200 | 201 | err = writeGxIgnore(pkgpath, []string{"Godeps/*"}) 202 | if err != nil { 203 | return nil, err 204 | } 205 | 206 | hash, err := i.pm.PublishPackage(pkgpath, &pkg.PackageBase) 207 | if err != nil { 208 | return nil, err 209 | } 210 | 211 | Log("published %s as %s", imppath, hash) 212 | 213 | dep := &gx.Dependency{ 214 | Hash: hash, 215 | Name: pkg.Name, 216 | Version: pkg.Version, 217 | } 218 | i.pkgs[imppath] = dep 219 | return dep, nil 220 | } 221 | 222 | func (i *Importer) depsToVendorForPackage(path string) ([]string, error) { 223 | rdeps := make(map[string]struct{}) 224 | 225 | gopkg, err := i.bctx.Import(path, "", 0) 226 | if err != nil { 227 | _, ok := err.(*build.NoGoError) 228 | if !ok { 229 | return nil, err 230 | } 231 | // if theres no go code here, there still might be some in lower directories 232 | } else { 233 | imps := append(gopkg.Imports, gopkg.TestImports...) 234 | // if the package existed and has go code in it 235 | gdeps := getBaseDVCS(path) + "/Godeps/_workspace/src/" 236 | for _, child := range imps { 237 | if strings.HasPrefix(child, gdeps) { 238 | child = child[len(gdeps):] 239 | } 240 | 241 | child = getBaseDVCS(child) 242 | if pathIsNotStdlib(child) && !strings.HasPrefix(child, path) { 243 | rdeps[child] = struct{}{} 244 | } 245 | } 246 | } 247 | 248 | dirents, err := ioutil.ReadDir(filepath.Join(i.gopath, "src", path)) 249 | if err != nil { 250 | return nil, err 251 | } 252 | 253 | for _, e := range dirents { 254 | if !e.IsDir() || skipDir(e.Name()) { 255 | continue 256 | } 257 | 258 | out, err := i.depsToVendorForPackage(filepath.Join(path, e.Name())) 259 | if err != nil { 260 | return nil, err 261 | } 262 | 263 | for _, o := range out { 264 | rdeps[o] = struct{}{} 265 | } 266 | } 267 | 268 | var depsToVendor []string 269 | for d, _ := range rdeps { 270 | depsToVendor = append(depsToVendor, d) 271 | } 272 | 273 | return depsToVendor, nil 274 | } 275 | 276 | func skipDir(name string) bool { 277 | switch name { 278 | case "Godeps", "vendor", ".git": 279 | return true 280 | default: 281 | return false 282 | } 283 | } 284 | 285 | func (i *Importer) rewriteImports(pkgpath string) error { 286 | 287 | filter := func(p string) bool { 288 | return !strings.HasPrefix(p, "vendor") && 289 | !strings.HasPrefix(p, ".git") && 290 | strings.HasSuffix(p, ".go") && 291 | !strings.HasPrefix(p, "Godeps") 292 | } 293 | 294 | base := pkgpath[len(i.gopath)+5:] 295 | gdepath := base + "/Godeps/_workspace/src/" 296 | rwf := func(in string) string { 297 | if strings.HasPrefix(in, gdepath) { 298 | in = in[len(gdepath):] 299 | } 300 | 301 | if !i.rewrite { 302 | // if rewrite not specified, just fixup godeps paths 303 | return in 304 | } 305 | 306 | dep, ok := i.pkgs[in] 307 | if ok { 308 | return "gx/" + dep.Hash + "/" + dep.Name 309 | } 310 | 311 | parts := strings.Split(in, "/") 312 | if len(parts) > 3 { 313 | obase := strings.Join(parts[:3], "/") 314 | dep, bok := i.pkgs[obase] 315 | if !bok { 316 | return in 317 | } 318 | 319 | return strings.Replace(in, obase, "gx/"+dep.Hash+"/"+dep.Name, 1) 320 | } 321 | 322 | return in 323 | } 324 | 325 | return rw.RewriteImports(pkgpath, rwf, filter) 326 | } 327 | 328 | // TODO: take an option to grab packages from local GOPATH 329 | func (imp *Importer) GoGet(path string) error { 330 | cmd := exec.Command("go", "get", path) 331 | env := os.Environ() 332 | for i, e := range env { 333 | if strings.HasPrefix(e, "GOPATH=") { 334 | env[i] = "GOPATH=" + imp.gopath 335 | } 336 | } 337 | cmd.Env = env 338 | out, err := cmd.CombinedOutput() 339 | if err != nil { 340 | return fmt.Errorf("go get failed: %s - %s", string(out), err) 341 | } 342 | return nil 343 | } 344 | 345 | func writeGxIgnore(dir string, ignore []string) error { 346 | return ioutil.WriteFile(filepath.Join(dir, ".gxignore"), []byte(strings.Join(ignore, "\n")), 0644) 347 | } 348 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package gx-js provides node specific hooks to be called by the gx tool. 3 | */ 4 | package main 5 | 6 | import ( 7 | "encoding/json" 8 | "fmt" 9 | "io/ioutil" 10 | "log" 11 | "os" 12 | "os/exec" 13 | "path/filepath" 14 | "strings" 15 | 16 | cli "github.com/codegangsta/cli" 17 | ) 18 | 19 | var cwd string 20 | 21 | var postInstallHookCommand = cli.Command{ 22 | Name: "post-install", 23 | Usage: "post install hook for newly installed node packages", 24 | Flags: []cli.Flag{ 25 | cli.BoolFlag{ 26 | Name: "global", 27 | Usage: "specifies whether or not the install was global", 28 | }, 29 | }, 30 | Action: func (c *cli.Context) { 31 | if !c.Args().Present() { 32 | log.Fatal("must specify path to newly installed package") 33 | } 34 | path := c.Args().First() 35 | packageJSON := "" 36 | recurse := false 37 | err := filepath.Walk(path, func(path string, fileinfo os.FileInfo, err error) error { 38 | if fileinfo.Name() == "package.json" && fileinfo.IsDir() == false { 39 | fmt.Println(path) 40 | packageJSON = path 41 | recurse = false 42 | } 43 | if recurse == true { 44 | return filepath.SkipDir 45 | } 46 | return err 47 | }) 48 | if err != nil { 49 | log.Fatal("post-install could not find package.json") 50 | } 51 | fmt.Println(packageJSON) 52 | fmt.Println(getNodeModulesBinFolder(path, c.Bool("global"))) 53 | binFolder := getNodeModulesBinFolder(path, c.Bool("global")) 54 | packageRoot := filepath.Dir(packageJSON) 55 | content, err := ioutil.ReadFile(packageJSON) 56 | if err != nil { 57 | log.Fatal("post-install could not read package.json") 58 | } 59 | 60 | var dat map[string]interface{} 61 | if err := json.Unmarshal(content, &dat); err != nil { 62 | log.Fatal("post-install could not parse package.json") 63 | } 64 | fmt.Println(dat) 65 | name := dat["name"].(string) 66 | var bin map[string]interface{} 67 | var binString string 68 | binObject, ok := dat["bin"].(map[string]interface{}) 69 | if !ok { 70 | binString, ok = dat["bin"].(string) 71 | if !ok { 72 | } 73 | } 74 | fmt.Println(binObject, binString) 75 | if len(binObject) != 0 { 76 | bin = binObject 77 | } 78 | if len(binString) != 0 { 79 | bin = make(map[string]interface{}) 80 | bin[name] = binString 81 | } 82 | fmt.Println(bin) 83 | for executable, location := range bin { 84 | f1 := filepath.Join(packageRoot, location.(string)) 85 | f2 := filepath.Join(binFolder, executable) 86 | fmt.Println(f2, " -> ", f1) 87 | var perm os.FileMode = 0755 88 | err := os.MkdirAll(binFolder, perm) 89 | if err != nil { 90 | log.Fatal("install-path could not create bin directory") 91 | } 92 | err = os.Symlink(f1, f2) 93 | if (err != nil) { 94 | log.Fatal("install-path could not create symlink:", err) 95 | } 96 | err = os.Chmod(f2, perm) 97 | if (err != nil) { 98 | log.Fatal("install-path could not change mode on the bin file", err) 99 | } 100 | } 101 | }, 102 | } 103 | var installPathHookCommand = cli.Command{ 104 | Name: "install-path", 105 | Usage: "prints out the install path", 106 | Flags: []cli.Flag{ 107 | cli.BoolFlag{ 108 | Name: "global", 109 | Usage: "print the global install directory", 110 | }, 111 | }, 112 | Action: func(c *cli.Context) { 113 | var npath string 114 | if c.Bool("global") { 115 | n, err := getNodejsExecutablePath() 116 | if err != nil { 117 | log.Fatal("install-path failed to get node path:", err) 118 | } 119 | npath = n 120 | } else { 121 | wd, err := os.Getwd() 122 | if err != nil { 123 | log.Fatal("install-path failed to get cwd:", err) 124 | } 125 | npath = wd 126 | } 127 | npath = getNodeModulesFolderPath(npath, c.Bool("global")) 128 | fmt.Println(npath) 129 | }, 130 | } 131 | 132 | // HookCommand provides `gx-js hook [hook] [args]`. 133 | var HookCommand = cli.Command{ 134 | Name: "hook", 135 | Usage: "node specific hooks to be called by the gx tool", 136 | Subcommands: []cli.Command{ 137 | installPathHookCommand, 138 | postInstallHookCommand, 139 | }, 140 | Action: func(c *cli.Context) {}, 141 | } 142 | 143 | func main() { 144 | app := cli.NewApp() 145 | app.Name = "gx-js" 146 | app.Author = "sterpe" 147 | app.Usage = "gx extensions for node.js" 148 | app.Version = "0.1.0" 149 | app.Flags = []cli.Flag{ 150 | cli.BoolFlag{ 151 | Name: "verbose", 152 | Usage: "turn on verbose output", 153 | }, 154 | } 155 | wd, err := os.Getwd() 156 | if err != nil { 157 | log.Fatal("failed to get cwd:", err) 158 | } 159 | cwd = wd 160 | 161 | app.Commands = []cli.Command{ 162 | HookCommand, 163 | { 164 | Name: "install-path", 165 | Category: "hook", 166 | }, 167 | } 168 | 169 | app.Run(os.Args) 170 | } 171 | 172 | func getNodeModulesBinFolder(path string, global bool) (string) { 173 | dir := filepath.Dir(path) 174 | base := filepath.Base(dir) 175 | dir = filepath.Join(dir, "..") 176 | 177 | for base != "node_modules" && (base != "." && base != "/") { 178 | base = filepath.Base(dir) 179 | dir = filepath.Join(dir, "..") 180 | } 181 | dir = filepath.Join(dir, "node_modules") 182 | if global { 183 | bin := filepath.Join(dir, "..", "..", "bin") 184 | return bin 185 | } 186 | dotbin := filepath.Join(dir, ".bin") 187 | return dotbin 188 | } 189 | func getNodeModulesFolderPath(path string, global bool) (string) { 190 | if global { 191 | path = filepath.Join(path, "..", "lib") 192 | } 193 | 194 | return filepath.Join(path, "node_modules") 195 | } 196 | 197 | func getNodejsExecutablePath() (string, error) { 198 | out, err := exec.Command("node", "-p", "process.execPath").Output() 199 | node := strings.TrimSpace(string(out[:])) 200 | 201 | if err != nil { 202 | return node, err 203 | } else if node == "" { 204 | return node, fmt.Errorf("node binary not found") 205 | } 206 | 207 | dir, _ := filepath.Split(node) 208 | 209 | return dir, nil 210 | } 211 | -------------------------------------------------------------------------------- /rewrite/rewrite.go: -------------------------------------------------------------------------------- 1 | package rewrite 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "go/ast" 8 | "go/parser" 9 | "go/printer" 10 | "go/token" 11 | "os" 12 | "regexp" 13 | "strconv" 14 | "strings" 15 | "sync" 16 | 17 | fs "github.com/kr/fs" 18 | ) 19 | 20 | var bufpool *sync.Pool 21 | 22 | func init() { 23 | bufpool = &sync.Pool{ 24 | New: func() interface{} { 25 | return new(bytes.Buffer) 26 | }, 27 | } 28 | } 29 | 30 | func RewriteImports(path string, rw func(string) string, filter func(string) bool) error { 31 | w := fs.Walk(path) 32 | for w.Step() { 33 | rel := w.Path()[len(path):] 34 | if len(rel) == 0 { 35 | continue 36 | } 37 | rel = rel[1:] 38 | 39 | if strings.HasPrefix(rel, ".git") || strings.HasPrefix(rel, "vendor") { 40 | w.SkipDir() 41 | continue 42 | } 43 | 44 | if !strings.HasSuffix(w.Path(), ".go") { 45 | continue 46 | } 47 | 48 | if !filter(rel) { 49 | continue 50 | } 51 | 52 | err := rewriteImportsInFile(w.Path(), rw) 53 | if err != nil { 54 | fmt.Println("rewrite error: ", err) 55 | } 56 | } 57 | return nil 58 | } 59 | 60 | // inspired by godeps rewrite, rewrites import paths with gx vendored names 61 | func rewriteImportsInFile(fi string, rw func(string) string) error { 62 | cfg := &printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8} 63 | fset := token.NewFileSet() 64 | file, err := parser.ParseFile(fset, fi, nil, parser.ParseComments) 65 | if err != nil { 66 | return err 67 | } 68 | 69 | var changed bool 70 | for _, imp := range file.Imports { 71 | p, err := strconv.Unquote(imp.Path.Value) 72 | if err != nil { 73 | return err 74 | } 75 | 76 | np := rw(p) 77 | 78 | if np != p { 79 | changed = true 80 | imp.Path.Value = strconv.Quote(np) 81 | } 82 | } 83 | 84 | if !changed { 85 | return nil 86 | } 87 | 88 | buf := bufpool.Get().(*bytes.Buffer) 89 | if err = cfg.Fprint(buf, fset, file); err != nil { 90 | return err 91 | } 92 | 93 | fset = token.NewFileSet() 94 | file, err = parser.ParseFile(fset, fi, buf, parser.ParseComments) 95 | if err != nil { 96 | return err 97 | } 98 | 99 | buf.Reset() 100 | bufpool.Put(buf) 101 | 102 | ast.SortImports(fset, file) 103 | 104 | wpath := fi + ".temp" 105 | w, err := os.Create(wpath) 106 | if err != nil { 107 | return err 108 | } 109 | 110 | if err = cfg.Fprint(w, fset, file); err != nil { 111 | return err 112 | } 113 | 114 | if err = w.Close(); err != nil { 115 | return err 116 | } 117 | 118 | return os.Rename(wpath, fi) 119 | } 120 | 121 | func fixCanonicalImports(buf []byte) (bool, error) { 122 | var i int 123 | var changed bool 124 | for { 125 | n, tok, err := bufio.ScanLines(buf[i:], true) 126 | if err != nil { 127 | return false, err 128 | } 129 | if n == 0 { 130 | return changed, nil 131 | } 132 | i += n 133 | 134 | stripped := stripImportComment(tok) 135 | if stripped != nil { 136 | nstr := copy(tok, stripped) 137 | copy(tok[nstr:], bytes.Repeat([]byte(" "), len(tok)-nstr)) 138 | changed = true 139 | } 140 | } 141 | } 142 | 143 | // more code from our friends over at godep 144 | const ( 145 | importAnnotation = `import\s+(?:"[^"]*"|` + "`[^`]*`" + `)` 146 | importComment = `(?://\s*` + importAnnotation + `\s*$|/\*\s*` + importAnnotation + `\s*\*/)` 147 | ) 148 | 149 | var ( 150 | importCommentRE = regexp.MustCompile(`\s*(package\s+\w+)\s+` + importComment + `(.*)`) 151 | pkgPrefix = []byte("package ") 152 | ) 153 | 154 | func stripImportComment(line []byte) []byte { 155 | if !bytes.HasPrefix(line, pkgPrefix) { 156 | // Fast path; this will skip all but one line in the file. 157 | // This assumes there is no whitespace before the keyword. 158 | return nil 159 | } 160 | if m := importCommentRE.FindSubmatch(line); m != nil { 161 | return append(m[1], m[2]...) 162 | } 163 | return nil 164 | } 165 | --------------------------------------------------------------------------------