Skip to content

Commit 7896ed7

Browse files
Support sourceType: "commonjs" (#17390)
* feat: support sourceType: commonjs * fix: improve eslint-parser souceType: commonjs handling * allow sourceType: commonjs in babel/core * set program.sourceType to script for commonjs * feat: disallow some allow* parser flags in commonjs mode * do not set allowReturnOutsideFunction for commonjs source type * update error messages * normalize Windows path * fix option typo * add souceType: "commonjs" compatibility layer * update default ecmaVersion to latest * update eslint-parser docs --------- Co-authored-by: Nicolò Ribaudo <[email protected]>
1 parent 1b9aba4 commit 7896ed7

File tree

46 files changed

+332
-53
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+332
-53
lines changed

eslint/babel-eslint-parser/README.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,25 +53,25 @@ Additional configuration options can be set in your ESLint configuration under t
5353
Note: When `requireConfigFile` is `false`, @babel/eslint-parser will still try to load the root babel config. If no configuration file is found, @babel/eslint-parser will not parse any experimental syntax. Though not recommended, if you have a babel config, but would like to prevent @babel/eslint-parser from loading Babel config, please specify
5454
**.eslintrc.js**
5555

56-
```js
57-
module.exports = {
58-
parser: "@babel/eslint-parser",
59-
parserOptions: {
60-
requireConfigFile: false,
61-
babelOptions: {
62-
babelrc: false,
63-
configFile: false,
64-
// your babel options
65-
presets: ["@babel/preset-env"],
56+
```js
57+
module.exports = {
58+
parser: "@babel/eslint-parser",
59+
parserOptions: {
60+
requireConfigFile: false,
61+
babelOptions: {
62+
babelrc: false,
63+
configFile: false,
64+
// your babel options
65+
presets: ["@babel/preset-env"],
66+
},
6667
},
67-
},
68-
};
69-
```
68+
};
69+
```
7070

71-
- `sourceType` can be set to `"module"`(default) or `"script"` if your code isn't using ECMAScript modules.
71+
- `sourceType` can be set to `"module"`(default), `"script"` or `"commonjs"`.
7272
<!-- TODO(Babel 8): Remove this -->
7373
- `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.
74-
- `ecmaFeatures.globalReturn` (default `false`) allow return statements in the global scope when used with `sourceType: "script"`.
74+
- `ecmaFeatures.globalReturn` (default `false`) allow return statements in the global scope when used with `sourceType: "script"`. This option will be deprecated, please use `sourceType: "commonjs"` instead.
7575
- `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`).
7676

7777
**.eslintrc.js**

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export = function normalizeESLintConfig(options: any) {
44
const {
55
babelOptions = {},
66
// ESLint sets ecmaVersion: undefined when ecmaVersion is not set in the config.
7-
ecmaVersion = 2020,
7+
ecmaVersion = "latest",
88
sourceType = "module",
99
requireConfigFile = true,
1010
...otherOptions
@@ -13,9 +13,7 @@ export = function normalizeESLintConfig(options: any) {
1313
return {
1414
babelOptions: { cwd: process.cwd(), ...babelOptions },
1515
ecmaVersion: ecmaVersion === "latest" ? 1e8 : ecmaVersion,
16-
// https://eslint.org/docs/latest/use/configure/language-options#specifying-javascript-options
17-
// ESLint supports "commonjs" but Babel parser does not.
18-
sourceType: sourceType === "commonjs" ? "script" : sourceType,
16+
sourceType,
1917
requireConfigFile,
2018
...otherOptions,
2119
} as Options;

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

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import babel = require("./babel-core.cts");
2+
import semver = require("semver");
23
import ESLINT_VERSION = require("../utils/eslint-version.cts");
34
import type { InputOptions } from "@babel/core";
45
import type { Options } from "../types.cts";
@@ -24,12 +25,26 @@ function getParserPlugins(
2425
return [["estree", estreeOptions], ...babelParserPlugins];
2526
}
2627

27-
function normalizeParserOptions(options: Options): InputOptions & {
28+
function normalizeParserOptions(
29+
options: Options,
30+
version: string,
31+
): InputOptions & {
2832
showIgnoredFiles?: boolean;
2933
} {
34+
// Babel <= 7.28.0 does not support `sourceType: "commonjs"`.
35+
if (
36+
!process.env.BABEL_8_BREAKING &&
37+
options.sourceType === "commonjs" &&
38+
!semver.satisfies(version, REQUIRED_VERSION(">=7.28.0"))
39+
) {
40+
options.sourceType = "script";
41+
options.ecmaFeatures = {
42+
...(options.ecmaFeatures ?? {}),
43+
globalReturn: true,
44+
};
45+
}
3046
return {
31-
// https://github.com/eslint/js/issues/519
32-
sourceType: options.sourceType as "module" | "script",
47+
sourceType: options.sourceType,
3348
filename: options.filePath,
3449
...options.babelOptions,
3550
parserOpts: {
@@ -40,9 +55,13 @@ function normalizeParserOptions(options: Options): InputOptions & {
4055
options.allowImportExportEverywhere ?? false,
4156
allowSuperOutsideMethod: true,
4257
}),
43-
allowReturnOutsideFunction:
44-
options.ecmaFeatures?.globalReturn ??
45-
(process.env.BABEL_8_BREAKING ? false : true),
58+
...(options.sourceType !== "commonjs"
59+
? {
60+
allowReturnOutsideFunction:
61+
options.ecmaFeatures?.globalReturn ??
62+
(process.env.BABEL_8_BREAKING ? false : true),
63+
}
64+
: {}),
4665
...options.babelOptions.parserOpts,
4766
plugins: getParserPlugins(options.babelOptions),
4867
// skip comment attaching for parsing performance
@@ -95,13 +114,13 @@ function getDefaultParserOptions(options: InputOptions): InputOptions {
95114
export async function normalizeBabelParseConfig(
96115
options: Options,
97116
): Promise<InputOptions> {
98-
const parseOptions = normalizeParserOptions(options);
117+
const parseOptions = normalizeParserOptions(options, babel.version);
99118
const config = await babel.loadPartialConfigAsync(parseOptions);
100119
return validateResolvedConfig(config, options, parseOptions);
101120
}
102121

103122
export function normalizeBabelParseConfigSync(options: Options): InputOptions {
104-
const parseOptions = normalizeParserOptions(options);
123+
const parseOptions = normalizeParserOptions(options, babel.version);
105124
const config = babel.loadPartialConfigSync(parseOptions);
106125
return validateResolvedConfig(config, options, parseOptions);
107126
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ describe("ESLint config", () => {
2222
});
2323

2424
it('should allow sourceType to be "commonjs"', () => {
25-
// sourceType "commonjs" allows require() calls.
25+
// sourceType "commonjs" allows require() calls and global returns
2626
verifyAndAssertMessages(
27-
'require("greetings").hello',
27+
'return require("greetings").hello',
2828
{},
2929
undefined,
3030
undefined,

packages/babel-core/src/config/validation/option-assertions.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,12 @@ export function assertSourceType(
125125
if (
126126
value !== undefined &&
127127
value !== "module" &&
128+
value !== "commonjs" &&
128129
value !== "script" &&
129130
value !== "unambiguous"
130131
) {
131132
throw new Error(
132-
`${msg(loc)} must be "module", "script", "unambiguous", or undefined`,
133+
`${msg(loc)} must be "module", "commonjs", "script", "unambiguous", or undefined`,
133134
);
134135
}
135136
// @ts-expect-error: TS can only narrow down the type when "strictNullCheck" is true

packages/babel-core/src/config/validation/options.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ export type ConfigApplicableTest = IgnoreItem | Array<IgnoreItem>;
232232
export type ConfigFileSearch = string | boolean;
233233
export type BabelrcSearch = boolean | IgnoreItem | IgnoreList;
234234
export type SourceMapsOption = boolean | "inline" | "both";
235-
export type SourceTypeOption = "module" | "script" | "unambiguous";
235+
export type SourceTypeOption = "module" | "commonjs" | "script" | "unambiguous";
236236
export type CompactOption = boolean | "auto";
237237
export type RootInputSourceMapOption = object | boolean;
238238
export type RootMode = "root" | "upward" | "upward-optional";

packages/babel-core/test/errors-stacks.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ describe("@babel/core errors", function () {
235235
root: fixture("invalid-option"),
236236
});
237237
}).toMatchInlineSnapshot(`
238-
"Error: .sourceType must be \\"module\\", \\"script\\", \\"unambiguous\\", or undefined
238+
"Error: .sourceType must be \\"module\\", \\"commonjs\\", \\"script\\", \\"unambiguous\\", or undefined
239239
at <CWD>/packages/babel-core/test/fixtures/errors/invalid-option/babel.config.json
240240
at Module.parseSync (<CWD>/packages/babel-core/src/parse.ts:_:_)
241241
at <CWD>/packages/babel-core/test/errors-stacks.js:_:_
@@ -252,7 +252,7 @@ describe("@babel/core errors", function () {
252252
sourceType: "foo",
253253
}),
254254
).toMatchInlineSnapshot(`
255-
"Error: .sourceType must be \\"module\\", \\"script\\", \\"unambiguous\\", or undefined
255+
"Error: .sourceType must be \\"module\\", \\"commonjs\\", \\"script\\", \\"unambiguous\\", or undefined
256256
at Module.parseSync (<CWD>/packages/babel-core/src/parse.ts:_:_)
257257
at <CWD>/packages/babel-core/test/errors-stacks.js:_:_
258258
at expectError (<CWD>/packages/babel-core/test/errors-stacks.js:_:_)

packages/babel-parser/data/schema.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,8 +252,8 @@
252252
"type": "string"
253253
},
254254
"sourceType": {
255-
"description": "Indicate the mode the code should be parsed in.\nCan be one of \"script\", \"module\", or \"unambiguous\". Defaults to \"script\".\n\"unambiguous\" will make @babel/parser attempt to guess, based on the presence\nof ES6 import or export statements.\nFiles with ES6 imports and exports are considered \"module\" and are otherwise \"script\".",
256-
"enum": ["module", "script", "unambiguous"],
255+
"description": "Indicate the mode the code should be parsed in.\nCan be one of \"script\", \"commonjs\", \"module\", or \"unambiguous\". Defaults to \"script\".\n\"unambiguous\" will make @babel/parser attempt to guess, based on the presence\nof ES6 import or export statements.\nFiles with ES6 imports and exports are considered \"module\" and are otherwise \"script\".",
256+
"enum": ["module", "commonjs", "script", "unambiguous"],
257257
"type": "string"
258258
},
259259
"startLine": {

packages/babel-parser/src/options.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { Plugin } from "./plugin-utils.ts";
33
// A second optional argument can be given to further configure
44
// the parser process. These options are recognized:
55

6-
export type SourceType = "script" | "module" | "unambiguous";
6+
export type SourceType = "script" | "commonjs" | "module" | "unambiguous";
77

88
export interface Options {
99
/**
@@ -71,12 +71,14 @@ export interface Options {
7171

7272
/**
7373
* Indicate the mode the code should be parsed in.
74-
* Can be one of "script", "module", or "unambiguous". Defaults to "script".
74+
* Can be one of "script", "commonjs", "module", or "unambiguous". Defaults to "script".
7575
* "unambiguous" will make @babel/parser attempt to guess, based on the presence
7676
* of ES6 import or export statements.
7777
* Files with ES6 imports and exports are considered "module" and are otherwise "script".
78+
*
79+
* Use "commonjs" to parse code that is intended to be run in a CommonJS environment such as Node.js.
7880
*/
79-
sourceType?: "script" | "module" | "unambiguous";
81+
sourceType?: SourceType;
8082

8183
/**
8284
* Correlate output AST nodes with their source filename.
@@ -261,5 +263,23 @@ export function getOptions(opts?: Options | null): OptionsWithDefaults {
261263
}
262264
}
263265

266+
if (options.sourceType === "commonjs") {
267+
if (opts.allowAwaitOutsideFunction != null) {
268+
throw new Error(
269+
"The `allowAwaitOutsideFunction` option cannot be used with `sourceType: 'commonjs'`.",
270+
);
271+
}
272+
if (opts.allowReturnOutsideFunction != null) {
273+
throw new Error(
274+
"`sourceType: 'commonjs'` implies `allowReturnOutsideFunction: true`, please remove the `allowReturnOutsideFunction` option or use `sourceType: 'script'`.",
275+
);
276+
}
277+
if (opts.allowNewTargetOutsideFunction != null) {
278+
throw new Error(
279+
"`sourceType: 'commonjs'` implies `allowNewTargetOutsideFunction: true`, please remove the `allowNewTargetOutsideFunction` option or use `sourceType: 'script'`.",
280+
);
281+
}
282+
}
283+
264284
return options;
265285
}

packages/babel-parser/src/parser/statement.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,11 @@ export default abstract class StatementParser extends ExpressionParser {
200200
file: Undone<N.File>,
201201
program: Undone<N.Program>,
202202
): N.File {
203-
file.program = this.parseProgram(program);
203+
file.program = this.parseProgram(
204+
program,
205+
tt.eof,
206+
this.options.sourceType === "module" ? "module" : "script",
207+
);
204208
file.comments = this.comments;
205209

206210
if (this.optionFlags & OptionFlags.Tokens) {
@@ -217,8 +221,8 @@ export default abstract class StatementParser extends ExpressionParser {
217221
parseProgram(
218222
this: Parser,
219223
program: Undone<N.Program>,
220-
end: TokenType = tt.eof,
221-
sourceType: SourceType = this.options.sourceType,
224+
end: TokenType,
225+
sourceType: SourceType,
222226
): N.Program {
223227
program.sourceType = sourceType;
224228
program.interpreter = this.parseInterpreterDirective();

0 commit comments

Comments
 (0)