umi icon indicating copy to clipboard operation
umi copied to clipboard

[Bug] umi4 monorepo 子包找不到依赖

Open twinkle77 opened this issue 1 year ago • 13 comments

What happens?

app 为应用,lib 为库,app 引用了 lib,app 打包的时候,报错,找不到 lib 的依赖 axios,而看 lib 下的 node_modules,axios 是存在的

image

Mini Showcase Repository(REQUIRED)

https://github.com/twinkle77/umi4-monorepo

Please provide a minimal reproduction then upload to your GitHub. 请提供 最小重现,并上传到你的 GitHub 仓库

How To Reproduce

Steps to reproduce the behavior: 1. 2.

Expected behavior 1. 2.

Context

  • Umi Version: 4.0.7
  • Node Version: 14.x
  • Platform: mac

twinkle77 avatar Sep 17 '22 14:09 twinkle77

mfsu 的问题,可以通过 mfsu: false 解决。

这个问题我记得很久以前就有了, @stormslowly 有空可以辛苦看下有没有解法。

fz6m avatar Sep 17 '22 17:09 fz6m

希望能有 mfsu 的能力 哈哈哈

twinkle77 avatar Sep 18 '22 02:09 twinkle77

希望能有 mfsu 的能力 哈哈哈

可以临时通过 externals 来解决 我目前是这样搞的

xierenyuan avatar Sep 19 '22 02:09 xierenyuan

希望能有 mfsu 的能力 哈哈哈

可以临时通过 externals 来解决 我目前是这样搞的

image

大佬,给 web 配置 externals: { axios } 么,问题是,除了 axios 还有非常多的其他依赖

twinkle77 avatar Sep 19 '22 02:09 twinkle77

配置这个吧 https://umijs.org/docs/api/config#alias 这个以可以解决

xierenyuan avatar Sep 19 '22 02:09 xierenyuan

配置这个吧 https://umijs.org/docs/api/config#alias 这个以可以解决

大佬,指的是?

alias: { lib: '../packages/lib/src' }

alias: { axios: '../packages/lib/node_modules' }

@xierenyuan

twinkle77 avatar Sep 19 '22 02:09 twinkle77

是的 axios 写成 require.resolve(’axios‘) 应该就可以 你试试

xierenyuan avatar Sep 19 '22 02:09 xierenyuan

是的 axios 写成 require.resolve(’axios‘) 应该就可以 你试试

image

老哥,为什么 app 的 .umirc.ts 文件 require.resolve('axios'),这个 axios 缺会指向 lib/node_modules 下的 axios ?

如果想批量注册的话,需要在 .umirc.ts 实现一个逻辑,收集所有 lib 类的依赖,生成一个 alias 格式的对象 ?

@xierenyuan 把你非子包的依赖的映射下 就没问题呢

twinkle77 avatar Sep 19 '22 03:09 twinkle77

是的 axios 写成 require.resolve(’axios‘) 应该就可以 你试试

确认不报错了

twinkle77 avatar Sep 19 '22 03:09 twinkle77

试了下,应该是 mfsu 的问题,lib 中的依赖 axios 在设置后 umi config monorepoRedirect 会提升到 app 库的 buildDep 中(个人猜测)

可以通过在 app 中安装 dep: axios,lib中改成 devDep: axios, peerDep: axios

zjfresh avatar Sep 20 '22 09:09 zjfresh

试了下,应该是 mfsu 的问题,lib 中的依赖 axios 在设置后 umi config monorepoRedirect 会提升到 app 库的 buildDep 中(个人猜测)

可以通过在 app 中安装 dep: axios,lib中改成 devDep: axios, peerDep: axios

安装到 app 不行的, lib 很多,app会非常臃肿

twinkle77 avatar Sep 20 '22 10:09 twinkle77

试了下,应该是 mfsu 的问题,lib 中的依赖 axios 在设置后 umi config monorepoRedirect 会提升到 app 库的 buildDep 中(个人猜测) 可以通过在 app 中安装 dep: axios,lib中改成 devDep: axios, peerDep: axios

安装到 app 不行的, lib 很多,app会非常臃肿

