{"action":"created","issue":{"url":"https://api.github.com/repos/angular/angular/issues/8962","repository_url":"https://api.github.com/repos/angular/angular","labels_url":"https://api.github.com/repos/angular/angular/issues/8962/labels{/name}","comments_url":"https://api.github.com/repos/angular/angular/issues/8962/comments","events_url":"https://api.github.com/repos/angular/angular/issues/8962/events","html_url":"https://github.com/angular/angular/issues/8962","id":158166528,"number":8962,"title":"Refactor decorator to metadata mapping","user":{"login":"tbosch","id":690815,"avatar_url":"https://avatars.githubusercontent.com/u/690815?v=3","gravatar_id":"","url":"https://api.github.com/users/tbosch","html_url":"https://github.com/tbosch","followers_url":"https://api.github.com/users/tbosch/followers","following_url":"https://api.github.com/users/tbosch/following{/other_user}","gists_url":"https://api.github.com/users/tbosch/gists{/gist_id}","starred_url":"https://api.github.com/users/tbosch/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tbosch/subscriptions","organizations_url":"https://api.github.com/users/tbosch/orgs","repos_url":"https://api.github.com/users/tbosch/repos","events_url":"https://api.github.com/users/tbosch/events{/privacy}","received_events_url":"https://api.github.com/users/tbosch/received_events","type":"User","site_admin":false},"labels":[],"state":"closed","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":4,"created_at":"2016-06-02T15:15:17Z","updated_at":"2016-10-04T21:08:59Z","closed_at":"2016-10-04T21:08:59Z","body":"Right now, users can create their own decorators that just create our internal `ComponentMetadata`, ... However, this does not work in offline compilation as we don't evaluate the user's code.\r\n\r\nInternally, we already have a mapping from decorator to metadata classes at 2 places (StaticReflector and ReflectionCapabilities). We could make this mapping generic and configurable via `CompilerConfig`.\r\n\r\n# Proposal\r\n\r\nAllow something like this:\r\n```\r\n// Metadata class\r\nclass MyComponentMetadata extends ComponentMetadata { ... }\r\n// Decorator\r\nfunction MyComponent(...args) {\r\n  return (cls) => {\r\n    var decoratorMetadata = {\r\n      type: MyComponent,\r\n      args: args\r\n    };\r\n    Reflect.defineMetadata(...);\r\n    return cls;\r\n  };\r\n}\r\n\r\nvar decoratorsToMetadata = new Map<any, Type>();\r\ndecoratorsToMetadata.set(Component, ComponentMetadata);\r\ndecoratorsToMetadata.set(Directive, DirectiveMetadata);\r\ndecoratorsToMetadata.set(MyComponent, MyComponentMetadata);\r\n...\r\n\r\nvar compilerConfig = new CompilerConfig({\r\n  decorators: decoratorsToMetadata\r\n});\r\n```\r\n\r\nThis relies on the following principles:\r\n\r\n- a decorator takes exactly the same arguments as the metadata that it represents\r\n- the decorator only stores its invocation (in the form of decoratorType / args), but does not create the metadata class\r\n- metadata classes are only created in the `CompileMetadataResolver` based on the mapping in the `CompilerConfig` from decorator classes to metadata classes.\r\n\r\nWith this, we get the following benefits:\r\n\r\n- works for offline compile, as we can also capture decorator invocations without running the decorator\r\n- simplification of `StaticReflector` and `ReflectionCapabilities` and all of our decorators, as they don't have to produce metadata instances any more.\r\n- we can unify the way tsickle stores metadata on the type and the metadata created by executing decorators during runtime\r\n- support for user defined decorators\r\n\r\n# Implementation details\r\n\r\n- Our decorators should not use `Reflect` to store metadata, but just patch properties like `MyComp.decorators` / `MyComp.propDecorators` in the same way as tsickle does it. With that we don't need a special support for tsickle.\r\n- Apply the approach above also for functions used to define animation. I.e. they should not return instances of metadata classed, but rather just return an object literal like `{ fn: trigger, args: [...] }`.\r\n- Change our utility functions to create decorators to take not argument and export them as public API for others to use.\r\n- The mapping in `CompilerConfig` would need to use `CompileIdentifierMetadata` to represent types and functions so that we can use it during runtime and offline compile alike."},"comment":{"url":"https://api.github.com/repos/angular/angular/issues/comments/251514299","html_url":"https://github.com/angular/angular/issues/8962#issuecomment-251514299","issue_url":"https://api.github.com/repos/angular/angular/issues/8962","id":251514299,"user":{"login":"tbosch","id":690815,"avatar_url":"https://avatars.githubusercontent.com/u/690815?v=3","gravatar_id":"","url":"https://api.github.com/users/tbosch","html_url":"https://github.com/tbosch","followers_url":"https://api.github.com/users/tbosch/followers","following_url":"https://api.github.com/users/tbosch/following{/other_user}","gists_url":"https://api.github.com/users/tbosch/gists{/gist_id}","starred_url":"https://api.github.com/users/tbosch/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tbosch/subscriptions","organizations_url":"https://api.github.com/users/tbosch/orgs","repos_url":"https://api.github.com/users/tbosch/repos","events_url":"https://api.github.com/users/tbosch/events{/privacy}","received_events_url":"https://api.github.com/users/tbosch/received_events","type":"User","site_admin":false},"created_at":"2016-10-04T21:08:59Z","updated_at":"2016-10-04T21:08:59Z","body":"This has landed in master, although without the support for custom decorators."}}
{"actor":{"display_login":"tbosch"}}