Repository: flex-falcon
Updated Branches:
  refs/heads/develop 680c335c7 -> 80eb23610


LiteralEmitter: when a function has [JSX] metadata, any XML literals inside are 
treated as JSX and React calls are emitted instead


Project: http://git-wip-us.apache.org/repos/asf/flex-falcon/repo
Commit: http://git-wip-us.apache.org/repos/asf/flex-falcon/commit/72f0c52a
Tree: http://git-wip-us.apache.org/repos/asf/flex-falcon/tree/72f0c52a
Diff: http://git-wip-us.apache.org/repos/asf/flex-falcon/diff/72f0c52a

Branch: refs/heads/develop
Commit: 72f0c52ad18b454b95ce4dfbf6c0e587742730eb
Parents: 8e79559
Author: Josh Tynjala <joshtynj...@gmail.com>
Authored: Thu Oct 13 14:26:27 2016 -0700
Committer: Josh Tynjala <joshtynj...@gmail.com>
Committed: Fri Oct 14 10:10:16 2016 -0700

----------------------------------------------------------------------
 .../js/flexjs/JSFlexJSEmitterTokens.java        |   1 +
 .../internal/codegen/js/jx/LiteralEmitter.java  | 422 ++++++++++++++++---
 .../codegen/js/flexjs/TestFlexJSJSX.java        | 180 ++++++++
 3 files changed, 534 insertions(+), 69 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/72f0c52a/compiler-jx/src/main/java/org/apache/flex/compiler/internal/codegen/js/flexjs/JSFlexJSEmitterTokens.java
----------------------------------------------------------------------
diff --git 
a/compiler-jx/src/main/java/org/apache/flex/compiler/internal/codegen/js/flexjs/JSFlexJSEmitterTokens.java
 
b/compiler-jx/src/main/java/org/apache/flex/compiler/internal/codegen/js/flexjs/JSFlexJSEmitterTokens.java
index 4431407..4a140e3 100644
--- 
a/compiler-jx/src/main/java/org/apache/flex/compiler/internal/codegen/js/flexjs/JSFlexJSEmitterTokens.java
+++ 
b/compiler-jx/src/main/java/org/apache/flex/compiler/internal/codegen/js/flexjs/JSFlexJSEmitterTokens.java
@@ -55,6 +55,7 @@ public enum JSFlexJSEmitterTokens implements IEmitterTokens
     CLOSURE_FUNCTION_NAME("org.apache.flex.utils.Language.closure"),
     SKIP_AS_COERCIONS("skipAsCoercions"),
     SKIP_FUNCTION_COERCIONS("skipFunctionCoercions"),
+    JSX("JSX"),
     ;
 
     private String token;

http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/72f0c52a/compiler-jx/src/main/java/org/apache/flex/compiler/internal/codegen/js/jx/LiteralEmitter.java
----------------------------------------------------------------------
diff --git 
a/compiler-jx/src/main/java/org/apache/flex/compiler/internal/codegen/js/jx/LiteralEmitter.java
 
b/compiler-jx/src/main/java/org/apache/flex/compiler/internal/codegen/js/jx/LiteralEmitter.java
index 1be3b1e..c3e371d 100644
--- 
a/compiler-jx/src/main/java/org/apache/flex/compiler/internal/codegen/js/jx/LiteralEmitter.java
+++ 
b/compiler-jx/src/main/java/org/apache/flex/compiler/internal/codegen/js/jx/LiteralEmitter.java
@@ -19,15 +19,25 @@
 
 package org.apache.flex.compiler.internal.codegen.js.jx;
 
+import java.util.ArrayList;
+import java.util.Stack;
+
 import org.apache.flex.compiler.codegen.ISubEmitter;
 import org.apache.flex.compiler.codegen.js.IJSEmitter;
+import org.apache.flex.compiler.common.IMetaInfo;
+import org.apache.flex.compiler.common.Multiname;
+import org.apache.flex.compiler.internal.codegen.as.ASEmitterTokens;
 import org.apache.flex.compiler.internal.codegen.js.JSSubEmitter;
