Lerna con Yarn, TypeScript y React Native: no se puede encontrar el módulo '@project/common' o sus declaraciones de tipo correspondientes.ts(2307)

Aug 19 2020

El proyecto utiliza Yarn, React Native, Lerna y Typescript. Está estructurado como un monorepo

Aquí está la estructura:

project
|- packages
   | - mobile
       | - src
       | - packages.json
       | - tsconfig.json
   | - cloud-functions
       | - src
       | - packages.json
       | - tsconfig.json
   | - common1
       | - lib
       | - src
       | - packages.json
       | - tsconfig.json
   | - common2
       | - lib
       | - src
       | - packages.json
       | - tsconfig.json
| - packages.json
| - tsconfig.json
| - lerna.json

lerna.json se ve así:

{
  "packages": [
    "packages/*"
  ],
  "npmClient": "yarn",
  "version": "0.0.7",
}

La raíz packages.json se ve así:

{
  "name": "project",
  "private": true,
  "scripts": {
    ...
  },
  "devDependencies": {
    "@types/node": "^14.0.27",
    "lerna": "^3.22.1",
    "ts-node": "^8.10.2",
    "typescript": "^3.9.7"
  }
}

La raíz tsconfig.json se ve así:

{
  "compilerOptions": {
    "noImplicitAny": true,
    "noUnusedLocals": true,
    "removeComments": true,
    "noLib": false,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "sourceMap": true,
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "baseUrl": "./",
    "paths": {
      "@project/common1": ["packages/common1/lib"],
      "@project/common2": ["packages/common2/lib"],
      "@project/mobile": ["packages/mobile/src"],
      "@project/cloud-functions": ["packages/cloud-functions/src"],
    }
  },
  "exclude": ["node_modules", "**/*.spec.ts", "**/__tests__/*", "babel.config.js", "metro.config.js", "jest.config.js"]
}

Los típicos paquetes/común/paquetes.json se ven así:

{
  "name": "@project/common1",
  "version": "0.0.7",
  "main": "lib/index.js",
  "types": "lib/index.d.ts",
  "files": [
    "lib/**/*"
  ],
  "private": true,
  "devDependencies": {
    "@project/common2": "latest", //for common1 only
    "@types/node": "^14.0.27",
    "ts-node": "^8.10.2",
    "typescript": "^3.9.7"
  },
  "dependencies": {
    ...
  }
}

Los típicos paquetes/common/tsconfig.json se ven así:

{
  "extends": "../../tsconfig.json",
  "compilerOptions": {
    "module": "commonjs",
    "outDir": "lib",
    "strict": true,
    "target": "es6"
  },
  "compileOnSave": true,
  "include": ["src"]
}

El archivo de React Native packages/mobile/packages.json se ve así:

{
    "name": "@project/mobile",
    "version": "0.0.7",
    "private": true,
    "dependencies": {
        "@project/common1": "latest",
        "@project/common2": "latest",
        ...
    },
    "devDependencies": {
        ...
        "ts-node": "^8.10.2",
        "typescript": "^3.8.3"
    },
}

Primero me encontré con:

lerna ERR! yarn install --mutex network:42424 --non-interactive stderr:
warning Waiting for the other yarn instance to finish (19560)
warning Waiting for the other yarn instance to finish (21568)
error An unexpected error occurred: "https://registry.yarnpkg.com/@project%2fcommon1: Not found".

Obviamente, Yarn está tratando de extraer las dependencias de su registro de paquetes. esto falla

Luego traté de eliminar las referencias a @project/common1 y @project/common2 en las dependencias de los paquetes.

En la fuente, VS Code subraya las importaciones en rojo e imprime:

Cannot find module '@project/common1' or its corresponding type declarations.ts(2307)

También traté de usar Yarn Workspace, pero me encontré con problemas de levantamiento de módulos con React Native. No quería crear una lista de todos los paquetes posiblemente incompatibles, ya que parece ser difícil de mantener.

"workspaces": {
  "nohoist": ["react-native", "react-native/**", "@react-native-community/checkbox", "@react-navigation/native"]
}

¿Hay una solución sencilla?

¿O es más simple para este caso de uso abandonar Lerna y usar repositorios comunes basados ​​en GitHub?

Respuestas

Xiiryo Aug 20 2020 at 22:32

No sé si este es el enfoque más simple, pero podría hacerlo funcionar agregando espacios de trabajo de Yarn.

En los paquetes principales.json agregué:

  "workspaces": {
    "packages": [
      "packages/*"
    ],
    "nohoist": [
      "**mobile**",
      "**react**",
      "**react-native**",
      ...
    ]
  },

En Lerna.json agregué:

  "useWorkspaces": true,

Desde la raíz tsconfig.json eliminé:

    "baseUrl": "./",
    "paths": {
      "@project/common1": ["packages/common1/lib"],
      "@project/common2": ["packages/common2/lib"],
      "@project/mobile": ["packages/mobile/src"],
      "@project/cloud-functions": ["packages/cloud-functions/src"],
    }