Merge pull request #7734 from nicolo-ribaudo/decorators-legacy-option
Decorators legacy option
This commit is contained in:
commit
339dfddca5
@ -1,4 +1,7 @@
|
||||
{
|
||||
"compact": false,
|
||||
"presets": ["es2015","stage-2"]
|
||||
"presets": [
|
||||
"es2015",
|
||||
["stage-2", { "decoratorsLegacy": true }]
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
{
|
||||
"plugins": ["external-helpers", ["proposal-class-properties", {"loose": true}]],
|
||||
"presets": ["stage-0", "es2015"]
|
||||
"presets": [
|
||||
["stage-0", { "decoratorsLegacy": true }],
|
||||
"es2015"
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
{
|
||||
"plugins": ["external-helpers", "proposal-class-properties"],
|
||||
"presets": ["stage-0", "es2015"]
|
||||
"presets": [
|
||||
["stage-0", { "decoratorsLegacy": true }],
|
||||
"es2015"
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
{
|
||||
"presets": ["es2015", "stage-2"]
|
||||
"presets": [
|
||||
"es2015",
|
||||
["stage-2", { "decoratorsLegacy": true }]
|
||||
]
|
||||
}
|
||||
|
||||
@ -62,11 +62,33 @@ Add the following line to your .babelrc file:
|
||||
}
|
||||
```
|
||||
|
||||
#### NOTE: Order of Plugins Matters!
|
||||
### Via CLI
|
||||
|
||||
```sh
|
||||
babel --plugins @babel/plugin-proposal-decorators script.js
|
||||
```
|
||||
|
||||
### Via Node API
|
||||
|
||||
```javascript
|
||||
require("@babel/core").transform("code", {
|
||||
plugins: ["@babel/plugin-proposal-decorators"]
|
||||
});
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
### `legacy`
|
||||
|
||||
`boolean`, defaults to `false`.
|
||||
|
||||
Use the legacy (stage 1) decorators syntax and behavior.
|
||||
|
||||
#### NOTE: Compatibility with `@babel/plugin-proposal-class-properties`
|
||||
|
||||
If you are including your plugins manually and using `@babel/plugin-proposal-class-properties`, make sure that `@babel/plugin-proposal-decorators` comes *before* `@babel/plugin-proposal-class-properties`.
|
||||
|
||||
Currently, `@babel/plugin-proposal-class-properties` must be used in `loose` mode to support the `@babel/plugin-proposal-decorators`. To use `@babel/plugin-proposal-class-properties` in spec mode with decorators, wait for the next major version of decorators (Stage 2).
|
||||
When using the `legacy: true` mode, `@babel/plugin-proposal-class-properties` must be used in `loose` mode to support the `@babel/plugin-proposal-decorators`.
|
||||
|
||||
Wrong:
|
||||
|
||||
@ -85,25 +107,20 @@ Right:
|
||||
{
|
||||
"plugins": [
|
||||
"@babel/plugin-proposal-decorators",
|
||||
"@babel/plugin-proposal-class-properties"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"plugins": [
|
||||
["@babel/plugin-proposal-decorators", { "legacy": true }],
|
||||
["@babel/plugin-proposal-class-properties", { "loose" : true }]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Via CLI
|
||||
|
||||
```sh
|
||||
babel --plugins @babel/plugin-proposal-decorators script.js
|
||||
```
|
||||
|
||||
### Via Node API
|
||||
|
||||
```javascript
|
||||
require("@babel/core").transform("code", {
|
||||
plugins: ["@babel/plugin-proposal-decorators"]
|
||||
});
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
* [Proposal: JavaScript Decorators](https://github.com/wycats/javascript-decorators/blob/master/README.md)
|
||||
|
||||
@ -1,278 +1,27 @@
|
||||
// Fork of https://github.com/loganfsmyth/babel-plugin-proposal-decorators-legacy
|
||||
|
||||
import { declare } from "@babel/helper-plugin-utils";
|
||||
import syntaxDecorators from "@babel/plugin-syntax-decorators";
|
||||
import { template, types as t } from "@babel/core";
|
||||
import visitor from "./transformer";
|
||||
import legacyVisitor from "./transformer-legacy";
|
||||
|
||||
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;
|
||||
}
|
||||
})
|
||||
`);
|
||||
|
||||
export default declare(api => {
|
||||
export default declare((api, options) => {
|
||||
api.assertVersion(7);
|
||||
|
||||
const WARNING_CALLS = new WeakSet();
|
||||
const { legacy = false } = options;
|
||||
if (typeof legacy !== "boolean") {
|
||||
throw new Error("'legacy' must be a boolean.");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.callee),
|
||||
if (legacy !== true) {
|
||||
throw new Error(
|
||||
"The new decorators proposal is not supported yet." +
|
||||
' You muse pass the `"legacy": true` option to' +
|
||||
" @babel/plugin-proposal-decorators",
|
||||
);
|
||||
if (identDecorators.length === 0) return;
|
||||
|
||||
return t.sequenceExpression(
|
||||
identDecorators
|
||||
.map(decorator => {
|
||||
const callee = decorator.callee;
|
||||
const id = (decorator.callee = path.scope.generateDeclaredUidIdentifier(
|
||||
"dec",
|
||||
));
|
||||
return t.assignmentExpression("=", id, callee);
|
||||
})
|
||||
.concat([path.node]),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a class expression with class-level decorators, create a new expression
|
||||
* with the proper decorated behavior.
|
||||
*/
|
||||
function applyClassDecorators(classPath) {
|
||||
if (!hasClassDecorators(classPath.node)) return;
|
||||
|
||||
const decorators = classPath.node.decorators || [];
|
||||
classPath.node.decorators = null;
|
||||
|
||||
const name = classPath.scope.generateDeclaredUidIdentifier("class");
|
||||
|
||||
return decorators
|
||||
.map(dec => dec.callee)
|
||||
.reverse()
|
||||
.reduce(function(acc, decorator) {
|
||||
return buildClassDecorator({
|
||||
CLASS_REF: t.cloneNode(name),
|
||||
DECORATOR: t.cloneNode(decorator),
|
||||
INNER: acc,
|
||||
}).expression;
|
||||
}, 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) {
|
||||
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) {
|
||||
if (!hasMethodDecorators(path.node.properties)) 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 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 })) {
|
||||
const descriptor = path.scope.generateDeclaredUidIdentifier(
|
||||
"descriptor",
|
||||
);
|
||||
|
||||
const initializer = node.value
|
||||
? t.functionExpression(
|
||||
null,
|
||||
[],
|
||||
t.blockStatement([t.returnStatement(node.value)]),
|
||||
)
|
||||
: t.nullLiteral();
|
||||
|
||||
node.value = t.callExpression(
|
||||
state.addHelper("initializerWarningHelper"),
|
||||
[descriptor, t.thisExpression()],
|
||||
);
|
||||
|
||||
WARNING_CALLS.add(node.value);
|
||||
|
||||
acc = acc.concat([
|
||||
t.assignmentExpression(
|
||||
"=",
|
||||
descriptor,
|
||||
t.callExpression(state.addHelper("applyDecoratedDescriptor"), [
|
||||
t.cloneNode(target),
|
||||
t.cloneNode(property),
|
||||
t.arrayExpression(decorators.map(dec => t.cloneNode(dec.callee))),
|
||||
t.objectExpression([
|
||||
t.objectProperty(
|
||||
t.identifier("enumerable"),
|
||||
t.booleanLiteral(true),
|
||||
),
|
||||
t.objectProperty(t.identifier("initializer"), initializer),
|
||||
]),
|
||||
]),
|
||||
),
|
||||
]);
|
||||
} else {
|
||||
acc = acc.concat(
|
||||
t.callExpression(state.addHelper("applyDecoratedDescriptor"), [
|
||||
t.cloneNode(target),
|
||||
t.cloneNode(property),
|
||||
t.arrayExpression(decorators.map(dec => t.cloneNode(dec.callee))),
|
||||
t.isObjectProperty(node) ||
|
||||
t.isClassProperty(node, { static: true })
|
||||
? buildGetObjectInitializer({
|
||||
TEMP: path.scope.generateDeclaredUidIdentifier("init"),
|
||||
TARGET: t.cloneNode(target),
|
||||
PROPERTY: t.cloneNode(property),
|
||||
}).expression
|
||||
: buildGetDescriptor({
|
||||
TARGET: t.cloneNode(target),
|
||||
PROPERTY: t.cloneNode(property),
|
||||
}).expression,
|
||||
t.cloneNode(target),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
return t.sequenceExpression([
|
||||
t.assignmentExpression("=", t.cloneNode(name), path.node),
|
||||
t.sequenceExpression(exprs),
|
||||
t.cloneNode(name),
|
||||
]);
|
||||
}
|
||||
|
||||
return {
|
||||
inherits: syntaxDecorators,
|
||||
|
||||
visitor: {
|
||||
ClassDeclaration(path) {
|
||||
const { node } = path;
|
||||
|
||||
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,
|
||||
t.exportNamedDeclaration(null, [
|
||||
t.exportSpecifier(t.cloneNode(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
|
||||
// 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 (!WARNING_CALLS.has(path.node.right)) return;
|
||||
|
||||
path.replaceWith(
|
||||
t.callExpression(state.addHelper("initializerDefineProperty"), [
|
||||
t.cloneNode(path.get("left.object").node),
|
||||
t.stringLiteral(path.get("left.property").node.name),
|
||||
t.cloneNode(path.get("right.arguments")[0].node),
|
||||
t.cloneNode(path.get("right.arguments")[1].node),
|
||||
]),
|
||||
);
|
||||
},
|
||||
},
|
||||
visitor: legacy ? legacyVisitor : visitor,
|
||||
};
|
||||
});
|
||||
|
||||
@ -0,0 +1,265 @@
|
||||
// Fork of https://github.com/loganfsmyth/babel-plugin-proposal-decorators-legacy
|
||||
|
||||
import { template, types as t } from "@babel/core";
|
||||
|
||||
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 WARNING_CALLS = new WeakSet();
|
||||
|
||||
/**
|
||||
* 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.callee),
|
||||
);
|
||||
if (identDecorators.length === 0) return;
|
||||
|
||||
return t.sequenceExpression(
|
||||
identDecorators
|
||||
.map(decorator => {
|
||||
const callee = decorator.callee;
|
||||
const id = (decorator.callee = path.scope.generateDeclaredUidIdentifier(
|
||||
"dec",
|
||||
));
|
||||
return t.assignmentExpression("=", id, callee);
|
||||
})
|
||||
.concat([path.node]),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a class expression with class-level decorators, create a new expression
|
||||
* with the proper decorated behavior.
|
||||
*/
|
||||
function applyClassDecorators(classPath) {
|
||||
if (!hasClassDecorators(classPath.node)) return;
|
||||
|
||||
const decorators = classPath.node.decorators || [];
|
||||
classPath.node.decorators = null;
|
||||
|
||||
const name = classPath.scope.generateDeclaredUidIdentifier("class");
|
||||
|
||||
return decorators
|
||||
.map(dec => dec.callee)
|
||||
.reverse()
|
||||
.reduce(function(acc, decorator) {
|
||||
return buildClassDecorator({
|
||||
CLASS_REF: t.cloneNode(name),
|
||||
DECORATOR: t.cloneNode(decorator),
|
||||
INNER: acc,
|
||||
}).expression;
|
||||
}, 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) {
|
||||
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) {
|
||||
if (!hasMethodDecorators(path.node.properties)) 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 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 })) {
|
||||
const descriptor = path.scope.generateDeclaredUidIdentifier("descriptor");
|
||||
|
||||
const initializer = node.value
|
||||
? t.functionExpression(
|
||||
null,
|
||||
[],
|
||||
t.blockStatement([t.returnStatement(node.value)]),
|
||||
)
|
||||
: t.nullLiteral();
|
||||
|
||||
node.value = t.callExpression(
|
||||
state.addHelper("initializerWarningHelper"),
|
||||
[descriptor, t.thisExpression()],
|
||||
);
|
||||
|
||||
WARNING_CALLS.add(node.value);
|
||||
|
||||
acc = acc.concat([
|
||||
t.assignmentExpression(
|
||||
"=",
|
||||
descriptor,
|
||||
t.callExpression(state.addHelper("applyDecoratedDescriptor"), [
|
||||
t.cloneNode(target),
|
||||
t.cloneNode(property),
|
||||
t.arrayExpression(decorators.map(dec => t.cloneNode(dec.callee))),
|
||||
t.objectExpression([
|
||||
t.objectProperty(
|
||||
t.identifier("enumerable"),
|
||||
t.booleanLiteral(true),
|
||||
),
|
||||
t.objectProperty(t.identifier("initializer"), initializer),
|
||||
]),
|
||||
]),
|
||||
),
|
||||
]);
|
||||
} else {
|
||||
acc = acc.concat(
|
||||
t.callExpression(state.addHelper("applyDecoratedDescriptor"), [
|
||||
t.cloneNode(target),
|
||||
t.cloneNode(property),
|
||||
t.arrayExpression(decorators.map(dec => t.cloneNode(dec.callee))),
|
||||
t.isObjectProperty(node) || t.isClassProperty(node, { static: true })
|
||||
? buildGetObjectInitializer({
|
||||
TEMP: path.scope.generateDeclaredUidIdentifier("init"),
|
||||
TARGET: t.cloneNode(target),
|
||||
PROPERTY: t.cloneNode(property),
|
||||
}).expression
|
||||
: buildGetDescriptor({
|
||||
TARGET: t.cloneNode(target),
|
||||
PROPERTY: t.cloneNode(property),
|
||||
}).expression,
|
||||
t.cloneNode(target),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
return t.sequenceExpression([
|
||||
t.assignmentExpression("=", t.cloneNode(name), path.node),
|
||||
t.sequenceExpression(exprs),
|
||||
t.cloneNode(name),
|
||||
]);
|
||||
}
|
||||
|
||||
export default {
|
||||
ClassDeclaration(path) {
|
||||
const { node } = path;
|
||||
|
||||
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,
|
||||
t.exportNamedDeclaration(null, [
|
||||
t.exportSpecifier(t.cloneNode(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
|
||||
// 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 (!WARNING_CALLS.has(path.node.right)) return;
|
||||
|
||||
path.replaceWith(
|
||||
t.callExpression(state.addHelper("initializerDefineProperty"), [
|
||||
t.cloneNode(path.get("left.object").node),
|
||||
t.stringLiteral(path.get("left.property").node.name),
|
||||
t.cloneNode(path.get("right.arguments")[0].node),
|
||||
t.cloneNode(path.get("right.arguments")[1].node),
|
||||
]),
|
||||
);
|
||||
},
|
||||
};
|
||||
@ -0,0 +1,3 @@
|
||||
// Not implemented yet
|
||||
|
||||
export default {};
|
||||
@ -1,4 +1,4 @@
|
||||
{
|
||||
"presets": [],
|
||||
"plugins": ["proposal-decorators"]
|
||||
"plugins": [["proposal-decorators", { "legacy": true }]]
|
||||
}
|
||||
@ -1,4 +1,7 @@
|
||||
{
|
||||
"presets": ["es2015"],
|
||||
"plugins": ["proposal-decorators", ["proposal-class-properties", {"loose": true}]]
|
||||
"plugins": [
|
||||
["proposal-decorators", { "legacy": true }],
|
||||
["proposal-class-properties", { "loose": true }]
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
{
|
||||
"presets": ["es2015", "stage-0"]
|
||||
"presets": [
|
||||
"es2015",
|
||||
["stage-0", { "decoratorsLegacy": true }]
|
||||
]
|
||||
}
|
||||
|
||||
@ -33,3 +33,11 @@ require("@babel/core").transform("code", {
|
||||
plugins: ["@babel/plugin-syntax-decorators"]
|
||||
});
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
### `legacy`
|
||||
|
||||
`boolean`, defaults to `false`.
|
||||
|
||||
Use the legacy (stage 1) decorators syntax.
|
||||
|
||||
@ -1,11 +1,16 @@
|
||||
import { declare } from "@babel/helper-plugin-utils";
|
||||
|
||||
export default declare(api => {
|
||||
export default declare((api, options) => {
|
||||
api.assertVersion(7);
|
||||
|
||||
const { legacy = false } = options;
|
||||
if (typeof legacy !== "boolean") {
|
||||
throw new Error("'legacy' must be a boolean.");
|
||||
}
|
||||
|
||||
return {
|
||||
manipulateOptions(opts, parserOpts) {
|
||||
parserOpts.plugins.push("decorators");
|
||||
parserOpts.plugins.push(legacy ? "decorators" : "decorators2");
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
@ -1,3 +1,9 @@
|
||||
{
|
||||
"plugins": ["external-helpers", "transform-function-name", "transform-classes", "proposal-decorators", "transform-modules-commonjs"]
|
||||
"plugins": [
|
||||
"external-helpers",
|
||||
"transform-function-name",
|
||||
"transform-classes",
|
||||
["proposal-decorators", { "legacy": true }],
|
||||
"transform-modules-commonjs"
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,3 +1,9 @@
|
||||
{
|
||||
"plugins": ["external-helpers", "transform-function-name", "transform-modules-commonjs", "transform-classes", "proposal-decorators"]
|
||||
"plugins": [
|
||||
"external-helpers",
|
||||
"transform-function-name",
|
||||
"transform-modules-commonjs",
|
||||
"transform-classes",
|
||||
["proposal-decorators", { "legacy": true }]
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,3 +1,9 @@
|
||||
{
|
||||
"plugins": ["external-helpers", "transform-function-name", "transform-modules-commonjs", "transform-classes", "proposal-decorators"]
|
||||
"plugins": [
|
||||
"external-helpers",
|
||||
"transform-function-name",
|
||||
"transform-modules-commonjs",
|
||||
"transform-classes",
|
||||
["proposal-decorators", { "legacy": true }]
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
{
|
||||
"plugins": ["external-helpers", "transform-function-name", "transform-classes", "proposal-decorators"]
|
||||
"plugins": [
|
||||
"external-helpers",
|
||||
"transform-function-name",
|
||||
"transform-classes",
|
||||
["proposal-decorators", { "legacy": true }]
|
||||
]
|
||||
}
|
||||
|
||||
@ -47,3 +47,9 @@ Enable "loose" transformations for any plugins in this preset that allow them.
|
||||
`boolean`, defaults to `false`.
|
||||
|
||||
Will use the native built-in instead of trying to polyfill behavior for any plugins that require one.
|
||||
|
||||
### `decoratorsLegacy`
|
||||
|
||||
`boolean`, defaults to `false`.
|
||||
|
||||
Use the legacy (stage 1) decorators syntax and behavior.
|
||||
|
||||
@ -3,16 +3,10 @@ import presetStage1 from "@babel/preset-stage-1";
|
||||
|
||||
import transformFunctionBind from "@babel/plugin-proposal-function-bind";
|
||||
|
||||
export default declare((api, opts) => {
|
||||
export default declare((api, opts = {}) => {
|
||||
api.assertVersion(7);
|
||||
|
||||
let loose = false;
|
||||
let useBuiltIns = false;
|
||||
|
||||
if (opts !== undefined) {
|
||||
if (opts.loose !== undefined) loose = opts.loose;
|
||||
if (opts.useBuiltIns !== undefined) useBuiltIns = opts.useBuiltIns;
|
||||
}
|
||||
const { loose = false, useBuiltIns = false, decoratorsLegacy = false } = opts;
|
||||
|
||||
if (typeof loose !== "boolean") {
|
||||
throw new Error("@babel/preset-stage-0 'loose' option must be a boolean.");
|
||||
@ -22,9 +16,22 @@ export default declare((api, opts) => {
|
||||
"@babel/preset-stage-0 'useBuiltIns' option must be a boolean.",
|
||||
);
|
||||
}
|
||||
if (typeof decoratorsLegacy !== "boolean") {
|
||||
throw new Error(
|
||||
"@babel/preset-stage-0 'decoratorsLegacy' option must be a boolean.",
|
||||
);
|
||||
}
|
||||
|
||||
if (decoratorsLegacy !== true) {
|
||||
throw new Error(
|
||||
"The new decorators proposal is not supported yet." +
|
||||
' You muse pass the `"decoratorsLegacy": true` option to' +
|
||||
" @babel/preset-stage-0",
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
presets: [[presetStage1, { loose, useBuiltIns }]],
|
||||
presets: [[presetStage1, { loose, useBuiltIns, decoratorsLegacy }]],
|
||||
plugins: [transformFunctionBind],
|
||||
};
|
||||
});
|
||||
|
||||
@ -58,6 +58,12 @@ Enable "loose" transformations for any plugins in this preset that allow them.
|
||||
|
||||
Will use the native built-in instead of trying to polyfill behavior for any plugins that require one.
|
||||
|
||||
### `decoratorsLegacy`
|
||||
|
||||
`boolean`, defaults to `false`.
|
||||
|
||||
Use the legacy (stage 1) decorators syntax and behavior.
|
||||
|
||||
## References
|
||||
|
||||
- Chapter "[The TC39 process for ECMAScript features](http://exploringjs.com/es2016-es2017/ch_tc39-process.html)" in "Exploring ES2016 and ES2017" by Axel Rauschmayer
|
||||
|
||||
@ -9,7 +9,6 @@
|
||||
"main": "lib/index.js",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "7.0.0-beta.44",
|
||||
"@babel/plugin-proposal-decorators": "7.0.0-beta.44",
|
||||
"@babel/plugin-proposal-do-expressions": "7.0.0-beta.44",
|
||||
"@babel/plugin-proposal-export-default-from": "7.0.0-beta.44",
|
||||
"@babel/plugin-proposal-logical-assignment-operators": "7.0.0-beta.44",
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { declare } from "@babel/helper-plugin-utils";
|
||||
import presetStage2 from "@babel/preset-stage-2";
|
||||
|
||||
import transformDecorators from "@babel/plugin-proposal-decorators";
|
||||
import transformExportDefaultFrom from "@babel/plugin-proposal-export-default-from";
|
||||
import transformLogicalAssignmentOperators from "@babel/plugin-proposal-logical-assignment-operators";
|
||||
import transformOptionalChaining from "@babel/plugin-proposal-optional-chaining";
|
||||
@ -9,16 +8,10 @@ import transformPipelineOperator from "@babel/plugin-proposal-pipeline-operator"
|
||||
import transformNullishCoalescingOperator from "@babel/plugin-proposal-nullish-coalescing-operator";
|
||||
import transformDoExpressions from "@babel/plugin-proposal-do-expressions";
|
||||
|
||||
export default declare((api, opts) => {
|
||||
export default declare((api, opts = {}) => {
|
||||
api.assertVersion(7);
|
||||
|
||||
let loose = false;
|
||||
let useBuiltIns = false;
|
||||
|
||||
if (opts !== undefined) {
|
||||
if (opts.loose !== undefined) loose = opts.loose;
|
||||
if (opts.useBuiltIns !== undefined) useBuiltIns = opts.useBuiltIns;
|
||||
}
|
||||
const { loose = false, useBuiltIns = false, decoratorsLegacy = false } = opts;
|
||||
|
||||
if (typeof loose !== "boolean") {
|
||||
throw new Error("@babel/preset-stage-1 'loose' option must be a boolean.");
|
||||
@ -28,11 +21,23 @@ export default declare((api, opts) => {
|
||||
"@babel/preset-stage-1 'useBuiltIns' option must be a boolean.",
|
||||
);
|
||||
}
|
||||
if (typeof decoratorsLegacy !== "boolean") {
|
||||
throw new Error(
|
||||
"@babel/preset-stage-1 'decoratorsLegacy' option must be a boolean.",
|
||||
);
|
||||
}
|
||||
|
||||
if (decoratorsLegacy !== true) {
|
||||
throw new Error(
|
||||
"The new decorators proposal is not supported yet." +
|
||||
' You muse pass the `"decoratorsLegacy": true` option to' +
|
||||
" @babel/preset-stage-1",
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
presets: [[presetStage2, { loose, useBuiltIns }]],
|
||||
presets: [[presetStage2, { loose, useBuiltIns, decoratorsLegacy }]],
|
||||
plugins: [
|
||||
transformDecorators,
|
||||
transformExportDefaultFrom,
|
||||
transformLogicalAssignmentOperators,
|
||||
[transformOptionalChaining, { loose }],
|
||||
|
||||
@ -58,6 +58,12 @@ Enable "loose" transformations for any plugins in this preset that allow them.
|
||||
|
||||
Will use the native built-in instead of trying to polyfill behavior for any plugins that require one.
|
||||
|
||||
### `decoratorsLegacy`
|
||||
|
||||
`boolean`, defaults to `false`.
|
||||
|
||||
Use the legacy (stage 1) decorators syntax and behavior.
|
||||
|
||||
## References
|
||||
|
||||
- Chapter "[The TC39 process for ECMAScript features](http://exploringjs.com/es2016-es2017/ch_tc39-process.html)" in "Exploring ES2016 and ES2017" by Axel Rauschmayer
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
"main": "lib/index.js",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "7.0.0-beta.44",
|
||||
"@babel/plugin-proposal-decorators": "7.0.0-beta.44",
|
||||
"@babel/plugin-proposal-export-namespace-from": "7.0.0-beta.44",
|
||||
"@babel/plugin-proposal-function-sent": "7.0.0-beta.44",
|
||||
"@babel/plugin-proposal-numeric-separator": "7.0.0-beta.44",
|
||||
|
||||
@ -1,21 +1,16 @@
|
||||
import { declare } from "@babel/helper-plugin-utils";
|
||||
import presetStage3 from "@babel/preset-stage-3";
|
||||
|
||||
import transformDecorators from "@babel/plugin-proposal-decorators";
|
||||
import transformFunctionSent from "@babel/plugin-proposal-function-sent";
|
||||
import transformExportNamespaceFrom from "@babel/plugin-proposal-export-namespace-from";
|
||||
import transformNumericSeparator from "@babel/plugin-proposal-numeric-separator";
|
||||
import transformThrowExpressions from "@babel/plugin-proposal-throw-expressions";
|
||||
|
||||
export default declare((api, opts) => {
|
||||
export default declare((api, opts = {}) => {
|
||||
api.assertVersion(7);
|
||||
|
||||
let loose = false;
|
||||
let useBuiltIns = false;
|
||||
|
||||
if (opts !== undefined) {
|
||||
if (opts.loose !== undefined) loose = opts.loose;
|
||||
if (opts.useBuiltIns !== undefined) useBuiltIns = opts.useBuiltIns;
|
||||
}
|
||||
const { loose = false, useBuiltIns = false, decoratorsLegacy = false } = opts;
|
||||
|
||||
if (typeof loose !== "boolean") {
|
||||
throw new Error("@babel/preset-stage-2 'loose' option must be a boolean.");
|
||||
@ -25,10 +20,24 @@ export default declare((api, opts) => {
|
||||
"@babel/preset-stage-2 'useBuiltIns' option must be a boolean.",
|
||||
);
|
||||
}
|
||||
if (typeof decoratorsLegacy !== "boolean") {
|
||||
throw new Error(
|
||||
"@babel/preset-stage-2 'decoratorsLegacy' option must be a boolean.",
|
||||
);
|
||||
}
|
||||
|
||||
if (decoratorsLegacy !== true) {
|
||||
throw new Error(
|
||||
"The new decorators proposal is not supported yet." +
|
||||
' You muse pass the `"decoratorsLegacy": true` option to' +
|
||||
" @babel/preset-stage-2",
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
presets: [[presetStage3, { loose, useBuiltIns }]],
|
||||
plugins: [
|
||||
[transformDecorators, { legacy: decoratorsLegacy }],
|
||||
transformFunctionSent,
|
||||
transformExportNamespaceFrom,
|
||||
transformNumericSeparator,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user