Skip to content

Commit

Permalink
feat(@formatjs/cli): allow compile to take in a glob pattern also
Browse files Browse the repository at this point in the history
  • Loading branch information
longlho committed Aug 8, 2020
1 parent d935028 commit cb36802
Show file tree
Hide file tree
Showing 10 changed files with 656 additions and 451 deletions.
15 changes: 10 additions & 5 deletions packages/cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,11 @@ See https://github.com/webpack/loader-utils#interpolatename for sample patterns`

// Long text wrapping to available terminal columns: https://github.com/tj/commander.js/pull/956
commander
.command('compile <translation_file>')
.command('compile <translation_files>')
.description(
`Compile extracted translation file into react-intl consumable JSON
We also verify that the messages are valid ICU and not malformed.`
We also verify that the messages are valid ICU and not malformed.
<translation_files> can be a glob like "foo/**/en.json"`
)
.option(
'--format <path>',
Expand All @@ -173,9 +174,13 @@ If this is not provided, result will be printed to stdout`
`Whether to compile to AST. See https://formatjs.io/docs/guides/advanced-usage#pre-parsing-messages
for more information`
)
.action((file: string, {outFile, ...opts}: CompileCLIOpts) =>
compile(file, outFile, opts)
);
.action(async (filePattern: string, {outFile, ...opts}: CompileCLIOpts) => {
const files = globSync(filePattern);
if (!files.length) {
throw new Error(`No input file found with pattern ${filePattern}`);
}
await compile(files, outFile, opts);
});

