nx
nx copied to clipboard
Conflicts with package-based monorepo and tsconfig paths aliases (Parsing issue with `NxViteTsPaths`) + solutions and fixes
Current Behavior
I have an Nx monorepo with package-based
approach in which I generated an app and some libraries using the Nx Plugin Generators (e.g. with nx g @nx/react:lib
).
Now since the adoption of nxViteTsPaths
(that replaced vite-tsconfig-paths
) I started to get the same error again and again whenever I use Nx generators to create an app and a library, and then try to import the library inside the app:
Failed to resolve entry for package "@myorg/mylib". The package may have incorrect main/module/exports specified in its package.json.
I already opened an issue in the past about this (https://github.com/nrwl/nx/issues/18492 ), now I'm reopening it with more context and new solutions. Unfortunately, the issue was closed before I could try the suggested solution and answer (my fault here). Also since the suggested solutions where not ideal to me (they nullified the reasons why I had chosen a package-based
monorepo) I started to did some digging and investigation:
TL;DR: scroll until "the less invasive solution" section.
Trying
At first I thought that the problem was caused by the properties in the package.json
so I generate 3 types of libraries with the Nx plugin generators (@nx/react:lib
):
I basically created:
- a publishable library (that is buildable and uses vite as bundler)
- a non publishable library that is buildable and uses vite as bundler
- a non publishable library without build process (no
package.json
, noviteconfig
file)
This were the results: (you can inspect this on the PoC I linked in this issue - you can follow the git history of the project where I explicitly wrote the shell commands used to generate each library step by step inside the commit messages).
nx g lib | Buildable | Non Buildable |
---|---|---|
Publishable | π΄ the package.json contains conflicts (main: dist/index.js, wrong exports, etc...) |
--- not possible, if publishable has to be buildable --- |
Non publishable | π΄ the package.json is present since it's buildable, same conflicts as above |
β
This is fine, the library does not have a package.json so nxViteTsPaths follows the path at tsconfig.base.json |
Solutions
I also try to found some alternative solutions, although these worked fine, they were not ideal for me, I had to choose between one of these:
- Solution A: Remove theΒ
package.json
Β totally from my custom internal library and use the Tspaths
to import the library. - Solution B: Change the
package.json
of the library by setting the"main"
prop to the actualΒ./src/index.ts
Β file. - Solution C: Tell Nx to build my custom library and set that
build
process astargetDefaults
onnx.json
to build the package every time (even when you use "serve").
The first one was not fine, if I choose to publish my library in the future I need a package.json
in place. The second one seems weird to me, I read online that it could also generate problems in the future (again, what happens If you want to publish it?). The third one makes sense, but it ruined my dev experience because it disables the hot reloading whenever I changed the library when working on my main package.
the less invasive solution:
After some try and error and studying what was going on with vite I realized something.
The real problem was not caused by the package.json
, not directly at least.
It was caused by the exact correlation between the alias in paths
(inside tsconfig.base.json
) and the package name inside the package.json
of the internal library.
So the fix is easy at this point, I changed the alias inside tsconfig.base.json
preventing it from being identical to the name of the internal library (the "name" in its own package.json).
For example, this was the situation before:
monorepo
βββ packages
βββ frontend
βββ my-lib
βββ package.json ("name": "@myorg/mylib")
tsconfig.base.json ( "paths": {"@myorg/mylib":"etc..."} )
In this case I just changed the tsconfig.base.json
altering the name of the paths alias to not match the package name any more (e.g. ``), by doing so I adopt the Solution A explained above but also keep my package.json
inside the custom library.
This is the final result:
monorepo
βββ packages
βββ frontend
βββ app.tsx ( import {TheComponent} from '@myorg/mylib-internal' )
βββ my-lib
βββ package.json ("name": "@myorg/mylib")
tsconfig.base.json ( "paths": {"@myorg/mylib-internal":"etc..."} )
You can try this scenario on the github repository I linked below in this issue report. Check also its git commits where I explain step by step what I've done in the commit messages.
Expected Behavior
There should be no issues when importing a library using the tsconfig paths aliases. (I think the new nxViteTsPath
should parse this Ts paths as before (the way vite-tsconfig-paths
used to do).
GitHub Repo
https://github.com/fsgreco/poc-nx-package-based-monorepo
Steps to Reproduce
You can clone the repository of my PoC linked above, launch the nx server frontend
app and try to import some components from the libraries already in place.
Otherwise you could try on the fly for yourself:
-
npx create-nx-workspace@latest package-based --preset=npm
-
cd package-based
-
mkdir packages
-
npm install @nx/react -W
-
npx nx g @nx/react:app frontend --routing --style styled-components --bundler vite --e2eTestRunner none
-
npx nx g @nx/react:lib lib-public-buildable --publishable=true --bundler=vite --importPath=@myorg/lib-public-buildable
- launch
npx nx s frontend
- try to import the component from
@myorg/lib-public-buildable
inside the frontendpackages/frontend/src/app/app.tsx
Nx Report
> NX Report complete - copy this into the issue template
Node : 18.18.2
OS : linux-x64
npm : 10.2.5
nx : 17.2.8
@nx/js : 17.2.8
@nx/linter : 17.2.8
@nx/eslint : 17.2.8
@nx/workspace : 17.2.8
@nx/devkit : 17.2.8
@nx/eslint-plugin : 17.2.8
@nx/react : 17.2.8
@nrwl/tao : 17.2.8
@nx/vite : 17.2.8
@nx/web : 17.2.8
typescript : 5.2.2
Failure Logs
[vite] Internal server error: Failed to resolve entry for package "lib-non-public-buildable". The package may have incorrect main/module/exports specified in its package.json.
Package Manager Version
10.2.5
Operating System
- [X] macOS
- [X] Linux
- [ ] Windows
- [ ] Other (Please specify)
Additional Information
Hope this could help others that had this error previously.
I am experiencing similar issues where all my libraries references in tsconfig.base.json are prefixed with a "@". The nxViteTsPaths plugin fails for all absolute imports "Error: Failed to resolve import "@shared/" from "libs\shared_". Does the file exist? ". Has anyone found a workaround?
@joewIST can you share the snippet of your tsconfig.base.json
file?
Sometimes I had that problem when I changed the paths
on the fly (meaning while Nx was serving the development environment), in that case I solved by exit (ctrl+c
) and launch the serve comand again (I don't remember if I also do another npm install
but I think that's not necessary).
I faced a similar problem and got it resolved by adding the following configuration to the .npmrc
file, at the root of my project:
link-workspace-packages=false
By doing so, packages are downloaded and installed from the registry. It seems to be a bug related to Vite and how PNPM workspaces link their inner packages in a monorepo. You'll also need to update your inner dependencies on package.json
to the latest version of each package - if you are using "workspace:*"
as dependency reference.
@fsgreco Wow this saved me. Thank you for putting the effort into this. The step-by-step reproduction you provided in the repo was super helpful.
It's a little weird to me that we would need to rename the library aliases so that they don't match the path but so long as it works I guess.
@fsgreco Wow this saved me. Thank you for putting the effort into this. The step-by-step reproduction you provided in the repo was super helpful.
It's a little weird to me that we would need to rename the library aliases so that they don't match the path but so long as it works I guess.
Glad it helped you :) indeed I take me a lot of try and error to understand what was happening. However I consider this just a workaround. Unfortunately the bug persists, and it has to do with how nxViteTsPaths
works under the hood. It doesn't happened with vite-tsconfig-paths
.
This also happens with build / swc; making sure the package.json name and config alias are different solves it.
for reference, it does not solve issues with temporal.io webpack bundle.
I also have this issue. It's extremely frustrating. Unfortunately, I can't use any of the work arounds here because I need to publish both the Core Library and the Library that consumes the Core Library for public consumption. Changing the alias would break my naming conventions for my module imports.
@Coly010 Are there any updates on when this issue may be addressed and the nxViteTsPaths
can be fixed?
I'm also having this issue, with a package based monorepo using vite and vitest.
I continually get Error: Failed to resolve entry for package "@ai-dev/comm". The package may have incorrect main/module/exports specified in its package.json.
When running vitest via nx test
I also get this warning:
> vitest
The CJS build of Vite's Node API is deprecated. See https://vitejs.dev/guide/troubleshooting.html#vite-cjs-node-api-deprecated for more details.
Vitest "cache.dir" is deprecated, use Vite's "cacheDir" instead if you want to change the cache director. Note caches will be written to "cacheDir/vitest"
I had an issue where things were working fine, then I copied in a package from another monorepo using the same exact configuration and I started getting errors. After I completely removed this package the problem persisted, even with the exact same files as before when it worked, so I reckon it could also be due to the NX workspace caching getting messed up ant not resetting!?
I've migrated your reproduction repo to latest (19.5.3), and it works now :) Similarly when creating the repo from scratch.
@Coly010 do you happen to have a repository? I just tried to recreate everything from scratch and it's still not working :/
I follow my own procedure, the only change I've done: step 3
is not needed any more since with --preset=npm
now Nx creates the folder packages
for you. Then on step 5
and 6
i choose "derived"