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.
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
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.
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
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
$enyo in your
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.
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 (
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.
dev.html files looks like this:
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:
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.jsif 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:
- Creates a
tools/compiled/folder in which it will collect in-process files (I exclude the
compiledfolder from git in my
- Creates a
build/www/folder in which it will output the final production build
- Uses Enyo’s built-in minifier to package the app’s core files
yuicompressor-2.4.7.jarinstalled in the
toolsfolder so I don’t have to worry about where it is in the path
- Copies images, css, compiled scripts, and the
index.htmlfile into the
And here is the code:
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.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!