ng-packagr: Secondary entry point bug: `src/` path gets part of package import path

Type of Issue

[x] Bug Report
[ ] Feature Request

Description

When placing the main entry point in src/ and every subentry point in a new folder inside src/ you will get the primary point correctly at @my/package-name but every secondary entry point is only accessible by the path @my/package-name/src/*.

This is a remaining error after fixing the base url stuff in this PR https://github.com/dherges/ng-packagr/pull/862

Relates to: https://github.com/dherges/ng-packagr/issues/854

How To Reproduce

I created a new example in the ng-packagr project. See my branch https://github.com/georgiee/ng-packagr/tree/src-entry-point-bug.

  1. Launch example nested-src.
  2. It works. But only because it’s fixed by providing this import
import { STATIC_FOO_VALUE, FooClass } from '@sample/nested-src/src/foo';

instead of

import { STATIC_FOO_VALUE, FooClass } from '@sample/nested-src/foo';

Expected Behaviour

This path should work:

import { STATIC_FOO_VALUE, FooClass } from '@sample/nested-src/foo';

Version Information

$: ng -v
@angular-devkit/architect    0.6.3 (cli-only)
@angular-devkit/core         0.6.0
@angular-devkit/schematics   0.6.3 (cli-only)
@angular/cdk                 6.0.1
@angular/router              6.0.1
@ngtools/json-schema         1.1.0
@schematics/angular          0.6.3 (cli-only)
@schematics/update           0.6.3 (cli-only)
rxjs                         6.1.0
typescript                   2.7.2

About this issue

  • Original URL
  • State: open
  • Created 6 years ago
  • Reactions: 9
  • Comments: 30 (14 by maintainers)

Most upvoted comments

Is there any intention on clarifying why placing package.json under the src folder works here? First, it is very commonplace and standard for the package.json to be at the root level of the package it is describing. Second, it’s not intuitive that

root (abc/def)
|--package.json
|--ng-package.json
|--src
  |--main
    |--public_api.ts
    |--...
  |--secondary
    |--package.json
    |--public_api.ts
    |--...

produces the packages:

abc/def
abc/def/src/secondary

while

root (abc/def
|--src
  |--package.json
  |--ng-package.json
  |--main
    |--public_api.ts
    |--...
  |--secondary
    |--package.json
    |--public_api.ts
    |--...

produces the packages:

abc/def
abc/def/secondary

Issues with pathing and dependencies notwithstanding, the location of my source code does not change. Why does the location of the package.json file itself dictate package destinations?

My exact use case doesn’t need a main module so I would rather have a package.json at the root that has a name of the common namespace (abc/def, in my example) and is completely independent from the packaged code itself and ng/package.json files in each of the feature modules that dictate how they would be packaged.

This also effects when adding secondary entry point to lib generated from ng g library

Hey everyone, so I managed to compile my library without the use of a /src in the root but I had to make some changes that’s worth mentioning. I followed the suggestion by @alan-agius4 to put secondary entry points at the root of the library. Also combined what I could gather from the Angular Package Format and the secondary entry points documentation. Notice the absence of a /src folder in my root directory.

My versions:

@angular-devkit/architect          0.803.25
@angular-devkit/build-angular      0.803.25
@angular-devkit/build-ng-packagr   0.803.25
@angular-devkit/build-optimizer    0.803.25
@angular-devkit/build-webpack      0.803.25
@angular-devkit/core               8.3.25
@angular-devkit/schematics         8.3.25
@angular/cli                       8.3.25
@ngtools/webpack                   8.3.25
@schematics/angular                8.3.25
@schematics/update                 0.803.25
ng-packagr                         5.7.1
rxjs                               6.4.0
typescript                         3.5.3
webpack                            4.39.2

The following is my library directory structure:

my-lib
├── ng-package.json
├── package.json
├── public-api.ts
├── test.ts
└── feature-1
    ├── src
    |   ├── public_api.ts
    |   └── *.ts
    └── package.json
└── feature-2
    ├── src
    |   ├── public_api.ts
    |   └── *.ts
    └── package.json
  • Inside my angular.json file, for my library project, i changed my sourceRoot from “project/my-lib/src” to “project/my-lib”
  • My package.json at the root of my library is:
{
  "name": "my-lib",
  "version": "0.0.1",
  "private": true,
  "peerDependencies": {
    "@angular/common": "^8.2.14",
    "@angular/core": "^8.2.14"
  },
  "ngPackage": {
    "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
    "dest": "../../dist/my-lib",
    "lib": {
      "entryFile": "./public-api.ts"
    }
  }
}
  • My ng-package.json at the root of my library is:
{
  "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
  "dest": "../../dist/my-lib",
  "lib": {
    "entryFile": "./public-api.ts"
  }
}

After I run ng build --project=my-lib, I get the following directory structure in dist/my-lib:

my-lib
├── bundles (folder)
├── esm5 (folder)
├── esm2015 (folder)
├── fesm5 (folder)
├── fesm2015 (folder)
├── feature-1 (folder)
├── feature-2 (folder)
├── my-lib.d.ts
├── my-lib.metadata.json
├── package.json
├── public-api.d.ts
├── README.md

Just as a side note as well, the public-api.ts at my root has the following code:

/*
 * Public API Surface of my-lib
 */

 export * from 'my-lib/feature-1';
 export * from 'my-lib/feature-2';

