import { isRuntimeOnly } from '../component' import { warn } from '../warning' import { getCompatConfig } from './compatConfig' export const enum DeprecationTypes { GLOBAL_MOUNT = 'GLOBAL_MOUNT', GLOBAL_MOUNT_CONTAINER = 'GLOBAL_MOUNT_CONTAINER', GLOBAL_EXTEND = 'GLOBAL_EXTEND', GLOBAL_PROTOTYPE = 'GLOBAL_PROTOTYPE', GLOBAL_SET = 'GLOBAL_SET', GLOBAL_DELETE = 'GLOBAL_DELETE', GLOBAL_OBSERVABLE = 'GLOBAL_OBSERVABLE', 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', INSTANCE_SET = 'INSTANCE_SET', INSTANCE_DELETE = 'INSTANCE_DELETE', INSTANCE_DESTROY = 'INSTANCE_DESTROY', INSTANCE_EVENT_EMITTER = 'INSTANCE_EVENT_EMITTER', INSTANCE_EVENT_HOOKS = 'INSTANCE_EVENT_HOOKS', INSTANCE_CHILDREN = 'INSTANCE_CHILDREN', INSTANCE_LISTENERS = 'INSTANCE_LISTENERS', INSTANCE_SCOPED_SLOTS = 'INSTANCE_SCOPED_SLOTS', OPTIONS_DATA_FN = 'OPTIONS_DATA_FN', OPTIONS_DATA_MERGE = 'OPTIONS_DATA_MERGE', OPTIONS_BEFORE_DESTROY = 'OPTIONS_BEFORE_DESTROY', OPTIONS_DESTROYED = 'OPTIONS_DESTROYED', WATCH_ARRAY = 'WATCH_ARRAY', PROPS_DEFAULT_THIS = 'PROPS_DEFAULT_THIS', V_ON_KEYCODE_MODIFIER = 'V_ON_KEYCODE_MODIFIER', CUSTOM_DIR = 'CUSTOM_DIR', ATTR_FALSE_VALUE = 'ATTR_FALSE_VALUE', ATTR_ENUMERATED_COERSION = 'ATTR_ENUMERATED_COERSION', TRANSITION_CLASSES = 'TRANSITION_CLASSES', TRANSITION_GROUP_ROOT = 'TRANSITION_GROUP_ROOT' } type DeprecationData = { message: string | ((...args: any[]) => string) link?: string } const deprecationData: Record = { [DeprecationTypes.GLOBAL_MOUNT]: { message: `The global app bootstrapping API has changed: vm.$mount() and the "el" ` + `option have been removed. Use createApp(RootComponent).mount() instead.`, link: `https://v3.vuejs.org/guide/migration/global-api.html#mounting-app-instance` }, [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 ` + `and will not be processed/replaced.`, link: `https://v3.vuejs.org/guide/migration/mount-changes.html` }, [DeprecationTypes.GLOBAL_EXTEND]: { message: `Vue.extend() has been removed in Vue 3. ` + `Use defineComponent() instead.`, link: `https://v3.vuejs.org/api/global-api.html#definecomponent` }, [DeprecationTypes.GLOBAL_PROTOTYPE]: { message: `Vue.prototype is no longer available in Vue 3. ` + `Use config.globalProperties instead.`, link: `https://v3.vuejs.org/guide/migration/global-api.html#vue-prototype-replaced-by-config-globalproperties` }, [DeprecationTypes.GLOBAL_SET]: { message: `Vue.set() has been removed as it is no longer needed in Vue 3. ` + `Simply use native JavaScript mutations.` }, [DeprecationTypes.GLOBAL_DELETE]: { message: `Vue.delete() has been removed as it is no longer needed in Vue 3. ` + `Simply use native JavaScript mutations.` }, [DeprecationTypes.GLOBAL_OBSERVABLE]: { message: `Vue.observable() has been removed. ` + `Use \`import { reactive } from "vue"\` from Composition API instead.`, link: `https://v3.vuejs.org/api/basic-reactivity.html` }, [DeprecationTypes.CONFIG_SILENT]: { message: `config.silent has been removed because it is not good practice to ` + `intentionally suppress warnings. You can use your browser console's ` + `filter features to focus on relevant messages.` }, [DeprecationTypes.CONFIG_DEVTOOLS]: { message: `config.devtools has been removed. To enable devtools for ` + `production, configure the __VUE_PROD_DEVTOOLS__ compile-time flag.`, link: `https://github.com/vuejs/vue-next/tree/master/packages/vue#bundler-build-feature-flags` }, [DeprecationTypes.CONFIG_KEY_CODES]: { message: `config.keyCodes has been removed. ` + `In Vue 3, you can directly use the kebab-case key names as v-on modifiers.`, link: `https://v3.vuejs.org/guide/migration/keycode-modifiers.html` }, [DeprecationTypes.CONFIG_PRODUCTION_TIP]: { message: `config.productionTip has been removed.`, link: `https://v3.vuejs.org/guide/migration/global-api.html#config-productiontip-removed` }, [DeprecationTypes.CONFIG_IGNORED_ELEMENTS]: { message: () => { let msg = `config.ignoredElements has been removed.` if (isRuntimeOnly()) { msg += ` Pass the "isCustomElement" option to @vue/compiler-dom instead.` } else { msg += ` Use config.isCustomElement instead.` } return msg }, link: `https://v3.vuejs.org/guide/migration/global-api.html#config-ignoredelements-is-now-config-iscustomelement` }, [DeprecationTypes.INSTANCE_SET]: { message: `vm.$set() has been removed as it is no longer needed in Vue 3. ` + `Simply use native JavaScript mutations.` }, [DeprecationTypes.INSTANCE_DELETE]: { message: `vm.$delete() has been removed as it is no longer needed in Vue 3. ` + `Simply use native JavaScript mutations.` }, [DeprecationTypes.INSTANCE_DESTROY]: { message: `vm.$destroy() has been removed. Use app.unmount() instead.`, link: `https://v3.vuejs.org/api/application-api.html#unmount` }, [DeprecationTypes.INSTANCE_EVENT_EMITTER]: { message: `vm.$on/$once/$off() have been removed. ` + `Use an external event emitter library instead.`, link: `https://v3.vuejs.org/guide/migration/events-api.html` }, [DeprecationTypes.INSTANCE_EVENT_HOOKS]: { message: `"hook:x" lifecycle events are no longer supported. ` + `Use Composition API to dynamically register lifecycle hooks.`, link: `https://v3.vuejs.org/api/composition-api.html#lifecycle-hooks` }, [DeprecationTypes.INSTANCE_CHILDREN]: { message: `vm.$children has been removed. Consider refactoring your logic ` + `to avoid relying on direct access to child components.`, link: `https://v3.vuejs.org/guide/migration/children.html` }, [DeprecationTypes.INSTANCE_LISTENERS]: { message: `vm.$listeners has been removed. Parent v-on listeners are now ` + `included in vm.$attrs and it is no longer necessary to separately use ` + `v-on="$listeners" if you are already using v-bind="$attrs".`, link: `https://v3.vuejs.org/guide/migration/listeners-removed.html` }, [DeprecationTypes.INSTANCE_SCOPED_SLOTS]: { message: `vm.$scopedSlots has been removed. Use vm.$slots instead.`, link: `https://v3.vuejs.org/guide/migration/slots-unification.html` }, [DeprecationTypes.OPTIONS_DATA_FN]: { message: `The "data" option can no longer be a plain object. ` + `Always use a function.`, link: `https://v3.vuejs.org/guide/migration/data-option.html` }, [DeprecationTypes.OPTIONS_DATA_MERGE]: { message: (key: string) => `Detected conflicting key "${key}" when merging data option values. ` + `In Vue 3, data keys are merged shallowly and will override one another.`, link: `https://v3.vuejs.org/guide/migration/data-option.html#mixin-merge-behavior-change` }, [DeprecationTypes.OPTIONS_BEFORE_DESTROY]: { message: `\`beforeDestroy\` has been renamed to \`beforeUnmount\`.` }, [DeprecationTypes.OPTIONS_DESTROYED]: { message: `\`destroyed\` has been renamed to \`unmounted\`.` }, [DeprecationTypes.WATCH_ARRAY]: { message: `"watch" option or vm.$watch on an array value will no longer ` + `trigger on array mutation unless the "deep" option is specified. ` + `If current usage is intended, you can disable the compat behavior and ` + `suppress this warning with:` + `\n\n configureCompat({ ${ DeprecationTypes.WATCH_ARRAY }: { enabled: false }})\n`, link: `https://v3.vuejs.org/guide/migration/watch.html` }, [DeprecationTypes.PROPS_DEFAULT_THIS]: { message: (key: string) => `props default value function no longer has access to "this". ` + `(found in prop "${key}")`, link: `https://v3.vuejs.org/guide/migration/props-default-this.html` }, [DeprecationTypes.CUSTOM_DIR]: { message: (legacyHook: string, newHook: string) => `Custom directive hook "${legacyHook}" has been removed. ` + `Use "${newHook}" instead.`, link: `https://v3.vuejs.org/guide/migration/custom-directives.html` }, [DeprecationTypes.V_ON_KEYCODE_MODIFIER]: { message: `Using keyCode as v-on modifier is no longer supported. ` + `Use kebab-case key name modifiers instead.`, link: `https://v3.vuejs.org/guide/migration/keycode-modifiers.html` }, [DeprecationTypes.ATTR_FALSE_VALUE]: { message: (name: string) => `Attribute "${name}" with v-bind value \`false\` will render ` + `${name}="false" instead of removing it in Vue 3. To remove the attribute, ` + `use \`null\` or \`undefined\` instead. If the usage is intended, ` + `you can disable the compat behavior and suppress this warning with:` + `\n\n configureCompat({ ${ DeprecationTypes.ATTR_FALSE_VALUE }: { enabled: false }})\n`, link: `https://v3.vuejs.org/guide/migration/attribute-coercion.html` }, [DeprecationTypes.ATTR_ENUMERATED_COERSION]: { message: (name: string, value: any, coerced: string) => `Enumerated attribute "${name}" with v-bind value \`${value}\` will ` + `${ value === null ? `be removed` : `render the value as-is` } instead of coercing the value to "${coerced}" in Vue 3. ` + `Always use explicit "true" or "false" values for enumerated attributes. ` + `If the usage is intended, ` + `you can disable the compat behavior and suppress this warning with:` + `\n\n configureCompat({ ${ DeprecationTypes.ATTR_ENUMERATED_COERSION }: { enabled: false }})\n`, link: `https://v3.vuejs.org/guide/migration/attribute-coercion.html` }, [DeprecationTypes.TRANSITION_CLASSES]: { message: `` // this feature cannot be runtime-detected }, [DeprecationTypes.TRANSITION_GROUP_ROOT]: { message: ` no longer renders a root element by ` + `default if no "tag" prop is specified. If you do not rely on the span ` + `for styling, you can disable the compat behavior and suppress this ` + `warning with:` + `\n\n configureCompat({ ${ DeprecationTypes.TRANSITION_GROUP_ROOT }: { enabled: false }})\n`, link: `https://v3.vuejs.org/guide/migration/transition-group.html` } } const hasWarned: Record = {} /** * @internal */ export function warnDeprecation(key: DeprecationTypes, ...args: any[]) { if (!__DEV__) { return } // check user config const config = getCompatConfig(key) if ( config && (config.warning === false || (config.enabled === false && config.warning !== true)) ) { return } // avoid spamming the same message const dupKey = key + args.join('') if (hasWarned[dupKey]) { return } hasWarned[dupKey] = true const { message, link } = deprecationData[key] warn( `(deprecation ${key}) ${ typeof message === 'function' ? message(...args) : message }${link ? `\n Details: ${link}` : ``}` ) }