diff --git a/bindings/bindings.go b/bindings/bindings.go index ede3a01..517fef1 100644 --- a/bindings/bindings.go +++ b/bindings/bindings.go @@ -2,6 +2,7 @@ package bindings import ( "fmt" + "strings" "github.com/dop251/goja" "golang.org/x/xerrors" @@ -733,8 +734,47 @@ func (b *Bindings) CommentGojaObject(comments []SyntheticComment, object *goja.O return nil, err } - node := object + // Group all comments that should be included into a JSDoc block. + jsDoc := make([]SyntheticComment, 0) + rest := make([]SyntheticComment, 0) + for _, c := range comments { + if !c.DoNotFormat && c.Leading && c.SingleLine { + jsDoc = append(jsDoc, c) + continue + } + rest = append(rest, c) + } + + // JSDoc comments should be blocked together + node := object + if len(jsDoc) > 0 { + var jsDocComment strings.Builder + // JSDoc requires '/**' start and ' */' end. The default synthetic comment only places 1 '*'. + // So include the second '*', and start the comment line. + jsDocComment.WriteString("*\n *") + sep := "" + for _, cmt := range jsDoc { + jsDocComment.WriteString(sep) + jsDocComment.WriteString(cmt.Text) + sep = "\n *" + } + jsDocComment.WriteString("\n ") + + res, err := commentF(goja.Undefined(), + node, + b.vm.ToValue(true), + b.vm.ToValue(false), + b.vm.ToValue(jsDocComment.String()), + b.vm.ToValue(true), + ) + if err != nil { + return nil, xerrors.Errorf("call addSyntheticComment for JSDoc: %w", err) + } + node = res.ToObject(b.vm) + } + + for _, c := range rest { res, err := commentF(goja.Undefined(), node, b.vm.ToValue(c.Leading), diff --git a/bindings/comments.go b/bindings/comments.go index 599c13f..cf60b19 100644 --- a/bindings/comments.go +++ b/bindings/comments.go @@ -19,13 +19,16 @@ type SyntheticComment struct { SingleLine bool Text string TrailingNewLine bool + + // DoNotFormat indicates this comment should not be attempted to be reformatted. + // Guts will attempt to format comments into JSDoc style comments where possible. + DoNotFormat bool } type SupportComments struct { comments []SyntheticComment } -// LeadingComment is a helper function for the most common type of comment. func (s *SupportComments) LeadingComment(text string) { s.AppendComment(SyntheticComment{ Leading: true, @@ -33,6 +36,7 @@ func (s *SupportComments) LeadingComment(text string) { // All go comments are `// ` prefixed, so add a space. Text: " " + text, TrailingNewLine: false, + DoNotFormat: true, }) } @@ -66,5 +70,6 @@ func (s Source) SourceComment() (SyntheticComment, bool) { SingleLine: true, Text: fmt.Sprintf(" From %s", s.File), TrailingNewLine: false, + DoNotFormat: true, }, s.File != "" } diff --git a/config/mutations.go b/config/mutations.go index 7035516..ae26b43 100644 --- a/config/mutations.go +++ b/config/mutations.go @@ -417,3 +417,24 @@ func isGoEnum(n bindings.Node) (*bindings.Alias, *bindings.UnionType, bool) { return al, union, true } + +// NoJSDocTransform prevents `guts` from reformatting Golang comments to JSDoc. +// JSDoc comments use `/** */` style multi-line comments. +func NoJSDocTransform(ts *guts.Typescript) { + ts.ForEach(func(key string, node bindings.Node) { + walk.Walk(&noJSDocTransformWalker{}, node) + }) +} + +type noJSDocTransformWalker struct{} + +func (v *noJSDocTransformWalker) Visit(node bindings.Node) walk.Visitor { + if commentedNode, ok := node.(bindings.Commentable); ok { + comments := commentedNode.Comments() + for i := range comments { + comments[i].DoNotFormat = true + } + } + + return v +} diff --git a/convert_test.go b/convert_test.go index c6f4d42..ee3988f 100644 --- a/convert_test.go +++ b/convert_test.go @@ -130,6 +130,8 @@ func TestGeneration(t *testing.T) { mutations = append(mutations, config.InterfaceToType) case "BiomeLintIgnoreAnyTypeParameters": mutations = append(mutations, config.BiomeLintIgnoreAnyTypeParameters) + case "NoJSDocTransform": + mutations = append(mutations, config.NoJSDocTransform) default: t.Fatal("unknown mutation, add it to the list:", m) } diff --git a/testdata/comments/comments.ts b/testdata/comments/comments.ts index 0c7cf9c..5c4abfe 100644 --- a/testdata/comments/comments.ts +++ b/testdata/comments/comments.ts @@ -8,18 +8,26 @@ export interface BlockComment { } // From comments/comments.go -// CommentedStructure is a struct with a comment. -// -// It actually has 2 comments?! -// TODO: Maybe add a third comment! +/** + * CommentedStructure is a struct with a comment. + * + * It actually has 2 comments?! + * TODO: Maybe add a third comment! + */ export interface CommentedStructure { readonly Inline: string; // Field comment - // Leading comment + /** + * Leading comment + */ readonly Leading: string; readonly Trailing: string; - // Leading comment + /** + * Leading comment + */ readonly All: string; // Inline comment - // Another leading comment + /** + * Another leading comment + */ readonly Block: string; /* Multi Line @@ -29,7 +37,9 @@ export interface CommentedStructure { } // From comments/comments.go -// Constant is just a value +/** + * Constant is just a value + */ export const Constant = "value"; // An inline note diff --git a/testdata/enums/enums.ts b/testdata/enums/enums.ts index 5d438c2..dd5d223 100644 --- a/testdata/enums/enums.ts +++ b/testdata/enums/enums.ts @@ -14,7 +14,9 @@ export enum EnumInt { } // From enums/enums.go -// EnumSliceType is a slice of string-based enums +/** + * EnumSliceType is a slice of string-based enums + */ export type EnumSliceType = readonly EnumString[]; // From enums/enums.go diff --git a/testdata/generics/generics.ts b/testdata/generics/generics.ts index 9c9dbc3..c43a217 100644 --- a/testdata/generics/generics.ts +++ b/testdata/generics/generics.ts @@ -25,7 +25,9 @@ export interface Complex { readonly dynamic: Fields; readonly comparable: boolean | null; @@ -49,7 +51,9 @@ export interface FieldsDiffOrder; } diff --git a/testdata/inheritance/inheritance.ts b/testdata/inheritance/inheritance.ts index ccfd3f6..1a2a739 100644 --- a/testdata/inheritance/inheritance.ts +++ b/testdata/inheritance/inheritance.ts @@ -21,8 +21,10 @@ export interface FooBarPtr extends Bar, GenBar { } // From codersdk/inheritance.go -// FooBuzz has a json tag for the embedded -// See: https://go.dev/play/p/-p6QYmY8mtR +/** + * FooBuzz has a json tag for the embedded + * See: https://go.dev/play/p/-p6QYmY8mtR + */ export interface FooBuzz { readonly foo: Buzz; // Json tag changes the inheritance readonly bazz: string; diff --git a/testdata/nojsdoc/mutations b/testdata/nojsdoc/mutations new file mode 100644 index 0000000..77c1593 --- /dev/null +++ b/testdata/nojsdoc/mutations @@ -0,0 +1 @@ +NoJSDocTransform \ No newline at end of file diff --git a/testdata/nojsdoc/nojsdoc.go b/testdata/nojsdoc/nojsdoc.go new file mode 100644 index 0000000..cc2511d --- /dev/null +++ b/testdata/nojsdoc/nojsdoc.go @@ -0,0 +1,7 @@ +package nojsdoc + +// Commented is a struct that does nothing +type Commented struct { + // Nothing is not very helpful + Nothing string +} diff --git a/testdata/nojsdoc/nojsdoc.ts b/testdata/nojsdoc/nojsdoc.ts new file mode 100644 index 0000000..77cc54a --- /dev/null +++ b/testdata/nojsdoc/nojsdoc.ts @@ -0,0 +1,8 @@ +// Code generated by 'guts'. DO NOT EDIT. + +// From nojsdoc/nojsdoc.go +// Commented is a struct that does nothing +interface Commented { + // Nothing is not very helpful + Nothing: string; +} diff --git a/testdata/nonids/nonids.ts b/testdata/nonids/nonids.ts index 795759b..b369b5a 100644 --- a/testdata/nonids/nonids.ts +++ b/testdata/nonids/nonids.ts @@ -2,7 +2,9 @@ // From nonids/nonids.go export interface Foo { - // Hyphen is an odd case, but this field is not ignored + /** + * Hyphen is an odd case, but this field is not ignored + */ readonly "-": string; readonly "hyphenated-string": string; readonly "1numbered": number; diff --git a/testdata/union/union.ts b/testdata/union/union.ts index 326de9c..dadd9e4 100644 --- a/testdata/union/union.ts +++ b/testdata/union/union.ts @@ -1,7 +1,9 @@ // Code generated by 'guts'. DO NOT EDIT. // From union/union.go -// Repeated constraints are redundant +/** + * Repeated constraints are redundant + */ export interface Repeated { readonly Value: T; }