Skip to content

带你玩转 babel 工具链(六)是时候来看看 preset-env 的源码了

一、前言

本文将带你学习preset-env源码,彻底理解这些配置后的含义。

往期回顾:

二、基本配置

preset-env的配置中,添加了core-js的 polyfill 的支持。useBuiltIns指定按需加载。

js
npm i @babel/preset-env core-js@3
npm i @babel/preset-env core-js@3
js
{
  "presets": [
    ["@babel/preset-env", {
      "useBuiltIns": "usage",
      "corejs": 3
    }]
  ]
}
{
  "presets": [
    ["@babel/preset-env", {
      "useBuiltIns": "usage",
      "corejs": 3
    }]
  ]
}

简单使用一下,我们写了一段includes的 api,看看打包后的代码是如何polyfill

js
console.log([].includes("1"));
console.log([].includes("1"));

@babel/preset-env帮我们在顶部添加了一段导入代码。实现了includes的 api

image.png

以上就是一个简单的例子,下面介绍下参数详细的作用

三、通过参数分析源码过程

我们以上面的代码为例

js
{
  "presets": [
    ["@babel/preset-env", {
      "useBuiltIns": "xxx",
      "corejs": 3
    }]
  ]
}
{
  "presets": [
    ["@babel/preset-env", {
      "useBuiltIns": "xxx",
      "corejs": 3
    }]
  ]
}
js
console.log([].includes("1"));
console.log([].includes("1"));

注意:在下面的例子中,将统一使用corejs@3

下面我将一一演示preset-env的参数帮助理解,并且对大家以后的项目配置也有一定的帮助。大家耐心看完~

大家先打开源码位置: node_modules/@babel/preset-env/lib/index.js

参数 1:forceAllTransforms

源码

js
if (
  (0, _semver.lt)(api.version, "7.13.0") ||
  opts.targets ||
  opts.configPath ||
  opts.browserslistEnv ||
  opts.ignoreBrowserslistConfig
) {
  {
    var hasUglifyTarget = false;

    if (optionsTargets != null && optionsTargets.uglify) {
      hasUglifyTarget = true;
      delete optionsTargets.uglify;
      console.warn(`
The uglify target has been deprecated. Set the top level
option \`forceAllTransforms: true\` instead.
`);
    }
  }
  targets = getLocalTargets(
    optionsTargets,
    ignoreBrowserslistConfig,
    configPath,
    browserslistEnv
  );
}
// 需要转换的目标环境 如果为true 就全部转换
const transformTargets = forceAllTransforms || hasUglifyTarget ? {} : targets;
if (
  (0, _semver.lt)(api.version, "7.13.0") ||
  opts.targets ||
  opts.configPath ||
  opts.browserslistEnv ||
  opts.ignoreBrowserslistConfig
) {
  {
    var hasUglifyTarget = false;

    if (optionsTargets != null && optionsTargets.uglify) {
      hasUglifyTarget = true;
      delete optionsTargets.uglify;
      console.warn(`
The uglify target has been deprecated. Set the top level
option \`forceAllTransforms: true\` instead.
`);
    }
  }
  targets = getLocalTargets(
    optionsTargets,
    ignoreBrowserslistConfig,
    configPath,
    browserslistEnv
  );
}
// 需要转换的目标环境 如果为true 就全部转换
const transformTargets = forceAllTransforms || hasUglifyTarget ? {} : targets;

preset-env中,在一开始会调用getLocalTargets获取当前你配置的targets

例如我配置:

js
"targets": [
    "last 2 versions",
]
"targets": [
    "last 2 versions",
]

经过getLocalTargets处理后,targets如下

image.png

它会列出,浏览器所能支持的最低版本。

当你在preset-env中配置上forceAllTransforms: true,那么代表所有的代码都需要polyfill

我们跟着源码继续往下看~

参数 2:include、exclude

源码

js
// 1. 指定包含的插件,比如配置targets之后,有些插件被排除了,但是我就是想用这个插件
// 2. 指定要包含的corejs polyfill语法,例如es.map, es.set等
const include = transformIncludesAndExcludes(optionsInclude);

// 1. 指定排除的插件,比如配置targets之后,有些插件被包含了,但是我想排除它
// 2. 指定要排除的corejs polyfill语法,例如es.map, es.set等
const exclude = transformIncludesAndExcludes(optionsExclude);
// 1. 指定包含的插件,比如配置targets之后,有些插件被排除了,但是我就是想用这个插件
// 2. 指定要包含的corejs polyfill语法,例如es.map, es.set等
const include = transformIncludesAndExcludes(optionsInclude);

