1 | var esutils = require('esutils')
|
2 | var groupProps = require('./lib/group-props')
|
3 | var mustUseProp = require('./lib/must-use-prop')
|
4 |
|
5 | var isInsideJsxExpression = function (t, path) {
|
6 | if (!path.parentPath) {
|
7 | return false
|
8 | }
|
9 | if (t.isJSXExpressionContainer(path.parentPath)) {
|
10 | return true
|
11 | }
|
12 | return isInsideJsxExpression(t, path.parentPath)
|
13 | }
|
14 |
|
15 | module.exports = function (babel) {
|
16 | var t = babel.types
|
17 |
|
18 | return {
|
19 | inherits: require('babel-plugin-syntax-jsx'),
|
20 | visitor: {
|
21 | JSXNamespacedName (path) {
|
22 | throw path.buildCodeFrameError(
|
23 | 'Namespaced tags/attributes are not supported. JSX is not XML.\n' +
|
24 | 'For attributes like xlink:href, use xlinkHref instead.'
|
25 | )
|
26 | },
|
27 | JSXElement: {
|
28 | exit (path, file) {
|
29 |
|
30 | var callExpr = buildElementCall(path.get('openingElement'), file)
|
31 | if (path.node.children.length) {
|
32 |
|
33 | callExpr.arguments.push(t.arrayExpression(path.node.children))
|
34 | if (callExpr.arguments.length >= 3) {
|
35 | callExpr._prettyCall = true
|
36 | }
|
37 | }
|
38 | path.replaceWith(t.inherits(callExpr, path.node))
|
39 | }
|
40 | },
|
41 | 'Program' (path) {
|
42 | path.traverse({
|
43 | 'ObjectMethod|ClassMethod' (path) {
|
44 | const params = path.get('params')
|
45 |
|
46 | if (params.length && params[0].node.name === 'h') {
|
47 | return
|
48 | }
|
49 |
|
50 | const jsxChecker = {
|
51 | hasJsx: false
|
52 | }
|
53 | path.traverse({
|
54 | JSXElement () {
|
55 | this.hasJsx = true
|
56 | }
|
57 | }, jsxChecker)
|
58 | if (!jsxChecker.hasJsx) {
|
59 | return
|
60 | }
|
61 |
|
62 | if (isInsideJsxExpression(t, path)) {
|
63 | return
|
64 | }
|
65 | const isRender = path.node.key.name === 'render'
|
66 |
|
67 | path.get('body').unshiftContainer('body', t.variableDeclaration('const', [
|
68 | t.variableDeclarator(
|
69 | t.identifier('h'),
|
70 | (
|
71 | isRender
|
72 | ? t.memberExpression(
|
73 | t.identifier('arguments'),
|
74 | t.numericLiteral(0),
|
75 | true
|
76 | )
|
77 | : t.memberExpression(
|
78 | t.thisExpression(),
|
79 | t.identifier('$createElement')
|
80 | )
|
81 | )
|
82 | )
|
83 | ]))
|
84 | },
|
85 | JSXOpeningElement (path) {
|
86 | const tag = path.get('name').node.name
|
87 | const attributes = path.get('attributes')
|
88 | const typeAttribute = attributes.find(attributePath => attributePath.node.name && attributePath.node.name.name === 'type')
|
89 | const type = typeAttribute && t.isStringLiteral(typeAttribute.node.value) ? typeAttribute.node.value.value : null
|
90 |
|
91 | attributes.forEach(attributePath => {
|
92 | const attribute = attributePath.get('name')
|
93 |
|
94 | if (!attribute.node) {
|
95 | return
|
96 | }
|
97 |
|
98 | const attr = attribute.node.name
|
99 |
|
100 | if (mustUseProp(tag, type, attr) && t.isJSXExpressionContainer(attributePath.node.value)) {
|
101 | attribute.replaceWith(t.JSXIdentifier(`domProps-${attr}`))
|
102 | }
|
103 | })
|
104 | }
|
105 | })
|
106 | }
|
107 | }
|
108 | }
|
109 |
|
110 | function buildElementCall (path, file) {
|
111 | path.parent.children = t.react.buildChildren(path.parent)
|
112 | var tagExpr = convertJSXIdentifier(path.node.name, path.node)
|
113 | var args = []
|
114 |
|
115 | var tagName
|
116 | if (t.isIdentifier(tagExpr)) {
|
117 | tagName = tagExpr.name
|
118 | } else if (t.isLiteral(tagExpr)) {
|
119 | tagName = tagExpr.value
|
120 | }
|
121 |
|
122 | if (t.react.isCompatTag(tagName)) {
|
123 | args.push(t.stringLiteral(tagName))
|
124 | } else {
|
125 | args.push(tagExpr)
|
126 | }
|
127 |
|
128 | var attribs = path.node.attributes
|
129 | if (attribs.length) {
|
130 | attribs = buildOpeningElementAttributes(attribs, file)
|
131 | args.push(attribs)
|
132 | }
|
133 | return t.callExpression(t.identifier('h'), args)
|
134 | }
|
135 |
|
136 | function convertJSXIdentifier (node, parent) {
|
137 | if (t.isJSXIdentifier(node)) {
|
138 | if (node.name === 'this' && t.isReferenced(node, parent)) {
|
139 | return t.thisExpression()
|
140 | } else if (esutils.keyword.isIdentifierNameES6(node.name)) {
|
141 | node.type = 'Identifier'
|
142 | } else {
|
143 | return t.stringLiteral(node.name)
|
144 | }
|
145 | } else if (t.isJSXMemberExpression(node)) {
|
146 | return t.memberExpression(
|
147 | convertJSXIdentifier(node.object, node),
|
148 | convertJSXIdentifier(node.property, node)
|
149 | )
|
150 | }
|
151 | return node
|
152 | }
|
153 |
|
154 | |
155 |
|
156 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 | function buildOpeningElementAttributes (attribs, file) {
|
162 | var _props = []
|
163 | var objs = []
|
164 |
|
165 | function pushProps () {
|
166 | if (!_props.length) return
|
167 | objs.push(t.objectExpression(_props))
|
168 | _props = []
|
169 | }
|
170 |
|
171 | while (attribs.length) {
|
172 | var prop = attribs.shift()
|
173 | if (t.isJSXSpreadAttribute(prop)) {
|
174 | pushProps()
|
175 | prop.argument._isSpread = true
|
176 | objs.push(prop.argument)
|
177 | } else {
|
178 | _props.push(convertAttribute(prop))
|
179 | }
|
180 | }
|
181 |
|
182 | pushProps()
|
183 |
|
184 | objs = objs.map(function (o) {
|
185 | return o._isSpread ? o : groupProps(o.properties, t)
|
186 | })
|
187 |
|
188 | if (objs.length === 1) {
|
189 |
|
190 | attribs = objs[0]
|
191 | } else if (objs.length) {
|
192 |
|
193 | var helper = file.addImport('babel-helper-vue-jsx-merge-props', 'default', '_mergeJSXProps')
|
194 |
|
195 | attribs = t.callExpression(
|
196 | helper,
|
197 | [t.arrayExpression(objs)]
|
198 | )
|
199 | }
|
200 | return attribs
|
201 | }
|
202 |
|
203 | function convertAttribute (node) {
|
204 | var value = convertAttributeValue(node.value || t.booleanLiteral(true))
|
205 | if (t.isStringLiteral(value) && !t.isJSXExpressionContainer(node.value)) {
|
206 | value.value = value.value.replace(/\n\s+/g, ' ')
|
207 | }
|
208 | if (t.isValidIdentifier(node.name.name)) {
|
209 | node.name.type = 'Identifier'
|
210 | } else {
|
211 | node.name = t.stringLiteral(node.name.name)
|
212 | }
|
213 | return t.inherits(t.objectProperty(node.name, value), node)
|
214 | }
|
215 |
|
216 | function convertAttributeValue (node) {
|
217 | if (t.isJSXExpressionContainer(node)) {
|
218 | return node.expression
|
219 | } else {
|
220 | return node
|
221 | }
|
222 | }
|
223 | }
|