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

[ 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

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 170
  • Comments: 185 (25 by maintainers)

Commits related to this issue

Most upvoted comments

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

this is how compilers work…

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.

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

Great work done in Angular team so far. We’re heavily using Angular in our projects and after one year of trying to get my head around all of these buzzy Angular2+ things, here is my finding : 1- Angular is massive and slow , you wanna make it fast ? use AOT and LazyLoading and gzip your stuff . 2- You wanna lazyLoad a component ? NOPE , you’d be able to one lazy load a route , so if you’re app is massive but only one page, enjoy 8mg bundle size. 3- You wanna use AOT ?? AOT is buggy and hard to comply with and not use heaps of javascript/es6 features and probably rewrite lot’s of your code. 4- You’re using AOT fine ? Alright , now have a look at your final bundle, it’s now even bigger than @angular/compiler plus your components not AOTed , well done.

5-As part of Angular2+ benefit , you now are eligible to use gzip , just in case you didn’t know how to use it before , now that Angular is massive , you’ll learn it better 😃 So there you go , they’re selling gziping as an option for optimising Angular2 code 😃

@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.

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. https://github.com/angular/angular-cli/issues/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.

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…

@swimmadude66 u can exclude unused files in tsconfig

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.”

@xe4me Please keep discussion in this thread relevant to the issue at hand, rather than just a general rant against the framework.

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 {
}

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.

@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.

I think new ivy renderer will solve quite few issues with AOT.

I love hearing this on every issue.

This issue turns 2 years old today 🎉

Still, not even a word from the core team on why this couldn’t be just a warning.

@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?

@tbosch Any word on changing this to a warning? It seems since my last visit quite a few others have chimed in with their own anecdotes, and I was wondering if you could give us an update.

Thanks!

One year later, and we STILL cannot get this changed to a warning. Ridiculous.

In my opinion, every component should be in its own NgModule. It’s not that scary to create another NgModule for every component. (Like what @angular/material did)

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

This is a basic requirement - leave un-referenced files. This restriction is a problem for larger projects. Please change this to warning.

@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.

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.

Is there a reason we still haven’t fixed this problem?

Is the “Make it a warning” solution not adequate? Can a member of the core team comment on this?

@sarunint In enterprise-sized applications like the one I originally opened this ticket for, that would be hundreds of modules, with very complicated imports in order to handle dependent directives and components. This has a very simple fix: if the compiler cannot find a module for a component, throw a warning and get rid of it in tree-shaking.

The real reason this is so annoying is the fact that barrel files become more of a hazard than a boon. It’s convenient to centralize your imports, but not if you are committing to including every single exported component in every app which uses your library.

Hey guys, just popping back in for a belated 2-year issue age celebration. There is a lot of cross-talk about possible causes for people, including the include and exclude sections of tsconfig.json, so I just want to clear up what the root issue is for this thread.

If you create a library of reusable components, and add barrel files (index.ts) for easier imports, then then Angular compiler will break when you use any of them. This is because as soon as you import anything from that barrelfile (say, @shared/components), it will attempt to find the module for EVERY SINGLE COMPONENT. Even if you specify the specific components you want (e.g. import { SharedToastComponent } from '@shared/components';), the compiler will still look for the module for each and every exported component, and throw an error (not a warning) about the missing components.

Obviously, there are workarounds.

  1. Stop using barrel files. This gets messy fast, since every component has to be fully specified
  2. Use an ExtrasModule and don’t use the module anywhere. This works well for people who have components only needed for testing, but not so well for people looking to pull from a component library

However, since using either of these workarounds results in the exact same output code after tree-shaking, its obvious that the other components don’t NEED to be included in a used module. All I’m asking for with this 2-year old issue is to turn that error in to a more appropriate warning. Makes sense, since the compilation would still succeed if allowed to continue.

Here’s hoping that in 2019 the Angular team will do something about this very old, very annoying issue. 🤞

This is so strange & embarrassing that this issue was opened in Dec. 2016 and still has this issue. I converted the structure of my whole app to use aot compilation. I have 4 modules which are lazy loaded and over 60 components. The compiler only complains for only couple of components (as per what the error suggests) which I’m sure are already part of declarations of one of the lazy loaded modules which is kind of strange to me.

Its even giving errors on components which are already part of some module.

Having an error on every component stub is the most annoying thing. Everyone is testing/writing specs right? So the component stub errors should be effecting everyone. I’m not saying the barrel issue is less annoying. I’m just saying not everyone is doing the barrels. But everyone should be doing specs. So the specs issue should be the greatest motivation for the core team to fix this. It is currently labeled “Frequency Low” but should be “Frequency High” because anyone doing unit testing will hit this issue.

remove the “export” so ts will not take it to the build.

This is really ridiculous. We have a shared NPM module exporting couple of pipes/directives and unless you import ALL it fails with this dumb error. It should really be changed to a warning and it should not break compile.

What I did was this:

I have saved all my stub / mock components with an .mock.ts extension and updated the tsconfig.app.json “exclude” array like so:

...
  "exclude": [
    "test.ts",
    "**/*.spec.ts",
    "**/*.mock.ts"
  ]
...

AOT now skips compilation of these files

I just ran into this also, so damn frustrating.

Facing the same issue with the exact same use case - we use Rollup, so unused components don’t even make it to the ultimate bundle.

Please do suppress this! It’s a big nuisance, and is stopping work.

I am facing exactly the same issue as you do, @swimmadude66 . The fact that this is not a suppressable warning is ridiculous.

Dear Angular team, is there anything you can do about this?

@KarthikSamyak this issue is not about people who have classes which SHOULD be in a module. This is about those of us with component libraries, which are purposefully excluded from all modules. They are unused code which should be ignored by the compiler. Instead, the compiler throws an unrecoverable error when it discovers them.

It really should be a simple change to make this error in to a warning, but for some reason it has taken OVER A YEAR and has been recently moved from pri_col1 to pri_col2 on the roadmap.

I am growing increasingly frustrated with the Angular team for their complete lack of response to this issue. Our company eventually gave up on using component libraries entirely, opting to instead copy files in manually. This is far from ideal, as we now have issues with nearly-identical, but un-shared, components.

Angular team, if you even read this issue any more, please just add a compiler setting for “ignoreUnusedComponents” and let us get on with using this framework.

@jabaa remove @Component annotation from your abstract class

accidentally clicked the wrong button, sorry!

I wonder if the “freq1: low” tag on this issue is due to the fact that AoT is such a spectacular pain in the ass to get working, that people aren’t even bothering with it? It’s a bit unbelievable that such a simple but painful issue has received essentially zero feedback from core Angular contributors.

Anyways, there is a way of excluding files that hasn’t specifically been mentioned. If you have a naming pattern (e.g. .spec.ts, .abstract.ts, .stfu-aot.ts), you can create a separate tsconfig.json file for AoT and use that instead: ngc -p tsconfig-aot.json. In this file you can use the "exclude": ["./app/**/*.stfu-aot.ts"] to exclude the files. It’s annoying, but it should work.

Edit: The above doesn’t seem to work with abstract classes that inherit from a component. Yay 😦

@DzmitryShylovich When I remove @Component the constructor isn’t inherited. I have to inject all injectables into all components instead of the abstract component. That’s redundant code. When I change the constructor and the services of the abstract class, I have to adept all child components.

Currently we work around this by implementing all abstract methods with dummy methods and creating a dummy module for all abstract components. But then someone could forget to override the dummy method. That’s all just a work around.

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';

I’m also having this issue with angular 7.2.0 and angular-cli 7.3.0

ERROR in : Cannot determine the module for class ModalMockComponent in /Users/user/repos/angular-skeleton/src/app/shared/modal/modal.component.mock.ts! Add ModalMockComponent to the NgModule to fix it.
Cannot determine the module for class TranslatePipeMock in /Users/user/repos/angular-skeleton/src/app/shared/pipes/translate.pipe.mock.ts! Add TranslatePipeMock to the NgModule to fix it.

E.g. I have a TranslatePipe and a TranslatePipeMock inside my Shared Module. TranslatePipe is included in the module, the mock pipe is not. translate.pipe.mock.ts is for unit testing purpose, so I can just import this file to each unit test.

But now my build fails ng build --prod

How do we fix this?

Atm i have a workaround which solves it

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "outDir": "../out-tsc/app",
    "types": []
  },
  "exclude": [
    "test.ts",
    "**/*.spec.ts",
    "**/*.mock.ts"
  ]
}

