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

Commit 4b538c7

Browse files
committed
fix(optimizations): fix multiple bugs (components not being purged, overlays not working from providers, etc) for manual tree shaking
1 parent 272205a commit 4b538c7

File tree

6 files changed

+1602
-1340
lines changed

6 files changed

+1602
-1340
lines changed

src/optimization.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ describe('optimization task', () => {
7676
const mockIndexPath = join('some', 'path', 'myApp', 'node_modules', 'ionic-angular', 'index.js');
7777

7878
spyOn(treeshake, treeshake.calculateUnusedComponents.name);
79-
spyOn(treeshake, treeshake.purgeUnusedImportsAndExportsFromIndex.name);
79+
spyOn(treeshake, treeshake.purgeUnusedImportsAndExportsFromModuleFile.name);
8080
spyOn(treeshake, treeshake.purgeComponentNgFactoryImportAndUsage.name);
8181
spyOn(treeshake, treeshake.purgeProviderControllerImportAndUsage.name);
8282
spyOn(treeshake, treeshake.purgeProviderClassNameFromIonicModuleForRoot.name);
@@ -92,7 +92,7 @@ describe('optimization task', () => {
9292
optimization.doOptimizations(context, new Map());
9393

9494
expect(treeshake.calculateUnusedComponents).not.toHaveBeenCalled();
95-
expect(treeshake.purgeUnusedImportsAndExportsFromIndex).not.toHaveBeenCalled();
95+
expect(treeshake.purgeUnusedImportsAndExportsFromModuleFile).not.toHaveBeenCalled();
9696
expect(treeshake.purgeComponentNgFactoryImportAndUsage).not.toHaveBeenCalled();
9797
expect(treeshake.purgeProviderControllerImportAndUsage).not.toHaveBeenCalled();
9898
expect(treeshake.purgeProviderClassNameFromIonicModuleForRoot).not.toHaveBeenCalled();
@@ -108,7 +108,7 @@ describe('optimization task', () => {
108108

109109
spyOn(treeshake, treeshake.getAppModuleNgFactoryPath.name);
110110
spyOn(treeshake, treeshake.calculateUnusedComponents.name).and.returnValue({ purgedModules: new Map()});
111-
spyOn(treeshake, treeshake.purgeUnusedImportsAndExportsFromIndex.name);
111+
spyOn(treeshake, treeshake.purgeUnusedImportsAndExportsFromModuleFile.name);
112112

113113
spyOn(helpers, helpers.getStringPropertyValue.name).and.callFake((propertyName: string) => {
114114
return mockIndexPath;
@@ -127,7 +127,7 @@ describe('optimization task', () => {
127127
optimization.doOptimizations(context, new Map());
128128

129129
expect(treeshake.calculateUnusedComponents).toHaveBeenCalled();
130-
expect(treeshake.purgeUnusedImportsAndExportsFromIndex).toHaveBeenCalled();
130+
expect(treeshake.purgeUnusedImportsAndExportsFromModuleFile).toHaveBeenCalled();
131131
});
132132
});
133133
});

src/optimization.ts

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@ import { Logger } from './logger/logger';
66
import { fillConfigDefaults, getUserConfigFile, replacePathVars } from './util/config';
77
import * as Constants from './util/constants';
88
import { BuildError } from './util/errors';
9-
import { getBooleanPropertyValue, getStringPropertyValue, webpackStatsToDependencyMap, printDependencyMap } from './util/helpers';
9+
import { changeExtension, getBooleanPropertyValue, getStringPropertyValue, webpackStatsToDependencyMap, printDependencyMap } from './util/helpers';
1010
import { BuildContext, TaskInfo } from './util/interfaces';
1111
import { runWebpackFullBuild, WebpackConfig } from './webpack';
1212
import { addPureAnnotation, purgeStaticCtorFields, purgeStaticFieldDecorators, purgeTranspiledDecorators } from './optimization/decorators';
13-
import { getAppModuleNgFactoryPath,
14-
calculateUnusedComponents,
13+
import { calculateUnusedComponents,
14+
checkIfProviderIsUsedInSrc,
1515
getIonicModuleFilePath,
16-
purgeUnusedImportsAndExportsFromIndex,
16+
purgeUnusedImportsAndExportsFromModuleFile,
17+
purgeUnusedExportsFromIndexFile,
1718
purgeComponentNgFactoryImportAndUsage,
1819
purgeProviderControllerImportAndUsage,
1920
purgeProviderClassNameFromIonicModuleForRoot
@@ -59,7 +60,7 @@ export function purgeGeneratedFiles(context: BuildContext, fileNameSuffix: strin
5960

6061
export function doOptimizations(context: BuildContext, dependencyMap: Map<string, Set<string>>) {
6162
// remove decorators
62-
const modifiedMap = new Map(dependencyMap);
63+
let modifiedMap = new Map(dependencyMap);
6364
if (getBooleanPropertyValue(Constants.ENV_PURGE_DECORATORS)) {
6465
removeDecorators(context);
6566
}
@@ -71,6 +72,8 @@ export function doOptimizations(context: BuildContext, dependencyMap: Map<string
7172
// since there is a breaking change here
7273
const ionicModulePath = getIonicModuleFilePath();
7374
if (context.fileCache.get(ionicModulePath)) {
75+
// due to how the angular compiler works in angular 4, we need to check if
76+
modifiedMap = checkIfProviderIsUsedInSrc(context, modifiedMap);
7477
const results = calculateUnusedComponents(modifiedMap);
7578
purgeUnusedImports(context, results.purgedModules);
7679
}
@@ -127,43 +130,57 @@ function purgeUnusedImports(context: BuildContext, purgeDependencyMap: Map<strin
127130
modulesToPurge.push(moduleToPurge);
128131
});
129132

130-
const updatedFileContent = purgeUnusedImportsAndExportsFromIndex(moduleFilePath, moduleFile.content, modulesToPurge);
133+
const updatedFileContent = purgeUnusedImportsAndExportsFromModuleFile(moduleFilePath, moduleFile.content, modulesToPurge);
131134
context.fileCache.set(moduleFilePath, { path: moduleFilePath, content: updatedFileContent });
132135

133-
attemptToPurgeUnusedProvider(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_ACTION_SHEET_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_ACTION_SHEET_VIEW_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_ACTION_SHEET_COMPONENT_FACTORY_PATH), getStringPropertyValue(Constants.ENV_ACTION_SHEET_CONTROLLER_CLASSNAME));
134-
attemptToPurgeUnusedProvider(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_ALERT_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_ALERT_VIEW_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_ALERT_COMPONENT_FACTORY_PATH), getStringPropertyValue(Constants.ENV_ALERT_CONTROLLER_CLASSNAME));
135-
attemptToPurgeUnusedProvider(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_LOADING_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_LOADING_VIEW_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_LOADING_COMPONENT_FACTORY_PATH), getStringPropertyValue(Constants.ENV_LOADING_CONTROLLER_CLASSNAME));
136-
attemptToPurgeUnusedProvider(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_MODAL_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_MODAL_VIEW_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_MODAL_COMPONENT_FACTORY_PATH), getStringPropertyValue(Constants.ENV_MODAL_CONTROLLER_CLASSNAME));
137-
attemptToPurgeUnusedProvider(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_PICKER_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_PICKER_VIEW_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_PICKER_COMPONENT_FACTORY_PATH), getStringPropertyValue(Constants.ENV_PICKER_CONTROLLER_CLASSNAME));
138-
attemptToPurgeUnusedProvider(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_POPOVER_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_POPOVER_VIEW_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_POPOVER_COMPONENT_FACTORY_PATH), getStringPropertyValue(Constants.ENV_POPOVER_CONTROLLER_CLASSNAME));
139-
attemptToPurgeUnusedProvider(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_TOAST_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_TOAST_VIEW_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_TOAST_COMPONENT_FACTORY_PATH), getStringPropertyValue(Constants.ENV_TOAST_CONTROLLER_CLASSNAME));
136+
const updatedIndexContent = purgeUnusedExportsFromIndexFile(file.path, file.content, modulesToPurge);
137+
context.fileCache.set(file.path, { path: file.path, content: updatedIndexContent });
138+
139+
attemptToPurgeUnusedProvider(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_ACTION_SHEET_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_ACTION_SHEET_CONTROLLER_CLASSNAME));
140+
attemptToPurgeUnusedProvider(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_ALERT_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_ALERT_CONTROLLER_CLASSNAME));
141+
attemptToPurgeUnusedProvider(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_LOADING_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_LOADING_CONTROLLER_CLASSNAME));
142+
attemptToPurgeUnusedProvider(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_MODAL_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_MODAL_CONTROLLER_CLASSNAME));
143+
attemptToPurgeUnusedProvider(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_PICKER_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_PICKER_CONTROLLER_CLASSNAME));
144+
attemptToPurgeUnusedProvider(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_POPOVER_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_POPOVER_CONTROLLER_CLASSNAME));
145+
attemptToPurgeUnusedProvider(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_TOAST_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_TOAST_CONTROLLER_CLASSNAME));
146+
147+
attemptToPurgeUnusedEntryComponents(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_ACTION_SHEET_COMPONENT_PATH), getStringPropertyValue(Constants.ENV_ACTION_SHEET_COMPONENT_FACTORY_PATH));
148+
attemptToPurgeUnusedEntryComponents(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_ALERT_COMPONENT_PATH), getStringPropertyValue(Constants.ENV_ALERT_COMPONENT_FACTORY_PATH));
149+
attemptToPurgeUnusedEntryComponents(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_LOADING_COMPONENT_PATH), getStringPropertyValue(Constants.ENV_LOADING_COMPONENT_FACTORY_PATH));
150+
attemptToPurgeUnusedEntryComponents(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_MODAL_COMPONENT_PATH), getStringPropertyValue(Constants.ENV_MODAL_COMPONENT_FACTORY_PATH));
151+
attemptToPurgeUnusedEntryComponents(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_PICKER_COMPONENT_PATH), getStringPropertyValue(Constants.ENV_PICKER_COMPONENT_FACTORY_PATH));
152+
attemptToPurgeUnusedEntryComponents(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_POPOVER_COMPONENT_PATH), getStringPropertyValue(Constants.ENV_POPOVER_COMPONENT_FACTORY_PATH));
153+
attemptToPurgeUnusedEntryComponents(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_TOAST_COMPONENT_PATH), getStringPropertyValue(Constants.ENV_TOAST_COMPONENT_FACTORY_PATH));
154+
attemptToPurgeUnusedEntryComponents(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_SELECT_POPOVER_COMPONENT_PATH), getStringPropertyValue(Constants.ENV_SELECT_POPOVER_COMPONENT_FACTORY_PATH));
140155
}
141156

