Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
DCloud
uni-app
提交
0de82f65
U
uni-app
项目概览
DCloud
/
uni-app
3 个月 前同步成功
通知
725
Star
38705
Fork
3642
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
7
列表
看板
标记
里程碑
合并请求
1
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
U
uni-app
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
7
Issue
7
列表
看板
标记
里程碑
合并请求
1
合并请求
1
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
0de82f65
编写于
10月 13, 2021
作者:
fxy060608
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
wip(mp): v-bind:class object syntax
上级
f209ff01
变更
6
隐藏空白更改
内联
并排
Showing
6 changed file
with
339 addition
and
28 deletion
+339
-28
packages/uni-mp-compiler/__tests__/class.spec.ts
packages/uni-mp-compiler/__tests__/class.spec.ts
+60
-0
packages/uni-mp-compiler/__tests__/test.spec.ts
packages/uni-mp-compiler/__tests__/test.spec.ts
+3
-3
packages/uni-mp-compiler/src/ast.ts
packages/uni-mp-compiler/src/ast.ts
+70
-0
packages/uni-mp-compiler/src/codegen.ts
packages/uni-mp-compiler/src/codegen.ts
+11
-5
packages/uni-mp-compiler/src/transforms/transformElement.ts
packages/uni-mp-compiler/src/transforms/transformElement.ts
+72
-2
packages/uni-mp-compiler/src/transforms/transformIdentifier.ts
...ges/uni-mp-compiler/src/transforms/transformIdentifier.ts
+123
-18
未找到文件。
packages/uni-mp-compiler/__tests__/class.spec.ts
0 → 100644
浏览文件 @
0de82f65
import
{
assert
}
from
'
./testUtils
'
describe
(
'
compiler: transform class
'
,
()
=>
{
test
(
`static class`
,
()
=>
{
assert
(
`<view class="foo"/>`
,
`<view class="foo"/>`
,
`(_ctx, _cache) => {
return {}
}`
)
assert
(
`<view class="foo bar"/>`
,
`<view class="foo bar"/>`
,
`(_ctx, _cache) => {
return {}
}`
)
assert
(
`<view class="foo bar"/>`
,
`<view class="foo bar"/>`
,
`(_ctx, _cache) => {
return {}
}`
)
})
test
(
'
v-bind:class basic
'
,
()
=>
{
assert
(
`<view :class="foo"/>`
,
`<view class="{{a}}"/>`
,
`(_ctx, _cache) => {
return { a: _ctx.foo }
}`
)
assert
(
`<view :class="foo | bar"/>`
,
`<view class="{{a}}"/>`
,
`(_ctx, _cache) => {
return { a: _ctx.foo | _ctx.bar }
}`
)
})
test
(
'
v-bind:class object syntax
'
,
()
=>
{
assert
(
`<view :class="{ red: isRed }"/>`
,
`<view class="{{[a && 'red']}}"/>`
,
`(_ctx, _cache) => {
return { a: _ctx.isRed }
}`
)
assert
(
`<view :class="{ a: 1, b: 0, c: true, d: false, e: null, f: undefined, g: ok, h: handle(ok), i: ok>1 }"/>`
,
`<view class="{{['a', 'c', a && 'g', b && 'h', c && 'i']}}"/>`
,
`(_ctx, _cache) => {
return { a: _ctx.ok, b: _ctx.handle(_ctx.ok), c: _ctx.ok > 1 }
}`
)
})
test
(
'
v-bind:class array syntax
'
,
()
=>
{})
})
packages/uni-mp-compiler/__tests__/test.spec.ts
浏览文件 @
0de82f65
...
...
@@ -33,10 +33,10 @@ function assert(
describe
(
'
compiler
'
,
()
=>
{
test
(
'
should wrap as function if expression is inline statement
'
,
()
=>
{
assert
(
`<
div v-on:click.prevent
/>`
,
`<view
id="{{id
}}"/>`
,
`<
view :class="{ a: 1, b: 0, c: true, d: false, e: null, f: undefined, g: ok, h: handle(ok), i: ok>1 }"
/>`
,
`<view
class="{{['a', 'c', a && 'g', b && 'h', c && 'i' ]
}}"/>`
,
`(_ctx, _cache) => {
return { a: _
vOn(() => {})
}
return { a: _
ctx.ok, b: _ctx.handle(_ctx.ok), c: _ctx.ok > 1
}
}`
,
{}
)
...
...
packages/uni-mp-compiler/src/ast.ts
浏览文件 @
0de82f65
...
...
@@ -23,6 +23,18 @@ import {
Pattern
,
RestElement
,
ArrowFunctionExpression
,
logicalExpression
,
stringLiteral
,
StringLiteral
,
isIdentifier
,
isStringLiteral
,
isLiteral
,
isBooleanLiteral
,
isBigIntLiteral
,
isDecimalLiteral
,
Literal
,
LogicalExpression
,
isNullLiteral
,
}
from
'
@babel/types
'
import
{
createCompilerError
,
...
...
@@ -154,3 +166,61 @@ function createVForArrowFunctionExpression({
blockStatement
([
returnStatement
(
objectExpression
(
properties
))])
)
}
export
function
createClassBindingArrayExpression
(
expr
:
ObjectExpression
)
{
const
elements
:
(
LogicalExpression
|
StringLiteral
)[]
=
[]
expr
.
properties
.
forEach
((
prop
)
=>
{
const
{
value
}
=
prop
as
ObjectProperty
if
(
isUndefined
(
value
as
Expression
))
{
// remove {a:undefined}
return
}
if
(
isLiteral
(
value
))
{
// {a:true,b:1,c:0} => ['a','b']
if
(
isTrueExpr
(
value
))
{
elements
.
push
(
parseStringLiteral
((
prop
as
ObjectProperty
).
key
))
}
return
}
elements
.
push
(
logicalExpression
(
'
&&
'
,
value
as
Expression
,
parseStringLiteral
((
prop
as
ObjectProperty
).
key
)
)
)
})
return
arrayExpression
(
elements
)
}
export
function
isUndefined
(
expr
:
Expression
)
{
return
isIdentifier
(
expr
)
&&
expr
.
name
===
'
undefined
'
}
function
isTrueExpr
(
expr
:
Literal
)
{
if
(
isNullLiteral
(
expr
))
{
return
false
}
if
(
isStringLiteral
(
expr
)
||
isNumericLiteral
(
expr
)
||
isBooleanLiteral
(
expr
)
||
isBigIntLiteral
(
expr
)
||
isDecimalLiteral
(
expr
)
)
{
return
!!
expr
.
value
}
return
true
}
function
parseStringLiteral
(
expr
:
Expression
|
Identifier
|
StringLiteral
|
NumericLiteral
)
{
if
(
isIdentifier
(
expr
))
{
return
stringLiteral
(
expr
.
name
)
}
if
(
isStringLiteral
(
expr
))
{
return
stringLiteral
(
expr
.
value
)
}
return
stringLiteral
(
''
)
}
packages/uni-mp-compiler/src/codegen.ts
浏览文件 @
0de82f65
...
...
@@ -13,6 +13,7 @@ import {
import
{
default
as
babelGenerate
}
from
'
@babel/generator
'
import
{
CodegenOptions
,
CodegenScope
}
from
'
./options
'
import
{
createObjectExpression
}
from
'
./ast
'
import
{
Expression
}
from
'
@babel/types
'
interface
CodegenContext
extends
CodegenOptions
{
code
:
string
...
...
@@ -81,11 +82,7 @@ export function generate(
}
push
(
`return `
)
push
(
babelGenerate
(
createObjectExpression
(
scope
.
properties
),
{
concise
:
true
,
}).
code
)
push
(
genBabelExpr
(
createObjectExpression
(
scope
.
properties
)))
if
(
useWithBlock
)
{
deindent
()
push
(
`}`
)
...
...
@@ -208,6 +205,15 @@ function createGenNodeContext() {
return
context
}
export
function
genBabelExpr
(
expr
:
Expression
)
{
return
babelGenerate
(
expr
,
{
concise
:
true
,
jsescOption
:
{
quotes
:
'
single
'
,
},
}).
code
}
export
function
genExpr
(
node
:
CodegenNode
|
symbol
|
string
,
context
?:
GenNodeContext
...
...
packages/uni-mp-compiler/src/transforms/transformElement.ts
浏览文件 @
0de82f65
...
...
@@ -106,13 +106,83 @@ function processProps(node: ElementNode, context: TransformContext) {
const
directiveTransform
=
context
.
directiveTransforms
[
name
]
if
(
directiveTransform
)
{
const
{
props
}
=
directiveTransform
(
prop
,
node
,
context
)
prop
.
exp
=
props
[
0
].
value
as
ExpressionNode
prop
.
exp
=
directiveTransform
(
prop
,
node
,
context
).
props
[
0
]
.
value
as
ExpressionNode
// const { arg } = prop
// if (arg && arg.type === NodeTypes.SIMPLE_EXPRESSION && prop.exp) {
// const { content } = arg
// if (content === 'class') {
// hasClassBinding = true
// processClass(prop, props, context)
// } else if (content === 'style') {
// hasStyleBinding = true
// processStyle(prop, props, context)
// }
// }
}
}
}
// remove static class and static style
// if (hasClassBinding) {
// const staticClassPropIndex = findStaticClassIndex(props)
// if (staticClassPropIndex > -1) {
// props.splice(staticClassPropIndex, 1)
// }
// }
// if (hasStyleBinding) {
// const staticStylePropIndex = findStaticStyleIndex(props)
// if (staticStylePropIndex > -1) {
// props.splice(staticStylePropIndex, 1)
// }
// }
}
function
isComponentTag
(
tag
:
string
)
{
return
tag
[
0
].
toLowerCase
()
+
tag
.
slice
(
1
)
===
'
component
'
}
// function findStaticClassIndex(props: (AttributeNode | DirectiveNode)[]) {
// return props.findIndex((prop) => prop.name === 'class')
// }
// function findStaticStyleIndex(props: (AttributeNode | DirectiveNode)[]) {
// return props.findIndex((prop) => prop.name === 'style')
// }
// function processClass(
// classBindingProp: DirectiveNode,
// props: (AttributeNode | DirectiveNode)[],
// context: TransformContext
// ) {
// if (!classBindingProp.exp) {
// return
// }
// const staticClassPropIndex = findStaticClassIndex(props)
// const staticClass =
// staticClassPropIndex > -1
// ? (props[staticClassPropIndex] as AttributeNode).value
// : ''
// const expr = parseExpr(classBindingProp.exp, context)
// if (!expr) {
// return
// }
// console.log(staticClass)
// if (isObjectExpression(expr)) {
// classBindingProp.exp = createSimpleExpression(
// genBabelExpr(createClassBindingArrayExpression(expr))
// )
// }
// }
// function processStyle(
// styleBindingPropprop: DirectiveNode,
// props: (AttributeNode | DirectiveNode)[],
// context: TransformContext
// ) {
// const staticStylePropIndex = findStaticStyleIndex(props)
// const staticStyle =
// staticStylePropIndex > -1
// ? (props[staticStylePropIndex] as AttributeNode).value
// : ''
// if (staticStyle) {
// console.log(staticStyle)
// }
// }
packages/uni-mp-compiler/src/transforms/transformIdentifier.ts
浏览文件 @
0de82f65
import
{
BaseNode
}
from
'
estree
'
import
{
walk
}
from
'
estree-walker
'
import
{
Expression
,
isIdentifier
,
isReferenced
}
from
'
@babel/types
'
import
{
Expression
,
isIdentifier
,
isLiteral
,
isObjectExpression
,
isReferenced
,
ObjectExpression
,
ObjectProperty
,
stringLiteral
,
}
from
'
@babel/types
'
import
{
AttributeNode
,
createCompoundExpression
,
createSimpleExpression
,
DirectiveNode
,
ExpressionNode
,
NodeTypes
,
SimpleExpressionNode
,
SourceLocation
,
TO_DISPLAY_STRING
,
}
from
'
@vue/compiler-core
'
import
{
createObjectProperty
,
parseExpr
}
from
'
../ast
'
import
{
genExpr
}
from
'
../codegen
'
import
{
createClassBindingArrayExpression
,
createObjectProperty
,
isUndefined
,
parseExpr
,
}
from
'
../ast
'
import
{
genBabelExpr
,
genExpr
}
from
'
../codegen
'
import
{
CodegenScope
,
CodegenVForScope
}
from
'
../options
'
import
{
isRootScope
,
...
...
@@ -19,7 +36,7 @@ import {
NodeTransform
,
TransformContext
,
}
from
'
../transform
'
import
{
isForElementNode
}
from
'
./vFor
'
import
{
ForElementNode
,
isForElementNode
}
from
'
./vFor
'
export
const
transformIdentifier
:
NodeTransform
=
(
node
,
context
)
=>
{
return
()
=>
{
...
...
@@ -34,32 +51,28 @@ export const transformIdentifier: NodeTransform = (node, context) => {
)
}
else
if
(
node
.
type
===
NodeTypes
.
ELEMENT
)
{
const
vFor
=
isForElementNode
(
node
)
&&
node
.
vFor
for
(
let
i
=
0
;
i
<
node
.
props
.
length
;
i
++
)
{
const
dir
=
node
.
props
[
i
]
const
{
props
}
=
node
for
(
let
i
=
0
;
i
<
props
.
length
;
i
++
)
{
const
dir
=
props
[
i
]
if
(
dir
.
type
===
NodeTypes
.
DIRECTIVE
)
{
const
arg
=
dir
.
arg
if
(
arg
)
{
// TODO 指令暂不不支持动态参数,v-bind:[arg] v-on:[event]
if
(
!
(
arg
.
type
===
NodeTypes
.
SIMPLE_EXPRESSION
&&
arg
.
isStatic
))
{
node
.
props
.
splice
(
i
,
1
)
props
.
splice
(
i
,
1
)
i
--
continue
}
}
const
exp
=
dir
.
exp
if
(
exp
)
{
if
(
vFor
&&
arg
&&
arg
.
type
===
NodeTypes
.
SIMPLE_EXPRESSION
&&
arg
.
content
===
'
key
'
&&
exp
.
type
===
NodeTypes
.
SIMPLE_EXPRESSION
&&
exp
.
content
===
vFor
.
valueAlias
)
{
exp
.
content
=
'
*this
'
continue
if
(
isSelfKey
(
dir
,
vFor
))
{
rewriteSelfKey
(
dir
)
}
else
if
(
isClassBinding
(
dir
))
{
rewriteClass
(
i
,
dir
,
props
,
context
)
}
else
{
dir
.
exp
=
rewriteExpression
(
exp
,
context
)
}
dir
.
exp
=
rewriteExpression
(
exp
,
context
)
}
}
}
...
...
@@ -67,6 +80,95 @@ export const transformIdentifier: NodeTransform = (node, context) => {
}
}
function
isSelfKey
(
{
arg
,
exp
}:
DirectiveNode
,
vFor
:
ForElementNode
[
'
vFor
'
]
|
false
)
{
return
(
vFor
&&
arg
&&
exp
&&
arg
.
type
===
NodeTypes
.
SIMPLE_EXPRESSION
&&
arg
.
content
===
'
key
'
&&
exp
.
type
===
NodeTypes
.
SIMPLE_EXPRESSION
&&
exp
.
content
===
vFor
.
valueAlias
)
}
function
rewriteSelfKey
(
dir
:
DirectiveNode
)
{
;(
dir
.
exp
as
SimpleExpressionNode
).
content
=
'
*this
'
}
function
isClassBinding
({
arg
,
exp
}:
DirectiveNode
)
{
return
(
arg
&&
arg
.
type
===
NodeTypes
.
SIMPLE_EXPRESSION
&&
arg
.
content
===
'
class
'
)
}
function
findStaticClassIndex
(
props
:
(
AttributeNode
|
DirectiveNode
)[])
{
return
props
.
findIndex
((
prop
)
=>
prop
.
name
===
'
class
'
)
}
function
rewriteClass
(
index
:
number
,
classBindingProp
:
DirectiveNode
,
props
:
(
AttributeNode
|
DirectiveNode
)[],
context
:
TransformContext
)
{
if
(
!
classBindingProp
.
exp
)
{
return
}
const
staticClassPropIndex
=
findStaticClassIndex
(
props
)
const
staticClass
=
staticClassPropIndex
>
-
1
?
(
props
[
staticClassPropIndex
]
as
AttributeNode
).
value
!
.
content
:
''
const
expr
=
parseExpr
(
classBindingProp
.
exp
,
context
)
if
(
!
expr
)
{
return
}
if
(
isObjectExpression
(
expr
))
{
// 重写{ key:value }所有的 value
rewriteObjectExpression
(
expr
,
classBindingProp
.
loc
,
context
)
const
arrExpr
=
createClassBindingArrayExpression
(
expr
)
if
(
staticClass
)
{
if
(
index
>
staticClassPropIndex
)
{
arrExpr
.
elements
.
unshift
(
stringLiteral
(
staticClass
))
}
else
{
arrExpr
.
elements
.
push
(
stringLiteral
(
staticClass
))
}
}
classBindingProp
.
exp
=
createSimpleExpression
(
genBabelExpr
(
arrExpr
))
}
else
{
classBindingProp
.
exp
=
rewriteExpression
(
classBindingProp
.
exp
,
context
)
}
}
function
rewriteObjectExpression
(
expr
:
ObjectExpression
,
loc
:
SourceLocation
,
context
:
TransformContext
)
{
expr
.
properties
.
forEach
((
prop
)
=>
{
const
{
value
}
=
prop
as
ObjectProperty
if
(
isLiteral
(
value
))
{
return
}
else
{
const
newExpr
=
parseExpr
(
rewriteExpression
(
createSimpleExpression
(
genBabelExpr
(
value
as
Expression
),
false
,
loc
),
context
,
value
as
Expression
),
context
)
if
(
newExpr
)
{
;(
prop
as
ObjectProperty
).
value
=
newExpr
}
}
})
}
export
function
rewriteExpression
(
node
:
ExpressionNode
,
context
:
TransformContext
,
...
...
@@ -83,6 +185,9 @@ export function rewriteExpression(
return
createSimpleExpression
(
code
)
}
}
if
(
isUndefined
(
babelNode
))
{
return
createSimpleExpression
(
'
undefined
'
,
false
,
node
.
loc
)
}
scope
=
findScope
(
babelNode
,
scope
)
!
const
id
=
scope
.
id
.
next
()
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录