Skip to content

Commit 2741efa

Browse files
committed
Fix ignore, add optional fields
1 parent 071026d commit 2741efa

File tree

1 file changed

+66
-38
lines changed

1 file changed

+66
-38
lines changed

scripts/apitypings/main.go

Lines changed: 66 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,14 @@ func main() {
3535
fmt.Println(codeBlocks.String())
3636
}
3737

38+
// TypescriptTypes holds all the code blocks created.
3839
type TypescriptTypes struct {
3940
// Each entry is the type name, and it's typescript code block.
4041
Types map[string]string
4142
Enums map[string]string
4243
}
4344

44-
// String just combines all the codeblocks. I store them in a map for unit testing purposes
45+
// String just combines all the codeblocks.
4546
func (t TypescriptTypes) String() string {
4647
var s strings.Builder
4748
sortedTypes := make([]string, 0, len(t.Types))
@@ -122,19 +123,25 @@ func (g *Generator) parsePackage(ctx context.Context, patterns ...string) error
122123
func (g *Generator) generateAll() (*TypescriptTypes, error) {
123124
structs := make(map[string]string)
124125
enums := make(map[string]types.Object)
125-
constants := make(map[string][]*types.Const)
126+
enumConsts := make(map[string][]*types.Const)
127+
//constants := make(map[string]string)
126128

129+
// Look for comments that indicate to ignore a type for typescript generation.
127130
ignoredTypes := make(map[string]struct{})
128-
ignoreRegex := regexp.MustCompile("@typescript-ignore:(?P<ignored_types>)")
131+
ignoreRegex := regexp.MustCompile("@typescript-ignore[:]?(?P<ignored_types>.*)")
129132
for _, file := range g.pkg.Syntax {
130133
for _, comment := range file.Comments {
131-
matches := ignoreRegex.FindStringSubmatch(comment.Text())
132-
ignored := ignoreRegex.SubexpIndex("ignored_types")
133-
if len(matches) >= ignored && matches[ignored] != "" {
134-
arr := strings.Split(matches[ignored], ",")
135-
for _, s := range arr {
136-
ignoredTypes[strings.TrimSpace(s)] = struct{}{}
134+
for _, line := range comment.List {
135+
text := line.Text
136+
matches := ignoreRegex.FindStringSubmatch(text)
137+
ignored := ignoreRegex.SubexpIndex("ignored_types")
138+
if len(matches) >= ignored && matches[ignored] != "" {
139+
arr := strings.Split(matches[ignored], ",")
140+
for _, s := range arr {
141+
ignoredTypes[strings.TrimSpace(s)] = struct{}{}
142+
}
137143
}
144+
138145
}
139146
}
140147
}
@@ -146,6 +153,7 @@ func (g *Generator) generateAll() (*TypescriptTypes, error) {
146153
continue
147154
}
148155

156+
// Exclude ignored types
149157
if _, ok := ignoredTypes[obj.Name()]; ok {
150158
continue
151159
}
@@ -171,24 +179,28 @@ func (g *Generator) generateAll() (*TypescriptTypes, error) {
171179
enums[obj.Name()] = obj
172180
}
173181
case *types.Var:
174-
// TODO: Are any enums var declarations?
182+
// TODO: Are any enums var declarations? This is also codersdk.Me.
175183
v := obj.(*types.Var)
176184
var _ = v
177185
case *types.Const:
178186
c := obj.(*types.Const)
179187
// We only care about named constant types, since they are enums
180188
if named, ok := c.Type().(*types.Named); ok {
181189
name := named.Obj().Name()
182-
constants[name] = append(constants[name], c)
190+
enumConsts[name] = append(enumConsts[name], c)
183191
}
192+
case *types.Func:
193+
// Noop
194+
default:
195+
fmt.Println(obj.Name())
184196
}
185197
}
186198

187199
// Write all enums
188200
enumCodeBlocks := make(map[string]string)
189201
for name, v := range enums {
190202
var values []string
191-
for _, elem := range constants[name] {
203+
for _, elem := range enumConsts[name] {
192204
// TODO: If we have non string constants, we need to handle that
193205
// here.
194206
values = append(values, elem.Val().String())
@@ -236,37 +248,47 @@ func (g *Generator) buildStruct(obj types.Object, st *types.Struct) (string, err
236248
jsonName = field.Name()
237249
}
238250

239-
var tsType string
240-
var comment string
251+
var tsType TypescriptType
241252
// If a `typescript:"string"` exists, we take this, and do not try to infer.
242253
typescriptTag := tag.Get("typescript")
243254
if typescriptTag == "-" {
244255
// Ignore this field
245256
continue
246257
} else if typescriptTag != "" {
247-
tsType = typescriptTag
258+
tsType.ValueType = typescriptTag
248259
} else {
249260
var err error
250-
tsType, comment, err = g.typescriptType(obj, field.Type())
261+
tsType, err = g.typescriptType(obj, field.Type())
251262
if err != nil {
252263
return "", xerrors.Errorf("typescript type: %w", err)
253264
}
254265
}
255266

256-
if comment != "" {
257-
s.WriteString(fmt.Sprintf("\t// %s\n", comment))
267+
if tsType.Comment != "" {
268+
s.WriteString(fmt.Sprintf("\t// %s\n", tsType.Comment))
269+
}
270+
optional := ""
271+
if tsType.Optional {
272+
optional = "?"
258273
}
259-
s.WriteString(fmt.Sprintf("\treadonly %s: %s\n", jsonName, tsType))
274+
s.WriteString(fmt.Sprintf("\treadonly %s%s: %s\n", jsonName, optional, tsType.ValueType))
260275
}
261276
s.WriteString("}\n")
262277
return s.String(), nil
263278
}
264279

280+
type TypescriptType struct {
281+
ValueType string
282+
Comment string
283+
// Optional indicates the value is an optional field in typescript.
284+
Optional bool
285+
}
286+
265287
// typescriptType this function returns a typescript type for a given
266288
// golang type.
267289
// Eg:
268290
// []byte returns "string"
269-
func (g *Generator) typescriptType(obj types.Object, ty types.Type) (string, string, error) {
291+
func (g *Generator) typescriptType(obj types.Object, ty types.Type) (TypescriptType, error) {
270292
switch ty.(type) {
271293
case *types.Basic:
272294
bs := ty.(*types.Basic)
@@ -275,22 +297,22 @@ func (g *Generator) typescriptType(obj types.Object, ty types.Type) (string, str
275297
// we want to put another switch to capture these types
276298
// and rename to typescript.
277299
switch {
278-
case bs.Info() == types.IsNumeric:
279-
return "number", "", nil
280-
case bs.Info() == types.IsBoolean:
281-
return "boolean", "", nil
300+
case bs.Info()&types.IsNumeric > 0:
301+
return TypescriptType{ValueType: "number"}, nil
302+
case bs.Info()&types.IsBoolean > 0:
303+
return TypescriptType{ValueType: "boolean"}, nil
282304
case bs.Kind() == types.Byte:
283305
// TODO: @emyrk What is a byte for typescript? A string? A uint8?
284-
return "byte", "", nil
306+
return TypescriptType{ValueType: "number", Comment: "This is a byte in golang"}, nil
285307
default:
286-
return bs.Name(), "", nil
308+
return TypescriptType{ValueType: bs.Name()}, nil
287309
}
288310
case *types.Struct:
289311
// TODO: This kinda sucks right now. It just dumps the struct def
290-
return ty.String(), "Unknown struct, this might not work", nil
312+
return TypescriptType{ValueType: ty.String(), Comment: "Unknown struct, this might not work"}, nil
291313
case *types.Map:
292314
// TODO: Typescript dictionary??? Object?
293-
return "map_not_implemented", "", nil
315+
return TypescriptType{ValueType: "map_not_implemented"}, nil
294316
case *types.Slice, *types.Array:
295317
// Slice/Arrays are pretty much the same.
296318
type hasElem interface {
@@ -304,14 +326,14 @@ func (g *Generator) typescriptType(obj types.Object, ty types.Type) (string, str
304326
case arr.Elem().String() == "byte":
305327
// All byte arrays are strings on the typescript.
306328
// Is this ok?
307-
return "string", "", nil
329+
return TypescriptType{ValueType: "string"}, nil
308330
default:
309331
// By default, just do an array of the underlying type.
310-
underlying, comment, err := g.typescriptType(obj, arr.Elem())
332+
underlying, err := g.typescriptType(obj, arr.Elem())
311333
if err != nil {
312-
return "", "", xerrors.Errorf("array: %w", err)
334+
return TypescriptType{}, xerrors.Errorf("array: %w", err)
313335
}
314-
return underlying + "[]", comment, nil
336+
return TypescriptType{ValueType: underlying.ValueType + "[]", Comment: underlying.Comment}, nil
315337
}
316338
case *types.Named:
317339
n := ty.(*types.Named)
@@ -322,20 +344,21 @@ func (g *Generator) typescriptType(obj types.Object, ty types.Type) (string, str
322344
if obj := g.pkg.Types.Scope().Lookup(name); obj != nil {
323345
// Sweet! Using other typescript types as fields. This could be an
324346
// enum or another struct
325-
return name, "", nil
347+
return TypescriptType{ValueType: name}, nil
326348
}
327349

328350
// These are special types that we handle uniquely.
329351
switch n.String() {
330352
case "net/url.URL":
331-
return "string", "", nil
353+
return TypescriptType{ValueType: "string"}, nil
332354
case "time.Time":
333-
return "string", "is this ok for time?", nil
355+
// We really should come up with a standard for time.
356+
return TypescriptType{ValueType: "string"}, nil
334357
}
335358

336359
// If it's a struct, just use the name of the struct type
337360
if _, ok := n.Underlying().(*types.Struct); ok {
338-
return name, "Unknown named type, this might not work", nil
361+
return TypescriptType{ValueType: name, Comment: "Unknown named type, this might not work"}, nil
339362
}
340363

341364
// Defer to the underlying type.
@@ -345,10 +368,15 @@ func (g *Generator) typescriptType(obj types.Object, ty types.Type) (string, str
345368
// TODO: Nullable fields? We could say these fields can be null in the
346369
// typescript.
347370
pt := ty.(*types.Pointer)
348-
return g.typescriptType(obj, pt.Elem())
371+
resp, err := g.typescriptType(obj, pt.Elem())
372+
if err != nil {
373+
return TypescriptType{}, xerrors.Errorf("pointer: %w", err)
374+
}
375+
resp.Optional = true
376+
return resp, nil
349377
}
350378

351379
// These are all the other types we need to support.
352380
// time.Time, uuid, etc.
353-
return "", "", xerrors.Errorf("unknown type: %s", ty.String())
381+
return TypescriptType{}, xerrors.Errorf("unknown type: %s", ty.String())
354382
}

0 commit comments

Comments
 (0)