@@ -25,11 +25,7 @@ export default function () {
2525 return opts . moduleSourceName || 'react-intl' ;
2626 }
2727
28- function getMessageDescriptorKey ( path ) {
29- if ( path . isIdentifier ( ) || path . isJSXIdentifier ( ) ) {
30- return path . node . name ;
31- }
32-
28+ function evaluatePath ( path ) {
3329 let evaluated = path . evaluate ( ) ;
3430 if ( evaluated . confident ) {
3531 return evaluated . value ;
@@ -40,62 +36,74 @@ export default function () {
4036 ) ;
4137 }
4238
39+ function getMessageDescriptorKey ( path ) {
40+ if ( path . isIdentifier ( ) || path . isJSXIdentifier ( ) ) {
41+ return path . node . name ;
42+ }
43+
44+ return evaluatePath ( path ) ;
45+ }
46+
4347 function getMessageDescriptorValue ( path ) {
4448 if ( path . isJSXExpressionContainer ( ) ) {
4549 path = path . get ( 'expression' ) ;
4650 }
4751
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 ( ) ;
5654 }
5755
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+ }
6082
83+ function createMessageDescriptor ( propPaths ) {
6184 return propPaths . reduce ( ( hash , [ keyPath , valuePath ] ) => {
6285 let key = getMessageDescriptorKey ( keyPath ) ;
6386
64- if ( ! DESCRIPTOR_PROPS . has ( key ) ) {
65- return hash ;
87+ if ( DESCRIPTOR_PROPS . has ( key ) ) {
88+ hash [ key ] = valuePath ;
6689 }
6790
68- let value = getMessageDescriptorValue ( valuePath ) . trim ( ) ;
91+ return hash ;
92+ } , { } ) ;
93+ }
6994
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 ] ;
8698
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} ) ;
93101 } else {
94- hash [ key ] = value ;
102+ descriptor [ key ] = getMessageDescriptorValue ( valuePath ) ;
95103 }
104+ } ) ;
96105
97- return hash ;
98- } , { } ) ;
106+ return descriptor ;
99107 }
100108
101109 function storeMessage ( { id, description, defaultMessage} , path , state ) {
@@ -199,18 +207,22 @@ export default function () {
199207 attributes . map ( ( attr ) => [
200208 attr . get ( 'name' ) ,
201209 attr . get ( 'value' ) ,
202- ] ) ,
203- { isJSXSource : true }
210+ ] )
204211 ) ;
205212
206213 // In order for a default message to be extracted when
207214 // declaring a JSX element, it must be done with standard
208215 // `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 .
213220 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+ } ) ;
214226 storeMessage ( descriptor , path , state ) ;
215227 }
216228 }
@@ -243,12 +255,8 @@ export default function () {
243255 ] )
244256 ) ;
245257
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 ) ;
252260 storeMessage ( descriptor , path , state ) ;
253261 }
254262
0 commit comments