Over a year after I opened this issue, my new approach is to minimize the use of barrel files. If you use TS paths like ~components/button/component in your imports everywhere, unused imports are ignored. If that unused component is referenced in a barrel file, it will throw.

That’s still not awesome for reusable libraries, since you have to access all library components with longer paths, instead of just importing from that library top level. Why, oh why can this not just be a warning?

We’re doing npm library with common components which supposed to be used not all at once and pissed by that issue during AoT compilation, damn it… Only solution atm is to create kind of UnusedComponentsModule in host project — just ridiculous! Also need NO_ERRORS_SCHEMA or it will swear about other components which can be used inside your unused components and if you will declare them, then you hit another issue where you can’t declare same component in two modules (related to #10646).

My current module:

import {NgModule, NO_ERRORS_SCHEMA} from '@angular/core';
import {CommonModule} from '@angular/common';
import {ReceiverPageComponent} from 'cb-web-platform';

@NgModule({
  imports: [
    CommonModule,
  ],
  declarations: [
    ReceiverPageComponent
  ],
  schemas: [
    NO_ERRORS_SCHEMA // IMPORTANT: need that for AoT compilation
  ]
})
export class UnusedComponentsModule {
}

Started running into this same issue recently myself.

+1 for changing error to warning.

Seems like an easy fix. Why the delay?

@gestj as @Phmager pointed out, dummy modules are not a fix for all cases. Additionally, they are a pretty janky fix, since you end up compiling code you don’t want or need.

For our case, we have patched the issue in a different way. We moved our shared components to an npm lib, and ignored node_modules in our tsConfig. I mentioned up above that that still doesn’t work, but only because we were using barrel files. If you point directly at each class you need inside node_modules, it will ignore the others. However, as soon as you point to a barrel, it throws the error for the unused files in the same barrel.

This is not ideal, since it kills all our wonderful barrel files, but at least its predictable.

Still hoping for this error to be demoted to a warning

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

This really should be a warning. I am trying to share a codebase as well and am running into this issue of components not being a part of an ngmodule. The problem here is exactly on par with an unused variable which is not an error; at most its a warning.

@UtopianStorm that is not a solution. It’s been mentioned above that having an “UnusedModule” is not only difficult to scale, but also creates an entire module of source files that should not be included in the distribution bundle.

Honestly, this is ridiculous, I really have nothing else to say about this other than the fact that I’m many hours in and yet still unable to get something that works under JIT working with AOT (I should be clear this is only one of about a half dozen issues so far).

I’ve got the same problem, and I’ve found a solution, maybe it works also by you.

My scenario was the following:

I have a MapPipes.ts, containing two Pipes.

One of the Pipes was used in my module, the other wasn’t. Thus, I didn’t register the second in the “declaration:” part of my @NgModule decorator. The problem happened to this second.

I registered also this (despite it wasn’t used), and now it works.

My suggestion is to change the angular compiler on a way that it tries to find modules only for the really used angular entities.

@rrmayer the status of this issue is most likely just “wait for ivy”, as with almost all “current” issues.

@tiagodws lol how long have you been waiting to post this? Did you set a reminder?

Workaround that I am using for my shared library of components/directives/pipes: create a simple dummy module that references all the class declarations. You don’t need to use this module in your real apps, it just has to exist on disk. Ex:

import { NgModule } from '@angular/core';

@NgModule({
    imports: [],
    declarations: [
        MyComponent,
        MyPipe,
        MyDirective,
        ...
    ],
})
export class DummyModule {
}

Oh boy! So much for an enterprise framework…

Please fix this. Make it into a warning! My general experience with the angular 5 aot build is less than stellar.

We have the same problem with inheritance and abstract classes and we didn’t find any solution. We have some components that extend an abstract component. In JIT everything works great but in AOT the modules for abstract components can’t be found and it’s not possible to declare abstract components in a module.

Currently we don’t have any solution other than avoid oop design patterns and use redundant code.

We just want this to be changed from “error” to “warning”, so our projects will actually compile… Is that so hard?

Another potential use case:

Environment specific components. I strip them for production.

const environmentComponents = production ? [] : [
  DevOnlyComponent,
];

@NgModule({
  declarations: [
    ...environmentComponents,
  ],
})
export class ExampleModule {
}

I managed to get everything else working with environment specific components and this is what stops me. 😪

@anjmao that’s incredibly similar to the reason I opened this in the first place. As a workaround for a small library, we found that this error doesn’t occur if you do not use barrel (index.ts) files. It seems that if you import 1 component/directive from an index, it tries to find a module for all the other exports of the index file.

Obviously I still believe this should be a warning, since barrel files are super convenient. However if your use case can’t wait another year for this fix, you may be able to get around it like that.

The angular compiler will use the output from tsconfig, so change tsconfig.app.json to exclude those files that you dont want includes e.g "exclude": [ "test.ts", "**/*.spec.ts", "**/*.mock.component.ts", ]

Ich kehre zurück am 01.11.2017.

Ich werde Ihre Nachricht nach meiner Rückkehr beantworten. In dringenden Fällen senden Sie bitte eine Kopie Ihrer E-Mail für technische Angelegenheiten an entwicklung@arxes-tolina.de, ansonsten an info@arxes-tolina.de. Ein anderer Mitarbeiter wird sich dann Ihrer E-Mail annehmen.

Hinweis: Dies ist eine automatische Antwort auf Ihre Nachricht “Re: [angular/angular] Angular2 AOT compilation - “Cannot determine the module for class (… many components which are unused)” (#13590)” gesendet am 23.10.2017 08:13:17.

Diese ist die einzige Benachrichtigung, die Sie empfangen werden, während diese Person abwesend ist.

Can anyone explain why it can’t be a warning instead of an error?

In my case, I just want to extract langs preferably from components that are connected to ngModule and just ignore those who aren’t. I have one main app folder with base components and application specific folders that sometimes override components from the main app when I try to extract such overridden component with xi18n it throws Cannot determine the module for class... error that in my mind could be just ignored and extraction could be continued without using that unused component.

One thing that I can think of that could be a problem is that I still use that class that is defined in that faulty component file as a base for creating the overridden component so it needs to be compiled but I just don’t need that component annotation cause as you cant use it in overrode component. At least I think you can’t because I have to recreate those annotations in derived components to make them work.

Maybe we should at least have the option to show either an error or a warning. Builds breaking because of components not being a part of a module on purpose is pretty annoying.

Here’s what solved the problem for me. My structure is as follows:

├── icons.module.ts
├── index.ts
├── icon
│   ├── icon.component.html
│   ├── icon.component.ts
│   └── icon.module.ts
└── icon-indicator
    ├── icon-indicator.component.html
    ├── icon-indicator.component.ts
    └── icon-indicator.module.ts

In icons.module.ts I had:

import { IconComponent } from './icon/icon.component';
import { IconIndicatorComponent } from './icon-indicator/icon-indicator.component';

export { IconIndicatorComponent, IconComponent };

@NgModule({
  /* this was all fine */ 
})
export class IconsModule {}

The index.ts had:

export * from './icons.module';

I am assuming the problem was that the compiler hadn’t parsed the icon and icon-indicator modules before encountering the corresponding components and thus threw the Cannot determine the module for class error. This project is ng 5. I only got the error when consuming it in an ng 7 project.

The solution is to move the exports one level down. So I removed the export statement in icons.module.ts and moved them to:

icon.module.ts:
export { IconComponent };
...

icon-indicator.module.ts:
export { IconIndicatorComponent };
...

and adjusted index.ts:

export * from './icons.module';
export * from './icon/icon.module';
export * from './icon-indicator/icon-indicator.module';

Hope this helps someone.

Similar to @yuezhizizhang, I still have this problem, even after adding a glob for mocks to the tsconfig.app.json exclude path.

It’s verging on infuriating that this hasn’t been resolved almost two years after the initial ticket; this is a common use-case which is BREAKING builds. When writing tests, the developer should ideally create mocks for pipes, services and so on unless. If you choose to do this, your build breaks. If not, then your spec files reference the real components/pipes/etc which leads to very tightly coupled tests.

Could someone from the Angular team please suggest how to get around this? Is creating a catch-all dummy module for mocks really the only option? That’s a poor hack, rather than something we should accept as best practice.

This should probably by severity3: broken. For those of us with multiple build targets and polymorphic dependencies (many use cases of which have been present above) this issue prevents builds from working without a crazy complicated built setup.

This is really annoying 😦

Its worth noting that our shared codebase is quite large, so an unused module would be by far the biggest module in the app. Our situation is not 100% typical, but still within the bounds of reason to be supported i believe.

Honestly after 5 months of watching this ticket go nowhere, we are looking at other options, including just killing our shared code repo

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.

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

Seems like the ‘export’ keyword next to the class determines in some way that the class which has the @Component decorator applied to, has to be included in some module. Whether you import the component’s class and declare directly in tests or declare in e.g. CoreMocksModule and then import the module, it all doesn’t matter.

There is a workaround though! Let’s say that we have our stub component declared like this:

@Component({
  selector: 'app-user-stub-component',
  template: ''
})
export class UserStubComponent {}

We have to remove the ‘export’ keyword but in some way we still have to export the component to use it in test/test module. Do it like this:

@Component({
  selector: 'app-user-stub-component',
  template: ''
})
class UserStubComponent {}

export default UserStubComponent;

Now, everywhere when you import the stub, we have to avoid brackets like follows: import UserStubComponent from 'path'

Hope it works. For me it solves AOT compilation problems.

Even with the workaround of creating a mock module, now the “ng build” will fail if a UI unit test writer forgets to add a single mock component to a mock module.

Above means that UI unit testing is currently a risk element for the CI/CD deployment pipeline!

Do we really want to have to explain to people higher in the food chain that Angular unit testers can break entire deployment builds with a single trivial omission? Nope. People that manage Angular developers and who make decisions on which framework to use for the next project should not be wondering about this sort of thing.

As a stopgap: why not just make the error that appears in “ng build” more informative for test writers: i.e. add additional text to the effect of “if this is a mock component used in testing: add it to a mock module in that file as per (this url)”

How your average friendly Angular dev writing unit tests got here:

a) IMHO it is a DRY-code practice to create a “helper” file to place mock components, services, etc. that all .spec files use. However this practice runs the tester smack into this bug scenario.

b) All the .spec files can import this “helper” file without any need of a “mock module” that has no relevance to actual test code. Mock components are useful as-is, without membership in a mock module that has no role in a spec’s test environment at all.

