codegen: Introduce TS codegen veneer (#54816)
* Split all named types out into defs, etc. * Use latest cuetsy, refactor generators accordingly * Return AST type from plugin TS generator * Near-complete checkin of TS veneer code generator * First full completed pass * Improve the attribute name * Defer use of the dashboard veneer type to follow-up * Remove dummy index, prettier on veneer * Fix merge errors in gen.go * Add match field to SpecialValueMap * Fix backend lint errors
This commit is contained in:
+50
-55
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/deepmap/oapi-codegen/pkg/codegen"
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
"github.com/grafana/cuetsy"
|
||||
tsast "github.com/grafana/cuetsy/ts/ast"
|
||||
"github.com/grafana/grafana/pkg/framework/coremodel"
|
||||
"github.com/grafana/grafana/pkg/plugins/pfs"
|
||||
"github.com/grafana/thema"
|
||||
@@ -101,15 +102,22 @@ type PluginTreeOrErr struct {
|
||||
// It is, for now, tailored specifically to Grafana core's codegen needs.
|
||||
type PluginTree pfs.Tree
|
||||
|
||||
func (pt *PluginTree) GenerateTS(path string) (WriteDiffer, error) {
|
||||
func (pt *PluginTree) GenerateTypeScriptAST() (*tsast.File, error) {
|
||||
t := (*pfs.Tree)(pt)
|
||||
f := &tsast.File{}
|
||||
|
||||
// TODO replace with cuetsy's TS AST
|
||||
f := &tvars_cuetsy_multi{
|
||||
Header: tvars_autogen_header{
|
||||
GeneratorPath: "public/app/plugins/gen.go", // FIXME hardcoding is not OK
|
||||
LineagePath: "models.cue",
|
||||
},
|
||||
tf := tvars_autogen_header{
|
||||
GeneratorPath: "public/app/plugins/gen.go", // FIXME hardcoding is not OK
|
||||
LineagePath: "models.cue",
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
err := tmpls.Lookup("autogen_header.tmpl").Execute(&buf, tf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error executing header template: %w", err)
|
||||
}
|
||||
|
||||
f.Doc = &tsast.Comment{
|
||||
Text: buf.String(),
|
||||
}
|
||||
|
||||
pi := t.RootPlugin()
|
||||
@@ -118,7 +126,9 @@ func (pt *PluginTree) GenerateTS(path string) (WriteDiffer, error) {
|
||||
return nil, nil
|
||||
}
|
||||
for _, im := range pi.CUEImports() {
|
||||
if tsim := convertImport(im); tsim != nil {
|
||||
if tsim, err := convertImport(im); err != nil {
|
||||
return nil, err
|
||||
} else if tsim.From.Value != "" {
|
||||
f.Imports = append(f.Imports, tsim)
|
||||
}
|
||||
}
|
||||
@@ -126,37 +136,36 @@ func (pt *PluginTree) GenerateTS(path string) (WriteDiffer, error) {
|
||||
for slotname, lin := range slotimps {
|
||||
v := thema.LatestVersion(lin)
|
||||
sch := thema.SchemaP(lin, v)
|
||||
// TODO need call expressions in cuetsy tsast to be able to do these
|
||||
sec := tsSection{
|
||||
V: v,
|
||||
ModelName: slotname,
|
||||
}
|
||||
// Inject a node for the const with the version
|
||||
f.Nodes = append(f.Nodes, tsast.Raw{
|
||||
// TODO need call expressions in cuetsy tsast to be able to do these properly
|
||||
Data: fmt.Sprintf("export const %sModelVersion = Object.freeze([%v, %v]);", slotname, v[0], v[1]),
|
||||
})
|
||||
|
||||
// TODO this is hardcoded for now, but should ultimately be a property of
|
||||
// whether the slot is a grouped lineage:
|
||||
// https://github.com/grafana/thema/issues/62
|
||||
if isGroupLineage(slotname) {
|
||||
b, err := cuetsy.Generate(sch.UnwrapCUE(), cuetsy.Config{})
|
||||
tsf, err := cuetsy.GenerateAST(sch.UnwrapCUE(), cuetsy.Config{
|
||||
Export: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: error translating %s lineage to TypeScript: %w", path, slotname, err)
|
||||
return nil, fmt.Errorf("error translating %s lineage to TypeScript: %w", slotname, err)
|
||||
}
|
||||
sec.Body = string(b)
|
||||
f.Nodes = append(f.Nodes, tsf.Nodes...)
|
||||
} else {
|
||||
a, err := cuetsy.GenerateSingleAST(strings.Title(lin.Name()), sch.UnwrapCUE(), cuetsy.TypeInterface)
|
||||
pair, err := cuetsy.GenerateSingleAST(strings.Title(lin.Name()), sch.UnwrapCUE(), cuetsy.TypeInterface)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: error translating %s lineage to TypeScript: %w", path, slotname, err)
|
||||
return nil, fmt.Errorf("error translating %s lineage to TypeScript: %w", slotname, err)
|
||||
}
|
||||
f.Nodes = append(f.Nodes, pair.T)
|
||||
if pair.D != nil {
|
||||
f.Nodes = append(f.Nodes, pair.D)
|
||||
}
|
||||
sec.Body = fmt.Sprint(a)
|
||||
}
|
||||
|
||||
f.Sections = append(f.Sections, sec)
|
||||
}
|
||||
|
||||
wd := NewWriteDiffer()
|
||||
var buf bytes.Buffer
|
||||
err := tmpls.Lookup("cuetsy_multi.tmpl").Execute(&buf, f)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: error executing plugin TS generator template: %w", path, err)
|
||||
}
|
||||
wd[filepath.Join(path, "models.gen.ts")] = buf.Bytes()
|
||||
return wd, nil
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func isGroupLineage(slotname string) bool {
|
||||
@@ -414,41 +423,27 @@ type TreeAndPath struct {
|
||||
}
|
||||
|
||||
// TODO convert this to use cuetsy ts types, once import * form is supported
|
||||
func convertImport(im *ast.ImportSpec) *tsImport {
|
||||
var err error
|
||||
tsim := &tsImport{}
|
||||
tsim.Pkg, err = MapCUEImportToTS(strings.Trim(im.Path.Value, "\""))
|
||||
if err != nil {
|
||||
// should be unreachable if paths has been verified already
|
||||
panic(err)
|
||||
func convertImport(im *ast.ImportSpec) (tsast.ImportSpec, error) {
|
||||
tsim := tsast.ImportSpec{}
|
||||
pkg, err := MapCUEImportToTS(strings.Trim(im.Path.Value, "\""))
|
||||
if err != nil || pkg == "" {
|
||||
// err should be unreachable if paths has been verified already
|
||||
// Empty string mapping means skip it
|
||||
return tsim, err
|
||||
}
|
||||
|
||||
if tsim.Pkg == "" {
|
||||
// Empty string mapping means skip it
|
||||
return nil
|
||||
}
|
||||
tsim.From = tsast.Str{Value: pkg}
|
||||
|
||||
if im.Name != nil && im.Name.String() != "" {
|
||||
tsim.Ident = im.Name.String()
|
||||
tsim.AsName = im.Name.String()
|
||||
} else {
|
||||
sl := strings.Split(im.Path.Value, "/")
|
||||
final := sl[len(sl)-1]
|
||||
if idx := strings.Index(final, ":"); idx != -1 {
|
||||
tsim.Pkg = final[idx:]
|
||||
tsim.AsName = final[idx:]
|
||||
} else {
|
||||
tsim.Pkg = final
|
||||
tsim.AsName = final
|
||||
}
|
||||
}
|
||||
return tsim
|
||||
}
|
||||
|
||||
type tsSection struct {
|
||||
V thema.SyntacticVersion
|
||||
ModelName string
|
||||
Body string
|
||||
}
|
||||
|
||||
type tsImport struct {
|
||||
Ident string
|
||||
Pkg string
|
||||
return tsim, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user