-
Notifications
You must be signed in to change notification settings - Fork 0
/
ts2go.go
162 lines (135 loc) · 4.07 KB
/
ts2go.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Package ts2go is a modular and highly customizable code generator for
// converting TypeScript type definitions into Golang code.
//
// The [Generate] function is the primary entry point for the package. It
// accepts a TypeScript source file as input and writes the generated Golang
// code to an output writer.
//
// The [WithMixin] function can be used to customize the data that is passed to
// the templates. This is useful for adding custom data to the parsed types
// before they are rendered.
//
// The [WithTemplateOverrideDir] function can be used to specify a directory
// that contains template overrides. This is useful for customizing the
// generated code without modifying the built-in templates. The templates used
// by this package are highly modular, allowing you to override only the parts
// that you need.
package ts2go
import (
"embed"
"fmt"
"io"
"io/fs"
"text/template"
"github.com/armsnyder/typescript-ast-go/parser"
)
//go:embed internal/templates
var templateFS embed.FS
// TemplateData is a wrapper around all of the data which is passed to the
// templates. It is used by [Mixin] functions to mutate the data before it is
// rendered.
type TemplateData struct {
SkipHeader bool
PackageName string
Structs []*Struct
TypeAliases []*TypeAlias
ConstGroups []*ConstGroup
}
// CustomData contains arbitrary data that can be used by template overrides.
type CustomData map[string]any
// Struct is the data model for the struct.tmpl template.
type Struct struct {
Name string
Doc []string
Embeds []string
Fields []*Field
CustomData CustomData
}
// Field is the data model for a field within a struct.
type Field struct {
Name string
Doc []string
Type string
IsPointer bool
JSONName string
OmitEmpty bool
CustomData CustomData
}
// TypeAlias is the data model for the type_alias.tmpl template.
type TypeAlias struct {
Name string
Doc []string
Type string
CustomData CustomData
}
// ConstGroup is the data model for the const_group.tmpl template.
type ConstGroup struct {
Doc []string
CustomData CustomData
}
var DefaultPackageName = "types"
// Generate converts the type definitions in the TypeScript source code into
// Golang and writes the result to the output writer.
func Generate(source io.Reader, output io.Writer, opts ...Option) error {
g := &generator{
source: source,
output: output,
}
for _, opt := range opts {
opt(g)
}
return g.generate()
}
type generator struct {
source io.Reader
output io.Writer
mixins []Mixin
templateOverrideFS fs.FS
}
func (g *generator) generate() error {
templateData, err := g.parseSource()
if err != nil {
return fmt.Errorf("failed to parse source: %w", err)
}
g.applyMixins(templateData)
tmpl, err := g.createTemplate()
if err != nil {
return fmt.Errorf("failed to create template: %w", err)
}
if err := g.writeOutput(tmpl, templateData); err != nil {
return fmt.Errorf("failed to write output: %w", err)
}
return nil
}
func (g *generator) parseSource() (*TemplateData, error) {
sourceBytes, err := io.ReadAll(g.source)
if err != nil {
return nil, fmt.Errorf("failed to read source: %w", err)
}
sourceFile := parser.Parse(sourceBytes)
data, err := parseSourceFile(sourceFile)
if err != nil {
return nil, fmt.Errorf("failed to parse source file into template data: %w", err)
}
return data, nil
}
func (g *generator) applyMixins(templateData *TemplateData) {
for _, mixin := range g.mixins {
mixin(templateData)
}
}
func (g *generator) createTemplate() (*template.Template, error) {
tmpl, err := template.ParseFS(templateFS, "internal/templates/*.tmpl")
if err != nil {
return nil, fmt.Errorf("failed to parse builtin templates: %w", err)
}
// TODO: Implement template overrides
_ = g.templateOverrideFS
return tmpl, nil
}
func (g *generator) writeOutput(tmpl *template.Template, templateData *TemplateData) error {
if err := tmpl.ExecuteTemplate(g.output, "output.tmpl", templateData); err != nil {
return fmt.Errorf("failed to execute template: %w", err)
}
return nil
}