// 1. 指定排除的插件,比如配置targets之后,有些插件被包含了,但是我想排除它
// 2. 指定要排除的corejs polyfill语法,例如es.map, es.set等
const exclude = transformIncludesAndExcludes(optionsExclude);

includeexclude是相对立的,支持配置两种模式

  • 插件名称,例如@babel/plugin-transform-xxx
  • polyfill 名, 例如es.array.includes

什么场景需要这种配置呢?我们知道preset-env是支持targets配置的,但是不一定非常准确,有时候可能会把我们需要支持的语言特性排除掉了,所以这时候就需要include,来单独添加插件或polyfill。同样的exclude使用来排除,浏览器支持的语言特性。

在下面的配置中,我添加了targets配置,设置当前环境为chrome最新的两个版本。那么对于上面的例子来讲,是不会被 polyfill 的。

js
{
    "presets": [
      ["@babel/preset-env", {
        "useBuiltIns": "usage",
        "corejs": 3,
        "targets": [
          "last 2 Chrome versions"
        ]
      }]
    ]
}
{
    "presets": [
      ["@babel/preset-env", {
        "useBuiltIns": "usage",
        "corejs": 3,
        "targets": [
          "last 2 Chrome versions"
        ]
      }]
    ]
}

结果如我们预期那样:

image.png

这时候我添加一个配置

js
{
    "presets": [
      ["@babel/preset-env", {
        "useBuiltIns": "usage",
        "corejs": 3,
        "targets": [
          "last 2 Chrome versions"
        ],
       "include": [
          "es.array.includes" // 这里添加了配置
       ]
      }]
    ]
}
{
    "presets": [
      ["@babel/preset-env", {
        "useBuiltIns": "usage",
        "corejs": 3,
        "targets": [
          "last 2 Chrome versions"
        ],
       "include": [
          "es.array.includes" // 这里添加了配置
       ]
      }]
    ]
}

重新打包看下,发现已经能正常的 polyfill 了

image.png

当然,你也可以配置插件,例如:你的浏览器其实不支持for of语法,但被targets排除掉了。这种情况就可以配置上插件名。

js
{
  "presets": [
    ["@babel/preset-env", {
      "useBuiltIns": "usage",
      "corejs": 3,
      "targets": [
        "last 2 Chrome versions"
      ],
      "include": [
        // 在这里配置
        "@babel/plugin-transform-for-of"
      ]
    }]
  ]
}
{
  "presets": [
    ["@babel/preset-env", {
      "useBuiltIns": "usage",
      "corejs": 3,
      "targets": [
        "last 2 Chrome versions"
      ],
      "include": [
        // 在这里配置
        "@babel/plugin-transform-for-of"
      ]
    }]
  ]
}

以上就是include的作用,exclude想必不用多说了~

参数 3:targets

targets的写法大家可以参考这篇文章Browser list 详解

源码:

js
// 获取所有插件对应的环境
const compatData = getPluginList(shippedProposals, bugfixes);
// 获取所有插件对应的环境
const compatData = getPluginList(shippedProposals, bugfixes);

我们先看下compatData长什么样?

image.png

可以发现preset-env中,列出了所有插件对应的浏览器最低可以支持的版本。在后面将通过targets做进一步的筛选。

其实babel@babel/compat-data中维护了一套配置。 我们定位到这个目录

js
node_modules/@babel/compat-data/data/plugins.json
node_modules/@babel/compat-data/data/plugins.json

image.png

core-js中,也同样维护了一份polyfilltargets配置

node_modules/core-js-compat/data.json

image.png

参数 4:modules

源码

js
const shouldSkipExportNamespaceFrom =
  (modules === "auto" &&
    (api.caller == null ? void 0 : api.caller(supportsExportNamespaceFrom))) ||
  (modules === false &&
    !(0, _helperCompilationTargets.isRequired)(
      "proposal-export-namespace-from",
      transformTargets,
      {
        compatData,
        includes: include.plugins,
        excludes: exclude.plugins,
      }
    ));

// modules如果是umd这些模块规范,就会加载下面这些插件
// proposal-dynamic-import
// proposal-export-namespace-from
// syntax-top-level-await

// modules: false
// 只支持语法,不进行转换
// syntax-dynamic-import
// syntax-export-namespace-from
const modulesPluginNames = getModulesPluginNames({
  modules,
  transformations: _moduleTransformations.default,
  shouldTransformESM:
    modules !== "auto" ||
    !(api.caller != null && api.caller(supportsStaticESM)),
  shouldTransformDynamicImport:
    modules !== "auto" ||
    !(api.caller != null && api.caller(supportsDynamicImport)),
  shouldTransformExportNamespaceFrom: !shouldSkipExportNamespaceFrom,
  shouldParseTopLevelAwait: !api.caller || api.caller(supportsTopLevelAwait),
});
const shouldSkipExportNamespaceFrom =
  (modules === "auto" &&
    (api.caller == null ? void 0 : api.caller(supportsExportNamespaceFrom))) ||
  (modules === false &&
    !(0, _helperCompilationTargets.isRequired)(
      "proposal-export-namespace-from",
      transformTargets,
      {
        compatData,
        includes: include.plugins,
        excludes: exclude.plugins,
      }
    ));

// modules如果是umd这些模块规范,就会加载下面这些插件
// proposal-dynamic-import
// proposal-export-namespace-from
// syntax-top-level-await

// modules: false
// 只支持语法,不进行转换
// syntax-dynamic-import
// syntax-export-namespace-from
const modulesPluginNames = getModulesPluginNames({
  modules,
  transformations: _moduleTransformations.default,
  shouldTransformESM:
    modules !== "auto" ||
    !(api.caller != null && api.caller(supportsStaticESM)),
  shouldTransformDynamicImport:
    modules !== "auto" ||
    !(api.caller != null && api.caller(supportsDynamicImport)),
  shouldTransformExportNamespaceFrom: !shouldSkipExportNamespaceFrom,
  shouldParseTopLevelAwait: !api.caller || api.caller(supportsTopLevelAwait),
});

在上面的代码中,我们可以看到,都有一段这样的代码:api.caller

它的作用是什么呢,我们先看看文档:

image.png

意思就是,我们可以告诉babel,我们已经支持了部分语言特性,例如webpack它自身已经可以识别esm, 动态import, top-level-await了,并且还自己实现了。那么我们可以告诉babel, 你不需要自己去编译了~剩下交给我。。

所以我们能打开babel-loader, 看下它的配置

image.png

告诉babel以上的语法都是支持的。

这样,在下面的源码里,就可以做到按需添加模块转换插件

js
const getModulesPluginNames = ({
  modules,
  transformations,
  shouldTransformESM, // 是否转换esm
  shouldTransformDynamicImport, // 是否转换动态import
  shouldTransformExportNamespaceFrom, // 是否转换命名导出 export * as ns from "mod";
  shouldParseTopLevelAwait, // 是否编译toplevel await
}) => {
  const modulesPluginNames = [];

  if (modules !== false && transformations[modules]) {
    if (shouldTransformESM) {
      modulesPluginNames.push(transformations[modules]);
    }

    if (
      shouldTransformDynamicImport &&
      shouldTransformESM &&
      modules !== "umd"
    ) {
      modulesPluginNames.push("proposal-dynamic-import");
    } else {
      if (shouldTransformDynamicImport) {
        console.warn(
          "Dynamic import can only be supported when transforming ES modules" +
            " to AMD, CommonJS or SystemJS. Only the parser plugin will be enabled."
        );
      }

      modulesPluginNames.push("syntax-dynamic-import");
    }
  } else {
    modulesPluginNames.push("syntax-dynamic-import");
  }

  if (shouldTransformExportNamespaceFrom) {
    modulesPluginNames.push("proposal-export-namespace-from");
  } else {
    modulesPluginNames.push("syntax-export-namespace-from");
  }

  if (shouldParseTopLevelAwait) {
    modulesPluginNames.push("syntax-top-level-await");
  }

  return modulesPluginNames;
};
const getModulesPluginNames = ({
  modules,
  transformations,
  shouldTransformESM, // 是否转换esm
  shouldTransformDynamicImport, // 是否转换动态import
  shouldTransformExportNamespaceFrom, // 是否转换命名导出 export * as ns from "mod";
  shouldParseTopLevelAwait, // 是否编译toplevel await
}) => {
  const modulesPluginNames = [];

  if (modules !== false && transformations[modules]) {
    if (shouldTransformESM) {
      modulesPluginNames.push(transformations[modules]);
    }

    if (
      shouldTransformDynamicImport &&
      shouldTransformESM &&
      modules !== "umd"
    ) {
      modulesPluginNames.push("proposal-dynamic-import");
    } else {
      if (shouldTransformDynamicImport) {
        console.warn(
          "Dynamic import can only be supported when transforming ES modules" +
            " to AMD, CommonJS or SystemJS. Only the parser plugin will be enabled."
        );
      }

      modulesPluginNames.push("syntax-dynamic-import");
    }
  } else {
    modulesPluginNames.push("syntax-dynamic-import");
  }

  if (shouldTransformExportNamespaceFrom) {
    modulesPluginNames.push("proposal-export-namespace-from");
  } else {
    modulesPluginNames.push("syntax-export-namespace-from");
  }

  if (shouldParseTopLevelAwait) {
    modulesPluginNames.push("syntax-top-level-await");
  }

  return modulesPluginNames;
};

