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

Commit

Permalink
fix(optimizations): fix multiple bugs (components not being purged, o…
Browse files Browse the repository at this point in the history
…verlays not working from providers, etc) for manual tree shaking
  • Loading branch information
danbucholtz committed May 4, 2017
1 parent 272205a commit 4b538c7
Show file tree
Hide file tree
Showing 6 changed files with 1,602 additions and 1,340 deletions.
8 changes: 4 additions & 4 deletions src/optimization.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ describe('optimization task', () => {
const mockIndexPath = join('some', 'path', 'myApp', 'node_modules', 'ionic-angular', 'index.js');

spyOn(treeshake, treeshake.calculateUnusedComponents.name);
spyOn(treeshake, treeshake.purgeUnusedImportsAndExportsFromIndex.name);
spyOn(treeshake, treeshake.purgeUnusedImportsAndExportsFromModuleFile.name);
spyOn(treeshake, treeshake.purgeComponentNgFactoryImportAndUsage.name);
spyOn(treeshake, treeshake.purgeProviderControllerImportAndUsage.name);
spyOn(treeshake, treeshake.purgeProviderClassNameFromIonicModuleForRoot.name);
Expand All @@ -92,7 +92,7 @@ describe('optimization task', () => {
optimization.doOptimizations(context, new Map());

expect(treeshake.calculateUnusedComponents).not.toHaveBeenCalled();
expect(treeshake.purgeUnusedImportsAndExportsFromIndex).not.toHaveBeenCalled();
expect(treeshake.purgeUnusedImportsAndExportsFromModuleFile).not.toHaveBeenCalled();
expect(treeshake.purgeComponentNgFactoryImportAndUsage).not.toHaveBeenCalled();
expect(treeshake.purgeProviderControllerImportAndUsage).not.toHaveBeenCalled();
expect(treeshake.purgeProviderClassNameFromIonicModuleForRoot).not.toHaveBeenCalled();
Expand All @@ -108,7 +108,7 @@ describe('optimization task', () => {

spyOn(treeshake, treeshake.getAppModuleNgFactoryPath.name);
spyOn(treeshake, treeshake.calculateUnusedComponents.name).and.returnValue({ purgedModules: new Map()});
spyOn(treeshake, treeshake.purgeUnusedImportsAndExportsFromIndex.name);
spyOn(treeshake, treeshake.purgeUnusedImportsAndExportsFromModuleFile.name);

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

expect(treeshake.calculateUnusedComponents).toHaveBeenCalled();
expect(treeshake.purgeUnusedImportsAndExportsFromIndex).toHaveBeenCalled();
expect(treeshake.purgeUnusedImportsAndExportsFromModuleFile).toHaveBeenCalled();
});
});
});
75 changes: 46 additions & 29 deletions src/optimization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import { Logger } from './logger/logger';
import { fillConfigDefaults, getUserConfigFile, replacePathVars } from './util/config';
import * as Constants from './util/constants';
import { BuildError } from './util/errors';
import { getBooleanPropertyValue, getStringPropertyValue, webpackStatsToDependencyMap, printDependencyMap } from './util/helpers';
import { changeExtension, getBooleanPropertyValue, getStringPropertyValue, webpackStatsToDependencyMap, printDependencyMap } from './util/helpers';
import { BuildContext, TaskInfo } from './util/interfaces';
import { runWebpackFullBuild, WebpackConfig } from './webpack';
import { addPureAnnotation, purgeStaticCtorFields, purgeStaticFieldDecorators, purgeTranspiledDecorators } from './optimization/decorators';
import { getAppModuleNgFactoryPath,
calculateUnusedComponents,
import { calculateUnusedComponents,
checkIfProviderIsUsedInSrc,
getIonicModuleFilePath,
purgeUnusedImportsAndExportsFromIndex,
purgeUnusedImportsAndExportsFromModuleFile,
purgeUnusedExportsFromIndexFile,
purgeComponentNgFactoryImportAndUsage,
purgeProviderControllerImportAndUsage,
purgeProviderClassNameFromIonicModuleForRoot
Expand Down Expand Up @@ -59,7 +60,7 @@ export function purgeGeneratedFiles(context: BuildContext, fileNameSuffix: strin

export function doOptimizations(context: BuildContext, dependencyMap: Map<string, Set<string>>) {
// remove decorators
const modifiedMap = new Map(dependencyMap);
let modifiedMap = new Map(dependencyMap);
if (getBooleanPropertyValue(Constants.ENV_PURGE_DECORATORS)) {
removeDecorators(context);
}
Expand All @@ -71,6 +72,8 @@ export function doOptimizations(context: BuildContext, dependencyMap: Map<string
// since there is a breaking change here
const ionicModulePath = getIonicModuleFilePath();
if (context.fileCache.get(ionicModulePath)) {
// due to how the angular compiler works in angular 4, we need to check if
modifiedMap = checkIfProviderIsUsedInSrc(context, modifiedMap);
const results = calculateUnusedComponents(modifiedMap);
purgeUnusedImports(context, results.purgedModules);
}
Expand Down Expand Up @@ -127,43 +130,57 @@ function purgeUnusedImports(context: BuildContext, purgeDependencyMap: Map<strin
modulesToPurge.push(moduleToPurge);
});

const updatedFileContent = purgeUnusedImportsAndExportsFromIndex(moduleFilePath, moduleFile.content, modulesToPurge);
const updatedFileContent = purgeUnusedImportsAndExportsFromModuleFile(moduleFilePath, moduleFile.content, modulesToPurge);
context.fileCache.set(moduleFilePath, { path: moduleFilePath, content: updatedFileContent });

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));
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));
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));
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));
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));
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));
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));
const updatedIndexContent = purgeUnusedExportsFromIndexFile(file.path, file.content, modulesToPurge);
context.fileCache.set(file.path, { path: file.path, content: updatedIndexContent });

