wip: support configuring compiler deprecations at runtime + warn invalid deprecation configs

This commit is contained in:
Evan You 2021-04-16 12:19:12 -04:00
parent 79cbf21c3e
commit bbf708dbe9
5 changed files with 72 additions and 22 deletions

View File

@ -14,14 +14,14 @@ export interface CompilerCompatOptions {
} }
export const enum CompilerDeprecationTypes { export const enum CompilerDeprecationTypes {
IS_ON_ELEMENT = 'IS_ON_ELEMENT', COMPILER_IS_ON_ELEMENT = 'COMPILER_IS_ON_ELEMENT',
V_BIND_SYNC = 'V_BIND_SYNC', COMPILER_V_BIND_SYNC = 'COMPILER_V_BIND_SYNC',
V_BIND_OBJECT_ORDER = 'V_BIND_OBJECT_ORDER', COMPILER_V_BIND_OBJECT_ORDER = 'COMPILER_V_BIND_OBJECT_ORDER',
V_ON_NATIVE_MODIFIER = 'V_ON_NATIVE_MODIFIER', COMPILER_V_ON_NATIVE_MODIFIER = 'COMPILER_V_ON_NATIVE_MODIFIER',
KEY_V_IF = 'KEY_V_IF', COMPILER_KEY_V_IF = 'COMPILER_KEY_V_IF',
KEY_V_FOR_TEMPLATE = 'KEY_V_FOR_TEMPLATE', COMPILER_KEY_V_FOR_TEMPLATE = 'COMPILER_KEY_V_FOR_TEMPLATE',
V_IF_V_FOR_PRECEDENCE = 'V_IF_V_FOR_PRECEDENCE', COMPILER_V_IF_V_FOR_PRECEDENCE = 'COMPILER_V_IF_V_FOR_PRECEDENCE',
NATIVE_TEMPLATE = 'NATIVE_TEMPLATE' COMPILER_NATIVE_TEMPLATE = 'COMPILER_NATIVE_TEMPLATE'
} }
type DeprecationData = { type DeprecationData = {
@ -30,7 +30,7 @@ type DeprecationData = {
} }
const deprecationData: Record<CompilerDeprecationTypes, DeprecationData> = { const deprecationData: Record<CompilerDeprecationTypes, DeprecationData> = {
[CompilerDeprecationTypes.IS_ON_ELEMENT]: { [CompilerDeprecationTypes.COMPILER_IS_ON_ELEMENT]: {
message: message:
`Platform-native elements with "is" prop will no longer be ` + `Platform-native elements with "is" prop will no longer be ` +
`treated as components in Vue 3 unless the "is" value is explicitly ` + `treated as components in Vue 3 unless the "is" value is explicitly ` +
@ -38,7 +38,7 @@ const deprecationData: Record<CompilerDeprecationTypes, DeprecationData> = {
link: `https://v3.vuejs.org/guide/migration/custom-elements-interop.html` link: `https://v3.vuejs.org/guide/migration/custom-elements-interop.html`
}, },
[CompilerDeprecationTypes.V_BIND_SYNC]: { [CompilerDeprecationTypes.COMPILER_V_BIND_SYNC]: {
message: key => message: key =>
`.sync modifier for v-bind has been removed. Use v-model with ` + `.sync modifier for v-bind has been removed. Use v-model with ` +
`argument instead. \`v-bind:${key}.sync\` should be changed to ` + `argument instead. \`v-bind:${key}.sync\` should be changed to ` +
@ -46,7 +46,7 @@ const deprecationData: Record<CompilerDeprecationTypes, DeprecationData> = {
link: `https://v3.vuejs.org/guide/migration/v-model.html` link: `https://v3.vuejs.org/guide/migration/v-model.html`
}, },
[CompilerDeprecationTypes.V_BIND_OBJECT_ORDER]: { [CompilerDeprecationTypes.COMPILER_V_BIND_OBJECT_ORDER]: {
message: message:
`v-bind="obj" usage is now order sensitive and behaves like JavaScript ` + `v-bind="obj" usage is now order sensitive and behaves like JavaScript ` +
`object spread: it will now overwrite an existing attribute that appears ` + `object spread: it will now overwrite an existing attribute that appears ` +
@ -56,22 +56,22 @@ const deprecationData: Record<CompilerDeprecationTypes, DeprecationData> = {
link: `https://v3.vuejs.org/guide/migration/v-bind.html` link: `https://v3.vuejs.org/guide/migration/v-bind.html`
}, },
[CompilerDeprecationTypes.V_ON_NATIVE_MODIFIER]: { [CompilerDeprecationTypes.COMPILER_V_ON_NATIVE_MODIFIER]: {
message: `.native modifier for v-on has been removed as is no longer necessary.`, message: `.native modifier for v-on has been removed as is no longer necessary.`,
link: `https://v3.vuejs.org/guide/migration/v-on-native-modifier-removed.html` link: `https://v3.vuejs.org/guide/migration/v-on-native-modifier-removed.html`
}, },
[CompilerDeprecationTypes.KEY_V_IF]: { [CompilerDeprecationTypes.COMPILER_KEY_V_IF]: {
message: ``, message: ``,
link: `https://v3.vuejs.org/guide/migration/key-attribute.html#on-conditional-branches` link: `https://v3.vuejs.org/guide/migration/key-attribute.html#on-conditional-branches`
}, },
[CompilerDeprecationTypes.KEY_V_FOR_TEMPLATE]: { [CompilerDeprecationTypes.COMPILER_KEY_V_FOR_TEMPLATE]: {
message: ``, message: ``,
link: `https://v3.vuejs.org/guide/migration/key-attribute.html#with-template-v-for` link: `https://v3.vuejs.org/guide/migration/key-attribute.html#with-template-v-for`
}, },
[CompilerDeprecationTypes.V_IF_V_FOR_PRECEDENCE]: { [CompilerDeprecationTypes.COMPILER_V_IF_V_FOR_PRECEDENCE]: {
message: message:
`v-if / v-for precedence when used on the same element has changed ` + `v-if / v-for precedence when used on the same element has changed ` +
`in Vue 3. It is best to avoid the ambiguity with either <template> tags ` + `in Vue 3. It is best to avoid the ambiguity with either <template> tags ` +
@ -79,7 +79,7 @@ const deprecationData: Record<CompilerDeprecationTypes, DeprecationData> = {
link: `https://v3.vuejs.org/guide/migration/v-if-v-for.html` link: `https://v3.vuejs.org/guide/migration/v-if-v-for.html`
}, },
[CompilerDeprecationTypes.NATIVE_TEMPLATE]: { [CompilerDeprecationTypes.COMPILER_NATIVE_TEMPLATE]: {
message: message:
`<template> with no special directives will render as a native template` + `<template> with no special directives will render as a native template` +
`element instead of its inner content in Vue 3.` `element instead of its inner content in Vue 3.`

View File

@ -517,7 +517,7 @@ function parseTag(
if ( if (
__COMPAT__ && __COMPAT__ &&
checkCompatEnabled( checkCompatEnabled(
CompilerDeprecationTypes.IS_ON_ELEMENT, CompilerDeprecationTypes.COMPILER_IS_ON_ELEMENT,
context, context,
p.loc p.loc
) )

View File

@ -96,7 +96,7 @@ export const transformOn: DirectiveTransform = (dir, node, context) => {
if (__COMPAT__ && __DEV__ && modifiers.includes('native')) { if (__COMPAT__ && __DEV__ && modifiers.includes('native')) {
warnDeprecation( warnDeprecation(
CompilerDeprecationTypes.V_ON_NATIVE_MODIFIER, CompilerDeprecationTypes.COMPILER_V_ON_NATIVE_MODIFIER,
context, context,
dir.loc dir.loc
) )

View File

@ -456,12 +456,48 @@ export type CompatConfig = Partial<
MODE?: 2 | 3 MODE?: 2 | 3
} }
const globalCompatConfig: CompatConfig = {} export const globalCompatConfig: CompatConfig = {}
export function configureCompat(config: CompatConfig) { export function configureCompat(config: CompatConfig) {
if (__DEV__) {
validateCompatConfig(config)
}
extend(globalCompatConfig, config) extend(globalCompatConfig, config)
} }
const seenConfigObjects = /*#__PURE__*/ new WeakSet<CompatConfig>()
const warnedInvalidKeys: Record<string, boolean> = {}
// dev only
export function validateCompatConfig(config: CompatConfig) {
if (seenConfigObjects.has(config)) {
return
}
seenConfigObjects.add(config)
for (const key of Object.keys(config)) {
if (
key !== 'MODE' &&
!(key in deprecationData) &&
!(key in warnedInvalidKeys)
) {
if (key.startsWith('COMPILER_')) {
if (isRuntimeOnly()) {
warn(
`Depreaction config "${key}" is compiler-specific and you are ` +
`running a runtime-only build of Vue. This deprecation should be ` +
`configured via compiler options in your build setup instead.`
// TODO link to migration build docs on build setup
)
}
} else {
warn(`Invalid deprecation config "${key}".`)
}
warnedInvalidKeys[key] = true
}
}
}
export function getCompatConfigForKey( export function getCompatConfigForKey(
key: DeprecationTypes | 'MODE', key: DeprecationTypes | 'MODE',
instance: ComponentInternalInstance | null instance: ComponentInternalInstance | null

View File

@ -47,7 +47,8 @@ import {
NO, NO,
makeMap, makeMap,
isPromise, isPromise,
ShapeFlags ShapeFlags,
extend
} from '@vue/shared' } from '@vue/shared'
import { SuspenseBoundary } from './components/Suspense' import { SuspenseBoundary } from './components/Suspense'
import { CompilerOptions } from '@vue/compiler-core' import { CompilerOptions } from '@vue/compiler-core'
@ -55,6 +56,7 @@ import { markAttrsAccessed } from './componentRenderUtils'
import { currentRenderingInstance } from './componentRenderContext' import { currentRenderingInstance } from './componentRenderContext'
import { startMeasure, endMeasure } from './profiling' import { startMeasure, endMeasure } from './profiling'
import { convertLegacyRenderFn } from './compat/renderFn' import { convertLegacyRenderFn } from './compat/renderFn'
import { globalCompatConfig, validateCompatConfig } from './compat/compatConfig'
export type Data = Record<string, unknown> export type Data = Record<string, unknown>
@ -692,6 +694,10 @@ export function finishComponentSetup(
if (__COMPAT__) { if (__COMPAT__) {
convertLegacyRenderFn(instance) convertLegacyRenderFn(instance)
if (__DEV__ && Component.compatConfig) {
validateCompatConfig(Component.compatConfig)
}
} }
// template / render function normalization // template / render function normalization
@ -710,10 +716,18 @@ export function finishComponentSetup(
if (__DEV__) { if (__DEV__) {
startMeasure(instance, `compile`) startMeasure(instance, `compile`)
} }
Component.render = compile(Component.template, { const compilerOptions: CompilerOptions = {
isCustomElement: instance.appContext.config.isCustomElement, isCustomElement: instance.appContext.config.isCustomElement,
delimiters: Component.delimiters delimiters: Component.delimiters
}) }
if (__COMPAT__) {
// pass runtime compat config into the compiler
compilerOptions.compatConfig = Object.create(globalCompatConfig)
if (Component.compatConfig) {
extend(compilerOptions.compatConfig, Component.compatConfig)
}
}
Component.render = compile(Component.template, compilerOptions)
if (__DEV__) { if (__DEV__) {
endMeasure(instance, `compile`) endMeasure(instance, `compile`)
} }