Challenges of Ionic 2 Production Builds

February 17th 2017 Ionic 2 Angular TypeScript

By default, Ionic 2 produces unminified development builds. To force an optimized production build, you need to add --prod switch to ionic build or ionic run command. Since development build doesn't include Angular AoT (Ahead of Time) compilation, your production build might turn out broken even if development build of the application worked just fine.

I encountered two different issues when trying to create a production build of my application.

The first one was related to the version of TypeScript I was using. Although, Ionic 2 comes preconfigured with TypeScript 2.0.9, I decided to upgrade it to its latest version 2.1.6, so that I could use the async and await keywords, which I really like. Although, the build succeeded, the application greeted me with a white screen and the following error in JavaScript console:

Error: Cannot find module "./app.module.ngfactory"

It happened even if I didn't use async and await or any other TypeScript 2.1 features at all. A bit of research revealed that Ionic 2 has some compatibility issues with TypeScript 2.1+ (related to Angular AoT compilation). To fix the problem, I had to downgrade TypeScript back to 2.0.9 and remove all TypeScript 2.1 features from my code. Fortunately enough, I noticed the issue early on, and didn't have all that much code to change yet.

The second issue actually caused the production build to fail with an error that was at least somewhat helpful:

build prod failed: Error encountered resolving symbol values statically. Function calls are not supported. Consider replacing the function or lambda with a reference to an exported function, resolving symbol AppModule in C:/Users/Damir/Temp/ionic-sample/src/app/app.module.ts

I checked the contents of my app.module.ts:

let pages: Array<any> = [
  MyApp,
  HomePage
];

let components: Array<any> = [
  MyComponent
];

@NgModule({
  declarations: pages.concat(components),
  imports: [
    IonicModule.forRoot(MyApp)
  ],
  bootstrap: [IonicApp],
  entryComponents: pages,
  providers: [{provide: ErrorHandler, useClass: IonicErrorHandler}]
})
export class AppModule {}

To avoid repeating the list of pages for both declarations and entryComponents, I declared separate arrays of pages, which must be included in both declarations and entryComponents, and components, which can only be included in declarations. Based on the error I expected the pages.concat() function call to be the offender. I assumed that moving the call to an exported function would fix the issue:

let pages: Array<any> = [
  MyApp,
  HomePage
];

let components: Array<any> = [
  MyComponent
];

export function declarations(): Array<any> {
  return pages.concat(components);
}

@NgModule({
  declarations: declarations(),
  imports: [
    IonicModule.forRoot(MyApp)
  ],
  bootstrap: [IonicApp],
  entryComponents: pages,
  providers: [{provide: ErrorHandler, useClass: IonicErrorHandler}]
})
export class AppModule {}

However, I still got the same error. Since I didn't want to abandon my idea of only listing the pages only once, I did some additional reading and finally learned about the subset of JavaScript that is supported by AoT compilation. This taught me that I could replace the offending pages.concat() call with the spread syntax:

let pages: Array<any> = [
  MyApp,
  HomePage
];

let components: Array<any> = [
  MyComponent
];

@NgModule({
  declarations: [...pages, ...components],
  imports: [
    IonicModule.forRoot(MyApp)
  ],
  bootstrap: [IonicApp],
  entryComponents: pages,
  providers: [{provide: ErrorHandler, useClass: IonicErrorHandler}]
})
export class AppModule {}

There's a more general lesson to my experience with Ionic 2 production builds. Although shorter build times make it convenient to use development builds during development, you still should include production builds in your build process on the continuous integration server from the beginning of the project. If you add to that at least some end-to-end tests, you should be able to detect and fix any issues as soon as they happen, instead of them surprising you in a later stage of the project.

Copyright
Creative Commons License