Hopefully this helps and I didn’t miss out anything. Also my first time contributing.

@alan-agius4 really thanks so much again, I was able to get it working in my dummy issue repo fairly easy, without even duplication of dependencies: https://github.com/abbazabacto/ngpackagr-issue/commit/9534c2b6271e4618002c75253a84ad3c9837cdd4

I can totally live with the package.json file in the ./src directory!

I did the simple steps:

  • rename package.json name to some src name
  • add package.json to ./src with the correct package name, version, ngPacakge-config and just the peerDependencies
  • then build ng-packagr from the original root by just pointing to the new pacakge.json in src ng-packagr -p src/package.json
  • (not in commit above) alternatively I configured dest ngPackage-config in ./src/package.json to be ../dist so the output is the same as in my other repos

@Qarun-Qadir-Bissoondial

Inside my angular.json file, for my library project, i changed my sourceRoot from “project/my-lib/src” to “project/my-lib”

Don’t forget testing:

        "test": {
          "builder": "@angular-devkit/build-angular:karma",
          "options": {
            "main": "projects/my-lib/test.ts",   <--------------- remove src/
            "tsConfig": "projects/my-lib/tsconfig.spec.json",
            "karmaConfig": "projects/my-lib/karma.conf.js"
          }
        },

I too have been fighting this for far too long. Your setup has gotten me the closest with only one annoyance, that that test.ts file shows up in test coverage reports. A small price to pay though.

I had a look at this, and it seems to be an Angular bug. I have opened an issue: https://github.com/angular/angular/issues/24225

It looks like it’s a problem with barrel exports.

@alan-agius4 I just pushed the commits to master, added one more commit to update the README.md with the same issue description as above. Hope you can spot where the issue in metadata generation comes from. GL and thanks again!

I am happy to see that everyone is a bit more happy now 😄

Thanks for the clarification about the src folders. Makes sense this way finally 😃 I will give the second package.json a try. Looks promising what @abbazabacto have done in the showcased commit 👌Thanks for this!

@alan-agius4 The primary endpoint should always be based on the name of the package.json as this is the name of the node module upon publishing, even if the public_api.ts is placed somewhere else. (Actually when I tried to place the public_api.ts somewhere else, it failed on some of my own aliases, guess I kind of understand why this is happening now)

The request is all about the secondary endpoints (and maybe also on the location of the main entry point), they are based on the paths relative to this package.json. Which means that your development files should always be in the same file structure as you want the eventual package/bundle paths to be.

I can understand that it become quite cumbersome to make this work on your end. Maybe it is just something we have to learn to live with.

I did updated a previous ngpackagr issue repo to demonstrate what a desired architecture would be https://github.com/abbazabacto/ngpackagr-issue:

  • package.json is pointing the primary entry point to src/index.ts
  • secondary entry points are all under src/**
  • tsconfig has path alias of entry points so they can depend on each other
  • primary entry point uses the path alias to make a module containing all sub modules, and re-exporting all contents (because the aliases are used, they are seen as an external dependencies and therefor not included in the primary bundle)
  • ./demo (Angular CLI) is using path alias to have examples demonstrating implementation

Yeah it’s exactly like @abbazabacto describes it. To draw a picture of it:

  1. This is a mess All secondary entry points (feature-*) are in the same folder as anything else.
.
├── node_modules
├── README.md
├── feature-a
│   └── public-api.ts
├── feature-b
│   └── public-api.ts
├── feature-c
│   └── public-api.ts
├── karma.conf.js
├── package.json
├── postinstall-message.js
├── public-api.ts
├── test.ts
├── tsconfig.lib.json
└── tsconfig.spec.json

  1. While this will collect any source in src/ and files like test.ts, README.md are located one level higher.
.
├── node_modules
├── README.md
├── karma.conf.js
├── package.json
├── src
│   ├── feature-a
│   │   └── public-api.ts
│   ├── feature-b
│   │   └── public-api.ts
│   ├── feature-c
│   │   └── public-api.ts
│   └── public-api.ts
├── test.ts
├── tsconfig.lib.json
└── tsconfig.spec.json

Thanks for your endless support @alan-agius4!

Okay, so from my understanding you want something like (Angular Material2)[https://github.com/angular/material2/tree/master/src/lib], IMHO, if you want something like that you should place a package.json under src And trigger the build there. That is the most straight forward way to achieve the desired structure, and If you do this you wouldn’t even need to create path mappings yourselves as ng-packagr handles these.

I will still try to find a solution for this feature/issue but from my end, I am a bit busy with other stuff right now. So it has to wait a couple of days, maybe weeks.

My worries is that basing the structure and naming of secondaries based on the lib.entryPoint is quite fragile. For instance, if you have something like the below it it will be hell to create the proper naming for 2nd and 3rd level entry point. And in this instance, why should ng-packagr make a distinction if the folder is src or not? It will become rather complex to create the naming of the packages, and where they should be emitted.

I don’t think that using ngPackage.lib is good enough. Especially for 3 level entrypoints.

├── src
│   ├── feature-a
│      └── src
│         └── public-api.ts
│         ├── sub-feature-a
│            └── src
│                  └── public-api.ts