Initial setup

Both Meteor and Ionic took their platform to the next level in tooling. Both provide CLI interface instead of bringing a bunch of dependencies and configure build tools. There are also differences between those tools.

In this post we will focus on the Ionic CLI - we will use it to serve the client side and build the Cordova project, and we will use Meteor as a server side only, and load the Meteor collections in the client side externally.

First, start by making sure your Node & NPM are up to date, and Node's version it above 5 (you can check it using node --version).

To begin with, we need to install Ionic using NPM:

$ npm install -g ionic cordova

We will create our WhatsApp app using the following command:

$ ionic start whatsapp --v2

To start our app, simply type:

$ ionic serve

NOTE: Ionic framework is built on top of Cordova which let's you build your app for mobile devices. For more information on how to run our app on a mobile device see the following link.

Ionic2 apps are written using Angular2. Although Angular2 apps can be created using Javascript, it is recommended to write them using Typescript, for 2 reasons:

  • It prevents runtime errors.
  • Dependency injection is done automatically based on the provided data-types.

In order to apply TypeScript, Ionic's build system is built on top of a module bundler and modules loader called Rollup.

In this tutorial we will use a custom build-config using Webpack, hence we're gonna re-define our build system. Both module-bundlers are great solutions for building our app, but Webpack provides us with some extra features like aliases, which are missing in Rollup.

Also, in order to load Meteor as external dependency without it's CLI, we need to use CommonJS modules loader (using Webpack) instead of ES2016 modules (using Rollup).

Ionic 2 + Webpack

At moment, Ionic 2 supports Webpack build only in it's beta version, so we need to use the beta version of @ionic/app-scripts, to let's change it:

$ npm install --save-dev @[email protected]

Now let's continue by configuring our Webpack build, first we need to tell Ionic that we are using Webpack and use are using custom build file - this is done by adding config to our package.json file:

1.1 Added build configuration for Ionic app scripts package.json
50
51
52
53
54
55
56
57
      "locator": "ios"
    }
  ],
  "description": "ionic2-meteor-messenger: An Ionic project",
  "config": {
    "ionic_webpack": "./config/webpack.config.js"
  }
}

Let's create our initial Webpack config - Ionic provides us a simple Webpack file that we can extend, it's located under [email protected]/app-scripts/config/webpack.config.js, so let's copy it and put in under ./config/webpack.config.js:

1.2 Added webpack base file config/webpack.config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
var path = require('path');
var webpack = require('webpack');
 
// for prod builds, we have already done AoT and AoT writes to disk
// so read the JS file from disk
// for dev buids, we actually want to pass in .ts files since we
// don't have .js files on disk, they're exclusively in memory
 
function getEntryPoint() {
  if (process.env.IONIC_ENV === 'prod') {
    return '{{TMP}}/app/main.prod.js';
  }
  return '{{TMP}}/app/main.dev.js';
}
 
function getPlugins() {
  if (process.env.IONIC_ENV === 'prod') {
    return [
      // This helps ensure the builds are consistent if source hasn't changed:
      new webpack.optimize.OccurrenceOrderPlugin(),
      // Try to dedupe duplicated modules, if any:
      // Add this back in when Angular fixes the issue: https://github.com/angular/angular-cli/issues/1587
      //new DedupePlugin()
    ];
  }
  return [];
}
 
module.exports = {
  entry: getEntryPoint(),
  output: {
    path: '{{BUILD}}',
    filename: 'main.js'
  },
 
  resolve: {
    extensions: ['.js', '.json']
  },
 
  module: {
    loaders: [
      {
        test: /\.json$/,
        loader: 'json'
      }
    ]
  },
 
  plugins: getPlugins(),
 
  // Some libraries import Node modules but don't use them in the browser.
  // Tell Webpack to provide empty mocks for them so importing them works.
  node: {
    fs: 'empty',
    net: 'empty',
    tls: 'empty'
  }
};

Now we have the basic Webpack file, and if you will run ionic serve again, you will notice that it uses Webpack for bundling.

Our next step is to add some custom config to our Webpack file, so let's do it:

1.3 Updated webpack file config/webpack.config.js
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
 
39
40
41
42
43
44
45
46
47
48
 
50
51
52
53
54
55
56
57
58
59
60
 
66
67
68
69
70
71
72
}
 
function getPlugins() {
  var plugins = [
    // Try to dedupe duplicated modules, if any:
    // Add this back in when Angular fixes the issue: https://github.com/angular/angular-cli/issues/1587
    //new DedupePlugin()
    new webpack.ProvidePlugin({
      __extends: 'typescript-extends'
    })
  ];
 
  if (process.env.IONIC_ENV === 'prod') {
    // This helps ensure the builds are consistent if source hasn't changed:
    plugins.push(new webpack.optimize.OccurrenceOrderPlugin())
  }
 
  return plugins;
}
 
module.exports = {
...some lines skipped...
  },
 
  resolve: {
    extensions: ['.js', '.json', '.ts'],
    alias: {
      'api': path.resolve(__dirname, '../api')
    }
  },
 
  module: {
...some lines skipped...
      {
        test: /\.json$/,
        loader: 'json'
      },
      {
        test: /\.ts$/,
        exclude: /(node_modules)/,
        loaders: ['awesome-typescript-loader']
      }
    ]
  },
...some lines skipped...
  node: {
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    __dirname: true
  }
};

So let's understand what have we done here:

  • We first added a Webpack plugin called ProvidePlugin which provides globals for our app, and we use typescript-extends package as our __extend - this will give us the ability to load external TypeScript modules with any issues.
  • We created an alias for api - which means that any import that starts with "api" will be resolved into the directory we specified (../api/ in our case - which we will later create there our server side using Meteor).

We just need to add typescript-extends and awesome-typescript-loader by installing it:

$ npm install --save-dev typescript-extends awesome-typescript-loader meteor-typings

TypeScript Configuration

Now, we need to make some modifications for the TypeScript compiler in order to use CommonJS (we have to use CommonJS, otherwise we won't be able to load Meteor as external dependency), we will also need to change some flags in the config for that.

So let's change tsconfig.json file:

1.5 Updated typscript compiler config tsconfig.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 
25
26
27
28
{
  "compilerOptions": {
    "target": "es5",
    "lib": [
      "es6",
      "dom"
    ],
    "module": "commonjs",
    "moduleResolution": "node",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "sourceMap": true,
    "noImplicitAny": false,
    "declaration": false,
    "skipLibCheck": true,
    "stripInternal": true
  },
  "include": [
    "src/**/*.ts"
...some lines skipped...
  "atom": {
    "rewriteTsconfig": false
  }
}

Summary

So in this step we installed Ionic and created a new project, and we changed the default bundler & modules loader - we use Webpack and CommonJS so we can load Meteor's dependencies.

In this point, you should be able to run your app by running:

$ ionic serve