UNPKG

@dvhb/react-intl-messages

Version:

Library for parsing source files and extract react-intl messages

126 lines (125 loc) 5.26 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const command_1 = require("@oclif/command"); const core_1 = require("@babel/core"); const path = require("path"); const base_1 = require("../base"); const utils_1 = require("../utils"); class Extract extends base_1.Base { constructor() { super(...arguments); this.messages = {}; this.fileToMessages = {}; this.processFile = async (filename) => { const compare = (a, b) => (a === b ? 0 : a < b ? -1 : 1); try { const code = await utils_1.readFile(filename); const posixName = utils_1.posixPath(filename); const result = core_1.transform(code, { filename, plugins: ['react-intl'] }); if (result && result.metadata) { // @ts-ignore const metadata = result.metadata['react-intl']; if (metadata.messages && metadata.messages.length) { const messages = metadata.messages; this.fileToMessages[posixName] = messages.sort((a, b) => compare(a.id, b.id)); } else { delete this.fileToMessages[posixName]; } } } catch (err) { utils_1.showError(`extractMessages: In ${filename}:\n${err.codeFrame || err}`); } }; } static async writeMessages(fileName, msgs) { return utils_1.writeFile(fileName, `${JSON.stringify(msgs, null, 2)}\n`); } /** * Merge messages to source files * @param locale */ async mergeToFile(locale) { const { flags: { messagesDir }, } = this.parse(Extract); const fileName = path.join(messagesDir, `${locale}.json`); const originalMessages = {}; try { const oldFile = await utils_1.readFile(fileName); let oldJson; try { oldJson = JSON.parse(oldFile); } catch (err) { throw new Error(`Error parsing messages JSON in file ${fileName}`); } oldJson.forEach((message) => { originalMessages[message.id] = message; delete originalMessages[message.id].files; }); } catch (err) { if (err.code !== 'ENOENT') { throw err; } } Object.keys(this.messages).forEach(id => { const newMsg = this.messages[id]; originalMessages[id] = originalMessages[id] || { id }; const msg = originalMessages[id]; msg.description = newMsg.description || msg.description; msg.defaultMessage = newMsg.defaultMessage || msg.defaultMessage; msg.message = msg.message || ''; msg.files = newMsg.files; }); const result = Object.keys(originalMessages) .map(key => originalMessages[key]) .filter(msg => msg.files || msg.message); await Extract.writeMessages(fileName, result); utils_1.showInfo(`Messages updated: ${fileName}`); } mergeMessages() { this.messages = {}; const messages = this.messages; Object.keys(this.fileToMessages).forEach(fileName => { this.fileToMessages[fileName].forEach(newMsg => { const message = messages[newMsg.id] || {}; messages[newMsg.id] = { description: newMsg.description || message.description, defaultMessage: newMsg.defaultMessage || message.defaultMessage, message: newMsg.message || message.message || '', files: message.files ? [...message.files, fileName].sort() : [fileName], }; }); }); } /** * Extract react-intl messages and write it to <dest>/_default.json * Also extends known localizations */ async run() { const { flags: { langs, pattern, ignore }, } = this.parse(Extract); const locales = langs.split(','); const ignorePattern = ignore && ignore.includes(',') ? ignore.split(',') : ignore; const files = await utils_1.glob(pattern, ignorePattern); await Promise.all(files.map(this.processFile)); this.mergeMessages(); await Promise.all(['_default', ...locales].map(locale => this.mergeToFile(locale))); } } exports.default = Extract; Extract.description = 'Extract translations from source files to json'; Extract.examples = [ `$ messages extract --langs=en,fr,de,ru --pattern="src/**/*.{ts,tsx}" `, ]; Extract.flags = Object.assign(Object.assign(Object.assign({}, base_1.Base.flags), base_1.Base.langFlags), { pattern: command_1.flags.string({ char: 'p', description: 'Regex mask for files', default: () => (base_1.Base.cosmiconfig ? base_1.Base.cosmiconfig.pattern : undefined), required: true, }), ignore: command_1.flags.string({ char: 'i', description: 'Regex mask for ignored files', default: () => (base_1.Base.cosmiconfig ? base_1.Base.cosmiconfig.ignore : undefined), }) });