UNPKG

babel-plugin-transform-react-fela-display-name

Version:

This plugin transforms the display names of all react-fela components created with createComponent or createComponentWithProxy to the name of the variable to which they are assigned.

238 lines (199 loc) 9.44 kB
'use strict'; var transformReactFelaDisplayName = function transformReactFelaDisplayName(_ref) { var t = _ref.types; var defaultFunctionNameRegEx = /^createComponent(WithProxy)?$/; var defaultReactFelaPackageRegEx = /react-fela(\/.*(\.js)?)?$/; var handleInjectDisplayName = function handleInjectDisplayName(initialLineNodePath, componentName, objectName) { if (!initialLineNodePath || !componentName) return; var leftLeft = objectName ? t.memberExpression(t.identifier(objectName), t.identifier(componentName)) : t.identifier(componentName); var left = t.memberExpression(leftLeft, t.identifier('displayName')); var right = t.stringLiteral(componentName); var displayNameAssignment = t.toStatement(t.assignmentExpression('=', left, right)); initialLineNodePath.insertAfter(displayNameAssignment); }; var identifierComesFromReactFela = function identifierComesFromReactFela(identifierDeclarationPath, calleeName, functionNameRegEx, reactFelaPackageRegEx) { var bindings = identifierDeclarationPath.scope.bindings; if (!bindings[calleeName]) return false; var sourcePath = bindings[calleeName].path; if (sourcePath.isImportSpecifier() && sourcePath.parentPath.isImportDeclaration()) { // Handle cases where the function is imported destructured. For example: // // import { createComponent } from 'react-fela'; // /* or */ // import { createComponentWithProxy }j from 'react-fela'; // var sourceImportFrom = sourcePath.parent.source.value, importedName = sourcePath.node.imported.name; var isFromReactFela = reactFelaPackageRegEx.test(sourceImportFrom); var validImportedName = functionNameRegEx.test(importedName); return isFromReactFela && validImportedName; } else if (sourcePath.isVariableDeclarator()) { var init = sourcePath.node.init; // This handles the following case: // // const createComponent = require('react-fela').createComponent; // if (t.isMemberExpression(init)) { var property = init.property, callee = init.object.callee; return callee && callee.name === 'require' && property && functionNameRegEx.test(property.name); } } return false; }; return { name: 'transform-react-fela-display-name', visitor: { AssignmentExpression: function AssignmentExpression(path, _ref2) { var opts = _ref2.opts; // This adds the ability to handle components created and assigned to objects of properties. // This handles the case of a component being created as a class property. For example: // // class MyParentComponent extends React.Component { // static MyChildComponent = createComponent(() => ({}), 'div'); // ... // } // var _path$node = path.node, left = _path$node.left, right = _path$node.right; var functionNameRegEx = new RegExp(opts.functionNameRegEx) || defaultFunctionNameRegEx; var reactFelaPackageRegEx = new RegExp(opts.reactFelaPackageRegEx) || defaultReactFelaPackageRegEx; if (t.isMemberExpression(left) && t.isCallExpression(right)) { var injectAssignmentDisplayName = function injectAssignmentDisplayName() { var objectName = left.object.name, propertyName = left.property.name; return handleInjectDisplayName(path.parentPath, propertyName, objectName); }; var callee = right.callee; if (t.isMemberExpression(callee)) { // This handles the case where the assignment is to a default import of the package. // For example: // // import ReactFela from 'react-fela'; // class MyParentComponent extends React.Component { // static MyChildComponent = ReactFela.createComponent(() => ({}), 'div'); // } // var variableName = callee.object.name; var bindings = path.scope.bindings; if (variableName && variableName === opts.globalSource) { injectAssignmentDisplayName(); return; } var binding = bindings[variableName]; if (!binding || !binding.path || !binding.path.parent) return; var importDeclaration = binding.path.parent; if (t.isImportDeclaration(importDeclaration)) { injectAssignmentDisplayName(); } } else if (identifierComesFromReactFela(path, callee.name, functionNameRegEx, reactFelaPackageRegEx)) { injectAssignmentDisplayName(); } } }, VariableDeclarator: function VariableDeclarator(path, _ref3) { var opts = _ref3.opts; // Match cases such as: // // const x = y; // var _path$node2 = path.node, id = _path$node2.id, init = _path$node2.init; var functionNameRegEx = new RegExp(opts.functionNameRegEx) || defaultFunctionNameRegEx; var reactFelaPackageRegEx = new RegExp(opts.reactFelaPackageRegEx) || defaultReactFelaPackageRegEx; if (!init) return; var callee = init.callee; if (t.isCallExpression(init)) { // Match cases such as: // // const x = y(); // var componentName = id.name; var initialLineNodePath = path.parentPath; // in case there is an Named Export declaration upstream such as: // export const MyComponent = createComponent(MyComponentRules, 'div'); // we will want to insert the displayName assignment after the export decleration var exportNamedDeclaration = initialLineNodePath.findParent(function (p) { return p.isExportNamedDeclaration(); }); if (exportNamedDeclaration) { initialLineNodePath = exportNamedDeclaration; } var injectDisplayName = function injectDisplayName() { return handleInjectDisplayName(initialLineNodePath, componentName); }; if (callee.name && callee.name.match(functionNameRegEx) && identifierComesFromReactFela(path, callee.name, functionNameRegEx, reactFelaPackageRegEx)) { // Match cases such as: // // const x = createComponent(...); // /* or */ // const y = createComponentWithProxy(...); // injectDisplayName(); } else if (t.isMemberExpression(callee)) { // This handles default imports of createComponent functions. For example: // // import ReactFela from 'react-fela'; // const renameIt = createComponent; // const MyComponent = renameIt(...); // var variableName = callee.object.name; if (variableName && variableName === opts.globalSource) { // This handles the case where the recipient matches the provided global source name. // For example: // /* babel plugin options = { globalSource: 'ReactFela' } */ // const MyComponent = ReactFela.createComponent(...); // injectDisplayName(); return; } var bindings = path.scope.bindings; if (!bindings[variableName]) return; var _bindings$variableNam = bindings[variableName].path, parent = _bindings$variableNam.parent, bindingNode = _bindings$variableNam.node; if (t.isImportDeclaration(parent)) { var value = bindings[variableName].path.parent.source.value; if (reactFelaPackageRegEx.test(value)) { injectDisplayName(); } } else if (t.isVariableDeclaration(parent)) { // This handles the following case: // // const ReactFela = require('react-fela'); // const MyComponent = ReactFela.createComponent(...); // var bindingInit = bindingNode.init; if (t.isVariableDeclarator(bindingNode) && t.isCallExpression(bindingInit)) { var isRequiredFromReact = bindingInit.arguments.some(function (arg) { return reactFelaPackageRegEx.test(arg.value); }); if (isRequiredFromReact) { injectDisplayName(); } } } } else { var _bindings = path.scope.bindings; var functionBinding = _bindings[callee.name]; if (!functionBinding) return; var _bindingInit = functionBinding.path.node.init; if (t.isIdentifier(_bindingInit) && identifierComesFromReactFela(functionBinding.path, _bindingInit.name, functionNameRegEx, reactFelaPackageRegEx)) { // This handles renaming of the createComponent functions. For example: // // import { createComponent } from 'react-fela'; // const renameIt = createComponent; // const MyComponent = renameIt(...); // injectDisplayName(); } } } } } }; }; module.exports = transformReactFelaDisplayName; //# sourceMappingURL=index.js.map