babel-plugin-nornj-in-jsx
Version:
Make the NornJ work gracefully in the JSX environment
232 lines (215 loc) • 7.53 kB
JavaScript
const nj = require('nornj/dist/nornj.common').default;
const astUtil = require('./util/ast');
const generate = require('./util/generate');
module.exports = function(babel) {
const types = babel.types;
return function(node, path, state) {
const quasis = [];
const expressions = [];
const expressionIdentifiers = new Set();
const isJSXMemberExpression = types.isJSXMemberExpression(node.openingElement.name);
let elName = node.openingElement.name.name;
const key = astUtil.getKey(node);
const attrs = astUtil.getAttributeMap(node);
const children = astUtil.getChildren(types, node);
const isSelfClosing = node.openingElement.selfClosing;
const childrenExpression = !isSelfClosing && astUtil.getSanitizedExpressionForContent(types, children, key);
const isComponent = !isJSXMemberExpression ? astUtil.REGEX_CAPITALIZE.test(elName) : true;
let tagName = '<';
if (!isComponent) {
tagName += elName;
} else {
quasis.push(
types.TemplateElement({
raw: tagName,
cooked: tagName
})
);
if (isJSXMemberExpression) {
elName = generate.getElName(types, node.openingElement.name);
} else {
elName = types.identifier(elName);
}
expressions.push(elName);
}
let lastAttrStr = '';
function _buildFromNjExp(directiveExpressions, attrStr) {
if (!nj.isString(directiveExpressions[0])) {
directiveExpressions.unshift('');
}
if (!nj.isString(directiveExpressions[directiveExpressions.length - 1])) {
directiveExpressions.push('');
}
directiveExpressions.forEach((e, i) => {
if (i == 0) {
quasis.push(
types.TemplateElement({
raw: attrStr + '"{{' + e,
cooked: attrStr + '"{{' + e
})
);
} else if (i == directiveExpressions.length - 1) {
lastAttrStr = e + '}}"';
} else {
if (nj.isString(e)) {
quasis.push(
types.TemplateElement({
raw: e,
cooked: e
})
);
} else {
e.noExpression = true;
expressions.push(e);
}
}
});
}
Object.keys(attrs).forEach((attrName, i) => {
const attr = attrs[attrName];
if (attr.type != 'JSXSpreadAttribute') {
const isDirective = astUtil.isDirective(attrName);
const _attrName = isDirective ? astUtil.transformDirective(attrName) : attrName;
const attrStr = lastAttrStr + (i == 0 ? (!isComponent ? tagName : '') : '') + ' ' + _attrName + '=';
let directiveConfig = isDirective
? nj.extensionConfig[
_attrName != 'style' ? _attrName.substr(2).replace(astUtil.REGEX_EX_ATTR, (all, name) => name) : _attrName
]
: {};
!directiveConfig && (directiveConfig = {});
if (!attr.value) {
lastAttrStr = attrStr.substr(0, attrStr.length - 1);
} else if (attr.value.type == 'JSXExpressionContainer') {
const expr = attr.value.expression;
let { useExpressionInProps } = directiveConfig;
const { isBindable } = directiveConfig;
if (isBindable) {
useExpressionInProps = true;
}
//Template literal case 1: `123 + abc`
if (isDirective && useExpressionInProps && types.isStringLiteral(expr) && !expr.extra) {
lastAttrStr = attrStr + '"{{' + expr.value + '}}"';
}
//Template literal case 2: `123 + abc + ${def}`
else if (
isDirective &&
useExpressionInProps &&
types.isCallExpression(expr) &&
types.isStringLiteral(expr.callee.object) &&
expr.callee.property.name === 'concat'
) {
const directiveExpressions = expr.arguments.map(e => {
if (types.isStringLiteral(e) && !e.extra) {
return e.value;
}
return e;
});
if (expr.callee.object.value !== '') {
directiveExpressions.unshift(!expr.callee.object.extra ? expr.callee.object.value : expr.callee.object);
}
_buildFromNjExp(directiveExpressions, attrStr);
} else if (isDirective && isBindable) {
if (types.isMemberExpression(expr)) {
const directiveMemberExpressions = generate.getDirectiveMemberExpression(types, expr);
quasis.push(
types.TemplateElement({
raw: attrStr + '"{{',
cooked: attrStr + '"{{'
})
);
const exprFirst = directiveMemberExpressions[0];
exprFirst.noExpression = true;
expressions.push(exprFirst);
lastAttrStr =
'.' +
directiveMemberExpressions
.slice(1)
.map(e => e.name)
.join('.') +
'}}"';
} else {
quasis.push(
types.TemplateElement({
raw: attrStr,
cooked: attrStr
})
);
if (expr.name != null) {
expr.paramIdentifierName = `set${nj.upperFirst(expr.name)}`;
}
expressions.push(expr);
lastAttrStr = '';
}
} else {
quasis.push(
types.TemplateElement({
raw: attrStr,
cooked: attrStr
})
);
expressions.push(expr);
lastAttrStr = '';
}
} else {
quasis.push(
types.TemplateElement({
raw: attrStr,
cooked: attrStr
})
);
expressions.push(attr.value);
lastAttrStr = '';
}
} else {
quasis.push(
types.TemplateElement({
raw: ' ',
cooked: ' '
})
);
attr.argument.isSpread = true;
expressions.push(attr.argument);
lastAttrStr = '';
}
});
if (!isSelfClosing) {
quasis.push(
types.TemplateElement({
raw: lastAttrStr + '>',
cooked: lastAttrStr + '>'
})
);
expressions.push(childrenExpression);
if (!isComponent) {
quasis.push(
types.TemplateElement({
raw: '</' + elName + '>',
cooked: '</' + elName + '>'
})
);
} else {
const closeTagPrefix = types.TemplateElement({
raw: '</',
cooked: '</'
});
closeTagPrefix.isCloseTagPrefix = true;
quasis.push(closeTagPrefix);
expressions.push(elName);
quasis.push(
types.TemplateElement({
raw: '>',
cooked: '>'
})
);
}
} else {
quasis.push(
types.TemplateElement({
raw: lastAttrStr + ' />',
cooked: lastAttrStr + ' />'
})
);
}
return generate.createRenderTmpl(babel, quasis, expressions, state.opts, path, 'directive', expressionIdentifiers);
};
};