From a0f9d4d827384e808c60b92d688106cfab2b5813 Mon Sep 17 00:00:00 2001 From: Chris Hewell Garrett Date: Sat, 27 Nov 2021 16:20:25 -0500 Subject: [PATCH] Adds the 2020-09 transform for decorators Implements the 2020-09 transform for the decorators proposal, with support for the `accessor` keyword. --- .../src/decorators.ts | 5 +- .../src/fields.ts | 7 +- .../src/index.ts | 4 +- .../babel-helpers/src/helpers-generated.ts | 4 + .../babel-helpers/src/helpers/applyDecs.js | 741 +++++++++++++++ .../CONTRIB.md | 374 ++++++++ .../src/index.ts | 13 +- .../src/transformer-2020-09.ts | 867 ++++++++++++++++++ .../fixtures/2020-09-accessors/options.json | 11 + .../2020-09-accessors/private/exec.js | 54 ++ .../2020-09-accessors/private/input.js | 7 + .../2020-09-accessors/private/output.js | 77 ++ .../fixtures/2020-09-accessors/public/exec.js | 75 ++ .../2020-09-accessors/public/input.js | 10 + .../2020-09-accessors/public/output.js | 60 ++ .../2020-09-accessors/static-private/exec.js | 52 ++ .../2020-09-accessors/static-private/input.js | 7 + .../static-private/output.js | 76 ++ .../2020-09-accessors/static-public/exec.js | 70 ++ .../2020-09-accessors/static-public/input.js | 10 + .../2020-09-accessors/static-public/output.js | 54 ++ .../undecorated-private/exec.js | 31 + .../undecorated-private/input.js | 5 + .../undecorated-private/output.js | 45 + .../undecorated-public/exec.js | 27 + .../undecorated-public/input.js | 7 + .../undecorated-public/output.js | 47 + .../undecorated-static-private/exec.js | 29 + .../undecorated-static-private/input.js | 5 + .../undecorated-static-private/output.js | 34 + .../undecorated-static-public/exec.js | 22 + .../undecorated-static-public/input.js | 7 + .../undecorated-static-public/output.js | 39 + .../2020-09-classes/expressions/input.js | 11 + .../2020-09-classes/expressions/output.js | 69 ++ .../2020-09-classes/inheritance/exec.js | 18 + .../2020-09-classes/inheritance/input.js | 5 + .../2020-09-classes/inheritance/output.js | 35 + .../2020-09-classes/initializers/exec.js | 41 + .../2020-09-classes/initializers/input.js | 13 + .../2020-09-classes/initializers/output.js | 43 + .../fixtures/2020-09-classes/options.json | 11 + .../replacement-with-expr/exec.js | 17 + .../replacement-with-expr/input.js | 6 + .../replacement-with-expr/output.js | 15 + .../2020-09-classes/replacement/exec.js | 19 + .../2020-09-classes/replacement/input.js | 6 + .../2020-09-classes/replacement/output.js | 22 + .../computed-keys-same-ast/exec.js | 33 + .../computed-keys-same-ast/input.js | 11 + .../computed-keys-same-ast/output.js | 24 + .../computed-keys-same-value/exec.js | 29 + .../computed-keys-same-value/input.js | 11 + .../computed-keys-same-value/output.js | 24 + .../method-and-field/exec.js | 23 + .../method-and-field/input.js | 9 + .../method-and-field/output.js | 21 + .../methods-with-same-key/exec.js | 19 + .../methods-with-same-key/input.js | 11 + .../methods-with-same-key/output.js | 24 + .../2020-09-duplicated-keys/options.json | 11 + .../test/fixtures/2020-09-fields/options.json | 11 + .../fixtures/2020-09-fields/private/exec.js | 41 + .../fixtures/2020-09-fields/private/input.js | 7 + .../fixtures/2020-09-fields/private/output.js | 44 + .../fixtures/2020-09-fields/public/exec.js | 62 ++ .../fixtures/2020-09-fields/public/input.js | 10 + .../fixtures/2020-09-fields/public/output.js | 20 + .../2020-09-fields/static-private/exec.js | 39 + .../2020-09-fields/static-private/input.js | 7 + .../2020-09-fields/static-private/output.js | 38 + .../2020-09-fields/static-public/exec.js | 57 ++ .../2020-09-fields/static-public/input.js | 10 + .../2020-09-fields/static-public/output.js | 17 + .../2020-09-getters-and-setters/options.json | 11 + .../private/exec.js | 67 ++ .../private/input.js | 21 + .../private/output.js | 50 + .../public/exec.js | 88 ++ .../public/input.js | 23 + .../public/output.js | 34 + .../static-private/exec.js | 65 ++ .../static-private/input.js | 21 + .../static-private/output.js | 47 + .../static-public/exec.js | 87 ++ .../static-public/input.js | 23 + .../static-public/output.js | 32 + .../fixtures/2020-09-getters/options.json | 11 + .../fixtures/2020-09-getters/private/exec.js | 39 + .../fixtures/2020-09-getters/private/input.js | 12 + .../2020-09-getters/private/output.js | 37 + .../fixtures/2020-09-getters/public/exec.js | 50 + .../fixtures/2020-09-getters/public/input.js | 13 + .../fixtures/2020-09-getters/public/output.js | 26 + .../2020-09-getters/static-private/exec.js | 38 + .../2020-09-getters/static-private/input.js | 12 + .../2020-09-getters/static-private/output.js | 34 + .../2020-09-getters/static-public/exec.js | 48 + .../2020-09-getters/static-public/input.js | 13 + .../2020-09-getters/static-public/output.js | 24 + .../fixtures/2020-09-metadata/class/exec.js | 10 + .../getting-previously-set-metadata/exec.js | 30 + .../inheritance-private/exec.js | 16 + .../inheritance-public/exec.js | 18 + .../2020-09-metadata/non-symbol-keys/exec.js | 10 + .../fixtures/2020-09-metadata/options.json | 11 + .../2020-09-metadata/proto-private/exec.js | 11 + .../2020-09-metadata/proto-public/exec.js | 11 + .../2020-09-metadata/static-private/exec.js | 11 + .../2020-09-metadata/static-public/exec.js | 11 + .../fixtures/2020-09-methods/options.json | 11 + .../fixtures/2020-09-methods/private/exec.js | 41 + .../fixtures/2020-09-methods/private/input.js | 12 + .../2020-09-methods/private/output.js | 34 + .../fixtures/2020-09-methods/public/exec.js | 50 + .../fixtures/2020-09-methods/public/input.js | 13 + .../fixtures/2020-09-methods/public/output.js | 26 + .../2020-09-methods/static-private/exec.js | 39 + .../2020-09-methods/static-private/input.js | 12 + .../2020-09-methods/static-private/output.js | 29 + .../2020-09-methods/static-public/exec.js | 48 + .../2020-09-methods/static-public/input.js | 13 + .../2020-09-methods/static-public/output.js | 24 + .../2020-09-misc/initializer-timing/exec.js | 34 + .../test/fixtures/2020-09-misc/options.json | 11 + .../valid-expression-formats/input.js | 22 + .../valid-expression-formats/output.js | 47 + .../fixtures/2020-09-setters/options.json | 11 + .../fixtures/2020-09-setters/private/exec.js | 40 + .../fixtures/2020-09-setters/private/input.js | 12 + .../2020-09-setters/private/output.js | 37 + .../fixtures/2020-09-setters/public/exec.js | 50 + .../fixtures/2020-09-setters/public/input.js | 13 + .../fixtures/2020-09-setters/public/output.js | 26 + .../2020-09-setters/static-private/exec.js | 38 + .../2020-09-setters/static-private/input.js | 12 + .../2020-09-setters/static-private/output.js | 34 + .../2020-09-setters/static-public/exec.js | 49 + .../2020-09-setters/static-public/input.js | 13 + .../2020-09-setters/static-public/output.js | 24 + .../src/index.ts | 12 +- packages/babel-runtime-corejs2/package.json | 9 + packages/babel-runtime-corejs3/package.json | 9 + packages/babel-runtime/package.json | 9 + .../src/ast-types/generated/index.ts | 2 + .../src/builders/generated/index.ts | 2 + packages/babel-types/src/definitions/core.ts | 2 + yarn.lock | 1 + 148 files changed, 5748 insertions(+), 8 deletions(-) create mode 100644 packages/babel-helpers/src/helpers/applyDecs.js create mode 100644 packages/babel-plugin-proposal-decorators/CONTRIB.md create mode 100644 packages/babel-plugin-proposal-decorators/src/transformer-2020-09.ts create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/options.json create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/private/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/private/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/private/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/public/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/public/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/public/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/static-private/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/static-private/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/static-private/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/static-public/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/static-public/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/static-public/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-private/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-private/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-private/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-public/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-public/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-public/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-static-private/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-static-private/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-static-private/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-static-public/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-static-public/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-static-public/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/expressions/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/expressions/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/inheritance/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/inheritance/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/inheritance/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/initializers/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/initializers/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/initializers/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/options.json create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/replacement-with-expr/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/replacement-with-expr/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/replacement-with-expr/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/replacement/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/replacement/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/replacement/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/computed-keys-same-ast/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/computed-keys-same-ast/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/computed-keys-same-ast/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/computed-keys-same-value/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/computed-keys-same-value/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/computed-keys-same-value/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/method-and-field/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/method-and-field/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/method-and-field/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/methods-with-same-key/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/methods-with-same-key/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/methods-with-same-key/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/options.json create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/options.json create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/private/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/private/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/private/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/public/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/public/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/public/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/static-private/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/static-private/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/static-private/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/static-public/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/static-public/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/static-public/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/options.json create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/private/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/private/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/private/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/public/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/public/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/public/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/static-private/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/static-private/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/static-private/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/static-public/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/static-public/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/static-public/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/options.json create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/private/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/private/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/private/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/public/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/public/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/public/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/static-private/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/static-private/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/static-private/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/static-public/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/static-public/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/static-public/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/class/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/getting-previously-set-metadata/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/inheritance-private/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/inheritance-public/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/non-symbol-keys/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/options.json create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/proto-private/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/proto-public/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/static-private/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/static-public/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/options.json create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/private/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/private/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/private/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/public/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/public/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/public/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/static-private/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/static-private/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/static-private/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/static-public/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/static-public/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/static-public/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-misc/initializer-timing/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-misc/options.json create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-misc/valid-expression-formats/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-misc/valid-expression-formats/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/options.json create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/private/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/private/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/private/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/public/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/public/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/public/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/static-private/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/static-private/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/static-private/output.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/static-public/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/static-public/input.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/static-public/output.js diff --git a/packages/babel-helper-create-class-features-plugin/src/decorators.ts b/packages/babel-helper-create-class-features-plugin/src/decorators.ts index d4175e68a395..432439aed912 100644 --- a/packages/babel-helper-create-class-features-plugin/src/decorators.ts +++ b/packages/babel-helper-create-class-features-plugin/src/decorators.ts @@ -80,7 +80,10 @@ function extractElementDescriptor( const properties: t.ObjectExpression["properties"] = [ prop("kind", t.stringLiteral(t.isClassMethod(node) ? node.kind : "field")), prop("decorators", takeDecorators(node as Decorable)), - prop("static", node.static && t.booleanLiteral(true)), + prop( + "static", + !t.isStaticBlock(node) && node.static && t.booleanLiteral(true), + ), prop("key", getKey(node)), ].filter(Boolean); diff --git a/packages/babel-helper-create-class-features-plugin/src/fields.ts b/packages/babel-helper-create-class-features-plugin/src/fields.ts index cbaee3f1ca2d..bebb6a107cc9 100644 --- a/packages/babel-helper-create-class-features-plugin/src/fields.ts +++ b/packages/babel-helper-create-class-features-plugin/src/fields.ts @@ -911,7 +911,7 @@ function replaceThisContext( getSuperRef, getObjectRef() { state.needsClassRef = true; - return isStaticBlock || path.node.static + return t.isStaticBlock(path.node) || path.node.static ? ref : t.memberExpression(ref, t.identifier("prototype")); }, @@ -931,7 +931,8 @@ function replaceThisContext( export type PropNode = | t.ClassProperty | t.ClassPrivateMethod - | t.ClassPrivateProperty; + | t.ClassPrivateProperty + | t.StaticBlock; export type PropPath = NodePath; export function buildFieldsInitNodes( @@ -963,7 +964,7 @@ export function buildFieldsInitNodes( for (const prop of props) { prop.isClassProperty() && ts.assertFieldTransformed(prop); - const isStatic = prop.node.static; + const isStatic = !t.isStaticBlock(prop.node) && prop.node.static; const isInstance = !isStatic; const isPrivate = prop.isPrivate(); const isPublic = !isPrivate; diff --git a/packages/babel-helper-create-class-features-plugin/src/index.ts b/packages/babel-helper-create-class-features-plugin/src/index.ts index 6e242a58c7c4..70da4597d09b 100644 --- a/packages/babel-helper-create-class-features-plugin/src/index.ts +++ b/packages/babel-helper-create-class-features-plugin/src/index.ts @@ -170,7 +170,7 @@ export function createClassFeaturePlugin({ path.isPrivate() || path.isStaticBlock?.() ) { - props.push(path); + props.push(path as PropPath); } } } @@ -246,7 +246,7 @@ export function createClassFeaturePlugin({ (referenceVisitor, state) => { if (isDecorated) return; for (const prop of props) { - if (prop.node.static) continue; + if (t.isStaticBlock(prop.node) || prop.node.static) continue; prop.traverse(referenceVisitor, state); } }, diff --git a/packages/babel-helpers/src/helpers-generated.ts b/packages/babel-helpers/src/helpers-generated.ts index 1f148f411cae..3ad1c4f67b23 100644 --- a/packages/babel-helpers/src/helpers-generated.ts +++ b/packages/babel-helpers/src/helpers-generated.ts @@ -13,6 +13,10 @@ function helper(minVersion, source) { } export default Object.freeze({ + applyDecs: helper( + "7.16.6", + 'var getOwnPropertyDescriptor=Object.getOwnPropertyDescriptor,defineProperty=Object.defineProperty,hasOwnProperty=Object.hasOwnProperty,create=Object.create,getOwnPropertySymbols=Object.getOwnPropertySymbols,setPrototypeOf=Object.setPrototypeOf,assign=Object.assign,isArray=Array.isArray,arrayFrom=Array.from,CONSTRUCTOR=0,PUBLIC=1,PRIVATE=2,symbolMessage="Metadata keys must be symbols, received: ";function createMetadataMethodsForProperty(metadataMap,kind,property){return{getMetadata(key){if("symbol"!=typeof key)throw new TypeError(symbolMessage+key);var metadataForKey=metadataMap[key];if(void 0!==metadataForKey)if(kind===PUBLIC){var pub=metadataForKey.public;if(void 0!==pub)return pub[property]}else if(kind===PRIVATE){var priv=metadataForKey.private;if(void 0!==priv)return priv.get(property)}else if(hasOwnProperty.call(metadataForKey,"constructor"))return metadataForKey.constructor},setMetadata(key,value){if("symbol"!=typeof key)throw new TypeError(symbolMessage+key);var metadataForKey=metadataMap[key];if(void 0===metadataForKey&&(metadataForKey=metadataMap[key]={}),kind===PUBLIC){var pub=metadataForKey.public;void 0===pub&&(pub=metadataForKey.public=create(null)),pub[property]=value}else if(kind===PRIVATE){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],metadataKeys=getOwnPropertySymbols(metadataMap);if(0!==metadataKeys.length){for(var i=0;i=STATIC;if(isStatic?(base=Class,metadataMap=staticMetadataMap,kind-=STATIC,staticInitializers||(staticInitializers=[]),initializers=staticInitializers):(base=Class.prototype,metadataMap=protoMetadataMap,protoInitializers||(protoInitializers=[]),initializers=protoInitializers),kind!==FIELD&&!privateName){var existingNonFields=isStatic?existingStaticNonFields:existingProtoNonFields,existingKind=existingNonFields.get(prop)||0;if(!0===existingKind||existingKind===GETTER&&kind!==SETTER||existingKind===SETTER&&kind!==GETTER)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: "+prop);!existingKind&&kind>METHOD?existingNonFields.set(prop,kind):existingNonFields.set(prop,!0)}applyMemberDec(ret,base,isStatic,metadataMap,initializers,decs,kind,prop,privateName)}}protoInitializers&&pushInitializers(ret,protoInitializers),staticInitializers&&pushInitializers(ret,staticInitializers)}function pushInitializers(ret,initializers){initializers.length>0?(initializers=initializers.slice(),ret.push((function(instance){for(var i=0;i0?ret.push((function(){for(var i=0;i= STATIC; + var base; + var metadataMap; + var initializers; + + if (isStatic) { + base = Class; + metadataMap = staticMetadataMap; + kind = kind - STATIC; + + if (!staticInitializers) { + staticInitializers = []; + } + + initializers = staticInitializers; + } else { + base = Class.prototype; + metadataMap = protoMetadataMap; + + if (!protoInitializers) { + protoInitializers = []; + } + + initializers = protoInitializers; + } + + if (kind !== FIELD && !privateName) { + var existingNonFields = isStatic + ? existingStaticNonFields + : existingProtoNonFields; + + var existingKind = existingNonFields.get(prop) || 0; + + if ( + existingKind === true || + (existingKind === GETTER && kind !== SETTER) || + (existingKind === SETTER && kind !== GETTER) + ) { + 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: " + + prop + ); + } else if (!existingKind && kind > METHOD) { + existingNonFields.set(prop, kind); + } else { + existingNonFields.set(prop, true); + } + } + + applyMemberDec( + ret, + base, + isStatic, + metadataMap, + initializers, + decs, + kind, + prop, + privateName + ); + } + + if (protoInitializers) { + pushInitializers(ret, protoInitializers); + } + + if (staticInitializers) { + pushInitializers(ret, staticInitializers); + } +} + +function pushInitializers(ret, initializers) { + if (initializers.length > 0) { + // Slice the array, which means that `addInitializer` can no longer add + // additional initializers to the array + initializers = initializers.slice(); + + ret.push(function (instance) { + for (var i = 0; i < initializers.length; i++) { + initializers[i].call(instance, instance); + } + }); + } else { + ret.push(function () {}); + } +} + +function applyClassDecs(ret, targetClass, metadataMap, classDecs) { + var initializers = []; + var newClass = targetClass; + + var name = targetClass.name; + var ctx = assign( + { + kind: "class", + name: name, + addInitializer: createAddInitializerMethod(initializers), + }, + createMetadataMethodsForProperty(metadataMap, CONSTRUCTOR, name) + ); + + for (var i = 0; i < classDecs.length; i++) { + newClass = classDecs[i](newClass, ctx) || newClass; + } + + ret.push(newClass); + + if (initializers.length > 0) { + ret.push(function () { + for (var i = 0; i < initializers.length; i++) { + initializers[i].call(newClass, newClass); + } + }); + } else { + ret.push(function () {}); + } +} + +/** + Basic usage: + + ```js + applyDecs( + Class, + [ + // member decorators + [ + dec, // dec or array of decs + 0, // kind of value being decorated + 'prop', // name of public prop on class containing the value being decorated, + '#p', // the name of the private property (if is private, undefined otherwise), + ] + ], + [ + // class decorators + dec1, dec2 + ] + ) + ``` + + Fully transpiled example: + + ```js + @dec + class Class { + @dec + a = 123; + + @dec + #a = 123; + + @dec + @dec2 + accessor b = 123; + + @dec + accessor #b = 123; + + @dec + c() { console.log('c'); } + + @dec + #c() { console.log('privC'); } + + @dec + get d() { console.log('d'); } + + @dec + get #d() { console.log('privD'); } + + @dec + set e(v) { console.log('e'); } + + @dec + set #e(v) { console.log('privE'); } + } + + + // becomes + let initializeInstance; + let initializeClass; + + let initA; + let initPrivA; + + let initB; + let initPrivB, getPrivB, setPrivB; + + let privC; + let privD; + let privE; + + let Class; + class _Class { + static { + let ret = applyDecs( + this, + [ + [dec, 0, 'a'], + [dec, 0, 'accessPrivA', 'a'] + [[dec, dec2], 1, 'b'], + [dec, 1, 'accessPrivB', 'b'], + [dec, 2, 'c'], + [dec, 2, 'privC', 'c'], + [dec, 3, 'd'], + [dec, 3, 'privD', 'd'], + [dec, 4, 'e'], + [dec, 4, 'privE', 'e'], + ], + [ + dec + ] + ) + + initA = ret[0]; + + initPrivA = ret[1]; + + initB = ret[2]; + + initPrivB = ret[3]; + getPrivB = ret[4]; + setPrivB = ret[5]; + + privC = ret[6]; + + privD = ret[7]; + + privE = ret[8]; + + initializeInstance = ret[9]; + + Class = ret[10] + + initializeClass = ret[11]; + } + + a = (initializeInstance(this), initA(this, 123)); + + #a = initPrivA(this, 123); + get accessPrivA() { return this.#a } + set accessPrivA(v) { this.#a = v } + + #bData = initB(this, 123); + get b() { return this.#bData } + set b(v) { this.#bData = v } + + #privBData = initPrivB(this, 123); + get accessPrivB() { return this.#privBData } + set accessPrivB(v) { this.#privBData = v } + get #b() { return getPrivB(this); } + set #b(v) { setPrivB(this, v); } + + c() { console.log('c'); } + + #c(...args) { return privC(this, args) } + privC() { console.log('privC'); } + + get d() { console.log('d'); } + + get #d() { return privD(this); } + privD() { console.log('privD'); } + + set e(v) { console.log('e'); } + + set #e(v) { return privE(this, v); } + privE(v) { console.log('privE'); } + } + + initializeClass(Class); + ``` + */ +export default function applyDecs(targetClass, memberDecs, classDecs) { + var ret = []; + var staticMetadataMap = {}; + + if (memberDecs) { + var protoMetadataMap = {}; + + applyMemberDecs( + ret, + targetClass, + protoMetadataMap, + staticMetadataMap, + memberDecs + ); + + convertMetadataMapToFinal(targetClass.prototype, protoMetadataMap); + } + + if (classDecs) { + applyClassDecs(ret, targetClass, staticMetadataMap, classDecs); + } + + convertMetadataMapToFinal(targetClass, staticMetadataMap); + + return ret; +} diff --git a/packages/babel-plugin-proposal-decorators/CONTRIB.md b/packages/babel-plugin-proposal-decorators/CONTRIB.md new file mode 100644 index 000000000000..8e4172d4059f --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/CONTRIB.md @@ -0,0 +1,374 @@ +These are notes about the implementation of the 2020-09 decorators transform. +The implementation's goals are (in descending order): + +1. Being accurate to the actual proposal (e.g. not defining additional + properties unless required, matching semantics exactly, etc.). This includes + being able to work properly with private fields and methods. +2. Transpiling to a very minimal and minifiable output. This transform will + affect each and every decorated class, so ensuring that the output is not 10x + the size of the original is important. +3. Having good runtime performance. Decoration output has the potential to + drastically impact startup performance, since it runs whenever a decorated + class is defined. In addition, every instance of a decorated class may be + impacted for certain types of decorators. + +All of these goals come somewhat at the expense of readability and can make the +implementation difficult to understand, so these notes are meant to document the +motivations behind the design. + +## Overview + +Given a simple decorated class like this one: + +```js +@dec +class Class { + @dec a = 123; + + @dec static #b() { + console.log('foo'); + } + + [someVal]() {} + + @dec + @dec2 + accessor #c = 456; +} +``` + +It's output would be something like the following: + +```js +import { applyDecs } from '@babel/helpers'; + +let initInstance, initClass, initA, callB, computedKey, initC, getC, setC; + +const elementDecs = [ + [dec, 0, 'a'], + [dec, 7, 'x', '#b'] + (computedKey = someVal, null) + [[dec, dec2], 1, 'y', '#c'] +]; + +const classDecs = [dec]; + +let Class; +class _Class { + static { + let ret = applyDecs( + this, + elementDecs, + [dec] + ); + + initA = ret[0]; + callB = ret[1]; + initC = ret[2]; + getC = ret[3]; + setC = ret[4]; + initInstance = ret[5]; + Class = ret[6]; + initClass = ret[7]; + } + + a = (initInstance(this), initA(this, 123)); + + static #b(...args) { + callB(this, args); + } + static x() { + console.log('foo'); + } + + [computedKey]() {} + + #y = initC(this, 123); + get y() { + return this.#y; + } + set y(v) { + this.#y = v; + } + get #c() { + return getC(this); + } + set #c(v) { + setC(this, v); + } + + static { + initClass(C); + } +} +``` + +Let's break this output down a bit: + +```js +let initInstance, initClass, initA, callB, initC, getC, setC; +``` + +First, we need to setup some local variables outside of the class. These are +for: + +- Decorated class field/accessor initializers +- Extra initializer functions added by `addInitializers` +- Private class methods + +These are essentially all values that cannot be defined on the class itself via +`Object.defineProperty`, so we have to insert them into the class manually, +ahead of time and populate them when we run our decorators. + +```js +const elementDecs = [ + [dec, 0, 'a'], + [dec, 7, 'x', '#b'] + (computedKey = someVal, null) + [[dec, dec2], 1, 'y', '#c'] +]; + +const classDecs = [dec]; +``` + +Next up, we define and evaluate the decorator member expressions. The reason we +do this _before_ defining the class is because we must interleave decorator +expressions with computed property key expressions, since computed properties +and decorators can run arbitrary code which can modify the runtime of subsequent +decorators or computed property keys. + +```js +let Class; +class _Class { +``` + +This class is being decorated directly, which means that the decorator may +replace the class itself. Class bindings are not mutable, so we need to create a +new `let` variable for the decorated class. + + +```js + static { + let ret = applyDecs( + this, + elementDecs, + classDecs + ); + + initA = ret[0]; + callB = ret[1]; + initC = ret[2]; + getC = ret[3]; + setC = ret[4]; + initInstance = ret[5]; + Class = ret[6]; + initClass = ret[7]; + } +``` + +Next, we immediately define a `static` block which actually applies the +decorators. This is important because we must apply the decorators _after_ the +class prototype has been fully setup, but _before_ static fields are run, since +static fields should only see the decorated version of the class. + +We apply the decorators to class elements and the class itself, and the +application returns an array of values that are used to populate all of the +local variables we defined earlier. The array's order is fully deterministic, so +we can assign the values based on an index we can calculate ahead of time. + +We'll come back to `applyDecs` in a bit to dig into what its format is exactly, +but now let's dig into the new definitions of our class elements. + +```js + a = (initInstance(this), initA(this, 123)); +``` + +Alright, so previously this was a simple class field. Since it's the first field +on the class, we've updated it to immediately call `initInstance` in its +initializer. This calls any initializers added with `addInitializer` for all of +the per-class values (methods and accessors), which should all be setup on the +instance before class fields are assigned. Then, it calls `initA` to get the +initial value of the field, which allows initializers returned from the +decorator to intercept and decorate it. It's important that the initial value +is used/defined _within_ the class body, because initializers can now refer to +private class fields, e.g. `a = this.#b` is a valid field initializer and would +become `a = initA(this, this.#b)`, which would also be valid. We cannot +extract initializer code, or any other code, from the class body because of +this. + +Overall, this decoration is pretty straightforward other than the fact that we +have to reference `initA` externally. + +```js + static #b(...args) { + callB(this, args); + } + static x() { + console.log('foo'); + } +``` + +Next up, we have a private static class method `#b`. This one is a bit more +complex, as our definition has been broken out into 2 parts: + +1. `static #b`: This is the method itself, which being a private method we + cannot overwrite with `defineProperty`. We also can't convert it into a + private field because that would change its semantics (would make it + writable). So, we instead have it proxy to the locally scoped `callB` + variable, which will be populated with the fully decorated method. +2. `static x`: This contains the _code_ of the original method. Once again, this + code cannot be removed from the class body because it may reference private + identifiers. However, we have moved the code to a _public_ method, which means + we can now read its value using `Object.getOwnPropertyDescriptor`. Decorators + use this to get the initial implementation of the method, which can then be + wrapped with decorator code/logic. They then `delete` this temporary property, + which is necessary because no additional elements should be added to a class + definition. + + The name for this method is unimportant, but because public method names + cannot be minified and we also need to pass the name into `applyDecs`, we + generate as small of a unique identifier as possible here, starting with 1 + char names which are not taken and growing until we find one that is free. + +```js + [computedKey]() {} +``` + +Next is the undecorated method with a computed key. This uses the previously +calculated and stored computed key. + +```js + #y = initC(this, 123); + get y() { + return this.#y; + } + set y(v) { + this.#y = v; + } + get #c() { + return getC(this); + } + set #c(v) { + setC(this, v); + } +``` + +Next up, we have the output for `accessor #c`. This is the most complicated +case, since we have to transpile the decorators, the `accessor` keyword, and +target a private field. Breaking it down piece by piece: + +```js + #y = initC(this, 123); +``` + +`accessor #c` desugars to a getter and setter which are backed by a new private +field, `#y`. Like before, the name of this field doesn't really matter, we'll +just generate a short, unique name. We call the decorated initializer for `#c` +and return that value to assign to the field. + +```js + get y() { + return this.#y; + } + set y(v) { + this.#y = v; + } +``` + +Next we have a getter and setter named `y` which provide access to the backing +storage for the accessor. These are the base getter/setter for the accessor, +which the decorator will get via `Object.getOwnPropertyDescriptor`. They will be +deleted from the class fully, so again the name is not important here, just +needs to be short. + +```js + get #c() { + return getC(this); + } + set #c(v) { + setC(this, v); + } +``` + +Next, we have the getter and setter for `#c` itself. These methods defer to +the `getC` and `setC` local variables, which will be the decorated versions of +the `get y` and `set y` methods from the previous step. + +```js + static { + initClass(C); + } +``` + +Finally, we call `initClass` in another static block, running any class and +static method initializers on the final class. This is done in a static block +for convenience with class expressions, but it could run immediately after the +class is defined. + +Ok, so now that we understand the general output, let's go back to `applyDecs`: + +```js +const elementDecs = [ + [dec, 0, 'a'], + [dec, 7, 'x', '#b'] + (computedKey = someVal, null) + [[dec, dec2], 1, 'y', '#c'] +]; + +const classDecs = [dec]; + +// ... + +let ret = applyDecs( + this, + elementDecs, + classDecs +); +``` + +`applyDecs` takes all of the decorators for the class and applies them. It +receives the following arguments: + +1. The class itself +2. Decorators to apply to class elements +3. Decorators to apply to the class itself + +The format of the data is designed to be as minimal as possible. Here's an +annotated version of the member descriptors: + +```js +[ + // List of decorators to apply to the field. Array if multiple decorators, + // otherwise just the single decorator itself. + dec, + + // The type of the decorator, represented as an enum. Static-ness is also + // encoded by adding 5 to the values + // 0 === FIELD + // 1 === ACCESSOR + // 2 === METHOD + // 3 === GETTER + // 4 === SETTER + // 5 === FIELD + STATIC + // 6 === ACCESSOR + STATIC + // 7 === METHOD + STATIC + // 8 === GETTER + STATIC + // 9 === SETTER + STATIC + 1, + + // The name of the public property that can be used to access the value. + // For public members this is the actual name, for private members this is + // the name of the public property which can be used to access the value + // (see descriptions of #b and #c above) + 'y', + + // Optional fourth value, this is the spelling of the private element's name, + // which signals that the element is private to `applyDecs` and is used in the + // decorator's context object + '#c' +], +``` + +Static and prototype decorators are all described like this. For class +decorators, it's just the list of decorators since no other context +is necessary. diff --git a/packages/babel-plugin-proposal-decorators/src/index.ts b/packages/babel-plugin-proposal-decorators/src/index.ts index a7e142769038..f439b5b5986b 100644 --- a/packages/babel-plugin-proposal-decorators/src/index.ts +++ b/packages/babel-plugin-proposal-decorators/src/index.ts @@ -7,6 +7,7 @@ import { FEATURES, } from "@babel/helper-create-class-features-plugin"; import legacyVisitor from "./transformer-legacy"; +import transformer2020_09 from "./transformer-2020-09"; export default declare((api, options) => { api.assertVersion(7); @@ -16,7 +17,7 @@ export default declare((api, options) => { throw new Error("'legacy' must be a boolean."); } - const { decoratorsBeforeExport } = options; + const { decoratorsBeforeExport, version } = options; if (decoratorsBeforeExport === undefined) { if (!legacy) { throw new Error( @@ -37,6 +38,10 @@ export default declare((api, options) => { } if (legacy) { + if (version !== undefined) { + throw new Error("'version' can't be used with legacy decorators"); + } + return { name: "proposal-decorators", inherits: syntaxDecorators, @@ -47,6 +52,12 @@ export default declare((api, options) => { }; } + if (version === "2020-09") { + return transformer2020_09(api, options); + } else if (!(version === "2018-09" || version === undefined)) { + throw new Error("Unsupported decorators version: " + version); + } + return createClassFeaturePlugin({ name: "proposal-decorators", diff --git a/packages/babel-plugin-proposal-decorators/src/transformer-2020-09.ts b/packages/babel-plugin-proposal-decorators/src/transformer-2020-09.ts new file mode 100644 index 000000000000..ca4bbb70f92c --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/src/transformer-2020-09.ts @@ -0,0 +1,867 @@ +import type { NodePath } from "@babel/traverse"; +import { types as t } from "@babel/core"; +import syntaxDecorators from "@babel/plugin-syntax-decorators"; + +type ClassDecoratableElement = + | t.ClassMethod + | t.ClassPrivateMethod + | t.ClassProperty + | t.ClassPrivateProperty + | t.ClassAccessorProperty; + +type ClassElement = + | ClassDecoratableElement + | t.TSDeclareMethod + | t.TSIndexSignature + | t.StaticBlock; + +type classUidGenerator = ( + isPrivate: B, +) => B extends true ? t.PrivateName : t.Identifier; + +function incrementId(id: number[], idx = id.length - 1): void { + // If index is -1, id needs an additional character, unshift A + if (idx === -1) { + id.unshift(65); + return; + } + + const current = id[idx]; + + if (current === 90) { + // if current is Z, skip to a + id[idx] = 97; + } else if (current === 122) { + // if current is z, reset to A and carry the 1 + id[idx] = 65; + incrementId(id, idx - 1); + } else { + // else, increment by one + id[idx] = current + 1; + } +} + +/** + * Generates a new element name that is unique to the given class. This can be + * used to create extra class fields and methods for the implementation, while + * keeping the length of those names as small as possible. This is important for + * minification purposes, since public names cannot be safely renamed/minified. + * + * Names are split into two namespaces, public and private. Static and non-static + * names are shared in the same namespace, because this is true for private names + * (you cannot have #x and static #x in the same class) and it's not worth the + * extra complexity for public names. + */ +function createUidGeneratorForClass( + body: NodePath[], +): (isPrivate: boolean) => t.Identifier | t.PrivateName { + let currentPublicId: number[], currentPrivateId: number[]; + + const publicNames = new Set(); + const privateNames = new Set(); + + for (const element of body) { + if ( + element.node.type === "TSIndexSignature" || + element.node.type === "StaticBlock" + ) { + continue; + } + + const { key } = element.node; + + if (key.type === "PrivateName") { + privateNames.add(key.id.name); + } else if (key.type === "Identifier") { + publicNames.add(key.name); + } + } + + return (isPrivate: boolean): t.Identifier | t.PrivateName => { + let currentId: number[], names: Set; + + if (isPrivate) { + if (!currentPrivateId) { + currentPrivateId = [65]; + } + + currentId = currentPrivateId; + names = privateNames; + } else { + if (!currentPublicId) { + currentPublicId = [65]; + } + + currentId = currentPublicId; + names = publicNames; + } + + let reifiedId = String.fromCharCode(...currentId); + + while (names.has(reifiedId)) { + incrementId(currentId); + reifiedId = String.fromCharCode(...currentId); + } + + incrementId(currentId); + + if (isPrivate) { + return t.privateName(t.identifier(reifiedId)); + } else { + return t.identifier(reifiedId); + } + }; +} + +/** + * Wraps the above generator function so that it's run lazily the first time + * it's actually required. Several types of decoration do not require this, so it + * saves iterating the class elements an additional time and allocating the space + * for the Sets of element names. + */ +function createLazyUidGeneratorForClass( + body: NodePath[], +): classUidGenerator { + let generator: (isPrivate: boolean) => t.Identifier | t.PrivateName; + + const lazyGenerator = (isPrivate: boolean): t.Identifier | t.PrivateName => { + if (!generator) { + generator = createUidGeneratorForClass(body); + } + + return generator(isPrivate); + }; + + return lazyGenerator as unknown as classUidGenerator; +} + +/** + * Takes a class definition and replaces it with an equivalent class declaration + * which is then assigned to a local variable. This allows us to reassign the + * local variable with the decorated version of the class. The class definition + * retains its original name so that `toString` is not affected, other + * references to the class are renamed instead. + */ +function replaceClassWithVar( + path: NodePath, +): [t.Identifier, NodePath] { + if (path.type === "ClassDeclaration") { + const varId = path.scope.generateUidIdentifierBasedOnNode(path.node.id); + const classId = t.identifier(path.node.id.name); + + path.scope.rename(classId.name, varId.name); + + path.insertBefore( + t.variableDeclaration("let", [t.variableDeclarator(varId)]), + ); + path.get("id").replaceWith(classId); + + return [t.cloneNode(varId), path]; + } else { + let className: string; + let varId: t.Identifier; + + if (path.node.id) { + className = path.node.id.name; + varId = generateLocalVarId(path, className); + path.scope.rename(className, varId.name); + } else if ( + path.parentPath.node.type === "VariableDeclarator" && + path.parentPath.node.id.type === "Identifier" + ) { + className = path.parentPath.node.id.name; + varId = generateLocalVarId(path, className); + } else { + varId = generateLocalVarId(path, "decorated_class"); + } + + const newClassExpr = t.classExpression( + className && t.identifier(className), + path.node.superClass, + path.node.body, + ); + + const [newPath] = path.replaceWith( + t.sequenceExpression([newClassExpr, varId]), + ); + + return [t.cloneNode(varId), newPath.get("expressions.0")]; + } +} + +function generateClassProperty( + key: t.PrivateName | t.Identifier, + value: t.Expression | undefined, + isStatic: boolean, +): t.ClassPrivateProperty | t.ClassProperty { + if (key.type === "PrivateName") { + return t.classPrivateProperty(key, value, undefined, isStatic); + } else { + return t.classProperty(key, value, undefined, undefined, isStatic); + } +} + +function addProxyAccessorsFor( + element: NodePath, + originalKey: t.PrivateName | t.Expression, + targetKey: t.PrivateName, + isComputed = false, +): void { + const { static: isStatic } = element.node; + + const getterBody = t.blockStatement([ + t.returnStatement(t.memberExpression(t.thisExpression(), targetKey)), + ]); + + const setterBody = t.blockStatement([ + t.expressionStatement( + t.assignmentExpression( + "=", + t.memberExpression(t.thisExpression(), targetKey), + t.identifier("v"), + ), + ), + ]); + + let getter: t.ClassMethod | t.ClassPrivateMethod, + setter: t.ClassMethod | t.ClassPrivateMethod; + + if (originalKey.type === "PrivateName") { + getter = t.classPrivateMethod( + "get", + t.cloneNode(originalKey), + [], + getterBody, + isStatic, + ); + setter = t.classPrivateMethod( + "set", + t.cloneNode(originalKey), + [t.identifier("v")], + setterBody, + isStatic, + ); + } else { + getter = t.classMethod( + "get", + t.cloneNode(originalKey), + [], + getterBody, + isComputed, + isStatic, + ); + setter = t.classMethod( + "set", + t.cloneNode(originalKey), + [t.identifier("v")], + setterBody, + isComputed, + isStatic, + ); + } + + element.insertAfter(setter); + element.insertAfter(getter); +} + +const FIELD = 0; +const ACCESSOR = 1; +const METHOD = 2; +const GETTER = 3; +const SETTER = 4; + +const STATIC = 5; + +function getElementKind(element: NodePath): number { + switch (element.node.type) { + case "ClassProperty": + case "ClassPrivateProperty": + return FIELD; + case "ClassAccessorProperty": + return ACCESSOR; + case "ClassMethod": + case "ClassPrivateMethod": + if (element.node.kind === "get") { + return GETTER; + } else if (element.node.kind === "set") { + return SETTER; + } else { + return METHOD; + } + } +} + +function generateLocalVarId(path: NodePath, name: string): t.Identifier { + const varId = path.scope.generateUidIdentifier(name); + path.scope.parent.push({ id: varId }); + return t.cloneNode(varId); +} + +// Information about the decorators applied to an element +interface DecoratorInfo { + // The expressions of the decorators themselves + decorators: t.Expression[]; + + // The kind of the decorated value, matches the kind value passed to applyDecs + kind: number; + + // whether or not the field is static + isStatic: boolean; + + // The public key of the decorator values/methods that it exposes + publicKey: t.StringLiteral | t.Expression; + + // The spelling of the private name (should it exist) + privateKey: t.PrivateName | undefined; + + // The names of local variables that will be used/returned from the decoration + locals: t.Identifier | t.Identifier[] | undefined; +} + +// Information about a computed property key. These must be evaluated +// interspersed with decorator expressions, which is why they get added to the +// array of DecoratorInfos later on. +interface ComputedPropInfo { + localComputedNameId: t.Identifier; + keyNode: t.Expression; +} + +function generateDecorationExprs( + decoratorInfo: (DecoratorInfo | ComputedPropInfo)[], +): t.ArrayExpression { + return t.arrayExpression( + decoratorInfo.map(el => { + if ("decorators" in el) { + const decs = + el.decorators.length > 1 + ? t.arrayExpression(el.decorators) + : el.decorators[0]; + + const kind = el.isStatic ? el.kind + STATIC : el.kind; + + return t.arrayExpression( + [ + decs, + t.numericLiteral(kind), + t.cloneNode(el.publicKey), + el.privateKey ? t.stringLiteral(el.privateKey.id.name) : undefined, + ].filter(v => v), + ); + } else { + return t.sequenceExpression([ + t.assignmentExpression("=", el.localComputedNameId, el.keyNode), + t.nullLiteral(), + ]); + } + }), + ); +} + +function extractElementLocalAssignments( + decorationInfo: (DecoratorInfo | ComputedPropInfo)[], +) { + const locals: t.Identifier[] = []; + + for (const el of decorationInfo) { + if ("locals" in el && el.locals) { + if (Array.isArray(el.locals)) { + locals.push(...el.locals); + } else { + locals.push(el.locals); + } + } + } + + return locals; +} + +function addCallAccessorsFor( + element: NodePath, + key: t.PrivateName, + getId: t.Identifier, + setId: t.Identifier, +) { + element.insertAfter( + t.classPrivateMethod( + "get", + t.cloneNode(key), + [], + t.blockStatement([ + t.expressionStatement( + t.callExpression(t.cloneNode(getId), [t.thisExpression()]), + ), + ]), + ), + ); + + element.insertAfter( + t.classPrivateMethod( + "set", + t.cloneNode(key), + [t.identifier("v")], + t.blockStatement([ + t.expressionStatement( + t.callExpression(t.cloneNode(setId), [ + t.thisExpression(), + t.identifier("v"), + ]), + ), + ]), + ), + ); +} + +function movePrivateMethod( + element: NodePath, + privateKey: t.PrivateName, + publicKey: t.Identifier, + methodLocalVar: t.Identifier, + isStatic: boolean, +) { + element.insertAfter( + t.classMethod( + "method", + publicKey, + element.node.params, + element.node.body, + false, + isStatic, + ), + ); + + let params: (t.Identifier | t.RestElement)[]; + let block: t.Statement[]; + + if (element.node.kind === "method") { + params = [t.restElement(t.identifier("args"))]; + block = [ + t.returnStatement( + t.callExpression(methodLocalVar, [ + t.thisExpression(), + t.identifier("args"), + ]), + ), + ]; + } else if (element.node.kind === "set") { + params = [t.identifier("v")]; + block = [ + t.expressionStatement( + t.callExpression(methodLocalVar, [ + t.thisExpression(), + t.identifier("v"), + ]), + ), + ]; + } else { + params = []; + block = [ + t.returnStatement(t.callExpression(methodLocalVar, [t.thisExpression()])), + ]; + } + + element.replaceWith( + t.classPrivateMethod( + element.node.kind, + privateKey, + params, + t.blockStatement(block), + isStatic, + ), + ); +} + +function isClassDecoratableElementPath( + path: NodePath, +): path is NodePath { + const { type } = path; + + return ( + type !== "TSDeclareMethod" && + type !== "TSIndexSignature" && + type !== "StaticBlock" + ); +} + +function transformClass( + path: NodePath, + state: any, +): NodePath { + const body = path.get("body.body"); + + const classDecorators = path.node.decorators; + let hasElementDecorators = false; + + const generateClassUid = createLazyUidGeneratorForClass(body); + + // Iterate over the class to see if we need to decorate it, and also to + // transform simple auto accessors which are not decorated + for (const element of body) { + if (!isClassDecoratableElementPath(element)) { + continue; + } + + if (element.node.decorators) { + hasElementDecorators = true; + } else if (element.node.type === "ClassAccessorProperty") { + const { key, value, static: isStatic } = element.node; + + const newId = generateClassUid(true); + + const valueNode = value ? t.cloneNode(value) : undefined; + + const newField = generateClassProperty(newId, valueNode, isStatic); + + const [newPath] = element.replaceWith(newField); + addProxyAccessorsFor(newPath, key, newId, element.node.computed); + } + } + + // If nothing is decorated, return + if (!classDecorators && !hasElementDecorators) return; + + const elementDecoratorInfo: (DecoratorInfo | ComputedPropInfo)[] = []; + + let firstFieldPath: + | NodePath + | undefined; + let constructorPath: NodePath | undefined; + let requiresProtoInit = false; + let requiresStaticInit = false; + + if (hasElementDecorators) { + for (const element of body) { + if (!isClassDecoratableElementPath(element)) { + continue; + } + + let { key } = element.node; + const kind = getElementKind(element); + const decorators = element.get("decorators"); + + const isPrivate = key.type === "PrivateName"; + const isComputed = + "computed" in element.node && element.node.computed === true; + const isStatic = !!element.node.static; + + let name = "computedKey"; + + if (isPrivate) { + name = (key as t.PrivateName).id.name; + } else if (key.type === "Identifier") { + name = key.name; + } + + if ( + element.node.type === "ClassMethod" && + element.node.kind === "constructor" + ) { + constructorPath = element as NodePath; + } + + if (isComputed) { + const keyPath = element.get("key"); + const localComputedNameId = generateLocalVarId(keyPath, name); + keyPath.replaceWith(localComputedNameId); + + elementDecoratorInfo.push({ + localComputedNameId: t.cloneNode(localComputedNameId), + keyNode: t.cloneNode(key as t.Expression), + }); + + key = localComputedNameId; + } + + if (Array.isArray(decorators)) { + let publicKey, privateKey, locals; + + if (kind === ACCESSOR) { + const { value } = element.node as t.ClassAccessorProperty; + + const params: t.Expression[] = [t.thisExpression()]; + + if (value) { + params.push(t.cloneNode(value)); + } + + const newId = generateClassUid(true); + const newFieldInitId = generateLocalVarId(element, `init_${name}`); + const newValue = t.callExpression( + t.cloneNode(newFieldInitId), + params, + ); + + const newField = generateClassProperty(newId, newValue, isStatic); + const [newPath] = element.replaceWith(newField); + + if (isPrivate) { + publicKey = generateClassUid(false); + privateKey = key; + + addProxyAccessorsFor(newPath, publicKey, newId); + + const getId = generateLocalVarId(newPath, `get_${name}`); + const setId = generateLocalVarId(newPath, `set_${name}`); + + addCallAccessorsFor(newPath, key as t.PrivateName, getId, setId); + + locals = [newFieldInitId, getId, setId]; + } else { + publicKey = key; + addProxyAccessorsFor(newPath, publicKey, newId, isComputed); + locals = newFieldInitId; + } + } else if (kind === FIELD) { + const initId = generateLocalVarId(element, `init_${name}`); + const valuePath = ( + element as NodePath + ).get("value"); + + valuePath.replaceWith( + t.callExpression( + t.cloneNode(initId), + [t.thisExpression(), valuePath.node].filter(v => v), + ), + ); + + locals = initId; + + if (isPrivate) { + publicKey = generateClassUid(false); + privateKey = key; + + addProxyAccessorsFor(element, publicKey, privateKey); + } else { + publicKey = key; + } + } else if (isPrivate) { + publicKey = generateClassUid(false); + privateKey = key; + + const methodLocalVar = generateLocalVarId(element, `call_${name}`); + + movePrivateMethod( + element as NodePath, + t.cloneNode(privateKey), + t.cloneNode(publicKey), + t.cloneNode(methodLocalVar), + isStatic, + ); + + locals = methodLocalVar; + } else { + publicKey = key; + } + + elementDecoratorInfo.push({ + kind, + decorators: decorators.map(d => d.node.expression), + publicKey: isComputed ? publicKey : t.stringLiteral(publicKey.name), + privateKey, + isStatic, + locals, + }); + + if (kind !== FIELD) { + if (isStatic) { + requiresStaticInit = true; + } else { + requiresProtoInit = true; + } + } + + element.node.decorators = null; + + if (!firstFieldPath && (kind === FIELD || kind === ACCESSOR)) { + firstFieldPath = element as NodePath< + t.ClassProperty | t.ClassPrivateProperty + >; + } + } + } + } + + const elementDecorations = generateDecorationExprs(elementDecoratorInfo); + const classDecorations = t.arrayExpression( + (classDecorators || []).map(d => d.expression), + ); + + const locals: t.Identifier[] = + extractElementLocalAssignments(elementDecoratorInfo); + let protoInitLocal: t.Identifier, + staticInitLocal: t.Identifier, + classInitLocal: t.Identifier; + + if (requiresProtoInit) { + protoInitLocal = generateLocalVarId(path, "initProto"); + locals.push(protoInitLocal); + + const protoInitCall = t.callExpression(t.cloneNode(protoInitLocal), [ + t.thisExpression(), + ]); + + if (firstFieldPath) { + const value = firstFieldPath.get("value"); + const body: t.Expression[] = [protoInitCall]; + + if (value.node) { + body.push(value.node); + } + + value.replaceWith(t.sequenceExpression(body)); + } else if (constructorPath) { + if (path.node.superClass) { + const superPath = constructorPath + .get("body.body") + .find( + path => + path.node.type === "ExpressionStatement" && + path.node.expression.type === "CallExpression" && + path.node.expression.callee.type === "Super", + ); + + if (!superPath) { + throw new Error( + `No super found inside class constructor for ${ + path.node.id ? path.node.id.name : "an anonymous class" + }, but the class extends from another class`, + ); + } + + superPath.insertAfter(t.expressionStatement(protoInitCall)); + } else { + constructorPath.node.body.body.unshift( + t.expressionStatement(protoInitCall), + ); + } + } else { + const body: t.Statement[] = [t.expressionStatement(protoInitCall)]; + + if (path.node.superClass) { + body.unshift( + t.expressionStatement( + t.callExpression(t.super(), [ + t.spreadElement(t.identifier("args")), + ]), + ), + ); + } + + path.node.body.body.unshift( + t.classMethod( + "constructor", + t.identifier("constructor"), + [t.restElement(t.identifier("args"))], + t.blockStatement(body), + ), + ); + } + } + + if (requiresStaticInit) { + staticInitLocal = generateLocalVarId(path, "initStatic"); + locals.push(staticInitLocal); + } + + if (classDecorators) { + classInitLocal = generateLocalVarId(path, "initClass"); + + const [localId, classPath] = replaceClassWithVar(path); + path = classPath; + + locals.push(localId, classInitLocal); + path.node.decorators = null; + } + + const classDecsLocal = generateLocalVarId(path, "classDecs"); + const elementDecsLocal = generateLocalVarId(path, "elementDecs"); + + const staticBlock = t.staticBlock( + [ + t.variableDeclaration("let", [ + t.variableDeclarator( + t.identifier("ret"), + t.callExpression(state.addHelper("applyDecs"), [ + t.thisExpression(), + t.cloneNode(elementDecsLocal), + t.cloneNode(classDecsLocal), + ]), + ), + ]), + ...locals.map((local, idx) => { + return t.expressionStatement( + t.assignmentExpression( + "=", + local, + t.memberExpression( + t.identifier("ret"), + t.numericLiteral(idx), + true, + ), + ), + ); + }), + requiresStaticInit && + t.expressionStatement( + t.callExpression(t.cloneNode(staticInitLocal), [t.thisExpression()]), + ), + ].filter(v => v), + ); + + path.node.body.body.unshift(staticBlock as unknown as ClassElement); + + if (classInitLocal) { + path.node.body.body.push( + t.staticBlock([ + t.expressionStatement( + t.callExpression(t.cloneNode(classInitLocal), []), + ), + ]), + ); + } + + path.insertBefore([ + t.assignmentExpression("=", elementDecsLocal, elementDecorations), + t.assignmentExpression("=", classDecsLocal, classDecorations), + ]); + + // Recrawl the scope to make sure new identifiers are properly synced + path.scope.crawl(); + + return path; +} + +export default function ({ assertVersion }, { decoratorsBeforeExport }) { + assertVersion("^7.16.0"); + + const VISITED = new WeakSet(); + + return { + name: "proposal-decorators", + inherits: syntaxDecorators, + manipulateOptions({ generatorOpts }) { + generatorOpts.decoratorsBeforeExport = decoratorsBeforeExport; + }, + + visitor: { + ClassDeclaration(path: NodePath, state: any) { + if (VISITED.has(path)) return; + + const newPath = transformClass(path, state); + + if (newPath) { + VISITED.add(newPath); + } + }, + + ClassExpression(path: NodePath, state: any) { + if (VISITED.has(path)) return; + + const newPath = transformClass(path, state); + + if (newPath) { + VISITED.add(newPath); + } + }, + }, + }; +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/options.json b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/options.json new file mode 100644 index 000000000000..fcd5c64e5a80 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/options.json @@ -0,0 +1,11 @@ +{ + "plugins": [ + [ + "proposal-decorators", + { "version": "2020-09", "decoratorsBeforeExport": false } + ], + "proposal-class-properties", + "proposal-private-methods", + "proposal-class-static-block" + ] +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/private/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/private/exec.js new file mode 100644 index 000000000000..013772daf69d --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/private/exec.js @@ -0,0 +1,54 @@ +function dec({ get, set }, context) { + context.addInitializer((instance) => { + instance[context.name + 'Context'] = context; + }); + + return { + get() { + return get.call(this) + 1; + }, + + set(v) { + set.call(this, v + 1); + }, + + initializer(v) { + return v ?? 1 + } + } +} + +class Foo { + @dec + accessor #a; + + @dec + accessor #b = 123; +} + +let foo = new Foo(); + +const aContext = foo['#aContext']; +const bContext = foo['#bContext']; + +expect(aContext.access.get.call(foo)).toBe(2); +aContext.access.set.call(foo, 123); +expect(aContext.access.get.call(foo)).toBe(125); +expect(aContext.name).toBe('#a'); +expect(aContext.kind).toBe('accessor'); +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'); + +expect(bContext.access.get.call(foo)).toBe(124); +bContext.access.set.call(foo, 123); +expect(bContext.access.get.call(foo)).toBe(125); +expect(bContext.name).toBe('#b'); +expect(bContext.kind).toBe('accessor'); +expect(bContext.isStatic).toBe(false); +expect(bContext.isPrivate).toBe(true); +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/2020-09-accessors/private/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/private/input.js new file mode 100644 index 000000000000..be4f348923bc --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/private/input.js @@ -0,0 +1,7 @@ +class Foo { + @dec + accessor #a; + + @dec + accessor #b = 123; +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/private/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/private/output.js new file mode 100644 index 000000000000..4925d4257a74 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/private/output.js @@ -0,0 +1,77 @@ +var _init_a, _get_a, _set_a, _init_b, _get_b, _set_b, _initProto, _classDecs, _elementDecs; + +_elementDecs = [[dec, 1, "A", "a"], [dec, 1, "B", "b"]] +_classDecs = [] + +var _A = /*#__PURE__*/new WeakMap(); + +var _a = /*#__PURE__*/new WeakMap(); + +var _B = /*#__PURE__*/new WeakMap(); + +var _b = /*#__PURE__*/new WeakMap(); + +class Foo { + constructor() { + babelHelpers.classPrivateFieldInitSpec(this, _b, { + get: _get_b2, + set: _set_b2 + }); + babelHelpers.classPrivateFieldInitSpec(this, _a, { + get: _get_a2, + set: _set_a2 + }); + babelHelpers.classPrivateFieldInitSpec(this, _A, { + writable: true, + value: (_initProto(this), _init_a(this)) + }); + babelHelpers.classPrivateFieldInitSpec(this, _B, { + writable: true, + value: _init_b(this, 123) + }); + } + + get A() { + return babelHelpers.classPrivateFieldGet(this, _A); + } + + set A(v) { + babelHelpers.classPrivateFieldSet(this, _A, v); + } + + get B() { + return babelHelpers.classPrivateFieldGet(this, _B); + } + + set B(v) { + babelHelpers.classPrivateFieldSet(this, _B, v); + } + +} + +function _set_a2(v) { + _set_a(this, v); +} + +function _get_a2() { + _get_a(this); +} + +function _set_b2(v) { + _set_b(this, v); +} + +function _get_b2() { + _get_b(this); +} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _init_a = ret[0]; + _get_a = ret[1]; + _set_a = ret[2]; + _init_b = ret[3]; + _get_b = ret[4]; + _set_b = ret[5]; + _initProto = ret[6]; +})(); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/public/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/public/exec.js new file mode 100644 index 000000000000..334f7f56a464 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/public/exec.js @@ -0,0 +1,75 @@ +function dec({ get, set }, context) { + context.addInitializer((instance) => { + instance[context.name + 'Context'] = context; + }); + + return { + get() { + return get.call(this) + 1; + }, + + set(v) { + set.call(this, v + 1); + }, + + initializer(v) { + return v ?? 1 + } + } +} + +class Foo { + @dec + accessor a; + + @dec + accessor b = 123; + + @dec + accessor ['c'] = 456; +} + +let foo = new Foo(); + +const aContext = foo['aContext']; +const bContext = foo['bContext']; +const cContext = foo['cContext']; + +expect(foo.a).toBe(2); +foo.a = 123; +expect(foo.a).toBe(125); +expect(aContext.name).toBe('a'); +expect(aContext.kind).toBe('accessor'); +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(foo.hasOwnProperty('a')).toBe(false); +expect(Foo.prototype.hasOwnProperty('a')).toBe(true); + +expect(foo.b).toBe(124); +foo.b = 123; +expect(foo.b).toBe(125); +expect(bContext.name).toBe('b'); +expect(bContext.kind).toBe('accessor'); +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'); +expect(foo.hasOwnProperty('b')).toBe(false); +expect(Foo.prototype.hasOwnProperty('b')).toBe(true); + +expect(foo.c).toBe(457); +foo.c = 456; +expect(foo.c).toBe(458); +expect(cContext.name).toBe('c'); +expect(cContext.kind).toBe('accessor'); +expect(cContext.isStatic).toBe(false); +expect(cContext.isPrivate).toBe(false); +expect(typeof cContext.addInitializer).toBe('function'); +expect(typeof cContext.setMetadata).toBe('function'); +expect(typeof cContext.getMetadata).toBe('function'); +expect(foo.hasOwnProperty('c')).toBe(false); +expect(Foo.prototype.hasOwnProperty('c')).toBe(true); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/public/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/public/input.js new file mode 100644 index 000000000000..5945d3196f1d --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/public/input.js @@ -0,0 +1,10 @@ +class Foo { + @dec + accessor a; + + @dec + accessor b = 123; + + @dec + accessor ['c'] = 456; +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/public/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/public/output.js new file mode 100644 index 000000000000..46e230ef4211 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/public/output.js @@ -0,0 +1,60 @@ +var _init_a, _init_b, _computedKey, _init_computedKey, _initProto, _classDecs, _elementDecs; + +_elementDecs = [[dec, 1, "a"], [dec, 1, "b"], (_computedKey = 'c', null), [dec, 1, _computedKey]] +_classDecs = [] + +var _A = /*#__PURE__*/new WeakMap(); + +var _B = /*#__PURE__*/new WeakMap(); + +var _C = /*#__PURE__*/new WeakMap(); + +class Foo { + constructor() { + babelHelpers.classPrivateFieldInitSpec(this, _A, { + writable: true, + value: (_initProto(this), _init_a(this)) + }); + babelHelpers.classPrivateFieldInitSpec(this, _B, { + writable: true, + value: _init_b(this, 123) + }); + babelHelpers.classPrivateFieldInitSpec(this, _C, { + writable: true, + value: _init_computedKey(this, 456) + }); + } + + get a() { + return babelHelpers.classPrivateFieldGet(this, _A); + } + + set a(v) { + babelHelpers.classPrivateFieldSet(this, _A, v); + } + + get b() { + return babelHelpers.classPrivateFieldGet(this, _B); + } + + set b(v) { + babelHelpers.classPrivateFieldSet(this, _B, v); + } + + get [_computedKey]() { + return babelHelpers.classPrivateFieldGet(this, _C); + } + + set [_computedKey](v) { + babelHelpers.classPrivateFieldSet(this, _C, v); + } + +} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _init_a = ret[0]; + _init_b = ret[1]; + _init_computedKey = ret[2]; + _initProto = ret[3]; +})(); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/static-private/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/static-private/exec.js new file mode 100644 index 000000000000..1da0cc94cd50 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/static-private/exec.js @@ -0,0 +1,52 @@ +function dec({ get, set }, context) { + context.addInitializer((instance) => { + instance[context.name + 'Context'] = context; + }); + + return { + get() { + return get.call(this) + 1; + }, + + set(v) { + set.call(this, v + 1); + }, + + initializer(v) { + return v ?? 1 + } + } +} + +class Foo { + @dec + static accessor #a; + + @dec + static accessor #b = 123; +} + +const aContext = Foo['#aContext']; +const bContext = Foo['#bContext']; + +expect(aContext.access.get.call(Foo)).toBe(2); +aContext.access.set.call(Foo, 123); +expect(aContext.access.get.call(Foo)).toBe(125); +expect(aContext.name).toBe('#a'); +expect(aContext.kind).toBe('accessor'); +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'); + +expect(bContext.access.get.call(Foo)).toBe(124); +bContext.access.set.call(Foo, 123); +expect(bContext.access.get.call(Foo)).toBe(125); +expect(bContext.name).toBe('#b'); +expect(bContext.kind).toBe('accessor'); +expect(bContext.isStatic).toBe(true); +expect(bContext.isPrivate).toBe(true); +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/2020-09-accessors/static-private/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/static-private/input.js new file mode 100644 index 000000000000..ebb88a741572 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/static-private/input.js @@ -0,0 +1,7 @@ +class Foo { + @dec + static accessor #a; + + @dec + static accessor #b = 123; +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/static-private/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/static-private/output.js new file mode 100644 index 000000000000..c07de4957f62 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/static-private/output.js @@ -0,0 +1,76 @@ +var _init_a, _get_a, _set_a, _init_b, _get_b, _set_b, _initStatic, _classDecs, _elementDecs; + +_elementDecs = [[dec, 6, "A", "a"], [dec, 6, "B", "b"]] +_classDecs = [] + +var _a = /*#__PURE__*/new WeakMap(); + +var _b = /*#__PURE__*/new WeakMap(); + +class Foo { + constructor() { + babelHelpers.classPrivateFieldInitSpec(this, _b, { + get: _get_b2, + set: _set_b2 + }); + babelHelpers.classPrivateFieldInitSpec(this, _a, { + get: _get_a2, + set: _set_a2 + }); + } + + static get A() { + return babelHelpers.classStaticPrivateFieldSpecGet(this, Foo, _A); + } + + static set A(v) { + babelHelpers.classStaticPrivateFieldSpecSet(this, Foo, _A, v); + } + + static get B() { + return babelHelpers.classStaticPrivateFieldSpecGet(this, Foo, _B); + } + + static set B(v) { + babelHelpers.classStaticPrivateFieldSpecSet(this, Foo, _B, v); + } + +} + +function _set_a2(v) { + _set_a(this, v); +} + +function _get_a2() { + _get_a(this); +} + +function _set_b2(v) { + _set_b(this, v); +} + +function _get_b2() { + _get_b(this); +} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _init_a = ret[0]; + _get_a = ret[1]; + _set_a = ret[2]; + _init_b = ret[3]; + _get_b = ret[4]; + _set_b = ret[5]; + _initStatic = ret[6]; + + _initStatic(Foo); +})(); + +var _A = { + writable: true, + value: _init_a(Foo) +}; +var _B = { + writable: true, + value: _init_b(Foo, 123) +}; diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/static-public/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/static-public/exec.js new file mode 100644 index 000000000000..813dbaebabc5 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/static-public/exec.js @@ -0,0 +1,70 @@ +function dec({ get, set }, context) { + context.addInitializer((instance) => { + instance[context.name + 'Context'] = context; + }); + + return { + get() { + return get.call(this) + 1; + }, + + set(v) { + set.call(this, v + 1); + }, + + initializer(v) { + return v ?? 1 + } + } +} + +class Foo { + @dec + static accessor a; + + @dec + static accessor b = 123; + + @dec + static accessor ['c'] = 456; +} + +const aContext = Foo['aContext']; +const bContext = Foo['bContext']; +const cContext = Foo['cContext']; + +expect(Foo.a).toBe(2); +Foo.a = 123; +expect(Foo.a).toBe(125); +expect(aContext.name).toBe('a'); +expect(aContext.kind).toBe('accessor'); +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(Foo.hasOwnProperty('a')).toBe(true); + +expect(Foo.b).toBe(124); +Foo.b = 123; +expect(Foo.b).toBe(125); +expect(bContext.name).toBe('b'); +expect(bContext.kind).toBe('accessor'); +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'); +expect(Foo.hasOwnProperty('b')).toBe(true); + +expect(Foo.c).toBe(457); +Foo.c = 456; +expect(Foo.c).toBe(458); +expect(cContext.name).toBe('c'); +expect(cContext.kind).toBe('accessor'); +expect(cContext.isStatic).toBe(true); +expect(cContext.isPrivate).toBe(false); +expect(typeof cContext.addInitializer).toBe('function'); +expect(typeof cContext.setMetadata).toBe('function'); +expect(typeof cContext.getMetadata).toBe('function'); +expect(Foo.hasOwnProperty('c')).toBe(true); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/static-public/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/static-public/input.js new file mode 100644 index 000000000000..edf06478601f --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/static-public/input.js @@ -0,0 +1,10 @@ +class Foo { + @dec + static accessor a; + + @dec + static accessor b = 123; + + @dec + static accessor ['c'] = 456; +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/static-public/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/static-public/output.js new file mode 100644 index 000000000000..5f577df204d6 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/static-public/output.js @@ -0,0 +1,54 @@ +var _init_a, _init_b, _computedKey, _init_computedKey, _initStatic, _classDecs, _elementDecs; + +_elementDecs = [[dec, 6, "a"], [dec, 6, "b"], (_computedKey = 'c', null), [dec, 6, _computedKey]] +_classDecs = [] + +class Foo { + static get a() { + return babelHelpers.classStaticPrivateFieldSpecGet(this, Foo, _A); + } + + static set a(v) { + babelHelpers.classStaticPrivateFieldSpecSet(this, Foo, _A, v); + } + + static get b() { + return babelHelpers.classStaticPrivateFieldSpecGet(this, Foo, _B); + } + + static set b(v) { + babelHelpers.classStaticPrivateFieldSpecSet(this, Foo, _B, v); + } + + static get [_computedKey]() { + return babelHelpers.classStaticPrivateFieldSpecGet(this, Foo, _C); + } + + static set [_computedKey](v) { + babelHelpers.classStaticPrivateFieldSpecSet(this, Foo, _C, v); + } + +} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _init_a = ret[0]; + _init_b = ret[1]; + _init_computedKey = ret[2]; + _initStatic = ret[3]; + + _initStatic(Foo); +})(); + +var _A = { + writable: true, + value: _init_a(Foo) +}; +var _B = { + writable: true, + value: _init_b(Foo, 123) +}; +var _C = { + writable: true, + value: _init_computedKey(Foo, 456) +}; diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-private/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-private/exec.js new file mode 100644 index 000000000000..51715be9f173 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-private/exec.js @@ -0,0 +1,31 @@ +class Foo { + accessor #a; + + accessor #b = 123; + + getA() { + return this.#a; + } + + setA(v) { + this.#a = v; + } + + getB() { + return this.#b; + } + + setB(v) { + this.#b = v; + } +} + +let foo = new Foo(); + +expect(foo.getA()).toBe(undefined); +foo.setA(123) +expect(foo.getA()).toBe(123); + +expect(foo.getB()).toBe(123); +foo.setB(456) +expect(foo.getB()).toBe(456); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-private/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-private/input.js new file mode 100644 index 000000000000..4afc45261337 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-private/input.js @@ -0,0 +1,5 @@ +class Foo { + accessor #a; + + accessor #b = 123; +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-private/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-private/output.js new file mode 100644 index 000000000000..29d3fbd66965 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-private/output.js @@ -0,0 +1,45 @@ +var _A = /*#__PURE__*/new WeakMap(); + +var _a = /*#__PURE__*/new WeakMap(); + +var _B = /*#__PURE__*/new WeakMap(); + +var _b = /*#__PURE__*/new WeakMap(); + +class Foo { + constructor() { + babelHelpers.classPrivateFieldInitSpec(this, _b, { + get: _get_b, + set: _set_b + }); + babelHelpers.classPrivateFieldInitSpec(this, _a, { + get: _get_a, + set: _set_a + }); + babelHelpers.classPrivateFieldInitSpec(this, _A, { + writable: true, + value: void 0 + }); + babelHelpers.classPrivateFieldInitSpec(this, _B, { + writable: true, + value: 123 + }); + } + +} + +function _get_a() { + return babelHelpers.classPrivateFieldGet(this, _A); +} + +function _set_a(v) { + babelHelpers.classPrivateFieldSet(this, _A, v); +} + +function _get_b() { + return babelHelpers.classPrivateFieldGet(this, _B); +} + +function _set_b(v) { + babelHelpers.classPrivateFieldSet(this, _B, v); +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-public/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-public/exec.js new file mode 100644 index 000000000000..5ccd5dcce44d --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-public/exec.js @@ -0,0 +1,27 @@ +class Foo { + accessor a; + + accessor b = 123; + + accessor ['c'] = 456; +} + +let foo = new Foo(); + +expect(foo.a).toBe(undefined); +foo.a = 123; +expect(foo.a).toBe(123); +expect(foo.hasOwnProperty('a')).toBe(false); +expect(Foo.prototype.hasOwnProperty('a')).toBe(true); + +expect(foo.b).toBe(123); +foo.b = 456 +expect(foo.b).toBe(456); +expect(foo.hasOwnProperty('b')).toBe(false); +expect(Foo.prototype.hasOwnProperty('b')).toBe(true); + +expect(foo.c).toBe(456); +foo.c = 789 +expect(foo.c).toBe(789); +expect(foo.hasOwnProperty('c')).toBe(false); +expect(Foo.prototype.hasOwnProperty('c')).toBe(true); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-public/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-public/input.js new file mode 100644 index 000000000000..ad27f18d5ed3 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-public/input.js @@ -0,0 +1,7 @@ +class Foo { + accessor a; + + accessor b = 123; + + accessor ['c'] = 456; +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-public/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-public/output.js new file mode 100644 index 000000000000..6ec6f9255a06 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-public/output.js @@ -0,0 +1,47 @@ +var _A = /*#__PURE__*/new WeakMap(); + +var _B = /*#__PURE__*/new WeakMap(); + +var _C = /*#__PURE__*/new WeakMap(); + +class Foo { + constructor() { + babelHelpers.classPrivateFieldInitSpec(this, _A, { + writable: true, + value: void 0 + }); + babelHelpers.classPrivateFieldInitSpec(this, _B, { + writable: true, + value: 123 + }); + babelHelpers.classPrivateFieldInitSpec(this, _C, { + writable: true, + value: 456 + }); + } + + get a() { + return babelHelpers.classPrivateFieldGet(this, _A); + } + + set a(v) { + babelHelpers.classPrivateFieldSet(this, _A, v); + } + + get b() { + return babelHelpers.classPrivateFieldGet(this, _B); + } + + set b(v) { + babelHelpers.classPrivateFieldSet(this, _B, v); + } + + get 'c'() { + return babelHelpers.classPrivateFieldGet(this, _C); + } + + set 'c'(v) { + babelHelpers.classPrivateFieldSet(this, _C, v); + } + +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-static-private/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-static-private/exec.js new file mode 100644 index 000000000000..21defb3f24d8 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-static-private/exec.js @@ -0,0 +1,29 @@ +class Foo { + static accessor #a; + + static accessor #b = 123; + + static getA() { + return this.#a; + } + + static setA(v) { + this.#a = v; + } + + static getB() { + return this.#b; + } + + static setB(v) { + this.#b = v; + } +} + +expect(Foo.getA()).toBe(undefined); +Foo.setA(123) +expect(Foo.getA()).toBe(123); + +expect(Foo.getB()).toBe(123); +Foo.setB(456) +expect(Foo.getB()).toBe(456); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-static-private/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-static-private/input.js new file mode 100644 index 000000000000..2e516a961de9 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-static-private/input.js @@ -0,0 +1,5 @@ +class Foo { + static accessor #a; + + static accessor #b = 123; +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-static-private/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-static-private/output.js new file mode 100644 index 000000000000..51de21b149b2 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-static-private/output.js @@ -0,0 +1,34 @@ +class Foo {} + +function _get_a() { + return babelHelpers.classStaticPrivateFieldSpecGet(this, Foo, _A); +} + +function _set_a(v) { + babelHelpers.classStaticPrivateFieldSpecSet(this, Foo, _A, v); +} + +function _get_b() { + return babelHelpers.classStaticPrivateFieldSpecGet(this, Foo, _B); +} + +function _set_b(v) { + babelHelpers.classStaticPrivateFieldSpecSet(this, Foo, _B, v); +} + +var _b = { + get: _get_b, + set: _set_b +}; +var _a = { + get: _get_a, + set: _set_a +}; +var _A = { + writable: true, + value: void 0 +}; +var _B = { + writable: true, + value: 123 +}; diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-static-public/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-static-public/exec.js new file mode 100644 index 000000000000..d22a4e710dd8 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-static-public/exec.js @@ -0,0 +1,22 @@ +class Foo { + static accessor a; + + static accessor b = 123; + + static accessor ['c'] = 456; +} + +expect(Foo.a).toBe(undefined); +Foo.a = 123; +expect(Foo.a).toBe(123); +expect(Foo.hasOwnProperty('a')).toBe(true); + +expect(Foo.b).toBe(123); +Foo.b = 456 +expect(Foo.b).toBe(456); +expect(Foo.hasOwnProperty('b')).toBe(true); + +expect(Foo.c).toBe(456); +Foo.c = 789 +expect(Foo.c).toBe(789); +expect(Foo.hasOwnProperty('c')).toBe(true); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-static-public/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-static-public/input.js new file mode 100644 index 000000000000..160e72cd531c --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-static-public/input.js @@ -0,0 +1,7 @@ +class Foo { + static accessor a; + + static accessor b = 123; + + static accessor ['c'] = 456; +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-static-public/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-static-public/output.js new file mode 100644 index 000000000000..4fe37de40ebf --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-accessors/undecorated-static-public/output.js @@ -0,0 +1,39 @@ +class Foo { + static get a() { + return babelHelpers.classStaticPrivateFieldSpecGet(this, Foo, _A); + } + + static set a(v) { + babelHelpers.classStaticPrivateFieldSpecSet(this, Foo, _A, v); + } + + static get b() { + return babelHelpers.classStaticPrivateFieldSpecGet(this, Foo, _B); + } + + static set b(v) { + babelHelpers.classStaticPrivateFieldSpecSet(this, Foo, _B, v); + } + + static get 'c'() { + return babelHelpers.classStaticPrivateFieldSpecGet(this, Foo, _C); + } + + static set 'c'(v) { + babelHelpers.classStaticPrivateFieldSpecSet(this, Foo, _C, v); + } + +} + +var _A = { + writable: true, + value: void 0 +}; +var _B = { + writable: true, + value: 123 +}; +var _C = { + writable: true, + value: 456 +}; diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/expressions/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/expressions/input.js new file mode 100644 index 000000000000..542bf870c079 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/expressions/input.js @@ -0,0 +1,11 @@ +const A = @dec class A {} +const B = @dec class C {} +const D = @dec class {} +const E = (@dec class {}, 123); +const F = [@dec class G {}, @dec class {}]; +const H = @dec class extends I {}; +const J = @dec class K extends L {}; + +function classFactory() { + return @dec class {} +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/expressions/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/expressions/output.js new file mode 100644 index 000000000000..41e8b5d77050 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/expressions/output.js @@ -0,0 +1,69 @@ +var _initClass, _A, _classDecs, _elementDecs, _class, _temp, _initClass2, _C, _classDecs2, _elementDecs2, _class2, _temp2, _initClass3, _D, _classDecs3, _elementDecs3, _class3, _temp3, _initClass4, _decorated_class, _classDecs4, _elementDecs4, _class4, _temp4, _initClass5, _G, _classDecs5, _elementDecs5, _class5, _temp5, _initClass6, _decorated_class2, _classDecs6, _elementDecs6, _class6, _temp6, _initClass7, _H, _classDecs7, _elementDecs7, _class7, _temp7, _initClass8, _K, _classDecs8, _elementDecs8, _class8, _temp8; + +const A = ((_elementDecs = [], _classDecs = [dec], (_temp = _class = class A {}, (() => { + let ret = babelHelpers.applyDecs(_class, _elementDecs, _classDecs); + _A = ret[0]; + _initClass = ret[1]; +})(), (() => { + _initClass(); +})(), _temp)), _A); +const B = ((_elementDecs2 = [], _classDecs2 = [dec], (_temp2 = _class2 = class C {}, (() => { + let ret = babelHelpers.applyDecs(_class2, _elementDecs2, _classDecs2); + _C = ret[0]; + _initClass2 = ret[1]; +})(), (() => { + _initClass2(); +})(), _temp2)), _C); +const D = ((_elementDecs3 = [], _classDecs3 = [dec], (_temp3 = _class3 = class D {}, (() => { + let ret = babelHelpers.applyDecs(_class3, _elementDecs3, _classDecs3); + _D = ret[0]; + _initClass3 = ret[1]; +})(), (() => { + _initClass3(); +})(), _temp3)), _D); +const E = (((_elementDecs4 = [], _classDecs4 = [dec], (_temp4 = _class4 = class {}, (() => { + let ret = babelHelpers.applyDecs(_class4, _elementDecs4, _classDecs4); + _decorated_class = ret[0]; + _initClass4 = ret[1]; +})(), (() => { + _initClass4(); +})(), _temp4)), _decorated_class), 123); +const F = [((_elementDecs5 = [], _classDecs5 = [dec], (_temp5 = _class5 = class G {}, (() => { + let ret = babelHelpers.applyDecs(_class5, _elementDecs5, _classDecs5); + _G = ret[0]; + _initClass5 = ret[1]; +})(), (() => { + _initClass5(); +})(), _temp5)), _G), ((_elementDecs6 = [], _classDecs6 = [dec], (_temp6 = _class6 = class {}, (() => { + let ret = babelHelpers.applyDecs(_class6, _elementDecs6, _classDecs6); + _decorated_class2 = ret[0]; + _initClass6 = ret[1]; +})(), (() => { + _initClass6(); +})(), _temp6)), _decorated_class2)]; +const H = ((_elementDecs7 = [], _classDecs7 = [dec], (_temp7 = _class7 = class H extends I {}, (() => { + let ret = babelHelpers.applyDecs(_class7, _elementDecs7, _classDecs7); + _H = ret[0]; + _initClass7 = ret[1]; +})(), (() => { + _initClass7(); +})(), _temp7)), _H); +const J = ((_elementDecs8 = [], _classDecs8 = [dec], (_temp8 = _class8 = class K extends L {}, (() => { + let ret = babelHelpers.applyDecs(_class8, _elementDecs8, _classDecs8); + _K = ret[0]; + _initClass8 = ret[1]; +})(), (() => { + _initClass8(); +})(), _temp8)), _K); + +function classFactory() { + var _initClass9, _decorated_class3, _classDecs9, _elementDecs9, _class9, _temp9; + + return (_elementDecs9 = [], _classDecs9 = [dec], (_temp9 = _class9 = class {}, (() => { + let ret = babelHelpers.applyDecs(_class9, _elementDecs9, _classDecs9); + _decorated_class3 = ret[0]; + _initClass9 = ret[1]; + })(), (() => { + _initClass9(); + })(), _temp9)), _decorated_class3; +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/inheritance/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/inheritance/exec.js new file mode 100644 index 000000000000..889e8c102cf4 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/inheritance/exec.js @@ -0,0 +1,18 @@ +let count = 0; + +function dec1(Klass) { + expect(++count).toBe(1); + expect(Klass.name).toBe('Bar'); +} + +@dec1 +class Bar {} + +function dec2(Klass) { + expect(++count).toBe(2); + expect(Klass.name).toBe('Foo'); + expect(Object.getPrototypeOf(Klass)).toBe(Bar); +} + +@dec2 +class Foo extends Bar {} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/inheritance/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/inheritance/input.js new file mode 100644 index 000000000000..7a4a6684555a --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/inheritance/input.js @@ -0,0 +1,5 @@ +@dec1 +class Bar {} + +@dec2 +class Foo extends Bar {} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/inheritance/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/inheritance/output.js new file mode 100644 index 000000000000..bc7e88895a12 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/inheritance/output.js @@ -0,0 +1,35 @@ +var _initClass, _classDecs, _elementDecs, _initClass2, _classDecs2, _elementDecs2; + +let _Bar; + +_elementDecs = [] +_classDecs = [dec1] + +class Bar {} + +(() => { + let ret = babelHelpers.applyDecs(Bar, _elementDecs, _classDecs); + _Bar = ret[0]; + _initClass = ret[1]; +})(); + +(() => { + _initClass(); +})(); + +let _Foo; + +_elementDecs2 = [] +_classDecs2 = [dec2] + +class Foo extends _Bar {} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs2, _classDecs2); + _Foo = ret[0]; + _initClass2 = ret[1]; +})(); + +(() => { + _initClass2(); +})(); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/initializers/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/initializers/exec.js new file mode 100644 index 000000000000..aefc3ba5b88c --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/initializers/exec.js @@ -0,0 +1,41 @@ +function dec1(Foo, { addInitializer }) { + expect(Foo.field).toBe(undefined); + addInitializer(() => { + Foo.initField = 123; + }); +} + +@dec1 +class Foo { + static { + expect(this.initField).toBe(undefined); + } + + static field = 123; +} + +expect(Foo.initField).toBe(123); +expect(Foo.field).toBe(123); + +function dec2(Bar, { addInitializer }) { + expect(Bar.field).toBe(123); + expect(Bar.otherField).toBe(undefined); + expect(Bar.initField).toBe(123); + addInitializer(() => { + Bar.initField = 456; + }); +} + +@dec2 +class Bar extends Foo { + static { + expect(this.initField).toBe(123); + this.otherField = 456; + } + + static field = 456; +} + +expect(Bar.initField).toBe(456); +expect(Bar.field).toBe(456); +expect(Bar.otherField).toBe(456); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/initializers/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/initializers/input.js new file mode 100644 index 000000000000..deeb483448f7 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/initializers/input.js @@ -0,0 +1,13 @@ +@dec +class Foo { + static field = 123; +} + +@dec +class Bar extends Foo { + static { + this.otherField = 456; + } + + static field = 123; +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/initializers/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/initializers/output.js new file mode 100644 index 000000000000..f97e4c9371c2 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/initializers/output.js @@ -0,0 +1,43 @@ +var _initClass, _classDecs, _elementDecs, _initClass2, _classDecs2, _elementDecs2; + +let _Foo; + +_elementDecs = [] +_classDecs = [dec] + +class Foo {} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _Foo = ret[0]; + _initClass = ret[1]; +})(); + +babelHelpers.defineProperty(Foo, "field", 123); + +(() => { + _initClass(); +})(); + +let _Bar; + +_elementDecs2 = [] +_classDecs2 = [dec] + +class Bar extends _Foo {} + +(() => { + let ret = babelHelpers.applyDecs(Bar, _elementDecs2, _classDecs2); + _Bar = ret[0]; + _initClass2 = ret[1]; +})(); + +(() => { + Bar.otherField = 456; +})(); + +babelHelpers.defineProperty(Bar, "field", 123); + +(() => { + _initClass2(); +})(); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/options.json b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/options.json new file mode 100644 index 000000000000..fcd5c64e5a80 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/options.json @@ -0,0 +1,11 @@ +{ + "plugins": [ + [ + "proposal-decorators", + { "version": "2020-09", "decoratorsBeforeExport": false } + ], + "proposal-class-properties", + "proposal-private-methods", + "proposal-class-static-block" + ] +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/replacement-with-expr/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/replacement-with-expr/exec.js new file mode 100644 index 000000000000..739e33a05be5 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/replacement-with-expr/exec.js @@ -0,0 +1,17 @@ +let replaced; + +function dec(Klass) { + replaced = class extends Klass {}; + + return replaced; +} + +const Foo = @dec class Bar { + static bar = new Bar(); +}; + +const foo = new Foo(); + +expect(Foo).toBe(replaced); +expect(Foo.bar).toBeInstanceOf(replaced); +expect(foo).toBeInstanceOf(replaced); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/replacement-with-expr/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/replacement-with-expr/input.js new file mode 100644 index 000000000000..fde7feb1abd7 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/replacement-with-expr/input.js @@ -0,0 +1,6 @@ +const Foo = @dec class Bar { + bar = new Bar(); +}; + +const foo = new Foo(); + diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/replacement-with-expr/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/replacement-with-expr/output.js new file mode 100644 index 000000000000..00213d180ddb --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/replacement-with-expr/output.js @@ -0,0 +1,15 @@ +var _initClass, _Bar, _classDecs, _elementDecs, _class, _temp; + +const Foo = ((_elementDecs = [], _classDecs = [dec], (_temp = _class = class Bar { + constructor() { + babelHelpers.defineProperty(this, "bar", new _Bar()); + } + +}, (() => { + let ret = babelHelpers.applyDecs(_class, _elementDecs, _classDecs); + _Bar = ret[0]; + _initClass = ret[1]; +})(), (() => { + _initClass(); +})(), _temp)), _Bar); +const foo = new Foo(); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/replacement/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/replacement/exec.js new file mode 100644 index 000000000000..c8dc49c0d85e --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/replacement/exec.js @@ -0,0 +1,19 @@ + +let replaced; + +function dec(Klass) { + replaced = class extends Klass {}; + + return replaced; +} + +@dec +class Foo { + static foo = new Foo(); +} + +const foo = new Foo(); + +expect(Foo).toBe(replaced); +expect(Foo.foo).toBeInstanceOf(replaced); +expect(foo).toBeInstanceOf(replaced); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/replacement/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/replacement/input.js new file mode 100644 index 000000000000..e6da5000db62 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/replacement/input.js @@ -0,0 +1,6 @@ +@dec +class Foo { + static foo = new Foo(); +} + +const foo = new Foo(); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/replacement/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/replacement/output.js new file mode 100644 index 000000000000..5eb69f4ddabb --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-classes/replacement/output.js @@ -0,0 +1,22 @@ +var _initClass, _classDecs, _elementDecs; + +let _Foo; + +_elementDecs = [] +_classDecs = [dec] + +class Foo {} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _Foo = ret[0]; + _initClass = ret[1]; +})(); + +babelHelpers.defineProperty(Foo, "foo", new _Foo()); + +(() => { + _initClass(); +})(); + +const foo = new _Foo(); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/computed-keys-same-ast/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/computed-keys-same-ast/exec.js new file mode 100644 index 000000000000..49fad189f4e2 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/computed-keys-same-ast/exec.js @@ -0,0 +1,33 @@ +var i = 0; + +function getKey() { + return (i++).toString(); +} + +let elements = []; + +function dec(fn, context) { + elements.push({ fn, context }); +} + +class Foo { + @dec + [getKey()]() { + return 1; + } + + @dec + [getKey()]() { + return 2; + } +} + +expect(elements).toHaveLength(2); + +expect(elements[0].context.name).toBe("0"); +expect(elements[0].fn()).toBe(1); + +expect(elements[1].context.name).toBe("1"); +expect(elements[1].fn()).toBe(2); + +expect(i).toBe(2); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/computed-keys-same-ast/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/computed-keys-same-ast/input.js new file mode 100644 index 000000000000..2ca438188867 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/computed-keys-same-ast/input.js @@ -0,0 +1,11 @@ +class Foo { + @dec + [getKey()]() { + return 1; + } + + @dec + [getKey()]() { + return 2; + } +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/computed-keys-same-ast/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/computed-keys-same-ast/output.js new file mode 100644 index 000000000000..debb57b52541 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/computed-keys-same-ast/output.js @@ -0,0 +1,24 @@ +var _computedKey, _computedKey2, _initProto, _classDecs, _elementDecs; + +_elementDecs = [(_computedKey = getKey(), null), [dec, 2, _computedKey], (_computedKey2 = getKey(), null), [dec, 2, _computedKey2]] +_classDecs = [] + +class Foo { + constructor(...args) { + _initProto(this); + } + + [_computedKey]() { + return 1; + } + + [_computedKey2]() { + return 2; + } + +} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _initProto = ret[0]; +})(); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/computed-keys-same-value/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/computed-keys-same-value/exec.js new file mode 100644 index 000000000000..c97e2293ac96 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/computed-keys-same-value/exec.js @@ -0,0 +1,29 @@ +expect(() => { + var i = 0; + var j = 0; + + function getKeyI() { + return (i++).toString(); + } + function getKeyJ() { + return (j++).toString(); + } + + let elements = []; + + function dec(fn, context) { + elements.push({ fn, context }); + } + + class Foo { + @dec + [getKeyI()]() { + return 1; + } + + @dec + [getKeyJ()]() { + return 2; + } + } +}).toThrow("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: 0") diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/computed-keys-same-value/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/computed-keys-same-value/input.js new file mode 100644 index 000000000000..8aeb57759b76 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/computed-keys-same-value/input.js @@ -0,0 +1,11 @@ +class Foo { + @dec + [getKeyI()]() { + return 1; + } + + @dec + [getKeyJ()]() { + return 2; + } +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/computed-keys-same-value/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/computed-keys-same-value/output.js new file mode 100644 index 000000000000..df2f1ff978f0 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/computed-keys-same-value/output.js @@ -0,0 +1,24 @@ +var _computedKey, _computedKey2, _initProto, _classDecs, _elementDecs; + +_elementDecs = [(_computedKey = getKeyI(), null), [dec, 2, _computedKey], (_computedKey2 = getKeyJ(), null), [dec, 2, _computedKey2]] +_classDecs = [] + +class Foo { + constructor(...args) { + _initProto(this); + } + + [_computedKey]() { + return 1; + } + + [_computedKey2]() { + return 2; + } + +} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _initProto = ret[0]; +})(); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/method-and-field/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/method-and-field/exec.js new file mode 100644 index 000000000000..8ac906f1823a --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/method-and-field/exec.js @@ -0,0 +1,23 @@ +let elements = []; + +function dec(val, context) { + elements.push({ val, context }); +} + +class Foo { + @dec + a = 123; + + @dec + a() { + return 1; + } +} + +expect(elements).toHaveLength(2); + +expect(elements[0].context.name).toBe("a"); +expect(elements[0].val).toBe(undefined); + +expect(elements[1].context.name).toBe("a"); +expect(elements[1].val()).toBe(1); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/method-and-field/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/method-and-field/input.js new file mode 100644 index 000000000000..522da0061204 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/method-and-field/input.js @@ -0,0 +1,9 @@ +class Foo { + @dec + a = 123; + + @dec + a() { + return 1; + } +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/method-and-field/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/method-and-field/output.js new file mode 100644 index 000000000000..f434ffecf8dc --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/method-and-field/output.js @@ -0,0 +1,21 @@ +var _init_a, _initProto, _classDecs, _elementDecs; + +_elementDecs = [[dec, 0, "a"], [dec, 2, "a"]] +_classDecs = [] + +class Foo { + constructor() { + babelHelpers.defineProperty(this, "a", (_initProto(this), _init_a(this, 123))); + } + + a() { + return 1; + } + +} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _init_a = ret[0]; + _initProto = ret[1]; +})(); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/methods-with-same-key/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/methods-with-same-key/exec.js new file mode 100644 index 000000000000..9e270b2a7488 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/methods-with-same-key/exec.js @@ -0,0 +1,19 @@ +expect(() => { + let elements = []; + + function dec(val, context) { + elements.push({ val, context }); + } + + class Foo { + @dec + a() { + return 1; + } + + @dec + a() { + return 2; + } + } +}).toThrow("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: a") diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/methods-with-same-key/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/methods-with-same-key/input.js new file mode 100644 index 000000000000..db90a40aa67b --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/methods-with-same-key/input.js @@ -0,0 +1,11 @@ +class Foo { + @dec + a() { + return 1; + } + + @dec + a() { + return 2; + } +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/methods-with-same-key/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/methods-with-same-key/output.js new file mode 100644 index 000000000000..f8bfe38a107c --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/methods-with-same-key/output.js @@ -0,0 +1,24 @@ +var _initProto, _classDecs, _elementDecs; + +_elementDecs = [[dec, 2, "a"], [dec, 2, "a"]] +_classDecs = [] + +class Foo { + constructor(...args) { + _initProto(this); + } + + a() { + return 1; + } + + a() { + return 2; + } + +} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _initProto = ret[0]; +})(); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/options.json b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/options.json new file mode 100644 index 000000000000..fcd5c64e5a80 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-duplicated-keys/options.json @@ -0,0 +1,11 @@ +{ + "plugins": [ + [ + "proposal-decorators", + { "version": "2020-09", "decoratorsBeforeExport": false } + ], + "proposal-class-properties", + "proposal-private-methods", + "proposal-class-static-block" + ] +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/options.json b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/options.json new file mode 100644 index 000000000000..fcd5c64e5a80 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/options.json @@ -0,0 +1,11 @@ +{ + "plugins": [ + [ + "proposal-decorators", + { "version": "2020-09", "decoratorsBeforeExport": false } + ], + "proposal-class-properties", + "proposal-private-methods", + "proposal-class-static-block" + ] +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/private/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/private/exec.js new file mode 100644 index 000000000000..b063a8585c97 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/private/exec.js @@ -0,0 +1,41 @@ +function dec(_v, context) { + return function (v) { + this[context.name + 'Context'] = context; + return (v ?? 1) + 1; + } +} + +class Foo { + @dec + #a; + + @dec + #b = 123; +} + +let foo = new Foo(); + +const aContext = foo['#aContext']; +const bContext = foo['#bContext']; + +expect(aContext.access.get.call(foo)).toBe(2); +aContext.access.set.call(foo, 123); +expect(aContext.access.get.call(foo)).toBe(123); +expect(aContext.name).toBe('#a'); +expect(aContext.kind).toBe('field'); +expect(aContext.isStatic).toBe(false); +expect(aContext.isPrivate).toBe(true); +expect(typeof aContext.addInitializer).toBe('undefined'); +expect(typeof aContext.setMetadata).toBe('function'); +expect(typeof aContext.getMetadata).toBe('function'); + +expect(bContext.access.get.call(foo)).toBe(124); +bContext.access.set.call(foo, 123); +expect(bContext.access.get.call(foo)).toBe(123); +expect(bContext.name).toBe('#b'); +expect(bContext.kind).toBe('field'); +expect(bContext.isStatic).toBe(false); +expect(bContext.isPrivate).toBe(true); +expect(typeof aContext.addInitializer).toBe('undefined'); +expect(typeof aContext.setMetadata).toBe('function'); +expect(typeof aContext.getMetadata).toBe('function'); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/private/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/private/input.js new file mode 100644 index 000000000000..222ec09fc95e --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/private/input.js @@ -0,0 +1,7 @@ +class Foo { + @dec + #a; + + @dec + #b = 123; +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/private/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/private/output.js new file mode 100644 index 000000000000..0a54671e7eac --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/private/output.js @@ -0,0 +1,44 @@ +var _init_a, _init_b, _classDecs, _elementDecs; + +_elementDecs = [[dec, 0, "A", "a"], [dec, 0, "B", "b"]] +_classDecs = [] + +var _a = /*#__PURE__*/new WeakMap(); + +var _b = /*#__PURE__*/new WeakMap(); + +class Foo { + constructor() { + babelHelpers.classPrivateFieldInitSpec(this, _a, { + writable: true, + value: _init_a(this) + }); + babelHelpers.classPrivateFieldInitSpec(this, _b, { + writable: true, + value: _init_b(this, 123) + }); + } + + get A() { + return babelHelpers.classPrivateFieldGet(this, _a); + } + + set A(v) { + babelHelpers.classPrivateFieldSet(this, _a, v); + } + + get B() { + return babelHelpers.classPrivateFieldGet(this, _b); + } + + set B(v) { + babelHelpers.classPrivateFieldSet(this, _b, v); + } + +} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _init_a = ret[0]; + _init_b = ret[1]; +})(); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/public/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/public/exec.js new file mode 100644 index 000000000000..4746bc28b9cc --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/public/exec.js @@ -0,0 +1,62 @@ +function dec(_v, context) { + return function (v) { + this[context.name + 'Context'] = context; + return (v ?? 1) + 1; + } +} + +class Foo { + @dec + a; + + @dec + b = 123; + + @dec + ['c'] = 456; +} + +let foo = new Foo(); + +const aContext = foo['aContext']; +const bContext = foo['bContext']; +const cContext = foo['cContext']; + +expect(foo.a).toBe(2); +foo.a = 123; +expect(foo.a).toBe(123); +expect(aContext.name).toBe('a'); +expect(aContext.kind).toBe('field'); +expect(aContext.isStatic).toBe(false); +expect(aContext.isPrivate).toBe(false); +expect(typeof aContext.addInitializer).toBe('undefined'); +expect(typeof aContext.setMetadata).toBe('function'); +expect(typeof aContext.getMetadata).toBe('function'); +expect(foo.hasOwnProperty('a')).toBe(true); +expect(Foo.prototype.hasOwnProperty('a')).toBe(false); + +expect(foo.b).toBe(124); +foo.b = 123; +expect(foo.b).toBe(123); +expect(bContext.name).toBe('b'); +expect(bContext.kind).toBe('field'); +expect(bContext.isStatic).toBe(false); +expect(bContext.isPrivate).toBe(false); +expect(typeof bContext.addInitializer).toBe('undefined'); +expect(typeof bContext.setMetadata).toBe('function'); +expect(typeof bContext.getMetadata).toBe('function'); +expect(foo.hasOwnProperty('b')).toBe(true); +expect(Foo.prototype.hasOwnProperty('b')).toBe(false); + +expect(foo.c).toBe(457); +foo.c = 456; +expect(foo.c).toBe(456); +expect(cContext.name).toBe('c'); +expect(cContext.kind).toBe('field'); +expect(cContext.isStatic).toBe(false); +expect(cContext.isPrivate).toBe(false); +expect(typeof cContext.addInitializer).toBe('undefined'); +expect(typeof cContext.setMetadata).toBe('function'); +expect(typeof cContext.getMetadata).toBe('function'); +expect(foo.hasOwnProperty('c')).toBe(true); +expect(Foo.prototype.hasOwnProperty('c')).toBe(false); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/public/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/public/input.js new file mode 100644 index 000000000000..8dff95c99a3a --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/public/input.js @@ -0,0 +1,10 @@ +class Foo { + @dec + a; + + @dec + b = 123; + + @dec + ['c'] = 456; +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/public/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/public/output.js new file mode 100644 index 000000000000..f272081c8205 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/public/output.js @@ -0,0 +1,20 @@ +var _init_a, _init_b, _computedKey, _init_computedKey, _classDecs, _elementDecs; + +_elementDecs = [[dec, 0, "a"], [dec, 0, "b"], (_computedKey = 'c', null), [dec, 0, _computedKey]] +_classDecs = [] + +class Foo { + constructor() { + babelHelpers.defineProperty(this, "a", _init_a(this)); + babelHelpers.defineProperty(this, "b", _init_b(this, 123)); + babelHelpers.defineProperty(this, _computedKey, _init_computedKey(this, 456)); + } + +} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _init_a = ret[0]; + _init_b = ret[1]; + _init_computedKey = ret[2]; +})(); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/static-private/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/static-private/exec.js new file mode 100644 index 000000000000..d411fc840cfc --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/static-private/exec.js @@ -0,0 +1,39 @@ +function dec(_v, context) { + return function (v) { + this[context.name + 'Context'] = context; + return (v ?? 1) + 1; + } +} + +class Foo { + @dec + static #a; + + @dec + static #b = 123; +} + +const aContext = Foo['#aContext']; +const bContext = Foo['#bContext']; + +expect(aContext.access.get.call(Foo)).toBe(2); +aContext.access.set.call(Foo, 123); +expect(aContext.access.get.call(Foo)).toBe(123); +expect(aContext.name).toBe('#a'); +expect(aContext.kind).toBe('field'); +expect(aContext.isStatic).toBe(true); +expect(aContext.isPrivate).toBe(true); +expect(typeof aContext.addInitializer).toBe('undefined'); +expect(typeof aContext.setMetadata).toBe('function'); +expect(typeof aContext.getMetadata).toBe('function'); + +expect(bContext.access.get.call(Foo)).toBe(124); +bContext.access.set.call(Foo, 123); +expect(bContext.access.get.call(Foo)).toBe(123); +expect(bContext.name).toBe('#b'); +expect(bContext.kind).toBe('field'); +expect(bContext.isStatic).toBe(true); +expect(bContext.isPrivate).toBe(true); +expect(typeof aContext.addInitializer).toBe('undefined'); +expect(typeof aContext.setMetadata).toBe('function'); +expect(typeof aContext.getMetadata).toBe('function'); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/static-private/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/static-private/input.js new file mode 100644 index 000000000000..830d82321863 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/static-private/input.js @@ -0,0 +1,7 @@ +class Foo { + @dec + static #a; + + @dec + static #b = 123; +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/static-private/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/static-private/output.js new file mode 100644 index 000000000000..7329473352cc --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/static-private/output.js @@ -0,0 +1,38 @@ +var _init_a, _init_b, _classDecs, _elementDecs; + +_elementDecs = [[dec, 5, "A", "a"], [dec, 5, "B", "b"]] +_classDecs = [] + +class Foo { + static get A() { + return babelHelpers.classStaticPrivateFieldSpecGet(this, Foo, _a); + } + + static set A(v) { + babelHelpers.classStaticPrivateFieldSpecSet(this, Foo, _a, v); + } + + static get B() { + return babelHelpers.classStaticPrivateFieldSpecGet(this, Foo, _b); + } + + static set B(v) { + babelHelpers.classStaticPrivateFieldSpecSet(this, Foo, _b, v); + } + +} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _init_a = ret[0]; + _init_b = ret[1]; +})(); + +var _a = { + writable: true, + value: _init_a(Foo) +}; +var _b = { + writable: true, + value: _init_b(Foo, 123) +}; diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/static-public/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/static-public/exec.js new file mode 100644 index 000000000000..f48a24d403cf --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/static-public/exec.js @@ -0,0 +1,57 @@ +function dec(_v, context) { + return function (v) { + this[context.name + 'Context'] = context; + return (v ?? 1) + 1; + } +} + +class Foo { + @dec + static a; + + @dec + static b = 123; + + @dec + static ['c'] = 456; +} + +const aContext = Foo['aContext']; +const bContext = Foo['bContext']; +const cContext = Foo['cContext']; + +expect(Foo.a).toBe(2); +Foo.a = 123; +expect(Foo.a).toBe(123); +expect(aContext.name).toBe('a'); +expect(aContext.kind).toBe('field'); +expect(aContext.isStatic).toBe(true); +expect(aContext.isPrivate).toBe(false); +expect(typeof aContext.addInitializer).toBe('undefined'); +expect(typeof aContext.setMetadata).toBe('function'); +expect(typeof aContext.getMetadata).toBe('function'); +expect(Foo.hasOwnProperty('a')).toBe(true); + +expect(Foo.b).toBe(124); +Foo.b = 123; +expect(Foo.b).toBe(123); +expect(bContext.name).toBe('b'); +expect(bContext.kind).toBe('field'); +expect(bContext.isStatic).toBe(true); +expect(bContext.isPrivate).toBe(false); +expect(typeof bContext.addInitializer).toBe('undefined'); +expect(typeof bContext.setMetadata).toBe('function'); +expect(typeof bContext.getMetadata).toBe('function'); +expect(Foo.hasOwnProperty('b')).toBe(true); + +expect(Foo.c).toBe(457); +Foo.c = 456; +expect(Foo.c).toBe(456); +expect(cContext.name).toBe('c'); +expect(cContext.kind).toBe('field'); +expect(cContext.isStatic).toBe(true); +expect(cContext.isPrivate).toBe(false); +expect(typeof cContext.addInitializer).toBe('undefined'); +expect(typeof cContext.setMetadata).toBe('function'); +expect(typeof cContext.getMetadata).toBe('function'); +expect(Foo.hasOwnProperty('c')).toBe(true); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/static-public/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/static-public/input.js new file mode 100644 index 000000000000..9b9dae98314a --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/static-public/input.js @@ -0,0 +1,10 @@ +class Foo { + @dec + static a; + + @dec + static b = 123; + + @dec + static ['c'] = 456; +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/static-public/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/static-public/output.js new file mode 100644 index 000000000000..39732bc638cd --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-fields/static-public/output.js @@ -0,0 +1,17 @@ +var _init_a, _init_b, _computedKey, _init_computedKey, _classDecs, _elementDecs; + +_elementDecs = [[dec, 5, "a"], [dec, 5, "b"], (_computedKey = 'c', null), [dec, 5, _computedKey]] +_classDecs = [] + +class Foo {} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _init_a = ret[0]; + _init_b = ret[1]; + _init_computedKey = ret[2]; +})(); + +babelHelpers.defineProperty(Foo, "a", _init_a(Foo)); +babelHelpers.defineProperty(Foo, "b", _init_b(Foo, 123)); +babelHelpers.defineProperty(Foo, _computedKey, _init_computedKey(Foo, 456)); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/options.json b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/options.json new file mode 100644 index 000000000000..fcd5c64e5a80 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/options.json @@ -0,0 +1,11 @@ +{ + "plugins": [ + [ + "proposal-decorators", + { "version": "2020-09", "decoratorsBeforeExport": false } + ], + "proposal-class-properties", + "proposal-private-methods", + "proposal-class-static-block" + ] +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/private/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/private/exec.js new file mode 100644 index 000000000000..a4f9a31d2589 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/private/exec.js @@ -0,0 +1,67 @@ +function dec(value, context) { + context.addInitializer((instance) => { + instance[context.name + '_' + context.kind + 'Context'] = context; + }); + + if (context.kind === 'getter') { + return function () { + return value.call(this) + 1; + } + } else { + return function (v) { + return value.call(this, v + 1); + } + } +} + +class Foo { + value = 1; + + @dec + get #a() { + return this.value; + } + + @dec + set #a(v) { + this.value = v; + } + + getA() { + return this.#a; + } + + setA(v) { + this.#a = v; + } +} + +let foo = new Foo(); + +const a_getterContext = foo['#a_getterContext']; +const a_setterContext = foo['#a_setterContext']; + +expect(a_getterContext.access.get.call(foo)).toBe(2); +expect(foo.getA()).toBe(2); +a_setterContext.access.set.call(foo, 123); +expect(a_getterContext.access.get.call(foo)).toBe(125); +expect(foo.getA()).toBe(125); +foo.setA(456); +expect(a_getterContext.access.get.call(foo)).toBe(458); +expect(foo.getA()).toBe(458); + +expect(a_getterContext.name).toBe('#a'); +expect(a_getterContext.kind).toBe('getter'); +expect(a_getterContext.isStatic).toBe(false); +expect(a_getterContext.isPrivate).toBe(true); +expect(typeof a_getterContext.addInitializer).toBe('function'); +expect(typeof a_getterContext.setMetadata).toBe('function'); +expect(typeof a_getterContext.getMetadata).toBe('function'); + +expect(a_setterContext.name).toBe('#a'); +expect(a_setterContext.kind).toBe('setter'); +expect(a_setterContext.isStatic).toBe(false); +expect(a_setterContext.isPrivate).toBe(true); +expect(typeof a_setterContext.addInitializer).toBe('function'); +expect(typeof a_setterContext.setMetadata).toBe('function'); +expect(typeof a_setterContext.getMetadata).toBe('function'); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/private/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/private/input.js new file mode 100644 index 000000000000..060ae225a21e --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/private/input.js @@ -0,0 +1,21 @@ +class Foo { + value = 1; + + @dec + get #a() { + return this.value; + } + + @dec + set #a(v) { + this.value = v; + } + + getA() { + return this.#a; + } + + setA(v) { + this.#a = v; + } +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/private/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/private/output.js new file mode 100644 index 000000000000..df03c7cdb101 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/private/output.js @@ -0,0 +1,50 @@ +var _call_a, _call_a2, _initProto, _classDecs, _elementDecs; + +_elementDecs = [[dec, 3, "A", "a"], [dec, 4, "B", "a"]] +_classDecs = [] + +var _a = /*#__PURE__*/new WeakMap(); + +class Foo { + constructor(...args) { + babelHelpers.classPrivateFieldInitSpec(this, _a, { + get: _get_a, + set: _set_a + }); + babelHelpers.defineProperty(this, "value", 1); + + _initProto(this); + } + + A() { + return this.value; + } + + B(v) { + this.value = v; + } + + getA() { + return babelHelpers.classPrivateFieldGet(this, _a); + } + + setA(v) { + babelHelpers.classPrivateFieldSet(this, _a, v); + } + +} + +function _get_a() { + return _call_a(this); +} + +function _set_a(v) { + _call_a2(this, v); +} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _call_a = ret[0]; + _call_a2 = ret[1]; + _initProto = ret[2]; +})(); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/public/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/public/exec.js new file mode 100644 index 000000000000..0420e538875b --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/public/exec.js @@ -0,0 +1,88 @@ +function dec(value, context) { + context.addInitializer((instance) => { + instance[context.name + '_' + context.kind + 'Context'] = context; + }); + + if (context.kind === 'getter') { + return function () { + return value.call(this) + 1; + } + } else { + return function (v) { + return value.call(this, v + 1); + } + } +} + +class Foo { + value = 1; + + @dec + get a() { + return this.value; + } + + @dec + set a(v) { + this.value = v; + } + + @dec + get ['b']() { + return this.value; + } + + @dec + set ['b'](v) { + this.value = v; + } +} + +let foo = new Foo(); + +const a_getterContext = foo['a_getterContext']; +const a_setterContext = foo['a_setterContext']; + +const b_getterContext = foo['b_getterContext']; +const b_setterContext = foo['b_setterContext']; + +expect(foo.a).toBe(2); +expect(foo.b).toBe(2); +foo.a = 123; +expect(foo.a).toBe(125); +expect(foo.b).toBe(125); +foo.b = 456; +expect(foo.a).toBe(458); +expect(foo.b).toBe(458); + +expect(a_getterContext.name).toBe('a'); +expect(a_getterContext.kind).toBe('getter'); +expect(a_getterContext.isStatic).toBe(false); +expect(a_getterContext.isPrivate).toBe(false); +expect(typeof a_getterContext.addInitializer).toBe('function'); +expect(typeof a_getterContext.setMetadata).toBe('function'); +expect(typeof a_getterContext.getMetadata).toBe('function'); + +expect(a_setterContext.name).toBe('a'); +expect(a_setterContext.kind).toBe('setter'); +expect(a_setterContext.isStatic).toBe(false); +expect(a_setterContext.isPrivate).toBe(false); +expect(typeof a_setterContext.addInitializer).toBe('function'); +expect(typeof a_setterContext.setMetadata).toBe('function'); +expect(typeof a_setterContext.getMetadata).toBe('function'); + +expect(b_getterContext.name).toBe('b'); +expect(b_getterContext.kind).toBe('getter'); +expect(b_getterContext.isStatic).toBe(false); +expect(b_getterContext.isPrivate).toBe(false); +expect(typeof b_getterContext.addInitializer).toBe('function'); +expect(typeof b_getterContext.setMetadata).toBe('function'); +expect(typeof b_getterContext.getMetadata).toBe('function'); + +expect(b_setterContext.name).toBe('b'); +expect(b_setterContext.kind).toBe('setter'); +expect(b_setterContext.isStatic).toBe(false); +expect(b_setterContext.isPrivate).toBe(false); +expect(typeof b_setterContext.addInitializer).toBe('function'); +expect(typeof b_setterContext.setMetadata).toBe('function'); +expect(typeof b_setterContext.getMetadata).toBe('function'); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/public/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/public/input.js new file mode 100644 index 000000000000..ef384166d689 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/public/input.js @@ -0,0 +1,23 @@ +class Foo { + value = 1; + + @dec + get a() { + return this.value; + } + + @dec + set a(v) { + this.value = v; + } + + @dec + get ['b']() { + return this.value; + } + + @dec + set ['b'](v) { + this.value = v; + } +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/public/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/public/output.js new file mode 100644 index 000000000000..77f389050de8 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/public/output.js @@ -0,0 +1,34 @@ +var _computedKey, _computedKey2, _initProto, _classDecs, _elementDecs; + +_elementDecs = [[dec, 3, "a"], [dec, 4, "a"], (_computedKey = 'b', null), [dec, 3, _computedKey], (_computedKey2 = 'b', null), [dec, 4, _computedKey2]] +_classDecs = [] + +class Foo { + constructor(...args) { + babelHelpers.defineProperty(this, "value", 1); + + _initProto(this); + } + + get a() { + return this.value; + } + + set a(v) { + this.value = v; + } + + get [_computedKey]() { + return this.value; + } + + set [_computedKey2](v) { + this.value = v; + } + +} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _initProto = ret[0]; +})(); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/static-private/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/static-private/exec.js new file mode 100644 index 000000000000..90bdd12c47ec --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/static-private/exec.js @@ -0,0 +1,65 @@ +function dec(value, context) { + context.addInitializer((instance) => { + instance[context.name + '_' + context.kind + 'Context'] = context; + }); + + if (context.kind === 'getter') { + return function () { + return value.call(this) + 1; + } + } else { + return function (v) { + return value.call(this, v + 1); + } + } +} + +class Foo { + static value = 1; + + @dec + static get #a() { + return this.value; + } + + @dec + static set #a(v) { + this.value = v; + } + + static getA() { + return this.#a; + } + + static setA(v) { + this.#a = v; + } +} + +const a_getterContext = Foo['#a_getterContext']; +const a_setterContext = Foo['#a_setterContext']; + +expect(a_getterContext.access.get.call(Foo)).toBe(2); +expect(Foo.getA()).toBe(2); +a_setterContext.access.set.call(Foo, 123); +expect(a_getterContext.access.get.call(Foo)).toBe(125); +expect(Foo.getA()).toBe(125); +Foo.setA(456); +expect(a_getterContext.access.get.call(Foo)).toBe(458); +expect(Foo.getA()).toBe(458); + +expect(a_getterContext.name).toBe('#a'); +expect(a_getterContext.kind).toBe('getter'); +expect(a_getterContext.isStatic).toBe(true); +expect(a_getterContext.isPrivate).toBe(true); +expect(typeof a_getterContext.addInitializer).toBe('function'); +expect(typeof a_getterContext.setMetadata).toBe('function'); +expect(typeof a_getterContext.getMetadata).toBe('function'); + +expect(a_setterContext.name).toBe('#a'); +expect(a_setterContext.kind).toBe('setter'); +expect(a_setterContext.isStatic).toBe(true); +expect(a_setterContext.isPrivate).toBe(true); +expect(typeof a_setterContext.addInitializer).toBe('function'); +expect(typeof a_setterContext.setMetadata).toBe('function'); +expect(typeof a_setterContext.getMetadata).toBe('function'); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/static-private/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/static-private/input.js new file mode 100644 index 000000000000..f439a9152528 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/static-private/input.js @@ -0,0 +1,21 @@ +class Foo { + static value = 1; + + @dec + static get #a() { + return this.value; + } + + @dec + static set #a(v) { + this.value = v; + } + + static getA() { + return this.#a; + } + + static setA(v) { + this.#a = v; + } +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/static-private/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/static-private/output.js new file mode 100644 index 000000000000..157e5a7c03b8 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/static-private/output.js @@ -0,0 +1,47 @@ +var _call_a, _call_a2, _initStatic, _classDecs, _elementDecs; + +_elementDecs = [[dec, 8, "A", "a"], [dec, 9, "B", "a"]] +_classDecs = [] + +class Foo { + static A() { + return this.value; + } + + static B(v) { + this.value = v; + } + + static getA() { + return babelHelpers.classStaticPrivateFieldSpecGet(this, Foo, _a); + } + + static setA(v) { + babelHelpers.classStaticPrivateFieldSpecSet(this, Foo, _a, v); + } + +} + +function _get_a() { + return _call_a(this); +} + +function _set_a(v) { + _call_a2(this, v); +} + +var _a = { + get: _get_a, + set: _set_a +}; + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _call_a = ret[0]; + _call_a2 = ret[1]; + _initStatic = ret[2]; + + _initStatic(Foo); +})(); + +babelHelpers.defineProperty(Foo, "value", 1); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/static-public/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/static-public/exec.js new file mode 100644 index 000000000000..23ea7b2f54cd --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/static-public/exec.js @@ -0,0 +1,87 @@ +function dec(value, context) { + context.addInitializer((instance) => { + instance[context.name + '_' + context.kind + 'Context'] = context; + }); + + if (context.kind === 'getter') { + return function () { + return value.call(this) + 1; + } + } else { + return function (v) { + return value.call(this, v + 1); + } + } +} + +class Foo { + static value = 1; + + @dec + static get a() { + return this.value; + } + + @dec + static set a(v) { + this.value = v; + } + + @dec + static get ['b']() { + return this.value; + } + + @dec + static set ['b'](v) { + this.value = v; + } +} + +const a_getterContext = Foo['a_getterContext']; +const a_setterContext = Foo['a_setterContext']; + +const b_getterContext = Foo['b_getterContext']; +const b_setterContext = Foo['b_setterContext']; + +expect(Foo.a).toBe(2); +expect(Foo.b).toBe(2); +Foo.a = 123; +expect(Foo.a).toBe(125); +expect(Foo.b).toBe(125); +Foo.b = 456; +expect(Foo.a).toBe(458); +expect(Foo.b).toBe(458); + +expect(a_getterContext.name).toBe('a'); +expect(a_getterContext.kind).toBe('getter'); +expect(a_getterContext.isStatic).toBe(true); +expect(a_getterContext.isPrivate).toBe(false); +expect(typeof a_getterContext.addInitializer).toBe('function'); +expect(typeof a_getterContext.setMetadata).toBe('function'); +expect(typeof a_getterContext.getMetadata).toBe('function'); + +expect(a_setterContext.name).toBe('a'); +expect(a_setterContext.kind).toBe('setter'); +expect(a_setterContext.isStatic).toBe(true); +expect(a_setterContext.isPrivate).toBe(false); +expect(typeof a_setterContext.addInitializer).toBe('function'); +expect(typeof a_setterContext.setMetadata).toBe('function'); +expect(typeof a_setterContext.getMetadata).toBe('function'); + +expect(b_getterContext.name).toBe('b'); +expect(b_getterContext.kind).toBe('getter'); +expect(b_getterContext.isStatic).toBe(true); +expect(b_getterContext.isPrivate).toBe(false); +expect(typeof b_getterContext.addInitializer).toBe('function'); +expect(typeof b_getterContext.setMetadata).toBe('function'); +expect(typeof b_getterContext.getMetadata).toBe('function'); + +expect(b_setterContext.name).toBe('b'); +expect(b_setterContext.kind).toBe('setter'); +expect(b_setterContext.isStatic).toBe(true); +expect(b_setterContext.isPrivate).toBe(false); +expect(typeof b_setterContext.addInitializer).toBe('function'); +expect(typeof b_setterContext.setMetadata).toBe('function'); +expect(typeof b_setterContext.getMetadata).toBe('function'); + diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/static-public/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/static-public/input.js new file mode 100644 index 000000000000..193bf7b836a2 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/static-public/input.js @@ -0,0 +1,23 @@ +class Foo { + static value = 1; + + @dec + static get a() { + return this.value; + } + + @dec + static set a(v) { + this.value = v; + } + + @dec + static get ['b']() { + return this.value; + } + + @dec + static set ['b'](v) { + this.value = v; + } +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/static-public/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/static-public/output.js new file mode 100644 index 000000000000..79b518870dde --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters-and-setters/static-public/output.js @@ -0,0 +1,32 @@ +var _computedKey, _computedKey2, _initStatic, _classDecs, _elementDecs; + +_elementDecs = [[dec, 8, "a"], [dec, 9, "a"], (_computedKey = 'b', null), [dec, 8, _computedKey], (_computedKey2 = 'b', null), [dec, 9, _computedKey2]] +_classDecs = [] + +class Foo { + static get a() { + return this.value; + } + + static set a(v) { + this.value = v; + } + + static get [_computedKey]() { + return this.value; + } + + static set [_computedKey2](v) { + this.value = v; + } + +} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _initStatic = ret[0]; + + _initStatic(Foo); +})(); + +babelHelpers.defineProperty(Foo, "value", 1); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/options.json b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/options.json new file mode 100644 index 000000000000..fcd5c64e5a80 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/options.json @@ -0,0 +1,11 @@ +{ + "plugins": [ + [ + "proposal-decorators", + { "version": "2020-09", "decoratorsBeforeExport": false } + ], + "proposal-class-properties", + "proposal-private-methods", + "proposal-class-static-block" + ] +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/private/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/private/exec.js new file mode 100644 index 000000000000..70c0965c67e9 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/private/exec.js @@ -0,0 +1,39 @@ +function dec(get, context) { + context.addInitializer((instance) => { + instance[context.name + 'Context'] = context; + }); + + return function () { + return get.call(this) + 1; + } +} + +class Foo { + value = 1; + + @dec + get #a() { + return this.value; + } + + getA() { + return this.#a; + } +} + +let foo = new Foo(); + +const aContext = foo['#aContext']; + +expect(aContext.access.get.call(foo)).toBe(2); +expect(foo.getA()).toBe(2); +foo.value = 123; +expect(aContext.access.get.call(foo)).toBe(124); +expect(foo.getA()).toBe(124); +expect(aContext.name).toBe('#a'); +expect(aContext.kind).toBe('getter'); +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/2020-09-getters/private/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/private/input.js new file mode 100644 index 000000000000..49c601a1dd5b --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/private/input.js @@ -0,0 +1,12 @@ +class Foo { + value = 1; + + @dec + get #a() { + return this.value; + } + + getA() { + return this.#a; + } +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/private/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/private/output.js new file mode 100644 index 000000000000..0b97442ebb57 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/private/output.js @@ -0,0 +1,37 @@ +var _call_a, _initProto, _classDecs, _elementDecs; + +_elementDecs = [[dec, 3, "A", "a"]] +_classDecs = [] + +var _a = /*#__PURE__*/new WeakMap(); + +class Foo { + constructor(...args) { + babelHelpers.classPrivateFieldInitSpec(this, _a, { + get: _get_a, + set: void 0 + }); + babelHelpers.defineProperty(this, "value", 1); + + _initProto(this); + } + + A() { + return this.value; + } + + getA() { + return babelHelpers.classPrivateFieldGet(this, _a); + } + +} + +function _get_a() { + return _call_a(this); +} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _call_a = ret[0]; + _initProto = ret[1]; +})(); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/public/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/public/exec.js new file mode 100644 index 000000000000..22f52e96d4f2 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/public/exec.js @@ -0,0 +1,50 @@ +function dec(get, context) { + context.addInitializer((instance) => { + instance[context.name + 'Context'] = context; + }); + + return function () { + return get.call(this) + 1; + } +} + +class Foo { + value = 1; + + @dec + get a() { + return this.value; + } + + @dec + get ['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('getter'); +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('getter'); +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/2020-09-getters/public/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/public/input.js new file mode 100644 index 000000000000..9d1cbae52f1d --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/public/input.js @@ -0,0 +1,13 @@ +class Foo { + value = 1; + + @dec + get a() { + return this.value; + } + + @dec + get ['b']() { + return this.value; + } +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/public/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/public/output.js new file mode 100644 index 000000000000..8d47a074b74e --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/public/output.js @@ -0,0 +1,26 @@ +var _computedKey, _initProto, _classDecs, _elementDecs; + +_elementDecs = [[dec, 3, "a"], (_computedKey = 'b', null), [dec, 3, _computedKey]] +_classDecs = [] + +class Foo { + constructor(...args) { + babelHelpers.defineProperty(this, "value", 1); + + _initProto(this); + } + + get a() { + return this.value; + } + + get [_computedKey]() { + return this.value; + } + +} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _initProto = ret[0]; +})(); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/static-private/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/static-private/exec.js new file mode 100644 index 000000000000..7d1c8027d545 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/static-private/exec.js @@ -0,0 +1,38 @@ +function dec(get, context) { + context.addInitializer((instance) => { + instance[context.name + 'Context'] = context; + }); + + return function () { + return get.call(this) + 1; + } +} + +class Foo { + static value = 1; + + @dec + static get #a() { + return this.value; + } + + static getA() { + return this.#a; + } +} + +const aContext = Foo['#aContext']; + +expect(aContext.access.get.call(Foo)).toBe(2); +expect(Foo.getA()).toBe(2); +Foo.value = 123; +expect(aContext.access.get.call(Foo)).toBe(124); +expect(Foo.getA()).toBe(124); + +expect(aContext.name).toBe('#a'); +expect(aContext.kind).toBe('getter'); +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/2020-09-getters/static-private/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/static-private/input.js new file mode 100644 index 000000000000..90edd26364d8 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/static-private/input.js @@ -0,0 +1,12 @@ +class Foo { + static value = 1; + + @dec + static get #a() { + return this.value; + } + + static getA() { + return this.#a; + } +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/static-private/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/static-private/output.js new file mode 100644 index 000000000000..21ba7111893d --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/static-private/output.js @@ -0,0 +1,34 @@ +var _call_a, _initStatic, _classDecs, _elementDecs; + +_elementDecs = [[dec, 8, "A", "a"]] +_classDecs = [] + +class Foo { + static A() { + return this.value; + } + + static getA() { + return babelHelpers.classStaticPrivateFieldSpecGet(this, Foo, _a); + } + +} + +function _get_a() { + return _call_a(this); +} + +var _a = { + get: _get_a, + set: void 0 +}; + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _call_a = ret[0]; + _initStatic = ret[1]; + + _initStatic(Foo); +})(); + +babelHelpers.defineProperty(Foo, "value", 1); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/static-public/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/static-public/exec.js new file mode 100644 index 000000000000..94c7b9d5dab5 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/static-public/exec.js @@ -0,0 +1,48 @@ +function dec(get, context) { + context.addInitializer((instance) => { + instance[context.name + 'Context'] = context; + }); + + return function () { + return get.call(this) + 1; + } +} + +class Foo { + static value = 1; + + @dec + static get a() { + return this.value; + } + + @dec + static get ['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('getter'); +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('getter'); +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/2020-09-getters/static-public/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/static-public/input.js new file mode 100644 index 000000000000..1383fa58abf4 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/static-public/input.js @@ -0,0 +1,13 @@ +class Foo { + static value = 1; + + @dec + static get a() { + return this.value; + } + + @dec + static get ['b']() { + return this.value; + } +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/static-public/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/static-public/output.js new file mode 100644 index 000000000000..886f0abd22a8 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-getters/static-public/output.js @@ -0,0 +1,24 @@ +var _computedKey, _initStatic, _classDecs, _elementDecs; + +_elementDecs = [[dec, 8, "a"], (_computedKey = 'b', null), [dec, 8, _computedKey]] +_classDecs = [] + +class Foo { + static get a() { + return this.value; + } + + static get [_computedKey]() { + return this.value; + } + +} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _initStatic = ret[0]; + + _initStatic(Foo); +})(); + +babelHelpers.defineProperty(Foo, "value", 1); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/class/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/class/exec.js new file mode 100644 index 000000000000..2eaa3d1263b0 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/class/exec.js @@ -0,0 +1,10 @@ +const key = Symbol(); + +function dec(_, { setMetadata }) { + setMetadata(key, 123); +} + +@dec +class Foo {} + +expect(Foo[Symbol.metadata][key].constructor).toBe(123); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/getting-previously-set-metadata/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/getting-previously-set-metadata/exec.js new file mode 100644 index 000000000000..73eeed66ac57 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/getting-previously-set-metadata/exec.js @@ -0,0 +1,30 @@ +const key = Symbol(); + +function dec1(_, { setMetadata, getMetadata }) { + expect(getMetadata(key)).toBe(undefined); + setMetadata(key, 123); + expect(getMetadata(key)).toBe(123); +} + +function dec2(_, { setMetadata, getMetadata }) { + expect(getMetadata(key)).toBe(123); + setMetadata(key, 456); + expect(getMetadata(key)).toBe(456); +} + +class Foo { + @dec1 @dec2 a; +} + +function dec3(_, { setMetadata, getMetadata }) { + expect(getMetadata(key)).toBe(undefined); + setMetadata(key, 789); + expect(getMetadata(key)).toBe(789); +} + +class Bar extends Foo { + @dec3 a; +} + +expect(Foo.prototype[Symbol.metadata][key].public.a).toBe(456); +expect(Bar.prototype[Symbol.metadata][key].public.a).toBe(789); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/inheritance-private/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/inheritance-private/exec.js new file mode 100644 index 000000000000..d36a857a1e77 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/inheritance-private/exec.js @@ -0,0 +1,16 @@ +const key = Symbol(); + +function dec(_, { setMetadata }) { + setMetadata(key, 123); +} + +class Foo { + @dec #a; +} + +class Bar extends Foo { + @dec #b; +} + +expect(Foo.prototype[Symbol.metadata][key].private).toEqual([123]); +expect(Bar.prototype[Symbol.metadata][key].private).toEqual([123, 123]); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/inheritance-public/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/inheritance-public/exec.js new file mode 100644 index 000000000000..666bc9601116 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/inheritance-public/exec.js @@ -0,0 +1,18 @@ +const key = Symbol(); + +function dec(_, { setMetadata }) { + setMetadata(key, 123); +} + +class Foo { + @dec a; +} + +class Bar extends Foo { + @dec b; +} + +expect(Foo.prototype[Symbol.metadata][key].public.a).toEqual(123); +expect(Foo.prototype[Symbol.metadata][key].public.b).toEqual(undefined); +expect(Bar.prototype[Symbol.metadata][key].public.a).toEqual(123); +expect(Bar.prototype[Symbol.metadata][key].public.b).toEqual(123); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/non-symbol-keys/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/non-symbol-keys/exec.js new file mode 100644 index 000000000000..a6c8ae1af6f8 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/non-symbol-keys/exec.js @@ -0,0 +1,10 @@ +expect(() => { + const key = 'test'; + + function dec(_, { setMetadata }) { + setMetadata(key, 123); + } + + @dec + class Foo {} +}).toThrow('Metadata keys must be symbols, received: test') diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/options.json b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/options.json new file mode 100644 index 000000000000..fcd5c64e5a80 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/options.json @@ -0,0 +1,11 @@ +{ + "plugins": [ + [ + "proposal-decorators", + { "version": "2020-09", "decoratorsBeforeExport": false } + ], + "proposal-class-properties", + "proposal-private-methods", + "proposal-class-static-block" + ] +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/proto-private/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/proto-private/exec.js new file mode 100644 index 000000000000..a10baeb36f72 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/proto-private/exec.js @@ -0,0 +1,11 @@ +const key = Symbol(); + +function dec(_, { setMetadata }) { + setMetadata(key, 123); +} + +class Foo { + @dec #a; +} + +expect(Foo.prototype[Symbol.metadata][key].private[0]).toBe(123); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/proto-public/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/proto-public/exec.js new file mode 100644 index 000000000000..021bdaf9a5e0 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/proto-public/exec.js @@ -0,0 +1,11 @@ +const key = Symbol(); + +function dec(_, { setMetadata }) { + setMetadata(key, 123); +} + +class Foo { + @dec a; +} + +expect(Foo.prototype[Symbol.metadata][key].public.a).toBe(123); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/static-private/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/static-private/exec.js new file mode 100644 index 000000000000..c8453c1559ce --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/static-private/exec.js @@ -0,0 +1,11 @@ +const key = Symbol(); + +function dec(_, { setMetadata }) { + setMetadata(key, 123); +} + +class Foo { + @dec static #a; +} + +expect(Foo[Symbol.metadata][key].private[0]).toBe(123); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/static-public/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/static-public/exec.js new file mode 100644 index 000000000000..1635eab50b54 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-metadata/static-public/exec.js @@ -0,0 +1,11 @@ +const key = Symbol(); + +function dec(_, { setMetadata }) { + setMetadata(key, 123); +} + +class Foo { + @dec static a; +} + +expect(Foo[Symbol.metadata][key].public.a).toBe(123); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/options.json b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/options.json new file mode 100644 index 000000000000..fcd5c64e5a80 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/options.json @@ -0,0 +1,11 @@ +{ + "plugins": [ + [ + "proposal-decorators", + { "version": "2020-09", "decoratorsBeforeExport": false } + ], + "proposal-class-properties", + "proposal-private-methods", + "proposal-class-static-block" + ] +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/private/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/private/exec.js new file mode 100644 index 000000000000..5a3ce1785ff0 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/private/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/2020-09-methods/private/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/private/input.js new file mode 100644 index 000000000000..fc7ace8128ee --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/private/input.js @@ -0,0 +1,12 @@ +class Foo { + value = 1; + + @dec + #a() { + return this.value; + } + + callA() { + return this.#a(); + } +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/private/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/private/output.js new file mode 100644 index 000000000000..4ab49284e9bb --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/private/output.js @@ -0,0 +1,34 @@ +var _call_a, _initProto, _classDecs, _elementDecs; + +_elementDecs = [[dec, 2, "A", "a"]] +_classDecs = [] + +var _a = /*#__PURE__*/new WeakSet(); + +class Foo { + constructor(..._args) { + babelHelpers.classPrivateMethodInitSpec(this, _a); + babelHelpers.defineProperty(this, "value", 1); + + _initProto(this); + } + + A() { + return this.value; + } + + callA() { + return babelHelpers.classPrivateMethodGet(this, _a, _a2).call(this); + } + +} + +function _a2(...args) { + return _call_a(this, args); +} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _call_a = ret[0]; + _initProto = ret[1]; +})(); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/public/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/public/exec.js new file mode 100644 index 000000000000..0f27b61caa4d --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/public/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/2020-09-methods/public/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/public/input.js new file mode 100644 index 000000000000..f364442146ea --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/public/input.js @@ -0,0 +1,13 @@ +class Foo { + value = 1; + + @dec + a() { + return this.value; + } + + @dec + ['b']() { + return this.value; + } +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/public/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/public/output.js new file mode 100644 index 000000000000..0337bd9c4b13 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/public/output.js @@ -0,0 +1,26 @@ +var _computedKey, _initProto, _classDecs, _elementDecs; + +_elementDecs = [[dec, 2, "a"], (_computedKey = 'b', null), [dec, 2, _computedKey]] +_classDecs = [] + +class Foo { + constructor(...args) { + babelHelpers.defineProperty(this, "value", 1); + + _initProto(this); + } + + a() { + return this.value; + } + + [_computedKey]() { + return this.value; + } + +} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _initProto = ret[0]; +})(); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/static-private/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/static-private/exec.js new file mode 100644 index 000000000000..bab58275b48a --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/static-private/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/2020-09-methods/static-private/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/static-private/input.js new file mode 100644 index 000000000000..e68191455f14 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/static-private/input.js @@ -0,0 +1,12 @@ +class Foo { + static value = 1; + + @dec + static #a() { + return this.value; + } + + static callA() { + return this.#a(); + } +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/static-private/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/static-private/output.js new file mode 100644 index 000000000000..b4c0f96cb435 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/static-private/output.js @@ -0,0 +1,29 @@ +var _call_a, _initStatic, _classDecs, _elementDecs; + +_elementDecs = [[dec, 7, "A", "a"]] +_classDecs = [] + +class Foo { + static A() { + return this.value; + } + + static callA() { + return babelHelpers.classStaticPrivateMethodGet(this, Foo, _a).call(this); + } + +} + +function _a(...args) { + return _call_a(this, args); +} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _call_a = ret[0]; + _initStatic = ret[1]; + + _initStatic(Foo); +})(); + +babelHelpers.defineProperty(Foo, "value", 1); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/static-public/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/static-public/exec.js new file mode 100644 index 000000000000..e68458db59d9 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/static-public/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/2020-09-methods/static-public/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/static-public/input.js new file mode 100644 index 000000000000..77dae8711cd3 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/static-public/input.js @@ -0,0 +1,13 @@ +class Foo { + static value = 1; + + @dec + static a() { + return this.value; + } + + @dec + static ['b']() { + return this.value; + } +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/static-public/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/static-public/output.js new file mode 100644 index 000000000000..9e7baa48e30b --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-methods/static-public/output.js @@ -0,0 +1,24 @@ +var _computedKey, _initStatic, _classDecs, _elementDecs; + +_elementDecs = [[dec, 7, "a"], (_computedKey = 'b', null), [dec, 7, _computedKey]] +_classDecs = [] + +class Foo { + static a() { + return this.value; + } + + static [_computedKey]() { + return this.value; + } + +} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _initStatic = ret[0]; + + _initStatic(Foo); +})(); + +babelHelpers.defineProperty(Foo, "value", 1); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-misc/initializer-timing/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-misc/initializer-timing/exec.js new file mode 100644 index 000000000000..ac5920dcd16a --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-misc/initializer-timing/exec.js @@ -0,0 +1,34 @@ +function dec1(fn, context) { + context.addInitializer((instance) => { + expect(instance.value).toBe(undefined); + }); + + return fn; +} + +class Foo { + value = 1; + + @dec1 + foo() {} +} + +function dec2(fn, context) { + context.addInitializer((instance) => { + expect(instance.value).toBe(1); + }); + + return fn; +} + + +class Bar extends Foo { + constructor() { + super(); + + this.value = 2; + } + + @dec2 + bar() {} +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-misc/options.json b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-misc/options.json new file mode 100644 index 000000000000..fcd5c64e5a80 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-misc/options.json @@ -0,0 +1,11 @@ +{ + "plugins": [ + [ + "proposal-decorators", + { "version": "2020-09", "decoratorsBeforeExport": false } + ], + "proposal-class-properties", + "proposal-private-methods", + "proposal-class-static-block" + ] +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-misc/valid-expression-formats/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-misc/valid-expression-formats/input.js new file mode 100644 index 000000000000..844a924e3e80 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-misc/valid-expression-formats/input.js @@ -0,0 +1,22 @@ +@dec +@call() +@chain.expr() +@(arbitrary + expr) +@(array[expr]) +class Foo { + #a; + + @dec + @call() + @chain.expr() + @(arbitrary + expr) + @(array[expr]) + method() {} + + makeClass() { + return class Nested { + @(this.#a) + bar; + } + } +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-misc/valid-expression-formats/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-misc/valid-expression-formats/output.js new file mode 100644 index 000000000000..9bd14b4dd1c3 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-misc/valid-expression-formats/output.js @@ -0,0 +1,47 @@ +var _initProto, _initClass, _classDecs, _elementDecs; + +let _Foo; + +_elementDecs = [[[dec, call(), chain.expr(), arbitrary + expr, array[expr]], 2, "method"]] +_classDecs = [dec, call(), chain.expr(), arbitrary + expr, array[expr]] + +var _a = /*#__PURE__*/new WeakMap(); + +class Foo { + constructor(...args) { + babelHelpers.classPrivateFieldInitSpec(this, _a, { + writable: true, + value: void 0 + }); + + _initProto(this); + } + + method() {} + + makeClass() { + var _init_bar, _classDecs2, _elementDecs2, _class, _temp; + + return _elementDecs2 = [[babelHelpers.classPrivateFieldGet(this, _a), 0, "bar"]], _classDecs2 = [], (_temp = _class = class Nested { + constructor() { + babelHelpers.defineProperty(this, "bar", _init_bar(this)); + } + + }, (() => { + let ret = babelHelpers.applyDecs(_class, _elementDecs2, _classDecs2); + _init_bar = ret[0]; + })(), _temp); + } + +} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _initProto = ret[0]; + _Foo = ret[1]; + _initClass = ret[2]; +})(); + +(() => { + _initClass(); +})(); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/options.json b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/options.json new file mode 100644 index 000000000000..fcd5c64e5a80 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/options.json @@ -0,0 +1,11 @@ +{ + "plugins": [ + [ + "proposal-decorators", + { "version": "2020-09", "decoratorsBeforeExport": false } + ], + "proposal-class-properties", + "proposal-private-methods", + "proposal-class-static-block" + ] +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/private/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/private/exec.js new file mode 100644 index 000000000000..c2768a0a2c67 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/private/exec.js @@ -0,0 +1,40 @@ +function dec(set, context) { + context.addInitializer((instance) => { + instance[context.name + 'Context'] = context; + }); + + return function (v) { + return set.call(this, v + 1); + } +} + +class Foo { + value = 1; + + @dec + set #a(v) { + return this.value = v; + } + + setA(v) { + this.#a = v; + } +} + +let foo = new Foo(); + +const aContext = foo['#aContext']; + +expect(foo.value).toBe(1); +aContext.access.set.call(foo, 123); +expect(foo.value).toBe(124); +foo.setA(456); +expect(foo.value).toBe(457); + +expect(aContext.name).toBe('#a'); +expect(aContext.kind).toBe('setter'); +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/2020-09-setters/private/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/private/input.js new file mode 100644 index 000000000000..254f2f102f9a --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/private/input.js @@ -0,0 +1,12 @@ +class Foo { + value = 1; + + @dec + set #a(v) { + return this.value = v; + } + + setA(v) { + this.#a = v; + } +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/private/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/private/output.js new file mode 100644 index 000000000000..8d8f38326294 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/private/output.js @@ -0,0 +1,37 @@ +var _call_a, _initProto, _classDecs, _elementDecs; + +_elementDecs = [[dec, 4, "A", "a"]] +_classDecs = [] + +var _a = /*#__PURE__*/new WeakMap(); + +class Foo { + constructor(...args) { + babelHelpers.classPrivateFieldInitSpec(this, _a, { + get: void 0, + set: _set_a + }); + babelHelpers.defineProperty(this, "value", 1); + + _initProto(this); + } + + A(v) { + return this.value = v; + } + + setA(v) { + babelHelpers.classPrivateFieldSet(this, _a, v); + } + +} + +function _set_a(v) { + _call_a(this, v); +} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _call_a = ret[0]; + _initProto = ret[1]; +})(); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/public/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/public/exec.js new file mode 100644 index 000000000000..ab2f98534b95 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/public/exec.js @@ -0,0 +1,50 @@ +function dec(set, context) { + context.addInitializer((instance) => { + instance[context.name + 'Context'] = context; + }); + + return function (v) { + return set.call(this, v + 1); + } +} + +class Foo { + value = 1; + + @dec + set a(v) { + return this.value = v; + } + + @dec + set ['b'](v) { + return this.value = v; + } +} + +let foo = new Foo(); + +const aContext = foo['aContext']; +const bContext = foo['bContext']; + +expect(foo.value).toBe(1); +foo.a = 123; +expect(foo.value).toBe(124); +foo.a = 456; +expect(foo.value).toBe(457); + +expect(aContext.name).toBe('a'); +expect(aContext.kind).toBe('setter'); +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('setter'); +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/2020-09-setters/public/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/public/input.js new file mode 100644 index 000000000000..b8b837257ff0 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/public/input.js @@ -0,0 +1,13 @@ +class Foo { + value = 1; + + @dec + set a(v) { + return this.value = v; + } + + @dec + set ['b'](v) { + return this.value = v; + } +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/public/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/public/output.js new file mode 100644 index 000000000000..d610a6f39593 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/public/output.js @@ -0,0 +1,26 @@ +var _computedKey, _initProto, _classDecs, _elementDecs; + +_elementDecs = [[dec, 4, "a"], (_computedKey = 'b', null), [dec, 4, _computedKey]] +_classDecs = [] + +class Foo { + constructor(...args) { + babelHelpers.defineProperty(this, "value", 1); + + _initProto(this); + } + + set a(v) { + return this.value = v; + } + + set [_computedKey](v) { + return this.value = v; + } + +} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _initProto = ret[0]; +})(); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/static-private/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/static-private/exec.js new file mode 100644 index 000000000000..55ecade3ac96 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/static-private/exec.js @@ -0,0 +1,38 @@ +function dec(set, context) { + context.addInitializer((instance) => { + instance[context.name + 'Context'] = context; + }); + + return function (v) { + return set.call(this, v + 1); + } +} + +class Foo { + static value = 1; + + @dec + static set #a(v) { + return this.value = v; + } + + static setA(v) { + this.#a = v; + } +} + +const aContext = Foo['#aContext']; + +expect(Foo.value).toBe(1); +aContext.access.set.call(Foo, 123); +expect(Foo.value).toBe(124); +Foo.setA(456); +expect(Foo.value).toBe(457); + +expect(aContext.name).toBe('#a'); +expect(aContext.kind).toBe('setter'); +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/2020-09-setters/static-private/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/static-private/input.js new file mode 100644 index 000000000000..0038d6b6bd30 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/static-private/input.js @@ -0,0 +1,12 @@ +class Foo { + static value = 1; + + @dec + static set #a(v) { + return this.value = v; + } + + static setA(v) { + this.#a = v; + } +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/static-private/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/static-private/output.js new file mode 100644 index 000000000000..783fe8acd20c --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/static-private/output.js @@ -0,0 +1,34 @@ +var _call_a, _initStatic, _classDecs, _elementDecs; + +_elementDecs = [[dec, 9, "A", "a"]] +_classDecs = [] + +class Foo { + static A(v) { + return this.value = v; + } + + static setA(v) { + babelHelpers.classStaticPrivateFieldSpecSet(this, Foo, _a, v); + } + +} + +function _set_a(v) { + _call_a(this, v); +} + +var _a = { + get: void 0, + set: _set_a +}; + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _call_a = ret[0]; + _initStatic = ret[1]; + + _initStatic(Foo); +})(); + +babelHelpers.defineProperty(Foo, "value", 1); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/static-public/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/static-public/exec.js new file mode 100644 index 000000000000..2e7eb639048c --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/static-public/exec.js @@ -0,0 +1,49 @@ +function dec(set, context) { + context.addInitializer((instance) => { + instance[context.name + 'Context'] = context; + }); + + return function (v) { + return set.call(this, v + 1); + } +} + +class Foo { + static value = 1; + + @dec + static set a(v) { + return this.value = v; + } + + @dec + static set ['b'](v) { + return this.value = v; + } +} + +const aContext = Foo['aContext']; +const bContext = Foo['bContext']; + + +expect(Foo.value).toBe(1); +Foo.a = 123; +expect(Foo.value).toBe(124); +Foo.a = 456; +expect(Foo.value).toBe(457); + +expect(aContext.name).toBe('a'); +expect(aContext.kind).toBe('setter'); +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('setter'); +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/2020-09-setters/static-public/input.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/static-public/input.js new file mode 100644 index 000000000000..0cca65be7fa3 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/static-public/input.js @@ -0,0 +1,13 @@ +class Foo { + static value = 1; + + @dec + static set a(v) { + return this.value = v; + } + + @dec + static set ['b'](v) { + return this.value = v; + } +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/static-public/output.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/static-public/output.js new file mode 100644 index 000000000000..3648deee3430 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2020-09-setters/static-public/output.js @@ -0,0 +1,24 @@ +var _computedKey, _initStatic, _classDecs, _elementDecs; + +_elementDecs = [[dec, 9, "a"], (_computedKey = 'b', null), [dec, 9, _computedKey]] +_classDecs = [] + +class Foo { + static set a(v) { + return this.value = v; + } + + static set [_computedKey](v) { + return this.value = v; + } + +} + +(() => { + let ret = babelHelpers.applyDecs(Foo, _elementDecs, _classDecs); + _initStatic = ret[0]; + + _initStatic(Foo); +})(); + +babelHelpers.defineProperty(Foo, "value", 1); diff --git a/packages/babel-plugin-syntax-decorators/src/index.ts b/packages/babel-plugin-syntax-decorators/src/index.ts index a825adfd77a5..fc8c6a5c362e 100644 --- a/packages/babel-plugin-syntax-decorators/src/index.ts +++ b/packages/babel-plugin-syntax-decorators/src/index.ts @@ -3,7 +3,7 @@ import { declare } from "@babel/helper-plugin-utils"; export default declare((api, options) => { api.assertVersion(7); - const { legacy = false } = options; + const { legacy = false, version } = options; if (typeof legacy !== "boolean") { throw new Error("'legacy' must be a boolean."); } @@ -29,6 +29,12 @@ export default declare((api, options) => { } } + if ( + !(version === "2020-09" || version === "2018-09" || version === undefined) + ) { + throw new Error("Unsupported decorators version: " + version); + } + return { name: "syntax-decorators", @@ -38,6 +44,10 @@ export default declare((api, options) => { ? "decorators-legacy" : ["decorators", { decoratorsBeforeExport }], ); + + if (version === "2020-09") { + parserOpts.plugins.push("decoratorAutoAccessors"); + } }, }; }); diff --git a/packages/babel-runtime-corejs2/package.json b/packages/babel-runtime-corejs2/package.json index b2f4ef3f086a..acea1aaaaf0a 100644 --- a/packages/babel-runtime-corejs2/package.json +++ b/packages/babel-runtime-corejs2/package.json @@ -18,6 +18,15 @@ "regenerator-runtime": "^0.13.4" }, "exports": { + "./helpers/applyDecs": [ + { + "node": "./helpers/applyDecs.js", + "import": "./helpers/esm/applyDecs.js", + "default": "./helpers/applyDecs.js" + }, + "./helpers/applyDecs.js" + ], + "./helpers/esm/applyDecs": "./helpers/esm/applyDecs.js", "./helpers/asyncIterator": [ { "node": "./helpers/asyncIterator.js", diff --git a/packages/babel-runtime-corejs3/package.json b/packages/babel-runtime-corejs3/package.json index 57485d0a5e33..56286b17066f 100644 --- a/packages/babel-runtime-corejs3/package.json +++ b/packages/babel-runtime-corejs3/package.json @@ -17,6 +17,15 @@ "regenerator-runtime": "^0.13.4" }, "exports": { + "./helpers/applyDecs": [ + { + "node": "./helpers/applyDecs.js", + "import": "./helpers/esm/applyDecs.js", + "default": "./helpers/applyDecs.js" + }, + "./helpers/applyDecs.js" + ], + "./helpers/esm/applyDecs": "./helpers/esm/applyDecs.js", "./helpers/asyncIterator": [ { "node": "./helpers/asyncIterator.js", diff --git a/packages/babel-runtime/package.json b/packages/babel-runtime/package.json index c1bba2a1a855..f74ff2f5f127 100644 --- a/packages/babel-runtime/package.json +++ b/packages/babel-runtime/package.json @@ -17,6 +17,15 @@ "regenerator-runtime": "^0.13.4" }, "exports": { + "./helpers/applyDecs": [ + { + "node": "./helpers/applyDecs.js", + "import": "./helpers/esm/applyDecs.js", + "default": "./helpers/applyDecs.js" + }, + "./helpers/applyDecs.js" + ], + "./helpers/esm/applyDecs": "./helpers/esm/applyDecs.js", "./helpers/asyncIterator": [ { "node": "./helpers/asyncIterator.js", diff --git a/packages/babel-types/src/ast-types/generated/index.ts b/packages/babel-types/src/ast-types/generated/index.ts index 5e6547f5e991..f750429c735d 100644 --- a/packages/babel-types/src/ast-types/generated/index.ts +++ b/packages/babel-types/src/ast-types/generated/index.ts @@ -736,8 +736,10 @@ export interface ClassBody extends BaseNode { | ClassPrivateMethod | ClassProperty | ClassPrivateProperty + | ClassAccessorProperty | TSDeclareMethod | TSIndexSignature + | StaticBlock >; } diff --git a/packages/babel-types/src/builders/generated/index.ts b/packages/babel-types/src/builders/generated/index.ts index 5ba1190e4145..130814020c40 100644 --- a/packages/babel-types/src/builders/generated/index.ts +++ b/packages/babel-types/src/builders/generated/index.ts @@ -344,8 +344,10 @@ export function classBody( | t.ClassPrivateMethod | t.ClassProperty | t.ClassPrivateProperty + | t.ClassAccessorProperty | t.TSDeclareMethod | t.TSIndexSignature + | t.StaticBlock >, ): t.ClassBody { return builder.apply("ClassBody", arguments); diff --git a/packages/babel-types/src/definitions/core.ts b/packages/babel-types/src/definitions/core.ts index 5c38150c6a46..df9783987d60 100644 --- a/packages/babel-types/src/definitions/core.ts +++ b/packages/babel-types/src/definitions/core.ts @@ -1262,8 +1262,10 @@ defineType("ClassBody", { "ClassPrivateMethod", "ClassProperty", "ClassPrivateProperty", + "ClassAccessorProperty", "TSDeclareMethod", "TSIndexSignature", + "StaticBlock", ), ), ), diff --git a/yarn.lock b/yarn.lock index 64d2507c1e46..da2049cc3e95 100644 --- a/yarn.lock +++ b/yarn.lock @@ -768,6 +768,7 @@ __metadata: resolution: "@babel/helper-module-transforms@condition:BABEL_8_BREAKING?:workspace:^7.16.5#a9f1b1" dependencies: "@babel/helper-module-transforms-BABEL_8_BREAKING-false": "npm:@babel/helper-module-transforms@workspace:^7.16.5" + checksum: 3f28946c026120f97d7ad2ce86b6413d1d15f64d251db3269fedf1e85216d04981aeacf83c1ae2d1e761df5004765d4bad4d2c4198c0b744efb0201740e630a7 languageName: node linkType: hard