@@ -25,11 +25,7 @@ export default function () {
25
25
return opts . moduleSourceName || 'react-intl' ;
26
26
}
27
27
28
- function getMessageDescriptorKey ( path ) {
29
- if ( path . isIdentifier ( ) || path . isJSXIdentifier ( ) ) {
30
- return path . node . name ;
31
- }
32
-
28
+ function evaluatePath ( path ) {
33
29
let evaluated = path . evaluate ( ) ;
34
30
if ( evaluated . confident ) {
35
31
return evaluated . value ;
@@ -40,62 +36,74 @@ export default function () {
40
36
) ;
41
37
}
42
38
39
+ function getMessageDescriptorKey ( path ) {
40
+ if ( path . isIdentifier ( ) || path . isJSXIdentifier ( ) ) {
41
+ return path . node . name ;
42
+ }
43
+
44
+ return evaluatePath ( path ) ;
45
+ }
46
+
43
47
function getMessageDescriptorValue ( path ) {
44
48
if ( path . isJSXExpressionContainer ( ) ) {
45
49
path = path . get ( 'expression' ) ;
46
50
}
47
51
48
- let evaluated = path . evaluate ( ) ;
49
- if ( evaluated . confident ) {
50
- return evaluated . value ;
51
- }
52
-
53
- throw path . buildCodeFrameError (
54
- '[React Intl] Messages must be statically evaluate-able for extraction.'
55
- ) ;
52
+ // Always trim the Message Descriptor values.
53
+ return evaluatePath ( path ) . trim ( ) ;
56
54
}
57
55
58
- function createMessageDescriptor ( propPaths , options = { } ) {
59
- const { isJSXSource = false } = options ;
56
+ function getICUMessageValue ( messagePath , { isJSXSource = false } = { } ) {
57
+ let message = getMessageDescriptorValue ( messagePath ) ;
58
+
59
+ try {
60
+ return printICUMessage ( message ) ;
61
+ } catch ( parseError ) {
62
+ if ( isJSXSource &&
63
+ messagePath . isLiteral ( ) &&
64
+ message . indexOf ( '\\\\' ) >= 0 ) {
65
+
66
+ throw messagePath . buildCodeFrameError (
67
+ '[React Intl] Message failed to parse. ' +
68
+ 'It looks like `\\`s were used for escaping, ' +
69
+ 'this won\'t work with JSX string literals. ' +
70
+ 'Wrap with `{}`. ' +
71
+ 'See: http://facebook.github.io/react/docs/jsx-gotchas.html'
72
+ ) ;
73
+ }
74
+
75
+ throw messagePath . buildCodeFrameError (
76
+ '[React Intl] Message failed to parse. ' +
77
+ 'See: http://formatjs.io/guides/message-syntax/' +
78
+ `\n${ parseError } `
79
+ ) ;
80
+ }
81
+ }
60
82
83
+ function createMessageDescriptor ( propPaths ) {
61
84
return propPaths . reduce ( ( hash , [ keyPath , valuePath ] ) => {
62
85
let key = getMessageDescriptorKey ( keyPath ) ;
63
86
64
- if ( ! DESCRIPTOR_PROPS . has ( key ) ) {
65
- return hash ;
87
+ if ( DESCRIPTOR_PROPS . has ( key ) ) {
88
+ hash [ key ] = valuePath ;
66
89
}
67
90
68
- let value = getMessageDescriptorValue ( valuePath ) . trim ( ) ;
91
+ return hash ;
92
+ } , { } ) ;
93
+ }
69
94
70
- if ( key === 'defaultMessage' ) {
71
- try {
72
- hash [ key ] = printICUMessage ( value ) ;
73
- } catch ( parseError ) {
74
- if ( isJSXSource &&
75
- valuePath . isLiteral ( ) &&
76
- value . indexOf ( '\\\\' ) >= 0 ) {
77
-
78
- throw valuePath . buildCodeFrameError (
79
- '[React Intl] Message failed to parse. ' +
80
- 'It looks like `\\`s were used for escaping, ' +
81
- 'this won\'t work with JSX string literals. ' +
82
- 'Wrap with `{}`. ' +
83
- 'See: http://facebook.github.io/react/docs/jsx-gotchas.html'
84
- ) ;
85
- }
95
+ function evaluateMessageDescriptor ( { ...descriptor } , { isJSXSource = false } = { } ) {
96
+ Object . keys ( descriptor ) . forEach ( ( key ) => {
97
+ let valuePath = descriptor [ key ] ;
86
98
87
- throw valuePath . buildCodeFrameError (
88
- '[React Intl] Message failed to parse. ' +
89
- 'See: http://formatjs.io/guides/message-syntax/' ,
90
- parseError
91
- ) ;
92
- }
99
+ if ( key === 'defaultMessage' ) {
100
+ descriptor [ key ] = getICUMessageValue ( valuePath , { isJSXSource} ) ;
93
101
} else {
94
- hash [ key ] = value ;
102
+ descriptor [ key ] = getMessageDescriptorValue ( valuePath ) ;
95
103
}
104
+ } ) ;
96
105
97
- return hash ;
98
- } , { } ) ;
106
+ return descriptor ;
99
107
}
100
108
101
109
function storeMessage ( { id, description, defaultMessage} , path , state ) {
@@ -199,18 +207,22 @@ export default function () {
199
207
attributes . map ( ( attr ) => [
200
208
attr . get ( 'name' ) ,
201
209
attr . get ( 'value' ) ,
202
- ] ) ,
203
- { isJSXSource : true }
210
+ ] )
204
211
) ;
205
212
206
213
// In order for a default message to be extracted when
207
214
// declaring a JSX element, it must be done with standard
208
215
// `key=value` attributes. But it's completely valid to
209
- // write `<FormattedMessage {...descriptor} />`, because it
210
- // will be skipped here and extracted elsewhere. When the
211
- // `defaultMessage` prop exists, the descriptor will be
212
- // checked .
216
+ // write `<FormattedMessage {...descriptor} />` or
217
+ // `<FormattedMessage id={dynamicId} />`, because it will be
218
+ // skipped here and extracted elsewhere. The descriptor will
219
+ // be extracted only if a `defaultMessage` prop exists .
213
220
if ( descriptor . defaultMessage ) {
221
+ // Evaluate the Message Descriptor values in a JSX
222
+ // context, then store it.
223
+ descriptor = evaluateMessageDescriptor ( descriptor , {
224
+ isJSXSource : true ,
225
+ } ) ;
214
226
storeMessage ( descriptor , path , state ) ;
215
227
}
216
228
}
@@ -243,12 +255,8 @@ export default function () {
243
255
] )
244
256
) ;
245
257
246
- if ( ! descriptor . defaultMessage ) {
247
- throw path . buildCodeFrameError (
248
- '[React Intl] Message is missing a `defaultMessage`.'
249
- ) ;
250
- }
251
-
258
+ // Evaluate the Message Descriptor values, then store it.
259
+ descriptor = evaluateMessageDescriptor ( descriptor ) ;
252
260
storeMessage ( descriptor , path , state ) ;
253
261
}
254
262
0 commit comments