UNPKG

babel-plugin-react-intl-auto

Version:

i18n for the component age. Auto management react-intl ID

139 lines (110 loc) 4.71 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.visitJSXElement = void 0; var t = _interopRequireWildcard(require("@babel/types")); var _utils = require("../utils"); var _isImportLocalName = require("../utils/isImportLocalName"); var _getPrefix = require("../utils/getPrefix"); function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; } 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; } // import blog from 'babel-log' // Process react-intl components const REACT_COMPONENTS = ['FormattedMessage', 'FormattedHTMLMessage']; const getElementAttributePaths = elementPath => { if (!elementPath) { return {}; } const attributesPath = elementPath.get('attributes'); const defaultMessagePath = attributesPath.find(attrPath => attrPath.node.name && attrPath.node.name.name === 'defaultMessage'); const idPath = attributesPath.find(attrPath => attrPath.node.name && attrPath.node.name.name === 'id'); const keyPath = attributesPath.find(attrPath => attrPath.node.name && attrPath.node.name.name === 'key'); return { id: idPath, defaultMessage: defaultMessagePath, key: keyPath }; }; /** Path "JSXAttribute" name: Node "JSXIdentifier" name: "defaultMessage" value: Node "StringLiteral" extra: Object { "raw": "\"hello\"", "rawValue": "hello", } value: "hello" */ const extractFromValuePath = jsxAttributePath => { // blog(jsxAttributePath) const valuePath = jsxAttributePath.get('value'); if (!valuePath) { return null; } /** Path "StringLiteral" extra: Object { "raw": "hello", "rawValue": "hello", } value: "hello" */ if (valuePath.isStringLiteral()) { return valuePath.node.value; } /** Path "JSXExpressionContainer" expression: Node "CallExpression" arguments: Array [] callee: Node "Identifier" name: "getMsg" */ if (valuePath.isJSXExpressionContainer()) { // Evaluate the message expression to see if it yields a string const evaluated = valuePath.get('expression').evaluate(); /** Object { "confident": true, "deopt": null, "value": "variable message", } */ if (evaluated.confident && typeof evaluated.value === 'string') { return evaluated.value; } throw valuePath.buildCodeFrameError(`[React Intl Auto] ${jsxAttributePath.get('name').node.name} must be statically evaluate-able for extraction.`); } return null; }; const generateId = (defaultMessage, state, key) => { // ID = path to the file + key let suffix = key && state.opts.useKey ? extractFromValuePath(key) : ''; if (!suffix) { // ID = path to the file + hash of the defaultMessage const messageValue = extractFromValuePath(defaultMessage); if (messageValue) { suffix = (0, _utils.createHash)(messageValue); } } const prefix = (0, _getPrefix.getPrefix)(state, suffix); // Insert an id attribute before the defaultMessage attribute defaultMessage.insertBefore(t.jsxAttribute(t.jsxIdentifier('id'), t.stringLiteral(prefix))); }; const visitJSXElement = (path, state) => { const jsxOpeningElement = path.get('openingElement'); // Is this a react-intl component? Handles both: // import { FormattedMessage as T } from 'react-intl' // import { FormattedMessage } from 'react-intl' if (t.isJSXIdentifier(jsxOpeningElement.node.name) && (0, _isImportLocalName.isImportLocalName)(jsxOpeningElement.node.name.name, REACT_COMPONENTS, state)) { // Get the attributes for the component const { id, defaultMessage, key } = getElementAttributePaths(jsxOpeningElement); // If valid message but missing ID, generate one if (!id && defaultMessage) { generateId(defaultMessage, state, state.opts.useKey ? key : undefined); } } }; exports.visitJSXElement = visitJSXElement;