Browserify打包Node.js项目报async_hooks缺失ENOENT错误求助
Browserify打包Node.js服务端代码报async_hooks不存在错误排查
报错信息
Error: Can't walk dependency graph: ENOENT: no such file or directory, lstat 'C:\Users\calon\source\repos\CAL0NZ0\Treasure-Box-Azure\async_hooks'
依赖来源:C:\Users\calon\source\repos\CAL0NZ0\Treasure-Box-Azure\node_modules\on-finished\index.js
触发场景
待处理代码为基于Express + Azure Cosmos DB实现的服务端逻辑,执行以下Browserify命令尝试将代码打包为浏览器可运行文件时触发上述报错,其余纯前端文件打包无异常:
browserify ./application/staging/index-s.js > ./application/deploy/index.js -t babelify --plugin tinyify
待打包代码内容如下:
const express = require('express'); const path = require('path'); const bodyParser = require('body-parser'); const { CosmosClient } = require('@azure/cosmos'); const config = require('./config'); const endpoint = config.endpoint; const key = config.key; const client = new CosmosClient({ endpoint, key }); const database = client.database('treasure_box'); const container = database.container('logins'); const app = express(); let initialPath = path.join(__dirname, 'treasure-box-azure'); app.use(bodyParser.json()); app.use(express.static(initialPath)); app.get('/', (req, res) => { res.sendFile(path.join(initialPath, 'index.html')); }); app.get('/login', (req, res) => { res.sendFile(path.join(initialPath, 'login.html')); }); app.get('/register', (req, res) => { res.sendFile(path.join(initialPath, 'register.html')); }); app.post('/register-user', (req, res) => { const { name, email, password } = req.body; if (!name.length || !email.length || !password.length) { res.json('Fill out all field'); } else { container .insert({ name: name, email: email, password: password, }) .returning(['name', 'email']) .then((data) => { res.json(data[0]); }) .catch((err) => { if (err.detail.includes('Already Exists')) { res.json('Email already exists'); } }); } }); app.post('/login-user', (req, res) => { const { email, password } = req.body; container .select('name', 'email') .where({ email: email, password: password, }) .then((data) => { if (data.length) { res.json(data[0]); } else { res.json('Email or password is incorrect'); } }); }); app.listen(403, (req, res) => { console.log('listening on 403'); });
错误根因
- 打包目标选择错误:当前代码是纯Node.js服务端后端代码,依赖的
express框架、on-finished(express的传递依赖)、Node.js原生模块、@azure/cosmos服务端SDK全部为服务端运行环境设计。报错中提到的async_hooks是Node.js独有的内置原生模块,浏览器环境不存在对应实现,Browserify遍历依赖树时找不到对应文件就会抛出ENOENT错误。 - 工具使用逻辑错误:Browserify的定位是打包CommonJS规范的前端代码,抹平浏览器端的模块加载差异,不会自动适配Node.js原生API和服务端专属依赖,直接将后端服务代码打包为浏览器可执行文件本身就不符合工具的设计使用场景。
- 代码本身存在API调用错误:代码中
container.insert().returning()、container.select().where()的写法不符合Azure Cosmos DB JS SDK的官方API规范,即便跳过打包步骤,代码在Node.js环境直接运行也会触发方法不存在的报错。
可行修复方案
- 停止用Browserify打包这段后端服务代码:后端代码不需要打包交付给浏览器,直接在Node.js环境执行
node ./application/staging/index-s.js即可启动服务,在403端口对外提供静态页面和接口服务。 - 前后端逻辑拆分:前端页面的交互逻辑单独编写JS代码,通过
fetch或其他请求工具调用Express服务暴露的/register-user、/login-user接口即可,这部分前端侧的JS代码可以正常使用Browserify打包。 - 修正Cosmos DB SDK的错误调用写法:使用官方标准API替换现有自定义写法,插入数据使用
container.items.create()方法,查询数据使用container.items.query()方法。 - 若确有浏览器端直连Cosmos DB的需求,不要引入服务端版
@azure/cosmos包,改用适配浏览器环境的Cosmos DB前端SDK,同时必须配置严格的数据库细粒度权限策略,禁止将数据库主密钥直接写入前端代码,否则会导致数据库完全泄露,所有数据可被任意篡改删除。
内容的提问来源于stack exchange,提问作者CAL0NZ0
相关产品推荐
相关产品推荐

