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

Angular2 AOT compilation - "Cannot determine the module for class (... many components which are unused)" #13590

Closed
swimmadude66 opened this issue Dec 20, 2016 · 185 comments
Labels
area: compiler Issues related to `ngc`, Angular's template compiler core: NgModule freq1: low hotlist: error messages P5 The team acknowledges the request but does not plan to address it, it remains open for discussion type: confusing
Projects
Milestone

Comments

@swimmadude66
Copy link

[ x ] bug report => search github for a similar issue or PR before submitting
[ ] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior
Unused components/pipes/directives in my workspace are detected by the compiler, which throws the error Cannot determine the module for class (...) for each file. It stops compilation, and does not seem be configurable. This is a problem, since I need to have those files in my workspace, but do not need them in the resultant app (partner implementations requiring different combos of shared components). This is especially frustrating with regards to compiling in a webpack loader, which should be able to provide a list of files which are included, regardless of workspace.

Expected behavior
I would expect these errors to be warnings, and/or able to be silenced by a compiler option. Alternatively, with regards to webpack, you could allow a list of files to be inserted, so that a webpack could provide all files in the require chain, instead of all files in the workspace.

Minimal reproduction of the problem with instructions
Cannot demo in plunkr, since it uses JIT.

  1. Create a basic angular app which bootstraps an ngModule with one component, AppComponent
  2. Get this app into a state which can be AOT compiled (should be pretty easy with a hello world)
  3. Add a component to the directory structure, but do not reference it anywhere in your code.
  4. Try to AOT compile again. You will get the warning Cannot determine the module for class

What is the motivation / use case for changing the behavior?
My company has a base app for ourselves, and our partners use modified versions of that app as their own. Rather than maintain all partners separately, we use a shared library of common generic components, imported as needed. For our base app, everything is fine, since we use every component. For partners, we cannot use AOT, since some of the components in the shared npm package do not have a declared module.

Please tell us about your environment:
Happens across all devices, but the current testing setup is:
WIndows 10
VS Code
Cmder (bash terminal)

  • Angular version:
    v2.1.0 (though we have also tested in 2.3.1

  • Browser: All - this is a compiler issue, not browser specific

  • Language: Typescript

  • Node (for AoT issues): node v6.3.0

@swimmadude66 swimmadude66 changed the title ngular2 AOT compilation - "Cannot determine the module for class (... many components which are unused)" Angular2 AOT compilation - "Cannot determine the module for class (... many components which are unused)" Dec 20, 2016
@DzmitryShylovich
Copy link
Contributor

Cannot determine the module for class

Component must be a part of a module. It's intended.
I would say it's a feature request.

@swimmadude66
Copy link
Author

swimmadude66 commented Dec 20, 2016

@DzmitryShylovich It makes sense for components you are USING to need to be part of a module. But as far as the compiler is concerned, these files should not matter. They are unused, un-referenced .ts files that happen to contain components.

@DzmitryShylovich
Copy link
Contributor

@swimmadude66 u can exclude unused files in tsconfig

@swimmadude66
Copy link
Author

@DzmitryShylovich you can, but barrel files complicate that. If a class is re-exported in a barrel file, I have to ignore the barrel file as well, which can cause problems with the classes I DO need from that barrel.

It seems weird that the compiler should try to compile any more than it absolutely has to. Tree-shaking aside, I'd expect the compiler to want to only deal with files given to it or explicitly linked to given files.

@DzmitryShylovich
Copy link
Contributor

It seems weird that the compiler should try to compile any more than it absolutely has to.

this is how compilers work...

@swimmadude66
Copy link
Author

swimmadude66 commented Dec 20, 2016

The fact that JIT compilation works should be pretty good evidence that the compiler does not NEED these files. It is throwing an error instead of a warning on files which could be excluded with no harm done.

you can talk down to me about how compilers work all day, but this is a real issue in a somewhat basic use case. I am only asking for some way to at least suppress the error and continue at my own risk.

My co-worker is attempting to remove any barrel files which could be blocking us from using blanket excludes, but I'd ask you take a look at the issue I originally opened against @ngtools/webpack, where another user had the same error from a component only referenced in their tests. angular/angular-cli#3636

The compiler is aware of files which I am not asking it to compile, and it is throwing unrecoverable errors for recoverable situations. plain as that.

@vicb vicb added the area: core Issues related to the framework runtime label Dec 20, 2016
@Toxicable
Copy link

I don't understand why you have components in the same directory that aren't included in the project.
I do think this could be a feature request but currently this is how the compiler works, also saying "it works in JIT" is not reason enough to think it should work in AOT.
What I think you need to do is separate out these files from your base app into modules then import them through some sort of package manager, this way it solves your problems, tidies up your directory and makes it easier to maintain from every aspect

@swimmadude66
Copy link
Author

@Toxicable the unused files are in a library-style npm module. Generally the ideal usecase is something like this:

in @project/angular (our shared code npm module) we have all the components, pipes, directives, and services needed to make our base app and communicate with our backend platform. Our partners want an app that looks similar, but uses a different home page, or has some new component added. However, most of the other pages will be the same, and all the services needed to connect to our platform might be the same.

Our approach to maximize reusable code was make each partner create modules, and import a combination of new pieces, and shared pieces from the npm module. My new HomeModule might have an import like:

import {
    OverviewComponent,
    ProfileComponent
} from './components/home';

import {
    AuthComponent
} from '@project/angular';

this then blows up in AOT saying: Cannot determine the module for class OverviewComponent since we are not using the OverviewComponent from @project/angular.

Since literally no files point at @project/angular/components/home/overview/component.ts I wouldn't expect the compiler to care at all that it is unused. but since the file exists inside the node_modules dir of our project, the compiler finds it, complains, and dies.

As for the JIT works !== AOT works, the base app works with AOT, and the changes in the Proof of Concept partner are incredibly small. I do not mean to imply that everything that works in JIT should work in AOT, but I have very good reason to believe that this should.

@kirjai
Copy link
Contributor

kirjai commented Dec 22, 2016

I have another example where this current behaviour doesn't make sense - tests.
Say, I have a feature directory some-feature, with some-feature.component.ts and some-feature.component.spec.ts.
This component uses content projection, so I would like to test that functionality by creating a test component inside my some-feature.component.spec.ts file which uses some-feature component in the view, like so:

@Component({
  selector: 'test-app',
  template: `<some-feature><p>projected content</p></some-feature>`
})
export class TestAppComponent {}

I then use this component in my testing module:

  ...
  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [TestAppComponent, SomeFeatureComponent]
    })
  })
  ...

