Compare commits
167 Commits
v4.0.9
...
cleanup-tests
| Author | SHA1 | Date | |
|---|---|---|---|
| 51892c296e | |||
| c5b699db00 | |||
| ee4dd7d4bf | |||
| 4801f649df | |||
| 9888569dcb | |||
| 1ade5a0892 | |||
| c819c8b533 | |||
| 827c9d0747 | |||
| f7f05d7558 | |||
| 1988878087 | |||
| 886ba86c2f | |||
| 0817dad7e7 | |||
| 93516a0b07 | |||
| 93e284ed9b | |||
| c02b05fa81 | |||
| 8de121d21c | |||
| 6914090086 | |||
| d54137810a | |||
| c2ac79c970 | |||
| 7ef86173ab | |||
| b75e3e1f40 | |||
| 5e9d17f8fa | |||
| b24797da01 | |||
| a243067883 | |||
| 088e61812a | |||
| 7052e88068 | |||
| b8913fcc65 | |||
| 62ed3c25c7 | |||
| 7fcf9d24f8 | |||
| c76ded8f0f | |||
| a15d01d383 | |||
| 8e1cce7918 | |||
| def79fd5d6 | |||
| fd2e5c983d | |||
| 8d5530ee2c | |||
| 2ab261eab7 | |||
| 143ac806da | |||
| f1752fe66f | |||
| aaab6099f2 | |||
| feb60f85c9 | |||
| 0b593bfe5d | |||
| 2e53fba68f | |||
| ba570c42cf | |||
| 0440af2147 | |||
| b793350fec | |||
| 26d0f7a80d | |||
| b7eada0149 | |||
| b8e769fcb6 | |||
| 9cb31653a4 | |||
| c53f340374 | |||
| 059b330579 | |||
| 8f6047cdfd | |||
| cf7545ef5a | |||
| c958cc8955 | |||
| b250b2d53a | |||
| ff4d827c09 | |||
| e4738491b3 | |||
| 54f7e11b28 | |||
| 8742bde701 | |||
| 2357140c68 | |||
| c5cbeac039 | |||
| 213c0bbe3c | |||
| 050cca0866 | |||
| 2d5579078b | |||
| e3639e2407 | |||
| c2a17c7b30 | |||
| 1266838829 | |||
| 64ecb9ea84 | |||
| 93444c58db | |||
| d148d7cb1f | |||
| a89081d440 | |||
| bba65605b1 | |||
| 06b7224ed9 | |||
| 2078c727c6 | |||
| fff3e40402 | |||
| 35bcd4405f | |||
| 00b4f2ff98 | |||
| e47b5ec192 | |||
| c55a7bed78 | |||
| 164c7ceea4 | |||
| 6ab48d8def | |||
| 8ac20285f6 | |||
| 888750ec27 | |||
| 133b96a2ff | |||
| f119497312 | |||
| 62b64ecc3d | |||
| a57b6824e0 | |||
| a26633f204 | |||
| f9cce4dd02 | |||
| 16572cd848 | |||
| f98c6a5425 | |||
| fde409ace6 | |||
| 9aaace76dc | |||
| 5eb59a5302 | |||
| 10b5fcf92e | |||
| dd0144c771 | |||
| 594f1e30dd | |||
| 871acccbb1 | |||
| cd38583216 | |||
| c454d946e3 | |||
| 9cfb5ddad9 | |||
| be44246221 | |||
| 002561be94 | |||
| 3fb6687013 | |||
| f691db546e | |||
| 25b2e11dd9 | |||
| e5c39375ac | |||
| aef72870b6 | |||
| 684f1032c6 | |||
| 7840ab66a5 | |||
| 4108b8330b | |||
| 445ae12fa4 | |||
| 5cedd62d7b | |||
| 40fb115e53 | |||
| b2e2cfe56d | |||
| 037bfbf4d9 | |||
| 048f2ce7d2 | |||
| b92589a3b0 | |||
| 1c62d4c5d0 | |||
| 7caca944b1 | |||
| 7bd34fb466 | |||
| b02e9a25ee | |||
| f1c8b2e2a2 | |||
| dbc50ac705 | |||
| c6a8fc1c04 | |||
| 42841c41a4 | |||
| 56fc6768d1 | |||
| ee3022228b | |||
| 05e6293bb3 | |||
| 2db0d123c8 | |||
| edc6220d51 | |||
| bacd473fe6 | |||
| 27ac1ee396 | |||
| 78dd89c13a | |||
| 6b87c21fc4 | |||
| 8d22e6f501 | |||
| 3c970cc9c1 | |||
| abba3c7526 | |||
| 4bf1c4ff66 | |||
| 41b6a11d11 | |||
| 2d28f920b0 | |||
| 29b174468d | |||
| d130ed2bc1 | |||
| 2145c14994 | |||
| 8359722e5d | |||
| a1d864d4a7 | |||
| 0ddff8b388 | |||
| 288e986161 | |||
| 30df8a1ac7 | |||
| cda544bca9 | |||
| 69c6ca528d | |||
| a4e39bdfd0 | |||
| b86b9189fa | |||
| d3d39423a3 | |||
| 73d5637564 | |||
| 7729aa956b | |||
| 8947dd077c | |||
| 1e954ddf3c | |||
| 1ac131e652 | |||
| 59548b4bdc | |||
| 21386b6474 | |||
| 79309659e1 | |||
| 5b76f041b3 | |||
| 1ed163f4ae | |||
| 670ec6fafb | |||
| 2e935df8bf | |||
| 0e953d1db5 |
@@ -0,0 +1,2 @@
|
|||||||
|
dist
|
||||||
|
node_modules
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+119
@@ -0,0 +1,119 @@
|
|||||||
|
module.exports = {
|
||||||
|
"extends": ["eslint:recommended","plugin:compat/recommended"],
|
||||||
|
"globals": {
|
||||||
|
"self": false,
|
||||||
|
"SOURCE_MAPS": true
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"node": true,
|
||||||
|
"es6": 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",
|
||||||
|
"no-prototype-builtins": "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",
|
||||||
|
"ecmaVersion": 6,
|
||||||
|
"ecmaFeatures": {}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -10,3 +10,6 @@ node_modules
|
|||||||
*.sublime-workspace
|
*.sublime-workspace
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
sauce_connect.log*
|
sauce_connect.log*
|
||||||
|
.idea
|
||||||
|
yarn-error.log
|
||||||
|
/handlebars-release.tgz
|
||||||
|
|||||||
-25
@@ -1,25 +0,0 @@
|
|||||||
.DS_Store
|
|
||||||
.gitignore
|
|
||||||
.rvmrc
|
|
||||||
.eslintrc
|
|
||||||
.travis.yml
|
|
||||||
.rspec
|
|
||||||
Gemfile
|
|
||||||
Gemfile.lock
|
|
||||||
Rakefile
|
|
||||||
Gruntfile.js
|
|
||||||
*.gemspec
|
|
||||||
*.nuspec
|
|
||||||
*.log
|
|
||||||
bench/*
|
|
||||||
configurations/*
|
|
||||||
components/*
|
|
||||||
coverage/*
|
|
||||||
dist/cdnjs/*
|
|
||||||
dist/components/*
|
|
||||||
spec/*
|
|
||||||
src/*
|
|
||||||
tasks/*
|
|
||||||
tmp/*
|
|
||||||
publish/*
|
|
||||||
vendor/*
|
|
||||||
+1
-2
@@ -13,12 +13,11 @@ env:
|
|||||||
- secure: Nm4AgSfsgNB21kgKrF9Tl7qVZU8YYREhouQunFracTcZZh2NZ2XH5aHuSiXCj88B13Cr/jGbJKsZ4T3QS3wWYtz6lkyVOx3H3iI+TMtqhD9RM3a7A4O+4vVN8IioB2YjhEu0OKjwgX5gp+0uF+pLEi7Hpj6fupD3AbbL5uYcKg8=
|
- secure: Nm4AgSfsgNB21kgKrF9Tl7qVZU8YYREhouQunFracTcZZh2NZ2XH5aHuSiXCj88B13Cr/jGbJKsZ4T3QS3wWYtz6lkyVOx3H3iI+TMtqhD9RM3a7A4O+4vVN8IioB2YjhEu0OKjwgX5gp+0uF+pLEi7Hpj6fupD3AbbL5uYcKg8=
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- node_js: '5'
|
- node_js: '10'
|
||||||
env:
|
env:
|
||||||
- PUBLISH=true
|
- PUBLISH=true
|
||||||
- secure: pLTzghtVll9yGKJI0AaB0uI8GypfWxLTaIB0ZL8//yN3nAEIKMhf/RRilYTsn/rKj2NUa7vt2edYILi3lttOUlCBOwTc9amiRms1W8Lwr/3IdWPeBLvLuH1zNJRm2lBAwU4LBSqaOwhGaxOQr6KHTnWudhNhgOucxpZfvfI/dFw=
|
- secure: pLTzghtVll9yGKJI0AaB0uI8GypfWxLTaIB0ZL8//yN3nAEIKMhf/RRilYTsn/rKj2NUa7vt2edYILi3lttOUlCBOwTc9amiRms1W8Lwr/3IdWPeBLvLuH1zNJRm2lBAwU4LBSqaOwhGaxOQr6KHTnWudhNhgOucxpZfvfI/dFw=
|
||||||
- secure: yERYCf7AwL11D9uMtacly/THGV8BlzsMmrt+iQVvGA3GaY6QMmfYqf6P6cCH98sH5etd1Y+1e6YrPeMjqI6lyRllT7FptoyOdHulazQe86VQN4sc0EpqMlH088kB7gGjTut9Z+X9ViooT5XEh9WA5jXEI9pXhQJNoIHkWPuwGuY=
|
- secure: yERYCf7AwL11D9uMtacly/THGV8BlzsMmrt+iQVvGA3GaY6QMmfYqf6P6cCH98sH5etd1Y+1e6YrPeMjqI6lyRllT7FptoyOdHulazQe86VQN4sc0EpqMlH088kB7gGjTut9Z+X9ViooT5XEh9WA5jXEI9pXhQJNoIHkWPuwGuY=
|
||||||
- node_js: '4'
|
|
||||||
cache:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
- node_modules
|
- node_modules
|
||||||
|
|||||||
+20
-3
@@ -10,6 +10,12 @@ Pull requests containing only failing tests demonstrating the issue are welcomed
|
|||||||
|
|
||||||
Documentation issues on the handlebarsjs.com site should be reported on [handlebars-site](https://github.com/wycats/handlebars-site).
|
Documentation issues on the handlebarsjs.com site should be reported on [handlebars-site](https://github.com/wycats/handlebars-site).
|
||||||
|
|
||||||
|
## Branches
|
||||||
|
|
||||||
|
* The branch `4.x` contains the currently released version. Bugfixes should be made in this branch.
|
||||||
|
* The branch `master` contains the next version. A release date is not yet specified. Maintainers
|
||||||
|
should merge the branch `4.x` into the master branch regularly.
|
||||||
|
|
||||||
## Pull Requests
|
## Pull Requests
|
||||||
|
|
||||||
We also accept [pull requests][pull-request]!
|
We also accept [pull requests][pull-request]!
|
||||||
@@ -75,13 +81,16 @@ npm link handlebars
|
|||||||
npm test
|
npm test
|
||||||
```
|
```
|
||||||
|
|
||||||
## Releasing
|
## Releasing the latest version
|
||||||
|
|
||||||
|
*When releasing a previous version of Handlebars, please look into the CONTRIBUNG.md in the corresponding branch.*
|
||||||
|
|
||||||
Handlebars utilizes the [release yeoman generator][generator-release] to perform most release tasks.
|
Handlebars utilizes the [release yeoman generator][generator-release] to perform most release tasks.
|
||||||
|
|
||||||
A full release may be completed with the following:
|
A full release may be completed with the following:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
npm ci
|
||||||
yo release
|
yo release
|
||||||
npm publish
|
npm publish
|
||||||
yo release:publish components handlebars.js dist/components/
|
yo release:publish components handlebars.js dist/components/
|
||||||
@@ -91,9 +100,17 @@ gem build handlebars-source.gemspec
|
|||||||
gem push handlebars-source-*.gem
|
gem push handlebars-source-*.gem
|
||||||
```
|
```
|
||||||
|
|
||||||
After this point the handlebars site needs to be updated to point to the new version numbers. The jsfiddle link should be updated to point to the most recent distribution for all instances in our documentation.
|
After the release, you should check that all places have really been updated. Especially verify that the `latest`-tags
|
||||||
|
in those places still point to the latest version
|
||||||
|
|
||||||
|
* [The npm-package](https://www.npmjs.com/package/handlebars) (check latest-tag)
|
||||||
|
* [The bower package](https://github.com/components/handlebars.js) (check the package.json)
|
||||||
|
* [The AWS S3 Bucket](https://s3.amazonaws.com/builds.handlebarsjs.com) (check latest-tag)
|
||||||
|
* [RubyGems](https://rubygems.org/gems/handlebars-source)
|
||||||
|
|
||||||
|
When everything is OK, the handlebars site needs to be updated to point to the new version numbers. The jsfiddle link should be updated to point to the most recent distribution for all instances in our documentation.
|
||||||
|
|
||||||
[generator-release]: https://github.com/walmartlabs/generator-release
|
[generator-release]: https://github.com/walmartlabs/generator-release
|
||||||
[pull-request]: https://github.com/wycats/handlebars.js/pull/new/master
|
[pull-request]: https://github.com/wycats/handlebars.js/pull/new/master
|
||||||
[issue]: https://github.com/wycats/handlebars.js/issues/new
|
[issue]: https://github.com/wycats/handlebars.js/issues/new
|
||||||
[jsfiddle]: https://jsfiddle.net/9D88g/47/
|
[jsfiddle]: https://jsfiddle.net/9D88g/180/
|
||||||
|
|||||||
+37
-82
@@ -5,18 +5,19 @@ module.exports = function(grunt) {
|
|||||||
pkg: grunt.file.readJSON('package.json'),
|
pkg: grunt.file.readJSON('package.json'),
|
||||||
|
|
||||||
eslint: {
|
eslint: {
|
||||||
options: {
|
|
||||||
},
|
|
||||||
files: [
|
files: [
|
||||||
'*.js',
|
'*.js',
|
||||||
'bench/**/*.js',
|
'bench/**/*.js',
|
||||||
'tasks/**/*.js',
|
'tasks/**/*.js',
|
||||||
'lib/**/!(*.min|parser).js',
|
'lib/**/!(*.min|parser).js',
|
||||||
'spec/**/!(*.amd|json2|require).js'
|
'spec/**/!(*.amd|json2|require).js',
|
||||||
|
'integration-testing/multi-nodejs-test/*.js',
|
||||||
|
'integration-testing/webpack-test/*.js',
|
||||||
|
'integration-testing/webpack-test/src/*.js'
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
clean: ['tmp', 'dist', 'lib/handlebars/compiler/parser.js'],
|
clean: ['tmp', 'dist', 'lib/handlebars/compiler/parser.js', 'integration-testing/**/node_modules'],
|
||||||
|
|
||||||
copy: {
|
copy: {
|
||||||
dist: {
|
dist: {
|
||||||
@@ -42,16 +43,17 @@ module.exports = function(grunt) {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
webpack: {
|
||||||
|
build: require('./webpack.config')
|
||||||
|
},
|
||||||
babel: {
|
babel: {
|
||||||
options: {
|
options: {
|
||||||
sourceMaps: 'inline',
|
sourceMaps: 'inline',
|
||||||
loose: ['es6.modules'],
|
|
||||||
auxiliaryCommentBefore: 'istanbul ignore next'
|
auxiliaryCommentBefore: 'istanbul ignore next'
|
||||||
},
|
},
|
||||||
amd: {
|
amd: {
|
||||||
options: {
|
options: {
|
||||||
modules: 'amd'
|
plugins: ['@babel/plugin-transform-modules-amd']
|
||||||
},
|
},
|
||||||
files: [{
|
files: [{
|
||||||
expand: true,
|
expand: true,
|
||||||
@@ -63,7 +65,11 @@ module.exports = function(grunt) {
|
|||||||
|
|
||||||
cjs: {
|
cjs: {
|
||||||
options: {
|
options: {
|
||||||
modules: 'common'
|
plugins: [
|
||||||
|
'@babel/plugin-transform-modules-commonjs',
|
||||||
|
// support "import * as Handlebars" for backward-compatibility
|
||||||
|
'babel-plugin-add-module-exports'
|
||||||
|
]
|
||||||
},
|
},
|
||||||
files: [{
|
files: [{
|
||||||
cwd: 'lib/',
|
cwd: 'lib/',
|
||||||
@@ -73,72 +79,6 @@ module.exports = function(grunt) {
|
|||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
webpack: {
|
|
||||||
options: {
|
|
||||||
context: __dirname,
|
|
||||||
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' }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
path: 'dist/',
|
|
||||||
library: 'Handlebars',
|
|
||||||
libraryTarget: 'umd'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handlebars: {
|
|
||||||
entry: './lib/handlebars.js',
|
|
||||||
output: {
|
|
||||||
filename: 'handlebars.js'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
runtime: {
|
|
||||||
entry: './lib/handlebars.runtime.js',
|
|
||||||
output: {
|
|
||||||
filename: 'handlebars.runtime.js'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
requirejs: {
|
|
||||||
options: {
|
|
||||||
optimize: 'none',
|
|
||||||
baseUrl: 'dist/amd/'
|
|
||||||
},
|
|
||||||
dist: {
|
|
||||||
options: {
|
|
||||||
name: 'handlebars',
|
|
||||||
out: 'dist/handlebars.amd.js'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
runtime: {
|
|
||||||
options: {
|
|
||||||
name: 'handlebars.runtime',
|
|
||||||
out: 'dist/handlebars.runtime.amd.js'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
uglify: {
|
|
||||||
options: {
|
|
||||||
mangle: true,
|
|
||||||
compress: true,
|
|
||||||
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');
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
concat: {
|
concat: {
|
||||||
tests: {
|
tests: {
|
||||||
@@ -166,8 +106,8 @@ module.exports = function(grunt) {
|
|||||||
browsers: [
|
browsers: [
|
||||||
{browserName: 'chrome'},
|
{browserName: 'chrome'},
|
||||||
{browserName: 'firefox', platform: 'Linux'},
|
{browserName: 'firefox', platform: 'Linux'},
|
||||||
{browserName: 'safari', version: 9, platform: 'OS X 10.11'},
|
// {browserName: 'safari', version: 9, platform: 'OS X 10.11'},
|
||||||
{browserName: 'safari', version: 8, platform: 'OS X 10.10'},
|
// {browserName: 'safari', version: 8, platform: 'OS X 10.10'},
|
||||||
{browserName: 'internet explorer', version: 11, platform: 'Windows 8.1'},
|
{browserName: 'internet explorer', version: 11, platform: 'Windows 8.1'},
|
||||||
{browserName: 'internet explorer', version: 10, platform: 'Windows 8'}
|
{browserName: 'internet explorer', version: 10, platform: 'Windows 8'}
|
||||||
]
|
]
|
||||||
@@ -186,6 +126,20 @@ module.exports = function(grunt) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
bgShell: {
|
||||||
|
checkTypes: {
|
||||||
|
cmd: 'npm run checkTypes',
|
||||||
|
bg: false,
|
||||||
|
fail: true
|
||||||
|
},
|
||||||
|
integrationTests: {
|
||||||
|
cmd: './integration-testing/run-integration-tests.sh',
|
||||||
|
bg: false,
|
||||||
|
fail: true
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
scripts: {
|
scripts: {
|
||||||
options: {
|
options: {
|
||||||
@@ -201,28 +155,28 @@ module.exports = function(grunt) {
|
|||||||
// Build a new version of the library
|
// Build a new version of the library
|
||||||
this.registerTask('build', 'Builds a distributable version of the current project', [
|
this.registerTask('build', 'Builds a distributable version of the current project', [
|
||||||
'eslint',
|
'eslint',
|
||||||
|
'bgShell:checkTypes',
|
||||||
'parser',
|
'parser',
|
||||||
'node',
|
'node',
|
||||||
'globals']);
|
'globals']);
|
||||||
|
|
||||||
this.registerTask('amd', ['babel:amd', 'requirejs']);
|
this.registerTask('amd', ['babel:amd']);
|
||||||
this.registerTask('node', ['babel:cjs']);
|
this.registerTask('node', ['babel:cjs']);
|
||||||
this.registerTask('globals', ['webpack']);
|
this.registerTask('globals', ['webpack']);
|
||||||
this.registerTask('tests', ['concat:tests']);
|
this.registerTask('tests', ['concat:tests']);
|
||||||
|
|
||||||
this.registerTask('release', 'Build final packages', ['eslint', 'amd', 'uglify', 'test:min', 'copy:dist', 'copy:components', 'copy:cdnjs']);
|
this.registerTask('release', 'Build final packages', ['eslint', 'amd', 'test:min', 'copy:dist', 'copy:components', 'copy:cdnjs']);
|
||||||
|
|
||||||
// Load tasks from npm
|
// Load tasks from npm
|
||||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||||
grunt.loadNpmTasks('grunt-contrib-concat');
|
grunt.loadNpmTasks('grunt-contrib-concat');
|
||||||
grunt.loadNpmTasks('grunt-contrib-connect');
|
grunt.loadNpmTasks('grunt-contrib-connect');
|
||||||
grunt.loadNpmTasks('grunt-contrib-copy');
|
grunt.loadNpmTasks('grunt-contrib-copy');
|
||||||
grunt.loadNpmTasks('grunt-contrib-requirejs');
|
|
||||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
|
||||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||||
grunt.loadNpmTasks('grunt-babel');
|
grunt.loadNpmTasks('grunt-babel');
|
||||||
|
grunt.loadNpmTasks('grunt-bg-shell');
|
||||||
grunt.loadNpmTasks('grunt-eslint');
|
grunt.loadNpmTasks('grunt-eslint');
|
||||||
grunt.loadNpmTasks('grunt-saucelabs');
|
grunt.loadNpmTasks('@knappi/grunt-saucelabs');
|
||||||
grunt.loadNpmTasks('grunt-webpack');
|
grunt.loadNpmTasks('grunt-webpack');
|
||||||
|
|
||||||
grunt.task.loadTasks('tasks');
|
grunt.task.loadTasks('tasks');
|
||||||
@@ -230,8 +184,9 @@ module.exports = function(grunt) {
|
|||||||
grunt.registerTask('bench', ['metrics']);
|
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'] : []);
|
||||||
|
|
||||||
grunt.registerTask('travis', process.env.PUBLISH ? ['default', 'sauce', 'metrics', 'publish:latest'] : ['default']);
|
grunt.registerTask('travis', process.env.PUBLISH ? ['default', 'bgShell:integrationTests', 'sauce', 'metrics', 'publish:latest'] : ['default']);
|
||||||
|
|
||||||
grunt.registerTask('dev', ['clean', 'connect', 'watch']);
|
grunt.registerTask('dev', ['clean', 'connect', 'watch']);
|
||||||
grunt.registerTask('default', ['clean', 'build', 'test', 'release']);
|
grunt.registerTask('default', ['clean', 'build', 'test', 'release']);
|
||||||
|
grunt.registerTask('integration-tests', ['default', 'bgShell:integrationTests']);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
[](https://travis-ci.org/wycats/handlebars.js)
|
[](https://travis-ci.org/wycats/handlebars.js)
|
||||||
|
[](https://ci.appveyor.com/project/wycats/handlebars-js)
|
||||||
[](https://saucelabs.com/u/handlebars)
|
[](https://saucelabs.com/u/handlebars)
|
||||||
|
|
||||||
Handlebars.js
|
Handlebars.js
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# Test against these versions of Node.js
|
||||||
|
environment:
|
||||||
|
matrix:
|
||||||
|
- nodejs_version: "10"
|
||||||
|
|
||||||
|
platform:
|
||||||
|
- x64
|
||||||
|
|
||||||
|
# Install scripts (runs after repo cloning)
|
||||||
|
install:
|
||||||
|
# Get the latest stable version of Node.js
|
||||||
|
- ps: Install-Product node $env:nodejs_version $env:platform
|
||||||
|
# Clone submodules (mustache spec)
|
||||||
|
- cmd: git submodule update --init --recursive
|
||||||
|
# Install modules
|
||||||
|
- cmd: npm install
|
||||||
|
- cmd: npm install -g grunt-cli
|
||||||
|
|
||||||
|
|
||||||
|
# Post-install test scripts
|
||||||
|
test_script:
|
||||||
|
# Output useful info for debugging
|
||||||
|
- cmd: node --version
|
||||||
|
- cmd: npm --version
|
||||||
|
# Run tests
|
||||||
|
- cmd: grunt --stack travis
|
||||||
|
|
||||||
|
# Don't actually build
|
||||||
|
build: off
|
||||||
|
|
||||||
|
on_failure:
|
||||||
|
- cmd: 7z a coverage.zip coverage
|
||||||
|
- cmd: appveyor PushArtifact coverage.zip
|
||||||
|
|
||||||
|
|
||||||
|
# Set build version format here instead of in the admin panel
|
||||||
|
version: "{build}"
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
module.exports = function(api) {
|
||||||
|
api.cache(false);
|
||||||
|
return {
|
||||||
|
presets: ['@babel/preset-env'],
|
||||||
|
plugins: ['@babel/plugin-transform-runtime']
|
||||||
|
};
|
||||||
|
};
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
var async = require('async'),
|
var async = require('neo-async'),
|
||||||
fs = require('fs'),
|
fs = require('fs'),
|
||||||
zlib = require('zlib');
|
zlib = require('zlib');
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ module.exports = {
|
|||||||
header: function() {
|
header: function() {
|
||||||
return 'Colors';
|
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: [
|
items: [
|
||||||
{name: 'red', current: true, url: '#Red'},
|
{name: 'red', current: true, url: '#Red'},
|
||||||
{name: 'green', current: false, url: '#Green'},
|
{name: 'green', current: false, url: '#Green'},
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ BenchWarmer.prototype = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
push: function(name, fn) {
|
push: function(name, fn) {
|
||||||
if (this.names.indexOf(name) == -1) {
|
if (this.names.indexOf(name) === -1) {
|
||||||
this.names.push(name);
|
this.names.push(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ BenchWarmer.prototype = {
|
|||||||
|
|
||||||
var errors = false, prop, bench;
|
var errors = false, prop, bench;
|
||||||
for (prop in self.errors) {
|
for (prop in self.errors) {
|
||||||
if (self.errors.hasOwnProperty(prop)
|
if (Object.prototype.hasOwnProperty.call(self, prop)
|
||||||
&& self.errors[prop].error.message !== 'EWOT') {
|
&& self.errors[prop].error.message !== 'EWOT') {
|
||||||
errors = true;
|
errors = true;
|
||||||
break;
|
break;
|
||||||
@@ -86,9 +86,8 @@ BenchWarmer.prototype = {
|
|||||||
|
|
||||||
if (errors) {
|
if (errors) {
|
||||||
print('\n\nErrors:\n');
|
print('\n\nErrors:\n');
|
||||||
for (prop in self.errors) {
|
Object.keys(self.errors).forEach(function(prop) {
|
||||||
if (self.errors.hasOwnProperty(prop)
|
if (self.errors[prop].error.message !== 'EWOT') {
|
||||||
&& self.errors[prop].error.message !== 'EWOT') {
|
|
||||||
bench = self.errors[prop];
|
bench = self.errors[prop];
|
||||||
print('\n' + bench.name + ':\n');
|
print('\n' + bench.name + ':\n');
|
||||||
print(bench.error.message);
|
print(bench.error.message);
|
||||||
@@ -97,7 +96,7 @@ BenchWarmer.prototype = {
|
|||||||
}
|
}
|
||||||
print('\n');
|
print('\n');
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "handlebars",
|
"name": "handlebars",
|
||||||
"version": "4.0.9",
|
"version": "4.5.3",
|
||||||
"main": "handlebars.js",
|
"main": "handlebars.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {}
|
"dependencies": {}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<package>
|
<package>
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>handlebars.js</id>
|
<id>handlebars.js</id>
|
||||||
<version>4.0.9</version>
|
<version>4.5.3</version>
|
||||||
<authors>handlebars.js Authors</authors>
|
<authors>handlebars.js Authors</authors>
|
||||||
<licenseUrl>https://github.com/wycats/handlebars.js/blob/master/LICENSE</licenseUrl>
|
<licenseUrl>https://github.com/wycats/handlebars.js/blob/master/LICENSE</licenseUrl>
|
||||||
<projectUrl>https://github.com/wycats/handlebars.js/</projectUrl>
|
<projectUrl>https://github.com/wycats/handlebars.js/</projectUrl>
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "handlebars",
|
||||||
|
"version": "4.5.3",
|
||||||
|
"license": "MIT",
|
||||||
|
"jspm": {
|
||||||
|
"main": "handlebars",
|
||||||
|
"shim": {
|
||||||
|
"handlebars": {
|
||||||
|
"exports": "Handlebars"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"handlebars.js",
|
||||||
|
"handlebars.runtime.js"
|
||||||
|
],
|
||||||
|
"buildConfig": {
|
||||||
|
"minify": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+57
-7
@@ -16,6 +16,34 @@ var ast = Handlebars.parse(myTemplate);
|
|||||||
Handlebars.precompile(ast);
|
Handlebars.precompile(ast);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Parsing
|
||||||
|
|
||||||
|
There are two primary APIs that are used to parse an existing template into the AST:
|
||||||
|
|
||||||
|
#### parseWithoutProcessing
|
||||||
|
|
||||||
|
`Handlebars.parseWithoutProcessing` is the primary mechanism to turn a raw template string into the Handlebars AST described in this document. No processing is done on the resulting AST which makes this ideal for codemod (for source to source transformation) tooling.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```js
|
||||||
|
let ast = Handlebars.parseWithoutProcessing(myTemplate);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### parse
|
||||||
|
|
||||||
|
`Handlebars.parse` will parse the template with `parseWithoutProcessing` (see above) then it will update the AST to strip extraneous whitespace. The whitespace stripping functionality handles two distinct situations:
|
||||||
|
|
||||||
|
* Removes whitespace around dynamic statements that are on a line by themselves (aka "stand alone")
|
||||||
|
* Applies "whitespace control" characters (i.e. `~`) by truncating the `ContentStatement` `value` property appropriately (e.g. `\n\n{{~foo}}` would have a `ContentStatement` with a `value` of `''`)
|
||||||
|
|
||||||
|
`Handlebars.parse` is used internally by `Handlebars.precompile` and `Handlebars.compile`.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```js
|
||||||
|
let ast = Handlebars.parse(myTemplate);
|
||||||
|
```
|
||||||
|
|
||||||
### Basic
|
### Basic
|
||||||
|
|
||||||
@@ -66,7 +94,7 @@ interface MustacheStatement <: Statement {
|
|||||||
|
|
||||||
interface BlockStatement <: Statement {
|
interface BlockStatement <: Statement {
|
||||||
type: "BlockStatement";
|
type: "BlockStatement";
|
||||||
path: PathExpression;
|
path: PathExpression | Literal;
|
||||||
params: [ Expression ];
|
params: [ Expression ];
|
||||||
hash: Hash;
|
hash: Hash;
|
||||||
|
|
||||||
@@ -296,21 +324,43 @@ The `Handlebars.JavaScriptCompiler` object has a number of methods that may be c
|
|||||||
- `initializeBuffer()`
|
- `initializeBuffer()`
|
||||||
Allows for buffers other than the default string buffer to be used. Generally needs to be paired with a custom `appendToBuffer` implementation.
|
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
|
```javascript
|
||||||
function MyCompiler() {
|
function MyCompiler() {
|
||||||
Handlebars.JavaScriptCompiler.apply(this, arguments);
|
Handlebars.JavaScriptCompiler.apply(this, arguments);
|
||||||
}
|
}
|
||||||
MyCompiler.prototype = Object.create(Handlebars.JavaScriptCompiler);
|
MyCompiler.prototype = new Handlebars.JavaScriptCompiler();
|
||||||
|
|
||||||
MyCompiler.nameLookup = function(parent, name, type) {
|
// Use this compile to compile BlockStatment-Blocks
|
||||||
if (type === 'partial') {
|
MyCompiler.prototype.compiler = MyCompiler
|
||||||
return 'MyPartialList[' + JSON.stringify(name) ']';
|
|
||||||
|
MyCompiler.prototype.nameLookup = function(parent, name, type) {
|
||||||
|
if (type === 'context') {
|
||||||
|
return this.source.functionCall('helpers.lookupLowerCase', '', [parent, JSON.stringify(name)])
|
||||||
} else {
|
} else {
|
||||||
return Handlebars.JavaScriptCompiler.prototype.nameLookup.call(this, parent, name, type);
|
return Handlebars.JavaScriptCompiler.prototype.nameLookup.call(this, parent, name, type);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
var env = Handlebars.create();
|
var env = Handlebars.create();
|
||||||
|
env.registerHelper('lookupLowerCase', function(parent, name) {
|
||||||
|
return parent[name.toLowerCase()]
|
||||||
|
})
|
||||||
|
|
||||||
env.JavaScriptCompiler = MyCompiler;
|
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'}
|
||||||
|
]
|
||||||
|
}));
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
Add a new integration test by creating a new subfolder
|
||||||
|
|
||||||
|
Add a file "test.sh" to that runs the test. "test.sh" should exit with a non-zero exit code
|
||||||
|
and display an error message, if something goes wrong.
|
||||||
|
|
||||||
|
* An integration test should reflect real-world setups that use handlebars.
|
||||||
|
* It should compile a minimal template and compare the output to an expected output.
|
||||||
|
* It should use "../.." as dependency for Handlebars so that the currently built library is used.
|
||||||
|
|
||||||
|
Currently, integration tests are only running on Linux, especially in travis-ci.
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
module.exports = {
|
||||||
|
"extends": "eslint:recommended",
|
||||||
|
"globals": {
|
||||||
|
"self": false
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
'no-console': 'off'
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
target
|
||||||
|
package-lock.json
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "multi-nodejs-test",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Simple integration test with all relevant NodeJS-versions.",
|
||||||
|
"keywords": [],
|
||||||
|
"author": "Nils Knappmeier",
|
||||||
|
"private": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"handlebars": "file:../.."
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "node run-handlebars.js",
|
||||||
|
"test-precompile": "handlebars precompile-test-template.txt.hbs"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Author: {{author}}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
// This test should run successfully with node 0.10++ as long as Handlebars has been compiled before
|
||||||
|
var assert = require('assert');
|
||||||
|
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'});
|
||||||
|
assert.strictEqual(output, 'Author: Yehuda');
|
||||||
|
|
||||||
|
console.log('Success');
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
cd "$( dirname "$( readlink -f "$0" )" )" || exit 1
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
||||||
|
|
||||||
|
# This script tests with precompiler and the built distribution with multiple NodeJS version.
|
||||||
|
# The rest of the travis-build will only work with newer NodeJS versions, because the build
|
||||||
|
# tools don't support older versions.
|
||||||
|
# However, the built distribution should work with older NodeJS versions as well.
|
||||||
|
# This test is simple by design. It merely ensures, that calling Handlebars does not fail with old versions.
|
||||||
|
# It does (almost) not test for correctness, because that is already done in the mocha-tests.
|
||||||
|
# And it does not use any NodeJS based testing framwork to make this part independent of the Node version.
|
||||||
|
|
||||||
|
# A list of NodeJS versions is expected as cli-args
|
||||||
|
echo "Handlebars should be able to run in various versions of NodeJS"
|
||||||
|
for i in 0.10 0.12 4 5 6 7 8 9 10 11 ; do
|
||||||
|
rm target node_modules package-lock.json -rf
|
||||||
|
mkdir target
|
||||||
|
nvm install "$i"
|
||||||
|
nvm exec "$i" npm install
|
||||||
|
nvm exec "$i" npm run test || exit 1
|
||||||
|
nvm exec "$i" npm run test-precompile || exit 1
|
||||||
|
|
||||||
|
echo Success
|
||||||
|
done
|
||||||
Executable
+13
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
cd "$( dirname "$( readlink -f "$0" )" )" || exit 1
|
||||||
|
|
||||||
|
for i in */test.sh ; do
|
||||||
|
(
|
||||||
|
echo "----------------------------------------"
|
||||||
|
echo "-- Running integration test: $i"
|
||||||
|
echo "----------------------------------------"
|
||||||
|
cd "$( dirname "$i" )" || exit 1
|
||||||
|
./test.sh || exit 1
|
||||||
|
)
|
||||||
|
done
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
"@roundingwellos/babel-plugin-handlebars-inline-precompile"
|
||||||
|
// "istanbul"
|
||||||
|
],
|
||||||
|
"presets": [
|
||||||
|
[
|
||||||
|
"@babel/preset-env"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
package-lock.json
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"name": "webpack-babel-test",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/core": "^7.5.5",
|
||||||
|
"@babel/preset-env": "^7.5.5",
|
||||||
|
"@roundingwellos/babel-plugin-handlebars-inline-precompile": "^3.0.1",
|
||||||
|
"babel-loader": "^8.0.6",
|
||||||
|
"babel-plugin-istanbul": "^5.2.0",
|
||||||
|
"handlebars": "file:../..",
|
||||||
|
"handlebars-loader": "^1.7.1",
|
||||||
|
"nyc": "^14.1.1",
|
||||||
|
"webpack": "^4.39.3",
|
||||||
|
"webpack-cli": "^3.3.7"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "webpack --config webpack.config.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import * as Handlebars from 'handlebars/runtime';
|
||||||
|
import hbs from 'handlebars-inline-precompile';
|
||||||
|
import {assertEquals} from '../../webpack-test/src/lib/assert';
|
||||||
|
|
||||||
|
Handlebars.registerHelper('loud', function(text) {
|
||||||
|
return text.toUpperCase();
|
||||||
|
});
|
||||||
|
|
||||||
|
const template = hbs`{{loud name}}`;
|
||||||
|
const output = template({name: 'yehuda'});
|
||||||
|
|
||||||
|
assertEquals(output, 'YEHUDA');
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export function assertEquals(actual, expected) {
|
||||||
|
if (actual !== expected) {
|
||||||
|
throw new Error(`Expected "${actual}" to equal "${expected}"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Cleanup: package-lock and "npm ci" is not working with local dependencies
|
||||||
|
rm dist package-lock.json -rf
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
for i in dist/*-test.js ; do
|
||||||
|
echo "----------------------"
|
||||||
|
echo "-- Running $i"
|
||||||
|
echo "----------------------"
|
||||||
|
node "$i"
|
||||||
|
echo "Success"
|
||||||
|
done
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const testFiles = fs.readdirSync('src');
|
||||||
|
const entryPoints = {};
|
||||||
|
testFiles
|
||||||
|
.filter(file => file.match(/-test.js$/))
|
||||||
|
.forEach(file => {
|
||||||
|
entryPoints[file] = `./src/${file}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: entryPoints,
|
||||||
|
mode: 'production',
|
||||||
|
output: {
|
||||||
|
filename: '[name]',
|
||||||
|
path: __dirname + '/dist'
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.js?$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: {
|
||||||
|
loader: 'babel-loader',
|
||||||
|
options: { cacheDirectory: false },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
minimize: false
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
package-lock.json
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "webpack-test",
|
||||||
|
"description": "Various tests with Handlebars and Webpack",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"build": "webpack --config webpack.config.js",
|
||||||
|
"test": "node dist/main.js"
|
||||||
|
},
|
||||||
|
"private": true,
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {
|
||||||
|
"handlebars": "file:../..",
|
||||||
|
"handlebars-loader": "^1.7.1",
|
||||||
|
"webpack": "^4.39.3",
|
||||||
|
"webpack-cli": "^3.3.7"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import Handlebars from 'handlebars/dist/handlebars';
|
||||||
|
|
||||||
|
import {assertEquals} from './lib/assert';
|
||||||
|
|
||||||
|
const template = Handlebars.compile('Author: {{author}}');
|
||||||
|
assertEquals(template({author: 'Yehuda'}), 'Author: Yehuda');
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import Handlebars from 'handlebars';
|
||||||
|
import {assertEquals} from './lib/assert';
|
||||||
|
|
||||||
|
|
||||||
|
const template = Handlebars.compile('Author: {{author}}');
|
||||||
|
assertEquals(template({author: 'Yehuda'}), 'Author: Yehuda');
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import {assertEquals} from './lib/assert';
|
||||||
|
|
||||||
|
import testTemplate from './test-template.handlebars';
|
||||||
|
assertEquals(testTemplate({author: 'Yehuda'}).trim(), 'Author: Yehuda');
|
||||||
|
|
||||||
|
|
||||||
|
const testTemplateRequire = require('./test-template.handlebars');
|
||||||
|
assertEquals(testTemplateRequire({author: 'Yehuda'}).trim(), 'Author: Yehuda');
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import * as HandlebarsViaImport from 'handlebars';
|
||||||
|
const HandlebarsViaRequire = require('handlebars');
|
||||||
|
import {assertEquals} from './lib/assert';
|
||||||
|
|
||||||
|
HandlebarsViaImport.registerHelper('loud', function(text) {
|
||||||
|
return text.toUpperCase();
|
||||||
|
});
|
||||||
|
|
||||||
|
const template = HandlebarsViaRequire.compile('Author: {{loud author}}');
|
||||||
|
assertEquals(template({author: 'Yehuda'}), 'Author: YEHUDA');
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import * as Handlebars from 'handlebars/dist/handlebars';
|
||||||
|
|
||||||
|
import {assertEquals} from './lib/assert';
|
||||||
|
|
||||||
|
const template = Handlebars.compile('Author: {{author}}');
|
||||||
|
assertEquals(template({author: 'Yehuda'}), 'Author: Yehuda');
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import * as Handlebars from 'handlebars';
|
||||||
|
import {assertEquals} from './lib/assert';
|
||||||
|
|
||||||
|
const template = Handlebars.compile('Author: {{author}}');
|
||||||
|
assertEquals(template({author: 'Yehuda'}), 'Author: Yehuda');
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export function assertEquals(actual, expected) {
|
||||||
|
if (actual !== expected) {
|
||||||
|
throw new Error(`Expected "${actual}" to equal "${expected}"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
Author: {{author}}
|
||||||
|
|
||||||
Executable
+16
@@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Cleanup: package-lock and "npm ci" is not working with local dependencies
|
||||||
|
rm dist package-lock.json -rf
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
for i in dist/*-test.js ; do
|
||||||
|
echo "----------------------"
|
||||||
|
echo "-- Running $i"
|
||||||
|
echo "----------------------"
|
||||||
|
node "$i"
|
||||||
|
echo "Success"
|
||||||
|
done
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const testFiles = fs.readdirSync('src');
|
||||||
|
const entryPoints = {};
|
||||||
|
testFiles
|
||||||
|
.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'}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
+4
-6
@@ -2,17 +2,16 @@ import runtime from './handlebars.runtime';
|
|||||||
|
|
||||||
// Compiler imports
|
// Compiler imports
|
||||||
import AST from './handlebars/compiler/ast';
|
import AST from './handlebars/compiler/ast';
|
||||||
import { parser as Parser, parse } from './handlebars/compiler/base';
|
import { parser as Parser, parse, parseWithoutProcessing } from './handlebars/compiler/base';
|
||||||
import { Compiler, compile, precompile } from './handlebars/compiler/compiler';
|
import { Compiler, compile, precompile } from './handlebars/compiler/compiler';
|
||||||
import JavaScriptCompiler from './handlebars/compiler/javascript-compiler';
|
import JavaScriptCompiler from './handlebars/compiler/javascript-compiler';
|
||||||
import Visitor from './handlebars/compiler/visitor';
|
import Visitor from './handlebars/compiler/visitor';
|
||||||
|
|
||||||
import noConflict from './handlebars/no-conflict';
|
import noConflict from './handlebars/no-conflict';
|
||||||
|
|
||||||
let _create = runtime.create;
|
|
||||||
function create() {
|
function create() {
|
||||||
let hb = _create();
|
let hb = runtime.create();
|
||||||
|
hb.create = create;
|
||||||
hb.compile = function(input, options) {
|
hb.compile = function(input, options) {
|
||||||
return compile(input, options, hb);
|
return compile(input, options, hb);
|
||||||
};
|
};
|
||||||
@@ -25,13 +24,12 @@ function create() {
|
|||||||
hb.JavaScriptCompiler = JavaScriptCompiler;
|
hb.JavaScriptCompiler = JavaScriptCompiler;
|
||||||
hb.Parser = Parser;
|
hb.Parser = Parser;
|
||||||
hb.parse = parse;
|
hb.parse = parse;
|
||||||
|
hb.parseWithoutProcessing = parseWithoutProcessing;
|
||||||
|
|
||||||
return hb;
|
return hb;
|
||||||
}
|
}
|
||||||
|
|
||||||
let inst = create();
|
let inst = create();
|
||||||
inst.create = create;
|
|
||||||
|
|
||||||
noConflict(inst);
|
noConflict(inst);
|
||||||
|
|
||||||
inst.Visitor = Visitor;
|
inst.Visitor = Visitor;
|
||||||
|
|||||||
+31
-10
@@ -4,31 +4,52 @@ import * as base from './handlebars/base';
|
|||||||
// (This is done to easily share code between commonjs and browse envs)
|
// (This is done to easily share code between commonjs and browse envs)
|
||||||
import SafeString from './handlebars/safe-string';
|
import SafeString from './handlebars/safe-string';
|
||||||
import Exception from './handlebars/exception';
|
import Exception from './handlebars/exception';
|
||||||
import * as Utils from './handlebars/utils';
|
import {
|
||||||
import * as runtime from './handlebars/runtime';
|
extend,
|
||||||
|
toString,
|
||||||
|
isFunction,
|
||||||
|
isArray,
|
||||||
|
indexOf,
|
||||||
|
escapeExpression,
|
||||||
|
isEmpty,
|
||||||
|
createFrame,
|
||||||
|
blockParams,
|
||||||
|
appendContextPath
|
||||||
|
} from './handlebars/utils';
|
||||||
|
import {checkRevision, template, wrapProgram, resolvePartial, invokePartial, noop} from './handlebars/runtime';
|
||||||
|
|
||||||
import noConflict from './handlebars/no-conflict';
|
import noConflict from './handlebars/no-conflict';
|
||||||
|
|
||||||
// For compatibility and usage outside of module systems, make the Handlebars object a namespace
|
// For compatibility and usage outside of module systems, make the Handlebars object a namespace
|
||||||
function create() {
|
export function create() {
|
||||||
let hb = new base.HandlebarsEnvironment();
|
let hb = new base.HandlebarsEnvironment();
|
||||||
|
|
||||||
Utils.extend(hb, base);
|
extend(hb, base);
|
||||||
hb.SafeString = SafeString;
|
hb.SafeString = SafeString;
|
||||||
hb.Exception = Exception;
|
hb.Exception = Exception;
|
||||||
hb.Utils = Utils;
|
hb.Utils = {
|
||||||
hb.escapeExpression = Utils.escapeExpression;
|
extend,
|
||||||
|
toString,
|
||||||
hb.VM = runtime;
|
isFunction,
|
||||||
|
isArray,
|
||||||
|
indexOf,
|
||||||
|
escapeExpression,
|
||||||
|
isEmpty,
|
||||||
|
createFrame,
|
||||||
|
blockParams,
|
||||||
|
appendContextPath
|
||||||
|
};
|
||||||
|
hb.escapeExpression = escapeExpression;
|
||||||
|
hb.create = create;
|
||||||
|
hb.VM = {checkRevision, template, wrapProgram, resolvePartial, invokePartial, noop};
|
||||||
hb.template = function(spec) {
|
hb.template = function(spec) {
|
||||||
return runtime.template(spec, hb);
|
return template(spec, hb);
|
||||||
};
|
};
|
||||||
|
|
||||||
return hb;
|
return hb;
|
||||||
}
|
}
|
||||||
|
|
||||||
let inst = create();
|
let inst = create();
|
||||||
inst.create = create;
|
|
||||||
|
|
||||||
noConflict(inst);
|
noConflict(inst);
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ import {registerDefaultHelpers} from './helpers';
|
|||||||
import {registerDefaultDecorators} from './decorators';
|
import {registerDefaultDecorators} from './decorators';
|
||||||
import logger from './logger';
|
import logger from './logger';
|
||||||
|
|
||||||
export const VERSION = '4.0.9';
|
export const VERSION = '4.5.3';
|
||||||
export const COMPILER_REVISION = 7;
|
export const COMPILER_REVISION = 8;
|
||||||
|
export const LAST_COMPATIBLE_COMPILER_REVISION = 7;
|
||||||
|
|
||||||
export const REVISION_CHANGES = {
|
export const REVISION_CHANGES = {
|
||||||
1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
|
1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
|
||||||
@@ -14,7 +15,8 @@ export const REVISION_CHANGES = {
|
|||||||
4: '== 1.x.x',
|
4: '== 1.x.x',
|
||||||
5: '== 2.0.0-alpha.x',
|
5: '== 2.0.0-alpha.x',
|
||||||
6: '>= 2.0.0-beta.1',
|
6: '>= 2.0.0-beta.1',
|
||||||
7: '>= 4.0.0'
|
7: '>= 4.0.0 <4.3.0',
|
||||||
|
8: '>= 4.3.0'
|
||||||
};
|
};
|
||||||
|
|
||||||
const objectType = '[object Object]';
|
const objectType = '[object Object]';
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export { parser };
|
|||||||
let yy = {};
|
let yy = {};
|
||||||
extend(yy, Helpers);
|
extend(yy, Helpers);
|
||||||
|
|
||||||
export function parse(input, options) {
|
export function parseWithoutProcessing(input, options) {
|
||||||
// Just return if an already-compiled AST was passed in.
|
// Just return if an already-compiled AST was passed in.
|
||||||
if (input.type === 'Program') { return input; }
|
if (input.type === 'Program') { return input; }
|
||||||
|
|
||||||
@@ -19,6 +19,14 @@ export function parse(input, options) {
|
|||||||
return new yy.SourceLocation(options && options.srcName, locInfo);
|
return new yy.SourceLocation(options && options.srcName, locInfo);
|
||||||
};
|
};
|
||||||
|
|
||||||
let strip = new WhitespaceControl(options);
|
let ast = parser.parse(input);
|
||||||
return strip.accept(parser.parse(input));
|
|
||||||
|
return ast;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parse(input, options) {
|
||||||
|
let ast = parseWithoutProcessing(input, options);
|
||||||
|
let strip = new WhitespaceControl(options);
|
||||||
|
|
||||||
|
return strip.accept(ast);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,11 @@
|
|||||||
/* global define */
|
|
||||||
import {isArray} from '../utils';
|
import {isArray} from '../utils';
|
||||||
|
|
||||||
let SourceNode;
|
let SourceNode;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
/* istanbul ignore next */
|
// the 'source-map' map package is ignored in 'umd' and 'amd' builds (via webpack-config)
|
||||||
if (typeof define !== 'function' || !define.amd) {
|
let SourceMap = require('source-map');
|
||||||
// We don't support this in AMD environments. For these environments, we asusme that
|
SourceNode = SourceMap.SourceNode;
|
||||||
// they are running on the browser and thus have no need for the source-map library.
|
|
||||||
let SourceMap = require('source-map');
|
|
||||||
SourceNode = SourceMap.SourceNode;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
/* NOP */
|
/* NOP */
|
||||||
}
|
}
|
||||||
@@ -118,21 +113,19 @@ CodeGen.prototype = {
|
|||||||
.replace(/"/g, '\\"')
|
.replace(/"/g, '\\"')
|
||||||
.replace(/\n/g, '\\n')
|
.replace(/\n/g, '\\n')
|
||||||
.replace(/\r/g, '\\r')
|
.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') + '"';
|
.replace(/\u2029/g, '\\u2029') + '"';
|
||||||
},
|
},
|
||||||
|
|
||||||
objectLiteral: function(obj) {
|
objectLiteral: function(obj) {
|
||||||
let pairs = [];
|
let pairs = [];
|
||||||
|
|
||||||
for (let key in obj) {
|
Object.keys(obj).forEach(key => {
|
||||||
if (obj.hasOwnProperty(key)) {
|
let value = castChunk(obj[key], this);
|
||||||
let value = castChunk(obj[key], this);
|
if (value !== 'undefined') {
|
||||||
if (value !== 'undefined') {
|
pairs.push([this.quotedString(key), ':', value]);
|
||||||
pairs.push([this.quotedString(key), ':', value]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
let ret = this.generateList(pairs);
|
let ret = this.generateList(pairs);
|
||||||
ret.prepend('{');
|
ret.prepend('{');
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable new-cap */
|
/* eslint-disable new-cap */
|
||||||
|
|
||||||
import Exception from '../exception';
|
import Exception from '../exception';
|
||||||
import {isArray, indexOf} from '../utils';
|
import {isArray, indexOf, extend} from '../utils';
|
||||||
import AST from './ast';
|
import AST from './ast';
|
||||||
|
|
||||||
const slice = [].slice;
|
const slice = [].slice;
|
||||||
@@ -54,9 +54,7 @@ Compiler.prototype = {
|
|||||||
|
|
||||||
options.blockParams = options.blockParams || [];
|
options.blockParams = options.blockParams || [];
|
||||||
|
|
||||||
// These changes will propagate to the other compiler components
|
options.knownHelpers = extend(Object.create(null), {
|
||||||
let knownHelpers = options.knownHelpers;
|
|
||||||
options.knownHelpers = {
|
|
||||||
'helperMissing': true,
|
'helperMissing': true,
|
||||||
'blockHelperMissing': true,
|
'blockHelperMissing': true,
|
||||||
'each': true,
|
'each': true,
|
||||||
@@ -65,15 +63,7 @@ Compiler.prototype = {
|
|||||||
'with': true,
|
'with': true,
|
||||||
'log': true,
|
'log': true,
|
||||||
'lookup': true
|
'lookup': true
|
||||||
};
|
}, options.knownHelpers);
|
||||||
if (knownHelpers) {
|
|
||||||
for (let name in knownHelpers) {
|
|
||||||
/* istanbul ignore else */
|
|
||||||
if (name in knownHelpers) {
|
|
||||||
this.options.knownHelpers[name] = knownHelpers[name];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.accept(program);
|
return this.accept(program);
|
||||||
},
|
},
|
||||||
@@ -369,7 +359,6 @@ Compiler.prototype = {
|
|||||||
if (isEligible && !isHelper) {
|
if (isEligible && !isHelper) {
|
||||||
let name = sexpr.path.parts[0],
|
let name = sexpr.path.parts[0],
|
||||||
options = this.options;
|
options = this.options;
|
||||||
|
|
||||||
if (options.knownHelpers[name]) {
|
if (options.knownHelpers[name]) {
|
||||||
isHelper = true;
|
isHelper = true;
|
||||||
} else if (options.knownHelpersOnly) {
|
} else if (options.knownHelpersOnly) {
|
||||||
@@ -488,7 +477,7 @@ export function compile(input, options = {}, env) {
|
|||||||
throw new Exception('You must pass a string or Handlebars AST to Handlebars.compile. You passed ' + input);
|
throw new Exception('You must pass a string or Handlebars AST to Handlebars.compile. You passed ' + input);
|
||||||
}
|
}
|
||||||
|
|
||||||
options = Object.assign({}, options);
|
options = extend({}, options);
|
||||||
if (!('data' in options)) {
|
if (!('data' in options)) {
|
||||||
options.data = true;
|
options.data = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export function SourceLocation(source, locInfo) {
|
|||||||
|
|
||||||
export function id(token) {
|
export function id(token) {
|
||||||
if (/^\[.*\]$/.test(token)) {
|
if (/^\[.*\]$/.test(token)) {
|
||||||
return token.substr(1, token.length - 2);
|
return token.substring(1, token.length - 1);
|
||||||
} else {
|
} else {
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
@@ -38,7 +38,7 @@ export function stripFlags(open, close) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function stripComment(comment) {
|
export function stripComment(comment) {
|
||||||
return comment.replace(/^\{\{~?\!-?-?/, '')
|
return comment.replace(/^\{\{~?!-?-?/, '')
|
||||||
.replace(/-?-?~?\}\}$/, '');
|
.replace(/-?-?~?\}\}$/, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,8 +47,7 @@ export function preparePath(data, parts, loc) {
|
|||||||
|
|
||||||
let original = data ? '@' : '',
|
let original = data ? '@' : '',
|
||||||
dig = [],
|
dig = [],
|
||||||
depth = 0,
|
depth = 0;
|
||||||
depthString = '';
|
|
||||||
|
|
||||||
for (let i = 0, l = parts.length; i < l; i++) {
|
for (let i = 0, l = parts.length; i < l; i++) {
|
||||||
let part = parts[i].part,
|
let part = parts[i].part,
|
||||||
@@ -62,7 +61,6 @@ export function preparePath(data, parts, loc) {
|
|||||||
throw new Exception('Invalid path: ' + original, {loc});
|
throw new Exception('Invalid path: ' + original, {loc});
|
||||||
} else if (part === '..') {
|
} else if (part === '..') {
|
||||||
depth++;
|
depth++;
|
||||||
depthString += '../';
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dig.push(part);
|
dig.push(part);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { COMPILER_REVISION, REVISION_CHANGES } from '../base';
|
|||||||
import Exception from '../exception';
|
import Exception from '../exception';
|
||||||
import {isArray} from '../utils';
|
import {isArray} from '../utils';
|
||||||
import CodeGen from './code-gen';
|
import CodeGen from './code-gen';
|
||||||
|
import {dangerousPropertyRegex} from '../helpers/lookup';
|
||||||
|
|
||||||
function Literal(value) {
|
function Literal(value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
@@ -13,10 +14,18 @@ JavaScriptCompiler.prototype = {
|
|||||||
// PUBLIC API: You can override these methods in a subclass to provide
|
// PUBLIC API: You can override these methods in a subclass to provide
|
||||||
// alternative compiled forms for name lookup and buffering semantics
|
// alternative compiled forms for name lookup and buffering semantics
|
||||||
nameLookup: function(parent, name/* , type*/) {
|
nameLookup: function(parent, name/* , type*/) {
|
||||||
if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
|
if (dangerousPropertyRegex.test(name)) {
|
||||||
return [parent, '.', name];
|
const isEnumerable = [ this.aliasable('container.propertyIsEnumerable'), '.call(', parent, ',', JSON.stringify(name), ')'];
|
||||||
} else {
|
return ['(', isEnumerable, '?', _actualLookup(), ' : undefined)'];
|
||||||
return [parent, '[', JSON.stringify(name), ']'];
|
}
|
||||||
|
return _actualLookup();
|
||||||
|
|
||||||
|
function _actualLookup() {
|
||||||
|
if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
|
||||||
|
return [parent, '.', name];
|
||||||
|
} else {
|
||||||
|
return [parent, '[', JSON.stringify(name), ']'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
depthedLookup: function(name) {
|
depthedLookup: function(name) {
|
||||||
@@ -133,7 +142,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (this.decorators) {
|
if (this.decorators) {
|
||||||
ret.main_d = this.decorators; // eslint-disable-line camelcase
|
ret.main_d = this.decorators; // eslint-disable-line camelcase
|
||||||
ret.useDecorators = true;
|
ret.useDecorators = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,14 +218,13 @@ JavaScriptCompiler.prototype = {
|
|||||||
// aliases will not be used, but this case is already being run on the client and
|
// 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.
|
// we aren't concern about minimizing the template size.
|
||||||
let aliasCount = 0;
|
let aliasCount = 0;
|
||||||
for (let alias in this.aliases) { // eslint-disable-line guard-for-in
|
Object.keys(this.aliases).forEach(alias => {
|
||||||
let node = this.aliases[alias];
|
let node = this.aliases[alias];
|
||||||
|
if (node.children && node.referenceCount > 1) {
|
||||||
if (this.aliases.hasOwnProperty(alias) && node.children && node.referenceCount > 1) {
|
|
||||||
varDeclarations += ', alias' + (++aliasCount) + '=' + alias;
|
varDeclarations += ', alias' + (++aliasCount) + '=' + alias;
|
||||||
node.children[0] = 'alias' + aliasCount;
|
node.children[0] = 'alias' + aliasCount;
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
let params = ['container', 'depth0', 'helpers', 'partials', 'data'];
|
let params = ['container', 'depth0', 'helpers', 'partials', 'data'];
|
||||||
|
|
||||||
@@ -308,7 +316,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
// replace it on the stack with the result of properly
|
// replace it on the stack with the result of properly
|
||||||
// invoking blockHelperMissing.
|
// invoking blockHelperMissing.
|
||||||
blockValue: function(name) {
|
blockValue: function(name) {
|
||||||
let blockHelperMissing = this.aliasable('helpers.blockHelperMissing'),
|
let blockHelperMissing = this.aliasable('container.hooks.blockHelperMissing'),
|
||||||
params = [this.contextName(0)];
|
params = [this.contextName(0)];
|
||||||
this.setupHelperArgs(name, 0, params);
|
this.setupHelperArgs(name, 0, params);
|
||||||
|
|
||||||
@@ -326,7 +334,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
// On stack, after, if lastHelper: value
|
// On stack, after, if lastHelper: value
|
||||||
ambiguousBlockValue: function() {
|
ambiguousBlockValue: function() {
|
||||||
// We're being a bit cheeky and reusing the options value from the prior exec
|
// We're being a bit cheeky and reusing the options value from the prior exec
|
||||||
let blockHelperMissing = this.aliasable('helpers.blockHelperMissing'),
|
let blockHelperMissing = this.aliasable('container.hooks.blockHelperMissing'),
|
||||||
params = [this.contextName(0)];
|
params = [this.contextName(0)];
|
||||||
this.setupHelperArgs('', 0, params, true);
|
this.setupHelperArgs('', 0, params, true);
|
||||||
|
|
||||||
@@ -336,9 +344,9 @@ JavaScriptCompiler.prototype = {
|
|||||||
params.splice(1, 0, current);
|
params.splice(1, 0, current);
|
||||||
|
|
||||||
this.pushSource([
|
this.pushSource([
|
||||||
'if (!', this.lastHelper, ') { ',
|
'if (!', this.lastHelper, ') { ',
|
||||||
current, ' = ', this.source.functionCall(blockHelperMissing, 'call', params),
|
current, ' = ', this.source.functionCall(blockHelperMissing, 'call', params),
|
||||||
'}']);
|
'}']);
|
||||||
},
|
},
|
||||||
|
|
||||||
// [appendContent]
|
// [appendContent]
|
||||||
@@ -535,7 +543,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
if (this.hash) {
|
if (this.hash) {
|
||||||
this.hashes.push(this.hash);
|
this.hashes.push(this.hash);
|
||||||
}
|
}
|
||||||
this.hash = {values: [], types: [], contexts: [], ids: []};
|
this.hash = {values: {}, types: [], contexts: [], ids: []};
|
||||||
},
|
},
|
||||||
popHash: function() {
|
popHash: function() {
|
||||||
let hash = this.hash;
|
let hash = this.hash;
|
||||||
@@ -619,18 +627,32 @@ JavaScriptCompiler.prototype = {
|
|||||||
// If the helper is not found, `helperMissing` is called.
|
// If the helper is not found, `helperMissing` is called.
|
||||||
invokeHelper: function(paramSize, name, isSimple) {
|
invokeHelper: function(paramSize, name, isSimple) {
|
||||||
let nonHelper = this.popStack(),
|
let nonHelper = this.popStack(),
|
||||||
helper = this.setupHelper(paramSize, name),
|
helper = this.setupHelper(paramSize, name);
|
||||||
simple = isSimple ? [helper.name, ' || '] : '';
|
|
||||||
|
|
||||||
let lookup = ['('].concat(simple, nonHelper);
|
let possibleFunctionCalls = [];
|
||||||
if (!this.options.strict) {
|
|
||||||
lookup.push(' || ', this.aliasable('helpers.helperMissing'));
|
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'));
|
||||||
}
|
}
|
||||||
lookup.push(')');
|
|
||||||
|
|
||||||
this.push(this.source.functionCall(lookup, 'call', helper.callParams));
|
let functionLookupCode = ['(', this.itemsSeparatedBy(possibleFunctionCalls, '||'), ')'];
|
||||||
|
let functionCall = this.source.functionCall(functionLookupCode, 'call', helper.callParams);
|
||||||
|
this.push(functionCall);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
itemsSeparatedBy: function(items, separator) {
|
||||||
|
let result = [];
|
||||||
|
result.push(items[0]);
|
||||||
|
for (let i = 1; i < items.length; i++) {
|
||||||
|
result.push(separator, items[i]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
// [invokeKnownHelper]
|
// [invokeKnownHelper]
|
||||||
//
|
//
|
||||||
// On stack, before: hash, inverse, program, params..., ...
|
// On stack, before: hash, inverse, program, params..., ...
|
||||||
@@ -669,16 +691,16 @@ JavaScriptCompiler.prototype = {
|
|||||||
if (!this.options.strict) {
|
if (!this.options.strict) {
|
||||||
lookup[0] = '(helper = ';
|
lookup[0] = '(helper = ';
|
||||||
lookup.push(
|
lookup.push(
|
||||||
' != null ? helper : ',
|
' != null ? helper : ',
|
||||||
this.aliasable('helpers.helperMissing')
|
this.aliasable('container.hooks.helperMissing')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.push([
|
this.push([
|
||||||
'(', lookup,
|
'(', lookup,
|
||||||
(helper.paramsInit ? ['),(', helper.paramsInit] : []), '),',
|
(helper.paramsInit ? ['),(', helper.paramsInit] : []), '),',
|
||||||
'(typeof helper === ', this.aliasable('"function"'), ' ? ',
|
'(typeof helper === ', this.aliasable('"function"'), ' ? ',
|
||||||
this.source.functionCall('helper', 'call', helper.callParams), ' : helper))'
|
this.source.functionCall('helper', 'call', helper.callParams), ' : helper))'
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -776,12 +798,12 @@ JavaScriptCompiler.prototype = {
|
|||||||
|
|
||||||
for (let i = 0, l = children.length; i < l; i++) {
|
for (let i = 0, l = children.length; i < l; i++) {
|
||||||
child = children[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);
|
let existing = this.matchExistingProgram(child);
|
||||||
|
|
||||||
if (existing == null) {
|
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;
|
let index = this.context.programs.length;
|
||||||
child.index = index;
|
child.index = index;
|
||||||
child.name = 'program' + index;
|
child.name = 'program' + index;
|
||||||
@@ -1069,6 +1091,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
|
|
||||||
setupHelperArgs: function(helper, paramSize, params, useRegister) {
|
setupHelperArgs: function(helper, paramSize, params, useRegister) {
|
||||||
let options = this.setupParams(helper, paramSize, params);
|
let options = this.setupParams(helper, paramSize, params);
|
||||||
|
options.loc = JSON.stringify(this.source.currentLocation);
|
||||||
options = this.objectLiteral(options);
|
options = this.objectLiteral(options);
|
||||||
if (useRegister) {
|
if (useRegister) {
|
||||||
this.useRegister('options');
|
this.useRegister('options');
|
||||||
@@ -1128,7 +1151,7 @@ function strictLookup(requireTerminal, compiler, parts, type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (requireTerminal) {
|
if (requireTerminal) {
|
||||||
return [compiler.aliasable('container.strict'), '(', stack, ', ', compiler.quotedString(parts[i]), ')'];
|
return [compiler.aliasable('container.strict'), '(', stack, ', ', compiler.quotedString(parts[i]), ', ', JSON.stringify(compiler.source.currentLocation), ' )'];
|
||||||
} else {
|
} else {
|
||||||
return stack;
|
return stack;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ function omitLeft(body, i, multiple) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We omit the last node if it's whitespace only and not preceeded by a non-content node.
|
// We omit the last node if it's whitespace only and not preceded by a non-content node.
|
||||||
let original = current.value;
|
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;
|
current.leftStripped = current.value !== original;
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
|
|
||||||
const errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
|
const errorProps = ['description', 'fileName', 'lineNumber', 'endLineNumber', 'message', 'name', 'number', 'stack'];
|
||||||
|
|
||||||
function Exception(message, node) {
|
function Exception(message, node) {
|
||||||
let loc = node && node.loc,
|
let loc = node && node.loc,
|
||||||
line,
|
line,
|
||||||
column;
|
endLineNumber,
|
||||||
|
column,
|
||||||
|
endColumn;
|
||||||
|
|
||||||
if (loc) {
|
if (loc) {
|
||||||
line = loc.start.line;
|
line = loc.start.line;
|
||||||
|
endLineNumber = loc.end.line;
|
||||||
column = loc.start.column;
|
column = loc.start.column;
|
||||||
|
endColumn = loc.end.column;
|
||||||
|
|
||||||
message += ' - ' + line + ':' + column;
|
message += ' - ' + line + ':' + column;
|
||||||
}
|
}
|
||||||
@@ -27,6 +32,7 @@ function Exception(message, node) {
|
|||||||
try {
|
try {
|
||||||
if (loc) {
|
if (loc) {
|
||||||
this.lineNumber = line;
|
this.lineNumber = line;
|
||||||
|
this.endLineNumber = endLineNumber;
|
||||||
|
|
||||||
// Work around issue under safari where we can't directly set the column value
|
// Work around issue under safari where we can't directly set the column value
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
@@ -35,8 +41,13 @@ function Exception(message, node) {
|
|||||||
value: column,
|
value: column,
|
||||||
enumerable: true
|
enumerable: true
|
||||||
});
|
});
|
||||||
|
Object.defineProperty(this, 'endColumn', {
|
||||||
|
value: endColumn,
|
||||||
|
enumerable: true
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
this.column = column;
|
this.column = column;
|
||||||
|
this.endColumn = endColumn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (nop) {
|
} catch (nop) {
|
||||||
|
|||||||
@@ -15,3 +15,12 @@ export function registerDefaultHelpers(instance) {
|
|||||||
registerLookup(instance);
|
registerLookup(instance);
|
||||||
registerWith(instance);
|
registerWith(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function moveHelperToHooks(instance, helperName, keepHelper) {
|
||||||
|
if (instance.helpers[helperName]) {
|
||||||
|
instance.hooks[helperName] = instance.helpers[helperName];
|
||||||
|
if (!keepHelper) {
|
||||||
|
delete instance.helpers[helperName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -49,21 +49,29 @@ export default function(instance) {
|
|||||||
execIteration(i, i, i === context.length - 1);
|
execIteration(i, i, i === context.length - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (global.Symbol && context[global.Symbol.iterator]) {
|
||||||
|
const newContext = [];
|
||||||
|
const iterator = context[global.Symbol.iterator]();
|
||||||
|
for (let it = iterator.next(); !it.done; it = iterator.next()) {
|
||||||
|
newContext.push(it.value);
|
||||||
|
}
|
||||||
|
context = newContext;
|
||||||
|
for (let j = context.length; i < j; i++) {
|
||||||
|
execIteration(i, i, i === context.length - 1);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let priorKey;
|
let priorKey;
|
||||||
|
|
||||||
for (let key in context) {
|
Object.keys(context).forEach(key => {
|
||||||
if (context.hasOwnProperty(key)) {
|
// We're running the iterations one step out of sync so we can detect
|
||||||
// We're running the iterations one step out of sync so we can detect
|
// the last iteration without have to scan the object twice and create
|
||||||
// the last iteration without have to scan the object twice and create
|
// an itermediate keys array.
|
||||||
// an itermediate keys array.
|
if (priorKey !== undefined) {
|
||||||
if (priorKey !== undefined) {
|
execIteration(priorKey, i - 1);
|
||||||
execIteration(priorKey, i - 1);
|
|
||||||
}
|
|
||||||
priorKey = key;
|
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
}
|
priorKey = key;
|
||||||
|
i++;
|
||||||
|
});
|
||||||
if (priorKey !== undefined) {
|
if (priorKey !== undefined) {
|
||||||
execIteration(priorKey, i - 1, true);
|
execIteration(priorKey, i - 1, true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import {isEmpty, isFunction} from '../utils';
|
import { isEmpty, isFunction } from '../utils';
|
||||||
|
import Exception from '../exception';
|
||||||
|
|
||||||
export default function(instance) {
|
export default function(instance) {
|
||||||
instance.registerHelper('if', function(conditional, options) {
|
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 (isFunction(conditional)) { conditional = conditional.call(this); }
|
||||||
|
|
||||||
// Default behavior is to render the positive path if the value is truthy and not empty.
|
// Default behavior is to render the positive path if the value is truthy and not empty.
|
||||||
@@ -15,6 +17,7 @@ export default function(instance) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
instance.registerHelper('unless', function(conditional, options) {
|
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});
|
return instance.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn, hash: options.hash});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
|
export const dangerousPropertyRegex = /^(constructor|__defineGetter__|__defineSetter__|__lookupGetter__|__proto__)$/;
|
||||||
|
|
||||||
export default function(instance) {
|
export default function(instance) {
|
||||||
instance.registerHelper('lookup', function(obj, field) {
|
instance.registerHelper('lookup', function(obj, field) {
|
||||||
return obj && obj[field];
|
if (!obj) {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
if (dangerousPropertyRegex.test(String(field)) && !Object.prototype.propertyIsEnumerable.call(obj, field)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return obj[field];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import {appendContextPath, blockParams, createFrame, isEmpty, isFunction} from '../utils';
|
import { appendContextPath, blockParams, createFrame, isEmpty, isFunction } from '../utils';
|
||||||
|
import Exception from '../exception';
|
||||||
|
|
||||||
export default function(instance) {
|
export default function(instance) {
|
||||||
instance.registerHelper('with', function(context, options) {
|
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 (isFunction(context)) { context = context.call(this); }
|
||||||
|
|
||||||
let fn = options.fn;
|
let fn = options.fn;
|
||||||
|
|||||||
@@ -24,10 +24,10 @@ let logger = {
|
|||||||
|
|
||||||
if (typeof console !== 'undefined' && logger.lookupLevel(logger.level) <= level) {
|
if (typeof console !== 'undefined' && logger.lookupLevel(logger.level) <= level) {
|
||||||
let method = logger.methodMap[level];
|
let method = logger.methodMap[level];
|
||||||
if (!console[method]) { // eslint-disable-line no-console
|
if (!console[method]) { // eslint-disable-line no-console
|
||||||
method = 'log';
|
method = 'log';
|
||||||
}
|
}
|
||||||
console[method](...message); // eslint-disable-line no-console
|
console[method](...message); // eslint-disable-line no-console
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+48
-36
@@ -1,26 +1,30 @@
|
|||||||
import * as Utils from './utils';
|
import {extend, escapeExpression} from './utils';
|
||||||
import Exception from './exception';
|
import Exception from './exception';
|
||||||
import { COMPILER_REVISION, REVISION_CHANGES, createFrame } from './base';
|
import {COMPILER_REVISION, createFrame, LAST_COMPATIBLE_COMPILER_REVISION, REVISION_CHANGES} from './base';
|
||||||
|
import {moveHelperToHooks} from './helpers';
|
||||||
|
|
||||||
export function checkRevision(compilerInfo) {
|
export function checkRevision(compilerInfo) {
|
||||||
const compilerRevision = compilerInfo && compilerInfo[0] || 1,
|
const compilerRevision = compilerInfo && compilerInfo[0] || 1,
|
||||||
currentRevision = COMPILER_REVISION;
|
currentRevision = COMPILER_REVISION;
|
||||||
|
|
||||||
if (compilerRevision !== currentRevision) {
|
if (compilerRevision >= LAST_COMPATIBLE_COMPILER_REVISION && compilerRevision <= COMPILER_REVISION) {
|
||||||
if (compilerRevision < currentRevision) {
|
return;
|
||||||
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. ' +
|
if (compilerRevision < LAST_COMPATIBLE_COMPILER_REVISION) {
|
||||||
'Please update your precompiler to a newer version (' + runtimeVersions + ') or downgrade your runtime to an older version (' + compilerVersions + ').');
|
const runtimeVersions = REVISION_CHANGES[currentRevision],
|
||||||
} else {
|
compilerVersions = REVISION_CHANGES[compilerRevision];
|
||||||
// Use the embedded version info since the runtime doesn't know about this revision yet
|
throw new Exception('Template was precompiled with an older version of Handlebars than the current runtime. ' +
|
||||||
throw new Exception('Template was precompiled with a newer 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 + ').');
|
||||||
'Please update your runtime to a newer version (' + compilerInfo[1] + ').');
|
} 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] + ').');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function template(templateSpec, env) {
|
export function template(templateSpec, env) {
|
||||||
|
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
if (!env) {
|
if (!env) {
|
||||||
throw new Exception('No environment passed to template');
|
throw new Exception('No environment passed to template');
|
||||||
@@ -32,23 +36,28 @@ export function template(templateSpec, env) {
|
|||||||
templateSpec.main.decorator = templateSpec.main_d;
|
templateSpec.main.decorator = templateSpec.main_d;
|
||||||
|
|
||||||
// Note: Using env.VM references rather than local var references throughout this section to allow
|
// Note: Using env.VM references rather than local var references throughout this section to allow
|
||||||
// for external users to override these as psuedo-supported APIs.
|
// for external users to override these as pseudo-supported APIs.
|
||||||
env.VM.checkRevision(templateSpec.compiler);
|
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;
|
||||||
|
|
||||||
function invokePartialWrapper(partial, context, options) {
|
function invokePartialWrapper(partial, context, options) {
|
||||||
if (options.hash) {
|
if (options.hash) {
|
||||||
context = Utils.extend({}, context, options.hash);
|
context = extend({}, context, options.hash);
|
||||||
if (options.ids) {
|
if (options.ids) {
|
||||||
options.ids[0] = true;
|
options.ids[0] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
partial = env.VM.resolvePartial.call(this, partial, context, options);
|
partial = env.VM.resolvePartial.call(this, partial, context, options);
|
||||||
let result = env.VM.invokePartial.call(this, partial, context, options);
|
|
||||||
|
let optionsWithHooks = extend({}, options, {hooks: this.hooks});
|
||||||
|
|
||||||
|
let result = env.VM.invokePartial.call(this, partial, context, optionsWithHooks);
|
||||||
|
|
||||||
if (result == null && env.compile) {
|
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, options);
|
result = options.partials[options.name](context, optionsWithHooks);
|
||||||
}
|
}
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
if (options.indent) {
|
if (options.indent) {
|
||||||
@@ -70,9 +79,9 @@ export function template(templateSpec, env) {
|
|||||||
|
|
||||||
// Just add water
|
// Just add water
|
||||||
let container = {
|
let container = {
|
||||||
strict: function(obj, name) {
|
strict: function(obj, name, loc) {
|
||||||
if (!(name in obj)) {
|
if (!obj || !(name in obj)) {
|
||||||
throw new Exception('"' + name + '" not defined in ' + obj);
|
throw new Exception('"' + name + '" not defined in ' + obj, { loc: loc });
|
||||||
}
|
}
|
||||||
return obj[name];
|
return obj[name];
|
||||||
},
|
},
|
||||||
@@ -88,7 +97,7 @@ export function template(templateSpec, env) {
|
|||||||
return typeof current === 'function' ? current.call(context) : current;
|
return typeof current === 'function' ? current.call(context) : current;
|
||||||
},
|
},
|
||||||
|
|
||||||
escapeExpression: Utils.escapeExpression,
|
escapeExpression: escapeExpression,
|
||||||
invokePartial: invokePartialWrapper,
|
invokePartial: invokePartialWrapper,
|
||||||
|
|
||||||
fn: function(i) {
|
fn: function(i) {
|
||||||
@@ -115,15 +124,6 @@ export function template(templateSpec, env) {
|
|||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
},
|
},
|
||||||
merge: function(param, common) {
|
|
||||||
let obj = param || common;
|
|
||||||
|
|
||||||
if (param && common && (param !== common)) {
|
|
||||||
obj = Utils.extend({}, common, param);
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
},
|
|
||||||
// An empty object to use as replacement for null-contexts
|
// An empty object to use as replacement for null-contexts
|
||||||
nullContext: Object.seal({}),
|
nullContext: Object.seal({}),
|
||||||
|
|
||||||
@@ -158,19 +158,28 @@ export function template(templateSpec, env) {
|
|||||||
|
|
||||||
ret._setup = function(options) {
|
ret._setup = function(options) {
|
||||||
if (!options.partial) {
|
if (!options.partial) {
|
||||||
container.helpers = container.merge(options.helpers, env.helpers);
|
container.helpers = extend({}, env.helpers, options.helpers);
|
||||||
|
|
||||||
if (templateSpec.usePartial) {
|
if (templateSpec.usePartial) {
|
||||||
container.partials = container.merge(options.partials, env.partials);
|
container.partials = extend({}, env.partials, options.partials);
|
||||||
}
|
}
|
||||||
if (templateSpec.usePartial || templateSpec.useDecorators) {
|
if (templateSpec.usePartial || templateSpec.useDecorators) {
|
||||||
container.decorators = container.merge(options.decorators, env.decorators);
|
container.decorators = extend({}, env.decorators, options.decorators);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
container.hooks = {};
|
||||||
|
|
||||||
|
let keepHelperInHelpers = options.allowCallsToHelperMissing || templateWasPrecompiledWithCompilerV7;
|
||||||
|
moveHelperToHooks(container, 'helperMissing', keepHelperInHelpers);
|
||||||
|
moveHelperToHooks(container, 'blockHelperMissing', keepHelperInHelpers);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
container.helpers = options.helpers;
|
container.helpers = options.helpers;
|
||||||
container.partials = options.partials;
|
container.partials = options.partials;
|
||||||
container.decorators = options.decorators;
|
container.decorators = options.decorators;
|
||||||
|
container.hooks = options.hooks;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ret._child = function(i, data, blockParams, depths) {
|
ret._child = function(i, data, blockParams, depths) {
|
||||||
@@ -209,6 +218,9 @@ export function wrapProgram(container, i, fn, data, declaredBlockParams, blockPa
|
|||||||
return prog;
|
return prog;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is currently part of the official API, therefore implementation details should not be changed.
|
||||||
|
*/
|
||||||
export function resolvePartial(partial, context, options) {
|
export function resolvePartial(partial, context, options) {
|
||||||
if (!partial) {
|
if (!partial) {
|
||||||
if (options.name === '@partial-block') {
|
if (options.name === '@partial-block') {
|
||||||
@@ -246,7 +258,7 @@ export function invokePartial(partial, context, options) {
|
|||||||
return fn(context, options);
|
return fn(context, options);
|
||||||
};
|
};
|
||||||
if (fn.partials) {
|
if (fn.partials) {
|
||||||
options.partials = Utils.extend({}, options.partials, fn.partials);
|
options.partials = extend({}, options.partials, fn.partials);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,7 +287,7 @@ function executeDecorators(fn, prog, container, depths, data, blockParams) {
|
|||||||
if (fn.decorator) {
|
if (fn.decorator) {
|
||||||
let props = {};
|
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);
|
extend(prog, props);
|
||||||
}
|
}
|
||||||
return prog;
|
return prog;
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-10
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
const escape = {
|
const escape = {
|
||||||
'&': '&',
|
'&': '&',
|
||||||
'<': '<',
|
'<': '<',
|
||||||
@@ -32,18 +33,23 @@ export let toString = Object.prototype.toString;
|
|||||||
// Sourced from lodash
|
// Sourced from lodash
|
||||||
// https://github.com/bestiejs/lodash/blob/master/LICENSE.txt
|
// https://github.com/bestiejs/lodash/blob/master/LICENSE.txt
|
||||||
/* eslint-disable func-style */
|
/* eslint-disable func-style */
|
||||||
let isFunction = function(value) {
|
export const isFunction = createIsFunction();
|
||||||
return typeof value === 'function';
|
|
||||||
};
|
|
||||||
// fallback for older versions of Chrome and Safari
|
function createIsFunction() {
|
||||||
/* istanbul ignore next */
|
// istanbul ignore next
|
||||||
if (isFunction(/x/)) {
|
// eslint-disable-next-line no-constant-condition
|
||||||
isFunction = function(value) {
|
if (typeof /x/ === 'function') {
|
||||||
return typeof value === 'function' && toString.call(value) === '[object Function]';
|
// fallback for older chrome/firefox versions
|
||||||
|
return function isFunction(value) {
|
||||||
|
return typeof value === 'function' && toString.call(value) === '[object Function]';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return function isFunction(value) {
|
||||||
|
return typeof value === 'function';
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export {isFunction};
|
|
||||||
/* eslint-enable func-style */
|
|
||||||
|
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
export const isArray = Array.isArray || function(value) {
|
export const isArray = Array.isArray || function(value) {
|
||||||
@@ -61,6 +67,7 @@ export function indexOf(array, value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function escapeExpression(string) {
|
export function escapeExpression(string) {
|
||||||
if (typeof string !== 'string') {
|
if (typeof string !== 'string') {
|
||||||
// don't escape SafeStrings, since they're already safe
|
// don't escape SafeStrings, since they're already safe
|
||||||
@@ -82,6 +89,7 @@ export function escapeExpression(string) {
|
|||||||
return string.replace(badChars, escapeChar);
|
return string.replace(badChars, escapeChar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function isEmpty(value) {
|
export function isEmpty(value) {
|
||||||
if (!value && value !== 0) {
|
if (!value && value !== 0) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
+36
-11
@@ -1,10 +1,10 @@
|
|||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
import Async from 'async';
|
import Async from 'neo-async';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import * as Handlebars from './handlebars';
|
import Handlebars from './handlebars';
|
||||||
import {basename} from 'path';
|
import {basename} from 'path';
|
||||||
import {SourceMapConsumer, SourceNode} from 'source-map';
|
import {SourceMapConsumer, SourceNode} from 'source-map';
|
||||||
import uglify from 'uglify-js';
|
|
||||||
|
|
||||||
module.exports.loadTemplates = function(opts, callback) {
|
module.exports.loadTemplates = function(opts, callback) {
|
||||||
loadStrings(opts, function(err, strings) {
|
loadStrings(opts, function(err, strings) {
|
||||||
@@ -60,7 +60,7 @@ function loadStrings(opts, callback) {
|
|||||||
|
|
||||||
function loadFiles(opts, callback) {
|
function loadFiles(opts, callback) {
|
||||||
// Build file extension pattern
|
// 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 + '$');
|
extension = new RegExp('\\.' + extension + '$');
|
||||||
|
|
||||||
let ret = [],
|
let ret = [],
|
||||||
@@ -235,7 +235,6 @@ module.exports.cli = function(opts) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (opts.map) {
|
if (opts.map) {
|
||||||
output.add('\n//# sourceMappingURL=' + opts.map + '\n');
|
output.add('\n//# sourceMappingURL=' + opts.map + '\n');
|
||||||
}
|
}
|
||||||
@@ -244,12 +243,7 @@ module.exports.cli = function(opts) {
|
|||||||
output.map = output.map + '';
|
output.map = output.map + '';
|
||||||
|
|
||||||
if (opts.min) {
|
if (opts.min) {
|
||||||
output = uglify.minify(output.code, {
|
output = minify(output, opts.map);
|
||||||
fromString: true,
|
|
||||||
|
|
||||||
outSourceMap: opts.map,
|
|
||||||
inSourceMap: JSON.parse(output.map)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.map) {
|
if (opts.map) {
|
||||||
@@ -271,3 +265,34 @@ function arrayCast(value) {
|
|||||||
}
|
}
|
||||||
return 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, {
|
||||||
|
sourceMap: {
|
||||||
|
content: output.map,
|
||||||
|
url: sourceMapFile
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
Generated
+12964
File diff suppressed because it is too large
Load Diff
+52
-23
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "handlebars",
|
"name": "handlebars",
|
||||||
"barename": "handlebars",
|
"barename": "handlebars",
|
||||||
"version": "4.0.9",
|
"version": "4.5.3",
|
||||||
"description": "Handlebars provides the power necessary to let you build semantic templates effectively with no frustration",
|
"description": "Handlebars provides the power necessary to let you build semantic templates effectively with no frustration",
|
||||||
"homepage": "http://www.handlebarsjs.com/",
|
"homepage": "http://www.handlebarsjs.com/",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
@@ -21,48 +21,66 @@
|
|||||||
"node": ">=0.4.7"
|
"node": ">=0.4.7"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"async": "^1.4.0",
|
"neo-async": "^2.6.0",
|
||||||
"optimist": "^0.6.1",
|
"optimist": "^0.6.1",
|
||||||
"source-map": "^0.4.4"
|
"source-map": "^0.6.1"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"uglify-js": "^2.6"
|
"uglify-js": "^3.1.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.7.4",
|
||||||
|
"@babel/plugin-transform-modules-amd": "^7.7.4",
|
||||||
|
"@babel/plugin-transform-modules-commonjs": "^7.7.4",
|
||||||
|
"@babel/plugin-transform-runtime": "^7.7.4",
|
||||||
|
"@babel/preset-env": "^7.7.4",
|
||||||
|
"@babel/runtime": "^7.7.4",
|
||||||
|
"@knappi/grunt-saucelabs": "^9.0.2",
|
||||||
"aws-sdk": "^2.1.49",
|
"aws-sdk": "^2.1.49",
|
||||||
"babel-loader": "^5.0.0",
|
"babel-loader": "^8.0.6",
|
||||||
"babel-runtime": "^5.1.10",
|
"babel-plugin-add-module-exports": "^1.0.2",
|
||||||
"benchmark": "~1.0",
|
"benchmark": "~1.0",
|
||||||
|
"chai": "^4.2.0",
|
||||||
|
"dirty-chai": "^2.0.1",
|
||||||
|
"dtslint": "^0.5.5",
|
||||||
"dustjs-linkedin": "^2.0.2",
|
"dustjs-linkedin": "^2.0.2",
|
||||||
"eco": "~1.1.0-rc-3",
|
"eco": "~1.1.0-rc-3",
|
||||||
"grunt": "~0.4.1",
|
"eslint-plugin-compat": "^3.3.0",
|
||||||
"grunt-babel": "^5.0.0",
|
"eslint-plugin-es5": "^1.4.1",
|
||||||
"grunt-cli": "~0.1.10",
|
"grunt": "^1.0.4",
|
||||||
"grunt-contrib-clean": "0.x",
|
"grunt-babel": "^8.0.0",
|
||||||
"grunt-contrib-concat": "0.x",
|
"grunt-bg-shell": "^2.3.3",
|
||||||
"grunt-contrib-connect": "0.x",
|
"grunt-cli": "^1",
|
||||||
"grunt-contrib-copy": "0.x",
|
"grunt-contrib-clean": "^1",
|
||||||
"grunt-contrib-requirejs": "0.x",
|
"grunt-contrib-concat": "^1",
|
||||||
"grunt-contrib-uglify": "0.x",
|
"grunt-contrib-connect": "^1",
|
||||||
"grunt-contrib-watch": "0.x",
|
"grunt-contrib-copy": "^1",
|
||||||
"grunt-eslint": "^17.1.0",
|
"grunt-contrib-watch": "^1.1.0",
|
||||||
"grunt-saucelabs": "8.x",
|
"grunt-eslint": "^20.1.0",
|
||||||
"grunt-webpack": "^1.0.8",
|
"grunt-webpack": "^3.1.3",
|
||||||
"istanbul": "^0.3.0",
|
"istanbul": "^0.3.0",
|
||||||
"jison": "~0.3.0",
|
"jison": "~0.3.0",
|
||||||
"mocha": "~1.20.0",
|
"mocha": "^5",
|
||||||
"mock-stdin": "^0.3.0",
|
"mock-stdin": "^0.3.0",
|
||||||
"mustache": "^2.1.3",
|
"mustache": "^2.1.3",
|
||||||
"semver": "^5.0.1",
|
"semver": "^5.0.1",
|
||||||
|
"sinon": "^7.5.0",
|
||||||
|
"typescript": "^3.4.3",
|
||||||
"underscore": "^1.5.1",
|
"underscore": "^1.5.1",
|
||||||
"webpack": "^1.12.6",
|
"webpack": "^4.41.2",
|
||||||
"webpack-dev-server": "^1.12.1"
|
"webpack-dev-server": "^3.9.0"
|
||||||
},
|
},
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
|
"types": "types/index.d.ts",
|
||||||
|
"browser": {
|
||||||
|
".": "./dist/cjs/handlebars.js",
|
||||||
|
"./runtime": "./dist/cjs/handlebars.runtime.js"
|
||||||
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"handlebars": "bin/handlebars"
|
"handlebars": "bin/handlebars"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"checkTypes": "dtslint types",
|
||||||
"test": "grunt"
|
"test": "grunt"
|
||||||
},
|
},
|
||||||
"jspm": {
|
"jspm": {
|
||||||
@@ -73,5 +91,16 @@
|
|||||||
"buildConfig": {
|
"buildConfig": {
|
||||||
"minify": true
|
"minify": true
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"files": [
|
||||||
|
"bin",
|
||||||
|
"dist/*.js",
|
||||||
|
"dist/amd/**/*.js",
|
||||||
|
"dist/cjs/**/*.js",
|
||||||
|
"lib",
|
||||||
|
"print-script",
|
||||||
|
"release-notes.md",
|
||||||
|
"runtime.js",
|
||||||
|
"types/*.d.ts"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+487
-129
@@ -2,7 +2,365 @@
|
|||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
[Commits](https://github.com/nknapp/handlebars.js/compare/v4.0.9...master)
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.5.3...master)
|
||||||
|
|
||||||
|
## v4.5.3 - November 18th, 2019
|
||||||
|
Bugfixes:
|
||||||
|
|
||||||
|
- fix: add "no-prototype-builtins" eslint-rule and fix all occurences - f7f05d7
|
||||||
|
- fix: add more properties required to be enumerable - 1988878
|
||||||
|
|
||||||
|
Chores / Build:
|
||||||
|
- fix: use !== 0 instead of != 0 - c02b05f
|
||||||
|
- add chai and dirty-chai and sinon, for cleaner test-assertions and spies,
|
||||||
|
deprecate old assertion-methods - 93e284e, 886ba86, 0817dad, 93516a0
|
||||||
|
|
||||||
|
Security:
|
||||||
|
|
||||||
|
- The properties `__proto__`, `__defineGetter__`, `__defineSetter__` and `__lookupGetter__`
|
||||||
|
have been added to the list of "properties that must be enumerable".
|
||||||
|
If a property by that name is found and not enumerable on its parent,
|
||||||
|
it will silently evaluate to `undefined`. This is done in both the compiled template and the "lookup"-helper.
|
||||||
|
This will prevent new Remote-Code-Execution exploits that have been
|
||||||
|
published recently.
|
||||||
|
|
||||||
|
Compatibility notes:
|
||||||
|
|
||||||
|
- Due to the security-fixes. The semantics of the templates using
|
||||||
|
`__proto__`, `__defineGetter__`, `__defineSetter__` and `__lookupGetter__` in the respect that those expression now return
|
||||||
|
`undefined` rather than their actual value from the proto.
|
||||||
|
- The semantics have not changed in cases where the properties are enumerable, as in:
|
||||||
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
__proto__: 'some string'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- The change may be breaking in that respect, but we still only
|
||||||
|
increase the patch-version, because the incompatible use-cases
|
||||||
|
are not intended, undocumented and far less important than fixing
|
||||||
|
Remote-Code-Execution exploits on existing systems.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.5.2...v4.5.3)
|
||||||
|
|
||||||
|
## v4.5.2 - November 13th, 2019
|
||||||
|
# Bugfixes
|
||||||
|
|
||||||
|
- fix: use String(field) in lookup when checking for "constructor" - d541378
|
||||||
|
- test: add fluent API for testing Handlebars - c2ac79c
|
||||||
|
|
||||||
|
Compatibility notes:
|
||||||
|
- no incompatibility are to be expected
|
||||||
|
|
||||||
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.5.1...v4.5.2)
|
||||||
|
|
||||||
|
## v4.5.1 - October 29th, 2019
|
||||||
|
Bugfixs
|
||||||
|
|
||||||
|
- fix: move "eslint-plugin-compat" to devDependencies - 5e9d17f (#1589)
|
||||||
|
|
||||||
|
Compatibility notes:
|
||||||
|
- No compatibility issues are to be expected
|
||||||
|
|
||||||
|
|
||||||
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.5.0...v4.5.1)
|
||||||
|
|
||||||
|
## v4.5.0 - October 28th, 2019
|
||||||
|
Features / Improvements
|
||||||
|
- Add method Handlebars.parseWithoutProcessing (#1584) - 62ed3c2
|
||||||
|
- add guard to if & unless helpers (#1549)
|
||||||
|
- show source location for the strict lookup exceptions - feb60f8
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
- Use objects for hash value tracking - 7fcf9d2
|
||||||
|
|
||||||
|
Chore:
|
||||||
|
- Resolve deprecation warning message from eslint while running eslint (#1586) - 7052e88
|
||||||
|
- chore: add eslint-plugin-compat and eslint-plugin-es5 - 088e618
|
||||||
|
|
||||||
|
Compatibility notes:
|
||||||
|
- No compatibility issues are to be expected
|
||||||
|
|
||||||
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.4.5...v4.5.0)
|
||||||
|
|
||||||
|
## v4.4.5 - October 20th, 2019
|
||||||
|
Bugfixes:
|
||||||
|
|
||||||
|
- Contents of raw-blocks must be matched with non-eager regex-matching - 8d5530e, #1579
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.4.4...v4.4.5)
|
||||||
|
|
||||||
|
## v4.4.4 - October 20th, 2019
|
||||||
|
Bugfixes:
|
||||||
|
- fix: prevent zero length tokens in raw-blocks (#1577, #1578) - f1752fe
|
||||||
|
|
||||||
|
Chore:
|
||||||
|
- chore: link to s3 bucket with https, add "npm ci" to build instructions - 0b593bf
|
||||||
|
|
||||||
|
Compatibility notes:
|
||||||
|
- no compatibility issues are expected
|
||||||
|
|
||||||
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.4.3...v4.4.4)
|
||||||
|
|
||||||
|
## v4.4.3 - October 8th, 2019
|
||||||
|
Bugfixes
|
||||||
|
|
||||||
|
Typings:
|
||||||
|
- add missing type fields to AST typings and add tests for them - 0440af2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.4.2...v4.4.3)
|
||||||
|
|
||||||
|
## v4.4.2 - October 2nd, 2019
|
||||||
|
- chore: fix grunt-saucelabs dependency - b7eada0
|
||||||
|
|
||||||
|
|
||||||
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.4.1...v4.4.2)
|
||||||
|
|
||||||
|
## v4.4.1 - October 2nd, 2019
|
||||||
|
- [#1562](https://github.com/wycats/handlebars.js/issues/1562) - Error message for syntax error missing location in 4.2.1+
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.4.0...v4.4.1)
|
||||||
|
|
||||||
|
## v4.4.0 - September 29th, 2019
|
||||||
|
- Added support for iterable objects in {{#each}} helper (#1557) - cf7545e
|
||||||
|
|
||||||
|
|
||||||
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.3.4...v4.4.0)
|
||||||
|
|
||||||
|
## v4.3.4 - September 28th, 2019
|
||||||
|
- fix: harden "propertyIsEnumerable"-check - ff4d827
|
||||||
|
|
||||||
|
Compatibility notes:
|
||||||
|
- No incompatibilities are known.
|
||||||
|
|
||||||
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.3.3...v4.3.4)
|
||||||
|
|
||||||
|
## v4.3.3 - September 27th, 2019
|
||||||
|
- fix test case for browsers that do not support __defineGetter__ - 8742bde
|
||||||
|
|
||||||
|
|
||||||
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.3.2...v4.3.3)
|
||||||
|
|
||||||
|
## v4.3.2 - September 26th, 2019
|
||||||
|
- Use Object.prototype.propertyIsEnumerable to check for constructors - 213c0bb, #1563
|
||||||
|
|
||||||
|
Compatibility notes:
|
||||||
|
- There are no breaking changes
|
||||||
|
|
||||||
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.3.1...v4.3.2)
|
||||||
|
|
||||||
|
## v4.3.1 - September 25th, 2019
|
||||||
|
Fixes:
|
||||||
|
|
||||||
|
- do not break on precompiled templates from Handlebars >=4.0.0 <4.3.0 - 1266838, #1561
|
||||||
|
- Ensure allowCallsToHelperMissing runtime option is optional in typings - 93444c5, 64ecb9e, #1560
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.3.0...v4.3.1)
|
||||||
|
|
||||||
|
## v4.3.0 - September 24th, 2019
|
||||||
|
Fixes:
|
||||||
|
|
||||||
|
- Security: Disallow calling "helperMissing" and "blockHelperMissing" directly - 2078c72
|
||||||
|
- Disallow calling "helperMissing" and "blockHelperMissing" directly - 2078c72
|
||||||
|
|
||||||
|
Features:
|
||||||
|
|
||||||
|
- Add new runtime option `allowCallsToHelperMissing` to allow calling `blockHelperMissing` and `helperMissing`.
|
||||||
|
|
||||||
|
Breaking changes:
|
||||||
|
|
||||||
|
Compatibility notes:
|
||||||
|
- Compiler revision increased - 06b7224
|
||||||
|
- This means that template compiled with versions prior to 4.3.0 will not work with runtimes >= 4.3.0
|
||||||
|
The increase was done because the "helperMissing" and "blockHelperMissing" are now moved from the helpers
|
||||||
|
to the internal "container.hooks" object, so old templates will not be able to call them anymore. We suggest
|
||||||
|
that you always recompile your templates with the latest compiler in your build pipelines.
|
||||||
|
|
||||||
|
- Disallow calling "helperMissing" and "blockHelperMissing" directly - 2078c72
|
||||||
|
- Calling "helperMissing" and "blockHelperMissing" directly from a template (like in `{{blockHelperMissing}}` was
|
||||||
|
never intended and was part of the exploits that have been revealed early in 2019
|
||||||
|
(see https://github.com/wycats/handlebars.js/issues/1495). *It is also part of a new exploit that
|
||||||
|
is not captured by the earlier fix.* In order to harden Handlebars against such exploits, calling thos helpers
|
||||||
|
is now not possible anymore. *Overriding* those helpers is still possible.
|
||||||
|
- If you really need this behavior, you can set the runtime option `allowCallsToHelperMissing` to `true` and the
|
||||||
|
calls will again be possible
|
||||||
|
|
||||||
|
Both bullet points imly that Handlebars is not 100% percent compatible to 4.2.0, despite the minor version bump.
|
||||||
|
|
||||||
|
We consider it more important to resolve a major security issue than to maintain 100% compatibility.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.2.1...v4.3.0)
|
||||||
|
|
||||||
|
## v4.2.1 - September 20th, 2019
|
||||||
|
Bugfixes:
|
||||||
|
|
||||||
|
- The "browser" property in the package.json has been updated to use the common-js builds instead of the minified UMD - c55a7be, #1553
|
||||||
|
|
||||||
|
Compatibility notes:
|
||||||
|
- No compatibility issues should arise
|
||||||
|
|
||||||
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.2.0...v4.2.1)
|
||||||
|
|
||||||
|
## v4.2.0 - September 3rd, 2019
|
||||||
|
Chore/Test:
|
||||||
|
- Use custom `grunt-saucelab` with current sauce-connect proxy - f119497
|
||||||
|
- Add framework for various integration tests - f9cce4d
|
||||||
|
- Add integration test for webpack - a57b682
|
||||||
|
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
- [#1544](https://github.com/wycats/handlebars.js/issues/1544) - Typescript types: `knownHelpers` doesnt allow for custom helpers ([@NickCis](https://api.github.com/users/NickCis))
|
||||||
|
- [#1534](https://github.com/wycats/handlebars.js/pull/1534) - Add typings for "Handlebars.VM.resolvePartial ([@AndrewLeedham](https://api.github.com/users/AndrewLeedham))
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- [#1540](https://github.com/wycats/handlebars.js/pull/1540) - added "browser"-property to package.json, resolves #1102 ([@ouijan](https://api.github.com/users/ouijan))
|
||||||
|
|
||||||
|
Compatibility notes:
|
||||||
|
- The new "browser"-property should not break anything, but you can never be sure. The integration test for webpack
|
||||||
|
shows that it works, but if it doesn't please open an issue.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.1.2-0...v4.2.0)
|
||||||
|
|
||||||
|
## v4.1.2-0 - August 25th, 2019
|
||||||
|
[#1540](https://github.com/wycats/handlebars.js/pull/1540) - added browser to package.json, resolves #1102 ([@ouijan](https://api.github.com/users/ouijan))
|
||||||
|
|
||||||
|
Compatibility notes:
|
||||||
|
- We are not sure if imports via webpack are still working, which is why this release is a pre-release
|
||||||
|
|
||||||
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.1.2...v4.1.2-0)
|
||||||
|
|
||||||
|
## v4.1.2 - April 13th, 2019
|
||||||
|
Chore/Test:
|
||||||
|
- [#1515](https://github.com/wycats/handlebars.js/pull/1515) - Port over linting and test for typings ([@zimmi88](https://api.github.com/users/zimmi88))
|
||||||
|
- chore: add missing typescript dependency, add package-lock.json - 594f1e3
|
||||||
|
- test: remove safari from saucelabs - 871accc
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
- fix: prevent RCE through the "lookup"-helper - cd38583
|
||||||
|
|
||||||
|
Compatibility notes:
|
||||||
|
|
||||||
|
Access to the constructor of a class thought `{{lookup obj "constructor" }}` is now prohibited. This closes
|
||||||
|
a leak that only half closed in versions 4.0.13 and 4.1.0, but it is a slight incompatibility.
|
||||||
|
|
||||||
|
This kind of access is not the intended use of Handlebars and leads to the vulnerability described
|
||||||
|
in #1495. We will **not** increase the major version, because such use is not intended or documented,
|
||||||
|
and because of the potential impact of the issue (we fear that most people won't use a new major version
|
||||||
|
and the issue may not be resolved on many systems).
|
||||||
|
|
||||||
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.1.1...v4.1.2)
|
||||||
|
|
||||||
|
## v4.1.1 - March 16th, 2019
|
||||||
|
Bugfixes:
|
||||||
|
- fix: add "runtime.d.ts" to allow "require('handlebars/runtime')" in TypeScript - 5cedd62
|
||||||
|
|
||||||
|
Refactorings:
|
||||||
|
- replace "async" with "neo-async" - 048f2ce
|
||||||
|
- use "substring"-function instead of "substr" - 445ae12
|
||||||
|
|
||||||
|
Compatibility notes:
|
||||||
|
- This is a bugfix release. There are no breaking change and no new features.
|
||||||
|
|
||||||
|
|
||||||
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.1.0...v4.1.1)
|
||||||
|
|
||||||
|
## v4.1.0 - February 7th, 2019
|
||||||
|
New Features
|
||||||
|
|
||||||
|
- import TypeScript typings - 27ac1ee
|
||||||
|
|
||||||
|
Security fixes:
|
||||||
|
|
||||||
|
- disallow access to the constructor in templates to prevent RCE - 42841c4, #1495
|
||||||
|
|
||||||
|
Housekeeping
|
||||||
|
|
||||||
|
- chore: fix components/handlebars package.json and auto-update on release - bacd473
|
||||||
|
- chore: Use node 10 to build handlebars - 78dd89c
|
||||||
|
- chore/doc: Add more release docs - 6b87c21
|
||||||
|
|
||||||
|
Compatibility notes:
|
||||||
|
|
||||||
|
Access to class constructors (i.e. `({}).constructor`) is now prohibited to prevent
|
||||||
|
Remote Code Execution. This means that following construct will no work anymore:
|
||||||
|
|
||||||
|
```
|
||||||
|
class SomeClass {
|
||||||
|
}
|
||||||
|
|
||||||
|
SomeClass.staticProperty = 'static'
|
||||||
|
|
||||||
|
var template = Handlebars.compile('{{constructor.staticProperty}}');
|
||||||
|
document.getElementById('output').innerHTML = template(new SomeClass());
|
||||||
|
// expected: 'static', but now this is empty.
|
||||||
|
```
|
||||||
|
|
||||||
|
This kind of access is not the intended use of Handlebars and leads to the vulnerability described in #1495. We will **not** increase the major version, because such use is not intended or documented, and because of the potential impact of the issue (we fear that most people won't use a new major version and the issue may not be resolved on many systems).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.0.12...v4.1.0)
|
||||||
|
|
||||||
|
## v4.0.12 - September 4th, 2018
|
||||||
|
New features:
|
||||||
|
|
||||||
|
- none
|
||||||
|
|
||||||
|
Various dependency updates
|
||||||
|
|
||||||
|
- [#1464](https://github.com/wycats/handlebars.js/pull/1464) - Bump versions of grunt-plugins to 1.x
|
||||||
|
- [#1398](https://github.com/wycats/handlebars.js/pull/1398) - Chore: updated various dev dependencies
|
||||||
|
- upgrade uglify-js - d3d3942
|
||||||
|
- Update grunt-eslint to 20.1.0 - 7729aa9
|
||||||
|
- Update dependencies "async" to 2.5.0 and "source-map" to 0.6.1 (73d5637)
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
|
||||||
|
- [components/handlebars.js#24](https://github.com/components/handlebars.js#24) Add package.json to components shim
|
||||||
|
- Updated `source-map`-package should work better with `rollup`[#1463](https://github.com/wycats/handlebars.js/issues/1463)
|
||||||
|
|
||||||
|
Removed obsolete code:
|
||||||
|
|
||||||
|
- unnecessary check - 0ddff8b
|
||||||
|
- Use `files` field - 69c6ca5
|
||||||
|
- Update jsfiddle to 4.0.11 - 8947dd0
|
||||||
|
|
||||||
|
Compatibility notes:
|
||||||
|
- No compatibility issues are to be expected
|
||||||
|
|
||||||
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.0.11...v4.0.12)
|
||||||
|
|
||||||
|
## 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://github.com/Turbo87))
|
||||||
|
- [#1233](https://github.com/wycats/handlebars.js/issues/1233) - Unable to build under windows - error at test:bin task ([@blikblum](https://github.com/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/wycats/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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.0.9...v4.0.10)
|
||||||
|
|
||||||
## v4.0.9 - May 21st, 2017
|
## v4.0.9 - May 21st, 2017
|
||||||
|
|
||||||
@@ -19,7 +377,7 @@
|
|||||||
Compatibility notes:
|
Compatibility notes:
|
||||||
- No compatibility issues are expected.
|
- No compatibility issues are expected.
|
||||||
|
|
||||||
[Commits](https://github.com/nknapp/handlebars.js/compare/v4.0.8...v4.0.9)
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.0.8...v4.0.9)
|
||||||
|
|
||||||
## v4.0.8 - May 2nd, 2017
|
## v4.0.8 - May 2nd, 2017
|
||||||
- [#1341](https://github.com/wycats/handlebars.js/issues/1341) [#1342](https://github.com/wycats/handlebars.js/issues/1342) Allow partial-blocks to be executed without "options" ([@nknapp](https://github.com/nknapp)) - a00c598
|
- [#1341](https://github.com/wycats/handlebars.js/issues/1341) [#1342](https://github.com/wycats/handlebars.js/issues/1342) Allow partial-blocks to be executed without "options" ([@nknapp](https://github.com/nknapp)) - a00c598
|
||||||
@@ -27,7 +385,7 @@ Compatibility notes:
|
|||||||
Compatibility notes:
|
Compatibility notes:
|
||||||
- No breaking changes
|
- No breaking changes
|
||||||
|
|
||||||
[Commits](https://github.com/nknapp/handlebars.js/compare/v4.0.7...v4.0.8)
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.0.7...v4.0.8)
|
||||||
|
|
||||||
## v4.0.7 - April 29th, 2017
|
## v4.0.7 - April 29th, 2017
|
||||||
- [#1319](https://github.com/wycats/handlebars.js/issues/1319): Fix context-stack when calling block-helpers on null values ([@nknapp](https://github.com/nknapp)) - c8f4b57
|
- [#1319](https://github.com/wycats/handlebars.js/issues/1319): Fix context-stack when calling block-helpers on null values ([@nknapp](https://github.com/nknapp)) - c8f4b57
|
||||||
@@ -63,8 +421,8 @@ Compatibility notes:
|
|||||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.0.5...v4.0.6)
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.0.5...v4.0.6)
|
||||||
|
|
||||||
## v4.0.5 - November 19th, 2015
|
## 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))
|
- [#1132](https://github.com/wycats/handlebars.js/pull/1132) - Update uglify-js to avoid vulnerability ([@plynchnlm](https://github.com/plynchnlm))
|
||||||
- [#1129](https://github.com/wycats/handlebars.js/issues/1129) - Minified lib returns an empty string ([@bricss](https://api.github.com/users/bricss))
|
- [#1129](https://github.com/wycats/handlebars.js/issues/1129) - Minified lib returns an empty string ([@bricss](https://github.com/bricss))
|
||||||
- Return current handlebars instance from noConflict - 685cf92
|
- Return current handlebars instance from noConflict - 685cf92
|
||||||
- Add webpack to dev dependency to support npm 3 - 7a6c228
|
- Add webpack to dev dependency to support npm 3 - 7a6c228
|
||||||
- Further relax uglify dependency - 0a3b3c2
|
- Further relax uglify dependency - 0a3b3c2
|
||||||
@@ -75,17 +433,17 @@ Compatibility notes:
|
|||||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.0.4...v4.0.5)
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.0.4...v4.0.5)
|
||||||
|
|
||||||
## v4.0.4 - October 29th, 2015
|
## 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))
|
- [#1121](https://github.com/wycats/handlebars.js/pull/1121) - Include partial name in 'undefined partial' exception message ([@shinypb](https://github.com/shinypb))
|
||||||
- [#1125](https://github.com/wycats/handlebars.js/pull/1125) - Add promised-handlebars to "in-the-wild"-list ([@nknapp](https://api.github.com/users/nknapp))
|
- [#1125](https://github.com/wycats/handlebars.js/pull/1125) - Add promised-handlebars to "in-the-wild"-list ([@nknapp](https://github.com/nknapp))
|
||||||
|
|
||||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.0.3...v4.0.4)
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.0.3...v4.0.4)
|
||||||
|
|
||||||
## v4.0.3 - September 23rd, 2015
|
## v4.0.3 - September 23rd, 2015
|
||||||
- [#1099](https://github.com/wycats/handlebars.js/issues/1099) - @partial-block is overridden ([@btmorex](https://api.github.com/users/btmorex))
|
- [#1099](https://github.com/wycats/handlebars.js/issues/1099) - @partial-block is overridden ([@btmorex](https://github.com/btmorex))
|
||||||
- [#1093](https://github.com/wycats/handlebars.js/issues/1093) - #each skips iteration on undefined values ([@florianpilz](https://api.github.com/users/florianpilz))
|
- [#1093](https://github.com/wycats/handlebars.js/issues/1093) - #each skips iteration on undefined values ([@florianpilz](https://github.com/florianpilz))
|
||||||
- [#1092](https://github.com/wycats/handlebars.js/issues/1092) - Square braces in key name ([@distantnative](https://api.github.com/users/distantnative))
|
- [#1092](https://github.com/wycats/handlebars.js/issues/1092) - Square braces in key name ([@distantnative](https://github.com/distantnative))
|
||||||
- [#1091](https://github.com/wycats/handlebars.js/pull/1091) - fix typo in release notes ([@nikolas](https://api.github.com/users/nikolas))
|
- [#1091](https://github.com/wycats/handlebars.js/pull/1091) - fix typo in release notes ([@nikolas](https://github.com/nikolas))
|
||||||
- [#1090](https://github.com/wycats/handlebars.js/pull/1090) - grammar fixes in 4.0.0 release notes ([@nikolas](https://api.github.com/users/nikolas))
|
- [#1090](https://github.com/wycats/handlebars.js/pull/1090) - grammar fixes in 4.0.0 release notes ([@nikolas](https://github.com/nikolas))
|
||||||
|
|
||||||
Compatibility notes:
|
Compatibility notes:
|
||||||
- `each` iteration with `undefined` values has been restored to the 3.0 behaviors. Helper calls with undefined context values will now execute against an arbitrary empty object to avoid executing against global object in non-strict mode.
|
- `each` iteration with `undefined` values has been restored to the 3.0 behaviors. Helper calls with undefined context values will now execute against an arbitrary empty object to avoid executing against global object in non-strict mode.
|
||||||
@@ -94,7 +452,7 @@ Compatibility notes:
|
|||||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.0.2...v4.0.3)
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.0.2...v4.0.3)
|
||||||
|
|
||||||
## v4.0.2 - September 4th, 2015
|
## v4.0.2 - September 4th, 2015
|
||||||
- [#1089](https://github.com/wycats/handlebars.js/issues/1089) - "Failover content" not working in multiple levels of inline partials ([@michaellopez](https://api.github.com/users/michaellopez))
|
- [#1089](https://github.com/wycats/handlebars.js/issues/1089) - "Failover content" not working in multiple levels of inline partials ([@michaellopez](https://github.com/michaellopez))
|
||||||
|
|
||||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.0.1...v4.0.2)
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.0.1...v4.0.2)
|
||||||
|
|
||||||
@@ -104,17 +462,17 @@ Compatibility notes:
|
|||||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.0.0...v4.0.1)
|
[Commits](https://github.com/wycats/handlebars.js/compare/v4.0.0...v4.0.1)
|
||||||
|
|
||||||
## v4.0.0 - September 1st, 2015
|
## v4.0.0 - September 1st, 2015
|
||||||
- [#1082](https://github.com/wycats/handlebars.js/pull/1082) - Decorators and Inline Partials ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#1082](https://github.com/wycats/handlebars.js/pull/1082) - Decorators and Inline Partials ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#1076](https://github.com/wycats/handlebars.js/pull/1076) - Implement partial blocks ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#1076](https://github.com/wycats/handlebars.js/pull/1076) - Implement partial blocks ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#1087](https://github.com/wycats/handlebars.js/pull/1087) - Fix #each when last object entry has empty key ([@denniskuczynski](https://api.github.com/users/denniskuczynski))
|
- [#1087](https://github.com/wycats/handlebars.js/pull/1087) - Fix #each when last object entry has empty key ([@denniskuczynski](https://github.com/denniskuczynski))
|
||||||
- [#1084](https://github.com/wycats/handlebars.js/pull/1084) - Bump uglify version to fix vulnerability ([@John-Steidley](https://api.github.com/users/John-Steidley))
|
- [#1084](https://github.com/wycats/handlebars.js/pull/1084) - Bump uglify version to fix vulnerability ([@John-Steidley](https://github.com/John-Steidley))
|
||||||
- [#1068](https://github.com/wycats/handlebars.js/pull/1068) - Fix typo ([@0xack13](https://api.github.com/users/0xack13))
|
- [#1068](https://github.com/wycats/handlebars.js/pull/1068) - Fix typo ([@0xack13](https://github.com/0xack13))
|
||||||
- [#1060](https://github.com/wycats/handlebars.js/pull/1060) - #1056 Fixed grammar for nested raw blocks ([@ericbn](https://api.github.com/users/ericbn))
|
- [#1060](https://github.com/wycats/handlebars.js/pull/1060) - #1056 Fixed grammar for nested raw blocks ([@ericbn](https://github.com/ericbn))
|
||||||
- [#1052](https://github.com/wycats/handlebars.js/pull/1052) - Updated year in License ([@maqnouch](https://api.github.com/users/maqnouch))
|
- [#1052](https://github.com/wycats/handlebars.js/pull/1052) - Updated year in License ([@maqnouch](https://github.com/maqnouch))
|
||||||
- [#1037](https://github.com/wycats/handlebars.js/pull/1037) - Fix minor typos in README ([@tomxtobin](https://api.github.com/users/tomxtobin))
|
- [#1037](https://github.com/wycats/handlebars.js/pull/1037) - Fix minor typos in README ([@tomxtobin](https://github.com/tomxtobin))
|
||||||
- [#1032](https://github.com/wycats/handlebars.js/issues/1032) - Is it possible to render a partial without the parent scope? ([@aputinski](https://api.github.com/users/aputinski))
|
- [#1032](https://github.com/wycats/handlebars.js/issues/1032) - Is it possible to render a partial without the parent scope? ([@aputinski](https://github.com/aputinski))
|
||||||
- [#1019](https://github.com/wycats/handlebars.js/pull/1019) - Fixes typo in tests ([@aymerick](https://api.github.com/users/aymerick))
|
- [#1019](https://github.com/wycats/handlebars.js/pull/1019) - Fixes typo in tests ([@aymerick](https://github.com/aymerick))
|
||||||
- [#1016](https://github.com/wycats/handlebars.js/issues/1016) - Version mis-match ([@mayankdedhia](https://api.github.com/users/mayankdedhia))
|
- [#1016](https://github.com/wycats/handlebars.js/issues/1016) - Version mis-match ([@mayankdedhia](https://github.com/mayankdedhia))
|
||||||
- [#1023](https://github.com/wycats/handlebars.js/issues/1023) - is it possible for nested custom helpers to communicate between each other?
|
- [#1023](https://github.com/wycats/handlebars.js/issues/1023) - is it possible for nested custom helpers to communicate between each other?
|
||||||
- [#893](https://github.com/wycats/handlebars.js/issues/893) - [Proposal] Section blocks.
|
- [#893](https://github.com/wycats/handlebars.js/issues/893) - [Proposal] Section blocks.
|
||||||
- [#792](https://github.com/wycats/handlebars.js/issues/792) - feature request: inline partial definitions
|
- [#792](https://github.com/wycats/handlebars.js/issues/792) - feature request: inline partial definitions
|
||||||
@@ -147,55 +505,55 @@ Compatibility notes:
|
|||||||
[Commits](https://github.com/wycats/handlebars.js/compare/v3.0.3...v4.0.0)
|
[Commits](https://github.com/wycats/handlebars.js/compare/v3.0.3...v4.0.0)
|
||||||
|
|
||||||
## v3.0.3 - April 28th, 2015
|
## v3.0.3 - April 28th, 2015
|
||||||
- [#1004](https://github.com/wycats/handlebars.js/issues/1004) - Latest version breaks with RequireJS (global is undefined) ([@boskee](https://api.github.com/users/boskee))
|
- [#1004](https://github.com/wycats/handlebars.js/issues/1004) - Latest version breaks with RequireJS (global is undefined) ([@boskee](https://github.com/boskee))
|
||||||
|
|
||||||
[Commits](https://github.com/wycats/handlebars.js/compare/v3.0.2...v3.0.3)
|
[Commits](https://github.com/wycats/handlebars.js/compare/v3.0.2...v3.0.3)
|
||||||
|
|
||||||
## v3.0.2 - April 20th, 2015
|
## v3.0.2 - April 20th, 2015
|
||||||
- [#998](https://github.com/wycats/handlebars.js/pull/998) - Add full support for es6 ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#998](https://github.com/wycats/handlebars.js/pull/998) - Add full support for es6 ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#994](https://github.com/wycats/handlebars.js/issues/994) - Access Handlebars.Visitor in browser ([@tamlyn](https://api.github.com/users/tamlyn))
|
- [#994](https://github.com/wycats/handlebars.js/issues/994) - Access Handlebars.Visitor in browser ([@tamlyn](https://github.com/tamlyn))
|
||||||
- [#990](https://github.com/wycats/handlebars.js/issues/990) - Allow passing null/undefined literals subexpressions ([@blimmer](https://api.github.com/users/blimmer))
|
- [#990](https://github.com/wycats/handlebars.js/issues/990) - Allow passing null/undefined literals subexpressions ([@blimmer](https://github.com/blimmer))
|
||||||
- [#989](https://github.com/wycats/handlebars.js/issues/989) - Source-map error with requirejs ([@SteppeEagle](https://api.github.com/users/SteppeEagle))
|
- [#989](https://github.com/wycats/handlebars.js/issues/989) - Source-map error with requirejs ([@SteppeEagle](https://github.com/SteppeEagle))
|
||||||
- [#967](https://github.com/wycats/handlebars.js/issues/967) - can't access "this" property ([@75lb](https://api.github.com/users/75lb))
|
- [#967](https://github.com/wycats/handlebars.js/issues/967) - can't access "this" property ([@75lb](https://github.com/75lb))
|
||||||
- Use captureStackTrace for error handler - a009a97
|
- Use captureStackTrace for error handler - a009a97
|
||||||
- Ignore branches tested without coverage monitoring - 37a664b
|
- Ignore branches tested without coverage monitoring - 37a664b
|
||||||
|
|
||||||
[Commits](https://github.com/wycats/handlebars.js/compare/v3.0.1...v3.0.2)
|
[Commits](https://github.com/wycats/handlebars.js/compare/v3.0.1...v3.0.2)
|
||||||
|
|
||||||
## v3.0.1 - March 24th, 2015
|
## v3.0.1 - March 24th, 2015
|
||||||
- [#984](https://github.com/wycats/handlebars.js/pull/984) - Adding documentation for passing arguments into partials ([@johneke](https://api.github.com/users/johneke))
|
- [#984](https://github.com/wycats/handlebars.js/pull/984) - Adding documentation for passing arguments into partials ([@johneke](https://github.com/johneke))
|
||||||
- [#973](https://github.com/wycats/handlebars.js/issues/973) - version 3 is slower than version 2 ([@elover](https://api.github.com/users/elover))
|
- [#973](https://github.com/wycats/handlebars.js/issues/973) - version 3 is slower than version 2 ([@elover](https://github.com/elover))
|
||||||
- [#966](https://github.com/wycats/handlebars.js/issues/966) - "handlebars --version" does not work with v3.0.0 ([@abloomston](https://api.github.com/users/abloomston))
|
- [#966](https://github.com/wycats/handlebars.js/issues/966) - "handlebars --version" does not work with v3.0.0 ([@abloomston](https://github.com/abloomston))
|
||||||
- [#964](https://github.com/wycats/handlebars.js/pull/964) - default is a reserved word ([@grassick](https://api.github.com/users/grassick))
|
- [#964](https://github.com/wycats/handlebars.js/pull/964) - default is a reserved word ([@grassick](https://github.com/grassick))
|
||||||
- [#962](https://github.com/wycats/handlebars.js/pull/962) - Add dashbars' link on README. ([@pismute](https://api.github.com/users/pismute))
|
- [#962](https://github.com/wycats/handlebars.js/pull/962) - Add dashbars' link on README. ([@pismute](https://github.com/pismute))
|
||||||
|
|
||||||
[Commits](https://github.com/wycats/handlebars.js/compare/v3.0.0...v3.0.1)
|
[Commits](https://github.com/wycats/handlebars.js/compare/v3.0.0...v3.0.1)
|
||||||
|
|
||||||
## v3.0.0 - February 10th, 2015
|
## v3.0.0 - February 10th, 2015
|
||||||
- [#941](https://github.com/wycats/handlebars.js/pull/941) - Add support for dynamic partial names ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#941](https://github.com/wycats/handlebars.js/pull/941) - Add support for dynamic partial names ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#940](https://github.com/wycats/handlebars.js/pull/940) - Add missing reserved words so compiler knows to use array syntax: ([@mattflaschen](https://api.github.com/users/mattflaschen))
|
- [#940](https://github.com/wycats/handlebars.js/pull/940) - Add missing reserved words so compiler knows to use array syntax: ([@mattflaschen](https://github.com/mattflaschen))
|
||||||
- [#938](https://github.com/wycats/handlebars.js/pull/938) - Fix example using #with helper ([@diwo](https://api.github.com/users/diwo))
|
- [#938](https://github.com/wycats/handlebars.js/pull/938) - Fix example using #with helper ([@diwo](https://github.com/diwo))
|
||||||
- [#930](https://github.com/wycats/handlebars.js/pull/930) - Add parent tracking and mutation to AST visitors ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#930](https://github.com/wycats/handlebars.js/pull/930) - Add parent tracking and mutation to AST visitors ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#926](https://github.com/wycats/handlebars.js/issues/926) - Depthed lookups fail when program duplicator runs ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#926](https://github.com/wycats/handlebars.js/issues/926) - Depthed lookups fail when program duplicator runs ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#918](https://github.com/wycats/handlebars.js/pull/918) - Add instructions for 'spec/mustache' to CONTRIBUTING.md, fix a few typos ([@oneeman](https://api.github.com/users/oneeman))
|
- [#918](https://github.com/wycats/handlebars.js/pull/918) - Add instructions for 'spec/mustache' to CONTRIBUTING.md, fix a few typos ([@oneeman](https://github.com/oneeman))
|
||||||
- [#915](https://github.com/wycats/handlebars.js/pull/915) - Ast update ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#915](https://github.com/wycats/handlebars.js/pull/915) - Ast update ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#910](https://github.com/wycats/handlebars.js/issues/910) - Different behavior of {{@last}} when {{#each}} in {{#each}} ([@zordius](https://api.github.com/users/zordius))
|
- [#910](https://github.com/wycats/handlebars.js/issues/910) - Different behavior of {{@last}} when {{#each}} in {{#each}} ([@zordius](https://github.com/zordius))
|
||||||
- [#907](https://github.com/wycats/handlebars.js/issues/907) - Implement named helper variable references ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#907](https://github.com/wycats/handlebars.js/issues/907) - Implement named helper variable references ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#906](https://github.com/wycats/handlebars.js/pull/906) - Add parser support for block params ([@mmun](https://api.github.com/users/mmun))
|
- [#906](https://github.com/wycats/handlebars.js/pull/906) - Add parser support for block params ([@mmun](https://github.com/mmun))
|
||||||
- [#903](https://github.com/wycats/handlebars.js/issues/903) - Only provide aliases for multiple use calls ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#903](https://github.com/wycats/handlebars.js/issues/903) - Only provide aliases for multiple use calls ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#902](https://github.com/wycats/handlebars.js/pull/902) - Generate Source Maps ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#902](https://github.com/wycats/handlebars.js/pull/902) - Generate Source Maps ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#901](https://github.com/wycats/handlebars.js/issues/901) - Still escapes with noEscape enabled on isolated Handlebars environment ([@zedknight](https://api.github.com/users/zedknight))
|
- [#901](https://github.com/wycats/handlebars.js/issues/901) - Still escapes with noEscape enabled on isolated Handlebars environment ([@zedknight](https://github.com/zedknight))
|
||||||
- [#896](https://github.com/wycats/handlebars.js/pull/896) - Simplify BlockNode by removing intermediate MustacheNode ([@mmun](https://api.github.com/users/mmun))
|
- [#896](https://github.com/wycats/handlebars.js/pull/896) - Simplify BlockNode by removing intermediate MustacheNode ([@mmun](https://github.com/mmun))
|
||||||
- [#892](https://github.com/wycats/handlebars.js/pull/892) - Implement parser for else chaining of helpers ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#892](https://github.com/wycats/handlebars.js/pull/892) - Implement parser for else chaining of helpers ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#889](https://github.com/wycats/handlebars.js/issues/889) - Consider extensible parser API ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#889](https://github.com/wycats/handlebars.js/issues/889) - Consider extensible parser API ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#887](https://github.com/wycats/handlebars.js/issues/887) - Handlebars.noConflict() option? ([@bradvogel](https://api.github.com/users/bradvogel))
|
- [#887](https://github.com/wycats/handlebars.js/issues/887) - Handlebars.noConflict() option? ([@bradvogel](https://github.com/bradvogel))
|
||||||
- [#886](https://github.com/wycats/handlebars.js/issues/886) - Add SafeString to context (or use duck-typing) ([@dominicbarnes](https://api.github.com/users/dominicbarnes))
|
- [#886](https://github.com/wycats/handlebars.js/issues/886) - Add SafeString to context (or use duck-typing) ([@dominicbarnes](https://github.com/dominicbarnes))
|
||||||
- [#870](https://github.com/wycats/handlebars.js/pull/870) - Registering undefined partial throws exception. ([@max-b](https://api.github.com/users/max-b))
|
- [#870](https://github.com/wycats/handlebars.js/pull/870) - Registering undefined partial throws exception. ([@max-b](https://github.com/max-b))
|
||||||
- [#866](https://github.com/wycats/handlebars.js/issues/866) - comments don't respect whitespace control ([@75lb](https://api.github.com/users/75lb))
|
- [#866](https://github.com/wycats/handlebars.js/issues/866) - comments don't respect whitespace control ([@75lb](https://github.com/75lb))
|
||||||
- [#863](https://github.com/wycats/handlebars.js/pull/863) - + jsDelivr CDN info ([@tomByrer](https://api.github.com/users/tomByrer))
|
- [#863](https://github.com/wycats/handlebars.js/pull/863) - + jsDelivr CDN info ([@tomByrer](https://github.com/tomByrer))
|
||||||
- [#858](https://github.com/wycats/handlebars.js/issues/858) - Disable new default auto-indent at included partials ([@majodev](https://api.github.com/users/majodev))
|
- [#858](https://github.com/wycats/handlebars.js/issues/858) - Disable new default auto-indent at included partials ([@majodev](https://github.com/majodev))
|
||||||
- [#856](https://github.com/wycats/handlebars.js/pull/856) - jspm compatibility ([@MajorBreakfast](https://api.github.com/users/MajorBreakfast))
|
- [#856](https://github.com/wycats/handlebars.js/pull/856) - jspm compatibility ([@MajorBreakfast](https://github.com/MajorBreakfast))
|
||||||
- [#805](https://github.com/wycats/handlebars.js/issues/805) - Request: "strict" lookups ([@nzakas](https://api.github.com/users/nzakas))
|
- [#805](https://github.com/wycats/handlebars.js/issues/805) - Request: "strict" lookups ([@nzakas](https://github.com/nzakas))
|
||||||
|
|
||||||
- Export the default object for handlebars/runtime - 5594416
|
- Export the default object for handlebars/runtime - 5594416
|
||||||
- Lookup partials when undefined - 617dd57
|
- Lookup partials when undefined - 617dd57
|
||||||
@@ -229,21 +587,21 @@ New Features:
|
|||||||
[Commits](https://github.com/wycats/handlebars.js/compare/v2.0.0-beta.1...v2.0.0)
|
[Commits](https://github.com/wycats/handlebars.js/compare/v2.0.0-beta.1...v2.0.0)
|
||||||
|
|
||||||
## v2.0.0-beta.1 - August 26th, 2014
|
## v2.0.0-beta.1 - August 26th, 2014
|
||||||
- [#787](https://github.com/wycats/handlebars.js/pull/787) - Remove whitespace surrounding standalone statements ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#787](https://github.com/wycats/handlebars.js/pull/787) - Remove whitespace surrounding standalone statements ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#827](https://github.com/wycats/handlebars.js/issues/827) - Render false literal as “false” ([@scoot557](https://api.github.com/users/scoot557))
|
- [#827](https://github.com/wycats/handlebars.js/issues/827) - Render false literal as “false” ([@scoot557](https://github.com/scoot557))
|
||||||
- [#767](https://github.com/wycats/handlebars.js/issues/767) - Subexpressions bug with hash and context ([@evensoul](https://api.github.com/users/evensoul))
|
- [#767](https://github.com/wycats/handlebars.js/issues/767) - Subexpressions bug with hash and context ([@evensoul](https://github.com/evensoul))
|
||||||
- Changes to 0/undefined handling
|
- Changes to 0/undefined handling
|
||||||
- [#731](https://github.com/wycats/handlebars.js/pull/731) - Strange behavior for {{#foo}} {{bar}} {{/foo}} when foo is 0 ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#731](https://github.com/wycats/handlebars.js/pull/731) - Strange behavior for {{#foo}} {{bar}} {{/foo}} when foo is 0 ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#820](https://github.com/wycats/handlebars.js/issues/820) - strange behavior for {{foo.bar}} when foo is 0 or null or false ([@zordius](https://api.github.com/users/zordius))
|
- [#820](https://github.com/wycats/handlebars.js/issues/820) - strange behavior for {{foo.bar}} when foo is 0 or null or false ([@zordius](https://github.com/zordius))
|
||||||
- [#837](https://github.com/wycats/handlebars.js/issues/837) - Strange input for custom helper ( foo.bar == false when foo is undefined ) ([@zordius](https://api.github.com/users/zordius))
|
- [#837](https://github.com/wycats/handlebars.js/issues/837) - Strange input for custom helper ( foo.bar == false when foo is undefined ) ([@zordius](https://github.com/zordius))
|
||||||
- [#819](https://github.com/wycats/handlebars.js/pull/819) - Implement recursive field lookup ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#819](https://github.com/wycats/handlebars.js/pull/819) - Implement recursive field lookup ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#764](https://github.com/wycats/handlebars.js/issues/764) - This reference not working for helpers ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#764](https://github.com/wycats/handlebars.js/issues/764) - This reference not working for helpers ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#773](https://github.com/wycats/handlebars.js/issues/773) - Implicit parameters in {{#each}} introduces a peculiarity in helpers calling convention ([@Bertrand](https://api.github.com/users/Bertrand))
|
- [#773](https://github.com/wycats/handlebars.js/issues/773) - Implicit parameters in {{#each}} introduces a peculiarity in helpers calling convention ([@Bertrand](https://github.com/Bertrand))
|
||||||
- [#783](https://github.com/wycats/handlebars.js/issues/783) - helperMissing and consistency for different expression types ([@ErisDS](https://api.github.com/users/ErisDS))
|
- [#783](https://github.com/wycats/handlebars.js/issues/783) - helperMissing and consistency for different expression types ([@ErisDS](https://github.com/ErisDS))
|
||||||
- [#795](https://github.com/wycats/handlebars.js/pull/795) - Turn the precompile script into a wrapper around a module. ([@jwietelmann](https://api.github.com/users/jwietelmann))
|
- [#795](https://github.com/wycats/handlebars.js/pull/795) - Turn the precompile script into a wrapper around a module. ([@jwietelmann](https://github.com/jwietelmann))
|
||||||
- [#823](https://github.com/wycats/handlebars.js/pull/823) - Support inverse sections on the with helper ([@dan-manges](https://api.github.com/users/dan-manges))
|
- [#823](https://github.com/wycats/handlebars.js/pull/823) - Support inverse sections on the with helper ([@dan-manges](https://github.com/dan-manges))
|
||||||
- [#834](https://github.com/wycats/handlebars.js/pull/834) - Refactor blocks, programs and inverses ([@mmun](https://api.github.com/users/mmun))
|
- [#834](https://github.com/wycats/handlebars.js/pull/834) - Refactor blocks, programs and inverses ([@mmun](https://github.com/mmun))
|
||||||
- [#852](https://github.com/wycats/handlebars.js/issues/852) - {{foo~}} space control behavior is different from older version ([@zordius](https://api.github.com/users/zordius))
|
- [#852](https://github.com/wycats/handlebars.js/issues/852) - {{foo~}} space control behavior is different from older version ([@zordius](https://github.com/zordius))
|
||||||
- [#835](https://github.com/wycats/handlebars.js/issues/835) - Templates overwritten if file is loaded twice
|
- [#835](https://github.com/wycats/handlebars.js/issues/835) - Templates overwritten if file is loaded twice
|
||||||
|
|
||||||
- Expose escapeExpression on the root object - 980c38c
|
- Expose escapeExpression on the root object - 980c38c
|
||||||
@@ -258,7 +616,7 @@ Compatibility notes:
|
|||||||
- Lines containing only block statements and whitespace are now removed. This matches the Mustache spec but may cause issues with code that expects whitespace to exist but would not otherwise.
|
- Lines containing only block statements and whitespace are now removed. This matches the Mustache spec but may cause issues with code that expects whitespace to exist but would not otherwise.
|
||||||
- Partials that are standalone will now indent their rendered content
|
- Partials that are standalone will now indent their rendered content
|
||||||
- `AST.ProgramNode`'s signature has changed.
|
- `AST.ProgramNode`'s signature has changed.
|
||||||
- Numerious methods/features removed from psuedo-API classes
|
- Numerious methods/features removed from pseudo-API classes
|
||||||
- `JavaScriptCompiler.register`
|
- `JavaScriptCompiler.register`
|
||||||
- `JavaScriptCompiler.replaceStack` no longer supports non-inline replace
|
- `JavaScriptCompiler.replaceStack` no longer supports non-inline replace
|
||||||
- `Compiler.disassemble`
|
- `Compiler.disassemble`
|
||||||
@@ -277,18 +635,18 @@ Compatibility notes:
|
|||||||
[Commits](https://github.com/wycats/handlebars.js/compare/v2.0.0-alpha.3...v2.0.0-alpha.4)
|
[Commits](https://github.com/wycats/handlebars.js/compare/v2.0.0-alpha.3...v2.0.0-alpha.4)
|
||||||
|
|
||||||
## v2.0.0-alpha.3 - May 19th, 2014
|
## v2.0.0-alpha.3 - May 19th, 2014
|
||||||
- [#797](https://github.com/wycats/handlebars.js/pull/797) - Pass full helper ID to helperMissing when options are provided ([@tomdale](https://api.github.com/users/tomdale))
|
- [#797](https://github.com/wycats/handlebars.js/pull/797) - Pass full helper ID to helperMissing when options are provided ([@tomdale](https://github.com/tomdale))
|
||||||
- [#793](https://github.com/wycats/handlebars.js/pull/793) - Ensure isHelper is coerced to a boolean ([@mmun](https://api.github.com/users/mmun))
|
- [#793](https://github.com/wycats/handlebars.js/pull/793) - Ensure isHelper is coerced to a boolean ([@mmun](https://github.com/mmun))
|
||||||
- Refactor template init logic - 085e5e1
|
- Refactor template init logic - 085e5e1
|
||||||
|
|
||||||
[Commits](https://github.com/wycats/handlebars.js/compare/v2.0.0-alpha.2...v2.0.0-alpha.3)
|
[Commits](https://github.com/wycats/handlebars.js/compare/v2.0.0-alpha.2...v2.0.0-alpha.3)
|
||||||
|
|
||||||
## v2.0.0-alpha.2 - March 6th, 2014
|
## v2.0.0-alpha.2 - March 6th, 2014
|
||||||
- [#756](https://github.com/wycats/handlebars.js/pull/756) - fix bug in IE<=8 (no Array::map), closes #751 ([@jenseng](https://api.github.com/users/jenseng))
|
- [#756](https://github.com/wycats/handlebars.js/pull/756) - fix bug in IE<=8 (no Array::map), closes #751 ([@jenseng](https://github.com/jenseng))
|
||||||
- [#749](https://github.com/wycats/handlebars.js/pull/749) - properly handle multiple subexpressions in the same hash, fixes #748 ([@jenseng](https://api.github.com/users/jenseng))
|
- [#749](https://github.com/wycats/handlebars.js/pull/749) - properly handle multiple subexpressions in the same hash, fixes #748 ([@jenseng](https://github.com/jenseng))
|
||||||
- [#743](https://github.com/wycats/handlebars.js/issues/743) - subexpression confusion/problem? ([@waynedpj](https://api.github.com/users/waynedpj))
|
- [#743](https://github.com/wycats/handlebars.js/issues/743) - subexpression confusion/problem? ([@waynedpj](https://github.com/waynedpj))
|
||||||
- [#746](https://github.com/wycats/handlebars.js/issues/746) - [CLI] support `handlebars --version` ([@apfelbox](https://api.github.com/users/apfelbox))
|
- [#746](https://github.com/wycats/handlebars.js/issues/746) - [CLI] support `handlebars --version` ([@apfelbox](https://github.com/apfelbox))
|
||||||
- [#747](https://github.com/wycats/handlebars.js/pull/747) - updated grunt-saucelabs, failing tests revealed ([@Jonahss](https://api.github.com/users/Jonahss))
|
- [#747](https://github.com/wycats/handlebars.js/pull/747) - updated grunt-saucelabs, failing tests revealed ([@Jonahss](https://github.com/Jonahss))
|
||||||
- Make JSON a requirement for the compiler. - 058c0fb
|
- Make JSON a requirement for the compiler. - 058c0fb
|
||||||
- Temporarily kill the AWS publish CI step - 8347ee2
|
- Temporarily kill the AWS publish CI step - 8347ee2
|
||||||
|
|
||||||
@@ -298,26 +656,26 @@ Compatibility notes:
|
|||||||
[Commits](https://github.com/wycats/handlebars.js/compare/v2.0.0-alpha.1...v2.0.0-alpha.2)
|
[Commits](https://github.com/wycats/handlebars.js/compare/v2.0.0-alpha.1...v2.0.0-alpha.2)
|
||||||
|
|
||||||
## v2.0.0-alpha.1 - February 10th, 2014
|
## v2.0.0-alpha.1 - February 10th, 2014
|
||||||
- [#182](https://github.com/wycats/handlebars.js/pull/182) - Allow passing hash parameters to partials ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#182](https://github.com/wycats/handlebars.js/pull/182) - Allow passing hash parameters to partials ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#392](https://github.com/wycats/handlebars.js/pull/392) - Access to root context in partials and helpers ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#392](https://github.com/wycats/handlebars.js/pull/392) - Access to root context in partials and helpers ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#472](https://github.com/wycats/handlebars.js/issues/472) - Helpers cannot have decimal parameters ([@kayleg](https://api.github.com/users/kayleg))
|
- [#472](https://github.com/wycats/handlebars.js/issues/472) - Helpers cannot have decimal parameters ([@kayleg](https://github.com/kayleg))
|
||||||
- [#569](https://github.com/wycats/handlebars.js/pull/569) - Unable to lookup array values using @index ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#569](https://github.com/wycats/handlebars.js/pull/569) - Unable to lookup array values using @index ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#491](https://github.com/wycats/handlebars.js/pull/491) - For nested helpers: get the @ variables of the outer helper from the inner one ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#491](https://github.com/wycats/handlebars.js/pull/491) - For nested helpers: get the @ variables of the outer helper from the inner one ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#669](https://github.com/wycats/handlebars.js/issues/669) - Ability to unregister a helper ([@dbachrach](https://api.github.com/users/dbachrach))
|
- [#669](https://github.com/wycats/handlebars.js/issues/669) - Ability to unregister a helper ([@dbachrach](https://github.com/dbachrach))
|
||||||
- [#730](https://github.com/wycats/handlebars.js/pull/730) - Raw block helpers ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#730](https://github.com/wycats/handlebars.js/pull/730) - Raw block helpers ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#634](https://github.com/wycats/handlebars.js/pull/634) - It would be great to have the helper name passed to `blockHelperMissing` ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#634](https://github.com/wycats/handlebars.js/pull/634) - It would be great to have the helper name passed to `blockHelperMissing` ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#729](https://github.com/wycats/handlebars.js/pull/729) - Convert template spec to object literal ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#729](https://github.com/wycats/handlebars.js/pull/729) - Convert template spec to object literal ([@kpdecker](https://github.com/kpdecker))
|
||||||
|
|
||||||
- [#658](https://github.com/wycats/handlebars.js/issues/658) - Depthed helpers do not work after an upgrade from 1.0.0 ([@xibxor](https://api.github.com/users/xibxor))
|
- [#658](https://github.com/wycats/handlebars.js/issues/658) - Depthed helpers do not work after an upgrade from 1.0.0 ([@xibxor](https://github.com/xibxor))
|
||||||
- [#671](https://github.com/wycats/handlebars.js/issues/671) - Crashes on no-parameter {{#each}} ([@stepancheg](https://api.github.com/users/stepancheg))
|
- [#671](https://github.com/wycats/handlebars.js/issues/671) - Crashes on no-parameter {{#each}} ([@stepancheg](https://github.com/stepancheg))
|
||||||
- [#689](https://github.com/wycats/handlebars.js/issues/689) - broken template precompilation ([@AAS](https://api.github.com/users/AAS))
|
- [#689](https://github.com/wycats/handlebars.js/issues/689) - broken template precompilation ([@AAS](https://github.com/AAS))
|
||||||
- [#698](https://github.com/wycats/handlebars.js/pull/698) - Fix parser generation under windows ([@osiris43](https://api.github.com/users/osiris43))
|
- [#698](https://github.com/wycats/handlebars.js/pull/698) - Fix parser generation under windows ([@osiris43](https://github.com/osiris43))
|
||||||
- [#699](https://github.com/wycats/handlebars.js/issues/699) - @DATA not compiles to invalid JS in stringParams mode ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#699](https://github.com/wycats/handlebars.js/issues/699) - @DATA not compiles to invalid JS in stringParams mode ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#705](https://github.com/wycats/handlebars.js/issues/705) - 1.3.0 can not be wrapped in an IIFE ([@craigteegarden](https://api.github.com/users/craigteegarden))
|
- [#705](https://github.com/wycats/handlebars.js/issues/705) - 1.3.0 can not be wrapped in an IIFE ([@craigteegarden](https://github.com/craigteegarden))
|
||||||
- [#706](https://github.com/wycats/handlebars.js/pull/706) - README: Use with helper instead of relying on blockHelperMissing ([@scottgonzalez](https://api.github.com/users/scottgonzalez))
|
- [#706](https://github.com/wycats/handlebars.js/pull/706) - README: Use with helper instead of relying on blockHelperMissing ([@scottgonzalez](https://github.com/scottgonzalez))
|
||||||
|
|
||||||
- [#700](https://github.com/wycats/handlebars.js/pull/700) - Remove redundant conditions ([@blakeembrey](https://api.github.com/users/blakeembrey))
|
- [#700](https://github.com/wycats/handlebars.js/pull/700) - Remove redundant conditions ([@blakeembrey](https://github.com/blakeembrey))
|
||||||
- [#704](https://github.com/wycats/handlebars.js/pull/704) - JavaScript Compiler Cleanup ([@blakeembrey](https://api.github.com/users/blakeembrey))
|
- [#704](https://github.com/wycats/handlebars.js/pull/704) - JavaScript Compiler Cleanup ([@blakeembrey](https://github.com/blakeembrey))
|
||||||
|
|
||||||
Compatibility notes:
|
Compatibility notes:
|
||||||
- `helperMissing` helper no longer has the indexed name argument. Helper name is now available via `options.name`.
|
- `helperMissing` helper no longer has the indexed name argument. Helper name is now available via `options.name`.
|
||||||
@@ -332,12 +690,12 @@ Compatibility notes:
|
|||||||
[Commits](https://github.com/wycats/handlebars.js/compare/v1.3.0...v2.0.0-alpha.1)
|
[Commits](https://github.com/wycats/handlebars.js/compare/v1.3.0...v2.0.0-alpha.1)
|
||||||
|
|
||||||
## v1.3.0 - January 1st, 2014
|
## v1.3.0 - January 1st, 2014
|
||||||
- [#690](https://github.com/wycats/handlebars.js/pull/690) - Added support for subexpressions ([@machty](https://api.github.com/users/machty))
|
- [#690](https://github.com/wycats/handlebars.js/pull/690) - Added support for subexpressions ([@machty](https://github.com/machty))
|
||||||
- [#696](https://github.com/wycats/handlebars.js/pull/696) - Fix for reserved keyword "default" ([@nateirwin](https://api.github.com/users/nateirwin))
|
- [#696](https://github.com/wycats/handlebars.js/pull/696) - Fix for reserved keyword "default" ([@nateirwin](https://github.com/nateirwin))
|
||||||
- [#692](https://github.com/wycats/handlebars.js/pull/692) - add line numbers to nodes when parsing ([@fivetanley](https://api.github.com/users/fivetanley))
|
- [#692](https://github.com/wycats/handlebars.js/pull/692) - add line numbers to nodes when parsing ([@fivetanley](https://github.com/fivetanley))
|
||||||
- [#695](https://github.com/wycats/handlebars.js/pull/695) - Pull options out from param setup to allow easier extension ([@blakeembrey](https://api.github.com/users/blakeembrey))
|
- [#695](https://github.com/wycats/handlebars.js/pull/695) - Pull options out from param setup to allow easier extension ([@blakeembrey](https://github.com/blakeembrey))
|
||||||
- [#694](https://github.com/wycats/handlebars.js/pull/694) - Make the environment reusable ([@blakeembrey](https://api.github.com/users/blakeembrey))
|
- [#694](https://github.com/wycats/handlebars.js/pull/694) - Make the environment reusable ([@blakeembrey](https://github.com/blakeembrey))
|
||||||
- [#636](https://github.com/wycats/handlebars.js/issues/636) - Print line and column of errors ([@sgronblo](https://api.github.com/users/sgronblo))
|
- [#636](https://github.com/wycats/handlebars.js/issues/636) - Print line and column of errors ([@sgronblo](https://github.com/sgronblo))
|
||||||
- Use literal for data lookup - c1a93d3
|
- Use literal for data lookup - c1a93d3
|
||||||
- Add stack handling sanity checks - cd885bf
|
- Add stack handling sanity checks - cd885bf
|
||||||
- Fix stack id "leak" on replaceStack - ddfe457
|
- Fix stack id "leak" on replaceStack - ddfe457
|
||||||
@@ -346,25 +704,25 @@ Compatibility notes:
|
|||||||
[Commits](https://github.com/wycats/handlebars.js/compare/v1.2.1...v1.3.0)
|
[Commits](https://github.com/wycats/handlebars.js/compare/v1.2.1...v1.3.0)
|
||||||
|
|
||||||
## v1.2.1 - December 26th, 2013
|
## v1.2.1 - December 26th, 2013
|
||||||
- [#684](https://github.com/wycats/handlebars.js/pull/684) - Allow any number of trailing characters for valid JavaScript variable ([@blakeembrey](https://api.github.com/users/blakeembrey))
|
- [#684](https://github.com/wycats/handlebars.js/pull/684) - Allow any number of trailing characters for valid JavaScript variable ([@blakeembrey](https://github.com/blakeembrey))
|
||||||
- [#686](https://github.com/wycats/handlebars.js/pull/686) - Falsy AMD module names in version 1.2.0 ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#686](https://github.com/wycats/handlebars.js/pull/686) - Falsy AMD module names in version 1.2.0 ([@kpdecker](https://github.com/kpdecker))
|
||||||
|
|
||||||
[Commits](https://github.com/wycats/handlebars.js/compare/v1.2.0...v1.2.1)
|
[Commits](https://github.com/wycats/handlebars.js/compare/v1.2.0...v1.2.1)
|
||||||
|
|
||||||
## v1.2.0 - December 23rd, 2013
|
## v1.2.0 - December 23rd, 2013
|
||||||
- [#675](https://github.com/wycats/handlebars.js/issues/675) - Cannot compile empty template for partial ([@erwinw](https://api.github.com/users/erwinw))
|
- [#675](https://github.com/wycats/handlebars.js/issues/675) - Cannot compile empty template for partial ([@erwinw](https://github.com/erwinw))
|
||||||
- [#677](https://github.com/wycats/handlebars.js/issues/677) - Triple brace statements fail under IE ([@hamzaCM](https://api.github.com/users/hamzaCM))
|
- [#677](https://github.com/wycats/handlebars.js/issues/677) - Triple brace statements fail under IE ([@hamzaCM](https://github.com/hamzaCM))
|
||||||
- [#655](https://github.com/wycats/handlebars.js/issues/655) - Loading Handlebars using bower ([@niki4810](https://api.github.com/users/niki4810))
|
- [#655](https://github.com/wycats/handlebars.js/issues/655) - Loading Handlebars using bower ([@niki4810](https://github.com/niki4810))
|
||||||
- [#657](https://github.com/wycats/handlebars.js/pull/657) - Fixes issue where cli compiles non handlebars templates ([@chrishoage](https://api.github.com/users/chrishoage))
|
- [#657](https://github.com/wycats/handlebars.js/pull/657) - Fixes issue where cli compiles non handlebars templates ([@chrishoage](https://github.com/chrishoage))
|
||||||
- [#681](https://github.com/wycats/handlebars.js/pull/681) - Adds in-browser testing and Saucelabs CI ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#681](https://github.com/wycats/handlebars.js/pull/681) - Adds in-browser testing and Saucelabs CI ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#661](https://github.com/wycats/handlebars.js/pull/661) - Add @first and @index to #each object iteration ([@cgp](https://api.github.com/users/cgp))
|
- [#661](https://github.com/wycats/handlebars.js/pull/661) - Add @first and @index to #each object iteration ([@cgp](https://github.com/cgp))
|
||||||
- [#650](https://github.com/wycats/handlebars.js/pull/650) - Handlebars is MIT-licensed ([@thomasboyt](https://api.github.com/users/thomasboyt))
|
- [#650](https://github.com/wycats/handlebars.js/pull/650) - Handlebars is MIT-licensed ([@thomasboyt](https://github.com/thomasboyt))
|
||||||
- [#641](https://github.com/wycats/handlebars.js/pull/641) - Document ember testing process ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#641](https://github.com/wycats/handlebars.js/pull/641) - Document ember testing process ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#662](https://github.com/wycats/handlebars.js/issues/662) - handlebars-source 1.1.2 is missing from RubyGems.
|
- [#662](https://github.com/wycats/handlebars.js/issues/662) - handlebars-source 1.1.2 is missing from RubyGems.
|
||||||
- [#656](https://github.com/wycats/handlebars.js/issues/656) - Expose COMPILER_REVISION checks as a hook ([@machty](https://api.github.com/users/machty))
|
- [#656](https://github.com/wycats/handlebars.js/issues/656) - Expose COMPILER_REVISION checks as a hook ([@machty](https://github.com/machty))
|
||||||
- [#668](https://github.com/wycats/handlebars.js/issues/668) - Consider publishing handlebars-runtime as a separate module on npm ([@dlmanning](https://api.github.com/users/dlmanning))
|
- [#668](https://github.com/wycats/handlebars.js/issues/668) - Consider publishing handlebars-runtime as a separate module on npm ([@dlmanning](https://github.com/dlmanning))
|
||||||
- [#679](https://github.com/wycats/handlebars.js/issues/679) - Unable to override invokePartial ([@mattbrailsford](https://api.github.com/users/mattbrailsford))
|
- [#679](https://github.com/wycats/handlebars.js/issues/679) - Unable to override invokePartial ([@mattbrailsford](https://github.com/mattbrailsford))
|
||||||
- [#646](https://github.com/wycats/handlebars.js/pull/646) - Fix "\\{{" immediately following "\{{" ([@dmarcotte](https://api.github.com/users/dmarcotte))
|
- [#646](https://github.com/wycats/handlebars.js/pull/646) - Fix "\\{{" immediately following "\{{" ([@dmarcotte](https://github.com/dmarcotte))
|
||||||
- Allow extend to work with non-prototyped objects - eb53f2e
|
- Allow extend to work with non-prototyped objects - eb53f2e
|
||||||
- Add JavascriptCompiler public API tests - 1a751b2
|
- Add JavascriptCompiler public API tests - 1a751b2
|
||||||
- Add AST test coverage for more complex paths - ddea5be
|
- Add AST test coverage for more complex paths - ddea5be
|
||||||
@@ -379,8 +737,8 @@ Compatibility notes:
|
|||||||
|
|
||||||
## v1.1.2 - November 5th, 2013
|
## v1.1.2 - November 5th, 2013
|
||||||
|
|
||||||
- [#645](https://github.com/wycats/handlebars.js/issues/645) - 1.1.1 fails under IE8 ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#645](https://github.com/wycats/handlebars.js/issues/645) - 1.1.1 fails under IE8 ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#644](https://github.com/wycats/handlebars.js/issues/644) - Using precompiled templates (AMD mode) with handlebars.runtime 1.1.1 ([@fddima](https://api.github.com/users/fddima))
|
- [#644](https://github.com/wycats/handlebars.js/issues/644) - Using precompiled templates (AMD mode) with handlebars.runtime 1.1.1 ([@fddima](https://github.com/fddima))
|
||||||
|
|
||||||
- Add simple binary utility tests - 96a45a4
|
- Add simple binary utility tests - 96a45a4
|
||||||
- Fix empty string compilation - eea708a
|
- Fix empty string compilation - eea708a
|
||||||
@@ -397,13 +755,13 @@ Compatibility notes:
|
|||||||
|
|
||||||
## v1.1.0 - November 3rd, 2013
|
## v1.1.0 - November 3rd, 2013
|
||||||
|
|
||||||
- [#628](https://github.com/wycats/handlebars.js/pull/628) - Convert code to ES6 modules ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#628](https://github.com/wycats/handlebars.js/pull/628) - Convert code to ES6 modules ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#336](https://github.com/wycats/handlebars.js/pull/336) - Add whitespace control syntax ([@kpdecker](https://api.github.com/users/kpdecker))
|
- [#336](https://github.com/wycats/handlebars.js/pull/336) - Add whitespace control syntax ([@kpdecker](https://github.com/kpdecker))
|
||||||
- [#535](https://github.com/wycats/handlebars.js/pull/535) - Fix for probable JIT error under Safari ([@sorentwo](https://api.github.com/users/sorentwo))
|
- [#535](https://github.com/wycats/handlebars.js/pull/535) - Fix for probable JIT error under Safari ([@sorentwo](https://github.com/sorentwo))
|
||||||
- [#483](https://github.com/wycats/handlebars.js/issues/483) - Add first and last @ vars to each helper ([@denniskuczynski](https://api.github.com/users/denniskuczynski))
|
- [#483](https://github.com/wycats/handlebars.js/issues/483) - Add first and last @ vars to each helper ([@denniskuczynski](https://github.com/denniskuczynski))
|
||||||
- [#557](https://github.com/wycats/handlebars.js/pull/557) - `\\{{foo}}` escaping only works in some situations ([@dmarcotte](https://api.github.com/users/dmarcotte))
|
- [#557](https://github.com/wycats/handlebars.js/pull/557) - `\\{{foo}}` escaping only works in some situations ([@dmarcotte](https://github.com/dmarcotte))
|
||||||
- [#552](https://github.com/wycats/handlebars.js/pull/552) - Added BOM removal flag. ([@blessenm](https://api.github.com/users/blessenm))
|
- [#552](https://github.com/wycats/handlebars.js/pull/552) - Added BOM removal flag. ([@blessenm](https://github.com/blessenm))
|
||||||
- [#543](https://github.com/wycats/handlebars.js/pull/543) - publish passing master builds to s3 ([@fivetanley](https://api.github.com/users/fivetanley))
|
- [#543](https://github.com/wycats/handlebars.js/pull/543) - publish passing master builds to s3 ([@fivetanley](https://github.com/fivetanley))
|
||||||
|
|
||||||
- [#608](https://github.com/wycats/handlebars.js/issues/608) - Add `includeZero` flag to `if` conditional
|
- [#608](https://github.com/wycats/handlebars.js/issues/608) - Add `includeZero` flag to `if` conditional
|
||||||
- [#498](https://github.com/wycats/handlebars.js/issues/498) - `Handlebars.compile` fails on empty string although a single blank works fine
|
- [#498](https://github.com/wycats/handlebars.js/issues/498) - `Handlebars.compile` fails on empty string although a single blank works fine
|
||||||
@@ -449,7 +807,7 @@ Compatibility notes:
|
|||||||
## v1.0.11 / 1.0.0-rc4 - May 13 2013
|
## v1.0.11 / 1.0.0-rc4 - May 13 2013
|
||||||
|
|
||||||
- [#458](https://github.com/wycats/handlebars.js/issues/458) - Fix `./foo` syntax ([@jpfiset](https://github.com/jpfiset))
|
- [#458](https://github.com/wycats/handlebars.js/issues/458) - Fix `./foo` syntax ([@jpfiset](https://github.com/jpfiset))
|
||||||
- [#460](https://github.com/wycats/handlebars.js/issues/460) - Allow `:` in unescaped identifers ([@jpfiset](https://github.com/jpfiset))
|
- [#460](https://github.com/wycats/handlebars.js/issues/460) - Allow `:` in unescaped identifiers ([@jpfiset](https://github.com/jpfiset))
|
||||||
- [#471](https://github.com/wycats/handlebars.js/issues/471) - Create release notes (These!)
|
- [#471](https://github.com/wycats/handlebars.js/issues/471) - Create release notes (These!)
|
||||||
- [#456](https://github.com/wycats/handlebars.js/issues/456) - Allow escaping of `\\`
|
- [#456](https://github.com/wycats/handlebars.js/issues/456) - Allow escaping of `\\`
|
||||||
- [#211](https://github.com/wycats/handlebars.js/issues/211) - Fix exception in `escapeExpression`
|
- [#211](https://github.com/wycats/handlebars.js/issues/211) - Fix exception in `escapeExpression`
|
||||||
|
|||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
import Handlebars = require('handlebars')
|
||||||
|
|
||||||
|
declare module "handlebars/runtime" {
|
||||||
|
|
||||||
|
}
|
||||||
+15
-5
@@ -1,14 +1,20 @@
|
|||||||
{
|
{
|
||||||
|
"extends": [
|
||||||
|
"../.eslintrc.js",
|
||||||
|
"plugin:es5/no-es2015"
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
"es5"
|
||||||
|
],
|
||||||
"globals": {
|
"globals": {
|
||||||
"CompilerContext": true,
|
"CompilerContext": true,
|
||||||
"Handlebars": true,
|
"Handlebars": true,
|
||||||
"handlebarsEnv": true,
|
"handlebarsEnv": true,
|
||||||
|
|
||||||
"shouldCompileTo": true,
|
"shouldCompileTo": true,
|
||||||
"shouldCompileToWithPartials": true,
|
"shouldCompileToWithPartials": true,
|
||||||
"shouldThrow": true,
|
"shouldThrow": true,
|
||||||
|
"expectTemplate": true,
|
||||||
"compileWithPartials": true,
|
"compileWithPartials": true,
|
||||||
|
|
||||||
"console": true,
|
"console": true,
|
||||||
"require": true,
|
"require": true,
|
||||||
"suite": true,
|
"suite": true,
|
||||||
@@ -21,16 +27,20 @@
|
|||||||
"start": true,
|
"start": true,
|
||||||
"stop": true,
|
"stop": true,
|
||||||
"ok": true,
|
"ok": true,
|
||||||
|
"sinon": true,
|
||||||
"strictEqual": true,
|
"strictEqual": true,
|
||||||
"define": true
|
"define": true,
|
||||||
|
"expect": true,
|
||||||
|
"chai": true
|
||||||
},
|
},
|
||||||
"env": {
|
"env": {
|
||||||
"mocha": true
|
"mocha": true
|
||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
// Disabling for tests, for now.
|
// Disabling for tests, for now.
|
||||||
"no-path-concat": 0,
|
"no-path-concat": "off",
|
||||||
|
|
||||||
"no-var": 0
|
"no-var": "off",
|
||||||
|
"dot-notation": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -18,12 +18,14 @@
|
|||||||
document.documentElement.className = 'headless';
|
document.documentElement.className = 'headless';
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<script src="/node_modules/sinon/pkg/sinon.js"></script>
|
||||||
|
<script src="/node_modules/chai/chai.js"></script>
|
||||||
|
<script src="/node_modules/dirty-chai/lib/dirty-chai.js"></script>
|
||||||
<script src="/node_modules/mocha/mocha.js"></script>
|
<script src="/node_modules/mocha/mocha.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
window.expect = chai.expect;
|
||||||
mocha.setup('bdd');
|
mocha.setup('bdd');
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script src="/spec/env/json2.js"></script>
|
|
||||||
<script src="/spec/env/require.js"></script>
|
<script src="/spec/env/require.js"></script>
|
||||||
|
|
||||||
<script src="/spec/env/common.js"></script>
|
<script src="/spec/env/common.js"></script>
|
||||||
|
|||||||
+4
-3
@@ -19,14 +19,15 @@
|
|||||||
document.documentElement.className = 'headless';
|
document.documentElement.className = 'headless';
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<script src="/node_modules/sinon/pkg/sinon.js"></script>
|
||||||
|
<script src="/node_modules/chai/chai.js"></script>
|
||||||
|
<script src="/node_modules/dirty-chai/lib/dirty-chai.js"></script>
|
||||||
<script src="/node_modules/mocha/mocha.js"></script>
|
<script src="/node_modules/mocha/mocha.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
window.expect = chai.expect;
|
||||||
mocha.setup('bdd');
|
mocha.setup('bdd');
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script src="/spec/env/json2.js"></script>
|
|
||||||
<script src="/spec/env/require.js"></script>
|
<script src="/spec/env/require.js"></script>
|
||||||
|
|
||||||
<script src="/spec/env/common.js"></script>
|
<script src="/spec/env/common.js"></script>
|
||||||
<script>
|
<script>
|
||||||
var requireFailure;
|
var requireFailure;
|
||||||
|
|||||||
+107
@@ -59,6 +59,7 @@ describe('ast', function() {
|
|||||||
equals(node.loc.end.column, lastColumn);
|
equals(node.loc.end.column, lastColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* eslint-disable no-multi-spaces */
|
||||||
ast = Handlebars.parse(
|
ast = Handlebars.parse(
|
||||||
'line 1 {{line1Token}}\n' // 1
|
'line 1 {{line1Token}}\n' // 1
|
||||||
+ ' line 2 {{line2token}}\n' // 2
|
+ ' line 2 {{line2token}}\n' // 2
|
||||||
@@ -71,6 +72,7 @@ describe('ast', function() {
|
|||||||
+ '{{else inverse}}\n' // 9
|
+ '{{else inverse}}\n' // 9
|
||||||
+ '{{else}}\n' // 10
|
+ '{{else}}\n' // 10
|
||||||
+ '{{/open}}'); // 11
|
+ '{{/open}}'); // 11
|
||||||
|
/* eslint-enable no-multi-spaces */
|
||||||
body = ast.body;
|
body = ast.body;
|
||||||
|
|
||||||
it('gets ContentNode line numbers', function() {
|
it('gets ContentNode line numbers', function() {
|
||||||
@@ -121,6 +123,40 @@ describe('ast', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('whitespace control', function() {
|
||||||
|
describe('parse', function() {
|
||||||
|
it('mustache', function() {
|
||||||
|
var ast = Handlebars.parse(' {{~comment~}} ');
|
||||||
|
|
||||||
|
equals(ast.body[0].value, '');
|
||||||
|
equals(ast.body[2].value, '');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('block statements', function() {
|
||||||
|
var ast = Handlebars.parse(' {{# comment~}} \nfoo\n {{~/comment}}');
|
||||||
|
|
||||||
|
equals(ast.body[0].value, '');
|
||||||
|
equals(ast.body[1].program.body[0].value, 'foo');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('parseWithoutProcessing', function() {
|
||||||
|
it('mustache', function() {
|
||||||
|
var ast = Handlebars.parseWithoutProcessing(' {{~comment~}} ');
|
||||||
|
|
||||||
|
equals(ast.body[0].value, ' ');
|
||||||
|
equals(ast.body[2].value, ' ');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('block statements', function() {
|
||||||
|
var ast = Handlebars.parseWithoutProcessing(' {{# comment~}} \nfoo\n {{~/comment}}');
|
||||||
|
|
||||||
|
equals(ast.body[0].value, ' ');
|
||||||
|
equals(ast.body[1].program.body[0].value, ' \nfoo\n ');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('standalone flags', function() {
|
describe('standalone flags', function() {
|
||||||
describe('mustache', function() {
|
describe('mustache', function() {
|
||||||
it('does not mark mustaches as standalone', function() {
|
it('does not mark mustaches as standalone', function() {
|
||||||
@@ -129,6 +165,54 @@ describe('ast', function() {
|
|||||||
equals(!!ast.body[2].value, true);
|
equals(!!ast.body[2].value, true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('blocks - parseWithoutProcessing', function() {
|
||||||
|
it('block mustaches', function() {
|
||||||
|
var ast = Handlebars.parseWithoutProcessing(' {{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} '),
|
||||||
|
block = ast.body[1];
|
||||||
|
|
||||||
|
equals(ast.body[0].value, ' ');
|
||||||
|
|
||||||
|
equals(block.program.body[0].value, ' \nfoo\n ');
|
||||||
|
equals(block.inverse.body[0].value, ' \n bar \n ');
|
||||||
|
|
||||||
|
equals(ast.body[2].value, ' ');
|
||||||
|
});
|
||||||
|
it('initial block mustaches', function() {
|
||||||
|
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}}'),
|
||||||
|
block = ast.body[0];
|
||||||
|
|
||||||
|
equals(block.program.body[0].value, ' \n');
|
||||||
|
equals(block.program.body[1].path.original, 'foo');
|
||||||
|
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}}'),
|
||||||
|
body = ast.body[0].program.body,
|
||||||
|
block = body[1];
|
||||||
|
|
||||||
|
equals(body[0].value, ' \n');
|
||||||
|
|
||||||
|
equals(block.program.body[0].value, ' \nfoo\n ');
|
||||||
|
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];
|
||||||
|
|
||||||
|
equals(ast.body[0].omit, undefined);
|
||||||
|
|
||||||
|
equals(block.program.body[0].value, ' \nfoo\n ');
|
||||||
|
equals(block.inverse.body[0].value, ' \n bar \n ');
|
||||||
|
|
||||||
|
equals(ast.body[2].value, ' ');
|
||||||
|
});
|
||||||
|
});
|
||||||
describe('blocks', function() {
|
describe('blocks', function() {
|
||||||
it('marks block mustaches as standalone', function() {
|
it('marks block mustaches as standalone', function() {
|
||||||
var ast = Handlebars.parse(' {{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} '),
|
var ast = Handlebars.parse(' {{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} '),
|
||||||
@@ -202,6 +286,18 @@ describe('ast', function() {
|
|||||||
equals(ast.body[2].value, '');
|
equals(ast.body[2].value, '');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('partials - parseWithoutProcessing', function() {
|
||||||
|
it('simple partial', function() {
|
||||||
|
var ast = Handlebars.parseWithoutProcessing('{{> partial }} ');
|
||||||
|
equals(ast.body[1].value, ' ');
|
||||||
|
});
|
||||||
|
it('indented partial', function() {
|
||||||
|
var ast = Handlebars.parseWithoutProcessing(' {{> partial }} ');
|
||||||
|
equals(ast.body[0].value, ' ');
|
||||||
|
equals(ast.body[1].indent, '');
|
||||||
|
equals(ast.body[2].value, ' ');
|
||||||
|
});
|
||||||
|
});
|
||||||
describe('partials', function() {
|
describe('partials', function() {
|
||||||
it('marks partial as standalone', function() {
|
it('marks partial as standalone', function() {
|
||||||
var ast = Handlebars.parse('{{> partial }} ');
|
var ast = Handlebars.parse('{{> partial }} ');
|
||||||
@@ -221,6 +317,17 @@ describe('ast', function() {
|
|||||||
equals(ast.body[1].omit, undefined);
|
equals(ast.body[1].omit, undefined);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('comments - parseWithoutProcessing', function() {
|
||||||
|
it('simple comment', function() {
|
||||||
|
var ast = Handlebars.parseWithoutProcessing('{{! comment }} ');
|
||||||
|
equals(ast.body[1].value, ' ');
|
||||||
|
});
|
||||||
|
it('indented comment', function() {
|
||||||
|
var ast = Handlebars.parseWithoutProcessing(' {{! comment }} ');
|
||||||
|
equals(ast.body[0].value, ' ');
|
||||||
|
equals(ast.body[2].value, ' ');
|
||||||
|
});
|
||||||
|
});
|
||||||
describe('comments', function() {
|
describe('comments', function() {
|
||||||
it('marks comment as standalone', function() {
|
it('marks comment as standalone', function() {
|
||||||
var ast = Handlebars.parse('{{! comment }} ');
|
var ast = Handlebars.parse('{{! comment }} ');
|
||||||
|
|||||||
+40
-2
@@ -254,6 +254,37 @@ describe('builtin helpers', function() {
|
|||||||
template({});
|
template({});
|
||||||
}, handlebarsEnv.Exception, 'Must pass iterator to #each');
|
}, handlebarsEnv.Exception, 'Must pass iterator to #each');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (global.Symbol && global.Symbol.iterator) {
|
||||||
|
it('each on iterable', function() {
|
||||||
|
function Iterator(arr) {
|
||||||
|
this.arr = arr;
|
||||||
|
this.index = 0;
|
||||||
|
}
|
||||||
|
Iterator.prototype.next = function() {
|
||||||
|
var value = this.arr[this.index];
|
||||||
|
var done = this.index === this.arr.length;
|
||||||
|
if (!done) {
|
||||||
|
this.index++;
|
||||||
|
}
|
||||||
|
return { value: value, done: done };
|
||||||
|
};
|
||||||
|
function Iterable(arr) {
|
||||||
|
this.arr = arr;
|
||||||
|
}
|
||||||
|
Iterable.prototype[global.Symbol.iterator] = 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 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');
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#log', function() {
|
describe('#log', function() {
|
||||||
@@ -312,10 +343,14 @@ describe('builtin helpers', function() {
|
|||||||
console.info = function(info) {
|
console.info = function(info) {
|
||||||
equals('whee', info);
|
equals('whee', info);
|
||||||
called = true;
|
called = true;
|
||||||
|
console.info = $info;
|
||||||
|
console.log = $log;
|
||||||
};
|
};
|
||||||
console.log = function(log) {
|
console.log = function(log) {
|
||||||
equals('whee', log);
|
equals('whee', log);
|
||||||
called = true;
|
called = true;
|
||||||
|
console.info = $info;
|
||||||
|
console.log = $log;
|
||||||
};
|
};
|
||||||
|
|
||||||
shouldCompileTo(string, hash, '');
|
shouldCompileTo(string, hash, '');
|
||||||
@@ -329,6 +364,7 @@ describe('builtin helpers', function() {
|
|||||||
console.error = function(log) {
|
console.error = function(log) {
|
||||||
equals('whee', log);
|
equals('whee', log);
|
||||||
called = true;
|
called = true;
|
||||||
|
console.error = $error;
|
||||||
};
|
};
|
||||||
|
|
||||||
shouldCompileTo(string, [hash,,,, {level: '03'}], '');
|
shouldCompileTo(string, [hash,,,, {level: '03'}], '');
|
||||||
@@ -343,6 +379,7 @@ describe('builtin helpers', function() {
|
|||||||
console.log = function(log) {
|
console.log = function(log) {
|
||||||
equals('whee', log);
|
equals('whee', log);
|
||||||
called = true;
|
called = true;
|
||||||
|
console.log = $log;
|
||||||
};
|
};
|
||||||
|
|
||||||
shouldCompileTo(string, [hash,,,, {level: '03'}], '');
|
shouldCompileTo(string, [hash,,,, {level: '03'}], '');
|
||||||
@@ -385,9 +422,9 @@ describe('builtin helpers', function() {
|
|||||||
var hash = { blah: 'whee' };
|
var hash = { blah: 'whee' };
|
||||||
var called = false;
|
var called = false;
|
||||||
|
|
||||||
console.info = console.log = console.error = console.debug = function(log) {
|
console.info = console.log = console.error = console.debug = function() {
|
||||||
equals('whee', log);
|
|
||||||
called = true;
|
called = true;
|
||||||
|
console.info = console.log = console.error = console.debug = $log;
|
||||||
};
|
};
|
||||||
|
|
||||||
shouldCompileTo(string, hash, '');
|
shouldCompileTo(string, hash, '');
|
||||||
@@ -403,6 +440,7 @@ describe('builtin helpers', function() {
|
|||||||
equals('foo', log2);
|
equals('foo', log2);
|
||||||
equals(1, log3);
|
equals(1, log3);
|
||||||
called = true;
|
called = true;
|
||||||
|
console.log = $log;
|
||||||
};
|
};
|
||||||
|
|
||||||
shouldCompileTo(string, hash, '');
|
shouldCompileTo(string, hash, '');
|
||||||
|
|||||||
+1
-1
@@ -59,7 +59,7 @@ describe('compiler', function() {
|
|||||||
Handlebars.compile(' \n {{#if}}\n{{/def}}')();
|
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) {
|
} catch (err) {
|
||||||
equal(err.propertyIsEnumerable('column'), true, 'Checking error column');
|
equal(Object.prototype.propertyIsEnumerable.call(err, 'column'), true, 'Checking error column');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Vendored
+11
-1
@@ -3,13 +3,23 @@ require('./common');
|
|||||||
var fs = require('fs'),
|
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;
|
||||||
|
|
||||||
|
global.sinon = require('sinon');
|
||||||
|
|
||||||
global.Handlebars = 'no-conflict';
|
global.Handlebars = 'no-conflict';
|
||||||
|
|
||||||
var filename = 'dist/handlebars.js';
|
var filename = 'dist/handlebars.js';
|
||||||
if (global.minimizedTest) {
|
if (global.minimizedTest) {
|
||||||
filename = 'dist/handlebars.min.js';
|
filename = 'dist/handlebars.min.js';
|
||||||
}
|
}
|
||||||
vm.runInThisContext(fs.readFileSync(__dirname + '/../../' + filename), filename);
|
var distHandlebars = fs.readFileSync(require.resolve('../../' + filename), 'utf-8');
|
||||||
|
vm.runInThisContext(distHandlebars, filename);
|
||||||
|
|
||||||
global.CompilerContext = {
|
global.CompilerContext = {
|
||||||
browser: true,
|
browser: true,
|
||||||
|
|||||||
Vendored
+103
-2
@@ -1,3 +1,7 @@
|
|||||||
|
var global = (function() {
|
||||||
|
return this;
|
||||||
|
}());
|
||||||
|
|
||||||
var AssertError;
|
var AssertError;
|
||||||
if (Error.captureStackTrace) {
|
if (Error.captureStackTrace) {
|
||||||
AssertError = function AssertError(message, caller) {
|
AssertError = function AssertError(message, caller) {
|
||||||
@@ -14,10 +18,16 @@ if (Error.captureStackTrace) {
|
|||||||
AssertError = Error;
|
AssertError = Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use "expectTemplate(template)...toCompileTo(output)" instead
|
||||||
|
*/
|
||||||
global.shouldCompileTo = function(string, hashOrArray, expected, message) {
|
global.shouldCompileTo = function(string, hashOrArray, expected, message) {
|
||||||
shouldCompileToWithPartials(string, hashOrArray, false, expected, message);
|
shouldCompileToWithPartials(string, hashOrArray, false, 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);
|
var result = compileWithPartials(string, hashOrArray, partials);
|
||||||
if (result !== expected) {
|
if (result !== expected) {
|
||||||
@@ -25,6 +35,9 @@ global.shouldCompileToWithPartials = function shouldCompileToWithPartials(string
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use "expectTemplate(template)...toCompileTo(output)" instead
|
||||||
|
*/
|
||||||
global.compileWithPartials = function(string, hashOrArray, partials) {
|
global.compileWithPartials = function(string, hashOrArray, partials) {
|
||||||
var template,
|
var template,
|
||||||
ary,
|
ary,
|
||||||
@@ -34,8 +47,8 @@ global.compileWithPartials = function(string, hashOrArray, partials) {
|
|||||||
delete hashOrArray.hash;
|
delete hashOrArray.hash;
|
||||||
} else if (Object.prototype.toString.call(hashOrArray) === '[object Array]') {
|
} else if (Object.prototype.toString.call(hashOrArray) === '[object Array]') {
|
||||||
ary = [];
|
ary = [];
|
||||||
ary.push(hashOrArray[0]);
|
ary.push(hashOrArray[0]); // input
|
||||||
ary.push({ helpers: hashOrArray[1], partials: hashOrArray[2] });
|
ary.push({helpers: hashOrArray[1], partials: hashOrArray[2]});
|
||||||
options = typeof hashOrArray[3] === 'object' ? hashOrArray[3] : {compat: hashOrArray[3]};
|
options = typeof hashOrArray[3] === 'object' ? hashOrArray[3] : {compat: hashOrArray[3]};
|
||||||
if (hashOrArray[4] != null) {
|
if (hashOrArray[4] != null) {
|
||||||
options.data = !!hashOrArray[4];
|
options.data = !!hashOrArray[4];
|
||||||
@@ -50,12 +63,20 @@ global.compileWithPartials = function(string, hashOrArray, partials) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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) {
|
global.equals = global.equal = function equals(a, b, msg) {
|
||||||
if (a !== b) {
|
if (a !== b) {
|
||||||
throw new AssertError("'" + a + "' should === '" + b + "'" + (msg ? ': ' + msg : ''), equals);
|
throw new AssertError("'" + a + "' should === '" + b + "'" + (msg ? ': ' + msg : ''), equals);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use chai's expect-style API instead (`expect(actualValue).to.equal(expectedValue)`)
|
||||||
|
* @see https://www.chaijs.com/api/bdd/#method_throw
|
||||||
|
*/
|
||||||
global.shouldThrow = function(callback, type, msg) {
|
global.shouldThrow = function(callback, type, msg) {
|
||||||
var failed;
|
var failed;
|
||||||
try {
|
try {
|
||||||
@@ -73,3 +94,83 @@ global.shouldThrow = function(callback, type, msg) {
|
|||||||
throw new AssertError('It failed to throw', shouldThrow);
|
throw new AssertError('It failed to throw', shouldThrow);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
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.compileOptions = {};
|
||||||
|
this.runtimeOptions = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
HandlebarsTestBench.prototype.withInput = function(input) {
|
||||||
|
this.input = input;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
HandlebarsTestBench.prototype.withHelper = function(name, helperFunction) {
|
||||||
|
this.helpers[name] = helperFunction;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
HandlebarsTestBench.prototype.withPartial = function(name, partialAsString) {
|
||||||
|
this.partials[name] = partialAsString;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
HandlebarsTestBench.prototype.withCompileOptions = function(compileOptions) {
|
||||||
|
this.compileOptions = compileOptions;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
HandlebarsTestBench.prototype.withRuntimeOptions = function(runtimeOptions) {
|
||||||
|
this.runtimeOptions = runtimeOptions;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
HandlebarsTestBench.prototype.withMessage = function(message) {
|
||||||
|
this.message = message;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
HandlebarsTestBench.prototype.toCompileTo = function(expectedOutputAsString) {
|
||||||
|
expect(this._compileAndExeute()).to.equal(expectedOutputAsString);
|
||||||
|
};
|
||||||
|
|
||||||
|
// see chai "to.throw" (https://www.chaijs.com/api/bdd/#method_throw)
|
||||||
|
HandlebarsTestBench.prototype.toThrow = function(errorLike, errMsgMatcher, msg) {
|
||||||
|
var self = this;
|
||||||
|
expect(function() {
|
||||||
|
self._compileAndExeute();
|
||||||
|
}).to.throw(errorLike, errMsgMatcher, msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
HandlebarsTestBench.prototype._compileAndExeute = function() {
|
||||||
|
var compile = Object.keys(this.partials).length > 0
|
||||||
|
? CompilerContext.compileWithPartial
|
||||||
|
: CompilerContext.compile;
|
||||||
|
|
||||||
|
var combinedRuntimeOptions = this._combineRuntimeOptions();
|
||||||
|
|
||||||
|
var template = compile(this.templateAsString, this.compileOptions);
|
||||||
|
return template(this.input, combinedRuntimeOptions);
|
||||||
|
};
|
||||||
|
|
||||||
|
HandlebarsTestBench.prototype._combineRuntimeOptions = function() {
|
||||||
|
var self = this;
|
||||||
|
var combinedRuntimeOptions = {};
|
||||||
|
Object.keys(this.runtimeOptions).forEach(function(key) {
|
||||||
|
combinedRuntimeOptions[key] = self.runtimeOptions[key];
|
||||||
|
});
|
||||||
|
combinedRuntimeOptions.helpers = this.helpers;
|
||||||
|
combinedRuntimeOptions.partials = this.partials;
|
||||||
|
return combinedRuntimeOptions;
|
||||||
|
};
|
||||||
|
|||||||
Vendored
-489
@@ -1,489 +0,0 @@
|
|||||||
/*
|
|
||||||
json2.js
|
|
||||||
2014-02-04
|
|
||||||
|
|
||||||
Public Domain.
|
|
||||||
|
|
||||||
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
|
|
||||||
|
|
||||||
See http://www.JSON.org/js.html
|
|
||||||
|
|
||||||
|
|
||||||
This code should be minified before deployment.
|
|
||||||
See http://javascript.crockford.com/jsmin.html
|
|
||||||
|
|
||||||
USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
|
|
||||||
NOT CONTROL.
|
|
||||||
|
|
||||||
|
|
||||||
This file creates a global JSON object containing two methods: stringify
|
|
||||||
and parse.
|
|
||||||
|
|
||||||
JSON.stringify(value, replacer, space)
|
|
||||||
value any JavaScript value, usually an object or array.
|
|
||||||
|
|
||||||
replacer an optional parameter that determines how object
|
|
||||||
values are stringified for objects. It can be a
|
|
||||||
function or an array of strings.
|
|
||||||
|
|
||||||
space an optional parameter that specifies the indentation
|
|
||||||
of nested structures. If it is omitted, the text will
|
|
||||||
be packed without extra whitespace. If it is a number,
|
|
||||||
it will specify the number of spaces to indent at each
|
|
||||||
level. If it is a string (such as '\t' or ' '),
|
|
||||||
it contains the characters used to indent at each level.
|
|
||||||
|
|
||||||
This method produces a JSON text from a JavaScript value.
|
|
||||||
|
|
||||||
When an object value is found, if the object contains a toJSON
|
|
||||||
method, its toJSON method will be called and the result will be
|
|
||||||
stringified. A toJSON method does not serialize: it returns the
|
|
||||||
value represented by the name/value pair that should be serialized,
|
|
||||||
or undefined if nothing should be serialized. The toJSON method
|
|
||||||
will be passed the key associated with the value, and this will be
|
|
||||||
bound to the value
|
|
||||||
|
|
||||||
For example, this would serialize Dates as ISO strings.
|
|
||||||
|
|
||||||
Date.prototype.toJSON = function (key) {
|
|
||||||
function f(n) {
|
|
||||||
// Format integers to have at least two digits.
|
|
||||||
return n < 10 ? '0' + n : n;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.getUTCFullYear() + '-' +
|
|
||||||
f(this.getUTCMonth() + 1) + '-' +
|
|
||||||
f(this.getUTCDate()) + 'T' +
|
|
||||||
f(this.getUTCHours()) + ':' +
|
|
||||||
f(this.getUTCMinutes()) + ':' +
|
|
||||||
f(this.getUTCSeconds()) + 'Z';
|
|
||||||
};
|
|
||||||
|
|
||||||
You can provide an optional replacer method. It will be passed the
|
|
||||||
key and value of each member, with this bound to the containing
|
|
||||||
object. The value that is returned from your method will be
|
|
||||||
serialized. If your method returns undefined, then the member will
|
|
||||||
be excluded from the serialization.
|
|
||||||
|
|
||||||
If the replacer parameter is an array of strings, then it will be
|
|
||||||
used to select the members to be serialized. It filters the results
|
|
||||||
such that only members with keys listed in the replacer array are
|
|
||||||
stringified.
|
|
||||||
|
|
||||||
Values that do not have JSON representations, such as undefined or
|
|
||||||
functions, will not be serialized. Such values in objects will be
|
|
||||||
dropped; in arrays they will be replaced with null. You can use
|
|
||||||
a replacer function to replace those with JSON values.
|
|
||||||
JSON.stringify(undefined) returns undefined.
|
|
||||||
|
|
||||||
The optional space parameter produces a stringification of the
|
|
||||||
value that is filled with line breaks and indentation to make it
|
|
||||||
easier to read.
|
|
||||||
|
|
||||||
If the space parameter is a non-empty string, then that string will
|
|
||||||
be used for indentation. If the space parameter is a number, then
|
|
||||||
the indentation will be that many spaces.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
text = JSON.stringify(['e', {pluribus: 'unum'}]);
|
|
||||||
// text is '["e",{"pluribus":"unum"}]'
|
|
||||||
|
|
||||||
|
|
||||||
text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
|
|
||||||
// text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
|
|
||||||
|
|
||||||
text = JSON.stringify([new Date()], function (key, value) {
|
|
||||||
return this[key] instanceof Date ?
|
|
||||||
'Date(' + this[key] + ')' : value;
|
|
||||||
});
|
|
||||||
// text is '["Date(---current time---)"]'
|
|
||||||
|
|
||||||
|
|
||||||
JSON.parse(text, reviver)
|
|
||||||
This method parses a JSON text to produce an object or array.
|
|
||||||
It can throw a SyntaxError exception.
|
|
||||||
|
|
||||||
The optional reviver parameter is a function that can filter and
|
|
||||||
transform the results. It receives each of the keys and values,
|
|
||||||
and its return value is used instead of the original value.
|
|
||||||
If it returns what it received, then the structure is not modified.
|
|
||||||
If it returns undefined then the member is deleted.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
// Parse the text. Values that look like ISO date strings will
|
|
||||||
// be converted to Date objects.
|
|
||||||
|
|
||||||
myData = JSON.parse(text, function (key, value) {
|
|
||||||
var a;
|
|
||||||
if (typeof value === 'string') {
|
|
||||||
a =
|
|
||||||
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
|
|
||||||
if (a) {
|
|
||||||
return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
|
|
||||||
+a[5], +a[6]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
});
|
|
||||||
|
|
||||||
myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
|
|
||||||
var d;
|
|
||||||
if (typeof value === 'string' &&
|
|
||||||
value.slice(0, 5) === 'Date(' &&
|
|
||||||
value.slice(-1) === ')') {
|
|
||||||
d = new Date(value.slice(5, -1));
|
|
||||||
if (d) {
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
This is a reference implementation. You are free to copy, modify, or
|
|
||||||
redistribute.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*jslint evil: true, regexp: true */
|
|
||||||
|
|
||||||
/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
|
|
||||||
call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
|
|
||||||
getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
|
|
||||||
lastIndex, length, parse, prototype, push, replace, slice, stringify,
|
|
||||||
test, toJSON, toString, valueOf
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// Create a JSON object only if one does not already exist. We create the
|
|
||||||
// methods in a closure to avoid creating global variables.
|
|
||||||
|
|
||||||
if (typeof JSON !== 'object') {
|
|
||||||
JSON = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
function f(n) {
|
|
||||||
// Format integers to have at least two digits.
|
|
||||||
return n < 10 ? '0' + n : n;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof Date.prototype.toJSON !== 'function') {
|
|
||||||
|
|
||||||
Date.prototype.toJSON = function () {
|
|
||||||
|
|
||||||
return isFinite(this.valueOf())
|
|
||||||
? this.getUTCFullYear() + '-' +
|
|
||||||
f(this.getUTCMonth() + 1) + '-' +
|
|
||||||
f(this.getUTCDate()) + 'T' +
|
|
||||||
f(this.getUTCHours()) + ':' +
|
|
||||||
f(this.getUTCMinutes()) + ':' +
|
|
||||||
f(this.getUTCSeconds()) + 'Z'
|
|
||||||
: null;
|
|
||||||
};
|
|
||||||
|
|
||||||
String.prototype.toJSON =
|
|
||||||
Number.prototype.toJSON =
|
|
||||||
Boolean.prototype.toJSON = function () {
|
|
||||||
return this.valueOf();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var cx,
|
|
||||||
escapable,
|
|
||||||
gap,
|
|
||||||
indent,
|
|
||||||
meta,
|
|
||||||
rep;
|
|
||||||
|
|
||||||
|
|
||||||
function quote(string) {
|
|
||||||
|
|
||||||
// If the string contains no control characters, no quote characters, and no
|
|
||||||
// backslash characters, then we can safely slap some quotes around it.
|
|
||||||
// Otherwise we must also replace the offending characters with safe escape
|
|
||||||
// sequences.
|
|
||||||
|
|
||||||
escapable.lastIndex = 0;
|
|
||||||
return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
|
|
||||||
var c = meta[a];
|
|
||||||
return typeof c === 'string'
|
|
||||||
? c
|
|
||||||
: '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
|
||||||
}) + '"' : '"' + string + '"';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function str(key, holder) {
|
|
||||||
|
|
||||||
// Produce a string from holder[key].
|
|
||||||
|
|
||||||
var i, // The loop counter.
|
|
||||||
k, // The member key.
|
|
||||||
v, // The member value.
|
|
||||||
length,
|
|
||||||
mind = gap,
|
|
||||||
partial,
|
|
||||||
value = holder[key];
|
|
||||||
|
|
||||||
// If the value has a toJSON method, call it to obtain a replacement value.
|
|
||||||
|
|
||||||
if (value && typeof value === 'object' &&
|
|
||||||
typeof value.toJSON === 'function') {
|
|
||||||
value = value.toJSON(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we were called with a replacer function, then call the replacer to
|
|
||||||
// obtain a replacement value.
|
|
||||||
|
|
||||||
if (typeof rep === 'function') {
|
|
||||||
value = rep.call(holder, key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// What happens next depends on the value's type.
|
|
||||||
|
|
||||||
switch (typeof value) {
|
|
||||||
case 'string':
|
|
||||||
return quote(value);
|
|
||||||
|
|
||||||
case 'number':
|
|
||||||
|
|
||||||
// JSON numbers must be finite. Encode non-finite numbers as null.
|
|
||||||
|
|
||||||
return isFinite(value) ? String(value) : 'null';
|
|
||||||
|
|
||||||
case 'boolean':
|
|
||||||
case 'null':
|
|
||||||
|
|
||||||
// If the value is a boolean or null, convert it to a string. Note:
|
|
||||||
// typeof null does not produce 'null'. The case is included here in
|
|
||||||
// the remote chance that this gets fixed someday.
|
|
||||||
|
|
||||||
return String(value);
|
|
||||||
|
|
||||||
// If the type is 'object', we might be dealing with an object or an array or
|
|
||||||
// null.
|
|
||||||
|
|
||||||
case 'object':
|
|
||||||
|
|
||||||
// Due to a specification blunder in ECMAScript, typeof null is 'object',
|
|
||||||
// so watch out for that case.
|
|
||||||
|
|
||||||
if (!value) {
|
|
||||||
return 'null';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make an array to hold the partial results of stringifying this object value.
|
|
||||||
|
|
||||||
gap += indent;
|
|
||||||
partial = [];
|
|
||||||
|
|
||||||
// Is the value an array?
|
|
||||||
|
|
||||||
if (Object.prototype.toString.apply(value) === '[object Array]') {
|
|
||||||
|
|
||||||
// The value is an array. Stringify every element. Use null as a placeholder
|
|
||||||
// for non-JSON values.
|
|
||||||
|
|
||||||
length = value.length;
|
|
||||||
for (i = 0; i < length; i += 1) {
|
|
||||||
partial[i] = str(i, value) || 'null';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Join all of the elements together, separated with commas, and wrap them in
|
|
||||||
// brackets.
|
|
||||||
|
|
||||||
v = partial.length === 0
|
|
||||||
? '[]'
|
|
||||||
: gap
|
|
||||||
? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
|
|
||||||
: '[' + partial.join(',') + ']';
|
|
||||||
gap = mind;
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the replacer is an array, use it to select the members to be stringified.
|
|
||||||
|
|
||||||
if (rep && typeof rep === 'object') {
|
|
||||||
length = rep.length;
|
|
||||||
for (i = 0; i < length; i += 1) {
|
|
||||||
if (typeof rep[i] === 'string') {
|
|
||||||
k = rep[i];
|
|
||||||
v = str(k, value);
|
|
||||||
if (v) {
|
|
||||||
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// Otherwise, iterate through all of the keys in the object.
|
|
||||||
|
|
||||||
for (k in value) {
|
|
||||||
if (Object.prototype.hasOwnProperty.call(value, k)) {
|
|
||||||
v = str(k, value);
|
|
||||||
if (v) {
|
|
||||||
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Join all of the member texts together, separated with commas,
|
|
||||||
// and wrap them in braces.
|
|
||||||
|
|
||||||
v = partial.length === 0
|
|
||||||
? '{}'
|
|
||||||
: gap
|
|
||||||
? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
|
|
||||||
: '{' + partial.join(',') + '}';
|
|
||||||
gap = mind;
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the JSON object does not yet have a stringify method, give it one.
|
|
||||||
|
|
||||||
if (typeof JSON.stringify !== 'function') {
|
|
||||||
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
|
|
||||||
meta = { // table of character substitutions
|
|
||||||
'\b': '\\b',
|
|
||||||
'\t': '\\t',
|
|
||||||
'\n': '\\n',
|
|
||||||
'\f': '\\f',
|
|
||||||
'\r': '\\r',
|
|
||||||
'"' : '\\"',
|
|
||||||
'\\': '\\\\'
|
|
||||||
};
|
|
||||||
JSON.stringify = function (value, replacer, space) {
|
|
||||||
|
|
||||||
// The stringify method takes a value and an optional replacer, and an optional
|
|
||||||
// space parameter, and returns a JSON text. The replacer can be a function
|
|
||||||
// that can replace values, or an array of strings that will select the keys.
|
|
||||||
// A default replacer method can be provided. Use of the space parameter can
|
|
||||||
// produce text that is more easily readable.
|
|
||||||
|
|
||||||
var i;
|
|
||||||
gap = '';
|
|
||||||
indent = '';
|
|
||||||
|
|
||||||
// If the space parameter is a number, make an indent string containing that
|
|
||||||
// many spaces.
|
|
||||||
|
|
||||||
if (typeof space === 'number') {
|
|
||||||
for (i = 0; i < space; i += 1) {
|
|
||||||
indent += ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the space parameter is a string, it will be used as the indent string.
|
|
||||||
|
|
||||||
} else if (typeof space === 'string') {
|
|
||||||
indent = space;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there is a replacer, it must be a function or an array.
|
|
||||||
// Otherwise, throw an error.
|
|
||||||
|
|
||||||
rep = replacer;
|
|
||||||
if (replacer && typeof replacer !== 'function' &&
|
|
||||||
(typeof replacer !== 'object' ||
|
|
||||||
typeof replacer.length !== 'number')) {
|
|
||||||
throw new Error('JSON.stringify');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make a fake root object containing our value under the key of ''.
|
|
||||||
// Return the result of stringifying the value.
|
|
||||||
|
|
||||||
return str('', {'': value});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// If the JSON object does not yet have a parse method, give it one.
|
|
||||||
|
|
||||||
if (typeof JSON.parse !== 'function') {
|
|
||||||
cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
|
|
||||||
JSON.parse = function (text, reviver) {
|
|
||||||
|
|
||||||
// The parse method takes a text and an optional reviver function, and returns
|
|
||||||
// a JavaScript value if the text is a valid JSON text.
|
|
||||||
|
|
||||||
var j;
|
|
||||||
|
|
||||||
function walk(holder, key) {
|
|
||||||
|
|
||||||
// The walk method is used to recursively walk the resulting structure so
|
|
||||||
// that modifications can be made.
|
|
||||||
|
|
||||||
var k, v, value = holder[key];
|
|
||||||
if (value && typeof value === 'object') {
|
|
||||||
for (k in value) {
|
|
||||||
if (Object.prototype.hasOwnProperty.call(value, k)) {
|
|
||||||
v = walk(value, k);
|
|
||||||
if (v !== undefined) {
|
|
||||||
value[k] = v;
|
|
||||||
} else {
|
|
||||||
delete value[k];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return reviver.call(holder, key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Parsing happens in four stages. In the first stage, we replace certain
|
|
||||||
// Unicode characters with escape sequences. JavaScript handles many characters
|
|
||||||
// incorrectly, either silently deleting them, or treating them as line endings.
|
|
||||||
|
|
||||||
text = String(text);
|
|
||||||
cx.lastIndex = 0;
|
|
||||||
if (cx.test(text)) {
|
|
||||||
text = text.replace(cx, function (a) {
|
|
||||||
return '\\u' +
|
|
||||||
('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// In the second stage, we run the text against regular expressions that look
|
|
||||||
// for non-JSON patterns. We are especially concerned with '()' and 'new'
|
|
||||||
// because they can cause invocation, and '=' because it can cause mutation.
|
|
||||||
// But just to be safe, we want to reject all unexpected forms.
|
|
||||||
|
|
||||||
// We split the second stage into 4 regexp operations in order to work around
|
|
||||||
// crippling inefficiencies in IE's and Safari's regexp engines. First we
|
|
||||||
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
|
|
||||||
// replace all simple value tokens with ']' characters. Third, we delete all
|
|
||||||
// open brackets that follow a colon or comma or that begin the text. Finally,
|
|
||||||
// we look to see that the remaining characters are only whitespace or ']' or
|
|
||||||
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
|
|
||||||
|
|
||||||
if (/^[\],:{}\s]*$/
|
|
||||||
.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
|
|
||||||
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
|
|
||||||
.replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
|
|
||||||
|
|
||||||
// In the third stage we use the eval function to compile the text into a
|
|
||||||
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
|
|
||||||
// in JavaScript: it can begin a block or an object literal. We wrap the text
|
|
||||||
// in parens to eliminate the ambiguity.
|
|
||||||
|
|
||||||
j = eval('(' + text + ')');
|
|
||||||
|
|
||||||
// In the optional fourth stage, we recursively walk the new structure, passing
|
|
||||||
// each name/value pair to a reviver function for possible transformation.
|
|
||||||
|
|
||||||
return typeof reviver === 'function'
|
|
||||||
? walk({'': j}, '')
|
|
||||||
: j;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the text is not JSON parseable, then a SyntaxError is thrown.
|
|
||||||
|
|
||||||
throw new SyntaxError('JSON.parse');
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}());
|
|
||||||
Vendored
+9
@@ -1,5 +1,14 @@
|
|||||||
require('./common');
|
require('./common');
|
||||||
|
|
||||||
|
var chai = require('chai');
|
||||||
|
var dirtyChai = require('dirty-chai');
|
||||||
|
|
||||||
|
|
||||||
|
chai.use(dirtyChai);
|
||||||
|
global.expect = chai.expect;
|
||||||
|
|
||||||
|
global.sinon = require('sinon');
|
||||||
|
|
||||||
global.Handlebars = require('../../lib');
|
global.Handlebars = require('../../lib');
|
||||||
|
|
||||||
global.CompilerContext = {
|
global.CompilerContext = {
|
||||||
|
|||||||
Vendored
+6
-2
@@ -15,7 +15,7 @@ if (grep === '--min') {
|
|||||||
|
|
||||||
var files = fs.readdirSync(testDir)
|
var files = fs.readdirSync(testDir)
|
||||||
.filter(function(name) { return (/.*\.js$/).test(name); })
|
.filter(function(name) { return (/.*\.js$/).test(name); })
|
||||||
.map(function(name) { return testDir + '/' + name; });
|
.map(function(name) { return testDir + path.sep + name; });
|
||||||
|
|
||||||
if (global.minimizedTest) {
|
if (global.minimizedTest) {
|
||||||
run('./runtime', function() {
|
run('./runtime', function() {
|
||||||
@@ -39,7 +39,11 @@ if (global.minimizedTest) {
|
|||||||
|
|
||||||
|
|
||||||
function run(env, callback) {
|
function run(env, callback) {
|
||||||
|
if (errors > 0) {
|
||||||
|
throw new Error('Errors have been detected before');
|
||||||
|
}
|
||||||
var mocha = new Mocha();
|
var mocha = new Mocha();
|
||||||
|
mocha.bail();
|
||||||
mocha.ui('bdd');
|
mocha.ui('bdd');
|
||||||
mocha.files = files.slice();
|
mocha.files = files.slice();
|
||||||
if (grep) {
|
if (grep) {
|
||||||
@@ -54,6 +58,6 @@ function run(env, callback) {
|
|||||||
require(env);
|
require(env);
|
||||||
mocha.run(function(errorCount) {
|
mocha.run(function(errorCount) {
|
||||||
errors += errorCount;
|
errors += errorCount;
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+9
@@ -3,6 +3,15 @@ require('./common');
|
|||||||
var fs = require('fs'),
|
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;
|
||||||
|
|
||||||
|
global.sinon = require('sinon');
|
||||||
|
|
||||||
global.Handlebars = 'no-conflict';
|
global.Handlebars = 'no-conflict';
|
||||||
|
|
||||||
var filename = 'dist/handlebars.runtime.js';
|
var filename = 'dist/handlebars.runtime.js';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
define(['handlebars.runtime'], function(Handlebars) {
|
define(['handlebars.runtime'], function(Handlebars) {
|
||||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||||
return templates['empty'] = template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
|
return templates['empty'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||||
return "";
|
return "";
|
||||||
},"useData":true});
|
},"useData":true});
|
||||||
});
|
});
|
||||||
|
|||||||
+103
-8
@@ -28,14 +28,43 @@ describe('helpers', function() {
|
|||||||
'raw block helper gets raw content');
|
'raw block helper gets raw content');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('helper for nested raw block gets raw content', function() {
|
describe('raw block parsing (with identity helper-function)', function() {
|
||||||
var string = '{{{{a}}}} {{{{b}}}} {{{{/b}}}} {{{{/a}}}}';
|
|
||||||
var helpers = {
|
function runWithIdentityHelper(template, expected) {
|
||||||
a: function(options) {
|
var helpers = {
|
||||||
|
identity: function(options) {
|
||||||
return options.fn();
|
return options.fn();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
shouldCompileTo(string, [{}, helpers], ' {{{{b}}}} {{{{/b}}}} ', 'raw block helper should get nested raw block as raw content');
|
shouldCompileTo(template, [{}, helpers], expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
it('helper for nested raw block gets raw content', function() {
|
||||||
|
runWithIdentityHelper('{{{{identity}}}} {{{{b}}}} {{{{/b}}}} {{{{/identity}}}}', ' {{{{b}}}} {{{{/b}}}} ');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('helper for nested raw block works with empty content', function() {
|
||||||
|
runWithIdentityHelper('{{{{identity}}}}{{{{/identity}}}}', '');
|
||||||
|
});
|
||||||
|
|
||||||
|
xit('helper for nested raw block works if nested raw blocks are broken', function() {
|
||||||
|
// This test was introduced in 4.4.4, but it was not the actual problem that lead to the patch release
|
||||||
|
// 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}}}} {{{{ {{{{/ }}}} }}}} ');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('helper for nested raw block closes after first matching close', function() {
|
||||||
|
runWithIdentityHelper('{{{{identity}}}}abc{{{{/identity}}}} {{{{identity}}}}abc{{{{/identity}}}}', 'abc abc');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('helper for nested raw block throw exception when with missing closing braces', function() {
|
||||||
|
var string = '{{{{a}}}} {{{{/a';
|
||||||
|
shouldThrow(function() {
|
||||||
|
Handlebars.compile(string)();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('helper block with identical context', function() {
|
it('helper block with identical context', function() {
|
||||||
@@ -113,7 +142,7 @@ describe('helpers', function() {
|
|||||||
{ 'name': 'Yehuda', 'id': 2 }
|
{ 'name': 'Yehuda', 'id': 2 }
|
||||||
]};
|
]};
|
||||||
|
|
||||||
shouldCompileTo(source, [data, {link: link}], '<ul><li><a href=\"/people/1\">Alan</a></li><li><a href=\"/people/2\">Yehuda</a></li></ul>');
|
shouldCompileTo(source, [data, {link: link}], '<ul><li><a href="/people/1">Alan</a></li><li><a href="/people/2">Yehuda</a></li></ul>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('block helper for undefined value', function() {
|
it('block helper for undefined value', function() {
|
||||||
@@ -737,4 +766,70 @@ describe('helpers', function() {
|
|||||||
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/);
|
||||||
|
});
|
||||||
|
|
||||||
|
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/);
|
||||||
|
});
|
||||||
|
|
||||||
|
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/);
|
||||||
|
});
|
||||||
|
|
||||||
|
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/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('unless helper - too few arguments', function() {
|
||||||
|
var template = CompilerContext.compile('{{#unless}}{{/unless}}');
|
||||||
|
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/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('with helper - too few arguments', function() {
|
||||||
|
var template = CompilerContext.compile('{{#with}}{{/with}}');
|
||||||
|
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/);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+4
-2
@@ -19,13 +19,15 @@
|
|||||||
document.documentElement.className = 'headless';
|
document.documentElement.className = 'headless';
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<script src="/node_modules/sinon/pkg/sinon.js"></script>
|
||||||
|
<script src="/node_modules/chai/chai.js"></script>
|
||||||
|
<script src="/node_modules/dirty-chai/lib/dirty-chai.js"></script>
|
||||||
<script src="/node_modules/mocha/mocha.js"></script>
|
<script src="/node_modules/mocha/mocha.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
window.expect = chai.expect;
|
||||||
mocha.setup('bdd');
|
mocha.setup('bdd');
|
||||||
</script>
|
</script>
|
||||||
<script src="/dist/handlebars.js"></script>
|
<script src="/dist/handlebars.js"></script>
|
||||||
|
|
||||||
<script src="/spec/env/json2.js"></script>
|
|
||||||
<script src="/spec/env/common.js"></script>
|
<script src="/spec/env/common.js"></script>
|
||||||
<script>
|
<script>
|
||||||
var CompilerContext = {
|
var CompilerContext = {
|
||||||
|
|||||||
+6
-6
@@ -51,7 +51,7 @@ describe('parser', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('parses mustaches with string parameters', 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() {
|
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 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"}}'), '{{ 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=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=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" baz=false}}'), '{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam", baz=BOOLEAN{false}} }}\n');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('parses contents followed by a mustache', function() {
|
it('parses contents followed by a mustache', function() {
|
||||||
@@ -136,7 +136,7 @@ describe('parser', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('parses a multi-line comment', 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() {
|
it('parses an inverse section', function() {
|
||||||
|
|||||||
@@ -263,6 +263,15 @@ describe('partials', function() {
|
|||||||
true,
|
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: '<code>before-block: {{@root/value}} {{> @partial-block }}</code>'}],
|
||||||
|
true,
|
||||||
|
'<code>before-block: success in-block: success</code>');
|
||||||
|
});
|
||||||
|
|
||||||
it('should allow the #each-helper to be used along with partial-blocks', function() {
|
it('should allow the #each-helper to be used along with partial-blocks', function() {
|
||||||
shouldCompileToWithPartials(
|
shouldCompileToWithPartials(
|
||||||
'<template>{{#> list value}}value = {{.}}{{/list}}</template>',
|
'<template>{{#> list value}}value = {{.}}{{/list}}</template>',
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ describe('precompiler', function() {
|
|||||||
|
|
||||||
var log,
|
var log,
|
||||||
logFunction,
|
logFunction,
|
||||||
|
errorLog,
|
||||||
|
errorLogFunction,
|
||||||
|
|
||||||
precompile,
|
precompile,
|
||||||
minify,
|
minify,
|
||||||
@@ -26,16 +28,51 @@ describe('precompiler', function() {
|
|||||||
content,
|
content,
|
||||||
writeFileSync;
|
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() {
|
beforeEach(function() {
|
||||||
precompile = Handlebars.precompile;
|
precompile = Handlebars.precompile;
|
||||||
minify = uglify.minify;
|
minify = uglify.minify;
|
||||||
writeFileSync = fs.writeFileSync;
|
writeFileSync = fs.writeFileSync;
|
||||||
|
|
||||||
|
// Mock stdout and stderr
|
||||||
logFunction = console.log;
|
logFunction = console.log;
|
||||||
log = '';
|
log = '';
|
||||||
console.log = function() {
|
console.log = function() {
|
||||||
log += Array.prototype.join.call(arguments, '');
|
log += Array.prototype.join.call(arguments, '');
|
||||||
};
|
};
|
||||||
|
errorLogFunction = console.error;
|
||||||
|
errorLog = '';
|
||||||
|
console.error = function() {
|
||||||
|
errorLog += Array.prototype.join.call(arguments, '');
|
||||||
|
};
|
||||||
|
|
||||||
fs.writeFileSync = function(_file, _content) {
|
fs.writeFileSync = function(_file, _content) {
|
||||||
file = _file;
|
file = _file;
|
||||||
content = _content;
|
content = _content;
|
||||||
@@ -46,6 +83,7 @@ describe('precompiler', function() {
|
|||||||
uglify.minify = minify;
|
uglify.minify = minify;
|
||||||
fs.writeFileSync = writeFileSync;
|
fs.writeFileSync = writeFileSync;
|
||||||
console.log = logFunction;
|
console.log = logFunction;
|
||||||
|
console.error = errorLogFunction;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should output version', function() {
|
it('should output version', function() {
|
||||||
@@ -148,6 +186,29 @@ describe('precompiler', function() {
|
|||||||
equal(log, 'min');
|
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() {
|
it('should output map', function() {
|
||||||
Precompiler.cli({templates: [emptyTemplate], map: 'foo.js.map'});
|
Precompiler.cli({templates: [emptyTemplate], map: 'foo.js.map'});
|
||||||
|
|
||||||
|
|||||||
+45
-2
@@ -211,12 +211,12 @@ describe('Regressions', function() {
|
|||||||
// It's valid to execute a block against an undefined context, but
|
// It's valid to execute a block against an undefined context, but
|
||||||
// helpers can not do so, so we expect to have an empty object here;
|
// helpers can not do so, so we expect to have an empty object here;
|
||||||
for (var name in this) {
|
for (var name in this) {
|
||||||
if (this.hasOwnProperty(name)) {
|
if (Object.prototype.hasOwnProperty.call(this, name)) {
|
||||||
return 'found';
|
return 'found';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// And to make IE happy, check for the known string as length is not enumerated.
|
// 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');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -291,4 +291,47 @@ describe('Regressions', function() {
|
|||||||
};
|
};
|
||||||
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();
|
||||||
|
|
||||||
|
registerTemplate(newHandlebarsInstance);
|
||||||
|
newHandlebarsInstance.registerHelper('loud', function(value) {
|
||||||
|
return value.toUpperCase();
|
||||||
|
});
|
||||||
|
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"');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow hash with protected array names', function() {
|
||||||
|
var obj = {array: [1], name: 'John'};
|
||||||
|
var helpers = {
|
||||||
|
helpa: function(options) {
|
||||||
|
return options.hash.length;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
shouldCompileTo('{{helpa length="foo"}}', [obj, helpers], 'foo');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+1
-1
@@ -21,7 +21,7 @@ describe('runtime', function() {
|
|||||||
shouldThrow(function() {
|
shouldThrow(function() {
|
||||||
Handlebars.template({
|
Handlebars.template({
|
||||||
main: {},
|
main: {},
|
||||||
compiler: [Handlebars.COMPILER_REVISION - 1]
|
compiler: [Handlebars.LAST_COMPATIBLE_COMPILER_REVISION - 1]
|
||||||
});
|
});
|
||||||
}, Error, /Template was precompiled with an older version of Handlebars than the current runtime/);
|
}, Error, /Template was precompiled with an older version of Handlebars than the current runtime/);
|
||||||
shouldThrow(function() {
|
shouldThrow(function() {
|
||||||
|
|||||||
@@ -0,0 +1,144 @@
|
|||||||
|
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';
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
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-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'); }});
|
||||||
|
}).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('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('');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
+2
-2
@@ -25,8 +25,8 @@ describe('spec', function() {
|
|||||||
// We nest the entire response from partials, not just the literals
|
// We nest the entire response from partials, not just the literals
|
||||||
|| (name === 'partials.json' && test.name === 'Standalone Indentation')
|
|| (name === 'partials.json' && test.name === 'Standalone Indentation')
|
||||||
|
|
||||||
|| (/\{\{\=/).test(test.template)
|
|| (/\{\{=/).test(test.template)
|
||||||
|| _.any(test.partials, function(partial) { return (/\{\{\=/).test(partial); })) {
|
|| _.any(test.partials, function(partial) { return (/\{\{=/).test(partial); })) {
|
||||||
it.skip(name + ' - ' + test.name);
|
it.skip(name + ' - ' + test.name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,6 +95,25 @@ describe('strict', function() {
|
|||||||
};
|
};
|
||||||
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');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should error contains correct location properties on missing property lookup', function() {
|
||||||
|
try {
|
||||||
|
var template = CompilerContext.compile('\n\n\n {{hello}}', {strict: true});
|
||||||
|
template({});
|
||||||
|
} catch (error) {
|
||||||
|
equals(error.lineNumber, 4);
|
||||||
|
equals(error.endLineNumber, 4);
|
||||||
|
equals(error.column, 5);
|
||||||
|
equals(error.endColumn, 10);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('assume objects', function() {
|
describe('assume objects', function() {
|
||||||
|
|||||||
+10
-5
@@ -282,13 +282,13 @@ describe('Tokenizer', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes mustaches with String params as "OPEN ID ID STRING CLOSE"', 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']);
|
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'STRING', 'CLOSE']);
|
||||||
shouldBeToken(result[3], 'STRING', 'baz');
|
shouldBeToken(result[3], 'STRING', 'baz');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes mustaches with String params using single quotes as "OPEN ID ID STRING CLOSE"', function() {
|
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']);
|
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'STRING', 'CLOSE']);
|
||||||
shouldBeToken(result[3], 'STRING', 'baz');
|
shouldBeToken(result[3], 'STRING', 'baz');
|
||||||
});
|
});
|
||||||
@@ -365,13 +365,13 @@ describe('Tokenizer', function() {
|
|||||||
result = tokenize('{{ foo bar\n baz=bat }}');
|
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\" }}');
|
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 }}');
|
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\"}}');
|
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');
|
shouldBeToken(result[2], 'ID', 'omg');
|
||||||
});
|
});
|
||||||
@@ -441,4 +441,9 @@ describe('Tokenizer', function() {
|
|||||||
result = tokenize('{{else foo as |bar baz|}}');
|
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']);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,14 +18,15 @@
|
|||||||
document.documentElement.className = 'headless';
|
document.documentElement.className = 'headless';
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<script src="/node_modules/sinon/pkg/sinon.js"></script>
|
||||||
|
<script src="/node_modules/chai/chai.js"></script>
|
||||||
|
<script src="/node_modules/dirty-chai/lib/dirty-chai.js"></script>
|
||||||
<script src="/node_modules/mocha/mocha.js"></script>
|
<script src="/node_modules/mocha/mocha.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
window.expect = chai.expect;
|
||||||
mocha.setup('bdd');
|
mocha.setup('bdd');
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script src="/spec/env/json2.js"></script>
|
|
||||||
<script src="/spec/env/require.js"></script>
|
<script src="/spec/env/require.js"></script>
|
||||||
|
|
||||||
<script src="/spec/env/common.js"></script>
|
<script src="/spec/env/common.js"></script>
|
||||||
<script>
|
<script>
|
||||||
requirejs.config({
|
requirejs.config({
|
||||||
|
|||||||
+5
-3
@@ -18,14 +18,16 @@
|
|||||||
document.documentElement.className = 'headless';
|
document.documentElement.className = 'headless';
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script src="/node_modules/sinon/pkg/sinon.js"></script>
|
||||||
|
<script src="/node_modules/chai/chai.js"></script>
|
||||||
|
<script src="/node_modules/dirty-chai/lib/dirty-chai.js"></script>
|
||||||
<script src="/node_modules/mocha/mocha.js"></script>
|
<script src="/node_modules/mocha/mocha.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
window.expect = chai.expect;
|
||||||
mocha.setup('bdd');
|
mocha.setup('bdd');
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script src="/spec/env/json2.js"></script>
|
|
||||||
<script src="/spec/env/require.js"></script>
|
<script src="/spec/env/require.js"></script>
|
||||||
|
|
||||||
<script src="/spec/env/common.js"></script>
|
<script src="/spec/env/common.js"></script>
|
||||||
<script>
|
<script>
|
||||||
requirejs.config({
|
requirejs.config({
|
||||||
|
|||||||
+3
-3
@@ -4,7 +4,7 @@
|
|||||||
%{
|
%{
|
||||||
|
|
||||||
function strip(start, end) {
|
function strip(start, end) {
|
||||||
return yytext = yytext.substr(start, yyleng-end);
|
return yytext = yytext.substring(start, yyleng - end + start);
|
||||||
}
|
}
|
||||||
|
|
||||||
%}
|
%}
|
||||||
@@ -59,11 +59,11 @@ ID [^\s!"#%-,\.\/;->@\[-\^`\{-~]+/{LOOKAHEAD}
|
|||||||
if (this.conditionStack[this.conditionStack.length-1] === 'raw') {
|
if (this.conditionStack[this.conditionStack.length-1] === 'raw') {
|
||||||
return 'CONTENT';
|
return 'CONTENT';
|
||||||
} else {
|
} else {
|
||||||
yytext = yytext.substr(5, yyleng-9);
|
strip(5, 9);
|
||||||
return 'END_RAW_BLOCK';
|
return 'END_RAW_BLOCK';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<raw>[^\x00]*?/("{{{{") { return 'CONTENT'; }
|
<raw>[^\x00]+?/("{{{{") { return 'CONTENT'; }
|
||||||
|
|
||||||
<com>[\s\S]*?"--"{RIGHT_STRIP}?"}}" {
|
<com>[\s\S]*?"--"{RIGHT_STRIP}?"}}" {
|
||||||
this.popState();
|
this.popState();
|
||||||
|
|||||||
+1
-1
@@ -39,7 +39,7 @@ content
|
|||||||
};
|
};
|
||||||
|
|
||||||
rawBlock
|
rawBlock
|
||||||
: openRawBlock content+ END_RAW_BLOCK -> yy.prepareRawBlock($1, $2, $3, @$)
|
: openRawBlock content* END_RAW_BLOCK -> yy.prepareRawBlock($1, $2, $3, @$)
|
||||||
;
|
;
|
||||||
|
|
||||||
openRawBlock
|
openRawBlock
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
var _ = require('underscore'),
|
var _ = require('underscore'),
|
||||||
async = require('async'),
|
async = require('neo-async'),
|
||||||
metrics = require('../bench');
|
metrics = require('../bench');
|
||||||
|
|
||||||
module.exports = function(grunt) {
|
module.exports = function(grunt) {
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
var _ = require('underscore'),
|
var _ = require('underscore'),
|
||||||
async = require('async'),
|
async = require('neo-async'),
|
||||||
AWS = require('aws-sdk'),
|
AWS = require('aws-sdk'),
|
||||||
git = require('./util/git'),
|
git = require('./util/git'),
|
||||||
semver = require('semver');
|
semver = require('semver');
|
||||||
@@ -66,7 +66,7 @@ module.exports = function(grunt) {
|
|||||||
var s3 = new AWS.S3(),
|
var s3 = new AWS.S3(),
|
||||||
bucket = process.env.S3_BUCKET_NAME;
|
bucket = process.env.S3_BUCKET_NAME;
|
||||||
|
|
||||||
async.forEach(_.keys(files), function(file, callback) {
|
async.each(_.keys(files), function(file, callback) {
|
||||||
var params = {Bucket: bucket, Key: file, Body: grunt.file.read(files[file])};
|
var params = {Bucket: bucket, Key: file, Body: grunt.file.read(files[file])};
|
||||||
s3.putObject(params, function(err) {
|
s3.putObject(params, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|||||||
+10
-4
@@ -6,9 +6,15 @@ module.exports = function(grunt) {
|
|||||||
grunt.registerTask('test:bin', function() {
|
grunt.registerTask('test:bin', function() {
|
||||||
var done = this.async();
|
var done = this.async();
|
||||||
|
|
||||||
|
var cmd = './bin/handlebars';
|
||||||
|
var args = [ '-a', 'spec/artifacts/empty.handlebars' ];
|
||||||
|
|
||||||
// On Windows, the executable handlebars.js file cannot be run directly
|
// On Windows, the executable handlebars.js file cannot be run directly
|
||||||
var prefix = os.type().match(/^Windows/) ? process.argv[0] : '';
|
if (os.platform() === 'win32') {
|
||||||
childProcess.exec(prefix + ' ./bin/handlebars -a spec/artifacts/empty.handlebars', function(err, stdout) {
|
args.unshift(cmd);
|
||||||
|
cmd = process.argv[0];
|
||||||
|
}
|
||||||
|
childProcess.execFile(cmd, args, function(err, stdout) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
@@ -27,7 +33,7 @@ module.exports = function(grunt) {
|
|||||||
|
|
||||||
var runner = childProcess.fork('./spec/env/runner', [], {stdio: 'inherit'});
|
var runner = childProcess.fork('./spec/env/runner', [], {stdio: 'inherit'});
|
||||||
runner.on('close', function(code) {
|
runner.on('close', function(code) {
|
||||||
if (code != 0) {
|
if (code !== 0) {
|
||||||
grunt.fatal(code + ' tests failed');
|
grunt.fatal(code + ' tests failed');
|
||||||
}
|
}
|
||||||
done();
|
done();
|
||||||
@@ -49,7 +55,7 @@ module.exports = function(grunt) {
|
|||||||
|
|
||||||
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) {
|
runner.on('close', function(code) {
|
||||||
if (code != 0) {
|
if (code !== 0) {
|
||||||
grunt.fatal(code + ' tests failed');
|
grunt.fatal(code + ' tests failed');
|
||||||
}
|
}
|
||||||
done();
|
done();
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user