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

lvy:Library build by angular-cli can not tree shaking in apps #34648

Closed
hpyou opened this issue Dec 30, 2019 · 15 comments
Closed

lvy:Library build by angular-cli can not tree shaking in apps #34648

hpyou opened this issue Dec 30, 2019 · 15 comments

Comments

@hpyou
Copy link

hpyou commented Dec 30, 2019

🐞 bug report

Affected Package

@angular/cli

Is this a regression?

it can not work in ng8 also, but i try to ask if it can work in ng9 or not

Description

A library build by angular-cli, improt a module or declare a component of the library into a app, then build the app with aot, the whole library code gets into bundle, the tree shaking did not work

🔬 Minimal Reproduction

https://stackblitz.com/edit/angular-srduuh

🔥 Exception or Error

In the demo i use the library jigsaw(https://github.com/rdkmaster/jigsaw) which build by angular-cli

image

and i import the module JigsawButtonModule which just has the component JigsawButton into AppModule

image

if you build the app with this environment,you will get the whole jigsaw code in vender.js

🌍 Your Environment

Angular CLI: 9.0.0-rc.7
Node: 10.18.0
OS: win32 x64

Angular: 9.0.0-rc.7
... animations, cli, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router
Ivy Workspace: Yes

Package Version

@angular-devkit/architect 0.900.0-rc.7
@angular-devkit/build-angular 0.900.0-rc.7
@angular-devkit/build-optimizer 0.900.0-rc.7
@angular-devkit/build-webpack 0.900.0-rc.7
@angular-devkit/core 9.0.0-rc.7
@angular-devkit/schematics 9.0.0-rc.7
@ngtools/webpack 9.0.0-rc.7
@schematics/angular 9.0.0-rc.7
@schematics/update 0.900.0-rc.7
rxjs 6.5.3
typescript 3.6.4
webpack 4.41.2

@hpyou hpyou changed the title Library build by angular-cli can not tree shaking in apps lvy:Library build by angular-cli can not tree shaking in apps Dec 30, 2019
@gkalpak gkalpak transferred this issue from angular/angular Dec 30, 2019
@alan-agius4
Copy link
Contributor

@hpyou which command are you using to build? Are you using the --prod command?

I tried to look at the reproduction and it doesn't seem that the entire library is being retained
image

@hpyou
Copy link
Author

hpyou commented Dec 30, 2019

@alan-agius4 I used ng --aot not --prod for looking for the code in bundle, if with --prod code will be uglified.

@alan-agius4
Copy link
Contributor

alan-agius4 commented Dec 30, 2019

--aot alone will not do any treeshaking or dead code elimination, hence the entire library will be retained.

The flags which are associated with tree-shaking and dead-code elimination are --optimization and --build-optimizer which are enabled by default under the production configuration.

To disable mangling you can use the NG_BUILD_MANGLE environment variable:

NG_BUILD_MANGLE=false ng build --prod

@hpyou
Copy link
Author

hpyou commented Dec 30, 2019

by using --prod , it shaked most code of library, but not every unused code, i will check it tomorry, then i will feedback to you.
by the way, i use NG_BUILD_MANGLE=false in cli, it throws it is not a cli commend

@alan-agius4
Copy link
Contributor

In windows try to use set NG_BUILD_MANGLE=false&& ng build --prod

@hpyou
Copy link
Author

hpyou commented Dec 31, 2019

@alan-agius4 i checked the bundle main-es5.js, it contains some package like import * as JSZip from 'jszip/dist/jszip.min'; used in my library, can‘t tree shaking shake these packages?

and some directive like JigsawTrustedHtmlModule not all are not removed, and the code path, you can just look at it, if you can find something wroted wrong.

@alan-agius4
Copy link
Contributor

Hi, based on your reproduction JigsawTrustedHtmlModule doesn't seem to be included in the final output.

alan@Alans-iMac angular-srduuh % grep -i JigsawTrustedHtmlModule dist/demo/main.c77024c7c06ef05e82e6.js        
alan@Alans-iMac angular-srduuh % 

Re JSZip module, probably it's not being tree-shaken because it's a non ECMAScript module which would cause bailouts.

@hpyou
Copy link
Author

hpyou commented Jan 2, 2020

I find the selector in the output, maybe not a module but the component JigsawTrustedHtml, and the component code is in there
image

and other components: JigsawBox, JigsawBlock
image
image

and a service PopupService

Is angular-srduuh your internal tool ?

@alan-agius4
Copy link
Contributor

alan-agius4 commented Jan 2, 2020

@hpyou, angular-srduuh is the reproduction you shared. I am unable to reproduce what you are seeing with the reproduction you provided when using the --prod flag.

alan@Alans-iMac angular-srduuh % grep -i jigsaw-box dist/demo/main.79ca699262c3864e5488.js   
alan@Alans-iMac angular-srduuh % 
alan@Alans-iMac angular-srduuh % grep -i jigsaw-block dist/demo/main.79ca699262c3864e5488.js
alan@Alans-iMac angular-srduuh % 
alan@Alans-iMac angular-srduuh % grep -i trustedHtml dist/demo/main.79ca699262c3864e5488.js
alan@Alans-iMac angular-srduuh % 

@hpyou
Copy link
Author

hpyou commented Jan 2, 2020

@alan-agius4 finally i find one config cause the tree-shaken not work

image

it's the property target of tsconfig.json, when i set it to es2015, ng-cli will build two main.js in output, and the tree-shaken can't shake all useless code clear . when i set es5, it works ok.

image

this config is create by ng new my-app in ng9 cli, it maybe should to be modified.

@alan-agius4
Copy link
Contributor

alan-agius4 commented Jan 2, 2020

Okay, I managed to replicate it, and the problem seems to be caused by NGCC, which is inserting the code in an incorrect position, which causes the creation of an EmptyStatement that Build Optimizer is not able to handle.

Given the following input

var JigsawTrustedHtml_1;
// @dynamic
let JigsawTrustedHtml = JigsawTrustedHtml_1 = class JigsawTrustedHtml {
    //====================================================
    constructor(_sanitizer, zone) {
        this._sanitizer = _sanitizer;
        this._initialized = false;
        this._registeredContexts = [];
        if (!window.hasOwnProperty('_jigsawInternalCallbackWrapper') || !(window['_jigsawInternalCallbackWrapper'] instanceof Function)) {
            window['_jigsawInternalCallbackWrapper'] = JigsawTrustedHtml_1._jigsawInternalCallbackWrapper;
        }
        JigsawTrustedHtml_1._zone = zone;
    }
    static _getContext(magicNumber) {
        return JigsawTrustedHtml_1._contexts[magicNumber];
    }
    static _registerContext(context) {
        if (CommonUtils.isUndefined(context)) {
            return -1;
        }
        const index = JigsawTrustedHtml_1._contexts.findIndex(i => i && i.context === context);
        let info = JigsawTrustedHtml_1._contexts[index];
        if (CommonUtils.isUndefined(info)) {
            info = { context: context, counter: 1 };
            JigsawTrustedHtml_1._contexts.push(info);
        }
        else {
            info.counter++;
        }
        return index;
    }
    static _jigsawInternalCallbackWrapper(callbackName, contextMagicNumber, ...args) {
        const contextInfo = JigsawTrustedHtml_1._getContext(contextMagicNumber);
        if (CommonUtils.isUndefined(contextInfo)) {
            console.error('no context found by magic number, callbackName = ' + callbackName);
            return;
        }
        const callbacks = JigsawTrustedHtml_1._callbacks.get(contextInfo.context);
        if (CommonUtils.isUndefined(callbacks)) {
            console.error('no callback cache info found by magic number, callbackName = ' + callbackName);
            return;
        }
        const callback = callbacks[callbackName];
        if (!(callback instanceof Function)) {
            console.error(`no callback function named "${callbackName}" found`);
            console.log(`Hint: add a member method named ${callbackName} to the context object.`);
            return;
        }
        JigsawTrustedHtml_1._zone.run(() => CommonUtils.safeInvokeCallback(contextInfo.context, callback, args));
    }
    static _declareCallback(context, name, callback) {
        if (CommonUtils.isUndefined(context)) {
            console.error(`invalid context for callback "{$name}"`);
            return;
        }
        if (CommonUtils.isUndefined(callback)) {
            console.error(`invalid callback "${name}", it is undefined.`);
            return;
        }
        if (!(callback instanceof Function)) {
            console.error(`invalid callback "${name}", it is not a function.`);
            return;
        }
        let callbacks = JigsawTrustedHtml_1._callbacks.get(context);
        if (CommonUtils.isUndefined(callbacks)) {
            callbacks = {};
            JigsawTrustedHtml_1._callbacks.set(context, callbacks);
        }
        callbacks[name] = callback;
    }
    static _clearCallbacks(context) {
        const idx = JigsawTrustedHtml_1._contexts.findIndex(i => i && i.context === context);
        if (idx == -1) {
            return;
        }
        const info = JigsawTrustedHtml_1._contexts[idx];
        info.counter--;
        if (info.counter == 0) {
            JigsawTrustedHtml_1._callbacks.delete(context);
            info.context = null;
            info.counter = -1;
            JigsawTrustedHtml_1._contexts[idx] = null;
        }
    }
    get trustedHtmlContext() {
        return this._trustedHtmlContext;
    }
    set trustedHtmlContext(value) {
        if (CommonUtils.isUndefined(value)) {
            return;
        }
        this._trustedHtmlContext = value;
        this._updateHtml();
    }
    get trustedHtml() {
        return this._trustedHtml;
    }
    set trustedHtml(value) {
        this._trustedHtml = CommonUtils.isDefined(value) ? value.trim() : '';
        this._updateHtml();
    }
    _updateHtml() {
        if (!this._trustedHtml || !this._initialized) {
            return;
        }
        const modifiedHtml = !this._trustedHtmlContext ? this._trustedHtml : this._trustedHtml
            .replace(/(on|\()(\w+)\)?\s*=(['"])\s*([_$a-z][_$a-z0-9.]*)\s*\((.*?)\)/ig, (found, prefix, event, quot, funcAccessor, args) => this._replacer(`on${event}=${quot}`, funcAccessor, args))
            .replace(/(javascript\s*:)\s*([_$a-z][_$a-z0-9]*)\s*\((.*?)\)/ig, (found, jsPrefix, funcAccessor, args) => this._replacer(jsPrefix, funcAccessor, args));
        if (modifiedHtml != this._modifiedHtml || !this._safeHtml) {
            this._modifiedHtml = modifiedHtml;
            this._safeHtml = this._sanitizer.bypassSecurityTrustHtml(modifiedHtml);
        }
    }
    _replacer(prefix, funcAccessor, args) {
        const [realContext, callback] = this._getCallback(this._trustedHtmlContext, funcAccessor);
        const magicNumber = this._registerContext(realContext);
        JigsawTrustedHtml_1._declareCallback(realContext, funcAccessor, callback);
        const modified = `${prefix}_jigsawInternalCallbackWrapper("${funcAccessor}",${magicNumber}`;
        args = CommonUtils.isDefined(args) ? args.trim() : '';
        return modified + (!!args ? ',' + args + ')' : ')');
    }
    _getCallback(context, accessor) {
        if (CommonUtils.isUndefined(context) || CommonUtils.isUndefined(accessor)) {
            return [null, null];
        }
        const accessors = accessor instanceof Array ? accessor : accessor.split(/\./);
        if (accessors[0] == 'this') {
            // 例如 this.aa.bb() 这里的this指context本身,要跳过
            // 这里可能会误伤这样的情况 this.aa.this.bb(),因为实际情况下是不可能有这样的属性链的,因此无视这个情况
            accessors.shift();
        }
        const tmp = context[accessors[0]];
        if (accessors.length == 1) {
            return [context, tmp];
        }
        accessors.shift();
        return this._getCallback(tmp, accessors);
    }
    _registerContext(context) {
        if (CommonUtils.isUndefined(context)) {
            return -1;
        }
        if (!this._registeredContexts.find(ctx => ctx === context)) {
            JigsawTrustedHtml_1._registerContext(context);
            this._registeredContexts.push(context);
        }
        return this._getMagicNumber(context);
    }
    _getMagicNumber(context) {
        return JigsawTrustedHtml_1._contexts.findIndex(i => i && i.context === context);
    }
    get innerHtml() {
        return this._safeHtml ? this._safeHtml : '';
    }
    ngOnInit() {
        this._initialized = true;
        this._updateHtml();
    }
    ngOnDestroy() {
        this._registeredContexts.forEach(ctx => JigsawTrustedHtml_1._clearCallbacks(ctx));
        this._registeredContexts = null;
        this._trustedHtmlContext = null;
        this._trustedHtml = null;
        this._safeHtml = null;
    }
};
JigsawTrustedHtml._callbacks = new Map();
JigsawTrustedHtml._contexts = [];
JigsawTrustedHtml.ctorParameters = () => [
    { type: DomSanitizer },
    { type: NgZone }
];
__decorate([
    Input(),
    __metadata("design:type", Object),
    __metadata("design:paramtypes", [Object])
], JigsawTrustedHtml.prototype, "trustedHtmlContext", null);
__decorate([
    Input(),
    __metadata("design:type", String),
    __metadata("design:paramtypes", [String])
], JigsawTrustedHtml.prototype, "trustedHtml", null);
__decorate([
    HostBinding('innerHtml'),
    __metadata("design:type", Object),
    __metadata("design:paramtypes", [])
], JigsawTrustedHtml.prototype, "innerHtml", null);
JigsawTrustedHtml = JigsawTrustedHtml_1 = __decorate([
    Directive({
        selector: '[trustedHtml]',
    }),
    __metadata("design:paramtypes", [DomSanitizer, NgZone])
], JigsawTrustedHtml);
let JigsawTrustedHtmlModule = class JigsawTrustedHtmlModule {
};

NGCC will produce the following output

var JigsawTrustedHtml_1;
// @dynamic
let JigsawTrustedHtml = JigsawTrustedHtml_1 = class JigsawTrustedHtml {
    //====================================================
    constructor(_sanitizer, zone) {
        this._sanitizer = _sanitizer;
        this._initialized = false;
        this._registeredContexts = [];
        if (!window.hasOwnProperty('_jigsawInternalCallbackWrapper') || !(window['_jigsawInternalCallbackWrapper'] instanceof Function)) {
            window['_jigsawInternalCallbackWrapper'] = JigsawTrustedHtml_1._jigsawInternalCallbackWrapper;
        }
        JigsawTrustedHtml_1._zone = zone;
    }
    static _getContext(magicNumber) {
        return JigsawTrustedHtml_1._contexts[magicNumber];
    }
    static _registerContext(context) {
        if (CommonUtils.isUndefined(context)) {
            return -1;
        }
        const index = JigsawTrustedHtml_1._contexts.findIndex(i => i && i.context === context);
        let info = JigsawTrustedHtml_1._contexts[index];
        if (CommonUtils.isUndefined(info)) {
            info = { context: context, counter: 1 };
            JigsawTrustedHtml_1._contexts.push(info);
        }
        else {
            info.counter++;
        }
        return index;
    }
    static _jigsawInternalCallbackWrapper(callbackName, contextMagicNumber, ...args) {
        const contextInfo = JigsawTrustedHtml_1._getContext(contextMagicNumber);
        if (CommonUtils.isUndefined(contextInfo)) {
            console.error('no context found by magic number, callbackName = ' + callbackName);
            return;
        }
        const callbacks = JigsawTrustedHtml_1._callbacks.get(contextInfo.context);
        if (CommonUtils.isUndefined(callbacks)) {
            console.error('no callback cache info found by magic number, callbackName = ' + callbackName);
            return;
        }
        const callback = callbacks[callbackName];
        if (!(callback instanceof Function)) {
            console.error(`no callback function named "${callbackName}" found`);
            console.log(`Hint: add a member method named ${callbackName} to the context object.`);
            return;
        }
        JigsawTrustedHtml_1._zone.run(() => CommonUtils.safeInvokeCallback(contextInfo.context, callback, args));
    }
    static _declareCallback(context, name, callback) {
        if (CommonUtils.isUndefined(context)) {
            console.error(`invalid context for callback "{$name}"`);
            return;
        }
        if (CommonUtils.isUndefined(callback)) {
            console.error(`invalid callback "${name}", it is undefined.`);
            return;
        }
        if (!(callback instanceof Function)) {
            console.error(`invalid callback "${name}", it is not a function.`);
            return;
        }
        let callbacks = JigsawTrustedHtml_1._callbacks.get(context);
        if (CommonUtils.isUndefined(callbacks)) {
            callbacks = {};
            JigsawTrustedHtml_1._callbacks.set(context, callbacks);
        }
        callbacks[name] = callback;
    }
    static _clearCallbacks(context) {
        const idx = JigsawTrustedHtml_1._contexts.findIndex(i => i && i.context === context);
        if (idx == -1) {
            return;
        }
        const info = JigsawTrustedHtml_1._contexts[idx];
        info.counter--;
        if (info.counter == 0) {
            JigsawTrustedHtml_1._callbacks.delete(context);
            info.context = null;
            info.counter = -1;
            JigsawTrustedHtml_1._contexts[idx] = null;
        }
    }
    get trustedHtmlContext() {
        return this._trustedHtmlContext;
    }
    set trustedHtmlContext(value) {
        if (CommonUtils.isUndefined(value)) {
            return;
        }
        this._trustedHtmlContext = value;
        this._updateHtml();
    }
    get trustedHtml() {
        return this._trustedHtml;
    }
    set trustedHtml(value) {
        this._trustedHtml = CommonUtils.isDefined(value) ? value.trim() : '';
        this._updateHtml();
    }
    _updateHtml() {
        if (!this._trustedHtml || !this._initialized) {
            return;
        }
        const modifiedHtml = !this._trustedHtmlContext ? this._trustedHtml : this._trustedHtml
            .replace(/(on|\()(\w+)\)?\s*=(['"])\s*([_$a-z][_$a-z0-9.]*)\s*\((.*?)\)/ig, (found, prefix, event, quot, funcAccessor, args) => this._replacer(`on${event}=${quot}`, funcAccessor, args))
            .replace(/(javascript\s*:)\s*([_$a-z][_$a-z0-9]*)\s*\((.*?)\)/ig, (found, jsPrefix, funcAccessor, args) => this._replacer(jsPrefix, funcAccessor, args));
        if (modifiedHtml != this._modifiedHtml || !this._safeHtml) {
            this._modifiedHtml = modifiedHtml;
            this._safeHtml = this._sanitizer.bypassSecurityTrustHtml(modifiedHtml);
        }
    }
    _replacer(prefix, funcAccessor, args) {
        const [realContext, callback] = this._getCallback(this._trustedHtmlContext, funcAccessor);
        const magicNumber = this._registerContext(realContext);
        JigsawTrustedHtml_1._declareCallback(realContext, funcAccessor, callback);
        const modified = `${prefix}_jigsawInternalCallbackWrapper("${funcAccessor}",${magicNumber}`;
        args = CommonUtils.isDefined(args) ? args.trim() : '';
        return modified + (!!args ? ',' + args + ')' : ')');
    }
    _getCallback(context, accessor) {
        if (CommonUtils.isUndefined(context) || CommonUtils.isUndefined(accessor)) {
            return [null, null];
        }
        const accessors = accessor instanceof Array ? accessor : accessor.split(/\./);
        if (accessors[0] == 'this') {
            // 例如 this.aa.bb() 这里的this指context本身,要跳过
            // 这里可能会误伤这样的情况 this.aa.this.bb(),因为实际情况下是不可能有这样的属性链的,因此无视这个情况
            accessors.shift();
        }
        const tmp = context[accessors[0]];
        if (accessors.length == 1) {
            return [context, tmp];
        }
        accessors.shift();
        return this._getCallback(tmp, accessors);
    }
    _registerContext(context) {
        if (CommonUtils.isUndefined(context)) {
            return -1;
        }
        if (!this._registeredContexts.find(ctx => ctx === context)) {
            JigsawTrustedHtml_1._registerContext(context);
            this._registeredContexts.push(context);
        }
        return this._getMagicNumber(context);
    }
    _getMagicNumber(context) {
        return JigsawTrustedHtml_1._contexts.findIndex(i => i && i.context === context);
    }
    get innerHtml() {
        return this._safeHtml ? this._safeHtml : '';
    }
    ngOnInit() {
        this._initialized = true;
        this._updateHtml();
    }
    ngOnDestroy() {
        this._registeredContexts.forEach(ctx => JigsawTrustedHtml_1._clearCallbacks(ctx));
        this._registeredContexts = null;
        this._trustedHtmlContext = null;
        this._trustedHtml = null;
        this._safeHtml = null;
    }
}
JigsawTrustedHtml.ɵfac = function JigsawTrustedHtml_Factory(t) { return new (t || JigsawTrustedHtml)(ɵngcc0.ɵɵdirectiveInject(ɵngcc3.DomSanitizer), ɵngcc0.ɵɵdirectiveInject(ɵngcc0.NgZone)); };
JigsawTrustedHtml.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: JigsawTrustedHtml, selectors: [["", "trustedHtml", ""]], hostBindings: function JigsawTrustedHtml_HostBindings(rf, ctx, elIndex) { if (rf & 1) {
        ɵngcc0.ɵɵallocHostVars(1);
    } if (rf & 2) {
        ɵngcc0.ɵɵhostProperty("innerHtml", ctx.innerHtml, ɵngcc0.ɵɵsanitizeHtml);
    } }, inputs: { trustedHtmlContext: "trustedHtmlContext", trustedHtml: "trustedHtml" } });;
JigsawTrustedHtml._callbacks = new Map();
JigsawTrustedHtml._contexts = [];
JigsawTrustedHtml.ctorParameters = () => [
    { type: DomSanitizer },
    { type: NgZone }
];
__decorate([
    Input(),
    __metadata("design:type", Object),
    __metadata("design:paramtypes", [Object])
], JigsawTrustedHtml.prototype, "trustedHtmlContext", null);
__decorate([
    Input(),
    __metadata("design:type", String),
    __metadata("design:paramtypes", [String])
], JigsawTrustedHtml.prototype, "trustedHtml", null);
__decorate([
    HostBinding('innerHtml'),
    __metadata("design:type", Object),
    __metadata("design:paramtypes", [])
], JigsawTrustedHtml.prototype, "innerHtml", null);
JigsawTrustedHtml = JigsawTrustedHtml_1 = __decorate([ __metadata("design:paramtypes", [DomSanitizer, NgZone])
], JigsawTrustedHtml);

The interesting parts are;

JigsawTrustedHtml.ɵfac = function JigsawTrustedHtml_Factory(t) { return new (t || JigsawTrustedHtml)(ɵngcc0.ɵɵdirectiveInject(ɵngcc3.DomSanitizer), ɵngcc0.ɵɵdirectiveInject(ɵngcc0.NgZone)); };
JigsawTrustedHtml.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: JigsawTrustedHtml, selectors: [["", "trustedHtml", ""]], hostBindings: function JigsawTrustedHtml_HostBindings(rf, ctx, elIndex) { if (rf & 1) {
        ɵngcc0.ɵɵallocHostVars(1);
    } if (rf & 2) {
        ɵngcc0.ɵɵhostProperty("innerHtml", ctx.innerHtml, ɵngcc0.ɵɵsanitizeHtml);
    } }, inputs: { trustedHtmlContext: "trustedHtmlContext", trustedHtml: "trustedHtml" } });;

Were we can see that the above statements were inserted before the last ; and in the end we are having a double ;; which in terms of AST will be equal to an EmptyStatement.

There are two solutions:

  1. We handle EmptyStatement in Build Optimizer
  2. NGCC compiler inserts ɵdir and ɵfac right before the last ; when present.

@alan-agius4 alan-agius4 self-assigned this Jan 2, 2020
@alan-agius4
Copy link
Contributor

Update: we decided that we should implement both of the above suggestions.

mgechev referenced this issue in angular/angular-cli Jan 4, 2020
…y statements (#16538)

NGCC currently produces empty statments which breaks class wrapping.

Closes #16509
mgechev referenced this issue in angular/angular-cli Jan 4, 2020
…y statements (#16538)

NGCC currently produces empty statments which breaks class wrapping.

Closes #16509
@alan-agius4
Copy link
Contributor

Transferring this to the FW repo for their part.

IE: NGCC compiler inserts ɵdir and ɵfac right before the last ; when present.

@alan-agius4 alan-agius4 reopened this Jan 6, 2020
@alan-agius4 alan-agius4 transferred this issue from angular/angular-cli Jan 6, 2020
@ngbot ngbot bot added this to the needsTriage milestone Jan 6, 2020
@alan-agius4 alan-agius4 removed their assignment Jan 6, 2020
@petebacondarwin
Copy link
Contributor

To elucidate, in ES2015 formats, where the class is defined via a let statement, ngcc is incorrectly taking the class expression, rather than the variable declaration statement as the node to use for inserting definitions. This should be a simple fix in EsmRenderingFormatter.addDefinitions().

@ngbot ngbot bot modified the milestones: needsTriage, Backlog Jan 6, 2020
@petebacondarwin petebacondarwin modified the milestones: Backlog, v9-candidates Jan 6, 2020
@alxhub alxhub modified the milestones: v9-candidates, v9-blockers Jan 7, 2020
petebacondarwin added a commit to petebacondarwin/angular that referenced this issue Jan 8, 2020
If a class was defined as a class expression
in a variable declaration, the definitions
were being inserted before the statment's
final semi-colon.

Now the insertion point will be after the
full statement.

Fixes angular#34648
alxhub pushed a commit that referenced this issue Jan 8, 2020
If a class was defined as a class expression
in a variable declaration, the definitions
were being inserted before the statment's
final semi-colon.

Now the insertion point will be after the
full statement.

Fixes #34648

PR Close #34677
@alxhub alxhub closed this as completed in 8815ace Jan 8, 2020
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Feb 8, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants