Skip to content

Commit de28707

Browse files
[babel 8] Align allow* parser options with ESLint behavior (#13921)
* [babel 8] Align `allow*` parser options with ESLint behavior - The `@babel/eslint-parser` `allowImportExportEverywhere` option is removed; users can pass it to `parserOpts` - `allowSuperOutsideMethod` is disabled - `allowReturnOutsideFunction` is inferred from `ecmaFeatures.globalReturn` * Update failing tests
1 parent 531db5d commit de28707

6 files changed

Lines changed: 167 additions & 34 deletions

File tree

eslint/babel-eslint-parser/README.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ Additional configuration options can be set in your ESLint configuration under t
5151

5252
- `requireConfigFile` (default `true`) can be set to `false` to allow @babel/eslint-parser to run on files that do not have a Babel configuration associated with them. This can be useful for linting files that are not transformed by Babel (such as tooling configuration files), though we recommend using the default parser via [glob-based configuration](https://eslint.org/docs/user-guide/configuring/configuration-files#configuration-based-on-glob-patterns). Note: @babel/eslint-parser will not parse any experimental syntax when no configuration file is found.
5353
- `sourceType` can be set to `"module"`(default) or `"script"` if your code isn't using ECMAScript modules.
54+
<!-- TODO(Babel 8): Remove this -->
5455
- `allowImportExportEverywhere` (default `false`) can be set to `true` to allow import and export declarations to appear anywhere a statement is allowed if your build environment supports that. Otherwise import and export declarations can only appear at a program's top level.
5556
- `ecmaFeatures.globalReturn` (default `false`) allow return statements in the global scope when used with `sourceType: "script"`.
5657
- `babelOptions` is an object containing Babel configuration [options](https://babeljs.io/docs/en/options) that are passed to Babel's parser at runtime. For cases where users might not want to use a Babel configuration file or are running Babel through another tool (such as Webpack with `babel-loader`).
@@ -97,13 +98,13 @@ This configuration is useful for monorepo, when you are running ESLint on every
9798

9899
```js
99100
module.exports = {
100-
"parser": "@babel/eslint-parser",
101-
"parserOptions": {
102-
"babelOptions": {
103-
"rootMode": "upward"
104-
}
105-
}
106-
}
101+
parser: "@babel/eslint-parser",
102+
parserOptions: {
103+
babelOptions: {
104+
rootMode: "upward",
105+
},
106+
},
107+
};
107108
```
108109

109110
### Run

eslint/babel-eslint-parser/src/analyze-scope.cjs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,8 +340,7 @@ module.exports = function analyzeScope(ast, parserOptions, client) {
340340
directive: false,
341341
nodejsScope:
342342
ast.sourceType === "script" &&
343-
(parserOptions.ecmaFeatures &&
344-
parserOptions.ecmaFeatures.globalReturn) === true,
343+
parserOptions.ecmaFeatures?.globalReturn === true,
345344
impliedStrict: false,
346345
sourceType: ast.sourceType,
347346
ecmaVersion: parserOptions.ecmaVersion,

eslint/babel-eslint-parser/src/configuration.cjs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ exports.normalizeESLintConfig = function (options) {
44
// ESLint sets ecmaVersion: undefined when ecmaVersion is not set in the config.
55
ecmaVersion = 2020,
66
sourceType = "module",
7-
allowImportExportEverywhere = false,
87
requireConfigFile = true,
98
...otherOptions
109
} = options;
@@ -13,7 +12,6 @@ exports.normalizeESLintConfig = function (options) {
1312
babelOptions: { cwd: process.cwd(), ...babelOptions },
1413
ecmaVersion: ecmaVersion === "latest" ? 1e8 : ecmaVersion,
1514
sourceType,
16-
allowImportExportEverywhere,
1715
requireConfigFile,
1816
...otherOptions,
1917
};

eslint/babel-eslint-parser/src/worker/configuration.cjs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,16 @@ function normalizeParserOptions(options) {
2626
filename: options.filePath,
2727
...options.babelOptions,
2828
parserOpts: {
29-
allowImportExportEverywhere: options.allowImportExportEverywhere,
30-
allowReturnOutsideFunction: true,
31-
allowSuperOutsideMethod: true,
29+
...(process.env.BABEL_8_BREAKING
30+
? {}
31+
: {
32+
allowImportExportEverywhere:
33+
options.allowImportExportEverywhere ?? false,
34+
allowSuperOutsideMethod: true,
35+
}),
36+
allowReturnOutsideFunction:
37+
options.ecmaFeatures?.globalReturn ??
38+
(process.env.BABEL_8_BREAKING ? false : true),
3239
...options.babelOptions.parserOpts,
3340
plugins: getParserPlugins(options.babelOptions),
3441
// skip comment attaching for parsing performance

eslint/babel-eslint-parser/test/index.js

Lines changed: 118 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,6 @@ describe("Babel and Espree", () => {
6868
globalReturn: true,
6969
// enable implied strict mode (if ecmaVersion >= 5)
7070
impliedStrict: true,
71-
// allow experimental object rest/spread
72-
experimentalObjectRestSpread: true,
7371
},
7472
tokens: true,
7573
loc: true,
@@ -78,7 +76,11 @@ describe("Babel and Espree", () => {
7876
sourceType: "module",
7977
};
8078

81-
function parseAndAssertSame(code, /* optional */ eslintVersion) {
79+
function parseAndAssertSame(
80+
code,
81+
eslintVersion = undefined,
82+
babelEcmaFeatures = null,
83+
) {
8284
code = unpad(code);
8385

8486
if (eslintVersion !== 8) {
@@ -91,6 +93,7 @@ describe("Babel and Espree", () => {
9193
eslintVisitorKeys: true,
9294
eslintScopeManager: true,
9395
babelOptions: BABEL_OPTIONS,
96+
ecmaFeatures: babelEcmaFeatures,
9497
}).ast;
9598

9699
deeplyRemoveProperties(babelAST, PROPS_TO_REMOVE);
@@ -113,6 +116,7 @@ describe("Babel and Espree", () => {
113116
eslintVisitorKeys: true,
114117
eslintScopeManager: true,
115118
babelOptions: BABEL_OPTIONS,
119+
ecmaFeatures: babelEcmaFeatures,
116120
}).ast;
117121

118122
deeplyRemoveProperties(babelAST, PROPS_TO_REMOVE);
@@ -851,24 +855,122 @@ describe("Babel and Espree", () => {
851855

852856
it("do not allow import export everywhere", () => {
853857
expect(() => {
854-
parseAndAssertSame('function F() { import a from "a"; }');
855-
}).toThrow(
856-
new SyntaxError(
857-
"'import' and 'export' may only appear at the top level",
858-
),
859-
);
860-
});
861-
862-
it("return outside function", () => {
863-
parseAndAssertSame("return;");
858+
parseForESLint('function F() { import a from "a"; }', {
859+
babelOptions: BABEL_OPTIONS,
860+
});
861+
}).toThrow(/'import' and 'export' may only appear at the top level/);
864862
});
865863

866-
it("super outside method", () => {
864+
it("allowImportExportEverywhere", () => {
867865
expect(() => {
868-
parseAndAssertSame("function F() { super(); }");
869-
}).toThrow(new SyntaxError("'super' keyword outside a method"));
866+
parseForESLint('function F() { import a from "a"; }', {
867+
babelOptions: {
868+
...BABEL_OPTIONS,
869+
parserOpts: {
870+
allowImportExportEverywhere: true,
871+
},
872+
},
873+
});
874+
}).not.toThrow();
870875
});
871876

877+
if (!process.env.BABEL_8_BREAKING) {
878+
it("top-level allowImportExportEverywhere", () => {
879+
expect(() => {
880+
parseForESLint('function F() { import a from "a"; }', {
881+
babelOptions: BABEL_OPTIONS,
882+
allowImportExportEverywhere: true,
883+
});
884+
}).not.toThrow();
885+
});
886+
}
887+
888+
if (process.env.BABEL_8_BREAKING) {
889+
it("return outside function with ecmaFeatures.globalReturn: true", () => {
890+
parseAndAssertSame("return;", /* version */ undefined, {
891+
globalReturn: true,
892+
});
893+
});
894+
895+
it("return outside function with ecmaFeatures.globalReturn: false", () => {
896+
expect(() =>
897+
parseForESLint("return;", {
898+
babelOptions: BABEL_OPTIONS,
899+
ecmaVersion: { globalReturn: false },
900+
}),
901+
).toThrow(new SyntaxError("'return' outside of function. (1:0)"));
902+
903+
expect(() =>
904+
parseForESLint8("return;", {
905+
babelOptions: BABEL_OPTIONS,
906+
ecmaVersion: { globalReturn: false },
907+
}),
908+
).toThrow(new SyntaxError("'return' outside of function. (1:0)"));
909+
});
910+
911+
it("return outside function without ecmaFeatures.globalReturn", () => {
912+
expect(() =>
913+
parseForESLint("return;", { babelOptions: BABEL_OPTIONS }),
914+
).toThrow(new SyntaxError("'return' outside of function. (1:0)"));
915+
916+
expect(() =>
917+
parseForESLint8("return;", { babelOptions: BABEL_OPTIONS }),
918+
).toThrow(new SyntaxError("'return' outside of function. (1:0)"));
919+
});
920+
} else {
921+
it("return outside function", () => {
922+
parseAndAssertSame("return;");
923+
});
924+
}
925+
926+
if (process.env.BABEL_8_BREAKING) {
927+
it("super outside method", () => {
928+
expect(() => {
929+
parseForESLint("function F() { super(); }", {
930+
babelOptions: BABEL_OPTIONS,
931+
});
932+
}).toThrow(
933+
/`super\(\)` is only valid inside a class constructor of a subclass\./,
934+
);
935+
});
936+
937+
it("super outside method - enabled", () => {
938+
expect(() => {
939+
parseForESLint("function F() { super(); }", {
940+
babelOptions: {
941+
...BABEL_OPTIONS,
942+
parserOpts: {
943+
allowSuperOutsideMethod: true,
944+
},
945+
},
946+
});
947+
}).not.toThrow();
948+
});
949+
} else {
950+
it("super outside method", () => {
951+
expect(() => {
952+
parseForESLint("function F() { super(); }", {
953+
babelOptions: BABEL_OPTIONS,
954+
});
955+
}).not.toThrow();
956+
});
957+
958+
it("super outside method - disabled", () => {
959+
expect(() => {
960+
parseForESLint("function F() { super(); }", {
961+
babelOptions: {
962+
...BABEL_OPTIONS,
963+
parserOpts: {
964+
allowSuperOutsideMethod: false,
965+
},
966+
},
967+
});
968+
}).toThrow(
969+
/`super\(\)` is only valid inside a class constructor of a subclass\./,
970+
);
971+
});
972+
}
973+
872974
it("StringLiteral", () => {
873975
parseAndAssertSame("");
874976
parseAndAssertSame("");

eslint/babel-eslint-tests/test/integration/eslint/verify.js

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,10 @@ describe("verify", () => {
4545
});
4646

4747
it("super keyword in class (issue #10)", () => {
48-
verifyAndAssertMessages("class Foo { constructor() { super() } }", {
49-
"no-undef": 1,
50-
});
48+
verifyAndAssertMessages(
49+
"class Foo extends class {} { constructor() { super() } }",
50+
{ "no-undef": 1 },
51+
);
5152
});
5253

5354
it("Rest parameter in destructuring assignment (issue #11)", () => {
@@ -1549,7 +1550,9 @@ describe("verify", () => {
15491550
);
15501551
});
15511552

1552-
it("allowImportExportEverywhere option (#327)", () => {
1553+
const babel7 = process.env.BABEL_8_BREAKING ? it.skip : it;
1554+
1555+
babel7("allowImportExportEverywhere option (#327)", () => {
15531556
verifyAndAssertMessages(
15541557
`
15551558
if (true) { import Foo from 'foo'; }
@@ -1570,6 +1573,29 @@ describe("verify", () => {
15701573
);
15711574
});
15721575

1576+
it("allowImportExportEverywhere @babel/parser option (#327)", () => {
1577+
verifyAndAssertMessages(
1578+
`
1579+
if (true) { import Foo from 'foo'; }
1580+
function foo() { import Bar from 'bar'; }
1581+
switch (a) { case 1: import FooBar from 'foobar'; }
1582+
`,
1583+
{},
1584+
[],
1585+
"module",
1586+
{
1587+
env: {},
1588+
parserOptions: {
1589+
ecmaVersion: 6,
1590+
sourceType: "module",
1591+
babelOptions: {
1592+
parserOpts: { allowImportExportEverywhere: true },
1593+
},
1594+
},
1595+
},
1596+
);
1597+
});
1598+
15731599
it("with does not crash parsing in script mode (strict off) #171", () => {
15741600
verifyAndAssertMessages("with (arguments) { length; }", {}, [], "script");
15751601
});

0 commit comments

Comments
 (0)