github.com/vugu/vugu@v0.3.5/gen/parser-util.go (about) 1 package gen 2 3 import ( 4 "fmt" 5 "go/ast" 6 "go/parser" 7 "go/printer" 8 "go/token" 9 "io" 10 "sort" 11 "strings" 12 13 // "github.com/vugu/vugu/internal/htmlx" 14 15 // "golang.org/x/net/html" 16 "github.com/vugu/html" 17 18 "github.com/vugu/vugu" 19 ) 20 21 func attrFromHtml(attr html.Attribute) vugu.VGAttribute { 22 return vugu.VGAttribute{ 23 Namespace: attr.Namespace, 24 Key: attr.OrigKey, 25 Val: attr.Val, 26 } 27 } 28 29 // func attrFromHtmlx(attr htmlx.Attribute) vugu.VGAttribute { 30 // return vugu.VGAttribute{ 31 // Namespace: attr.Namespace, 32 // Key: attr.Key, 33 // Val: attr.Val, 34 // } 35 // } 36 37 // stuff that is common to both parsers can get moved into here 38 39 func staticVGAttr(inAttr []html.Attribute) (ret []vugu.VGAttribute) { 40 41 for _, a := range inAttr { 42 switch { 43 // case a.Key == "vg-if": 44 // case a.Key == "vg-for": 45 // case a.Key == "vg-key": 46 // case a.Key == "vg-html": 47 case strings.HasPrefix(a.Key, "vg-"): 48 case strings.HasPrefix(a.Key, "."): 49 case strings.HasPrefix(a.Key, ":"): 50 case strings.HasPrefix(a.Key, "@"): 51 default: 52 ret = append(ret, attrFromHtml(a)) 53 } 54 } 55 56 return ret 57 } 58 59 func vgSlotName(n *html.Node) string { 60 for _, a := range n.Attr { 61 if a.Key == "name" { 62 return a.Val 63 } 64 } 65 return "" 66 } 67 68 func vgVarExpr(n *html.Node) string { 69 for _, a := range n.Attr { 70 if a.Key == "vg-var" { 71 return a.Val 72 } 73 } 74 return "" 75 } 76 77 func vgIfExpr(n *html.Node) string { 78 for _, a := range n.Attr { 79 if a.Key == "vg-if" { 80 return a.Val 81 } 82 } 83 return "" 84 } 85 86 func vgKeyExpr(n *html.Node) string { 87 for _, a := range n.Attr { 88 if a.Key == "vg-key" { 89 return a.Val 90 } 91 } 92 return "" 93 } 94 95 func vgCompExpr(n *html.Node) string { 96 for _, a := range n.Attr { 97 if a.Key == "expr" { 98 return a.Val 99 } 100 } 101 return "" 102 } 103 104 // func vgIfExprx(n *htmlx.Node) string { 105 // for _, a := range n.Attr { 106 // if a.Key == "vg-if" { 107 // return a.Val 108 // } 109 // } 110 // return "" 111 // } 112 113 type vgForAttr struct { 114 expr string 115 noshadow bool 116 } 117 118 func vgForExpr(n *html.Node) (vgForAttr, error) { 119 for _, a := range n.Attr { 120 if strings.HasPrefix(a.Key, "vg-for") { 121 v := vgForAttr{expr: strings.TrimSpace(a.Val)} 122 opts := strings.Split(a.Key, ".") 123 if len(opts) > 1 { 124 for _, opt := range opts[1:] { 125 switch opt { 126 case "noshadow": 127 v.noshadow = true 128 default: 129 return vgForAttr{}, fmt.Errorf("option %q unknown", opt) 130 } 131 } 132 } 133 return v, nil 134 } 135 } 136 return vgForAttr{}, nil 137 } 138 139 func vgHTMLExpr(n *html.Node) string { 140 for _, a := range n.Attr { 141 // vg-html and vg-content are the same thing, 142 // the name vg-content was introduced to call out 143 // the difference between Vue's v-html attribute 144 // which does not perform escaping. 145 if a.Key == "vg-html" { 146 return a.Val 147 } 148 if a.Key == "vg-content" { 149 return a.Val 150 } 151 } 152 return "" 153 } 154 155 // extract ":attr" stuff from a node 156 func dynamicVGAttrExpr(n *html.Node) (ret map[string]string, retKeys []string) { 157 var da []html.Attribute 158 // get dynamic attrs first 159 for _, a := range n.Attr { 160 // ":" and "vg-attr" are the AttributeLister case 161 if strings.HasPrefix(a.OrigKey, ":") || a.OrigKey == "vg-attr" { 162 da = append(da, a) 163 } 164 } 165 if len(da) == 0 { // don't allocate map if we don't have to 166 return 167 } 168 // make map as small as possible 169 ret = make(map[string]string, len(da)) 170 retKeys = make([]string, len(da)) 171 for i, a := range da { 172 k := strings.TrimPrefix(a.OrigKey, ":") 173 retKeys[i] = k 174 ret[k] = a.Val 175 } 176 sort.Strings(retKeys) 177 return 178 } 179 180 // extract ".prop" stuff from a node 181 func propVGAttrExpr(n *html.Node) (ret map[string]string, retKeys []string) { 182 var da []html.Attribute 183 // get prop attrs first 184 for _, a := range n.Attr { 185 if strings.HasPrefix(a.OrigKey, ".") { 186 da = append(da, a) 187 } 188 } 189 if len(da) == 0 { // don't allocate map if we don't have to 190 return 191 } 192 // make map as small as possible 193 ret = make(map[string]string, len(da)) 194 retKeys = make([]string, len(da)) 195 for i, a := range da { 196 k := strings.TrimPrefix(a.OrigKey, ".") 197 retKeys[i] = k 198 ret[k] = a.Val 199 } 200 sort.Strings(retKeys) 201 return 202 } 203 204 // returns vg-js-create and vg-js-populate 205 func jsCallbackVGAttrExpr(n *html.Node) (ret map[string]string) { 206 for _, attr := range n.Attr { 207 if strings.HasPrefix(attr.OrigKey, "vg-js-") { 208 if ret == nil { 209 ret = make(map[string]string, 2) 210 } 211 ret[attr.OrigKey] = attr.Val 212 } 213 } 214 return ret 215 } 216 217 func vgDOMEventExprs(n *html.Node) (ret map[string]string, retKeys []string) { 218 return vgEventExprs(n) 219 } 220 221 // extract "@event" stuff from a node 222 func vgEventExprs(n *html.Node) (ret map[string]string, retKeys []string) { 223 var da []html.Attribute 224 // get attrs first 225 for _, a := range n.Attr { 226 if strings.HasPrefix(a.OrigKey, "@") { 227 da = append(da, a) 228 } 229 } 230 if len(da) == 0 { // don't allocate map if we don't have to 231 return 232 } 233 // make map as small as possible 234 ret = make(map[string]string, len(da)) 235 for _, a := range da { 236 k := strings.TrimPrefix(a.OrigKey, "@") 237 retKeys = append(retKeys, k) 238 ret[k] = a.Val 239 } 240 return 241 } 242 243 // var vgDOMParseExprRE = regexp.MustCompile(`^([a-zA-Z0-9_.]+)\((.*)\)$`) 244 245 // func vgDOMParseExpr(expr string) (receiver string, methodName string, argList string) { 246 // parts := vgDOMParseExprRE.FindStringSubmatch(expr) 247 // if len(parts) != 3 { 248 // return 249 // } 250 // argList = parts[2] 251 // f := parts[1] 252 // fparts := strings.Split(f, ".") 253 254 // receiver, methodName = strings.Join(fparts[:len(fparts)-1], "."), fparts[len(fparts)-1] 255 256 // // if len(fparts) == 1 { // just "methodName" 257 // // methodName = f 258 // // } else if len(fparts) > 2 { // "a.b.MethodName" 259 // // receiver, methodName = strings.Join(fparts[:len(fparts)-1], "."), fparts[len(fparts)-1] 260 // // } else { // "a.MethodName" 261 // // receiver, methodName = fparts[0], fparts[1] 262 // // } 263 // return 264 // } 265 266 // ^([a-zA-Z0-9_.]+)\((.*)\)$ 267 268 // dedupImports reads Go source and removes duplicate import statements. 269 func dedupImports(r io.Reader, w io.Writer, fname string) error { 270 271 fset := token.NewFileSet() // positions are relative to fset 272 f, err := parser.ParseFile(fset, fname, r, parser.AllErrors|parser.ParseComments) 273 if err != nil { 274 return err 275 } 276 277 // ast.Print(fset, f) 278 // ast.Print(fset, f) 279 280 // ast.Print(fset, f.Decls) 281 // f.Decls = f.Decls[1:] 282 dedupAstFileImports(f) 283 ast.SortImports(fset, f) 284 285 err = printer.Fprint(w, fset, f) 286 if err != nil { 287 return err 288 } 289 290 return nil 291 } 292 293 func dedupAstFileImports(f *ast.File) { 294 295 imap := make(map[string]bool, len(f.Imports)+10) 296 297 outdecls := make([]ast.Decl, 0, len(f.Decls)) 298 for _, decl := range f.Decls { 299 300 // check for import declaration 301 genDecl, _ := decl.(*ast.GenDecl) 302 // not an import declaration, just copy and continue 303 if genDecl == nil || genDecl.Tok != token.IMPORT { 304 outdecls = append(outdecls, decl) 305 continue 306 } 307 308 // for imports, we loop over each ImportSpec (each package, regardless of which form of import statement) 309 outspecs := make([]ast.Spec, 0, len(genDecl.Specs)) 310 for _, spec := range genDecl.Specs { 311 ispec := spec.(*ast.ImportSpec) 312 // always use path 313 key := ispec.Path.Value 314 // if name is present, prepend 315 if ispec.Name != nil { 316 key = ispec.Name.Name + " " + key 317 } 318 319 // if we've seen this import before, then just move to the next 320 if imap[key] { 321 continue 322 } 323 imap[key] = true // mark this import as having been seen 324 325 // keep the import 326 outspecs = append(outspecs, ispec) 327 } 328 329 // use outspecs for this import decl, unless it's empty in which case we remove/skip the whole import decl 330 if len(outspecs) == 0 { 331 continue 332 } 333 genDecl.Specs = outspecs 334 outdecls = append(outdecls, genDecl) 335 336 } 337 f.Decls = outdecls 338 339 }