142-
function attemptToPurgeUnusedProvider(context: BuildContext, dependencyMap: Map<string, Set<string>>, providerPath: string, providerComponentPath: string, providerComponentFactoryPath: string, providerClassName: string) {
157+
function attemptToPurgeUnusedProvider(context: BuildContext, dependencyMap: Map<string, Set<string>>, providerPath: string, providerClassName: string) {
143158
if (dependencyMap.has(providerPath)) {
144-
// awwww yissssssss
145-
146-
// first, get the content of the app module ngfactory file
147-
const appModuleNgFactoryPath = getAppModuleNgFactoryPath();
148-
const file = context.fileCache.get(appModuleNgFactoryPath);
149-
if (!file) {
150-
return;
151-
}
152-
153-
let updatedContent = purgeComponentNgFactoryImportAndUsage(file.path, file.content, providerComponentFactoryPath);
154-
updatedContent = purgeProviderControllerImportAndUsage(file.path, updatedContent, providerPath);
155-
context.fileCache.set(appModuleNgFactoryPath, { path: appModuleNgFactoryPath, content: updatedContent});
159+
const ngModuleFactoryFiles = context.fileCache.getAll().filter(file => file.path.endsWith(changeExtension(getStringPropertyValue(Constants.ENV_NG_MODULE_FILE_NAME_SUFFIX), '.ngfactory.js')));
160+
ngModuleFactoryFiles.forEach(ngModuleFactoryFile => {
161+
const newContent = purgeProviderControllerImportAndUsage(ngModuleFactoryFile.path, ngModuleFactoryFile.content, providerPath);
162+
context.fileCache.set(ngModuleFactoryFile.path, { path: ngModuleFactoryFile.path, content: newContent});
163+
});
156164

157-
// purge the provider name from the forRoot method providers list
158165
const moduleFilePath = getIonicModuleFilePath();
159166
const ionicModuleFile = context.fileCache.get(moduleFilePath);
160-
let newModuleFileContent = purgeProviderClassNameFromIonicModuleForRoot(ionicModuleFile.content, providerClassName);
167+
const newModuleFileContent = purgeProviderClassNameFromIonicModuleForRoot(ionicModuleFile.content, providerClassName);
161168

162-
// purge the component from the index file
169+
// purge the component from the module file
163170
context.fileCache.set(moduleFilePath, { path: moduleFilePath, content: newModuleFileContent});
164171
}
165172
}
166173

174+
function attemptToPurgeUnusedEntryComponents(context: BuildContext, dependencyMap: Map<string, Set<string>>, entryComponentPath: string, entryComponentFactoryPath: string) {
175+
if (dependencyMap.has(entryComponentPath)) {
176+
const ngModuleFactoryFiles = context.fileCache.getAll().filter(file => file.path.endsWith(changeExtension(getStringPropertyValue(Constants.ENV_NG_MODULE_FILE_NAME_SUFFIX), '.ngfactory.js')));
177+
ngModuleFactoryFiles.forEach(ngModuleFactoryFile => {
178+
const updatedContent = purgeComponentNgFactoryImportAndUsage(ngModuleFactoryFile.path, ngModuleFactoryFile.content, entryComponentFactoryPath);
179+
context.fileCache.set(ngModuleFactoryFile.path, { path: ngModuleFactoryFile.path, content: updatedContent});
180+
});
181+
}
182+
}
183+
167184
export function getConfig(context: BuildContext, configFile: string): WebpackConfig {
168185
configFile = getUserConfigFile(context, taskInfo, configFile);
169186

0 commit comments

Comments
 (0)