-----
-
+---
# Contributing
@@ -24,14 +23,15 @@ contributing, please read the
## Not sure where to start?
- If you aren't just making a documentation change, you'll probably want to learn a bit about a few topics.
- - [ASTs](https://en.wikipedia.org/wiki/Abstract_syntax_tree) (Abstract Syntax Tree): The Babel AST [spec](https://github.com/babel/babel/blob/master/packages/babel-parser/ast/spec.md) is a bit different from [ESTree](https://github.com/estree/estree). The differences are listed [here](https://babeljs.io/docs/en/next/babel-parser.html#output).
- - Check out [`/doc`](https://github.com/babel/babel/tree/master/doc) for information about Babel's internals
- - Check out [the Babel Plugin Handbook](https://github.com/thejameskyle/babel-handbook/blob/master/translations/en/plugin-handbook.md#babel-plugin-handbook) - core plugins are written the same way as any other plugin!
- - Check out [AST Explorer](http://astexplorer.net/#/scUfOmVOG5) to learn more about ASTs or make your own plugin in the browser
+ - [ASTs](https://en.wikipedia.org/wiki/Abstract_syntax_tree) (Abstract Syntax Tree): The Babel AST [spec](https://github.com/babel/babel/blob/master/packages/babel-parser/ast/spec.md) is a bit different from [ESTree](https://github.com/estree/estree). The differences are listed [here](https://babeljs.io/docs/en/next/babel-parser.html#output).
+ - Check out [`/doc`](https://github.com/babel/babel/tree/master/doc) for information about Babel's internals
+ - Check out [the Babel Plugin Handbook](https://github.com/thejameskyle/babel-handbook/blob/master/translations/en/plugin-handbook.md#babel-plugin-handbook) - core plugins are written the same way as any other plugin!
+ - Check out [AST Explorer](http://astexplorer.net/#/scUfOmVOG5) to learn more about ASTs or make your own plugin in the browser
- When you feel ready to jump into the Babel source code, a good place to start is to look for issues tagged with [help wanted](https://github.com/babel/babel/labels/help%20wanted) and/or [good first issue](https://github.com/babel/babel/labels/good%20first%20issue).
- Follow along with what we are working on by joining our [Slack](https://babeljs.slack.com) (you can sign-up [here](https://slack.babeljs.io/)
- for an invite), following our announcements on [Twitter](https://twitter.com/babeljs), and reading (or participating!) in our [meeting notes](https://github.com/babel/notes).
+ for an invite), following our announcements on [Twitter](https://twitter.com/babeljs), and reading (or participating!) in our [meeting notes](https://github.com/babel/notes).
- Check out our [website](http://babeljs.io/) and the [repo](https://github.com/babel/website)
+- You can contribute by triaging issues which may include reproducing bug reports or asking for vital information, such as version numbers or reproduction instructions. If you would like to start triaging issues, one easy way to get started is to [subscribe to babel on CodeTriage](https://www.codetriage.com/babel/babel). [![Open Source Helpers](https://www.codetriage.com/babel/babel/badges/users.svg)](https://www.codetriage.com/babel/babel)
## Chat
@@ -48,8 +48,12 @@ Installation instructions can be found here: https://yarnpkg.com/en/docs/install
### Setup
+Fork the `babel` repository to your GitHub Account.
+
+Then, run:
+
```sh
-$ git clone https://github.com/babel/babel
+$ git clone https://github.com//babel
$ cd babel
$ make bootstrap
```
@@ -157,12 +161,12 @@ $ ./scripts/test-cov.sh
In case you're not able to reproduce an error on CI locally, it may be due to
- - Node Version: Travis CI runs the tests against all major node versions. If your tests use JavaScript features unsupported by lower versions of node, then use [minNodeVersion option](#writing-tests) in options.json.
- - Timeout: Check the CI log and if the only errors are timeout errors and you are sure that it's not related to the changes you made, ask someone in the slack channel to trigger rebuild on the CI build and it might be resolved
+- Node Version: Travis CI runs the tests against all major node versions. If your tests use JavaScript features unsupported by lower versions of node, then use [minNodeVersion option](#writing-tests) in options.json.
+- Timeout: Check the CI log and if the only errors are timeout errors and you are sure that it's not related to the changes you made, ask someone in the slack channel to trigger rebuild on the CI build and it might be resolved
In case you're locally getting errors which are not on the CI, it may be due to
- - Updates in Dependencies: Make sure you run `make bootstrap` before you run `make build` or `make watch` before you run the tests.
+- Updates in Dependencies: Make sure you run `make bootstrap` before you run `make build` or `make watch` before you run the tests.
### Writing tests
@@ -176,15 +180,16 @@ For example, in [`@babel/plugin-transform-exponentiation-operator/test`](https:/
- There is an `index.js` file. It imports our [test helper](https://github.com/babel/babel/tree/master/packages/babel-helper-plugin-test-runner). (You don't have to worry about this).
- There can be multiple folders under [`/fixtures`](https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-exponentiation-operator/test/fixtures)
- - There is an [`options.json`](https://github.com/babel/babel/blob/master/packages/babel-plugin-transform-exponentiation-operator/test/fixtures/exponentian-operator/options.json) file whose function is similar to a `.babelrc` file, allowing you to pass in the plugins and settings you need for your tests.
- - For this test, we only need the relevant plugin, so it's just `{ "plugins": ["@babel/plugin-transform-exponentiation-operator"] }`.
- - If necessary, you can have an `options.json` with different options in each subfolder.
+
+ - There is an [`options.json`](https://github.com/babel/babel/blob/master/packages/babel-plugin-transform-exponentiation-operator/test/fixtures/exponentian-operator/options.json) file whose function is similar to a `.babelrc` file, allowing you to pass in the plugins and settings you need for your tests.
+ - For this test, we only need the relevant plugin, so it's just `{ "plugins": ["@babel/plugin-transform-exponentiation-operator"] }`.
+ - If necessary, you can have an `options.json` with different options in each subfolder.
- In each subfolder, you can organize your directory structure by categories of tests. (Example: these folders can be named after the feature you are testing or can reference the issue number they fix)
- Generally, there are two kinds of tests for plugins
- - The first is a simple test of the input and output produced by running Babel on some code. We do this by creating an [`input.js`](https://github.com/babel/babel/blob/master/packages/babel-plugin-transform-exponentiation-operator/test/fixtures/exponentian-operator/binary/input.js) file and an [`output.js`](https://github.com/babel/babel/blob/master/packages/babel-plugin-transform-exponentiation-operator/test/fixtures/exponentian-operator/binary/output.js) file. This kind of test only works in sub-subdirectories of `/fixtures`, i.e. `/fixtures/exponentian-operator/binary/input.js` and **not** `/fixtures/exponentian-operator/input.js`.
- - If you need to expect an error, you can ignore creating the `output.js` file and pass a new `throws` key to the `options.json` that contains the error string that is created.
- - The second and preferred type is a test that actually evaluates the produced code and asserts that certain properties are true or false. We do this by creating an [`exec.js`](https://github.com/babel/babel/blob/master/packages/babel-plugin-transform-exponentiation-operator/test/fixtures/exponentian-operator/comprehensive/exec.js) file.
+ - The first is a simple test of the input and output produced by running Babel on some code. We do this by creating an [`input.js`](https://github.com/babel/babel/blob/master/packages/babel-plugin-transform-exponentiation-operator/test/fixtures/exponentian-operator/binary/input.js) file and an [`output.js`](https://github.com/babel/babel/blob/master/packages/babel-plugin-transform-exponentiation-operator/test/fixtures/exponentian-operator/binary/output.js) file. This kind of test only works in sub-subdirectories of `/fixtures`, i.e. `/fixtures/exponentian-operator/binary/input.js` and **not** `/fixtures/exponentian-operator/input.js`.
+ - If you need to expect an error, you can ignore creating the `output.js` file and pass a new `throws` key to the `options.json` that contains the error string that is created.
+ - The second and preferred type is a test that actually evaluates the produced code and asserts that certain properties are true or false. We do this by creating an [`exec.js`](https://github.com/babel/babel/blob/master/packages/babel-plugin-transform-exponentiation-operator/test/fixtures/exponentian-operator/comprehensive/exec.js) file.
In a fixture test, you simply write out the code you want to transform in `input.js`.
@@ -199,6 +204,7 @@ and the expected output after transforming it with your `options.json` in `outpu
// output.js
Math.pow(2, 2);
```
+
In an `exec.js` test, we run or check that the code actually does what it's supposed to do rather than just check the static output.
```js
@@ -234,9 +240,9 @@ Inside the `packages/babel-parser/test/fixtures` folder are categories/groupings
etc.). To add a test, create a folder under one of these groupings (or create a new one) with a
descriptive name, and add the following:
-* Create an `input.js` file that contains the code you want the babel parser to parse.
+- Create an `input.js` file that contains the code you want the babel parser to parse.
-* Add an `output.json` file with the expected parser output. For added convenience, if there is no `output.json` present, the test runner will generate one for you.
+- Add an `output.json` file with the expected parser output. For added convenience, if there is no `output.json` present, the test runner will generate one for you.
After writing tests for @babel/parser, just build it by running:
@@ -316,6 +322,7 @@ Note that the code shown in Chrome DevTools is compiled code and therefore diffe
- Start working about the Babel transform itself!
## Internals
+
- AST spec ([babel-parser/ast/spec.md](https://github.com/babel/babel/blob/master/packages/babel-parser/ast/spec.md))
- Versioning ([doc/design/versioning.md](https://github.com/babel/babel/blob/master/doc/design/versioning.md))
- Monorepo ([doc/design/monorepo.md](https://github.com/babel/babel/blob/master/doc/design/monorepo.md))
diff --git a/Gulpfile.js b/Gulpfile.js
index 5df9882ca3f2..a78f68ea4914 100644
--- a/Gulpfile.js
+++ b/Gulpfile.js
@@ -10,26 +10,26 @@ const fancyLog = require("fancy-log");
const filter = require("gulp-filter");
const gulp = require("gulp");
const path = require("path");
-const webpack = require("webpack");
-const merge = require("merge-stream");
const rollup = require("rollup");
+const rollupAlias = require("@rollup/plugin-alias");
const rollupBabel = require("rollup-plugin-babel");
+const rollupBabelSource = require("./scripts/rollup-plugin-babel-source");
+const rollupCommonJs = require("rollup-plugin-commonjs");
+const rollupJson = require("@rollup/plugin-json");
+const rollupNodeBuiltins = require("rollup-plugin-node-builtins");
+const rollupNodeGlobals = require("rollup-plugin-node-globals");
const rollupNodeResolve = require("rollup-plugin-node-resolve");
const rollupReplace = require("rollup-plugin-replace");
-const { registerStandalonePackageTask } = require("./scripts/gulp-tasks");
+const { terser: rollupTerser } = require("rollup-plugin-terser");
-const sources = ["codemods", "packages"];
+const defaultSourcesGlob = "./@(codemods|packages|eslint)/*/src/**/*.js";
function swapSrcWithLib(srcPath) {
const parts = srcPath.split(path.sep);
- parts[1] = "lib";
+ parts[2] = "lib";
return parts.join(path.sep);
}
-function getGlobFromSource(source) {
- return `./${source}/*/src/**/*.js`;
-}
-
function getIndexFromPackage(name) {
return `${name}/src/index.js`;
}
@@ -56,68 +56,234 @@ function rename(fn) {
});
}
-function buildBabel(exclude) {
- return merge(
- sources.map(source => {
- const base = path.join(__dirname, source);
+function buildBabel(exclude, sourcesGlob = defaultSourcesGlob) {
+ const base = __dirname;
- let stream = gulp.src(getGlobFromSource(source), { base: base });
+ let stream = gulp.src(sourcesGlob, { base: __dirname });
- if (exclude) {
- const filters = exclude.map(p => `!**/${p}/**`);
- filters.unshift("**");
- stream = stream.pipe(filter(filters));
- }
+ if (exclude) {
+ const filters = exclude.map(p => `!**/${p}/**`);
+ filters.unshift("**");
+ stream = stream.pipe(filter(filters));
+ }
- return stream
- .pipe(errorsLogger())
- .pipe(newer({ dest: base, map: swapSrcWithLib }))
- .pipe(compilationLogger())
- .pipe(babel())
- .pipe(
- // Passing 'file.relative' because newer() above uses a relative
- // path and this keeps it consistent.
- rename(file => path.resolve(file.base, swapSrcWithLib(file.relative)))
- )
- .pipe(gulp.dest(base));
- })
- );
+ return stream
+ .pipe(errorsLogger())
+ .pipe(newer({ dest: base, map: swapSrcWithLib }))
+ .pipe(compilationLogger())
+ .pipe(babel())
+ .pipe(
+ // Passing 'file.relative' because newer() above uses a relative
+ // path and this keeps it consistent.
+ rename(file => path.resolve(file.base, swapSrcWithLib(file.relative)))
+ )
+ .pipe(gulp.dest(base));
}
+let babelVersion = require("./packages/babel-core/package.json").version;
function buildRollup(packages) {
+ const sourcemap = process.env.NODE_ENV === "production";
+ const minify = !!process.env.IS_PUBLISH;
return Promise.all(
- packages.map(pkg => {
- const input = getIndexFromPackage(pkg);
- fancyLog(`Compiling '${chalk.cyan(input)}' with rollup ...`);
- return rollup
- .rollup({
- input,
- plugins: [
- rollupReplace({
- "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV),
- }),
- rollupBabel({
- envName: "babel-parser",
- }),
- rollupNodeResolve(),
- ],
- })
- .then(bundle => {
- return bundle.write({
- file: path.join(pkg, "lib/index.js"),
- format: "cjs",
- name: "babel-parser",
- sourcemap: process.env.NODE_ENV !== "production",
+ packages.map(
+ ({ src, format, dest, name, filename, version = babelVersion }) => {
+ const extraPlugins = [];
+ let inputExternal = undefined,
+ outputGlobals = undefined,
+ nodeResolveBrowser = false,
+ babelEnvName = "rollup";
+ switch (src) {
+ case "packages/babel-standalone":
+ nodeResolveBrowser = true;
+ babelEnvName = "standalone";
+ if (minify) {
+ extraPlugins.push(
+ rollupTerser({
+ include: /^.+\.min\.js$/,
+ })
+ );
+ }
+ break;
+ case "packages/babel-preset-env-standalone":
+ nodeResolveBrowser = true;
+ babelEnvName = "standalone";
+ if (minify) {
+ extraPlugins.push(
+ rollupTerser({
+ include: /^.+\.min\.js$/,
+ })
+ );
+ }
+ inputExternal = ["@babel/standalone"];
+ outputGlobals = {
+ "@babel/standalone": "Babel",
+ };
+ extraPlugins.push(
+ rollupAlias({
+ entries: [
+ {
+ find: "./available-plugins",
+ replacement: require.resolve(
+ path.join(__dirname, src, "./src/available-plugins")
+ ),
+ },
+ {
+ find: "caniuse-lite/data/regions",
+ replacement: require.resolve(
+ path.join(__dirname, src, "./src/caniuse-lite-regions")
+ ),
+ },
+ ],
+ })
+ );
+ break;
+ }
+ // If this build is part of a pull request, include the pull request number in
+ // the version number.
+ if (process.env.CIRCLE_PR_NUMBER) {
+ const prVersion = "+pr." + process.env.CIRCLE_PR_NUMBER;
+ babelVersion += prVersion;
+ version += prVersion;
+ }
+ const input = getIndexFromPackage(src);
+ fancyLog(`Compiling '${chalk.cyan(input)}' with rollup ...`);
+ return rollup
+ .rollup({
+ input,
+ external: inputExternal,
+ plugins: [
+ ...extraPlugins,
+ rollupBabelSource(),
+ rollupReplace({
+ "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV),
+ BABEL_VERSION: JSON.stringify(babelVersion),
+ VERSION: JSON.stringify(version),
+ }),
+ rollupBabel({
+ envName: babelEnvName,
+ babelrc: false,
+ extends: "./babel.config.js",
+ }),
+ rollupNodeResolve({
+ browser: nodeResolveBrowser,
+ preferBuiltins: true,
+ //todo: When Yarn workspaces is enabled, remove `dedupe` option
+ dedupe(importee) {
+ return (
+ importee.startsWith("lodash/") ||
+ [
+ "babel-plugin-dynamic-import-node/utils",
+ "esutils",
+ "semver",
+ "source-map",
+ ].includes(importee)
+ );
+ },
+ }),
+ rollupCommonJs({
+ include: [
+ /node_modules/,
+ "packages/babel-runtime/regenerator/**",
+ "packages/babel-preset-env/data/*.js",
+ // Rollup doesn't read export maps, so it loads the cjs fallback
+ "packages/babel-compat-data/*.js",
+ ],
+ namedExports: {
+ "babel-plugin-dynamic-import-node/utils.js": [
+ "createDynamicImportTransform",
+ "getImportSource",
+ ],
+ "@babel/standalone": ["availablePlugins", "registerPlugin"],
+ },
+ }),
+ rollupJson(),
+ rollupNodeBuiltins(),
+ rollupNodeGlobals({ sourceMap: sourcemap }),
+ ],
+ })
+ .then(bundle => {
+ const outputFile = path.resolve(src, dest, filename || "index.js");
+ return bundle
+ .write({
+ file: outputFile,
+ format,
+ name,
+ globals: outputGlobals,
+ sourcemap: sourcemap,
+ })
+ .then(() => {
+ if (!process.env.IS_PUBLISH) {
+ fancyLog(
+ chalk.yellow(
+ `Skipped minification of '${chalk.cyan(
+ path.relative(path.join(__dirname, ".."), outputFile)
+ )}' because not publishing`
+ )
+ );
+ return undefined;
+ }
+ fancyLog(
+ `Minifying '${chalk.cyan(
+ path.relative(path.join(__dirname, ".."), outputFile)
+ )}'...`
+ );
+
+ return bundle.write({
+ file: outputFile.replace(/\.js$/, ".min.js"),
+ format,
+ name,
+ globals: outputGlobals,
+ sourcemap: sourcemap,
+ });
+ });
});
- });
- })
+ }
+ )
);
}
-const bundles = ["packages/babel-parser"];
+const libBundles = [
+ {
+ src: "packages/babel-parser",
+ format: "cjs",
+ dest: "lib",
+ version: require("./packages/babel-parser/package").version,
+ },
+];
+
+const standaloneBundle = [
+ {
+ src: "packages/babel-standalone",
+ format: "umd",
+ name: "Babel",
+ filename: "babel.js",
+ dest: "",
+ version: require("./packages/babel-core/package").version,
+ },
+];
+
+const presetEnvStandaloneBundle = [
+ {
+ src: "packages/babel-preset-env-standalone",
+ format: "umd",
+ name: "BabelPresetEnv",
+ filename: "babel-preset-env.js",
+ dest: "",
+ version: require("./packages/babel-preset-env/package").version,
+ },
+];
-gulp.task("build-rollup", () => buildRollup(bundles));
-gulp.task("build-babel", () => buildBabel(/* exclude */ bundles));
+gulp.task("build-rollup", () => buildRollup(libBundles));
+gulp.task("build-babel-standalone", () => buildRollup(standaloneBundle));
+
+gulp.task("build-babel-preset-env-standalone", () =>
+ buildRollup(presetEnvStandaloneBundle)
+);
+
+gulp.task("build-babel", () => buildBabel(/* exclude */ libBundles));
+gulp.task("build-babel-types", () =>
+ buildBabel(/* exclude */ libBundles, "packages/babel-types/src/**/*.js")
+);
gulp.task("build", gulp.parallel("build-rollup", "build-babel"));
gulp.task("default", gulp.series("build"));
@@ -128,47 +294,9 @@ gulp.task(
"watch",
gulp.series("build-no-bundle", function watch() {
gulpWatch(
- sources.map(getGlobFromSource),
+ defaultSourcesGlob,
{ debounceDelay: 200 },
gulp.task("build-no-bundle")
);
})
);
-
-registerStandalonePackageTask(
- gulp,
- "babel",
- "Babel",
- path.join(__dirname, "packages"),
- require("./packages/babel-standalone/package.json").version
-);
-
-const presetEnvWebpackPlugins = [
- new webpack.NormalModuleReplacementPlugin(
- /\.\/available-plugins/,
- require.resolve(
- path.join(
- __dirname,
- "./packages/babel-preset-env-standalone/src/available-plugins"
- )
- )
- ),
- new webpack.NormalModuleReplacementPlugin(
- /caniuse-lite\/data\/regions\/.+/,
- require.resolve(
- path.join(
- __dirname,
- "./packages/babel-preset-env-standalone/src/caniuse-lite-regions"
- )
- )
- ),
-];
-
-registerStandalonePackageTask(
- gulp,
- "babel-preset-env",
- "babelPresetEnv",
- path.join(__dirname, "packages"),
- require("./packages/babel-preset-env-standalone/package.json").version,
- presetEnvWebpackPlugins
-);
diff --git a/Makefile b/Makefile
index 5fa5cb0cfdbf..dfe03b547e9c 100644
--- a/Makefile
+++ b/Makefile
@@ -1,67 +1,129 @@
-MAKEFLAGS = -j1
-FLOW_COMMIT = 09669846b7a7ca5a6c23c12d56bb3bebdafd67e9
-TEST262_COMMIT = de567d3aa5de4eaa11e00131d26b9fe77997dfb0
+FLOW_COMMIT = a1f9a4c709dcebb27a5084acf47755fbae699c25
+TEST262_COMMIT = 28b4fcca4b1b1d278dfe0cc0e69c7d9d59b31aab
+TYPESCRIPT_COMMIT = 5fc917be2e4dd64c8e9504d36615cd7fbfdd4cd3
+
+FORCE_PUBLISH = "@babel/runtime,@babel/runtime-corejs2,@babel/runtime-corejs3,@babel/standalone,@babel/preset-env-standalone"
# Fix color output until TravisCI fixes https://github.com/travis-ci/travis-ci/issues/7967
export FORCE_COLOR = true
-SOURCES = packages codemods
+SOURCES = packages codemods eslint
+
+COMMA := ,
+EMPTY :=
+SPACE := $(EMPTY) $(EMPTY)
+COMMA_SEPARATED_SOURCES = $(subst $(SPACE),$(COMMA),$(SOURCES))
+
+YARN := yarn --silent
+NODE := $(YARN) node
+
.PHONY: build build-dist watch lint fix clean test-clean test-only test test-ci publish bootstrap
-build: clean clean-lib
- ./node_modules/.bin/gulp build
- node ./packages/babel-standalone/scripts/generate.js
- node ./packages/babel-types/scripts/generateTypeHelpers.js
- # call build again as the generated files might need to be compiled again.
- ./node_modules/.bin/gulp build
- # generate flow and typescript typings
- node packages/babel-types/scripts/generators/flow.js > ./packages/babel-types/lib/index.js.flow
- node packages/babel-types/scripts/generators/typescript.js > ./packages/babel-types/lib/index.d.ts
+build: build-bundle
ifneq ("$(BABEL_COVERAGE)", "true")
- make build-standalone
- make build-preset-env-standalone
+ $(MAKE) build-standalone
endif
-build-standalone:
- ./node_modules/.bin/gulp build-babel-standalone
+build-bundle: clean clean-lib
+ $(YARN) gulp build
+ $(MAKE) generate-standalone generate-type-helpers
+ # call build again as the generated files might need to be compiled again.
+ $(YARN) gulp build
+ $(MAKE) build-typings
+ $(MAKE) build-dist
+
+build-bundle-ci: bootstrap-only
+ $(MAKE) build-bundle
+
+generate-standalone:
+ $(NODE) packages/babel-standalone/scripts/generate.js
+
+generate-type-helpers:
+ $(NODE) packages/babel-types/scripts/generateTypeHelpers.js
+
+build-typings: build-flow-typings build-typescript-typings
+
+build-flow-typings:
+ $(NODE) packages/babel-types/scripts/generators/flow.js > packages/babel-types/lib/index.js.flow
+
+build-typescript-typings:
+ $(NODE) packages/babel-types/scripts/generators/typescript.js > packages/babel-types/lib/index.d.ts
+
+build-standalone: build-babel-standalone build-preset-env-standalone
+
+build-standalone-ci: build-bundle-ci
+ $(MAKE) build-standalone
+
+build-babel-standalone:
+ $(YARN) gulp build-babel-standalone
build-preset-env-standalone:
- ./node_modules/.bin/gulp build-babel-preset-env-standalone
+ $(YARN) gulp build-babel-preset-env-standalone
prepublish-build-standalone:
- BABEL_ENV=production IS_PUBLISH=true ./node_modules/.bin/gulp build-babel-standalone
+ BABEL_ENV=production IS_PUBLISH=true $(YARN) gulp build-babel-standalone
prepublish-build-preset-env-standalone:
- BABEL_ENV=production IS_PUBLISH=true ./node_modules/.bin/gulp build-babel-preset-env-standalone
+ BABEL_ENV=production IS_PUBLISH=true $(YARN) gulp build-babel-preset-env-standalone
-build-dist: build
+build-dist: build-polyfill-dist build-plugin-transform-runtime-dist
+
+build-polyfill-dist:
cd packages/babel-polyfill; \
scripts/build-dist.sh
- cd packages/babel-plugin-transform-runtime; \
- node scripts/build-dist.js
-watch: clean clean-lib
+build-plugin-transform-runtime-dist:
+ cd packages/babel-plugin-transform-runtime; \
+ $(NODE) scripts/build-dist.js
+build-no-bundle: clean clean-lib
+ BABEL_ENV=development $(YARN) gulp build-no-bundle
# Ensure that build artifacts for types are created during local
# development too.
- BABEL_ENV=development ./node_modules/.bin/gulp build-no-bundle
- node ./packages/babel-types/scripts/generateTypeHelpers.js
- node packages/babel-types/scripts/generators/flow.js > ./packages/babel-types/lib/index.js.flow
- node packages/babel-types/scripts/generators/typescript.js > ./packages/babel-types/lib/index.d.ts
- BABEL_ENV=development ./node_modules/.bin/gulp watch
+ $(MAKE) generate-type-helpers
+ $(MAKE) build-typings
+
+watch: build-no-bundle
+ BABEL_ENV=development $(YARN) gulp watch
+
+code-quality-ci: flowcheck-ci lint-ci
+
+flowcheck-ci: bootstrap-flowcheck
+ $(MAKE) flow
+
+code-quality: flow lint
flow:
- ./node_modules/.bin/flow check --strip-root
+ $(YARN) flow check --strip-root
+
+bootstrap-flowcheck: bootstrap-only
+ $(YARN) gulp build-babel-types
+ $(MAKE) build-typings
+
+lint-ci: lint-js-ci lint-ts-ci
+
+lint-js-ci: bootstrap-only
+ $(MAKE) lint-js
+
+lint-ts-ci: bootstrap-flowcheck
+ $(MAKE) lint-ts
-lint:
- ./node_modules/.bin/eslint scripts $(SOURCES) '*.js' --format=codeframe
+lint: lint-js lint-ts
-fix: fix-json
- ./node_modules/.bin/eslint scripts $(SOURCES) '*.js' --format=codeframe --fix
+lint-js:
+ $(YARN) eslint scripts $(SOURCES) '*.js' --format=codeframe
+
+lint-ts:
+ scripts/lint-ts-typings.sh
+
+fix: fix-json fix-js
+
+fix-js:
+ $(YARN) eslint scripts $(SOURCES) '*.js' --format=codeframe --fix
fix-json:
- ./node_modules/.bin/prettier "{packages,codemod}/*/test/fixtures/**/options.json" --write --loglevel warn
+ $(YARN) prettier "{$(COMMA_SEPARATED_SOURCES)}/*/test/fixtures/**/options.json" --write --loglevel warn
clean: test-clean
rm -f .npmrc
@@ -74,72 +136,92 @@ test-clean:
$(foreach source, $(SOURCES), \
$(call clean-source-test, $(source)))
+# Does not work on Windows; use "$(YARN) jest" instead
test-only:
BABEL_ENV=test ./scripts/test.sh
- make test-clean
+ $(MAKE) test-clean
test: lint test-only
-test-ci: bootstrap test-only
+test-ci: jest-ci
+
+jest-ci: build-standalone-ci
+ BABEL_ENV=test $(YARN) jest --maxWorkers=4 --ci
+ $(MAKE) test-clean
+# Does not work on Windows
test-ci-coverage: SHELL:=/bin/bash
test-ci-coverage:
- BABEL_COVERAGE=true BABEL_ENV=test make bootstrap
+ BABEL_COVERAGE=true BABEL_ENV=test $(MAKE) bootstrap
BABEL_ENV=test TEST_TYPE=cov ./scripts/test-cov.sh
bash <(curl -s https://codecov.io/bash) -f coverage/coverage-final.json
bootstrap-flow:
- rm -rf ./build/flow
- mkdir -p ./build
- git clone --branch=master --single-branch --shallow-since=2018-11-01 https://github.com/facebook/flow.git ./build/flow
+ rm -rf build/flow
+ mkdir -p build
+ git clone --branch=master --single-branch --shallow-since=2018-11-01 https://github.com/facebook/flow.git build/flow
cd build/flow && git checkout $(FLOW_COMMIT)
test-flow:
- node scripts/tests/flow/run_babel_parser_flow_tests.js
+ $(NODE) scripts/parser-tests/flow
-test-flow-ci: bootstrap test-flow
+test-flow-ci: build-bundle-ci bootstrap-flow
+ $(MAKE) test-flow
test-flow-update-whitelist:
- node scripts/tests/flow/run_babel_parser_flow_tests.js --update-whitelist
+ $(NODE) scripts/parser-tests/flow --update-whitelist
-bootstrap-test262:
- rm -rf ./build/test262
+bootstrap-typescript:
+ rm -rf ./build/typescript
mkdir -p ./build
- git clone --branch=master --single-branch --shallow-since=2019-01-01 https://github.com/tc39/test262.git ./build/test262
+ git clone --branch=master --single-branch --shallow-since=2019-09-01 https://github.com/microsoft/TypeScript.git ./build/typescript
+ cd build/typescript && git checkout $(TYPESCRIPT_COMMIT)
+
+test-typescript:
+ $(NODE) scripts/parser-tests/typescript
+
+test-typescript-ci: build-bundle-ci bootstrap-typescript
+ $(MAKE) test-typescript
+
+test-typescript-update-whitelist:
+ $(NODE) scripts/parser-tests/typescript --update-whitelist
+
+bootstrap-test262:
+ rm -rf build/test262
+ mkdir -p build
+ git clone --branch=master --single-branch --shallow-since=2019-12-01 https://github.com/tc39/test262.git build/test262
cd build/test262 && git checkout $(TEST262_COMMIT)
test-test262:
- node scripts/tests/test262/run_babel_parser_test262.js
+ $(NODE) scripts/parser-tests/test262
-test-test262-ci: bootstrap test-test262
+test-test262-ci: build-bundle-ci bootstrap-test262
+ $(MAKE) test-test262
test-test262-update-whitelist:
- node scripts/tests/test262/run_babel_parser_test262.js --update-whitelist
+ $(NODE) scripts/parser-tests/test262 --update-whitelist
+# Does not work on Windows
clone-license:
./scripts/clone-license.sh
-prepublish-build:
- make clean-lib
- rm -rf packages/babel-runtime/helpers
- rm -rf packages/babel-runtime-corejs2/helpers
- rm -rf packages/babel-runtime-corejs2/core-js
- NODE_ENV=production BABEL_ENV=production make build-dist
- make clone-license
+prepublish-build: clean-lib clean-runtime-helpers
+ NODE_ENV=production BABEL_ENV=production $(MAKE) build
+ $(MAKE) clone-license
prepublish:
- make bootstrap-only
- make prepublish-build
- make test
+ $(MAKE) bootstrap-only
+ $(MAKE) prepublish-build
+ IS_PUBLISH=true $(MAKE) test
new-version:
git pull --rebase
- ./node_modules/.bin/lerna version --force-publish="@babel/runtime,@babel/runtime-corejs2,@babel/runtime-corejs3,@babel/standalone,@babel/preset-env-standalone"
+ $(YARN) lerna version --force-publish=$(FORCE_PUBLISH)
# NOTE: Run make new-version first
publish: prepublish
- ./node_modules/.bin/lerna publish from-git --require-scripts
- make clean
+ $(YARN) lerna publish from-git
+ $(MAKE) clean
publish-ci: prepublish
ifneq ("$(NPM_TOKEN)", "")
@@ -148,22 +230,48 @@ else
echo "Missing NPM_TOKEN env var"
exit 1
endif
- ./node_modules/.bin/lerna publish from-git --require-scripts --yes
+ $(YARN) lerna publish from-git --yes
rm -f .npmrc
- make clean
+ $(MAKE) clean
-bootstrap-only: clean-all
+publish-test:
+ifneq ("$(I_AM_USING_VERDACCIO)", "I_AM_SURE")
+ echo "You probably don't know what you are doing"
+ exit 1
+endif
+ $(MAKE) prepublish-build
+ $(YARN) lerna version patch --force-publish=$(FORCE_PUBLISH) --no-push --yes --tag-version-prefix="version-e2e-test-"
+ $(YARN) lerna publish from-git --registry http://localhost:4873 --yes --tag-version-prefix="version-e2e-test-"
+ $(MAKE) clean
+
+publish-eslint:
+ $(call set-json-field, ./eslint/$(PKG)/package.json, private, false)
+ cd eslint/$(PKG); yarn publish
+ $(call set-json-field, ./eslint/$(PKG)/package.json, private, true)
+
+bootstrap-only: lerna-bootstrap
+
+yarn-install: clean-all
yarn --ignore-engines
- ./node_modules/.bin/lerna bootstrap -- --ignore-engines
+
+lerna-bootstrap: yarn-install
+# todo: remove `-- -- --ignore-engines` in Babel 8
+ $(YARN) lerna bootstrap -- -- --ignore-engines
bootstrap: bootstrap-only
- make build
- cd packages/babel-plugin-transform-runtime; \
- node scripts/build-dist.js
+ $(MAKE) build
clean-lib:
+ # TODO: Don't delete eslint/*/lib when they use src
$(foreach source, $(SOURCES), \
- $(call clean-source-lib, $(source)))
+ $(if $(filter-out $(source), eslint), \
+ $(call clean-source-lib, $(source))))
+
+clean-runtime-helpers:
+ rm -f packages/babel-runtime/helpers/**/*.js
+ rm -f packages/babel-runtime-corejs2/helpers/**/*.js
+ rm -f packages/babel-runtime-corejs3/helpers/**/*.js
+ rm -rf packages/babel-runtime-corejs2/core-js
clean-all:
rm -rf node_modules
@@ -173,7 +281,13 @@ clean-all:
$(foreach source, $(SOURCES), \
$(call clean-source-all, $(source)))
- make clean
+ $(MAKE) clean
+
+update-env-corejs-fixture:
+ rm -rf packages/babel-preset-env/node_modules/core-js-compat
+ $(YARN) lerna bootstrap
+ $(MAKE) build-bundle
+ OVERWRITE=true $(YARN) jest packages/babel-preset-env
define clean-source-lib
rm -rf $(1)/*/lib
@@ -187,8 +301,16 @@ define clean-source-test
endef
define clean-source-all
- rm -rf $(1)/*/lib
+ # TODO: Don't delete eslint/*/lib when they use src
+ $(if $(filter-out $1, eslint), $(call clean-source-lib, $1))
rm -rf $(1)/*/node_modules
rm -rf $(1)/*/package-lock.json
endef
+
+define set-json-field
+ $(NODE) -e "\
+ require('fs').writeFileSync('$1'.trim(), \
+ JSON.stringify({ ...require('$1'.trim()), $2: $3 }, null, 2) + '\\n' \
+ )"
+endef
diff --git a/README.md b/README.md
index 579b85a7cd88..0b17712e2e75 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@
-
+
@@ -80,14 +80,14 @@ Try it out at our [REPL](https://babeljs.io/repl/build/master#?code_lz=NoRgNATGD
Mostly a handful of volunteers, funded by you! Please check out our [team page](https://babeljs.io/team)!
-### Looking for support?
-
-For questions and support please join our [Slack Community](https://slack.babeljs.io/) (you can sign-up [here](https://slack.babeljs.io/) for an invite), ask a question on [Stack Overflow](https://stackoverflow.com/questions/tagged/babeljs), or ping us on [Twitter](https://twitter.com/babeljs).
-
### Is there a Babel song?
I'm so glad you asked: [Hallelujah —— In Praise of Babel](SONG.md) by [@angus-c](https://github.com/angus-c), [audio version](https://youtu.be/40abpedBKK8) by [@swyx](https://twitter.com/@swyx). Tweet us your recordings!
+### Looking for support?
+
+For questions and support please join our [Slack Community](https://slack.babeljs.io/) (you can sign-up [here](https://slack.babeljs.io/) for an invite), ask a question on [Stack Overflow](https://stackoverflow.com/questions/tagged/babeljs), or ping us on [Twitter](https://twitter.com/babeljs).
+
### Where are the docs?
Check out our website: [babeljs.io](https://babeljs.io/), and report issues/features at [babel/website](https://github.com/babel/website/issues).
@@ -98,12 +98,19 @@ Please read through our [CONTRIBUTING.md](CONTRIBUTING.md) and fill out the issu
### Want to contribute to Babel?
-Check out our [CONTRIBUTING.md](CONTRIBUTING.md) to get started with setting up the repo.
+Check out:
+
+- Our [#development](https://babeljs.slack.com/messages/development) Slack channel and say hi ([signup](https://slack.babeljs.io))!
+- Issues with the [good first issue](https://github.com/babel/babel/labels/good%20first%20issue) and [help wanted](https://github.com/babel/babel/labels/help%20wanted) label. We suggest also looking at the [closed ones](https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aclosed+label%3A%22good+first+issue%22) to get a sense of the kinds of issues you can tackle.
+
+Some resources:
-- If you have already joined Slack, join our [#development](https://babeljs.slack.com/messages/development) channel and say hi!
-- Check out the issues with the [good first issue](https://github.com/babel/babel/labels/good%20first%20issue) and [help wanted](https://github.com/babel/babel/labels/help%20wanted) label. We suggest also looking at the closed ones to get a sense of the kinds of issues you can tackle.
+- Our [CONTRIBUTING.md](CONTRIBUTING.md) to get started with setting up the repo.
- Our discussions/notes/roadmap: [babel/notes](https://github.com/babel/notes)
- Our progress on TC39 proposals: [babel/proposals](https://github.com/babel/proposals)
+- Our blog which contains release posts and explanations: [/blog](https://babeljs.io/blog)
+- Our videos page with talks about open source and Babel: [/videos](https://babeljs.io/videos)
+- Our [podcast](https://podcast.babeljs.io)
### How is the repo structured?
diff --git a/babel.config.js b/babel.config.js
index 82966a7f07ca..ae2cb5a99cb3 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -14,22 +14,38 @@ module.exports = function(api) {
let convertESM = true;
let ignoreLib = true;
- let includeRuntime = false;
+ let includeRegeneratorRuntime = false;
+
+ let transformRuntimeOptions;
+
const nodeVersion = "6.9";
+ // The vast majority of our src files are modules, but we use
+ // unambiguous to keep things simple until we get around to renaming
+ // the modules to be more easily distinguished from CommonJS
+ const unambiguousSources = [
+ "packages/*/src",
+ "packages/*/test",
+ "codemods/*/src",
+ "codemods/*/test",
+ "eslint/*/src",
+ "eslint/*/test",
+ ];
switch (env) {
// Configs used during bundling builds.
- case "babel-parser":
- convertESM = false;
- ignoreLib = false;
- envOpts.targets = {
- node: nodeVersion,
- };
- break;
case "standalone":
+ includeRegeneratorRuntime = true;
+ unambiguousSources.push("packages/babel-runtime/regenerator");
+ case "rollup":
convertESM = false;
ignoreLib = false;
- includeRuntime = true;
+ // rollup-commonjs will converts node_modules to ESM
+ unambiguousSources.push(
+ "**/node_modules",
+ "packages/babel-preset-env/data",
+ "packages/babel-compat-data"
+ );
+ if (env === "rollup") envOpts.targets = { node: nodeVersion };
break;
case "production":
// Config during builds before publish.
@@ -50,6 +66,16 @@ module.exports = function(api) {
break;
}
+ if (includeRegeneratorRuntime) {
+ const babelRuntimePkgPath = require.resolve("@babel/runtime/package.json");
+
+ transformRuntimeOptions = {
+ helpers: false, // Helpers are handled by rollup when needed
+ regenerator: true,
+ version: require(babelRuntimePkgPath).version,
+ };
+ }
+
const config = {
// Our dependencies are all standard CommonJS, along with all sorts of
// other random files in Babel's codebase, so we use script as the default,
@@ -77,9 +103,14 @@ module.exports = function(api) {
"@babel/proposal-object-rest-spread",
{ useBuiltIns: true, loose: true },
],
+ ["@babel/plugin-proposal-optional-chaining", { loose: true }],
+ ["@babel/plugin-proposal-nullish-coalescing-operator", { loose: true }],
- // Explicitly use the lazy version of CommonJS modules.
- convertESM ? ["@babel/transform-modules-commonjs", { lazy: true }] : null,
+ convertESM ? "@babel/transform-modules-commonjs" : null,
+ // Until Jest supports native mjs, we must simulate it 🤷
+ env === "test" || env === "development"
+ ? "@babel/plugin-proposal-dynamic-import"
+ : null,
].filter(Boolean),
overrides: [
{
@@ -90,42 +121,25 @@ module.exports = function(api) {
],
},
{
- test: "./packages/babel-register",
+ test: ["./packages/babel-cli", "./packages/babel-core"],
plugins: [
- // Override the root options to disable lazy imports for babel-register
- // because otherwise the require hook will try to lazy-import things
- // leading to dependency cycles.
- convertESM ? "@babel/transform-modules-commonjs" : null,
+ // Explicitly use the lazy version of CommonJS modules.
+ convertESM
+ ? ["@babel/transform-modules-commonjs", { lazy: true }]
+ : null,
].filter(Boolean),
},
{
test: "./packages/babel-polyfill",
presets: [["@babel/env", envOptsNoTargets]],
- plugins: [["@babel/transform-modules-commonjs", { lazy: false }]],
},
{
- // The vast majority of our src files are modules, but we use
- // unambiguous to keep things simple until we get around to renaming
- // the modules to be more easily distinguished from CommonJS
- test: [
- "packages/*/src",
- "packages/*/test",
- "codemods/*/src",
- "codemods/*/test",
- ],
+ test: unambiguousSources,
sourceType: "unambiguous",
},
- {
- // The runtime transform shouldn't process its own runtime or core-js.
- exclude: [
- "packages/babel-runtime",
- /[\\/]node_modules[\\/](?:@babel\/runtime|babel-runtime|core-js)[\\/]/,
- ],
- plugins: [
- includeRuntime
- ? ["@babel/transform-runtime", { version: "7.4.4" }]
- : null,
- ].filter(Boolean),
+ includeRegeneratorRuntime && {
+ exclude: /regenerator-runtime/,
+ plugins: [["@babel/transform-runtime", transformRuntimeOptions]],
},
].filter(Boolean),
};
diff --git a/codecov.yml b/codecov.yml
index ed4545a1bf7d..17d3dfb026d0 100644
--- a/codecov.yml
+++ b/codecov.yml
@@ -5,7 +5,7 @@ coverage:
status:
project:
default:
- target: "80%"
+ target: "90%"
patch:
enabled: false
ignore:
diff --git a/codemods/babel-plugin-codemod-object-assign-to-object-spread/package.json b/codemods/babel-plugin-codemod-object-assign-to-object-spread/package.json
index 747fa0fe68e7..e432a89a4486 100644
--- a/codemods/babel-plugin-codemod-object-assign-to-object-spread/package.json
+++ b/codemods/babel-plugin-codemod-object-assign-to-object-spread/package.json
@@ -1,6 +1,6 @@
{
"name": "@babel/plugin-codemod-object-assign-to-object-spread",
- "version": "7.0.0",
+ "version": "7.7.4",
"description": "Transforms Object.assign into object spread syntax",
"repository": "https://github.com/babel/babel/tree/master/codemods/babel-plugin-codemod-object-assign-to-object-spread",
"license": "MIT",
@@ -13,16 +13,13 @@
"@babel/plugin"
],
"dependencies": {
- "@babel/plugin-syntax-object-rest-spread": "^7.0.0"
+ "@babel/plugin-syntax-object-rest-spread": "^7.7.4"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
},
"devDependencies": {
- "@babel/core": "^7.0.0",
- "@babel/helper-plugin-test-runner": "^7.0.0"
- },
- "publishConfig": {
- "access": "public"
+ "@babel/core": "^7.7.4",
+ "@babel/helper-plugin-test-runner": "^7.7.4"
}
}
diff --git a/codemods/babel-plugin-codemod-optional-catch-binding/package.json b/codemods/babel-plugin-codemod-optional-catch-binding/package.json
index 45913353021c..68196e27c1ba 100644
--- a/codemods/babel-plugin-codemod-optional-catch-binding/package.json
+++ b/codemods/babel-plugin-codemod-optional-catch-binding/package.json
@@ -1,6 +1,6 @@
{
"name": "@babel/plugin-codemod-optional-catch-binding",
- "version": "7.0.0",
+ "version": "7.7.4",
"description": "Remove unused catch bindings",
"repository": "https://github.com/babel/babel/tree/master/codemods/babel-plugin-codemod-remove-unused-catch-binding",
"license": "MIT",
@@ -13,13 +13,13 @@
"@babel/plugin"
],
"dependencies": {
- "@babel/plugin-syntax-optional-catch-binding": "^7.0.0"
+ "@babel/plugin-syntax-optional-catch-binding": "^7.7.4"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
},
"devDependencies": {
- "@babel/core": "^7.0.0",
- "@babel/helper-plugin-test-runner": "^7.0.0"
+ "@babel/core": "^7.7.4",
+ "@babel/helper-plugin-test-runner": "^7.7.4"
}
}
diff --git a/codemods/babel-plugin-codemod-optional-catch-binding/test/fixtures/codemod-optional-catch-binding/try-catch-finally-used-binding/options.json b/codemods/babel-plugin-codemod-optional-catch-binding/test/fixtures/codemod-optional-catch-binding/try-catch-finally-used-binding/options.json
index 0587911559c4..cbe0e9893752 100644
--- a/codemods/babel-plugin-codemod-optional-catch-binding/test/fixtures/codemod-optional-catch-binding/try-catch-finally-used-binding/options.json
+++ b/codemods/babel-plugin-codemod-optional-catch-binding/test/fixtures/codemod-optional-catch-binding/try-catch-finally-used-binding/options.json
@@ -1,3 +1,3 @@
{
- "plugins": ["../../../../lib"]
- }
+ "plugins": ["../../../../lib"]
+}
diff --git a/doc/design/monorepo.md b/doc/design/monorepo.md
index d10cc9741b76..0460dcd82b73 100644
--- a/doc/design/monorepo.md
+++ b/doc/design/monorepo.md
@@ -15,7 +15,7 @@ This is quite taboo but let's look at the pros and cons:
* Easy to coordinate changes across modules.
* Single place to report issues.
* Easier to setup a development environment.
- * Tests across modules are run together which finds bugs that touch multiple modules easier.
+ * Tests across modules are run together which finds bugs that touch multiple modules more easily.
**Cons:**
diff --git a/eslint/babel-eslint-config-internal/.npmignore b/eslint/babel-eslint-config-internal/.npmignore
new file mode 100644
index 000000000000..ad69326ce8c2
--- /dev/null
+++ b/eslint/babel-eslint-config-internal/.npmignore
@@ -0,0 +1,4 @@
+src
+test
+.*
+*.log
diff --git a/eslint/babel-eslint-config-internal/README.md b/eslint/babel-eslint-config-internal/README.md
new file mode 100644
index 000000000000..20d79ebc44bb
--- /dev/null
+++ b/eslint/babel-eslint-config-internal/README.md
@@ -0,0 +1,4 @@
+## @babel/eslint-config-internal
+---
+
+ESLint config for the Babel codebase (originally taken from `eslint-config-kittens`)
diff --git a/eslint/babel-eslint-config-internal/index.js b/eslint/babel-eslint-config-internal/index.js
new file mode 100644
index 000000000000..752207e50245
--- /dev/null
+++ b/eslint/babel-eslint-config-internal/index.js
@@ -0,0 +1,49 @@
+"use strict";
+
+module.exports = {
+ parser: "babel-eslint",
+ extends: "eslint:recommended",
+ plugins: ["flowtype"],
+ parserOptions: {
+ ecmaVersion: 2020,
+ sourceType: "module",
+ },
+ globals: {
+ // Flow
+ Iterator: true,
+ $Keys: true,
+ },
+ env: {
+ node: true,
+ es2020: true,
+ browser: true,
+ },
+ rules: {
+ camelcase: "off",
+ "consistent-return": "off",
+ curly: ["error", "multi-line"],
+ "linebreak-style": ["error", "unix"],
+ "new-cap": "off",
+ "no-case-declarations": "error",
+ "no-cond-assign": "off",
+ "no-confusing-arrow": "error",
+ "no-console": "off",
+ "no-constant-condition": "off",
+ "no-empty": "off",
+ "no-fallthrough": "off",
+ "no-inner-declarations": "off",
+ "no-labels": "off",
+ "no-loop-func": "off",
+ "no-process-exit": "off",
+ "no-return-assign": "off",
+ "no-shadow": "off",
+ "no-underscore-dangle": "off",
+ "no-unreachable": "off",
+ "no-use-before-define": "off",
+ "no-var": "error",
+ "prefer-const": "error",
+ strict: "off",
+ "flowtype/define-flow-type": "warn",
+ "flowtype/use-flow-type": "warn",
+ },
+};
diff --git a/eslint/babel-eslint-config-internal/package.json b/eslint/babel-eslint-config-internal/package.json
new file mode 100644
index 000000000000..37a02b8826ab
--- /dev/null
+++ b/eslint/babel-eslint-config-internal/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "@babel/eslint-config-internal",
+ "version": "0.0.0",
+ "description": "The Babel Team's ESLint configuration. Since it's internal, it might not respect semver.",
+ "author": "Sebastian McKenzie ",
+ "homepage": "https://babeljs.io/",
+ "license": "MIT",
+ "private": true,
+ "repository": "https://github.com/babel/babel/tree/master/eslint/babel-eslint-config-internal",
+ "main": "lib/index.js",
+ "peerDependencies": {
+ "babel-eslint": "^10.0.0 || ^11.0.0-0",
+ "eslint-plugin-flowtype": "^3.0.0"
+ }
+}
diff --git a/eslint/babel-eslint-parser/.npmignore b/eslint/babel-eslint-parser/.npmignore
new file mode 100644
index 000000000000..c3aafe7a7721
--- /dev/null
+++ b/eslint/babel-eslint-parser/.npmignore
@@ -0,0 +1,4 @@
+src
+test
+.*
+*.log
\ No newline at end of file
diff --git a/eslint/babel-eslint-parser/README.md b/eslint/babel-eslint-parser/README.md
new file mode 100644
index 000000000000..5d27fc7211eb
--- /dev/null
+++ b/eslint/babel-eslint-parser/README.md
@@ -0,0 +1,124 @@
+# @babel/eslint-parser [![npm](https://img.shields.io/npm/v/@babel/eslint-parser.svg)](https://www.npmjs.com/package/@babel/eslint-parser) [![travis](https://img.shields.io/travis/babel/@babel/eslint-parser/master.svg)](https://travis-ci.org/babel/@babel/eslint-parser) [![npm-downloads](https://img.shields.io/npm/dm/@babel/eslint-parser.svg)](https://www.npmjs.com/package/@babel/eslint-parser)
+
+**@babel/eslint-parser** allows you to lint **ALL** valid Babel code with the fantastic
+[ESLint](https://github.com/eslint/eslint).
+
+## When should I use @babel/eslint-parser?
+
+ESLint's default parser and core rules [only support the latest final ECMAScript standard](https://github.com/eslint/eslint/blob/a675c89573836adaf108a932696b061946abf1e6/README.md#what-about-experimental-features) and do not support experimental (such as new features) and non-standard (such as Flow or TypeScript types) syntax provided by Babel. @babel/eslint-parser is a parser that allows ESLint to run on source code that is transformed by Babel.
+
+**Note:** You only need to use @babel/parser-eslint if you are using Babel to transform your code. If this is not the case, please use the relevant parser for your chosen flavor of ECMAScript (note that the default parser supports all non-experimental syntax as well as JSX).
+
+## How does it work?
+
+ESLint allows for the use of [custom parsers](https://eslint.org/docs/developer-guide/working-with-custom-parsers). When using this plugin, your code is parsed by Babel's parser (using the configuration specified in your [Babel configuration file](https://babeljs.io/docs/en/configuration)) and the resulting AST is
+transformed into an [ESTree](https://github.com/estree/estree)-compliant structure that ESLint can understand. All location info such as line numbers,
+columns is also retained so you can track down errors with ease.
+
+**Note:** ESLint's core rules do not support experimental syntax and may therefore not work as expected when using `@babel/eslint-parser`. Please use the companion [`@babel/eslint-plugin`](https://github.com/babel/babel/tree/master/eslint/babel-eslint-plugin) plugin for core rules that you have issues with.
+
+## Usage
+
+### Installation
+
+```sh
+$ npm install eslint @babel/core @babel/eslint-parser --save-dev
+# or
+$ yarn add eslint @babel/core @babel/eslint-parser -D
+```
+
+**Note:** @babel/eslint-parser requires `@babel/core@>=7.2.0` and a valid Babel configuration file to run. If you do not have this already set up, please see the [Babel Usage Guide](https://babeljs.io/docs/en/usage).
+
+### Setup
+
+To use @babel/eslint-parser, `"@babel/eslint-parser"` must be specified as the `parser` in your ESLint configuration file (see [here](https://eslint.org/docs/user-guide/configuring#specifying-parser) for more detailed information).
+
+**.eslintrc.js**
+
+```js
+module.exports = {
+ parser: "@babel/eslint-parser",
+};
+```
+
+With the parser set, your configuration can be configured as described in the [Configuring ESLint](https://eslint.org/docs/user-guide/configuring) documentation.
+
+**Note:** The `parserOptions` described in the [official documentation](https://eslint.org/docs/user-guide/configuring#specifying-parser-options) are for the default parser and are not necessarily supported by @babel/eslint-parser. Please see the section directly below for supported `parserOptions`.
+
+### Additional parser configuration
+
+Additional configuration options can be set in your ESLint configuration under the `parserOptions` key. Please note that the `ecmaFeatures` config property may still be required for ESLint to work properly with features not in ECMAScript 5 by default.
+
+- `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-based-on-glob-patterns). Note: @babel/eslint-parser will not parse any experimental syntax when no configuration file is found.
+- `sourceType` can be set to `"module"`(default) or `"script"` if your code isn't using ECMAScript modules.
+- `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.
+- `ecmaFeatures.globalReturn` (default `false`) allow return statements in the global scope when used with `sourceType: "script"`.
+- `babelOptions` passes through Babel's configuration [loading](https://babeljs.io/docs/en/options#config-loading-options) and [merging](https://babeljs.io/docs/en/options#config-merging-options) options (for instance, in case of a monorepo). When not defined, @babel/eslint-parser will use Babel's default configuration file resolution logic.
+
+**.eslintrc.js**
+
+```js
+module.exports = {
+ parser: "@babel/eslint-parser",
+ parserOptions: {
+ sourceType: "module",
+ allowImportExportEverywhere: false,
+ ecmaFeatures: {
+ globalReturn: false,
+ },
+ babelOptions: {
+ configFile: "path/to/config.js",
+ },
+ },
+};
+```
+
+**.eslintrc.js using glob-based configuration**
+
+This configuration would use the default parser for all files except for those found by the `"files/transformed/by/babel/*.js"` glob.
+
+```js
+module.exports = {
+ rules: {
+ indent: "error"
+ },
+ overrides: [
+ {
+ files: ["files/transformed/by/babel/*.js"],
+ parser: "@babel/eslint-parser",
+ }
+ ]
+};
+```
+
+### Run
+
+```sh
+$ ./node_modules/.bin/eslint yourfile.js
+```
+
+## Known issues
+
+Flow:
+
+> Check out [eslint-plugin-flowtype](https://github.com/gajus/eslint-plugin-flowtype): An `eslint` plugin that makes flow type annotations global variables and marks declarations as used. Solves the problem of false positives with `no-undef` and `no-unused-vars`.
+
+- `no-undef` for global flow types: `ReactElement`, `ReactClass` [#130](https://github.com/babel/@babel/eslint-parser/issues/130#issuecomment-111215076)
+ - Workaround: define types as globals in `.eslintrc` or define types and import them `import type ReactElement from './types'`
+- `no-unused-vars/no-undef` with Flow declarations (`declare module A {}`) [#132](https://github.com/babel/@babel/eslint-parser/issues/132#issuecomment-112815926)
+
+Modules/strict mode
+
+- `no-unused-vars: ["error", { vars: local }]` [#136](https://github.com/babel/@babel/eslint-parser/issues/136)
+
+Please check out [eslint-plugin-react](https://github.com/yannickcr/eslint-plugin-react) for React/JSX issues.
+
+- `no-unused-vars` with jsx
+
+Please check out [eslint-plugin-babel](https://github.com/babel/eslint-plugin-babel) for other issues.
+
+## Questions and support
+
+If you have an issue, please first check if it can be reproduced with the default parser and with the latest versions of `eslint` and `@babel/eslint-parser`. If it is not reproducible with the default parser, it is most likely an issue with `@babel/eslint-parser`.
+
+For questions and support please visit the [`#discussion`](https://babeljs.slack.com/messages/discussion/) Babel Slack channel (sign up [here](https://github.com/babel/notes/issues/38)) or the ESLint [Gitter](https://gitter.im/eslint/eslint).
diff --git a/eslint/babel-eslint-parser/package.json b/eslint/babel-eslint-parser/package.json
new file mode 100644
index 000000000000..bf3ea3e45b05
--- /dev/null
+++ b/eslint/babel-eslint-parser/package.json
@@ -0,0 +1,37 @@
+{
+ "name": "@babel/eslint-parser",
+ "version": "0.0.0",
+ "description": "ESLint parser that allows for linting of experimental syntax transformed by Babel",
+ "author": "Sebastian McKenzie ",
+ "license": "MIT",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/babel/babel.git",
+ "directory": "eslint/babel-eslint-parser"
+ },
+ "bugs": {
+ "url": "https://github.com/babel/babel/issues"
+ },
+ "homepage": "https://github.com/babel/babel/tree/master/eslint/babel-eslint-parser",
+ "engines": {
+ "node": ">=10.9"
+ },
+ "main": "lib/index.js",
+ "peerDependencies": {
+ "@babel/core": ">=7.2.0",
+ "eslint": ">=6.0.0"
+ },
+ "dependencies": {
+ "eslint-scope": "5.0.0",
+ "eslint-visitor-keys": "^1.1.0",
+ "semver": "^6.3.0"
+ },
+ "devDependencies": {
+ "@babel/eslint-shared-fixtures": "*",
+ "@babel/core": "^7.2.0",
+ "dedent": "^0.7.0",
+ "eslint": "^6.0.1",
+ "espree": "^6.0.0"
+ }
+}
diff --git a/eslint/babel-eslint-parser/src/analyze-scope.js b/eslint/babel-eslint-parser/src/analyze-scope.js
new file mode 100644
index 000000000000..ffb9eb95ba82
--- /dev/null
+++ b/eslint/babel-eslint-parser/src/analyze-scope.js
@@ -0,0 +1,344 @@
+import { types as t } from "@babel/core";
+import escope from "eslint-scope";
+import { Definition } from "eslint-scope/lib/definition";
+import OriginalPatternVisitor from "eslint-scope/lib/pattern-visitor";
+import OriginalReferencer from "eslint-scope/lib/referencer";
+import { getKeys as fallback } from "eslint-visitor-keys";
+import childVisitorKeys from "./visitor-keys";
+
+const flowFlippedAliasKeys = t.FLIPPED_ALIAS_KEYS.Flow.concat([
+ "ArrayPattern",
+ "ClassDeclaration",
+ "ClassExpression",
+ "FunctionDeclaration",
+ "FunctionExpression",
+ "Identifier",
+ "ObjectPattern",
+ "RestElement",
+]);
+
+const visitorKeysMap = Object.entries(t.VISITOR_KEYS).reduce(
+ (acc, [key, value]) => {
+ if (!flowFlippedAliasKeys.includes(value)) {
+ acc[key] = value;
+ }
+ return acc;
+ },
+ {},
+);
+
+const propertyTypes = {
+ // loops
+ callProperties: { type: "loop", values: ["value"] },
+ indexers: { type: "loop", values: ["key", "value"] },
+ properties: { type: "loop", values: ["argument", "value"] },
+ types: { type: "loop" },
+ params: { type: "loop" },
+ // single property
+ argument: { type: "single" },
+ elementType: { type: "single" },
+ qualification: { type: "single" },
+ rest: { type: "single" },
+ returnType: { type: "single" },
+ // others
+ typeAnnotation: { type: "typeAnnotation" },
+ typeParameters: { type: "typeParameters" },
+ id: { type: "id" },
+};
+
+class PatternVisitor extends OriginalPatternVisitor {
+ ArrayPattern(node) {
+ node.elements.forEach(this.visit, this);
+ }
+
+ ObjectPattern(node) {
+ node.properties.forEach(this.visit, this);
+ }
+}
+
+class Referencer extends OriginalReferencer {
+ // inherits.
+ visitPattern(node, options, callback) {
+ if (!node) {
+ return;
+ }
+
+ // Visit type annotations.
+ this._checkIdentifierOrVisit(node.typeAnnotation);
+ if (t.isAssignmentPattern(node)) {
+ this._checkIdentifierOrVisit(node.left.typeAnnotation);
+ }
+
+ // Overwrite `super.visitPattern(node, options, callback)` in order to not visit `ArrayPattern#typeAnnotation` and `ObjectPattern#typeAnnotation`.
+ if (typeof options === "function") {
+ callback = options;
+ options = { processRightHandNodes: false };
+ }
+
+ const visitor = new PatternVisitor(this.options, node, callback);
+ visitor.visit(node);
+
+ // Process the right hand nodes recursively.
+ if (options.processRightHandNodes) {
+ visitor.rightHandNodes.forEach(this.visit, this);
+ }
+ }
+
+ // inherits.
+ visitClass(node) {
+ // Decorators.
+ this._visitArray(node.decorators);
+
+ // Flow type parameters.
+ const typeParamScope = this._nestTypeParamScope(node);
+
+ // Flow super types.
+ this._visitTypeAnnotation(node.implements);
+ this._visitTypeAnnotation(
+ node.superTypeParameters && node.superTypeParameters.params,
+ );
+
+ // Basic.
+ super.visitClass(node);
+
+ // Close the type parameter scope.
+ if (typeParamScope) {
+ this.close(node);
+ }
+ }
+
+ // inherits.
+ visitFunction(node) {
+ const typeParamScope = this._nestTypeParamScope(node);
+
+ // Flow return types.
+ this._checkIdentifierOrVisit(node.returnType);
+
+ // Basic.
+ super.visitFunction(node);
+
+ // Close the type parameter scope.
+ if (typeParamScope) {
+ this.close(node);
+ }
+ }
+
+ // inherits.
+ visitProperty(node) {
+ if (node.value?.type === "TypeCastExpression") {
+ this._visitTypeAnnotation(node.value);
+ }
+ this._visitArray(node.decorators);
+ super.visitProperty(node);
+ }
+
+ InterfaceDeclaration(node) {
+ this._createScopeVariable(node, node.id);
+
+ const typeParamScope = this._nestTypeParamScope(node);
+
+ // TODO: Handle mixins
+ this._visitArray(node.extends);
+ this.visit(node.body);
+
+ if (typeParamScope) {
+ this.close(node);
+ }
+ }
+
+ TypeAlias(node) {
+ this._createScopeVariable(node, node.id);
+
+ const typeParamScope = this._nestTypeParamScope(node);
+
+ this.visit(node.right);
+
+ if (typeParamScope) {
+ this.close(node);
+ }
+ }
+
+ ClassProperty(node) {
+ this._visitClassProperty(node);
+ }
+
+ ClassPrivateProperty(node) {
+ this._visitClassProperty(node);
+ }
+
+ // TODO: Update to visit type annotations when TypeScript/Flow support this syntax.
+ ClassPrivateMethod(node) {
+ super.MethodDefinition(node);
+ }
+
+ DeclareModule(node) {
+ this._visitDeclareX(node);
+ }
+
+ DeclareFunction(node) {
+ this._visitDeclareX(node);
+ }
+
+ DeclareVariable(node) {
+ this._visitDeclareX(node);
+ }
+
+ DeclareClass(node) {
+ this._visitDeclareX(node);
+ }
+
+ // visit OptionalMemberExpression as a MemberExpression.
+ OptionalMemberExpression(node) {
+ super.MemberExpression(node);
+ }
+
+ _visitClassProperty(node) {
+ this._visitTypeAnnotation(node.typeAnnotation);
+ this.visitProperty(node);
+ }
+
+ _visitDeclareX(node) {
+ if (node.id) {
+ this._createScopeVariable(node, node.id);
+ }
+
+ const typeParamScope = this._nestTypeParamScope(node);
+ if (typeParamScope) {
+ this.close(node);
+ }
+ }
+
+ _createScopeVariable(node, name) {
+ this.currentScope().variableScope.__define(
+ name,
+ new Definition("Variable", name, node, null, null, null),
+ );
+ }
+
+ _nestTypeParamScope(node) {
+ if (!node.typeParameters) {
+ return null;
+ }
+
+ const parentScope = this.scopeManager.__currentScope;
+ const scope = new escope.Scope(
+ this.scopeManager,
+ "type-parameters",
+ parentScope,
+ node,
+ false,
+ );
+
+ this.scopeManager.__nestScope(scope);
+ for (let j = 0; j < node.typeParameters.params.length; j++) {
+ const name = node.typeParameters.params[j];
+ scope.__define(name, new Definition("TypeParameter", name, name));
+ if (name.typeAnnotation) {
+ this._checkIdentifierOrVisit(name);
+ }
+ }
+ scope.__define = function() {
+ return parentScope.__define.apply(parentScope, arguments);
+ };
+
+ return scope;
+ }
+
+ _visitTypeAnnotation(node) {
+ if (!node) {
+ return;
+ }
+ if (Array.isArray(node)) {
+ node.forEach(this._visitTypeAnnotation, this);
+ return;
+ }
+
+ // get property to check (params, id, etc...)
+ const visitorValues = visitorKeysMap[node.type];
+ if (!visitorValues) {
+ return;
+ }
+
+ // can have multiple properties
+ for (let i = 0; i < visitorValues.length; i++) {
+ const visitorValue = visitorValues[i];
+ const propertyType = propertyTypes[visitorValue];
+ const nodeProperty = node[visitorValue];
+ // check if property or type is defined
+ if (propertyType == null || nodeProperty == null) {
+ continue;
+ }
+ if (propertyType.type === "loop") {
+ for (let j = 0; j < nodeProperty.length; j++) {
+ if (Array.isArray(propertyType.values)) {
+ for (let k = 0; k < propertyType.values.length; k++) {
+ const loopPropertyNode = nodeProperty[j][propertyType.values[k]];
+ if (loopPropertyNode) {
+ this._checkIdentifierOrVisit(loopPropertyNode);
+ }
+ }
+ } else {
+ this._checkIdentifierOrVisit(nodeProperty[j]);
+ }
+ }
+ } else if (propertyType.type === "single") {
+ this._checkIdentifierOrVisit(nodeProperty);
+ } else if (propertyType.type === "typeAnnotation") {
+ this._visitTypeAnnotation(node.typeAnnotation);
+ } else if (propertyType.type === "typeParameters") {
+ for (let l = 0; l < node.typeParameters.params.length; l++) {
+ this._checkIdentifierOrVisit(node.typeParameters.params[l]);
+ }
+ } else if (propertyType.type === "id") {
+ if (node.id.type === "Identifier") {
+ this._checkIdentifierOrVisit(node.id);
+ } else {
+ this._visitTypeAnnotation(node.id);
+ }
+ }
+ }
+ }
+
+ _checkIdentifierOrVisit(node) {
+ if (node?.typeAnnotation) {
+ this._visitTypeAnnotation(node.typeAnnotation);
+ } else if (node?.type === "Identifier") {
+ this.visit(node);
+ } else {
+ this._visitTypeAnnotation(node);
+ }
+ }
+
+ _visitArray(nodeList) {
+ if (nodeList) {
+ for (const node of nodeList) {
+ this.visit(node);
+ }
+ }
+ }
+}
+
+export default function(ast, parserOptions) {
+ const options = {
+ ignoreEval: true,
+ optimistic: false,
+ directive: false,
+ nodejsScope:
+ ast.sourceType === "script" &&
+ (parserOptions.ecmaFeatures &&
+ parserOptions.ecmaFeatures.globalReturn) === true,
+ impliedStrict: false,
+ sourceType: ast.sourceType,
+ ecmaVersion: parserOptions.ecmaVersion,
+ fallback,
+ };
+
+ options.childVisitorKeys = childVisitorKeys;
+
+ const scopeManager = new escope.ScopeManager(options);
+ const referencer = new Referencer(options, scopeManager);
+
+ referencer.visit(ast);
+
+ return scopeManager;
+}
diff --git a/eslint/babel-eslint-parser/src/babylon-to-espree/convertAST.js b/eslint/babel-eslint-parser/src/babylon-to-espree/convertAST.js
new file mode 100644
index 000000000000..4e048668717b
--- /dev/null
+++ b/eslint/babel-eslint-parser/src/babylon-to-espree/convertAST.js
@@ -0,0 +1,96 @@
+import { types as t, traverse } from "@babel/core";
+import convertProgramNode from "./convertProgramNode";
+
+const astTransformVisitor = {
+ noScope: true,
+ enter(path) {
+ const node = path.node;
+
+ // private var to track original node type
+ node._babelType = node.type;
+
+ if (node.innerComments) {
+ delete node.innerComments;
+ }
+
+ if (node.trailingComments) {
+ delete node.trailingComments;
+ }
+
+ if (node.leadingComments) {
+ delete node.leadingComments;
+ }
+ },
+ exit(path) {
+ const node = path.node;
+
+ if (path.isTypeParameter()) {
+ node.type = "Identifier";
+ node.typeAnnotation = node.bound;
+ delete node.bound;
+ }
+
+ // flow: prevent "no-undef"
+ // for "Component" in: "let x: React.Component"
+ if (path.isQualifiedTypeIdentifier()) {
+ delete node.id;
+ }
+ // for "b" in: "var a: { b: Foo }"
+ if (path.isObjectTypeProperty()) {
+ delete node.key;
+ }
+ // for "indexer" in: "var a: {[indexer: string]: number}"
+ if (path.isObjectTypeIndexer()) {
+ delete node.id;
+ }
+ // for "param" in: "var a: { func(param: Foo): Bar };"
+ if (path.isFunctionTypeParam()) {
+ delete node.name;
+ }
+
+ // modules
+ if (path.isImportDeclaration()) {
+ delete node.isType;
+ }
+
+ // template string range fixes
+ if (path.isTemplateLiteral()) {
+ for (let i = 0; i < node.quasis.length; i++) {
+ const q = node.quasis[i];
+ q.range[0] -= 1;
+ if (q.tail) {
+ q.range[1] += 1;
+ } else {
+ q.range[1] += 2;
+ }
+ q.loc.start.column -= 1;
+ if (q.tail) {
+ q.loc.end.column += 1;
+ } else {
+ q.loc.end.column += 2;
+ }
+ }
+ }
+ },
+};
+
+export default function(ast, code) {
+ const state = { source: code };
+
+ // Monkey patch visitor keys in order to be able to traverse the estree nodes
+ t.VISITOR_KEYS.Property = t.VISITOR_KEYS.ObjectProperty;
+ t.VISITOR_KEYS.MethodDefinition = [
+ "key",
+ "value",
+ "decorators",
+ "returnType",
+ "typeParameters",
+ ];
+
+ traverse(ast, astTransformVisitor, null, state);
+
+ delete t.VISITOR_KEYS.Property;
+ delete t.VISITOR_KEYS.MethodDefinition;
+
+ convertProgramNode(ast);
+}
diff --git a/eslint/babel-eslint-parser/src/babylon-to-espree/convertComments.js b/eslint/babel-eslint-parser/src/babylon-to-espree/convertComments.js
new file mode 100644
index 000000000000..b52ec9ff5a48
--- /dev/null
+++ b/eslint/babel-eslint-parser/src/babylon-to-espree/convertComments.js
@@ -0,0 +1,15 @@
+export default function(comments) {
+ for (let i = 0; i < comments.length; i++) {
+ const comment = comments[i];
+ if (comment.type === "CommentBlock") {
+ comment.type = "Block";
+ } else if (comment.type === "CommentLine") {
+ comment.type = "Line";
+ }
+ // sometimes comments don't get ranges computed,
+ // even with options.ranges === true
+ if (!comment.range) {
+ comment.range = [comment.start, comment.end];
+ }
+ }
+}
diff --git a/eslint/babel-eslint-parser/src/babylon-to-espree/convertProgramNode.js b/eslint/babel-eslint-parser/src/babylon-to-espree/convertProgramNode.js
new file mode 100644
index 000000000000..a435487cb6a9
--- /dev/null
+++ b/eslint/babel-eslint-parser/src/babylon-to-espree/convertProgramNode.js
@@ -0,0 +1,38 @@
+export default function(ast) {
+ ast.type = "Program";
+ ast.sourceType = ast.program.sourceType;
+ ast.directives = ast.program.directives;
+ ast.body = ast.program.body;
+ delete ast.program;
+
+ if (ast.comments.length) {
+ const lastComment = ast.comments[ast.comments.length - 1];
+
+ if (!ast.tokens.length) {
+ // if no tokens, the program starts at the end of the last comment
+ ast.start = lastComment.end;
+ ast.loc.start.line = lastComment.loc.end.line;
+ ast.loc.start.column = lastComment.loc.end.column;
+ } else {
+ const lastToken = ast.tokens[ast.tokens.length - 1];
+
+ if (lastComment.end > lastToken.end) {
+ // If there is a comment after the last token, the program ends at the
+ // last token and not the comment
+ ast.range[1] = lastToken.end;
+ ast.loc.end.line = lastToken.loc.end.line;
+ ast.loc.end.column = lastToken.loc.end.column;
+ }
+ }
+ } else {
+ if (!ast.tokens.length) {
+ ast.loc.start.line = 1;
+ ast.loc.end.line = 1;
+ }
+ }
+
+ if (ast.body && ast.body.length > 0) {
+ ast.loc.start.line = ast.body[0].loc.start.line;
+ ast.range[0] = ast.body[0].start;
+ }
+}
diff --git a/eslint/babel-eslint-parser/src/babylon-to-espree/convertTemplateType.js b/eslint/babel-eslint-parser/src/babylon-to-espree/convertTemplateType.js
new file mode 100644
index 000000000000..3b674adae624
--- /dev/null
+++ b/eslint/babel-eslint-parser/src/babylon-to-espree/convertTemplateType.js
@@ -0,0 +1,92 @@
+import { tokTypes as tt } from "@babel/core";
+
+export default function(tokens) {
+ let curlyBrace = null;
+ let templateTokens = [];
+ const result = [];
+
+ function addTemplateType() {
+ const start = templateTokens[0];
+ const end = templateTokens[templateTokens.length - 1];
+
+ const value = templateTokens.reduce((result, token) => {
+ if (token.value) {
+ result += token.value;
+ } else if (token.type !== tt.template) {
+ result += token.type.label;
+ }
+
+ return result;
+ }, "");
+
+ result.push({
+ type: "Template",
+ value: value,
+ start: start.start,
+ end: end.end,
+ loc: {
+ start: start.loc.start,
+ end: end.loc.end,
+ },
+ });
+
+ templateTokens = [];
+ }
+
+ tokens.forEach(token => {
+ switch (token.type) {
+ case tt.backQuote:
+ if (curlyBrace) {
+ result.push(curlyBrace);
+ curlyBrace = null;
+ }
+
+ templateTokens.push(token);
+
+ if (templateTokens.length > 1) {
+ addTemplateType();
+ }
+
+ break;
+
+ case tt.dollarBraceL:
+ templateTokens.push(token);
+ addTemplateType();
+ break;
+
+ case tt.braceR:
+ if (curlyBrace) {
+ result.push(curlyBrace);
+ }
+
+ curlyBrace = token;
+ break;
+
+ case tt.template:
+ if (curlyBrace) {
+ templateTokens.push(curlyBrace);
+ curlyBrace = null;
+ }
+
+ templateTokens.push(token);
+ break;
+
+ case tt.eof:
+ if (curlyBrace) {
+ result.push(curlyBrace);
+ }
+
+ break;
+
+ default:
+ if (curlyBrace) {
+ result.push(curlyBrace);
+ curlyBrace = null;
+ }
+
+ result.push(token);
+ }
+ });
+
+ return result;
+}
diff --git a/eslint/babel-eslint-parser/src/babylon-to-espree/convertToken.js b/eslint/babel-eslint-parser/src/babylon-to-espree/convertToken.js
new file mode 100644
index 000000000000..d8c9f09092f7
--- /dev/null
+++ b/eslint/babel-eslint-parser/src/babylon-to-espree/convertToken.js
@@ -0,0 +1,88 @@
+import { tokTypes as tt } from "@babel/core";
+
+export default function(token, source) {
+ const type = token.type;
+ token.range = [token.start, token.end];
+
+ if (type === tt.name) {
+ token.type = "Identifier";
+ } else if (
+ type === tt.semi ||
+ type === tt.comma ||
+ type === tt.parenL ||
+ type === tt.parenR ||
+ type === tt.braceL ||
+ type === tt.braceR ||
+ type === tt.slash ||
+ type === tt.dot ||
+ type === tt.bracketL ||
+ type === tt.bracketR ||
+ type === tt.ellipsis ||
+ type === tt.arrow ||
+ type === tt.pipeline ||
+ type === tt.star ||
+ type === tt.incDec ||
+ type === tt.colon ||
+ type === tt.question ||
+ type === tt.questionDot ||
+ type === tt.template ||
+ type === tt.backQuote ||
+ type === tt.dollarBraceL ||
+ type === tt.at ||
+ type === tt.logicalOR ||
+ type === tt.logicalAND ||
+ type === tt.nullishCoalescing ||
+ type === tt.bitwiseOR ||
+ type === tt.bitwiseXOR ||
+ type === tt.bitwiseAND ||
+ type === tt.equality ||
+ type === tt.relational ||
+ type === tt.bitShift ||
+ type === tt.plusMin ||
+ type === tt.modulo ||
+ type === tt.exponent ||
+ type === tt.bang ||
+ type === tt.tilde ||
+ type === tt.doubleColon ||
+ type === tt.hash ||
+ type.isAssign
+ ) {
+ token.type = "Punctuator";
+ if (!token.value) token.value = type.label;
+ } else if (type === tt.jsxTagStart) {
+ token.type = "Punctuator";
+ token.value = "<";
+ } else if (type === tt.jsxTagEnd) {
+ token.type = "Punctuator";
+ token.value = ">";
+ } else if (type === tt.jsxName) {
+ token.type = "JSXIdentifier";
+ } else if (type === tt.jsxText) {
+ token.type = "JSXText";
+ } else if (type.keyword === "null") {
+ token.type = "Null";
+ } else if (type.keyword === "false" || type.keyword === "true") {
+ token.type = "Boolean";
+ } else if (type.keyword) {
+ token.type = "Keyword";
+ } else if (type === tt.num) {
+ token.type = "Numeric";
+ token.value = source.slice(token.start, token.end);
+ } else if (type === tt.string) {
+ token.type = "String";
+ token.value = source.slice(token.start, token.end);
+ } else if (type === tt.regexp) {
+ token.type = "RegularExpression";
+ const value = token.value;
+ token.regex = {
+ pattern: value.pattern,
+ flags: value.flags,
+ };
+ token.value = `/${value.pattern}/${value.flags}`;
+ } else if (type === tt.bigint) {
+ token.type = "Numeric";
+ token.value = `${token.value}n`;
+ }
+
+ return token;
+}
diff --git a/eslint/babel-eslint-parser/src/babylon-to-espree/convertTokens.js b/eslint/babel-eslint-parser/src/babylon-to-espree/convertTokens.js
new file mode 100644
index 000000000000..44f4ef41bc3f
--- /dev/null
+++ b/eslint/babel-eslint-parser/src/babylon-to-espree/convertTokens.js
@@ -0,0 +1,8 @@
+import convertTemplateType from "./convertTemplateType";
+import convertToken from "./convertToken";
+
+export default function(tokens, code) {
+ return convertTemplateType(tokens)
+ .filter(t => t.type !== "CommentLine" && t.type !== "CommentBlock")
+ .map(t => convertToken(t, code));
+}
diff --git a/eslint/babel-eslint-parser/src/babylon-to-espree/index.js b/eslint/babel-eslint-parser/src/babylon-to-espree/index.js
new file mode 100644
index 000000000000..7a9de2b4cc0b
--- /dev/null
+++ b/eslint/babel-eslint-parser/src/babylon-to-espree/index.js
@@ -0,0 +1,9 @@
+import convertTokens from "./convertTokens";
+import convertComments from "./convertComments";
+import convertAST from "./convertAST";
+
+export default function(ast, code) {
+ ast.tokens = convertTokens(ast.tokens, code);
+ convertComments(ast.comments);
+ convertAST(ast, code);
+}
diff --git a/eslint/babel-eslint-parser/src/configuration.js b/eslint/babel-eslint-parser/src/configuration.js
new file mode 100644
index 000000000000..e6d036372f12
--- /dev/null
+++ b/eslint/babel-eslint-parser/src/configuration.js
@@ -0,0 +1,61 @@
+import { loadPartialConfig } from "@babel/core";
+
+export function normalizeESLintConfig(options) {
+ const defaultOptions = {
+ babelOptions: {},
+ ecmaVersion: 2020,
+ sourceType: "module",
+ allowImportExportEverywhere: false,
+ };
+
+ return Object.assign(defaultOptions, options);
+}
+
+export function normalizeBabelParseConfig(options) {
+ const parseOptions = {
+ sourceType: options.sourceType,
+ filename: options.filePath,
+ cwd: options.babelOptions.cwd,
+ root: options.babelOptions.root,
+ rootMode: options.babelOptions.rootMode,
+ envName: options.babelOptions.envName,
+ configFile: options.babelOptions.configFile,
+ babelrc: options.babelOptions.babelrc,
+ babelrcRoots: options.babelOptions.babelrcRoots,
+ extends: options.babelOptions.extends,
+ env: options.babelOptions.env,
+ overrides: options.babelOptions.overrides,
+ test: options.babelOptions.test,
+ include: options.babelOptions.include,
+ exclude: options.babelOptions.exclude,
+ ignore: options.babelOptions.ignore,
+ only: options.babelOptions.only,
+ parserOpts: {
+ allowImportExportEverywhere: options.allowImportExportEverywhere,
+ allowReturnOutsideFunction: true,
+ allowSuperOutsideMethod: true,
+ ranges: true,
+ tokens: true,
+ plugins: ["estree"],
+ },
+ caller: {
+ name: "@babel/eslint-parser",
+ },
+ };
+
+ if (options.requireConfigFile !== false) {
+ const config = loadPartialConfig(parseOptions);
+
+ if (config !== null) {
+ if (!config.hasFilesystemConfig()) {
+ throw new Error(
+ `No Babel config file detected for ${config.options.filename}. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the config files.`,
+ );
+ }
+
+ return config.options;
+ }
+ }
+
+ return parseOptions;
+}
diff --git a/eslint/babel-eslint-parser/src/index.js b/eslint/babel-eslint-parser/src/index.js
new file mode 100644
index 000000000000..2d54014b4468
--- /dev/null
+++ b/eslint/babel-eslint-parser/src/index.js
@@ -0,0 +1,26 @@
+import semver from "semver";
+import { version as CURRENT_BABEL_VERSION } from "@babel/core";
+import parseWithScope from "./parse-with-scope";
+import { normalizeESLintConfig } from "./configuration";
+import packageJson from "../package.json";
+
+const SUPPORTED_BABEL_VERSION_RANGE =
+ packageJson.peerDependencies["@babel/core"];
+const IS_RUNNING_SUPPORTED_VERSION = semver.satisfies(
+ CURRENT_BABEL_VERSION,
+ SUPPORTED_BABEL_VERSION_RANGE,
+);
+
+export function parse(code, options) {
+ return parseForESLint(code, options).ast;
+}
+
+export function parseForESLint(code, options = {}) {
+ if (!IS_RUNNING_SUPPORTED_VERSION) {
+ throw new Error(
+ `babel-eslint@${packageJson.version} does not support @babel/core@${CURRENT_BABEL_VERSION}. Please downgrade to babel-eslint@^10 or upgrade to @babel/core@${SUPPORTED_BABEL_VERSION_RANGE}`,
+ );
+ }
+
+ return parseWithScope(code, normalizeESLintConfig(options));
+}
diff --git a/eslint/babel-eslint-parser/src/parse-with-scope.js b/eslint/babel-eslint-parser/src/parse-with-scope.js
new file mode 100644
index 000000000000..17516f5724d7
--- /dev/null
+++ b/eslint/babel-eslint-parser/src/parse-with-scope.js
@@ -0,0 +1,10 @@
+import visitorKeys from "./visitor-keys";
+import analyzeScope from "./analyze-scope";
+import parse from "./parse";
+
+export default function(code, options) {
+ const ast = parse(code, options);
+ const scopeManager = analyzeScope(ast, options);
+
+ return { ast, scopeManager, visitorKeys };
+}
diff --git a/eslint/babel-eslint-parser/src/parse.js b/eslint/babel-eslint-parser/src/parse.js
new file mode 100644
index 000000000000..e39f371295d6
--- /dev/null
+++ b/eslint/babel-eslint-parser/src/parse.js
@@ -0,0 +1,23 @@
+import { parseSync as babelParse } from "@babel/core";
+import babylonToEspree from "./babylon-to-espree";
+import { normalizeBabelParseConfig } from "./configuration";
+
+export default function parse(code, options) {
+ const parseOptions = normalizeBabelParseConfig(options);
+ let ast;
+
+ try {
+ ast = babelParse(code, parseOptions);
+ } catch (err) {
+ if (err instanceof SyntaxError) {
+ err.lineNumber = err.loc.line;
+ err.column = err.loc.column;
+ }
+
+ throw err;
+ }
+
+ babylonToEspree(ast, code);
+
+ return ast;
+}
diff --git a/eslint/babel-eslint-parser/src/visitor-keys.js b/eslint/babel-eslint-parser/src/visitor-keys.js
new file mode 100644
index 000000000000..6676a86602e3
--- /dev/null
+++ b/eslint/babel-eslint-parser/src/visitor-keys.js
@@ -0,0 +1,15 @@
+import { types as t } from "@babel/core";
+import { KEYS as ESLINT_VISITOR_KEYS } from "eslint-visitor-keys";
+
+const { VISITOR_KEYS: BABEL_VISITOR_KEYS } = t;
+
+export default Object.assign(
+ {
+ Literal: ESLINT_VISITOR_KEYS.Literal,
+ MethodDefinition: ["decorators"].concat(
+ ESLINT_VISITOR_KEYS.MethodDefinition,
+ ),
+ Property: ["decorators"].concat(ESLINT_VISITOR_KEYS.Property),
+ },
+ BABEL_VISITOR_KEYS,
+);
diff --git a/eslint/babel-eslint-parser/test/babel-eslint-parser.js b/eslint/babel-eslint-parser/test/babel-eslint-parser.js
new file mode 100644
index 000000000000..3e692340ce59
--- /dev/null
+++ b/eslint/babel-eslint-parser/test/babel-eslint-parser.js
@@ -0,0 +1,535 @@
+import assert from "assert";
+import espree from "espree";
+import escope from "eslint-scope";
+import unpad from "dedent";
+import { parseForESLint } from "../src";
+import assertImplementsAST from "./helpers/assert-implements-ast";
+
+const babelOptions = {
+ configFile: require.resolve(
+ "@babel/eslint-shared-fixtures/config/babel.config.js",
+ ),
+};
+
+function parseAndAssertSame(code) {
+ code = unpad(code);
+ const esAST = espree.parse(code, {
+ ecmaFeatures: {
+ // enable JSX parsing
+ jsx: true,
+ // enable return in global scope
+ globalReturn: true,
+ // enable implied strict mode (if ecmaVersion >= 5)
+ impliedStrict: true,
+ // allow experimental object rest/spread
+ experimentalObjectRestSpread: true,
+ },
+ tokens: true,
+ loc: true,
+ range: true,
+ comment: true,
+ ecmaVersion: 2020,
+ sourceType: "module",
+ });
+ const babylonAST = parseForESLint(code, {
+ eslintVisitorKeys: true,
+ eslintScopeManager: true,
+ babelOptions,
+ }).ast;
+ assertImplementsAST(esAST, babylonAST);
+}
+
+describe("babylon-to-espree", () => {
+ describe("compatibility", () => {
+ it("should allow ast.analyze to be called without options", function() {
+ const esAST = parseForESLint("`test`", {
+ eslintScopeManager: true,
+ eslintVisitorKeys: true,
+ babelOptions,
+ }).ast;
+ expect(() => {
+ escope.analyze(esAST);
+ }).not.toThrow(new TypeError("Should allow no options argument."));
+ });
+ });
+
+ describe("templates", () => {
+ it("empty template string", () => {
+ parseAndAssertSame("``");
+ });
+
+ it("template string", () => {
+ parseAndAssertSame("`test`");
+ });
+
+ it("template string using $", () => {
+ parseAndAssertSame("`$`");
+ });
+
+ it("template string with expression", () => {
+ parseAndAssertSame("`${a}`");
+ });
+
+ it("template string with multiple expressions", () => {
+ parseAndAssertSame("`${a}${b}${c}`");
+ });
+
+ it("template string with expression and strings", () => {
+ parseAndAssertSame("`a${a}a`");
+ });
+
+ it("template string with binary expression", () => {
+ parseAndAssertSame("`a${a + b}a`");
+ });
+
+ it("tagged template", () => {
+ parseAndAssertSame("jsx``");
+ });
+
+ it("tagged template with expression", () => {
+ parseAndAssertSame("jsx``");
+ });
+
+ it("tagged template with new operator", () => {
+ parseAndAssertSame("new raw`42`");
+ });
+
+ it("template with nested function/object", () => {
+ parseAndAssertSame(
+ "`outer${{x: {y: 10}}}bar${`nested${function(){return 1;}}endnest`}end`",
+ );
+ });
+
+ it("template with braces inside and outside of template string #96", () => {
+ parseAndAssertSame(
+ "if (a) { var target = `{}a:${webpackPort}{}}}}`; } else { app.use(); }",
+ );
+ });
+
+ it("template also with braces #96", () => {
+ parseAndAssertSame(`
+ export default function f1() {
+ function f2(foo) {
+ const bar = 3;
+ return \`\${foo} \${bar}\`;
+ }
+ return f2;
+ }
+ `);
+ });
+
+ it("template with destructuring #31", () => {
+ parseAndAssertSame(`
+ module.exports = {
+ render() {
+ var {name} = this.props;
+ return Math.max(null, \`Name: \${name}, Name: \${name}\`);
+ }
+ };
+ `);
+ });
+
+ it("template with arrow returning template #603", () => {
+ parseAndAssertSame(`
+ var a = \`\${() => {
+ \`\${''}\`
+ }}\`;
+ `);
+ });
+
+ it("template string with object with template string inside", () => {
+ parseAndAssertSame("`${ { a:`${2}` } }`");
+ });
+ });
+
+ it("simple expression", () => {
+ parseAndAssertSame("a = 1");
+ });
+
+ it("logical NOT", () => {
+ parseAndAssertSame("!0");
+ });
+
+ it("bitwise NOT", () => {
+ parseAndAssertSame("~0");
+ });
+
+ it("class declaration", () => {
+ parseAndAssertSame("class Foo {}");
+ });
+
+ it("class expression", () => {
+ parseAndAssertSame("var a = class Foo {}");
+ });
+
+ it("jsx expression", () => {
+ parseAndAssertSame("");
+ });
+
+ it("jsx expression with 'this' as identifier", () => {
+ parseAndAssertSame("");
+ });
+
+ it("jsx expression with a dynamic attribute", () => {
+ parseAndAssertSame("");
+ });
+
+ it("jsx expression with a member expression as identifier", () => {
+ parseAndAssertSame("");
+ });
+
+ it("jsx expression with spread", () => {
+ parseAndAssertSame("var myDivElement = ;");
+ });
+
+ it("empty jsx text", () => {
+ parseAndAssertSame("");
+ });
+
+ it("jsx text with content", () => {
+ parseAndAssertSame("Hello, world!");
+ });
+
+ it("nested jsx", () => {
+ parseAndAssertSame("
\n
Wat
\n
");
+ });
+
+ it("default import", () => {
+ parseAndAssertSame('import foo from "foo";');
+ });
+
+ it("import specifier", () => {
+ parseAndAssertSame('import { foo } from "foo";');
+ });
+
+ it("import specifier with name", () => {
+ parseAndAssertSame('import { foo as bar } from "foo";');
+ });
+
+ it("import bare", () => {
+ parseAndAssertSame('import "foo";');
+ });
+
+ it("export default class declaration", () => {
+ parseAndAssertSame("export default class Foo {}");
+ });
+
+ it("export default class expression", () => {
+ parseAndAssertSame("export default class {}");
+ });
+
+ it("export default function declaration", () => {
+ parseAndAssertSame("export default function Foo() {}");
+ });
+
+ it("export default function expression", () => {
+ parseAndAssertSame("export default function () {}");
+ });
+
+ it("export all", () => {
+ parseAndAssertSame('export * from "foo";');
+ });
+
+ it("export named", () => {
+ parseAndAssertSame("var foo = 1;export { foo };");
+ });
+
+ it("export named alias", () => {
+ parseAndAssertSame("var foo = 1;export { foo as bar };");
+ });
+
+ // Espree doesn't support the optional chaining operator yet
+ it("optional chaining operator (token)", () => {
+ const code = "foo?.bar";
+ const babylonAST = parseForESLint(code, {
+ eslintVisitorKeys: true,
+ eslintScopeManager: true,
+ babelOptions,
+ }).ast;
+ assert.strictEqual(babylonAST.tokens[1].type, "Punctuator");
+ });
+
+ // Espree doesn't support the nullish coalescing operator yet
+ it("nullish coalescing operator (token)", () => {
+ const code = "foo ?? bar";
+ const babylonAST = parseForESLint(code, {
+ eslintVisitorKeys: true,
+ eslintScopeManager: true,
+ babelOptions,
+ }).ast;
+ assert.strictEqual(babylonAST.tokens[1].type, "Punctuator");
+ });
+
+ // Espree doesn't support the pipeline operator yet
+ it("pipeline operator (token)", () => {
+ const code = "foo |> bar";
+ const babylonAST = parseForESLint(code, {
+ eslintVisitorKeys: true,
+ eslintScopeManager: true,
+ babelOptions,
+ }).ast;
+ assert.strictEqual(babylonAST.tokens[1].type, "Punctuator");
+ });
+
+ // Espree doesn't support private fields yet
+ it("hash (token)", () => {
+ const code = "class A { #x }";
+ const babylonAST = parseForESLint(code, {
+ eslintVisitorKeys: true,
+ eslintScopeManager: true,
+ babelOptions,
+ }).ast;
+ assert.strictEqual(babylonAST.tokens[3].type, "Punctuator");
+ assert.strictEqual(babylonAST.tokens[3].value, "#");
+ });
+
+ it.skip("empty program with line comment", () => {
+ parseAndAssertSame("// single comment");
+ });
+
+ it.skip("empty program with block comment", () => {
+ parseAndAssertSame(" /* multiline\n * comment\n*/");
+ });
+
+ it("line comments", () => {
+ parseAndAssertSame(`
+ // single comment
+ var foo = 15; // comment next to statement
+ // second comment after statement
+ `);
+ });
+
+ it("block comments", () => {
+ parseAndAssertSame(`
+ /* single comment */
+ var foo = 15; /* comment next to statement */
+ /*
+ * multiline
+ * comment
+ */
+ `);
+ });
+
+ it("block comments #124", () => {
+ parseAndAssertSame(`
+ React.createClass({
+ render() {
+ // return (
+ //
+ // ); // <-- this is the line that is reported
+ }
+ });
+ `);
+ });
+
+ it("null", () => {
+ parseAndAssertSame("null");
+ });
+
+ it("boolean", () => {
+ parseAndAssertSame("if (true) {} else if (false) {}");
+ });
+
+ it("regexp", () => {
+ parseAndAssertSame("/affix-top|affix-bottom|affix|[a-z]/");
+ });
+
+ it("regexp", () => {
+ parseAndAssertSame("const foo = /foo/;");
+ });
+
+ it("regexp y flag", () => {
+ parseAndAssertSame("const foo = /foo/y;");
+ });
+
+ it("regexp u flag", () => {
+ parseAndAssertSame("const foo = /foo/u;");
+ });
+
+ it("regexp in a template string", () => {
+ parseAndAssertSame('`${/\\d/.exec("1")[0]}`');
+ });
+
+ it("first line is empty", () => {
+ parseAndAssertSame('\nimport Immutable from "immutable";');
+ });
+
+ it("empty", () => {
+ parseAndAssertSame("");
+ });
+
+ it("jsdoc", () => {
+ parseAndAssertSame(`
+ /**
+ * @param {object} options
+ * @return {number}
+ */
+ const test = function({ a, b, c }) {
+ return a + b + c;
+ };
+ module.exports = test;
+ `);
+ });
+
+ it("empty block with comment", () => {
+ parseAndAssertSame(`
+ function a () {
+ try {
+ b();
+ } catch (e) {
+ // asdf
+ }
+ }
+ `);
+ });
+
+ describe("babel tests", () => {
+ it("MethodDefinition", () => {
+ parseAndAssertSame(`
+ export default class A {
+ a() {}
+ }
+ `);
+ });
+
+ it("MethodDefinition 2", () => {
+ parseAndAssertSame(
+ "export default class Bar { get bar() { return 42; }}",
+ );
+ });
+
+ it("ClassMethod", () => {
+ parseAndAssertSame(`
+ class A {
+ constructor() {
+ }
+ }
+ `);
+ });
+
+ it("ClassMethod multiple params", () => {
+ parseAndAssertSame(`
+ class A {
+ constructor(a, b, c) {
+ }
+ }
+ `);
+ });
+
+ it("ClassMethod multiline", () => {
+ parseAndAssertSame(`
+ class A {
+ constructor (
+ a,
+ b,
+ c
+ )
+
+ {
+
+ }
+ }
+ `);
+ });
+
+ it("ClassMethod oneline", () => {
+ parseAndAssertSame("class A { constructor(a, b, c) {} }");
+ });
+
+ it("ObjectMethod", () => {
+ parseAndAssertSame(`
+ var a = {
+ b(c) {
+ }
+ }
+ `);
+ });
+
+ it("do not allow import export everywhere", () => {
+ assert.throws(() => {
+ parseAndAssertSame('function F() { import a from "a"; }');
+ }, /SyntaxError: 'import' and 'export' may only appear at the top level/);
+ });
+
+ it("return outside function", () => {
+ parseAndAssertSame("return;");
+ });
+
+ it("super outside method", () => {
+ assert.throws(() => {
+ parseAndAssertSame("function F() { super(); }");
+ }, /SyntaxError: 'super' keyword outside a method/);
+ });
+
+ it("StringLiteral", () => {
+ parseAndAssertSame("");
+ parseAndAssertSame("");
+ parseAndAssertSame("a");
+ });
+
+ it("getters and setters", () => {
+ parseAndAssertSame("class A { get x ( ) { ; } }");
+ parseAndAssertSame(`
+ class A {
+ get x(
+ )
+ {
+ ;
+ }
+ }
+ `);
+ parseAndAssertSame("class A { set x (a) { ; } }");
+ parseAndAssertSame(`
+ class A {
+ set x(a
+ )
+ {
+ ;
+ }
+ }
+ `);
+ parseAndAssertSame(`
+ var B = {
+ get x () {
+ return this.ecks;
+ },
+ set x (ecks) {
+ this.ecks = ecks;
+ }
+ };
+ `);
+ });
+
+ it("RestOperator", () => {
+ parseAndAssertSame("var { a, ...b } = c");
+ parseAndAssertSame("var [ a, ...b ] = c");
+ parseAndAssertSame("var a = function (...b) {}");
+ });
+
+ it("SpreadOperator", () => {
+ parseAndAssertSame("var a = { b, ...c }");
+ parseAndAssertSame("var a = [ a, ...b ]");
+ parseAndAssertSame("var a = sum(...b)");
+ });
+
+ it("Async/Await", () => {
+ parseAndAssertSame(`
+ async function a() {
+ await 1;
+ }
+ `);
+ });
+
+ it("BigInt", () => {
+ parseAndAssertSame(`
+ const a = 1n;
+ `);
+ });
+
+ it("Dynamic Import", () => {
+ parseAndAssertSame(`
+ const a = import('a');
+ `);
+ });
+ });
+});
diff --git a/eslint/babel-eslint-parser/test/fixtures/eslint-plugin-import/.eslintrc.yml b/eslint/babel-eslint-parser/test/fixtures/eslint-plugin-import/.eslintrc.yml
new file mode 100644
index 000000000000..418b3d0c0ceb
--- /dev/null
+++ b/eslint/babel-eslint-parser/test/fixtures/eslint-plugin-import/.eslintrc.yml
@@ -0,0 +1,11 @@
+root: true
+
+# babel-eslint
+parser: ../../../lib/index.js
+
+# use eslint-plugin-import
+plugins:
+ - import
+rules:
+ import/no-named-as-default: error
+ no-unused-vars: error
diff --git a/eslint/babel-eslint-parser/test/fixtures/eslint-plugin-import/a.js b/eslint/babel-eslint-parser/test/fixtures/eslint-plugin-import/a.js
new file mode 100644
index 000000000000..e8d96fc4d62d
--- /dev/null
+++ b/eslint/babel-eslint-parser/test/fixtures/eslint-plugin-import/a.js
@@ -0,0 +1 @@
+export default function foo() { }
diff --git a/eslint/babel-eslint-parser/test/fixtures/eslint-plugin-import/b.js b/eslint/babel-eslint-parser/test/fixtures/eslint-plugin-import/b.js
new file mode 100644
index 000000000000..b3a52f870198
--- /dev/null
+++ b/eslint/babel-eslint-parser/test/fixtures/eslint-plugin-import/b.js
@@ -0,0 +1 @@
+import foo from './a.js';
diff --git a/eslint/babel-eslint-parser/test/fixtures/eslint-plugin-import/c.js b/eslint/babel-eslint-parser/test/fixtures/eslint-plugin-import/c.js
new file mode 100644
index 000000000000..2beac98f3b74
--- /dev/null
+++ b/eslint/babel-eslint-parser/test/fixtures/eslint-plugin-import/c.js
@@ -0,0 +1,4 @@
+// @flow
+type Foo = {};
+
+const FlowTypeButton = ({ }: Foo) => { };
diff --git a/eslint/babel-eslint-parser/test/fixtures/rules/strict/function-with.js b/eslint/babel-eslint-parser/test/fixtures/rules/strict/function-with.js
new file mode 100644
index 000000000000..3df7515269d5
--- /dev/null
+++ b/eslint/babel-eslint-parser/test/fixtures/rules/strict/function-with.js
@@ -0,0 +1,3 @@
+function x () {
+ "use strict";
+}
diff --git a/eslint/babel-eslint-parser/test/fixtures/rules/strict/function-without.js b/eslint/babel-eslint-parser/test/fixtures/rules/strict/function-without.js
new file mode 100644
index 000000000000..20b3e9e55005
--- /dev/null
+++ b/eslint/babel-eslint-parser/test/fixtures/rules/strict/function-without.js
@@ -0,0 +1 @@
+function x () {}
diff --git a/eslint/babel-eslint-parser/test/fixtures/rules/strict/global-with-function-with.js b/eslint/babel-eslint-parser/test/fixtures/rules/strict/global-with-function-with.js
new file mode 100644
index 000000000000..ef682f72c947
--- /dev/null
+++ b/eslint/babel-eslint-parser/test/fixtures/rules/strict/global-with-function-with.js
@@ -0,0 +1,5 @@
+"use strict";
+
+function x () {
+ "use strict";
+}
diff --git a/eslint/babel-eslint-parser/test/fixtures/rules/strict/global-with-function-without.js b/eslint/babel-eslint-parser/test/fixtures/rules/strict/global-with-function-without.js
new file mode 100644
index 000000000000..206f708141b7
--- /dev/null
+++ b/eslint/babel-eslint-parser/test/fixtures/rules/strict/global-with-function-without.js
@@ -0,0 +1,3 @@
+"use strict";
+
+function x () {}
diff --git a/eslint/babel-eslint-parser/test/fixtures/rules/strict/global-with.js b/eslint/babel-eslint-parser/test/fixtures/rules/strict/global-with.js
new file mode 100644
index 000000000000..e967aa4994d0
--- /dev/null
+++ b/eslint/babel-eslint-parser/test/fixtures/rules/strict/global-with.js
@@ -0,0 +1,6 @@
+"use strict";
+/*
+The empty statement is intentional. As of now, ESLint won't enforce
+string: [2, "global"] on a program with an empty body. A test for that without
+massaging the AST to ESlint's input format should fail.
+*/
diff --git a/eslint/babel-eslint-parser/test/fixtures/rules/strict/none.js b/eslint/babel-eslint-parser/test/fixtures/rules/strict/none.js
new file mode 100644
index 000000000000..4a6db98f6226
--- /dev/null
+++ b/eslint/babel-eslint-parser/test/fixtures/rules/strict/none.js
@@ -0,0 +1 @@
+"no use strict anywhere";
diff --git a/eslint/babel-eslint-parser/test/fixtures/rules/syntax-error.js b/eslint/babel-eslint-parser/test/fixtures/rules/syntax-error.js
new file mode 100644
index 000000000000..6fa194a18f9e
--- /dev/null
+++ b/eslint/babel-eslint-parser/test/fixtures/rules/syntax-error.js
@@ -0,0 +1,6 @@
+class ClassName {
+ constructor() {
+
+ },
+ aMethod() {}
+}
diff --git a/eslint/babel-eslint-parser/test/helpers/assert-implements-ast.js b/eslint/babel-eslint-parser/test/helpers/assert-implements-ast.js
new file mode 100644
index 000000000000..3c435940a4cf
--- /dev/null
+++ b/eslint/babel-eslint-parser/test/helpers/assert-implements-ast.js
@@ -0,0 +1,40 @@
+// Checks if the source ast implements the target ast. Ignores extra keys on source ast
+export default function assertImplementsAST(target, source, path) {
+ if (!path) {
+ path = [];
+ }
+
+ function error(text) {
+ const err = new Error(`At ${path.join(".")}: ${text}:`);
+ err.depth = path.length + 1;
+ throw err;
+ }
+
+ const typeA = target === null ? "null" : typeof target;
+ const typeB = source === null ? "null" : typeof source;
+ if (typeA !== typeB) {
+ error(
+ `have different types (${typeA} !== ${typeB}) (${target} !== ${source})`,
+ );
+ } else if (
+ typeA === "object" &&
+ ["RegExp"].indexOf(target.constructor.name) !== -1 &&
+ target.constructor.name !== source.constructor.name
+ ) {
+ error(
+ `object have different constructors (${target.constructor.name} !== ${source.constructor.name}`,
+ );
+ } else if (typeA === "object") {
+ const keysTarget = Object.keys(target);
+ for (const i in keysTarget) {
+ const key = keysTarget[i];
+ path.push(key);
+ assertImplementsAST(target[key], source[key], path);
+ path.pop();
+ }
+ } else if (target !== source) {
+ error(
+ `are different (${JSON.stringify(target)} !== ${JSON.stringify(source)})`,
+ );
+ }
+}
diff --git a/eslint/babel-eslint-parser/test/integration.js b/eslint/babel-eslint-parser/test/integration.js
new file mode 100644
index 000000000000..d2df877c562e
--- /dev/null
+++ b/eslint/babel-eslint-parser/test/integration.js
@@ -0,0 +1,237 @@
+import eslint from "eslint";
+import fs from "fs";
+import path from "path";
+import * as parser from "../src";
+
+eslint.linter.defineParser("current-babel-eslint", parser);
+
+const paths = {
+ fixtures: path.join(__dirname, "fixtures", "rules"),
+};
+
+const encoding = "utf8";
+const errorLevel = 2;
+
+const baseEslintOpts = {
+ parser: "current-babel-eslint",
+ parserOptions: {
+ sourceType: "script",
+ requireConfigFile: false,
+ babelOptions: { configFile: false }
+ },
+};
+
+/**
+ * Load a fixture and run eslint.linter.verify() on it.
+ * Pass the return value to done().
+ * @param object opts
+ * @param function done
+ */
+function lint(opts, done) {
+ readFixture(opts.fixture, (err, src) => {
+ if (err) return done(err);
+ done(null, eslint.linter.verify(src, opts.eslint));
+ });
+}
+
+/**
+ * Read a fixture file, passing the content to done().
+ * @param string|array id
+ * @param function done
+ */
+function readFixture(id, done) {
+ if (Array.isArray(id)) id = path.join.apply(path, id);
+ if (!path.extname(id)) id += ".js";
+ fs.readFile(path.join(paths.fixtures, id), encoding, done);
+}
+// readFixture
+
+describe("Rules:", () => {
+ describe("`strict`", strictSuite);
+});
+// describe
+
+function strictSuite() {
+ const ruleId = "strict";
+
+ describe("when set to 'never'", () => {
+ const eslintOpts = Object.assign({}, baseEslintOpts, {
+ rules: {},
+ });
+ eslintOpts.rules[ruleId] = [errorLevel, "never"];
+
+ ["global-with", "function-with"].forEach(fixture => {
+ it(`should error on ${fixture.match(/^[^-]+/)[0]} directive`, done => {
+ lint(
+ {
+ fixture: ["strict", fixture],
+ eslint: eslintOpts,
+ },
+ (err, report) => {
+ if (err) return done(err);
+ expect(report[0].ruleId).toBe(ruleId);
+ done();
+ },
+ );
+ });
+ // it
+ });
+ });
+ // describe
+
+ describe("when set to 'global'", () => {
+ const eslintOpts = Object.assign({}, baseEslintOpts, {
+ rules: {},
+ });
+ eslintOpts.rules[ruleId] = [errorLevel, "global"];
+
+ it("shouldn't error on single global directive", done => {
+ lint(
+ {
+ fixture: ["strict", "global-with"],
+ eslint: eslintOpts,
+ },
+ (err, report) => {
+ if (err) return done(err);
+ expect(report.length).toBe(0);
+ done();
+ },
+ );
+ });
+ // it
+
+ it("should error twice on global directive: no and function directive: yes", done => {
+ lint(
+ {
+ fixture: ["strict", "function-with"],
+ eslint: eslintOpts,
+ },
+ (err, report) => {
+ if (err) return done(err);
+ [0, 1].forEach(i => {
+ expect(report[0].ruleId).toBe(ruleId);
+ });
+ done();
+ },
+ );
+ });
+ // it
+
+ it("should error on function directive", done => {
+ lint(
+ {
+ fixture: ["strict", "global-with-function-with"],
+ eslint: eslintOpts,
+ },
+ (err, report) => {
+ if (err) return done(err);
+ expect(report[0].ruleId).toBe(ruleId);
+ // This is to make sure the test fails prior to adapting Babel AST
+ // directive representation to ESLint format. Otherwise it reports an
+ // error for missing global directive that masquerades as the expected
+ // result of the previous assertion.
+ expect(report[0].nodeType).not.toBe("Program");
+ done();
+ },
+ );
+ });
+ // it
+
+ it("should error on no directive", done => {
+ lint(
+ {
+ fixture: ["strict", "none"],
+ eslint: eslintOpts,
+ },
+ (err, report) => {
+ if (err) return done(err);
+ expect(report[0].ruleId).toBe(ruleId);
+ done();
+ },
+ );
+ });
+ // it
+ });
+ // describe
+
+ describe("when set to 'function'", () => {
+ const eslintOpts = Object.assign({}, baseEslintOpts, {
+ rules: {},
+ });
+ eslintOpts.rules[ruleId] = [errorLevel, "function"];
+
+ it("shouldn't error on single function directive", done => {
+ lint(
+ {
+ fixture: ["strict", "function-with"],
+ eslint: eslintOpts,
+ },
+ (err, report) => {
+ if (err) return done(err);
+ expect(report.length).toBe(0);
+ done();
+ },
+ );
+ });
+ // it
+
+ it("should error twice on function directive: no and global directive: yes", done => {
+ lint(
+ {
+ fixture: ["strict", "global-with-function-without"],
+ eslint: eslintOpts,
+ },
+ (err, report) => {
+ if (err) return done(err);
+ [0, 1].forEach(i => {
+ expect(report[i].ruleId).toBe(ruleId);
+ });
+ done();
+ },
+ );
+ });
+ // it
+
+ it("should error on only global directive", done => {
+ lint(
+ {
+ fixture: ["strict", "global-with"],
+ eslint: eslintOpts,
+ },
+ (err, report) => {
+ if (err) return done(err);
+ expect(report[0].ruleId).toBe(ruleId);
+ done();
+ },
+ );
+ });
+ // it
+
+ it("should error on extraneous global directive", done => {
+ lint(
+ {
+ fixture: ["strict", "global-with-function-with"],
+ eslint: eslintOpts,
+ },
+ (err, report) => {
+ if (err) return done(err);
+ expect(report[0].ruleId).toBe(ruleId);
+ expect(report[0].nodeType.indexOf("Function")).toBe(-1);
+ done();
+ },
+ );
+ });
+ // it
+ });
+}
+
+describe("https://github.com/babel/babel-eslint/issues/558", () => {
+ it("doesn't crash with eslint-plugin-import", () => {
+ const engine = new eslint.CLIEngine({ ignore: false });
+ const files = ["a.js", "b.js", "c.js"];
+ let fileWithPath = files.map(file =>
+ path.resolve(__dirname, `./fixtures/eslint-plugin-import/${file}`),
+ );
+ engine.executeOnFiles(fileWithPath);
+ });
+});
diff --git a/eslint/babel-eslint-parser/test/non-regression.js b/eslint/babel-eslint-parser/test/non-regression.js
new file mode 100644
index 000000000000..13524702e335
--- /dev/null
+++ b/eslint/babel-eslint-parser/test/non-regression.js
@@ -0,0 +1,1955 @@
+import eslint from "eslint";
+import path from "path";
+import unpad from "dedent";
+import * as parser from "../src";
+
+function verifyAndAssertMessagesWithSpecificESLint(
+ code,
+ rules,
+ expectedMessages,
+ sourceType,
+ overrideConfig,
+ linter,
+) {
+ const config = {
+ parser: "current-babel-eslint",
+ rules,
+ env: {
+ node: true,
+ es6: true,
+ },
+ ...overrideConfig,
+ parserOptions: {
+ sourceType,
+ requireConfigFile: false,
+ babelOptions: {
+ configFile: require.resolve(
+ "@babel/eslint-shared-fixtures/config/babel.config.js",
+ ),
+ },
+ ...overrideConfig?.parserOptions,
+ },
+ };
+
+ const messages = linter.verify(code, config);
+
+ if (messages.length !== expectedMessages.length) {
+ throw new Error(
+ `Expected ${expectedMessages.length} message(s), got ${
+ messages.length
+ }\n${JSON.stringify(messages, null, 2)}`,
+ );
+ }
+
+ messages.forEach((message, i) => {
+ const formatedMessage = `${message.line}:${message.column} ${
+ message.message
+ }${message.ruleId ? ` ${message.ruleId}` : ""}`;
+ if (formatedMessage !== expectedMessages[i]) {
+ throw new Error(
+ `
+ Message ${i} does not match:
+ Expected: ${expectedMessages[i]}
+ Actual: ${formatedMessage}
+ `,
+ );
+ }
+ });
+}
+
+function verifyAndAssertMessages(
+ code,
+ rules,
+ expectedMessages,
+ sourceType,
+ overrideConfig,
+) {
+ const linter = new eslint.Linter();
+ linter.defineParser("current-babel-eslint", parser);
+
+ verifyAndAssertMessagesWithSpecificESLint(
+ unpad(`${code}`),
+ rules || {},
+ expectedMessages || [],
+ sourceType,
+ overrideConfig,
+ linter,
+ );
+}
+
+describe("verify", () => {
+ it("arrow function support (issue #1)", () => {
+ verifyAndAssertMessages("describe('stuff', () => {});");
+ });
+
+ it("EOL validation (issue #2)", () => {
+ verifyAndAssertMessages(
+ 'module.exports = "something";',
+ { "eol-last": 1, semi: 1 },
+ ["1:30 Newline required at end of file but not found. eol-last"],
+ );
+ });
+
+ xit("Readable error messages (issue #3)", () => {
+ verifyAndAssertMessages("{ , res }", {}, [
+ "1:3 Parsing error: Unexpected token",
+ ]);
+ });
+
+ it("Modules support (issue #5)", () => {
+ verifyAndAssertMessages(
+ `
+ import Foo from 'foo';
+ export default Foo;
+ export const c = 'c';
+ export class Store {}
+ `,
+ );
+ });
+
+ it("Rest parameters (issue #7)", () => {
+ verifyAndAssertMessages("function foo(...args) { return args; }", {
+ "no-undef": 1,
+ });
+ });
+
+ it("Exported classes should be used (issue #8)", () => {
+ verifyAndAssertMessages("class Foo {} module.exports = Foo;", {
+ "no-unused-vars": 1,
+ });
+ });
+
+ it("super keyword in class (issue #10)", () => {
+ verifyAndAssertMessages("class Foo { constructor() { super() } }", {
+ "no-undef": 1,
+ });
+ });
+
+ it("Rest parameter in destructuring assignment (issue #11)", () => {
+ verifyAndAssertMessages(
+ "const [a, ...rest] = ['1', '2', '3']; module.exports = rest;",
+ { "no-undef": 1 },
+ );
+ });
+
+ it("JSX attribute names marked as variables (issue #12)", () => {
+ verifyAndAssertMessages('module.exports = ', {
+ "no-undef": 1,
+ });
+ });
+
+ it("Multiple destructured assignment with compound properties (issue #16)", () => {
+ verifyAndAssertMessages("module.exports = { ...a.a, ...a.b };", {
+ "no-dupe-keys": 1,
+ });
+ });
+
+ it("Arrow function with non-block bodies (issue #20)", () => {
+ verifyAndAssertMessages(
+ '"use strict"; () => 1',
+ { strict: [1, "global"] },
+ [],
+ "script",
+ );
+ });
+
+ it("#242", () => {
+ verifyAndAssertMessages('"use strict"; asdf;', {
+ "no-irregular-whitespace": 1,
+ });
+ });
+
+ it("await keyword (issue #22)", () => {
+ verifyAndAssertMessages("async function foo() { await bar(); }", {
+ "no-unused-expressions": 1,
+ });
+ });
+
+ it("arrow functions (issue #27)", () => {
+ verifyAndAssertMessages("[1, 2, 3].map(i => i * 2);", {
+ "func-names": 1,
+ "space-before-blocks": 1,
+ });
+ });
+
+ it("comment with padded-blocks (issue #33)", () => {
+ verifyAndAssertMessages(
+ `
+ if (a) {
+ // i'm a comment!
+ let b = c
+ }
+ `,
+ { "padded-blocks": [1, "never"] },
+ );
+ });
+
+ describe("flow", () => {
+ it("check regular function", () => {
+ verifyAndAssertMessages(
+ "function a(b, c) { b += 1; c += 1; return b + c; } a;",
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("type alias", () => {
+ verifyAndAssertMessages("type SomeNewType = any;", { "no-undef": 1 });
+ });
+
+ it("type cast expression #102", () => {
+ verifyAndAssertMessages("for (let a of (a: Array)) {}");
+ });
+
+ it("multiple nullable type annotations and return #108", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ import type Foo2 from 'foo';
+ import type Foo3 from 'foo';
+ function log(foo: ?Foo, foo2: ?Foo2): ?Foo3 {
+ console.log(foo, foo2);
+ }
+ log(1, 2);
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("interface declaration", () => {
+ verifyAndAssertMessages(
+ `
+ interface Foo {};
+ interface Bar {
+ foo: Foo,
+ };
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ ["2:11 'Bar' is defined but never used. no-unused-vars"],
+ );
+ });
+
+ it("type parameter bounds (classes)", () => {
+ verifyAndAssertMessages(
+ `
+ import type {Foo, Foo2} from 'foo';
+ import Base from 'base';
+ class Log extends Base {
+ messages: {[T1]: T2};
+ }
+ new Log();
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ ["3:34 'T4' is defined but never used. no-unused-vars"],
+ );
+ });
+
+ it("type parameter scope (classes)", () => {
+ verifyAndAssertMessages(
+ `
+ T;
+ class Foo {}
+ T;
+ new Foo();
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ [
+ "1:1 'T' is not defined. no-undef",
+ "2:11 'T' is defined but never used. no-unused-vars",
+ "3:1 'T' is not defined. no-undef",
+ ],
+ );
+ });
+
+ it("type parameter bounds (interfaces)", () => {
+ verifyAndAssertMessages(
+ `
+ import type {Foo, Foo2, Bar} from '';
+ interface Log extends Bar {
+ messages: {[T1]: T2};
+ }
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ [
+ "2:11 'Log' is defined but never used. no-unused-vars",
+ "2:38 'T4' is defined but never used. no-unused-vars",
+ ],
+ );
+ });
+
+ it("type parameter scope (interfaces)", () => {
+ verifyAndAssertMessages(
+ `
+ T;
+ interface Foo {};
+ T;
+ Foo;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ [
+ "1:1 'T' is not defined. no-undef",
+ "2:15 'T' is defined but never used. no-unused-vars",
+ "3:1 'T' is not defined. no-undef",
+ ],
+ );
+ });
+
+ it("type parameter bounds (type aliases)", () => {
+ verifyAndAssertMessages(
+ `
+ import type {Foo, Foo2, Foo3} from 'foo';
+ type Log = {
+ messages: {[T1]: T2};
+ delay: Foo3;
+ };
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ [
+ "2:6 'Log' is defined but never used. no-unused-vars",
+ "2:29 'T3' is defined but never used. no-unused-vars",
+ ],
+ );
+ });
+
+ it("type parameter scope (type aliases)", () => {
+ verifyAndAssertMessages(
+ `
+ T;
+ type Foo = {};
+ T;
+ Foo;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ [
+ "1:1 'T' is not defined. no-undef",
+ "2:10 'T' is defined but never used. no-unused-vars",
+ "3:1 'T' is not defined. no-undef",
+ ],
+ );
+ });
+
+ it("type parameter bounds (functions)", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ import type Foo2 from 'foo';
+ function log(a: T1, b: T2): T3 { return a + b; }
+ log(1, 2);
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ ["3:37 'T4' is defined but never used. no-unused-vars"],
+ );
+ });
+
+ it("type parameter scope (functions)", () => {
+ verifyAndAssertMessages(
+ `
+ T;
+ function log() {}
+ T;
+ log;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ [
+ "1:1 'T' is not defined. no-undef",
+ "2:14 'T' is defined but never used. no-unused-vars",
+ "3:1 'T' is not defined. no-undef",
+ ],
+ );
+ });
+
+ it("nested type annotations", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ function foo(callback: () => Foo) {
+ return callback();
+ }
+ foo();
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("type in var declaration", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ var x: Foo = 1;
+ x;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("object type annotation", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ var a: {numVal: Foo};
+ a;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("object property types", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ import type Foo2 from 'foo';
+ var a = {
+ circle: (null : ?{ setNativeProps(props: Foo): Foo2 })
+ };
+ a;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("namespaced types", () => {
+ verifyAndAssertMessages(
+ `
+ var React = require('react-native');
+ var b = {
+ openExternalExample: (null: ?React.Component)
+ };
+ var c = {
+ render(): React.Component {}
+ };
+ b;
+ c;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("ArrayTypeAnnotation", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ var x: Foo[]; x;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("ClassImplements", () => {
+ verifyAndAssertMessages(
+ `
+ import type Bar from 'foo';
+ export default class Foo implements Bar {}
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("type alias creates declaration + usage", () => {
+ verifyAndAssertMessages(
+ `
+ type Foo = any;
+ var x : Foo = 1; x;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("type alias with type parameters", () => {
+ verifyAndAssertMessages(
+ `
+ import type Bar from 'foo';
+ import type Foo3 from 'foo';
+ type Foo = Bar
+ var x : Foo = 1; x;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("export type alias", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo2 from 'foo';
+ export type Foo = Foo2;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("polymorphic types #109", () => {
+ verifyAndAssertMessages(
+ "export default function groupByEveryN(array: Array, n: number): Array> { n; }",
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("types definition from import", () => {
+ verifyAndAssertMessages(
+ `
+ import type Promise from 'bluebird';
+ type Operation = () => Promise;
+ x: Operation;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("polymorphic/generic types for class #123", () => {
+ verifyAndAssertMessages(
+ `
+ class Box {
+ value: T;
+ }
+ var box = new Box();
+ console.log(box.value);
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("polymorphic/generic types for function #123", () => {
+ verifyAndAssertMessages(
+ `
+ export function identity(value) {
+ var a: T = value; a;
+ }
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("polymorphic/generic types for type alias #123", () => {
+ verifyAndAssertMessages(
+ `
+ import Bar from './Bar';
+ type Foo = Bar; var x: Foo = 1; console.log(x);
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("polymorphic/generic types - outside of fn scope #123", () => {
+ verifyAndAssertMessages(
+ `
+ export function foo(value) { value; };
+ var b: T = 1; b;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ [
+ "1:21 'T' is defined but never used. no-unused-vars",
+ "2:8 'T' is not defined. no-undef",
+ ],
+ );
+ });
+
+ it("polymorphic/generic types - extending unknown #123", () => {
+ verifyAndAssertMessages(
+ `
+ import Bar from 'bar';
+ export class Foo extends Bar {}
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ ["2:30 'T' is not defined. no-undef"],
+ );
+ });
+
+ it("polymorphic/generic types - function calls", () => {
+ verifyAndAssertMessages(
+ `
+ function f(): T {}
+ f();
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ ["2:3 'T' is not defined. no-undef"],
+ );
+ });
+
+ it("polymorphic/generic types - function calls #644", () => {
+ verifyAndAssertMessages(
+ `
+ import type {Type} from 'Type';
+ function f(): T {}
+ f();
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("support declarations #132", () => {
+ verifyAndAssertMessages(
+ `
+ declare class A { static () : number }
+ declare module B { declare var x: number; }
+ declare function foo(): void;
+ declare var bar
+ A; B; foo(); bar;
+ `,
+ { "no-undef": 1, "no-unused-vars": 1 },
+ );
+ });
+
+ it("supports type spreading", () => {
+ verifyAndAssertMessages(
+ `
+ type U = {};
+ type T = {a: number, ...U, ...V};
+ `,
+ { "no-undef": 1, "no-unused-vars": 1 },
+ [
+ "2:6 'T' is defined but never used. no-unused-vars",
+ "2:31 'V' is not defined. no-undef",
+ ],
+ );
+ });
+
+ it("1", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ import type Foo2 from 'foo';
+ export default function(a: Foo, b: ?Foo2, c){ a; b; c; }
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("2", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ export default function(a: () => Foo){ a; }
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("3", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ import type Foo2 from 'foo';
+ export default function(a: (_:Foo) => Foo2){ a; }
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("4", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ import type Foo2 from 'foo';
+ import type Foo3 from 'foo';
+ export default function(a: (_1:Foo, _2:Foo2) => Foo3){ a; }
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("5", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ import type Foo2 from 'foo';
+ export default function(a: (_1:Foo, ...foo:Array) => number){ a; }
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("6", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ export default function(): Foo {}
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("7", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ export default function():() => Foo {}
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("8", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ import type Foo2 from 'foo';
+ export default function():(_?:Foo) => Foo2{}
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("9", () => {
+ verifyAndAssertMessages(
+ "export default function (a: T1, b: T2) { b; }",
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("10", () => {
+ verifyAndAssertMessages(
+ "var a=function(a: T1, b: T2) {return a + b;}; a;",
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("11", () => {
+ verifyAndAssertMessages("var a={*id(x: T): T { x; }}; a;", {
+ "no-unused-vars": 1,
+ "no-undef": 1,
+ });
+ });
+
+ it("12", () => {
+ verifyAndAssertMessages("var a={async id(x: T): T { x; }}; a;", {
+ "no-unused-vars": 1,
+ "no-undef": 1,
+ });
+ });
+
+ it("13", () => {
+ verifyAndAssertMessages("var a={123(x: T): T { x; }}; a;", {
+ "no-unused-vars": 1,
+ "no-undef": 1,
+ });
+ });
+
+ it("14", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ import type Foo2 from 'foo';
+ export default class Bar {set fooProp(value:Foo):Foo2{ value; }}
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("15", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo2 from 'foo';
+ export default class Foo {get fooProp(): Foo2{}}
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("16", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ var numVal:Foo; numVal;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("17", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ var a: {numVal: Foo;}; a;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("18", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ import type Foo2 from 'foo';
+ import type Foo3 from 'foo';
+ var a: ?{numVal: Foo; [indexer: Foo2]: Foo3}; a;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("19", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ import type Foo2 from 'foo';
+ var a: {numVal: Foo; subObj?: ?{strVal: Foo2}}; a;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("20", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ import type Foo2 from 'foo';
+ import type Foo3 from 'foo';
+ import type Foo4 from 'foo';
+ var a: { [a: Foo]: Foo2; [b: Foo3]: Foo4; }; a;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("21", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ import type Foo2 from 'foo';
+ import type Foo3 from 'foo';
+ var a: {add(x:Foo, ...y:Array): Foo3}; a;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("22", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ import type Foo2 from 'foo';
+ import type Foo3 from 'foo';
+ var a: { id(x: Foo2): Foo3; }; a;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("23", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ var a:Array = [1, 2, 3]; a;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("24", () => {
+ verifyAndAssertMessages(
+ `
+ import type Baz from 'baz';
+ export default class Bar extends Baz { };
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("25", () => {
+ verifyAndAssertMessages(
+ "export default class Bar { bar(): T { return 42; }}",
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("26", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ import type Foo2 from 'foo';
+ export default class Bar { static prop1:Foo; prop2:Foo2; }
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("27", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ import type Foo2 from 'foo';
+ var x : Foo | Foo2 = 4; x;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("28", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ import type Foo2 from 'foo';
+ var x : () => Foo | () => Foo2; x;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("29", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ import type Foo2 from 'foo';
+ var x: typeof Foo | number = Foo2; x;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("30", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ var {x}: {x: Foo; } = { x: 'hello' }; x;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("31", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ var [x]: Array = [ 'hello' ]; x;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("32", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ export default function({x}: { x: Foo; }) { x; }
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("33", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ function foo([x]: Array) { x; } foo();
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("34", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ import type Foo2 from 'foo';
+ var a: Map >; a;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("35", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ var a: ?Promise[]; a;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("36", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ import type Foo2 from 'foo';
+ var a:(...rest:Array) => Foo2; a;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("37", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ import type Foo2 from 'foo';
+ import type Foo3 from 'foo';
+ import type Foo4 from 'foo';
+ var a: (x: Foo2, ...y:Foo3[]) => Foo4; a;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("38", () => {
+ verifyAndAssertMessages(
+ `
+ import type {foo, bar} from 'baz';
+ foo; bar;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("39", () => {
+ verifyAndAssertMessages(
+ `
+ import type {foo as bar} from 'baz';
+ bar;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("40", () => {
+ verifyAndAssertMessages(
+ `
+ import type from 'foo';
+ type;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("41", () => {
+ verifyAndAssertMessages(
+ `
+ import type, {foo} from 'bar';
+ type; foo;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("43", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ var a: Foo[]; a;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("44", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ var a: ?Foo[]; a;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("45", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ var a: (?Foo)[]; a;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("46", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ var a: () => Foo[]; a;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("47", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ var a: (() => Foo)[]; a;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("48", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ var a: typeof Foo[]; a;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("49", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'foo';
+ import type Foo2 from 'foo';
+ import type Foo3 from 'foo';
+ var a : [Foo, Foo2,] = [123, 'duck',]; a;
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+ });
+
+ it("class usage", () => {
+ verifyAndAssertMessages("class Lol {} module.exports = Lol;", {
+ "no-unused-vars": 1,
+ });
+ });
+
+ it("class definition: gaearon/redux#24", () => {
+ verifyAndAssertMessages(
+ `
+ export default function root(stores) {
+ return DecoratedComponent => class ReduxRootDecorator {
+ a() { DecoratedComponent; stores; }
+ };
+ }
+ `,
+ { "no-undef": 1, "no-unused-vars": 1 },
+ );
+ });
+
+ it("class properties #71", () => {
+ verifyAndAssertMessages("class Lol { foo = 'bar'; }", { "no-undef": 1 });
+ });
+
+ it("template strings #31", () => {
+ verifyAndAssertMessages("console.log(`${a}, b`);", { "comma-spacing": 1 });
+ });
+
+ it("template with destructuring #31", () => {
+ verifyAndAssertMessages(
+ `
+ module.exports = {
+ render() {
+ var {name} = this.props;
+ return Math.max(null, \`Name: \${name}, Name: \${name}\`);
+ }
+ };
+ `,
+ { "comma-spacing": 1 },
+ );
+ });
+
+ it("template with arrow returning template #603", () => {
+ verifyAndAssertMessages(
+ `
+ var a = \`\${() => {
+ \`\${''}\`
+ }}\`;
+ `,
+ { indent: 1 },
+ [],
+ );
+ });
+
+ describe("decorators #72 (legacy)", () => {
+ function verifyDecoratorsLegacyAndAssertMessages(
+ code,
+ rules,
+ expectedMessages,
+ sourceType,
+ ) {
+ const overrideConfig = {
+ parserOptions: {
+ sourceType,
+ babelOptions: {
+ configFile: require.resolve(
+ "@babel/eslint-shared-fixtures/config/babel.config.decorators-legacy.js",
+ ),
+ },
+ },
+ };
+ return verifyAndAssertMessages(
+ code,
+ rules,
+ expectedMessages,
+ sourceType,
+ overrideConfig,
+ );
+ }
+
+ it("class declaration", () => {
+ verifyDecoratorsLegacyAndAssertMessages(
+ `
+ import classDeclaration from 'decorator';
+ import decoratorParameter from 'decorator';
+ @classDeclaration((parameter) => parameter)
+ @classDeclaration(decoratorParameter)
+ @classDeclaration
+ export class TextareaAutosize {}
+ `,
+ { "no-unused-vars": 1 },
+ );
+ });
+
+ it("method definition", () => {
+ verifyDecoratorsLegacyAndAssertMessages(
+ `
+ import classMethodDeclarationA from 'decorator';
+ import decoratorParameter from 'decorator';
+ export class TextareaAutosize {
+ @classMethodDeclarationA((parameter) => parameter)
+ @classMethodDeclarationA(decoratorParameter)
+ @classMethodDeclarationA
+ methodDeclaration(e) {
+ e();
+ }
+ }
+ `,
+ { "no-unused-vars": 1 },
+ );
+ });
+
+ it("method definition get/set", () => {
+ verifyDecoratorsLegacyAndAssertMessages(
+ `
+ import classMethodDeclarationA from 'decorator';
+ import decoratorParameter from 'decorator';
+ export class TextareaAutosize {
+ @classMethodDeclarationA((parameter) => parameter)
+ @classMethodDeclarationA(decoratorParameter)
+ @classMethodDeclarationA
+ get bar() { }
+ @classMethodDeclarationA((parameter) => parameter)
+ @classMethodDeclarationA(decoratorParameter)
+ @classMethodDeclarationA
+ set bar(val) { val; }
+ }
+ `,
+ { "no-unused-vars": 1 },
+ );
+ });
+
+ it("object property", () => {
+ verifyDecoratorsLegacyAndAssertMessages(
+ `
+ import classMethodDeclarationA from 'decorator';
+ import decoratorParameter from 'decorator';
+ var obj = {
+ @classMethodDeclarationA((parameter) => parameter)
+ @classMethodDeclarationA(decoratorParameter)
+ @classMethodDeclarationA
+ methodDeclaration(e) {
+ e();
+ }
+ };
+ obj;
+ `,
+ { "no-unused-vars": 1 },
+ );
+ });
+
+ it("object property get/set", () => {
+ verifyDecoratorsLegacyAndAssertMessages(
+ `
+ import classMethodDeclarationA from 'decorator';
+ import decoratorParameter from 'decorator';
+ var obj = {
+ @classMethodDeclarationA((parameter) => parameter)
+ @classMethodDeclarationA(decoratorParameter)
+ @classMethodDeclarationA
+ get bar() { },
+ @classMethodDeclarationA((parameter) => parameter)
+ @classMethodDeclarationA(decoratorParameter)
+ @classMethodDeclarationA
+ set bar(val) { val; }
+ };
+ obj;
+ `,
+ { "no-unused-vars": 1 },
+ );
+ });
+ });
+
+ describe("decorators #72", () => {
+ it("class declaration", () => {
+ verifyAndAssertMessages(
+ `
+ import classDeclaration from 'decorator';
+ import decoratorParameter from 'decorator';
+ export
+ @classDeclaration((parameter) => parameter)
+ @classDeclaration(decoratorParameter)
+ @classDeclaration
+ class TextareaAutosize {}
+ `,
+ { "no-unused-vars": 1 },
+ );
+ });
+
+ it("method definition", () => {
+ verifyAndAssertMessages(
+ `
+ import classMethodDeclarationA from 'decorator';
+ import decoratorParameter from 'decorator';
+ export class TextareaAutosize {
+ @classMethodDeclarationA((parameter) => parameter)
+ @classMethodDeclarationA(decoratorParameter)
+ @classMethodDeclarationA
+ methodDeclaration(e) {
+ e();
+ }
+ }
+ `,
+ { "no-unused-vars": 1 },
+ );
+ });
+
+ it("method definition get/set", () => {
+ verifyAndAssertMessages(
+ `
+ import classMethodDeclarationA from 'decorator';
+ import decoratorParameter from 'decorator';
+ export class TextareaAutosize {
+ @classMethodDeclarationA((parameter) => parameter)
+ @classMethodDeclarationA(decoratorParameter)
+ @classMethodDeclarationA
+ get bar() { }
+ @classMethodDeclarationA((parameter) => parameter)
+ @classMethodDeclarationA(decoratorParameter)
+ @classMethodDeclarationA
+ set bar(val) { val; }
+ }
+ `,
+ { "no-unused-vars": 1 },
+ );
+ });
+ });
+
+ it("detects minimal no-unused-vars case #120", () => {
+ verifyAndAssertMessages("var unused;", { "no-unused-vars": 1 }, [
+ "1:5 'unused' is defined but never used. no-unused-vars",
+ ]);
+ });
+
+ // This two tests are disabled, as the feature to visit properties when
+ // there is a spread/rest operator has been removed as it caused problems
+ // with other rules #249
+ it.skip("visits excluded properties left of spread #95", () => {
+ verifyAndAssertMessages(
+ "var originalObject = {}; var {field1, field2, ...clone} = originalObject;",
+ { "no-unused-vars": 1 },
+ );
+ });
+
+ it.skip("visits excluded properties left of spread #210", () => {
+ verifyAndAssertMessages(
+ "const props = { yo: 'yo' }; const { ...otherProps } = props;",
+ { "no-unused-vars": 1 },
+ );
+ });
+
+ it("does not mark spread variables false-positive", () => {
+ verifyAndAssertMessages(
+ "var originalObject = {}; var {field1, field2, ...clone} = originalObject;",
+ { "no-undef": 1, "no-redeclare": 1 },
+ );
+ });
+
+ it("does not mark spread variables false-positive", () => {
+ verifyAndAssertMessages(
+ "const props = { yo: 'yo' }; const { ...otherProps } = props;",
+ { "no-undef": 1, "no-redeclare": 1 },
+ );
+ });
+
+ it("does not mark spread variables as use-before-define #249", () => {
+ verifyAndAssertMessages(
+ "var originalObject = {}; var {field1, field2, ...clone} = originalObject;",
+ { "no-use-before-define": 1 },
+ );
+ });
+
+ it("detects no-unused-vars with object destructuring #142", () => {
+ verifyAndAssertMessages(
+ "const {Bacona} = require('baconjs')",
+ { "no-undef": 1, "no-unused-vars": 1 },
+ ["1:8 'Bacona' is assigned a value but never used. no-unused-vars"],
+ );
+ });
+
+ it("don't warn no-unused-vars with spread #142", () => {
+ verifyAndAssertMessages(
+ `
+ export default function test(data) {
+ return {
+ foo: 'bar',
+ ...data
+ };
+ }
+ `,
+ { "no-undef": 1, "no-unused-vars": 1 },
+ );
+ });
+
+ it("excludes comment tokens #153", () => {
+ verifyAndAssertMessages(
+ `
+ var a = [
+ 1,
+ 2, // a trailing comment makes this line fail comma-dangle (always-multiline)
+ ];
+ `,
+ { "comma-dangle": [2, "always-multiline"] },
+ );
+
+ verifyAndAssertMessages(
+ `
+ switch (a) {
+ // A comment here makes the above line fail brace-style
+ case 1:
+ console.log(a);
+ }
+ `,
+ { "brace-style": 2 },
+ );
+ });
+
+ it("ternary and parens #149", () => {
+ verifyAndAssertMessages("true ? (true) : false;", { "space-infix-ops": 1 });
+ });
+
+ it("line comment space-in-parens #124", () => {
+ verifyAndAssertMessages(
+ `
+ React.createClass({
+ render() {
+ // return (
+ //
+ // ); // <-- this is the line that is reported
+ }
+ });
+ `,
+ { "space-in-parens": 1 },
+ );
+ });
+
+ it("block comment space-in-parens #124", () => {
+ verifyAndAssertMessages(
+ `
+ React.createClass({
+ render() {
+ /*
+ return (
+
+ ); // <-- this is the line that is reported
+ */
+ }
+ });
+ `,
+ { "space-in-parens": 1 },
+ );
+ });
+
+ it("no no-undef error with rest #11", () => {
+ verifyAndAssertMessages("const [a, ...rest] = ['1', '2', '3']; a; rest;", {
+ "no-undef": 1,
+ "no-unused-vars": 1,
+ });
+ });
+
+ it("async function with space-before-function-paren #168", () => {
+ verifyAndAssertMessages("it('handles updates', async function() {});", {
+ "space-before-function-paren": [1, "never"],
+ });
+ });
+
+ it("default param flow type no-unused-vars #184", () => {
+ verifyAndAssertMessages(
+ `
+ type ResolveOptionType = {
+ depth?: number,
+ identifier?: string
+ };
+
+ export default function resolve(
+ options: ResolveOptionType = {}
+ ): Object {
+ options;
+ }
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+
+ it("no-use-before-define #192", () => {
+ verifyAndAssertMessages(
+ `
+ console.log(x);
+ var x = 1;
+ `,
+ { "no-use-before-define": 1 },
+ ["1:13 'x' was used before it was defined. no-use-before-define"],
+ );
+ });
+
+ it("jsx and stringliteral #216", () => {
+ verifyAndAssertMessages("");
+ });
+
+ it("getter/setter #218", () => {
+ verifyAndAssertMessages(
+ `
+ class Person {
+ set a (v) { }
+ }
+ `,
+ {
+ "space-before-function-paren": 1,
+ "keyword-spacing": [1, { before: true }],
+ indent: 1,
+ },
+ );
+ });
+
+ it("getter/setter #220", () => {
+ verifyAndAssertMessages(
+ `
+ var B = {
+ get x () {
+ return this.ecks;
+ },
+ set x (ecks) {
+ this.ecks = ecks;
+ }
+ };
+ `,
+ { "no-dupe-keys": 1 },
+ );
+ });
+
+ it("fixes issues with flow types and ObjectPattern", () => {
+ verifyAndAssertMessages(
+ `
+ import type Foo from 'bar';
+ export default class Foobar {
+ foo({ bar }: Foo) { bar; }
+ bar({ foo }: Foo) { foo; }
+ }
+ `,
+ { "no-unused-vars": 1, "no-shadow": 1 },
+ );
+ });
+
+ it("correctly detects redeclares if in script mode #217", () => {
+ verifyAndAssertMessages(
+ `
+ var a = 321;
+ var a = 123;
+ `,
+ { "no-redeclare": 1 },
+ ["2:5 'a' is already defined. no-redeclare"],
+ "script",
+ );
+ });
+
+ it("correctly detects redeclares if in module mode #217", () => {
+ verifyAndAssertMessages(
+ `
+ var a = 321;
+ var a = 123;
+ `,
+ { "no-redeclare": 1 },
+ ["2:5 'a' is already defined. no-redeclare"],
+ "module",
+ );
+ });
+
+ it("no-implicit-globals in script: globalReturn is false", () => {
+ verifyAndAssertMessages(
+ "var leakedGlobal = 1;",
+ { "no-implicit-globals": 1 },
+ [
+ "1:5 Unexpected 'var' declaration in the global scope, wrap in an IIFE for a local variable, assign as global property for a global variable. no-implicit-globals",
+ ],
+ "script",
+ {
+ env: {},
+ parserOptions: {
+ ecmaVersion: 6,
+ sourceType: "script",
+ ecmaFeatures: { globalReturn: false },
+ },
+ },
+ );
+ });
+
+ it("no-implicit-globals in script: globalReturn is true", () => {
+ verifyAndAssertMessages(
+ "var leakedGlobal = 1;",
+ { "no-implicit-globals": 1 },
+ [],
+ "script",
+ {
+ env: {},
+ parserOptions: {
+ ecmaVersion: 6,
+ sourceType: "script",
+ ecmaFeatures: { globalReturn: true },
+ },
+ },
+ );
+ });
+
+ it("no-implicit-globals in module", () => {
+ verifyAndAssertMessages(
+ "var leakedGlobal = 1;",
+ { "no-implicit-globals": 1 },
+ [],
+ "module",
+ {
+ env: {},
+ parserOptions: { ecmaVersion: 6, sourceType: "module" },
+ },
+ );
+ });
+
+ it("no-implicit-globals in default", () => {
+ verifyAndAssertMessages(
+ "var leakedGlobal = 1;",
+ { "no-implicit-globals": 1 },
+ [],
+ undefined,
+ {
+ env: {},
+ parserOptions: { ecmaVersion: 6 },
+ },
+ );
+ });
+
+ it("allowImportExportEverywhere option (#327)", () => {
+ verifyAndAssertMessages(
+ `
+ if (true) { import Foo from 'foo'; }
+ function foo() { import Bar from 'bar'; }
+ switch (a) { case 1: import FooBar from 'foobar'; }
+ `,
+ {},
+ [],
+ "module",
+ {
+ env: {},
+ parserOptions: {
+ ecmaVersion: 6,
+ sourceType: "module",
+ allowImportExportEverywhere: true,
+ },
+ },
+ );
+ });
+
+ it("with does not crash parsing in script mode (strict off) #171", () => {
+ verifyAndAssertMessages("with (arguments) { length; }", {}, [], "script");
+ });
+
+ xit("with does crash parsing in module mode (strict on) #171", () => {
+ verifyAndAssertMessages("with (arguments) { length; }", {}, [
+ "1:1 Parsing error: 'with' in strict mode",
+ ]);
+ });
+
+ it("new.target is not reported as undef #235", () => {
+ verifyAndAssertMessages("function foo () { return new.target }", {
+ "no-undef": 1,
+ });
+ });
+
+ it("decorator does not create TypeError #229", () => {
+ verifyAndAssertMessages(
+ `
+ class A {
+ @test
+ f() {}
+ }
+ `,
+ { "no-undef": 1 },
+ ["2:4 'test' is not defined. no-undef"],
+ );
+ });
+
+ it("Flow definition does not trigger warnings #223", () => {
+ verifyAndAssertMessages(
+ `
+ import { Map as $Map } from 'immutable';
+ function myFunction($state: $Map, { a, b, c } : { a: ?Object, b: ?Object, c: $Map }) {}
+ `,
+ { "no-dupe-args": 1, "no-redeclare": 1, "no-shadow": 1 },
+ );
+ });
+
+ it("newline-before-return with comments #289", () => {
+ verifyAndAssertMessages(
+ `
+ function a() {
+ if (b) {
+ /* eslint-disable no-console */
+ console.log('test');
+ /* eslint-enable no-console */
+ }
+
+ return hasGlobal;
+ }
+ `,
+ { "newline-before-return": 1 },
+ );
+ });
+
+ it("spaced-comment with shebang #163", () => {
+ verifyAndAssertMessages(
+ `
+ #!/usr/bin/env babel-node
+ import {spawn} from 'foobar';
+ `,
+ { "spaced-comment": 1 },
+ );
+ });
+
+ describe("Class Property Declarations", () => {
+ it("no-redeclare false positive 1", () => {
+ verifyAndAssertMessages(
+ `
+ class Group {
+ static propTypes = {};
+ }
+ class TypicalForm {
+ static propTypes = {};
+ }
+ `,
+ { "no-redeclare": 1 },
+ );
+ });
+
+ it("no-redeclare false positive 2", () => {
+ verifyAndAssertMessages(
+ `
+ function validate() {}
+ class MyComponent {
+ static validate = validate;
+ }
+ `,
+ { "no-redeclare": 1 },
+ );
+ });
+
+ it("check references", () => {
+ verifyAndAssertMessages(
+ `
+ var a;
+ class A {
+ prop1;
+ prop2 = a;
+ prop3 = b;
+ }
+ new A
+ `,
+ { "no-undef": 1, "no-unused-vars": 1, "no-redeclare": 1 },
+ ["5:11 'b' is not defined. no-undef"],
+ );
+ });
+ });
+
+ it("dynamic import support", () => {
+ verifyAndAssertMessages("import('test-module').then(() => {})");
+ });
+
+ it("regex with es6 unicodeCodePointEscapes", () => {
+ verifyAndAssertMessages(
+ "string.replace(/[\u{0000A0}-\u{10FFFF}<>&]/gmiu, (char) => `${char.codePointAt(0).toString(16)};`);",
+ );
+ });
+
+ describe("class field declarations", () => {
+ describe("field declarations", () => {
+ it("should not be undefined", () => {
+ verifyAndAssertMessages(
+ `
+ class C {
+ d = 1;
+ }
+ `,
+ { "no-undef": 1 },
+ );
+ });
+
+ it("should not be unused", () => {
+ verifyAndAssertMessages(
+ `
+ export class C {
+ d = 1;
+ }
+ `,
+ { "no-unused-vars": 1 },
+ );
+ });
+ });
+
+ describe("private field declarations", () => {
+ it("should not be undefined", () => {
+ verifyAndAssertMessages(
+ `
+ class C {
+ #d = 1;
+ }
+ `,
+ { "no-undef": 1 },
+ );
+ });
+
+ it("should not be unused", () => {
+ verifyAndAssertMessages(
+ `
+ export class C {
+ #d = 1;
+ }
+ `,
+ { "no-unused-vars": 1 },
+ );
+ });
+ });
+
+ describe("private methods", () => {
+ it("should not be undefined", () => {
+ verifyAndAssertMessages(
+ `
+ class C {
+ #d() {};
+ }
+ `,
+ { "no-undef": 1 },
+ );
+ });
+
+ it("should not be unused", () => {
+ verifyAndAssertMessages(
+ `
+ export class C {
+ #d() {};
+ }
+ `,
+ { "no-unused-vars": 1 },
+ );
+ });
+ });
+ });
+
+ describe("optional chaining operator", () => {
+ it("should not be undefined #595", () => {
+ verifyAndAssertMessages(
+ `
+ const foo = {};
+ foo?.bar;
+ `,
+ { "no-undef": 1 },
+ );
+ });
+ });
+
+ it("flow types on class method should be visited correctly", () => {
+ verifyAndAssertMessages(
+ `
+ import type NodeType from 'foo';
+ class NodeUtils {
+ finishNodeAt(node: T): T { return node; }
+ }
+
+ new NodeUtils();
+ `,
+ { "no-unused-vars": 1 },
+ );
+ });
+
+ it("works with dynamicImport", () => {
+ verifyAndAssertMessages(
+ `
+ import('a');
+ `,
+ );
+ });
+
+ it("works with numericSeparator", () => {
+ verifyAndAssertMessages(
+ `
+ 1_000
+ `,
+ );
+ });
+
+ it("works with optionalChaining", () => {
+ verifyAndAssertMessages(
+ `
+ a?.b
+ `,
+ );
+ });
+
+ it("works with import.meta", () => {
+ verifyAndAssertMessages(
+ `
+ import.meta
+ `,
+ );
+ });
+
+ it("works with classPrivateProperties", () => {
+ verifyAndAssertMessages(
+ `
+ class A { #a = 1; }
+ `,
+ );
+ });
+
+ it("works with classPrivateMethods", () => {
+ verifyAndAssertMessages(
+ `
+ class A { #a(b, c) {} }
+ `,
+ );
+ });
+
+ it("works with arrow function classPrivateProperties", () => {
+ verifyAndAssertMessages(
+ `
+ class A { #a = (a, b) => {}; }
+ `,
+ );
+ });
+
+ it("works with optionalCatchBinding", () => {
+ verifyAndAssertMessages(
+ `
+ try {} catch {}
+ try {} catch {} finally {}
+ `,
+ );
+ });
+
+ it("exportDefaultFrom", () => {
+ verifyAndAssertMessages(
+ `
+ export v from "mod"
+ `,
+ );
+ });
+
+ it("exportNamespaceFrom", () => {
+ verifyAndAssertMessages(
+ `
+ export * as ns from "mod"
+ `,
+ );
+ });
+
+ it("ignore eval in scope analysis", () => {
+ verifyAndAssertMessages(
+ `
+ const a = 1;
+ console.log(a);
+ eval('');
+ `,
+ { "no-unused-vars": 1, "no-undef": 1 },
+ );
+ });
+});
diff --git a/eslint/babel-eslint-plugin-development/.npmignore b/eslint/babel-eslint-plugin-development/.npmignore
new file mode 100644
index 000000000000..c3aafe7a7721
--- /dev/null
+++ b/eslint/babel-eslint-plugin-development/.npmignore
@@ -0,0 +1,4 @@
+src
+test
+.*
+*.log
\ No newline at end of file
diff --git a/eslint/babel-eslint-plugin-development/README.md b/eslint/babel-eslint-plugin-development/README.md
new file mode 100644
index 000000000000..12cc354b7470
--- /dev/null
+++ b/eslint/babel-eslint-plugin-development/README.md
@@ -0,0 +1,38 @@
+# @babel/eslint-plugin-development
+
+A set of eslint rules to enforce best practices in the development of Babel plugins.
+
+## Installation
+
+You'll first need to install [ESLint](http://eslint.org):
+
+```
+$ npm install --save-dev eslint
+```
+
+Next, install `@babel/eslint-plugin-development`:
+
+```
+$ npm install --save-dev @babel/eslint-plugin-development
+```
+
+Then, load the plugin in your `.eslintrc` configuration file. You can omit the `eslint-plugin-` prefix:
+
+```json
+{
+ "plugins": ["@babel/development"]
+}
+```
+
+## Supported Rules
+
+> Note: Rules marked with :wrench: are autofixable.
+
+* `@babel/development/no-deprecated-clone` (:wrench:): Disallows using the deprecated
+ `t.clone(node)` and `t.cloneDeep(node)` methods from `@babel/types`. Those
+ calls are replaced with `t.cloneNode(node)` when using `eslint --fix`.
+* `@babel/development/no-undefined-identifier`: Disallows using
+ `t.identifier("undefined")` to create a node which represents an `undefined`
+ value, since it might cause problem if `undefined` is redeclared.
+* `@babel/development/plugin-name`: Requires plugins to have a `name` property, which
+ can be useful for debugging purposes.
diff --git a/eslint/babel-eslint-plugin-development/package.json b/eslint/babel-eslint-plugin-development/package.json
new file mode 100644
index 000000000000..609022429995
--- /dev/null
+++ b/eslint/babel-eslint-plugin-development/package.json
@@ -0,0 +1,36 @@
+{
+ "name": "@babel/eslint-plugin-development",
+ "version": "0.0.0",
+ "description": "ESLint rules that enforce best practices in the development of Babel plugins.",
+ "private": true,
+ "keywords": [
+ "eslint",
+ "eslintplugin",
+ "eslint-plugin"
+ ],
+ "author": {
+ "name": "Nicolò Ribaudo",
+ "email": "nicolo.ribaudo@gmail.com",
+ "url": "https://github.com/nicolo-ribaudo"
+ },
+ "main": "lib/index.js",
+ "devDependencies": {
+ "eslint": "^5.9.0"
+ },
+ "engines": {
+ "node": ">=10.9"
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/babel/babel.git",
+ "directory": "eslint/babel-eslint-plugin-development"
+ },
+ "bugs": {
+ "url": "https://github.com/babel/babel/issues"
+ },
+ "homepage": "https://github.com/babel/babel/tree/master/eslint/babel-eslint-plugin-development"
+}
diff --git a/eslint/babel-eslint-plugin-development/src/index.js b/eslint/babel-eslint-plugin-development/src/index.js
new file mode 100644
index 000000000000..ae6662710684
--- /dev/null
+++ b/eslint/babel-eslint-plugin-development/src/index.js
@@ -0,0 +1,11 @@
+import noDeprecatedClone from "./rules/no-deprecated-clone";
+import noUndefinedIdentifier from "./rules/no-undefined-identifier";
+import pluginName from "./rules/plugin-name";
+
+export default {
+ rules: {
+ "no-deprecated-clone": noDeprecatedClone,
+ "no-undefined-identifier": noUndefinedIdentifier,
+ "plugin-name": pluginName,
+ },
+};
diff --git a/eslint/babel-eslint-plugin-development/src/rules/no-deprecated-clone.js b/eslint/babel-eslint-plugin-development/src/rules/no-deprecated-clone.js
new file mode 100644
index 000000000000..dddc5eb3b729
--- /dev/null
+++ b/eslint/babel-eslint-plugin-development/src/rules/no-deprecated-clone.js
@@ -0,0 +1,39 @@
+import getReferenceOrigin from "../utils/get-reference-origin";
+import isFromBabelTypes from "../utils/is-from-babel-types";
+
+export default {
+ meta: {
+ schema: [],
+ fixable: "code",
+ },
+ create(context) {
+ return {
+ CallExpression(node) {
+ const { callee } = node;
+ const scope = context.getScope();
+
+ const origin = getReferenceOrigin(callee, scope);
+ if (!origin) return;
+
+ const { name } = origin;
+ if (
+ (name === "clone" || name === "cloneDeep") &&
+ isFromBabelTypes(origin, scope)
+ ) {
+ const isMemberExpression = callee.type === "MemberExpression";
+ const id = isMemberExpression ? callee.property : callee;
+
+ context.report({
+ node: id,
+ message: `t.${name}() is deprecated. Use t.cloneNode() instead.`,
+ fix(fixer) {
+ if (isMemberExpression) {
+ return fixer.replaceText(id, "cloneNode");
+ }
+ },
+ });
+ }
+ },
+ };
+ },
+};
diff --git a/eslint/babel-eslint-plugin-development/src/rules/no-undefined-identifier.js b/eslint/babel-eslint-plugin-development/src/rules/no-undefined-identifier.js
new file mode 100644
index 000000000000..33e154be8979
--- /dev/null
+++ b/eslint/babel-eslint-plugin-development/src/rules/no-undefined-identifier.js
@@ -0,0 +1,39 @@
+import getReferenceOrigin from "../utils/get-reference-origin";
+import isFromBabelTypes from "../utils/is-from-babel-types";
+
+function firstArgumentIsUndefinedString(argumentsArray) {
+ return (
+ argumentsArray.length > 0 &&
+ argumentsArray[0].type === "Literal" &&
+ argumentsArray[0].value === "undefined"
+ );
+}
+
+export default {
+ meta: {
+ schema: [],
+ },
+ create(context) {
+ return {
+ CallExpression(node) {
+ const { callee } = node;
+ const scope = context.getScope();
+
+ const origin = getReferenceOrigin(callee, scope);
+ if (!origin) return;
+
+ const { name } = origin;
+ if (
+ (name === "identifier" || name === "Identifier") &&
+ firstArgumentIsUndefinedString(node.arguments) &&
+ isFromBabelTypes(origin, scope)
+ ) {
+ context.report(
+ node,
+ "Use path.scope.buildUndefinedNode() to create an undefined identifier directly.",
+ );
+ }
+ },
+ };
+ },
+};
diff --git a/eslint/babel-eslint-plugin-development/src/rules/plugin-name.js b/eslint/babel-eslint-plugin-development/src/rules/plugin-name.js
new file mode 100644
index 000000000000..df14476cd43b
--- /dev/null
+++ b/eslint/babel-eslint-plugin-development/src/rules/plugin-name.js
@@ -0,0 +1,50 @@
+import isBabelPluginFactory from "../utils/is-babel-plugin-factory";
+
+function getReturnValue(node) {
+ const { body } = node;
+
+ if (body.type === "BlockStatement") {
+ const returnNode = body.body.find(n => n.type === "ReturnStatement");
+ return returnNode && returnNode.argument;
+ }
+
+ // Arrow functions with implicit return
+ return body;
+}
+
+export default {
+ meta: {
+ schema: [],
+ },
+ create(context /*: Context */) {
+ let pluginFound = false;
+
+ return {
+ FunctionDeclaration: functionVisitor,
+ FunctionExpression: functionVisitor,
+ ArrowFunctionExpression: functionVisitor,
+
+ "Program:exit"(node) {
+ if (!pluginFound) {
+ context.report(node, "This file does not export a Babel plugin.");
+ }
+ },
+ };
+
+ function functionVisitor(node) {
+ if (!isBabelPluginFactory(node, context.getScope())) return;
+
+ const returnValue = getReturnValue(node);
+ if (!returnValue || returnValue.type !== "ObjectExpression") return;
+
+ pluginFound = true;
+
+ if (!returnValue.properties.some(p => p.key.name === "name")) {
+ context.report(
+ returnValue,
+ "This Babel plugin doesn't have a 'name' property.",
+ );
+ }
+ }
+ },
+};
diff --git a/eslint/babel-eslint-plugin-development/src/utils/get-export-name.js b/eslint/babel-eslint-plugin-development/src/utils/get-export-name.js
new file mode 100644
index 000000000000..2d75e3e46a4d
--- /dev/null
+++ b/eslint/babel-eslint-plugin-development/src/utils/get-export-name.js
@@ -0,0 +1,22 @@
+export default function getExportName(node) {
+ const { parent } = node;
+
+ if (parent.type === "ExportDefaultDeclaration") {
+ return "default";
+ }
+
+ if (parent.type === "ExportNamedDeclaration") {
+ return node.id.name;
+ }
+
+ if (
+ parent.type === "AssignmentExpression" &&
+ parent.left.type === "MemberExpression" &&
+ parent.left.object.type === "Identifier" &&
+ parent.left.object.name === "module" &&
+ parent.left.property.type === "Identifier" &&
+ parent.left.property.name === "exports"
+ ) {
+ return "module.exports";
+ }
+}
diff --git a/eslint/babel-eslint-plugin-development/src/utils/get-reference-origin.js b/eslint/babel-eslint-plugin-development/src/utils/get-reference-origin.js
new file mode 100644
index 000000000000..25fd7b0a541f
--- /dev/null
+++ b/eslint/babel-eslint-plugin-development/src/utils/get-reference-origin.js
@@ -0,0 +1,141 @@
+/*::
+type ReferenceOriginImport = { kind: "import", source: string, name: string };
+type ReferenceOriginParam = {
+ kind: "param",
+ index: number,
+ functionNode: Node,
+};
+
+type ReferenceOrigin =
+ | ReferenceOriginImport
+ | ReferenceOriginParam
+ | { kind: "import *", source: string }
+ | {
+ kind: "property",
+ base: ReferenceOriginImport | ReferenceOriginParam,
+ path: string,
+ name: string,
+ };
+*/
+
+// Given a node and a context, returns a description of where its value comes
+// from.
+// It resolves imports, parameters of exported functions and property accesses.
+// See the ReferenceOrigin type for more informations.
+export default function getReferenceOrigin(
+ node,
+ scope,
+) /*: ?ReferenceOrigin */ {
+ if (node.type === "Identifier") {
+ const variable = getVariableDefinition(node.name, scope);
+ if (!variable) return null;
+
+ const definition = variable.definition;
+ const defNode = definition.node;
+
+ if (definition.type === "ImportBinding") {
+ if (defNode.type === "ImportSpecifier") {
+ return {
+ kind: "import",
+ source: definition.parent.source.value,
+ name: defNode.imported.name,
+ };
+ }
+ if (defNode.type === "ImportNamespaceSpecifier") {
+ return {
+ kind: "import *",
+ source: definition.parent.source.value,
+ };
+ }
+ }
+
+ if (definition.type === "Variable" && defNode.init) {
+ const origin = getReferenceOrigin(defNode.init, variable.scope);
+ return origin && patternToProperty(definition.name, origin);
+ }
+
+ if (definition.type === "Parameter") {
+ return patternToProperty(definition.name, {
+ kind: "param",
+ index: definition.index,
+ functionNode: definition.node,
+ });
+ }
+ }
+
+ if (node.type === "MemberExpression" && !node.computed) {
+ const origin = getReferenceOrigin(node.object, scope);
+ return origin && addProperty(origin, node.property.name);
+ }
+
+ return null;
+}
+
+function getVariableDefinition(name, scope) {
+ let currentScope = scope;
+ do {
+ const variable = currentScope.set.get(name);
+ if (variable && variable.defs[0]) {
+ return { scope: currentScope, definition: variable.defs[0] };
+ }
+ } while ((currentScope = currentScope.upper));
+}
+
+function patternToProperty(id, base) {
+ const path = getPatternPath(id);
+ return path && path.reduce(addProperty, base);
+}
+
+// Adds a property to a given origin. If it was a namespace import it becomes
+// a named import, so that `import * as x from "foo"; x.bar` and
+// `import { bar } from "foo"` have the same origin.
+function addProperty(origin, name) {
+ if (origin.kind === "import *") {
+ return {
+ kind: "import",
+ source: origin.source,
+ name,
+ };
+ }
+ if (origin.kind === "property") {
+ return {
+ kind: "property",
+ base: origin.base,
+ path: origin.path + "." + name,
+ name,
+ };
+ }
+ return {
+ kind: "property",
+ base: origin,
+ path: name,
+ name,
+ };
+}
+
+// if "node" is c of { a: { b: c } }, the result is ["a","b"]
+function getPatternPath(node) {
+ let current = node;
+ const path = [];
+
+ // Unshift keys to path while going up
+ do {
+ const property = current.parent;
+ if (
+ property.type === "ArrayPattern" ||
+ property.type === "AssignmentPattern" ||
+ property.computed
+ ) {
+ // These nodes are not supported.
+ return null;
+ }
+ if (property.type === "Property") {
+ path.unshift(property.key.name);
+ } else {
+ // The destructuring pattern is finished
+ break;
+ }
+ } while ((current = current.parent.parent));
+
+ return path;
+}
diff --git a/eslint/babel-eslint-plugin-development/src/utils/is-babel-plugin-factory.js b/eslint/babel-eslint-plugin-development/src/utils/is-babel-plugin-factory.js
new file mode 100644
index 000000000000..19d889bffe21
--- /dev/null
+++ b/eslint/babel-eslint-plugin-development/src/utils/is-babel-plugin-factory.js
@@ -0,0 +1,24 @@
+import getReferenceOrigin from "./get-reference-origin";
+import getExportName from "./get-export-name";
+
+export default function isBabelPluginFactory(node, scope) {
+ const { parent } = node;
+
+ if (parent.type === "CallExpression") {
+ const calleeOrigin = getReferenceOrigin(parent.callee, scope);
+
+ // Using "declare" from "@babel/helper-plugin-utils"
+ return !!(
+ calleeOrigin &&
+ calleeOrigin.kind === "import" &&
+ calleeOrigin.name === "declare" &&
+ calleeOrigin.source === "@babel/helper-plugin-utils"
+ );
+ }
+
+ const exportName = getExportName(node);
+
+ // export default function ({ types: t }) {}
+ // module.exports = function ({ types: t }) {}
+ return exportName === "default" || exportName === "module.exports";
+}
diff --git a/eslint/babel-eslint-plugin-development/src/utils/is-from-babel-types.js b/eslint/babel-eslint-plugin-development/src/utils/is-from-babel-types.js
new file mode 100644
index 000000000000..4a7a24bca040
--- /dev/null
+++ b/eslint/babel-eslint-plugin-development/src/utils/is-from-babel-types.js
@@ -0,0 +1,33 @@
+import isBabelPluginFactory from "./is-babel-plugin-factory";
+
+// Check if a ReferenceOrigin (returned by ./get-reference-origin.js)
+// is a reference to a @babel/types export.
+export default function isFromBabelTypes(
+ origin /*: ReferenceOrigin */,
+ scope /*: Scope */,
+) {
+ if (origin.kind === "import" && origin.source === "@babel/types") {
+ // imported from @babel/types
+ return true;
+ }
+
+ if (
+ origin.kind === "property" &&
+ origin.base.kind === "import" &&
+ origin.base.name === "types" &&
+ origin.base.source === "@babel/core"
+ ) {
+ // imported from @babel/core
+ return true;
+ }
+
+ if (
+ origin.kind === "property" &&
+ origin.base.kind === "param" &&
+ origin.base.index === 0
+ ) {
+ return isBabelPluginFactory(origin.base.functionNode, scope);
+ }
+
+ return false;
+}
diff --git a/eslint/babel-eslint-plugin-development/test/rules/no-deprecated-clone.js b/eslint/babel-eslint-plugin-development/test/rules/no-deprecated-clone.js
new file mode 100644
index 000000000000..9905bc170f0c
--- /dev/null
+++ b/eslint/babel-eslint-plugin-development/test/rules/no-deprecated-clone.js
@@ -0,0 +1,136 @@
+import rule from "../../src/rules/no-deprecated-clone";
+import { RuleTester } from "eslint";
+
+const cloneError = "t.clone() is deprecated. Use t.cloneNode() instead.";
+const cloneDeepError =
+ "t.cloneDeep() is deprecated. Use t.cloneNode() instead.";
+
+const ruleTester = new RuleTester({
+ parserOptions: { sourceType: "module" },
+});
+
+ruleTester.run("no-deprecated-clone", rule, {
+ valid: [
+ `_.clone(obj)`,
+ `_.cloneDeep(obj)`,
+ `import * as t from "lib"; t.clone();`,
+ `import * as t from "lib"; t.cloneDeep();`,
+ `function f(_) { _.types.clone(); }`,
+ `function f(_) { _.types.cloneDeep(); }`,
+ `import * as t from "@babel/types"; t.cloneNode();`,
+ ],
+ invalid: [
+ {
+ code: `import { clone } from "@babel/types"; clone();`,
+ errors: [cloneError],
+ },
+ {
+ code: `import { cloneDeep } from "@babel/types"; cloneDeep();`,
+ errors: [cloneDeepError],
+ },
+ {
+ code: `import { clone } from "@babel/types"; var clone2 = clone; clone2();`,
+ errors: [cloneError],
+ },
+ {
+ code: `import { cloneDeep } from "@babel/types"; var cloneDeep2 = cloneDeep; cloneDeep2();`,
+ errors: [cloneDeepError],
+ },
+ {
+ code: `import * as t from "@babel/types"; t.clone();`,
+ errors: [cloneError],
+ },
+ {
+ code: `import * as t from "@babel/types"; t.cloneDeep();`,
+ errors: [cloneDeepError],
+ },
+ {
+ code: `import * as t from "@babel/types"; var { clone } = t; clone();`,
+ errors: [cloneError],
+ },
+ {
+ code: `import * as t from "@babel/types"; var { cloneDeep } = t; cloneDeep();`,
+ errors: [cloneDeepError],
+ },
+ {
+ code: `import { clone as c } from "@babel/types"; c();`,
+ errors: [cloneError],
+ },
+ {
+ code: `import { cloneDeep as cD } from "@babel/types"; cD();`,
+ errors: [cloneDeepError],
+ },
+ {
+ code: `import * as babel from "@babel/core"; babel.types.clone();`,
+ errors: [cloneError],
+ },
+ {
+ code: `import * as babel from "@babel/core"; babel.types.cloneDeep();`,
+ errors: [cloneDeepError],
+ },
+ {
+ code: `import { types } from "@babel/core"; types.clone();`,
+ errors: [cloneError],
+ },
+ {
+ code: `import { types } from "@babel/core"; types.cloneDeep();`,
+ errors: [cloneDeepError],
+ },
+ {
+ code: `import { types as t } from "@babel/core"; t.clone();`,
+ errors: [cloneError],
+ },
+ {
+ code: `import { types as t } from "@babel/core"; t.cloneDeep();`,
+ errors: [cloneDeepError],
+ },
+ {
+ code: `export default function plugin(babel) { babel.types.clone() }`,
+ errors: [cloneError],
+ },
+ {
+ code: `export default function plugin(babel) { babel.types.cloneDeep() }`,
+ errors: [cloneDeepError],
+ },
+ {
+ code: `export default function plugin({ types }) { types.clone() }`,
+ errors: [cloneError],
+ },
+ {
+ code: `export default function plugin({ types }) { types.cloneDeep() }`,
+ errors: [cloneDeepError],
+ },
+ {
+ code: `export default function plugin({ types: t }) { t.clone() }`,
+ errors: [cloneError],
+ },
+ {
+ code: `export default function plugin({ types: t }) { t.cloneDeep() }`,
+ errors: [cloneDeepError],
+ },
+ {
+ code: `export default ({ types }) => { types.clone() }`,
+ errors: [cloneError],
+ },
+ {
+ code: `export default ({ types }) => { types.cloneDeep() }`,
+ errors: [cloneDeepError],
+ },
+ {
+ code: `module.exports = function plugin({ types }) { types.clone() }`,
+ errors: [cloneError],
+ },
+ {
+ code: `module.exports = function plugin({ types }) { types.cloneDeep() }`,
+ errors: [cloneDeepError],
+ },
+ {
+ code: `import { declare } from "@babel/helper-plugin-utils"; declare(({ types }) => { types.clone() });`,
+ errors: [cloneError],
+ },
+ {
+ code: `import { declare } from "@babel/helper-plugin-utils"; declare(({ types }) => { types.cloneDeep() });`,
+ errors: [cloneDeepError],
+ },
+ ],
+});
diff --git a/eslint/babel-eslint-plugin-development/test/rules/no-undefined-identifier.js b/eslint/babel-eslint-plugin-development/test/rules/no-undefined-identifier.js
new file mode 100644
index 000000000000..d6d8c6b28b6b
--- /dev/null
+++ b/eslint/babel-eslint-plugin-development/test/rules/no-undefined-identifier.js
@@ -0,0 +1,78 @@
+import rule from "../../src/rules/no-undefined-identifier";
+import { RuleTester } from "eslint";
+
+const error =
+ "Use path.scope.buildUndefinedNode() to create an undefined identifier directly.";
+
+const ruleTester = new RuleTester({
+ parserOptions: { sourceType: "module" },
+});
+
+ruleTester.run("no-undefined-identifier", rule, {
+ valid: [
+ `_.identifier("undefined")`,
+ `_.Identifier("undefined")`,
+ `import * as t from "lib"; t.identifier("undefined");`,
+ `function f(_) { _.types.identifier("undefined"); }`,
+ `import * as t from "@babel/types"; t.identifier("not_undefined");`,
+ `path.scope.buildUndefinedNode();`,
+ ],
+ invalid: [
+ {
+ code: `import { identifier } from "@babel/types"; identifier("undefined");`,
+ errors: [error],
+ },
+ {
+ code: `import { Identifier } from "@babel/types"; Identifier("undefined");`,
+ errors: [error],
+ },
+ {
+ code: `import * as t from "@babel/types"; t.identifier("undefined");`,
+ errors: [error],
+ },
+ {
+ code: `import * as t from "@babel/types"; var { identifier } = t; identifier("undefined");`,
+ errors: [error],
+ },
+ {
+ code: `import { identifier as id } from "@babel/types"; id("undefined");`,
+ errors: [error],
+ },
+ {
+ code: `import * as babel from "@babel/core"; babel.types.identifier("undefined");`,
+ errors: [error],
+ },
+ {
+ code: `import { types } from "@babel/core"; types.identifier("undefined");`,
+ errors: [error],
+ },
+ {
+ code: `import { types as t } from "@babel/core"; t.identifier("undefined");`,
+ errors: [error],
+ },
+ {
+ code: `export default function plugin(babel) { babel.types.identifier("undefined") }`,
+ errors: [error],
+ },
+ {
+ code: `export default function plugin({ types }) { types.identifier("undefined") }`,
+ errors: [error],
+ },
+ {
+ code: `export default function plugin({ types: t }) { t.identifier("undefined") }`,
+ errors: [error],
+ },
+ {
+ code: `export default ({ types }) => { types.identifier("undefined") }`,
+ errors: [error],
+ },
+ {
+ code: `module.exports = function plugin({ types }) { types.identifier("undefined") }`,
+ errors: [error],
+ },
+ {
+ code: `import { declare } from "@babel/helper-plugin-utils"; declare(({ types }) => { types.identifier("undefined") });`,
+ errors: [error],
+ },
+ ],
+});
diff --git a/eslint/babel-eslint-plugin-development/test/rules/plugin-name.js b/eslint/babel-eslint-plugin-development/test/rules/plugin-name.js
new file mode 100644
index 000000000000..d39924efe752
--- /dev/null
+++ b/eslint/babel-eslint-plugin-development/test/rules/plugin-name.js
@@ -0,0 +1,52 @@
+import rule from "../../src/rules/plugin-name";
+import { RuleTester } from "eslint";
+
+const missingPluginError = "This file does not export a Babel plugin.";
+const missingNameError = "This Babel plugin doesn't have a 'name' property.";
+
+const ruleTester = new RuleTester({
+ parserOptions: { sourceType: "module" },
+});
+
+ruleTester.run("plugin-name", rule, {
+ valid: [
+ `export default function () { return { name: "test-plugin" } }`,
+ `import { declare } from "@babel/helper-plugin-utils"; declare(() => { return { name: "test-plugin" } })`,
+ `import { declare } from "@babel/helper-plugin-utils"; declare(() => ({ name: "test-plugin" }))`,
+ `module.exports = function () { return { name: "foo" }; }`,
+ ],
+ invalid: [
+ {
+ code: `function fn() { return { name: "foo" } }`,
+ errors: [missingPluginError],
+ },
+ {
+ code: `export function fn() { return { name: "foo" } }`,
+ errors: [missingPluginError],
+ },
+ {
+ code: `(function fn() { return { name: "foo" } })`,
+ errors: [missingPluginError],
+ },
+ {
+ code: `() => { return { name: "foo" } }`,
+ errors: [missingPluginError],
+ },
+ {
+ code: `export default function fn() {}`,
+ errors: [missingPluginError],
+ },
+ {
+ code: `export default function fn() { return {} }`,
+ errors: [missingNameError],
+ },
+ {
+ code: `import { declare } from "@babel/helper-plugin-utils"; declare(() => ({}))`,
+ errors: [missingNameError],
+ },
+ {
+ code: `module.exports = function () { return {} }`,
+ errors: [missingNameError],
+ },
+ ],
+});
diff --git a/eslint/babel-eslint-plugin/.npmignore b/eslint/babel-eslint-plugin/.npmignore
new file mode 100644
index 000000000000..ad69326ce8c2
--- /dev/null
+++ b/eslint/babel-eslint-plugin/.npmignore
@@ -0,0 +1,4 @@
+src
+test
+.*
+*.log
diff --git a/eslint/babel-eslint-plugin/LICENSE b/eslint/babel-eslint-plugin/LICENSE
new file mode 100644
index 000000000000..3d0e2f3067b8
--- /dev/null
+++ b/eslint/babel-eslint-plugin/LICENSE
@@ -0,0 +1,23 @@
+Copyright (c) 2014-2015 Jason Quense
+Original work by respective rule authors; copywrites noted in files.
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/eslint/babel-eslint-plugin/README.md b/eslint/babel-eslint-plugin/README.md
new file mode 100644
index 000000000000..5577c01344c0
--- /dev/null
+++ b/eslint/babel-eslint-plugin/README.md
@@ -0,0 +1,47 @@
+# @babel/eslint-plugin
+
+Companion rules for `@babel/eslint-parser`. `@babel/eslint-parser` does a great job at adapting `eslint`
+for use with Babel, but it can't change the built-in rules to support experimental features.
+`@babel/eslint-plugin` re-implements problematic rules so they do not give false positives or negatives.
+
+> Requires Node 10.9 or greater
+
+### Install
+
+```sh
+npm install @babel/eslint-plugin --save-dev
+```
+
+Load the plugin in your `.eslintrc.json` file:
+
+```json
+{
+ "plugins": ["@babel/eslint-plugin"]
+}
+```
+
+Finally enable all the rules you would like to use (remember to disable the
+original ones as well!).
+
+```json
+{
+ "rules": {
+ "babel/new-cap": "error",
+ "babel/no-invalid-this": "error",
+ "babel/no-unused-expressions": "error",
+ "babel/object-curly-spacing": "error",
+ "babel/semi": "error",
+ }
+}
+```
+### Rules
+
+Each rule corresponds to a core `eslint` rule and has the same options.
+
+🛠: means it's autofixable with `--fix`.
+
+- `babel/new-cap`: handles decorators (`@Decorator`)
+- `babel/no-invalid-this`: handles class fields and private class methods (`class A { a = this.b; }`)
+- `babel/no-unused-expressions`: handles `do` expressions
+- `babel/object-curly-spacing`: handles `export * as x from "mod";` (🛠)
+- `babel/semi`: Handles class properties (🛠)
diff --git a/eslint/babel-eslint-plugin/package.json b/eslint/babel-eslint-plugin/package.json
new file mode 100644
index 000000000000..f259174f472b
--- /dev/null
+++ b/eslint/babel-eslint-plugin/package.json
@@ -0,0 +1,41 @@
+{
+ "name": "@babel/eslint-plugin",
+ "version": "0.0.0",
+ "description": "Companion rules for @babel/eslint-parser",
+ "main": "lib/index.js",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/babel/babel.git",
+ "directory": "eslint/babel-eslint-plugin"
+ },
+ "keywords": [
+ "babel",
+ "eslint",
+ "eslintplugin",
+ "eslint-plugin",
+ "babel-eslint"
+ ],
+ "author": "Jason Quense @monasticpanic",
+ "license": "MIT",
+ "private": true,
+ "engines": {
+ "node": ">=10.9"
+ },
+ "bugs": {
+ "url": "https://github.com/babel/babel/issues"
+ },
+ "homepage": "https://github.com/babel/babel/tree/master/eslint/babel-eslint-plugin",
+ "peerDependencies": {
+ "@babel/eslint-parser": "0.0.0",
+ "eslint": ">=6.0.0"
+ },
+ "dependencies": {
+ "eslint-rule-composer": "^0.3.0"
+ },
+ "devDependencies": {
+ "@babel/eslint-shared-fixtures": "*",
+ "@babel/eslint-parser": "*",
+ "eslint": "^6.0.0",
+ "lodash.clonedeep": "^4.5.0"
+ }
+}
diff --git a/eslint/babel-eslint-plugin/src/index.js b/eslint/babel-eslint-plugin/src/index.js
new file mode 100644
index 000000000000..96fd40caf70e
--- /dev/null
+++ b/eslint/babel-eslint-plugin/src/index.js
@@ -0,0 +1,22 @@
+import newCap from "./rules/new-cap";
+import noInvalidThis from "./rules/no-invalid-this";
+import noUnusedExpressions from "./rules/no-unused-expressions";
+import objectCurlySpacing from "./rules/object-curly-spacing";
+import semi from "./rules/semi";
+
+module.exports = {
+ rules: {
+ "new-cap": newCap,
+ "no-invalid-this": noInvalidThis,
+ "no-unused-expressions": noUnusedExpressions,
+ "object-curly-spacing": objectCurlySpacing,
+ semi,
+ },
+ rulesConfig: {
+ "new-cap": "off",
+ "no-invalid-this": "off",
+ "no-unused-expressions": "off",
+ "object-curly-spacing": "off",
+ semi: "off",
+ },
+};
diff --git a/eslint/babel-eslint-plugin/src/rules/new-cap.js b/eslint/babel-eslint-plugin/src/rules/new-cap.js
new file mode 100644
index 000000000000..1fddf1a43b5a
--- /dev/null
+++ b/eslint/babel-eslint-plugin/src/rules/new-cap.js
@@ -0,0 +1,18 @@
+import ruleComposer from "eslint-rule-composer";
+import eslint from "eslint";
+
+const rule = new eslint.Linter().getRules().get("new-cap");
+
+/**
+ * Returns whether a node is under a decorator or not.
+ * @param {ASTNode} node CallExpression node
+ * @returns {Boolean} Returns true if the node is under a decorator.
+ */
+function isDecorator(node) {
+ return node.parent.type === "Decorator";
+}
+
+export default ruleComposer.filterReports(
+ rule,
+ problem => !isDecorator(problem.node),
+);
diff --git a/eslint/babel-eslint-plugin/src/rules/no-invalid-this.js b/eslint/babel-eslint-plugin/src/rules/no-invalid-this.js
new file mode 100644
index 000000000000..4c6434235c2a
--- /dev/null
+++ b/eslint/babel-eslint-plugin/src/rules/no-invalid-this.js
@@ -0,0 +1,20 @@
+import ruleComposer from "eslint-rule-composer";
+import eslint from "eslint";
+
+const noInvalidThisRule = new eslint.Linter().getRules().get("no-invalid-this");
+
+export default ruleComposer.filterReports(noInvalidThisRule, problem => {
+ let inClassProperty = false;
+ let node = problem.node;
+
+ while (node) {
+ if (node.type === "ClassProperty" || node.type === "ClassPrivateProperty") {
+ inClassProperty = true;
+ return;
+ }
+
+ node = node.parent;
+ }
+
+ return !inClassProperty;
+});
diff --git a/eslint/babel-eslint-plugin/src/rules/no-unused-expressions.js b/eslint/babel-eslint-plugin/src/rules/no-unused-expressions.js
new file mode 100644
index 000000000000..a313b0743245
--- /dev/null
+++ b/eslint/babel-eslint-plugin/src/rules/no-unused-expressions.js
@@ -0,0 +1,64 @@
+import ruleComposer from "eslint-rule-composer";
+import eslint from "eslint";
+
+const rule = new eslint.Linter().getRules().get("no-unused-expressions");
+
+/**
+ * @param {ASTNode} node - any node
+ * @returns {boolean} whether the given node is either an IfStatement or an
+ * ExpressionStatement and is the last node in the body of a BlockStatement
+ */
+function isFinalStatementInBlockStatement(node) {
+ const parent = node.parent;
+ return (
+ /^(?:If|Expression)Statement$/.test(node.type) &&
+ parent.type === "BlockStatement" &&
+ parent.body[parent.body.length - 1] === node
+ );
+}
+
+/**
+ * @param {ASTNode} node - any node
+ * @returns {boolean} whether the given node represents an unbroken chain of
+ * tail ExpressionStatements and IfStatements within a DoExpression
+ * https://github.com/tc39/proposal-do-expressions
+ */
+function isInDoStatement(node) {
+ if (!node) return false;
+
+ if (node.type === "DoExpression") return true;
+
+ // this is an `else if`
+ if (
+ node.type === "IfStatement" &&
+ node.parent &&
+ node.parent.type === "IfStatement"
+ ) {
+ return isInDoStatement(node.parent);
+ }
+
+ if (isFinalStatementInBlockStatement(node)) {
+ return isInDoStatement(node.parent.parent);
+ }
+
+ return false;
+}
+
+/**
+ * @param {ASTNode} node - any node
+ * @returns {boolean} whether the given node is an optional call expression,
+ * https://github.com/tc39/proposal-optional-chaining
+ */
+function isOptionalCallExpression(node) {
+ return (
+ !!node &&
+ node.type === "ExpressionStatement" &&
+ node.expression.type === "OptionalCallExpression"
+ );
+}
+
+export default ruleComposer.filterReports(
+ rule,
+ problem =>
+ !isInDoStatement(problem.node) && !isOptionalCallExpression(problem.node),
+);
diff --git a/eslint/babel-eslint-plugin/src/rules/object-curly-spacing.js b/eslint/babel-eslint-plugin/src/rules/object-curly-spacing.js
new file mode 100644
index 000000000000..210f7a9fe5fa
--- /dev/null
+++ b/eslint/babel-eslint-plugin/src/rules/object-curly-spacing.js
@@ -0,0 +1,20 @@
+import ruleComposer from "eslint-rule-composer";
+import eslint from "eslint";
+
+const rule = new eslint.Linter().getRules().get("object-curly-spacing");
+
+export default ruleComposer.filterReports(rule, problem => {
+ const node = problem.node;
+
+ // Allow exportDefaultFrom syntax:
+ // export x from '...';
+ if (
+ node.type === "ExportNamedDeclaration" &&
+ node.specifiers.length === 1 &&
+ node.specifiers[0].type === "ExportDefaultSpecifier"
+ ) {
+ return false;
+ }
+
+ return true;
+});
diff --git a/eslint/babel-eslint-plugin/src/rules/semi.js b/eslint/babel-eslint-plugin/src/rules/semi.js
new file mode 100644
index 000000000000..f8cc783ac64a
--- /dev/null
+++ b/eslint/babel-eslint-plugin/src/rules/semi.js
@@ -0,0 +1,105 @@
+import ruleComposer from "eslint-rule-composer";
+import eslint from "eslint";
+
+const OPT_OUT_PATTERN = /^[-[(/+`]/; // One of [(/+-`
+
+const rule = new eslint.Linter().getRules().get("semi");
+
+function isSemicolon(token) {
+ return token.type === "Punctuator" && token.value === ";";
+}
+
+function isUnnecessarySemicolon(context, lastToken) {
+ if (!isSemicolon(lastToken)) {
+ return false;
+ }
+
+ const nextToken = context.getSourceCode().getTokenAfter(lastToken);
+
+ if (!nextToken) {
+ return true;
+ }
+
+ const lastTokenLine = lastToken.loc.end.line;
+ const nextTokenLine = nextToken.loc.start.line;
+ const isOptOutToken =
+ OPT_OUT_PATTERN.test(nextToken.value) &&
+ nextToken.value !== "++" &&
+ nextToken.value !== "--";
+ const isDivider = nextToken.value === "}" || nextToken.value === ";";
+
+ return (lastTokenLine !== nextTokenLine && !isOptOutToken) || isDivider;
+}
+
+function isOneLinerBlock(context, node) {
+ const nextToken = context.getSourceCode().getTokenAfter(node);
+
+ if (!nextToken || nextToken.value !== "}") {
+ return false;
+ }
+
+ const parent = node.parent;
+
+ return (
+ parent &&
+ parent.type === "BlockStatement" &&
+ parent.loc.start.line === parent.loc.end.line
+ );
+}
+
+function report(context, node, missing) {
+ const lastToken = context.getSourceCode().getLastToken(node);
+
+ let message,
+ fix,
+ loc = lastToken.loc;
+
+ if (!missing) {
+ message = "Missing semicolon.";
+ loc = loc.end;
+ fix = function(fixer) {
+ return fixer.insertTextAfter(lastToken, ";");
+ };
+ } else {
+ message = "Extra semicolon.";
+ loc = loc.start;
+ fix = function(fixer) {
+ return fixer.remove(lastToken);
+ };
+ }
+
+ context.report({
+ node,
+ loc,
+ message,
+ fix,
+ });
+}
+
+export default ruleComposer.joinReports([
+ rule,
+ context => ({
+ ClassProperty(node) {
+ const options = context.options[1];
+ const exceptOneLine = options && options.omitLastInOneLineBlock === true;
+ const sourceCode = context.getSourceCode();
+ const lastToken = sourceCode.getLastToken(node);
+
+ if (context.options[0] === "never") {
+ if (isUnnecessarySemicolon(context, lastToken)) {
+ report(context, node, true);
+ }
+ } else {
+ if (!isSemicolon(lastToken)) {
+ if (!exceptOneLine || !isOneLinerBlock(context, node)) {
+ report(context, node);
+ }
+ } else {
+ if (exceptOneLine && isOneLinerBlock(context, node)) {
+ report(context, node, true);
+ }
+ }
+ }
+ },
+ }),
+]);
diff --git a/eslint/babel-eslint-plugin/test/helpers/RuleTester.js b/eslint/babel-eslint-plugin/test/helpers/RuleTester.js
new file mode 100644
index 000000000000..47166bd51185
--- /dev/null
+++ b/eslint/babel-eslint-plugin/test/helpers/RuleTester.js
@@ -0,0 +1,14 @@
+import { RuleTester } from "eslint";
+
+RuleTester.setDefaultConfig({
+ parser: require.resolve("@babel/eslint-parser"),
+ parserOptions: {
+ babelOptions: {
+ configFile: require.resolve(
+ "@babel/eslint-shared-fixtures/config/babel.config.js",
+ ),
+ },
+ },
+});
+
+export default RuleTester;
diff --git a/eslint/babel-eslint-plugin/test/rules/new-cap.js b/eslint/babel-eslint-plugin/test/rules/new-cap.js
new file mode 100644
index 000000000000..d5e44984152e
--- /dev/null
+++ b/eslint/babel-eslint-plugin/test/rules/new-cap.js
@@ -0,0 +1,12 @@
+import rule from "../../src/rules/new-cap";
+import RuleTester from "../helpers/RuleTester";
+
+const ruleTester = new RuleTester();
+ruleTester.run("@babel/new-cap", rule, {
+ valid: [
+ {
+ code: "@MyDecorator(123) class MyClass{}",
+ },
+ ],
+ invalid: [],
+});
diff --git a/eslint/babel-eslint-plugin/test/rules/no-invalid-this.js b/eslint/babel-eslint-plugin/test/rules/no-invalid-this.js
new file mode 100644
index 000000000000..0f8b12a5159b
--- /dev/null
+++ b/eslint/babel-eslint-plugin/test/rules/no-invalid-this.js
@@ -0,0 +1,121 @@
+import cloneDeep from "lodash.clonedeep";
+import rule from "../../src/rules/no-invalid-this";
+import RuleTester from "../helpers/RuleTester";
+
+/**
+ * A constant value for non strict mode environment.
+ * @returns {void}
+ */
+function NORMAL(pattern) {
+ pattern.parserOptions.sourceType = "script";
+}
+
+/**
+ * A constant value for strict mode environment.
+ * This modifies pattern object to make strict mode.
+ * @param {Object} pattern - A pattern object to modify.
+ * @returns {void}
+ */
+function USE_STRICT(pattern) {
+ pattern.code = '"use strict"; ' + pattern.code;
+}
+
+/**
+ * A constant value for implied strict mode.
+ * This modifies pattern object to impose strict mode.
+ * @param {Object} pattern - A pattern object to modify.
+ * @returns {void}
+ */
+function IMPLIED_STRICT(pattern) {
+ pattern.code = "/* implied strict mode */ " + pattern.code;
+ pattern.parserOptions.ecmaFeatures = pattern.parserOptions.ecmaFeatures || {};
+ pattern.parserOptions.ecmaFeatures.impliedStrict = true;
+}
+
+/**
+ * A constant value for modules environment.
+ * This modifies pattern object to make modules.
+ * @param {Object} pattern - A pattern object to modify.
+ * @returns {void}
+ */
+function MODULES(pattern) {
+ pattern.code = "/* modules */ " + pattern.code;
+}
+
+/**
+ * Extracts patterns each condition for a specified type. The type is `valid` or `invalid`.
+ * @param {Object[]} patterns - Original patterns.
+ * @param {string} type - One of `"valid"` or `"invalid"`.
+ * @returns {Object[]} Test patterns.
+ */
+function extractPatterns(patterns, type) {
+ // Clone and apply the pattern environment.
+ const patternsList = patterns.map(function(pattern) {
+ return pattern[type].map(function(applyCondition) {
+ const thisPattern = cloneDeep(pattern);
+
+ applyCondition(thisPattern);
+
+ if (type === "valid") {
+ thisPattern.errors = [];
+ } else {
+ thisPattern.code += " /* should error */";
+ }
+
+ delete thisPattern.invalid;
+ delete thisPattern.valid;
+
+ return thisPattern;
+ });
+ });
+
+ // Flatten.
+ return Array.prototype.concat.apply([], patternsList);
+}
+
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
+const errors = [
+ { message: "Unexpected 'this'.", type: "ThisExpression" },
+ { message: "Unexpected 'this'.", type: "ThisExpression" },
+];
+
+const patterns = [
+ // Class private fields
+ {
+ code: "class A {a = this.b;};",
+ parserOptions: { ecmaVersion: 6 },
+ valid: [NORMAL, USE_STRICT, IMPLIED_STRICT, MODULES],
+ invalid: [],
+ },
+
+ {
+ code: "class A {a = () => {return this.b;};};",
+ parserOptions: { ecmaVersion: 6 },
+ valid: [NORMAL, USE_STRICT, IMPLIED_STRICT, MODULES],
+ invalid: [],
+ },
+
+ // Class Private methods
+ {
+ code: "class A {#a = this.b;};",
+ parserOptions: { ecmaVersion: 6 },
+ valid: [NORMAL, USE_STRICT, IMPLIED_STRICT, MODULES],
+ invalid: [],
+ },
+
+ {
+ code: "class A {#a = () => {return this.b;};};",
+ parserOptions: { ecmaVersion: 6 },
+ valid: [NORMAL, USE_STRICT, IMPLIED_STRICT, MODULES],
+ invalid: [],
+ },
+];
+
+const ruleTester = new RuleTester();
+ruleTester.run("@babel/no-invalid-this", rule, {
+ valid: extractPatterns(patterns, "valid"),
+ invalid: extractPatterns(patterns, "invalid"),
+});
diff --git a/eslint/babel-eslint-plugin/test/rules/no-unused-expressions.js b/eslint/babel-eslint-plugin/test/rules/no-unused-expressions.js
new file mode 100644
index 000000000000..991bf6fa80b4
--- /dev/null
+++ b/eslint/babel-eslint-plugin/test/rules/no-unused-expressions.js
@@ -0,0 +1,37 @@
+import rule from "../../src/rules/no-unused-expressions";
+import RuleTester from "../helpers/RuleTester";
+
+const ruleTester = new RuleTester();
+ruleTester.run("@babel/no-unused-expressions", rule, {
+ valid: [
+ "let a = do { if (foo) { foo.bar; } }",
+ "let a = do { foo; }",
+ "let a = do { let b = 2; foo; }",
+ "let a = do { (foo + 1); }",
+ "let a = do { if (foo) { if (foo.bar) { foo.bar; } } }",
+ "let a = do { if (foo) { if (foo.bar) { foo.bar; } else if (foo.baz) { foo.baz; } } }",
+ "foo.bar?.();",
+ ],
+ invalid: [
+ {
+ code: "let a = do { foo; let b = 2; }",
+ errors: [
+ {
+ message:
+ "Expected an assignment or function call and instead saw an expression.",
+ type: "ExpressionStatement",
+ },
+ ],
+ },
+ {
+ code: "let a = do { if (foo) { foo.bar } else { a; bar.foo } }",
+ errors: [
+ {
+ message:
+ "Expected an assignment or function call and instead saw an expression.",
+ type: "ExpressionStatement",
+ },
+ ],
+ },
+ ],
+});
diff --git a/eslint/babel-eslint-plugin/test/rules/object-curly-spacing.js b/eslint/babel-eslint-plugin/test/rules/object-curly-spacing.js
new file mode 100644
index 000000000000..8a08a09516c7
--- /dev/null
+++ b/eslint/babel-eslint-plugin/test/rules/object-curly-spacing.js
@@ -0,0 +1,13 @@
+import rule from "../../src/rules/object-curly-spacing";
+import RuleTester from "../helpers/RuleTester";
+
+const ruleTester = new RuleTester();
+ruleTester.run("@babel/object-curly-spacing", rule, {
+ valid: [
+ {
+ code: 'export x from "mod";',
+ },
+ ],
+
+ invalid: [],
+});
diff --git a/eslint/babel-eslint-plugin/test/rules/semi.js b/eslint/babel-eslint-plugin/test/rules/semi.js
new file mode 100644
index 000000000000..d7a757b62616
--- /dev/null
+++ b/eslint/babel-eslint-plugin/test/rules/semi.js
@@ -0,0 +1,52 @@
+import rule from "../../src/rules/semi";
+import RuleTester from "../helpers/RuleTester";
+
+const ruleTester = new RuleTester();
+
+ruleTester.run("semi", rule, {
+ valid: [
+ "class Foo { bar = 'example'; }",
+ "class Foo { static bar = 'example'; }",
+ {
+ code: "class Foo { bar = () => {}; }",
+ options: ["always", { omitLastInOneLineBlock: true }],
+ },
+
+ // never
+ { code: "class Foo { bar = 'example' }", options: ["never"] },
+ { code: "class Foo { static bar = 'example' }", options: ["never"] },
+ { code: "class Foo { bar = () => {} }", options: ["never"] },
+ ],
+ invalid: [
+ {
+ code: "class Foo { bar = 'example' }",
+ errors: [{ message: "Missing semicolon." }],
+ },
+ {
+ code: "class Foo { static bar = 'example' }",
+ errors: [{ message: "Missing semicolon." }],
+ },
+ {
+ code: "class Foo { bar = () => {} }",
+ options: ["always", { omitLastInOneLineBlock: true }],
+ errors: [{ message: "Missing semicolon." }],
+ },
+
+ // "never"
+ {
+ code: "class Foo { bar = 'example'; }",
+ options: ["never"],
+ errors: [{ message: "Extra semicolon." }],
+ },
+ {
+ code: "class Foo { static bar = 'example'; }",
+ options: ["never"],
+ errors: [{ message: "Extra semicolon." }],
+ },
+ {
+ code: "class Foo { bar = () => {}; }",
+ options: ["never"],
+ errors: [{ message: "Extra semicolon." }],
+ },
+ ],
+});
diff --git a/eslint/babel-eslint-shared-fixtures/config/babel.config.decorators-legacy.js b/eslint/babel-eslint-shared-fixtures/config/babel.config.decorators-legacy.js
new file mode 100644
index 000000000000..dbad846412e6
--- /dev/null
+++ b/eslint/babel-eslint-shared-fixtures/config/babel.config.decorators-legacy.js
@@ -0,0 +1,6 @@
+"use strict";
+
+module.exports = {
+ presets: [["@babel/preset-env", { forceAllTransforms: true }]],
+ plugins: [["@babel/plugin-proposal-decorators", { legacy: true }]],
+};
diff --git a/eslint/babel-eslint-shared-fixtures/config/babel.config.js b/eslint/babel-eslint-shared-fixtures/config/babel.config.js
new file mode 100644
index 000000000000..202b888fe7b1
--- /dev/null
+++ b/eslint/babel-eslint-shared-fixtures/config/babel.config.js
@@ -0,0 +1,24 @@
+"use strict";
+
+module.exports = {
+ presets: [
+ ["@babel/preset-env", { forceAllTransforms: true }],
+ ["@babel/preset-flow", { all: true }],
+ "@babel/preset-react",
+ ],
+ plugins: [
+ "@babel/plugin-syntax-dynamic-import",
+ "@babel/plugin-syntax-import-meta",
+ "@babel/plugin-syntax-export-default-from",
+ "@babel/plugin-proposal-class-properties",
+ "@babel/plugin-proposal-nullish-coalescing-operator",
+ "@babel/plugin-proposal-optional-chaining",
+ "@babel/plugin-syntax-numeric-separator",
+ "@babel/plugin-syntax-export-namespace-from",
+ ["@babel/plugin-proposal-decorators", { decoratorsBeforeExport: false }],
+ ["@babel/plugin-proposal-pipeline-operator", { proposal: "minimal" }],
+ "@babel/plugin-syntax-bigint",
+ "@babel/plugin-proposal-private-methods",
+ "@babel/plugin-proposal-do-expressions",
+ ],
+};
diff --git a/eslint/babel-eslint-shared-fixtures/package.json b/eslint/babel-eslint-shared-fixtures/package.json
new file mode 100644
index 000000000000..96bbb96dde67
--- /dev/null
+++ b/eslint/babel-eslint-shared-fixtures/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "@babel/eslint-shared-fixtures",
+ "version": "0.0.0",
+ "description": "Shared fixtures for testing @babel/eslint-* packages",
+ "license": "MIT",
+ "private": true,
+ "dependencies": {
+ "@babel/plugin-proposal-class-properties": "^7.1.0",
+ "@babel/plugin-proposal-decorators": "^7.1.2",
+ "@babel/plugin-proposal-do-expressions": "^7.7.4",
+ "@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0",
+ "@babel/plugin-proposal-optional-chaining": "^7.0.0",
+ "@babel/plugin-proposal-pipeline-operator": "^7.0.0",
+ "@babel/plugin-proposal-private-methods": "^7.7.4",
+ "@babel/plugin-syntax-bigint": "^7.7.4",
+ "@babel/plugin-syntax-dynamic-import": "^7.0.0",
+ "@babel/plugin-syntax-export-default-from": "^7.0.0",
+ "@babel/plugin-syntax-export-namespace-from": "^7.0.0",
+ "@babel/plugin-syntax-import-meta": "^7.0.0",
+ "@babel/plugin-syntax-numeric-separator": "^7.0.0",
+ "@babel/preset-env": "^7.1.5",
+ "@babel/preset-flow": "^7.0.0",
+ "@babel/preset-react": "^7.0.0"
+ }
+}
diff --git a/jest.config.js b/jest.config.js
new file mode 100644
index 000000000000..667ea5395c29
--- /dev/null
+++ b/jest.config.js
@@ -0,0 +1,68 @@
+// These packages undet the @babel namespace aren't in this monorepo.
+const externalBabelPackages = [
+ "plugin-syntax-async-generators",
+ "plugin-syntax-bigint",
+ "plugin-syntax-dynamic-import",
+ "plugin-syntax-json-strings",
+ "plugin-syntax-nullish-coalescing-operator",
+ "plugin-syntax-object-rest-spread",
+ "plugin-syntax-optional-catch-binding",
+ "plugin-syntax-optional-chaining",
+];
+
+// prettier-ignore
+const monorepoPackagePattern =
+ `^@babel/(?!eslint-)(?!${externalBabelPackages.join("|")})([a-zA-Z0-9_-]+)$`;
+
+module.exports = {
+ collectCoverageFrom: [
+ "packages/*/src/**/*.mjs",
+ "packages/*/src/**/*.js",
+ "codemods/*/src/**/*.mjs",
+ "codemods/*/src/**/*.js",
+ "eslint/*/src/**/*.mjs",
+ "eslint/*/src/**/*.js",
+ ],
+ // The eslint/* packages use ESLint v6, which has dropped support for Node v6.
+ // TODO: Remove this process.version check in Babel 8.
+ testRegex: `./(packages|codemods${
+ /^v6./u.test(process.version) ? "" : "|eslint"
+ })/[^/]+/test/.+\\.m?js$`,
+ testPathIgnorePatterns: [
+ "/node_modules/",
+ "/test/fixtures/",
+ "/test/debug-fixtures/",
+ "/babel-parser/test/expressions/",
+ "/test/tmp/",
+ "/test/__data__/",
+ "/test/helpers/",
+ "/test/warning\\.js",
+ "/build/",
+ "_browser\\.js",
+ ],
+ testEnvironment: "node",
+ setupFilesAfterEnv: ["/test/testSetupFile.js"],
+ transformIgnorePatterns: [
+ "/node_modules/",
+ "/packages/babel-standalone/babel(\\.min)?\\.js",
+ "/packages/babel-preset-env-standalone/babel-preset-env(\\.min)?\\.js",
+ "/test/(fixtures|tmp|__data__)/",
+ "/(packages|codemods|eslint)/[^/]+/lib/",
+ ],
+ coveragePathIgnorePatterns: [
+ "/node_modules/",
+ "/packages/babel-standalone/babel(\\.min)?\\.js",
+ "/packages/babel-preset-env-standalone/babel-preset-env(\\.min)?\\.js",
+ "/test/(fixtures|tmp|__data__)/",
+ ],
+ modulePathIgnorePatterns: [
+ "/test/fixtures/",
+ "/test/tmp/",
+ "/test/__data__/",
+ "/build/",
+ ],
+ moduleNameMapper: {
+ [monorepoPackagePattern]: "/packages/babel-$1/",
+ "^@babel/eslint-([a-zA-Z0-9_-]+)$": "/eslint/babel-eslint-$1/",
+ },
+};
diff --git a/lerna.json b/lerna.json
index 10d091d9337f..b4692f70a257 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,5 +1,5 @@
{
- "version": "7.5.5",
+ "version": "7.8.3",
"changelog": {
"repo": "babel/babel",
"cacheDir": ".changelog",
@@ -11,7 +11,8 @@
"PR: Polish :nail_care:": ":nail_care: Polish",
"PR: Docs :memo:": ":memo: Documentation",
"PR: Internal :house:": ":house: Internal",
- "PR: Performance :running_woman:": ":running_woman: Performance"
+ "PR: Performance :running_woman:": ":running_woman: Performance",
+ "PR: Revert :leftwards_arrow_with_hook:": ":leftwards_arrow_with_hook: Revert"
}
},
"command": {
@@ -20,14 +21,18 @@
"*.md",
"*.txt",
"test/**",
+ "**/test/**",
"codemods/**",
"# We ignore every JSON file, except for built-in-modules, built-ins and plugins defined in babel-preset-env/data.",
- "@(!(built-in-modules|built-ins|plugins|package)).json"
+ "@(!(built-in-modules|built-ins|plugins|package)).json",
+ "# Until the ESLint packages version are aligned with Babel's, we ignore them",
+ "eslint/**"
]
}
},
"packages": [
"codemods/*",
+ "eslint/*",
"packages/*"
],
"npmClient": "yarn",
diff --git a/lib/third-party-libs.js.flow b/lib/third-party-libs.js.flow
index 884b1e68bbfb..b41fbe725b4e 100644
--- a/lib/third-party-libs.js.flow
+++ b/lib/third-party-libs.js.flow
@@ -4,6 +4,7 @@
declare module "resolve" {
declare export default {
+ (string, {| basedir: string |}, (err: ?Error, res: string) => void): void;
sync: (string, {| basedir: string |}) => string;
};
}
@@ -178,10 +179,10 @@ declare module "convert-source-map" {
SourceMap: SourceMap,
Converter: Converter,
fromObject(obj: SourceMap | SourceMapGenerator): Converter,
- fromJSON(str: string): Converter,
+ fromJSON(str: string | Buffer): Converter,
fromBase64(str: string): Converter,
fromComment(str: string): Converter,
- fromMapFileComment(str: string): Converter,
+ fromMapFileComment(str: string, dir: string): Converter,
fromSource(str: string): Converter,
fromMapFileSource(str: string, dir: string): Converter,
removeComments(str: string): string,
@@ -190,12 +191,6 @@ declare module "convert-source-map" {
};
}
-declare module "js-levenshtein" {
- declare module.exports: {
- (string, string): number,
- };
-}
-
declare module "core-js-compat/data" {
declare type Target = "node" | "chrome" | "opera" | "edge" | "firefox" | "safari" | "ie" | "ios" | "android" | "electron" | "samsung";
diff --git a/package.json b/package.json
index bd04e0638455..235975a0ac67 100644
--- a/package.json
+++ b/package.json
@@ -3,42 +3,49 @@
"private": true,
"license": "MIT",
"scripts": {
+ "bootstrap": "make bootstrap",
"build": "make build",
"fix": "make fix",
"lint": "make lint",
"test": "make test"
},
"devDependencies": {
- "@babel/cli": "^7.4.4",
- "@babel/core": "^7.4.5",
+ "@babel/cli": "^7.7.0",
+ "@babel/core": "^7.7.2",
"@babel/eslint-plugin-development": "^1.0.1",
- "@babel/plugin-proposal-class-properties": "^7.4.4",
- "@babel/plugin-proposal-export-namespace-from": "^7.2.0",
+ "@babel/plugin-proposal-class-properties": "^7.7.0",
+ "@babel/plugin-proposal-export-namespace-from": "^7.5.2",
+ "@babel/plugin-proposal-nullish-coalescing-operator": "^7.4.4",
"@babel/plugin-proposal-numeric-separator": "^7.2.0",
- "@babel/plugin-transform-modules-commonjs": "^7.4.4",
- "@babel/plugin-transform-runtime": "^7.4.4",
- "@babel/preset-env": "^7.4.5",
+ "@babel/plugin-proposal-object-rest-spread": "^7.7.4",
+ "@babel/plugin-proposal-optional-chaining": "^7.6.0",
+ "@babel/plugin-transform-flow-strip-types": "^7.7.4",
+ "@babel/plugin-transform-for-of": "^7.7.4",
+ "@babel/plugin-transform-modules-commonjs": "^7.7.0",
+ "@babel/plugin-transform-runtime": "^7.6.2",
+ "@babel/preset-env": "^7.7.1",
"@babel/preset-flow": "^7.0.0",
- "@babel/register": "^7.4.4",
- "@babel/runtime": "^7.4.5",
- "babel-eslint": "^11.0.0-beta.0",
- "babel-jest": "^24.8.0",
- "babel-loader": "^8.0.6",
+ "@babel/register": "^7.7.0",
+ "@babel/runtime": "^7.7.2",
+ "@rollup/plugin-alias": "^2.2.0",
+ "@rollup/plugin-json": "^4.0.0",
+ "babel-eslint": "^11.0.0-beta.2",
+ "babel-jest": "^24.9.0",
"babel-plugin-transform-charcodes": "^0.2.0",
"browserify": "^16.2.3",
"bundle-collapser": "^1.2.1",
"chalk": "^2.4.2",
"charcodes": "^0.2.0",
"derequire": "^2.0.2",
- "duplicate-package-checker-webpack-plugin": "^2.1.0",
"enhanced-resolve": "^3.0.0",
- "eslint": "^5.16.0",
+ "eslint": "^6.0.1",
"eslint-config-babel": "^9.0.0",
+ "eslint-import-resolver-node": "^0.3.2",
"eslint-plugin-flowtype": "^3.8.2",
"eslint-plugin-import": "^2.17.2",
"eslint-plugin-prettier": "^3.1.0",
"fancy-log": "^1.3.3",
- "flow-bin": "^0.94.0",
+ "flow-bin": "^0.108.0",
"graceful-fs": "^4.1.15",
"gulp": "^4.0.2",
"gulp-babel": "^8.0.0",
@@ -46,35 +53,35 @@
"gulp-newer": "^1.0.0",
"gulp-plumber": "^1.2.1",
"gulp-rename": "^1.4.0",
- "gulp-uglify": "^3.0.2",
"gulp-watch": "^5.0.1",
- "husky": "^2.3.0",
- "jest": "^24.8.0",
- "lerna": "^3.14.2",
+ "husky": "^3.0.0",
+ "jest": "^24.9.0",
+ "lerna": "^3.19.0",
"lerna-changelog": "^0.5.0",
- "lint-staged": "^8.1.7",
+ "lint-staged": "^9.2.0",
"lodash": "^4.17.13",
- "merge-stream": "^1.0.1",
+ "mergeiterator": "^1.2.5",
"output-file-sync": "^2.0.0",
- "prettier": "^1.17.1",
+ "prettier": "^1.19.1",
"pump": "^3.0.0",
"rimraf": "^2.6.3",
- "rollup": "^1.12.0",
+ "rollup": "^1.27.5",
"rollup-plugin-babel": "^4.0.0",
+ "rollup-plugin-commonjs": "^10.1.0",
+ "rollup-plugin-node-builtins": "^2.1.2",
+ "rollup-plugin-node-globals": "^1.4.0",
"rollup-plugin-node-resolve": "^5.0.0",
"rollup-plugin-replace": "^2.2.0",
- "test262-stream": "^1.2.0",
+ "rollup-plugin-terser": "^5.1.2",
+ "test262-stream": "^1.3.0",
"through2": "^2.0.0",
- "warnings-to-errors-webpack-plugin": "^2.0.0",
- "webpack": "^3.4.1",
- "webpack-dependency-suite": "^2.4.4",
- "webpack-stream": "^4.0.0"
+ "typescript": "^3.6.3"
},
"resolutions": {
- "@lerna/**/@lerna/collect-updates": "https://github.com/babel/lerna.git#babel-collect-updates"
+ "@lerna/collect-updates": "https://github.com/babel/lerna.git#babel-collect-updates"
},
"engines": {
- "node": ">= 6.9.0 < 13.0.0",
+ "node": ">= 6.9.0 < 14.0.0",
"npm": ">= 3.x <= 6.x",
"yarn": ">=0.27.5 || >=1.0.0-20170811"
},
@@ -87,53 +94,5 @@
"hooks": {
"pre-commit": "lint-staged"
}
- },
- "jest": {
- "collectCoverageFrom": [
- "packages/*/src/**/*.mjs",
- "packages/*/src/**/*.js",
- "codemods/*/src/**/*.mjs",
- "codemods/*/src/**/*.js"
- ],
- "testRegex": "./(packages|codemods)/[^/]+/test/.+\\.m?js$",
- "testPathIgnorePatterns": [
- "/node_modules/",
- "/test/fixtures/",
- "/test/debug-fixtures/",
- "/babel-parser/test/expressions/",
- "/test/tmp/",
- "/test/__data__/",
- "/test/helpers/",
- "/test/warning\\.js",
- "/build/",
- "_browser\\.js"
- ],
- "testEnvironment": "node",
- "setupFilesAfterEnv": [
- "/test/testSetupFile.js"
- ],
- "transformIgnorePatterns": [
- "/node_modules/",
- "/packages/babel-standalone/babel(\\.min)?\\.js",
- "/packages/babel-preset-env-standalone/babel-preset-env(\\.min)?\\.js",
- "/test/(fixtures|tmp|__data__)/",
- "/(packages|codemods)/[^/]+/lib/"
- ],
- "coveragePathIgnorePatterns": [
- "/node_modules/",
- "/packages/babel-standalone/babel(\\.min)?\\.js",
- "/packages/babel-preset-env-standalone/babel-preset-env(\\.min)?\\.js",
- "/test/(fixtures|tmp|__data__)/"
- ],
- "modulePathIgnorePatterns": [
- "/test/fixtures/",
- "/test/tmp/",
- "/test/__data__/",
- "/build/"
- ],
- "moduleNameMapper": {
- "^@babel/([a-zA-Z0-9_-]+)$": "/packages/babel-$1/"
- }
- },
- "dependencies": {}
+ }
}
diff --git a/packages/babel-cli/package.json b/packages/babel-cli/package.json
index 0b4bd8731363..371e106e9726 100644
--- a/packages/babel-cli/package.json
+++ b/packages/babel-cli/package.json
@@ -1,6 +1,6 @@
{
"name": "@babel/cli",
- "version": "7.5.5",
+ "version": "7.8.3",
"description": "Babel command line.",
"author": "Sebastian McKenzie ",
"homepage": "https://babeljs.io/",
@@ -19,25 +19,25 @@
"compiler"
],
"dependencies": {
- "commander": "^2.8.1",
+ "commander": "^4.0.1",
"convert-source-map": "^1.1.0",
"fs-readdir-recursive": "^1.1.0",
"glob": "^7.0.0",
"lodash": "^4.17.13",
- "mkdirp": "^0.5.1",
- "output-file-sync": "^2.0.0",
+ "make-dir": "^2.1.0",
"slash": "^2.0.0",
"source-map": "^0.5.0"
},
"optionalDependencies": {
- "chokidar": "^2.0.4"
+ "chokidar": "^2.1.8"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
},
"devDependencies": {
- "@babel/core": "^7.5.5",
- "@babel/helper-fixtures": "^7.5.5"
+ "@babel/core": "^7.8.3",
+ "@babel/helper-fixtures": "^7.8.3",
+ "rimraf": "^3.0.0"
},
"bin": {
"babel": "./bin/babel.js",
diff --git a/packages/babel-cli/src/babel/dir.js b/packages/babel-cli/src/babel/dir.js
index 32136b78f02a..9b199b98a4e2 100644
--- a/packages/babel-cli/src/babel/dir.js
+++ b/packages/babel-cli/src/babel/dir.js
@@ -1,8 +1,7 @@
// @flow
import defaults from "lodash/defaults";
-import outputFileSync from "output-file-sync";
-import { sync as mkdirpSync } from "mkdirp";
+import { sync as makeDirSync } from "make-dir";
import slash from "slash";
import path from "path";
import fs from "fs";
@@ -10,21 +9,40 @@ import fs from "fs";
import * as util from "./util";
import { type CmdOptions } from "./options";
+const FILE_TYPE = Object.freeze({
+ NON_COMPILABLE: "NON_COMPILABLE",
+ COMPILED: "COMPILED",
+ IGNORED: "IGNORED",
+ ERR_COMPILATION: "ERR_COMPILATION",
+});
+
+function outputFileSync(filePath: string, data: string | Buffer): void {
+ makeDirSync(path.dirname(filePath));
+ fs.writeFileSync(filePath, data);
+}
+
export default async function({
cliOptions,
babelOptions,
}: CmdOptions): Promise {
const filenames = cliOptions.filenames;
- async function write(src: string, base: string): Promise {
+ async function write(
+ src: string,
+ base: string,
+ ): Promise<$Keys> {
let relative = path.relative(base, src);
if (!util.isCompilableExtension(relative, cliOptions.extensions)) {
- return false;
+ return FILE_TYPE.NON_COMPILABLE;
}
- // remove extension and then append back on .js
- relative = util.adjustRelative(relative, cliOptions.keepFileExtension);
+ relative = util.withExtension(
+ relative,
+ cliOptions.keepFileExtension
+ ? path.extname(relative)
+ : cliOptions.outFileExtension,
+ );
const dest = getDest(relative, base);
@@ -39,7 +57,7 @@ export default async function({
),
);
- if (!res) return false;
+ if (!res) return FILE_TYPE.IGNORED;
// we've requested explicit sourcemaps to be written to disk
if (
@@ -60,11 +78,11 @@ export default async function({
console.log(src + " -> " + dest);
}
- return true;
+ return FILE_TYPE.COMPILED;
} catch (err) {
if (cliOptions.watch) {
console.error(err);
- return false;
+ return FILE_TYPE.ERR_COMPILATION;
}
throw err;
@@ -81,13 +99,16 @@ export default async function({
async function handleFile(src: string, base: string): Promise {
const written = await write(src, base);
- if (!written && cliOptions.copyFiles) {
+ if (
+ (cliOptions.copyFiles && written === FILE_TYPE.NON_COMPILABLE) ||
+ (cliOptions.copyIgnored && written === FILE_TYPE.IGNORED)
+ ) {
const filename = path.relative(base, src);
const dest = getDest(filename, base);
outputFileSync(dest, fs.readFileSync(src));
util.chmod(src, dest);
}
- return written;
+ return written === FILE_TYPE.COMPILED;
}
async function handle(filenameOrDir: string): Promise {
@@ -122,24 +143,26 @@ export default async function({
util.deleteDir(cliOptions.outDir);
}
- mkdirpSync(cliOptions.outDir);
+ makeDirSync(cliOptions.outDir);
let compiledFiles = 0;
for (const filename of cliOptions.filenames) {
compiledFiles += await handle(filename);
}
- console.log(
- `Successfully compiled ${compiledFiles} ${
- compiledFiles !== 1 ? "files" : "file"
- } with Babel.`,
- );
+ if (!cliOptions.quiet) {
+ console.log(
+ `Successfully compiled ${compiledFiles} ${
+ compiledFiles !== 1 ? "files" : "file"
+ } with Babel.`,
+ );
+ }
}
if (cliOptions.watch) {
const chokidar = util.requireChokidar();
- filenames.forEach(function(filenameOrDir) {
+ filenames.forEach(function(filenameOrDir: string): void {
const watcher = chokidar.watch(filenameOrDir, {
persistent: true,
ignoreInitial: true,
@@ -149,8 +172,8 @@ export default async function({
},
});
- ["add", "change"].forEach(function(type) {
- watcher.on(type, function(filename) {
+ ["add", "change"].forEach(function(type: string): void {
+ watcher.on(type, function(filename: string): void {
handleFile(
filename,
filename === filenameOrDir
diff --git a/packages/babel-cli/src/babel/file.js b/packages/babel-cli/src/babel/file.js
index 14cf4c562971..c1c60ff91615 100644
--- a/packages/babel-cli/src/babel/file.js
+++ b/packages/babel-cli/src/babel/file.js
@@ -4,6 +4,7 @@ import convertSourceMap from "convert-source-map";
import defaults from "lodash/defaults";
import sourceMap from "source-map";
import slash from "slash";
+import { sync as makeDirSync } from "make-dir";
import path from "path";
import fs from "fs";
@@ -89,6 +90,8 @@ export default async function({
const result = buildResult(fileResults);
if (cliOptions.outFile) {
+ makeDirSync(path.dirname(cliOptions.outFile));
+
// we've requested for a sourcemap to be written to disk
if (babelOptions.sourceMaps && babelOptions.sourceMaps !== "inline") {
const mapLoc = cliOptions.outFile + ".map";
@@ -103,24 +106,22 @@ export default async function({
}
function readStdin(): Promise {
- return new Promise(
- (resolve: Function, reject: Function): void => {
- let code = "";
+ return new Promise((resolve: Function, reject: Function): void => {
+ let code = "";
- process.stdin.setEncoding("utf8");
+ process.stdin.setEncoding("utf8");
- process.stdin.on("readable", function() {
- const chunk = process.stdin.read();
- // $FlowIgnore
- if (chunk !== null) code += chunk;
- });
+ process.stdin.on("readable", function() {
+ const chunk = process.stdin.read();
+ // $FlowIgnore
+ if (chunk !== null) code += chunk;
+ });
- process.stdin.on("end", function() {
- resolve(code);
- });
- process.stdin.on("error", reject);
- },
- );
+ process.stdin.on("end", function() {
+ resolve(code);
+ });
+ process.stdin.on("error", reject);
+ });
}
async function stdin(): Promise {
@@ -215,6 +216,7 @@ export default async function({
const chokidar = util.requireChokidar();
chokidar
.watch(filenames, {
+ disableGlobbing: true,
persistent: true,
ignoreInitial: true,
awaitWriteFinish: {
@@ -222,8 +224,11 @@ export default async function({
pollInterval: 10,
},
})
- .on("all", function(type: string, filename: string) {
- if (!util.isCompilableExtension(filename, cliOptions.extensions)) {
+ .on("all", function(type: string, filename: string): void {
+ if (
+ !util.isCompilableExtension(filename, cliOptions.extensions) &&
+ !filenames.includes(filename)
+ ) {
return;
}
diff --git a/packages/babel-cli/src/babel/index.js b/packages/babel-cli/src/babel/index.js
index f3194576072a..a740bcdedadf 100755
--- a/packages/babel-cli/src/babel/index.js
+++ b/packages/babel-cli/src/babel/index.js
@@ -6,8 +6,12 @@ import fileCommand from "./file";
const opts = parseArgv(process.argv);
-const fn = opts.cliOptions.outDir ? dirCommand : fileCommand;
-fn(opts).catch(err => {
- console.error(err);
- process.exit(1);
-});
+if (opts) {
+ const fn = opts.cliOptions.outDir ? dirCommand : fileCommand;
+ fn(opts).catch(err => {
+ console.error(err);
+ process.exitCode = 1;
+ });
+} else {
+ process.exitCode = 2;
+}
diff --git a/packages/babel-cli/src/babel/options.js b/packages/babel-cli/src/babel/options.js
index 19ae7bfce878..6e4dfeaed2d4 100644
--- a/packages/babel-cli/src/babel/options.js
+++ b/packages/babel-cli/src/babel/options.js
@@ -12,19 +12,19 @@ import pkg from "../../package.json";
// Standard Babel input configs.
commander.option(
"-f, --filename [filename]",
- "filename to use when reading from stdin - this will be used in source-maps, errors etc",
+ "The filename to use when reading from stdin. This will be used in source-maps, errors etc.",
);
commander.option(
"--presets [list]",
- "comma-separated list of preset names",
+ "A comma-separated list of preset names.",
collect,
);
commander.option(
"--plugins [list]",
- "comma-separated list of plugin names",
+ "A comma-separated list of plugin names.",
collect,
);
-commander.option("--config-file [path]", "Path to a .babelrc file to use");
+commander.option("--config-file [path]", "Path to a .babelrc file to use.");
commander.option(
"--env-name [name]",
"The name of the 'env' to use when loading configs and plugins. " +
@@ -40,97 +40,101 @@ commander.option(
commander.option("--source-type [script|module]", "");
commander.option(
"--no-babelrc",
- "Whether or not to look up .babelrc and .babelignore files",
+ "Whether or not to look up .babelrc and .babelignore files.",
);
commander.option(
"--ignore [list]",
- "list of glob paths to **not** compile",
+ "List of glob paths to **not** compile.",
collect,
);
commander.option(
"--only [list]",
- "list of glob paths to **only** compile",
+ "List of glob paths to **only** compile.",
collect,
);
// Misc babel config.
commander.option(
"--no-highlight-code",
- "enable/disable ANSI syntax highlighting of code frames (on by default)",
+ "Enable or disable ANSI syntax highlighting of code frames. (on by default)",
);
// General output formatting.
commander.option(
"--no-comments",
- "write comments to generated output (true by default)",
+ "Write comments to generated output. (true by default)",
);
commander.option(
"--retain-lines",
- "retain line numbers - will result in really ugly code",
+ "Retain line numbers. This will result in really ugly code.",
);
commander.option(
"--compact [true|false|auto]",
- "do not include superfluous whitespace characters and line terminators",
+ "Do not include superfluous whitespace characters and line terminators.",
booleanify,
);
-commander.option("--minified", "save as much bytes when printing [true|false]");
+commander.option(
+ "--minified",
+ "Save as many bytes when printing. (false by default)",
+);
commander.option(
"--auxiliary-comment-before [string]",
- "print a comment before any injected non-user code",
+ "Print a comment before any injected non-user code.",
);
commander.option(
"--auxiliary-comment-after [string]",
- "print a comment after any injected non-user code",
+ "Print a comment after any injected non-user code.",
);
// General source map formatting.
commander.option("-s, --source-maps [true|false|inline|both]", "", booleanify);
commander.option(
"--source-map-target [string]",
- "set `file` on returned source map",
+ "Set `file` on returned source map.",
);
commander.option(
"--source-file-name [string]",
- "set `sources[0]` on returned source map",
+ "Set `sources[0]` on returned source map.",
);
commander.option(
"--source-root [filename]",
- "the root from which all sources are relative",
+ "The root from which all sources are relative.",
);
// Config params for certain module output formats.
commander.option(
"--module-root [filename]",
- "optional prefix for the AMD module formatter that will be prepend to the filename on module definitions",
+ // eslint-disable-next-line max-len
+ "Optional prefix for the AMD module formatter that will be prepended to the filename on module definitions.",
);
-commander.option("-M, --module-ids", "insert an explicit id for modules");
+commander.option("-M, --module-ids", "Insert an explicit id for modules.");
commander.option(
"--module-id [string]",
- "specify a custom name for module ids",
+ "Specify a custom name for module ids.",
);
// "babel" command specific arguments that are not passed to @babel/core.
commander.option(
"-x, --extensions [extensions]",
- "List of extensions to compile when a directory has been input [.es6,.js,.es,.jsx,.mjs]",
+ "List of extensions to compile when a directory has been the input. [.es6,.js,.es,.jsx,.mjs]",
collect,
);
commander.option(
"--keep-file-extension",
- "Preserve the file extensions of the input files",
+ "Preserve the file extensions of the input files.",
);
-commander.option("-w, --watch", "Recompile files on changes");
+commander.option("-w, --watch", "Recompile files on changes.");
commander.option(
"--skip-initial-build",
- "Do not compile files before watching",
+ "Do not compile files before watching.",
);
commander.option(
"-o, --out-file [out]",
- "Compile all input files into a single file",
+ "Compile all input files into a single file.",
);
commander.option(
"-d, --out-dir [out]",
- "Compile an input directory of modules into an output directory",
+ "Compile an input directory of modules into an output directory.",
);
commander.option(
"--relative",
@@ -138,27 +142,47 @@ commander.option(
);
commander.option(
"-D, --copy-files",
- "When compiling a directory copy over non-compilable files",
+ "When compiling a directory copy over non-compilable files.",
);
commander.option(
"--include-dotfiles",
- "Include dotfiles when compiling and copying non-compilable files",
+ "Include dotfiles when compiling and copying non-compilable files.",
+);
+commander.option(
+ "--verbose",
+ "Log everything. This option conflicts with --quiet",
+);
+commander.option(
+ "--quiet",
+ "Don't log anything. This option conflicts with --verbose",
);
-commander.option("--verbose", "Log everything");
commander.option(
"--delete-dir-on-start",
- "Delete the out directory before compilation",
+ "Delete the out directory before compilation.",
+);
+commander.option(
+ "--out-file-extension [string]",
+ "Use a specific extension for the output files",
+);
+
+commander.option(
+ "--copy-ignored",
+ "Include ignored files when copying non-compilable files.",
);
commander.version(pkg.version + " (@babel/core " + version + ")");
commander.usage("[options] ");
+// register an empty action handler so that commander.js can throw on
+// unknown options _after_ args
+// see https://github.com/tj/commander.js/issues/561#issuecomment-522209408
+commander.action(() => {});
export type CmdOptions = {
babelOptions: Object,
cliOptions: Object,
};
-export default function parseArgv(args: Array): CmdOptions {
+export default function parseArgv(args: Array): CmdOptions | null {
//
commander.parse(args);
@@ -207,6 +231,10 @@ export default function parseArgv(args: Array): CmdOptions {
errors.push("--delete-dir-on-start requires --out-dir");
}
+ if (commander.verbose && commander.quiet) {
+ errors.push("--verbose and --quiet cannot be used together");
+ }
+
if (
!commander.outDir &&
filenames.length === 0 &&
@@ -218,12 +246,18 @@ export default function parseArgv(args: Array): CmdOptions {
);
}
+ if (commander.keepFileExtension && commander.outFileExtension) {
+ errors.push(
+ "--out-file-extension cannot be used with --keep-file-extension",
+ );
+ }
+
if (errors.length) {
console.error("babel:");
errors.forEach(function(e) {
console.error(" " + e);
});
- process.exit(2);
+ return null;
}
const opts = commander.opts();
@@ -274,6 +308,7 @@ export default function parseArgv(args: Array): CmdOptions {
filenames,
extensions: opts.extensions,
keepFileExtension: opts.keepFileExtension,
+ outFileExtension: opts.outFileExtension,
watch: opts.watch,
skipInitialBuild: opts.skipInitialBuild,
outFile: opts.outFile,
@@ -282,8 +317,10 @@ export default function parseArgv(args: Array): CmdOptions {
copyFiles: opts.copyFiles,
includeDotfiles: opts.includeDotfiles,
verbose: opts.verbose,
+ quiet: opts.quiet,
deleteDirOnStart: opts.deleteDirOnStart,
sourceMapTarget: opts.sourceMapTarget,
+ copyIgnored: opts.copyIgnored,
},
};
}
@@ -300,7 +337,10 @@ function booleanify(val: any): boolean | any {
return val;
}
-function collect(value, previousValue): Array {
+function collect(
+ value: string | any,
+ previousValue: Array,
+): Array {
// If the user passed the option with no value, like "babel file.js --presets", do nothing.
if (typeof value !== "string") return previousValue;
diff --git a/packages/babel-cli/src/babel/util.js b/packages/babel-cli/src/babel/util.js
index daacba53f0d2..3cdfe4927982 100644
--- a/packages/babel-cli/src/babel/util.js
+++ b/packages/babel-cli/src/babel/util.js
@@ -112,7 +112,7 @@ export function deleteDir(path: string): void {
process.on("uncaughtException", function(err) {
console.error(err);
- process.exit(1);
+ process.exitCode = 1;
});
export function requireChokidar(): Object {
@@ -127,12 +127,7 @@ export function requireChokidar(): Object {
}
}
-export function adjustRelative(
- relative: string,
- keepFileExtension: boolean,
-): string {
- if (keepFileExtension) {
- return relative;
- }
- return relative.replace(/\.(\w*?)$/, "") + ".js";
+export function withExtension(filename: string, ext: string = ".js") {
+ const newBasename = path.basename(filename, path.extname(filename)) + ext;
+ return path.join(path.dirname(filename), newBasename);
}
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore in babelrc/.babelrc b/packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore in babelrc/.babelrc
new file mode 100644
index 000000000000..b1fb07ff865b
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore in babelrc/.babelrc
@@ -0,0 +1,3 @@
+{
+ "ignore": ["src/foo"]
+}
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore in babelrc/in-files/src/.foorc b/packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore in babelrc/in-files/src/.foorc
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore in babelrc/in-files/src/README.md b/packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore in babelrc/in-files/src/README.md
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore/out-files/lib/foo/.foo.js b/packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore in babelrc/in-files/src/foo/.foo.js
similarity index 100%
rename from packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore/out-files/lib/foo/.foo.js
rename to packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore in babelrc/in-files/src/foo/.foo.js
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore/out-files/lib/foo/index.js b/packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore in babelrc/in-files/src/foo/index.js
similarity index 100%
rename from packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore/out-files/lib/foo/index.js
rename to packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore in babelrc/in-files/src/foo/index.js
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with only/out-files/lib/index.js b/packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore in babelrc/in-files/src/index.js
similarity index 100%
rename from packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with only/out-files/lib/index.js
rename to packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore in babelrc/in-files/src/index.js
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore in babelrc/options.json b/packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore in babelrc/options.json
new file mode 100644
index 000000000000..bb94e945f80e
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore in babelrc/options.json
@@ -0,0 +1,10 @@
+{
+ "args": [
+ "src",
+ "--out-dir",
+ "lib",
+ "--copy-files",
+ "--include-dotfiles",
+ "--verbose"
+ ]
+}
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore in babelrc/out-files/lib/.foorc b/packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore in babelrc/out-files/lib/.foorc
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore in babelrc/out-files/lib/README.md b/packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore in babelrc/out-files/lib/README.md
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore in babelrc/out-files/lib/index.js b/packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore in babelrc/out-files/lib/index.js
new file mode 100644
index 000000000000..1073b618d0dd
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore in babelrc/out-files/lib/index.js
@@ -0,0 +1,3 @@
+"use strict";
+
+index;
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore in babelrc/stdout.txt b/packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore in babelrc/stdout.txt
new file mode 100644
index 000000000000..84a430432e47
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files --include-dotfiles with ignore in babelrc/stdout.txt
@@ -0,0 +1,2 @@
+src/index.js -> lib/index.js
+Successfully compiled 1 file with Babel.
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelignore/.babelignore b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelignore/.babelignore
new file mode 100644
index 000000000000..8b4ba6ce4d35
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelignore/.babelignore
@@ -0,0 +1 @@
+src/foo
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelignore/in-files/src/.foorc b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelignore/in-files/src/.foorc
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelignore/in-files/src/README.md b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelignore/in-files/src/README.md
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore/out-files/lib/foo/bar.js b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelignore/in-files/src/foo/bar.js
similarity index 100%
rename from packages/babel-cli/test/fixtures/babel/--copy-files with ignore/out-files/lib/foo/bar.js
rename to packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelignore/in-files/src/foo/bar.js
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with only/out-files/lib/index.js b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelignore/in-files/src/index.js
similarity index 100%
rename from packages/babel-cli/test/fixtures/babel/--copy-files with only/out-files/lib/index.js
rename to packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelignore/in-files/src/index.js
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelignore/options.json b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelignore/options.json
new file mode 100644
index 000000000000..d16d70e6ffec
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelignore/options.json
@@ -0,0 +1,10 @@
+{
+ "args": [
+ "src",
+ "--out-dir",
+ "lib",
+ "--copy-files",
+ "--copy-ignored",
+ "--verbose"
+ ]
+}
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelignore/out-files/lib/README.md b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelignore/out-files/lib/README.md
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelignore/out-files/lib/foo/bar.js b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelignore/out-files/lib/foo/bar.js
new file mode 100644
index 000000000000..e46160df1c7a
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelignore/out-files/lib/foo/bar.js
@@ -0,0 +1 @@
+bar;
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelignore/out-files/lib/index.js b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelignore/out-files/lib/index.js
new file mode 100644
index 000000000000..1073b618d0dd
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelignore/out-files/lib/index.js
@@ -0,0 +1,3 @@
+"use strict";
+
+index;
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelignore/stdout.txt b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelignore/stdout.txt
new file mode 100644
index 000000000000..84a430432e47
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelignore/stdout.txt
@@ -0,0 +1,2 @@
+src/index.js -> lib/index.js
+Successfully compiled 1 file with Babel.
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelrc/.babelrc b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelrc/.babelrc
new file mode 100644
index 000000000000..b1fb07ff865b
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelrc/.babelrc
@@ -0,0 +1,3 @@
+{
+ "ignore": ["src/foo"]
+}
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelrc/in-files/src/.foorc b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelrc/in-files/src/.foorc
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelrc/in-files/src/README.md b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelrc/in-files/src/README.md
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelrc/in-files/src/foo/bar.js b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelrc/in-files/src/foo/bar.js
new file mode 100644
index 000000000000..e46160df1c7a
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelrc/in-files/src/foo/bar.js
@@ -0,0 +1 @@
+bar;
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelrc/in-files/src/index.js b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelrc/in-files/src/index.js
new file mode 100644
index 000000000000..c6788558edcb
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelrc/in-files/src/index.js
@@ -0,0 +1 @@
+index;
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelrc/options.json b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelrc/options.json
new file mode 100644
index 000000000000..d16d70e6ffec
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelrc/options.json
@@ -0,0 +1,10 @@
+{
+ "args": [
+ "src",
+ "--out-dir",
+ "lib",
+ "--copy-files",
+ "--copy-ignored",
+ "--verbose"
+ ]
+}
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelrc/out-files/lib/README.md b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelrc/out-files/lib/README.md
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelrc/out-files/lib/foo/bar.js b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelrc/out-files/lib/foo/bar.js
new file mode 100644
index 000000000000..e46160df1c7a
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelrc/out-files/lib/foo/bar.js
@@ -0,0 +1 @@
+bar;
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelrc/out-files/lib/index.js b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelrc/out-files/lib/index.js
new file mode 100644
index 000000000000..1073b618d0dd
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelrc/out-files/lib/index.js
@@ -0,0 +1,3 @@
+"use strict";
+
+index;
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelrc/stdout.txt b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelrc/stdout.txt
new file mode 100644
index 000000000000..84a430432e47
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore and copyIgnored in babelrc/stdout.txt
@@ -0,0 +1,2 @@
+src/index.js -> lib/index.js
+Successfully compiled 1 file with Babel.
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelignore/.babelignore b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelignore/.babelignore
new file mode 100644
index 000000000000..8b4ba6ce4d35
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelignore/.babelignore
@@ -0,0 +1 @@
+src/foo
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelignore/in-files/src/.foorc b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelignore/in-files/src/.foorc
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelignore/in-files/src/README.md b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelignore/in-files/src/README.md
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelignore/in-files/src/foo/bar.js b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelignore/in-files/src/foo/bar.js
new file mode 100644
index 000000000000..e46160df1c7a
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelignore/in-files/src/foo/bar.js
@@ -0,0 +1 @@
+bar;
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelignore/in-files/src/index.js b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelignore/in-files/src/index.js
new file mode 100644
index 000000000000..c6788558edcb
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelignore/in-files/src/index.js
@@ -0,0 +1 @@
+index;
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelignore/options.json b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelignore/options.json
new file mode 100644
index 000000000000..39e3d0dc2606
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelignore/options.json
@@ -0,0 +1,9 @@
+{
+ "args": [
+ "src",
+ "--out-dir",
+ "lib",
+ "--copy-files",
+ "--verbose"
+ ]
+}
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelignore/out-files/lib/README.md b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelignore/out-files/lib/README.md
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelignore/out-files/lib/index.js b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelignore/out-files/lib/index.js
new file mode 100644
index 000000000000..1073b618d0dd
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelignore/out-files/lib/index.js
@@ -0,0 +1,3 @@
+"use strict";
+
+index;
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelignore/stdout.txt b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelignore/stdout.txt
new file mode 100644
index 000000000000..84a430432e47
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelignore/stdout.txt
@@ -0,0 +1,2 @@
+src/index.js -> lib/index.js
+Successfully compiled 1 file with Babel.
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelrc/.babelrc b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelrc/.babelrc
new file mode 100644
index 000000000000..b1fb07ff865b
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelrc/.babelrc
@@ -0,0 +1,3 @@
+{
+ "ignore": ["src/foo"]
+}
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelrc/in-files/src/.foorc b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelrc/in-files/src/.foorc
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelrc/in-files/src/README.md b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelrc/in-files/src/README.md
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelrc/in-files/src/foo/bar.js b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelrc/in-files/src/foo/bar.js
new file mode 100644
index 000000000000..e46160df1c7a
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelrc/in-files/src/foo/bar.js
@@ -0,0 +1 @@
+bar;
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelrc/in-files/src/index.js b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelrc/in-files/src/index.js
new file mode 100644
index 000000000000..c6788558edcb
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelrc/in-files/src/index.js
@@ -0,0 +1 @@
+index;
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelrc/options.json b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelrc/options.json
new file mode 100644
index 000000000000..39e3d0dc2606
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelrc/options.json
@@ -0,0 +1,9 @@
+{
+ "args": [
+ "src",
+ "--out-dir",
+ "lib",
+ "--copy-files",
+ "--verbose"
+ ]
+}
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelrc/out-files/lib/README.md b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelrc/out-files/lib/README.md
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelrc/out-files/lib/index.js b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelrc/out-files/lib/index.js
new file mode 100644
index 000000000000..1073b618d0dd
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelrc/out-files/lib/index.js
@@ -0,0 +1,3 @@
+"use strict";
+
+index;
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelrc/stdout.txt b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelrc/stdout.txt
new file mode 100644
index 000000000000..84a430432e47
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore in babelrc/stdout.txt
@@ -0,0 +1,2 @@
+src/index.js -> lib/index.js
+Successfully compiled 1 file with Babel.
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore with copyIgnored/in-files/src/.foorc b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore with copyIgnored/in-files/src/.foorc
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore with copyIgnored/in-files/src/README.md b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore with copyIgnored/in-files/src/README.md
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore with copyIgnored/in-files/src/foo/bar.js b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore with copyIgnored/in-files/src/foo/bar.js
new file mode 100644
index 000000000000..e46160df1c7a
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore with copyIgnored/in-files/src/foo/bar.js
@@ -0,0 +1 @@
+bar;
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore with copyIgnored/in-files/src/index.js b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore with copyIgnored/in-files/src/index.js
new file mode 100644
index 000000000000..c6788558edcb
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore with copyIgnored/in-files/src/index.js
@@ -0,0 +1 @@
+index;
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore with copyIgnored/options.json b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore with copyIgnored/options.json
new file mode 100644
index 000000000000..f49a79c08fe5
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore with copyIgnored/options.json
@@ -0,0 +1,12 @@
+{
+ "args": [
+ "src",
+ "--out-dir",
+ "lib",
+ "--copy-files",
+ "--ignore",
+ "src/foo",
+ "--copy-ignored",
+ "--verbose"
+ ]
+}
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore with copyIgnored/out-files/lib/README.md b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore with copyIgnored/out-files/lib/README.md
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore with copyIgnored/out-files/lib/foo/bar.js b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore with copyIgnored/out-files/lib/foo/bar.js
new file mode 100644
index 000000000000..e46160df1c7a
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore with copyIgnored/out-files/lib/foo/bar.js
@@ -0,0 +1 @@
+bar;
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore with copyIgnored/out-files/lib/index.js b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore with copyIgnored/out-files/lib/index.js
new file mode 100644
index 000000000000..cb5e86b12ef3
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore with copyIgnored/out-files/lib/index.js
@@ -0,0 +1,3 @@
+"use strict";
+
+index;
\ No newline at end of file
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with ignore with copyIgnored/stdout.txt b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore with copyIgnored/stdout.txt
new file mode 100644
index 000000000000..84a430432e47
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with ignore with copyIgnored/stdout.txt
@@ -0,0 +1,2 @@
+src/index.js -> lib/index.js
+Successfully compiled 1 file with Babel.
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with only copy copyIgnored/in-files/src/.foorc b/packages/babel-cli/test/fixtures/babel/--copy-files with only copy copyIgnored/in-files/src/.foorc
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with only copy copyIgnored/in-files/src/README.md b/packages/babel-cli/test/fixtures/babel/--copy-files with only copy copyIgnored/in-files/src/README.md
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with only copy copyIgnored/in-files/src/foo/bar.js b/packages/babel-cli/test/fixtures/babel/--copy-files with only copy copyIgnored/in-files/src/foo/bar.js
new file mode 100644
index 000000000000..e46160df1c7a
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with only copy copyIgnored/in-files/src/foo/bar.js
@@ -0,0 +1 @@
+bar;
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with only copy copyIgnored/in-files/src/index.js b/packages/babel-cli/test/fixtures/babel/--copy-files with only copy copyIgnored/in-files/src/index.js
new file mode 100644
index 000000000000..c6788558edcb
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with only copy copyIgnored/in-files/src/index.js
@@ -0,0 +1 @@
+index;
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with only copy copyIgnored/options.json b/packages/babel-cli/test/fixtures/babel/--copy-files with only copy copyIgnored/options.json
new file mode 100644
index 000000000000..f3adb3c91b91
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with only copy copyIgnored/options.json
@@ -0,0 +1,12 @@
+{
+ "args": [
+ "src",
+ "--out-dir",
+ "lib",
+ "--copy-files",
+ "--only",
+ "src/foo/*",
+ "--copy-ignored",
+ "--verbose"
+ ]
+}
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with only copy copyIgnored/out-files/lib/README.md b/packages/babel-cli/test/fixtures/babel/--copy-files with only copy copyIgnored/out-files/lib/README.md
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with only copy copyIgnored/out-files/lib/foo/bar.js b/packages/babel-cli/test/fixtures/babel/--copy-files with only copy copyIgnored/out-files/lib/foo/bar.js
new file mode 100644
index 000000000000..0ed075a19327
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with only copy copyIgnored/out-files/lib/foo/bar.js
@@ -0,0 +1,3 @@
+"use strict";
+
+bar;
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with only copy copyIgnored/out-files/lib/index.js b/packages/babel-cli/test/fixtures/babel/--copy-files with only copy copyIgnored/out-files/lib/index.js
new file mode 100644
index 000000000000..c6788558edcb
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with only copy copyIgnored/out-files/lib/index.js
@@ -0,0 +1 @@
+index;
diff --git a/packages/babel-cli/test/fixtures/babel/--copy-files with only copy copyIgnored/stdout.txt b/packages/babel-cli/test/fixtures/babel/--copy-files with only copy copyIgnored/stdout.txt
new file mode 100644
index 000000000000..673dafc7483e
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--copy-files with only copy copyIgnored/stdout.txt
@@ -0,0 +1,2 @@
+src/foo/bar.js -> lib/foo/bar.js
+Successfully compiled 1 file with Babel.
diff --git a/packages/babel-cli/test/fixtures/babel/--minified/in-files/script.js b/packages/babel-cli/test/fixtures/babel/--minified/in-files/script.js
new file mode 100644
index 000000000000..3353ddb752c2
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--minified/in-files/script.js
@@ -0,0 +1 @@
+() => 42;
diff --git a/packages/babel-cli/test/fixtures/babel/--minified/options.json b/packages/babel-cli/test/fixtures/babel/--minified/options.json
new file mode 100644
index 000000000000..2df8c827b826
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--minified/options.json
@@ -0,0 +1,3 @@
+{
+ "args": ["--out-file", "script2.js", "--no-comments", "--minified", "script.js"]
+}
diff --git a/packages/babel-cli/test/fixtures/babel/--minified/out-files/script2.js b/packages/babel-cli/test/fixtures/babel/--minified/out-files/script2.js
new file mode 100644
index 000000000000..b9260cd4afe8
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/--minified/out-files/script2.js
@@ -0,0 +1 @@
+"use strict";(function(){return 42});
diff --git a/packages/babel-cli/test/fixtures/babel/dir --out-dir --out-file-extension/in-files/src/bar.mjs b/packages/babel-cli/test/fixtures/babel/dir --out-dir --out-file-extension/in-files/src/bar.mjs
new file mode 100644
index 000000000000..7c1178b8d64f
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/dir --out-dir --out-file-extension/in-files/src/bar.mjs
@@ -0,0 +1 @@
+arr.map(x => x / DIVIDER);
diff --git a/packages/babel-cli/test/fixtures/babel/dir --out-dir --out-file-extension/in-files/src/foo.jsx b/packages/babel-cli/test/fixtures/babel/dir --out-dir --out-file-extension/in-files/src/foo.jsx
new file mode 100644
index 000000000000..4bc9539528d4
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/dir --out-dir --out-file-extension/in-files/src/foo.jsx
@@ -0,0 +1 @@
+arr.map(x => x * MULTIPLIER);
diff --git a/packages/babel-cli/test/fixtures/babel/dir --out-dir --out-file-extension/options.json b/packages/babel-cli/test/fixtures/babel/dir --out-dir --out-file-extension/options.json
new file mode 100644
index 000000000000..5150f4941a1f
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/dir --out-dir --out-file-extension/options.json
@@ -0,0 +1,12 @@
+{
+ "args": [
+ "src",
+ "--out-dir",
+ "lib",
+ "--out-file-extension",
+ ".mjs",
+ "--extensions",
+ ".jsx,.mjs",
+ "--verbose"
+ ]
+}
diff --git a/packages/babel-cli/test/fixtures/babel/dir --out-dir --out-file-extension/out-files/lib/bar.mjs b/packages/babel-cli/test/fixtures/babel/dir --out-dir --out-file-extension/out-files/lib/bar.mjs
new file mode 100644
index 000000000000..5d02843681e4
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/dir --out-dir --out-file-extension/out-files/lib/bar.mjs
@@ -0,0 +1,5 @@
+"use strict";
+
+arr.map(function (x) {
+ return x / DIVIDER;
+});
diff --git a/packages/babel-cli/test/fixtures/babel/dir --out-dir --out-file-extension/out-files/lib/foo.mjs b/packages/babel-cli/test/fixtures/babel/dir --out-dir --out-file-extension/out-files/lib/foo.mjs
new file mode 100644
index 000000000000..ae4557e57bc3
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/dir --out-dir --out-file-extension/out-files/lib/foo.mjs
@@ -0,0 +1,5 @@
+"use strict";
+
+arr.map(function (x) {
+ return x * MULTIPLIER;
+});
diff --git a/packages/babel-cli/test/fixtures/babel/dir --out-dir --out-file-extension/stdout.txt b/packages/babel-cli/test/fixtures/babel/dir --out-dir --out-file-extension/stdout.txt
new file mode 100644
index 000000000000..24ba955afb2b
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/dir --out-dir --out-file-extension/stdout.txt
@@ -0,0 +1,3 @@
+src/bar.mjs -> lib/bar.mjs
+src/foo.jsx -> lib/foo.mjs
+Successfully compiled 2 files with Babel.
diff --git a/packages/babel-cli/test/fixtures/babel/error --keep-file-extension --out-file-extension/options.json b/packages/babel-cli/test/fixtures/babel/error --keep-file-extension --out-file-extension/options.json
new file mode 100644
index 000000000000..8ad9743c9dbc
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/error --keep-file-extension --out-file-extension/options.json
@@ -0,0 +1,4 @@
+{
+ "args": ["--keep-file-extension", "--out-file-extension", ".mjs"],
+ "stderrContains": true
+}
diff --git a/packages/babel-cli/test/fixtures/babel/error --keep-file-extension --out-file-extension/stderr.txt b/packages/babel-cli/test/fixtures/babel/error --keep-file-extension --out-file-extension/stderr.txt
new file mode 100644
index 000000000000..acc6f8edb8bb
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/error --keep-file-extension --out-file-extension/stderr.txt
@@ -0,0 +1 @@
+--out-file-extension cannot be used with --keep-file-extension
diff --git a/packages/babel-cli/test/fixtures/babel/error incorrect options after args/in-files/script.js b/packages/babel-cli/test/fixtures/babel/error incorrect options after args/in-files/script.js
new file mode 100644
index 000000000000..4bc9539528d4
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/error incorrect options after args/in-files/script.js
@@ -0,0 +1 @@
+arr.map(x => x * MULTIPLIER);
diff --git a/packages/babel-cli/test/fixtures/babel/error incorrect options after args/options.json b/packages/babel-cli/test/fixtures/babel/error incorrect options after args/options.json
new file mode 100644
index 000000000000..dde64755da06
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/error incorrect options after args/options.json
@@ -0,0 +1,4 @@
+{
+ "args": ["script.js", "--spruce-maps"],
+ "stderrContains": true
+}
diff --git a/packages/babel-cli/test/fixtures/babel/error incorrect options after args/stderr.txt b/packages/babel-cli/test/fixtures/babel/error incorrect options after args/stderr.txt
new file mode 100644
index 000000000000..294d5ccaeaed
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/error incorrect options after args/stderr.txt
@@ -0,0 +1 @@
+error: unknown option '--spruce-maps'
diff --git a/packages/babel-cli/test/fixtures/babel/error incorrect options before args/in-files/script.js b/packages/babel-cli/test/fixtures/babel/error incorrect options before args/in-files/script.js
new file mode 100644
index 000000000000..4bc9539528d4
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/error incorrect options before args/in-files/script.js
@@ -0,0 +1 @@
+arr.map(x => x * MULTIPLIER);
diff --git a/packages/babel-cli/test/fixtures/babel/error incorrect options before args/options.json b/packages/babel-cli/test/fixtures/babel/error incorrect options before args/options.json
new file mode 100644
index 000000000000..0d7953971726
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/error incorrect options before args/options.json
@@ -0,0 +1,4 @@
+{
+ "args": ["--spruce-maps", "script.js"],
+ "stderrContains": true
+}
diff --git a/packages/babel-cli/test/fixtures/babel/error incorrect options before args/stderr.txt b/packages/babel-cli/test/fixtures/babel/error incorrect options before args/stderr.txt
new file mode 100644
index 000000000000..294d5ccaeaed
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/error incorrect options before args/stderr.txt
@@ -0,0 +1 @@
+error: unknown option '--spruce-maps'
diff --git a/packages/babel-cli/test/fixtures/babel/filename --out-file deep/in-files/script.js b/packages/babel-cli/test/fixtures/babel/filename --out-file deep/in-files/script.js
new file mode 100644
index 000000000000..4bc9539528d4
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/filename --out-file deep/in-files/script.js
@@ -0,0 +1 @@
+arr.map(x => x * MULTIPLIER);
diff --git a/packages/babel-cli/test/fixtures/babel/filename --out-file deep/options.json b/packages/babel-cli/test/fixtures/babel/filename --out-file deep/options.json
new file mode 100644
index 000000000000..d24719ba4a76
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/filename --out-file deep/options.json
@@ -0,0 +1,3 @@
+{
+ "args": ["script.js", "--out-file", "folder/nested/script.js"]
+}
diff --git a/packages/babel-cli/test/fixtures/babel/filename --out-file deep/out-files/folder/nested/script.js b/packages/babel-cli/test/fixtures/babel/filename --out-file deep/out-files/folder/nested/script.js
new file mode 100644
index 000000000000..ae4557e57bc3
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/filename --out-file deep/out-files/folder/nested/script.js
@@ -0,0 +1,5 @@
+"use strict";
+
+arr.map(function (x) {
+ return x * MULTIPLIER;
+});
diff --git a/packages/babel-cli/test/fixtures/babel/stdin --filename windows/options.json b/packages/babel-cli/test/fixtures/babel/stdin --filename windows/options.json
new file mode 100644
index 000000000000..0c847447d595
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/stdin --filename windows/options.json
@@ -0,0 +1,5 @@
+{
+ "args": ["--filename", "test.js"],
+ "stderrContains": true,
+ "os": ["win32"]
+}
diff --git a/packages/babel-cli/test/fixtures/babel/stdin --filename windows/stderr.txt b/packages/babel-cli/test/fixtures/babel/stdin --filename windows/stderr.txt
new file mode 100644
index 000000000000..1ca69db1084e
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/stdin --filename windows/stderr.txt
@@ -0,0 +1 @@
+SyntaxError: \test.js: Unexpected token, expected ";" (2:10)
diff --git a/packages/babel-cli/test/fixtures/babel/stdin --filename windows/stdin.txt b/packages/babel-cli/test/fixtures/babel/stdin --filename windows/stdin.txt
new file mode 100644
index 000000000000..1d931c7e8559
--- /dev/null
+++ b/packages/babel-cli/test/fixtures/babel/stdin --filename windows/stdin.txt
@@ -0,0 +1,3 @@
+arr.map(function () {
+ return $]!;
+});
diff --git a/packages/babel-cli/test/fixtures/babel/stdin --filename/options.json b/packages/babel-cli/test/fixtures/babel/stdin --filename/options.json
index 4274d9a29614..724c35ed8520 100644
--- a/packages/babel-cli/test/fixtures/babel/stdin --filename/options.json
+++ b/packages/babel-cli/test/fixtures/babel/stdin --filename/options.json
@@ -1,4 +1,5 @@
{
"args": ["--filename", "test.js"],
- "stderrContains": true
+ "stderrContains": true,
+ "os": ["linux", "darwin"]
}
diff --git a/packages/babel-cli/test/index.js b/packages/babel-cli/test/index.js
index 64f64273319c..e7ee2cbb6f40 100644
--- a/packages/babel-cli/test/index.js
+++ b/packages/babel-cli/test/index.js
@@ -1,7 +1,7 @@
const readdir = require("fs-readdir-recursive");
const helper = require("@babel/helper-fixtures");
const rimraf = require("rimraf");
-const outputFileSync = require("output-file-sync");
+const { sync: makeDirSync } = require("make-dir");
const child = require("child_process");
const merge = require("lodash/merge");
const path = require("path");
@@ -14,6 +14,11 @@ const fileFilter = function(x) {
return x !== ".DS_Store";
};
+const outputFileSync = function(filePath, data) {
+ makeDirSync(path.dirname(filePath));
+ fs.writeFileSync(filePath, data);
+};
+
const presetLocs = [path.join(__dirname, "../../babel-preset-react")];
const pluginLocs = [
@@ -92,7 +97,8 @@ const assertTest = function(stdout, stderr, opts, cwd) {
if (
// saveInFiles always creates an empty .babelrc, so lets exclude for now
filename !== ".babelrc" &&
- !opts.inFiles.hasOwnProperty(filename)
+ filename !== ".babelignore" &&
+ !Object.prototype.hasOwnProperty.call(opts.inFiles, filename)
) {
const expected = opts.outFiles[filename];
const actual = actualFiles[filename];
@@ -115,13 +121,6 @@ const buildTest = function(binName, testName, opts) {
const binLoc = path.join(__dirname, "../lib", binName);
return function(callback) {
- const dir = process.cwd();
-
- process.chdir(__dirname);
- if (fs.existsSync(tmpLoc)) rimraf.sync(tmpLoc);
- fs.mkdirSync(tmpLoc);
- process.chdir(tmpLoc);
-
saveInFiles(opts.inFiles);
let args = [binLoc];
@@ -159,7 +158,6 @@ const buildTest = function(binName, testName, opts) {
args.map(arg => `"${arg}"`).join(" ") + ": " + err.message;
}
- process.chdir(dir);
callback(err);
});
@@ -175,6 +173,26 @@ fs.readdirSync(fixtureLoc).forEach(function(binName) {
const suiteLoc = path.join(fixtureLoc, binName);
describe("bin/" + binName, function() {
+ let cwd;
+
+ beforeEach(() => {
+ cwd = process.cwd();
+
+ if (fs.existsSync(tmpLoc)) {
+ for (const child of fs.readdirSync(tmpLoc)) {
+ rimraf.sync(path.join(tmpLoc, child));
+ }
+ } else {
+ fs.mkdirSync(tmpLoc);
+ }
+
+ process.chdir(tmpLoc);
+ });
+
+ afterEach(() => {
+ process.chdir(cwd);
+ });
+
fs.readdirSync(suiteLoc).forEach(function(testName) {
if (testName.startsWith(".")) return;
@@ -185,7 +203,29 @@ fs.readdirSync(fixtureLoc).forEach(function(binName) {
};
const optionsLoc = path.join(testLoc, "options.json");
- if (fs.existsSync(optionsLoc)) merge(opts, require(optionsLoc));
+ if (fs.existsSync(optionsLoc)) {
+ const taskOpts = require(optionsLoc);
+ if (taskOpts.os) {
+ let os = taskOpts.os;
+
+ if (!Array.isArray(os) && typeof os !== "string") {
+ throw new Error(
+ `'os' should be either string or string array: ${taskOpts.os}`,
+ );
+ }
+
+ if (typeof os === "string") {
+ os = [os];
+ }
+
+ if (!os.includes(process.platform)) {
+ return;
+ }
+
+ delete taskOpts.os;
+ }
+ merge(opts, taskOpts);
+ }
["stdout", "stdin", "stderr"].forEach(function(key) {
const loc = path.join(testLoc, key + ".txt");
@@ -200,12 +240,18 @@ fs.readdirSync(fixtureLoc).forEach(function(binName) {
opts.inFiles = readDir(path.join(testLoc, "in-files"), fileFilter);
const babelrcLoc = path.join(testLoc, ".babelrc");
+ const babelIgnoreLoc = path.join(testLoc, ".babelignore");
if (fs.existsSync(babelrcLoc)) {
// copy .babelrc file to tmp directory
opts.inFiles[".babelrc"] = helper.readFile(babelrcLoc);
+ opts.inFiles[".babelignore"] = helper.readFile(babelIgnoreLoc);
+ }
+ if (fs.existsSync(babelIgnoreLoc)) {
+ // copy .babelignore file to tmp directory
+ opts.inFiles[".babelignore"] = helper.readFile(babelIgnoreLoc);
}
- it(testName, buildTest(binName, testName, opts));
+ it(testName, buildTest(binName, testName, opts), 20000);
});
});
});
diff --git a/packages/babel-code-frame/package.json b/packages/babel-code-frame/package.json
index 0b753eb11230..e5ba6996a98a 100644
--- a/packages/babel-code-frame/package.json
+++ b/packages/babel-code-frame/package.json
@@ -1,6 +1,6 @@
{
"name": "@babel/code-frame",
- "version": "7.5.5",
+ "version": "7.8.3",
"description": "Generate errors that contain a code frame that point to source locations.",
"author": "Sebastian McKenzie ",
"homepage": "https://babeljs.io/",
@@ -11,7 +11,7 @@
"repository": "https://github.com/babel/babel/tree/master/packages/babel-code-frame",
"main": "lib/index.js",
"dependencies": {
- "@babel/highlight": "^7.0.0"
+ "@babel/highlight": "^7.8.3"
},
"devDependencies": {
"chalk": "^2.0.0",
diff --git a/packages/babel-compat-data/.npmignore b/packages/babel-compat-data/.npmignore
new file mode 100644
index 000000000000..57e1671459aa
--- /dev/null
+++ b/packages/babel-compat-data/.npmignore
@@ -0,0 +1,4 @@
+scripts
+src
+test
+*.log
diff --git a/packages/babel-compat-data/corejs2-built-ins.js b/packages/babel-compat-data/corejs2-built-ins.js
new file mode 100644
index 000000000000..ccbe72f0d58b
--- /dev/null
+++ b/packages/babel-compat-data/corejs2-built-ins.js
@@ -0,0 +1,4 @@
+// Node < 13.3 doesn't support export maps in package.json.
+// Use this proxy file as a fallback.
+
+module.exports = require("./data/corejs2-built-ins.json");
diff --git a/packages/babel-preset-env/data/built-in-modules.json b/packages/babel-compat-data/data/built-in-modules.json
similarity index 100%
rename from packages/babel-preset-env/data/built-in-modules.json
rename to packages/babel-compat-data/data/built-in-modules.json
diff --git a/packages/babel-preset-env/data/corejs2-built-ins.json b/packages/babel-compat-data/data/corejs2-built-ins.json
similarity index 99%
rename from packages/babel-preset-env/data/corejs2-built-ins.json
rename to packages/babel-compat-data/data/corejs2-built-ins.json
index 2079f1c3d74a..5c81e27f27f5 100644
--- a/packages/babel-preset-env/data/corejs2-built-ins.json
+++ b/packages/babel-compat-data/data/corejs2-built-ins.json
@@ -77,6 +77,7 @@
"safari": "12",
"node": "11",
"ios": "12",
+ "samsung": "10.2",
"opera": "56",
"electron": "4"
},
@@ -1496,6 +1497,7 @@
"safari": "12",
"node": "10",
"ios": "12",
+ "samsung": "9.2",
"opera": "53",
"electron": "3.1"
},
@@ -1505,6 +1507,7 @@
"safari": "12",
"node": "10",
"ios": "12",
+ "samsung": "9.2",
"opera": "53",
"electron": "3.1"
},
diff --git a/packages/babel-compat-data/data/native-modules.json b/packages/babel-compat-data/data/native-modules.json
new file mode 100644
index 000000000000..7e33f27eb290
--- /dev/null
+++ b/packages/babel-compat-data/data/native-modules.json
@@ -0,0 +1,12 @@
+{
+ "es6.module": {
+ "edge": "16",
+ "firefox": "60",
+ "chrome": "61",
+ "safari": "10.1",
+ "opera": "48",
+ "ios_saf": "10.3",
+ "and_chr": "74",
+ "and_ff": "66"
+ }
+}
diff --git a/packages/babel-compat-data/data/overlapping-plugins.json b/packages/babel-compat-data/data/overlapping-plugins.json
new file mode 100644
index 000000000000..2cd7597e6081
--- /dev/null
+++ b/packages/babel-compat-data/data/overlapping-plugins.json
@@ -0,0 +1,3 @@
+{
+ "transform-regenerator": []
+}
\ No newline at end of file
diff --git a/packages/babel-preset-env/data/plugins.json b/packages/babel-compat-data/data/plugins.json
similarity index 95%
rename from packages/babel-preset-env/data/plugins.json
rename to packages/babel-compat-data/data/plugins.json
index 382a51399431..cc4902eaa9c0 100644
--- a/packages/babel-preset-env/data/plugins.json
+++ b/packages/babel-compat-data/data/plugins.json
@@ -3,7 +3,9 @@
"chrome": "41",
"edge": "13",
"firefox": "34",
+ "safari": "13",
"node": "4",
+ "ios": "13",
"samsung": "3.4",
"opera": "28",
"electron": "0.24"
@@ -273,6 +275,7 @@
"safari": "11.1",
"node": "10",
"ios": "11.3",
+ "samsung": "9.2",
"opera": "51",
"electron": "3.1"
},
@@ -282,6 +285,7 @@
"safari": "12",
"node": "10",
"ios": "12",
+ "samsung": "9.2",
"opera": "53",
"electron": "3.1"
},
@@ -291,14 +295,20 @@
"safari": "11.1",
"node": "10",
"ios": "11.3",
+ "samsung": "9.2",
"opera": "53",
"electron": "3.1"
},
+ "proposal-optional-chaining": {
+ "chrome": "80",
+ "opera": "67"
+ },
"transform-named-capturing-groups-regex": {
"chrome": "64",
"safari": "11.1",
"node": "10",
"ios": "11.3",
+ "samsung": "9.2",
"opera": "51",
"electron": "3.1"
},
@@ -343,5 +353,10 @@
"phantom": "2",
"samsung": "2.1",
"electron": "0.2"
+ },
+ "proposal-nullish-coalescing-operator": {
+ "chrome": "80",
+ "firefox": "72",
+ "opera": "67"
}
}
diff --git a/packages/babel-compat-data/native-modules.js b/packages/babel-compat-data/native-modules.js
new file mode 100644
index 000000000000..efa3031c0a53
--- /dev/null
+++ b/packages/babel-compat-data/native-modules.js
@@ -0,0 +1,4 @@
+// Node < 13.3 doesn't support export maps in package.json.
+// Use this proxy file as a fallback.
+
+module.exports = require("./data/native-modules.json");
diff --git a/packages/babel-compat-data/overlapping-plugins.js b/packages/babel-compat-data/overlapping-plugins.js
new file mode 100644
index 000000000000..9f7d8efde56f
--- /dev/null
+++ b/packages/babel-compat-data/overlapping-plugins.js
@@ -0,0 +1,4 @@
+// Node < 13.3 doesn't support export maps in package.json.
+// Use this proxy file as a fallback.
+
+module.exports = require("./data/overlapping-plugins.json");
diff --git a/packages/babel-compat-data/package.json b/packages/babel-compat-data/package.json
new file mode 100644
index 000000000000..824d84bca145
--- /dev/null
+++ b/packages/babel-compat-data/package.json
@@ -0,0 +1,36 @@
+{
+ "name": "@babel/compat-data",
+ "version": "7.8.1",
+ "author": "The Babel Team (https://babeljs.io/team)",
+ "license": "MIT",
+ "description": "",
+ "repository": "https://github.com/babel/babel/tree/master/packages/babel-compat-data",
+ "publishConfig": {
+ "access": "public"
+ },
+ "exports": {
+ "./plugins": "./data/plugins.json",
+ "./native-modules": "./data/native-modules.json",
+ "./corejs2-built-ins": "./data/corejs2-built-ins.json",
+ "./overlapping-plugins": "./data/overlapping-plugins.json"
+ },
+ "scripts": {
+ "build-data": "./scripts/download-compat-table.sh; node ./scripts/build-data.js; node ./scripts/build-modules-support.js; node ./scripts/build-overlapping-plugins.js"
+ },
+ "keywords": [
+ "babel",
+ "compat-table",
+ "compat-data"
+ ],
+ "dependencies": {
+ "browserslist": "^4.8.2",
+ "invariant": "^2.2.4",
+ "semver": "^5.5.0"
+ },
+ "devDependencies": {
+ "@babel/helper-compilation-targets": "^7.8.1",
+ "caniuse-db": "1.0.30000969",
+ "electron-to-chromium": "1.3.113",
+ "lodash": "^4.17.15"
+ }
+}
diff --git a/packages/babel-compat-data/plugins.js b/packages/babel-compat-data/plugins.js
new file mode 100644
index 000000000000..74a9d60f2310
--- /dev/null
+++ b/packages/babel-compat-data/plugins.js
@@ -0,0 +1,4 @@
+// Node < 13.3 doesn't support export maps in package.json.
+// Use this proxy file as a fallback.
+
+module.exports = require("./data/plugins.json");
diff --git a/packages/babel-preset-env/scripts/build-data.js b/packages/babel-compat-data/scripts/build-data.js
similarity index 97%
rename from packages/babel-preset-env/scripts/build-data.js
rename to packages/babel-compat-data/scripts/build-data.js
index d4502ddaa91f..b4ac76c25c4e 100644
--- a/packages/babel-preset-env/scripts/build-data.js
+++ b/packages/babel-compat-data/scripts/build-data.js
@@ -7,7 +7,7 @@ const flattenDeep = require("lodash/flattenDeep");
const isEqual = require("lodash/isEqual");
const mapValues = require("lodash/mapValues");
const pickBy = require("lodash/pickBy");
-const unreleasedLabels = require("../data/unreleased-labels");
+const { unreleasedLabels } = require("babel/helper-compilation-targets");
const electronToChromiumVersions = require("electron-to-chromium").versions;
const electronToChromiumKeys = Object.keys(
@@ -46,7 +46,7 @@ const renameTests = (tests, getName, category) =>
// environments (node4 and chrome45), as well as familial relationships (edge
// and ie11) can be handled properly.
-const envs = require("compat-table/environments");
+const envs = require("../build/compat-table/environments");
const byTestSuite = suite => browser => {
return Array.isArray(browser.test_suites)
@@ -56,7 +56,7 @@ const byTestSuite = suite => browser => {
const compatSources = ["es5", "es6", "es2016plus", "esnext"].reduce(
(result, source) => {
- const data = require(`compat-table/data-${source}`);
+ const data = require(`../build/compat-table/data-${source}`);
data.browsers = pickBy(envs, byTestSuite(source));
result.push(data);
return result;
@@ -293,7 +293,7 @@ const generateData = (environments, features) => {
["plugin", "corejs2-built-in"].forEach(target => {
const newData = generateData(
environments,
- require(`../data/${target}-features`)
+ require(`./data/${target}-features`)
);
const dataPath = path.join(__dirname, `../data/${target}s.json`);
diff --git a/packages/babel-preset-env/scripts/build-modules-support.js b/packages/babel-compat-data/scripts/build-modules-support.js
similarity index 100%
rename from packages/babel-preset-env/scripts/build-modules-support.js
rename to packages/babel-compat-data/scripts/build-modules-support.js
diff --git a/packages/babel-compat-data/scripts/build-overlapping-plugins.js b/packages/babel-compat-data/scripts/build-overlapping-plugins.js
new file mode 100644
index 000000000000..24e34cfeb92e
--- /dev/null
+++ b/packages/babel-compat-data/scripts/build-overlapping-plugins.js
@@ -0,0 +1,15 @@
+"use strict";
+
+const fs = require("fs");
+const overlappingPlugins = require("./data/overlapping-plugins");
+
+fs.writeFileSync(
+ __dirname + "/../data/overlapping-plugins.json",
+ JSON.stringify(overlappingPlugins, replacer, 2)
+);
+
+function replacer(key, val) {
+ if (val instanceof Set) return Array.from(val);
+ if (val instanceof Map) return Object.fromEntries(val);
+ return val;
+}
diff --git a/packages/babel-preset-env/data/corejs2-built-in-features.js b/packages/babel-compat-data/scripts/data/corejs2-built-in-features.js
similarity index 80%
rename from packages/babel-preset-env/data/corejs2-built-in-features.js
rename to packages/babel-compat-data/scripts/data/corejs2-built-in-features.js
index f18244b4c9ad..2beb9bbed4c8 100644
--- a/packages/babel-preset-env/data/corejs2-built-in-features.js
+++ b/packages/babel-compat-data/scripts/data/corejs2-built-in-features.js
@@ -26,7 +26,7 @@ const typedArrayMethods = [
"typed arrays / %TypedArray%[Symbol.species]",
];
-const es = {
+module.exports = {
// compat-table missing babel6 mapping
// "es6.array.concat": {
// features: [
@@ -34,7 +34,8 @@ const es = {
// "well-known symbols / Symbol.species, Array.prototype.concat",
// ]
// },
- "es6.array.copy-within": "Array.prototype methods / Array.prototype.copyWithin",
+ "es6.array.copy-within":
+ "Array.prototype methods / Array.prototype.copyWithin",
"es6.array.every": "Array methods / Array.prototype.every",
"es6.array.fill": "Array.prototype methods / Array.prototype.fill",
"es6.array.filter": {
@@ -46,7 +47,8 @@ const es = {
},
"es6.array.find": "Array.prototype methods / Array.prototype.find",
"es6.array.find-index": "Array.prototype methods / Array.prototype.findIndex",
- "es7.array.flat-map": "Array.prototype.{flat, flatMap} / Array.prototype.flatMap",
+ "es7.array.flat-map":
+ "Array.prototype.{flat, flatMap} / Array.prototype.flatMap",
"es6.array.for-each": "Array methods / Array.prototype.forEach",
"es6.array.from": "Array static methods / Array.from",
"es7.array.includes": "Array.prototype.includes",
@@ -90,12 +92,36 @@ const es = {
"es6.function.has-instance": "well-known symbols / Symbol.hasInstance",
"es6.function.name": {
features: [
- "function \"name\" property / function statements",
- "function \"name\" property / function expressions",
+ 'function "name" property / function statements',
+ 'function "name" property / function expressions',
],
},
- "es6.map": "Map",
+ // This is explicit to prevent Map-related proposals (like
+ // Map.prototype.upsert) from being included
+ "es6.map": {
+ features: [
+ "Map / basic functionality",
+ "Map / constructor arguments",
+ "Map / constructor requires new",
+ "Map / constructor accepts null",
+ "Map / constructor invokes set",
+ "Map / iterator closing",
+ "Map / Map.prototype.add returns this",
+ "Map / -0 key converts to +0",
+ "Map / Map.prototype.size",
+ "Map / Map.prototype.delete",
+ "Map / Map.prototype.clear",
+ "Map / Map.prototype.forEach",
+ "Map / Map.prototype.keys",
+ "Map / Map.prototype.values",
+ "Map / Map.prototype.entries",
+ "Map / Map.prototype[Symbol.iterator]",
+ "Map / Map.prototype isn't an instance",
+ "Map / Map iterator prototype chain",
+ "Map / Map[Symbol.species]",
+ ],
+ },
"es6.math.acosh": "Math methods / Math.acosh",
"es6.math.asinh": "Math methods / Math.asinh",
@@ -150,13 +176,19 @@ const es = {
],
},
"es6.object.define-property": "Object static methods / Object.defineProperty",
- "es6.object.define-properties": "Object static methods / Object.defineProperties",
+ "es6.object.define-properties":
+ "Object static methods / Object.defineProperties",
"es7.object.entries": "Object static methods / Object.entries",
- "es6.object.freeze": "Object static methods accept primitives / Object.freeze",
- "es6.object.get-own-property-descriptor": "Object static methods accept primitives / Object.getOwnPropertyDescriptor",
- "es7.object.get-own-property-descriptors": "Object static methods / Object.getOwnPropertyDescriptors",
- "es6.object.get-own-property-names": "Object static methods accept primitives / Object.getOwnPropertyNames",
- "es6.object.get-prototype-of": "Object static methods accept primitives / Object.getPrototypeOf",
+ "es6.object.freeze":
+ "Object static methods accept primitives / Object.freeze",
+ "es6.object.get-own-property-descriptor":
+ "Object static methods accept primitives / Object.getOwnPropertyDescriptor",
+ "es7.object.get-own-property-descriptors":
+ "Object static methods / Object.getOwnPropertyDescriptors",
+ "es6.object.get-own-property-names":
+ "Object static methods accept primitives / Object.getOwnPropertyNames",
+ "es6.object.get-prototype-of":
+ "Object static methods accept primitives / Object.getPrototypeOf",
"es7.object.lookup-getter": {
features: [
"Object.prototype getter/setter methods / __lookupGetter__",
@@ -175,15 +207,20 @@ const es = {
"Object.prototype getter/setter methods / __lookupSetter__, data properties can shadow accessors",
],
},
- "es6.object.prevent-extensions": "Object static methods accept primitives / Object.preventExtensions",
+ "es6.object.prevent-extensions":
+ "Object static methods accept primitives / Object.preventExtensions",
"es6.object.to-string": "well-known symbols / Symbol.toStringTag",
"es6.object.is": "Object static methods / Object.is",
- "es6.object.is-frozen": "Object static methods accept primitives / Object.isFrozen",
- "es6.object.is-sealed": "Object static methods accept primitives / Object.isSealed",
- "es6.object.is-extensible": "Object static methods accept primitives / Object.isExtensible",
+ "es6.object.is-frozen":
+ "Object static methods accept primitives / Object.isFrozen",
+ "es6.object.is-sealed":
+ "Object static methods accept primitives / Object.isSealed",
+ "es6.object.is-extensible":
+ "Object static methods accept primitives / Object.isExtensible",
"es6.object.keys": "Object static methods accept primitives / Object.keys",
"es6.object.seal": "Object static methods accept primitives / Object.seal",
- "es6.object.set-prototype-of": "Object static methods / Object.setPrototypeOf",
+ "es6.object.set-prototype-of":
+ "Object static methods / Object.setPrototypeOf",
"es7.object.values": "Object static methods / Object.values",
"es6.promise": {
@@ -200,7 +237,8 @@ const es = {
"es6.reflect.define-property": "Reflect / Reflect.defineProperty",
"es6.reflect.delete-property": "Reflect / Reflect.deleteProperty",
"es6.reflect.get": "Reflect / Reflect.get",
- "es6.reflect.get-own-property-descriptor": "Reflect / Reflect.getOwnPropertyDescriptor",
+ "es6.reflect.get-own-property-descriptor":
+ "Reflect / Reflect.getOwnPropertyDescriptor",
"es6.reflect.get-prototype-of": "Reflect / Reflect.getPrototypeOf",
"es6.reflect.has": "Reflect / Reflect.has",
"es6.reflect.is-extensible": "Reflect / Reflect.isExtensible",
@@ -216,11 +254,16 @@ const es = {
],
},
"es6.regexp.flags": "RegExp.prototype properties / RegExp.prototype.flags",
- "es6.regexp.match": "RegExp.prototype properties / RegExp.prototype[Symbol.match]",
- "es6.regexp.replace": "RegExp.prototype properties / RegExp.prototype[Symbol.replace]",
- "es6.regexp.split": "RegExp.prototype properties / RegExp.prototype[Symbol.split]",
- "es6.regexp.search": "RegExp.prototype properties / RegExp.prototype[Symbol.search]",
- "es6.regexp.to-string": "miscellaneous / RegExp.prototype.toString generic and uses \"flags\" property",
+ "es6.regexp.match":
+ "RegExp.prototype properties / RegExp.prototype[Symbol.match]",
+ "es6.regexp.replace":
+ "RegExp.prototype properties / RegExp.prototype[Symbol.replace]",
+ "es6.regexp.split":
+ "RegExp.prototype properties / RegExp.prototype[Symbol.split]",
+ "es6.regexp.search":
+ "RegExp.prototype properties / RegExp.prototype[Symbol.search]",
+ "es6.regexp.to-string":
+ 'miscellaneous / RegExp.prototype.toString generic and uses "flags" property',
// This is explicit due to prevent the stage-1 Set proposals under the
// category "Set methods" from being included.
@@ -271,15 +314,18 @@ const es = {
"es6.string.big": "String.prototype HTML methods",
"es6.string.blink": "String.prototype HTML methods",
"es6.string.bold": "String.prototype HTML methods",
- "es6.string.code-point-at": "String.prototype methods / String.prototype.codePointAt",
- "es6.string.ends-with": "String.prototype methods / String.prototype.endsWith",
+ "es6.string.code-point-at":
+ "String.prototype methods / String.prototype.codePointAt",
+ "es6.string.ends-with":
+ "String.prototype methods / String.prototype.endsWith",
"es6.string.fixed": "String.prototype HTML methods",
"es6.string.fontcolor": "String.prototype HTML methods",
"es6.string.fontsize": "String.prototype HTML methods",
"es6.string.from-code-point": "String static methods / String.fromCodePoint",
"es6.string.includes": "String.prototype methods / String.prototype.includes",
"es6.string.italics": "String.prototype HTML methods",
- "es6.string.iterator": "String.prototype methods / String.prototype[Symbol.iterator]",
+ "es6.string.iterator":
+ "String.prototype methods / String.prototype[Symbol.iterator]",
"es6.string.link": "String.prototype HTML methods",
// "String.prototype methods / String.prototype.normalize" not implemented
"es7.string.pad-start": "String padding / String.prototype.padStart",
@@ -287,7 +333,8 @@ const es = {
"es6.string.raw": "String static methods / String.raw",
"es6.string.repeat": "String.prototype methods / String.prototype.repeat",
"es6.string.small": "String.prototype HTML methods",
- "es6.string.starts-with": "String.prototype methods / String.prototype.startsWith",
+ "es6.string.starts-with":
+ "String.prototype methods / String.prototype.startsWith",
"es6.string.strike": "String.prototype HTML methods",
"es6.string.sub": "String.prototype HTML methods",
"es6.string.sup": "String.prototype HTML methods",
@@ -329,7 +376,3 @@ const es = {
"es6.weak-set": "WeakSet",
};
-
-const proposals = require("./shipped-proposals").builtIns;
-
-module.exports = Object.assign({}, es, proposals);
diff --git a/packages/babel-compat-data/scripts/data/overlapping-plugins.js b/packages/babel-compat-data/scripts/data/overlapping-plugins.js
new file mode 100644
index 000000000000..56cb8d896d6f
--- /dev/null
+++ b/packages/babel-compat-data/scripts/data/overlapping-plugins.js
@@ -0,0 +1,16 @@
+"use strict";
+
+module.exports = new Map();
+
+// async -> regenerator is better than async -> generator -> regenerator
+ifIncluded("transform-regenerator")
+ // Temporarly disabled: https://github.com/babel/babel/issues/10678
+ // .isUnnecessary("transform-async-to-generator");
+
+function ifIncluded(name) {
+ const set = new Set();
+ module.exports.set(name, set);
+ return {
+ isUnnecessary(name) { set.add(name); return this; },
+ };
+}
diff --git a/packages/babel-preset-env/data/plugin-features.js b/packages/babel-compat-data/scripts/data/plugin-features.js
similarity index 85%
rename from packages/babel-preset-env/data/plugin-features.js
rename to packages/babel-compat-data/scripts/data/plugin-features.js
index 3a84be381683..e0f20c7d1acf 100644
--- a/packages/babel-preset-env/data/plugin-features.js
+++ b/packages/babel-compat-data/scripts/data/plugin-features.js
@@ -1,42 +1,36 @@
-const es = {
- "transform-template-literals": {
- features: ["template literals"],
- },
- "transform-literals": {
- features: ["Unicode code point escapes"],
- },
- "transform-function-name": {
- features: ['function "name" property'],
- },
+/* eslint sort-keys: "error" */
+
+module.exports = {
+ "proposal-async-generator-functions": "Asynchronous Iterators",
+ "proposal-json-strings": "JSON superset",
+ "proposal-nullish-coalescing-operator": "nullish coalescing operator (??)",
+ "proposal-object-rest-spread": "object rest/spread properties",
+ "proposal-optional-catch-binding": "optional catch binding",
+ "proposal-optional-chaining": "optional chaining operator (?.)",
+ "proposal-unicode-property-regex": "RegExp Unicode Property Escapes",
+
"transform-arrow-functions": {
features: ["arrow functions"],
},
+
+ "transform-async-to-generator": {
+ features: ["async functions"],
+ },
+
"transform-block-scoped-functions": {
features: ["block-level function declaration"],
},
+ "transform-block-scoping": {
+ features: ["const", "let"],
+ },
"transform-classes": {
features: ["class", "super"],
},
- "transform-object-super": {
- features: ["super"],
- },
- "transform-shorthand-properties": {
- features: ["object literal extensions / shorthand properties"],
- },
- "transform-duplicate-keys": {
- features: ["miscellaneous / duplicate property names in strict mode"],
- },
"transform-computed-properties": {
features: ["object literal extensions / computed properties"],
},
- "transform-for-of": {
- features: ["for..of loops"],
- },
- "transform-sticky-regex": {
- features: [
- 'RegExp "y" and "u" flags / "y" flag, lastIndex',
- 'RegExp "y" and "u" flags / "y" flag',
- ],
+ "transform-destructuring": {
+ features: ["destructuring, assignment", "destructuring, declarations"],
},
// We want to apply this prior to unicode regex so that "." and "u"
@@ -45,17 +39,29 @@ const es = {
// Ref: https://github.com/babel/babel/pull/7065#issuecomment-395959112
"transform-dotall-regex": "s (dotAll) flag for regular expressions",
- "transform-unicode-regex": {
- features: [
- 'RegExp "y" and "u" flags / "u" flag, case folding',
- 'RegExp "y" and "u" flags / "u" flag, Unicode code point escapes',
- 'RegExp "y" and "u" flags / "u" flag, non-BMP Unicode characters',
- 'RegExp "y" and "u" flags / "u" flag',
- ],
+ "transform-duplicate-keys": {
+ features: ["miscellaneous / duplicate property names in strict mode"],
},
-
- "transform-spread": {
- features: "spread syntax for iterable objects",
+ "transform-exponentiation-operator": {
+ features: ["exponentiation (**) operator"],
+ },
+ "transform-for-of": {
+ features: ["for..of loops"],
+ },
+ "transform-function-name": {
+ features: ['function "name" property'],
+ },
+ "transform-literals": {
+ features: ["Unicode code point escapes"],
+ },
+ "transform-member-expression-literals":
+ "Object/array literal extensions / Reserved words as property names",
+ "transform-named-capturing-groups-regex": "RegExp named capture groups",
+ "transform-new-target": {
+ features: ["new.target"],
+ },
+ "transform-object-super": {
+ features: ["super"],
},
"transform-parameters": {
features: [
@@ -64,45 +70,36 @@ const es = {
"destructuring, parameters / defaults, arrow function",
],
},
- "transform-destructuring": {
+ "transform-property-literals":
+ "Object/array literal extensions / Reserved words as property names",
+ "transform-regenerator": {
+ features: ["generators"],
+ },
+ "transform-reserved-words": "Miscellaneous / Unreserved words",
+ "transform-shorthand-properties": {
+ features: ["object literal extensions / shorthand properties"],
+ },
+ "transform-spread": {
+ features: "spread syntax for iterable objects",
+ },
+ "transform-sticky-regex": {
features: [
- "destructuring, assignment",
- "destructuring, declarations",
+ 'RegExp "y" and "u" flags / "y" flag, lastIndex',
+ 'RegExp "y" and "u" flags / "y" flag',
],
},
- "transform-block-scoping": {
- features: ["const", "let"],
+ "transform-template-literals": {
+ features: ["template literals"],
},
"transform-typeof-symbol": {
features: ["Symbol / typeof support"],
},
- "transform-new-target": {
- features: ["new.target"],
- },
- "transform-regenerator": {
- features: ["generators"],
- },
-
- "transform-exponentiation-operator": {
- features: ["exponentiation (**) operator"],
- },
-
- "transform-async-to-generator": {
- features: ["async functions"],
+ "transform-unicode-regex": {
+ features: [
+ 'RegExp "y" and "u" flags / "u" flag, case folding',
+ 'RegExp "y" and "u" flags / "u" flag, Unicode code point escapes',
+ 'RegExp "y" and "u" flags / "u" flag, non-BMP Unicode characters',
+ 'RegExp "y" and "u" flags / "u" flag',
+ ],
},
-
- "proposal-async-generator-functions": "Asynchronous Iterators",
- "proposal-object-rest-spread": "object rest/spread properties",
- "proposal-unicode-property-regex": "RegExp Unicode Property Escapes",
-
- "proposal-json-strings": "JSON superset",
- "proposal-optional-catch-binding": "optional catch binding",
- "transform-named-capturing-groups-regex": "RegExp named capture groups",
- "transform-member-expression-literals": "Object/array literal extensions / Reserved words as property names",
- "transform-property-literals": "Object/array literal extensions / Reserved words as property names",
- "transform-reserved-words": "Miscellaneous / Unreserved words",
};
-
-const proposals = require("./shipped-proposals").features;
-
-module.exports = Object.assign({}, es, proposals);
diff --git a/packages/babel-compat-data/scripts/download-compat-table.sh b/packages/babel-compat-data/scripts/download-compat-table.sh
new file mode 100755
index 000000000000..fd67e9d6d3ff
--- /dev/null
+++ b/packages/babel-compat-data/scripts/download-compat-table.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+set -e
+
+COMPAT_TABLE_COMMIT=4195aca631ad904cb0efeb62a9c2d8c8511706f8
+rm -rf build/compat-table
+mkdir -p build
+git clone --branch=gh-pages --single-branch --shallow-since=2019-11-14 https://github.com/kangax/compat-table.git build/compat-table
+cd build/compat-table && git checkout -qf $COMPAT_TABLE_COMMIT
diff --git a/packages/babel-core/package.json b/packages/babel-core/package.json
index 56efb4a95fc5..01c55c538843 100644
--- a/packages/babel-core/package.json
+++ b/packages/babel-core/package.json
@@ -1,6 +1,6 @@
{
"name": "@babel/core",
- "version": "7.5.5",
+ "version": "7.8.3",
"description": "Babel compiler core.",
"main": "lib/index.js",
"author": "Sebastian McKenzie ",
@@ -28,20 +28,27 @@
"engines": {
"node": ">=6.9.0"
},
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ },
"browser": {
"./lib/config/files/index.js": "./lib/config/files/index-browser.js",
- "./lib/transform-file.js": "./lib/transform-file-browser.js"
+ "./lib/transform-file.js": "./lib/transform-file-browser.js",
+ "./src/config/files/index.js": "./src/config/files/index-browser.js",
+ "./src/transform-file.js": "./src/transform-file-browser.js"
},
"dependencies": {
- "@babel/code-frame": "^7.5.5",
- "@babel/generator": "^7.5.5",
- "@babel/helpers": "^7.5.5",
- "@babel/parser": "^7.5.5",
- "@babel/template": "^7.4.4",
- "@babel/traverse": "^7.5.5",
- "@babel/types": "^7.5.5",
- "convert-source-map": "^1.1.0",
+ "@babel/code-frame": "^7.8.3",
+ "@babel/generator": "^7.8.3",
+ "@babel/helpers": "^7.8.3",
+ "@babel/parser": "^7.8.3",
+ "@babel/template": "^7.8.3",
+ "@babel/traverse": "^7.8.3",
+ "@babel/types": "^7.8.3",
+ "convert-source-map": "^1.7.0",
"debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.1",
"json5": "^2.1.0",
"lodash": "^4.17.13",
"resolve": "^1.3.2",
@@ -49,7 +56,6 @@
"source-map": "^0.5.0"
},
"devDependencies": {
- "@babel/helper-transform-fixture-test-runner": "^7.5.5",
- "@babel/register": "^7.5.5"
+ "@babel/helper-transform-fixture-test-runner": "^7.8.3"
}
}
diff --git a/packages/babel-core/src/config/caching.js b/packages/babel-core/src/config/caching.js
index 5b915e7f7767..fcad6e8fccbe 100644
--- a/packages/babel-core/src/config/caching.js
+++ b/packages/babel-core/src/config/caching.js
@@ -1,5 +1,17 @@
// @flow
+import gensync, { type Handler } from "gensync";
+import {
+ maybeAsync,
+ isAsync,
+ onFirstPause,
+ waitFor,
+ isThenable,
+} from "../gensync-utils/async";
+import { isIterableIterator } from "./util";
+
+export type { CacheConfigurator };
+
export type SimpleCacheConfigurator = SimpleCacheConfiguratorFn &
SimpleCacheConfiguratorObj;
@@ -14,91 +26,223 @@ type SimpleCacheConfiguratorObj = {
invalidate: (handler: () => T) => T,
};
-type CacheEntry = Array<{
+export type CacheEntry = Array<{
value: ResultT,
- valid: SideChannel => boolean,
+ valid: SideChannel => Handler,
}>;
-export type { CacheConfigurator };
+const synchronize = (
+ gen: (...ArgsT) => Handler,
+ // $FlowIssue https://github.com/facebook/flow/issues/7279
+): ((...args: ArgsT) => ResultT) => {
+ return gensync(gen).sync;
+};
-/**
- * Given a function with a single argument, cache its results based on its argument and how it
- * configures its caching behavior. Cached values are stored strongly.
- */
-export function makeStrongCache(
+// eslint-disable-next-line require-yield, no-unused-vars
+function* genTrue(data: any) {
+ return true;
+}
+
+export function makeWeakCache(
+ handler: (ArgT, CacheConfigurator) => Handler | ResultT,
+): (ArgT, SideChannel) => Handler {
+ return makeCachedFunction(WeakMap, handler);
+}
+
+export function makeWeakCacheSync(
handler: (ArgT, CacheConfigurator) => ResultT,
): (ArgT, SideChannel) => ResultT {
- return makeCachedFunction(new Map(), handler);
+ return synchronize<[ArgT, SideChannel], ResultT>(
+ makeWeakCache(handler),
+ );
}
-/**
- * Given a function with a single argument, cache its results based on its argument and how it
- * configures its caching behavior. Cached values are stored weakly and the function argument must be
- * an object type.
- */
-export function makeWeakCache<
- ArgT: {} | Array<*> | $ReadOnlyArray<*>,
- ResultT,
- SideChannel,
->(
+export function makeStrongCache(
+ handler: (ArgT, CacheConfigurator) => Handler | ResultT,
+): (ArgT, SideChannel) => Handler {
+ return makeCachedFunction(Map, handler);
+}
+
+export function makeStrongCacheSync(
handler: (ArgT, CacheConfigurator) => ResultT,
): (ArgT, SideChannel) => ResultT {
- return makeCachedFunction(new WeakMap(), handler);
+ return synchronize<[ArgT, SideChannel], ResultT>(
+ makeStrongCache(handler),
+ );
+}
+
+/* NOTE: Part of the logic explained in this comment is explained in the
+ * getCachedValueOrWait and setupAsyncLocks functions.
+ *
+ * > There are only two hard things in Computer Science: cache invalidation and naming things.
+ * > -- Phil Karlton
+ *
+ * I don't know if Phil was also thinking about handling a cache whose invalidation function is
+ * defined asynchronously is considered, but it is REALLY hard to do correctly.
+ *
+ * The implemented logic (only when gensync is run asynchronously) is the following:
+ * 1. If there is a valid cache associated to the current "arg" parameter,
+ * a. RETURN the cached value
+ * 3. If there is a FinishLock associated to the current "arg" parameter representing a valid cache,
+ * a. Wait for that lock to be released
+ * b. RETURN the value associated with that lock
+ * 5. Start executing the function to be cached
+ * a. If it pauses on a promise, then
+ * i. Let FinishLock be a new lock
+ * ii. Store FinishLock as associated to the current "arg" parameter
+ * iii. Wait for the function to finish executing
+ * iv. Release FinishLock
+ * v. Send the function result to anyone waiting on FinishLock
+ * 6. Store the result in the cache
+ * 7. RETURN the result
+ */
+function makeCachedFunction(
+ CallCache: Class,
+ handler: (ArgT, CacheConfigurator) => Handler | ResultT,
+): (ArgT, SideChannel) => Handler {
+ const callCacheSync = new CallCache();
+ const callCacheAsync = new CallCache();
+ const futureCache = new CallCache();
+
+ return function* cachedFunction(arg: ArgT, data: SideChannel) {
+ const asyncContext = yield* isAsync();
+ const callCache = asyncContext ? callCacheAsync : callCacheSync;
+
+ const cached = yield* getCachedValueOrWait(
+ asyncContext,
+ callCache,
+ futureCache,
+ arg,
+ data,
+ );
+ if (cached.valid) return cached.value;
+
+ const cache = new CacheConfigurator(data);
+
+ const handlerResult = handler(arg, cache);
+
+ let finishLock: ?Lock;
+ let value: ResultT;
+
+ if (isIterableIterator(handlerResult)) {
+ // Flow refines handlerResult to Generator
+ const gen = (handlerResult: Generator<*, ResultT, *>);
+
+ value = yield* onFirstPause(gen, () => {
+ finishLock = setupAsyncLocks(cache, futureCache, arg);
+ });
+ } else {
+ // $FlowIgnore doesn't refine handlerResult to ResultT
+ value = (handlerResult: ResultT);
+ }
+
+ updateFunctionCache(callCache, cache, arg, value);
+
+ if (finishLock) {
+ futureCache.delete(arg);
+ finishLock.release(value);
+ }
+
+ return value;
+ };
}
type CacheMap =
| Map>
| WeakMap>;
-function makeCachedFunction<
+function* getCachedValue<
ArgT,
ResultT,
SideChannel,
// $FlowIssue https://github.com/facebook/flow/issues/4528
Cache: CacheMap,
>(
- callCache: Cache,
- handler: (ArgT, CacheConfigurator) => ResultT,
-): (ArgT, SideChannel) => ResultT {
- return function cachedFunction(arg, data) {
- let cachedValue: CacheEntry | void = callCache.get(
- arg,
- );
-
- if (cachedValue) {
- for (const { value, valid } of cachedValue) {
- if (valid(data)) return value;
- }
+ cache: Cache,
+ arg: ArgT,
+ data: SideChannel,
+): Handler<{ valid: true, value: ResultT } | { valid: false, value: null }> {
+ const cachedValue: CacheEntry | void = cache.get(arg);
+
+ if (cachedValue) {
+ for (const { value, valid } of cachedValue) {
+ if (yield* valid(data)) return { valid: true, value };
}
+ }
- const cache = new CacheConfigurator(data);
+ return { valid: false, value: null };
+}
+
+function* getCachedValueOrWait(
+ asyncContext: boolean,
+ callCache: CacheMap,
+ futureCache: CacheMap, SideChannel>,
+ arg: ArgT,
+ data: SideChannel,
+): Handler<{ valid: true, value: ResultT } | { valid: false, value: null }> {
+ const cached = yield* getCachedValue(callCache, arg, data);
+ if (cached.valid) {
+ return cached;
+ }
- const value = handler(arg, cache);
-
- if (!cache.configured()) cache.forever();
-
- cache.deactivate();
-
- switch (cache.mode()) {
- case "forever":
- cachedValue = [{ value, valid: () => true }];
- callCache.set(arg, cachedValue);
- break;
- case "invalidate":
- cachedValue = [{ value, valid: cache.validator() }];
- callCache.set(arg, cachedValue);
- break;
- case "valid":
- if (cachedValue) {
- cachedValue.push({ value, valid: cache.validator() });
- } else {
- cachedValue = [{ value, valid: cache.validator() }];
- callCache.set(arg, cachedValue);
- }
+ if (asyncContext) {
+ const cached = yield* getCachedValue(futureCache, arg, data);
+ if (cached.valid) {
+ const value = yield* waitFor(cached.value.promise);
+ return { valid: true, value };
}
+ }
- return value;
- };
+ return { valid: false, value: null };
+}
+
+function setupAsyncLocks(
+ config: CacheConfigurator,
+ futureCache: CacheMap, SideChannel>,
+ arg: ArgT,
+): Lock {
+ const finishLock = new Lock();
+
+ updateFunctionCache(futureCache, config, arg, finishLock);
+
+ return finishLock;
+}
+
+function updateFunctionCache<
+ ArgT,
+ ResultT,
+ SideChannel,
+ // $FlowIssue https://github.com/facebook/flow/issues/4528
+ Cache: CacheMap,
+>(
+ cache: Cache,
+ config: CacheConfigurator,
+ arg: ArgT,
+ value: ResultT,
+) {
+ if (!config.configured()) config.forever();
+
+ let cachedValue: CacheEntry | void = cache.get(arg);
+
+ config.deactivate();
+
+ switch (config.mode()) {
+ case "forever":
+ cachedValue = [{ value, valid: genTrue }];
+ cache.set(arg, cachedValue);
+ break;
+ case "invalidate":
+ cachedValue = [{ value, valid: config.validator() }];
+ cache.set(arg, cachedValue);
+ break;
+ case "valid":
+ if (cachedValue) {
+ cachedValue.push({ value, valid: config.validator() });
+ } else {
+ cachedValue = [{ value, valid: config.validator() }];
+ cache.set(arg, cachedValue);
+ }
+ }
}
class CacheConfigurator {
@@ -109,7 +253,7 @@ class CacheConfigurator {
_configured: boolean = false;
- _pairs: Array<[mixed, (SideChannel) => mixed]> = [];
+ _pairs: Array<[mixed, (SideChannel) => Handler]> = [];
_data: SideChannel;
@@ -162,30 +306,36 @@ class CacheConfigurator {
this._configured = true;
const key = handler(this._data);
- this._pairs.push([key, handler]);
+
+ const fn = maybeAsync(
+ handler,
+ `You appear to be using an async cache handler, but Babel has been called synchronously`,
+ );
+
+ if (isThenable(key)) {
+ return key.then(key => {
+ this._pairs.push([key, fn]);
+ return key;
+ });
+ }
+
+ this._pairs.push([key, fn]);
return key;
}
invalidate(handler: SideChannel => T): T {
- if (!this._active) {
- throw new Error("Cannot change caching after evaluation has completed.");
- }
- if (this._never || this._forever) {
- throw new Error(
- "Caching has already been configured with .never or .forever()",
- );
- }
this._invalidate = true;
- this._configured = true;
-
- const key = handler(this._data);
- this._pairs.push([key, handler]);
- return key;
+ return this.using(handler);
}
- validator(): SideChannel => boolean {
+ validator(): SideChannel => Handler {
const pairs = this._pairs;
- return (data: SideChannel) => pairs.every(([key, fn]) => key === fn(data));
+ return function*(data: SideChannel) {
+ for (const [key, fn] of pairs) {
+ if (key !== (yield* fn(data))) return false;
+ }
+ return true;
+ };
}
deactivate() {
@@ -219,8 +369,18 @@ function makeSimpleConfigurator(
// Types are limited here so that in the future these values can be used
// as part of Babel's caching logic.
-type SimpleType = string | boolean | number | null | void;
+type SimpleType = string | boolean | number | null | void | Promise;
export function assertSimpleType(value: mixed): SimpleType {
+ if (isThenable(value)) {
+ throw new Error(
+ `You appear to be using an async cache handler, ` +
+ `which your current version of Babel does not support. ` +
+ `We may add support for this in the future, ` +
+ `but if you're on the most recent version of @babel/core and still ` +
+ `seeing this error, then you'll need to synchronously handle your caching logic.`,
+ );
+ }
+
if (
value != null &&
typeof value !== "string" &&
@@ -233,3 +393,20 @@ export function assertSimpleType(value: mixed): SimpleType {
}
return value;
}
+
+class Lock {
+ released: boolean = false;
+ promise: Promise;
+ _resolve: (value: T) => void;
+
+ constructor() {
+ this.promise = new Promise(resolve => {
+ this._resolve = resolve;
+ });
+ }
+
+ release(value: T) {
+ this.released = true;
+ this._resolve(value);
+ }
+}
diff --git a/packages/babel-core/src/config/config-chain.js b/packages/babel-core/src/config/config-chain.js
index 3bbedcdff1a9..1af3f35d2371 100644
--- a/packages/babel-core/src/config/config-chain.js
+++ b/packages/babel-core/src/config/config-chain.js
@@ -2,6 +2,7 @@
import path from "path";
import buildDebug from "debug";
+import type { Handler } from "gensync";
import {
validate,
type ValidatedOptions,
@@ -24,7 +25,7 @@ import {
type FilePackageData,
} from "./files";
-import { makeWeakCache, makeStrongCache } from "./caching";
+import { makeWeakCacheSync, makeStrongCacheSync } from "./caching";
import {
createCachedDescriptors,
@@ -57,11 +58,11 @@ export type ConfigContext = {
/**
* Build a config chain for a given preset.
*/
-export function buildPresetChain(
+export function* buildPresetChain(
arg: PresetInstance,
context: *,
-): ConfigChain | null {
- const chain = buildPresetChainWalker(arg, context);
+): Handler {
+ const chain = yield* buildPresetChainWalker(arg, context);
if (!chain) return null;
return {
@@ -82,11 +83,11 @@ export const buildPresetChainWalker: (
overridesEnv: (preset, index, envName) =>
loadPresetOverridesEnvDescriptors(preset)(index)(envName),
});
-const loadPresetDescriptors = makeWeakCache((preset: PresetInstance) =>
+const loadPresetDescriptors = makeWeakCacheSync((preset: PresetInstance) =>
buildRootDescriptors(preset, preset.alias, createUncachedDescriptors),
);
-const loadPresetEnvDescriptors = makeWeakCache((preset: PresetInstance) =>
- makeStrongCache((envName: string) =>
+const loadPresetEnvDescriptors = makeWeakCacheSync((preset: PresetInstance) =>
+ makeStrongCacheSync((envName: string) =>
buildEnvDescriptors(
preset,
preset.alias,
@@ -95,20 +96,21 @@ const loadPresetEnvDescriptors = makeWeakCache((preset: PresetInstance) =>
),
),
);
-const loadPresetOverridesDescriptors = makeWeakCache((preset: PresetInstance) =>
- makeStrongCache((index: number) =>
- buildOverrideDescriptors(
- preset,
- preset.alias,
- createUncachedDescriptors,
- index,
+const loadPresetOverridesDescriptors = makeWeakCacheSync(
+ (preset: PresetInstance) =>
+ makeStrongCacheSync((index: number) =>
+ buildOverrideDescriptors(
+ preset,
+ preset.alias,
+ createUncachedDescriptors,
+ index,
+ ),
),
- ),
);
-const loadPresetOverridesEnvDescriptors = makeWeakCache(
+const loadPresetOverridesEnvDescriptors = makeWeakCacheSync(
(preset: PresetInstance) =>
- makeStrongCache((index: number) =>
- makeStrongCache((envName: string) =>
+ makeStrongCacheSync((index: number) =>
+ makeStrongCacheSync((envName: string) =>
buildOverrideEnvDescriptors(
preset,
preset.alias,
@@ -129,11 +131,11 @@ export type RootConfigChain = ConfigChain & {
/**
* Build a config chain for Babel's full root configuration.
*/
-export function buildRootChain(
+export function* buildRootChain(
opts: ValidatedOptions,
context: ConfigContext,
-): RootConfigChain | null {
- const programmaticChain = loadProgrammaticChain(
+): Handler {
+ const programmaticChain = yield* loadProgrammaticChain(
{
options: opts,
dirname: context.cwd,
@@ -144,14 +146,18 @@ export function buildRootChain(
let configFile;
if (typeof opts.configFile === "string") {
- configFile = loadConfig(
+ configFile = yield* loadConfig(
opts.configFile,
context.cwd,
context.envName,
context.caller,
);
} else if (opts.configFile !== false) {
- configFile = findRootConfig(context.root, context.envName, context.caller);
+ configFile = yield* findRootConfig(
+ context.root,
+ context.envName,
+ context.caller,
+ );
}
let { babelrc, babelrcRoots } = opts;
@@ -160,7 +166,7 @@ export function buildRootChain(
const configFileChain = emptyChain();
if (configFile) {
const validatedFile = validateConfigFile(configFile);
- const result = loadFileChain(validatedFile, context);
+ const result = yield* loadFileChain(validatedFile, context);
if (!result) return null;
// Allow config files to toggle `.babelrc` resolution on and off and
@@ -178,7 +184,7 @@ export function buildRootChain(
const pkgData =
typeof context.filename === "string"
- ? findPackageData(context.filename)
+ ? yield* findPackageData(context.filename)
: null;
let ignoreFile, babelrcFile;
@@ -189,7 +195,7 @@ export function buildRootChain(
pkgData &&
babelrcLoadEnabled(context, pkgData, babelrcRoots, babelrcRootsDirectory)
) {
- ({ ignore: ignoreFile, config: babelrcFile } = findRelativeConfig(
+ ({ ignore: ignoreFile, config: babelrcFile } = yield* findRelativeConfig(
pkgData,
context.envName,
context.caller,
@@ -203,7 +209,10 @@ export function buildRootChain(
}
if (babelrcFile) {
- const result = loadFileChain(validateBabelrcFile(babelrcFile), context);
+ const result = yield* loadFileChain(
+ validateBabelrcFile(babelrcFile),
+ context,
+ );
if (!result) return null;
mergeChain(fileChain, result);
@@ -268,7 +277,7 @@ function babelrcLoadEnabled(
});
}
-const validateConfigFile = makeWeakCache(
+const validateConfigFile = makeWeakCacheSync(
(file: ConfigFile): ValidatedFile => ({
filepath: file.filepath,
dirname: file.dirname,
@@ -276,7 +285,7 @@ const validateConfigFile = makeWeakCache(
}),
);
-const validateBabelrcFile = makeWeakCache(
+const validateBabelrcFile = makeWeakCacheSync(
(file: ConfigFile): ValidatedFile => ({
filepath: file.filepath,
dirname: file.dirname,
@@ -284,7 +293,7 @@ const validateBabelrcFile = makeWeakCache(
}),
);
-const validateExtendFile = makeWeakCache(
+const validateExtendFile = makeWeakCacheSync(
(file: ConfigFile): ValidatedFile => ({
filepath: file.filepath,
dirname: file.dirname,
@@ -321,11 +330,11 @@ const loadFileChain = makeChainWalker({
overridesEnv: (file, index, envName) =>
loadFileOverridesEnvDescriptors(file)(index)(envName),
});
-const loadFileDescriptors = makeWeakCache((file: ValidatedFile) =>
+const loadFileDescriptors = makeWeakCacheSync((file: ValidatedFile) =>
buildRootDescriptors(file, file.filepath, createUncachedDescriptors),
);
-const loadFileEnvDescriptors = makeWeakCache((file: ValidatedFile) =>
- makeStrongCache((envName: string) =>
+const loadFileEnvDescriptors = makeWeakCacheSync((file: ValidatedFile) =>
+ makeStrongCacheSync((envName: string) =>
buildEnvDescriptors(
file,
file.filepath,
@@ -334,8 +343,8 @@ const loadFileEnvDescriptors = makeWeakCache((file: ValidatedFile) =>
),
),
);
-const loadFileOverridesDescriptors = makeWeakCache((file: ValidatedFile) =>
- makeStrongCache((index: number) =>
+const loadFileOverridesDescriptors = makeWeakCacheSync((file: ValidatedFile) =>
+ makeStrongCacheSync((index: number) =>
buildOverrideDescriptors(
file,
file.filepath,
@@ -344,18 +353,19 @@ const loadFileOverridesDescriptors = makeWeakCache((file: ValidatedFile) =>
),
),
);
-const loadFileOverridesEnvDescriptors = makeWeakCache((file: ValidatedFile) =>
- makeStrongCache((index: number) =>
- makeStrongCache((envName: string) =>
- buildOverrideEnvDescriptors(
- file,
- file.filepath,
- createUncachedDescriptors,
- index,
- envName,
+const loadFileOverridesEnvDescriptors = makeWeakCacheSync(
+ (file: ValidatedFile) =>
+ makeStrongCacheSync((index: number) =>
+ makeStrongCacheSync((envName: string) =>
+ buildOverrideEnvDescriptors(
+ file,
+ file.filepath,
+ createUncachedDescriptors,
+ index,
+ envName,
+ ),
),
),
- ),
);
function buildRootDescriptors({ dirname, options }, alias, descriptors) {
@@ -414,8 +424,12 @@ function makeChainWalker({
env: (ArgT, string) => OptionsAndDescriptors | null,
overrides: (ArgT, number) => OptionsAndDescriptors,
overridesEnv: (ArgT, number, string) => OptionsAndDescriptors | null,
-}): (ArgT, ConfigContext, Set | void) => ConfigChain | null {
- return (input, context, files = new Set()) => {
+}): (
+ ArgT,
+ ConfigContext,
+ Set | void,
+) => Handler {
+ return function*(input, context, files = new Set()) {
const { dirname } = input;
const flattenedConfigs = [];
@@ -459,7 +473,9 @@ function makeChainWalker({
const chain = emptyChain();
for (const op of flattenedConfigs) {
- if (!mergeExtendsChain(chain, op.options, dirname, context, files)) {
+ if (
+ !(yield* mergeExtendsChain(chain, op.options, dirname, context, files))
+ ) {
return null;
}
@@ -469,16 +485,16 @@ function makeChainWalker({
};
}
-function mergeExtendsChain(
+function* mergeExtendsChain(
chain: ConfigChain,
opts: ValidatedOptions,
dirname: string,
context: ConfigContext,
files: Set,
-): boolean {
+): Handler {
if (opts.extends === undefined) return true;
- const file = loadConfig(
+ const file = yield* loadConfig(
opts.extends,
dirname,
context.envName,
@@ -494,7 +510,11 @@ function mergeExtendsChain(
}
files.add(file);
- const fileChain = loadFileChain(validateExtendFile(file), context, files);
+ const fileChain = yield* loadFileChain(
+ validateExtendFile(file),
+ context,
+ files,
+ );
files.delete(file);
if (!fileChain) return false;
@@ -549,7 +569,7 @@ function normalizeOptions(opts: ValidatedOptions): ValidatedOptions {
// "sourceMap" is just aliased to sourceMap, so copy it over as
// we merge the options together.
- if (options.hasOwnProperty("sourceMap")) {
+ if (Object.prototype.hasOwnProperty.call(options, "sourceMap")) {
options.sourceMaps = options.sourceMap;
delete options.sourceMap;
}
diff --git a/packages/babel-core/src/config/config-descriptors.js b/packages/babel-core/src/config/config-descriptors.js
index e17cca93c680..fa60d47d7139 100644
--- a/packages/babel-core/src/config/config-descriptors.js
+++ b/packages/babel-core/src/config/config-descriptors.js
@@ -5,8 +5,8 @@ import { loadPlugin, loadPreset } from "./files";
import { getItemDescriptor } from "./item";
import {
- makeWeakCache,
- makeStrongCache,
+ makeWeakCacheSync,
+ makeStrongCacheSync,
type CacheConfigurator,
} from "./caching";
@@ -130,11 +130,11 @@ export function createUncachedDescriptors(
}
const PRESET_DESCRIPTOR_CACHE = new WeakMap();
-const createCachedPresetDescriptors = makeWeakCache(
+const createCachedPresetDescriptors = makeWeakCacheSync(
(items: PluginList, cache: CacheConfigurator) => {
const dirname = cache.using(dir => dir);
- return makeStrongCache((alias: string) =>
- makeStrongCache((passPerPreset: boolean) =>
+ return makeStrongCacheSync((alias: string) =>
+ makeStrongCacheSync((passPerPreset: boolean) =>
createPresetDescriptors(items, dirname, alias, passPerPreset).map(
// Items are cached using the overall preset array identity when
// possibly, but individual descriptors are also cached if a match
@@ -147,10 +147,10 @@ const createCachedPresetDescriptors = makeWeakCache(
);
const PLUGIN_DESCRIPTOR_CACHE = new WeakMap();
-const createCachedPluginDescriptors = makeWeakCache(
+const createCachedPluginDescriptors = makeWeakCacheSync(
(items: PluginList, cache: CacheConfigurator) => {
const dirname = cache.using(dir => dir);
- return makeStrongCache((alias: string) =>
+ return makeStrongCacheSync((alias: string) =>
createPluginDescriptors(items, dirname, alias).map(
// Items are cached using the overall plugin array identity when
// possibly, but individual descriptors are also cached if a match
@@ -345,6 +345,7 @@ function assertNoDuplicates(items: Array): void {
}
if (nameMap.has(item.name)) {
+ const conflicts = items.filter(i => i.value === item.value);
throw new Error(
[
`Duplicate plugin/preset detected.`,
@@ -355,6 +356,9 @@ function assertNoDuplicates(items: Array): void {
` ['some-plugin', {}],`,
` ['some-plugin', {}, 'some unique name'],`,
` ]`,
+ ``,
+ `Duplicates detected are:`,
+ `${JSON.stringify(conflicts, null, 2)}`,
].join("\n"),
);
}
diff --git a/packages/babel-core/src/config/files/configuration.js b/packages/babel-core/src/config/files/configuration.js
index ab3353f6fde3..611cd49223e2 100644
--- a/packages/babel-core/src/config/files/configuration.js
+++ b/packages/babel-core/src/config/files/configuration.js
@@ -2,33 +2,48 @@
import buildDebug from "debug";
import path from "path";
-import fs from "fs";
import json5 from "json5";
-import resolve from "resolve";
+import gensync, { type Handler } from "gensync";
import {
makeStrongCache,
- makeWeakCache,
+ makeWeakCacheSync,
type CacheConfigurator,
} from "../caching";
import makeAPI, { type PluginAPI } from "../helpers/config-api";
import { makeStaticFileCache } from "./utils";
+import loadCjsOrMjsDefault from "./module-types";
import pathPatternToRegex from "../pattern-to-regex";
import type { FilePackageData, RelativeConfig, ConfigFile } from "./types";
import type { CallerMetadata } from "../validation/options";
+import * as fs from "../../gensync-utils/fs";
+import resolve from "../../gensync-utils/resolve";
+
const debug = buildDebug("babel:config:loading:files:configuration");
-const BABEL_CONFIG_JS_FILENAME = "babel.config.js";
+export const ROOT_CONFIG_FILENAMES = [
+ "babel.config.js",
+ "babel.config.cjs",
+ "babel.config.mjs",
+ "babel.config.json",
+];
+const RELATIVE_CONFIG_FILENAMES = [
+ ".babelrc",
+ ".babelrc.js",
+ ".babelrc.cjs",
+ ".babelrc.mjs",
+ ".babelrc.json",
+];
-const BABELRC_FILENAME = ".babelrc";
-const BABELRC_JS_FILENAME = ".babelrc.js";
const BABELIGNORE_FILENAME = ".babelignore";
-export function findConfigUpwards(rootDir: string): string | null {
+export function* findConfigUpwards(rootDir: string): Handler {
let dirname = rootDir;
while (true) {
- if (fs.existsSync(path.join(dirname, BABEL_CONFIG_JS_FILENAME))) {
- return dirname;
+ for (const filename of ROOT_CONFIG_FILENAMES) {
+ if (yield* fs.exists(path.join(dirname, filename))) {
+ return dirname;
+ }
}
const nextDir = path.dirname(dirname);
@@ -39,11 +54,11 @@ export function findConfigUpwards(rootDir: string): string | null {
return null;
}
-export function findRelativeConfig(
+export function* findRelativeConfig(
packageData: FilePackageData,
envName: string,
caller: CallerMetadata | void,
-): RelativeConfig {
+): Handler {
let config = null;
let ignore = null;
@@ -51,50 +66,20 @@ export function findRelativeConfig(
for (const loc of packageData.directories) {
if (!config) {
- config = [BABELRC_FILENAME, BABELRC_JS_FILENAME].reduce(
- (previousConfig: ConfigFile | null, name) => {
- const filepath = path.join(loc, name);
- const config = readConfig(filepath, envName, caller);
-
- if (config && previousConfig) {
- throw new Error(
- `Multiple configuration files found. Please remove one:\n` +
- ` - ${path.basename(previousConfig.filepath)}\n` +
- ` - ${name}\n` +
- `from ${loc}`,
- );
- }
-
- return config || previousConfig;
- },
- null,
- );
-
- const pkgConfig =
+ config = yield* loadOneConfig(
+ RELATIVE_CONFIG_FILENAMES,
+ loc,
+ envName,
+ caller,
packageData.pkg && packageData.pkg.dirname === loc
? packageToBabelConfig(packageData.pkg)
- : null;
-
- if (pkgConfig) {
- if (config) {
- throw new Error(
- `Multiple configuration files found. Please remove one:\n` +
- ` - ${path.basename(pkgConfig.filepath)}#babel\n` +
- ` - ${path.basename(config.filepath)}\n` +
- `from ${loc}`,
- );
- }
- config = pkgConfig;
- }
-
- if (config) {
- debug("Found configuration %o from %o.", config.filepath, dirname);
- }
+ : null,
+ );
}
if (!ignore) {
const ignoreLoc = path.join(loc, BABELIGNORE_FILENAME);
- ignore = readIgnoreConfig(ignoreLoc);
+ ignore = yield* readIgnoreConfig(ignoreLoc);
if (ignore) {
debug("Found ignore %o from %o.", ignore.filepath, dirname);
@@ -109,25 +94,50 @@ export function findRootConfig(
dirname: string,
envName: string,
caller: CallerMetadata | void,
-): ConfigFile | null {
- const filepath = path.resolve(dirname, BABEL_CONFIG_JS_FILENAME);
+): Handler {
+ return loadOneConfig(ROOT_CONFIG_FILENAMES, dirname, envName, caller);
+}
- const conf = readConfig(filepath, envName, caller);
- if (conf) {
- debug("Found root config %o in %o.", BABEL_CONFIG_JS_FILENAME, dirname);
+function* loadOneConfig(
+ names: string[],
+ dirname: string,
+ envName: string,
+ caller: CallerMetadata | void,
+ previousConfig?: ConfigFile | null = null,
+): Handler {
+ const configs = yield* gensync.all(
+ names.map(filename =>
+ readConfig(path.join(dirname, filename), envName, caller),
+ ),
+ );
+ const config = configs.reduce((previousConfig: ConfigFile | null, config) => {
+ if (config && previousConfig) {
+ throw new Error(
+ `Multiple configuration files found. Please remove one:\n` +
+ ` - ${path.basename(previousConfig.filepath)}\n` +
+ ` - ${config.filepath}\n` +
+ `from ${dirname}`,
+ );
+ }
+
+ return config || previousConfig;
+ }, previousConfig);
+
+ if (config) {
+ debug("Found configuration %o from %o.", config.filepath, dirname);
}
- return conf;
+ return config;
}
-export function loadConfig(
+export function* loadConfig(
name: string,
dirname: string,
envName: string,
caller: CallerMetadata | void,
-): ConfigFile {
- const filepath = resolve.sync(name, { basedir: dirname });
+): Handler {
+ const filepath = yield* resolve(name, { basedir: dirname });
- const conf = readConfig(filepath, envName, caller);
+ const conf = yield* readConfig(filepath, envName, caller);
if (!conf) {
throw new Error(`Config file ${filepath} contains no configuration data`);
}
@@ -140,89 +150,90 @@ export function loadConfig(
* Read the given config file, returning the result. Returns null if no config was found, but will
* throw if there are parsing errors while loading a config.
*/
-function readConfig(filepath, envName, caller): ConfigFile | null {
- return path.extname(filepath) === ".js"
+function readConfig(filepath, envName, caller) {
+ const ext = path.extname(filepath);
+ return ext === ".js" || ext === ".cjs" || ext === ".mjs"
? readConfigJS(filepath, { envName, caller })
: readConfigJSON5(filepath);
}
const LOADING_CONFIGS = new Set();
-const readConfigJS = makeStrongCache(
- (
- filepath: string,
- cache: CacheConfigurator<{
- envName: string,
- caller: CallerMetadata | void,
- }>,
- ) => {
- if (!fs.existsSync(filepath)) {
- cache.forever();
- return null;
- }
+const readConfigJS = makeStrongCache(function* readConfigJS(
+ filepath: string,
+ cache: CacheConfigurator<{
+ envName: string,
+ caller: CallerMetadata | void,
+ }>,
+): Handler {
+ if (!fs.exists.sync(filepath)) {
+ cache.forever();
+ return null;
+ }
- // The `require()` call below can make this code reentrant if a require hook like @babel/register has been
- // loaded into the system. That would cause Babel to attempt to compile the `.babelrc.js` file as it loads
- // below. To cover this case, we auto-ignore re-entrant config processing.
- if (LOADING_CONFIGS.has(filepath)) {
- cache.never();
-
- debug("Auto-ignoring usage of config %o.", filepath);
- return {
- filepath,
- dirname: path.dirname(filepath),
- options: {},
- };
- }
+ // The `require()` call below can make this code reentrant if a require hook like @babel/register has been
+ // loaded into the system. That would cause Babel to attempt to compile the `.babelrc.js` file as it loads
+ // below. To cover this case, we auto-ignore re-entrant config processing.
+ if (LOADING_CONFIGS.has(filepath)) {
+ cache.never();
- let options;
- try {
- LOADING_CONFIGS.add(filepath);
-
- // $FlowIssue
- const configModule = (require(filepath): mixed);
- options =
- configModule && configModule.__esModule
- ? configModule.default || undefined
- : configModule;
- } catch (err) {
- err.message = `${filepath}: Error while loading config - ${err.message}`;
- throw err;
- } finally {
- LOADING_CONFIGS.delete(filepath);
- }
+ debug("Auto-ignoring usage of config %o.", filepath);
+ return {
+ filepath,
+ dirname: path.dirname(filepath),
+ options: {},
+ };
+ }
- if (typeof options === "function") {
- options = ((options: any): (api: PluginAPI) => {})(makeAPI(cache));
+ let options: mixed;
+ try {
+ LOADING_CONFIGS.add(filepath);
+ options = (yield* loadCjsOrMjsDefault(
+ filepath,
+ "You appear to be using a native ECMAScript module configuration " +
+ "file, which is only supported when running Babel asynchronously.",
+ ): mixed);
+ } catch (err) {
+ err.message = `${filepath}: Error while loading config - ${err.message}`;
+ throw err;
+ } finally {
+ LOADING_CONFIGS.delete(filepath);
+ }
- if (!cache.configured()) throwConfigError();
- }
+ let assertCache = false;
+ if (typeof options === "function") {
+ yield* []; // if we want to make it possible to use async configs
+ options = ((options: any): (api: PluginAPI) => {})(makeAPI(cache));
- if (!options || typeof options !== "object" || Array.isArray(options)) {
- throw new Error(
- `${filepath}: Configuration should be an exported JavaScript object.`,
- );
- }
+ assertCache = true;
+ }
- if (typeof options.then === "function") {
- throw new Error(
- `You appear to be using an async configuration, ` +
- `which your current version of Babel does not support. ` +
- `We may add support for this in the future, ` +
- `but if you're on the most recent version of @babel/core and still ` +
- `seeing this error, then you'll need to synchronously return your config.`,
- );
- }
+ if (!options || typeof options !== "object" || Array.isArray(options)) {
+ throw new Error(
+ `${filepath}: Configuration should be an exported JavaScript object.`,
+ );
+ }
- return {
- filepath,
- dirname: path.dirname(filepath),
- options,
- };
- },
-);
+ if (typeof options.then === "function") {
+ throw new Error(
+ `You appear to be using an async configuration, ` +
+ `which your current version of Babel does not support. ` +
+ `We may add support for this in the future, ` +
+ `but if you're on the most recent version of @babel/core and still ` +
+ `seeing this error, then you'll need to synchronously return your config.`,
+ );
+ }
+
+ if (assertCache && !cache.configured()) throwConfigError();
+
+ return {
+ filepath,
+ dirname: path.dirname(filepath),
+ options,
+ };
+});
-const packageToBabelConfig = makeWeakCache(
+const packageToBabelConfig = makeWeakCacheSync(
(file: ConfigFile): ConfigFile | null => {
const babel = file.options[("babel": string)];
diff --git a/packages/babel-core/src/config/files/import.js b/packages/babel-core/src/config/files/import.js
new file mode 100644
index 000000000000..e16bc29752a9
--- /dev/null
+++ b/packages/babel-core/src/config/files/import.js
@@ -0,0 +1,7 @@
+// We keep this in a seprate file so that in older node versions, where
+// import() isn't supported, we can try/catch around the require() call
+// when loading this file.
+
+export default function import_(filepath: string) {
+ return import(filepath);
+}
diff --git a/packages/babel-core/src/config/files/index-browser.js b/packages/babel-core/src/config/files/index-browser.js
index 1d2adccaa811..8e3a8cf26ce2 100644
--- a/packages/babel-core/src/config/files/index-browser.js
+++ b/packages/babel-core/src/config/files/index-browser.js
@@ -1,5 +1,7 @@
// @flow
+import type { Handler } from "gensync";
+
import type {
ConfigFile,
IgnoreFile,
@@ -11,13 +13,15 @@ import type { CallerMetadata } from "../validation/options";
export type { ConfigFile, IgnoreFile, RelativeConfig, FilePackageData };
-export function findConfigUpwards(
+// eslint-disable-next-line require-yield
+export function* findConfigUpwards(
rootDir: string, // eslint-disable-line no-unused-vars
-): string | null {
+): Handler {
return null;
}
-export function findPackageData(filepath: string): FilePackageData {
+// eslint-disable-next-line require-yield
+export function* findPackageData(filepath: string): Handler {
return {
filepath,
directories: [],
@@ -26,31 +30,36 @@ export function findPackageData(filepath: string): FilePackageData {
};
}
-export function findRelativeConfig(
+// eslint-disable-next-line require-yield
+export function* findRelativeConfig(
pkgData: FilePackageData, // eslint-disable-line no-unused-vars
envName: string, // eslint-disable-line no-unused-vars
caller: CallerMetadata | void, // eslint-disable-line no-unused-vars
-): RelativeConfig {
+): Handler {
return { pkg: null, config: null, ignore: null };
}
-export function findRootConfig(
+// eslint-disable-next-line require-yield
+export function* findRootConfig(
dirname: string, // eslint-disable-line no-unused-vars
envName: string, // eslint-disable-line no-unused-vars
caller: CallerMetadata | void, // eslint-disable-line no-unused-vars
-): ConfigFile | null {
+): Handler {
return null;
}
-export function loadConfig(
+// eslint-disable-next-line require-yield
+export function* loadConfig(
name: string,
dirname: string,
envName: string, // eslint-disable-line no-unused-vars
caller: CallerMetadata | void, // eslint-disable-line no-unused-vars
-): ConfigFile {
+): Handler {
throw new Error(`Cannot load ${name} relative to ${dirname} in a browser`);
}
+export const ROOT_CONFIG_FILENAMES = [];
+
// eslint-disable-next-line no-unused-vars
export function resolvePlugin(name: string, dirname: string): string | null {
return null;
diff --git a/packages/babel-core/src/config/files/index.js b/packages/babel-core/src/config/files/index.js
index 3c74f2d4eebb..464ef66253f4 100644
--- a/packages/babel-core/src/config/files/index.js
+++ b/packages/babel-core/src/config/files/index.js
@@ -14,6 +14,7 @@ export {
findRelativeConfig,
findRootConfig,
loadConfig,
+ ROOT_CONFIG_FILENAMES,
} from "./configuration";
export type {
ConfigFile,
diff --git a/packages/babel-core/src/config/files/module-types.js b/packages/babel-core/src/config/files/module-types.js
new file mode 100644
index 000000000000..f90fb40b516d
--- /dev/null
+++ b/packages/babel-core/src/config/files/module-types.js
@@ -0,0 +1,59 @@
+import { isAsync, waitFor } from "../../gensync-utils/async";
+import type { Handler } from "gensync";
+import path from "path";
+
+let import_;
+try {
+ // Node < 13.3 doesn't support import() syntax.
+ import_ = require("./import").default;
+} catch {}
+
+export default function* loadCjsOrMjsDefault(
+ filepath: string,
+ asyncError: string,
+): Handler {
+ switch (guessJSModuleType(filepath)) {
+ case "cjs":
+ return loadCjsDefault(filepath);
+ case "unknown":
+ try {
+ return loadCjsDefault(filepath);
+ } catch (e) {
+ if (e.code !== "ERR_REQUIRE_ESM") throw e;
+ }
+ case "mjs":
+ if (yield* isAsync()) {
+ return yield* waitFor(loadMjsDefault(filepath));
+ }
+ throw new Error(asyncError);
+ }
+}
+
+function guessJSModuleType(filename: string): "cjs" | "mjs" | "unknown" {
+ switch (path.extname(filename)) {
+ case ".cjs":
+ return "cjs";
+ case ".mjs":
+ return "mjs";
+ default:
+ return "unknown";
+ }
+}
+
+function loadCjsDefault(filepath: string) {
+ const module = (require(filepath): mixed);
+ // TODO (Babel 8): Remove "undefined" fallback
+ return module?.__esModule ? module.default || undefined : module;
+}
+
+async function loadMjsDefault(filepath: string) {
+ if (!import_) {
+ throw new Error(
+ "Internal error: Native ECMAScript modules aren't supported" +
+ " by this platform.\n",
+ );
+ }
+
+ const module = await import_(filepath);
+ return module.default;
+}
diff --git a/packages/babel-core/src/config/files/package.js b/packages/babel-core/src/config/files/package.js
index 8e370a472d56..278ddfb852f5 100644
--- a/packages/babel-core/src/config/files/package.js
+++ b/packages/babel-core/src/config/files/package.js
@@ -1,6 +1,7 @@
// @flow
import path from "path";
+import type { Handler } from "gensync";
import { makeStaticFileCache } from "./utils";
import type { ConfigFile, FilePackageData } from "./types";
@@ -12,7 +13,7 @@ const PACKAGE_FILENAME = "package.json";
* of Babel's config requires general package information to decide when to
* search for .babelrc files
*/
-export function findPackageData(filepath: string): FilePackageData {
+export function* findPackageData(filepath: string): Handler {
let pkg = null;
const directories = [];
let isPackage = true;
@@ -21,7 +22,7 @@ export function findPackageData(filepath: string): FilePackageData {
while (!pkg && path.basename(dirname) !== "node_modules") {
directories.push(dirname);
- pkg = readConfigPackage(path.join(dirname, PACKAGE_FILENAME));
+ pkg = yield* readConfigPackage(path.join(dirname, PACKAGE_FILENAME));
const nextLoc = path.dirname(dirname);
if (dirname === nextLoc) {
diff --git a/packages/babel-core/src/config/files/utils.js b/packages/babel-core/src/config/files/utils.js
index db202259d5e8..61c3b24a7a4a 100644
--- a/packages/babel-core/src/config/files/utils.js
+++ b/packages/babel-core/src/config/files/utils.js
@@ -1,24 +1,32 @@
// @flow
-import fs from "fs";
-import { makeStrongCache } from "../caching";
+import type { Gensync, Handler } from "gensync";
+
+import { makeStrongCache, type CacheConfigurator } from "../caching";
+import * as fs from "../../gensync-utils/fs";
+import nodeFs from "fs";
export function makeStaticFileCache(
fn: (string, string) => T,
-): string => T | null {
- return makeStrongCache((filepath, cache) => {
- if (cache.invalidate(() => fileMtime(filepath)) === null) {
+): Gensync<[string], T | null> {
+ return (makeStrongCache(function*(
+ filepath: string,
+ cache: CacheConfigurator,
+ ): Handler {
+ const cached = cache.invalidate(() => fileMtime(filepath));
+
+ if (cached === null) {
cache.forever();
return null;
}
- return fn(filepath, fs.readFileSync(filepath, "utf8"));
- });
+ return fn(filepath, yield* fs.readFile(filepath, "utf8"));
+ }): Gensync);
}
function fileMtime(filepath: string): number | null {
try {
- return +fs.statSync(filepath).mtime;
+ return +nodeFs.statSync(filepath).mtime;
} catch (e) {
if (e.code !== "ENOENT" && e.code !== "ENOTDIR") throw e;
}
diff --git a/packages/babel-core/src/config/full.js b/packages/babel-core/src/config/full.js
index 88c65ff882be..ab24a1936b87 100644
--- a/packages/babel-core/src/config/full.js
+++ b/packages/babel-core/src/config/full.js
@@ -1,5 +1,8 @@
// @flow
+import gensync, { type Handler } from "gensync";
+import { forwardAsync } from "../gensync-utils/async";
+
import { mergeOptions } from "./util";
import * as context from "../index";
import Plugin from "./plugin";
@@ -12,12 +15,17 @@ import {
} from "./config-chain";
import type { UnloadedDescriptor } from "./config-descriptors";
import traverse from "@babel/traverse";
-import { makeWeakCache, type CacheConfigurator } from "./caching";
+import {
+ makeWeakCache,
+ makeWeakCacheSync,
+ type CacheConfigurator,
+} from "./caching";
import { validate, type CallerMetadata } from "./validation/options";
import { validatePluginObject } from "./validation/plugins";
import makeAPI from "./helpers/config-api";
import loadPrivatePartialConfig from "./partial";
+import type { ValidatedOptions } from "./validation/options";
type LoadedDescriptor = {
value: {},
@@ -44,10 +52,10 @@ type SimpleContext = {
caller: CallerMetadata | void,
};
-export default function loadFullConfig(
+export default gensync<[any], ResolvedConfig | null>(function* loadFullConfig(
inputOpts: mixed,
-): ResolvedConfig | null {
- const result = loadPrivatePartialConfig(inputOpts);
+): Handler {
+ const result = yield* loadPrivatePartialConfig(inputOpts);
if (!result) {
return null;
}
@@ -62,28 +70,29 @@ export default function loadFullConfig(
throw new Error("Assertion failure - plugins and presets exist");
}
- const ignored = (function recurseDescriptors(
+ const ignored = yield* (function* recurseDescriptors(
config: {
plugins: Array,
presets: Array,
},
pass: Array,
) {
- const plugins = config.plugins.reduce((acc, descriptor) => {
+ const plugins = [];
+ for (const descriptor of config.plugins) {
if (descriptor.options !== false) {
- acc.push(loadPluginDescriptor(descriptor, context));
+ plugins.push(yield* loadPluginDescriptor(descriptor, context));
}
- return acc;
- }, []);
- const presets = config.presets.reduce((acc, descriptor) => {
+ }
+
+ const presets = [];
+ for (const descriptor of config.presets) {
if (descriptor.options !== false) {
- acc.push({
- preset: loadPresetDescriptor(descriptor, context),
+ presets.push({
+ preset: yield* loadPresetDescriptor(descriptor, context),
pass: descriptor.ownPass ? [] : pass,
});
}
- return acc;
- }, []);
+ }
// resolve presets
if (presets.length > 0) {
@@ -98,7 +107,7 @@ export default function loadFullConfig(
for (const { preset, pass } of presets) {
if (!preset) return true;
- const ignored = recurseDescriptors(
+ const ignored = yield* recurseDescriptors(
{
plugins: preset.plugins,
presets: preset.presets,
@@ -164,61 +173,61 @@ export default function loadFullConfig(
options: opts,
passes: passes,
};
-}
+});
/**
* Load a generic plugin/preset from the given descriptor loaded from the config object.
*/
-const loadDescriptor = makeWeakCache(
- (
- { value, options, dirname, alias }: UnloadedDescriptor,
- cache: CacheConfigurator,
- ): LoadedDescriptor => {
- // Disabled presets should already have been filtered out
- if (options === false) throw new Error("Assertion failure");
-
- options = options || {};
-
- let item = value;
- if (typeof value === "function") {
- const api = {
- ...context,
- ...makeAPI(cache),
- };
- try {
- item = value(api, options, dirname);
- } catch (e) {
- if (alias) {
- e.message += ` (While processing: ${JSON.stringify(alias)})`;
- }
- throw e;
+const loadDescriptor = makeWeakCache(function*(
+ { value, options, dirname, alias }: UnloadedDescriptor,
+ cache: CacheConfigurator,
+): Handler {
+ // Disabled presets should already have been filtered out
+ if (options === false) throw new Error("Assertion failure");
+
+ options = options || {};
+
+ let item = value;
+ if (typeof value === "function") {
+ const api = {
+ ...context,
+ ...makeAPI(cache),
+ };
+ try {
+ item = value(api, options, dirname);
+ } catch (e) {
+ if (alias) {
+ e.message += ` (While processing: ${JSON.stringify(alias)})`;
}
+ throw e;
}
+ }
- if (!item || typeof item !== "object") {
- throw new Error("Plugin/Preset did not return an object.");
- }
+ if (!item || typeof item !== "object") {
+ throw new Error("Plugin/Preset did not return an object.");
+ }
- if (typeof item.then === "function") {
- throw new Error(
- `You appear to be using an async plugin, ` +
- `which your current version of Babel does not support. ` +
- `If you're using a published plugin, ` +
- `you may need to upgrade your @babel/core version.`,
- );
- }
+ if (typeof item.then === "function") {
+ yield* []; // if we want to support async plugins
- return { value: item, options, dirname, alias };
- },
-);
+ throw new Error(
+ `You appear to be using an async plugin, ` +
+ `which your current version of Babel does not support. ` +
+ `If you're using a published plugin, ` +
+ `you may need to upgrade your @babel/core version.`,
+ );
+ }
+
+ return { value: item, options, dirname, alias };
+});
/**
* Instantiate a plugin for the given descriptor, returning the plugin/options pair.
*/
-function loadPluginDescriptor(
+function* loadPluginDescriptor(
descriptor: UnloadedDescriptor,
context: SimpleContext,
-): Plugin {
+): Handler {
if (descriptor.value instanceof Plugin) {
if (descriptor.options) {
throw new Error(
@@ -229,69 +238,105 @@ function loadPluginDescriptor(
return descriptor.value;
}
- return instantiatePlugin(loadDescriptor(descriptor, context), context);
+ return yield* instantiatePlugin(
+ yield* loadDescriptor(descriptor, context),
+ context,
+ );
}
-const instantiatePlugin = makeWeakCache(
- (
- { value, options, dirname, alias }: LoadedDescriptor,
- cache: CacheConfigurator,
- ): Plugin => {
- const pluginObj = validatePluginObject(value);
+const instantiatePlugin = makeWeakCache(function*(
+ { value, options, dirname, alias }: LoadedDescriptor,
+ cache: CacheConfigurator,
+): Handler {
+ const pluginObj = validatePluginObject(value);
- const plugin = {
- ...pluginObj,
- };
- if (plugin.visitor) {
- plugin.visitor = traverse.explode({
- ...plugin.visitor,
- });
- }
+ const plugin = {
+ ...pluginObj,
+ };
+ if (plugin.visitor) {
+ plugin.visitor = traverse.explode({
+ ...plugin.visitor,
+ });
+ }
- if (plugin.inherits) {
- const inheritsDescriptor = {
- name: undefined,
- alias: `${alias}$inherits`,
- value: plugin.inherits,
- options,
- dirname,
- };
+ if (plugin.inherits) {
+ const inheritsDescriptor = {
+ name: undefined,
+ alias: `${alias}$inherits`,
+ value: plugin.inherits,
+ options,
+ dirname,
+ };
+ const inherits = yield* forwardAsync(loadPluginDescriptor, run => {
// If the inherited plugin changes, reinstantiate this plugin.
- const inherits = cache.invalidate(data =>
- loadPluginDescriptor(inheritsDescriptor, data),
- );
+ return cache.invalidate(data => run(inheritsDescriptor, data));
+ });
+
+ plugin.pre = chain(inherits.pre, plugin.pre);
+ plugin.post = chain(inherits.post, plugin.post);
+ plugin.manipulateOptions = chain(
+ inherits.manipulateOptions,
+ plugin.manipulateOptions,
+ );
+ plugin.visitor = traverse.visitors.merge([
+ inherits.visitor || {},
+ plugin.visitor || {},
+ ]);
+ }
+
+ return new Plugin(plugin, options, alias);
+});
+
+const validateIfOptionNeedsFilename = (
+ options: ValidatedOptions,
+ descriptor: UnloadedDescriptor,
+): void => {
+ if (options.test || options.include || options.exclude) {
+ const formattedPresetName = descriptor.name
+ ? `"${descriptor.name}"`
+ : "/* your preset */";
+ throw new Error(
+ [
+ `Preset ${formattedPresetName} requires a filename to be set when babel is called directly,`,
+ `\`\`\``,
+ `babel.transform(code, { filename: 'file.ts', presets: [${formattedPresetName}] });`,
+ `\`\`\``,
+ `See https://babeljs.io/docs/en/options#filename for more information.`,
+ ].join("\n"),
+ );
+ }
+};
- plugin.pre = chain(inherits.pre, plugin.pre);
- plugin.post = chain(inherits.post, plugin.post);
- plugin.manipulateOptions = chain(
- inherits.manipulateOptions,
- plugin.manipulateOptions,
+const validatePreset = (
+ preset: PresetInstance,
+ context: ConfigContext,
+ descriptor: UnloadedDescriptor,
+): void => {
+ if (!context.filename) {
+ const { options } = preset;
+ validateIfOptionNeedsFilename(options, descriptor);
+ if (options.overrides) {
+ options.overrides.forEach(overrideOptions =>
+ validateIfOptionNeedsFilename(overrideOptions, descriptor),
);
- plugin.visitor = traverse.visitors.merge([
- inherits.visitor || {},
- plugin.visitor || {},
- ]);
}
-
- return new Plugin(plugin, options, alias);
- },
-);
+ }
+};
/**
* Generate a config object that will act as the root of a new nested config.
*/
-const loadPresetDescriptor = (
+function* loadPresetDescriptor(
descriptor: UnloadedDescriptor,
context: ConfigContext,
-): ConfigChain | null => {
- return buildPresetChain(
- instantiatePreset(loadDescriptor(descriptor, context)),
- context,
- );
-};
+): Handler {
+ const preset = instantiatePreset(yield* loadDescriptor(descriptor, context));
+ validatePreset(preset, context, descriptor);
+ return yield* buildPresetChain(preset, context);
+}
-const instantiatePreset = makeWeakCache(
+const instantiatePreset = makeWeakCacheSync(
({ value, dirname, alias }: LoadedDescriptor): PresetInstance => {
return {
options: validate("preset", value),
diff --git a/packages/babel-core/src/config/index.js b/packages/babel-core/src/config/index.js
index 51016d34c757..11888a48ec7c 100644
--- a/packages/babel-core/src/config/index.js
+++ b/packages/babel-core/src/config/index.js
@@ -1,6 +1,7 @@
// @flow
-import loadFullConfig from "./full";
+import gensync from "gensync";
+
export type {
ResolvedConfig,
InputOptions,
@@ -8,12 +9,29 @@ export type {
Plugin,
} from "./full";
+import loadFullConfig from "./full";
+import { loadPartialConfig as loadPartialConfigRunner } from "./partial";
+
export { loadFullConfig as default };
-export { loadPartialConfig } from "./partial";
export type { PartialConfig } from "./partial";
-export function loadOptions(opts: {}): Object | null {
- const config = loadFullConfig(opts);
-
+const loadOptionsRunner = gensync<[mixed], Object | null>(function*(opts) {
+ const config = yield* loadFullConfig(opts);
return config ? config.options : null;
-}
+});
+
+const maybeErrback = runner => (opts: mixed, callback: Function) => {
+ if (callback === undefined && typeof opts === "function") {
+ callback = opts;
+ opts = undefined;
+ }
+ return callback ? runner.errback(opts, callback) : runner.sync(opts);
+};
+
+export const loadPartialConfig = maybeErrback(loadPartialConfigRunner);
+export const loadPartialConfigSync = loadPartialConfigRunner.sync;
+export const loadPartialConfigAsync = loadPartialConfigRunner.async;
+
+export const loadOptions = maybeErrback(loadOptionsRunner);
+export const loadOptionsSync = loadOptionsRunner.sync;
+export const loadOptionsAsync = loadOptionsRunner.async;
diff --git a/packages/babel-core/src/config/partial.js b/packages/babel-core/src/config/partial.js
index 9f5f85ed4edc..615f3dc08b73 100644
--- a/packages/babel-core/src/config/partial.js
+++ b/packages/babel-core/src/config/partial.js
@@ -1,6 +1,7 @@
// @flow
import path from "path";
+import gensync, { type Handler } from "gensync";
import Plugin from "./plugin";
import { mergeOptions } from "./util";
import { createItemFromDescriptor } from "./item";
@@ -12,26 +13,36 @@ import {
type RootMode,
} from "./validation/options";
-import { findConfigUpwards, type ConfigFile, type IgnoreFile } from "./files";
-
-function resolveRootMode(rootDir: string, rootMode: RootMode): string {
+import {
+ findConfigUpwards,
+ ROOT_CONFIG_FILENAMES,
+ type ConfigFile,
+ type IgnoreFile,
+} from "./files";
+
+function* resolveRootMode(
+ rootDir: string,
+ rootMode: RootMode,
+): Handler {
switch (rootMode) {
case "root":
return rootDir;
case "upward-optional": {
- const upwardRootDir = findConfigUpwards(rootDir);
+ const upwardRootDir = yield* findConfigUpwards(rootDir);
return upwardRootDir === null ? rootDir : upwardRootDir;
}
case "upward": {
- const upwardRootDir = findConfigUpwards(rootDir);
+ const upwardRootDir = yield* findConfigUpwards(rootDir);
if (upwardRootDir !== null) return upwardRootDir;
throw Object.assign(
(new Error(
`Babel was run with rootMode:"upward" but a root could not ` +
- `be found when searching upward from "${rootDir}"`,
+ `be found when searching upward from "${rootDir}".\n` +
+ `One of the following config files must be in the directory tree: ` +
+ `"${ROOT_CONFIG_FILENAMES.join(", ")}".`,
): any),
{
code: "BABEL_ROOT_NOT_FOUND",
@@ -40,19 +51,21 @@ function resolveRootMode(rootDir: string, rootMode: RootMode): string {
);
}
default:
- throw new Error(`Assertion failure - unknown rootMode value`);
+ throw new Error(`Assertion failure - unknown rootMode value.`);
}
}
-export default function loadPrivatePartialConfig(
- inputOpts: mixed,
-): {
+type PrivPartialConfig = {
options: ValidatedOptions,
context: ConfigContext,
ignore: IgnoreFile | void,
babelrc: ConfigFile | void,
config: ConfigFile | void,
-} | null {
+};
+
+export default function* loadPrivatePartialConfig(
+ inputOpts: mixed,
+): Handler {
if (
inputOpts != null &&
(typeof inputOpts !== "object" || Array.isArray(inputOpts))
@@ -70,7 +83,7 @@ export default function loadPrivatePartialConfig(
caller,
} = args;
const absoluteCwd = path.resolve(cwd);
- const absoluteRootDir = resolveRootMode(
+ const absoluteRootDir = yield* resolveRootMode(
path.resolve(absoluteCwd, rootDir),
rootMode,
);
@@ -86,7 +99,7 @@ export default function loadPrivatePartialConfig(
caller,
};
- const configChain = buildRootChain(args, context);
+ const configChain = yield* buildRootChain(args, context);
if (!configChain) return null;
const options = {};
@@ -122,8 +135,10 @@ export default function loadPrivatePartialConfig(
};
}
-export function loadPartialConfig(inputOpts: mixed): PartialConfig | null {
- const result = loadPrivatePartialConfig(inputOpts);
+export const loadPartialConfig = gensync<[any], PartialConfig | null>(function*(
+ inputOpts: mixed,
+): Handler {
+ const result: ?PrivPartialConfig = yield* loadPrivatePartialConfig(inputOpts);
if (!result) return null;
const { options, babelrc, ignore, config } = result;
@@ -143,7 +158,7 @@ export function loadPartialConfig(inputOpts: mixed): PartialConfig | null {
ignore ? ignore.filepath : undefined,
config ? config.filepath : undefined,
);
-}
+});
export type { PartialConfig };
diff --git a/packages/babel-core/src/config/util.js b/packages/babel-core/src/config/util.js
index f64975216db3..b9d3af143dd3 100644
--- a/packages/babel-core/src/config/util.js
+++ b/packages/babel-core/src/config/util.js
@@ -28,3 +28,14 @@ function mergeDefaultFields(target: T, source: T) {
if (val !== undefined) target[k] = (val: any);
}
}
+
+export function isIterableIterator(value: mixed): boolean %checks {
+ return (
+ /*:: value instanceof Generator && */
+ // /*:: "@@iterator" in value && */
+ !!value &&
+ typeof value.next === "function" &&
+ // $FlowIgnore
+ typeof value[Symbol.iterator] === "function"
+ );
+}
diff --git a/packages/babel-core/src/config/validation/option-assertions.js b/packages/babel-core/src/config/validation/option-assertions.js
index 87244b4f16b3..a279a72781a0 100644
--- a/packages/babel-core/src/config/validation/option-assertions.js
+++ b/packages/babel-core/src/config/validation/option-assertions.js
@@ -278,7 +278,7 @@ export function assertConfigFileSearch(
) {
throw new Error(
`${msg(loc)} must be a undefined, a boolean, a string, ` +
- `got ${JSON.stringify(value)}`,
+ `got ${JSON.stringify((value: any))}`,
);
}
@@ -302,7 +302,7 @@ export function assertBabelrcSearch(
} else if (!checkValidTest(value)) {
throw new Error(
`${msg(loc)} must be a undefined, a boolean, a string/Function/RegExp ` +
- `or an array of those, got ${JSON.stringify(value)}`,
+ `or an array of those, got ${JSON.stringify((value: any))}`,
);
}
return (value: any);
diff --git a/packages/babel-core/src/config/validation/options.js b/packages/babel-core/src/config/validation/options.js
index 3ee072ae344f..02c51649a4cb 100644
--- a/packages/babel-core/src/config/validation/options.js
+++ b/packages/babel-core/src/config/validation/options.js
@@ -272,7 +272,8 @@ export type OptionsSource =
| "configfile"
| "babelrcfile"
| "extendsfile"
- | "preset";
+ | "preset"
+ | "plugin";
type RootPath = $ReadOnly<{
type: "root",
diff --git a/packages/babel-core/src/config/validation/plugins.js b/packages/babel-core/src/config/validation/plugins.js
index 7800de965e31..ea1fc514306d 100644
--- a/packages/babel-core/src/config/validation/plugins.js
+++ b/packages/babel-core/src/config/validation/plugins.js
@@ -84,10 +84,19 @@ export type PluginObject = {
};
export function validatePluginObject(obj: {}): PluginObject {
+ const rootPath: RootPath = {
+ type: "root",
+ source: "plugin",
+ };
Object.keys(obj).forEach(key => {
const validator = VALIDATORS[key];
+ const optLoc = {
+ type: "option",
+ name: key,
+ parent: rootPath,
+ };
- if (validator) validator(key, obj[key]);
+ if (validator) validator(optLoc, obj[key]);
else throw new Error(`.${key} is not a valid Plugin property`);
});
diff --git a/packages/babel-core/src/gensync-utils/async.js b/packages/babel-core/src/gensync-utils/async.js
new file mode 100644
index 000000000000..84edcc394135
--- /dev/null
+++ b/packages/babel-core/src/gensync-utils/async.js
@@ -0,0 +1,110 @@
+// @flow
+
+import gensync, { type Gensync, type Handler } from "gensync";
+
+type MaybePromise = T | Promise;
+
+const id = x => x;
+
+const runGenerator = gensync(function*(item) {
+ return yield* item;
+});
+
+// This Gensync returns true if the current execution contect is
+// asynchronous, otherwise it returns false.
+export const isAsync = gensync<[], boolean>({
+ sync: () => false,
+ errback: cb => cb(null, true),
+});
+
+// This function wraps any functions (which could be either synchronous or
+// asynchronous) with a Gensync. If the wrapped function returns a promise
+// but the current execution context is synchronous, it will throw the
+// provided error.
+// This is used to handle user-provided functions which could be asynchronous.
+export function maybeAsync(
+ fn: (...args: Args) => T,
+ message: string,
+): Gensync {
+ return gensync({
+ sync(...args) {
+ const result = fn.apply(this, args);
+ if (isThenable(result)) throw new Error(message);
+ return result;
+ },
+ async(...args) {
+ return Promise.resolve(fn.apply(this, args));
+ },
+ });
+}
+
+const withKind = (gensync<[any], any>({
+ sync: cb => cb("sync"),
+ async: cb => cb("async"),
+}): (cb: (kind: "sync" | "async") => MaybePromise) => Handler);
+
+// This function wraps a generator (or a Gensync) into another function which,
+// when called, will run the provided generator in a sync or async way, depending
+// on the execution context where this forwardAsync function is called.
+// This is useful, for example, when passing a callback to a function which isn't
+// aware of gensync, but it only knows about synchronous and asynchronous functions.
+// An example is cache.using, which being exposed to the user must be as simple as
+// possible:
+// yield* forwardAsync(gensyncFn, wrappedFn =>
+// cache.using(x => {
+// // Here we don't know about gensync. wrappedFn is a
+// // normal sync or async function
+// return wrappedFn(x);
+// })
+// )
+export function forwardAsync(
+ action: (...args: ActionArgs) => Handler,
+ cb: (
+ adapted: (...args: ActionArgs) => MaybePromise,
+ ) => MaybePromise,
+): Handler {
+ const g = gensync(action);
+ return withKind(kind => {
+ const adapted = g[kind];
+ return cb(adapted);
+ });
+}
+
+// If the given generator is executed asynchronously, the first time that it
+// is paused (i.e. When it yields a gensync generator which can't be run
+// synchronously), call the "firstPause" callback.
+export const onFirstPause = (gensync<[any, any], any>({
+ name: "onFirstPause",
+ arity: 2,
+ sync: function(item) {
+ return runGenerator.sync(item);
+ },
+ errback: function(item, firstPause, cb) {
+ let completed = false;
+
+ runGenerator.errback(item, (err, value) => {
+ completed = true;
+ cb(err, value);
+ });
+
+ if (!completed) {
+ firstPause();
+ }
+ },
+}): (gen: Generator<*, T, *>, cb: Function) => Handler);
+
+// Wait for the given promise to be resolved
+export const waitFor = (gensync<[any], any>({
+ sync: id,
+ async: id,
+}): (p: T | Promise) => Handler);
+
+export function isThenable(val: mixed): boolean %checks {
+ return (
+ /*:: val instanceof Promise && */
+ !!val &&
+ (typeof val === "object" || typeof val === "function") &&
+ !!val.then &&
+ typeof val.then === "function"
+ );
+}
diff --git a/packages/babel-core/src/gensync-utils/fs.js b/packages/babel-core/src/gensync-utils/fs.js
new file mode 100644
index 000000000000..6b8f60c7b145
--- /dev/null
+++ b/packages/babel-core/src/gensync-utils/fs.js
@@ -0,0 +1,21 @@
+// @flow
+
+import fs from "fs";
+import gensync from "gensync";
+
+export const readFile = gensync<[string, "utf8"], string>({
+ sync: fs.readFileSync,
+ errback: fs.readFile,
+});
+
+export const exists = gensync<[string], boolean>({
+ sync(path) {
+ try {
+ fs.accessSync(path);
+ return true;
+ } catch {
+ return false;
+ }
+ },
+ errback: (path, cb) => fs.access(path, undefined, err => cb(null, !err)),
+});
diff --git a/packages/babel-core/src/gensync-utils/resolve.js b/packages/babel-core/src/gensync-utils/resolve.js
new file mode 100644
index 000000000000..d16f6e7c4b2c
--- /dev/null
+++ b/packages/babel-core/src/gensync-utils/resolve.js
@@ -0,0 +1,9 @@
+// @flow
+
+import resolve from "resolve";
+import gensync from "gensync";
+
+export default gensync<[string, {| basedir: string |}], string>({
+ sync: resolve.sync,
+ errback: resolve,
+});
diff --git a/packages/babel-core/src/index.js b/packages/babel-core/src/index.js
index e7ae7c0edfc6..23d023b5cabd 100644
--- a/packages/babel-core/src/index.js
+++ b/packages/babel-core/src/index.js
@@ -1,9 +1,7 @@
// @flow
export { default as File } from "./transformation/file/file";
-export {
- default as buildExternalHelpers,
-} from "./tools/build-external-helpers";
+export { default as buildExternalHelpers } from "./tools/build-external-helpers";
export { resolvePlugin, resolvePreset } from "./config/files";
export { version } from "../package.json";
@@ -17,7 +15,14 @@ export { default as template } from "@babel/template";
export { createConfigItem } from "./config/item";
-export { loadPartialConfig, loadOptions } from "./config";
+export {
+ loadPartialConfig,
+ loadPartialConfigSync,
+ loadPartialConfigAsync,
+ loadOptions,
+ loadOptionsSync,
+ loadOptionsAsync,
+} from "./config";
export { transform, transformSync, transformAsync } from "./transform";
export {
diff --git a/packages/babel-core/src/parse.js b/packages/babel-core/src/parse.js
index b3397a1a2e23..3609df763530 100644
--- a/packages/babel-core/src/parse.js
+++ b/packages/babel-core/src/parse.js
@@ -1,14 +1,13 @@
// @flow
+import gensync from "gensync";
+
import loadConfig, { type InputOptions } from "./config";
-import normalizeFile from "./transformation/normalize-file";
+import parser from "./parser";
+import type { ParseResult } from "./parser";
import normalizeOptions from "./transformation/normalize-opts";
-type AstRoot = BabelNodeFile | BabelNodeProgram;
-
-export type ParseResult = AstRoot;
-
-export type FileParseCallback = {
+type FileParseCallback = {
(Error, null): any,
(null, ParseResult | null): any,
};
@@ -22,6 +21,18 @@ type Parse = {
(code: string, opts: ?InputOptions): ParseResult | null,
};
+const parseRunner = gensync<[string, ?InputOptions], ParseResult | null>(
+ function* parse(code, opts) {
+ const config = yield* loadConfig(opts);
+
+ if (config === null) {
+ return null;
+ }
+
+ return yield* parser(config.passes, normalizeOptions(config), code);
+ },
+);
+
export const parse: Parse = (function parse(code, opts, callback) {
if (typeof opts === "function") {
callback = opts;
@@ -30,55 +41,10 @@ export const parse: Parse = (function parse(code, opts, callback) {
// For backward-compat with Babel 7's early betas, we allow sync parsing when
// no callback is given. Will be dropped in some future Babel major version.
- if (callback === undefined) return parseSync(code, opts);
-
- const config = loadConfig(opts);
-
- if (config === null) {
- return null;
- }
-
- // Reassign to keep Flowtype happy.
- const cb = callback;
-
- // Just delaying the transform one tick for now to simulate async behavior
- // but more async logic may land here eventually.
- process.nextTick(() => {
- let ast = null;
- try {
- const cfg = loadConfig(opts);
- if (cfg === null) return cb(null, null);
-
- ast = normalizeFile(cfg.passes, normalizeOptions(cfg), code).ast;
- } catch (err) {
- return cb(err);
- }
+ if (callback === undefined) return parseRunner.sync(code, opts);
- cb(null, ast);
- });
+ parseRunner.errback(code, opts, callback);
}: Function);
-export function parseSync(
- code: string,
- opts?: InputOptions,
-): ParseResult | null {
- const config = loadConfig(opts);
-
- if (config === null) {
- return null;
- }
-
- return normalizeFile(config.passes, normalizeOptions(config), code).ast;
-}
-
-export function parseAsync(
- code: string,
- opts?: InputOptions,
-): Promise {
- return new Promise((res, rej) => {
- parse(code, opts, (err, result) => {
- if (err == null) res(result);
- else rej(err);
- });
- });
-}
+export const parseSync = parseRunner.sync;
+export const parseAsync = parseRunner.async;
diff --git a/packages/babel-core/src/parser/index.js b/packages/babel-core/src/parser/index.js
new file mode 100644
index 000000000000..3ddf5ca4b64d
--- /dev/null
+++ b/packages/babel-core/src/parser/index.js
@@ -0,0 +1,76 @@
+import type { Handler } from "gensync";
+import { parse } from "@babel/parser";
+import { codeFrameColumns } from "@babel/code-frame";
+import generateMissingPluginMessage from "./util/missing-plugin-helper";
+
+type AstRoot = BabelNodeFile | BabelNodeProgram;
+
+export type ParseResult = AstRoot;
+
+export default function* parser(
+ pluginPasses: PluginPasses,
+ { parserOpts, highlightCode = true, filename = "unknown" }: Object,
+ code: string,
+): Handler {
+ try {
+ const results = [];
+ for (const plugins of pluginPasses) {
+ for (const plugin of plugins) {
+ const { parserOverride } = plugin;
+ if (parserOverride) {
+ const ast = parserOverride(code, parserOpts, parse);
+
+ if (ast !== undefined) results.push(ast);
+ }
+ }
+ }
+
+ if (results.length === 0) {
+ return parse(code, parserOpts);
+ } else if (results.length === 1) {
+ yield* []; // If we want to allow async parsers
+ if (typeof results[0].then === "function") {
+ throw new Error(
+ `You appear to be using an async parser plugin, ` +
+ `which your current version of Babel does not support. ` +
+ `If you're using a published plugin, you may need to upgrade ` +
+ `your @babel/core version.`,
+ );
+ }
+ return results[0];
+ }
+ throw new Error("More than one plugin attempted to override parsing.");
+ } catch (err) {
+ if (err.code === "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED") {
+ err.message +=
+ "\nConsider renaming the file to '.mjs', or setting sourceType:module " +
+ "or sourceType:unambiguous in your Babel config for this file.";
+ // err.code will be changed to BABEL_PARSE_ERROR later.
+ }
+
+ const { loc, missingPlugin } = err;
+ if (loc) {
+ const codeFrame = codeFrameColumns(
+ code,
+ {
+ start: {
+ line: loc.line,
+ column: loc.column + 1,
+ },
+ },
+ {
+ highlightCode,
+ },
+ );
+ if (missingPlugin) {
+ err.message =
+ `${filename}: ` +
+ generateMissingPluginMessage(missingPlugin[0], loc, codeFrame);
+ } else {
+ err.message = `${filename}: ${err.message}\n\n` + codeFrame;
+ }
+ err.code = "BABEL_PARSE_ERROR";
+ }
+ throw err;
+ }
+}
diff --git a/packages/babel-core/src/transformation/util/missing-plugin-helper.js b/packages/babel-core/src/parser/util/missing-plugin-helper.js
similarity index 100%
rename from packages/babel-core/src/transformation/util/missing-plugin-helper.js
rename to packages/babel-core/src/parser/util/missing-plugin-helper.js
index b0642c50c3cf..2560f3ea94bc 100644
--- a/packages/babel-core/src/transformation/util/missing-plugin-helper.js
+++ b/packages/babel-core/src/parser/util/missing-plugin-helper.js
@@ -113,16 +113,6 @@ const pluginNameMap = {
url: "https://git.io/vAlRe",
},
},
- nullishCoalescingOperator: {
- syntax: {
- name: "@babel/plugin-syntax-nullish-coalescing-operator",
- url: "https://git.io/vb4yx",
- },
- transform: {
- name: "@babel/plugin-proposal-nullish-coalescing-operator",
- url: "https://git.io/vb4Se",
- },
- },
numericSeparator: {
syntax: {
name: "@babel/plugin-syntax-numeric-separator",
@@ -188,6 +178,16 @@ const pluginNameMap = {
url: "https://git.io/vb4yp",
},
},
+ nullishCoalescingOperator: {
+ syntax: {
+ name: "@babel/plugin-syntax-nullish-coalescing-operator",
+ url: "https://git.io/vb4yx",
+ },
+ transform: {
+ name: "@babel/plugin-proposal-nullish-coalescing-operator",
+ url: "https://git.io/vb4Se",
+ },
+ },
objectRestSpread: {
syntax: {
name: "@babel/plugin-syntax-object-rest-spread",
diff --git a/packages/babel-core/src/transform-ast.js b/packages/babel-core/src/transform-ast.js
index 101177dcece9..9a40bda90d3a 100644
--- a/packages/babel-core/src/transform-ast.js
+++ b/packages/babel-core/src/transform-ast.js
@@ -1,9 +1,10 @@
// @flow
-import loadConfig, { type InputOptions } from "./config";
+import gensync from "gensync";
+
+import loadConfig, { type InputOptions, type ResolvedConfig } from "./config";
import {
- runSync,
- runAsync,
+ run,
type FileResult,
type FileResultCallback,
} from "./transformation";
@@ -24,6 +25,18 @@ type TransformFromAst = {
(ast: AstRoot, code: string, opts: ?InputOptions): FileResult | null,
};
+const transformFromAstRunner = gensync<
+ [AstRoot, string, ?InputOptions],
+ FileResult | null,
+>(function*(ast, code, opts) {
+ const config: ResolvedConfig | null = yield* loadConfig(opts);
+ if (config === null) return null;
+
+ if (!ast) throw new Error("No AST given");
+
+ return yield* run(config, code, ast);
+});
+
export const transformFromAst: TransformFromAst = (function transformFromAst(
ast,
code,
@@ -37,50 +50,12 @@ export const transformFromAst: TransformFromAst = (function transformFromAst(
// For backward-compat with Babel 6, we allow sync transformation when
// no callback is given. Will be dropped in some future Babel major version.
- if (callback === undefined) return transformFromAstSync(ast, code, opts);
-
- // Reassign to keep Flowtype happy.
- const cb = callback;
-
- // Just delaying the transform one tick for now to simulate async behavior
- // but more async logic may land here eventually.
- process.nextTick(() => {
- let cfg;
- try {
- cfg = loadConfig(opts);
- if (cfg === null) return cb(null, null);
- } catch (err) {
- return cb(err);
- }
-
- if (!ast) return cb(new Error("No AST given"));
+ if (callback === undefined) {
+ return transformFromAstRunner.sync(ast, code, opts);
+ }
- runAsync(cfg, code, ast, cb);
- });
+ transformFromAstRunner.errback(ast, code, opts, callback);
}: Function);
-export function transformFromAstSync(
- ast: AstRoot,
- code: string,
- opts: ?InputOptions,
-): FileResult | null {
- const config = loadConfig(opts);
- if (config === null) return null;
-
- if (!ast) throw new Error("No AST given");
-
- return runSync(config, code, ast);
-}
-
-export function transformFromAstAsync(
- ast: AstRoot,
- code: string,
- opts: ?InputOptions,
-): Promise {
- return new Promise((res, rej) => {
- transformFromAst(ast, code, opts, (err, result) => {
- if (err == null) res(result);
- else rej(err);
- });
- });
-}
+export const transformFromAstSync = transformFromAstRunner.sync;
+export const transformFromAstAsync = transformFromAstRunner.async;
diff --git a/packages/babel-core/src/transform-file.js b/packages/babel-core/src/transform-file.js
index d3c9ab640fc3..9d2c3e6ae406 100644
--- a/packages/babel-core/src/transform-file.js
+++ b/packages/babel-core/src/transform-file.js
@@ -1,13 +1,14 @@
// @flow
-import fs from "fs";
-import loadConfig, { type InputOptions } from "./config";
+import gensync from "gensync";
+
+import loadConfig, { type InputOptions, type ResolvedConfig } from "./config";
import {
- runSync,
- runAsync,
+ run,
type FileResult,
type FileResultCallback,
} from "./transformation";
+import * as fs from "./gensync-utils/fs";
import typeof * as transformFileBrowserType from "./transform-file-browser";
import typeof * as transformFileType from "./transform-file";
@@ -22,74 +23,26 @@ type TransformFile = {
(filename: string, opts: ?InputOptions, callback: FileResultCallback): void,
};
-export const transformFile: TransformFile = (function transformFile(
- filename,
- opts,
- callback,
-) {
- let options;
- if (typeof opts === "function") {
- callback = opts;
- opts = undefined;
- }
-
- if (opts == null) {
- options = { filename };
- } else if (opts && typeof opts === "object") {
- options = {
- ...opts,
- filename,
- };
- }
-
- process.nextTick(() => {
- let cfg;
- try {
- cfg = loadConfig(options);
- if (cfg === null) return callback(null, null);
- } catch (err) {
- return callback(err);
+const transformFileRunner = gensync<[string, ?InputOptions], FileResult | null>(
+ function*(filename, opts) {
+ let options;
+ if (opts == null) {
+ options = { filename };
+ } else if (opts && typeof opts === "object") {
+ options = {
+ ...opts,
+ filename,
+ };
}
- // Reassignment to keep Flow happy.
- const config = cfg;
-
- fs.readFile(filename, "utf8", function(err, code: string) {
- if (err) return callback(err, null);
-
- runAsync(config, code, null, callback);
- });
- });
-}: Function);
-
-export function transformFileSync(
- filename: string,
- opts: ?InputOptions,
-): FileResult | null {
- let options;
- if (opts == null) {
- options = { filename };
- } else if (opts && typeof opts === "object") {
- options = {
- ...opts,
- filename,
- };
- }
-
- const config = loadConfig(options);
- if (config === null) return null;
+ const config: ResolvedConfig | null = yield* loadConfig(options);
+ if (config === null) return null;
- return runSync(config, fs.readFileSync(filename, "utf8"));
-}
+ const code = yield* fs.readFile(filename, "utf8");
+ return yield* run(config, code);
+ },
+);
-export function transformFileAsync(
- filename: string,
- opts: ?InputOptions,
-): Promise {
- return new Promise((res, rej) => {
- transformFile(filename, opts, (err, result) => {
- if (err == null) res(result);
- else rej(err);
- });
- });
-}
+export const transformFile: TransformFile = transformFileRunner.errback;
+export const transformFileSync = transformFileRunner.sync;
+export const transformFileAsync = transformFileRunner.async;
diff --git a/packages/babel-core/src/transform.js b/packages/babel-core/src/transform.js
index 4a2f0e5c1691..0ace438c29fa 100644
--- a/packages/babel-core/src/transform.js
+++ b/packages/babel-core/src/transform.js
@@ -1,8 +1,10 @@
// @flow
-import loadConfig, { type InputOptions } from "./config";
+
+import gensync from "gensync";
+
+import loadConfig, { type InputOptions, type ResolvedConfig } from "./config";
import {
- runSync,
- runAsync,
+ run,
type FileResult,
type FileResultCallback,
} from "./transformation";
@@ -16,6 +18,15 @@ type Transform = {
(code: string, opts: ?InputOptions): FileResult | null,
};
+const transformRunner = gensync<[string, ?InputOptions], FileResult | null>(
+ function* transform(code, opts) {
+ const config: ResolvedConfig | null = yield* loadConfig(opts);
+ if (config === null) return null;
+
+ return yield* run(config, code);
+ },
+);
+
export const transform: Transform = (function transform(code, opts, callback) {
if (typeof opts === "function") {
callback = opts;
@@ -24,44 +35,10 @@ export const transform: Transform = (function transform(code, opts, callback) {
// For backward-compat with Babel 6, we allow sync transformation when
// no callback is given. Will be dropped in some future Babel major version.
- if (callback === undefined) return transformSync(code, opts);
+ if (callback === undefined) return transformRunner.sync(code, opts);
- // Reassign to keep Flowtype happy.
- const cb = callback;
-
- // Just delaying the transform one tick for now to simulate async behavior
- // but more async logic may land here eventually.
- process.nextTick(() => {
- let cfg;
- try {
- cfg = loadConfig(opts);
- if (cfg === null) return cb(null, null);
- } catch (err) {
- return cb(err);
- }
-
- runAsync(cfg, code, null, cb);
- });
+ transformRunner.errback(code, opts, callback);
}: Function);
-export function transformSync(
- code: string,
- opts: ?InputOptions,
-): FileResult | null {
- const config = loadConfig(opts);
- if (config === null) return null;
-
- return runSync(config, code);
-}
-
-export function transformAsync(
- code: string,
- opts: ?InputOptions,
-): Promise {
- return new Promise((res, rej) => {
- transform(code, opts, (err, result) => {
- if (err == null) res(result);
- else rej(err);
- });
- });
-}
+export const transformSync = transformRunner.sync;
+export const transformAsync = transformRunner.async;
diff --git a/packages/babel-core/src/transformation/block-hoist-plugin.js b/packages/babel-core/src/transformation/block-hoist-plugin.js
index 8e8fe69699f0..49ecf5a18556 100644
--- a/packages/babel-core/src/transformation/block-hoist-plugin.js
+++ b/packages/babel-core/src/transformation/block-hoist-plugin.js
@@ -11,7 +11,7 @@ export default function loadBlockHoistPlugin(): Plugin {
// Lazy-init the internal plugin to remove the init-time circular
// dependency between plugins being passed @babel/core's export object,
// which loads this file, and this 'loadConfig' loading plugins.
- const config = loadConfig({
+ const config = loadConfig.sync({
babelrc: false,
configFile: false,
plugins: [blockHoistPlugin],
diff --git a/packages/babel-core/src/transformation/file/file.js b/packages/babel-core/src/transformation/file/file.js
index a0a9f65bcdf4..ed86452c64ff 100644
--- a/packages/babel-core/src/transformation/file/file.js
+++ b/packages/babel-core/src/transformation/file/file.js
@@ -19,6 +19,17 @@ const errorVisitor = {
},
};
+export type NodeLocation = {
+ loc?: {
+ end?: { line: number, column: number },
+ start: { line: number, column: number },
+ },
+ _loc?: {
+ end?: { line: number, column: number },
+ start: { line: number, column: number },
+ },
+};
+
export default class File {
_map: Map = new Map();
opts: Object;
@@ -250,17 +261,12 @@ export default class File {
}
buildCodeFrameError(
- node: ?{
- loc?: { start: { line: number, column: number } },
- _loc?: { start: { line: number, column: number } },
- },
+ node: ?NodeLocation,
msg: string,
Error: typeof Error = SyntaxError,
): Error {
let loc = node && (node.loc || node._loc);
- msg = `${this.opts.filename}: ${msg}`;
-
if (!loc && node) {
const state = {
loc: null,
@@ -287,6 +293,13 @@ export default class File {
line: loc.start.line,
column: loc.start.column + 1,
},
+ end:
+ loc.end && loc.start.line === loc.end.line
+ ? {
+ line: loc.end.line,
+ column: loc.end.column + 1,
+ }
+ : undefined,
},
{ highlightCode },
);
diff --git a/packages/babel-core/src/transformation/file/generate.js b/packages/babel-core/src/transformation/file/generate.js
index e124c16fc4c6..b07851fe9cba 100644
--- a/packages/babel-core/src/transformation/file/generate.js
+++ b/packages/babel-core/src/transformation/file/generate.js
@@ -1,7 +1,7 @@
// @flow
import type { PluginPasses } from "../../config";
-import convertSourceMap, { type SourceMap } from "convert-source-map";
+import convertSourceMap, { typeof SourceMap } from "convert-source-map";
import generate from "@babel/generator";
import type File from "./file";
@@ -41,7 +41,7 @@ export default function generateCode(
if (typeof result.then === "function") {
throw new Error(
- `You appear to be using an async parser plugin, ` +
+ `You appear to be using an async codegen plugin, ` +
`which your current version of Babel does not support. ` +
`If you're using a published plugin, ` +
`you may need to upgrade your @babel/core version.`,
diff --git a/packages/babel-core/src/transformation/file/merge-map.js b/packages/babel-core/src/transformation/file/merge-map.js
index ff2e65b042b8..911867d96890 100644
--- a/packages/babel-core/src/transformation/file/merge-map.js
+++ b/packages/babel-core/src/transformation/file/merge-map.js
@@ -1,6 +1,6 @@
// @flow
-import type { SourceMap } from "convert-source-map";
+import typeof { SourceMap } from "convert-source-map";
import sourceMap from "source-map";
export default function mergeSourceMap(
diff --git a/packages/babel-core/src/transformation/index.js b/packages/babel-core/src/transformation/index.js
index a03c4db0cf2a..970549f9d94e 100644
--- a/packages/babel-core/src/transformation/index.js
+++ b/packages/babel-core/src/transformation/index.js
@@ -1,6 +1,7 @@
// @flow
import traverse from "@babel/traverse";
-import type { SourceMap } from "convert-source-map";
+import typeof { SourceMap } from "convert-source-map";
+import type { Handler } from "gensync";
import type { ResolvedConfig, PluginPasses } from "../config";
@@ -25,41 +26,41 @@ export type FileResult = {
map: SourceMap | null,
};
-export function runAsync(
+export function* run(
config: ResolvedConfig,
code: string,
ast: ?(BabelNodeFile | BabelNodeProgram),
- callback: Function,
-) {
- let result;
- try {
- result = runSync(config, code, ast);
- } catch (err) {
- return callback(err);
- }
-
- // We don't actually care about calling this synchronously here because it is
- // already running within a .nextTick handler from the transform calls above.
- return callback(null, result);
-}
-
-export function runSync(
- config: ResolvedConfig,
- code: string,
- ast: ?(BabelNodeFile | BabelNodeProgram),
-): FileResult {
- const file = normalizeFile(
+): Handler {
+ const file = yield* normalizeFile(
config.passes,
normalizeOptions(config),
code,
ast,
);
- transformFile(file, config.passes);
-
const opts = file.opts;
- const { outputCode, outputMap } =
- opts.code !== false ? generateCode(config.passes, file) : {};
+ try {
+ yield* transformFile(file, config.passes);
+ } catch (e) {
+ e.message = `${opts.filename ?? "unknown"}: ${e.message}`;
+ if (!e.code) {
+ e.code = "BABEL_TRANSFORM_ERROR";
+ }
+ throw e;
+ }
+
+ let outputCode, outputMap;
+ try {
+ if (opts.code !== false) {
+ ({ outputCode, outputMap } = generateCode(config.passes, file));
+ }
+ } catch (e) {
+ e.message = `${opts.filename ?? "unknown"}: ${e.message}`;
+ if (!e.code) {
+ e.code = "BABEL_GENERATE_ERROR";
+ }
+ throw e;
+ }
return {
metadata: file.metadata,
@@ -71,7 +72,7 @@ export function runSync(
};
}
-function transformFile(file: File, pluginPasses: PluginPasses): void {
+function* transformFile(file: File, pluginPasses: PluginPasses): Handler {
for (const pluginPairs of pluginPasses) {
const passPairs = [];
const passes = [];
@@ -90,6 +91,7 @@ function transformFile(file: File, pluginPasses: PluginPasses): void {
if (fn) {
const result = fn.call(pass, file);
+ yield* [];
if (isThenable(result)) {
throw new Error(
`You appear to be using an plugin with an async .pre, ` +
@@ -114,6 +116,7 @@ function transformFile(file: File, pluginPasses: PluginPasses): void {
if (fn) {
const result = fn.call(pass, file);
+ yield* [];
if (isThenable(result)) {
throw new Error(
`You appear to be using an plugin with an async .post, ` +
diff --git a/packages/babel-core/src/transformation/normalize-file.js b/packages/babel-core/src/transformation/normalize-file.js
index 1df42ad763b2..69afea4f9fd6 100644
--- a/packages/babel-core/src/transformation/normalize-file.js
+++ b/packages/babel-core/src/transformation/normalize-file.js
@@ -1,17 +1,18 @@
// @flow
+import fs from "fs";
import path from "path";
import buildDebug from "debug";
import cloneDeep from "lodash/cloneDeep";
+import type { Handler } from "gensync";
import * as t from "@babel/types";
import type { PluginPasses } from "../config";
import convertSourceMap, { typeof Converter } from "convert-source-map";
-import { parse } from "@babel/parser";
-import { codeFrameColumns } from "@babel/code-frame";
import File from "./file/file";
-import generateMissingPluginMessage from "./util/missing-plugin-helper";
+import parser from "../parser";
const debug = buildDebug("babel:transform:file");
+const LARGE_INPUT_SOURCEMAP_THRESHOLD = 1_000_000;
export type NormalizedFile = {
code: string,
@@ -19,14 +20,25 @@ export type NormalizedFile = {
inputMap: Converter | null,
};
-export default function normalizeFile(
+export default function* normalizeFile(
pluginPasses: PluginPasses,
options: Object,
code: string,
ast: ?(BabelNodeFile | BabelNodeProgram),
-): File {
+): Handler {
code = `${code || ""}`;
+ if (ast) {
+ if (ast.type === "Program") {
+ ast = t.file(ast, [], []);
+ } else if (ast.type !== "File") {
+ throw new Error("AST root must be a Program or File node");
+ }
+ ast = cloneDeep(ast);
+ } else {
+ ast = yield* parser(pluginPasses, options, code);
+ }
+
let inputMap = null;
if (options.inputSourceMap !== false) {
// If an explicit object is passed in, it overrides the processing of
@@ -36,54 +48,41 @@ export default function normalizeFile(
}
if (!inputMap) {
- try {
- inputMap = convertSourceMap.fromSource(code);
-
- if (inputMap) {
- code = convertSourceMap.removeComments(code);
+ const lastComment = extractComments(INLINE_SOURCEMAP_REGEX, ast);
+ if (lastComment) {
+ try {
+ inputMap = convertSourceMap.fromComment(lastComment);
+ } catch (err) {
+ debug("discarding unknown inline input sourcemap", err);
}
- } catch (err) {
- debug("discarding unknown inline input sourcemap", err);
- code = convertSourceMap.removeComments(code);
}
}
if (!inputMap) {
- if (typeof options.filename === "string") {
+ const lastComment = extractComments(EXTERNAL_SOURCEMAP_REGEX, ast);
+ if (typeof options.filename === "string" && lastComment) {
try {
- inputMap = convertSourceMap.fromMapFileSource(
- code,
- path.dirname(options.filename),
+ // when `lastComment` is non-null, EXTERNAL_SOURCEMAP_REGEX must have matches
+ const match: [string, string] = (EXTERNAL_SOURCEMAP_REGEX.exec(
+ lastComment,
+ ): any);
+ const inputMapContent: Buffer = fs.readFileSync(
+ path.resolve(path.dirname(options.filename), match[1]),
);
-
- if (inputMap) {
- code = convertSourceMap.removeMapFileComments(code);
+ if (inputMapContent.length > LARGE_INPUT_SOURCEMAP_THRESHOLD) {
+ debug("skip merging input map > 1 MB");
+ } else {
+ inputMap = convertSourceMap.fromJSON(inputMapContent);
}
} catch (err) {
debug("discarding unknown file input sourcemap", err);
- code = convertSourceMap.removeMapFileComments(code);
}
- } else {
+ } else if (lastComment) {
debug("discarding un-loadable file input sourcemap");
- code = convertSourceMap.removeMapFileComments(code);
}
}
}
- if (ast) {
- if (ast.type === "Program") {
- ast = t.file(ast, [], []);
- } else if (ast.type !== "File") {
- throw new Error("AST root must be a Program or File node");
- }
- ast = cloneDeep(ast);
- } else {
- // The parser's AST types aren't fully compatible with the types generated
- // by the logic in babel-types.
- // $FlowFixMe
- ast = parser(pluginPasses, options, code);
- }
-
return new File(options, {
code,
ast,
@@ -91,68 +90,47 @@ export default function normalizeFile(
});
}
-function parser(
- pluginPasses: PluginPasses,
- { parserOpts, highlightCode = true, filename = "unknown" }: Object,
- code: string,
-) {
- try {
- const results = [];
- for (const plugins of pluginPasses) {
- for (const plugin of plugins) {
- const { parserOverride } = plugin;
- if (parserOverride) {
- const ast = parserOverride(code, parserOpts, parse);
-
- if (ast !== undefined) results.push(ast);
- }
- }
- }
+// These regexps are copied from the convert-source-map package,
+// but without // or /* at the beginning of the comment.
- if (results.length === 0) {
- return parse(code, parserOpts);
- } else if (results.length === 1) {
- if (typeof results[0].then === "function") {
- throw new Error(
- `You appear to be using an async codegen plugin, ` +
- `which your current version of Babel does not support. ` +
- `If you're using a published plugin, you may need to upgrade ` +
- `your @babel/core version.`,
- );
- }
- return results[0];
- }
- throw new Error("More than one plugin attempted to override parsing.");
- } catch (err) {
- if (err.code === "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED") {
- err.message +=
- "\nConsider renaming the file to '.mjs', or setting sourceType:module " +
- "or sourceType:unambiguous in your Babel config for this file.";
- }
+// eslint-disable-next-line max-len
+const INLINE_SOURCEMAP_REGEX = /^[@#]\s+sourceMappingURL=data:(?:application|text)\/json;(?:charset[:=]\S+?;)?base64,(?:.*)$/;
+const EXTERNAL_SOURCEMAP_REGEX = /^[@#][ \t]+sourceMappingURL=([^\s'"`]+)[ \t]*$/;
- const { loc, missingPlugin } = err;
- if (loc) {
- const codeFrame = codeFrameColumns(
- code,
- {
- start: {
- line: loc.line,
- column: loc.column + 1,
- },
- },
- {
- highlightCode,
- },
- );
- if (missingPlugin) {
- err.message =
- `${filename}: ` +
- generateMissingPluginMessage(missingPlugin[0], loc, codeFrame);
- } else {
- err.message = `${filename}: ${err.message}\n\n` + codeFrame;
+function extractCommentsFromList(regex, comments, lastComment) {
+ if (comments) {
+ comments = comments.filter(({ value }) => {
+ if (regex.test(value)) {
+ lastComment = value;
+ return false;
}
- err.code = "BABEL_PARSE_ERROR";
- }
- throw err;
+ return true;
+ });
}
+ return [comments, lastComment];
+}
+
+function extractComments(regex, ast) {
+ let lastComment = null;
+ t.traverseFast(ast, node => {
+ // $FlowIgnore destructuring with expressions is not supported
+ [node.leadingComments, lastComment] = extractCommentsFromList(
+ regex,
+ node.leadingComments,
+ lastComment,
+ );
+ // $FlowIgnore destructuring with expressions is not supported
+ [node.innerComments, lastComment] = extractCommentsFromList(
+ regex,
+ node.innerComments,
+ lastComment,
+ );
+ // $FlowIgnore destructuring with expressions is not supported
+ [node.trailingComments, lastComment] = extractCommentsFromList(
+ regex,
+ node.trailingComments,
+ lastComment,
+ );
+ });
+ return lastComment;
}
diff --git a/packages/babel-core/src/transformation/plugin-pass.js b/packages/babel-core/src/transformation/plugin-pass.js
index 6e62c42bc786..e11f97804745 100644
--- a/packages/babel-core/src/transformation/plugin-pass.js
+++ b/packages/babel-core/src/transformation/plugin-pass.js
@@ -1,6 +1,7 @@
// @flow
import type File from "./file/file";
+import type NodeLocation from "./file/file";
export default class PluginPass {
_map: Map = new Map();
@@ -47,14 +48,7 @@ export default class PluginPass {
return this.file.getModuleName();
}
- buildCodeFrameError(
- node: ?{
- loc?: { start: { line: number, column: number } },
- _loc?: { start: { line: number, column: number } },
- },
- msg: string,
- Error?: typeof Error,
- ) {
+ buildCodeFrameError(node: ?NodeLocation, msg: string, Error?: typeof Error) {
return this.file.buildCodeFrameError(node, msg, Error);
}
}
diff --git a/packages/babel-core/test/api.js b/packages/babel-core/test/api.js
index 9f2f955f1d7e..5ce9a364c36f 100644
--- a/packages/babel-core/test/api.js
+++ b/packages/babel-core/test/api.js
@@ -760,11 +760,11 @@ describe("api", function() {
options,
function(err) {
expect(err.message).toMatch(
- "Support for the experimental syntax 'dynamicImport' isn't currently enabled (1:9)",
+ "Support for the experimental syntax 'pipelineOperator' isn't currently enabled (1:3):",
);
expect(err.message).toMatch(
- "Add @babel/plugin-syntax-dynamic-import (https://git.io/vb4Sv) to the " +
- "'plugins' section of your Babel config to enable parsing.",
+ "Add @babel/plugin-proposal-pipeline-operator (https://git.io/vb4SU) to the " +
+ "'plugins' section of your Babel config to enable transformation.",
);
done();
},
diff --git a/packages/babel-core/test/async.js b/packages/babel-core/test/async.js
new file mode 100644
index 000000000000..3d834c9e637d
--- /dev/null
+++ b/packages/babel-core/test/async.js
@@ -0,0 +1,214 @@
+import path from "path";
+import * as babel from "..";
+
+const nodeGte8 = (...args) => {
+ // "minNodeVersion": "8.0.0" <-- For Ctrl+F when dropping node 6
+ const testFn = process.version.slice(0, 3) === "v6." ? it.skip : it;
+ testFn(...args);
+};
+
+describe("asynchronicity", () => {
+ const base = path.join(__dirname, "fixtures", "async");
+ let cwd;
+
+ beforeEach(function() {
+ cwd = process.cwd();
+ process.chdir(base);
+ });
+
+ afterEach(function() {
+ process.chdir(cwd);
+ });
+
+ describe("config file", () => {
+ describe("async function", () => {
+ nodeGte8("called synchronously", () => {
+ process.chdir("config-file-async-function");
+
+ expect(() =>
+ babel.transformSync(""),
+ ).toThrowErrorMatchingInlineSnapshot(
+ `"You appear to be using an async configuration, which your current version of Babel does` +
+ ` not support. We may add support for this in the future, but if you're on the most recent` +
+ ` version of @babel/core and still seeing this error, then you'll need to synchronously` +
+ ` return your config."`,
+ );
+ });
+
+ nodeGte8("called asynchronously", async () => {
+ process.chdir("config-file-async-function");
+
+ await expect(
+ babel.transformAsync(""),
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
+ `"You appear to be using an async configuration, which your current version of Babel does` +
+ ` not support. We may add support for this in the future, but if you're on the most recent` +
+ ` version of @babel/core and still seeing this error, then you'll need to synchronously` +
+ ` return your config."`,
+ );
+ });
+ });
+
+ describe("promise", () => {
+ it("called synchronously", () => {
+ process.chdir("config-file-promise");
+
+ expect(() =>
+ babel.transformSync(""),
+ ).toThrowErrorMatchingInlineSnapshot(
+ `"You appear to be using an async configuration, which your current version of Babel does` +
+ ` not support. We may add support for this in the future, but if you're on the most recent` +
+ ` version of @babel/core and still seeing this error, then you'll need to synchronously` +
+ ` return your config."`,
+ );
+ });
+
+ it("called asynchronously", async () => {
+ process.chdir("config-file-promise");
+
+ await expect(
+ babel.transformAsync(""),
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
+ `"You appear to be using an async configuration, which your current version of Babel does` +
+ ` not support. We may add support for this in the future, but if you're on the most recent` +
+ ` version of @babel/core and still seeing this error, then you'll need to synchronously` +
+ ` return your config."`,
+ );
+ });
+ });
+
+ describe("cache.using", () => {
+ nodeGte8("called synchronously", () => {
+ process.chdir("config-cache");
+
+ expect(() =>
+ babel.transformSync(""),
+ ).toThrowErrorMatchingInlineSnapshot(
+ `"You appear to be using an async cache handler, which your current version of Babel does` +
+ ` not support. We may add support for this in the future, but if you're on the most recent` +
+ ` version of @babel/core and still seeing this error, then you'll need to synchronously` +
+ ` handle your caching logic."`,
+ );
+ });
+
+ nodeGte8("called asynchronously", async () => {
+ process.chdir("config-cache");
+
+ await expect(
+ babel.transformAsync(""),
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
+ `"You appear to be using an async cache handler, which your current version of Babel does` +
+ ` not support. We may add support for this in the future, but if you're on the most recent` +
+ ` version of @babel/core and still seeing this error, then you'll need to synchronously` +
+ ` handle your caching logic."`,
+ );
+ });
+ });
+ });
+
+ describe("plugin", () => {
+ describe("factory function", () => {
+ nodeGte8("called synchronously", () => {
+ process.chdir("plugin");
+
+ expect(() =>
+ babel.transformSync(""),
+ ).toThrowErrorMatchingInlineSnapshot(
+ `"[BABEL] unknown: You appear to be using an async plugin, which your current version of Babel` +
+ ` does not support. If you're using a published plugin, you may need to upgrade your` +
+ ` @babel/core version."`,
+ );
+ });
+
+ nodeGte8("called asynchronously", async () => {
+ process.chdir("plugin");
+
+ await expect(
+ babel.transformAsync(""),
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
+ `"[BABEL] unknown: You appear to be using an async plugin, which your current version of Babel` +
+ ` does not support. If you're using a published plugin, you may need to upgrade your` +
+ ` @babel/core version."`,
+ );
+ });
+ });
+
+ describe(".pre", () => {
+ nodeGte8("called synchronously", () => {
+ process.chdir("plugin-pre");
+
+ expect(() =>
+ babel.transformSync(""),
+ ).toThrowErrorMatchingInlineSnapshot(
+ `"unknown: You appear to be using an plugin with an async .pre, which your current version` +
+ ` of Babel does not support. If you're using a published plugin, you may need to upgrade your` +
+ ` @babel/core version."`,
+ );
+ });
+
+ nodeGte8("called asynchronously", async () => {
+ process.chdir("plugin-pre");
+
+ await expect(
+ babel.transformAsync(""),
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
+ `"unknown: You appear to be using an plugin with an async .pre, which your current version` +
+ ` of Babel does not support. If you're using a published plugin, you may need to upgrade your` +
+ ` @babel/core version."`,
+ );
+ });
+ });
+
+ describe(".post", () => {
+ nodeGte8("called synchronously", () => {
+ process.chdir("plugin-post");
+
+ expect(() =>
+ babel.transformSync(""),
+ ).toThrowErrorMatchingInlineSnapshot(
+ `"unknown: You appear to be using an plugin with an async .post, which your current version` +
+ ` of Babel does not support. If you're using a published plugin, you may need to upgrade your` +
+ ` @babel/core version."`,
+ );
+ });
+
+ nodeGte8("called asynchronously", async () => {
+ process.chdir("plugin-post");
+
+ await expect(
+ babel.transformAsync(""),
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
+ `"unknown: You appear to be using an plugin with an async .post, which your current version` +
+ ` of Babel does not support. If you're using a published plugin, you may need to upgrade your` +
+ ` @babel/core version."`,
+ );
+ });
+ });
+
+ describe("inherits", () => {
+ nodeGte8("called synchronously", () => {
+ process.chdir("plugin-inherits");
+
+ expect(() =>
+ babel.transformSync(""),
+ ).toThrowErrorMatchingInlineSnapshot(
+ `"[BABEL] unknown: You appear to be using an async plugin, which your current version of Babel` +
+ ` does not support. If you're using a published plugin, you may need to upgrade your` +
+ ` @babel/core version."`,
+ );
+ });
+
+ nodeGte8("called asynchronously", async () => {
+ process.chdir("plugin-inherits");
+
+ await expect(
+ babel.transformAsync(""),
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
+ `"[BABEL] unknown: You appear to be using an async plugin, which your current version of Babel` +
+ ` does not support. If you're using a published plugin, you may need to upgrade your` +
+ ` @babel/core version."`,
+ );
+ });
+ });
+ });
+});
diff --git a/packages/babel-core/test/caching-api.js b/packages/babel-core/test/caching-api.js
index 0318759fe861..f8d442c150b4 100644
--- a/packages/babel-core/test/caching-api.js
+++ b/packages/babel-core/test/caching-api.js
@@ -1,10 +1,12 @@
-import { makeStrongCache } from "../lib/config/caching";
+import gensync from "gensync";
+import { makeStrongCacheSync, makeStrongCache } from "../lib/config/caching";
+import { waitFor } from "../lib/gensync-utils/async";
describe("caching API", () => {
it("should allow permacaching with .forever()", () => {
let count = 0;
- const fn = makeStrongCache((arg, cache) => {
+ const fn = makeStrongCacheSync((arg, cache) => {
cache.forever();
return { arg, count: count++ };
});
@@ -21,7 +23,7 @@ describe("caching API", () => {
it("should allow disabling caching with .never()", () => {
let count = 0;
- const fn = makeStrongCache((arg, cache) => {
+ const fn = makeStrongCacheSync((arg, cache) => {
cache.never();
return { arg, count: count++ };
});
@@ -41,7 +43,7 @@ describe("caching API", () => {
let count = 0;
let other = "default";
- const fn = makeStrongCache((arg, cache) => {
+ const fn = makeStrongCacheSync((arg, cache) => {
const val = cache.using(() => other);
return { arg, val, count: count++ };
@@ -82,7 +84,7 @@ describe("caching API", () => {
let count = 0;
let other = "default";
- const fn = makeStrongCache((arg, cache) => {
+ const fn = makeStrongCacheSync((arg, cache) => {
const val = cache.invalidate(() => other);
return { arg, val, count: count++ };
@@ -124,7 +126,7 @@ describe("caching API", () => {
let other = "default";
let another = "another";
- const fn = makeStrongCache((arg, cache) => {
+ const fn = makeStrongCacheSync((arg, cache) => {
const val = cache.using(() => other);
const val2 = cache.invalidate(() => another);
@@ -223,7 +225,7 @@ describe("caching API", () => {
it("should auto-permacache by default", () => {
let count = 0;
- const fn = makeStrongCache(arg => ({ arg, count: count++ }));
+ const fn = makeStrongCacheSync(arg => ({ arg, count: count++ }));
expect(fn("one")).toEqual({ arg: "one", count: 0 });
expect(fn("one")).toBe(fn("one"));
@@ -235,7 +237,7 @@ describe("caching API", () => {
});
it("should throw if you set permacaching and use .using", () => {
- const fn = makeStrongCache((arg, cache) => {
+ const fn = makeStrongCacheSync((arg, cache) => {
cache.forever();
cache.using(() => null);
@@ -245,7 +247,7 @@ describe("caching API", () => {
});
it("should throw if you set permacaching and use .invalidate", () => {
- const fn = makeStrongCache((arg, cache) => {
+ const fn = makeStrongCacheSync((arg, cache) => {
cache.forever();
cache.invalidate(() => null);
@@ -255,7 +257,7 @@ describe("caching API", () => {
});
it("should throw if you set permacaching and use .never", () => {
- const fn = makeStrongCache((arg, cache) => {
+ const fn = makeStrongCacheSync((arg, cache) => {
cache.forever();
cache.never();
@@ -265,7 +267,7 @@ describe("caching API", () => {
});
it("should throw if you set no caching and use .using", () => {
- const fn = makeStrongCache((arg, cache) => {
+ const fn = makeStrongCacheSync((arg, cache) => {
cache.never();
cache.using(() => null);
@@ -275,7 +277,7 @@ describe("caching API", () => {
});
it("should throw if you set no caching and use .invalidate", () => {
- const fn = makeStrongCache((arg, cache) => {
+ const fn = makeStrongCacheSync((arg, cache) => {
cache.never();
cache.invalidate(() => null);
@@ -285,7 +287,7 @@ describe("caching API", () => {
});
it("should throw if you set no caching and use .never", () => {
- const fn = makeStrongCache((arg, cache) => {
+ const fn = makeStrongCacheSync((arg, cache) => {
cache.never();
cache.using(() => null);
@@ -295,7 +297,7 @@ describe("caching API", () => {
});
it("should throw if you configure .forever after exiting", () => {
- const fn = makeStrongCache((arg, cache) => cache);
+ const fn = makeStrongCacheSync((arg, cache) => cache);
expect(() => fn().forever()).toThrow(
/Cannot change caching after evaluation/,
@@ -303,7 +305,7 @@ describe("caching API", () => {
});
it("should throw if you configure .never after exiting", () => {
- const fn = makeStrongCache((arg, cache) => cache);
+ const fn = makeStrongCacheSync((arg, cache) => cache);
expect(() => fn().never()).toThrow(
/Cannot change caching after evaluation/,
@@ -311,7 +313,7 @@ describe("caching API", () => {
});
it("should throw if you configure .using after exiting", () => {
- const fn = makeStrongCache((arg, cache) => cache);
+ const fn = makeStrongCacheSync((arg, cache) => cache);
expect(() => fn().using(() => null)).toThrow(
/Cannot change caching after evaluation/,
@@ -319,7 +321,7 @@ describe("caching API", () => {
});
it("should throw if you configure .invalidate after exiting", () => {
- const fn = makeStrongCache((arg, cache) => cache);
+ const fn = makeStrongCacheSync((arg, cache) => cache);
expect(() => fn().invalidate(() => null)).toThrow(
/Cannot change caching after evaluation/,
@@ -330,7 +332,7 @@ describe("caching API", () => {
it("should allow permacaching with cache(true)", () => {
let count = 0;
- const fn = makeStrongCache((arg, cache) => {
+ const fn = makeStrongCacheSync((arg, cache) => {
cache = cache.simple();
cache(true);
@@ -349,7 +351,7 @@ describe("caching API", () => {
it("should allow disabling caching with cache(false)", () => {
let count = 0;
- const fn = makeStrongCache((arg, cache) => {
+ const fn = makeStrongCacheSync((arg, cache) => {
cache = cache.simple();
cache(false);
@@ -371,7 +373,7 @@ describe("caching API", () => {
let count = 0;
let other = "default";
- const fn = makeStrongCache((arg, cache) => {
+ const fn = makeStrongCacheSync((arg, cache) => {
cache = cache.simple();
const val = cache(() => other);
@@ -410,4 +412,60 @@ describe("caching API", () => {
expect(fn("two")).toBe(fn("two"));
});
});
+
+ describe("async", () => {
+ const wait = gensync({
+ sync: () => {},
+ errback: (t, cb) => setTimeout(cb, t),
+ });
+
+ it("should throw if the cache is configured asynchronously", async () => {
+ const fn = gensync(
+ makeStrongCache(function*(arg, cache) {
+ yield* wait(1000);
+ cache.never();
+ return { arg };
+ }),
+ ).async;
+
+ await expect(fn("bar")).rejects.toThrowErrorMatchingInlineSnapshot(
+ `"Cannot change caching after evaluation has completed."`,
+ );
+ });
+
+ it("should allow asynchronous cache invalidation functions", async () => {
+ const fn = gensync(
+ makeStrongCache(function*(arg, cache) {
+ yield* waitFor(
+ cache.using(async () => {
+ await wait.async(50);
+ return "x";
+ }),
+ );
+ return { arg };
+ }),
+ ).async;
+
+ const [res1, res2] = await Promise.all([fn("foo"), fn("foo")]);
+
+ expect(res1).toBe(res2);
+ });
+
+ it("should allow synchronous yield before cache configuration", async () => {
+ const fn = gensync(
+ makeStrongCache(function*(arg, cache) {
+ yield* gensync({
+ sync: () => 2,
+ errback: cb => cb(null, 2),
+ })();
+ cache.forever();
+ return { arg };
+ }),
+ ).async;
+
+ const [res1, res2] = await Promise.all([fn("foo"), fn("foo")]);
+
+ expect(res1).toBe(res2);
+ });
+ });
});
diff --git a/packages/babel-core/test/config-chain.js b/packages/babel-core/test/config-chain.js
index 849da38cfc02..aef0ba90cb3c 100644
--- a/packages/babel-core/test/config-chain.js
+++ b/packages/babel-core/test/config-chain.js
@@ -1,16 +1,72 @@
import fs from "fs";
+import os from "os";
import path from "path";
-import { loadOptions as loadOptionsOrig } from "../lib";
+import escapeRegExp from "lodash/escapeRegExp";
+import * as babel from "../lib";
+
+// TODO: In Babel 8, we can directly uses fs.promises which is supported by
+// node 8+
+const pfs =
+ fs.promises ??
+ new Proxy(fs, {
+ get(target, name) {
+ if (name === "copyFile") {
+ // fs.copyFile is only supported since node 8.5
+ // https://stackoverflow.com/a/30405105/2359289
+ return function copyFile(source, target) {
+ const rd = fs.createReadStream(source);
+ const wr = fs.createWriteStream(target);
+ return new Promise(function(resolve, reject) {
+ rd.on("error", reject);
+ wr.on("error", reject);
+ wr.on("finish", resolve);
+ rd.pipe(wr);
+ }).catch(function(error) {
+ rd.destroy();
+ wr.end();
+ throw error;
+ });
+ };
+ }
+
+ return (...args) =>
+ new Promise((resolve, reject) =>
+ target[name](...args, (error, result) => {
+ if (error) reject(error);
+ else resolve(result);
+ }),
+ );
+ },
+ });
function fixture(...args) {
return path.join(__dirname, "fixtures", "config", ...args);
}
function loadOptions(opts) {
- return loadOptionsOrig({
- cwd: __dirname,
- ...opts,
- });
+ return babel.loadOptions({ cwd: __dirname, ...opts });
+}
+
+function loadOptionsAsync(opts) {
+ return babel.loadOptionsAsync({ cwd: __dirname, ...opts });
+}
+
+function pairs(items) {
+ const pairs = [];
+ for (let i = 0; i < items.length - 1; i++) {
+ for (let j = i + 1; j < items.length; j++) {
+ pairs.push([items[i], items[j]]);
+ }
+ }
+ return pairs;
+}
+
+async function getTemp(name) {
+ const cwd = await pfs.mkdtemp(os.tmpdir() + path.sep + name);
+ const tmp = name => path.join(cwd, name);
+ const config = name =>
+ pfs.copyFile(fixture("config-files-templates", name), tmp(name));
+ return { cwd, tmp, config };
}
describe("buildConfigChain", function() {
@@ -33,7 +89,7 @@ describe("buildConfigChain", function() {
cwd: fixture("nonexistant-fake"),
filename: fixture("nonexistant-fake", "src.js"),
babelrc: false,
- test: new RegExp(fixture("nonexistant-fake")),
+ test: new RegExp(escapeRegExp(fixture("nonexistant-fake"))),
comments: true,
});
@@ -69,7 +125,7 @@ describe("buildConfigChain", function() {
cwd: fixture("nonexistant-fake"),
filename: fixture("nonexistant-fake", "src.js"),
babelrc: false,
- test: new RegExp(fixture("nonexistant-unknown")),
+ test: new RegExp(escapeRegExp(fixture("nonexistant-unknown"))),
comments: true,
});
@@ -107,7 +163,7 @@ describe("buildConfigChain", function() {
cwd: fixture("nonexistant-fake"),
filename: fixture("nonexistant-fake", "src.js"),
babelrc: false,
- test: [new RegExp(fixture("nonexistant-fake"))],
+ test: [new RegExp(escapeRegExp(fixture("nonexistant-fake")))],
comments: true,
});
@@ -143,7 +199,7 @@ describe("buildConfigChain", function() {
cwd: fixture("nonexistant-fake"),
filename: fixture("nonexistant-fake", "src.js"),
babelrc: false,
- test: [new RegExp(fixture("nonexistant-unknown"))],
+ test: [new RegExp(escapeRegExp(fixture("nonexistant-unknown")))],
comments: true,
});
@@ -183,7 +239,7 @@ describe("buildConfigChain", function() {
cwd: fixture("nonexistant-fake"),
filename: fixture("nonexistant-fake", "src.js"),
babelrc: false,
- include: new RegExp(fixture("nonexistant-fake")),
+ include: new RegExp(escapeRegExp(fixture("nonexistant-fake"))),
comments: true,
});
@@ -219,7 +275,7 @@ describe("buildConfigChain", function() {
cwd: fixture("nonexistant-fake"),
filename: fixture("nonexistant-fake", "src.js"),
babelrc: false,
- include: new RegExp(fixture("nonexistant-unknown")),
+ include: new RegExp(escapeRegExp(fixture("nonexistant-unknown"))),
comments: true,
});
@@ -257,7 +313,7 @@ describe("buildConfigChain", function() {
cwd: fixture("nonexistant-fake"),
filename: fixture("nonexistant-fake", "src.js"),
babelrc: false,
- include: [new RegExp(fixture("nonexistant-fake"))],
+ include: [new RegExp(escapeRegExp(fixture("nonexistant-fake")))],
comments: true,
});
@@ -293,7 +349,7 @@ describe("buildConfigChain", function() {
cwd: fixture("nonexistant-fake"),
filename: fixture("nonexistant-fake", "src.js"),
babelrc: false,
- include: [new RegExp(fixture("nonexistant-unknown"))],
+ include: [new RegExp(escapeRegExp(fixture("nonexistant-unknown")))],
comments: true,
});
@@ -333,7 +389,7 @@ describe("buildConfigChain", function() {
cwd: fixture("nonexistant-fake"),
filename: fixture("nonexistant-fake", "src.js"),
babelrc: false,
- exclude: new RegExp(fixture("nonexistant-fake")),
+ exclude: new RegExp(escapeRegExp(fixture("nonexistant-fake"))),
comments: true,
});
@@ -369,7 +425,7 @@ describe("buildConfigChain", function() {
cwd: fixture("nonexistant-fake"),
filename: fixture("nonexistant-fake", "src.js"),
babelrc: false,
- exclude: new RegExp(fixture("nonexistant-unknown")),
+ exclude: new RegExp(escapeRegExp(fixture("nonexistant-unknown"))),
comments: true,
});
@@ -407,7 +463,7 @@ describe("buildConfigChain", function() {
cwd: fixture("nonexistant-fake"),
filename: fixture("nonexistant-fake", "src.js"),
babelrc: false,
- exclude: [new RegExp(fixture("nonexistant-fake"))],
+ exclude: [new RegExp(escapeRegExp(fixture("nonexistant-fake")))],
comments: true,
});
@@ -443,7 +499,7 @@ describe("buildConfigChain", function() {
cwd: fixture("nonexistant-fake"),
filename: fixture("nonexistant-fake", "src.js"),
babelrc: false,
- exclude: [new RegExp(fixture("nonexistant-unknown"))],
+ exclude: [new RegExp(escapeRegExp(fixture("nonexistant-unknown")))],
comments: true,
});
@@ -943,111 +999,250 @@ describe("buildConfigChain", function() {
}
});
- it("should load .babelrc", () => {
- const filename = fixture("config-files", "babelrc", "src.js");
+ describe("root", () => {
+ test.each(["babel.config.json", "babel.config.js", "babel.config.cjs"])(
+ "should load %s synchronously",
+ async name => {
+ const { cwd, tmp, config } = await getTemp(
+ `babel-test-load-config-sync-${name}`,
+ );
+ const filename = tmp("src.js");
+
+ await config(name);
+
+ expect(loadOptions({ filename, cwd })).toEqual({
+ ...getDefaults(),
+ filename,
+ cwd,
+ root: cwd,
+ comments: true,
+ });
+ },
+ );
+
+ test("should not load babel.config.mjs synchronously", async () => {
+ const { cwd, tmp, config } = await getTemp(
+ "babel-test-load-config-sync-babel.config.mjs",
+ );
+ const filename = tmp("src.js");
- expect(
- loadOptions({
+ await config("babel.config.mjs");
+
+ expect(() => loadOptions({ filename, cwd })).toThrow(
+ /is only supported when running Babel asynchronously/,
+ );
+ });
+
+ test.each([
+ "babel.config.json",
+ "babel.config.js",
+ "babel.config.cjs",
+ "babel.config.mjs",
+ ])("should load %s asynchronously", async name => {
+ const { cwd, tmp, config } = await getTemp(
+ `babel-test-load-config-async-${name}`,
+ );
+ const filename = tmp("src.js");
+
+ // We can't transpile import() while publishing, and it isn't supported
+ // by jest.
+ if (process.env.IS_PUBLISH && name === "babel.config.mjs") return;
+
+ await config(name);
+
+ expect(await loadOptionsAsync({ filename, cwd })).toEqual({
+ ...getDefaults(),
filename,
- cwd: path.dirname(filename),
- }),
- ).toEqual({
- ...getDefaults(),
- filename: filename,
- cwd: path.dirname(filename),
- root: path.dirname(filename),
- comments: true,
+ cwd,
+ root: cwd,
+ comments: true,
+ });
});
- });
- it("should load .babelrc.js", () => {
- const filename = fixture("config-files", "babelrc-js", "src.js");
+ test.each(
+ pairs([
+ "babel.config.json",
+ "babel.config.js",
+ "babel.config.cjs",
+ "babel.config.mjs",
+ ]),
+ )("should throw if both %s and %s are used", async (name1, name2) => {
+ const { cwd, tmp, config } = await getTemp(
+ `babel-test-dup-config-${name1}-${name2}`,
+ );
+
+ // We can't transpile import() while publishing, and it isn't supported
+ // by jest.
+ if (
+ process.env.IS_PUBLISH &&
+ (name1 === "babel.config.mjs" || name2 === "babel.config.mjs")
+ ) {
+ return;
+ }
- expect(loadOptions({ filename, cwd: path.dirname(filename) })).toEqual({
- ...getDefaults(),
- filename: filename,
- cwd: path.dirname(filename),
- root: path.dirname(filename),
- comments: true,
+ await Promise.all([config(name1), config(name2)]);
+
+ await expect(
+ loadOptionsAsync({ filename: tmp("src.js"), cwd }),
+ ).rejects.toThrow(/Multiple configuration files found/);
});
});
- it("should load package.json#babel", () => {
- const filename = fixture("config-files", "pkg", "src.js");
+ describe("relative", () => {
+ test.each([
+ "package.json",
+ ".babelrc",
+ ".babelrc.js",
+ ".babelrc.cjs",
+ ".babelrc.json",
+ ])("should load %s synchronously", async name => {
+ const { cwd, tmp, config } = await getTemp(
+ `babel-test-load-config-${name}`,
+ );
+ const filename = tmp("src.js");
- expect(loadOptions({ filename, cwd: path.dirname(filename) })).toEqual({
- ...getDefaults(),
- filename: filename,
- cwd: path.dirname(filename),
- root: path.dirname(filename),
- comments: true,
+ await config(name);
+
+ expect(loadOptions({ filename, cwd })).toEqual({
+ ...getDefaults(),
+ filename,
+ cwd,
+ root: cwd,
+ comments: true,
+ });
});
- });
- it("should load .babelignore", () => {
- const filename = fixture("config-files", "babelignore", "src.js");
+ test("should not load .babelrc.mjs synchronously", async () => {
+ const { cwd, tmp, config } = await getTemp(
+ "babel-test-load-config-sync-.babelrc.mjs",
+ );
+ const filename = tmp("src.js");
- expect(loadOptions({ filename, cwd: path.dirname(filename) })).toBeNull();
- });
+ await config(".babelrc.mjs");
- it("should throw if there are both .babelrc and .babelrc.js", () => {
- const filename = fixture("config-files", "both-babelrc", "src.js");
+ expect(() => loadOptions({ filename, cwd })).toThrow(
+ /is only supported when running Babel asynchronously/,
+ );
+ });
- expect(() =>
- loadOptions({ filename, cwd: path.dirname(filename) }),
- ).toThrow(/Multiple configuration files found/);
- });
+ test.each([
+ "package.json",
+ ".babelrc",
+ ".babelrc.js",
+ ".babelrc.cjs",
+ ".babelrc.mjs",
+ ])("should load %s asynchronously", async name => {
+ const { cwd, tmp, config } = await getTemp(
+ `babel-test-load-config-${name}`,
+ );
+ const filename = tmp("src.js");
- it("should throw if there are both .babelrc and package.json", () => {
- const filename = fixture("config-files", "pkg-babelrc", "src.js");
+ // We can't transpile import() while publishing, and it isn't supported
+ // by jest.
+ if (process.env.IS_PUBLISH && name === ".babelrc.mjs") return;
- expect(() =>
- loadOptions({ filename, cwd: path.dirname(filename) }),
- ).toThrow(/Multiple configuration files found/);
- });
+ await config(name);
- it("should throw if there are both .babelrc.js and package.json", () => {
- const filename = fixture("config-files", "pkg-babelrc-js", "src.js");
+ expect(await loadOptionsAsync({ filename, cwd })).toEqual({
+ ...getDefaults(),
+ filename,
+ cwd,
+ root: cwd,
+ comments: true,
+ });
+ });
- expect(() =>
- loadOptions({ filename, cwd: path.dirname(filename) }),
- ).toThrow(/Multiple configuration files found/);
- });
+ it("should load .babelignore", () => {
+ const filename = fixture("config-files", "babelignore", "src.js");
- it("should ignore package.json without a 'babel' property", () => {
- const filename = fixture("config-files", "pkg-ignored", "src.js");
+ expect(
+ loadOptions({ filename, cwd: path.dirname(filename) }),
+ ).toBeNull();
+ });
- expect(loadOptions({ filename, cwd: path.dirname(filename) })).toEqual({
- ...getDefaults(),
- filename: filename,
- cwd: path.dirname(filename),
- root: path.dirname(filename),
- comments: true,
+ test.each(
+ pairs([
+ "package.json",
+ ".babelrc",
+ ".babelrc.js",
+ ".babelrc.cjs",
+ ".babelrc.mjs",
+ ".babelrc.json",
+ ]),
+ )("should throw if both %s and %s are used", async (name1, name2) => {
+ const { cwd, tmp, config } = await getTemp(
+ `babel-test-dup-config-${name1}-${name2}`,
+ );
+
+ // We can't transpile import() while publishing, and it isn't supported
+ // by jest.
+ if (
+ process.env.IS_PUBLISH &&
+ (name1 === ".babelrc.mjs" || name2 === ".babelrc.mjs")
+ ) {
+ return;
+ }
+
+ await Promise.all([config(name1), config(name2)]);
+
+ await expect(
+ loadOptionsAsync({ filename: tmp("src.js"), cwd }),
+ ).rejects.toThrow(/Multiple configuration files found/);
});
- });
- it("should show helpful errors for .babelrc", () => {
- const filename = fixture("config-files", "babelrc-error", "src.js");
+ it("should ignore package.json without a 'babel' property", () => {
+ const filename = fixture("config-files", "pkg-ignored", "src.js");
- expect(() =>
- loadOptions({ filename, cwd: path.dirname(filename) }),
- ).toThrow(/Error while parsing config - /);
- });
+ expect(loadOptions({ filename, cwd: path.dirname(filename) })).toEqual({
+ ...getDefaults(),
+ filename: filename,
+ cwd: path.dirname(filename),
+ root: path.dirname(filename),
+ comments: true,
+ });
+ });
- it("should show helpful errors for .babelrc.js", () => {
- const filename = fixture("config-files", "babelrc-js-error", "src.js");
+ test.each`
+ config | dir | error
+ ${".babelrc"} | ${"babelrc-error"} | ${/Error while parsing config - /}
+ ${".babelrc.json"} | ${"babelrc-json-error"} | ${/Error while parsing config - /}
+ ${".babelrc.js"} | ${"babelrc-js-error"} | ${/Babelrc threw an error/}
+ ${".babelrc.cjs"} | ${"babelrc-cjs-error"} | ${/Babelrc threw an error/}
+ ${".babelrc.mjs"} | ${"babelrc-mjs-error"} | ${/Babelrc threw an error/}
+ ${"package.json"} | ${"pkg-error"} | ${/Error while parsing JSON - /}
+ `("should show helpful errors for $config", async ({ dir, error }) => {
+ const filename = fixture("config-files", dir, "src.js");
- expect(() =>
- loadOptions({ filename, cwd: path.dirname(filename) }),
- ).toThrow(/Babelrc threw an error/);
+ // We can't transpile import() while publishing, and it isn't supported
+ // by jest.
+ if (process.env.IS_PUBLISH && dir === "babelrc-mjs-error") return;
+
+ await expect(
+ loadOptionsAsync({ filename, cwd: path.dirname(filename) }),
+ ).rejects.toThrow(error);
+ });
});
- it("should show helpful errors for package.json", () => {
- const filename = fixture("config-files", "pkg-error", "src.js");
+ it("should throw when `test` presents but `filename` is not passed", () => {
+ expect(() => loadOptions({ test: /\.ts$/, plugins: [] })).toThrow(
+ /Configuration contains string\/RegExp pattern/,
+ );
+ });
- expect(() =>
- loadOptions({ filename, cwd: path.dirname(filename) }),
- ).toThrow(/Error while parsing JSON - /);
+ it("should throw when `preset` requires `filename` but it was not passed", () => {
+ expect(() => {
+ loadOptions({
+ presets: [require("./fixtures/config-loading/preset4")],
+ });
+ }).toThrow(/Preset \/\* your preset \*\/ requires a filename/);
+ });
+
+ it("should throw when `preset.overrides` requires `filename` but it was not passed", () => {
+ expect(() => {
+ loadOptions({
+ presets: [require("./fixtures/config-loading/preset5")],
+ });
+ }).toThrow(/Preset \/\* your preset \*\/ requires a filename/);
});
});
});
diff --git a/packages/babel-core/test/config-loading.js b/packages/babel-core/test/config-loading.js
index b31eac8ed2d9..4f61315404e4 100644
--- a/packages/babel-core/test/config-loading.js
+++ b/packages/babel-core/test/config-loading.js
@@ -1,6 +1,8 @@
-import loadConfig, { loadPartialConfig } from "../lib/config";
+import loadConfigRunner, { loadPartialConfig } from "../lib/config";
import path from "path";
+const loadConfig = loadConfigRunner.sync;
+
describe("@babel/core config loading", () => {
const FILEPATH = path.join(
__dirname,
@@ -322,6 +324,21 @@ describe("@babel/core config loading", () => {
}
}
});
+
+ it("should thrown when plugin is not valid", () => {
+ const fooPlugin = {
+ inherits: "inhertis-should-not-be-string",
+ };
+ const opts = {
+ cwd: path.dirname(FILEPATH),
+ filename: FILEPATH,
+ plugins: [fooPlugin],
+ };
+
+ expect(() => loadConfig(opts)).toThrow(
+ /\.inherits must be a function, or undefined/,
+ );
+ });
});
describe("caller metadata", () => {
diff --git a/packages/babel-core/test/fixtures/api/parsing-errors/only-syntax/file.js b/packages/babel-core/test/fixtures/api/parsing-errors/only-syntax/file.js
index 55194f0636b3..c6c1a3ba7cd2 100644
--- a/packages/babel-core/test/fixtures/api/parsing-errors/only-syntax/file.js
+++ b/packages/babel-core/test/fixtures/api/parsing-errors/only-syntax/file.js
@@ -1 +1 @@
-var $ = import("jquery");
\ No newline at end of file
+a |> b
\ No newline at end of file
diff --git a/packages/babel-core/test/fixtures/async/config-cache/babel.config.js b/packages/babel-core/test/fixtures/async/config-cache/babel.config.js
new file mode 100644
index 000000000000..f17724435869
--- /dev/null
+++ b/packages/babel-core/test/fixtures/async/config-cache/babel.config.js
@@ -0,0 +1,12 @@
+const wait = t => new Promise(r => setTimeout(r, t));
+
+module.exports = function(api) {
+ api.cache.using(async () => {
+ await wait(50);
+ return 2;
+ })
+
+ return {
+ plugins: ["./plugin"],
+ };
+};
diff --git a/packages/babel-core/test/fixtures/async/config-cache/plugin.js b/packages/babel-core/test/fixtures/async/config-cache/plugin.js
new file mode 100644
index 000000000000..f3164a2364f9
--- /dev/null
+++ b/packages/babel-core/test/fixtures/async/config-cache/plugin.js
@@ -0,0 +1,9 @@
+module.exports = function plugin({ types: t }) {
+ return {
+ visitor: {
+ Program(path) {
+ path.pushContainer("body", t.stringLiteral("success"));
+ },
+ },
+ };
+};
diff --git a/packages/babel-core/test/fixtures/async/config-file-async-function/babel.config.js b/packages/babel-core/test/fixtures/async/config-file-async-function/babel.config.js
new file mode 100644
index 000000000000..14e85e8e700f
--- /dev/null
+++ b/packages/babel-core/test/fixtures/async/config-file-async-function/babel.config.js
@@ -0,0 +1,11 @@
+const wait = t => new Promise(r => setTimeout(r, t));
+
+module.exports = async function(api) {
+ await wait(50);
+
+ api.cache.never();
+
+ return {
+ plugins: ["./plugin"],
+ };
+};
diff --git a/packages/babel-core/test/fixtures/async/config-file-async-function/plugin.js b/packages/babel-core/test/fixtures/async/config-file-async-function/plugin.js
new file mode 100644
index 000000000000..f3164a2364f9
--- /dev/null
+++ b/packages/babel-core/test/fixtures/async/config-file-async-function/plugin.js
@@ -0,0 +1,9 @@
+module.exports = function plugin({ types: t }) {
+ return {
+ visitor: {
+ Program(path) {
+ path.pushContainer("body", t.stringLiteral("success"));
+ },
+ },
+ };
+};
diff --git a/packages/babel-core/test/fixtures/async/config-file-promise/babel.config.js b/packages/babel-core/test/fixtures/async/config-file-promise/babel.config.js
new file mode 100644
index 000000000000..dffa9f6fdbe1
--- /dev/null
+++ b/packages/babel-core/test/fixtures/async/config-file-promise/babel.config.js
@@ -0,0 +1,5 @@
+const wait = t => new Promise(r => setTimeout(r, t));
+
+module.exports = wait(50).then(() => ({
+ plugins: ["./plugin"],
+}));
diff --git a/packages/babel-core/test/fixtures/async/config-file-promise/plugin.js b/packages/babel-core/test/fixtures/async/config-file-promise/plugin.js
new file mode 100644
index 000000000000..f3164a2364f9
--- /dev/null
+++ b/packages/babel-core/test/fixtures/async/config-file-promise/plugin.js
@@ -0,0 +1,9 @@
+module.exports = function plugin({ types: t }) {
+ return {
+ visitor: {
+ Program(path) {
+ path.pushContainer("body", t.stringLiteral("success"));
+ },
+ },
+ };
+};
diff --git a/packages/babel-core/test/fixtures/async/plugin-inherits/babel.config.js b/packages/babel-core/test/fixtures/async/plugin-inherits/babel.config.js
new file mode 100644
index 000000000000..8b5df0db23c0
--- /dev/null
+++ b/packages/babel-core/test/fixtures/async/plugin-inherits/babel.config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ plugins: ["./plugin"],
+};
diff --git a/packages/babel-core/test/fixtures/async/plugin-inherits/plugin.js b/packages/babel-core/test/fixtures/async/plugin-inherits/plugin.js
new file mode 100644
index 000000000000..6f7f0b197f46
--- /dev/null
+++ b/packages/babel-core/test/fixtures/async/plugin-inherits/plugin.js
@@ -0,0 +1,10 @@
+module.exports = function plugin({ types: t }) {
+ return {
+ inherits: require("./plugin2"),
+ visitor: {
+ Program(path) {
+ path.pushContainer("body", t.stringLiteral("success"));
+ },
+ },
+ };
+};
diff --git a/packages/babel-core/test/fixtures/async/plugin-inherits/plugin2.js b/packages/babel-core/test/fixtures/async/plugin-inherits/plugin2.js
new file mode 100644
index 000000000000..f881463f9814
--- /dev/null
+++ b/packages/babel-core/test/fixtures/async/plugin-inherits/plugin2.js
@@ -0,0 +1,13 @@
+const wait = t => new Promise(r => setTimeout(r, t));
+
+module.exports = async function plugin({ types: t }) {
+ await wait(50);
+
+ return {
+ visitor: {
+ Program(path) {
+ path.pushContainer("body", t.stringLiteral("success 2"));
+ },
+ },
+ };
+};
diff --git a/packages/babel-core/test/fixtures/async/plugin-post/babel.config.js b/packages/babel-core/test/fixtures/async/plugin-post/babel.config.js
new file mode 100644
index 000000000000..8b5df0db23c0
--- /dev/null
+++ b/packages/babel-core/test/fixtures/async/plugin-post/babel.config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ plugins: ["./plugin"],
+};
diff --git a/packages/babel-core/test/fixtures/async/plugin-post/plugin.js b/packages/babel-core/test/fixtures/async/plugin-post/plugin.js
new file mode 100644
index 000000000000..8ceebfd644d4
--- /dev/null
+++ b/packages/babel-core/test/fixtures/async/plugin-post/plugin.js
@@ -0,0 +1,15 @@
+const wait = t => new Promise(r => setTimeout(r, t));
+
+module.exports = function plugin({ types: t }) {
+ return {
+ async post() {
+ await wait(50);
+ },
+
+ visitor: {
+ Program(path) {
+ path.pushContainer("body", t.stringLiteral("success"));
+ },
+ },
+ };
+};
diff --git a/packages/babel-core/test/fixtures/async/plugin-pre/babel.config.js b/packages/babel-core/test/fixtures/async/plugin-pre/babel.config.js
new file mode 100644
index 000000000000..8b5df0db23c0
--- /dev/null
+++ b/packages/babel-core/test/fixtures/async/plugin-pre/babel.config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ plugins: ["./plugin"],
+};
diff --git a/packages/babel-core/test/fixtures/async/plugin-pre/plugin.js b/packages/babel-core/test/fixtures/async/plugin-pre/plugin.js
new file mode 100644
index 000000000000..d354a0a60fb7
--- /dev/null
+++ b/packages/babel-core/test/fixtures/async/plugin-pre/plugin.js
@@ -0,0 +1,15 @@
+const wait = t => new Promise(r => setTimeout(r, t));
+
+module.exports = function plugin({ types: t }) {
+ return {
+ async pre() {
+ await wait(50);
+ },
+
+ visitor: {
+ Program(path) {
+ path.pushContainer("body", t.stringLiteral("success"));
+ },
+ },
+ };
+};
diff --git a/packages/babel-core/test/fixtures/async/plugin/babel.config.js b/packages/babel-core/test/fixtures/async/plugin/babel.config.js
new file mode 100644
index 000000000000..8b5df0db23c0
--- /dev/null
+++ b/packages/babel-core/test/fixtures/async/plugin/babel.config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ plugins: ["./plugin"],
+};
diff --git a/packages/babel-core/test/fixtures/async/plugin/plugin.js b/packages/babel-core/test/fixtures/async/plugin/plugin.js
new file mode 100644
index 000000000000..9777dd80ae42
--- /dev/null
+++ b/packages/babel-core/test/fixtures/async/plugin/plugin.js
@@ -0,0 +1,13 @@
+const wait = t => new Promise(r => setTimeout(r, t));
+
+module.exports = async function plugin({ types: t }) {
+ await wait(50);
+
+ return {
+ visitor: {
+ Program(path) {
+ path.pushContainer("body", t.stringLiteral("success"));
+ },
+ },
+ };
+};
diff --git a/packages/babel-core/test/fixtures/config-loading/preset4.js b/packages/babel-core/test/fixtures/config-loading/preset4.js
new file mode 100644
index 000000000000..1698ff05edd1
--- /dev/null
+++ b/packages/babel-core/test/fixtures/config-loading/preset4.js
@@ -0,0 +1,6 @@
+module.exports = function() {
+ return {
+ test: /\.ts$/,
+ plugins: []
+ }
+};
diff --git a/packages/babel-core/test/fixtures/config-loading/preset5.js b/packages/babel-core/test/fixtures/config-loading/preset5.js
new file mode 100644
index 000000000000..65ecb47f93c8
--- /dev/null
+++ b/packages/babel-core/test/fixtures/config-loading/preset5.js
@@ -0,0 +1,8 @@
+module.exports = function() {
+ return {
+ overrides: [{
+ test: /\.ts$/,
+ plugins: []
+ }]
+ }
+};
diff --git a/packages/babel-core/test/fixtures/config/config-files-templates/.babelrc b/packages/babel-core/test/fixtures/config/config-files-templates/.babelrc
new file mode 100644
index 000000000000..b445e5d66381
--- /dev/null
+++ b/packages/babel-core/test/fixtures/config/config-files-templates/.babelrc
@@ -0,0 +1,3 @@
+{
+ "comments": true
+}
diff --git a/packages/babel-core/test/fixtures/config/config-files-templates/.babelrc.cjs b/packages/babel-core/test/fixtures/config/config-files-templates/.babelrc.cjs
new file mode 100644
index 000000000000..409f4a98ac90
--- /dev/null
+++ b/packages/babel-core/test/fixtures/config/config-files-templates/.babelrc.cjs
@@ -0,0 +1,3 @@
+module.exports = {
+ comments: true
+};
diff --git a/packages/babel-core/test/fixtures/config/config-files-templates/.babelrc.js b/packages/babel-core/test/fixtures/config/config-files-templates/.babelrc.js
new file mode 100644
index 000000000000..409f4a98ac90
--- /dev/null
+++ b/packages/babel-core/test/fixtures/config/config-files-templates/.babelrc.js
@@ -0,0 +1,3 @@
+module.exports = {
+ comments: true
+};
diff --git a/packages/babel-core/test/fixtures/config/config-files-templates/.babelrc.json b/packages/babel-core/test/fixtures/config/config-files-templates/.babelrc.json
new file mode 100644
index 000000000000..b445e5d66381
--- /dev/null
+++ b/packages/babel-core/test/fixtures/config/config-files-templates/.babelrc.json
@@ -0,0 +1,3 @@
+{
+ "comments": true
+}
diff --git a/packages/babel-core/test/fixtures/config/config-files-templates/.babelrc.mjs b/packages/babel-core/test/fixtures/config/config-files-templates/.babelrc.mjs
new file mode 100644
index 000000000000..7fc8bbfea752
--- /dev/null
+++ b/packages/babel-core/test/fixtures/config/config-files-templates/.babelrc.mjs
@@ -0,0 +1,8 @@
+// Until Jest supports native mjs, we must simulate it 🤷
+
+module.exports = new Promise(resolve => resolve({
+ default: {
+ comments: true
+ }
+}));
+module.exports.__esModule = true;
diff --git a/packages/babel-core/test/fixtures/config/config-files-templates/babel.config.cjs b/packages/babel-core/test/fixtures/config/config-files-templates/babel.config.cjs
new file mode 100644
index 000000000000..409f4a98ac90
--- /dev/null
+++ b/packages/babel-core/test/fixtures/config/config-files-templates/babel.config.cjs
@@ -0,0 +1,3 @@
+module.exports = {
+ comments: true
+};
diff --git a/packages/babel-core/test/fixtures/config/config-files-templates/babel.config.js b/packages/babel-core/test/fixtures/config/config-files-templates/babel.config.js
new file mode 100644
index 000000000000..409f4a98ac90
--- /dev/null
+++ b/packages/babel-core/test/fixtures/config/config-files-templates/babel.config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ comments: true
+};
diff --git a/packages/babel-core/test/fixtures/config/config-files-templates/babel.config.json b/packages/babel-core/test/fixtures/config/config-files-templates/babel.config.json
new file mode 100644
index 000000000000..4b7be6033f68
--- /dev/null
+++ b/packages/babel-core/test/fixtures/config/config-files-templates/babel.config.json
@@ -0,0 +1,3 @@
+{
+ "comments": true
+}
\ No newline at end of file
diff --git a/packages/babel-core/test/fixtures/config/config-files-templates/babel.config.mjs b/packages/babel-core/test/fixtures/config/config-files-templates/babel.config.mjs
new file mode 100644
index 000000000000..7fc8bbfea752
--- /dev/null
+++ b/packages/babel-core/test/fixtures/config/config-files-templates/babel.config.mjs
@@ -0,0 +1,8 @@
+// Until Jest supports native mjs, we must simulate it 🤷
+
+module.exports = new Promise(resolve => resolve({
+ default: {
+ comments: true
+ }
+}));
+module.exports.__esModule = true;
diff --git a/packages/babel-core/test/fixtures/config/config-files/pkg/package.json b/packages/babel-core/test/fixtures/config/config-files-templates/package.json
similarity index 100%
rename from packages/babel-core/test/fixtures/config/config-files/pkg/package.json
rename to packages/babel-core/test/fixtures/config/config-files-templates/package.json
diff --git a/packages/babel-core/test/fixtures/config/config-files/babelrc-cjs-error/.babelrc.cjs b/packages/babel-core/test/fixtures/config/config-files/babelrc-cjs-error/.babelrc.cjs
new file mode 100644
index 000000000000..accbe7c461f1
--- /dev/null
+++ b/packages/babel-core/test/fixtures/config/config-files/babelrc-cjs-error/.babelrc.cjs
@@ -0,0 +1,3 @@
+module.exports = function() {
+ throw new Error("Babelrc threw an error");
+};
diff --git a/packages/babel-core/test/fixtures/config/config-files/babelrc-js/.babelrc.js b/packages/babel-core/test/fixtures/config/config-files/babelrc-js/.babelrc.js
deleted file mode 100644
index 37622f0ad309..000000000000
--- a/packages/babel-core/test/fixtures/config/config-files/babelrc-js/.babelrc.js
+++ /dev/null
@@ -1,3 +0,0 @@
-module.exports = {
- comments: true,
-};
diff --git a/packages/babel-core/test/fixtures/config/config-files/babelrc-json-error/.babelrc b/packages/babel-core/test/fixtures/config/config-files/babelrc-json-error/.babelrc
new file mode 100644
index 000000000000..1d936d04a4e4
--- /dev/null
+++ b/packages/babel-core/test/fixtures/config/config-files/babelrc-json-error/.babelrc
@@ -0,0 +1 @@
+{45
diff --git a/packages/babel-core/test/fixtures/config/config-files/babelrc-mjs-error/.babelrc.mjs b/packages/babel-core/test/fixtures/config/config-files/babelrc-mjs-error/.babelrc.mjs
new file mode 100644
index 000000000000..5a4fdd06a352
--- /dev/null
+++ b/packages/babel-core/test/fixtures/config/config-files/babelrc-mjs-error/.babelrc.mjs
@@ -0,0 +1,8 @@
+// Until Jest supports native mjs, we must simulate it 🤷
+
+module.exports = new Promise(resolve => resolve({
+ default: function () {
+ throw new Error("Babelrc threw an error");
+ }
+}));
+module.exports.__esModule = true;
diff --git a/packages/babel-core/test/fixtures/config/config-files/babelrc/.babelrc b/packages/babel-core/test/fixtures/config/config-files/babelrc/.babelrc
deleted file mode 100644
index 2f6b10872c8d..000000000000
--- a/packages/babel-core/test/fixtures/config/config-files/babelrc/.babelrc
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- comments: true,
-}
diff --git a/packages/babel-core/test/fixtures/config/config-files/both-babelrc/.babelrc b/packages/babel-core/test/fixtures/config/config-files/both-babelrc/.babelrc
deleted file mode 100644
index 0967ef424bce..000000000000
--- a/packages/babel-core/test/fixtures/config/config-files/both-babelrc/.babelrc
+++ /dev/null
@@ -1 +0,0 @@
-{}
diff --git a/packages/babel-core/test/fixtures/config/config-files/both-babelrc/.babelrc.js b/packages/babel-core/test/fixtures/config/config-files/both-babelrc/.babelrc.js
deleted file mode 100644
index f053ebf7976e..000000000000
--- a/packages/babel-core/test/fixtures/config/config-files/both-babelrc/.babelrc.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = {};
diff --git a/packages/babel-core/test/fixtures/config/config-files/pkg-babelrc-js/.babelrc.js b/packages/babel-core/test/fixtures/config/config-files/pkg-babelrc-js/.babelrc.js
deleted file mode 100644
index f053ebf7976e..000000000000
--- a/packages/babel-core/test/fixtures/config/config-files/pkg-babelrc-js/.babelrc.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = {};
diff --git a/packages/babel-core/test/fixtures/config/config-files/pkg-babelrc-js/package.json b/packages/babel-core/test/fixtures/config/config-files/pkg-babelrc-js/package.json
deleted file mode 100644
index e25be3ecd260..000000000000
--- a/packages/babel-core/test/fixtures/config/config-files/pkg-babelrc-js/package.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "babel": {}
-}
diff --git a/packages/babel-core/test/fixtures/config/config-files/pkg-babelrc/.babelrc b/packages/babel-core/test/fixtures/config/config-files/pkg-babelrc/.babelrc
deleted file mode 100644
index 0967ef424bce..000000000000
--- a/packages/babel-core/test/fixtures/config/config-files/pkg-babelrc/.babelrc
+++ /dev/null
@@ -1 +0,0 @@
-{}
diff --git a/packages/babel-core/test/fixtures/config/config-files/pkg-babelrc/package.json b/packages/babel-core/test/fixtures/config/config-files/pkg-babelrc/package.json
deleted file mode 100644
index e25be3ecd260..000000000000
--- a/packages/babel-core/test/fixtures/config/config-files/pkg-babelrc/package.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "babel": {}
-}
diff --git a/packages/babel-core/test/fixtures/parse/output.json b/packages/babel-core/test/fixtures/parse/output.json
index 06fb684869dc..5c48f0e09836 100644
--- a/packages/babel-core/test/fixtures/parse/output.json
+++ b/packages/babel-core/test/fixtures/parse/output.json
@@ -2,6 +2,7 @@
"type": "File",
"start": 0,
"end": 91,
+ "errors": [],
"loc": {
"start": {
"line": 1,
diff --git a/packages/babel-core/test/fixtures/plugins/build-code-frame-error/exec.js b/packages/babel-core/test/fixtures/plugins/build-code-frame-error/exec.js
index 3462e87a1932..0b56fb9bfb43 100644
--- a/packages/babel-core/test/fixtures/plugins/build-code-frame-error/exec.js
+++ b/packages/babel-core/test/fixtures/plugins/build-code-frame-error/exec.js
@@ -1,16 +1,18 @@
-var code = "function f() {}";
-transform(code, {
- plugins: [
- function() {
- return {
- visitor: {
- FunctionDeclaration: function(path) {
- throw path.buildCodeFrameError("someMsg");
+expect(() => {
+ var code = "function f() {}";
+ transform(code, {
+ plugins: [
+ function() {
+ return {
+ visitor: {
+ FunctionDeclaration: function(path) {
+ throw path.buildCodeFrameError("someMsg");
+ },
},
- },
- };
- },
- ],
- // hard to assert on ANSI escape codes
- highlightCode: false,
-});
+ };
+ },
+ ],
+ // hard to assert on ANSI escape codes
+ highlightCode: false,
+ });
+}).toThrow(/^unknown: someMsg\s+> 1 \| function f\(\) {}/);
diff --git a/packages/babel-core/test/fixtures/plugins/build-code-frame-error/options.json b/packages/babel-core/test/fixtures/plugins/build-code-frame-error/options.json
deleted file mode 100644
index b24f27d786a8..000000000000
--- a/packages/babel-core/test/fixtures/plugins/build-code-frame-error/options.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "throws": "undefined: someMsg\n> 1 | function f() {}"
-}
diff --git a/packages/babel-core/test/fixtures/plugins/transform-error/exec.js b/packages/babel-core/test/fixtures/plugins/transform-error/exec.js
new file mode 100644
index 000000000000..a5d11f514dea
--- /dev/null
+++ b/packages/babel-core/test/fixtures/plugins/transform-error/exec.js
@@ -0,0 +1,15 @@
+var code = "function f() {}";
+transform(code, {
+ plugins: [
+ function() {
+ return {
+ visitor: {
+ FunctionDeclaration: function(path) {
+ throw new Error("someMsg");
+ },
+ },
+ };
+ },
+ ],
+ filename: "/fake/path/example.js"
+});
diff --git a/packages/babel-core/test/fixtures/plugins/transform-error/options.json b/packages/babel-core/test/fixtures/plugins/transform-error/options.json
new file mode 100644
index 000000000000..056950478dcf
--- /dev/null
+++ b/packages/babel-core/test/fixtures/plugins/transform-error/options.json
@@ -0,0 +1,4 @@
+{
+ "throws": "/fake/path/example.js: someMsg",
+ "os": ["linux", "darwin"]
+}
diff --git a/packages/babel-core/test/fixtures/transformation/source-maps/comment-inside-string/input.js b/packages/babel-core/test/fixtures/transformation/source-maps/comment-inside-string/input.js
new file mode 100644
index 000000000000..b883f5d7b9a4
--- /dev/null
+++ b/packages/babel-core/test/fixtures/transformation/source-maps/comment-inside-string/input.js
@@ -0,0 +1,8 @@
+// https://github.com/babel/babel/issues/9790
+const comment = `//# sourceMappingURL=${path.basename(
+ sourceMapFilename
+)}`
+
+// https://github.com/babel/babel/issues/9956
+this.shadowRoot.innerHTML = ``;
diff --git a/packages/babel-core/test/fixtures/transformation/source-maps/comment-inside-string/options.json b/packages/babel-core/test/fixtures/transformation/source-maps/comment-inside-string/options.json
new file mode 100644
index 000000000000..0e6084f210d8
--- /dev/null
+++ b/packages/babel-core/test/fixtures/transformation/source-maps/comment-inside-string/options.json
@@ -0,0 +1,3 @@
+{
+ "inputSourceMap": true
+}
diff --git a/packages/babel-core/test/fixtures/transformation/source-maps/comment-inside-string/output.js b/packages/babel-core/test/fixtures/transformation/source-maps/comment-inside-string/output.js
new file mode 100644
index 000000000000..9188ec5abc3b
--- /dev/null
+++ b/packages/babel-core/test/fixtures/transformation/source-maps/comment-inside-string/output.js
@@ -0,0 +1,5 @@
+// https://github.com/babel/babel/issues/9790
+const comment = `//# sourceMappingURL=${path.basename(sourceMapFilename)}`; // https://github.com/babel/babel/issues/9956
+
+this.shadowRoot.innerHTML = ``;
diff --git a/packages/babel-core/test/fixtures/transformation/source-maps/input-source-map-external/input.js b/packages/babel-core/test/fixtures/transformation/source-maps/input-source-map-external/input.js
new file mode 100644
index 000000000000..f7250b67d0b4
--- /dev/null
+++ b/packages/babel-core/test/fixtures/transformation/source-maps/input-source-map-external/input.js
@@ -0,0 +1,5 @@
+var foo = function () {
+ return 4;
+};
+
+//# sourceMappingURL=input.js.map
diff --git a/packages/babel-core/test/fixtures/transformation/source-maps/input-source-map-external/input.js.map b/packages/babel-core/test/fixtures/transformation/source-maps/input-source-map-external/input.js.map
new file mode 100644
index 000000000000..cb88961ebdc0
--- /dev/null
+++ b/packages/babel-core/test/fixtures/transformation/source-maps/input-source-map-external/input.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["original.js"],"names":[],"mappings":"AAAA,UAAU,Y;SAAM,C;CAAC","sourcesContent":["var foo = () => 4;"]}
diff --git a/packages/babel-core/test/fixtures/transformation/source-maps/input-source-map-external/options.json b/packages/babel-core/test/fixtures/transformation/source-maps/input-source-map-external/options.json
new file mode 100644
index 000000000000..0e6084f210d8
--- /dev/null
+++ b/packages/babel-core/test/fixtures/transformation/source-maps/input-source-map-external/options.json
@@ -0,0 +1,3 @@
+{
+ "inputSourceMap": true
+}
diff --git a/packages/babel-core/test/fixtures/transformation/source-maps/input-source-map-external/output.js b/packages/babel-core/test/fixtures/transformation/source-maps/input-source-map-external/output.js
new file mode 100644
index 000000000000..cf3efeceb306
--- /dev/null
+++ b/packages/babel-core/test/fixtures/transformation/source-maps/input-source-map-external/output.js
@@ -0,0 +1,3 @@
+var foo = function () {
+ return 4;
+};
diff --git a/packages/babel-core/test/fixtures/transformation/source-maps/input-source-map-external/source-map.json b/packages/babel-core/test/fixtures/transformation/source-maps/input-source-map-external/source-map.json
new file mode 100644
index 000000000000..63c01db5fede
--- /dev/null
+++ b/packages/babel-core/test/fixtures/transformation/source-maps/input-source-map-external/source-map.json
@@ -0,0 +1,7 @@
+{
+ "mappings": "AAAA,IAAA,GAAA,GAAU,Y;SAAM,C;AAAC,CAAjB",
+ "names": [],
+ "sources": ["original.js"],
+ "sourcesContent": ["var foo = () => 4;"],
+ "version": 3
+}
diff --git a/packages/babel-core/test/option-manager.js b/packages/babel-core/test/option-manager.js
index 4a6301ab4777..5533d54c46d1 100644
--- a/packages/babel-core/test/option-manager.js
+++ b/packages/babel-core/test/option-manager.js
@@ -27,14 +27,19 @@ describe("option-manager", () => {
return { plugin, calls };
}
- it("should throw if a plugin is repeated", () => {
- const { calls, plugin } = makePlugin();
+ it("should throw if a plugin is repeated, with information about the repeated plugin", () => {
+ const { calls, plugin } = makePlugin("my-plugin");
expect(() => {
loadOptions({
- plugins: [plugin, plugin],
+ plugins: [
+ [plugin, undefined, "my-plugin"],
+ [plugin, undefined, "my-plugin"],
+ ],
});
- }).toThrow(/Duplicate plugin\/preset detected/);
+ }).toThrow(
+ /Duplicate plugin\/preset detected.*Duplicates detected are.*my-plugin.*my-plugin/ms,
+ );
expect(calls).toEqual([]);
});
@@ -54,7 +59,10 @@ describe("option-manager", () => {
const { calls: calls2, plugin: plugin2 } = makePlugin();
loadOptions({
- plugins: [[plugin1, { arg: 1 }], [plugin2, { arg: 2 }, "some-name"]],
+ plugins: [
+ [plugin1, { arg: 1 }],
+ [plugin2, { arg: 2 }, "some-name"],
+ ],
});
expect(calls1).toEqual([{ arg: 1 }]);
expect(calls2).toEqual([{ arg: 2 }]);
@@ -69,7 +77,10 @@ describe("option-manager", () => {
plugins: [[plugin1, { arg: 1 }]],
env: {
test: {
- plugins: [[plugin1, { arg: 3 }], [plugin2, { arg: 2 }]],
+ plugins: [
+ [plugin1, { arg: 3 }],
+ [plugin2, { arg: 2 }],
+ ],
},
},
});
@@ -93,7 +104,10 @@ describe("option-manager", () => {
const { calls: calls2, plugin: preset2 } = makePlugin();
loadOptions({
- presets: [[preset1, { arg: 1 }], [preset2, { arg: 2 }, "some-name"]],
+ presets: [
+ [preset1, { arg: 1 }],
+ [preset2, { arg: 2 }, "some-name"],
+ ],
});
expect(calls1).toEqual([{ arg: 1 }]);
expect(calls2).toEqual([{ arg: 2 }]);
@@ -107,7 +121,10 @@ describe("option-manager", () => {
presets: [[preset1, { arg: 1 }]],
env: {
test: {
- presets: [[preset1, { arg: 3 }], [preset2, { arg: 2 }]],
+ presets: [
+ [preset1, { arg: 3 }],
+ [preset2, { arg: 2 }],
+ ],
},
},
});
@@ -125,7 +142,10 @@ describe("option-manager", () => {
presets: [[preset1, { arg: 1 }]],
env: {
test: {
- presets: [[preset1, { arg: 3 }], [preset2, { arg: 2 }]],
+ presets: [
+ [preset1, { arg: 3 }],
+ [preset2, { arg: 2 }],
+ ],
},
},
});
diff --git a/packages/babel-generator/.npmignore b/packages/babel-generator/.npmignore
index f9806945836e..f3b1e0392a0b 100644
--- a/packages/babel-generator/.npmignore
+++ b/packages/babel-generator/.npmignore
@@ -1,3 +1,4 @@
src
test
+scripts
*.log
diff --git a/packages/babel-generator/package.json b/packages/babel-generator/package.json
index 815171acf262..b21685f11751 100644
--- a/packages/babel-generator/package.json
+++ b/packages/babel-generator/package.json
@@ -1,6 +1,6 @@
{
"name": "@babel/generator",
- "version": "7.5.5",
+ "version": "7.8.3",
"description": "Turns an AST into code.",
"author": "Sebastian McKenzie ",
"homepage": "https://babeljs.io/",
@@ -14,14 +14,13 @@
"lib"
],
"dependencies": {
- "@babel/types": "^7.5.5",
+ "@babel/types": "^7.8.3",
"jsesc": "^2.5.1",
"lodash": "^4.17.13",
- "source-map": "^0.5.0",
- "trim-right": "^1.0.1"
+ "source-map": "^0.5.0"
},
"devDependencies": {
- "@babel/helper-fixtures": "^7.5.5",
- "@babel/parser": "^7.5.5"
+ "@babel/helper-fixtures": "^7.8.3",
+ "@babel/parser": "^7.8.3"
}
}
diff --git a/packages/babel-generator/src/buffer.js b/packages/babel-generator/src/buffer.js
index fd64201a921b..d9af9a167a14 100644
--- a/packages/babel-generator/src/buffer.js
+++ b/packages/babel-generator/src/buffer.js
@@ -1,5 +1,4 @@
import type SourceMap from "./source-map";
-import trimRight from "trim-right";
const SPACES_RE = /^[ \t]+$/;
@@ -43,7 +42,7 @@ export default class Buffer {
const result = {
// Whatever trim is used here should not execute a regex against the
// source string since it may be arbitrarily large after all transformations
- code: trimRight(this._buf.join("")),
+ code: this._buf.join("").trimRight(),
map: null,
rawMappings: map && map.getRawMappings(),
};
diff --git a/packages/babel-generator/src/generators/classes.js b/packages/babel-generator/src/generators/classes.js
index e8c62339a6d2..1713293369d4 100644
--- a/packages/babel-generator/src/generators/classes.js
+++ b/packages/babel-generator/src/generators/classes.js
@@ -71,26 +71,8 @@ export function ClassBody(node: Object) {
export function ClassProperty(node: Object) {
this.printJoin(node.decorators, node);
+ this.tsPrintClassMemberModifiers(node, /* isField */ true);
- if (node.accessibility) {
- // TS
- this.word(node.accessibility);
- this.space();
- }
- if (node.static) {
- this.word("static");
- this.space();
- }
- if (node.abstract) {
- // TS
- this.word("abstract");
- this.space();
- }
- if (node.readonly) {
- // TS
- this.word("readonly");
- this.space();
- }
if (node.computed) {
this.token("[");
this.print(node.key, node);
@@ -148,23 +130,6 @@ export function ClassPrivateMethod(node: Object) {
export function _classMethodHead(node) {
this.printJoin(node.decorators, node);
-
- if (node.accessibility) {
- // TS
- this.word(node.accessibility);
- this.space();
- }
-
- if (node.abstract) {
- // TS
- this.word("abstract");
- this.space();
- }
-
- if (node.static) {
- this.word("static");
- this.space();
- }
-
+ this.tsPrintClassMemberModifiers(node, /* isField */ false);
this._methodHead(node);
}
diff --git a/packages/babel-generator/src/generators/expressions.js b/packages/babel-generator/src/generators/expressions.js
index 4f9a525bd05e..62c3aca83bc0 100644
--- a/packages/babel-generator/src/generators/expressions.js
+++ b/packages/babel-generator/src/generators/expressions.js
@@ -264,3 +264,8 @@ export function PrivateName(node: Object) {
this.token("#");
this.print(node.id, node);
}
+
+export function V8IntrinsicIdentifier(node: Object) {
+ this.token("%");
+ this.word(node.name);
+}
diff --git a/packages/babel-generator/src/generators/flow.js b/packages/babel-generator/src/generators/flow.js
index 225428b00801..9a8441ba827c 100644
--- a/packages/babel-generator/src/generators/flow.js
+++ b/packages/babel-generator/src/generators/flow.js
@@ -134,6 +134,92 @@ export function DeclareExportAllDeclaration(/*node: Object*/) {
ExportAllDeclaration.apply(this, arguments);
}
+export function EnumDeclaration(node: Object) {
+ const { id, body } = node;
+ this.word("enum");
+ this.space();
+ this.print(id, node);
+ this.print(body, node);
+}
+
+function enumExplicitType(
+ context: Object,
+ name: string,
+ hasExplicitType: boolean,
+) {
+ if (hasExplicitType) {
+ context.space();
+ context.word("of");
+ context.space();
+ context.word(name);
+ }
+ context.space();
+}
+
+function enumBody(context: Object, node: Object) {
+ const { members } = node;
+ context.token("{");
+ context.indent();
+ context.newline();
+ for (const member of members) {
+ context.print(member, node);
+ context.newline();
+ }
+ context.dedent();
+ context.token("}");
+}
+
+export function EnumBooleanBody(node: Object) {
+ const { explicitType } = node;
+ enumExplicitType(this, "boolean", explicitType);
+ enumBody(this, node);
+}
+
+export function EnumNumberBody(node: Object) {
+ const { explicitType } = node;
+ enumExplicitType(this, "number", explicitType);
+ enumBody(this, node);
+}
+
+export function EnumStringBody(node: Object) {
+ const { explicitType } = node;
+ enumExplicitType(this, "string", explicitType);
+ enumBody(this, node);
+}
+
+export function EnumSymbolBody(node: Object) {
+ enumExplicitType(this, "symbol", true);
+ enumBody(this, node);
+}
+
+export function EnumDefaultedMember(node: Object) {
+ const { id } = node;
+ this.print(id, node);
+ this.token(",");
+}
+
+function enumInitializedMember(context: Object, node: Object) {
+ const { id, init } = node;
+ context.print(id, node);
+ context.space();
+ context.token("=");
+ context.space();
+ context.print(init, node);
+ context.token(",");
+}
+
+export function EnumBooleanMember(node: Object) {
+ enumInitializedMember(this, node);
+}
+
+export function EnumNumberMember(node: Object) {
+ enumInitializedMember(this, node);
+}
+
+export function EnumStringMember(node: Object) {
+ enumInitializedMember(this, node);
+}
+
function FlowExportDeclaration(node: Object) {
if (node.declaration) {
const declar = node.declaration;
diff --git a/packages/babel-generator/src/generators/types.js b/packages/babel-generator/src/generators/types.js
index fdd090dd4113..fbad28e03b30 100644
--- a/packages/babel-generator/src/generators/types.js
+++ b/packages/babel-generator/src/generators/types.js
@@ -65,9 +65,9 @@ export function ObjectProperty(node: Object) {
// shorthand!
if (
node.shorthand &&
- (t.isIdentifier(node.key) &&
- t.isIdentifier(node.value) &&
- node.key.name === node.value.name)
+ t.isIdentifier(node.key) &&
+ t.isIdentifier(node.value) &&
+ node.key.name === node.value.name
) {
return;
}
diff --git a/packages/babel-generator/src/generators/typescript.js b/packages/babel-generator/src/generators/typescript.js
index 68d31f3d43f3..39c580d74d2f 100644
--- a/packages/babel-generator/src/generators/typescript.js
+++ b/packages/babel-generator/src/generators/typescript.js
@@ -129,6 +129,9 @@ export function TSIndexSignature(node) {
export function TSAnyKeyword() {
this.word("any");
}
+export function TSBigIntKeyword() {
+ this.word("bigint");
+}
export function TSUnknownKeyword() {
this.word("unknown");
}
@@ -194,11 +197,17 @@ export function TSTypeReference(node) {
}
export function TSTypePredicate(node) {
+ if (node.asserts) {
+ this.word("asserts");
+ this.space();
+ }
this.print(node.parameterName);
- this.space();
- this.word("is");
- this.space();
- this.print(node.typeAnnotation.typeAnnotation);
+ if (node.typeAnnotation) {
+ this.space();
+ this.word("is");
+ this.space();
+ this.print(node.typeAnnotation.typeAnnotation);
+ }
}
export function TSTypeQuery(node) {
@@ -547,3 +556,26 @@ export function tsPrintSignatureDeclarationBase(node) {
this.token(")");
this.print(node.typeAnnotation, node);
}
+
+export function tsPrintClassMemberModifiers(node, isField) {
+ if (isField && node.declare) {
+ this.word("declare");
+ this.space();
+ }
+ if (node.accessibility) {
+ this.word(node.accessibility);
+ this.space();
+ }
+ if (node.static) {
+ this.word("static");
+ this.space();
+ }
+ if (node.abstract) {
+ this.word("abstract");
+ this.space();
+ }
+ if (isField && node.readonly) {
+ this.word("readonly");
+ this.space();
+ }
+}
diff --git a/packages/babel-generator/src/index.js b/packages/babel-generator/src/index.js
index 4e7399d4bb47..9d288fb0f3cf 100644
--- a/packages/babel-generator/src/index.js
+++ b/packages/babel-generator/src/index.js
@@ -70,7 +70,8 @@ function normalizeOptions(code, opts): Format {
format.shouldPrintComment ||
(value =>
format.comments ||
- (value.indexOf("@license") >= 0 || value.indexOf("@preserve") >= 0));
+ value.indexOf("@license") >= 0 ||
+ value.indexOf("@preserve") >= 0);
}
if (format.compact === "auto") {
diff --git a/packages/babel-generator/src/node/index.js b/packages/babel-generator/src/node/index.js
index 394e63b5cbdd..faf700dc681b 100644
--- a/packages/babel-generator/src/node/index.js
+++ b/packages/babel-generator/src/node/index.js
@@ -97,5 +97,18 @@ export function needsParens(node, parent, printStack) {
if (isOrHasCallExpression(node)) return true;
}
+ /* this check is for NullishCoalescing being used with LogicalOperators like && and ||
+ * For example when someone creates an ast programmaticaly like this
+ * t.logicalExpression(
+ * "??",
+ * t.logicalExpression("||", t.identifier("a"), t.identifier("b")),
+ * t.identifier("c"),
+ * );
+ * In the example above the AST is equivalent to writing a || b ?? c
+ * This is incorrect because NullishCoalescing when used with LogicalExpressions should have parenthesis
+ * The correct syntax is (a || b) ?? c, that is why we need parenthesis in this case
+ */
+ if (t.isLogicalExpression(node) && parent.operator === "??") return true;
+
return find(expandedParens, node, parent, printStack);
}
diff --git a/packages/babel-generator/src/node/parentheses.js b/packages/babel-generator/src/node/parentheses.js
index 8c38fa627239..1222f2d34a84 100644
--- a/packages/babel-generator/src/node/parentheses.js
+++ b/packages/babel-generator/src/node/parentheses.js
@@ -2,6 +2,7 @@ import * as t from "@babel/types";
const PRECEDENCE = {
"||": 0,
+ "??": 0,
"&&": 1,
"|": 2,
"^": 3,
@@ -35,14 +36,22 @@ export function NullableTypeAnnotation(node: Object, parent: Object): boolean {
return t.isArrayTypeAnnotation(parent);
}
-export function FunctionTypeAnnotation(node: Object, parent: Object): boolean {
+export function FunctionTypeAnnotation(
+ node: Object,
+ parent: Object,
+ printStack: Array