Skip to content
This repository was archived by the owner on May 4, 2020. It is now read-only.

Commit af58ad2

Browse files
author
Long Ho
committed
feat(babel-plugin-react-intl): add option to parse pragma
1 parent 39f1fe4 commit af58ad2

File tree

7 files changed

+64
-7
lines changed

7 files changed

+64
-7
lines changed

packages/babel-plugin-react-intl/README.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ If a message descriptor has a `description`, it'll be removed from the source af
4545
}
4646
```
4747

48-
#### Options
48+
### Options
4949

5050
- **`messagesDir`**: The target location where the plugin will output a `.json` file corresponding to each component from which React Intl messages were extracted. If not provided, the extracted message descriptors will only be accessible via Babel's API.
5151

@@ -63,14 +63,25 @@ If a message descriptor has a `description`, it'll be removed from the source af
6363

6464
- **`outputEmptyJson`**: output file with empty `[]` if src has no messages. For build systems like bazel that relies on specific output mapping, not writing out a file can cause issues.
6565

66+
- **`pragma`**: parse specific additional custom pragma. This allows you to tag certain file with metadata such as `project`. For example with this file:
67+
68+
```tsx
69+
// @intl-meta project:my-custom-project
70+
import {FormattedMessage} from 'react-intl';
71+
72+
<FormattedMessage defaultMessage="foo" id="bar" />;
73+
```
74+
75+
and with option `{pragma: "@intl-meta"}`, we'll parse out `// @intl-meta project:my-custom-project` into `{project: 'my-custom-project'}` in the result file.
76+
6677
### Via Node API
6778

6879
The extract message descriptors are available via the `metadata` property on the object returned from Babel's `transform()` API:
6980

7081
```javascript
7182
require('@babel/core').transform('code', {
7283
plugins: ['react-intl'],
73-
}); // => { code, map, ast, metadata['react-intl'].messages };
84+
}); // => { code, map, ast, metadata['react-intl'].messages, metadata['react-intl'].meta };
7485
```
7586

7687
[react intl]: http://formatjs.io/react/

