From 0b29359f9ab1e9773aa858e79271587397013a27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Sun, 6 Mar 2022 07:20:05 -0500 Subject: [PATCH] fix: push `newClass` only when class is decorated (#14334) --- .../babel-helpers/src/helpers-generated.ts | 2 +- .../babel-helpers/src/helpers/applyDecs.js | 65 +++++++++---------- .../private-with-initializers/exec.js | 41 ++++++++++++ .../private/exec.js | 17 ----- .../public-with-initializers/exec.js | 50 ++++++++++++++ .../2021-12-methods--to-es2015/public/exec.js | 23 ------- .../static-private-with-initializers/exec.js | 39 +++++++++++ .../static-private/exec.js | 17 ----- .../static-public-with-initializers/exec.js | 48 ++++++++++++++ .../static-public/exec.js | 23 ------- 10 files changed, 208 insertions(+), 117 deletions(-) create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/private-with-initializers/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/public-with-initializers/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/static-private-with-initializers/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/static-public-with-initializers/exec.js diff --git a/packages/babel-helpers/src/helpers-generated.ts b/packages/babel-helpers/src/helpers-generated.ts index e27f2044bd06..15c28b9b2de4 100644 --- a/packages/babel-helpers/src/helpers-generated.ts +++ b/packages/babel-helpers/src/helpers-generated.ts @@ -15,7 +15,7 @@ function helper(minVersion, source) { export default Object.freeze({ applyDecs: helper( "7.17.0", - 'function createMetadataMethodsForProperty(metadataMap,kind,property){return{getMetadata:function(key){if("symbol"!=typeof key)throw new TypeError("Metadata keys must be symbols, received: "+key);var metadataForKey=metadataMap[key];if(void 0!==metadataForKey)if(1===kind){var pub=metadataForKey.public;if(void 0!==pub)return pub[property]}else if(2===kind){var priv=metadataForKey.private;if(void 0!==priv)return priv.get(property)}else if(Object.hasOwnProperty.call(metadataForKey,"constructor"))return metadataForKey.constructor},setMetadata:function(key,value){if("symbol"!=typeof key)throw new TypeError("Metadata keys must be symbols, received: "+key);var metadataForKey=metadataMap[key];if(void 0===metadataForKey&&(metadataForKey=metadataMap[key]={}),1===kind){var pub=metadataForKey.public;void 0===pub&&(pub=metadataForKey.public={}),pub[property]=value}else if(2===kind){var priv=metadataForKey.priv;void 0===priv&&(priv=metadataForKey.private=new Map),priv.set(property,value)}else metadataForKey.constructor=value}}}function convertMetadataMapToFinal(obj,metadataMap){var parentMetadataMap=obj[Symbol.metadata||Symbol.for("Symbol.metadata")],metadataKeys=Object.getOwnPropertySymbols(metadataMap);if(0!==metadataKeys.length){for(var i=0;i=0;i--){var newInit;if(void 0!==(newValue=(0,decs[i])(value,ctx)))assertValidReturnValue(kind,newValue),0===kind?newInit=newValue:1===kind?(newInit=newValue.initializer,get=newValue.get||value.get,set=newValue.set||value.set,value={get:get,set:set}):value=newValue,void 0!==newInit&&(void 0===initializer?initializer=newInit:"function"==typeof initializer?initializer=[initializer,newInit]:initializer.push(newInit))}if(0===kind||1===kind){if(void 0===initializer)initializer=function(instance,init){return init};else if("function"!=typeof initializer){var ownInitializers=initializer;initializer=function(instance,init){for(var value=init,i=0;i3,isStatic=kind>=5;if(isStatic?(base=Class,metadataMap=staticMetadataMap,kind-=5,initializers=staticInitializers):(base=Class.prototype,metadataMap=protoMetadataMap,initializers=protoInitializers),0!==kind&&!isPrivate){var existingNonFields=isStatic?existingStaticNonFields:existingProtoNonFields,existingKind=existingNonFields.get(name)||0;if(!0===existingKind||3===existingKind&&4!==kind||4===existingKind&&3!==kind)throw new Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: "+name);!existingKind&&kind>2?existingNonFields.set(name,kind):existingNonFields.set(name,!0)}applyMemberDec(ret,base,decInfo,name,kind,isStatic,isPrivate,metadataMap,initializers)}}protoInitializers.length>0&&pushInitializers(ret,protoInitializers),staticInitializers.length>0&&pushInitializers(ret,staticInitializers)}function pushInitializers(ret,initializers){initializers.length>0?(initializers=initializers.slice(),ret.push((function(instance){for(var i=0;i=0;i--)newClass=classDecs[i](newClass,ctx)||newClass;ret.push(newClass),initializers.length>0?ret.push((function(){for(var i=0;i=0;i--){var newInit;if(void 0!==(newValue=(0,decs[i])(value,ctx)))assertValidReturnValue(kind,newValue),0===kind?newInit=newValue:1===kind?(newInit=newValue.initializer,get=newValue.get||value.get,set=newValue.set||value.set,value={get:get,set:set}):value=newValue,void 0!==newInit&&(void 0===initializer?initializer=newInit:"function"==typeof initializer?initializer=[initializer,newInit]:initializer.push(newInit))}if(0===kind||1===kind){if(void 0===initializer)initializer=function(instance,init){return init};else if("function"!=typeof initializer){var ownInitializers=initializer;initializer=function(instance,init){for(var value=init,i=0;i3,isStatic=kind>=5;if(isStatic?(base=Class,metadataMap=staticMetadataMap,kind-=5,initializers=staticInitializers):(base=Class.prototype,metadataMap=protoMetadataMap,initializers=protoInitializers),0!==kind&&!isPrivate){var existingNonFields=isStatic?existingStaticNonFields:existingProtoNonFields,existingKind=existingNonFields.get(name)||0;if(!0===existingKind||3===existingKind&&4!==kind||4===existingKind&&3!==kind)throw new Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: "+name);!existingKind&&kind>2?existingNonFields.set(name,kind):existingNonFields.set(name,!0)}applyMemberDec(ret,base,decInfo,name,kind,isStatic,isPrivate,metadataMap,initializers)}}pushInitializers(ret,protoInitializers),pushInitializers(ret,staticInitializers)}function pushInitializers(ret,initializers){initializers.length>0&&(initializers=initializers.slice(),ret.push((function(instance){for(var i=0;i0){for(var newClass=targetClass,name=targetClass.name,ctx=Object.assign({kind:"class",name:name,addInitializer:createAddInitializerMethod(initializers)},createMetadataMethodsForProperty(metadataMap,0,name)),i=classDecs.length-1;i>=0;i--)newClass=classDecs[i](newClass,ctx)||newClass;ret.push(newClass)}initializers.length>0?ret.push((function(){for(var i=0;i 0) { - pushInitializers(ret, protoInitializers); - } - - if (staticInitializers.length > 0) { - pushInitializers(ret, staticInitializers); - } + pushInitializers(ret, protoInitializers); + pushInitializers(ret, staticInitializers); } function pushInitializers(ret, initializers) { @@ -521,23 +516,25 @@ function pushInitializers(ret, initializers) { function applyClassDecs(ret, targetClass, metadataMap, classDecs) { var initializers = []; - var newClass = targetClass; - - var name = targetClass.name; - var ctx = Object.assign( - { - kind: "class", - name: name, - addInitializer: createAddInitializerMethod(initializers), - }, - createMetadataMethodsForProperty(metadataMap, 0 /* CONSTRUCTOR */, name) - ); + if (classDecs.length > 0) { + var newClass = targetClass; + + var name = targetClass.name; + var ctx = Object.assign( + { + kind: "class", + name: name, + addInitializer: createAddInitializerMethod(initializers), + }, + createMetadataMethodsForProperty(metadataMap, 0 /* CONSTRUCTOR */, name) + ); - for (var i = classDecs.length - 1; i >= 0; i--) { - newClass = classDecs[i](newClass, ctx) || newClass; - } + for (var i = classDecs.length - 1; i >= 0; i--) { + newClass = classDecs[i](newClass, ctx) || newClass; + } - ret.push(newClass); + ret.push(newClass); + } if (initializers.length > 0) { ret.push(function () { @@ -699,23 +696,19 @@ export default function applyDecs(targetClass, memberDecs, classDecs) { var ret = []; var staticMetadataMap = {}; - if (memberDecs) { - var protoMetadataMap = {}; + var protoMetadataMap = {}; - applyMemberDecs( - ret, - targetClass, - protoMetadataMap, - staticMetadataMap, - memberDecs - ); + applyMemberDecs( + ret, + targetClass, + protoMetadataMap, + staticMetadataMap, + memberDecs + ); - convertMetadataMapToFinal(targetClass.prototype, protoMetadataMap); - } + convertMetadataMapToFinal(targetClass.prototype, protoMetadataMap); - if (classDecs) { - applyClassDecs(ret, targetClass, staticMetadataMap, classDecs); - } + applyClassDecs(ret, targetClass, staticMetadataMap, classDecs); convertMetadataMapToFinal(targetClass, staticMetadataMap); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/private-with-initializers/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/private-with-initializers/exec.js new file mode 100644 index 000000000000..5a3ce1785ff0 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/private-with-initializers/exec.js @@ -0,0 +1,41 @@ +function dec(fn, context) { + context.addInitializer((instance) => { + instance[context.name + 'Context'] = context; + }); + + return function () { + return fn.call(this) + 1; + } +} + +class Foo { + value = 1; + + @dec + #a() { + return this.value; + } + + callA() { + return this.#a(); + } +} + +let foo = new Foo(); + +const aContext = foo['#aContext']; + +// First call gets the method, second call calls the method with correct `this` +expect(aContext.access.get.call(foo).call(foo)).toBe(2); +expect(foo.callA()).toBe(2); +foo.value = 123; +expect(aContext.access.get.call(foo).call(foo)).toBe(124); +expect(foo.callA()).toBe(124); + +expect(aContext.name).toBe('#a'); +expect(aContext.kind).toBe('method'); +expect(aContext.isStatic).toBe(false); +expect(aContext.isPrivate).toBe(true); +expect(typeof aContext.addInitializer).toBe('function'); +expect(typeof aContext.setMetadata).toBe('function'); +expect(typeof aContext.getMetadata).toBe('function'); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/private/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/private/exec.js index 5a3ce1785ff0..a74cb2331eca 100644 --- a/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/private/exec.js +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/private/exec.js @@ -1,8 +1,4 @@ function dec(fn, context) { - context.addInitializer((instance) => { - instance[context.name + 'Context'] = context; - }); - return function () { return fn.call(this) + 1; } @@ -23,19 +19,6 @@ class Foo { let foo = new Foo(); -const aContext = foo['#aContext']; - -// First call gets the method, second call calls the method with correct `this` -expect(aContext.access.get.call(foo).call(foo)).toBe(2); expect(foo.callA()).toBe(2); foo.value = 123; -expect(aContext.access.get.call(foo).call(foo)).toBe(124); expect(foo.callA()).toBe(124); - -expect(aContext.name).toBe('#a'); -expect(aContext.kind).toBe('method'); -expect(aContext.isStatic).toBe(false); -expect(aContext.isPrivate).toBe(true); -expect(typeof aContext.addInitializer).toBe('function'); -expect(typeof aContext.setMetadata).toBe('function'); -expect(typeof aContext.getMetadata).toBe('function'); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/public-with-initializers/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/public-with-initializers/exec.js new file mode 100644 index 000000000000..0f27b61caa4d --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/public-with-initializers/exec.js @@ -0,0 +1,50 @@ +function dec(fn, context) { + context.addInitializer((instance) => { + instance[context.name + 'Context'] = context; + }); + + return function () { + return fn.call(this) + 1; + } +} + +class Foo { + value = 1; + + @dec + a() { + return this.value; + } + + @dec + ['b']() { + return this.value; + } +} + +let foo = new Foo(); + +const aContext = foo['aContext']; +const bContext = foo['bContext']; + +expect(foo.a()).toBe(2); +expect(foo.b()).toBe(2); +foo.value = 123; +expect(foo.a()).toBe(124); +expect(foo.b()).toBe(124); + +expect(aContext.name).toBe('a'); +expect(aContext.kind).toBe('method'); +expect(aContext.isStatic).toBe(false); +expect(aContext.isPrivate).toBe(false); +expect(typeof aContext.addInitializer).toBe('function'); +expect(typeof aContext.setMetadata).toBe('function'); +expect(typeof aContext.getMetadata).toBe('function'); + +expect(bContext.name).toBe('b'); +expect(bContext.kind).toBe('method'); +expect(bContext.isStatic).toBe(false); +expect(bContext.isPrivate).toBe(false); +expect(typeof bContext.addInitializer).toBe('function'); +expect(typeof bContext.setMetadata).toBe('function'); +expect(typeof bContext.getMetadata).toBe('function'); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/public/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/public/exec.js index 0f27b61caa4d..b9ec2b92e328 100644 --- a/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/public/exec.js +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/public/exec.js @@ -1,8 +1,4 @@ function dec(fn, context) { - context.addInitializer((instance) => { - instance[context.name + 'Context'] = context; - }); - return function () { return fn.call(this) + 1; } @@ -24,27 +20,8 @@ class Foo { let foo = new Foo(); -const aContext = foo['aContext']; -const bContext = foo['bContext']; - expect(foo.a()).toBe(2); expect(foo.b()).toBe(2); foo.value = 123; expect(foo.a()).toBe(124); expect(foo.b()).toBe(124); - -expect(aContext.name).toBe('a'); -expect(aContext.kind).toBe('method'); -expect(aContext.isStatic).toBe(false); -expect(aContext.isPrivate).toBe(false); -expect(typeof aContext.addInitializer).toBe('function'); -expect(typeof aContext.setMetadata).toBe('function'); -expect(typeof aContext.getMetadata).toBe('function'); - -expect(bContext.name).toBe('b'); -expect(bContext.kind).toBe('method'); -expect(bContext.isStatic).toBe(false); -expect(bContext.isPrivate).toBe(false); -expect(typeof bContext.addInitializer).toBe('function'); -expect(typeof bContext.setMetadata).toBe('function'); -expect(typeof bContext.getMetadata).toBe('function'); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/static-private-with-initializers/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/static-private-with-initializers/exec.js new file mode 100644 index 000000000000..bab58275b48a --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/static-private-with-initializers/exec.js @@ -0,0 +1,39 @@ +function dec(fn, context) { + context.addInitializer((instance) => { + instance[context.name + 'Context'] = context; + }); + + return function () { + return fn.call(this) + 1; + } +} + +class Foo { + static value = 1; + + @dec + static #a() { + return this.value; + } + + static callA() { + return this.#a(); + } +} + +const aContext = Foo['#aContext']; + +// First call gets the method, second call calls the method with correct `this` +expect(aContext.access.get.call(Foo).call(Foo)).toBe(2); +expect(Foo.callA()).toBe(2); +Foo.value = 123; +expect(aContext.access.get.call(Foo).call(Foo)).toBe(124); +expect(Foo.callA()).toBe(124); + +expect(aContext.name).toBe('#a'); +expect(aContext.kind).toBe('method'); +expect(aContext.isStatic).toBe(true); +expect(aContext.isPrivate).toBe(true); +expect(typeof aContext.addInitializer).toBe('function'); +expect(typeof aContext.setMetadata).toBe('function'); +expect(typeof aContext.getMetadata).toBe('function'); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/static-private/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/static-private/exec.js index bab58275b48a..47deb64ec304 100644 --- a/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/static-private/exec.js +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/static-private/exec.js @@ -1,8 +1,4 @@ function dec(fn, context) { - context.addInitializer((instance) => { - instance[context.name + 'Context'] = context; - }); - return function () { return fn.call(this) + 1; } @@ -21,19 +17,6 @@ class Foo { } } -const aContext = Foo['#aContext']; - -// First call gets the method, second call calls the method with correct `this` -expect(aContext.access.get.call(Foo).call(Foo)).toBe(2); expect(Foo.callA()).toBe(2); Foo.value = 123; -expect(aContext.access.get.call(Foo).call(Foo)).toBe(124); expect(Foo.callA()).toBe(124); - -expect(aContext.name).toBe('#a'); -expect(aContext.kind).toBe('method'); -expect(aContext.isStatic).toBe(true); -expect(aContext.isPrivate).toBe(true); -expect(typeof aContext.addInitializer).toBe('function'); -expect(typeof aContext.setMetadata).toBe('function'); -expect(typeof aContext.getMetadata).toBe('function'); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/static-public-with-initializers/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/static-public-with-initializers/exec.js new file mode 100644 index 000000000000..e68458db59d9 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/static-public-with-initializers/exec.js @@ -0,0 +1,48 @@ +function dec(fn, context) { + context.addInitializer((instance) => { + instance[context.name + 'Context'] = context; + }); + + return function () { + return fn.call(this) + 1; + } +} + +class Foo { + static value = 1; + + @dec + static a() { + return this.value; + } + + @dec + static ['b']() { + return this.value; + } +} + +const aContext = Foo['aContext']; +const bContext = Foo['bContext']; + +expect(Foo.a()).toBe(2); +expect(Foo.b()).toBe(2); +Foo.value = 123; +expect(Foo.a()).toBe(124); +expect(Foo.b()).toBe(124); + +expect(aContext.name).toBe('a'); +expect(aContext.kind).toBe('method'); +expect(aContext.isStatic).toBe(true); +expect(aContext.isPrivate).toBe(false); +expect(typeof aContext.addInitializer).toBe('function'); +expect(typeof aContext.setMetadata).toBe('function'); +expect(typeof aContext.getMetadata).toBe('function'); + +expect(bContext.name).toBe('b'); +expect(bContext.kind).toBe('method'); +expect(bContext.isStatic).toBe(true); +expect(bContext.isPrivate).toBe(false); +expect(typeof bContext.addInitializer).toBe('function'); +expect(typeof bContext.setMetadata).toBe('function'); +expect(typeof bContext.getMetadata).toBe('function'); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/static-public/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/static-public/exec.js index e68458db59d9..e5d03ee48021 100644 --- a/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/static-public/exec.js +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2021-12-methods--to-es2015/static-public/exec.js @@ -1,8 +1,4 @@ function dec(fn, context) { - context.addInitializer((instance) => { - instance[context.name + 'Context'] = context; - }); - return function () { return fn.call(this) + 1; } @@ -22,27 +18,8 @@ class Foo { } } -const aContext = Foo['aContext']; -const bContext = Foo['bContext']; - expect(Foo.a()).toBe(2); expect(Foo.b()).toBe(2); Foo.value = 123; expect(Foo.a()).toBe(124); expect(Foo.b()).toBe(124); - -expect(aContext.name).toBe('a'); -expect(aContext.kind).toBe('method'); -expect(aContext.isStatic).toBe(true); -expect(aContext.isPrivate).toBe(false); -expect(typeof aContext.addInitializer).toBe('function'); -expect(typeof aContext.setMetadata).toBe('function'); -expect(typeof aContext.getMetadata).toBe('function'); - -expect(bContext.name).toBe('b'); -expect(bContext.kind).toBe('method'); -expect(bContext.isStatic).toBe(true); -expect(bContext.isPrivate).toBe(false); -expect(typeof bContext.addInitializer).toBe('function'); -expect(typeof bContext.setMetadata).toBe('function'); -expect(typeof bContext.getMetadata).toBe('function');