diff --git a/packages/babel-plugin-proposal-decorators/src/index.js b/packages/babel-plugin-proposal-decorators/src/index.js index 65324fb4ea..a5d33b21a8 100644 --- a/packages/babel-plugin-proposal-decorators/src/index.js +++ b/packages/babel-plugin-proposal-decorators/src/index.js @@ -63,11 +63,11 @@ export default function() { * with the proper decorated behavior. */ function applyClassDecorators(classPath) { + if (!hasClassDecorators(classPath.node)) return; + const decorators = classPath.node.decorators || []; classPath.node.decorators = null; - if (decorators.length === 0) return; - const name = classPath.scope.generateDeclaredUidIdentifier("class"); return decorators @@ -82,30 +82,30 @@ export default function() { }, classPath.node); } + function hasClassDecorators(classNode) { + return !!(classNode.decorators && classNode.decorators.length); + } + /** * 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; + if (!hasMethodDecorators(path.node.body.body)) return; return applyTargetDecorators(path, state, path.node.body.body); } + function hasMethodDecorators(body) { + return body.some(node => node.decorators && node.decorators.length); + } + /** * 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; + if (!hasMethodDecorators(path.node.properties)) return; return applyTargetDecorators(path, state, path.node.properties); } @@ -215,32 +215,29 @@ export default function() { inherits: syntaxDecorators, 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"); + if (!hasClassDecorators(node) && !hasMethodDecorators(node.body.body)) { + return; + } - path.replaceWith( - t.variableDeclaration("let", [ - t.variableDeclarator(ref, t.toExpression(node)), - ]), - ); + const ref = node.id || path.scope.generateUidIdentifier("class"); + const letDeclaration = t.variableDeclaration("let", [ + t.variableDeclarator(ref, t.toExpression(node)), + ]); + + if (path.parentPath.isExportDefaultDeclaration()) { + // Split the class declaration and the export into two separate statements. + path.parentPath.replaceWithMultiple([ + letDeclaration, + t.exportNamedDeclaration(null, [ + t.exportSpecifier(ref, t.identifier("default")), + ]), + ]); + } else { + path.replaceWith(letDeclaration); + } }, ClassExpression(path, state) { // Create a replacement for the class node if there is one. We do one pass to replace classes with diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/decl-to-expression/class-decorators/actual.js b/packages/babel-plugin-proposal-decorators/test/fixtures/decl-to-expression/class-decorators/actual.js new file mode 100644 index 0000000000..cb39542cf9 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/decl-to-expression/class-decorators/actual.js @@ -0,0 +1,2 @@ +export default @dec class A {} +@dec class B {} \ No newline at end of file diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/decl-to-expression/class-decorators/expected.js b/packages/babel-plugin-proposal-decorators/test/fixtures/decl-to-expression/class-decorators/expected.js new file mode 100644 index 0000000000..1070d97d7b --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/decl-to-expression/class-decorators/expected.js @@ -0,0 +1,5 @@ +var _class, _class2; + +export default dec(_class = class A {}) || _class; + +let B = dec(_class2 = class B {}) || _class2; diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/decl-to-expression/method-decorators/actual.js b/packages/babel-plugin-proposal-decorators/test/fixtures/decl-to-expression/method-decorators/actual.js new file mode 100644 index 0000000000..290f2ede60 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/decl-to-expression/method-decorators/actual.js @@ -0,0 +1,6 @@ +export default class A { + @dec foo() {} +} +class B { + @dec foo() {} +} \ No newline at end of file diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/decl-to-expression/method-decorators/expected.js b/packages/babel-plugin-proposal-decorators/test/fixtures/decl-to-expression/method-decorators/expected.js new file mode 100644 index 0000000000..27b1191153 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/decl-to-expression/method-decorators/expected.js @@ -0,0 +1,13 @@ +var _class, _class2; + +function _applyDecoratedDescriptor(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) { Object['define' + 'Property'](target, property, desc); desc = null; } return desc; } + +let A = (_class2 = class A { + foo() {} + +}, (_applyDecoratedDescriptor(_class2.prototype, "foo", [dec], Object.getOwnPropertyDescriptor(_class2.prototype, "foo"), _class2.prototype)), _class2); +export { A as default }; +let B = (_class = class B { + foo() {} + +}, (_applyDecoratedDescriptor(_class.prototype, "foo", [dec], Object.getOwnPropertyDescriptor(_class.prototype, "foo"), _class.prototype)), _class); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/decl-to-expression/no-decorators/actual.js b/packages/babel-plugin-proposal-decorators/test/fixtures/decl-to-expression/no-decorators/actual.js new file mode 100644 index 0000000000..aa07db3e33 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/decl-to-expression/no-decorators/actual.js @@ -0,0 +1,6 @@ +export default class A { + foo() {} +} +class B { + foo() {} +} \ No newline at end of file diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/decl-to-expression/no-decorators/expected.js b/packages/babel-plugin-proposal-decorators/test/fixtures/decl-to-expression/no-decorators/expected.js new file mode 100644 index 0000000000..32f5012a73 --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/decl-to-expression/no-decorators/expected.js @@ -0,0 +1,9 @@ +export default class A { + foo() {} + +} + +class B { + foo() {} + +} diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/decl-to-expression/options.json b/packages/babel-plugin-proposal-decorators/test/fixtures/decl-to-expression/options.json new file mode 100644 index 0000000000..f2cf7da30b --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/decl-to-expression/options.json @@ -0,0 +1,4 @@ +{ + "presets": [], + "plugins": ["proposal-decorators"] +} \ No newline at end of file