diff --git a/packages/runtime-core/src/compat/compatConfig.ts b/packages/runtime-core/src/compat/compatConfig.ts new file mode 100644 index 00000000..f6395883 --- /dev/null +++ b/packages/runtime-core/src/compat/compatConfig.ts @@ -0,0 +1,26 @@ +import { extend } from '@vue/shared' +import { DeprecationTypes } from './deprecations' + +export type CompatConfig = Partial< + Record +> + +export interface DeprecationConfigItem { + warning?: boolean // defaults to true + mode?: 2 | 3 // defaults to 2 +} + +const globalCompatConfig: CompatConfig = {} + +export function configureCompat(config: CompatConfig) { + extend(globalCompatConfig, config) +} + +/** + * @internal + */ +export function getCompatConfig( + key: DeprecationTypes +): DeprecationConfigItem | undefined { + return globalCompatConfig[key] +} diff --git a/packages/runtime-core/src/compat/deprecations.ts b/packages/runtime-core/src/compat/deprecations.ts index 73eca672..e345a0b2 100644 --- a/packages/runtime-core/src/compat/deprecations.ts +++ b/packages/runtime-core/src/compat/deprecations.ts @@ -1,37 +1,38 @@ import { isRuntimeOnly } from '../component' import { warn } from '../warning' +import { getCompatConfig } from './compatConfig' export const enum DeprecationTypes { - CONFIG_SILENT, - CONFIG_DEVTOOLS, - CONFIG_KEY_CODES, - CONFIG_PRODUCTION_TIP, - CONFIG_IGNORED_ELEMENTS, + CONFIG_SILENT = 'CONFIG_SILENT', + CONFIG_DEVTOOLS = 'CONFIG_DEVTOOLS', + CONFIG_KEY_CODES = 'CONFIG_KEY_CODES', + CONFIG_PRODUCTION_TIP = 'CONFIG_PRODUCTION_TIP', + CONFIG_IGNORED_ELEMENTS = 'CONFIG_IGNORED_ELEMENTS', - GLOBAL_PROTOTYPE, - GLOBAL_SET, - GLOBAL_DELETE, - GLOBAL_OBSERVABLE, - GLOBAL_DOM_TEMPLATE_MOUNT, + GLOBAL_PROTOTYPE = 'GLOBAL_PROTOTYPE', + GLOBAL_SET = 'GLOBAL_SET', + GLOBAL_DELETE = 'GLOBAL_DELETE', + GLOBAL_OBSERVABLE = 'GLOBAL_OBSERVABLE', + GLOBAL_MOUNT_CONTAINER = 'GLOBAL_MOUNT_CONTAINER', - INSTANCE_SET, - INSTANCE_DELETE, - INSTANCE_MOUNT, - INSTANCE_DESTROY, - INSTANCE_EVENT_EMITTER, - INSTANCE_EVENT_HOOKS, - INSTANCE_CHILDREN, + INSTANCE_SET = 'INSTANCE_SET', + INSTANCE_DELETE = 'INSTANCE_DELETE', + INSTANCE_MOUNT = 'INSTANCE_MOUNT', + INSTANCE_DESTROY = 'INSTANCE_DESTROY', + INSTANCE_EVENT_EMITTER = 'INSTANCE_EVENT_EMITTER', + INSTANCE_EVENT_HOOKS = 'INSTANCE_EVENT_HOOKS', + INSTANCE_CHILDREN = 'INSTANCE_CHILDREN', - OPTIONS_DATA_FN, - OPTIONS_DATA_MERGE, - OPTIONS_BEFORE_DESTROY, - OPTIONS_DESTROYED, + OPTIONS_DATA_FN = 'OPTIONS_DATA_FN', + OPTIONS_DATA_MERGE = 'OPTIONS_DATA_MERGE', + OPTIONS_BEFORE_DESTROY = 'OPTIONS_BEFORE_DESTROY', + OPTIONS_DESTROYED = 'OPTIONS_DESTROYED', - PROPS_DEFAULT_THIS, + PROPS_DEFAULT_THIS = 'PROPS_DEFAULT_THIS', - CUSTOM_DIR, + CUSTOM_DIR = 'CUSTOM_DIR', - V_ON_KEYCODE_MODIFIER + V_ON_KEYCODE_MODIFIER = 'V_ON_KEYCODE_MODIFIER' } type DeprecationData = { @@ -39,7 +40,7 @@ type DeprecationData = { link?: string } -const deprecations: Record = { +const deprecationMessages: Record = { [DeprecationTypes.CONFIG_SILENT]: { message: `config.silent has been removed because it is not good practice to ` + @@ -105,7 +106,7 @@ const deprecations: Record = { link: `https://v3.vuejs.org/api/basic-reactivity.html` }, - [DeprecationTypes.GLOBAL_DOM_TEMPLATE_MOUNT]: { + [DeprecationTypes.GLOBAL_MOUNT_CONTAINER]: { message: `Vue detected directives on the mount container. ` + `In Vue 3, the container is no longer considered part of the template ` + @@ -204,18 +205,30 @@ const deprecations: Record = { const hasWarned: Record = {} +/** + * @internal + */ export function warnDeprecation(key: DeprecationTypes, ...args: any[]) { - if (!__COMPAT__ || !__DEV__) { + if (!__DEV__) { return } + + // check user config + const config = getCompatConfig(key) + if (config && config.warning === false) { + return + } + + // avoid spamming the same message const dupKey = key + args.join('') if (hasWarned[dupKey]) { return } + hasWarned[dupKey] = true - const { message, link } = deprecations[key] + const { message, link } = deprecationMessages[key] warn( - `[DEPRECATION] ${ + `(DEPRECATION ${key}) ${ typeof message === 'function' ? message(...args) : message }${link ? `\n Details: ${link}` : ``}` ) diff --git a/packages/runtime-core/src/compat/global.ts b/packages/runtime-core/src/compat/global.ts index b475a659..b94ac23c 100644 --- a/packages/runtime-core/src/compat/global.ts +++ b/packages/runtime-core/src/compat/global.ts @@ -29,6 +29,7 @@ import { warnDeprecation, DeprecationTypes } from './deprecations' import { version } from '..' import { LegacyConfig } from './globalConfig' import { LegacyDirective } from './customDirective' +import { configureCompat } from './compatConfig' /** * @deprecated the default `Vue` export has been removed in Vue 3. The type for @@ -73,6 +74,8 @@ export type CompatVue = Pick & { * @deprecated filters have been removed from Vue 3. */ filter(name: string, arg: any): null + + configureCompat: typeof configureCompat } export let isCopyingConfig = false @@ -81,11 +84,6 @@ export let isCopyingConfig = false export function createCompatVue( createApp: CreateAppFunction ): CompatVue { - if (!__COMPAT__) { - // @ts-ignore this function will never be called in non-compat mode - return - } - const Vue: CompatVue = function Vue(options: ComponentOptions = {}) { return createCompatApp(options, Vue) } as any @@ -215,6 +213,8 @@ export function createCompatVue( // TODO compiler warning for filters (maybe behavior compat?) }) as any + Vue.configureCompat = configureCompat + return Vue } @@ -306,7 +306,7 @@ export function installCompatMount( for (let i = 0; i < container.attributes.length; i++) { const attr = container.attributes[i] if (attr.name !== 'v-cloak' && /^(v-|:|@)/.test(attr.name)) { - warnDeprecation(DeprecationTypes.GLOBAL_DOM_TEMPLATE_MOUNT) + warnDeprecation(DeprecationTypes.GLOBAL_MOUNT_CONTAINER) break } } diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index 54a589f8..569b51ab 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -282,8 +282,20 @@ export const ssrUtils = (__NODE_JS__ ? _ssrUtils : null) as typeof _ssrUtils // 2.x COMPAT ------------------------------------------------------------------ -// Important: every function exported here must have `if (!__COMPAT__) return` -// checks -export { warnDeprecation, DeprecationTypes } from './compat/deprecations' -export { createCompatVue, CompatVue } from './compat/global' +export { DeprecationTypes } from './compat/deprecations' +export { CompatVue } from './compat/global' export { LegacyConfig } from './compat/globalConfig' + +import { warnDeprecation } from './compat/deprecations' +import { createCompatVue } from './compat/global' +import { getCompatConfig } from './compat/compatConfig' + +const _compatUtils = { + warnDeprecation, + createCompatVue, + getCompatConfig +} + +export const compatUtils = (__COMPAT__ + ? _compatUtils + : null) as typeof _compatUtils diff --git a/packages/runtime-dom/src/directives/vOn.ts b/packages/runtime-dom/src/directives/vOn.ts index 93d62ec6..a9118961 100644 --- a/packages/runtime-dom/src/directives/vOn.ts +++ b/packages/runtime-dom/src/directives/vOn.ts @@ -1,8 +1,8 @@ import { - DeprecationTypes, - warnDeprecation, getCurrentInstance, - LegacyConfig + DeprecationTypes, + LegacyConfig, + compatUtils } from '@vue/runtime-core' import { hyphenate, isArray } from '@vue/shared' @@ -62,7 +62,7 @@ export const withKeys = (fn: Function, modifiers: string[]) => { keyCodes = ((getCurrentInstance()!.appContext .config as any) as LegacyConfig).keyCodes if (__DEV__ && modifiers.some(m => /^\d+$/.test(m))) { - warnDeprecation(DeprecationTypes.V_ON_KEYCODE_MODIFIER) + compatUtils.warnDeprecation(DeprecationTypes.V_ON_KEYCODE_MODIFIER) } } diff --git a/packages/runtime-dom/src/index.ts b/packages/runtime-dom/src/index.ts index 77adc6aa..767b7007 100644 --- a/packages/runtime-dom/src/index.ts +++ b/packages/runtime-dom/src/index.ts @@ -9,8 +9,8 @@ import { App, RootHydrateFunction, isRuntimeOnly, - warnDeprecation, - DeprecationTypes + DeprecationTypes, + compatUtils } from '@vue/runtime-core' import { nodeOps } from './nodeOps' import { patchProp, forcePatchProp } from './patchProp' @@ -78,7 +78,7 @@ export const createApp = ((...args) => { for (let i = 0; i < container.attributes.length; i++) { const attr = container.attributes[i] if (attr.name !== 'v-cloak' && /^(v-|:|@)/.test(attr.name)) { - warnDeprecation(DeprecationTypes.GLOBAL_DOM_TEMPLATE_MOUNT) + compatUtils.warnDeprecation(DeprecationTypes.GLOBAL_MOUNT_CONTAINER) break } } diff --git a/packages/vue-compat/src/index.ts b/packages/vue-compat/src/index.ts index d766d248..09036c5d 100644 --- a/packages/vue-compat/src/index.ts +++ b/packages/vue-compat/src/index.ts @@ -7,7 +7,7 @@ import { RenderFunction, warn, createApp, - createCompatVue + compatUtils } from '@vue/runtime-dom' import { isString, NOOP, generateCodeFrame, extend } from '@vue/shared' import { InternalRenderFunction } from 'packages/runtime-core/src/component' @@ -92,9 +92,10 @@ function compileToFunction( registerRuntimeCompiler(compileToFunction) -const Vue = createCompatVue(createApp) +const Vue = compatUtils.createCompatVue(createApp) Vue.compile = compileToFunction + extend(Vue, runtimeDom) export default Vue diff --git a/packages/vue-compat/src/runtime.ts b/packages/vue-compat/src/runtime.ts index 04b60b36..a9f19cf7 100644 --- a/packages/vue-compat/src/runtime.ts +++ b/packages/vue-compat/src/runtime.ts @@ -1,15 +1,18 @@ // This entry exports the runtime only, and is built as // `dist/vue.esm-bundler.js` which is used by default for bundlers. import { initDev } from './dev' -import { warn } from '@vue/runtime-dom' +import { compatUtils, createApp, warn } from '@vue/runtime-dom' +import { extend } from '@vue/shared' if (__DEV__) { initDev() } -export * from '@vue/runtime-dom' +import * as runtimeDom from '@vue/runtime-dom' -export const compile = () => { +const Vue = compatUtils.createCompatVue(createApp) + +Vue.compile = (() => { if (__DEV__) { warn( `Runtime compilation is not supported in this build of Vue.` + @@ -22,4 +25,8 @@ export const compile = () => { : ``) /* should not happen */ ) } -} +}) as any + +extend(Vue, runtimeDom) + +export default Vue