Typescript Auto Import Misses
I have an Angular CLI project that has a library and an application, and I am finding the IntelliJ IDEA Auto Import feature unhelpful.
ng new a8cli --defaults=true
cd a8cli
ng g library lib1
ng build lib1
If I try referencing the Lib1Module from the AppModule, then I might get one of the following suggestions:
import { Lib1Module } from 'projects/lib1/src/public-api';
import { Lib1Module } from 'projects/lib1/src/lib/lib1.module';
import { Lib1Module } from 'lib1/lib1';
import { Lib1Module } from 'lib1/public-api';
import { Lib1Module } from 'lib1/lib/lib1.module';
but what I really want is:
import { Lib1Module } from 'lib1';
NOTE: This already has a YT request: https://youtrack.jetbrains.com/issue/WEB-39705
But there are more scenarios than just that. For example, within the library code I expect the Auto Import statements to be relative within the module. If I remove the Lib1Component import statement from lib1.module.ts, and use the Auto Import feature I get the following suggestions:
import { Lib1Component } from '../public-api';
import { Lib1Component } from './lib1.component'; // this is correct
import { Lib1Component } from 'lib1/lib1';
import { Lib1Component } from 'lib1/public-api';
import { Lib1Component } from 'lib1/lib/lib1.component';
import { Lib1Component } from "projects/lib1/src/lib/lib1.component";
The correct one IS within this list, but the other options are WRONG, so the IDE is not helping by suggesting so many.
Now consider a multi-module library like the Angular Material Components repository, where each component is within its own module. References within a module should use relative pathing (e.g. "./selection-list), while crossing module references should use the absolute module name (e.g. "@angular/material/divider")
See https://github.com/angular/components/blob/master/src/material/list/list-module.ts#L26
If I remove the MatDividerModule and use Auto Import:
import { MatDividerModule } from "../../tools/public_api_guard/material/divider";
import { MatDividerModule } from '..';
import { MatDividerModule } from '../divider';
import { MatDividerModule } from '../public-api';
import { MatDividerModule } from '../divider/divider-module';
All of these options are incorrect, because the MatDividerModule is not part of the @angular/material/list module, so the reference must reference the other module properly like:
import {MatDividerModule} from '@angular/material/divider';
Conclusion
I know this is a complicated problem that involves tsconfig.json settings and IntelliJ IDEA settings, and if we wanted to get fancy maybe even angular.json.
Are there solutions to all these problems that I am missing?
How can IntelliJ IDEA possibly infer the correct auto import without understanding how the code is being packaged and bundled? (ng-packagr, webpack)
Please sign in to leave a comment.
I forgot to mention that IntelliJ IDEA also suggests:
be replaced with:
within Angular Material https://github.com/angular/components/blob/master/src/material/core/ripple/index.ts#L11. The suggested replacement is wrong because the reference MUST BE RELATIVE and if it should be module based, then the correct module would be @angular/material/core .
>How can IntelliJ IDEA possibly infer the correct auto import without understanding how the code is being packaged and bundled? (ng-packagr, webpack)
Of course, in general there is no way for the IDE to know how the certain app is going to be bundled and deployed. We can hardcode the certain patterns for apps with known structure and setup (like default apps generated with cli), but for other cases we can only rely on path mappings in tsconfig.json files + some heuristics.
I can suggest playing with import settings in Settings | Editor | Code Style | TypeScript | Imports (and make sure that the
dist
directory is not excluded from indexing some that the generated library modules can be resolved)>We can hardcode the certain patterns for apps with known structure and setup
An analysis needs to be done, but I believe the best approach will be to make this configuration driven like other aspects of the IDE (like setting the JavaScript version). Hardcoded defaults could be populated on module creation, but always customizable by the user.
>and make sure that the
dist
directory is not excludedI disagree with this idea. Consider a development scenario where a developer creates a new symbol in the Library code and then attempts to use that symbol in the Application code. Auto Import should not rely on the /dist being updated. In fact, I have modified my project's default setup to include the sources from the Library within the serving of the Application, so changes made to the Library while ng serve-ing will result in the Application refreshing. I have then configured ng serve --prod to use /dist.
Also keep in mind that TypeScript code often have multiple tsconfig*.json files within different directories to support various libraries & applications and build operations.
But again, the core question is, can an IDE know with certainty how the import statement should be correctly declared? If the answer is no, then the IDE should provide a configurable system where by the import syntax can be configured.
> can an IDE know with certainty how the import statement should be correctly declared?
it can't
> If the answer is no, then the IDE should provide a configurable system where by the import syntax can be configured.
some options are available in Settings | Editor | Code Style | TypeScript | Imports.
If this is not enough, please feel free to file a feature request for adding more options to youtrack, https://youtrack.jetbrains.com/issues/WEB