Josh Bavari's Thoughts

Thoughts on technology and philosophy

Windows and Webpack With TypeScript and Babel

less than a 1 minute read

I’ve recently been diving into the land of Webpack to handle all the heavy lifting of using Babel to compile my TypeScript and ES6 into ES5 JavaScript to be used in Ionic 2.

The current set up I’m working with involves having Webpack use the awesome-typescript-loader to load up TypeScript and compile the TypeScript files, as well as load up Babel and compile the ES6 JavaScript using Babel.

The set up

The file structure looks like this:

1
2
3
4
5
6
./
  ./www
    ./app
      ./components
        ./datepipe.js
        ./app.ts

This worked great on my Mac. However, one thing I ran into on my Windows machine was this particular error:

Cannot find module "./www/app/app.js"

Take a look at the webpack.config.js in the ionic-conference-app, with a portion of it below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* snipped */
  module: {
    loaders: [
      {
        test: /\.js$/,
        loader: "awesome-typescript-loader?doTypeCheck=false&useBabel=true&useWebpackText=true",
        include: /www\/app\//,
        exclude: /node_modules/
      },
      {
        test: /\.ts$/,
        loader: "awesome-typescript-loader",
        include: /www\/app\//,
        exclude: /node_modules/
       }
    ]
  },
/* snipped */

Webpack uses loaders to take the files and add them to the final build output. It knows where to get these files from the webpack config module loaders array, where each loader specifies the include paths, as per the webpack docs:

include: A condition that must be met

A condition may be a RegExp, a string containing the absolute path, a function(absPath): bool, or an array of one of these combined with “and”.

Take note of the include line we had at first: include: /www\/app\//,, line 7 and 13 in the pasted snipped above.

Sure this will work in a Unix based runtime. If you’re running on a Windows machine, these paths may be a problem. As it doesn’t understand the /. This tip came from Edward McLeod-Jones, who pointed out this issue.

You might want to try to make RegEx fun, by doing this:

1
include: /www(\/|\\)app(\/|\\)/,  // <--- Change the regex to support either type of folder separator`

However, since we’re doing Node.js, it provides APIs to help us out with cross-platform changes like this with the path module.

Do this instead:

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
var path = require('path');
var wwwPath = path.resolve(__dirname, 'www');
var outputPath = path.join(wwwPath, 'build', 'js');
var appPath = path.join(wwwPath, 'app');
var appJsPath = path.join(appPath, 'app.js');

/* snip */
  module: {
    loaders: [
      {
        test: /\.js$/,
        loader: "awesome-typescript-loader?doTypeCheck=false&useBabel=true&useWebpackText=true",
        include: [wwwPath],
        // include: /www(\/|\\)app(\/|\\)/,
        exclude: /node_modules/
      },
      {
        test: /\.ts$/,
        loader: "awesome-typescript-loader",
        // include: /www(\/|\\)app(\/|\\)/,
        include: [wwwPath],
        exclude: /node_modules/
       }
    ]
  },
/* snip */

Angular 2 Injectables

less than a 1 minute read

I’ve been fortunate enough to be working on Angular 2 while being on the Ionic team.

I really enjoyed Pascal Precht’s post about Dependency injection in Angular 2. One thing that I want to shed some more light on is how dependency injection works in an Angular 2 application using the @Injectable metadata thats passed for a class that’s to be injected.

Quick tip: take a quick look at the Angular 2 cheat sheet to see some more of these Angular 2 syntax and API.

The basics of Depdendency injection

The gist of it we need:

1) A class with @Injectable to tell angular 2 that its to be injected – DataService
2) A class with a constructor that accepts a type to be injected

A solid example, DataService marked as @Injectable that also needs Http to be injected for its use:

1
2
3
4
5
6
7
8
9
import {Injectable, bind} from 'angular2/core';
import {Http} from 'angular2/http';

@Injectable() /* This is #1 */
export class DataService {
  constructor(http: Http /* This is #2 */ ) {
    this.http = http;
  }
}

What we have in the example above is a class, DataService, that needs Http to do what it needs to be done.

An example scenario

Let’s say we have the following scenario, an angular 2 application with an app, a page for sessions, session details, speakers, and a data service that provides the data for those pages.

We’d want the app instance to instantiate the data service, then have a component schedule that can use the data service as provided by the app instance. From there, we’d have the session detail that also gets the schedule data from the data service.

The hierarchy would look like this:

1
2
3
4
5
6
7
8
          App
           |
  |.............|...............|
  |             |               |
   Schedule      Speakers     Session-detail
  |
  |
  Schedule-list

All of these components will need the instance of data service.

We’ll need:

  • A data serviced marked with @Injectable.
  • A schedule page
  • A schedule-list component
  • A speakers page

Say you have the following:

In www/app/service/data.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import {Injectable} from 'angular2/core';
import {Http} from 'angular2/http';

@Injectable()
export class DataService {
  constructor(http: Http) {
    this.http = http;
    this.data = null;
    console.log('DataService constructor');
  }

  retrieveData() {
    this.http.get('api/data/data.json')
    .map(res => res.json())
    .subscribe(data => {
      this.data = data;
    });
  }
}

We’d also have our Application object, www/app/app.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import {App, Platform, StatusBar} from 'ionic/ionic';
import {DataService} from './service/data';
import {AboutPage} from './about/about';
import {MapPage} from './map/map';
import {SchedulePage} from './schedule/schedule';
import {SpeakersPage} from './speakers/speakers';