Sure the workaround is trivial…but just finding this bug via google search, reading and digesting the thread and helpful workarounds was not.

I just add this to the end of helper file which contains the mock components (among other things):

/*
  mock components
  !! Each mock component added to this file *must* be added to the "MockTestModule", see below !!
*/
@Component({
  selector: 'app-mock-component',
  template: '<span>mock widget</span>'
})
export class MockComponent {}

/*
  This is an unused module to resolve the ng build error:
    'ERROR in : Cannot determine the module for class MockComponent
    in C:/code/myRepo/src/assets/test/test-resources.ts!
    Add MockComponent to the NgModule to fix it.'

  Reference: https://github.com/angular/issues/13590

  Each mock component added to this file *must* be added to the "MockTestModule"
*/
@NgModule({
  declarations: [ MockComponent ],
})
export class MockTestModule {}

Note that the above file in reality is quite large…the “MockTestModule” is buried way down at the end of the helper file…

…I hope we don’t have to write a package.json entry for “ng build” that intercepts the call, and inserts a console message “hey UI unit testers, did you add your mock component to the mock module?” nudge.

Also of note is that when I run this application in ng serve, the component works fine.

Our use-case is the same. We want to have a library of modules/components to create apps really easy and have the ability to replace/extend odd ones when needed.

