Transform decorated classes from the export visitor (#8047)

Fixes gh-8041

<!--
Before making a PR please make sure to read our contributing guidelines
https://github.com/babel/babel/blob/master/CONTRIBUTING.md

For issue references: Add a comma-separated list of a [closing word](https://help.github.com/articles/closing-issues-via-commit-messages/) followed by the ticket number fixed by the PR. It should be underlined in the preview if done correctly.
-->

| Q                        | A <!--(Can use an emoji 👍) -->
| ------------------------ | ---
| Fixed Issues?            | #8041
| Patch: Bug Fix?          |👍
| Major: Breaking Change?  |
| Minor: New Feature?      |
| Tests Added + Pass?      | Yes
| Documentation PR         | <!-- If so, add `[skip ci]` to your commit message to skip CI -->
| Any Dependency Changes?  |
| License                  | MIT

<!-- Describe your changes below in as much detail as possible -->
This commit is contained in:
Nicolò Ribaudo
2018-05-25 17:36:30 +02:00
committed by Logan Smyth
parent 9c707f9670
commit 90a174e7c4
4 changed files with 51 additions and 20 deletions

View File

@@ -206,31 +206,42 @@ function applyTargetDecorators(path, state, decoratedProps) {
]);
}
function decoratedClassToExpression({ node, scope }) {
if (!hasClassDecorators(node) && !hasMethodDecorators(node.body.body)) {
return;
}
const ref = node.id
? t.cloneNode(node.id)
: scope.generateUidIdentifier("class");
return t.variableDeclaration("let", [
t.variableDeclarator(ref, t.toExpression(node)),
]);
}
export default {
ClassDeclaration(path) {
const { node } = path;
ExportDefaultDeclaration(path) {
const decl = path.get("declaration");
if (!decl.isClassDeclaration()) return;
if (!hasClassDecorators(node) && !hasMethodDecorators(node.body.body)) {
return;
}
const ref = node.id
? t.cloneNode(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,
const replacement = decoratedClassToExpression(decl);
if (replacement) {
path.replaceWithMultiple([
replacement,
t.exportNamedDeclaration(null, [
t.exportSpecifier(t.cloneNode(ref), t.identifier("default")),
t.exportSpecifier(
t.cloneNode(replacement.declarations[0].id),
t.identifier("default"),
),
]),
]);
} else {
path.replaceWith(letDeclaration);
}
},
ClassDeclaration(path) {
const replacement = decoratedClassToExpression(path);
if (replacement) {
path.replaceWith(replacement);
}
},
ClassExpression(path, state) {

View File

@@ -0,0 +1,4 @@
export default class {
@foo
bar() {}
}

View File

@@ -0,0 +1,6 @@
{
"plugins": [
["proposal-decorators", { "legacy": true }],
"proposal-class-properties"
]
}

View File

@@ -0,0 +1,10 @@
var _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 _class = (_class2 = class {
bar() {}
}, (_applyDecoratedDescriptor(_class2.prototype, "bar", [foo], Object.getOwnPropertyDescriptor(_class2.prototype, "bar"), _class2.prototype)), _class2);
export { _class as default };