@App({
  templateUrl: 'app/app.html',
  providers: [DataService]
  /* 
    Here we're saying, please include an instance of 
    DataService for all my children components,
    in this case being sessions, speakers, 
    and session detail.
  */
})
class ConferenceApp {
  constructor(platform: Platform, data: DataService) {
    data.retrieveData();
  }
}

Then we’d have our speakers component, www/app/speakers/speakers.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import {Page} from 'ionic/ionic';
import {DataService} from '../service/data';
import {SpeakerDetailPage} from '../speaker-detail/speaker-detail';
import {SessionDetailPage} from '../session-detail/session-detail';

@Page({
  templateUrl: 'app/speakers/speakers.html'
})
export class SpeakersPage {
  constructor(nav: NavController, data: DataService) {
    this.nav = nav;
    this.speakers = null;
    this.dataService = data;
  }

  onInit() {
    this.speakers = this.dataService.getSchedule();
  }
}

Now I want to point something out above. If we had the SpeakersPage to also have a providers: [DataService], we are telling angular 2 to create an instance of DataService to make it available for SpeakersPage’s children, instead of using the DataService that ConferenceApp provided.

I repeat, we’d have two instances of DataService with this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import {Page} from 'ionic/ionic';
import {DataService} from '../service/data';
import {SpeakerDetailPage} from '../speaker-detail/speaker-detail';
import {SessionDetailPage} from '../session-detail/session-detail';

@Page({
  templateUrl: 'app/speakers/speakers.html',
  providers: [DataService]
  // This will instantiate a new DataService for its children
})
export class SpeakersPage {
  constructor(nav: NavController, data: DataService) {
    this.nav = nav;
    this.speakers = null;
    this.dataService = data;
  }

  onInit() {
    this.speakers = this.dataService.getSchedule();
  }
}

This is very important. If we had a console.log statement in the DataService constructor, we’d see it run twice with the providers: [DataService] being specified.

One thing to remember, if at the root application you specify a provider, it will be available to all children components that it contains, unless you specify a providers that then will initialize a new instance of that provided class.

I hope this post helps clear up dependency injection in Angular 2. Now get back to coding!

Npm Production Checklist

less than a 1 minute read

I recently read this post by RisingStack over the Node.js Production Checklist.

Since this was aimed at releasing node.js applications for the most part, I wanted to touch base on a ‘production checklist’ on releasing a npm module.

The npm-developers guide does a great job of explaining much you need to know about publishing your npm module.

This post will mainly serve as a work in progress document to supplement the developers guide, as well as serving as a way to help me continue to keep learning best practices all the time.

Some of the methods I use in my npm release schedule are as follows:

  1. Keeping files out of your package
  2. Locking versions
  3. Continuous integration (tests, install, etc)
  4. Alpha/beta pushes (user testing)
  5. npm-check-updates

Keeping files out of your package

We want our users to only have to download what they need to use your module. This may mean removing any files that are not beneficial for the user. Much like .gitignore for files being checked into git, npm has .npmignore.

Straight from npm docs:

Use a .npmignore file to keep stuff out of your package. If there’s no .npmignore file, but there is a .gitignore file, then npm will ignore the stuff matched by the .gitignore file. If you want to include something that is excluded by your .gitignore file, you can create an empty .npmignore file to override it. Like git, npm looks for .npmignore and .gitignore files in all subdirectories of your package, not only the root directory.

We can also use a command in the npm CLI called prune.

Again, straight from the npm documentation:

This command removes “extraneous” packages. If a package name is provided, then only packages matching one of the supplied names are removed. Extraneous packages are packages that are not listed on the parent package’s dependencies list. If the —production flag is specified or the NODE_ENV environment variable is set to production, this command will remove the packages specified in your devDependencies. Setting —production=false will negate NODE_ENV being set to production.

This is a great way to ensure you dont have any extra packages you might have installed via npm install and either forgot to pass the --save flag or were just testing functionality.

You can also use this command inline with the --production flag when installing node modules to avoid having the extra cruft of the devDependencies, which are mainly used for developing the modules (think testing, etc).

Locking versions

Theres a few methods to use with npm to lock down your versions. You can use the npm shrinkwrap command or specify node modules to bundle in your module with bundleDependencies.

The strategy is, you can either pack those in with your tarball as they are in your node_modules folder, or you can lock down those exact versions by storing the exact tarball to download when the user npm installs your module.

Edit: I’ve just learned on a new method to package bundle up node modules by using the bundledDependencies attribute in your projects package.json file.

Bundling

By specifying bundleDependencies in your package.json, you are telling npm on its publishing process to include those modules listed in the tarball it creates and pushes up to the npm registry. For example, if you go to any npm repo and download the tarball in the url there, unzip it and open it, you’ll see those exact node modules in them as you’ve got in your own node_modules folder.

This effectively locks down your modules versions, at the cost of a larger tarball size the user will download intially. Any modules that are not bundled, will then be downloaded and installed after the tarball is downloaded.

Shrinkwrapping

If you’ve ever done any Rails, you’ve probably seen the gemfile.lock. The equivalent in Node land is the npm-shrinkwrap.json file.

What the npm shrinkwrap command does is looks at your node_modules folder and looks at the packages installed there and compares those to what is in your package.json file.

Straight from npm documentation:

The shrinkwrap command has locked down the dependencies based on what’s currently installed in node_modules. The installation behavior is changed to:

The module tree described by the shrinkwrap is reproduced. This means reproducing the structure described in the file, using the specific files referenced in “resolved” if available, falling back to normal package resolution using “version” if one isn’t.

The tree is walked and any missing dependencies are installed in the usual fasion.

