@dolphinweex/dof-weex-vue-precompiler
Version:
a precompiler for weex-vue-render.
231 lines (213 loc) • 6.91 kB
JavaScript
/**
* @fileOverview to wrap object leterals with _px2rem()
*/
const esprima = require('esprima-next')
const escodegen = require('escodegen')
const bindingStyleNamesForPx2Rem = require('../config').bindingStyleNamesForPx2Rem
const issues = 'https://github.com/weexteam/weex-vue-precompiler/issues'
const { ast } = require('../util')
const { getCompiler } = require('../components')
const { getTransformer } = require('wxv-transformer')
function transformArray (ast, tagName, rootValue) {
const elements = ast.elements
for (let i = 0, l = elements.length; i < l; i++) {
const element = elements[i]
const result = transformNode(element, tagName, rootValue)
if (result) {
elements[i] = result
}
}
return ast
}
/**
* transform ConditionalExpressions. e.g.:
* :style="a ? b : c" => :style="_px2rem(a, rootValue) ? _px2rem(b, rootValue) : _px2rem(c, rootValue)"
* @param {ConditionalExpression} ast
*/
function transformConditional (ast, tagName, rootValue, propKeyAST) {
ast.consequent = transformNode(ast.consequent, tagName, rootValue, propKeyAST)
ast.alternate = transformNode(ast.alternate, tagName, rootValue, propKeyAST)
return ast
}
/**
* transform :style="{width:w}" => :style="{width:_px2rem(w, rootValue)}"
* This kind of style binding with object literal is a good practice.
* @param {ObjectExpression} ast
*/
function transformObject (ast, tagName, rootValue) {
const compiler = getCompiler(tagName)
if (compiler) {
return compiler.compile(ast, bindingStyleNamesForPx2Rem, rootValue, transformNode)
}
const properties = ast.properties
for (let i = 0, l = properties.length; i < l; i++) {
const prop = properties[i]
const keyNode = prop.key
const keyType = keyNode.type
const key = keyType === 'Literal' ? keyNode.value : keyNode.name
if (bindingStyleNamesForPx2Rem.indexOf(key) > -1) {
prop.value = transformNode(prop.value, tagName, rootValue, true/*asPropValue*/, prop.key)
}
}
return ast
}
function transformLiteral (...args) {
// not to transform literal string directly since we don't know
// if we could use 0.5px to support hairline unless in runtime.
return transformAsWhole(...args)
}
/**
* type = 'Identifier'
* @param {Identifier} ast
*/
function transformIdentifier (...args) {
return transformAsWhole(...args)
}
/**
* transform MemberExpression like :styles="myData.styles"
*/
function transformMember (...args) {
return transformAsWhole(...args)
}
/**
* transform CallExpression like :stylles="getMyStyles()"
*/
function transformCall (ast, ...args) {
const name = ast.callee.name
if (name && name.match(/_processExclusiveStyle|_px2rem/)) {
return ast // already transformed.
}
return transformAsWhole(ast, ...args)
}
/**
* transform a value object for a property in a object expression.
* @param {Literal || Identifier} ast a value expression in object literal.
*/
function transformAsValue (ast, tagName, rootValue, propKeyAST) {
if (propKeyAST && propKeyAST.type) {
var propKey = { type: 'Literal', value: propKeyAST.type === 'Literal' ? propKeyAST.value : propKeyAST.name}
return {
type: 'CallExpression',
callee: {
type: 'Identifier',
name: '_px2rem'
},
arguments: [ast, { type: 'Literal', value: rootValue }, propKey]
}
}else{
return {
type: 'CallExpression',
callee: {
type: 'Identifier',
name: '_px2rem'
},
arguments: [ast, { type: 'Literal', value: rootValue }]
}
}
}
/**
* transform :style="expression" => :style="_px2rem(expression, opts)" directly
* wrapping with _px2rem or _processExclusiveStyle function.
* //////////////////////
* support node type:
* - MemberExpression
* - Identifier
* - CallExpression
* //////////////////////
* not support:
* - ObjectExpression
* - ConditionalExpression
* - ArrayExpression
*/
function transformAsWhole (ast, tagName, rootValue) {
let callName = '_px2rem'
const args = [ast, { type: 'Literal', value: rootValue }]
const transformer = getTransformer(tagName)
if (transformer) {
// special treatment for exclusive styles, such as text-lines
callName = '_processExclusiveStyle'
args.push({
type: 'Literal',
value: tagName,
})
}
return {
type: 'CallExpression',
callee: {
type: 'Identifier',
name: callName
},
arguments: args
}
}
/**
* @param {boolean} asPropValue: whether this ast node is a value node for a style
* object. If it is, we shouldn't use _processExclusiveStyle.
*/
function transformNode (ast, tagName, rootValue, asPropValue, propKeyAST) {
if (asPropValue) {
return transformAsValue(ast, tagName, rootValue, propKeyAST)
}
const type = ast.type
switch (type) {
// not as whole types.
case 'ArrayExpression':
return transformArray(ast, tagName, rootValue)
case 'ConditionalExpression':
return transformConditional(ast, tagName, rootValue, propKeyAST)
case 'ObjectExpression': {
// 判断是否包含 SpreadElement ,如果包含则把整个ast当作一个对象传进 px2rem()中
const hasSpreadElement = ast.properties.find(prop=>"SpreadElement" == prop.type);
if (hasSpreadElement) {
return transformAsWhole(ast, tagName, rootValue, propKeyAST)
}
return transformObject(ast, tagName, rootValue)
}
// as whole types.
case 'Identifier':
return transformIdentifier(ast, tagName, rootValue, propKeyAST)
case 'CallExpression':
return transformCall(ast, tagName, rootValue, propKeyAST)
case 'MemberExpression':
return transformMember(ast, tagName, rootValue, propKeyAST)
case 'Literal':
return transformLiteral(ast, tagName, rootValue, propKeyAST)
case 'LogicalExpression':
return transformAsWhole(ast, tagName, rootValue, propKeyAST)
default: {
console.warn('[weex-vue-precompiler]: current expression not in transform lists:', type)
console.log('[weex-vue-precomiler]: current ast node:', ast)
return transformAsWhole(ast, tagName, rootValue, propKeyAST)
}
}
}
function styleBindingHook (
el,
attrsMap,
attrsList,
attrs,
staticClass
) {
try {
const styleBinding = el.styleBinding
if (!styleBinding) {
return
}
const parsedAst = ast.parseAst(styleBinding.trim())
const { rootValue } = this.config.px2rem
const transformedAst = transformNode(parsedAst, el._origTag || el.tag, rootValue)
const res = escodegen.generate(transformedAst, {
format: {
indent: {
style: ' '
},
newline: '',
}
})
el.styleBinding = res
} catch (err) {
console.log(`[weex-vue-precompiler] ooops! There\'s a err, please paste this error`
+ `stack to the repo's issue list: ${issues}`, err)
}
}
module.exports = styleBindingHook