Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix handling of HTML entities in JSX parsing #56

Merged
merged 2 commits into from Nov 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 16 additions & 10 deletions src/jsx-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,24 @@ export function parseJSX(fragment) {
ontext: (text) => {
let txt = text;
let m = jsExpr.exec(txt);

const addText = txt => {
const lastNode = stack[0].childNodes.slice(-1)[0];
if (lastNode && lastNode.nodeName === '#text') {
lastNode.value += txt;
} else {
stack[0].childNodes.push({
nodeName: '#text',
value: txt,
childNodes: []
});
}
};

if (m) {
while ((m = jsExpr.exec(txt))) {
if (m[1]) {
stack[0].childNodes.push({
nodeName: '#text',
value: m[1],
childNodes: []
});
addText(m[1]);
}
stack[0].childNodes.push({
nodeName: '#expression',
Expand All @@ -44,11 +54,7 @@ export function parseJSX(fragment) {
}
}
if (txt) {
stack[0].childNodes.push({
nodeName: '#text',
value: txt,
childNodes: []
});
addText(txt);
}
}
};
Expand Down
106 changes: 58 additions & 48 deletions test/jsx-parser.js
Original file line number Diff line number Diff line change
@@ -1,88 +1,98 @@
import fs from 'fs';
import path from 'path';
import { test } from 'tap';
import { parseJSX, jsxToText } from '../src/jsx-parser';

const defaults = {};

test('JSX Parse plain text', (t) => {
const ast = parseJSX('This is plain text')
const ast = parseJSX('This is plain text');
t.same(ast.length, 1);
t.same(ast[0].nodeName, '#text')
t.same(ast[0].value, 'This is plain text')
t.same(ast[0].nodeName, '#text');
t.same(ast[0].value, 'This is plain text');
t.end();
});

test('JSX Transform bare javascript expression', (t) => {
const ast = parseJSX('{{name}}')
const ast = parseJSX('{{name}}');
t.same(ast.length, 1);
t.same(ast[0].nodeName, '#expression')
t.same(ast[0].value, '{{name}}')
t.same(ast[0].nodeName, '#expression');
t.same(ast[0].value, '{{name}}');
t.end();
});

test('JSX Transform leading javascript expression', (t) => {
const ast = parseJSX('{{name}}, you are so fine')
const ast = parseJSX('{{name}}, you are so fine');
t.same(ast.length, 2);
t.same(ast[0].nodeName, '#expression')
t.same(ast[0].value, '{{name}}')
t.same(ast[1].nodeName, '#text')
t.same(ast[1].value, ', you are so fine')
t.same(ast[0].nodeName, '#expression');
t.same(ast[0].value, '{{name}}');
t.same(ast[1].nodeName, '#text');
t.same(ast[1].value, ', you are so fine');
t.end();
});

test('JSX Transform trailing javascript expression', (t) => {
const ast = parseJSX('My name is {{name}}')
const ast = parseJSX('My name is {{name}}');
t.same(ast.length, 2);
t.same(ast[0].nodeName, '#text')
t.same(ast[0].value, 'My name is ')
t.same(ast[1].nodeName, '#expression')
t.same(ast[1].value, '{{name}}')
t.same(ast[0].nodeName, '#text');
t.same(ast[0].value, 'My name is ');
t.same(ast[1].nodeName, '#expression');
t.same(ast[1].value, '{{name}}');
t.end();
});

test('JSX Transform javascript expression', (t) => {
const ast = parseJSX('My name is {{name}}. And you?')
const ast = parseJSX('My name is {{name}}. And you?');
t.same(ast.length, 3);
t.same(ast[0].nodeName, '#text')
t.same(ast[0].value, 'My name is ')
t.same(ast[1].nodeName, '#expression')
t.same(ast[1].value, '{{name}}')
t.same(ast[2].nodeName, '#text')
t.same(ast[2].value, '. And you?')
t.same(ast[0].nodeName, '#text');
t.same(ast[0].value, 'My name is ');
t.same(ast[1].nodeName, '#expression');
t.same(ast[1].value, '{{name}}');
t.same(ast[2].nodeName, '#text');
t.same(ast[2].value, '. And you?');
t.end();
});

test('JSX Nested expression', (t) => {
const ast = parseJSX('Hello, <strong>{{name}}</strong>, how are you?')
const ast = parseJSX('Hello, <strong>{{name}}</strong>, how are you?');
t.same(ast.length, 3);
t.same(ast[0].nodeName, '#text')
t.same(ast[1].nodeName, 'strong')
t.same(ast[1].childNodes.length, 1)
t.same(ast[1].childNodes[0].nodeName, '#expression')
t.same(ast[1].childNodes[0].value, '{{name}}')
t.same(ast[2].nodeName, '#text')
t.same(ast[0].nodeName, '#text');
t.same(ast[1].nodeName, 'strong');
t.same(ast[1].childNodes.length, 1);
t.same(ast[1].childNodes[0].nodeName, '#expression');
t.same(ast[1].childNodes[0].value, '{{name}}');
t.same(ast[2].nodeName, '#text');
t.end();
});


test('JSX child node', (t) => {
const ast = parseJSX('I agree to the <Link>terms</Link>.')
t.same(ast.length, 3)
t.same(ast[0].nodeName, '#text')
t.same(ast[1].nodeName, 'Link')
t.same(ast[1].childNodes.length, 1)
t.same(ast[1].childNodes[0].nodeName, '#text')
t.same(ast[1].childNodes[0].value, 'terms')
t.same(ast[2].nodeName, '#text')
t.same(ast[2].value, '.')
const ast = parseJSX('I agree to the <Link>terms</Link>.');
t.same(ast.length, 3);
t.same(ast[0].nodeName, '#text');
t.same(ast[1].nodeName, 'Link');
t.same(ast[1].childNodes.length, 1);
t.same(ast[1].childNodes[0].nodeName, '#text');
t.same(ast[1].childNodes[0].value, 'terms');
t.same(ast[2].nodeName, '#text');
t.same(ast[2].value, '.');
t.end();
});

test('parseJSX merges text nodes', (t) => {
const ast = parseJSX('Don&apos;t do this');
t.same(ast.length, 1);
t.same(ast[0].value, 'Don\'t do this');
t.end();
});

test('JSX to i18next', (t) => {
t.same(jsxToText('Basic text'), 'Basic text')
t.same(jsxToText('Hello, {{name}}'), 'Hello, <1>{{name}}</1>')
t.same(jsxToText('I agree to the <Link>terms</Link>.'), 'I agree to the <1>terms</1>.')
t.same(jsxToText('One &amp; two'), 'One & two')
t.end()
})
t.same(jsxToText('Basic text'), 'Basic text');
t.same(jsxToText('Hello, {{name}}'), 'Hello, <1>{{name}}</1>');
t.same(jsxToText('I agree to the <Link>terms</Link>.'), 'I agree to the <1>terms</1>.');
t.same(jsxToText('One &amp; two'), 'One & two');
t.end();
});

test('HTML entities', (t) => {
t.same(jsxToText('Don&apos;t do this <strong>Dave</strong>'), 'Don\'t do this <1>Dave</1>');
t.end();
});