mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-28 15:06:29 -05:00
Merge pull request #10747 from Microsoft/react_emit_entities_2
When emitting react code, replace HTML numeric entities with their encoded characters
This commit is contained in:
@@ -2030,7 +2030,7 @@ const _super = (function (geti, seti) {
|
||||
emitTrailingCommentsOfPosition(commentRange.pos);
|
||||
}
|
||||
|
||||
emitExpression(node.initializer);
|
||||
emitExpression(initializer);
|
||||
}
|
||||
|
||||
function emitShorthandPropertyAssignment(node: ShorthandPropertyAssignment) {
|
||||
|
||||
@@ -905,6 +905,10 @@ namespace ts {
|
||||
return currentToken = scanner.scanJsxToken();
|
||||
}
|
||||
|
||||
function scanJsxAttributeValue(): SyntaxKind {
|
||||
return currentToken = scanner.scanJsxAttributeValue();
|
||||
}
|
||||
|
||||
function speculationHelper<T>(callback: () => T, isLookAhead: boolean): T {
|
||||
// Keep track of the state we'll need to rollback to if lookahead fails (or if the
|
||||
// caller asked us to always reset our state).
|
||||
@@ -3831,8 +3835,8 @@ namespace ts {
|
||||
scanJsxIdentifier();
|
||||
const node = <JsxAttribute>createNode(SyntaxKind.JsxAttribute);
|
||||
node.name = parseIdentifierName();
|
||||
if (parseOptional(SyntaxKind.EqualsToken)) {
|
||||
switch (token()) {
|
||||
if (token() === SyntaxKind.EqualsToken) {
|
||||
switch (scanJsxAttributeValue()) {
|
||||
case SyntaxKind.StringLiteral:
|
||||
node.initializer = parseLiteralNode();
|
||||
break;
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace ts {
|
||||
reScanSlashToken(): SyntaxKind;
|
||||
reScanTemplateToken(): SyntaxKind;
|
||||
scanJsxIdentifier(): SyntaxKind;
|
||||
scanJsxAttributeValue(): SyntaxKind;
|
||||
reScanJsxToken(): SyntaxKind;
|
||||
scanJsxToken(): SyntaxKind;
|
||||
scanJSDocToken(): SyntaxKind;
|
||||
@@ -817,6 +818,7 @@ namespace ts {
|
||||
reScanSlashToken,
|
||||
reScanTemplateToken,
|
||||
scanJsxIdentifier,
|
||||
scanJsxAttributeValue,
|
||||
reScanJsxToken,
|
||||
scanJsxToken,
|
||||
scanJSDocToken,
|
||||
@@ -911,7 +913,7 @@ namespace ts {
|
||||
return value;
|
||||
}
|
||||
|
||||
function scanString(): string {
|
||||
function scanString(allowEscapes = true): string {
|
||||
const quote = text.charCodeAt(pos);
|
||||
pos++;
|
||||
let result = "";
|
||||
@@ -929,7 +931,7 @@ namespace ts {
|
||||
pos++;
|
||||
break;
|
||||
}
|
||||
if (ch === CharacterCodes.backslash) {
|
||||
if (ch === CharacterCodes.backslash && allowEscapes) {
|
||||
result += text.substring(start, pos);
|
||||
result += scanEscapeSequence();
|
||||
start = pos;
|
||||
@@ -1737,6 +1739,20 @@ namespace ts {
|
||||
return token;
|
||||
}
|
||||
|
||||
function scanJsxAttributeValue(): SyntaxKind {
|
||||
startPos = pos;
|
||||
|
||||
switch (text.charCodeAt(pos)) {
|
||||
case CharacterCodes.doubleQuote:
|
||||
case CharacterCodes.singleQuote:
|
||||
tokenValue = scanString(/*allowEscapes*/ false);
|
||||
return token = SyntaxKind.StringLiteral;
|
||||
default:
|
||||
// If this scans anything other than `{`, it's a parse error.
|
||||
return scan();
|
||||
}
|
||||
}
|
||||
|
||||
function scanJSDocToken(): SyntaxKind {
|
||||
if (pos >= end) {
|
||||
return token = SyntaxKind.EndOfFileToken;
|
||||
|
||||
@@ -140,7 +140,8 @@ namespace ts {
|
||||
return createLiteral(true);
|
||||
}
|
||||
else if (node.kind === SyntaxKind.StringLiteral) {
|
||||
return node;
|
||||
const decoded = tryDecodeEntities((<StringLiteral>node).text);
|
||||
return decoded ? createLiteral(decoded, /*location*/ node) : node;
|
||||
}
|
||||
else if (node.kind === SyntaxKind.JsxExpression) {
|
||||
return visitJsxExpression(<JsxExpression>node);
|
||||
@@ -210,19 +211,31 @@ namespace ts {
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes JSX entities.
|
||||
* Replace entities like " ", "{", and "�" with the characters they encode.
|
||||
* See https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references
|
||||
*/
|
||||
function decodeEntities(text: string) {
|
||||
return text.replace(/&(\w+);/g, function(s: any, m: string) {
|
||||
if (entities[m] !== undefined) {
|
||||
return String.fromCharCode(entities[m]);
|
||||
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));
|
||||
}
|
||||
else if (hex) {
|
||||
return String.fromCharCode(parseInt(hex, 16));
|
||||
}
|
||||
else {
|
||||
return s;
|
||||
const ch = entities[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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Like `decodeEntities` but returns `undefined` if there were no entities to decode. */
|
||||
function tryDecodeEntities(text: string): string | undefined {
|
||||
const decoded = decodeEntities(text);
|
||||
return decoded === text ? undefined : decoded;
|
||||
}
|
||||
|
||||
function getTagName(node: JsxElement | JsxOpeningLikeElement): Expression {
|
||||
if (node.kind === SyntaxKind.JsxElement) {
|
||||
return getTagName((<JsxElement>node).openingElement);
|
||||
|
||||
@@ -9,8 +9,27 @@ declare var React: any;
|
||||
|
||||
<div>Dot goes here: · ¬AnEntity; </div>;
|
||||
<div>Be careful of "-ed strings!</div>;
|
||||
<div>{{braces}}</div>;
|
||||
// Escapes do nothing
|
||||
<div>\n</div>;
|
||||
|
||||
// Also works in string literal attributes
|
||||
<div attr="{…}\"></div>;
|
||||
// Does not happen for a string literal that happens to be inside an attribute (and escapes then work)
|
||||
<div attr={"{…}\""}></div>;
|
||||
// Preserves single quotes
|
||||
<div attr='"'></div>
|
||||
|
||||
|
||||
//// [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}}");
|
||||
// Escapes do nothing
|
||||
React.createElement("div", null, "\\n");
|
||||
// Also works in string literal attributes
|
||||
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: '"' });
|
||||
|
||||
@@ -23,3 +23,30 @@ declare var React: any;
|
||||
>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22))
|
||||
>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22))
|
||||
|
||||
<div>{{braces}}</div>;
|
||||
>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22))
|
||||
>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22))
|
||||
|
||||
// Escapes do nothing
|
||||
<div>\n</div>;
|
||||
>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22))
|
||||
>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22))
|
||||
|
||||
// Also works in string literal attributes
|
||||
<div attr="{…}\"></div>;
|
||||
>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22))
|
||||
>attr : Symbol(unknown)
|
||||
>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22))
|
||||
|
||||
// Does not happen for a string literal that happens to be inside an attribute (and escapes then work)
|
||||
<div attr={"{…}\""}></div>;
|
||||
>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22))
|
||||
>attr : Symbol(unknown)
|
||||
>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22))
|
||||
|
||||
// Preserves single quotes
|
||||
<div attr='"'></div>
|
||||
>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22))
|
||||
>attr : Symbol(unknown)
|
||||
>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22))
|
||||
|
||||
|
||||
@@ -25,3 +25,36 @@ declare var React: any;
|
||||
>div : any
|
||||
>div : any
|
||||
|
||||
<div>{{braces}}</div>;
|
||||
><div>{{braces}}</div> : JSX.Element
|
||||
>div : any
|
||||
>div : any
|
||||
|
||||
// Escapes do nothing
|
||||
<div>\n</div>;
|
||||
><div>\n</div> : JSX.Element
|
||||
>div : any
|
||||
>div : any
|
||||
|
||||
// Also works in string literal attributes
|
||||
<div attr="{…}\"></div>;
|
||||
><div attr="{…}\"></div> : JSX.Element
|
||||
>div : any
|
||||
>attr : any
|
||||
>div : any
|
||||
|
||||
// Does not happen for a string literal that happens to be inside an attribute (and escapes then work)
|
||||
<div attr={"{…}\""}></div>;
|
||||
><div attr={"{…}\""}></div> : JSX.Element
|
||||
>div : any
|
||||
>attr : any
|
||||
>"{…}\"" : string
|
||||
>div : any
|
||||
|
||||
// Preserves single quotes
|
||||
<div attr='"'></div>
|
||||
><div attr='"'></div> : JSX.Element
|
||||
>div : any
|
||||
>attr : any
|
||||
>div : any
|
||||
|
||||
|
||||
@@ -10,3 +10,13 @@ declare var React: any;
|
||||
|
||||
<div>Dot goes here: · ¬AnEntity; </div>;
|
||||
<div>Be careful of "-ed strings!</div>;
|
||||
<div>{{braces}}</div>;
|
||||
// Escapes do nothing
|
||||
<div>\n</div>;
|
||||
|
||||
// Also works in string literal attributes
|
||||
<div attr="{…}\"></div>;
|
||||
// Does not happen for a string literal that happens to be inside an attribute (and escapes then work)
|
||||
<div attr={"{…}\""}></div>;
|
||||
// Preserves single quotes
|
||||
<div attr='"'></div>
|
||||
|
||||
Reference in New Issue
Block a user