A huge misconception is that shrinkwrap locks down what versions are in your package.json file. This is incorrect. Just to reiterate, it will look in your node_modules and use that compared to your package.json file.

Edit: Another thing to note, if you take a look in your npm-shrinkwrap.json, you’ll see the exact URL for that versions tarball to download, which npm cli will use to grab without taking a peep at anything else. This may cause issues for some users, as stated by Forrest on this Ionic CLI issue:

Due to how shrinkwrap works, it ends up bypassing the npm cache and redownloading every package mentioned in the shrinkwrap each time npm install is run. If users have any issues with their network, or something gets sticky with npm’s CDN, the whole install can fail, forcing them to start over again.

Heed this warning, friends.

Testing

Try to avoid the old addage, ‘it worked on my machine’, by having a CI server pull your package, run some tests, and even sometimes install the module to make sure it works on other machines besides your own.

I really enjoy using CircleCI, as it is free for open source projects! You can normally specify a config file that says what version of node/npm to use and the rest is automated.

Alpha/beta pushes

I covered this in a previous article about using npm tags.

The idea is, before publishing your version to npm, try doing a alpha/beta push first (to save the version in npm you’re about to publish, since there can only be one version) to let users npm install the module to run some tests before commiting that to the latest tag for everyone to install.

npm Check Updates

There’s a nice module that looks at your package.json file and looks to see the latest tags on the modules you specify in your dependencies.

It will give you some heads up if there are latest packages, so you can update if you have too many out of date packages.

Hope this helps!

References:

Using Npm Tags

less than a 1 minute read

If you do any kind of deployments to npm, you’ll probably find learning more about npm tags very helpful.

Just as git tags mark commits in your repository history, npm tags mark a specific version in the npm registry of your published versions.

If you didn’t know, npm manages a tag ‘latest’ that points to the last version you put out with npm publish.

The syntax to publish a new version and tag that version with the name beta, use: npm publish --tag beta.

Installing from tags

To have your users install your node module, they just type npm install. What that does is looks at npm’s latest tag for your repository, and installs that version.

They can also specify a version by passing it after the @ character after the module name: npm install module@1.7.3.

Lets say you have some beta users and always want them to grab the beta, without having to remember the latest version you’ve pushed.

You just run npm publish --tag beta, then have them run npm install module@beta.

At any time, they can still specify the beta version npm install module@1.7.3-beta.1 if they want to hop down or up a version, for example.

Looking up npm tags

Using the npm CLI, you can easily see the versions by running npm view ionic dist-tags. Just replace ionic with whatever module you’d want to see.

You can also look up the entire list of versions npm maintains at the url, registry.npmjs.org/ionic.

As a fun fact, npm uses tags on its own tool, npm, to mark the latest, latest-2, next, next-2, as well as their next versions in their current major/minor versions, for example:

latest => 3.3.8 next => 3.3.9 latest-2 => 2.14.7 next-2 => 2.14.8 v3.x-latest => 3.3.8 3.x-latest => 3.3.8 3.x-next => 3.3.9 v3.x-next => 3.3.9

Also, I made a quick tool to look up tags for you npm version.

Assigning a tag

Let’s say you have a blessed version you now want to promote. It’s super simple to set up that tag to the previous version.

Just run npm dist-tags add ionic-app-lib@0.6.5 latest and you’ll have the latest tag point at 0.6.5.

Whoops, I accidently published without a tag!

This has happened to me thousands of times. I’ve run npm publish without specifying a tag, and now, my latest points at an alpha version. How embarassing.

The scenario is this – my module ionic-app-lib currently has its latest tag at 0.6.4, i’m working on 2.0.0-alpha.18, and I type in npm publish. I wanted to tag this as alpha, but because of my haste, now all my users will grab this version blindly without wanting it.

Thankfully, this is easily fixed – we just have to point latest tag back to its version.

First, just put latest back to 0.6.4, like so: npm dist-tags add ionic-app-lib@0.6.4 latest.

Now we put alpha to what we wanted originally, like so: npm dist-tags add ionic-app-lib@2.0.0-alpha.18 alpha.

Bam! Now everything is back to how we want it!

Removing tags

This is super simple: npm dist-tags rm alpha – this wipes it out.

Hope this helps!

Understanding Built Node Modules

less than a 1 minute read

If you’ve recently change node versions and begun running into issues with some of your modules, you might get a little help from understanding how native node modules work.

TL;DR: If you upgraded node, run npm rebuild or rm -rf node_modules && npm install.

Why: This has to do with some of your modules you may be using having native bindings to your current system runtime.

This then put me on a quest to understand more how native node modules are used. What I’m referring to, is using Node addons:

Addons are dynamically linked shared objects. They can provide glue to C and C++ libraries. The API (at the moment) is rather complex, involving knowledge of several libraries

Node.js statically compiles all its dependencies into the executable. When compiling your module, you don’t need to worry about linking to any of these libraries.

Since I maintain the Ionic CLI, we have a few depedencies to a native node module, node-sass.

Node-sass relies on some native C/C++ bindings to compile SASS down to CSS.

If you have a version of Node 0.12 for example, and install the ionic-cli, then install Node 4.2.1, you may have issues running the CLI.

This is due to the module building itself with that version of node and using those bindings, then when you install a new version of Node, you can’t use those same bindings.

When you change versions of node, make sure you do a quick rm -rf node_modules if on mac, or deleting the node_modules folder on windows and doing a fresh npm install.

If you want to read a little more, background information is shared by Chris Williams about his struggles maintaining the node-serialport module on this post.

Releasing Electron for Windows

less than a 1 minute read

Releasing Electron applications on Windows can be a tricky issue. Especially if you mainly use a Mac (like me). And you have to think about that pesky code signing thing you have to do to avoid the annoying ‘SmartScreen’ filter users may get.

Thankfully, there’s a great tool called Squirrel made by Paul Betts that does a ton of the heavy lifting for you. Codesigning and all.

I really got a ton of knowledge from the blog post, Creating a Windows Distribution of an Electron App using Squirrel and Using Electron Packager to Package an Electron App.

I wanted to curate a ton of knowledge in one place, so here we go.

I use a few tools to get this done on my Mac:

First, let’s look at the project layout:

Project Layout

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
/build # Installers go here
  /osx
  /resources # Icons, iconset, images, etc
  /win # Binaries to build, Package.nuspec file to specify configurations
  packager.json # File used by electron-builder to build OSX DMG file.
/dist # Distributions go here (.app, .exe, .dmg)
  /osx
  /win
/docs # Docs about project.
/node_modules # Modules used for building/packaging/testing
/scss # Sass for CSS compilation in www
/spec # Tests
  AppCtrl.spec.js
www # Source code for the application
  /css
  /data
  /img
  /js
  /lib
  /node_modules # Modules here used by the application itself.
  /templates

karma.conf.js # Configuration for tests.
livereload.js # Dev script to set up live reload in Electron
package.json # Main package.json with scripts/dependencies to package/build.

Process

First we’ll need to make the exe and associated files to a dist folder. From there, we take the win dist files and pack them into the Setup.exe file, where Squirrel will do the heavy lifting to pack all this into a one step process.

npm Scripts

We’ll use the npm script pack:win task to put all our www files into a nice package (resources, exe, etc) and output to the dist folder.

pack:win step will just execute electron-packager with some relevant information. Please note the asar=true, this is recommended because sometimes node_modules can get nested a few times and the file paths will be too long for certain Windows platforms.

Script:

