Skip to content

Commit ff8fcd2

Browse files
Hanks10100yyx990803
authored andcommitted
feat(weex): support parse object literal in binding attrs and styles (#7291)
1 parent f8cb3a2 commit ff8fcd2

File tree

7 files changed

+179
-7
lines changed

7 files changed

+179
-7
lines changed

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
"devDependencies": {
6060
"@types/node": "^8.0.33",
6161
"@types/webpack": "^3.0.13",
62+
"acorn": "^5.2.1",
6263
"babel-core": "^6.25.0",
6364
"babel-eslint": "^8.0.3",
6465
"babel-helper-vue-jsx-merge-props": "^2.0.2",
@@ -79,6 +80,7 @@
7980
"cz-conventional-changelog": "^2.0.0",
8081
"de-indent": "^1.0.2",
8182
"es6-promise": "^4.1.0",
83+
"escodegen": "^1.8.1",
8284
"eslint": "^4.13.1",
8385
"eslint-loader": "^1.7.1",
8486
"eslint-plugin-flowtype": "^2.34.0",

packages/weex-template-compiler/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
},
1919
"homepage": "https://github.com/vuejs/vue/tree/dev/packages/weex-template-compiler#readme",
2020
"dependencies": {
21+
"acorn": "^5.2.1",
22+
"escodegen": "^1.8.1",
2123
"he": "^1.1.0"
2224
}
2325
}

src/platforms/weex/compiler/modules/recycle-list/v-bind.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* @flow */
22

33
import { camelize } from 'shared/util'
4+
import { generateBinding } from 'weex/util/parser'
45
import { bindRE } from 'compiler/parser/index'
56
import { getAndRemoveAttr, addRawAttr } from 'compiler/helpers'
67

