]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: compiler deprecation config
authorEvan You <yyx990803@gmail.com>
Mon, 12 Apr 2021 23:42:09 +0000 (19:42 -0400)
committerEvan You <yyx990803@gmail.com>
Mon, 12 Apr 2021 23:43:53 +0000 (19:43 -0400)
packages/compiler-core/src/compat/compatConfig.ts [new file with mode: 0644]
packages/compiler-core/src/errors.ts
packages/compiler-core/src/index.ts
packages/compiler-core/src/options.ts
packages/compiler-core/src/parse.ts
packages/compiler-core/src/transform.ts
packages/compiler-dom/src/errors.ts
packages/compiler-dom/src/transforms/vOn.ts
packages/compiler-ssr/src/errors.ts

diff --git a/packages/compiler-core/src/compat/compatConfig.ts b/packages/compiler-core/src/compat/compatConfig.ts
new file mode 100644 (file)
index 0000000..3abb77f
--- /dev/null
@@ -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)
+}
index a3419c88455f97b0790764f3ef5116ad9e87dbae..d1f1e0a8c633cf98768c9f17058f5a84f143846d 100644 (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,
index 03388b1f7a8c4a1935a513852725f66444a211a5..303de3af73e9ef052aac1be6099e8f0f083cf83b 100644 (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'
index 1b5450de29d65900107086161fe3b5018beb90de..62fd2f1cb23565a675d43d6fee30d312df31a622 100644 (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 {
index 3bcf4159d5f06717b7dcb021b8cb56f0441adae3..73c25f01863359dd1ca651ee40beac226d0d1cc4 100644 (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
   }
 }
 
index 9dd5b8be4daf6d019bdf80a6bf4a6265cf24ea1d..36f7f2e01f5bd5814cfdfb4609d8723005081d5c 100644 (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,
index d88b7ca8ca8de39f216e37ed3ee0e425f5e46077..159bd4ba3b010579974ffe18dd6d8147f8c35fdb 100644 (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 {
index f83d4a48b1aa5a23ea3bd72e610d2c69832c3daf..25eeaa25bc7bad0410f12dc66037f688e772a413 100644 (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]
index a1e057a78c230f639dce6d22ba77b612f85671ac..5030a56db5220a1a446041ff9908d68b6d14c191 100644 (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 {