From 0e0e386a3feec0d9868c1f2dcb0cc08a059e0239 Mon Sep 17 00:00:00 2001 From: lokson Date: Wed, 10 May 2017 00:59:14 +0200 Subject: [PATCH 1/8] warn when component should be an object, but is not --- src/core/instance/state.js | 22 ++++++++++++++++++++- test/unit/features/options/computed.spec.js | 7 +++++++ test/unit/features/options/methods.spec.js | 7 +++++++ test/unit/features/options/watch.spec.js | 7 +++++++ 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/core/instance/state.js b/src/core/instance/state.js index d2d8fa14998..8772379829a 100644 --- a/src/core/instance/state.js +++ b/src/core/instance/state.js @@ -148,8 +148,14 @@ function getData (data: Function, vm: Component): any { const computedWatcherOptions = { lazy: true } function initComputed (vm: Component, computed: Object) { + if (!isPlainObject(computed)) { + computed = {} + process.env.NODE_ENV !== 'production' && warn( + 'computed should be an object.', + vm + ) + } const watchers = vm._computedWatchers = Object.create(null) - for (const key in computed) { const userDef = computed[key] let getter = typeof userDef === 'function' ? userDef : userDef.get @@ -213,6 +219,13 @@ function createComputedGetter (key) { } function initMethods (vm: Component, methods: Object) { + if (!isPlainObject(methods)) { + methods = {} + process.env.NODE_ENV !== 'production' && warn( + 'methods should be an object.', + vm + ) + } const props = vm.$options.props for (const key in methods) { vm[key] = methods[key] == null ? noop : bind(methods[key], vm) @@ -235,6 +248,13 @@ function initMethods (vm: Component, methods: Object) { } function initWatch (vm: Component, watch: Object) { + if (!isPlainObject(watch)) { + watch = {} + process.env.NODE_ENV !== 'production' && warn( + 'watch should be an object.', + vm + ) + } for (const key in watch) { const handler = watch[key] if (Array.isArray(handler)) { diff --git a/test/unit/features/options/computed.spec.js b/test/unit/features/options/computed.spec.js index d4515b011ba..e9cdc91d288 100644 --- a/test/unit/features/options/computed.spec.js +++ b/test/unit/features/options/computed.spec.js @@ -48,6 +48,13 @@ describe('Options computed', () => { }).then(done) }) + it('warn non object', () => { + new Vue({ + computed: 'wrong' + }) + expect('computed should be an object').toHaveBeenWarned() + }) + it('warn with setter and no getter', () => { const vm = new Vue({ template: ` diff --git a/test/unit/features/options/methods.spec.js b/test/unit/features/options/methods.spec.js index 3e6991144be..d6bf0abc688 100644 --- a/test/unit/features/options/methods.spec.js +++ b/test/unit/features/options/methods.spec.js @@ -24,4 +24,11 @@ describe('Options methods', () => { }) expect(`method "hello" has an undefined value in the component definition`).toHaveBeenWarned() }) + + it('should warn non object', () => { + new Vue({ + methods: 'wrong' + }) + expect('methods should be an object').toHaveBeenWarned() + }) }) diff --git a/test/unit/features/options/watch.spec.js b/test/unit/features/options/watch.spec.js index e0111d184c0..c4496e9602c 100644 --- a/test/unit/features/options/watch.spec.js +++ b/test/unit/features/options/watch.spec.js @@ -23,6 +23,13 @@ describe('Options watch', () => { }).then(done) }) + it('warn non object', () => { + new Vue({ + watch: 'wrong' + }) + expect('watch should be an object').toHaveBeenWarned() + }) + it('string method name', done => { const vm = new Vue({ data: { From 340f6fb56d3a2af6d39940bd5fea701b4f97760c Mon Sep 17 00:00:00 2001 From: lokson Date: Wed, 10 May 2017 14:27:13 +0200 Subject: [PATCH 2/8] remarks --- src/core/instance/state.js | 35 +++++++++------------ test/unit/features/options/computed.spec.js | 7 +++++ test/unit/features/options/methods.spec.js | 7 +++++ test/unit/features/options/watch.spec.js | 7 +++++ 4 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/core/instance/state.js b/src/core/instance/state.js index 8772379829a..f2582770124 100644 --- a/src/core/instance/state.js +++ b/src/core/instance/state.js @@ -60,6 +60,17 @@ const isReservedProp = { slot: 1 } +function parseObjectOption (vm: Component, option: Object, name: string) { + if (!isPlainObject(option)) { + process.env.NODE_ENV !== 'production' && warn( + `${name} should be an object.`, + vm + ) + return {} + } + return option +} + function initProps (vm: Component, propsOptions: Object) { const propsData = vm.$options.propsData || {} const props = vm._props = {} @@ -148,13 +159,7 @@ function getData (data: Function, vm: Component): any { const computedWatcherOptions = { lazy: true } function initComputed (vm: Component, computed: Object) { - if (!isPlainObject(computed)) { - computed = {} - process.env.NODE_ENV !== 'production' && warn( - 'computed should be an object.', - vm - ) - } + computed = parseObjectOption(vm, computed, 'computed') const watchers = vm._computedWatchers = Object.create(null) for (const key in computed) { const userDef = computed[key] @@ -219,13 +224,7 @@ function createComputedGetter (key) { } function initMethods (vm: Component, methods: Object) { - if (!isPlainObject(methods)) { - methods = {} - process.env.NODE_ENV !== 'production' && warn( - 'methods should be an object.', - vm - ) - } + methods = parseObjectOption(vm, methods, 'methods') const props = vm.$options.props for (const key in methods) { vm[key] = methods[key] == null ? noop : bind(methods[key], vm) @@ -248,13 +247,7 @@ function initMethods (vm: Component, methods: Object) { } function initWatch (vm: Component, watch: Object) { - if (!isPlainObject(watch)) { - watch = {} - process.env.NODE_ENV !== 'production' && warn( - 'watch should be an object.', - vm - ) - } + watch = parseObjectOption(vm, watch, 'watch') for (const key in watch) { const handler = watch[key] if (Array.isArray(handler)) { diff --git a/test/unit/features/options/computed.spec.js b/test/unit/features/options/computed.spec.js index e9cdc91d288..0f67a22ee86 100644 --- a/test/unit/features/options/computed.spec.js +++ b/test/unit/features/options/computed.spec.js @@ -55,6 +55,13 @@ describe('Options computed', () => { expect('computed should be an object').toHaveBeenWarned() }) + it('don\'t warn when is an object', () => { + new Vue({ + computed: {} + }) + expect('computed should be an object').not.toHaveBeenWarned() + }) + it('warn with setter and no getter', () => { const vm = new Vue({ template: ` diff --git a/test/unit/features/options/methods.spec.js b/test/unit/features/options/methods.spec.js index d6bf0abc688..c38c1354a8e 100644 --- a/test/unit/features/options/methods.spec.js +++ b/test/unit/features/options/methods.spec.js @@ -31,4 +31,11 @@ describe('Options methods', () => { }) expect('methods should be an object').toHaveBeenWarned() }) + + it('don\'t warn when is an object', () => { + new Vue({ + methods: {} + }) + expect('computed should be an object').not.toHaveBeenWarned() + }) }) diff --git a/test/unit/features/options/watch.spec.js b/test/unit/features/options/watch.spec.js index c4496e9602c..d650e081804 100644 --- a/test/unit/features/options/watch.spec.js +++ b/test/unit/features/options/watch.spec.js @@ -30,6 +30,13 @@ describe('Options watch', () => { expect('watch should be an object').toHaveBeenWarned() }) + it('don\'t warn when is an object', () => { + new Vue({ + watch: {} + }) + expect('computed should be an object').not.toHaveBeenWarned() + }) + it('string method name', done => { const vm = new Vue({ data: { From e9001a1bca5a13079f8661f30d55bdfa50e6f67c Mon Sep 17 00:00:00 2001 From: lokson Date: Wed, 10 May 2017 15:08:58 +0200 Subject: [PATCH 3/8] remarks --- test/helpers/test-object-option.js | 17 +++++++++++++++++ test/unit/features/options/computed.spec.js | 15 ++------------- test/unit/features/options/methods.spec.js | 17 +++-------------- test/unit/features/options/watch.spec.js | 15 ++------------- 4 files changed, 24 insertions(+), 40 deletions(-) create mode 100644 test/helpers/test-object-option.js diff --git a/test/helpers/test-object-option.js b/test/helpers/test-object-option.js new file mode 100644 index 00000000000..e7943427013 --- /dev/null +++ b/test/helpers/test-object-option.js @@ -0,0 +1,17 @@ +import Vue from 'vue' + +export default function testObjectOption (name: string) { + it('should warn non object', () => { + const options = {} + options[name] = 'wrong' + new Vue(options) + expect(`${name} should be an object`).toHaveBeenWarned() + }) + + it('don\'t warn when is an object', () => { + const options = {} + options[name] = {} + new Vue(options) + expect(`${name} should be an object`).not.toHaveBeenWarned() + }) +} diff --git a/test/unit/features/options/computed.spec.js b/test/unit/features/options/computed.spec.js index 0f67a22ee86..a48c7ed13b7 100644 --- a/test/unit/features/options/computed.spec.js +++ b/test/unit/features/options/computed.spec.js @@ -1,4 +1,5 @@ import Vue from 'vue' +import testObjectOption from '../../../helpers/test-object-option' describe('Options computed', () => { it('basic usage', done => { @@ -48,19 +49,7 @@ describe('Options computed', () => { }).then(done) }) - it('warn non object', () => { - new Vue({ - computed: 'wrong' - }) - expect('computed should be an object').toHaveBeenWarned() - }) - - it('don\'t warn when is an object', () => { - new Vue({ - computed: {} - }) - expect('computed should be an object').not.toHaveBeenWarned() - }) + testObjectOption('computed') it('warn with setter and no getter', () => { const vm = new Vue({ diff --git a/test/unit/features/options/methods.spec.js b/test/unit/features/options/methods.spec.js index c38c1354a8e..66039c968d7 100644 --- a/test/unit/features/options/methods.spec.js +++ b/test/unit/features/options/methods.spec.js @@ -1,4 +1,5 @@ import Vue from 'vue' +import testObjectOption from '../../../helpers/test-object-option' describe('Options methods', () => { it('should have correct context', () => { @@ -16,6 +17,8 @@ describe('Options methods', () => { expect(vm.a).toBe(2) }) + testObjectOption('methods') + it('should warn undefined methods', () => { new Vue({ methods: { @@ -24,18 +27,4 @@ describe('Options methods', () => { }) expect(`method "hello" has an undefined value in the component definition`).toHaveBeenWarned() }) - - it('should warn non object', () => { - new Vue({ - methods: 'wrong' - }) - expect('methods should be an object').toHaveBeenWarned() - }) - - it('don\'t warn when is an object', () => { - new Vue({ - methods: {} - }) - expect('computed should be an object').not.toHaveBeenWarned() - }) }) diff --git a/test/unit/features/options/watch.spec.js b/test/unit/features/options/watch.spec.js index d650e081804..702cd2b8c4e 100644 --- a/test/unit/features/options/watch.spec.js +++ b/test/unit/features/options/watch.spec.js @@ -1,4 +1,5 @@ import Vue from 'vue' +import testObjectOption from '../../../helpers/test-object-option' describe('Options watch', () => { let spy @@ -23,19 +24,7 @@ describe('Options watch', () => { }).then(done) }) - it('warn non object', () => { - new Vue({ - watch: 'wrong' - }) - expect('watch should be an object').toHaveBeenWarned() - }) - - it('don\'t warn when is an object', () => { - new Vue({ - watch: {} - }) - expect('computed should be an object').not.toHaveBeenWarned() - }) + testObjectOption('watch') it('string method name', done => { const vm = new Vue({ From 978cecb27e8f57420221c2570fc464313da8881e Mon Sep 17 00:00:00 2001 From: lokson Date: Wed, 10 May 2017 15:48:28 +0200 Subject: [PATCH 4/8] remarks --- src/core/instance/state.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/core/instance/state.js b/src/core/instance/state.js index f2582770124..bda43d8ac4f 100644 --- a/src/core/instance/state.js +++ b/src/core/instance/state.js @@ -60,7 +60,8 @@ const isReservedProp = { slot: 1 } -function parseObjectOption (vm: Component, option: Object, name: string) { +function parseObjectOption (vm: Component, name: string) { + const option = vm.$options[name] if (!isPlainObject(option)) { process.env.NODE_ENV !== 'production' && warn( `${name} should be an object.`, @@ -159,7 +160,7 @@ function getData (data: Function, vm: Component): any { const computedWatcherOptions = { lazy: true } function initComputed (vm: Component, computed: Object) { - computed = parseObjectOption(vm, computed, 'computed') + computed = parseObjectOption(vm, 'computed') const watchers = vm._computedWatchers = Object.create(null) for (const key in computed) { const userDef = computed[key] @@ -224,7 +225,7 @@ function createComputedGetter (key) { } function initMethods (vm: Component, methods: Object) { - methods = parseObjectOption(vm, methods, 'methods') + methods = parseObjectOption(vm, 'methods') const props = vm.$options.props for (const key in methods) { vm[key] = methods[key] == null ? noop : bind(methods[key], vm) @@ -247,7 +248,7 @@ function initMethods (vm: Component, methods: Object) { } function initWatch (vm: Component, watch: Object) { - watch = parseObjectOption(vm, watch, 'watch') + watch = parseObjectOption(vm, 'watch') for (const key in watch) { const handler = watch[key] if (Array.isArray(handler)) { From ff905f340419ab24279ba8940092dd28afb43129 Mon Sep 17 00:00:00 2001 From: lokson Date: Wed, 10 May 2017 16:07:49 +0200 Subject: [PATCH 5/8] rename to checkOptionType and guard production --- src/core/instance/state.js | 12 +++++------- test/helpers/test-object-option.js | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/core/instance/state.js b/src/core/instance/state.js index bda43d8ac4f..01c86fb6f3c 100644 --- a/src/core/instance/state.js +++ b/src/core/instance/state.js @@ -60,16 +60,14 @@ const isReservedProp = { slot: 1 } -function parseObjectOption (vm: Component, name: string) { +function checkOptionType (vm: Component, name: string) { const option = vm.$options[name] if (!isPlainObject(option)) { - process.env.NODE_ENV !== 'production' && warn( + warn( `${name} should be an object.`, vm ) - return {} } - return option } function initProps (vm: Component, propsOptions: Object) { @@ -160,7 +158,7 @@ function getData (data: Function, vm: Component): any { const computedWatcherOptions = { lazy: true } function initComputed (vm: Component, computed: Object) { - computed = parseObjectOption(vm, 'computed') + process.env.NODE_ENV !== 'production' && checkOptionType(vm, 'computed') const watchers = vm._computedWatchers = Object.create(null) for (const key in computed) { const userDef = computed[key] @@ -225,7 +223,7 @@ function createComputedGetter (key) { } function initMethods (vm: Component, methods: Object) { - methods = parseObjectOption(vm, 'methods') + process.env.NODE_ENV !== 'production' && checkOptionType(vm, 'methods') const props = vm.$options.props for (const key in methods) { vm[key] = methods[key] == null ? noop : bind(methods[key], vm) @@ -248,7 +246,7 @@ function initMethods (vm: Component, methods: Object) { } function initWatch (vm: Component, watch: Object) { - watch = parseObjectOption(vm, 'watch') + process.env.NODE_ENV !== 'production' && checkOptionType(vm, 'watch') for (const key in watch) { const handler = watch[key] if (Array.isArray(handler)) { diff --git a/test/helpers/test-object-option.js b/test/helpers/test-object-option.js index e7943427013..87ed2621651 100644 --- a/test/helpers/test-object-option.js +++ b/test/helpers/test-object-option.js @@ -3,7 +3,7 @@ import Vue from 'vue' export default function testObjectOption (name: string) { it('should warn non object', () => { const options = {} - options[name] = 'wrong' + options[name] = () => {} new Vue(options) expect(`${name} should be an object`).toHaveBeenWarned() }) From 3471d0b2866f64a27ae7a565d7be231ef03f048e Mon Sep 17 00:00:00 2001 From: lokson Date: Wed, 10 May 2017 23:21:36 +0200 Subject: [PATCH 6/8] typo --- src/core/instance/state.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/instance/state.js b/src/core/instance/state.js index 01c86fb6f3c..ab82908787e 100644 --- a/src/core/instance/state.js +++ b/src/core/instance/state.js @@ -160,6 +160,7 @@ const computedWatcherOptions = { lazy: true } function initComputed (vm: Component, computed: Object) { process.env.NODE_ENV !== 'production' && checkOptionType(vm, 'computed') const watchers = vm._computedWatchers = Object.create(null) + for (const key in computed) { const userDef = computed[key] let getter = typeof userDef === 'function' ? userDef : userDef.get From 320990b7c06a9ab86e11874bd0b318e533f9dd94 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Thu, 11 May 2017 11:32:20 +0200 Subject: [PATCH 7/8] Update state.js --- src/core/instance/state.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/instance/state.js b/src/core/instance/state.js index ab82908787e..81306663f77 100644 --- a/src/core/instance/state.js +++ b/src/core/instance/state.js @@ -64,7 +64,7 @@ function checkOptionType (vm: Component, name: string) { const option = vm.$options[name] if (!isPlainObject(option)) { warn( - `${name} should be an object.`, + `component option "${name}" should be an object.`, vm ) } From c5301b7237f7448ba3d218c1d359a2c9e905ed50 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Thu, 11 May 2017 11:35:32 +0200 Subject: [PATCH 8/8] Update test-object-option.js --- test/helpers/test-object-option.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/helpers/test-object-option.js b/test/helpers/test-object-option.js index 87ed2621651..1012ea10b44 100644 --- a/test/helpers/test-object-option.js +++ b/test/helpers/test-object-option.js @@ -5,13 +5,13 @@ export default function testObjectOption (name: string) { const options = {} options[name] = () => {} new Vue(options) - expect(`${name} should be an object`).toHaveBeenWarned() + expect(`component option "${name}" should be an object`).toHaveBeenWarned() }) it('don\'t warn when is an object', () => { const options = {} options[name] = {} new Vue(options) - expect(`${name} should be an object`).not.toHaveBeenWarned() + expect(`component option "${name}" should be an object`).not.toHaveBeenWarned() }) }