github.com/jgarto/itcv@v0.0.0-20180826224514-4eea09c1aa0d/cmd/reactGen/comp_gen.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "go/ast" 6 "go/format" 7 "io/ioutil" 8 "strings" 9 "unicode" 10 "unicode/utf8" 11 12 "myitcv.io/gogenerate" 13 ) 14 15 type compGen struct { 16 *coreGen 17 18 Recv string 19 Name string 20 21 HasState bool 22 HasProps bool 23 HasGetInitState bool 24 HasComponentWillReceiveProps bool 25 26 PropsHasEquals bool 27 StateHasEquals bool 28 29 RendersSlice bool 30 31 RendersMethods []RendersMethod 32 } 33 34 type RendersMethod struct { 35 Name string 36 Type string 37 } 38 39 func (g *gen) genComp(defName string) { 40 41 name := strings.TrimSuffix(defName, compDefSuffix) 42 43 if g.isReactCore { 44 panic(fmt.Errorf("don't yet know how to generate core components like %v", name)) 45 } 46 47 r, _ := utf8.DecodeRuneInString(name) 48 49 cg := &compGen{ 50 coreGen: newCoreGen(g), 51 Name: name, 52 Recv: string(unicode.ToLower(r)), 53 } 54 55 _, hasState := g.types[name+stateTypeSuffix] 56 57 _, hasPropsTmpl := g.types[propsTypeTmplPrefix+name+propsTypeSuffix] 58 _, hasPropsType := g.types[name+propsTypeSuffix] 59 60 hasProps := hasPropsTmpl || hasPropsType 61 62 cg.HasState = hasState 63 cg.HasProps = hasProps 64 65 for _, ff := range g.nonPointMeths[defName] { 66 m := ff.fn 67 68 // gather Renders* methods 69 func() { 70 if !strings.HasPrefix(m.Name.Name, "Renders") { 71 return 72 } 73 74 if m.Type.Params == nil || len(m.Type.Params.List) != 1 { 75 return 76 } 77 78 if m.Type.Results != nil && len(m.Type.Results.List) != 0 { 79 return 80 } 81 82 ap := m.Type.Params.List[0] 83 84 cg.RendersMethods = append(cg.RendersMethods, RendersMethod{ 85 Name: m.Name.Name, 86 Type: astNodeString(ap.Type), 87 }) 88 }() 89 90 func() { 91 if m.Name.Name != "Render" { 92 return 93 } 94 95 if m.Type.Params != nil && len(m.Type.Params.List) != 0 { 96 return 97 } 98 99 if m.Type.Results == nil || len(m.Type.Results.List) != 1 { 100 return 101 } 102 103 rp := m.Type.Results.List[0] 104 105 at, ok := rp.Type.(*ast.ArrayType) 106 107 if !ok { 108 return 109 } 110 111 if at.Len == nil { 112 cg.RendersSlice = true 113 } 114 }() 115 } 116 117 if hasState { 118 for _, ff := range g.nonPointMeths[defName] { 119 m := ff.fn 120 121 if m.Name.Name != getInitialState { 122 continue 123 } 124 125 if m.Type.Params != nil && len(m.Type.Params.List) > 0 { 126 continue 127 } 128 129 if m.Type.Results != nil && len(m.Type.Results.List) != 1 { 130 continue 131 } 132 133 rp := m.Type.Results.List[0] 134 135 id, ok := rp.Type.(*ast.Ident) 136 if !ok { 137 continue 138 } 139 140 if id.Name == name+stateTypeSuffix { 141 cg.HasGetInitState = true 142 break 143 } 144 } 145 146 for _, ff := range g.nonPointMeths[name+stateTypeSuffix] { 147 m := ff.fn 148 149 if m.Name.Name != equals { 150 continue 151 } 152 153 if m.Type.Params != nil && len(m.Type.Params.List) != 1 { 154 continue 155 } 156 157 if m.Type.Results != nil && len(m.Type.Results.List) != 1 { 158 continue 159 } 160 161 { 162 v := m.Type.Params.List[0] 163 164 id, ok := v.Type.(*ast.Ident) 165 if !ok { 166 continue 167 } 168 169 if id.Name != name+stateTypeSuffix { 170 continue 171 } 172 } 173 174 { 175 v := m.Type.Results.List[0] 176 177 id, ok := v.Type.(*ast.Ident) 178 if !ok { 179 continue 180 } 181 182 if id.Name != "bool" { 183 continue 184 } 185 } 186 187 cg.StateHasEquals = true 188 } 189 } 190 191 if hasProps { 192 for _, ff := range g.nonPointMeths[defName] { 193 m := ff.fn 194 195 if m.Name.Name != componentWillReceiveProps { 196 continue 197 } 198 199 if m.Type.Params != nil && len(m.Type.Params.List) != 1 { 200 continue 201 } 202 203 if m.Type.Results != nil && len(m.Type.Results.List) != 0 { 204 continue 205 } 206 207 p := m.Type.Params.List[0] 208 209 id, ok := p.Type.(*ast.Ident) 210 if !ok { 211 continue 212 } 213 214 if id.Name == name+propsTypeSuffix { 215 cg.HasComponentWillReceiveProps = true 216 break 217 } 218 } 219 220 for _, ff := range g.nonPointMeths[name+propsTypeSuffix] { 221 m := ff.fn 222 223 if m.Name.Name != equals { 224 continue 225 } 226 227 if m.Type.Params != nil && len(m.Type.Params.List) != 1 { 228 continue 229 } 230 231 if m.Type.Results != nil && len(m.Type.Results.List) != 1 { 232 continue 233 } 234 235 { 236 v := m.Type.Params.List[0] 237 238 id, ok := v.Type.(*ast.Ident) 239 if !ok { 240 continue 241 } 242 243 if id.Name != name+propsTypeSuffix { 244 continue 245 } 246 } 247 248 { 249 v := m.Type.Results.List[0] 250 251 id, ok := v.Type.(*ast.Ident) 252 if !ok { 253 continue 254 } 255 256 if id.Name != "bool" { 257 continue 258 } 259 } 260 261 cg.PropsHasEquals = true 262 } 263 } 264 265 cg.pf("// Code generated by %v. DO NOT EDIT.\n", reactGenCmd) 266 cg.pln() 267 cg.pf("package %v\n", cg.pkg) 268 269 cg.pf("import \"%v\"\n", reactPkg) 270 cg.pln() 271 272 cg.pt(` 273 {{ $cg := . }} 274 275 type {{.Name}}Elem struct { 276 react.Element 277 } 278 279 {{range $rm := .RendersMethods }} 280 func ({{$cg.Recv}} *{{$cg.Name}}Elem) {{$rm.Name}}({{$rm.Type}}) {} 281 {{end}} 282 283 {{if .RendersMethods}} 284 func ({{$cg.Recv}} *{{$cg.Name}}Elem) noop() { 285 var v {{.Name}}Def 286 r := v.Render() 287 288 {{range $rm := .RendersMethods }} 289 v.{{$rm.Name}}(r) 290 {{end -}} 291 } 292 {{end}} 293 294 func build{{.Name}}(cd react.ComponentDef) react.Component { 295 return {{.Name}}Def{ComponentDef: cd} 296 } 297 298 func build{{.Name}}Elem({{if .HasProps}}props {{.Name}}Props,{{end}} children ...react.Element) *{{.Name}}Elem { 299 return &{{.Name}}Elem{ 300 Element: react.CreateElement(build{{.Name}}, {{if .HasProps}}props{{else}}nil{{end}}, children...), 301 } 302 } 303 304 func ({{.Recv}} {{.Name}}Def) RendersElement() react.Element { 305 {{if .RendersSlice -}} 306 rr := t.Render() 307 elems := make([]react.Element, 0, len(rr)) 308 for _, r := range rr { 309 elems = append(elems, r) 310 } 311 return react.Fragment(elems...) 312 {{else -}} 313 return {{.Recv}}.Render() 314 {{end -}} 315 } 316 317 {{if .HasState}} 318 // SetState is an auto-generated proxy proxy to update the state for the 319 // {{.Name}} component. SetState does not immediately mutate {{.Recv}}.State() 320 // but creates a pending state transition. 321 func ({{.Recv}} {{.Name}}Def) SetState(state {{.Name}}State) { 322 {{.Recv}}.ComponentDef.SetState(state) 323 } 324 325 // State is an auto-generated proxy to return the current state in use for the 326 // render of the {{.Name}} component 327 func ({{.Recv}} {{.Name}}Def) State() {{.Name}}State { 328 return {{.Recv}}.ComponentDef.State().({{.Name}}State) 329 } 330 331 // IsState is an auto-generated definition so that {{.Name}}State implements 332 // the myitcv.io/react.State interface. 333 func ({{.Recv}} {{.Name}}State) IsState() {} 334 335 var _ react.State = {{.Name}}State{} 336 337 // GetInitialStateIntf is an auto-generated proxy to GetInitialState 338 func ({{.Recv}} {{.Name}}Def) GetInitialStateIntf() react.State { 339 {{if .HasGetInitState -}} 340 return {{.Recv}}.GetInitialState() 341 {{else -}} 342 return {{.Name}}State{} 343 {{end -}} 344 } 345 346 func ({{.Recv}} {{.Name}}State) EqualsIntf(val react.State) bool { 347 {{if .StateHasEquals -}} 348 return {{.Recv}}.Equals(val.({{.Name}}State)) 349 {{else -}} 350 return {{.Recv}} == val.({{.Name}}State) 351 {{end -}} 352 } 353 {{end}} 354 355 356 {{if .HasProps}} 357 // IsProps is an auto-generated definition so that {{.Name}}Props implements 358 // the myitcv.io/react.Props interface. 359 func ({{.Recv}} {{.Name}}Props) IsProps() {} 360 361 // Props is an auto-generated proxy to the current props of {{.Name}} 362 func ({{.Recv}} {{.Name}}Def) Props() {{.Name}}Props { 363 uprops := {{.Recv}}.ComponentDef.Props() 364 return uprops.({{.Name}}Props) 365 } 366 367 {{if .HasComponentWillReceiveProps}} 368 // ComponentWillReceivePropsIntf is an auto-generated proxy to 369 // ComponentWillReceiveProps 370 func ({{.Recv}} {{.Name}}Def) ComponentWillReceivePropsIntf(val interface{}) { 371 ourProps := val.({{.Name}}Props) 372 {{.Recv}}.ComponentWillReceiveProps(ourProps) 373 } 374 {{end}} 375 376 func ({{.Recv}} {{.Name}}Props) EqualsIntf(val react.Props) bool { 377 {{if .PropsHasEquals -}} 378 return {{.Recv}}.Equals(val.({{.Name}}Props)) 379 {{else -}} 380 return {{.Recv}} == val.({{.Name}}Props) 381 {{end -}} 382 } 383 384 var _ react.Props = {{.Name}}Props{} 385 {{end}} 386 `, cg) 387 388 ofName := gogenerate.NameFile(name, reactGenCmd) 389 toWrite := cg.buf.Bytes() 390 391 out, err := format.Source(toWrite) 392 if err == nil { 393 toWrite = out 394 } 395 396 if err := ioutil.WriteFile(ofName, toWrite, 0644); err != nil { 397 fatalf("could not write %v: %v", ofName, err) 398 } 399 }