记一次webpack构建报错

五一结束,开开心心的上班,结果第一天就给了一个惊喜,原本webpack构建好好地,突然就报了一个错。

错误日志如下

1
Can't import the named export 'SetArray' from non EcmaScript module (only default export is available

详细错误日志如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[Build] [BABEL] Note: The code generator has deoptimised the styling of /data/auto-ci/wspace/node_modules/lodash/lodash.js as it exceeds the max of 500KB.
[Build] ./node_modules/@jridgewell/gen-mapping/dist/gen-mapping.mjs 38:20-28
[Build] Can't import the named export 'SetArray' from non EcmaScript module (only default export is available)
[Build] at HarmonyImportSpecifierDependency._getErrors (/data/auto-ci/wspace/node_modules/webpack/lib/dependencies/HarmonyImportSpecifierDependency.js:88:6)
[Build] at HarmonyImportSpecifierDependency.getErrors (/data/auto-ci/wspace/node_modules/webpack/lib/dependencies/HarmonyImportSpecifierDependency.js:68:16)
[Build] at Compilation.reportDependencyErrorsAndWarnings (/data/auto-ci/wspace/node_modules/webpack/lib/Compilation.js:1463:22)
[Build] at /data/auto-ci/wspace/node_modules/webpack/lib/Compilation.js:1258:10
[Build] at AsyncSeriesHook.eval [as callAsync] (eval at create (/data/auto-ci/wspace/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:22:1)
[Build] at AsyncSeriesHook.lazyCompileHook (/data/auto-ci/wspace/node_modules/tapable/lib/Hook.js:154:20)
[Build] at Compilation.finish (/data/auto-ci/wspace/node_modules/webpack/lib/Compilation.js:1253:28)
[Build] at /data/auto-ci/wspace/node_modules/webpack/lib/Compiler.js:672:17
[Build] at _done (eval at create (/data/auto-ci/wspace/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:7:1)
[Build] at eval (eval at create (/data/auto-ci/wspace/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:30:22)
[Build] at /data/auto-ci/wspace/node_modules/webpack/lib/Compilation.js:1185:12
[Build] at /data/auto-ci/wspace/node_modules/webpack/lib/Compilation.js:1097:9
[Build] at processTicksAndRejections (internal/process/task_queues.js:79:11)
[Build] @ ./node_modules/@babel/generator/lib/source-map.js
[Build] @ ./node_modules/@babel/generator/lib/index.js

源码分析

经过一番测试大致锁定了报错范围。

直接报错的位置在 @jridgewell/gen-mapping 这个包,这个包在引入@jridgewell/set-array
而上面错误的详细信息也可以看出就是
Can't import the named export 'SetArray' from non EcmaScript module (only default export is available)
大致意思就是无法导出 SetArray 这个模块。

于是github上找到这两个包的源码

1
2
// @jridgewell/gen-maping/dist/gen-mapping.mjs
import { SetArray, put } from '@jridgewell/set-array';
1
2
3
4
5
6
7
// @jridgewell/set-array/dist/set-array.mjs
class SetArray {
constructor() {
this._indexes = { __proto__: null };
this.array = [];
}
}

检查了源码发现并没有什么问题,都是es6的正常语法,至此,似乎没办法往下查了。

重新分析

既然看源码看不出来,那么就从另一个角度来分析一下。

1
2
本地构建正常,服务端构建机报错,项目并没有锁定
这个项目并没有使用 package-lock.json 锁定包版本。

难道是因为 node_modules 的某个依赖包偷偷更新了?
有很大可能是这样。

要验证上面的问题也很简单,直接删掉本地node_modules,重新npm i安装所有依赖。
果然,重新安装复现了问题了,那么基本就锁定了是版本问题了。
同时也发现了@jridgewell/set-array这个包是个新包,@jridgewell/gen-mapping也是最近才引入@jridgewell/set-array包的。

分析了node_modules内部的互相依赖关系,发现 @babel/系列的npm包依赖了@jridgewell/gen-mapping
那么很大可能是最近 @babel/
更新导致的了。

验证猜想

为了验证这个这个猜想,我将package.json 里面所有的@babel/* 包的“^”都去掉(固定版本,不自动安装新包)

1
2
3
"@babel/******": "^7.10.2"
=>
"@babel/******": "7.10.2"

很幸运,固定@babel/** 系列的版本之后能正常编译了,这个猜想正确了。

解决问题

既然知道原因了,那么解决办法也就有了
直接修改babel相关所有依赖包版本、或者加入package-lock.json?
可以解决,但是经过仔细考虑,最终还是放弃了上面的办法。
原因是:似乎上面的代码并没有什么问题,但是确实又在构建引入 SetArray 模块的时候报错了。
那么会不会是构建的问题呢?构建不支持这个 .mjs 后缀的文件?

经过搜索,网上最近也有很多人遇到这个问题,其中一个解决办法让我似乎明白了问题原因了

1
2
3
4
5
6
7
8
9
10
11
12
const config = {
module: {
// ...
rules: [
// ...
{
include: /node_modules/,
test: /\.mjs$/,
type: 'javascript/auto'
}]
}
}

将上面的配置配置之后,果然不再报错了。
搜了下 javascript/auto找到如下结果
https://cdn.epoos.com/img/blog/webpack.png
这个图上说的应该就是原因了,虽然他说的是json文件,但是对于mjs应该也是同样的道理。

至此,问题解决,核心关键就在于配置mjs文件的构建 rule 类型为 javascript/auto

.mjs 是 nodejs 能识别的文件后缀,直接构建到项目中在前端浏览器是不能直接跑的,因此需要打包转义。