那可以考虑放弃 monorepoRedirect ,通过 [email protected] config mfsu:exclude ['@mono/lib'] 来实现,这种需要 father 构建 Bundless 包, src 目录不能经过 umi 的源码编译(否则会进入 mfsu 的逻辑,然后 lib 的相关 dep 如 axios 不在 app 的依赖会报错)

lib/package.json

{
  "name": "@mono/lib",
  "version": "1.0.0",
  "main": "dist/index.js",
  "module": "dist/index.js",
  "types": "dist/index.d.js",
  "files": [
    "dist",
    "compiled"
  ],
  "scripts": {
    "dev": "father dev",
    "build": "father build",
    "postinstall": "pnpm run build",
    "build:deps": "father prebundle",
    "prepublishOnly": "father doctor && npm run build"
  },
  "dependencies": {
    "axios": "^0.27.2"
  },
  "devDependencies": {
    "father": "^4.0.0",
    "webpack": ">=5.11.0 <6.0.0"
  }
}

lib/.fatherrc.ts

import { defineConfig } from 'father';

export default defineConfig({
  esm: {
    output: 'dist'
  },
});

lib/tsconfig.json

{
  "compilerOptions": {
    "strict": true,
    "declaration": true,
    "skipLibCheck": true,
    "baseUrl": ".",
    "jsx": "react-jsx"
  }
}

zjfresh avatar Sep 20 '22 11:09 zjfresh

试了下,应该是 mfsu 的问题,lib 中的依赖 axios 在设置后 umi config monorepoRedirect 会提升到 app 库的 buildDep 中(个人猜测) 可以通过在 app 中安装 dep: axios,lib中改成 devDep: axios, peerDep: axios

安装到 app 不行的, lib 很多,app会非常臃肿

那可以考虑放弃 monorepoRedirect ,通过 [email protected] config mfsu:exclude ['@mono/lib'] 来实现,这种需要 father 构建 Bundless 包, src 目录不能经过 umi 的源码编译(否则会进入 mfsu 的逻辑,然后 lib 的相关 dep 如 axios 不在 app 的依赖会报错)

lib/package.json

{
  "name": "@mono/lib",
  "version": "1.0.0",
  "main": "dist/index.js",
  "module": "dist/index.js",
  "types": "dist/index.d.js",
  "files": [
    "dist",
    "compiled"
  ],
  "scripts": {
    "dev": "father dev",
    "build": "father build",
    "postinstall": "pnpm run build",
    "build:deps": "father prebundle",
    "prepublishOnly": "father doctor && npm run build"
  },
  "dependencies": {
    "axios": "^0.27.2"
  },
  "devDependencies": {
    "father": "^4.0.0",
    "webpack": ">=5.11.0 <6.0.0"
  }
}

lib/.fatherrc.ts

import { defineConfig } from 'father';

export default defineConfig({
  esm: {
    output: 'dist'
  },
});

lib/tsconfig.json

{
  "compilerOptions": {
    "strict": true,
    "declaration": true,
    "skipLibCheck": true,
    "baseUrl": ".",
    "jsx": "react-jsx"
  }
}

我试试 感谢

twinkle77 avatar Sep 20 '22 12:09 twinkle77

现状

现在以最新版本 umi ( ^4.0.30 ) 验证,已不存在如题所述的问题。

发生的新问题

@stormslowly 有时间可以辛苦看下这个问题 🌹

虽然不会发生如题所述的问题,但是在 改动引用子包内容 的时候,mfsu 会报依赖错误:

error - MFSU[eager] analyze dependencies failed with error Error: ENOENT: no such file or directory, open '.../umi4-monorepo/packages/app/node_modules/.cache/lib/src/index.js'

将子包添加到 mfsu.exclude 也无法解决:

// .umirc.ts

export default {
  // 当改动子包内容时,仍然会出现依赖错误
  mfsu: {
    exclude: ['@mono/lib']
  }
}

