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

Commit b6c8352

Browse files
author
Long Ho
committed
feat(@formatjs/cli): add support for pragma
--pragma <pragma> parse specific additional custom pragma. This allows you to tag certain file with metadata such as `project`. For example with this file: ``` // @intl-meta project:my-custom-project import {FormattedMessage} from 'react-intl'; <FormattedMessage defaultMessage="foo" id="bar" />; ``` 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.
1 parent af58ad2 commit b6c8352

File tree

6 files changed

+73
-56
lines changed

6 files changed

+73
-56
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,6 @@ export default declare((api: any, options: OptionsSchema) => {
420420
const pragmaLine = comments
421421
.map(c => c.value)
422422
.find(c => c.includes(pragma));
423-
console.log(comments);
424423
if (!pragmaLine) {
425424
return;
426425
}
@@ -686,3 +685,4 @@ function getMessagesObjectFromExpression(
686685
}
687686
return currentPath;
688687
}
688+
export {OptionsSchema} from './options';

packages/cli/README.md

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,44 +5,42 @@ including React components that uses `react-intl`.
55

66
## Usage
77

8-
```shell
8+
````shell
99
$ npm -g i @formatjs/cli
1010
$ formatjs extract --help
1111
Usage: formatjs extract [options] [files...]
12+
Usage: formatjs extract [options] [files...]
13+
1214
Extract string messages from React components that use react-intl.
1315
The input language is expected to be TypeScript or ES2017 with JSX.
1416

1517
Options:
16-
--messages-dir <dir> The target location where the plugin will output a `.json` file
17-
corresponding to each component from which React Intl messages
18-
were extracted. If not provided, the extracted message
18+
--messages-dir <dir> 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
1919
descriptors will be printed to standard output.
20-
--out-file <path> The target file path where the plugin will output an aggregated \`.json\` file of allthe translations from the \`files\`
21-
supplied.
20+
--out-file <path> The target file path where the plugin will output an aggregated `.json` file of allthe translations from the `files` supplied.
2221
This flag will ignore --messages-dir
23-
--id-interpolation-pattern <pattern> If certain message descriptors don\'t have id, this \`pattern\` will be used to automaticallygenerate IDs for them. Default to
24-
\`[contenthash:5]\`.
22+
--id-interpolation-pattern <pattern> If certain message descriptors don't have id, this `pattern` will be used to automaticallygenerate IDs for them. Default to `[contenthash:5]`.
2523
See https://github.com/webpack/loader-utils#interpolatename for sample patterns
26-
--extract-source-location Whether the metadata about the location of the message in the
27-
source file should be extracted. If `true`, then `file`,
28-
`start`, and `end` fields will exist for each extracted message
29-
descriptors. (default: false)
30-
--module-source-name <name> The ES6 module source name of the React Intl package. Defaults
31-
to: `"react-intl"`, but can be changed to another name/path to
32-
React Intl.
33-
--remove-default-message Remove `defaultMessage` field in generated js after extraction
34-
(default: false)
35-
--additional-component-names <comma-separated-names> Additional component names to extract messages from, e.g:
36-
`['FormattedFooBarMessage']`. **NOTE**: By default we check for
37-
the fact that `FormattedMessage` are
38-
imported from `moduleSourceName` to make sure variable alias
39-
works. This option does not do that so it's less safe.
40-
--extract-from-format-message-call Opt-in to extract from `intl.formatMessage` call with the same
41-
restrictions, e.g: has to be called with object literal such as
42-
`intl.formatMessage({ id: 'foo', defaultMessage: 'bar',
43-
description: 'baz'})` (default: false)
44-
--ignore List of glob paths to **not** extract translations from.
45-
--output-empty-json 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.
24+
--extract-source-location Whether the metadata about the location of the message in the source file should be extracted. If `true`, then `file`, `start`, and `end` fields will exist for each extracted
25+
message descriptors. (default: false)
26+
--module-source-name <name> The ES6 module source name of the React Intl package. Defaults to: `"react-intl"`, but can be changed to another name/path to React Intl.
27+
--remove-default-message Remove `defaultMessage` field in generated js after extraction (default: false)
28+
--additional-component-names <comma-separated-names> Additional component names to extract messages from, e.g: `['FormattedFooBarMessage']`. **NOTE**: By default we check for the fact that `FormattedMessage` is imported from
29+
`moduleSourceName` to make sure variable alias works. This option does not do that so it's less safe.
30+
--extract-from-format-message-call Opt-in to extract from `intl.formatMessage` call with the same restrictions, e.g: has to be called with object literal such as `intl.formatMessage({ id: 'foo', defaultMessage:
31+
'bar', description: 'baz'})` (default: false)
32+
--output-empty-json 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. (default: false)
33+
--ignore <files> List of glob paths to **not** extract translations from.
4634
--throws Whether to throw an exception when we fail to process any file in the batch.
47-
-h, --help output usage information
48-
```
35+
--pragma <pragma> parse specific additional custom pragma. This allows you to tag certain file with metadata such as `project`. For example with this file:
36+
37+
```
38+
// @intl-meta project:my-custom-project
39+
import {FormattedMessage} from 'react-intl';
40+
41+
<FormattedMessage defaultMessage="foo" id="bar" />;
42+
```
43+
44+
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.
45+
-h, --help display help for command
46+
````

packages/cli/src/cli.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,19 @@ async function main(argv: string[]) {
111111
'--throws',
112112
'Whether to throw an exception when we fail to process any file in the batch.'
113113
)
114+
.option(
115+
'--pragma <pragma>',
116+
`parse specific additional custom pragma. This allows you to tag certain file with metadata such as \`project\`. For example with this file:
117+
118+
\`\`\`
119+
// @intl-meta project:my-custom-project
120+
import {FormattedMessage} from 'react-intl';
121+
122+
<FormattedMessage defaultMessage="foo" id="bar" />;
123+
\`\`\`
124+
125+
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.`
126+
)
114127
.action(async (files: readonly string[], cmdObj: ExtractCLIOptions) => {
115128
const processedFiles = [];
116129
for (const f of files) {
@@ -132,6 +145,7 @@ async function main(argv: string[]) {
132145
additionalComponentNames: cmdObj.additionalComponentNames,
133146
extractFromFormatMessageCall: cmdObj.extractFromFormatMessageCall,
134147
outputEmptyJson: cmdObj.outputEmptyJson,
148+
pragma: cmdObj.pragma,
135149
});
136150
process.exit(0);
137151
});

packages/cli/src/extract.ts

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
import {ExtractedMessageDescriptor} from 'babel-plugin-react-intl/dist';
2-
import {OptionsSchema} from 'babel-plugin-react-intl/dist/options';
1+
import {ExtractionResult, OptionsSchema} from 'babel-plugin-react-intl';
32
import * as babel from '@babel/core';
43
import {warn, getStdinAsString} from './console_utils';
5-
import keyBy from 'lodash/keyBy';
64
import {outputJSONSync} from 'fs-extra';
75
import {interpolateName} from 'loader-utils';
86
import {IOptions as GlobOptions} from 'glob';
@@ -61,19 +59,6 @@ function getBabelConfig(
6159
};
6260
}
6361

