diff --git a/packages/babel-core/test/parse.js b/packages/babel-core/test/parse.js index ed5c95b4f3..ec36951cb0 100644 --- a/packages/babel-core/test/parse.js +++ b/packages/babel-core/test/parse.js @@ -23,7 +23,9 @@ describe("parse", function() { const output = require(fixture("output.json")); const result = parse(input, { - parserOpts: { plugins: ["decorators"] }, + parserOpts: { + plugins: [["decorators", { decoratorsBeforeExport: false }]], + }, cwd: fixture(), }); expect(JSON.parse(JSON.stringify(result))).toEqual(output); diff --git a/packages/babel-parser/src/plugin-utils.js b/packages/babel-parser/src/plugin-utils.js index 69cc39f7ac..8260bdc00c 100644 --- a/packages/babel-parser/src/plugin-utils.js +++ b/packages/babel-parser/src/plugin-utils.js @@ -41,13 +41,26 @@ export function getPluginOption( const PIPELINE_PROPOSALS = ["minimal"]; export function validatePlugins(plugins: PluginList) { - if ( - hasPlugin(plugins, "decorators") && - hasPlugin(plugins, "decorators-legacy") - ) { - throw new Error( - "Cannot use the decorators and decorators-legacy plugin together", + if (hasPlugin(plugins, "decorators")) { + if (hasPlugin(plugins, "decorators-legacy")) { + throw new Error( + "Cannot use the decorators and decorators-legacy plugin together", + ); + } + + const decoratorsBeforeExport = getPluginOption( + plugins, + "decorators", + "decoratorsBeforeExport", ); + if (decoratorsBeforeExport == null) { + throw new Error( + "The 'decorators' plugin requires a" + + " 'decoratorsBeforeExport' option, whose value must be a boolean.", + ); + } else if (typeof decoratorsBeforeExport !== "boolean") { + throw new Error("'decoratorsBeforeExport' must be a boolean."); + } } if (hasPlugin(plugins, "flow") && hasPlugin(plugins, "typescript")) { diff --git a/packages/babel-parser/test/fixtures/experimental/decorators-2/class-property/options.json b/packages/babel-parser/test/fixtures/experimental/decorators-2/class-property/options.json index 806b2fc7ca..21fa2f7d9b 100644 --- a/packages/babel-parser/test/fixtures/experimental/decorators-2/class-property/options.json +++ b/packages/babel-parser/test/fixtures/experimental/decorators-2/class-property/options.json @@ -1,3 +1,7 @@ { - "plugins": ["classProperties", "classPrivateProperties", "decorators"] + "plugins": [ + "classProperties", + "classPrivateProperties", + ["decorators", { "decoratorsBeforeExport": false }] + ] } diff --git a/packages/babel-parser/test/fixtures/experimental/decorators-2/compued-property/options.json b/packages/babel-parser/test/fixtures/experimental/decorators-2/compued-property/options.json index 49038f416f..1ec7ff9a42 100644 --- a/packages/babel-parser/test/fixtures/experimental/decorators-2/compued-property/options.json +++ b/packages/babel-parser/test/fixtures/experimental/decorators-2/compued-property/options.json @@ -1,3 +1,6 @@ { - "plugins": ["decorators", "classProperties"] + "plugins": [ + ["decorators", { "decoratorsBeforeExport": false }], + "classProperties" + ] } diff --git a/packages/babel-parser/test/fixtures/experimental/decorators-2/decoratorsBeforeExport-boolean/input.js b/packages/babel-parser/test/fixtures/experimental/decorators-2/decoratorsBeforeExport-boolean/input.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/babel-parser/test/fixtures/experimental/decorators-2/decoratorsBeforeExport-boolean/options.json b/packages/babel-parser/test/fixtures/experimental/decorators-2/decoratorsBeforeExport-boolean/options.json new file mode 100644 index 0000000000..30762d985c --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/decorators-2/decoratorsBeforeExport-boolean/options.json @@ -0,0 +1,6 @@ +{ + "plugins": [ + ["decorators", { "decoratorsBeforeExport": "yes" }] + ], + "throws": "'decoratorsBeforeExport' must be a boolean." +} diff --git a/packages/babel-parser/test/fixtures/experimental/decorators-2/decoratorsBeforeExport-required/input.js b/packages/babel-parser/test/fixtures/experimental/decorators-2/decoratorsBeforeExport-required/input.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/babel-parser/test/fixtures/experimental/decorators-2/decoratorsBeforeExport-required/options.json b/packages/babel-parser/test/fixtures/experimental/decorators-2/decoratorsBeforeExport-required/options.json new file mode 100644 index 0000000000..6e76b5fb33 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/decorators-2/decoratorsBeforeExport-required/options.json @@ -0,0 +1,4 @@ +{ + "plugins": ["decorators"], + "throws": "The 'decorators' plugin requires a 'decoratorsBeforeExport' option, whose value must be a boolean." +} diff --git a/packages/babel-parser/test/fixtures/experimental/decorators-2/options.json b/packages/babel-parser/test/fixtures/experimental/decorators-2/options.json index d760b0afda..14ae2b56d3 100644 --- a/packages/babel-parser/test/fixtures/experimental/decorators-2/options.json +++ b/packages/babel-parser/test/fixtures/experimental/decorators-2/options.json @@ -1,3 +1,5 @@ { - "plugins": ["decorators"] + "plugins": [ + ["decorators", { "decoratorsBeforeExport": false }] + ] } diff --git a/packages/babel-parser/test/fixtures/experimental/decorators-2/private-property/options.json b/packages/babel-parser/test/fixtures/experimental/decorators-2/private-property/options.json index 806b2fc7ca..21fa2f7d9b 100644 --- a/packages/babel-parser/test/fixtures/experimental/decorators-2/private-property/options.json +++ b/packages/babel-parser/test/fixtures/experimental/decorators-2/private-property/options.json @@ -1,3 +1,7 @@ { - "plugins": ["classProperties", "classPrivateProperties", "decorators"] + "plugins": [ + "classProperties", + "classPrivateProperties", + ["decorators", { "decoratorsBeforeExport": false }] + ] } diff --git a/packages/babel-parser/test/fixtures/experimental/decorators-2/static-property/options.json b/packages/babel-parser/test/fixtures/experimental/decorators-2/static-property/options.json index b508cf45fd..4ee4c94460 100644 --- a/packages/babel-parser/test/fixtures/experimental/decorators-2/static-property/options.json +++ b/packages/babel-parser/test/fixtures/experimental/decorators-2/static-property/options.json @@ -1,3 +1,6 @@ { - "plugins": ["classProperties", "decorators"] + "plugins": [ + "classProperties", + ["decorators", { "decoratorsBeforeExport": false }] + ] } diff --git a/packages/babel-parser/test/plugin-options.js b/packages/babel-parser/test/plugin-options.js index a2f29026b6..44e6cb26ae 100644 --- a/packages/babel-parser/test/plugin-options.js +++ b/packages/babel-parser/test/plugin-options.js @@ -9,19 +9,56 @@ describe("plugin options", function() { // NOTE: This test is not specific about decorators, it can be applied // to any plugin with options. - const NAME = "decorators"; - const OPT_1 = [NAME, { decoratorsBeforeExport: true }]; - const OPT_2 = [NAME, { decoratorsBeforeExport: false }]; - const SYNTAX_1 = "@dec export class C {}"; - const SYNTAX_2 = "export @dec class C {}"; - const SYNTAX_DEFAULT = "export @dec class C {}"; - it("when they aren't specified", function() { - expect(getParser(SYNTAX_DEFAULT, [NAME, OPT_1])).not.toThrow(); - expect(getParser(SYNTAX_DEFAULT, [NAME, OPT_2])).not.toThrow(); + const WITHOUT_FLAG = "flow"; + const WITH_FLAG = ["flow", { all: true }]; + + const CODE = "new Foo(y)"; + + const AST_WITHOUT_FLAG = { + type: "BinaryExpression", + operator: ">", + left: { + type: "BinaryExpression", + operator: "<", + left: { type: "NewExpression" }, + right: { type: "Identifier" }, + }, + right: { type: "Identifier", extra: { parenthesized: true } }, + }; + + const AST_WITH_FLAG = { + type: "NewExpression", + callee: { type: "Identifier" }, + arguments: [{ type: "Identifier" }], + typeArguments: { + type: "TypeParameterInstantiation", + params: [ + { type: "GenericTypeAnnotation", id: { type: "Identifier" } }, + ], + }, + }; + + expect( + getParser(CODE, [WITHOUT_FLAG, WITH_FLAG])().program.body[0].expression, + ).toMatchObject(AST_WITHOUT_FLAG); + + expect( + getParser(CODE, [WITHOUT_FLAG])().program.body[0].expression, + ).toMatchObject(AST_WITHOUT_FLAG); + + expect( + getParser(CODE, [WITH_FLAG])().program.body[0].expression, + ).toMatchObject(AST_WITH_FLAG); }); it("when they are specified", function() { + const NAME = "decorators"; + const OPT_1 = [NAME, { decoratorsBeforeExport: true }]; + const OPT_2 = [NAME, { decoratorsBeforeExport: false }]; + const SYNTAX_1 = "@dec export class C {}"; + const SYNTAX_2 = "export @dec class C {}"; + expect(getParser(SYNTAX_1, [OPT_1, OPT_2])).not.toThrow(); expect(getParser(SYNTAX_2, [OPT_2, OPT_1])).not.toThrow(); expect(getParser(SYNTAX_1, [OPT_2, OPT_1])).toThrow(); diff --git a/packages/babel-plugin-syntax-decorators/src/index.js b/packages/babel-plugin-syntax-decorators/src/index.js index 7c2a454c94..1a73a63c88 100644 --- a/packages/babel-plugin-syntax-decorators/src/index.js +++ b/packages/babel-plugin-syntax-decorators/src/index.js @@ -17,7 +17,14 @@ export default declare((api, options) => { } const { decoratorsBeforeExport } = options; - if (decoratorsBeforeExport !== undefined) { + if (decoratorsBeforeExport === undefined) { + if (!legacy) { + throw new Error( + "The '@babel/plugin-syntax-decorators' plugin requires a" + + " 'decoratorsBeforeExport' option, whose value must be a boolean.", + ); + } + } else { if (legacy) { throw new Error( "'decoratorsBeforeExport' can't be used with legacy decorators.", diff --git a/packages/babel-plugin-syntax-decorators/test/index.js b/packages/babel-plugin-syntax-decorators/test/index.js index 4ff1679172..38106f6288 100644 --- a/packages/babel-plugin-syntax-decorators/test/index.js +++ b/packages/babel-plugin-syntax-decorators/test/index.js @@ -37,6 +37,10 @@ describe("'decoratorsBeforeExport' option", function() { expect(makeParser("", { decoratorsBeforeExport: "before" })).toThrow(); }); + test.skip("is required", function() { + expect(makeParser("", { legacy: false })).toThrow(/decoratorsBeforeExport/); + }); + test("is incompatible with legacy", function() { expect( makeParser("", { decoratorsBeforeExport: false, legacy: true }), @@ -47,8 +51,6 @@ describe("'decoratorsBeforeExport' option", function() { const AFTER = "export @dec class Foo {}"; // These are skipped - run(BEFORE, undefined, true); - run(AFTER, undefined, false); run(BEFORE, true, false); run(AFTER, true, true); run(BEFORE, false, true);