angular / angular/angular

Provide contol over paths of inserted imports in ivy library build

Open
#43,271 5 comments 0 reactions 0 assignees View on GitHub
area: compiler P3
Dominant language
TypeScript
Stars
101k
Forks
27.5k
Avg merge
1d 19h
Merged PRs (30d)
288

Description

### Which @angular/* package(s) are relevant/releated to the feature request?

compiler-cli

### Description

Currently the ivy library compiler tries to guess where it has to find modules, directives, components, or pipes when it has to introduce new imports. As far as I can tell, it does this based on whether the target is an APF entry point or not.

This doesn't work well in our use case, which is a monorepository managed via yarn workspaces. In other words, local packages are installed in `node_modules` in the repo itself, and will be built into APF npm packages and deployed later on. We don't have `paths` in (most of) our tsconfigs, as node's resolution algorithm is perfectly capable of handling our setup.
But, because the files aren't in APF locally, the compiler inserts deep imports, e.g. `../../node_modules/@scope/pkg/src/my.module` rather than `@scope/pkg`.

This yields errors when using ng-packagr, as can be seen in this example repo: https://github.com/bgotink/angular-repro-20210826. The error is the same as the one seen in #38876.

We've got our own (closed source) library pipeline which "corrects" the paths the ivy compiler adds, to run the relative import back into a bare package specifier. This is done via a `beforeTs` transformer in `Program#emit`'s `customTransformers` option. We cannot implement the same behaviour in the `.d.ts` files though, as there is no `afterDeclaration` transformer in the angular `Program` (typescript's `Program` has it though). This leads to packages being published with invalid types.
I've included the output of our internal pipeline in the repository linked above.
This transformer actually already existed before we switched to ivy. It was introduced to work around #23917. We extended it when we switched to ivy, without considering the root cause of the new import paths.

### Proposed solution

Provide more control over inserted imports in the APIs of `@angular/compiler-cli`. While the compiler-cli itself doesn't know the entire context it is used in, and it has to make certain assumptions, the consumer of the compiler-cli package has more understanding of the context and can make more educated decisions.

In our scenario we would want the compiler to always use bare package specifiers for imports that are not in the entry point currently being built.

### Alternatives considered

- Consider non-APF packages inside a `node_modules` folder as "these will be APF when published" rather than "this is a local private package".
Whether this is a viable option is questionable at best, as there might be repositories where local private packages are linked via yarn workspaces
This could be turned on/off via an option in the compiler-cli APIs or via `angularCompilerOptions`.
- Provide an `afterDeclaration` custom transformer option in `Program#emit`. This would allow us to continue hacking around the inserted imports by replacing relative imports into `node_modules` with bare package specifiers.

Alternatively, do nothing in angular. We could build our entire monorepository in topographical order (dependencies before dependants) rather than the order in which the projects occur in `angular.json`, which would make it possible to use tsconfig `paths` to load the built APF packages for local dependencies.
This would work, but…
- It would require all (transitive) dependencies always be built to build any single package. This conflicts with our test pipeline which, like `nx`'s `affected` feature, builds only the packages impacted by the PR to dramatically speed up the pipeline for large projects.
- It would lead to confusing behaviour if someone forgets to rebuild a dependency, as the tsconfig would lead to the previous version being used. This is especially true as we still want to resolve dependencies to the typescript source in our IDE. Added to that the number of mistakes with our generated `@microsoft/api-extractor` API reports would skyrocket.

Contributor guide

Open the contributing guide

Research direction

Start with the @angular/compiler-cli APIs and the Program#emit customTransformers entry point, then reproduce the deep-import failure in the linked angular-repro-20210826 repository. Compare imports produced for APF and non-APF packages, including declaration output and angularCompilerOptions. Done means consumers can control inserted import paths, including bare package specifiers in generated declarations.

Written by the indexing model from the issue text.

Assessment

Tech stack
angular, typescript
Domain
build-system, tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.