1
2
3
4
{
  "scripts": {
    "pack:win": "electron-packager ./www \"Project\" --out=dist/win --platform=win32 --arch=ia32 --version=0.29.1 --icon=build/resources/icon.ico --version-string.CompanyName=\"My Company\" --version-string.ProductName=\"Project\" --version-string.FileDescription=\"Project\" --asar=true"
  }

Electron Build script

I used a simple build script in node to assist in some of the heavy lifting. I recommend getting an Extended Validation certificate from this blog post.

This will take the windows package in dist/win and create dist/win/Setup.exe.

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
#!/usr/bin/env node
// File is in root/build/win/build.js
// First call nuget pack Package.nuspec
// Then you'll have Project.<version>.nupkg
// Run Squirrel.exe --releaseify Project.<version>.nupkg --icon iconPath --loadingGif loadingGifPath
// resources in build/resources/

//Need to get around weird command line passes with windows paths
function addWindowsPathFix(path) {
  return ['"', path, '"'].join('');
}

var childProcess = require('child_process'),
  path = require('path'),
  packageJsonPath = path.join(__dirname, '..', '..', 'package.json'),
  packageJson = require(packageJsonPath),
  loadingGifPath = path.join(__dirname, '..', 'resources', 'windows-loader.png'),
  nugetPackageSpecPath = path.join(__dirname, 'Package.nuspec'),
  nugetPackageOutputPath = path.join(__dirname),
  nugetPackageName = ['Project', '.1.0.0', '.nupkg'].join(''),
  // nugetPackageName = ['Project', packageJson.version, '.nupkg'].join(''),
  nugetPackagePath = path.join(nugetPackageOutputPath, nugetPackageName),
  nugetExePath = path.join(__dirname, 'nuget.exe'),
  setupIconPath = path.join(__dirname, '..', 'resources', 'icon.ico'),
  setupReleasePath = path.join(__dirname, '..', '..', 'dist', 'win'),
  signatureCertificatePath = path.join(__dirname, 'Certificate.pfx'),
  signParams = ['"/a /f "', addWindowsPathFix(signatureCertificatePath), '" /p ', process.env.PRIVATE_CERT_PASSWORD, '"'].join(''),
  squirrelExePath = path.join(__dirname, 'Squirrel.exe');

  console.log('sign params', signParams);

var createNugetPackageCommand = [addWindowsPathFix(nugetExePath), 'pack', addWindowsPathFix(nugetPackageSpecPath), '-OutputDirectory', addWindowsPathFix(nugetPackageOutputPath)].join(' ');
var createSetupCommand = [
              addWindowsPathFix(squirrelExePath),
              '--releasify', addWindowsPathFix(nugetPackagePath),
              '--loadingGif', addWindowsPathFix(loadingGifPath),
              '--icon', addWindowsPathFix(setupIconPath),
              '--releaseDir', addWindowsPathFix(setupReleasePath),
              '--signWithParams', signParams
            ].join(' ');


console.log('Creating nuget package from nuget spec file:', nugetPackageSpecPath);
// console.log(createNugetPackageCommand);
childProcess.execSync(createNugetPackageCommand);
console.log('Created nuget package');

console.log('Building Setup.exe');
// console.log(createSetupCommand);
childProcess.execSync(createSetupCommand);
console.log('Built Setup.exe');

Hope this helps!

Lazy Loading Your Node Modules

less than a 1 minute read

While working at Ionic I’ve been focused on the Ionic CLI.

My first big refactor of the CLI was pulling out most of the 21 commands it offers into an external library (ionic-app-lib) that could be consumed by both the Ionic CLI and our GUI – Ionic Lab.

The refactor went rather smoothly.

However, one thing happened that was not expected – now that the ionic-app-lib bundled all the commands together, whenever you required the app-lib module, it was rather slower than expected.

For example, whenever you ran: var IonicAppLib = require('ionic-app-lib'); – it would take a wee bit longer.

Here’s the code for the included moduled ionic-app-lib:

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
var browser = require('./lib/browser'),
    configXml = require('./lib/config-xml'),
    cordova = require('./lib/cordova'),
    events = require('./lib/events'),
    hooks = require('./lib/hooks'),
    info = require('./lib/info'),
    ioConfig = require('./lib/io-config'),
    login = require('./lib/login'),
    logging = require('./lib/logging'),
    multibar = require('./lib/multibar'),
    opbeat = require('./lib/opbeat'),
    project = require('./lib/project'),
    share = require('./lib/share'),
    semver = require('semver'),
    serve = require('./lib/serve'),
    settings = require('./lib/settings'),
    setup = require('./lib/setup'),
    start = require('./lib/start'),
    state = require('./lib/state'),
    upload = require('./lib/upload'),
    utils = require('./lib/utils');

module.exports = {
  browser: browser,
  configXml: configXml,
  cordova: cordova,
  events: events,
  hooks: hooks,
  info: info,
  ioConfig: ioConfig,
  login: login,
  logging: logging,
  multibar: multibar,
  opbeat: opbeat,
  project: project,
  share: share,
  semver: semver,
  serve: serve,
  settings: settings,
  setup: setup,
  start: start,
  state: state,
  upload: upload,
  utils: utils
}

As you can see, whenever this module is require’d in, it require’s even more modules. This means, more file read requests and fulfilling those just to get this module working.

Also to note – anytime a new command was added in, it must be exported by adding in another annoying require statement.

Lazy loading via JavaScript getters

While looking through other open source projects, I came across the idea of lazy loading your modules on demand.

One way to do this is with JavaScript getters being defined. We wont require the module until it is requested.

For example, the code snippet:

1
2
3
4
5
var IonicAppLib = require('ionic-app-lib');
var options = { port: 8100, liveReloadPort: 35729 };

//Do not load the serve command until it is requested as below:
IonicAppLib.serve.start(options);

What’s happening above – require('ionic-app-lib') is called, which sets up the getters for start, serve, run, etc. Then, when the command is called, the require for the module then happens, thereby getting the module loaded, and returning it to the caller.

Here’s that code to enforce the lazy loading of modules:

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
var fs = require('fs'),
    IonicAppLib = module.exports,
    path = require('path');

var camelCase = function camelCase(input) {
    return input.toLowerCase().replace(/-(.)/g, function(match, group1) {
        return group1.toUpperCase();
    });
};

//
// Setup all modules as lazy-loaded getters.
//
fs.readdirSync(path.join(__dirname, 'lib')).forEach(function (file) {
  file = file.replace('.js', '');
  var command;

  if (file.indexOf('-') > 0) {
    // console.log('file', file);
    command = camelCase(file);
  } else {
    command = file;
  }

  IonicAppLib.__defineGetter__(command, function () {
    return require('./lib/' + file);
  });
});

IonicAppLib.__defineGetter__('semver', function () {
  return require('semver');
});

Testing

I threw together a quick test to ensure that all of the modules were still correctly being accessible:

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
var index = require('../index');

describe('index', function() {

  it('should have index defined', function() {
    expect(index).toBeDefined();
  });

  function testForProperty(input) {
    it('should have ' + input + ' available', function() {
      expect(index[input]).toBeDefined();
    });
  }

  var objs = ['browser', 'configXml', 'cordova', 'events', 'hooks', 'info',
              'ioConfig', 'login', 'logging', 'multibar', 'opbeat', 'project',
              'share', 'semver', 'serve', 'settings', 'setup', 'start', 'state',
              'stats', 'upload', 'utils'];

  // Doing it this way to give better failure messages. 
  // Ensures all commands are available currently from
  objs.forEach(function(obj) {
    // expect(index[obj], obj).toBeDefined();
    testForProperty(obj);
  });

});

Gotchas

For one – you’ll need to ensure your files adhere to some naming conventions. For our commands, we had some with hyphens (-) that we had to account for, as you can see above if (file.indexOf('-') > 0).

Also – if you want to export other modules you can set up other getters, as I did with semver above.

If you want to short circuit lazy loading, go ahead and just export them as normal.

Performance

We say about a 8x performance increase by lazy loading the modules.

CLI run times:

1
2
Not lazy loading modules:   830ms
Lazy loading modules:       200ms

Enjoy!

Codesigning Electron Applications

less than a 1 minute read

Lately I’ve been busy at work creating and maintaining Ionic Lab. It’s been a fun and challenging problem using HTML/CSS/JavaScript to create native OSX/Windows applications.

I’m going to admit – I’ve gotten a few hybrid projects on the App store. Honestly though I had a lot of help.

This time I was mostly on my own.

I’m not great at the native dev and half the problems I occur are with the platform I am dealing with. In this I mean – Android I deal with how Google does signing and releasing and how Apple does signing and releasing.

I’m going to cover mainly Apple policies to release an app on your own with or without the App store. With Electron, we’re going to make a native build, so we’ll need to know how to do this.

Mac’s Gatekeeper

On Mac OSX, there’s an application that checks all the applications you download and run to see if they are valid and trusted.

Certainly you’ve seen the message from an app you’ve downloaded: "App can't be opened because it is from an unidentified developer."

If you create and app and do not codesign it with a valid Apple dev account, your users will see this. It’s not a good thing.

How to codesign

The main tool of codesigning is the CLI tool codesign.

I really found a lot of help from OSX Code Signing in Depth.

It’s pretty clear right away what you need to run and how to verify what you need to run. I’d like to go over how to do it with Electron, specifically.

I posted the script below. I want to highlight the issues I ran into as a result of my ignorance.

One issue I ran into – I was using the “Mac Development” certificate to sign – and when I ran the verify command (codesign -vvvv -d "/path/to/MyApp.app") it gave me a good to go signal. When I ran the security CLI command (spctl --assess -vvvv "/path/to/MyApp.app"), it was rejected.

My error: I thought “Mac Development” was a “Developer-ID application”.

It’s not. I was an account admin. In the Apple Member Center for Certificate Administration, I could only set up a “Mac Development” type certificate. Apple member center would not let met set up a “Developer ID Application” certificate. You need a ‘team agent’ to set one up for you. (That or become a team agent)

That being said – ensure you sign with a certificate type of “Developer ID Application” to sign with, and you’re good to go.

I set up my codesign script like the following. There’s comments to help understand:

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
# Invoke this script with a relative `.app` path
# EX:
# codesign.sh "dist/osx/MyApp-darwin-x64/MyApp.app"

# I had better luck using the iPhoneOS codesign_allocate
export CODESIGN_ALLOCATE="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/codesign_allocate"

# Next two are specified in Apple docs:
# export CODESIGN_ALLOCATE="/Applications/Xcode.app/Contents/Developer/usr/bin/codesign_allocate"
# export CODESIGN_ALLOCATE="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/codesign_allocate"

# However, neither worked, and gave error:
# /Users/JoshBavari/Development/ionic-gui/dist/osx/MyApp-darwin-x64/MyApp.app/Contents/Frameworks/Electron Framework.framework/Electron Framework: cannot find code object on disk

#Run the following to get a list of certs
# security find-identity
app="$PWD/$1"
identity="<ENTER_ID_OF_RESULT_FROM_SECURITY_FIND_IDENTITY_COMMAND>"

echo "### signing frameworks"
codesign --deep --force --verify --verbose --sign "$identity" "$app/Contents/Frameworks/Electron Framework.framework/Electron Framework"
codesign --deep --force --verify --verbose --sign "$identity" "$app/Contents/Frameworks/Electron Framework.framework/"
/Versions/A"
codesign --deep --force --verify --verbose --sign "$identity" "$app/Contents/Frameworks/Electron Framework.framework/Versions/Current/Electron Framework"
codesign --deep --force --verify --verbose --sign "$identity" "$app/Contents/Frameworks/Electron Helper EH.app/Contents/MacOS/Electron Helper EH"
codesign --deep --force --verify --verbose --sign "$identity" "$app/Contents/Frameworks/Electron Helper NP.app/Contents/MacOS/Electron Helper NP"
codesign --deep --force --verify --verbose --sign "$identity" "$app/Contents/Frameworks/Electron Helper NP.app/Contents/MacOS/Electron Helper NP"
codesign --deep --force --verify --verbose --sign "$identity" "$app/Contents/Frameworks/MyApp Helper.app/Contents/MacOS/MyApp Helper"
codesign --deep --force --verify --verbose --sign "$identity" "$app/Contents/Frameworks/Mantle.framework/Mantle"
codesign --deep --force --verify --verbose --sign "$identity" "$app/Contents/Frameworks/Mantle.framework/Versions/A"
codesign --deep --force --verify --verbose --sign "$identity" "$app/Contents/Frameworks/ReactiveCocoa.framework/ReactiveCocoa"
codesign --deep --force --verify --verbose --sign "$identity" "$app/Contents/Frameworks/ReactiveCocoa.framework/Versions/A"
codesign --deep --force --verify --verbose --sign "$identity" "$app/Contents/Frameworks/Squirrel.framework/Squirrel"
codesign --deep --force --verify --verbose --sign "$identity" "$app/Contents/Frameworks/Squirrel.framework/Versions/A"

echo "### signing app"
codesign --deep --force --verify --verbose --sign "$identity" "$app"


echo "### Zipping app"
ditto -c -k --sequesterRsrc --keepParent dist/osx/MyApp-darwin-x64/MyApp.app/ dist/osx/MyApp-Mac.zip

echo "### verifying signature",
codesign -vvvv -d "$app"
sudo spctl -a -vvvv "$app"

Pitfalls

Since I wasn’t very familiar with the Apple specifics I’d like to high light a few pitfalls I ran into with my ignorance.

A ‘Developer-ID signed app’ means setting up a certificate (private key + cert) with “type” as “Developer ID Application”. This does NOT mean a “Mac Development” certificate. From the OSX Codesigning guide:

Like Gatekeeper, spctl will only accept Developer ID-signed apps and apps downloaded from the Mac App Store by default. It will reject apps signed with Mac App Store development or distribution certificates.

Issues

Most users say to specify this environment variable:

export CODESIGN_ALLOCATE="/Applications/Xcode.app/Contents/Developer/usr/bin/codesign_allocate"

For some reason, I couldn’t use the default codesign allocate as specified in the Github issue above. Instead, I had to go with this Environment variable for CODESIGN_ALLOCATE for iPhoneOS.platform:

export CODESIGN_ALLOCATE="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/codesign_allocate"

Hints

Only include signed code in directories that should contain signed code. Only include resources in directories that should contain resources. Do not use the —resource-rules flag or ResourceRules.plist. They have been obsoleted and will be rejected.

A little note on signing frameworks [5]:

Signing Frameworks

When you sign frameworks, you have to sign a specific version. So, let’s say your framework is called CSMail, you’d sign CSMail.framework/Versions/A. If you try and just sign the top level folder it will silently fail, as will CSMail.framework/Versions/Current (see “Symbolic Links” below).

Symbolic Links

Any symbolic links will be silently ignored and this extends to the path you give to the codesign command line utility. I think there’s actually a problem with symbolic links: you can add them to a Resources folder and it won’t invalidate the signature (whereas you cannot add normal files). I’ve reported this to Apple (rdar://problem/6050445).

Helpful links

  1. Apple Code Signing Overview
  2. Apple OS X Code Signing In Depth
  3. Apple Anatomy of Framework Bundles
  4. Apple codesign Man Page
  5. Chris Suter’s Blog – Code signing
  6. Stackoverflow – Creating Symlinks in OSX Frameworks
  7. How to sign your Mac OSX app for Gatekeeper
  8. Codesigning and Mavericks 9 Atom Electron – Signing Mac App
  9. Codesign – useful info in Xcode > 5.0
  10. Electron for the Mac App Store
  11. nw.js issue about code signing.

Writing Unit Tests for Electron and AngularJS

less than a 1 minute read

Unit testing is something most of us dev’s don’t think much of. Until we encounter some simple to solve bugs or have regressions in code that drives us crazy.

JavaScript testing itself is hard with no clear cut path to take. Most times, you’ll have to decide important things for yourself as far as which testing framework to use and the tools to do them.

I enjoy Jasmine testing framework right now. For my node projects, I like to use the node package jasmine-node. However, Electron is basically a web browser with node conveniences, so we want to test browser related things.

Since Electron applications take a unique approach to combining elements from the browser with conveniences from node, such as require, __dirname, global and other keywords specific to node, testing gets a little more complicated.

I’m going to outline a few of the approaches I took. I’m sure they are not perfect, I’m still learning and I’m outlining that here.

Tools of the trade

I outlined some things I did to test AngularJS in a previous post. I pretty much use the same tools and set up:

1
npm install -g karma karma-jasmine karma-phantomjs-launcher karma-spec-reporter phantomjs

Now I’ve got my karma.config.js file:

1
2
3
4
5
6
7
8
9
//..snip..
// list of files / patterns to load in the browser
files: [
  'www/lib/angular/angular.min.js',
  'node_modules/angular-mocks/angular-mocks.js',
  'www/js/**/*.js',
  'spec/**/*.js'
]
//..snip..

Now we’re set up to do some testing!

Exposing require to AngularJS service

I first wanted a one stop shop for all my node conveniences in one angular js service to contain what Electron provides.

Here’s my service:

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
angular.module('app.services')
.factory('NodeService', function() {
  var fixPath = require('fix-path'),
      fs = require('fs'),
      ipc = require('ipc'),
      opn = require('opn'),
      path = require('path'),
      shell = require('shell');

  //Fixes the path issue with node being run outside of this GUI  
  fixPath();
  process.env.PATH = process.env.PATH + ':/usr/local/bin';

  //Path from root -> 'www'
  //__dirname == 'www' dir
  var appJsonPath = path.join(__dirname, 'package.json');
  var appJson = require(appJsonPath);

  return {
    appJson: appJson,
    fixPath: fixPath,
    fs: fs,
    ipc: ipc,
    opn: opn,
    path: path;
  };
});

Test set up for Service

Now, hopefully I have all my node conveniences in one place (require, __dirname, etc).

Let’s get a simple test up:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
describe('#NodeService', function() {
  var NodeService;

  beforeEach(function() {
      //Ensure angular modules available
    module('app.services');
  });

  beforeEach(inject(function(_NodeService_) {
    NodeService = _NodeService_;
  }));

  it('should have node service defined', function() {
    expect(NodeService).toBeDefined();
  });
});

If we run this test without anything else, we’ll see immediately a problem:

1
ReferenceError: Can't find variable: require

My approach to this is simple – create a faked out global variable that represents require and does what you want, such as:

1
2
3
4
5
6
7
8
9
10
11
12
13
var fakePackageJson = { name: "Fake package.json name" };
window.require = function(requirePath) {
  console.log('Requiring:', requirePath);
  switch(requirePath) {
    case 'ipc':
      return ipcSpy;
    case 'fs':
      return fsSpy;
    case '/spec/package.json':
      return fakePackageJson;
  }
};
window.__dirname = '/some/fake/path';

Package.json test setup

Let’s define some quick scripts to run from our package.json to help others run our tests:

1
2
3
4
5
//..snip..
  "scripts": {
    "test": "karma start"
  }
//..snip

Now when we run our tests, we’ll have the faked out node modules passed back.

This is just one approach to take to setting up some faking out for node modules using Electron, Angular JS, and Jasmine.

Hope this helps.

Comparisons of nw.js and Electron

less than a 1 minute read

In the last few months, I’ve been playing around with two tools to help bridge the gap between the web and native desktop applications. There are two main tools that come to mind – nw.js (formerly known as Node Webkit) and Electron (formerly known as Atom Shell).

This post focuses on using both, the differences between the two, and focusing on issues that I’ve encountered.

Outline:

  • Getting started – package.json
  • Native Menus (application menu)
  • Shell execution (child processes)
  • Packaging / run
  • Icons
  • Performance

Nw.js

Getting started

Nw.js and Electron share a lot of the same steps for getting started. The only real difference between the two is how they are run, and how they handle the node process internally.

With Nw.js, your app is bundled together. With Electron, the application is set up differently – with the main node process the handle running the browser process, and the rendering process, which handles all things from the browser (the event loop).

To get running, download the nw.js app or the electron app. Both of these applications look at your package.json file to get running by looking at the main attribute.

Bootstrapping

For nw.js, the main attribute should specify which html file to start loading when your application launched. With Electron, your main attribute should specify a JavaScript file to be run.

You also specify attributes about the nw.js window that runs via the window attribute, things like toolbar, width, and height, notably.

With Electron, the JS file that you specify will launch the browser window and specify other attributes like width, height, and other window attributes.

For convenience sake, I also created a node run script to execute the Nw.js app with my current folder. To run the node-webkit app, you simply type npm run nwjs. I also included a livereload script to watch my www folder to live reload my changes in the nw.js app.

Here’s a quick look at the package.json file used to bootstrap nw.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
  "name": "nwjs-app",
  "version": "1.0.0",
  "description": "",
  "main": "www/index.html",
  "scripts": {
    "nwjs": "/Applications/nwjs.app/Contents/MacOS/nwjs . & node livereload",
    "electron": "/Applications/Electron.app/Contents/MacOS/Electron . & node livereload"
  },
  "window": {
    "toolbar": true,
    "width": 800,
    "height": 500
  }
}

Here’s a quick look at the package.json file used to bootstrap Electron:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
  "name": "nwjs-app",
  "version": "1.0.0",
  "description": "",
  "main": "src/main.js",
  "scripts": {
    "nwjs": "/Applications/nwjs.app/Contents/MacOS/nwjs . & node livereload",
    "electron": "/Applications/Electron.app/Contents/MacOS/Electron . & node livereload"
  },
  "window": {
    "toolbar": true,
    "width": 800,
    "height": 500
  }
}