64-
function getReactIntlMessages(
65-
babelResult: babel.BabelFileResult | null
66-
): Record<string, ExtractedMessageDescriptor> {
67-
if (babelResult === null) {
68-
return {};
69-
} else {
70-
const messages: ExtractedMessageDescriptor[] = (babelResult.metadata as any)[
71-
'react-intl'
72-
].messages;
73-
return keyBy(messages, 'id');
74-
}
75-
}
76-
7762
export async function extract(
7863
files: readonly string[],
7964
{idInterpolationPattern, throws, ...babelOpts}: ExtractOptions
@@ -102,13 +87,14 @@ export async function extract(
10287
return throws ? promise : promise.catch(e => warn(e));
10388
})
10489
);
105-
return Object.values(
106-
results.reduce(
107-
(all, babelResult) =>
108-
babelResult ? {...all, ...getReactIntlMessages(babelResult)} : all,
109-
{} as Record<string, ExtractedMessageDescriptor>
110-
)
111-
);
90+
return results
91+
.filter(r => r && r.metadata)
92+
.map(
93+
r =>
94+
((r as babel.BabelFileResult).metadata as any)[
95+
'react-intl'
96+
] as ExtractionResult
97+
);
11298
}
11399
if (files.length === 0 && process.stdin.isTTY) {
114100
warn('Reading source file from TTY.');
@@ -133,7 +119,11 @@ export async function extract(
133119
getBabelConfig(babelOpts)
134120
);
135121

136-
return Object.values(getReactIntlMessages(babelResult));
122+
return [
123+
((babelResult as babel.BabelFileResult).metadata as any)[
124+
'react-intl'
125+
] as ExtractionResult,
126+
];
137127
}
138128

139129
export default async function extractAndWrite(
@@ -144,8 +134,12 @@ export default async function extractAndWrite(
144134
if (outFile) {
145135
extractOpts.messagesDir = undefined;
146136
}
147-
const extractedMessages = await extract(files, extractOpts);
137+
const extractionResults = await extract(files, extractOpts);
148138
const printMessagesToStdout = extractOpts.messagesDir == null && !outFile;
139+
const extractedMessages = extractionResults
140+
.map(m => m.messages)
141+
.filter(Boolean)
142+
.reduce((all, messages) => [...all, ...messages], []);
149143
if (outFile) {
150144
outputJSONSync(outFile, extractedMessages, {
151145
spaces: 2,

packages/cli/tests/extract/integration_tests/__snapshots__/index.test.ts.snap

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,16 @@ Options:
230230
--output-empty-json 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. (default: false)
231231
--ignore <files> List of glob paths to **not** extract translations from.
232232
--throws Whether to throw an exception when we fail to process any file in the batch.
233+
--pragma <pragma> parse specific additional custom pragma. This allows you to tag certain file with metadata such as \`project\`. For example with this file:
234+
235+
\`\`\`
236+
// @intl-meta project:my-custom-project
237+
import {FormattedMessage} from 'react-intl';
238+
239+
<FormattedMessage defaultMessage=\\"foo\\" id=\\"bar\\" />;
240+
\`\`\`
241+
242+
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.
233243
-h, --help display help for command
234244
"
235245
`;

packages/cli/tests/extract/integration_tests/defineMessages/actual.ignore.js

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

0 commit comments

Comments
 (0)