另外,还会根据你的modules配置,去添加对应的模块转换插件, 可以看到默认是auto,使用了commonjs模块转换插件

js
{
  auto: "transform-modules-commonjs",
  amd: "transform-modules-amd",
  commonjs: "transform-modules-commonjs",
  cjs: "transform-modules-commonjs",
  systemjs: "transform-modules-systemjs",
  umd: "transform-modules-umd"
}
{
  auto: "transform-modules-commonjs",
  amd: "transform-modules-amd",
  commonjs: "transform-modules-commonjs",
  cjs: "transform-modules-commonjs",
  systemjs: "transform-modules-systemjs",
  umd: "transform-modules-umd"
}

总结一下:

  1. 获取当前环境是否支持命名空间导出,例如export * as xxx from 'xxx'

  2. 获取对应的模块插件,如果还支持top-level-await就返回syntax-top-level-await, 如果有动态 import, 就返回syntax-dynamic-import(其中有一些细节,不详细展开了)

    js
    // node_modules/@babel/preset-env/lib/module-transformations.js
    {
      auto: "transform-modules-commonjs",
      amd: "transform-modules-amd",
      commonjs: "transform-modules-commonjs",
      cjs: "transform-modules-commonjs",
      systemjs: "transform-modules-systemjs",
      umd: "transform-modules-umd"
    }
    // node_modules/@babel/preset-env/lib/module-transformations.js
    {
      auto: "transform-modules-commonjs",
      amd: "transform-modules-amd",
      commonjs: "transform-modules-commonjs",
      cjs: "transform-modules-commonjs",
      systemjs: "transform-modules-systemjs",
      umd: "transform-modules-umd"
    }

    如果配置modules: false,其实不需要做转换了,只需要支持语法 ,以下是配置modules: false之后所需的插件。

    image.png

由于modules默认值为auto, 所以默认的模块规范就是commonjs, 进而使用@babel/transform-modules-commonjs进行转换。

其他配置同理~

参数 5:useBuiltIns

该配置必须和corejs搭配使用

源码

前面说到babel维护了一套compactData配置。

image.png

下面就会根据环境配置,筛选出需要的插件

js
// 根据目标环境 筛选出需要的插件
const pluginNames = (0, _helperCompilationTargets.filterItems)(
  compatData,
  include.plugins,
  exclude.plugins,
  transformTargets,
  modulesPluginNames,
  (0, _getOptionSpecificExcludes.default)({
    loose,
  }),
  _shippedProposals.pluginSyntaxMap
);
// 根据目标环境 筛选出需要的插件
const pluginNames = (0, _helperCompilationTargets.filterItems)(
  compatData,
  include.plugins,
  exclude.plugins,
  transformTargets,
  modulesPluginNames,
  (0, _getOptionSpecificExcludes.default)({
    loose,
  }),
  _shippedProposals.pluginSyntaxMap
);

image.png

获取到需要的插件后,就到达很关键的地方了, 我们看下polyfill是如何添加的

