Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions examples/globalconfig/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Code generated by tygo. DO NOT EDIT.

//////////
// source: globalconfig.go

export interface Config {
Duration: number /* int, ns */;
}
2 changes: 2 additions & 0 deletions tygo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ packages:
export type Something = string | number;
- path: "github.com/gzuidhof/tygo/examples/simple"
fallback_type: unknown
type_mappings:
time.Time: "string /* RFC3339 */"
- path: "github.com/gzuidhof/tygo/examples/inheritance"
fallback_type: unknown
frontmatter:
Expand Down
1 change: 0 additions & 1 deletion tygo/fixtures_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ func parseMarkdownFixtures(fileContents []byte) ([]MarkdownFixture, error) {
}

return fixtures, nil

}

// Tests all markdown files in `testdata/fixtures/` directory.
Expand Down
2 changes: 1 addition & 1 deletion tygo/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func (g *Tygo) Generate() error {
return nil
}

err = os.WriteFile(outPath, []byte(code), 0664)
err = os.WriteFile(outPath, []byte(code), 0o664)
if err != nil {
return nil
}
Expand Down
1 change: 0 additions & 1 deletion tygo/package_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ func (g *PackageGenerator) generateFile(s *strings.Builder, file *ast.File, file

ast.Inspect(file, func(n ast.Node) bool {
switch x := n.(type) {

// GenDecl can be an import, type, var, or const expression
case *ast.GenDecl:
if x.Tok == token.IMPORT {
Expand Down
63 changes: 63 additions & 0 deletions tygo/testdata/fixtures/simple.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,71 @@ Empty file
Unexported

```go
// A comment on an unexported constant
const myValue = 3

// A comment on an unexported type
type myType struct {
// A comment on an unexported field
field string
}

// Mixed unexported and exported
const (
unexportedValue = 7 // A comment on an unexported constant
ExportedValue = 42 // A comment on an exported constant
)

// Unexported group
const (
unexportedValue1 = 1 // A comment on an unexported constant
unexportedValue2 = 2 // Another comment on an unexported constant
)
```

```ts
/**
* Mixed unexported and exported
*/
export const ExportedValue = 42; // A comment on an exported constant
```


Comma

```go
type A struct {
Foo, Bar, baz string // A comment on fields separated by a comma
}

type B struct {
// A comment above the fields separated by a comma
Foo, Bar, baz string
}


```
```ts
export interface A {
Foo: string; // A comment on fields separated by a comma
Bar: string; // A comment on fields separated by a comma
}
export interface B {
/**
* A comment above the fields separated by a comma
*/
Foo: string;
/**
* A comment above the fields separated by a comma
*/
Bar: string;
}
```

```go
const Pi, E = 3.14, 2.71 // A comment on constants separated by a comma
```
```ts
export const Pi = 3.14; // A comment on constants separated by a comma
export const E = 2.71; // A comment on constants separated by a comma
```
175 changes: 91 additions & 84 deletions tygo/write.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ import (
"github.com/fatih/structtag"
)

var validJSNameRegexp = regexp.MustCompile(`(?m)^[\pL_][\pL\pN_]*$`)
var backquoteEscapeRegexp = regexp.MustCompile(`([$\\])`)
var octalPrefixRegexp = regexp.MustCompile(`^0[0-7]`)
var unicode8Regexp = regexp.MustCompile(`\\\\|\\U[\da-fA-F]{8}`)
var (
validJSNameRegexp = regexp.MustCompile(`(?m)^[\pL_][\pL\pN_]*$`)
backquoteEscapeRegexp = regexp.MustCompile(`([$\\])`)
octalPrefixRegexp = regexp.MustCompile(`^0[0-7]`)
unicode8Regexp = regexp.MustCompile(`\\\\|\\U[\da-fA-F]{8}`)
)

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_precedence#table
var jsNumberOperatorPrecedence = map[token.Token]int{
Expand Down Expand Up @@ -292,110 +294,115 @@ func (g *PackageGenerator) writeStructFields(s *strings.Builder, fields []*ast.F
required := false
readonly := false

var fieldName string
fieldNames := make([]string, 0, len(f.Names))
if len(f.Names) == 0 { // anonymous field
if name, valid := getAnonymousFieldName(f.Type); valid {
fieldName = name
fieldNames = append(fieldNames, name)
}
} else {
for _, name := range f.Names {
if len(name.Name) == 0 || 'A' > name.Name[0] || name.Name[0] > 'Z' {
continue
}
fieldNames = append(fieldNames, name.Name)
}
}
if len(f.Names) != 0 && f.Names[0] != nil && len(f.Names[0].Name) != 0 {
fieldName = f.Names[0].Name
}
if len(fieldName) == 0 || 'A' > fieldName[0] || fieldName[0] > 'Z' {
continue
}

var name string
var tstype string
if f.Tag != nil {
tags, err := structtag.Parse(f.Tag.Value[1 : len(f.Tag.Value)-1])
if err != nil {
panic(err)
}
for _, fieldName := range fieldNames {

jsonTag, err := tags.Get("json")
if err == nil {
name = jsonTag.Name
if name == "-" {
continue
var name string
var tstype string
if f.Tag != nil {
tags, err := structtag.Parse(f.Tag.Value[1 : len(f.Tag.Value)-1])
if err != nil {
panic(err)
}

optional = jsonTag.HasOption("omitempty") || jsonTag.HasOption("omitzero")
}
yamlTag, err := tags.Get("yaml")
if err == nil {
name = yamlTag.Name
if name == "-" {
continue
jsonTag, err := tags.Get("json")
if err == nil {
name = jsonTag.Name
if name == "-" {
continue
}

optional = jsonTag.HasOption("omitempty") || jsonTag.HasOption("omitzero")
}
yamlTag, err := tags.Get("yaml")
if err == nil {
name = yamlTag.Name
if name == "-" {
continue
}

optional = yamlTag.HasOption("omitempty")
}

optional = yamlTag.HasOption("omitempty")
tstypeTag, err := tags.Get("tstype")
if err == nil {
tstype = tstypeTag.Name
if tstype == "-" || tstypeTag.HasOption("extends") {
continue
}
required = tstypeTag.HasOption("required")
readonly = tstypeTag.HasOption("readonly")
}
}

tstypeTag, err := tags.Get("tstype")
if err == nil {
tstype = tstypeTag.Name
if tstype == "-" || tstypeTag.HasOption("extends") {
continue
if len(name) == 0 {
if g.conf.Flavor == "yaml" {
name = strings.ToLower(fieldName)
} else {
name = fieldName
}
required = tstypeTag.HasOption("required")
readonly = tstypeTag.HasOption("readonly")
}
}

if len(name) == 0 {
if g.conf.Flavor == "yaml" {
name = strings.ToLower(fieldName)
} else {
name = fieldName
if g.PreserveTypeComments() {
g.writeCommentGroupIfNotNil(s, f.Doc, depth+1)
}
}

if g.PreserveTypeComments() {
g.writeCommentGroupIfNotNil(s, f.Doc, depth+1)
}
g.writeIndent(s, depth+1)
quoted := !validJSName(name)
if quoted {
s.WriteByte('\'')
}
if readonly {
s.WriteString("readonly ")
}
s.WriteString(name)
if quoted {
s.WriteByte('\'')
}

g.writeIndent(s, depth+1)
quoted := !validJSName(name)
if quoted {
s.WriteByte('\'')
}
if readonly {
s.WriteString("readonly ")
}
s.WriteString(name)
if quoted {
s.WriteByte('\'')
}
switch t := f.Type.(type) {
case *ast.StarExpr:
optional = !required
f.Type = t.X
}

switch t := f.Type.(type) {
case *ast.StarExpr:
optional = !required
f.Type = t.X
}
if optional && g.conf.OptionalType == "undefined" {
s.WriteByte('?')
}

if optional && g.conf.OptionalType == "undefined" {
s.WriteByte('?')
}
s.WriteString(": ")

s.WriteString(": ")
if tstype == "" {
g.writeType(s, f.Type, nil, depth, false)
if optional && g.conf.OptionalType == "null" {
s.WriteString(" | null")
}
} else {
s.WriteString(tstype)
}
s.WriteByte(';')

if tstype == "" {
g.writeType(s, f.Type, nil, depth, false)
if optional && g.conf.OptionalType == "null" {
s.WriteString(" | null")
if f.Comment != nil && g.PreserveTypeComments() {
// Line comment is present, that means a comment after the field.
s.WriteString(" // ")
s.WriteString(f.Comment.Text())
} else {
s.WriteByte('\n')
}
} else {
s.WriteString(tstype)
}
s.WriteByte(';')

if f.Comment != nil && g.PreserveTypeComments() {
// Line comment is present, that means a comment after the field.
s.WriteString(" // ")
s.WriteString(f.Comment.Text())
} else {
s.WriteByte('\n')
}

}
Expand Down
4 changes: 4 additions & 0 deletions tygo/write_comment.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ import (
"strings"
)

// PreserveDocComments returns true if doc comments should be preserved.
// These are comments that are not associated with a type or function, but rather
// with the package or file itself.
func (g *PackageGenerator) PreserveDocComments() bool {
return g.conf.PreserveComments == "default"
}

// PreserveTypeComments returns true if type comments should be preserved.
func (g *PackageGenerator) PreserveTypeComments() bool {
return g.conf.PreserveComments == "types" || g.conf.PreserveComments == "default"
}
Expand Down
11 changes: 11 additions & 0 deletions tygo/write_toplevel.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func (g *PackageGenerator) emitVar(s *strings.Builder, dec *ast.GenDecl) {
}
s.WriteString(v[1:len(v)-1] + "\n")
}

func (g *PackageGenerator) writeGroupDecl(s *strings.Builder, decl *ast.GenDecl) {
// This checks whether the declaration is a group declaration like:
// const (
Expand All @@ -53,6 +54,16 @@ func (g *PackageGenerator) writeGroupDecl(s *strings.Builder, decl *ast.GenDecl)
// )
isGroupedDeclaration := len(decl.Specs) > 1

// Check if decl is exported, if not, we exit early so we don't write its comment.
if !isGroupedDeclaration {
if ts, ok := decl.Specs[0].(*ast.TypeSpec); ok && !ts.Name.IsExported() {
return
}
if vs, ok := decl.Specs[0].(*ast.ValueSpec); ok && !vs.Names[0].IsExported() {
return
}
}

if !isGroupedDeclaration && g.PreserveTypeComments() {
g.writeCommentGroupIfNotNil(s, decl.Doc, 0)
}
Expand Down