fix(63726): fix declaration emit for multiline jsdoc literal types - #4839
fix(63726): fix declaration emit for multiline jsdoc literal types#4839Oleksandr Tarasiuk (a-tarasyuk) wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Adds a regression test and adjusts scanner trivia-skipping to correctly preserve multiline JSDoc literal union types during declaration emit (fixes TypeScript issue #63726).
Changes:
- Added a new compiler test case for multiline JSDoc union literal typedefs in JS files.
- Updated the reference baseline to validate correct
.d.tsoutput and preserved JSDoc formatting. - Adjusted
GetTextOfNodeFromSourceTextto use JSDoc-aware trivia skipping when extracting text from JSDoc nodes.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| testdata/tests/cases/compiler/jsdocMultilineUnion.ts | New regression test covering multiline JSDoc literal union typedef formatting. |
| testdata/baselines/reference/compiler/jsdocMultilineUnion.js | Baseline validating correct declaration emit and preserved multiline JSDoc formatting. |
| internal/scanner/utilities.go | Uses JSDoc-aware trivia skipping when extracting node text for JSDoc nodes. |
073e322 to
0ad87d1
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Suppressed comments (1)
internal/scanner/utilities.go:33
SkipTriviaExonly removes a JSDoc asterisk while locating the node start; it does not remove prefixes inside a node's span. JSDoc types accept multiline template literals (parser.go:2823), so a type such as@typedef {`a\n * b`} Tstill emits the embedded*. Normalize every extracted line for descendants ofJSDocTypeExpression(as the reference implementation does inutilities.ts:1290-1330) and cover a multiline template literal.
pos = SkipTriviaEx(sourceText, pos, &SkipTriviaOptions{
InJSDoc: node.Flags&ast.NodeFlagsJSDoc != 0,
})
|
The suppressed comment actually seems possibly true, have not checked. |
|
Checked, and what it noted is already a 6.0 problem, so nothing new |
|
Though, perhaps worth fixing: diff --git a/internal/scanner/utilities.go b/internal/scanner/utilities.go
index d62247c6e4..096583636f 100644
--- a/internal/scanner/utilities.go
+++ b/internal/scanner/utilities.go
@@ -8,6 +8,7 @@ import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/debug"
+ "github.com/microsoft/typescript-go/internal/stringutil"
)
func tokenIsIdentifierOrKeyword(token ast.Kind) bool {
@@ -22,6 +23,32 @@ func GetSourceTextOfNodeFromSourceFile(sourceFile *ast.SourceFile, node *ast.Nod
return GetTextOfNodeFromSourceText(sourceFile.Text(), node, includeTrivia)
}
+func isJSDocTypeExpressionOrChild(node *ast.Node) bool {
+ if node.Flags&ast.NodeFlagsJSDoc == 0 {
+ return false
+ }
+ for current := node; current != nil; current = current.Parent {
+ if ast.IsJSDocTypeExpression(current) || ast.IsTypeNode(current) {
+ return true
+ }
+ }
+ return false
+}
+
+func stripJSDocTypePrefixes(text string) string {
+ text = strings.ReplaceAll(text, "\r\n", "\n")
+ text = strings.ReplaceAll(text, "\r", "\n")
+ lines := strings.Split(text, "\n")
+ for i, line := range lines {
+ line = strings.TrimLeftFunc(line, stringutil.IsWhiteSpaceLike)
+ if strings.HasPrefix(line, "*") {
+ line = strings.TrimLeftFunc(line[1:], stringutil.IsWhiteSpaceLike)
+ }
+ lines[i] = line
+ }
+ return strings.Join(lines, "\n")
+}
+
func GetTextOfNodeFromSourceText(sourceText string, node *ast.Node, includeTrivia bool) string {
if ast.NodeIsMissing(node) {
return ""
@@ -33,6 +60,9 @@ func GetTextOfNodeFromSourceText(sourceText string, node *ast.Node, includeTrivi
})
}
text := sourceText[pos:node.End()]
+ if isJSDocTypeExpressionOrChild(node) {
+ text = stripJSDocTypePrefixes(text)
+ }
if node.Flags&ast.NodeFlagsReparserTransformedLiteral != 0 {
// This is similar to `getLiteralTextOfNode` in the printer, but without the context of an `emitContext` to provide overrides
if ast.IsStringLiteral(node) {
@@ -48,10 +78,6 @@ func GetTextOfNodeFromSourceText(sourceText string, node *ast.Node, includeTrivi
// Fail on any other kinds.
debug.FailBadSyntaxKind(node, "Unexpected reparser-transformed node kind")
}
- // if (isJSDocTypeExpressionOrChild(node)) {
- // // strip space + asterisk at line start
- // text = text.split(/\r\n|\n|\r/).map(line => line.replace(/^\s*\*/, "").trimStart()).join("\n");
- // }
return text
} |
Fixes microsoft/TypeScript#63726