+import 
org.apache.flex.compiler.internal.codegen.js.flexjs.JSFlexJSEmitterTokens;
 import org.apache.flex.compiler.internal.tree.as.LiteralNode;
 import org.apache.flex.compiler.internal.tree.as.RegExpLiteralNode;
 import org.apache.flex.compiler.internal.tree.as.XMLLiteralNode;
 import org.apache.flex.compiler.tree.as.IASNode;
+import org.apache.flex.compiler.tree.as.IFunctionNode;
+import org.apache.flex.compiler.tree.as.IImportNode;
 import org.apache.flex.compiler.tree.as.ILiteralNode;
 import org.apache.flex.compiler.tree.as.ILiteralNode.LiteralType;
+import org.apache.flex.compiler.tree.as.IScopedNode;
 
 public class LiteralEmitter extends JSSubEmitter implements
         ISubEmitter<ILiteralNode>
@@ -49,60 +59,83 @@ public class LiteralEmitter extends JSSubEmitter implements
         {
             if (node.getLiteralType() == LiteralType.XML)
             {
-               newlineReplacement = "\\\\\n";
-               XMLLiteralNode xmlNode = (XMLLiteralNode)node;
-               if (xmlNode.getContentsNode().getChildCount() == 1)
-               {
-                       if (s.contains("'"))
-                               s = "\"" + s + "\"";
-                       else
-                               s = "'" + s + "'";
-               }
-               else
-               {
-                               StringBuilder sb = new StringBuilder();
-                       // probably contains {initializers}
-                               boolean inAttribute = false;
-                       int n = xmlNode.getContentsNode().getChildCount();
-                       for (int i = 0; i < n; i++)
-                       {
-                               if (i > 0)
-                                       sb.append(" + ");
-                               IASNode child = 
xmlNode.getContentsNode().getChild(i);
-                               if (child instanceof LiteralNode)
-                               {
-                                       s = ((LiteralNode)child).getValue(true);
-                               if (s.contains("'"))
-                                       sb.append("\"" + s + "\"");
-                               else
-                                       sb.append("'" + s + "'");
-                               }
-                               else
-                               {
-                                       s = getEmitter().stringifyNode(child);
-                                       if (inAttribute)
-                                       {
-                                               sb.append("'\"' + ");
-
-                                               sb.append(s);
-                                               
-                                               sb.append(" + '\"'");
-                                       }
-                                       else
-                                               sb.append(s);
-                               }
-                                       inAttribute = s.equals("=");
-                       }
-                       s = sb.toString();
-               }
-                char c = s.charAt(0);
-                if (c == '"')
+                boolean jsx = false;
+                IFunctionNode functionNode = (IFunctionNode) node
+                        .getAncestorOfType(IFunctionNode.class);
+                if (functionNode != null)
                 {
-                    s = s.substring(1, s.length() - 1);
-                    s = s.replace("\"", "\\\"");
-                    s = "\"" + s + "\"";
+                    IMetaInfo[] metaInfos = functionNode.getMetaInfos();
+                    for (IMetaInfo metaInfo : metaInfos)
+                    {
+                        if 
(metaInfo.getTagName().equals(JSFlexJSEmitterTokens.JSX.getToken()))
+                        {
+                            jsx = true;
+                            break;
+                        }
+                    }
+                }
+                XMLLiteralNode xmlNode = (XMLLiteralNode) node;
+                if (jsx)
+                {
+                    emitJSX(xmlNode);
+                    return;
+                }
+                else
+                {
+                    newlineReplacement = "\\\\\n";
+                    if (xmlNode.getContentsNode().getChildCount() == 1)
+                    {
+                        if (s.contains("'"))
+                            s = "\"" + s + "\"";
+                        else
+                            s = "'" + s + "'";
+                    }
+                    else
+                    {
+                        StringBuilder sb = new StringBuilder();
+                        // probably contains {initializers}
+                        boolean inAttribute = false;
+                        int n = xmlNode.getContentsNode().getChildCount();
+                        for (int i = 0; i < n; i++)
+                        {
+                            if (i > 0)
+                                sb.append(" + ");
+                            IASNode child = 
xmlNode.getContentsNode().getChild(i);
+                            if (child instanceof LiteralNode)
+                            {
+                                s = ((LiteralNode) child).getValue(true);
+                                if (s.contains("'"))
+                                    sb.append("\"" + s + "\"");
+                                else
+                                    sb.append("'" + s + "'");
+                            }
+                            else
+                            {
+                                s = getEmitter().stringifyNode(child);
+                                if (inAttribute)
+                                {
+                                    sb.append("'\"' + ");
+
+                                    sb.append(s);
+
+                                    sb.append(" + '\"'");
+                                }
+                                else
+                                    sb.append(s);
+                            }
+                            inAttribute = s.equals("=");
+                        }
+                        s = sb.toString();
+                    }
+                    char c = s.charAt(0);
+                    if (c == '"')
+                    {
+                        s = s.substring(1, s.length() - 1);
+                        s = s.replace("\"", "\\\"");
+                        s = "\"" + s + "\"";
+                    }
+                    s = "new XML( " + s + ")";
                 }
-                s = "new XML( " + s + ")";
             }
             s = s.replaceAll("\n", "__NEWLINE_PLACEHOLDER__");
             s = s.replaceAll("\r", "__CR_PLACEHOLDER__");
@@ -121,30 +154,281 @@ public class LiteralEmitter extends JSSubEmitter 
implements
             s = s.replaceAll("__NEWLINE_PLACEHOLDER__", newlineReplacement);
             if (node.getLiteralType() == LiteralType.STRING)
             {
-               char c = s.charAt(0);
-               if (c == '"')
-               {
-                       s = s.substring(1, s.length() - 1);
-                       s = s.replace("\"", "\\\"");
-                       s = "\"" + s + "\"";
-               }
-               else if (c == '\'')
-               {
-                       s = s.substring(1, s.length() - 1);
-                       s = s.replace("'", "\\'");                      
-                       s = "'" + s + "'";
-               }
-               s = s.replace("\u2028", "\\u2028");
-               s = s.replace("\u2029", "\\u2029");
+                char c = s.charAt(0);
+                if (c == '"')
+                {
+                    s = s.substring(1, s.length() - 1);
+                    s = s.replace("\"", "\\\"");
+                    s = "\"" + s + "\"";
+                }
+                else if (c == '\'')
+                {
+                    s = s.substring(1, s.length() - 1);
+                    s = s.replace("'", "\\'");
+                    s = "'" + s + "'";
+                }
+                s = s.replace("\u2028", "\\u2028");
+                s = s.replace("\u2029", "\\u2029");
             }
 
         }
 
         if (!isWritten)
         {
-                       startMapping(node);
+            startMapping(node);
             write(s);
-                       endMapping(node);
+            endMapping(node);
+        }
+    }
+
+    private void emitJSX(XMLLiteralNode node)
+    {
+        int childCount = node.getContentsNode().getChildCount();
+        Stack<String> elementStack = new Stack<String>();
+        String elementName = null;
+        boolean endsWithAttribute = false;
+        for (int i = 0; i < childCount; i++)
+        {
+            IASNode child = node.getContentsNode().getChild(i);
+            if (child instanceof ILiteralNode)
+            {
+                ILiteralNode literalChild = (ILiteralNode) child;
+                if (literalChild.getLiteralType() != LiteralType.XML)
+                {
+                    //inside {} syntax. emit normally.
+                    getEmitter().getWalker().walk(literalChild);
+                    continue;
+                }
+                String value = literalChild.getValue(true);
+                value = value.replaceAll(ASEmitterTokens.NEW_LINE.getToken(), 
"");
+                value = value.trim();
+                while (value.length() > 0)
+                {
+                    int nextTagStartIndex = value.indexOf("<");
+                    int nextTagEndIndex = value.indexOf(">");
+                    boolean selfClosing = false;
+                    boolean startsWithAttribute = false;
+                    if (nextTagEndIndex > 0
+                            && value.charAt(nextTagEndIndex - 1) == '/')
+                    {
+                        selfClosing = true;
+                    }
+                    if (endsWithAttribute)
+                    {
+                        //we'll fall back into attribute parsing below
+                        endsWithAttribute = false;
+                        startsWithAttribute = true;
+                        elementName = elementStack.peek();
+                    }
+                    else if (nextTagStartIndex == 0)
+                    {
+                        //assume that the name ends at the end of the open tag
+                        int endNameIndex = nextTagEndIndex;
+                        if (endNameIndex == -1)
+                        {
+                            //literal ends with an attribute that uses {} 
syntax
+                            endNameIndex = value.length() - 1;
+                        }
+                        int attributeIndex = value.indexOf(" ");
+                        if (attributeIndex > 0 && attributeIndex < 
endNameIndex)
+                        {
+                            //if there are attributes, the name does not end at
+                            //the end of the open tag
+                            endNameIndex = attributeIndex;
+                        }
+                        elementName = value.substring(1, endNameIndex);
+                        if (elementName.endsWith("/"))
+                        {
+                            elementName = elementName.substring(0, 
elementName.length() - 1);
+                        }
+                        if (elementName.startsWith("/"))
+                        {
+                            //the close tag of the current element
+                            elementName = elementName.substring(1);
+                            elementName = getQualifiedElementName(elementName, 
node);
+                            if (elementStack.size() > 0)
+                            {
+                                indentPop();
+                            }
+                            write(ASEmitterTokens.PAREN_CLOSE);
+                            String topOfStack = elementStack.pop();
+                            assert topOfStack.equals(elementName);
+                            value = value.substring(nextTagEndIndex + 1);
+                            value = value.trim();
+                            continue;
+                        }
+                        else
+                        {
+                            //the open tag of a new element
+                            if (elementStack.size() > 0)
+                            {
+                                indentPush();
+                                writeNewline(ASEmitterTokens.COMMA);
+                            }
+                            elementName = getQualifiedElementName(elementName, 
node);
+                            elementStack.push(elementName);
+                            write("React.createElement");
+                            write(ASEmitterTokens.PAREN_OPEN);
+                            write(elementName);
+                            value = value.substring(endNameIndex);
+                            value = value.trim();
+                            //we changed the string, so find it again
+                            nextTagEndIndex = value.indexOf(">");
+                        }
+                    }
+                    else
+                    {
+                        //we're inside an element's open and closing tags
+                        String elementText = value.substring(0, 
nextTagStartIndex);
+                        writeToken(ASEmitterTokens.COMMA);
+                        emitJSXText(elementText);
+                        value = value.substring(nextTagStartIndex);
+                        continue;
+                    }
+                    //parse the tag's attributes
+                    if (nextTagEndIndex == -1)
+                    {
+                        //literal ends with an attribute that uses {} syntax
+                        endsWithAttribute = true;
+                        nextTagEndIndex = value.length() - 1;
+                    }
+                    int attributesEndIndex = (selfClosing && nextTagEndIndex > 
0) ? nextTagEndIndex - 1 : nextTagEndIndex;
+                    String attributes = value.substring(0, attributesEndIndex);
+                    emitJSXAttributes(attributes, startsWithAttribute, 
endsWithAttribute);
+                    if (selfClosing)
+                    {
+                        //end of open tag, including attributes
+                        write(ASEmitterTokens.PAREN_CLOSE);
+                        if (elementStack.size() > 0)
+                        {
+                            indentPop();
+                        }
+                        String topOfStack = elementStack.pop();
+                        assert topOfStack.equals(elementName);
+                    }
+                    value = value.substring(nextTagEndIndex + 1);
+                    value = value.trim();
+                }
+            }
+            else
+            {
+                if (!endsWithAttribute)
+                {
+                    writeToken(ASEmitterTokens.COMMA);
+                }
+                //not a literal, and inside {} syntax. emit normally.
+                getEmitter().getWalker().walk(child);
+            }
+        }
+    }
+
+    private void emitJSXAttributes(String value, boolean startsWithAttribute, 
boolean endsWithAttribute)
+    {
+        int attributeCount = 0;
+        while (true)
+        {
+            int charCount = value.length();
+            if (charCount == 0)
+            {
+                break;
+            }
+            int endAttributeNameIndex = value.indexOf("=");
+            if (endAttributeNameIndex == -1)
+            {
+                endAttributeNameIndex = value.length();
+            }
+            String attributeName = value.substring(0, endAttributeNameIndex);
+            writeToken(ASEmitterTokens.COMMA);
+            if (!startsWithAttribute && attributeCount == 0)
+            {
+                writeToken(ASEmitterTokens.BLOCK_OPEN);
+            }
+            if (attributeName.indexOf('-') >= 0)
+            {
+                emitJSXText(attributeName);
+            }
+            else
+            {
+                write(attributeName);
+            }
+            writeToken(ASEmitterTokens.COLON);
+            attributeCount++;
+            if ((endAttributeNameIndex + 1) >= charCount)
+            {
+                //literal ends with an attribute that uses {} syntax
+                break;
+            }
+            int quoteChar = value.charAt(endAttributeNameIndex + 1);
+            int startAttributeValueIndex = endAttributeNameIndex + 2;
+            if (startAttributeValueIndex > charCount)
+            {
+                startAttributeValueIndex = charCount;
+            }
+            int endAttributeValueIndex = value.indexOf(quoteChar, 
startAttributeValueIndex);
+            if (endAttributeValueIndex == -1 || endAttributeValueIndex > 
charCount)
+            {
+                endAttributeValueIndex = charCount;
+            }
+            if (endAttributeValueIndex > 0)
+            {
+                String attributeValue = 
value.substring(startAttributeValueIndex, endAttributeValueIndex);
+                emitJSXText(attributeValue);
+                value = value.substring(endAttributeValueIndex + 1);
+                value = value.trim();
+            }
+        }
+        if (!endsWithAttribute)
+        {
+            if (!startsWithAttribute && attributeCount == 0)
+            {
+                writeToken(ASEmitterTokens.COMMA);
+                write(ASEmitterTokens.NULL);
+            }
+            else
+            {
+                write(ASEmitterTokens.SPACE);
+                write(ASEmitterTokens.BLOCK_CLOSE);
+            }
+        }
+    }
+
+    private void emitJSXText(String elementText)
+    {
+        write(ASEmitterTokens.SINGLE_QUOTE);
+        elementText = elementText.replaceAll("'", "\\\\\'");
+        write(elementText);
+        write(ASEmitterTokens.SINGLE_QUOTE);
+    }
+
+    private String getQualifiedElementName(String elementName, IASNode node)
+    {
+        String firstChar = elementName.substring(0, 1);
+        boolean isHTMLTag = firstChar.toLowerCase().equals(firstChar);
+        if (isHTMLTag)
+        {
+            return ASEmitterTokens.SINGLE_QUOTE.getToken() + elementName + 
ASEmitterTokens.SINGLE_QUOTE.getToken();
+        }
+        ArrayList<IImportNode> importNodes = new ArrayList<IImportNode>();
+        IScopedNode scopedNode = node.getContainingScope();
+        scopedNode.getAllImportNodes(importNodes);
+        for (IImportNode importNode : importNodes)
+        {
+            if (importNode.isWildcardImport())
+            {
+                continue;
+            }
+            String importName = importNode.getImportName();
+            String importAlias = importNode.getImportAlias();
+            if (importAlias != null && importAlias.equals(elementName))
+            {
+                return importName;
+            }
+            String baseName = Multiname.getBaseNameForQName(importName);
+            if (baseName.equals(elementName))
+            {
+                return importName;
+            }
         }
+        return elementName;
     }
 }

http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/72f0c52a/compiler-jx/src/test/java/org/apache/flex/compiler/internal/codegen/js/flexjs/TestFlexJSJSX.java
----------------------------------------------------------------------
diff --git 
a/compiler-jx/src/test/java/org/apache/flex/compiler/internal/codegen/js/flexjs/TestFlexJSJSX.java
 
b/compiler-jx/src/test/java/org/apache/flex/compiler/internal/codegen/js/flexjs/TestFlexJSJSX.java
new file mode 100644
index 0000000..a3c1c07
--- /dev/null
+++ 
b/compiler-jx/src/test/java/org/apache/flex/compiler/internal/codegen/js/flexjs/TestFlexJSJSX.java
@@ -0,0 +1,180 @@
+/*
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+package org.apache.flex.compiler.internal.codegen.js.flexjs;
+
+import org.apache.flex.compiler.driver.IBackend;
+import org.apache.flex.compiler.internal.driver.js.flexjs.FlexJSBackend;
+import org.apache.flex.compiler.internal.test.ASTestBase;
+import org.apache.flex.compiler.tree.as.IFunctionNode;
+
+import org.junit.Test;
+
+public class TestFlexJSJSX extends ASTestBase
+{
+    @Test
+    public void testJSXMetadataWithoutXMLLiterals()
+    {
+        IFunctionNode node = getMethod("[JSX]\nfunction foo() {}");
+        asBlockWalker.visitFunction(node);
+        assertOut("FalconTest_A.prototype.foo = function() {\n}");
+    }
+
+    @Test
+    public void testSimpleSelfClosingHTMLTag()
+    {
+        IFunctionNode node = getMethod("[JSX]\nfunction foo() {return 
<div/>}");
+        asBlockWalker.visitFunction(node);
+        assertOut("FalconTest_A.prototype.foo = function() {\n  return 
React.createElement('div', null);\n}");
+    }
+
+    @Test
+    public void testSimpleSelfClosingHTMLTagWithTrailingSpace()
+    {
+        IFunctionNode node = getMethod("[JSX]\nfunction foo() {return <div 
/>}");
+        asBlockWalker.visitFunction(node);
+        assertOut("FalconTest_A.prototype.foo = function() {\n  return 
React.createElement('div', null);\n}");
+    }
+
+    @Test
+    public void testSelfClosingHTMLTagWithAttribute()
+    {
+        IFunctionNode node = getMethod("[JSX]\nfunction foo() {return <div 
id=\"foo\"/>}");
+        asBlockWalker.visitFunction(node);
+        assertOut("FalconTest_A.prototype.foo = function() {\n  return 
React.createElement('div', { id: 'foo' });\n}");
+    }
+
+    @Test
+    public void testSelfClosingHTMLTagWithMultipleAttributes()
+    {
+        IFunctionNode node = getMethod("[JSX]\nfunction foo() {return <div 
id=\"foo\" className=\"bar\"/>}");
+        asBlockWalker.visitFunction(node);
+        assertOut("FalconTest_A.prototype.foo = function() {\n  return 
React.createElement('div', { id: 'foo', className: 'bar' });\n}");
+    }
+
+    @Test
+    public void testSelfClosingHTMLTagWithSingleQuoteAttribute()
+    {
+        IFunctionNode node = getMethod("[JSX]\nfunction foo() {return <div 
id=\'foo\'/>}");
+        asBlockWalker.visitFunction(node);
+        assertOut("FalconTest_A.prototype.foo = function() {\n  return 
React.createElement('div', { id: 'foo' });\n}");
+    }
+
+    @Test
+    public void testSelfClosingHTMLTagWithSingleQuoteInAttribute()
+    {
+        IFunctionNode node = getMethod("[JSX]\nfunction foo() {return <div 
id=\"'\"/>}");
+        asBlockWalker.visitFunction(node);
+        assertOut("FalconTest_A.prototype.foo = function() {\n  return 
React.createElement('div', { id: '\\\'' });\n}");
+    }
+
+    @Test
+    public void testSelfClosingHTMLTagWithDashInAttributeName()
+    {
+        IFunctionNode node = getMethod("[JSX]\nfunction foo() {return <div 
data-prop=\"foo\"/>}");
+        asBlockWalker.visitFunction(node);
+        assertOut("FalconTest_A.prototype.foo = function() {\n  return 
React.createElement('div', { 'data-prop': 'foo' });\n}");
+    }
+
+    @Test
+    public void testSelfClosingHTMLTagWithSpaceInAttribute()
+    {
+        IFunctionNode node = getMethod("[JSX]\nfunction foo() {return <div 
className=\"foo bar\"/>}");
+        asBlockWalker.visitFunction(node);
+        assertOut("FalconTest_A.prototype.foo = function() {\n  return 
React.createElement('div', { className: 'foo bar' });\n}");
+    }
+
+    @Test
+    public void testSelfClosingHTMLTagWithSpaceInSingleQuoteAttribute()
+    {
+        IFunctionNode node = getMethod("[JSX]\nfunction foo() {return <div 
className=\'foo bar\'/>}");
+        asBlockWalker.visitFunction(node);
+        assertOut("FalconTest_A.prototype.foo = function() {\n  return 
React.createElement('div', { className: 'foo bar' });\n}");
+    }
+
+    @Test
+    public void testSelfClosingHTMLTagWithCurlyAttribute()
+    {
+        IFunctionNode node = getMethod("[JSX]\nfunction foo() {return <div 
id={2}/>}");
+        asBlockWalker.visitFunction(node);
+        assertOut("FalconTest_A.prototype.foo = function() {\n  return 
React.createElement('div', { id: 2 });\n}");
+    }
+
+    @Test
+    public void testSimpleOpenAndCloseHTMLTag()
+    {
+        IFunctionNode node = getMethod("[JSX]\nfunction foo() {return 
<div></div>}");
+        asBlockWalker.visitFunction(node);
+        assertOut("FalconTest_A.prototype.foo = function() {\n  return 
React.createElement('div', null);\n}");
+    }
+
+    @Test
+    public void testOpenAndCloseHTMLTagWithChildText()
+    {
+        IFunctionNode node = getMethod("[JSX]\nfunction foo() {return 
<div>Foo</div>}");
+        asBlockWalker.visitFunction(node);
+        assertOut("FalconTest_A.prototype.foo = function() {\n  return 
React.createElement('div', null, 'Foo');\n}");
+    }
+
+    @Test
+    public void testOpenAndCloseHTMLTagWithAttribute()
+    {
+        IFunctionNode node = getMethod("[JSX]\nfunction foo() {return <div 
id=\"foo\"></div>}");
+        asBlockWalker.visitFunction(node);
+        assertOut("FalconTest_A.prototype.foo = function() {\n  return 
React.createElement('div', { id: 'foo' });\n}");
+    }
+
+    @Test
+    public void testOpenAndCloseHTMLTagWithMultipleAttributes()
+    {
+        IFunctionNode node = getMethod("[JSX]\nfunction foo() {return <div 
id=\"foo\" className=\"bar\"></div>}");
+        asBlockWalker.visitFunction(node);
+        assertOut("FalconTest_A.prototype.foo = function() {\n  return 
React.createElement('div', { id: 'foo', className: 'bar' });\n}");
+    }
+
+    @Test
+    public void testOpenAndCloseHTMLTagWithAttributeAndChildText()
+    {
+        IFunctionNode node = getMethod("[JSX]\nfunction foo() {return <div 
id=\"foo\">Foo</div>}");
+        asBlockWalker.visitFunction(node);
+        assertOut("FalconTest_A.prototype.foo = function() {\n  return 
React.createElement('div', { id: 'foo' }, 'Foo');\n}");
+    }
+
+    @Test
+    public void testNestedHTMLTags()
+    {
+        IFunctionNode node = getMethod("[JSX]\nfunction foo() {return 
<div><button/></div>}");
+        asBlockWalker.visitFunction(node);
+        assertOut("FalconTest_A.prototype.foo = function() {\n  return 
React.createElement('div', null,\n    React.createElement('button', 
null));\n}");
+    }
+
+    @Test
+    public void testImportedClass()
+    {
+        IFunctionNode node = getMethod("[JSX]\nfunction foo() {\n  import 
flash.events.EventDispatcher;\n  return <EventDispatcher/>;\n}");
+        asBlockWalker.visitFunction(node);
+        assertOut("FalconTest_A.prototype.foo = function() {\n  return 
React.createElement(flash.events.EventDispatcher, null);\n}");
+    }
+
+    @Override
+    protected IBackend createBackend()
+    {
+        return new FlexJSBackend();
+    }
+}

Reply via email to