attemptToPurgeUnusedProvider(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_ACTION_SHEET_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_ACTION_SHEET_CONTROLLER_CLASSNAME));
attemptToPurgeUnusedProvider(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_ALERT_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_ALERT_CONTROLLER_CLASSNAME));
attemptToPurgeUnusedProvider(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_LOADING_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_LOADING_CONTROLLER_CLASSNAME));
attemptToPurgeUnusedProvider(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_MODAL_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_MODAL_CONTROLLER_CLASSNAME));
attemptToPurgeUnusedProvider(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_PICKER_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_PICKER_CONTROLLER_CLASSNAME));
attemptToPurgeUnusedProvider(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_POPOVER_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_POPOVER_CONTROLLER_CLASSNAME));
attemptToPurgeUnusedProvider(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_TOAST_CONTROLLER_PATH), getStringPropertyValue(Constants.ENV_TOAST_CONTROLLER_CLASSNAME));

attemptToPurgeUnusedEntryComponents(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_ACTION_SHEET_COMPONENT_PATH), getStringPropertyValue(Constants.ENV_ACTION_SHEET_COMPONENT_FACTORY_PATH));
attemptToPurgeUnusedEntryComponents(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_ALERT_COMPONENT_PATH), getStringPropertyValue(Constants.ENV_ALERT_COMPONENT_FACTORY_PATH));
attemptToPurgeUnusedEntryComponents(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_LOADING_COMPONENT_PATH), getStringPropertyValue(Constants.ENV_LOADING_COMPONENT_FACTORY_PATH));
attemptToPurgeUnusedEntryComponents(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_MODAL_COMPONENT_PATH), getStringPropertyValue(Constants.ENV_MODAL_COMPONENT_FACTORY_PATH));
attemptToPurgeUnusedEntryComponents(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_PICKER_COMPONENT_PATH), getStringPropertyValue(Constants.ENV_PICKER_COMPONENT_FACTORY_PATH));
attemptToPurgeUnusedEntryComponents(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_POPOVER_COMPONENT_PATH), getStringPropertyValue(Constants.ENV_POPOVER_COMPONENT_FACTORY_PATH));
attemptToPurgeUnusedEntryComponents(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_TOAST_COMPONENT_PATH), getStringPropertyValue(Constants.ENV_TOAST_COMPONENT_FACTORY_PATH));
attemptToPurgeUnusedEntryComponents(context, purgeDependencyMap, getStringPropertyValue(Constants.ENV_SELECT_POPOVER_COMPONENT_PATH), getStringPropertyValue(Constants.ENV_SELECT_POPOVER_COMPONENT_FACTORY_PATH));
}

