2020-02-05 17:01:00 -05:00

141 lines
4.0 KiB
TypeScript

import {
DirectiveTransform,
ElementTypes,
transformModel,
findProp,
NodeTypes,
createDOMCompilerError,
DOMErrorCodes,
createObjectProperty,
createSimpleExpression,
createCallExpression,
PlainElementNode,
ExpressionNode,
createConditionalExpression,
createInterpolation,
hasDynamicKeyVBind
} from '@vue/compiler-dom'
import {
SSR_LOOSE_EQUAL,
SSR_LOOSE_CONTAIN,
SSR_RENDER_DYNAMIC_MODEL
} from '../runtimeHelpers'
import { DirectiveTransformResult } from 'packages/compiler-core/src/transform'
export const ssrTransformModel: DirectiveTransform = (dir, node, context) => {
const model = dir.exp!
function checkDuplicatedValue() {
const value = findProp(node, 'value')
if (value) {
context.onError(
createDOMCompilerError(
DOMErrorCodes.X_V_MODEL_UNNECESSARY_VALUE,
value.loc
)
)
}
}
if (node.tagType === ElementTypes.ELEMENT) {
const res: DirectiveTransformResult = { props: [] }
const defaultProps = [
// default value binding for text type inputs
createObjectProperty(createSimpleExpression(`value`, true), model)
]
if (node.tag === 'input') {
const type = findProp(node, 'type')
if (type) {
const value = findValueBinding(node)
if (type.type === NodeTypes.DIRECTIVE) {
// dynamic type
res.ssrTagParts = [
createCallExpression(context.helper(SSR_RENDER_DYNAMIC_MODEL), [
type.exp!,
model,
value
])
]
} else if (type.value) {
// static type
switch (type.value.content) {
case 'radio':
res.props = [
createObjectProperty(
createSimpleExpression(`checked`, true),
createCallExpression(context.helper(SSR_LOOSE_EQUAL), [
model,
value
])
)
]
break
case 'checkbox':
res.props = [
createObjectProperty(
createSimpleExpression(`checked`, true),
createConditionalExpression(
createCallExpression(`Array.isArray`, [model]),
createCallExpression(context.helper(SSR_LOOSE_CONTAIN), [
model,
value
]),
model
)
)
]
break
case 'file':
context.onError(
createDOMCompilerError(
DOMErrorCodes.X_V_MODEL_ON_FILE_INPUT_ELEMENT,
dir.loc
)
)
break
default:
checkDuplicatedValue()
res.props = defaultProps
break
}
}
} else if (hasDynamicKeyVBind(node)) {
// dynamic type due to dynamic v-bind
// NOOP, handled in ssrTransformElement due to need to rewrite
// the entire props expression
} else {
// text type
checkDuplicatedValue()
res.props = defaultProps
}
} else if (node.tag === 'textarea') {
checkDuplicatedValue()
node.children = [createInterpolation(model, model.loc)]
} else if (node.tag === 'select') {
// NOOP
// select relies on client-side directive to set initial selected state.
} else {
context.onError(
createDOMCompilerError(
DOMErrorCodes.X_V_MODEL_ON_INVALID_ELEMENT,
dir.loc
)
)
}
return res
} else {
// component v-model
return transformModel(dir, node, context)
}
}
function findValueBinding(node: PlainElementNode): ExpressionNode {
const valueBinding = findProp(node, 'value')
return valueBinding
? valueBinding.type === NodeTypes.DIRECTIVE
? valueBinding.exp!
: createSimpleExpression(valueBinding.value!.content, true)
: createSimpleExpression(`null`, false)
}