We’re having issues the other way as well: If we want to replace 1x component inside a module, we create a new module and import the components we still want in there:

import { HeaderComponent, SidebarComponent } from '@mylibs/layout';
import { FooterComponent } from './footer.component';

@NgModule({ declarations: [ HeaderComponent, SidebarComponent, FooterComponent ] })
export class MyLayoutModule { }

@NgModule({ imports: [ MyLayoutModule ] })
export class AppModule { }

The error is: `HeaderComponent is declared in 2 modules: lib/module.ts and app/module.ts Being a warning instead would at least allow us to move forward 😦

Just realised - Happy Birthday to this issue 😃

How is this still not fixed almost 1 year later? Pulling my hair out to get AOT to work, but this issue makes me hit a brick wall.

you don’t need to add @Component() decorator to any service. Only @Injectable()

@bigjetplane Here is plunker with the problem. There is an abstract class with decorator and everything works in jit. When you remove the decorator from the abstract class, the app can’t render because not all dependencies could be loaded: Can't resolve all parameters for App: (?).

That’s our use case. We have an abstract class with constructor und injects. We only want to override some abstract methods in the child components

The given example does not work in aot. The difference between aot and jit is a big problem for us. We are developing with jit. The production build is with aot. So we are developing a week with jit without errors and warnings and after the week we want a production build and get many errors from nowhere. I prefer a switch for jit, where I can enable aot errors. A jit build needs 10-20 seconds. An aot build needs 25 minutes.

@jabaa what version do you use? constructor should be inherited regardless of @Component.

Unless I import an exported asset, why does Angular care if the file exists?

More often than not I’m refactoring a component or removing components and just want to comment out the stale component references like I did back in the CommonJS days. This would be plenty safe enough since I never use barrel files cuz I’ve been burned too many times on that front, and having Angular force me to commit to physically removing the assets from my project is just unacceptable when I’m neck deep in a prototyping phase…

I’m stuck in Angular 6 because I’m the only developer working on this project and it would be hell to refactor things to get up to the latest stable tools in the midst of having to ship new features… so at the very least it would be great if Angular 6+ could be patched with a compiler flag for disabling this behavior…

In my case, I fixed it for my unit tests, where I mocked the translate pipe.

I changed the export class to default export:

import { PipeTransform, Pipe } from '@angular/core';

@Pipe({
  name: 'translate'
})
class TranslatePipeMock implements PipeTransform { // <-- NOT: "export class TranslatePipeMock implements PipeTransform"
  public name = 'translate';

  public transform(query: string): any {
    return query;
  }
}

export default TranslatePipeMock;

exclude option doesn’t for a mock pipe:

@Pipe({ name: 'translate' })
export class MockTranslatePipe implements PipeTransform {
    transform(value: string): string {
        //Do stuff here, if you want
        return value;
    }
}

My tsconfig.app.json has excluded this file:

"exclude": [
        "test.ts",
        "**/*.mock.ts",
        "**/*.spec.ts"
    ]

However, when I run ng-xi18n --i18nFormat=xlf2 --outFile=./assets/i18n/messages.xlf, it still complains:

Cannot determine the module for class MockTranslatePipe in src/test/translate.service.mock.ts! Add MockTranslatePipe to the NgModule to fix it.

… the final goal should be using tsconfig.json option "noUnusedLocals": true which eliminates everything what is unused.

Has anyone recently moved from angular 4 to angular 5 and noticed that some of their components which are actually being declared in modules are throwing this error?

@alastair-todd sorry, i lost notification from you question in tons of other notifications. You are correct - Decorator inheritance is not supported.

See answer from @robwormald

Angular’s compiler looks the the file set you’ve handed to tsconfig - so excluding files from there should exclude them from this check.

So you could try to make a conventions for mock file names, like: selection-tool.component.mock.ts. Then exclude via

"exclude": [
    //... other excludes
    "**/*.component.mock.ts"
]

I’m also hoping for a fix of this. Apparently using stub-components for testing is intended by the Angular developers - so excluding them from the build in a clean way also needs to be possible. For now I’m using the work-around suggested by gestj (defining a fake module in which the stub-component is declared).

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.

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.

In my case , I fixed it . Problem was with casing. I used uppercase for folders but the auto import of VS code imported the path with lower case.

Unused imports can cause this error…

import { UnusedImportComponent } from "./used-somewhere-else-or-not.component";

When TypeScript strict, Angular importing, in AOT compilation, not used components from a library and lanching this error… Check non used components / modules has non sense…

image

By deleting the library folder where is the non used module and components, the application launch in strict-aot nicely - but surely don’t make sense to delete this folder manually

Another use case for this:

Typescript allows you to setup multiple resolution paths with the paths variable in tsconfig.json. Ex:

{
    ...
    "paths": {
        "~src/*": ["src/*", "src-gen/*"]
    }
}

Allowing you to import things like this:

import { NgModule } from "@angular/core";
import { ExampleComponent } from "~src/example.component";

@NgModule({
    declarations: [
        ExampleComponent
    ],
})
export class ExampleModule {}

If you are generating a component inside src-gen that the developer can “override” by creating another one inside the src folder, the same error starts happening with the (now unused) component inside src-gen (which shouldn’t need to be deleted, as it could be extended by the overrider).

In my case, things where fine with NG 5, CLI and AOT. But after upgrading to NG 6, I get similar error for an unused component. Actually the component is used through a service injected to the other component. I have many similar components constructed and used the same way. But just one has problem one upgrading to the latest NG CLI 6.0 and 6.0.1

My use case https://github.com/angular/angular/issues/23475 I’m creating validators library and I don’t want to create module for each validator directive, all I want is just be able to bundle validator directives to npm package so user can install and import only validators that he needs to they NgModule. Also I don’t want to create one module for all validators because they all will be bundled in the final bundle which is a huge waste of size.

I’m happy to create PR to fix issue.

Fake components used by tests of many real components should not be in a module; They are just inclued in TestBed of tested components. This error is stupid. Please solve that as it is during from one year.

So with new @angular/cli (1.7.0+) even IgnoreModule somehow does not workaround this problem.

In my opinion, every component should be in its own NgModule

Do you even try to write unit-tests? This bug is making creating test helpers kinda problematic.

@andela-andrewmakenzi this has been suggested before, further up in this gigantic chat (absolutely no shame in not reading through the whole thing). However, issues emerge if you depend on one component in a library that uses barrel files (index.ts). If you import any component from a barrel file, the compiler tries to load all components referenced in that barrel file, and will complain that they are not in a module. This makes it difficult to package re-usable component libraries, which is basically the whole point of component architecture in the first place.

Your solution works great for people who have mock components and are getting this error when they are not running tests. But if your organization (like mine) tried to create a common component library, and pull in just the ones we need for any given project, TSC’s exclude sadly will not help you.

This is taking the size of a history book.

Literally just ran into this issue as well. Compiler tries to pull in a mock that is only used in tests and fails because it’s not part of the main module. If it knows it doesn’t need the file then it should be a warning at most.

I’ve also met the problem. But for me seems I just wrongly put the path of importing dodule

I think it is vital for the Angular developers to ignore these files.

If someone can give me tips on where I can find this code, I will be happy to fix it myself. This is a nuisance that is very annoying. Ran into it again yesterday.

If anybody thinks this is a feature and not bug, how about we have a flag for this?

On Wed, 9 Aug 2017 at 2:40 AM, Leonardo Vidal notifications@github.com wrote:

+8 months and it is still an issue. Same problem here ERROR in Cannot determine the module for class PasoFooterComponent

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/angular/angular/issues/13590#issuecomment-321082545, or mute the thread https://github.com/notifications/unsubscribe-auth/AEM6r7FOTLcicWJN3Oijw2pwKTLGL6cFks5sWM61gaJpZM4LSAwS .

Angular’s compiler looks the the file set you’ve handed to tsconfig - so excluding files from there should exclude them from this check.

Maybe my solution will help somebody: I just created a dummy module which declares all the mock component. This module is not imported by anything - it just keeps the AoT compiler happy. Consequently none of the mock components are part of the compiled code. Not ideal, but problem solved.

I’d love to hear about any better solution.

Ah I see, yes I misread your problem. You aren’t pulling in a pre-compiled shared library via NPM here, you have a local TS library in your project. Do I understand correctly now?

I do agree that it should “just work” and you are correct it definitely sounds like the same problem where it is finding components that have no module in the application. A possible solution would be to use an AoT build specific tsconfig.json that excludes the files and folders that are not needed for the AoT build.

EDIT: it turns out this is a different error, resulting from a bug in ngtools/webpack. That ticket has been opened here: https://github.com/angular/angular-cli/issues/6228


New fun on this front for my company. In a recent attempt to upgrade our systems to v2.4.10, I ended up with a couple dozen errors of this variety:

ModuleNotFoundError: Module not found: Error: Can't resolve '../../../../../../../../$_gendir/src/components/spinner/component.ngfactory'

It seems to be logging for all the components in our shared library that are not being used by the current application. This is eerily similar to the error for which I opened the ticket originally.

I’m not sure what else we can do about this though. We have tried addressing each component we need from the shared lib directly (not using any index.ts files, as those seemed to pull in every file referenced in the index) and moving all the shared lib in to node_modules.

Why does the compiler need to know about every angular component in my node_modules folder? Even if it needs to read them to build its map, it should not care about whether or not they have a module!

@DzmitryShylovich @kirjai this is only a problem with TestComponents in a spec file if you export them. And as they should only usually be used within the same file there is no need to export them. Problem solved for me.

I’ve been getting this error with an abstract class that extends NgClass. Removing the inheritance seems to fix this issue, but obviously creates other issues.

build:dev in https://github.com/AngularClass/angular2-webpack-starter auto converts a string to a string array to match a function definition, build:aot shows the error. During development it appears that frequent AOT builds are necessary.