function attemptToPurgeUnusedProvider(context: BuildContext, dependencyMap: Map<string, Set<string>>, providerPath: string, providerComponentPath: string, providerComponentFactoryPath: string, providerClassName: string) {
function attemptToPurgeUnusedProvider(context: BuildContext, dependencyMap: Map<string, Set<string>>, providerPath: string, providerClassName: string) {
if (dependencyMap.has(providerPath)) {
// awwww yissssssss

// first, get the content of the app module ngfactory file
const appModuleNgFactoryPath = getAppModuleNgFactoryPath();
const file = context.fileCache.get(appModuleNgFactoryPath);
if (!file) {
return;
}

let updatedContent = purgeComponentNgFactoryImportAndUsage(file.path, file.content, providerComponentFactoryPath);
updatedContent = purgeProviderControllerImportAndUsage(file.path, updatedContent, providerPath);
context.fileCache.set(appModuleNgFactoryPath, { path: appModuleNgFactoryPath, content: updatedContent});
const ngModuleFactoryFiles = context.fileCache.getAll().filter(file => file.path.endsWith(changeExtension(getStringPropertyValue(Constants.ENV_NG_MODULE_FILE_NAME_SUFFIX), '.ngfactory.js')));
ngModuleFactoryFiles.forEach(ngModuleFactoryFile => {
const newContent = purgeProviderControllerImportAndUsage(ngModuleFactoryFile.path, ngModuleFactoryFile.content, providerPath);
context.fileCache.set(ngModuleFactoryFile.path, { path: ngModuleFactoryFile.path, content: newContent});
});

// purge the provider name from the forRoot method providers list
const moduleFilePath = getIonicModuleFilePath();
const ionicModuleFile = context.fileCache.get(moduleFilePath);
let newModuleFileContent = purgeProviderClassNameFromIonicModuleForRoot(ionicModuleFile.content, providerClassName);
const newModuleFileContent = purgeProviderClassNameFromIonicModuleForRoot(ionicModuleFile.content, providerClassName);

// purge the component from the index file
// purge the component from the module file
context.fileCache.set(moduleFilePath, { path: moduleFilePath, content: newModuleFileContent});
}
}

function attemptToPurgeUnusedEntryComponents(context: BuildContext, dependencyMap: Map<string, Set<string>>, entryComponentPath: string, entryComponentFactoryPath: string) {
if (dependencyMap.has(entryComponentPath)) {
const ngModuleFactoryFiles = context.fileCache.getAll().filter(file => file.path.endsWith(changeExtension(getStringPropertyValue(Constants.ENV_NG_MODULE_FILE_NAME_SUFFIX), '.ngfactory.js')));
ngModuleFactoryFiles.forEach(ngModuleFactoryFile => {
const updatedContent = purgeComponentNgFactoryImportAndUsage(ngModuleFactoryFile.path, ngModuleFactoryFile.content, entryComponentFactoryPath);
context.fileCache.set(ngModuleFactoryFile.path, { path: ngModuleFactoryFile.path, content: updatedContent});
});
}
}

export function getConfig(context: BuildContext, configFile: string): WebpackConfig {
configFile = getUserConfigFile(context, taskInfo, configFile);

Expand Down
Loading

0 comments on commit 4b538c7

Please sign in to comment.