-
Notifications
You must be signed in to change notification settings - Fork 25.8k
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
Comments
@hpyou which command are you using to build? Are you using the I tried to look at the reproduction and it doesn't seem that the entire library is being retained |
@alan-agius4 I used ng --aot not --prod for looking for the code in bundle, if with --prod code will be uglified. |
The flags which are associated with tree-shaking and dead-code elimination are To disable mangling you can use the NG_BUILD_MANGLE=false ng build --prod |
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. |
In windows try to use |
@alan-agius4 i checked the bundle main-es5.js, it contains some package like and some directive like |
Hi, based on your reproduction alan@Alans-iMac angular-srduuh % grep -i JigsawTrustedHtmlModule dist/demo/main.c77024c7c06ef05e82e6.js
alan@Alans-iMac angular-srduuh % Re |
@hpyou, 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 % |
@alan-agius4 finally i find one config cause the tree-shaken not work it's the property this config is create by |
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 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 There are two solutions:
|
Update: we decided that we should implement both of the above suggestions. |
…y statements (#16538) NGCC currently produces empty statments which breaks class wrapping. Closes #16509
…y statements (#16538) NGCC currently produces empty statments which breaks class wrapping. Closes #16509
Transferring this to the FW repo for their part. IE: NGCC compiler inserts |
To elucidate, in ES2015 formats, where the class is defined via a |
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
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
🐞 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
and i import the module
JigsawButtonModule
which just has the componentJigsawButton
intoAppModule
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
The text was updated successfully, but these errors were encountered: