UNPKG

ts-transform-react-intl

Version:

Extracts string messages for translation from modules that use React Intl.

125 lines (124 loc) 4.49 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const ts = require("typescript"); const macro_1 = require("./macro"); const loader_utils_1 = require("loader-utils"); const DEFAULT_OPTS = { macroImportName: macro_1._.name, macroModuleSpecifier: require("../package.json").name, baseUrl: "", onMsgExtracted: () => undefined }; /** * Trim the trailing & beginning ': 'asd' -> asd * * @param {string} txt text * @returns trimmed string */ function trimSingleQuote(txt) { return txt.replace(/["']/g, ""); } /** * Extract the object literal in TS AST into MessageDescriptor * * @param {ts.ObjectLiteralExpression} node object literal * @returns {Messages} */ function extractMessageDescriptor(node, sf, interpolateNameFnOrPattern, baseUrl = "") { const msg = { id: "", description: "", defaultMessage: "" }; // Go thru each property ts.forEachChild(node, (p) => { switch (p.name.getText(sf)) { case "id": const id = trimSingleQuote(p.initializer.getText(sf)); msg.id = id; break; case "description": msg.description = trimSingleQuote(p.initializer.getText(sf)); break; case "defaultMessage": msg.defaultMessage = trimSingleQuote(p.initializer.getText(sf)); break; } }); switch (typeof interpolateNameFnOrPattern) { case "string": msg.id = loader_utils_1.interpolateName({ sourcePath: sf.fileName.replace(baseUrl, "") }, interpolateNameFnOrPattern, { content: JSON.stringify(msg) }); break; case "function": msg.id = interpolateNameFnOrPattern(sf.fileName, msg); break; } return msg; } function findMacroHook(node, sf, macroImportName, macroModuleSpecifier) { let hook = ""; if (ts.isImportDeclaration(node) && trimSingleQuote(node.moduleSpecifier.getText(sf)) === macroModuleSpecifier) { const { namedBindings } = node.importClause; // Search through named bindings to find our macro ts.forEachChild(namedBindings, p => { if (!ts.isImportSpecifier(p)) { return; } // This is a alias, like `import {_ as foo}` if (p.propertyName) { if (p.propertyName.getText(sf) === macroImportName) { hook = p.name.getText(sf); } } else if (p.name.getText(sf) === macroImportName) { hook = p.name.getText(sf); } }); } return hook; } function isMacroExpression(node, sf, hook) { // Make sure it's a function call return (hook && ts.isCallExpression(node) && // Make sure the fn name matches our hook node.expression.getText(sf) === hook && // Make sure we got only 1 arg node.arguments.length === 1 && node.arguments[0] && // Make sure it's a object literal ts.isObjectLiteralExpression(node.arguments[0])); } function transform(opts) { opts = Object.assign({}, DEFAULT_OPTS, opts); return (ctx) => { function getVisitor(sf) { let hook = ""; const visitor = (node) => { if (!hook) { hook = findMacroHook(node, sf, opts.macroImportName, opts.macroModuleSpecifier); if (hook) { return null; } } // If it's not our macro, skip if (!isMacroExpression(node, sf, hook)) { return ts.visitEachChild(node, visitor, ctx); } const msgObjLiteral = node.arguments[0]; const msg = extractMessageDescriptor(msgObjLiteral, sf, opts.interpolateName, opts.baseUrl); if (typeof opts.onMsgExtracted === "function") { opts.onMsgExtracted(msg.id, msg, sf.fileName); } return ts.createObjectLiteral([ ts.createPropertyAssignment("id", ts.createStringLiteral(msg.id)), ts.createPropertyAssignment("defaultMessage", ts.createStringLiteral(msg.defaultMessage)) ]); }; return visitor; } return (sf) => ts.visitNode(sf, getVisitor(sf)); }; } exports.transform = transform;