diff options
author | Ryan Lewis <ryanharrisonlewis@gmail.com> | 2015-12-13 20:42:13 -0800 |
---|---|---|
committer | Ryan Lewis <ryanharrisonlewis@gmail.com> | 2015-12-13 20:42:13 -0800 |
commit | 21bf95c92a1c04fd8a31742c1ea21ccb2d195c9c (patch) | |
tree | a120f52441e2f62ecb61696a4f67fdc1c15273b4 | |
parent | cc0b239aafefdef0342334b90f3c9b3ac6b19cea (diff) | |
parent | a6121cae797161f74bdd5a25e0c56379992557d7 (diff) | |
download | handlebars.js-21bf95c92a1c04fd8a31742c1ea21ccb2d195c9c.zip handlebars.js-21bf95c92a1c04fd8a31742c1ea21ccb2d195c9c.tar.gz handlebars.js-21bf95c92a1c04fd8a31742c1ea21ccb2d195c9c.tar.bz2 |
Merge branch 'master' of https://github.com/ryanmurakami/handlebars.js
-rw-r--r-- | .travis.yml | 4 | ||||
-rw-r--r-- | Gruntfile.js | 11 | ||||
-rw-r--r-- | components/bower.json | 2 | ||||
-rw-r--r-- | components/handlebars.js.nuspec | 2 | ||||
-rw-r--r-- | lib/handlebars/base.js | 2 | ||||
-rw-r--r-- | lib/handlebars/compiler/compiler.js | 61 | ||||
-rw-r--r-- | lib/handlebars/compiler/javascript-compiler.js | 106 | ||||
-rw-r--r-- | lib/handlebars/exception.js | 19 | ||||
-rw-r--r-- | lib/handlebars/helpers/block-helper-missing.js | 12 | ||||
-rw-r--r-- | lib/handlebars/helpers/each.js | 15 | ||||
-rw-r--r-- | lib/handlebars/helpers/with.js | 8 | ||||
-rw-r--r-- | lib/handlebars/no-conflict.js | 1 | ||||
-rw-r--r-- | lib/handlebars/runtime.js | 26 | ||||
-rw-r--r-- | lib/handlebars/utils.js | 8 | ||||
-rw-r--r-- | package.json | 8 | ||||
-rwxr-xr-x | print-script | 1 | ||||
-rw-r--r-- | release-notes.md | 14 | ||||
-rw-r--r-- | spec/env/browser.js | 7 | ||||
-rw-r--r-- | spec/env/runner.js | 24 | ||||
-rw-r--r-- | spec/env/runtime.js | 7 | ||||
-rw-r--r-- | spec/regressions.js | 21 | ||||
-rw-r--r-- | spec/runtime.js | 37 | ||||
-rw-r--r-- | spec/string-params.js | 176 | ||||
-rw-r--r-- | spec/subexpressions.js | 44 | ||||
-rw-r--r-- | spec/track-ids.js | 237 | ||||
-rw-r--r-- | tasks/test.js | 11 |
26 files changed, 122 insertions, 742 deletions
diff --git a/.travis.yml b/.travis.yml index c6e2c84..00c8b00 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,12 +13,12 @@ env: - secure: Nm4AgSfsgNB21kgKrF9Tl7qVZU8YYREhouQunFracTcZZh2NZ2XH5aHuSiXCj88B13Cr/jGbJKsZ4T3QS3wWYtz6lkyVOx3H3iI+TMtqhD9RM3a7A4O+4vVN8IioB2YjhEu0OKjwgX5gp+0uF+pLEi7Hpj6fupD3AbbL5uYcKg8= matrix: include: - - node_js: '0.12' + - node_js: '5' env: - PUBLISH=true - secure: pLTzghtVll9yGKJI0AaB0uI8GypfWxLTaIB0ZL8//yN3nAEIKMhf/RRilYTsn/rKj2NUa7vt2edYILi3lttOUlCBOwTc9amiRms1W8Lwr/3IdWPeBLvLuH1zNJRm2lBAwU4LBSqaOwhGaxOQr6KHTnWudhNhgOucxpZfvfI/dFw= - secure: yERYCf7AwL11D9uMtacly/THGV8BlzsMmrt+iQVvGA3GaY6QMmfYqf6P6cCH98sH5etd1Y+1e6YrPeMjqI6lyRllT7FptoyOdHulazQe86VQN4sc0EpqMlH088kB7gGjTut9Z+X9ViooT5XEh9WA5jXEI9pXhQJNoIHkWPuwGuY= - - node_js: '4.0.0' + - node_js: '4' cache: directories: - node_modules diff --git a/Gruntfile.js b/Gruntfile.js index 3bb1c16..97cfff0 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -125,7 +125,7 @@ module.exports = function(grunt) { options: { mangle: true, compress: true, - preserveComments: 'some' + preserveComments: /(?:^!|@(?:license|preserve|cc_on))/ }, dist: { files: [{ @@ -166,11 +166,10 @@ module.exports = function(grunt) { browsers: [ {browserName: 'chrome'}, {browserName: 'firefox', platform: 'Linux'}, - {browserName: 'safari', version: 7, platform: 'OS X 10.9'}, - {browserName: 'safari', version: 6, platform: 'OS X 10.8'}, + {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: 9, platform: 'Windows 7'} + {browserName: 'internet explorer', version: 10, platform: 'Windows 8'} ] } }, @@ -211,7 +210,7 @@ module.exports = function(grunt) { this.registerTask('globals', ['webpack']); this.registerTask('tests', ['concat:tests']); - this.registerTask('release', 'Build final packages', ['eslint', 'amd', 'uglify', 'copy:dist', 'copy:components', 'copy:cdnjs']); + this.registerTask('release', 'Build final packages', ['eslint', 'amd', 'uglify', 'test:min', 'copy:dist', 'copy:components', 'copy:cdnjs']); // Load tasks from npm grunt.loadNpmTasks('grunt-contrib-clean'); diff --git a/components/bower.json b/components/bower.json index e4aaab9..840c772 100644 --- a/components/bower.json +++ b/components/bower.json @@ -1,6 +1,6 @@ { "name": "handlebars", - "version": "4.0.4", + "version": "4.0.5", "main": "handlebars.js", "license": "MIT", "dependencies": {} diff --git a/components/handlebars.js.nuspec b/components/handlebars.js.nuspec index 326827e..9ede3d6 100644 --- a/components/handlebars.js.nuspec +++ b/components/handlebars.js.nuspec @@ -2,7 +2,7 @@ <package> <metadata> <id>handlebars.js</id> - <version>4.0.4</version> + <version>4.0.5</version> <authors>handlebars.js Authors</authors> <licenseUrl>https://github.com/wycats/handlebars.js/blob/master/LICENSE</licenseUrl> <projectUrl>https://github.com/wycats/handlebars.js/</projectUrl> diff --git a/lib/handlebars/base.js b/lib/handlebars/base.js index 84a8915..836422d 100644 --- a/lib/handlebars/base.js +++ b/lib/handlebars/base.js @@ -4,7 +4,7 @@ import {registerDefaultHelpers} from './helpers'; import {registerDefaultDecorators} from './decorators'; import logger from './logger'; -export const VERSION = '4.0.4'; +export const VERSION = '4.0.5'; export const COMPILER_REVISION = 7; export const REVISION_CHANGES = { diff --git a/lib/handlebars/compiler/compiler.js b/lib/handlebars/compiler/compiler.js index 987d0d4..040e99a 100644 --- a/lib/handlebars/compiler/compiler.js +++ b/lib/handlebars/compiler/compiler.js @@ -49,8 +49,6 @@ Compiler.prototype = { this.opcodes = []; this.children = []; this.options = options; - this.stringParams = options.stringParams; - this.trackIds = options.trackIds; options.blockParams = options.blockParams || []; @@ -393,49 +391,7 @@ Compiler.prototype = { }, pushParam: function(val) { - let value = val.value != null ? val.value : val.original || ''; - - if (this.stringParams) { - if (value.replace) { - value = value - .replace(/^(\.?\.\/)*/g, '') - .replace(/\//g, '.'); - } - - if (val.depth) { - this.addDepth(val.depth); - } - this.opcode('getContext', val.depth || 0); - this.opcode('pushStringParam', value, val.type); - - if (val.type === 'SubExpression') { - // SubExpressions get evaluated and passed in - // in string params mode. - this.accept(val); - } - } else { - if (this.trackIds) { - let blockParamIndex; - if (val.parts && !AST.helpers.scopedId(val) && !val.depth) { - blockParamIndex = this.blockParamIndex(val.parts[0]); - } - if (blockParamIndex) { - let blockParamChild = val.parts.slice(1).join('.'); - this.opcode('pushId', 'BlockParam', blockParamIndex, blockParamChild); - } else { - value = val.original || value; - if (value.replace) { - value = value - .replace(/^this(?:\.|$)/, '') - .replace(/^\.\//, '') - .replace(/^\.$/, ''); - } - - this.opcode('pushId', val.type, value); - } - } - this.accept(val); - } + this.accept(val); }, setupFullMustacheParams: function(sexpr, program, inverse, omitEmpty) { @@ -505,25 +461,12 @@ export function compile(input, options = {}, env) { } // Template is only compiled on first use and cached after that point. - function ret(context, execOptions) { + return function(context, execOptions) { if (!compiled) { compiled = compileInput(); } return compiled.call(this, context, execOptions); - } - ret._setup = function(setupOptions) { - if (!compiled) { - compiled = compileInput(); - } - return compiled._setup(setupOptions); - }; - ret._child = function(i, data, blockParams, depths) { - if (!compiled) { - compiled = compileInput(); - } - return compiled._child(i, data, blockParams, depths); }; - return ret; } function argEquals(a, b) { diff --git a/lib/handlebars/compiler/javascript-compiler.js b/lib/handlebars/compiler/javascript-compiler.js index 97939df..1708032 100644 --- a/lib/handlebars/compiler/javascript-compiler.js +++ b/lib/handlebars/compiler/javascript-compiler.js @@ -57,8 +57,6 @@ JavaScriptCompiler.prototype = { compile: function(environment, options, context, asObject) { this.environment = environment; this.options = options; - this.stringParams = this.options.stringParams; - this.trackIds = this.options.trackIds; this.precompile = !asObject; this.name = this.environment.name; @@ -498,37 +496,7 @@ JavaScriptCompiler.prototype = { this.push([this.aliasable('container.lambda'), '(', this.popStack(), ', ', this.contextName(0), ')']); }, - // [pushStringParam] - // - // On stack, before: ... - // On stack, after: string, currentContext, ... - // - // This opcode is designed for use in string mode, which - // provides the string value of a parameter along with its - // depth rather than resolving it immediately. - pushStringParam: function(string, type) { - this.pushContext(); - this.pushString(type); - - // If it's a subexpression, the string result - // will be pushed after this opcode. - if (type !== 'SubExpression') { - if (typeof string === 'string') { - this.pushString(string); - } else { - this.pushStackLiteral(string); - } - } - }, - emptyHash: function(omitEmpty) { - if (this.trackIds) { - this.push('{}'); // hashIds - } - if (this.stringParams) { - this.push('{}'); // hashContexts - this.push('{}'); // hashTypes - } this.pushStackLiteral(omitEmpty ? 'undefined' : '{}'); }, pushHash: function() { @@ -541,14 +509,6 @@ JavaScriptCompiler.prototype = { let hash = this.hash; this.hash = this.hashes.pop(); - if (this.trackIds) { - this.push(this.objectLiteral(hash.ids)); - } - if (this.stringParams) { - this.push(this.objectLiteral(hash.contexts)); - this.push(this.objectLiteral(hash.types)); - } - this.push(this.objectLiteral(hash.values)); }, @@ -727,44 +687,7 @@ 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; - - if (this.trackIds) { - id = this.popStack(); - } - if (this.stringParams) { - type = this.popStack(); - context = this.popStack(); - } - - let hash = this.hash; - if (context) { - hash.contexts[key] = context; - } - if (type) { - hash.types[key] = type; - } - if (id) { - hash.ids[key] = id; - } - hash.values[key] = value; - }, - - pushId: function(type, name, child) { - if (type === 'BlockParam') { - this.pushStackLiteral( - 'blockParams[' + name[0] + '].path[' + name[1] + ']' - + (child ? ' + ' + JSON.stringify('.' + child) : '')); - } else if (type === 'PathExpression') { - this.pushString(name); - } else if (type === 'SubExpression') { - this.pushStackLiteral('true'); - } else { - this.pushStackLiteral('null'); - } + this.hash.values[key] = this.popStack(); }, // HELPERS @@ -997,9 +920,6 @@ JavaScriptCompiler.prototype = { setupParams: function(helper, paramSize, params) { let options = {}, - contexts = [], - types = [], - ids = [], objectArgs = !params, param; @@ -1010,14 +930,6 @@ JavaScriptCompiler.prototype = { options.name = this.quotedString(helper); options.hash = this.popStack(); - if (this.trackIds) { - options.hashIds = this.popStack(); - } - if (this.stringParams) { - options.hashTypes = this.popStack(); - options.hashContexts = this.popStack(); - } - let inverse = this.popStack(), program = this.popStack(); @@ -1034,28 +946,12 @@ JavaScriptCompiler.prototype = { while (i--) { param = this.popStack(); params[i] = param; - - if (this.trackIds) { - ids[i] = this.popStack(); - } - if (this.stringParams) { - types[i] = this.popStack(); - contexts[i] = this.popStack(); - } } if (objectArgs) { options.args = this.source.generateArray(params); } - if (this.trackIds) { - options.ids = this.source.generateArray(ids); - } - if (this.stringParams) { - options.types = this.source.generateArray(types); - options.contexts = this.source.generateArray(contexts); - } - if (this.options.data) { options.data = 'data'; } diff --git a/lib/handlebars/exception.js b/lib/handlebars/exception.js index 52499c0..af675d9 100644 --- a/lib/handlebars/exception.js +++ b/lib/handlebars/exception.js @@ -12,7 +12,7 @@ function Exception(message, node) { message += ' - ' + line + ':' + column; } - let tmp = Error.prototype.constructor.call(this, message); + let tmp = Error.prototype.constructor.call(this, message, loc && loc.source, line); // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. for (let idx = 0; idx < errorProps.length; idx++) { @@ -24,9 +24,20 @@ function Exception(message, node) { Error.captureStackTrace(this, Exception); } - if (loc) { - this.lineNumber = line; - this.column = column; + try { + if (loc) { + this.lineNumber = line; + + // Work around issue under safari where we can't directly set the column value + /* istanbul ignore next */ + if (Object.defineProperty) { + Object.defineProperty(this, 'column', {value: column}); + } else { + this.column = column; + } + } + } catch (nop) { + /* Ignore if the browser is very particular */ } } diff --git a/lib/handlebars/helpers/block-helper-missing.js b/lib/handlebars/helpers/block-helper-missing.js index 6639ddb..e6d162f 100644 --- a/lib/handlebars/helpers/block-helper-missing.js +++ b/lib/handlebars/helpers/block-helper-missing.js @@ -1,4 +1,4 @@ -import {appendContextPath, createFrame, isArray} from '../utils'; +import {isArray} from '../utils'; export default function(instance) { instance.registerHelper('blockHelperMissing', function(context, options) { @@ -11,21 +11,11 @@ export default function(instance) { return inverse(this); } else if (isArray(context)) { if (context.length > 0) { - if (options.ids) { - options.ids = [options.name]; - } - return instance.helpers.each(context, options); } else { return inverse(this); } } else { - if (options.data && options.ids) { - let data = createFrame(options.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 fb11903..914928d 100644 --- a/lib/handlebars/helpers/each.js +++ b/lib/handlebars/helpers/each.js @@ -1,4 +1,4 @@ -import {appendContextPath, blockParams, createFrame, isArray, isFunction} from '../utils'; +import {createFrame, isArray, isFunction} from '../utils'; import Exception from '../exception'; export default function(instance) { @@ -11,12 +11,7 @@ export default function(instance) { inverse = options.inverse, i = 0, ret = '', - data, - contextPath; - - if (options.data && options.ids) { - contextPath = appendContextPath(options.data.contextPath, options.ids[0]) + '.'; - } + data; if (isFunction(context)) { context = context.call(this); } @@ -30,15 +25,11 @@ export default function(instance) { data.index = index; data.first = index === 0; data.last = !!last; - - if (contextPath) { - data.contextPath = contextPath + field; - } } ret = ret + fn(context[field], { data: data, - blockParams: blockParams([context[field], field], [contextPath + field, null]) + blockParams: [context[field], field] }); } diff --git a/lib/handlebars/helpers/with.js b/lib/handlebars/helpers/with.js index 7418cd0..bb352c5 100644 --- a/lib/handlebars/helpers/with.js +++ b/lib/handlebars/helpers/with.js @@ -1,4 +1,4 @@ -import {appendContextPath, blockParams, createFrame, isEmpty, isFunction} from '../utils'; +import {isEmpty, isFunction} from '../utils'; export default function(instance) { instance.registerHelper('with', function(context, options) { @@ -8,14 +8,10 @@ export default function(instance) { if (!isEmpty(context)) { let data = options.data; - if (options.data && options.ids) { - data = createFrame(options.data); - data.contextPath = appendContextPath(options.data.contextPath, options.ids[0]); - } return fn(context, { data: data, - blockParams: blockParams([context], [data && data.contextPath]) + blockParams: [context] }); } else { return options.inverse(this); diff --git a/lib/handlebars/no-conflict.js b/lib/handlebars/no-conflict.js index ad41e96..40a44d7 100644 --- a/lib/handlebars/no-conflict.js +++ b/lib/handlebars/no-conflict.js @@ -8,5 +8,6 @@ export default function(Handlebars) { if (root.Handlebars === Handlebars) { root.Handlebars = $Handlebars; } + return Handlebars; }; } diff --git a/lib/handlebars/runtime.js b/lib/handlebars/runtime.js index b47d961..55eb1c1 100644 --- a/lib/handlebars/runtime.js +++ b/lib/handlebars/runtime.js @@ -38,9 +38,6 @@ export function template(templateSpec, env) { function invokePartialWrapper(partial, context, options) { if (options.hash) { context = Utils.extend({}, context, options.hash); - if (options.ids) { - options.ids[0] = true; - } } partial = env.VM.resolvePartial.call(this, partial, context, options); @@ -132,7 +129,7 @@ export function template(templateSpec, env) { function ret(context, options = {}) { let data = options.data; - ret._setup(options); + _setup(options); if (!options.partial && templateSpec.useData) { data = initData(context, data); } @@ -140,7 +137,7 @@ export function template(templateSpec, env) { 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]; } @@ -154,7 +151,7 @@ export function template(templateSpec, env) { } ret.isTop = true; - ret._setup = function(options) { + function _setup(options) { if (!options.partial) { container.helpers = container.merge(options.helpers, env.helpers); @@ -169,25 +166,15 @@ export function template(templateSpec, env) { container.partials = options.partials; container.decorators = options.decorators; } - }; - - ret._child = function(i, data, blockParams, depths) { - if (templateSpec.useBlockParams && !blockParams) { - throw new Exception('must pass block params'); - } - if (templateSpec.useDepths && !depths) { - throw new Exception('must pass parent depths'); - } + } - return wrapProgram(container, i, templateSpec[i], data, 0, blockParams, depths); - }; return ret; } export function wrapProgram(container, i, fn, data, declaredBlockParams, blockParams, depths) { function prog(context, options = {}) { let currentDepths = depths; - if (depths && context !== depths[0]) { + if (depths && context != depths[0]) { currentDepths = [context].concat(depths); } @@ -224,9 +211,6 @@ export function resolvePartial(partial, context, options) { export function invokePartial(partial, context, options) { options.partial = true; - if (options.ids) { - options.data.contextPath = options.ids[0] || options.data.contextPath; - } let partialBlock; if (options.fn && options.fn !== noop) { diff --git a/lib/handlebars/utils.js b/lib/handlebars/utils.js index 2584601..9d08394 100644 --- a/lib/handlebars/utils.js +++ b/lib/handlebars/utils.js @@ -98,11 +98,3 @@ export function createFrame(object) { return frame; } -export function blockParams(params, ids) { - params.path = ids; - return params; -} - -export function appendContextPath(contextPath, id) { - return (contextPath ? contextPath + '.' : '') + id; -} diff --git a/package.json b/package.json index 6053803..a5b7887 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "handlebars", "barename": "handlebars", - "version": "4.0.4", + "version": "4.0.5", "description": "Handlebars provides the power necessary to let you build semantic templates effectively with no frustration", "homepage": "http://www.handlebarsjs.com/", "keywords": [ @@ -26,7 +26,7 @@ "source-map": "^0.4.4" }, "optionalDependencies": { - "uglify-js": "~2.4" + "uglify-js": "^2.6" }, "devDependencies": { "aws-sdk": "^2.1.49", @@ -54,7 +54,9 @@ "mock-stdin": "^0.3.0", "mustache": "^2.1.3", "semver": "^5.0.1", - "underscore": "^1.5.1" + "underscore": "^1.5.1", + "webpack": "^1.12.6", + "webpack-dev-server": "^1.12.1" }, "main": "lib/index.js", "bin": { diff --git a/print-script b/print-script index 046b99c..8464698 100755 --- a/print-script +++ b/print-script @@ -16,7 +16,6 @@ var template = Handlebars.precompile(script, { assumeObjects: true, compat: false, strict: true, - trackIds: true, knownHelpersOnly: false }); diff --git a/release-notes.md b/release-notes.md index 5ea873e..497f5e8 100644 --- a/release-notes.md +++ b/release-notes.md @@ -2,7 +2,19 @@ ## Development -[Commits](https://github.com/wycats/handlebars.js/compare/v4.0.4...master) +[Commits](https://github.com/wycats/handlebars.js/compare/v4.0.5...master) + +## v4.0.5 - November 19th, 2015 +- [#1132](https://github.com/wycats/handlebars.js/pull/1132) - Update uglify-js to avoid vulnerability ([@plynchnlm](https://api.github.com/users/plynchnlm)) +- [#1129](https://github.com/wycats/handlebars.js/issues/1129) - Minified lib returns an empty string ([@bricss](https://api.github.com/users/bricss)) +- Return current handlebars instance from noConflict - 685cf92 +- Add webpack to dev dependency to support npm 3 - 7a6c228 +- Further relax uglify dependency - 0a3b3c2 +- Include tests for minimized artifacts - c21118d +- Fix lint errors under latest eslint - 9f59de9 +- Add print-script helper script - 98a6717 + +[Commits](https://github.com/wycats/handlebars.js/compare/v4.0.4...v4.0.5) ## v4.0.4 - October 29th, 2015 - [#1121](https://github.com/wycats/handlebars.js/pull/1121) - Include partial name in 'undefined partial' exception message ([@shinypb](https://api.github.com/users/shinypb)) diff --git a/spec/env/browser.js b/spec/env/browser.js index 8049dda..60a5d35 100644 --- a/spec/env/browser.js +++ b/spec/env/browser.js @@ -4,7 +4,12 @@ var fs = require('fs'), vm = require('vm'); global.Handlebars = 'no-conflict'; -vm.runInThisContext(fs.readFileSync(__dirname + '/../../dist/handlebars.js'), 'dist/handlebars.js'); + +var filename = 'dist/handlebars.js'; +if (global.minimizedTest) { + filename = 'dist/handlebars.min.js'; +} +vm.runInThisContext(fs.readFileSync(__dirname + '/../../' + filename), filename); global.CompilerContext = { browser: true, diff --git a/spec/env/runner.js b/spec/env/runner.js index 4ff1e7e..f4b23d8 100644 --- a/spec/env/runner.js +++ b/spec/env/runner.js @@ -7,19 +7,35 @@ var errors = 0, testDir = path.dirname(__dirname), grep = process.argv[2]; +// Lazy hack, but whatever +if (grep === '--min') { + global.minimizedTest = true; + grep = undefined; +} + var files = fs.readdirSync(testDir) .filter(function(name) { return (/.*\.js$/).test(name); }) .map(function(name) { return testDir + '/' + name; }); -run('./runtime', function() { - run('./browser', function() { - run('./node', function() { +if (global.minimizedTest) { + run('./runtime', function() { + run('./browser', function() { /* eslint-disable no-process-exit */ process.exit(errors); /* eslint-enable no-process-exit */ }); }); -}); +} else { + run('./runtime', function() { + run('./browser', function() { + run('./node', function() { + /* eslint-disable no-process-exit */ + process.exit(errors); + /* eslint-enable no-process-exit */ + }); + }); + }); +} function run(env, callback) { diff --git a/spec/env/runtime.js b/spec/env/runtime.js index 9d1c049..642acd3 100644 --- a/spec/env/runtime.js +++ b/spec/env/runtime.js @@ -4,7 +4,12 @@ var fs = require('fs'), vm = require('vm'); global.Handlebars = 'no-conflict'; -vm.runInThisContext(fs.readFileSync(__dirname + '/../../dist/handlebars.runtime.js'), 'dist/handlebars.runtime.js'); + +var filename = 'dist/handlebars.runtime.js'; +if (global.minimizedTest) { + filename = 'dist/handlebars.runtime.min.js'; +} +vm.runInThisContext(fs.readFileSync(__dirname + '/../../' + filename), filename); var parse = require('../../dist/cjs/handlebars/compiler/base').parse; var compiler = require('../../dist/cjs/handlebars/compiler/compiler'); diff --git a/spec/regressions.js b/spec/regressions.js index 83765a2..a1eec2f 100644 --- a/spec/regressions.js +++ b/spec/regressions.js @@ -247,4 +247,25 @@ describe('Regressions', function() { }; shouldCompileToWithPartials(string, [{}, {}, partials], true, 'Outer'); }); + + it('GH-1135 : Context handling within each iteration', function() { + var obj = {array: [1], name: 'John'}; + var helpers = { + myif: function(conditional, options) { + if (conditional) { + return options.fn(this); + } else { + 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'); + }); }); diff --git a/spec/runtime.js b/spec/runtime.js index a4830ad..2a85899 100644 --- a/spec/runtime.js +++ b/spec/runtime.js @@ -32,43 +32,6 @@ describe('runtime', function() { }); }); - describe('#child', function() { - if (!Handlebars.compile) { - return; - } - - 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'); - }); - - 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'); - }); - it('should expose child template', function() { - var template = Handlebars.compile('{{#foo}}bar{{/foo}}'); - // 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'); - }); - }); - describe('#noConflict', function() { if (!CompilerContext.browser) { return; diff --git a/spec/string-params.js b/spec/string-params.js deleted file mode 100644 index b76f291..0000000 --- a/spec/string-params.js +++ /dev/null @@ -1,176 +0,0 @@ -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 helpers = { - wycats: function(passiveVoice, noun) { - return 'HELP ME MY BOSS ' + passiveVoice + ' ' + noun; - } - }; - - var result = template({}, {helpers: helpers}); - - 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 helpers = { - wycats: function(passiveVoice, noun, options) { - return 'HELP ME MY BOSS ' + passiveVoice + ' ' + - noun + ': ' + options.fn(this); - } - }; - - var result = template({}, {helpers: helpers}); - - 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 helpers = { - tomdale: function(desire, noun, options) { - return 'STOP ME FROM READING HACKER NEWS I ' + - options.contexts[0][desire] + ' ' + noun; - }, - - 'with': function(context, options) { - return options.fn(options.contexts[0][context]); - } - }; - - 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'); - }); - - it('information about the types is passed along', function() { - 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(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'); - equal(falseBool, false, 'raw booleans are passed through'); - return 'Helper called'; - } - }; - - var result = template({}, { helpers: helpers }); - equal(result, 'Helper called'); - }); - - it('hash parameters get type information', function() { - var template = CompilerContext.compile("{{tomdale he.says desire='need' noun=dad.joke bool=true}}", { stringParams: true }); - - var helpers = { - tomdale: function(exclamation, options) { - equal(exclamation, 'he.says'); - equal(options.types[0], 'PathExpression'); - - equal(options.hashTypes.desire, 'StringLiteral'); - equal(options.hashTypes.noun, 'PathExpression'); - equal(options.hashTypes.bool, 'BooleanLiteral'); - equal(options.hash.desire, 'need'); - equal(options.hash.noun, 'dad.joke'); - equal(options.hash.bool, true); - return 'Helper called'; - } - }; - - var result = template({}, { helpers: helpers }); - equal(result, 'Helper called'); - }); - - 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 context = {dale: {}}; - - var helpers = { - tomdale: function(exclamation, options) { - equal(exclamation, 'he.says'); - equal(options.types[0], 'PathExpression'); - - equal(options.contexts.length, 1); - equal(options.hashContexts.noun, context); - equal(options.hash.desire, 'need'); - equal(options.hash.noun, 'dad.joke'); - equal(options.hash.bool, true); - return 'Helper called'; - }, - 'with': function(withContext, options) { - return options.fn(options.contexts[0][withContext]); - } - }; - - var result = template(context, { helpers: helpers }); - equal(result, 'Helper called'); - }); - - 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 helpers = { - tomdale: function(desire, noun, options) { - return 'STOP ME FROM READING HACKER NEWS I ' + - options.contexts[0][desire] + ' ' + noun + ' ' + - options.fn(this); - }, - - 'with': function(context, options) { - return options.fn(options.contexts[0][context]); - } - }; - - 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'); - }); - - it('with nested block ambiguous', function() { - var template = CompilerContext.compile('{{#with content}}{{#view}}{{firstName}} {{lastName}}{{/view}}{{/with}}', {stringParams: true}); - - var helpers = { - 'with': function() { - return 'WITH'; - }, - view: function() { - return 'VIEW'; - } - }; - - var result = template({}, {helpers: helpers}); - equals(result, 'WITH'); - }); - - it('should handle DATA', function() { - var template = CompilerContext.compile('{{foo @bar}}', { stringParams: true }); - - var helpers = { - foo: function(bar, options) { - equal(bar, '@bar'); - equal(options.types[0], 'PathExpression'); - return 'Foo!'; - } - }; - - var result = template({}, { helpers: helpers }); - equal(result, 'Foo!'); - }); -}); diff --git a/spec/subexpressions.js b/spec/subexpressions.js index dad741e..3810eb8 100644 --- a/spec/subexpressions.js +++ b/spec/subexpressions.js @@ -162,50 +162,6 @@ describe('subexpressions', function() { shouldCompileTo(string, [context, helpers], '<input aria-label="Name" placeholder="Example User" />'); }); - it('in string params mode,', function() { - 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'); - 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'); - return a; - } - }; - - 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 helpers = { - blog: function(options) { - equals(options.hashTypes.fun, 'SubExpression'); - return 'val is ' + options.hash.fun; - }, - bork: function() { - return 'BORK'; - } - }; - - var result = template({}, {helpers: helpers}); - equals(result, 'val is BORK'); - }); - it('subexpression functions on the context', function() { var string = '{{foo (bar)}}!'; var context = { diff --git a/spec/track-ids.js b/spec/track-ids.js deleted file mode 100644 index 30a4661..0000000 --- a/spec/track-ids.js +++ /dev/null @@ -1,237 +0,0 @@ -describe('track ids', function() { - var context; - beforeEach(function() { - context = {is: {a: 'foo'}, slave: {driver: 'bar'}}; - }); - - it('should not include anything without the flag', function() { - var template = CompilerContext.compile('{{wycats is.a slave.driver}}'); - - var helpers = { - wycats: function(passiveVoice, noun, options) { - equal(options.ids, undefined); - equal(options.hashIds, undefined); - - return 'success'; - } - }; - - equals(template({}, {helpers: helpers}), 'success'); - }); - it('should include argument ids', function() { - 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; - } - }; - - 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 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; - } - }; - - 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 helpers = { - wycats: function(passiveVoice, noun, thiz, thiz2, options) { - equal(options.ids[0], 'is.a'); - equal(options.ids[1], '../slave.driver'); - equal(options.ids[2], 'is.a'); - equal(options.ids[3], ''); - - 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'); - }); - it('should note @data references', function() { - 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; - } - }; - - 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 helpers = { - wycats: function(passiveVoice, noun, options) { - equal(options.ids[0], null); - equal(options.ids[1], null); - equal(options.hashIds.key, null); - - return 'HELP ME MY BOSS ' + passiveVoice + ' ' + noun + ' ' + options.hash.key; - } - }; - - 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 helpers = { - sub: function() { return 1; }, - wycats: function(passiveVoice, options) { - equal(options.ids[0], true); - - return 'HELP ME MY BOSS ' + passiveVoice; - } - }; - - 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 helpers = { - doIt: function(options) { - var blockParams = [this.is]; - blockParams.path = ['zomg']; - 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; - } - }; - - equals(template(context, {helpers: helpers}), 'HELP ME MY BOSS zomg.a:foo slave.driver:bar'); - }); - - describe('builtin helpers', function() { - var helpers = { - blockParams: function(name, options) { - return name + ':' + options.ids[0] + '\n'; - }, - wycats: function(name, options) { - return name + ':' + options.data.contextPath + '\n'; - } - }; - - 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'); - }); - 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'); - }); - 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'); - }); - 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'); - }); - }); - 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'); - }); - 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'); - }); - }); - 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'); - }); - 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'); - }); - 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'); - }); - }); - }); - - describe('partials', function() { - var helpers = { - blockParams: function(name, options) { - return name + ':' + options.ids[0] + '\n'; - }, - wycats: function(name, options) { - return name + ':' + options.data.contextPath + '\n'; - } - }; - - 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 partials = { - dude: CompilerContext.compile('{{wycats name}}', {trackIds: true}) - }; - - 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 partials = { - 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'); - }); - - 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 partials = { - dude: CompilerContext.compile('{{wycats name}}', {trackIds: true}) - }; - - equals(template(hash, {helpers: helpers, partials: partials}), 'Dudes: Yehuda:true\nAlan:true\n'); - }); - }); -}); diff --git a/tasks/test.js b/tasks/test.js index 7d6659b..18a6c26 100644 --- a/tasks/test.js +++ b/tasks/test.js @@ -40,6 +40,17 @@ module.exports = function(grunt) { done(); }); }); + grunt.registerTask('test:min', function() { + var done = this.async(); + + var runner = childProcess.fork('./spec/env/runner', ['--min'], {stdio: 'inherit'}); + runner.on('close', function(code) { + if (code != 0) { + grunt.fatal(code + ' tests failed'); + } + done(); + }); + }); grunt.registerTask('test:check-cov', function() { var done = this.async(); |