[Pkg-javascript-commits] [node-babel-plugin-transform-decorators-legacy] 01/02: Import Upstream version 1.3.4
Daniel Ring
techwolf-guest at moszumanska.debian.org
Sat Sep 23 07:02:00 UTC 2017
This is an automated email from the git hooks/post-receive script.
techwolf-guest pushed a commit to branch master
in repository node-babel-plugin-transform-decorators-legacy.
commit 5907368a96a5d97538b5108b05e14d7b219d4088
Author: Daniel Ring <dring at wolfishly.me>
Date: Thu Sep 21 21:59:13 2017 -0700
Import Upstream version 1.3.4
---
.gitignore | 2 +
.npmignore | 3 +
CHANGELOG.md | 37 ++
LICENSE | 22 +
README.md | 100 ++++
package.json | 40 ++
src/.babelrc | 8 +
src/index.js | 324 +++++++++++
test/.babelrc | 9 +
test/index.js | 1723 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
10 files changed, 2268 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..626c4f3
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/lib
+/node_modules
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..d5383e7
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,3 @@
+/src
+/test
+/node_modules
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..457ba43
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,37 @@
+
+# Changelog
+
+## v1.3.4
+
+* Properly support decorating class properties on subclasses.
+
+## v1.3.3
+
+* Fix same bug as fixed 1.3.1, except introduced for a different reason this time around.
+
+## v1.3.2
+
+* Fix a small bug which required the explicit deletion of `descriptor.value` when converting an initializer to an accessor.
+
+## v1.3.1
+
+* With the new split-out helpers in 1.3.0, Babel's `transform-runtime` was injecting the import statements too late. For now we avoid this by not allowing it to be rewritten by the transform.
+
+## v1.3.0
+
+* Class property support for descriptors where originally only the value was used
+* Static class property support
+
+## v1.2.0
+
+* Support class properties with no initializer, e.g. @decorator a;
+* Properly handle decorating properties/methods with string and numeric literal keys
+
+## v1.1.0
+
+* Support static class methods
+* Display helpful errors when using unsupported syntax
+
+## v1.0.0
+
+* Initial release of decorator support
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..7e9ec26
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2015 Logan Smyth <loganfsmyth at gmail.com>
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c222ea9
--- /dev/null
+++ b/README.md
@@ -0,0 +1,100 @@
+
+
+# Babel Legacy Decorator plugin
+
+This is a plugin for Babel 6 that is meant to replicate the old decorator behavior from
+Babel 5 in order to allow people to more easily transition to Babel 6 without needing to
+be blocked on updates to the decorator proposal or for Babel to re-implement it.
+
+## Why "legacy"?
+
+Decorators are still only a relatively new proposal, and they are (at least currently) still
+in flux. Many people have started to use them in their original form, where each decorator
+is essentially a function of the form
+
+ function(target, property, descriptor){}
+
+This form is very likely to change moving forward, and Babel 6 did not wish to support
+the older form when it was known that it would change in the future. As such, I created this
+plugin to help people transition to Babel 6 without requiring them to drop their decorators
+or requiring them to wait for the new proposal update and then update all their code.
+
+## Best Effort
+
+This plugin is a best effort to be compatible with Babel 5's transpiler output, but there
+are a few things that were difficult to reproduce, and a few things that were simply incorrect
+in Babel 5 with respect to the decorators proposal.
+
+Two main things to mention as differences, though not things you are likely to encounter:
+
+1. Decorators expressions are evaluated top to bottom, and executed bottom to top. e.g.
+
+ ```
+ function dec(id){
+ console.log('evaluated', id);
+ return (target, property, descriptor) => console.log('executed', id);
+ }
+
+ class Example {
+ @dec(1)
+ @dec(2)
+ method(){}
+ }
+ ```
+
+ In Babel 5, this would output:
+
+ ```
+ evaluated 2
+ evaluated 1
+ executed 2
+ executed 1
+ ```
+
+ With this plugin, it will result in:
+
+ ```
+ evaluated 1
+ evaluated 2
+ executed 2
+ executed 1
+ ```
+
+ which is what the spec dictates as the correct behavior and was incorrect in Babel 5.
+
+2. Static class property initializers are evaluated once up front.
+
+ If you decorate a static class property, you will get a descriptor with an `initializer` property.
+ However whereas with Babel 5 this could be re-executed multiple times with potentially differing
+ results, `decorators-legacy` will precompute the value and return an initializer that will
+ return that value. e.g.
+
+ ```
+ function dec(target, prop, descriptor){
+ let {initializer} = descriptor;
+ delete descriptor.initializer;
+ delete descriptor.writable;
+
+ descriptor.get = function(){
+ return initializer.call(this);
+ };
+ }
+
+ var i = 0;
+
+ class Example {
+ @dec
+ static prop = i++;
+ }
+ ```
+
+ In Babel 5, every access to `prop` would increment `i`.
+ In Babel 6, the very first value of `i` will be cached for future `initializer` calls.
+
+ The spec is a little vague around how initializers work for repeat calls, and I'd consider
+ calling an `initializer` multiple times to be a mistake in general, so hopefully this will
+ not cause anyone trouble.
+
+## License
+
+MIT (c) 2015
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..be169c6
--- /dev/null
+++ b/package.json
@@ -0,0 +1,40 @@
+{
+ "name": "babel-plugin-transform-decorators-legacy",
+ "description": "A plugin for Babel 6 that (mostly) replicates the old decorator behavior from Babel 5.",
+ "version": "1.3.4",
+ "author": "Logan Smyth <loganfsmyth at gmail.com>",
+ "main": "lib",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/loganfsmyth/babel-plugin-transform-decorators-legacy.git"
+ },
+ "bugs": {
+ "url": "https://github.com/loganfsmyth/babel-plugin-transform-decorators-legacy/issues"
+ },
+ "keywords": [
+ "babel",
+ "babel-plugin",
+ "es7",
+ "decorators"
+ ],
+ "devDependencies": {
+ "babel-cli": "^6.2.0",
+ "babel-plugin-transform-class-properties": "^6.3.0",
+ "babel-plugin-transform-runtime": "^6.1.18",
+ "babel-preset-es2015": "^6.1.18",
+ "chai": "^3.4.1",
+ "mocha": "^2.3.4"
+ },
+ "dependencies": {
+ "babel-plugin-syntax-decorators": "^6.1.18",
+ "babel-runtime": "^6.2.0",
+ "babel-template": "^6.3.0"
+ },
+ "scripts": {
+ "build": "babel src -d lib",
+ "watch": "babel src -d lib -w",
+ "prepublish": "npm run build",
+ "test": "babel-node node_modules/.bin/_mocha -- test"
+ }
+}
diff --git a/src/.babelrc b/src/.babelrc
new file mode 100644
index 0000000..ea6ea78
--- /dev/null
+++ b/src/.babelrc
@@ -0,0 +1,8 @@
+{
+ "presets": [
+ "es2015",
+ ],
+ "plugins": [
+ "transform-runtime",
+ ],
+}
diff --git a/src/index.js b/src/index.js
new file mode 100644
index 0000000..b63b3d1
--- /dev/null
+++ b/src/index.js
@@ -0,0 +1,324 @@
+import template from 'babel-template';
+
+const buildClassDecorator = template(`
+ DECORATOR(CLASS_REF = INNER) || CLASS_REF;
+`);
+
+const buildClassPrototype = template(`
+ CLASS_REF.prototype;
+`);
+
+const buildGetDescriptor = template(`
+ Object.getOwnPropertyDescriptor(TARGET, PROPERTY);
+`);
+
+
+const buildGetObjectInitializer = template(`
+ (TEMP = Object.getOwnPropertyDescriptor(TARGET, PROPERTY), (TEMP = TEMP ? TEMP.value : undefined), {
+ enumerable: true,
+ configurable: true,
+ writable: true,
+ initializer: function(){
+ return TEMP;
+ }
+ })
+`);
+
+const buildInitializerWarningHelper = template(`
+ function NAME(descriptor, context){
+ throw new Error('Decorating class property failed. Please ensure that transform-class-properties is enabled.');
+ }
+`);
+
+const buildInitializerDefineProperty = template(`
+ function NAME(target, property, descriptor, context){
+ if (!descriptor) return;
+
+ Object.defineProperty(target, property, {
+ enumerable: descriptor.enumerable,
+ configurable: descriptor.configurable,
+ writable: descriptor.writable,
+ value: descriptor.initializer ? descriptor.initializer.call(context) : void 0,
+ });
+ }
+`);
+
+const buildApplyDecoratedDescriptor = template(`
+ function NAME(target, property, decorators, descriptor, context){
+ var desc = {};
+ Object['ke' + 'ys'](descriptor).forEach(function(key){
+ desc[key] = descriptor[key];
+ });
+ desc.enumerable = !!desc.enumerable;
+ desc.configurable = !!desc.configurable;
+ if ('value' in desc || desc.initializer){
+ desc.writable = true;
+ }
+
+ desc = decorators.slice().reverse().reduce(function(desc, decorator){
+ return decorator(target, property, desc) || desc;
+ }, desc);
+
+ if (context && desc.initializer !== void 0){
+ desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
+ desc.initializer = undefined;
+ }
+
+ if (desc.initializer === void 0){
+ // This is a hack to avoid this being processed by 'transform-runtime'.
+ // See issue #9.
+ Object['define' + 'Property'](target, property, desc);
+ desc = null;
+ }
+
+ return desc;
+ }
+`);
+
+export default function({types: t}){
+ /**
+ * Add a helper to take an initial descriptor, apply some decorators to it, and optionally
+ * define the property.
+ */
+ function ensureApplyDecoratedDescriptorHelper(path, state){
+ if (!state.applyDecoratedDescriptor){
+ state.applyDecoratedDescriptor = path.scope.generateUidIdentifier('applyDecoratedDescriptor');
+ const helper = buildApplyDecoratedDescriptor({
+ NAME: state.applyDecoratedDescriptor,
+ });
+ path.scope.getProgramParent().path.unshiftContainer('body', helper);
+ }
+
+ return state.applyDecoratedDescriptor;
+ }
+
+ /**
+ * Add a helper to call as a replacement for class property definition.
+ */
+ function ensureInitializerDefineProp(path, state){
+ if (!state.initializerDefineProp){
+ state.initializerDefineProp = path.scope.generateUidIdentifier('initDefineProp');
+ const helper = buildInitializerDefineProperty({
+ NAME: state.initializerDefineProp,
+ })
+ path.scope.getProgramParent().path.unshiftContainer('body', helper);
+ }
+
+ return state.initializerDefineProp;
+ }
+
+ /**
+ * Add a helper that will throw a useful error if the transform fails to detect the class
+ * property assignment, so users know something failed.
+ */
+ function ensureInitializerWarning(path, state){
+ if (!state.initializerWarningHelper){
+ state.initializerWarningHelper = path.scope.generateUidIdentifier('initializerWarningHelper');
+ const helper = buildInitializerWarningHelper({
+ NAME: state.initializerWarningHelper,
+ })
+ path.scope.getProgramParent().path.unshiftContainer('body', helper);
+ }
+
+ return state.initializerWarningHelper;
+ }
+
+ /**
+ * If the decorator expressions are non-identifiers, hoist them to before the class so we can be sure
+ * that they are evaluated in order.
+ */
+ function applyEnsureOrdering(path){
+ // TODO: This should probably also hoist computed properties.
+ const decorators = (path.isClass() ? [path].concat(path.get('body.body')) : path.get('properties'))
+ .reduce((acc, prop) => acc.concat(prop.node.decorators || []), []);
+
+ const identDecorators = decorators.filter(decorator => !t.isIdentifier(decorator.expression));
+ if (identDecorators.length === 0) return;
+
+ return t.sequenceExpression(identDecorators.map(decorator => {
+ const expression = decorator.expression;
+ const id = decorator.expression = path.scope.generateDeclaredUidIdentifier('dec');
+ return t.assignmentExpression('=', id, expression);
+ }).concat([path.node]));
+ }
+
+ /**
+ * Given a class expression with class-level decorators, create a new expression
+ * with the proper decorated behavior.
+ */
+ function applyClassDecorators(classPath, state){
+ const decorators = classPath.node.decorators || [];
+ classPath.node.decorators = null;
+
+ if (decorators.length === 0) return;
+
+ const name = classPath.scope.generateDeclaredUidIdentifier('class');
+
+ return decorators
+ .map(dec => dec.expression)
+ .reverse()
+ .reduce(function(acc, decorator){
+ return buildClassDecorator({
+ CLASS_REF: name,
+ DECORATOR: decorator,
+ INNER: acc,
+ }).expression;
+ }, classPath.node);
+ }
+
+ /**
+ * Given a class expression with method-level decorators, create a new expression
+ * with the proper decorated behavior.
+ */
+ function applyMethodDecorators(path, state){
+ const hasMethodDecorators = path.node.body.body.some(function(node){
+ return (node.decorators || []).length > 0;
+ });
+
+ if (!hasMethodDecorators) return;
+
+ return applyTargetDecorators(path, state, path.node.body.body);
+ }
+
+ /**
+ * Given an object expression with property decorators, create a new expression
+ * with the proper decorated behavior.
+ */
+ function applyObjectDecorators(path, state){
+ const hasMethodDecorators = path.node.properties.some(function(node){
+ return (node.decorators || []).length > 0;
+ });
+
+ if (!hasMethodDecorators) return;
+
+ return applyTargetDecorators(path, state, path.node.properties);
+ }
+
+ /**
+ * A helper to pull out property decorators into a sequence expression.
+ */
+ function applyTargetDecorators(path, state, decoratedProps){
+ const descName = path.scope.generateDeclaredUidIdentifier('desc');
+ const valueTemp = path.scope.generateDeclaredUidIdentifier('value');
+
+ const name = path.scope.generateDeclaredUidIdentifier(path.isClass() ? 'class' : 'obj');
+
+ const exprs = decoratedProps.reduce(function(acc, node){
+ const decorators = node.decorators || [];
+ node.decorators = null;
+
+ if (decorators.length === 0) return acc;
+
+ if (node.computed){
+ throw path.buildCodeFrameError('Computed method/property decorators are not yet supported.')
+ }
+
+ const property = t.isLiteral(node.key) ? node.key : t.stringLiteral(node.key.name);
+
+ const target = (path.isClass() && !node.static) ? buildClassPrototype({
+ CLASS_REF: name,
+ }).expression : name;
+
+ if (t.isClassProperty(node, {static: false})){
+ let descriptor = path.scope.generateDeclaredUidIdentifier('descriptor');
+
+ const initializer = node.value ?
+ t.functionExpression(null, [], t.blockStatement([t.returnStatement(node.value)])) :
+ t.nullLiteral();
+ node.value = t.callExpression(ensureInitializerWarning(path, state), [descriptor, t.thisExpression()]);
+
+ acc = acc.concat([
+ t.assignmentExpression('=', descriptor, t.callExpression(ensureApplyDecoratedDescriptorHelper(path, state), [
+ target,
+ property,
+ t.arrayExpression(decorators.map(dec => dec.expression)),
+ t.objectExpression([
+ t.objectProperty(t.identifier('enumerable'), t.booleanLiteral(true)),
+ t.objectProperty(t.identifier('initializer'), initializer),
+ ]),
+ ])),
+ ]);
+ } else {
+ acc = acc.concat(
+ t.callExpression(ensureApplyDecoratedDescriptorHelper(path, state), [
+ target,
+ property,
+ t.arrayExpression(decorators.map(dec => dec.expression)),
+ (t.isObjectProperty(node) || t.isClassProperty(node, {static: true})) ? buildGetObjectInitializer({
+ TEMP: path.scope.generateDeclaredUidIdentifier('init'),
+ TARGET: target,
+ PROPERTY: property,
+ }).expression : buildGetDescriptor({
+ TARGET: target,
+ PROPERTY: property,
+ }).expression,
+ target,
+ ])
+ );
+ }
+
+ return acc;
+ }, []);
+
+ return t.sequenceExpression([
+ t.assignmentExpression('=', name, path.node),
+ t.sequenceExpression(exprs),
+ name,
+ ]);
+ }
+
+ return {
+ inherits: require("babel-plugin-syntax-decorators"),
+
+ visitor: {
+ ExportDefaultDeclaration(path){
+ if (!path.get("declaration").isClassDeclaration()) return;
+
+ const {node} = path;
+ const ref = node.declaration.id || path.scope.generateUidIdentifier("default");
+ node.declaration.id = ref;
+
+ // Split the class declaration and the export into two separate statements.
+ path.replaceWith(node.declaration);
+ path.insertAfter(t.exportNamedDeclaration(null, [t.exportSpecifier(ref, t.identifier('default'))]));
+ },
+ ClassDeclaration(path){
+ const {node} = path;
+
+ const ref = node.id || path.scope.generateUidIdentifier("class");
+
+ path.replaceWith(t.variableDeclaration("let", [
+ t.variableDeclarator(ref, t.toExpression(node))
+ ]));
+ },
+ ClassExpression(path, state){
+ // Create a replacement for the class node if there is one. We do one pass to replace classes with
+ // class decorators, and a second pass to process method decorators.
+ const decoratedClass = applyEnsureOrdering(path) || applyClassDecorators(path, state) || applyMethodDecorators(path, state);
+
+ if (decoratedClass) path.replaceWith(decoratedClass);
+ },
+ ObjectExpression(path, state){
+ const decoratedObject = applyEnsureOrdering(path) || applyObjectDecorators(path, state);
+
+ if (decoratedObject) path.replaceWith(decoratedObject);
+ },
+
+ AssignmentExpression(path, state){
+ if (!state.initializerWarningHelper) return;
+
+ if (!path.get('left').isMemberExpression()) return;
+ if (!path.get('left.property').isIdentifier()) return;
+ if (!path.get('right').isCallExpression()) return;
+ if (!path.get('right.callee').isIdentifier({name: state.initializerWarningHelper.name})) return;
+
+ path.replaceWith(t.callExpression(ensureInitializerDefineProp(path, state), [
+ path.get('left.object').node,
+ t.stringLiteral(path.get('left.property').node.name),
+ path.get('right.arguments')[0].node,
+ path.get('right.arguments')[1].node,
+ ]));
+ },
+ }
+ };
+};
diff --git a/test/.babelrc b/test/.babelrc
new file mode 100644
index 0000000..2bebe40
--- /dev/null
+++ b/test/.babelrc
@@ -0,0 +1,9 @@
+{
+ "presets": [
+ "es2015",
+ ],
+ "plugins": [
+ "../",
+ "transform-class-properties",
+ ],
+}
diff --git a/test/index.js b/test/index.js
new file mode 100644
index 0000000..0ff3779
--- /dev/null
+++ b/test/index.js
@@ -0,0 +1,1723 @@
+import {expect} from 'chai';
+
+describe('decorators', function(){
+ describe('class', function(){
+ describe('ordering', function(){
+ it('should evaluate descriptor expressions in order', function(){
+ const calls = [];
+ function dec(id){
+ calls.push(id);
+ return function(){};
+ }
+
+ @dec(1)
+ @dec(2)
+ class Example {
+ @dec(3)
+ @dec(4)
+ method1(){}
+
+ @dec(5)
+ @dec(6)
+ prop1 = 1;
+
+ @dec(7)
+ @dec(8)
+ method2(){}
+
+ @dec(9)
+ @dec(10)
+ prop2 = 2;
+ }
+
+ expect(calls).to.eql([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
+ });
+
+ it('should call decorators in reverse order per-method', function(){
+ const calls = [];
+ function dec(id){
+ return function(){
+ calls.push(id);
+ };
+ }
+
+ @dec(10)
+ @dec(9)
+ class Example {
+ @dec(2)
+ @dec(1)
+ method1(){}
+
+ @dec(4)
+ @dec(3)
+ prop1 = 1;
+
+ @dec(6)
+ @dec(5)
+ method2(){}
+
+ @dec(8)
+ @dec(7)
+ prop2 = 2;
+ }
+
+ expect(calls).to.eql([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
+ });
+ });
+
+ describe('constructors', function(){
+ it('should allow returning a new constructor', function(){
+ function dec(cls){
+ return class Child extends cls {
+ child(){}
+ };
+ }
+
+ @dec
+ class Parent {
+ parent(){}
+ }
+
+ expect(Parent.prototype.parent).to.be.a('function');
+ expect(Parent.prototype.child).to.be.a('function');
+ });
+
+ it('should allow mutating the existing constructor', function(){
+ function dec(cls){
+ cls.staticProp = 'prop';
+ }
+
+ @dec
+ class Parent {
+ parent(){}
+ }
+
+ expect(Parent.staticProp).to.eql('prop');
+ });
+ });
+
+ describe('prototype methods', function(){
+ it('should support numeric props', function(){
+ function dec(target, name, descriptor){
+ expect(target).to.be.ok;
+ expect(name).to.eql(4);
+ expect(descriptor).to.be.an('object');
+ }
+
+ class Example {
+ @dec
+ 4(){
+
+ }
+ }
+ });
+
+ it('should support string props', function(){
+ function dec(target, name, descriptor){
+ expect(target).to.be.ok;
+ expect(name).to.eql("str");
+ expect(descriptor).to.be.an('object');
+ }
+
+ class Example {
+ @dec
+ "str"(){
+
+ }
+ }
+ });
+
+ it('should allow returning a descriptor', function(){
+ function dec(target, name, descriptor){
+ expect(target).to.be.ok;
+ expect(name).to.be.a('string');
+ expect(descriptor).to.be.an('object');
+
+ target.decoratedProps = (target.decoratedProps || []).concat([name]);
+
+ let value = descriptor.value;
+ return {
+ enumerable: name.indexOf('enum') !== -1,
+ configurable: name.indexOf('conf') !== -1,
+ writable: name.indexOf('write') !== -1,
+ value: function(...args){
+ return '__' + value.apply(this, args) + '__';
+ },
+ };
+ }
+
+ class Example {
+ @dec
+ enumconfwrite(){
+ return 1;
+ }
+
+ @dec
+ enumconf(){
+ return 2;
+ }
+
+ @dec
+ enumwrite(){
+ return 3;
+ }
+
+ @dec
+ enum(){
+ return 4;
+ }
+
+ @dec
+ confwrite(){
+ return 5;
+ }
+
+ @dec
+ conf(){
+ return 6;
+ }
+
+ @dec
+ write(){
+ return 7;
+ }
+
+ @dec
+ _(){
+ return 8;
+ }
+ }
+
+ expect(Example.prototype).to.have.ownProperty('decoratedProps');
+ expect(Example.prototype.decoratedProps).to.eql([
+ "enumconfwrite",
+ "enumconf",
+ "enumwrite",
+ "enum",
+ "confwrite",
+ "conf",
+ "write",
+ "_",
+ ]);
+
+ const inst = new Example();
+
+ const descs = Object.getOwnPropertyDescriptors(Example.prototype);
+ expect(descs.enumconfwrite.enumerable).to.be.true;
+ expect(descs.enumconfwrite.writable).to.be.true;
+ expect(descs.enumconfwrite.configurable).to.be.true;
+ expect(inst.enumconfwrite()).to.eql('__1__');
+
+ expect(descs.enumconf.enumerable).to.be.true;
+ expect(descs.enumconf.writable).to.be.false;
+ expect(descs.enumconf.configurable).to.be.true;
+ expect(inst.enumconf()).to.eql('__2__');
+
+ expect(descs.enumwrite.enumerable).to.be.true;
+ expect(descs.enumwrite.writable).to.be.true;
+ expect(descs.enumwrite.configurable).to.be.false;
+ expect(inst.enumwrite()).to.eql('__3__');
+
+ expect(descs.enum.enumerable).to.be.true;
+ expect(descs.enum.writable).to.be.false;
+ expect(descs.enum.configurable).to.be.false;
+ expect(inst.enum()).to.eql('__4__');
+
+ expect(descs.confwrite.enumerable).to.be.false;
+ expect(descs.confwrite.writable).to.be.true;
+ expect(descs.confwrite.configurable).to.be.true;
+ expect(inst.confwrite()).to.eql('__5__');
+
+ expect(descs.conf.enumerable).to.be.false;
+ expect(descs.conf.writable).to.be.false;
+ expect(descs.conf.configurable).to.be.true;
+ expect(inst.conf()).to.eql('__6__');
+
+ expect(descs.write.enumerable).to.be.false;
+ expect(descs.write.writable).to.be.true;
+ expect(descs.write.configurable).to.be.false;
+ expect(inst.write()).to.eql('__7__');
+
+ expect(descs._.enumerable).to.be.false;
+ expect(descs._.writable).to.be.false;
+ expect(descs._.configurable).to.be.false;
+ expect(inst._()).to.eql('__8__');
+ });
+
+ it('should allow mutating the original descriptor', function(){
+ function dec(target, name, descriptor){
+ expect(target).to.be.ok;
+ expect(name).to.be.a('string');
+ expect(descriptor).to.be.an('object');
+
+ target.decoratedProps = (target.decoratedProps || []).concat([name]);
+
+ let value = descriptor.value;
+ Object.assign(descriptor, {
+ enumerable: name.indexOf('enum') !== -1,
+ configurable: name.indexOf('conf') !== -1,
+ writable: name.indexOf('write') !== -1,
+ value: function(...args){
+ return '__' + value.apply(this, args) + '__';
+ },
+ });
+ }
+
+ class Example {
+ @dec
+ enumconfwrite(){
+ return 1;
+ }
+
+ @dec
+ enumconf(){
+ return 2;
+ }
+
+ @dec
+ enumwrite(){
+ return 3;
+ }
+
+ @dec
+ enum(){
+ return 4;
+ }
+
+ @dec
+ confwrite(){
+ return 5;
+ }
+
+ @dec
+ conf(){
+ return 6;
+ }
+
+ @dec
+ write(){
+ return 7;
+ }
+
+ @dec
+ _(){
+ return 8;
+ }
+ }
+
+ expect(Example.prototype).to.have.ownProperty('decoratedProps');
+ expect(Example.prototype.decoratedProps).to.eql([
+ "enumconfwrite",
+ "enumconf",
+ "enumwrite",
+ "enum",
+ "confwrite",
+ "conf",
+ "write",
+ "_",
+ ]);
+
+ const inst = new Example();
+
+ const descs = Object.getOwnPropertyDescriptors(Example.prototype);
+ expect(descs.enumconfwrite.enumerable).to.be.true;
+ expect(descs.enumconfwrite.writable).to.be.true;
+ expect(descs.enumconfwrite.configurable).to.be.true;
+ expect(inst.enumconfwrite()).to.eql('__1__');
+
+ expect(descs.enumconf.enumerable).to.be.true;
+ expect(descs.enumconf.writable).to.be.false;
+ expect(descs.enumconf.configurable).to.be.true;
+ expect(inst.enumconf()).to.eql('__2__');
+
+ expect(descs.enumwrite.enumerable).to.be.true;
+ expect(descs.enumwrite.writable).to.be.true;
+ expect(descs.enumwrite.configurable).to.be.false;
+ expect(inst.enumwrite()).to.eql('__3__');
+
+ expect(descs.enum.enumerable).to.be.true;
+ expect(descs.enum.writable).to.be.false;
+ expect(descs.enum.configurable).to.be.false;
+ expect(inst.enum()).to.eql('__4__');
+
+ expect(descs.confwrite.enumerable).to.be.false;
+ expect(descs.confwrite.writable).to.be.true;
+ expect(descs.confwrite.configurable).to.be.true;
+ expect(inst.confwrite()).to.eql('__5__');
+
+ expect(descs.conf.enumerable).to.be.false;
+ expect(descs.conf.writable).to.be.false;
+ expect(descs.conf.configurable).to.be.true;
+ expect(inst.conf()).to.eql('__6__');
+
+ expect(descs.write.enumerable).to.be.false;
+ expect(descs.write.writable).to.be.true;
+ expect(descs.write.configurable).to.be.false;
+ expect(inst.write()).to.eql('__7__');
+
+ expect(descs._.enumerable).to.be.false;
+ expect(descs._.writable).to.be.false;
+ expect(descs._.configurable).to.be.false;
+ expect(inst._()).to.eql('__8__');
+ });
+ });
+
+ describe('static methods', function(){
+ it('should support numeric props', function(){
+ function dec(target, name, descriptor){
+ expect(target).to.be.ok;
+ expect(name).to.eql(4);
+ expect(descriptor).to.be.an('object');
+ }
+
+ class Example {
+ @dec
+ static 4(){
+
+ }
+ }
+ });
+
+ it('should support string props', function(){
+ function dec(target, name, descriptor){
+ expect(target).to.be.ok;
+ expect(name).to.eql("str");
+ expect(descriptor).to.be.an('object');
+ }
+
+ class Example {
+ @dec
+ static "str"(){
+
+ }
+ }
+ });
+
+ it('should allow returning a descriptor', function(){
+ function dec(target, name, descriptor){
+ expect(target).to.be.ok;
+ expect(name).to.be.a('string');
+ expect(descriptor).to.be.an('object');
+
+ target.decoratedProps = (target.decoratedProps || []).concat([name]);
+
+ let value = descriptor.value;
+ return {
+ enumerable: name.indexOf('enum') !== -1,
+ configurable: name.indexOf('conf') !== -1,
+ writable: name.indexOf('write') !== -1,
+ value: function(...args){
+ return '__' + value.apply(this, args) + '__';
+ },
+ };
+ }
+
+ class Example {
+ @dec
+ static enumconfwrite(){
+ return 1;
+ }
+
+ @dec
+ static enumconf(){
+ return 2;
+ }
+
+ @dec
+ static enumwrite(){
+ return 3;
+ }
+
+ @dec
+ static enum(){
+ return 4;
+ }
+
+ @dec
+ static confwrite(){
+ return 5;
+ }
+
+ @dec
+ static conf(){
+ return 6;
+ }
+
+ @dec
+ static write(){
+ return 7;
+ }
+
+ @dec
+ static _(){
+ return 8;
+ }
+ }
+
+ expect(Example).to.have.ownProperty('decoratedProps');
+ expect(Example.decoratedProps).to.eql([
+ "enumconfwrite",
+ "enumconf",
+ "enumwrite",
+ "enum",
+ "confwrite",
+ "conf",
+ "write",
+ "_",
+ ]);
+
+ const descs = Object.getOwnPropertyDescriptors(Example);
+ expect(descs.enumconfwrite.enumerable).to.be.true;
+ expect(descs.enumconfwrite.writable).to.be.true;
+ expect(descs.enumconfwrite.configurable).to.be.true;
+ expect(Example.enumconfwrite()).to.eql('__1__');
+
+ expect(descs.enumconf.enumerable).to.be.true;
+ expect(descs.enumconf.writable).to.be.false;
+ expect(descs.enumconf.configurable).to.be.true;
+ expect(Example.enumconf()).to.eql('__2__');
+
+ expect(descs.enumwrite.enumerable).to.be.true;
+ expect(descs.enumwrite.writable).to.be.true;
+ expect(descs.enumwrite.configurable).to.be.false;
+ expect(Example.enumwrite()).to.eql('__3__');
+
+ expect(descs.enum.enumerable).to.be.true;
+ expect(descs.enum.writable).to.be.false;
+ expect(descs.enum.configurable).to.be.false;
+ expect(Example.enum()).to.eql('__4__');
+
+ expect(descs.confwrite.enumerable).to.be.false;
+ expect(descs.confwrite.writable).to.be.true;
+ expect(descs.confwrite.configurable).to.be.true;
+ expect(Example.confwrite()).to.eql('__5__');
+
+ expect(descs.conf.enumerable).to.be.false;
+ expect(descs.conf.writable).to.be.false;
+ expect(descs.conf.configurable).to.be.true;
+ expect(Example.conf()).to.eql('__6__');
+
+ expect(descs.write.enumerable).to.be.false;
+ expect(descs.write.writable).to.be.true;
+ expect(descs.write.configurable).to.be.false;
+ expect(Example.write()).to.eql('__7__');
+
+ expect(descs._.enumerable).to.be.false;
+ expect(descs._.writable).to.be.false;
+ expect(descs._.configurable).to.be.false;
+ expect(Example._()).to.eql('__8__');
+ });
+
+ it('should allow mutating the original descriptor', function(){
+ function dec(target, name, descriptor){
+ expect(target).to.be.ok;
+ expect(name).to.be.a('string');
+ expect(descriptor).to.be.an('object');
+
+ target.decoratedProps = (target.decoratedProps || []).concat([name]);
+
+ let value = descriptor.value;
+ Object.assign(descriptor, {
+ enumerable: name.indexOf('enum') !== -1,
+ configurable: name.indexOf('conf') !== -1,
+ writable: name.indexOf('write') !== -1,
+ value: function(...args){
+ return '__' + value.apply(this, args) + '__';
+ },
+ });
+ }
+
+ class Example {
+ @dec
+ static enumconfwrite(){
+ return 1;
+ }
+
+ @dec
+ static enumconf(){
+ return 2;
+ }
+
+ @dec
+ static enumwrite(){
+ return 3;
+ }
+
+ @dec
+ static enum(){
+ return 4;
+ }
+
+ @dec
+ static confwrite(){
+ return 5;
+ }
+
+ @dec
+ static conf(){
+ return 6;
+ }
+
+ @dec
+ static write(){
+ return 7;
+ }
+
+ @dec
+ static _(){
+ return 8;
+ }
+ }
+
+ expect(Example).to.have.ownProperty('decoratedProps');
+ expect(Example.decoratedProps).to.eql([
+ "enumconfwrite",
+ "enumconf",
+ "enumwrite",
+ "enum",
+ "confwrite",
+ "conf",
+ "write",
+ "_",
+ ]);
+
+ const descs = Object.getOwnPropertyDescriptors(Example);
+ expect(descs.enumconfwrite.enumerable).to.be.true;
+ expect(descs.enumconfwrite.writable).to.be.true;
+ expect(descs.enumconfwrite.configurable).to.be.true;
+ expect(Example.enumconfwrite()).to.eql('__1__');
+
+ expect(descs.enumconf.enumerable).to.be.true;
+ expect(descs.enumconf.writable).to.be.false;
+ expect(descs.enumconf.configurable).to.be.true;
+ expect(Example.enumconf()).to.eql('__2__');
+
+ expect(descs.enumwrite.enumerable).to.be.true;
+ expect(descs.enumwrite.writable).to.be.true;
+ expect(descs.enumwrite.configurable).to.be.false;
+ expect(Example.enumwrite()).to.eql('__3__');
+
+ expect(descs.enum.enumerable).to.be.true;
+ expect(descs.enum.writable).to.be.false;
+ expect(descs.enum.configurable).to.be.false;
+ expect(Example.enum()).to.eql('__4__');
+
+ expect(descs.confwrite.enumerable).to.be.false;
+ expect(descs.confwrite.writable).to.be.true;
+ expect(descs.confwrite.configurable).to.be.true;
+ expect(Example.confwrite()).to.eql('__5__');
+
+ expect(descs.conf.enumerable).to.be.false;
+ expect(descs.conf.writable).to.be.false;
+ expect(descs.conf.configurable).to.be.true;
+ expect(Example.conf()).to.eql('__6__');
+
+ expect(descs.write.enumerable).to.be.false;
+ expect(descs.write.writable).to.be.true;
+ expect(descs.write.configurable).to.be.false;
+ expect(Example.write()).to.eql('__7__');
+
+ expect(descs._.enumerable).to.be.false;
+ expect(descs._.writable).to.be.false;
+ expect(descs._.configurable).to.be.false;
+ expect(Example._()).to.eql('__8__');
+ });
+ });
+
+ describe('prototype properties', function(){
+ it('should support decorating properties that have no initializer', function(){
+ function dec(target, name, descriptor){
+
+ }
+
+ class Example {
+ @dec prop;
+ }
+
+ let inst = new Example();
+ expect(inst).to.have.ownProperty('prop');
+ expect(inst.prop).to.be.undefined;
+ });
+
+ it('should support mutating an initialzer into an accessor', function(){
+ function dec(target, name, descriptor){
+ expect(target).to.be.ok;
+ expect(name).to.eql("prop");
+ expect(descriptor).to.be.an('object');
+
+ let {initializer} = descriptor;
+ delete descriptor.initializer;
+ delete descriptor.writable;
+
+ let value;
+ descriptor.get = function(){
+ if (initializer){
+ value = '__' + initializer.call(this) + '__';
+ initializer = null;
+ }
+ return value;
+ };
+ }
+
+ class Example {
+ @dec
+ prop = 3;
+ }
+
+ let inst = new Example();
+
+ expect(inst.prop).to.eql('__3__');
+ });
+
+ it('should support properties on child classes', function(){
+ function dec(target, name, descriptor){
+ expect(target).to.be.ok;
+ expect(name).to.be.a('string');
+ expect(descriptor).to.be.an('object');
+
+ target.decoratedProps = (target.decoratedProps || []).concat([name]);
+
+ let initializer = descriptor.initializer;
+ descriptor.initializer = function(...args){
+ return '__' + initializer.apply(this, args) + '__';
+ };
+ }
+
+ class Base {
+ @dec
+ prop2 = 4;
+ }
+
+ class Example extends Base {
+ @dec
+ prop = 3;
+ }
+
+ let inst = new Example();
+
+ expect(inst.prop).to.eql('__3__');
+ expect(inst.prop2).to.eql('__4__');
+ });
+
+ it('should allow returning a descriptor', function(){
+ function dec(target, name, descriptor){
+ expect(target).to.be.ok;
+ expect(name).to.be.a('string');
+ expect(descriptor).to.be.an('object');
+
+ target.decoratedProps = (target.decoratedProps || []).concat([name]);
+
+ let initializer = descriptor.initializer;
+ return {
+ enumerable: name.indexOf('enum') !== -1,
+ configurable: name.indexOf('conf') !== -1,
+ writable: name.indexOf('write') !== -1,
+ initializer: function(...args){
+ return '__' + initializer.apply(this, args) + '__';
+ },
+ };
+ }
+
+ class Example {
+ @dec
+ enumconfwrite = 1;
+
+ @dec
+ enumconf = 2;
+
+ @dec
+ enumwrite = 3;
+
+ @dec
+ enum = 4;
+
+ @dec
+ confwrite = 5;
+
+ @dec
+ conf = 6;
+
+ @dec
+ write = 7;
+
+ @dec
+ _ = 8;
+ }
+ const inst = new Example();
+
+ expect(Example.prototype).to.have.ownProperty('decoratedProps');
+ expect(inst.decoratedProps).to.eql([
+ "enumconfwrite",
+ "enumconf",
+ "enumwrite",
+ "enum",
+ "confwrite",
+ "conf",
+ "write",
+ "_",
+ ]);
+
+ const descs = Object.getOwnPropertyDescriptors(inst);
+ expect(descs.enumconfwrite.enumerable).to.be.true;
+ expect(descs.enumconfwrite.writable).to.be.true;
+ expect(descs.enumconfwrite.configurable).to.be.true;
+ expect(inst.enumconfwrite).to.eql('__1__');
+
+ expect(descs.enumconf.enumerable).to.be.true;
+ expect(descs.enumconf.writable).to.be.false;
+ expect(descs.enumconf.configurable).to.be.true;
+ expect(inst.enumconf).to.eql('__2__');
+
+ expect(descs.enumwrite.enumerable).to.be.true;
+ expect(descs.enumwrite.writable).to.be.true;
+ expect(descs.enumwrite.configurable).to.be.false;
+ expect(inst.enumwrite).to.eql('__3__');
+
+ expect(descs.enum.enumerable).to.be.true;
+ expect(descs.enum.writable).to.be.false;
+ expect(descs.enum.configurable).to.be.false;
+ expect(inst.enum).to.eql('__4__');
+
+ expect(descs.confwrite.enumerable).to.be.false;
+ expect(descs.confwrite.writable).to.be.true;
+ expect(descs.confwrite.configurable).to.be.true;
+ expect(inst.confwrite).to.eql('__5__');
+
+ expect(descs.conf.enumerable).to.be.false;
+ expect(descs.conf.writable).to.be.false;
+ expect(descs.conf.configurable).to.be.true;
+ expect(inst.conf).to.eql('__6__');
+
+ expect(descs.write.enumerable).to.be.false;
+ expect(descs.write.writable).to.be.true;
+ expect(descs.write.configurable).to.be.false;
+ expect(inst.write).to.eql('__7__');
+
+ expect(descs._.enumerable).to.be.false;
+ expect(descs._.writable).to.be.false;
+ expect(descs._.configurable).to.be.false;
+ expect(inst._).to.eql('__8__');
+ });
+
+ it('should allow mutating the original descriptor', function(){
+ function dec(target, name, descriptor){
+ expect(target).to.be.ok;
+ expect(name).to.be.a('string');
+ expect(descriptor).to.be.an('object');
+
+ target.decoratedProps = (target.decoratedProps || []).concat([name]);
+
+ let initializer = descriptor.initializer;
+ Object.assign(descriptor, {
+ enumerable: name.indexOf('enum') !== -1,
+ configurable: name.indexOf('conf') !== -1,
+ writable: name.indexOf('write') !== -1,
+ initializer: function(...args){
+ return '__' + initializer.apply(this, args) + '__';
+ },
+ });
+ }
+
+ class Example {
+ @dec
+ enumconfwrite = 1;
+
+ @dec
+ enumconf = 2;
+
+ @dec
+ enumwrite = 3;
+
+ @dec
+ enum = 4;
+
+ @dec
+ confwrite = 5;
+
+ @dec
+ conf = 6;
+
+ @dec
+ write = 7;
+
+ @dec
+ _ = 8;
+ }
+ const inst = new Example();
+
+ expect(Example.prototype).to.have.ownProperty('decoratedProps');
+ expect(inst.decoratedProps).to.eql([
+ "enumconfwrite",
+ "enumconf",
+ "enumwrite",
+ "enum",
+ "confwrite",
+ "conf",
+ "write",
+ "_",
+ ]);
+
+ const descs = Object.getOwnPropertyDescriptors(inst);
+ expect(descs.enumconfwrite.enumerable).to.be.true;
+ expect(descs.enumconfwrite.writable).to.be.true;
+ expect(descs.enumconfwrite.configurable).to.be.true;
+ expect(inst.enumconfwrite).to.eql('__1__');
+
+ expect(descs.enumconf.enumerable).to.be.true;
+ expect(descs.enumconf.writable).to.be.false;
+ expect(descs.enumconf.configurable).to.be.true;
+ expect(inst.enumconf).to.eql('__2__');
+
+ expect(descs.enumwrite.enumerable).to.be.true;
+ expect(descs.enumwrite.writable).to.be.true;
+ expect(descs.enumwrite.configurable).to.be.false;
+ expect(inst.enumwrite).to.eql('__3__');
+
+ expect(descs.enum.enumerable).to.be.true;
+ expect(descs.enum.writable).to.be.false;
+ expect(descs.enum.configurable).to.be.false;
+ expect(inst.enum).to.eql('__4__');
+
+ expect(descs.confwrite.enumerable).to.be.false;
+ expect(descs.confwrite.writable).to.be.true;
+ expect(descs.confwrite.configurable).to.be.true;
+ expect(inst.confwrite).to.eql('__5__');
+
+ expect(descs.conf.enumerable).to.be.false;
+ expect(descs.conf.writable).to.be.false;
+ expect(descs.conf.configurable).to.be.true;
+ expect(inst.conf).to.eql('__6__');
+
+ expect(descs.write.enumerable).to.be.false;
+ expect(descs.write.writable).to.be.true;
+ expect(descs.write.configurable).to.be.false;
+ expect(inst.write).to.eql('__7__');
+
+ expect(descs._.enumerable).to.be.false;
+ expect(descs._.writable).to.be.false;
+ expect(descs._.configurable).to.be.false;
+ expect(inst._).to.eql('__8__');
+ });
+ });
+
+ describe('static properties', function(){
+ it('should support decorating properties that have no initializer', function(){
+ function dec(target, name, descriptor){
+
+ }
+
+ class Example {
+ @dec static prop;
+ }
+
+ expect(Example).to.have.ownProperty('prop');
+ expect(Example.prop).to.be.undefined;
+ });
+
+ it('should support mutating an initialzer into an accessor', function(){
+ function dec(target, name, descriptor){
+ expect(target).to.be.ok;
+ expect(name).to.eql("prop");
+ expect(descriptor).to.be.an('object');
+
+ let {initializer} = descriptor;
+ delete descriptor.initializer;
+ delete descriptor.writable;
+
+ let value;
+ descriptor.get = function(){
+ if (initializer){
+ value = '__' + initializer.call(this) + '__';
+ initializer = null;
+ }
+ return value;
+ };
+ }
+
+ class Example {
+ @dec
+ static prop = 3;
+ }
+
+ expect(Example.prop).to.eql('__3__');
+ });
+
+ it('should allow returning a descriptor', function(){
+ function dec(target, name, descriptor){
+ expect(target).to.be.ok;
+ expect(name).to.be.a('string');
+ expect(descriptor).to.be.an('object');
+
+ target.decoratedProps = (target.decoratedProps || []).concat([name]);
+
+ let initializer = descriptor.initializer;
+ return {
+ enumerable: name.indexOf('enum') !== -1,
+ configurable: name.indexOf('conf') !== -1,
+ writable: name.indexOf('write') !== -1,
+ initializer: function(...args){
+ return '__' + initializer.apply(this, args) + '__';
+ },
+ };
+ }
+
+ class Example {
+ @dec
+ static enumconfwrite = 1;
+
+ @dec
+ static enumconf = 2;
+
+ @dec
+ static enumwrite = 3;
+
+ @dec
+ static enum = 4;
+
+ @dec
+ static confwrite = 5;
+
+ @dec
+ static conf = 6;
+
+ @dec
+ static write = 7;
+
+ @dec
+ static _ = 8;
+ }
+ const inst = new Example();
+
+ expect(Example).to.have.ownProperty('decoratedProps');
+ expect(Example.decoratedProps).to.eql([
+ "enumconfwrite",
+ "enumconf",
+ "enumwrite",
+ "enum",
+ "confwrite",
+ "conf",
+ "write",
+ "_",
+ ]);
+
+ const descs = Object.getOwnPropertyDescriptors(Example);
+ expect(descs.enumconfwrite.enumerable).to.be.true;
+ expect(descs.enumconfwrite.writable).to.be.true;
+ expect(descs.enumconfwrite.configurable).to.be.true;
+ expect(Example.enumconfwrite).to.eql('__1__');
+
+ expect(descs.enumconf.enumerable).to.be.true;
+ expect(descs.enumconf.writable).to.be.false;
+ expect(descs.enumconf.configurable).to.be.true;
+ expect(Example.enumconf).to.eql('__2__');
+
+ expect(descs.enumwrite.enumerable).to.be.true;
+ expect(descs.enumwrite.writable).to.be.true;
+ expect(descs.enumwrite.configurable).to.be.false;
+ expect(Example.enumwrite).to.eql('__3__');
+
+ expect(descs.enum.enumerable).to.be.true;
+ expect(descs.enum.writable).to.be.false;
+ expect(descs.enum.configurable).to.be.false;
+ expect(Example.enum).to.eql('__4__');
+
+ expect(descs.confwrite.enumerable).to.be.false;
+ expect(descs.confwrite.writable).to.be.true;
+ expect(descs.confwrite.configurable).to.be.true;
+ expect(Example.confwrite).to.eql('__5__');
+
+ expect(descs.conf.enumerable).to.be.false;
+ expect(descs.conf.writable).to.be.false;
+ expect(descs.conf.configurable).to.be.true;
+ expect(Example.conf).to.eql('__6__');
+
+ expect(descs.write.enumerable).to.be.false;
+ expect(descs.write.writable).to.be.true;
+ expect(descs.write.configurable).to.be.false;
+ expect(Example.write).to.eql('__7__');
+
+ expect(descs._.enumerable).to.be.false;
+ expect(descs._.writable).to.be.false;
+ expect(descs._.configurable).to.be.false;
+ expect(Example._).to.eql('__8__');
+ });
+
+ it('should allow mutating the original descriptor', function(){
+ function dec(target, name, descriptor){
+ expect(target).to.be.ok;
+ expect(name).to.be.a('string');
+ expect(descriptor).to.be.an('object');
+
+ target.decoratedProps = (target.decoratedProps || []).concat([name]);
+
+ let initializer = descriptor.initializer;
+ Object.assign(descriptor, {
+ enumerable: name.indexOf('enum') !== -1,
+ configurable: name.indexOf('conf') !== -1,
+ writable: name.indexOf('write') !== -1,
+ initializer: function(...args){
+ return '__' + initializer.apply(this, args) + '__';
+ },
+ });
+ }
+
+ class Example {
+ @dec
+ static enumconfwrite = 1;
+
+ @dec
+ static enumconf = 2;
+
+ @dec
+ static enumwrite = 3;
+
+ @dec
+ static enum = 4;
+
+ @dec
+ static confwrite = 5;
+
+ @dec
+ static conf = 6;
+
+ @dec
+ static write = 7;
+
+ @dec
+ static _ = 8;
+ }
+ const inst = new Example();
+
+ expect(Example).to.have.ownProperty('decoratedProps');
+ expect(Example.decoratedProps).to.eql([
+ "enumconfwrite",
+ "enumconf",
+ "enumwrite",
+ "enum",
+ "confwrite",
+ "conf",
+ "write",
+ "_",
+ ]);
+
+ const descs = Object.getOwnPropertyDescriptors(Example);
+ expect(descs.enumconfwrite.enumerable).to.be.true;
+ expect(descs.enumconfwrite.writable).to.be.true;
+ expect(descs.enumconfwrite.configurable).to.be.true;
+ expect(Example.enumconfwrite).to.eql('__1__');
+
+ expect(descs.enumconf.enumerable).to.be.true;
+ expect(descs.enumconf.writable).to.be.false;
+ expect(descs.enumconf.configurable).to.be.true;
+ expect(Example.enumconf).to.eql('__2__');
+
+ expect(descs.enumwrite.enumerable).to.be.true;
+ expect(descs.enumwrite.writable).to.be.true;
+ expect(descs.enumwrite.configurable).to.be.false;
+ expect(Example.enumwrite).to.eql('__3__');
+
+ expect(descs.enum.enumerable).to.be.true;
+ expect(descs.enum.writable).to.be.false;
+ expect(descs.enum.configurable).to.be.false;
+ expect(Example.enum).to.eql('__4__');
+
+ expect(descs.confwrite.enumerable).to.be.false;
+ expect(descs.confwrite.writable).to.be.true;
+ expect(descs.confwrite.configurable).to.be.true;
+ expect(Example.confwrite).to.eql('__5__');
+
+ expect(descs.conf.enumerable).to.be.false;
+ expect(descs.conf.writable).to.be.false;
+ expect(descs.conf.configurable).to.be.true;
+ expect(Example.conf).to.eql('__6__');
+
+ expect(descs.write.enumerable).to.be.false;
+ expect(descs.write.writable).to.be.true;
+ expect(descs.write.configurable).to.be.false;
+ expect(Example.write).to.eql('__7__');
+
+ expect(descs._.enumerable).to.be.false;
+ expect(descs._.writable).to.be.false;
+ expect(descs._.configurable).to.be.false;
+ expect(Example._).to.eql('__8__');
+ });
+ });
+ });
+
+ describe('object', function(){
+ describe('ordering', function(){
+ it('should evaluate descriptor expressions in order', function(){
+ const calls = [];
+ function dec(id){
+ calls.push(id);
+ return function(){};
+ }
+
+ const obj = {
+ @dec(1)
+ @dec(2)
+ method1(){},
+
+ @dec(3)
+ @dec(4)
+ prop1: 1,
+
+ @dec(5)
+ @dec(6)
+ method2(){},
+
+ @dec(7)
+ @dec(8)
+ prop2: 2,
+ }
+
+ expect(calls).to.eql([1, 2, 3, 4, 5, 6, 7, 8]);
+ });
+
+ it('should call descriptors in reverse order per-method', function(){
+ const calls = [];
+ function dec(id){
+ return function(){
+ calls.push(id);
+ };
+ }
+
+ const obj = {
+ @dec(2)
+ @dec(1)
+ method1(){},
+
+ @dec(4)
+ @dec(3)
+ prop1: 1,
+
+ @dec(6)
+ @dec(5)
+ method2(){},
+
+ @dec(8)
+ @dec(7)
+ prop2: 2,
+ }
+
+ expect(calls).to.eql([1, 2, 3, 4, 5, 6, 7, 8]);
+ });
+ });
+
+ describe('methods', function(){
+ it('should support numeric props', function(){
+ function dec(target, name, descriptor){
+ expect(target).to.be.ok;
+ expect(name).to.eql(4);
+ expect(descriptor).to.be.an('object');
+ }
+
+ const inst = {
+ @dec
+ 4(){
+
+ }
+ };
+ });
+
+ it('should support string props', function(){
+ function dec(target, name, descriptor){
+ expect(target).to.be.ok;
+ expect(name).to.eql("str");
+ expect(descriptor).to.be.an('object');
+ }
+
+ const inst = {
+ @dec
+ "str"(){
+
+ }
+ };
+ });
+
+ it('should allow returning a descriptor', function(){
+ function dec(target, name, descriptor){
+ expect(target).to.be.ok;
+ expect(name).to.be.a('string');
+ expect(descriptor).to.be.an('object');
+
+ target.decoratedProps = (target.decoratedProps || []).concat([name]);
+
+ let value = descriptor.value;
+ return {
+ enumerable: name.indexOf('enum') !== -1,
+ configurable: name.indexOf('conf') !== -1,
+ writable: name.indexOf('write') !== -1,
+ value: function(...args){
+ return '__' + value.apply(this, args) + '__';
+ },
+ };
+ }
+
+ const inst = {
+ @dec
+ enumconfwrite(){
+ return 1;
+ },
+
+ @dec
+ enumconf(){
+ return 2;
+ },
+
+ @dec
+ enumwrite(){
+ return 3;
+ },
+
+ @dec
+ enum(){
+ return 4;
+ },
+
+ @dec
+ confwrite(){
+ return 5;
+ },
+
+ @dec
+ conf(){
+ return 6;
+ },
+
+ @dec
+ write(){
+ return 7;
+ },
+
+ @dec
+ _(){
+ return 8;
+ },
+ }
+
+ expect(inst).to.have.ownProperty('decoratedProps');
+ expect(inst.decoratedProps).to.eql([
+ "enumconfwrite",
+ "enumconf",
+ "enumwrite",
+ "enum",
+ "confwrite",
+ "conf",
+ "write",
+ "_",
+ ]);
+
+ const descs = Object.getOwnPropertyDescriptors(inst);
+ expect(descs.enumconfwrite.enumerable).to.be.true;
+ expect(descs.enumconfwrite.writable).to.be.true;
+ expect(descs.enumconfwrite.configurable).to.be.true;
+ expect(inst.enumconfwrite()).to.eql('__1__');
+
+ expect(descs.enumconf.enumerable).to.be.true;
+ expect(descs.enumconf.writable).to.be.false;
+ expect(descs.enumconf.configurable).to.be.true;
+ expect(inst.enumconf()).to.eql('__2__');
+
+ expect(descs.enumwrite.enumerable).to.be.true;
+ expect(descs.enumwrite.writable).to.be.true;
+ expect(descs.enumwrite.configurable).to.be.false;
+ expect(inst.enumwrite()).to.eql('__3__');
+
+ expect(descs.enum.enumerable).to.be.true;
+ expect(descs.enum.writable).to.be.false;
+ expect(descs.enum.configurable).to.be.false;
+ expect(inst.enum()).to.eql('__4__');
+
+ expect(descs.confwrite.enumerable).to.be.false;
+ expect(descs.confwrite.writable).to.be.true;
+ expect(descs.confwrite.configurable).to.be.true;
+ expect(inst.confwrite()).to.eql('__5__');
+
+ expect(descs.conf.enumerable).to.be.false;
+ expect(descs.conf.writable).to.be.false;
+ expect(descs.conf.configurable).to.be.true;
+ expect(inst.conf()).to.eql('__6__');
+
+ expect(descs.write.enumerable).to.be.false;
+ expect(descs.write.writable).to.be.true;
+ expect(descs.write.configurable).to.be.false;
+ expect(inst.write()).to.eql('__7__');
+
+ expect(descs._.enumerable).to.be.false;
+ expect(descs._.writable).to.be.false;
+ expect(descs._.configurable).to.be.false;
+ expect(inst._()).to.eql('__8__');
+ });
+
+ it('should allow mutating the original descriptor', function(){
+ function dec(target, name, descriptor){
+ expect(target).to.be.ok;
+ expect(name).to.be.a('string');
+ expect(descriptor).to.be.an('object');
+
+ target.decoratedProps = (target.decoratedProps || []).concat([name]);
+
+ let value = descriptor.value;
+ Object.assign(descriptor, {
+ enumerable: name.indexOf('enum') !== -1,
+ configurable: name.indexOf('conf') !== -1,
+ writable: name.indexOf('write') !== -1,
+ value: function(...args){
+ return '__' + value.apply(this, args) + '__';
+ },
+ });
+ }
+
+ const inst = {
+ @dec
+ enumconfwrite(){
+ return 1;
+ },
+
+ @dec
+ enumconf(){
+ return 2;
+ },
+
+ @dec
+ enumwrite(){
+ return 3;
+ },
+
+ @dec
+ enum(){
+ return 4;
+ },
+
+ @dec
+ confwrite(){
+ return 5;
+ },
+
+ @dec
+ conf(){
+ return 6;
+ },
+
+ @dec
+ write(){
+ return 7;
+ },
+
+ @dec
+ _(){
+ return 8;
+ },
+ }
+
+ expect(inst).to.have.ownProperty('decoratedProps');
+ expect(inst.decoratedProps).to.eql([
+ "enumconfwrite",
+ "enumconf",
+ "enumwrite",
+ "enum",
+ "confwrite",
+ "conf",
+ "write",
+ "_",
+ ]);
+
+ const descs = Object.getOwnPropertyDescriptors(inst);
+ expect(descs.enumconfwrite.enumerable).to.be.true;
+ expect(descs.enumconfwrite.writable).to.be.true;
+ expect(descs.enumconfwrite.configurable).to.be.true;
+ expect(inst.enumconfwrite()).to.eql('__1__');
+
+ expect(descs.enumconf.enumerable).to.be.true;
+ expect(descs.enumconf.writable).to.be.false;
+ expect(descs.enumconf.configurable).to.be.true;
+ expect(inst.enumconf()).to.eql('__2__');
+
+ expect(descs.enumwrite.enumerable).to.be.true;
+ expect(descs.enumwrite.writable).to.be.true;
+ expect(descs.enumwrite.configurable).to.be.false;
+ expect(inst.enumwrite()).to.eql('__3__');
+
+ expect(descs.enum.enumerable).to.be.true;
+ expect(descs.enum.writable).to.be.false;
+ expect(descs.enum.configurable).to.be.false;
+ expect(inst.enum()).to.eql('__4__');
+
+ expect(descs.confwrite.enumerable).to.be.false;
+ expect(descs.confwrite.writable).to.be.true;
+ expect(descs.confwrite.configurable).to.be.true;
+ expect(inst.confwrite()).to.eql('__5__');
+
+ expect(descs.conf.enumerable).to.be.false;
+ expect(descs.conf.writable).to.be.false;
+ expect(descs.conf.configurable).to.be.true;
+ expect(inst.conf()).to.eql('__6__');
+
+ expect(descs.write.enumerable).to.be.false;
+ expect(descs.write.writable).to.be.true;
+ expect(descs.write.configurable).to.be.false;
+ expect(inst.write()).to.eql('__7__');
+
+ expect(descs._.enumerable).to.be.false;
+ expect(descs._.writable).to.be.false;
+ expect(descs._.configurable).to.be.false;
+ expect(inst._()).to.eql('__8__');
+ });
+ });
+
+ describe('properties', function(){
+ it('should support numeric props', function(){
+ function dec(target, name, descriptor){
+ expect(target).to.be.ok;
+ expect(name).to.eql(4);
+ expect(descriptor).to.be.an('object');
+ }
+
+ const inst = {
+ @dec
+ 4: 1
+ };
+ });
+
+ it('should support string props', function(){
+ function dec(target, name, descriptor){
+ expect(target).to.be.ok;
+ expect(name).to.eql("str");
+ expect(descriptor).to.be.an('object');
+ }
+
+ const inst = {
+ @dec
+ "str": 1
+ };
+ });
+
+ it('should support mutating an initialzer into an accessor', function(){
+ function dec(target, name, descriptor){
+ expect(target).to.be.ok;
+ expect(name).to.eql("prop");
+ expect(descriptor).to.be.an('object');
+
+ let {initializer} = descriptor;
+ delete descriptor.initializer;
+ delete descriptor.writable;
+
+ let value;
+ descriptor.get = function(){
+ if (initializer){
+ value = '__' + initializer.call(this) + '__';
+ initializer = null;
+ }
+ return value;
+ };
+ }
+
+ let inst = {
+ @dec
+ prop: 3
+ };
+
+ expect(inst.prop).to.eql('__3__');
+ });
+
+ it('should allow returning a descriptor', function(){
+ function dec(target, name, descriptor){
+ expect(target).to.be.ok;
+ expect(name).to.be.a('string');
+ expect(descriptor).to.be.an('object');
+
+ target.decoratedProps = (target.decoratedProps || []).concat([name]);
+
+ let initializer = descriptor.initializer;
+ return {
+ enumerable: name.indexOf('enum') !== -1,
+ configurable: name.indexOf('conf') !== -1,
+ writable: name.indexOf('write') !== -1,
+ initializer: function(...args){
+ return '__' + initializer.apply(this, args) + '__';
+ },
+ };
+ }
+
+ const inst = {
+ @dec
+ enumconfwrite: 1,
+
+ @dec
+ enumconf: 2,
+
+ @dec
+ enumwrite: 3,
+
+ @dec
+ enum: 4,
+
+ @dec
+ confwrite: 5,
+
+ @dec
+ conf: 6,
+
+ @dec
+ write: 7,
+
+ @dec
+ _: 8,
+ };
+
+ expect(inst).to.have.ownProperty('decoratedProps');
+ expect(inst.decoratedProps).to.eql([
+ "enumconfwrite",
+ "enumconf",
+ "enumwrite",
+ "enum",
+ "confwrite",
+ "conf",
+ "write",
+ "_",
+ ]);
+
+ const descs = Object.getOwnPropertyDescriptors(inst);
+ expect(descs.enumconfwrite.enumerable).to.be.true;
+ expect(descs.enumconfwrite.writable).to.be.true;
+ expect(descs.enumconfwrite.configurable).to.be.true;
+ expect(inst.enumconfwrite).to.eql('__1__');
+
+ expect(descs.enumconf.enumerable).to.be.true;
+ expect(descs.enumconf.writable).to.be.false;
+ expect(descs.enumconf.configurable).to.be.true;
+ expect(inst.enumconf).to.eql('__2__');
+
+ expect(descs.enumwrite.enumerable).to.be.true;
+ expect(descs.enumwrite.writable).to.be.true;
+ expect(descs.enumwrite.configurable).to.be.false;
+ expect(inst.enumwrite).to.eql('__3__');
+
+ expect(descs.enum.enumerable).to.be.true;
+ expect(descs.enum.writable).to.be.false;
+ expect(descs.enum.configurable).to.be.false;
+ expect(inst.enum).to.eql('__4__');
+
+ expect(descs.confwrite.enumerable).to.be.false;
+ expect(descs.confwrite.writable).to.be.true;
+ expect(descs.confwrite.configurable).to.be.true;
+ expect(inst.confwrite).to.eql('__5__');
+
+ expect(descs.conf.enumerable).to.be.false;
+ expect(descs.conf.writable).to.be.false;
+ expect(descs.conf.configurable).to.be.true;
+ expect(inst.conf).to.eql('__6__');
+
+ expect(descs.write.enumerable).to.be.false;
+ expect(descs.write.writable).to.be.true;
+ expect(descs.write.configurable).to.be.false;
+ expect(inst.write).to.eql('__7__');
+
+ expect(descs._.enumerable).to.be.false;
+ expect(descs._.writable).to.be.false;
+ expect(descs._.configurable).to.be.false;
+ expect(inst._).to.eql('__8__');
+ });
+
+ it('should allow mutating the original descriptor', function(){
+ function dec(target, name, descriptor){
+ expect(target).to.be.ok;
+ expect(name).to.be.a('string');
+ expect(descriptor).to.be.an('object');
+
+ target.decoratedProps = (target.decoratedProps || []).concat([name]);
+
+ let initializer = descriptor.initializer;
+ Object.assign(descriptor, {
+ enumerable: name.indexOf('enum') !== -1,
+ configurable: name.indexOf('conf') !== -1,
+ writable: name.indexOf('write') !== -1,
+ initializer: function(...args){
+ return '__' + initializer.apply(this, args) + '__';
+ },
+ });
+ }
+
+ const inst = {
+ @dec
+ enumconfwrite: 1,
+
+ @dec
+ enumconf: 2,
+
+ @dec
+ enumwrite: 3,
+
+ @dec
+ enum: 4,
+
+ @dec
+ confwrite: 5,
+
+ @dec
+ conf: 6,
+
+ @dec
+ write: 7,
+
+ @dec
+ _: 8,
+ };
+
+ expect(inst).to.have.ownProperty('decoratedProps');
+ expect(inst.decoratedProps).to.eql([
+ "enumconfwrite",
+ "enumconf",
+ "enumwrite",
+ "enum",
+ "confwrite",
+ "conf",
+ "write",
+ "_",
+ ]);
+
+ const descs = Object.getOwnPropertyDescriptors(inst);
+ expect(descs.enumconfwrite.enumerable).to.be.true;
+ expect(descs.enumconfwrite.writable).to.be.true;
+ expect(descs.enumconfwrite.configurable).to.be.true;
+ expect(inst.enumconfwrite).to.eql('__1__');
+
+ expect(descs.enumconf.enumerable).to.be.true;
+ expect(descs.enumconf.writable).to.be.false;
+ expect(descs.enumconf.configurable).to.be.true;
+ expect(inst.enumconf).to.eql('__2__');
+
+ expect(descs.enumwrite.enumerable).to.be.true;
+ expect(descs.enumwrite.writable).to.be.true;
+ expect(descs.enumwrite.configurable).to.be.false;
+ expect(inst.enumwrite).to.eql('__3__');
+
+ expect(descs.enum.enumerable).to.be.true;
+ expect(descs.enum.writable).to.be.false;
+ expect(descs.enum.configurable).to.be.false;
+ expect(inst.enum).to.eql('__4__');
+
+ expect(descs.confwrite.enumerable).to.be.false;
+ expect(descs.confwrite.writable).to.be.true;
+ expect(descs.confwrite.configurable).to.be.true;
+ expect(inst.confwrite).to.eql('__5__');
+
+ expect(descs.conf.enumerable).to.be.false;
+ expect(descs.conf.writable).to.be.false;
+ expect(descs.conf.configurable).to.be.true;
+ expect(inst.conf).to.eql('__6__');
+
+ expect(descs.write.enumerable).to.be.false;
+ expect(descs.write.writable).to.be.true;
+ expect(descs.write.configurable).to.be.false;
+ expect(inst.write).to.eql('__7__');
+
+ expect(descs._.enumerable).to.be.false;
+ expect(descs._.writable).to.be.false;
+ expect(descs._.configurable).to.be.false;
+ expect(inst._).to.eql('__8__');
+ });
+ });
+ });
+});
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-babel-plugin-transform-decorators-legacy.git
More information about the Pkg-javascript-commits
mailing list