# Developer Guide: Lightweight-tokens, making components tree shakable ###### tags: `design` ## Overview This document describes best practices around using injection tokens in a way which supports tree-shaking. [Tree-shakable-providers](https://angular.io/guide/dependency-injection-providers#tree-shakable-providers) are the preferred way to included services only when they are used. This document describes pattern to be used with components to achieves proper tree-shaking. Having your codebase designed for tree-shaking is especially important for the library developers. Libraries often have many capabilities. When an application uses a library it often uses only a small set of those capabilities, therefore we want to ensure that the unused capabilities are properly tree-shaken. The problem which we are trying to solve is that in order to inject or query for something, we need to be able to explicitly identify that something. In Angular we use the `Type` as the injection-token to identify that something which we are looking for. The implication of this is that the injection-token is than retained regardless if the injection-token gets injected or not. This causes the injection-token to be retained, which prevents tree-shaking. The solution is to separate the injection-token from the implementation so that the injection-token is very lightweight. The injection-token will still be retained by the tree-shaker but because it is lightweight it is not a problem. ## Content Query Injection To better explain the condition under which this occurs lets look at this hypothetical situation which demonstrates the problem. ```htmlmixed= <lib-card> <lib-header>...</lib-header> </lib-card> ``` Lets assume that we are using a library which provides `<lib-card>` component. The `<lib-card>` component can optionally take `<lib-header>`, and `<lib-body>` as configuration. The most likely implementation is that `<lib-card>` component uses `@ContanteChild`/`@ContentChildren` to get a hold of `<lib-header>`, and `<lib-body>`. Here is a straw man implementation. ```typescript= @Component({ selector: 'lib-header', ..., }) class LibHeaderComponent {} @Component({ selector: 'lib-card', ..., }) class LibCardComponent { @ContentChild(LibHeaderComponent) header: LibHeaderComponent|null = null; } ``` Lets further assume that `<lib-header>` is optional therefore a simplified usage would be: ```htmlmixed= <lib-card></lib-card> ``` In such usage one would expect that `<lib-header>` should be tree shaken as it is not used. This is not so because `LibCardComponent` refers to it like so `@ContentChild(LibHeaderComponent) header: LibHeaderComponent;` Notice that there are two references to the `LibHeaderComponent`: 1. **type position**: A type position is a reference to `LibHeaderComponent` as a type. In our example `header: LibHeaderComponent` refers to `LibHeaderComponent` in a type position. TypeScript elides the type position references, therefore this reference is erased after TypeScript compilation and as a result has no impact on tree-shaking. 2. **value position**: A value position is a reference to `LibHeaderComponent` which must be retained at runtime (can't be elided.) In our example `@ContentChild(LibHeaderComponent)` refers to `LibHeaderComponent` in a value position. The retention of the reference influences the tree-shaking process. Because value positions are retained they prevent the value from being tree shaken. In our example `LibHeaderComponent` will be retained regardless of if `<lib-header>` is used by the developer in the application. The retention is an undesirable property of this design. If `LibHeaderComponent` is large (code + template + styles) it will have a negative impact on the size of the application ## Library developer vs application developer It is important to separate what code is written by library vs application developer. Template written by the application developer: ```htmlmixed= <lib-card> <lib-header>...</lib-header> </lib-card> ``` Implementation written by the library developer: ```typescript= @Component({ selector: 'lib-header', ..., }) class LibHeaderComponent {} @Component({ selector: 'lib-card', ..., }) class LibCardComponent { @ContentChild(LibHeaderComponent) header: LibHeaderComponent|null = null; } ``` It is important to realize that the library developer has no idea at the time of implementing the library how it will be used by the application. The implication is that the library developer must design for all possibilities. On the other hand the application developer should not worry about the library implementation details. Therefore from the application developer it comes as a great surprise that usage of `<lib-card>` retains `<lib-header>` even if it is not used in the application. The core problem is that the sub-optimal choices in the library design effect the application. This is an issue because the developer (library) which is a source of the issue is not the same developer (application) as the one who suffers the consequences of that choice (retaining too much). For this reason it is very important to avoid this pattern as it will lead to unnecessarily large bundles. # Using lightweight token In order to prevent the retention of unused components it is necessary for the library author to use the lightweight token pattern. The pattern consist of using a small abstract class as injection token. Later implement that abstract class with actual implementation. The result is that the abstract class will be retained but it is small and so it has almost no impact on the application developer. ```typescript= abstract class LibHeaderToken {} @Component({ selector: 'lib-header', providers: [ {provide: LibHeaderToken, useExisting: LibHeaderComponent} ] ..., }) class LibHeaderComponent extends LibHeaderToken {} @Component({ selector: 'lib-card', ..., }) class LibCardComponent { @ContentChild(LibHeaderToken) header: LibHeaderToken|null = null; } ``` Notice: - The `LibCardComponent` implementation no longer refers to `LibHeaderToken` (neither in value not type position). This allows full tree shaking of `LibHeaderComponent` to take place. - The `LibHeaderToken` consists of just class declaration but no concrete implementation. The implication is that abstract class is small and will not materially impact the application. - The `LibHeaderComponent` must tie the lightweight token with itself (in `providers`) so that Angular can correctly inject the concrete type. Without this Angular has no way of knowing that `LibHeaderToken` should inject `LibHeaderComponent`. Having such a knowledge would prevent the tree-shaking of `LibHeaderComponent` which would defeat the purpose. To summarize the lightweight token pattern consists of: 1. A lightweight token which is represented as an `abstract class`. 2. A injection of the lightweight pattern (such as `@ContentChild` or `@ContentChildren`). 3. A provider in the implementation of the lightweight token which associates the lightweight token with the implementation. ## Using lightweight token as API definition A key thing to notice is that `LibCard` now injects `LibHeaderToken` rather than `LibHeaderComponent`. What if `LibCardComponent` needs to invoke methods on the `LibHeaderComponent`, how to do that in a type safe manner? The solution is to add abstract methods to the `LibHeaderToken` which allows the `LibCardComponent` to communicate with the `LibHeaderComponent` without actually referring to `LibHeaderComponent`. Because `LibHeaderComponent` implements `LibHeaderToken` TypeScript will ensure type safety. ```typescript= abstract class LibHeaderToken { abstract doSomething(): void; } @Component({ selector: 'lib-header', providers: [ {provide: LibHeaderToken, useExisting: LibHeader} ] ..., }) class LibHeaderComponent extends LibHeaderToken { doSomething(): void { // Concrete implemantation of `doSomething` } } @Component({ selector: 'lib-card', ..., }) class LibCardComponent implement AfterContentInit { @ContentChild(LibHeaderToken) header: LibHeaderToken|null = null; ngAfterContentInit(): void { this.header && this.header.doSomething(); } } ``` ## When to use the lightweight-token pattern This problem arises when component is used as a injection-token. There are two cases when that can happen: 1)with query and 2) with constructor injection. ```typescript= class MyComponent { constructor(@Optional() other: OtherComponent) {} @ContentQuery(OtherComponent) other: OtherComponent|null; @ViewQuery(OtherComponent) other: OtherComponent|null; } ``` In the above example, all three uses of `OtherComponent` will cause its retention (not tree-shakable.) This is because in all cases the `OtherComponent` is used in the value position. The constructor usage is in value position, but TypeScript `--emitDecoratorMetadata` converts the type position to value position. Effectively changing `constructor(@Optional() other: OtherComponent)` to `constructor(@Optional() @Inject(OtherComponent) other)` which makes it a value position. This causes the tree-shaker to retain the reference. For services use [tree-shakable-providers](https://angular.io/guide/dependency-injection-providers#tree-shakable-providers) ## Suggested naming strategies Angular style guide suggest how things should be name but these are only suggestions and are not enforced. Therefore you are free to pick any name for the component and the injection-token. Lightweight tokens are only useful with components. Angular style guide suggest that components are name with `Component` suffix (such as `FooComponent`.) Therefore our suggestion is to suffix lightweight tokens with `Token` suffix (such as `FooToken`.) By using `Component` and `Token` the relationship is communicated through the name.