Skip to content

Commit

Permalink
fix #124: slice with full unicode width inside jsx
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed May 21, 2020
1 parent 8478a98 commit c949276
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 4 deletions.
8 changes: 4 additions & 4 deletions internal/lexer/lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -1804,7 +1804,7 @@ func decodeJSXEntities(decoded []uint16, text string) []uint16 {
}

func fixWhitespaceAndDecodeJSXEntities(text string) []uint16 {
lastNonWhitespace := -1
afterLastNonWhitespace := -1
decoded := []uint16{}
i := 0

Expand All @@ -1818,13 +1818,13 @@ func fixWhitespaceAndDecodeJSXEntities(text string) []uint16 {
switch c {
case '\r', '\n', '\u2028', '\u2029':
// Newline
if firstNonWhitespace != -1 && lastNonWhitespace != -1 {
if firstNonWhitespace != -1 && afterLastNonWhitespace != -1 {
if len(decoded) > 0 {
decoded = append(decoded, ' ')
}

// Trim whitespace off the start and end of lines in the middle
decoded = decodeJSXEntities(decoded, text[firstNonWhitespace:lastNonWhitespace+1])
decoded = decodeJSXEntities(decoded, text[firstNonWhitespace:afterLastNonWhitespace])
}

// Reset for the next line
Expand All @@ -1836,7 +1836,7 @@ func fixWhitespaceAndDecodeJSXEntities(text string) []uint16 {
default:
// Check for unusual whitespace characters
if !IsWhitespace(c) {
lastNonWhitespace = i
afterLastNonWhitespace = i + width
if firstNonWhitespace == -1 {
firstNonWhitespace = i
}
Expand Down
30 changes: 30 additions & 0 deletions internal/parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1265,6 +1265,7 @@ func TestJSX(t *testing.T) {
expectPrintedJSX(t, "<a b={1, 2}/>", "React.createElement(\"a\", {\n b: (1, 2)\n});\n")
expectPrintedJSX(t, "<a b={<c/>}/>", "React.createElement(\"a\", {\n b: React.createElement(\"c\", null)\n});\n")
expectPrintedJSX(t, "<a {...props}/>", "React.createElement(\"a\", {\n ...props\n});\n")
expectPrintedJSX(t, "<a b=\"πŸ™‚\"/>", "React.createElement(\"a\", {\n b: \"πŸ™‚\"\n});\n")

expectPrintedJSX(t, "<a>\n</a>", "React.createElement(\"a\", null);\n")
expectPrintedJSX(t, "<a>123</a>", "React.createElement(\"a\", null, \"123\");\n")
Expand All @@ -1278,6 +1279,7 @@ func TestJSX(t *testing.T) {
expectPrintedJSX(t, "<a>{1, 2}</a>", "React.createElement(\"a\", null, (1, 2));\n")
expectPrintedJSX(t, "<a>&lt;&gt;</a>", "React.createElement(\"a\", null, \"<>\");\n")
expectPrintedJSX(t, "<a>&wrong;</a>", "React.createElement(\"a\", null, \"&wrong;\");\n")
expectPrintedJSX(t, "<a>πŸ™‚</a>", "React.createElement(\"a\", null, \"πŸ™‚\");\n")

// Note: The TypeScript compiler and Babel disagree. This matches TypeScript.
expectPrintedJSX(t, "<a b=\" c\"/>", "React.createElement(\"a\", {\n b: \" c\"\n});\n")
Expand All @@ -1293,6 +1295,20 @@ func TestJSX(t *testing.T) {
expectPrintedJSX(t, "<a b=\" \nc\"/>", "React.createElement(\"a\", {\n b: \" \\nc\"\n});\n")
expectPrintedJSX(t, "<a b=\"\n c\"/>", "React.createElement(\"a\", {\n b: \"\\n c\"\n});\n")

// Same test as above except with multi-byte Unicode characters
expectPrintedJSX(t, "<a b=\" πŸ™‚\"/>", "React.createElement(\"a\", {\n b: \" πŸ™‚\"\n});\n")
expectPrintedJSX(t, "<a b=\" \nπŸ™‚\"/>", "React.createElement(\"a\", {\n b: \" \\nπŸ™‚\"\n});\n")
expectPrintedJSX(t, "<a b=\"\n πŸ™‚\"/>", "React.createElement(\"a\", {\n b: \"\\n πŸ™‚\"\n});\n")
expectPrintedJSX(t, "<a b=\"πŸ™‚ \"/>", "React.createElement(\"a\", {\n b: \"πŸ™‚ \"\n});\n")
expectPrintedJSX(t, "<a b=\"πŸ™‚ \n\"/>", "React.createElement(\"a\", {\n b: \"πŸ™‚ \\n\"\n});\n")
expectPrintedJSX(t, "<a b=\"πŸ™‚\n \"/>", "React.createElement(\"a\", {\n b: \"πŸ™‚\\n \"\n});\n")
expectPrintedJSX(t, "<a b=\"πŸ™‚ πŸ•\"/>", "React.createElement(\"a\", {\n b: \"πŸ™‚ πŸ•\"\n});\n")
expectPrintedJSX(t, "<a b=\"πŸ™‚ \nπŸ•\"/>", "React.createElement(\"a\", {\n b: \"πŸ™‚ \\nπŸ•\"\n});\n")
expectPrintedJSX(t, "<a b=\"πŸ™‚\n πŸ•\"/>", "React.createElement(\"a\", {\n b: \"πŸ™‚\\n πŸ•\"\n});\n")
expectPrintedJSX(t, "<a b=\" πŸ™‚\"/>", "React.createElement(\"a\", {\n b: \" πŸ™‚\"\n});\n")
expectPrintedJSX(t, "<a b=\" \nπŸ™‚\"/>", "React.createElement(\"a\", {\n b: \" \\nπŸ™‚\"\n});\n")
expectPrintedJSX(t, "<a b=\"\n πŸ™‚\"/>", "React.createElement(\"a\", {\n b: \"\\n πŸ™‚\"\n});\n")

expectPrintedJSX(t, "<a> b</a>", "React.createElement(\"a\", null, \" b\");\n")
expectPrintedJSX(t, "<a> \nb</a>", "React.createElement(\"a\", null, \"b\");\n")
expectPrintedJSX(t, "<a>\n b</a>", "React.createElement(\"a\", null, \"b\");\n")
Expand All @@ -1306,6 +1322,20 @@ func TestJSX(t *testing.T) {
expectPrintedJSX(t, "<a> \nb</a>", "React.createElement(\"a\", null, \"b\");\n")
expectPrintedJSX(t, "<a>\n b</a>", "React.createElement(\"a\", null, \"b\");\n")

// Same test as above except with multi-byte Unicode characters
expectPrintedJSX(t, "<a> πŸ™‚</a>", "React.createElement(\"a\", null, \" πŸ™‚\");\n")
expectPrintedJSX(t, "<a> \nπŸ™‚</a>", "React.createElement(\"a\", null, \"πŸ™‚\");\n")
expectPrintedJSX(t, "<a>\n πŸ™‚</a>", "React.createElement(\"a\", null, \"πŸ™‚\");\n")
expectPrintedJSX(t, "<a>πŸ™‚ </a>", "React.createElement(\"a\", null, \"πŸ™‚ \");\n")
expectPrintedJSX(t, "<a>πŸ™‚ \n</a>", "React.createElement(\"a\", null, \"πŸ™‚\");\n")
expectPrintedJSX(t, "<a>πŸ™‚\n </a>", "React.createElement(\"a\", null, \"πŸ™‚\");\n")
expectPrintedJSX(t, "<a>πŸ™‚ πŸ•</a>", "React.createElement(\"a\", null, \"πŸ™‚ πŸ•\");\n")
expectPrintedJSX(t, "<a>πŸ™‚ \nπŸ•</a>", "React.createElement(\"a\", null, \"πŸ™‚ πŸ•\");\n")
expectPrintedJSX(t, "<a>πŸ™‚\n πŸ•</a>", "React.createElement(\"a\", null, \"πŸ™‚ πŸ•\");\n")
expectPrintedJSX(t, "<a> πŸ™‚</a>", "React.createElement(\"a\", null, \" πŸ™‚\");\n")
expectPrintedJSX(t, "<a> \nπŸ™‚</a>", "React.createElement(\"a\", null, \"πŸ™‚\");\n")
expectPrintedJSX(t, "<a>\n πŸ™‚</a>", "React.createElement(\"a\", null, \"πŸ™‚\");\n")

expectParseErrorJSX(t, "<a b=true/>", "<stdin>: error: Expected \"{\" but found \"true\"\n")
expectParseErrorJSX(t, "</a>", "<stdin>: error: Expected identifier but found \"/\"\n")
expectParseErrorJSX(t, "<a></b>", "<stdin>: error: Expected closing tag \"b\" to match opening tag \"a\"\n")
Expand Down

0 comments on commit c949276

Please sign in to comment.