js
// 获取polyfill插件
const polyfillPlugins = getPolyfillPlugins({
  useBuiltIns,
  corejs,
  polyfillTargets: targets,
  include: include.builtIns,
  exclude: exclude.builtIns,
  proposals,
  shippedProposals,
  regenerator: pluginNames.has("transform-regenerator"),
  debug,
});
// 获取polyfill插件
const polyfillPlugins = getPolyfillPlugins({
  useBuiltIns,
  corejs,
  polyfillTargets: targets,
  include: include.builtIns,
  exclude: exclude.builtIns,
  proposals,
  shippedProposals,
  regenerator: pluginNames.has("transform-regenerator"),
  debug,
});
js
const getPolyfillPlugins = ({
  useBuiltIns,
  corejs,
  polyfillTargets,
  include,
  exclude,
  proposals,
  shippedProposals,
  regenerator,
  debug,
}) => {
  const polyfillPlugins = [];

  if (useBuiltIns === "usage" || useBuiltIns === "entry") {
    const pluginOptions = {
      method: `${useBuiltIns}-global`,
      version: corejs ? corejs.toString() : undefined,
      targets: polyfillTargets,
      include,
      exclude,
      proposals,
      shippedProposals,
      debug,
    };
    // 判断是否配置corejs
    if (corejs) {
      if (useBuiltIns === "usage") {
        if (corejs.major === 2) {
          // 添加 babel-plugin-polyfill-corejs2 和 babel-polyfill 插件
          polyfillPlugins.push(
            [pluginCoreJS2, pluginOptions],
            [
              _babelPolyfill.default,
              {
                usage: true,
              },
            ]
          );
        } else {
          // 添加 babel-plugin-polyfill-corejs3 插件 和 babel-polyfill 插件
          polyfillPlugins.push(
            [pluginCoreJS3, pluginOptions],
            [
              _babelPolyfill.default,
              {
                usage: true,
                deprecated: true,
              },
            ]
          );
        }
        // 添加 babel-plugin-polyfill-regenerator 插件
        if (regenerator) {
          polyfillPlugins.push([
            pluginRegenerator,
            {
              method: "usage-global",
              debug,
            },
          ]);
        }
      } else {
        if (corejs.major === 2) {
          // babel-polyfill 插件(全局引入)、babel-plugin-polyfill-corejs2插件
          // 注意插件执行顺序,先执行的babel-polyfill
          polyfillPlugins.push(
            [
              _babelPolyfill.default,
              {
                regenerator,
              },
            ],
            [pluginCoreJS2, pluginOptions]
          );
        } else {
          // 添加 babel-plugin-polyfill-corejs3 插件 和 babel-polyfill 插件
          polyfillPlugins.push(
            [pluginCoreJS3, pluginOptions],
            [
              _babelPolyfill.default,
              {
                deprecated: true,
              },
            ]
          );

          if (!regenerator) {
            polyfillPlugins.push([_regenerator.default, pluginOptions]);
          }
        }
      }
    }
  }

  return polyfillPlugins;
};
const getPolyfillPlugins = ({
  useBuiltIns,
  corejs,
  polyfillTargets,
  include,
  exclude,
  proposals,
  shippedProposals,
  regenerator,
  debug,
}) => {
  const polyfillPlugins = [];

  if (useBuiltIns === "usage" || useBuiltIns === "entry") {
    const pluginOptions = {
      method: `${useBuiltIns}-global`,
      version: corejs ? corejs.toString() : undefined,
      targets: polyfillTargets,
      include,
      exclude,
      proposals,
      shippedProposals,
      debug,
    };
    // 判断是否配置corejs
    if (corejs) {
      if (useBuiltIns === "usage") {
        if (corejs.major === 2) {
          // 添加 babel-plugin-polyfill-corejs2 和 babel-polyfill 插件
          polyfillPlugins.push(
            [pluginCoreJS2, pluginOptions],
            [
              _babelPolyfill.default,
              {
                usage: true,
              },
            ]
          );
        } else {
          // 添加 babel-plugin-polyfill-corejs3 插件 和 babel-polyfill 插件
          polyfillPlugins.push(
            [pluginCoreJS3, pluginOptions],
            [
              _babelPolyfill.default,
              {
                usage: true,
                deprecated: true,
              },
            ]
          );
        }
        // 添加 babel-plugin-polyfill-regenerator 插件
        if (regenerator) {
          polyfillPlugins.push([
            pluginRegenerator,
            {
              method: "usage-global",
              debug,
            },
          ]);
        }
      } else {
        if (corejs.major === 2) {
          // babel-polyfill 插件(全局引入)、babel-plugin-polyfill-corejs2插件
          // 注意插件执行顺序,先执行的babel-polyfill
          polyfillPlugins.push(
            [
              _babelPolyfill.default,
              {
                regenerator,
              },
            ],
            [pluginCoreJS2, pluginOptions]
          );
        } else {
          // 添加 babel-plugin-polyfill-corejs3 插件 和 babel-polyfill 插件
          polyfillPlugins.push(
            [pluginCoreJS3, pluginOptions],
            [
              _babelPolyfill.default,
              {
                deprecated: true,
              },
            ]
          );

          if (!regenerator) {
            polyfillPlugins.push([_regenerator.default, pluginOptions]);
          }
        }
      }
    }
  }

  return polyfillPlugins;
};

