github.com/ernestokarim/closurer@v0.0.0-20130119214741-f245d086c750/config/conf.go (about) 1 package config 2 3 import ( 4 "encoding/xml" 5 "log" 6 "os" 7 "strings" 8 "time" 9 10 "github.com/ernestokarim/closurer/app" 11 ) 12 13 var ( 14 globalConf *Config 15 lastModification time.Time 16 ) 17 18 func Load() error { 19 if globalConf != nil && !NoCache { 20 info, err := os.Lstat(ConfPath) 21 if err != nil { 22 return app.Error(err) 23 } 24 25 if info.ModTime() == lastModification { 26 return nil 27 } 28 } 29 30 f, err := os.Open(ConfPath) 31 if err != nil { 32 return app.Error(err) 33 } 34 defer f.Close() 35 36 conf := new(Config) 37 if err := xml.NewDecoder(f).Decode(&conf); err != nil { 38 return app.Error(err) 39 } 40 41 // Assign it before validating, because we need it to 42 // inherit targets. 43 globalConf = conf 44 45 if err := conf.validate(); err != nil { 46 return err 47 } 48 49 info, err := os.Lstat(ConfPath) 50 if err != nil { 51 return app.Error(err) 52 } 53 lastModification = info.ModTime() 54 55 return nil 56 } 57 58 func Current() *Config { 59 return globalConf 60 } 61 62 func (c *Config) validate() error { 63 // Library & compiler paths 64 if c.Js != nil { 65 if c.Js.Root == "" { 66 return app.Errorf("The JS root folder is required") 67 } 68 69 if c.Js.Formatting != "" && c.Js.Formatting != "PRETTY_PRINT" { 70 return app.Errorf("formatting mode not allowed: %s", c.Js.Formatting) 71 } 72 73 if c.Js.SideEffects != "" && c.Js.SideEffects != "true" { 74 return app.Errorf("boolean value not allowed: %s", c.Js.SideEffects) 75 } 76 77 if c.Js.Language != "" { 78 modes := map[string]bool{ 79 "ECMASCRIPT3": true, 80 "ECMASCRIPT5": true, 81 "ECMASCRIPT5_STRICT": true, 82 } 83 if _, ok := modes[c.Js.Language]; !ok { 84 return app.Errorf("language mode not allowed: %s", c.Js.Language) 85 } 86 } 87 88 // JS targets and inheritation 89 if len(c.Js.Targets) == 0 { 90 return app.Errorf("No target provided for JS code") 91 } 92 for _, t := range c.Js.Targets { 93 if err := t.ApplyInherits(); err != nil { 94 return err 95 } 96 } 97 98 // Check compilation mode and warnings level 99 for _, t := range c.Js.Targets { 100 modes := map[string]bool{ 101 "SIMPLE": true, 102 "ADVANCED": true, 103 "WHITESPACE": true, 104 "RAW": true, 105 } 106 if _, ok := modes[t.Mode]; !ok { 107 return app.Errorf("Illegal compilation mode in target %s: %s", t.Name, t.Mode) 108 } 109 110 levels := map[string]bool{ 111 "QUIET": true, 112 "DEFAULT": true, 113 "VERBOSE": true, 114 } 115 if _, ok := levels[t.Level]; !ok { 116 return app.Errorf("Illegal warning level in target %s: %s", t.Name, t.Level) 117 } 118 } 119 120 // Check that the command line target is in the config file 121 found := false 122 for _, name := range TargetList() { 123 for _, t := range c.Js.Targets { 124 if t.Name == name { 125 found = true 126 break 127 } 128 } 129 if !found { 130 return app.Errorf("Target %s not found in the config file", name) 131 } 132 } 133 134 // Validate the compilation checks 135 if c.Js.Checks != nil { 136 validChecks(c.Js.Checks.Errors) 137 validChecks(c.Js.Checks.Warnings) 138 validChecks(c.Js.Checks.Offs) 139 } 140 141 // Check the prepend files 142 if c.Js.Prepends != nil { 143 for _, prepend := range c.Js.Prepends { 144 if prepend.File == "" { 145 return app.Errorf("prepend file empty") 146 } 147 } 148 } 149 } 150 151 if c.Build == "" { 152 return app.Errorf("The build folder is required") 153 } 154 if c.Library != nil && c.Library.Root == "" { 155 return app.Errorf("The Closure Library path is required") 156 } 157 if c.Js != nil && c.Js.Compiler == "" { 158 return app.Errorf("The Closure Compiler path is required") 159 } 160 161 if c.Gss != nil { 162 // GSS compiler 163 if c.Gss.Compiler == "" { 164 return app.Errorf("The Closure Stylesheets path is required") 165 } 166 167 // GSS targets 168 if len(c.Gss.Targets) == 0 { 169 return app.Errorf("No target provided for GSS code") 170 } 171 172 // At least one input file should be provided 173 if len(c.Gss.Inputs) == 0 { 174 return app.Errorf("No inputs provided for GSS code") 175 } 176 177 // Compare JS targets and GSS targets 178 if c.Js != nil { 179 if len(c.Js.Targets) != len(c.Gss.Targets) { 180 return app.Errorf("Different number of targets provided for GSS & JS") 181 } 182 for i, tjs := range c.Js.Targets { 183 tgss := c.Gss.Targets[i] 184 if tjs.Name != tgss.Name { 185 return app.Errorf("Targets with different name or order: %s != %s", 186 tjs.Name, tgss.Name) 187 } 188 189 // Rename property of the GSS target 190 if tgss.Rename != "true" && tgss.Rename != "false" && tgss.Rename != "" { 191 return app.Errorf("Illegal renaming policy value") 192 } 193 194 // Apply the inherits option 195 if err := tgss.ApplyInherits(); err != nil { 196 return err 197 } 198 199 // Check that the GSS defines don't have a value 200 for _, d := range tgss.Defines { 201 if d.Value != "" { 202 return app.Errorf("Define values in GSS should be empty") 203 } 204 } 205 } 206 } 207 } 208 209 // Soy compiler 210 if c.Soy != nil && c.Soy.Root != "" && c.Soy.Compiler == "" { 211 return app.Errorf("The Closure Templates path is required") 212 } 213 214 // Current targets in build mode 215 if c.Js != nil && c.Gss != nil { 216 for _, t := range TargetList() { 217 SelectTarget(t) 218 219 tjs := c.Js.CurTarget() 220 tgss := c.Gss.CurTarget() 221 222 if tjs == nil || tgss == nil { 223 return app.Errorf("Target not found in the config: %s", t) 224 } 225 226 if Build && IsTarget(tjs.Name) { 227 if tjs.Output == "" { 228 return app.Errorf("Target to build JS without an output file: %s", 229 tjs.Name) 230 } 231 if tgss != nil && tgss.Output == "" { 232 return app.Errorf("Target to build GSS without an output file: %s", 233 tjs.Name) 234 } 235 } 236 } 237 } 238 239 // Fix the compilers paths 240 if c.Js != nil { 241 c.Js.Compiler = fixPath(c.Js.Compiler) 242 } 243 if c.Gss != nil { 244 c.Gss.Compiler = fixPath(c.Gss.Compiler) 245 } 246 if c.Soy != nil { 247 c.Soy.Compiler = fixPath(c.Soy.Compiler) 248 } 249 if c.Library != nil { 250 c.Library.Root = fixPath(c.Library.Root) 251 } 252 253 return nil 254 } 255 256 // Replace the ~ with the correct folder path 257 func fixPath(p string) string { 258 if !strings.Contains(p, "~") { 259 return p 260 } 261 262 user := os.Getenv("USER") 263 if user == "" { 264 user = os.Getenv("USERNAME") 265 } 266 if user == "" { 267 log.Fatal("Found ~ in a path, but USER nor USERNAME are exported in the env") 268 } 269 270 return strings.Replace(p, "~", "/home/"+user, -1) 271 } 272 273 func validChecks(lst []*CheckNode) error { 274 for _, check := range lst { 275 checks := map[string]bool{ 276 "ambiguousFunctionDecl": true, 277 "checkRegExp": true, 278 "checkTypes": true, 279 "checkVars": true, 280 "constantProperty": true, 281 "deprecated": true, 282 "fileoverviewTags": true, 283 "internetExplorerChecks": true, 284 "invalidCasts": true, 285 "missingProperties": true, 286 "nonStandardJsDocs": true, 287 "strictModuleDepCheck": true, 288 "typeInvalidation": true, 289 "undefinedNames": true, 290 "undefinedVars": true, 291 "unknownDefines": true, 292 "uselessCode": true, 293 "globalThis": true, 294 "duplicateMessage": true, 295 } 296 if _, ok := checks[check.Name]; !ok { 297 return app.Errorf("Illegal check: %s", check.Name) 298 } 299 } 300 301 return nil 302 }