babel-plugin-react-transform
Advanced tools
Comparing version 1.1.1 to 2.0.0-beta1
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 |
100
README.md
# 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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
59
37373
9
666
1
222
2
+ Addedarray-find@^1.0.0
+ Addedarray-find@1.0.0(transitive)
- Removedpath-parse@^1.0.5
- Removedpath-parse@1.0.7(transitive)