From 3a30a32820290380371a293804964bab7329fc6f Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 16 May 2025 08:35:39 -0500 Subject: [PATCH 1/4] docs: update readme with latest coder guts maing.go link --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5d2ff77..0ed6b87 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ interface SimpleType { `guts` is a library, not a command line utility. This is to allow configuration with code, and also helps with package resolution. -See the [simple example](./example/simple) for a basic usage of the library. A larger example can be found in the [Coder repository](https://github.com/coder/coder/blob/a632a841d4f5666c2c1690801f88cd1a1fcffc00/scripts/apitypings/main.go). +See the [simple example](./example/simple) for a basic usage of the library. A larger example can be found in the [Coder repository](https://github.com/coder/coder/blob/main/scripts/apitypings/main.go). ```go // Step 1: Create a new Golang parser @@ -87,6 +87,14 @@ And the output is: export type EnumString = "bar" | "baz" | "foo" | "qux"; ``` +# Alternative solutions + +The guts package was created to offer a more flexible, programmatic alternative to existing Go-to-TypeScript code generation tools out there. + +The other solutions out there function as command-line utilities with yaml configurability. `guts` is a library, giving it a much more flexible and dynamic configuration that static generators can’t easily support. + +Unlike many of its counterparts, guts leverages the official TypeScript compiler under the hood, ensuring that the generated TypeScript definitions are semantically correct, syntactically valid, and aligned with the latest language features. + # Helpful notes From 54b11c8ec1c8005d29862a436b07dd31383d92fe Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 30 May 2025 10:09:24 -0500 Subject: [PATCH 2/4] feat: enums using alias types (#31) --- convert.go | 43 ++++++++++++++++++++++----------- testdata/enumtypes/enumtypes.go | 9 +++++++ testdata/enumtypes/enumtypes.ts | 5 ++++ 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/convert.go b/convert.go index 64151f3..8bfaa3d 100644 --- a/convert.go +++ b/convert.go @@ -494,26 +494,41 @@ func (ts *Typescript) parse(obj types.Object) error { // TODO: Are any enums var declarations? This is also codersdk.Me. return nil // Maybe we should treat these like consts? case *types.Const: - // Names are very likely enums - named, ok := obj.Type().(*types.Named) - if !ok { - // It could be a raw const value to generate. - if _, ok := obj.Type().(*types.Basic); ok { - cnst, err := ts.constantDeclaration(obj) - if err != nil { - return xerrors.Errorf("basic const %q: %w", objectIdentifier.Ref(), err) + type constMethods interface { + Obj() *types.TypeName + Underlying() types.Type + } + + var use constMethods + { // TODO: This block could be cleaned up + // Names & aliases are very likely enums + named, namedOk := obj.Type().(*types.Named) + aliased, aliasOk := obj.Type().(*types.Alias) + + if !namedOk && !aliasOk { + // It could be a raw const value to generate. + if _, ok := obj.Type().(*types.Basic); ok { + cnst, err := ts.constantDeclaration(obj) + if err != nil { + return xerrors.Errorf("basic const %q: %w", objectIdentifier.Ref(), err) + } + return ts.setNode(objectIdentifier.Ref(), typescriptNode{ + Node: cnst, + }) } - return ts.setNode(objectIdentifier.Ref(), typescriptNode{ - Node: cnst, - }) + return xerrors.Errorf("const %q is not a named type", objectIdentifier.Ref()) + } + if namedOk { + use = named + } else { + use = aliased } - return xerrors.Errorf("const %q is not a named type", objectIdentifier.Ref()) } // Treat it as an enum. - enumObjName := ts.parsed.Identifier(named.Obj()) + enumObjName := ts.parsed.Identifier(use.Obj()) - switch named.Underlying().(type) { + switch use.Underlying().(type) { case *types.Basic: default: return xerrors.Errorf("const %q is not a basic type, enums only support basic", objectIdentifier.Ref()) diff --git a/testdata/enumtypes/enumtypes.go b/testdata/enumtypes/enumtypes.go index d13ec85..460e13b 100644 --- a/testdata/enumtypes/enumtypes.go +++ b/testdata/enumtypes/enumtypes.go @@ -26,3 +26,12 @@ const ( AudienceTenant Audience = "tenant" AudienceTeam Audience = "team" ) + +type EnumAlias = string + +const ( + EnumAliasString EnumAlias = "string" + EnumAliasNumber EnumAlias = "number" + EnumAliasBoolean EnumAlias = "bool" + EnumAliasListString EnumAlias = "list(string)" +) diff --git a/testdata/enumtypes/enumtypes.ts b/testdata/enumtypes/enumtypes.ts index cfe2902..2771035 100644 --- a/testdata/enumtypes/enumtypes.ts +++ b/testdata/enumtypes/enumtypes.ts @@ -5,6 +5,11 @@ export type Audience = "team" | "tenant" | "world"; export const Audiences: Audience[] = ["team", "tenant", "world"]; +// From enumtypes/enumtypes.go +export type EnumAlias = "bool" | "list(string)" | "number" | "string"; + +export const EnumAliases: EnumAlias[] = ["bool", "list(string)", "number", "string"]; + // From enumtypes/enumtypes.go export type EnumInt = 10 | 5; From 641893a616211745e6be0d34ca2703fc8ee7aa4f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 12:40:46 -0500 Subject: [PATCH 3/4] chore(deps): bump golang.org/x/tools from 0.33.0 to 0.34.0 (#32) Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.33.0 to 0.34.0. - [Release notes](https://github.com/golang/tools/releases) - [Commits](https://github.com/golang/tools/compare/v0.33.0...v0.34.0) --- updated-dependencies: - dependency-name: golang.org/x/tools dependency-version: 0.34.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index a3ca2d8..cdc5839 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/dop251/goja v0.0.0-20241024094426-79f3a7efcdbd github.com/fatih/structtag v1.2.0 github.com/stretchr/testify v1.10.0 - golang.org/x/tools v0.33.0 + golang.org/x/tools v0.34.0 golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da ) @@ -18,8 +18,8 @@ require ( github.com/kr/pretty v0.3.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/sync v0.14.0 // indirect + golang.org/x/mod v0.25.0 // indirect + golang.org/x/sync v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 5021f2c..0ed6cfb 100644 --- a/go.sum +++ b/go.sum @@ -30,14 +30,14 @@ github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjR github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 9010ce7d6a3d6d11661db760136a09e415368298 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Mon, 14 Jul 2025 10:10:28 -0600 Subject: [PATCH 4/4] fix: handle consts with references to external types (#35) * fix: handle consts with references to external types Consts to external types look like enums, but fail to generate. * add comment --- convert.go | 9 ++++++++- node.go | 5 +++++ testdata/enums/enums.go | 8 ++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/convert.go b/convert.go index 8bfaa3d..09325b7 100644 --- a/convert.go +++ b/convert.go @@ -180,7 +180,14 @@ func (p *GoParser) ToTypescript() (*Typescript, error) { if err != nil { return nil, fmt.Errorf("node %q: %w", key, err) } - typescript.typescriptNodes[key] = &newNode + + // If the Node is nil, then it serves no purpose and can be + // removed from the typescriptNodes map. + if newNode.Node == nil { + delete(typescript.typescriptNodes, key) + } else { + typescript.typescriptNodes[key] = &newNode + } } return typescript, nil diff --git a/node.go b/node.go index 0bd0658..3b19a07 100644 --- a/node.go +++ b/node.go @@ -29,6 +29,11 @@ func (t typescriptNode) applyMutations() (typescriptNode, error) { func (t *typescriptNode) AddEnum(member *bindings.EnumMember) { t.mutations = append(t.mutations, func(v bindings.Node) (bindings.Node, error) { + if v == nil { + // Just delete the enum if the reference type cannot be found. + return nil, nil + } + alias, ok := v.(*bindings.Alias) if ok { // Switch to an enum diff --git a/testdata/enums/enums.go b/testdata/enums/enums.go index 82c7771..39cda22 100644 --- a/testdata/enums/enums.go +++ b/testdata/enums/enums.go @@ -1,5 +1,7 @@ package enums +import "time" + type ( EnumString string EnumSliceType []EnumString @@ -26,3 +28,9 @@ const ( AudienceTenant Audience = "tenant" AudienceTeam Audience = "team" ) + +// EmptyEnum references `time.Duration`, so the constant is considered an enum. +// However, 'time.Duration' is not a referenced type, so the enum does not exist +// in the output. +// For now, this kind of constant is ignored. +const EmptyEnum = 30 * time.Second