babel-plugin-jsx-to-handlebars
Advanced tools
Comparing version
@@ -1,2 +0,1 @@ | ||
require('./car-tile')(); | ||
require('./test')(); | ||
@@ -3,0 +2,0 @@ var Handlebars = require('handlebars'); |
@@ -8,3 +8,3 @@ 'use strict'; | ||
var localContextRef = t.identifier('localContext'); | ||
var vars = undefined; | ||
var vars = new Map(); | ||
return new Plugin('handlebars', { | ||
@@ -32,3 +32,3 @@ visitor: { | ||
enter: function enter() { | ||
vars = findVariablesInJSX(this); | ||
findVariablesInJSX(this); | ||
}, | ||
@@ -58,38 +58,2 @@ exit: function exit(node) { | ||
}, | ||
VariableDeclaration: { | ||
enter: function enter() { | ||
// Add line 'context.<varname> = <varname>;' | ||
// after each variable declaration in methods but not in jsx-attributes | ||
// This fills the handlebars render-context with all local vars and react.props | ||
if (isInMethodDefinition(this) && !wasInsideJSXExpressionContainer(this)) { | ||
var _iteratorNormalCompletion = true; | ||
var _didIteratorError = false; | ||
var _iteratorError = undefined; | ||
try { | ||
for (var _iterator = this.get('declarations')[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
var decl = _step.value; | ||
var varRef = vars.get(createKeyFromPath(decl.get('id'))); | ||
if (varRef) { | ||
this.replaceWith(t.expressionStatement(t.assignmentExpression('=', createMemberExpression(localContextRef, varRef), decl.get('init').node))); | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError = true; | ||
_iteratorError = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion && _iterator['return']) { | ||
_iterator['return'](); | ||
} | ||
} finally { | ||
if (_didIteratorError) { | ||
throw _iteratorError; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
MemberExpression: { | ||
@@ -118,3 +82,2 @@ enter: function enter() { | ||
function findVariablesInJSX(path) { | ||
var vars = new Map(); | ||
path.traverse({ | ||
@@ -128,8 +91,7 @@ JSXExpressionContainer: { | ||
if (!isThisPropsExpression(expression)) { | ||
var key = createKeyFromPath(expression); | ||
var varRef = vars.get(key); | ||
if (!varRef) { | ||
varRef = scope.generateUidIdentifier('var'); | ||
vars.set(key, varRef); | ||
var varRef = newVariableReplacement(expression); | ||
if (!isFunctionInsideJSXExpressionContainer(expression)) { | ||
findClosestStatement(expression).insertBefore(t.expressionStatement(t.assignmentExpression('=', createMemberExpression(localContextRef, varRef.name), expression.node))); | ||
} | ||
this.setData('original', expression.node); | ||
expression.replaceWith(t.identifier(varRef.name)); | ||
@@ -142,3 +104,2 @@ } | ||
}); | ||
return vars; | ||
} | ||
@@ -148,10 +109,10 @@ | ||
switch (path.type) { | ||
case 'Literal': | ||
return path.get('value').node; | ||
case 'Identifier': | ||
case 'JSXIdentifier': | ||
return path.get('name').node; | ||
case 'MemberExpression': | ||
case 'JSXMemberExpression': | ||
return createKeyFromPath(path.get('object')) + '.' + createKeyFromPath(path.get('property')); | ||
case 'LogicalExpression': | ||
return createKeyFromPath(path.get('left')) + path.get('operator').node + createKeyFromPath(path.get('right')); | ||
case 'ThisExpression': | ||
return 'this'; | ||
default: | ||
@@ -169,3 +130,5 @@ throw new Error('Unable to create key for path type: ' + path.type); | ||
// TODO: Dynamic Component with Dynamic Partial | ||
tagName = '{{' + stringifyExpression(filterThisExpressions(namePath)) + '}}'; | ||
var varRef = newVariableReplacement(namePath); | ||
findClosestStatement(namePath).insertBefore(t.expressionStatement(t.assignmentExpression('=', createMemberExpression(localContextRef, varRef.name), rewriteJsxThisProps(namePath).node))); | ||
tagName = '{{' + varRef.name + '}}'; | ||
} else { | ||
@@ -190,3 +153,3 @@ tagName = namePath.get('name').node; | ||
// ... attributes... | ||
var attributes = opening.get('attributes'); | ||
var attributes = filterAttriubtes(opening.get('attributes')); | ||
if (hasSpreadAttribute(attributes)) { | ||
@@ -198,23 +161,20 @@ markup += processAttributesWithSpread(attributes, tagName, renderAsPartial); | ||
if (selfClosing) { | ||
markup += ' />'; | ||
if (renderAsPartial) { | ||
markup += '}}'; | ||
} else { | ||
if (renderAsPartial) { | ||
markup += '}}'; | ||
} else { | ||
markup += '>'; | ||
} | ||
markup += '>'; | ||
} | ||
// ... children... | ||
var childMarkup = ''; | ||
path.traverse({ | ||
enter: function enter() { | ||
if (this.isJSXElement()) { | ||
markup += processJSXElement(this); | ||
childMarkup += processJSXElement(this); | ||
} else if (this.isJSXExpressionContainer() && !isInsideJSXAttribute(this)) { | ||
markup += processJSXExpressionContainer.bind(this)(); | ||
childMarkup += processJSXExpressionContainer.bind(this)(); | ||
} else if (this.isJSXOpeningElement() || this.isJSXClosingElement()) { | ||
// Skip | ||
} else if (this.isLiteral()) { | ||
markup += this.get('value').node; | ||
childMarkup += this.get('value').node; | ||
} else { | ||
@@ -226,2 +186,3 @@ throw new Error('Unknown element during JSX processing: ' + this.type); | ||
}); | ||
markup += childMarkup.trim().replace(/(>|}})[\n\t]+\s*(<|{{)/g, '$1$2'); | ||
@@ -231,3 +192,3 @@ // ... and closing tag | ||
markup += '{{/' + tagName + '}}'; | ||
} else if (!selfClosing) { | ||
} else if (!selfClosing || !isHtmlVoidElement(tagName)) { | ||
markup += '</' + tagName + '>'; | ||
@@ -240,9 +201,9 @@ } | ||
var objects = []; | ||
var _iteratorNormalCompletion2 = true; | ||
var _didIteratorError2 = false; | ||
var _iteratorError2 = undefined; | ||
var _iteratorNormalCompletion = true; | ||
var _didIteratorError = false; | ||
var _iteratorError = undefined; | ||
try { | ||
for (var _iterator2 = attributes[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { | ||
var attribute = _step2.value; | ||
for (var _iterator = attributes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
var attribute = _step.value; | ||
@@ -256,3 +217,3 @@ if (attribute.isJSXSpreadAttribute()) { | ||
if (valuePath.isJSXExpressionContainer()) { | ||
value = valuePath.get('expression').node; | ||
value = valuePath.getData('original') || valuePath.get('expression').node; | ||
} else if (valuePath.isLiteral()) { | ||
@@ -265,12 +226,12 @@ value = valuePath.node; | ||
} catch (err) { | ||
_didIteratorError2 = true; | ||
_iteratorError2 = err; | ||
_didIteratorError = true; | ||
_iteratorError = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion2 && _iterator2['return']) { | ||
_iterator2['return'](); | ||
if (!_iteratorNormalCompletion && _iterator['return']) { | ||
_iterator['return'](); | ||
} | ||
} finally { | ||
if (_didIteratorError2) { | ||
throw _iteratorError2; | ||
if (_didIteratorError) { | ||
throw _iteratorError; | ||
} | ||
@@ -286,3 +247,4 @@ } | ||
} | ||
return ' {{#each ' + ref.name + '}}{{@key}}="{{this}}"{{/each}}'; | ||
findClosestStatement(attributes[0]).insertBefore(t.expressionStatement(t.assignmentExpression('=', createMemberExpression(localContextRef, ref.name), t.callExpression(createMemberExpression(t.callExpression(createMemberExpression(t.callExpression(createMemberExpression('Object', 'keys'), [createMemberExpression(localContextRef, ref.name)]), 'filter'), [t.functionExpression(null, [t.identifier('key')], t.blockStatement([t.returnStatement(t.logicalExpression('&&', t.logicalExpression('!=', t.identifier('key'), t.literal('children')), t.logicalExpression('!=', t.identifier('key'), t.literal('__$spread$__'))))]))]), 'reduce'), [t.functionExpression(null, [t.identifier('props'), t.identifier('key')], t.blockStatement([t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier('props'), t.identifier('key'), true), t.memberExpression(createMemberExpression(localContextRef, ref.name), t.identifier('key'), true))), t.returnStatement(t.identifier('props'))])), t.objectExpression([])])))); | ||
return '{{#each ' + ref.name + '}} {{@key}}="{{this}}"{{/each}}'; | ||
} | ||
@@ -292,9 +254,9 @@ | ||
var markup = ''; | ||
var _iteratorNormalCompletion3 = true; | ||
var _didIteratorError3 = false; | ||
var _iteratorError3 = undefined; | ||
var _iteratorNormalCompletion2 = true; | ||
var _didIteratorError2 = false; | ||
var _iteratorError2 = undefined; | ||
try { | ||
for (var _iterator3 = attributes[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { | ||
var attribute = _step3.value; | ||
for (var _iterator2 = attributes[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { | ||
var attribute = _step2.value; | ||
@@ -305,7 +267,7 @@ markup += ' ' + getAttributeName(attribute) + '='; | ||
if (!renderAsPartial) { | ||
markup += '{{'; | ||
markup += '"{{'; | ||
} | ||
markup += stringifyExpression(filterThisExpressions(value.get('expression'))); | ||
if (!renderAsPartial) { | ||
markup += '}}'; | ||
markup += '}}"'; | ||
} | ||
@@ -319,12 +281,12 @@ } else if (value.isLiteral()) { | ||
} catch (err) { | ||
_didIteratorError3 = true; | ||
_iteratorError3 = err; | ||
_didIteratorError2 = true; | ||
_iteratorError2 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion3 && _iterator3['return']) { | ||
_iterator3['return'](); | ||
if (!_iteratorNormalCompletion2 && _iterator2['return']) { | ||
_iterator2['return'](); | ||
} | ||
} finally { | ||
if (_didIteratorError3) { | ||
throw _iteratorError3; | ||
if (_didIteratorError2) { | ||
throw _iteratorError2; | ||
} | ||
@@ -349,17 +311,38 @@ } | ||
this.replaceWith(t.functionExpression(null, this.node.params, t.blockStatement([t.returnStatement(body.node)]), false, false)); | ||
return; | ||
} | ||
if (this.isFunctionExpression()) { | ||
this.setData('was-jsx', true); | ||
var body = this.get('body').get('body'); | ||
var firstStatement = body[0]; | ||
firstStatement.insertBefore(t.variableDeclaration('var', [t.variableDeclarator(localContextRef, t.callExpression(createMemberExpression('Object', 'assign'), [t.objectExpression([]), localContextRef]))])); | ||
if (this.isFunction()) { | ||
var params = this.get('params'); | ||
for (var i = params.length - 1; i >= 0; i--) { | ||
var _name3 = params[i].get('name').node; | ||
var varRef = vars.get(createKeyFromPath(params[i])); | ||
if (varRef) { | ||
firstStatement.insertBefore(t.expressionStatement(t.assignmentExpression('=', createMemberExpression(localContextRef, varRef.name), t.identifier(_name3)))); | ||
var objects = []; | ||
var _iteratorNormalCompletion3 = true; | ||
var _didIteratorError3 = false; | ||
var _iteratorError3 = undefined; | ||
try { | ||
for (var _iterator3 = params[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { | ||
var param = _step3.value; | ||
var _name3 = param.get('name').node; | ||
var varRef = vars.get(createKeyFromPath(param)); | ||
if (varRef) { | ||
objects.push(t.property(null, t.literal(varRef.name), t.identifier(_name3))); | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError3 = true; | ||
_iteratorError3 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion3 && _iterator3['return']) { | ||
_iterator3['return'](); | ||
} | ||
} finally { | ||
if (_didIteratorError3) { | ||
throw _iteratorError3; | ||
} | ||
} | ||
} | ||
var body = this.get('body').get('body'); | ||
var firstStatement = body[0]; | ||
firstStatement.insertBefore(t.variableDeclaration('var', [t.variableDeclarator(localContextRef, t.callExpression(createMemberExpression('Object', 'assign'), [t.objectExpression([]), localContextRef, t.objectExpression(objects)]))])); | ||
} | ||
@@ -407,9 +390,4 @@ } | ||
switch (path.get('type').node) { | ||
case 'Literal': | ||
return path; | ||
case 'Identifier': | ||
return path; | ||
case 'BinaryExpression': | ||
path.replaceWith(t.binaryExpression(path.get('operator').node, filterThisExpressions(path.get('left')).node, filterThisExpressions(path.get('right')).node)); | ||
return path; | ||
case 'MemberExpression': | ||
@@ -422,17 +400,2 @@ if (path.get('object').isThisExpression()) { | ||
return path; | ||
case 'CallExpression': | ||
if (path.get('callee').isMemberExpression()) { | ||
path.replaceWith(t.callExpression(filterThisExpressions(path.get('callee')).node, path.get('arguments').node)); | ||
} | ||
return path; | ||
case 'JSXMemberExpression': | ||
var object = path.get('object'); | ||
if (object.isJSXMemberExpression()) { | ||
path.replaceWith(t.jSXMemberExpression(filterThisExpressions(object).node, path.get('property').node)); | ||
} else { | ||
if (object.get('name').node == 'this') { | ||
path.replaceWith(path.get('property').node); | ||
} | ||
} | ||
return path; | ||
} | ||
@@ -445,16 +408,6 @@ throw new Error('Unknown expression node type: ' + path.get('type').node); | ||
switch (path.get('type').node) { | ||
case 'Literal': | ||
return path.get('value').node; | ||
case 'Identifier': | ||
return path.get('name').node; | ||
case 'BinaryExpression': | ||
return stringifyExpression(path.get('left')) + path.get('operator').node + stringifyExpression(path.get('right')); | ||
case 'MemberExpression': | ||
return stringifyExpression(path.get('object')) + '.' + stringifyExpression(path.get('property')); | ||
case 'ThisExpression': | ||
return 'this'; | ||
case 'JSXMemberExpression': | ||
return stringifyExpression(path.get('object')) + '.' + stringifyExpression(path.get('property')); | ||
case 'JSXIdentifier': | ||
return path.get('name').node; | ||
} | ||
@@ -464,2 +417,14 @@ throw new Error('Unknown expression node type: ' + path.type); | ||
function rewriteJsxThisProps(path) { | ||
if (path.isJSXMemberExpression()) { | ||
var object = path.get('object'); | ||
if (object.isJSXIdentifier() && object.get('name').node == 'this') { | ||
path.replaceWith(createMemberExpression(localContextRef, path.get('property').node)); | ||
} else { | ||
rewriteJsxThisProps(path.get('object')); | ||
} | ||
} | ||
return path; | ||
} | ||
// -- helper ------------------- | ||
@@ -498,18 +463,22 @@ | ||
function isInMethodDefinition(path) { | ||
return path.findParent(function (path) { | ||
return path.isMethodDefinition(); | ||
}); | ||
} | ||
function wasInsideJSXExpressionContainer(path) { | ||
function isInsideJSXAttribute(path) { | ||
return !!path.findParent(function (path) { | ||
return path.getData('was-jsx'); | ||
return path.isJSXAttribute(); | ||
}); | ||
} | ||
function isInsideJSXAttribute(path) { | ||
return !!path.findParent(function (path) { | ||
return path.isJSXAttribute(); | ||
function isFunctionInsideJSXExpressionContainer(path) { | ||
var result = false; | ||
path = path.findParent(function (path) { | ||
return path.isFunction(); | ||
}); | ||
if (path && path.isFunction()) { | ||
path = path.findParent(function (path) { | ||
return path.isJSXExpressionContainer(); | ||
}); | ||
if (path && path.isJSXExpressionContainer()) { | ||
result = true; | ||
} | ||
} | ||
return result; | ||
} | ||
@@ -568,2 +537,19 @@ | ||
function newVariableReplacement(path) { | ||
var key = createKeyFromPath(path); | ||
var varRef = vars.get(key); | ||
if (!varRef) { | ||
varRef = path.scope.generateUidIdentifier('var'); | ||
vars.set(key, varRef); | ||
} | ||
return varRef; | ||
} | ||
function filterAttriubtes(attributes) { | ||
var filteredAttributes = ['key', 'children']; | ||
return attributes.filter(function (attribute) { | ||
return !attribute.has('name') || filteredAttributes.indexOf(attribute.get('name').get('name').node) == -1; | ||
}); | ||
} | ||
function getAttributeName(attribute) { | ||
@@ -579,3 +565,11 @@ var JSXAttributeAliases = { | ||
} | ||
function isHtmlVoidElement(tagName) { | ||
var voidElements = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', | ||
// Note: menuitem is not void in react | ||
// 'menuitem', | ||
'meta', 'param', 'source', 'track', 'wbr']; | ||
return voidElements.indexOf(tagName) > -1; | ||
} | ||
}; | ||
@@ -6,3 +6,3 @@ module.exports = function(opts) { | ||
let localContextRef = t.identifier('localContext'); | ||
let vars; | ||
let vars = new Map(); | ||
return new Plugin('handlebars', { | ||
@@ -51,3 +51,3 @@ visitor: { | ||
enter() { | ||
vars = findVariablesInJSX(this); | ||
findVariablesInJSX(this); | ||
}, | ||
@@ -168,25 +168,2 @@ exit(node) { | ||
}, | ||
VariableDeclaration: { | ||
enter() { | ||
// Add line 'context.<varname> = <varname>;' | ||
// after each variable declaration in methods but not in jsx-attributes | ||
// This fills the handlebars render-context with all local vars and react.props | ||
if (isInMethodDefinition(this) && !wasInsideJSXExpressionContainer(this)) { | ||
for (let decl of this.get('declarations')) { | ||
let varRef = vars.get(createKeyFromPath(decl.get('id'))); | ||
if (varRef) { | ||
this.replaceWith( | ||
t.expressionStatement( | ||
t.assignmentExpression( | ||
'=', | ||
createMemberExpression(localContextRef, varRef), | ||
decl.get('init').node | ||
) | ||
) | ||
); | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
MemberExpression: { | ||
@@ -242,3 +219,2 @@ enter() { | ||
function findVariablesInJSX(path) { | ||
let vars = new Map(); | ||
path.traverse({ | ||
@@ -252,8 +228,15 @@ JSXExpressionContainer: { | ||
if (!isThisPropsExpression(expression)) { | ||
let key = createKeyFromPath(expression); | ||
let varRef = vars.get(key); | ||
if (!varRef) { | ||
varRef = scope.generateUidIdentifier('var'); | ||
vars.set(key, varRef); | ||
let varRef = newVariableReplacement(expression); | ||
if (!isFunctionInsideJSXExpressionContainer(expression)) { | ||
findClosestStatement(expression).insertBefore( | ||
t.expressionStatement( | ||
t.assignmentExpression( | ||
'=', | ||
createMemberExpression(localContextRef, varRef.name), | ||
expression.node | ||
) | ||
) | ||
); | ||
} | ||
this.setData('original', expression.node); | ||
expression.replaceWith(t.identifier(varRef.name)); | ||
@@ -266,3 +249,2 @@ } | ||
}); | ||
return vars; | ||
} | ||
@@ -272,12 +254,10 @@ | ||
switch (path.type) { | ||
case 'Literal': | ||
return path.get('value').node; | ||
case 'Identifier': | ||
case 'JSXIdentifier': | ||
return path.get('name').node; | ||
case 'MemberExpression': | ||
case 'JSXMemberExpression': | ||
return createKeyFromPath(path.get('object')) + '.' + createKeyFromPath(path.get('property')); | ||
case 'LogicalExpression': | ||
return createKeyFromPath(path.get('left')) | ||
+ path.get('operator').node | ||
+ createKeyFromPath(path.get('right')); | ||
case 'ThisExpression': | ||
return 'this'; | ||
default: | ||
@@ -295,3 +275,13 @@ throw new Error('Unable to create key for path type: ' + path.type); | ||
// TODO: Dynamic Component with Dynamic Partial | ||
tagName = '{{' + stringifyExpression(filterThisExpressions(namePath)) + '}}'; | ||
let varRef = newVariableReplacement(namePath); | ||
findClosestStatement(namePath).insertBefore( | ||
t.expressionStatement( | ||
t.assignmentExpression( | ||
'=', | ||
createMemberExpression(localContextRef, varRef.name), | ||
rewriteJsxThisProps(namePath).node | ||
) | ||
) | ||
); | ||
tagName = '{{' + varRef.name + '}}'; | ||
} else { | ||
@@ -324,3 +314,3 @@ tagName = namePath.get('name').node; | ||
// ... attributes... | ||
var attributes = opening.get('attributes'); | ||
var attributes = filterAttriubtes(opening.get('attributes')); | ||
if (hasSpreadAttribute(attributes)) { | ||
@@ -332,23 +322,20 @@ markup += processAttributesWithSpread(attributes, tagName, renderAsPartial); | ||
if (selfClosing) { | ||
markup += ' />'; | ||
if (renderAsPartial) { | ||
markup += '}}'; | ||
} else { | ||
if (renderAsPartial) { | ||
markup += '}}'; | ||
} else { | ||
markup += '>'; | ||
} | ||
markup += '>'; | ||
} | ||
// ... children... | ||
let childMarkup = ''; | ||
path.traverse({ | ||
enter() { | ||
if (this.isJSXElement()) { | ||
markup += processJSXElement(this); | ||
childMarkup += processJSXElement(this); | ||
} else if (this.isJSXExpressionContainer() && !isInsideJSXAttribute(this)) { | ||
markup += processJSXExpressionContainer.bind(this)(); | ||
childMarkup += processJSXExpressionContainer.bind(this)(); | ||
} else if (this.isJSXOpeningElement() || this.isJSXClosingElement()) { | ||
// Skip | ||
} else if (this.isLiteral()) { | ||
markup += this.get('value').node; | ||
childMarkup += this.get('value').node; | ||
} else { | ||
@@ -360,2 +347,3 @@ throw new Error('Unknown element during JSX processing: ' + this.type); | ||
}); | ||
markup += childMarkup.trim().replace(/(>|}})[\n\t]+\s*(<|{{)/g, '$1$2'); | ||
@@ -365,3 +353,3 @@ // ... and closing tag | ||
markup += '{{/' + tagName + '}}'; | ||
} else if (!selfClosing) { | ||
} else if (!selfClosing || !isHtmlVoidElement(tagName)) { | ||
markup += '</' + tagName + '>'; | ||
@@ -382,3 +370,3 @@ } | ||
if (valuePath.isJSXExpressionContainer()) { | ||
value = valuePath.get('expression').node; | ||
value = valuePath.getData('original') || valuePath.get('expression').node; | ||
} else if (valuePath.isLiteral()) { | ||
@@ -431,3 +419,82 @@ value = valuePath.node; | ||
} | ||
return ' {{#each ' + ref.name + '}}{{@key}}="{{this}}"{{/each}}'; | ||
findClosestStatement(attributes[0]).insertBefore( | ||
t.expressionStatement( | ||
t.assignmentExpression( | ||
'=', | ||
createMemberExpression(localContextRef, ref.name), | ||
t.callExpression( | ||
createMemberExpression( | ||
t.callExpression( | ||
createMemberExpression( | ||
t.callExpression( | ||
createMemberExpression('Object', 'keys'), | ||
[ | ||
createMemberExpression(localContextRef, ref.name) | ||
] | ||
), | ||
'filter' | ||
), | ||
[ | ||
t.functionExpression( | ||
null, | ||
[ | ||
t.identifier('key') | ||
], | ||
t.blockStatement([ | ||
t.returnStatement( | ||
t.logicalExpression( | ||
'&&', | ||
t.logicalExpression( | ||
'!=', | ||
t.identifier('key'), | ||
t.literal('children') | ||
), | ||
t.logicalExpression( | ||
'!=', | ||
t.identifier('key'), | ||
t.literal('__$spread$__') | ||
) | ||
) | ||
) | ||
]) | ||
) | ||
] | ||
), | ||
'reduce' | ||
), | ||
[ | ||
t.functionExpression( | ||
null, | ||
[ | ||
t.identifier('props'), | ||
t.identifier('key') | ||
], | ||
t.blockStatement([ | ||
t.expressionStatement( | ||
t.assignmentExpression( | ||
'=', | ||
t.memberExpression( | ||
t.identifier('props'), | ||
t.identifier('key'), | ||
true | ||
), | ||
t.memberExpression( | ||
createMemberExpression(localContextRef, ref.name), | ||
t.identifier('key'), | ||
true | ||
) | ||
) | ||
), | ||
t.returnStatement( | ||
t.identifier('props') | ||
) | ||
]) | ||
), | ||
t.objectExpression([]) | ||
] | ||
) | ||
) | ||
) | ||
); | ||
return '{{#each ' + ref.name + '}} {{@key}}="{{this}}"{{/each}}'; | ||
} | ||
@@ -442,7 +509,7 @@ | ||
if (!renderAsPartial) { | ||
markup += '{{' | ||
markup += '"{{' | ||
} | ||
markup += stringifyExpression(filterThisExpressions(value.get('expression'))); | ||
if (!renderAsPartial) { | ||
markup += '}}' | ||
markup += '}}"' | ||
} | ||
@@ -482,6 +549,19 @@ } else if (value.isLiteral()) { | ||
); | ||
return; | ||
} | ||
if (this.isFunctionExpression()) { | ||
this.setData('was-jsx', true); | ||
if (this.isFunction()) { | ||
let params = this.get('params'); | ||
let objects = []; | ||
for (let param of params) { | ||
let name = param.get('name').node; | ||
let varRef = vars.get(createKeyFromPath(param)); | ||
if (varRef) { | ||
objects.push( | ||
t.property( | ||
null, | ||
t.literal(varRef.name), | ||
t.identifier(name) | ||
) | ||
); | ||
} | ||
} | ||
let body = this.get('body').get('body'); | ||
@@ -497,3 +577,4 @@ let firstStatement = body[0]; | ||
t.objectExpression([]), | ||
localContextRef | ||
localContextRef, | ||
t.objectExpression(objects) | ||
] | ||
@@ -504,18 +585,2 @@ ) | ||
); | ||
let params = this.get('params'); | ||
for (let i = params.length - 1; i >= 0; i--) { | ||
let name = params[i].get('name').node; | ||
var varRef = vars.get(createKeyFromPath(params[i])); | ||
if (varRef) { | ||
firstStatement.insertBefore( | ||
t.expressionStatement( | ||
t.assignmentExpression( | ||
'=', | ||
createMemberExpression(localContextRef, varRef.name), | ||
t.identifier(name) | ||
) | ||
) | ||
); | ||
} | ||
} | ||
} | ||
@@ -606,15 +671,4 @@ } | ||
switch (path.get('type').node) { | ||
case 'Literal': | ||
return path; | ||
case 'Identifier': | ||
return path; | ||
case 'BinaryExpression': | ||
path.replaceWith( | ||
t.binaryExpression( | ||
path.get('operator').node, | ||
filterThisExpressions(path.get('left')).node, | ||
filterThisExpressions(path.get('right')).node | ||
) | ||
); | ||
return path; | ||
case 'MemberExpression': | ||
@@ -629,26 +683,2 @@ if (path.get('object').isThisExpression()) { | ||
return path; | ||
case 'CallExpression': | ||
if (path.get('callee').isMemberExpression()) { | ||
path.replaceWith( | ||
t.callExpression(filterThisExpressions(path.get('callee')).node, path.get('arguments').node) | ||
); | ||
} | ||
return path; | ||
case 'JSXMemberExpression': | ||
let object = path.get('object'); | ||
if (object.isJSXMemberExpression()) { | ||
path.replaceWith( | ||
t.jSXMemberExpression( | ||
filterThisExpressions(object).node, | ||
path.get('property').node | ||
) | ||
); | ||
} else { | ||
if (object.get('name').node == 'this') { | ||
path.replaceWith( | ||
path.get('property').node | ||
) | ||
} | ||
} | ||
return path; | ||
} | ||
@@ -661,18 +691,6 @@ throw new Error('Unknown expression node type: ' + path.get('type').node); | ||
switch (path.get('type').node) { | ||
case 'Literal': | ||
return path.get('value').node; | ||
case 'Identifier': | ||
return path.get('name').node; | ||
case 'BinaryExpression': | ||
return stringifyExpression(path.get('left')) | ||
+ path.get('operator').node | ||
+ stringifyExpression(path.get('right')); | ||
case 'MemberExpression': | ||
return stringifyExpression(path.get('object')) + '.' + stringifyExpression(path.get('property')); | ||
case 'ThisExpression': | ||
return 'this'; | ||
case 'JSXMemberExpression': | ||
return stringifyExpression(path.get('object')) + '.' + stringifyExpression(path.get('property')); | ||
case 'JSXIdentifier': | ||
return path.get('name').node; | ||
} | ||
@@ -682,2 +700,16 @@ throw new Error('Unknown expression node type: ' + path.type); | ||
function rewriteJsxThisProps(path) { | ||
if (path.isJSXMemberExpression()) { | ||
let object = path.get('object'); | ||
if (object.isJSXIdentifier() && object.get('name').node == 'this') { | ||
path.replaceWith( | ||
createMemberExpression(localContextRef, path.get('property').node) | ||
); | ||
} else { | ||
rewriteJsxThisProps(path.get('object')); | ||
} | ||
} | ||
return path; | ||
} | ||
// -- helper ------------------- | ||
@@ -708,14 +740,18 @@ | ||
function isInMethodDefinition(path) { | ||
return path.findParent(path => path.isMethodDefinition()); | ||
function isInsideJSXAttribute(path) { | ||
return !!path.findParent(path => path.isJSXAttribute()); | ||
} | ||
function wasInsideJSXExpressionContainer(path) { | ||
return !!path.findParent(path => path.getData('was-jsx')); | ||
function isFunctionInsideJSXExpressionContainer(path) { | ||
let result = false; | ||
path = path.findParent(path => path.isFunction()); | ||
if (path && (path.isFunction())) { | ||
path = path.findParent(path => path.isJSXExpressionContainer()); | ||
if (path && path.isJSXExpressionContainer()) { | ||
result = true; | ||
} | ||
} | ||
return result; | ||
} | ||
function isInsideJSXAttribute(path) { | ||
return !!path.findParent(path => path.isJSXAttribute()); | ||
} | ||
function isThisPropsExpression(path) { | ||
@@ -785,2 +821,20 @@ if (path.isMemberExpression()) { | ||
function newVariableReplacement(path) { | ||
let key = createKeyFromPath(path); | ||
let varRef = vars.get(key); | ||
if (!varRef) { | ||
varRef = path.scope.generateUidIdentifier('var'); | ||
vars.set(key, varRef); | ||
} | ||
return varRef; | ||
} | ||
function filterAttriubtes(attributes) { | ||
const filteredAttributes = [ | ||
'key', | ||
'children' | ||
]; | ||
return attributes.filter(attribute => !attribute.has('name') || filteredAttributes.indexOf(attribute.get('name').get('name').node) == -1); | ||
} | ||
function getAttributeName(attribute) { | ||
@@ -797,2 +851,25 @@ const JSXAttributeAliases = { | ||
function isHtmlVoidElement(tagName) { | ||
const voidElements = [ | ||
'area', | ||
'base', | ||
'br', | ||
'col', | ||
'embed', | ||
'hr', | ||
'img', | ||
'input', | ||
'keygen', | ||
'link', | ||
// Note: menuitem is not void in react | ||
// 'menuitem', | ||
'meta', | ||
'param', | ||
'source', | ||
'track', | ||
'wbr' | ||
]; | ||
return voidElements.indexOf(tagName) > -1; | ||
} | ||
} |
{ | ||
"name": "babel-plugin-jsx-to-handlebars", | ||
"version": "0.1.1", | ||
"version": "0.1.2", | ||
"description": "babel-plugin to convert simple stateless react compontents to handlebars templates", | ||
@@ -11,6 +11,23 @@ "main": "dist/plugin.dist.js", | ||
"browserify": "browserify demo/main.js > demo/index.js", | ||
"test": "tape ./tests/**/*-test.js | faucet" | ||
"test": "babel-istanbul cover tests/run.js | faucet" | ||
}, | ||
"author": "markus.wolf@sinnerschrader.com", | ||
"author": { | ||
"name": "SinnerSchrader - Freie Radikale", | ||
"email": "team-freie-radikale@sinnerschrader.com" | ||
}, | ||
"contributors": [ | ||
{ | ||
"name": "Markus Wolf", | ||
"email": "markus.wolf@sinnerschrader.com" | ||
} | ||
], | ||
"license": "MIT", | ||
"repository": { | ||
"type": "git", | ||
"url": "sinnerschrader/babel-plugin-jsx-to-handlebars" | ||
}, | ||
"engines": { | ||
"node": ">= 4.0.0", | ||
"npm": ">= 2.5.1" | ||
}, | ||
"dependencies": { | ||
@@ -21,8 +38,11 @@ "babel": "^5.8.23", | ||
"devDependencies": { | ||
"babel-istanbul": "^0.3.20", | ||
"browserify": "^11.1.0", | ||
"classnames": "^2.1.3", | ||
"faucet": "0.0.1", | ||
"glob": "^5.0.14", | ||
"nodemon": "^1.5.0", | ||
"react": "^0.13.3", | ||
"tape": "^4.2.0" | ||
} | ||
} |
# babel-plugin-jsx-to-handlebars | ||
[](https://github.com/sinnerschrader/babel-plugin-jsx-to-handlebars) | ||
[](https://travis-ci.org/sinnerschrader/babel-plugin-jsx-to-handlebars) | ||
[](https://www.npmjs.com/package/babel-plugin-jsx-to-handlebars) | ||
Convertes simple stateless react compontents to handlebars templates | ||
@@ -3,0 +8,0 @@ |
@@ -1,33 +0,50 @@ | ||
var fs = require('fs'); | ||
var babel = require('babel'); | ||
var Handlebars = require('handlebars'); | ||
import * as path from 'path'; | ||
import * as fs from 'fs'; | ||
import * as babel from 'babel'; | ||
import Handlebars from 'handlebars'; | ||
import React from 'react'; | ||
var helpers = function(path) { | ||
return helpers.compile(helpers.load(helpers.transform(path))); | ||
export function handlebars(file, data = {}) { | ||
var dir = path.dirname(file); | ||
return compileTemplate(customRequire(dir, babelTransform(file)), undefined, data)(); | ||
} | ||
helpers.transform = function(path) { | ||
var source = fs.readFileSync(path); | ||
export function react(file, data = {}) { | ||
let Component = require(path.resolve(__dirname, '..', file)); | ||
return React.renderToStaticMarkup(React.createElement(Component, data)); | ||
} | ||
export function babelTransform(file, enablePlugin = true) { | ||
let source = fs.readFileSync(file); | ||
return babel.transform(source, { | ||
stage: 0, | ||
plugins: ['../dist/plugin.dist.js'] | ||
plugins: enablePlugin ? ['../dist/plugin.dist.js'] : [] | ||
}).code; | ||
} | ||
helpers.load = function(code) { | ||
var mod = { | ||
exports: {} | ||
}; | ||
var fn = new Function('module', 'exports', 'require', code); | ||
fn.call(null, mod, mod.exports, require); | ||
return mod.exports; | ||
export function customRequire(dir, code) { | ||
let commonjs = function(code) { | ||
let mod = { | ||
exports: {} | ||
}; | ||
let req = function(id) { | ||
if (id[0] == '.') { | ||
id = './' + path.join(dir, id); | ||
return commonjs(babelTransform(id)); | ||
} else { | ||
return require(id); | ||
} | ||
} | ||
let fn = new Function('module', 'exports', 'require', code); | ||
fn.call(null, mod, mod.exports, req); | ||
return mod.exports; | ||
} | ||
return commonjs(code); | ||
} | ||
helpers.compile = function(templateModule, children) { | ||
children = children || ''; | ||
export function compileTemplate(templateModule, children = '', data = {}) { | ||
templateModule.call(null); | ||
var name = templateModule.name; | ||
return Handlebars.compile('{{#>' + name + '}}' + children + '{{/' + name + '}}'); | ||
let name = templateModule.name; | ||
let props = Object.keys(data).map(key => `${key}="${data[key]}"`).join(' '); | ||
return Handlebars.compile(`{{#>${name} ${props}}}${children}{{/${name}}}`); | ||
} | ||
module.exports = helpers; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
40
60%1959
10.68%12
71.43%72120
-2.41%8
60%5
25%