diff --git a/packages/core/src/render3/METADATA.md b/packages/core/src/render3/METADATA.md new file mode 100644 index 0000000000..a9fa9ed351 --- /dev/null +++ b/packages/core/src/render3/METADATA.md @@ -0,0 +1,92 @@ +# Metadata storage in `.d.ts` files + +Currently angular stores metadata in `.metadata.json` files. This is problematic for several reasons: +- Error prone (tools have to know how to work with them.) +- Metadata does not follow import/exports. +- Complicated to explain, maintain. +- Hard for 3rd party contributors to add value. + +For Ivy we will be storing the metadata directly in `.d.ts` in the form of type information. This has several advantages: +- TypeScript does most of the work of resolution +- 3rd party developers already understand `.d.ts` as well as TypeScript type system. + +The key information which Ivy needs is the selector information. That is what pipe, component or directive has to be inserted at which location. These selector maps are declared in `@NgModule` annotations. Therefore the goal is to store: selectors and `@NgModule` import/exports rules. + +Let's assume that we have a file such as: +```javascript +@Pipe({ name: 'myPipe' }) +export class MyPipe { } + +@Component({ selector: `my-component` }) +export class MyComponent { } + + +@Component({ selector: `[myDirective]` }) +export class MyDirective { } + +@NgModule({ + declarations: [MyDirective, MyPipe], + exports: [MyDirective, MyPipe] +}) +export class MyModule { } + +@NgModule({ + declarations: [MyComponent], + imports: [MyModule], + exports: [MyComponent], +}) +export class MyAppModule { } +``` + +Then we can encode the selector information into the types as shown in this `.d.ts` file: +```javascript +export class MyPipe { + static ngPipeDef: PipeDef<{type: MyPipe, selector: 'myPipe'}>; +} + +export class MyComponent { + static ngComponentDef: ComponentDef<{type: MyComponent, selector: 'my-component'}>; +} + +export class MyDirective { + static ngDirectiveDef: DirectiveDef<{type: MyDirective, selector: '[myDirective]'}>; +} + +export class MyModule { + static ngInjectorDef: InjectorDef; + static ngModule = { + type: MyModule, + imports: [], + exports: [MyDirective, MyPipe] + }; +} + +export class MyAppModule { + static ngModule = { + type: MyAppModule, + imports: [MyModule], + exports: [MyComponent] + }; +} +``` + +This means that `ngtsc` can extract selector information from `Program` by having TypeScript resolving the types and then doing walking the resolved AST. This would be equivalent to something like this in code: +```javascript +const MyComponent_selector /* inferred type: 'my-component' */ = getSelector(MyComponent); +const MyDirective_selector /* inferred type: '[myDirective]' */ = getSelector(MyDirective); +const MyPipe_selector /* inferred type: 'myPipe' */ = getSelector(MyPipe); + +const MyModule_exports /* inferred type: typeof MyDirective | typeof MyPipe */ = + getNgModuleExports(MyModule); +const MyAppModule_imports /* inferred type: typeof MyModule */ = getNgModuleImports(MyAppModule); +const MyAppModule_transitive_imports /* inferred type: typeof MyDirective | typeof MyPipe */ = + getNgModuleExports(MyAppModule_imports); + +declare function getNgModuleExports(module: {ngModule: {exports: T[]}}): T; +declare function getNgModuleImports(module: {ngModule: {imports: T[]}}): T; +declare function getSelector(componentType: { + ngComponentDef?: ComponentDef<{selector: T}>, + ngDirectiveDef?: DirectiveDef<{selector: T}>, + ngPipeDef?: PipeDef<{selector: T}>, +}): T; +```` \ No newline at end of file diff --git a/packages/core/src/render3/STATUS.md b/packages/core/src/render3/STATUS.md index 7a691c89a9..df0919a32b 100644 --- a/packages/core/src/render3/STATUS.md +++ b/packages/core/src/render3/STATUS.md @@ -8,7 +8,79 @@ We currently expect Ivy to remain behind the flag until it's feature complete an # Implementation Status -## Annotations +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 `.metadata.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`. + + +## `@angular/compiler-cli` changes +### `ngtsc` TSC compiler transformer + +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 metadata from `.d.ts` (see: [METADATA.md](./METADATA.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 [METADATA.md](./METADATA.md) + - ❌ `@Component` => see [METADATA.md](./METADATA.md) + - ❌ `@Directive` => see [METADATA.md](./METADATA.md) + - ❌ `@NgModule` => see [METADATA.md](./METADATA.md) +- ❌ 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 +- ❌ Integration with WebPack (cli) +- ❌ Rewrite existing code by interpreting the associated metadata + - ❌ `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) +- ❌ Documentation + + +## `@angular/core` changes + +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. + + +# Crosscutting + +## Decorators | Annotation | `defineXXX()` | Run time | Spec | Compiler | Back Patch | | -------------------- | ------------------------------ | ------- | -------- | -------- | -------- | | `@Component` | ✅ `defineComponent()` | ✅ | ✅ | ✅ | ❌ | @@ -31,86 +103,6 @@ We currently expect Ivy to remain behind the flag until it's feature complete an -## Life Cycle Hooks -| Feature | Runtime | Spec | Compiler | -| ------------------------- | ------- | -------- | -------- | -| `onChanges()` | ✅ | ✅ | ✅ | -| `onDestroy()` | ✅ | ✅ | ✅ | -| `onInit()` | ✅ | ✅ | ✅ | -| `onChanges()` | ✅ | ✅ | ✅ | -| `doCheck()` | ✅ | ✅ | ✅ | -| `afterViewChecked()` | ✅ | ✅ | ✅ | -| `afterViewInit()` | ✅ | ✅ | ✅ | -| `afterContentChecked()` | ✅ | ✅ | ✅ | -| `afterContentInit()` | ✅ | ✅ | ✅ | -| listener teardown | ✅ | ✅ | ✅ | - - - -## Template Syntax -| Feature | Runtime | Spec | Compiler | -| -------------------------------- | ------- | -------- | -------- | -| `
` | ✅ | ✅ | ✅ | -| `
{{exp}}
` | ✅ | ✅ | ✅ | -| `
` | ✅ | ✅ | ✅ | -| `
` | ✅ | ✅ | ✅ | -| `
` | ✅ | ✅ | ✅ | -| `
` | ✅ | ✅ | ✅ | -| `
` | ✅ | ✅ | ✅ | -| `
` | ✅ | ✅ | ✅ | -| `
` | ✅ | ✅ | ❌ | -| `
` | ✅ | ✅ | ✅ | -| `
` | ❌ | ❌ | ❌ | -| `
` | ✅ | ✅ | ❌ | -| `
` | ✅ | ✅ | ✅ | -| `
` | ❌ | ❌ | ❌ | -| `
` | ✅ | ✅ | ❌ | -| `{{ ['literal', exp ] }}` | ✅ | ✅ | ✅ | -| `{{ { a: 'literal', b: exp } }}` | ✅ | ✅ | ✅ | -| `{{ exp \| pipe: arg }}` | ✅ | ✅ | ✅ | - - - -## `@Query` -| Feature | Runtime | Spec | Compiler | -| ------------------------------- | ------- | -------- | -------- | -| `@Query(descendants)` | ✅ | ✅ | n/a | -| `@Query(one)` | ✅ | ✅ | n/a | -| `@Query(read)` | ✅ | ✅ | n/a | -| `@Query(selector)` | ✅ | ✅ | n/a | -| `@Query(Type)` | ✅ | ✅ | n/a | -| `@ContentChildred` | ✅ | ✅ | ❌ | -| `@ContentChild` | ✅ | ✅ | ✅ | -| `@ViewChildren` | ✅ | ✅ | ❌ | -| `@ViewChild` | ✅ | ✅ | ✅ | - - - -## Content Projection -| Feature | Runtime | Spec | Compiler | -| ------------------------------- | ------- | -------- | -------- | -| `` | ✅ | ✅ | ✅ | -| `` | ✅ | ✅ | ✅ | -| container `projectAs` | ✅ | ✅ | ❌ | - - - -## Injection Features -| Feature | Runtime | Spec | Compiler | -| ----------------------------------- | ------- | -------- | -------- | -| `inject(Type)` | ✅ | ✅ | ✅ | -| `directiveInject(Type)` | ✅ | ✅ | ❌ | -| `inject(Type, SkipSelf)` | ❌ | ❌ | ❌ | -| `attribute('name')` | ✅ | ✅ | ❌ | -| `injectChangeDetectionRef()` | ✅ | ✅ | ❌ | -| `injectElementRef()` | ✅ | ✅ | ✅ | -| `injectViewContainerRef()` | ✅ | ✅ | ✅ | -| `injectTemplateRef()` | ✅ | ✅ | ✅ | -| default `inject()` with no injector | ❌ | ❌ | ❌ | -| sanitization with no injector | ✅ | ✅ | ❌ | - - - ## Change Detection | Feature | Runtime | | ----------------------------------- | ------- | @@ -131,11 +123,90 @@ We currently expect Ivy to remain behind the flag until it's feature complete an | ----------------------------------- | ------- | | `renderComponent()` | ✅ | | `getHostElement()` | ✅ | -| `createInjector()` | ❌ | +| `createInjector()` | ✅ | + +## Template Compiler + +### Template Syntax +| Feature | Runtime | Spec | Compiler | +| --------------------------------------- | ------- | -------- | -------- | +| `
` | ✅ | ✅ | ✅ | +| `
{{exp}}
` | ✅ | ✅ | ✅ | +| `
` | ✅ | ✅ | ✅ | +| `
` | ✅ | ✅ | ✅ | +| `
` | ✅ | ✅ | ✅ | +| `
` | ✅ | ✅ | ✅ | +| `
` | ✅ | ✅ | ✅ | +| `
` | ✅ | ✅ | ✅ | +| `
` | ✅ | ✅ | ❌ | +| `
` | ✅ | ✅ | ✅ | +| `
` | ❌ | ❌ | ❌ | +| `
` | ✅ | ✅ | ❌ | +| `
` | ✅ | ✅ | ✅ | +| `
` | ❌ | ❌ | ❌ | +| `
` | ✅ | ✅ | ❌ | +| `{{ ['literal', exp ] }}` | ✅ | ✅ | ✅ | +| `{{ { a: 'literal', b: exp } }}` | ✅ | ✅ | ✅ | +| `{{ exp \| pipe: arg }}` | ✅ | ✅ | ✅ | +| `` | ❌ | ❌ | ❌ | +| `` sanitization | ❌ | ❌ | ❌ | + +### Life Cycle Hooks +| Feature | Runtime | Spec | Compiler | +| ------------------------- | ------- | -------- | -------- | +| `onChanges()` | ✅ | ✅ | ✅ | +| `onDestroy()` | ✅ | ✅ | ✅ | +| `onInit()` | ✅ | ✅ | ✅ | +| `onChanges()` | ✅ | ✅ | ✅ | +| `doCheck()` | ✅ | ✅ | ✅ | +| `afterViewChecked()` | ✅ | ✅ | ✅ | +| `afterViewInit()` | ✅ | ✅ | ✅ | +| `afterContentChecked()` | ✅ | ✅ | ✅ | +| `afterContentInit()` | ✅ | ✅ | ✅ | +| listener teardown | ✅ | ✅ | ✅ | -## I18N +### `@Query` +| Feature | Runtime | Spec | Compiler | +| ------------------------------- | ------- | -------- | -------- | +| `@Query(descendants)` | ✅ | ✅ | n/a | +| `@Query(one)` | ✅ | ✅ | n/a | +| `@Query(read)` | ✅ | ✅ | n/a | +| `@Query(selector)` | ✅ | ✅ | n/a | +| `@Query(Type)` | ✅ | ✅ | n/a | +| `@ContentChildred` | ✅ | ✅ | ❌ | +| `@ContentChild` | ✅ | ✅ | ✅ | +| `@ViewChildren` | ✅ | ✅ | ❌ | +| `@ViewChild` | ✅ | ✅ | ✅ | + + + +### Content Projection +| Feature | Runtime | Spec | Compiler | +| ------------------------------- | ------- | -------- | -------- | +| `` | ✅ | ✅ | ✅ | +| `` | ✅ | ✅ | ✅ | +| container `projectAs` | ✅ | ✅ | ❌ | + + + +### Injection Features +| Feature | Runtime | Spec | Compiler | +| ----------------------------------- | ------- | -------- | -------- | +| `inject(Type)` | ✅ | ✅ | ✅ | +| `directiveInject(Type)` | ✅ | ✅ | ❌ | +| `inject(Type, SkipSelf)` | ❌ | ❌ | ❌ | +| `attribute('name')` | ✅ | ✅ | ❌ | +| `injectChangeDetectionRef()` | ✅ | ✅ | ❌ | +| `injectElementRef()` | ✅ | ✅ | ✅ | +| `injectViewContainerRef()` | ✅ | ✅ | ✅ | +| `injectTemplateRef()` | ✅ | ✅ | ✅ | +| default `inject()` with no injector | ❌ | ❌ | ❌ | +| sanitization with no injector | ✅ | ✅ | ❌ | + + +### I18N | Feature | Runtime | Spec | Compiler | | ----------------------------------- | ------- | -------- | -------- | | translate text literals | ❌ | ❌ | ❌ | @@ -143,10 +214,17 @@ We currently expect Ivy to remain behind the flag until it's feature complete an | ICU | ❌ | ❌ | ❌ | +### View Encapsulation +| Feature | Runtime | Spec | Compiler | +| ----------------------------------- | ------- | -------- | -------- | +| Render3.None | ✅ | ✅ | ✅ | +| Render2.None | ✅ | ✅ | ✅ | +| Render2.Emulated | ❌ | ❌ | ❌ | +| Render2.Native | ❌ | ❌ | ❌ | -## `______Ref`s +### `______Ref`s | Method | View Container Ref | Template Ref | Embeded View Ref | View Ref | Element Ref | Change Detection Ref | | ---------------------- | ------------------ | ------------ | ---------------- | -------- | ----------- | -------------------- | | `clear()` | ❌ | n/a | n/a | n/a | n/a | n/a | @@ -165,8 +243,3 @@ We currently expect Ivy to remain behind the flag until it's feature complete an | `checkNoChanges()` | n/a | n/a | ❌ | n/a | n/a | ✅ | | `reattach()` | n/a | n/a | ❌ | n/a | n/a | ✅ | | `nativeElement()` | n/a | n/a | n/a | n/a | ✅ | n/a | - -## Missing Pieces -- Sanitization ✅ -- Back patching in tree shakable way. ❌ -- attribute namespace ❌ \ No newline at end of file