Organizing and packaging an Enyo 2 app

I enjoy using the Enyo framework to write apps (mainly because I am familiar with it from webOS development; it’s not perfect for everything by any means, but it’s one of the fastest methods for me to move from a mockup to a working app), and lately that has meant experimenting around with the pre-release (but public) version of Enyo 2.0. Unfortunately, Enyo 2’s documentation is pretty hit and miss at the moment. If you have used Enyo in the past most aspects of Enyo 2 should be very familiar, but for some tasks you simply need to dig into the source code and figure out how things work by hand.

One of those tasks is building Enyo for use in a production environment, and since I’ve been fighting with this over the course of my development of TapWatch, I figured I would share how I am doing things.

Project organization

To start, here’s how I typically organize things in my project’s root folder (this is certainly not proscriptive, but you need to know it to understand the logic behind the scripts that follow):

- build/
- css/
- images/
- source/
  - enyo/
  - lib/
    - onyx/
  - kinds/
  - package.js
- tools/
  - build.sh
  - package.js
- dev.html
- index.html

Working top to bottom, build is where my final production builds will end up, while css and images are where I store my common stylesheets and image files. Keeping these both in the root of the project makes things easier when it comes to previewing the app during development.

The source folder is where I store all of my Javascript files. Enyo 2 will automatically load package.js when you link against its parent folder, so the root package.js file is my access-point for all of my app-specific functionality. I typically store my custom app kinds in the kinds folder, although depending on the complexity of the app I might break them up differently (for instance, organize based on views, models, and so forth). Where you store your app code doesn’t matter a jot, to be honest. You can go as simple or complicated as you want.

I use git to manage my project, and the enyo and onyx folders are git submodules pointing to their respective GitHub repositories. I like using submodules because it makes it ridiculously easy to test out bleeding edge additions, while still being able to fall back to a particular commit or tag that I know is stable if I need to prep a build for distribution. Using submodules also allows me to experiment with different versions of Enyo and Onyx for different apps. If I were storing it in a central location, I could inadvertently break things in one app by updating Enyo for use with another. GitBox, my favorite Mac git client, provides great support for submodules; after you add them, you can manage the submodule just like another repository, and it’s one click to revert to your last saved commit if you are experimenting with bleeding edge commits.

The relationship between the enyo folder and the lib folder containing Onyx and any other official or third-party packages is something you will want to maintain. By placing your packages in lib next to the enyo root folder you can very easily access your packages without worrying about their specific placement using the special strings $lib and $enyo in your package.js files.

The tools folder is where I store my build.sh script that is responsible for putting together my production builds along with other utilities; more on that in a bit. The package.js file inside of tools simply links against the Enyo source and my app’s main package; this is used by Enyo when building itself for production use.

Lastly, dev.html is my entry point to quickly preview my app in a browser, while index.html is the actual HTML file that I will use in my production builds. These two are different because the development version needs to link against my various CSS resources, Enyo, and my app source separately, while the production version links against a much smaller number of compressed files.

Of course, I include a number of other things in my project root folder that aren’t shown here both to cut down on the complexity and because they are not applicable to all projects. For instance, I typically store platform-specific code and resources in top-level folders (iOS, webOS, etc.).

HTML files

Before you need to worry about building your production scripts, you will want to setup your HTML to allow you to process your app. As you can see above, I keep at least two copies around: a dev.html file for quick browser testing, and index.html for the actual production code.

My TapWatch dev.html files looks like this:

<!DOCTYPE HTML>
<html lang="en-US">
<head>
    <meta charset="UTF-8">
    <title>TapWatch Dev</title>

    <!--Include Enyo (debugging); automatically includes Enyo styles-->
    <script src="source/enyo/enyo.js" type="text/javascript"></script>

    <!--Include styles-->
    <link rel="stylesheet" href="css/styles.css" type="text/css">

    <!--Include application-->
    <script src="source/package.js" type="text/javascript"></script>

    <!--Configure for viewing on mobile devices-->
    <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no">

    <!--LiveReload, for live refreshing-->
    <script>document.write('<script src="http://' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1"></' + 'script>')</script>
</head>
<body>
    <script type="text/javascript">
        new TapWatch.app().write();
    </script>
</body>
</html>

Most of this is stuff you can just copy and paste straight into your own app (aside from the point where I initialize TapWatch, of course).

One item of interest is the LiveReload integration. LiveReload is an awesome tool for Macs (although I believe there’s a Window pre-release version, too) that can watch your web folder and do things like automatically compile LESS files every time you save and then ping the preview that the styles have changed. I use this in conjunction with the Espresso preview to have a preview of my app in my editor that updates while I work. This is an insanely helpful bit of wizardry; being able to see my changes in live time really speeds up my workflow.

As for the production-ready index.html, it’s a bit simpler:

<!DOCTYPE HTML>
<html lang="en-US">
<head>
    <meta charset="UTF-8">
    <title>TapWatch</title>

    <!--Include our styles-->
    <link rel="stylesheet" href="css/styles.css" type="text/css">

    <!--Include our application sourcecode-->
    <script src="sources.js" type="text/javascript"></script>

    <!--Configure for viewing on mobile devices-->
    <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
</head>
<body>
    <script type="text/javascript">
        new TapWatch.app().write();
    </script>
</body>
</html>

The links to sources.js and so forth rely on my specific build layout; trying to preview this file from anywhere but a final build folder does nothing.

The tools folder and build.sh

In order to build your production app you are going to need to get your hands dirty with a little shell scripting. Never fear, though! The process is fairly simple, and the necessary shell commands innocuous.

You will probably want to do some or all of the following:

  • Compile Enyo, any third-party packages you depend on, and your app’s code into a single file (this is a very easy one-step process, but it will require that you install node.js first)
  • Concatenate and minify your built app with third-party scripts (like cordova.js if you are building a PhoneGap app)
  • Concatenate and minify your CSS, if you have more than one CSS file
  • Copy the files you need for a production build (and only those files) into your build directory for distribution
  • Perform any platform-specific logic

In order to accomplish these tasks, my personal build.sh script does the following:

  1. Creates a tools/compiled/ folder in which it will collect in-process files (I exclude the compiled folder from git in my .gitignore file)
  2. Creates a build/www/ folder in which it will output the final production build
  3. Uses Enyo’s built-in minifier to package the app’s core files
  4. Further concatenates and minifies CSS and Javascript using YUICompressor (this step is entirely optional, or you could always use a different minifier); I have yuicompressor-2.4.7.jar installed in the tools folder so I don’t have to worry about where it is in the path
  5. Copies images, css, compiled scripts, and the index.html file into the build/www/ folder

And here is the code:

#!/bin/bash

# Setup path to node (to make sure it's in the path)
export PATH="/path/to/node/bin:$PATH"
export NODE_PATH="/path/to/node:/path/to/node/lib/node_modules"

# Make sure the base directory is the tools directory
# This makes sure relative paths always work right
ORIGINAL_PWD="$pwd"
cd "$( dirname "${BASH_SOURCE[0]}" )"

# Ensure basic paths exist
# If we don't do this, later actions might fail
mkdir -p compiled/enyo-min
mkdir -p compiled/css
mkdir -p ../build/www/images
mkdir -p ../build/www/css

# Build the app and Enyo
../source/enyo/tools/minify.sh -no-alias -output compiled/enyo-min/app package.js

# YUI compress our Javascript
cat compiled/enyo-min/app.js | java -jar yuicompressor.2.4.7.jar -o compiled/sources.js --type js

# YUI compress our CSS, as well
cat compiled/enyo-min/app.css ../css/styles.css | java -jar yuicompressor-2.4.7.jar -o compiled/css/styles.css

# WWW build
# Copy our latest images, CSS, and HTML to the www directory
rsync -av ../images/ ../build/www/images/
cp compiled/css/styles.css ../build/www/css/styles.css
cp ../index.html ../build/www/index.html
cp compiled/sources.js ../build/www/sources.js

# Resume our working directory
cd "$ORIGINAL_PWD"

Of course, this is pretty specific to my own project; you would likely be using completely different paths for some of the items, and you might not want the extra minification and so forth.

The most important bit is the line that builds Enyo and the app:

../source/enyo/tools/minify.sh -no-alias -output compiled/enyo-min/app package.js

As best I can tell, the -no-alias argument has to do with how Enyo dependency loading is handled. I have not had a chance to test what aliases do, though. The -output argument specifies the file name (with optional folders prepending it). So in this case, the final files will be called app.js and app.css, and will be stored in the compiled/enyo-min/ folder. There are a couple of other arguments, but when loading the minify script from directly within your Enyo installation, they don’t appear to be necessary. You can always use the -h argument for a full listing.

In order for the Enyo minify.sh script to work, you will want to include this in your tools/package.js file to tell it to combine Enyo with your app:

enyo.depends(
    '$enyo/source/minify/',
    '../source/'
);

There are some other fun things you can do in the build script, as well. For instance, if you are building an iOS app using PhoneGap or similar, you can use the following conditional statements to process differently when you are running the script from an Xcode build step vs. directly:

if [ -z "$IPHONEOS_DEPLOYMENT_TARGET" ]; then
    # SCRIPT EXECUTED DIRECTLY
fi
if [ -n "$IPHONEOS_DEPLOYMENT_TARGET" ]; then
    # EXECUTED FROM XCODE IOS BUILD STEP
fi

And of course you can add platform-specific build steps using the same basic tools (rsync -av to copy all files in a folder, cp to copy a single file, and mkdir -p to make sure an entire directory path exists are all very handy).

Once you have your build script setup, you can create a custom build by executing the build script in the Terminal, or by adding it to your build steps in Xcode or similar if you are building for a specific platform.

Go forth and build

Hopefully my particular setup has provided you with some ideas or a starting point for organizing and building your own app’s source for production distribution. Enjoy!

4 responses to “Organizing and packaging an Enyo 2 app”

Leave a response

  1. Tegan Snyder says:

    Ian thanks for taking the time to share this. I’m still trying to find the perfect workflow.

    I have created a phonegap project in xcode. Since xcode editor sucks with HTML/JS/CSS I use espresso to do my edits. I’m trying to find a solid build script to take my project and run it through uglifyjs.

    Here is my plan:

    Create one build directory. This is the directory that the xcode project uses for phonegap.

    Create one dev directory. This contains my espresso project.

    Then I need to create a build script to take my dev directory, minify everything and output to my build directory.

    Thoughts?

    • Ian Beck says:

      That’s effectively what I’m doing with my build script above. Some additional points about how I integrate the build script with Xcode:

      Before doing any building, I created a build/www/ folder (which the script above outputs its final scripts and files to), and in Xcode added it to the project as a folder reference per the PhoneGap documentation.

      In the Xcode build phases for my target, I created the following Run Script action and moved it to the very top:

      # Run the build script to generate the www folder
      "$PROJECT_DIR"/tools/build.sh

      So when I build the project in Xcode, my project files get built by my script and moved to the build/www/ directory, then Xcode copies that directory into the resources folder for the iOS application the same as PhoneGap normally works and I end up with the final application in a different folder of my build/ folder (that’s where Xcode is setup to send my builds by default; if you are saving them elsewhere the final product might end up somewhere differently).

      Hope that’s helpful!

  2. web development company says:

    How much has the Enyo core changed between Enyo 1 and Enyo 2? How is Enyo 2 organized? Can I use Enyo to build packaged apps for iOS? Android?

    • Ian Beck says:

      There’s a fair amount of change between Enyo 1 and 2. Although the core ideas are unchanged (kinds and their inheritance, Components, Controls, etc.), the UI layer has changed quite a lot and there is a fair amount of adjustment to the underlying APIs (particularly for things like Ajax calls; that whole area is completely rewritten) and of course there’s a lot of stuff that came with Enyo 1 that is no longer relevant (like db8, services, and other webOS-specific things).

      You can certainly use Enyo 2 for packaging apps for iOS and elsewhere, although it’s by no means as easy as it sounds because different OSes have very different issues (think cross-browser testing for websites, but every device/OS has IE 6-style arbitrary problems of its own). That’s what I did for TapWatch, for instance, which runs on webOS and iOS using Enyo 2.

Respond to Ian Beck

(cancel reply)