Babel 没有将导入的 node_modules 转译为 ES5 - 包括 ES2015 语法

oli*_*ren 4 javascript webpack babeljs

我的 babel+webpack 配置工作正常,但生成的包无法在 IE11 中运行,因为它包含const声明。我认为有es2015预设就足以解决这个问题?运行$(npm bin)/babel test/some-es2015.js会产生严格的 ES5.1 代码,因此 Babel 似乎可以工作,但在 IE11 中运行的实际代码位于从node_modules.

当在我的结果包中 grep 时,'const '我得到这样的某些行(eval 是由于 eval 源映射顺便说一句):

eval("\nObject.defineProperty(exports, \"__esModule\", { value: true });\nconst validator = __webpack_require__(/*! validator */ \"./node_modules/tcomb-additional-types/node_modules/validator/index.js\");\nconst t = __webpack_require__(/*! tcomb */ \"./node_modules/tcomb/index.js\");\nconst IP = t.refinement(t.String, validator.isIP);\nexports.IP = IP;\nexports.default = IP;\n//# sourceMappingURL=ip.js.map\n\n//# sourceURL=webpack:///./node_modules/tcomb-additional-types/lib/string/ip.js?");
Run Code Online (Sandbox Code Playgroud)

需要注意的重要部分是诸如 之类的内容const validator =。这不是 ES5.1 语法。我自己的代码似乎已经很好地转换为 ES5 了。/node_modules/tcomb-additional-types/lib/string/ip.js我可以在他们使用 的地方看到这个文件const,所以这不是 Babel 添加consts,而是包含它们的源。大多数其他软件包都是 ES5。

到目前为止,我发现大多数consts 都来自material-uitcomb-additional-types

巴别塔.babelrc:

{
    "compact": false,
    "presets": [
        "es2015",
        "es2017"
    ],
    "plugins": [
        ["transform-runtime", {
            "polyfill": false,
            "regenerator": true
        }],
        "transform-class-properties",
        "transform-react-jsx",
        "transform-object-rest-spread"
    ]
}
Run Code Online (Sandbox Code Playgroud)

网页包配置:

const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin');

/** @returns {String} an absolute path */
function toRoot(rootRelativeDir) {
  return path.resolve(__dirname, '..', rootRelativeDir);
}

module.exports = {
  entry: ['./src/app.js', './styles/flex.less'].map(toRoot),
  output: {
    filename: 'bundle.js',
    path: toRoot('.webpack/dist')
  },
  resolve: {
    extensions: ['.js', '.jsx'],
    alias: {}
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              /* General options are read using .babelrc - only webpack loader specific here */
              cacheDirectory: toRoot('.webpack/babel_cache')
            }
          }
        ]
      }
    ]
  },
  plugins: [new CopyWebpackPlugin([toRoot('public')])]
};
Run Code Online (Sandbox Code Playgroud)

oli*_*ren 5

我的根本问题是一些 Node 包不是使用 ES5 语法编写的,并且 Babel 转换由于某种原因没有转换它们。这是正常问题

找出发生这种情况的原因非常容易(@Vincent 的回答有帮助);exclude: /node_modules/我在配置里有。当然,删除它会“解决”问题,但它会引入新的问题,因为它的exclude存在是有原因的,因为你不希望 Babel 处理其中的每个文件。

所以你想要的是这样的:选择性过滤允许某些模块

尝试构建一个正则表达式来允许 node_modules 下的包列表,但限制其余的包是很麻烦且容易出错的。值得庆幸的是,Webpack 文档描述了条件规则(其中exclude之一)可以是

  • 字符串:要匹配输入,必须以提供的字符串开头。IE。绝对目录路径或文件的绝对路径。
  • 正则表达式:它使用输入进行测试。
  • 函数:使用输入调用它,并且必须返回一个匹配的真值。
  • 一组条件:至少有一个条件必须匹配。
  • 对象:所有属性必须匹配。每个属性都有定义的行为。

创建这样的函数很容易!所以我没有将exclude: /node_modules改为 ,而是将其改为exclude: excludeCondition,其中excludeCondition有以下函数:

function excludeCondition(path){

  const nonEs5SyntaxPackages = [
    'material-ui',
    'tcomb-additional-types'
  ]

  // DO transpile these packages
  if (nonEs5SyntaxPackages.some( pkg => path.match(pkg))) {
    return false;
  }

  // Ignore all other modules that are in node_modules
  if (path.match(toRoot("node_modules"))) { return true; }

  else return false;
}
Run Code Online (Sandbox Code Playgroud)

这解决了我的问题,因为只有极少数包使用 ES2015 语法,因此将它们添加到允许列表中是可以管理的。


附录 既然人们询问了toRoot(),这是逐字代码:

/** @returns {String} an absolute path */
function toRoot(rootRelativeDir) {
  return path.resolve(__dirname, '..', rootRelativeDir);
}
Run Code Online (Sandbox Code Playgroud)

适应您自己的需求。

更完整的代码:

const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin');

/** @returns {String} an absolute path */
function toRoot(rootRelativeDir) {
  return path.resolve(__dirname, '..', rootRelativeDir);
}

function excludeCondition(path) {
  const nonEs2015Packages = ['tcomb-additional-types', 'material-ui'];

  // DO transpile these packages
  if (nonEs2015Packages.some(pkg => path.match(pkg))) {
    return false;
  }

  // Ignore all other modules that are in node_modules
  return Boolean(path.match(toRoot('node_modules')));
}

module.exports = {
  entry: ['./src/app.js', './styles/custom.less', './styles/flex.less', './styles/rc_slider.less'].map(toRoot),
  output: {
    filename: 'bundle.js',
    path: toRoot('.webpack/dist')
  },
  resolve: {
    extensions: ['.js', '.jsx'],
    alias: {}
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: excludeCondition,
        use: [
          {
            loader: 'babel-loader',
            options: {
              /* General options are read using .babelrc - only webpack loader specific here */
              cacheDirectory: toRoot('.webpack/babel_cache')
            }
          }
        ]
      }
    ]
  },
  plugins: [new CopyWebpackPlugin([toRoot('public')])]
};
Run Code Online (Sandbox Code Playgroud)