From 88ee4757e77f97afb206132eddb64e688700eb37 Mon Sep 17 00:00:00 2001
From: Yehuda Katz <wycats@gmail.com>
Date: Mon, 1 Jul 2013 13:59:58 -0700
Subject: [PATCH] Initial work on ES6 modules

---
 Gruntfile.js                                  |  51 ++++
 configurations/browser.js                     |   6 +
 configurations/concat.js                      |  16 ++
 configurations/connect.js                     |   8 +
 configurations/transpile.js                   |  25 ++
 configurations/watch.js                       |   4 +
 lib/handlebars.js                             |  22 +-
 lib/handlebars/base.js                        | 246 +++++++++---------
 lib/handlebars/compiler/ast.js                |  39 ++-
 lib/handlebars/compiler/base.js               |  25 +-
 lib/handlebars/compiler/compiler.js           |  46 ++--
 .../compiler/javascript-compiler.js           |  24 +-
 lib/handlebars/runtime.js                     | 202 +++++++-------
 lib/handlebars/utils.js                       |  83 +++---
 package.json                                  |  54 ++--
 15 files changed, 452 insertions(+), 399 deletions(-)
 create mode 100644 Gruntfile.js
 create mode 100644 configurations/browser.js
 create mode 100644 configurations/concat.js
 create mode 100644 configurations/connect.js
 create mode 100644 configurations/transpile.js
 create mode 100644 configurations/watch.js

diff --git a/Gruntfile.js b/Gruntfile.js
new file mode 100644
index 000000000..56231b7c8
--- /dev/null
+++ b/Gruntfile.js
@@ -0,0 +1,51 @@
+function config(name) {
+  return require('./configurations/' + name);
+}
+
+module.exports = function(grunt) {
+
+  grunt.initConfig({
+    pkg: grunt.file.readJSON('package.json'),
+
+    clean: ["dist"],
+    watch: config('watch') ,
+    concat: config('concat'),
+    browser: config('browser'),
+    connect: config('connect'),
+    transpile: config('transpile')
+  });
+
+  // By default, (i.e., if you invoke `grunt` without arguments), do
+  // a new build.
+  this.registerTask('default', ['build']);
+
+  // Build a new version of the library
+  this.registerTask('build', "Builds a distributable version of the current project", [
+                    'clean',
+                    'transpile:amd',
+                    'concat:library',
+                    'concat:browser',
+                    'browser:dist',
+                    'bytes']);
+
+  this.registerTask('tests', "Builds the test package", [
+                    'build',
+                    'concat:deps',
+                    'transpile:tests']);
+
+  // Run a server. This is ideal for running the QUnit tests in the browser.
+  this.registerTask('server', [
+                    'build',
+                    'tests',
+                    'connect',
+                    'watch']);
+
+  // Load tasks from npm
+  grunt.loadNpmTasks('grunt-contrib-clean');
+  grunt.loadNpmTasks('grunt-contrib-concat');
+  grunt.loadNpmTasks('grunt-contrib-connect');
+  grunt.loadNpmTasks('grunt-contrib-watch');
+  grunt.loadNpmTasks('grunt-es6-module-transpiler');
+
+  grunt.task.loadTasks('tasks');
+};
diff --git a/configurations/browser.js b/configurations/browser.js
new file mode 100644
index 000000000..e3d0dc3b2
--- /dev/null
+++ b/configurations/browser.js
@@ -0,0 +1,6 @@
+module.exports = {
+  dist: {
+    src: 'tmp/<%= pkg.barename %>.browser1.js',
+    dest: 'dist/<%= pkg.name %>-<%= pkg.version %>.js'
+  }
+};
diff --git a/configurations/concat.js b/configurations/concat.js
new file mode 100644
index 000000000..4111eee6b
--- /dev/null
+++ b/configurations/concat.js
@@ -0,0 +1,16 @@
+module.exports = {
+  library: {
+    src: ['tmp/<%= pkg.barename %>.amd.js'],
+    dest: 'dist/<%= pkg.name %>-<%= pkg.version %>.amd.js'
+  },
+
+  deps: {
+    src: ['vendor/deps/*.js'],
+    dest: 'tmp/deps.amd.js'
+  },
+
+  browser: {
+    src: ['vendor/loader.js', 'tmp/<%= pkg.barename %>.amd.js'],
+    dest: 'tmp/<%= pkg.barename %>.browser1.js'
+  }
+};
diff --git a/configurations/connect.js b/configurations/connect.js
new file mode 100644
index 000000000..dacc7600b
--- /dev/null
+++ b/configurations/connect.js
@@ -0,0 +1,8 @@
+module.exports = {
+  server: {},
+  options: {
+    hostname: '0.0.0.0',
+    port: 8000,
+    base: '.'
+  }
+};
diff --git a/configurations/transpile.js b/configurations/transpile.js
new file mode 100644
index 000000000..e912ecfb4
--- /dev/null
+++ b/configurations/transpile.js
@@ -0,0 +1,25 @@
+module.exports = {
+  amd: {
+    type: 'amd',
+    src: ["lib/<%= pkg.barename %>.js", "lib/*/**/*.js"],
+    dest: "tmp/<%= pkg.barename %>.amd.js"
+  },
+
+  cjs: {
+    type: 'cjs',
+    src: ["lib/<%= pkg.barename %>.js", "lib/*/**/*.js"],
+    dest: "tmp/<%= pkg.barename %>.cjs.js"
+  },
+
+  globals: {
+    type: 'globals',
+    src: ["lib/<%= pkg.barename %>.js", "lib/*/**/*.js"],
+    dest: "tmp/<%= pkg.barename %>.globals.js"
+  },
+
+  tests: {
+    type: 'amd',
+    src: ['test/test_helpers.js', 'test/tests.js', 'test/tests/**/*_test.js'],
+    dest: 'tmp/tests.amd.js'
+  }
+};
diff --git a/configurations/watch.js b/configurations/watch.js
new file mode 100644
index 000000000..49b1c49bd
--- /dev/null
+++ b/configurations/watch.js
@@ -0,0 +1,4 @@
+module.exports = {
+  files: ['lib/**', 'vendor/*', 'test/**/*'],
+  tasks: ['build', 'tests']
+};
diff --git a/lib/handlebars.js b/lib/handlebars.js
index f82ec3bad..e051aa5c0 100644
--- a/lib/handlebars.js
+++ b/lib/handlebars.js
@@ -1,15 +1,19 @@
-var handlebars = require("./handlebars/base"),
+import handlebars from "handlebars/base";
 
 // Each of these augment the Handlebars object. No need to setup here.
 // (This is done to easily share code between commonjs and browse envs)
