--- /dev/null
+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)
+}
import { SourceLocation } from './ast'
export interface CompilerError extends SyntaxError {
- code: number
+ code: number | string
loc?: SourceLocation
}
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,
} from './transforms/transformElement'
export { processSlotOutlet } from './transforms/transformSlotOutlet'
export { generateCodeFrame } from '@vue/shared'
+
+// v2 compat only
+export {
+ checkCompatEnabled,
+ warnDeprecation,
+ CompilerDeprecationTypes
+} from './compat/compatConfig'
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
*/
* Only needed for DOM compilers
*/
decodeEntities?: (rawText: string, asAttr: boolean) => string
- onError?: (error: CompilerError) => void
/**
* Keep comments in the templates AST, even in production
*/
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.
*/
* needed to render inline CSS variables on component root
*/
ssrCssVars?: string
- onError?: (error: CompilerError) => void
}
export interface CodegenOptions extends SharedTransformCodegenOptions {
-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,
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 =
decodeEntities: (rawText: string): string =>
rawText.replace(decodeRE, (_, p1) => decodeMap[p1]),
onError: defaultOnError,
+ onWarn: defaultOnWarn,
comments: false
}
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(
originalSource: content,
source: content,
inPre: false,
- inVPre: false
+ inVPre: false,
+ onWarn: options.onWarn
}
}
capitalize,
camelize
} from '@vue/shared'
-import { defaultOnError } from './errors'
+import { defaultOnError, defaultOnWarn } from './errors'
import {
TO_DISPLAY_STRING,
FRAGMENT,
} from './runtimeHelpers'
import { isVSlot } from './utils'
import { hoistStatic, isSingleElementRoot } from './transforms/hoistStatic'
+import { CompilerCompatOptions } from './compat/compatConfig'
// There are two types of transforms:
//
}
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>
bindingMetadata = EMPTY_OBJ,
inline = false,
isTS = false,
- onError = defaultOnError
+ onError = defaultOnError,
+ onWarn = defaultOnWarn,
+ compatConfig
}: TransformOptions
): TransformContext {
const nameMatch = filename.replace(/\?.*$/, '').match(/([^/\\]+)\.\w+$/)
inline,
isTS,
onError,
+ onWarn,
+ compatConfig,
// state
root,
export function createDOMCompilerError(
code: DOMErrorCodes,
loc?: SourceLocation
-): DOMCompilerError {
+) {
return createCompilerError(
code,
loc,
__DEV__ || !__BROWSER__ ? DOMErrorMessages : undefined
- )
+ ) as DOMCompilerError
}
export const enum DOMErrorCodes {
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'
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]
export function createSSRCompilerError(
code: SSRErrorCodes,
loc?: SourceLocation
-): SSRCompilerError {
- return createCompilerError(code, loc, SSRErrorMessages)
+) {
+ return createCompilerError(code, loc, SSRErrorMessages) as SSRCompilerError
}
export const enum SSRErrorCodes {