angular-cli: Cannot import a module above the `src/app` folder

Bug Report or Feature Request (mark with an x)

- [x] bug report -> please search issues before submitting
- [ ] feature request

Versions.

@angular/cli: 1.2.2 node: 8.1.4 os: darwin x64 @angular/animations: 4.3.1 @angular/common: 4.3.1 @angular/compiler: 4.3.1 @angular/core: 4.3.1 @angular/forms: 4.3.1 @angular/http: 4.3.1 @angular/platform-browser: 4.3.1 @angular/platform-browser-dynamic: 4.3.1 @angular/router: 4.3.1 @angular/cli: 1.2.2 @angular/compiler-cli: 4.3.1 @angular/language-service: 4.3.1

Repro steps.

  1. clone https://github.com/silenceisgolden/angular-cli-module-import-issue.
  2. install latest global @angular/cli.
  3. run ng serve --aot.

Notice that there are modules in a lib/ directory that is even with the generated src/ directory. This contains 2 modules and 2 components to reproduce the issue.

The log given by the failure.

> ng serve --aot
** NG Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200 **
Hash: 178d829ae638b28ca8b6                                                              
Time: 4870ms
chunk    {0} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 177 kB {4} [initial] [rendered]
chunk    {1} main.bundle.js, main.bundle.js.map (main) 15.1 kB {3} [initial] [rendered]
chunk    {2} styles.bundle.js, styles.bundle.js.map (styles) 10.5 kB {4} [initial] [rendered]
chunk    {3} vendor.bundle.js, vendor.bundle.js.map (vendor) 1.15 MB [initial] [rendered]
chunk    {4} inline.bundle.js, inline.bundle.js.map (inline) 0 bytes [entry] [rendered]

ERROR in ~/.../new-app/src/$$_gendir/app/app.component.ngfactory.ts (11,21): Cannot find module '../../../lib/component-b.component.ngfactory'.

ERROR in ~/.../new-app/src/$$_gendir/lib/component-a.component.ngfactory.ts (10,21): Cannot find module './component-a.component'.

ERROR in ~/.../new-app/src/$$_gendir/lib/module-a.module.ngfactory.ts (10,21): Cannot find module './module-a.module'.

ERROR in ~/.../new-app/src/$$_gendir/lib/component-b.component.ngfactory.ts (11,21): Cannot find module './component-a.component'.

ERROR in ~/.../new-app/src/$$_gendir/lib/component-b.component.ngfactory.ts (12,21): Cannot find module './component-b.component'.

ERROR in ~/.../new-app/src/$$_gendir/lib/module-b.module.ngfactory.ts (10,21): Cannot find module './module-b.module'.

ERROR in ~/.../new-app/src/$$_gendir/lib/module-b.module.ngfactory.ts (11,21): Cannot find module './module-a.module'.

ERROR in ./src/$$_gendir/app/app.component.ngfactory.ts
Module not found: Error: Can't resolve '../../../lib/component-b.component.ngfactory' in '~/.../new-app/src/$$_gendir/app'
 @ ./src/$$_gendir/app/app.component.ngfactory.ts 9:0-67
 @ ./src/$$_gendir/app/app.module.ngfactory.ts
 @ ./src/main.ts
 @ multi webpack-dev-server/client?http://localhost:4200 ./src/main.ts
webpack: Failed to compile.

Desired functionality.

ng serve --aot should not error when importing a module outside of the generated source code from angular cli’s new command.

Mention any other details that might be useful.

Please let me know if you need any other info.

Also please note the file paths in the error output have been stripped a bit to preserve some other requirements.

My intent is to create an angular app that will serve as documentation and at the same time have a compile step to build a library of components, but manage it all in one repository.

If this is a duplicate I apologize, but please explain what the current status is with this issue before closing if you can. Thanks!

About this issue

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

Most upvoted comments

I have the same issue. Our use-case is that we have multiple Angular apps in a monorepo (7 of them, each in it’s own subfolder). We want to keep the components that are shared between those apps in a “Shared” folder at the root of the repository.

We were able to get half-way there by adding:

"paths": {
      "@shared/*": ["../Shared/*"]
 }

to the app’s tsconfig.json files.

Then, in the app.module.ts of each app, we can do stuff like:

import { InputComponent } from '@shared/components/input/input.component';

@NgModule({
    declarations: [
        InputComponent,
    ],
});

This works as long as the shared components don’t have any dependencies injected via DI. As soon as we add constructor(private <something>)..., we get runtime exceptions. Adding a providers array to the @Component decorator also results in runtime exceptions.

@filipesilva could you please expand on why you think doing something like this is a hack? Also, why would this be considered a “pretend library”?

@filipesilva This issue has become more relevant than ever now, with the addition of libraries in the cli.

We should be able to reference in src/app/demos … import { LibraryModule } from 'projects/library/src/public_api'; and build for deployment.

It works fine if I consume import { LibraryModule } from '@org/library' however then I have to go and change the imports whenever I go to build my library’s demo, importing the unpublished projects/library/src is ideal for development.

Thoughts?

Possibly related to https://github.com/angular/angular-cli/issues/7113. TL;DR: Lock the dependency of “enhanced-resolve” to 3.3.0 by running: npm i -DE enhanced-resolve@3.3.0 Though keep in mind that this is a workaround, and should be removed once “enhanced-resolve” fixes its bugs.

This issue was created originally for Angular CLI 1.2.2.

Things are very different now:

I don’t think the original reported issue (Cannot import a module above the src/app folder) is still applicable. At the time I think it was mostly related to how we mapped some files and how the AOT compiler looked for them

The underlying topic of consuming/building libraries is still very relevant though.

Our stance on that is still the same as listed in the library story: you should build your lib, then consume whatever was built.

Applications and libraries are built differently. It doesn’t make any sense at all, from a build system point of view, to attempt to build your library with a build system that isn’t meant to build libraries. The feature set of the two build systems is just plain different. You might try, and succeed, where the feature set matches, but things won’t go so well once you start going into specific feature of each.

We simply cannot recommend building your lib inside your app because it is incorrect and will lead to problems in all but the most trivial of cases.

That being said, we don’t stop you from using the tsconfig paths property to force that behaviour. If you feel confident that your library can be built using the app builder, you should be able to go ahead and do that. You’ll need to understand how TS compilation options work, and how paths interact.

@gtranter so to your point, you should be able to use paths right now. If you find a problem with that then I think that’s a bug. I’d appreciate if you opened a new issue with a reproduction so we can investigate it. But please remember to introduce a reproduction. This class of problems is very often due to setup.

@filipesilva wrote:

Your app shouldn’t be building an external library, but rather consuming it.

There is a very good reason to need to do this at the development level. Where different teams building different applications are using a shared library, and that shared library has a shared development model whereby the consuming application teams participate in the library’s development, library changes and additions come directly out of and are synchronous with application development work. This is my particular situation - both the library and various applications are internal to my organization, and the library is published and made available via an internal registry like any node package might be. A common library is desired to provide consistency across applications and reduce overall development effort.

As a practical example, application ‘X’ - which uses library ‘Y’ - needs a new feature that requires a new component. The new component will belong to the library, not the application. In order to develop the component, it needs to exist alongside the application to ensure that it meets the requirements of the application’s new feature. Another use case is around fixing bugs or adding features to existing library components.

Waiting for the component to be available in the library before proceeding with work on the new application feature is highly impractical and takes too long (just not very “agile” at all). There is also a lot of back and forth that would be required until it is right, so that would also consume a lot of extra cycles if they were separate development efforts.

Migrating the component out of the application into the library once the feature and component are completed, is again extra work (changing the application configuration to pull the component from the library dependency instead of from itself). This requires extra testing. And because this is at the end of the development cycle for the application’s needs (story has been completed), in reality it never gets done because the application team doesn’t have as great a stake in the library as it does in the application (they “own” the application but not the library). So the new component lives on in the application and never gets shared via the library, defeating the purpose of the shared library entirely.

A simultaneous development model is the only good solution, so enabling developers to be able to seamlessly work on the two projects at the same time and see local changes to the library component while working on their application is essential.

This is really easy to set up via TypeScript Module Resolution using a paths setting under compilerOptions in the application project’s tsconfig, but Angular CLI’s compiler does not work properly with it, whereas the TypeScript compiler works fine.

Please reconsider making this a priority.

@filipesilva I would agree on the scarcity and I do think it is something @angular/cli can help with at some point. I would imagine that your base app would be generated by ng new <app-name> but inside of that generated app you could run ng g library or outside of an app maybe ng new <library-name> --library. Thanks for taking a quick look at it!

As far as I can tell, this is a different problem than the enhanced-resolve thing. This has to do with what the app root actually means, and how things are resolved relative to it.

AOT generates imports and whatnot, and because of the process paths the things it’s trying to import don’t really make sense because they are outside the project root:

ERROR in ~/.../new-app/src/$$_gendir/app/app.component.ngfactory.ts (11,21): Cannot find module '../../../lib/component-b.component.ngfactory'.

ERROR in ~/.../new-app/src/$$_gendir/lib/module-a.module.ngfactory.ts (10,21): Cannot find module './module-a.module'.

This is a bug and the fix might be a trivial fix, or it might not not.

But it’s not something we consider very high priority to fix right now since the usecase (pretend library) is sort of a hack. Your app shouldn’t be building an external library, but rather consuming it.

There’s some guidance about linking libraries in https://github.com/angular/angular-cli/wiki/stories-linked-library but I understand that overall library guidance is scarce.