我们可以总结如下几点

  • 存在 corejs 配置

    • useBuiltIns: usage

      • 如果配置 core-js@3

        • 添加 babel-plugin-polyfill-corejs3 插件
        • 添加 babel-polyfill 插件 (@babel/preset-env/lib/polyfills/babel-polyfill.js)
      • 如果配置 core-js@2

        • 添加 babel-plugin-polyfill-corejs2 插件
        • 添加 babel-polyfill 插件 (@babel/preset-env/lib/polyfills/babel-polyfill.js)
      • 如果配置 transform-regenerator

        • 添加 babel-plugin-polyfill-regenerator 插件
    • useBuiltIns: entry | false

      • 如果配置 core-js@3

        • 添加 babel-plugin-polyfill-corejs3 插件
        • 添加 babel-polyfill 插件 (@babel/preset-env/lib/polyfills/babel-polyfill.js)
      • 如果配置 core-js@2

        • 添加 babel-plugin-polyfill-corejs2
        • 添加 babel-polyfill 插件 (@babel/preset-env/lib/polyfills/babel-polyfill.js)
      • 如果没有配置 transform-regenerator 插件

        • 添加 regenerator 插件删除 regenerator 导入(@babel/preset-env/lib/polyfills/regenerator.js)

使用:

好的,上面就是polyfill插件的具体添加过程,下面我们来看看useBuiltIns是如何使用的。

  • useBuiltIns: "usage"的配置下,打包结果如下 image.png 可以看到能够实现按需引入

  • useBuiltIns: "entry"的配置下,还需要在入口文件中添加core-js的导入,如何你还想支持async语法,还需要引入regenerator-runtime/runtime.js

    js
    import "core-js"; // 其他语言特性支持
    import "regenerator-runtime/runtime.js"; // 支持async
    console.log([].includes("1"));
    import "core-js"; // 其他语言特性支持
    import "regenerator-runtime/runtime.js"; // 支持async
    console.log([].includes("1"));

    image.png

    可以看到,会把所有的 polyfill 都引入进来,所以entry的配置并不推荐使用,会全量引入

  • useBuiltIns: false配置下,core-js配置将失效,不会帮助引入polyfill

参数 6:corejs

corejs 就比较简单了,指定 corejs 的版本就可以了,但是必须搭配useBuiltIns使用哦~

参数 7:debug

源码

js
if (debug) {
  console.log("@babel/preset-env: `DEBUG` option");
  console.log("\nUsing targets:");
  console.log(
    JSON.stringify(
      (0, _helperCompilationTargets.prettifyTargets)(targets),
      null,
      2
    )
  );
  console.log(`\nUsing modules transform: ${modules.toString()}`);
  console.log("\nUsing plugins:");
  pluginNames.forEach((pluginName) => {
    (0, _debug.logPlugin)(pluginName, targets, compatData);
  });

  if (!useBuiltIns) {
    console.log(
      "\nUsing polyfills: No polyfills were added, since the `useBuiltIns` option was not set."
    );
  }
}
if (debug) {
  console.log("@babel/preset-env: `DEBUG` option");
  console.log("\nUsing targets:");
  console.log(
    JSON.stringify(
      (0, _helperCompilationTargets.prettifyTargets)(targets),
      null,
      2
    )
  );
  console.log(`\nUsing modules transform: ${modules.toString()}`);
  console.log("\nUsing plugins:");
  pluginNames.forEach((pluginName) => {
    (0, _debug.logPlugin)(pluginName, targets, compatData);
  });

  if (!useBuiltIns) {
    console.log(
      "\nUsing polyfills: No polyfills were added, since the `useBuiltIns` option was not set."
    );
  }
}

使用:

当配置上debug: true后,控制台就能看见你使用了哪些插件

image.png

四、babel-plugin-polyfill-corejs3

我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿

helper-define-polyfill-provider