feat(compiler): warn invalid children for transition and keep-alive

This commit is contained in:
Evan You 2020-02-06 15:53:26 -05:00
parent 605cc3db17
commit 4cc39e14a2
5 changed files with 46 additions and 2 deletions

View File

@ -81,6 +81,7 @@ export const enum ErrorCodes {
X_V_MODEL_MALFORMED_EXPRESSION, X_V_MODEL_MALFORMED_EXPRESSION,
X_V_MODEL_ON_SCOPE_VARIABLE, X_V_MODEL_ON_SCOPE_VARIABLE,
X_INVALID_EXPRESSION, X_INVALID_EXPRESSION,
X_KEEP_ALIVE_INVALID_CHILDREN,
// generic errors // generic errors
X_PREFIX_ID_NOT_SUPPORTED, X_PREFIX_ID_NOT_SUPPORTED,
@ -174,6 +175,7 @@ export const errorMessages: { [code: number]: string } = {
[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.`, [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.`,
[ErrorCodes.X_INVALID_EXPRESSION]: `Invalid JavaScript expression.`, [ErrorCodes.X_INVALID_EXPRESSION]: `Invalid JavaScript expression.`,
[ErrorCodes.X_KEEP_ALIVE_INVALID_CHILDREN]: `<KeepAlive> expects exactly one child component.`,
// 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.`,

View File

@ -100,6 +100,17 @@ export const transformElement: NodeTransform = (node, context) => {
if (!hasProps) { if (!hasProps) {
args.push(`null`) args.push(`null`)
} }
if (__DEV__ && nodeType === KEEP_ALIVE && node.children.length > 1) {
context.onError(
createCompilerError(ErrorCodes.X_KEEP_ALIVE_INVALID_CHILDREN, {
start: node.children[0].loc.start,
end: node.children[node.children.length - 1].loc.end,
source: ''
})
)
}
// Portal & KeepAlive should have normal children instead of slots // Portal & KeepAlive should have normal children instead of slots
// Portal is not a real component has dedicated handling in the renderer // Portal is not a real component has dedicated handling in the renderer
// KeepAlive should not track its own deps so that it can be used inside // KeepAlive should not track its own deps so that it can be used inside

View File

@ -30,6 +30,7 @@ export const enum DOMErrorCodes {
X_V_MODEL_ON_FILE_INPUT_ELEMENT, X_V_MODEL_ON_FILE_INPUT_ELEMENT,
X_V_MODEL_UNNECESSARY_VALUE, X_V_MODEL_UNNECESSARY_VALUE,
X_V_SHOW_NO_EXPRESSION, X_V_SHOW_NO_EXPRESSION,
X_TRANSITION_INVALID_CHILDREN,
__EXTEND_POINT__ __EXTEND_POINT__
} }
@ -42,5 +43,6 @@ export const DOMErrorMessages: { [code: number]: string } = {
[DOMErrorCodes.X_V_MODEL_ARG_ON_ELEMENT]: `v-model argument is not supported on plain elements.`, [DOMErrorCodes.X_V_MODEL_ARG_ON_ELEMENT]: `v-model argument is not supported on plain elements.`,
[DOMErrorCodes.X_V_MODEL_ON_FILE_INPUT_ELEMENT]: `v-model cannot used on file inputs since they are read-only. Use a v-on:change listener instead.`, [DOMErrorCodes.X_V_MODEL_ON_FILE_INPUT_ELEMENT]: `v-model cannot used on file inputs since they are read-only. Use a v-on:change listener instead.`,
[DOMErrorCodes.X_V_MODEL_UNNECESSARY_VALUE]: `Unnecessary value binding used alongside v-model. It will interfere with v-model's behavior.`, [DOMErrorCodes.X_V_MODEL_UNNECESSARY_VALUE]: `Unnecessary value binding used alongside v-model. It will interfere with v-model's behavior.`,
[DOMErrorCodes.X_V_SHOW_NO_EXPRESSION]: `v-show is missing expression.` [DOMErrorCodes.X_V_SHOW_NO_EXPRESSION]: `v-show is missing expression.`,
[DOMErrorCodes.X_TRANSITION_INVALID_CHILDREN]: `<Transition> expects exactly one child element or component.`
} }

View File

@ -17,6 +17,7 @@ import { transformModel } from './transforms/vModel'
import { transformOn } from './transforms/vOn' import { transformOn } from './transforms/vOn'
import { transformShow } from './transforms/vShow' import { transformShow } from './transforms/vShow'
import { TRANSITION, TRANSITION_GROUP } from './runtimeHelpers' import { TRANSITION, TRANSITION_GROUP } from './runtimeHelpers'
import { warnTransitionChildren } from './transforms/warnTransitionChildren'
export const parserOptions = __BROWSER__ export const parserOptions = __BROWSER__
? parserOptionsMinimal ? parserOptionsMinimal
@ -37,7 +38,11 @@ export function compile(
return baseCompile(template, { return baseCompile(template, {
...parserOptions, ...parserOptions,
...options, ...options,
nodeTransforms: [transformStyle, ...(options.nodeTransforms || [])], nodeTransforms: [
transformStyle,
...(__DEV__ ? [warnTransitionChildren] : []),
...(options.nodeTransforms || [])
],
directiveTransforms: { directiveTransforms: {
cloak: noopDirectiveTransform, cloak: noopDirectiveTransform,
html: transformVHtml, html: transformVHtml,

View File

@ -0,0 +1,24 @@
import { NodeTransform, NodeTypes, ElementTypes } from '@vue/compiler-core'
import { TRANSITION } from '../runtimeHelpers'
import { createDOMCompilerError, DOMErrorCodes } from '../errors'
export const warnTransitionChildren: NodeTransform = (node, context) => {
if (
node.type === NodeTypes.ELEMENT &&
node.tagType === ElementTypes.COMPONENT
) {
const component = context.isBuiltInComponent(node.tag)
if (
component === TRANSITION &&
(node.children.length > 1 || node.children[0].type === NodeTypes.FOR)
) {
context.onError(
createDOMCompilerError(DOMErrorCodes.X_TRANSITION_INVALID_CHILDREN, {
start: node.children[0].loc.start,
end: node.children[node.children.length - 1].loc.end,
source: ''
})
)
}
}
}