From a13fc5a6f94bab2fd14c6f2a827188cf0d29917a Mon Sep 17 00:00:00 2001 From: Gleb Ivanov Date: Mon, 19 Feb 2018 18:49:00 +0400 Subject: [PATCH 1/6] Added reflection support --- dist/vue-class-component.common.js | 28 ++++++++++++++++++++++-- dist/vue-class-component.js | 35 +++++++++++++++++++++++++----- dist/vue-class-component.min.js | 2 +- package-lock.json | 6 +++++ package.json | 3 +++ src/component.ts | 20 +++++++++++++++-- src/reflect.ts | 29 +++++++++++++++++++++++++ test/test.ts | 20 +++++++++++++++++ 8 files changed, 132 insertions(+), 11 deletions(-) create mode 100644 src/reflect.ts diff --git a/dist/vue-class-component.common.js b/dist/vue-class-component.common.js index 6b1763c..478cc13 100644 --- a/dist/vue-class-component.common.js +++ b/dist/vue-class-component.common.js @@ -9,8 +9,25 @@ Object.defineProperty(exports, '__esModule', { value: true }); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } +require('reflect-metadata'); var Vue = _interopDefault(require('vue')); +function copyReflectionMetadata(from, to, reflectionMap) { + shallowCopy(from.prototype, to.prototype, reflectionMap.instance); + shallowCopy(from, to, reflectionMap.static); +} +function shallowCopy(from, to, propertyKeys) { + var _loop_1 = function (propertyKey) { + propertyKeys[propertyKey].forEach(function (metadataKey) { + var metadata = Reflect.getOwnMetadata(metadataKey, from, propertyKey); + Reflect.defineMetadata(metadataKey, metadata, to, propertyKey); + }); + }; + for (var propertyKey in propertyKeys) { + _loop_1(propertyKey); + } +} + var hasProto = { __proto__: [] } instanceof Array; function createDecorator(factory) { return function (target, key, index) { @@ -105,6 +122,10 @@ var $internalHooks = [ ]; function componentFactory(Component, options) { if (options === void 0) { options = {}; } + var reflectionMap = { + instance: {}, + static: {} + }; options.name = options.name || Component._componentTag || Component.name; // prototype props. var proto = Component.prototype; @@ -112,6 +133,7 @@ function componentFactory(Component, options) { if (key === 'constructor') { return; } + reflectionMap.instance[key] = Reflect.getOwnMetadataKeys(proto, key); // hooks if ($internalHooks.indexOf(key) > -1) { options[key] = proto[key]; @@ -147,7 +169,8 @@ function componentFactory(Component, options) { ? superProto.constructor : Vue; var Extended = Super.extend(options); - forwardStaticMembers(Extended, Component, Super); + forwardStaticMembersAndCollectReflection(Extended, Component, Super, reflectionMap); + copyReflectionMetadata(Component, Extended, reflectionMap); return Extended; } var reservedPropertyNames = [ @@ -165,13 +188,14 @@ var reservedPropertyNames = [ 'directive', 'filter' ]; -function forwardStaticMembers(Extended, Original, Super) { +function forwardStaticMembersAndCollectReflection(Extended, Original, Super, reflectionMap) { // We have to use getOwnPropertyNames since Babel registers methods as non-enumerable Object.getOwnPropertyNames(Original).forEach(function (key) { // `prototype` should not be overwritten if (key === 'prototype') { return; } + reflectionMap.static[key] = Reflect.getOwnMetadataKeys(Original, key); // Some browsers does not allow reconfigure built-in properties var extendedDescriptor = Object.getOwnPropertyDescriptor(Extended, key); if (extendedDescriptor && !extendedDescriptor.configurable) { diff --git a/dist/vue-class-component.js b/dist/vue-class-component.js index 6a22752..9cfbc01 100644 --- a/dist/vue-class-component.js +++ b/dist/vue-class-component.js @@ -4,13 +4,29 @@ * @license MIT */ (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('vue')) : - typeof define === 'function' && define.amd ? define(['exports', 'vue'], factory) : - (factory((global.VueClassComponent = {}),global.Vue)); -}(this, (function (exports,Vue) { 'use strict'; + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('reflect-metadata'), require('vue')) : + typeof define === 'function' && define.amd ? define(['exports', 'reflect-metadata', 'vue'], factory) : + (factory((global.VueClassComponent = {}),null,global.Vue)); +}(this, (function (exports,reflectMetadata,Vue) { 'use strict'; Vue = Vue && Vue.hasOwnProperty('default') ? Vue['default'] : Vue; +function copyReflectionMetadata(from, to, reflectionMap) { + shallowCopy(from.prototype, to.prototype, reflectionMap.instance); + shallowCopy(from, to, reflectionMap.static); +} +function shallowCopy(from, to, propertyKeys) { + var _loop_1 = function (propertyKey) { + propertyKeys[propertyKey].forEach(function (metadataKey) { + var metadata = Reflect.getOwnMetadata(metadataKey, from, propertyKey); + Reflect.defineMetadata(metadataKey, metadata, to, propertyKey); + }); + }; + for (var propertyKey in propertyKeys) { + _loop_1(propertyKey); + } +} + var hasProto = { __proto__: [] } instanceof Array; function createDecorator(factory) { return function (target, key, index) { @@ -105,6 +121,10 @@ var $internalHooks = [ ]; function componentFactory(Component, options) { if (options === void 0) { options = {}; } + var reflectionMap = { + instance: {}, + static: {} + }; options.name = options.name || Component._componentTag || Component.name; // prototype props. var proto = Component.prototype; @@ -112,6 +132,7 @@ function componentFactory(Component, options) { if (key === 'constructor') { return; } + reflectionMap.instance[key] = Reflect.getOwnMetadataKeys(proto, key); // hooks if ($internalHooks.indexOf(key) > -1) { options[key] = proto[key]; @@ -147,7 +168,8 @@ function componentFactory(Component, options) { ? superProto.constructor : Vue; var Extended = Super.extend(options); - forwardStaticMembers(Extended, Component, Super); + forwardStaticMembersAndCollectReflection(Extended, Component, Super, reflectionMap); + copyReflectionMetadata(Component, Extended, reflectionMap); return Extended; } var reservedPropertyNames = [ @@ -165,13 +187,14 @@ var reservedPropertyNames = [ 'directive', 'filter' ]; -function forwardStaticMembers(Extended, Original, Super) { +function forwardStaticMembersAndCollectReflection(Extended, Original, Super, reflectionMap) { // We have to use getOwnPropertyNames since Babel registers methods as non-enumerable Object.getOwnPropertyNames(Original).forEach(function (key) { // `prototype` should not be overwritten if (key === 'prototype') { return; } + reflectionMap.static[key] = Reflect.getOwnMetadataKeys(Original, key); // Some browsers does not allow reconfigure built-in properties var extendedDescriptor = Object.getOwnPropertyDescriptor(Extended, key); if (extendedDescriptor && !extendedDescriptor.configurable) { diff --git a/dist/vue-class-component.min.js b/dist/vue-class-component.min.js index 4dbf8b4..0ce2e17 100644 --- a/dist/vue-class-component.min.js +++ b/dist/vue-class-component.min.js @@ -3,4 +3,4 @@ * (c) 2015-present Evan You * @license MIT */ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("vue")):"function"==typeof define&&define.amd?define(["exports","vue"],t):t(e.VueClassComponent={},e.Vue)}(this,function(e,t){"use strict";t=t&&t.hasOwnProperty("default")?t.default:t;var r={__proto__:[]}instanceof Array;var o=["data","beforeCreate","created","beforeMount","mounted","beforeDestroy","destroyed","beforeUpdate","updated","activated","deactivated","render","errorCaptured"];function n(e,n){void 0===n&&(n={}),n.name=n.name||e._componentTag||e.name;var i=e.prototype;Object.getOwnPropertyNames(i).forEach(function(e){if("constructor"!==e)if(o.indexOf(e)>-1)n[e]=i[e];else{var t=Object.getOwnPropertyDescriptor(i,e);"function"==typeof t.value?(n.methods||(n.methods={}))[e]=t.value:(t.get||t.set)&&((n.computed||(n.computed={}))[e]={get:t.get,set:t.set})}}),(n.mixins||(n.mixins=[])).push({data:function(){return function(e,t){var r=t.prototype._init;t.prototype._init=function(){var t=this,r=Object.getOwnPropertyNames(e);if(e.$options.props)for(var o in e.$options.props)e.hasOwnProperty(o)||r.push(o);r.forEach(function(r){"_"!==r.charAt(0)&&Object.defineProperty(t,r,{get:function(){return e[r]},set:function(t){return e[r]=t},configurable:!0})})};var o=new t;t.prototype._init=r;var n={};return Object.keys(o).forEach(function(e){void 0!==o[e]&&(n[e]=o[e])}),n}(this,e)}});var c=e.__decorators__;c&&(c.forEach(function(e){return e(n)}),delete e.__decorators__);var u,a,f,p=Object.getPrototypeOf(e.prototype),s=p instanceof t?p.constructor:t,d=s.extend(n);return u=d,a=e,f=s,Object.getOwnPropertyNames(a).forEach(function(e){if("prototype"!==e){var t=Object.getOwnPropertyDescriptor(u,e);if(!t||t.configurable){var o,n,i=Object.getOwnPropertyDescriptor(a,e);if(!r){if("cid"===e)return;var c=Object.getOwnPropertyDescriptor(f,e);if(o=i.value,n=typeof o,null!=o&&("object"===n||"function"===n)&&c&&c.value===i.value)return}Object.defineProperty(u,e,i)}}}),d}function i(e){return"function"==typeof e?n(e):function(t){return n(t,e)}}!function(e){(i||(i={})).registerHooks=function(e){o.push.apply(o,e)}}();var c=i;e.default=c,e.createDecorator=function(e){return function(t,r,o){var n="function"==typeof t?t:t.constructor;n.__decorators__||(n.__decorators__=[]),"number"!=typeof o&&(o=void 0),n.__decorators__.push(function(t){return e(t,r,o)})}},e.mixins=function(){for(var e=[],r=0;r-1)t[e]=i[e];else{var r=Object.getOwnPropertyDescriptor(i,e);"function"==typeof r.value?(t.methods||(t.methods={}))[e]=r.value:(r.get||r.set)&&((t.computed||(t.computed={}))[e]={get:r.get,set:r.set})}}),(t.mixins||(t.mixins=[])).push({data:function(){return function(e,t){var r=t.prototype._init;t.prototype._init=function(){var t=this,r=Object.getOwnPropertyNames(e);if(e.$options.props)for(var o in e.$options.props)e.hasOwnProperty(o)||r.push(o);r.forEach(function(r){"_"!==r.charAt(0)&&Object.defineProperty(t,r,{get:function(){return e[r]},set:function(t){return e[r]=t},configurable:!0})})};var o=new t;t.prototype._init=r;var n={};return Object.keys(o).forEach(function(e){void 0!==o[e]&&(n[e]=o[e])}),n}(this,e)}});var f=e.__decorators__;f&&(f.forEach(function(e){return e(t)}),delete e.__decorators__);var u,p,s,d,y,v,_,l=Object.getPrototypeOf(e.prototype),O=l instanceof r?l.constructor:r,m=O.extend(t);return u=m,p=e,s=O,d=c,Object.getOwnPropertyNames(p).forEach(function(e){if("prototype"!==e){d.static[e]=Reflect.getOwnMetadataKeys(p,e);var t=Object.getOwnPropertyDescriptor(u,e);if(!t||t.configurable){var r,o,a=Object.getOwnPropertyDescriptor(p,e);if(!n){if("cid"===e)return;var c=Object.getOwnPropertyDescriptor(s,e);if(r=a.value,o=typeof r,null!=r&&("object"===o||"function"===o)&&c&&c.value===a.value)return}Object.defineProperty(u,e,a)}}}),v=m,_=c,o((y=e).prototype,v.prototype,_.instance),o(y,v,_.static),m}function i(e){return"function"==typeof e?c(e):function(t){return c(t,e)}}!function(e){(i||(i={})).registerHooks=function(e){a.push.apply(a,e)}}();var f=i;e.default=f,e.createDecorator=function(e){return function(t,r,o){var n="function"==typeof t?t:t.constructor;n.__decorators__||(n.__decorators__=[]),"number"!=typeof o&&(o=void 0),n.__decorators__.push(function(t){return e(t,r,o)})}},e.mixins=function(){for(var e=[],t=0;t, options: ComponentOptions = {} ): VueClass { + const reflectionMap: ReflectionMap = { + instance: {}, + static: {} + }; + options.name = options.name || (Component as any)._componentTag || (Component as any).name // prototype props. const proto = Component.prototype @@ -30,6 +37,7 @@ export function componentFactory ( if (key === 'constructor') { return } + reflectionMap.instance[key] = Reflect.getOwnMetadataKeys(proto, key); // hooks if ($internalHooks.indexOf(key) > -1) { options[key] = proto[key] @@ -69,7 +77,8 @@ export function componentFactory ( : Vue const Extended = Super.extend(options) - forwardStaticMembers(Extended, Component, Super) + forwardStaticMembersAndCollectReflection(Extended, Component, Super, reflectionMap) + copyReflectionMetadata(Component, Extended, reflectionMap) return Extended } @@ -93,7 +102,12 @@ const reservedPropertyNames = [ 'filter' ] -function forwardStaticMembers (Extended: typeof Vue, Original: typeof Vue, Super: typeof Vue): void { +function forwardStaticMembersAndCollectReflection ( + Extended: typeof Vue, + Original: typeof Vue, + Super: typeof Vue, + reflectionMap: ReflectionMap +): void { // We have to use getOwnPropertyNames since Babel registers methods as non-enumerable Object.getOwnPropertyNames(Original).forEach(key => { // `prototype` should not be overwritten @@ -101,6 +115,8 @@ function forwardStaticMembers (Extended: typeof Vue, Original: typeof Vue, Super return } + reflectionMap.static[key] = Reflect.getOwnMetadataKeys(Original, key); + // Some browsers does not allow reconfigure built-in properties const extendedDescriptor = Object.getOwnPropertyDescriptor(Extended, key) if (extendedDescriptor && !extendedDescriptor.configurable) { diff --git a/src/reflect.ts b/src/reflect.ts new file mode 100644 index 0000000..f13fe8a --- /dev/null +++ b/src/reflect.ts @@ -0,0 +1,29 @@ +import { VueConstructor } from 'vue' +import 'reflect-metadata' + +export type StringToArrayMap = { + [key: string]: Array +} + +export type ReflectionMap = { + instance: StringToArrayMap, + static: StringToArrayMap +} + +export function copyReflectionMetadata( + from: VueConstructor, + to: VueConstructor, + reflectionMap: ReflectionMap +) { + shallowCopy(from.prototype, to.prototype, reflectionMap.instance); + shallowCopy(from, to, reflectionMap.static); +} + +function shallowCopy(from: VueConstructor, to: VueConstructor, propertyKeys: StringToArrayMap) { + for (const propertyKey in propertyKeys) { + propertyKeys[propertyKey].forEach((metadataKey) => { + const metadata = Reflect.getOwnMetadata(metadataKey, from, propertyKey) + Reflect.defineMetadata(metadataKey, metadata, to, propertyKey) + }) + } +} diff --git a/test/test.ts b/test/test.ts index 9ff686c..cb11b87 100644 --- a/test/test.ts +++ b/test/test.ts @@ -1,3 +1,4 @@ +import 'reflect-metadata'; import Component, { createDecorator, mixins } from '../lib' import { expect } from 'chai' import * as td from 'testdouble' @@ -369,4 +370,23 @@ describe('vue-class-component', () => { expect(vm.valueA).to.equal('hi') expect(vm.valueB).to.equal(456) }) + + it('copies reflection metadata', function () { + @Component + class Test extends Vue { + @Reflect.metadata('worksStatic', true) + static staticValue: string = 'staticValue'; + + @Reflect.metadata('worksMethod', true) + test(): void { } + + @Reflect.metadata('worksAccessor', true) + get testAccessor(): boolean { return true; } + set testAccessor(value: boolean) { void(value) } + } + + expect(Reflect.getOwnMetadata('worksStatic', Test, 'staticValue')).to.equal(true); + expect(Reflect.getOwnMetadata('worksMethod', Test.prototype, 'test')).to.equal(true); + expect(Reflect.getOwnMetadata('worksAccessor', Test.prototype, 'testAccessor')).to.equal(true); + }) }) From cdd63663b115e78037b15841e841b8e3f3ef36d3 Mon Sep 17 00:00:00 2001 From: Gleb Ivanov Date: Mon, 19 Feb 2018 21:42:11 +0400 Subject: [PATCH 2/6] fixed review issues --- dist/vue-class-component.common.js | 28 ++---------------------- dist/vue-class-component.js | 35 +++++------------------------- dist/vue-class-component.min.js | 2 +- package.json | 5 ++--- src/component.ts | 20 ++++++++++++----- src/globals.d.ts | 27 ++++++++++++++++++++++- src/reflect.ts | 33 +++++++++++++++------------- test/test.ts | 26 +++++++++++----------- 8 files changed, 82 insertions(+), 94 deletions(-) diff --git a/dist/vue-class-component.common.js b/dist/vue-class-component.common.js index 478cc13..6b1763c 100644 --- a/dist/vue-class-component.common.js +++ b/dist/vue-class-component.common.js @@ -9,25 +9,8 @@ Object.defineProperty(exports, '__esModule', { value: true }); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } -require('reflect-metadata'); var Vue = _interopDefault(require('vue')); -function copyReflectionMetadata(from, to, reflectionMap) { - shallowCopy(from.prototype, to.prototype, reflectionMap.instance); - shallowCopy(from, to, reflectionMap.static); -} -function shallowCopy(from, to, propertyKeys) { - var _loop_1 = function (propertyKey) { - propertyKeys[propertyKey].forEach(function (metadataKey) { - var metadata = Reflect.getOwnMetadata(metadataKey, from, propertyKey); - Reflect.defineMetadata(metadataKey, metadata, to, propertyKey); - }); - }; - for (var propertyKey in propertyKeys) { - _loop_1(propertyKey); - } -} - var hasProto = { __proto__: [] } instanceof Array; function createDecorator(factory) { return function (target, key, index) { @@ -122,10 +105,6 @@ var $internalHooks = [ ]; function componentFactory(Component, options) { if (options === void 0) { options = {}; } - var reflectionMap = { - instance: {}, - static: {} - }; options.name = options.name || Component._componentTag || Component.name; // prototype props. var proto = Component.prototype; @@ -133,7 +112,6 @@ function componentFactory(Component, options) { if (key === 'constructor') { return; } - reflectionMap.instance[key] = Reflect.getOwnMetadataKeys(proto, key); // hooks if ($internalHooks.indexOf(key) > -1) { options[key] = proto[key]; @@ -169,8 +147,7 @@ function componentFactory(Component, options) { ? superProto.constructor : Vue; var Extended = Super.extend(options); - forwardStaticMembersAndCollectReflection(Extended, Component, Super, reflectionMap); - copyReflectionMetadata(Component, Extended, reflectionMap); + forwardStaticMembers(Extended, Component, Super); return Extended; } var reservedPropertyNames = [ @@ -188,14 +165,13 @@ var reservedPropertyNames = [ 'directive', 'filter' ]; -function forwardStaticMembersAndCollectReflection(Extended, Original, Super, reflectionMap) { +function forwardStaticMembers(Extended, Original, Super) { // We have to use getOwnPropertyNames since Babel registers methods as non-enumerable Object.getOwnPropertyNames(Original).forEach(function (key) { // `prototype` should not be overwritten if (key === 'prototype') { return; } - reflectionMap.static[key] = Reflect.getOwnMetadataKeys(Original, key); // Some browsers does not allow reconfigure built-in properties var extendedDescriptor = Object.getOwnPropertyDescriptor(Extended, key); if (extendedDescriptor && !extendedDescriptor.configurable) { diff --git a/dist/vue-class-component.js b/dist/vue-class-component.js index 9cfbc01..6a22752 100644 --- a/dist/vue-class-component.js +++ b/dist/vue-class-component.js @@ -4,29 +4,13 @@ * @license MIT */ (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('reflect-metadata'), require('vue')) : - typeof define === 'function' && define.amd ? define(['exports', 'reflect-metadata', 'vue'], factory) : - (factory((global.VueClassComponent = {}),null,global.Vue)); -}(this, (function (exports,reflectMetadata,Vue) { 'use strict'; + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('vue')) : + typeof define === 'function' && define.amd ? define(['exports', 'vue'], factory) : + (factory((global.VueClassComponent = {}),global.Vue)); +}(this, (function (exports,Vue) { 'use strict'; Vue = Vue && Vue.hasOwnProperty('default') ? Vue['default'] : Vue; -function copyReflectionMetadata(from, to, reflectionMap) { - shallowCopy(from.prototype, to.prototype, reflectionMap.instance); - shallowCopy(from, to, reflectionMap.static); -} -function shallowCopy(from, to, propertyKeys) { - var _loop_1 = function (propertyKey) { - propertyKeys[propertyKey].forEach(function (metadataKey) { - var metadata = Reflect.getOwnMetadata(metadataKey, from, propertyKey); - Reflect.defineMetadata(metadataKey, metadata, to, propertyKey); - }); - }; - for (var propertyKey in propertyKeys) { - _loop_1(propertyKey); - } -} - var hasProto = { __proto__: [] } instanceof Array; function createDecorator(factory) { return function (target, key, index) { @@ -121,10 +105,6 @@ var $internalHooks = [ ]; function componentFactory(Component, options) { if (options === void 0) { options = {}; } - var reflectionMap = { - instance: {}, - static: {} - }; options.name = options.name || Component._componentTag || Component.name; // prototype props. var proto = Component.prototype; @@ -132,7 +112,6 @@ function componentFactory(Component, options) { if (key === 'constructor') { return; } - reflectionMap.instance[key] = Reflect.getOwnMetadataKeys(proto, key); // hooks if ($internalHooks.indexOf(key) > -1) { options[key] = proto[key]; @@ -168,8 +147,7 @@ function componentFactory(Component, options) { ? superProto.constructor : Vue; var Extended = Super.extend(options); - forwardStaticMembersAndCollectReflection(Extended, Component, Super, reflectionMap); - copyReflectionMetadata(Component, Extended, reflectionMap); + forwardStaticMembers(Extended, Component, Super); return Extended; } var reservedPropertyNames = [ @@ -187,14 +165,13 @@ var reservedPropertyNames = [ 'directive', 'filter' ]; -function forwardStaticMembersAndCollectReflection(Extended, Original, Super, reflectionMap) { +function forwardStaticMembers(Extended, Original, Super) { // We have to use getOwnPropertyNames since Babel registers methods as non-enumerable Object.getOwnPropertyNames(Original).forEach(function (key) { // `prototype` should not be overwritten if (key === 'prototype') { return; } - reflectionMap.static[key] = Reflect.getOwnMetadataKeys(Original, key); // Some browsers does not allow reconfigure built-in properties var extendedDescriptor = Object.getOwnPropertyDescriptor(Extended, key); if (extendedDescriptor && !extendedDescriptor.configurable) { diff --git a/dist/vue-class-component.min.js b/dist/vue-class-component.min.js index 0ce2e17..4dbf8b4 100644 --- a/dist/vue-class-component.min.js +++ b/dist/vue-class-component.min.js @@ -3,4 +3,4 @@ * (c) 2015-present Evan You * @license MIT */ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("reflect-metadata"),require("vue")):"function"==typeof define&&define.amd?define(["exports","reflect-metadata","vue"],t):t(e.VueClassComponent={},null,e.Vue)}(this,function(e,t,r){"use strict";function o(e,t,r){var o=function(o){r[o].forEach(function(r){var n=Reflect.getOwnMetadata(r,e,o);Reflect.defineMetadata(r,n,t,o)})};for(var n in r)o(n)}r=r&&r.hasOwnProperty("default")?r.default:r;var n={__proto__:[]}instanceof Array;var a=["data","beforeCreate","created","beforeMount","mounted","beforeDestroy","destroyed","beforeUpdate","updated","activated","deactivated","render","errorCaptured"];function c(e,t){void 0===t&&(t={});var c={instance:{},static:{}};t.name=t.name||e._componentTag||e.name;var i=e.prototype;Object.getOwnPropertyNames(i).forEach(function(e){if("constructor"!==e)if(c.instance[e]=Reflect.getOwnMetadataKeys(i,e),a.indexOf(e)>-1)t[e]=i[e];else{var r=Object.getOwnPropertyDescriptor(i,e);"function"==typeof r.value?(t.methods||(t.methods={}))[e]=r.value:(r.get||r.set)&&((t.computed||(t.computed={}))[e]={get:r.get,set:r.set})}}),(t.mixins||(t.mixins=[])).push({data:function(){return function(e,t){var r=t.prototype._init;t.prototype._init=function(){var t=this,r=Object.getOwnPropertyNames(e);if(e.$options.props)for(var o in e.$options.props)e.hasOwnProperty(o)||r.push(o);r.forEach(function(r){"_"!==r.charAt(0)&&Object.defineProperty(t,r,{get:function(){return e[r]},set:function(t){return e[r]=t},configurable:!0})})};var o=new t;t.prototype._init=r;var n={};return Object.keys(o).forEach(function(e){void 0!==o[e]&&(n[e]=o[e])}),n}(this,e)}});var f=e.__decorators__;f&&(f.forEach(function(e){return e(t)}),delete e.__decorators__);var u,p,s,d,y,v,_,l=Object.getPrototypeOf(e.prototype),O=l instanceof r?l.constructor:r,m=O.extend(t);return u=m,p=e,s=O,d=c,Object.getOwnPropertyNames(p).forEach(function(e){if("prototype"!==e){d.static[e]=Reflect.getOwnMetadataKeys(p,e);var t=Object.getOwnPropertyDescriptor(u,e);if(!t||t.configurable){var r,o,a=Object.getOwnPropertyDescriptor(p,e);if(!n){if("cid"===e)return;var c=Object.getOwnPropertyDescriptor(s,e);if(r=a.value,o=typeof r,null!=r&&("object"===o||"function"===o)&&c&&c.value===a.value)return}Object.defineProperty(u,e,a)}}}),v=m,_=c,o((y=e).prototype,v.prototype,_.instance),o(y,v,_.static),m}function i(e){return"function"==typeof e?c(e):function(t){return c(t,e)}}!function(e){(i||(i={})).registerHooks=function(e){a.push.apply(a,e)}}();var f=i;e.default=f,e.createDecorator=function(e){return function(t,r,o){var n="function"==typeof t?t:t.constructor;n.__decorators__||(n.__decorators__=[]),"number"!=typeof o&&(o=void 0),n.__decorators__.push(function(t){return e(t,r,o)})}},e.mixins=function(){for(var e=[],t=0;t-1)n[e]=i[e];else{var t=Object.getOwnPropertyDescriptor(i,e);"function"==typeof t.value?(n.methods||(n.methods={}))[e]=t.value:(t.get||t.set)&&((n.computed||(n.computed={}))[e]={get:t.get,set:t.set})}}),(n.mixins||(n.mixins=[])).push({data:function(){return function(e,t){var r=t.prototype._init;t.prototype._init=function(){var t=this,r=Object.getOwnPropertyNames(e);if(e.$options.props)for(var o in e.$options.props)e.hasOwnProperty(o)||r.push(o);r.forEach(function(r){"_"!==r.charAt(0)&&Object.defineProperty(t,r,{get:function(){return e[r]},set:function(t){return e[r]=t},configurable:!0})})};var o=new t;t.prototype._init=r;var n={};return Object.keys(o).forEach(function(e){void 0!==o[e]&&(n[e]=o[e])}),n}(this,e)}});var c=e.__decorators__;c&&(c.forEach(function(e){return e(n)}),delete e.__decorators__);var u,a,f,p=Object.getPrototypeOf(e.prototype),s=p instanceof t?p.constructor:t,d=s.extend(n);return u=d,a=e,f=s,Object.getOwnPropertyNames(a).forEach(function(e){if("prototype"!==e){var t=Object.getOwnPropertyDescriptor(u,e);if(!t||t.configurable){var o,n,i=Object.getOwnPropertyDescriptor(a,e);if(!r){if("cid"===e)return;var c=Object.getOwnPropertyDescriptor(f,e);if(o=i.value,n=typeof o,null!=o&&("object"===n||"function"===n)&&c&&c.value===i.value)return}Object.defineProperty(u,e,i)}}}),d}function i(e){return"function"==typeof e?n(e):function(t){return n(t,e)}}!function(e){(i||(i={})).registerHooks=function(e){o.push.apply(o,e)}}();var c=i;e.default=c,e.createDecorator=function(e){return function(t,r,o){var n="function"==typeof t?t:t.constructor;n.__decorators__||(n.__decorators__=[]),"number"!=typeof o&&(o=void 0),n.__decorators__.push(function(t){return e(t,r,o)})}},e.mixins=function(){for(var e=[],r=0;r -1) { options[key] = proto[key] @@ -78,7 +81,10 @@ export function componentFactory ( const Extended = Super.extend(options) forwardStaticMembersAndCollectReflection(Extended, Component, Super, reflectionMap) - copyReflectionMetadata(Component, Extended, reflectionMap) + + if (reflectionIsSupported()) { + copyReflectionMetadata(Component, Extended, reflectionMap) + } return Extended } @@ -115,7 +121,9 @@ function forwardStaticMembersAndCollectReflection ( return } - reflectionMap.static[key] = Reflect.getOwnMetadataKeys(Original, key); + if (reflectionIsSupported()) { + reflectionMap.static[key] = Reflect.getOwnMetadataKeys(Original, key) + } // Some browsers does not allow reconfigure built-in properties const extendedDescriptor = Object.getOwnPropertyDescriptor(Extended, key) diff --git a/src/globals.d.ts b/src/globals.d.ts index 89a7ede..eca4ca2 100644 --- a/src/globals.d.ts +++ b/src/globals.d.ts @@ -3,8 +3,33 @@ * should not expose to userland */ +declare namespace Reflect { + function decorate(decorators: ClassDecorator[], target: Function): Function + function decorate(decorators: (PropertyDecorator | MethodDecorator)[], target: Object, propertyKey: string | symbol, attributes?: PropertyDescriptor): PropertyDescriptor + function metadata(metadataKey: any, metadataValue: any): { + (target: Function): void + (target: Object, propertyKey: string | symbol): void + } + function defineMetadata(metadataKey: any, metadataValue: any, target: Object): void + function defineMetadata(metadataKey: any, metadataValue: any, target: Object, propertyKey: string | symbol): void + function hasMetadata(metadataKey: any, target: Object): boolean + function hasMetadata(metadataKey: any, target: Object, propertyKey: string | symbol): boolean + function hasOwnMetadata(metadataKey: any, target: Object): boolean + function hasOwnMetadata(metadataKey: any, target: Object, propertyKey: string | symbol): boolean + function getMetadata(metadataKey: any, target: Object): any + function getMetadata(metadataKey: any, target: Object, propertyKey: string | symbol): any + function getOwnMetadata(metadataKey: any, target: Object): any + function getOwnMetadata(metadataKey: any, target: Object, propertyKey: string | symbol): any + function getMetadataKeys(target: Object): any[] + function getMetadataKeys(target: Object, propertyKey: string | symbol): any[] + function getOwnMetadataKeys(target: Object): any[] + function getOwnMetadataKeys(target: Object, propertyKey: string | symbol): any[] + function deleteMetadata(metadataKey: any, target: Object): boolean + function deleteMetadata(metadataKey: any, target: Object, propertyKey: string | symbol): boolean + } + declare const process: { env: { NODE_ENV: string } -} \ No newline at end of file +} diff --git a/src/reflect.ts b/src/reflect.ts index f13fe8a..808181e 100644 --- a/src/reflect.ts +++ b/src/reflect.ts @@ -1,29 +1,32 @@ import { VueConstructor } from 'vue' -import 'reflect-metadata' export type StringToArrayMap = { - [key: string]: Array + [key: string]: Array } export type ReflectionMap = { - instance: StringToArrayMap, - static: StringToArrayMap + instance: StringToArrayMap, + static: StringToArrayMap +} + +export function reflectionIsSupported() { + return (Reflect && Reflect.defineMetadata) !== undefined } export function copyReflectionMetadata( - from: VueConstructor, - to: VueConstructor, - reflectionMap: ReflectionMap + from: VueConstructor, + to: VueConstructor, + reflectionMap: ReflectionMap ) { - shallowCopy(from.prototype, to.prototype, reflectionMap.instance); - shallowCopy(from, to, reflectionMap.static); + shallowCopy(from.prototype, to.prototype, reflectionMap.instance) + shallowCopy(from, to, reflectionMap.static) } function shallowCopy(from: VueConstructor, to: VueConstructor, propertyKeys: StringToArrayMap) { - for (const propertyKey in propertyKeys) { - propertyKeys[propertyKey].forEach((metadataKey) => { - const metadata = Reflect.getOwnMetadata(metadataKey, from, propertyKey) - Reflect.defineMetadata(metadataKey, metadata, to, propertyKey) - }) - } + for (const propertyKey in propertyKeys) { + propertyKeys[propertyKey].forEach((metadataKey) => { + const metadata = Reflect.getOwnMetadata(metadataKey, from, propertyKey) + Reflect.defineMetadata(metadataKey, metadata, to, propertyKey) + }) + } } diff --git a/test/test.ts b/test/test.ts index cb11b87..b09ccd9 100644 --- a/test/test.ts +++ b/test/test.ts @@ -372,21 +372,21 @@ describe('vue-class-component', () => { }) it('copies reflection metadata', function () { - @Component - class Test extends Vue { - @Reflect.metadata('worksStatic', true) - static staticValue: string = 'staticValue'; + @Component + class Test extends Vue { + @Reflect.metadata('worksStatic', true) + static staticValue: string = 'staticValue' - @Reflect.metadata('worksMethod', true) - test(): void { } + @Reflect.metadata('worksMethod', true) + test(): void { } - @Reflect.metadata('worksAccessor', true) - get testAccessor(): boolean { return true; } - set testAccessor(value: boolean) { void(value) } - } + @Reflect.metadata('worksAccessor', true) + get testAccessor(): boolean { return true } + set testAccessor(value: boolean) { void(value) } + } - expect(Reflect.getOwnMetadata('worksStatic', Test, 'staticValue')).to.equal(true); - expect(Reflect.getOwnMetadata('worksMethod', Test.prototype, 'test')).to.equal(true); - expect(Reflect.getOwnMetadata('worksAccessor', Test.prototype, 'testAccessor')).to.equal(true); + expect(Reflect.getOwnMetadata('worksStatic', Test, 'staticValue')).to.equal(true) + expect(Reflect.getOwnMetadata('worksMethod', Test.prototype, 'test')).to.equal(true) + expect(Reflect.getOwnMetadata('worksAccessor', Test.prototype, 'testAccessor')).to.equal(true) }) }) From 07eff38aa3f82d9e0426a1ebdfcf134556d8a6da Mon Sep 17 00:00:00 2001 From: Gleb Ivanov Date: Mon, 19 Feb 2018 21:44:28 +0400 Subject: [PATCH 3/6] fixed review issues --- src/component.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/component.ts b/src/component.ts index 847f2f1..0ce28c9 100644 --- a/src/component.ts +++ b/src/component.ts @@ -109,10 +109,10 @@ const reservedPropertyNames = [ ] function forwardStaticMembersAndCollectReflection ( - Extended: typeof Vue, - Original: typeof Vue, - Super: typeof Vue, - reflectionMap: ReflectionMap + Extended: typeof Vue, + Original: typeof Vue, + Super: typeof Vue, + reflectionMap: ReflectionMap ): void { // We have to use getOwnPropertyNames since Babel registers methods as non-enumerable Object.getOwnPropertyNames(Original).forEach(key => { From 49e9c90f2550e9198079f3a770aa4879ed8e8a77 Mon Sep 17 00:00:00 2001 From: Gleb Ivanov Date: Tue, 20 Feb 2018 22:23:53 +0400 Subject: [PATCH 4/6] fixed corner case with constructor --- src/component.ts | 6 +++++- src/reflect.ts | 11 +++++++++-- test/test.ts | 7 +++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/component.ts b/src/component.ts index 0ce28c9..c07063b 100644 --- a/src/component.ts +++ b/src/component.ts @@ -26,9 +26,13 @@ export function componentFactory ( ): VueClass { const reflectionMap: ReflectionMap = { instance: {}, - static: {} + static: {}, + constructor: [] } + if (reflectionIsSupported()) { + reflectionMap.constructor = Reflect.getOwnMetadataKeys(Component) + } options.name = options.name || (Component as any)._componentTag || (Component as any).name // prototype props. const proto = Component.prototype diff --git a/src/reflect.ts b/src/reflect.ts index 808181e..46975a0 100644 --- a/src/reflect.ts +++ b/src/reflect.ts @@ -5,6 +5,7 @@ export type StringToArrayMap = { } export type ReflectionMap = { + constructor: Array, instance: StringToArrayMap, static: StringToArrayMap } @@ -20,13 +21,19 @@ export function copyReflectionMetadata( ) { shallowCopy(from.prototype, to.prototype, reflectionMap.instance) shallowCopy(from, to, reflectionMap.static) + shallowCopy(from, to, {'constructor': reflectionMap.constructor}) } function shallowCopy(from: VueConstructor, to: VueConstructor, propertyKeys: StringToArrayMap) { for (const propertyKey in propertyKeys) { propertyKeys[propertyKey].forEach((metadataKey) => { - const metadata = Reflect.getOwnMetadata(metadataKey, from, propertyKey) - Reflect.defineMetadata(metadataKey, metadata, to, propertyKey) + if (propertyKey == 'constructor') { + const metadata = Reflect.getOwnMetadata(metadataKey, from) + Reflect.defineMetadata(metadataKey, metadata, to) + } else { + const metadata = Reflect.getOwnMetadata(metadataKey, from, propertyKey) + Reflect.defineMetadata(metadataKey, metadata, to, propertyKey) + } }) } } diff --git a/test/test.ts b/test/test.ts index b09ccd9..5a758c7 100644 --- a/test/test.ts +++ b/test/test.ts @@ -372,8 +372,14 @@ describe('vue-class-component', () => { }) it('copies reflection metadata', function () { + const trickyDecorator = (proto: Vue, _: string) => + Reflect.defineMetadata('worksConstructor', true, proto.constructor); + @Component class Test extends Vue { + @trickyDecorator + trickyCase: string = 'trickyCase' + @Reflect.metadata('worksStatic', true) static staticValue: string = 'staticValue' @@ -385,6 +391,7 @@ describe('vue-class-component', () => { set testAccessor(value: boolean) { void(value) } } + expect(Reflect.getOwnMetadata('worksConstructor', Test)).to.equal(true) expect(Reflect.getOwnMetadata('worksStatic', Test, 'staticValue')).to.equal(true) expect(Reflect.getOwnMetadata('worksMethod', Test.prototype, 'test')).to.equal(true) expect(Reflect.getOwnMetadata('worksAccessor', Test.prototype, 'testAccessor')).to.equal(true) From 614b008c72b921efb75d880d816f7a6f597ecd9b Mon Sep 17 00:00:00 2001 From: Gleb Ivanov Date: Tue, 20 Feb 2018 22:27:00 +0400 Subject: [PATCH 5/6] fixed codestyle --- src/reflect.ts | 8 ++++---- test/test.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/reflect.ts b/src/reflect.ts index 46975a0..67e3918 100644 --- a/src/reflect.ts +++ b/src/reflect.ts @@ -28,11 +28,11 @@ function shallowCopy(from: VueConstructor, to: VueConstructor, propertyKeys: Str for (const propertyKey in propertyKeys) { propertyKeys[propertyKey].forEach((metadataKey) => { if (propertyKey == 'constructor') { - const metadata = Reflect.getOwnMetadata(metadataKey, from) - Reflect.defineMetadata(metadataKey, metadata, to) + const metadata = Reflect.getOwnMetadata(metadataKey, from) + Reflect.defineMetadata(metadataKey, metadata, to) } else { - const metadata = Reflect.getOwnMetadata(metadataKey, from, propertyKey) - Reflect.defineMetadata(metadataKey, metadata, to, propertyKey) + const metadata = Reflect.getOwnMetadata(metadataKey, from, propertyKey) + Reflect.defineMetadata(metadataKey, metadata, to, propertyKey) } }) } diff --git a/test/test.ts b/test/test.ts index 5a758c7..1edf71f 100644 --- a/test/test.ts +++ b/test/test.ts @@ -373,7 +373,7 @@ describe('vue-class-component', () => { it('copies reflection metadata', function () { const trickyDecorator = (proto: Vue, _: string) => - Reflect.defineMetadata('worksConstructor', true, proto.constructor); + Reflect.defineMetadata('worksConstructor', true, proto.constructor) @Component class Test extends Vue { From a362138240b17ed004aac0542616707d94437263 Mon Sep 17 00:00:00 2001 From: Gleb Ivanov Date: Fri, 2 Mar 2018 10:38:27 +0400 Subject: [PATCH 6/6] fixed codestyle in test.ts and reflect.ts to match project codestyle --- src/reflect.ts | 6 +++--- test/test.ts | 22 +++++++++++++--------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/reflect.ts b/src/reflect.ts index 67e3918..56b7a27 100644 --- a/src/reflect.ts +++ b/src/reflect.ts @@ -10,11 +10,11 @@ export type ReflectionMap = { static: StringToArrayMap } -export function reflectionIsSupported() { +export function reflectionIsSupported () { return (Reflect && Reflect.defineMetadata) !== undefined } -export function copyReflectionMetadata( +export function copyReflectionMetadata ( from: VueConstructor, to: VueConstructor, reflectionMap: ReflectionMap @@ -24,7 +24,7 @@ export function copyReflectionMetadata( shallowCopy(from, to, {'constructor': reflectionMap.constructor}) } -function shallowCopy(from: VueConstructor, to: VueConstructor, propertyKeys: StringToArrayMap) { +function shallowCopy (from: VueConstructor, to: VueConstructor, propertyKeys: StringToArrayMap) { for (const propertyKey in propertyKeys) { propertyKeys[propertyKey].forEach((metadataKey) => { if (propertyKey == 'constructor') { diff --git a/test/test.ts b/test/test.ts index 1edf71f..8fb1557 100644 --- a/test/test.ts +++ b/test/test.ts @@ -372,23 +372,27 @@ describe('vue-class-component', () => { }) it('copies reflection metadata', function () { - const trickyDecorator = (proto: Vue, _: string) => - Reflect.defineMetadata('worksConstructor', true, proto.constructor) - @Component + @Reflect.metadata('worksConstructor', true) class Test extends Vue { - @trickyDecorator - trickyCase: string = 'trickyCase' - @Reflect.metadata('worksStatic', true) static staticValue: string = 'staticValue' + private _test: boolean = false; + @Reflect.metadata('worksMethod', true) - test(): void { } + test (): void { + void 0 + } @Reflect.metadata('worksAccessor', true) - get testAccessor(): boolean { return true } - set testAccessor(value: boolean) { void(value) } + get testAccessor (): boolean { + return this._test + } + + set testAccessor (value: boolean) { + this._test = value + } } expect(Reflect.getOwnMetadata('worksConstructor', Test)).to.equal(true)