return n0
}"
`;
+
+exports[`compiler: expression > update expression 1`] = `
+"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
+const t0 = _template("<div> </div>", true)
+
+export function render(_ctx) {
+ const n1 = t0()
+ const n0 = _child(n1)
+ const x1 = _child(n1)
+ _renderEffect(() => {
+ const _String = String
+ const _foo = _ctx.foo
+
+ _setText(n0, _toDisplayString(_String(_foo.id++)) + " " + _toDisplayString(_foo) + " " + _toDisplayString(_ctx.bar))
+ _setText(x1, _toDisplayString(_String(_foo.id++)) + " " + _toDisplayString(_foo) + " " + _toDisplayString(_ctx.bar))
+ _setProp(n1, "id", _String(_foo.id++))
+ _setProp(n1, "foo", _foo)
+ _setProp(n1, "bar", _ctx.bar++)
+ })
+ return n1
+}"
+`;
import { BindingTypes } from '@vue/compiler-dom'
-import { transformChildren, transformText } from '../../src'
+import {
+ transformChildren,
+ transformElement,
+ transformText,
+ transformVBind,
+} from '../../src'
import { makeCompile } from './_utils'
const compileWithExpression = makeCompile({
- nodeTransforms: [transformChildren, transformText],
+ nodeTransforms: [transformElement, transformChildren, transformText],
+ directiveTransforms: { bind: transformVBind },
})
describe('compiler: expression', () => {
expect(code).toMatchSnapshot()
expect(code).contains(`$props['bar']`)
})
+
+ test('update expression', () => {
+ const { code } = compileWithExpression(`
+ <div :id="String(foo.id++)" :foo="foo" :bar="bar++">
+ {{ String(foo.id++) }} {{ foo }} {{ bar }}
+ </div>
+ `)
+ expect(code).toMatchSnapshot()
+ expect(code).contains(`_String(_foo.id++)`)
+ })
})
test('function ref', () => {
const { ir, code } = compileWithTransformRef(
- `<div :ref="bar => foo = bar" />`,
+ `<div :ref="bar => {
+ foo.value = bar
+ ;({ baz } = bar)
+ console.log(foo.value, baz)
+ }" />`,
)
expect(ir.block.dynamic.children[0]).toMatchObject({
id: 0,
type: IRNodeTypes.SET_TEMPLATE_REF,
element: 0,
value: {
- content: 'bar => foo = bar',
isStatic: false,
},
},
])
expect(code).toMatchSnapshot()
expect(code).contains('const _setTemplateRef = _createTemplateRefSetter()')
- expect(code).contains('_setTemplateRef(n0, bar => _ctx.foo = bar, r0)')
+ expect(code).contains(`_setTemplateRef(n0, bar => {
+ _foo.value = bar
+ ;({ baz: _ctx.baz } = bar)
+ console.log(_foo.value, _ctx.baz)
+ }, r0)`)
})
test('ref + v-if', () => {
expressions: SimpleExpressionNode[],
): DeclarationResult {
// analyze variables
- const { seenVariable, variableToExpMap, expToVariableMap, seenIdentifier } =
- analyzeExpressions(expressions)
+ const {
+ seenVariable,
+ variableToExpMap,
+ expToVariableMap,
+ seenIdentifier,
+ updatedVariable,
+ } = analyzeExpressions(expressions)
// process repeated identifiers and member expressions
// e.g., `foo[baz]` will be transformed into `foo_baz`
variableToExpMap,
expToVariableMap,
seenIdentifier,
+ updatedVariable,
)
// process duplicate expressions after identifier and member expression handling.
context,
expressions,
varDeclarations,
+ updatedVariable,
+ expToVariableMap,
)
return genDeclarations([...varDeclarations, ...expDeclarations], context)
const variableToExpMap = new Map<string, Set<SimpleExpressionNode>>()
const expToVariableMap = new Map<SimpleExpressionNode, string[]>()
const seenIdentifier = new Set<string>()
+ const updatedVariable = new Set<string>()
const registerVariable = (
name: string,
exp: SimpleExpressionNode,
isIdentifier: boolean,
+ parentStack: Node[] = [],
) => {
if (isIdentifier) seenIdentifier.add(name)
seenVariable[name] = (seenVariable[name] || 0) + 1
(variableToExpMap.get(name) || new Set()).add(exp),
)
expToVariableMap.set(exp, (expToVariableMap.get(exp) || []).concat(name))
+ if (
+ parentStack.some(
+ p => p.type === 'UpdateExpression' || p.type === 'AssignmentExpression',
+ )
+ ) {
+ updatedVariable.add(name)
+ }
}
for (const exp of expressions) {
const memberExp = extractMemberExpression(parent, name => {
registerVariable(name, exp, true)
})
- registerVariable(memberExp, exp, false)
+ registerVariable(memberExp, exp, false, parentStack)
} else if (!parentStack.some(isMemberExpression)) {
- registerVariable(currentNode.name, exp, true)
+ registerVariable(currentNode.name, exp, true, parentStack)
}
})
}
- return { seenVariable, seenIdentifier, variableToExpMap, expToVariableMap }
+ return {
+ seenVariable,
+ seenIdentifier,
+ variableToExpMap,
+ expToVariableMap,
+ updatedVariable,
+ }
}
function processRepeatedVariables(
variableToExpMap: Map<string, Set<SimpleExpressionNode>>,
expToVariableMap: Map<SimpleExpressionNode, string[]>,
seenIdentifier: Set<string>,
+ updatedVariable: Set<string>,
): DeclarationValue[] {
const declarations: DeclarationValue[] = []
for (const [name, exps] of variableToExpMap) {
+ if (updatedVariable.has(name)) continue
if (seenVariable[name] > 1 && exps.size > 0) {
const isIdentifier = seenIdentifier.has(name)
const varName = isIdentifier ? name : genVarName(name)
context: CodegenContext,
expressions: SimpleExpressionNode[],
varDeclarations: DeclarationValue[],
+ updatedVariable: Set<string>,
+ expToVariableMap: Map<SimpleExpressionNode, string[]>,
): DeclarationValue[] {
const declarations: DeclarationValue[] = []
const seenExp = expressions.reduce(
(acc, exp) => {
+ const variables = expToVariableMap.get(exp)
// only handle expressions that are not identifiers
- if (exp.ast && exp.ast.type !== 'Identifier') {
+ if (
+ exp.ast &&
+ exp.ast.type !== 'Identifier' &&
+ !(variables && variables.some(v => updatedVariable.has(v)))
+ ) {
acc[exp.content] = (acc[exp.content] || 0) + 1
}
return acc