diff --git a/Gruntfile.js b/Gruntfile.js index db33746e..eadb3ab3 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,28 +1,39 @@ /* eslint-disable no-process-env */ module.exports = function(grunt) { - grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), - clean: ['tmp', 'dist', 'lib/handlebars/compiler/parser.js', 'integration-testing/**/node_modules'], + clean: [ + 'tmp', + 'dist', + 'lib/handlebars/compiler/parser.js', + 'integration-testing/**/node_modules' + ], copy: { dist: { options: { processContent: function(content) { - return grunt.template.process('/**!\n\n @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt Expat\n <%= pkg.name %> v<%= pkg.version %>\n\n<%= grunt.file.read("LICENSE") %>\n*/\n') - + content - + '\n// @license-end\n'; + return ( + grunt.template.process( + '/**!\n\n @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt Expat\n <%= pkg.name %> v<%= pkg.version %>\n\n<%= grunt.file.read("LICENSE") %>\n*/\n' + ) + + content + + '\n// @license-end\n' + ); } }, - files: [ - {expand: true, cwd: 'dist/', src: ['*.js'], dest: 'dist/'} - ] + files: [{ expand: true, cwd: 'dist/', src: ['*.js'], dest: 'dist/' }] }, components: { files: [ - {expand: true, cwd: 'components/', src: ['**'], dest: 'dist/components'}, - {expand: true, cwd: 'dist/', src: ['*.js'], dest: 'dist/components'} + { + expand: true, + cwd: 'components/', + src: ['**'], + dest: 'dist/components' + }, + { expand: true, cwd: 'dist/', src: ['*.js'], dest: 'dist/components' } ] } }, @@ -34,12 +45,14 @@ module.exports = function(grunt) { auxiliaryCommentBefore: 'istanbul ignore next' }, cjs: { - files: [{ - cwd: 'lib/', - expand: true, - src: '**/!(index).js', - dest: 'dist/cjs/' - }] + files: [ + { + cwd: 'lib/', + expand: true, + src: '**/!(index).js', + dest: 'dist/cjs/' + } + ] } }, webpack: { @@ -72,15 +85,17 @@ module.exports = function(grunt) { preserveComments: /(?:^!|@(?:license|preserve|cc_on))/ }, dist: { - files: [{ - cwd: 'dist/', - expand: true, - src: ['handlebars*.js', '!*.min.js'], - dest: 'dist/', - rename: function(dest, src) { - return dest + src.replace(/\.js$/, '.min.js'); + files: [ + { + cwd: 'dist/', + expand: true, + src: ['handlebars*.js', '!*.min.js'], + dest: 'dist/', + rename: function(dest, src) { + return dest + src.replace(/\.js$/, '.min.js'); + } } - }] + ] } }, @@ -108,24 +123,39 @@ module.exports = function(grunt) { detailedError: true, concurrency: 4, browsers: [ - {browserName: 'chrome'}, - {browserName: 'firefox', platform: 'Linux'}, + { browserName: 'chrome' }, + { browserName: 'firefox', platform: 'Linux' }, // {browserName: 'safari', version: 9, platform: 'OS X 10.11'}, // {browserName: 'safari', version: 8, platform: 'OS X 10.10'}, - {browserName: 'internet explorer', version: 11, platform: 'Windows 8.1'}, - {browserName: 'internet explorer', version: 10, platform: 'Windows 8'} + { + browserName: 'internet explorer', + version: 11, + platform: 'Windows 8.1' + }, + { + browserName: 'internet explorer', + version: 10, + platform: 'Windows 8' + } ] } }, sanity: { options: { build: process.env.TRAVIS_JOB_ID, - urls: ['http://localhost:9999/spec/umd.html?headless=true', 'http://localhost:9999/spec/umd-runtime.html?headless=true'], + urls: [ + 'http://localhost:9999/spec/umd.html?headless=true', + 'http://localhost:9999/spec/umd-runtime.html?headless=true' + ], detailedError: true, concurrency: 2, browsers: [ - {browserName: 'chrome'}, - {browserName: 'internet explorer', version: 10, platform: 'Windows 8'} + { browserName: 'chrome' }, + { + browserName: 'internet explorer', + version: 10, + platform: 'Windows 8' + } ] } } @@ -137,7 +167,6 @@ module.exports = function(grunt) { bg: false, fail: true } - }, watch: { @@ -167,9 +196,9 @@ module.exports = function(grunt) { grunt.task.loadTasks('tasks'); this.registerTask( - 'build', - 'Builds a distributable version of the current project', - ['parser', 'node', 'globals'] + 'build', + 'Builds a distributable version of the current project', + ['parser', 'node', 'globals'] ); this.registerTask('node', ['babel:cjs']); @@ -183,13 +212,25 @@ module.exports = function(grunt) { ]); grunt.registerTask('bench', ['metrics']); - grunt.registerTask('sauce', process.env.SAUCE_USERNAME ? ['tests', 'connect', 'saucelabs-mocha'] : []); + grunt.registerTask( + 'sauce', + process.env.SAUCE_USERNAME ? ['tests', 'connect', 'saucelabs-mocha'] : [] + ); // Requires secret properties (saucelabs-credentials etc.) from .travis.yaml - grunt.registerTask('extensive-tests-and-publish-to-aws', ['default', 'bgShell:integrationTests', 'sauce', 'metrics', 'publish:latest']); + grunt.registerTask('extensive-tests-and-publish-to-aws', [ + 'default', + 'bgShell:integrationTests', + 'sauce', + 'metrics', + 'publish:latest' + ]); grunt.registerTask('on-file-change', ['build', 'concat:tests', 'test']); // === Primary tasks === grunt.registerTask('dev', ['clean', 'connect', 'watch']); grunt.registerTask('default', ['clean', 'build', 'test', 'release']); - grunt.registerTask('integration-tests', ['default', 'bgShell:integrationTests']); + grunt.registerTask('integration-tests', [ + 'default', + 'bgShell:integrationTests' + ]); }; diff --git a/bench/dist-size.js b/bench/dist-size.js index d0bd2489..5bbf7ee3 100644 --- a/bench/dist-size.js +++ b/bench/dist-size.js @@ -1,12 +1,14 @@ var async = require('neo-async'), - fs = require('fs'), - zlib = require('zlib'); + fs = require('fs'), + zlib = require('zlib'); module.exports = function(grunt, callback) { var distFiles = fs.readdirSync('dist'), - distSizes = {}; + distSizes = {}; - async.each(distFiles, function(file, callback) { + async.each( + distFiles, + function(file, callback) { var content; try { content = fs.readFileSync('dist/' + file); @@ -32,7 +34,10 @@ module.exports = function(grunt, callback) { }); }, function() { - grunt.log.writeln('Distribution sizes: ' + JSON.stringify(distSizes, undefined, 2)); + grunt.log.writeln( + 'Distribution sizes: ' + JSON.stringify(distSizes, undefined, 2) + ); callback([distSizes]); - }); + } + ); }; diff --git a/bench/index.js b/bench/index.js index 3e357e54..462b046f 100644 --- a/bench/index.js +++ b/bench/index.js @@ -2,7 +2,7 @@ var fs = require('fs'); var metrics = fs.readdirSync(__dirname); metrics.forEach(function(metric) { - if (metric === 'index.js' || !(/(.*)\.js$/.test(metric))) { + if (metric === 'index.js' || !/(.*)\.js$/.test(metric)) { return; } diff --git a/bench/precompile-size.js b/bench/precompile-size.js index 66eacd02..5aca9423 100644 --- a/bench/precompile-size.js +++ b/bench/precompile-size.js @@ -1,5 +1,5 @@ var _ = require('underscore'), - templates = require('./templates'); + templates = require('./templates'); module.exports = function(grunt, callback) { // Deferring to here in case we have a build for parser, etc as part of this grunt exec @@ -8,12 +8,17 @@ module.exports = function(grunt, callback) { var templateSizes = {}; _.each(templates, function(info, template) { var src = info.handlebars, - compiled = Handlebars.precompile(src, {}), - knownHelpers = Handlebars.precompile(src, {knownHelpersOnly: true, knownHelpers: info.helpers}); + compiled = Handlebars.precompile(src, {}), + knownHelpers = Handlebars.precompile(src, { + knownHelpersOnly: true, + knownHelpers: info.helpers + }); templateSizes[template] = compiled.length; templateSizes['knownOnly_' + template] = knownHelpers.length; }); - grunt.log.writeln('Precompiled sizes: ' + JSON.stringify(templateSizes, undefined, 2)); + grunt.log.writeln( + 'Precompiled sizes: ' + JSON.stringify(templateSizes, undefined, 2) + ); callback([templateSizes]); }; diff --git a/bench/templates/arguments.js b/bench/templates/arguments.js index aaa03468..f73d845f 100644 --- a/bench/templates/arguments.js +++ b/bench/templates/arguments.js @@ -8,5 +8,6 @@ module.exports = { bar: true }, - handlebars: '{{foo person "person" 1 true foo=bar foo="person" foo=1 foo=true}}' + handlebars: + '{{foo person "person" 1 true foo=bar foo="person" foo=1 foo=true}}' }; diff --git a/bench/templates/array-each.js b/bench/templates/array-each.js index 50e1c02b..ddf07a6f 100644 --- a/bench/templates/array-each.js +++ b/bench/templates/array-each.js @@ -1,5 +1,12 @@ module.exports = { - context: { names: [{name: 'Moe'}, {name: 'Larry'}, {name: 'Curly'}, {name: 'Shemp'}] }, + context: { + names: [ + { name: 'Moe' }, + { name: 'Larry' }, + { name: 'Curly' }, + { name: 'Shemp' } + ] + }, handlebars: '{{#each names}}{{name}}{{/each}}', dust: '{#names}{name}{/names}', mustache: '{{#names}}{{name}}{{/names}}', diff --git a/bench/templates/array-mustache.js b/bench/templates/array-mustache.js index 220c6fe4..e7b2355a 100644 --- a/bench/templates/array-mustache.js +++ b/bench/templates/array-mustache.js @@ -1,4 +1,11 @@ module.exports = { - context: { names: [{name: 'Moe'}, {name: 'Larry'}, {name: 'Curly'}, {name: 'Shemp'}] }, + context: { + names: [ + { name: 'Moe' }, + { name: 'Larry' }, + { name: 'Curly' }, + { name: 'Shemp' } + ] + }, handlebars: '{{#names}}{{name}}{{/names}}' }; diff --git a/bench/templates/complex.js b/bench/templates/complex.js index 3e5e26c4..545a479c 100644 --- a/bench/templates/complex.js +++ b/bench/templates/complex.js @@ -7,9 +7,9 @@ module.exports = { }, hasItems: true, // To make things fairer in mustache land due to no `{{if}}` construct on arrays items: [ - {name: 'red', current: true, url: '#Red'}, - {name: 'green', current: false, url: '#Green'}, - {name: 'blue', current: false, url: '#Blue'} + { name: 'red', current: true, url: '#Red' }, + { name: 'green', current: false, url: '#Green' }, + { name: 'blue', current: false, url: '#Blue' } ] }, diff --git a/bench/templates/data.js b/bench/templates/data.js index be10e839..4cb969d6 100644 --- a/bench/templates/data.js +++ b/bench/templates/data.js @@ -1,4 +1,11 @@ module.exports = { - context: { names: [{name: 'Moe'}, {name: 'Larry'}, {name: 'Curly'}, {name: 'Shemp'}] }, + context: { + names: [ + { name: 'Moe' }, + { name: 'Larry' }, + { name: 'Curly' }, + { name: 'Shemp' } + ] + }, handlebars: '{{#each names}}{{@index}}{{name}}{{/each}}' }; diff --git a/bench/templates/depth-1.js b/bench/templates/depth-1.js index 0f2576f4..65cb33b7 100644 --- a/bench/templates/depth-1.js +++ b/bench/templates/depth-1.js @@ -1,5 +1,13 @@ module.exports = { - context: { names: [{name: 'Moe'}, {name: 'Larry'}, {name: 'Curly'}, {name: 'Shemp'}], foo: 'bar' }, + context: { + names: [ + { name: 'Moe' }, + { name: 'Larry' }, + { name: 'Curly' }, + { name: 'Shemp' } + ], + foo: 'bar' + }, handlebars: '{{#each names}}{{../foo}}{{/each}}', mustache: '{{#names}}{{foo}}{{/names}}', eco: '<% for item in @names: %><%= @foo %><% end %>' diff --git a/bench/templates/depth-2.js b/bench/templates/depth-2.js index bff6ce85..bef804d1 100644 --- a/bench/templates/depth-2.js +++ b/bench/templates/depth-2.js @@ -1,6 +1,16 @@ module.exports = { - context: { names: [{bat: 'foo', name: ['Moe']}, {bat: 'foo', name: ['Larry']}, {bat: 'foo', name: ['Curly']}, {bat: 'foo', name: ['Shemp']}], foo: 'bar' }, - handlebars: '{{#each names}}{{#each name}}{{../bat}}{{../../foo}}{{/each}}{{/each}}', + context: { + names: [ + { bat: 'foo', name: ['Moe'] }, + { bat: 'foo', name: ['Larry'] }, + { bat: 'foo', name: ['Curly'] }, + { bat: 'foo', name: ['Shemp'] } + ], + foo: 'bar' + }, + handlebars: + '{{#each names}}{{#each name}}{{../bat}}{{../../foo}}{{/each}}{{/each}}', mustache: '{{#names}}{{#name}}{{bat}}{{foo}}{{/name}}{{/names}}', - eco: '<% for item in @names: %><% for child in item.name: %><%= item.bat %><%= @foo %><% end %><% end %>' + eco: + '<% for item in @names: %><% for child in item.name: %><%= item.bat %><%= @foo %><% end %><% end %>' }; diff --git a/bench/templates/index.js b/bench/templates/index.js index 943f9cdf..a718ea38 100644 --- a/bench/templates/index.js +++ b/bench/templates/index.js @@ -2,7 +2,7 @@ var fs = require('fs'); var templates = fs.readdirSync(__dirname); templates.forEach(function(template) { - if (template === 'index.js' || !(/(.*)\.js$/.test(template))) { + if (template === 'index.js' || !/(.*)\.js$/.test(template)) { return; } module.exports[RegExp.$1] = require('./' + RegExp.$1); diff --git a/bench/templates/partial-recursion.js b/bench/templates/partial-recursion.js index b903553b..ce3c32a7 100644 --- a/bench/templates/partial-recursion.js +++ b/bench/templates/partial-recursion.js @@ -1,5 +1,8 @@ module.exports = { - context: { name: '1', kids: [{ name: '1.1', kids: [{name: '1.1.1', kids: []}] }] }, + context: { + name: '1', + kids: [{ name: '1.1', kids: [{ name: '1.1.1', kids: [] }] }] + }, partials: { mustache: { recursion: '{{name}}{{#kids}}{{>recursion}}{{/kids}}' }, handlebars: { recursion: '{{name}}{{#each kids}}{{>recursion}}{{/each}}' } diff --git a/bench/templates/partial.js b/bench/templates/partial.js index 949e9c07..79a9d7df 100644 --- a/bench/templates/partial.js +++ b/bench/templates/partial.js @@ -1,8 +1,16 @@ module.exports = { - context: { peeps: [{name: 'Moe', count: 15}, {name: 'Larry', count: 5}, {name: 'Curly', count: 1}] }, + context: { + peeps: [ + { name: 'Moe', count: 15 }, + { name: 'Larry', count: 5 }, + { name: 'Curly', count: 1 } + ] + }, partials: { mustache: { variables: 'Hello {{name}}! You have {{count}} new messages.' }, - handlebars: { variables: 'Hello {{name}}! You have {{count}} new messages.' } + handlebars: { + variables: 'Hello {{name}}! You have {{count}} new messages.' + } }, handlebars: '{{#each peeps}}{{>variables}}{{/each}}', diff --git a/bench/templates/paths.js b/bench/templates/paths.js index fed039d5..3725e471 100644 --- a/bench/templates/paths.js +++ b/bench/templates/paths.js @@ -1,7 +1,9 @@ module.exports = { - context: { person: { name: {bar: {baz: 'Larry'}}, age: 45 } }, - handlebars: '{{person.name.bar.baz}}{{person.age}}{{person.foo}}{{animal.age}}', + context: { person: { name: { bar: { baz: 'Larry' } }, age: 45 } }, + handlebars: + '{{person.name.bar.baz}}{{person.age}}{{person.foo}}{{animal.age}}', dust: '{person.name.bar.baz}{person.age}{person.foo}{animal.age}', - eco: '<%= @person.name.bar.baz %><%= @person.age %><%= @person.foo %><% if @animal: %><%= @animal.age %><% end %>', + eco: + '<%= @person.name.bar.baz %><%= @person.age %><%= @person.foo %><% if @animal: %><%= @animal.age %><% end %>', mustache: '{{person.name.bar.baz}}{{person.age}}{{person.foo}}{{animal.age}}' }; diff --git a/bench/templates/variables.js b/bench/templates/variables.js index 41e4feaf..fddece1b 100644 --- a/bench/templates/variables.js +++ b/bench/templates/variables.js @@ -1,8 +1,7 @@ module.exports = { - context: {name: 'Mick', count: 30}, + context: { name: 'Mick', count: 30 }, handlebars: 'Hello {{name}}! You have {{count}} new messages.', dust: 'Hello {name}! You have {count} new messages.', mustache: 'Hello {{name}}! You have {{count}} new messages.', eco: 'Hello <%= @name %>! You have <%= @count %> new messages.' }; - diff --git a/bench/throughput.js b/bench/throughput.js index e8ea5d91..b5e3a56a 100644 --- a/bench/throughput.js +++ b/bench/throughput.js @@ -1,19 +1,27 @@ var _ = require('underscore'), - runner = require('./util/template-runner'), - - eco, dust, Handlebars, Mustache; + runner = require('./util/template-runner'), + eco, + dust, + Handlebars, + Mustache; try { dust = require('dustjs-linkedin'); -} catch (err) { /* NOP */ } +} catch (err) { + /* NOP */ +} try { Mustache = require('mustache'); -} catch (err) { /* NOP */ } +} catch (err) { + /* NOP */ +} try { eco = require('eco'); -} catch (err) { /* NOP */ } +} catch (err) { + /* NOP */ +} function error() { throw new Error('EWOT'); @@ -22,21 +30,28 @@ function error() { function makeSuite(bench, name, template, handlebarsOnly) { // Create aliases to minimize any impact from having to walk up the closure tree. var templateName = name, + context = template.context, + partials = template.partials, + handlebarsOut, + compatOut, + dustOut, + ecoOut, + mustacheOut; - context = template.context, - partials = template.partials, - - handlebarsOut, - compatOut, - dustOut, - ecoOut, - mustacheOut; - - var handlebar = Handlebars.compile(template.handlebars, {data: false}), - compat = Handlebars.compile(template.handlebars, {data: false, compat: true}), - options = {helpers: template.helpers}; - _.each(template.partials && template.partials.handlebars, function(partial, partialName) { - Handlebars.registerPartial(partialName, Handlebars.compile(partial, {data: false})); + var handlebar = Handlebars.compile(template.handlebars, { data: false }), + compat = Handlebars.compile(template.handlebars, { + data: false, + compat: true + }), + options = { helpers: template.helpers }; + _.each(template.partials && template.partials.handlebars, function( + partial, + partialName + ) { + Handlebars.registerPartial( + partialName, + Handlebars.compile(partial, { data: false }) + ); }); handlebarsOut = handlebar(context, options); @@ -58,7 +73,9 @@ function makeSuite(bench, name, template, handlebarsOnly) { dustOut = false; dust.loadSource(dust.compile(template.dust, templateName)); - dust.render(templateName, context, function(err, out) { dustOut = out; }); + dust.render(templateName, context, function(err, out) { + dustOut = out; + }); bench('dust', function() { dust.render(templateName, context, function() {}); @@ -84,7 +101,7 @@ function makeSuite(bench, name, template, handlebarsOnly) { if (Mustache) { var mustacheSource = template.mustache, - mustachePartials = partials && partials.mustache; + mustachePartials = partials && partials.mustache; if (mustacheSource) { mustacheOut = Mustache.to_html(mustacheSource, context, mustachePartials); @@ -107,9 +124,16 @@ function makeSuite(bench, name, template, handlebarsOnly) { b = b.replace(/\s/g, ''); if (handlebarsOut !== b) { - throw new Error('Template output mismatch: ' + name - + '\n\nHandlebars: ' + handlebarsOut - + '\n\n' + lang + ': ' + b); + throw new Error( + 'Template output mismatch: ' + + name + + '\n\nHandlebars: ' + + handlebarsOut + + '\n\n' + + lang + + ': ' + + b + ); } } diff --git a/bench/util/benchwarmer.js b/bench/util/benchwarmer.js index 58f138a0..90415bf5 100644 --- a/bench/util/benchwarmer.js +++ b/bench/util/benchwarmer.js @@ -1,5 +1,5 @@ var _ = require('underscore'), - Benchmark = require('benchmark'); + Benchmark = require('benchmark'); function BenchWarmer() { this.benchmarks = []; @@ -33,16 +33,21 @@ BenchWarmer.prototype = { this.names.push(name); } - var first = this.first, suiteName = this.suiteName, self = this; + var first = this.first, + suiteName = this.suiteName, + self = this; this.first = false; var bench = new Benchmark(fn, { name: this.suiteName + ': ' + name, onComplete: function() { - if (first) { self.startLine(suiteName); } + if (first) { + self.startLine(suiteName); + } self.writeBench(bench); self.currentBenches.push(bench); - }, onError: function() { + }, + onError: function() { self.errors[this.name] = this; } }); @@ -75,10 +80,14 @@ BenchWarmer.prototype = { }); print('\n'); - var errors = false, prop, bench; + var errors = false, + prop, + bench; for (prop in self.errors) { - if (Object.prototype.hasOwnProperty.call(self, prop) - && self.errors[prop].error.message !== 'EWOT') { + if ( + Object.prototype.hasOwnProperty.call(self, prop) && + self.errors[prop].error.message !== 'EWOT' + ) { errors = true; break; } @@ -107,23 +116,39 @@ BenchWarmer.prototype = { }, scaleTimes: function() { - var scaled = this.scaled = {}; - _.each(this.times, function(times, name) { - var output = scaled[name] = {}; + var scaled = (this.scaled = {}); + _.each( + this.times, + function(times, name) { + var output = (scaled[name] = {}); - _.each(times, function(time, lang) { - output[lang] = ((time - this.minimum) / (this.maximum - this.minimum) * 100).toFixed(2); - }, this); - }, this); + _.each( + times, + function(time, lang) { + output[lang] = ( + ((time - this.minimum) / (this.maximum - this.minimum)) * + 100 + ).toFixed(2); + }, + this + ); + }, + this + ); }, printHeader: function(title, winners) { - var benchSize = 0, names = this.names, i, l; + var benchSize = 0, + names = this.names, + i, + l; for (i = 0, l = names.length; i < l; i++) { var name = names[i]; - if (benchSize < name.length) { benchSize = name.length; } + if (benchSize < name.length) { + benchSize = name.length; + } } this.nameSize = benchSize + 2; @@ -146,7 +171,9 @@ BenchWarmer.prototype = { }, startLine: function(name) { - var winners = Benchmark.map(this.winners(this.currentBenches), function(bench) { + var winners = Benchmark.map(this.winners(this.currentBenches), function( + bench + ) { return bench.name.split(': ')[1]; }); @@ -164,9 +191,9 @@ BenchWarmer.prototype = { if (!bench.error) { var count = bench.hz, - moe = count * bench.stats.rme / 100, - minimum, - maximum; + moe = (count * bench.stats.rme) / 100, + minimum, + maximum; count = Math.round(count / 1000); moe = Math.round(moe / 1000); diff --git a/bench/util/template-runner.js b/bench/util/template-runner.js index b492ff3a..370e157e 100644 --- a/bench/util/template-runner.js +++ b/bench/util/template-runner.js @@ -1,12 +1,12 @@ var _ = require('underscore'), - BenchWarmer = require('./benchwarmer'), - templates = require('../templates'); + BenchWarmer = require('./benchwarmer'), + templates = require('../templates'); module.exports = function(grunt, makeSuite, callback) { var warmer = new BenchWarmer(); var handlebarsOnly = grunt.option('handlebars-only'), - grep = grunt.option('grep'); + grep = grunt.option('grep'); if (grep) { grep = new RegExp(grep); } diff --git a/integration-testing/multi-nodejs-test/run-handlebars.js b/integration-testing/multi-nodejs-test/run-handlebars.js index 9aded26c..6b0cbdca 100755 --- a/integration-testing/multi-nodejs-test/run-handlebars.js +++ b/integration-testing/multi-nodejs-test/run-handlebars.js @@ -5,7 +5,7 @@ var Handlebars = require('handlebars'); console.log('Testing built Handlebars with Node version ' + process.version); var template = Handlebars.compile('Author: {{author}}'); -var output = template({author: 'Yehuda'}); +var output = template({ author: 'Yehuda' }); assert.strictEqual(output, 'Author: Yehuda'); console.log('Success'); diff --git a/integration-testing/webpack-babel-test/src/handlebars-inline-precompile-test.js b/integration-testing/webpack-babel-test/src/handlebars-inline-precompile-test.js index 7f764e32..2d54945c 100644 --- a/integration-testing/webpack-babel-test/src/handlebars-inline-precompile-test.js +++ b/integration-testing/webpack-babel-test/src/handlebars-inline-precompile-test.js @@ -1,12 +1,12 @@ -import * as Handlebars from 'handlebars/runtime' +import * as Handlebars from 'handlebars/runtime'; import hbs from 'handlebars-inline-precompile'; -import {assertEquals} from "../../webpack-test/src/lib/assert"; +import { assertEquals } from '../../webpack-test/src/lib/assert'; Handlebars.registerHelper('loud', function(text) { - return text.toUpperCase(); + return text.toUpperCase(); }); const template = hbs`{{loud name}}`; -const output = template({name: 'yehuda'}) +const output = template({ name: 'yehuda' }); -assertEquals(output, 'YEHUDA') \ No newline at end of file +assertEquals(output, 'YEHUDA'); diff --git a/integration-testing/webpack-babel-test/src/lib/assert.js b/integration-testing/webpack-babel-test/src/lib/assert.js index 9f33188d..00095006 100644 --- a/integration-testing/webpack-babel-test/src/lib/assert.js +++ b/integration-testing/webpack-babel-test/src/lib/assert.js @@ -1,5 +1,5 @@ export function assertEquals(actual, expected) { - if (actual !== expected) { - throw new Error(`Expected "${actual}" to equal "${expected}"`); - } + if (actual !== expected) { + throw new Error(`Expected "${actual}" to equal "${expected}"`); + } } diff --git a/integration-testing/webpack-babel-test/webpack.config.js b/integration-testing/webpack-babel-test/webpack.config.js index 9ad0a09b..bebb6ee0 100644 --- a/integration-testing/webpack-babel-test/webpack.config.js +++ b/integration-testing/webpack-babel-test/webpack.config.js @@ -3,30 +3,30 @@ const fs = require('fs'); const testFiles = fs.readdirSync('src'); const entryPoints = {}; testFiles - .filter(file => file.match(/-test.js$/)) - .forEach(file => { - entryPoints[file] = `./src/${file}`; - }); + .filter(file => file.match(/-test.js$/)) + .forEach(file => { + entryPoints[file] = `./src/${file}`; + }); module.exports = { - entry: entryPoints, - output: { - filename: '[name]', - path: __dirname + '/dist' - }, - module: { - rules: [ - { - test: /\.js?$/, - exclude: /node_modules/, - use: { - loader: 'babel-loader', - options: { cacheDirectory: false }, - } - } - ] - }, - optimization: { - minimize: false - } + entry: entryPoints, + output: { + filename: '[name]', + path: __dirname + '/dist' + }, + module: { + rules: [ + { + test: /\.js?$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader', + options: { cacheDirectory: false } + } + } + ] + }, + optimization: { + minimize: false + } }; diff --git a/integration-testing/webpack-test/src/handlebars-default-import-pre-4.2-test.js b/integration-testing/webpack-test/src/handlebars-default-import-pre-4.2-test.js index 6bc630da..b7ffe711 100644 --- a/integration-testing/webpack-test/src/handlebars-default-import-pre-4.2-test.js +++ b/integration-testing/webpack-test/src/handlebars-default-import-pre-4.2-test.js @@ -1,6 +1,6 @@ import Handlebars from 'handlebars/dist/handlebars'; -import {assertEquals} from './lib/assert'; +import { assertEquals } from './lib/assert'; const template = Handlebars.compile('Author: {{author}}'); -assertEquals(template({author: 'Yehuda'}), 'Author: Yehuda'); +assertEquals(template({ author: 'Yehuda' }), 'Author: Yehuda'); diff --git a/integration-testing/webpack-test/src/handlebars-default-import-test.js b/integration-testing/webpack-test/src/handlebars-default-import-test.js index 97fc3f8b..d8730b6c 100644 --- a/integration-testing/webpack-test/src/handlebars-default-import-test.js +++ b/integration-testing/webpack-test/src/handlebars-default-import-test.js @@ -1,6 +1,5 @@ import Handlebars from 'handlebars'; -import {assertEquals} from './lib/assert'; - +import { assertEquals } from './lib/assert'; const template = Handlebars.compile('Author: {{author}}'); -assertEquals(template({author: 'Yehuda'}), 'Author: Yehuda'); +assertEquals(template({ author: 'Yehuda' }), 'Author: Yehuda'); diff --git a/integration-testing/webpack-test/src/handlebars-loader-test.js b/integration-testing/webpack-test/src/handlebars-loader-test.js index 4727c7a3..c6fc8d8d 100644 --- a/integration-testing/webpack-test/src/handlebars-loader-test.js +++ b/integration-testing/webpack-test/src/handlebars-loader-test.js @@ -1,8 +1,10 @@ -import {assertEquals} from './lib/assert'; +import { assertEquals } from './lib/assert'; import testTemplate from './test-template.handlebars'; -assertEquals(testTemplate({author: 'Yehuda'}).trim(), 'Author: Yehuda'); - +assertEquals(testTemplate({ author: 'Yehuda' }).trim(), 'Author: Yehuda'); const testTemplateRequire = require('./test-template.handlebars'); -assertEquals(testTemplateRequire({author: 'Yehuda'}).trim(), 'Author: Yehuda'); +assertEquals( + testTemplateRequire({ author: 'Yehuda' }).trim(), + 'Author: Yehuda' +); diff --git a/integration-testing/webpack-test/src/handlebars-require-vs-import-test.js b/integration-testing/webpack-test/src/handlebars-require-vs-import-test.js index 13e2c97d..9c49f0cb 100644 --- a/integration-testing/webpack-test/src/handlebars-require-vs-import-test.js +++ b/integration-testing/webpack-test/src/handlebars-require-vs-import-test.js @@ -1,10 +1,10 @@ import * as HandlebarsViaImport from 'handlebars'; const HandlebarsViaRequire = require('handlebars'); -import {assertEquals} from './lib/assert'; +import { assertEquals } from './lib/assert'; HandlebarsViaImport.registerHelper('loud', function(text) { - return text.toUpperCase(); + return text.toUpperCase(); }); const template = HandlebarsViaRequire.compile('Author: {{loud author}}'); -assertEquals(template({author: 'Yehuda'}), 'Author: YEHUDA'); +assertEquals(template({ author: 'Yehuda' }), 'Author: YEHUDA'); diff --git a/integration-testing/webpack-test/src/handlebars-wildcard-import-pre-4.2-test.js b/integration-testing/webpack-test/src/handlebars-wildcard-import-pre-4.2-test.js index 3731c5ac..2e4a1ff4 100644 --- a/integration-testing/webpack-test/src/handlebars-wildcard-import-pre-4.2-test.js +++ b/integration-testing/webpack-test/src/handlebars-wildcard-import-pre-4.2-test.js @@ -1,6 +1,6 @@ import * as Handlebars from 'handlebars/dist/handlebars'; -import {assertEquals} from './lib/assert'; +import { assertEquals } from './lib/assert'; const template = Handlebars.compile('Author: {{author}}'); -assertEquals(template({author: 'Yehuda'}), 'Author: Yehuda'); +assertEquals(template({ author: 'Yehuda' }), 'Author: Yehuda'); diff --git a/integration-testing/webpack-test/src/handlebars-wildcard-import-test.js b/integration-testing/webpack-test/src/handlebars-wildcard-import-test.js index 2ea0c1b4..bbf3bb81 100644 --- a/integration-testing/webpack-test/src/handlebars-wildcard-import-test.js +++ b/integration-testing/webpack-test/src/handlebars-wildcard-import-test.js @@ -1,5 +1,5 @@ import * as Handlebars from 'handlebars'; -import {assertEquals} from './lib/assert'; +import { assertEquals } from './lib/assert'; const template = Handlebars.compile('Author: {{author}}'); -assertEquals(template({author: 'Yehuda'}), 'Author: Yehuda'); +assertEquals(template({ author: 'Yehuda' }), 'Author: Yehuda'); diff --git a/integration-testing/webpack-test/src/lib/assert.js b/integration-testing/webpack-test/src/lib/assert.js index 9f33188d..00095006 100644 --- a/integration-testing/webpack-test/src/lib/assert.js +++ b/integration-testing/webpack-test/src/lib/assert.js @@ -1,5 +1,5 @@ export function assertEquals(actual, expected) { - if (actual !== expected) { - throw new Error(`Expected "${actual}" to equal "${expected}"`); - } + if (actual !== expected) { + throw new Error(`Expected "${actual}" to equal "${expected}"`); + } } diff --git a/integration-testing/webpack-test/webpack.config.js b/integration-testing/webpack-test/webpack.config.js index 95732122..207aed30 100644 --- a/integration-testing/webpack-test/webpack.config.js +++ b/integration-testing/webpack-test/webpack.config.js @@ -3,20 +3,18 @@ const fs = require('fs'); const testFiles = fs.readdirSync('src'); const entryPoints = {}; testFiles - .filter(file => file.match(/-test.js$/)) - .forEach(file => { - entryPoints[file] = `./src/${file}`; - }); + .filter(file => file.match(/-test.js$/)) + .forEach(file => { + entryPoints[file] = `./src/${file}`; + }); module.exports = { - entry: entryPoints, - output: { - filename: '[name]', - path: __dirname + '/dist' - }, - module: { - rules: [ - {test: /\.handlebars$/, loader: 'handlebars-loader'} - ] - } + entry: entryPoints, + output: { + filename: '[name]', + path: __dirname + '/dist' + }, + module: { + rules: [{ test: /\.handlebars$/, loader: 'handlebars-loader' }] + } }; diff --git a/lib/handlebars.js b/lib/handlebars.js index 69bcb410..c4f8fc78 100644 --- a/lib/handlebars.js +++ b/lib/handlebars.js @@ -2,7 +2,11 @@ import runtime from './handlebars.runtime'; // Compiler imports import AST from './handlebars/compiler/ast'; -import { parser as Parser, parse, parseWithoutProcessing } from './handlebars/compiler/base'; +import { + parser as Parser, + parse, + parseWithoutProcessing +} from './handlebars/compiler/base'; import { Compiler, compile, precompile } from './handlebars/compiler/compiler'; import JavaScriptCompiler from './handlebars/compiler/javascript-compiler'; import Visitor from './handlebars/compiler/visitor'; diff --git a/lib/handlebars/base.js b/lib/handlebars/base.js index ad3541b5..c655e8f0 100644 --- a/lib/handlebars/base.js +++ b/lib/handlebars/base.js @@ -1,7 +1,7 @@ -import {createFrame, extend, toString} from './utils'; +import { createFrame, extend, toString } from './utils'; import Exception from './exception'; -import {registerDefaultHelpers} from './helpers'; -import {registerDefaultDecorators} from './decorators'; +import { registerDefaultHelpers } from './helpers'; +import { registerDefaultDecorators } from './decorators'; import logger from './logger'; export const VERSION = '4.5.3'; @@ -38,7 +38,9 @@ HandlebarsEnvironment.prototype = { registerHelper: function(name, fn) { if (toString.call(name) === objectType) { - if (fn) { throw new Exception('Arg not supported with multiple helpers'); } + if (fn) { + throw new Exception('Arg not supported with multiple helpers'); + } extend(this.helpers, name); } else { this.helpers[name] = fn; @@ -53,7 +55,9 @@ HandlebarsEnvironment.prototype = { extend(this.partials, name); } else { if (typeof partial === 'undefined') { - throw new Exception(`Attempting to register a partial called "${name}" as undefined`); + throw new Exception( + `Attempting to register a partial called "${name}" as undefined` + ); } this.partials[name] = partial; } @@ -64,7 +68,9 @@ HandlebarsEnvironment.prototype = { registerDecorator: function(name, fn) { if (toString.call(name) === objectType) { - if (fn) { throw new Exception('Arg not supported with multiple decorators'); } + if (fn) { + throw new Exception('Arg not supported with multiple decorators'); + } extend(this.decorators, name); } else { this.decorators[name] = fn; @@ -77,4 +83,4 @@ HandlebarsEnvironment.prototype = { export let log = logger.log; -export {createFrame, logger}; +export { createFrame, logger }; diff --git a/lib/handlebars/compiler/ast.js b/lib/handlebars/compiler/ast.js index 1699569b..d77629a5 100644 --- a/lib/handlebars/compiler/ast.js +++ b/lib/handlebars/compiler/ast.js @@ -5,24 +5,28 @@ let AST = { // * it is an eligible helper, and // * it has at least one parameter or hash segment helperExpression: function(node) { - return (node.type === 'SubExpression') - || ((node.type === 'MustacheStatement' || node.type === 'BlockStatement') - && !!((node.params && node.params.length) || node.hash)); + return ( + node.type === 'SubExpression' || + ((node.type === 'MustacheStatement' || + node.type === 'BlockStatement') && + !!((node.params && node.params.length) || node.hash)) + ); }, scopedId: function(path) { - return (/^\.|this\b/).test(path.original); + return /^\.|this\b/.test(path.original); }, // an ID is simple if it only has one part, and that part is not // `..` or `this`. simpleId: function(path) { - return path.parts.length === 1 && !AST.helpers.scopedId(path) && !path.depth; + return ( + path.parts.length === 1 && !AST.helpers.scopedId(path) && !path.depth + ); } } }; - // Must be exported as an object rather than the root of the module as the jison lexer // must modify the object to operate properly. export default AST; diff --git a/lib/handlebars/compiler/base.js b/lib/handlebars/compiler/base.js index 19989ca5..1dd5af1a 100644 --- a/lib/handlebars/compiler/base.js +++ b/lib/handlebars/compiler/base.js @@ -10,7 +10,9 @@ extend(yy, Helpers); export function parseWithoutProcessing(input, options) { // Just return if an already-compiled AST was passed in. - if (input.type === 'Program') { return input; } + if (input.type === 'Program') { + return input; + } parser.yy = yy; diff --git a/lib/handlebars/compiler/code-gen.js b/lib/handlebars/compiler/code-gen.js index 30c5af74..4a174ba0 100644 --- a/lib/handlebars/compiler/code-gen.js +++ b/lib/handlebars/compiler/code-gen.js @@ -1,5 +1,5 @@ /* global define */ -import {isArray} from '../utils'; +import { isArray } from '../utils'; let SourceNode; @@ -38,7 +38,7 @@ if (!SourceNode) { this.src = chunks + this.src; }, toStringWithSourceMap: function() { - return {code: this.toString()}; + return { code: this.toString() }; }, toString: function() { return this.src; @@ -46,7 +46,6 @@ if (!SourceNode) { }; } - function castChunk(chunk, codeGen, loc) { if (isArray(chunk)) { let ret = []; @@ -62,7 +61,6 @@ function castChunk(chunk, codeGen, loc) { return chunk; } - function CodeGen(srcFile) { this.srcFile = srcFile; this.source = []; @@ -94,17 +92,22 @@ CodeGen.prototype = { }, empty: function() { - let loc = this.currentLocation || {start: {}}; + let loc = this.currentLocation || { start: {} }; return new SourceNode(loc.start.line, loc.start.column, this.srcFile); }, - wrap: function(chunk, loc = this.currentLocation || {start: {}}) { + wrap: function(chunk, loc = this.currentLocation || { start: {} }) { if (chunk instanceof SourceNode) { return chunk; } chunk = castChunk(chunk, this, loc); - return new SourceNode(loc.start.line, loc.start.column, this.srcFile, chunk); + return new SourceNode( + loc.start.line, + loc.start.column, + this.srcFile, + chunk + ); }, functionCall: function(fn, type, params) { @@ -113,13 +116,17 @@ CodeGen.prototype = { }, quotedString: function(str) { - return '"' + (str + '') - .replace(/\\/g, '\\\\') - .replace(/"/g, '\\"') - .replace(/\n/g, '\\n') - .replace(/\r/g, '\\r') - .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4 - .replace(/\u2029/g, '\\u2029') + '"'; + return ( + '"' + + (str + '') + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"') + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r') + .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4 + .replace(/\u2029/g, '\\u2029') + + '"' + ); }, objectLiteral: function(obj) { @@ -138,7 +145,6 @@ CodeGen.prototype = { return ret; }, - generateList: function(entries) { let ret = this.empty(); @@ -163,4 +169,3 @@ CodeGen.prototype = { }; export default CodeGen; - diff --git a/lib/handlebars/compiler/compiler.js b/lib/handlebars/compiler/compiler.js index b7a2f1c2..d759dd4d 100644 --- a/lib/handlebars/compiler/compiler.js +++ b/lib/handlebars/compiler/compiler.js @@ -1,7 +1,7 @@ /* eslint-disable new-cap */ import Exception from '../exception'; -import {isArray, indexOf, extend} from '../utils'; +import { isArray, indexOf, extend } from '../utils'; import AST from './ast'; const slice = [].slice; @@ -24,8 +24,11 @@ Compiler.prototype = { for (let i = 0; i < len; i++) { let opcode = this.opcodes[i], - otherOpcode = other.opcodes[i]; - if (opcode.opcode !== otherOpcode.opcode || !argEquals(opcode.args, otherOpcode.args)) { + otherOpcode = other.opcodes[i]; + if ( + opcode.opcode !== otherOpcode.opcode || + !argEquals(opcode.args, otherOpcode.args) + ) { return false; } } @@ -52,24 +55,28 @@ Compiler.prototype = { options.blockParams = options.blockParams || []; - options.knownHelpers = extend(Object.create(null), { - 'helperMissing': true, - 'blockHelperMissing': true, - 'each': true, - 'if': true, - 'unless': true, - 'with': true, - 'log': true, - 'lookup': true - }, options.knownHelpers); + options.knownHelpers = extend( + Object.create(null), + { + helperMissing: true, + blockHelperMissing: true, + each: true, + if: true, + unless: true, + with: true, + log: true, + lookup: true + }, + options.knownHelpers + ); return this.accept(program); }, compileProgram: function(program) { let childCompiler = new this.compiler(), // eslint-disable-line new-cap - result = childCompiler.compile(program, this.options), - guid = this.guid++; + result = childCompiler.compile(program, this.options), + guid = this.guid++; this.usePartial = this.usePartial || result.usePartial; @@ -95,7 +102,7 @@ Compiler.prototype = { this.options.blockParams.unshift(program.blockParams); let body = program.body, - bodyLength = body.length; + bodyLength = body.length; for (let i = 0; i < bodyLength; i++) { this.accept(body[i]); } @@ -112,7 +119,7 @@ Compiler.prototype = { transformLiteralToPath(block); let program = block.program, - inverse = block.inverse; + inverse = block.inverse; program = program && this.compileProgram(program); inverse = inverse && this.compileProgram(inverse); @@ -147,7 +154,7 @@ Compiler.prototype = { DecoratorBlock(decorator) { let program = decorator.program && this.compileProgram(decorator.program); let params = this.setupFullMustacheParams(decorator, program, undefined), - path = decorator.path; + path = decorator.path; this.useDecorators = true; this.opcode('registerDecorator', params.length, path.original); @@ -163,17 +170,20 @@ Compiler.prototype = { let params = partial.params; if (params.length > 1) { - throw new Exception('Unsupported number of partial arguments: ' + params.length, partial); + throw new Exception( + 'Unsupported number of partial arguments: ' + params.length, + partial + ); } else if (!params.length) { if (this.options.explicitPartialContext) { this.opcode('pushLiteral', 'undefined'); } else { - params.push({type: 'PathExpression', parts: [], depth: 0}); + params.push({ type: 'PathExpression', parts: [], depth: 0 }); } } let partialName = partial.name.original, - isDynamic = partial.name.type === 'SubExpression'; + isDynamic = partial.name.type === 'SubExpression'; if (isDynamic) { this.accept(partial.name); } @@ -206,7 +216,6 @@ Compiler.prototype = { this.DecoratorBlock(decorator); }, - ContentStatement: function(content) { if (content.value) { this.opcode('appendContent', content.value); @@ -229,8 +238,8 @@ Compiler.prototype = { }, ambiguousSexpr: function(sexpr, program, inverse) { let path = sexpr.path, - name = path.parts[0], - isBlock = program != null || inverse != null; + name = path.parts[0], + isBlock = program != null || inverse != null; this.opcode('getContext', path.depth); @@ -252,19 +261,27 @@ Compiler.prototype = { helperSexpr: function(sexpr, program, inverse) { let params = this.setupFullMustacheParams(sexpr, program, inverse), - path = sexpr.path, - name = path.parts[0]; + path = sexpr.path, + name = path.parts[0]; if (this.options.knownHelpers[name]) { this.opcode('invokeKnownHelper', params.length, name); } else if (this.options.knownHelpersOnly) { - throw new Exception('You specified knownHelpersOnly, but used the unknown helper ' + name, sexpr); + throw new Exception( + 'You specified knownHelpersOnly, but used the unknown helper ' + name, + sexpr + ); } else { path.strict = true; path.falsy = true; this.accept(path); - this.opcode('invokeHelper', params.length, path.original, AST.helpers.simpleId(path)); + this.opcode( + 'invokeHelper', + params.length, + path.original, + AST.helpers.simpleId(path) + ); } }, @@ -273,8 +290,8 @@ Compiler.prototype = { this.opcode('getContext', path.depth); let name = path.parts[0], - scoped = AST.helpers.scopedId(path), - blockParamId = !path.depth && !scoped && this.blockParamIndex(name); + scoped = AST.helpers.scopedId(path), + blockParamId = !path.depth && !scoped && this.blockParamIndex(name); if (blockParamId) { this.opcode('lookupBlockParam', blockParamId, path.parts); @@ -285,7 +302,13 @@ Compiler.prototype = { this.options.data = true; this.opcode('lookupData', path.depth, path.parts, path.strict); } else { - this.opcode('lookupOnContext', path.parts, path.falsy, path.strict, scoped); + this.opcode( + 'lookupOnContext', + path.parts, + path.falsy, + path.strict, + scoped + ); } }, @@ -311,8 +334,8 @@ Compiler.prototype = { Hash: function(hash) { let pairs = hash.pairs, - i = 0, - l = pairs.length; + i = 0, + l = pairs.length; this.opcode('pushHash'); @@ -327,7 +350,11 @@ Compiler.prototype = { // HELPERS opcode: function(name) { - this.opcodes.push({ opcode: name, args: slice.call(arguments, 1), loc: this.sourceNode[0].loc }); + this.opcodes.push({ + opcode: name, + args: slice.call(arguments, 1), + loc: this.sourceNode[0].loc + }); }, addDepth: function(depth) { @@ -356,7 +383,7 @@ Compiler.prototype = { // An eligible helper is one that does not have a complex path, i.e. `this.foo`, `../foo` etc. if (isEligible && !isHelper) { let name = sexpr.path.parts[0], - options = this.options; + options = this.options; if (options.knownHelpers[name]) { isHelper = true; } else if (options.knownHelpersOnly) { @@ -400,9 +427,13 @@ Compiler.prototype = { }, blockParamIndex: function(name) { - for (let depth = 0, len = this.options.blockParams.length; depth < len; depth++) { + for ( + let depth = 0, len = this.options.blockParams.length; + depth < len; + depth++ + ) { let blockParams = this.options.blockParams[depth], - param = blockParams && indexOf(blockParams, name); + param = blockParams && indexOf(blockParams, name); if (blockParams && param >= 0) { return [depth, param]; } @@ -426,7 +457,12 @@ export function compile(input, options = {}, env) { function compileInput() { let environment = compileEnvironment(input, options, env), - templateSpec = new env.JavaScriptCompiler().compile(environment, options, undefined, true); + templateSpec = new env.JavaScriptCompiler().compile( + environment, + options, + undefined, + true + ); return env.template(templateSpec); } @@ -440,12 +476,20 @@ export function compile(input, options = {}, env) { } function validateInput(input, options) { - if (input == null || (typeof input !== 'string' && input.type !== 'Program')) { - throw new Exception('You must pass a string or Handlebars AST to Handlebars.compile. You passed ' + input); + if ( + input == null || + (typeof input !== 'string' && input.type !== 'Program') + ) { + throw new Exception( + 'You must pass a string or Handlebars AST to Handlebars.compile. You passed ' + + input + ); } if (options.trackIds || options.stringParams) { - throw new Exception('TrackIds and stringParams are no longer supported. See Github #1145'); + throw new Exception( + 'TrackIds and stringParams are no longer supported. See Github #1145' + ); } if (!('data' in options)) { diff --git a/lib/handlebars/compiler/helpers.js b/lib/handlebars/compiler/helpers.js index f26b4e15..27033a1d 100644 --- a/lib/handlebars/compiler/helpers.js +++ b/lib/handlebars/compiler/helpers.js @@ -4,9 +4,12 @@ function validateClose(open, close) { close = close.path ? close.path.original : close; if (open.path.original !== close) { - let errorNode = {loc: open.path.loc}; + let errorNode = { loc: open.path.loc }; - throw new Exception(open.path.original + " doesn't match " + close, errorNode); + throw new Exception( + open.path.original + " doesn't match " + close, + errorNode + ); } } @@ -38,27 +41,26 @@ export function stripFlags(open, close) { } export function stripComment(comment) { - return comment.replace(/^\{\{~?!-?-?/, '') - .replace(/-?-?~?\}\}$/, ''); + return comment.replace(/^\{\{~?!-?-?/, '').replace(/-?-?~?\}\}$/, ''); } export function preparePath(data, parts, loc) { loc = this.locInfo(loc); let original = data ? '@' : '', - dig = [], - depth = 0; + dig = [], + depth = 0; for (let i = 0, l = parts.length; i < l; i++) { let part = parts[i].part, - // If we have [] syntax then we do not treat path references as operators, - // i.e. foo.[this] resolves to approximately context.foo['this'] - isLiteral = parts[i].original !== part; + // If we have [] syntax then we do not treat path references as operators, + // i.e. foo.[this] resolves to approximately context.foo['this'] + isLiteral = parts[i].original !== part; original += (parts[i].separator || '') + part; if (!isLiteral && (part === '..' || part === '.' || part === 'this')) { if (dig.length > 0) { - throw new Exception('Invalid path: ' + original, {loc}); + throw new Exception('Invalid path: ' + original, { loc }); } else if (part === '..') { depth++; } @@ -80,9 +82,9 @@ export function preparePath(data, parts, loc) { export function prepareMustache(path, params, hash, open, strip, locInfo) { // Must use charAt to support IE pre-10 let escapeFlag = open.charAt(3) || open.charAt(2), - escaped = escapeFlag !== '{' && escapeFlag !== '&'; + escaped = escapeFlag !== '{' && escapeFlag !== '&'; - let decorator = (/\*/.test(open)); + let decorator = /\*/.test(open); return { type: decorator ? 'Decorator' : 'MustacheStatement', path, @@ -118,21 +120,30 @@ export function prepareRawBlock(openRawBlock, contents, close, locInfo) { }; } -export function prepareBlock(openBlock, program, inverseAndProgram, close, inverted, locInfo) { +export function prepareBlock( + openBlock, + program, + inverseAndProgram, + close, + inverted, + locInfo +) { if (close && close.path) { validateClose(openBlock, close); } - let decorator = (/\*/.test(openBlock.open)); + let decorator = /\*/.test(openBlock.open); program.blockParams = openBlock.blockParams; - let inverse, - inverseStrip; + let inverse, inverseStrip; if (inverseAndProgram) { if (decorator) { - throw new Exception('Unexpected inverse block on decorator', inverseAndProgram); + throw new Exception( + 'Unexpected inverse block on decorator', + inverseAndProgram + ); } if (inverseAndProgram.chain) { @@ -166,7 +177,7 @@ export function prepareBlock(openBlock, program, inverseAndProgram, close, inver export function prepareProgram(statements, loc) { if (!loc && statements.length) { const firstLoc = statements[0].loc, - lastLoc = statements[statements.length - 1].loc; + lastLoc = statements[statements.length - 1].loc; /* istanbul ignore else */ if (firstLoc && lastLoc) { @@ -192,7 +203,6 @@ export function prepareProgram(statements, loc) { }; } - export function preparePartialBlock(open, program, close, locInfo) { validateClose(open, close); @@ -207,4 +217,3 @@ export function preparePartialBlock(open, program, close, locInfo) { loc: this.locInfo(locInfo) }; } - diff --git a/lib/handlebars/compiler/javascript-compiler.js b/lib/handlebars/compiler/javascript-compiler.js index 066f4ba5..729b9d69 100644 --- a/lib/handlebars/compiler/javascript-compiler.js +++ b/lib/handlebars/compiler/javascript-compiler.js @@ -1,8 +1,8 @@ import { COMPILER_REVISION, REVISION_CHANGES } from '../base'; import Exception from '../exception'; -import {isArray} from '../utils'; +import { isArray } from '../utils'; import CodeGen from './code-gen'; -import {dangerousPropertyRegex} from '../helpers/lookup'; +import { dangerousPropertyRegex } from '../helpers/lookup'; function Literal(value) { this.value = value; @@ -13,9 +13,16 @@ function JavaScriptCompiler() {} JavaScriptCompiler.prototype = { // PUBLIC API: You can override these methods in a subclass to provide // alternative compiled forms for name lookup and buffering semantics - nameLookup: function(parent, name/* , type*/) { + nameLookup: function(parent, name /* , type*/) { if (dangerousPropertyRegex.test(name)) { - const isEnumerable = [ this.aliasable('container.propertyIsEnumerable'), '.call(', parent, ',', JSON.stringify(name), ')']; + const isEnumerable = [ + this.aliasable('container.propertyIsEnumerable'), + '.call(', + parent, + ',', + JSON.stringify(name), + ')' + ]; return ['(', isEnumerable, '?', _actualLookup(), ' : undefined)']; } return _actualLookup(); @@ -34,7 +41,7 @@ JavaScriptCompiler.prototype = { compilerInfo: function() { const revision = COMPILER_REVISION, - versions = REVISION_CHANGES[revision]; + versions = REVISION_CHANGES[revision]; return [revision, versions]; }, @@ -89,14 +96,18 @@ JavaScriptCompiler.prototype = { this.compileChildren(environment, options); - this.useDepths = this.useDepths || environment.useDepths || environment.useDecorators || this.options.compat; + this.useDepths = + this.useDepths || + environment.useDepths || + environment.useDecorators || + this.options.compat; this.useBlockParams = this.useBlockParams || environment.useBlockParams; let opcodes = environment.opcodes, - opcode, - firstLoc, - i, - l; + opcode, + firstLoc, + i, + l; for (i = 0, l = opcodes.length; i < l; i++) { opcode = opcodes[i]; @@ -122,9 +133,20 @@ JavaScriptCompiler.prototype = { this.decorators.push('return fn;'); if (asObject) { - this.decorators = Function.apply(this, ['fn', 'props', 'container', 'depth0', 'data', 'blockParams', 'depths', this.decorators.merge()]); + this.decorators = Function.apply(this, [ + 'fn', + 'props', + 'container', + 'depth0', + 'data', + 'blockParams', + 'depths', + this.decorators.merge() + ]); } else { - this.decorators.prepend('function(fn, props, container, depth0, data, blockParams, depths) {\n'); + this.decorators.prepend( + 'function(fn, props, container, depth0, data, blockParams, depths) {\n' + ); this.decorators.push('}\n'); this.decorators = this.decorators.merge(); } @@ -144,7 +166,7 @@ JavaScriptCompiler.prototype = { ret.useDecorators = true; } - let {programs, decorators} = this.context; + let { programs, decorators } = this.context; for (i = 0, l = programs.length; i < l; i++) { if (programs[i]) { ret[i] = programs[i]; @@ -174,11 +196,11 @@ JavaScriptCompiler.prototype = { if (!asObject) { ret.compiler = JSON.stringify(ret.compiler); - this.source.currentLocation = {start: {line: 1, column: 0}}; + this.source.currentLocation = { start: { line: 1, column: 0 } }; ret = this.objectLiteral(ret); if (options.srcName) { - ret = ret.toStringWithSourceMap({file: options.destName}); + ret = ret.toStringWithSourceMap({ file: options.destName }); ret.map = ret.map && ret.map.toString(); } else { ret = ret.toString(); @@ -219,7 +241,7 @@ JavaScriptCompiler.prototype = { Object.keys(this.aliases).forEach(alias => { let node = this.aliases[alias]; if (node.children && node.referenceCount > 1) { - varDeclarations += ', alias' + (++aliasCount) + '=' + alias; + varDeclarations += ', alias' + ++aliasCount + '=' + alias; node.children[0] = 'alias' + aliasCount; } }); @@ -241,18 +263,23 @@ JavaScriptCompiler.prototype = { return Function.apply(this, params); } else { - return this.source.wrap(['function(', params.join(','), ') {\n ', source, '}']); + return this.source.wrap([ + 'function(', + params.join(','), + ') {\n ', + source, + '}' + ]); } }, mergeSource: function(varDeclarations) { let isSimple = this.environment.isSimple, - appendOnly = !this.forceBuffer, - appendFirst, - - sourceSeen, - bufferStart, - bufferEnd; - this.source.each((line) => { + appendOnly = !this.forceBuffer, + appendFirst, + sourceSeen, + bufferStart, + bufferEnd; + this.source.each(line => { if (line.appendToBuffer) { if (bufferStart) { line.prepend(' + '); @@ -278,7 +305,6 @@ JavaScriptCompiler.prototype = { } }); - if (appendOnly) { if (bufferStart) { bufferStart.prepend('return '); @@ -287,7 +313,8 @@ JavaScriptCompiler.prototype = { this.source.push('return "";'); } } else { - varDeclarations += ', buffer = ' + (appendFirst ? '' : this.initializeBuffer()); + varDeclarations += + ', buffer = ' + (appendFirst ? '' : this.initializeBuffer()); if (bufferStart) { bufferStart.prepend('return buffer + '); @@ -298,7 +325,9 @@ JavaScriptCompiler.prototype = { } if (varDeclarations) { - this.source.prepend('var ' + varDeclarations.substring(2) + (appendFirst ? '' : ';\n')); + this.source.prepend( + 'var ' + varDeclarations.substring(2) + (appendFirst ? '' : ';\n') + ); } return this.source.merge(); @@ -314,8 +343,10 @@ JavaScriptCompiler.prototype = { // replace it on the stack with the result of properly // invoking blockHelperMissing. blockValue: function(name) { - let blockHelperMissing = this.aliasable('container.hooks.blockHelperMissing'), - params = [this.contextName(0)]; + let blockHelperMissing = this.aliasable( + 'container.hooks.blockHelperMissing' + ), + params = [this.contextName(0)]; this.setupHelperArgs(name, 0, params); let blockName = this.popStack(); @@ -332,8 +363,10 @@ JavaScriptCompiler.prototype = { // On stack, after, if lastHelper: value ambiguousBlockValue: function() { // We're being a bit cheeky and reusing the options value from the prior exec - let blockHelperMissing = this.aliasable('container.hooks.blockHelperMissing'), - params = [this.contextName(0)]; + let blockHelperMissing = this.aliasable( + 'container.hooks.blockHelperMissing' + ), + params = [this.contextName(0)]; this.setupHelperArgs('', 0, params, true); this.flushInline(); @@ -342,9 +375,14 @@ JavaScriptCompiler.prototype = { params.splice(1, 0, current); this.pushSource([ - 'if (!', this.lastHelper, ') { ', - current, ' = ', this.source.functionCall(blockHelperMissing, 'call', params), - '}']); + 'if (!', + this.lastHelper, + ') { ', + current, + ' = ', + this.source.functionCall(blockHelperMissing, 'call', params), + '}' + ]); }, // [appendContent] @@ -374,14 +412,24 @@ JavaScriptCompiler.prototype = { // Otherwise, the empty string is appended append: function() { if (this.isInline()) { - this.replaceStack((current) => [' != null ? ', current, ' : ""']); + this.replaceStack(current => [' != null ? ', current, ' : ""']); this.pushSource(this.appendToBuffer(this.popStack())); } else { let local = this.popStack(); - this.pushSource(['if (', local, ' != null) { ', this.appendToBuffer(local, undefined, true), ' }']); + this.pushSource([ + 'if (', + local, + ' != null) { ', + this.appendToBuffer(local, undefined, true), + ' }' + ]); if (this.environment.isSimple) { - this.pushSource(['else { ', this.appendToBuffer("''", undefined, true), ' }']); + this.pushSource([ + 'else { ', + this.appendToBuffer("''", undefined, true), + ' }' + ]); } } }, @@ -393,8 +441,14 @@ JavaScriptCompiler.prototype = { // // Escape `value` and append it to the buffer appendEscaped: function() { - this.pushSource(this.appendToBuffer( - [this.aliasable('container.escapeExpression'), '(', this.popStack(), ')'])); + this.pushSource( + this.appendToBuffer([ + this.aliasable('container.escapeExpression'), + '(', + this.popStack(), + ')' + ]) + ); }, // [getContext] @@ -478,7 +532,7 @@ JavaScriptCompiler.prototype = { let len = parts.length; for (; i < len; i++) { /* eslint-disable no-loop-func */ - this.replaceStack((current) => { + this.replaceStack(current => { let lookup = this.nameLookup(current, parts[i], type); // We want to ensure that zero and false are handled properly if the context (falsy flag) // needs to have the special handling for these values. @@ -501,7 +555,14 @@ JavaScriptCompiler.prototype = { // If the `value` is a lambda, replace it on the stack by // the return value of the lambda resolvePossibleLambda: function() { - this.push([this.aliasable('container.lambda'), '(', this.popStack(), ', ', this.contextName(0), ')']); + this.push([ + this.aliasable('container.lambda'), + '(', + this.popStack(), + ', ', + this.contextName(0), + ')' + ]); }, emptyHash: function(omitEmpty) { @@ -511,7 +572,7 @@ JavaScriptCompiler.prototype = { if (this.hash) { this.hashes.push(this.hash); } - this.hash = {values: {}}; + this.hash = { values: {} }; }, popHash: function() { let hash = this.hash; @@ -567,11 +628,16 @@ JavaScriptCompiler.prototype = { // and inserts the decorator into the decorators list. registerDecorator(paramSize, name) { let foundDecorator = this.nameLookup('decorators', name, 'decorator'), - options = this.setupHelperArgs(name, paramSize); + options = this.setupHelperArgs(name, paramSize); this.decorators.push([ 'fn = ', - this.decorators.functionCall(foundDecorator, '', ['fn', 'props', 'container', options]), + this.decorators.functionCall(foundDecorator, '', [ + 'fn', + 'props', + 'container', + options + ]), ' || fn;' ]); }, @@ -587,21 +653,32 @@ JavaScriptCompiler.prototype = { // If the helper is not found, `helperMissing` is called. invokeHelper: function(paramSize, name, isSimple) { let nonHelper = this.popStack(), - helper = this.setupHelper(paramSize, name); + helper = this.setupHelper(paramSize, name); let possibleFunctionCalls = []; - if (isSimple) { // direct call to helper + if (isSimple) { + // direct call to helper possibleFunctionCalls.push(helper.name); } // call a function from the input object possibleFunctionCalls.push(nonHelper); if (!this.options.strict) { - possibleFunctionCalls.push(this.aliasable('container.hooks.helperMissing')); + possibleFunctionCalls.push( + this.aliasable('container.hooks.helperMissing') + ); } - let functionLookupCode = ['(', this.itemsSeparatedBy(possibleFunctionCalls, '||'), ')']; - let functionCall = this.source.functionCall(functionLookupCode, 'call', helper.callParams); + let functionLookupCode = [ + '(', + this.itemsSeparatedBy(possibleFunctionCalls, '||'), + ')' + ]; + let functionCall = this.source.functionCall( + functionLookupCode, + 'call', + helper.callParams + ); this.push(functionCall); }, @@ -645,22 +722,31 @@ JavaScriptCompiler.prototype = { this.emptyHash(); let helper = this.setupHelper(0, name, helperCall); - let helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper'); + let helperName = (this.lastHelper = this.nameLookup( + 'helpers', + name, + 'helper' + )); let lookup = ['(', '(helper = ', helperName, ' || ', nonHelper, ')']; if (!this.options.strict) { lookup[0] = '(helper = '; lookup.push( - ' != null ? helper : ', - this.aliasable('container.hooks.helperMissing') + ' != null ? helper : ', + this.aliasable('container.hooks.helperMissing') ); } this.push([ - '(', lookup, - (helper.paramsInit ? ['),(', helper.paramsInit] : []), '),', - '(typeof helper === ', this.aliasable('"function"'), ' ? ', - this.source.functionCall('helper', 'call', helper.callParams), ' : helper))' + '(', + lookup, + helper.paramsInit ? ['),(', helper.paramsInit] : [], + '),', + '(typeof helper === ', + this.aliasable('"function"'), + ' ? ', + this.source.functionCall('helper', 'call', helper.callParams), + ' : helper))' ]); }, @@ -673,7 +759,7 @@ JavaScriptCompiler.prototype = { // and pushes the result of the invocation back. invokePartial: function(isDynamic, name, indent) { let params = [], - options = this.setupParams(name, 1, params); + options = this.setupParams(name, 1, params); if (isDynamic) { name = this.popStack(); @@ -717,7 +803,9 @@ JavaScriptCompiler.prototype = { compiler: JavaScriptCompiler, compileChildren: function(environment, options) { - let children = environment.children, child, compiler; + let children = environment.children, + child, + compiler; for (let i = 0, l = children.length; i < l; i++) { child = children[i]; @@ -730,7 +818,12 @@ JavaScriptCompiler.prototype = { let index = this.context.programs.length; child.index = index; child.name = 'program' + index; - this.context.programs[index] = compiler.compile(child, options, this.context, !this.precompile); + this.context.programs[index] = compiler.compile( + child, + options, + this.context, + !this.precompile + ); this.context.decorators[index] = compiler.decorators; this.context.environments[index] = child; @@ -758,7 +851,7 @@ JavaScriptCompiler.prototype = { programExpression: function(guid) { let child = this.environment.children[guid], - programParams = [child.index, 'data', child.blockParams]; + programParams = [child.index, 'data', child.blockParams]; if (this.useBlockParams || this.useDepths) { programParams.push('blockParams'); @@ -793,7 +886,11 @@ JavaScriptCompiler.prototype = { pushSource: function(source) { if (this.pendingContent) { this.source.push( - this.appendToBuffer(this.source.quotedString(this.pendingContent), this.pendingLocation)); + this.appendToBuffer( + this.source.quotedString(this.pendingContent), + this.pendingLocation + ) + ); this.pendingContent = undefined; } @@ -804,9 +901,9 @@ JavaScriptCompiler.prototype = { replaceStack: function(callback) { let prefix = ['('], - stack, - createdStack, - usedLiteral; + stack, + createdStack, + usedLiteral; /* istanbul ignore next */ if (!this.isInline()) { @@ -843,7 +940,9 @@ JavaScriptCompiler.prototype = { incrStack: function() { this.stackSlot++; - if (this.stackSlot > this.stackVars.length) { this.stackVars.push('stack' + this.stackSlot); } + if (this.stackSlot > this.stackVars.length) { + this.stackVars.push('stack' + this.stackSlot); + } return this.topStackName(); }, topStackName: function() { @@ -870,9 +969,9 @@ JavaScriptCompiler.prototype = { popStack: function(wrapped) { let inline = this.isInline(), - item = (inline ? this.inlineStack : this.compileStack).pop(); + item = (inline ? this.inlineStack : this.compileStack).pop(); - if (!wrapped && (item instanceof Literal)) { + if (!wrapped && item instanceof Literal) { return item.value; } else { if (!inline) { @@ -887,8 +986,8 @@ JavaScriptCompiler.prototype = { }, topStack: function() { - let stack = (this.isInline() ? this.inlineStack : this.compileStack), - item = stack[stack.length - 1]; + let stack = this.isInline() ? this.inlineStack : this.compileStack, + item = stack[stack.length - 1]; /* istanbul ignore if */ if (item instanceof Literal) { @@ -930,9 +1029,13 @@ JavaScriptCompiler.prototype = { setupHelper: function(paramSize, name, blockHelper) { let params = [], - paramsInit = this.setupHelperArgs(name, paramSize, params, blockHelper); + paramsInit = this.setupHelperArgs(name, paramSize, params, blockHelper); let foundHelper = this.nameLookup('helpers', name, 'helper'), - callContext = this.aliasable(`${this.contextName(0)} != null ? ${this.contextName(0)} : (container.nullContext || {})`); + callContext = this.aliasable( + `${this.contextName(0)} != null ? ${this.contextName( + 0 + )} : (container.nullContext || {})` + ); return { params: params, @@ -944,8 +1047,8 @@ JavaScriptCompiler.prototype = { setupParams: function(helper, paramSize, params) { let options = {}, - objectArgs = !params, - param; + objectArgs = !params, + param; if (objectArgs) { params = []; @@ -955,7 +1058,7 @@ JavaScriptCompiler.prototype = { options.hash = this.popStack(); let inverse = this.popStack(), - program = this.popStack(); + program = this.popStack(); // Avoid setting fn and inverse if neither are set. This allows // helpers to do a check for `if (options.fn)` @@ -1002,7 +1105,6 @@ JavaScriptCompiler.prototype = { } }; - (function() { const reservedWords = ( 'break else new var' + @@ -1023,21 +1125,24 @@ JavaScriptCompiler.prototype = { ' null true false' ).split(' '); - const compilerWords = JavaScriptCompiler.RESERVED_WORDS = {}; + const compilerWords = (JavaScriptCompiler.RESERVED_WORDS = {}); for (let i = 0, l = reservedWords.length; i < l; i++) { compilerWords[reservedWords[i]] = true; } -}()); +})(); JavaScriptCompiler.isValidJavaScriptVariableName = function(name) { - return !JavaScriptCompiler.RESERVED_WORDS[name] && (/^[a-zA-Z_$][0-9a-zA-Z_$]*$/).test(name); + return ( + !JavaScriptCompiler.RESERVED_WORDS[name] && + /^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(name) + ); }; function strictLookup(requireTerminal, compiler, parts, type) { let stack = compiler.popStack(), - i = 0, - len = parts.length; + i = 0, + len = parts.length; if (requireTerminal) { len--; } @@ -1047,7 +1152,16 @@ function strictLookup(requireTerminal, compiler, parts, type) { } if (requireTerminal) { - return [compiler.aliasable('container.strict'), '(', stack, ', ', compiler.quotedString(parts[i]), ', ', JSON.stringify(compiler.source.currentLocation), ' )']; + return [ + compiler.aliasable('container.strict'), + '(', + stack, + ', ', + compiler.quotedString(parts[i]), + ', ', + JSON.stringify(compiler.source.currentLocation), + ' )' + ]; } else { return stack; } diff --git a/lib/handlebars/compiler/printer.js b/lib/handlebars/compiler/printer.js index 6ad43bae..e087806c 100644 --- a/lib/handlebars/compiler/printer.js +++ b/lib/handlebars/compiler/printer.js @@ -24,13 +24,14 @@ PrintVisitor.prototype.pad = function(string) { PrintVisitor.prototype.Program = function(program) { let out = '', - body = program.body, - i, l; + body = program.body, + i, + l; if (program.blockParams) { let blockParams = 'BLOCK PARAMS: ['; for (i = 0, l = program.blockParams.length; i < l; i++) { - blockParams += ' ' + program.blockParams[i]; + blockParams += ' ' + program.blockParams[i]; } blockParams += ' ]'; out += this.pad(blockParams); @@ -52,11 +53,14 @@ PrintVisitor.prototype.Decorator = function(mustache) { return this.pad('{{ DIRECTIVE ' + this.SubExpression(mustache) + ' }}'); }; -PrintVisitor.prototype.BlockStatement = -PrintVisitor.prototype.DecoratorBlock = function(block) { +PrintVisitor.prototype.BlockStatement = PrintVisitor.prototype.DecoratorBlock = function( + block +) { let out = ''; - out += this.pad((block.type === 'DecoratorBlock' ? 'DIRECTIVE ' : '') + 'BLOCK:'); + out += this.pad( + (block.type === 'DecoratorBlock' ? 'DIRECTIVE ' : '') + 'BLOCK:' + ); this.padding++; out += this.pad(this.SubExpression(block)); if (block.program) { @@ -66,12 +70,16 @@ PrintVisitor.prototype.DecoratorBlock = function(block) { this.padding--; } if (block.inverse) { - if (block.program) { this.padding++; } + if (block.program) { + this.padding++; + } out += this.pad('{{^}}'); this.padding++; out += this.accept(block.inverse); this.padding--; - if (block.program) { this.padding--; } + if (block.program) { + this.padding--; + } } this.padding--; @@ -115,8 +123,8 @@ PrintVisitor.prototype.CommentStatement = function(comment) { PrintVisitor.prototype.SubExpression = function(sexpr) { let params = sexpr.params, - paramStrings = [], - hash; + paramStrings = [], + hash; for (let i = 0, l = params.length; i < l; i++) { paramStrings.push(this.accept(params[i])); @@ -134,7 +142,6 @@ PrintVisitor.prototype.PathExpression = function(id) { return (id.data ? '@' : '') + 'PATH:' + path; }; - PrintVisitor.prototype.StringLiteral = function(string) { return '"' + string.value + '"'; }; @@ -157,7 +164,7 @@ PrintVisitor.prototype.NullLiteral = function() { PrintVisitor.prototype.Hash = function(hash) { let pairs = hash.pairs, - joinedPairs = []; + joinedPairs = []; for (let i = 0, l = pairs.length; i < l; i++) { joinedPairs.push(this.accept(pairs[i])); diff --git a/lib/handlebars/compiler/visitor.js b/lib/handlebars/compiler/visitor.js index 2c504d1b..76bb01f1 100644 --- a/lib/handlebars/compiler/visitor.js +++ b/lib/handlebars/compiler/visitor.js @@ -15,7 +15,14 @@ Visitor.prototype = { // Hacky sanity check: This may have a few false positives for type for the helper // methods but will generally do the right thing without a lot of overhead. if (value && !Visitor.prototype[value.type]) { - throw new Exception('Unexpected node type "' + value.type + '" found when accepting ' + name + ' on ' + node.type); + throw new Exception( + 'Unexpected node type "' + + value.type + + '" found when accepting ' + + name + + ' on ' + + node.type + ); } node[name] = value; } diff --git a/lib/handlebars/compiler/whitespace-control.js b/lib/handlebars/compiler/whitespace-control.js index 8ebfbef5..7db68398 100644 --- a/lib/handlebars/compiler/whitespace-control.js +++ b/lib/handlebars/compiler/whitespace-control.js @@ -14,18 +14,18 @@ WhitespaceControl.prototype.Program = function(program) { let body = program.body; for (let i = 0, l = body.length; i < l; i++) { let current = body[i], - strip = this.accept(current); + strip = this.accept(current); if (!strip) { continue; } let _isPrevWhitespace = isPrevWhitespace(body, i, isRoot), - _isNextWhitespace = isNextWhitespace(body, i, isRoot), - - openStandalone = strip.openStandalone && _isPrevWhitespace, - closeStandalone = strip.closeStandalone && _isNextWhitespace, - inlineStandalone = strip.inlineStandalone && _isPrevWhitespace && _isNextWhitespace; + _isNextWhitespace = isNextWhitespace(body, i, isRoot), + openStandalone = strip.openStandalone && _isPrevWhitespace, + closeStandalone = strip.closeStandalone && _isNextWhitespace, + inlineStandalone = + strip.inlineStandalone && _isPrevWhitespace && _isNextWhitespace; if (strip.close) { omitRight(body, i, true); @@ -41,7 +41,7 @@ WhitespaceControl.prototype.Program = function(program) { // If we are on a standalone node, save the indent info for partials if (current.type === 'PartialStatement') { // Pull out the whitespace from the final line - current.indent = (/([ \t]+$)/).exec(body[i - 1].original)[1]; + current.indent = /([ \t]+$)/.exec(body[i - 1].original)[1]; } } } @@ -62,17 +62,17 @@ WhitespaceControl.prototype.Program = function(program) { return program; }; -WhitespaceControl.prototype.BlockStatement = -WhitespaceControl.prototype.DecoratorBlock = -WhitespaceControl.prototype.PartialBlockStatement = function(block) { +WhitespaceControl.prototype.BlockStatement = WhitespaceControl.prototype.DecoratorBlock = WhitespaceControl.prototype.PartialBlockStatement = function( + block +) { this.accept(block.program); this.accept(block.inverse); // Find the inverse program that is involed with whitespace stripping. let program = block.program || block.inverse, - inverse = block.program && block.inverse, - firstInverse = inverse, - lastInverse = inverse; + inverse = block.program && block.inverse, + firstInverse = inverse, + lastInverse = inverse; if (inverse && inverse.chained) { firstInverse = inverse.body[0].program; @@ -112,9 +112,11 @@ WhitespaceControl.prototype.PartialBlockStatement = function(block) { } // Find standalone else statments - if (!this.options.ignoreStandalone - && isPrevWhitespace(program.body) - && isNextWhitespace(firstInverse.body)) { + if ( + !this.options.ignoreStandalone && + isPrevWhitespace(program.body) && + isNextWhitespace(firstInverse.body) + ) { omitLeft(program.body); omitRight(firstInverse.body); } @@ -125,13 +127,15 @@ WhitespaceControl.prototype.PartialBlockStatement = function(block) { return strip; }; -WhitespaceControl.prototype.Decorator = -WhitespaceControl.prototype.MustacheStatement = function(mustache) { +WhitespaceControl.prototype.Decorator = WhitespaceControl.prototype.MustacheStatement = function( + mustache +) { return mustache.strip; }; -WhitespaceControl.prototype.PartialStatement = - WhitespaceControl.prototype.CommentStatement = function(node) { +WhitespaceControl.prototype.PartialStatement = WhitespaceControl.prototype.CommentStatement = function( + node +) { /* istanbul ignore next */ let strip = node.strip || {}; return { @@ -141,7 +145,6 @@ WhitespaceControl.prototype.PartialStatement = }; }; - function isPrevWhitespace(body, i, isRoot) { if (i === undefined) { i = body.length; @@ -150,13 +153,15 @@ function isPrevWhitespace(body, i, isRoot) { // Nodes that end with newlines are considered whitespace (but are special // cased for strip operations) let prev = body[i - 1], - sibling = body[i - 2]; + sibling = body[i - 2]; if (!prev) { return isRoot; } if (prev.type === 'ContentStatement') { - return (sibling || !isRoot ? (/\r?\n\s*?$/) : (/(^|\r?\n)\s*?$/)).test(prev.original); + return (sibling || !isRoot ? /\r?\n\s*?$/ : /(^|\r?\n)\s*?$/).test( + prev.original + ); } } function isNextWhitespace(body, i, isRoot) { @@ -165,13 +170,15 @@ function isNextWhitespace(body, i, isRoot) { } let next = body[i + 1], - sibling = body[i + 2]; + sibling = body[i + 2]; if (!next) { return isRoot; } if (next.type === 'ContentStatement') { - return (sibling || !isRoot ? (/^\s*?\r?\n/) : (/^\s*?(\r?\n|$)/)).test(next.original); + return (sibling || !isRoot ? /^\s*?\r?\n/ : /^\s*?(\r?\n|$)/).test( + next.original + ); } } @@ -184,12 +191,19 @@ function isNextWhitespace(body, i, isRoot) { // content is met. function omitRight(body, i, multiple) { let current = body[i == null ? 0 : i + 1]; - if (!current || current.type !== 'ContentStatement' || (!multiple && current.rightStripped)) { + if ( + !current || + current.type !== 'ContentStatement' || + (!multiple && current.rightStripped) + ) { return; } let original = current.value; - current.value = current.value.replace(multiple ? (/^\s+/) : (/^[ \t]*\r?\n?/), ''); + current.value = current.value.replace( + multiple ? /^\s+/ : /^[ \t]*\r?\n?/, + '' + ); current.rightStripped = current.value !== original; } @@ -202,13 +216,17 @@ function omitRight(body, i, multiple) { // content is met. function omitLeft(body, i, multiple) { let current = body[i == null ? body.length - 1 : i - 1]; - if (!current || current.type !== 'ContentStatement' || (!multiple && current.leftStripped)) { + if ( + !current || + current.type !== 'ContentStatement' || + (!multiple && current.leftStripped) + ) { return; } // We omit the last node if it's whitespace only and not preceded by a non-content node. let original = current.value; - current.value = current.value.replace(multiple ? (/\s+$/) : (/[ \t]+$/), ''); + current.value = current.value.replace(multiple ? /\s+$/ : /[ \t]+$/, ''); current.leftStripped = current.value !== original; return current.leftStripped; } diff --git a/lib/handlebars/decorators.js b/lib/handlebars/decorators.js index 6f5a6152..d9d5e8a9 100644 --- a/lib/handlebars/decorators.js +++ b/lib/handlebars/decorators.js @@ -3,4 +3,3 @@ import registerInline from './decorators/inline'; export function registerDefaultDecorators(instance) { registerInline(instance); } - diff --git a/lib/handlebars/decorators/inline.js b/lib/handlebars/decorators/inline.js index 21424662..02f5be7b 100644 --- a/lib/handlebars/decorators/inline.js +++ b/lib/handlebars/decorators/inline.js @@ -1,4 +1,4 @@ -import {extend} from '../utils'; +import { extend } from '../utils'; export default function(instance) { instance.registerDecorator('inline', function(fn, props, container, options) { diff --git a/lib/handlebars/exception.js b/lib/handlebars/exception.js index 6333f190..b2fdc00b 100644 --- a/lib/handlebars/exception.js +++ b/lib/handlebars/exception.js @@ -1,12 +1,20 @@ - -const errorProps = ['description', 'fileName', 'lineNumber', 'endLineNumber', 'message', 'name', 'number', 'stack']; +const errorProps = [ + 'description', + 'fileName', + 'lineNumber', + 'endLineNumber', + 'message', + 'name', + 'number', + 'stack' +]; function Exception(message, node) { let loc = node && node.loc, - line, - endLineNumber, - column, - endColumn; + line, + endLineNumber, + column, + endColumn; if (loc) { line = loc.start.line; diff --git a/lib/handlebars/helpers/block-helper-missing.js b/lib/handlebars/helpers/block-helper-missing.js index e6d162f7..c16ad1a6 100644 --- a/lib/handlebars/helpers/block-helper-missing.js +++ b/lib/handlebars/helpers/block-helper-missing.js @@ -1,9 +1,9 @@ -import {isArray} from '../utils'; +import { isArray } from '../utils'; export default function(instance) { instance.registerHelper('blockHelperMissing', function(context, options) { let inverse = options.inverse, - fn = options.fn; + fn = options.fn; if (context === true) { return fn(this); diff --git a/lib/handlebars/helpers/each.js b/lib/handlebars/helpers/each.js index f65a6aff..25651f45 100644 --- a/lib/handlebars/helpers/each.js +++ b/lib/handlebars/helpers/each.js @@ -1,4 +1,4 @@ -import {createFrame, isArray, isFunction} from '../utils'; +import { createFrame, isArray, isFunction } from '../utils'; import Exception from '../exception'; export default function(instance) { @@ -8,12 +8,14 @@ export default function(instance) { } let fn = options.fn, - inverse = options.inverse, - i = 0, - ret = '', - data; + inverse = options.inverse, + i = 0, + ret = '', + data; - if (isFunction(context)) { context = context.call(this); } + if (isFunction(context)) { + context = context.call(this); + } if (options.data) { data = createFrame(options.data); @@ -27,10 +29,12 @@ export default function(instance) { data.last = !!last; } - ret = ret + fn(context[field], { - data: data, - blockParams: [context[field], field] - }); + ret = + ret + + fn(context[field], { + data: data, + blockParams: [context[field], field] + }); } if (context && typeof context === 'object') { diff --git a/lib/handlebars/helpers/helper-missing.js b/lib/handlebars/helpers/helper-missing.js index ec32e824..f8869e9c 100644 --- a/lib/handlebars/helpers/helper-missing.js +++ b/lib/handlebars/helpers/helper-missing.js @@ -7,7 +7,9 @@ export default function(instance) { return undefined; } else { // Someone is actually trying to call something, blow up. - throw new Exception('Missing helper: "' + arguments[arguments.length - 1].name + '"'); + throw new Exception( + 'Missing helper: "' + arguments[arguments.length - 1].name + '"' + ); } }); } diff --git a/lib/handlebars/helpers/if.js b/lib/handlebars/helpers/if.js index 5be88b70..55241ecb 100644 --- a/lib/handlebars/helpers/if.js +++ b/lib/handlebars/helpers/if.js @@ -3,8 +3,12 @@ import Exception from '../exception'; export default function(instance) { instance.registerHelper('if', function(conditional, options) { - if (arguments.length != 2) { throw new Exception('#if requires exactly one argument');} - if (isFunction(conditional)) { conditional = conditional.call(this); } + if (arguments.length != 2) { + throw new Exception('#if requires exactly one argument'); + } + if (isFunction(conditional)) { + conditional = conditional.call(this); + } // Default behavior is to render the positive path if the value is truthy and not empty. // The `includeZero` option may be set to treat the condtional as purely not empty based on the @@ -17,7 +21,13 @@ export default function(instance) { }); instance.registerHelper('unless', function(conditional, options) { - if (arguments.length != 2) { throw new Exception('#unless requires exactly one argument');} - return instance.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn, hash: options.hash}); + if (arguments.length != 2) { + throw new Exception('#unless requires exactly one argument'); + } + return instance.helpers['if'].call(this, conditional, { + fn: options.inverse, + inverse: options.fn, + hash: options.hash + }); }); } diff --git a/lib/handlebars/helpers/log.js b/lib/handlebars/helpers/log.js index 4bde4a10..ab0d7364 100644 --- a/lib/handlebars/helpers/log.js +++ b/lib/handlebars/helpers/log.js @@ -1,7 +1,7 @@ export default function(instance) { instance.registerHelper('log', function(/* message, options */) { let args = [undefined], - options = arguments[arguments.length - 1]; + options = arguments[arguments.length - 1]; for (let i = 0; i < arguments.length - 1; i++) { args.push(arguments[i]); } @@ -14,6 +14,6 @@ export default function(instance) { } args[0] = level; - instance.log(... args); + instance.log(...args); }); } diff --git a/lib/handlebars/helpers/lookup.js b/lib/handlebars/helpers/lookup.js index 8c1b604b..66de538b 100644 --- a/lib/handlebars/helpers/lookup.js +++ b/lib/handlebars/helpers/lookup.js @@ -5,7 +5,10 @@ export default function(instance) { if (!obj) { return obj; } - if (dangerousPropertyRegex.test(String(field)) && !Object.prototype.propertyIsEnumerable.call(obj, field)) { + if ( + dangerousPropertyRegex.test(String(field)) && + !Object.prototype.propertyIsEnumerable.call(obj, field) + ) { return undefined; } return obj[field]; diff --git a/lib/handlebars/helpers/with.js b/lib/handlebars/helpers/with.js index 6020e6aa..3394b7ae 100644 --- a/lib/handlebars/helpers/with.js +++ b/lib/handlebars/helpers/with.js @@ -1,10 +1,14 @@ -import {isEmpty, isFunction} from '../utils'; +import { isEmpty, isFunction } from '../utils'; import Exception from '../exception'; export default function(instance) { instance.registerHelper('with', function(context, options) { - if (arguments.length != 2) { throw new Exception('#with requires exactly one argument');} - if (isFunction(context)) { context = context.call(this); } + if (arguments.length != 2) { + throw new Exception('#with requires exactly one argument'); + } + if (isFunction(context)) { + context = context.call(this); + } let fn = options.fn; diff --git a/lib/handlebars/logger.js b/lib/handlebars/logger.js index 1e916a99..bc411b25 100644 --- a/lib/handlebars/logger.js +++ b/lib/handlebars/logger.js @@ -1,4 +1,4 @@ -import {indexOf} from './utils'; +import { indexOf } from './utils'; let logger = { methodMap: ['debug', 'info', 'warn', 'error'], @@ -22,9 +22,13 @@ let logger = { log: function(level, ...message) { level = logger.lookupLevel(level); - if (typeof console !== 'undefined' && logger.lookupLevel(logger.level) <= level) { + if ( + typeof console !== 'undefined' && + logger.lookupLevel(logger.level) <= level + ) { let method = logger.methodMap[level]; - if (!console[method]) { // eslint-disable-line no-console + // eslint-disable-next-line no-console + if (!console[method]) { method = 'log'; } console[method](...message); // eslint-disable-line no-console diff --git a/lib/handlebars/no-conflict.js b/lib/handlebars/no-conflict.js index 40a44d7a..618b750f 100644 --- a/lib/handlebars/no-conflict.js +++ b/lib/handlebars/no-conflict.js @@ -1,8 +1,7 @@ -/* global window */ export default function(Handlebars) { /* istanbul ignore next */ let root = typeof global !== 'undefined' ? global : window, - $Handlebars = root.Handlebars; + $Handlebars = root.Handlebars; /* istanbul ignore next */ Handlebars.noConflict = function() { if (root.Handlebars === Handlebars) { diff --git a/lib/handlebars/runtime.js b/lib/handlebars/runtime.js index 7292ed45..75224977 100644 --- a/lib/handlebars/runtime.js +++ b/lib/handlebars/runtime.js @@ -1,30 +1,47 @@ import * as Utils from './utils'; import Exception from './exception'; -import {COMPILER_REVISION, createFrame, LAST_COMPATIBLE_COMPILER_REVISION, REVISION_CHANGES} from './base'; -import {moveHelperToHooks} from './helpers'; +import { + COMPILER_REVISION, + createFrame, + LAST_COMPATIBLE_COMPILER_REVISION, + REVISION_CHANGES +} from './base'; +import { moveHelperToHooks } from './helpers'; export function checkRevision(compilerInfo) { - const compilerRevision = compilerInfo && compilerInfo[0] || 1, - currentRevision = COMPILER_REVISION; + const compilerRevision = (compilerInfo && compilerInfo[0]) || 1, + currentRevision = COMPILER_REVISION; - if (compilerRevision >= LAST_COMPATIBLE_COMPILER_REVISION && compilerRevision <= COMPILER_REVISION) { + if ( + compilerRevision >= LAST_COMPATIBLE_COMPILER_REVISION && + compilerRevision <= COMPILER_REVISION + ) { return; } if (compilerRevision < LAST_COMPATIBLE_COMPILER_REVISION) { const runtimeVersions = REVISION_CHANGES[currentRevision], - compilerVersions = REVISION_CHANGES[compilerRevision]; - throw new Exception('Template was precompiled with an older version of Handlebars than the current runtime. ' + - 'Please update your precompiler to a newer version (' + runtimeVersions + ') or downgrade your runtime to an older version (' + compilerVersions + ').'); + compilerVersions = REVISION_CHANGES[compilerRevision]; + throw new Exception( + 'Template was precompiled with an older version of Handlebars than the current runtime. ' + + 'Please update your precompiler to a newer version (' + + runtimeVersions + + ') or downgrade your runtime to an older version (' + + compilerVersions + + ').' + ); } else { // Use the embedded version info since the runtime doesn't know about this revision yet - throw new Exception('Template was precompiled with a newer version of Handlebars than the current runtime. ' + - 'Please update your runtime to a newer version (' + compilerInfo[1] + ').'); + throw new Exception( + 'Template was precompiled with a newer version of Handlebars than the current runtime. ' + + 'Please update your runtime to a newer version (' + + compilerInfo[1] + + ').' + ); } } export function template(templateSpec, env) { - /* istanbul ignore next */ if (!env) { throw new Exception('No environment passed to template'); @@ -40,7 +57,8 @@ export function template(templateSpec, env) { env.VM.checkRevision(templateSpec.compiler); // backwards compatibility for precompiled templates with compiler-version 7 (<4.3.0) - const templateWasPrecompiledWithCompilerV7 = templateSpec.compiler && templateSpec.compiler[0] === 7; + const templateWasPrecompiledWithCompilerV7 = + templateSpec.compiler && templateSpec.compiler[0] === 7; function invokePartialWrapper(partial, context, options) { if (options.hash) { @@ -48,12 +66,21 @@ export function template(templateSpec, env) { } partial = env.VM.resolvePartial.call(this, partial, context, options); - let optionsWithHooks = Utils.extend({}, options, {hooks: this.hooks}); + let optionsWithHooks = Utils.extend({}, options, { hooks: this.hooks }); - let result = env.VM.invokePartial.call(this, partial, context, optionsWithHooks); + let result = env.VM.invokePartial.call( + this, + partial, + context, + optionsWithHooks + ); if (result == null && env.compile) { - options.partials[options.name] = env.compile(partial, templateSpec.compilerOptions, env); + options.partials[options.name] = env.compile( + partial, + templateSpec.compilerOptions, + env + ); result = options.partials[options.name](context, optionsWithHooks); } if (result != null) { @@ -70,7 +97,11 @@ export function template(templateSpec, env) { } return result; } else { - throw new Exception('The partial ' + options.name + ' could not be compiled when running in runtime-only mode'); + throw new Exception( + 'The partial ' + + options.name + + ' could not be compiled when running in runtime-only mode' + ); } } @@ -78,7 +109,9 @@ export function template(templateSpec, env) { let container = { strict: function(obj, name, loc) { if (!obj || !(name in obj)) { - throw new Exception('"' + name + '" not defined in ' + obj, { loc: loc }); + throw new Exception('"' + name + '" not defined in ' + obj, { + loc: loc + }); } return obj[name]; }, @@ -106,9 +139,17 @@ export function template(templateSpec, env) { programs: [], program: function(i, data, declaredBlockParams, blockParams, depths) { let programWrapper = this.programs[i], - fn = this.fn(i); + fn = this.fn(i); if (data || depths || blockParams || declaredBlockParams) { - programWrapper = wrapProgram(this, i, fn, data, declaredBlockParams, blockParams, depths); + programWrapper = wrapProgram( + this, + i, + fn, + data, + declaredBlockParams, + blockParams, + depths + ); } else if (!programWrapper) { programWrapper = this.programs[i] = wrapProgram(this, i, fn); } @@ -124,7 +165,7 @@ export function template(templateSpec, env) { mergeIfNeeded: function(param, common) { let obj = param || common; - if (param && common && (param !== common)) { + if (param && common && param !== common) { obj = Utils.extend({}, common, param); } @@ -145,19 +186,40 @@ export function template(templateSpec, env) { data = initData(context, data); } let depths, - blockParams = templateSpec.useBlockParams ? [] : undefined; + blockParams = templateSpec.useBlockParams ? [] : undefined; if (templateSpec.useDepths) { if (options.depths) { - depths = context != options.depths[0] ? [context].concat(options.depths) : options.depths; + depths = + context != options.depths[0] + ? [context].concat(options.depths) + : options.depths; } else { depths = [context]; } } - function main(context/*, options*/) { - return '' + templateSpec.main(container, context, container.helpers, container.partials, data, blockParams, depths); + function main(context /*, options*/) { + return ( + '' + + templateSpec.main( + container, + context, + container.helpers, + container.partials, + data, + blockParams, + depths + ) + ); } - main = executeDecorators(templateSpec.main, main, container, options.depths || [], data, blockParams); + main = executeDecorators( + templateSpec.main, + main, + container, + options.depths || [], + data, + blockParams + ); return main(context, options); } ret.isTop = true; @@ -168,43 +230,65 @@ export function template(templateSpec, env) { if (templateSpec.usePartial) { // Use mergeIfNeeded here to prevent compiling global partials multiple times - container.partials = container.mergeIfNeeded(options.partials, env.partials); + container.partials = container.mergeIfNeeded( + options.partials, + env.partials + ); } if (templateSpec.usePartial || templateSpec.useDecorators) { - container.decorators = Utils.extend({}, env.decorators, options.decorators); + container.decorators = Utils.extend( + {}, + env.decorators, + options.decorators + ); } container.hooks = {}; - let keepHelperInHelpers = options.allowCallsToHelperMissing || templateWasPrecompiledWithCompilerV7; + let keepHelperInHelpers = + options.allowCallsToHelperMissing || + templateWasPrecompiledWithCompilerV7; moveHelperToHooks(container, 'helperMissing', keepHelperInHelpers); moveHelperToHooks(container, 'blockHelperMissing', keepHelperInHelpers); - } else { container.helpers = options.helpers; container.partials = options.partials; container.decorators = options.decorators; container.hooks = options.hooks; } - } return ret; } -export function wrapProgram(container, i, fn, data, declaredBlockParams, blockParams, depths) { +export function wrapProgram( + container, + i, + fn, + data, + declaredBlockParams, + blockParams, + depths +) { function prog(context, options = {}) { let currentDepths = depths; - if (depths && context != depths[0] && !(context === container.nullContext && depths[0] === null)) { + if ( + depths && + context != depths[0] && + !(context === container.nullContext && depths[0] === null) + ) { currentDepths = [context].concat(depths); } - return fn(container, - context, - container.helpers, container.partials, - options.data || data, - blockParams && [options.blockParams].concat(blockParams), - currentDepths); + return fn( + container, + context, + container.helpers, + container.partials, + options.data || data, + blockParams && [options.blockParams].concat(blockParams), + currentDepths + ); } prog = executeDecorators(fn, prog, container, depths, data, blockParams); @@ -243,8 +327,10 @@ export function invokePartial(partial, context, options) { options.data = createFrame(options.data); // Wrapper function to get access to currentPartialBlock from the closure let fn = options.fn; - partialBlock = options.data['partial-block'] = function partialBlockWrapper(context, options = {}) { - + partialBlock = options.data['partial-block'] = function partialBlockWrapper( + context, + options = {} + ) { // Restore the partial-block from the closure for the execution of the block // i.e. the part inside the block of the partial call. options.data = createFrame(options.data); @@ -267,7 +353,9 @@ export function invokePartial(partial, context, options) { } } -export function noop() { return ''; } +export function noop() { + return ''; +} function initData(context, data) { if (!data || !('root' in data)) { @@ -280,7 +368,15 @@ function initData(context, data) { function executeDecorators(fn, prog, container, depths, data, blockParams) { if (fn.decorator) { let props = {}; - prog = fn.decorator(prog, props, container, depths && depths[0], data, blockParams, depths); + prog = fn.decorator( + prog, + props, + container, + depths && depths[0], + data, + blockParams, + depths + ); Utils.extend(prog, props); } return prog; diff --git a/lib/handlebars/utils.js b/lib/handlebars/utils.js index 688703b5..7ee41df1 100644 --- a/lib/handlebars/utils.js +++ b/lib/handlebars/utils.js @@ -1,4 +1,3 @@ - const escape = { '&': '&', '<': '<', @@ -10,13 +9,13 @@ const escape = { }; const badChars = /[&<>"'`=]/g, - possible = /[&<>"'`=]/; + possible = /[&<>"'`=]/; function escapeChar(chr) { return escape[chr]; } -export function extend(obj/* , ...source */) { +export function extend(obj /* , ...source */) { for (let i = 1; i < arguments.length; i++) { for (let key in arguments[i]) { if (Object.prototype.hasOwnProperty.call(arguments[i], key)) { @@ -40,16 +39,23 @@ let isFunction = function(value) { /* istanbul ignore next */ if (isFunction(/x/)) { isFunction = function(value) { - return typeof value === 'function' && toString.call(value) === '[object Function]'; + return ( + typeof value === 'function' && + toString.call(value) === '[object Function]' + ); }; } -export {isFunction}; +export { isFunction }; /* eslint-enable func-style */ /* istanbul ignore next */ -export const isArray = Array.isArray || function(value) { - return (value && typeof value === 'object') ? toString.call(value) === '[object Array]' : false; -}; +export const isArray = + Array.isArray || + function(value) { + return value && typeof value === 'object' + ? toString.call(value) === '[object Array]' + : false; + }; // Older IE versions do not directly support indexOf so we must implement our own, sadly. export function indexOf(array, value) { @@ -61,7 +67,6 @@ export function indexOf(array, value) { return -1; } - export function escapeExpression(string) { if (typeof string !== 'string') { // don't escape SafeStrings, since they're already safe @@ -79,7 +84,9 @@ export function escapeExpression(string) { string = '' + string; } - if (!possible.test(string)) { return string; } + if (!possible.test(string)) { + return string; + } return string.replace(badChars, escapeChar); } @@ -98,4 +105,3 @@ export function createFrame(object) { frame._parent = object; return frame; } - diff --git a/lib/precompiler.js b/lib/precompiler.js index ecf264ef..689c9f4d 100644 --- a/lib/precompiler.js +++ b/lib/precompiler.js @@ -2,9 +2,8 @@ import Async from 'neo-async'; import fs from 'fs'; import * as Handlebars from './handlebars'; -import {basename} from 'path'; -import {SourceMapConsumer, SourceNode} from 'source-map'; - +import { basename } from 'path'; +import { SourceMapConsumer, SourceNode } from 'source-map'; module.exports.loadTemplates = function(opts, callback) { loadStrings(opts, function(err, strings) { @@ -25,14 +24,19 @@ module.exports.loadTemplates = function(opts, callback) { function loadStrings(opts, callback) { let strings = arrayCast(opts.string), - names = arrayCast(opts.name); + names = arrayCast(opts.name); - if (names.length !== strings.length - && strings.length > 1) { - return callback(new Handlebars.Exception('Number of names did not match the number of string inputs')); + if (names.length !== strings.length && strings.length > 1) { + return callback( + new Handlebars.Exception( + 'Number of names did not match the number of string inputs' + ) + ); } - Async.map(strings, function(string, callback) { + Async.map( + strings, + function(string, callback) { if (string !== '-') { callback(undefined, string); } else { @@ -55,80 +59,94 @@ function loadStrings(opts, callback) { source: string })); callback(err, strings); - }); + } + ); } function loadFiles(opts, callback) { // Build file extension pattern - let extension = (opts.extension || 'handlebars').replace(/[\\^$*+?.():=!|{}\-[\]]/g, function(arg) { return '\\' + arg; }); + let extension = (opts.extension || 'handlebars').replace( + /[\\^$*+?.():=!|{}\-[\]]/g, + function(arg) { + return '\\' + arg; + } + ); extension = new RegExp('\\.' + extension + '$'); let ret = [], - queue = (opts.files || []).map((template) => ({template, root: opts.root})); - Async.whilst(() => queue.length, function(callback) { - let {template: path, root} = queue.shift(); + queue = (opts.files || []).map(template => ({ template, root: opts.root })); + Async.whilst( + () => queue.length, + function(callback) { + let { template: path, root } = queue.shift(); - fs.stat(path, function(err, stat) { - if (err) { - return callback(new Handlebars.Exception(`Unable to open template file "${path}"`)); - } + fs.stat(path, function(err, stat) { + if (err) { + return callback( + new Handlebars.Exception(`Unable to open template file "${path}"`) + ); + } - if (stat.isDirectory()) { - opts.hasDirectory = true; + if (stat.isDirectory()) { + opts.hasDirectory = true; - fs.readdir(path, function(err, children) { - /* istanbul ignore next : Race condition that being too lazy to test */ - if (err) { - return callback(err); - } - children.forEach(function(file) { - let childPath = path + '/' + file; - - if (extension.test(childPath) || fs.statSync(childPath).isDirectory()) { - queue.push({template: childPath, root: root || path}); + fs.readdir(path, function(err, children) { + /* istanbul ignore next : Race condition that being too lazy to test */ + if (err) { + return callback(err); } - }); + children.forEach(function(file) { + let childPath = path + '/' + file; - callback(); - }); + if ( + extension.test(childPath) || + fs.statSync(childPath).isDirectory() + ) { + queue.push({ template: childPath, root: root || path }); + } + }); + + callback(); + }); + } else { + fs.readFile(path, 'utf8', function(err, data) { + /* istanbul ignore next : Race condition that being too lazy to test */ + if (err) { + return callback(err); + } + + if (opts.bom && data.indexOf('\uFEFF') === 0) { + data = data.substring(1); + } + + // Clean the template name + let name = path; + if (!root) { + name = basename(name); + } else if (name.indexOf(root) === 0) { + name = name.substring(root.length + 1); + } + name = name.replace(extension, ''); + + ret.push({ + path: path, + name: name, + source: data + }); + + callback(); + }); + } + }); + }, + function(err) { + if (err) { + callback(err); } else { - fs.readFile(path, 'utf8', function(err, data) { - /* istanbul ignore next : Race condition that being too lazy to test */ - if (err) { - return callback(err); - } - - if (opts.bom && data.indexOf('\uFEFF') === 0) { - data = data.substring(1); - } - - // Clean the template name - let name = path; - if (!root) { - name = basename(name); - } else if (name.indexOf(root) === 0) { - name = name.substring(root.length + 1); - } - name = name.replace(extension, ''); - - ret.push({ - path: path, - name: name, - source: data - }); - - callback(); - }); + callback(undefined, ret); } - }); - }, - function(err) { - if (err) { - callback(err); - } else { - callback(undefined, ret); } - }); + ); } module.exports.cli = function(opts) { @@ -138,7 +156,9 @@ module.exports.cli = function(opts) { } if (!opts.templates.length && !opts.hasDirectory) { - throw new Handlebars.Exception('Must define at least one template or directory.'); + throw new Handlebars.Exception( + 'Must define at least one template or directory.' + ); } if (opts.simple && opts.min) { @@ -147,12 +167,18 @@ module.exports.cli = function(opts) { const multiple = opts.templates.length !== 1 || opts.hasDirectory; if (opts.simple && multiple) { - throw new Handlebars.Exception('Unable to output multiple templates in simple mode'); + throw new Handlebars.Exception( + 'Unable to output multiple templates in simple mode' + ); } // Force simple mode if we have only one template and it's unnamed. - if (!opts.amd && !opts.commonjs && opts.templates.length === 1 - && !opts.templates[0].name) { + if ( + !opts.amd && + !opts.commonjs && + opts.templates.length === 1 && + !opts.templates[0].name + ) { opts.simple = true; } @@ -172,7 +198,11 @@ module.exports.cli = function(opts) { let output = new SourceNode(); if (!opts.simple) { if (opts.amd) { - output.add('define([\'' + opts.handlebarPath + 'handlebars.runtime\'], function(Handlebars) {\n Handlebars = Handlebars["default"];'); + output.add( + "define(['" + + opts.handlebarPath + + 'handlebars.runtime\'], function(Handlebars) {\n Handlebars = Handlebars["default"];' + ); } else if (opts.commonjs) { output.add('var Handlebars = require("' + opts.commonjs + '");'); } else { @@ -206,7 +236,10 @@ module.exports.cli = function(opts) { // If we are generating a source map, we have to reconstruct the SourceNode object if (opts.map) { let consumer = new SourceMapConsumer(precompiled.map); - precompiled = SourceNode.fromStringWithSourceMap(precompiled.code, consumer); + precompiled = SourceNode.fromStringWithSourceMap( + precompiled.code, + consumer + ); } if (opts.simple) { @@ -219,7 +252,14 @@ module.exports.cli = function(opts) { if (opts.amd && !multiple) { output.add('return '); } - output.add([objectName, '[\'', template.name, '\'] = template(', precompiled, ');\n']); + output.add([ + objectName, + "['", + template.name, + "'] = template(", + precompiled, + ');\n' + ]); } }); @@ -286,7 +326,9 @@ function minify(output, sourceMapFile) { throw e; } // it does not exist! - console.error('Code minimization is disabled due to missing uglify-js dependency'); + console.error( + 'Code minimization is disabled due to missing uglify-js dependency' + ); return output; } return require('uglify-js').minify(output.code, { diff --git a/spec/ast.js b/spec/ast.js index ffd0a958..1f4146ae 100644 --- a/spec/ast.js +++ b/spec/ast.js @@ -7,44 +7,102 @@ describe('ast', function() { describe('BlockStatement', function() { it('should throw on mustache mismatch', function() { - shouldThrow(function() { - handlebarsEnv.parse('\n {{#foo}}{{/bar}}'); - }, Handlebars.Exception, "foo doesn't match bar - 2:5"); + shouldThrow( + function() { + handlebarsEnv.parse('\n {{#foo}}{{/bar}}'); + }, + Handlebars.Exception, + "foo doesn't match bar - 2:5" + ); }); }); describe('helpers', function() { describe('#helperExpression', function() { it('should handle mustache statements', function() { - equals(AST.helpers.helperExpression({type: 'MustacheStatement', params: [], hash: undefined}), false); - equals(AST.helpers.helperExpression({type: 'MustacheStatement', params: [1], hash: undefined}), true); - equals(AST.helpers.helperExpression({type: 'MustacheStatement', params: [], hash: {}}), true); + equals( + AST.helpers.helperExpression({ + type: 'MustacheStatement', + params: [], + hash: undefined + }), + false + ); + equals( + AST.helpers.helperExpression({ + type: 'MustacheStatement', + params: [1], + hash: undefined + }), + true + ); + equals( + AST.helpers.helperExpression({ + type: 'MustacheStatement', + params: [], + hash: {} + }), + true + ); }); it('should handle block statements', function() { - equals(AST.helpers.helperExpression({type: 'BlockStatement', params: [], hash: undefined}), false); - equals(AST.helpers.helperExpression({type: 'BlockStatement', params: [1], hash: undefined}), true); - equals(AST.helpers.helperExpression({type: 'BlockStatement', params: [], hash: {}}), true); + equals( + AST.helpers.helperExpression({ + type: 'BlockStatement', + params: [], + hash: undefined + }), + false + ); + equals( + AST.helpers.helperExpression({ + type: 'BlockStatement', + params: [1], + hash: undefined + }), + true + ); + equals( + AST.helpers.helperExpression({ + type: 'BlockStatement', + params: [], + hash: {} + }), + true + ); }); it('should handle subexpressions', function() { - equals(AST.helpers.helperExpression({type: 'SubExpression'}), true); + equals(AST.helpers.helperExpression({ type: 'SubExpression' }), true); }); it('should work with non-helper nodes', function() { - equals(AST.helpers.helperExpression({type: 'Program'}), false); + equals(AST.helpers.helperExpression({ type: 'Program' }), false); - equals(AST.helpers.helperExpression({type: 'PartialStatement'}), false); - equals(AST.helpers.helperExpression({type: 'ContentStatement'}), false); - equals(AST.helpers.helperExpression({type: 'CommentStatement'}), false); + equals( + AST.helpers.helperExpression({ type: 'PartialStatement' }), + false + ); + equals( + AST.helpers.helperExpression({ type: 'ContentStatement' }), + false + ); + equals( + AST.helpers.helperExpression({ type: 'CommentStatement' }), + false + ); - equals(AST.helpers.helperExpression({type: 'PathExpression'}), false); + equals(AST.helpers.helperExpression({ type: 'PathExpression' }), false); - equals(AST.helpers.helperExpression({type: 'StringLiteral'}), false); - equals(AST.helpers.helperExpression({type: 'NumberLiteral'}), false); - equals(AST.helpers.helperExpression({type: 'BooleanLiteral'}), false); - equals(AST.helpers.helperExpression({type: 'UndefinedLiteral'}), false); - equals(AST.helpers.helperExpression({type: 'NullLiteral'}), false); + equals(AST.helpers.helperExpression({ type: 'StringLiteral' }), false); + equals(AST.helpers.helperExpression({ type: 'NumberLiteral' }), false); + equals(AST.helpers.helperExpression({ type: 'BooleanLiteral' }), false); + equals( + AST.helpers.helperExpression({ type: 'UndefinedLiteral' }), + false + ); + equals(AST.helpers.helperExpression({ type: 'NullLiteral' }), false); - equals(AST.helpers.helperExpression({type: 'Hash'}), false); - equals(AST.helpers.helperExpression({type: 'HashPair'}), false); + equals(AST.helpers.helperExpression({ type: 'Hash' }), false); + equals(AST.helpers.helperExpression({ type: 'HashPair' }), false); }); }); }); @@ -61,17 +119,18 @@ describe('ast', function() { /* eslint-disable no-multi-spaces */ ast = Handlebars.parse( - 'line 1 {{line1Token}}\n' // 1 - + ' line 2 {{line2token}}\n' // 2 - + ' line 3 {{#blockHelperOnLine3}}\n' // 3 - + 'line 4{{line4token}}\n' // 4 - + 'line5{{else}}\n' // 5 - + '{{line6Token}}\n' // 6 - + '{{/blockHelperOnLine3}}\n' // 7 - + '{{#open}}\n' // 8 - + '{{else inverse}}\n' // 9 - + '{{else}}\n' // 10 - + '{{/open}}'); // 11 + 'line 1 {{line1Token}}\n' + // 1 + ' line 2 {{line2token}}\n' + // 2 + ' line 3 {{#blockHelperOnLine3}}\n' + // 3 + 'line 4{{line4token}}\n' + // 4 + 'line5{{else}}\n' + // 5 + '{{line6Token}}\n' + // 6 + '{{/blockHelperOnLine3}}\n' + // 7 + '{{#open}}\n' + // 8 + '{{else inverse}}\n' + // 9 + '{{else}}\n' + // 10 + '{{/open}}' + ); // 11 /* eslint-enable no-multi-spaces */ body = ast.body; @@ -92,35 +151,35 @@ describe('ast', function() { it('gets MustacheStatement line numbers correct across newlines', function() { var secondMustacheStatement = body[3]; testColumns(secondMustacheStatement, 2, 2, 8, 22); - }); + }); - it('gets the block helper information correct', function() { - var blockHelperNode = body[5]; - testColumns(blockHelperNode, 3, 7, 8, 23); - }); + it('gets the block helper information correct', function() { + var blockHelperNode = body[5]; + testColumns(blockHelperNode, 3, 7, 8, 23); + }); - it('correctly records the line numbers the program of a block helper', function() { - var blockHelperNode = body[5], - program = blockHelperNode.program; + it('correctly records the line numbers the program of a block helper', function() { + var blockHelperNode = body[5], + program = blockHelperNode.program; - testColumns(program, 3, 5, 31, 5); - }); + testColumns(program, 3, 5, 31, 5); + }); - it('correctly records the line numbers of an inverse of a block helper', function() { - var blockHelperNode = body[5], - inverse = blockHelperNode.inverse; + it('correctly records the line numbers of an inverse of a block helper', function() { + var blockHelperNode = body[5], + inverse = blockHelperNode.inverse; - testColumns(inverse, 5, 7, 13, 0); - }); + testColumns(inverse, 5, 7, 13, 0); + }); - it('correctly records the line number of chained inverses', function() { - var chainInverseNode = body[7]; + it('correctly records the line number of chained inverses', function() { + var chainInverseNode = body[7]; - testColumns(chainInverseNode.program, 8, 9, 9, 0); - testColumns(chainInverseNode.inverse, 9, 10, 16, 0); - testColumns(chainInverseNode.inverse.body[0].program, 9, 10, 16, 0); - testColumns(chainInverseNode.inverse.body[0].inverse, 10, 11, 8, 0); - }); + testColumns(chainInverseNode.program, 8, 9, 9, 0); + testColumns(chainInverseNode.inverse, 9, 10, 16, 0); + testColumns(chainInverseNode.inverse.body[0].program, 9, 10, 16, 0); + testColumns(chainInverseNode.inverse.body[0].inverse, 10, 11, 8, 0); + }); }); describe('whitespace control', function() { @@ -149,7 +208,9 @@ describe('ast', function() { }); it('block statements', function() { - var ast = Handlebars.parseWithoutProcessing(' {{# comment~}} \nfoo\n {{~/comment}}'); + var ast = Handlebars.parseWithoutProcessing( + ' {{# comment~}} \nfoo\n {{~/comment}}' + ); equals(ast.body[0].value, ' '); equals(ast.body[1].program.body[0].value, ' \nfoo\n '); @@ -167,7 +228,9 @@ describe('ast', function() { }); describe('blocks - parseWithoutProcessing', function() { it('block mustaches', function() { - var ast = Handlebars.parseWithoutProcessing(' {{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} '), + var ast = Handlebars.parseWithoutProcessing( + ' {{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} ' + ), block = ast.body[1]; equals(ast.body[0].value, ' '); @@ -178,13 +241,17 @@ describe('ast', function() { equals(ast.body[2].value, ' '); }); it('initial block mustaches', function() { - var ast = Handlebars.parseWithoutProcessing('{{# comment}} \nfoo\n {{/comment}}'), + var ast = Handlebars.parseWithoutProcessing( + '{{# comment}} \nfoo\n {{/comment}}' + ), block = ast.body[0]; equals(block.program.body[0].value, ' \nfoo\n '); }); it('mustaches with children', function() { - var ast = Handlebars.parseWithoutProcessing('{{# comment}} \n{{foo}}\n {{/comment}}'), + var ast = Handlebars.parseWithoutProcessing( + '{{# comment}} \n{{foo}}\n {{/comment}}' + ), block = ast.body[0]; equals(block.program.body[0].value, ' \n'); @@ -192,7 +259,9 @@ describe('ast', function() { equals(block.program.body[2].value, '\n '); }); it('nested block mustaches', function() { - var ast = Handlebars.parseWithoutProcessing('{{#foo}} \n{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} \n{{/foo}}'), + var ast = Handlebars.parseWithoutProcessing( + '{{#foo}} \n{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} \n{{/foo}}' + ), body = ast.body[0].program.body, block = body[1]; @@ -202,8 +271,10 @@ describe('ast', function() { equals(block.inverse.body[0].value, ' \n bar \n '); }); it('column 0 block mustaches', function() { - var ast = Handlebars.parseWithoutProcessing('test\n{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} '), - block = ast.body[1]; + var ast = Handlebars.parseWithoutProcessing( + 'test\n{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} ' + ), + block = ast.body[1]; equals(ast.body[0].omit, undefined); @@ -215,8 +286,10 @@ describe('ast', function() { }); describe('blocks', function() { it('marks block mustaches as standalone', function() { - var ast = Handlebars.parse(' {{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} '), - block = ast.body[1]; + var ast = Handlebars.parse( + ' {{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} ' + ), + block = ast.body[1]; equals(ast.body[0].value, ''); @@ -227,22 +300,24 @@ describe('ast', function() { }); it('marks initial block mustaches as standalone', function() { var ast = Handlebars.parse('{{# comment}} \nfoo\n {{/comment}}'), - block = ast.body[0]; + block = ast.body[0]; equals(block.program.body[0].value, 'foo\n'); }); it('marks mustaches with children as standalone', function() { var ast = Handlebars.parse('{{# comment}} \n{{foo}}\n {{/comment}}'), - block = ast.body[0]; + block = ast.body[0]; equals(block.program.body[0].value, ''); equals(block.program.body[1].path.original, 'foo'); equals(block.program.body[2].value, '\n'); }); it('marks nested block mustaches as standalone', function() { - var ast = Handlebars.parse('{{#foo}} \n{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} \n{{/foo}}'), - body = ast.body[0].program.body, - block = body[1]; + var ast = Handlebars.parse( + '{{#foo}} \n{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} \n{{/foo}}' + ), + body = ast.body[0].program.body, + block = body[1]; equals(body[0].value, ''); @@ -252,9 +327,11 @@ describe('ast', function() { equals(body[0].value, ''); }); it('does not mark nested block mustaches as standalone', function() { - var ast = Handlebars.parse('{{#foo}} {{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} {{/foo}}'), - body = ast.body[0].program.body, - block = body[1]; + var ast = Handlebars.parse( + '{{#foo}} {{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} {{/foo}}' + ), + body = ast.body[0].program.body, + block = body[1]; equals(body[0].omit, undefined); @@ -264,9 +341,11 @@ describe('ast', function() { equals(body[0].omit, undefined); }); it('does not mark nested initial block mustaches as standalone', function() { - var ast = Handlebars.parse('{{#foo}}{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}}{{/foo}}'), - body = ast.body[0].program.body, - block = body[0]; + var ast = Handlebars.parse( + '{{#foo}}{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}}{{/foo}}' + ), + body = ast.body[0].program.body, + block = body[0]; equals(block.program.body[0].value, ' \nfoo\n'); equals(block.inverse.body[0].value, ' bar \n '); @@ -275,8 +354,10 @@ describe('ast', function() { }); it('marks column 0 block mustaches as standalone', function() { - var ast = Handlebars.parse('test\n{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} '), - block = ast.body[1]; + var ast = Handlebars.parse( + 'test\n{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} ' + ), + block = ast.body[1]; equals(ast.body[0].omit, undefined); @@ -348,4 +429,3 @@ describe('ast', function() { }); }); }); - diff --git a/spec/basic.js b/spec/basic.js index e4b25366..65d22872 100644 --- a/spec/basic.js +++ b/spec/basic.js @@ -18,8 +18,12 @@ describe('basic context', function() { }); it('compiling with a basic context', function() { - shouldCompileTo('Goodbye\n{{cruel}}\n{{world}}!', {cruel: 'cruel', world: 'world'}, 'Goodbye\ncruel\nworld!', - 'It works if all the required keys are provided'); + shouldCompileTo( + 'Goodbye\n{{cruel}}\n{{world}}!', + { cruel: 'cruel', world: 'world' }, + 'Goodbye\ncruel\nworld!', + 'It works if all the required keys are provided' + ); }); it('compiling with a string context', function() { @@ -27,15 +31,26 @@ describe('basic context', function() { }); it('compiling with an undefined context', function() { - shouldCompileTo('Goodbye\n{{cruel}}\n{{world.bar}}!', undefined, 'Goodbye\n\n!'); + shouldCompileTo( + 'Goodbye\n{{cruel}}\n{{world.bar}}!', + undefined, + 'Goodbye\n\n!' + ); - shouldCompileTo('{{#unless foo}}Goodbye{{../test}}{{test2}}{{/unless}}', undefined, 'Goodbye'); + shouldCompileTo( + '{{#unless foo}}Goodbye{{../test}}{{test2}}{{/unless}}', + undefined, + 'Goodbye' + ); }); it('comments', function() { - shouldCompileTo('{{! Goodbye}}Goodbye\n{{cruel}}\n{{world}}!', - {cruel: 'cruel', world: 'world'}, 'Goodbye\ncruel\nworld!', - 'comments are ignored'); + shouldCompileTo( + '{{! Goodbye}}Goodbye\n{{cruel}}\n{{world}}!', + { cruel: 'cruel', world: 'world' }, + 'Goodbye\ncruel\nworld!', + 'comments are ignored' + ); shouldCompileTo(' {{~! comment ~}} blah', {}, 'blah'); shouldCompileTo(' {{~!-- long-comment --~}} blah', {}, 'blah'); @@ -47,237 +62,506 @@ describe('basic context', function() { it('boolean', function() { var string = '{{#goodbye}}GOODBYE {{/goodbye}}cruel {{world}}!'; - shouldCompileTo(string, {goodbye: true, world: 'world'}, 'GOODBYE cruel world!', - 'booleans show the contents when true'); + shouldCompileTo( + string, + { goodbye: true, world: 'world' }, + 'GOODBYE cruel world!', + 'booleans show the contents when true' + ); - shouldCompileTo(string, {goodbye: false, world: 'world'}, 'cruel world!', - 'booleans do not show the contents when false'); + shouldCompileTo( + string, + { goodbye: false, world: 'world' }, + 'cruel world!', + 'booleans do not show the contents when false' + ); }); it('zeros', function() { - shouldCompileTo('num1: {{num1}}, num2: {{num2}}', {num1: 42, num2: 0}, - 'num1: 42, num2: 0'); + shouldCompileTo( + 'num1: {{num1}}, num2: {{num2}}', + { num1: 42, num2: 0 }, + 'num1: 42, num2: 0' + ); shouldCompileTo('num: {{.}}', 0, 'num: 0'); - shouldCompileTo('num: {{num1/num2}}', {num1: {num2: 0}}, 'num: 0'); + shouldCompileTo('num: {{num1/num2}}', { num1: { num2: 0 } }, 'num: 0'); }); it('false', function() { /* eslint-disable no-new-wrappers */ - shouldCompileTo('val1: {{val1}}, val2: {{val2}}', {val1: false, val2: new Boolean(false)}, 'val1: false, val2: false'); + shouldCompileTo( + 'val1: {{val1}}, val2: {{val2}}', + { val1: false, val2: new Boolean(false) }, + 'val1: false, val2: false' + ); shouldCompileTo('val: {{.}}', false, 'val: false'); - shouldCompileTo('val: {{val1/val2}}', {val1: {val2: false}}, 'val: false'); + shouldCompileTo( + 'val: {{val1/val2}}', + { val1: { val2: false } }, + 'val: false' + ); - shouldCompileTo('val1: {{{val1}}}, val2: {{{val2}}}', {val1: false, val2: new Boolean(false)}, 'val1: false, val2: false'); - shouldCompileTo('val: {{{val1/val2}}}', {val1: {val2: false}}, 'val: false'); + shouldCompileTo( + 'val1: {{{val1}}}, val2: {{{val2}}}', + { val1: false, val2: new Boolean(false) }, + 'val1: false, val2: false' + ); + shouldCompileTo( + 'val: {{{val1/val2}}}', + { val1: { val2: false } }, + 'val: false' + ); /* eslint-enable */ }); it('should handle undefined and null', function() { - shouldCompileTo('{{awesome undefined null}}', - { - awesome: function(_undefined, _null, options) { - return (_undefined === undefined) + ' ' + (_null === null) + ' ' + (typeof options); - } - }, - 'true true object'); - shouldCompileTo('{{undefined}}', - { - 'undefined': function() { - return 'undefined!'; - } - }, - 'undefined!'); - shouldCompileTo('{{null}}', - { - 'null': function() { - return 'null!'; - } - }, - 'null!'); + shouldCompileTo( + '{{awesome undefined null}}', + { + awesome: function(_undefined, _null, options) { + return ( + (_undefined === undefined) + + ' ' + + (_null === null) + + ' ' + + typeof options + ); + } + }, + 'true true object' + ); + shouldCompileTo( + '{{undefined}}', + { + undefined: function() { + return 'undefined!'; + } + }, + 'undefined!' + ); + shouldCompileTo( + '{{null}}', + { + null: function() { + return 'null!'; + } + }, + 'null!' + ); }); it('newlines', function() { - shouldCompileTo("Alan's\nTest", {}, "Alan's\nTest"); - shouldCompileTo("Alan's\rTest", {}, "Alan's\rTest"); + shouldCompileTo("Alan's\nTest", {}, "Alan's\nTest"); + shouldCompileTo("Alan's\rTest", {}, "Alan's\rTest"); }); it('escaping text', function() { - shouldCompileTo("Awesome's", {}, "Awesome's", "text is escaped so that it doesn't get caught on single quotes"); - shouldCompileTo('Awesome\\', {}, 'Awesome\\', "text is escaped so that the closing quote can't be ignored"); - shouldCompileTo('Awesome\\\\ foo', {}, 'Awesome\\\\ foo', "text is escaped so that it doesn't mess up backslashes"); - shouldCompileTo('Awesome {{foo}}', {foo: '\\'}, 'Awesome \\', "text is escaped so that it doesn't mess up backslashes"); - shouldCompileTo(" ' ' ", {}, " ' ' ", 'double quotes never produce invalid javascript'); + shouldCompileTo( + "Awesome's", + {}, + "Awesome's", + "text is escaped so that it doesn't get caught on single quotes" + ); + shouldCompileTo( + 'Awesome\\', + {}, + 'Awesome\\', + "text is escaped so that the closing quote can't be ignored" + ); + shouldCompileTo( + 'Awesome\\\\ foo', + {}, + 'Awesome\\\\ foo', + "text is escaped so that it doesn't mess up backslashes" + ); + shouldCompileTo( + 'Awesome {{foo}}', + { foo: '\\' }, + 'Awesome \\', + "text is escaped so that it doesn't mess up backslashes" + ); + shouldCompileTo( + " ' ' ", + {}, + " ' ' ", + 'double quotes never produce invalid javascript' + ); }); it('escaping expressions', function() { - shouldCompileTo('{{{awesome}}}', {awesome: '&\'\\<>'}, '&\'\\<>', - "expressions with 3 handlebars aren't escaped"); + shouldCompileTo( + '{{{awesome}}}', + { awesome: "&'\\<>" }, + "&'\\<>", + "expressions with 3 handlebars aren't escaped" + ); - shouldCompileTo('{{&awesome}}', {awesome: '&\'\\<>'}, '&\'\\<>', - "expressions with {{& handlebars aren't escaped"); + shouldCompileTo( + '{{&awesome}}', + { awesome: "&'\\<>" }, + "&'\\<>", + "expressions with {{& handlebars aren't escaped" + ); - shouldCompileTo('{{awesome}}', {awesome: "&\"'`\\<>"}, '&"'`\\<>', - 'by default expressions should be escaped'); + shouldCompileTo( + '{{awesome}}', + { awesome: '&"\'`\\<>' }, + '&"'`\\<>', + 'by default expressions should be escaped' + ); - shouldCompileTo('{{awesome}}', {awesome: 'Escaped, looks like: <b>'}, 'Escaped, <b> looks like: &lt;b&gt;', - 'escaping should properly handle amperstands'); + shouldCompileTo( + '{{awesome}}', + { awesome: 'Escaped, looks like: <b>' }, + 'Escaped, <b> looks like: &lt;b&gt;', + 'escaping should properly handle amperstands' + ); }); it("functions returning safestrings shouldn't be escaped", function() { - var hash = {awesome: function() { return new Handlebars.SafeString('&\'\\<>'); }}; - shouldCompileTo('{{awesome}}', hash, '&\'\\<>', - "functions returning safestrings aren't escaped"); + var hash = { + awesome: function() { + return new Handlebars.SafeString("&'\\<>"); + } + }; + shouldCompileTo( + '{{awesome}}', + hash, + "&'\\<>", + "functions returning safestrings aren't escaped" + ); }); it('functions', function() { - shouldCompileTo('{{awesome}}', {awesome: function() { return 'Awesome'; }}, 'Awesome', - 'functions are called and render their output'); - shouldCompileTo('{{awesome}}', {awesome: function() { return this.more; }, more: 'More awesome'}, 'More awesome', - 'functions are bound to the context'); + shouldCompileTo( + '{{awesome}}', + { + awesome: function() { + return 'Awesome'; + } + }, + 'Awesome', + 'functions are called and render their output' + ); + shouldCompileTo( + '{{awesome}}', + { + awesome: function() { + return this.more; + }, + more: 'More awesome' + }, + 'More awesome', + 'functions are bound to the context' + ); }); it('functions with context argument', function() { - shouldCompileTo('{{awesome frank}}', - {awesome: function(context) { return context; }, - frank: 'Frank'}, - 'Frank', 'functions are called with context arguments'); + shouldCompileTo( + '{{awesome frank}}', + { + awesome: function(context) { + return context; + }, + frank: 'Frank' + }, + 'Frank', + 'functions are called with context arguments' + ); }); it('pathed functions with context argument', function() { - shouldCompileTo('{{bar.awesome frank}}', - {bar: {awesome: function(context) { return context; }}, - frank: 'Frank'}, - 'Frank', 'functions are called with context arguments'); + shouldCompileTo( + '{{bar.awesome frank}}', + { + bar: { + awesome: function(context) { + return context; + } + }, + frank: 'Frank' + }, + 'Frank', + 'functions are called with context arguments' + ); }); it('depthed functions with context argument', function() { - shouldCompileTo('{{#with frank}}{{../awesome .}}{{/with}}', - {awesome: function(context) { return context; }, - frank: 'Frank'}, - 'Frank', 'functions are called with context arguments'); + shouldCompileTo( + '{{#with frank}}{{../awesome .}}{{/with}}', + { + awesome: function(context) { + return context; + }, + frank: 'Frank' + }, + 'Frank', + 'functions are called with context arguments' + ); }); it('block functions with context argument', function() { - shouldCompileTo('{{#awesome 1}}inner {{.}}{{/awesome}}', - {awesome: function(context, options) { return options.fn(context); }}, - 'inner 1', 'block functions are called with context and options'); + shouldCompileTo( + '{{#awesome 1}}inner {{.}}{{/awesome}}', + { + awesome: function(context, options) { + return options.fn(context); + } + }, + 'inner 1', + 'block functions are called with context and options' + ); }); it('depthed block functions with context argument', function() { - shouldCompileTo('{{#with value}}{{#../awesome 1}}inner {{.}}{{/../awesome}}{{/with}}', - {value: true, awesome: function(context, options) { return options.fn(context); }}, - 'inner 1', 'block functions are called with context and options'); + shouldCompileTo( + '{{#with value}}{{#../awesome 1}}inner {{.}}{{/../awesome}}{{/with}}', + { + value: true, + awesome: function(context, options) { + return options.fn(context); + } + }, + 'inner 1', + 'block functions are called with context and options' + ); }); it('block functions without context argument', function() { - shouldCompileTo('{{#awesome}}inner{{/awesome}}', - {awesome: function(options) { return options.fn(this); }}, - 'inner', 'block functions are called with options'); + shouldCompileTo( + '{{#awesome}}inner{{/awesome}}', + { + awesome: function(options) { + return options.fn(this); + } + }, + 'inner', + 'block functions are called with options' + ); }); it('pathed block functions without context argument', function() { - shouldCompileTo('{{#foo.awesome}}inner{{/foo.awesome}}', - {foo: {awesome: function() { return this; }}}, - 'inner', 'block functions are called with options'); + shouldCompileTo( + '{{#foo.awesome}}inner{{/foo.awesome}}', + { + foo: { + awesome: function() { + return this; + } + } + }, + 'inner', + 'block functions are called with options' + ); }); it('depthed block functions without context argument', function() { - shouldCompileTo('{{#with value}}{{#../awesome}}inner{{/../awesome}}{{/with}}', - {value: true, awesome: function() { return this; }}, - 'inner', 'block functions are called with options'); + shouldCompileTo( + '{{#with value}}{{#../awesome}}inner{{/../awesome}}{{/with}}', + { + value: true, + awesome: function() { + return this; + } + }, + 'inner', + 'block functions are called with options' + ); }); - it('paths with hyphens', function() { - shouldCompileTo('{{foo-bar}}', {'foo-bar': 'baz'}, 'baz', 'Paths can contain hyphens (-)'); - shouldCompileTo('{{foo.foo-bar}}', {foo: {'foo-bar': 'baz'}}, 'baz', 'Paths can contain hyphens (-)'); - shouldCompileTo('{{foo/foo-bar}}', {foo: {'foo-bar': 'baz'}}, 'baz', 'Paths can contain hyphens (-)'); + shouldCompileTo( + '{{foo-bar}}', + { 'foo-bar': 'baz' }, + 'baz', + 'Paths can contain hyphens (-)' + ); + shouldCompileTo( + '{{foo.foo-bar}}', + { foo: { 'foo-bar': 'baz' } }, + 'baz', + 'Paths can contain hyphens (-)' + ); + shouldCompileTo( + '{{foo/foo-bar}}', + { foo: { 'foo-bar': 'baz' } }, + 'baz', + 'Paths can contain hyphens (-)' + ); }); it('nested paths', function() { - shouldCompileTo('Goodbye {{alan/expression}} world!', {alan: {expression: 'beautiful'}}, - 'Goodbye beautiful world!', 'Nested paths access nested objects'); + shouldCompileTo( + 'Goodbye {{alan/expression}} world!', + { alan: { expression: 'beautiful' } }, + 'Goodbye beautiful world!', + 'Nested paths access nested objects' + ); }); it('nested paths with empty string value', function() { - shouldCompileTo('Goodbye {{alan/expression}} world!', {alan: {expression: ''}}, - 'Goodbye world!', 'Nested paths access nested objects with empty string'); + shouldCompileTo( + 'Goodbye {{alan/expression}} world!', + { alan: { expression: '' } }, + 'Goodbye world!', + 'Nested paths access nested objects with empty string' + ); }); it('literal paths', function() { - shouldCompileTo('Goodbye {{[@alan]/expression}} world!', {'@alan': {expression: 'beautiful'}}, - 'Goodbye beautiful world!', 'Literal paths can be used'); - shouldCompileTo('Goodbye {{[foo bar]/expression}} world!', {'foo bar': {expression: 'beautiful'}}, - 'Goodbye beautiful world!', 'Literal paths can be used'); + shouldCompileTo( + 'Goodbye {{[@alan]/expression}} world!', + { '@alan': { expression: 'beautiful' } }, + 'Goodbye beautiful world!', + 'Literal paths can be used' + ); + shouldCompileTo( + 'Goodbye {{[foo bar]/expression}} world!', + { 'foo bar': { expression: 'beautiful' } }, + 'Goodbye beautiful world!', + 'Literal paths can be used' + ); }); it('literal references', function() { - shouldCompileTo('Goodbye {{[foo bar]}} world!', {'foo bar': 'beautiful'}, 'Goodbye beautiful world!'); - shouldCompileTo('Goodbye {{"foo bar"}} world!', {'foo bar': 'beautiful'}, 'Goodbye beautiful world!'); - shouldCompileTo("Goodbye {{'foo bar'}} world!", {'foo bar': 'beautiful'}, 'Goodbye beautiful world!'); - shouldCompileTo('Goodbye {{"foo[bar"}} world!', {'foo[bar': 'beautiful'}, 'Goodbye beautiful world!'); - shouldCompileTo('Goodbye {{"foo\'bar"}} world!', {"foo'bar": 'beautiful'}, 'Goodbye beautiful world!'); - shouldCompileTo("Goodbye {{'foo\"bar'}} world!", {'foo"bar': 'beautiful'}, 'Goodbye beautiful world!'); + shouldCompileTo( + 'Goodbye {{[foo bar]}} world!', + { 'foo bar': 'beautiful' }, + 'Goodbye beautiful world!' + ); + shouldCompileTo( + 'Goodbye {{"foo bar"}} world!', + { 'foo bar': 'beautiful' }, + 'Goodbye beautiful world!' + ); + shouldCompileTo( + "Goodbye {{'foo bar'}} world!", + { 'foo bar': 'beautiful' }, + 'Goodbye beautiful world!' + ); + shouldCompileTo( + 'Goodbye {{"foo[bar"}} world!', + { 'foo[bar': 'beautiful' }, + 'Goodbye beautiful world!' + ); + shouldCompileTo( + 'Goodbye {{"foo\'bar"}} world!', + { "foo'bar": 'beautiful' }, + 'Goodbye beautiful world!' + ); + shouldCompileTo( + "Goodbye {{'foo\"bar'}} world!", + { 'foo"bar': 'beautiful' }, + 'Goodbye beautiful world!' + ); }); it("that current context path ({{.}}) doesn't hit helpers", function() { - shouldCompileTo('test: {{.}}', [null, {helper: 'awesome'}], 'test: '); + shouldCompileTo('test: {{.}}', [null, { helper: 'awesome' }], 'test: '); }); it('complex but empty paths', function() { - shouldCompileTo('{{person/name}}', {person: {name: null}}, ''); - shouldCompileTo('{{person/name}}', {person: {}}, ''); + shouldCompileTo('{{person/name}}', { person: { name: null } }, ''); + shouldCompileTo('{{person/name}}', { person: {} }, ''); }); it('this keyword in paths', function() { var string = '{{#goodbyes}}{{this}}{{/goodbyes}}'; - var hash = {goodbyes: ['goodbye', 'Goodbye', 'GOODBYE']}; - shouldCompileTo(string, hash, 'goodbyeGoodbyeGOODBYE', - 'This keyword in paths evaluates to current context'); + var hash = { goodbyes: ['goodbye', 'Goodbye', 'GOODBYE'] }; + shouldCompileTo( + string, + hash, + 'goodbyeGoodbyeGOODBYE', + 'This keyword in paths evaluates to current context' + ); string = '{{#hellos}}{{this/text}}{{/hellos}}'; - hash = {hellos: [{text: 'hello'}, {text: 'Hello'}, {text: 'HELLO'}]}; - shouldCompileTo(string, hash, 'helloHelloHELLO', 'This keyword evaluates in more complex paths'); + hash = { + hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }] + }; + shouldCompileTo( + string, + hash, + 'helloHelloHELLO', + 'This keyword evaluates in more complex paths' + ); }); it('this keyword nested inside path', function() { - shouldThrow(function() { - CompilerContext.compile('{{#hellos}}{{text/this/foo}}{{/hellos}}'); - }, Error, 'Invalid path: text/this - 1:13'); + shouldThrow( + function() { + CompilerContext.compile('{{#hellos}}{{text/this/foo}}{{/hellos}}'); + }, + Error, + 'Invalid path: text/this - 1:13' + ); - shouldCompileTo('{{[this]}}', {'this': 'bar'}, 'bar'); - shouldCompileTo('{{text/[this]}}', {text: {'this': 'bar'}}, 'bar'); + shouldCompileTo('{{[this]}}', { this: 'bar' }, 'bar'); + shouldCompileTo('{{text/[this]}}', { text: { this: 'bar' } }, 'bar'); }); it('this keyword in helpers', function() { - var helpers = {foo: function(value) { + var helpers = { + foo: function(value) { return 'bar ' + value; - }}; + } + }; var string = '{{#goodbyes}}{{foo this}}{{/goodbyes}}'; - var hash = {goodbyes: ['goodbye', 'Goodbye', 'GOODBYE']}; - shouldCompileTo(string, [hash, helpers], 'bar goodbyebar Goodbyebar GOODBYE', - 'This keyword in paths evaluates to current context'); + var hash = { goodbyes: ['goodbye', 'Goodbye', 'GOODBYE'] }; + shouldCompileTo( + string, + [hash, helpers], + 'bar goodbyebar Goodbyebar GOODBYE', + 'This keyword in paths evaluates to current context' + ); string = '{{#hellos}}{{foo this/text}}{{/hellos}}'; - hash = {hellos: [{text: 'hello'}, {text: 'Hello'}, {text: 'HELLO'}]}; - shouldCompileTo(string, [hash, helpers], 'bar hellobar Hellobar HELLO', 'This keyword evaluates in more complex paths'); + hash = { + hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }] + }; + shouldCompileTo( + string, + [hash, helpers], + 'bar hellobar Hellobar HELLO', + 'This keyword evaluates in more complex paths' + ); }); it('this keyword nested inside helpers param', function() { var string = '{{#hellos}}{{foo text/this/foo}}{{/hellos}}'; - shouldThrow(function() { - CompilerContext.compile(string); - }, Error, 'Invalid path: text/this - 1:17'); + shouldThrow( + function() { + CompilerContext.compile(string); + }, + Error, + 'Invalid path: text/this - 1:17' + ); shouldCompileTo( - '{{foo [this]}}', - {foo: function(value) { return value; }, 'this': 'bar'}, - 'bar'); + '{{foo [this]}}', + { + foo: function(value) { + return value; + }, + this: 'bar' + }, + 'bar' + ); shouldCompileTo( - '{{foo text/[this]}}', - {foo: function(value) { return value; }, text: {'this': 'bar'}}, - 'bar'); + '{{foo text/[this]}}', + { + foo: function(value) { + return value; + }, + text: { this: 'bar' } + }, + 'bar' + ); }); it('pass string literals', function() { shouldCompileTo('{{"foo"}}', {}, ''); shouldCompileTo('{{"foo"}}', { foo: 'bar' }, 'bar'); - shouldCompileTo('{{#"foo"}}{{.}}{{/"foo"}}', { foo: ['bar', 'baz'] }, 'barbaz'); + shouldCompileTo( + '{{#"foo"}}{{.}}{{/"foo"}}', + { foo: ['bar', 'baz'] }, + 'barbaz' + ); }); it('pass number literals', function() { @@ -285,13 +569,21 @@ describe('basic context', function() { shouldCompileTo('{{12}}', { '12': 'bar' }, 'bar'); shouldCompileTo('{{12.34}}', {}, ''); shouldCompileTo('{{12.34}}', { '12.34': 'bar' }, 'bar'); - shouldCompileTo('{{12.34 1}}', { '12.34': function(arg) { return 'bar' + arg; } }, 'bar1'); + shouldCompileTo( + '{{12.34 1}}', + { + '12.34': function(arg) { + return 'bar' + arg; + } + }, + 'bar1' + ); }); it('pass boolean literals', function() { shouldCompileTo('{{true}}', {}, ''); shouldCompileTo('{{true}}', { '': 'foo' }, ''); - shouldCompileTo('{{false}}', { 'false': 'foo' }, 'foo'); + shouldCompileTo('{{false}}', { false: 'foo' }, 'foo'); }); it('should handle literals in subexpression', function() { @@ -300,6 +592,17 @@ describe('basic context', function() { return arg; } }; - shouldCompileTo('{{foo (false)}}', [{ 'false': function() { return 'bar'; } }, helpers], 'bar'); + shouldCompileTo( + '{{foo (false)}}', + [ + { + false: function() { + return 'bar'; + } + }, + helpers + ], + 'bar' + ); }); }); diff --git a/spec/blocks.js b/spec/blocks.js index 2fbaee7c..1ef2d1be 100644 --- a/spec/blocks.js +++ b/spec/blocks.js @@ -1,51 +1,99 @@ describe('blocks', function() { it('array', function() { var string = '{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!'; - var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'}; - shouldCompileTo(string, hash, 'goodbye! Goodbye! GOODBYE! cruel world!', - 'Arrays iterate over the contents when not empty'); + var hash = { + goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }], + world: 'world' + }; + shouldCompileTo( + string, + hash, + 'goodbye! Goodbye! GOODBYE! cruel world!', + 'Arrays iterate over the contents when not empty' + ); - shouldCompileTo(string, {goodbyes: [], world: 'world'}, 'cruel world!', - 'Arrays ignore the contents when empty'); + shouldCompileTo( + string, + { goodbyes: [], world: 'world' }, + 'cruel world!', + 'Arrays ignore the contents when empty' + ); }); it('array without data', function() { - var string = '{{#goodbyes}}{{text}}{{/goodbyes}} {{#goodbyes}}{{text}}{{/goodbyes}}'; - var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'}; - shouldCompileTo(string, [hash,,, false], 'goodbyeGoodbyeGOODBYE goodbyeGoodbyeGOODBYE'); + var string = + '{{#goodbyes}}{{text}}{{/goodbyes}} {{#goodbyes}}{{text}}{{/goodbyes}}'; + var hash = { + goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }], + world: 'world' + }; + shouldCompileTo( + string, + [hash, , , false], + 'goodbyeGoodbyeGOODBYE goodbyeGoodbyeGOODBYE' + ); }); it('array with @index', function() { - var string = '{{#goodbyes}}{{@index}}. {{text}}! {{/goodbyes}}cruel {{world}}!'; - var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'}; + var string = + '{{#goodbyes}}{{@index}}. {{text}}! {{/goodbyes}}cruel {{world}}!'; + var hash = { + goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }], + world: 'world' + }; var template = CompilerContext.compile(string); var result = template(hash); - equal(result, '0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!', 'The @index variable is used'); + equal( + result, + '0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!', + 'The @index variable is used' + ); }); it('empty block', function() { var string = '{{#goodbyes}}{{/goodbyes}}cruel {{world}}!'; - var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'}; - shouldCompileTo(string, hash, 'cruel world!', - 'Arrays iterate over the contents when not empty'); + var hash = { + goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }], + world: 'world' + }; + shouldCompileTo( + string, + hash, + 'cruel world!', + 'Arrays iterate over the contents when not empty' + ); - shouldCompileTo(string, {goodbyes: [], world: 'world'}, 'cruel world!', - 'Arrays ignore the contents when empty'); + shouldCompileTo( + string, + { goodbyes: [], world: 'world' }, + 'cruel world!', + 'Arrays ignore the contents when empty' + ); }); it('block with complex lookup', function() { var string = '{{#goodbyes}}{{text}} cruel {{../name}}! {{/goodbyes}}'; - var hash = {name: 'Alan', goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}]}; + var hash = { + name: 'Alan', + goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }] + }; - shouldCompileTo(string, hash, 'goodbye cruel Alan! Goodbye cruel Alan! GOODBYE cruel Alan! ', - 'Templates can access variables in contexts up the stack with relative path syntax'); + shouldCompileTo( + string, + hash, + 'goodbye cruel Alan! Goodbye cruel Alan! GOODBYE cruel Alan! ', + 'Templates can access variables in contexts up the stack with relative path syntax' + ); }); it('multiple blocks with complex lookup', function() { var string = '{{#goodbyes}}{{../name}}{{../name}}{{/goodbyes}}'; - var hash = {name: 'Alan', goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}]}; + var hash = { + name: 'Alan', + goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }] + }; shouldCompileTo(string, hash, 'AlanAlanAlanAlanAlanAlan'); }); @@ -59,111 +107,201 @@ describe('blocks', function() { }); it('block with deep nested complex lookup', function() { - var string = '{{#outer}}Goodbye {{#inner}}cruel {{../sibling}} {{../../omg}}{{/inner}}{{/outer}}'; - var hash = {omg: 'OMG!', outer: [{ sibling: 'sad', inner: [{ text: 'goodbye' }] }] }; + var string = + '{{#outer}}Goodbye {{#inner}}cruel {{../sibling}} {{../../omg}}{{/inner}}{{/outer}}'; + var hash = { + omg: 'OMG!', + outer: [{ sibling: 'sad', inner: [{ text: 'goodbye' }] }] + }; shouldCompileTo(string, hash, 'Goodbye cruel sad OMG!'); }); it('works with cached blocks', function() { - var template = CompilerContext.compile('{{#each person}}{{#with .}}{{first}} {{last}}{{/with}}{{/each}}', {data: false}); + var template = CompilerContext.compile( + '{{#each person}}{{#with .}}{{first}} {{last}}{{/with}}{{/each}}', + { data: false } + ); - var result = template({person: [{first: 'Alan', last: 'Johnson'}, {first: 'Alan', last: 'Johnson'}]}); + var result = template({ + person: [ + { first: 'Alan', last: 'Johnson' }, + { first: 'Alan', last: 'Johnson' } + ] + }); equals(result, 'Alan JohnsonAlan Johnson'); }); describe('inverted sections', function() { it('inverted sections with unset value', function() { - var string = '{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}'; + var string = + '{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}'; var hash = {}; - shouldCompileTo(string, hash, 'Right On!', "Inverted section rendered when value isn't set."); + shouldCompileTo( + string, + hash, + 'Right On!', + "Inverted section rendered when value isn't set." + ); }); it('inverted section with false value', function() { - var string = '{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}'; - var hash = {goodbyes: false}; - shouldCompileTo(string, hash, 'Right On!', 'Inverted section rendered when value is false.'); + var string = + '{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}'; + var hash = { goodbyes: false }; + shouldCompileTo( + string, + hash, + 'Right On!', + 'Inverted section rendered when value is false.' + ); }); it('inverted section with empty set', function() { - var string = '{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}'; - var hash = {goodbyes: []}; - shouldCompileTo(string, hash, 'Right On!', 'Inverted section rendered when value is empty set.'); + var string = + '{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}'; + var hash = { goodbyes: [] }; + shouldCompileTo( + string, + hash, + 'Right On!', + 'Inverted section rendered when value is empty set.' + ); }); it('block inverted sections', function() { - shouldCompileTo('{{#people}}{{name}}{{^}}{{none}}{{/people}}', {none: 'No people'}, - 'No people'); + shouldCompileTo( + '{{#people}}{{name}}{{^}}{{none}}{{/people}}', + { none: 'No people' }, + 'No people' + ); }); it('chained inverted sections', function() { - shouldCompileTo('{{#people}}{{name}}{{else if none}}{{none}}{{/people}}', {none: 'No people'}, - 'No people'); - shouldCompileTo('{{#people}}{{name}}{{else if nothere}}fail{{else unless nothere}}{{none}}{{/people}}', {none: 'No people'}, - 'No people'); - shouldCompileTo('{{#people}}{{name}}{{else if none}}{{none}}{{else}}fail{{/people}}', {none: 'No people'}, - 'No people'); + shouldCompileTo( + '{{#people}}{{name}}{{else if none}}{{none}}{{/people}}', + { none: 'No people' }, + 'No people' + ); + shouldCompileTo( + '{{#people}}{{name}}{{else if nothere}}fail{{else unless nothere}}{{none}}{{/people}}', + { none: 'No people' }, + 'No people' + ); + shouldCompileTo( + '{{#people}}{{name}}{{else if none}}{{none}}{{else}}fail{{/people}}', + { none: 'No people' }, + 'No people' + ); }); it('chained inverted sections with mismatch', function() { shouldThrow(function() { - shouldCompileTo('{{#people}}{{name}}{{else if none}}{{none}}{{/if}}', {none: 'No people'}, - 'No people'); + shouldCompileTo( + '{{#people}}{{name}}{{else if none}}{{none}}{{/if}}', + { none: 'No people' }, + 'No people' + ); }, Error); }); it('block inverted sections with empty arrays', function() { - shouldCompileTo('{{#people}}{{name}}{{^}}{{none}}{{/people}}', {none: 'No people', people: []}, - 'No people'); + shouldCompileTo( + '{{#people}}{{name}}{{^}}{{none}}{{/people}}', + { none: 'No people', people: [] }, + 'No people' + ); }); }); describe('standalone sections', function() { it('block standalone else sections', function() { - shouldCompileTo('{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n', {none: 'No people'}, - 'No people\n'); - shouldCompileTo('{{#none}}\n{{.}}\n{{^}}\n{{none}}\n{{/none}}\n', {none: 'No people'}, - 'No people\n'); - shouldCompileTo('{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n', {none: 'No people'}, - 'No people\n'); + shouldCompileTo( + '{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n', + { none: 'No people' }, + 'No people\n' + ); + shouldCompileTo( + '{{#none}}\n{{.}}\n{{^}}\n{{none}}\n{{/none}}\n', + { none: 'No people' }, + 'No people\n' + ); + shouldCompileTo( + '{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n', + { none: 'No people' }, + 'No people\n' + ); }); it('block standalone else sections can be disabled', function() { shouldCompileTo( '{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n', - [{none: 'No people'}, {}, {}, {ignoreStandalone: true}], - '\nNo people\n\n'); + [{ none: 'No people' }, {}, {}, { ignoreStandalone: true }], + '\nNo people\n\n' + ); shouldCompileTo( '{{#none}}\n{{.}}\n{{^}}\nFail\n{{/none}}\n', - [{none: 'No people'}, {}, {}, {ignoreStandalone: true}], - '\nNo people\n\n'); + [{ none: 'No people' }, {}, {}, { ignoreStandalone: true }], + '\nNo people\n\n' + ); }); it('block standalone chained else sections', function() { - shouldCompileTo('{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{/people}}\n', {none: 'No people'}, - 'No people\n'); - shouldCompileTo('{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{^}}\n{{/people}}\n', {none: 'No people'}, - 'No people\n'); + shouldCompileTo( + '{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{/people}}\n', + { none: 'No people' }, + 'No people\n' + ); + shouldCompileTo( + '{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{^}}\n{{/people}}\n', + { none: 'No people' }, + 'No people\n' + ); }); it('should handle nesting', function() { - shouldCompileTo('{{#data}}\n{{#if true}}\n{{.}}\n{{/if}}\n{{/data}}\nOK.', {data: [1, 3, 5]}, '1\n3\n5\nOK.'); + shouldCompileTo( + '{{#data}}\n{{#if true}}\n{{.}}\n{{/if}}\n{{/data}}\nOK.', + { data: [1, 3, 5] }, + '1\n3\n5\nOK.' + ); }); }); describe('compat mode', function() { it('block with deep recursive lookup lookup', function() { - var string = '{{#outer}}Goodbye {{#inner}}cruel {{omg}}{{/inner}}{{/outer}}'; - var hash = {omg: 'OMG!', outer: [{ inner: [{ text: 'goodbye' }] }] }; + var string = + '{{#outer}}Goodbye {{#inner}}cruel {{omg}}{{/inner}}{{/outer}}'; + var hash = { omg: 'OMG!', outer: [{ inner: [{ text: 'goodbye' }] }] }; - shouldCompileTo(string, [hash, undefined, undefined, true], 'Goodbye cruel OMG!'); + shouldCompileTo( + string, + [hash, undefined, undefined, true], + 'Goodbye cruel OMG!' + ); }); it('block with deep recursive pathed lookup', function() { - var string = '{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}'; - var hash = {omg: {yes: 'OMG!'}, outer: [{ inner: [{ yes: 'no', text: 'goodbye' }] }] }; + var string = + '{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}'; + var hash = { + omg: { yes: 'OMG!' }, + outer: [{ inner: [{ yes: 'no', text: 'goodbye' }] }] + }; - shouldCompileTo(string, [hash, undefined, undefined, true], 'Goodbye cruel OMG!'); + shouldCompileTo( + string, + [hash, undefined, undefined, true], + 'Goodbye cruel OMG!' + ); }); it('block with missed recursive lookup', function() { - var string = '{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}'; - var hash = {omg: {no: 'OMG!'}, outer: [{ inner: [{ yes: 'no', text: 'goodbye' }] }] }; + var string = + '{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}'; + var hash = { + omg: { no: 'OMG!' }, + outer: [{ inner: [{ yes: 'no', text: 'goodbye' }] }] + }; - shouldCompileTo(string, [hash, undefined, undefined, true], 'Goodbye cruel '); + shouldCompileTo( + string, + [hash, undefined, undefined, true], + 'Goodbye cruel ' + ); }); }); @@ -181,9 +319,10 @@ describe('blocks', function() { } }; shouldCompileTo( - '{{#helper}}{{*decorator}}{{/helper}}', - {hash: {}, helpers: helpers, decorators: decorators}, - 'success'); + '{{#helper}}{{*decorator}}{{/helper}}', + { hash: {}, helpers: helpers, decorators: decorators }, + 'success' + ); }); it('should apply allow undefined return', function() { var helpers = { @@ -197,9 +336,10 @@ describe('blocks', function() { } }; shouldCompileTo( - '{{#helper}}{{*decorator}}suc{{/helper}}', - {hash: {}, helpers: helpers, decorators: decorators}, - 'success'); + '{{#helper}}{{*decorator}}suc{{/helper}}', + { hash: {}, helpers: helpers, decorators: decorators }, + 'success' + ); }); it('should apply block decorators', function() { @@ -215,9 +355,10 @@ describe('blocks', function() { } }; shouldCompileTo( - '{{#helper}}{{#*decorator}}success{{/decorator}}{{/helper}}', - {hash: {}, helpers: helpers, decorators: decorators}, - 'success'); + '{{#helper}}{{#*decorator}}success{{/decorator}}{{/helper}}', + { hash: {}, helpers: helpers, decorators: decorators }, + 'success' + ); }); it('should support nested decorators', function() { var helpers = { @@ -235,9 +376,10 @@ describe('blocks', function() { } }; shouldCompileTo( - '{{#helper}}{{#*decorator}}{{#*nested}}suc{{/nested}}cess{{/decorator}}{{/helper}}', - {hash: {}, helpers: helpers, decorators: decorators}, - 'success'); + '{{#helper}}{{#*decorator}}{{#*nested}}suc{{/nested}}cess{{/decorator}}{{/helper}}', + { hash: {}, helpers: helpers, decorators: decorators }, + 'success' + ); }); it('should apply multiple decorators', function() { @@ -253,9 +395,10 @@ describe('blocks', function() { } }; shouldCompileTo( - '{{#helper}}{{#*decorator}}suc{{/decorator}}{{#*decorator}}cess{{/decorator}}{{/helper}}', - {hash: {}, helpers: helpers, decorators: decorators}, - 'success'); + '{{#helper}}{{#*decorator}}suc{{/decorator}}{{#*decorator}}cess{{/decorator}}{{/helper}}', + { hash: {}, helpers: helpers, decorators: decorators }, + 'success' + ); }); it('should access parent variables', function() { @@ -271,9 +414,10 @@ describe('blocks', function() { } }; shouldCompileTo( - '{{#helper}}{{*decorator foo}}{{/helper}}', - {hash: {'foo': 'success'}, helpers: helpers, decorators: decorators}, - 'success'); + '{{#helper}}{{*decorator foo}}{{/helper}}', + { hash: { foo: 'success' }, helpers: helpers, decorators: decorators }, + 'success' + ); }); it('should work with root program', function() { var run; @@ -285,9 +429,10 @@ describe('blocks', function() { } }; shouldCompileTo( - '{{*decorator "success"}}', - {hash: {'foo': 'success'}, decorators: decorators}, - ''); + '{{*decorator "success"}}', + { hash: { foo: 'success' }, decorators: decorators }, + '' + ); equals(run, true); }); it('should fail when accessing variables from root', function() { @@ -300,9 +445,10 @@ describe('blocks', function() { } }; shouldCompileTo( - '{{*decorator foo}}', - {hash: {'foo': 'fail'}, decorators: decorators}, - ''); + '{{*decorator foo}}', + { hash: { foo: 'fail' }, decorators: decorators }, + '' + ); equals(run, true); }); @@ -335,12 +481,23 @@ describe('blocks', function() { equals(handlebarsEnv.decorators.bar, undefined); }); it('fails with multiple and args', function() { - shouldThrow(function() { - handlebarsEnv.registerDecorator({ - world: function() { return 'world!'; }, - testHelper: function() { return 'found it!'; } - }, {}); - }, Error, 'Arg not supported with multiple decorators'); + shouldThrow( + function() { + handlebarsEnv.registerDecorator( + { + world: function() { + return 'world!'; + }, + testHelper: function() { + return 'found it!'; + } + }, + {} + ); + }, + Error, + 'Arg not supported with multiple decorators' + ); }); }); }); diff --git a/spec/builtins.js b/spec/builtins.js index b29927f0..d6bc2986 100644 --- a/spec/builtins.js +++ b/spec/builtins.js @@ -2,64 +2,156 @@ describe('builtin helpers', function() { describe('#if', function() { it('if', function() { var string = '{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!'; - shouldCompileTo(string, {goodbye: true, world: 'world'}, 'GOODBYE cruel world!', - 'if with boolean argument shows the contents when true'); - shouldCompileTo(string, {goodbye: 'dummy', world: 'world'}, 'GOODBYE cruel world!', - 'if with string argument shows the contents'); - shouldCompileTo(string, {goodbye: false, world: 'world'}, 'cruel world!', - 'if with boolean argument does not show the contents when false'); - shouldCompileTo(string, {world: 'world'}, 'cruel world!', - 'if with undefined does not show the contents'); - shouldCompileTo(string, {goodbye: ['foo'], world: 'world'}, 'GOODBYE cruel world!', - 'if with non-empty array shows the contents'); - shouldCompileTo(string, {goodbye: [], world: 'world'}, 'cruel world!', - 'if with empty array does not show the contents'); - shouldCompileTo(string, {goodbye: 0, world: 'world'}, 'cruel world!', - 'if with zero does not show the contents'); - shouldCompileTo('{{#if goodbye includeZero=true}}GOODBYE {{/if}}cruel {{world}}!', - {goodbye: 0, world: 'world'}, 'GOODBYE cruel world!', - 'if with zero does not show the contents'); + shouldCompileTo( + string, + { goodbye: true, world: 'world' }, + 'GOODBYE cruel world!', + 'if with boolean argument shows the contents when true' + ); + shouldCompileTo( + string, + { goodbye: 'dummy', world: 'world' }, + 'GOODBYE cruel world!', + 'if with string argument shows the contents' + ); + shouldCompileTo( + string, + { goodbye: false, world: 'world' }, + 'cruel world!', + 'if with boolean argument does not show the contents when false' + ); + shouldCompileTo( + string, + { world: 'world' }, + 'cruel world!', + 'if with undefined does not show the contents' + ); + shouldCompileTo( + string, + { goodbye: ['foo'], world: 'world' }, + 'GOODBYE cruel world!', + 'if with non-empty array shows the contents' + ); + shouldCompileTo( + string, + { goodbye: [], world: 'world' }, + 'cruel world!', + 'if with empty array does not show the contents' + ); + shouldCompileTo( + string, + { goodbye: 0, world: 'world' }, + 'cruel world!', + 'if with zero does not show the contents' + ); + shouldCompileTo( + '{{#if goodbye includeZero=true}}GOODBYE {{/if}}cruel {{world}}!', + { goodbye: 0, world: 'world' }, + 'GOODBYE cruel world!', + 'if with zero does not show the contents' + ); }); it('if with function argument', function() { var string = '{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!'; - shouldCompileTo(string, {goodbye: function() {return true; }, world: 'world'}, 'GOODBYE cruel world!', - 'if with function shows the contents when function returns true'); - shouldCompileTo(string, {goodbye: function() {return this.world; }, world: 'world'}, 'GOODBYE cruel world!', - 'if with function shows the contents when function returns string'); - shouldCompileTo(string, {goodbye: function() {return false; }, world: 'world'}, 'cruel world!', - 'if with function does not show the contents when returns false'); - shouldCompileTo(string, {goodbye: function() {return this.foo; }, world: 'world'}, 'cruel world!', - 'if with function does not show the contents when returns undefined'); + shouldCompileTo( + string, + { + goodbye: function() { + return true; + }, + world: 'world' + }, + 'GOODBYE cruel world!', + 'if with function shows the contents when function returns true' + ); + shouldCompileTo( + string, + { + goodbye: function() { + return this.world; + }, + world: 'world' + }, + 'GOODBYE cruel world!', + 'if with function shows the contents when function returns string' + ); + shouldCompileTo( + string, + { + goodbye: function() { + return false; + }, + world: 'world' + }, + 'cruel world!', + 'if with function does not show the contents when returns false' + ); + shouldCompileTo( + string, + { + goodbye: function() { + return this.foo; + }, + world: 'world' + }, + 'cruel world!', + 'if with function does not show the contents when returns undefined' + ); }); it('should not change the depth list', function() { - var string = '{{#with foo}}{{#if goodbye}}GOODBYE cruel {{../world}}!{{/if}}{{/with}}'; - shouldCompileTo(string, {foo: {goodbye: true}, world: 'world'}, 'GOODBYE cruel world!'); + var string = + '{{#with foo}}{{#if goodbye}}GOODBYE cruel {{../world}}!{{/if}}{{/with}}'; + shouldCompileTo( + string, + { foo: { goodbye: true }, world: 'world' }, + 'GOODBYE cruel world!' + ); }); }); describe('#with', function() { it('with', function() { var string = '{{#with person}}{{first}} {{last}}{{/with}}'; - shouldCompileTo(string, {person: {first: 'Alan', last: 'Johnson'}}, 'Alan Johnson'); + shouldCompileTo( + string, + { person: { first: 'Alan', last: 'Johnson' } }, + 'Alan Johnson' + ); }); it('with with function argument', function() { var string = '{{#with person}}{{first}} {{last}}{{/with}}'; - shouldCompileTo(string, {person: function() { return {first: 'Alan', last: 'Johnson'}; }}, 'Alan Johnson'); + shouldCompileTo( + string, + { + person: function() { + return { first: 'Alan', last: 'Johnson' }; + } + }, + 'Alan Johnson' + ); }); it('with with else', function() { - var string = '{{#with person}}Person is present{{else}}Person is not present{{/with}}'; + var string = + '{{#with person}}Person is present{{else}}Person is not present{{/with}}'; shouldCompileTo(string, {}, 'Person is not present'); }); it('with provides block parameter', function() { var string = '{{#with person as |foo|}}{{foo.first}} {{last}}{{/with}}'; - shouldCompileTo(string, {person: {first: 'Alan', last: 'Johnson'}}, 'Alan Johnson'); + shouldCompileTo( + string, + { person: { first: 'Alan', last: 'Johnson' } }, + 'Alan Johnson' + ); }); it('works when data is disabled', function() { - var template = CompilerContext.compile('{{#with person as |foo|}}{{foo.first}} {{last}}{{/with}}', {data: false}); + var template = CompilerContext.compile( + '{{#with person as |foo|}}{{foo.first}} {{last}}{{/with}}', + { data: false } + ); - var result = template({person: {first: 'Alan', last: 'Johnson'}}); + var result = template({ person: { first: 'Alan', last: 'Johnson' } }); equals(result, 'Alan Johnson'); }); }); @@ -73,92 +165,179 @@ describe('builtin helpers', function() { it('each', function() { var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!'; - var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'}; - shouldCompileTo(string, hash, 'goodbye! Goodbye! GOODBYE! cruel world!', - 'each with array argument iterates over the contents when not empty'); - shouldCompileTo(string, {goodbyes: [], world: 'world'}, 'cruel world!', - 'each with array argument ignores the contents when empty'); + var hash = { + goodbyes: [ + { text: 'goodbye' }, + { text: 'Goodbye' }, + { text: 'GOODBYE' } + ], + world: 'world' + }; + shouldCompileTo( + string, + hash, + 'goodbye! Goodbye! GOODBYE! cruel world!', + 'each with array argument iterates over the contents when not empty' + ); + shouldCompileTo( + string, + { goodbyes: [], world: 'world' }, + 'cruel world!', + 'each with array argument ignores the contents when empty' + ); }); it('each without data', function() { var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!'; - var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'}; - shouldCompileTo(string, [hash,,,, false], 'goodbye! Goodbye! GOODBYE! cruel world!'); + var hash = { + goodbyes: [ + { text: 'goodbye' }, + { text: 'Goodbye' }, + { text: 'GOODBYE' } + ], + world: 'world' + }; + shouldCompileTo( + string, + [hash, , , , false], + 'goodbye! Goodbye! GOODBYE! cruel world!' + ); - hash = {goodbyes: 'cruel', world: 'world'}; - shouldCompileTo('{{#each .}}{{.}}{{/each}}', [hash,,,, false], 'cruelworld'); + hash = { goodbyes: 'cruel', world: 'world' }; + shouldCompileTo( + '{{#each .}}{{.}}{{/each}}', + [hash, , , , false], + 'cruelworld' + ); }); it('each without context', function() { var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!'; - shouldCompileTo(string, [,,,, ], 'cruel !'); + shouldCompileTo(string, [, , , ,], 'cruel !'); }); it('each with an object and @key', function() { - var string = '{{#each goodbyes}}{{@key}}. {{text}}! {{/each}}cruel {{world}}!'; + var string = + '{{#each goodbyes}}{{@key}}. {{text}}! {{/each}}cruel {{world}}!'; function Clazz() { - this['#1'] = {text: 'goodbye'}; - this[2] = {text: 'GOODBYE'}; + this['#1'] = { text: 'goodbye' }; + this[2] = { text: 'GOODBYE' }; } Clazz.prototype.foo = 'fail'; - var hash = {goodbyes: new Clazz(), world: 'world'}; + var hash = { goodbyes: new Clazz(), world: 'world' }; // Object property iteration order is undefined according to ECMA spec, // so we need to check both possible orders // @see http://stackoverflow.com/questions/280713/elements-order-in-a-for-in-loop var actual = compileWithPartials(string, hash); - var expected1 = '<b>#1</b>. goodbye! 2. GOODBYE! cruel world!'; - var expected2 = '2. GOODBYE! <b>#1</b>. goodbye! cruel world!'; + var expected1 = + '<b>#1</b>. goodbye! 2. GOODBYE! cruel world!'; + var expected2 = + '2. GOODBYE! <b>#1</b>. goodbye! cruel world!'; - equals(actual === expected1 || actual === expected2, true, 'each with object argument iterates over the contents when not empty'); - shouldCompileTo(string, {goodbyes: {}, world: 'world'}, 'cruel world!'); + equals( + actual === expected1 || actual === expected2, + true, + 'each with object argument iterates over the contents when not empty' + ); + shouldCompileTo(string, { goodbyes: {}, world: 'world' }, 'cruel world!'); }); it('each with @index', function() { - var string = '{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!'; - var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'}; + var string = + '{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!'; + var hash = { + goodbyes: [ + { text: 'goodbye' }, + { text: 'Goodbye' }, + { text: 'GOODBYE' } + ], + world: 'world' + }; var template = CompilerContext.compile(string); var result = template(hash); - equal(result, '0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!', 'The @index variable is used'); + equal( + result, + '0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!', + 'The @index variable is used' + ); }); it('each with nested @index', function() { - var string = '{{#each goodbyes}}{{@index}}. {{text}}! {{#each ../goodbyes}}{{@index}} {{/each}}After {{@index}} {{/each}}{{@index}}cruel {{world}}!'; - var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'}; + var string = + '{{#each goodbyes}}{{@index}}. {{text}}! {{#each ../goodbyes}}{{@index}} {{/each}}After {{@index}} {{/each}}{{@index}}cruel {{world}}!'; + var hash = { + goodbyes: [ + { text: 'goodbye' }, + { text: 'Goodbye' }, + { text: 'GOODBYE' } + ], + world: 'world' + }; var template = CompilerContext.compile(string); var result = template(hash); - equal(result, '0. goodbye! 0 1 2 After 0 1. Goodbye! 0 1 2 After 1 2. GOODBYE! 0 1 2 After 2 cruel world!', 'The @index variable is used'); + equal( + result, + '0. goodbye! 0 1 2 After 0 1. Goodbye! 0 1 2 After 1 2. GOODBYE! 0 1 2 After 2 cruel world!', + 'The @index variable is used' + ); }); it('each with block params', function() { - var string = '{{#each goodbyes as |value index|}}{{index}}. {{value.text}}! {{#each ../goodbyes as |childValue childIndex|}} {{index}} {{childIndex}}{{/each}} After {{index}} {{/each}}{{index}}cruel {{world}}!'; - var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}], world: 'world'}; + var string = + '{{#each goodbyes as |value index|}}{{index}}. {{value.text}}! {{#each ../goodbyes as |childValue childIndex|}} {{index}} {{childIndex}}{{/each}} After {{index}} {{/each}}{{index}}cruel {{world}}!'; + var hash = { + goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }], + world: 'world' + }; var template = CompilerContext.compile(string); var result = template(hash); - equal(result, '0. goodbye! 0 0 0 1 After 0 1. Goodbye! 1 0 1 1 After 1 cruel world!'); + equal( + result, + '0. goodbye! 0 0 0 1 After 0 1. Goodbye! 1 0 1 1 After 1 cruel world!' + ); }); it('each object with @index', function() { - var string = '{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!'; - var hash = {goodbyes: {'a': {text: 'goodbye'}, b: {text: 'Goodbye'}, c: {text: 'GOODBYE'}}, world: 'world'}; + var string = + '{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!'; + var hash = { + goodbyes: { + a: { text: 'goodbye' }, + b: { text: 'Goodbye' }, + c: { text: 'GOODBYE' } + }, + world: 'world' + }; var template = CompilerContext.compile(string); var result = template(hash); - equal(result, '0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!', 'The @index variable is used'); + equal( + result, + '0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!', + 'The @index variable is used' + ); }); - it('each with @first', function() { - var string = '{{#each goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/each}}cruel {{world}}!'; - var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'}; + var string = + '{{#each goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/each}}cruel {{world}}!'; + var hash = { + goodbyes: [ + { text: 'goodbye' }, + { text: 'Goodbye' }, + { text: 'GOODBYE' } + ], + world: 'world' + }; var template = CompilerContext.compile(string); var result = template(hash); @@ -167,18 +346,34 @@ describe('builtin helpers', function() { }); it('each with nested @first', function() { - var string = '{{#each goodbyes}}({{#if @first}}{{text}}! {{/if}}{{#each ../goodbyes}}{{#if @first}}{{text}}!{{/if}}{{/each}}{{#if @first}} {{text}}!{{/if}}) {{/each}}cruel {{world}}!'; - var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'}; + var string = + '{{#each goodbyes}}({{#if @first}}{{text}}! {{/if}}{{#each ../goodbyes}}{{#if @first}}{{text}}!{{/if}}{{/each}}{{#if @first}} {{text}}!{{/if}}) {{/each}}cruel {{world}}!'; + var hash = { + goodbyes: [ + { text: 'goodbye' }, + { text: 'Goodbye' }, + { text: 'GOODBYE' } + ], + world: 'world' + }; var template = CompilerContext.compile(string); var result = template(hash); - equal(result, '(goodbye! goodbye! goodbye!) (goodbye!) (goodbye!) cruel world!', 'The @first variable is used'); + equal( + result, + '(goodbye! goodbye! goodbye!) (goodbye!) (goodbye!) cruel world!', + 'The @first variable is used' + ); }); it('each object with @first', function() { - var string = '{{#each goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/each}}cruel {{world}}!'; - var hash = {goodbyes: {'foo': {text: 'goodbye'}, bar: {text: 'Goodbye'}}, world: 'world'}; + var string = + '{{#each goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/each}}cruel {{world}}!'; + var hash = { + goodbyes: { foo: { text: 'goodbye' }, bar: { text: 'Goodbye' } }, + world: 'world' + }; var template = CompilerContext.compile(string); var result = template(hash); @@ -187,8 +382,16 @@ describe('builtin helpers', function() { }); it('each with @last', function() { - var string = '{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!'; - var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'}; + var string = + '{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!'; + var hash = { + goodbyes: [ + { text: 'goodbye' }, + { text: 'Goodbye' }, + { text: 'GOODBYE' } + ], + world: 'world' + }; var template = CompilerContext.compile(string); var result = template(hash); @@ -197,8 +400,12 @@ describe('builtin helpers', function() { }); it('each object with @last', function() { - var string = '{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!'; - var hash = {goodbyes: {'foo': {text: 'goodbye'}, bar: {text: 'Goodbye'}}, world: 'world'}; + var string = + '{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!'; + var hash = { + goodbyes: { foo: { text: 'goodbye' }, bar: { text: 'Goodbye' } }, + world: 'world' + }; var template = CompilerContext.compile(string); var result = template(hash); @@ -207,37 +414,78 @@ describe('builtin helpers', function() { }); it('each with nested @last', function() { - var string = '{{#each goodbyes}}({{#if @last}}{{text}}! {{/if}}{{#each ../goodbyes}}{{#if @last}}{{text}}!{{/if}}{{/each}}{{#if @last}} {{text}}!{{/if}}) {{/each}}cruel {{world}}!'; - var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'}; + var string = + '{{#each goodbyes}}({{#if @last}}{{text}}! {{/if}}{{#each ../goodbyes}}{{#if @last}}{{text}}!{{/if}}{{/each}}{{#if @last}} {{text}}!{{/if}}) {{/each}}cruel {{world}}!'; + var hash = { + goodbyes: [ + { text: 'goodbye' }, + { text: 'Goodbye' }, + { text: 'GOODBYE' } + ], + world: 'world' + }; var template = CompilerContext.compile(string); var result = template(hash); - equal(result, '(GOODBYE!) (GOODBYE!) (GOODBYE! GOODBYE! GOODBYE!) cruel world!', 'The @last variable is used'); + equal( + result, + '(GOODBYE!) (GOODBYE!) (GOODBYE! GOODBYE! GOODBYE!) cruel world!', + 'The @last variable is used' + ); }); it('each with function argument', function() { var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!'; - var hash = {goodbyes: function() { return [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}]; }, world: 'world'}; - shouldCompileTo(string, hash, 'goodbye! Goodbye! GOODBYE! cruel world!', - 'each with array function argument iterates over the contents when not empty'); - shouldCompileTo(string, {goodbyes: [], world: 'world'}, 'cruel world!', - 'each with array function argument ignores the contents when empty'); + var hash = { + goodbyes: function() { + return [ + { text: 'goodbye' }, + { text: 'Goodbye' }, + { text: 'GOODBYE' } + ]; + }, + world: 'world' + }; + shouldCompileTo( + string, + hash, + 'goodbye! Goodbye! GOODBYE! cruel world!', + 'each with array function argument iterates over the contents when not empty' + ); + shouldCompileTo( + string, + { goodbyes: [], world: 'world' }, + 'cruel world!', + 'each with array function argument ignores the contents when empty' + ); }); it('each object when last key is an empty string', function() { - var string = '{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!'; - var hash = {goodbyes: {'a': {text: 'goodbye'}, b: {text: 'Goodbye'}, '': {text: 'GOODBYE'}}, world: 'world'}; + var string = + '{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!'; + var hash = { + goodbyes: { + a: { text: 'goodbye' }, + b: { text: 'Goodbye' }, + '': { text: 'GOODBYE' } + }, + world: 'world' + }; var template = CompilerContext.compile(string); var result = template(hash); - equal(result, '0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!', 'Empty string key is not skipped'); + equal( + result, + '0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!', + 'Empty string key is not skipped' + ); }); it('data passed to helpers', function() { var string = '{{#each letters}}{{this}}{{detectDataInsideEach}}{{/each}}'; - var hash = {letters: ['a', 'b', 'c']}; + var hash = { letters: ['a', 'b', 'c'] }; var template = CompilerContext.compile(string); var result = template(hash, { @@ -249,10 +497,16 @@ describe('builtin helpers', function() { }); it('each on implicit context', function() { - shouldThrow(function() { - var template = CompilerContext.compile('{{#each}}{{text}}! {{/each}}cruel world!'); - template({}); - }, handlebarsEnv.Exception, 'Must pass iterator to #each'); + shouldThrow( + function() { + var template = CompilerContext.compile( + '{{#each}}{{text}}! {{/each}}cruel world!' + ); + template({}); + }, + handlebarsEnv.Exception, + 'Must pass iterator to #each' + ); }); if (global.Symbol && global.Symbol.iterator) { @@ -276,13 +530,25 @@ describe('builtin helpers', function() { return new Iterator(this.arr); }; var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!'; - var goodbyes = new Iterable([{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}]); + var goodbyes = new Iterable([ + { text: 'goodbye' }, + { text: 'Goodbye' }, + { text: 'GOODBYE' } + ]); var goodbyesEmpty = new Iterable([]); - var hash = {goodbyes: goodbyes, world: 'world'}; - shouldCompileTo(string, hash, 'goodbye! Goodbye! GOODBYE! cruel world!', - 'each with array argument iterates over the contents when not empty'); - shouldCompileTo(string, {goodbyes: goodbyesEmpty, world: 'world'}, 'cruel world!', - 'each with array argument ignores the contents when empty'); + var hash = { goodbyes: goodbyes, world: 'world' }; + shouldCompileTo( + string, + hash, + 'goodbye! Goodbye! GOODBYE! cruel world!', + 'each with array argument iterates over the contents when not empty' + ); + shouldCompileTo( + string, + { goodbyes: goodbyesEmpty, world: 'world' }, + 'cruel world!', + 'each with array argument ignores the contents when empty' + ); }); } }); @@ -293,9 +559,7 @@ describe('builtin helpers', function() { return; } - var $log, - $info, - $error; + var $log, $info, $error; beforeEach(function() { $log = console.log; $info = console.info; @@ -331,7 +595,7 @@ describe('builtin helpers', function() { logArg = arg; }; - shouldCompileTo(string, [hash,,,, {level: '03'}], ''); + shouldCompileTo(string, [hash, , , , { level: '03' }], ''); equals('03', levelArg); equals('whee', logArg); }); @@ -367,13 +631,13 @@ describe('builtin helpers', function() { console.error = $error; }; - shouldCompileTo(string, [hash,,,, {level: '03'}], ''); + shouldCompileTo(string, [hash, , , , { level: '03' }], ''); equals(true, called); }); it('should handle missing logger', function() { var string = '{{log blah}}'; var hash = { blah: 'whee' }, - called = false; + called = false; console.error = undefined; console.log = function(log) { @@ -382,7 +646,7 @@ describe('builtin helpers', function() { console.log = $log; }; - shouldCompileTo(string, [hash,,,, {level: '03'}], ''); + shouldCompileTo(string, [hash, , , , { level: '03' }], ''); equals(true, called); }); @@ -396,12 +660,12 @@ describe('builtin helpers', function() { called = true; }; - shouldCompileTo(string, [hash,,,, {level: 'error'}], ''); + shouldCompileTo(string, [hash, , , , { level: 'error' }], ''); equals(true, called); called = false; - shouldCompileTo(string, [hash,,,, {level: 'ERROR'}], ''); + shouldCompileTo(string, [hash, , , , { level: 'ERROR' }], ''); equals(true, called); }); it('should handle hash log levels', function() { @@ -449,11 +713,10 @@ describe('builtin helpers', function() { /* eslint-enable no-console */ }); - describe('#lookup', function() { it('should lookup arbitrary content', function() { var string = '{{#each goodbyes}}{{lookup ../data .}}{{/each}}', - hash = {goodbyes: [0, 1], data: ['foo', 'bar']}; + hash = { goodbyes: [0, 1], data: ['foo', 'bar'] }; var template = CompilerContext.compile(string); var result = template(hash); @@ -462,7 +725,7 @@ describe('builtin helpers', function() { }); it('should not fail on undefined value', function() { var string = '{{#each goodbyes}}{{lookup ../bar .}}{{/each}}', - hash = {goodbyes: [0, 1], data: ['foo', 'bar']}; + hash = { goodbyes: [0, 1], data: ['foo', 'bar'] }; var template = CompilerContext.compile(string); var result = template(hash); diff --git a/spec/compiler.js b/spec/compiler.js index 7734bccf..8b30ed4a 100644 --- a/spec/compiler.js +++ b/spec/compiler.js @@ -13,37 +13,84 @@ describe('compiler', function() { equal(compile('foo').equals(compile('foo')), true); equal(compile('{{foo}}').equals(compile('{{foo}}')), true); equal(compile('{{foo.bar}}').equals(compile('{{foo.bar}}')), true); - equal(compile('{{foo.bar baz "foo" true false bat=1}}').equals(compile('{{foo.bar baz "foo" true false bat=1}}')), true); - equal(compile('{{foo.bar (baz bat=1)}}').equals(compile('{{foo.bar (baz bat=1)}}')), true); - equal(compile('{{#foo}} {{/foo}}').equals(compile('{{#foo}} {{/foo}}')), true); + equal( + compile('{{foo.bar baz "foo" true false bat=1}}').equals( + compile('{{foo.bar baz "foo" true false bat=1}}') + ), + true + ); + equal( + compile('{{foo.bar (baz bat=1)}}').equals( + compile('{{foo.bar (baz bat=1)}}') + ), + true + ); + equal( + compile('{{#foo}} {{/foo}}').equals(compile('{{#foo}} {{/foo}}')), + true + ); }); it('should treat as not equal', function() { equal(compile('foo').equals(compile('bar')), false); equal(compile('{{foo}}').equals(compile('{{bar}}')), false); equal(compile('{{foo.bar}}').equals(compile('{{bar.bar}}')), false); - equal(compile('{{foo.bar baz bat=1}}').equals(compile('{{foo.bar bar bat=1}}')), false); - equal(compile('{{foo.bar (baz bat=1)}}').equals(compile('{{foo.bar (bar bat=1)}}')), false); - equal(compile('{{#foo}} {{/foo}}').equals(compile('{{#bar}} {{/bar}}')), false); - equal(compile('{{#foo}} {{/foo}}').equals(compile('{{#foo}} {{foo}}{{/foo}}')), false); + equal( + compile('{{foo.bar baz bat=1}}').equals( + compile('{{foo.bar bar bat=1}}') + ), + false + ); + equal( + compile('{{foo.bar (baz bat=1)}}').equals( + compile('{{foo.bar (bar bat=1)}}') + ), + false + ); + equal( + compile('{{#foo}} {{/foo}}').equals(compile('{{#bar}} {{/bar}}')), + false + ); + equal( + compile('{{#foo}} {{/foo}}').equals( + compile('{{#foo}} {{foo}}{{/foo}}') + ), + false + ); }); }); describe('#compile', function() { it('should fail with invalid input', function() { - shouldThrow(function() { - Handlebars.compile(null); - }, Error, 'You must pass a string or Handlebars AST to Handlebars.compile. You passed null'); - shouldThrow(function() { - Handlebars.compile({}); - }, Error, 'You must pass a string or Handlebars AST to Handlebars.compile. You passed [object Object]'); + shouldThrow( + function() { + Handlebars.compile(null); + }, + Error, + 'You must pass a string or Handlebars AST to Handlebars.compile. You passed null' + ); + shouldThrow( + function() { + Handlebars.compile({}); + }, + Error, + 'You must pass a string or Handlebars AST to Handlebars.compile. You passed [object Object]' + ); }); it('should include the location in the error (row and column)', function() { try { Handlebars.compile(' \n {{#if}}\n{{/def}}')(); - equal(true, false, 'Statement must throw exception. This line should not be executed.'); + equal( + true, + false, + 'Statement must throw exception. This line should not be executed.' + ); } catch (err) { - equal(err.message, 'if doesn\'t match def - 2:5', 'Checking error message'); + equal( + err.message, + "if doesn't match def - 2:5", + 'Checking error message' + ); if (Object.getOwnPropertyDescriptor(err, 'column').writable) { // In Safari 8, the column-property is read-only. This means that even if it is set with defineProperty, // its value won't change (https://github.com/jquery/esprima/issues/1290#issuecomment-132455482) @@ -57,17 +104,28 @@ describe('compiler', function() { it('should include the location as enumerable property', function() { try { Handlebars.compile(' \n {{#if}}\n{{/def}}')(); - equal(true, false, 'Statement must throw exception. This line should not be executed.'); + equal( + true, + false, + 'Statement must throw exception. This line should not be executed.' + ); } catch (err) { - equal(Object.prototype.propertyIsEnumerable.call(err, 'column'), true, 'Checking error column'); + equal( + Object.prototype.propertyIsEnumerable.call(err, 'column'), + true, + 'Checking error column' + ); } }); it('can utilize AST instance', function() { - equal(Handlebars.compile({ - type: 'Program', - body: [ {type: 'ContentStatement', value: 'Hello'}] - })(), 'Hello'); + equal( + Handlebars.compile({ + type: 'Program', + body: [{ type: 'ContentStatement', value: 'Hello' }] + })(), + 'Hello' + ); }); it('can pass through an empty string', function() { @@ -75,42 +133,69 @@ describe('compiler', function() { }); it('throws on desupported options', function() { - shouldThrow(function() { - Handlebars.compile('Dudes', {trackIds: true}); - }, Error, 'TrackIds and stringParams are no longer supported. See Github #1145'); - shouldThrow(function() { - Handlebars.compile('Dudes', {stringParams: true}); - }, Error, 'TrackIds and stringParams are no longer supported. See Github #1145'); + shouldThrow( + function() { + Handlebars.compile('Dudes', { trackIds: true }); + }, + Error, + 'TrackIds and stringParams are no longer supported. See Github #1145' + ); + shouldThrow( + function() { + Handlebars.compile('Dudes', { stringParams: true }); + }, + Error, + 'TrackIds and stringParams are no longer supported. See Github #1145' + ); }); it('should not modify the options.data property(GH-1327)', function() { - var options = {data: [{a: 'foo'}, {a: 'bar'}]}; + var options = { data: [{ a: 'foo' }, { a: 'bar' }] }; Handlebars.compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)(); - equal(JSON.stringify(options, 0, 2), JSON.stringify({data: [{a: 'foo'}, {a: 'bar'}]}, 0, 2)); + equal( + JSON.stringify(options, 0, 2), + JSON.stringify({ data: [{ a: 'foo' }, { a: 'bar' }] }, 0, 2) + ); }); it('should not modify the options.knownHelpers property(GH-1327)', function() { - var options = {knownHelpers: {}}; + var options = { knownHelpers: {} }; Handlebars.compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)(); - equal(JSON.stringify(options, 0, 2), JSON.stringify({knownHelpers: {}}, 0, 2)); + equal( + JSON.stringify(options, 0, 2), + JSON.stringify({ knownHelpers: {} }, 0, 2) + ); }); }); describe('#precompile', function() { it('should fail with invalid input', function() { - shouldThrow(function() { - Handlebars.precompile(null); - }, Error, 'You must pass a string or Handlebars AST to Handlebars.compile. You passed null'); - shouldThrow(function() { - Handlebars.precompile({}); - }, Error, 'You must pass a string or Handlebars AST to Handlebars.compile. You passed [object Object]'); + shouldThrow( + function() { + Handlebars.precompile(null); + }, + Error, + 'You must pass a string or Handlebars AST to Handlebars.compile. You passed null' + ); + shouldThrow( + function() { + Handlebars.precompile({}); + }, + Error, + 'You must pass a string or Handlebars AST to Handlebars.compile. You passed [object Object]' + ); }); it('can utilize AST instance', function() { - equal(/return "Hello"/.test(Handlebars.precompile({ - type: 'Program', - body: [ {type: 'ContentStatement', value: 'Hello'}] - })), true); + equal( + /return "Hello"/.test( + Handlebars.precompile({ + type: 'Program', + body: [{ type: 'ContentStatement', value: 'Hello' }] + }) + ), + true + ); }); it('can pass through an empty string', function() { diff --git a/spec/data.js b/spec/data.js index 547a2664..0defdc14 100644 --- a/spec/data.js +++ b/spec/data.js @@ -1,6 +1,6 @@ describe('data', function() { it('passing in data to a compiled function that expects data - works with helpers', function() { - var template = CompilerContext.compile('{{hello}}', {data: true}); + var template = CompilerContext.compile('{{hello}}', { data: true }); var helpers = { hello: function(options) { @@ -8,7 +8,10 @@ describe('data', function() { } }; - var result = template({noun: 'cat'}, {helpers: helpers, data: {adjective: 'happy'}}); + var result = template( + { noun: 'cat' }, + { helpers: helpers, data: { adjective: 'happy' } } + ); equals('happy cat', result, 'Data output by helper'); }); @@ -19,7 +22,9 @@ describe('data', function() { }); it('deep @foo triggers automatic top-level data', function() { - var template = CompilerContext.compile('{{#let world="world"}}{{#if foo}}{{#if foo}}Hello {{@world}}{{/if}}{{/if}}{{/let}}'); + var template = CompilerContext.compile( + '{{#let world="world"}}{{#if foo}}{{#if foo}}Hello {{@world}}{{/if}}{{/if}}{{/let}}' + ); var helpers = Handlebars.createFrame(handlebarsEnv.helpers); @@ -47,7 +52,11 @@ describe('data', function() { }; var result = template({}, { helpers: helpers, data: { world: 'world' } }); - equals('Hello world', result, '@foo as a parameter retrieves template data'); + equals( + 'Hello world', + result, + '@foo as a parameter retrieves template data' + ); }); it('hash values can be looked up via @foo', function() { @@ -59,7 +68,11 @@ describe('data', function() { }; var result = template({}, { helpers: helpers, data: { world: 'world' } }); - equals('Hello world', result, '@foo as a parameter retrieves template data'); + equals( + 'Hello world', + result, + '@foo as a parameter retrieves template data' + ); }); it('nested parameter data can be looked up via @foo.bar', function() { @@ -70,8 +83,15 @@ describe('data', function() { } }; - var result = template({}, { helpers: helpers, data: { world: {bar: 'world' } } }); - equals('Hello world', result, '@foo as a parameter retrieves template data'); + var result = template( + {}, + { helpers: helpers, data: { world: { bar: 'world' } } } + ); + equals( + 'Hello world', + result, + '@foo as a parameter retrieves template data' + ); }); it('nested parameter data does not fail with @world.bar', function() { @@ -82,8 +102,15 @@ describe('data', function() { } }; - var result = template({}, { helpers: helpers, data: { foo: {bar: 'world' } } }); - equals('Hello undefined', result, '@foo as a parameter retrieves template data'); + var result = template( + {}, + { helpers: helpers, data: { foo: { bar: 'world' } } } + ); + equals( + 'Hello undefined', + result, + '@foo as a parameter retrieves template data' + ); }); it('parameter data throws when using complex scope references', function() { @@ -96,17 +123,38 @@ describe('data', function() { it('data can be functions', function() { var template = CompilerContext.compile('{{@hello}}'); - var result = template({}, { data: { hello: function() { return 'hello'; } } }); + var result = template( + {}, + { + data: { + hello: function() { + return 'hello'; + } + } + } + ); equals('hello', result); }); it('data can be functions with params', function() { var template = CompilerContext.compile('{{@hello "hello"}}'); - var result = template({}, { data: { hello: function(arg) { return arg; } } }); + var result = template( + {}, + { + data: { + hello: function(arg) { + return arg; + } + } + } + ); equals('hello', result); }); it('data is inherited downstream', function() { - var template = CompilerContext.compile('{{#let foo=1 bar=2}}{{#let foo=bar.baz}}{{@bar}}{{@foo}}{{/let}}{{@foo}}{{/let}}', { data: true }); + var template = CompilerContext.compile( + '{{#let foo=1 bar=2}}{{#let foo=bar.baz}}{{@bar}}{{@foo}}{{/let}}{{@foo}}{{/let}}', + { data: true } + ); var helpers = { let: function(options) { var frame = Handlebars.createFrame(options.data); @@ -115,19 +163,22 @@ describe('data', function() { frame[prop] = options.hash[prop]; } } - return options.fn(this, {data: frame}); + return options.fn(this, { data: frame }); } }; - var result = template({ bar: { baz: 'hello world' } }, { helpers: helpers, data: {} }); + var result = template( + { bar: { baz: 'hello world' } }, + { helpers: helpers, data: {} } + ); equals('2hello world1', result, 'data variables are inherited downstream'); }); it('passing in data to a compiled function that expects data - works with helpers in partials', function() { - var template = CompilerContext.compile('{{>myPartial}}', {data: true}); + var template = CompilerContext.compile('{{>myPartial}}', { data: true }); var partials = { - myPartial: CompilerContext.compile('{{hello}}', {data: true}) + myPartial: CompilerContext.compile('{{hello}}', { data: true }) }; var helpers = { @@ -136,12 +187,15 @@ describe('data', function() { } }; - var result = template({noun: 'cat'}, {helpers: helpers, partials: partials, data: {adjective: 'happy'}}); + var result = template( + { noun: 'cat' }, + { helpers: helpers, partials: partials, data: { adjective: 'happy' } } + ); equals('happy cat', result, 'Data output by helper inside partial'); }); it('passing in data to a compiled function that expects data - works with helpers and parameters', function() { - var template = CompilerContext.compile('{{hello world}}', {data: true}); + var template = CompilerContext.compile('{{hello world}}', { data: true }); var helpers = { hello: function(noun, options) { @@ -149,12 +203,17 @@ describe('data', function() { } }; - var result = template({exclaim: true, world: 'world'}, {helpers: helpers, data: {adjective: 'happy'}}); + var result = template( + { exclaim: true, world: 'world' }, + { helpers: helpers, data: { adjective: 'happy' } } + ); equals('happy world!', result, 'Data output by helper'); }); it('passing in data to a compiled function that expects data - works with block helpers', function() { - var template = CompilerContext.compile('{{#hello}}{{world}}{{/hello}}', {data: true}); + var template = CompilerContext.compile('{{#hello}}{{world}}{{/hello}}', { + data: true + }); var helpers = { hello: function(options) { @@ -165,106 +224,140 @@ describe('data', function() { } }; - var result = template({exclaim: true}, {helpers: helpers, data: {adjective: 'happy'}}); + var result = template( + { exclaim: true }, + { helpers: helpers, data: { adjective: 'happy' } } + ); equals('happy world!', result, 'Data output by helper'); }); it('passing in data to a compiled function that expects data - works with block helpers that use ..', function() { - var template = CompilerContext.compile('{{#hello}}{{world ../zomg}}{{/hello}}', {data: true}); + var template = CompilerContext.compile( + '{{#hello}}{{world ../zomg}}{{/hello}}', + { data: true } + ); var helpers = { hello: function(options) { - return options.fn({exclaim: '?'}); + return options.fn({ exclaim: '?' }); }, world: function(thing, options) { return options.data.adjective + ' ' + thing + (this.exclaim || ''); } }; - var result = template({exclaim: true, zomg: 'world'}, {helpers: helpers, data: {adjective: 'happy'}}); + var result = template( + { exclaim: true, zomg: 'world' }, + { helpers: helpers, data: { adjective: 'happy' } } + ); equals('happy world?', result, 'Data output by helper'); }); it('passing in data to a compiled function that expects data - data is passed to with block helpers where children use ..', function() { - var template = CompilerContext.compile('{{#hello}}{{world ../zomg}}{{/hello}}', {data: true}); + var template = CompilerContext.compile( + '{{#hello}}{{world ../zomg}}{{/hello}}', + { data: true } + ); var helpers = { hello: function(options) { - return options.data.accessData + ' ' + options.fn({exclaim: '?'}); + return options.data.accessData + ' ' + options.fn({ exclaim: '?' }); }, world: function(thing, options) { return options.data.adjective + ' ' + thing + (this.exclaim || ''); } }; - var result = template({exclaim: true, zomg: 'world'}, {helpers: helpers, data: {adjective: 'happy', accessData: '#win'}}); + var result = template( + { exclaim: true, zomg: 'world' }, + { helpers: helpers, data: { adjective: 'happy', accessData: '#win' } } + ); equals('#win happy world?', result, 'Data output by helper'); }); it('you can override inherited data when invoking a helper', function() { - var template = CompilerContext.compile('{{#hello}}{{world zomg}}{{/hello}}', {data: true}); + var template = CompilerContext.compile( + '{{#hello}}{{world zomg}}{{/hello}}', + { data: true } + ); var helpers = { hello: function(options) { - return options.fn({exclaim: '?', zomg: 'world'}, { data: {adjective: 'sad'} }); + return options.fn( + { exclaim: '?', zomg: 'world' }, + { data: { adjective: 'sad' } } + ); }, world: function(thing, options) { return options.data.adjective + ' ' + thing + (this.exclaim || ''); } }; - var result = template({exclaim: true, zomg: 'planet'}, {helpers: helpers, data: {adjective: 'happy'}}); + var result = template( + { exclaim: true, zomg: 'planet' }, + { helpers: helpers, data: { adjective: 'happy' } } + ); equals('sad world?', result, 'Overriden data output by helper'); }); - it('you can override inherited data when invoking a helper with depth', function() { - var template = CompilerContext.compile('{{#hello}}{{world ../zomg}}{{/hello}}', {data: true}); + var template = CompilerContext.compile( + '{{#hello}}{{world ../zomg}}{{/hello}}', + { data: true } + ); var helpers = { hello: function(options) { - return options.fn({exclaim: '?'}, { data: {adjective: 'sad'} }); + return options.fn({ exclaim: '?' }, { data: { adjective: 'sad' } }); }, world: function(thing, options) { return options.data.adjective + ' ' + thing + (this.exclaim || ''); } }; - var result = template({exclaim: true, zomg: 'world'}, {helpers: helpers, data: {adjective: 'happy'}}); + var result = template( + { exclaim: true, zomg: 'world' }, + { helpers: helpers, data: { adjective: 'happy' } } + ); equals('sad world?', result, 'Overriden data output by helper'); }); describe('@root', function() { it('the root context can be looked up via @root', function() { var template = CompilerContext.compile('{{@root.foo}}'); - var result = template({foo: 'hello'}, { data: {} }); + var result = template({ foo: 'hello' }, { data: {} }); equals('hello', result); - result = template({foo: 'hello'}, {}); + result = template({ foo: 'hello' }, {}); equals('hello', result); }); it('passed root values take priority', function() { var template = CompilerContext.compile('{{@root.foo}}'); - var result = template({}, { data: {root: {foo: 'hello'} } }); + var result = template({}, { data: { root: { foo: 'hello' } } }); equals('hello', result); }); }); describe('nesting', function() { it('the root context can be looked up via @root', function() { - var template = CompilerContext.compile('{{#helper}}{{#helper}}{{@./depth}} {{@../depth}} {{@../../depth}}{{/helper}}{{/helper}}'); - var result = template({foo: 'hello'}, { - helpers: { - helper: function(options) { - var frame = Handlebars.createFrame(options.data); - frame.depth = options.data.depth + 1; - return options.fn(this, {data: frame}); + var template = CompilerContext.compile( + '{{#helper}}{{#helper}}{{@./depth}} {{@../depth}} {{@../../depth}}{{/helper}}{{/helper}}' + ); + var result = template( + { foo: 'hello' }, + { + helpers: { + helper: function(options) { + var frame = Handlebars.createFrame(options.data); + frame.depth = options.data.depth + 1; + return options.fn(this, { data: frame }); + } + }, + data: { + depth: 0 } - }, - data: { - depth: 0 } - }); + ); equals('2 1 0', result); }); }); diff --git a/spec/env/browser.js b/spec/env/browser.js index d1dab0d5..28541b77 100644 --- a/spec/env/browser.js +++ b/spec/env/browser.js @@ -1,12 +1,11 @@ require('./common'); var fs = require('fs'), - vm = require('vm'); + vm = require('vm'); var chai = require('chai'); var dirtyChai = require('dirty-chai'); - chai.use(dirtyChai); global.expect = chai.expect; @@ -18,7 +17,10 @@ var filename = 'dist/handlebars.js'; if (global.minimizedTest) { filename = 'dist/handlebars.min.js'; } -var distHandlebars = fs.readFileSync(require.resolve('../../' + filename), 'utf-8'); +var distHandlebars = fs.readFileSync( + require.resolve('../../' + filename), + 'utf-8' +); vm.runInThisContext(distHandlebars, filename); global.CompilerContext = { diff --git a/spec/env/common.js b/spec/env/common.js index ac942184..64451fee 100644 --- a/spec/env/common.js +++ b/spec/env/common.js @@ -1,6 +1,6 @@ var global = (function() { return this; -}()); +})(); var AssertError; if (Error.captureStackTrace) { @@ -28,10 +28,19 @@ global.shouldCompileTo = function(string, hashOrArray, expected, message) { /** * @deprecated Use "expectTemplate(template)...toCompileTo(output)" instead */ -global.shouldCompileToWithPartials = function shouldCompileToWithPartials(string, hashOrArray, partials, expected, message) { +global.shouldCompileToWithPartials = function shouldCompileToWithPartials( + string, + hashOrArray, + partials, + expected, + message +) { var result = compileWithPartials(string, hashOrArray, partials); if (result !== expected) { - throw new AssertError("'" + result + "' should === '" + expected + "': " + message, shouldCompileToWithPartials); + throw new AssertError( + "'" + result + "' should === '" + expected + "': " + message, + shouldCompileToWithPartials + ); } }; @@ -39,17 +48,18 @@ global.shouldCompileToWithPartials = function shouldCompileToWithPartials(string * @deprecated Use "expectTemplate(template)...toCompileTo(output)" instead */ global.compileWithPartials = function(string, hashOrArray, partials) { - var template, - ary, - options; + var template, ary, options; if (hashOrArray && hashOrArray.hash) { ary = [hashOrArray.hash, hashOrArray]; delete hashOrArray.hash; } else if (Object.prototype.toString.call(hashOrArray) === '[object Array]') { ary = []; ary.push(hashOrArray[0]); // input - ary.push({helpers: hashOrArray[1], partials: hashOrArray[2]}); - options = typeof hashOrArray[3] === 'object' ? hashOrArray[3] : {compat: hashOrArray[3]}; + ary.push({ helpers: hashOrArray[1], partials: hashOrArray[2] }); + options = + typeof hashOrArray[3] === 'object' + ? hashOrArray[3] + : { compat: hashOrArray[3] }; if (hashOrArray[4] != null) { options.data = !!hashOrArray[4]; ary[1].data = hashOrArray[4]; @@ -58,18 +68,23 @@ global.compileWithPartials = function(string, hashOrArray, partials) { ary = [hashOrArray]; } - template = CompilerContext[partials ? 'compileWithPartial' : 'compile'](string, options); + template = CompilerContext[partials ? 'compileWithPartial' : 'compile']( + string, + options + ); return template.apply(this, ary); }; - /** * @deprecated Use chai's expect-style API instead (`expect(actualValue).to.equal(expectedValue)`) * @see https://www.chaijs.com/api/bdd/ */ global.equals = global.equal = function equals(a, b, msg) { if (a !== b) { - throw new AssertError("'" + a + "' should === '" + b + "'" + (msg ? ': ' + msg : ''), equals); + throw new AssertError( + "'" + a + "' should === '" + b + "'" + (msg ? ': ' + msg : ''), + equals + ); } }; @@ -86,8 +101,19 @@ global.shouldThrow = function(callback, type, msg) { if (type && !(caught instanceof type)) { throw new AssertError('Type failure: ' + caught); } - if (msg && !(msg.test ? msg.test(caught.message) : msg === caught.message)) { - throw new AssertError('Throw mismatch: Expected ' + caught.message + ' to match ' + msg + '\n\n' + caught.stack, shouldThrow); + if ( + msg && + !(msg.test ? msg.test(caught.message) : msg === caught.message) + ) { + throw new AssertError( + 'Throw mismatch: Expected ' + + caught.message + + ' to match ' + + msg + + '\n\n' + + caught.stack, + shouldThrow + ); } } if (failed) { @@ -95,18 +121,17 @@ global.shouldThrow = function(callback, type, msg) { } }; - global.expectTemplate = function(templateAsString) { return new HandlebarsTestBench(templateAsString); }; - function HandlebarsTestBench(templateAsString) { this.templateAsString = templateAsString; this.helpers = {}; this.partials = {}; this.input = {}; - this.message = 'Template' + templateAsString + ' does not evaluate to expected output'; + this.message = + 'Template' + templateAsString + ' does not evaluate to expected output'; this.compileOptions = {}; this.runtimeOptions = {}; } @@ -146,7 +171,11 @@ HandlebarsTestBench.prototype.toCompileTo = function(expectedOutputAsString) { }; // see chai "to.throw" (https://www.chaijs.com/api/bdd/#method_throw) -HandlebarsTestBench.prototype.toThrow = function(errorLike, errMsgMatcher, msg) { +HandlebarsTestBench.prototype.toThrow = function( + errorLike, + errMsgMatcher, + msg +) { var self = this; expect(function() { self._compileAndExeute(); @@ -154,7 +183,8 @@ HandlebarsTestBench.prototype.toThrow = function(errorLike, errMsgMatcher, msg) }; HandlebarsTestBench.prototype._compileAndExeute = function() { - var compile = Object.keys(this.partials).length > 0 + var compile = + Object.keys(this.partials).length > 0 ? CompilerContext.compileWithPartial : CompilerContext.compile; diff --git a/spec/env/node.js b/spec/env/node.js index 0695468b..1d1cd09c 100644 --- a/spec/env/node.js +++ b/spec/env/node.js @@ -3,7 +3,6 @@ require('./common'); var chai = require('chai'); var dirtyChai = require('dirty-chai'); - chai.use(dirtyChai); global.expect = chai.expect; diff --git a/spec/env/runner.js b/spec/env/runner.js index a069c2d7..39c522ca 100644 --- a/spec/env/runner.js +++ b/spec/env/runner.js @@ -1,11 +1,11 @@ /* eslint-disable no-console */ var fs = require('fs'), - Mocha = require('mocha'), - path = require('path'); + Mocha = require('mocha'), + path = require('path'); var errors = 0, - testDir = path.dirname(__dirname), - grep = process.argv[2]; + testDir = path.dirname(__dirname), + grep = process.argv[2]; // Lazy hack, but whatever if (grep === '--min') { @@ -13,9 +13,14 @@ if (grep === '--min') { grep = undefined; } -var files = fs.readdirSync(testDir) - .filter(function(name) { return (/.*\.js$/).test(name); }) - .map(function(name) { return testDir + path.sep + name; }); +var files = fs + .readdirSync(testDir) + .filter(function(name) { + return /.*\.js$/.test(name); + }) + .map(function(name) { + return testDir + path.sep + name; + }); if (global.minimizedTest) { run('./runtime', function() { @@ -37,7 +42,6 @@ if (global.minimizedTest) { }); } - function run(env, callback) { var mocha = new Mocha(); mocha.ui('bdd'); diff --git a/spec/env/runtime.js b/spec/env/runtime.js index 4f6d2853..99b5bf78 100644 --- a/spec/env/runtime.js +++ b/spec/env/runtime.js @@ -1,12 +1,11 @@ require('./common'); var fs = require('fs'), - vm = require('vm'); + vm = require('vm'); var chai = require('chai'); var dirtyChai = require('dirty-chai'); - chai.use(dirtyChai); global.expect = chai.expect; @@ -18,7 +17,10 @@ var filename = 'dist/handlebars.runtime.js'; if (global.minimizedTest) { filename = 'dist/handlebars.runtime.min.js'; } -vm.runInThisContext(fs.readFileSync(__dirname + '/../../' + filename), filename); +vm.runInThisContext( + fs.readFileSync(__dirname + '/../../' + filename), + filename +); var parse = require('../../dist/cjs/handlebars/compiler/base').parse; var compiler = require('../../dist/cjs/handlebars/compiler/compiler'); @@ -30,7 +32,11 @@ global.CompilerContext = { compile: function(template, options) { // Hack the compiler on to the environment for these specific tests handlebarsEnv.precompile = function(precompileTemplate, precompileOptions) { - return compiler.precompile(precompileTemplate, precompileOptions, handlebarsEnv); + return compiler.precompile( + precompileTemplate, + precompileOptions, + handlebarsEnv + ); }; handlebarsEnv.parse = parse; handlebarsEnv.Compiler = compiler.Compiler; diff --git a/spec/helpers.js b/spec/helpers.js index 634d7027..9d4bc202 100644 --- a/spec/helpers.js +++ b/spec/helpers.js @@ -1,35 +1,57 @@ describe('helpers', function() { it('helper with complex lookup$', function() { var string = '{{#goodbyes}}{{{link ../prefix}}}{{/goodbyes}}'; - var hash = {prefix: '/root', goodbyes: [{text: 'Goodbye', url: 'goodbye'}]}; - var helpers = {link: function(prefix) { - return '' + this.text + ''; - }}; - shouldCompileTo(string, [hash, helpers], 'Goodbye'); + var hash = { + prefix: '/root', + goodbyes: [{ text: 'Goodbye', url: 'goodbye' }] + }; + var helpers = { + link: function(prefix) { + return ( + '' + this.text + '' + ); + } + }; + shouldCompileTo( + string, + [hash, helpers], + 'Goodbye' + ); }); it('helper for raw block gets raw content', function() { var string = '{{{{raw}}}} {{test}} {{{{/raw}}}}'; var hash = { test: 'hello' }; - var helpers = { raw: function(options) { + var helpers = { + raw: function(options) { return options.fn(); - } }; - shouldCompileTo(string, [hash, helpers], ' {{test}} ', - 'raw block helper gets raw content'); + } + }; + shouldCompileTo( + string, + [hash, helpers], + ' {{test}} ', + 'raw block helper gets raw content' + ); }); it('helper for raw block gets parameters', function() { var string = '{{{{raw 1 2 3}}}} {{test}} {{{{/raw}}}}'; var hash = { test: 'hello' }; - var helpers = { raw: function(a, b, c, options) { + var helpers = { + raw: function(a, b, c, options) { return options.fn() + a + b + c; - } }; - shouldCompileTo(string, [hash, helpers], ' {{test}} 123', - 'raw block helper gets raw content'); + } + }; + shouldCompileTo( + string, + [hash, helpers], + ' {{test}} 123', + 'raw block helper gets raw content' + ); }); describe('raw block parsing (with identity helper-function)', function() { - function runWithIdentityHelper(template, expected) { var helpers = { identity: function(options) { @@ -40,7 +62,10 @@ describe('helpers', function() { } it('helper for nested raw block gets raw content', function() { - runWithIdentityHelper('{{{{identity}}}} {{{{b}}}} {{{{/b}}}} {{{{/identity}}}}', ' {{{{b}}}} {{{{/b}}}} '); + runWithIdentityHelper( + '{{{{identity}}}} {{{{b}}}} {{{{/b}}}} {{{{/identity}}}}', + ' {{{{b}}}} {{{{/b}}}} ' + ); }); it('helper for nested raw block works with empty content', function() { @@ -52,11 +77,17 @@ describe('helpers', function() { // The test is deactivated, because in 3.x this template cases an exception and it also does not work in 4.4.3 // If anyone can make this template work without breaking everything else, then go for it, // but for now, this is just a known bug, that will be documented. - runWithIdentityHelper('{{{{identity}}}} {{{{a}}}} {{{{ {{{{/ }}}} }}}} {{{{/identity}}}}', ' {{{{a}}}} {{{{ {{{{/ }}}} }}}} '); + runWithIdentityHelper( + '{{{{identity}}}} {{{{a}}}} {{{{ {{{{/ }}}} }}}} {{{{/identity}}}}', + ' {{{{a}}}} {{{{ {{{{/ }}}} }}}} ' + ); }); it('helper for nested raw block closes after first matching close', function() { - runWithIdentityHelper('{{{{identity}}}}abc{{{{/identity}}}} {{{{identity}}}}abc{{{{/identity}}}}', 'abc abc'); + runWithIdentityHelper( + '{{{{identity}}}}abc{{{{/identity}}}} {{{{identity}}}}abc{{{{/identity}}}}', + 'abc abc' + ); }); it('helper for nested raw block throw exception when with missing closing braces', function() { @@ -69,58 +100,121 @@ describe('helpers', function() { it('helper block with identical context', function() { var string = '{{#goodbyes}}{{name}}{{/goodbyes}}'; - var hash = {name: 'Alan'}; - var helpers = {goodbyes: function(options) { - var out = ''; - var byes = ['Goodbye', 'goodbye', 'GOODBYE']; - for (var i = 0, j = byes.length; i < j; i++) { - out += byes[i] + ' ' + options.fn(this) + '! '; + var hash = { name: 'Alan' }; + var helpers = { + goodbyes: function(options) { + var out = ''; + var byes = ['Goodbye', 'goodbye', 'GOODBYE']; + for (var i = 0, j = byes.length; i < j; i++) { + out += byes[i] + ' ' + options.fn(this) + '! '; + } + return out; } - return out; - }}; - shouldCompileTo(string, [hash, helpers], 'Goodbye Alan! goodbye Alan! GOODBYE Alan! '); + }; + shouldCompileTo( + string, + [hash, helpers], + 'Goodbye Alan! goodbye Alan! GOODBYE Alan! ' + ); }); it('helper block with complex lookup expression', function() { var string = '{{#goodbyes}}{{../name}}{{/goodbyes}}'; - var hash = {name: 'Alan'}; - var helpers = {goodbyes: function(options) { - var out = ''; - var byes = ['Goodbye', 'goodbye', 'GOODBYE']; - for (var i = 0, j = byes.length; i < j; i++) { - out += byes[i] + ' ' + options.fn({}) + '! '; + var hash = { name: 'Alan' }; + var helpers = { + goodbyes: function(options) { + var out = ''; + var byes = ['Goodbye', 'goodbye', 'GOODBYE']; + for (var i = 0, j = byes.length; i < j; i++) { + out += byes[i] + ' ' + options.fn({}) + '! '; + } + return out; } - return out; - }}; - shouldCompileTo(string, [hash, helpers], 'Goodbye Alan! goodbye Alan! GOODBYE Alan! '); + }; + shouldCompileTo( + string, + [hash, helpers], + 'Goodbye Alan! goodbye Alan! GOODBYE Alan! ' + ); }); it('helper with complex lookup and nested template', function() { - var string = '{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}'; - var hash = {prefix: '/root', goodbyes: [{text: 'Goodbye', url: 'goodbye'}]}; - var helpers = {link: function(prefix, options) { - return '' + options.fn(this) + ''; - }}; - shouldCompileToWithPartials(string, [hash, helpers], false, 'Goodbye'); + var string = + '{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}'; + var hash = { + prefix: '/root', + goodbyes: [{ text: 'Goodbye', url: 'goodbye' }] + }; + var helpers = { + link: function(prefix, options) { + return ( + '' + + options.fn(this) + + '' + ); + } + }; + shouldCompileToWithPartials( + string, + [hash, helpers], + false, + 'Goodbye' + ); }); it('helper with complex lookup and nested template in VM+Compiler', function() { - var string = '{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}'; - var hash = {prefix: '/root', goodbyes: [{text: 'Goodbye', url: 'goodbye'}]}; - var helpers = {link: function(prefix, options) { - return '' + options.fn(this) + ''; - }}; - shouldCompileToWithPartials(string, [hash, helpers], true, 'Goodbye'); + var string = + '{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}'; + var hash = { + prefix: '/root', + goodbyes: [{ text: 'Goodbye', url: 'goodbye' }] + }; + var helpers = { + link: function(prefix, options) { + return ( + '' + + options.fn(this) + + '' + ); + } + }; + shouldCompileToWithPartials( + string, + [hash, helpers], + true, + 'Goodbye' + ); }); it('helper returning undefined value', function() { - shouldCompileTo(' {{nothere}}', [{}, {nothere: function() {}}], ' '); - shouldCompileTo(' {{#nothere}}{{/nothere}}', [{}, {nothere: function() {}}], ' '); + shouldCompileTo(' {{nothere}}', [{}, { nothere: function() {} }], ' '); + shouldCompileTo( + ' {{#nothere}}{{/nothere}}', + [{}, { nothere: function() {} }], + ' ' + ); }); it('block helper', function() { var string = '{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!'; var template = CompilerContext.compile(string); - var result = template({world: 'world'}, { helpers: {goodbyes: function(options) { return options.fn({text: 'GOODBYE'}); }}}); + var result = template( + { world: 'world' }, + { + helpers: { + goodbyes: function(options) { + return options.fn({ text: 'GOODBYE' }); + } + } + } + ); equal(result, 'GOODBYE! cruel world!', 'Block helper executed'); }); @@ -128,21 +222,41 @@ describe('helpers', function() { var string = '{{#form}}

{{name}}

{{/form}}'; var template = CompilerContext.compile(string); - var result = template({name: 'Yehuda'}, {helpers: {form: function(options) { return '
' + options.fn(this) + '
'; } }}); - equal(result, '

Yehuda

', 'Block helper executed with current context'); + var result = template( + { name: 'Yehuda' }, + { + helpers: { + form: function(options) { + return '
' + options.fn(this) + '
'; + } + } + } + ); + equal( + result, + '

Yehuda

', + 'Block helper executed with current context' + ); }); it('block helper should have context in this', function() { - var source = ''; + var source = + ''; function link(options) { return '' + options.fn(this) + ''; } - var data = { 'people': [ - { 'name': 'Alan', 'id': 1 }, - { 'name': 'Yehuda', 'id': 2 } - ]}; + var data = { + people: [ + { name: 'Alan', id: 1 }, + { name: 'Yehuda', id: 2 } + ] + }; - shouldCompileTo(source, [data, {link: link}], ''); + shouldCompileTo( + source, + [data, { link: link }], + '' + ); }); it('block helper for undefined value', function() { @@ -153,7 +267,16 @@ describe('helpers', function() { var string = '{{#form yehuda}}

{{name}}

{{/form}}'; var template = CompilerContext.compile(string); - var result = template({yehuda: {name: 'Yehuda'}}, { helpers: {form: function(context, options) { return '
' + options.fn(context) + '
'; }}}); + var result = template( + { yehuda: { name: 'Yehuda' } }, + { + helpers: { + form: function(context, options) { + return '
' + options.fn(context) + '
'; + } + } + } + ); equal(result, '

Yehuda

', 'Context variable resolved'); }); @@ -161,27 +284,52 @@ describe('helpers', function() { var string = '{{#form yehuda/cat}}

{{name}}

{{/form}}'; var template = CompilerContext.compile(string); - var result = template({yehuda: {name: 'Yehuda', cat: {name: 'Harold'}}}, { helpers: {form: function(context, options) { return '
' + options.fn(context) + '
'; }}}); - equal(result, '

Harold

', 'Complex path variable resolved'); + var result = template( + { yehuda: { name: 'Yehuda', cat: { name: 'Harold' } } }, + { + helpers: { + form: function(context, options) { + return '
' + options.fn(context) + '
'; + } + } + } + ); + equal( + result, + '

Harold

', + 'Complex path variable resolved' + ); }); it('nested block helpers', function() { - var string = '{{#form yehuda}}

{{name}}

{{#link}}Hello{{/link}}{{/form}}'; + var string = + '{{#form yehuda}}

{{name}}

{{#link}}Hello{{/link}}{{/form}}'; var template = CompilerContext.compile(string); - var result = template({ - yehuda: {name: 'Yehuda' } - }, { - helpers: { - link: function(options) { return '' + options.fn(this) + ''; }, - form: function(context, options) { return '
' + options.fn(context) + '
'; } + var result = template( + { + yehuda: { name: 'Yehuda' } + }, + { + helpers: { + link: function(options) { + return '' + options.fn(this) + ''; + }, + form: function(context, options) { + return '
' + options.fn(context) + '
'; + } + } } - }); - equal(result, '

Yehuda

Hello
', 'Both blocks executed'); + ); + equal( + result, + '

Yehuda

Hello
', + 'Both blocks executed' + ); }); it('block helper inverted sections', function() { - var string = '{{#list people}}{{name}}{{^}}Nobody\'s here{{/list}}'; + var string = "{{#list people}}{{name}}{{^}}Nobody's here{{/list}}"; function list(context, options) { if (context.length > 0) { var out = '