2019-10-16 17:56:00 +00:00
|
|
|
import { DirectiveTransform, TransformContext } from '../transform'
|
2019-10-10 14:33:58 +00:00
|
|
|
import {
|
|
|
|
createSimpleExpression,
|
|
|
|
createObjectProperty,
|
|
|
|
createCompoundExpression,
|
|
|
|
NodeTypes,
|
2019-10-16 17:56:00 +00:00
|
|
|
Property,
|
|
|
|
CompoundExpressionNode,
|
|
|
|
createInterpolation
|
2019-10-10 14:33:58 +00:00
|
|
|
} from '../ast'
|
|
|
|
import { createCompilerError, ErrorCodes } from '../errors'
|
2019-10-10 15:15:24 +00:00
|
|
|
import { isMemberExpression } from '../utils'
|
2019-10-16 17:56:00 +00:00
|
|
|
import { isObject } from '@vue/shared'
|
2019-10-10 14:33:58 +00:00
|
|
|
|
|
|
|
export const transformModel: DirectiveTransform = (dir, node, context) => {
|
|
|
|
const { exp, arg } = dir
|
|
|
|
if (!exp) {
|
2019-10-10 15:15:24 +00:00
|
|
|
context.onError(
|
|
|
|
createCompilerError(ErrorCodes.X_V_MODEL_NO_EXPRESSION, dir.loc)
|
|
|
|
)
|
2019-10-10 14:33:58 +00:00
|
|
|
return createTransformProps()
|
|
|
|
}
|
|
|
|
|
2019-10-10 15:15:24 +00:00
|
|
|
const expString =
|
|
|
|
exp.type === NodeTypes.SIMPLE_EXPRESSION ? exp.content : exp.loc.source
|
|
|
|
if (!isMemberExpression(expString)) {
|
2019-10-10 14:33:58 +00:00
|
|
|
context.onError(
|
2019-10-10 15:15:24 +00:00
|
|
|
createCompilerError(ErrorCodes.X_V_MODEL_MALFORMED_EXPRESSION, exp.loc)
|
2019-10-10 14:33:58 +00:00
|
|
|
)
|
|
|
|
return createTransformProps()
|
|
|
|
}
|
|
|
|
|
|
|
|
const propName = arg ? arg : createSimpleExpression('modelValue', true)
|
|
|
|
const eventName = arg
|
|
|
|
? arg.type === NodeTypes.SIMPLE_EXPRESSION && arg.isStatic
|
|
|
|
? createSimpleExpression('onUpdate:' + arg.content, true)
|
|
|
|
: createCompoundExpression([
|
|
|
|
createSimpleExpression('onUpdate:', true),
|
|
|
|
'+',
|
|
|
|
...(arg.type === NodeTypes.SIMPLE_EXPRESSION ? [arg] : arg.children)
|
|
|
|
])
|
|
|
|
: createSimpleExpression('onUpdate:modelValue', true)
|
|
|
|
|
2019-10-16 17:56:00 +00:00
|
|
|
let assignmentChildren =
|
|
|
|
exp.type === NodeTypes.SIMPLE_EXPRESSION ? [exp] : exp.children
|
|
|
|
// For a member expression used in assignment, it only needs to be updated
|
|
|
|
// if the expression involves scope variables. Otherwise we can mark the
|
|
|
|
// expression as constant to avoid it being included in `dynamicPropNames`
|
|
|
|
// of the element. This optimization relies on `prefixIdentifiers: true`.
|
|
|
|
if (!__BROWSER__ && context.prefixIdentifiers) {
|
|
|
|
assignmentChildren = assignmentChildren.map(c => toConstant(c, context))
|
|
|
|
}
|
|
|
|
|
2019-10-10 22:02:51 +00:00
|
|
|
const props = [
|
2019-10-10 14:33:58 +00:00
|
|
|
createObjectProperty(propName, dir.exp!),
|
|
|
|
createObjectProperty(
|
|
|
|
eventName,
|
|
|
|
createCompoundExpression([
|
|
|
|
`$event => (`,
|
2019-10-16 17:56:00 +00:00
|
|
|
...assignmentChildren,
|
2019-10-10 14:33:58 +00:00
|
|
|
` = $event)`
|
|
|
|
])
|
|
|
|
)
|
2019-10-10 22:02:51 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
if (dir.modifiers.length) {
|
|
|
|
// TODO add modelModifiers prop
|
|
|
|
}
|
|
|
|
|
|
|
|
return createTransformProps(props)
|
2019-10-10 14:33:58 +00:00
|
|
|
}
|
|
|
|
|
2019-10-16 17:56:00 +00:00
|
|
|
function toConstant(
|
|
|
|
exp: CompoundExpressionNode | CompoundExpressionNode['children'][0],
|
|
|
|
context: TransformContext
|
|
|
|
): any {
|
|
|
|
if (!isObject(exp) || exp.type === NodeTypes.TEXT) {
|
|
|
|
return exp
|
|
|
|
}
|
|
|
|
if (exp.type === NodeTypes.SIMPLE_EXPRESSION) {
|
|
|
|
if (exp.isStatic || context.identifiers[exp.content]) {
|
|
|
|
return exp
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
...exp,
|
|
|
|
isConstant: true
|
|
|
|
}
|
|
|
|
} else if (exp.type === NodeTypes.COMPOUND_EXPRESSION) {
|
|
|
|
return createCompoundExpression(
|
|
|
|
exp.children.map(c => toConstant(c, context))
|
|
|
|
)
|
|
|
|
} else if (exp.type === NodeTypes.INTERPOLATION) {
|
|
|
|
return createInterpolation(toConstant(exp.content, context), exp.loc)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-10 14:33:58 +00:00
|
|
|
function createTransformProps(props: Property[] = []) {
|
|
|
|
return { props, needRuntime: false }
|
|
|
|
}
|