这篇文章上次修改于 736 天前,可能其部分内容已经发生变化,如有疑问可询问作者。
又是一个万恶的周一,回到公司发现上周五改完需求提交的代码在 CI 的时候发生了故障(还好只是测试环境)。简单概括,就是不知道为什么 ua-parser-js 这个依赖的 TypeScript 类型读取不到,需要安装一个叫 @types/ua-parser-js 的库。
Type error: Could not find a declaration file for module 'ua-parser-js'. '/app/node_modules/.pnpm/[email protected]/node_modules/ua-parser-js/src/ua-parser.js' implicitly has an 'any' type.
Try `npm i --save-dev @types/ua-parser-js` if it exists or add a new declaration (.d.ts) file containing `declare module 'ua-parser-js';`
1 | import axios from "axios";
> 2 | import uap from "ua-parser-js";
| ^而实际上这个库我是已经安装了的,只是它写在了 package.json 文件的 devDependencies 里面。这个项目的 package.json 有个很诡异的点,就是几乎所有的包都写在了 dependencies 里面,包括其他 @types/xxxx 的包,我就觉得很奇怪,为什么读取不到 devDependencies 里面的包呢?
我从 CI 的流程一步一步看,最终定位到了 DockerFile 文件,光看代码的执行流程并没有发现什么奇怪的问题,总体来说就是安装依赖,启动进程,然后导出 3000 端口映射。我尝试用自己本地的 Docker 环境跑了一遍。
FROM node:18.15.0 AS runner
WORKDIR /app
ENV NODE_ENV production
RUN addgroup --system --gid 941 nodejs
RUN adduser --system --uid 941 nextjs
COPY . ./
WORKDIR ./
RUN chmod 777 .
RUN npx --yes pnpm install
RUN npx pnpm build
USER nextjs
EXPOSE 3000
ENV PORT 3000
CMD ["npm", "start"]docker build -t usercenter .结果和前文一样,也是一模一样的报错。我尝试排查问题,并把这个配置文件发到了群里,@提莫 认为可能是环境变量 ENV NODE_ENV production 这行设置导致的。这小小的变量设置影响会有这么大么,我去掉它重新打包,发现的确正常了。
devDeps 是干什么的
先不说环境变量的事,为什么要有 devDependencies 而不是直接 dependencies 一把梭呢,我认为这个包如果要提供给其他包使用,总会有一个打包好的版本可以直接使用(不需要在父项目里面再执行构建一遍代码),那么负责构建过程的包是不是就没有必要被再次安装了?
如果你要构建这个项目本身,则还是必须安装 devDependencies 下的依赖才行,可以看看用 Vite 一类的脚手架,他们的 package.json 是不是就是这么写的?
为什么没有安装 devDeps
既然 devDependencies 的使用方式是正确的,那么错就错在其他地方了。仔细看过程,环境变量的设置放在了最前面,后面才安装依赖文件,这就可能导致没能安装上 devDependencies 下的所有依赖。
这个猜想是正确的,我查阅了 PNPM 的文档,的确是这样子。因此这个 DockerFile 的过程是存在问题的,不应该在安装依赖之前强制设置成 production 模式。
pnpm will not install any package listed in
devDependenciesand will remove those insofar they were already installed, if theNODE_ENVenvironment variable is set to production. Use this flag to instruct pnpm to ignoreNODE_ENVand take its production status from this flag instead.如果
NODE_ENV环境变量被设置为production, pnpm 将不会安装devDependencies中列出的任何包,并且将删除那些已经安装的包。使用这个标志可以指示 pnpm 忽略NODE_ENV,并从这个标志中获取它的生产状态。
同时感谢群友 @咲奈 的回答:
- 好像确实是这样,如果你在
pnpm i安装的时候就给了NODE_ENV为生产,就不装devDeps - Docker 文件里不需要声明
NODE_ENV,因为你也没用到这个变量,还影响了pnpm i安装的依赖 - Scripts 里不需要使用
cross-env设定NODE_ENV,在常规情况下,它的值一般只有 development / test / production,并且由构建工具自动帮你设定,你不需要设定。 - 如果在特殊情况下,需要在过程中
production的NODE_ENV下执行pnpm i,workaround 时先把NODE_ENV改成development装完依赖再改回来。 - 不应该把
@types/*下面的依赖放到deps里,虽然大部分情况下,依赖在哪都是无所谓的,这只是个通俗约定,但你问的 GPT 骗了你
没有评论