提交 74151356 编写于 作者: Q qiang

Merge branch 'dev' into alpha

......@@ -14,6 +14,9 @@
"uni-core/*": [
"./src/core/*"
],
"uni-wrapper/*": [
"./src/core/runtime/wrapper/*"
],
"@dcloudio/*": [
"./packages/*"
]
......
......@@ -204,6 +204,41 @@ describe('mp:compiler-mp-alipay', () => {
)
})
it('generate attrs with mergeVirtualHostAttributes', () => {
assertCodegen(
'<custom-view>hello world</custom-view>',
'<custom-view vue-id="551070e6-1" onVueInit="__l" virtualHostStyle="{{virtualHostStyle}}" virtualHostClass="{{(virtualHostClass)}}">hello world</custom-view>',
'with(this){}',
{
mergeVirtualHostAttributes: true
}
)
assertCodegen(
'<custom-view :class="class1" :style="style">hello world</custom-view>',
'<custom-view vue-id="551070e6-1" onVueInit="__l" virtualHostStyle="{{(style)+virtualHostStyle}}" virtualHostClass="{{((class1)+\' \'+virtualHostClass)}}">hello world</custom-view>',
'with(this){}',
{
mergeVirtualHostAttributes: true
}
)
assertCodegen(
'<view><custom-view>hello world</custom-view></view>',
'<view class="{{(virtualHostClass)}}" style="{{virtualHostStyle}}"><custom-view vue-id="551070e6-1" onVueInit="__l">hello world</custom-view></view>',
'with(this){}',
{
mergeVirtualHostAttributes: true
}
)
assertCodegen(
'<view><custom-view :class="class1" :style="style">hello world</custom-view></view>',
'<view class="{{(virtualHostClass)}}" style="{{virtualHostStyle}}"><custom-view vue-id="551070e6-1" onVueInit="__l" virtualHostStyle="{{(style)}}" virtualHostClass="{{(class1)}}">hello world</custom-view></view>',
'with(this){}',
{
mergeVirtualHostAttributes: true
}
)
})
it('generate getPhoneNumber', () => {
assertCodegen(
'<button open-type="getPhoneNumber" @getphonenumber="getPhoneNumber">获取手机号</button>',
......
const compiler = require('../lib')
function assertCodegen (template, templateCode, renderCode = 'with(this){}') {
function assertCodegen (template, templateCode, renderCode = 'with(this){}', options = {}) {
const res = compiler.compile(template, {
resourcePath: 'test.wxml',
mp: {
mp: Object.assign({
minified: true,
isTest: true,
platform: 'mp-weixin'
}
}, options)
})
expect(res.template).toBe(templateCode)
......@@ -253,6 +253,108 @@ describe('mp:compiler', () => {
// )
})
it('generate attrs with mergeVirtualHostAttributes', () => {
assertCodegen(
'<view>hello world</view>',
'<view class="{{[virtualHostClass]}}" style="{{virtualHostStyle}}">hello world</view>',
'with(this){}',
{
mergeVirtualHostAttributes: true
}
)
assertCodegen(
'<view><view>hello world</view></view>',
'<view class="{{[virtualHostClass]}}" style="{{virtualHostStyle}}"><view>hello world</view></view>',
'with(this){}',
{
mergeVirtualHostAttributes: true
}
)
assertCodegen(
'<custom-view>hello world</custom-view>',
'<custom-view vue-id="551070e6-1" class="{{[virtualHostClass]}}" style="{{virtualHostStyle}}" bind:__l="__l" virtualHostStyle="{{virtualHostStyle}}" virtualHostClass="{{[virtualHostClass]}}" vue-slots="{{[\'default\']}}">hello world</custom-view>',
'with(this){}',
{
mergeVirtualHostAttributes: true
}
)
assertCodegen(
'<custom-view :class="class1" :style="style">hello world</custom-view>',
'<custom-view class="{{[class1,virtualHostClass]}}" style="{{(style)+virtualHostStyle}}" vue-id="551070e6-1" bind:__l="__l" virtualHostStyle="{{(style)+virtualHostStyle}}" virtualHostClass="{{[class1,virtualHostClass]}}" vue-slots="{{[\'default\']}}">hello world</custom-view>',
'with(this){}',
{
mergeVirtualHostAttributes: true
}
)
assertCodegen(
'<view><custom-view>hello world</custom-view></view>',
'<view class="{{[virtualHostClass]}}" style="{{virtualHostStyle}}"><custom-view vue-id="551070e6-1" bind:__l="__l" vue-slots="{{[\'default\']}}">hello world</custom-view></view>',
'with(this){}',
{
mergeVirtualHostAttributes: true
}
)
assertCodegen(
'<view><custom-view :class="class1" :style="style">hello world</custom-view></view>',
'<view class="{{[virtualHostClass]}}" style="{{virtualHostStyle}}"><custom-view class="{{[class1]}}" style="{{(style)}}" vue-id="551070e6-1" bind:__l="__l" virtualHostStyle="{{(style)}}" virtualHostClass="{{[class1]}}" vue-slots="{{[\'default\']}}">hello world</custom-view></view>',
'with(this){}',
{
mergeVirtualHostAttributes: true
}
)
})
it('generate class with mergeVirtualHostAttributes', () => {
assertCodegen(
'<view class="a">hello world</view>',
'<view class="{{[\'a\',virtualHostClass]}}" style="{{virtualHostStyle}}">hello world</view>',
'with(this){}',
{
mergeVirtualHostAttributes: true
}
)
assertCodegen(
'<view :class="class1">hello world</view>',
'<view class="{{[class1,virtualHostClass]}}" style="{{virtualHostStyle}}">hello world</view>',
'with(this){}',
{
mergeVirtualHostAttributes: true
}
)
assertCodegen(
'<view :class="[class1,class2]">hello world</view>',
'<view class="{{[class1,class2,virtualHostClass]}}" style="{{virtualHostStyle}}">hello world</view>',
'with(this){}',
{
mergeVirtualHostAttributes: true
}
)
assertCodegen(
'<view :class="{class1,class2}">hello world</view>',
'<view class="{{[(class1)?\'class1\':\'\',(class2)?\'class2\':\'\',virtualHostClass]}}" style="{{virtualHostStyle}}">hello world</view>',
'with(this){}',
{
mergeVirtualHostAttributes: true
}
)
assertCodegen(
'<view class="a external-class c" :class="class1">hello world</view>',
'<view class="{{[\'a\',\'external-class\',\'c\',class1,virtualHostClass]}}" style="{{virtualHostStyle}}">hello world</view>',
'with(this){}',
{
mergeVirtualHostAttributes: true
}
)
assertCodegen(
'<view class="a external-class c" :class="class1"><view class="a external-class c" :class="class1">hello world</view></view>',
'<view class="{{[\'a\',\'external-class\',\'c\',class1,virtualHostClass]}}" style="{{virtualHostStyle}}"><view class="{{[\'a\',\'external-class\',\'c\',class1]}}">hello world</view></view>',
'with(this){}',
{
mergeVirtualHostAttributes: true
}
)
})
it('generate staticStyle', () => {
assertCodegen(
'<view style="height:400upx">hello world</view>',
......@@ -272,6 +374,33 @@ describe('mp:compiler', () => {
// )
})
it('generate style with mergeVirtualHostAttributes', () => {
assertCodegen(
'<view style="color:red">hello world</view>',
'<view class="{{[virtualHostClass]}}" style="{{\'color:red;\'+virtualHostStyle}}">hello world</view>',
'with(this){}',
{
mergeVirtualHostAttributes: true
}
)
assertCodegen(
'<view :style="style">hello world</view>',
'<view style="{{(style)+virtualHostStyle}}" class="{{[virtualHostClass]}}">hello world</view>',
'with(this){}',
{
mergeVirtualHostAttributes: true
}
)
assertCodegen(
'<view :style="{color}">hello world</view>',
'<view style="{{\'color:\'+(color)+\';\'+virtualHostStyle}}" class="{{[virtualHostClass]}}">hello world</view>',
'with(this){}',
{
mergeVirtualHostAttributes: true
}
)
})
it('generate v-show directive', () => {
assertCodegen(
'<view v-show="shown">hello world</view>',
......
......@@ -66,6 +66,8 @@ module.exports = {
ATTR_DATA_EVENT_PARAMS: 'data-event-params',
ATTR_DATA_EVENT_LIST: 'data-event-list',
ATTE_DATA_CUSTOM_HIDDEN: 'data-custom-hidden',
VIRTUAL_HOST_STYLE: 'virtualHostStyle',
VIRTUAL_HOST_CLASS: 'virtualHostClass',
INTERNAL_GET_ORIG,
INTERNAL_GET_CLASS,
INTERNAL_GET_STYLE,
......
......@@ -2,7 +2,12 @@ const t = require('@babel/types')
const uniI18n = require('@dcloudio/uni-cli-i18n')
const {
getCode
VIRTUAL_HOST_CLASS
} = require('../../../constants')
const {
getCode,
isRootElement
} = require('../../../util')
function processClassArrayExpressionElements (classArrayExpression) {
......@@ -76,45 +81,29 @@ function processClassArrayExpression (classValuePath) {
module.exports = function processClass (paths, path, state) {
const classPath = paths.class
const staticClassPath = paths.staticClass
const mergeVirtualHostAttributes = state.options.mergeVirtualHostAttributes
let classArrayExpression
if (classPath) {
const classValuePath = classPath.get('value')
if (classValuePath.isObjectExpression()) { // object
classValuePath.replaceWith(
processStaticClass(
processClassObjectExpression(classValuePath),
staticClassPath,
state
)
)
classArrayExpression = processClassObjectExpression(classValuePath)
} else if (classValuePath.isArrayExpression()) { // array
classValuePath.replaceWith(
processStaticClass(
processClassArrayExpression(classValuePath),
staticClassPath,
state
)
)
classArrayExpression = processClassArrayExpression(classValuePath)
} else if (
classValuePath.isStringLiteral() || // :class="'a'"
classValuePath.isIdentifier() || // TODO 需要优化到下一个条件,:class="classObject"
classValuePath.isMemberExpression() || // 需要优化到下一个条件,:class="item.classObject"
classValuePath.isConditionalExpression() ||
classValuePath.isLogicalExpression() ||
classValuePath.isBinaryExpression()
classValuePath.isIdentifier() || // TODO 需要优化到下一个条件,:class="classObject"
classValuePath.isMemberExpression() || // 需要优化到下一个条件,:class="item.classObject"
classValuePath.isConditionalExpression() ||
classValuePath.isLogicalExpression() ||
classValuePath.isBinaryExpression()
) {
// 理论上 ConditionalExpression,LogicalExpression 可能存在 classObject,应该__get_class,还是先不考虑这种情况吧
// ConditionalExpression :class="index === currentIndex ? activeStyle : itemStyle"
// BinaryExpression :class="'m-content-head-'+message.user"
classValuePath.replaceWith(
processStaticClass(
t.arrayExpression([classValuePath.node]),
staticClassPath,
state
)
)
classArrayExpression = t.arrayExpression([classValuePath.node])
} else if (
classValuePath.isIdentifier() ||
classValuePath.isMemberExpression()
classValuePath.isMemberExpression()
) { // classObject :class="classObject" :class="vm.classObject"
// TODO 目前先不考虑 classObject,styleObject
......@@ -135,5 +124,20 @@ module.exports = function processClass (paths, path, state) {
state.errors.add(':class' + uniI18n.__('templateCompiler.noSupportSyntax', { 0: getCode(classValuePath.node) }))
}
}
if (mergeVirtualHostAttributes && isRootElement(path.parentPath)) {
const virtualHostClass = t.identifier(VIRTUAL_HOST_CLASS)
if (classArrayExpression) {
classArrayExpression.elements.push(virtualHostClass)
} else {
classArrayExpression = t.arrayExpression([virtualHostClass])
const property = t.objectProperty(t.identifier('class'), processStaticClass(classArrayExpression, staticClassPath, state))
path.node.properties.push(property)
return []
}
}
if (classArrayExpression) {
const classValuePath = classPath.get('value')
classValuePath.replaceWith(processStaticClass(classArrayExpression, staticClassPath, state))
}
return []
}
......@@ -2,12 +2,14 @@ const t = require('@babel/types')
const {
IDENTIFIER_STYLE,
INTERNAL_GET_STYLE
INTERNAL_GET_STYLE,
VIRTUAL_HOST_STYLE
} = require('../../../constants')
const {
getCode,
hyphenate
hyphenate,
isRootElement
} = require('../../../util')
const getMemberExpr = require('../member-expr')
......@@ -120,6 +122,7 @@ function generateGetStyle (stylePath, styleValuePath, staticStylePath, state) {
module.exports = function processStyle (paths, path, state) {
const stylePath = paths.style
const staticStylePath = paths.staticStyle
const mergeVirtualHostAttributes = state.options.mergeVirtualHostAttributes
if (stylePath) {
const styleValuePath = stylePath.get('value')
if (styleValuePath.isObjectExpression()) {
......@@ -146,11 +149,11 @@ module.exports = function processStyle (paths, path, state) {
}
} else if (
styleValuePath.isStringLiteral() || // :style="'background:red'"
styleValuePath.isIdentifier() || // TODO 需要优化到下一个条件,:style="styleObject"
styleValuePath.isMemberExpression() || // TODO 需要优化到下一个条件,:style="item.styleObject"
styleValuePath.isConditionalExpression() ||
styleValuePath.isLogicalExpression() ||
styleValuePath.isBinaryExpression()
styleValuePath.isIdentifier() || // TODO 需要优化到下一个条件,:style="styleObject"
styleValuePath.isMemberExpression() || // TODO 需要优化到下一个条件,:style="item.styleObject"
styleValuePath.isConditionalExpression() ||
styleValuePath.isLogicalExpression() ||
styleValuePath.isBinaryExpression()
) {
// 理论上 ConditionalExpression,LogicalExpression 可能存在 styleObject,应该__get_style,还是先不考虑这种情况吧
// ConditionalExpression :style="index === currentIndex ? activeStyle : itemStyle"
......@@ -164,14 +167,28 @@ module.exports = function processStyle (paths, path, state) {
)
} else if (
styleValuePath.isIdentifier() ||
styleValuePath.isMemberExpression()
styleValuePath.isMemberExpression()
) { // TODO 目前先不考虑 classObject,styleObject
// generateGetStyle(stylePath, styleValuePath, staticStylePath, state)
} else {
state.errors.add(`:style 不支持 ${getCode(styleValuePath.node)} 语法`)
}
if (mergeVirtualHostAttributes && isRootElement(path.parentPath)) {
styleValuePath.replaceWith(t.binaryExpression('+', styleValuePath.node, t.identifier(VIRTUAL_HOST_STYLE)))
}
} else if (staticStylePath) {
if (mergeVirtualHostAttributes && isRootElement(path.parentPath)) {
const styleNode = processStaticStyle([t.identifier(VIRTUAL_HOST_STYLE)], staticStylePath, state)
const property = t.objectProperty(t.identifier('style'), styleNode)
path.node.properties.push(property)
return []
}
staticStylePath.get('value').replaceWith(getStaticStyleStringLiteral(staticStylePath, state))
} else {
if (mergeVirtualHostAttributes && isRootElement(path.parentPath)) {
const property = t.objectProperty(t.identifier('style'), t.identifier(VIRTUAL_HOST_STYLE))
path.node.properties.push(property)
}
}
return []
}
}
const t = require('@babel/types')
const babelTraverse = require('@babel/traverse').default
const {
INTERNAL_SET_MODEL
} = require('../../../constants')
const t = require('@babel/types')
const babelTraverse = require('@babel/traverse').default
const {
INTERNAL_SET_MODEL
} = require('../../../constants')
module.exports = {
getModelEventFunctionExpr (funcExpr, propPath, modifiers = []) {
let targetExpr
......@@ -44,4 +44,4 @@ module.exports = {
)
)
}
}
}
......@@ -24,7 +24,8 @@ const {
hyphenate,
traverseFilter,
getComponentName,
hasEscapeQuote
hasEscapeQuote,
isRootElement
} = require('../../util')
const traverseData = require('./data')
......@@ -182,7 +183,10 @@ module.exports = {
if (this.options.scopeId) {
addStaticClass(path, this.options.scopeId)
}
// 根节点无 attrs 时添加空对象,方便后续合并外层 attrs
if (this.options.mergeVirtualHostAttributes && !t.isObjectExpression(path.node.arguments[1]) && isRootElement(path)) {
path.node.arguments.splice(1, 0, t.objectExpression([]))
}
const dataPath = path.get('arguments.1')
dataPath && dataPath.isObjectExpression() && traverseData(dataPath, this, tagNode.value)
}
......
......@@ -4,12 +4,16 @@ const {
const {
SELF_CLOSING_TAGS,
INTERNAL_EVENT_LINK
INTERNAL_EVENT_LINK,
VIRTUAL_HOST_STYLE,
VIRTUAL_HOST_CLASS
} = require('../constants')
const uniI18n = require('@dcloudio/uni-cli-i18n')
function processElement (ast, state, isRoot) {
const platformName = state.options.platform.name
const platform = state.options.platform
const platformName = platform.name
const mergeVirtualHostAttributes = state.options.mergeVirtualHostAttributes
// <template slot="f"></template>
if (ast.type === 'template' && hasOwn(ast.attr, 'slot')) {
ast.type = 'view'
......@@ -36,13 +40,29 @@ function processElement (ast, state, isRoot) {
}]
delete ast.attr.innerHTML
}
if (state.options.platform.isComponent(ast.type)) {
if (platform.isComponent(ast.type)) {
if (platformName === 'mp-alipay') {
ast.attr.onVueInit = INTERNAL_EVENT_LINK
} else if (platformName !== 'mp-baidu') {
ast.attr['bind:' + INTERNAL_EVENT_LINK] = INTERNAL_EVENT_LINK
}
if (mergeVirtualHostAttributes && platform.isComponent(ast.type)) {
const obj = {
style: VIRTUAL_HOST_STYLE,
class: VIRTUAL_HOST_CLASS
}
Object.keys(obj).forEach(key => {
if (key in ast.attr) {
ast.attr[obj[key]] = ast.attr[key]
}
// 支付宝小程序自定义组件外部属性始终无效
if (platformName === 'mp-alipay') {
delete ast.attr[key]
}
})
}
const children = ast.children
// default slot
let defaultSlot = false
......
......@@ -5,7 +5,8 @@ const uniI18n = require('@dcloudio/uni-cli-i18n')
const {
METHOD_RENDER_LIST,
METHOD_RESOLVE_SCOPED_SLOTS
METHOD_RESOLVE_SCOPED_SLOTS,
METHOD_CREATE_ELEMENT
} = require('./constants')
function cached (fn) {
......@@ -270,6 +271,11 @@ function hasEscapeQuote (path) {
return has
}
function isRootElement (path) {
const result = path.findParent(path => (path.isCallExpression() && path.get('callee').isIdentifier({ name: METHOD_CREATE_ELEMENT })) || path.isReturnStatement())
return result.isReturnStatement()
}
module.exports = {
hasOwn,
isUnaryTag: makeMap(
......@@ -301,5 +307,6 @@ module.exports = {
processMemberExpression,
getForIndexIdentifier,
isSimpleObjectExpression,
hasEscapeQuote
hasEscapeQuote,
isRootElement
}
......@@ -319,6 +319,8 @@ if ((process.env.UNI_PLATFORM === 'mp-kuaishou' || process.env.UNI_PLATFORM ===
process.env.SCOPED_SLOTS_COMPILER = modes[2]
}
process.env.MERGE_VIRTUAL_HOST_ATTRIBUTES = (!!platformOptions.mergeVirtualHostAttributes).toString()
process.env.UNI_STAT_UNI_CLOUD = ''
process.env.UNI_STAT_DEBUG = ''
if (
......
......@@ -66,7 +66,8 @@ module.exports = function (content, map) {
Object.assign(vueLoaderOptions.options.compilerOptions, {
mp: {
platform: process.env.UNI_PLATFORM,
scopedSlotsCompiler: process.env.SCOPED_SLOTS_COMPILER
scopedSlotsCompiler: process.env.SCOPED_SLOTS_COMPILER,
mergeVirtualHostAttributes: process.env.MERGE_VIRTUAL_HOST_ATTRIBUTES === 'true'
},
filterModules,
filterTagName,
......
......@@ -136,7 +136,8 @@ const NON_APP_JSON_KEYS = [
'optimization',
'scopedSlotsCompiler',
'usingComponents',
'uniStatistics'
'uniStatistics',
'mergeVirtualHostAttributes'
]
module.exports = {
hasOwn,
......
......@@ -225,17 +225,31 @@ function parsePropType (key, type, defaultValue, file) {
return type
}
export function initProperties (props, isBehavior = false, file = '') {
export function initProperties (props, isBehavior = false, file = '', options) {
const properties = {}
if (!isBehavior) {
properties.vueId = {
type: String,
value: ''
}
// 用于字节跳动小程序模拟抽象节点
properties.generic = {
type: Object,
value: null
if (__PLATFORM__ === 'mp-toutiao' || __PLATFORM__ === 'mp-lark') {
// 用于字节跳动小程序模拟抽象节点
properties.generic = {
type: Object,
value: null
}
}
if (__PLATFORM__ === 'mp-weixin' || __PLATFORM__ === 'mp-alipay') {
if (__PLATFORM__ === 'mp-alipay' || options.virtualHost) {
properties.virtualHostStyle = {
type: null,
value: ''
}
properties.virtualHostClass = {
type: null,
value: ''
}
}
}
// scopedSlotsCompiler auto
properties.scopedSlotsCompiler = {
......
......@@ -501,7 +501,7 @@ export default {
top: 0;
left: 0;
overflow: hidden;
background-color: black;
background-color: transparent;
}
.uni-map-slot {
......
......@@ -38,7 +38,7 @@ export default function parseBaseComponent (vueComponentOptions, {
options,
data: initData(vueOptions, Vue.prototype),
behaviors: initBehaviors(vueOptions, initBehavior),
properties: initProperties(vueOptions.props, false, vueOptions.__file),
properties: initProperties(vueOptions.props, false, vueOptions.__file, options),
lifetimes: {
attached () {
const properties = this.properties
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册