wip: compiler deprecation config

This commit is contained in:
Evan You 2021-04-12 19:42:09 -04:00
parent 115372dd5b
commit e130c7db23
9 changed files with 198 additions and 18 deletions

View File

@ -0,0 +1,133 @@
import { SourceLocation } from '../ast'
import { CompilerError } from '../errors'
import { ParserContext } from '../parse'
import { TransformContext } from '../transform'
export type CompilerCompatConfig = Partial<
Record<CompilerDeprecationTypes, boolean | 'suppress-warning'>
> & {
MODE?: 2 | 3
}
export interface CompilerCompatOptions {
compatConfig?: CompilerCompatConfig
}
export const enum CompilerDeprecationTypes {
IS_ON_ELEMENT = 'IS_ON_ELEMENT',
V_BIND_SYNC = 'V_BIND_SYNC',
V_BIND_OBJECT_ORDER = 'V_BIND_OBJECT_ORDER',
V_ON_NATIVE_MODIFIER = 'V_ON_NATIVE_MODIFIER',
KEY_V_IF = 'KEY_V_IF',
KEY_V_FOR_TEMPLATE = 'KEY_V_FOR_TEMPLATE',
V_IF_V_FOR_PRECEDENCE = 'V_IF_V_FOR_PRECEDENCE',
NATIVE_TEMPLATE = 'NATIVE_TEMPLATE'
}
type DeprecationData = {
message: string | ((...args: any[]) => string)
link?: string
}
const deprecationData: Record<CompilerDeprecationTypes, DeprecationData> = {
[CompilerDeprecationTypes.IS_ON_ELEMENT]: {
message: ``,
link: `https://v3.vuejs.org/guide/migration/custom-elements-interop.html`
},
[CompilerDeprecationTypes.V_BIND_SYNC]: {
message: key =>
`.sync modifier for v-bind has been removed. Use v-model with ` +
`argument instead. \`v-bind:${key}.sync\` should be changed to ` +
`\`v-model:${key}\`.`,
link: `https://v3.vuejs.org/guide/migration/v-model.html`
},
[CompilerDeprecationTypes.V_BIND_OBJECT_ORDER]: {
message:
`v-bind="obj" usage is now order sensitive and behaves like JavaScript ` +
`object spread: it will now overwrite an existing attribute that appears ` +
`before v-bind in the case of conflicting keys. To retain 2.x behavior, ` +
`move v-bind to and make it the first attribute. If all occurences ` +
`of this warning are working as intended, you can suppress it.`,
link: `https://v3.vuejs.org/guide/migration/v-bind.html`
},
[CompilerDeprecationTypes.V_ON_NATIVE_MODIFIER]: {
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`
},
[CompilerDeprecationTypes.KEY_V_IF]: {
message: ``,
link: `https://v3.vuejs.org/guide/migration/key-attribute.html#on-conditional-branches`
},
[CompilerDeprecationTypes.KEY_V_FOR_TEMPLATE]: {
message: ``,
link: `https://v3.vuejs.org/guide/migration/key-attribute.html#with-template-v-for`
},
[CompilerDeprecationTypes.V_IF_V_FOR_PRECEDENCE]: {
message:
`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 ` +
`or a computed property that filters v-for data source.`,
link: `https://v3.vuejs.org/guide/migration/v-if-v-for.html`
},
[CompilerDeprecationTypes.NATIVE_TEMPLATE]: {
message:
`<template> with no special directives will render as a native template` +
`element instead of its inner content in Vue 3.`
}
}
function getCompatValue(
key: CompilerDeprecationTypes,
context: ParserContext | TransformContext
) {
const config = (context as ParserContext).options
? (context as ParserContext).options.compatConfig
: (context as TransformContext).compatConfig
return config && config[key]
}
export function checkCompatEnabled(
key: CompilerDeprecationTypes,
context: ParserContext | TransformContext,
loc: SourceLocation | null,
...args: any[]
): boolean {
const enabled = getCompatValue(key, context) !== false
if (__DEV__ && enabled) {
warnDeprecation(key, context, loc, ...args)
}
return enabled
}
export function warnDeprecation(
key: CompilerDeprecationTypes,
context: ParserContext | TransformContext,
loc: SourceLocation | null,
...args: any[]
) {
const val = getCompatValue(key, context)
if (val === 'suppress-warning') {
return
}
const { message, link } = deprecationData[key]
const msg = `(deprecation ${key}) ${
typeof message === 'function' ? message(...args) : message
}${link ? `\n Details: ${link}` : ``}`
if (loc) {
const err = new SyntaxError(msg) as CompilerError
err.code = key
err.loc = loc
context.onWarn(err)
return
}
context.onWarn(msg)
}

