@@ -35,13 +35,14 @@ func main() {
35
35
fmt .Println (codeBlocks .String ())
36
36
}
37
37
38
+ // TypescriptTypes holds all the code blocks created.
38
39
type TypescriptTypes struct {
39
40
// Each entry is the type name, and it's typescript code block.
40
41
Types map [string ]string
41
42
Enums map [string ]string
42
43
}
43
44
44
- // String just combines all the codeblocks. I store them in a map for unit testing purposes
45
+ // String just combines all the codeblocks.
45
46
func (t TypescriptTypes ) String () string {
46
47
var s strings.Builder
47
48
sortedTypes := make ([]string , 0 , len (t .Types ))
@@ -122,19 +123,25 @@ func (g *Generator) parsePackage(ctx context.Context, patterns ...string) error
122
123
func (g * Generator ) generateAll () (* TypescriptTypes , error ) {
123
124
structs := make (map [string ]string )
124
125
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)
126
128
129
+ // Look for comments that indicate to ignore a type for typescript generation.
127
130
ignoredTypes := make (map [string ]struct {})
128
- ignoreRegex := regexp .MustCompile ("@typescript-ignore: (?P<ignored_types>)" )
131
+ ignoreRegex := regexp .MustCompile ("@typescript-ignore[:]? (?P<ignored_types>.* )" )
129
132
for _ , file := range g .pkg .Syntax {
130
133
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
+ }
137
143
}
144
+
138
145
}
139
146
}
140
147
}
@@ -146,6 +153,7 @@ func (g *Generator) generateAll() (*TypescriptTypes, error) {
146
153
continue
147
154
}
148
155
156
+ // Exclude ignored types
149
157
if _ , ok := ignoredTypes [obj .Name ()]; ok {
150
158
continue
151
159
}
@@ -171,24 +179,28 @@ func (g *Generator) generateAll() (*TypescriptTypes, error) {
171
179
enums [obj .Name ()] = obj
172
180
}
173
181
case * types.Var :
174
- // TODO: Are any enums var declarations?
182
+ // TODO: Are any enums var declarations? This is also codersdk.Me.
175
183
v := obj .(* types.Var )
176
184
var _ = v
177
185
case * types.Const :
178
186
c := obj .(* types.Const )
179
187
// We only care about named constant types, since they are enums
180
188
if named , ok := c .Type ().(* types.Named ); ok {
181
189
name := named .Obj ().Name ()
182
- constants [name ] = append (constants [name ], c )
190
+ enumConsts [name ] = append (enumConsts [name ], c )
183
191
}
192
+ case * types.Func :
193
+ // Noop
194
+ default :
195
+ fmt .Println (obj .Name ())
184
196
}
185
197
}
186
198
187
199
// Write all enums
188
200
enumCodeBlocks := make (map [string ]string )
189
201
for name , v := range enums {
190
202
var values []string
191
- for _ , elem := range constants [name ] {
203
+ for _ , elem := range enumConsts [name ] {
192
204
// TODO: If we have non string constants, we need to handle that
193
205
// here.
194
206
values = append (values , elem .Val ().String ())
@@ -236,37 +248,47 @@ func (g *Generator) buildStruct(obj types.Object, st *types.Struct) (string, err
236
248
jsonName = field .Name ()
237
249
}
238
250
239
- var tsType string
240
- var comment string
251
+ var tsType TypescriptType
241
252
// If a `typescript:"string"` exists, we take this, and do not try to infer.
242
253
typescriptTag := tag .Get ("typescript" )
243
254
if typescriptTag == "-" {
244
255
// Ignore this field
245
256
continue
246
257
} else if typescriptTag != "" {
247
- tsType = typescriptTag
258
+ tsType . ValueType = typescriptTag
248
259
} else {
249
260
var err error
250
- tsType , comment , err = g .typescriptType (obj , field .Type ())
261
+ tsType , err = g .typescriptType (obj , field .Type ())
251
262
if err != nil {
252
263
return "" , xerrors .Errorf ("typescript type: %w" , err )
253
264
}
254
265
}
255
266
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 = "?"
258
273
}
259
- s .WriteString (fmt .Sprintf ("\t readonly %s: %s\n " , jsonName , tsType ))
274
+ s .WriteString (fmt .Sprintf ("\t readonly %s%s : %s\n " , jsonName , optional , tsType . ValueType ))
260
275
}
261
276
s .WriteString ("}\n " )
262
277
return s .String (), nil
263
278
}
264
279
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
+
265
287
// typescriptType this function returns a typescript type for a given
266
288
// golang type.
267
289
// Eg:
268
290
// []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 ) {
270
292
switch ty .(type ) {
271
293
case * types.Basic :
272
294
bs := ty .(* types.Basic )
@@ -275,22 +297,22 @@ func (g *Generator) typescriptType(obj types.Object, ty types.Type) (string, str
275
297
// we want to put another switch to capture these types
276
298
// and rename to typescript.
277
299
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
282
304
case bs .Kind () == types .Byte :
283
305
// 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
285
307
default :
286
- return bs .Name (), "" , nil
308
+ return TypescriptType { ValueType : bs .Name ()} , nil
287
309
}
288
310
case * types.Struct :
289
311
// 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
291
313
case * types.Map :
292
314
// TODO: Typescript dictionary??? Object?
293
- return "map_not_implemented" , "" , nil
315
+ return TypescriptType { ValueType : "map_not_implemented" } , nil
294
316
case * types.Slice , * types.Array :
295
317
// Slice/Arrays are pretty much the same.
296
318
type hasElem interface {
@@ -304,14 +326,14 @@ func (g *Generator) typescriptType(obj types.Object, ty types.Type) (string, str
304
326
case arr .Elem ().String () == "byte" :
305
327
// All byte arrays are strings on the typescript.
306
328
// Is this ok?
307
- return "string" , "" , nil
329
+ return TypescriptType { ValueType : "string" } , nil
308
330
default :
309
331
// 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 ())
311
333
if err != nil {
312
- return "" , "" , xerrors .Errorf ("array: %w" , err )
334
+ return TypescriptType {} , xerrors .Errorf ("array: %w" , err )
313
335
}
314
- return underlying + "[]" , comment , nil
336
+ return TypescriptType { ValueType : underlying . ValueType + "[]" , Comment : underlying . Comment } , nil
315
337
}
316
338
case * types.Named :
317
339
n := ty .(* types.Named )
@@ -322,20 +344,21 @@ func (g *Generator) typescriptType(obj types.Object, ty types.Type) (string, str
322
344
if obj := g .pkg .Types .Scope ().Lookup (name ); obj != nil {
323
345
// Sweet! Using other typescript types as fields. This could be an
324
346
// enum or another struct
325
- return name , "" , nil
347
+ return TypescriptType { ValueType : name } , nil
326
348
}
327
349
328
350
// These are special types that we handle uniquely.
329
351
switch n .String () {
330
352
case "net/url.URL" :
331
- return "string" , "" , nil
353
+ return TypescriptType { ValueType : "string" } , nil
332
354
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
334
357
}
335
358
336
359
// If it's a struct, just use the name of the struct type
337
360
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
339
362
}
340
363
341
364
// Defer to the underlying type.
@@ -345,10 +368,15 @@ func (g *Generator) typescriptType(obj types.Object, ty types.Type) (string, str
345
368
// TODO: Nullable fields? We could say these fields can be null in the
346
369
// typescript.
347
370
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
349
377
}
350
378
351
379
// These are all the other types we need to support.
352
380
// time.Time, uuid, etc.
353
- return "" , "" , xerrors .Errorf ("unknown type: %s" , ty .String ())
381
+ return TypescriptType {} , xerrors .Errorf ("unknown type: %s" , ty .String ())
354
382
}
0 commit comments