[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