Skip to content

Commit

Permalink
Improve XHTML entity scanning in JSX
Browse files Browse the repository at this point in the history
  • Loading branch information
ariya committed Nov 23, 2016
1 parent 0189bff commit 5bf2fab
Showing 17 changed files with 2,260 additions and 17 deletions.
61 changes: 44 additions & 17 deletions src/jsx-parser.ts
Original file line number Diff line number Diff line change
@@ -102,27 +102,54 @@ export class JSXParser extends Parser {
};
}

scanXHTMLEntity() {
scanXHTMLEntity(quote: string) {
let result = '&';

let str = '';
while (!this.scanner.eof()) {
const ch = this.scanner.source[this.scanner.index++];
if (ch === ';') {
if (str[0] === '#') {
str = str.substr(1);
const hex = (str[0] === 'x');
const cp = hex ? parseInt('0' + str, 16) : parseInt(str, 10);
result = String.fromCharCode(cp);
} else if (XHTMLEntities[str]) {
result = XHTMLEntities[str];
} else {
result += ch;
}
let valid = true;
let terminated = false;
let numeric = false;
let hex = false;

while (!this.scanner.eof() && valid && !terminated) {
const ch = this.scanner.source[this.scanner.index];
if (ch === quote) {
break;
}
str += ch;
terminated = (ch === ';');
result += ch;
++this.scanner.index;
if (!terminated) {
switch (result.length) {
case 2:
// e.g. '{'
numeric = (ch === '#');
break;
case 3:
if (numeric) {
// e.g. 'A'
hex = (ch === 'x');
valid = hex || Character.isDecimalDigit(ch.charCodeAt(0));
numeric = numeric && !hex;
}
break;
default:
valid = valid && !(numeric && !Character.isDecimalDigit(ch.charCodeAt(0)));
valid = valid && !(hex && !Character.isHexDigit(ch.charCodeAt(0)));
break;
}
}
}

if (valid && terminated && result.length > 2) {
// e.g. 'A' becomes just '#x41'
const str = result.substr(1, result.length - 2);
if (numeric && str.length > 1) {
result = String.fromCharCode(parseInt(str.substr(1), 10));
} else if (hex && str.length > 2) {
result = String.fromCharCode(parseInt('0' + str.substr(1), 16));
} else if (!numeric && !hex && XHTMLEntities[str]) {
result = XHTMLEntities[str];
}
}

return result;
@@ -156,7 +183,7 @@ export class JSXParser extends Parser {
if (ch === quote) {
break;
} else if (ch === '&') {
str += this.scanXHTMLEntity();
str += this.scanXHTMLEntity(quote);
} else {
str += ch;
}
1 change: 1 addition & 0 deletions test/fixtures/JSX/attribute-empty-entity1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<x y="&#x;" />
276 changes: 276 additions & 0 deletions test/fixtures/JSX/attribute-empty-entity1.tree.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
{
"type": "Program",
"body": [
{
"type": "ExpressionStatement",
"expression": {
"type": "JSXElement",
"openingElement": {
"type": "JSXOpeningElement",
"name": {
"type": "JSXIdentifier",
"name": "x",
"range": [
1,
2
],
"loc": {
"start": {
"line": 1,
"column": 1
},
"end": {
"line": 1,
"column": 2
}
}
},
"selfClosing": true,
"attributes": [
{
"type": "JSXAttribute",
"name": {
"type": "JSXIdentifier",
"name": "y",
"range": [
3,
4
],
"loc": {
"start": {
"line": 1,
"column": 3
},
"end": {
"line": 1,
"column": 4
}
}
},
"value": {
"type": "Literal",
"value": "&#x;",
"raw": "\"&#x;\"",
"range": [
5,
11
],
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 11
}
}
},
"range": [
3,
11
],
"loc": {
"start": {
"line": 1,
"column": 3
},
"end": {
"line": 1,
"column": 11
}
}
}
],
"range": [
0,
14
],
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 14
}
}
},
"children": [],
"closingElement": null,
"range": [
0,
14
],
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 14
}
}
},
"range": [
0,
14
],
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 14
}
}
}
],
"sourceType": "script",
"range": [
0,
14
],
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 14
}
},
"tokens": [
{
"type": "Punctuator",
"value": "<",
"range": [
0,
1
],
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 1
}
}
},
{
"type": "JSXIdentifier",
"value": "x",
"range": [
1,
2
],
"loc": {
"start": {
"line": 1,
"column": 1
},
"end": {
"line": 1,
"column": 2
}
}
},
{
"type": "JSXIdentifier",
"value": "y",
"range": [
3,
4
],
"loc": {
"start": {
"line": 1,
"column": 3
},
"end": {
"line": 1,
"column": 4
}
}
},
{
"type": "Punctuator",
"value": "=",
"range": [
4,
5
],
"loc": {
"start": {
"line": 1,
"column": 4
},
"end": {
"line": 1,
"column": 5
}
}
},
{
"type": "String",
"value": "\"&#x;\"",
"range": [
5,
11
],
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 11
}
}
},
{
"type": "Punctuator",
"value": "/",
"range": [
12,
13
],
"loc": {
"start": {
"line": 1,
"column": 12
},
"end": {
"line": 1,
"column": 13
}
}
},
{
"type": "Punctuator",
"value": ">",
"range": [
13,
14
],
"loc": {
"start": {
"line": 1,
"column": 13
},
"end": {
"line": 1,
"column": 14
}
}
}
]
}
1 change: 1 addition & 0 deletions test/fixtures/JSX/attribute-empty-entity2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<x y="&#;" />
Loading

0 comments on commit 5bf2fab

Please sign in to comment.