diff --git a/packages/uni-mp-compiler/__tests__/class.spec.ts b/packages/uni-mp-compiler/__tests__/class.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fc2c9f033a81ff33ed7e15bae5803cf36dfbdad4
--- /dev/null
+++ b/packages/uni-mp-compiler/__tests__/class.spec.ts
@@ -0,0 +1,60 @@
+import { assert } from './testUtils'
+
+describe('compiler: transform class', () => {
+ test(`static class`, () => {
+ assert(
+ ``,
+ ``,
+ `(_ctx, _cache) => {
+ return {}
+}`
+ )
+ assert(
+ ``,
+ ``,
+ `(_ctx, _cache) => {
+ return {}
+}`
+ )
+ assert(
+ ``,
+ ``,
+ `(_ctx, _cache) => {
+ return {}
+}`
+ )
+ })
+ test('v-bind:class basic', () => {
+ assert(
+ ``,
+ ``,
+ `(_ctx, _cache) => {
+ return { a: _ctx.foo }
+}`
+ )
+ assert(
+ ``,
+ ``,
+ `(_ctx, _cache) => {
+ return { a: _ctx.foo | _ctx.bar }
+}`
+ )
+ })
+ test('v-bind:class object syntax', () => {
+ assert(
+ ``,
+ ``,
+ `(_ctx, _cache) => {
+ return { a: _ctx.isRed }
+}`
+ )
+ assert(
+ ``,
+ ``,
+ `(_ctx, _cache) => {
+ return { a: _ctx.ok, b: _ctx.handle(_ctx.ok), c: _ctx.ok > 1 }
+}`
+ )
+ })
+ test('v-bind:class array syntax', () => {})
+})
diff --git a/packages/uni-mp-compiler/__tests__/test.spec.ts b/packages/uni-mp-compiler/__tests__/test.spec.ts
index 894a21f5fe64d2cecf651fd8bd5f377cb35e487c..87e0bab9917a3f3f9fc0b1a4e6c002bf975266f1 100644
--- a/packages/uni-mp-compiler/__tests__/test.spec.ts
+++ b/packages/uni-mp-compiler/__tests__/test.spec.ts
@@ -33,10 +33,10 @@ function assert(
describe('compiler', () => {
test('should wrap as function if expression is inline statement', () => {
assert(
- `
`,
- ``,
+ ``,
+ ``,
`(_ctx, _cache) => {
- return { a: _vOn(() => {}) }
+ return { a: _ctx.ok, b: _ctx.handle(_ctx.ok), c: _ctx.ok > 1 }
}`,
{}
)
diff --git a/packages/uni-mp-compiler/src/ast.ts b/packages/uni-mp-compiler/src/ast.ts
index a724dbc4ca6e4e941b9493c056def5d5eb849976..59876eacccc99a81e6b039ee5728cfb6b2022795 100644
--- a/packages/uni-mp-compiler/src/ast.ts
+++ b/packages/uni-mp-compiler/src/ast.ts
@@ -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('')
+}
diff --git a/packages/uni-mp-compiler/src/codegen.ts b/packages/uni-mp-compiler/src/codegen.ts
index 1ca73d0db66a9750b569a65f20ac50fc2d5c7723..4c0d904d9dc476b922e94fc9d5d62d7af11483cd 100644
--- a/packages/uni-mp-compiler/src/codegen.ts
+++ b/packages/uni-mp-compiler/src/codegen.ts
@@ -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
diff --git a/packages/uni-mp-compiler/src/transforms/transformElement.ts b/packages/uni-mp-compiler/src/transforms/transformElement.ts
index 48d9b6f7d47439ad01134c772babeb7db319df75..a7e55b2897caa33e07e4b338a7ab9772b16284df 100644
--- a/packages/uni-mp-compiler/src/transforms/transformElement.ts
+++ b/packages/uni-mp-compiler/src/transforms/transformElement.ts
@@ -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)
+// }
+// }
diff --git a/packages/uni-mp-compiler/src/transforms/transformIdentifier.ts b/packages/uni-mp-compiler/src/transforms/transformIdentifier.ts
index 80a4250d7c46e1fe511221ac720a197d20d56738..dbf2484fd98e7115a633f514d17f75d8dbe1e515 100644
--- a/packages/uni-mp-compiler/src/transforms/transformIdentifier.ts
+++ b/packages/uni-mp-compiler/src/transforms/transformIdentifier.ts
@@ -1,16 +1,33 @@
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()