From e97685e989526a501d5884a9c265891d52bf3a3e Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Tue, 3 Dec 2019 22:37:15 +0100 Subject: [PATCH] style: reformat all files using prettier --- Gruntfile.js | 146 ++- bench/dist-size.js | 17 +- bench/index.js | 2 +- bench/precompile-size.js | 13 +- bench/templates/arguments.js | 3 +- bench/templates/array-each.js | 9 +- bench/templates/array-mustache.js | 9 +- bench/templates/complex.js | 6 +- bench/templates/data.js | 9 +- bench/templates/depth-1.js | 10 +- bench/templates/depth-2.js | 16 +- bench/templates/index.js | 2 +- bench/templates/partial-recursion.js | 5 +- bench/templates/partial.js | 12 +- bench/templates/paths.js | 8 +- bench/templates/variables.js | 3 +- bench/throughput.js | 76 +- bench/util/benchwarmer.js | 69 +- bench/util/template-runner.js | 6 +- .../multi-nodejs-test/run-handlebars.js | 2 +- .../src/handlebars-inline-precompile-test.js | 10 +- .../webpack-babel-test/src/lib/assert.js | 6 +- .../webpack-babel-test/webpack.config.js | 48 +- .../handlebars-default-import-pre-4.2-test.js | 4 +- .../src/handlebars-default-import-test.js | 5 +- .../src/handlebars-loader-test.js | 10 +- .../src/handlebars-require-vs-import-test.js | 6 +- ...handlebars-wildcard-import-pre-4.2-test.js | 4 +- .../src/handlebars-wildcard-import-test.js | 4 +- .../webpack-test/src/lib/assert.js | 6 +- .../webpack-test/webpack.config.js | 26 +- lib/handlebars.js | 6 +- lib/handlebars/base.js | 20 +- lib/handlebars/compiler/ast.js | 16 +- lib/handlebars/compiler/base.js | 4 +- lib/handlebars/compiler/code-gen.js | 37 +- lib/handlebars/compiler/compiler.js | 148 ++- lib/handlebars/compiler/helpers.js | 49 +- .../compiler/javascript-compiler.js | 297 ++++-- lib/handlebars/compiler/printer.js | 31 +- lib/handlebars/compiler/visitor.js | 9 +- lib/handlebars/compiler/whitespace-control.js | 76 +- lib/handlebars/decorators.js | 1 - lib/handlebars/decorators/inline.js | 2 +- lib/handlebars/exception.js | 20 +- .../helpers/block-helper-missing.js | 11 +- lib/handlebars/helpers/each.js | 38 +- lib/handlebars/helpers/helper-missing.js | 4 +- lib/handlebars/helpers/if.js | 18 +- lib/handlebars/helpers/log.js | 4 +- lib/handlebars/helpers/lookup.js | 5 +- lib/handlebars/helpers/with.js | 21 +- lib/handlebars/logger.js | 10 +- lib/handlebars/no-conflict.js | 3 +- lib/handlebars/runtime.js | 190 +++- lib/handlebars/utils.js | 27 +- lib/precompiler.js | 192 ++-- spec/ast.js | 244 +++-- spec/basic.js | 581 ++++++++--- spec/blocks.js | 361 +++++-- spec/builtins.js | 481 +++++++-- spec/compiler.js | 147 ++- spec/data.js | 189 +++- spec/env/browser.js | 8 +- spec/env/common.js | 66 +- spec/env/node.js | 1 - spec/env/runner.js | 20 +- spec/env/runtime.js | 14 +- spec/helpers.js | 974 +++++++++++++----- spec/javascript-compiler.js | 14 +- spec/parser.js | 358 +++++-- spec/partials.js | 666 +++++++++--- spec/precompiler.js | 277 +++-- spec/regressions.js | 329 ++++-- spec/require.js | 4 +- spec/runtime.js | 118 ++- spec/security.js | 322 +++--- spec/source-map.js | 24 +- spec/spec.js | 40 +- spec/strict.js | 195 ++-- spec/string-params.js | 150 ++- spec/subexpressions.js | 96 +- spec/tokenizer.js | 443 +++++++- spec/track-ids.js | 316 ++++-- spec/utils.js | 21 +- spec/visitor.js | 78 +- spec/whitespace-control.js | 81 +- tasks/metrics.js | 15 +- tasks/parser.js | 16 +- tasks/publish.js | 47 +- tasks/test.js | 48 +- tasks/util/git.js | 18 +- tasks/version.js | 78 +- 93 files changed, 6238 insertions(+), 2393 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index d91783f5f..9cf025960 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,32 +1,42 @@ /* 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\n <%= pkg.name %> v<%= pkg.version %>\n\n<%= grunt.file.read("LICENSE") %>\n*/\n') - + content; + return ( + grunt.template.process( + '/**!\n\n @license\n <%= pkg.name %> v<%= pkg.version %>\n\n<%= grunt.file.read("LICENSE") %>\n*/\n' + ) + content + ); } }, - files: [ - {expand: true, cwd: 'dist/', src: ['*.js'], dest: 'dist/'} - ] + files: [{ expand: true, cwd: 'dist/', src: ['*.js'], dest: 'dist/' }] }, cdnjs: { files: [ - {expand: true, cwd: 'dist/', src: ['*.js'], dest: 'dist/cdnjs'} + { expand: true, cwd: 'dist/', src: ['*.js'], dest: 'dist/cdnjs' } ] }, 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' } ] } }, @@ -41,24 +51,28 @@ module.exports = function(grunt) { options: { modules: 'amd' }, - files: [{ - expand: true, - cwd: 'lib/', - src: '**/!(index).js', - dest: 'dist/amd/' - }] + files: [ + { + expand: true, + cwd: 'lib/', + src: '**/!(index).js', + dest: 'dist/amd/' + } + ] }, cjs: { options: { modules: 'common' }, - files: [{ - cwd: 'lib/', - expand: true, - src: '**/!(index).js', - dest: 'dist/cjs/' - }] + files: [ + { + cwd: 'lib/', + expand: true, + src: '**/!(index).js', + dest: 'dist/cjs/' + } + ] } }, webpack: { @@ -67,7 +81,12 @@ module.exports = function(grunt) { module: { loaders: [ // the optional 'runtime' transformer tells babel to require the runtime instead of inlining it. - { test: /\.jsx?$/, exclude: /node_modules/, loader: 'babel-loader?optional=runtime&loose=es6.modules&auxiliaryCommentBefore=istanbul%20ignore%20next' } + { + test: /\.jsx?$/, + exclude: /node_modules/, + loader: + 'babel-loader?optional=runtime&loose=es6.modules&auxiliaryCommentBefore=istanbul%20ignore%20next' + } ] }, output: { @@ -116,15 +135,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'); + } } - }] + ] } }, @@ -148,28 +169,41 @@ module.exports = function(grunt) { all: { options: { build: process.env.TRAVIS_JOB_ID, - urls: ['http://localhost:9999/spec/?headless=true', 'http://localhost:9999/spec/amd.html?headless=true'], + urls: [ + 'http://localhost:9999/spec/?headless=true', + 'http://localhost:9999/spec/amd.html?headless=true' + ], 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/amd-runtime.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/amd-runtime.html?headless=true', + 'http://localhost:9999/spec/umd-runtime.html?headless=true' + ], detailedError: true, concurrency: 2, - browsers: [ - {browserName: 'chrome'} - ] + browsers: [{ browserName: 'chrome' }] } } }, @@ -180,7 +214,6 @@ module.exports = function(grunt) { bg: false, fail: true } - }, watch: { @@ -211,9 +244,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']); @@ -231,13 +264,30 @@ module.exports = function(grunt) { this.registerTask('amd', ['babel:amd', 'requirejs']); 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('on-file-change', ['build', 'amd', 'concat:tests', 'test']); + grunt.registerTask('extensive-tests-and-publish-to-aws', [ + 'default', + 'bgShell:integrationTests', + 'sauce', + 'metrics', + 'publish:latest' + ]); + grunt.registerTask('on-file-change', [ + 'build', + 'amd', + '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 d0bd24892..5bbf7ee33 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 3e357e54a..462b046f5 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 66eacd025..5aca9423c 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 aaa034686..f73d845f8 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 50e1c02b8..ddf07a6fe 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 220c6fe47..e7b2355ac 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 3e5e26c4e..545a479cb 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 be10e8399..4cb969d66 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 0f2576f43..65cb33b71 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 bff6ce850..bef804d1e 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 943f9cdfe..a718ea388 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 b903553b5..ce3c32a74 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 949e9c075..79a9d7dfa 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 fed039d51..3725e471c 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 41e4feafe..fddece1bc 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 e8ea5d910..b5e3a56a5 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; - - 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})); + 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 }) + ); }); 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 58f138a0e..90415bf56 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] = {}; - - _.each(times, function(time, lang) { - output[lang] = ((time - this.minimum) / (this.maximum - this.minimum) * 100).toFixed(2); - }, this); - }, this); + 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 + ); }, 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 b492ff3af..370e157ec 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 9aded26cf..6b0cbdca6 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 7f764e328..2d54945c8 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 9f33188db..00095006a 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 9ad0a09b2..bebb6ee0e 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 6bc630da3..b7ffe7119 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 97fc3f8b7..d8730b6c7 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 4727c7a3a..c6fc8d8d9 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 13e2c97d7..9c49f0cbd 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 3731c5ac8..2e4a1ff4d 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 2ea0c1b49..bbf3bb81a 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 9f33188db..00095006a 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 957321223..207aed30c 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 69bcb410b..c4f8fc781 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 ad3541b53..c655e8f09 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 1699569ba..d77629a52 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 19989ca5e..1dd5af1a4 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 30c5af740..4a174ba08 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 b56ca5c26..33bd2b11a 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; } } @@ -54,24 +57,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; @@ -97,7 +104,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]); } @@ -114,7 +121,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); @@ -149,7 +156,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); @@ -165,17 +172,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); } @@ -208,7 +218,6 @@ Compiler.prototype = { this.DecoratorBlock(decorator); }, - ContentStatement: function(content) { if (content.value) { this.opcode('appendContent', content.value); @@ -231,8 +240,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); @@ -254,19 +263,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) + ); } }, @@ -275,8 +292,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); @@ -287,7 +304,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 + ); } }, @@ -313,8 +336,8 @@ Compiler.prototype = { Hash: function(hash) { let pairs = hash.pairs, - i = 0, - l = pairs.length; + i = 0, + l = pairs.length; this.opcode('pushHash'); @@ -329,7 +352,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) { @@ -358,7 +385,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) { @@ -386,9 +413,7 @@ Compiler.prototype = { if (this.stringParams) { if (value.replace) { - value = value - .replace(/^(\.?\.\/)*/g, '') - .replace(/\//g, '.'); + value = value.replace(/^(\.?\.\/)*/g, '').replace(/\//g, '.'); } if (val.depth) { @@ -406,7 +431,7 @@ Compiler.prototype = { if (this.trackIds) { let blockParamIndex; if (val.parts && !AST.helpers.scopedId(val) && !val.depth) { - blockParamIndex = this.blockParamIndex(val.parts[0]); + blockParamIndex = this.blockParamIndex(val.parts[0]); } if (blockParamIndex) { let blockParamChild = val.parts.slice(1).join('.'); @@ -415,9 +440,9 @@ Compiler.prototype = { value = val.original || value; if (value.replace) { value = value - .replace(/^this(?:\.|$)/, '') - .replace(/^\.\//, '') - .replace(/^\.$/, ''); + .replace(/^this(?:\.|$)/, '') + .replace(/^\.\//, '') + .replace(/^\.$/, ''); } this.opcode('pushId', val.type, value); @@ -444,9 +469,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]; } @@ -455,8 +484,14 @@ Compiler.prototype = { }; export function precompile(input, options, env) { - if (input == null || (typeof input !== 'string' && input.type !== 'Program')) { - throw new Exception('You must pass a string or Handlebars AST to Handlebars.precompile. 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.precompile. You passed ' + + input + ); } options = options || {}; @@ -468,13 +503,19 @@ export function precompile(input, options, env) { } let ast = env.parse(input, options), - environment = new env.Compiler().compile(ast, options); + environment = new env.Compiler().compile(ast, options); return new env.JavaScriptCompiler().compile(environment, options); } export function compile(input, options = {}, env) { - 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 + ); } options = extend({}, options); @@ -489,8 +530,13 @@ export function compile(input, options = {}, env) { function compileInput() { let ast = env.parse(input, options), - environment = new env.Compiler().compile(ast, options), - templateSpec = new env.JavaScriptCompiler().compile(environment, options, undefined, true); + environment = new env.Compiler().compile(ast, options), + templateSpec = new env.JavaScriptCompiler().compile( + environment, + options, + undefined, + true + ); return env.template(templateSpec); } diff --git a/lib/handlebars/compiler/helpers.js b/lib/handlebars/compiler/helpers.js index f26b4e15c..27033a1d5 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 9a4c02daf..bcf9bb21c 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]; }, @@ -91,14 +98,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]; @@ -124,9 +135,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(); } @@ -146,7 +168,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]; @@ -176,11 +198,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(); @@ -221,7 +243,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; } }); @@ -243,18 +265,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(' + '); @@ -280,7 +307,6 @@ JavaScriptCompiler.prototype = { } }); - if (appendOnly) { if (bufferStart) { bufferStart.prepend('return '); @@ -289,7 +315,8 @@ JavaScriptCompiler.prototype = { this.source.push('return "";'); } } else { - varDeclarations += ', buffer = ' + (appendFirst ? '' : this.initializeBuffer()); + varDeclarations += + ', buffer = ' + (appendFirst ? '' : this.initializeBuffer()); if (bufferStart) { bufferStart.prepend('return buffer + '); @@ -300,7 +327,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(); @@ -316,8 +345,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(); @@ -334,8 +365,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(); @@ -344,9 +377,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] @@ -376,14 +414,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), + ' }' + ]); } } }, @@ -395,8 +443,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] @@ -480,7 +534,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. @@ -503,7 +557,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), + ')' + ]); }, // [pushStringParam] @@ -543,7 +604,7 @@ JavaScriptCompiler.prototype = { if (this.hash) { this.hashes.push(this.hash); } - this.hash = {values: {}, types: [], contexts: [], ids: []}; + this.hash = { values: {}, types: [], contexts: [], ids: [] }; }, popHash: function() { let hash = this.hash; @@ -607,11 +668,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;' ]); }, @@ -627,21 +693,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); }, @@ -685,22 +762,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))' ]); }, @@ -713,7 +799,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(); @@ -750,9 +836,9 @@ JavaScriptCompiler.prototype = { // Pops a value off the stack and assigns it to the current hash assignToHash: function(key) { let value = this.popStack(), - context, - type, - id; + context, + type, + id; if (this.trackIds) { id = this.popStack(); @@ -778,8 +864,13 @@ JavaScriptCompiler.prototype = { pushId: function(type, name, child) { if (type === 'BlockParam') { this.pushStackLiteral( - 'blockParams[' + name[0] + '].path[' + name[1] + ']' - + (child ? ' + ' + JSON.stringify('.' + child) : '')); + 'blockParams[' + + name[0] + + '].path[' + + name[1] + + ']' + + (child ? ' + ' + JSON.stringify('.' + child) : '') + ); } else if (type === 'PathExpression') { this.pushString(name); } else if (type === 'SubExpression') { @@ -794,7 +885,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]; @@ -807,7 +900,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; @@ -835,7 +933,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'); @@ -870,7 +968,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; } @@ -881,9 +983,9 @@ JavaScriptCompiler.prototype = { replaceStack: function(callback) { let prefix = ['('], - stack, - createdStack, - usedLiteral; + stack, + createdStack, + usedLiteral; /* istanbul ignore next */ if (!this.isInline()) { @@ -920,7 +1022,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() { @@ -947,9 +1051,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) { @@ -964,8 +1068,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) { @@ -1007,9 +1111,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, @@ -1021,11 +1129,11 @@ JavaScriptCompiler.prototype = { setupParams: function(helper, paramSize, params) { let options = {}, - contexts = [], - types = [], - ids = [], - objectArgs = !params, - param; + contexts = [], + types = [], + ids = [], + objectArgs = !params, + param; if (objectArgs) { params = []; @@ -1043,7 +1151,7 @@ JavaScriptCompiler.prototype = { } 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)` @@ -1106,7 +1214,6 @@ JavaScriptCompiler.prototype = { } }; - (function() { const reservedWords = ( 'break else new var' + @@ -1127,21 +1234,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--; } @@ -1151,7 +1261,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 6ad43baec..e087806cd 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 2c504d1b2..76bb01f12 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 8ebfbef57..7db683983 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 6f5a61525..d9d5e8a96 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 214246620..02f5be7b5 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 6333f1907..b2fdc00bc 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 6639ddb9d..9aeea9b97 100644 --- a/lib/handlebars/helpers/block-helper-missing.js +++ b/lib/handlebars/helpers/block-helper-missing.js @@ -1,9 +1,9 @@ -import {appendContextPath, createFrame, isArray} from '../utils'; +import { appendContextPath, createFrame, 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); @@ -22,8 +22,11 @@ export default function(instance) { } else { if (options.data && options.ids) { let data = createFrame(options.data); - data.contextPath = appendContextPath(options.data.contextPath, options.name); - options = {data: data}; + data.contextPath = appendContextPath( + options.data.contextPath, + options.name + ); + options = { data: data }; } return fn(context, options); diff --git a/lib/handlebars/helpers/each.js b/lib/handlebars/helpers/each.js index 8e57b7782..c1fa20f39 100644 --- a/lib/handlebars/helpers/each.js +++ b/lib/handlebars/helpers/each.js @@ -1,4 +1,10 @@ -import {appendContextPath, blockParams, createFrame, isArray, isFunction} from '../utils'; +import { + appendContextPath, + blockParams, + createFrame, + isArray, + isFunction +} from '../utils'; import Exception from '../exception'; export default function(instance) { @@ -8,17 +14,20 @@ export default function(instance) { } let fn = options.fn, - inverse = options.inverse, - i = 0, - ret = '', - data, - contextPath; + inverse = options.inverse, + i = 0, + ret = '', + data, + contextPath; if (options.data && options.ids) { - contextPath = appendContextPath(options.data.contextPath, options.ids[0]) + '.'; + contextPath = + appendContextPath(options.data.contextPath, options.ids[0]) + '.'; } - if (isFunction(context)) { context = context.call(this); } + if (isFunction(context)) { + context = context.call(this); + } if (options.data) { data = createFrame(options.data); @@ -36,10 +45,15 @@ export default function(instance) { } } - ret = ret + fn(context[field], { - data: data, - blockParams: blockParams([context[field], field], [contextPath + field, null]) - }); + ret = + ret + + fn(context[field], { + data: data, + blockParams: blockParams( + [context[field], field], + [contextPath + field, null] + ) + }); } if (context && typeof context === 'object') { diff --git a/lib/handlebars/helpers/helper-missing.js b/lib/handlebars/helpers/helper-missing.js index ec32e8245..f8869e9c9 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 5be88b70a..55241ecb7 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 4bde4a10d..ab0d73646 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 8c1b604be..66de538b6 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 cfeab4240..eb1cf1a90 100644 --- a/lib/handlebars/helpers/with.js +++ b/lib/handlebars/helpers/with.js @@ -1,10 +1,20 @@ -import { appendContextPath, blockParams, createFrame, isEmpty, isFunction } from '../utils'; +import { + appendContextPath, + blockParams, + createFrame, + 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; @@ -12,7 +22,10 @@ export default function(instance) { let data = options.data; if (options.data && options.ids) { data = createFrame(options.data); - data.contextPath = appendContextPath(options.data.contextPath, options.ids[0]); + data.contextPath = appendContextPath( + options.data.contextPath, + options.ids[0] + ); } return fn(context, { diff --git a/lib/handlebars/logger.js b/lib/handlebars/logger.js index 1e916a995..bc411b250 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 40a44d7a6..618b750f0 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 59b1cc106..4d67895af 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) { @@ -51,12 +69,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) { @@ -73,7 +100,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' + ); } } @@ -81,7 +112,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]; }, @@ -109,9 +142,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); } @@ -127,7 +168,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); } @@ -148,19 +189,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; @@ -171,25 +233,32 @@ 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; } - }; ret._child = function(i, data, blockParams, depths) { @@ -200,24 +269,47 @@ export function template(templateSpec, env) { throw new Exception('must pass parent depths'); } - return wrapProgram(container, i, templateSpec[i], data, 0, blockParams, depths); + return wrapProgram( + container, + i, + templateSpec[i], + data, + 0, + blockParams, + depths + ); }; 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); @@ -259,8 +351,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); @@ -283,7 +377,9 @@ export function invokePartial(partial, context, options) { } } -export function noop() { return ''; } +export function noop() { + return ''; +} function initData(context, data) { if (!data || !('root' in data)) { @@ -296,7 +392,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 8e1a4014f..6fa43b23c 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); } diff --git a/lib/precompiler.js b/lib/precompiler.js index ecf264eff..689c9f4d9 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); - - if (names.length !== strings.length - && strings.length > 1) { - return callback(new Handlebars.Exception('Number of names did not match the number of string inputs')); + 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' + ) + ); } - 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}"`)); - } - - if (stat.isDirectory()) { - opts.hasDirectory = true; + fs.stat(path, function(err, stat) { + if (err) { + return callback( + new Handlebars.Exception(`Unable to open template file "${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; + if (stat.isDirectory()) { + opts.hasDirectory = true; - 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; + + 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); + } - 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 - }); + if (opts.bom && data.indexOf('\uFEFF') === 0) { + data = data.substring(1); + } - callback(); - }); + // 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 { + 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 ffd0a9589..1f4146ae0 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: '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: '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: '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: '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: '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 e4b253665..65d228729 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 2fbaee7cc..1ef2d1be2 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'); - - shouldCompileTo(string, {goodbyes: [], world: 'world'}, 'cruel world!', - 'Arrays ignore the contents when 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' + ); }); 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'); - - shouldCompileTo(string, {goodbyes: [], world: 'world'}, 'cruel world!', - 'Arrays ignore the contents when 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' + ); }); it('block with complex lookup', function() { var string = '{{#goodbyes}}{{text}} cruel {{../name}}! {{/goodbyes}}'; - 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'); + 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' + ); }); 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 result = template({person: [{first: 'Alan', last: 'Johnson'}, {first: 'Alan', last: 'Johnson'}]}); + 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' } + ] + }); 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 b29927f06..d6bc29868 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 3d37b5940..fe394b720 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,33 +133,52 @@ describe('compiler', function() { }); 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.precompile. You passed null'); - shouldThrow(function() { - Handlebars.precompile({}); - }, Error, 'You must pass a string or Handlebars AST to Handlebars.precompile. You passed [object Object]'); + shouldThrow( + function() { + Handlebars.precompile(null); + }, + Error, + 'You must pass a string or Handlebars AST to Handlebars.precompile. You passed null' + ); + shouldThrow( + function() { + Handlebars.precompile({}); + }, + Error, + 'You must pass a string or Handlebars AST to Handlebars.precompile. 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 547a2664e..0defdc14a 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 d1dab0d5e..28541b776 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 ac9421845..64451fee4 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 0695468ba..1d1cd09c8 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 a069c2d71..39c522cad 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 4f6d28534..99b5bf785 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 634d7027e..9d4bc202e 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 = '
    {{#people}}
  • {{#link}}{{name}}{{/link}}
  • {{/people}}
'; + var source = + '
    {{#people}}
  • {{#link}}{{name}}{{/link}}
  • {{/people}}
'; 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 = '
    '; @@ -197,20 +345,35 @@ describe('helpers', function() { } } - var hash = {people: [{name: 'Alan'}, {name: 'Yehuda'}]}; - var empty = {people: []}; + var hash = { people: [{ name: 'Alan' }, { name: 'Yehuda' }] }; + var empty = { people: [] }; var rootMessage = { people: [], - message: 'Nobody\'s here' + message: "Nobody's here" }; var messageString = '{{#list people}}Hello{{^}}{{message}}{{/list}}'; // the meaning here may be kind of hard to catch, but list.not is always called, // so we should see the output of both - shouldCompileTo(string, [hash, { list: list }], '
    • Alan
    • Yehuda
    ', 'an inverse wrapper is passed in as a new context'); - shouldCompileTo(string, [empty, { list: list }], '

    Nobody\'s here

    ', 'an inverse wrapper can be optionally called'); - shouldCompileTo(messageString, [rootMessage, { list: list }], '

    Nobody's here

    ', 'the context of an inverse is the parent of the block'); + shouldCompileTo( + string, + [hash, { list: list }], + '
    • Alan
    • Yehuda
    ', + 'an inverse wrapper is passed in as a new context' + ); + shouldCompileTo( + string, + [empty, { list: list }], + "

    Nobody's here

    ", + 'an inverse wrapper can be optionally called' + ); + shouldCompileTo( + messageString, + [rootMessage, { list: list }], + '

    Nobody's here

    ', + 'the context of an inverse is the parent of the block' + ); }); it('pathed lambas with parameters', function() { @@ -231,40 +394,97 @@ describe('helpers', function() { describe('helpers hash', function() { it('providing a helpers hash', function() { - shouldCompileTo('Goodbye {{cruel}} {{world}}!', [{cruel: 'cruel'}, {world: function() { return 'world'; }}], 'Goodbye cruel world!', - 'helpers hash is available'); + shouldCompileTo( + 'Goodbye {{cruel}} {{world}}!', + [ + { cruel: 'cruel' }, + { + world: function() { + return 'world'; + } + } + ], + 'Goodbye cruel world!', + 'helpers hash is available' + ); - shouldCompileTo('Goodbye {{#iter}}{{cruel}} {{world}}{{/iter}}!', [{iter: [{cruel: 'cruel'}]}, {world: function() { return 'world'; }}], - 'Goodbye cruel world!', 'helpers hash is available inside other blocks'); + shouldCompileTo( + 'Goodbye {{#iter}}{{cruel}} {{world}}{{/iter}}!', + [ + { iter: [{ cruel: 'cruel' }] }, + { + world: function() { + return 'world'; + } + } + ], + 'Goodbye cruel world!', + 'helpers hash is available inside other blocks' + ); }); it('in cases of conflict, helpers win', function() { - shouldCompileTo('{{{lookup}}}', [{lookup: 'Explicit'}, {lookup: function() { return 'helpers'; }}], 'helpers', - 'helpers hash has precedence escaped expansion'); - shouldCompileTo('{{lookup}}', [{lookup: 'Explicit'}, {lookup: function() { return 'helpers'; }}], 'helpers', - 'helpers hash has precedence simple expansion'); + shouldCompileTo( + '{{{lookup}}}', + [ + { lookup: 'Explicit' }, + { + lookup: function() { + return 'helpers'; + } + } + ], + 'helpers', + 'helpers hash has precedence escaped expansion' + ); + shouldCompileTo( + '{{lookup}}', + [ + { lookup: 'Explicit' }, + { + lookup: function() { + return 'helpers'; + } + } + ], + 'helpers', + 'helpers hash has precedence simple expansion' + ); }); it('the helpers hash is available is nested contexts', function() { shouldCompileTo( '{{#outer}}{{#inner}}{{helper}}{{/inner}}{{/outer}}', [ - {'outer': {'inner': {'unused': []}}}, - {'helper': function() { return 'helper'; }} + { outer: { inner: { unused: [] } } }, + { + helper: function() { + return 'helper'; + } + } ], 'helper', - 'helpers hash is available in nested contexts.'); + 'helpers hash is available in nested contexts.' + ); }); it('the helper hash should augment the global hash', function() { - handlebarsEnv.registerHelper('test_helper', function() { return 'found it!'; }); + handlebarsEnv.registerHelper('test_helper', function() { + return 'found it!'; + }); shouldCompileTo( - '{{test_helper}} {{#if cruel}}Goodbye {{cruel}} {{world}}!{{/if}}', [ - {cruel: 'cruel'}, - {world: function() { return 'world!'; }} + '{{test_helper}} {{#if cruel}}Goodbye {{cruel}} {{world}}!{{/if}}', + [ + { cruel: 'cruel' }, + { + world: function() { + return 'world!'; + } + } ], - 'found it! Goodbye cruel world!!'); + 'found it! Goodbye cruel world!!' + ); }); }); @@ -284,55 +504,106 @@ describe('helpers', function() { handlebarsEnv.helpers = {}; handlebarsEnv.registerHelper({ - 'if': helpers['if'], - world: function() { return 'world!'; }, - testHelper: function() { return 'found it!'; } + if: helpers['if'], + world: function() { + return 'world!'; + }, + testHelper: function() { + return 'found it!'; + } }); shouldCompileTo( '{{testHelper}} {{#if cruel}}Goodbye {{cruel}} {{world}}!{{/if}}', - [{cruel: 'cruel'}], - 'found it! Goodbye cruel world!!'); + [{ cruel: 'cruel' }], + 'found it! Goodbye cruel world!!' + ); }); it('fails with multiple and args', function() { - shouldThrow(function() { - handlebarsEnv.registerHelper({ - world: function() { return 'world!'; }, - testHelper: function() { return 'found it!'; } - }, {}); - }, Error, 'Arg not supported with multiple helpers'); + shouldThrow( + function() { + handlebarsEnv.registerHelper( + { + world: function() { + return 'world!'; + }, + testHelper: function() { + return 'found it!'; + } + }, + {} + ); + }, + Error, + 'Arg not supported with multiple helpers' + ); }); }); it('decimal number literals work', function() { var string = 'Message: {{hello -1.2 1.2}}'; - var helpers = {hello: function(times, times2) { - if (typeof times !== 'number') { times = 'NaN'; } - if (typeof times2 !== 'number') { times2 = 'NaN'; } - return 'Hello ' + times + ' ' + times2 + ' times'; - }}; - shouldCompileTo(string, [{}, helpers], 'Message: Hello -1.2 1.2 times', 'template with a negative integer literal'); + var helpers = { + hello: function(times, times2) { + if (typeof times !== 'number') { + times = 'NaN'; + } + if (typeof times2 !== 'number') { + times2 = 'NaN'; + } + return 'Hello ' + times + ' ' + times2 + ' times'; + } + }; + shouldCompileTo( + string, + [{}, helpers], + 'Message: Hello -1.2 1.2 times', + 'template with a negative integer literal' + ); }); it('negative number literals work', function() { var string = 'Message: {{hello -12}}'; - var helpers = {hello: function(times) { - if (typeof times !== 'number') { times = 'NaN'; } - return 'Hello ' + times + ' times'; - }}; - shouldCompileTo(string, [{}, helpers], 'Message: Hello -12 times', 'template with a negative integer literal'); + var helpers = { + hello: function(times) { + if (typeof times !== 'number') { + times = 'NaN'; + } + return 'Hello ' + times + ' times'; + } + }; + shouldCompileTo( + string, + [{}, helpers], + 'Message: Hello -12 times', + 'template with a negative integer literal' + ); }); describe('String literal parameters', function() { it('simple literals work', function() { var string = 'Message: {{hello "world" 12 true false}}'; - var helpers = {hello: function(param, times, bool1, bool2) { - if (typeof times !== 'number') { times = 'NaN'; } - if (typeof bool1 !== 'boolean') { bool1 = 'NaB'; } - if (typeof bool2 !== 'boolean') { bool2 = 'NaB'; } - return 'Hello ' + param + ' ' + times + ' times: ' + bool1 + ' ' + bool2; - }}; - shouldCompileTo(string, [{}, helpers], 'Message: Hello world 12 times: true false', 'template with a simple String literal'); + var helpers = { + hello: function(param, times, bool1, bool2) { + if (typeof times !== 'number') { + times = 'NaN'; + } + if (typeof bool1 !== 'boolean') { + bool1 = 'NaB'; + } + if (typeof bool2 !== 'boolean') { + bool2 = 'NaB'; + } + return ( + 'Hello ' + param + ' ' + times + ' times: ' + bool1 + ' ' + bool2 + ); + } + }; + shouldCompileTo( + string, + [{}, helpers], + 'Message: Hello world 12 times: true false', + 'template with a simple String literal' + ); }); it('using a quote in the middle of a parameter raises an error', function() { @@ -344,57 +615,111 @@ describe('helpers', function() { it('escaping a String is possible', function() { var string = 'Message: {{{hello "\\"world\\""}}}'; - var helpers = {hello: function(param) { return 'Hello ' + param; }}; - shouldCompileTo(string, [{}, helpers], 'Message: Hello "world"', 'template with an escaped String literal'); + var helpers = { + hello: function(param) { + return 'Hello ' + param; + } + }; + shouldCompileTo( + string, + [{}, helpers], + 'Message: Hello "world"', + 'template with an escaped String literal' + ); }); it("it works with ' marks", function() { var string = 'Message: {{{hello "Alan\'s world"}}}'; - var helpers = {hello: function(param) { return 'Hello ' + param; }}; - shouldCompileTo(string, [{}, helpers], "Message: Hello Alan's world", "template with a ' mark"); + var helpers = { + hello: function(param) { + return 'Hello ' + param; + } + }; + shouldCompileTo( + string, + [{}, helpers], + "Message: Hello Alan's world", + "template with a ' mark" + ); }); }); it('negative number literals work', function() { var string = 'Message: {{hello -12}}'; - var helpers = {hello: function(times) { - if (typeof times !== 'number') { times = 'NaN'; } - return 'Hello ' + times + ' times'; - }}; - shouldCompileTo(string, [{}, helpers], 'Message: Hello -12 times', 'template with a negative integer literal'); + var helpers = { + hello: function(times) { + if (typeof times !== 'number') { + times = 'NaN'; + } + return 'Hello ' + times + ' times'; + } + }; + shouldCompileTo( + string, + [{}, helpers], + 'Message: Hello -12 times', + 'template with a negative integer literal' + ); }); describe('multiple parameters', function() { it('simple multi-params work', function() { var string = 'Message: {{goodbye cruel world}}'; - var hash = {cruel: 'cruel', world: 'world'}; - var helpers = {goodbye: function(cruel, world) { return 'Goodbye ' + cruel + ' ' + world; }}; - shouldCompileTo(string, [hash, helpers], 'Message: Goodbye cruel world', 'regular helpers with multiple params'); + var hash = { cruel: 'cruel', world: 'world' }; + var helpers = { + goodbye: function(cruel, world) { + return 'Goodbye ' + cruel + ' ' + world; + } + }; + shouldCompileTo( + string, + [hash, helpers], + 'Message: Goodbye cruel world', + 'regular helpers with multiple params' + ); }); it('block multi-params work', function() { - var string = 'Message: {{#goodbye cruel world}}{{greeting}} {{adj}} {{noun}}{{/goodbye}}'; - var hash = {cruel: 'cruel', world: 'world'}; - var helpers = {goodbye: function(cruel, world, options) { - return options.fn({greeting: 'Goodbye', adj: cruel, noun: world}); - }}; - shouldCompileTo(string, [hash, helpers], 'Message: Goodbye cruel world', 'block helpers with multiple params'); + var string = + 'Message: {{#goodbye cruel world}}{{greeting}} {{adj}} {{noun}}{{/goodbye}}'; + var hash = { cruel: 'cruel', world: 'world' }; + var helpers = { + goodbye: function(cruel, world, options) { + return options.fn({ greeting: 'Goodbye', adj: cruel, noun: world }); + } + }; + shouldCompileTo( + string, + [hash, helpers], + 'Message: Goodbye cruel world', + 'block helpers with multiple params' + ); }); }); describe('hash', function() { it('helpers can take an optional hash', function() { - var template = CompilerContext.compile('{{goodbye cruel="CRUEL" world="WORLD" times=12}}'); + var template = CompilerContext.compile( + '{{goodbye cruel="CRUEL" world="WORLD" times=12}}' + ); var helpers = { goodbye: function(options) { - return 'GOODBYE ' + options.hash.cruel + ' ' + options.hash.world + ' ' + options.hash.times + ' TIMES'; + return ( + 'GOODBYE ' + + options.hash.cruel + + ' ' + + options.hash.world + + ' ' + + options.hash.times + + ' TIMES' + ); } }; var context = {}; - var result = template(context, {helpers: helpers}); + var result = template(context, { helpers: helpers }); equals(result, 'GOODBYE CRUEL WORLD 12 TIMES', 'Helper output hash'); }); @@ -413,38 +738,62 @@ describe('helpers', function() { var context = {}; - var template = CompilerContext.compile('{{goodbye cruel="CRUEL" world="WORLD" print=true}}'); - var result = template(context, {helpers: helpers}); + var template = CompilerContext.compile( + '{{goodbye cruel="CRUEL" world="WORLD" print=true}}' + ); + var result = template(context, { helpers: helpers }); equals(result, 'GOODBYE CRUEL WORLD', 'Helper output hash'); - template = CompilerContext.compile('{{goodbye cruel="CRUEL" world="WORLD" print=false}}'); - result = template(context, {helpers: helpers}); + template = CompilerContext.compile( + '{{goodbye cruel="CRUEL" world="WORLD" print=false}}' + ); + result = template(context, { helpers: helpers }); equals(result, 'NOT PRINTING', 'Boolean helper parameter honored'); }); it('block helpers can take an optional hash', function() { - var template = CompilerContext.compile('{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}'); + var template = CompilerContext.compile( + '{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}' + ); var helpers = { goodbye: function(options) { - return 'GOODBYE ' + options.hash.cruel + ' ' + options.fn(this) + ' ' + options.hash.times + ' TIMES'; + return ( + 'GOODBYE ' + + options.hash.cruel + + ' ' + + options.fn(this) + + ' ' + + options.hash.times + + ' TIMES' + ); } }; - var result = template({}, {helpers: helpers}); + var result = template({}, { helpers: helpers }); equals(result, 'GOODBYE CRUEL world 12 TIMES', 'Hash parameters output'); }); it('block helpers can take an optional hash with single quoted stings', function() { - var template = CompilerContext.compile('{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}'); + var template = CompilerContext.compile( + '{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}' + ); var helpers = { goodbye: function(options) { - return 'GOODBYE ' + options.hash.cruel + ' ' + options.fn(this) + ' ' + options.hash.times + ' TIMES'; + return ( + 'GOODBYE ' + + options.hash.cruel + + ' ' + + options.fn(this) + + ' ' + + options.hash.times + + ' TIMES' + ); } }; - var result = template({}, {helpers: helpers}); + var result = template({}, { helpers: helpers }); equals(result, 'GOODBYE CRUEL world 12 TIMES', 'Hash parameters output'); }); @@ -461,22 +810,30 @@ describe('helpers', function() { } }; - var template = CompilerContext.compile('{{#goodbye cruel="CRUEL" print=true}}world{{/goodbye}}'); - var result = template({}, {helpers: helpers}); + var template = CompilerContext.compile( + '{{#goodbye cruel="CRUEL" print=true}}world{{/goodbye}}' + ); + var result = template({}, { helpers: helpers }); equals(result, 'GOODBYE CRUEL world', 'Boolean hash parameter honored'); - template = CompilerContext.compile('{{#goodbye cruel="CRUEL" print=false}}world{{/goodbye}}'); - result = template({}, {helpers: helpers}); + template = CompilerContext.compile( + '{{#goodbye cruel="CRUEL" print=false}}world{{/goodbye}}' + ); + result = template({}, { helpers: helpers }); equals(result, 'NOT PRINTING', 'Boolean hash parameter honored'); }); }); describe('helperMissing', function() { it('if a context is not found, helperMissing is used', function() { - shouldThrow(function() { + shouldThrow( + function() { var template = CompilerContext.compile('{{hello}} {{link_to world}}'); template({}); - }, undefined, /Missing helper: "link_to"/); + }, + undefined, + /Missing helper: "link_to"/ + ); }); it('if a context is not found, custom helperMissing is used', function() { @@ -512,50 +869,91 @@ describe('helpers', function() { describe('knownHelpers', function() { it('Known helper should render helper', function() { - var template = CompilerContext.compile('{{hello}}', {knownHelpers: {hello: true}}); + var template = CompilerContext.compile('{{hello}}', { + knownHelpers: { hello: true } + }); - var result = template({}, {helpers: {hello: function() { return 'foo'; }}}); + var result = template( + {}, + { + helpers: { + hello: function() { + return 'foo'; + } + } + } + ); equal(result, 'foo', "'foo' should === '" + result); }); it('Unknown helper in knownHelpers only mode should be passed as undefined', function() { - var template = CompilerContext.compile('{{typeof hello}}', {knownHelpers: {'typeof': true}, knownHelpersOnly: true}); + var template = CompilerContext.compile('{{typeof hello}}', { + knownHelpers: { typeof: true }, + knownHelpersOnly: true + }); - var result = template({}, {helpers: {'typeof': function(arg) { return typeof arg; }, hello: function() { return 'foo'; }}}); + var result = template( + {}, + { + helpers: { + typeof: function(arg) { + return typeof arg; + }, + hello: function() { + return 'foo'; + } + } + } + ); equal(result, 'undefined', "'undefined' should === '" + result); }); it('Builtin helpers available in knownHelpers only mode', function() { - var template = CompilerContext.compile('{{#unless foo}}bar{{/unless}}', {knownHelpersOnly: true}); + var template = CompilerContext.compile('{{#unless foo}}bar{{/unless}}', { + knownHelpersOnly: true + }); var result = template({}); equal(result, 'bar', "'bar' should === '" + result); }); it('Field lookup works in knownHelpers only mode', function() { - var template = CompilerContext.compile('{{foo}}', {knownHelpersOnly: true}); + var template = CompilerContext.compile('{{foo}}', { + knownHelpersOnly: true + }); - var result = template({foo: 'bar'}); + var result = template({ foo: 'bar' }); equal(result, 'bar', "'bar' should === '" + result); }); it('Conditional blocks work in knownHelpers only mode', function() { - var template = CompilerContext.compile('{{#foo}}bar{{/foo}}', {knownHelpersOnly: true}); + var template = CompilerContext.compile('{{#foo}}bar{{/foo}}', { + knownHelpersOnly: true + }); - var result = template({foo: 'baz'}); + var result = template({ foo: 'baz' }); equal(result, 'bar', "'bar' should === '" + result); }); it('Invert blocks work in knownHelpers only mode', function() { - var template = CompilerContext.compile('{{^foo}}bar{{/foo}}', {knownHelpersOnly: true}); + var template = CompilerContext.compile('{{^foo}}bar{{/foo}}', { + knownHelpersOnly: true + }); - var result = template({foo: false}); + var result = template({ foo: false }); equal(result, 'bar', "'bar' should === '" + result); }); it('Functions are bound to the context in knownHelpers only mode', function() { - var template = CompilerContext.compile('{{foo}}', {knownHelpersOnly: true}); - var result = template({foo: function() { return this.bar; }, bar: 'bar'}); + var template = CompilerContext.compile('{{foo}}', { + knownHelpersOnly: true + }); + var result = template({ + foo: function() { + return this.bar; + }, + bar: 'bar' + }); equal(result, 'bar', "'bar' should === '" + result); }); it('Unknown helper call in knownHelpers only mode should throw', function() { shouldThrow(function() { - CompilerContext.compile('{{typeof hello}}', {knownHelpersOnly: true}); + CompilerContext.compile('{{typeof hello}}', { knownHelpersOnly: true }); }, Error); }); }); @@ -563,12 +961,23 @@ describe('helpers', function() { describe('blockHelperMissing', function() { it('lambdas are resolved by blockHelperMissing, not handlebars proper', function() { var string = '{{#truthy}}yep{{/truthy}}'; - var data = { truthy: function() { return true; } }; + var data = { + truthy: function() { + return true; + } + }; shouldCompileTo(string, data, 'yep'); }); it('lambdas resolved by blockHelperMissing are bound to the context', function() { var string = '{{#truthy}}yep{{/truthy}}'; - var boundData = { truthy: function() { return this.truthiness(); }, truthiness: function() { return false; } }; + var boundData = { + truthy: function() { + return this.truthiness(); + }, + truthiness: function() { + return false; + } + }; shouldCompileTo(string, boundData, ''); }); }); @@ -594,26 +1003,49 @@ describe('helpers', function() { shouldCompileTo('{{helper 1}}', [context, helpers], 'ran: helper'); }); it('should include in ambiguous block calls', function() { - shouldCompileTo('{{#helper}}{{/helper}}', [context, helpers], 'ran: helper'); + shouldCompileTo( + '{{#helper}}{{/helper}}', + [context, helpers], + 'ran: helper' + ); }); it('should include in simple block calls', function() { - shouldCompileTo('{{#./helper}}{{/./helper}}', [context, helpers], 'missing: ./helper'); + shouldCompileTo( + '{{#./helper}}{{/./helper}}', + [context, helpers], + 'missing: ./helper' + ); }); it('should include in helper block calls', function() { - shouldCompileTo('{{#helper 1}}{{/helper}}', [context, helpers], 'ran: helper'); + shouldCompileTo( + '{{#helper 1}}{{/helper}}', + [context, helpers], + 'ran: helper' + ); }); it('should include in known helper calls', function() { - var template = CompilerContext.compile('{{helper}}', {knownHelpers: {'helper': true}, knownHelpersOnly: true}); + var template = CompilerContext.compile('{{helper}}', { + knownHelpers: { helper: true }, + knownHelpersOnly: true + }); - equal(template({}, {helpers: helpers}), 'ran: helper'); + equal(template({}, { helpers: helpers }), 'ran: helper'); }); it('should include full id', function() { - shouldCompileTo('{{#foo.helper}}{{/foo.helper}}', [{foo: {}}, helpers], 'missing: foo.helper'); + shouldCompileTo( + '{{#foo.helper}}{{/foo.helper}}', + [{ foo: {} }, helpers], + 'missing: foo.helper' + ); }); it('should include full id if a hash is passed', function() { - shouldCompileTo('{{#foo.helper bar=baz}}{{/foo.helper}}', [{foo: {}}, helpers], 'helper missing: foo.helper'); + shouldCompileTo( + '{{#foo.helper bar=baz}}{{/foo.helper}}', + [{ foo: {} }, helpers], + 'helper missing: foo.helper' + ); }); }); @@ -636,12 +1068,14 @@ describe('helpers', function() { world: 'world' }; - var result = template(context, {helpers: helpers}); + var result = template(context, { helpers: helpers }); equals(result, 'GOODBYE cruel WORLD', 'Helper executed'); }); it('helpers take precedence over same-named context properties$', function() { - var template = CompilerContext.compile('{{#goodbye}} {{cruel world}}{{/goodbye}}'); + var template = CompilerContext.compile( + '{{#goodbye}} {{cruel world}}{{/goodbye}}' + ); var helpers = { goodbye: function(options) { @@ -658,12 +1092,14 @@ describe('helpers', function() { world: 'world' }; - var result = template(context, {helpers: helpers}); + var result = template(context, { helpers: helpers }); equals(result, 'GOODBYE cruel WORLD', 'Helper executed'); }); it('Scoped names take precedence over helpers', function() { - var template = CompilerContext.compile('{{this.goodbye}} {{cruel world}} {{cruel this.goodbye}}'); + var template = CompilerContext.compile( + '{{this.goodbye}} {{cruel world}} {{cruel this.goodbye}}' + ); var helpers = { goodbye: function() { @@ -680,12 +1116,18 @@ describe('helpers', function() { world: 'world' }; - var result = template(context, {helpers: helpers}); - equals(result, 'goodbye cruel WORLD cruel GOODBYE', 'Helper not executed'); + var result = template(context, { helpers: helpers }); + equals( + result, + 'goodbye cruel WORLD cruel GOODBYE', + 'Helper not executed' + ); }); it('Scoped names take precedence over block helpers', function() { - var template = CompilerContext.compile('{{#goodbye}} {{cruel world}}{{/goodbye}} {{this.goodbye}}'); + var template = CompilerContext.compile( + '{{#goodbye}} {{cruel world}}{{/goodbye}} {{this.goodbye}}' + ); var helpers = { goodbye: function(options) { @@ -702,21 +1144,25 @@ describe('helpers', function() { world: 'world' }; - var result = template(context, {helpers: helpers}); + var result = template(context, { helpers: helpers }); equals(result, 'GOODBYE cruel WORLD goodbye', 'Helper executed'); }); }); describe('block params', function() { it('should take presedence over context values', function() { - var hash = {value: 'foo'}; + var hash = { value: 'foo' }; var helpers = { goodbyes: function(options) { equals(options.fn.blockParams, 1); - return options.fn({value: 'bar'}, {blockParams: [1, 2]}); + return options.fn({ value: 'bar' }, { blockParams: [1, 2] }); } }; - shouldCompileTo('{{#goodbyes as |value|}}{{value}}{{/goodbyes}}{{value}}', [hash, helpers], '1foo'); + shouldCompileTo( + '{{#goodbyes as |value|}}{{value}}{{/goodbyes}}{{value}}', + [hash, helpers], + '1foo' + ); }); it('should take presedence over helper values', function() { var hash = {}; @@ -726,110 +1172,160 @@ describe('helpers', function() { }, goodbyes: function(options) { equals(options.fn.blockParams, 1); - return options.fn({}, {blockParams: [1, 2]}); + return options.fn({}, { blockParams: [1, 2] }); } }; - shouldCompileTo('{{#goodbyes as |value|}}{{value}}{{/goodbyes}}{{value}}', [hash, helpers], '1foo'); + shouldCompileTo( + '{{#goodbyes as |value|}}{{value}}{{/goodbyes}}{{value}}', + [hash, helpers], + '1foo' + ); }); it('should not take presedence over pathed values', function() { - var hash = {value: 'bar'}; + var hash = { value: 'bar' }; var helpers = { value: function() { return 'foo'; }, goodbyes: function(options) { equals(options.fn.blockParams, 1); - return options.fn(this, {blockParams: [1, 2]}); + return options.fn(this, { blockParams: [1, 2] }); } }; - shouldCompileTo('{{#goodbyes as |value|}}{{./value}}{{/goodbyes}}{{value}}', [hash, helpers], 'barfoo'); + shouldCompileTo( + '{{#goodbyes as |value|}}{{./value}}{{/goodbyes}}{{value}}', + [hash, helpers], + 'barfoo' + ); }); it('should take presednece over parent block params', function() { - var hash = {value: 'foo'}, - value = 1; + var hash = { value: 'foo' }, + value = 1; var helpers = { goodbyes: function(options) { - return options.fn({value: 'bar'}, {blockParams: options.fn.blockParams === 1 ? [value++, value++] : undefined}); + return options.fn( + { value: 'bar' }, + { + blockParams: + options.fn.blockParams === 1 ? [value++, value++] : undefined + } + ); } }; - shouldCompileTo('{{#goodbyes as |value|}}{{#goodbyes}}{{value}}{{#goodbyes as |value|}}{{value}}{{/goodbyes}}{{/goodbyes}}{{/goodbyes}}{{value}}', [hash, helpers], '13foo'); + shouldCompileTo( + '{{#goodbyes as |value|}}{{#goodbyes}}{{value}}{{#goodbyes as |value|}}{{value}}{{/goodbyes}}{{/goodbyes}}{{/goodbyes}}{{value}}', + [hash, helpers], + '13foo' + ); }); it('should allow block params on chained helpers', function() { - var hash = {value: 'foo'}; + var hash = { value: 'foo' }; var helpers = { goodbyes: function(options) { equals(options.fn.blockParams, 1); - return options.fn({value: 'bar'}, {blockParams: [1, 2]}); + return options.fn({ value: 'bar' }, { blockParams: [1, 2] }); } }; - shouldCompileTo('{{#if bar}}{{else goodbyes as |value|}}{{value}}{{/if}}{{value}}', [hash, helpers], '1foo'); + shouldCompileTo( + '{{#if bar}}{{else goodbyes as |value|}}{{value}}{{/if}}{{value}}', + [hash, helpers], + '1foo' + ); }); }); describe('built-in helpers malformed arguments ', function() { it('if helper - too few arguments', function() { var template = CompilerContext.compile('{{#if}}{{/if}}'); - shouldThrow(function() { - - template({}); - }, undefined, /#if requires exactly one argument/); + shouldThrow( + function() { + template({}); + }, + undefined, + /#if requires exactly one argument/ + ); }); it('if helper - too many arguments, string', function() { var template = CompilerContext.compile('{{#if test "string"}}{{/if}}'); - shouldThrow(function() { - - template({}); - }, undefined, /#if requires exactly one argument/); + shouldThrow( + function() { + template({}); + }, + undefined, + /#if requires exactly one argument/ + ); }); it('if helper - too many arguments, undefined', function() { var template = CompilerContext.compile('{{#if test undefined}}{{/if}}'); - shouldThrow(function() { - - template({}); - }, undefined, /#if requires exactly one argument/); + shouldThrow( + function() { + template({}); + }, + undefined, + /#if requires exactly one argument/ + ); }); it('if helper - too many arguments, null', function() { var template = CompilerContext.compile('{{#if test null}}{{/if}}'); - shouldThrow(function() { - - template({}); - }, undefined, /#if requires exactly one argument/); + shouldThrow( + function() { + template({}); + }, + undefined, + /#if requires exactly one argument/ + ); }); it('unless helper - too few arguments', function() { var template = CompilerContext.compile('{{#unless}}{{/unless}}'); - shouldThrow(function() { - - template({}); - }, undefined, /#unless requires exactly one argument/); + shouldThrow( + function() { + template({}); + }, + undefined, + /#unless requires exactly one argument/ + ); }); it('unless helper - too many arguments', function() { - var template = CompilerContext.compile('{{#unless test null}}{{/unless}}'); - shouldThrow(function() { - - template({}); - }, undefined, /#unless requires exactly one argument/); + var template = CompilerContext.compile( + '{{#unless test null}}{{/unless}}' + ); + shouldThrow( + function() { + template({}); + }, + undefined, + /#unless requires exactly one argument/ + ); }); it('with helper - too few arguments', function() { var template = CompilerContext.compile('{{#with}}{{/with}}'); - shouldThrow(function() { - - template({}); - }, undefined, /#with requires exactly one argument/); + shouldThrow( + function() { + template({}); + }, + undefined, + /#with requires exactly one argument/ + ); }); it('with helper - too many arguments', function() { - var template = CompilerContext.compile('{{#with test "string"}}{{/with}}'); - shouldThrow(function() { - - template({}); - }, undefined, /#with requires exactly one argument/); + var template = CompilerContext.compile( + '{{#with test "string"}}{{/with}}' + ); + shouldThrow( + function() { + template({}); + }, + undefined, + /#with requires exactly one argument/ + ); }); }); }); diff --git a/spec/javascript-compiler.js b/spec/javascript-compiler.js index 5a10388f0..21b8e2a0a 100644 --- a/spec/javascript-compiler.js +++ b/spec/javascript-compiler.js @@ -13,7 +13,10 @@ describe('javascript-compiler api', function() { }); it('should allow override', function() { - handlebarsEnv.JavaScriptCompiler.prototype.nameLookup = function(parent, name) { + handlebarsEnv.JavaScriptCompiler.prototype.nameLookup = function( + parent, + name + ) { return parent + '.bar_' + name; }; /* eslint-disable camelcase */ @@ -43,7 +46,7 @@ describe('javascript-compiler api', function() { }; handlebarsEnv.VM.checkRevision = function(compilerInfo) { if (compilerInfo !== 'crazy') { - throw new Error('It didn\'t work'); + throw new Error("It didn't work"); } }; shouldCompileTo('{{foo}} ', { foo: 'food' }, 'food '); @@ -54,7 +57,8 @@ describe('javascript-compiler api', function() { beforeEach(function() { handlebarsEnv.JavaScriptCompiler.prototype.forceBuffer = true; $superAppend = handlebarsEnv.JavaScriptCompiler.prototype.appendToBuffer; - $superCreate = handlebarsEnv.JavaScriptCompiler.prototype.initializeBuffer; + $superCreate = + handlebarsEnv.JavaScriptCompiler.prototype.initializeBuffer; }); afterEach(function() { handlebarsEnv.JavaScriptCompiler.prototype.forceBuffer = false; @@ -69,7 +73,9 @@ describe('javascript-compiler api', function() { shouldCompileTo('{{foo}} ', { foo: 'food' }, 'foo_food '); }); it('should allow append buffer override', function() { - handlebarsEnv.JavaScriptCompiler.prototype.appendToBuffer = function(string) { + handlebarsEnv.JavaScriptCompiler.prototype.appendToBuffer = function( + string + ) { return $superAppend.call(this, [string, ' + "_foo"']); }; shouldCompileTo('{{foo}}', { foo: 'food' }, 'food_foo'); diff --git a/spec/parser.js b/spec/parser.js index 856031c3b..2aa7e39ff 100644 --- a/spec/parser.js +++ b/spec/parser.js @@ -68,7 +68,10 @@ describe('parser', function() { equals(astFor('{{null}}'), '{{ NULL [] }}\n'); }); it('parses mustaches with undefined and null parameters', function() { - equals(astFor('{{foo undefined null}}'), '{{ PATH:foo [UNDEFINED, NULL] }}\n'); + equals( + astFor('{{foo undefined null}}'), + '{{ PATH:foo [UNDEFINED, NULL] }}\n' + ); }); it('parses mustaches with DATA parameters', function() { @@ -78,23 +81,53 @@ describe('parser', function() { it('parses mustaches with hash arguments', function() { equals(astFor('{{foo bar=baz}}'), '{{ PATH:foo [] HASH{bar=PATH:baz} }}\n'); equals(astFor('{{foo bar=1}}'), '{{ PATH:foo [] HASH{bar=NUMBER{1}} }}\n'); - equals(astFor('{{foo bar=true}}'), '{{ PATH:foo [] HASH{bar=BOOLEAN{true}} }}\n'); - equals(astFor('{{foo bar=false}}'), '{{ PATH:foo [] HASH{bar=BOOLEAN{false}} }}\n'); - equals(astFor('{{foo bar=@baz}}'), '{{ PATH:foo [] HASH{bar=@PATH:baz} }}\n'); - - equals(astFor('{{foo bar=baz bat=bam}}'), '{{ PATH:foo [] HASH{bar=PATH:baz, bat=PATH:bam} }}\n'); - equals(astFor('{{foo bar=baz bat="bam"}}'), '{{ PATH:foo [] HASH{bar=PATH:baz, bat="bam"} }}\n'); + equals( + astFor('{{foo bar=true}}'), + '{{ PATH:foo [] HASH{bar=BOOLEAN{true}} }}\n' + ); + equals( + astFor('{{foo bar=false}}'), + '{{ PATH:foo [] HASH{bar=BOOLEAN{false}} }}\n' + ); + equals( + astFor('{{foo bar=@baz}}'), + '{{ PATH:foo [] HASH{bar=@PATH:baz} }}\n' + ); + + equals( + astFor('{{foo bar=baz bat=bam}}'), + '{{ PATH:foo [] HASH{bar=PATH:baz, bat=PATH:bam} }}\n' + ); + equals( + astFor('{{foo bar=baz bat="bam"}}'), + '{{ PATH:foo [] HASH{bar=PATH:baz, bat="bam"} }}\n' + ); equals(astFor("{{foo bat='bam'}}"), '{{ PATH:foo [] HASH{bat="bam"} }}\n'); - equals(astFor('{{foo omg bar=baz bat="bam"}}'), '{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam"} }}\n'); - equals(astFor('{{foo omg bar=baz bat="bam" baz=1}}'), '{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam", baz=NUMBER{1}} }}\n'); - equals(astFor('{{foo omg bar=baz bat="bam" baz=true}}'), '{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam", baz=BOOLEAN{true}} }}\n'); - equals(astFor('{{foo omg bar=baz bat="bam" baz=false}}'), '{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam", baz=BOOLEAN{false}} }}\n'); + equals( + astFor('{{foo omg bar=baz bat="bam"}}'), + '{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam"} }}\n' + ); + equals( + astFor('{{foo omg bar=baz bat="bam" baz=1}}'), + '{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam", baz=NUMBER{1}} }}\n' + ); + equals( + astFor('{{foo omg bar=baz bat="bam" baz=true}}'), + '{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam", baz=BOOLEAN{true}} }}\n' + ); + equals( + astFor('{{foo omg bar=baz bat="bam" baz=false}}'), + '{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam", baz=BOOLEAN{false}} }}\n' + ); }); it('parses contents followed by a mustache', function() { - equals(astFor('foo bar {{baz}}'), 'CONTENT[ \'foo bar \' ]\n{{ PATH:baz [] }}\n'); + equals( + astFor('foo bar {{baz}}'), + "CONTENT[ 'foo bar ' ]\n{{ PATH:baz [] }}\n" + ); }); it('parses a partial', function() { @@ -108,47 +141,81 @@ describe('parser', function() { }); it('parses a partial with hash', function() { - equals(astFor('{{> foo bar=bat}}'), '{{> PARTIAL:foo HASH{bar=PATH:bat} }}\n'); + equals( + astFor('{{> foo bar=bat}}'), + '{{> PARTIAL:foo HASH{bar=PATH:bat} }}\n' + ); }); it('parses a partial with context and hash', function() { - equals(astFor('{{> foo bar bat=baz}}'), '{{> PARTIAL:foo PATH:bar HASH{bat=PATH:baz} }}\n'); + equals( + astFor('{{> foo bar bat=baz}}'), + '{{> PARTIAL:foo PATH:bar HASH{bat=PATH:baz} }}\n' + ); }); it('parses a partial with a complex name', function() { - equals(astFor('{{> shared/partial?.bar}}'), '{{> PARTIAL:shared/partial?.bar }}\n'); + equals( + astFor('{{> shared/partial?.bar}}'), + '{{> PARTIAL:shared/partial?.bar }}\n' + ); }); it('parsers partial blocks', function() { - equals(astFor('{{#> foo}}bar{{/foo}}'), '{{> PARTIAL BLOCK:foo PROGRAM:\n CONTENT[ \'bar\' ]\n }}\n'); + equals( + astFor('{{#> foo}}bar{{/foo}}'), + "{{> PARTIAL BLOCK:foo PROGRAM:\n CONTENT[ 'bar' ]\n }}\n" + ); }); it('should handle parser block mismatch', function() { - shouldThrow(function() { - astFor('{{#> goodbyes}}{{/hellos}}'); - }, Error, (/goodbyes doesn't match hellos/)); + shouldThrow( + function() { + astFor('{{#> goodbyes}}{{/hellos}}'); + }, + Error, + /goodbyes doesn't match hellos/ + ); }); it('parsers partial blocks with arguments', function() { - equals(astFor('{{#> foo context hash=value}}bar{{/foo}}'), '{{> PARTIAL BLOCK:foo PATH:context HASH{hash=PATH:value} PROGRAM:\n CONTENT[ \'bar\' ]\n }}\n'); + equals( + astFor('{{#> foo context hash=value}}bar{{/foo}}'), + "{{> PARTIAL BLOCK:foo PATH:context HASH{hash=PATH:value} PROGRAM:\n CONTENT[ 'bar' ]\n }}\n" + ); }); it('parses a comment', function() { - equals(astFor('{{! this is a comment }}'), "{{! ' this is a comment ' }}\n"); + equals( + astFor('{{! this is a comment }}'), + "{{! ' this is a comment ' }}\n" + ); }); it('parses a multi-line comment', function() { - equals(astFor('{{!\nthis is a multi-line comment\n}}'), "{{! '\nthis is a multi-line comment\n' }}\n"); + equals( + astFor('{{!\nthis is a multi-line comment\n}}'), + "{{! '\nthis is a multi-line comment\n' }}\n" + ); }); it('parses an inverse section', function() { - equals(astFor('{{#foo}} bar {{^}} baz {{/foo}}'), "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n CONTENT[ ' baz ' ]\n"); + equals( + astFor('{{#foo}} bar {{^}} baz {{/foo}}'), + "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n CONTENT[ ' baz ' ]\n" + ); }); it('parses an inverse (else-style) section', function() { - equals(astFor('{{#foo}} bar {{else}} baz {{/foo}}'), "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n CONTENT[ ' baz ' ]\n"); + equals( + astFor('{{#foo}} bar {{else}} baz {{/foo}}'), + "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n CONTENT[ ' baz ' ]\n" + ); }); it('parses multiple inverse sections', function() { - equals(astFor('{{#foo}} bar {{else if bar}}{{else}} baz {{/foo}}'), "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n BLOCK:\n PATH:if [PATH:bar]\n PROGRAM:\n {{^}}\n CONTENT[ ' baz ' ]\n"); + equals( + astFor('{{#foo}} bar {{else if bar}}{{else}} baz {{/foo}}'), + "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n BLOCK:\n PATH:if [PATH:bar]\n PROGRAM:\n {{^}}\n CONTENT[ ' baz ' ]\n" + ); }); it('parses empty blocks', function() { @@ -156,31 +223,52 @@ describe('parser', function() { }); it('parses empty blocks with empty inverse section', function() { - equals(astFor('{{#foo}}{{^}}{{/foo}}'), 'BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n'); + equals( + astFor('{{#foo}}{{^}}{{/foo}}'), + 'BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n' + ); }); it('parses empty blocks with empty inverse (else-style) section', function() { - equals(astFor('{{#foo}}{{else}}{{/foo}}'), 'BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n'); + equals( + astFor('{{#foo}}{{else}}{{/foo}}'), + 'BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n' + ); }); it('parses non-empty blocks with empty inverse section', function() { - equals(astFor('{{#foo}} bar {{^}}{{/foo}}'), "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n"); + equals( + astFor('{{#foo}} bar {{^}}{{/foo}}'), + "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n" + ); }); it('parses non-empty blocks with empty inverse (else-style) section', function() { - equals(astFor('{{#foo}} bar {{else}}{{/foo}}'), "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n"); + equals( + astFor('{{#foo}} bar {{else}}{{/foo}}'), + "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n" + ); }); it('parses empty blocks with non-empty inverse section', function() { - equals(astFor('{{#foo}}{{^}} bar {{/foo}}'), "BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n CONTENT[ ' bar ' ]\n"); + equals( + astFor('{{#foo}}{{^}} bar {{/foo}}'), + "BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n CONTENT[ ' bar ' ]\n" + ); }); it('parses empty blocks with non-empty inverse (else-style) section', function() { - equals(astFor('{{#foo}}{{else}} bar {{/foo}}'), "BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n CONTENT[ ' bar ' ]\n"); + equals( + astFor('{{#foo}}{{else}} bar {{/foo}}'), + "BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n CONTENT[ ' bar ' ]\n" + ); }); it('parses a standalone inverse section', function() { - equals(astFor('{{^foo}}bar{{/foo}}'), "BLOCK:\n PATH:foo []\n {{^}}\n CONTENT[ 'bar' ]\n"); + equals( + astFor('{{^foo}}bar{{/foo}}'), + "BLOCK:\n PATH:foo []\n {{^}}\n CONTENT[ 'bar' ]\n" + ); }); it('throws on old inverse section', function() { shouldThrow(function() { @@ -189,105 +277,179 @@ describe('parser', function() { }); it('parses block with block params', function() { - equals(astFor('{{#foo as |bar baz|}}content{{/foo}}'), "BLOCK:\n PATH:foo []\n PROGRAM:\n BLOCK PARAMS: [ bar baz ]\n CONTENT[ 'content' ]\n"); + equals( + astFor('{{#foo as |bar baz|}}content{{/foo}}'), + "BLOCK:\n PATH:foo []\n PROGRAM:\n BLOCK PARAMS: [ bar baz ]\n CONTENT[ 'content' ]\n" + ); }); it('parses inverse block with block params', function() { - equals(astFor('{{^foo as |bar baz|}}content{{/foo}}'), "BLOCK:\n PATH:foo []\n {{^}}\n BLOCK PARAMS: [ bar baz ]\n CONTENT[ 'content' ]\n"); + equals( + astFor('{{^foo as |bar baz|}}content{{/foo}}'), + "BLOCK:\n PATH:foo []\n {{^}}\n BLOCK PARAMS: [ bar baz ]\n CONTENT[ 'content' ]\n" + ); }); it('parses chained inverse block with block params', function() { - equals(astFor('{{#foo}}{{else foo as |bar baz|}}content{{/foo}}'), "BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n BLOCK:\n PATH:foo []\n PROGRAM:\n BLOCK PARAMS: [ bar baz ]\n CONTENT[ 'content' ]\n"); - }); - it('raises if there\'s a Parse error', function() { - shouldThrow(function() { - astFor('foo{{^}}bar'); - }, Error, /Parse error on line 1/); - shouldThrow(function() { - astFor('{{foo}'); - }, Error, /Parse error on line 1/); - shouldThrow(function() { - astFor('{{foo &}}'); - }, Error, /Parse error on line 1/); - shouldThrow(function() { - astFor('{{#goodbyes}}{{/hellos}}'); - }, Error, /goodbyes doesn't match hellos/); - - shouldThrow(function() { - astFor('{{{{goodbyes}}}} {{{{/hellos}}}}'); - }, Error, /goodbyes doesn't match hellos/); + equals( + astFor('{{#foo}}{{else foo as |bar baz|}}content{{/foo}}'), + "BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n BLOCK:\n PATH:foo []\n PROGRAM:\n BLOCK PARAMS: [ bar baz ]\n CONTENT[ 'content' ]\n" + ); + }); + it("raises if there's a Parse error", function() { + shouldThrow( + function() { + astFor('foo{{^}}bar'); + }, + Error, + /Parse error on line 1/ + ); + shouldThrow( + function() { + astFor('{{foo}'); + }, + Error, + /Parse error on line 1/ + ); + shouldThrow( + function() { + astFor('{{foo &}}'); + }, + Error, + /Parse error on line 1/ + ); + shouldThrow( + function() { + astFor('{{#goodbyes}}{{/hellos}}'); + }, + Error, + /goodbyes doesn't match hellos/ + ); + + shouldThrow( + function() { + astFor('{{{{goodbyes}}}} {{{{/hellos}}}}'); + }, + Error, + /goodbyes doesn't match hellos/ + ); }); it('should handle invalid paths', function() { - shouldThrow(function() { - astFor('{{foo/../bar}}'); - }, Error, /Invalid path: foo\/\.\. - 1:2/); - shouldThrow(function() { - astFor('{{foo/./bar}}'); - }, Error, /Invalid path: foo\/\. - 1:2/); - shouldThrow(function() { - astFor('{{foo/this/bar}}'); - }, Error, /Invalid path: foo\/this - 1:2/); + shouldThrow( + function() { + astFor('{{foo/../bar}}'); + }, + Error, + /Invalid path: foo\/\.\. - 1:2/ + ); + shouldThrow( + function() { + astFor('{{foo/./bar}}'); + }, + Error, + /Invalid path: foo\/\. - 1:2/ + ); + shouldThrow( + function() { + astFor('{{foo/this/bar}}'); + }, + Error, + /Invalid path: foo\/this - 1:2/ + ); }); it('knows how to report the correct line number in errors', function() { - shouldThrow(function() { - astFor('hello\nmy\n{{foo}'); - }, Error, /Parse error on line 3/); - shouldThrow(function() { - astFor('hello\n\nmy\n\n{{foo}'); - }, Error, /Parse error on line 5/); + shouldThrow( + function() { + astFor('hello\nmy\n{{foo}'); + }, + Error, + /Parse error on line 3/ + ); + shouldThrow( + function() { + astFor('hello\n\nmy\n\n{{foo}'); + }, + Error, + /Parse error on line 5/ + ); }); it('knows how to report the correct line number in errors when the first character is a newline', function() { - shouldThrow(function() { - astFor('\n\nhello\n\nmy\n\n{{foo}'); - }, Error, /Parse error on line 7/); + shouldThrow( + function() { + astFor('\n\nhello\n\nmy\n\n{{foo}'); + }, + Error, + /Parse error on line 7/ + ); }); describe('externally compiled AST', function() { it('can pass through an already-compiled AST', function() { - equals(astFor({ - type: 'Program', - body: [ {type: 'ContentStatement', value: 'Hello'}] - }), 'CONTENT[ \'Hello\' ]\n'); + equals( + astFor({ + type: 'Program', + body: [{ type: 'ContentStatement', value: 'Hello' }] + }), + "CONTENT[ 'Hello' ]\n" + ); }); }); describe('directives', function() { it('should parse block directives', function() { - equals(astFor('{{#* foo}}{{/foo}}'), 'DIRECTIVE BLOCK:\n PATH:foo []\n PROGRAM:\n'); + equals( + astFor('{{#* foo}}{{/foo}}'), + 'DIRECTIVE BLOCK:\n PATH:foo []\n PROGRAM:\n' + ); }); it('should parse directives', function() { equals(astFor('{{* foo}}'), '{{ DIRECTIVE PATH:foo [] }}\n'); }); it('should fail if directives have inverse', function() { - shouldThrow(function() { - astFor('{{#* foo}}{{^}}{{/foo}}'); - }, Error, /Unexpected inverse/); + shouldThrow( + function() { + astFor('{{#* foo}}{{^}}{{/foo}}'); + }, + Error, + /Unexpected inverse/ + ); }); }); it('GH1024 - should track program location properly', function() { - var p = Handlebars.parse('\n' - + ' {{#if foo}}\n' - + ' {{bar}}\n' - + ' {{else}} {{baz}}\n' - + '\n' - + ' {{/if}}\n' - + ' '); + var p = Handlebars.parse( + '\n' + + ' {{#if foo}}\n' + + ' {{bar}}\n' + + ' {{else}} {{baz}}\n' + + '\n' + + ' {{/if}}\n' + + ' ' + ); // We really need a deep equals but for now this should be stable... - equals(JSON.stringify(p.loc), JSON.stringify({ - start: { line: 1, column: 0 }, - end: { line: 7, column: 4 } - })); - equals(JSON.stringify(p.body[1].program.loc), JSON.stringify({ - start: { line: 2, column: 13 }, - end: { line: 4, column: 7 } - })); - equals(JSON.stringify(p.body[1].inverse.loc), JSON.stringify({ - start: { line: 4, column: 15 }, - end: { line: 6, column: 5 } - })); + equals( + JSON.stringify(p.loc), + JSON.stringify({ + start: { line: 1, column: 0 }, + end: { line: 7, column: 4 } + }) + ); + equals( + JSON.stringify(p.body[1].program.loc), + JSON.stringify({ + start: { line: 2, column: 13 }, + end: { line: 4, column: 7 } + }) + ); + equals( + JSON.stringify(p.body[1].inverse.loc), + JSON.stringify({ + start: { line: 4, column: 15 }, + end: { line: 6, column: 5 } + }) + ); }); }); diff --git a/spec/partials.js b/spec/partials.js index 79f1dc881..d00d4147e 100644 --- a/spec/partials.js +++ b/spec/partials.js @@ -2,115 +2,224 @@ describe('partials', function() { it('basic partials', function() { var string = 'Dudes: {{#dudes}}{{> dude}}{{/dudes}}'; var partial = '{{name}} ({{url}}) '; - var hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]}; - shouldCompileToWithPartials(string, [hash, {}, {dude: partial}], true, 'Dudes: Yehuda (http://yehuda) Alan (http://alan) '); - shouldCompileToWithPartials(string, [hash, {}, {dude: partial},, false], true, 'Dudes: Yehuda (http://yehuda) Alan (http://alan) '); + var hash = { + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }; + shouldCompileToWithPartials( + string, + [hash, {}, { dude: partial }], + true, + 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ' + ); + shouldCompileToWithPartials( + string, + [hash, {}, { dude: partial }, , false], + true, + 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ' + ); }); it('dynamic partials', function() { var string = 'Dudes: {{#dudes}}{{> (partial)}}{{/dudes}}'; var partial = '{{name}} ({{url}}) '; - var hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]}; + var hash = { + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }; var helpers = { partial: function() { return 'dude'; } }; - shouldCompileToWithPartials(string, [hash, helpers, {dude: partial}], true, 'Dudes: Yehuda (http://yehuda) Alan (http://alan) '); - shouldCompileToWithPartials(string, [hash, helpers, {dude: partial},, false], true, 'Dudes: Yehuda (http://yehuda) Alan (http://alan) '); + shouldCompileToWithPartials( + string, + [hash, helpers, { dude: partial }], + true, + 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ' + ); + shouldCompileToWithPartials( + string, + [hash, helpers, { dude: partial }, , false], + true, + 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ' + ); }); it('failing dynamic partials', function() { var string = 'Dudes: {{#dudes}}{{> (partial)}}{{/dudes}}'; var partial = '{{name}} ({{url}}) '; - var hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]}; + var hash = { + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }; var helpers = { partial: function() { return 'missing'; } }; - shouldThrow(function() { - shouldCompileToWithPartials(string, [hash, helpers, {dude: partial}], true, 'Dudes: Yehuda (http://yehuda) Alan (http://alan) '); - }, Handlebars.Exception, 'The partial missing could not be found'); + shouldThrow( + function() { + shouldCompileToWithPartials( + string, + [hash, helpers, { dude: partial }], + true, + 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ' + ); + }, + Handlebars.Exception, + 'The partial missing could not be found' + ); }); it('partials with context', function() { var string = 'Dudes: {{>dude dudes}}'; var partial = '{{#this}}{{name}} ({{url}}) {{/this}}'; - var hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]}; - shouldCompileToWithPartials(string, [hash, {}, {dude: partial}], true, 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ', - 'Partials can be passed a context'); + var hash = { + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }; + shouldCompileToWithPartials( + string, + [hash, {}, { dude: partial }], + true, + 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ', + 'Partials can be passed a context' + ); }); it('partials with no context', function() { var partial = '{{name}} ({{url}}) '; - var hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]}; + var hash = { + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }; shouldCompileToWithPartials( - 'Dudes: {{#dudes}}{{>dude}}{{/dudes}}', - [hash, {}, {dude: partial}, {explicitPartialContext: true}], - true, - 'Dudes: () () '); + 'Dudes: {{#dudes}}{{>dude}}{{/dudes}}', + [hash, {}, { dude: partial }, { explicitPartialContext: true }], + true, + 'Dudes: () () ' + ); shouldCompileToWithPartials( - 'Dudes: {{#dudes}}{{>dude name="foo"}}{{/dudes}}', - [hash, {}, {dude: partial}, {explicitPartialContext: true}], - true, - 'Dudes: foo () foo () '); + 'Dudes: {{#dudes}}{{>dude name="foo"}}{{/dudes}}', + [hash, {}, { dude: partial }, { explicitPartialContext: true }], + true, + 'Dudes: foo () foo () ' + ); }); it('partials with string context', function() { var string = 'Dudes: {{>dude "dudes"}}'; var partial = '{{.}}'; var hash = {}; - shouldCompileToWithPartials(string, [hash, {}, {dude: partial}], true, 'Dudes: dudes'); + shouldCompileToWithPartials( + string, + [hash, {}, { dude: partial }], + true, + 'Dudes: dudes' + ); }); it('partials with undefined context', function() { var string = 'Dudes: {{>dude dudes}}'; var partial = '{{foo}} Empty'; var hash = {}; - shouldCompileToWithPartials(string, [hash, {}, {dude: partial}], true, 'Dudes: Empty'); + shouldCompileToWithPartials( + string, + [hash, {}, { dude: partial }], + true, + 'Dudes: Empty' + ); }); it('partials with duplicate parameters', function() { - shouldThrow(function() { - CompilerContext.compile('Dudes: {{>dude dudes foo bar=baz}}'); - }, Error, 'Unsupported number of partial arguments: 2 - 1:7'); + shouldThrow( + function() { + CompilerContext.compile('Dudes: {{>dude dudes foo bar=baz}}'); + }, + Error, + 'Unsupported number of partial arguments: 2 - 1:7' + ); }); it('partials with parameters', function() { var string = 'Dudes: {{#dudes}}{{> dude others=..}}{{/dudes}}'; var partial = '{{others.foo}}{{name}} ({{url}}) '; - var hash = {foo: 'bar', dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]}; - shouldCompileToWithPartials(string, [hash, {}, {dude: partial}], true, 'Dudes: barYehuda (http://yehuda) barAlan (http://alan) ', - 'Basic partials output based on current context.'); + var hash = { + foo: 'bar', + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }; + shouldCompileToWithPartials( + string, + [hash, {}, { dude: partial }], + true, + 'Dudes: barYehuda (http://yehuda) barAlan (http://alan) ', + 'Basic partials output based on current context.' + ); }); it('partial in a partial', function() { var string = 'Dudes: {{#dudes}}{{>dude}}{{/dudes}}'; var dude = '{{name}} {{> url}} '; var url = '{{url}}'; - var hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]}; - shouldCompileToWithPartials(string, [hash, {}, {dude: dude, url: url}], true, 'Dudes: Yehuda http://yehuda Alan http://alan ', 'Partials are rendered inside of other partials'); + var hash = { + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }; + shouldCompileToWithPartials( + string, + [hash, {}, { dude: dude, url: url }], + true, + 'Dudes: Yehuda http://yehuda Alan http://alan ', + 'Partials are rendered inside of other partials' + ); }); it('rendering undefined partial throws an exception', function() { - shouldThrow(function() { - var template = CompilerContext.compile('{{> whatever}}'); - template(); - }, Handlebars.Exception, 'The partial whatever could not be found'); + shouldThrow( + function() { + var template = CompilerContext.compile('{{> whatever}}'); + template(); + }, + Handlebars.Exception, + 'The partial whatever could not be found' + ); }); it('registering undefined partial throws an exception', function() { - shouldThrow(function() { - var undef; - handlebarsEnv.registerPartial('undefined_test', undef); - }, Handlebars.Exception, 'Attempting to register a partial called "undefined_test" as undefined'); + shouldThrow( + function() { + var undef; + handlebarsEnv.registerPartial('undefined_test', undef); + }, + Handlebars.Exception, + 'Attempting to register a partial called "undefined_test" as undefined' + ); }); it('rendering template partial in vm mode throws an exception', function() { - shouldThrow(function() { - var template = CompilerContext.compile('{{> whatever}}'); - template(); - }, Handlebars.Exception, 'The partial whatever could not be found'); + shouldThrow( + function() { + var template = CompilerContext.compile('{{> whatever}}'); + template(); + }, + Handlebars.Exception, + 'The partial whatever could not be found' + ); }); it('rendering function partial in vm mode', function() { @@ -118,30 +227,57 @@ describe('partials', function() { function partial(context) { return context.name + ' (' + context.url + ') '; } - var hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]}; - shouldCompileTo(string, [hash, {}, {dude: partial}], 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ', - 'Function partials output based in VM.'); + var hash = { + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }; + shouldCompileTo( + string, + [hash, {}, { dude: partial }], + 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ', + 'Function partials output based in VM.' + ); }); it('GH-14: a partial preceding a selector', function() { var string = 'Dudes: {{>dude}} {{anotherDude}}'; var dude = '{{name}}'; - var hash = {name: 'Jeepers', anotherDude: 'Creepers'}; - shouldCompileToWithPartials(string, [hash, {}, {dude: dude}], true, 'Dudes: Jeepers Creepers', 'Regular selectors can follow a partial'); + var hash = { name: 'Jeepers', anotherDude: 'Creepers' }; + shouldCompileToWithPartials( + string, + [hash, {}, { dude: dude }], + true, + 'Dudes: Jeepers Creepers', + 'Regular selectors can follow a partial' + ); }); it('Partials with slash paths', function() { var string = 'Dudes: {{> shared/dude}}'; var dude = '{{name}}'; - var hash = {name: 'Jeepers', anotherDude: 'Creepers'}; - shouldCompileToWithPartials(string, [hash, {}, {'shared/dude': dude}], true, 'Dudes: Jeepers', 'Partials can use literal paths'); + var hash = { name: 'Jeepers', anotherDude: 'Creepers' }; + shouldCompileToWithPartials( + string, + [hash, {}, { 'shared/dude': dude }], + true, + 'Dudes: Jeepers', + 'Partials can use literal paths' + ); }); it('Partials with slash and point paths', function() { var string = 'Dudes: {{> shared/dude.thing}}'; var dude = '{{name}}'; - var hash = {name: 'Jeepers', anotherDude: 'Creepers'}; - shouldCompileToWithPartials(string, [hash, {}, {'shared/dude.thing': dude}], true, 'Dudes: Jeepers', 'Partials can use literal with points in paths'); + var hash = { name: 'Jeepers', anotherDude: 'Creepers' }; + shouldCompileToWithPartials( + string, + [hash, {}, { 'shared/dude.thing': dude }], + true, + 'Dudes: Jeepers', + 'Partials can use literal with points in paths' + ); }); it('Global Partials', function() { @@ -149,8 +285,14 @@ describe('partials', function() { var string = 'Dudes: {{> shared/dude}} {{> globalTest}}'; var dude = '{{name}}'; - var hash = {name: 'Jeepers', anotherDude: 'Creepers'}; - shouldCompileToWithPartials(string, [hash, {}, {'shared/dude': dude}], true, 'Dudes: Jeepers Creepers', 'Partials can use globals or passed'); + var hash = { name: 'Jeepers', anotherDude: 'Creepers' }; + shouldCompileToWithPartials( + string, + [hash, {}, { 'shared/dude': dude }], + true, + 'Dudes: Jeepers Creepers', + 'Partials can use globals or passed' + ); handlebarsEnv.unregisterPartial('globalTest'); equals(handlebarsEnv.partials.globalTest, undefined); @@ -163,51 +305,95 @@ describe('partials', function() { }); var string = 'Dudes: {{> shared/dude}} {{> globalTest}}'; - var hash = {name: 'Jeepers', anotherDude: 'Creepers'}; - shouldCompileToWithPartials(string, [hash], true, 'Dudes: Jeepers Creepers', 'Partials can use globals or passed'); + var hash = { name: 'Jeepers', anotherDude: 'Creepers' }; + shouldCompileToWithPartials( + string, + [hash], + true, + 'Dudes: Jeepers Creepers', + 'Partials can use globals or passed' + ); }); it('Partials with integer path', function() { var string = 'Dudes: {{> 404}}'; var dude = '{{name}}'; - var hash = {name: 'Jeepers', anotherDude: 'Creepers'}; - shouldCompileToWithPartials(string, [hash, {}, {404: dude}], true, 'Dudes: Jeepers', 'Partials can use literal paths'); + var hash = { name: 'Jeepers', anotherDude: 'Creepers' }; + shouldCompileToWithPartials( + string, + [hash, {}, { 404: dude }], + true, + 'Dudes: Jeepers', + 'Partials can use literal paths' + ); }); it('Partials with complex path', function() { var string = 'Dudes: {{> 404/asdf?.bar}}'; var dude = '{{name}}'; - var hash = {name: 'Jeepers', anotherDude: 'Creepers'}; - shouldCompileToWithPartials(string, [hash, {}, {'404/asdf?.bar': dude}], true, 'Dudes: Jeepers', 'Partials can use literal paths'); + var hash = { name: 'Jeepers', anotherDude: 'Creepers' }; + shouldCompileToWithPartials( + string, + [hash, {}, { '404/asdf?.bar': dude }], + true, + 'Dudes: Jeepers', + 'Partials can use literal paths' + ); }); it('Partials with escaped', function() { var string = 'Dudes: {{> [+404/asdf?.bar]}}'; var dude = '{{name}}'; - var hash = {name: 'Jeepers', anotherDude: 'Creepers'}; - shouldCompileToWithPartials(string, [hash, {}, {'+404/asdf?.bar': dude}], true, 'Dudes: Jeepers', 'Partials can use literal paths'); + var hash = { name: 'Jeepers', anotherDude: 'Creepers' }; + shouldCompileToWithPartials( + string, + [hash, {}, { '+404/asdf?.bar': dude }], + true, + 'Dudes: Jeepers', + 'Partials can use literal paths' + ); }); it('Partials with string', function() { - var string = 'Dudes: {{> \'+404/asdf?.bar\'}}'; + var string = "Dudes: {{> '+404/asdf?.bar'}}"; var dude = '{{name}}'; - var hash = {name: 'Jeepers', anotherDude: 'Creepers'}; - shouldCompileToWithPartials(string, [hash, {}, {'+404/asdf?.bar': dude}], true, 'Dudes: Jeepers', 'Partials can use literal paths'); + var hash = { name: 'Jeepers', anotherDude: 'Creepers' }; + shouldCompileToWithPartials( + string, + [hash, {}, { '+404/asdf?.bar': dude }], + true, + 'Dudes: Jeepers', + 'Partials can use literal paths' + ); }); it('should handle empty partial', function() { var string = 'Dudes: {{#dudes}}{{> dude}}{{/dudes}}'; var partial = ''; - var hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]}; - shouldCompileToWithPartials(string, [hash, {}, {dude: partial}], true, 'Dudes: '); + var hash = { + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }; + shouldCompileToWithPartials( + string, + [hash, {}, { dude: partial }], + true, + 'Dudes: ' + ); }); it('throw on missing partial', function() { var compile = handlebarsEnv.compile; handlebarsEnv.compile = undefined; - shouldThrow(function() { - shouldCompileTo('{{> dude}}', [{}, {}, {dude: 'fail'}], ''); - }, Error, /The partial dude could not be compiled/); + shouldThrow( + function() { + shouldCompileTo('{{> dude}}', [{}, {}, { dude: 'fail' }], ''); + }, + Error, + /The partial dude could not be compiled/ + ); handlebarsEnv.compile = compile; }); @@ -217,224 +403,309 @@ describe('partials', function() { '{{#> dude}}success{{/dude}}', [{}, {}, {}], true, - 'success'); + 'success' + ); }); it('should execute default block with proper context', function() { shouldCompileToWithPartials( '{{#> dude context}}{{value}}{{/dude}}', - [{context: {value: 'success'}}, {}, {}], + [{ context: { value: 'success' } }, {}, {}], true, - 'success'); + 'success' + ); }); it('should propagate block parameters to default block', function() { shouldCompileToWithPartials( '{{#with context as |me|}}{{#> dude}}{{me.value}}{{/dude}}{{/with}}', - [{context: {value: 'success'}}, {}, {}], + [{ context: { value: 'success' } }, {}, {}], true, - 'success'); + 'success' + ); }); it('should not use partial block if partial exists', function() { shouldCompileToWithPartials( '{{#> dude}}fail{{/dude}}', - [{}, {}, {dude: 'success'}], + [{}, {}, { dude: 'success' }], true, - 'success'); + 'success' + ); }); it('should render block from partial', function() { shouldCompileToWithPartials( '{{#> dude}}success{{/dude}}', - [{}, {}, {dude: '{{> @partial-block }}'}], + [{}, {}, { dude: '{{> @partial-block }}' }], true, - 'success'); + 'success' + ); }); it('should be able to render the partial-block twice', function() { shouldCompileToWithPartials( - '{{#> dude}}success{{/dude}}', - [{}, {}, {dude: '{{> @partial-block }} {{> @partial-block }}'}], - true, - 'success success'); + '{{#> dude}}success{{/dude}}', + [{}, {}, { dude: '{{> @partial-block }} {{> @partial-block }}' }], + true, + 'success success' + ); }); it('should render block from partial with context', function() { shouldCompileToWithPartials( '{{#> dude}}{{value}}{{/dude}}', - [{context: {value: 'success'}}, {}, {dude: '{{#with context}}{{> @partial-block }}{{/with}}'}], + [ + { context: { value: 'success' } }, + {}, + { dude: '{{#with context}}{{> @partial-block }}{{/with}}' } + ], true, - 'success'); + 'success' + ); }); it('should be able to access the @data frame from a partial-block', function() { shouldCompileToWithPartials( '{{#> dude}}in-block: {{@root/value}}{{/dude}}', - [{value: 'success'}, {}, {dude: 'before-block: {{@root/value}} {{> @partial-block }}'}], + [ + { value: 'success' }, + {}, + { + dude: + 'before-block: {{@root/value}} {{> @partial-block }}' + } + ], true, - 'before-block: success in-block: success'); + 'before-block: success in-block: success' + ); }); it('should allow the #each-helper to be used along with partial-blocks', function() { shouldCompileToWithPartials( '', [ - {value: ['a', 'b', 'c']}, + { value: ['a', 'b', 'c'] }, {}, { - list: '{{#each .}}{{> @partial-block}}{{/each}}' + list: + '{{#each .}}{{> @partial-block}}{{/each}}' } ], true, - ''); + '' + ); }); it('should render block from partial with context (twice)', function() { shouldCompileToWithPartials( - '{{#> dude}}{{value}}{{/dude}}', - [ - {context: {value: 'success'}}, - {}, - { - dude: '{{#with context}}{{> @partial-block }} {{> @partial-block }}{{/with}}' - } - ], - true, - 'success success'); + '{{#> dude}}{{value}}{{/dude}}', + [ + { context: { value: 'success' } }, + {}, + { + dude: + '{{#with context}}{{> @partial-block }} {{> @partial-block }}{{/with}}' + } + ], + true, + 'success success' + ); }); it('should render block from partial with context', function() { shouldCompileToWithPartials( '{{#> dude}}{{../context/value}}{{/dude}}', - [{context: {value: 'success'}}, {}, {dude: '{{#with context}}{{> @partial-block }}{{/with}}'}], + [ + { context: { value: 'success' } }, + {}, + { dude: '{{#with context}}{{> @partial-block }}{{/with}}' } + ], true, - 'success'); + 'success' + ); }); it('should render block from partial with block params', function() { shouldCompileToWithPartials( '{{#with context as |me|}}{{#> dude}}{{me.value}}{{/dude}}{{/with}}', - [{context: {value: 'success'}}, {}, {dude: '{{> @partial-block }}'}], + [ + { context: { value: 'success' } }, + {}, + { dude: '{{> @partial-block }}' } + ], true, - 'success'); + 'success' + ); }); it('should render nested partial blocks', function() { shouldCompileToWithPartials( '', [ - {value: 'success'}, + { value: 'success' }, {}, { - outer: '{{#> nested}}{{> @partial-block}}{{/nested}}', + outer: + '{{#> nested}}{{> @partial-block}}{{/nested}}', nested: '{{> @partial-block}}' } ], true, - ''); + '' + ); }); it('should render nested partial blocks at different nesting levels', function() { shouldCompileToWithPartials( '', [ - {value: 'success'}, + { value: 'success' }, {}, { - outer: '{{#> nested}}{{> @partial-block}}{{/nested}}{{> @partial-block}}', + outer: + '{{#> nested}}{{> @partial-block}}{{/nested}}{{> @partial-block}}', nested: '{{> @partial-block}}' } ], true, - ''); + '' + ); }); it('should render nested partial blocks at different nesting levels (twice)', function() { shouldCompileToWithPartials( '', [ - {value: 'success'}, + { value: 'success' }, {}, { - outer: '{{#> nested}}{{> @partial-block}} {{> @partial-block}}{{/nested}}{{> @partial-block}}+{{> @partial-block}}', + outer: + '{{#> nested}}{{> @partial-block}} {{> @partial-block}}{{/nested}}{{> @partial-block}}+{{> @partial-block}}', nested: '{{> @partial-block}}' } ], true, - ''); + '' + ); }); it('should render nested partial blocks (twice at each level)', function() { shouldCompileToWithPartials( '', [ - {value: 'success'}, + { value: 'success' }, {}, { - outer: '{{#> nested}}{{> @partial-block}} {{> @partial-block}}{{/nested}}', + outer: + '{{#> nested}}{{> @partial-block}} {{> @partial-block}}{{/nested}}', nested: '{{> @partial-block}}{{> @partial-block}}' } ], true, ''); + 'success successsuccess success' + + '' + ); }); }); describe('inline partials', function() { it('should define inline partials for template', function() { - shouldCompileTo('{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}', {}, 'success'); + shouldCompileTo( + '{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}', + {}, + 'success' + ); }); it('should overwrite multiple partials in the same template', function() { - shouldCompileTo('{{#*inline "myPartial"}}fail{{/inline}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}', {}, 'success'); + shouldCompileTo( + '{{#*inline "myPartial"}}fail{{/inline}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}', + {}, + 'success' + ); }); it('should define inline partials for block', function() { - shouldCompileTo('{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}{{/with}}', {}, 'success'); - shouldThrow(function() { - shouldCompileTo('{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{/with}}{{> myPartial}}', {}, 'success'); - }, Error, /myPartial could not/); + shouldCompileTo( + '{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}{{/with}}', + {}, + 'success' + ); + shouldThrow( + function() { + shouldCompileTo( + '{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{/with}}{{> myPartial}}', + {}, + 'success' + ); + }, + Error, + /myPartial could not/ + ); }); it('should override global partials', function() { - shouldCompileTo('{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}', {hash: {}, partials: {myPartial: function() { return 'fail'; }}}, 'success'); + shouldCompileTo( + '{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}', + { + hash: {}, + partials: { + myPartial: function() { + return 'fail'; + } + } + }, + 'success' + ); }); it('should override template partials', function() { - shouldCompileTo('{{#*inline "myPartial"}}fail{{/inline}}{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}{{/with}}', {}, 'success'); + shouldCompileTo( + '{{#*inline "myPartial"}}fail{{/inline}}{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}{{/with}}', + {}, + 'success' + ); }); it('should override partials down the entire stack', function() { - shouldCompileTo('{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{#with .}}{{#with .}}{{> myPartial}}{{/with}}{{/with}}{{/with}}', {}, 'success'); + shouldCompileTo( + '{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{#with .}}{{#with .}}{{> myPartial}}{{/with}}{{/with}}{{/with}}', + {}, + 'success' + ); }); it('should define inline partials for partial call', function() { shouldCompileToWithPartials( '{{#*inline "myPartial"}}success{{/inline}}{{> dude}}', - [{}, {}, {dude: '{{> myPartial }}'}], + [{}, {}, { dude: '{{> myPartial }}' }], true, - 'success'); + 'success' + ); }); it('should define inline partials in partial block call', function() { shouldCompileToWithPartials( '{{#> dude}}{{#*inline "myPartial"}}success{{/inline}}{{/dude}}', - [{}, {}, {dude: '{{> myPartial }}'}], + [{}, {}, { dude: '{{> myPartial }}' }], true, - 'success'); + 'success' + ); }); it('should render nested inline partials', function() { shouldCompileToWithPartials( '{{#*inline "outer"}}{{#>inner}}{{>@partial-block}}{{/inner}}{{/inline}}' + - '{{#*inline "inner"}}{{>@partial-block}}{{/inline}}' + - '{{#>outer}}{{value}}{{/outer}}', - [{value: 'success'}, {}, {}], + '{{#*inline "inner"}}{{>@partial-block}}{{/inline}}' + + '{{#>outer}}{{value}}{{/outer}}', + [{ value: 'success' }, {}, {}], true, - 'success'); + 'success' + ); }); it('should render nested inline partials with partial-blocks on different nesting levels', function() { shouldCompileToWithPartials( '{{#*inline "outer"}}{{#>inner}}{{>@partial-block}}{{/inner}}{{>@partial-block}}{{/inline}}' + - '{{#*inline "inner"}}{{>@partial-block}}{{/inline}}' + - '{{#>outer}}{{value}}{{/outer}}', - [{value: 'success'}, {}, {}], + '{{#*inline "inner"}}{{>@partial-block}}{{/inline}}' + + '{{#>outer}}{{value}}{{/outer}}', + [{ value: 'success' }, {}, {}], true, - 'successsuccess'); + 'successsuccess' + ); }); it('should render nested inline partials (twice at each level)', function() { shouldCompileToWithPartials( '{{#*inline "outer"}}{{#>inner}}{{>@partial-block}} {{>@partial-block}}{{/inner}}{{/inline}}' + - '{{#*inline "inner"}}{{>@partial-block}}{{>@partial-block}}{{/inline}}' + - '{{#>outer}}{{value}}{{/outer}}', - [{value: 'success'}, {}, {}], + '{{#*inline "inner"}}{{>@partial-block}}{{>@partial-block}}{{/inline}}' + + '{{#>outer}}{{value}}{{/outer}}', + [{ value: 'success' }, {}, {}], true, - 'success successsuccess success'); + 'success successsuccess success' + ); }); }); @@ -442,8 +713,8 @@ describe('partials', function() { if (Handlebars.compile) { var env = Handlebars.create(); env.registerPartial('partial', '{{foo}}'); - var template = env.compile('{{foo}} {{> partial}}', {noEscape: true}); - equal(template({foo: '<'}), '< <'); + var template = env.compile('{{foo}} {{> partial}}', { noEscape: true }); + equal(template({ foo: '<' }), '< <'); } }); @@ -451,25 +722,52 @@ describe('partials', function() { it('indented partials', function() { var string = 'Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}'; var dude = '{{name}}\n'; - var hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]}; - shouldCompileToWithPartials(string, [hash, {}, {dude: dude}], true, - 'Dudes:\n Yehuda\n Alan\n'); + var hash = { + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }; + shouldCompileToWithPartials( + string, + [hash, {}, { dude: dude }], + true, + 'Dudes:\n Yehuda\n Alan\n' + ); }); it('nested indented partials', function() { var string = 'Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}'; var dude = '{{name}}\n {{> url}}'; var url = '{{url}}!\n'; - var hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]}; - shouldCompileToWithPartials(string, [hash, {}, {dude: dude, url: url}], true, - 'Dudes:\n Yehuda\n http://yehuda!\n Alan\n http://alan!\n'); + var hash = { + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }; + shouldCompileToWithPartials( + string, + [hash, {}, { dude: dude, url: url }], + true, + 'Dudes:\n Yehuda\n http://yehuda!\n Alan\n http://alan!\n' + ); }); it('prevent nested indented partials', function() { var string = 'Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}'; var dude = '{{name}}\n {{> url}}'; var url = '{{url}}!\n'; - var hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]}; - shouldCompileToWithPartials(string, [hash, {}, {dude: dude, url: url}, {preventIndent: true}], true, - 'Dudes:\n Yehuda\n http://yehuda!\n Alan\n http://alan!\n'); + var hash = { + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }; + shouldCompileToWithPartials( + string, + [hash, {}, { dude: dude, url: url }, { preventIndent: true }], + true, + 'Dudes:\n Yehuda\n http://yehuda!\n Alan\n http://alan!\n' + ); }); }); @@ -477,26 +775,70 @@ describe('partials', function() { it('partials can access parents', function() { var string = 'Dudes: {{#dudes}}{{> dude}}{{/dudes}}'; var partial = '{{name}} ({{url}}) {{root}} '; - var hash = {root: 'yes', dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]}; - shouldCompileToWithPartials(string, [hash, {}, {dude: partial}, true], true, 'Dudes: Yehuda (http://yehuda) yes Alan (http://alan) yes '); + var hash = { + root: 'yes', + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }; + shouldCompileToWithPartials( + string, + [hash, {}, { dude: partial }, true], + true, + 'Dudes: Yehuda (http://yehuda) yes Alan (http://alan) yes ' + ); }); it('partials can access parents with custom context', function() { var string = 'Dudes: {{#dudes}}{{> dude "test"}}{{/dudes}}'; var partial = '{{name}} ({{url}}) {{root}} '; - var hash = {root: 'yes', dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]}; - shouldCompileToWithPartials(string, [hash, {}, {dude: partial}, true], true, 'Dudes: Yehuda (http://yehuda) yes Alan (http://alan) yes '); + var hash = { + root: 'yes', + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }; + shouldCompileToWithPartials( + string, + [hash, {}, { dude: partial }, true], + true, + 'Dudes: Yehuda (http://yehuda) yes Alan (http://alan) yes ' + ); }); it('partials can access parents without data', function() { var string = 'Dudes: {{#dudes}}{{> dude}}{{/dudes}}'; var partial = '{{name}} ({{url}}) {{root}} '; - var hash = {root: 'yes', dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]}; - shouldCompileToWithPartials(string, [hash, {}, {dude: partial}, true, false], true, 'Dudes: Yehuda (http://yehuda) yes Alan (http://alan) yes '); + var hash = { + root: 'yes', + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }; + shouldCompileToWithPartials( + string, + [hash, {}, { dude: partial }, true, false], + true, + 'Dudes: Yehuda (http://yehuda) yes Alan (http://alan) yes ' + ); }); it('partials inherit compat', function() { var string = 'Dudes: {{> dude}}'; var partial = '{{#dudes}}{{name}} ({{url}}) {{root}} {{/dudes}}'; - var hash = {root: 'yes', dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]}; - shouldCompileToWithPartials(string, [hash, {}, {dude: partial}, true], true, 'Dudes: Yehuda (http://yehuda) yes Alan (http://alan) yes '); + var hash = { + root: 'yes', + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }; + shouldCompileToWithPartials( + string, + [hash, {}, { dude: partial }, true], + true, + 'Dudes: Yehuda (http://yehuda) yes Alan (http://alan) yes ' + ); }); }); }); diff --git a/spec/precompiler.js b/spec/precompiler.js index 9f2a6442b..2261023cc 100644 --- a/spec/precompiler.js +++ b/spec/precompiler.js @@ -6,27 +6,24 @@ describe('precompiler', function() { } var Handlebars = require('../lib'), - Precompiler = require('../dist/cjs/precompiler'), - fs = require('fs'), - uglify = require('uglify-js'); + Precompiler = require('../dist/cjs/precompiler'), + fs = require('fs'), + uglify = require('uglify-js'); var log, - logFunction, - errorLog, - errorLogFunction, - - precompile, - minify, - - emptyTemplate = { - path: __dirname + '/artifacts/empty.handlebars', - name: 'empty', - source: '' - }, - - file, - content, - writeFileSync; + logFunction, + errorLog, + errorLogFunction, + precompile, + minify, + emptyTemplate = { + path: __dirname + '/artifacts/empty.handlebars', + name: 'empty', + source: '' + }, + file, + content, + writeFileSync; /** * Mock the Module.prototype.require-function such that an error is thrown, when "uglify-js" is loaded. @@ -87,102 +84,166 @@ describe('precompiler', function() { }); it('should output version', function() { - Precompiler.cli({templates: [], version: true}); + Precompiler.cli({ templates: [], version: true }); equals(log, Handlebars.VERSION); }); it('should throw if lacking templates', function() { - shouldThrow(function() { - Precompiler.cli({templates: []}); - }, Handlebars.Exception, 'Must define at least one template or directory.'); + shouldThrow( + function() { + Precompiler.cli({ templates: [] }); + }, + Handlebars.Exception, + 'Must define at least one template or directory.' + ); }); it('should handle empty/filtered directories', function() { - Precompiler.cli({hasDirectory: true, templates: []}); + Precompiler.cli({ hasDirectory: true, templates: [] }); // Success is not throwing }); it('should throw when combining simple and minimized', function() { - shouldThrow(function() { - Precompiler.cli({templates: [__dirname], simple: true, min: true}); - }, Handlebars.Exception, 'Unable to minimize simple output'); + shouldThrow( + function() { + Precompiler.cli({ templates: [__dirname], simple: true, min: true }); + }, + Handlebars.Exception, + 'Unable to minimize simple output' + ); }); it('should throw when combining simple and multiple templates', function() { - shouldThrow(function() { - Precompiler.cli({templates: [__dirname + '/artifacts/empty.handlebars', __dirname + '/artifacts/empty.handlebars'], simple: true}); - }, Handlebars.Exception, 'Unable to output multiple templates in simple mode'); + shouldThrow( + function() { + Precompiler.cli({ + templates: [ + __dirname + '/artifacts/empty.handlebars', + __dirname + '/artifacts/empty.handlebars' + ], + simple: true + }); + }, + Handlebars.Exception, + 'Unable to output multiple templates in simple mode' + ); }); it('should throw when missing name', function() { - shouldThrow(function() { - Precompiler.cli({templates: [{source: ''}], amd: true}); - }, Handlebars.Exception, 'Name missing for template'); + shouldThrow( + function() { + Precompiler.cli({ templates: [{ source: '' }], amd: true }); + }, + Handlebars.Exception, + 'Name missing for template' + ); }); it('should throw when combining simple and directories', function() { - shouldThrow(function() { - Precompiler.cli({hasDirectory: true, templates: [1], simple: true}); - }, Handlebars.Exception, 'Unable to output multiple templates in simple mode'); + shouldThrow( + function() { + Precompiler.cli({ hasDirectory: true, templates: [1], simple: true }); + }, + Handlebars.Exception, + 'Unable to output multiple templates in simple mode' + ); }); it('should output simple templates', function() { - Handlebars.precompile = function() { return 'simple'; }; - Precompiler.cli({templates: [emptyTemplate], simple: true}); + Handlebars.precompile = function() { + return 'simple'; + }; + Precompiler.cli({ templates: [emptyTemplate], simple: true }); equal(log, 'simple\n'); }); it('should default to simple templates', function() { - Handlebars.precompile = function() { return 'simple'; }; - Precompiler.cli({templates: [{source: ''}]}); + Handlebars.precompile = function() { + return 'simple'; + }; + Precompiler.cli({ templates: [{ source: '' }] }); equal(log, 'simple\n'); }); it('should output amd templates', function() { - Handlebars.precompile = function() { return 'amd'; }; - Precompiler.cli({templates: [emptyTemplate], amd: true}); + Handlebars.precompile = function() { + return 'amd'; + }; + Precompiler.cli({ templates: [emptyTemplate], amd: true }); equal(/template\(amd\)/.test(log), true); }); it('should output multiple amd', function() { - Handlebars.precompile = function() { return 'amd'; }; - Precompiler.cli({templates: [emptyTemplate, emptyTemplate], amd: true, namespace: 'foo'}); + Handlebars.precompile = function() { + return 'amd'; + }; + Precompiler.cli({ + templates: [emptyTemplate, emptyTemplate], + amd: true, + namespace: 'foo' + }); equal(/templates = foo = foo \|\|/.test(log), true); equal(/return templates/.test(log), true); equal(/template\(amd\)/.test(log), true); }); it('should output amd partials', function() { - Handlebars.precompile = function() { return 'amd'; }; - Precompiler.cli({templates: [emptyTemplate], amd: true, partial: true}); + Handlebars.precompile = function() { + return 'amd'; + }; + Precompiler.cli({ templates: [emptyTemplate], amd: true, partial: true }); equal(/return Handlebars\.partials\['empty'\]/.test(log), true); equal(/template\(amd\)/.test(log), true); }); it('should output multiple amd partials', function() { - Handlebars.precompile = function() { return 'amd'; }; - Precompiler.cli({templates: [emptyTemplate, emptyTemplate], amd: true, partial: true}); + Handlebars.precompile = function() { + return 'amd'; + }; + Precompiler.cli({ + templates: [emptyTemplate, emptyTemplate], + amd: true, + partial: true + }); equal(/return Handlebars\.partials\[/.test(log), false); equal(/template\(amd\)/.test(log), true); }); it('should output commonjs templates', function() { - Handlebars.precompile = function() { return 'commonjs'; }; - Precompiler.cli({templates: [emptyTemplate], commonjs: true}); + Handlebars.precompile = function() { + return 'commonjs'; + }; + Precompiler.cli({ templates: [emptyTemplate], commonjs: true }); equal(/template\(commonjs\)/.test(log), true); }); it('should set data flag', function() { - Handlebars.precompile = function(data, options) { equal(options.data, true); return 'simple'; }; - Precompiler.cli({templates: [emptyTemplate], simple: true, data: true}); + Handlebars.precompile = function(data, options) { + equal(options.data, true); + return 'simple'; + }; + Precompiler.cli({ templates: [emptyTemplate], simple: true, data: true }); equal(log, 'simple\n'); }); it('should set known helpers', function() { - Handlebars.precompile = function(data, options) { equal(options.knownHelpers.foo, true); return 'simple'; }; - Precompiler.cli({templates: [emptyTemplate], simple: true, known: 'foo'}); + Handlebars.precompile = function(data, options) { + equal(options.knownHelpers.foo, true); + return 'simple'; + }; + Precompiler.cli({ templates: [emptyTemplate], simple: true, known: 'foo' }); equal(log, 'simple\n'); }); it('should output to file system', function() { - Handlebars.precompile = function() { return 'simple'; }; - Precompiler.cli({templates: [emptyTemplate], simple: true, output: 'file!'}); + Handlebars.precompile = function() { + return 'simple'; + }; + Precompiler.cli({ + templates: [emptyTemplate], + simple: true, + output: 'file!' + }); equal(file, 'file!'); equal(content, 'simple\n'); equal(log, ''); }); it('should output minimized templates', function() { - Handlebars.precompile = function() { return 'amd'; }; - uglify.minify = function() { return {code: 'min'}; }; - Precompiler.cli({templates: [emptyTemplate], min: true}); + Handlebars.precompile = function() { + return 'amd'; + }; + uglify.minify = function() { + return { code: 'min' }; + }; + Precompiler.cli({ templates: [emptyTemplate], min: true }); equal(log, 'min'); }); @@ -191,8 +252,10 @@ describe('precompiler', function() { error.code = 'MODULE_NOT_FOUND'; mockRequireUglify(error, function() { var Precompiler = require('../dist/cjs/precompiler'); - Handlebars.precompile = function() { return 'amd'; }; - Precompiler.cli({templates: [emptyTemplate], min: true}); + Handlebars.precompile = function() { + return 'amd'; + }; + Precompiler.cli({ templates: [emptyTemplate], min: true }); equal(/template\(amd\)/.test(log), true); equal(/\n/.test(log), true); equal(/Code minimization is disabled/.test(errorLog), true); @@ -201,23 +264,33 @@ describe('precompiler', function() { it('should fail on errors (other than missing module) while loading uglify-js', function() { mockRequireUglify(new Error('Mock Error'), function() { - shouldThrow(function() { - var Precompiler = require('../dist/cjs/precompiler'); - Handlebars.precompile = function() { return 'amd'; }; - Precompiler.cli({templates: [emptyTemplate], min: true}); - }, Error, 'Mock Error'); + shouldThrow( + function() { + var Precompiler = require('../dist/cjs/precompiler'); + Handlebars.precompile = function() { + return 'amd'; + }; + Precompiler.cli({ templates: [emptyTemplate], min: true }); + }, + Error, + 'Mock Error' + ); }); }); it('should output map', function() { - Precompiler.cli({templates: [emptyTemplate], map: 'foo.js.map'}); + Precompiler.cli({ templates: [emptyTemplate], map: 'foo.js.map' }); equal(file, 'foo.js.map'); equal(log.match(/sourceMappingURL=/g).length, 1); }); it('should output map', function() { - Precompiler.cli({templates: [emptyTemplate], min: true, map: 'foo.js.map'}); + Precompiler.cli({ + templates: [emptyTemplate], + min: true, + map: 'foo.js.map' + }); equal(file, 'foo.js.map'); equal(log.match(/sourceMappingURL=/g).length, 1); @@ -225,35 +298,48 @@ describe('precompiler', function() { describe('#loadTemplates', function() { it('should throw on missing template', function(done) { - Precompiler.loadTemplates({files: ['foo']}, function(err) { + Precompiler.loadTemplates({ files: ['foo'] }, function(err) { equal(err.message, 'Unable to open template file "foo"'); done(); }); }); it('should enumerate directories by extension', function(done) { - Precompiler.loadTemplates({files: [__dirname + '/artifacts'], extension: 'hbs'}, function(err, opts) { - equal(opts.templates.length, 1); - equal(opts.templates[0].name, 'example_2'); - done(err); - }); + Precompiler.loadTemplates( + { files: [__dirname + '/artifacts'], extension: 'hbs' }, + function(err, opts) { + equal(opts.templates.length, 1); + equal(opts.templates[0].name, 'example_2'); + done(err); + } + ); }); it('should enumerate all templates by extension', function(done) { - Precompiler.loadTemplates({files: [__dirname + '/artifacts'], extension: 'handlebars'}, function(err, opts) { - equal(opts.templates.length, 3); - equal(opts.templates[0].name, 'bom'); - equal(opts.templates[1].name, 'empty'); - equal(opts.templates[2].name, 'example_1'); - done(err); - }); + Precompiler.loadTemplates( + { files: [__dirname + '/artifacts'], extension: 'handlebars' }, + function(err, opts) { + equal(opts.templates.length, 3); + equal(opts.templates[0].name, 'bom'); + equal(opts.templates[1].name, 'empty'); + equal(opts.templates[2].name, 'example_1'); + done(err); + } + ); }); it('should handle regular expression characters in extensions', function(done) { - Precompiler.loadTemplates({files: [__dirname + '/artifacts'], extension: 'hb(s'}, function(err) { - // Success is not throwing - done(err); - }); + Precompiler.loadTemplates( + { files: [__dirname + '/artifacts'], extension: 'hb(s' }, + function(err) { + // Success is not throwing + done(err); + } + ); }); it('should handle BOM', function(done) { - var opts = {files: [__dirname + '/artifacts/bom.handlebars'], extension: 'handlebars', bom: true}; + var opts = { + files: [__dirname + '/artifacts/bom.handlebars'], + extension: 'handlebars', + bom: true + }; Precompiler.loadTemplates(opts, function(err, opts) { equal(opts.templates[0].source, 'a'); done(err); @@ -261,7 +347,11 @@ describe('precompiler', function() { }); it('should handle different root', function(done) { - var opts = {files: [__dirname + '/artifacts/empty.handlebars'], simple: true, root: 'foo/'}; + var opts = { + files: [__dirname + '/artifacts/empty.handlebars'], + simple: true, + root: 'foo/' + }; Precompiler.loadTemplates(opts, function(err, opts) { equal(opts.templates[0].name, __dirname + '/artifacts/empty'); done(err); @@ -269,7 +359,7 @@ describe('precompiler', function() { }); it('should accept string inputs', function(done) { - var opts = {string: ''}; + var opts = { string: '' }; Precompiler.loadTemplates(opts, function(err, opts) { equal(opts.templates[0].name, undefined); equal(opts.templates[0].source, ''); @@ -277,7 +367,7 @@ describe('precompiler', function() { }); }); it('should accept string array inputs', function(done) { - var opts = {string: ['', 'bar'], name: ['beep', 'boop']}; + var opts = { string: ['', 'bar'], name: ['beep', 'boop'] }; Precompiler.loadTemplates(opts, function(err, opts) { equal(opts.templates[0].name, 'beep'); equal(opts.templates[0].source, ''); @@ -288,7 +378,7 @@ describe('precompiler', function() { }); it('should accept stdin input', function(done) { var stdin = require('mock-stdin').stdin(); - Precompiler.loadTemplates({string: '-'}, function(err, opts) { + Precompiler.loadTemplates({ string: '-' }, function(err, opts) { equal(opts.templates[0].source, 'foo'); done(err); }); @@ -297,9 +387,12 @@ describe('precompiler', function() { stdin.end(); }); it('error on name missing', function(done) { - var opts = {string: ['', 'bar']}; + var opts = { string: ['', 'bar'] }; Precompiler.loadTemplates(opts, function(err) { - equal(err.message, 'Number of names did not match the number of string inputs'); + equal( + err.message, + 'Number of names did not match the number of string inputs' + ); done(); }); }); diff --git a/spec/regressions.js b/spec/regressions.js index 3eccb2817..821c48dc2 100644 --- a/spec/regressions.js +++ b/spec/regressions.js @@ -1,93 +1,134 @@ describe('Regressions', function() { it('GH-94: Cannot read property of undefined', function() { var data = { - 'books': [{ - 'title': 'The origin of species', - 'author': { - 'name': 'Charles Darwin' + books: [ + { + title: 'The origin of species', + author: { + name: 'Charles Darwin' + } + }, + { + title: 'Lazarillo de Tormes' } - }, { - 'title': 'Lazarillo de Tormes' - }] + ] }; var string = '{{#books}}{{title}}{{author.name}}{{/books}}'; - shouldCompileTo(string, data, 'The origin of speciesCharles DarwinLazarillo de Tormes', - 'Renders without an undefined property error'); + shouldCompileTo( + string, + data, + 'The origin of speciesCharles DarwinLazarillo de Tormes', + 'Renders without an undefined property error' + ); }); it("GH-150: Inverted sections print when they shouldn't", function() { var string = '{{^set}}not set{{/set}} :: {{#set}}set{{/set}}'; - shouldCompileTo(string, {}, 'not set :: ', "inverted sections run when property isn't present in context"); - shouldCompileTo(string, {set: undefined}, 'not set :: ', 'inverted sections run when property is undefined'); - shouldCompileTo(string, {set: false}, 'not set :: ', 'inverted sections run when property is false'); - shouldCompileTo(string, {set: true}, ' :: set', "inverted sections don't run when property is true"); + shouldCompileTo( + string, + {}, + 'not set :: ', + "inverted sections run when property isn't present in context" + ); + shouldCompileTo( + string, + { set: undefined }, + 'not set :: ', + 'inverted sections run when property is undefined' + ); + shouldCompileTo( + string, + { set: false }, + 'not set :: ', + 'inverted sections run when property is false' + ); + shouldCompileTo( + string, + { set: true }, + ' :: set', + "inverted sections don't run when property is true" + ); }); it('GH-158: Using array index twice, breaks the template', function() { var string = '{{arr.[0]}}, {{arr.[1]}}'; - var data = { 'arr': [1, 2] }; + var data = { arr: [1, 2] }; shouldCompileTo(string, data, '1, 2', 'it works as expected'); }); it("bug reported by @fat where lambdas weren't being properly resolved", function() { - var string = 'This is a slightly more complicated {{thing}}..\n' - + '{{! Just ignore this business. }}\n' - + 'Check this out:\n' - + '{{#hasThings}}\n' - + '
      \n' - + '{{#things}}\n' - + '
    • {{word}}
    • \n' - + '{{/things}}
    .\n' - + '{{/hasThings}}\n' - + '{{^hasThings}}\n' - + '\n' - + 'Nothing to check out...\n' - + '{{/hasThings}}'; + var string = + 'This is a slightly more complicated {{thing}}..\n' + + '{{! Just ignore this business. }}\n' + + 'Check this out:\n' + + '{{#hasThings}}\n' + + '
      \n' + + '{{#things}}\n' + + '
    • {{word}}
    • \n' + + '{{/things}}
    .\n' + + '{{/hasThings}}\n' + + '{{^hasThings}}\n' + + '\n' + + 'Nothing to check out...\n' + + '{{/hasThings}}'; var data = { thing: function() { return 'blah'; }, things: [ - {className: 'one', word: '@fat'}, - {className: 'two', word: '@dhg'}, - {className: 'three', word: '@sayrer'} + { className: 'one', word: '@fat' }, + { className: 'two', word: '@dhg' }, + { className: 'three', word: '@sayrer' } ], hasThings: function() { return true; } }; - var output = 'This is a slightly more complicated blah..\n' - + 'Check this out:\n' - + '
      \n' - + '
    • @fat
    • \n' - + '
    • @dhg
    • \n' - + '
    • @sayrer
    • \n' - + '
    .\n'; + var output = + 'This is a slightly more complicated blah..\n' + + 'Check this out:\n' + + '
      \n' + + '
    • @fat
    • \n' + + '
    • @dhg
    • \n' + + '
    • @sayrer
    • \n' + + '
    .\n'; shouldCompileTo(string, data, output); }); it('GH-408: Multiple loops fail', function() { var context = [ { name: 'John Doe', location: { city: 'Chicago' } }, - { name: 'Jane Doe', location: { city: 'New York'} } + { name: 'Jane Doe', location: { city: 'New York' } } ]; - var template = CompilerContext.compile('{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}'); + var template = CompilerContext.compile( + '{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}' + ); var result = template(context); - equals(result, 'John DoeJane DoeJohn DoeJane DoeJohn DoeJane Doe', 'It should output multiple times'); + equals( + result, + 'John DoeJane DoeJohn DoeJane DoeJohn DoeJane Doe', + 'It should output multiple times' + ); }); it('GS-428: Nested if else rendering', function() { - var succeedingTemplate = '{{#inverse}} {{#blk}} Unexpected {{/blk}} {{else}} {{#blk}} Expected {{/blk}} {{/inverse}}'; - var failingTemplate = '{{#inverse}} {{#blk}} Unexpected {{/blk}} {{else}} {{#blk}} Expected {{/blk}} {{/inverse}}'; + var succeedingTemplate = + '{{#inverse}} {{#blk}} Unexpected {{/blk}} {{else}} {{#blk}} Expected {{/blk}} {{/inverse}}'; + var failingTemplate = + '{{#inverse}} {{#blk}} Unexpected {{/blk}} {{else}} {{#blk}} Expected {{/blk}} {{/inverse}}'; var helpers = { - blk: function(block) { return block.fn(''); }, - inverse: function(block) { return block.inverse(''); } + blk: function(block) { + return block.fn(''); + }, + inverse: function(block) { + return block.inverse(''); + } }; shouldCompileTo(succeedingTemplate, [{}, helpers], ' Expected '); @@ -95,7 +136,7 @@ describe('Regressions', function() { }); it('GH-458: Scoped this identifier', function() { - shouldCompileTo('{{./foo}}', {foo: 'bar'}, 'bar'); + shouldCompileTo('{{./foo}}', { foo: 'bar' }, 'bar'); }); it('GH-375: Unicode line terminators', function() { @@ -104,11 +145,11 @@ describe('Regressions', function() { it('GH-534: Object prototype aliases', function() { /* eslint-disable no-extend-native */ - Object.prototype[0xD834] = true; + Object.prototype[0xd834] = true; shouldCompileTo('{{foo}}', { foo: 'bar' }, 'bar'); - delete Object.prototype[0xD834]; + delete Object.prototype[0xd834]; /* eslint-enable no-extend-native */ }); @@ -123,34 +164,46 @@ describe('Regressions', function() { it('GH-676: Using array in escaping mustache fails', function() { var string = '{{arr}}'; - var data = { 'arr': [1, 2] }; + var data = { arr: [1, 2] }; shouldCompileTo(string, data, data.arr.toString(), 'it works as expected'); }); it('Mustache man page', function() { - var string = 'Hello {{name}}. You have just won ${{value}}!{{#in_ca}} Well, ${{taxed_value}}, after taxes.{{/in_ca}}'; + var string = + 'Hello {{name}}. You have just won ${{value}}!{{#in_ca}} Well, ${{taxed_value}}, after taxes.{{/in_ca}}'; var data = { - 'name': 'Chris', - 'value': 10000, - 'taxed_value': 10000 - (10000 * 0.4), - 'in_ca': true + name: 'Chris', + value: 10000, + taxed_value: 10000 - 10000 * 0.4, + in_ca: true }; - shouldCompileTo(string, data, 'Hello Chris. You have just won $10000! Well, $6000, after taxes.', 'the hello world mustache example works'); + shouldCompileTo( + string, + data, + 'Hello Chris. You have just won $10000! Well, $6000, after taxes.', + 'the hello world mustache example works' + ); }); it('GH-731: zero context rendering', function() { - shouldCompileTo('{{#foo}} This is {{bar}} ~ {{/foo}}', {foo: 0, bar: 'OK'}, ' This is ~ '); + shouldCompileTo( + '{{#foo}} This is {{bar}} ~ {{/foo}}', + { foo: 0, bar: 'OK' }, + ' This is ~ ' + ); }); it('GH-820: zero pathed rendering', function() { - shouldCompileTo('{{foo.bar}}', {foo: 0}, ''); + shouldCompileTo('{{foo.bar}}', { foo: 0 }, ''); }); it('GH-837: undefined values for helpers', function() { var helpers = { - str: function(value) { return value + ''; } + str: function(value) { + return value + ''; + } }; shouldCompileTo('{{str bar.baz}}', [{}, helpers], 'undefined'); @@ -159,15 +212,13 @@ describe('Regressions', function() { it('GH-926: Depths and de-dupe', function() { var context = { name: 'foo', - data: [ - 1 - ], - notData: [ - 1 - ] + data: [1], + notData: [1] }; - var template = CompilerContext.compile('{{#if dater}}{{#each data}}{{../name}}{{/each}}{{else}}{{#each notData}}{{../name}}{{/each}}{{/if}}'); + var template = CompilerContext.compile( + '{{#if dater}}{{#each data}}{{../name}}{{/each}}{{else}}{{#each notData}}{{../name}}{{/each}}{{/if}}' + ); var result = template(context); equals(result, 'foo'); @@ -176,11 +227,15 @@ describe('Regressions', function() { it('GH-1021: Each empty string key', function() { var data = { '': 'foo', - 'name': 'Chris', - 'value': 10000 + name: 'Chris', + value: 10000 }; - shouldCompileTo('{{#each data}}Key: {{@key}}\n{{/each}}', {data: data}, 'Key: \nKey: name\nKey: value\n'); + shouldCompileTo( + '{{#each data}}Key: {{@key}}\n{{/each}}', + { data: data }, + 'Key: \nKey: name\nKey: value\n' + ); }); it('GH-1054: Should handle simple safe string responses', function() { @@ -194,18 +249,27 @@ describe('Regressions', function() { } }; - shouldCompileToWithPartials(root, [{}, helpers, partials], true, ''); + shouldCompileToWithPartials( + root, + [{}, helpers, partials], + true, + '' + ); }); it('GH-1065: Sparse arrays', function() { var array = []; array[1] = 'foo'; array[3] = 'bar'; - shouldCompileTo('{{#each array}}{{@index}}{{.}}{{/each}}', {array: array}, '1foo3bar'); + shouldCompileTo( + '{{#each array}}{{@index}}{{.}}{{/each}}', + { array: array }, + '1foo3bar' + ); }); it('GH-1093: Undefined helper context', function() { - var obj = {foo: undefined, bar: 'bat'}; + var obj = { foo: undefined, bar: 'bat' }; var helpers = { helper: function() { // It's valid to execute a block against an undefined context, but @@ -216,28 +280,45 @@ describe('Regressions', function() { } } // And to make IE happy, check for the known string as length is not enumerated. - return (this === 'bat' ? 'found' : 'not'); + return this === 'bat' ? 'found' : 'not'; } }; - shouldCompileTo('{{#each obj}}{{{helper}}}{{.}}{{/each}}', [{obj: obj}, helpers], 'notfoundbat'); + shouldCompileTo( + '{{#each obj}}{{{helper}}}{{.}}{{/each}}', + [{ obj: obj }, helpers], + 'notfoundbat' + ); }); it('should support multiple levels of inline partials', function() { - var string = '{{#> layout}}{{#*inline "subcontent"}}subcontent{{/inline}}{{/layout}}'; + var string = + '{{#> layout}}{{#*inline "subcontent"}}subcontent{{/inline}}{{/layout}}'; var partials = { doctype: 'doctype{{> content}}', - layout: '{{#> doctype}}{{#*inline "content"}}layout{{> subcontent}}{{/inline}}{{/doctype}}' + layout: + '{{#> doctype}}{{#*inline "content"}}layout{{> subcontent}}{{/inline}}{{/doctype}}' }; - shouldCompileToWithPartials(string, [{}, {}, partials], true, 'doctypelayoutsubcontent'); + shouldCompileToWithPartials( + string, + [{}, {}, partials], + true, + 'doctypelayoutsubcontent' + ); }); it('GH-1089: should support failover content in multiple levels of inline partials', function() { var string = '{{#> layout}}{{/layout}}'; var partials = { doctype: 'doctype{{> content}}', - layout: '{{#> doctype}}{{#*inline "content"}}layout{{#> subcontent}}subcontent{{/subcontent}}{{/inline}}{{/doctype}}' + layout: + '{{#> doctype}}{{#*inline "content"}}layout{{#> subcontent}}subcontent{{/subcontent}}{{/inline}}{{/doctype}}' }; - shouldCompileToWithPartials(string, [{}, {}, partials], true, 'doctypelayoutsubcontent'); + shouldCompileToWithPartials( + string, + [{}, {}, partials], + true, + 'doctypelayoutsubcontent' + ); }); it('GH-1099: should support greater than 3 nested levels of inline partials', function() { var string = '{{#> layout}}Outer{{/layout}}'; @@ -249,51 +330,63 @@ describe('Regressions', function() { }); it('GH-1135 : Context handling within each iteration', function() { - var obj = {array: [1], name: 'John'}; + var obj = { array: [1], name: 'John' }; var helpers = { myif: function(conditional, options) { if (conditional) { - return options.fn(this); + return options.fn(this); } else { - return options.inverse(this); + return options.inverse(this); } } }; shouldCompileTo( - '{{#each array}}\n' - + ' 1. IF: {{#if true}}{{../name}}-{{../../name}}-{{../../../name}}{{/if}}\n' - + ' 2. MYIF: {{#myif true}}{{../name}}={{../../name}}={{../../../name}}{{/myif}}\n' - + '{{/each}}', [obj, helpers], - ' 1. IF: John--\n' - + ' 2. MYIF: John==\n'); + '{{#each array}}\n' + + ' 1. IF: {{#if true}}{{../name}}-{{../../name}}-{{../../../name}}{{/if}}\n' + + ' 2. MYIF: {{#myif true}}{{../name}}={{../../name}}={{../../../name}}{{/myif}}\n' + + '{{/each}}', + [obj, helpers], + ' 1. IF: John--\n' + ' 2. MYIF: John==\n' + ); }); it('GH-1186: Support block params for existing programs', function() { var string = - '{{#*inline "test"}}{{> @partial-block }}{{/inline}}' - + '{{#>test }}{{#each listOne as |item|}}{{ item }}{{/each}}{{/test}}' - + '{{#>test }}{{#each listTwo as |item|}}{{ item }}{{/each}}{{/test}}'; + '{{#*inline "test"}}{{> @partial-block }}{{/inline}}' + + '{{#>test }}{{#each listOne as |item|}}{{ item }}{{/each}}{{/test}}' + + '{{#>test }}{{#each listTwo as |item|}}{{ item }}{{/each}}{{/test}}'; - shouldCompileTo(string, { listOne: ['a'], listTwo: ['b']}, 'ab', ''); + shouldCompileTo(string, { listOne: ['a'], listTwo: ['b'] }, 'ab', ''); }); it('GH-1319: "unless" breaks when "each" value equals "null"', function() { - var string = '{{#each list}}{{#unless ./prop}}parent={{../value}} {{/unless}}{{/each}}'; - shouldCompileTo(string, { value: 'parent', list: [ null, 'a'] }, 'parent=parent parent=parent ', ''); + var string = + '{{#each list}}{{#unless ./prop}}parent={{../value}} {{/unless}}{{/each}}'; + shouldCompileTo( + string, + { value: 'parent', list: [null, 'a'] }, + 'parent=parent parent=parent ', + '' + ); }); it('GH-1341: 4.0.7 release breaks {{#if @partial-block}} usage', function() { var string = 'template {{>partial}} template'; var partials = { - partialWithBlock: '{{#if @partial-block}} block {{> @partial-block}} block {{/if}}', + partialWithBlock: + '{{#if @partial-block}} block {{> @partial-block}} block {{/if}}', partial: '{{#> partialWithBlock}} partial {{/partialWithBlock}}' }; - shouldCompileToWithPartials(string, [{}, {}, partials], true, 'template block partial block template'); + shouldCompileToWithPartials( + string, + [{}, {}, partials], + true, + 'template block partial block template' + ); }); describe('GH-1561: 4.3.x should still work with precompiled templates from 4.0.0 <= x < 4.3.0', function() { - it('should compile and execute templates', function() { var newHandlebarsInstance = Handlebars.create(); @@ -301,31 +394,53 @@ describe('Regressions', function() { newHandlebarsInstance.registerHelper('loud', function(value) { return value.toUpperCase(); }); - var result = newHandlebarsInstance.templates['test.hbs']({name: 'yehuda'}); + var result = newHandlebarsInstance.templates['test.hbs']({ + name: 'yehuda' + }); equals(result.trim(), 'YEHUDA'); }); it('should call "helperMissing" if a helper is missing', function() { var newHandlebarsInstance = Handlebars.create(); - shouldThrow(function() { - registerTemplate(newHandlebarsInstance); - newHandlebarsInstance.templates['test.hbs']({}); - }, Handlebars.Exception, 'Missing helper: "loud"'); + shouldThrow( + function() { + registerTemplate(newHandlebarsInstance); + newHandlebarsInstance.templates['test.hbs']({}); + }, + Handlebars.Exception, + 'Missing helper: "loud"' + ); }); // This is a only slightly modified precompiled templated from compiled with 4.2.1 function registerTemplate(Handlebars) { - var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {}; - templates['test.hbs'] = template({'compiler': [7, '>= 4.0.0'], 'main': function(container, depth0, helpers, partials, data) { - return container.escapeExpression((helpers.loud || (depth0 && depth0.loud) || helpers.helperMissing).call(depth0 != null ? depth0 : (container.nullContext || {}), (depth0 != null ? depth0.name : depth0), {'name': 'loud', 'hash': {}, 'data': data})) - + '\n\n'; - }, 'useData': true}); + var template = Handlebars.template, + templates = (Handlebars.templates = Handlebars.templates || {}); + templates['test.hbs'] = template({ + compiler: [7, '>= 4.0.0'], + main: function(container, depth0, helpers, partials, data) { + return ( + container.escapeExpression( + ( + helpers.loud || + (depth0 && depth0.loud) || + helpers.helperMissing + ).call( + depth0 != null ? depth0 : container.nullContext || {}, + depth0 != null ? depth0.name : depth0, + { name: 'loud', hash: {}, data: data } + ) + ) + '\n\n' + ); + }, + useData: true + }); } }); it('should allow hash with protected array names', function() { - var obj = {array: [1], name: 'John'}; + var obj = { array: [1], name: 'John' }; var helpers = { helpa: function(options) { return options.hash.length; @@ -352,7 +467,7 @@ describe('Regressions', function() { it('should only compile global partials once', function() { var templateSpy = sinon.spy(newHandlebarsInstance, 'template'); newHandlebarsInstance.registerPartial({ - 'dude': 'I am a partial' + dude: 'I am a partial' }); var string = 'Dudes: {{> dude}} {{> dude}}'; newHandlebarsInstance.compile(string)(); // This should compile template + partial once diff --git a/spec/require.js b/spec/require.js index 6c0b5ae46..f8ddba59a 100644 --- a/spec/require.js +++ b/spec/require.js @@ -5,7 +5,7 @@ if (typeof require !== 'undefined' && require.extensions['.handlebars']) { equal(template, require('./artifacts/example_1.handlebars')); var expected = 'foo\n'; - var result = template({foo: 'foo'}); + var result = template({ foo: 'foo' }); equal(result, expected); }); @@ -15,7 +15,7 @@ if (typeof require !== 'undefined' && require.extensions['.handlebars']) { equal(template, require('./artifacts/example_2.hbs')); var expected = 'Hello, World!\n'; - var result = template({name: 'World'}); + var result = template({ name: 'World' }); equal(result, expected); }); diff --git a/spec/runtime.js b/spec/runtime.js index 3549640b7..7924a5f00 100644 --- a/spec/runtime.js +++ b/spec/runtime.js @@ -1,34 +1,58 @@ describe('runtime', function() { describe('#template', function() { it('should throw on invalid templates', function() { - shouldThrow(function() { - Handlebars.template({}); - }, Error, 'Unknown template object: object'); - shouldThrow(function() { - Handlebars.template(); - }, Error, 'Unknown template object: undefined'); - shouldThrow(function() { - Handlebars.template(''); - }, Error, 'Unknown template object: string'); + shouldThrow( + function() { + Handlebars.template({}); + }, + Error, + 'Unknown template object: object' + ); + shouldThrow( + function() { + Handlebars.template(); + }, + Error, + 'Unknown template object: undefined' + ); + shouldThrow( + function() { + Handlebars.template(''); + }, + Error, + 'Unknown template object: string' + ); }); it('should throw on version mismatch', function() { - shouldThrow(function() { - Handlebars.template({ - main: {}, - compiler: [Handlebars.COMPILER_REVISION + 1] - }); - }, Error, /Template was precompiled with a newer version of Handlebars than the current runtime/); - shouldThrow(function() { - Handlebars.template({ - main: {}, - compiler: [Handlebars.LAST_COMPATIBLE_COMPILER_REVISION - 1] - }); - }, Error, /Template was precompiled with an older version of Handlebars than the current runtime/); - shouldThrow(function() { - Handlebars.template({ - main: {} - }); - }, Error, /Template was precompiled with an older version of Handlebars than the current runtime/); + shouldThrow( + function() { + Handlebars.template({ + main: {}, + compiler: [Handlebars.COMPILER_REVISION + 1] + }); + }, + Error, + /Template was precompiled with a newer version of Handlebars than the current runtime/ + ); + shouldThrow( + function() { + Handlebars.template({ + main: {}, + compiler: [Handlebars.LAST_COMPATIBLE_COMPILER_REVISION - 1] + }); + }, + Error, + /Template was precompiled with an older version of Handlebars than the current runtime/ + ); + shouldThrow( + function() { + Handlebars.template({ + main: {} + }); + }, + Error, + /Template was precompiled with an older version of Handlebars than the current runtime/ + ); }); }); @@ -38,34 +62,42 @@ describe('runtime', function() { } it('should throw for depthed methods without depths', function() { - shouldThrow(function() { - var template = Handlebars.compile('{{#foo}}{{../bar}}{{/foo}}'); - // Calling twice to hit the non-compiled case. - template._setup({}); - template._setup({}); - template._child(1); - }, Error, 'must pass parent depths'); + shouldThrow( + function() { + var template = Handlebars.compile('{{#foo}}{{../bar}}{{/foo}}'); + // Calling twice to hit the non-compiled case. + template._setup({}); + template._setup({}); + template._child(1); + }, + Error, + 'must pass parent depths' + ); }); it('should throw for block param methods without params', function() { - shouldThrow(function() { - var template = Handlebars.compile('{{#foo as |foo|}}{{foo}}{{/foo}}'); - // Calling twice to hit the non-compiled case. - template._setup({}); - template._setup({}); - template._child(1); - }, Error, 'must pass block params'); + shouldThrow( + function() { + var template = Handlebars.compile('{{#foo as |foo|}}{{foo}}{{/foo}}'); + // Calling twice to hit the non-compiled case. + template._setup({}); + template._setup({}); + template._child(1); + }, + Error, + 'must pass block params' + ); }); it('should expose child template', function() { var template = Handlebars.compile('{{#foo}}bar{{/foo}}'); - // Calling twice to hit the non-compiled case. + // Calling twice to hit the non-compiled case. equal(template._child(1)(), 'bar'); equal(template._child(1)(), 'bar'); }); it('should render depthed content', function() { var template = Handlebars.compile('{{#foo}}{{../bar}}{{/foo}}'); - // Calling twice to hit the non-compiled case. - equal(template._child(1, undefined, [], [{bar: 'baz'}])(), 'baz'); + // Calling twice to hit the non-compiled case. + equal(template._child(1, undefined, [], [{ bar: 'baz' }])(), 'baz'); }); }); diff --git a/spec/security.js b/spec/security.js index 18bfa5dd7..ed0189281 100644 --- a/spec/security.js +++ b/spec/security.js @@ -1,144 +1,208 @@ describe('security issues', function() { - describe('GH-1495: Prevent Remote Code Execution via constructor', function() { - it('should not allow constructors to be accessed', function() { - expectTemplate('{{lookup (lookup this "constructor") "name"}}') - .withInput({}) - .toCompileTo(''); - - expectTemplate('{{constructor.name}}') - .withInput({}) - .toCompileTo(''); - }); - - it('GH-1603: should not allow constructors to be accessed (lookup via toString)', function() { - expectTemplate('{{lookup (lookup this (list "constructor")) "name"}}') - .withInput({}) - .withHelper('list', function(element) { - return [element]; - }) - .toCompileTo(''); - }); - - - it('should allow the "constructor" property to be accessed if it is enumerable', function() { - shouldCompileTo('{{constructor.name}}', {'constructor': { - 'name': 'here we go' - }}, 'here we go'); - shouldCompileTo('{{lookup (lookup this "constructor") "name"}}', {'constructor': { - 'name': 'here we go' - }}, 'here we go'); - }); - - it('should allow the "constructor" property to be accessed if it is enumerable', function() { - shouldCompileTo('{{lookup (lookup this "constructor") "name"}}', {'constructor': { - 'name': 'here we go' - }}, 'here we go'); - }); - - - it('should allow prototype properties that are not constructors', function() { - function TestClass() { - } - - Object.defineProperty(TestClass.prototype, 'abc', { - get: function() { - return 'xyz'; - } + describe('GH-1495: Prevent Remote Code Execution via constructor', function() { + it('should not allow constructors to be accessed', function() { + expectTemplate('{{lookup (lookup this "constructor") "name"}}') + .withInput({}) + .toCompileTo(''); + + expectTemplate('{{constructor.name}}') + .withInput({}) + .toCompileTo(''); + }); - }); + it('GH-1603: should not allow constructors to be accessed (lookup via toString)', function() { + expectTemplate('{{lookup (lookup this (list "constructor")) "name"}}') + .withInput({}) + .withHelper('list', function(element) { + return [element]; + }) + .toCompileTo(''); + }); - shouldCompileTo('{{#with this as |obj|}}{{obj.abc}}{{/with}}', - new TestClass(), 'xyz'); - shouldCompileTo('{{#with this as |obj|}}{{lookup obj "abc"}}{{/with}}', - new TestClass(), 'xyz'); + it('should allow the "constructor" property to be accessed if it is enumerable', function() { + shouldCompileTo( + '{{constructor.name}}', + { + constructor: { + name: 'here we go' + } + }, + 'here we go' + ); + shouldCompileTo( + '{{lookup (lookup this "constructor") "name"}}', + { + constructor: { + name: 'here we go' + } + }, + 'here we go' + ); + }); - }); + it('should allow the "constructor" property to be accessed if it is enumerable', function() { + shouldCompileTo( + '{{lookup (lookup this "constructor") "name"}}', + { + constructor: { + name: 'here we go' + } + }, + 'here we go' + ); }); - describe('GH-1558: Prevent explicit call of helperMissing-helpers', function() { - if (!Handlebars.compile) { - return; - } + it('should allow prototype properties that are not constructors', function() { + function TestClass() {} - describe('without the option "allowExplicitCallOfHelperMissing"', function() { - it('should throw an exception when calling "{{helperMissing}}" ', function() { - shouldThrow(function() { - var template = Handlebars.compile('{{helperMissing}}'); - template({}); - }, Error); - }); - it('should throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', function() { - shouldThrow(function() { - var template = Handlebars.compile('{{#helperMissing}}{{/helperMissing}}'); - template({}); - }, Error); - }); - it('should throw an exception when calling "{{blockHelperMissing "abc" .}}" ', function() { - var functionCalls = []; - expect(function() { - var template = Handlebars.compile('{{blockHelperMissing "abc" .}}'); - template({ fn: function() { functionCalls.push('called'); }}); - }).to.throw(Error); - expect(functionCalls.length).to.equal(0); - }); - it('should throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', function() { - shouldThrow(function() { - var template = Handlebars.compile('{{#blockHelperMissing .}}{{/blockHelperMissing}}'); - template({ fn: function() { return 'functionInData';}}); - }, Error); - }); - }); + Object.defineProperty(TestClass.prototype, 'abc', { + get: function() { + return 'xyz'; + } + }); - describe('with the option "allowCallsToHelperMissing" set to true', function() { - it('should not throw an exception when calling "{{helperMissing}}" ', function() { - var template = Handlebars.compile('{{helperMissing}}'); - template({}, {allowCallsToHelperMissing: true}); - }); - it('should not throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', function() { - var template = Handlebars.compile('{{#helperMissing}}{{/helperMissing}}'); - template({}, {allowCallsToHelperMissing: true}); - }); - it('should not throw an exception when calling "{{blockHelperMissing "abc" .}}" ', function() { - var functionCalls = []; - var template = Handlebars.compile('{{blockHelperMissing "abc" .}}'); - template({ fn: function() { functionCalls.push('called'); }}, {allowCallsToHelperMissing: true}); - equals(functionCalls.length, 1); - }); - it('should not throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', function() { - var template = Handlebars.compile('{{#blockHelperMissing true}}sdads{{/blockHelperMissing}}'); - template({}, {allowCallsToHelperMissing: true}); - }); - }); + shouldCompileTo( + '{{#with this as |obj|}}{{obj.abc}}{{/with}}', + new TestClass(), + 'xyz' + ); + shouldCompileTo( + '{{#with this as |obj|}}{{lookup obj "abc"}}{{/with}}', + new TestClass(), + 'xyz' + ); }); - - describe('GH-1563', function() { - it('should not allow to access constructor after overriding via __defineGetter__', function() { - if (({}).__defineGetter__ == null || ({}).__lookupGetter__ == null) { - return this.skip(); // Browser does not support this exploit anyway + }); + + describe('GH-1558: Prevent explicit call of helperMissing-helpers', function() { + if (!Handlebars.compile) { + return; + } + + describe('without the option "allowExplicitCallOfHelperMissing"', function() { + it('should throw an exception when calling "{{helperMissing}}" ', function() { + shouldThrow(function() { + var template = Handlebars.compile('{{helperMissing}}'); + template({}); + }, Error); + }); + it('should throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', function() { + shouldThrow(function() { + var template = Handlebars.compile( + '{{#helperMissing}}{{/helperMissing}}' + ); + template({}); + }, Error); + }); + it('should throw an exception when calling "{{blockHelperMissing "abc" .}}" ', function() { + var functionCalls = []; + expect(function() { + var template = Handlebars.compile('{{blockHelperMissing "abc" .}}'); + template({ + fn: function() { + functionCalls.push('called'); } - expectTemplate('{{__defineGetter__ "undefined" valueOf }}' + - '{{#with __lookupGetter__ }}' + - '{{__defineGetter__ "propertyIsEnumerable" (this.bind (this.bind 1)) }}' + - '{{constructor.name}}' + - '{{/with}}') - .withInput({}) - .toThrow(/Missing helper: "__defineGetter__"/); - }); + }); + }).to.throw(Error); + expect(functionCalls.length).to.equal(0); + }); + it('should throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', function() { + shouldThrow(function() { + var template = Handlebars.compile( + '{{#blockHelperMissing .}}{{/blockHelperMissing}}' + ); + template({ + fn: function() { + return 'functionInData'; + } + }); + }, Error); + }); }); - describe('GH-1595', function() { - it('properties, that are required to be enumerable', function() { - expectTemplate('{{constructor}}').withInput({}).toCompileTo(''); - expectTemplate('{{__defineGetter__}}').withInput({}).toCompileTo(''); - expectTemplate('{{__defineSetter__}}').withInput({}).toCompileTo(''); - expectTemplate('{{__lookupGetter__}}').withInput({}).toCompileTo(''); - expectTemplate('{{__proto__}}').withInput({}).toCompileTo(''); - - expectTemplate('{{lookup "constructor"}}').withInput({}).toCompileTo(''); - expectTemplate('{{lookup "__defineGetter__"}}').withInput({}).toCompileTo(''); - expectTemplate('{{lookup "__defineSetter__"}}').withInput({}).toCompileTo(''); - expectTemplate('{{lookup "__lookupGetter__"}}').withInput({}).toCompileTo(''); - expectTemplate('{{lookup "__proto__"}}').withInput({}).toCompileTo(''); + describe('with the option "allowCallsToHelperMissing" set to true', function() { + it('should not throw an exception when calling "{{helperMissing}}" ', function() { + var template = Handlebars.compile('{{helperMissing}}'); + template({}, { allowCallsToHelperMissing: true }); + }); + it('should not throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', function() { + var template = Handlebars.compile( + '{{#helperMissing}}{{/helperMissing}}' + ); + template({}, { allowCallsToHelperMissing: true }); + }); + it('should not throw an exception when calling "{{blockHelperMissing "abc" .}}" ', function() { + var functionCalls = []; + var template = Handlebars.compile('{{blockHelperMissing "abc" .}}'); + template( + { + fn: function() { + functionCalls.push('called'); + } + }, + { allowCallsToHelperMissing: true } + ); + equals(functionCalls.length, 1); }); + it('should not throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', function() { + var template = Handlebars.compile( + '{{#blockHelperMissing true}}sdads{{/blockHelperMissing}}' + ); + template({}, { allowCallsToHelperMissing: true }); + }); + }); + }); + + describe('GH-1563', function() { + it('should not allow to access constructor after overriding via __defineGetter__', function() { + if ({}.__defineGetter__ == null || {}.__lookupGetter__ == null) { + return this.skip(); // Browser does not support this exploit anyway + } + expectTemplate( + '{{__defineGetter__ "undefined" valueOf }}' + + '{{#with __lookupGetter__ }}' + + '{{__defineGetter__ "propertyIsEnumerable" (this.bind (this.bind 1)) }}' + + '{{constructor.name}}' + + '{{/with}}' + ) + .withInput({}) + .toThrow(/Missing helper: "__defineGetter__"/); + }); + }); + + describe('GH-1595', function() { + it('properties, that are required to be enumerable', function() { + expectTemplate('{{constructor}}') + .withInput({}) + .toCompileTo(''); + expectTemplate('{{__defineGetter__}}') + .withInput({}) + .toCompileTo(''); + expectTemplate('{{__defineSetter__}}') + .withInput({}) + .toCompileTo(''); + expectTemplate('{{__lookupGetter__}}') + .withInput({}) + .toCompileTo(''); + expectTemplate('{{__proto__}}') + .withInput({}) + .toCompileTo(''); + + expectTemplate('{{lookup "constructor"}}') + .withInput({}) + .toCompileTo(''); + expectTemplate('{{lookup "__defineGetter__"}}') + .withInput({}) + .toCompileTo(''); + expectTemplate('{{lookup "__defineSetter__"}}') + .withInput({}) + .toCompileTo(''); + expectTemplate('{{lookup "__lookupGetter__"}}') + .withInput({}) + .toCompileTo(''); + expectTemplate('{{lookup "__proto__"}}') + .withInput({}) + .toCompileTo(''); }); + }); }); diff --git a/spec/source-map.js b/spec/source-map.js index 54e5e4d91..024aaae60 100644 --- a/spec/source-map.js +++ b/spec/source-map.js @@ -1,7 +1,7 @@ try { if (typeof define !== 'function' || !define.amd) { var SourceMap = require('source-map'), - SourceMapConsumer = SourceMap.SourceMapConsumer; + SourceMapConsumer = SourceMap.SourceMapConsumer; } } catch (err) { /* NOP for in browser */ @@ -13,22 +13,28 @@ describe('source-map', function() { } it('should safely include source map info', function() { - var template = Handlebars.precompile('{{hello}}', {destName: 'dest.js', srcName: 'src.hbs'}); + var template = Handlebars.precompile('{{hello}}', { + destName: 'dest.js', + srcName: 'src.hbs' + }); equal(!!template.code, true); equal(!!template.map, !CompilerContext.browser); }); it('should map source properly', function() { - var templateSource = ' b{{hello}} \n {{bar}}a {{#block arg hash=(subex 1 subval)}}{{/block}}', - template = Handlebars.precompile(templateSource, {destName: 'dest.js', srcName: 'src.hbs'}); + var templateSource = + ' b{{hello}} \n {{bar}}a {{#block arg hash=(subex 1 subval)}}{{/block}}', + template = Handlebars.precompile(templateSource, { + destName: 'dest.js', + srcName: 'src.hbs' + }); if (template.map) { var consumer = new SourceMapConsumer(template.map), - lines = template.code.split('\n'), - srcLines = templateSource.split('\n'), - - generated = grepLine('" b"', lines), - source = grepLine(' b', srcLines); + lines = template.code.split('\n'), + srcLines = templateSource.split('\n'), + generated = grepLine('" b"', lines), + source = grepLine(' b', srcLines); var mapped = consumer.originalPositionFor(generated); equal(mapped.line, source.line); diff --git a/spec/spec.js b/spec/spec.js index 805609d01..ff4d7bc98 100644 --- a/spec/spec.js +++ b/spec/spec.js @@ -5,11 +5,11 @@ describe('spec', function() { } var _ = require('underscore'), - fs = require('fs'); + fs = require('fs'); var specDir = __dirname + '/mustache/specs/'; var specs = _.filter(fs.readdirSync(specDir), function(name) { - return (/.*\.json$/).test(name); + return /.*\.json$/.test(name); }); _.each(specs, function(name) { @@ -17,16 +17,17 @@ describe('spec', function() { _.each(spec.tests, function(test) { // Our lambda implementation knowingly deviates from the optional Mustace lambda spec // We also do not support alternative delimeters - if (name === '~lambdas.json' - - // We also choose to throw if paritals are not found - || (name === 'partials.json' && test.name === 'Failed Lookup') - - // We nest the entire response from partials, not just the literals - || (name === 'partials.json' && test.name === 'Standalone Indentation') - - || (/\{\{=/).test(test.template) - || _.any(test.partials, function(partial) { return (/\{\{=/).test(partial); })) { + if ( + name === '~lambdas.json' || + // We also choose to throw if paritals are not found + (name === 'partials.json' && test.name === 'Failed Lookup') || + // We nest the entire response from partials, not just the literals + (name === 'partials.json' && test.name === 'Standalone Indentation') || + /\{\{=/.test(test.template) || + _.any(test.partials, function(partial) { + return /\{\{=/.test(partial); + }) + ) { it.skip(name + ' - ' + test.name); return; } @@ -40,9 +41,20 @@ describe('spec', function() { } it(name + ' - ' + test.name, function() { if (test.partials) { - shouldCompileToWithPartials(test.template, [data, {}, test.partials, true], true, test.expected, test.desc + ' "' + test.template + '"'); + shouldCompileToWithPartials( + test.template, + [data, {}, test.partials, true], + true, + test.expected, + test.desc + ' "' + test.template + '"' + ); } else { - shouldCompileTo(test.template, [data, {}, {}, true], test.expected, test.desc + ' "' + test.template + '"'); + shouldCompileTo( + test.template, + [data, {}, {}, true], + test.expected, + test.desc + ' "' + test.template + '"' + ); } }); }); diff --git a/spec/strict.js b/spec/strict.js index 3bd7cc18e..680bbc0d8 100644 --- a/spec/strict.js +++ b/spec/strict.js @@ -3,43 +3,58 @@ var Exception = Handlebars.Exception; describe('strict', function() { describe('strict mode', function() { it('should error on missing property lookup', function() { - shouldThrow(function() { - var template = CompilerContext.compile('{{hello}}', {strict: true}); + shouldThrow( + function() { + var template = CompilerContext.compile('{{hello}}', { strict: true }); - template({}); - }, Exception, /"hello" not defined in/); + template({}); + }, + Exception, + /"hello" not defined in/ + ); }); it('should error on missing child', function() { - var template = CompilerContext.compile('{{hello.bar}}', {strict: true}); - equals(template({hello: {bar: 'foo'}}), 'foo'); + var template = CompilerContext.compile('{{hello.bar}}', { strict: true }); + equals(template({ hello: { bar: 'foo' } }), 'foo'); - shouldThrow(function() { - template({hello: {}}); - }, Exception, /"bar" not defined in/); + shouldThrow( + function() { + template({ hello: {} }); + }, + Exception, + /"bar" not defined in/ + ); }); it('should handle explicit undefined', function() { - var template = CompilerContext.compile('{{hello.bar}}', {strict: true}); + var template = CompilerContext.compile('{{hello.bar}}', { strict: true }); - equals(template({hello: {bar: undefined}}), ''); + equals(template({ hello: { bar: undefined } }), ''); }); it('should error on missing property lookup in known helpers mode', function() { - shouldThrow(function() { - var template = CompilerContext.compile('{{hello}}', {strict: true, knownHelpersOnly: true}); - - template({}); - }, Exception, /"hello" not defined in/); + shouldThrow( + function() { + var template = CompilerContext.compile('{{hello}}', { + strict: true, + knownHelpersOnly: true + }); + + template({}); + }, + Exception, + /"hello" not defined in/ + ); }); it('should error on missing context', function() { shouldThrow(function() { - var template = CompilerContext.compile('{{hello}}', {strict: true}); + var template = CompilerContext.compile('{{hello}}', { strict: true }); template(); }, Error); }); it('should error on missing data lookup', function() { - var template = CompilerContext.compile('{{@hello}}', {strict: true}); - equals(template(undefined, {data: {hello: 'foo'}}), 'foo'); + var template = CompilerContext.compile('{{@hello}}', { strict: true }); + equals(template(undefined, { data: { hello: 'foo' } }), 'foo'); shouldThrow(function() { template(); @@ -47,45 +62,81 @@ describe('strict', function() { }); it('should not run helperMissing for helper calls', function() { - shouldThrow(function() { - var template = CompilerContext.compile('{{hello foo}}', {strict: true}); - - template({foo: true}); - }, Exception, /"hello" not defined in/); - - shouldThrow(function() { - var template = CompilerContext.compile('{{#hello foo}}{{/hello}}', {strict: true}); - - template({foo: true}); - }, Exception, /"hello" not defined in/); + shouldThrow( + function() { + var template = CompilerContext.compile('{{hello foo}}', { + strict: true + }); + + template({ foo: true }); + }, + Exception, + /"hello" not defined in/ + ); + + shouldThrow( + function() { + var template = CompilerContext.compile('{{#hello foo}}{{/hello}}', { + strict: true + }); + + template({ foo: true }); + }, + Exception, + /"hello" not defined in/ + ); }); it('should throw on ambiguous blocks', function() { - shouldThrow(function() { - var template = CompilerContext.compile('{{#hello}}{{/hello}}', {strict: true}); - - template({}); - }, Exception, /"hello" not defined in/); - - shouldThrow(function() { - var template = CompilerContext.compile('{{^hello}}{{/hello}}', {strict: true}); - - template({}); - }, Exception, /"hello" not defined in/); - - shouldThrow(function() { - var template = CompilerContext.compile('{{#hello.bar}}{{/hello.bar}}', {strict: true}); - - template({hello: {}}); - }, Exception, /"bar" not defined in/); + shouldThrow( + function() { + var template = CompilerContext.compile('{{#hello}}{{/hello}}', { + strict: true + }); + + template({}); + }, + Exception, + /"hello" not defined in/ + ); + + shouldThrow( + function() { + var template = CompilerContext.compile('{{^hello}}{{/hello}}', { + strict: true + }); + + template({}); + }, + Exception, + /"hello" not defined in/ + ); + + shouldThrow( + function() { + var template = CompilerContext.compile( + '{{#hello.bar}}{{/hello.bar}}', + { strict: true } + ); + + template({ hello: {} }); + }, + Exception, + /"bar" not defined in/ + ); }); it('should allow undefined parameters when passed to helpers', function() { - var template = CompilerContext.compile('{{#unless foo}}success{{/unless}}', {strict: true}); + var template = CompilerContext.compile( + '{{#unless foo}}success{{/unless}}', + { strict: true } + ); equals(template({}), 'success'); }); it('should allow undefined hash when passed to helpers', function() { - var template = CompilerContext.compile('{{helper value=@foo}}', {strict: true}); + var template = CompilerContext.compile('{{helper value=@foo}}', { + strict: true + }); var helpers = { helper: function(options) { equals('value' in options.hash, true); @@ -93,19 +144,27 @@ describe('strict', function() { return 'success'; } }; - equals(template({}, {helpers: helpers}), 'success'); + equals(template({}, { helpers: helpers }), 'success'); }); it('should show error location on missing property lookup', function() { - shouldThrow(function() { - var template = CompilerContext.compile('\n\n\n {{hello}}', {strict: true}); - template({}); - }, Exception, '"hello" not defined in [object Object] - 4:5'); + shouldThrow( + function() { + var template = CompilerContext.compile('\n\n\n {{hello}}', { + strict: true + }); + template({}); + }, + Exception, + '"hello" not defined in [object Object] - 4:5' + ); }); it('should error contains correct location properties on missing property lookup', function() { try { - var template = CompilerContext.compile('\n\n\n {{hello}}', {strict: true}); + var template = CompilerContext.compile('\n\n\n {{hello}}', { + strict: true + }); template({}); } catch (error) { equals(error.lineNumber, 4); @@ -118,25 +177,33 @@ describe('strict', function() { describe('assume objects', function() { it('should ignore missing property', function() { - var template = CompilerContext.compile('{{hello}}', {assumeObjects: true}); + var template = CompilerContext.compile('{{hello}}', { + assumeObjects: true + }); equal(template({}), ''); }); it('should ignore missing child', function() { - var template = CompilerContext.compile('{{hello.bar}}', {assumeObjects: true}); + var template = CompilerContext.compile('{{hello.bar}}', { + assumeObjects: true + }); - equal(template({hello: {}}), ''); + equal(template({ hello: {} }), ''); }); it('should error on missing object', function() { shouldThrow(function() { - var template = CompilerContext.compile('{{hello.bar}}', {assumeObjects: true}); + var template = CompilerContext.compile('{{hello.bar}}', { + assumeObjects: true + }); template({}); }, Error); }); it('should error on missing context', function() { shouldThrow(function() { - var template = CompilerContext.compile('{{hello}}', {assumeObjects: true}); + var template = CompilerContext.compile('{{hello}}', { + assumeObjects: true + }); template(); }, Error); @@ -144,14 +211,18 @@ describe('strict', function() { it('should error on missing data lookup', function() { shouldThrow(function() { - var template = CompilerContext.compile('{{@hello.bar}}', {assumeObjects: true}); + var template = CompilerContext.compile('{{@hello.bar}}', { + assumeObjects: true + }); template(); }, Error); }); it('should execute blockHelperMissing', function() { - var template = CompilerContext.compile('{{^hello}}foo{{/hello}}', {assumeObjects: true}); + var template = CompilerContext.compile('{{^hello}}foo{{/hello}}', { + assumeObjects: true + }); equals(template({}), 'foo'); }); diff --git a/spec/string-params.js b/spec/string-params.js index b76f291f7..9f83611a4 100644 --- a/spec/string-params.js +++ b/spec/string-params.js @@ -1,6 +1,8 @@ describe('string params mode', function() { it('arguments to helpers can be retrieved from options hash in string form', function() { - var template = CompilerContext.compile('{{wycats is.a slave.driver}}', {stringParams: true}); + var template = CompilerContext.compile('{{wycats is.a slave.driver}}', { + stringParams: true + }); var helpers = { wycats: function(passiveVoice, noun) { @@ -8,57 +10,99 @@ describe('string params mode', function() { } }; - var result = template({}, {helpers: helpers}); + var result = template({}, { helpers: helpers }); - equals(result, 'HELP ME MY BOSS is.a slave.driver', 'String parameters output'); + equals( + result, + 'HELP ME MY BOSS is.a slave.driver', + 'String parameters output' + ); }); it('when using block form, arguments to helpers can be retrieved from options hash in string form', function() { - var template = CompilerContext.compile('{{#wycats is.a slave.driver}}help :({{/wycats}}', {stringParams: true}); + var template = CompilerContext.compile( + '{{#wycats is.a slave.driver}}help :({{/wycats}}', + { stringParams: true } + ); var helpers = { wycats: function(passiveVoice, noun, options) { - return 'HELP ME MY BOSS ' + passiveVoice + ' ' + - noun + ': ' + options.fn(this); + return ( + 'HELP ME MY BOSS ' + + passiveVoice + + ' ' + + noun + + ': ' + + options.fn(this) + ); } }; - var result = template({}, {helpers: helpers}); + var result = template({}, { helpers: helpers }); - equals(result, 'HELP ME MY BOSS is.a slave.driver: help :(', 'String parameters output'); + equals( + result, + 'HELP ME MY BOSS is.a slave.driver: help :(', + 'String parameters output' + ); }); it('when inside a block in String mode, .. passes the appropriate context in the options hash', function() { - var template = CompilerContext.compile('{{#with dale}}{{tomdale ../need dad.joke}}{{/with}}', {stringParams: true}); + var template = CompilerContext.compile( + '{{#with dale}}{{tomdale ../need dad.joke}}{{/with}}', + { stringParams: true } + ); var helpers = { tomdale: function(desire, noun, options) { - return 'STOP ME FROM READING HACKER NEWS I ' + - options.contexts[0][desire] + ' ' + noun; + return ( + 'STOP ME FROM READING HACKER NEWS I ' + + options.contexts[0][desire] + + ' ' + + noun + ); }, - 'with': function(context, options) { + with: function(context, options) { return options.fn(options.contexts[0][context]); } }; - var result = template({ - dale: {}, + var result = template( + { + dale: {}, - need: 'need-a' - }, {helpers: helpers}); - - equals(result, 'STOP ME FROM READING HACKER NEWS I need-a dad.joke', 'Proper context variable output'); + need: 'need-a' + }, + { helpers: helpers } + ); + + equals( + result, + 'STOP ME FROM READING HACKER NEWS I need-a dad.joke', + 'Proper context variable output' + ); }); it('information about the types is passed along', function() { - var template = CompilerContext.compile("{{tomdale 'need' dad.joke true false}}", { stringParams: true }); + var template = CompilerContext.compile( + "{{tomdale 'need' dad.joke true false}}", + { stringParams: true } + ); var helpers = { tomdale: function(desire, noun, trueBool, falseBool, options) { equal(options.types[0], 'StringLiteral', 'the string type is passed'); - equal(options.types[1], 'PathExpression', 'the expression type is passed'); - equal(options.types[2], 'BooleanLiteral', 'the expression type is passed'); + equal( + options.types[1], + 'PathExpression', + 'the expression type is passed' + ); + equal( + options.types[2], + 'BooleanLiteral', + 'the expression type is passed' + ); equal(desire, 'need', 'the string form is passed for strings'); equal(noun, 'dad.joke', 'the string form is passed for expressions'); equal(trueBool, true, 'raw booleans are passed through'); @@ -72,7 +116,10 @@ describe('string params mode', function() { }); it('hash parameters get type information', function() { - var template = CompilerContext.compile("{{tomdale he.says desire='need' noun=dad.joke bool=true}}", { stringParams: true }); + var template = CompilerContext.compile( + "{{tomdale he.says desire='need' noun=dad.joke bool=true}}", + { stringParams: true } + ); var helpers = { tomdale: function(exclamation, options) { @@ -94,9 +141,12 @@ describe('string params mode', function() { }); it('hash parameters get context information', function() { - var template = CompilerContext.compile("{{#with dale}}{{tomdale he.says desire='need' noun=../dad/joke bool=true}}{{/with}}", { stringParams: true }); + var template = CompilerContext.compile( + "{{#with dale}}{{tomdale he.says desire='need' noun=../dad/joke bool=true}}{{/with}}", + { stringParams: true } + ); - var context = {dale: {}}; + var context = { dale: {} }; var helpers = { tomdale: function(exclamation, options) { @@ -110,7 +160,7 @@ describe('string params mode', function() { equal(options.hash.bool, true); return 'Helper called'; }, - 'with': function(withContext, options) { + with: function(withContext, options) { return options.fn(options.contexts[0][withContext]); } }; @@ -120,34 +170,52 @@ describe('string params mode', function() { }); it('when inside a block in String mode, .. passes the appropriate context in the options hash to a block helper', function() { - var template = CompilerContext.compile('{{#with dale}}{{#tomdale ../need dad.joke}}wot{{/tomdale}}{{/with}}', {stringParams: true}); + var template = CompilerContext.compile( + '{{#with dale}}{{#tomdale ../need dad.joke}}wot{{/tomdale}}{{/with}}', + { stringParams: true } + ); var helpers = { tomdale: function(desire, noun, options) { - return 'STOP ME FROM READING HACKER NEWS I ' + - options.contexts[0][desire] + ' ' + noun + ' ' + - options.fn(this); + return ( + 'STOP ME FROM READING HACKER NEWS I ' + + options.contexts[0][desire] + + ' ' + + noun + + ' ' + + options.fn(this) + ); }, - 'with': function(context, options) { + with: function(context, options) { return options.fn(options.contexts[0][context]); } }; - var result = template({ - dale: {}, + var result = template( + { + dale: {}, - need: 'need-a' - }, {helpers: helpers}); - - equals(result, 'STOP ME FROM READING HACKER NEWS I need-a dad.joke wot', 'Proper context variable output'); + need: 'need-a' + }, + { helpers: helpers } + ); + + equals( + result, + 'STOP ME FROM READING HACKER NEWS I need-a dad.joke wot', + 'Proper context variable output' + ); }); it('with nested block ambiguous', function() { - var template = CompilerContext.compile('{{#with content}}{{#view}}{{firstName}} {{lastName}}{{/view}}{{/with}}', {stringParams: true}); + var template = CompilerContext.compile( + '{{#with content}}{{#view}}{{firstName}} {{lastName}}{{/view}}{{/with}}', + { stringParams: true } + ); var helpers = { - 'with': function() { + with: function() { return 'WITH'; }, view: function() { @@ -155,12 +223,14 @@ describe('string params mode', function() { } }; - var result = template({}, {helpers: helpers}); + var result = template({}, { helpers: helpers }); equals(result, 'WITH'); }); it('should handle DATA', function() { - var template = CompilerContext.compile('{{foo @bar}}', { stringParams: true }); + var template = CompilerContext.compile('{{foo @bar}}', { + stringParams: true + }); var helpers = { foo: function(bar, options) { diff --git a/spec/subexpressions.js b/spec/subexpressions.js index dad741e4a..228510511 100644 --- a/spec/subexpressions.js +++ b/spec/subexpressions.js @@ -31,7 +31,7 @@ describe('subexpressions', function() { it('mixed paths and helpers', function() { var string = '{{blog baz.bat (equal a b) baz.bar}}'; - var context = { bar: 'LOL', baz: {bat: 'foo!', bar: 'bar!'} }; + var context = { bar: 'LOL', baz: { bat: 'foo!', bar: 'bar!' } }; var helpers = { blog: function(val, that, theOther) { return 'val is ' + val + ', ' + that + ' and ' + theOther; @@ -59,7 +59,7 @@ describe('subexpressions', function() { }); it('GH-800 : Complex subexpressions', function() { - var context = {a: 'a', b: 'b', c: {c: 'c'}, d: 'd', e: {e: 'e'}}; + var context = { a: 'a', b: 'b', c: { c: 'c' }, d: 'd', e: { e: 'e' } }; var helpers = { dash: function(a, b) { return a + '-' + b; @@ -69,7 +69,11 @@ describe('subexpressions', function() { } }; - shouldCompileTo("{{dash 'abc' (concat a b)}}", [context, helpers], 'abc-ab'); + shouldCompileTo( + "{{dash 'abc' (concat a b)}}", + [context, helpers], + 'abc-ab' + ); shouldCompileTo('{{dash d (concat a b)}}', [context, helpers], 'd-ab'); shouldCompileTo('{{dash c.c (concat a b)}}', [context, helpers], 'c-ab'); shouldCompileTo('{{dash (concat a b) c.c}}', [context, helpers], 'ab-c'); @@ -122,24 +126,36 @@ describe('subexpressions', function() { }); it('multiple subexpressions in a hash', function() { - var string = '{{input aria-label=(t "Name") placeholder=(t "Example User")}}'; + var string = + '{{input aria-label=(t "Name") placeholder=(t "Example User")}}'; var helpers = { input: function(options) { var hash = options.hash; var ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']); var placeholder = Handlebars.Utils.escapeExpression(hash.placeholder); - return new Handlebars.SafeString(''); + return new Handlebars.SafeString( + '' + ); }, t: function(defaultString) { return new Handlebars.SafeString(defaultString); } }; - shouldCompileTo(string, [{}, helpers], ''); + shouldCompileTo( + string, + [{}, helpers], + '' + ); }); it('multiple subexpressions in a hash with context', function() { - var string = '{{input aria-label=(t item.field) placeholder=(t item.placeholder)}}'; + var string = + '{{input aria-label=(t item.field) placeholder=(t item.placeholder)}}'; var context = { item: { @@ -153,44 +169,82 @@ describe('subexpressions', function() { var hash = options.hash; var ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']); var placeholder = Handlebars.Utils.escapeExpression(hash.placeholder); - return new Handlebars.SafeString(''); + return new Handlebars.SafeString( + '' + ); }, t: function(defaultString) { return new Handlebars.SafeString(defaultString); } }; - shouldCompileTo(string, [context, helpers], ''); + shouldCompileTo( + string, + [context, helpers], + '' + ); }); it('in string params mode,', function() { - var template = CompilerContext.compile('{{snog (blorg foo x=y) yeah a=b}}', {stringParams: true}); + var template = CompilerContext.compile( + '{{snog (blorg foo x=y) yeah a=b}}', + { stringParams: true } + ); var helpers = { snog: function(a, b, options) { equals(a, 'foo'); - equals(options.types.length, 2, 'string params for outer helper processed correctly'); - equals(options.types[0], 'SubExpression', 'string params for outer helper processed correctly'); - equals(options.types[1], 'PathExpression', 'string params for outer helper processed correctly'); + equals( + options.types.length, + 2, + 'string params for outer helper processed correctly' + ); + equals( + options.types[0], + 'SubExpression', + 'string params for outer helper processed correctly' + ); + equals( + options.types[1], + 'PathExpression', + 'string params for outer helper processed correctly' + ); return a + b; }, blorg: function(a, options) { - equals(options.types.length, 1, 'string params for inner helper processed correctly'); - equals(options.types[0], 'PathExpression', 'string params for inner helper processed correctly'); + equals( + options.types.length, + 1, + 'string params for inner helper processed correctly' + ); + equals( + options.types[0], + 'PathExpression', + 'string params for inner helper processed correctly' + ); return a; } }; - var result = template({ - foo: {}, - yeah: {} - }, {helpers: helpers}); + var result = template( + { + foo: {}, + yeah: {} + }, + { helpers: helpers } + ); equals(result, 'fooyeah'); }); it('as hashes in string params mode', function() { - var template = CompilerContext.compile('{{blog fun=(bork)}}', {stringParams: true}); + var template = CompilerContext.compile('{{blog fun=(bork)}}', { + stringParams: true + }); var helpers = { blog: function(options) { @@ -202,7 +256,7 @@ describe('subexpressions', function() { } }; - var result = template({}, {helpers: helpers}); + var result = template({}, { helpers: helpers }); equals(result, 'val is BORK'); }); diff --git a/spec/tokenizer.js b/spec/tokenizer.js index ec15d597f..ed1666038 100644 --- a/spec/tokenizer.js +++ b/spec/tokenizer.js @@ -15,18 +15,18 @@ describe('Tokenizer', function() { function tokenize(template) { var parser = Handlebars.Parser, - lexer = parser.lexer; + lexer = parser.lexer; lexer.setInput(template); var out = [], - token; + token; while ((token = lexer.lex())) { var result = parser.terminals_[token] || token; if (!result || result === 'EOF' || result === 'INVALID') { break; } - out.push({name: result, text: lexer.yytext}); + out.push({ name: result, text: lexer.yytext }); } return out; @@ -55,7 +55,16 @@ describe('Tokenizer', function() { it('supports escaping delimiters', function() { var result = tokenize('{{foo}} \\{{bar}} {{baz}}'); - shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN', + 'ID', + 'CLOSE', + 'CONTENT', + 'CONTENT', + 'OPEN', + 'ID', + 'CLOSE' + ]); shouldBeToken(result[3], 'CONTENT', ' '); shouldBeToken(result[4], 'CONTENT', '{{bar}} '); @@ -63,7 +72,14 @@ describe('Tokenizer', function() { it('supports escaping multiple delimiters', function() { var result = tokenize('{{foo}} \\{{bar}} \\{{baz}}'); - shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'CONTENT']); + shouldMatchTokens(result, [ + 'OPEN', + 'ID', + 'CLOSE', + 'CONTENT', + 'CONTENT', + 'CONTENT' + ]); shouldBeToken(result[3], 'CONTENT', ' '); shouldBeToken(result[4], 'CONTENT', '{{bar}} '); @@ -72,14 +88,35 @@ describe('Tokenizer', function() { it('supports escaping a triple stash', function() { var result = tokenize('{{foo}} \\{{{bar}}} {{baz}}'); - shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN', + 'ID', + 'CLOSE', + 'CONTENT', + 'CONTENT', + 'OPEN', + 'ID', + 'CLOSE' + ]); shouldBeToken(result[4], 'CONTENT', '{{{bar}}} '); }); it('supports escaping escape character', function() { var result = tokenize('{{foo}} \\\\{{bar}} {{baz}}'); - shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN', + 'ID', + 'CLOSE', + 'CONTENT', + 'OPEN', + 'ID', + 'CLOSE', + 'CONTENT', + 'OPEN', + 'ID', + 'CLOSE' + ]); shouldBeToken(result[3], 'CONTENT', ' \\'); shouldBeToken(result[5], 'ID', 'bar'); @@ -87,7 +124,19 @@ describe('Tokenizer', function() { it('supports escaping multiple escape characters', function() { var result = tokenize('{{foo}} \\\\{{bar}} \\\\{{baz}}'); - shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN', + 'ID', + 'CLOSE', + 'CONTENT', + 'OPEN', + 'ID', + 'CLOSE', + 'CONTENT', + 'OPEN', + 'ID', + 'CLOSE' + ]); shouldBeToken(result[3], 'CONTENT', ' \\'); shouldBeToken(result[5], 'ID', 'bar'); @@ -97,7 +146,18 @@ describe('Tokenizer', function() { it('supports escaped mustaches after escaped escape characters', function() { var result = tokenize('{{foo}} \\\\{{bar}} \\{{baz}}'); - shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'CONTENT']); + shouldMatchTokens(result, [ + 'OPEN', + 'ID', + 'CLOSE', + 'CONTENT', + 'OPEN', + 'ID', + 'CLOSE', + 'CONTENT', + 'CONTENT', + 'CONTENT' + ]); shouldBeToken(result[3], 'CONTENT', ' \\'); shouldBeToken(result[4], 'OPEN', '{{'); @@ -108,7 +168,17 @@ describe('Tokenizer', function() { it('supports escaped escape characters after escaped mustaches', function() { var result = tokenize('{{foo}} \\{{bar}} \\\\{{baz}}'); - shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN', + 'ID', + 'CLOSE', + 'CONTENT', + 'CONTENT', + 'CONTENT', + 'OPEN', + 'ID', + 'CLOSE' + ]); shouldBeToken(result[4], 'CONTENT', '{{bar}} '); shouldBeToken(result[5], 'CONTENT', '\\'); @@ -118,7 +188,19 @@ describe('Tokenizer', function() { it('supports escaped escape character on a triple stash', function() { var result = tokenize('{{foo}} \\\\{{{bar}}} {{baz}}'); - shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN_UNESCAPED', 'ID', 'CLOSE_UNESCAPED', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN', + 'ID', + 'CLOSE', + 'CONTENT', + 'OPEN_UNESCAPED', + 'ID', + 'CLOSE_UNESCAPED', + 'CONTENT', + 'OPEN', + 'ID', + 'CLOSE' + ]); shouldBeToken(result[3], 'CONTENT', ' \\'); shouldBeToken(result[5], 'ID', 'bar'); @@ -133,7 +215,15 @@ describe('Tokenizer', function() { var result = tokenize('{{foo.bar}}'); shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']); - shouldMatchTokens(tokenize('{{foo.bar.baz}}'), ['OPEN', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']); + shouldMatchTokens(tokenize('{{foo.bar.baz}}'), [ + 'OPEN', + 'ID', + 'SEP', + 'ID', + 'SEP', + 'ID', + 'CLOSE' + ]); }); it('allows path literals with []', function() { @@ -143,7 +233,18 @@ describe('Tokenizer', function() { it('allows multiple path literals on a line with []', function() { var result = tokenize('{{foo.[bar]}}{{foo.[baz]}}'); - shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE', 'OPEN', 'ID', 'SEP', 'ID', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN', + 'ID', + 'SEP', + 'ID', + 'CLOSE', + 'OPEN', + 'ID', + 'SEP', + 'ID', + 'CLOSE' + ]); }); it('allows escaped literals in []', function() { @@ -158,13 +259,29 @@ describe('Tokenizer', function() { it('tokenizes a path as "OPEN (ID SEP)* ID CLOSE"', function() { var result = tokenize('{{../foo/bar}}'); - shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN', + 'ID', + 'SEP', + 'ID', + 'SEP', + 'ID', + 'CLOSE' + ]); shouldBeToken(result[1], 'ID', '..'); }); it('tokenizes a path with .. as a parent path', function() { var result = tokenize('{{../foo.bar}}'); - shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN', + 'ID', + 'SEP', + 'ID', + 'SEP', + 'ID', + 'CLOSE' + ]); shouldBeToken(result[1], 'ID', '..'); }); @@ -216,7 +333,15 @@ describe('Tokenizer', function() { it('tokenizes a partial space at the }); as "OPEN_PARTIAL ID CLOSE"', function() { var result = tokenize('{{>foo/bar.baz }}'); - shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN_PARTIAL', + 'ID', + 'SEP', + 'ID', + 'SEP', + 'ID', + 'CLOSE' + ]); }); it('tokenizes partial block declarations', function() { @@ -225,34 +350,69 @@ describe('Tokenizer', function() { }); it('tokenizes a comment as "COMMENT"', function() { var result = tokenize('foo {{! this is a comment }} bar {{ baz }}'); - shouldMatchTokens(result, ['CONTENT', 'COMMENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); + shouldMatchTokens(result, [ + 'CONTENT', + 'COMMENT', + 'CONTENT', + 'OPEN', + 'ID', + 'CLOSE' + ]); shouldBeToken(result[1], 'COMMENT', '{{! this is a comment }}'); }); it('tokenizes a block comment as "COMMENT"', function() { var result = tokenize('foo {{!-- this is a {{comment}} --}} bar {{ baz }}'); - shouldMatchTokens(result, ['CONTENT', 'COMMENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); + shouldMatchTokens(result, [ + 'CONTENT', + 'COMMENT', + 'CONTENT', + 'OPEN', + 'ID', + 'CLOSE' + ]); shouldBeToken(result[1], 'COMMENT', '{{!-- this is a {{comment}} --}}'); }); it('tokenizes a block comment with whitespace as "COMMENT"', function() { - var result = tokenize('foo {{!-- this is a\n{{comment}}\n--}} bar {{ baz }}'); - shouldMatchTokens(result, ['CONTENT', 'COMMENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); + var result = tokenize( + 'foo {{!-- this is a\n{{comment}}\n--}} bar {{ baz }}' + ); + shouldMatchTokens(result, [ + 'CONTENT', + 'COMMENT', + 'CONTENT', + 'OPEN', + 'ID', + 'CLOSE' + ]); shouldBeToken(result[1], 'COMMENT', '{{!-- this is a\n{{comment}}\n--}}'); }); it('tokenizes open and closing blocks as OPEN_BLOCK, ID, CLOSE ..., OPEN_ENDBLOCK ID CLOSE', function() { var result = tokenize('{{#foo}}content{{/foo}}'); - shouldMatchTokens(result, ['OPEN_BLOCK', 'ID', 'CLOSE', 'CONTENT', 'OPEN_ENDBLOCK', 'ID', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN_BLOCK', + 'ID', + 'CLOSE', + 'CONTENT', + 'OPEN_ENDBLOCK', + 'ID', + 'CLOSE' + ]); }); it('tokenizes directives', function() { - shouldMatchTokens( - tokenize('{{#*foo}}content{{/foo}}'), - ['OPEN_BLOCK', 'ID', 'CLOSE', 'CONTENT', 'OPEN_ENDBLOCK', 'ID', 'CLOSE']); - shouldMatchTokens( - tokenize('{{*foo}}'), - ['OPEN', 'ID', 'CLOSE']); + shouldMatchTokens(tokenize('{{#*foo}}content{{/foo}}'), [ + 'OPEN_BLOCK', + 'ID', + 'CLOSE', + 'CONTENT', + 'OPEN_ENDBLOCK', + 'ID', + 'CLOSE' + ]); + shouldMatchTokens(tokenize('{{*foo}}'), ['OPEN', 'ID', 'CLOSE']); }); it('tokenizes inverse sections as "INVERSE"', function() { @@ -351,28 +511,98 @@ describe('Tokenizer', function() { shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'EQUALS', 'ID', 'CLOSE']); result = tokenize('{{ foo bar baz=bat }}'); - shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'ID', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN', + 'ID', + 'ID', + 'ID', + 'EQUALS', + 'ID', + 'CLOSE' + ]); result = tokenize('{{ foo bar baz=1 }}'); - shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'NUMBER', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN', + 'ID', + 'ID', + 'ID', + 'EQUALS', + 'NUMBER', + 'CLOSE' + ]); result = tokenize('{{ foo bar baz=true }}'); - shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'BOOLEAN', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN', + 'ID', + 'ID', + 'ID', + 'EQUALS', + 'BOOLEAN', + 'CLOSE' + ]); result = tokenize('{{ foo bar baz=false }}'); - shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'BOOLEAN', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN', + 'ID', + 'ID', + 'ID', + 'EQUALS', + 'BOOLEAN', + 'CLOSE' + ]); result = tokenize('{{ foo bar\n baz=bat }}'); - shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'ID', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN', + 'ID', + 'ID', + 'ID', + 'EQUALS', + 'ID', + 'CLOSE' + ]); result = tokenize('{{ foo bar baz="bat" }}'); - shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'STRING', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN', + 'ID', + 'ID', + 'ID', + 'EQUALS', + 'STRING', + 'CLOSE' + ]); result = tokenize('{{ foo bar baz="bat" bam=wot }}'); - shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'STRING', 'ID', 'EQUALS', 'ID', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN', + 'ID', + 'ID', + 'ID', + 'EQUALS', + 'STRING', + 'ID', + 'EQUALS', + 'ID', + 'CLOSE' + ]); result = tokenize('{{foo omg bar=baz bat="bam"}}'); - shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'ID', 'ID', 'EQUALS', 'STRING', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN', + 'ID', + 'ID', + 'ID', + 'EQUALS', + 'ID', + 'ID', + 'EQUALS', + 'STRING', + 'CLOSE' + ]); shouldBeToken(result[2], 'ID', 'omg'); }); @@ -386,7 +616,15 @@ describe('Tokenizer', function() { shouldBeToken(result[3], 'ID', 'bar'); result = tokenize('{{ foo bar=@baz }}'); - shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'EQUALS', 'DATA', 'ID', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN', + 'ID', + 'ID', + 'EQUALS', + 'DATA', + 'ID', + 'CLOSE' + ]); shouldBeToken(result[5], 'ID', 'baz'); }); @@ -400,12 +638,27 @@ describe('Tokenizer', function() { it('tokenizes subexpressions', function() { var result = tokenize('{{foo (bar)}}'); - shouldMatchTokens(result, ['OPEN', 'ID', 'OPEN_SEXPR', 'ID', 'CLOSE_SEXPR', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN', + 'ID', + 'OPEN_SEXPR', + 'ID', + 'CLOSE_SEXPR', + 'CLOSE' + ]); shouldBeToken(result[1], 'ID', 'foo'); shouldBeToken(result[3], 'ID', 'bar'); result = tokenize('{{foo (a-x b-y)}}'); - shouldMatchTokens(result, ['OPEN', 'ID', 'OPEN_SEXPR', 'ID', 'ID', 'CLOSE_SEXPR', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN', + 'ID', + 'OPEN_SEXPR', + 'ID', + 'ID', + 'CLOSE_SEXPR', + 'CLOSE' + ]); shouldBeToken(result[1], 'ID', 'foo'); shouldBeToken(result[3], 'ID', 'a-x'); shouldBeToken(result[4], 'ID', 'b-y'); @@ -413,7 +666,21 @@ describe('Tokenizer', function() { it('tokenizes nested subexpressions', function() { var result = tokenize('{{foo (bar (lol rofl)) (baz)}}'); - shouldMatchTokens(result, ['OPEN', 'ID', 'OPEN_SEXPR', 'ID', 'OPEN_SEXPR', 'ID', 'ID', 'CLOSE_SEXPR', 'CLOSE_SEXPR', 'OPEN_SEXPR', 'ID', 'CLOSE_SEXPR', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN', + 'ID', + 'OPEN_SEXPR', + 'ID', + 'OPEN_SEXPR', + 'ID', + 'ID', + 'CLOSE_SEXPR', + 'CLOSE_SEXPR', + 'OPEN_SEXPR', + 'ID', + 'CLOSE_SEXPR', + 'CLOSE' + ]); shouldBeToken(result[3], 'ID', 'bar'); shouldBeToken(result[5], 'ID', 'lol'); shouldBeToken(result[6], 'ID', 'rofl'); @@ -421,29 +688,109 @@ describe('Tokenizer', function() { }); it('tokenizes nested subexpressions: literals', function() { - var result = tokenize("{{foo (bar (lol true) false) (baz 1) (blah 'b') (blorg \"c\")}}"); - shouldMatchTokens(result, ['OPEN', 'ID', 'OPEN_SEXPR', 'ID', 'OPEN_SEXPR', 'ID', 'BOOLEAN', 'CLOSE_SEXPR', 'BOOLEAN', 'CLOSE_SEXPR', 'OPEN_SEXPR', 'ID', 'NUMBER', 'CLOSE_SEXPR', 'OPEN_SEXPR', 'ID', 'STRING', 'CLOSE_SEXPR', 'OPEN_SEXPR', 'ID', 'STRING', 'CLOSE_SEXPR', 'CLOSE']); + var result = tokenize( + '{{foo (bar (lol true) false) (baz 1) (blah \'b\') (blorg "c")}}' + ); + shouldMatchTokens(result, [ + 'OPEN', + 'ID', + 'OPEN_SEXPR', + 'ID', + 'OPEN_SEXPR', + 'ID', + 'BOOLEAN', + 'CLOSE_SEXPR', + 'BOOLEAN', + 'CLOSE_SEXPR', + 'OPEN_SEXPR', + 'ID', + 'NUMBER', + 'CLOSE_SEXPR', + 'OPEN_SEXPR', + 'ID', + 'STRING', + 'CLOSE_SEXPR', + 'OPEN_SEXPR', + 'ID', + 'STRING', + 'CLOSE_SEXPR', + 'CLOSE' + ]); }); it('tokenizes block params', function() { var result = tokenize('{{#foo as |bar|}}'); - shouldMatchTokens(result, ['OPEN_BLOCK', 'ID', 'OPEN_BLOCK_PARAMS', 'ID', 'CLOSE_BLOCK_PARAMS', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN_BLOCK', + 'ID', + 'OPEN_BLOCK_PARAMS', + 'ID', + 'CLOSE_BLOCK_PARAMS', + 'CLOSE' + ]); result = tokenize('{{#foo as |bar baz|}}'); - shouldMatchTokens(result, ['OPEN_BLOCK', 'ID', 'OPEN_BLOCK_PARAMS', 'ID', 'ID', 'CLOSE_BLOCK_PARAMS', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN_BLOCK', + 'ID', + 'OPEN_BLOCK_PARAMS', + 'ID', + 'ID', + 'CLOSE_BLOCK_PARAMS', + 'CLOSE' + ]); result = tokenize('{{#foo as | bar baz |}}'); - shouldMatchTokens(result, ['OPEN_BLOCK', 'ID', 'OPEN_BLOCK_PARAMS', 'ID', 'ID', 'CLOSE_BLOCK_PARAMS', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN_BLOCK', + 'ID', + 'OPEN_BLOCK_PARAMS', + 'ID', + 'ID', + 'CLOSE_BLOCK_PARAMS', + 'CLOSE' + ]); result = tokenize('{{#foo as as | bar baz |}}'); - shouldMatchTokens(result, ['OPEN_BLOCK', 'ID', 'ID', 'OPEN_BLOCK_PARAMS', 'ID', 'ID', 'CLOSE_BLOCK_PARAMS', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN_BLOCK', + 'ID', + 'ID', + 'OPEN_BLOCK_PARAMS', + 'ID', + 'ID', + 'CLOSE_BLOCK_PARAMS', + 'CLOSE' + ]); result = tokenize('{{else foo as |bar baz|}}'); - shouldMatchTokens(result, ['OPEN_INVERSE_CHAIN', 'ID', 'OPEN_BLOCK_PARAMS', 'ID', 'ID', 'CLOSE_BLOCK_PARAMS', 'CLOSE']); + shouldMatchTokens(result, [ + 'OPEN_INVERSE_CHAIN', + 'ID', + 'OPEN_BLOCK_PARAMS', + 'ID', + 'ID', + 'CLOSE_BLOCK_PARAMS', + 'CLOSE' + ]); }); it('tokenizes raw blocks', function() { - var result = tokenize('{{{{a}}}} abc {{{{/a}}}} aaa {{{{a}}}} abc {{{{/a}}}}'); - shouldMatchTokens(result, ['OPEN_RAW_BLOCK', 'ID', 'CLOSE_RAW_BLOCK', 'CONTENT', 'END_RAW_BLOCK', 'CONTENT', 'OPEN_RAW_BLOCK', 'ID', 'CLOSE_RAW_BLOCK', 'CONTENT', 'END_RAW_BLOCK']); + var result = tokenize( + '{{{{a}}}} abc {{{{/a}}}} aaa {{{{a}}}} abc {{{{/a}}}}' + ); + shouldMatchTokens(result, [ + 'OPEN_RAW_BLOCK', + 'ID', + 'CLOSE_RAW_BLOCK', + 'CONTENT', + 'END_RAW_BLOCK', + 'CONTENT', + 'OPEN_RAW_BLOCK', + 'ID', + 'CLOSE_RAW_BLOCK', + 'CONTENT', + 'END_RAW_BLOCK' + ]); }); }); diff --git a/spec/track-ids.js b/spec/track-ids.js index 30a46617d..f7ad7abda 100644 --- a/spec/track-ids.js +++ b/spec/track-ids.js @@ -1,7 +1,7 @@ describe('track ids', function() { var context; beforeEach(function() { - context = {is: {a: 'foo'}, slave: {driver: 'bar'}}; + context = { is: { a: 'foo' }, slave: { driver: 'bar' } }; }); it('should not include anything without the flag', function() { @@ -16,38 +16,70 @@ describe('track ids', function() { } }; - equals(template({}, {helpers: helpers}), 'success'); + equals(template({}, { helpers: helpers }), 'success'); }); it('should include argument ids', function() { - var template = CompilerContext.compile('{{wycats is.a slave.driver}}', {trackIds: true}); + var template = CompilerContext.compile('{{wycats is.a slave.driver}}', { + trackIds: true + }); var helpers = { wycats: function(passiveVoice, noun, options) { equal(options.ids[0], 'is.a'); equal(options.ids[1], 'slave.driver'); - return 'HELP ME MY BOSS ' + options.ids[0] + ':' + passiveVoice + ' ' + options.ids[1] + ':' + noun; + return ( + 'HELP ME MY BOSS ' + + options.ids[0] + + ':' + + passiveVoice + + ' ' + + options.ids[1] + + ':' + + noun + ); } }; - equals(template(context, {helpers: helpers}), 'HELP ME MY BOSS is.a:foo slave.driver:bar'); + equals( + template(context, { helpers: helpers }), + 'HELP ME MY BOSS is.a:foo slave.driver:bar' + ); }); it('should include hash ids', function() { - var template = CompilerContext.compile('{{wycats bat=is.a baz=slave.driver}}', {trackIds: true}); + var template = CompilerContext.compile( + '{{wycats bat=is.a baz=slave.driver}}', + { trackIds: true } + ); var helpers = { wycats: function(options) { equal(options.hashIds.bat, 'is.a'); equal(options.hashIds.baz, 'slave.driver'); - return 'HELP ME MY BOSS ' + options.hashIds.bat + ':' + options.hash.bat + ' ' + options.hashIds.baz + ':' + options.hash.baz; + return ( + 'HELP ME MY BOSS ' + + options.hashIds.bat + + ':' + + options.hash.bat + + ' ' + + options.hashIds.baz + + ':' + + options.hash.baz + ); } }; - equals(template(context, {helpers: helpers}), 'HELP ME MY BOSS is.a:foo slave.driver:bar'); + equals( + template(context, { helpers: helpers }), + 'HELP ME MY BOSS is.a:foo slave.driver:bar' + ); }); it('should note ../ and ./ references', function() { - var template = CompilerContext.compile('{{wycats ./is.a ../slave.driver this.is.a this}}', {trackIds: true}); + var template = CompilerContext.compile( + '{{wycats ./is.a ../slave.driver this.is.a this}}', + { trackIds: true } + ); var helpers = { wycats: function(passiveVoice, noun, thiz, thiz2, options) { @@ -56,29 +88,57 @@ describe('track ids', function() { equal(options.ids[2], 'is.a'); equal(options.ids[3], ''); - return 'HELP ME MY BOSS ' + options.ids[0] + ':' + passiveVoice + ' ' + options.ids[1] + ':' + noun; + return ( + 'HELP ME MY BOSS ' + + options.ids[0] + + ':' + + passiveVoice + + ' ' + + options.ids[1] + + ':' + + noun + ); } }; - equals(template(context, {helpers: helpers}), 'HELP ME MY BOSS is.a:foo ../slave.driver:undefined'); + equals( + template(context, { helpers: helpers }), + 'HELP ME MY BOSS is.a:foo ../slave.driver:undefined' + ); }); it('should note @data references', function() { - var template = CompilerContext.compile('{{wycats @is.a @slave.driver}}', {trackIds: true}); + var template = CompilerContext.compile('{{wycats @is.a @slave.driver}}', { + trackIds: true + }); var helpers = { wycats: function(passiveVoice, noun, options) { equal(options.ids[0], '@is.a'); equal(options.ids[1], '@slave.driver'); - return 'HELP ME MY BOSS ' + options.ids[0] + ':' + passiveVoice + ' ' + options.ids[1] + ':' + noun; + return ( + 'HELP ME MY BOSS ' + + options.ids[0] + + ':' + + passiveVoice + + ' ' + + options.ids[1] + + ':' + + noun + ); } }; - equals(template({}, {helpers: helpers, data: context}), 'HELP ME MY BOSS @is.a:foo @slave.driver:bar'); + equals( + template({}, { helpers: helpers, data: context }), + 'HELP ME MY BOSS @is.a:foo @slave.driver:bar' + ); }); it('should return null for constants', function() { - var template = CompilerContext.compile('{{wycats 1 "foo" key=false}}', {trackIds: true}); + var template = CompilerContext.compile('{{wycats 1 "foo" key=false}}', { + trackIds: true + }); var helpers = { wycats: function(passiveVoice, noun, options) { @@ -86,17 +146,31 @@ describe('track ids', function() { equal(options.ids[1], null); equal(options.hashIds.key, null); - return 'HELP ME MY BOSS ' + passiveVoice + ' ' + noun + ' ' + options.hash.key; + return ( + 'HELP ME MY BOSS ' + + passiveVoice + + ' ' + + noun + + ' ' + + options.hash.key + ); } }; - equals(template(context, {helpers: helpers}), 'HELP ME MY BOSS 1 foo false'); + equals( + template(context, { helpers: helpers }), + 'HELP ME MY BOSS 1 foo false' + ); }); it('should return true for subexpressions', function() { - var template = CompilerContext.compile('{{wycats (sub)}}', {trackIds: true}); + var template = CompilerContext.compile('{{wycats (sub)}}', { + trackIds: true + }); var helpers = { - sub: function() { return 1; }, + sub: function() { + return 1; + }, wycats: function(passiveVoice, options) { equal(options.ids[0], true); @@ -104,28 +178,43 @@ describe('track ids', function() { } }; - equals(template(context, {helpers: helpers}), 'HELP ME MY BOSS 1'); + equals(template(context, { helpers: helpers }), 'HELP ME MY BOSS 1'); }); it('should use block param paths', function() { - var template = CompilerContext.compile('{{#doIt as |is|}}{{wycats is.a slave.driver is}}{{/doIt}}', {trackIds: true}); + var template = CompilerContext.compile( + '{{#doIt as |is|}}{{wycats is.a slave.driver is}}{{/doIt}}', + { trackIds: true } + ); var helpers = { doIt: function(options) { var blockParams = [this.is]; blockParams.path = ['zomg']; - return options.fn(this, {blockParams: blockParams}); + return options.fn(this, { blockParams: blockParams }); }, wycats: function(passiveVoice, noun, blah, options) { equal(options.ids[0], 'zomg.a'); equal(options.ids[1], 'slave.driver'); equal(options.ids[2], 'zomg'); - return 'HELP ME MY BOSS ' + options.ids[0] + ':' + passiveVoice + ' ' + options.ids[1] + ':' + noun; + return ( + 'HELP ME MY BOSS ' + + options.ids[0] + + ':' + + passiveVoice + + ' ' + + options.ids[1] + + ':' + + noun + ); } }; - equals(template(context, {helpers: helpers}), 'HELP ME MY BOSS zomg.a:foo slave.driver:bar'); + equals( + template(context, { helpers: helpers }), + 'HELP ME MY BOSS zomg.a:foo slave.driver:bar' + ); }); describe('builtin helpers', function() { @@ -140,53 +229,119 @@ describe('track ids', function() { describe('#each', function() { it('should track contextPath for arrays', function() { - var template = CompilerContext.compile('{{#each array}}{{wycats name}}{{/each}}', {trackIds: true}); - - equals(template({array: [{name: 'foo'}, {name: 'bar'}]}, {helpers: helpers}), 'foo:array.0\nbar:array.1\n'); + var template = CompilerContext.compile( + '{{#each array}}{{wycats name}}{{/each}}', + { trackIds: true } + ); + + equals( + template( + { array: [{ name: 'foo' }, { name: 'bar' }] }, + { helpers: helpers } + ), + 'foo:array.0\nbar:array.1\n' + ); }); it('should track contextPath for keys', function() { - var template = CompilerContext.compile('{{#each object}}{{wycats name}}{{/each}}', {trackIds: true}); - - equals(template({object: {foo: {name: 'foo'}, bar: {name: 'bar'}}}, {helpers: helpers}), 'foo:object.foo\nbar:object.bar\n'); + var template = CompilerContext.compile( + '{{#each object}}{{wycats name}}{{/each}}', + { trackIds: true } + ); + + equals( + template( + { object: { foo: { name: 'foo' }, bar: { name: 'bar' } } }, + { helpers: helpers } + ), + 'foo:object.foo\nbar:object.bar\n' + ); }); it('should handle nesting', function() { - var template = CompilerContext.compile('{{#each .}}{{#each .}}{{wycats name}}{{/each}}{{/each}}', {trackIds: true}); - - equals(template({array: [{name: 'foo'}, {name: 'bar'}]}, {helpers: helpers}), 'foo:.array..0\nbar:.array..1\n'); + var template = CompilerContext.compile( + '{{#each .}}{{#each .}}{{wycats name}}{{/each}}{{/each}}', + { trackIds: true } + ); + + equals( + template( + { array: [{ name: 'foo' }, { name: 'bar' }] }, + { helpers: helpers } + ), + 'foo:.array..0\nbar:.array..1\n' + ); }); it('should handle block params', function() { - var template = CompilerContext.compile('{{#each array as |value|}}{{blockParams value.name}}{{/each}}', {trackIds: true}); - - equals(template({array: [{name: 'foo'}, {name: 'bar'}]}, {helpers: helpers}), 'foo:array.0.name\nbar:array.1.name\n'); + var template = CompilerContext.compile( + '{{#each array as |value|}}{{blockParams value.name}}{{/each}}', + { trackIds: true } + ); + + equals( + template( + { array: [{ name: 'foo' }, { name: 'bar' }] }, + { helpers: helpers } + ), + 'foo:array.0.name\nbar:array.1.name\n' + ); }); }); describe('#with', function() { it('should track contextPath', function() { - var template = CompilerContext.compile('{{#with field}}{{wycats name}}{{/with}}', {trackIds: true}); - - equals(template({field: {name: 'foo'}}, {helpers: helpers}), 'foo:field\n'); + var template = CompilerContext.compile( + '{{#with field}}{{wycats name}}{{/with}}', + { trackIds: true } + ); + + equals( + template({ field: { name: 'foo' } }, { helpers: helpers }), + 'foo:field\n' + ); }); it('should handle nesting', function() { - var template = CompilerContext.compile('{{#with bat}}{{#with field}}{{wycats name}}{{/with}}{{/with}}', {trackIds: true}); - - equals(template({bat: {field: {name: 'foo'}}}, {helpers: helpers}), 'foo:bat.field\n'); + var template = CompilerContext.compile( + '{{#with bat}}{{#with field}}{{wycats name}}{{/with}}{{/with}}', + { trackIds: true } + ); + + equals( + template({ bat: { field: { name: 'foo' } } }, { helpers: helpers }), + 'foo:bat.field\n' + ); }); }); describe('#blockHelperMissing', function() { it('should track contextPath for arrays', function() { - var template = CompilerContext.compile('{{#field}}{{wycats name}}{{/field}}', {trackIds: true}); - - equals(template({field: [{name: 'foo'}]}, {helpers: helpers}), 'foo:field.0\n'); + var template = CompilerContext.compile( + '{{#field}}{{wycats name}}{{/field}}', + { trackIds: true } + ); + + equals( + template({ field: [{ name: 'foo' }] }, { helpers: helpers }), + 'foo:field.0\n' + ); }); it('should track contextPath for keys', function() { - var template = CompilerContext.compile('{{#field}}{{wycats name}}{{/field}}', {trackIds: true}); - - equals(template({field: {name: 'foo'}}, {helpers: helpers}), 'foo:field\n'); + var template = CompilerContext.compile( + '{{#field}}{{wycats name}}{{/field}}', + { trackIds: true } + ); + + equals( + template({ field: { name: 'foo' } }, { helpers: helpers }), + 'foo:field\n' + ); }); it('should handle nesting', function() { - var template = CompilerContext.compile('{{#bat}}{{#field}}{{wycats name}}{{/field}}{{/bat}}', {trackIds: true}); - - equals(template({bat: {field: {name: 'foo'}}}, {helpers: helpers}), 'foo:bat.field\n'); + var template = CompilerContext.compile( + '{{#bat}}{{#field}}{{wycats name}}{{/field}}{{/bat}}', + { trackIds: true } + ); + + equals( + template({ bat: { field: { name: 'foo' } } }, { helpers: helpers }), + 'foo:bat.field\n' + ); }); }); }); @@ -202,36 +357,71 @@ describe('track ids', function() { }; it('should pass track id for basic partial', function() { - var template = CompilerContext.compile('Dudes: {{#dudes}}{{> dude}}{{/dudes}}', {trackIds: true}), - hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]}; + var template = CompilerContext.compile( + 'Dudes: {{#dudes}}{{> dude}}{{/dudes}}', + { trackIds: true } + ), + hash = { + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }; var partials = { - dude: CompilerContext.compile('{{wycats name}}', {trackIds: true}) + dude: CompilerContext.compile('{{wycats name}}', { trackIds: true }) }; - equals(template(hash, {helpers: helpers, partials: partials}), 'Dudes: Yehuda:dudes.0\nAlan:dudes.1\n'); + equals( + template(hash, { helpers: helpers, partials: partials }), + 'Dudes: Yehuda:dudes.0\nAlan:dudes.1\n' + ); }); it('should pass track id for context partial', function() { - var template = CompilerContext.compile('Dudes: {{> dude dudes}}', {trackIds: true}), - hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]}; + var template = CompilerContext.compile('Dudes: {{> dude dudes}}', { + trackIds: true + }), + hash = { + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }; var partials = { - dude: CompilerContext.compile('{{#each this}}{{wycats name}}{{/each}}', {trackIds: true}) + dude: CompilerContext.compile( + '{{#each this}}{{wycats name}}{{/each}}', + { trackIds: true } + ) }; - equals(template(hash, {helpers: helpers, partials: partials}), 'Dudes: Yehuda:dudes..0\nAlan:dudes..1\n'); + equals( + template(hash, { helpers: helpers, partials: partials }), + 'Dudes: Yehuda:dudes..0\nAlan:dudes..1\n' + ); }); it('should invalidate context for partials with parameters', function() { - var template = CompilerContext.compile('Dudes: {{#dudes}}{{> dude . bar="foo"}}{{/dudes}}', {trackIds: true}), - hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]}; + var template = CompilerContext.compile( + 'Dudes: {{#dudes}}{{> dude . bar="foo"}}{{/dudes}}', + { trackIds: true } + ), + hash = { + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }; var partials = { - dude: CompilerContext.compile('{{wycats name}}', {trackIds: true}) + dude: CompilerContext.compile('{{wycats name}}', { trackIds: true }) }; - equals(template(hash, {helpers: helpers, partials: partials}), 'Dudes: Yehuda:true\nAlan:true\n'); + equals( + template(hash, { helpers: helpers, partials: partials }), + 'Dudes: Yehuda:true\nAlan:true\n' + ); }); }); }); diff --git a/spec/utils.js b/spec/utils.js index 7248ac447..667aa0ed3 100644 --- a/spec/utils.js +++ b/spec/utils.js @@ -5,19 +5,30 @@ describe('utils', function() { if (!(safe instanceof Handlebars.SafeString)) { throw new Error('Must be instance of SafeString'); } - equals(safe.toString(), 'testing 1, 2, 3', 'SafeString is equivalent to its underlying string'); + equals( + safe.toString(), + 'testing 1, 2, 3', + 'SafeString is equivalent to its underlying string' + ); }); it('it should not escape SafeString properties', function() { var name = new Handlebars.SafeString('Sean O'Malley'); - shouldCompileTo('{{name}}', [{name: name}], 'Sean O'Malley'); + shouldCompileTo( + '{{name}}', + [{ name: name }], + 'Sean O'Malley' + ); }); }); describe('#escapeExpression', function() { it('shouhld escape html', function() { - equals(Handlebars.Utils.escapeExpression('foo<&"\'>'), 'foo<&"'>'); + equals( + Handlebars.Utils.escapeExpression('foo<&"\'>'), + 'foo<&"'>' + ); equals(Handlebars.Utils.escapeExpression('foo='), 'foo='); }); it('should not escape SafeString', function() { @@ -58,7 +69,7 @@ describe('utils', function() { equals(Handlebars.Utils.isEmpty(0), false); equals(Handlebars.Utils.isEmpty([1]), false); equals(Handlebars.Utils.isEmpty('foo'), false); - equals(Handlebars.Utils.isEmpty({bar: 1}), false); + equals(Handlebars.Utils.isEmpty({ bar: 1 }), false); }); }); @@ -69,7 +80,7 @@ describe('utils', function() { } A.prototype.b = 4; - var b = {b: 2}; + var b = { b: 2 }; Handlebars.Utils.extend(b, new A()); diff --git a/spec/visitor.js b/spec/visitor.js index d3fb795e2..2bec356fe 100644 --- a/spec/visitor.js +++ b/spec/visitor.js @@ -7,7 +7,11 @@ describe('Visitor', function() { // Simply run the thing and make sure it does not fail and that all of the // stub methods are executed var visitor = new Handlebars.Visitor(); - visitor.accept(Handlebars.parse('{{foo}}{{#foo (bar 1 "1" true undefined null) foo=@data}}{{!comment}}{{> bar }} {{/foo}}')); + visitor.accept( + Handlebars.parse( + '{{foo}}{{#foo (bar 1 "1" true undefined null) foo=@data}}{{!comment}}{{> bar }} {{/foo}}' + ) + ); visitor.accept(Handlebars.parse('{{#> bar }} {{/bar}}')); visitor.accept(Handlebars.parse('{{#* bar }} {{/bar}}')); visitor.accept(Handlebars.parse('{{* bar }}')); @@ -40,7 +44,11 @@ describe('Visitor', function() { equal(comment.value, 'comment'); }; - visitor.accept(Handlebars.parse('{{#foo.bar (foo.bar 1 "2" true) foo=@foo.bar}}{{!comment}}{{> bar }} {{/foo.bar}}')); + visitor.accept( + Handlebars.parse( + '{{#foo.bar (foo.bar 1 "2" true) foo=@foo.bar}}{{!comment}}{{> bar }} {{/foo.bar}}' + ) + ); }); describe('mutating', function() { @@ -50,12 +58,15 @@ describe('Visitor', function() { visitor.mutating = true; visitor.StringLiteral = function(string) { - return {type: 'NumberLiteral', value: 42, loc: string.loc}; + return { type: 'NumberLiteral', value: 42, loc: string.loc }; }; var ast = Handlebars.parse('{{foo foo="foo"}}'); visitor.accept(ast); - equals(Handlebars.print(ast), '{{ PATH:foo [] HASH{foo=NUMBER{42}} }}\n'); + equals( + Handlebars.print(ast), + '{{ PATH:foo [] HASH{foo=NUMBER{42}} }}\n' + ); }); it('should treat undefined resonse as identity', function() { var visitor = new Handlebars.Visitor(); @@ -63,7 +74,10 @@ describe('Visitor', function() { var ast = Handlebars.parse('{{foo foo=42}}'); visitor.accept(ast); - equals(Handlebars.print(ast), '{{ PATH:foo [] HASH{foo=NUMBER{42}} }}\n'); + equals( + Handlebars.print(ast), + '{{ PATH:foo [] HASH{foo=NUMBER{42}} }}\n' + ); }); it('should remove false responses', function() { var visitor = new Handlebars.Visitor(); @@ -78,30 +92,38 @@ describe('Visitor', function() { equals(Handlebars.print(ast), '{{ PATH:foo [] }}\n'); }); it('should throw when removing required values', function() { - shouldThrow(function() { - var visitor = new Handlebars.Visitor(); - - visitor.mutating = true; - visitor.PathExpression = function() { - return false; - }; - - var ast = Handlebars.parse('{{foo 42}}'); - visitor.accept(ast); - }, Handlebars.Exception, 'MustacheStatement requires path'); + shouldThrow( + function() { + var visitor = new Handlebars.Visitor(); + + visitor.mutating = true; + visitor.PathExpression = function() { + return false; + }; + + var ast = Handlebars.parse('{{foo 42}}'); + visitor.accept(ast); + }, + Handlebars.Exception, + 'MustacheStatement requires path' + ); }); it('should throw when returning non-node responses', function() { - shouldThrow(function() { - var visitor = new Handlebars.Visitor(); - - visitor.mutating = true; - visitor.PathExpression = function() { - return {}; - }; - - var ast = Handlebars.parse('{{foo 42}}'); - visitor.accept(ast); - }, Handlebars.Exception, 'Unexpected node type "undefined" found when accepting path on MustacheStatement'); + shouldThrow( + function() { + var visitor = new Handlebars.Visitor(); + + visitor.mutating = true; + visitor.PathExpression = function() { + return {}; + }; + + var ast = Handlebars.parse('{{foo 42}}'); + visitor.accept(ast); + }, + Handlebars.Exception, + 'Unexpected node type "undefined" found when accepting path on MustacheStatement' + ); }); }); describe('arrays', function() { @@ -110,7 +132,7 @@ describe('Visitor', function() { visitor.mutating = true; visitor.StringLiteral = function(string) { - return {type: 'NumberLiteral', value: 42, loc: string.locInfo}; + return { type: 'NumberLiteral', value: 42, loc: string.locInfo }; }; var ast = Handlebars.parse('{{foo "foo"}}'); diff --git a/spec/whitespace-control.js b/spec/whitespace-control.js index a5c9bf14d..b1a6db045 100644 --- a/spec/whitespace-control.js +++ b/spec/whitespace-control.js @@ -1,6 +1,6 @@ describe('whitespace control', function() { it('should strip whitespace around mustache calls', function() { - var hash = {foo: 'bar<'}; + var hash = { foo: 'bar<' }; shouldCompileTo(' {{~foo~}} ', hash, 'bar<'); shouldCompileTo(' {{~foo}} ', hash, 'bar< '); @@ -14,15 +14,23 @@ describe('whitespace control', function() { describe('blocks', function() { it('should strip whitespace around simple block calls', function() { - var hash = {foo: 'bar<'}; + var hash = { foo: 'bar<' }; shouldCompileTo(' {{~#if foo~}} bar {{~/if~}} ', hash, 'bar'); shouldCompileTo(' {{#if foo~}} bar {{/if~}} ', hash, ' bar '); shouldCompileTo(' {{~#if foo}} bar {{~/if}} ', hash, ' bar '); shouldCompileTo(' {{#if foo}} bar {{/if}} ', hash, ' bar '); - shouldCompileTo(' \n\n{{~#if foo~}} \n\nbar \n\n{{~/if~}}\n\n ', hash, 'bar'); - shouldCompileTo(' a\n\n{{~#if foo~}} \n\nbar \n\n{{~/if~}}\n\na ', hash, ' abara '); + shouldCompileTo( + ' \n\n{{~#if foo~}} \n\nbar \n\n{{~/if~}}\n\n ', + hash, + 'bar' + ); + shouldCompileTo( + ' a\n\n{{~#if foo~}} \n\nbar \n\n{{~/if~}}\n\na ', + hash, + ' abara ' + ); }); it('should strip whitespace around inverse block calls', function() { var hash = {}; @@ -32,10 +40,14 @@ describe('whitespace control', function() { shouldCompileTo(' {{~^if foo}} bar {{~/if}} ', hash, ' bar '); shouldCompileTo(' {{^if foo}} bar {{/if}} ', hash, ' bar '); - shouldCompileTo(' \n\n{{~^if foo~}} \n\nbar \n\n{{~/if~}}\n\n ', hash, 'bar'); + shouldCompileTo( + ' \n\n{{~^if foo~}} \n\nbar \n\n{{~/if~}}\n\n ', + hash, + 'bar' + ); }); it('should strip whitespace around complex block calls', function() { - var hash = {foo: 'bar<'}; + var hash = { foo: 'bar<' }; shouldCompileTo('{{#if foo~}} bar {{~^~}} baz {{~/if}}', hash, 'bar'); shouldCompileTo('{{#if foo~}} bar {{^~}} baz {{/if}}', hash, 'bar '); @@ -44,8 +56,16 @@ describe('whitespace control', function() { shouldCompileTo('{{#if foo~}} bar {{~else~}} baz {{~/if}}', hash, 'bar'); - shouldCompileTo('\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n', hash, 'bar'); - shouldCompileTo('\n\n{{~#if foo~}} \n\n{{{foo}}} \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n', hash, 'bar<'); + shouldCompileTo( + '\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n', + hash, + 'bar' + ); + shouldCompileTo( + '\n\n{{~#if foo~}} \n\n{{{foo}}} \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n', + hash, + 'bar<' + ); hash = {}; @@ -56,21 +76,50 @@ describe('whitespace control', function() { shouldCompileTo('{{#if foo~}} bar {{~else~}} baz {{~/if}}', hash, 'baz'); - shouldCompileTo('\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n', hash, 'baz'); + shouldCompileTo( + '\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n', + hash, + 'baz' + ); }); }); it('should strip whitespace around partials', function() { - shouldCompileToWithPartials('foo {{~> dude~}} ', [{}, {}, {dude: 'bar'}], true, 'foobar'); - shouldCompileToWithPartials('foo {{> dude~}} ', [{}, {}, {dude: 'bar'}], true, 'foo bar'); - shouldCompileToWithPartials('foo {{> dude}} ', [{}, {}, {dude: 'bar'}], true, 'foo bar '); - - shouldCompileToWithPartials('foo\n {{~> dude}} ', [{}, {}, {dude: 'bar'}], true, 'foobar'); - shouldCompileToWithPartials('foo\n {{> dude}} ', [{}, {}, {dude: 'bar'}], true, 'foo\n bar'); + shouldCompileToWithPartials( + 'foo {{~> dude~}} ', + [{}, {}, { dude: 'bar' }], + true, + 'foobar' + ); + shouldCompileToWithPartials( + 'foo {{> dude~}} ', + [{}, {}, { dude: 'bar' }], + true, + 'foo bar' + ); + shouldCompileToWithPartials( + 'foo {{> dude}} ', + [{}, {}, { dude: 'bar' }], + true, + 'foo bar ' + ); + + shouldCompileToWithPartials( + 'foo\n {{~> dude}} ', + [{}, {}, { dude: 'bar' }], + true, + 'foobar' + ); + shouldCompileToWithPartials( + 'foo\n {{> dude}} ', + [{}, {}, { dude: 'bar' }], + true, + 'foo\n bar' + ); }); it('should only strip whitespace once', function() { - var hash = {foo: 'bar'}; + var hash = { foo: 'bar' }; shouldCompileTo(' {{~foo~}} {{foo}} {{foo}} ', hash, 'barbar bar '); }); diff --git a/tasks/metrics.js b/tasks/metrics.js index a1fa5c95a..f903ceb7b 100644 --- a/tasks/metrics.js +++ b/tasks/metrics.js @@ -1,14 +1,16 @@ var _ = require('underscore'), - async = require('neo-async'), - metrics = require('../bench'); + async = require('neo-async'), + metrics = require('../bench'); module.exports = function(grunt) { grunt.registerTask('metrics', function() { var done = this.async(), - execName = grunt.option('name'), - events = {}; + execName = grunt.option('name'), + events = {}; - async.each(_.keys(metrics), function(name, complete) { + async.each( + _.keys(metrics), + function(name, complete) { if (/^_/.test(name) || (execName && name !== execName)) { return complete(); } @@ -18,6 +20,7 @@ module.exports = function(grunt) { complete(); }); }, - done); + done + ); }); }; diff --git a/tasks/parser.js b/tasks/parser.js index 7ff725850..0adba111d 100644 --- a/tasks/parser.js +++ b/tasks/parser.js @@ -7,10 +7,14 @@ module.exports = function(grunt) { var cmd = './node_modules/.bin/jison'; if (process.platform === 'win32') { - cmd = 'node_modules\\.bin\\jison.cmd'; + cmd = 'node_modules\\.bin\\jison.cmd'; } - var child = childProcess.spawn(cmd, ['-m', 'js', 'src/handlebars.yy', 'src/handlebars.l'], {stdio: 'inherit'}); + var child = childProcess.spawn( + cmd, + ['-m', 'js', 'src/handlebars.yy', 'src/handlebars.l'], + { stdio: 'inherit' } + ); child.on('exit', function(code) { if (code != 0) { grunt.fatal('Jison failure: ' + code); @@ -18,7 +22,13 @@ module.exports = function(grunt) { return; } - var src = ['src/parser-prefix.js', 'handlebars.js', 'src/parser-suffix.js'].map(grunt.file.read).join(''); + var src = [ + 'src/parser-prefix.js', + 'handlebars.js', + 'src/parser-suffix.js' + ] + .map(grunt.file.read) + .join(''); grunt.file.delete('handlebars.js'); grunt.file.write('lib/handlebars/compiler/parser.js', src); diff --git a/tasks/publish.js b/tasks/publish.js index c3729596b..b22c9b8ea 100644 --- a/tasks/publish.js +++ b/tasks/publish.js @@ -1,8 +1,8 @@ var _ = require('underscore'), - async = require('neo-async'), - AWS = require('aws-sdk'), - git = require('./util/git'), - semver = require('semver'); + async = require('neo-async'), + AWS = require('aws-sdk'), + git = require('./util/git'), + semver = require('semver'); module.exports = function(grunt) { grunt.registerTask('publish:latest', function() { @@ -53,21 +53,27 @@ module.exports = function(grunt) { function initSDK() { var bucket = process.env.S3_BUCKET_NAME, - key = process.env.S3_ACCESS_KEY_ID, - secret = process.env.S3_SECRET_ACCESS_KEY; + key = process.env.S3_ACCESS_KEY_ID, + secret = process.env.S3_SECRET_ACCESS_KEY; if (!bucket || !key || !secret) { throw new Error('Missing S3 config values'); } - AWS.config.update({accessKeyId: key, secretAccessKey: secret}); + AWS.config.update({ accessKeyId: key, secretAccessKey: secret }); } function publish(files, callback) { var s3 = new AWS.S3(), - bucket = process.env.S3_BUCKET_NAME; + bucket = process.env.S3_BUCKET_NAME; - async.each(_.keys(files), function(file, callback) { - var params = {Bucket: bucket, Key: file, Body: grunt.file.read(files[file])}; + async.each( + _.keys(files), + function(file, callback) { + var params = { + Bucket: bucket, + Key: file, + Body: grunt.file.read(files[file]) + }; s3.putObject(params, function(err) { if (err) { throw err; @@ -77,15 +83,24 @@ module.exports = function(grunt) { } }); }, - callback); + callback + ); } function fileMap(suffixes) { var map = {}; - _.each(['handlebars.js', 'handlebars.min.js', 'handlebars.runtime.js', 'handlebars.runtime.min.js'], function(file) { - _.each(suffixes, function(suffix) { - map[file.replace(/\.js$/, suffix + '.js')] = 'dist/' + file; - }); - }); + _.each( + [ + 'handlebars.js', + 'handlebars.min.js', + 'handlebars.runtime.js', + 'handlebars.runtime.min.js' + ], + function(file) { + _.each(suffixes, function(suffix) { + map[file.replace(/\.js$/, suffix + '.js')] = 'dist/' + file; + }); + } + ); return map; } }; diff --git a/tasks/test.js b/tasks/test.js index a17fc2a22..94fc8c25a 100644 --- a/tasks/test.js +++ b/tasks/test.js @@ -1,13 +1,13 @@ var childProcess = require('child_process'), - fs = require('fs'), - os = require('os'); + fs = require('fs'), + os = require('os'); module.exports = function(grunt) { grunt.registerTask('test:bin', function() { var done = this.async(); var cmd = './bin/handlebars'; - var args = [ '-a', 'spec/artifacts/empty.handlebars' ]; + var args = ['-a', 'spec/artifacts/empty.handlebars']; // On Windows, the executable handlebars.js file cannot be run directly if (os.platform() === 'win32') { @@ -19,10 +19,19 @@ module.exports = function(grunt) { throw err; } - var expected = fs.readFileSync('./spec/expected/empty.amd.js').toString().replace(/\r\n/g, '\n'); + var expected = fs + .readFileSync('./spec/expected/empty.amd.js') + .toString() + .replace(/\r\n/g, '\n'); if (stdout.toString() !== expected) { - throw new Error('Expected binary output differed:\n\n"' + stdout + '"\n\n"' + expected + '"'); + throw new Error( + 'Expected binary output differed:\n\n"' + + stdout + + '"\n\n"' + + expected + + '"' + ); } done(); @@ -31,7 +40,9 @@ module.exports = function(grunt) { grunt.registerTask('test:mocha', function() { var done = this.async(); - var runner = childProcess.fork('./spec/env/runner', [], {stdio: 'inherit'}); + var runner = childProcess.fork('./spec/env/runner', [], { + stdio: 'inherit' + }); runner.on('close', function(code) { if (code !== 0) { grunt.fatal(code + ' tests failed'); @@ -42,7 +53,11 @@ module.exports = function(grunt) { grunt.registerTask('test:cov', function() { var done = this.async(); - var runner = childProcess.fork('node_modules/istanbul/lib/cli.js', ['cover', '--source-map', '--', './spec/env/runner.js'], {stdio: 'inherit'}); + var runner = childProcess.fork( + 'node_modules/istanbul/lib/cli.js', + ['cover', '--source-map', '--', './spec/env/runner.js'], + { stdio: 'inherit' } + ); runner.on('close', function(code) { if (code != 0) { grunt.fatal(code + ' tests failed'); @@ -53,7 +68,9 @@ module.exports = function(grunt) { grunt.registerTask('test:min', function() { var done = this.async(); - var runner = childProcess.fork('./spec/env/runner', ['--min'], {stdio: 'inherit'}); + var runner = childProcess.fork('./spec/env/runner', ['--min'], { + stdio: 'inherit' + }); runner.on('close', function(code) { if (code !== 0) { grunt.fatal(code + ' tests failed'); @@ -65,7 +82,20 @@ module.exports = function(grunt) { grunt.registerTask('test:check-cov', function() { var done = this.async(); - var runner = childProcess.fork('node_modules/istanbul/lib/cli.js', ['check-coverage', '--statements', '100', '--functions', '100', '--branches', '100', '--lines 100'], {stdio: 'inherit'}); + var runner = childProcess.fork( + 'node_modules/istanbul/lib/cli.js', + [ + 'check-coverage', + '--statements', + '100', + '--functions', + '100', + '--branches', + '100', + '--lines 100' + ], + { stdio: 'inherit' } + ); runner.on('close', function(code) { if (code != 0) { grunt.fatal('Coverage check failed: ' + code); diff --git a/tasks/util/git.js b/tasks/util/git.js index 03802630d..5418bf03d 100644 --- a/tasks/util/git.js +++ b/tasks/util/git.js @@ -17,7 +17,10 @@ module.exports = { }); }, clean: function(callback) { - childProcess.exec('git diff-index --name-only HEAD --', {}, function(err, stdout) { + childProcess.exec('git diff-index --name-only HEAD --', {}, function( + err, + stdout + ) { callback(undefined, !err && !stdout); }); }, @@ -47,10 +50,13 @@ module.exports = { }); }, master: function(callback) { - childProcess.exec('git rev-parse --short origin/master', {}, function(err, stdout) { + childProcess.exec('git rev-parse --short origin/master', {}, function( + err, + stdout + ) { // This will error if master was not checked out but in this case we know we are not master // so we can ignore. - if (err && !(/Needed a single revision/.test(err.message))) { + if (err && !/Needed a single revision/.test(err.message)) { throw new Error('git.master: ' + err.message); } @@ -77,7 +83,9 @@ module.exports = { }); }, tag: function(name, callback) { - childProcess.exec('git tag -a --message=' + name + ' ' + name, {}, function(err) { + childProcess.exec('git tag -a --message=' + name + ' ' + name, {}, function( + err + ) { if (err) { throw new Error('git.tag: ' + err.message); } @@ -98,7 +106,7 @@ module.exports = { }); var versionTags = tags.filter(function(info) { - return (/^v/.test(info[0])); + return /^v/.test(info[0]); }); callback(undefined, versionTags[0] || tags[0]); diff --git a/tasks/version.js b/tasks/version.js index 05f703419..05c05fa01 100644 --- a/tasks/version.js +++ b/tasks/version.js @@ -1,38 +1,64 @@ var async = require('neo-async'), - git = require('./util/git'), - semver = require('semver'); + git = require('./util/git'), + semver = require('semver'); module.exports = function(grunt) { - grunt.registerTask('version', 'Updates the current release version', function() { - var done = this.async(), + grunt.registerTask( + 'version', + 'Updates the current release version', + function() { + var done = this.async(), pkg = grunt.config('pkg'), version = grunt.option('ver'); - if (!semver.valid(version)) { - throw new Error('Must provide a version number (Ex: --ver=1.0.0):\n\t' + version + '\n\n'); - } + if (!semver.valid(version)) { + throw new Error( + 'Must provide a version number (Ex: --ver=1.0.0):\n\t' + + version + + '\n\n' + ); + } - pkg.version = version; - grunt.config('pkg', pkg); + pkg.version = version; + grunt.config('pkg', pkg); - grunt.log.writeln('Updating to version ' + version); + grunt.log.writeln('Updating to version ' + version); - async.each([ - ['lib/handlebars/base.js', (/const VERSION = ['"](.*)['"];/), 'const VERSION = \'' + version + '\';'], - ['components/bower.json', (/"version":.*/), '"version": "' + version + '",'], - ['components/package.json', /"version":.*/, '"version": "' + version + '",'], - ['components/handlebars.js.nuspec', (/.*<\/version>/), '' + version + ''] - ], - function(args, callback) { - replace.apply(undefined, args); - grunt.log.writeln(' - ' + args[0]); - git.add(args[0], callback); - }, - function() { - grunt.task.run(['default']); - done(); - }); - }); + async.each( + [ + [ + 'lib/handlebars/base.js', + /const VERSION = ['"](.*)['"];/, + "const VERSION = '" + version + "';" + ], + [ + 'components/bower.json', + /"version":.*/, + '"version": "' + version + '",' + ], + [ + 'components/package.json', + /"version":.*/, + '"version": "' + version + '",' + ], + [ + 'components/handlebars.js.nuspec', + /.*<\/version>/, + '' + version + '' + ] + ], + function(args, callback) { + replace.apply(undefined, args); + grunt.log.writeln(' - ' + args[0]); + git.add(args[0], callback); + }, + function() { + grunt.task.run(['default']); + done(); + } + ); + } + ); function replace(path, regex, value) { var content = grunt.file.read(path);