macos下webpack打包node项目遇到的关于二进制模块fsevents的坑

项目背景

leader给了一个需求,将一个node项目适配到云函数上,其中最大的问题就是依赖问题,由于用到一些私有的依赖,不能使用官方提供的在线安装依赖功能,只能自己把node_module打包,上传。由于node_modules很容易就几百m大小,这么大小的文件传递到云函数上非常的不方便,于是准备通过webpack将项目打包成单文件js,然后上传到云函数上执行

问题

开始在linux下编写代码,进行webpack打包一切正常,由于一些原因转移到imac上开发,我满怀欣喜的写完代码,准备打包上传,然后结束战斗,结果打包时是总是报错。

ERROR in ./node_modules/fsevents/fsevents.node 1:0
Module parse failed: Unexpected character '�' (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders

报错信息其实很明了,webpack不认识这个后缀的包,很正常,.node后缀的文件,是使用其他语言编写的二进制模块,由于webpack本身只针对js文件打包,并且浏览器是不能运行.node后缀的二进制模块,所以webpack默认是不认识这个后缀的。

在解决问题的过程中主要有两个难题:

  • 两个系统间的打包配置完全没变化,为什么会报这种错误
  • 对fsevents完全不了解是干什么的,一头雾水

解决流程

开始以为和nodejs的fs模块有关,怀疑是webpack无法打包node原生模块的问题,使用了webpack-node-externals,这里我错误的以为webpack-node-externals只会把node原生模块给过滤掉,给后面自己又挖了个坑。

在设置了wbepack的externals配置后,项目打包成功。我继续满心欢喜上传到云函数上,但是却总是运行失败。不由得继续排查问题,在摸瞎排查的过程中,尝试把node_modules同样和项目代码传了上去,淦,竟然运行成功。

好了,又和依赖有关系,由于云函数自带node环境,肯定是有些第三方的模块没有打包进去,我想了下,唯一值得怀疑的就是webpack-node-externals,查了下npm上的说明,发现这个包的作用是忽略所有node_modules下的打包请求,不管第几方模块,都给爬,我说怎么打包成功了,node_modules下全忽略了,当然就成功了。

到这里,由于项目已经成功跑到云函数上,只是要上传node_modules,于是和leader沟通了下,还是觉得上传node_modules太麻烦了,于是继续解决原生模块打包问题,此时我还在错以为fsevents是内部模块,整个问题和内部模块打包有关系。

终于,我决定去 npm 看下 fsevents 到底是个什么东西。

Native access to MacOS FSEvents in Node.js

This is a low-level library. For a cross-platform file watching module that uses fsevents, check out Chokidar.

敏锐的我注意到了macos,再联想到linux下打包没问题,让我抓住了问题的关键,是操作系统的问题!后来经过了解,fsevents是针对macos的文件系统的api做了一层封装,是一个比较底层的模块,提供给上层模块调用,比如Chokidar模块,回去查了下package.json,看到果然有Chokidar模块。

确实没想到,webpack会根据不同操作系统去针对性打包,由于在macos下打包,Chokidar用到了fsevents,fsevents又是个二进制模块,webpack默认又不识别二进制模块,于是导致打包总是失败。

由于nodejs是个跨平台项目,潜意识中根本不会觉得问题会出在平台差异性上,排查问题时在错误的方向浪费了很多时间。

结果

最终经过查询资料和验证,发现fsevents只是单独针对macos平台,如果在linux上运行,打包时没有fsevents依然可以运行,于是在webpack externals配置了下,把fsevents模块排除掉,打包成功,上传到云函数,运行成功。

后记

虽然解决了问题,项目成功在云函数上运行,但是还是没有解决遇到二进制打包的问题,下次遇到依赖linux系统的二进制模块时就没法取巧直接绕过去了,经过搜索发现,找到了专门针对.node后缀二进制模块的 node-loader,node-loader 只在 target为node的情况下生效,很合理,通过引入 node-loader 最终完美解决了二进制模块打包的问题。

你可能感兴趣的:(node)