-  utils = require("./handlebars/utils"),
-  compiler = require("./handlebars/compiler"),
-  runtime = require("./handlebars/runtime");
+import { SafeString, Exception, extend, escapeExpression, isEmpty } from "handlebars/utils";
+import compiler from "handlebars/compiler";
+import runtime from "handlebars/runtime";
 
+// For compatibility and usage outside of module systems, make the Handlebars object a namespace
 var create = function() {
   var hb = handlebars.create();
 
-  utils.attach(hb);
+  hb.SafeString = SafeString;
+  hb.Exception = Exception;
+  hb.utils = { extend: extend, escapeExpression: escapeExpression, isEmpty: isEmpty };
+
   compiler.attach(hb);
   runtime.attach(hb);
 
@@ -19,10 +23,10 @@ var create = function() {
 var Handlebars = create();
 Handlebars.create = create;
 
-module.exports = Handlebars; // instantiate an instance
+export default Handlebars;
 
 // Publish a Node.js require() handler for .handlebars and .hbs files
-if (require.extensions) {
+if (typeof require !== 'undefined' && require.extensions) {
   var extension = function(module, filename) {
     var fs = require("fs");
     var templateString = fs.readFileSync(filename, "utf8");
@@ -32,10 +36,6 @@ if (require.extensions) {
   require.extensions[".hbs"] = extension;
 }
 
-// BEGIN(BROWSER)
-
-// END(BROWSER)
-
 // USAGE:
 // var handlebars = require('handlebars');
 
diff --git a/lib/handlebars/base.js b/lib/handlebars/base.js
index 44a369c5e..98496786d 100644
--- a/lib/handlebars/base.js
+++ b/lib/handlebars/base.js
@@ -1,166 +1,168 @@
 /*jshint eqnull: true */
 
-module.exports.create = function() {
+import { Exception, extend } from "handlebars/utils";
 
-var Handlebars = {};
+var K = function() { return this; };
 
-// BEGIN(BROWSER)
+export VERSION = "1.0.0";
+export COMPILER_REVISION = 4;
 
-Handlebars.VERSION = "1.0.0";
-Handlebars.COMPILER_REVISION = 4;
-
-Handlebars.REVISION_CHANGES = {
+export REVISION_CHANGES = {
   1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
   2: '== 1.0.0-rc.3',
   3: '== 1.0.0-rc.4',
   4: '>= 1.0.0'
 };
 
-Handlebars.helpers  = {};
-Handlebars.partials = {};
+// TODO: Make this a class
+export default function(helpers, partials) {
 
-var toString = Object.prototype.toString,
-    functionType = '[object Function]',
-    objectType = '[object Object]';
+  var exports = {};
 
-Handlebars.registerHelper = function(name, fn, inverse) {
-  if (toString.call(name) === objectType) {
-    if (inverse || fn) { throw new Handlebars.Exception('Arg not supported with multiple helpers'); }
-    Handlebars.Utils.extend(this.helpers, name);
-  } else {
-    if (inverse) { fn.not = inverse; }
-    this.helpers[name] = fn;
-  }
-};
 
-Handlebars.registerPartial = function(name, str) {
-  if (toString.call(name) === objectType) {
-    Handlebars.Utils.extend(this.partials,  name);
-  } else {
-    this.partials[name] = str;
-  }
-};
-
-Handlebars.registerHelper('helperMissing', function(arg) {
-  if(arguments.length === 2) {
-    return undefined;
-  } else {
-    throw new Error("Missing helper: '" + arg + "'");
-  }
-});
+  helpers = helpers || {};
+  partials = partials || {};
 
-Handlebars.registerHelper('blockHelperMissing', function(context, options) {
-  var inverse = options.inverse || function() {}, fn = options.fn;
+  var toString = Object.prototype.toString,
+      functionType = '[object Function]',
+      objectType = '[object Object]';
 
-  var type = toString.call(context);
+  exports.registerHelper(name, fn, inverse) {
+    if (toString.call(name) === objectType) {
+      if (inverse || fn) { throw new Exception('Arg not supported with multiple helpers'); }
+      extend(helpers, name);
+    } else {
+      if (inverse) { fn.not = inverse; }
+      helpers[name] = fn;
+    }
+  };
 
-  if(type === functionType) { context = context.call(this); }
+  exports.registerPartial(name, str) {
+    if (toString.call(name) === objectType) {
+      extend(partials,  name);
+    } else {
+      partials[name] = str;
+    }
+  };
 
-  if(context === true) {
-    return fn(this);
-  } else if(context === false || context == null) {
-    return inverse(this);
-  } else if(type === "[object Array]") {
-    if(context.length > 0) {
-      return Handlebars.helpers.each(context, options);
+  exports.registerHelper('helperMissing', function(arg) {
+    if(arguments.length === 2) {
+      return undefined;
     } else {
-      return inverse(this);
+      throw new Error("Missing helper: '" + arg + "'");
     }
-  } else {
-    return fn(context);
-  }
-});
+  });
 
-Handlebars.K = function() {};
+  exports.registerHelper('blockHelperMissing', function(context, options) {
+    var inverse = options.inverse || function() {}, fn = options.fn;
 
-Handlebars.createFrame = Object.create || function(object) {
-  Handlebars.K.prototype = object;
-  var obj = new Handlebars.K();
-  Handlebars.K.prototype = null;
-  return obj;
-};
-
-Handlebars.logger = {
-  DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3,
+    var type = toString.call(context);
 
-  methodMap: {0: 'debug', 1: 'info', 2: 'warn', 3: 'error'},
+    if(type === functionType) { context = context.call(this); }
 
-  // can be overridden in the host environment
-  log: function(level, obj) {
-    if (Handlebars.logger.level <= level) {
-      var method = Handlebars.logger.methodMap[level];
-      if (typeof console !== 'undefined' && console[method]) {
-        console[method].call(console, obj);
+    if(context === true) {
+      return fn(this);
+    } else if(context === false || context == null) {
+      return inverse(this);
+    } else if(type === "[object Array]") {
+      if(context.length > 0) {
+        return Handlebars.helpers.each(context, options);
+      } else {
+        return inverse(this);
       }
+    } else {
+      return fn(context);
     }
-  }
-};
-
-Handlebars.log = function(level, obj) { Handlebars.logger.log(level, obj); };
+  });
 
-Handlebars.registerHelper('each', function(context, options) {
-  var fn = options.fn, inverse = options.inverse;
-  var i = 0, ret = "", data;
+  exports.registerHelper('each', function(context, options) {
+    var fn = options.fn, inverse = options.inverse;
+    var i = 0, ret = "", data;
 
-  var type = toString.call(context);
-  if(type === functionType) { context = context.call(this); }
+    var type = toString.call(context);
+    if(type === functionType) { context = context.call(this); }
 
-  if (options.data) {
-    data = Handlebars.createFrame(options.data);
-  }
+    if (options.data) {
+      data = Handlebars.createFrame(options.data);
+    }
 
-  if(context && typeof context === 'object') {
-    if(context instanceof Array){
-      for(var j = context.length; i<j; i++) {
-        if (data) { data.index = i; }
-        ret = ret + fn(context[i], { data: data });
-      }
-    } else {
-      for(var key in context) {
-        if(context.hasOwnProperty(key)) {
-          if(data) { data.key = key; }
-          ret = ret + fn(context[key], {data: data});
-          i++;
+    if(context && typeof context === 'object') {
+      if(context instanceof Array){
+        for(var j = context.length; i<j; i++) {
+          if (data) { data.index = i; }
+          ret = ret + fn(context[i], { data: data });
+        }
+      } else {
+        for(var key in context) {
+          if(context.hasOwnProperty(key)) {
+            if(data) { data.key = key; }
+            ret = ret + fn(context[key], {data: data});
+            i++;
+          }
         }
       }
     }
-  }
 
-  if(i === 0){
-    ret = inverse(this);
-  }
+    if(i === 0){
+      ret = inverse(this);
+    }
 
-  return ret;
-});
+    return ret;
+  });
 
-Handlebars.registerHelper('if', function(conditional, options) {
-  var type = toString.call(conditional);
-  if(type === functionType) { conditional = conditional.call(this); }
+  exports.registerHelper('if', function(conditional, options) {
+    var type = toString.call(conditional);
+    if(type === functionType) { conditional = conditional.call(this); }
 
-  if(!conditional || Handlebars.Utils.isEmpty(conditional)) {
-    return options.inverse(this);
-  } else {
-    return options.fn(this);
-  }
-});
+    if(!conditional || Handlebars.Utils.isEmpty(conditional)) {
+      return options.inverse(this);
+    } else {
+      return options.fn(this);
+    }
+  });
+
+  exports.registerHelper('unless', function(conditional, options) {
+    return Handlebars.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn});
+  });
+
+  exports.registerHelper('with', function(context, options) {
+    var type = toString.call(context);
+    if(type === functionType) { context = context.call(this); }
 
-Handlebars.registerHelper('unless', function(conditional, options) {
-  return Handlebars.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn});
-});
+    if (!Handlebars.Utils.isEmpty(context)) return options.fn(context);
+  });
 
-Handlebars.registerHelper('with', function(context, options) {
-  var type = toString.call(context);
-  if(type === functionType) { context = context.call(this); }
+  exports.registerHelper('log', function(context, options) {
+    var level = options.data && options.data.level != null ? parseInt(options.data.level, 10) : 1;
+    Handlebars.log(level, context);
+  });
 
-  if (!Handlebars.Utils.isEmpty(context)) return options.fn(context);
-});
+  return Handlebars;
+}
 
-Handlebars.registerHelper('log', function(context, options) {
-  var level = options.data && options.data.level != null ? parseInt(options.data.level, 10) : 1;
-  Handlebars.log(level, context);
-});
+var levels = {
+  DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3
+}
 
-// END(BROWSER)
+var methodMap = { 0: 'debug', 1: 'info', 2: 'warn', 3: 'error' };
 
-return Handlebars;
+export logger = {
+  // can be overridden in the host environment
+  log: function(level, obj) {
+    if (Handlebars.logger.level <= level) {
+      var method = Handlebars.logger.methodMap[level];
+      if (typeof console !== 'undefined' && console[method]) {
+        console[method].call(console, obj);
+      }
+    }
+  }
+};
+
+export function log(level, obj) { logger.log(level, obj); };
+
+export createFrame = Object.create || function(object) {
+  K.prototype = object;
+  var obj = new K();
+  K.prototype = null;
+  return obj;
 };
diff --git a/lib/handlebars/compiler/ast.js b/lib/handlebars/compiler/ast.js
index 567e29747..fcf5c6e87 100644
--- a/lib/handlebars/compiler/ast.js
+++ b/lib/handlebars/compiler/ast.js
@@ -1,15 +1,12 @@
-exports.attach = function(Handlebars) {
+import { Exception } from "handlebars/utils";
 
-// BEGIN(BROWSER)
-Handlebars.AST = {};
-
-Handlebars.AST.ProgramNode = function(statements, inverse) {
+export function ProgramNode(statements, inverse) {
   this.type = "program";
   this.statements = statements;
   if(inverse) { this.inverse = new Handlebars.AST.ProgramNode(inverse); }
 };
 
-Handlebars.AST.MustacheNode = function(rawParams, hash, unescaped) {
+export function MustacheNode(rawParams, hash, unescaped) {
   this.type = "mustache";
   this.escaped = !unescaped;
   this.hash = hash;
@@ -31,15 +28,15 @@ Handlebars.AST.MustacheNode = function(rawParams, hash, unescaped) {
   // pass or at runtime.
 };
 
-Handlebars.AST.PartialNode = function(partialName, context) {
+export function PartialNode(partialName, context) {
   this.type         = "partial";
   this.partialName  = partialName;
   this.context      = context;
 };
 
-Handlebars.AST.BlockNode = function(mustache, program, inverse, close) {
+export function BlockNode(mustache, program, inverse, close) {
   if(mustache.id.original !== close.original) {
-    throw new Handlebars.Exception(mustache.id.original + " doesn't match " + close.original);
+    throw new Exception(mustache.id.original + " doesn't match " + close.original);
   }
 
   this.type = "block";
@@ -52,17 +49,17 @@ Handlebars.AST.BlockNode = function(mustache, program, inverse, close) {
   }
 };
 
-Handlebars.AST.ContentNode = function(string) {
+export function ContentNode(string) {
   this.type = "content";
   this.string = string;
 };
 
-Handlebars.AST.HashNode = function(pairs) {
+export function HashNode(pairs) {
   this.type = "hash";
   this.pairs = pairs;
 };
 
-Handlebars.AST.IdNode = function(parts) {
+export function IdNode(part) {
   this.type = "ID";
 
   var original = "",
@@ -93,43 +90,37 @@ Handlebars.AST.IdNode = function(parts) {
   this.stringModeValue = this.string;
 };
 
-Handlebars.AST.PartialNameNode = function(name) {
+export function PartialNameNode(name) {
   this.type = "PARTIAL_NAME";
   this.name = name.original;
 };
 
-Handlebars.AST.DataNode = function(id) {
+export function DataNode(id) {
   this.type = "DATA";
   this.id = id;
 };
 
-Handlebars.AST.StringNode = function(string) {
+export function StringNode(string) {
   this.type = "STRING";
   this.original =
     this.string =
     this.stringModeValue = string;
 };
 
-Handlebars.AST.IntegerNode = function(integer) {
+export function IntegerNode(integer) {
   this.type = "INTEGER";
   this.original =
     this.integer = integer;
   this.stringModeValue = Number(integer);
 };
 
-Handlebars.AST.BooleanNode = function(bool) {
+export function BooleanNode(bool) {
   this.type = "BOOLEAN";
   this.bool = bool;
   this.stringModeValue = bool === "true";
 };
 
-Handlebars.AST.CommentNode = function(comment) {
+export function CommentNode(comment) {
   this.type = "comment";
   this.comment = comment;
 };
-
-// END(BROWSER)
-
-return Handlebars;
-};
-
diff --git a/lib/handlebars/compiler/base.js b/lib/handlebars/compiler/base.js
index 759445154..525cd5bfa 100644
--- a/lib/handlebars/compiler/base.js
+++ b/lib/handlebars/compiler/base.js
@@ -1,21 +1,12 @@
-var handlebars = require("./parser");
+import { parser } from "handlebars/compiler/parser";
+module AST from "handlebars/compiler/ast":
 
-exports.attach = function(Handlebars) {
-
-// BEGIN(BROWSER)
-
-Handlebars.Parser = handlebars;
-
-Handlebars.parse = function(input) {
+export Parser = parser;
 
+export function(input) {
   // Just return if an already-compile AST was passed in.
-  if(input.constructor === Handlebars.AST.ProgramNode) { return input; }
-
-  Handlebars.Parser.yy = Handlebars.AST;
-  return Handlebars.Parser.parse(input);
-};
-
-// END(BROWSER)
+  if(input.constructor === AST.ProgramNode) { return input; }
 
-return Handlebars;
-};
+  parser.yy = AST;
+  return parser.parse(input);
+}
diff --git a/lib/handlebars/compiler/compiler.js b/lib/handlebars/compiler/compiler.js
index 98e12b1dc..a21ccc0d9 100644
--- a/lib/handlebars/compiler/compiler.js
+++ b/lib/handlebars/compiler/compiler.js
@@ -1,13 +1,11 @@
-var compilerbase = require("./base");
-
-exports.attach = function(Handlebars) {
-
-compilerbase.attach(Handlebars);
-
-// BEGIN(BROWSER)
+import { Exception } from "handlebars/utils";
+import parse from "handlebars/compiler/base";
+import { template } from "handlebars/runtime";
 
 /*jshint eqnull:true*/
-var Compiler = Handlebars.Compiler = function() {};
+
+var Compiler = function() {};
+export default Compiler;
 
 // the foundHelper register will disambiguate helper lookup from finding a
 // function in a context. This is necessary for mustache compatibility, which
@@ -40,6 +38,7 @@ Compiler.prototype = {
 
     return out.join("\n");
   },
+
   equals: function(other) {
     var len = this.opcodes.length;
     if (other.opcodes.length !== len) {
@@ -301,7 +300,7 @@ Compiler.prototype = {
   DATA: function(data) {
     this.options.data = true;
     if (data.id.isScoped || data.id.depth) {
-      throw new Handlebars.Exception('Scoped data references are not supported: ' + data.original);
+      throw new Exception('Scoped data references are not supported: ' + data.original);
     }
 
     this.opcode('lookupData');
@@ -415,35 +414,39 @@ Compiler.prototype = {
   }
 };
 
-Handlebars.precompile = function(input, options) {
+export function precompile(input, options) {
   if (input == null || (typeof input !== 'string' && input.constructor !== Handlebars.AST.ProgramNode)) {
-    throw new Handlebars.Exception("You must pass a string or Handlebars AST to Handlebars.precompile. You passed " + input);
+    throw new Exception("You must pass a string or Handlebars AST to Handlebars.precompile. You passed " + input);
   }
 
   options = options || {};
   if (!('data' in options)) {
     options.data = true;
   }
-  var ast = Handlebars.parse(input);
+
+  var ast = parse(input);
   var environment = new Compiler().compile(ast, options);
-  return new Handlebars.JavaScriptCompiler().compile(environment, options);
+  return new JavaScriptCompiler().compile(environment, options);
 };
 
-Handlebars.compile = function(input, options) {
+export function compile = function(input, options) {
   if (input == null || (typeof input !== 'string' && input.constructor !== Handlebars.AST.ProgramNode)) {
-    throw new Handlebars.Exception("You must pass a string or Handlebars AST to Handlebars.compile. You passed " + input);
+    throw new Exception("You must pass a string or Handlebars AST to Handlebars.compile. You passed " + input);
   }
 
   options = options || {};
+
   if (!('data' in options)) {
     options.data = true;
   }
+
   var compiled;
+
   function compile() {
-    var ast = Handlebars.parse(input);
+    var ast = parse(input);
     var environment = new Compiler().compile(ast, options);
-    var templateSpec = new Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true);
-    return Handlebars.template(templateSpec);
+    var templateSpec = new JavaScriptCompiler().compile(environment, options, undefined, true);
+    return template(templateSpec);
   }
 
   // Template is only compiled on first use and cached after that point.
@@ -454,10 +457,3 @@ Handlebars.compile = function(input, options) {
     return compiled.call(this, context, options);
   };
 };
-
-
-// END(BROWSER)
-
-return Handlebars;
-
-};
diff --git a/lib/handlebars/compiler/javascript-compiler.js b/lib/handlebars/compiler/javascript-compiler.js
index 4548c6aeb..49660ffdd 100644
--- a/lib/handlebars/compiler/javascript-compiler.js
+++ b/lib/handlebars/compiler/javascript-compiler.js
@@ -1,18 +1,12 @@
-var compilerbase = require("./base");
+import { COMPILER_REVISION, REVISION_CHANGES } from "handlebars/base";
 
-exports.attach = function(Handlebars) {
-
-compilerbase.attach(Handlebars);
-
-// BEGIN(BROWSER)
-/*jshint eqnull:true*/
-
-var Literal = function(value) {
+function Literal(value) {
   this.value = value;
 };
 
 
-var JavaScriptCompiler = Handlebars.JavaScriptCompiler = function() {};
+function JavaScriptCompiler() {};
+export default JavaScriptCompiler;
 
 JavaScriptCompiler.prototype = {
   // PUBLIC API: You can override these methods in a subclass to provide
@@ -162,8 +156,8 @@ JavaScriptCompiler.prototype = {
     var source = this.mergeSource();
 
     if (!this.isChild) {
-      var revision = Handlebars.COMPILER_REVISION,
-          versions = Handlebars.REVISION_CHANGES[revision];
+      var revision = COMPILER_REVISION,
+          versions = REVISION_CHANGES[revision];
       source = "this.compilerInfo = ["+revision+",'"+versions+"'];\n"+source;
     }
 
@@ -848,9 +842,3 @@ JavaScriptCompiler.isValidJavaScriptVariableName = function(name) {
   }
   return false;
 };
-
-// END(BROWSER)
-
-return Handlebars;
-
-};
diff --git a/lib/handlebars/runtime.js b/lib/handlebars/runtime.js
index 2e845c4cc..2585ef3e0 100644
--- a/lib/handlebars/runtime.js
+++ b/lib/handlebars/runtime.js
@@ -1,106 +1,102 @@
-exports.attach = function(Handlebars) {
-
-// BEGIN(BROWSER)
-
-Handlebars.VM = {
-  template: function(templateSpec) {
-    // Just add water
-    var container = {
-      escapeExpression: Handlebars.Utils.escapeExpression,
-      invokePartial: Handlebars.VM.invokePartial,
-      programs: [],
-      program: function(i, fn, data) {
-        var programWrapper = this.programs[i];
-        if(data) {
-          programWrapper = Handlebars.VM.program(i, fn, data);
-        } else if (!programWrapper) {
-          programWrapper = this.programs[i] = Handlebars.VM.program(i, fn);
-        }
-        return programWrapper;
-      },
-      merge: function(param, common) {
-        var ret = param || common;
-
-        if (param && common) {
-          ret = {};
-          Handlebars.Utils.extend(ret, common);
-          Handlebars.Utils.extend(ret, param);
-        }
-        return ret;
-      },
-      programWithDepth: Handlebars.VM.programWithDepth,
-      noop: Handlebars.VM.noop,
-      compilerInfo: null
-    };
-
-    return function(context, options) {
-      options = options || {};
-      var result = templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
-
-      var compilerInfo = container.compilerInfo || [],
-          compilerRevision = compilerInfo[0] || 1,
-          currentRevision = Handlebars.COMPILER_REVISION;
-
-      if (compilerRevision !== currentRevision) {
-        if (compilerRevision < currentRevision) {
-          var runtimeVersions = Handlebars.REVISION_CHANGES[currentRevision],
-              compilerVersions = Handlebars.REVISION_CHANGES[compilerRevision];
-          throw "Template was precompiled with an older version of Handlebars than the current runtime. "+
-                "Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+").";
-        } else {
-          // Use the embedded version info since the runtime doesn't know about this revision yet
-          throw "Template was precompiled with a newer version of Handlebars than the current runtime. "+
-                "Please update your runtime to a newer version ("+compilerInfo[1]+").";
-        }
+import { escapeExpression, extend, Exceptions } from "handlebars/utils";
+import { COMPILER_REVISION, REVISION_CHANGES } from "handlebars/base";
+
+// TODO: Deal with the fact that compile is necessary for on-the-fly partial
+// compilation but won't be present for precompiled templates.
+
+export function template(templateSpec) {
+  // Just add water
+  var container = {
+    escapeExpression: escapeExpression,
+    invokePartial: invokePartial,
+    programs: [],
+    program: function(i, fn, data) {
+      var programWrapper = this.programs[i];
+      if(data) {
+        programWrapper = program(i, fn, data);
+      } else if (!programWrapper) {
+        programWrapper = this.programs[i] = program(i, fn);
+      }
+      return programWrapper;
+    },
+    merge: function(param, common) {
+      var ret = param || common;
+
+      if (param && common) {
+        ret = {};
+        extend(ret, common);
+        extend(ret, param);
+      }
+      return ret;
+    },
+    programWithDepth: programWithDepth,
+    noop: noop,
+    compilerInfo: null
+  };
+
+  return function(context, options) {
+    options = options || {};
+    // TODO: Why does templateSpec require a reference to the global Handlebars?
+    var result = templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
+
+    var compilerInfo = container.compilerInfo || [],
+        compilerRevision = compilerInfo[0] || 1,
+        currentRevision = COMPILER_REVISION;
+
+    if (compilerRevision !== currentRevision) {
+      if (compilerRevision < currentRevision) {
+        var runtimeVersions = REVISION_CHANGES[currentRevision],
+            compilerVersions = REVISION_CHANGES[compilerRevision];
+        throw "Template was precompiled with an older version of Handlebars than the current runtime. "+
+              "Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+").";
+      } else {
+        // Use the embedded version info since the runtime doesn't know about this revision yet
+        throw "Template was precompiled with a newer version of Handlebars than the current runtime. "+
+              "Please update your runtime to a newer version ("+compilerInfo[1]+").";
       }
-
-      return result;
-    };
-  },
-
-  programWithDepth: function(i, fn, data /*, $depth */) {
-    var args = Array.prototype.slice.call(arguments, 3);
-
-    var program = function(context, options) {
-      options = options || {};
-
-      return fn.apply(this, [context, options.data || data].concat(args));
-    };
-    program.program = i;
-    program.depth = args.length;
-    return program;
-  },
-  program: function(i, fn, data) {
-    var program = function(context, options) {
-      options = options || {};
-
-      return fn(context, options.data || data);
-    };
-    program.program = i;
-    program.depth = 0;
-    return program;
-  },
-  noop: function() { return ""; },
-  invokePartial: function(partial, name, context, helpers, partials, data) {
-    var options = { helpers: helpers, partials: partials, data: data };
-
-    if(partial === undefined) {
-      throw new Handlebars.Exception("The partial " + name + " could not be found");
-    } else if(partial instanceof Function) {
-      return partial(context, options);
-    } else if (!Handlebars.compile) {
-      throw new Handlebars.Exception("The partial " + name + " could not be compiled when running in runtime-only mode");
-    } else {
-      partials[name] = Handlebars.compile(partial, {data: data !== undefined});
-      return partials[name](context, options);
     }
-  }
-};
 
-Handlebars.template = Handlebars.VM.template;
-
-// END(BROWSER)
-
-return Handlebars;
-
-};
+    return result;
+  };
+}
+
+export function programWithDepth(i, fn, data /*, $depth */) {
+  var args = Array.prototype.slice.call(arguments, 3);
+
+  var program = function(context, options) {
+    options = options || {};
+
+    return fn.apply(this, [context, options.data || data].concat(args));
+  };
+  program.program = i;
+  program.depth = args.length;
+  return program;
+}
+
+export function program(i, fn, data) {
+  var program = function(context, options) {
+    options = options || {};
+
+    return fn(context, options.data || data);
+  };
+  program.program = i;
+  program.depth = 0;
+  return program;
+}
+
+export function noop() { return ""; }
+
+export function invokePartial(partial, name, context, helpers, partials, data) {
+  var options = { helpers: helpers, partials: partials, data: data };
+
+  if(partial === undefined) {
+    throw new Exception("The partial " + name + " could not be found");
+  } else if(partial instanceof Function) {
+    return partial(context, options);
+  } else if (!compile) {
+    throw new Exception("The partial " + name + " could not be compiled when running in runtime-only mode");
+  } else {
+    partials[name] = compile(partial, {data: data !== undefined});
+    return partials[name](context, options);
+  }
+}
diff --git a/lib/handlebars/utils.js b/lib/handlebars/utils.js
index 1e0e4c902..d14795c2e 100644
--- a/lib/handlebars/utils.js
+++ b/lib/handlebars/utils.js
@@ -1,12 +1,8 @@
-exports.attach = function(Handlebars) {
-
 var toString = Object.prototype.toString;
 
-// BEGIN(BROWSER)
-
 var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
 
-Handlebars.Exception = function(message) {
+export function Exception(message) {
   var tmp = Error.prototype.constructor.apply(this, arguments);
 
   // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
@@ -14,13 +10,15 @@ Handlebars.Exception = function(message) {
     this[errorProps[idx]] = tmp[errorProps[idx]];
   }
 };
-Handlebars.Exception.prototype = new Error();
+
+Exception.prototype = new Error();
 
 // Build out our basic SafeString type
-Handlebars.SafeString = function(string) {
+export function SafeString(string) {
   this.string = string;
 };
-Handlebars.SafeString.prototype.toString = function() {
+
+SafeString.prototype.toString = function() {
   return this.string.toString();
 };
 
@@ -40,44 +38,37 @@ var escapeChar = function(chr) {
   return escape[chr] || "&amp;";
 };
 
-Handlebars.Utils = {
-  extend: function(obj, value) {
-    for(var key in value) {
-      if(value.hasOwnProperty(key)) {
-        obj[key] = value[key];
-      }
-    }
-  },
-
-  escapeExpression: function(string) {
-    // don't escape SafeStrings, since they're already safe
-    if (string instanceof Handlebars.SafeString) {
-      return string.toString();
-    } else if (string == null || string === false) {
-      return "";
-    }
-
-    // Force a string conversion as this will be done by the append regardless and
-    // the regex test will do this transparently behind the scenes, causing issues if
-    // an object's to string has escaped characters in it.
-    string = string.toString();
-
-    if(!possible.test(string)) { return string; }
-    return string.replace(badChars, escapeChar);
-  },
-
-  isEmpty: function(value) {
-    if (!value && value !== 0) {
-      return true;
-    } else if(toString.call(value) === "[object Array]" && value.length === 0) {
-      return true;
-    } else {
-      return false;
+export function extend(obj, value) {
+  for(var key in value) {
+    if(value.hasOwnProperty(key)) {
+      obj[key] = value[key];
     }
   }
-};
-
-// END(BROWSER)
+}
+
+export function escapeExpression(string) {
+  // don't escape SafeStrings, since they're already safe
+  if (string instanceof SafeString) {
+    return string.toString();
+  } else if (string == null || string === false) {
+    return "";
+  }
 
-return Handlebars;
-};
+  // Force a string conversion as this will be done by the append regardless and
+  // the regex test will do this transparently behind the scenes, causing issues if
+  // an object's to string has escaped characters in it.
+  string = string.toString();
+
+  if(!possible.test(string)) { return string; }
+  return string.replace(badChars, escapeChar);
+}
+
+export function isEmpty(value) {
+  if (!value && value !== 0) {
+    return true;
+  } else if(toString.call(value) === "[object Array]" && value.length === 0) {
+    return true;
+  } else {
+    return false;
+  }
+}
diff --git a/package.json b/package.json
index 13fdc74ee..024ba0a82 100644
--- a/package.json
+++ b/package.json
@@ -1,39 +1,27 @@
 {
-  "name": "handlebars",
-  "description": "Extension of the Mustache logicless template language",
-  "version": "1.0.12",
-  "homepage": "http://www.handlebarsjs.com/",
-  "keywords": [
-    "handlebars",
-    "mustache",
-    "template",
-    "html"
-  ],
+  "name": "handlebars.js",
+  "barename": "handlebars",
+  "version": "1.1.0",
+  "description": "Handlebars provides the power necessary to let you build semantic templates effectively with no frustration",
+  "main": "index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
   "repository": {
     "type": "git",
-    "url": "git://github.com/wycats/handlebars.js.git"
-  },
-  "engines": {
-    "node": ">=0.4.7"
-  },
-  "dependencies": {
-    "optimist": "~0.3",
-    "uglify-js": "~2.3"
+    "url": "https://github.com/wycats/handlebars.js.git"
   },
+  "author": "Yehuda Katz",
+  "license": "BSD",
+  "readmeFilename": "README.md",
   "devDependencies": {
-    "benchmark": "~1.0",
-    "dust": "~0.3",
-    "jison": "~0.3",
-    "mocha": "*",
-    "mustache": "~0.7.2",
-    "should": "~1.2.2"
-  },
-  "main": "lib/handlebars.js",
-  "bin": {
-    "handlebars": "bin/handlebars"
-  },
-  "scripts": {
-    "test": "node ./spec/env/runner"
-  },
-  "optionalDependencies": {}
+    "grunt": "~0.4.1",
+    "connect": "~2.7.4",
+    "grunt-contrib-concat": "~0.3.0",
+    "grunt-contrib-clean": "~0.4.1",
+    "grunt-contrib-connect": "~0.3.0",
+    "grunt-contrib-watch": "~0.4.4",
+    "grunt-hang": "~0.1.2",
+    "grunt-es6-module-transpiler": "~0.3.0"
+  }
 }
