Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support property renaming for decorators #1125

Merged
merged 3 commits into from
Dec 3, 2019

Conversation

rictic
Copy link
Contributor

@rictic rictic commented Dec 2, 2019

TypeScript converts code like this:

class Foo {
  @decorator prop = 'hi';
}

into JS like this:

tslib_1.__decorate([
    decorator,
    tslib_1.__metadata("design:type", Object)
], Foo.prototype, "prop", void 0);

This is a problem for Closure Compiler's property renaming, because the property prop may have been renamed, but "prop" is just a string literal, and so it won't be renamed. Fortunately there is an API that Closure Compiler knows to process at build time that will rename a string literal representing a field name on a type: https://google.github.io/closure-library/api/goog.reflect.html#objectProperty

With this change, when emitting goog.module output, tsickle will notice calls to __decorate and instead emit

tslib_1.__decorate([
    decorator,
    tslib_1.__metadata("design:type", Object)
], Foo.prototype, __googReflect.objectProperty("prop", Foo.prototype), void 0);

where __googReflect is defined just after the goog.module section as

const __googReflect = goog.require("goog.reflect");

In this way, the decorator may safely use the property name with the assurance that it will be renamed at runtime when compiled with Closure Compiler.

Doc (unfortunately internal to google) considering the various implementation options: https://goto.google.com/renaming-safe-decorators

TypeScript converts code like this:

```typescript
class Foo {
  @decorator prop = 'hi';
}
```

into JS like this:

```javascript
tslib_1.__decorate([
    decorator,
    tslib_1.__metadata("design:type", Object)
], Foo.prototype, "prop", void 0);
```

This is a problem for Closure Compiler's property renaming, because the property `prop` may have been renamed, but `"prop"` is just a string literal, and so it won't be renamed. Fortunately there is an API that Closure Compiler knows to process at build time that will rename a string literal representing a field name on a type: https://google.github.io/closure-library/api/goog.reflect.html#objectProperty

With this change, when emitting goog.module output, tsickle will notice calls to `__decorate` and instead emit

```javascript
tslib_1.__decorate([
    decorator,
    tslib_1.__metadata("design:type", Object)
], Foo.prototype, __googReflect.objectProperty("prop", Foo.prototype), void 0);
```

where `__googReflect` is defined just after the `goog.module` section as

```javascript
const __googReflect = goog.require("goog.reflect");
```

In this way, the decorator may safely use the property name with the assurance that it will be renamed at runtime when compiled with Closure Compiler.

Doc (unfortunately internal to google) considering the various implementation options: https://goto.google.com/renaming-safe-decorators
@mprobst mprobst self-requested a review December 2, 2019 08:37
Copy link
Contributor

@mprobst mprobst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks nice!

src/decorators.ts Outdated Show resolved Hide resolved
src/decorators.ts Outdated Show resolved Hide resolved
src/decorators.ts Outdated Show resolved Hide resolved
src/decorators.ts Outdated Show resolved Hide resolved
src/decorators.ts Outdated Show resolved Hide resolved
src/decorators.ts Show resolved Hide resolved
src/decorators.ts Show resolved Hide resolved
src/decorators.ts Outdated Show resolved Hide resolved
test/e2e_closure_test.ts Outdated Show resolved Hide resolved
test_files/fake_goog_reflect.js Outdated Show resolved Hide resolved
@rictic rictic requested a review from mprobst December 2, 2019 20:01
@mprobst
Copy link
Contributor

mprobst commented Dec 2, 2019

Approved. Let's do another TAP round, and then I can try to land tomorrow.

@rictic
Copy link
Contributor Author

rictic commented Dec 3, 2019

Last night's TAP run is looking good. See http://test/283069087 (I have fixes for the two failures in TypeScript tests).

@mprobst mprobst merged commit 8233af4 into angular:master Dec 3, 2019
@mprobst
Copy link
Contributor

mprobst commented Dec 3, 2019

Thanks, landed!

@rictic rictic deleted the just-googReflect-decorators branch December 3, 2019 19:25
rictic added a commit to rictic/tsickle that referenced this pull request Dec 15, 2019
@ExportDecoratedItemsIfPublic is a new decorator modifier for fields which should only be exported if public. When private or protected they are renamable and may be eliminated as dead code. This is more optimal version of @ExportDecoratedItems for fields which can only be accessed by reflection if they're public.

Our use case ExportDecoratedItemsIfPublic is the LitElement @Property decorator, where public properties are accessible as HTML attributes and may be written by raw HTML files or various template languages (lit-html, soy, angular templates, etc). This obviously is taking place outside of the class hierarchy of the element and so is only valid for public properties, but it is still useful for private properties to participate in the update lifecycle that comes along with @Property.

This change, combined with the already-landed angular#1125 we'll be able to remove the final @ExportDecoratedItems annotation from the LitElement decorators.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants