├── spec
├── artifacts
│ ├── bom.handlebars
│ ├── empty.handlebars
│ ├── example_1.handlebars
│ └── example_2.hbs
├── expected
│ └── empty.amd.js
├── env
│ ├── node.js
│ ├── browser.js
│ ├── runner.js
│ ├── runtime.js
│ └── common.js
├── .eslintrc
├── require.js
├── source-map.js
├── runtime.js
├── spec.js
├── umd-runtime.html
├── utils.js
├── index.html
├── javascript-compiler.js
├── umd.html
├── compiler.js
├── whitespace-control.js
├── strict.js
└── visitor.js
├── src
├── parser-prefix.js
├── parser-suffix.js
├── handlebars.yy
└── handlebars.l
├── .istanbul.yml
├── .gitmodules
├── components
├── bower.json
├── component.json
├── lib
│ └── handlebars
│ │ └── source.rb
├── handlebars.js.nuspec
├── handlebars-source.gemspec
└── composer.json
├── bench
├── templates
│ ├── object-mustache.js
│ ├── string.js
│ ├── array-mustache.js
│ ├── data.js
│ ├── arguments.js
│ ├── index.js
│ ├── depth-1.js
│ ├── object.js
│ ├── complex.dust
│ ├── array-each.js
│ ├── complex.mustache
│ ├── complex.handlebars
│ ├── variables.js
│ ├── subexpression.js
│ ├── complex.eco
│ ├── paths.js
│ ├── depth-2.js
│ ├── partial-recursion.js
│ ├── partial.js
│ └── complex.js
├── .eslintrc
├── index.js
├── precompile-size.js
├── util
│ ├── template-runner.js
│ └── benchwarmer.js
├── dist-size.js
└── throughput.js
├── lib
├── handlebars
│ ├── decorators.js
│ ├── helpers
│ │ ├── lookup.js
│ │ ├── helper-missing.js
│ │ ├── with.js
│ │ ├── log.js
│ │ ├── block-helper-missing.js
│ │ ├── if.js
│ │ └── each.js
│ ├── safe-string.js
│ ├── no-conflict.js
│ ├── helpers.js
│ ├── decorators
│ │ └── inline.js
│ ├── compiler
│ │ ├── base.js
│ │ ├── ast.js
│ │ ├── visitor.js
│ │ ├── code-gen.js
│ │ ├── printer.js
│ │ └── helpers.js
│ ├── logger.js
│ ├── exception.js
│ ├── base.js
│ └── utils.js
├── index.js
├── handlebars.runtime.js
└── handlebars.js
├── runtime.js
├── .gitignore
├── tasks
├── .eslintrc
├── metrics.js
├── parser.js
├── version.js
├── test.js
├── publish.js
└── util
│ └── git.js
├── .npmignore
├── dist
└── cjs
│ ├── handlebars
│ ├── helpers
│ │ ├── lookup.js
│ │ ├── helper-missing.js
│ │ ├── with.js
│ │ ├── log.js
│ │ ├── block-helper-missing.js
│ │ └── if.js
│ ├── decorators.js
│ ├── safe-string.js
│ ├── no-conflict.js
│ ├── decorators
│ │ └── inline.js
│ ├── helpers.js
│ ├── compiler
│ │ ├── ast.js
│ │ └── base.js
│ ├── logger.js
│ └── exception.js
│ ├── handlebars.runtime.js
│ └── handlebars.js
├── LICENSE
├── .travis.yml
├── docs
└── decorators-api.md
├── package.json
├── FAQ.md
├── print-script
├── bin
└── handlebars
├── CONTRIBUTING.md
├── .eslintrc
└── Gruntfile.js
/spec/artifacts/bom.handlebars:
--------------------------------------------------------------------------------
1 | a
--------------------------------------------------------------------------------
/spec/artifacts/empty.handlebars:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/spec/artifacts/example_1.handlebars:
--------------------------------------------------------------------------------
1 | {{foo}}
2 |
--------------------------------------------------------------------------------
/spec/artifacts/example_2.hbs:
--------------------------------------------------------------------------------
1 | Hello, {{name}}!
2 |
--------------------------------------------------------------------------------
/src/parser-prefix.js:
--------------------------------------------------------------------------------
1 | /* istanbul ignore next */
2 |
--------------------------------------------------------------------------------
/.istanbul.yml:
--------------------------------------------------------------------------------
1 | instrumentation:
2 | excludes: ['**/spec/**']
3 |
--------------------------------------------------------------------------------
/src/parser-suffix.js:
--------------------------------------------------------------------------------
1 | exports.__esModule = true;
2 | exports['default'] = handlebars;
3 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "spec/mustache"]
2 | path = spec/mustache
3 | url = git://github.com/mustache/spec.git
4 |
--------------------------------------------------------------------------------
/components/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "handlebars",
3 | "version": "4.0.5",
4 | "main": "handlebars.js",
5 | "license": "MIT",
6 | "dependencies": {}
7 | }
8 |
--------------------------------------------------------------------------------
/bench/templates/object-mustache.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | context: { person: { name: 'Larry', age: 45 } },
3 | handlebars: '{{#person}}{{name}}{{age}}{{/person}}'
4 | };
5 |
--------------------------------------------------------------------------------
/lib/handlebars/decorators.js:
--------------------------------------------------------------------------------
1 | import registerInline from './decorators/inline';
2 |
3 | export function registerDefaultDecorators(instance) {
4 | registerInline(instance);
5 | }
6 |
7 |
--------------------------------------------------------------------------------
/lib/handlebars/helpers/lookup.js:
--------------------------------------------------------------------------------
1 | export default function(instance) {
2 | instance.registerHelper('lookup', function(obj, field) {
3 | return obj && obj[field];
4 | });
5 | }
6 |
--------------------------------------------------------------------------------
/bench/templates/string.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | context: {},
3 | handlebars: 'Hello world',
4 | dust: 'Hello world',
5 | mustache: 'Hello world',
6 | eco: 'Hello world'
7 | };
8 |
--------------------------------------------------------------------------------
/runtime.js:
--------------------------------------------------------------------------------
1 | // Create a simple path alias to allow browserify to resolve
2 | // the runtime on a supported path.
3 | module.exports = require('./dist/cjs/handlebars.runtime')['default'];
4 |
--------------------------------------------------------------------------------
/bench/templates/array-mustache.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | context: { names: [{name: 'Moe'}, {name: 'Larry'}, {name: 'Curly'}, {name: 'Shemp'}] },
3 | handlebars: '{{#names}}{{name}}{{/names}}'
4 | };
5 |
--------------------------------------------------------------------------------
/bench/templates/data.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | context: { names: [{name: 'Moe'}, {name: 'Larry'}, {name: 'Curly'}, {name: 'Shemp'}] },
3 | handlebars: '{{#each names}}{{@index}}{{name}}{{/each}}'
4 | };
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor
2 | .rvmrc
3 | .DS_Store
4 | lib/handlebars/compiler/parser.js
5 | /tmp/
6 | /coverage/
7 | node_modules
8 | *.sublime-project
9 | *.sublime-workspace
10 | npm-debug.log
11 | sauce_connect.log*
12 |
--------------------------------------------------------------------------------
/components/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "handlebars",
3 | "repo": "components/handlebars.js",
4 | "version": "1.0.0",
5 | "main": "handlebars.js",
6 | "scripts": [
7 | "handlebars.js"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/bench/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "globals": {
3 | "require": true
4 | },
5 | "rules": {
6 | // Disabling for tests, for now.
7 | "no-path-concat": 0,
8 |
9 | "no-var": 0,
10 | "no-shadow": 0,
11 | "handle-callback-err": 0,
12 | "no-console": 0
13 | }
14 | }
--------------------------------------------------------------------------------
/bench/templates/arguments.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | helpers: {
3 | foo: function() {
4 | return '';
5 | }
6 | },
7 | context: {
8 | bar: true
9 | },
10 |
11 | handlebars: '{{foo person "person" 1 true foo=bar foo="person" foo=1 foo=true}}'
12 | };
13 |
--------------------------------------------------------------------------------
/lib/handlebars/safe-string.js:
--------------------------------------------------------------------------------
1 | // Build out our basic SafeString type
2 | function SafeString(string) {
3 | this.string = string;
4 | }
5 |
6 | SafeString.prototype.toString = SafeString.prototype.toHTML = function() {
7 | return '' + this.string;
8 | };
9 |
10 | export default SafeString;
11 |
--------------------------------------------------------------------------------
/bench/templates/index.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 |
3 | var templates = fs.readdirSync(__dirname);
4 | templates.forEach(function(template) {
5 | if (template === 'index.js' || !(/(.*)\.js$/.test(template))) {
6 | return;
7 | }
8 | module.exports[RegExp.$1] = require('./' + RegExp.$1);
9 | });
10 |
--------------------------------------------------------------------------------
/bench/templates/depth-1.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | context: { names: [{name: 'Moe'}, {name: 'Larry'}, {name: 'Curly'}, {name: 'Shemp'}], foo: 'bar' },
3 | handlebars: '{{#each names}}{{../foo}}{{/each}}',
4 | mustache: '{{#names}}{{foo}}{{/names}}',
5 | eco: '<% for item in @names: %><%= @foo %><% end %>'
6 | };
7 |
--------------------------------------------------------------------------------
/bench/templates/object.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | context: { person: { name: 'Larry', age: 45 } },
3 | handlebars: '{{#with person}}{{name}}{{age}}{{/with}}',
4 | dust: '{#person}{name}{age}{/person}',
5 | eco: '<%= @person.name %><%= @person.age %>',
6 | mustache: '{{#person}}{{name}}{{age}}{{/person}}'
7 | };
8 |
--------------------------------------------------------------------------------
/components/lib/handlebars/source.rb:
--------------------------------------------------------------------------------
1 | module Handlebars
2 | module Source
3 | def self.bundled_path
4 | File.expand_path("../../../handlebars.js", __FILE__)
5 | end
6 |
7 | def self.runtime_bundled_path
8 | File.expand_path("../../../handlebars.runtime.js", __FILE__)
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/bench/templates/complex.dust:
--------------------------------------------------------------------------------
1 |
{header}
2 | {?items}
3 |
4 | {#items}
5 | {#current}
6 | - {name}
7 | {:else}
8 | - {name}
9 | {/current}
10 | {/items}
11 |
12 | {:else}
13 | The list is empty.
14 | {/items}
15 |
--------------------------------------------------------------------------------
/bench/templates/array-each.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | context: { names: [{name: 'Moe'}, {name: 'Larry'}, {name: 'Curly'}, {name: 'Shemp'}] },
3 | handlebars: '{{#each names}}{{name}}{{/each}}',
4 | dust: '{#names}{name}{/names}',
5 | mustache: '{{#names}}{{name}}{{/names}}',
6 | eco: '<% for item in @names: %><%= item.name %><% end %>'
7 | };
8 |
--------------------------------------------------------------------------------
/bench/templates/complex.mustache:
--------------------------------------------------------------------------------
1 | {{header}}
2 | {{#hasItems}}
3 |
4 | {{#items}}
5 | {{#current}}
6 | - {{name}}
7 | {{/current}}
8 | {{^current}}
9 | - {{name}}
10 | {{/current}}
11 | {{/items}}
12 |
13 | {{/hasItems}}
14 |
--------------------------------------------------------------------------------
/bench/templates/complex.handlebars:
--------------------------------------------------------------------------------
1 | {{header}}
2 | {{#if items}}
3 |
4 | {{#each items}}
5 | {{#if current}}
6 | - {{name}}
7 | {{^}}
8 | - {{name}}
9 | {{/if}}
10 | {{/each}}
11 |
12 | {{^}}
13 | The list is empty.
14 | {{/if}}
15 |
--------------------------------------------------------------------------------
/bench/templates/variables.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | context: {name: 'Mick', count: 30},
3 | handlebars: 'Hello {{name}}! You have {{count}} new messages.',
4 | dust: 'Hello {name}! You have {count} new messages.',
5 | mustache: 'Hello {{name}}! You have {{count}} new messages.',
6 | eco: 'Hello <%= @name %>! You have <%= @count %> new messages.'
7 | };
8 |
9 |
--------------------------------------------------------------------------------
/tasks/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "globals": {
3 | "require": true
4 | },
5 | "rules": {
6 | // Disabling for tests, for now.
7 | "no-path-concat": 0,
8 |
9 | "no-var": 0,
10 | "no-shadow": 0,
11 | "handle-callback-err": 0,
12 | "no-console": 0,
13 | "no-process-env": 0,
14 | "dot-notation": [2, {"allowKeywords": true}]
15 | }
16 | }
--------------------------------------------------------------------------------
/bench/templates/subexpression.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | helpers: {
3 | echo: function(value) {
4 | return 'foo ' + value;
5 | },
6 | header: function() {
7 | return 'Colors';
8 | }
9 | },
10 | handlebars: '{{echo (header)}}',
11 | eco: '<%= @echo(@header()) %>'
12 | };
13 |
14 | module.exports.context = module.exports.helpers;
15 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .gitignore
3 | .rvmrc
4 | .eslintrc
5 | .travis.yml
6 | .rspec
7 | Gemfile
8 | Gemfile.lock
9 | Rakefile
10 | Gruntfile.js
11 | *.gemspec
12 | *.nuspec
13 | *.log
14 | bench/*
15 | configurations/*
16 | components/*
17 | coverage/*
18 | dist/cdnjs/*
19 | dist/components/*
20 | spec/*
21 | src/*
22 | tasks/*
23 | tmp/*
24 | publish/*
25 | vendor/*
26 |
--------------------------------------------------------------------------------
/bench/index.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 |
3 | var metrics = fs.readdirSync(__dirname);
4 | metrics.forEach(function(metric) {
5 | if (metric === 'index.js' || !(/(.*)\.js$/.test(metric))) {
6 | return;
7 | }
8 |
9 | var name = RegExp.$1;
10 | metric = require('./' + name);
11 | if (metric instanceof Function) {
12 | module.exports[name] = metric;
13 | }
14 | });
15 |
--------------------------------------------------------------------------------
/spec/expected/empty.amd.js:
--------------------------------------------------------------------------------
1 | define(['handlebars.runtime'], function(Handlebars) {
2 | Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
3 | return templates['empty'] = template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
4 | return "";
5 | },"useData":true});
6 | });
7 |
--------------------------------------------------------------------------------
/bench/templates/complex.eco:
--------------------------------------------------------------------------------
1 | <%= @header() %>
2 | <% if @items.length: %>
3 |
4 | <% for item in @items: %>
5 | <% if item.current: %>
6 | - <%= item.name %>
7 | <% else: %>
8 | - <%= item.name %>
9 | <% end %>
10 | <% end %>
11 |
12 | <% else: %>
13 | The list is empty.
14 | <% end %>
15 |
--------------------------------------------------------------------------------
/lib/handlebars/no-conflict.js:
--------------------------------------------------------------------------------
1 | /* global window */
2 | export default function(Handlebars) {
3 | /* istanbul ignore next */
4 | let root = typeof global !== 'undefined' ? global : window,
5 | $Handlebars = root.Handlebars;
6 | /* istanbul ignore next */
7 | Handlebars.noConflict = function() {
8 | if (root.Handlebars === Handlebars) {
9 | root.Handlebars = $Handlebars;
10 | }
11 | return Handlebars;
12 | };
13 | }
14 |
--------------------------------------------------------------------------------
/bench/templates/paths.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | context: { person: { name: {bar: {baz: 'Larry'}}, age: 45 } },
3 | handlebars: '{{person.name.bar.baz}}{{person.age}}{{person.foo}}{{animal.age}}',
4 | dust: '{person.name.bar.baz}{person.age}{person.foo}{animal.age}',
5 | eco: '<%= @person.name.bar.baz %><%= @person.age %><%= @person.foo %><% if @animal: %><%= @animal.age %><% end %>',
6 | mustache: '{{person.name.bar.baz}}{{person.age}}{{person.foo}}{{animal.age}}'
7 | };
8 |
--------------------------------------------------------------------------------
/bench/templates/depth-2.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | context: { names: [{bat: 'foo', name: ['Moe']}, {bat: 'foo', name: ['Larry']}, {bat: 'foo', name: ['Curly']}, {bat: 'foo', name: ['Shemp']}], foo: 'bar' },
3 | handlebars: '{{#each names}}{{#each name}}{{../bat}}{{../../foo}}{{/each}}{{/each}}',
4 | mustache: '{{#names}}{{#name}}{{bat}}{{foo}}{{/name}}{{/names}}',
5 | eco: '<% for item in @names: %><% for child in item.name: %><%= item.bat %><%= @foo %><% end %><% end %>'
6 | };
7 |
--------------------------------------------------------------------------------
/bench/templates/partial-recursion.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | context: { name: '1', kids: [{ name: '1.1', kids: [{name: '1.1.1', kids: []}] }] },
3 | partials: {
4 | mustache: { recursion: '{{name}}{{#kids}}{{>recursion}}{{/kids}}' },
5 | handlebars: { recursion: '{{name}}{{#each kids}}{{>recursion}}{{/each}}' }
6 | },
7 | handlebars: '{{name}}{{#each kids}}{{>recursion}}{{/each}}',
8 | dust: '{name}{#kids}{>recursion:./}{/kids}',
9 | mustache: '{{name}}{{#kids}}{{>recursion}}{{/kids}}'
10 | };
11 |
--------------------------------------------------------------------------------
/bench/templates/partial.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | context: { peeps: [{name: 'Moe', count: 15}, {name: 'Larry', count: 5}, {name: 'Curly', count: 1}] },
3 | partials: {
4 | mustache: { variables: 'Hello {{name}}! You have {{count}} new messages.' },
5 | handlebars: { variables: 'Hello {{name}}! You have {{count}} new messages.' }
6 | },
7 |
8 | handlebars: '{{#each peeps}}{{>variables}}{{/each}}',
9 | dust: '{#peeps}{>variables/}{/peeps}',
10 | mustache: '{{#peeps}}{{>variables}}{{/peeps}}'
11 | };
12 |
--------------------------------------------------------------------------------
/lib/handlebars/helpers/helper-missing.js:
--------------------------------------------------------------------------------
1 | import Exception from '../exception';
2 |
3 | export default function(instance) {
4 | instance.registerHelper('helperMissing', function(/* [args, ]options */) {
5 | if (arguments.length === 1) {
6 | // A missing field in a {{foo}} construct.
7 | return undefined;
8 | } else {
9 | // Someone is actually trying to call something, blow up.
10 | throw new Exception('Missing helper: "' + arguments[arguments.length - 1].name + '"');
11 | }
12 | });
13 | }
14 |
--------------------------------------------------------------------------------
/lib/handlebars/helpers/with.js:
--------------------------------------------------------------------------------
1 | import {isEmpty, isFunction} from '../utils';
2 |
3 | export default function(instance) {
4 | instance.registerHelper('with', function(context, options) {
5 | if (isFunction(context)) { context = context.call(this); }
6 |
7 | let fn = options.fn;
8 |
9 | if (!isEmpty(context)) {
10 | let data = options.data;
11 |
12 | return fn(context, {
13 | data: data,
14 | blockParams: [context]
15 | });
16 | } else {
17 | return options.inverse(this);
18 | }
19 | });
20 | }
21 |
--------------------------------------------------------------------------------
/lib/handlebars/helpers/log.js:
--------------------------------------------------------------------------------
1 | export default function(instance) {
2 | instance.registerHelper('log', function(/* message, options */) {
3 | let args = [undefined],
4 | options = arguments[arguments.length - 1];
5 | for (let i = 0; i < arguments.length - 1; i++) {
6 | args.push(arguments[i]);
7 | }
8 |
9 | let level = 1;
10 | if (options.hash.level != null) {
11 | level = options.hash.level;
12 | } else if (options.data && options.data.level != null) {
13 | level = options.data.level;
14 | }
15 | args[0] = level;
16 |
17 | instance.log(... args);
18 | });
19 | }
20 |
--------------------------------------------------------------------------------
/tasks/metrics.js:
--------------------------------------------------------------------------------
1 | var _ = require('underscore'),
2 | async = require('async'),
3 | metrics = require('../bench');
4 |
5 | module.exports = function(grunt) {
6 | grunt.registerTask('metrics', function() {
7 | var done = this.async(),
8 | execName = grunt.option('name'),
9 | events = {};
10 |
11 | async.each(_.keys(metrics), function(name, complete) {
12 | if (/^_/.test(name) || (execName && name !== execName)) {
13 | return complete();
14 | }
15 |
16 | metrics[name](grunt, function(data) {
17 | events[name] = data;
18 | complete();
19 | });
20 | },
21 | done);
22 | });
23 | };
24 |
--------------------------------------------------------------------------------
/lib/handlebars/helpers.js:
--------------------------------------------------------------------------------
1 | import registerBlockHelperMissing from './helpers/block-helper-missing';
2 | import registerEach from './helpers/each';
3 | import registerHelperMissing from './helpers/helper-missing';
4 | import registerIf from './helpers/if';
5 | import registerLog from './helpers/log';
6 | import registerLookup from './helpers/lookup';
7 | import registerWith from './helpers/with';
8 |
9 | export function registerDefaultHelpers(instance) {
10 | registerBlockHelperMissing(instance);
11 | registerEach(instance);
12 | registerHelperMissing(instance);
13 | registerIf(instance);
14 | registerLog(instance);
15 | registerLookup(instance);
16 | registerWith(instance);
17 | }
18 |
--------------------------------------------------------------------------------
/components/handlebars.js.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | handlebars.js
5 | 4.0.5
6 | handlebars.js Authors
7 | https://github.com/wycats/handlebars.js/blob/master/LICENSE
8 | https://github.com/wycats/handlebars.js/
9 | false
10 | Extension of the Mustache logicless template language
11 |
12 | handlebars mustache template html
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/lib/handlebars/helpers/block-helper-missing.js:
--------------------------------------------------------------------------------
1 | import {isArray} from '../utils';
2 |
3 | export default function(instance) {
4 | instance.registerHelper('blockHelperMissing', function(context, options) {
5 | let inverse = options.inverse,
6 | fn = options.fn;
7 |
8 | if (context === true) {
9 | return fn(this);
10 | } else if (context === false || context == null) {
11 | return inverse(this);
12 | } else if (isArray(context)) {
13 | if (context.length > 0) {
14 | return instance.helpers.each(context, options);
15 | } else {
16 | return inverse(this);
17 | }
18 | } else {
19 | return fn(context, options);
20 | }
21 | });
22 | }
23 |
--------------------------------------------------------------------------------
/spec/env/node.js:
--------------------------------------------------------------------------------
1 | require('./common');
2 |
3 | global.Handlebars = require('../../lib');
4 |
5 | global.CompilerContext = {
6 | compile: function(template, options) {
7 | var templateSpec = handlebarsEnv.precompile(template, options);
8 | return handlebarsEnv.template(safeEval(templateSpec));
9 | },
10 | compileWithPartial: function(template, options) {
11 | return handlebarsEnv.compile(template, options);
12 | }
13 | };
14 |
15 | function safeEval(templateSpec) {
16 | /* eslint-disable no-eval, no-console */
17 | try {
18 | return eval('(' + templateSpec + ')');
19 | } catch (err) {
20 | console.error(templateSpec);
21 | throw err;
22 | }
23 | /* eslint-enable no-eval, no-console */
24 | }
25 |
--------------------------------------------------------------------------------
/lib/handlebars/decorators/inline.js:
--------------------------------------------------------------------------------
1 | import {extend} from '../utils';
2 |
3 | export default function(instance) {
4 | instance.registerDecorator('inline', function(fn, props, container, options) {
5 | let ret = fn;
6 | if (!props.partials) {
7 | props.partials = {};
8 | ret = function(context, options) {
9 | // Create a new partials stack frame prior to exec.
10 | let original = container.partials;
11 | container.partials = extend({}, original, props.partials);
12 | let ret = fn(context, options);
13 | container.partials = original;
14 | return ret;
15 | };
16 | }
17 |
18 | props.partials[options.args[0]] = options.fn;
19 |
20 | return ret;
21 | });
22 | }
23 |
--------------------------------------------------------------------------------
/bench/templates/complex.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 |
3 | module.exports = {
4 | context: {
5 | header: function() {
6 | return 'Colors';
7 | },
8 | hasItems: true, // To make things fairer in mustache land due to no `{{if}}` construct on arrays
9 | items: [
10 | {name: 'red', current: true, url: '#Red'},
11 | {name: 'green', current: false, url: '#Green'},
12 | {name: 'blue', current: false, url: '#Blue'}
13 | ]
14 | },
15 |
16 | handlebars: fs.readFileSync(__dirname + '/complex.handlebars').toString(),
17 | dust: fs.readFileSync(__dirname + '/complex.dust').toString(),
18 | eco: fs.readFileSync(__dirname + '/complex.eco').toString(),
19 | mustache: fs.readFileSync(__dirname + '/complex.mustache').toString()
20 | };
21 |
--------------------------------------------------------------------------------
/lib/handlebars/compiler/base.js:
--------------------------------------------------------------------------------
1 | import parser from './parser';
2 | import WhitespaceControl from './whitespace-control';
3 | import * as Helpers from './helpers';
4 | import { extend } from '../utils';
5 |
6 | export { parser };
7 |
8 | let yy = {};
9 | extend(yy, Helpers);
10 |
11 | export function parse(input, options) {
12 | // Just return if an already-compiled AST was passed in.
13 | if (input.type === 'Program') { return input; }
14 |
15 | parser.yy = yy;
16 |
17 | // Altering the shared object here, but this is ok as parser is a sync operation
18 | yy.locInfo = function(locInfo) {
19 | return new yy.SourceLocation(options && options.srcName, locInfo);
20 | };
21 |
22 | let strip = new WhitespaceControl(options);
23 | return strip.accept(parser.parse(input));
24 | }
25 |
--------------------------------------------------------------------------------
/spec/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "globals": {
3 | "CompilerContext": true,
4 | "Handlebars": true,
5 | "handlebarsEnv": true,
6 |
7 | "shouldCompileTo": true,
8 | "shouldCompileToWithPartials": true,
9 | "shouldThrow": true,
10 | "compileWithPartials": true,
11 |
12 | "console": true,
13 | "require": true,
14 | "suite": true,
15 | "equal": true,
16 | "equals": true,
17 | "test": true,
18 | "testBoth": true,
19 | "raises": true,
20 | "deepEqual": true,
21 | "start": true,
22 | "stop": true,
23 | "ok": true,
24 | "strictEqual": true,
25 | "define": true
26 | },
27 | "env": {
28 | "mocha": true
29 | },
30 | "rules": {
31 | // Disabling for tests, for now.
32 | "no-path-concat": 0,
33 |
34 | "no-var": 0
35 | }
36 | }
--------------------------------------------------------------------------------
/components/handlebars-source.gemspec:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | require 'json'
3 |
4 | package = JSON.parse(File.read('bower.json'))
5 |
6 | Gem::Specification.new do |gem|
7 | gem.name = "handlebars-source"
8 | gem.authors = ["Yehuda Katz"]
9 | gem.email = ["wycats@gmail.com"]
10 | gem.date = Time.now.strftime("%Y-%m-%d")
11 | gem.description = %q{Handlebars.js source code wrapper for (pre)compilation gems.}
12 | gem.summary = %q{Handlebars.js source code wrapper}
13 | gem.homepage = "https://github.com/wycats/handlebars.js/"
14 | gem.version = package["version"].sub "-", "."
15 | gem.license = "MIT"
16 |
17 | gem.files = [
18 | 'handlebars.js',
19 | 'handlebars.runtime.js',
20 | 'lib/handlebars/source.rb'
21 | ]
22 | end
23 |
--------------------------------------------------------------------------------
/spec/require.js:
--------------------------------------------------------------------------------
1 | if (typeof require !== 'undefined' && require.extensions['.handlebars']) {
2 | describe('Require', function() {
3 | it('Load .handlebars files with require()', function() {
4 | var template = require('./artifacts/example_1');
5 | equal(template, require('./artifacts/example_1.handlebars'));
6 |
7 | var expected = 'foo\n';
8 | var result = template({foo: 'foo'});
9 |
10 | equal(result, expected);
11 | });
12 |
13 | it('Load .hbs files with require()', function() {
14 | var template = require('./artifacts/example_2');
15 | equal(template, require('./artifacts/example_2.hbs'));
16 |
17 | var expected = 'Hello, World!\n';
18 | var result = template({name: 'World'});
19 |
20 | equal(result, expected);
21 | });
22 | });
23 | }
24 |
--------------------------------------------------------------------------------
/bench/precompile-size.js:
--------------------------------------------------------------------------------
1 | var _ = require('underscore'),
2 | templates = require('./templates');
3 |
4 | module.exports = function(grunt, callback) {
5 | // Deferring to here in case we have a build for parser, etc as part of this grunt exec
6 | var Handlebars = require('../lib');
7 |
8 | var templateSizes = {};
9 | _.each(templates, function(info, template) {
10 | var src = info.handlebars,
11 | compiled = Handlebars.precompile(src, {}),
12 | knownHelpers = Handlebars.precompile(src, {knownHelpersOnly: true, knownHelpers: info.helpers});
13 |
14 | templateSizes[template] = compiled.length;
15 | templateSizes['knownOnly_' + template] = knownHelpers.length;
16 | });
17 | grunt.log.writeln('Precompiled sizes: ' + JSON.stringify(templateSizes, undefined, 2));
18 | callback([templateSizes]);
19 | };
20 |
--------------------------------------------------------------------------------
/bench/util/template-runner.js:
--------------------------------------------------------------------------------
1 | var _ = require('underscore'),
2 | BenchWarmer = require('./benchwarmer'),
3 | templates = require('../templates');
4 |
5 | module.exports = function(grunt, makeSuite, callback) {
6 | var warmer = new BenchWarmer();
7 |
8 | var handlebarsOnly = grunt.option('handlebars-only'),
9 | grep = grunt.option('grep');
10 | if (grep) {
11 | grep = new RegExp(grep);
12 | }
13 |
14 | _.each(templates, function(template, name) {
15 | if (!template.handlebars || (grep && !grep.test(name))) {
16 | return;
17 | }
18 |
19 | warmer.suite(name, function(bench) {
20 | makeSuite(bench, name, template, handlebarsOnly);
21 | });
22 | });
23 |
24 | warmer.bench(function() {
25 | if (callback) {
26 | callback(warmer.times, warmer.scaled);
27 | }
28 | });
29 | };
30 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | // USAGE:
2 | // var handlebars = require('handlebars');
3 | /* eslint-disable no-var */
4 |
5 | // var local = handlebars.create();
6 |
7 | var handlebars = require('../dist/cjs/handlebars')['default'];
8 |
9 | var printer = require('../dist/cjs/handlebars/compiler/printer');
10 | handlebars.PrintVisitor = printer.PrintVisitor;
11 | handlebars.print = printer.print;
12 |
13 | module.exports = handlebars;
14 |
15 | // Publish a Node.js require() handler for .handlebars and .hbs files
16 | function extension(module, filename) {
17 | var fs = require('fs');
18 | var templateString = fs.readFileSync(filename, 'utf8');
19 | module.exports = handlebars.compile(templateString);
20 | }
21 | /* istanbul ignore else */
22 | if (typeof require !== 'undefined' && require.extensions) {
23 | require.extensions['.handlebars'] = extension;
24 | require.extensions['.hbs'] = extension;
25 | }
26 |
--------------------------------------------------------------------------------
/dist/cjs/handlebars/helpers/lookup.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 |
5 | exports['default'] = function (instance) {
6 | instance.registerHelper('lookup', function (obj, field) {
7 | return obj && obj[field];
8 | });
9 | };
10 |
11 | module.exports = exports['default'];
12 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2xpYi9oYW5kbGViYXJzL2hlbHBlcnMvbG9va3VwLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7cUJBQWUsVUFBUyxRQUFRLEVBQUU7QUFDaEMsVUFBUSxDQUFDLGNBQWMsQ0FBQyxRQUFRLEVBQUUsVUFBUyxHQUFHLEVBQUUsS0FBSyxFQUFFO0FBQ3JELFdBQU8sR0FBRyxJQUFJLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQztHQUMxQixDQUFDLENBQUM7Q0FDSiIsImZpbGUiOiJsb29rdXAuanMiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZGVmYXVsdCBmdW5jdGlvbihpbnN0YW5jZSkge1xuICBpbnN0YW5jZS5yZWdpc3RlckhlbHBlcignbG9va3VwJywgZnVuY3Rpb24ob2JqLCBmaWVsZCkge1xuICAgIHJldHVybiBvYmogJiYgb2JqW2ZpZWxkXTtcbiAgfSk7XG59XG4iXX0=
13 |
--------------------------------------------------------------------------------
/lib/handlebars/helpers/if.js:
--------------------------------------------------------------------------------
1 | import {isEmpty, isFunction} from '../utils';
2 |
3 | export default function(instance) {
4 | instance.registerHelper('if', function(conditional, options) {
5 | if (isFunction(conditional)) { conditional = conditional.call(this); }
6 |
7 | // Default behavior is to render the positive path if the value is truthy and not empty.
8 | // The `includeZero` option may be set to treat the condtional as purely not empty based on the
9 | // behavior of isEmpty. Effectively this determines if 0 is handled by the positive path or negative.
10 | if ((!options.hash.includeZero && !conditional) || isEmpty(conditional)) {
11 | return options.inverse(this);
12 | } else {
13 | return options.fn(this);
14 | }
15 | });
16 |
17 | instance.registerHelper('unless', function(conditional, options) {
18 | return instance.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn, hash: options.hash});
19 | });
20 | }
21 |
--------------------------------------------------------------------------------
/spec/env/browser.js:
--------------------------------------------------------------------------------
1 | require('./common');
2 |
3 | var fs = require('fs'),
4 | vm = require('vm');
5 |
6 | global.Handlebars = 'no-conflict';
7 |
8 | var filename = 'dist/handlebars.js';
9 | if (global.minimizedTest) {
10 | filename = 'dist/handlebars.min.js';
11 | }
12 | vm.runInThisContext(fs.readFileSync(__dirname + '/../../' + filename), filename);
13 |
14 | global.CompilerContext = {
15 | browser: true,
16 |
17 | compile: function(template, options) {
18 | var templateSpec = handlebarsEnv.precompile(template, options);
19 | return handlebarsEnv.template(safeEval(templateSpec));
20 | },
21 | compileWithPartial: function(template, options) {
22 | return handlebarsEnv.compile(template, options);
23 | }
24 | };
25 |
26 | function safeEval(templateSpec) {
27 | /* eslint-disable no-eval, no-console */
28 | try {
29 | return eval('(' + templateSpec + ')');
30 | } catch (err) {
31 | console.error(templateSpec);
32 | throw err;
33 | }
34 | /* eslint-enable no-eval, no-console */
35 | }
36 |
--------------------------------------------------------------------------------
/tasks/parser.js:
--------------------------------------------------------------------------------
1 | var childProcess = require('child_process');
2 |
3 | module.exports = function(grunt) {
4 | grunt.registerTask('parser', 'Generate jison parser.', function() {
5 | var done = this.async();
6 |
7 | var cmd = './node_modules/.bin/jison';
8 |
9 | if (process.platform === 'win32') {
10 | cmd = 'node_modules\\.bin\\jison.cmd';
11 | }
12 |
13 | var child = childProcess.spawn(cmd, ['-m', 'js', 'src/handlebars.yy', 'src/handlebars.l'], {stdio: 'inherit'});
14 | child.on('exit', function(code) {
15 | if (code != 0) {
16 | grunt.fatal('Jison failure: ' + code);
17 | done();
18 | return;
19 | }
20 |
21 | var src = ['src/parser-prefix.js', 'handlebars.js', 'src/parser-suffix.js'].map(grunt.file.read).join('');
22 | grunt.file.delete('handlebars.js');
23 |
24 | grunt.file.write('lib/handlebars/compiler/parser.js', src);
25 | grunt.log.writeln('Parser "lib/handlebars/compiler/parser.js" created.');
26 | done();
27 | });
28 | });
29 | };
30 |
--------------------------------------------------------------------------------
/components/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "components/handlebars.js",
3 | "description": "Handlebars.js and Mustache are both logicless templating languages that keep the view and the code separated like we all know they should be.",
4 | "homepage": "http://handlebarsjs.com",
5 | "license": "MIT",
6 | "type": "component",
7 | "keywords": [
8 | "handlebars",
9 | "mustache",
10 | "html"
11 | ],
12 | "authors": [
13 | {
14 | "name": "Chris Wanstrath",
15 | "homepage": "http://chriswanstrath.com"
16 | }
17 | ],
18 | "require": {
19 | "robloach/component-installer": "*"
20 | },
21 | "extra": {
22 | "component": {
23 | "name": "handlebars",
24 | "scripts": [
25 | "handlebars.js"
26 | ],
27 | "files": [
28 | "handlebars.runtime.js"
29 | ],
30 | "shim": {
31 | "exports": "Handlebars"
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/dist/cjs/handlebars/decorators.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 | exports.registerDefaultDecorators = registerDefaultDecorators;
5 | // istanbul ignore next
6 |
7 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
8 |
9 | var _decoratorsInline = require('./decorators/inline');
10 |
11 | var _decoratorsInline2 = _interopRequireDefault(_decoratorsInline);
12 |
13 | function registerDefaultDecorators(instance) {
14 | _decoratorsInline2['default'](instance);
15 | }
16 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL2xpYi9oYW5kbGViYXJzL2RlY29yYXRvcnMuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Z0NBQTJCLHFCQUFxQjs7OztBQUV6QyxTQUFTLHlCQUF5QixDQUFDLFFBQVEsRUFBRTtBQUNsRCxnQ0FBZSxRQUFRLENBQUMsQ0FBQztDQUMxQiIsImZpbGUiOiJkZWNvcmF0b3JzLmpzIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHJlZ2lzdGVySW5saW5lIGZyb20gJy4vZGVjb3JhdG9ycy9pbmxpbmUnO1xuXG5leHBvcnQgZnVuY3Rpb24gcmVnaXN0ZXJEZWZhdWx0RGVjb3JhdG9ycyhpbnN0YW5jZSkge1xuICByZWdpc3RlcklubGluZShpbnN0YW5jZSk7XG59XG5cbiJdfQ==
17 |
--------------------------------------------------------------------------------
/lib/handlebars/compiler/ast.js:
--------------------------------------------------------------------------------
1 | let AST = {
2 | // Public API used to evaluate derived attributes regarding AST nodes
3 | helpers: {
4 | // a mustache is definitely a helper if:
5 | // * it is an eligible helper, and
6 | // * it has at least one parameter or hash segment
7 | helperExpression: function(node) {
8 | return (node.type === 'SubExpression')
9 | || ((node.type === 'MustacheStatement' || node.type === 'BlockStatement')
10 | && !!((node.params && node.params.length) || node.hash));
11 | },
12 |
13 | scopedId: function(path) {
14 | return (/^\.|this\b/).test(path.original);
15 | },
16 |
17 | // an ID is simple if it only has one part, and that part is not
18 | // `..` or `this`.
19 | simpleId: function(path) {
20 | return path.parts.length === 1 && !AST.helpers.scopedId(path) && !path.depth;
21 | }
22 | }
23 | };
24 |
25 |
26 | // Must be exported as an object rather than the root of the module as the jison lexer
27 | // must modify the object to operate properly.
28 | export default AST;
29 |
--------------------------------------------------------------------------------
/bench/dist-size.js:
--------------------------------------------------------------------------------
1 | var async = require('async'),
2 | fs = require('fs'),
3 | zlib = require('zlib');
4 |
5 | module.exports = function(grunt, callback) {
6 | var distFiles = fs.readdirSync('dist'),
7 | distSizes = {};
8 |
9 | async.each(distFiles, function(file, callback) {
10 | var content;
11 | try {
12 | content = fs.readFileSync('dist/' + file);
13 | } catch (err) {
14 | if (err.code === 'EISDIR') {
15 | callback();
16 | return;
17 | } else {
18 | throw err;
19 | }
20 | }
21 |
22 | file = file.replace(/\.js/, '').replace(/\./g, '_');
23 | distSizes[file] = content.length;
24 |
25 | zlib.gzip(content, function(err, data) {
26 | if (err) {
27 | throw err;
28 | }
29 |
30 | distSizes[file + '_gz'] = data.length;
31 | callback();
32 | });
33 | },
34 | function() {
35 | grunt.log.writeln('Distribution sizes: ' + JSON.stringify(distSizes, undefined, 2));
36 | callback([distSizes]);
37 | });
38 | };
39 |
--------------------------------------------------------------------------------
/lib/handlebars/logger.js:
--------------------------------------------------------------------------------
1 | import {indexOf} from './utils';
2 |
3 | let logger = {
4 | methodMap: ['debug', 'info', 'warn', 'error'],
5 | level: 'info',
6 |
7 | // Maps a given level value to the `methodMap` indexes above.
8 | lookupLevel: function(level) {
9 | if (typeof level === 'string') {
10 | let levelMap = indexOf(logger.methodMap, level.toLowerCase());
11 | if (levelMap >= 0) {
12 | level = levelMap;
13 | } else {
14 | level = parseInt(level, 10);
15 | }
16 | }
17 |
18 | return level;
19 | },
20 |
21 | // Can be overridden in the host environment
22 | log: function(level, ...message) {
23 | level = logger.lookupLevel(level);
24 |
25 | if (typeof console !== 'undefined' && logger.lookupLevel(logger.level) <= level) {
26 | let method = logger.methodMap[level];
27 | if (!console[method]) { // eslint-disable-line no-console
28 | method = 'log';
29 | }
30 | console[method](...message); // eslint-disable-line no-console
31 | }
32 | }
33 | };
34 |
35 | export default logger;
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (C) 2011-2016 by Yehuda Katz
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/lib/handlebars.runtime.js:
--------------------------------------------------------------------------------
1 | import * as base from './handlebars/base';
2 |
3 | // Each of these augment the Handlebars object. No need to setup here.
4 | // (This is done to easily share code between commonjs and browse envs)
5 | import SafeString from './handlebars/safe-string';
6 | import Exception from './handlebars/exception';
7 | import * as Utils from './handlebars/utils';
8 | import * as runtime from './handlebars/runtime';
9 |
10 | import noConflict from './handlebars/no-conflict';
11 |
12 | // For compatibility and usage outside of module systems, make the Handlebars object a namespace
13 | function create() {
14 | let hb = new base.HandlebarsEnvironment();
15 |
16 | Utils.extend(hb, base);
17 | hb.SafeString = SafeString;
18 | hb.Exception = Exception;
19 | hb.Utils = Utils;
20 | hb.escapeExpression = Utils.escapeExpression;
21 |
22 | hb.VM = runtime;
23 | hb.template = function(spec) {
24 | return runtime.template(spec, hb);
25 | };
26 |
27 | return hb;
28 | }
29 |
30 | let inst = create();
31 | inst.create = create;
32 |
33 | noConflict(inst);
34 |
35 | inst['default'] = inst;
36 |
37 | export default inst;
38 |
--------------------------------------------------------------------------------
/lib/handlebars.js:
--------------------------------------------------------------------------------
1 | import runtime from './handlebars.runtime';
2 |
3 | // Compiler imports
4 | import AST from './handlebars/compiler/ast';
5 | import { parser as Parser, parse } from './handlebars/compiler/base';
6 | import { Compiler, compile, precompile } from './handlebars/compiler/compiler';
7 | import JavaScriptCompiler from './handlebars/compiler/javascript-compiler';
8 | import Visitor from './handlebars/compiler/visitor';
9 |
10 | import noConflict from './handlebars/no-conflict';
11 |
12 | let _create = runtime.create;
13 | function create() {
14 | let hb = _create();
15 |
16 | hb.compile = function(input, options) {
17 | return compile(input, options, hb);
18 | };
19 | hb.precompile = function(input, options) {
20 | return precompile(input, options, hb);
21 | };
22 |
23 | hb.AST = AST;
24 | hb.Compiler = Compiler;
25 | hb.JavaScriptCompiler = JavaScriptCompiler;
26 | hb.Parser = Parser;
27 | hb.parse = parse;
28 |
29 | return hb;
30 | }
31 |
32 | let inst = create();
33 | inst.create = create;
34 |
35 | noConflict(inst);
36 |
37 | inst.Visitor = Visitor;
38 |
39 | inst['default'] = inst;
40 |
41 | export default inst;
42 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | before_install:
3 | - npm install -g grunt-cli
4 | script:
5 | - grunt --stack travis
6 | email:
7 | on_failure: change
8 | on_success: never
9 | env:
10 | global:
11 | - S3_BUCKET_NAME=builds.handlebarsjs.com
12 | - secure: ckyEe5dzjdFDjmZ6wIrhGm0CFBEnKq8c1dYptfgVV/Q5/nJFGzu8T0yTjouS/ERxzdT2H327/63VCxhFnLCRHrsh4rlW/rCy4XI3O/0TeMLgFPa4TXkO8359qZ4CB44TBb3NsJyQXNMYdJpPLTCVTMpuiqqkFFOr+6OeggR7ufA=
13 | - secure: Nm4AgSfsgNB21kgKrF9Tl7qVZU8YYREhouQunFracTcZZh2NZ2XH5aHuSiXCj88B13Cr/jGbJKsZ4T3QS3wWYtz6lkyVOx3H3iI+TMtqhD9RM3a7A4O+4vVN8IioB2YjhEu0OKjwgX5gp+0uF+pLEi7Hpj6fupD3AbbL5uYcKg8=
14 | matrix:
15 | include:
16 | - node_js: '5'
17 | env:
18 | - PUBLISH=true
19 | - secure: pLTzghtVll9yGKJI0AaB0uI8GypfWxLTaIB0ZL8//yN3nAEIKMhf/RRilYTsn/rKj2NUa7vt2edYILi3lttOUlCBOwTc9amiRms1W8Lwr/3IdWPeBLvLuH1zNJRm2lBAwU4LBSqaOwhGaxOQr6KHTnWudhNhgOucxpZfvfI/dFw=
20 | - secure: yERYCf7AwL11D9uMtacly/THGV8BlzsMmrt+iQVvGA3GaY6QMmfYqf6P6cCH98sH5etd1Y+1e6YrPeMjqI6lyRllT7FptoyOdHulazQe86VQN4sc0EpqMlH088kB7gGjTut9Z+X9ViooT5XEh9WA5jXEI9pXhQJNoIHkWPuwGuY=
21 | - node_js: '4'
22 | cache:
23 | directories:
24 | - node_modules
25 |
26 | git:
27 | depth: 100
28 |
--------------------------------------------------------------------------------
/dist/cjs/handlebars/safe-string.js:
--------------------------------------------------------------------------------
1 | // Build out our basic SafeString type
2 | 'use strict';
3 |
4 | exports.__esModule = true;
5 | function SafeString(string) {
6 | this.string = string;
7 | }
8 |
9 | SafeString.prototype.toString = SafeString.prototype.toHTML = function () {
10 | return '' + this.string;
11 | };
12 |
13 | exports['default'] = SafeString;
14 | module.exports = exports['default'];
15 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL2xpYi9oYW5kbGViYXJzL3NhZmUtc3RyaW5nLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7QUFDQSxTQUFTLFVBQVUsQ0FBQyxNQUFNLEVBQUU7QUFDMUIsTUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7Q0FDdEI7O0FBRUQsVUFBVSxDQUFDLFNBQVMsQ0FBQyxRQUFRLEdBQUcsVUFBVSxDQUFDLFNBQVMsQ0FBQyxNQUFNLEdBQUcsWUFBVztBQUN2RSxTQUFPLEVBQUUsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDO0NBQ3pCLENBQUM7O3FCQUVhLFVBQVUiLCJmaWxlIjoic2FmZS1zdHJpbmcuanMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBCdWlsZCBvdXQgb3VyIGJhc2ljIFNhZmVTdHJpbmcgdHlwZVxuZnVuY3Rpb24gU2FmZVN0cmluZyhzdHJpbmcpIHtcbiAgdGhpcy5zdHJpbmcgPSBzdHJpbmc7XG59XG5cblNhZmVTdHJpbmcucHJvdG90eXBlLnRvU3RyaW5nID0gU2FmZVN0cmluZy5wcm90b3R5cGUudG9IVE1MID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiAnJyArIHRoaXMuc3RyaW5nO1xufTtcblxuZXhwb3J0IGRlZmF1bHQgU2FmZVN0cmluZztcbiJdfQ==
16 |
--------------------------------------------------------------------------------
/lib/handlebars/exception.js:
--------------------------------------------------------------------------------
1 |
2 | const errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
3 |
4 | function Exception(message, node) {
5 | let loc = node && node.loc,
6 | line,
7 | column;
8 | if (loc) {
9 | line = loc.start.line;
10 | column = loc.start.column;
11 |
12 | message += ' - ' + line + ':' + column;
13 | }
14 |
15 | let tmp = Error.prototype.constructor.call(this, message);
16 |
17 | // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
18 | for (let idx = 0; idx < errorProps.length; idx++) {
19 | this[errorProps[idx]] = tmp[errorProps[idx]];
20 | }
21 |
22 | /* istanbul ignore else */
23 | if (Error.captureStackTrace) {
24 | Error.captureStackTrace(this, Exception);
25 | }
26 |
27 | try {
28 | if (loc) {
29 | this.lineNumber = line;
30 |
31 | // Work around issue under safari where we can't directly set the column value
32 | /* istanbul ignore next */
33 | if (Object.defineProperty) {
34 | Object.defineProperty(this, 'column', {value: column});
35 | } else {
36 | this.column = column;
37 | }
38 | }
39 | } catch (nop) {
40 | /* Ignore if the browser is very particular */
41 | }
42 | }
43 |
44 | Exception.prototype = new Error();
45 |
46 | export default Exception;
47 |
--------------------------------------------------------------------------------
/tasks/version.js:
--------------------------------------------------------------------------------
1 | var async = require('async'),
2 | git = require('./util/git'),
3 | semver = require('semver');
4 |
5 | module.exports = function(grunt) {
6 | grunt.registerTask('version', 'Updates the current release version', function() {
7 | var done = this.async(),
8 | pkg = grunt.config('pkg'),
9 | version = grunt.option('ver');
10 |
11 | if (!semver.valid(version)) {
12 | throw new Error('Must provide a version number (Ex: --ver=1.0.0):\n\t' + version + '\n\n');
13 | }
14 |
15 | pkg.version = version;
16 | grunt.config('pkg', pkg);
17 |
18 | grunt.log.writeln('Updating to version ' + version);
19 |
20 | async.each([
21 | ['lib/handlebars/base.js', (/const VERSION = ['"](.*)['"];/), 'const VERSION = \'' + version + '\';'],
22 | ['components/bower.json', (/"version":.*/), '"version": "' + version + '",'],
23 | ['components/handlebars.js.nuspec', (/.*<\/version>/), '' + version + '']
24 | ],
25 | function(args, callback) {
26 | replace.apply(undefined, args);
27 | grunt.log.writeln(' - ' + args[0]);
28 | git.add(args[0], callback);
29 | },
30 | function() {
31 | grunt.task.run(['default']);
32 | done();
33 | });
34 | });
35 |
36 | function replace(path, regex, value) {
37 | var content = grunt.file.read(path);
38 | content = content.replace(regex, value);
39 | grunt.file.write(path, content);
40 | }
41 | };
42 |
--------------------------------------------------------------------------------
/spec/env/runner.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 | var fs = require('fs'),
3 | Mocha = require('mocha'),
4 | path = require('path');
5 |
6 | var errors = 0,
7 | testDir = path.dirname(__dirname),
8 | grep = process.argv[2];
9 |
10 | // Lazy hack, but whatever
11 | if (grep === '--min') {
12 | global.minimizedTest = true;
13 | grep = undefined;
14 | }
15 |
16 | var files = fs.readdirSync(testDir)
17 | .filter(function(name) { return (/.*\.js$/).test(name); })
18 | .map(function(name) { return testDir + '/' + name; });
19 |
20 | if (global.minimizedTest) {
21 | run('./runtime', function() {
22 | run('./browser', function() {
23 | /* eslint-disable no-process-exit */
24 | process.exit(errors);
25 | /* eslint-enable no-process-exit */
26 | });
27 | });
28 | } else {
29 | run('./runtime', function() {
30 | run('./browser', function() {
31 | run('./node', function() {
32 | /* eslint-disable no-process-exit */
33 | process.exit(errors);
34 | /* eslint-enable no-process-exit */
35 | });
36 | });
37 | });
38 | }
39 |
40 |
41 | function run(env, callback) {
42 | var mocha = new Mocha();
43 | mocha.ui('bdd');
44 | mocha.files = files.slice();
45 | if (grep) {
46 | mocha.grep(grep);
47 | }
48 |
49 | files.forEach(function(name) {
50 | delete require.cache[name];
51 | });
52 |
53 | console.log('Running env: ' + env);
54 | require(env);
55 | mocha.run(function(errorCount) {
56 | errors += errorCount;
57 | callback();
58 | });
59 | }
60 |
--------------------------------------------------------------------------------
/dist/cjs/handlebars/no-conflict.js:
--------------------------------------------------------------------------------
1 | /* global window */
2 | 'use strict';
3 |
4 | exports.__esModule = true;
5 |
6 | exports['default'] = function (Handlebars) {
7 | /* istanbul ignore next */
8 | var root = typeof global !== 'undefined' ? global : window,
9 | $Handlebars = root.Handlebars;
10 | /* istanbul ignore next */
11 | Handlebars.noConflict = function () {
12 | if (root.Handlebars === Handlebars) {
13 | root.Handlebars = $Handlebars;
14 | }
15 | return Handlebars;
16 | };
17 | };
18 |
19 | module.exports = exports['default'];
20 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL2xpYi9oYW5kbGViYXJzL25vLWNvbmZsaWN0LmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O3FCQUNlLFVBQVMsVUFBVSxFQUFFOztBQUVsQyxNQUFJLElBQUksR0FBRyxPQUFPLE1BQU0sS0FBSyxXQUFXLEdBQUcsTUFBTSxHQUFHLE1BQU07TUFDdEQsV0FBVyxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUM7O0FBRWxDLFlBQVUsQ0FBQyxVQUFVLEdBQUcsWUFBVztBQUNqQyxRQUFJLElBQUksQ0FBQyxVQUFVLEtBQUssVUFBVSxFQUFFO0FBQ2xDLFVBQUksQ0FBQyxVQUFVLEdBQUcsV0FBVyxDQUFDO0tBQy9CO0FBQ0QsV0FBTyxVQUFVLENBQUM7R0FDbkIsQ0FBQztDQUNIIiwiZmlsZSI6Im5vLWNvbmZsaWN0LmpzIiwic291cmNlc0NvbnRlbnQiOlsiLyogZ2xvYmFsIHdpbmRvdyAqL1xuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24oSGFuZGxlYmFycykge1xuICAvKiBpc3RhbmJ1bCBpZ25vcmUgbmV4dCAqL1xuICBsZXQgcm9vdCA9IHR5cGVvZiBnbG9iYWwgIT09ICd1bmRlZmluZWQnID8gZ2xvYmFsIDogd2luZG93LFxuICAgICAgJEhhbmRsZWJhcnMgPSByb290LkhhbmRsZWJhcnM7XG4gIC8qIGlzdGFuYnVsIGlnbm9yZSBuZXh0ICovXG4gIEhhbmRsZWJhcnMubm9Db25mbGljdCA9IGZ1bmN0aW9uKCkge1xuICAgIGlmIChyb290LkhhbmRsZWJhcnMgPT09IEhhbmRsZWJhcnMpIHtcbiAgICAgIHJvb3QuSGFuZGxlYmFycyA9ICRIYW5kbGViYXJzO1xuICAgIH1cbiAgICByZXR1cm4gSGFuZGxlYmFycztcbiAgfTtcbn1cbiJdfQ==
21 |
--------------------------------------------------------------------------------
/spec/source-map.js:
--------------------------------------------------------------------------------
1 | try {
2 | if (typeof define !== 'function' || !define.amd) {
3 | var SourceMap = require('source-map'),
4 | SourceMapConsumer = SourceMap.SourceMapConsumer;
5 | }
6 | } catch (err) {
7 | /* NOP for in browser */
8 | }
9 |
10 | describe('source-map', function() {
11 | if (!Handlebars.precompile || !SourceMap) {
12 | return;
13 | }
14 |
15 | it('should safely include source map info', function() {
16 | var template = Handlebars.precompile('{{hello}}', {destName: 'dest.js', srcName: 'src.hbs'});
17 |
18 | equal(!!template.code, true);
19 | equal(!!template.map, !CompilerContext.browser);
20 | });
21 | it('should map source properly', function() {
22 | var templateSource = ' b{{hello}} \n {{bar}}a {{#block arg hash=(subex 1 subval)}}{{/block}}',
23 | template = Handlebars.precompile(templateSource, {destName: 'dest.js', srcName: 'src.hbs'});
24 |
25 | if (template.map) {
26 | var consumer = new SourceMapConsumer(template.map),
27 | lines = template.code.split('\n'),
28 | srcLines = templateSource.split('\n'),
29 |
30 | generated = grepLine('" b"', lines),
31 | source = grepLine(' b', srcLines);
32 |
33 | var mapped = consumer.originalPositionFor(generated);
34 | equal(mapped.line, source.line);
35 | equal(mapped.column, source.column);
36 | }
37 | });
38 | });
39 |
40 | function grepLine(token, lines) {
41 | for (var i = 0; i < lines.length; i++) {
42 | var column = lines[i].indexOf(token);
43 | if (column >= 0) {
44 | return {
45 | line: i + 1,
46 | column: column
47 | };
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/spec/runtime.js:
--------------------------------------------------------------------------------
1 | describe('runtime', function() {
2 | describe('#template', function() {
3 | it('should throw on invalid templates', function() {
4 | shouldThrow(function() {
5 | Handlebars.template({});
6 | }, Error, 'Unknown template object: object');
7 | shouldThrow(function() {
8 | Handlebars.template();
9 | }, Error, 'Unknown template object: undefined');
10 | shouldThrow(function() {
11 | Handlebars.template('');
12 | }, Error, 'Unknown template object: string');
13 | });
14 | it('should throw on version mismatch', function() {
15 | shouldThrow(function() {
16 | Handlebars.template({
17 | main: {},
18 | compiler: [Handlebars.COMPILER_REVISION + 1]
19 | });
20 | }, Error, /Template was precompiled with a newer version of Handlebars than the current runtime/);
21 | shouldThrow(function() {
22 | Handlebars.template({
23 | main: {},
24 | compiler: [Handlebars.COMPILER_REVISION - 1]
25 | });
26 | }, Error, /Template was precompiled with an older version of Handlebars than the current runtime/);
27 | shouldThrow(function() {
28 | Handlebars.template({
29 | main: {}
30 | });
31 | }, Error, /Template was precompiled with an older version of Handlebars than the current runtime/);
32 | });
33 | });
34 |
35 | describe('#noConflict', function() {
36 | if (!CompilerContext.browser) {
37 | return;
38 | }
39 |
40 | it('should reset on no conflict', function() {
41 | var reset = Handlebars;
42 | Handlebars.noConflict();
43 | equal(Handlebars, 'no-conflict');
44 |
45 | Handlebars = 'really, none';
46 | reset.noConflict();
47 | equal(Handlebars, 'really, none');
48 |
49 | Handlebars = reset;
50 | });
51 | });
52 | });
53 |
--------------------------------------------------------------------------------
/docs/decorators-api.md:
--------------------------------------------------------------------------------
1 | # Decorators
2 |
3 | Decorators allow for blocks to be annotated with metadata or wrapped in functionality prior to execution of the block. This may be used to communicate with the containing helper or to setup a particular state in the system prior to running the block.
4 |
5 | Decorators are registered through similar methods as helpers, `registerDecorators` and `unregisterDecorators`. These can then be referenced via the friendly name in the template using the `{{* decorator}}` and `{{#* decorator}}{/decorator}}` syntaxes. These syntaxes are derivatives of the normal mustache syntax and as such have all of the same argument and whitespace behaviors.
6 |
7 | Decorators are executed when the block program is instantiated and are passed `(program, props, container, context, data, blockParams, depths)`.
8 |
9 | - `program`: The block to wrap
10 | - `props`: Object used to set metadata on the final function. Any values set on this object will be set on the function, regardless of if the original function is replaced or not. Metadata should be applied using this object as values applied to `program` may be masked by subsequent decorators that may wrap `program`.
11 | - `container`: The current runtime container
12 | - `context`: The current context. Since the decorator is run before the block that contains it, this is the parent context.
13 | - `data`: The current `@data` values
14 | - `blockParams`: The current block parameters stack
15 | - `depths`: The current context stack
16 |
17 | Decorators may set values on `props` or return a modified function that wraps `program` in particular behaviors. If the decorator returns nothing, then `program` is left unaltered.
18 |
19 | The [inline partial](https://github.com/wycats/handlebars.js/blob/master/lib/handlebars/decorators/inline.js) implementation provides an example of decorators being used for both metadata and wrapping behaviors.
20 |
--------------------------------------------------------------------------------
/spec/spec.js:
--------------------------------------------------------------------------------
1 | describe('spec', function() {
2 | // NOP Under non-node environments
3 | if (typeof process === 'undefined') {
4 | return;
5 | }
6 |
7 | var _ = require('underscore'),
8 | fs = require('fs');
9 |
10 | var specDir = __dirname + '/mustache/specs/';
11 | var specs = _.filter(fs.readdirSync(specDir), function(name) {
12 | return (/.*\.json$/).test(name);
13 | });
14 |
15 | _.each(specs, function(name) {
16 | var spec = require(specDir + name);
17 | _.each(spec.tests, function(test) {
18 | // Our lambda implementation knowingly deviates from the optional Mustace lambda spec
19 | // We also do not support alternative delimeters
20 | if (name === '~lambdas.json'
21 |
22 | // We also choose to throw if paritals are not found
23 | || (name === 'partials.json' && test.name === 'Failed Lookup')
24 |
25 | // We nest the entire response from partials, not just the literals
26 | || (name === 'partials.json' && test.name === 'Standalone Indentation')
27 |
28 | || (/\{\{\=/).test(test.template)
29 | || _.any(test.partials, function(partial) { return (/\{\{\=/).test(partial); })) {
30 | it.skip(name + ' - ' + test.name);
31 | return;
32 | }
33 |
34 | var data = _.clone(test.data);
35 | if (data.lambda) {
36 | // Blergh
37 | /* eslint-disable no-eval */
38 | data.lambda = eval('(' + data.lambda.js + ')');
39 | /* eslint-enable no-eval */
40 | }
41 | it(name + ' - ' + test.name, function() {
42 | if (test.partials) {
43 | shouldCompileToWithPartials(test.template, [data, {}, test.partials, true], true, test.expected, test.desc + ' "' + test.template + '"');
44 | } else {
45 | shouldCompileTo(test.template, [data, {}, {}, true], test.expected, test.desc + ' "' + test.template + '"');
46 | }
47 | });
48 | });
49 | });
50 | });
51 |
--------------------------------------------------------------------------------
/lib/handlebars/helpers/each.js:
--------------------------------------------------------------------------------
1 | import {createFrame, isArray, isFunction} from '../utils';
2 | import Exception from '../exception';
3 |
4 | export default function(instance) {
5 | instance.registerHelper('each', function(context, options) {
6 | if (!options) {
7 | throw new Exception('Must pass iterator to #each');
8 | }
9 |
10 | let fn = options.fn,
11 | inverse = options.inverse,
12 | i = 0,
13 | ret = '',
14 | data;
15 |
16 | if (isFunction(context)) { context = context.call(this); }
17 |
18 | if (options.data) {
19 | data = createFrame(options.data);
20 | }
21 |
22 | function execIteration(field, index, last) {
23 | if (data) {
24 | data.key = field;
25 | data.index = index;
26 | data.first = index === 0;
27 | data.last = !!last;
28 | }
29 |
30 | ret = ret + fn(context[field], {
31 | data: data,
32 | blockParams: [context[field], field]
33 | });
34 | }
35 |
36 | if (context && typeof context === 'object') {
37 | if (isArray(context)) {
38 | for (let j = context.length; i < j; i++) {
39 | if (i in context) {
40 | execIteration(i, i, i === context.length - 1);
41 | }
42 | }
43 | } else {
44 | let priorKey;
45 |
46 | for (let key in context) {
47 | if (context.hasOwnProperty(key)) {
48 | // We're running the iterations one step out of sync so we can detect
49 | // the last iteration without have to scan the object twice and create
50 | // an itermediate keys array.
51 | if (priorKey !== undefined) {
52 | execIteration(priorKey, i - 1);
53 | }
54 | priorKey = key;
55 | i++;
56 | }
57 | }
58 | if (priorKey !== undefined) {
59 | execIteration(priorKey, i - 1, true);
60 | }
61 | }
62 | }
63 |
64 | if (i === 0) {
65 | ret = inverse(this);
66 | }
67 |
68 | return ret;
69 | });
70 | }
71 |
--------------------------------------------------------------------------------
/dist/cjs/handlebars/helpers/helper-missing.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 | // istanbul ignore next
5 |
6 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
7 |
8 | var _exception = require('../exception');
9 |
10 | var _exception2 = _interopRequireDefault(_exception);
11 |
12 | exports['default'] = function (instance) {
13 | instance.registerHelper('helperMissing', function () /* [args, ]options */{
14 | if (arguments.length === 1) {
15 | // A missing field in a {{foo}} construct.
16 | return undefined;
17 | } else {
18 | // Someone is actually trying to call something, blow up.
19 | throw new _exception2['default']('Missing helper: "' + arguments[arguments.length - 1].name + '"');
20 | }
21 | });
22 | };
23 |
24 | module.exports = exports['default'];
25 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2xpYi9oYW5kbGViYXJzL2hlbHBlcnMvaGVscGVyLW1pc3NpbmcuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozt5QkFBc0IsY0FBYzs7OztxQkFFckIsVUFBUyxRQUFRLEVBQUU7QUFDaEMsVUFBUSxDQUFDLGNBQWMsQ0FBQyxlQUFlLEVBQUUsaUNBQWdDO0FBQ3ZFLFFBQUksU0FBUyxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUU7O0FBRTFCLGFBQU8sU0FBUyxDQUFDO0tBQ2xCLE1BQU07O0FBRUwsWUFBTSwyQkFBYyxtQkFBbUIsR0FBRyxTQUFTLENBQUMsU0FBUyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxJQUFJLEdBQUcsR0FBRyxDQUFDLENBQUM7S0FDdkY7R0FDRixDQUFDLENBQUM7Q0FDSiIsImZpbGUiOiJoZWxwZXItbWlzc2luZy5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBFeGNlcHRpb24gZnJvbSAnLi4vZXhjZXB0aW9uJztcblxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24oaW5zdGFuY2UpIHtcbiAgaW5zdGFuY2UucmVnaXN0ZXJIZWxwZXIoJ2hlbHBlck1pc3NpbmcnLCBmdW5jdGlvbigvKiBbYXJncywgXW9wdGlvbnMgKi8pIHtcbiAgICBpZiAoYXJndW1lbnRzLmxlbmd0aCA9PT0gMSkge1xuICAgICAgLy8gQSBtaXNzaW5nIGZpZWxkIGluIGEge3tmb299fSBjb25zdHJ1Y3QuXG4gICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBTb21lb25lIGlzIGFjdHVhbGx5IHRyeWluZyB0byBjYWxsIHNvbWV0aGluZywgYmxvdyB1cC5cbiAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJ01pc3NpbmcgaGVscGVyOiBcIicgKyBhcmd1bWVudHNbYXJndW1lbnRzLmxlbmd0aCAtIDFdLm5hbWUgKyAnXCInKTtcbiAgICB9XG4gIH0pO1xufVxuIl19
26 |
--------------------------------------------------------------------------------
/spec/env/runtime.js:
--------------------------------------------------------------------------------
1 | require('./common');
2 |
3 | var fs = require('fs'),
4 | vm = require('vm');
5 |
6 | global.Handlebars = 'no-conflict';
7 |
8 | var filename = 'dist/handlebars.runtime.js';
9 | if (global.minimizedTest) {
10 | filename = 'dist/handlebars.runtime.min.js';
11 | }
12 | vm.runInThisContext(fs.readFileSync(__dirname + '/../../' + filename), filename);
13 |
14 | var parse = require('../../dist/cjs/handlebars/compiler/base').parse;
15 | var compiler = require('../../dist/cjs/handlebars/compiler/compiler');
16 | var JavaScriptCompiler = require('../../dist/cjs/handlebars/compiler/javascript-compiler');
17 |
18 | global.CompilerContext = {
19 | browser: true,
20 |
21 | compile: function(template, options) {
22 | // Hack the compiler on to the environment for these specific tests
23 | handlebarsEnv.precompile = function(precompileTemplate, precompileOptions) {
24 | return compiler.precompile(precompileTemplate, precompileOptions, handlebarsEnv);
25 | };
26 | handlebarsEnv.parse = parse;
27 | handlebarsEnv.Compiler = compiler.Compiler;
28 | handlebarsEnv.JavaScriptCompiler = JavaScriptCompiler;
29 |
30 | var templateSpec = handlebarsEnv.precompile(template, options);
31 | return handlebarsEnv.template(safeEval(templateSpec));
32 | },
33 | compileWithPartial: function(template, options) {
34 | // Hack the compiler on to the environment for these specific tests
35 | handlebarsEnv.compile = function(compileTemplate, compileOptions) {
36 | return compiler.compile(compileTemplate, compileOptions, handlebarsEnv);
37 | };
38 | handlebarsEnv.parse = parse;
39 | handlebarsEnv.Compiler = compiler.Compiler;
40 | handlebarsEnv.JavaScriptCompiler = JavaScriptCompiler;
41 |
42 | return handlebarsEnv.compile(template, options);
43 | }
44 | };
45 |
46 | function safeEval(templateSpec) {
47 | /* eslint-disable no-eval, no-console */
48 | try {
49 | return eval('(' + templateSpec + ')');
50 | } catch (err) {
51 | console.error(templateSpec);
52 | throw err;
53 | }
54 | /* eslint-enable no-eval, no-console */
55 | }
56 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "handlebars",
3 | "barename": "handlebars",
4 | "version": "4.0.5",
5 | "description": "Handlebars provides the power necessary to let you build semantic templates effectively with no frustration",
6 | "homepage": "http://www.handlebarsjs.com/",
7 | "keywords": [
8 | "handlebars",
9 | "mustache",
10 | "template",
11 | "html"
12 | ],
13 | "repository": {
14 | "type": "git",
15 | "url": "https://github.com/wycats/handlebars.js.git"
16 | },
17 | "author": "Yehuda Katz",
18 | "license": "MIT",
19 | "readmeFilename": "README.md",
20 | "engines": {
21 | "node": ">=0.4.7"
22 | },
23 | "dependencies": {
24 | "async": "^1.4.0",
25 | "source-map": "^0.4.4",
26 | "yargs": "^3.32.0"
27 | },
28 | "optionalDependencies": {
29 | "uglify-js": "^2.6"
30 | },
31 | "devDependencies": {
32 | "aws-sdk": "^2.1.49",
33 | "babel-loader": "^5.0.0",
34 | "babel-runtime": "^5.1.10",
35 | "benchmark": "~1.0",
36 | "dustjs-linkedin": "^2.0.2",
37 | "eco": "~1.1.0-rc-3",
38 | "grunt": "~0.4.1",
39 | "grunt-babel": "^5.0.0",
40 | "grunt-cli": "~0.1.10",
41 | "grunt-contrib-clean": "0.x",
42 | "grunt-contrib-concat": "0.x",
43 | "grunt-contrib-connect": "0.x",
44 | "grunt-contrib-copy": "0.x",
45 | "grunt-contrib-uglify": "0.x",
46 | "grunt-contrib-watch": "0.x",
47 | "grunt-eslint": "^17.1.0",
48 | "grunt-saucelabs": "8.x",
49 | "grunt-webpack": "^1.0.8",
50 | "istanbul": "github:kpdecker/istanbul",
51 | "jison": "~0.3.0",
52 | "mocha": "~1.20.0",
53 | "mock-stdin": "^0.3.0",
54 | "mustache": "^2.1.3",
55 | "semver": "^5.0.1",
56 | "underscore": "^1.5.1",
57 | "webpack": "^1.12.6",
58 | "webpack-dev-server": "^1.12.1"
59 | },
60 | "main": "lib/index.js",
61 | "bin": {
62 | "handlebars": "bin/handlebars"
63 | },
64 | "scripts": {
65 | "test": "grunt"
66 | },
67 | "jspm": {
68 | "main": "handlebars",
69 | "directories": {
70 | "lib": "dist/cjs"
71 | },
72 | "buildConfig": {
73 | "minify": true
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/dist/cjs/handlebars/helpers/with.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 |
5 | var _utils = require('../utils');
6 |
7 | exports['default'] = function (instance) {
8 | instance.registerHelper('with', function (context, options) {
9 | if (_utils.isFunction(context)) {
10 | context = context.call(this);
11 | }
12 |
13 | var fn = options.fn;
14 |
15 | if (!_utils.isEmpty(context)) {
16 | var data = options.data;
17 |
18 | return fn(context, {
19 | data: data,
20 | blockParams: [context]
21 | });
22 | } else {
23 | return options.inverse(this);
24 | }
25 | });
26 | };
27 |
28 | module.exports = exports['default'];
29 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2xpYi9oYW5kbGViYXJzL2hlbHBlcnMvd2l0aC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7O3FCQUFrQyxVQUFVOztxQkFFN0IsVUFBUyxRQUFRLEVBQUU7QUFDaEMsVUFBUSxDQUFDLGNBQWMsQ0FBQyxNQUFNLEVBQUUsVUFBUyxPQUFPLEVBQUUsT0FBTyxFQUFFO0FBQ3pELFFBQUksa0JBQVcsT0FBTyxDQUFDLEVBQUU7QUFBRSxhQUFPLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztLQUFFOztBQUUxRCxRQUFJLEVBQUUsR0FBRyxPQUFPLENBQUMsRUFBRSxDQUFDOztBQUVwQixRQUFJLENBQUMsZUFBUSxPQUFPLENBQUMsRUFBRTtBQUNyQixVQUFJLElBQUksR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDOztBQUV4QixhQUFPLEVBQUUsQ0FBQyxPQUFPLEVBQUU7QUFDakIsWUFBSSxFQUFFLElBQUk7QUFDVixtQkFBVyxFQUFFLENBQUMsT0FBTyxDQUFDO09BQ3ZCLENBQUMsQ0FBQztLQUNKLE1BQU07QUFDTCxhQUFPLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7S0FDOUI7R0FDRixDQUFDLENBQUM7Q0FDSiIsImZpbGUiOiJ3aXRoLmpzIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtpc0VtcHR5LCBpc0Z1bmN0aW9ufSBmcm9tICcuLi91dGlscyc7XG5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uKGluc3RhbmNlKSB7XG4gIGluc3RhbmNlLnJlZ2lzdGVySGVscGVyKCd3aXRoJywgZnVuY3Rpb24oY29udGV4dCwgb3B0aW9ucykge1xuICAgIGlmIChpc0Z1bmN0aW9uKGNvbnRleHQpKSB7IGNvbnRleHQgPSBjb250ZXh0LmNhbGwodGhpcyk7IH1cblxuICAgIGxldCBmbiA9IG9wdGlvbnMuZm47XG5cbiAgICBpZiAoIWlzRW1wdHkoY29udGV4dCkpIHtcbiAgICAgIGxldCBkYXRhID0gb3B0aW9ucy5kYXRhO1xuXG4gICAgICByZXR1cm4gZm4oY29udGV4dCwge1xuICAgICAgICBkYXRhOiBkYXRhLFxuICAgICAgICBibG9ja1BhcmFtczogW2NvbnRleHRdXG4gICAgICB9KTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIG9wdGlvbnMuaW52ZXJzZSh0aGlzKTtcbiAgICB9XG4gIH0pO1xufVxuIl19
30 |
--------------------------------------------------------------------------------
/tasks/test.js:
--------------------------------------------------------------------------------
1 | var childProcess = require('child_process'),
2 | fs = require('fs');
3 |
4 | module.exports = function(grunt) {
5 | grunt.registerTask('test:bin', function() {
6 | var done = this.async();
7 |
8 | childProcess.exec('./bin/handlebars -a spec/artifacts/empty.handlebars', function(err, stdout) {
9 | if (err) {
10 | throw err;
11 | }
12 |
13 | var expected = fs.readFileSync('./spec/expected/empty.amd.js');
14 | if (stdout.toString() !== expected.toString()) {
15 | throw new Error('Expected binary output differed:\n\n"' + stdout + '"\n\n"' + expected + '"');
16 | }
17 |
18 | done();
19 | });
20 | });
21 | grunt.registerTask('test:mocha', function() {
22 | var done = this.async();
23 |
24 | var runner = childProcess.fork('./spec/env/runner', [], {stdio: 'inherit'});
25 | runner.on('close', function(code) {
26 | if (code != 0) {
27 | grunt.fatal(code + ' tests failed');
28 | }
29 | done();
30 | });
31 | });
32 | grunt.registerTask('test:cov', function() {
33 | var done = this.async();
34 |
35 | var runner = childProcess.fork('node_modules/.bin/istanbul', ['cover', '--source-map', '--', './spec/env/runner.js'], {stdio: 'inherit'});
36 | runner.on('close', function(code) {
37 | if (code != 0) {
38 | grunt.fatal(code + ' tests failed');
39 | }
40 | done();
41 | });
42 | });
43 | grunt.registerTask('test:min', function() {
44 | var done = this.async();
45 |
46 | var runner = childProcess.fork('./spec/env/runner', ['--min'], {stdio: 'inherit'});
47 | runner.on('close', function(code) {
48 | if (code != 0) {
49 | grunt.fatal(code + ' tests failed');
50 | }
51 | done();
52 | });
53 | });
54 |
55 | grunt.registerTask('test:check-cov', function() {
56 | var done = this.async();
57 |
58 | var runner = childProcess.fork('node_modules/.bin/istanbul', ['check-coverage', '--statements', '100', '--functions', '100', '--branches', '100', '--lines 100'], {stdio: 'inherit'});
59 | runner.on('close', function(code) {
60 | if (code != 0) {
61 | grunt.fatal('Coverage check failed: ' + code);
62 | }
63 | done();
64 | });
65 | });
66 | grunt.registerTask('test', ['test:bin', 'test:cov', 'test:check-cov']);
67 | };
68 |
--------------------------------------------------------------------------------
/lib/handlebars/base.js:
--------------------------------------------------------------------------------
1 | import {createFrame, extend, toString} from './utils';
2 | import Exception from './exception';
3 | import {registerDefaultHelpers} from './helpers';
4 | import {registerDefaultDecorators} from './decorators';
5 | import logger from './logger';
6 |
7 | export const VERSION = '4.0.5';
8 | export const COMPILER_REVISION = 7;
9 |
10 | export const REVISION_CHANGES = {
11 | 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
12 | 2: '== 1.0.0-rc.3',
13 | 3: '== 1.0.0-rc.4',
14 | 4: '== 1.x.x',
15 | 5: '== 2.0.0-alpha.x',
16 | 6: '>= 2.0.0-beta.1',
17 | 7: '>= 4.0.0'
18 | };
19 |
20 | const objectType = '[object Object]';
21 |
22 | export function HandlebarsEnvironment(helpers, partials, decorators) {
23 | this.helpers = helpers || {};
24 | this.partials = partials || {};
25 | this.decorators = decorators || {};
26 |
27 | registerDefaultHelpers(this);
28 | registerDefaultDecorators(this);
29 | }
30 |
31 | HandlebarsEnvironment.prototype = {
32 | constructor: HandlebarsEnvironment,
33 |
34 | logger: logger,
35 | log: logger.log,
36 |
37 | registerHelper: function(name, fn) {
38 | if (toString.call(name) === objectType) {
39 | if (fn) { throw new Exception('Arg not supported with multiple helpers'); }
40 | extend(this.helpers, name);
41 | } else {
42 | this.helpers[name] = fn;
43 | }
44 | },
45 | unregisterHelper: function(name) {
46 | delete this.helpers[name];
47 | },
48 |
49 | registerPartial: function(name, partial) {
50 | if (toString.call(name) === objectType) {
51 | extend(this.partials, name);
52 | } else {
53 | if (typeof partial === 'undefined') {
54 | throw new Exception(`Attempting to register a partial called "${name}" as undefined`);
55 | }
56 | this.partials[name] = partial;
57 | }
58 | },
59 | unregisterPartial: function(name) {
60 | delete this.partials[name];
61 | },
62 |
63 | registerDecorator: function(name, fn) {
64 | if (toString.call(name) === objectType) {
65 | if (fn) { throw new Exception('Arg not supported with multiple decorators'); }
66 | extend(this.decorators, name);
67 | } else {
68 | this.decorators[name] = fn;
69 | }
70 | },
71 | unregisterDecorator: function(name) {
72 | delete this.decorators[name];
73 | }
74 | };
75 |
76 | export let log = logger.log;
77 |
78 | export {createFrame, logger};
79 |
--------------------------------------------------------------------------------
/dist/cjs/handlebars/helpers/log.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 |
5 | exports['default'] = function (instance) {
6 | instance.registerHelper('log', function () /* message, options */{
7 | var args = [undefined],
8 | options = arguments[arguments.length - 1];
9 | for (var i = 0; i < arguments.length - 1; i++) {
10 | args.push(arguments[i]);
11 | }
12 |
13 | var level = 1;
14 | if (options.hash.level != null) {
15 | level = options.hash.level;
16 | } else if (options.data && options.data.level != null) {
17 | level = options.data.level;
18 | }
19 | args[0] = level;
20 |
21 | instance.log.apply(instance, args);
22 | });
23 | };
24 |
25 | module.exports = exports['default'];
26 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2xpYi9oYW5kbGViYXJzL2hlbHBlcnMvbG9nLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7cUJBQWUsVUFBUyxRQUFRLEVBQUU7QUFDaEMsVUFBUSxDQUFDLGNBQWMsQ0FBQyxLQUFLLEVBQUUsa0NBQWlDO0FBQzlELFFBQUksSUFBSSxHQUFHLENBQUMsU0FBUyxDQUFDO1FBQ2xCLE9BQU8sR0FBRyxTQUFTLENBQUMsU0FBUyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQztBQUM5QyxTQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsU0FBUyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUU7QUFDN0MsVUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztLQUN6Qjs7QUFFRCxRQUFJLEtBQUssR0FBRyxDQUFDLENBQUM7QUFDZCxRQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsS0FBSyxJQUFJLElBQUksRUFBRTtBQUM5QixXQUFLLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUM7S0FDNUIsTUFBTSxJQUFJLE9BQU8sQ0FBQyxJQUFJLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxLQUFLLElBQUksSUFBSSxFQUFFO0FBQ3JELFdBQUssR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQztLQUM1QjtBQUNELFFBQUksQ0FBQyxDQUFDLENBQUMsR0FBRyxLQUFLLENBQUM7O0FBRWhCLFlBQVEsQ0FBQyxHQUFHLE1BQUEsQ0FBWixRQUFRLEVBQVMsSUFBSSxDQUFDLENBQUM7R0FDeEIsQ0FBQyxDQUFDO0NBQ0oiLCJmaWxlIjoibG9nLmpzIiwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24oaW5zdGFuY2UpIHtcbiAgaW5zdGFuY2UucmVnaXN0ZXJIZWxwZXIoJ2xvZycsIGZ1bmN0aW9uKC8qIG1lc3NhZ2UsIG9wdGlvbnMgKi8pIHtcbiAgICBsZXQgYXJncyA9IFt1bmRlZmluZWRdLFxuICAgICAgICBvcHRpb25zID0gYXJndW1lbnRzW2FyZ3VtZW50cy5sZW5ndGggLSAxXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGFyZ3VtZW50cy5sZW5ndGggLSAxOyBpKyspIHtcbiAgICAgIGFyZ3MucHVzaChhcmd1bWVudHNbaV0pO1xuICAgIH1cblxuICAgIGxldCBsZXZlbCA9IDE7XG4gICAgaWYgKG9wdGlvbnMuaGFzaC5sZXZlbCAhPSBudWxsKSB7XG4gICAgICBsZXZlbCA9IG9wdGlvbnMuaGFzaC5sZXZlbDtcbiAgICB9IGVsc2UgaWYgKG9wdGlvbnMuZGF0YSAmJiBvcHRpb25zLmRhdGEubGV2ZWwgIT0gbnVsbCkge1xuICAgICAgbGV2ZWwgPSBvcHRpb25zLmRhdGEubGV2ZWw7XG4gICAgfVxuICAgIGFyZ3NbMF0gPSBsZXZlbDtcblxuICAgIGluc3RhbmNlLmxvZyguLi4gYXJncyk7XG4gIH0pO1xufVxuIl19
27 |
--------------------------------------------------------------------------------
/dist/cjs/handlebars/helpers/block-helper-missing.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 |
5 | var _utils = require('../utils');
6 |
7 | exports['default'] = function (instance) {
8 | instance.registerHelper('blockHelperMissing', function (context, options) {
9 | var inverse = options.inverse,
10 | fn = options.fn;
11 |
12 | if (context === true) {
13 | return fn(this);
14 | } else if (context === false || context == null) {
15 | return inverse(this);
16 | } else if (_utils.isArray(context)) {
17 | if (context.length > 0) {
18 | return instance.helpers.each(context, options);
19 | } else {
20 | return inverse(this);
21 | }
22 | } else {
23 | return fn(context, options);
24 | }
25 | });
26 | };
27 |
28 | module.exports = exports['default'];
29 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2xpYi9oYW5kbGViYXJzL2hlbHBlcnMvYmxvY2staGVscGVyLW1pc3NpbmcuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7OztxQkFBc0IsVUFBVTs7cUJBRWpCLFVBQVMsUUFBUSxFQUFFO0FBQ2hDLFVBQVEsQ0FBQyxjQUFjLENBQUMsb0JBQW9CLEVBQUUsVUFBUyxPQUFPLEVBQUUsT0FBTyxFQUFFO0FBQ3ZFLFFBQUksT0FBTyxHQUFHLE9BQU8sQ0FBQyxPQUFPO1FBQ3pCLEVBQUUsR0FBRyxPQUFPLENBQUMsRUFBRSxDQUFDOztBQUVwQixRQUFJLE9BQU8sS0FBSyxJQUFJLEVBQUU7QUFDcEIsYUFBTyxFQUFFLENBQUMsSUFBSSxDQUFDLENBQUM7S0FDakIsTUFBTSxJQUFJLE9BQU8sS0FBSyxLQUFLLElBQUksT0FBTyxJQUFJLElBQUksRUFBRTtBQUMvQyxhQUFPLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztLQUN0QixNQUFNLElBQUksZUFBUSxPQUFPLENBQUMsRUFBRTtBQUMzQixVQUFJLE9BQU8sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFO0FBQ3RCLGVBQU8sUUFBUSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDO09BQ2hELE1BQU07QUFDTCxlQUFPLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztPQUN0QjtLQUNGLE1BQU07QUFDTCxhQUFPLEVBQUUsQ0FBQyxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7S0FDN0I7R0FDRixDQUFDLENBQUM7Q0FDSiIsImZpbGUiOiJibG9jay1oZWxwZXItbWlzc2luZy5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7aXNBcnJheX0gZnJvbSAnLi4vdXRpbHMnO1xuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbihpbnN0YW5jZSkge1xuICBpbnN0YW5jZS5yZWdpc3RlckhlbHBlcignYmxvY2tIZWxwZXJNaXNzaW5nJywgZnVuY3Rpb24oY29udGV4dCwgb3B0aW9ucykge1xuICAgIGxldCBpbnZlcnNlID0gb3B0aW9ucy5pbnZlcnNlLFxuICAgICAgICBmbiA9IG9wdGlvbnMuZm47XG5cbiAgICBpZiAoY29udGV4dCA9PT0gdHJ1ZSkge1xuICAgICAgcmV0dXJuIGZuKHRoaXMpO1xuICAgIH0gZWxzZSBpZiAoY29udGV4dCA9PT0gZmFsc2UgfHwgY29udGV4dCA9PSBudWxsKSB7XG4gICAgICByZXR1cm4gaW52ZXJzZSh0aGlzKTtcbiAgICB9IGVsc2UgaWYgKGlzQXJyYXkoY29udGV4dCkpIHtcbiAgICAgIGlmIChjb250ZXh0Lmxlbmd0aCA+IDApIHtcbiAgICAgICAgcmV0dXJuIGluc3RhbmNlLmhlbHBlcnMuZWFjaChjb250ZXh0LCBvcHRpb25zKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiBpbnZlcnNlKHRoaXMpO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gZm4oY29udGV4dCwgb3B0aW9ucyk7XG4gICAgfVxuICB9KTtcbn1cbiJdfQ==
30 |
--------------------------------------------------------------------------------
/tasks/publish.js:
--------------------------------------------------------------------------------
1 | var _ = require('underscore'),
2 | async = require('async'),
3 | AWS = require('aws-sdk'),
4 | git = require('./util/git'),
5 | semver = require('semver');
6 |
7 | module.exports = function(grunt) {
8 | grunt.registerTask('publish:latest', function() {
9 | var done = this.async();
10 |
11 | git.debug(function(remotes, branches) {
12 | grunt.log.writeln('remotes: ' + remotes);
13 | grunt.log.writeln('branches: ' + branches);
14 |
15 | git.commitInfo(function(err, info) {
16 | grunt.log.writeln('tag: ' + info.tagName);
17 |
18 | if (info.isMaster) {
19 | initSDK();
20 |
21 | var files = ['-latest', '-' + info.head];
22 | if (info.tagName && semver.valid(info.tagName)) {
23 | files.push('-' + info.tagName);
24 | }
25 |
26 | publish(fileMap(files), done);
27 | } else {
28 | // Silently ignore for branches
29 | done();
30 | }
31 | });
32 | });
33 | });
34 | grunt.registerTask('publish:version', function() {
35 | var done = this.async();
36 | initSDK();
37 |
38 | git.commitInfo(function(err, info) {
39 | if (!info.tagName) {
40 | throw new Error('The current commit must be tagged');
41 | }
42 | publish(fileMap(['-' + info.tagName]), done);
43 | });
44 | });
45 |
46 | function initSDK() {
47 | var bucket = process.env.S3_BUCKET_NAME,
48 | key = process.env.S3_ACCESS_KEY_ID,
49 | secret = process.env.S3_SECRET_ACCESS_KEY;
50 |
51 | if (!bucket || !key || !secret) {
52 | throw new Error('Missing S3 config values');
53 | }
54 |
55 | AWS.config.update({accessKeyId: key, secretAccessKey: secret});
56 | }
57 | function publish(files, callback) {
58 | var s3 = new AWS.S3(),
59 | bucket = process.env.S3_BUCKET_NAME;
60 |
61 | async.forEach(_.keys(files), function(file, callback) {
62 | var params = {Bucket: bucket, Key: file, Body: grunt.file.read(files[file])};
63 | s3.putObject(params, function(err) {
64 | if (err) {
65 | throw err;
66 | } else {
67 | grunt.log.writeln('Published ' + file + ' to build server.');
68 | callback();
69 | }
70 | });
71 | },
72 | callback);
73 | }
74 | function fileMap(suffixes) {
75 | var map = {};
76 | _.each(['handlebars.js', 'handlebars.min.js', 'handlebars.runtime.js', 'handlebars.runtime.min.js'], function(file) {
77 | _.each(suffixes, function(suffix) {
78 | map[file.replace(/\.js$/, suffix + '.js')] = 'dist/' + file;
79 | });
80 | });
81 | return map;
82 | }
83 | };
84 |
--------------------------------------------------------------------------------
/spec/env/common.js:
--------------------------------------------------------------------------------
1 | var AssertError;
2 | if (Error.captureStackTrace) {
3 | AssertError = function AssertError(message, caller) {
4 | Error.prototype.constructor.call(this, message);
5 | this.message = message;
6 |
7 | if (Error.captureStackTrace) {
8 | Error.captureStackTrace(this, caller || AssertError);
9 | }
10 | };
11 |
12 | AssertError.prototype = new Error();
13 | } else {
14 | AssertError = Error;
15 | }
16 |
17 | global.shouldCompileTo = function(string, hashOrArray, expected, message) {
18 | shouldCompileToWithPartials(string, hashOrArray, false, expected, message);
19 | };
20 |
21 | global.shouldCompileToWithPartials = function shouldCompileToWithPartials(string, hashOrArray, partials, expected, message) {
22 | var result = compileWithPartials(string, hashOrArray, partials);
23 | if (result !== expected) {
24 | throw new AssertError("'" + result + "' should === '" + expected + "': " + message, shouldCompileToWithPartials);
25 | }
26 | };
27 |
28 | global.compileWithPartials = function(string, hashOrArray, partials) {
29 | var template,
30 | ary,
31 | options;
32 | if (hashOrArray && hashOrArray.hash) {
33 | ary = [hashOrArray.hash, hashOrArray];
34 | delete hashOrArray.hash;
35 | } else if (Object.prototype.toString.call(hashOrArray) === '[object Array]') {
36 | ary = [];
37 | ary.push(hashOrArray[0]);
38 | ary.push({ helpers: hashOrArray[1], partials: hashOrArray[2] });
39 | options = typeof hashOrArray[3] === 'object' ? hashOrArray[3] : {compat: hashOrArray[3]};
40 | if (hashOrArray[4] != null) {
41 | options.data = !!hashOrArray[4];
42 | ary[1].data = hashOrArray[4];
43 | }
44 | } else {
45 | ary = [hashOrArray];
46 | }
47 |
48 | template = CompilerContext[partials ? 'compileWithPartial' : 'compile'](string, options);
49 | return template.apply(this, ary);
50 | };
51 |
52 |
53 | global.equals = global.equal = function equals(a, b, msg) {
54 | if (a !== b) {
55 | throw new AssertError("'" + a + "' should === '" + b + "'" + (msg ? ': ' + msg : ''), equals);
56 | }
57 | };
58 |
59 | global.shouldThrow = function(callback, type, msg) {
60 | var failed;
61 | try {
62 | callback();
63 | failed = true;
64 | } catch (caught) {
65 | if (type && !(caught instanceof type)) {
66 | throw new AssertError('Type failure: ' + caught);
67 | }
68 | if (msg && !(msg.test ? msg.test(caught.message) : msg === caught.message)) {
69 | throw new AssertError('Throw mismatch: Expected ' + caught.message + ' to match ' + msg + '\n\n' + caught.stack, shouldThrow);
70 | }
71 | }
72 | if (failed) {
73 | throw new AssertError('It failed to throw', shouldThrow);
74 | }
75 | };
76 |
--------------------------------------------------------------------------------
/dist/cjs/handlebars/decorators/inline.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 |
5 | var _utils = require('../utils');
6 |
7 | exports['default'] = function (instance) {
8 | instance.registerDecorator('inline', function (fn, props, container, options) {
9 | var ret = fn;
10 | if (!props.partials) {
11 | props.partials = {};
12 | ret = function (context, options) {
13 | // Create a new partials stack frame prior to exec.
14 | var original = container.partials;
15 | container.partials = _utils.extend({}, original, props.partials);
16 | var ret = fn(context, options);
17 | container.partials = original;
18 | return ret;
19 | };
20 | }
21 |
22 | props.partials[options.args[0]] = options.fn;
23 |
24 | return ret;
25 | });
26 | };
27 |
28 | module.exports = exports['default'];
29 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2xpYi9oYW5kbGViYXJzL2RlY29yYXRvcnMvaW5saW5lLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7cUJBQXFCLFVBQVU7O3FCQUVoQixVQUFTLFFBQVEsRUFBRTtBQUNoQyxVQUFRLENBQUMsaUJBQWlCLENBQUMsUUFBUSxFQUFFLFVBQVMsRUFBRSxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFO0FBQzNFLFFBQUksR0FBRyxHQUFHLEVBQUUsQ0FBQztBQUNiLFFBQUksQ0FBQyxLQUFLLENBQUMsUUFBUSxFQUFFO0FBQ25CLFdBQUssQ0FBQyxRQUFRLEdBQUcsRUFBRSxDQUFDO0FBQ3BCLFNBQUcsR0FBRyxVQUFTLE9BQU8sRUFBRSxPQUFPLEVBQUU7O0FBRS9CLFlBQUksUUFBUSxHQUFHLFNBQVMsQ0FBQyxRQUFRLENBQUM7QUFDbEMsaUJBQVMsQ0FBQyxRQUFRLEdBQUcsY0FBTyxFQUFFLEVBQUUsUUFBUSxFQUFFLEtBQUssQ0FBQyxRQUFRLENBQUMsQ0FBQztBQUMxRCxZQUFJLEdBQUcsR0FBRyxFQUFFLENBQUMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDO0FBQy9CLGlCQUFTLENBQUMsUUFBUSxHQUFHLFFBQVEsQ0FBQztBQUM5QixlQUFPLEdBQUcsQ0FBQztPQUNaLENBQUM7S0FDSDs7QUFFRCxTQUFLLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxPQUFPLENBQUMsRUFBRSxDQUFDOztBQUU3QyxXQUFPLEdBQUcsQ0FBQztHQUNaLENBQUMsQ0FBQztDQUNKIiwiZmlsZSI6ImlubGluZS5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7ZXh0ZW5kfSBmcm9tICcuLi91dGlscyc7XG5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uKGluc3RhbmNlKSB7XG4gIGluc3RhbmNlLnJlZ2lzdGVyRGVjb3JhdG9yKCdpbmxpbmUnLCBmdW5jdGlvbihmbiwgcHJvcHMsIGNvbnRhaW5lciwgb3B0aW9ucykge1xuICAgIGxldCByZXQgPSBmbjtcbiAgICBpZiAoIXByb3BzLnBhcnRpYWxzKSB7XG4gICAgICBwcm9wcy5wYXJ0aWFscyA9IHt9O1xuICAgICAgcmV0ID0gZnVuY3Rpb24oY29udGV4dCwgb3B0aW9ucykge1xuICAgICAgICAvLyBDcmVhdGUgYSBuZXcgcGFydGlhbHMgc3RhY2sgZnJhbWUgcHJpb3IgdG8gZXhlYy5cbiAgICAgICAgbGV0IG9yaWdpbmFsID0gY29udGFpbmVyLnBhcnRpYWxzO1xuICAgICAgICBjb250YWluZXIucGFydGlhbHMgPSBleHRlbmQoe30sIG9yaWdpbmFsLCBwcm9wcy5wYXJ0aWFscyk7XG4gICAgICAgIGxldCByZXQgPSBmbihjb250ZXh0LCBvcHRpb25zKTtcbiAgICAgICAgY29udGFpbmVyLnBhcnRpYWxzID0gb3JpZ2luYWw7XG4gICAgICAgIHJldHVybiByZXQ7XG4gICAgICB9O1xuICAgIH1cblxuICAgIHByb3BzLnBhcnRpYWxzW29wdGlvbnMuYXJnc1swXV0gPSBvcHRpb25zLmZuO1xuXG4gICAgcmV0dXJuIHJldDtcbiAgfSk7XG59XG4iXX0=
30 |
--------------------------------------------------------------------------------
/spec/umd-runtime.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Mocha
4 |
5 |
6 |
7 |
8 |
14 |
15 |
21 |
22 |
25 |
26 |
27 |
28 |
29 |
30 |
37 |
85 |
86 |
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/lib/handlebars/utils.js:
--------------------------------------------------------------------------------
1 | const escape = {
2 | '&': '&',
3 | '<': '<',
4 | '>': '>',
5 | '"': '"',
6 | "'": ''',
7 | '`': '`',
8 | '=': '='
9 | };
10 |
11 | const badChars = /[&<>"'`=]/g,
12 | possible = /[&<>"'`=]/;
13 |
14 | function escapeChar(chr) {
15 | return escape[chr];
16 | }
17 |
18 | export function extend(obj/* , ...source */) {
19 | for (let i = 1; i < arguments.length; i++) {
20 | for (let key in arguments[i]) {
21 | if (Object.prototype.hasOwnProperty.call(arguments[i], key)) {
22 | obj[key] = arguments[i][key];
23 | }
24 | }
25 | }
26 |
27 | return obj;
28 | }
29 |
30 | export let toString = Object.prototype.toString;
31 |
32 | // Sourced from lodash
33 | // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt
34 | /* eslint-disable func-style */
35 | let isFunction = function(value) {
36 | return typeof value === 'function';
37 | };
38 | // fallback for older versions of Chrome and Safari
39 | /* istanbul ignore next */
40 | if (isFunction(/x/)) {
41 | isFunction = function(value) {
42 | return typeof value === 'function' && toString.call(value) === '[object Function]';
43 | };
44 | }
45 | export {isFunction};
46 | /* eslint-enable func-style */
47 |
48 | /* istanbul ignore next */
49 | export const isArray = Array.isArray || function(value) {
50 | return (value && typeof value === 'object') ? toString.call(value) === '[object Array]' : false;
51 | };
52 |
53 | // Older IE versions do not directly support indexOf so we must implement our own, sadly.
54 | export function indexOf(array, value) {
55 | for (let i = 0, len = array.length; i < len; i++) {
56 | if (array[i] === value) {
57 | return i;
58 | }
59 | }
60 | return -1;
61 | }
62 |
63 |
64 | export function escapeExpression(string) {
65 | if (typeof string !== 'string') {
66 | // don't escape SafeStrings, since they're already safe
67 | if (string && string.toHTML) {
68 | return string.toHTML();
69 | } else if (string == null) {
70 | return '';
71 | } else if (!string) {
72 | return string + '';
73 | }
74 |
75 | // Force a string conversion as this will be done by the append regardless and
76 | // the regex test will do this transparently behind the scenes, causing issues if
77 | // an object's to string has escaped characters in it.
78 | string = '' + string;
79 | }
80 |
81 | if (!possible.test(string)) { return string; }
82 | return string.replace(badChars, escapeChar);
83 | }
84 |
85 | export function isEmpty(value) {
86 | if (!value && value !== 0) {
87 | return true;
88 | } else if (isArray(value) && value.length === 0) {
89 | return true;
90 | } else {
91 | return false;
92 | }
93 | }
94 |
95 | export function createFrame(object) {
96 | let frame = extend({}, object);
97 | frame._parent = object;
98 | return frame;
99 | }
100 |
101 |
--------------------------------------------------------------------------------
/dist/cjs/handlebars/helpers.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 | exports.registerDefaultHelpers = registerDefaultHelpers;
5 | // istanbul ignore next
6 |
7 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
8 |
9 | var _helpersBlockHelperMissing = require('./helpers/block-helper-missing');
10 |
11 | var _helpersBlockHelperMissing2 = _interopRequireDefault(_helpersBlockHelperMissing);
12 |
13 | var _helpersEach = require('./helpers/each');
14 |
15 | var _helpersEach2 = _interopRequireDefault(_helpersEach);
16 |
17 | var _helpersHelperMissing = require('./helpers/helper-missing');
18 |
19 | var _helpersHelperMissing2 = _interopRequireDefault(_helpersHelperMissing);
20 |
21 | var _helpersIf = require('./helpers/if');
22 |
23 | var _helpersIf2 = _interopRequireDefault(_helpersIf);
24 |
25 | var _helpersLog = require('./helpers/log');
26 |
27 | var _helpersLog2 = _interopRequireDefault(_helpersLog);
28 |
29 | var _helpersLookup = require('./helpers/lookup');
30 |
31 | var _helpersLookup2 = _interopRequireDefault(_helpersLookup);
32 |
33 | var _helpersWith = require('./helpers/with');
34 |
35 | var _helpersWith2 = _interopRequireDefault(_helpersWith);
36 |
37 | function registerDefaultHelpers(instance) {
38 | _helpersBlockHelperMissing2['default'](instance);
39 | _helpersEach2['default'](instance);
40 | _helpersHelperMissing2['default'](instance);
41 | _helpersIf2['default'](instance);
42 | _helpersLog2['default'](instance);
43 | _helpersLookup2['default'](instance);
44 | _helpersWith2['default'](instance);
45 | }
46 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL2xpYi9oYW5kbGViYXJzL2hlbHBlcnMuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7eUNBQXVDLGdDQUFnQzs7OzsyQkFDOUMsZ0JBQWdCOzs7O29DQUNQLDBCQUEwQjs7Ozt5QkFDckMsY0FBYzs7OzswQkFDYixlQUFlOzs7OzZCQUNaLGtCQUFrQjs7OzsyQkFDcEIsZ0JBQWdCOzs7O0FBRWxDLFNBQVMsc0JBQXNCLENBQUMsUUFBUSxFQUFFO0FBQy9DLHlDQUEyQixRQUFRLENBQUMsQ0FBQztBQUNyQywyQkFBYSxRQUFRLENBQUMsQ0FBQztBQUN2QixvQ0FBc0IsUUFBUSxDQUFDLENBQUM7QUFDaEMseUJBQVcsUUFBUSxDQUFDLENBQUM7QUFDckIsMEJBQVksUUFBUSxDQUFDLENBQUM7QUFDdEIsNkJBQWUsUUFBUSxDQUFDLENBQUM7QUFDekIsMkJBQWEsUUFBUSxDQUFDLENBQUM7Q0FDeEIiLCJmaWxlIjoiaGVscGVycy5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCByZWdpc3RlckJsb2NrSGVscGVyTWlzc2luZyBmcm9tICcuL2hlbHBlcnMvYmxvY2staGVscGVyLW1pc3NpbmcnO1xuaW1wb3J0IHJlZ2lzdGVyRWFjaCBmcm9tICcuL2hlbHBlcnMvZWFjaCc7XG5pbXBvcnQgcmVnaXN0ZXJIZWxwZXJNaXNzaW5nIGZyb20gJy4vaGVscGVycy9oZWxwZXItbWlzc2luZyc7XG5pbXBvcnQgcmVnaXN0ZXJJZiBmcm9tICcuL2hlbHBlcnMvaWYnO1xuaW1wb3J0IHJlZ2lzdGVyTG9nIGZyb20gJy4vaGVscGVycy9sb2cnO1xuaW1wb3J0IHJlZ2lzdGVyTG9va3VwIGZyb20gJy4vaGVscGVycy9sb29rdXAnO1xuaW1wb3J0IHJlZ2lzdGVyV2l0aCBmcm9tICcuL2hlbHBlcnMvd2l0aCc7XG5cbmV4cG9ydCBmdW5jdGlvbiByZWdpc3RlckRlZmF1bHRIZWxwZXJzKGluc3RhbmNlKSB7XG4gIHJlZ2lzdGVyQmxvY2tIZWxwZXJNaXNzaW5nKGluc3RhbmNlKTtcbiAgcmVnaXN0ZXJFYWNoKGluc3RhbmNlKTtcbiAgcmVnaXN0ZXJIZWxwZXJNaXNzaW5nKGluc3RhbmNlKTtcbiAgcmVnaXN0ZXJJZihpbnN0YW5jZSk7XG4gIHJlZ2lzdGVyTG9nKGluc3RhbmNlKTtcbiAgcmVnaXN0ZXJMb29rdXAoaW5zdGFuY2UpO1xuICByZWdpc3RlcldpdGgoaW5zdGFuY2UpO1xufVxuIl19
47 |
--------------------------------------------------------------------------------
/spec/utils.js:
--------------------------------------------------------------------------------
1 | describe('utils', function() {
2 | describe('#SafeString', function() {
3 | it('constructing a safestring from a string and checking its type', function() {
4 | var safe = new Handlebars.SafeString('testing 1, 2, 3');
5 | if (!(safe instanceof Handlebars.SafeString)) {
6 | throw new Error('Must be instance of SafeString');
7 | }
8 | equals(safe.toString(), 'testing 1, 2, 3', 'SafeString is equivalent to its underlying string');
9 | });
10 |
11 | it('it should not escape SafeString properties', function() {
12 | var name = new Handlebars.SafeString('Sean O'Malley');
13 |
14 | shouldCompileTo('{{name}}', [{name: name}], 'Sean O'Malley');
15 | });
16 | });
17 |
18 | describe('#escapeExpression', function() {
19 | it('shouhld escape html', function() {
20 | equals(Handlebars.Utils.escapeExpression('foo<&"\'>'), 'foo<&"'>');
21 | equals(Handlebars.Utils.escapeExpression('foo='), 'foo=');
22 | });
23 | it('should not escape SafeString', function() {
24 | var string = new Handlebars.SafeString('foo<&"\'>');
25 | equals(Handlebars.Utils.escapeExpression(string), 'foo<&"\'>');
26 |
27 | var obj = {
28 | toHTML: function() {
29 | return 'foo<&"\'>';
30 | }
31 | };
32 | equals(Handlebars.Utils.escapeExpression(obj), 'foo<&"\'>');
33 | });
34 | it('should handle falsy', function() {
35 | equals(Handlebars.Utils.escapeExpression(''), '');
36 | equals(Handlebars.Utils.escapeExpression(undefined), '');
37 | equals(Handlebars.Utils.escapeExpression(null), '');
38 |
39 | equals(Handlebars.Utils.escapeExpression(false), 'false');
40 | equals(Handlebars.Utils.escapeExpression(0), '0');
41 | });
42 | it('should handle empty objects', function() {
43 | equals(Handlebars.Utils.escapeExpression({}), {}.toString());
44 | equals(Handlebars.Utils.escapeExpression([]), [].toString());
45 | });
46 | });
47 |
48 | describe('#isEmpty', function() {
49 | it('should not be empty', function() {
50 | equals(Handlebars.Utils.isEmpty(undefined), true);
51 | equals(Handlebars.Utils.isEmpty(null), true);
52 | equals(Handlebars.Utils.isEmpty(false), true);
53 | equals(Handlebars.Utils.isEmpty(''), true);
54 | equals(Handlebars.Utils.isEmpty([]), true);
55 | });
56 |
57 | it('should be empty', function() {
58 | equals(Handlebars.Utils.isEmpty(0), false);
59 | equals(Handlebars.Utils.isEmpty([1]), false);
60 | equals(Handlebars.Utils.isEmpty('foo'), false);
61 | equals(Handlebars.Utils.isEmpty({bar: 1}), false);
62 | });
63 | });
64 |
65 | describe('#extend', function() {
66 | it('should ignore prototype values', function() {
67 | function A() {
68 | this.a = 1;
69 | }
70 | A.prototype.b = 4;
71 |
72 | var b = {b: 2};
73 |
74 | Handlebars.Utils.extend(b, new A());
75 |
76 | equals(b.a, 1);
77 | equals(b.b, 2);
78 | });
79 | });
80 | });
81 |
--------------------------------------------------------------------------------
/FAQ.md:
--------------------------------------------------------------------------------
1 | # Frequently Asked Questions
2 |
3 | 1. How can I file a bug report:
4 |
5 | See our guidelines on [reporting issues](https://github.com/wycats/handlebars.js/blob/master/CONTRIBUTING.md#reporting-issues).
6 |
7 | 1. Why isn't my Mustache template working?
8 |
9 | Handlebars deviates from Mustache slightly on a few behaviors. These variations are documented in our [readme](https://github.com/wycats/handlebars.js#differences-between-handlebarsjs-and-mustache).
10 |
11 | 1. Why is it slower when compiling?
12 |
13 | The Handlebars compiler must parse the template and construct a JavaScript program which can then be run. Under some environments such as older mobile devices this can have a performance impact which can be avoided by precompiling. Generally it's recommended that precompilation and the runtime library be used on all clients.
14 |
15 | 1. Why doesn't this work with Content Security Policy restrictions?
16 |
17 | When not using the precompiler, Handlebars generates a dynamic function for each template which can cause issues with pages that have enabled Content Policy. It's recommended that templates are precompiled or the `unsafe-eval` policy is enabled for sites that must generate dynamic templates at runtime.
18 |
19 | 1. How can I include script tags in my template?
20 |
21 | If loading the template via an inlined `
28 | ```
29 |
30 | It's generally recommended that templates are served through external, precompiled, files, which do not suffer from this issue.
31 |
32 | 1. Why are my precompiled scripts throwing exceptions?
33 |
34 | When using the precompiler, it's important that a supporting version of the Handlebars runtime be loaded on the target page. In version 1.x there were rudimentary checks to compare the version but these did not always work. This is fixed under 2.x but the version checking does not work between these two versions. If you see unexpected errors such as `undefined is not a function` or similar, please verify that the same version is being used for both the precompiler and the client. This can be checked via:
35 |
36 | ```sh
37 | handlebars --version
38 | ```
39 | If using the integrated precompiler and
40 |
41 | ```javascript
42 | console.log(Handlebars.VERSION);
43 | ```
44 | On the client side.
45 |
46 | We include the built client libraries in the npm package for those who want to be certain that they are using the same client libraries as the compiler.
47 |
48 | Should these match, please file an issue with us, per our [issue filing guidelines](https://github.com/wycats/handlebars.js/blob/master/CONTRIBUTING.md#reporting-issues).
49 |
50 | 1. How do I load the runtime library when using AMD?
51 |
52 | The `handlebars.runtime.js` file includes a UMD build, which exposes the library as both the module root and the `default` field for compatibility.
53 |
--------------------------------------------------------------------------------
/spec/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Mocha
5 |
6 |
7 |
8 |
9 |
15 |
16 |
22 |
23 |
26 |
27 |
28 |
29 |
30 |
52 |
53 |
92 |
93 |
94 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/print-script:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env node
2 | /* eslint-disable no-console, no-var */
3 | // Util script for debugging source code generation issues
4 |
5 | var script = process.argv[2].replace(/\\n/g, '\n'),
6 | verbose = process.argv[3] === '-v';
7 |
8 | var Handlebars = require('./lib'),
9 | SourceMap = require('source-map'),
10 | SourceMapConsumer = SourceMap.SourceMapConsumer;
11 |
12 | var template = Handlebars.precompile(script, {
13 | srcName: 'input.hbs',
14 | destName: 'output.js',
15 |
16 | assumeObjects: true,
17 | compat: false,
18 | strict: true,
19 | knownHelpersOnly: false
20 | });
21 |
22 | if (!verbose) {
23 | console.log(template);
24 | } else {
25 | var consumer = new SourceMapConsumer(template.map),
26 | lines = template.code.split('\n'),
27 | srcLines = script.split('\n');
28 |
29 | console.log();
30 | console.log('Source:');
31 | srcLines.forEach(function(source, index) {
32 | console.log(index + 1, source);
33 | });
34 | console.log();
35 | console.log('Generated:');
36 | console.log(template.code);
37 | lines.forEach(function(source, index) {
38 | console.log(index + 1, source);
39 | });
40 | console.log();
41 | console.log('Map:');
42 | console.log(template.map);
43 | console.log();
44 |
45 | function collectSource(lines, lineName, colName, order) {
46 | var ret = {},
47 | ordered = [],
48 | last;
49 |
50 | function collect(current) {
51 | if (last) {
52 | var mapLines = lines.slice(last[lineName] - 1, current && current[lineName]);
53 | if (mapLines.length) {
54 | if (current) {
55 | mapLines[mapLines.length - 1] = mapLines[mapLines.length - 1].slice(0, current[colName]);
56 | }
57 | mapLines[0] = mapLines[0].slice(last[colName]);
58 | }
59 | ret[last[lineName] + ':' + last[colName]] = mapLines.join('\n');
60 | ordered.push({
61 | startLine: last[lineName],
62 | startCol: last[colName],
63 | endLine: current && current[lineName]
64 | });
65 | }
66 | last = current;
67 | }
68 |
69 | consumer.eachMapping(collect, undefined, order);
70 | collect();
71 |
72 | return ret;
73 | }
74 |
75 | srcLines = collectSource(srcLines, 'originalLine', 'originalColumn', SourceMapConsumer.ORIGINAL_ORDER);
76 | lines = collectSource(lines, 'generatedLine', 'generatedColumn');
77 |
78 | consumer.eachMapping(function(mapping) {
79 | var originalSrc = srcLines[mapping.originalLine + ':' + mapping.originalColumn],
80 | generatedSrc = lines[mapping.generatedLine + ':' + mapping.generatedColumn];
81 |
82 | if (!mapping.originalLine) {
83 | console.log('generated', mapping.generatedLine + ':' + mapping.generatedColumn, generatedSrc);
84 | } else {
85 | console.log('map',
86 | mapping.source,
87 | mapping.originalLine + ':' + mapping.originalColumn,
88 | originalSrc,
89 | '->',
90 | mapping.generatedLine + ':' + mapping.generatedColumn,
91 | generatedSrc);
92 | }
93 | });
94 | }
95 |
--------------------------------------------------------------------------------
/spec/javascript-compiler.js:
--------------------------------------------------------------------------------
1 | describe('javascript-compiler api', function() {
2 | if (!Handlebars.JavaScriptCompiler) {
3 | return;
4 | }
5 |
6 | describe('#nameLookup', function() {
7 | var $superName;
8 | beforeEach(function() {
9 | $superName = handlebarsEnv.JavaScriptCompiler.prototype.nameLookup;
10 | });
11 | afterEach(function() {
12 | handlebarsEnv.JavaScriptCompiler.prototype.nameLookup = $superName;
13 | });
14 |
15 | it('should allow override', function() {
16 | handlebarsEnv.JavaScriptCompiler.prototype.nameLookup = function(parent, name) {
17 | return parent + '.bar_' + name;
18 | };
19 | /* eslint-disable camelcase */
20 | shouldCompileTo('{{foo}}', { bar_foo: 'food' }, 'food');
21 | /* eslint-enable camelcase */
22 | });
23 |
24 | // Tests nameLookup dot vs. bracket behavior. Bracket is required in certain cases
25 | // to avoid errors in older browsers.
26 | it('should handle reserved words', function() {
27 | shouldCompileTo('{{foo}} {{~null~}}', { foo: 'food' }, 'food');
28 | });
29 | });
30 | describe('#compilerInfo', function() {
31 | var $superCheck, $superInfo;
32 | beforeEach(function() {
33 | $superCheck = handlebarsEnv.VM.checkRevision;
34 | $superInfo = handlebarsEnv.JavaScriptCompiler.prototype.compilerInfo;
35 | });
36 | afterEach(function() {
37 | handlebarsEnv.VM.checkRevision = $superCheck;
38 | handlebarsEnv.JavaScriptCompiler.prototype.compilerInfo = $superInfo;
39 | });
40 | it('should allow compilerInfo override', function() {
41 | handlebarsEnv.JavaScriptCompiler.prototype.compilerInfo = function() {
42 | return 'crazy';
43 | };
44 | handlebarsEnv.VM.checkRevision = function(compilerInfo) {
45 | if (compilerInfo !== 'crazy') {
46 | throw new Error('It didn\'t work');
47 | }
48 | };
49 | shouldCompileTo('{{foo}} ', { foo: 'food' }, 'food ');
50 | });
51 | });
52 | describe('buffer', function() {
53 | var $superAppend, $superCreate;
54 | beforeEach(function() {
55 | handlebarsEnv.JavaScriptCompiler.prototype.forceBuffer = true;
56 | $superAppend = handlebarsEnv.JavaScriptCompiler.prototype.appendToBuffer;
57 | $superCreate = handlebarsEnv.JavaScriptCompiler.prototype.initializeBuffer;
58 | });
59 | afterEach(function() {
60 | handlebarsEnv.JavaScriptCompiler.prototype.forceBuffer = false;
61 | handlebarsEnv.JavaScriptCompiler.prototype.appendToBuffer = $superAppend;
62 | handlebarsEnv.JavaScriptCompiler.prototype.initializeBuffer = $superCreate;
63 | });
64 |
65 | it('should allow init buffer override', function() {
66 | handlebarsEnv.JavaScriptCompiler.prototype.initializeBuffer = function() {
67 | return this.quotedString('foo_');
68 | };
69 | shouldCompileTo('{{foo}} ', { foo: 'food' }, 'foo_food ');
70 | });
71 | it('should allow append buffer override', function() {
72 | handlebarsEnv.JavaScriptCompiler.prototype.appendToBuffer = function(string) {
73 | return $superAppend.call(this, [string, ' + "_foo"']);
74 | };
75 | shouldCompileTo('{{foo}}', { foo: 'food' }, 'food_foo');
76 | });
77 | });
78 | });
79 |
--------------------------------------------------------------------------------
/dist/cjs/handlebars/helpers/if.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 |
5 | var _utils = require('../utils');
6 |
7 | exports['default'] = function (instance) {
8 | instance.registerHelper('if', function (conditional, options) {
9 | if (_utils.isFunction(conditional)) {
10 | conditional = conditional.call(this);
11 | }
12 |
13 | // Default behavior is to render the positive path if the value is truthy and not empty.
14 | // The `includeZero` option may be set to treat the condtional as purely not empty based on the
15 | // behavior of isEmpty. Effectively this determines if 0 is handled by the positive path or negative.
16 | if (!options.hash.includeZero && !conditional || _utils.isEmpty(conditional)) {
17 | return options.inverse(this);
18 | } else {
19 | return options.fn(this);
20 | }
21 | });
22 |
23 | instance.registerHelper('unless', function (conditional, options) {
24 | return instance.helpers['if'].call(this, conditional, { fn: options.inverse, inverse: options.fn, hash: options.hash });
25 | });
26 | };
27 |
28 | module.exports = exports['default'];
29 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2xpYi9oYW5kbGViYXJzL2hlbHBlcnMvaWYuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7OztxQkFBa0MsVUFBVTs7cUJBRTdCLFVBQVMsUUFBUSxFQUFFO0FBQ2hDLFVBQVEsQ0FBQyxjQUFjLENBQUMsSUFBSSxFQUFFLFVBQVMsV0FBVyxFQUFFLE9BQU8sRUFBRTtBQUMzRCxRQUFJLGtCQUFXLFdBQVcsQ0FBQyxFQUFFO0FBQUUsaUJBQVcsR0FBRyxXQUFXLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0tBQUU7Ozs7O0FBS3RFLFFBQUksQUFBQyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsV0FBVyxJQUFJLENBQUMsV0FBVyxJQUFLLGVBQVEsV0FBVyxDQUFDLEVBQUU7QUFDdkUsYUFBTyxPQUFPLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO0tBQzlCLE1BQU07QUFDTCxhQUFPLE9BQU8sQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLENBQUM7S0FDekI7R0FDRixDQUFDLENBQUM7O0FBRUgsVUFBUSxDQUFDLGNBQWMsQ0FBQyxRQUFRLEVBQUUsVUFBUyxXQUFXLEVBQUUsT0FBTyxFQUFFO0FBQy9ELFdBQU8sUUFBUSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLFdBQVcsRUFBRSxFQUFDLEVBQUUsRUFBRSxPQUFPLENBQUMsT0FBTyxFQUFFLE9BQU8sRUFBRSxPQUFPLENBQUMsRUFBRSxFQUFFLElBQUksRUFBRSxPQUFPLENBQUMsSUFBSSxFQUFDLENBQUMsQ0FBQztHQUN2SCxDQUFDLENBQUM7Q0FDSiIsImZpbGUiOiJpZi5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7aXNFbXB0eSwgaXNGdW5jdGlvbn0gZnJvbSAnLi4vdXRpbHMnO1xuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbihpbnN0YW5jZSkge1xuICBpbnN0YW5jZS5yZWdpc3RlckhlbHBlcignaWYnLCBmdW5jdGlvbihjb25kaXRpb25hbCwgb3B0aW9ucykge1xuICAgIGlmIChpc0Z1bmN0aW9uKGNvbmRpdGlvbmFsKSkgeyBjb25kaXRpb25hbCA9IGNvbmRpdGlvbmFsLmNhbGwodGhpcyk7IH1cblxuICAgIC8vIERlZmF1bHQgYmVoYXZpb3IgaXMgdG8gcmVuZGVyIHRoZSBwb3NpdGl2ZSBwYXRoIGlmIHRoZSB2YWx1ZSBpcyB0cnV0aHkgYW5kIG5vdCBlbXB0eS5cbiAgICAvLyBUaGUgYGluY2x1ZGVaZXJvYCBvcHRpb24gbWF5IGJlIHNldCB0byB0cmVhdCB0aGUgY29uZHRpb25hbCBhcyBwdXJlbHkgbm90IGVtcHR5IGJhc2VkIG9uIHRoZVxuICAgIC8vIGJlaGF2aW9yIG9mIGlzRW1wdHkuIEVmZmVjdGl2ZWx5IHRoaXMgZGV0ZXJtaW5lcyBpZiAwIGlzIGhhbmRsZWQgYnkgdGhlIHBvc2l0aXZlIHBhdGggb3IgbmVnYXRpdmUuXG4gICAgaWYgKCghb3B0aW9ucy5oYXNoLmluY2x1ZGVaZXJvICYmICFjb25kaXRpb25hbCkgfHwgaXNFbXB0eShjb25kaXRpb25hbCkpIHtcbiAgICAgIHJldHVybiBvcHRpb25zLmludmVyc2UodGhpcyk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBvcHRpb25zLmZuKHRoaXMpO1xuICAgIH1cbiAgfSk7XG5cbiAgaW5zdGFuY2UucmVnaXN0ZXJIZWxwZXIoJ3VubGVzcycsIGZ1bmN0aW9uKGNvbmRpdGlvbmFsLCBvcHRpb25zKSB7XG4gICAgcmV0dXJuIGluc3RhbmNlLmhlbHBlcnNbJ2lmJ10uY2FsbCh0aGlzLCBjb25kaXRpb25hbCwge2ZuOiBvcHRpb25zLmludmVyc2UsIGludmVyc2U6IG9wdGlvbnMuZm4sIGhhc2g6IG9wdGlvbnMuaGFzaH0pO1xuICB9KTtcbn1cbiJdfQ==
30 |
--------------------------------------------------------------------------------
/dist/cjs/handlebars/compiler/ast.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 | var AST = {
5 | // Public API used to evaluate derived attributes regarding AST nodes
6 | helpers: {
7 | // a mustache is definitely a helper if:
8 | // * it is an eligible helper, and
9 | // * it has at least one parameter or hash segment
10 | helperExpression: function helperExpression(node) {
11 | return node.type === 'SubExpression' || (node.type === 'MustacheStatement' || node.type === 'BlockStatement') && !!(node.params && node.params.length || node.hash);
12 | },
13 |
14 | scopedId: function scopedId(path) {
15 | return (/^\.|this\b/.test(path.original)
16 | );
17 | },
18 |
19 | // an ID is simple if it only has one part, and that part is not
20 | // `..` or `this`.
21 | simpleId: function simpleId(path) {
22 | return path.parts.length === 1 && !AST.helpers.scopedId(path) && !path.depth;
23 | }
24 | }
25 | };
26 |
27 | // Must be exported as an object rather than the root of the module as the jison lexer
28 | // must modify the object to operate properly.
29 | exports['default'] = AST;
30 | module.exports = exports['default'];
31 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2xpYi9oYW5kbGViYXJzL2NvbXBpbGVyL2FzdC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxJQUFJLEdBQUcsR0FBRzs7QUFFUixTQUFPLEVBQUU7Ozs7QUFJUCxvQkFBZ0IsRUFBRSwwQkFBUyxJQUFJLEVBQUU7QUFDL0IsYUFBTyxBQUFDLElBQUksQ0FBQyxJQUFJLEtBQUssZUFBZSxJQUM3QixDQUFDLElBQUksQ0FBQyxJQUFJLEtBQUssbUJBQW1CLElBQUksSUFBSSxDQUFDLElBQUksS0FBSyxnQkFBZ0IsQ0FBQSxJQUNuRSxDQUFDLEVBQUUsQUFBQyxJQUFJLENBQUMsTUFBTSxJQUFJLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxJQUFLLElBQUksQ0FBQyxJQUFJLENBQUEsQUFBQyxBQUFDLENBQUM7S0FDaEU7O0FBRUQsWUFBUSxFQUFFLGtCQUFTLElBQUksRUFBRTtBQUN2QixhQUFPLEFBQUMsYUFBWSxDQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDO1FBQUM7S0FDM0M7Ozs7QUFJRCxZQUFRLEVBQUUsa0JBQVMsSUFBSSxFQUFFO0FBQ3ZCLGFBQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLEtBQUssQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDO0tBQzlFO0dBQ0Y7Q0FDRixDQUFDOzs7O3FCQUthLEdBQUciLCJmaWxlIjoiYXN0LmpzIiwic291cmNlc0NvbnRlbnQiOlsibGV0IEFTVCA9IHtcbiAgLy8gUHVibGljIEFQSSB1c2VkIHRvIGV2YWx1YXRlIGRlcml2ZWQgYXR0cmlidXRlcyByZWdhcmRpbmcgQVNUIG5vZGVzXG4gIGhlbHBlcnM6IHtcbiAgICAvLyBhIG11c3RhY2hlIGlzIGRlZmluaXRlbHkgYSBoZWxwZXIgaWY6XG4gICAgLy8gKiBpdCBpcyBhbiBlbGlnaWJsZSBoZWxwZXIsIGFuZFxuICAgIC8vICogaXQgaGFzIGF0IGxlYXN0IG9uZSBwYXJhbWV0ZXIgb3IgaGFzaCBzZWdtZW50XG4gICAgaGVscGVyRXhwcmVzc2lvbjogZnVuY3Rpb24obm9kZSkge1xuICAgICAgcmV0dXJuIChub2RlLnR5cGUgPT09ICdTdWJFeHByZXNzaW9uJylcbiAgICAgICAgICB8fCAoKG5vZGUudHlwZSA9PT0gJ011c3RhY2hlU3RhdGVtZW50JyB8fCBub2RlLnR5cGUgPT09ICdCbG9ja1N0YXRlbWVudCcpXG4gICAgICAgICAgICAmJiAhISgobm9kZS5wYXJhbXMgJiYgbm9kZS5wYXJhbXMubGVuZ3RoKSB8fCBub2RlLmhhc2gpKTtcbiAgICB9LFxuXG4gICAgc2NvcGVkSWQ6IGZ1bmN0aW9uKHBhdGgpIHtcbiAgICAgIHJldHVybiAoL15cXC58dGhpc1xcYi8pLnRlc3QocGF0aC5vcmlnaW5hbCk7XG4gICAgfSxcblxuICAgIC8vIGFuIElEIGlzIHNpbXBsZSBpZiBpdCBvbmx5IGhhcyBvbmUgcGFydCwgYW5kIHRoYXQgcGFydCBpcyBub3RcbiAgICAvLyBgLi5gIG9yIGB0aGlzYC5cbiAgICBzaW1wbGVJZDogZnVuY3Rpb24ocGF0aCkge1xuICAgICAgcmV0dXJuIHBhdGgucGFydHMubGVuZ3RoID09PSAxICYmICFBU1QuaGVscGVycy5zY29wZWRJZChwYXRoKSAmJiAhcGF0aC5kZXB0aDtcbiAgICB9XG4gIH1cbn07XG5cblxuLy8gTXVzdCBiZSBleHBvcnRlZCBhcyBhbiBvYmplY3QgcmF0aGVyIHRoYW4gdGhlIHJvb3Qgb2YgdGhlIG1vZHVsZSBhcyB0aGUgamlzb24gbGV4ZXJcbi8vIG11c3QgbW9kaWZ5IHRoZSBvYmplY3QgdG8gb3BlcmF0ZSBwcm9wZXJseS5cbmV4cG9ydCBkZWZhdWx0IEFTVDtcbiJdfQ==
32 |
--------------------------------------------------------------------------------
/tasks/util/git.js:
--------------------------------------------------------------------------------
1 | var childProcess = require('child_process');
2 |
3 | module.exports = {
4 | debug: function(callback) {
5 | childProcess.exec('git remote -v', {}, function(err, remotes) {
6 | if (err) {
7 | throw new Error('git.remote: ' + err.message);
8 | }
9 |
10 | childProcess.exec('git branch -a', {}, function(err, branches) {
11 | if (err) {
12 | throw new Error('git.branch: ' + err.message);
13 | }
14 |
15 | callback(remotes, branches);
16 | });
17 | });
18 | },
19 | clean: function(callback) {
20 | childProcess.exec('git diff-index --name-only HEAD --', {}, function(err, stdout) {
21 | callback(undefined, !err && !stdout);
22 | });
23 | },
24 |
25 | commitInfo: function(callback) {
26 | module.exports.head(function(err, headSha) {
27 | module.exports.master(function(err, masterSha) {
28 | module.exports.tagName(function(err, tagName) {
29 | callback(undefined, {
30 | head: headSha,
31 | master: masterSha,
32 | tagName: tagName,
33 | isMaster: headSha === masterSha
34 | });
35 | });
36 | });
37 | });
38 | },
39 |
40 | head: function(callback) {
41 | childProcess.exec('git rev-parse --short HEAD', {}, function(err, stdout) {
42 | if (err) {
43 | throw new Error('git.head: ' + err.message);
44 | }
45 |
46 | callback(undefined, stdout.trim());
47 | });
48 | },
49 | master: function(callback) {
50 | childProcess.exec('git rev-parse --short origin/master', {}, function(err, stdout) {
51 | // This will error if master was not checked out but in this case we know we are not master
52 | // so we can ignore.
53 | if (err && !(/Needed a single revision/.test(err.message))) {
54 | throw new Error('git.master: ' + err.message);
55 | }
56 |
57 | callback(undefined, stdout.trim());
58 | });
59 | },
60 |
61 | add: function(path, callback) {
62 | childProcess.exec('git add -f ' + path, {}, function(err) {
63 | if (err) {
64 | throw new Error('git.add: ' + err.message);
65 | }
66 |
67 | callback();
68 | });
69 | },
70 | commit: function(name, callback) {
71 | childProcess.exec('git commit --message=' + name, {}, function(err) {
72 | if (err) {
73 | throw new Error('git.commit: ' + err.message);
74 | }
75 |
76 | callback();
77 | });
78 | },
79 | tag: function(name, callback) {
80 | childProcess.exec('git tag -a --message=' + name + ' ' + name, {}, function(err) {
81 | if (err) {
82 | throw new Error('git.tag: ' + err.message);
83 | }
84 |
85 | callback();
86 | });
87 | },
88 | tagName: function(callback) {
89 | childProcess.exec('git describe --tags', {}, function(err, stdout) {
90 | if (err) {
91 | throw new Error('git.tagName: ' + err.message);
92 | }
93 |
94 | var tags = stdout.trim().split(/\n/);
95 | tags = tags.filter(function(info) {
96 | info = info.split('-');
97 | return info.length == 1;
98 | });
99 |
100 | var versionTags = tags.filter(function(info) {
101 | return (/^v/.test(info[0]));
102 | });
103 |
104 | callback(undefined, versionTags[0] || tags[0]);
105 | });
106 | }
107 | };
108 |
--------------------------------------------------------------------------------
/dist/cjs/handlebars/compiler/base.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 | exports.parse = parse;
5 | // istanbul ignore next
6 |
7 | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
8 |
9 | // istanbul ignore next
10 |
11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
12 |
13 | var _parser = require('./parser');
14 |
15 | var _parser2 = _interopRequireDefault(_parser);
16 |
17 | var _whitespaceControl = require('./whitespace-control');
18 |
19 | var _whitespaceControl2 = _interopRequireDefault(_whitespaceControl);
20 |
21 | var _helpers = require('./helpers');
22 |
23 | var Helpers = _interopRequireWildcard(_helpers);
24 |
25 | var _utils = require('../utils');
26 |
27 | exports.parser = _parser2['default'];
28 |
29 | var yy = {};
30 | _utils.extend(yy, Helpers);
31 |
32 | function parse(input, options) {
33 | // Just return if an already-compiled AST was passed in.
34 | if (input.type === 'Program') {
35 | return input;
36 | }
37 |
38 | _parser2['default'].yy = yy;
39 |
40 | // Altering the shared object here, but this is ok as parser is a sync operation
41 | yy.locInfo = function (locInfo) {
42 | return new yy.SourceLocation(options && options.srcName, locInfo);
43 | };
44 |
45 | var strip = new _whitespaceControl2['default'](options);
46 | return strip.accept(_parser2['default'].parse(input));
47 | }
48 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2xpYi9oYW5kbGViYXJzL2NvbXBpbGVyL2Jhc2UuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7O3NCQUFtQixVQUFVOzs7O2lDQUNDLHNCQUFzQjs7Ozt1QkFDM0IsV0FBVzs7SUFBeEIsT0FBTzs7cUJBQ0ksVUFBVTs7UUFFeEIsTUFBTTs7QUFFZixJQUFJLEVBQUUsR0FBRyxFQUFFLENBQUM7QUFDWixjQUFPLEVBQUUsRUFBRSxPQUFPLENBQUMsQ0FBQzs7QUFFYixTQUFTLEtBQUssQ0FBQyxLQUFLLEVBQUUsT0FBTyxFQUFFOztBQUVwQyxNQUFJLEtBQUssQ0FBQyxJQUFJLEtBQUssU0FBUyxFQUFFO0FBQUUsV0FBTyxLQUFLLENBQUM7R0FBRTs7QUFFL0Msc0JBQU8sRUFBRSxHQUFHLEVBQUUsQ0FBQzs7O0FBR2YsSUFBRSxDQUFDLE9BQU8sR0FBRyxVQUFTLE9BQU8sRUFBRTtBQUM3QixXQUFPLElBQUksRUFBRSxDQUFDLGNBQWMsQ0FBQyxPQUFPLElBQUksT0FBTyxDQUFDLE9BQU8sRUFBRSxPQUFPLENBQUMsQ0FBQztHQUNuRSxDQUFDOztBQUVGLE1BQUksS0FBSyxHQUFHLG1DQUFzQixPQUFPLENBQUMsQ0FBQztBQUMzQyxTQUFPLEtBQUssQ0FBQyxNQUFNLENBQUMsb0JBQU8sS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7Q0FDMUMiLCJmaWxlIjoiYmFzZS5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBwYXJzZXIgZnJvbSAnLi9wYXJzZXInO1xuaW1wb3J0IFdoaXRlc3BhY2VDb250cm9sIGZyb20gJy4vd2hpdGVzcGFjZS1jb250cm9sJztcbmltcG9ydCAqIGFzIEhlbHBlcnMgZnJvbSAnLi9oZWxwZXJzJztcbmltcG9ydCB7IGV4dGVuZCB9IGZyb20gJy4uL3V0aWxzJztcblxuZXhwb3J0IHsgcGFyc2VyIH07XG5cbmxldCB5eSA9IHt9O1xuZXh0ZW5kKHl5LCBIZWxwZXJzKTtcblxuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlKGlucHV0LCBvcHRpb25zKSB7XG4gIC8vIEp1c3QgcmV0dXJuIGlmIGFuIGFscmVhZHktY29tcGlsZWQgQVNUIHdhcyBwYXNzZWQgaW4uXG4gIGlmIChpbnB1dC50eXBlID09PSAnUHJvZ3JhbScpIHsgcmV0dXJuIGlucHV0OyB9XG5cbiAgcGFyc2VyLnl5ID0geXk7XG5cbiAgLy8gQWx0ZXJpbmcgdGhlIHNoYXJlZCBvYmplY3QgaGVyZSwgYnV0IHRoaXMgaXMgb2sgYXMgcGFyc2VyIGlzIGEgc3luYyBvcGVyYXRpb25cbiAgeXkubG9jSW5mbyA9IGZ1bmN0aW9uKGxvY0luZm8pIHtcbiAgICByZXR1cm4gbmV3IHl5LlNvdXJjZUxvY2F0aW9uKG9wdGlvbnMgJiYgb3B0aW9ucy5zcmNOYW1lLCBsb2NJbmZvKTtcbiAgfTtcblxuICBsZXQgc3RyaXAgPSBuZXcgV2hpdGVzcGFjZUNvbnRyb2wob3B0aW9ucyk7XG4gIHJldHVybiBzdHJpcC5hY2NlcHQocGFyc2VyLnBhcnNlKGlucHV0KSk7XG59XG4iXX0=
49 |
--------------------------------------------------------------------------------
/spec/umd.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Mocha
4 |
5 |
6 |
7 |
8 |
14 |
15 |
21 |
22 |
25 |
26 |
27 |
28 |
29 |
30 |
59 |
105 |
106 |
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/spec/compiler.js:
--------------------------------------------------------------------------------
1 | describe('compiler', function() {
2 | if (!Handlebars.compile) {
3 | return;
4 | }
5 |
6 | describe('#equals', function() {
7 | function compile(string) {
8 | var ast = Handlebars.parse(string);
9 | return new Handlebars.Compiler().compile(ast, {});
10 | }
11 |
12 | it('should treat as equal', function() {
13 | equal(compile('foo').equals(compile('foo')), true);
14 | equal(compile('{{foo}}').equals(compile('{{foo}}')), true);
15 | equal(compile('{{foo.bar}}').equals(compile('{{foo.bar}}')), true);
16 | equal(compile('{{foo.bar baz "foo" true false bat=1}}').equals(compile('{{foo.bar baz "foo" true false bat=1}}')), true);
17 | equal(compile('{{foo.bar (baz bat=1)}}').equals(compile('{{foo.bar (baz bat=1)}}')), true);
18 | equal(compile('{{#foo}} {{/foo}}').equals(compile('{{#foo}} {{/foo}}')), true);
19 | });
20 | it('should treat as not equal', function() {
21 | equal(compile('foo').equals(compile('bar')), false);
22 | equal(compile('{{foo}}').equals(compile('{{bar}}')), false);
23 | equal(compile('{{foo.bar}}').equals(compile('{{bar.bar}}')), false);
24 | equal(compile('{{foo.bar baz bat=1}}').equals(compile('{{foo.bar bar bat=1}}')), false);
25 | equal(compile('{{foo.bar (baz bat=1)}}').equals(compile('{{foo.bar (bar bat=1)}}')), false);
26 | equal(compile('{{#foo}} {{/foo}}').equals(compile('{{#bar}} {{/bar}}')), false);
27 | equal(compile('{{#foo}} {{/foo}}').equals(compile('{{#foo}} {{foo}}{{/foo}}')), false);
28 | });
29 | });
30 |
31 | describe('#compile', function() {
32 | it('should fail with invalid input', function() {
33 | shouldThrow(function() {
34 | Handlebars.compile(null);
35 | }, Error, 'You must pass a string or Handlebars AST to Handlebars.compile. You passed null');
36 | shouldThrow(function() {
37 | Handlebars.compile({});
38 | }, Error, 'You must pass a string or Handlebars AST to Handlebars.compile. You passed [object Object]');
39 | });
40 |
41 | it('can utilize AST instance', function() {
42 | equal(Handlebars.compile({
43 | type: 'Program',
44 | body: [ {type: 'ContentStatement', value: 'Hello'}]
45 | })(), 'Hello');
46 | });
47 |
48 | it('can pass through an empty string', function() {
49 | equal(Handlebars.compile('')(), '');
50 | });
51 |
52 | it('throws on desupported options', function() {
53 | shouldThrow(function() {
54 | Handlebars.compile('Dudes', {trackIds: true});
55 | }, Error, 'TrackIds and stringParams are no longer supported. See Github #1145');
56 | shouldThrow(function() {
57 | Handlebars.compile('Dudes', {stringParams: true});
58 | }, Error, 'TrackIds and stringParams are no longer supported. See Github #1145');
59 | });
60 | });
61 |
62 | describe('#precompile', function() {
63 | it('should fail with invalid input', function() {
64 | shouldThrow(function() {
65 | Handlebars.precompile(null);
66 | }, Error, 'You must pass a string or Handlebars AST to Handlebars.compile. You passed null');
67 | shouldThrow(function() {
68 | Handlebars.precompile({});
69 | }, Error, 'You must pass a string or Handlebars AST to Handlebars.compile. You passed [object Object]');
70 | });
71 |
72 | it('can utilize AST instance', function() {
73 | equal(/return "Hello"/.test(Handlebars.precompile({
74 | type: 'Program',
75 | body: [ {type: 'ContentStatement', value: 'Hello'}]
76 | })), true);
77 | });
78 |
79 | it('can pass through an empty string', function() {
80 | equal(/return ""/.test(Handlebars.precompile('')), true);
81 | });
82 | });
83 | });
84 |
--------------------------------------------------------------------------------
/spec/whitespace-control.js:
--------------------------------------------------------------------------------
1 | describe('whitespace control', function() {
2 | it('should strip whitespace around mustache calls', function() {
3 | var hash = {foo: 'bar<'};
4 |
5 | shouldCompileTo(' {{~foo~}} ', hash, 'bar<');
6 | shouldCompileTo(' {{~foo}} ', hash, 'bar< ');
7 | shouldCompileTo(' {{foo~}} ', hash, ' bar<');
8 |
9 | shouldCompileTo(' {{~&foo~}} ', hash, 'bar<');
10 | shouldCompileTo(' {{~{foo}~}} ', hash, 'bar<');
11 |
12 | shouldCompileTo('1\n{{foo~}} \n\n 23\n{{bar}}4', {}, '1\n23\n4');
13 | });
14 |
15 | describe('blocks', function() {
16 | it('should strip whitespace around simple block calls', function() {
17 | var hash = {foo: 'bar<'};
18 |
19 | shouldCompileTo(' {{~#if foo~}} bar {{~/if~}} ', hash, 'bar');
20 | shouldCompileTo(' {{#if foo~}} bar {{/if~}} ', hash, ' bar ');
21 | shouldCompileTo(' {{~#if foo}} bar {{~/if}} ', hash, ' bar ');
22 | shouldCompileTo(' {{#if foo}} bar {{/if}} ', hash, ' bar ');
23 |
24 | shouldCompileTo(' \n\n{{~#if foo~}} \n\nbar \n\n{{~/if~}}\n\n ', hash, 'bar');
25 | shouldCompileTo(' a\n\n{{~#if foo~}} \n\nbar \n\n{{~/if~}}\n\na ', hash, ' abara ');
26 | });
27 | it('should strip whitespace around inverse block calls', function() {
28 | var hash = {};
29 |
30 | shouldCompileTo(' {{~^if foo~}} bar {{~/if~}} ', hash, 'bar');
31 | shouldCompileTo(' {{^if foo~}} bar {{/if~}} ', hash, ' bar ');
32 | shouldCompileTo(' {{~^if foo}} bar {{~/if}} ', hash, ' bar ');
33 | shouldCompileTo(' {{^if foo}} bar {{/if}} ', hash, ' bar ');
34 |
35 | shouldCompileTo(' \n\n{{~^if foo~}} \n\nbar \n\n{{~/if~}}\n\n ', hash, 'bar');
36 | });
37 | it('should strip whitespace around complex block calls', function() {
38 | var hash = {foo: 'bar<'};
39 |
40 | shouldCompileTo('{{#if foo~}} bar {{~^~}} baz {{~/if}}', hash, 'bar');
41 | shouldCompileTo('{{#if foo~}} bar {{^~}} baz {{/if}}', hash, 'bar ');
42 | shouldCompileTo('{{#if foo}} bar {{~^~}} baz {{~/if}}', hash, ' bar');
43 | shouldCompileTo('{{#if foo}} bar {{^~}} baz {{/if}}', hash, ' bar ');
44 |
45 | shouldCompileTo('{{#if foo~}} bar {{~else~}} baz {{~/if}}', hash, 'bar');
46 |
47 | shouldCompileTo('\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n', hash, 'bar');
48 | shouldCompileTo('\n\n{{~#if foo~}} \n\n{{{foo}}} \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n', hash, 'bar<');
49 |
50 | hash = {};
51 |
52 | shouldCompileTo('{{#if foo~}} bar {{~^~}} baz {{~/if}}', hash, 'baz');
53 | shouldCompileTo('{{#if foo}} bar {{~^~}} baz {{/if}}', hash, 'baz ');
54 | shouldCompileTo('{{#if foo~}} bar {{~^}} baz {{~/if}}', hash, ' baz');
55 | shouldCompileTo('{{#if foo~}} bar {{~^}} baz {{/if}}', hash, ' baz ');
56 |
57 | shouldCompileTo('{{#if foo~}} bar {{~else~}} baz {{~/if}}', hash, 'baz');
58 |
59 | shouldCompileTo('\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n', hash, 'baz');
60 | });
61 | });
62 |
63 | it('should strip whitespace around partials', function() {
64 | shouldCompileToWithPartials('foo {{~> dude~}} ', [{}, {}, {dude: 'bar'}], true, 'foobar');
65 | shouldCompileToWithPartials('foo {{> dude~}} ', [{}, {}, {dude: 'bar'}], true, 'foo bar');
66 | shouldCompileToWithPartials('foo {{> dude}} ', [{}, {}, {dude: 'bar'}], true, 'foo bar ');
67 |
68 | shouldCompileToWithPartials('foo\n {{~> dude}} ', [{}, {}, {dude: 'bar'}], true, 'foobar');
69 | shouldCompileToWithPartials('foo\n {{> dude}} ', [{}, {}, {dude: 'bar'}], true, 'foo\n bar');
70 | });
71 |
72 | it('should only strip whitespace once', function() {
73 | var hash = {foo: 'bar'};
74 |
75 | shouldCompileTo(' {{~foo~}} {{foo}} {{foo}} ', hash, 'barbar bar ');
76 | });
77 | });
78 |
--------------------------------------------------------------------------------
/bench/throughput.js:
--------------------------------------------------------------------------------
1 | var _ = require('underscore'),
2 | runner = require('./util/template-runner'),
3 |
4 | eco, dust, Handlebars, Mustache;
5 |
6 | try {
7 | dust = require('dustjs-linkedin');
8 | } catch (err) { /* NOP */ }
9 |
10 | try {
11 | Mustache = require('mustache');
12 | } catch (err) { /* NOP */ }
13 |
14 | try {
15 | eco = require('eco');
16 | } catch (err) { /* NOP */ }
17 |
18 | function error() {
19 | throw new Error('EWOT');
20 | }
21 |
22 | function makeSuite(bench, name, template, handlebarsOnly) {
23 | // Create aliases to minimize any impact from having to walk up the closure tree.
24 | var templateName = name,
25 |
26 | context = template.context,
27 | partials = template.partials,
28 |
29 | handlebarsOut,
30 | compatOut,
31 | dustOut,
32 | ecoOut,
33 | mustacheOut;
34 |
35 | var handlebar = Handlebars.compile(template.handlebars, {data: false}),
36 | compat = Handlebars.compile(template.handlebars, {data: false, compat: true}),
37 | options = {helpers: template.helpers};
38 | _.each(template.partials && template.partials.handlebars, function(partial, partialName) {
39 | Handlebars.registerPartial(partialName, Handlebars.compile(partial, {data: false}));
40 | });
41 |
42 | handlebarsOut = handlebar(context, options);
43 | bench('handlebars', function() {
44 | handlebar(context, options);
45 | });
46 |
47 | compatOut = compat(context, options);
48 | bench('compat', function() {
49 | compat(context, options);
50 | });
51 |
52 | if (handlebarsOnly) {
53 | return;
54 | }
55 |
56 | if (dust) {
57 | if (template.dust) {
58 | dustOut = false;
59 | dust.loadSource(dust.compile(template.dust, templateName));
60 |
61 | dust.render(templateName, context, function(err, out) { dustOut = out; });
62 |
63 | bench('dust', function() {
64 | dust.render(templateName, context, function() {});
65 | });
66 | } else {
67 | bench('dust', error);
68 | }
69 | }
70 |
71 | if (eco) {
72 | if (template.eco) {
73 | var ecoTemplate = eco.compile(template.eco);
74 |
75 | ecoOut = ecoTemplate(context);
76 |
77 | bench('eco', function() {
78 | ecoTemplate(context);
79 | });
80 | } else {
81 | bench('eco', error);
82 | }
83 | }
84 |
85 | if (Mustache) {
86 | var mustacheSource = template.mustache,
87 | mustachePartials = partials && partials.mustache;
88 |
89 | if (mustacheSource) {
90 | mustacheOut = Mustache.to_html(mustacheSource, context, mustachePartials);
91 |
92 | bench('mustache', function() {
93 | Mustache.to_html(mustacheSource, context, mustachePartials);
94 | });
95 | } else {
96 | bench('mustache', error);
97 | }
98 | }
99 |
100 | // Hack around whitespace until we have whitespace control
101 | handlebarsOut = handlebarsOut.replace(/\s/g, '');
102 | function compare(b, lang) {
103 | if (b == null) {
104 | return;
105 | }
106 |
107 | b = b.replace(/\s/g, '');
108 |
109 | if (handlebarsOut !== b) {
110 | throw new Error('Template output mismatch: ' + name
111 | + '\n\nHandlebars: ' + handlebarsOut
112 | + '\n\n' + lang + ': ' + b);
113 | }
114 | }
115 |
116 | compare(compatOut, 'compat');
117 | compare(dustOut, 'dust');
118 | compare(ecoOut, 'eco');
119 | compare(mustacheOut, 'mustache');
120 | }
121 |
122 | module.exports = function(grunt, callback) {
123 | // Deferring load incase we are being run inline with the grunt build
124 | Handlebars = require('../lib');
125 |
126 | console.log('Execution Throughput');
127 | runner(grunt, makeSuite, function(times, scaled) {
128 | callback(scaled);
129 | });
130 | };
131 |
--------------------------------------------------------------------------------
/bin/handlebars:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var yargs = require('yargs')
4 | .usage('Precompile handlebar templates.\nUsage: $0 [template|directory]...', {
5 | 'f': {
6 | 'type': 'string',
7 | 'description': 'Output File',
8 | 'alias': 'output'
9 | },
10 | 'map': {
11 | 'type': 'string',
12 | 'description': 'Source Map File'
13 | },
14 | 'a': {
15 | 'type': 'boolean',
16 | 'description': 'Exports amd style (require.js)',
17 | 'alias': 'amd'
18 | },
19 | 'c': {
20 | 'type': 'string',
21 | 'description': 'Exports CommonJS style, path to Handlebars module',
22 | 'alias': 'commonjs',
23 | 'default': null
24 | },
25 | 'h': {
26 | 'type': 'string',
27 | 'description': 'Path to handlebar.js (only valid for amd-style)',
28 | 'alias': 'handlebarPath',
29 | 'default': ''
30 | },
31 | 'k': {
32 | 'type': 'string',
33 | 'description': 'Known helpers',
34 | 'alias': 'known'
35 | },
36 | 'o': {
37 | 'type': 'boolean',
38 | 'description': 'Known helpers only',
39 | 'alias': 'knownOnly'
40 | },
41 | 'm': {
42 | 'type': 'boolean',
43 | 'description': 'Minimize output',
44 | 'alias': 'min'
45 | },
46 | 'n': {
47 | 'type': 'string',
48 | 'description': 'Template namespace',
49 | 'alias': 'namespace',
50 | 'default': 'Handlebars.templates'
51 | },
52 | 's': {
53 | 'type': 'boolean',
54 | 'description': 'Output template function only.',
55 | 'alias': 'simple'
56 | },
57 | 'N': {
58 | 'type': 'string',
59 | 'description': 'Name of passed string templates. Optional if running in a simple mode. Required when operating on multiple templates.',
60 | 'alias': 'name'
61 | },
62 | 'i': {
63 | 'type': 'string',
64 | 'description': 'Generates a template from the passed CLI argument.\n"-" is treated as a special value and causes stdin to be read for the template value.',
65 | 'alias': 'string'
66 | },
67 | 'r': {
68 | 'type': 'string',
69 | 'description': 'Template root. Base value that will be stripped from template names.',
70 | 'alias': 'root'
71 | },
72 | 'p': {
73 | 'type': 'boolean',
74 | 'description': 'Compiling a partial template',
75 | 'alias': 'partial'
76 | },
77 | 'd': {
78 | 'type': 'boolean',
79 | 'description': 'Include data when compiling',
80 | 'alias': 'data'
81 | },
82 | 'e': {
83 | 'type': 'string',
84 | 'description': 'Template extension.',
85 | 'alias': 'extension',
86 | 'default': 'handlebars'
87 | },
88 | 'b': {
89 | 'type': 'boolean',
90 | 'description': 'Removes the BOM (Byte Order Mark) from the beginning of the templates.',
91 | 'alias': 'bom'
92 | },
93 | 'v': {
94 | 'type': 'boolean',
95 | 'description': 'Prints the current compiler version',
96 | 'alias': 'version'
97 | },
98 |
99 | 'help': {
100 | 'type': 'boolean',
101 | 'description': 'Outputs this message'
102 | }
103 | })
104 |
105 | .wrap(120);
106 |
107 |
108 | var argv = yargs.argv;
109 | argv.files = argv._;
110 | delete argv._;
111 |
112 | var Precompiler = require('../dist/cjs/precompiler');
113 | Precompiler.loadTemplates(argv, function(err, opts) {
114 | if (err) {
115 | throw err;
116 | }
117 |
118 | if (opts.help || (!opts.templates.length && !opts.version)) {
119 | yargs.showHelp();
120 | } else {
121 | Precompiler.cli(opts);
122 | }
123 | });
124 |
--------------------------------------------------------------------------------
/lib/handlebars/compiler/visitor.js:
--------------------------------------------------------------------------------
1 | import Exception from '../exception';
2 |
3 | function Visitor() {
4 | this.parents = [];
5 | }
6 |
7 | Visitor.prototype = {
8 | constructor: Visitor,
9 | mutating: false,
10 |
11 | // Visits a given value. If mutating, will replace the value if necessary.
12 | acceptKey: function(node, name) {
13 | let value = this.accept(node[name]);
14 | if (this.mutating) {
15 | // Hacky sanity check: This may have a few false positives for type for the helper
16 | // methods but will generally do the right thing without a lot of overhead.
17 | if (value && !Visitor.prototype[value.type]) {
18 | throw new Exception('Unexpected node type "' + value.type + '" found when accepting ' + name + ' on ' + node.type);
19 | }
20 | node[name] = value;
21 | }
22 | },
23 |
24 | // Performs an accept operation with added sanity check to ensure
25 | // required keys are not removed.
26 | acceptRequired: function(node, name) {
27 | this.acceptKey(node, name);
28 |
29 | if (!node[name]) {
30 | throw new Exception(node.type + ' requires ' + name);
31 | }
32 | },
33 |
34 | // Traverses a given array. If mutating, empty respnses will be removed
35 | // for child elements.
36 | acceptArray: function(array) {
37 | for (let i = 0, l = array.length; i < l; i++) {
38 | this.acceptKey(array, i);
39 |
40 | if (!array[i]) {
41 | array.splice(i, 1);
42 | i--;
43 | l--;
44 | }
45 | }
46 | },
47 |
48 | accept: function(object) {
49 | if (!object) {
50 | return;
51 | }
52 |
53 | /* istanbul ignore next: Sanity code */
54 | if (!this[object.type]) {
55 | throw new Exception('Unknown type: ' + object.type, object);
56 | }
57 |
58 | if (this.current) {
59 | this.parents.unshift(this.current);
60 | }
61 | this.current = object;
62 |
63 | let ret = this[object.type](object);
64 |
65 | this.current = this.parents.shift();
66 |
67 | if (!this.mutating || ret) {
68 | return ret;
69 | } else if (ret !== false) {
70 | return object;
71 | }
72 | },
73 |
74 | Program: function(program) {
75 | this.acceptArray(program.body);
76 | },
77 |
78 | MustacheStatement: visitSubExpression,
79 | Decorator: visitSubExpression,
80 |
81 | BlockStatement: visitBlock,
82 | DecoratorBlock: visitBlock,
83 |
84 | PartialStatement: visitPartial,
85 | PartialBlockStatement: function(partial) {
86 | visitPartial.call(this, partial);
87 |
88 | this.acceptKey(partial, 'program');
89 | },
90 |
91 | ContentStatement: function(/* content */) {},
92 | CommentStatement: function(/* comment */) {},
93 |
94 | SubExpression: visitSubExpression,
95 |
96 | PathExpression: function(/* path */) {},
97 |
98 | StringLiteral: function(/* string */) {},
99 | NumberLiteral: function(/* number */) {},
100 | BooleanLiteral: function(/* bool */) {},
101 | UndefinedLiteral: function(/* literal */) {},
102 | NullLiteral: function(/* literal */) {},
103 |
104 | Hash: function(hash) {
105 | this.acceptArray(hash.pairs);
106 | },
107 | HashPair: function(pair) {
108 | this.acceptRequired(pair, 'value');
109 | }
110 | };
111 |
112 | function visitSubExpression(mustache) {
113 | this.acceptRequired(mustache, 'path');
114 | this.acceptArray(mustache.params);
115 | this.acceptKey(mustache, 'hash');
116 | }
117 | function visitBlock(block) {
118 | visitSubExpression.call(this, block);
119 |
120 | this.acceptKey(block, 'program');
121 | this.acceptKey(block, 'inverse');
122 | }
123 | function visitPartial(partial) {
124 | this.acceptRequired(partial, 'name');
125 | this.acceptArray(partial.params);
126 | this.acceptKey(partial, 'hash');
127 | }
128 |
129 | export default Visitor;
130 |
--------------------------------------------------------------------------------
/dist/cjs/handlebars/logger.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 |
5 | var _utils = require('./utils');
6 |
7 | var logger = {
8 | methodMap: ['debug', 'info', 'warn', 'error'],
9 | level: 'info',
10 |
11 | // Maps a given level value to the `methodMap` indexes above.
12 | lookupLevel: function lookupLevel(level) {
13 | if (typeof level === 'string') {
14 | var levelMap = _utils.indexOf(logger.methodMap, level.toLowerCase());
15 | if (levelMap >= 0) {
16 | level = levelMap;
17 | } else {
18 | level = parseInt(level, 10);
19 | }
20 | }
21 |
22 | return level;
23 | },
24 |
25 | // Can be overridden in the host environment
26 | log: function log(level) {
27 | level = logger.lookupLevel(level);
28 |
29 | if (typeof console !== 'undefined' && logger.lookupLevel(logger.level) <= level) {
30 | var method = logger.methodMap[level];
31 | if (!console[method]) {
32 | // eslint-disable-line no-console
33 | method = 'log';
34 | }
35 |
36 | for (var _len = arguments.length, message = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
37 | message[_key - 1] = arguments[_key];
38 | }
39 |
40 | console[method].apply(console, message); // eslint-disable-line no-console
41 | }
42 | }
43 | };
44 |
45 | exports['default'] = logger;
46 | module.exports = exports['default'];
47 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL2xpYi9oYW5kbGViYXJzL2xvZ2dlci5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7O3FCQUFzQixTQUFTOztBQUUvQixJQUFJLE1BQU0sR0FBRztBQUNYLFdBQVMsRUFBRSxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLE9BQU8sQ0FBQztBQUM3QyxPQUFLLEVBQUUsTUFBTTs7O0FBR2IsYUFBVyxFQUFFLHFCQUFTLEtBQUssRUFBRTtBQUMzQixRQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsRUFBRTtBQUM3QixVQUFJLFFBQVEsR0FBRyxlQUFRLE1BQU0sQ0FBQyxTQUFTLEVBQUUsS0FBSyxDQUFDLFdBQVcsRUFBRSxDQUFDLENBQUM7QUFDOUQsVUFBSSxRQUFRLElBQUksQ0FBQyxFQUFFO0FBQ2pCLGFBQUssR0FBRyxRQUFRLENBQUM7T0FDbEIsTUFBTTtBQUNMLGFBQUssR0FBRyxRQUFRLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO09BQzdCO0tBQ0Y7O0FBRUQsV0FBTyxLQUFLLENBQUM7R0FDZDs7O0FBR0QsS0FBRyxFQUFFLGFBQVMsS0FBSyxFQUFjO0FBQy9CLFNBQUssR0FBRyxNQUFNLENBQUMsV0FBVyxDQUFDLEtBQUssQ0FBQyxDQUFDOztBQUVsQyxRQUFJLE9BQU8sT0FBTyxLQUFLLFdBQVcsSUFBSSxNQUFNLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsSUFBSSxLQUFLLEVBQUU7QUFDL0UsVUFBSSxNQUFNLEdBQUcsTUFBTSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQztBQUNyQyxVQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxFQUFFOztBQUNwQixjQUFNLEdBQUcsS0FBSyxDQUFDO09BQ2hCOzt3Q0FQbUIsT0FBTztBQUFQLGVBQU87OztBQVEzQixhQUFPLENBQUMsTUFBTSxPQUFDLENBQWYsT0FBTyxFQUFZLE9BQU8sQ0FBQyxDQUFDO0tBQzdCO0dBQ0Y7Q0FDRixDQUFDOztxQkFFYSxNQUFNIiwiZmlsZSI6ImxvZ2dlci5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7aW5kZXhPZn0gZnJvbSAnLi91dGlscyc7XG5cbmxldCBsb2dnZXIgPSB7XG4gIG1ldGhvZE1hcDogWydkZWJ1ZycsICdpbmZvJywgJ3dhcm4nLCAnZXJyb3InXSxcbiAgbGV2ZWw6ICdpbmZvJyxcblxuICAvLyBNYXBzIGEgZ2l2ZW4gbGV2ZWwgdmFsdWUgdG8gdGhlIGBtZXRob2RNYXBgIGluZGV4ZXMgYWJvdmUuXG4gIGxvb2t1cExldmVsOiBmdW5jdGlvbihsZXZlbCkge1xuICAgIGlmICh0eXBlb2YgbGV2ZWwgPT09ICdzdHJpbmcnKSB7XG4gICAgICBsZXQgbGV2ZWxNYXAgPSBpbmRleE9mKGxvZ2dlci5tZXRob2RNYXAsIGxldmVsLnRvTG93ZXJDYXNlKCkpO1xuICAgICAgaWYgKGxldmVsTWFwID49IDApIHtcbiAgICAgICAgbGV2ZWwgPSBsZXZlbE1hcDtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGxldmVsID0gcGFyc2VJbnQobGV2ZWwsIDEwKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gbGV2ZWw7XG4gIH0sXG5cbiAgLy8gQ2FuIGJlIG92ZXJyaWRkZW4gaW4gdGhlIGhvc3QgZW52aXJvbm1lbnRcbiAgbG9nOiBmdW5jdGlvbihsZXZlbCwgLi4ubWVzc2FnZSkge1xuICAgIGxldmVsID0gbG9nZ2VyLmxvb2t1cExldmVsKGxldmVsKTtcblxuICAgIGlmICh0eXBlb2YgY29uc29sZSAhPT0gJ3VuZGVmaW5lZCcgJiYgbG9nZ2VyLmxvb2t1cExldmVsKGxvZ2dlci5sZXZlbCkgPD0gbGV2ZWwpIHtcbiAgICAgIGxldCBtZXRob2QgPSBsb2dnZXIubWV0aG9kTWFwW2xldmVsXTtcbiAgICAgIGlmICghY29uc29sZVttZXRob2RdKSB7ICAgLy8gZXNsaW50LWRpc2FibGUtbGluZSBuby1jb25zb2xlXG4gICAgICAgIG1ldGhvZCA9ICdsb2cnO1xuICAgICAgfVxuICAgICAgY29uc29sZVttZXRob2RdKC4uLm1lc3NhZ2UpOyAgICAvLyBlc2xpbnQtZGlzYWJsZS1saW5lIG5vLWNvbnNvbGVcbiAgICB9XG4gIH1cbn07XG5cbmV4cG9ydCBkZWZhdWx0IGxvZ2dlcjtcbiJdfQ==
48 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | ## Reporting Issues
4 |
5 | Please see our [FAQ](https://github.com/wycats/handlebars.js/blob/master/FAQ.md) for common issues that people run into.
6 |
7 | Should you run into other issues with the project, please don't hesitate to let us know by filing an [issue][issue]! In general we are going to ask for an example of the problem failing, which can be as simple as a jsfiddle/jsbin/etc. We've put together a jsfiddle [template][jsfiddle] to ease this. (We will keep this link up to date as new releases occur, so feel free to check back here)
8 |
9 | Pull requests containing only failing tests demonstrating the issue are welcomed and this also helps ensure that your issue won't regress in the future once it's fixed.
10 |
11 | Documentation issues on the handlebarsjs.com site should be reported on [handlebars-site](https://github.com/wycats/handlebars-site).
12 |
13 | ## Pull Requests
14 |
15 | We also accept [pull requests][pull-request]!
16 |
17 | Generally we like to see pull requests that
18 | - Maintain the existing code style
19 | - Are focused on a single change (i.e. avoid large refactoring or style adjustments in untouched code if not the primary goal of the pull request)
20 | - Have [good commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
21 | - Have tests
22 | - Don't significantly decrease the current code coverage (see coverage/lcov-report/index.html)
23 |
24 | ## Building
25 |
26 | To build Handlebars.js you'll need a few things installed.
27 |
28 | * Node.js
29 | * [Grunt](http://gruntjs.com/getting-started)
30 |
31 | Before building, you need to make sure that the Git submodule `spec/mustache` is included (i.e. the directory `spec/mustache` should not be empty). To include it, if using Git version 1.6.5 or newer, use `git clone --recursive` rather than `git clone`. Or, if you already cloned without `--recursive`, use `git submodule update --init`.
32 |
33 | Project dependencies may be installed via `npm install`.
34 |
35 | To build Handlebars.js from scratch, you'll want to run `grunt`
36 | in the root of the project. That will build Handlebars and output the
37 | results to the dist/ folder. To re-run tests, run `grunt test` or `npm test`.
38 | You can also run our set of benchmarks with `grunt bench`.
39 |
40 | The `grunt dev` implements watching for tests and allows for in browser testing at `http://localhost:9999/spec/`.
41 |
42 | If you notice any problems, please report them to the GitHub issue tracker at
43 | [http://github.com/wycats/handlebars.js/issues](http://github.com/wycats/handlebars.js/issues).
44 |
45 | ##Running Tests
46 |
47 | To run tests locally, first install all dependencies.
48 | ```sh
49 | npm install
50 | ```
51 |
52 | Clone the mustache specs into the spec/mustache folder.
53 | ```sh
54 | cd spec
55 | rm -r mustache
56 | git clone https://github.com/mustache/spec.git mustache
57 | ```
58 |
59 | From the root directory, run the tests.
60 | ```sh
61 | npm test
62 | ```
63 |
64 | ## Ember testing
65 |
66 | The current ember distribution should be tested as part of the handlebars release process. This requires building the `handlebars-source` gem locally and then executing the ember test script.
67 |
68 | ```sh
69 | npm link
70 | grunt build release
71 | cp dist/*.js $emberRepoDir/bower_components/handlebars/
72 |
73 | cd $emberRepoDir
74 | npm link handlebars
75 | npm test
76 | ```
77 |
78 | ## Releasing
79 |
80 | Handlebars utilizes the [release yeoman generator][generator-release] to perform most release tasks.
81 |
82 | A full release may be completed with the following:
83 |
84 | ```
85 | yo release
86 | npm publish
87 | yo release:publish components handlebars.js dist/components/
88 |
89 | cd dist/components/
90 | gem build handlebars-source.gemspec
91 | gem push handlebars-source-*.gem
92 | ```
93 |
94 | After this point the handlebars site needs to be updated to point to the new version numbers. The jsfiddle link should be updated to point to the most recent distribution for all instances in our documentation.
95 |
96 | [generator-release]: https://github.com/walmartlabs/generator-release
97 | [pull-request]: https://github.com/wycats/handlebars.js/pull/new/master
98 | [issue]: https://github.com/wycats/handlebars.js/issues/new
99 | [jsfiddle]: https://jsfiddle.net/9D88g/47/
100 |
--------------------------------------------------------------------------------
/dist/cjs/handlebars/exception.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 |
5 | var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
6 |
7 | function Exception(message, node) {
8 | var loc = node && node.loc,
9 | line = undefined,
10 | column = undefined;
11 | if (loc) {
12 | line = loc.start.line;
13 | column = loc.start.column;
14 |
15 | message += ' - ' + line + ':' + column;
16 | }
17 |
18 | var tmp = Error.prototype.constructor.call(this, message);
19 |
20 | // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
21 | for (var idx = 0; idx < errorProps.length; idx++) {
22 | this[errorProps[idx]] = tmp[errorProps[idx]];
23 | }
24 |
25 | /* istanbul ignore else */
26 | if (Error.captureStackTrace) {
27 | Error.captureStackTrace(this, Exception);
28 | }
29 |
30 | try {
31 | if (loc) {
32 | this.lineNumber = line;
33 |
34 | // Work around issue under safari where we can't directly set the column value
35 | /* istanbul ignore next */
36 | if (Object.defineProperty) {
37 | Object.defineProperty(this, 'column', { value: column });
38 | } else {
39 | this.column = column;
40 | }
41 | }
42 | } catch (nop) {
43 | /* Ignore if the browser is very particular */
44 | }
45 | }
46 |
47 | Exception.prototype = new Error();
48 |
49 | exports['default'] = Exception;
50 | module.exports = exports['default'];
51 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL2xpYi9oYW5kbGViYXJzL2V4Y2VwdGlvbi5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7O0FBQ0EsSUFBTSxVQUFVLEdBQUcsQ0FBQyxhQUFhLEVBQUUsVUFBVSxFQUFFLFlBQVksRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLFFBQVEsRUFBRSxPQUFPLENBQUMsQ0FBQzs7QUFFbkcsU0FBUyxTQUFTLENBQUMsT0FBTyxFQUFFLElBQUksRUFBRTtBQUNoQyxNQUFJLEdBQUcsR0FBRyxJQUFJLElBQUksSUFBSSxDQUFDLEdBQUc7TUFDdEIsSUFBSSxZQUFBO01BQ0osTUFBTSxZQUFBLENBQUM7QUFDWCxNQUFJLEdBQUcsRUFBRTtBQUNQLFFBQUksR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQztBQUN0QixVQUFNLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUM7O0FBRTFCLFdBQU8sSUFBSSxLQUFLLEdBQUcsSUFBSSxHQUFHLEdBQUcsR0FBRyxNQUFNLENBQUM7R0FDeEM7O0FBRUQsTUFBSSxHQUFHLEdBQUcsS0FBSyxDQUFDLFNBQVMsQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxPQUFPLENBQUMsQ0FBQzs7O0FBRzFELE9BQUssSUFBSSxHQUFHLEdBQUcsQ0FBQyxFQUFFLEdBQUcsR0FBRyxVQUFVLENBQUMsTUFBTSxFQUFFLEdBQUcsRUFBRSxFQUFFO0FBQ2hELFFBQUksQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxHQUFHLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7R0FDOUM7OztBQUdELE1BQUksS0FBSyxDQUFDLGlCQUFpQixFQUFFO0FBQzNCLFNBQUssQ0FBQyxpQkFBaUIsQ0FBQyxJQUFJLEVBQUUsU0FBUyxDQUFDLENBQUM7R0FDMUM7O0FBRUQsTUFBSTtBQUNGLFFBQUksR0FBRyxFQUFFO0FBQ1AsVUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUM7Ozs7QUFJdkIsVUFBSSxNQUFNLENBQUMsY0FBYyxFQUFFO0FBQ3pCLGNBQU0sQ0FBQyxjQUFjLENBQUMsSUFBSSxFQUFFLFFBQVEsRUFBRSxFQUFDLEtBQUssRUFBRSxNQUFNLEVBQUMsQ0FBQyxDQUFDO09BQ3hELE1BQU07QUFDTCxZQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQztPQUN0QjtLQUNGO0dBQ0YsQ0FBQyxPQUFPLEdBQUcsRUFBRTs7R0FFYjtDQUNGOztBQUVELFNBQVMsQ0FBQyxTQUFTLEdBQUcsSUFBSSxLQUFLLEVBQUUsQ0FBQzs7cUJBRW5CLFNBQVMiLCJmaWxlIjoiZXhjZXB0aW9uLmpzIiwic291cmNlc0NvbnRlbnQiOlsiXG5jb25zdCBlcnJvclByb3BzID0gWydkZXNjcmlwdGlvbicsICdmaWxlTmFtZScsICdsaW5lTnVtYmVyJywgJ21lc3NhZ2UnLCAnbmFtZScsICdudW1iZXInLCAnc3RhY2snXTtcblxuZnVuY3Rpb24gRXhjZXB0aW9uKG1lc3NhZ2UsIG5vZGUpIHtcbiAgbGV0IGxvYyA9IG5vZGUgJiYgbm9kZS5sb2MsXG4gICAgICBsaW5lLFxuICAgICAgY29sdW1uO1xuICBpZiAobG9jKSB7XG4gICAgbGluZSA9IGxvYy5zdGFydC5saW5lO1xuICAgIGNvbHVtbiA9IGxvYy5zdGFydC5jb2x1bW47XG5cbiAgICBtZXNzYWdlICs9ICcgLSAnICsgbGluZSArICc6JyArIGNvbHVtbjtcbiAgfVxuXG4gIGxldCB0bXAgPSBFcnJvci5wcm90b3R5cGUuY29uc3RydWN0b3IuY2FsbCh0aGlzLCBtZXNzYWdlKTtcblxuICAvLyBVbmZvcnR1bmF0ZWx5IGVycm9ycyBhcmUgbm90IGVudW1lcmFibGUgaW4gQ2hyb21lIChhdCBsZWFzdCksIHNvIGBmb3IgcHJvcCBpbiB0bXBgIGRvZXNuJ3Qgd29yay5cbiAgZm9yIChsZXQgaWR4ID0gMDsgaWR4IDwgZXJyb3JQcm9wcy5sZW5ndGg7IGlkeCsrKSB7XG4gICAgdGhpc1tlcnJvclByb3BzW2lkeF1dID0gdG1wW2Vycm9yUHJvcHNbaWR4XV07XG4gIH1cblxuICAvKiBpc3RhbmJ1bCBpZ25vcmUgZWxzZSAqL1xuICBpZiAoRXJyb3IuY2FwdHVyZVN0YWNrVHJhY2UpIHtcbiAgICBFcnJvci5jYXB0dXJlU3RhY2tUcmFjZSh0aGlzLCBFeGNlcHRpb24pO1xuICB9XG5cbiAgdHJ5IHtcbiAgICBpZiAobG9jKSB7XG4gICAgICB0aGlzLmxpbmVOdW1iZXIgPSBsaW5lO1xuXG4gICAgICAvLyBXb3JrIGFyb3VuZCBpc3N1ZSB1bmRlciBzYWZhcmkgd2hlcmUgd2UgY2FuJ3QgZGlyZWN0bHkgc2V0IHRoZSBjb2x1bW4gdmFsdWVcbiAgICAgIC8qIGlzdGFuYnVsIGlnbm9yZSBuZXh0ICovXG4gICAgICBpZiAoT2JqZWN0LmRlZmluZVByb3BlcnR5KSB7XG4gICAgICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0aGlzLCAnY29sdW1uJywge3ZhbHVlOiBjb2x1bW59KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMuY29sdW1uID0gY29sdW1uO1xuICAgICAgfVxuICAgIH1cbiAgfSBjYXRjaCAobm9wKSB7XG4gICAgLyogSWdub3JlIGlmIHRoZSBicm93c2VyIGlzIHZlcnkgcGFydGljdWxhciAqL1xuICB9XG59XG5cbkV4Y2VwdGlvbi5wcm90b3R5cGUgPSBuZXcgRXJyb3IoKTtcblxuZXhwb3J0IGRlZmF1bHQgRXhjZXB0aW9uO1xuIl19
52 |
--------------------------------------------------------------------------------
/lib/handlebars/compiler/code-gen.js:
--------------------------------------------------------------------------------
1 | /* global define */
2 | import {isArray} from '../utils';
3 |
4 | let SourceNode;
5 |
6 | try {
7 | /* istanbul ignore next */
8 | if (typeof define !== 'function' || !define.amd) {
9 | // We don't support this in AMD environments. For these environments, we asusme that
10 | // they are running on the browser and thus have no need for the source-map library.
11 | let SourceMap = require('source-map');
12 | SourceNode = SourceMap.SourceNode;
13 | }
14 | } catch (err) {
15 | /* NOP */
16 | }
17 |
18 | /* istanbul ignore if: tested but not covered in istanbul due to dist build */
19 | if (!SourceNode) {
20 | SourceNode = function(line, column, srcFile, chunks) {
21 | this.src = '';
22 | if (chunks) {
23 | this.add(chunks);
24 | }
25 | };
26 | /* istanbul ignore next */
27 | SourceNode.prototype = {
28 | add: function(chunks) {
29 | if (isArray(chunks)) {
30 | chunks = chunks.join('');
31 | }
32 | this.src += chunks;
33 | },
34 | prepend: function(chunks) {
35 | if (isArray(chunks)) {
36 | chunks = chunks.join('');
37 | }
38 | this.src = chunks + this.src;
39 | },
40 | toStringWithSourceMap: function() {
41 | return {code: this.toString()};
42 | },
43 | toString: function() {
44 | return this.src;
45 | }
46 | };
47 | }
48 |
49 |
50 | function castChunk(chunk, codeGen, loc) {
51 | if (isArray(chunk)) {
52 | let ret = [];
53 |
54 | for (let i = 0, len = chunk.length; i < len; i++) {
55 | ret.push(codeGen.wrap(chunk[i], loc));
56 | }
57 | return ret;
58 | } else if (typeof chunk === 'boolean' || typeof chunk === 'number') {
59 | // Handle primitives that the SourceNode will throw up on
60 | return chunk + '';
61 | }
62 | return chunk;
63 | }
64 |
65 |
66 | function CodeGen(srcFile) {
67 | this.srcFile = srcFile;
68 | this.source = [];
69 | }
70 |
71 | CodeGen.prototype = {
72 | isEmpty() {
73 | return !this.source.length;
74 | },
75 | prepend: function(source, loc) {
76 | this.source.unshift(this.wrap(source, loc));
77 | },
78 | push: function(source, loc) {
79 | this.source.push(this.wrap(source, loc));
80 | },
81 |
82 | merge: function() {
83 | let source = this.empty();
84 | this.each(function(line) {
85 | source.add([' ', line, '\n']);
86 | });
87 | return source;
88 | },
89 |
90 | each: function(iter) {
91 | for (let i = 0, len = this.source.length; i < len; i++) {
92 | iter(this.source[i]);
93 | }
94 | },
95 |
96 | empty: function() {
97 | let loc = this.currentLocation || {start: {}};
98 | return new SourceNode(loc.start.line, loc.start.column, this.srcFile);
99 | },
100 | wrap: function(chunk, loc = this.currentLocation || {start: {}}) {
101 | if (chunk instanceof SourceNode) {
102 | return chunk;
103 | }
104 |
105 | chunk = castChunk(chunk, this, loc);
106 |
107 | return new SourceNode(loc.start.line, loc.start.column, this.srcFile, chunk);
108 | },
109 |
110 | functionCall: function(fn, type, params) {
111 | params = this.generateList(params);
112 | return this.wrap([fn, type ? '.' + type + '(' : '(', params, ')']);
113 | },
114 |
115 | quotedString: function(str) {
116 | return '"' + (str + '')
117 | .replace(/\\/g, '\\\\')
118 | .replace(/"/g, '\\"')
119 | .replace(/\n/g, '\\n')
120 | .replace(/\r/g, '\\r')
121 | .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4
122 | .replace(/\u2029/g, '\\u2029') + '"';
123 | },
124 |
125 | objectLiteral: function(obj) {
126 | let pairs = [];
127 |
128 | for (let key in obj) {
129 | if (obj.hasOwnProperty(key)) {
130 | let value = castChunk(obj[key], this);
131 | if (value !== 'undefined') {
132 | pairs.push([this.quotedString(key), ':', value]);
133 | }
134 | }
135 | }
136 |
137 | let ret = this.generateList(pairs);
138 | ret.prepend('{');
139 | ret.add('}');
140 | return ret;
141 | },
142 |
143 |
144 | generateList: function(entries) {
145 | let ret = this.empty();
146 |
147 | for (let i = 0, len = entries.length; i < len; i++) {
148 | if (i) {
149 | ret.add(',');
150 | }
151 |
152 | ret.add(castChunk(entries[i], this));
153 | }
154 |
155 | return ret;
156 | },
157 |
158 | generateArray: function(entries) {
159 | let ret = this.generateList(entries);
160 | ret.prepend('[');
161 | ret.add(']');
162 |
163 | return ret;
164 | }
165 | };
166 |
167 | export default CodeGen;
168 |
169 |
--------------------------------------------------------------------------------
/dist/cjs/handlebars.runtime.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 | // istanbul ignore next
5 |
6 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
7 |
8 | // istanbul ignore next
9 |
10 | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
11 |
12 | var _handlebarsBase = require('./handlebars/base');
13 |
14 | var base = _interopRequireWildcard(_handlebarsBase);
15 |
16 | // Each of these augment the Handlebars object. No need to setup here.
17 | // (This is done to easily share code between commonjs and browse envs)
18 |
19 | var _handlebarsSafeString = require('./handlebars/safe-string');
20 |
21 | var _handlebarsSafeString2 = _interopRequireDefault(_handlebarsSafeString);
22 |
23 | var _handlebarsException = require('./handlebars/exception');
24 |
25 | var _handlebarsException2 = _interopRequireDefault(_handlebarsException);
26 |
27 | var _handlebarsUtils = require('./handlebars/utils');
28 |
29 | var Utils = _interopRequireWildcard(_handlebarsUtils);
30 |
31 | var _handlebarsRuntime = require('./handlebars/runtime');
32 |
33 | var runtime = _interopRequireWildcard(_handlebarsRuntime);
34 |
35 | var _handlebarsNoConflict = require('./handlebars/no-conflict');
36 |
37 | var _handlebarsNoConflict2 = _interopRequireDefault(_handlebarsNoConflict);
38 |
39 | // For compatibility and usage outside of module systems, make the Handlebars object a namespace
40 | function create() {
41 | var hb = new base.HandlebarsEnvironment();
42 |
43 | Utils.extend(hb, base);
44 | hb.SafeString = _handlebarsSafeString2['default'];
45 | hb.Exception = _handlebarsException2['default'];
46 | hb.Utils = Utils;
47 | hb.escapeExpression = Utils.escapeExpression;
48 |
49 | hb.VM = runtime;
50 | hb.template = function (spec) {
51 | return runtime.template(spec, hb);
52 | };
53 |
54 | return hb;
55 | }
56 |
57 | var inst = create();
58 | inst.create = create;
59 |
60 | _handlebarsNoConflict2['default'](inst);
61 |
62 | inst['default'] = inst;
63 |
64 | exports['default'] = inst;
65 | module.exports = exports['default'];
66 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL2xpYi9oYW5kbGViYXJzLnJ1bnRpbWUuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7OEJBQXNCLG1CQUFtQjs7SUFBN0IsSUFBSTs7Ozs7b0NBSU8sMEJBQTBCOzs7O21DQUMzQix3QkFBd0I7Ozs7K0JBQ3ZCLG9CQUFvQjs7SUFBL0IsS0FBSzs7aUNBQ1Esc0JBQXNCOztJQUFuQyxPQUFPOztvQ0FFSSwwQkFBMEI7Ozs7O0FBR2pELFNBQVMsTUFBTSxHQUFHO0FBQ2hCLE1BQUksRUFBRSxHQUFHLElBQUksSUFBSSxDQUFDLHFCQUFxQixFQUFFLENBQUM7O0FBRTFDLE9BQUssQ0FBQyxNQUFNLENBQUMsRUFBRSxFQUFFLElBQUksQ0FBQyxDQUFDO0FBQ3ZCLElBQUUsQ0FBQyxVQUFVLG9DQUFhLENBQUM7QUFDM0IsSUFBRSxDQUFDLFNBQVMsbUNBQVksQ0FBQztBQUN6QixJQUFFLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQztBQUNqQixJQUFFLENBQUMsZ0JBQWdCLEdBQUcsS0FBSyxDQUFDLGdCQUFnQixDQUFDOztBQUU3QyxJQUFFLENBQUMsRUFBRSxHQUFHLE9BQU8sQ0FBQztBQUNoQixJQUFFLENBQUMsUUFBUSxHQUFHLFVBQVMsSUFBSSxFQUFFO0FBQzNCLFdBQU8sT0FBTyxDQUFDLFFBQVEsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLENBQUM7R0FDbkMsQ0FBQzs7QUFFRixTQUFPLEVBQUUsQ0FBQztDQUNYOztBQUVELElBQUksSUFBSSxHQUFHLE1BQU0sRUFBRSxDQUFDO0FBQ3BCLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDOztBQUVyQixrQ0FBVyxJQUFJLENBQUMsQ0FBQzs7QUFFakIsSUFBSSxDQUFDLFNBQVMsQ0FBQyxHQUFHLElBQUksQ0FBQzs7cUJBRVIsSUFBSSIsImZpbGUiOiJoYW5kbGViYXJzLnJ1bnRpbWUuanMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBiYXNlIGZyb20gJy4vaGFuZGxlYmFycy9iYXNlJztcblxuLy8gRWFjaCBvZiB0aGVzZSBhdWdtZW50IHRoZSBIYW5kbGViYXJzIG9iamVjdC4gTm8gbmVlZCB0byBzZXR1cCBoZXJlLlxuLy8gKFRoaXMgaXMgZG9uZSB0byBlYXNpbHkgc2hhcmUgY29kZSBiZXR3ZWVuIGNvbW1vbmpzIGFuZCBicm93c2UgZW52cylcbmltcG9ydCBTYWZlU3RyaW5nIGZyb20gJy4vaGFuZGxlYmFycy9zYWZlLXN0cmluZyc7XG5pbXBvcnQgRXhjZXB0aW9uIGZyb20gJy4vaGFuZGxlYmFycy9leGNlcHRpb24nO1xuaW1wb3J0ICogYXMgVXRpbHMgZnJvbSAnLi9oYW5kbGViYXJzL3V0aWxzJztcbmltcG9ydCAqIGFzIHJ1bnRpbWUgZnJvbSAnLi9oYW5kbGViYXJzL3J1bnRpbWUnO1xuXG5pbXBvcnQgbm9Db25mbGljdCBmcm9tICcuL2hhbmRsZWJhcnMvbm8tY29uZmxpY3QnO1xuXG4vLyBGb3IgY29tcGF0aWJpbGl0eSBhbmQgdXNhZ2Ugb3V0c2lkZSBvZiBtb2R1bGUgc3lzdGVtcywgbWFrZSB0aGUgSGFuZGxlYmFycyBvYmplY3QgYSBuYW1lc3BhY2VcbmZ1bmN0aW9uIGNyZWF0ZSgpIHtcbiAgbGV0IGhiID0gbmV3IGJhc2UuSGFuZGxlYmFyc0Vudmlyb25tZW50KCk7XG5cbiAgVXRpbHMuZXh0ZW5kKGhiLCBiYXNlKTtcbiAgaGIuU2FmZVN0cmluZyA9IFNhZmVTdHJpbmc7XG4gIGhiLkV4Y2VwdGlvbiA9IEV4Y2VwdGlvbjtcbiAgaGIuVXRpbHMgPSBVdGlscztcbiAgaGIuZXNjYXBlRXhwcmVzc2lvbiA9IFV0aWxzLmVzY2FwZUV4cHJlc3Npb247XG5cbiAgaGIuVk0gPSBydW50aW1lO1xuICBoYi50ZW1wbGF0ZSA9IGZ1bmN0aW9uKHNwZWMpIHtcbiAgICByZXR1cm4gcnVudGltZS50ZW1wbGF0ZShzcGVjLCBoYik7XG4gIH07XG5cbiAgcmV0dXJuIGhiO1xufVxuXG5sZXQgaW5zdCA9IGNyZWF0ZSgpO1xuaW5zdC5jcmVhdGUgPSBjcmVhdGU7XG5cbm5vQ29uZmxpY3QoaW5zdCk7XG5cbmluc3RbJ2RlZmF1bHQnXSA9IGluc3Q7XG5cbmV4cG9ydCBkZWZhdWx0IGluc3Q7XG4iXX0=
67 |
--------------------------------------------------------------------------------
/dist/cjs/handlebars.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 | // istanbul ignore next
5 |
6 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
7 |
8 | var _handlebarsRuntime = require('./handlebars.runtime');
9 |
10 | var _handlebarsRuntime2 = _interopRequireDefault(_handlebarsRuntime);
11 |
12 | // Compiler imports
13 |
14 | var _handlebarsCompilerAst = require('./handlebars/compiler/ast');
15 |
16 | var _handlebarsCompilerAst2 = _interopRequireDefault(_handlebarsCompilerAst);
17 |
18 | var _handlebarsCompilerBase = require('./handlebars/compiler/base');
19 |
20 | var _handlebarsCompilerCompiler = require('./handlebars/compiler/compiler');
21 |
22 | var _handlebarsCompilerJavascriptCompiler = require('./handlebars/compiler/javascript-compiler');
23 |
24 | var _handlebarsCompilerJavascriptCompiler2 = _interopRequireDefault(_handlebarsCompilerJavascriptCompiler);
25 |
26 | var _handlebarsCompilerVisitor = require('./handlebars/compiler/visitor');
27 |
28 | var _handlebarsCompilerVisitor2 = _interopRequireDefault(_handlebarsCompilerVisitor);
29 |
30 | var _handlebarsNoConflict = require('./handlebars/no-conflict');
31 |
32 | var _handlebarsNoConflict2 = _interopRequireDefault(_handlebarsNoConflict);
33 |
34 | var _create = _handlebarsRuntime2['default'].create;
35 | function create() {
36 | var hb = _create();
37 |
38 | hb.compile = function (input, options) {
39 | return _handlebarsCompilerCompiler.compile(input, options, hb);
40 | };
41 | hb.precompile = function (input, options) {
42 | return _handlebarsCompilerCompiler.precompile(input, options, hb);
43 | };
44 |
45 | hb.AST = _handlebarsCompilerAst2['default'];
46 | hb.Compiler = _handlebarsCompilerCompiler.Compiler;
47 | hb.JavaScriptCompiler = _handlebarsCompilerJavascriptCompiler2['default'];
48 | hb.Parser = _handlebarsCompilerBase.parser;
49 | hb.parse = _handlebarsCompilerBase.parse;
50 |
51 | return hb;
52 | }
53 |
54 | var inst = create();
55 | inst.create = create;
56 |
57 | _handlebarsNoConflict2['default'](inst);
58 |
59 | inst.Visitor = _handlebarsCompilerVisitor2['default'];
60 |
61 | inst['default'] = inst;
62 |
63 | exports['default'] = inst;
64 | module.exports = exports['default'];
65 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL2xpYi9oYW5kbGViYXJzLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7aUNBQW9CLHNCQUFzQjs7Ozs7O3FDQUcxQiwyQkFBMkI7Ozs7c0NBQ0gsNEJBQTRCOzswQ0FDdEIsZ0NBQWdDOztvREFDL0MsMkNBQTJDOzs7O3lDQUN0RCwrQkFBK0I7Ozs7b0NBRTVCLDBCQUEwQjs7OztBQUVqRCxJQUFJLE9BQU8sR0FBRywrQkFBUSxNQUFNLENBQUM7QUFDN0IsU0FBUyxNQUFNLEdBQUc7QUFDaEIsTUFBSSxFQUFFLEdBQUcsT0FBTyxFQUFFLENBQUM7O0FBRW5CLElBQUUsQ0FBQyxPQUFPLEdBQUcsVUFBUyxLQUFLLEVBQUUsT0FBTyxFQUFFO0FBQ3BDLFdBQU8sb0NBQVEsS0FBSyxFQUFFLE9BQU8sRUFBRSxFQUFFLENBQUMsQ0FBQztHQUNwQyxDQUFDO0FBQ0YsSUFBRSxDQUFDLFVBQVUsR0FBRyxVQUFTLEtBQUssRUFBRSxPQUFPLEVBQUU7QUFDdkMsV0FBTyx1Q0FBVyxLQUFLLEVBQUUsT0FBTyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0dBQ3ZDLENBQUM7O0FBRUYsSUFBRSxDQUFDLEdBQUcscUNBQU0sQ0FBQztBQUNiLElBQUUsQ0FBQyxRQUFRLHVDQUFXLENBQUM7QUFDdkIsSUFBRSxDQUFDLGtCQUFrQixvREFBcUIsQ0FBQztBQUMzQyxJQUFFLENBQUMsTUFBTSxpQ0FBUyxDQUFDO0FBQ25CLElBQUUsQ0FBQyxLQUFLLGdDQUFRLENBQUM7O0FBRWpCLFNBQU8sRUFBRSxDQUFDO0NBQ1g7O0FBRUQsSUFBSSxJQUFJLEdBQUcsTUFBTSxFQUFFLENBQUM7QUFDcEIsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7O0FBRXJCLGtDQUFXLElBQUksQ0FBQyxDQUFDOztBQUVqQixJQUFJLENBQUMsT0FBTyx5Q0FBVSxDQUFDOztBQUV2QixJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsSUFBSSxDQUFDOztxQkFFUixJQUFJIiwiZmlsZSI6ImhhbmRsZWJhcnMuanMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgcnVudGltZSBmcm9tICcuL2hhbmRsZWJhcnMucnVudGltZSc7XG5cbi8vIENvbXBpbGVyIGltcG9ydHNcbmltcG9ydCBBU1QgZnJvbSAnLi9oYW5kbGViYXJzL2NvbXBpbGVyL2FzdCc7XG5pbXBvcnQgeyBwYXJzZXIgYXMgUGFyc2VyLCBwYXJzZSB9IGZyb20gJy4vaGFuZGxlYmFycy9jb21waWxlci9iYXNlJztcbmltcG9ydCB7IENvbXBpbGVyLCBjb21waWxlLCBwcmVjb21waWxlIH0gZnJvbSAnLi9oYW5kbGViYXJzL2NvbXBpbGVyL2NvbXBpbGVyJztcbmltcG9ydCBKYXZhU2NyaXB0Q29tcGlsZXIgZnJvbSAnLi9oYW5kbGViYXJzL2NvbXBpbGVyL2phdmFzY3JpcHQtY29tcGlsZXInO1xuaW1wb3J0IFZpc2l0b3IgZnJvbSAnLi9oYW5kbGViYXJzL2NvbXBpbGVyL3Zpc2l0b3InO1xuXG5pbXBvcnQgbm9Db25mbGljdCBmcm9tICcuL2hhbmRsZWJhcnMvbm8tY29uZmxpY3QnO1xuXG5sZXQgX2NyZWF0ZSA9IHJ1bnRpbWUuY3JlYXRlO1xuZnVuY3Rpb24gY3JlYXRlKCkge1xuICBsZXQgaGIgPSBfY3JlYXRlKCk7XG5cbiAgaGIuY29tcGlsZSA9IGZ1bmN0aW9uKGlucHV0LCBvcHRpb25zKSB7XG4gICAgcmV0dXJuIGNvbXBpbGUoaW5wdXQsIG9wdGlvbnMsIGhiKTtcbiAgfTtcbiAgaGIucHJlY29tcGlsZSA9IGZ1bmN0aW9uKGlucHV0LCBvcHRpb25zKSB7XG4gICAgcmV0dXJuIHByZWNvbXBpbGUoaW5wdXQsIG9wdGlvbnMsIGhiKTtcbiAgfTtcblxuICBoYi5BU1QgPSBBU1Q7XG4gIGhiLkNvbXBpbGVyID0gQ29tcGlsZXI7XG4gIGhiLkphdmFTY3JpcHRDb21waWxlciA9IEphdmFTY3JpcHRDb21waWxlcjtcbiAgaGIuUGFyc2VyID0gUGFyc2VyO1xuICBoYi5wYXJzZSA9IHBhcnNlO1xuXG4gIHJldHVybiBoYjtcbn1cblxubGV0IGluc3QgPSBjcmVhdGUoKTtcbmluc3QuY3JlYXRlID0gY3JlYXRlO1xuXG5ub0NvbmZsaWN0KGluc3QpO1xuXG5pbnN0LlZpc2l0b3IgPSBWaXNpdG9yO1xuXG5pbnN0WydkZWZhdWx0J10gPSBpbnN0O1xuXG5leHBvcnQgZGVmYXVsdCBpbnN0O1xuIl19
66 |
--------------------------------------------------------------------------------
/lib/handlebars/compiler/printer.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable new-cap */
2 | import Visitor from './visitor';
3 |
4 | export function print(ast) {
5 | return new PrintVisitor().accept(ast);
6 | }
7 |
8 | export function PrintVisitor() {
9 | this.padding = 0;
10 | }
11 |
12 | PrintVisitor.prototype = new Visitor();
13 |
14 | PrintVisitor.prototype.pad = function(string) {
15 | let out = '';
16 |
17 | for (let i = 0, l = this.padding; i < l; i++) {
18 | out += ' ';
19 | }
20 |
21 | out += string + '\n';
22 | return out;
23 | };
24 |
25 | PrintVisitor.prototype.Program = function(program) {
26 | let out = '',
27 | body = program.body,
28 | i, l;
29 |
30 | if (program.blockParams) {
31 | let blockParams = 'BLOCK PARAMS: [';
32 | for (i = 0, l = program.blockParams.length; i < l; i++) {
33 | blockParams += ' ' + program.blockParams[i];
34 | }
35 | blockParams += ' ]';
36 | out += this.pad(blockParams);
37 | }
38 |
39 | for (i = 0, l = body.length; i < l; i++) {
40 | out += this.accept(body[i]);
41 | }
42 |
43 | this.padding--;
44 |
45 | return out;
46 | };
47 |
48 | PrintVisitor.prototype.MustacheStatement = function(mustache) {
49 | return this.pad('{{ ' + this.SubExpression(mustache) + ' }}');
50 | };
51 | PrintVisitor.prototype.Decorator = function(mustache) {
52 | return this.pad('{{ DIRECTIVE ' + this.SubExpression(mustache) + ' }}');
53 | };
54 |
55 | PrintVisitor.prototype.BlockStatement =
56 | PrintVisitor.prototype.DecoratorBlock = function(block) {
57 | let out = '';
58 |
59 | out += this.pad((block.type === 'DecoratorBlock' ? 'DIRECTIVE ' : '') + 'BLOCK:');
60 | this.padding++;
61 | out += this.pad(this.SubExpression(block));
62 | if (block.program) {
63 | out += this.pad('PROGRAM:');
64 | this.padding++;
65 | out += this.accept(block.program);
66 | this.padding--;
67 | }
68 | if (block.inverse) {
69 | if (block.program) { this.padding++; }
70 | out += this.pad('{{^}}');
71 | this.padding++;
72 | out += this.accept(block.inverse);
73 | this.padding--;
74 | if (block.program) { this.padding--; }
75 | }
76 | this.padding--;
77 |
78 | return out;
79 | };
80 |
81 | PrintVisitor.prototype.PartialStatement = function(partial) {
82 | let content = 'PARTIAL:' + partial.name.original;
83 | if (partial.params[0]) {
84 | content += ' ' + this.accept(partial.params[0]);
85 | }
86 | if (partial.hash) {
87 | content += ' ' + this.accept(partial.hash);
88 | }
89 | return this.pad('{{> ' + content + ' }}');
90 | };
91 | PrintVisitor.prototype.PartialBlockStatement = function(partial) {
92 | let content = 'PARTIAL BLOCK:' + partial.name.original;
93 | if (partial.params[0]) {
94 | content += ' ' + this.accept(partial.params[0]);
95 | }
96 | if (partial.hash) {
97 | content += ' ' + this.accept(partial.hash);
98 | }
99 |
100 | content += ' ' + this.pad('PROGRAM:');
101 | this.padding++;
102 | content += this.accept(partial.program);
103 | this.padding--;
104 |
105 | return this.pad('{{> ' + content + ' }}');
106 | };
107 |
108 | PrintVisitor.prototype.ContentStatement = function(content) {
109 | return this.pad("CONTENT[ '" + content.value + "' ]");
110 | };
111 |
112 | PrintVisitor.prototype.CommentStatement = function(comment) {
113 | return this.pad("{{! '" + comment.value + "' }}");
114 | };
115 |
116 | PrintVisitor.prototype.SubExpression = function(sexpr) {
117 | let params = sexpr.params,
118 | paramStrings = [],
119 | hash;
120 |
121 | for (let i = 0, l = params.length; i < l; i++) {
122 | paramStrings.push(this.accept(params[i]));
123 | }
124 |
125 | params = '[' + paramStrings.join(', ') + ']';
126 |
127 | hash = sexpr.hash ? ' ' + this.accept(sexpr.hash) : '';
128 |
129 | return this.accept(sexpr.path) + ' ' + params + hash;
130 | };
131 |
132 | PrintVisitor.prototype.PathExpression = function(id) {
133 | let path = id.parts.join('/');
134 | return (id.data ? '@' : '') + 'PATH:' + path;
135 | };
136 |
137 |
138 | PrintVisitor.prototype.StringLiteral = function(string) {
139 | return '"' + string.value + '"';
140 | };
141 |
142 | PrintVisitor.prototype.NumberLiteral = function(number) {
143 | return 'NUMBER{' + number.value + '}';
144 | };
145 |
146 | PrintVisitor.prototype.BooleanLiteral = function(bool) {
147 | return 'BOOLEAN{' + bool.value + '}';
148 | };
149 |
150 | PrintVisitor.prototype.UndefinedLiteral = function() {
151 | return 'UNDEFINED';
152 | };
153 |
154 | PrintVisitor.prototype.NullLiteral = function() {
155 | return 'NULL';
156 | };
157 |
158 | PrintVisitor.prototype.Hash = function(hash) {
159 | let pairs = hash.pairs,
160 | joinedPairs = [];
161 |
162 | for (let i = 0, l = pairs.length; i < l; i++) {
163 | joinedPairs.push(this.accept(pairs[i]));
164 | }
165 |
166 | return 'HASH{' + joinedPairs.join(', ') + '}';
167 | };
168 | PrintVisitor.prototype.HashPair = function(pair) {
169 | return pair.key + '=' + this.accept(pair.value);
170 | };
171 | /* eslint-enable new-cap */
172 |
--------------------------------------------------------------------------------
/src/handlebars.yy:
--------------------------------------------------------------------------------
1 | %start root
2 |
3 | %ebnf
4 |
5 | %%
6 |
7 | root
8 | : program EOF { return $1; }
9 | ;
10 |
11 | program
12 | : statement* -> yy.prepareProgram($1)
13 | ;
14 |
15 | statement
16 | : mustache -> $1
17 | | block -> $1
18 | | rawBlock -> $1
19 | | partial -> $1
20 | | partialBlock -> $1
21 | | content -> $1
22 | | COMMENT {
23 | $$ = {
24 | type: 'CommentStatement',
25 | value: yy.stripComment($1),
26 | strip: yy.stripFlags($1, $1),
27 | loc: yy.locInfo(@$)
28 | };
29 | };
30 |
31 | content
32 | : CONTENT {
33 | $$ = {
34 | type: 'ContentStatement',
35 | original: $1,
36 | value: $1,
37 | loc: yy.locInfo(@$)
38 | };
39 | };
40 |
41 | rawBlock
42 | : openRawBlock content+ END_RAW_BLOCK -> yy.prepareRawBlock($1, $2, $3, @$)
43 | ;
44 |
45 | openRawBlock
46 | : OPEN_RAW_BLOCK helperName param* hash? CLOSE_RAW_BLOCK -> { path: $2, params: $3, hash: $4 }
47 | ;
48 |
49 | block
50 | : openBlock program inverseChain? closeBlock -> yy.prepareBlock($1, $2, $3, $4, false, @$)
51 | | openInverse program inverseAndProgram? closeBlock -> yy.prepareBlock($1, $2, $3, $4, true, @$)
52 | ;
53 |
54 | openBlock
55 | : OPEN_BLOCK helperName param* hash? blockParams? CLOSE -> { open: $1, path: $2, params: $3, hash: $4, blockParams: $5, strip: yy.stripFlags($1, $6) }
56 | ;
57 |
58 | openInverse
59 | : OPEN_INVERSE helperName param* hash? blockParams? CLOSE -> { path: $2, params: $3, hash: $4, blockParams: $5, strip: yy.stripFlags($1, $6) }
60 | ;
61 |
62 | openInverseChain
63 | : OPEN_INVERSE_CHAIN helperName param* hash? blockParams? CLOSE -> { path: $2, params: $3, hash: $4, blockParams: $5, strip: yy.stripFlags($1, $6) }
64 | ;
65 |
66 | inverseAndProgram
67 | : INVERSE program -> { strip: yy.stripFlags($1, $1), program: $2 }
68 | ;
69 |
70 | inverseChain
71 | : openInverseChain program inverseChain? {
72 | var inverse = yy.prepareBlock($1, $2, $3, $3, false, @$),
73 | program = yy.prepareProgram([inverse], $2.loc);
74 | program.chained = true;
75 |
76 | $$ = { strip: $1.strip, program: program, chain: true };
77 | }
78 | | inverseAndProgram -> $1
79 | ;
80 |
81 | closeBlock
82 | : OPEN_ENDBLOCK helperName CLOSE -> {path: $2, strip: yy.stripFlags($1, $3)}
83 | ;
84 |
85 | mustache
86 | // Parsing out the '&' escape token at AST level saves ~500 bytes after min due to the removal of one parser node.
87 | // This also allows for handler unification as all mustache node instances can utilize the same handler
88 | : OPEN helperName param* hash? CLOSE -> yy.prepareMustache($2, $3, $4, $1, yy.stripFlags($1, $5), @$)
89 | | OPEN_UNESCAPED helperName param* hash? CLOSE_UNESCAPED -> yy.prepareMustache($2, $3, $4, $1, yy.stripFlags($1, $5), @$)
90 | ;
91 |
92 | partial
93 | : OPEN_PARTIAL partialName param* hash? CLOSE {
94 | $$ = {
95 | type: 'PartialStatement',
96 | name: $2,
97 | params: $3,
98 | hash: $4,
99 | indent: '',
100 | strip: yy.stripFlags($1, $5),
101 | loc: yy.locInfo(@$)
102 | };
103 | }
104 | ;
105 | partialBlock
106 | : openPartialBlock program closeBlock -> yy.preparePartialBlock($1, $2, $3, @$)
107 | ;
108 | openPartialBlock
109 | : OPEN_PARTIAL_BLOCK partialName param* hash? CLOSE -> { path: $2, params: $3, hash: $4, strip: yy.stripFlags($1, $5) }
110 | ;
111 |
112 | param
113 | : helperName -> $1
114 | | sexpr -> $1
115 | ;
116 |
117 | sexpr
118 | : OPEN_SEXPR helperName param* hash? CLOSE_SEXPR {
119 | $$ = {
120 | type: 'SubExpression',
121 | path: $2,
122 | params: $3,
123 | hash: $4,
124 | loc: yy.locInfo(@$)
125 | };
126 | };
127 |
128 | hash
129 | : hashSegment+ -> {type: 'Hash', pairs: $1, loc: yy.locInfo(@$)}
130 | ;
131 |
132 | hashSegment
133 | : ID EQUALS param -> {type: 'HashPair', key: yy.id($1), value: $3, loc: yy.locInfo(@$)}
134 | ;
135 |
136 | blockParams
137 | : OPEN_BLOCK_PARAMS ID+ CLOSE_BLOCK_PARAMS -> yy.id($2)
138 | ;
139 |
140 | helperName
141 | : path -> $1
142 | | dataName -> $1
143 | | STRING -> {type: 'StringLiteral', value: $1, original: $1, loc: yy.locInfo(@$)}
144 | | NUMBER -> {type: 'NumberLiteral', value: Number($1), original: Number($1), loc: yy.locInfo(@$)}
145 | | BOOLEAN -> {type: 'BooleanLiteral', value: $1 === 'true', original: $1 === 'true', loc: yy.locInfo(@$)}
146 | | UNDEFINED -> {type: 'UndefinedLiteral', original: undefined, value: undefined, loc: yy.locInfo(@$)}
147 | | NULL -> {type: 'NullLiteral', original: null, value: null, loc: yy.locInfo(@$)}
148 | ;
149 |
150 | partialName
151 | : helperName -> $1
152 | | sexpr -> $1
153 | ;
154 |
155 | dataName
156 | : DATA pathSegments -> yy.preparePath(true, $2, @$)
157 | ;
158 |
159 | path
160 | : pathSegments -> yy.preparePath(false, $1, @$)
161 | ;
162 |
163 | pathSegments
164 | : pathSegments SEP ID { $1.push({part: yy.id($3), original: $3, separator: $2}); $$ = $1; }
165 | | ID -> [{part: yy.id($1), original: $1}]
166 | ;
167 |
--------------------------------------------------------------------------------
/spec/strict.js:
--------------------------------------------------------------------------------
1 | var Exception = Handlebars.Exception;
2 |
3 | describe('strict', function() {
4 | describe('strict mode', function() {
5 | it('should error on missing property lookup', function() {
6 | shouldThrow(function() {
7 | var template = CompilerContext.compile('{{hello}}', {strict: true});
8 |
9 | template({});
10 | }, Exception, /"hello" not defined in/);
11 | });
12 | it('should error on missing child', function() {
13 | var template = CompilerContext.compile('{{hello.bar}}', {strict: true});
14 | equals(template({hello: {bar: 'foo'}}), 'foo');
15 |
16 | shouldThrow(function() {
17 | template({hello: {}});
18 | }, Exception, /"bar" not defined in/);
19 | });
20 | it('should handle explicit undefined', function() {
21 | var template = CompilerContext.compile('{{hello.bar}}', {strict: true});
22 |
23 | equals(template({hello: {bar: undefined}}), '');
24 | });
25 | it('should error on missing property lookup in known helpers mode', function() {
26 | shouldThrow(function() {
27 | var template = CompilerContext.compile('{{hello}}', {strict: true, knownHelpersOnly: true});
28 |
29 | template({});
30 | }, Exception, /"hello" not defined in/);
31 | });
32 | it('should error on missing context', function() {
33 | shouldThrow(function() {
34 | var template = CompilerContext.compile('{{hello}}', {strict: true});
35 |
36 | template();
37 | }, Error);
38 | });
39 |
40 | it('should error on missing data lookup', function() {
41 | var template = CompilerContext.compile('{{@hello}}', {strict: true});
42 | equals(template(undefined, {data: {hello: 'foo'}}), 'foo');
43 |
44 | shouldThrow(function() {
45 | template();
46 | }, Error);
47 | });
48 |
49 | it('should not run helperMissing for helper calls', function() {
50 | shouldThrow(function() {
51 | var template = CompilerContext.compile('{{hello foo}}', {strict: true});
52 |
53 | template({foo: true});
54 | }, Exception, /"hello" not defined in/);
55 |
56 | shouldThrow(function() {
57 | var template = CompilerContext.compile('{{#hello foo}}{{/hello}}', {strict: true});
58 |
59 | template({foo: true});
60 | }, Exception, /"hello" not defined in/);
61 | });
62 | it('should throw on ambiguous blocks', function() {
63 | shouldThrow(function() {
64 | var template = CompilerContext.compile('{{#hello}}{{/hello}}', {strict: true});
65 |
66 | template({});
67 | }, Exception, /"hello" not defined in/);
68 |
69 | shouldThrow(function() {
70 | var template = CompilerContext.compile('{{^hello}}{{/hello}}', {strict: true});
71 |
72 | template({});
73 | }, Exception, /"hello" not defined in/);
74 |
75 | shouldThrow(function() {
76 | var template = CompilerContext.compile('{{#hello.bar}}{{/hello.bar}}', {strict: true});
77 |
78 | template({hello: {}});
79 | }, Exception, /"bar" not defined in/);
80 | });
81 |
82 | it('should allow undefined parameters when passed to helpers', function() {
83 | var template = CompilerContext.compile('{{#unless foo}}success{{/unless}}', {strict: true});
84 | equals(template({}), 'success');
85 | });
86 |
87 | it('should allow undefined hash when passed to helpers', function() {
88 | var template = CompilerContext.compile('{{helper value=@foo}}', {strict: true});
89 | var helpers = {
90 | helper: function(options) {
91 | equals('value' in options.hash, true);
92 | equals(options.hash.value, undefined);
93 | return 'success';
94 | }
95 | };
96 | equals(template({}, {helpers: helpers}), 'success');
97 | });
98 | });
99 |
100 | describe('assume objects', function() {
101 | it('should ignore missing property', function() {
102 | var template = CompilerContext.compile('{{hello}}', {assumeObjects: true});
103 |
104 | equal(template({}), '');
105 | });
106 | it('should ignore missing child', function() {
107 | var template = CompilerContext.compile('{{hello.bar}}', {assumeObjects: true});
108 |
109 | equal(template({hello: {}}), '');
110 | });
111 | it('should error on missing object', function() {
112 | shouldThrow(function() {
113 | var template = CompilerContext.compile('{{hello.bar}}', {assumeObjects: true});
114 |
115 | template({});
116 | }, Error);
117 | });
118 | it('should error on missing context', function() {
119 | shouldThrow(function() {
120 | var template = CompilerContext.compile('{{hello}}', {assumeObjects: true});
121 |
122 | template();
123 | }, Error);
124 | });
125 |
126 | it('should error on missing data lookup', function() {
127 | shouldThrow(function() {
128 | var template = CompilerContext.compile('{{@hello.bar}}', {assumeObjects: true});
129 |
130 | template();
131 | }, Error);
132 | });
133 |
134 | it('should execute blockHelperMissing', function() {
135 | var template = CompilerContext.compile('{{^hello}}foo{{/hello}}', {assumeObjects: true});
136 |
137 | equals(template({}), 'foo');
138 | });
139 | });
140 | });
141 |
--------------------------------------------------------------------------------
/spec/visitor.js:
--------------------------------------------------------------------------------
1 | describe('Visitor', function() {
2 | if (!Handlebars.Visitor || !Handlebars.print) {
3 | return;
4 | }
5 |
6 | it('should provide coverage', function() {
7 | // Simply run the thing and make sure it does not fail and that all of the
8 | // stub methods are executed
9 | var visitor = new Handlebars.Visitor();
10 | visitor.accept(Handlebars.parse('{{foo}}{{#foo (bar 1 "1" true undefined null) foo=@data}}{{!comment}}{{> bar }} {{/foo}}'));
11 | visitor.accept(Handlebars.parse('{{#> bar }} {{/bar}}'));
12 | visitor.accept(Handlebars.parse('{{#* bar }} {{/bar}}'));
13 | visitor.accept(Handlebars.parse('{{* bar }}'));
14 | });
15 |
16 | it('should traverse to stubs', function() {
17 | var visitor = new Handlebars.Visitor();
18 |
19 | visitor.StringLiteral = function(string) {
20 | equal(string.value, '2');
21 | };
22 | visitor.NumberLiteral = function(number) {
23 | equal(number.value, 1);
24 | };
25 | visitor.BooleanLiteral = function(bool) {
26 | equal(bool.value, true);
27 |
28 | equal(this.parents.length, 3);
29 | equal(this.parents[0].type, 'SubExpression');
30 | equal(this.parents[1].type, 'BlockStatement');
31 | equal(this.parents[2].type, 'Program');
32 | };
33 | visitor.PathExpression = function(id) {
34 | equal(/(foo\.)?bar$/.test(id.original), true);
35 | };
36 | visitor.ContentStatement = function(content) {
37 | equal(content.value, ' ');
38 | };
39 | visitor.CommentStatement = function(comment) {
40 | equal(comment.value, 'comment');
41 | };
42 |
43 | visitor.accept(Handlebars.parse('{{#foo.bar (foo.bar 1 "2" true) foo=@foo.bar}}{{!comment}}{{> bar }} {{/foo.bar}}'));
44 | });
45 |
46 | describe('mutating', function() {
47 | describe('fields', function() {
48 | it('should replace value', function() {
49 | var visitor = new Handlebars.Visitor();
50 |
51 | visitor.mutating = true;
52 | visitor.StringLiteral = function(string) {
53 | return {type: 'NumberLiteral', value: 42, loc: string.loc};
54 | };
55 |
56 | var ast = Handlebars.parse('{{foo foo="foo"}}');
57 | visitor.accept(ast);
58 | equals(Handlebars.print(ast), '{{ PATH:foo [] HASH{foo=NUMBER{42}} }}\n');
59 | });
60 | it('should treat undefined resonse as identity', function() {
61 | var visitor = new Handlebars.Visitor();
62 | visitor.mutating = true;
63 |
64 | var ast = Handlebars.parse('{{foo foo=42}}');
65 | visitor.accept(ast);
66 | equals(Handlebars.print(ast), '{{ PATH:foo [] HASH{foo=NUMBER{42}} }}\n');
67 | });
68 | it('should remove false responses', function() {
69 | var visitor = new Handlebars.Visitor();
70 |
71 | visitor.mutating = true;
72 | visitor.Hash = function() {
73 | return false;
74 | };
75 |
76 | var ast = Handlebars.parse('{{foo foo=42}}');
77 | visitor.accept(ast);
78 | equals(Handlebars.print(ast), '{{ PATH:foo [] }}\n');
79 | });
80 | it('should throw when removing required values', function() {
81 | shouldThrow(function() {
82 | var visitor = new Handlebars.Visitor();
83 |
84 | visitor.mutating = true;
85 | visitor.PathExpression = function() {
86 | return false;
87 | };
88 |
89 | var ast = Handlebars.parse('{{foo 42}}');
90 | visitor.accept(ast);
91 | }, Handlebars.Exception, 'MustacheStatement requires path');
92 | });
93 | it('should throw when returning non-node responses', function() {
94 | shouldThrow(function() {
95 | var visitor = new Handlebars.Visitor();
96 |
97 | visitor.mutating = true;
98 | visitor.PathExpression = function() {
99 | return {};
100 | };
101 |
102 | var ast = Handlebars.parse('{{foo 42}}');
103 | visitor.accept(ast);
104 | }, Handlebars.Exception, 'Unexpected node type "undefined" found when accepting path on MustacheStatement');
105 | });
106 | });
107 | describe('arrays', function() {
108 | it('should replace value', function() {
109 | var visitor = new Handlebars.Visitor();
110 |
111 | visitor.mutating = true;
112 | visitor.StringLiteral = function(string) {
113 | return {type: 'NumberLiteral', value: 42, loc: string.locInfo};
114 | };
115 |
116 | var ast = Handlebars.parse('{{foo "foo"}}');
117 | visitor.accept(ast);
118 | equals(Handlebars.print(ast), '{{ PATH:foo [NUMBER{42}] }}\n');
119 | });
120 | it('should treat undefined resonse as identity', function() {
121 | var visitor = new Handlebars.Visitor();
122 | visitor.mutating = true;
123 |
124 | var ast = Handlebars.parse('{{foo 42}}');
125 | visitor.accept(ast);
126 | equals(Handlebars.print(ast), '{{ PATH:foo [NUMBER{42}] }}\n');
127 | });
128 | it('should remove false responses', function() {
129 | var visitor = new Handlebars.Visitor();
130 |
131 | visitor.mutating = true;
132 | visitor.NumberLiteral = function() {
133 | return false;
134 | };
135 |
136 | var ast = Handlebars.parse('{{foo 42}}');
137 | visitor.accept(ast);
138 | equals(Handlebars.print(ast), '{{ PATH:foo [] }}\n');
139 | });
140 | });
141 | });
142 | });
143 |
--------------------------------------------------------------------------------
/src/handlebars.l:
--------------------------------------------------------------------------------
1 |
2 | %x mu emu com raw
3 |
4 | %{
5 |
6 | function strip(start, end) {
7 | return yytext = yytext.substr(start, yyleng-end);
8 | }
9 |
10 | %}
11 |
12 | LEFT_STRIP "~"
13 | RIGHT_STRIP "~"
14 |
15 | LOOKAHEAD [=~}\s\/.)|]
16 | LITERAL_LOOKAHEAD [~}\s)]
17 |
18 | /*
19 | ID is the inverse of control characters.
20 | Control characters ranges:
21 | [\s] Whitespace
22 | [!"#%-,\./] !, ", #, %, &, ', (, ), *, +, ,, ., /, Exceptions in range: $, -
23 | [;->@] ;, <, =, >, @, Exceptions in range: :, ?
24 | [\[-\^`] [, \, ], ^, `, Exceptions in range: _
25 | [\{-~] {, |, }, ~
26 | */
27 | ID [^\s!"#%-,\.\/;->@\[-\^`\{-~]+/{LOOKAHEAD}
28 |
29 | %%
30 |
31 | [^\x00]*?/("{{") {
32 | if(yytext.slice(-2) === "\\\\") {
33 | strip(0,1);
34 | this.begin("mu");
35 | } else if(yytext.slice(-1) === "\\") {
36 | strip(0,1);
37 | this.begin("emu");
38 | } else {
39 | this.begin("mu");
40 | }
41 | if(yytext) return 'CONTENT';
42 | }
43 |
44 | [^\x00]+ return 'CONTENT';
45 |
46 | // marks CONTENT up to the next mustache or escaped mustache
47 | [^\x00]{2,}?/("{{"|"\\{{"|"\\\\{{"|<>) {
48 | this.popState();
49 | return 'CONTENT';
50 | }
51 |
52 | // nested raw block will create stacked 'raw' condition
53 | "{{{{"/[^/] this.begin('raw'); return 'CONTENT';
54 | "{{{{/"[^\s!"#%-,\.\/;->@\[-\^`\{-~]+/[=}\s\/.]"}}}}" {
55 | this.popState();
56 | // Should be using `this.topState()` below, but it currently
57 | // returns the second top instead of the first top. Opened an
58 | // issue about it at https://github.com/zaach/jison/issues/291
59 | if (this.conditionStack[this.conditionStack.length-1] === 'raw') {
60 | return 'CONTENT';
61 | } else {
62 | yytext = yytext.substr(5, yyleng-9);
63 | return 'END_RAW_BLOCK';
64 | }
65 | }
66 | [^\x00]*?/("{{{{") { return 'CONTENT'; }
67 |
68 | [\s\S]*?"--"{RIGHT_STRIP}?"}}" {
69 | this.popState();
70 | return 'COMMENT';
71 | }
72 |
73 | "(" return 'OPEN_SEXPR';
74 | ")" return 'CLOSE_SEXPR';
75 |
76 | "{{{{" { return 'OPEN_RAW_BLOCK'; }
77 | "}}}}" {
78 | this.popState();
79 | this.begin('raw');
80 | return 'CLOSE_RAW_BLOCK';
81 | }
82 | "{{"{LEFT_STRIP}?">" return 'OPEN_PARTIAL';
83 | "{{"{LEFT_STRIP}?"#>" return 'OPEN_PARTIAL_BLOCK';
84 | "{{"{LEFT_STRIP}?"#""*"? return 'OPEN_BLOCK';
85 | "{{"{LEFT_STRIP}?"/" return 'OPEN_ENDBLOCK';
86 | "{{"{LEFT_STRIP}?"^"\s*{RIGHT_STRIP}?"}}" this.popState(); return 'INVERSE';
87 | "{{"{LEFT_STRIP}?\s*"else"\s*{RIGHT_STRIP}?"}}" this.popState(); return 'INVERSE';
88 | "{{"{LEFT_STRIP}?"^" return 'OPEN_INVERSE';
89 | "{{"{LEFT_STRIP}?\s*"else" return 'OPEN_INVERSE_CHAIN';
90 | "{{"{LEFT_STRIP}?"{" return 'OPEN_UNESCAPED';
91 | "{{"{LEFT_STRIP}?"&" return 'OPEN';
92 | "{{"{LEFT_STRIP}?"!--" {
93 | this.unput(yytext);
94 | this.popState();
95 | this.begin('com');
96 | }
97 | "{{"{LEFT_STRIP}?"!"[\s\S]*?"}}" {
98 | this.popState();
99 | return 'COMMENT';
100 | }
101 | "{{"{LEFT_STRIP}?"*"? return 'OPEN';
102 |
103 | "=" return 'EQUALS';
104 | ".." return 'ID';
105 | "."/{LOOKAHEAD} return 'ID';
106 | [\/.] return 'SEP';
107 | \s+ // ignore whitespace
108 | "}"{RIGHT_STRIP}?"}}" this.popState(); return 'CLOSE_UNESCAPED';
109 | {RIGHT_STRIP}?"}}" this.popState(); return 'CLOSE';
110 | '"'("\\"["]|[^"])*'"' yytext = strip(1,2).replace(/\\"/g,'"'); return 'STRING';
111 | "'"("\\"[']|[^'])*"'" yytext = strip(1,2).replace(/\\'/g,"'"); return 'STRING';
112 | "@" return 'DATA';
113 | "true"/{LITERAL_LOOKAHEAD} return 'BOOLEAN';
114 | "false"/{LITERAL_LOOKAHEAD} return 'BOOLEAN';
115 | "undefined"/{LITERAL_LOOKAHEAD} return 'UNDEFINED';
116 | "null"/{LITERAL_LOOKAHEAD} return 'NULL';
117 | \-?[0-9]+(?:\.[0-9]+)?/{LITERAL_LOOKAHEAD} return 'NUMBER';
118 | "as"\s+"|" return 'OPEN_BLOCK_PARAMS';
119 | "|" return 'CLOSE_BLOCK_PARAMS';
120 |
121 | {ID} return 'ID';
122 |
123 | '['('\\]'|[^\]])*']' yytext = yytext.replace(/\\([\\\]])/g,'$1'); return 'ID';
124 | . return 'INVALID';
125 |
126 | <> return 'EOF';
127 |
--------------------------------------------------------------------------------
/bench/util/benchwarmer.js:
--------------------------------------------------------------------------------
1 | var _ = require('underscore'),
2 | Benchmark = require('benchmark');
3 |
4 | function BenchWarmer() {
5 | this.benchmarks = [];
6 | this.currentBenches = [];
7 | this.names = [];
8 | this.times = {};
9 | this.minimum = Infinity;
10 | this.maximum = -Infinity;
11 | this.errors = {};
12 | }
13 |
14 | var print = require('sys').print;
15 |
16 | BenchWarmer.prototype = {
17 | winners: function(benches) {
18 | return Benchmark.filter(benches, 'fastest');
19 | },
20 | suite: function(suite, fn) {
21 | this.suiteName = suite;
22 | this.times[suite] = {};
23 | this.first = true;
24 |
25 | var self = this;
26 |
27 | fn(function(name, benchFn) {
28 | self.push(name, benchFn);
29 | });
30 | },
31 | push: function(name, fn) {
32 | if (this.names.indexOf(name) == -1) {
33 | this.names.push(name);
34 | }
35 |
36 | var first = this.first, suiteName = this.suiteName, self = this;
37 | this.first = false;
38 |
39 | var bench = new Benchmark(fn, {
40 | name: this.suiteName + ': ' + name,
41 | onComplete: function() {
42 | if (first) { self.startLine(suiteName); }
43 | self.writeBench(bench);
44 | self.currentBenches.push(bench);
45 | }, onError: function() {
46 | self.errors[this.name] = this;
47 | }
48 | });
49 | bench.suiteName = this.suiteName;
50 | bench.benchName = name;
51 |
52 | this.benchmarks.push(bench);
53 | },
54 |
55 | bench: function(callback) {
56 | var self = this;
57 |
58 | this.printHeader('ops/msec', true);
59 |
60 | Benchmark.invoke(this.benchmarks, {
61 | name: 'run',
62 | onComplete: function() {
63 | self.scaleTimes();
64 |
65 | self.startLine('');
66 |
67 | print('\n');
68 | self.printHeader('scaled');
69 | _.each(self.scaled, function(value, name) {
70 | self.startLine(name);
71 |
72 | _.each(self.names, function(lang) {
73 | self.writeValue(value[lang] || '');
74 | });
75 | });
76 | print('\n');
77 |
78 | var errors = false, prop, bench;
79 | for (prop in self.errors) {
80 | if (self.errors.hasOwnProperty(prop)
81 | && self.errors[prop].error.message !== 'EWOT') {
82 | errors = true;
83 | break;
84 | }
85 | }
86 |
87 | if (errors) {
88 | print('\n\nErrors:\n');
89 | for (prop in self.errors) {
90 | if (self.errors.hasOwnProperty(prop)
91 | && self.errors[prop].error.message !== 'EWOT') {
92 | bench = self.errors[prop];
93 | print('\n' + bench.name + ':\n');
94 | print(bench.error.message);
95 | if (bench.error.stack) {
96 | print(bench.error.stack.join('\n'));
97 | }
98 | print('\n');
99 | }
100 | }
101 | }
102 |
103 | callback();
104 | }
105 | });
106 |
107 | print('\n');
108 | },
109 |
110 | scaleTimes: function() {
111 | var scaled = this.scaled = {};
112 | _.each(this.times, function(times, name) {
113 | var output = scaled[name] = {};
114 |
115 | _.each(times, function(time, lang) {
116 | output[lang] = ((time - this.minimum) / (this.maximum - this.minimum) * 100).toFixed(2);
117 | }, this);
118 | }, this);
119 | },
120 |
121 | printHeader: function(title, winners) {
122 | var benchSize = 0, names = this.names, i, l;
123 |
124 | for (i = 0, l = names.length; i < l; i++) {
125 | var name = names[i];
126 |
127 | if (benchSize < name.length) { benchSize = name.length; }
128 | }
129 |
130 | this.nameSize = benchSize + 2;
131 | this.benchSize = 20;
132 | var horSize = 0;
133 |
134 | this.startLine(title);
135 | horSize = horSize + this.benchSize;
136 | for (i = 0, l = names.length; i < l; i++) {
137 | this.writeValue(names[i]);
138 | horSize = horSize + this.benchSize;
139 | }
140 |
141 | if (winners) {
142 | print('WINNER(S)');
143 | horSize = horSize + 'WINNER(S)'.length;
144 | }
145 |
146 | print('\n' + new Array(horSize + 1).join('-'));
147 | },
148 |
149 | startLine: function(name) {
150 | var winners = Benchmark.map(this.winners(this.currentBenches), function(bench) {
151 | return bench.name.split(': ')[1];
152 | });
153 |
154 | this.currentBenches = [];
155 |
156 | print(winners.join(', '));
157 | print('\n');
158 |
159 | if (name) {
160 | this.writeValue(name);
161 | }
162 | },
163 | writeBench: function(bench) {
164 | var out;
165 |
166 | if (!bench.error) {
167 | var count = bench.hz,
168 | moe = count * bench.stats.rme / 100,
169 | minimum,
170 | maximum;
171 |
172 | count = Math.round(count / 1000);
173 | moe = Math.round(moe / 1000);
174 | minimum = count - moe;
175 | maximum = count + moe;
176 |
177 | out = count + ' ±' + moe + ' (' + bench.cycles + ')';
178 |
179 | this.times[bench.suiteName][bench.benchName] = count;
180 | this.minimum = Math.min(this.minimum, minimum);
181 | this.maximum = Math.max(this.maximum, maximum);
182 | } else if (bench.error.message === 'EWOT') {
183 | out = 'NA';
184 | } else {
185 | out = 'E';
186 | }
187 | this.writeValue(out);
188 | },
189 |
190 | writeValue: function(out) {
191 | var padding = this.benchSize - out.length + 1;
192 | out = out + new Array(padding).join(' ');
193 | print(out);
194 | }
195 | };
196 |
197 | module.exports = BenchWarmer;
198 |
--------------------------------------------------------------------------------
/lib/handlebars/compiler/helpers.js:
--------------------------------------------------------------------------------
1 | import Exception from '../exception';
2 |
3 | function validateClose(open, close) {
4 | close = close.path ? close.path.original : close;
5 |
6 | if (open.path.original !== close) {
7 | let errorNode = {loc: open.path.loc};
8 |
9 | throw new Exception(open.path.original + " doesn't match " + close, errorNode);
10 | }
11 | }
12 |
13 | export function SourceLocation(source, locInfo) {
14 | this.source = source;
15 | this.start = {
16 | line: locInfo.first_line,
17 | column: locInfo.first_column
18 | };
19 | this.end = {
20 | line: locInfo.last_line,
21 | column: locInfo.last_column
22 | };
23 | }
24 |
25 | export function id(token) {
26 | if (/^\[.*\]$/.test(token)) {
27 | return token.substr(1, token.length - 2);
28 | } else {
29 | return token;
30 | }
31 | }
32 |
33 | export function stripFlags(open, close) {
34 | return {
35 | open: open.charAt(2) === '~',
36 | close: close.charAt(close.length - 3) === '~'
37 | };
38 | }
39 |
40 | export function stripComment(comment) {
41 | return comment.replace(/^\{\{~?\!-?-?/, '')
42 | .replace(/-?-?~?\}\}$/, '');
43 | }
44 |
45 | export function preparePath(data, parts, loc) {
46 | loc = this.locInfo(loc);
47 |
48 | let original = data ? '@' : '',
49 | dig = [],
50 | depth = 0,
51 | depthString = '';
52 |
53 | for (let i = 0, l = parts.length; i < l; i++) {
54 | let part = parts[i].part,
55 | // If we have [] syntax then we do not treat path references as operators,
56 | // i.e. foo.[this] resolves to approximately context.foo['this']
57 | isLiteral = parts[i].original !== part;
58 | original += (parts[i].separator || '') + part;
59 |
60 | if (!isLiteral && (part === '..' || part === '.' || part === 'this')) {
61 | if (dig.length > 0) {
62 | throw new Exception('Invalid path: ' + original, {loc});
63 | } else if (part === '..') {
64 | depth++;
65 | depthString += '../';
66 | }
67 | } else {
68 | dig.push(part);
69 | }
70 | }
71 |
72 | return {
73 | type: 'PathExpression',
74 | data,
75 | depth,
76 | parts: dig,
77 | original,
78 | loc
79 | };
80 | }
81 |
82 | export function prepareMustache(path, params, hash, open, strip, locInfo) {
83 | // Must use charAt to support IE pre-10
84 | let escapeFlag = open.charAt(3) || open.charAt(2),
85 | escaped = escapeFlag !== '{' && escapeFlag !== '&';
86 |
87 | let decorator = (/\*/.test(open));
88 | return {
89 | type: decorator ? 'Decorator' : 'MustacheStatement',
90 | path,
91 | params,
92 | hash,
93 | escaped,
94 | strip,
95 | loc: this.locInfo(locInfo)
96 | };
97 | }
98 |
99 | export function prepareRawBlock(openRawBlock, contents, close, locInfo) {
100 | validateClose(openRawBlock, close);
101 |
102 | locInfo = this.locInfo(locInfo);
103 | let program = {
104 | type: 'Program',
105 | body: contents,
106 | strip: {},
107 | loc: locInfo
108 | };
109 |
110 | return {
111 | type: 'BlockStatement',
112 | path: openRawBlock.path,
113 | params: openRawBlock.params,
114 | hash: openRawBlock.hash,
115 | program,
116 | openStrip: {},
117 | inverseStrip: {},
118 | closeStrip: {},
119 | loc: locInfo
120 | };
121 | }
122 |
123 | export function prepareBlock(openBlock, program, inverseAndProgram, close, inverted, locInfo) {
124 | if (close && close.path) {
125 | validateClose(openBlock, close);
126 | }
127 |
128 | let decorator = (/\*/.test(openBlock.open));
129 |
130 | program.blockParams = openBlock.blockParams;
131 |
132 | let inverse,
133 | inverseStrip;
134 |
135 | if (inverseAndProgram) {
136 | if (decorator) {
137 | throw new Exception('Unexpected inverse block on decorator', inverseAndProgram);
138 | }
139 |
140 | if (inverseAndProgram.chain) {
141 | inverseAndProgram.program.body[0].closeStrip = close.strip;
142 | }
143 |
144 | inverseStrip = inverseAndProgram.strip;
145 | inverse = inverseAndProgram.program;
146 | }
147 |
148 | if (inverted) {
149 | inverted = inverse;
150 | inverse = program;
151 | program = inverted;
152 | }
153 |
154 | return {
155 | type: decorator ? 'DecoratorBlock' : 'BlockStatement',
156 | path: openBlock.path,
157 | params: openBlock.params,
158 | hash: openBlock.hash,
159 | program,
160 | inverse,
161 | openStrip: openBlock.strip,
162 | inverseStrip,
163 | closeStrip: close && close.strip,
164 | loc: this.locInfo(locInfo)
165 | };
166 | }
167 |
168 | export function prepareProgram(statements, loc) {
169 | if (!loc && statements.length) {
170 | const firstLoc = statements[0].loc,
171 | lastLoc = statements[statements.length - 1].loc;
172 |
173 | /* istanbul ignore else */
174 | if (firstLoc && lastLoc) {
175 | loc = {
176 | source: firstLoc.source,
177 | start: {
178 | line: firstLoc.start.line,
179 | column: firstLoc.start.column
180 | },
181 | end: {
182 | line: lastLoc.end.line,
183 | column: lastLoc.end.column
184 | }
185 | };
186 | }
187 | }
188 |
189 | return {
190 | type: 'Program',
191 | body: statements,
192 | strip: {},
193 | loc: loc
194 | };
195 | }
196 |
197 |
198 | export function preparePartialBlock(open, program, close, locInfo) {
199 | validateClose(open, close);
200 |
201 | return {
202 | type: 'PartialBlockStatement',
203 | name: open.path,
204 | params: open.params,
205 | hash: open.hash,
206 | program,
207 | openStrip: open.strip,
208 | closeStrip: close && close.strip,
209 | loc: this.locInfo(locInfo)
210 | };
211 | }
212 |
213 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "globals": {
3 | "self": false
4 | },
5 | "env": {
6 | "node": true
7 | },
8 | "ecmaFeatures": {
9 | // Enabling features that can be implemented without polyfills. Want to avoid polyfills at this time.
10 | "arrowFunctions": true,
11 | "blockBindings": true,
12 | "defaultParams": true,
13 | "destructuring": true,
14 | "modules": true,
15 | "objectLiteralComputedProperties": true,
16 | "objectLiteralDuplicateProperties": true,
17 | "objectLiteralShorthandMethods": true,
18 | "objectLiteralShorthandProperties": true,
19 | "restParams": true,
20 | "spread": true,
21 | "templateStrings": true
22 | },
23 | "rules": {
24 | // Possible Errors //
25 | //-----------------//
26 |
27 | "comma-dangle": [2, "never"],
28 | "no-cond-assign": [2, "except-parens"],
29 |
30 | // Allow for debugging
31 | "no-console": 1,
32 |
33 | "no-constant-condition": 2,
34 | "no-control-regex": 2,
35 |
36 | // Allow for debugging
37 | "no-debugger": 1,
38 |
39 | "no-dupe-args": 2,
40 | "no-dupe-keys": 2,
41 | "no-duplicate-case": 2,
42 | "no-empty": 2,
43 | "no-empty-character-class": 2,
44 | "no-ex-assign": 2,
45 | "no-extra-boolean-cast": 2,
46 | "no-extra-parens": 0,
47 | "no-extra-semi": 2,
48 | "no-func-assign": 0,
49 |
50 | // Stylistic... might consider disallowing in the future
51 | "no-inner-declarations": 0,
52 |
53 | "no-invalid-regexp": 2,
54 | "no-irregular-whitespace": 2,
55 | "no-negated-in-lhs": 2,
56 | "no-obj-calls": 2,
57 | "no-regex-spaces": 2,
58 | "quote-props": [2, "as-needed", {"keywords": true}],
59 | "no-sparse-arrays": 0,
60 |
61 | // Optimizer and coverage will handle/highlight this and can be useful for debugging
62 | "no-unreachable": 1,
63 |
64 | "use-isnan": 2,
65 | "valid-jsdoc": 0,
66 | "valid-typeof": 2,
67 |
68 |
69 | // Best Practices //
70 | //----------------//
71 | "block-scoped-var": 0,
72 | "complexity": 0,
73 | "consistent-return": 0,
74 | "curly": 2,
75 | "default-case": 1,
76 | "dot-notation": [2, {"allowKeywords": false}],
77 | "eqeqeq": 0,
78 | "guard-for-in": 1,
79 | "no-alert": 2,
80 | "no-caller": 2,
81 | "no-div-regex": 1,
82 | "no-else-return": 0,
83 | "no-empty-label": 2,
84 | "no-eq-null": 0,
85 | "no-eval": 2,
86 | "no-extend-native": 2,
87 | "no-extra-bind": 2,
88 | "no-fallthrough": 2,
89 | "no-floating-decimal": 2,
90 | "no-implied-eval": 2,
91 | "no-iterator": 2,
92 | "no-labels": 2,
93 | "no-lone-blocks": 2,
94 | "no-loop-func": 2,
95 | "no-multi-spaces": 2,
96 | "no-multi-str": 1,
97 | "no-native-reassign": 2,
98 | "no-new": 2,
99 | "no-new-func": 2,
100 | "no-new-wrappers": 2,
101 | "no-octal": 2,
102 | "no-octal-escape": 2,
103 | "no-param-reassign": 0,
104 | "no-process-env": 2,
105 | "no-proto": 2,
106 | "no-redeclare": 2,
107 | "no-return-assign": 2,
108 | "no-script-url": 2,
109 | "no-self-compare": 2,
110 | "no-sequences": 2,
111 | "no-throw-literal": 2,
112 | "no-unused-expressions": 2,
113 | "no-void": 0,
114 | "no-warning-comments": 1,
115 | "no-with": 2,
116 | "radix": 2,
117 | "vars-on-top": 0,
118 | "wrap-iife": 2,
119 | "yoda": 0,
120 |
121 |
122 | // Strict //
123 | //--------//
124 | "strict": 0,
125 |
126 |
127 | // Variables //
128 | //-----------//
129 | "no-catch-shadow": 2,
130 | "no-delete-var": 2,
131 | "no-label-var": 2,
132 | "no-shadow": 0,
133 | "no-shadow-restricted-names": 2,
134 | "no-undef": 2,
135 | "no-undef-init": 2,
136 | "no-undefined": 0,
137 | "no-unused-vars": [2, {"vars": "all", "args": "after-used"}],
138 | "no-use-before-define": [2, "nofunc"],
139 |
140 |
141 | // Node.js //
142 | //---------//
143 | // Others left to environment defaults
144 | "no-mixed-requires": 0,
145 |
146 |
147 | // Stylistic //
148 | //-----------//
149 | "indent": 0,
150 | "brace-style": [2, "1tbs", {"allowSingleLine": true}],
151 | "camelcase": 2,
152 | "comma-spacing": [2, {"before": false, "after": true}],
153 | "comma-style": [2, "last"],
154 | "consistent-this": [1, "self"],
155 | "eol-last": 2,
156 | "func-names": 0,
157 | "func-style": [2, "declaration"],
158 | "key-spacing": [2, {
159 | "beforeColon": false,
160 | "afterColon": true
161 | }],
162 | "max-nested-callbacks": 0,
163 | "new-cap": 2,
164 | "new-parens": 2,
165 | "newline-after-var": 0,
166 | "no-array-constructor": 2,
167 | "no-continue": 0,
168 | "no-inline-comments": 0,
169 | "no-lonely-if": 2,
170 | "no-mixed-spaces-and-tabs": 2,
171 | "no-multiple-empty-lines": 0,
172 | "no-nested-ternary": 1,
173 | "no-new-object": 2,
174 | "no-spaced-func": 2,
175 | "no-ternary": 0,
176 | "no-trailing-spaces": 2,
177 | "no-underscore-dangle": 0,
178 | "no-extra-parens": [2, "functions"],
179 | "one-var": 0,
180 | "operator-assignment": 0,
181 | "padded-blocks": 0,
182 | "quote-props": 0,
183 | "quotes": [2, "single", "avoid-escape"],
184 | "semi": 2,
185 | "semi-spacing": [2, {"before": false, "after": true}],
186 | "sort-vars": 0,
187 | "space-after-keywords": [2, "always"],
188 | "space-before-blocks": [2, "always"],
189 | "space-before-function-paren": [2, {"anonymous": "never", "named": "never"}],
190 | "space-in-brackets": 0,
191 | "space-in-parens": [2, "never"],
192 | "space-infix-ops": 2,
193 | "space-return-throw-case": 2,
194 | "space-unary-ops": 2,
195 | "spaced-comment": [2, "always", {"markers": [","]}],
196 | "wrap-regex": 1,
197 |
198 | "no-var": 1
199 | }
200 | }
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-process-env */
2 | module.exports = function(grunt) {
3 |
4 | grunt.initConfig({
5 | pkg: grunt.file.readJSON('package.json'),
6 |
7 | eslint: {
8 | options: {
9 | },
10 | files: [
11 | '*.js',
12 | 'bench/**/*.js',
13 | 'tasks/**/*.js',
14 | 'lib/**/!(*.min|parser).js',
15 | 'spec/**/!(*.amd|json2|require).js'
16 | ]
17 | },
18 |
19 | clean: ['tmp', 'dist', 'lib/handlebars/compiler/parser.js'],
20 |
21 | copy: {
22 | dist: {
23 | options: {
24 | processContent: function(content) {
25 | return grunt.template.process('/**!\n\n @license\n <%= pkg.name %> v<%= pkg.version %>\n\n<%= grunt.file.read("LICENSE") %>\n*/\n')
26 | + content;
27 | }
28 | },
29 | files: [
30 | {expand: true, cwd: 'dist/', src: ['*.js'], dest: 'dist/'}
31 | ]
32 | },
33 | components: {
34 | files: [
35 | {expand: true, cwd: 'components/', src: ['**'], dest: 'dist/components'},
36 | {expand: true, cwd: 'dist/', src: ['*.js'], dest: 'dist/components'}
37 | ]
38 | }
39 | },
40 |
41 | babel: {
42 | options: {
43 | sourceMaps: 'inline',
44 | loose: ['es6.modules'],
45 | auxiliaryCommentBefore: 'istanbul ignore next'
46 | },
47 | cjs: {
48 | files: [{
49 | cwd: 'lib/',
50 | expand: true,
51 | src: '**/!(index).js',
52 | dest: 'dist/cjs/'
53 | }]
54 | }
55 | },
56 | webpack: {
57 | options: {
58 | context: __dirname,
59 | output: {
60 | path: 'dist/',
61 | library: 'Handlebars',
62 | libraryTarget: 'umd'
63 | }
64 | },
65 | handlebars: {
66 | entry: './dist/cjs/handlebars.js',
67 | output: {
68 | filename: 'handlebars.js'
69 | }
70 | },
71 | runtime: {
72 | entry: './dist/cjs/handlebars.runtime.js',
73 | output: {
74 | filename: 'handlebars.runtime.js'
75 | }
76 | }
77 | },
78 |
79 | uglify: {
80 | options: {
81 | mangle: true,
82 | compress: true,
83 | preserveComments: /(?:^!|@(?:license|preserve|cc_on))/
84 | },
85 | dist: {
86 | files: [{
87 | cwd: 'dist/',
88 | expand: true,
89 | src: ['handlebars*.js', '!*.min.js'],
90 | dest: 'dist/',
91 | rename: function(dest, src) {
92 | return dest + src.replace(/\.js$/, '.min.js');
93 | }
94 | }]
95 | }
96 | },
97 |
98 | concat: {
99 | tests: {
100 | src: ['spec/!(require).js'],
101 | dest: 'tmp/tests.js'
102 | }
103 | },
104 |
105 | connect: {
106 | server: {
107 | options: {
108 | base: '.',
109 | hostname: '*',
110 | port: 9999
111 | }
112 | }
113 | },
114 | 'saucelabs-mocha': {
115 | all: {
116 | options: {
117 | build: process.env.TRAVIS_JOB_ID,
118 | urls: ['http://localhost:9999/spec/?headless=true'],
119 | detailedError: true,
120 | concurrency: 4,
121 | browsers: [
122 | {browserName: 'chrome'},
123 | {browserName: 'firefox', platform: 'Linux'},
124 | {browserName: 'safari', version: 9, platform: 'OS X 10.11'},
125 | {browserName: 'safari', version: 8, platform: 'OS X 10.10'},
126 | {browserName: 'internet explorer', version: 11, platform: 'Windows 8.1'},
127 | {browserName: 'internet explorer', version: 10, platform: 'Windows 8'}
128 | ]
129 | }
130 | },
131 | sanity: {
132 | options: {
133 | build: process.env.TRAVIS_JOB_ID,
134 | urls: ['http://localhost:9999/spec/umd.html?headless=true', 'http://localhost:9999/spec/umd-runtime.html?headless=true'],
135 | detailedError: true,
136 | concurrency: 2,
137 | browsers: [
138 | {browserName: 'chrome'},
139 | {browserName: 'internet explorer', version: 10, platform: 'Windows 8'}
140 | ]
141 | }
142 | }
143 | },
144 |
145 | watch: {
146 | scripts: {
147 | options: {
148 | atBegin: true
149 | },
150 |
151 | files: ['src/*', 'lib/**/*.js', 'spec/**/*.js'],
152 | tasks: ['build', 'tests', 'test']
153 | }
154 | }
155 | });
156 |
157 | // Build a new version of the library
158 | this.registerTask('build', 'Builds a distributable version of the current project', [
159 | 'eslint',
160 | 'parser',
161 | 'node',
162 | 'globals']);
163 |
164 | this.registerTask('node', ['babel:cjs']);
165 | this.registerTask('globals', ['webpack']);
166 | this.registerTask('tests', ['concat:tests']);
167 |
168 | this.registerTask('release', 'Build final packages', ['eslint', 'uglify', 'test:min', 'copy:dist', 'copy:components']);
169 |
170 | // Load tasks from npm
171 | grunt.loadNpmTasks('grunt-contrib-clean');
172 | grunt.loadNpmTasks('grunt-contrib-concat');
173 | grunt.loadNpmTasks('grunt-contrib-connect');
174 | grunt.loadNpmTasks('grunt-contrib-copy');
175 | grunt.loadNpmTasks('grunt-contrib-uglify');
176 | grunt.loadNpmTasks('grunt-contrib-watch');
177 | grunt.loadNpmTasks('grunt-babel');
178 | grunt.loadNpmTasks('grunt-eslint');
179 | grunt.loadNpmTasks('grunt-saucelabs');
180 | grunt.loadNpmTasks('grunt-webpack');
181 |
182 | grunt.task.loadTasks('tasks');
183 |
184 | grunt.registerTask('bench', ['metrics']);
185 | grunt.registerTask('sauce', process.env.SAUCE_USERNAME ? ['tests', 'connect', 'saucelabs-mocha'] : []);
186 |
187 | grunt.registerTask('travis', process.env.PUBLISH ? ['default', 'sauce', 'metrics', 'publish:latest'] : ['default']);
188 |
189 | grunt.registerTask('dev', ['clean', 'connect', 'watch']);
190 | grunt.registerTask('default', ['clean', 'build', 'test', 'release']);
191 | };
192 |
--------------------------------------------------------------------------------