diff --git a/examples/globalconfig/index.ts b/examples/globalconfig/index.ts new file mode 100644 index 0000000..ce94c77 --- /dev/null +++ b/examples/globalconfig/index.ts @@ -0,0 +1,8 @@ +// Code generated by tygo. DO NOT EDIT. + +////////// +// source: globalconfig.go + +export interface Config { + Duration: number /* int, ns */; +} diff --git a/tygo.yaml b/tygo.yaml index 396bb09..39165ae 100644 --- a/tygo.yaml +++ b/tygo.yaml @@ -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: diff --git a/tygo/fixtures_test.go b/tygo/fixtures_test.go index 117856f..efe102a 100644 --- a/tygo/fixtures_test.go +++ b/tygo/fixtures_test.go @@ -78,7 +78,6 @@ func parseMarkdownFixtures(fileContents []byte) ([]MarkdownFixture, error) { } return fixtures, nil - } // Tests all markdown files in `testdata/fixtures/` directory. diff --git a/tygo/generator.go b/tygo/generator.go index 788453d..e1914da 100644 --- a/tygo/generator.go +++ b/tygo/generator.go @@ -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 } diff --git a/tygo/package_generator.go b/tygo/package_generator.go index 9af6a8a..53dc343 100644 --- a/tygo/package_generator.go +++ b/tygo/package_generator.go @@ -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 { diff --git a/tygo/testdata/fixtures/simple.md b/tygo/testdata/fixtures/simple.md index e35833c..0cb6f44 100644 --- a/tygo/testdata/fixtures/simple.md +++ b/tygo/testdata/fixtures/simple.md @@ -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 ``` \ No newline at end of file diff --git a/tygo/write.go b/tygo/write.go index a002eef..6db83d7 100644 --- a/tygo/write.go +++ b/tygo/write.go @@ -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{ @@ -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') } } diff --git a/tygo/write_comment.go b/tygo/write_comment.go index 4326d76..279f75e 100644 --- a/tygo/write_comment.go +++ b/tygo/write_comment.go @@ -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" } diff --git a/tygo/write_toplevel.go b/tygo/write_toplevel.go index 5699980..32442e3 100644 --- a/tygo/write_toplevel.go +++ b/tygo/write_toplevel.go @@ -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 ( @@ -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) }