123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- var esutils = require('esutils')
- var groupProps = require('./lib/group-props')
- var mustUseProp = require('./lib/must-use-prop')
- var isInsideJsxExpression = function (t, path) {
- if (!path.parentPath) {
- return false
- }
- if (t.isJSXExpressionContainer(path.parentPath)) {
- return true
- }
- return isInsideJsxExpression(t, path.parentPath)
- }
- module.exports = function (babel) {
- var t = babel.types
- return {
- inherits: require('babel-plugin-syntax-jsx'),
- visitor: {
- JSXNamespacedName (path) {
- throw path.buildCodeFrameError(
- 'Namespaced tags/attributes are not supported. JSX is not XML.\n' +
- 'For attributes like xlink:href, use xlinkHref instead.'
- )
- },
- JSXElement: {
- exit (path, file) {
- // turn tag into createElement call
- var callExpr = buildElementCall(path.get('openingElement'), file)
- if (path.node.children.length) {
- // add children array as 3rd arg
- callExpr.arguments.push(t.arrayExpression(path.node.children))
- if (callExpr.arguments.length >= 3) {
- callExpr._prettyCall = true
- }
- }
- path.replaceWith(t.inherits(callExpr, path.node))
- }
- },
- 'Program' (path) {
- path.traverse({
- 'ObjectMethod|ClassMethod' (path) {
- const params = path.get('params')
- // do nothing if there is (h) param
- if (params.length && params[0].node.name === 'h') {
- return
- }
- // do nothing if there is no JSX inside
- const jsxChecker = {
- hasJsx: false
- }
- path.traverse({
- JSXElement () {
- this.hasJsx = true
- }
- }, jsxChecker)
- if (!jsxChecker.hasJsx) {
- return
- }
- // do nothing if this method is a part of JSX expression
- if (isInsideJsxExpression(t, path)) {
- return
- }
- const isRender = path.node.key.name === 'render'
- // inject h otherwise
- path.get('body').unshiftContainer('body', t.variableDeclaration('const', [
- t.variableDeclarator(
- t.identifier('h'),
- (
- isRender
- ? t.memberExpression(
- t.identifier('arguments'),
- t.numericLiteral(0),
- true
- )
- : t.memberExpression(
- t.thisExpression(),
- t.identifier('$createElement')
- )
- )
- )
- ]))
- },
- JSXOpeningElement (path) {
- const tag = path.get('name').node.name
- const attributes = path.get('attributes')
- const typeAttribute = attributes.find(attributePath => attributePath.node.name && attributePath.node.name.name === 'type')
- const type = typeAttribute && t.isStringLiteral(typeAttribute.node.value) ? typeAttribute.node.value.value : null
- attributes.forEach(attributePath => {
- const attribute = attributePath.get('name')
- if (!attribute.node) {
- return
- }
- const attr = attribute.node.name
- if (mustUseProp(tag, type, attr) && t.isJSXExpressionContainer(attributePath.node.value)) {
- attribute.replaceWith(t.JSXIdentifier(`domProps-${attr}`))
- }
- })
- }
- })
- }
- }
- }
- function buildElementCall (path, file) {
- path.parent.children = t.react.buildChildren(path.parent)
- var tagExpr = convertJSXIdentifier(path.node.name, path.node)
- var args = []
- var tagName
- if (t.isIdentifier(tagExpr)) {
- tagName = tagExpr.name
- } else if (t.isLiteral(tagExpr)) {
- tagName = tagExpr.value
- }
- if (t.react.isCompatTag(tagName)) {
- args.push(t.stringLiteral(tagName))
- } else {
- args.push(tagExpr)
- }
- var attribs = path.node.attributes
- if (attribs.length) {
- attribs = buildOpeningElementAttributes(attribs, file)
- args.push(attribs)
- }
- return t.callExpression(t.identifier('h'), args)
- }
- function convertJSXIdentifier (node, parent) {
- if (t.isJSXIdentifier(node)) {
- if (node.name === 'this' && t.isReferenced(node, parent)) {
- return t.thisExpression()
- } else if (esutils.keyword.isIdentifierNameES6(node.name)) {
- node.type = 'Identifier'
- } else {
- return t.stringLiteral(node.name)
- }
- } else if (t.isJSXMemberExpression(node)) {
- return t.memberExpression(
- convertJSXIdentifier(node.object, node),
- convertJSXIdentifier(node.property, node)
- )
- }
- return node
- }
- /**
- * The logic for this is quite terse. It's because we need to
- * support spread elements. We loop over all attributes,
- * breaking on spreads, we then push a new object containing
- * all prior attributes to an array for later processing.
- */
- function buildOpeningElementAttributes (attribs, file) {
- var _props = []
- var objs = []
- function pushProps () {
- if (!_props.length) return
- objs.push(t.objectExpression(_props))
- _props = []
- }
- while (attribs.length) {
- var prop = attribs.shift()
- if (t.isJSXSpreadAttribute(prop)) {
- pushProps()
- prop.argument._isSpread = true
- objs.push(prop.argument)
- } else {
- _props.push(convertAttribute(prop))
- }
- }
- pushProps()
- objs = objs.map(function (o) {
- return o._isSpread ? o : groupProps(o.properties, t)
- })
- if (objs.length === 1) {
- // only one object
- attribs = objs[0]
- } else if (objs.length) {
- // add prop merging helper
- var helper = file.addImport('babel-helper-vue-jsx-merge-props', 'default', '_mergeJSXProps')
- // spread it
- attribs = t.callExpression(
- helper,
- [t.arrayExpression(objs)]
- )
- }
- return attribs
- }
- function convertAttribute (node) {
- var value = convertAttributeValue(node.value || t.booleanLiteral(true))
- if (t.isStringLiteral(value) && !t.isJSXExpressionContainer(node.value)) {
- value.value = value.value.replace(/\n\s+/g, ' ')
- }
- if (t.isValidIdentifier(node.name.name)) {
- node.name.type = 'Identifier'
- } else {
- node.name = t.stringLiteral(node.name.name)
- }
- return t.inherits(t.objectProperty(node.name, value), node)
- }
- function convertAttributeValue (node) {
- if (t.isJSXExpressionContainer(node)) {
- return node.expression
- } else {
- return node
- }
- }
- }
|