Skip to content

buildOptimizer deleted some code from a property setter in a component that came from an ng-packagr library #14084

@fr0

Description

@fr0

🐞 Bug report

Command (mark with an x)

- [ ] new
- [x] build
- [x] serve
- [ ] test
- [ ] e2e
- [ ] generate
- [ ] add
- [ ] update
- [ ] lint
- [ ] xi18n
- [ ] run
- [ ] config
- [ ] help
- [ ] version
- [ ] doc

Is this a regression?

Unknown.

Description

The build optimizer seems to have stripped out some important code. Here's my original TypeScript (template and styles ommitted for brevity):

export interface IExpandable {
  isExpanded: boolean;
  isExpandedChange: EventEmitter<boolean>;
  canExpand: boolean;
  toggleExpand(): void;
  expand();
  collapse();
}

@Component({
  selector: 'atlas-expandable-if',
  template: `
...
  `,
  styles: [`
...
  `]
})
export class ExpandableIfComponent implements IExpandable {
  @Input() preserveSpaceForIcon: boolean = true;
  @Input() canExpand: boolean = true;
  @Input() expandIcon: string;
  @Input() collapseIcon: string;
  @Input() loadingIcon: string;
  @Input() delayedExpand = false;
  @Input() overrideIcon: string|false = false;
  @Output() isExpandedChange: EventEmitter<boolean> = new EventEmitter<boolean>();
  isExpanding = false;

  constructor(public icons: AtlasIconPaths,
              private changeDetector: ChangeDetectorRef) { }

  private _isExpanded = false;
  get isExpanded(): boolean { return this._isExpanded; }
  @Input() set isExpanded(value: boolean) {
    if (this._isExpanded !== value) {
      this._isExpanded = value;
      this.isExpandedChange.emit(value);
      this.changeDetector.markForCheck();
    }
  }

  clickHeader(event: MouseEvent) {
    if (!event.shiftKey) {
      event.preventDefault();
      event.stopPropagation();
      this.toggleExpand();
    }
  }

  toggleExpand() {
    if (this.isExpanding) { return; }
    if (this.isExpanded || this.canExpand) {
      if (this.isExpanded) {
        this.collapse();
      }
      else {
        this.expand();
      }
    }
  }
  expand() {
    if (this.delayedExpand) {
      this.isExpanding = true;
      setTimeout(() => {
        this.isExpanding = false;
        this.isExpanded = true;
      });
      this.changeDetector.markForCheck();
    }
    else {
      this.isExpanded = true;
    }
  }
  collapse() {
    this.isExpanded = false;
  }
}

And here's the code that gets generated with buildOptimizer: true (un-minified, but otherwise presented as-is):

Notice the set isExpanded property setter

                    sa = class {
                        constructor(e, t) {
                            this.icons = e, this.changeDetector = t, this.preserveSpaceForIcon = !0, this.canExpand = !0, this.delayedExpand = !1, this.overrideIcon = !1, this.isExpandedChange = new it, this.isExpanding = !1, this._isExpanded = !1
                        }
                        get isExpanded() {
                            return this._isExpanded
                        }
                        set isExpanded(e) {
                            this._isExpanded !== e && (this._isExpanded = e)
                        }
                        clickHeader(e) {
                            e.shiftKey || (e.preventDefault(), e.stopPropagation(), this.toggleExpand())
                        }
                        toggleExpand() {
                            this.isExpanding || (this.isExpanded || this.canExpand) && (this.isExpanded ? this.collapse() : this.expand())
                        }
                        expand() {
                            this.delayedExpand ? (this.isExpanding = !0, setTimeout(() => {
                                this.isExpanding = !1, this.isExpanded = !0
                            }), this.changeDetector.markForCheck()) : this.isExpanded = !0
                        }
                        collapse() {
                            this.isExpanded = !1
                        }
                    },

This code is in a library that gets packaged via ng-packagr version 5.0.1. However, here is the code that ng-packagr generates:

let ExpandableIfComponent = class ExpandableIfComponent {
    constructor(icons, changeDetector) {
        this.icons = icons;
        this.changeDetector = changeDetector;
        this.preserveSpaceForIcon = true;
        this.canExpand = true;
        this.delayedExpand = false;
        this.overrideIcon = false;
        this.isExpandedChange = new EventEmitter();
        this.isExpanding = false;
        this._isExpanded = false;
    }
    get isExpanded() { return this._isExpanded; }
    set isExpanded(value) {
        if (this._isExpanded !== value) {
            this._isExpanded = value;
            this.isExpandedChange.emit(value);
            this.changeDetector.markForCheck();
        }
    }
    clickHeader(event) {
        if (!event.shiftKey) {
            event.preventDefault();
            event.stopPropagation();
            this.toggleExpand();
        }
    }
    toggleExpand() {
        if (this.isExpanding) {
            return;
        }
        if (this.isExpanded || this.canExpand) {
            if (this.isExpanded) {
                this.collapse();
            }
            else {
                this.expand();
            }
        }
    }
    expand() {
        if (this.delayedExpand) {
            this.isExpanding = true;
            setTimeout(() => {
                this.isExpanding = false;
                this.isExpanded = true;
            });
            this.changeDetector.markForCheck();
        }
        else {
            this.isExpanded = true;
        }
    }
    collapse() {
        this.isExpanded = false;
    }
};
__decorate([
    Input(),
    __metadata("design:type", Boolean)
], ExpandableIfComponent.prototype, "preserveSpaceForIcon", void 0);
__decorate([
    Input(),
    __metadata("design:type", Boolean)
], ExpandableIfComponent.prototype, "canExpand", void 0);
__decorate([
    Input(),
    __metadata("design:type", String)
], ExpandableIfComponent.prototype, "expandIcon", void 0);
__decorate([
    Input(),
    __metadata("design:type", String)
], ExpandableIfComponent.prototype, "collapseIcon", void 0);
__decorate([
    Input(),
    __metadata("design:type", String)
], ExpandableIfComponent.prototype, "loadingIcon", void 0);
__decorate([
    Input(),
    __metadata("design:type", Object)
], ExpandableIfComponent.prototype, "delayedExpand", void 0);
__decorate([
    Input(),
    __metadata("design:type", Object)
], ExpandableIfComponent.prototype, "overrideIcon", void 0);
__decorate([
    Output(),
    __metadata("design:type", EventEmitter)
], ExpandableIfComponent.prototype, "isExpandedChange", void 0);
__decorate([
    Input(),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], ExpandableIfComponent.prototype, "isExpanded", null);
ExpandableIfComponent = __decorate([
    Component({
        selector: 'atlas-expandable-if',
        template: `
...
        styles: [`
...
  `]
    }),
    __metadata("design:paramtypes", [AtlasIconPaths,
        ChangeDetectorRef])
], ExpandableIfComponent);

The correct code is generated if buildOptimizer: false is used in my angular.json.

🌍 Your Environment




Angular CLI: 7.3.7
Node: 11.12.0
OS: darwin x64
Angular: 7.2.11
... animations, common, compiler, compiler-cli, core, elements
... forms, http, language-service, platform-browser
... platform-browser-dynamic, router

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.13.7
@angular-devkit/build-angular     0.13.7
@angular-devkit/build-optimizer   0.13.7
@angular-devkit/core              7.3.7
@angular-devkit/schematics        7.3.7
@angular/cli                      7.3.7
@ngtools/json-schema              1.1.0
@schematics/angular               7.3.7
@schematics/update                0.13.7
ng-packagr                        5.0.1
rxjs                              6.4.0
typescript                        3.2.4
webpack                           4.29.6

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions