如何在npm workspaces+TypeScript项目引用的单体仓库中禁止非法依赖?
问题背景
我希望在基于TypeScript和npm的单体仓库中,于编译层面建立模块边界,防止任意越界导入,但尚未找到可行方案。我使用npm workspaces为子项目定义独立依赖,借助TypeScript项目引用隔离各项目的编译配置,项目结构示例如下:
/backend - /src - tsconfig.json - package.json /frontend - /src - tsconfig.json - package.json package.json tsconfig.json /node_modules
当前场景下,前端拥有React这类专属依赖,后端则依赖Express.js等,但npm workspaces会将所有依赖存入同一node_modules供所有项目访问,且无法阻止前后端互相导入对方代码。请问如何在TypeScript编译阶段就阻止这类非法导入?
解决方案
1. 子项目tsconfig配置排除其他模块源码
在每个子项目的tsconfig.json中,通过exclude字段明确排除其他子项目的源码目录,让TypeScript编译时直接识别跨模块导入为非法:
比如前端frontend/tsconfig.json添加:
{ "compilerOptions": { // 原有编译配置 }, "exclude": [ "../backend/src", "../node_modules" ] }
后端backend/tsconfig.json对应添加:
{ "compilerOptions": { // 原有编译配置 }, "exclude": [ "../frontend/src", "../node_modules" ] }
同时确保根目录的tsconfig.json仅通过项目引用(references)管理子项目,而非直接包含所有源码:
{ "files": [], "references": [ { "path": "./frontend" }, { "path": "./backend" } ] }
这样编译时TypeScript会严格遵循每个子项目的排除规则,跨模块导入会直接抛出“找不到模块”的编译错误。
2. 使用paths配置拦截非法导入路径
如果子项目使用了路径别名(比如@backend/*),可以在子项目的tsconfig.json中通过paths配置将对方的别名指向不存在的文件,强制触发编译错误:
比如前端frontend/tsconfig.json:
{ "compilerOptions": { // 原有配置 "paths": { "@backend/*": ["../__invalid__/"] } } }
当前端代码尝试导入@backend/utils时,TypeScript会因找不到../__invalid__/utils而报错,直接阻止非法导入。
3. 配合npm workspaces的nohoist隔离专属依赖
针对npm workspaces将所有依赖放入统一node_modules导致的跨模块依赖访问问题,可以在根package.json中使用nohoist配置,将前端/后端的专属依赖保留在子项目本地,避免跨模块访问:
{ "workspaces": { "packages": ["frontend", "backend"], "nohoist": [ "frontend/**/react", "frontend/**/react-dom", "backend/**/express" ] } }
这样前端的React依赖会留在frontend/node_modules,后端无法导入;同理Express会留在backend/node_modules,前端无法访问,从依赖层面进一步加固边界。
内容的提问来源于stack exchange,提问作者tscz

