又是一个万恶的周一,回到公司发现上周五改完需求提交的代码在 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
devDependencies
and will remove those insofar they were already installed, if theNODE_ENV
environment variable is set to production. Use this flag to instruct pnpm to ignoreNODE_ENV
and 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 骗了你
没有评论