Additionally for Electron, my main.js file looks like the following:

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
var app = require('app');  // Module to control application life.
var BrowserWindow = require('browser-window');  // Module to create native browser window.
var Menu = require('menu');
var ipc = require('ipc');

// var menu = new Menu();
// Report crashes to our server.
// require('crash-reporter').start();

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the javascript object is GCed.
var mainWindow = null;
var menu;

var browserOptions = {
  height: 600,
  title: 'Electron App',
  width: 800
};

// Quit when all windows are closed.
app.on('window-all-closed', function() {
  if (process.platform != 'darwin')
    app.quit();
});

// This method will be called when Electron has done everything
// initialization and ready for creating browser windows.
app.on('ready', function() {
  // Create the browser window.
  mainWindow = new BrowserWindow(browserOptions);

  // and load the index.html of the app.
  mainWindow.loadUrl('file://' + __dirname + '/www/index.html');

  // Emitted when the window is closed.
  mainWindow.on('closed', function() {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null;
  });

  ipc.on('update-application-menu', function(event, template, keystrokesByCommand) {
    //Go through the templates, wrap their click events back to the browser
    console.log('update-application-menu - template');
    console.log(template);
    translateTemplate(template, keystrokesByCommand);
    menu = Menu
    Menu.setApplicationMenu(menu);
  });
});