View File

@ -1,7 +1,7 @@
import { SourceLocation } from './ast'
export interface CompilerError extends SyntaxError {
code: number
code: number | string
loc?: SourceLocation
}
@ -13,6 +13,11 @@ export function defaultOnError(error: CompilerError) {
throw error
}
export function defaultOnWarn(msg: string | CompilerError) {
__DEV__ &&
console.warn(`[Vue warn]`, typeof msg === 'string' ? msg : msg.message)
}
export function createCompilerError<T extends number>(
code: T,
loc?: SourceLocation,

View File

@ -57,3 +57,10 @@ export {
} from './transforms/transformElement'
export { processSlotOutlet } from './transforms/transformSlotOutlet'
export { generateCodeFrame } from '@vue/shared'
// v2 compat only
export {
checkCompatEnabled,
warnDeprecation,
CompilerDeprecationTypes
} from './compat/compatConfig'

View File

@ -6,9 +6,17 @@ import {
DirectiveTransform,
TransformContext
} from './transform'
import { CompilerCompatOptions } from './compat/compatConfig'
import { ParserPlugin } from '@babel/parser'
export interface ParserOptions {
export interface ErrorHandlingOptions {
onWarn?: (msg: string | CompilerError) => void
onError?: (error: CompilerError) => void
}
export interface ParserOptions
extends ErrorHandlingOptions,
CompilerCompatOptions {
/**
* e.g. platform native elements, e.g. `<div>` for browsers
*/
@ -48,7 +56,6 @@ export interface ParserOptions {
* Only needed for DOM compilers
*/
decodeEntities?: (rawText: string, asAttr: boolean) => string
onError?: (error: CompilerError) => void
/**
* Keep comments in the templates AST, even in production
*/
@ -138,7 +145,10 @@ interface SharedTransformCodegenOptions {
filename?: string
}
export interface TransformOptions extends SharedTransformCodegenOptions {
export interface TransformOptions
extends SharedTransformCodegenOptions,
ErrorHandlingOptions,
CompilerCompatOptions {
/**
* An array of node transforms to be applied to every AST node.
*/
@ -213,7 +223,6 @@ export interface TransformOptions extends SharedTransformCodegenOptions {
* needed to render inline CSS variables on component root
*/
ssrCssVars?: string
onError?: (error: CompilerError) => void
}
export interface CodegenOptions extends SharedTransformCodegenOptions {

View File

@ -1,6 +1,11 @@
import { ParserOptions } from './options'
import { ErrorHandlingOptions, ParserOptions } from './options'
import { NO, isArray, makeMap, extend } from '@vue/shared'
import { ErrorCodes, createCompilerError, defaultOnError } from './errors'
import {
ErrorCodes,
createCompilerError,
defaultOnError,
defaultOnWarn
} from './errors'
import {
assert,
advancePositionWithMutation,
@ -25,8 +30,12 @@ import {
createRoot,
ConstantTypes
} from './ast'
import { CompilerCompatOptions } from './compat/compatConfig'
type OptionalOptions = 'isNativeTag' | 'isBuiltInComponent'
type OptionalOptions =
| 'isNativeTag'
| 'isBuiltInComponent'
| keyof CompilerCompatOptions
type MergedParserOptions = Omit<Required<ParserOptions>, OptionalOptions> &
Pick<ParserOptions, OptionalOptions>
type AttributeValue =
@ -59,6 +68,7 @@ export const defaultParserOptions: MergedParserOptions = {
decodeEntities: (rawText: string): string =>
rawText.replace(decodeRE, (_, p1) => decodeMap[p1]),
onError: defaultOnError,
onWarn: defaultOnWarn,
comments: false
}
@ -80,6 +90,7 @@ export interface ParserContext {
column: number
inPre: boolean // HTML <pre> tag, preserve whitespaces
inVPre: boolean // v-pre, do not process directives and interpolations
onWarn: NonNullable<ErrorHandlingOptions['onWarn']>
}
export function baseParse(
@ -111,7 +122,8 @@ function createParserContext(
originalSource: content,
source: content,
inPre: false,
inVPre: false
inVPre: false,
onWarn: options.onWarn
}
}

View File

@ -28,7 +28,7 @@ import {
capitalize,
camelize
} from '@vue/shared'
import { defaultOnError } from './errors'
import { defaultOnError, defaultOnWarn } from './errors'
import {
TO_DISPLAY_STRING,
FRAGMENT,
@ -40,6 +40,7 @@ import {
} from './runtimeHelpers'
import { isVSlot } from './utils'
import { hoistStatic, isSingleElementRoot } from './transforms/hoistStatic'
import { CompilerCompatOptions } from './compat/compatConfig'
// There are two types of transforms:
//
@ -83,7 +84,10 @@ export interface ImportItem {
}
export interface TransformContext
extends Required<Omit<TransformOptions, 'filename'>> {
extends Required<
Omit<TransformOptions, 'filename' | keyof CompilerCompatOptions>
>,
CompilerCompatOptions {
selfName: string | null
root: RootNode
helpers: Map<symbol, number>
@ -136,7 +140,9 @@ export function createTransformContext(
bindingMetadata = EMPTY_OBJ,
inline = false,
isTS = false,
onError = defaultOnError
onError = defaultOnError,
onWarn = defaultOnWarn,
compatConfig
}: TransformOptions
): TransformContext {
const nameMatch = filename.replace(/\?.*$/, '').match(/([^/\\]+)\.\w+$/)
@ -160,6 +166,8 @@ export function createTransformContext(
inline,
isTS,
onError,
onWarn,
compatConfig,
// state
root,

View File

@ -12,12 +12,12 @@ export interface DOMCompilerError extends CompilerError {
export function createDOMCompilerError(
code: DOMErrorCodes,
loc?: SourceLocation
): DOMCompilerError {
) {
return createCompilerError(
code,
loc,
__DEV__ || !__BROWSER__ ? DOMErrorMessages : undefined
)
) as DOMCompilerError
}
export const enum DOMErrorCodes {

View File

@ -8,7 +8,9 @@ import {
createCompoundExpression,
ExpressionNode,
SimpleExpressionNode,
isStaticExp
isStaticExp,
warnDeprecation,
CompilerDeprecationTypes
} from '@vue/compiler-core'
import { V_ON_WITH_MODIFIERS, V_ON_WITH_KEYS } from '../runtimeHelpers'
import { makeMap, capitalize } from '@vue/shared'
@ -93,7 +95,11 @@ export const transformOn: DirectiveTransform = (dir, node, context) => {
if (!modifiers.length) return baseResult
if (__COMPAT__ && __DEV__ && modifiers.includes('native')) {
console.warn('.native modifier for v-on has been removed')
warnDeprecation(
CompilerDeprecationTypes.V_ON_NATIVE_MODIFIER,
context,
dir.loc
)
}
let { key, value: handlerExp } = baseResult.props[0]

View File

@ -12,8 +12,8 @@ export interface SSRCompilerError extends CompilerError {
export function createSSRCompilerError(
code: SSRErrorCodes,
loc?: SourceLocation
): SSRCompilerError {
return createCompilerError(code, loc, SSRErrorMessages)
) {
return createCompilerError(code, loc, SSRErrorMessages) as SSRCompilerError
}
export const enum SSRErrorCodes {