diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 09a3215b0f5..e20ab163172 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -1337,20 +1337,6 @@ namespace ts { return utf16EncodeAsString(escapedValue); } - // Derived from the 10.1.1 UTF16Encoding of the ES6 Spec. - function utf16EncodeAsString(codePoint: number): string { - Debug.assert(0x0 <= codePoint && codePoint <= 0x10FFFF); - - if (codePoint <= 65535) { - return String.fromCharCode(codePoint); - } - - const codeUnit1 = Math.floor((codePoint - 65536) / 1024) + 0xD800; - const codeUnit2 = ((codePoint - 65536) % 1024) + 0xDC00; - - return String.fromCharCode(codeUnit1, codeUnit2); - } - // Current character is known to be a backslash. Check for Unicode escape of the form '\uXXXX' // and return code point value if valid Unicode escape is found. Otherwise return -1. function peekUnicodeEscape(): number { @@ -2339,4 +2325,25 @@ namespace ts { } return 1; } + + // Derived from the 10.1.1 UTF16Encoding of the ES6 Spec. + function utf16EncodeAsStringFallback(codePoint: number) { + Debug.assert(0x0 <= codePoint && codePoint <= 0x10FFFF); + + if (codePoint <= 65535) { + return String.fromCharCode(codePoint); + } + + const codeUnit1 = Math.floor((codePoint - 65536) / 1024) + 0xD800; + const codeUnit2 = ((codePoint - 65536) % 1024) + 0xDC00; + + return String.fromCharCode(codeUnit1, codeUnit2); + } + + const utf16EncodeAsStringWorker: (codePoint: number) => string = (String as any).fromCodePoint ? codePoint => String.fromCodePoint(codePoint) : utf16EncodeAsStringFallback; + + /* @internal */ + export function utf16EncodeAsString(codePoint: number) { + return utf16EncodeAsStringWorker(codePoint); + } } diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index 4b75deb95cb..308bfc18664 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -253,15 +253,15 @@ namespace ts { function decodeEntities(text: string): string { return text.replace(/&((#((\d+)|x([\da-fA-F]+)))|(\w+));/g, (match, _all, _number, _digits, decimal, hex, word) => { if (decimal) { - return String.fromCharCode(parseInt(decimal, 10)); + return utf16EncodeAsString(parseInt(decimal, 10)); } else if (hex) { - return String.fromCharCode(parseInt(hex, 16)); + return utf16EncodeAsString(parseInt(hex, 16)); } else { const ch = entities.get(word); // If this is not a valid entity, then just use `match` (replace it with itself, i.e. don't replace) - return ch ? String.fromCharCode(ch) : match; + return ch ? utf16EncodeAsString(ch) : match; } }); } diff --git a/tests/baselines/reference/tsxReactEmitEntities.js b/tests/baselines/reference/tsxReactEmitEntities.js index 41c294b8bab..dac60a9c33a 100644 --- a/tests/baselines/reference/tsxReactEmitEntities.js +++ b/tests/baselines/reference/tsxReactEmitEntities.js @@ -18,8 +18,9 @@ declare var React: any; // Does not happen for a string literal that happens to be inside an attribute (and escapes then work)
; // Preserves single quotes -
- +
; +// https://github.com/microsoft/TypeScript/issues/35732 +
🐈🐕🐇🐑
; //// [file.js] React.createElement("div", null, "Dot goes here: \u00B7 ¬AnEntity; "); @@ -33,3 +34,5 @@ React.createElement("div", { attr: "{\u2026}\\" }); React.createElement("div", { attr: "{…}\"" }); // Preserves single quotes React.createElement("div", { attr: '"' }); +// https://github.com/microsoft/TypeScript/issues/35732 +React.createElement("div", null, "\uD83D\uDC08\uD83D\uDC15\uD83D\uDC07\uD83D\uDC11"); diff --git a/tests/baselines/reference/tsxReactEmitEntities.symbols b/tests/baselines/reference/tsxReactEmitEntities.symbols index edb7f038c7b..567e4b66449 100644 --- a/tests/baselines/reference/tsxReactEmitEntities.symbols +++ b/tests/baselines/reference/tsxReactEmitEntities.symbols @@ -45,8 +45,13 @@ declare var React: any; >div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22)) // Preserves single quotes -
+
; >div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22)) >attr : Symbol(attr, Decl(file.tsx, 19, 4)) >div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22)) +// https://github.com/microsoft/TypeScript/issues/35732 +
🐈🐕🐇🐑
; +>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22)) +>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22)) + diff --git a/tests/baselines/reference/tsxReactEmitEntities.types b/tests/baselines/reference/tsxReactEmitEntities.types index 1029b8d8ffd..084cdfb20e5 100644 --- a/tests/baselines/reference/tsxReactEmitEntities.types +++ b/tests/baselines/reference/tsxReactEmitEntities.types @@ -46,9 +46,15 @@ declare var React: any; >div : any // Preserves single quotes -
+
; >
: JSX.Element >div : any >attr : string >div : any +// https://github.com/microsoft/TypeScript/issues/35732 +
🐈🐕🐇🐑
; +>
🐈🐕🐇🐑
: JSX.Element +>div : any +>div : any + diff --git a/tests/cases/conformance/jsx/tsxReactEmitEntities.tsx b/tests/cases/conformance/jsx/tsxReactEmitEntities.tsx index e4b83ebb34b..a2200e32a9b 100644 --- a/tests/cases/conformance/jsx/tsxReactEmitEntities.tsx +++ b/tests/cases/conformance/jsx/tsxReactEmitEntities.tsx @@ -19,4 +19,6 @@ declare var React: any; // Does not happen for a string literal that happens to be inside an attribute (and escapes then work)
; // Preserves single quotes -
+
; +// https://github.com/microsoft/TypeScript/issues/35732 +
🐈🐕🐇🐑
; \ No newline at end of file