1/* @internal */ 2namespace ts.codefix { 3 const fixID = "wrapJsxInFragment"; 4 const errorCodes = [Diagnostics.JSX_expressions_must_have_one_parent_element.code]; 5 registerCodeFix({ 6 errorCodes, 7 getCodeActions: function getCodeActionsToWrapJsxInFragment(context) { 8 const { sourceFile, span } = context; 9 const node = findNodeToFix(sourceFile, span.start); 10 if (!node) return undefined; 11 const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, node)); 12 return [createCodeFixAction(fixID, changes, Diagnostics.Wrap_in_JSX_fragment, fixID, Diagnostics.Wrap_all_unparented_JSX_in_JSX_fragment)]; 13 }, 14 fixIds: [fixID], 15 getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => { 16 const node = findNodeToFix(context.sourceFile, diag.start); 17 if (!node) return undefined; 18 doChange(changes, context.sourceFile, node); 19 }), 20 }); 21 22 function findNodeToFix(sourceFile: SourceFile, pos: number): BinaryExpression | undefined { 23 // The error always at 1st token that is "<" in "<a /><a />" 24 const lessThanToken = getTokenAtPosition(sourceFile, pos); 25 const firstJsxElementOrOpenElement = lessThanToken.parent; 26 let binaryExpr = firstJsxElementOrOpenElement.parent; 27 if (!isBinaryExpression(binaryExpr)) { 28 // In case the start element is a JsxSelfClosingElement, it the end. 29 // For JsxOpenElement, find one more parent 30 binaryExpr = binaryExpr.parent; 31 if (!isBinaryExpression(binaryExpr)) return undefined; 32 } 33 if (!nodeIsMissing(binaryExpr.operatorToken)) return undefined; 34 return binaryExpr; 35 } 36 37 function doChange(changeTracker: textChanges.ChangeTracker, sf: SourceFile, node: Node) { 38 const jsx = flattenInvalidBinaryExpr(node); 39 if (jsx) changeTracker.replaceNode(sf, node, factory.createJsxFragment(factory.createJsxOpeningFragment(), jsx, factory.createJsxJsxClosingFragment())); 40 } 41 // The invalid syntax is constructed as 42 // InvalidJsxTree :: One of 43 // JsxElement CommaToken InvalidJsxTree 44 // JsxElement CommaToken JsxElement 45 function flattenInvalidBinaryExpr(node: Node): JsxChild[] | undefined { 46 const children: JsxChild[] = []; 47 let current = node; 48 while (true) { 49 if (isBinaryExpression(current) && nodeIsMissing(current.operatorToken) && current.operatorToken.kind === SyntaxKind.CommaToken) { 50 children.push(current.left as JsxChild); 51 if (isJsxChild(current.right)) { 52 children.push(current.right); 53 // Indicates the tree has go to the bottom 54 return children; 55 } 56 else if (isBinaryExpression(current.right)) { 57 current = current.right; 58 continue; 59 } 60 // Unreachable case 61 else return undefined; 62 } 63 // Unreachable case 64 else return undefined; 65 } 66 } 67} 68