if (argv.length < 3) {
commander.help();
Expand Down
27 changes: 22 additions & 5 deletions packages/cli/src/compile.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {parse, MessageFormatElement} from 'intl-messageformat-parser';
import {outputFileSync, readJSONSync} from 'fs-extra';
import {outputFileSync, readJSON} from 'fs-extra';
import * as stringify from 'json-stable-stringify';
import {resolveBuiltinFormatter} from './formatters';

Expand All @@ -12,16 +12,33 @@ export interface Opts {
ast?: boolean;
format?: string;
}
export default function compile(
inputFile: string,
export default async function compile(
inputFiles: string[],
outFile?: string,
{ast, format}: Opts = {}
) {
const formatter = resolveBuiltinFormatter(format);

const messages: Record<string, string> = formatter.compile(
readJSONSync(inputFile)
const messages: Record<string, string> = {};
const idsWithFileName: Record<string, string> = {};
const compiledFiles = await Promise.all(
inputFiles.map(f => readJSON(f).then(formatter.compile))
);
for (let i = 0; i < inputFiles.length; i++) {
const inputFile = inputFiles[i];
const compiled = compiledFiles[i];
for (const id in compiled) {
if (messages[id] && messages[id] !== compiled[id]) {
throw new Error(`Conflicting ID "${id}" with different translation found in these 2 files:
ID: ${id}
Message from ${idsWithFileName[id]}: ${messages[id]}
Message from ${compiled[id]}: ${inputFile}
`);
}
messages[id] = compiled[id];
idsWithFileName[id] = inputFile;
}
}
const results: Record<string, string | MessageFormatElement[]> = {};

for (const [id, message] of Object.entries(messages)) {
Expand Down
202 changes: 137 additions & 65 deletions packages/cli/tests/compile/__snapshots__/integration.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,62 +2,76 @@

exports[`AST 1`] = `
Object {
"a1d12": Array [
Object {
"type": 0,
"value": "I have ",
"stderr": "",
"stdout": "{
\\"a1d12\\": [
{
\\"type\\": 0,
\\"value\\": \\"I have \\"
},
Object {
"offset": 0,
"options": Object {
"one": Object {
"value": Array [
Object {
"type": 0,
"value": "a dog",
},
],
},
"other": Object {
"value": Array [
Object {
"type": 0,
"value": "many dogs",
},
],
{
\\"offset\\": 0,
\\"options\\": {
\\"one\\": {
\\"value\\": [
{
\\"type\\": 0,
\\"value\\": \\"a dog\\"
}
]
},
\\"other\\": {
\\"value\\": [
{
\\"type\\": 0,
\\"value\\": \\"many dogs\\"
}
]
}
},
"pluralType": "cardinal",
"type": 6,
"value": "count",
},
],
"a1dd2": Array [
Object {
"type": 0,
"value": "my name is ",
},
Object {
"type": 1,
"value": "name",
},
\\"pluralType\\": \\"cardinal\\",
\\"type\\": 6,
\\"value\\": \\"count\\"
}
],
"ashd2": Array [
Object {
"type": 0,
"value": "a message",
\\"a1dd2\\": [
{
\\"type\\": 0,
\\"value\\": \\"my name is \\"
},
{
\\"type\\": 1,
\\"value\\": \\"name\\"
}
],
\\"ashd2\\": [
{
\\"type\\": 0,
\\"value\\": \\"a message\\"
}
]
}
",
}
`;

exports[`basic case: empty json 1`] = `Object {}`;
exports[`basic case: empty json 1`] = `
Object {
"stderr": "",
"stdout": "{
}
",
}
`;

exports[`basic case: help 1`] = `
"Usage: formatjs compile [options] <translation_file>
Object {
"stderr": "",
"stdout": "Usage: formatjs compile [options] <translation_files>
Compile extracted translation file into react-intl consumable JSON
We also verify that the messages are valid ICU and not malformed.
We also verify that the messages are valid ICU and not malformed.
<translation_files> can be a glob like \\"foo/**/en.json\\"
Options:
--format <path> Path to a formatter file that converts \`<translation_file>\` to \`Record<string, string>\`
Expand All @@ -75,66 +89,117 @@ Options:
https://formatjs.io/docs/guides/advanced-usage#pre-parsing-messages
for more information
-h, --help display help for command
"
",
}
`;
exports[`compile glob 1`] = `
Object {
"stderr": "",
"stdout": "{
\\"1\\": \\"a message\\",
\\"2\\": \\"my name is {name}\\",
\\"3\\": \\"I have {count, plural, one{a dog} other{many dogs}}\\",
\\"a1d12\\": \\"I have {count, plural, one{a dog} other{many dogs}}\\",
\\"a1dd2\\": \\"my name is {name}\\",
\\"ashd2\\": \\"a message\\"
}
",
}
`;
exports[`normal json 1`] = `
Object {
"a1d12": "I have {count, plural, one{a dog} other{many dogs}}",
"a1dd2": "my name is {name}",
"ashd2": "a message",
"stderr": "",
"stdout": "{
\\"a1d12\\": \\"I have {count, plural, one{a dog} other{many dogs}}\\",
\\"a1dd2\\": \\"my name is {name}\\",
\\"ashd2\\": \\"a message\\"
}
",
}
`;
exports[`normal json with crowdin 1`] = `
Object {
"a1d12": "I have {count, plural, one{a dog} other{many dogs}}",
"a1dd2": "my name is {name}",
"ashd2": "a message",
"stderr": "",
"stdout": "{
\\"a1d12\\": \\"I have {count, plural, one{a dog} other{many dogs}}\\",
\\"a1dd2\\": \\"my name is {name}\\",
\\"ashd2\\": \\"a message\\"
}
",
}
`;
exports[`normal json with formatter 1`] = `
Object {
"a1d12": "I have {count, plural, one{a dog} other{many dogs}}",
"a1dd2": "my name is {name}",
"ashd2": "a message",
"stderr": "",
"stdout": "{
\\"a1d12\\": \\"I have {count, plural, one{a dog} other{many dogs}}\\",
\\"a1dd2\\": \\"my name is {name}\\",
\\"ashd2\\": \\"a message\\"
}
",
}
`;
exports[`normal json with lokalise 1`] = `
Object {
"a1d12": "I have {count, plural, one{a dog} other{many dogs}}",
"a1dd2": "my name is {name}",
"ashd2": "a message",
"stderr": "",
"stdout": "{
\\"a1d12\\": \\"I have {count, plural, one{a dog} other{many dogs}}\\",
\\"a1dd2\\": \\"my name is {name}\\",
\\"ashd2\\": \\"a message\\"
}
",
}
`;
exports[`normal json with simple 1`] = `
Object {
"a1d12": "I have {count, plural, one{a dog} other{many dogs}}",
"a1dd2": "my name is {name}",
"ashd2": "a message",
"stderr": "",
"stdout": "{
\\"a1d12\\": \\"I have {count, plural, one{a dog} other{many dogs}}\\",
\\"a1dd2\\": \\"my name is {name}\\",
\\"ashd2\\": \\"a message\\"
}
",
}
`;
exports[`normal json with smartling 1`] = `
Object {
"a1d12": "I have {count, plural, one{a dog} other{many dogs}}",
"a1dd2": "my name is {name}",
"ashd2": "a message",
"stderr": "",
"stdout": "{
\\"a1d12\\": \\"I have {count, plural, one{a dog} other{many dogs}}\\",
\\"a1dd2\\": \\"my name is {name}\\",
\\"ashd2\\": \\"a message\\"
}
",
}
`;
exports[`normal json with transifex 1`] = `
Object {
"a1d12": "I have {count, plural, one{a dog} other{many dogs}}",
"a1dd2": "my name is {name}",
"ashd2": "a message",
"stderr": "",
"stdout": "{
\\"a1d12\\": \\"I have {count, plural, one{a dog} other{many dogs}}\\",
\\"a1dd2\\": \\"my name is {name}\\",
\\"ashd2\\": \\"a message\\"
}
",
}
`;
exports[`out-file --ast 1`] = `
Object {
"stderr": "",
"stdout": "",
}
`;
exports[`out-file --ast 2`] = `
Object {
"a1d12": Array [
Object {
Expand Down Expand Up @@ -186,6 +251,13 @@ Object {
`;
exports[`out-file 1`] = `
Object {
"stderr": "",
"stdout": "",
}
`;
exports[`out-file 2`] = `
Object {
"a1d12": "I have {count, plural, one{a dog} other{many dogs}}",
"a1dd2": "my name is {name}",
Expand Down
14 changes: 14 additions & 0 deletions packages/cli/tests/compile/glob-conflict/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"ashd2": {
"defaultMessage": "a message",
"description": "foo"
},
"a1dd2": {
"defaultMessage": "my name is {name}",
"description": "foo"
},
"a1d12": {
"defaultMessage": "I have {count, plural, one{a dog} other{many dogs}}",
"description": "foo"
}
}
14 changes: 14 additions & 0 deletions packages/cli/tests/compile/glob-conflict/en2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"1": {
"defaultMessage": "a message",
"description": "foo"
},
"2": {
"defaultMessage": "my name is {name}",
"description": "foo"
},
"a1d12": {
"defaultMessage": "I have {count, plural, one{a dog} other{many cats}}",
"description": "foo"
}
}
14 changes: 14 additions & 0 deletions packages/cli/tests/compile/glob/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"ashd2": {
"defaultMessage": "a message",
"description": "foo"
},
"a1dd2": {
"defaultMessage": "my name is {name}",
"description": "foo"
},
"a1d12": {
"defaultMessage": "I have {count, plural, one{a dog} other{many dogs}}",
"description": "foo"
}
}
Loading

0 comments on commit cb36802

Please sign in to comment.