packages/babel-plugin-react-intl/src/index.ts

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ interface MessageDescriptor {
4545
export type ExtractedMessageDescriptor = MessageDescriptor &
4646
Partial<SourceLocation> & {file?: string};
4747

48+
export type ExtractionResult = {
49+
messages: ExtractedMessageDescriptor[];
50+
meta: Map<string, string>;
51+
};
52+
4853
type MessageDescriptorPath = Record<
4954
keyof MessageDescriptor,
5055
NodePath<StringLiteral> | undefined
@@ -84,6 +89,7 @@ interface BabelTransformationFile {
8489

8590
interface State {
8691
ReactIntlMessages: Map<string, ExtractedMessageDescriptor>;
92+
ReactIntlMeta: Map<string, string>;
8793
}
8894

8995
function getICUMessageValue(
@@ -335,7 +341,7 @@ export default declare((api: any, options: OptionsSchema) => {
335341
name: 'babel-plugin-react-intl',
336342
baseDataPath: 'options',
337343
});
338-
const {messagesDir, outputEmptyJson} = options;
344+
const {messagesDir, outputEmptyJson, pragma} = options;
339345

340346
/**
341347
* Store this in the node itself so that multiple passes work. Specifically
@@ -355,6 +361,7 @@ export default declare((api: any, options: OptionsSchema) => {
355361
pre() {
356362
if (!this.ReactIntlMessages) {
357363
this.ReactIntlMessages = new Map();
364+
this.ReactIntlMeta = new Map();
358365
}
359366
},
360367

@@ -370,9 +377,12 @@ export default declare((api: any, options: OptionsSchema) => {
370377
const basename = filename
371378
? p.basename(filename, p.extname(filename))
372379
: null;
373-
const {ReactIntlMessages: messages} = this;
380+
const {ReactIntlMessages: messages, ReactIntlMeta} = this;
374381
const descriptors = Array.from(messages.values());
375-
state.metadata['react-intl'] = {messages: descriptors};
382+
state.metadata['react-intl'] = {
383+
messages: descriptors,
384+
meta: ReactIntlMeta,
385+
} as ExtractionResult;
376386

377387
if (basename && messagesDir && (outputEmptyJson || descriptors.length)) {
378388
// Make sure the relative path is "absolute" before
@@ -401,6 +411,28 @@ export default declare((api: any, options: OptionsSchema) => {
401411
},
402412

403413
visitor: {
414+
ImportDeclaration(path) {
415+
const comments = path.node.leadingComments;
416+
const {ReactIntlMeta} = this;
417+
if (!pragma || !comments) {
418+
return;
419+
}
420+
const pragmaLine = comments
421+
.map(c => c.value)
422+
.find(c => c.includes(pragma));
423+
console.log(comments);
424+
if (!pragmaLine) {
425+
return;
426+
}
427+
pragmaLine
428+
.split(pragma)[1]
429+
.trim()
430+
.split(/\s+/g)
431+
.forEach(kv => {
432+
const [k, v] = kv.split(':');
433+
ReactIntlMeta.set(k, v);
434+
});
435+
},
404436
JSXOpeningElement(
405437
path,
406438
{

packages/babel-plugin-react-intl/src/options.schema.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
"removeDefaultMessage": {"type": "boolean"},
1212
"extractFromFormatMessageCall": {"type": "boolean"},
1313
"additionalComponentNames": {"type": "array", "items": {"type": "string"}},
14-
"outputEmptyJson": {"type": "boolean"}
14+
"outputEmptyJson": {"type": "boolean"},
15+
"pragma": {"type": "string"}
1516
},
1617
"additionalProperties": false
1718
}

packages/babel-plugin-react-intl/src/options.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ export interface OptionsSchema {
1414
extractFromFormatMessageCall?: boolean;
1515
additionalComponentNames?: string[];
1616
outputEmptyJson?: boolean;
17+
pragma?: string;
1718
}

packages/babel-plugin-react-intl/test/__snapshots__/index.test.ts.snap

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Object {
99
"id": "foo.bar.baz",
1010
},
1111
],
12+
"meta": Map {},
1213
}
1314
`;
1415

@@ -83,6 +84,9 @@ Object {
8384
"id": "escaped.apostrophe",
8485
},
8586
],
87+
"meta": Map {
88+
"project" => "amazing",
89+
},
8690
}
8791
`;
8892

@@ -102,6 +106,7 @@ function _getRequireWildcardCache() { if (typeof WeakMap !== \\"function\\") ret
102106
103107
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== \\"object\\" && typeof obj !== \\"function\\") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
104108
109+
// @react-intl project:amazing
105110
const msgs = (0, _reactIntl.defineMessages)({
106111
header: {
107112
\\"id\\": \\"foo.bar.baz\\",
@@ -192,6 +197,7 @@ Object {
192197
"id": "foo.bar.baz",
193198
},
194199
],
200+
"meta": Map {},
195201
}
196202
`;
197203

@@ -254,6 +260,7 @@ Object {
254260
"id": "foo.bar.baz",
255261
},
256262
],
263+
"meta": Map {},
257264
}
258265
`;
259266

@@ -321,6 +328,7 @@ Object {
321328
"id": "foo.bar.baz",
322329
},
323330
],
331+
"meta": Map {},
324332
}
325333
`;
326334

@@ -374,6 +382,7 @@ Object {
374382
"id": "foo",
375383
},
376384
],
385+
"meta": Map {},
377386
}
378387
`;
379388

@@ -444,6 +453,7 @@ Object {
444453
"id": "foo",
445454
},
446455
],
456+
"meta": Map {},
447457
}
448458
`;
449459

packages/babel-plugin-react-intl/test/fixtures/defineMessages/actual.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// @react-intl project:amazing
12
import React, {Component} from 'react';
23
import {defineMessages, FormattedMessage} from 'react-intl';
34

packages/babel-plugin-react-intl/test/index.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ describe('emit asserts for: ', () => {
3838
if (fs.existsSync(actualMessagesPath)) fs.unlinkSync(actualMessagesPath);
3939

4040
const {code: actual, metadata} = transform(
41-
join(fixtureDir, 'actual.js')
41+
join(fixtureDir, 'actual.js'),
42+
{pragma: '@react-intl'}
4243
)!;
4344
expect((metadata as any)['react-intl']).toMatchSnapshot();
4445
// Check code output

0 commit comments

Comments
 (0)