github.com/JarrahG/buffalocli@v0.0.0-20230801092127-b85bfd5d395a/internal/genny/assets/webpack/webpack.go (about) 1 package webpack 2 3 import ( 4 "embed" 5 "fmt" 6 "io/fs" 7 "os/exec" 8 "path/filepath" 9 "runtime" 10 "strings" 11 12 "github.com/gobuffalo/genny/v2" 13 "github.com/gobuffalo/genny/v2/gogen" 14 ) 15 16 const YARN = "yarn" 17 18 //go:embed templates/* templates/assets/css/_buffalo.scss.tmpl 19 var templates embed.FS 20 21 // Templates used for generating webpack 22 // (exported mostly for the "fix" command) 23 func Templates() (fs.FS, error) { 24 return fs.Sub(templates, "templates") 25 } 26 27 // BinPath is the path to the local install of webpack 28 var BinPath = func() string { 29 s := filepath.Join("node_modules", ".bin", "webpack") 30 if runtime.GOOS == "windows" { 31 s += ".cmd" 32 } 33 return s 34 }() 35 36 // New generator for creating webpack asset files 37 func New(opts *Options) (*genny.Generator, error) { 38 g := genny.New() 39 40 if err := opts.Validate(); err != nil { 41 return g, err 42 } 43 44 g.RunFn(func(r *genny.Runner) error { 45 if opts.App.WithYarn { 46 if _, err := r.LookPath(YARN); err == nil { 47 return nil 48 } 49 // If yarn is not installed, it still can be installed with npm. 50 } 51 if _, err := r.LookPath("npm"); err != nil { 52 return fmt.Errorf("could not find npm executable") 53 } 54 return nil 55 }) 56 57 temp, err := Templates() 58 if err != nil { 59 return g, err 60 } 61 62 if err := g.FS(temp); err != nil { 63 return g, err 64 } 65 66 data := map[string]interface{}{ 67 "opts": opts, 68 } 69 t := gogen.TemplateTransformer(data, gogen.TemplateHelpers) 70 g.Transformer(t) 71 g.Transformer(genny.Dot()) 72 73 // TODO: workaround for 1.16, remove when we upgrade to 1.17 and rename "dot-*" files back to "-dot-*" 74 g.Transformer(genny.NewTransformer("*", func(f genny.File) (genny.File, error) { 75 name := f.Name() 76 if strings.HasPrefix(name, "dot-") { 77 name = strings.TrimPrefix(name, "dot-") 78 name = "." + name 79 } 80 return genny.NewFile(name, f), nil 81 })) 82 g.Transformer(genny.Replace("/dot-", "/.")) 83 84 g.RunFn(func(r *genny.Runner) error { 85 return installPkgs(r, opts) 86 }) 87 88 return g, nil 89 } 90 91 func installPkgs(r *genny.Runner, opts *Options) error { 92 command := YARN 93 args := []string{"install"} 94 95 if !opts.App.WithYarn { 96 command = "npm" 97 args = []string{"install", "--no-progress", "--save"} 98 } else { 99 if err := installYarn(r); err != nil { 100 return err 101 } 102 if err := setupYarnBerry(r); err != nil { 103 return err 104 } 105 } 106 107 r.Exec(exec.Command(command, "--version")) 108 c := exec.Command(command, args...) 109 c.Stdout = yarnWriter{ 110 fn: r.Logger.Debug, 111 } 112 c.Stderr = yarnWriter{ 113 fn: r.Logger.Debug, 114 } 115 return r.Exec(c) 116 } 117 118 type yarnWriter struct { 119 fn func(...interface{}) 120 } 121 122 func (y yarnWriter) Write(p []byte) (int, error) { 123 y.fn(string(p)) 124 return len(p), nil 125 } 126 127 func installYarn(r *genny.Runner) error { 128 // if there's no yarn, install it! 129 if _, err := r.LookPath(YARN); err == nil { 130 return nil 131 } 132 yargs := []string{"install", "-g", "yarn"} 133 return r.Exec(exec.Command("npm", yargs...)) 134 } 135 136 func setupYarnBerry(r *genny.Runner) error { 137 if err := r.Exec(exec.Command(YARN, "--version")); err != nil { 138 return err 139 } 140 if err := r.Exec(exec.Command(YARN, "set", "version", "berry")); err != nil { 141 return err 142 } 143 // the yarnrc config format is different between classic and berry so 144 // it should be configured after berry installation (set version) 145 if err := r.Exec(exec.Command(YARN, "config", "set", "enableGlobalCache", "true")); err != nil { 146 return err 147 } 148 if err := r.Exec(exec.Command(YARN, "config", "set", "logFilters", "--json", `[{"code":"YN0013","level":"discard"}]`)); err != nil { 149 return err 150 } 151 return nil 152 }