diff --git a/packages/babel-parser/README.md b/packages/babel-parser/README.md index 91f6fbe0f1..663ed0875f 100644 --- a/packages/babel-parser/README.md +++ b/packages/babel-parser/README.md @@ -127,7 +127,7 @@ require("@babel/parser").parse("code", { | `typescript` ([repo](https://github.com/Microsoft/TypeScript)) | `var a: string = "";` | | `doExpressions` | `var a = do { if (true) { 'hi'; } };` | | `objectRestSpread` ([proposal](https://github.com/tc39/proposal-object-rest-spread)) | `var a = { b, ...c };` | -| `decorators` (Stage 1) and `decorators2` (Stage 2 [proposal](https://github.com/tc39/proposal-decorators)) | `@a class A {}` | +| `decorators` (Stage 2 [proposal](https://github.com/tc39/proposal-decorators)) and `decorators-legacy` (Stage 1) | `@a class A {}` | | `classProperties` ([proposal](https://github.com/tc39/proposal-class-public-fields)) | `class A { b = 1; }` | | `classPrivateProperties` ([proposal](https://github.com/tc39/proposal-private-fields)) | `class A { #b = 1; }` | | `classPrivateMethods` ([proposal](https://github.com/tc39/proposal-private-methods)) | `class A { #c() {} }` | @@ -146,6 +146,25 @@ require("@babel/parser").parse("code", { | `pipelineOperator` ([proposal](https://github.com/babel/proposals/issues/29)) | `a \|> b` | | `nullishCoalescingOperator` ([proposal](https://github.com/babel/proposals/issues/14)) | `a ?? b` | + +#### Plugins options + +> NOTE: When a plugin is specified multiple times, only the first options are considered. + +- `decorators`: + - `decoratorsBeforeExport` (`boolean`) + ```js + // decoratorsBeforeExport: true + @dec + export class C {} + + // decoratorsBeforeExport: false + export @dec class C {} + ``` +- `flow`: + - `all` (`boolean`) + + ### FAQ #### Will the Babel parser support a plugin system? diff --git a/packages/babel-parser/src/parser/index.js b/packages/babel-parser/src/parser/index.js index a84603c41c..8042410ff6 100644 --- a/packages/babel-parser/src/parser/index.js +++ b/packages/babel-parser/src/parser/index.js @@ -33,8 +33,8 @@ export default class Parser extends StatementParser { function pluginsMap(plugins: PluginList): PluginsMap { const pluginMap: PluginsMap = (Object.create(null): Object); for (const plugin of plugins) { - if (Array.isArray(plugin)) pluginMap[plugin[0]] = plugin[1] || {}; - else pluginMap[plugin] = {}; + const [name, options = {}] = Array.isArray(plugin) ? plugin : [plugin, {}]; + if (!pluginMap[name]) pluginMap[name] = options || {}; } return pluginMap; } diff --git a/packages/babel-parser/test/plugin-options.js b/packages/babel-parser/test/plugin-options.js new file mode 100644 index 0000000000..a2f29026b6 --- /dev/null +++ b/packages/babel-parser/test/plugin-options.js @@ -0,0 +1,31 @@ +import { parse } from "../lib"; + +function getParser(code, plugins) { + return () => parse(code, { plugins, sourceType: "module" }); +} + +describe("plugin options", function() { + describe("the first options are used", 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(); + }); + + it("when they are specified", function() { + 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(); + expect(getParser(SYNTAX_2, [OPT_1, OPT_2])).toThrow(); + }); + }); +});