babel-plugin-react-intl-auto
Version:
i18n for the component age. Auto management react-intl ID
139 lines (110 loc) • 4.71 kB
JavaScript
;
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;