Also decode entities when emitting attributes. Also, lexer should not process string escapes in jsx attributes.

This commit is contained in:
Andy Hanson 2016-09-08 07:49:29 -07:00
parent eea03801e0
commit a8eb4a20df
8 changed files with 111 additions and 7 deletions

View File

@ -2030,7 +2030,7 @@ const _super = (function (geti, seti) {
emitTrailingCommentsOfPosition(commentRange.pos);
}
emitExpression(node.initializer);
emitExpression(initializer);
}
function emitShorthandPropertyAssignment(node: ShorthandPropertyAssignment) {

View File

@ -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;

View File

@ -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;

View File

@ -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);
@ -213,7 +214,7 @@ namespace ts {
* Replace entities like "&nbsp;", "&#123;", and "&#xDEADBEEF;" with the characters they encode.
* See https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references
*/
function decodeEntities(text: string) {
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));
@ -229,6 +230,12 @@ namespace ts {
});
}
/** 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);

View File

@ -10,9 +10,26 @@ declare var React: any;
<div>Dot goes here: &middot; &notAnEntity; </div>;
<div>Be careful of &quot;-ed strings!</div>;
<div>&#0123;&#123;braces&#x7d;&#x7D;</div>;
// Escapes do nothing
<div>\n</div>;
// Also works in string literal attributes
<div attr="&#0123;&hellip;&#x7D;\"></div>;
// Does not happen for a string literal that happens to be inside an attribute (and escapes then work)
<div attr={"&#0123;&hellip;&#x7D;\""}></div>;
// Preserves single quotes
<div attr='"'></div>
//// [file.js]
React.createElement("div", null, "Dot goes here: \u00B7 &notAnEntity; ");
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: "&#0123;&hellip;&#x7D;\"" });
// Preserves single quotes
React.createElement("div", { attr: '"' });

View File

@ -27,3 +27,26 @@ declare var React: any;
>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="&#0123;&hellip;&#x7D;\"></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={"&#0123;&hellip;&#x7D;\""}></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))

View File

@ -30,3 +30,31 @@ declare var React: any;
>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="&#0123;&hellip;&#x7D;\"></div>;
><div attr="&#0123;&hellip;&#x7D;\"></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={"&#0123;&hellip;&#x7D;\""}></div>;
><div attr={"&#0123;&hellip;&#x7D;\""}></div> : JSX.Element
>div : any
>attr : any
>"&#0123;&hellip;&#x7D;\"" : string
>div : any
// Preserves single quotes
<div attr='"'></div>
><div attr='"'></div> : JSX.Element
>div : any
>attr : any
>div : any

View File

@ -11,3 +11,12 @@ declare var React: any;
<div>Dot goes here: &middot; &notAnEntity; </div>;
<div>Be careful of &quot;-ed strings!</div>;
<div>&#0123;&#123;braces&#x7d;&#x7D;</div>;
// Escapes do nothing
<div>\n</div>;
// Also works in string literal attributes
<div attr="&#0123;&hellip;&#x7D;\"></div>;
// Does not happen for a string literal that happens to be inside an attribute (and escapes then work)
<div attr={"&#0123;&hellip;&#x7D;\""}></div>;
// Preserves single quotes
<div attr='"'></div>