Add an option to Babylon to have decorators before export (#7869)

* 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
This commit is contained in:
Nicolò Ribaudo 2018-05-15 18:39:54 +02:00 committed by Brian Ng
parent a955efa3e3
commit e45d5c3b65
17 changed files with 490 additions and 25 deletions

View File

@ -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");
}
@ -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);
}

View File

@ -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];
}
}

View File

@ -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;
}

View File

@ -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;
}
@ -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 (
@ -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")
);
}

View File

@ -0,0 +1,6 @@
{
"sourceType": "module",
"plugins": [
["decorators", { "decoratorsBeforeExport": true }]
]
}

View File

@ -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": []
}
}

View File

@ -0,0 +1,7 @@
{
"sourceType": "module",
"plugins": [
["decorators", { "decoratorsBeforeExport": true }]
],
"throws": "Unexpected token (1:15)"
}

View File

@ -0,0 +1,2 @@
@decorator
export default class Foo {}

View File

@ -0,0 +1,6 @@
{
"sourceType": "module",
"plugins": [
["decorators", { "decoratorsBeforeExport": true }]
]
}

View File

@ -0,0 +1,132 @@
{
"type": "File",
"start": 0,
"end": 38,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 2,
"column": 27
}
},
"program": {
"type": "Program",
"start": 0,
"end": 38,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 2,
"column": 27
}
},
"sourceType": "module",
"body": [
{
"type": "ExportDefaultDeclaration",
"start": 11,
"end": 38,
"loc": {
"start": {
"line": 2,
"column": 0
},
"end": {
"line": 2,
"column": 27
}
},
"declaration": {
"type": "ClassDeclaration",
"start": 0,
"end": 38,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 2,
"column": 27
}
},
"decorators": [
{
"type": "Decorator",
"start": 0,
"end": 10,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 10
}
},
"callee": {
"type": "Identifier",
"start": 1,
"end": 10,
"loc": {
"start": {
"line": 1,
"column": 1
},
"end": {
"line": 1,
"column": 10
},
"identifierName": "decorator"
},
"name": "decorator"
}
}
],
"id": {
"type": "Identifier",
"start": 32,
"end": 35,
"loc": {
"start": {
"line": 2,
"column": 21
},
"end": {
"line": 2,
"column": 24
},
"identifierName": "Foo"
},
"name": "Foo"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start": 36,
"end": 38,
"loc": {
"start": {
"line": 2,
"column": 25
},
"end": {
"line": 2,
"column": 27
}
},
"body": []
}
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,2 @@
@decorator
export class Foo {}

View File

@ -0,0 +1,6 @@
{
"sourceType": "module",
"plugins": [
["decorators", { "decoratorsBeforeExport": true }]
]
}

View File

@ -0,0 +1,134 @@
{
"type": "File",
"start": 0,
"end": 30,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 2,
"column": 19
}
},
"program": {
"type": "Program",
"start": 0,
"end": 30,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 2,
"column": 19
}
},
"sourceType": "module",
"body": [
{
"type": "ExportNamedDeclaration",
"start": 11,
"end": 30,
"loc": {
"start": {
"line": 2,
"column": 0
},
"end": {
"line": 2,
"column": 19
}
},
"specifiers": [],
"source": null,
"declaration": {
"type": "ClassDeclaration",
"start": 0,
"end": 30,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 2,
"column": 19
}
},
"decorators": [
{
"type": "Decorator",
"start": 0,
"end": 10,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 10
}
},
"callee": {
"type": "Identifier",
"start": 1,
"end": 10,
"loc": {
"start": {
"line": 1,
"column": 1
},
"end": {
"line": 1,
"column": 10
},
"identifierName": "decorator"
},
"name": "decorator"
}
}
],
"id": {
"type": "Identifier",
"start": 24,
"end": 27,
"loc": {
"start": {
"line": 2,
"column": 13
},
"end": {
"line": 2,
"column": 16
},
"identifierName": "Foo"
},
"name": "Foo"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start": 28,
"end": 30,
"loc": {
"start": {
"line": 2,
"column": 17
},
"end": {
"line": 2,
"column": 19
}
},
"body": []
}
}
}
],
"directives": []
}
}

View File

@ -1,5 +1,5 @@
{
"sourceType": "module",
"throws": "This experimental syntax requires enabling the parser plugin: 'decorators' (1:7)",
"throws": "This experimental syntax requires enabling one of the following parser plugin(s): 'decorators, decorators-legacy' (1:7)",
"plugins": null
}

View File

@ -1,4 +1,4 @@
{
"plugins": ["decorators-legacy", "decorators"],
"throws": "Cannot use decorators and decorators2 plugin together"
"throws": "Cannot use the decorators and decorators-legacy plugin together"
}