Everything's valid and great, until when i run ng build --prod --aot using angular-cli, which throws:
Cannot determine the module for class TestAppComponent.

I don't think AOT should be concerned with .spec files..

@tbosch
Copy link
Contributor

tbosch commented Dec 27, 2016

The general rule is: ngc will compile everything that tsc compiles. And it will try to compile every component that it finds. However, Angular can't compile components without a related module.

We could change this error into a warning though.

@mmc41
Copy link

mmc41 commented Dec 28, 2016

I also have test wrapper components that are used for testing only and that causes AOT to blow up as described here. Would be nice if AOT could ignore all components that satisfies a wildcard expression like TestComponent* etc.

@swimmadude66
Copy link
Author

Alright, so more info here. We do seem to have found a workaround for our case (doesn't fix the testWrapper case). It seems the real issue in our case was our barrel files. When importing ANYTHING from a barrel file, it follows the chain of all things re-emitted by the barrels. Since we were pulling in our components from a top level PROJECT/components, it was analyzing ALL components instead of just the one we wanted. This would still probably be better as a warning, but I can understand a little better why the compiler cared about those components.

@born2net
Copy link

born2net commented Jan 15, 2017

seeing same error:

$ ./node_modules/.bin/ng-xi18n
Error: Cannot determine the module for class DividerPanel in C:/msweb/studiotouch/src/comps/dividerpanel/DividerPanel.ts!
Cannot determine the module for class EntryPanel in C:/msweb/studiotouch/src/comps/entry/EntryPanel.ts!
Cannot determine the module for class ForgotPass in C:/msweb/studiotouch/src/comps/entry/ForgotPass.ts!
Cannot determine the module for class Footer in C:/msweb/studiotouch/src/comps/footer/Footer.ts!
Cannot determine the module for class Infobox in C:/msweb/studiotouch/src/comps/infobox/Infobox.ts!
Cannot determine the module for class InputNumeric in C:/msweb/studiotouch/src/comps/inputnumeric/InputNumeric.ts!
Cannot determine the module for class InputString in C:/msweb/studiotouch/src/comps/inputstring/InputString.ts!
Cannot determine the module for class Loading in C:/msweb/studiotouch/src/comps/loading/Loading.ts!
Cannot determine the module for class MapAddress in C:/msweb/studiotouch/src/comps/mapaddress/MapAddress.ts!
Cannot determine the module for class Minitab in C:/msweb/studiotouch/src/comps/minitabs/MiniTab.ts!
Cannot determine the module for class Minitabs in C:/msweb/studiotouch/src/comps/minitabs/MiniTabs.ts!
Cannot determine the module for class ModalDialog in C:/msweb/studiotouch/src/comps/modaldialog/ModalDialog.ts!
Cannot determine the module for class Ng2Highcharts in C:/msweb/studiotouch/src/comps/ng2-highcharts/src/directives/ng2-highcharts.ts!

Cannot determine the module for class Ng2Highstocks in C:/msweb/studiotouch/src/comps/ng2-highcharts/src/directives/ng2-highstocks.ts!

Cannot determine the module for class Ng2Highmaps in C:/msweb/studiotouch/src/comps/ng2-highcharts/src/directives/ng2-highmaps.ts!
Cannot determine the module for class simplelistEditable in C:/msweb/studiotouch/src/comps/simplelist/simplelistEditable.ts!
Cannot determine the module for class simplelist in C:/msweb/studiotouch/src/comps/simplelist/simplelist.ts!
Cannot determine the module for class FilterPipe in C:/msweb/studiotouch/src/pipes/FilterPipe.ts!
Cannot determine the module for class FilterPipeEqual in C:/msweb/studiotouch/src/pipes/FilterPipeNot.ts!
Cannot determine the module for class OrderBy in C:/msweb/studiotouch/src/pipes/OrderBy.ts!
Cannot determine the module for class ReplacePipe in C:/msweb/studiotouch/src/pipes/ReplacePipe.ts!
Cannot determine the module for class SortBy in C:/msweb/studiotouch/src/pipes/SortBy.ts!
    at analyzeAndValidateNgModules (C:\msweb\studiotouch\node_modules\@angular\compiler\bundles\compiler.umd.js:24878:17)
    at Extractor.extract (C:\msweb\studiotouch\node_modules\@angular\compiler\bundles\compiler.umd.js:27727:20)
    at Extractor.extractBundle (C:\msweb\studiotouch\node_modules\@angular\compiler-cli\src\extractor.js:40:33)
    at Extractor.extract (C:\msweb\studiotouch\node_modules\@angular\compiler-cli\src\extractor.js:30:34)
    at extract (C:\msweb\studiotouch\node_modules\@angular\compiler-cli\src\extract_i18n.js:7:67)
    at Object.main (C:\msweb\studiotouch\node_modules\@angular\tsc-wrapped\src\main.js:47:16)
    at Object.<anonymous> (C:\msweb\studiotouch\node_modules\@angular\compiler-cli\src\extract_i18n.js:14:9)
    at Module._compile (module.js:556:32)
    at Object.Module._extensions..js (module.js:565:10)
    at Module.load (module.js:473:32)
Extraction failed

root@DESKTOP-VEUHFOL /cygdrive/c/msweb/studiotouch

Angular 2 Kitchen sink: http://ng2.javascriptninja.io
and source@ https://github.com/born2net/Angular-kitchen-sink
Regards,

Sean

@born2net
Copy link

i18 should not walk the entire project structure and instead look at the declarations used.

@born2net
Copy link

born2net commented Jan 15, 2017

I tried to clean the directives that are not being used, and the rabbit hole just got deeper:

$ ./node_modules/.bin/ng-xi18n
Error: Error at C:/msweb/ng-skeleton/node_modules/typescript/lib/lib.d.ts:18770:11: Duplicate identifier 'ActiveXObject'.
Error at C:/msweb/ng-skeleton/node_modules/typescript/lib/lib.d.ts:18773:13: Duplicate identifier 'ActiveXObject'.
Error at C:/msweb/ng-skeleton/e2e/app.po.ts:1:38: Cannot find module 'protractor'.
Error at C:/msweb/ng-skeleton/node_modules/@angular/core/src/facade/lang.d.ts:12:17: Cannot find name 'Map'.
Error at C:/msweb/ng-skeleton/node_modules/@angular/core/src/facade/lang.d.ts:13:17: Cannot find name 'Set'.
Error at C:/msweb/ng-skeleton/node_modules/@angular/core/src/application_init.d.ts:16:18: Cannot find name 'Promise'.
Error at C:/msweb/ng-skeleton/node_modules/rxjs/Observable.d.ts:68:60: Cannot find name 'Promise'.
Error at C:/msweb/ng-skeleton/node_modules/rxjs/Observable.d.ts:68:70: Cannot find name 'Promise'.
Error at C:/msweb/ng-skeleton/node_modules/@angular/core/src/linker/compiler.d.ts:53:49: Cannot find name 'Promise'.
Error at C:/msweb/ng-skeleton/node_modules/@angular/core/src/linker/compiler.d.ts:61:65: Cannot find name 'Promise'.
Error at C:/msweb/ng-skeleton/node_modules/@angular/core/src/application_ref.d.ts:116:67: Cannot find name 'Promise'.
Error at C:/msweb/ng-skeleton/node_modules/@angular/core/src/application_ref.d.ts:132:101: Cannot find name 'Promise'.
Error at C:/msweb/ng-skeleton/node_modules/@angular/core/src/application_ref.d.ts:158:67: Cannot find name 'Promise'.
Error at C:/msweb/ng-skeleton/node_modules/@angular/core/src/application_ref.d.ts:160:101: Cannot find name 'Promise'.

let me add that all is working fine with angular-cli and angular-compiler, so its just i18 that is not liking my project.

Reference (nice and clean setup): https://github.com/born2net/ng-skeleton

Regards,

Sean

@klihelp
Copy link

klihelp commented Feb 1, 2017

Same error for commented out components .. unused and commented out components for development stage are just useful step to not process

// two comments aot read:

//  {path: 'about', component: AboutComponent 
// import { AboutComponent } from './about';

@DzmitryShylovich
Copy link
Contributor

@kirjai ngc compiles all files inside a source directory. it doesn't matter if you are import it or not.

@kirjai
Copy link
Contributor

kirjai commented Feb 1, 2017

@DzmitryShylovich which I understand, I'm just saying that I don't think that the AOT should care about this file at all in this case.. in my mind, skipping .spec files during AOT compilation is the way to go. But clearly there's something that's stopping the team from doing that, I understand.

As an alternative, then maybe the putting .spec files in the same directory as the entity the tests are written for should not be suggested by the style guide?

@gestj
Copy link

gestj commented Feb 14, 2017

I also run into this error message (and some others) because of our test code and AOT in combination.

I can recommend this article. It explains how certain error messages can be provoked and how to fix/workaround them.

Considering that people will stumble into this exact issue if they follow the official testing guide and build their app with AOT, you may want to update the guide?

(If someone looks for an answer regarding RouterLinkStubDirective)
You can fix it by adding a "dummy" module:

/**
 * Needed so that `aot` build is working. But it isn't used throughout our tests and/or app.
 */
@NgModule({
    imports: [
        AppModule
    ],
    declarations: [
        RouterLinkStubDirective,
        RouterOutletStubComponent
    ]
})
export class FakeRouterModule {
}

@0xphilipp
Copy link

0xphilipp commented Feb 15, 2017

By the way, it also tries to determine the module for abstract classes:
export abstract class AsyncTransform extends AsyncPipe implements PipeTransform { ... }

"Error: Cannot determine the module for class AsyncTransform"

Adding it to a module also doesn't work 😄.
"Cannot assign an abstract constructor type to a non-abstract constructor type."

@mlakmal
Copy link

mlakmal commented Feb 17, 2017

this is happening for some classes as well.

Err: Cannot determine the module for class AppGlobalModalComponent

export class CustomGlobalModalComponent extends AppGlobalModalComponent {}

@maxime1992
Copy link

Having an issue to maintain compatibility for a library built with ng 9 that I wanted people running ng 8 to be able to use.

I offer through the library some utility classes that your components can extend upon. In that chain of parent classes some of these are abstract and with ng 9 need to have a @Directive({ selector: 'random' }) to be compatible with ng 8.

So I nearly got away with it... BUT:

Cannot determine the module for class NgxSubFormComponent in /........./node_modules/ngx-sub-form/ngx-sub-form.d.ts! Add NgxSubFormComponent to the NgModule to fix it.`

I do not expose any module within the library, people are just supposed to extends on the classes we offer. But I don't want them to have to imports the parent classes in their modules (wouldn't make any sense).

So I'm stuck and I'll just cut a breaking change release requiring people to upgrade to ng 9 instead of having backward compat

@pkozlowski-opensource pkozlowski-opensource added core: NgModule area: compiler Issues related to `ngc`, Angular's template compiler labels Mar 10, 2020
@JoostK JoostK removed area: core Issues related to the framework runtime area: compiler Issues related to `ngc`, Angular's template compiler labels May 28, 2020
@ngbot ngbot bot removed this from the Backlog milestone May 28, 2020
@JoostK JoostK added the area: compiler Issues related to `ngc`, Angular's template compiler label May 28, 2020
@ngbot ngbot bot added this to the Backlog milestone May 28, 2020
@ngbot ngbot bot modified the milestones: Backlog, needsTriage May 28, 2020
@alxhub
Copy link
Member

alxhub commented May 28, 2020

Hey all, sorry for the silence on this issue.

ngc in View Engine, by design, generates ngfactory files for every component in the "compilation" - that is, the full set of TS files defined by your tsconfig. This is how TypeScript
itself works - it compiles all the files defined by a tsconfig. ngc is just doing extra processing on top of that.

So if an @Component is present in that compilation and ngc can see it (meaning it's top-level and exported), ngc will try to compile it. There is no way around this other than to ensure ngc doesn't compile the file which declares the component in the first place.

The correct way to do that is to scope your tsconfig. For example, you might have a top-level tsconfig for your editor, which includes all files, and then a tsconfig.app.json for your application compilation specifically, which inherits from the editor config and excludes spec files. Only the app config would be compiled with ngc.

Projects (each tsconfig is a "project") should be structured such that a component and its module are always compiled together.

In Ivy the same rules still largely apply, with several small differences:

  • By default Ivy will try to compile any @Component, regardless of whether it's exported.
  • It's no longer an error, in Ivy, to have a component without a module. However, you will still get template type-checking errors if that component tries to use other components/directives in its template, since without a module nothing else will be visible to that component.
  • It's possible to tell the Ivy compiler to ignore a particular component or module and leave it until runtime, by adding a jit: true flag to the decorator.

@swimmadude66
Copy link
Author

@alxhub I'm glad to hear it is no longer an error!

I understand that ngc will try and compile anything that tsc would, and that tsc will compile anything within its scope. That being said, the requirement that a component be added to a module is 100% an angular concern, so removing it from your typescript project scope feels like kicking the can down the road. Additionally, with patterns like barrel files being common, it can be difficult to truly remove a single file from compilation, without ALSO removing it from any re-exports along the way. This is why the calls to simply "exclude those components" above were often met with dissatisfaction.

Regarding the remaining limitation with ivy (the template errors on components without modules), is it too much to ask for those to be warnings as well? I understand that may be more difficult (or impossible) if type-checking occurs before the component is identified as module-less, but it seems to me like a warning along the lines of Warning: ExampleUnusedComponent does not belong to an NgModule, and will be excluded from the output accurately captures the idea that the component (and any others referenced by it) will not be included unless they are added to an ngModule.

Overall, I am very excited to see movement on this issue, and look forward to trying out my original use case with the new changes in the ivy compiler!

@ngbot ngbot bot modified the milestones: needsTriage, Backlog Sep 22, 2020
@jelbourn jelbourn added P4 A relatively minor issue that is not relevant to core functions P5 The team acknowledges the request but does not plan to address it, it remains open for discussion and removed P4 A relatively minor issue that is not relevant to core functions severity2: inconvenient labels Oct 1, 2020
@jessicajaniuk
Copy link
Contributor

Thanks for reporting this issue. This issue is now obsolete due to changes in the recent releases. Please update to the most recent Angular version.

If the problem still exists in your application, please open a new issue and follow the instructions in the issue template.

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Apr 14, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: compiler Issues related to `ngc`, Angular's template compiler core: NgModule freq1: low hotlist: error messages P5 The team acknowledges the request but does not plan to address it, it remains open for discussion type: confusing
Projects
No open projects
Compiler
  
Prio_col2
Development

No branches or pull requests