HOWTO: webpack

EDIT: updated webpack.config.js for webpack2, switch typescript to ts-loader, update for ionic beta 7

I use webpack to package other angular apps, so I was really excited to see Ionic2 adopting it in the early alpha releases. Now apparently they have decided to go in a different direction, and if anybody else is interested in continuing to use webpack to build Ionic2 apps, I thought I would share what I have learned so far in order to at least get a starter app to run.

webpack.config.js

var path = require('path');

module.exports = {
  entry: [
    'reflect-metadata/Reflect',
    './zones',
    path.resolve('app/app')
  ],
  output: {
    path: path.resolve('www/build/js'),
    filename: 'app.bundle.js',
    pathinfo: true // show module paths in the bundle, handy for debugging
  },
  module: {
    loaders: [
      { test: /\.ts$/, loader: 'ts' },
      { test: /\.scss$/, loaders: "style!css!sass" },
      { test: /\.png$/, loader: 'url?limit=100000' },
      { test: /\.woff(2)?(\?v=.+)?$/, loader: "url?limit=10000&mimetype=application/font-woff" },
      { test: /\.(ttf|eot|svg)(\?v=.+)?$/, loader: 'file' },
      { test: /\.html$/, loader: 'html' },
      { test: /\.json$/, loader: 'json' }
    ],
    noParse: [
      /reflect-metadata/,
    ]
  },
  resolve: {
    // NOTE: this syntax is for webpack2
    modules: [
      path.join(__dirname, 'www', 'app'),
      "node_modules"
    ],
    extensions: ["", ".js", ".ts"]
  },
  sassLoader: {
    includePaths: [
      path.resolve(__dirname, "./node_modules/ionic-angular/"),
      path.resolve(__dirname, "./node_modules/ionicons/dist/scss/")
    ]
  }
};

Obviously, youā€™re going to need to make sure all those loaders are installed as devDependencies: ts-loader, css-loader, html-loader, style-loader, sass-loader, url-loader, file-loader. If you want the functionality of ā€œionic serveā€, you will also want to install webpack-dev-server globally.

index.html

Get rid of the stylesheet links in the head and the angular-polyfills load in the body. Weā€™ve put angular-polyfills in as an entry point, so itā€™s already in the bundle. The stylesheets will get added inā€¦

app.ts

Load the stylesheets in the constructor, like this:

  constructor(platform: Platform) {
    platform.ready().then(() => {
      if (platform.is('ios')) {
        require('./theme/app.ios.scss');
      } else {
        if (platform.is('windows')) {
          require('./theme/app.wp.scss');
        } else {
          require('./theme/app.md.scss');
        }
      }
      StatusBar.styleDefault();
    });
  }

If you prefer ios to md as a default, you could swap the ios check for an android one and have ios where I have md.

app.variables.css

Add this to the ā€œApp Shared Variablesā€ section:

$font-path: '../../node_modules/ionic-angular/fonts';

require.d.ts

At the same directory level as app.ts:

declare var require: {
    <T>(path: string): T;
    (paths: string[], callback: (...modules: any[]) => void): void;
    ensure: (paths: string[], callback: (require: <T>(path: string) => T) => void) => void;
};

zones.ts

At the same level as webpack.config.js:

require('zone.js/dist/zone');
require('zone.js/dist/long-stack-trace-zone');

The long stack trace import could be made conditional on some sort of development/production flag.

templates

One thing I really love about using webpack here is that page classes can load their templates and stylesheets with it, without needing to fool around with copying files into staging directories. So, for example, the starter home.ts can use:

template: require<string>('./home.html')

This also works for custom styles intended to apply to a single page or component:

styles: [require<string>('./component.styl')]

Youā€™ll need a .styl test using ā€œraw!stylusā€ and the stylus-loader. To use css or scss here, youā€™ll need some sort of special case from the default scss loader that loads to raw instead of the style loader.

6 Likes

Hey there! Thanks for sharing this!

Just thought Iā€™d chime in and go over why weā€™re switching off of webpack. While webpack is great, it also comes with a step learning curve for beginners. We could pick another build tool, but chances are, some people would prefer a different option.

So what weā€™ve decided to do is ship with a minimal starter that uses browserify, but can easily be swapped out for a different tool.

I appreciate that, although your ā€œeasilyā€ might not be in the same zip code as the ā€œeasilyā€ of the rest of us mortals, and I ran into several roadblocks that looked quite daunting at first. Iā€™m still not entirely thrilled with having to modify $font_path, and especially with that ugly relative path, but it was the first thing out of several attempts that worked.

Thanks for this HOWTO. I muddled through some customization of webpack (replace-task-webpack-plugin to inject env-variables) with an earlier starter app, and found myself stuck when the build system reverted to gulp.

This walkthrough has gotten me 98% there. Images under build/ also needed to move under /theme.

However the ionicons fonts arenā€™t loading. I added the $font-path to app.variables.scss, and see webpack picking up the fonts

