lepu-test-platform-web/node_modules/babel-plugin-transform-vue-jsx/index.js

224 lines
6.6 KiB
JavaScript
Raw Normal View History

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
}
}
}