Native Menus

Electron

Due to the way electron is split up into two processes, the main process (that handles native menus) and the browser process (mainly your app), menus are mainly available to be set on the main process.

If you want your app to change your application menus, you’ll need to use the ipc module electron provides to get a message out to the main process to update the menus.

Other than that, the menu system is super easy if you wish to use static menus.

Nw.js

It’s dead simple. Since it’s all one bundled process, just call the set menu, and you’re good. It’s easy to set short cuts and modify the menus.

Shell execution

In nw.js, you’re good to go when it comes to making external shell calls.

When it comes to electron, make sure you spawn your child processes with the pipe stdio option. Without that option, you may run into some errors (due to the fact electron doesnt have a stdout it manages easily).

Packaging / running

It’s really easy on both platforms. Just set up your package.json/index.html/main.js file and run the appropriate command.

I don’t have a lot of experience with nw.js, so I cant speak to the packaging process.

For electron, to run I like to use electron-prebuilt to run my www files as an app, using electron-packager to package into an .app file, and electron-builder to create installers (dmg/setup.exe).

Icons

To get custom icons for your app files for Mac, you need an .icns file that bundles up all your icons in all the formats/sizes for your dock icon, your cmd+tab icon, and your running icon.

I used this as a walkthrough.

I first started with a size of 1024x1024 pixels, then used the following commands:

1
2
3
4
5
6
7
8
9
10
11
12
# Enter app.iconset, drop in icon.png as a 1024 x 1024 image.
# Run the following commands:
sips -z 16 16     icon.png --out ./icon_16x16.png
sips -z 32 32     icon.png --out ./icon_16x16@2x.png
sips -z 32 32     icon.png --out ./icon_32x32.png
sips -z 64 64     icon.png --out ./icon_32x32@2x.png
sips -z 128 128   icon.png --out ./icon_128x128.png
sips -z 256 256   icon.png --out ./icon_128x128@2x.png
sips -z 256 256   icon.png --out ./icon_256x256.png
sips -z 512 512   icon.png --out ./icon_256x256@2x.png
sips -z 512 512   icon.png --out ./icon_512x512.png
cp icon.png icon_512x512@2x.png

Then just run:

1
iconutil -c icns app.iconset -o ./app-dir/YourAppName.app/Contents/Resources/app.icns

You should now have your app with icons ready to go.

Performance

I didn’t see a lot of major performance bumps from using either platform. It’s JavaScript after all.

Closing words

Most of all, have fun with developing with these tools! They’re open source and free, so when you get a chance, share some knowledge, post an issue, respond to an issue, or even submit a PR.

We’re all in this together.