@@ -12,9 +13,7 @@ export function preTransformVBind (el: ASTElement, options: WeexCompilerOptions)
1213
for (const attr in el.attrsMap) {
1314
if (bindRE.test(attr)) {
1415
const name: string = parseAttrName(attr)
15-
const value = {
16-
'@binding': getAndRemoveAttr(el, attr)
17-
}
16+
const value = generateBinding(getAndRemoveAttr(el, attr))
1817
delete el.attrsMap[attr]
1918
addRawAttr(el, name, value)
2019
}

src/platforms/weex/compiler/modules/style.js

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* @flow */
22

3-
import { cached, camelize } from 'shared/util'
3+
import { cached, camelize, isPlainObject } from 'shared/util'
44
import { parseText } from 'compiler/parser/text-parser'
55
import {
66
getAndRemoveAttr,
@@ -10,7 +10,7 @@ import {
1010

1111
type StaticStyleResult = {
1212
dynamic: boolean,
13-
styleResult: string
13+
styleResult: string | Object | void
1414
};
1515

1616
const normalize = cached(camelize)
@@ -27,12 +27,14 @@ function transformNode (el: ASTElement, options: CompilerOptions) {
2727
)
2828
}
2929
if (!dynamic && styleResult) {
30+
// $flow-disable-line
3031
el.staticStyle = styleResult
3132
}
3233
const styleBinding = getBindingAttr(el, 'style', false /* getStatic */)
3334
if (styleBinding) {
3435
el.styleBinding = styleBinding
3536
} else if (dynamic) {
37+
// $flow-disable-line
3638
el.styleBinding = styleResult
3739
}
3840
}
@@ -53,7 +55,7 @@ function parseStaticStyle (staticStyle: ?string, options: CompilerOptions): Stat
5355
// "width: 200px; height: {{y}}" -> {width: 200, height: y}
5456
let dynamic = false
5557
let styleResult = ''
56-
if (staticStyle) {
58+
if (typeof staticStyle === 'string') {
5759
const styleList = staticStyle.trim().split(';').map(style => {
5860
const result = style.trim().split(':')
5961
if (result.length !== 2) {
@@ -71,6 +73,8 @@ function parseStaticStyle (staticStyle: ?string, options: CompilerOptions): Stat
7173
if (styleList.length) {
7274
styleResult = '{' + styleList.join(',') + '}'
7375
}
76+
} else if (isPlainObject(staticStyle)) {
77+
styleResult = JSON.stringify(staticStyle) || ''
7478
}
7579
return { dynamic, styleResult }
7680
}

src/platforms/weex/util/parser.js

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/* @flow */
2+
3+
// import { warn } from 'core/util/index'
4+
5+
// this will be preserved during build
6+
// $flow-disable-line
7+
const acorn = require('acorn') // $flow-disable-line
8+
const walk = require('acorn/dist/walk') // $flow-disable-line
9+
const escodegen = require('escodegen')
10+
11+
export function nodeToBinding (node: Object): any {
12+
switch (node.type) {
13+
case 'Literal': return node.value
14+
case 'Identifier':
15+
case 'UnaryExpression':
16+
case 'BinaryExpression':
17+
case 'LogicalExpression':
18+
case 'ConditionalExpression':
19+
case 'MemberExpression': return { '@binding': escodegen.generate(node) }
20+
case 'ArrayExpression': return node.elements.map(_ => nodeToBinding(_))
21+
case 'ObjectExpression': {
22+
const object = {}
23+
node.properties.forEach(prop => {
24+
if (!prop.key || prop.key.type !== 'Identifier') {
25+
return
26+
}
27+
const key = escodegen.generate(prop.key)
28+
const value = nodeToBinding(prop.value)
29+
if (key && value) {
30+
object[key] = value
31+
}
32+
})
33+
return object
34+
}
35+
default: {
36+
// warn(`Not support ${node.type}: "${escodegen.generate(node)}"`)
37+
return ''
38+
}
39+
}
40+
}
41+
42+
export function generateBinding (exp: ?string): any {
43+
if (exp && typeof exp === 'string') {
44+
let ast = null
45+
try {
46+
ast = acorn.parse(`(${exp})`)
47+
} catch (e) {
48+
// warn(`Failed to parse the expression: "${exp}"`)
49+
return ''
50+
}
51+
52+
let output = ''
53+
walk.simple(ast, {
54+
Expression (node) {
55+
output = nodeToBinding(node)
56+
}
57+
})
58+
return output
59+
}
60+
}

test/weex/cases/cases.spec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ describe('Usage', () => {
6464
it('text node', createRenderTestCase('recycle-list/text-node'))
6565
it('attributes', createRenderTestCase('recycle-list/attrs'))
6666
// it('class name', createRenderTestCase('recycle-list/classname'))
67-
// it('inline style', createRenderTestCase('recycle-list/inline-style'))
67+
it('inline style', createRenderTestCase('recycle-list/inline-style'))
6868
it('v-if', createRenderTestCase('recycle-list/v-if'))
6969
it('v-else', createRenderTestCase('recycle-list/v-else'))
7070
it('v-else-if', createRenderTestCase('recycle-list/v-else-if'))

test/weex/compiler/parser.spec.js

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { generateBinding } from '../../../src/platforms/weex/util/parser'
2+
3+
describe('expression parser', () => {
4+
describe('generateBinding', () => {
5+
it('primitive literal', () => {
6+
expect(generateBinding('15')).toEqual(15)
7+
expect(generateBinding('"xxx"')).toEqual('xxx')
8+
})
9+
10+
it('identifiers', () => {
11+
expect(generateBinding('x')).toEqual({ '@binding': 'x' })
12+
expect(generateBinding('x.y')).toEqual({ '@binding': 'x.y' })
13+
expect(generateBinding(`x.y['z']`)).toEqual({ '@binding': `x.y['z']` })
14+
})
15+
16+
it('object literal', () => {
17+
expect(generateBinding('{}')).toEqual({})
18+
expect(generateBinding('{ abc: 25 }')).toEqual({ abc: 25 })
19+
expect(generateBinding('{ abc: 25, def: "xxx" }')).toEqual({ abc: 25, def: 'xxx' })
20+
expect(generateBinding('{ a: 3, b: { bb: "bb", bbb: { bbc: "BBC" } } }'))
21+
.toEqual({ a: 3, b: { bb: 'bb', bbb: { bbc: 'BBC' }}})
22+
})
23+
24+
it('array literal', () => {
25+
expect(generateBinding('[]')).toEqual([])
26+
expect(generateBinding('[{ abc: 25 }]')).toEqual([{ abc: 25 }])
27+
expect(generateBinding('[{ abc: 25, def: ["xxx"] }]')).toEqual([{ abc: 25, def: ['xxx'] }])
28+
expect(generateBinding('{ a: [3,16], b: [{ bb: ["aa","bb"], bbb: [{bbc:"BBC"}] }] }'))
29+
.toEqual({ a: [3, 16], b: [{ bb: ['aa', 'bb'], bbb: [{ bbc: 'BBC' }] }] })
30+
})
31+
32+
it('expressions', () => {
33+
expect(generateBinding(`3 + 5`)).toEqual({ '@binding': `3 + 5` })
34+
expect(generateBinding(`'x' + 2`)).toEqual({ '@binding': `'x' + 2` })
35+
expect(generateBinding(`\`xx\` + 2`)).toEqual({ '@binding': `\`xx\` + 2` })
36+
expect(generateBinding(`item.size * 23 + 'px'`)).toEqual({ '@binding': `item.size * 23 + 'px'` })
37+
})
38+
39+
it('object bindings', () => {
40+
expect(generateBinding(`{ color: textColor }`)).toEqual({
41+
color: { '@binding': 'textColor' }
42+
})
43+
expect(generateBinding(`{ color: '#FF' + 66 * 100, fontSize: item.size }`)).toEqual({
44+
color: { '@binding': `'#FF' + 66 * 100` },
45+
fontSize: { '@binding': 'item.size' }
46+
})
47+
expect(generateBinding(`{
48+
x: { xx: obj, xy: -2 + 5 },
49+
y: {
50+
yy: { yyy: obj.y || yy },
51+
yz: typeof object.yz === 'string' ? object.yz : ''
52+
}
53+
}`)).toEqual({
54+
x: { xx: { '@binding': 'obj' }, xy: { '@binding': '-2 + 5' }},
55+
y: {
56+
yy: { yyy: { '@binding': 'obj.y || yy' }},
57+
yz: { '@binding': `typeof object.yz === 'string' ? object.yz : ''` }
58+
}
59+
})
60+
})
61+
62+
it('array bindings', () => {
63+
expect(generateBinding(`[textColor, 3 + 5, 'string']`)).toEqual([
64+
{ '@binding': 'textColor' },
65+
{ '@binding': '3 + 5' },
66+
'string'
67+
])
68+
expect(generateBinding(`[
69+
{ color: '#FF' + 66 * -100 },
70+
item && item.style,
71+
{ fontSize: item.size | 0 }
72+
]`)).toEqual([
73+
{ color: { '@binding': `'#FF' + 66 * -100` }},
74+
{ '@binding': 'item && item.style' },
75+
{ fontSize: { '@binding': 'item.size | 0' }}
76+
])
77+
expect(generateBinding(`[{
78+
x: [{ xx: [fn instanceof Function ? 'function' : '' , 25] }],
79+
y: {
80+
yy: [{ yyy: [obj.yy.y, obj.y.yy] }],
81+
yz: [object.yz, void 0]
82+
}
83+
}]`)).toEqual([{
84+
x: [{ xx: [{ '@binding': `fn instanceof Function ? 'function' : ''` }, 25] }],
85+
y: {
86+
yy: [{ yyy: [{ '@binding': 'obj.yy.y' }, { '@binding': 'obj.y.yy' }] }],
87+
yz: [{ '@binding': 'object.yz' }, { '@binding': 'void 0' }]
88+
}
89+
}])
90+
})
91+
92+
it('unsupported bindings', () => {
93+
expect(generateBinding('() => {}')).toEqual('')
94+
expect(generateBinding('function(){}')).toEqual('')
95+
expect(generateBinding('(function(){})()')).toEqual('')
96+
expect(generateBinding('var abc = 35')).toEqual('')
97+
expect(generateBinding('abc++')).toEqual('')
98+
expect(generateBinding('x.y(0)')).toEqual('')
99+
expect(generateBinding('class X {}')).toEqual('')
100+
expect(generateBinding('if (typeof x == null) { 35 }')).toEqual('')
101+
expect(generateBinding('while (x == null)')).toEqual('')
102+
expect(generateBinding('new Function()')).toEqual('')
103+
})
104+
})
105+
})

0 commit comments

Comments
 (0)