复现方法

  1. 拉下本 issue 中的仓库 https://github.com/twinkle77/umi4-monorepo

  2. 将应用 packages/app/package.json 的 umi 版本改为最新:

    // packages/app/package.json
    +  "umi": "^4.0.30",
    
  3. 启动项目

      pnpm i
      cd ./packages/app && pnpm dev
    
  4. 改动子包 packages/lib/src/index.ts 代码,即可复现。

fz6m avatar Nov 12 '22 01:11 fz6m

目前还是建议 使用 normal strategy 配合 monorepo 使用

stormslowly avatar Nov 12 '22 15:11 stormslowly

normal 策略的问题在于是会报 axios 找不到(如本 issue 中的问题)😭 :

error - Can not resolve dependence : 'axios', please install it
error - AssertionError [ERR_ASSERTION]: dependence not found: axios

fz6m avatar Nov 12 '22 17:11 fz6m

@twinkle77 https://github.com/umijs/umi/releases/tag/v4.0.33 应该解决此问题了

stormslowly avatar Nov 30 '22 09:11 stormslowly

@fz6m "@umijs/max": "4.0.64", dev没问题,但build还是提示找不到packages/components里面的依赖classnames antd,mfsu配置是 strategy: 'normal', 请指点~

image

复现仓库

  • pnpm i
  • pnpm build:umi4app

buquan avatar Apr 09 '23 02:04 buquan

packages/components/src/InnerButton/index.jsx 这个文件里用到了 antdclassnames 这俩依赖,都没有在 package.json 里声明,这是不规范的。

要把子包当成真正的组件库开发,依赖不能是隐形的,用到的依赖必须要满足如下其一的条件:

  1. dependencies 里,如果该包被发到 npm 上,安装该包的人也会安装 dependencies 里面的依赖,从而确保这个依赖一定存在。

  2. devDependenciespeerDependencies 里,在 devDependencies 是为了开发时的类型和调试开发,但我们不希望他多个实例,而是复用主项目的,那就必须也声明到 peerDependencies 里,说明这个依赖需要引用这个包的项目安装哪些依赖。

按这个场景,我们可以自己决策,antd 很大,不应该多实例,应该是 2 的声明法,classnames 很小,多实例也不会产生预期外的问题(一般工具库多实例是无所谓的,但组件库多实例有风险,比如 context 不一致,消息不成队列等),所以 1 / 2 都可以。

{
  "name": "@ifss/components",
  "version": "0.0.0",
  "private": true,
  "main": "src/index.ts",
  "typings": "index.d.ts",
  "scripts": {
    "format": "prettier --write \"src/**/*.{js,jsx,tsx,ts,less,json}\"",
    "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'",
    "lint:fix": "eslint --fix 'src/**/*.{js,jsx,ts,tsx}'"
  },
+ "dependencies": {
+   "classnames": "^2.3.2"
+ },
  "devDependencies": {
    "eslint": "^8.35.0",
    "@ifss/eslint-config-custom": "workspace:*",
    "@ifss/tsconfig": "workspace:*",
+   "antd": "4.18.9"
  },
+ "peerDependencies": {
+   "antd": "^4"
+ }
}

现在我们已经按照 npm 包开发规范声明完了依赖,下一步需要解决 monorepo peer deps 问题,详见 monorepoRedirect ,这是 monorepo 内部引用与 npm 安装下来不一样的地方,通过添加 monorepoRedirect.peerDeps: true 解决,就可以构建成功了。

注:上面说到的 classnames 之所以选择了 1 ,工具库一般会这么依赖,多实例没有风险,这样即使该包被发布到 npm ,安装该包的项目也无需每个都安装一遍 classnames ,特别是这种工具库很多的情况,假定有 10 个,安装这个包的项目都要安装这 10 个 peer deps ,是不合理的。

fz6m avatar Apr 09 '23 09:04 fz6m

@fz6m 感谢指点,之前看过你的这篇文章,但隐形依赖在dev可以跑还是挺困惑的,因为dev可以跑就误以为umi做了魔法处理,可以不用声明依赖的

buquan avatar Apr 09 '23 09:04 buquan

这个是 MFSU 的问题,mfsu: false 行为是正常的。

fz6m avatar Apr 09 '23 10:04 fz6m