diff --git a/packages/core/src/render3/STATUS.md b/packages/core/src/render3/STATUS.md index 1069a7d766..4fe7282581 100644 --- a/packages/core/src/render3/STATUS.md +++ b/packages/core/src/render3/STATUS.md @@ -11,7 +11,7 @@ We currently expect Ivy to remain behind the flag until it's feature complete an The work can be divided into three categories: - `@angular/compiler-cli`: TypeScript transformer pipeline which includes two command line tools: - `ngtsc`: (Angular TypeScript Compiler) Angular compiler which strips out `@Component` (and friends) and replaces it with `defineComponent` (and friends). - - `ngcc`: (Angular Compatibility Compiler) NPM upgrade compiler which reads the `.STORING_METADATA_IN_D.TS.json` files and `.js` files and adds `defineComponent` (and friends) into the `node_module`. This in effect converts a pre-ivy module into ivy module. + - `ngcc`: (Angular Compatibility Compiler) NPM upgrade compiler which reads the `STORING_METADATA_IN_D.TS.json` files and `.js` files and adds `defineComponent` (and friends) into the `node_module`. This in effect converts a pre-ivy module into ivy module. - `@angular/compiler`: Ivy Compiler which converts decorator into ivy - `@angular/core`: Decorators which can be patched with `@angular/compiler`. @@ -22,45 +22,31 @@ The work can be divided into three categories: TSC transformer which removes and converts `@Pipe`, `@Component`, `@Directive` and `@NgModule` to the corresponding `definePipe`, `defineComponent`, `defineDirective` and `defineInjector`. -- ❌ Basic setup of the transformer into `tsc` -- ❌ Can read STORING_METADATA_IN_D.TS from `.d.ts` (see: [STORING_METADATA_IN_D.TS.md](./STORING_METADATA_IN_D.TS.md)) -- ❌ Detect decorators and convert them to the `defineXXX` method using the `__Compiler` in `@angular/compiler`. - - ❌ `@Pipe` => `definePipe` - - ❌ `@Component` => `defineComponent` - - ❌ `@Directive` => `defineDirective` - - ❌ `@NgModule` => `defineInjector` -- ❌ Encode selectors into `.d.ts` file. - - ❌ `@Pipe` => see [STORING_METADATA_IN_D.TS.md](./STORING_METADATA_IN_D.TS.md) - - ❌ `@Component` => see [STORING_METADATA_IN_D.TS.md](./STORING_METADATA_IN_D.TS.md) - - ❌ `@Directive` => see [STORING_METADATA_IN_D.TS.md](./STORING_METADATA_IN_D.TS.md) - - ❌ `@NgModule` => see [STORING_METADATA_IN_D.TS.md](./STORING_METADATA_IN_D.TS.md) -- ❌ support `extends` for `@Pipe`, `@Component`, `@Directive` and `@NgModule`. +- ✅ Basic setup of the transformer into `tsc` +- ✅ Can read STORING_METADATA_IN_D.TS from `.d.ts` (see: [STORING_METADATA_IN_D.TS.md](./STORING_METADATA_IN_D.TS.md)) +- ✅ Detect decorators and convert them to the `defineXXX` method using the `__Compiler` in `@angular/compiler`. +- ✅ Encode selectors into `.d.ts` file. +- ✅ support `extends` for `@Pipe`, `@Component`, `@Directive` and `@NgModule`. - ❌ Documentation ### `ngcc` Angular `node_module` compatibility compiler A tool which "upgrades" `node_module` compiled with non-ivy `ngc` into ivy compliant format. -- ❌ Basic setup of stand alone executable +- ✅ Basic setup of stand alone executable +- ✅ Rewrite existing code by interpreting the associated STORING_METADATA_IN_D.TS - ❌ Integration with WebPack (cli) -- ❌ Rewrite existing code by interpreting the associated STORING_METADATA_IN_D.TS - - ❌ `PipeCompiler`: `@Pipe` => `definePipe` - - ❌ `DirectiveCompiler`: `@Directive` => `defineDirective` - - ❌ `NgModuleCompiler`: `@NgModule` => `defineInjector` - - ❌ `ComponentCompiler`: `@Component` => `defineComponent` - - ❌ `TemplateCompiler` - - ❌ `StyleCompiler` - ❌ Documentation ## `@angular/compiler` changes -- ❌ Component compilation: Translates `@Component` => `defineComponent` - - ❌ `TemplateCompiler` (current known as `ViewCompiler`) - - ❌ `StyleCompiler` -- ❌ `PipeCompiler`: Translates `@Pipe` => `definePipe` -- ❌ `DirectiveCompiler`: Translates `@Directive` => `defineDirective` -- ❌ `InjectableCompiler`: Translates `@Injectable` => `defineInjectable` -- ❌ `NgModuleCompiler`: Translates `@NgModule` => `defineInjector` (and `defineNgModule` only in jit) +- ✅ Component compilation: Translates `@Component` => `defineComponent` + - ✅ `TemplateCompiler` (current known as `ViewCompiler`) + - ✅ `StyleCompiler` +- ✅ `PipeCompiler`: Translates `@Pipe` => `definePipe` +- ✅ `DirectiveCompiler`: Translates `@Directive` => `defineDirective` +- ✅ `InjectableCompiler`: Translates `@Injectable` => `defineInjectable` +- ✅ `NgModuleCompiler`: Translates `@NgModule` => `defineInjector` (and `defineNgModule` only in jit) - ❌ Documentation @@ -68,40 +54,39 @@ A tool which "upgrades" `node_module` compiled with non-ivy `ngc` into ivy compl The goal is for the `@Component` (and friends) to be the compiler of template. Since decorators are functions which execute during parsing of the `.js` file, the decorator can compile the template into Ivy. The AoT compiler's job is to remove the `@Component` and replace it with call to `defineComponent`. -- ❌ Remove `createDecorator` (and friends) since we no longer support other modes. -- ❌ `@angular/compiler` can patch itself onto: - - ❌ `@Injectable` - - ❌ `@NgModule` - - ❌ `@Pipe` - - ❌ `@Directive` - - ❌ `@Component` -- ❌ `ResourceLoader.resolved: Promise<>` Returns true if all `templateUrl`s and `styleUrl` have been resolved and application is ready to be bootstrapped. +- ✅ `@angular/compiler` can patch itself onto: + - ✅ `@Injectable` + - ✅ `@NgModule` + - ✅ `@Pipe` + - ✅ `@Directive` + - ✅ `@Component` +- ✅ `ResourceLoader.resolved: Promise<>` Returns true if all `templateUrl`s and `styleUrl` have been resolved and application is ready to be bootstrapped. # Testing / Debugging -- ❌ in debug mode publish components into DOM nodes for easier debugging. +- ✅ in debug mode publish components into DOM nodes for easier debugging. # Crosscutting ## Decorators -| Annotation | `defineXXX()` | Run time | Spec | Compiler | Back Patch | -| -------------------- | ------------------------------ | ------- | -------- | -------- | -------- | -| `@Component` | ✅ `defineComponent()` | ✅ | ✅ | ✅ | ❌ | -| `@Directive` | ✅ `defineDirective()` | ✅ | ✅ | ✅ | ❌ | -| `@Directive` | ❌ `defineAbstractDirective()` | ❌ | ❌ | ❌ | ❌ | -| `@Pipe` | ✅ `definePipe()` | ✅ | ✅ | ✅ | ❌ | -| `@Injectable` | ✅ `defineInjectable()` | ✅ | ❌ | ❌ | ❌ | -| `@NgModule` | ✅ `defineInjector()` | ✅ | ❌ | ❌ | ❌ | -| `@ConfigureInjector` | ❌ `defineInjector()` | ❌ | ❌ | ❌ | ❌ | +| Annotation | `defineXXX()` | Run time | Spec | Compiler | +| -------------------- | ------------------------------ | ------- | -------- | -------- | +| `@Component` | ✅ `defineComponent()` | ✅ | ✅ | ✅ | +| `@Directive` | ✅ `defineDirective()` | ✅ | ✅ | ✅ | +| `@Directive` | ✅ `defineBase()` | ✅ | ✅ | ✅ | +| `@Pipe` | ✅ `definePipe()` | ✅ | ✅ | ✅ | +| `@Injectable` | ✅ `defineInjectable()` | ✅ | ✅ | ✅ | +| `@NgModule` | ✅ `defineInjector()` | ✅ | ✅ | ✅ | +| `@ConfigureInjector` | ✅ `defineInjector()` | ❌ | ❌ | ❌ | ## Component Composition | Feature | Runtime | Spec | Compiler | | ---------------------------------------- | ------- | -------- | -------- | -| creation reordering based on injection | ❌ | ❌ | ✅ | -| `class CompA extends CompB {}` | ❌ | ❌ | ❌ | -| `class CompA extends CompB { @Input }` | ❌ | ❌ | ❌ | -| `class CompA extends CompB { @Output }` | ❌ | ❌ | ❌ | +| creation reordering based on injection | ✅ | ✅ | ✅ | +| `class CompA extends CompB {}` | ✅ | ✅ | ✅ | +| `class CompA extends CompB { @Input }` | ✅ | ✅ | ✅ | +| `class CompA extends CompB { @Output }` | ✅ | ✅ | ✅ | @@ -147,16 +132,12 @@ The goal is for the `@Component` (and friends) to be the compiler of template. S | `
` | ✅ | ✅ | ✅ | | `
` | ✅ | ✅ | ✅ | | `
` | ✅ | ✅ | ✅ | -| `
`
Compiler still needs to be updated to process templates with namespaced attributes. ([see #24386](https://github.com/angular/angular/pull/24386)) | ✅ | ✅ | ❌ | +| `
` | ✅ | ✅ | ✅ | | `{{ ['literal', exp ] }}` | ✅ | ✅ | ✅ | | `{{ { a: 'literal', b: exp } }}` | ✅ | ✅ | ✅ | | `{{ exp \| pipe: arg }}` | ✅ | ✅ | ✅ | | `` | ✅ | ✅ | ✅ | -| `` sanitization | ❌ | ❌ | ❌ | -| `
` | ❌ | ❌ | ❌ | -| `
` | ❌ | ❌ | ❌ | -| `
` | ❌ | ❌ | ❌ | -| `
` | ❌ | ❌ | ❌ | +| `` sanitization | ✅ | ✅ | ✅ | | [`
`][gh23560] | ✅ | ✅ | ✅ | | [``][gh23561] | ✅ | ✅ | ✅ | | [``][gh24381] | ✅ | ✅ | ✅ | @@ -189,10 +170,10 @@ The goal is for the `@Component` (and friends) to be the compiler of template. S | `@Query(read)` | ✅ | ✅ | n/a | | `@Query(selector)` | ✅ | ✅ | n/a | | `@Query(Type)` | ✅ | ✅ | n/a | -| `@ContentChildren` | ✅ | ✅ | ❌ | +| `@ContentChildren` | ✅ | ✅ | ✅ | | `@ContentChild` | ✅ | ✅ | ✅ | -| `@ViewChildren` | ✅ | ✅ | ❌ | -| `@ViewChild` | ✅ | ✅ | ❌ | +| `@ViewChildren` | ✅ | ✅ | ✅ | +| `@ViewChild` | ✅ | ✅ | ✅ | @@ -201,7 +182,7 @@ The goal is for the `@Component` (and friends) to be the compiler of template. S | ------------------------------- | ------- | -------- | -------- | | `` | ✅ | ✅ | ✅ | | `` | ✅ | ✅ | ✅ | -| container `projectAs` | ✅ | ✅ | ❌ | +| container `ngProjectAs` | ✅ | ✅ | ✅ | @@ -209,22 +190,22 @@ The goal is for the `@Component` (and friends) to be the compiler of template. S | Feature | Runtime | Spec | Compiler | | ----------------------------------- | ------- | -------- | -------- | | `inject(Type)` | ✅ | ✅ | ✅ | -| `directiveInject(Type)` | ✅ | ✅ | ❌ | +| `directiveInject(Type)` | ✅ | ✅ | ✅ | | `inject(Type, SkipSelf)` | ❌ | ❌ | ❌ | | `attribute('name')` | ✅ | ✅ | ❌ | -| `injectChangeDetectionRef()` | ✅ | ✅ | ❌ | +| `injectChangeDetectionRef()` | ✅ | ✅ | ✅ | | `injectElementRef()` | ✅ | ✅ | ✅ | | `injectViewContainerRef()` | ✅ | ✅ | ✅ | | `injectTemplateRef()` | ✅ | ✅ | ✅ | | `injectRenderer2()` | ✅ | ✅ | ✅ | -| default `inject()` with no injector | ❌ | ❌ | ❌ | -| sanitization with no injector | ✅ | ✅ | ❌ | +| default `inject()` with no injector | ✅ | ✅ | ✅ | +| sanitization with no injector | ✅ | ✅ | ✅ | ### I18N | Feature | Runtime | Spec | Compiler | | ----------------------------------- | ------- | -------- | -------- | -| translate text literals | ✅ | ✅ | ✅ | +| translate text literals | ❌ | ❌ | ✅ | | rearrange text nodes | ❌ | ❌ | ❌ | | ICU | ❌ | ❌ | ❌ | @@ -232,10 +213,10 @@ The goal is for the `@Component` (and friends) to be the compiler of template. S ### View Encapsulation | Feature | Runtime | Spec | Compiler | | ----------------------------------- | ------- | -------- | -------- | -| Renderer3.None | ✅ | ✅ | ✅ | -| Renderer2.None | ✅ | ✅ | ✅ | -| Renderer2.Emulated | ❌ | ❌ | ❌ | -| Renderer2.Native | ❌ | ❌ | ❌ | +| Renderer3.None | ✅ | ✅ | ✅ | +| Renderer2.None | ✅ | ✅ | ✅ | +| Renderer2.Emulated | ✅ | ✅ | ✅ | +| Renderer2.Native | ✅ | ✅ | ✅ | diff --git a/packages/core/src/render3/STORING_METADATA_IN_D.TS.md b/packages/core/src/render3/STORING_METADATA_IN_D.TS.md new file mode 100644 index 0000000000..2c14ab5e63 --- /dev/null +++ b/packages/core/src/render3/STORING_METADATA_IN_D.TS.md @@ -0,0 +1,57 @@ +# Storing Metadata in `.d.ts` files + +Previous version of Angular used `metadata.json` files to store information about directives/component/pipes/ng-modules. +`ngc` compiler would than do a global analysis to generate the `.ngfactory.ts` files from the `metadata.json`. +Ivy strives for locality, which means that `ngtsc` should not need any global information in order to compile the system. +The above is mostly true. +Unfortunately, in order for `ngtsc` to generate code which is tree shakable `ngtsc` does need to have global knowledge. + +Here is an abbreviated example of breakage of tree-shake-ability. +```typescript +@Directive({ + selector: '[tooltip]' +}) +export class TooltipDirective { + // ngtsc generates this: + static ngDirectiveDef = defineDirective(...); +} + +@Component({ + selector: 'my-app', + template: 'Hello World!' +}) +class MyAppComponent { + // ngtsc generates this: + static ngDirectiveDef = defineComponent({ + ... + directives: [ + // BREAKS TREE-SHAKING!!! + // TooltipDirective included here because it was declared in the NgModule + // ngtsc does not know it can be omitted. + // Only way for ngtsc to know that it can omit TooltipDirective is if it knows + // its selector and see if the selector matches the current component's template. + TooltipDirective + ] + }); +} + +@NgModule({ + declarations: [MyAppComponent, TooltipDirective], + bootstrap: [MyAppComponent], +}) +class MyAppModule { + // ngtsc generates this: + static ngDirectiveDef = defineNgModule(...); +} +``` + +Notice that `ngtsc` can't remove `TooltipDirective` because it would need to know its selector and see if the directive matches in the component's template. +Knowing the selector breaks locality and so we make an exception for some locality information such as selector, inputs and outputs. +Since we are breaking the locality rule, we need to store the information someplace since `ngtsc` can't have access to the `TooltipDirective` source. +We store the information in the `.d.ts` file like so. + +```typescript +class TooltipDirective { + static ngDirectiveDef: DirectiveDefWithMeta +} +```