Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

babel-plugin-react-transform

Package Overview
Dependencies
Maintainers
2
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

babel-plugin-react-transform - npm Package Compare versions

Comparing version 1.1.1 to 2.0.0-beta1

.babelrc

545

lib/index.js
'use strict';
Object.defineProperty(exports, '__esModule', {
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
Object.defineProperty(exports, "__esModule", {
value: true
});
var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })();
exports.default = function (_ref) {
var t = _ref.types;
var template = _ref.template;
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
function matchesPatterns(path, patterns) {
return !!(0, _arrayFind2.default)(patterns, function (pattern) {
return t.isIdentifier(path.node, { name: pattern }) || path.matchesPattern(pattern);
});
}
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } }
function isReactLikeClass(node) {
return !!(0, _arrayFind2.default)(node.body.body, function (classMember) {
return t.isClassMethod(classMember) && t.isIdentifier(classMember.key, { name: 'render' });
});
}
var _path = require('path');
function isReactLikeComponentObject(node) {
return t.isObjectExpression(node) && !!(0, _arrayFind2.default)(node.properties, function (objectMember) {
return (t.isObjectProperty(objectMember) || t.isObjectMethod(objectMember)) && (t.isIdentifier(objectMember.key, { name: 'render' }) || t.isStringLiteral(objectMember.key, { value: 'render' }));
});
}
var _path2 = _interopRequireDefault(_path);
// `foo({ displayName: 'NAME' });` => 'NAME'
function getDisplayName(node) {
var property = (0, _arrayFind2.default)(node.arguments[0].properties, function (node) {
return node.key.name === 'displayName';
});
return property && property.value.value;
}
var _pathParse = require('path-parse');
function hasParentFunction(path) {
return !!path.findParent(function (parentPath) {
return parentPath.isFunction();
});
}
var _pathParse2 = _interopRequireDefault(_pathParse);
// wrapperFunction("componentId")(node)
function wrapComponent(node, componentId, wrapperFunctionId) {
return t.callExpression(t.callExpression(wrapperFunctionId, [t.stringLiteral(componentId)]), [node]);
}
exports['default'] = function (_ref) {
var Plugin = _ref.Plugin;
var t = _ref.types;
// `{ name: foo }` => Node { type: "ObjectExpression", properties: [...] }
function toObjectExpression(object) {
var properties = Object.keys(object).map(function (key) {
return t.objectProperty(t.identifier(key), object[key]);
});
var parentDir = _path2['default'].resolve(_path2['default'].join(__dirname, '..', '..'));
function resolvePathConservatively(specifiedPath, filePath) {
if (specifiedPath[0] === '.') {
throw new Error('Relative path like ' + specifiedPath + ' is only allowed if ' + 'babel-plugin-react-transform is inside a node_modules folder.');
}
return specifiedPath;
return t.objectExpression(properties);
}
function resolvePathAssumingWeAreInNodeModules(specifiedPath, filePath) {
if (specifiedPath[0] === '.') {
return '.' + _path2['default'].sep + _path2['default'].relative(_path2['default'].dirname(filePath), _path2['default'].resolve(_path2['default'].join(parentDir, '..', specifiedPath)));
}
return specifiedPath;
}
var resolvePath = _path2['default'].basename(parentDir) === 'node_modules' ? resolvePathAssumingWeAreInNodeModules : resolvePathConservatively;
var depthKey = '__reactTransformDepth';
var recordsKey = '__reactTransformRecords';
var wrapComponentIdKey = '__reactTransformWrapComponentId';
var optionsKey = '__reactTransformOptions';
var cacheKey = '__reactTransformCache';
var wrapperFunctionTemplate = template('\n function WRAPPER_FUNCTION_ID(ID_PARAM) {\n return function(COMPONENT_PARAM) {\n return EXPRESSION;\n };\n }\n ');
function isRenderMethod(member) {
return member.kind === 'method' && member.key.name === 'render';
}
var VISITED_KEY = 'react-transform-' + Date.now();
/**
* Does this class have a render function?
*/
function isComponentishClass(cls) {
return cls.body.body.filter(isRenderMethod).length > 0;
}
var componentVisitor = {
Class: function Class(path) {
if (path.node[VISITED_KEY] || !matchesPatterns(path.get('superClass'), this.superClasses) && !isReactLikeClass(path.node)) {
return;
}
function buildIsCreateClassCallExpression(factoryMethods) {
var matchMemberExpressions = {};
path.node[VISITED_KEY] = true;
factoryMethods.forEach(function (method) {
matchMemberExpressions[method] = t.buildMatchMemberExpression(method);
});
var componentName = path.node.id && path.node.id.name || null;
var componentId = componentName || path.scope.generateUid('component');
var isInFunction = hasParentFunction(path);
return function (node) {
for (var i = 0; i < factoryMethods.length; i++) {
var method = factoryMethods[i];
if (method.indexOf('.') !== -1) {
if (matchMemberExpressions[method](node.callee)) {
return true;
}
} else {
if (node.callee.name === method) {
return true;
}
}
}
};
}
this.components.push({
id: componentId,
name: componentName,
isInFunction: isInFunction
});
/**
* Does this node look like a createClass() call?
*/
function isCreateClass(node, isCreateClassCallExpression) {
if (!node || !t.isCallExpression(node)) {
return false;
}
if (!isCreateClassCallExpression(node)) {
return false;
}
var args = node.arguments;
if (args.length !== 1) {
return false;
}
var first = args[0];
if (!t.isObjectExpression(first)) {
return false;
}
return true;
}
// Can't wrap ClassDeclarations
var isStatement = t.isStatement(path.node);
var expression = t.toExpression(path.node);
/**
* Infers a displayName from either a class node, or a createClass() call node.
*/
function findDisplayName(node) {
if (node.id) {
return node.id.name;
}
if (!node.arguments) {
return;
}
var props = node.arguments[0].properties;
for (var i = 0; i < props.length; i++) {
var prop = props[i];
var key = t.toComputedKey(prop);
if (t.isLiteral(key, { value: 'displayName' })) {
return prop.value.value;
// wrapperFunction("componentId")(node)
var wrapped = wrapComponent(expression, componentId, this.wrapperFunctionId);
var constId = undefined;
if (isStatement) {
// wrapperFunction("componentId")(class Foo ...) => const Foo = wrapperFunction("componentId")(class Foo ...)
constId = t.identifier(componentName || componentId);
wrapped = t.variableDeclaration('const', [t.variableDeclarator(constId, wrapped)]);
}
}
}
function isValidOptions(options) {
return typeof options === 'object' && Array.isArray(options.transforms);
}
if (t.isExportDefaultDeclaration(path.parent)) {
path.parentPath.insertBefore(wrapped);
path.parent.declaration = constId;
} else {
path.replaceWith(wrapped);
}
},
CallExpression: function CallExpression(path) {
if (path.node[VISITED_KEY] || !matchesPatterns(path.get('callee'), this.factoryMethods) && !isReactLikeComponentObject(path.node.arguments[0])) {
return;
}
var didWarnAboutLegacyConfig = false;
function warnOnceAboutLegacyConfig() {
if (didWarnAboutLegacyConfig) {
return;
}
console.warn('Warning: you are using an outdated format of React Transform configuration. ' + 'Please update your configuration to the new format. See the Releases page for migration instructions: ' + 'https://github.com/gaearon/babel-plugin-react-transform/releases');
didWarnAboutLegacyConfig = true;
}
path.node[VISITED_KEY] = true;
/**
* Enforces plugin options to be defined and returns them.
*/
function getPluginOptions(file) {
if (!file.opts || !file.opts.extra) {
return;
}
// `foo({ displayName: 'NAME' });` => 'NAME'
var componentName = getDisplayName(path.node);
var componentId = componentName || path.scope.generateUid('component');
var isInFunction = hasParentFunction(path);
var pluginOptions = file.opts.extra['react-transform'];
if (Array.isArray(pluginOptions)) {
warnOnceAboutLegacyConfig();
var transforms = pluginOptions.map(function (option) {
option.transform = option.transform || option.target;
return option;
this.components.push({
id: componentId,
name: componentName,
isInFunction: isInFunction
});
pluginOptions = { transforms: transforms };
}
if (!isValidOptions(pluginOptions)) {
throw new Error('babel-plugin-react-transform requires that you specify ' + 'extras["react-transform"] in .babelrc ' + 'or in your Babel Node API call options, and that it is an object with ' + 'a transforms property which is an array.');
path.replaceWith(wrapComponent(path.node, componentId, this.wrapperFunctionId));
}
return pluginOptions;
}
};
/**
* Creates a record about us having visited a valid React component.
* Such records will later be merged into a single object.
*/
function createComponentRecord(node, scope, file, state) {
var displayName = findDisplayName(node) || undefined;
var uniqueId = scope.generateUidIdentifier('$' + (displayName || 'Unknown')).name;
var ReactTransformBuilder = (function () {
function ReactTransformBuilder(file, options) {
_classCallCheck(this, ReactTransformBuilder);
var props = [];
if (typeof displayName === 'string') {
props.push(t.property('init', t.identifier('displayName'), t.literal(displayName)));
this.file = file;
this.program = file.path;
this.options = this.normalizeOptions(options);
// @todo: clean this shit up
this.configuredTransformsIds = [];
}
if (state[depthKey] > 0) {
props.push(t.property('init', t.identifier('isInFunction'), t.literal(true)));
}
return [uniqueId, t.objectExpression(props)];
}
_createClass(ReactTransformBuilder, [{
key: 'normalizeOptions',
value: function normalizeOptions(options) {
return {
factoryMethods: options.factoryMethods || ['React.createClass'],
superClasses: options.superClasses || ['React.Component'],
transforms: options.transforms.map(function (opts) {
return {
transform: opts.transform,
locals: opts.locals || [],
imports: opts.imports || []
};
})
};
}
}, {
key: 'build',
value: function build() {
var componentsDeclarationId = this.file.scope.generateUidIdentifier('components');
var wrapperFunctionId = this.file.scope.generateUidIdentifier('wrapComponent');
/**
* Memorizes the fact that we have visited a valid component in the plugin state.
* We will later retrieve memorized records to compose an object out of them.
*/
function addComponentRecord(node, scope, file, state) {
var _createComponentRecord = createComponentRecord(node, scope, file, state);
var components = this.collectAndWrapComponents(wrapperFunctionId);
var _createComponentRecord2 = _slicedToArray(_createComponentRecord, 2);
if (!components.length) {
return;
}
var uniqueId = _createComponentRecord2[0];
var definition = _createComponentRecord2[1];
var componentsDeclaration = this.initComponentsDeclaration(componentsDeclarationId, components);
var configuredTransforms = this.initTransformers(componentsDeclarationId);
var wrapperFunction = this.initWrapperFunction(wrapperFunctionId);
state[recordsKey] = state[recordsKey] || [];
state[recordsKey].push(t.property('init', t.identifier(uniqueId), definition));
return uniqueId;
}
var body = this.program.node.body;
/**
* Have we visited any components so far?
*/
function foundComponentRecords(state) {
var records = state[recordsKey];
return records && records.length > 0;
}
body.unshift(wrapperFunction);
configuredTransforms.reverse().forEach(function (node) {
return body.unshift(node);
});
body.unshift(componentsDeclaration);
}
/**
* Turns all component records recorded so far, into a variable.
*/
function defineComponentRecords(scope, state) {
var records = state[recordsKey];
state[recordsKey] = [];
/**
* const Foo = _wrapComponent('Foo')(class Foo extends React.Component {});
* ...
* const Bar = _wrapComponent('Bar')(React.createClass({
* displayName: 'Bar'
* }));
*/
var id = scope.generateUidIdentifier('components');
return [id, t.variableDeclaration('var', [t.variableDeclarator(id, t.objectExpression(records))])];
}
}, {
key: 'collectAndWrapComponents',
value: function collectAndWrapComponents(wrapperFunctionId) {
var components = [];
/**
* Imports and calls a particular transformation target function.
* You may specify several such transformations, so they are handled separately.
*/
function defineInitTransformCall(scope, file, recordsId, targetOptions) {
var id = scope.generateUidIdentifier('reactComponentWrapper');
var transform = targetOptions.transform;
var _targetOptions$imports = targetOptions.imports;
var imports = _targetOptions$imports === undefined ? [] : _targetOptions$imports;
var _targetOptions$locals = targetOptions.locals;
var locals = _targetOptions$locals === undefined ? [] : _targetOptions$locals;
var filename = file.opts.filename;
this.file.path.traverse(componentVisitor, {
wrapperFunctionId: wrapperFunctionId,
components: components,
factoryMethods: this.options.factoryMethods,
superClasses: this.options.superClasses,
currentlyInFunction: false
});
function isSameAsFileBeingProcessed(importPath) {
var _parsePath = (0, _pathParse2['default'])(resolvePath(importPath, filename));
return components;
}
var dir = _parsePath.dir;
var base = _parsePath.base;
var ext = _parsePath.ext;
var name = _parsePath.name;
/**
* const _components = {
* Foo: {
* displayName: "Foo"
* }
* };
*/
return dir === '.' && name === (0, _pathParse2['default'])(filename).name;
}
}, {
key: 'initComponentsDeclaration',
value: function initComponentsDeclaration(componentsDeclarationId, components) {
var uniqueId = 0;
if (imports.some(isSameAsFileBeingProcessed)) {
return;
}
var props = components.map(function (component) {
var componentId = component.id;
var componentProps = [];
return [id, t.variableDeclaration('var', [t.variableDeclarator(id, t.callExpression(file.addImport(resolvePath(transform, filename)), [t.objectExpression([t.property('init', t.identifier('filename'), t.literal(filename)), t.property('init', t.identifier('components'), recordsId), t.property('init', t.identifier('locals'), t.arrayExpression(locals.map(function (local) {
return t.identifier(local);
}))), t.property('init', t.identifier('imports'), t.arrayExpression(imports.map(function (imp) {
return file.addImport(resolvePath(imp, filename), imp, 'absolute');
})))])]))])];
}
if (component.name) {
componentProps.push(t.objectProperty(t.identifier('displayName'), t.stringLiteral(component.name)));
}
/**
* Defines the function that calls every transform.
* This is the function every component will be wrapped with.
*/
function defineWrapComponent(wrapComponentId, initTransformIds) {
return t.functionDeclaration(wrapComponentId, [t.identifier('uniqueId')], t.blockStatement([t.returnStatement(t.functionExpression(null, [t.identifier('ReactClass')], t.blockStatement([t.returnStatement(initTransformIds.reduce(function (composed, initTransformId) {
return t.callExpression(initTransformId, [composed, t.identifier('uniqueId')]);
}, t.identifier('ReactClass')))])))]));
}
return new Plugin('babel-plugin-react-transform', {
visitor: {
Function: {
enter: function enter(node, parent, scope, file) {
if (!this.state[depthKey]) {
this.state[depthKey] = 0;
if (component.isInFunction) {
componentProps.push(t.objectProperty(t.identifier('isInFunction'), t.booleanLiteral(true)));
}
this.state[depthKey]++;
},
exit: function exit(node, parent, scope, file) {
this.state[depthKey]--;
}
},
Class: function Class(node, parent, scope, file) {
if (!isComponentishClass(node)) {
return;
}
return t.objectProperty(t.identifier(componentId), t.objectExpression(componentProps));
});
var wrapReactComponentId = this.state[wrapComponentIdKey];
var uniqueId = addComponentRecord(node, scope, file, this.state);
return t.variableDeclaration('const', [t.variableDeclarator(componentsDeclarationId, t.objectExpression(props))]);
}
node.decorators = node.decorators || [];
node.decorators.push(t.decorator(t.callExpression(wrapReactComponentId, [t.literal(uniqueId)])));
},
/**
* import _transformLib from "transform-lib";
* ...
* const _transformLib2 = _transformLib({
* filename: "filename",
* components: _components,
* locals: [],
* imports: []
* });
*/
CallExpression: {
exit: function exit(node, parent, scope, file) {
var isCreateClassCallExpression = this.state[cacheKey].isCreateClassCallExpression;
}, {
key: 'initTransformers',
value: function initTransformers(componentsDeclarationId) {
var _this = this;
if (!isCreateClass(node, isCreateClassCallExpression)) {
return;
}
return this.options.transforms.map(function (transform) {
var transformName = transform.transform;
var transformImportId = _this.file.addImport(transformName, 'default', transformName);
var wrapReactComponentId = this.state[wrapComponentIdKey];
var uniqueId = addComponentRecord(node, scope, file, this.state);
var transformLocals = transform.locals.map(function (local) {
return t.identifier(local);
});
return t.callExpression(t.callExpression(wrapReactComponentId, [t.literal(uniqueId)]), [node]);
}
},
var transformImports = transform.imports.map(function (importName) {
return _this.file.addImport(importName, 'default', importName);
});
Program: {
enter: function enter(node, parent, scope, file) {
var options = getPluginOptions(file);
var factoryMethods = options.factoryMethods || ['React.createClass', 'createClass'];
this.state[optionsKey] = options;
this.state[cacheKey] = {
isCreateClassCallExpression: buildIsCreateClassCallExpression(factoryMethods)
};
var configuredTransformId = _this.file.scope.generateUidIdentifier(transformName);
var configuredTransform = t.variableDeclaration('const', [t.variableDeclarator(configuredTransformId, t.callExpression(transformImportId, [toObjectExpression({
filename: t.stringLiteral(_this.file.opts.filename),
components: componentsDeclarationId,
locals: t.arrayExpression(transformLocals),
imports: t.arrayExpression(transformImports)
})]))]);
this.state[wrapComponentIdKey] = scope.generateUidIdentifier('wrapComponent');
},
_this.configuredTransformsIds.push(configuredTransformId);
exit: function exit(node, parent, scope, file) {
if (!foundComponentRecords(this.state)) {
return;
}
return configuredTransform;
});
}
// Generate a variable holding component records
var allTransforms = this.state[optionsKey].transforms;
/**
* function _wrapComponent(id) {
* return function (Component) {
* return _transformLib2(Component, id);
* };
* }
*/
var _defineComponentRecords = defineComponentRecords(scope, this.state);
}, {
key: 'initWrapperFunction',
value: function initWrapperFunction(wrapperFunctionId) {
var idParam = t.identifier('id');
var componentParam = t.identifier('Component');
var _defineComponentRecords2 = _slicedToArray(_defineComponentRecords, 2);
var expression = this.configuredTransformsIds.reverse().reduce(function (memo, transformId) {
return t.callExpression(transformId, [memo, idParam]);
}, componentParam);
var recordsId = _defineComponentRecords2[0];
var recordsVar = _defineComponentRecords2[1];
return wrapperFunctionTemplate({
WRAPPER_FUNCTION_ID: wrapperFunctionId,
ID_PARAM: idParam,
COMPONENT_PARAM: componentParam,
EXPRESSION: expression
});
}
}], [{
key: 'validateOptions',
value: function validateOptions(options) {
return (typeof options === 'undefined' ? 'undefined' : _typeof(options)) === 'object' && Array.isArray(options.transforms);
}
}, {
key: 'assertValidOptions',
value: function assertValidOptions(options) {
if (!ReactTransformBuilder.validateOptions(options)) {
throw new Error('babel-plugin-react-transform requires that you specify options ' + 'in .babelrc or from the Babel Node API, and that it is an object ' + 'with a transforms property which is an array.');
}
}
}]);
// Import transformation functions and initialize them
var initTransformCalls = allTransforms.map(function (transformOptions) {
return defineInitTransformCall(scope, file, recordsId, transformOptions);
}).filter(Boolean);
var initTransformIds = initTransformCalls.map(function (c) {
return c[0];
});
var initTransformVars = initTransformCalls.map(function (c) {
return c[1];
});
return ReactTransformBuilder;
})();
// Create one uber function calling each transformation
var wrapComponentId = this.state[wrapComponentIdKey];
var wrapComponent = defineWrapComponent(wrapComponentId, initTransformIds);
return {
visitor: {
Program: function Program(path, _ref2) {
var file = _ref2.file;
var opts = _ref2.opts;
return t.program([recordsVar].concat(_toConsumableArray(initTransformVars), [wrapComponent], _toConsumableArray(node.body)));
}
ReactTransformBuilder.assertValidOptions(opts);
var builder = new ReactTransformBuilder(file, opts);
builder.build();
}
}
});
};
};
module.exports = exports['default'];
var _arrayFind = require('array-find');
var _arrayFind2 = _interopRequireDefault(_arrayFind);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _typeof(obj) { return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
{
"name": "babel-plugin-react-transform",
"version": "1.1.1",
"version": "2.0.0-beta1",
"description": "Babel plugin to instrument React components with custom transforms",

@@ -17,3 +17,9 @@ "main": "lib/index.js",

"devDependencies": {
"babel": "^5.8.23",
"babel-cli": "^6.2.0",
"babel-core": "^6.2.1",
"babel-eslint": "^4.1.6",
"babel-preset-es2015": "^6.1.18",
"babel-register": "^6.2.0",
"eslint": "^1.10.3",
"eslint-plugin-react": "^3.11.2",
"mocha": "^2.2.5",

@@ -24,4 +30,4 @@ "rimraf": "^2.4.3"

"clean": "rimraf lib",
"build": "babel-plugin build",
"test": "mocha --compilers js:babel/register",
"build": "babel src -d lib",
"test": "mocha --compilers js:babel-register",
"test:watch": "npm run test -- --watch",

@@ -40,4 +46,4 @@ "prepublish": "npm run clean && npm run build"

"dependencies": {
"path-parse": "^1.0.5"
"array-find": "^1.0.0"
}
}

@@ -10,3 +10,3 @@ # Patrons

* [Ken Wheeler](http://kenwheeler.github.io/)
* Chung Yen Li
* [Chung Yen Li](https://www.facebook.com/prototocal.lee)
* [Sunil Pai](https://twitter.com/threepointone)

@@ -17,2 +17,5 @@ * [Charlie Cheever](https://twitter.com/ccheever)

* [Jed Watson](https://twitter.com/jedwatson)
* [Sasha Aickin](https://twitter.com/xander76)
* [Sasha Aickin](https://twitter.com/xander76)
* [Stefan Tennigkeit](https://twitter.com/whobubble)
* [Sam Vincent](https://twitter.com/samvincent)
* Olegzandr Denman
# babel-plugin-react-transform
This Babel plugin wraps all React components into arbitrary transforms written by the community.
In other words, **it lets you instrument React components** in any custom way.
[![react-transform channel on slack](https://img.shields.io/badge/slack-react--transform%40reactiflux-61DAFB.svg?style=flat-square)](http://www.reactiflux.com)
Such transforms can do a variety of things:
:rocket: **Now with [Babel 6](https://github.com/babel/babel) support** (thank you [@thejameskyle](https://github.com/thejameskyle)!)
* catch errors inside `render()` like **[react-transform-catch-errors](https://github.com/gaearon/react-transform-catch-errors)**;
* enable hot reloading like **[react-transform-hmr](https://github.com/gaearon/react-transform-hmr)**;
* render an inline prop inspector like **[react-transform-debug-inspector](https://github.com/alexkuz/react-transform-debug-inspector)**;
* highlight parts of the screen when components update like
**[react-transform-render-visualizer](https://github.com/spredfast/react-transform-render-visualizer)**;
* etc.
This plugin wraps React components with arbitrary transforms. In other words, **it allows you to instrument React components** in any way—limited only by your imagination.
The limit is your imagination and the time you feel compelled to spend on writing these transforms.
Time will show whether it is an amazing, or a terrible idea.
* [Ecosystem](#ecosystem)
* [Demo Project](#demo-project)
* [Installation](#installation)
* [Writing Transforms](#writing-transforms)
[![react-transform channel on slack](https://img.shields.io/badge/slack-react--transform%40reactiflux-61DAFB.svg?style=flat-square)](http://www.reactiflux.com)
## Ecosystem
## Demo
For a reference implementation, see [**react-transform-boilerplate**](https://github.com/gaearon/react-transform-boilerplate).
#### Transforms
* [**react-transform-hmr**](https://github.com/gaearon/react-transform-hmr) - enables hot reloading using HMR API
* [**react-transform-catch-errors**](https://github.com/gaearon/react-transform-catch-errors) - catches errors inside `render()`
* [**react-transform-debug-inspector**](https://github.com/alexkuz/react-transform-debug-inspector) - renders an inline prop inspector
* [**react-transform-render-visualizer**](https://github.com/spredfast/react-transform-render-visualizer) - highlight components when updated
Feeling inspired? Learn [how to write transforms](#writing-transforms) and send a PR!
## Demo Project
Check out **[react-transform-boilerplate](https://github.com/gaearon/react-transform-boilerplate)** for a demo showing a combination of transforms.
![](http://i.imgur.com/AhGY28T.gif)
![](https://cloud.githubusercontent.com/assets/1539088/11611771/ae1a6bd8-9bac-11e5-9206-42447e0fe064.gif)
## Installation
First, install the plugin:
This plugin is designed to be used with the Babel 6 ecosystem. These instructions assume you have a working project set up. If you do not have Babel set up in your project, [learn how to integrate](https://babeljs.io/docs/setup/) it with your toolkit before installing this plugin.
```
##### Using NPM
Install plugin and save in `devDependencies`:
```bash
npm install --save-dev babel-plugin-react-transform
```
Then, install the transforms you’re interested in:
Install some transforms:
```
```bash
npm install --save-dev react-transform-hmr

@@ -41,8 +52,10 @@ npm install --save-dev react-transform-catch-errors

Then edit your `.babelrc` to include `extra.react-transform`.
It must be an object with a `transforms` property being an array of the transforms you want to use:
##### Configuration
Add react-transform to the list of plugins in your babel configuration (usually `.babelrc`):
```js
{
"stage": 0,
"presets": ["react", "es2015"],
"env": {

@@ -54,15 +67,9 @@ // this plugin will be included only in development mode, e.g.

"plugins": [
// Include babel-plugin-react-display-name if you’re
// using React.createClass() *before* react-transform:
// "react-display-name",
"react-transform"
],
"extra": {
// must be an object
"react-transform": {
// must be an array
// must be an array with options object as second item
["react-transform", {
// must be an array of objects
"transforms": [{
// can be an NPM module name or a local path
"transform": "react-transform-hmr",
// see specific transform's docs for "imports" and "locals" it needs
// see transform docs for "imports" and "locals" dependencies
"imports": ["react"],

@@ -78,7 +85,7 @@ "locals": ["module"]

}]
},
// by default we only look for `React.createClass` (and ES6 classes)
// but you can tell the plugin to look for different component factories:
// factoryMethods: ["React.createClass", "createClass"]
}
// by default we only look for `React.createClass` (and ES6 classes)
// but you can tell the plugin to look for different component factories:
// factoryMethods: ["React.createClass", "createClass"]
}]
]
}

@@ -89,3 +96,3 @@ }

As you can see each transform, apart from the `transform` field where you write it name, also has `imports` and `locals` fields. You should consult the docs of each individual transform to learn which `imports` and `locals` it might need, and how it uses them. You probably already guessed that this is just a way to inject local variables (like `module`) or dependencies (like `react`) into the transforms that need them.
As you can see, each transform, apart from the `transform` field where you write it name, also has `imports` and `locals` fields. You should consult the docs of each individual transform to learn which `imports` and `locals` it might need, and how it uses them. You probably already guessed that this is just a way to inject local variables (like `module`) or dependencies (like `react`) into the transforms that need them.

@@ -96,3 +103,3 @@ Note that when using `React.createClass()` and allowing `babel` to extract the `displayName` property you must ensure that [babel-plugin-react-display-name](https://github.com/babel/babel/tree/development/packages/babel-plugin-react-display-name) is included before `react-transform`. See [this github issue](https://github.com/gaearon/babel-plugin-react-transform/issues/19) for more details.

## Writing a Transform
## Writing Transforms

@@ -139,3 +146,3 @@ It’s not hard to write a custom transform! First, make sure you call your NPM package `react-transform-*` so we have uniform naming across the transforms. The only thing you should export from your transform module is a function.

Oh, how do I get `displayName`?
Oh, how do I get `displayName`?
Actually, we give your transformation function a single argument called `options`. Yes, `options`:

@@ -207,17 +214,4 @@

Now go ahead and write your own!
Don’t forget to tag it with `react-transform` keyword on npm.
Now go ahead and write your own! Don’t forget to tag it with `react-transform` [keyword on npm](https://www.npmjs.com/browse/keyword/react-transform).
## Ecosystem
* **https://github.com/gaearon/react-transform-boilerplate**
* **https://github.com/gaearon/react-transform-hmr**
* **https://github.com/gaearon/react-transform-catch-errors**
* **https://github.com/alexkuz/react-transform-debug-inspector**
* Feeling inspired? Send a PR!
## Discussion
You can discuss React Transform and related projects in **#react-transform** channel on [Reactiflux Slack](http://reactiflux.com).
## Patrons

@@ -224,0 +218,0 @@

import path from 'path';
import fs from 'fs';
import assert from 'assert';
import { transformFileSync } from 'babel';
import { transformFileSync } from 'babel-core';
import plugin from '../src';

@@ -6,0 +6,0 @@

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc