ng-packagr: Exporting modules from outside the library folder fails with an error

Type of Issue

[x ] Bug Report
[ ] Feature Request

Description

I’m trying to export parts of an existing angular app as a library, but it seems there is path issues. Here is my folder structure: image

And my public_api.ts:

export * from '../src/app/shared/shared.module';

But when trying to build the library, I get this error:

Building Angular library from lib/ng-package.json

BUILD ERROR
Error at /Users/noda/github/addon-library/test/lib/.ng_build/ts/public_api.ts:2:15: Cannot find module '../src/app/shared/shared.module'.
Error: Error at /Users/noda/github/addon-library/test/lib/.ng_build/ts/public_api.ts:2:15: Cannot find module '../src/app/shared/shared.module'.
    at new UserError (/Users/noda/github/addon-library/test/node_modules/@angular/tsc-wrapped/src/tsc.js:27:28)
    at check (/Users/noda/github/addon-library/test/node_modules/@angular/tsc-wrapped/src/tsc.js:93:15)
    at Tsc.typeCheck (/Users/noda/github/addon-library/test/node_modules/@angular/tsc-wrapped/src/tsc.js:173:9)
    at /Users/noda/github/addon-library/test/node_modules/@angular/tsc-wrapped/src/main.js:122:23
    at <anonymous>

How To Reproduce

  1. Create a typical angular-cli app with a feature module.
  2. Create a dedicated lib folder containing ng-packagr configuration
  3. Try to export your app feature module in lib/public_api.ts with something like export * from '../src/app/my-feature/my-feature.module';
  4. Build the library

Expected Behaviour

The library builds without error πŸ˜„

Version Information

ng-packagr: v1.6.0
node: v8.8.1
@angular: v5.0.5
rxjs: 5.5.2
zone.js: 0.8.14

please include any version information that might be relevant, e.g. other third-party libraries

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 24
  • Comments: 39 (4 by maintainers)

Most upvoted comments

I get the same error when using sencondary entry points.

I try to build the library according to the following scheme:

  • All services which have to have one and only one instance per application should be implemented in the BarModule here.
  • And ViewModules should import services from FooModule.

Here is a link to the git project of the sample library: https://github.com/annalen/example-library.

This example project is structured as follows:

example-library
β”œβ”€β”€ foo
|   β”œβ”€β”€ bar
|   |	β”œβ”€β”€ src
|   |	|   β”œβ”€β”€ bar.module.ts
|   |	|   └── settings.service.ts
|   |	β”œβ”€β”€ index.ts
|   |	β”œβ”€β”€ package.json
|   |	└── public_api.ts
|   β”œβ”€β”€ view
|   |	β”œβ”€β”€ src
|   |	|   β”œβ”€β”€ view.component.ts
|   |	|   β”œβ”€β”€ view.component.html
|   |	|   └── view.module.ts
|   |	β”œβ”€β”€ index.ts
|   |	β”œβ”€β”€ package.json
|   |	└── public_api.ts
|   └── package.json
β”œβ”€β”€ public_api.ts
└── package.json

In this example case, the ViewModule imports the BarModule and the ViewComponent uses the SettingsService.

And I think that’s why I get the following error.:

File '.../example-library/lib/foo/bar/src/bar.module.ts' is not under 'rootDir' 
'...\example-library\lib\foo\view'. 'rootDir' is expected to contain all source files.
error TS6059: 
File '.../example-library/lib/foo/bar/src/settings.service.ts' is not under 'rootDir' 
'..\example-library\lib\foo\view'. 'rootDir' is expected to contain all source files.

Thank you for your help.

I have found a solution that suits my needs.

Context: The utils package used as an example below is meant to be a framework-agnostic tool set that is used across internal applications (vanilla, angular, etc). If the project was written in JS, then any tool in the package could be utilized.

While better solution exists in the form of custom NPM scripts, I am also developing some Angular packages alongside, which is why I’m using Angular CLI to handle the scaffolding and build process. That’s why you won’t see any true Angular modules being used in the example.

Anyways, it made sense to split tools by feature or function into separate packages that would then be able to be imported separately into consuming applications. The utils package evolved into having secondary endpoints with a source structure that resembled:

.
β”œβ”€β”€ dist
β”œβ”€β”€ node_modules
β”œβ”€β”€ packages (1)
β”‚  β”œβ”€β”€ utils (2)
β”‚  β”‚   └── geometry (3)
β”‚  β”‚   β”‚   β”œβ”€β”€ src
β”‚  β”‚   β”‚   β”‚   β”œβ”€β”€ lib
β”‚  β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ esri
β”‚  β”‚   β”‚   β”‚   β”‚   β”‚   └── centroidFromGeometry.ts
β”‚  β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ generic
β”‚  β”‚   β”‚   β”‚   β”‚   β”‚   └── nearestIndex.ts (6)
β”‚  β”‚   β”‚   β”‚   └── public-api.ts
β”‚  β”‚   β”‚   β”œβ”€β”€ public-api.ts
β”‚  β”‚   β”‚   β”œβ”€β”€ ng-package.json (7)
β”‚  β”‚   β”‚   └── package.json
β”‚  β”‚   β”œβ”€β”€ number (4)
β”‚  β”‚   β”‚   β”œβ”€β”€ src
β”‚  β”‚   β”‚   β”‚   β”œβ”€β”€ lib
β”‚  β”‚   β”‚   β”‚   β”‚   └── smallestIndex.ts (5)
β”‚  β”‚   β”‚   β”‚   └── public-api.ts
β”‚  β”‚   β”‚   β”œβ”€β”€ public-api.ts
β”‚  β”‚   β”‚   β”œβ”€β”€ ng-package.json
β”‚  β”‚   β”‚   └── package.json
β”‚  β”‚   β”œβ”€β”€ public-api.ts
β”‚  β”‚   β”œβ”€β”€ ng-package.json
β”‚  β”‚   └── package.json
β”œβ”€β”€ angular.json
β”œβ”€β”€ package.json
β”œβ”€β”€ tsconfig.json
└── tslint.json

(1) Renamed default β€œprojects” folder generated by Angular CLI. The scope of the project is @tamu-gisc.

(2) Scoped package entry point. The entry point is @tamu-gisc/utils

(3) Secondary entry point for the utils package. The entry point is @tamu-gisc/utils/geometry

(4) Secondary entry point for the utils package. - The entry point is @tamu-gisc/utils/number

(5) Dependency FOR (6)

(7) Specifies the entry file for the secondary entry point:

{
  "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
  "lib": {
    "entryFile": "public-api.ts",
    "umdModuleIds": {}
  }
}

When ng build --project=utils was executed, NgPackagr attempted to build both (3) geometry and (4) number packages as secondary entry points for (2) utils. However, when NgPackagr builds the (3) geometry secondary entry point, it realizes that (5) is not at or in the same package directory relative to (3) geometry’s public-api.ts entry file specified in (7).

The solution I found was reformatting the project structure to the following:

.
β”œβ”€β”€ dist
β”œβ”€β”€ node_modules
β”œβ”€β”€ packages (1)
β”‚  β”œβ”€β”€ utils (2)
β”‚  β”‚   β”œβ”€β”€ internal (8)
β”‚  β”‚   β”‚   β”œβ”€β”€ geometry
β”‚  β”‚   β”‚   β”‚   β”œβ”€β”€ esri
β”‚  β”‚   β”‚   β”‚   β”‚   └── centroidFromGeometry.ts
β”‚  β”‚   β”‚   β”‚   β”œβ”€β”€ generic
β”‚  β”‚   β”‚   β”‚   β”‚   └── nearestIndex.ts (6)
β”‚  β”‚   β”‚   β”œβ”€β”€ number
β”‚  β”‚   β”‚   β”‚   └── smallestIndex.ts (5)
β”‚  β”‚   β”œβ”€β”€ geometry (3)
β”‚  β”‚   β”‚   β”œβ”€β”€ ng-package.json (7)
β”‚  β”‚   β”‚   └── package.json
β”‚  β”‚   β”œβ”€β”€ number (4)
β”‚  β”‚   β”‚   β”œβ”€β”€ ng-package.json
β”‚  β”‚   β”‚   └── package.json
β”‚  β”‚   β”œβ”€β”€ public-api.ts
β”‚  β”‚   β”œβ”€β”€ public-api.geometry.ts (9)
β”‚  β”‚   β”œβ”€β”€ public-api.number.ts
β”‚  β”‚   β”œβ”€β”€ ng-package.json
β”‚  β”‚   └── package.json
β”œβ”€β”€ angular.json
β”œβ”€β”€ package.json
β”œβ”€β”€ tsconfig.json
└── tslint.json

With this layout, all the business logic of the library modules goes into (8) internal, and (6) has no problem importing (5). The secondary entry point file for the (3) geometry package has moved to the root of the (9) utils package.

The geometry package (7) ng-package.json now looks like:

{
 "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
 "lib": {
   "entryFile": "../public-api.geometry.ts",
   "umdModuleIds": {}
 }
}

Why this works?

This explanation is potentially wrong, so take it with a grain of salt. However, it’s the best I can do in my own words.

NgPackagr recognizes secondary entry points by the presence of either package.json with the β€œngPackage” property OR ng-package.json (Source). Because of this, it is possible to designate secondary entry points with either or both package.json or ng-package.json (see (3) and (4) in the file tree above) and omitting any source files there. The source files are moved to a β€œshared” space where all packages can access and reference (See (8)). The entry file for each of the packages are alongside the primary entry point’s entry file (See (9)) and in this way the all source files are at or in the same package directory relative to their respective entry file, meeting the 'rootDir' is expected to contain all source files. requirement.

I have found this solution from https://github.com/dherges/nx-packaged.

package.json

  "scripts": {
    "build:libs": "yarn libs:one-lib:build && yarn libs:two-lib:build && yarn libs:three-lib:build",
    "libs:one-lib:build": "ng-packagr -p libs/one-lib/package.json ",
    "libs:two-lib:build": "ng-packagr -p libs/two-lib/package.json",
    "libs:three-lib:build": "ts-node libs/three-lib/build.ts"
  }

libs/one-lib/package.json

{
  "$schema": "../../node_modules/ng-packagr/package.schema.json",
  "name": "@nx-packaged/one-lib",
  "version": "1.0.0",
  "ngPackage": {
    "lib": {
      "entryFile": "index.ts"
    },
    "dest": "../../@nx-packaged/one-lib"
  }
}

output

$ npm run build:libs
$ ls @nx-packaged/
one-lib		one-lib.tgz	three-lib	three-lib.tgz	two-lib		two-lib.tgz

Basically the owne, @dherges, want us to run ng-packagr for each library.

If so, my question is how we build ONE package to include all library.

I’m facing the same problem, because I have some internal shared code used for some different libs at the same repo

I’m also trying to include common code in a number of shared libraries and getting the file outside rootDir issue.

Being able to programmatically compile and override rootDir in a custom tsconfig is not helping, because ng-packagr just overwrites the rootDir I specified in https://github.com/dherges/ng-packagr/blob/master/src/lib/ts/tsconfig.ts#L55

Seems like there is currently no way to include common code in a number of libraries.

I was thinking to have a symbolic link, but it’s kinda ugly…

I’m building two libraries, on that may be standalone (an icons library) and one (a UI component library) that depends on the icons. I use tsconfig paths to manage references between the two libraries. The only solution that works is to build the standalone library, copy it to my main project node_modules directory (or symlink). This is quirky and makes the build more brittle, however there doesn’t appear to be any other option.

Still having this issue with Angular 6. Thanks to this I have no way to even create a simple common logger service

Edit: solved by using the method suggested above