diff --git a/internal/transformers/jsxtransforms/jsx.go b/internal/transformers/jsxtransforms/jsx.go index a9650e5438..6b3b1e0302 100644 --- a/internal/transformers/jsxtransforms/jsx.go +++ b/internal/transformers/jsxtransforms/jsx.go @@ -839,23 +839,22 @@ func (tx *JSXTransformer) visitJsxExpression(expression *ast.JsxExpression) *ast var htmlEntityMatcher = regexp2.MustCompile(`&((#((\d+)|x([\da-fA-F]+)))|(\w+));`, regexp2.ECMAScript) func htmlEntityReplacer(m regexp2.Match) string { - decimal := m.GroupByNumber(3) - if decimal != nil { + decimal := m.GroupByNumber(4) + if decimal != nil && decimal.Capture.String() != "" { parsed, err := strconv.ParseInt(decimal.Capture.String(), 10, 32) if err == nil { return string(rune(parsed)) } } - hex := m.GroupByNumber(4) - if hex != nil { + hex := m.GroupByNumber(5) + if hex != nil && hex.Capture.String() != "" { parsed, err := strconv.ParseInt(hex.Capture.String(), 16, 32) if err == nil { return string(rune(parsed)) } } - word := m.GroupByNumber(5) - if word != nil { - // If this is not a valid entity, then just use `match` (replace it with itself, i.e. don't replace) + word := m.GroupByNumber(6) + if word != nil && word.Capture.String() != "" { res, ok := entities[word.Capture.String()] if ok { return string(res) diff --git a/testdata/baselines/reference/submodule/conformance/tsxReactEmitEntities.js b/testdata/baselines/reference/submodule/conformance/tsxReactEmitEntities.js index 5bf423bf70..4eed5bcb7e 100644 --- a/testdata/baselines/reference/submodule/conformance/tsxReactEmitEntities.js +++ b/testdata/baselines/reference/submodule/conformance/tsxReactEmitEntities.js @@ -25,16 +25,16 @@ declare var React: any;
🐈🐕🐇🐑
; //// [file.js] -React.createElement("div", null, "Dot goes here: · ¬AnEntity; "); -React.createElement("div", null, "Be careful of "-ed strings!"); -React.createElement("div", null, "{{braces}}"); +React.createElement("div", null, "Dot goes here: \u00B7 ¬AnEntity; "); +React.createElement("div", null, "Be careful of \"-ed strings!"); +React.createElement("div", null, "{{braces}}"); // Escapes do nothing React.createElement("div", null, "\\n"); // Also works in string literal attributes -React.createElement("div", { attr: "{…}\\" }); +React.createElement("div", { attr: "{\u2026}\\" }); // Does not happen for a string literal that happens to be inside an attribute (and escapes then work) React.createElement("div", { attr: "{…}\"" }); // Preserves single quotes React.createElement("div", { attr: "\"" }); // https://github.com/microsoft/TypeScript/issues/35732 -React.createElement("div", null, "🐈🐕\uD83D\uDC07\uD83D\uDC11"); +React.createElement("div", null, "\uD83D\uDC08\uD83D\uDC15\uD83D\uDC07\uD83D\uDC11"); diff --git a/testdata/baselines/reference/submodule/conformance/tsxReactEmitEntities.js.diff b/testdata/baselines/reference/submodule/conformance/tsxReactEmitEntities.js.diff index caf74392c1..7316f258cb 100644 --- a/testdata/baselines/reference/submodule/conformance/tsxReactEmitEntities.js.diff +++ b/testdata/baselines/reference/submodule/conformance/tsxReactEmitEntities.js.diff @@ -1,25 +1,10 @@ --- old.tsxReactEmitEntities.js +++ new.tsxReactEmitEntities.js -@@= skipped -24, +24 lines =@@ -
🐈🐕🐇🐑
; - - //// [file.js] --React.createElement("div", null, "Dot goes here: \u00B7 ¬AnEntity; "); --React.createElement("div", null, "Be careful of \"-ed strings!"); --React.createElement("div", null, "{{braces}}"); -+React.createElement("div", null, "Dot goes here: · ¬AnEntity; "); -+React.createElement("div", null, "Be careful of "-ed strings!"); -+React.createElement("div", null, "{{braces}}"); - // Escapes do nothing - React.createElement("div", null, "\\n"); - // Also works in string literal attributes --React.createElement("div", { attr: "{\u2026}\\" }); -+React.createElement("div", { attr: "{…}\\" }); +@@= skipped -34, +34 lines =@@ // Does not happen for a string literal that happens to be inside an attribute (and escapes then work) React.createElement("div", { attr: "{…}\"" }); // Preserves single quotes -React.createElement("div", { attr: '"' }); +React.createElement("div", { attr: "\"" }); // https://github.com/microsoft/TypeScript/issues/35732 --React.createElement("div", null, "\uD83D\uDC08\uD83D\uDC15\uD83D\uDC07\uD83D\uDC11"); -+React.createElement("div", null, "🐈🐕\uD83D\uDC07\uD83D\uDC11"); \ No newline at end of file + React.createElement("div", null, "\uD83D\uDC08\uD83D\uDC15\uD83D\uDC07\uD83D\uDC11"); \ No newline at end of file diff --git a/testdata/baselines/reference/submodule/conformance/tsxReactEmitNesting.js b/testdata/baselines/reference/submodule/conformance/tsxReactEmitNesting.js index 212b2a5476..6ce168d46f 100644 --- a/testdata/baselines/reference/submodule/conformance/tsxReactEmitNesting.js +++ b/testdata/baselines/reference/submodule/conformance/tsxReactEmitNesting.js @@ -38,6 +38,6 @@ let render = (ctrl, model) => //// [file.js] // A simple render function with nesting and control statements -let render = (ctrl, model) => vdom.createElement("section", { class: "todoapp" }, vdom.createElement("header", { class: "header" }, vdom.createElement("h1", null, "todos <x>"), vdom.createElement("input", { class: "new-todo", autofocus: true, autocomplete: "off", placeholder: "What needs to be done?", value: model.newTodo, onKeyup: ctrl.addTodo.bind(ctrl, model) })), vdom.createElement("section", { class: "main", style: { display: (model.todos && model.todos.length) ? "block" : "none" } }, vdom.createElement("input", { class: "toggle-all", type: "checkbox", onChange: ctrl.toggleAll.bind(ctrl) }), vdom.createElement("ul", { class: "todo-list" }, model.filteredTodos.map((todo) => vdom.createElement("li", { class: { todo: true, completed: todo.completed, editing: todo == model.editedTodo } }, vdom.createElement("div", { class: "view" }, (!todo.editable) ? +let render = (ctrl, model) => vdom.createElement("section", { class: "todoapp" }, vdom.createElement("header", { class: "header" }, vdom.createElement("h1", null, "todos "), vdom.createElement("input", { class: "new-todo", autofocus: true, autocomplete: "off", placeholder: "What needs to be done?", value: model.newTodo, onKeyup: ctrl.addTodo.bind(ctrl, model) })), vdom.createElement("section", { class: "main", style: { display: (model.todos && model.todos.length) ? "block" : "none" } }, vdom.createElement("input", { class: "toggle-all", type: "checkbox", onChange: ctrl.toggleAll.bind(ctrl) }), vdom.createElement("ul", { class: "todo-list" }, model.filteredTodos.map((todo) => vdom.createElement("li", { class: { todo: true, completed: todo.completed, editing: todo == model.editedTodo } }, vdom.createElement("div", { class: "view" }, (!todo.editable) ? vdom.createElement("input", { class: "toggle", type: "checkbox" }) : null, vdom.createElement("label", { onDoubleClick: () => { ctrl.editTodo(todo); } }, todo.title), vdom.createElement("button", { class: "destroy", onClick: ctrl.removeTodo.bind(ctrl, todo) }), vdom.createElement("div", { class: "iconBorder" }, vdom.createElement("div", { class: "icon" })))))))); diff --git a/testdata/baselines/reference/submodule/conformance/tsxReactEmitNesting.js.diff b/testdata/baselines/reference/submodule/conformance/tsxReactEmitNesting.js.diff index 78b7410280..c57bf6ef74 100644 --- a/testdata/baselines/reference/submodule/conformance/tsxReactEmitNesting.js.diff +++ b/testdata/baselines/reference/submodule/conformance/tsxReactEmitNesting.js.diff @@ -19,6 +19,6 @@ - vdom.createElement("button", { class: "destroy", onClick: ctrl.removeTodo.bind(ctrl, todo) }), - vdom.createElement("div", { class: "iconBorder" }, - vdom.createElement("div", { class: "icon" })))))))); -+let render = (ctrl, model) => vdom.createElement("section", { class: "todoapp" }, vdom.createElement("header", { class: "header" }, vdom.createElement("h1", null, "todos <x>"), vdom.createElement("input", { class: "new-todo", autofocus: true, autocomplete: "off", placeholder: "What needs to be done?", value: model.newTodo, onKeyup: ctrl.addTodo.bind(ctrl, model) })), vdom.createElement("section", { class: "main", style: { display: (model.todos && model.todos.length) ? "block" : "none" } }, vdom.createElement("input", { class: "toggle-all", type: "checkbox", onChange: ctrl.toggleAll.bind(ctrl) }), vdom.createElement("ul", { class: "todo-list" }, model.filteredTodos.map((todo) => vdom.createElement("li", { class: { todo: true, completed: todo.completed, editing: todo == model.editedTodo } }, vdom.createElement("div", { class: "view" }, (!todo.editable) ? ++let render = (ctrl, model) => vdom.createElement("section", { class: "todoapp" }, vdom.createElement("header", { class: "header" }, vdom.createElement("h1", null, "todos "), vdom.createElement("input", { class: "new-todo", autofocus: true, autocomplete: "off", placeholder: "What needs to be done?", value: model.newTodo, onKeyup: ctrl.addTodo.bind(ctrl, model) })), vdom.createElement("section", { class: "main", style: { display: (model.todos && model.todos.length) ? "block" : "none" } }, vdom.createElement("input", { class: "toggle-all", type: "checkbox", onChange: ctrl.toggleAll.bind(ctrl) }), vdom.createElement("ul", { class: "todo-list" }, model.filteredTodos.map((todo) => vdom.createElement("li", { class: { todo: true, completed: todo.completed, editing: todo == model.editedTodo } }, vdom.createElement("div", { class: "view" }, (!todo.editable) ? + vdom.createElement("input", { class: "toggle", type: "checkbox" }) + : null, vdom.createElement("label", { onDoubleClick: () => { ctrl.editTodo(todo); } }, todo.title), vdom.createElement("button", { class: "destroy", onClick: ctrl.removeTodo.bind(ctrl, todo) }), vdom.createElement("div", { class: "iconBorder" }, vdom.createElement("div", { class: "icon" })))))))); \ No newline at end of file