Skip to content
This repository was archived by the owner on May 1, 2020. It is now read-only.

Commit 8f49908

Browse files
committed
feature(optimization): purge ctor parameters static field from all non-entry components in ionic and angular
1 parent 254bd8a commit 8f49908

File tree

7 files changed

+202
-23
lines changed

7 files changed

+202
-23
lines changed

src/optimization.spec.ts

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ describe('optimization task', () => {
1919

2020
spyOn(helpers, helpers.getBooleanPropertyValue.name).and.returnValue(false);
2121
spyOn(decorators, decorators.purgeStaticFieldDecorators.name);
22+
spyOn(decorators, decorators.purgeStaticCtorFields.name);
2223
spyOn(decorators, decorators.purgeTranspiledDecorators.name);
2324
spyOn(treeshake, treeshake.calculateUnusedComponents.name);
2425

@@ -28,6 +29,7 @@ describe('optimization task', () => {
2829
// assert
2930
expect(result).toBeTruthy();
3031
expect(decorators.purgeStaticFieldDecorators).not.toHaveBeenCalled();
32+
expect(decorators.purgeStaticCtorFields).not.toHaveBeenCalled();
3133
expect(decorators.purgeTranspiledDecorators).not.toHaveBeenCalled();
3234
expect(treeshake.calculateUnusedComponents).not.toHaveBeenCalled();
3335
});

src/optimization.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { BuildError } from './util/errors';
99
import { getBooleanPropertyValue, getStringPropertyValue, webpackStatsToDependencyMap, printDependencyMap } from './util/helpers';
1010
import { BuildContext, TaskInfo } from './util/interfaces';
1111
import { runWebpackFullBuild, WebpackConfig } from './webpack';
12-
import { addPureAnnotation, purgeStaticFieldDecorators, purgeTranspiledDecorators } from './optimization/decorators';
12+
import { addPureAnnotation, purgeStaticCtorFields, purgeStaticFieldDecorators, purgeTranspiledDecorators } from './optimization/decorators';
1313
import { getAppModuleNgFactoryPath, calculateUnusedComponents, purgeUnusedImportsAndExportsFromIndex, purgeComponentNgFactoryImportAndUsage, purgeProviderControllerImportAndUsage, purgeProviderClassNameFromIonicModuleForRoot } from './optimization/treeshake';
1414

1515
export function optimization(context: BuildContext, configFile: string) {
@@ -83,6 +83,7 @@ function removeDecorators(context: BuildContext) {
8383
jsFiles.forEach(jsFile => {
8484
let magicString = new MagicString(jsFile.content);
8585
magicString = purgeStaticFieldDecorators(jsFile.path, jsFile.content, getStringPropertyValue(Constants.ENV_VAR_IONIC_ANGULAR_DIR), getStringPropertyValue(Constants.ENV_VAR_AT_ANGULAR_DIR), context.srcDir, magicString);
86+
magicString = purgeStaticCtorFields(jsFile.path, jsFile.content, getStringPropertyValue(Constants.ENV_VAR_IONIC_ANGULAR_DIR), getStringPropertyValue(Constants.ENV_VAR_AT_ANGULAR_DIR), context.srcDir, magicString);
8687
magicString = purgeTranspiledDecorators(jsFile.path, jsFile.content, getStringPropertyValue(Constants.ENV_VAR_IONIC_ANGULAR_DIR), getStringPropertyValue(Constants.ENV_VAR_AT_ANGULAR_DIR), context.srcDir, magicString);
8788
magicString = addPureAnnotation(jsFile.path, jsFile.content, getStringPropertyValue(Constants.ENV_VAR_IONIC_ANGULAR_DIR), getStringPropertyValue(Constants.ENV_VAR_AT_ANGULAR_DIR), context.srcDir, magicString);
8889
jsFile.content = magicString.toString();

src/optimization/decorators.spec.ts

+114-20
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const srcDir = join(baseDir, 'src');
1212
describe('optimization', () => {
1313
describe('purgeStaticFieldDecorators', () => {
1414

15-
it('should remove the static decorator', () => {
15+
it('should remove the static decorators', () => {
1616
// arrange
1717
const decoratorStatement = `
1818
import { Taco } from 'blah';
@@ -252,7 +252,7 @@ some more content
252252
});
253253

254254
it('should not remove decorators when it has an injectable statement in it', () => {
255-
const knownContent = `
255+
const knownContent = `
256256
var ActionSheetController = (function () {
257257
/**
258258
* @param {?} _app
@@ -294,7 +294,7 @@ ActionSheetController.ctorParameters = function () { return [
294294
});
295295

296296
it('should work with the ionic-angular index file', () => {
297-
const ionicModuleDecorator = `
297+
const ionicModuleDecorator = `
298298
IonicModule.decorators = [
299299
{ type: NgModule, args: [{
300300
imports: [
@@ -408,7 +408,7 @@ IonicModule.decorators = [
408408
},] },
409409
];
410410
`;
411-
const knownContent = `
411+
const knownContent = `
412412
import { ANALYZE_FOR_ENTRY_COMPONENTS, APP_INITIALIZER, ComponentFactoryResolver, Inject, Injector, NgModule, NgZone, Optional } from '@angular/core';
413413
import { APP_BASE_HREF, Location, LocationStrategy, HashLocationStrategy, PathLocationStrategy, PlatformLocation } from '@angular/common';
414414
import { DOCUMENT } from '@angular/platform-browser';
@@ -782,14 +782,14 @@ export function provideLocationStrategy(platformLocationStrategy, baseHref, conf
782782
expect(result.indexOf(ionicModuleDecorator)).toEqual(-1);
783783
});
784784

785-
it('shoud process component file correctly', () => {
785+
it('should process component file correctly', () => {
786786

787-
const propDecorators = `
787+
const propDecorators = `
788788
ActionSheetCmp.propDecorators = {
789789
'keyUp': [{ type: HostListener, args: ['body:keyup', ['$event'],] },],
790790
};
791791
`;
792-
const decoratorContent = `
792+
const decoratorContent = `
793793
ActionSheetCmp.decorators = [
794794
{ type: Component, args: [{
795795
selector: 'ion-action-sheet',
@@ -821,7 +821,7 @@ ActionSheetCmp.decorators = [
821821
},] },
822822
];
823823
`;
824-
const knownContent = `
824+
const knownContent = `
825825
import { Component, ElementRef, HostListener, Renderer, ViewEncapsulation } from '@angular/core';
826826
import { GestureController, BLOCK_ALL } from '../../gestures/gesture-controller';
827827
import { Config } from '../../config/config';
@@ -1003,42 +1003,42 @@ let actionSheetIds = -1;
10031003
describe('purgeTranspiledDecorators', () => {
10041004
it('should purge out transpiled decorators', () => {
10051005

1006-
const inputDecorator = `
1006+
const inputDecorator = `
10071007
__decorate([
10081008
Input(),
10091009
__metadata("design:type", String)
10101010
], AboutPage.prototype, "someVariable", void 0);
10111011
`;
10121012

1013-
const outputDecorator = `
1013+
const outputDecorator = `
10141014
__decorate([
10151015
Output(),
10161016
__metadata("design:type", typeof (_a = typeof EventEmitter !== "undefined" && EventEmitter) === "function" && _a || Object)
10171017
], AboutPage.prototype, "emitter", void 0);
10181018
`;
10191019

1020-
const viewChildDecorator = `
1020+
const viewChildDecorator = `
10211021
__decorate([
10221022
ViewChild('test', { read: ElementRef }),
10231023
__metadata("design:type", Object)
10241024
], AboutPage.prototype, "test", void 0);
10251025
`;
10261026

1027-
const viewChildrenDecorator = `
1027+
const viewChildrenDecorator = `
10281028
__decorate([
10291029
ViewChildren('test'),
10301030
__metadata("design:type", Object)
10311031
], AboutPage.prototype, "tests", void 0);
10321032
`;
10331033

1034-
const hostBindingDecorator = `
1034+
const hostBindingDecorator = `
10351035
__decorate([
10361036
HostBinding('class.searchbar-has-focus'),
10371037
__metadata("design:type", Boolean)
10381038
], AboutPage.prototype, "_sbHasFocus", void 0);
10391039
`;
10401040

1041-
const hostListenerDecorator = `
1041+
const hostListenerDecorator = `
10421042
__decorate([
10431043
HostListener('click', ['$event']),
10441044
__metadata("design:type", Function),
@@ -1047,7 +1047,7 @@ __decorate([
10471047
], AboutPage.prototype, "someFunction", null);
10481048
`;
10491049

1050-
const classDecorators = `
1050+
const classDecorators = `
10511051
AboutPage = __decorate([
10521052
IonicPage(),
10531053
Component({
@@ -1058,7 +1058,7 @@ AboutPage = __decorate([
10581058
], AboutPage);
10591059
`;
10601060

1061-
const knownContent = `
1061+
const knownContent = `
10621062
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
10631063
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
10641064
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -1113,14 +1113,14 @@ var _a, _b, _c;
11131113

11141114
it('should not purge any injectable decorators', () => {
11151115

1116-
const injectableDecorator = `
1116+
const injectableDecorator = `
11171117
ConferenceData = __decorate([
11181118
Injectable(),
11191119
__metadata("design:paramtypes", [typeof (_a = typeof Http !== "undefined" && Http) === "function" && _a || Object, typeof (_b = typeof UserData !== "undefined" && UserData) === "function" && _b || Object])
11201120
], ConferenceData);
11211121
`;
11221122

1123-
const knownContent = `
1123+
const knownContent = `
11241124
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
11251125
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
11261126
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -1283,7 +1283,7 @@ var _a, _b;
12831283

12841284
describe('addPureAnnotation', () => {
12851285
it('should add the pure annotation to a transpiled class', () => {
1286-
const knownContent = `
1286+
const knownContent = `
12871287
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
12881288
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
12891289
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -1407,7 +1407,7 @@ function CardContent_tsickle_Closure_declarations() {
14071407
//# sourceMappingURL=card-content.js.map
14081408
`;
14091409

1410-
const expectedContent = `
1410+
const expectedContent = `
14111411
var __extends = (this && this.__extends) || (function () {
14121412
var extendStatics = Object.setPrototypeOf ||
14131413
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
@@ -1464,4 +1464,98 @@ function CardContent_tsickle_Closure_declarations() {
14641464
expect(result).toEqual(expectedContent);
14651465
});
14661466
});
1467+
1468+
describe('purgeStaticCtorFields', () => {
1469+
it('should purge the ctor field', () => {
1470+
1471+
const ctorParams = `
1472+
Badge.ctorParameters = function () { return [
1473+
{ type: Config, },
1474+
{ type: ElementRef, },
1475+
{ type: Renderer, },
1476+
]; };
1477+
`;
1478+
const knownContent = `
1479+
var __extends = (this && this.__extends) || (function () {
1480+
var extendStatics = Object.setPrototypeOf ||
1481+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
1482+
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
1483+
return function (d, b) {
1484+
extendStatics(d, b);
1485+
function __() { this.constructor = d; }
1486+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
1487+
};
1488+
})();
1489+
import { Directive, ElementRef, Renderer } from '@angular/core';
1490+
import { Config } from '../../config/config';
1491+
import { Ion } from '../ion';
1492+
1493+
var Badge = (function (_super) {
1494+
__extends(Badge, _super);
1495+
1496+
function Badge(config, elementRef, renderer) {
1497+
return _super.call(this, config, elementRef, renderer, 'badge') || this;
1498+
}
1499+
return Badge;
1500+
}(Ion));
1501+
export { Badge };
1502+
Badge.decorators = [
1503+
{ type: Directive, args: [{
1504+
selector: 'ion-badge'
1505+
},] },
1506+
];
1507+
1508+
${ctorParams}
1509+
function Badge_tsickle_Closure_declarations() {
1510+
1511+
Badge.decorators;
1512+
1513+
Badge.ctorParameters;
1514+
}
1515+
//# sourceMappingURL=badge.js.map
1516+
`;
1517+
1518+
1519+
1520+
let magicString = new MagicString(knownContent);
1521+
const filePath = join(ionicAngular, 'components', 'badge', 'badge.js');
1522+
magicString = decorators.purgeStaticCtorFields(filePath, knownContent, ionicAngular, angularDir, srcDir, magicString);
1523+
const result: string = magicString.toString();
1524+
expect(result.indexOf(ctorParams)).toEqual(-1);
1525+
});
1526+
1527+
it('should purge an empty ctor field', () => {
1528+
const ctorParams = `
1529+
Avatar.ctorParameters = function () { return []; };
1530+
`;
1531+
const knownContent = `
1532+
1533+
var Avatar = (function () {
1534+
function Avatar() {
1535+
}
1536+
return Avatar;
1537+
}());
1538+
export { Avatar };
1539+
Avatar.decorators = [
1540+
{ type: Directive, args: [{
1541+
selector: 'ion-avatar'
1542+
},] },
1543+
];
1544+
1545+
${ctorParams}
1546+
function Avatar_tsickle_Closure_declarations() {
1547+
Avatar.decorators;
1548+
1549+
Avatar.ctorParameters;
1550+
}
1551+
//# sourceMappingURL=avatar.js.map
1552+
`;
1553+
1554+
let magicString = new MagicString(knownContent);
1555+
const filePath = join(ionicAngular, 'components', 'badge', 'badge.js');
1556+
magicString = decorators.purgeStaticCtorFields(filePath, knownContent, ionicAngular, angularDir, srcDir, magicString);
1557+
const result: string = magicString.toString();
1558+
expect(result.indexOf(ctorParams)).toEqual(-1);
1559+
});
1560+
});
14671561
});

src/optimization/decorators.ts

+57-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ import {
1313
SyntaxKind } from 'typescript';
1414

1515
import { Logger } from '../logger/logger';
16+
import * as Constants from '../util/constants';
17+
import { getStringPropertyValue } from '../util/helpers';
1618
import { MagicString } from '../util/interfaces';
17-
import { findNodes, getTypescriptSourceFile } from '../util/typescript-utils';
19+
import { getNodeStringContent, findNodes, getTypescriptSourceFile } from '../util/typescript-utils';
1820

1921
export function addPureAnnotation(filePath: string, originalFileContent: string, ionicAngularDir: string, angularDir: string, srcDir: string, magicString: MagicString) {
2022
Logger.debug(`[decorators] addPureAnnotation: processing ${filePath} ...`);
@@ -41,7 +43,6 @@ export function addPureAnnotation(filePath: string, originalFileContent: string,
4143
magicString.prependLeft(parenthesizedExpression.pos, PURE_ANNOTATION);
4244

4345
}
44-
4546
}
4647
});
4748
return magicString;
@@ -102,15 +103,69 @@ export function purgeStaticFieldDecorators(filePath: string, originalFileContent
102103
if (filePath.indexOf(angularDir) >= 0 || filePath.indexOf(ionicAngularDir) >= 0 || filePath.indexOf(srcDir) >= 0) {
103104
Logger.debug(`[decorators] purgeStaticFieldDecorators: processing ${filePath} ...`);
104105
const typescriptFile = getTypescriptSourceFile(filePath, originalFileContent);
106+
105107
const decoratorExpressionStatements = getDecoratorsExpressionStatements(typescriptFile);
106108
removeDecorators(decoratorExpressionStatements, magicString);
109+
107110
const propDecoratorsExpressionStatements = getPropDecoratorsExpressionStatements(typescriptFile);
108111
removePropDecorators(propDecoratorsExpressionStatements, magicString);
112+
109113
Logger.debug(`[decorators] purgeStaticFieldDecorators: processing ${filePath} ... DONE`);
110114
}
111115
return magicString;
112116
}
113117

118+
export function purgeStaticCtorFields(filePath: string, originalFileContent: string, ionicAngularDir: string, angularDir: string, srcDir: string, magicString: MagicString) {
119+
// TODO - we could extend this to other libs and stuff too such as material 2, but that doesn't seem
120+
// particularly maintainable
121+
if ((filePath.indexOf(angularDir) >= 0 || filePath.indexOf(ionicAngularDir) >= 0) && !isIonicEntryComponent(filePath)) {
122+
Logger.debug(`[decorators] purgeStaticCtorFields: processing ${filePath} ...`);
123+
const typescriptFile = getTypescriptSourceFile(filePath, originalFileContent);
124+
const expressionStatements = findNodes(typescriptFile, typescriptFile, SyntaxKind.ExpressionStatement, false) as ExpressionStatement[];
125+
const toPurge: ExpressionStatement[] = [];
126+
for (const expressionStatement of expressionStatements) {
127+
if (expressionStatement.expression && expressionStatement.expression.kind === SyntaxKind.BinaryExpression
128+
&& (expressionStatement.expression as BinaryExpression).left
129+
&& (expressionStatement.expression as BinaryExpression).left.kind === SyntaxKind.PropertyAccessExpression
130+
&& ((expressionStatement.expression as BinaryExpression).left as PropertyAccessExpression).name
131+
&& ((expressionStatement.expression as BinaryExpression).left as PropertyAccessExpression).name.text === 'ctorParameters'
132+
) {
133+
134+
toPurge.push(expressionStatement);
135+
136+
}
137+
}
138+
139+
toPurge.forEach(tsNode => {
140+
magicString.overwrite(tsNode.pos, tsNode.end, '');
141+
});
142+
143+
Logger.debug(`[decorators] purgeStaticFieldDecorators: processing ${filePath} ... DONE`);
144+
}
145+
return magicString;
146+
}
147+
148+
function isIonicEntryComponent(filePath: string) {
149+
if (filePath === getStringPropertyValue(Constants.ENV_ACTION_SHEET_COMPONENT_PATH)) {
150+
return true;
151+
} else if (filePath === getStringPropertyValue(Constants.ENV_ALERT_COMPONENT_PATH)) {
152+
return true;
153+
} else if (filePath === getStringPropertyValue(Constants.ENV_APP_ROOT_COMPONENT_PATH)) {
154+
return true;
155+
} else if (filePath === getStringPropertyValue(Constants.ENV_LOADING_COMPONENT_PATH)) {
156+
return true;
157+
} else if (filePath === getStringPropertyValue(Constants.ENV_MODAL_COMPONENT_PATH)) {
158+
return true;
159+
} else if (filePath === getStringPropertyValue(Constants.ENV_PICKER_COMPONENT_PATH)) {
160+
return true;
161+
} else if (filePath === getStringPropertyValue(Constants.ENV_POPOVER_COMPONENT_PATH)) {
162+
return true;
163+
} else if (filePath === getStringPropertyValue(Constants.ENV_TOAST_COMPONENT_PATH)) {
164+
return true;
165+
}
166+
return false;
167+
}
168+
114169
function getDecoratorsExpressionStatements(typescriptFile: SourceFile) {
115170
const expressionStatements = findNodes(typescriptFile, typescriptFile, SyntaxKind.ExpressionStatement, false) as ExpressionStatement[];
116171
const decoratorExpressionStatements: ExpressionStatement[] = [];

0 commit comments

Comments
 (0)