Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

'rootDir' is expected to contain all source files. Bundle failed: test-icons #11289

Open
SunStupic opened this issue Jul 26, 2022 · 22 comments
Open
Assignees
Labels
scope: react Issues related to React support for Nx type: bug

Comments

@SunStupic
Copy link

SunStupic commented Jul 26, 2022

Current Behavior

Now when I run build command in our repo with multiple publishable libs, I get error like this:

test-icons:build-lib

Bundling test-icons...
Error during bundle: Error: /Users/stupic/Documents/demos/build-lib-bug-repro/libs/test-icons/src/index.ts(2,15): semantic error TS6059: File '/Users/stupic/Documents/demos/build-lib-bug-repro/libs/test-core/src/index.ts' is not under 'rootDir' '/Users/stupic/Documents/demos/build-lib-bug-repro/libs/test-icons/src'. 'rootDir' is expected to contain all source files.
Bundle failed: test-icons

 ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

 >  NX   Ran target build-lib for project test-icons (3s)

    ✖    1/1 failed
    ✔    0/1 succeeded [0 read from cache]

Expected Behavior

Expect to build successfully or with a better error message like: test-core has no build-lib target.

Steps to Reproduce

  1. clone the repro repo:
git clone git@github.com:SunStupic/build-lib-bug-repro.git
  1. pnpm i
  2. nx run test-icons:build-lib

Failure Logs

test-icons:build-lib

Bundling test-icons...
Error during bundle: Error: /Users/stupic/Documents/demos/build-lib-bug-repro/libs/test-icons/src/index.ts(2,15): semantic error TS6059: File '/Users/stupic/Documents/demos/build-lib-bug-repro/libs/test-core/src/index.ts' is not under 'rootDir' '/Users/stupic/Documents/demos/build-lib-bug-repro/libs/test-icons/src'. 'rootDir' is expected to contain all source files.
Bundle failed: test-icons

 ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

 >  NX   Ran target build-lib for project test-icons (3s)

    ✖    1/1 failed
    ✔    0/1 succeeded [0 read from cache]

Environment

 nx report

 >  NX   Running global Nx CLI with PNPM may have issues.

   Prefer to use "pnpm" (https://pnpm.io/cli/exec) to execute commands in this workspace.
    TIP  create a shortcut such as: alias pnx="pnpm nx --"



 >  NX   Report complete - copy this into the issue template

   Node : 16.15.1
   OS   : darwin x64
   pnpm : 7.5.0

   nx : 14.4.3
   @nrwl/angular : Not Found
   @nrwl/cypress : 14.4.3
   @nrwl/detox : Not Found
   @nrwl/devkit : Not Found
   @nrwl/eslint-plugin-nx : 14.4.3
   @nrwl/express : Not Found
   @nrwl/jest : 14.4.3
   @nrwl/js : Not Found
   @nrwl/linter : 14.4.3
   @nrwl/nest : Not Found
   @nrwl/next : Not Found
   @nrwl/node : Not Found
   @nrwl/nx-cloud : Not Found
   @nrwl/nx-plugin : Not Found
   @nrwl/react : 14.4.3
   @nrwl/react-native : Not Found
   @nrwl/schematics : Not Found
   @nrwl/storybook : Not Found
   @nrwl/web : 14.4.3
   @nrwl/workspace : 14.4.3
   typescript : 4.7.4
   ---------------------------------------
   Community plugins:

Workaround

Now the workaround is simple, we need to add build-lib for all the publishable packages or we change back to use build.

@athrunsun
Copy link

To help everyone understand what's going on, here is the process of how I found the root cause.

What's the problem

Say we have a monorepo, with several libraries inside, let's name them lib-a, lib-b and lib-c.

lib-a depends on lib-b and lib-c, in /path-to-monorepo/libs/lib-a/src/foo.tsx, there will be:

import { jim } from '@project-org/lib-b';
import { john } from '@project-org/lib-c';

Path aliases are defined in root tsconfig.base.json (which is extended by root tsconfig.json):

  • @project-org/lib-b points to /path-to-monorepo/libs/lib-b/src/bar.ts
  • @project-org/lib-c points to /path-to-monorepo/libs/lib-c/src/tor.ts

lib-a has a build target build-lib, which invokes executor @nrwl/web:rollup, another build target build which invokes build-lib and some other targets. lib-b has only a build target build which also invokes executor @nrwl/web:rollup, lib-c has 2 build targets build and build-lib just like lib-a.

Here is the essential part of project.json of lib-a:

{
    "sourceRoot": "libs/lib-a/src",
    "projectType": "library",
    "tags": [],
    "targets": {
        "build": {
            "executor": "@nrwl/workspace:run-commands",
            "options": {
                "commands": ["nx run lib-a:build-lib", "nx run lib-a:build-css"],
                "parallel": false
            }
        },
        "build-lib": {
            "executor": "@nrwl/web:rollup",
            "outputs": ["{options.outputPath}"],
            "options": {
                "outputPath": "dist/libs/lib-a",
                "tsConfig": "libs/lib-a/tsconfig.lib.json",
                "project": "libs/lib-a/package.json",
                "entryFile": "libs/lib-a/src/index.ts",
            }
        }
    }
}

And here is the essential part of project.json of lib-b:

{
    "sourceRoot": "libs/lib-b/src",
    "projectType": "library",
    "tags": [],
    "targets": {
        "build": {
            "executor": "@nrwl/web:rollup",
            "outputs": ["{options.outputPath}"],
            "options": {
                "outputPath": "dist/libs/lib-b",
                "tsConfig": "libs/lib-b/tsconfig.lib.json",
                "project": "libs/lib-b/package.json",
                "entryFile": "libs/lib-b/src/bar.ts",
            }
        }
    }
}

And here is the essential part of project.json of lib-c:

{
    "sourceRoot": "libs/lib-c/src",
    "projectType": "library",
    "tags": [],
    "targets": {
        "build": {
            "executor": "@nrwl/workspace:run-commands",
            "options": {
                "commands": ["nx run lib-c:build-svg", "nx run lib-c:build-lib"],
                "parallel": false
            }
        },
        "build-lib": {
            "executor": "@nrwl/web:rollup",
            "outputs": ["{options.outputPath}"],
            "options": {
                "outputPath": "dist/libs/lib-c",
                "tsConfig": "libs/lib-c/tsconfig.lib.json",
                "project": "libs/lib-c/package.json",
                "entryFile": "libs/lib-c/src/tor.ts",
            }
        }
    }
}

All tsconfig.lib.json extends the root tsconfig.base.json.

Now when we issue nx run lib-a:build, we'll see an error like:

Error during bundle: Error: /path-to-monorepo/libs/lib-a/src/foo.tsx(5,37): semantic error TS6059: File '/path-to-monorepo/libs/lib-b/src/bar.ts' is not under 'rootDir' '/path-to-monorepo/libs/lib-a/src'. 'rootDir' is expected to contain all source files.

What's strange is, there's NO such error for lib-b.

Dig into the source code of nx to find the root cause

We can add --verbose option when building lib-a to see where exactly the error is thrown, and it takes us here, hmm, doesn't help too much.

Let's dive directly into rollup executor then.

NOTE the original error says TS6059 - which means it is thrown by tsc. Although the library is transpiled by babel by default, tsc must be invoked somewhere.

Ah, here it is. But, hmm, still doesn't know what could go wrong.

Let's take a look what createCompilerOptions(options, dependencies) produces.

But hold on, options is rollup options, what about dependencies? It's the calculated project dependencies, which looks like:

[
  {
    name: '@project-org/lib-c',
    outputs: [ 'dist/libs/lib-c' ],
    node: { type: 'lib', name: 'lib-c', data: [Object] }
  },
  // ...
]

However we'll find @project-org/lib-b missing in the list. Stay calm, we'll come back to this later.

With this list of dependencies, createCompilerOptions(options, dependencies) produces:

{
  rootDir: '/path-to-monorepo/libs/lib-a/src',
  allowJs: false,
  declaration: true,
  paths: {
    '@project/lib-b': [ 'libs/lib-b/src/foo.ts' ],
    '@project/lib-c': [ 'dist/libs/lib-c' ],
    // ...
  }
}

Huh? @project/lib-c points to dist/libs/lib-c instead of libs/lib-c/src/tor.ts, why?

In packages/workspace/src/utilities/buildable-libs-utils.ts, computeCompilerOptionsPaths() reads path aliases defined in project's tsconfig.json and update them, in updatePaths(), paths[dep.name] = dep.outputs. Ah ha, this is where the outputs folder dist/libs/lib-c is used to replaced the original path. And because @project-org/lib-b is missing in the dependency list, its path alias stays intact.

What's inside dist/libs/lib-c? Generated .d.ts files, transpiled .js files, etc., anyway NO .ts source files, thus won't break TS check performed by rollup-plugin-typescript2.

However, @project/lib-b is not so lucky, libs/lib-b/src/foo.ts is a TS source file itself outside of rootDir, thus tsc will complain.

Now, the question becomes, why is @project-org/lib-b missing in the dependency list?

Let's get back to const { target, dependencies } = calculateProjectDependencies(...), one of the arguments of this function call, projectGraph, is read from node_modules/.cache/nx/nxdeps.json (default path).

And next is the essential part.

const depNode = projGraph.nodes[dep] || projGraph.externalNodes[dep];, where projGraph.nodes looks like:

{
    "lib-b": {
        "name": "lib-b",
        "type": "lib",
        "data": {
            "root": "libs/lib-b",
            "sourceRoot": "libs/lib-b/src",
            "projectType": "library",
            "tags": [],
            "targets": {
                "build": {
                    "executor": "@nrwl/web:rollup",
                    "outputs": ["{options.outputPath}"],
                    "options": {
                        "outputPath": "dist/libs/lib-b",
                        "tsConfig": "libs/lib-b/tsconfig.lib.json",
                        "project": "libs/lib-b/package.json",
                        "entryFile": "libs/lib-b/src/bar.ts",
                        "external": ["react/jsx-runtime"],
                        "rollupConfig": "@nrwl/react/plugins/bundle-rollup",
                    }
                }
            },
            "files": [
                // ...
            ]
        }
    },
    "lib-c": {
        "name": "lib-c",
        "type": "lib",
        "data": {
            "root": "libs/lib-c",
            "sourceRoot": "libs/lib-c/src",
            "projectType": "library",
            "tags": [],
            "targets": {
                "build": {
                    "executor": "@nrwl/workspace:run-commands",
                    "options": {
                        "commands": ["nx run lib-c:build-svg", "nx run lib-c:build-lib"],
                        "parallel": false
                    }
                },
                "build-lib": {
                    "executor": "@nrwl/web:rollup",
                    "outputs": ["{options.outputPath}"],
                    "options": {
                        "outputPath": "dist/libs/lib-c",
                        "tsConfig": "libs/lib-c/tsconfig.lib.json",
                        "project": "libs/lib-c/package.json",
                        "entryFile": "libs/lib-c/src/tor.ts",
                        "external": ["react/jsx-runtime"],
                        "rollupConfig": "@nrwl/react/plugins/bundle-rollup",
                    }
                }
            },
            "files": [
                // ...
            ]
        }
    }
}

if (isBuildable(targetName, depNode)), targetName is build-lib since rollup executor is invoked by build-lib of lib-a.

Let's look at the definition of isBuildable(), it determines whether a dependency is buildable by:

node.data.targets &&
    node.data.targets[target] &&
    node.data.targets[target].executor !== ''

As for lib-c, it returns true. But as for lib-b, since it doesn't have a build-lib target, that condition returns false! This is why lib-b was missing in the dependency list.

Solution

To make nx thinks lib-b as buildable, we need to define a build-lib target for it. Here is the updated version of project.json of lib-b:

{
    "sourceRoot": "libs/lib-b/src",
    "projectType": "library",
    "tags": [],
    "targets": {
        "build": {
            "executor": "@nrwl/workspace:run-commands",
            "options": {
                "commands": ["nx run lib-b:build-lib"],
                "parallel": false
            }
        },
        "build-lib": {
            "executor": "@nrwl/web:rollup",
            "outputs": ["{options.outputPath}"],
            "options": {
                "outputPath": "dist/libs/lib-b",
                "tsConfig": "libs/lib-b/tsconfig.lib.json",
                "project": "libs/lib-b/package.json",
                "entryFile": "libs/lib-b/src/bar.ts",
            }
        }
    }
}

To summarize it, all internal dependencies of a library must have a build target of the same name, in order to make nx treat them as buildable.

Other workarounds

  • Use a customized rollup config to remove rollup-plugin-typescript2.
  • Change path alias of @project-org/lib-b in tsconfig.base.json to "@project-org/lib-b": ["dist/libs/lib-b", "libs/lib-b/src/bar.ts"]
    • This has a downside that, in our editor like vscode, @project-org/lib-b will point to dist directory which contains .d.ts files but not real source .ts files, not a good development experience.
  • Add a path alias "@project-org/lib-b": ["dist/libs/lib-b"] in libs/lib-b/tsconfig.lib.json
    • This is better than the previous one, @project-org/lib-b still points to libs/lib-b/src/bar.ts in our editor (because of the path alias "@project-org/lib-b": ["libs/lib-b/src/bar.ts"] in root tsconfig.json), while "@project-org/lib-b": ["dist/libs/lib-b"] in libs/lib-b/tsconfig.lib.json overrides it - when parsedTSConfig.compilerOptions.paths exists, path aliases in parsedTSConfig.extends are bypassed.

@AgentEnder AgentEnder added the scope: react Issues related to React support for Nx label Jul 26, 2022
@JonathanWilbur
Copy link

@SunStupic I had an issue like this (if not the same issue). What fixed it for me was adding the paths entry in the tsconfig.base.json in the root of the monorepo for the project that I was attempting to build as well. So if you have a libraries liba and libb, and libb imports liba from within the monorepo, you not only need to ensure that there is a liba entry in the paths section of your base TypeScript configuration, but also that there is a libb entry as well.

I'd be curious to hear if this works for you.

@matigda
Copy link

matigda commented Aug 11, 2022

I'm using "@nrwl/angular:ng-packagr-lite" as an executor and @JonathanWilbur - I tried to add those paths basically in every tsconfig file I found. Unfortunately still getting this error.

@xeinebiu
Copy link

I had the same issue. I fixed it by updating to the latest NX.

Please make sure you either update globally the nx or execute it using npx.

Example:
npx nx build {libraryName}

@ActJV
Copy link

ActJV commented Sep 27, 2022

adding the dist path(s) to the tsconfig did the trick for me.

@xeinebiu
Copy link

xeinebiu commented Sep 27, 2022

adding the dist path(s) to the tsconfig did the trick for me.

Thats for sure works, but is hard to develop this way. You have to manually build libraries in correct order. You are also importing now from DIST, means changes you make are not seamless applied to the rest of the project.

@Stephanemw
Copy link

Stephanemw commented Nov 21, 2022

all internal dependencies of a library must have a build target of the same name, in order to make nx treat them as buildable

that solved my problems - this is non-obvious, would be great to have some output that helped diagnose this issue when having multiple and interdependent js buildable libraries.

Thanks @athrunsun !

@ronaldsutantio-acn
Copy link

Any update on this? all workaround mentioned above didn't work for me.
I am using nx v14.3.6 with react library and tried also with latest nx version v15.2.1 as well, but the result is same.

@d-s-i
Copy link

d-s-i commented Jan 3, 2023

Changing the tsconfig paths helped compiling the project, but it raises another error `Error: Cannot find module '@my-org/lib-b', because the imported lib path is not changed in the compiled file

@whalemare
Copy link

In my case, I can resolve issue by removing "composite": true in tsconfig.json of my application

Снимок экрана 2023-02-20 в 10 30 31

@7alip
Copy link

7alip commented Mar 17, 2023

In my case, I noticed that when I moved some files to a different folder, some relative import paths were automatically changed.

It was import { } from 'libs/types/src...' but should be import { } from '../common'

@AdditionAddict
Copy link

AdditionAddict commented Apr 3, 2023

I keep getting this error with @nrwl/angular:ng-packagr-lite libs and it's pretty miserable DX when it happens.

I keep doing nx reset which fixes it for a little while but then it breaks again. Using pnpm and windows if it makes a difference. Not sure if I'm going a little mad but using admin cmd to run commands seems to work better. But could be coincidence since I've tried everything: deleting my %temp% files, deleting the pnpm cache in my local, deleting node_modules, reinstalling.

update: not had any further issues on latest 15.9.2. I don’t recommend messing with tsconfigs almost ever (trust the nx team more than ts server)

@Frikki
Copy link

Frikki commented Aug 29, 2023

Check that the include paths in the tsconfig.lib.json don’t reference files in another library.

@kopach
Copy link
Contributor

kopach commented Oct 19, 2023

Found issue in @nx/rollup package. Can be temporarily fixed by patching @nx/rollup dependency. E.g. by using pnpm's patch mechanism

--- a/src/executors/rollup/rollup.impl.js
+++ b/src/executors/rollup/rollup.impl.js
@@ -214,7 +214,7 @@ exports.createRollupOptions = createRollupOptions;
 function createTsCompilerOptions(config, dependencies, options) {
     const compilerOptionPaths = (0, buildable_libs_utils_1.computeCompilerOptionsPaths)(config, dependencies);
     const compilerOptions = {
-        rootDir: options.projectRoot,
+        rootDir: config.options.rootDir,
         allowJs: options.allowJs,
         declaration: true,
         paths: compilerOptionPaths,

Fix also pushed to main repo #19725

@mandarini mandarini self-assigned this Oct 24, 2023
@mandarini
Copy link
Member

mandarini commented Oct 25, 2023

The issue, as very well explained by @athrunsun, is that if your build target names are different, nx will throw the 'rootDir' is expected to contain all source files.. This is fixable if you align all the build target names to be the same.

I will look into if we are planning to address this. Sorry about that!

Solution for now: #17798 (comment)

@leosvelperez
Copy link
Member

leosvelperez commented Nov 23, 2023

The 'rootDir' is expected to contain all source files error can be caused for a number of reasons. Some might be legit TS setup issues leading to trying to compile files that are outside of the project root dir, while in a lot of cases, it's down to how we handle "buildable" libraries in Nx incremental builds scenarios.

As explained (great detective work! 👏🏻 ) by @athrunsun in #11289 (comment), Nx executors that support the incremental builds scenarios, look for targets with the same name as the build target name of the top-level project being built. The found targets' info will be used to collect their output paths and remap on the fly the TS path mappings existing in the tsconfig file at the root of your workspace to point to the compiled source. The remapping is done so we can provide the best possible DX. By default, your projects point to the dependency sources (that's what your IDE will use) and at build time, it will point to the dependency compiled code to avoid building it again. When looking up for the dependencies targets, if they are not found, the TS path mapping will remain pointing to the source code of the dependency and given it's a different project, its location will be outside of the rootDir of the project that depends on it. Hence the error.

Also, if the dependencies between targets are not correctly configured (using targetDefaults and/or dependsOn), the task graph won't be constructed correctly, and therefore, the targets from dependencies won't be found leading to the same issue.

Please note that error will also happen if the dependencies of buildable libraries are not buildable (have a target to build the library). In such cases, no target will be found because there's none and the same error will be thrown. Buildable libraries must depend on other buildable libraries. There are a couple of executors that support inlining non-buildable libraries into buildable libraries, but that feature is highly experimental and it's not recommended.

Identifying the buildable target from the dependencies has always been the challenge and we've had a constraint of keeping the target names aligned. Nx generators always generate "build" target, but of course, in real-world scenarios, developers can change those names and use something else. This is often the case when needing to do extra tasks and new targets are added and composed to build a project.

In Nx 16.6.0, we introduced a new way to identify the build target of dependent buildable libraries. The new way doesn't rely on the targets having the same name, but rather on the information available in the task graph. It's still key to have the target dependencies correctly configured. This behavior is not yet enabled by default, but you can try it by setting the following env var: NX_BUILDABLE_LIBRARIES_TASK_GRAPH=true.

We're trying to get feedback whether this new way works as expected before turning it on by default. Could you try it and let us know how it goes? We want to know:

  • We don't break existing setups (with the same target name)
  • It handles buildable library targets with different names (provided they are part of the task graph, so, target dependencies must be correctly configured)

If you already solved your issue by ensuring the same target name, you could still try this and provide feedback. As mentioned above, we want to make sure the new way doesn't break existing scenarios.

If it doesn't work for you, please try to provide a reproduction so the feedback is actionable and we can troubleshoot it.

@ggagosh
Copy link

ggagosh commented Dec 1, 2023

Buildable libraries must depend on other buildable libraries. There are a couple of executors that support inlining non-buildable libraries into buildable libraries, but that feature is highly experimental and it's not recommended.

In my current application, I am integrating non-buildable libraries into a buildable application using @nx/webpack:webpack.

However, when attempting to use the swc executor, I encountered an error. My current strategy is to convert all non-buildable libraries into buildable ones, as it seems to be the only viable solution to make this work.

@thebeard
Copy link

@7alip Must have saved me hours with your comment. Thank you

@SebastianPodgajny
Copy link

SebastianPodgajny commented Mar 19, 2024

Looks like this error is thrown in 17.3 if library has circular dependency
Lib A has circular
Lib B depends on lib A
Lib A build passes
Lib B build throws is not under 'rootDir' error for lib A

but this happens also using ng-packagr directly might be related to new template build pipeline angular/angular#54571 or bug in TS 5.4.2 microsoft/TypeScript#57750

@brodziakm
Copy link

We've started experiencing similar issues with TypeScript 5.4. (we're not using nrwl, but I'm pretty sure that this is the same underlying problem). It seems to be a TypeScript cause as opposed to Angular 17.3. We are seeing it in some library secondary entrypoints, and it isn't clear why it has started happening.

Using the TypeScript nightly builds I narrowed it down to working in 5.4.0-dev.20231221, but broken in 5.4.0-dev.20231222

That seems to point to this change as the cause: microsoft/TypeScript#55015

Hopefully this helps to identify the underlying problem. We're still investigating how to address it properly.

@brodziakm
Copy link

Looks like microsoft/TypeScript#57973 might fix the problem that was introduced in TypeScript 5.4

@zaldih
Copy link

zaldih commented Apr 6, 2024

I wasted a lot of time researching this problem because no solution worked for me.
In the end the solution was to change the library from tsc to vite.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
scope: react Issues related to React support for Nx type: bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.