From 79309659e1496fead1d0a4743286398f1512b666 Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Fri, 13 Oct 2017 23:54:53 +0200 Subject: [PATCH 1/9] Gracefully handle missing uglify-js dependency closes #1391 uglify-js is an optional dependency and should be treated as such. This commit gracefully handles MODULE_NOT_FOUND errors while loading uglify. - Check for existing uglify-js (and load uglify-js) only if minification was activated - Use "require.resolve" to check if uglify exists. Otherwise, a missing dependency of uglify-js would cause the same behavior as missing uglify-js. (Only a warning, no error) - The code to load and run uglify is put into a single for readability purposes - Tests use a mockup Module._resolveFilename to simulate the missing module. This function is used by both "require" and "require.resolve", so both are mocked equally. (cherry picked from commit d5caa56) --- lib/precompiler.js | 40 +++++++++++++++++++++++------ spec/precompiler.js | 61 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 8 deletions(-) diff --git a/lib/precompiler.js b/lib/precompiler.js index 6ba3800c..64608f1e 100644 --- a/lib/precompiler.js +++ b/lib/precompiler.js @@ -4,7 +4,7 @@ import fs from 'fs'; import * as Handlebars from './handlebars'; import {basename} from 'path'; import {SourceMapConsumer, SourceNode} from 'source-map'; -import uglify from 'uglify-js'; + module.exports.loadTemplates = function(opts, callback) { loadStrings(opts, function(err, strings) { @@ -235,7 +235,6 @@ module.exports.cli = function(opts) { } } - if (opts.map) { output.add('\n//# sourceMappingURL=' + opts.map + '\n'); } @@ -244,12 +243,7 @@ module.exports.cli = function(opts) { output.map = output.map + ''; if (opts.min) { - output = uglify.minify(output.code, { - fromString: true, - - outSourceMap: opts.map, - inSourceMap: JSON.parse(output.map) - }); + output = minify(output, opts.map); } if (opts.map) { @@ -271,3 +265,33 @@ function arrayCast(value) { } return value; } + +/** + * Run uglify to minify the compiled template, if uglify exists in the dependencies. + * + * We are using `require` instead of `import` here, because es6-modules do not allow + * dynamic imports and uglify-js is an optional dependency. Since we are inside NodeJS here, this + * should not be a problem. + * + * @param {string} output the compiled template + * @param {string} sourceMapFile the file to write the source map to. + */ +function minify(output, sourceMapFile) { + try { + // Try to resolve uglify-js in order to see if it does exist + require.resolve('uglify-js'); + } catch (e) { + if (e.code !== 'MODULE_NOT_FOUND') { + // Something else seems to be wrong + throw e; + } + // it does not exist! + console.error('Code minimization is disabled due to missing uglify-js dependency'); + return output; + } + return require('uglify-js').minify(output.code, { + fromString: true, + outSourceMap: sourceMapFile, + inSourceMap: JSON.parse(output.map) + }); +} diff --git a/spec/precompiler.js b/spec/precompiler.js index 006a37e3..9f2a6442 100644 --- a/spec/precompiler.js +++ b/spec/precompiler.js @@ -12,6 +12,8 @@ describe('precompiler', function() { var log, logFunction, + errorLog, + errorLogFunction, precompile, minify, @@ -26,16 +28,51 @@ describe('precompiler', function() { content, writeFileSync; + /** + * Mock the Module.prototype.require-function such that an error is thrown, when "uglify-js" is loaded. + * + * The function cleans up its mess when "callback" is finished + * + * @param {Error} loadError the error that should be thrown if uglify is loaded + * @param {function} callback a callback-function to run when the mock is active. + */ + function mockRequireUglify(loadError, callback) { + var Module = require('module'); + var _resolveFilename = Module._resolveFilename; + delete require.cache[require.resolve('uglify-js')]; + delete require.cache[require.resolve('../dist/cjs/precompiler')]; + Module._resolveFilename = function(request, mod) { + if (request === 'uglify-js') { + throw loadError; + } + return _resolveFilename.call(this, request, mod); + }; + try { + callback(); + } finally { + Module._resolveFilename = _resolveFilename; + delete require.cache[require.resolve('uglify-js')]; + delete require.cache[require.resolve('../dist/cjs/precompiler')]; + } + } + beforeEach(function() { precompile = Handlebars.precompile; minify = uglify.minify; writeFileSync = fs.writeFileSync; + // Mock stdout and stderr logFunction = console.log; log = ''; console.log = function() { log += Array.prototype.join.call(arguments, ''); }; + errorLogFunction = console.error; + errorLog = ''; + console.error = function() { + errorLog += Array.prototype.join.call(arguments, ''); + }; + fs.writeFileSync = function(_file, _content) { file = _file; content = _content; @@ -46,6 +83,7 @@ describe('precompiler', function() { uglify.minify = minify; fs.writeFileSync = writeFileSync; console.log = logFunction; + console.error = errorLogFunction; }); it('should output version', function() { @@ -148,6 +186,29 @@ describe('precompiler', function() { equal(log, 'min'); }); + it('should omit minimization gracefully, if uglify-js is missing', function() { + var error = new Error("Cannot find module 'uglify-js'"); + 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}); + equal(/template\(amd\)/.test(log), true); + equal(/\n/.test(log), true); + equal(/Code minimization is disabled/.test(errorLog), true); + }); + }); + + 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'); + }); + }); + it('should output map', function() { Precompiler.cli({templates: [emptyTemplate], map: 'foo.js.map'}); From 21386b6474b6dbd731bcd92295ddf8ae7b48a3bf Mon Sep 17 00:00:00 2001 From: Marcos Marado Date: Thu, 5 Oct 2017 18:44:35 +0200 Subject: [PATCH 2/9] Update (C) year in the LICENSE file Welcome to 2017! (cherry picked from commit 33773c2) --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 307ebc1c..b802d14e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (C) 2011-2016 by Yehuda Katz +Copyright (C) 2011-2017 by Yehuda Katz Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 59548b4bdca44f139347697e52e9bcbb511ae6dd Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Thu, 24 Aug 2017 22:16:21 +0200 Subject: [PATCH 3/9] Extend compiler-api example by replacing child-compiler closes #1376 (cherry picked from commit ce3cd8a) --- docs/compiler-api.md | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/docs/compiler-api.md b/docs/compiler-api.md index 29382191..f419fbcd 100644 --- a/docs/compiler-api.md +++ b/docs/compiler-api.md @@ -66,7 +66,7 @@ interface MustacheStatement <: Statement { interface BlockStatement <: Statement { type: "BlockStatement"; - path: PathExpression; + path: PathExpression | Literal; params: [ Expression ]; hash: Hash; @@ -296,21 +296,43 @@ The `Handlebars.JavaScriptCompiler` object has a number of methods that may be c - `initializeBuffer()` Allows for buffers other than the default string buffer to be used. Generally needs to be paired with a custom `appendToBuffer` implementation. +### Example for the compiler api. + +This example changes all lookups of properties are performed by a helper (`lookupLowerCase`) which looks for `test` if `{{Test}}` occurs in the template. This is just to illustrate how compiler behavior can be change. + +There is also [a jsfiddle with this code](https://jsfiddle.net/9D88g/162/) if you want to play around with it. + + ```javascript function MyCompiler() { Handlebars.JavaScriptCompiler.apply(this, arguments); } -MyCompiler.prototype = Object.create(Handlebars.JavaScriptCompiler); +MyCompiler.prototype = new Handlebars.JavaScriptCompiler(); -MyCompiler.nameLookup = function(parent, name, type) { - if (type === 'partial') { - return 'MyPartialList[' + JSON.stringify(name) ']'; +// Use this compile to compile BlockStatment-Blocks +MyCompiler.prototype.compiler = MyCompiler + +MyCompiler.prototype.nameLookup = function(parent, name, type) { + if (type === 'context') { + return this.source.functionCall('helpers.lookupLowerCase', '', [parent, JSON.stringify(name)]) } else { return Handlebars.JavaScriptCompiler.prototype.nameLookup.call(this, parent, name, type); } -}; +} var env = Handlebars.create(); +env.registerHelper('lookupLowerCase', function(parent, name) { + return parent[name.toLowerCase()] +}) + env.JavaScriptCompiler = MyCompiler; -env.compile('my template'); + +var template = env.compile('{{#each Test}} ({{Value}}) {{/each}}'); +console.log(template({ + test: [ + {value: 'a'}, + {value: 'b'}, + {value: 'c'} + ] +})); ``` From 1ac131e652edd5d26b4d8ac34f90acf6e812f86f Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Tue, 17 Oct 2017 22:51:42 +0200 Subject: [PATCH 4/9] Update release notes --- release-notes.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/release-notes.md b/release-notes.md index 4db22231..fae71054 100644 --- a/release-notes.md +++ b/release-notes.md @@ -2,7 +2,17 @@ ## Development -[Commits](https://github.com/nknapp/handlebars.js/compare/v4.0.10...master) +[Commits](https://github.com/wycats/handlebars.js/compare/v4.0.11...master) + +## v4.0.11 - October 17th, 2017 +- [#1391](https://github.com/wycats/handlebars.js/issues/1391) - `uglify-js` is unconditionally imported, but only listed as optional dependency ([@Turbo87](https://api.github.com/users/Turbo87)) +- [#1233](https://github.com/wycats/handlebars.js/issues/1233) - Unable to build under windows - error at test:bin task ([@blikblum](https://api.github.com/users/blikblum)) +- Update (C) year in the LICENSE file - 21386b6 + +Compatibility notes: +- This is a bugfix release. There are no breaking change and no new features. + +[Commits](https://github.com/nknapp/handlebars.js/compare/v4.0.10...v4.0.11) ## v4.0.10 - May 21st, 2017 - Fix regression in 4.0.9: Replace "Object.assign" (not support in IE) by "util/extend" - 0e953d1 From 1e954ddf3c3ec6d2318e1fadc5e03aaf065b2fbd Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Tue, 17 Oct 2017 22:52:25 +0200 Subject: [PATCH 5/9] v4.0.11 --- components/bower.json | 2 +- components/handlebars.js.nuspec | 2 +- lib/handlebars/base.js | 2 +- package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/bower.json b/components/bower.json index 0bf954a4..7a28ba7b 100644 --- a/components/bower.json +++ b/components/bower.json @@ -1,6 +1,6 @@ { "name": "handlebars", - "version": "4.0.10", + "version": "4.0.11", "main": "handlebars.js", "license": "MIT", "dependencies": {} diff --git a/components/handlebars.js.nuspec b/components/handlebars.js.nuspec index 9b735a97..f84690c2 100644 --- a/components/handlebars.js.nuspec +++ b/components/handlebars.js.nuspec @@ -2,7 +2,7 @@ handlebars.js - 4.0.10 + 4.0.11 handlebars.js Authors https://github.com/wycats/handlebars.js/blob/master/LICENSE https://github.com/wycats/handlebars.js/ diff --git a/lib/handlebars/base.js b/lib/handlebars/base.js index d0ad1331..f1a39575 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.10'; +export const VERSION = '4.0.11'; export const COMPILER_REVISION = 7; export const REVISION_CHANGES = { diff --git a/package.json b/package.json index fcbabc2c..b458985e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "handlebars", "barename": "handlebars", - "version": "4.0.10", + "version": "4.0.11", "description": "Handlebars provides the power necessary to let you build semantic templates effectively with no frustration", "homepage": "http://www.handlebarsjs.com/", "keywords": [ From 8947dd077cc83d1ed947d42d9b145cc35d3e0fe8 Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Tue, 17 Oct 2017 23:15:53 +0200 Subject: [PATCH 6/9] Update jsfiddle to 4.0.11 --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 42cdc56d..71ce84a8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -96,4 +96,4 @@ After this point the handlebars site needs to be updated to point to the new ver [generator-release]: https://github.com/walmartlabs/generator-release [pull-request]: https://github.com/wycats/handlebars.js/pull/new/master [issue]: https://github.com/wycats/handlebars.js/issues/new -[jsfiddle]: https://jsfiddle.net/9D88g/113/ +[jsfiddle]: https://jsfiddle.net/9D88g/180/ From 7729aa956bfe4dae1cd0956626d6db3cb07e3bc6 Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Sat, 21 Oct 2017 15:42:27 +0200 Subject: [PATCH 7/9] Update grunt-eslint to 20.1.0 --- .eslintrc | 200 ------------------ .eslintrc.js | 129 +++++++++++ bench/templates/complex.js | 2 +- lib/handlebars/compiler/code-gen.js | 2 +- lib/handlebars/compiler/helpers.js | 6 +- .../compiler/javascript-compiler.js | 8 +- lib/handlebars/logger.js | 4 +- lib/precompiler.js | 2 +- package.json | 2 +- spec/ast.js | 2 + spec/helpers.js | 2 +- spec/parser.js | 12 +- spec/spec.js | 4 +- spec/tokenizer.js | 10 +- 14 files changed, 157 insertions(+), 228 deletions(-) delete mode 100644 .eslintrc create mode 100644 .eslintrc.js diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 8cd3bd6f..00000000 --- a/.eslintrc +++ /dev/null @@ -1,200 +0,0 @@ -{ - "globals": { - "self": false - }, - "env": { - "node": true - }, - "ecmaFeatures": { - // Enabling features that can be implemented without polyfills. Want to avoid polyfills at this time. - "arrowFunctions": true, - "blockBindings": true, - "defaultParams": true, - "destructuring": true, - "modules": true, - "objectLiteralComputedProperties": true, - "objectLiteralDuplicateProperties": true, - "objectLiteralShorthandMethods": true, - "objectLiteralShorthandProperties": true, - "restParams": true, - "spread": true, - "templateStrings": true - }, - "rules": { - // Possible Errors // - //-----------------// - - "comma-dangle": [2, "never"], - "no-cond-assign": [2, "except-parens"], - - // Allow for debugging - "no-console": 1, - - "no-constant-condition": 2, - "no-control-regex": 2, - - // Allow for debugging - "no-debugger": 1, - - "no-dupe-args": 2, - "no-dupe-keys": 2, - "no-duplicate-case": 2, - "no-empty": 2, - "no-empty-character-class": 2, - "no-ex-assign": 2, - "no-extra-boolean-cast": 2, - "no-extra-parens": 0, - "no-extra-semi": 2, - "no-func-assign": 0, - - // Stylistic... might consider disallowing in the future - "no-inner-declarations": 0, - - "no-invalid-regexp": 2, - "no-irregular-whitespace": 2, - "no-negated-in-lhs": 2, - "no-obj-calls": 2, - "no-regex-spaces": 2, - "quote-props": [2, "as-needed", {"keywords": true}], - "no-sparse-arrays": 0, - - // Optimizer and coverage will handle/highlight this and can be useful for debugging - "no-unreachable": 1, - - "use-isnan": 2, - "valid-jsdoc": 0, - "valid-typeof": 2, - - - // Best Practices // - //----------------// - "block-scoped-var": 0, - "complexity": 0, - "consistent-return": 0, - "curly": 2, - "default-case": 1, - "dot-notation": [2, {"allowKeywords": false}], - "eqeqeq": 0, - "guard-for-in": 1, - "no-alert": 2, - "no-caller": 2, - "no-div-regex": 1, - "no-else-return": 0, - "no-empty-label": 2, - "no-eq-null": 0, - "no-eval": 2, - "no-extend-native": 2, - "no-extra-bind": 2, - "no-fallthrough": 2, - "no-floating-decimal": 2, - "no-implied-eval": 2, - "no-iterator": 2, - "no-labels": 2, - "no-lone-blocks": 2, - "no-loop-func": 2, - "no-multi-spaces": 2, - "no-multi-str": 1, - "no-native-reassign": 2, - "no-new": 2, - "no-new-func": 2, - "no-new-wrappers": 2, - "no-octal": 2, - "no-octal-escape": 2, - "no-param-reassign": 0, - "no-process-env": 2, - "no-proto": 2, - "no-redeclare": 2, - "no-return-assign": 2, - "no-script-url": 2, - "no-self-compare": 2, - "no-sequences": 2, - "no-throw-literal": 2, - "no-unused-expressions": 2, - "no-void": 0, - "no-warning-comments": 1, - "no-with": 2, - "radix": 2, - "vars-on-top": 0, - "wrap-iife": 2, - "yoda": 0, - - - // Strict // - //--------// - "strict": 0, - - - // Variables // - //-----------// - "no-catch-shadow": 2, - "no-delete-var": 2, - "no-label-var": 2, - "no-shadow": 0, - "no-shadow-restricted-names": 2, - "no-undef": 2, - "no-undef-init": 2, - "no-undefined": 0, - "no-unused-vars": [2, {"vars": "all", "args": "after-used"}], - "no-use-before-define": [2, "nofunc"], - - - // Node.js // - //---------// - // Others left to environment defaults - "no-mixed-requires": 0, - - - // Stylistic // - //-----------// - "indent": 0, - "brace-style": [2, "1tbs", {"allowSingleLine": true}], - "camelcase": 2, - "comma-spacing": [2, {"before": false, "after": true}], - "comma-style": [2, "last"], - "consistent-this": [1, "self"], - "eol-last": 2, - "func-names": 0, - "func-style": [2, "declaration"], - "key-spacing": [2, { - "beforeColon": false, - "afterColon": true - }], - "max-nested-callbacks": 0, - "new-cap": 2, - "new-parens": 2, - "newline-after-var": 0, - "no-array-constructor": 2, - "no-continue": 0, - "no-inline-comments": 0, - "no-lonely-if": 2, - "no-mixed-spaces-and-tabs": 2, - "no-multiple-empty-lines": 0, - "no-nested-ternary": 1, - "no-new-object": 2, - "no-spaced-func": 2, - "no-ternary": 0, - "no-trailing-spaces": 2, - "no-underscore-dangle": 0, - "no-extra-parens": [2, "functions"], - "one-var": 0, - "operator-assignment": 0, - "padded-blocks": 0, - "quote-props": 0, - "quotes": [2, "single", "avoid-escape"], - "semi": 2, - "semi-spacing": [2, {"before": false, "after": true}], - "sort-vars": 0, - "space-after-keywords": [2, "always"], - "space-before-blocks": [2, "always"], - "space-before-function-paren": [2, {"anonymous": "never", "named": "never"}], - "space-in-brackets": 0, - "space-in-parens": [2, "never"], - "space-infix-ops": 2, - "space-return-throw-case": 2, - "space-unary-ops": 2, - "spaced-comment": [2, "always", {"markers": [","]}], - "wrap-regex": 1, - - "no-var": 1 - } -} \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..34daed78 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,129 @@ +module.exports = { + "extends": "eslint:recommended", + "globals": { + "self": false + }, + "env": { + "node": true + }, + "ecmaFeatures": { + // Enabling features that can be implemented without polyfills. Want to avoid polyfills at this time. + "arrowFunctions": true, + "blockBindings": true, + "defaultParams": true, + "destructuring": true, + "modules": true, + "objectLiteralComputedProperties": true, + "objectLiteralDuplicateProperties": true, + "objectLiteralShorthandMethods": true, + "objectLiteralShorthandProperties": true, + "restParams": true, + "spread": true, + "templateStrings": true + }, + "rules": { + // overrides eslint:recommended defaults + "no-sparse-arrays": "off", + "no-func-assign": "off", + "no-console": "warn", + "no-debugger": "warn", + "no-unreachable": "warn", + + // Possible Errors // + //-----------------// + "no-unsafe-negation": "error", + + + // Best Practices // + //----------------// + "curly": "error", + "default-case": "warn", + "dot-notation": ["error", { "allowKeywords": false }], + "guard-for-in": "warn", + "no-alert": "error", + "no-caller": "error", + "no-div-regex": "warn", + "no-eval": "error", + "no-extend-native": "error", + "no-extra-bind": "error", + "no-floating-decimal": "error", + "no-implied-eval": "error", + "no-iterator": "error", + "no-labels": "error", + "no-lone-blocks": "error", + "no-loop-func": "error", + "no-multi-spaces": "error", + "no-multi-str": "warn", + "no-global-assign": "error", + "no-new": "error", + "no-new-func": "error", + "no-new-wrappers": "error", + "no-octal-escape": "error", + "no-process-env": "error", + "no-proto": "error", + "no-return-assign": "error", + "no-script-url": "error", + "no-self-compare": "error", + "no-sequences": "error", + "no-throw-literal": "error", + "no-unused-expressions": "error", + "no-warning-comments": "warn", + "no-with": "error", + "radix": "error", + "wrap-iife": "error", + + + // Variables // + //-----------// + "no-catch-shadow": "error", + "no-label-var": "error", + "no-shadow-restricted-names": "error", + "no-undef-init": "error", + "no-use-before-define": ["error", "nofunc"], + + + // Stylistic Issues // + //------------------// + "comma-dangle": ["error", "never"], + "quote-props": ["error", "as-needed", { "keywords": true, "unnecessary": false }], + "brace-style": ["error", "1tbs", { "allowSingleLine": true }], + "camelcase": "error", + "comma-spacing": ["error", { "before": false, "after": true }], + "comma-style": ["error", "last"], + "consistent-this": ["warn", "self"], + "eol-last": "error", + "func-style": ["error", "declaration"], + "key-spacing": ["error", { + "beforeColon": false, + "afterColon": true + }], + "new-cap": "error", + "new-parens": "error", + "no-array-constructor": "error", + "no-lonely-if": "error", + "no-mixed-spaces-and-tabs": "error", + "no-nested-ternary": "warn", + "no-new-object": "error", + "no-spaced-func": "error", + "no-trailing-spaces": "error", + "no-extra-parens": ["error", "functions"], + "quotes": ["error", "single", "avoid-escape"], + "semi": "error", + "semi-spacing": ["error", { "before": false, "after": true }], + "keyword-spacing": "error", + "space-before-blocks": ["error", "always"], + "space-before-function-paren": ["error", { "anonymous": "never", "named": "never" }], + "space-in-parens": ["error", "never"], + "space-infix-ops": "error", + "space-unary-ops": "error", + "spaced-comment": ["error", "always", { "markers": [","] }], + "wrap-regex": "warn", + + // ECMAScript 6 // + //--------------// + "no-var": "warn" + }, + "parserOptions": { + "sourceType": "module" + } +} \ No newline at end of file diff --git a/bench/templates/complex.js b/bench/templates/complex.js index feba874d..3e5e26c4 100644 --- a/bench/templates/complex.js +++ b/bench/templates/complex.js @@ -5,7 +5,7 @@ module.exports = { header: function() { return 'Colors'; }, - hasItems: true, // To make things fairer in mustache land due to no `{{if}}` construct on arrays + 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'}, diff --git a/lib/handlebars/compiler/code-gen.js b/lib/handlebars/compiler/code-gen.js index 5ec052f7..43c0481b 100644 --- a/lib/handlebars/compiler/code-gen.js +++ b/lib/handlebars/compiler/code-gen.js @@ -118,7 +118,7 @@ CodeGen.prototype = { .replace(/"/g, '\\"') .replace(/\n/g, '\\n') .replace(/\r/g, '\\r') - .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4 + .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4 .replace(/\u2029/g, '\\u2029') + '"'; }, diff --git a/lib/handlebars/compiler/helpers.js b/lib/handlebars/compiler/helpers.js index e09a08df..432df877 100644 --- a/lib/handlebars/compiler/helpers.js +++ b/lib/handlebars/compiler/helpers.js @@ -38,7 +38,7 @@ export function stripFlags(open, close) { } export function stripComment(comment) { - return comment.replace(/^\{\{~?\!-?-?/, '') + return comment.replace(/^\{\{~?!-?-?/, '') .replace(/-?-?~?\}\}$/, ''); } @@ -47,8 +47,7 @@ export function preparePath(data, parts, loc) { let original = data ? '@' : '', dig = [], - depth = 0, - depthString = ''; + depth = 0; for (let i = 0, l = parts.length; i < l; i++) { let part = parts[i].part, @@ -62,7 +61,6 @@ export function preparePath(data, parts, loc) { throw new Exception('Invalid path: ' + original, {loc}); } else if (part === '..') { depth++; - depthString += '../'; } } else { dig.push(part); diff --git a/lib/handlebars/compiler/javascript-compiler.js b/lib/handlebars/compiler/javascript-compiler.js index bf4be8af..471144dd 100644 --- a/lib/handlebars/compiler/javascript-compiler.js +++ b/lib/handlebars/compiler/javascript-compiler.js @@ -133,7 +133,7 @@ JavaScriptCompiler.prototype = { }; if (this.decorators) { - ret.main_d = this.decorators; // eslint-disable-line camelcase + ret.main_d = this.decorators; // eslint-disable-line camelcase ret.useDecorators = true; } @@ -209,7 +209,7 @@ JavaScriptCompiler.prototype = { // aliases will not be used, but this case is already being run on the client and // we aren't concern about minimizing the template size. let aliasCount = 0; - for (let alias in this.aliases) { // eslint-disable-line guard-for-in + for (let alias in this.aliases) { // eslint-disable-line guard-for-in let node = this.aliases[alias]; if (this.aliases.hasOwnProperty(alias) && node.children && node.referenceCount > 1) { @@ -776,12 +776,12 @@ JavaScriptCompiler.prototype = { for (let i = 0, l = children.length; i < l; i++) { child = children[i]; - compiler = new this.compiler(); // eslint-disable-line new-cap + compiler = new this.compiler(); // eslint-disable-line new-cap let existing = this.matchExistingProgram(child); if (existing == null) { - this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children + this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children let index = this.context.programs.length; child.index = index; child.name = 'program' + index; diff --git a/lib/handlebars/logger.js b/lib/handlebars/logger.js index 1ab0051f..1e916a99 100644 --- a/lib/handlebars/logger.js +++ b/lib/handlebars/logger.js @@ -24,10 +24,10 @@ let logger = { if (typeof console !== 'undefined' && logger.lookupLevel(logger.level) <= level) { let method = logger.methodMap[level]; - if (!console[method]) { // eslint-disable-line no-console + if (!console[method]) { // eslint-disable-line no-console method = 'log'; } - console[method](...message); // eslint-disable-line no-console + console[method](...message); // eslint-disable-line no-console } } }; diff --git a/lib/precompiler.js b/lib/precompiler.js index 64608f1e..330f4bb1 100644 --- a/lib/precompiler.js +++ b/lib/precompiler.js @@ -60,7 +60,7 @@ function loadStrings(opts, callback) { 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 = [], diff --git a/package.json b/package.json index b458985e..f09eb8ae 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "grunt-contrib-requirejs": "0.x", "grunt-contrib-uglify": "0.x", "grunt-contrib-watch": "0.x", - "grunt-eslint": "^17.1.0", + "grunt-eslint": "^20.1.0", "grunt-saucelabs": "8.x", "grunt-webpack": "^1.0.8", "istanbul": "^0.3.0", diff --git a/spec/ast.js b/spec/ast.js index 8f346d88..e43438be 100644 --- a/spec/ast.js +++ b/spec/ast.js @@ -59,6 +59,7 @@ describe('ast', function() { equals(node.loc.end.column, lastColumn); } + /* eslint-disable no-multi-spaces */ ast = Handlebars.parse( 'line 1 {{line1Token}}\n' // 1 + ' line 2 {{line2token}}\n' // 2 @@ -71,6 +72,7 @@ describe('ast', function() { + '{{else inverse}}\n' // 9 + '{{else}}\n' // 10 + '{{/open}}'); // 11 + /* eslint-enable no-multi-spaces */ body = ast.body; it('gets ContentNode line numbers', function() { diff --git a/spec/helpers.js b/spec/helpers.js index 94e503f1..15bc2f12 100644 --- a/spec/helpers.js +++ b/spec/helpers.js @@ -113,7 +113,7 @@ describe('helpers', function() { { 'name': 'Yehuda', 'id': 2 } ]}; - shouldCompileTo(source, [data, {link: link}], ''); + shouldCompileTo(source, [data, {link: link}], ''); }); it('block helper for undefined value', function() { diff --git a/spec/parser.js b/spec/parser.js index 4527d19c..856031c3 100644 --- a/spec/parser.js +++ b/spec/parser.js @@ -51,7 +51,7 @@ describe('parser', function() { }); it('parses mustaches with string parameters', function() { - equals(astFor('{{foo bar \"baz\" }}'), '{{ PATH:foo [PATH:bar, "baz"] }}\n'); + equals(astFor('{{foo bar "baz" }}'), '{{ PATH:foo [PATH:bar, "baz"] }}\n'); }); it('parses mustaches with NUMBER parameters', function() { @@ -87,10 +87,10 @@ describe('parser', function() { 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() { @@ -136,7 +136,7 @@ describe('parser', function() { }); 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() { diff --git a/spec/spec.js b/spec/spec.js index 221d32ec..805609d0 100644 --- a/spec/spec.js +++ b/spec/spec.js @@ -25,8 +25,8 @@ describe('spec', function() { // 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); })) { + || (/\{\{=/).test(test.template) + || _.any(test.partials, function(partial) { return (/\{\{=/).test(partial); })) { it.skip(name + ' - ' + test.name); return; } diff --git a/spec/tokenizer.js b/spec/tokenizer.js index 428804e0..1a361b75 100644 --- a/spec/tokenizer.js +++ b/spec/tokenizer.js @@ -282,13 +282,13 @@ describe('Tokenizer', function() { }); it('tokenizes mustaches with String params as "OPEN ID ID STRING CLOSE"', function() { - var result = tokenize('{{ foo bar \"baz\" }}'); + var result = tokenize('{{ foo bar "baz" }}'); shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'STRING', 'CLOSE']); shouldBeToken(result[3], 'STRING', 'baz'); }); it('tokenizes mustaches with String params using single quotes as "OPEN ID ID STRING CLOSE"', function() { - var result = tokenize("{{ foo bar \'baz\' }}"); + var result = tokenize("{{ foo bar 'baz' }}"); shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'STRING', 'CLOSE']); shouldBeToken(result[3], 'STRING', 'baz'); }); @@ -365,13 +365,13 @@ describe('Tokenizer', function() { result = tokenize('{{ foo bar\n baz=bat }}'); shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'ID', 'CLOSE']); - result = tokenize('{{ foo bar baz=\"bat\" }}'); + result = tokenize('{{ foo bar baz="bat" }}'); shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'STRING', 'CLOSE']); - result = tokenize('{{ foo bar baz=\"bat\" bam=wot }}'); + result = tokenize('{{ foo bar baz="bat" bam=wot }}'); shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'STRING', 'ID', 'EQUALS', 'ID', 'CLOSE']); - result = tokenize('{{foo omg bar=baz bat=\"bam\"}}'); + result = tokenize('{{foo omg bar=baz bat="bam"}}'); shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'ID', 'ID', 'EQUALS', 'STRING', 'CLOSE']); shouldBeToken(result[2], 'ID', 'omg'); }); From 73d56375640e47ddd54ebc636b30dab9b68538b7 Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Sat, 21 Oct 2017 16:09:33 +0200 Subject: [PATCH 8/9] Update dependencies "async" to 2.5.0 and "source-map" to 0.6.1 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index f09eb8ae..589b87ba 100644 --- a/package.json +++ b/package.json @@ -21,9 +21,9 @@ "node": ">=0.4.7" }, "dependencies": { - "async": "^1.4.0", + "async": "^2.5.0", "optimist": "^0.6.1", - "source-map": "^0.4.4" + "source-map": "^0.6.1" }, "optionalDependencies": { "uglify-js": "^2.6" From d3d39423a3ad138b11d039a498ca8c135635e128 Mon Sep 17 00:00:00 2001 From: tim Date: Sat, 21 Oct 2017 18:44:31 +0200 Subject: [PATCH 9/9] upgrade uglify-js --- lib/precompiler.js | 7 ++++--- package.json | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/precompiler.js b/lib/precompiler.js index 330f4bb1..ab3eb201 100644 --- a/lib/precompiler.js +++ b/lib/precompiler.js @@ -290,8 +290,9 @@ function minify(output, sourceMapFile) { return output; } return require('uglify-js').minify(output.code, { - fromString: true, - outSourceMap: sourceMapFile, - inSourceMap: JSON.parse(output.map) + sourceMap: { + content: output.map, + url: sourceMapFile + } }); } diff --git a/package.json b/package.json index 589b87ba..b583ee34 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "source-map": "^0.6.1" }, "optionalDependencies": { - "uglify-js": "^2.6" + "uglify-js": "^3.1.4" }, "devDependencies": { "aws-sdk": "^2.1.49",