diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml index 695ef10..573d291 100644 --- a/.github/workflows/goreleaser.yml +++ b/.github/workflows/goreleaser.yml @@ -3,24 +3,32 @@ name: goreleaser on: pull_request: push: + + +permissions: + contents: write jobs: goreleaser: runs-on: ubuntu-latest + env: + flags: "" steps: + - if: ${{ !startsWith(github.ref, 'refs/tags/v') }} + run: echo "flags=--snapshot" >> $GITHUB_ENV # See https://github.com/goreleaser/goreleaser-action/issues/387 - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v5 with: go-version: "^1.18" - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v2 + uses: goreleaser/goreleaser-action@v6 with: distribution: goreleaser - version: latest - args: release --rm-dist + version: '~> v2' + args: release --clean ${{ env.flags }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index aab7caf..e997001 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,5 @@ examples/time examples/tygo examples/http + +dist/ \ No newline at end of file diff --git a/README.md b/README.md index 33dd32a..8af9711 100644 --- a/README.md +++ b/README.md @@ -4,15 +4,12 @@ Tygo is a tool for generating Typescript typings from Golang source files that j It preserves comments, understands constants and also supports non-struct `type` expressions. It's perfect for generating equivalent types for a Golang REST API to be used in your front-end codebase. -**🚀 Now supports Golang 1.18 generic types, struct inheritance** +**🚀 Supports Golang 1.18 generic types and struct inheritance** ## Installation ```shell -# Go >= 1.17 go install github.com/gzuidhof/tygo@latest -# Go < 1.17: -go install github.com/gzuidhof/tygo ``` ## Example @@ -395,6 +392,7 @@ export interface ABCD< ## YAML support Tygo supports generating typings for YAML-serializable objects that can be understood by Go apps. + By default, Tygo will respect `yaml` Go struct tags, in addition to `json`, but it will not apply any transformations to untagged fields. However, the default behavior of the popular `gopkg.in/yaml.v2` package for Go structs without tags is to downcase the struct field names. To emulate this behavior, one can use the `flavor` configuration option: @@ -424,10 +422,10 @@ export interface Foo { ## Related projects -- [**typescriptify-golang-structs**](https://github.com/tkrajina/typescriptify-golang-structs): Probably the most popular choice. The downside of this package is that it relies on reflection rather than parsing, which means that certain things can't be kept such as comments without adding a bunch of tags to your structs. The CLI generates a Go file which is then executed and reflected on, and its library requires you to manually specify all types that should be converted. -- [**go2ts**](https://github.com/StirlingMarketingGroup/go2ts): A transpiler with a web interface, this project was based off this project. It's perfect for quick one-off transpilations. There is no CLI, no support for `const` and there are no ways to customize the output. +- [**typescriptify-golang-structs**](https://github.com/tkrajina/typescriptify-golang-structs): Probably the most popular choice. The downside of this package is that it relies on reflection rather than parsing, which means that certain things can't be kept such as comments without adding a bunch of tags to your structs. The CLI generates a Go file which is then executed and reflected on. The library requires you to manually specify all types that should be converted. +- [**go2ts**](https://github.com/StirlingMarketingGroup/go2ts): A transpiler with a web interface, this project can be seen as an evolution of this project. It's perfect for quick one-off transpilations. There is no CLI, no support for `const` and there are no ways to customize the output. -If this repository was useful for your project, consider leaving a star. +**If `tygo` is useful for your project, consider leaving a star.** ## License diff --git a/goreleaser.yml b/goreleaser.yml index 445415a..6f459e9 100644 --- a/goreleaser.yml +++ b/goreleaser.yml @@ -1,3 +1,4 @@ +version: 2 project_name: tygo builds: - binary: tygo @@ -7,6 +8,7 @@ builds: - linux goarch: - amd64 + - arm64 ldflags: - -s -w -X github.com/gzuidhof/tygo/cmd.version={{.Version}} -X github.com/gzuidhof/tygo/cmd.commit={{.Commit}} -X github.com/gzuidhof/tygo/cmd.commitDate={{.CommitDate}} archives: diff --git a/tygo/config.go b/tygo/config.go index c56d51a..0505450 100644 --- a/tygo/config.go +++ b/tygo/config.go @@ -53,6 +53,12 @@ type PackageConfig struct { // Default interface for Typescript-generated interfaces to extend. Extends string `yaml:"extends"` + + // Set the optional type (null or undefined). + // Supported values: "default", "undefined" (same as "default"), "" (same as "default"), "null". + // Default is "undefined". + // Useful for usage with JSON marshalers that output null for optional fields (e.g. gofiber JSON). + OptionalType string `yaml:"optional_type"` } type Config struct { @@ -85,6 +91,12 @@ func (c Config) PackageConfig(packagePath string) *PackageConfig { if err != nil { log.Fatalf("Invalid config for package %s: %s", packagePath, err) } + + pc.OptionalType, err = normalizeOptionalType(pc.OptionalType) + if err != nil { + log.Fatalf("Invalid config for package %s: %s", packagePath, err) + } + return pc } } @@ -116,6 +128,17 @@ func normalizePreserveComments(preserveComments string) (string, error) { } } +func normalizeOptionalType(optional string) (string, error) { + switch optional { + case "", "default", "undefined": + return "undefined", nil + case "null": + return "null", nil + default: + return "", fmt.Errorf("unsupported optional: %s", optional) + } +} + func (c PackageConfig) IsFileIgnored(pathToFile string) bool { basename := filepath.Base(pathToFile) for _, ef := range c.ExcludeFiles { diff --git a/tygo/write.go b/tygo/write.go index 0db9e00..6dc6a42 100644 --- a/tygo/write.go +++ b/tygo/write.go @@ -373,7 +373,7 @@ func (g *PackageGenerator) writeStructFields(s *strings.Builder, fields []*ast.F f.Type = t.X } - if optional { + if optional && g.conf.OptionalType == "undefined" { s.WriteByte('?') } @@ -381,6 +381,9 @@ func (g *PackageGenerator) writeStructFields(s *strings.Builder, fields []*ast.F if tstype == "" { g.writeType(s, f.Type, nil, depth, false) + if optional && g.conf.OptionalType == "null" { + s.WriteString(" | null") + } } else { s.WriteString(tstype) }