Repository: vuejs/vue
Pull request: Add warnHandler to allow users to set a custom warn callback

Similar to errorHandler, but for warnings. https://vuejs.org/v2/api/#errorHandler

Sorry for not following the "Ideally you should open a suggestion issue first and have it greenlighted before working on it." rule, no loss on my side if you don't want to accept this.

I would like this feature as it will allow Vue to call a user provided callback for `warn`ings which helps for asserting the state of warnings during testing or development, without having to either spyon or monkeypatch the `console.error` function.

**What kind of change does this PR introduce?** (check at least one)

- [ ] Bugfix
- [X] Feature
- [ ] Code style update
- [ ] Refactor
- [ ] Build-related changes
- [ ] Other, please describe:

**Does this PR introduce a breaking change?** (check one)

- [ ] Yes
- [X] No

If yes, please describe the impact and migration path for existing applications:

**The PR fulfills these requirements:**

- [X] It's submitted to the `dev` branch for v2.x (or to a previous version branch), _not_ the `master` branch
- [ ] When resolving a specific issue, it's referenced in the PR's title (e.g. `fix #xxx[,#xxx]`, where "xxx" is the issue number)
- [X] All tests are passing: https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#development-setup
- [X] New/updated tests are included

If adding a **new feature**, the PR's description includes:
- [X] A convincing reason for adding this feature (to avoid wasting your time, it's best to open a suggestion issue first and wait for approval before working on it)

(sorry!)

**Other information:**

jsfiddle example: https://jsfiddle.net/vx0ks9yo/3/

Changed files:
filename: src/core/config.js
patch: @@ -16,6 +16,7 @@ export type Config = {
   performance: boolean;
   devtools: boolean;
   errorHandler: ?(err: Error, vm: Component, info: string) => void;
+  warnHandler: ?(msg: string, vm: Component, trace: string) => void;
   ignoredElements: Array<string>;
   keyCodes: { [key: string]: number | Array<number> };
 
@@ -62,6 +63,11 @@ export default ({
    */
   errorHandler: null,
 
+  /**
+   * Warn handler for watcher warns
+   */
+  warnHandler: null,
+
   /**
    * Ignore certain custom elements
    */

filename: src/core/util/debug.js
patch: @@ -15,10 +15,12 @@ if (process.env.NODE_ENV !== 'production') {
     .replace(/[-_]/g, '')
 
   warn = (msg, vm) => {
-    if (hasConsole && (!config.silent)) {
-      console.error(`[Vue warn]: ${msg}` + (
-        vm ? generateComponentTrace(vm) : ''
-      ))
+    const trace = vm ? generateComponentTrace(vm) : ''
+
+    if (config.warnHandler) {
+      config.warnHandler.call(null, msg, vm, trace)
+    } else if (hasConsole && (!config.silent)) {
+      console.error(`[Vue warn]: ${msg}${trace}`)
     }
   }
 

filename: test/unit/features/debug.spec.js
patch: @@ -1,5 +1,5 @@
 import Vue from 'vue'
-import { formatComponentName } from 'core/util/debug'
+import { formatComponentName, warn } from 'core/util/debug'
 
 describe('Debug utilities', () => {
   it('properly format component names', () => {
@@ -80,4 +80,38 @@ found in
            <Root>`
     ).toHaveBeenWarned()
   })
+
+  describe('warn', () => {
+    const msg = 'message'
+    const vm = new Vue()
+
+    it('calls warnHandler if warnHandler is set', () => {
+      Vue.config.warnHandler = jasmine.createSpy()
+
+      warn(msg, vm)
+
+      expect(Vue.config.warnHandler).toHaveBeenCalledWith(msg, vm, jasmine.any(String))
+
+      Vue.config.warnHandler = null
+    })
+
+    it('calls console.error if silent is false', () => {
+      Vue.config.silent = false
+
+      warn(msg, vm)
+
+      expect(msg).toHaveBeenWarned()
+      expect(console.error).toHaveBeenCalled()
+    })
+
+    it('does not call console.error if silent is true', () => {
+      Vue.config.silent = true
+
+      warn(msg, vm)
+
+      expect(console.error).not.toHaveBeenCalled()
+
+      Vue.config.silent = false
+    })
+  })
 })

filename: types/test/vue-test.ts
patch: @@ -63,6 +63,12 @@ class Test extends Vue {
         vm.testMethods();
       }
     };
+    config.warnHandler = (msg, vm) => {
+      if (vm instanceof Test) {
+        vm.testProperties();
+        vm.testMethods();
+      }
+    };
     config.keyCodes = { esc: 27 };
   }
 

filename: types/vue.d.ts
patch: @@ -75,6 +75,7 @@ export declare class Vue {
     productionTip: boolean;
     performance: boolean;
     errorHandler(err: Error, vm: Vue, info: string): void;
+    warnHandler(msg: string, vm: Vue, trace: string): void;
     ignoredElements: string[];
     keyCodes: { [key: string]: number };
   }