Skip to content

Commit 8630c1b

Browse files
committed
JSX: Fix whitespace and escaping of attribute values
The behavior is reversed engineer from Babel since the semantics is not documented in the JSX "spec". Fixes #2088
1 parent c58237a commit 8630c1b

File tree

2 files changed

+45
-0
lines changed

2 files changed

+45
-0
lines changed

src/codegeneration/JsxTransformer.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@ import {
1717
JSX_PLACEHOLDER,
1818
JSX_SPREAD_ATTRIBUTE,
1919
JSX_TEXT,
20+
LITERAL_EXPRESSION,
2021
} from '../syntax/trees/ParseTreeType.js';
2122
import {
2223
JsxText,
2324
LiteralExpression,
2425
LiteralPropertyName,
2526
SpreadExpression,
2627
} from '../syntax/trees/ParseTrees.js';
28+
import {LiteralToken} from '../syntax/LiteralToken.js';
2729
import {ParseTreeTransformer} from './ParseTreeTransformer.js';
2830
import {STRING} from '../syntax/TokenType.js';
2931
import {
@@ -135,6 +137,13 @@ export class JsxTransformer extends ParseTreeTransformer {
135137
let value;
136138
if (tree.value === null) {
137139
value = createTrueLiteral();
140+
} else if (tree.value.type === LITERAL_EXPRESSION) {
141+
const {literalToken} = tree.value;
142+
const v = literalToken.value;
143+
const {location} = literalToken;
144+
const lit =
145+
new LiteralToken(STRING, normalizeAttributeValue(v), location);
146+
value = new LiteralExpression(location, lit);
138147
} else {
139148
value = this.transformAny(tree.value);
140149
}
@@ -202,3 +211,9 @@ function jsxIdentifierToToken(token) {
202211
}
203212
return createIdentifierToken(value);
204213
}
214+
215+
function normalizeAttributeValue(s) {
216+
return JSON.stringify(
217+
s.slice(1, -1). // remove the quotes
218+
replace(/\n\s+/g, ' '));
219+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Options: --jsx=f
2+
3+
// This file has trailing whitespace. Make sure your editor does not strip
4+
// them.
5+
6+
function f(name, props) {
7+
return props.b;
8+
}
9+
10+
assert.equal(<a b=" c "/>, ' c ');
11+
assert.equal(<a b=" c "/>, ' c ');
12+
assert.equal(<a b="c "/>, 'c ');
13+
assert.equal(<a b="c "/>, 'c ');
14+
assert.equal(<a b="\tc\n"/>, '\\tc\\n');
15+
assert.equal(<a b="'"/>, '\'');
16+
assert.equal(<a b='"'/>, '"');
17+
assert.equal(<a b="&"/>, '&');
18+
assert.equal(<a b='
19+
'/>, '\n');
20+
assert.equal(<a b='
21+
'/>, ' \n');
22+
assert.equal(<a b='
23+
'/>, ' ');
24+
assert.equal(<a b='
25+
26+
'/>, ' ');
27+
assert.equal(<a b='
28+
29+
'/>, ' ');
30+
assert.equal(<a b=' '/>, '\t');

0 commit comments

Comments
 (0)