Pesquisa de site

reactjs - Monorepo pnpm com Typescript, conflitos de aliases de caminho entre dependências locais durante a compilação


Eu tenho um PNPM com monorepo TurbeRepo, usei este modelo. Neste monorepo, eu tenho um aplicativo NextJS que utiliza um pacote ui-shared (código compartilhado local, não é realmente um pacote npm).

O ui-shared é feito no estilo de, mas meu pacote é, claro, muito maior. Com um index.ts que exporta todos os componentes e utilitários compartilhados.

ATUALIZAÇÃO: Adicionado um repositório mínimo que reproduz o problema

Ao construir o ui-shared, ele é construído com sucesso, mas quando construo o app Nextjs, a construção do ui-shared falha, pois não consegue resolver o caminho a partir do tsconfig. Então, após alguns dias de investigação, consegui identificar o que acredito ser o problema. Quando o app Next é construído, ele também constrói o ui-shared e isso falha porque o caminho agora é baseado no Nextjs e não na própria biblioteca. Não tenho certeza se esta é a causa, mas acredito que seja, e não sei como posso corrigir isso.

(Pacote compartilhado ou pacote é como vou me referir a ui-shared)

Construir o pacote compartilhado resulta em sucesso

ESM ⚡️ Build success in 112ms
ESM dist/index.mjs 220.69 KB
DTS Build start
DTS ⚡️ Build success in 4792ms

Partes do package.json

  "main": "./src/index.ts",
  "module": "./dist/index.mjs",
  "types": "./src/index.ts",
  "scripts": {
    "lint": "eslint *.ts*",
    "build": "tsup ./src/index.ts --format esm,cjs --dts --external react",
    "dev": "tsup ./src/index.ts --format esm,cjs --watch --dts --external react"
  },

O tsconfig do pacote

{
  "extends": "tsconfig/react-library.json",
  "compilerOptions": {
    "baseUrl": ".",
    "resolveJsonModule": true,
    "allowJs": true,
    "paths": {
      "@/*": ["src/*"]
    }
  },
  "include": ["**/*.ts", "**/*.tsx"],
  "exclude": ["dist", "build", "node_modules"]
}

Quando eu executo a construção do aplicativo NextJs, recebo este erro

pnpm build

> @company/[email  build /work/company/monorepo/apps/business
> next build

See more info here: https://nextjs.org/docs/messages/invalid-next-config
info  - Skipping linting
info  - Checking validity of types .Failed to compile.

../../packages/ui-shared/src/components/AnimatedLoadingMedia.tsx:9:23
Type error: Cannot find module '@/utils/clsxm' or its corresponding type declarations.

   7 | } from "react";
   8 | 
>  9 | import { clsxm } from "@/utils/clsxm";
     |                       ^
  10 | 
  11 | type AnimatedLoadingMediaProps = {
  12 |   isVideo: boolean;

> Build error occurred
Error: Call retries were exceeded
    at ChildProcessWorker.initialize (/work/company/monorepo/node_modules/.pnpm/[email _twoewwu6sg7cdf3ao6njtbftne/node_modules/next/dist/compiled/jest-worker/index.js:1:11661)
    at ChildProcessWorker._onExit (/work/company/monorepo/node_modules/.pnpm/[email _twoewwu6sg7cdf3ao6njtbftne/node_modules/next/dist/compiled/jest-worker/index.js:1:12599)
    at ChildProcess.emit (node:events:390:28)
    at Process.ChildProcess._handle.onexit (node:internal/child_process:290:12) {
  type: 'WorkerError'
}

Aplicativo Nextjs tsconfig

{
  "extends": "tsconfig/nextjs.json",
  "compilerOptions": {
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "baseUrl": ".",
    "module": "esnext",
    "paths": {
      "@/*": ["src/*"],
      "@": ["src"],
      "@public/*": ["public/*"],
      "@public": ["public"]
    },
  },
  "include": [
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}

De alguma forma, a biblioteca precisa resolver seus próprios caminhos quando está sendo construída a partir do Next.js. Ou melhor ainda, a biblioteca deveria ser construída em seu próprio escopo e o Next.js só deveria consumir os resultados. Eu não faço ideia se isso pode funcionar assim

Eu tentei adicionar bibliotecas de terceiros, como tsc-alias, tsconfig-path e assim por diante. Mas a compilação continua falhando. Eu tentei ver e de alguma forma impedir o Next.js de construir o pacote, mas não consegui encontrar um jeito.

Resposta #1

Depois de muitas horas batendo minha cabeça contra isso, acabei usando apenas o próprio nome do pacote como um alias.

package.json:

{
  "name": "@pkg/utils",
  ...
}

tsconfig:

"baseUrl": ".",
"paths": {
  "@pkg/utils/*": ["./src/*"]
}
// /packages/utils/src/bar.ts
import someSiblingUtil from '@pkg/utils/foo.util';

Não tenho 100% de certeza, mas a razão pela qual acho que isso está funcionando é que, quando o próprio pacote está importando o arquivo, ele usa o alias ./src, mas quando é importado em um aplicativo que depende dele, o aplicativo vê a referência @pkg/utils e simplesmente a lê da mesma forma que importa a dependência do pacote para si mesmo.

Artigos relacionados