Skip to content

Commit

Permalink
Add an option to Babylon to have decorators before export (#7869)
Browse files Browse the repository at this point in the history
* Add support for plugin options in Babylon

They work similarly to how they work in Babel. e.g.
  babylon.parse({
    options: [
      "plugin1",
      ["plugin2", { option: true }]
    ]
  });

The inernal api to get an option is
  this.getPluginOption("pluginName", "option")
If the plugin isn't defined, it returns undefined.

* Add Babylon option decorators.secoratorsBeforeExport

* Nit
  • Loading branch information
nicolo-ribaudo authored and existentialism committed May 15, 2018
1 parent a955efa commit e45d5c3
Show file tree
Hide file tree
Showing 17 changed files with 490 additions and 25 deletions.
40 changes: 24 additions & 16 deletions packages/babylon/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,39 +67,39 @@ function getParserClass(
pluginsFromOptions: $ReadOnlyArray<string>,
): Class<Parser> {
if (
pluginsFromOptions.indexOf("decorators-legacy") >= 0 &&
pluginsFromOptions.indexOf("decorators") >= 0
hasPlugin(pluginsFromOptions, "decorators") &&
hasPlugin(pluginsFromOptions, "decorators-legacy")
) {
throw new Error("Cannot use decorators and decorators2 plugin together");
throw new Error(
"Cannot use the decorators and decorators-legacy plugin together",
);
}

// Filter out just the plugins that have an actual mixin associated with them.
let pluginList = pluginsFromOptions.filter(
p => p === "estree" || p === "flow" || p === "jsx" || p === "typescript",
);
let pluginList = pluginsFromOptions.filter(plugin => {
const p = getPluginName(plugin);
return p === "estree" || p === "flow" || p === "jsx" || p === "typescript";
});

if (pluginList.indexOf("flow") >= 0) {
if (hasPlugin(pluginList, "flow")) {
// ensure flow plugin loads last
pluginList = pluginList.filter(plugin => plugin !== "flow");
pluginList = pluginList.filter(p => getPluginName(p) !== "flow");
pluginList.push("flow");
}

if (
pluginList.indexOf("flow") >= 0 &&
pluginList.indexOf("typescript") >= 0
) {
if (hasPlugin(pluginList, "flow") && hasPlugin(pluginList, "typescript")) {
throw new Error("Cannot combine flow and typescript plugins.");
}

if (pluginList.indexOf("typescript") >= 0) {
if (hasPlugin(pluginList, "typescript")) {
// ensure typescript plugin loads last
pluginList = pluginList.filter(plugin => plugin !== "typescript");
pluginList = pluginList.filter(p => getPluginName(p) !== "typescript");
pluginList.push("typescript");
}

if (pluginList.indexOf("estree") >= 0) {
if (hasPlugin(pluginList, "estree")) {
// ensure estree plugin loads first
pluginList = pluginList.filter(plugin => plugin !== "estree");
pluginList = pluginList.filter(p => getPluginName(p) !== "estree");
pluginList.unshift("estree");
}

Expand All @@ -114,3 +114,11 @@ function getParserClass(
}
return cls;
}

function getPluginName(plugin) {
return Array.isArray(plugin) ? plugin[0] : plugin;
}

function hasPlugin(pluginsList, name) {
return pluginsList.some(plugin => getPluginName(plugin) === name);
}
6 changes: 5 additions & 1 deletion packages/babylon/src/parser/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ export default class BaseParser {
}

hasPlugin(name: string): boolean {
return !!this.plugins[name];
return Object.hasOwnProperty.call(this.plugins, name);
}

getPluginOption(plugin: string, name: string) {
if (this.hasPlugin(plugin)) return this.plugins[plugin][name];
}
}
7 changes: 4 additions & 3 deletions packages/babylon/src/parser/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ export default class Parser extends StatementParser {
function pluginsMap(
pluginList: $ReadOnlyArray<string>,
): { [key: string]: boolean } {
const pluginMap = {};
for (const name of pluginList) {
pluginMap[name] = true;
const pluginMap = Object.create(null);
for (const plugin of pluginList) {
const [name, options = {}] = Array.isArray(plugin) ? plugin : [plugin];
pluginMap[name] = options;
}
return pluginMap;
}
25 changes: 22 additions & 3 deletions packages/babylon/src/parser/statement.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,10 @@ export default class StatementParser extends ExpressionParser {
}

parseDecorators(allowExport?: boolean): void {
if (this.hasPlugin("decorators")) {
if (
this.hasPlugin("decorators") &&
!this.getPluginOption("decorators", "decoratorsBeforeExport")
) {
allowExport = false;
}

Expand Down Expand Up @@ -1422,6 +1425,12 @@ export default class StatementParser extends ExpressionParser {
} else if (this.match(tt._class)) {
return this.parseClass(expr, true, true);
} else if (this.match(tt.at)) {
if (
this.hasPlugin("decorators") &&
this.getPluginOption("decorators", "decoratorsBeforeExport")
) {
this.unexpected();
}
this.parseDecorators(false);
return this.parseClass(expr, true, true);
} else if (
Expand Down Expand Up @@ -1518,14 +1527,24 @@ export default class StatementParser extends ExpressionParser {
}

shouldParseExportDeclaration(): boolean {
if (this.match(tt.at)) {
this.expectOnePlugin(["decorators", "decorators-legacy"]);
if (this.hasPlugin("decorators")) {
if (this.getPluginOption("decorators", "decoratorsBeforeExport")) {
this.unexpected();
} else {
return true;
}
}
}

return (
this.state.type.keyword === "var" ||
this.state.type.keyword === "const" ||
this.state.type.keyword === "let" ||
this.state.type.keyword === "function" ||
this.state.type.keyword === "class" ||
this.isContextual("async") ||
(this.match(tt.at) && this.expectPlugin("decorators"))
this.isContextual("async")
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default (@decorator class Foo {})
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"sourceType": "module",
"plugins": [
["decorators", { "decoratorsBeforeExport": true }]
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
{
"type": "File",
"start": 0,
"end": 40,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 40
}
},
"program": {
"type": "Program",
"start": 0,
"end": 40,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 40
}
},
"sourceType": "module",
"body": [
{
"type": "ExportDefaultDeclaration",
"start": 0,
"end": 40,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 40
}
},
"declaration": {
"type": "ClassExpression",
"start": 16,
"end": 39,
"loc": {
"start": {
"line": 1,
"column": 16
},
"end": {
"line": 1,
"column": 39
}
},
"decorators": [
{
"type": "Decorator",
"start": 16,
"end": 26,
"loc": {
"start": {
"line": 1,
"column": 16
},
"end": {
"line": 1,
"column": 26
}
},
"callee": {
"type": "Identifier",
"start": 17,
"end": 26,
"loc": {
"start": {
"line": 1,
"column": 17
},
"end": {
"line": 1,
"column": 26
},
"identifierName": "decorator"
},
"name": "decorator"
}
}
],
"id": {
"type": "Identifier",
"start": 33,
"end": 36,
"loc": {
"start": {
"line": 1,
"column": 33
},
"end": {
"line": 1,
"column": 36
},
"identifierName": "Foo"
},
"name": "Foo"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start": 37,
"end": 39,
"loc": {
"start": {
"line": 1,
"column": 37
},
"end": {
"line": 1,
"column": 39
}
},
"body": []
},
"extra": {
"parenthesized": true,
"parenStart": 15
}
}
}
],
"directives": []
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default @decorator class Foo {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"sourceType": "module",
"plugins": [
["decorators", { "decoratorsBeforeExport": true }]
],
"throws": "Unexpected token (1:15)"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@decorator
export default class Foo {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"sourceType": "module",
"plugins": [
["decorators", { "decoratorsBeforeExport": true }]
]
}
Loading

0 comments on commit e45d5c3

Please sign in to comment.