vue3-yuanma/packages/compiler-core/src/transforms/vModel.ts

112 lines
3.3 KiB
TypeScript
Raw Normal View History

import { DirectiveTransform, TransformContext } from '../transform'
import {
createSimpleExpression,
createObjectProperty,
createCompoundExpression,
NodeTypes,
Property,
CompoundExpressionNode,
createInterpolation
} from '../ast'
import { createCompilerError, ErrorCodes } from '../errors'
import { isMemberExpression, isSimpleIdentifier } from '../utils'
import { isObject } from '@vue/shared'
export const transformModel: DirectiveTransform = (dir, node, context) => {
const { exp, arg } = dir
if (!exp) {
context.onError(
createCompilerError(ErrorCodes.X_V_MODEL_NO_EXPRESSION, dir.loc)
)
return createTransformProps()
}
const expString =
exp.type === NodeTypes.SIMPLE_EXPRESSION ? exp.content : exp.loc.source
if (!isMemberExpression(expString)) {
context.onError(
createCompilerError(ErrorCodes.X_V_MODEL_MALFORMED_EXPRESSION, exp.loc)
)
return createTransformProps()
}
if (
!__BROWSER__ &&
context.prefixIdentifiers &&
isSimpleIdentifier(expString) &&
context.identifiers[expString]
) {
context.onError(
createCompilerError(ErrorCodes.X_V_MODEL_ON_SCOPE_VARIABLE, exp.loc)
)
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)
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 = [
createObjectProperty(propName, dir.exp!),
createObjectProperty(
eventName,
createCompoundExpression([
`$event => (`,
...assignmentChildren,
` = $event)`
])
)
2019-10-10 22:02:51 +00:00
]
if (dir.modifiers.length) {
// TODO add modelModifiers prop
}
return createTransformProps(props)
}
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)
}
}
function createTransformProps(props: Property[] = []) {
return { props, needRuntime: false }
}