diff --git a/packages/babel-generator/src/generators/classes.js b/packages/babel-generator/src/generators/classes.js index da1b14f78e..4e27f2fe12 100644 --- a/packages/babel-generator/src/generators/classes.js +++ b/packages/babel-generator/src/generators/classes.js @@ -2,8 +2,9 @@ import * as t from "@babel/types"; export function ClassDeclaration(node: Object, parent: Object) { if ( - !t.isExportDefaultDeclaration(parent) && - !t.isExportNamedDeclaration(parent) + !this.format.decoratorsBeforeExport || + (!t.isExportDefaultDeclaration(parent) && + !t.isExportNamedDeclaration(parent)) ) { this.printJoin(node.decorators, node); } diff --git a/packages/babel-generator/src/generators/modules.js b/packages/babel-generator/src/generators/modules.js index dd7313ac7c..b081a2882d 100644 --- a/packages/babel-generator/src/generators/modules.js +++ b/packages/babel-generator/src/generators/modules.js @@ -57,7 +57,10 @@ export function ExportAllDeclaration(node: Object) { } export function ExportNamedDeclaration(node: Object) { - if (t.isClassDeclaration(node.declaration)) { + if ( + this.format.decoratorsBeforeExport && + t.isClassDeclaration(node.declaration) + ) { this.printJoin(node.declaration.decorators, node); } @@ -67,7 +70,10 @@ export function ExportNamedDeclaration(node: Object) { } export function ExportDefaultDeclaration(node: Object) { - if (t.isClassDeclaration(node.declaration)) { + if ( + this.format.decoratorsBeforeExport && + t.isClassDeclaration(node.declaration) + ) { this.printJoin(node.declaration.decorators, node); } diff --git a/packages/babel-generator/src/index.js b/packages/babel-generator/src/index.js index 3862789ba2..33806375ee 100644 --- a/packages/babel-generator/src/index.js +++ b/packages/babel-generator/src/index.js @@ -52,6 +52,10 @@ function normalizeOptions(code, opts): Format { style: " ", base: 0, }, + decoratorsBeforeExport: + opts.decoratorsBeforeExport === undefined + ? true + : opts.decoratorsBeforeExport, }; if (format.minified) { diff --git a/packages/babel-generator/src/printer.js b/packages/babel-generator/src/printer.js index e9bbf212cd..93dd318991 100644 --- a/packages/babel-generator/src/printer.js +++ b/packages/babel-generator/src/printer.js @@ -25,6 +25,7 @@ export type Format = { style: string, base: number, }, + decoratorsBeforeExport: boolean, }; export default class Printer { diff --git a/packages/babel-generator/test/fixtures/decoratorsBeforeExport/false-to-false/input.js b/packages/babel-generator/test/fixtures/decoratorsBeforeExport/false-to-false/input.js new file mode 100644 index 0000000000..61d49cad5f --- /dev/null +++ b/packages/babel-generator/test/fixtures/decoratorsBeforeExport/false-to-false/input.js @@ -0,0 +1,3 @@ +export default @dec class Foo {} + +export @dec class Bar {} diff --git a/packages/babel-generator/test/fixtures/decoratorsBeforeExport/false-to-false/options.json b/packages/babel-generator/test/fixtures/decoratorsBeforeExport/false-to-false/options.json new file mode 100644 index 0000000000..f4f6d04ce8 --- /dev/null +++ b/packages/babel-generator/test/fixtures/decoratorsBeforeExport/false-to-false/options.json @@ -0,0 +1,6 @@ +{ + "plugins": [ + ["decorators", { "decoratorsBeforeExport": false }] + ], + "decoratorsBeforeExport": false +} diff --git a/packages/babel-generator/test/fixtures/decoratorsBeforeExport/false-to-false/output.js b/packages/babel-generator/test/fixtures/decoratorsBeforeExport/false-to-false/output.js new file mode 100644 index 0000000000..7762319b58 --- /dev/null +++ b/packages/babel-generator/test/fixtures/decoratorsBeforeExport/false-to-false/output.js @@ -0,0 +1,4 @@ +export default @dec +class Foo {} +export @dec +class Bar {} \ No newline at end of file diff --git a/packages/babel-generator/test/fixtures/decoratorsBeforeExport/false-to-true/input.js b/packages/babel-generator/test/fixtures/decoratorsBeforeExport/false-to-true/input.js new file mode 100644 index 0000000000..61d49cad5f --- /dev/null +++ b/packages/babel-generator/test/fixtures/decoratorsBeforeExport/false-to-true/input.js @@ -0,0 +1,3 @@ +export default @dec class Foo {} + +export @dec class Bar {} diff --git a/packages/babel-generator/test/fixtures/decoratorsBeforeExport/false-to-true/options.json b/packages/babel-generator/test/fixtures/decoratorsBeforeExport/false-to-true/options.json new file mode 100644 index 0000000000..c05b74601a --- /dev/null +++ b/packages/babel-generator/test/fixtures/decoratorsBeforeExport/false-to-true/options.json @@ -0,0 +1,6 @@ +{ + "plugins": [ + ["decorators", { "decoratorsBeforeExport": false }] + ], + "decoratorsBeforeExport": true +} diff --git a/packages/babel-generator/test/fixtures/decoratorsBeforeExport/false-to-true/output.js b/packages/babel-generator/test/fixtures/decoratorsBeforeExport/false-to-true/output.js new file mode 100644 index 0000000000..95fbf7e023 --- /dev/null +++ b/packages/babel-generator/test/fixtures/decoratorsBeforeExport/false-to-true/output.js @@ -0,0 +1,4 @@ +@dec +export default class Foo {} +@dec +export class Bar {} \ No newline at end of file diff --git a/packages/babel-generator/test/fixtures/decoratorsBeforeExport/true-to-false/input.js b/packages/babel-generator/test/fixtures/decoratorsBeforeExport/true-to-false/input.js new file mode 100644 index 0000000000..545665144d --- /dev/null +++ b/packages/babel-generator/test/fixtures/decoratorsBeforeExport/true-to-false/input.js @@ -0,0 +1,5 @@ +@dec +export default class Foo {} + +@dec +export class Bar {} diff --git a/packages/babel-generator/test/fixtures/decoratorsBeforeExport/true-to-false/options.json b/packages/babel-generator/test/fixtures/decoratorsBeforeExport/true-to-false/options.json new file mode 100644 index 0000000000..c8764bcb18 --- /dev/null +++ b/packages/babel-generator/test/fixtures/decoratorsBeforeExport/true-to-false/options.json @@ -0,0 +1,6 @@ +{ + "plugins": [ + ["decorators", { "decoratorsBeforeExport": true }] + ], + "decoratorsBeforeExport": false +} diff --git a/packages/babel-generator/test/fixtures/decoratorsBeforeExport/true-to-false/output.js b/packages/babel-generator/test/fixtures/decoratorsBeforeExport/true-to-false/output.js new file mode 100644 index 0000000000..7762319b58 --- /dev/null +++ b/packages/babel-generator/test/fixtures/decoratorsBeforeExport/true-to-false/output.js @@ -0,0 +1,4 @@ +export default @dec +class Foo {} +export @dec +class Bar {} \ No newline at end of file diff --git a/packages/babel-generator/test/fixtures/decoratorsBeforeExport/true-to-true/input.js b/packages/babel-generator/test/fixtures/decoratorsBeforeExport/true-to-true/input.js new file mode 100644 index 0000000000..545665144d --- /dev/null +++ b/packages/babel-generator/test/fixtures/decoratorsBeforeExport/true-to-true/input.js @@ -0,0 +1,5 @@ +@dec +export default class Foo {} + +@dec +export class Bar {} diff --git a/packages/babel-generator/test/fixtures/decoratorsBeforeExport/true-to-true/options.json b/packages/babel-generator/test/fixtures/decoratorsBeforeExport/true-to-true/options.json new file mode 100644 index 0000000000..cae43e34e2 --- /dev/null +++ b/packages/babel-generator/test/fixtures/decoratorsBeforeExport/true-to-true/options.json @@ -0,0 +1,6 @@ +{ + "plugins": [ + ["decorators", { "decoratorsBeforeExport": true }] + ], + "decoratorsBeforeExport": true +} diff --git a/packages/babel-generator/test/fixtures/decoratorsBeforeExport/true-to-true/output.js b/packages/babel-generator/test/fixtures/decoratorsBeforeExport/true-to-true/output.js new file mode 100644 index 0000000000..95fbf7e023 --- /dev/null +++ b/packages/babel-generator/test/fixtures/decoratorsBeforeExport/true-to-true/output.js @@ -0,0 +1,4 @@ +@dec +export default class Foo {} +@dec +export class Bar {} \ No newline at end of file diff --git a/packages/babel-plugin-proposal-decorators/README.md b/packages/babel-plugin-proposal-decorators/README.md index b5bf1a1b20..3411b3708d 100644 --- a/packages/babel-plugin-proposal-decorators/README.md +++ b/packages/babel-plugin-proposal-decorators/README.md @@ -78,6 +78,23 @@ require("@babel/core").transform("code", { ## Options +### `decoratorsBeforeExport` + +`boolean`, defaults to `true`. + +```js +// decoratorsBeforeExport: true +@decorator +export class Foo {} + +// decoratorsBeforeExport: false +export @decorator class Bar {} +``` + +This option was added to help tc39 collect feedback from the community by allowing experimentation with both possible syntaxes. + +For more information, check out: [tc39/proposal-decorators#69](https://github.com/tc39/proposal-decorators/issues/69). + ### `legacy` `boolean`, defaults to `false`. diff --git a/packages/babel-plugin-proposal-decorators/src/index.js b/packages/babel-plugin-proposal-decorators/src/index.js index 51d4a124d3..3c242ec4a0 100644 --- a/packages/babel-plugin-proposal-decorators/src/index.js +++ b/packages/babel-plugin-proposal-decorators/src/index.js @@ -6,7 +6,7 @@ import legacyVisitor from "./transformer-legacy"; export default declare((api, options) => { api.assertVersion(7); - const { legacy = false } = options; + const { legacy = false, decoratorsBeforeExport } = options; if (typeof legacy !== "boolean") { throw new Error("'legacy' must be a boolean."); } @@ -19,9 +19,24 @@ export default declare((api, options) => { ); } + if (decoratorsBeforeExport !== undefined) { + if (legacy) { + throw new Error( + "'decoratorsBeforeExport' can't be used with legacy decorators.", + ); + } + if (typeof decoratorsBeforeExport !== "boolean") { + throw new Error("'decoratorsBeforeExport' must be a boolean."); + } + } + return { inherits: syntaxDecorators, + manipulateOptions({ generatorOpts }) { + generatorOpts.decoratorsBeforeExport = decoratorsBeforeExport; + }, + visitor: legacy ? legacyVisitor : visitor, }; });