[awesome-typescript-loader] You have `resolveGlobs` enabled and don't have an `exclude` directive in your tsconfig file. This WILL slow down your compilation. Please add:
{
    // ...
    "exclude": [
        "node_modules",
        "bower_components"
    ]
}

Hash: 322dd91fae222bc86ad3
Version: webpack 1.12.14
Time: 13105ms
                             Asset     Size  Chunks             Chunk Names
a937e2cae14e68262a45aa91204c2fdf.ttf  19.8 kB          [emitted]
311d81961c5880647fec7eaca1221b2a.woff2    61 kB          [emitted]
74c652671225d6ded874a648502e5f0a.ttf   144 kB          [emitted]
9ff15bd34ea83e4dd3f23c20c7f5090e.ttf  19.9 kB          [emitted]
7e2d32e7141050d758a38b4ec96390c0.woff  13.4 kB          [emitted]
07f8fb6acbabeb10d3fad9ab02d65e0b.ttf  19.9 kB          [emitted]
f94d5e5102359961c44a1da1b58d37c9.woff  13.3 kB          [emitted]
81414686e99c00d2921e03dd53c0ab04.woff  80.4 kB          [emitted]
0f3b7101a8adc1afe1fbe89775553c32.woff  13.2 kB          [emitted]
1f4fd7e4df65487f07ba9148f7ca095d.ttf    20 kB          [emitted]
43183beef21370d8a4b0d64152287eba.woff  13.3 kB          [emitted]
2fd9c16b805724d590c0cff96da070a4.ttf   415 kB          [emitted]
a165a42685795361b25593effb32fdb1.ttf   415 kB          [emitted]
                     app.bundle.js   7.9 MB       0  [emitted]  main

but continue to see 404s/ FNF using a webinspector

file:///android_asset/www/311d81961c5880647fec7eaca1221b2a.woff2 Failed to load resource: net::ERR_FILE_NOT_FOUND
file:///android_asset/www/81414686e99c00d2921e03dd53c0ab04.woff Failed to load resource:     net::ERR_FILE_NOT_FOUND
file:///android_asset/www/74c652671225d6ded874a648502e5f0a.ttf Failed to load resource: net::ERR_FILE_NOT_FOUND

The files are being copied to www/build/js and are correspondingly available in platforms/android/assets/www/build/js/path

1 Like

Hmm. I wonder whether it makes more sense and/or is easier to get whoever is looking for them to look for them there or to get webpack to put them up a couple of directory levels.

FIXED. thanks for the suggestion @rapropos

Iā€™m sure there is opinions on which way is ā€˜correctā€™, but either of the following fixes my issue finding ionicon fonts (pick one).

supply a publicPath on the output configuration:

output: {
	path: path.resolve('www/build/js'),
	filename: 'app.bundle.js',
	pathinfo: true, // show module paths in the bundle, handy for debugging
	publicPath: 'build/js/'
}  

specify the path on the loader &name=path/[hash].[ext] :

{
    test: /\.woff(2)?(\?v=.+)?$/,
    loader: "url?limit=10000&mimetype=application/font-woff&name=build/js/[hash].[ext]"
}
1 Like

Also on my targeted device, platform.ready doesnā€™t fire immediately. To prevent un-styled content I picked a theme to load by default.

constructor(platform: Platform) {
 //prevents flash of ugly that happens before platform.ready() fires
 require('./theme/app.md.scss');

 platform.ready().then(() => {
   if (platform.is('md')) {
    require('./theme/app.md.scss');
   } else {
   if (platform.is('ios')) {
    require('./theme/app.ios.scss');
   } else {
    require('./theme/app.wp.scss');
   }
 }
 if (window.StatusBar) {
    window.StatusBar.styleDefault();
 }
});

}

1 Like

Hey since the chosen theme loads after the default one it overwrites the default?

Also as far as i see the ionic way was to load all of them and just use a class with the platform for the separation of files, so would this lower the css to load since it just requires the first one?

Thanks.

{ test: /\.scss$/, loaders: "style!css!sass" }
to
{ test: /\.scss$/, loader: "style!css!sass" },

1 Like

Thank you. Unfortunately I canā€™t edit the post any more. The forum software prohibits editing posts older than 60 days.

Hereā€™s my related question:

Ionic is moving to a webpack2 build process shortly. Stay tuned.

Are there any news on this?

Hi, Do you have any update on this?

Staying tuned for quite a while now

I see you updated this, but it seems like this is no longer complete is it?

Sadly, I canā€™t update it further. The BBS software freezes edits to posts older than X days.

Do you still do the require part the way itā€™s described? Itā€™s not working for me at the moment (Canā€™t find the files, triple checked the paths). "Module not found " error. Iā€™m talking about the scss here btw

I copy the stock webpack.config.js from node_modules/@ionic/app-scripts/config and then just add as little as I can get away with.

I have to primitize a lot of logic, I had very cool features in webpack which now just donā€™t work anymore :ā€™(. They wrecked a lot of things with the new app-scripts.