Skip to content
This repository was archived by the owner on Jun 8, 2019. It is now read-only.

Commit 7a3b57b

Browse files
committed
Fix backslash-escaping issues extracted messages
This fixes the backslash-escaping issues when extracting messages. This also upgrades to [email protected] which has support for escaping backslashes themselves. One wrinkle is that strings declared in JSX are double-escaped like XML/HTML. This means that if backslash-escaping is needed for a message that will be extracted, it must be wrapped in `{}` to make it an JSXExpression. A warning has been added when this pattern is detected with a helpful message on how to resolve it. See: http://facebook.github.io/react/docs/jsx-gotchas.html Fixes #13
1 parent 87d90c1 commit 7a3b57b

File tree

3 files changed

+35
-8
lines changed

3 files changed

+35
-8
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"author": "Eric Ferraiuolo <[email protected]>",
1212
"dependencies": {
1313
"babel-runtime": "^5.8.20",
14-
"intl-messageformat-parser": "^1.1.0",
14+
"intl-messageformat-parser": "^1.2.0",
1515
"mkdirp": "^0.5.1"
1616
},
1717
"devDependencies": {

src/index.js

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ export default function ({Plugin, types: t}) {
6464
);
6565
}
6666

67-
function createMessageDescriptor(propPaths) {
67+
function createMessageDescriptor(propPaths, options = {}) {
68+
const {isJSXSource = false} = options;
69+
6870
return propPaths.reduce((hash, [keyPath, valuePath]) => {
6971
let key = getMessageDescriptorKey(keyPath);
7072

@@ -75,6 +77,19 @@ export default function ({Plugin, types: t}) {
7577
try {
7678
hash[key] = printICUMessage(value);
7779
} catch (e) {
80+
if (isJSXSource &&
81+
valuePath.isLiteral() &&
82+
value.indexOf('\\\\') >= 0) {
83+
84+
throw valuePath.errorWithNode(
85+
'[React Intl] Message failed to parse. ' +
86+
'It looks like `\\`s were used for escaping, ' +
87+
'this won\'t work with JSX string literals. ' +
88+
'Wrap with `{}`. ' +
89+
'See: http://facebook.github.io/react/docs/jsx-gotchas.html'
90+
);
91+
}
92+
7893
throw valuePath.errorWithNode(
7994
`[React Intl] Message failed to parse: ${e} ` +
8095
'See: http://formatjs.io/guides/message-syntax/'
@@ -203,7 +218,8 @@ export default function ({Plugin, types: t}) {
203218
attributes.map((attr) => [
204219
attr.get('name'),
205220
attr.get('value'),
206-
])
221+
]),
222+
{isJSXSource: true}
207223
);
208224

209225
// In order for a default message to be extracted when

src/print-icu-message.js

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@
66

77
import {parse} from 'intl-messageformat-parser';
88

9+
const ESCAPED_CHARS = {
10+
'\\' : '\\\\',
11+
'\\#': '\\#',
12+
'{' : '\\{',
13+
'}' : '\\}',
14+
};
15+
16+
const ESAPE_CHARS_REGEXP = /\\#|[{}\\]/g;
17+
918
export default function (message) {
1019
let ast = parse(message);
1120
return printICUMessage(ast);
@@ -14,7 +23,7 @@ export default function (message) {
1423
function printICUMessage(ast) {
1524
let printedNodes = ast.elements.map((node) => {
1625
if (node.type === 'messageTextElement') {
17-
return node.value;
26+
return printMessageTextASTNode(node);
1827
}
1928

2029
if (!node.format) {
@@ -41,16 +50,18 @@ function getArgumentType(astType) {
4150
return astType.replace(/Format$/, '').toLowerCase();
4251
}
4352

44-
function printSimpleFormatASTNode(node) {
45-
let {id, format} = node;
53+
function printMessageTextASTNode({value}) {
54+
return value.replace(ESAPE_CHARS_REGEXP, (char) => ESCAPED_CHARS[char]);
55+
}
56+
57+
function printSimpleFormatASTNode({id, format}) {
4658
let argumentType = getArgumentType(format.type);
4759
let style = format.style ? `, ${format.style}` : '';
4860

4961
return `{${id}, ${argumentType}${style}}`;
5062
}
5163

52-
function printOptionalFormatASTNode(node) {
53-
let {id, format} = node;
64+
function printOptionalFormatASTNode({id, format}) {
5465
let argumentType = getArgumentType(format.type);
5566
let offset = format.offset ? `, offset:${format.offset}` : '';
5667

0 commit comments

Comments
 (0)