feat(compiler-core/v-model): error when v-model is used on scope variable
This commit is contained in:
parent
5481f76ce8
commit
25dd507f71
@ -361,7 +361,7 @@ describe('compiler: transform v-model', () => {
|
|||||||
|
|
||||||
test('should mark update handler dynamic if it refers slot scope variables', () => {
|
test('should mark update handler dynamic if it refers slot scope variables', () => {
|
||||||
const root = parseWithVModel(
|
const root = parseWithVModel(
|
||||||
'<Comp v-slot="{ foo }"><input v-model="foo"/></Comp>',
|
'<Comp v-slot="{ foo }"><input v-model="foo.bar"/></Comp>',
|
||||||
{
|
{
|
||||||
prefixIdentifiers: true
|
prefixIdentifiers: true
|
||||||
}
|
}
|
||||||
@ -407,5 +407,20 @@ describe('compiler: transform v-model', () => {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('used on scope variable', () => {
|
||||||
|
const onError = jest.fn()
|
||||||
|
parseWithVModel('<span v-for="i in list" v-model="i" />', {
|
||||||
|
onError,
|
||||||
|
prefixIdentifiers: true
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(onError).toHaveBeenCalledTimes(1)
|
||||||
|
expect(onError).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
code: ErrorCodes.X_V_MODEL_ON_SCOPE_VARIABLE
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -81,6 +81,7 @@ export const enum ErrorCodes {
|
|||||||
X_V_SLOT_MISPLACED,
|
X_V_SLOT_MISPLACED,
|
||||||
X_V_MODEL_NO_EXPRESSION,
|
X_V_MODEL_NO_EXPRESSION,
|
||||||
X_V_MODEL_MALFORMED_EXPRESSION,
|
X_V_MODEL_MALFORMED_EXPRESSION,
|
||||||
|
X_V_MODEL_ON_SCOPE_VARIABLE,
|
||||||
|
|
||||||
// generic errors
|
// generic errors
|
||||||
X_PREFIX_ID_NOT_SUPPORTED,
|
X_PREFIX_ID_NOT_SUPPORTED,
|
||||||
@ -171,6 +172,7 @@ export const errorMessages: { [code: number]: string } = {
|
|||||||
[ErrorCodes.X_V_SLOT_MISPLACED]: `v-slot can only be used on components or <template> tags.`,
|
[ErrorCodes.X_V_SLOT_MISPLACED]: `v-slot can only be used on components or <template> tags.`,
|
||||||
[ErrorCodes.X_V_MODEL_NO_EXPRESSION]: `v-model is missing expression.`,
|
[ErrorCodes.X_V_MODEL_NO_EXPRESSION]: `v-model is missing expression.`,
|
||||||
[ErrorCodes.X_V_MODEL_MALFORMED_EXPRESSION]: `v-model value must be a valid JavaScript member expression.`,
|
[ErrorCodes.X_V_MODEL_MALFORMED_EXPRESSION]: `v-model value must be a valid JavaScript member expression.`,
|
||||||
|
[ErrorCodes.X_V_MODEL_ON_SCOPE_VARIABLE]: `v-model cannot be used on v-for or v-slot scope variables because they are not writable.`,
|
||||||
|
|
||||||
// generic errors
|
// generic errors
|
||||||
[ErrorCodes.X_PREFIX_ID_NOT_SUPPORTED]: `"prefixIdentifiers" option is not supported in this build of compiler.`,
|
[ErrorCodes.X_PREFIX_ID_NOT_SUPPORTED]: `"prefixIdentifiers" option is not supported in this build of compiler.`,
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
createInterpolation
|
createInterpolation
|
||||||
} from '../ast'
|
} from '../ast'
|
||||||
import { createCompilerError, ErrorCodes } from '../errors'
|
import { createCompilerError, ErrorCodes } from '../errors'
|
||||||
import { isMemberExpression } from '../utils'
|
import { isMemberExpression, isSimpleIdentifier } from '../utils'
|
||||||
import { isObject } from '@vue/shared'
|
import { isObject } from '@vue/shared'
|
||||||
|
|
||||||
export const transformModel: DirectiveTransform = (dir, node, context) => {
|
export const transformModel: DirectiveTransform = (dir, node, context) => {
|
||||||
@ -30,6 +30,18 @@ export const transformModel: DirectiveTransform = (dir, node, context) => {
|
|||||||
return createTransformProps()
|
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 propName = arg ? arg : createSimpleExpression('modelValue', true)
|
||||||
const eventName = arg
|
const eventName = arg
|
||||||
? arg.type === NodeTypes.SIMPLE_EXPRESSION && arg.isStatic
|
? arg.type === NodeTypes.SIMPLE_EXPRESSION && arg.isStatic
|
||||||
|
@ -15,7 +15,12 @@ import {
|
|||||||
} from '../runtimeHelpers'
|
} from '../runtimeHelpers'
|
||||||
|
|
||||||
export const transformModel: DirectiveTransform = (dir, node, context) => {
|
export const transformModel: DirectiveTransform = (dir, node, context) => {
|
||||||
const res = baseTransform(dir, node, context)
|
const baseResult = baseTransform(dir, node, context)
|
||||||
|
// base transform has errors
|
||||||
|
if (!baseResult.props.length) {
|
||||||
|
return baseResult
|
||||||
|
}
|
||||||
|
|
||||||
const { tag, tagType } = node
|
const { tag, tagType } = node
|
||||||
if (tagType === ElementTypes.ELEMENT) {
|
if (tagType === ElementTypes.ELEMENT) {
|
||||||
if (dir.arg) {
|
if (dir.arg) {
|
||||||
@ -63,7 +68,7 @@ export const transformModel: DirectiveTransform = (dir, node, context) => {
|
|||||||
// by returning the helper symbol via needRuntime
|
// by returning the helper symbol via needRuntime
|
||||||
// the import will replaced a resovleDirective call.
|
// the import will replaced a resovleDirective call.
|
||||||
if (!isInvalidType) {
|
if (!isInvalidType) {
|
||||||
res.needRuntime = context.helper(directiveToUse)
|
baseResult.needRuntime = context.helper(directiveToUse)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
context.onError(
|
context.onError(
|
||||||
@ -74,5 +79,5 @@ export const transformModel: DirectiveTransform = (dir, node, context) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res
|
return baseResult
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user