Compare commits
210 Commits
4.x
...
precompile-esm
| Author | SHA1 | Date | |
|---|---|---|---|
| 88f789e419 | |||
| 8ce2be4496 | |||
| d3b93571a7 | |||
| 99d162cadc | |||
| 7147755b55 | |||
| 9413ea99ce | |||
| f202c501a3 | |||
| bb55dead2d | |||
| 6e10b1f658 | |||
| 65f24eb8b6 | |||
| 35f001862c | |||
| b3ffb16e5d | |||
| 42adee3f82 | |||
| b4a54ad86e | |||
| e6686963ba | |||
| 262b84b08a | |||
| 6186128ad0 | |||
| 03fe783a69 | |||
| e534a911f3 | |||
| f6ff3bf52b | |||
| da41887377 | |||
| 83baaa469f | |||
| 785a63e0a8 | |||
| 903ca504b5 | |||
| 69dbe65c19 | |||
| 54cbc8c6c6 | |||
| b14ff4a6fc | |||
| e0de1d9a4a | |||
| 9bff03fd3b | |||
| a9fe6d5a03 | |||
| ce8982fe55 | |||
| 80809ef7fc | |||
| 30bd78d457 | |||
| 0896d00c2e | |||
| f73be4ebf3 | |||
| 29ed243268 | |||
| dd6ff05079 | |||
| c18ed16f71 | |||
| 7e5890cf5a | |||
| c7278d176a | |||
| 35ea8e61bf | |||
| a98b01c2ee | |||
| b3b26712d9 | |||
| 37f4ea18b4 | |||
| 48870b41f7 | |||
| 82c613244a | |||
| 4da5882660 | |||
| 476d61667d | |||
| 5ba0c72fe6 | |||
| 49621c15ac | |||
| 1dc2c0bffb | |||
| fe58756da2 | |||
| b0f1a62ddb | |||
| 7d9450d3c3 | |||
| e538c29d92 | |||
| 6790c080c6 | |||
| 2f509a8fca | |||
| 19bdace85a | |||
| 071003b89c | |||
| 1a08e1d0a7 | |||
| 212f9d930b | |||
| a65a55382c | |||
| b1034a6702 | |||
| 8727d2015a | |||
| a39bfa5fe3 | |||
| 75794c5dbc | |||
| 3438891bd2 | |||
| 0ec71dcc5c | |||
| 6f17a8e75c | |||
| f8eade7b41 | |||
| d36adf9343 | |||
| 2fd4a3da7a | |||
| 3800b7dd4c | |||
| 2ea32e8db9 | |||
| fd3ca85d13 | |||
| edfe6b899c | |||
| 0c8230c253 | |||
| d23ccf636a | |||
| 8a062f19d5 | |||
| 44ee971e44 | |||
| 37411901da | |||
| dfe0fcb130 | |||
| 3fb331ef40 | |||
| 2f0991a022 | |||
| 4085e066fc | |||
| dce6f54fdf | |||
| 7b11f55c53 | |||
| b0bb2a42b4 | |||
| e46c239b3d | |||
| 081e1d0ad9 | |||
| 2ef6fbd1fc | |||
| 50e844b563 | |||
| c56995d706 | |||
| 566536b03d | |||
| 6aaac4a779 | |||
| 4ae86f3282 | |||
| 25bae31578 | |||
| 74795a3a7f | |||
| 0419daec52 | |||
| 8df74a0119 | |||
| 35d3fdb1dd | |||
| dce44541db | |||
| a510d82252 | |||
| 77e38330e6 | |||
| 3ce4425056 | |||
| 2f8aa1c839 | |||
| 2f681d256f | |||
| 1b04524a53 | |||
| 3fc7e6feb2 | |||
| 16c654579a | |||
| f349aa43c0 | |||
| 913b845aa9 | |||
| 920bf1c5ad | |||
| d1eca1c5cf | |||
| d7d14ed6e5 | |||
| 379172e236 | |||
| 148b19182d | |||
| e1fa310101 | |||
| 94419deb95 | |||
| 63be1c8130 | |||
| 7f3da649c0 | |||
| f21f900cf5 | |||
| 999220bf2d | |||
| d5caa56d6c | |||
| ce3cd8a685 | |||
| 33773c2cb2 | |||
| 7b74175e0e | |||
| c933387415 | |||
| 1a78717286 | |||
| 64002dc506 | |||
| fb26f69485 | |||
| be107d2ca1 | |||
| 2441ba1335 | |||
| 5c676a32fb | |||
| 228950169d | |||
| 680ec9666a | |||
| 58ec656fbc | |||
| 77674e4f6c | |||
| 714a4c4482 | |||
| d40cbfc70d | |||
| b6e2f76a45 | |||
| 1b78b5b7b2 | |||
| a5e6afb05d | |||
| 8baba76b64 | |||
| 508347ea94 | |||
| ad341bfc1b | |||
| 911e676af4 | |||
| 0fa10b6e87 | |||
| 660a117536 | |||
| c9970200a0 | |||
| 6300e57f11 | |||
| 6c89939af7 | |||
| a6476739f0 | |||
| 9119265204 | |||
| d117b3e0f9 | |||
| 2b7ed7e0bb | |||
| 9a36966b16 | |||
| 6c92579faf | |||
| 7535e48a79 | |||
| 11b99b361d | |||
| 275ab37b2e | |||
| bff3af52f2 | |||
| 9fb7baddcc | |||
| 21696005cd | |||
| 601ce8a69f | |||
| bbe0a94d6e | |||
| f2211ddd65 | |||
| e8722e2551 | |||
| 094a2564fe | |||
| a6a0e50551 | |||
| 5c9d5b5b54 | |||
| f0d4159a5e | |||
| 2ff795cf2f | |||
| e8ceafa652 | |||
| 6b644802d9 | |||
| 768ddbd661 | |||
| 06baeaebfd | |||
| c7be766902 | |||
| b37232fc8d | |||
| 0757843712 | |||
| 27212f5953 | |||
| 7130a11539 | |||
| 4788b03192 | |||
| 0b8fd1d8a8 | |||
| 89c8b3a886 | |||
| b55a120e82 | |||
| dfc75545fa | |||
| 400bada31f | |||
| 834695e241 | |||
| 5302782c08 | |||
| 118836f390 | |||
| ee6fadffb4 | |||
| ec64cf9293 | |||
| 9a94d1771f | |||
| 164f1c13f7 | |||
| 8517352e20 | |||
| 63fdb92472 | |||
| 8a9c79b799 | |||
| eb1ec52b45 | |||
| 21bf95c92a | |||
| cc0b239aaf | |||
| a6121cae79 | |||
| 871c32a6f9 | |||
| 326734b0f3 | |||
| 2ea6119a83 | |||
| 8289c0bf3a | |||
| 25458fdc2a | |||
| 63a08890cc | |||
| 0f054efcdb | |||
| 3f77b82ed0 |
@@ -11,7 +11,6 @@ node_modules
|
|||||||
.nyc_output
|
.nyc_output
|
||||||
|
|
||||||
# Generated files
|
# Generated files
|
||||||
lib/handlebars/compiler/parser.js
|
|
||||||
/coverage/
|
/coverage/
|
||||||
/dist/
|
/dist/
|
||||||
/tests/integration/*/dist/
|
/tests/integration/*/dist/
|
||||||
|
|||||||
+6
-8
@@ -1,11 +1,14 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
extends: ['eslint:recommended', 'plugin:compat/recommended', 'prettier'],
|
extends: ['eslint:recommended', 'plugin:compat/recommended', 'prettier'],
|
||||||
globals: {
|
globals: {
|
||||||
self: false
|
self: false,
|
||||||
},
|
},
|
||||||
env: {
|
env: {
|
||||||
node: true,
|
node: true,
|
||||||
es6: true
|
es2020: true,
|
||||||
|
},
|
||||||
|
parserOptions: {
|
||||||
|
sourceType: 'module',
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
'no-console': 'warn',
|
'no-console': 'warn',
|
||||||
@@ -56,11 +59,6 @@ module.exports = {
|
|||||||
|
|
||||||
// ECMAScript 6 //
|
// ECMAScript 6 //
|
||||||
//--------------//
|
//--------------//
|
||||||
'no-var': 'error'
|
'no-var': 'error',
|
||||||
},
|
},
|
||||||
parserOptions: {
|
|
||||||
sourceType: 'module',
|
|
||||||
ecmaVersion: 6,
|
|
||||||
ecmaFeatures: {}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# Upgrade to Prettier 2.7
|
||||||
|
3d228334530860a6e3f99dc10777c84bf22292c1
|
||||||
|
# Format markdown files with Prettier
|
||||||
|
dfe2eaaf20f0b679d94e5a799757c4394d80f1cc
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
Before filing issues, please check the following points first:
|
||||||
|
|
||||||
|
- [ ] Please don't open issues for security issues. Instead, file a report at https://www.npmjs.com/advisories/report?package=handlebars
|
||||||
|
- [ ] Have a look at https://github.com/handlebars-lang/handlebars.js/blob/master/CONTRIBUTING.md
|
||||||
|
- [ ] Read the FAQ at https://github.com/handlebars-lang/handlebars.js/blob/master/FAQ.md
|
||||||
|
- [ ] Use the jsfiddle-template at https://jsfiddle.net/4nbwjaqz/4/ to reproduce problems or bugs
|
||||||
|
|
||||||
|
This will probably help you to get a solution faster.
|
||||||
|
For bugs, it would be great to have a PR with a failing test-case.
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
Before creating a pull-request, please check https://github.com/handlebars-lang/handlebars.js/blob/master/CONTRIBUTING.md first.
|
||||||
|
|
||||||
|
Generally we like to see pull requests that
|
||||||
|
|
||||||
|
- [ ] Please don't start pull requests for security issues. Instead, file a report at https://www.npmjs.com/advisories/report?package=handlebars
|
||||||
|
- [ ] Maintain the existing code style
|
||||||
|
- [ ] Are focused on a single change (i.e. avoid large refactoring or style adjustments in untouched code if not the primary goal of the pull request)
|
||||||
|
- [ ] Have good commit messages
|
||||||
|
- [ ] Have tests
|
||||||
|
- [ ] Have the [typings](https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html) (lib/handlebars.d.ts) updated on every API change. If you need help, updating those, please mention that in the PR description.
|
||||||
|
- [ ] Don't significantly decrease the current code coverage (see coverage/lcov-report/index.html)
|
||||||
|
- [ ] Currently, the `4.x`-branch contains the latest version. Please target that branch in the PR.
|
||||||
@@ -17,7 +17,7 @@ jobs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: '16'
|
node-version: '18'
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
@@ -25,6 +25,31 @@ jobs:
|
|||||||
- name: Lint
|
- name: Lint
|
||||||
run: npm run lint
|
run: npm run lint
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
name: Test (dependencies)
|
||||||
|
runs-on: 'ubuntu-latest'
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
# Node 14 ships with npm v6, which doesn't install peer-dependencies by default.
|
||||||
|
# Starting with npm v7 (which is shipped with Node >= 16), peer-dependencies are
|
||||||
|
# automatically installed. So this test (check for unmet peer-dependencies) only
|
||||||
|
# works with Node <= 14.
|
||||||
|
node-version: '14'
|
||||||
|
|
||||||
|
# Simulate an installation by a dependent package
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
rm package-lock.json
|
||||||
|
npm install --production
|
||||||
|
|
||||||
|
- name: Check dependency tree
|
||||||
|
run: npm ls
|
||||||
|
|
||||||
test:
|
test:
|
||||||
name: Test (Node)
|
name: Test (Node)
|
||||||
runs-on: ${{ matrix.operating-system }}
|
runs-on: ${{ matrix.operating-system }}
|
||||||
@@ -33,7 +58,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
operating-system: ['ubuntu-latest', 'windows-latest']
|
operating-system: ['ubuntu-latest', 'windows-latest']
|
||||||
# https://nodejs.org/en/about/releases/
|
# https://nodejs.org/en/about/releases/
|
||||||
node-version: ['10', '12', '14', '16', '17']
|
node-version: ['12', '14', '16', '18', '20']
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -53,8 +78,6 @@ jobs:
|
|||||||
run: npm run test
|
run: npm run test
|
||||||
|
|
||||||
- name: Test (Integration)
|
- name: Test (Integration)
|
||||||
# https://github.com/webpack/webpack/issues/14532
|
|
||||||
if: ${{ matrix.node-version != '17' }}
|
|
||||||
run: |
|
run: |
|
||||||
cd ./tests/integration/rollup-test && ./test.sh && cd -
|
cd ./tests/integration/rollup-test && ./test.sh && cd -
|
||||||
cd ./tests/integration/webpack-babel-test && ./test.sh && cd -
|
cd ./tests/integration/webpack-babel-test && ./test.sh && cd -
|
||||||
@@ -62,7 +85,7 @@ jobs:
|
|||||||
|
|
||||||
browser:
|
browser:
|
||||||
name: Test (Browser)
|
name: Test (Browser)
|
||||||
runs-on: 'ubuntu-latest'
|
runs-on: 'ubuntu-22.04'
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
@@ -72,7 +95,7 @@ jobs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: '16'
|
node-version: '18'
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish-aws-s3:
|
||||||
|
name: Publish to AWS S3
|
||||||
|
runs-on: 'ubuntu-latest'
|
||||||
|
environment: 'builds.handlebarsjs.com.s3.amazonaws.com'
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: '18'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Publish
|
||||||
|
run: |
|
||||||
|
git config --global user.email "release@handlebarsjs.com"
|
||||||
|
git config --global user.name "handlebars-lang"
|
||||||
|
npm run publish:aws
|
||||||
|
env:
|
||||||
|
S3_BUCKET_NAME: "builds.handlebarsjs.com"
|
||||||
|
S3_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY_ID }}
|
||||||
|
S3_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_ACCESS_KEY }}
|
||||||
@@ -12,7 +12,6 @@ node_modules
|
|||||||
.nyc_output
|
.nyc_output
|
||||||
|
|
||||||
# Generated files
|
# Generated files
|
||||||
lib/handlebars/compiler/parser.js
|
|
||||||
/coverage/
|
/coverage/
|
||||||
/dist/
|
/dist/
|
||||||
/tests/integration/*/dist/
|
/tests/integration/*/dist/
|
||||||
|
|||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
[submodule "spec/mustache"]
|
[submodule "spec/mustache"]
|
||||||
path = spec/mustache
|
path = spec/mustache
|
||||||
url = git://github.com/mustache/spec.git
|
url = https://github.com/mustache/spec.git
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ node_modules
|
|||||||
.nyc_output
|
.nyc_output
|
||||||
|
|
||||||
# Generated files
|
# Generated files
|
||||||
lib/handlebars/compiler/parser.js
|
|
||||||
/coverage/
|
/coverage/
|
||||||
/dist/
|
/dist/
|
||||||
/tests/integration/*/dist/
|
/tests/integration/*/dist/
|
||||||
|
|||||||
+13
-5
@@ -1,10 +1,16 @@
|
|||||||
# How to Contribute
|
# How to Contribute
|
||||||
|
|
||||||
|
## Reporting Security Issues
|
||||||
|
|
||||||
|
Please refer to our [Security Policy](https://github.com/handlebars-lang/handlebars.js/blob/master/SECURITY.md).
|
||||||
|
|
||||||
## Reporting Issues
|
## Reporting Issues
|
||||||
|
|
||||||
Please see our [FAQ](https://github.com/handlebars-lang/handlebars.js/blob/master/FAQ.md) for common issues that people run into.
|
Please refer to our [FAQ](https://github.com/handlebars-lang/handlebars.js/blob/master/FAQ.md) for common issues that people run into.
|
||||||
|
|
||||||
Should you run into other issues with the project, please don't hesitate to let us know by filing an [issue][issue]! In general we are going to ask for an example of the problem failing, which can be as simple as a jsfiddle/jsbin/etc. We've put together a jsfiddle [template][jsfiddle] to ease this. (We will keep this link up to date as new releases occur, so feel free to check back here)
|
Should you run into other issues with the project, please don't hesitate to let us know by filing an [issue][issue]!
|
||||||
|
|
||||||
|
In general, we are going to ask for an **example** of the problem failing, which can be as simple as a jsfiddle/jsbin/etc. We've put together a jsfiddle **[template][jsfiddle]** to ease this. (We will keep this link up to date as new releases occur, so feel free to check back here).
|
||||||
|
|
||||||
Pull requests containing only failing tests demonstrating the issue are welcomed and this also helps ensure that your issue won't regress in the future once it's fixed.
|
Pull requests containing only failing tests demonstrating the issue are welcomed and this also helps ensure that your issue won't regress in the future once it's fixed.
|
||||||
|
|
||||||
@@ -15,6 +21,8 @@ Documentation issues on the [handlebarsjs.com](https://handlebarsjs.com) site sh
|
|||||||
- The branch `4.x` contains the currently released version. Bugfixes should be made in this branch.
|
- 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
|
- 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.
|
should merge the branch `4.x` into the master branch regularly.
|
||||||
|
- The branch `3.x` contains the legacy version `3.x`. Bugfixes are applied separately (if needed). The branch will not
|
||||||
|
be merged with any of the other branches.
|
||||||
|
|
||||||
## Pull Requests
|
## Pull Requests
|
||||||
|
|
||||||
@@ -49,7 +57,7 @@ The `grunt dev` implements watching for tests and allows for in browser testing
|
|||||||
If you notice any problems, please report them to the GitHub issue tracker at
|
If you notice any problems, please report them to the GitHub issue tracker at
|
||||||
[http://github.com/handlebars-lang/handlebars.js/issues](http://github.com/handlebars-lang/handlebars.js/issues).
|
[http://github.com/handlebars-lang/handlebars.js/issues](http://github.com/handlebars-lang/handlebars.js/issues).
|
||||||
|
|
||||||
##Running Tests
|
## Running Tests
|
||||||
|
|
||||||
To run tests locally, first install all dependencies.
|
To run tests locally, first install all dependencies.
|
||||||
|
|
||||||
@@ -86,7 +94,7 @@ You can use the following scripts to make sure that the CI job does not fail:
|
|||||||
- **npm run lint** will run `eslint` and fail on warnings
|
- **npm run lint** will run `eslint` and fail on warnings
|
||||||
- **npm run format** will run `prettier` on all files
|
- **npm run format** will run `prettier` on all files
|
||||||
- **npm run check-before-pull-request** will perform all most checks that our CI job does in its build-job, excluding the "integration-test".
|
- **npm run check-before-pull-request** will perform all most checks that our CI job does in its build-job, excluding the "integration-test".
|
||||||
- **npm run integration-test** will run integration tests (using old NodeJS versions and integrations with webpack, babel and so on)
|
- **npm run test:integration** will run integration tests (using old NodeJS versions and integrations with webpack, babel and so on)
|
||||||
These tests only work on a Linux-machine with `nvm` installed (for running tests in multiple versions of NodeJS).
|
These tests only work on a Linux-machine with `nvm` installed (for running tests in multiple versions of NodeJS).
|
||||||
|
|
||||||
## Releasing the latest version
|
## Releasing the latest version
|
||||||
@@ -132,4 +140,4 @@ in the `handlebars-lang/docs`-repo).
|
|||||||
[generator-release]: https://github.com/walmartlabs/generator-release
|
[generator-release]: https://github.com/walmartlabs/generator-release
|
||||||
[pull-request]: https://github.com/handlebars-lang/handlebars.js/pull/new/master
|
[pull-request]: https://github.com/handlebars-lang/handlebars.js/pull/new/master
|
||||||
[issue]: https://github.com/handlebars-lang/handlebars.js/issues/new
|
[issue]: https://github.com/handlebars-lang/handlebars.js/issues/new
|
||||||
[jsfiddle]: https://jsfiddle.net/9D88g/180/
|
[jsfiddle]: https://jsfiddle.net/4nbwjaqz/4/
|
||||||
|
|||||||
@@ -1,62 +1,54 @@
|
|||||||
# Frequently Asked Questions
|
# Frequently Asked Questions
|
||||||
|
|
||||||
1. How can I file a bug report:
|
## How can I file a bug report:
|
||||||
|
|
||||||
See our guidelines on [reporting issues](https://github.com/handlebars-lang/handlebars.js/blob/master/CONTRIBUTING.md#reporting-issues).
|
See our guidelines on [reporting issues](https://github.com/handlebars-lang/handlebars.js/blob/master/CONTRIBUTING.md#reporting-issues).
|
||||||
|
|
||||||
1. Why isn't my Mustache template working?
|
## Why isn't my Mustache template working?
|
||||||
|
|
||||||
Handlebars deviates from Mustache slightly on a few behaviors. These variations are documented in our [readme](https://github.com/handlebars-lang/handlebars.js#differences-between-handlebarsjs-and-mustache).
|
Handlebars deviates from Mustache slightly on a few behaviors. These variations are documented in our [readme](https://github.com/handlebars-lang/handlebars.js#differences-between-handlebarsjs-and-mustache).
|
||||||
|
|
||||||
1. Why is it slower when compiling?
|
## Why is it slower when compiling?
|
||||||
|
|
||||||
The Handlebars compiler must parse the template and construct a JavaScript program which can then be run. Under some environments such as older mobile devices this can have a performance impact which can be avoided by precompiling. Generally it's recommended that precompilation and the runtime library be used on all clients.
|
The Handlebars compiler must parse the template and construct a JavaScript program which can then be run. Under some environments such as older mobile devices this can have a performance impact which can be avoided by precompiling. Generally it's recommended that precompilation and the runtime library be used on all clients.
|
||||||
|
|
||||||
1. Why doesn't this work with Content Security Policy restrictions?
|
## Why doesn't this work with Content Security Policy restrictions?
|
||||||
|
|
||||||
When not using the precompiler, Handlebars generates a dynamic function for each template which can cause issues with pages that have enabled Content Policy. It's recommended that templates are precompiled or the `unsafe-eval` policy is enabled for sites that must generate dynamic templates at runtime.
|
When not using the precompiler, Handlebars generates a dynamic function for each template which can cause issues with pages that have enabled Content Policy. It's recommended that templates are precompiled or the `unsafe-eval` policy is enabled for sites that must generate dynamic templates at runtime.
|
||||||
|
|
||||||
1. How can I include script tags in my template?
|
## How can I include script tags in my template?
|
||||||
|
|
||||||
If loading the template via an inlined `<script type="text/x-handlebars">` tag then you may need to break up the script tag with an empty comment to avoid browser parser errors:
|
If loading the template via an inlined `<script type="text/x-handlebars">` tag then you may need to break up the script tag with an empty comment to avoid browser parser errors:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<script type="text/x-handlebars">
|
<script type="text/x-handlebars">
|
||||||
foo
|
foo
|
||||||
<scr{{!}}ipt src="bar"></scr{{!}}ipt>
|
<scr{{!}}ipt src="bar"></scr{{!}}ipt>
|
||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
It's generally recommended that templates are served through external, precompiled, files, which do not suffer from this issue.
|
It's generally recommended that templates are served through external, precompiled, files, which do not suffer from this issue.
|
||||||
|
|
||||||
1. Why are my precompiled scripts throwing exceptions?
|
## Why are my precompiled scripts throwing exceptions?
|
||||||
|
|
||||||
When using the precompiler, it's important that a supporting version of the Handlebars runtime be loaded on the target page. In version 1.x there were rudimentary checks to compare the version but these did not always work. This is fixed under 2.x but the version checking does not work between these two versions. If you see unexpected errors such as `undefined is not a function` or similar, please verify that the same version is being used for both the precompiler and the client. This can be checked via:
|
When using the precompiler, it's important that a supporting version of the Handlebars runtime be loaded on the target page. In version 1.x there were rudimentary checks to compare the version but these did not always work. This is fixed under 2.x but the version checking does not work between these two versions. If you see unexpected errors such as `undefined is not a function` or similar, please verify that the same version is being used for both the precompiler and the client. This can be checked via:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
handlebars --version
|
handlebars --version
|
||||||
```
|
```
|
||||||
|
|
||||||
If using the integrated precompiler and
|
If using the integrated precompiler and
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
console.log(Handlebars.VERSION);
|
console.log(Handlebars.VERSION);
|
||||||
```
|
```
|
||||||
|
|
||||||
On the client side.
|
On the client side.
|
||||||
|
|
||||||
We include the built client libraries in the npm package for those who want to be certain that they are using the same client libraries as the compiler.
|
We include the built client libraries in the npm package for those who want to be certain that they are using the same client libraries as the compiler.
|
||||||
|
|
||||||
Should these match, please file an issue with us, per our [issue filing guidelines](https://github.com/handlebars-lang/handlebars.js/blob/master/CONTRIBUTING.md#reporting-issues).
|
Should these match, please file an issue with us, per our [issue filing guidelines](https://github.com/handlebars-lang/handlebars.js/blob/master/CONTRIBUTING.md#reporting-issues).
|
||||||
|
|
||||||
1. Why doesn't IE like the `default` name in the AMD module?
|
## How do I load the runtime library when using AMD?
|
||||||
|
|
||||||
Some browsers such as particular versions of IE treat `default` as a reserved word in JavaScript source files. To safely use this you need to reference this via the `Handlebars['default']` lookup method. This is an unfortunate side effect of the shims necessary to backport the Handlebars ES6 code to all current browsers.
|
The `handlebars.runtime.js` file includes a UMD build, which exposes the library as both the module root and the `default` field for compatibility.
|
||||||
|
|
||||||
1. How do I load the runtime library when using AMD?
|
|
||||||
|
|
||||||
There are two options for loading under AMD environments. The first is to use the `handlebars.runtime.amd.js` file. This may require a [path mapping](https://github.com/handlebars-lang/handlebars.js/blob/master/spec/amd-runtime.html#L31) as well as access via the `default` field.
|
|
||||||
|
|
||||||
The other option is to load the `handlebars.runtime.js` UMD build, which might not require path configuration and exposes the library as both the module root and the `default` field for compatibility.
|
|
||||||
|
|
||||||
If not using ES6 transpilers or accessing submodules in the build the former option should be sufficient for most use cases.
|
|
||||||
|
|||||||
+50
-113
@@ -1,32 +1,24 @@
|
|||||||
/* eslint-disable no-process-env */
|
/* eslint-disable no-process-env */
|
||||||
module.exports = function(grunt) {
|
module.exports = function (grunt) {
|
||||||
grunt.initConfig({
|
grunt.initConfig({
|
||||||
pkg: grunt.file.readJSON('package.json'),
|
pkg: grunt.file.readJSON('package.json'),
|
||||||
|
|
||||||
clean: [
|
clean: ['tmp', 'dist', 'tests/integration/**/node_modules'],
|
||||||
'tmp',
|
|
||||||
'dist',
|
|
||||||
'lib/handlebars/compiler/parser.js',
|
|
||||||
'/tests/integration/**/node_modules'
|
|
||||||
],
|
|
||||||
|
|
||||||
copy: {
|
copy: {
|
||||||
dist: {
|
dist: {
|
||||||
options: {
|
options: {
|
||||||
processContent: function(content) {
|
processContent: function (content) {
|
||||||
return (
|
return (
|
||||||
grunt.template.process(
|
grunt.template.process(
|
||||||
'/**!\n\n @license\n <%= pkg.name %> v<%= pkg.version %>\n\n<%= grunt.file.read("LICENSE") %>\n*/\n'
|
'/**!\n\n @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt Expat\n <%= pkg.name %> v<%= pkg.version %>\n\n<%= grunt.file.read("LICENSE") %>\n*/\n'
|
||||||
) + content
|
) +
|
||||||
|
content +
|
||||||
|
'\n// @license-end\n'
|
||||||
);
|
);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
files: [{ expand: true, cwd: 'dist/', src: ['*.js'], dest: 'dist/' }]
|
|
||||||
},
|
},
|
||||||
cdnjs: {
|
files: [{ expand: true, cwd: 'dist/', src: ['*.js'], dest: 'dist/' }],
|
||||||
files: [
|
|
||||||
{ expand: true, cwd: 'dist/', src: ['*.js'], dest: 'dist/cdnjs' }
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
files: [
|
files: [
|
||||||
@@ -34,105 +26,63 @@ module.exports = function(grunt) {
|
|||||||
expand: true,
|
expand: true,
|
||||||
cwd: 'components/',
|
cwd: 'components/',
|
||||||
src: ['**'],
|
src: ['**'],
|
||||||
dest: 'dist/components'
|
dest: 'dist/components',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expand: true,
|
||||||
|
cwd: 'dist/',
|
||||||
|
src: ['*.js'],
|
||||||
|
dest: 'dist/components',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{ expand: true, cwd: 'dist/', src: ['*.js'], dest: 'dist/components' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
babel: {
|
babel: {
|
||||||
options: {
|
options: {
|
||||||
sourceMaps: 'inline',
|
sourceMaps: 'inline',
|
||||||
loose: ['es6.modules'],
|
loose: ['es6.modules'],
|
||||||
auxiliaryCommentBefore: 'istanbul ignore next'
|
auxiliaryCommentBefore: 'istanbul ignore next',
|
||||||
},
|
},
|
||||||
amd: {
|
|
||||||
options: {
|
|
||||||
modules: 'amd'
|
|
||||||
},
|
|
||||||
files: [
|
|
||||||
{
|
|
||||||
expand: true,
|
|
||||||
cwd: 'lib/',
|
|
||||||
src: '**/!(index).js',
|
|
||||||
dest: 'dist/amd/'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
cjs: {
|
cjs: {
|
||||||
options: {
|
|
||||||
modules: 'common'
|
|
||||||
},
|
|
||||||
files: [
|
files: [
|
||||||
{
|
{
|
||||||
cwd: 'lib/',
|
cwd: 'lib/',
|
||||||
expand: true,
|
expand: true,
|
||||||
src: '**/!(index).js',
|
src: '**/!(index).js',
|
||||||
dest: 'dist/cjs/'
|
dest: 'dist/cjs/',
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
webpack: {
|
webpack: {
|
||||||
options: {
|
options: {
|
||||||
context: __dirname,
|
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: {
|
output: {
|
||||||
path: 'dist/',
|
path: 'dist/',
|
||||||
library: 'Handlebars',
|
library: 'Handlebars',
|
||||||
libraryTarget: 'umd'
|
libraryTarget: 'umd',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
handlebars: {
|
handlebars: {
|
||||||
entry: './lib/handlebars.js',
|
entry: './dist/cjs/handlebars.js',
|
||||||
output: {
|
output: {
|
||||||
filename: 'handlebars.js'
|
filename: 'handlebars.js',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
runtime: {
|
runtime: {
|
||||||
entry: './lib/handlebars.runtime.js',
|
entry: './dist/cjs/handlebars.runtime.js',
|
||||||
output: {
|
output: {
|
||||||
filename: 'handlebars.runtime.js'
|
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: {
|
uglify: {
|
||||||
options: {
|
options: {
|
||||||
mangle: true,
|
mangle: true,
|
||||||
compress: true,
|
compress: true,
|
||||||
preserveComments: /(?:^!|@(?:license|preserve|cc_on))/
|
preserveComments: /(?:^!|@(?:license|preserve|cc_on))/,
|
||||||
},
|
},
|
||||||
dist: {
|
dist: {
|
||||||
files: [
|
files: [
|
||||||
@@ -141,19 +91,19 @@ module.exports = function(grunt) {
|
|||||||
expand: true,
|
expand: true,
|
||||||
src: ['handlebars*.js', '!*.min.js'],
|
src: ['handlebars*.js', '!*.min.js'],
|
||||||
dest: 'dist/',
|
dest: 'dist/',
|
||||||
rename: function(dest, src) {
|
rename: function (dest, src) {
|
||||||
return dest + src.replace(/\.js$/, '.min.js');
|
return dest + src.replace(/\.js$/, '.min.js');
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
concat: {
|
concat: {
|
||||||
tests: {
|
tests: {
|
||||||
src: ['spec/!(require).js'],
|
src: ['spec/!(require).js'],
|
||||||
dest: 'tmp/tests.js'
|
dest: 'tmp/tests.js',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
connect: {
|
connect: {
|
||||||
@@ -161,29 +111,27 @@ module.exports = function(grunt) {
|
|||||||
options: {
|
options: {
|
||||||
base: '.',
|
base: '.',
|
||||||
hostname: '*',
|
hostname: '*',
|
||||||
port: 9999
|
port: 9999,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
bgShell: {
|
shell: {
|
||||||
integrationTests: {
|
integrationTests: {
|
||||||
cmd: './tests/integration/run-integration-tests.sh',
|
command: './tests/integration/run-integration-tests.sh',
|
||||||
bg: false,
|
},
|
||||||
fail: true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
scripts: {
|
scripts: {
|
||||||
options: {
|
options: {
|
||||||
atBegin: true
|
atBegin: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
files: ['src/*', 'lib/**/*.js', 'spec/**/*.js'],
|
files: ['src/*', 'lib/**/*.js', 'spec/**/*.js'],
|
||||||
tasks: ['on-file-change']
|
tasks: ['on-file-change'],
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Load tasks from npm
|
// Load tasks from npm
|
||||||
@@ -191,32 +139,21 @@ module.exports = function(grunt) {
|
|||||||
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-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-shell');
|
||||||
grunt.loadNpmTasks('grunt-webpack');
|
grunt.loadNpmTasks('grunt-webpack');
|
||||||
|
|
||||||
grunt.task.loadTasks('tasks');
|
grunt.task.loadTasks('tasks');
|
||||||
|
|
||||||
grunt.registerTask('node', ['babel:cjs']);
|
grunt.registerTask('node', ['babel:cjs']);
|
||||||
grunt.registerTask('amd', ['babel:amd', 'requirejs']);
|
|
||||||
grunt.registerTask('globals', ['webpack']);
|
grunt.registerTask('globals', ['webpack']);
|
||||||
grunt.registerTask('release', 'Build final packages', [
|
grunt.registerTask('release', 'Build final packages', [
|
||||||
'uglify',
|
'uglify',
|
||||||
'test:min',
|
'test:min',
|
||||||
'copy:dist',
|
'copy:dist',
|
||||||
'copy:components',
|
'copy:components',
|
||||||
'copy:cdnjs'
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Requires secret properties from .travis.yaml
|
|
||||||
grunt.registerTask('extensive-tests-and-publish-to-aws', [
|
|
||||||
'default',
|
|
||||||
'bgShell:integrationTests',
|
|
||||||
'metrics',
|
|
||||||
'publish-to-aws'
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
grunt.registerTask('on-file-change', ['build', 'concat:tests', 'test']);
|
grunt.registerTask('on-file-change', ['build', 'concat:tests', 'test']);
|
||||||
@@ -230,10 +167,10 @@ module.exports = function(grunt) {
|
|||||||
grunt.registerTask(
|
grunt.registerTask(
|
||||||
'build',
|
'build',
|
||||||
'Builds a distributable version of the current project',
|
'Builds a distributable version of the current project',
|
||||||
['parser', 'node', 'amd', 'globals']
|
['node', 'globals']
|
||||||
);
|
);
|
||||||
grunt.registerTask('integration-tests', [
|
grunt.registerTask('integration-tests', [
|
||||||
'default',
|
'default',
|
||||||
'bgShell:integrationTests'
|
'shell:integrationTests',
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|||||||
+46
-45
@@ -1,18 +1,18 @@
|
|||||||
[](https://github.com/handlebars-lang/handlebars.js/actions)
|
[](https://github.com/handlebars-lang/handlebars.js/actions/workflows/ci.yml)
|
||||||
[](https://www.jsdelivr.com/package/npm/handlebars)
|
[](https://www.jsdelivr.com/package/npm/handlebars)
|
||||||
[](https://bundlephobia.com/package/handlebars)
|
[](https://www.npmjs.com/package/handlebars)
|
||||||
|
[](https://www.npmjs.com/package/handlebars)
|
||||||
|
[](https://bundlephobia.com/package/handlebars)
|
||||||
[](https://packagephobia.com/result?p=handlebars)
|
[](https://packagephobia.com/result?p=handlebars)
|
||||||
|
|
||||||
Handlebars.js
|
Handlebars.js
|
||||||
=============
|
=============
|
||||||
|
|
||||||
Handlebars.js is an extension to the [Mustache templating
|
Handlebars provides the power necessary to let you build **semantic templates** effectively with no frustration.
|
||||||
language](https://mustache.github.io/) created by Chris Wanstrath.
|
Handlebars is largely compatible with Mustache templates. In most cases it is possible to swap out Mustache with Handlebars and continue using your current templates.
|
||||||
Handlebars.js and Mustache are both logicless templating languages that
|
|
||||||
keep the view and the code separated like we all know they should be.
|
|
||||||
|
|
||||||
Checkout the official Handlebars docs site at
|
Checkout the official Handlebars docs site at
|
||||||
[https://handlebarsjs.com/](https://handlebarsjs.com) and the live demo at [http://tryhandlebarsjs.com/](http://tryhandlebarsjs.com/).
|
[handlebarsjs.com](https://handlebarsjs.com) and try our [live demo](https://handlebarsjs.com/playground.html).
|
||||||
|
|
||||||
Installing
|
Installing
|
||||||
----------
|
----------
|
||||||
@@ -72,23 +72,23 @@ Block expressions have the same syntax as mustache sections but should not be co
|
|||||||
There are a few Mustache behaviors that Handlebars does not implement.
|
There are a few Mustache behaviors that Handlebars does not implement.
|
||||||
- Handlebars deviates from Mustache slightly in that it does not perform recursive lookup by default. The compile time `compat` flag must be set to enable this functionality. Users should note that there is a performance cost for enabling this flag. The exact cost varies by template, but it's recommended that performance sensitive operations should avoid this mode and instead opt for explicit path references.
|
- Handlebars deviates from Mustache slightly in that it does not perform recursive lookup by default. The compile time `compat` flag must be set to enable this functionality. Users should note that there is a performance cost for enabling this flag. The exact cost varies by template, but it's recommended that performance sensitive operations should avoid this mode and instead opt for explicit path references.
|
||||||
- The optional Mustache-style lambdas are not supported. Instead Handlebars provides its own lambda resolution that follows the behaviors of helpers.
|
- The optional Mustache-style lambdas are not supported. Instead Handlebars provides its own lambda resolution that follows the behaviors of helpers.
|
||||||
|
- Handlebars does not allow space between the opening `{{` and a command character such as `#`, `/` or `>`. The command character must immediately follow the braces, so for example `{{> partial }}` is allowed but `{{ > partial }}` is not.
|
||||||
- Alternative delimiters are not supported.
|
- Alternative delimiters are not supported.
|
||||||
|
|
||||||
|
|
||||||
Supported Environments
|
Supported Environments
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
Handlebars has been designed to work in any ECMAScript 3 environment. This includes
|
Handlebars has been designed to work in any ECMAScript 2020 environment. This includes
|
||||||
|
|
||||||
- Node.js
|
- Node.js
|
||||||
- Chrome
|
- Chrome
|
||||||
- Firefox
|
- Firefox
|
||||||
- Safari 5+
|
- Safari
|
||||||
- Opera 11+
|
- Edge
|
||||||
- IE 6+
|
|
||||||
|
If you need to support older environments, use Handlebars version 4.
|
||||||
|
|
||||||
Older versions and other runtimes are likely to work but have not been formally
|
|
||||||
tested. The compiler requires `JSON.stringify` to be implemented natively or via a polyfill. If using the precompiler this is not necessary.
|
|
||||||
|
|
||||||
Performance
|
Performance
|
||||||
-----------
|
-----------
|
||||||
@@ -109,6 +109,12 @@ Upgrading
|
|||||||
|
|
||||||
See [release-notes.md](https://github.com/handlebars-lang/handlebars.js/blob/master/release-notes.md) for upgrade notes.
|
See [release-notes.md](https://github.com/handlebars-lang/handlebars.js/blob/master/release-notes.md) for upgrade notes.
|
||||||
|
|
||||||
|
If you are using Handlebars in production, please regularly look for issues labeled
|
||||||
|
[possibly breaking](https://github.com/handlebars-lang/handlebars.js/issues?q=is%3Aopen+is%3Aissue+label%3A%22possibly+breaking%22).
|
||||||
|
If this label is applied to an issue, it means that the requested change is probably not a breaking change,
|
||||||
|
but since Handlebars is widely in use by a lot of people, there's always a chance that it breaks somebody's build.
|
||||||
|
|
||||||
|
|
||||||
Known Issues
|
Known Issues
|
||||||
------------
|
------------
|
||||||
|
|
||||||
@@ -118,40 +124,35 @@ See [FAQ.md](https://github.com/handlebars-lang/handlebars.js/blob/master/FAQ.md
|
|||||||
Handlebars in the Wild
|
Handlebars in the Wild
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
* [Assemble](http://assemble.io), by [@jonschlinkert](https://github.com/jonschlinkert)
|
* [apiDoc](https://github.com/apidoc/apidoc) apiDoc uses handlebars as parsing engine for api documentation view generation.
|
||||||
and [@doowb](https://github.com/doowb), is a static site generator that uses Handlebars.js
|
* [Assemble](https://assemble.io), by [@jonschlinkert](https://github.com/jonschlinkert) and [@doowb](https://github.com/doowb), is a static site generator that uses Handlebars.js as its template engine.
|
||||||
as its template engine.
|
* [CoSchedule](https://coschedule.com) An editorial calendar for WordPress that uses Handlebars.js.
|
||||||
* [Cory](https://github.com/leo/cory), by [@leo](https://github.com/leo), is another tiny static site generator
|
* [Ember.js](https://www.emberjs.com) makes Handlebars.js the primary way to structure your views, also with automatic data binding support.
|
||||||
* [CoSchedule](http://coschedule.com) An editorial calendar for WordPress that uses Handlebars.js
|
* [express-handlebars](https://github.com/express-handlebars/express-handlebars) A Handlebars view engine for Express which doesn't suck.
|
||||||
* [dashbars](https://github.com/pismute/dashbars) A modern helper library for Handlebars.js.
|
* [express-hbs](https://github.com/TryGhost/express-hbs) Express Handlebars template engine with inheritance, partials, i18n and async helpers.
|
||||||
* [Ember.js](http://www.emberjs.com) makes Handlebars.js the primary way to
|
|
||||||
structure your views, also with automatic data binding support.
|
|
||||||
* [Ghost](https://ghost.org/) Just a blogging platform.
|
* [Ghost](https://ghost.org/) Just a blogging platform.
|
||||||
* [handlebars_assets](http://github.com/leshill/handlebars_assets): A Rails Asset Pipeline gem
|
* [handlebars-action](https://github.com/marketplace/actions/handlebars-action) A GitHub action to transform files in your repository with Handlebars templating.
|
||||||
from Les Hill (@leshill).
|
* [handlebars_assets](https://github.com/leshill/handlebars_assets) A Rails Asset Pipeline gem from Les Hill (@leshill).
|
||||||
* [handlebars-helpers](https://github.com/assemble/handlebars-helpers) is an extensive library
|
* [handlebars-helpers](https://github.com/assemble/handlebars-helpers) is an extensive library with 100+ handlebars helpers.
|
||||||
with 100+ handlebars helpers.
|
* [handlebars-layouts](https://github.com/shannonmoeller/handlebars-layouts) is a set of helpers which implement extensible and embeddable layout blocks as seen in other popular templating languages.
|
||||||
* [handlebars-layouts](https://github.com/shannonmoeller/handlebars-layouts) is a set of helpers which implement extendible and embeddable layout blocks as seen in other popular templating languages.
|
* [handlebars-loader](https://github.com/pcardune/handlebars-loader) A handlebars template loader for webpack.
|
||||||
* [hbs](http://github.com/donpark/hbs): An Express.js view engine adapter for Handlebars.js,
|
* [handlebars-wax](https://github.com/shannonmoeller/handlebars-wax) The missing Handlebars API. Effortless registration of data, partials, helpers, and decorators using file-system globs, modules, and plain-old JavaScript objects.
|
||||||
from Don Park.
|
* [hbs](https://github.com/pillarjs/hbs) An Express.js view engine adapter for Handlebars.js, from Don Park.
|
||||||
* [koa-hbs](https://github.com/jwilm/koa-hbs): [koa](https://github.com/koajs/koa) generator based
|
* [html-bundler-webpack-plugin](https://github.com/webdiscus/html-bundler-webpack-plugin) The webpack plugin to compile templates, [supports Handlebars](https://github.com/webdiscus/html-bundler-webpack-plugin#using-template-handlebars).
|
||||||
renderer for Handlebars.js.
|
* [incremental-bars](https://github.com/atomictag/incremental-bars) adds support for [incremental-dom](https://github.com/google/incremental-dom) as template target to Handlebars.
|
||||||
* [jblotus](http://github.com/jblotus) created [http://tryhandlebarsjs.com](http://tryhandlebarsjs.com)
|
* [jblotus](https://github.com/jblotus) created [http://tryhandlebarsjs.com](http://tryhandlebarsjs.com) for anyone who would like to try out Handlebars.js in their browser.
|
||||||
for anyone who would like to try out Handlebars.js in their browser.
|
* [jQuery plugin](https://71104.github.io/jquery-handlebars/) allows you to use Handlebars.js with [jQuery](http://jquery.com/).
|
||||||
* [jQuery plugin](http://71104.github.io/jquery-handlebars/): allows you to use
|
|
||||||
Handlebars.js with [jQuery](http://jquery.com/).
|
|
||||||
* [Lumbar](http://walmartlabs.github.io/lumbar) provides easy module-based template management for
|
|
||||||
handlebars projects.
|
|
||||||
* [Marionette.Handlebars](https://github.com/hashchange/marionette.handlebars) adds support for Handlebars and Mustache templates to Marionette.
|
|
||||||
* [sammy.js](http://github.com/quirkey/sammy) by Aaron Quint, a.k.a. quirkey,
|
|
||||||
supports Handlebars.js as one of its template plugins.
|
|
||||||
* [SproutCore](http://www.sproutcore.com) uses Handlebars.js as its main
|
|
||||||
templating engine, extending it with automatic data binding support.
|
|
||||||
* [YUI](http://yuilibrary.com/yui/docs/handlebars/) implements a port of handlebars
|
|
||||||
* [Swag](https://github.com/elving/swag) by [@elving](https://github.com/elving) is a growing collection of helpers for handlebars.js. Give your handlebars.js templates some swag son!
|
|
||||||
* [DOMBars](https://github.com/blakeembrey/dombars) is a DOM-based templating engine built on the Handlebars parser and runtime **DEPRECATED**
|
|
||||||
* [promised-handlebars](https://github.com/nknapp/promised-handlebars) is a wrapper for Handlebars that allows helpers to return Promises.
|
|
||||||
* [just-handlebars-helpers](https://github.com/leapfrogtechnology/just-handlebars-helpers) A fully tested lightweight package with common Handlebars helpers.
|
* [just-handlebars-helpers](https://github.com/leapfrogtechnology/just-handlebars-helpers) A fully tested lightweight package with common Handlebars helpers.
|
||||||
|
* [koa-hbs](https://github.com/jwilm/koa-hbs) [koa](https://github.com/koajs/koa) generator based renderer for Handlebars.js.
|
||||||
|
* [Marionette.Handlebars](https://github.com/hashchange/marionette.handlebars) adds support for Handlebars and Mustache templates to Marionette.
|
||||||
|
* [openVALIDATION](https://github.com/openvalidation/openvalidation) a natural language compiler for validation rules. Generates program code in Java, JavaScript, C#, Python and Rust with handlebars.
|
||||||
|
* [Plop](https://plopjs.com/) is a micro-generator framework that makes it easy to create files with a level of uniformity.
|
||||||
|
* [promised-handlebars](https://github.com/nknapp/promised-handlebars) is a wrapper for Handlebars that allows helpers to return Promises.
|
||||||
|
* [sammy.js](https://github.com/quirkey/sammy) by Aaron Quint, a.k.a. quirkey, supports Handlebars.js as one of its template plugins.
|
||||||
|
* [Swag](https://github.com/elving/swag) by [@elving](https://github.com/elving) is a growing collection of helpers for handlebars.js. Give your handlebars.js templates some swag son!
|
||||||
|
* [SproutCore](https://www.sproutcore.com) uses Handlebars.js as its main templating engine, extending it with automatic data binding support.
|
||||||
|
* [vite-plugin-handlebars](https://github.com/alexlafroscia/vite-plugin-handlebars) A package for Vite 2. Allows for running your HTML files through the Handlebars compiler.
|
||||||
|
* [YUI](https://yuilibrary.com/yui/docs/handlebars/) implements a port of handlebars.
|
||||||
|
|
||||||
External Resources
|
External Resources
|
||||||
------------------
|
------------------
|
||||||
|
|||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
We recommend always using the latest versions of Handlebars and its official companion libraries to ensure your application remains as secure as possible.
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
| Version | Supported |
|
||||||
|
| ------- | ------------------ |
|
||||||
|
| 5.0.x | :white_check_mark: |
|
||||||
|
| 4.7.x | :white_check_mark: |
|
||||||
|
| < 4.7 | :x: |
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
To report a vulnerability, please visit https://github.com/handlebars-lang/handlebars.js/security.
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
rules: {
|
|
||||||
'no-console': 0,
|
|
||||||
'no-var': 0
|
|
||||||
}
|
|
||||||
};
|
|
||||||
-176
@@ -1,176 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
var argv = parseArgs({
|
|
||||||
'f': {
|
|
||||||
'type': 'string',
|
|
||||||
'description': 'Output File',
|
|
||||||
'alias': 'output'
|
|
||||||
},
|
|
||||||
'map': {
|
|
||||||
'type': 'string',
|
|
||||||
'description': 'Source Map File'
|
|
||||||
},
|
|
||||||
'a': {
|
|
||||||
'type': 'boolean',
|
|
||||||
'description': 'Exports amd style (require.js)',
|
|
||||||
'alias': 'amd'
|
|
||||||
},
|
|
||||||
'c': {
|
|
||||||
'type': 'string',
|
|
||||||
'description': 'Exports CommonJS style, path to Handlebars module',
|
|
||||||
'alias': 'commonjs',
|
|
||||||
'default': null
|
|
||||||
},
|
|
||||||
'h': {
|
|
||||||
'type': 'string',
|
|
||||||
'description': 'Path to handlebar.js (only valid for amd-style)',
|
|
||||||
'alias': 'handlebarPath',
|
|
||||||
'default': ''
|
|
||||||
},
|
|
||||||
'k': {
|
|
||||||
'type': 'string',
|
|
||||||
'description': 'Known helpers',
|
|
||||||
'alias': 'known'
|
|
||||||
},
|
|
||||||
'o': {
|
|
||||||
'type': 'boolean',
|
|
||||||
'description': 'Known helpers only',
|
|
||||||
'alias': 'knownOnly'
|
|
||||||
},
|
|
||||||
'm': {
|
|
||||||
'type': 'boolean',
|
|
||||||
'description': 'Minimize output',
|
|
||||||
'alias': 'min'
|
|
||||||
},
|
|
||||||
'n': {
|
|
||||||
'type': 'string',
|
|
||||||
'description': 'Template namespace',
|
|
||||||
'alias': 'namespace',
|
|
||||||
'default': 'Handlebars.templates'
|
|
||||||
},
|
|
||||||
's': {
|
|
||||||
'type': 'boolean',
|
|
||||||
'description': 'Output template function only.',
|
|
||||||
'alias': 'simple'
|
|
||||||
},
|
|
||||||
'N': {
|
|
||||||
'type': 'string',
|
|
||||||
'description': 'Name of passed string templates. Optional if running in a simple mode. Required when operating on multiple templates.',
|
|
||||||
'alias': 'name'
|
|
||||||
},
|
|
||||||
'i': {
|
|
||||||
'type': 'string',
|
|
||||||
'description': 'Generates a template from the passed CLI argument.\n"-" is treated as a special value and causes stdin to be read for the template value.',
|
|
||||||
'alias': 'string'
|
|
||||||
},
|
|
||||||
'r': {
|
|
||||||
'type': 'string',
|
|
||||||
'description': 'Template root. Base value that will be stripped from template names.',
|
|
||||||
'alias': 'root'
|
|
||||||
},
|
|
||||||
'p': {
|
|
||||||
'type': 'boolean',
|
|
||||||
'description': 'Compiling a partial template',
|
|
||||||
'alias': 'partial'
|
|
||||||
},
|
|
||||||
'd': {
|
|
||||||
'type': 'boolean',
|
|
||||||
'description': 'Include data when compiling',
|
|
||||||
'alias': 'data'
|
|
||||||
},
|
|
||||||
'e': {
|
|
||||||
'type': 'string',
|
|
||||||
'description': 'Template extension.',
|
|
||||||
'alias': 'extension',
|
|
||||||
'default': 'handlebars'
|
|
||||||
},
|
|
||||||
'b': {
|
|
||||||
'type': 'boolean',
|
|
||||||
'description': 'Removes the BOM (Byte Order Mark) from the beginning of the templates.',
|
|
||||||
'alias': 'bom'
|
|
||||||
},
|
|
||||||
'v': {
|
|
||||||
'type': 'boolean',
|
|
||||||
'description': 'Prints the current compiler version',
|
|
||||||
'alias': 'version'
|
|
||||||
},
|
|
||||||
'help': {
|
|
||||||
'type': 'boolean',
|
|
||||||
'description': 'Outputs this message'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
argv.files = argv._;
|
|
||||||
delete argv._;
|
|
||||||
|
|
||||||
var Precompiler = require('../dist/cjs/precompiler');
|
|
||||||
Precompiler.loadTemplates(argv, function(err, opts) {
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts.help || (!opts.templates.length && !opts.version)) {
|
|
||||||
printUsage(argv._spec, 120);
|
|
||||||
} else {
|
|
||||||
Precompiler.cli(opts);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function pad(n) {
|
|
||||||
var str = '';
|
|
||||||
while (str.length < n) {
|
|
||||||
str += ' ';
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseArgs(spec) {
|
|
||||||
var opts = { alias: {}, boolean: [], default: {}, string: [] };
|
|
||||||
|
|
||||||
Object.keys(spec).forEach(function (arg) {
|
|
||||||
var opt = spec[arg];
|
|
||||||
opts[opt.type].push(arg);
|
|
||||||
if ('alias' in opt) opts.alias[arg] = opt.alias;
|
|
||||||
if ('default' in opt) opts.default[arg] = opt.default;
|
|
||||||
});
|
|
||||||
|
|
||||||
var argv = require('minimist')(process.argv.slice(2), opts);
|
|
||||||
argv._spec = spec;
|
|
||||||
return argv;
|
|
||||||
}
|
|
||||||
|
|
||||||
function printUsage(spec, wrap) {
|
|
||||||
var wordwrap = require('wordwrap');
|
|
||||||
|
|
||||||
console.log('Precompile handlebar templates.');
|
|
||||||
console.log('Usage: handlebars [template|directory]...');
|
|
||||||
|
|
||||||
var opts = [];
|
|
||||||
var width = 0;
|
|
||||||
Object.keys(spec).forEach(function (arg) {
|
|
||||||
var opt = spec[arg];
|
|
||||||
|
|
||||||
var name = (arg.length === 1 ? '-' : '--') + arg;
|
|
||||||
if ('alias' in opt) name += ', --' + opt.alias;
|
|
||||||
|
|
||||||
var meta = '[' + opt.type + ']';
|
|
||||||
if ('default' in opt) meta += ' [default: ' + JSON.stringify(opt.default) + ']';
|
|
||||||
|
|
||||||
opts.push({ name: name, desc: opt.description, meta: meta });
|
|
||||||
if (name.length > width) width = name.length;
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log('Options:');
|
|
||||||
opts.forEach(function (opt) {
|
|
||||||
var desc = wordwrap(width + 4, wrap + 1)(opt.desc);
|
|
||||||
|
|
||||||
console.log(' %s%s%s%s%s',
|
|
||||||
opt.name,
|
|
||||||
pad(width - opt.name.length + 2),
|
|
||||||
desc.slice(width + 4),
|
|
||||||
pad(wrap - opt.meta.length - desc.split(/\n/).pop().length),
|
|
||||||
opt.meta
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Executable
+131
@@ -0,0 +1,131 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const yargs = require('yargs')
|
||||||
|
.usage('Precompile handlebar templates.\nUsage: $0 [template|directory]...')
|
||||||
|
.option('f', {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Output File',
|
||||||
|
alias: 'output',
|
||||||
|
})
|
||||||
|
.option('map', {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Source Map File',
|
||||||
|
})
|
||||||
|
.option('esm', {
|
||||||
|
type: 'string',
|
||||||
|
description:
|
||||||
|
'Exports ECMAScript module style, path to Handlebars module (eg. "handlebars/lib/handlebars")',
|
||||||
|
default: null,
|
||||||
|
})
|
||||||
|
.option('c', {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Exports CommonJS style, path to Handlebars module',
|
||||||
|
alias: 'commonjs',
|
||||||
|
default: null,
|
||||||
|
})
|
||||||
|
.option('a', {
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Exports AMD style (require.js)',
|
||||||
|
alias: 'amd',
|
||||||
|
deprecated: true,
|
||||||
|
})
|
||||||
|
.option('h', {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Path to handlebar.js (only valid for AMD-style)',
|
||||||
|
alias: 'handlebarPath',
|
||||||
|
default: '',
|
||||||
|
deprecated: true,
|
||||||
|
})
|
||||||
|
.option('k', {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Known helpers',
|
||||||
|
alias: 'known',
|
||||||
|
})
|
||||||
|
.option('o', {
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Known helpers only',
|
||||||
|
alias: 'knownOnly',
|
||||||
|
})
|
||||||
|
.option('m', {
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Minimize output',
|
||||||
|
alias: 'min',
|
||||||
|
})
|
||||||
|
.option('n', {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Template namespace',
|
||||||
|
alias: 'namespace',
|
||||||
|
default: 'Handlebars.templates',
|
||||||
|
})
|
||||||
|
.option('s', {
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Output template function only.',
|
||||||
|
alias: 'simple',
|
||||||
|
})
|
||||||
|
.option('N', {
|
||||||
|
type: 'string',
|
||||||
|
description:
|
||||||
|
'Name of passed string templates. Optional if running in a simple mode. Required when operating on multiple templates.',
|
||||||
|
alias: 'name',
|
||||||
|
})
|
||||||
|
.option('i', {
|
||||||
|
type: 'string',
|
||||||
|
description:
|
||||||
|
'Generates a template from the passed CLI argument.\n"-" is treated as a special value and causes stdin to be read for the template value.',
|
||||||
|
alias: 'string',
|
||||||
|
})
|
||||||
|
.option('r', {
|
||||||
|
type: 'string',
|
||||||
|
description:
|
||||||
|
'Template root. Base value that will be stripped from template names.',
|
||||||
|
alias: 'root',
|
||||||
|
})
|
||||||
|
.option('p', {
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Compiling a partial template',
|
||||||
|
alias: 'partial',
|
||||||
|
})
|
||||||
|
.option('d', {
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Include data when compiling',
|
||||||
|
alias: 'data',
|
||||||
|
})
|
||||||
|
.option('e', {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Template extension.',
|
||||||
|
alias: 'extension',
|
||||||
|
default: 'handlebars',
|
||||||
|
})
|
||||||
|
.option('b', {
|
||||||
|
type: 'boolean',
|
||||||
|
description:
|
||||||
|
'Removes the BOM (Byte Order Mark) from the beginning of the templates.',
|
||||||
|
alias: 'bom',
|
||||||
|
})
|
||||||
|
.option('v', {
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Prints the current compiler version',
|
||||||
|
alias: 'version',
|
||||||
|
})
|
||||||
|
.option('help', {
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Outputs this message',
|
||||||
|
})
|
||||||
|
.wrap(120);
|
||||||
|
|
||||||
|
const argv = yargs.argv;
|
||||||
|
argv.files = argv._;
|
||||||
|
delete argv._;
|
||||||
|
|
||||||
|
const Precompiler = require('../dist/cjs/precompiler');
|
||||||
|
Precompiler.loadTemplates(argv, function (err, opts) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.help || (!opts.templates.length && !opts.version)) {
|
||||||
|
yargs.showHelp();
|
||||||
|
} else {
|
||||||
|
Precompiler.cli(opts);
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "handlebars",
|
"name": "handlebars",
|
||||||
"version": "4.7.7",
|
"version": "5.0.0-alpha.1",
|
||||||
"main": "handlebars.js",
|
"main": "handlebars.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {}
|
"dependencies": {}
|
||||||
|
|||||||
@@ -3,7 +3,5 @@
|
|||||||
"repo": "components/handlebars.js",
|
"repo": "components/handlebars.js",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"main": "handlebars.js",
|
"main": "handlebars.js",
|
||||||
"scripts": [
|
"scripts": ["handlebars.js"]
|
||||||
"handlebars.js"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<package>
|
<package>
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>handlebars.js</id>
|
<id>handlebars.js</id>
|
||||||
<version>4.7.7</version>
|
<version>5.0.0-alpha.1</version>
|
||||||
<authors>handlebars.js Authors</authors>
|
<authors>handlebars.js Authors</authors>
|
||||||
<licenseUrl>https://github.com/handlebars-lang/handlebars.js/blob/master/LICENSE</licenseUrl>
|
<licenseUrl>https://github.com/handlebars-lang/handlebars.js/blob/master/LICENSE</licenseUrl>
|
||||||
<projectUrl>https://github.com/handlebars-lang/handlebars.js/</projectUrl>
|
<projectUrl>https://github.com/handlebars-lang/handlebars.js/</projectUrl>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "handlebars",
|
"name": "handlebars",
|
||||||
"version": "4.7.7",
|
"version": "5.0.0-alpha.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"jspm": {
|
"jspm": {
|
||||||
"main": "handlebars",
|
"main": "handlebars",
|
||||||
|
|||||||
+27
-26
@@ -34,8 +34,8 @@ let ast = Handlebars.parseWithoutProcessing(myTemplate);
|
|||||||
|
|
||||||
`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:
|
`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")
|
- 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 `''`)
|
- 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`.
|
`Handlebars.parse` is used internally by `Handlebars.precompile` and `Handlebars.compile`.
|
||||||
|
|
||||||
@@ -132,7 +132,6 @@ interface PartialBlockStatement <: Statement {
|
|||||||
|
|
||||||
`name` will be a `SubExpression` when tied to a dynamic partial, i.e. `{{> (foo) }}`, otherwise this is a path or literal whose `original` value is used to lookup the desired partial.
|
`name` will be a `SubExpression` when tied to a dynamic partial, i.e. `{{> (foo) }}`, otherwise this is a path or literal whose `original` value is used to lookup the desired partial.
|
||||||
|
|
||||||
|
|
||||||
```java
|
```java
|
||||||
interface ContentStatement <: Statement {
|
interface ContentStatement <: Statement {
|
||||||
type: "ContentStatement";
|
type: "ContentStatement";
|
||||||
@@ -148,7 +147,6 @@ interface CommentStatement <: Statement {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
```java
|
```java
|
||||||
interface Decorator <: Statement {
|
interface Decorator <: Statement {
|
||||||
type: "Decorator";
|
type: "Decorator";
|
||||||
@@ -209,7 +207,6 @@ interface PathExpression <: Expression {
|
|||||||
- `parts` is an array of the names in the path. `foo.bar` would be `['foo', 'bar']`. Scope references, `.`, `..`, and `this` should be omitted from this array.
|
- `parts` is an array of the names in the path. `foo.bar` would be `['foo', 'bar']`. Scope references, `.`, `..`, and `this` should be omitted from this array.
|
||||||
- `original` is the path as entered by the user. Separator and scope references are left untouched.
|
- `original` is the path as entered by the user. Separator and scope references are left untouched.
|
||||||
|
|
||||||
|
|
||||||
##### Literals
|
##### Literals
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@@ -242,7 +239,6 @@ interface NullLiteral <: Literal {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Miscellaneous
|
### Miscellaneous
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@@ -279,8 +275,8 @@ function ImportScanner() {
|
|||||||
}
|
}
|
||||||
ImportScanner.prototype = new Visitor();
|
ImportScanner.prototype = new Visitor();
|
||||||
|
|
||||||
ImportScanner.prototype.PartialStatement = function(partial) {
|
ImportScanner.prototype.PartialStatement = function (partial) {
|
||||||
this.partials.push({request: partial.name.original});
|
this.partials.push({ request: partial.name.original });
|
||||||
|
|
||||||
Visitor.prototype.PartialStatement.call(this, partial);
|
Visitor.prototype.PartialStatement.call(this, partial);
|
||||||
};
|
};
|
||||||
@@ -291,7 +287,7 @@ scanner.accept(ast);
|
|||||||
|
|
||||||
The current node's ancestors will be maintained in the `parents` array, with the most recent parent listed first.
|
The current node's ancestors will be maintained in the `parents` array, with the most recent parent listed first.
|
||||||
|
|
||||||
The visitor may also be configured to operate in mutation mode by setting the `mutation` field to true. When in this mode, handler methods may return any valid AST node and it will replace the one they are currently operating on. Returning `false` will remove the given value (if valid) and returning `undefined` will leave the node in tact. This return structure only apply to mutation mode and non-mutation mode visitors are free to return whatever values they wish.
|
The visitor may also be configured to operate in mutation mode by setting the `mutating` field to true. When in this mode, handler methods may return any valid AST node and it will replace the one they are currently operating on. Returning `false` will remove the given value (if valid) and returning `undefined` will leave the node intact. This return structure only apply to mutation mode and non-mutation mode visitors are free to return whatever values they wish.
|
||||||
|
|
||||||
Implementors that may need to support mutation mode are encouraged to utilize the `acceptKey`, `acceptRequired` and `acceptArray` helpers which provide the conditional overwrite behavior as well as implement sanity checks where pertinent.
|
Implementors that may need to support mutation mode are encouraged to utilize the `acceptKey`, `acceptRequired` and `acceptArray` helpers which provide the conditional overwrite behavior as well as implement sanity checks where pertinent.
|
||||||
|
|
||||||
@@ -300,7 +296,7 @@ Implementors that may need to support mutation mode are encouraged to utilize th
|
|||||||
The `Handlebars.JavaScriptCompiler` object has a number of methods that may be customized to alter the output of the compiler:
|
The `Handlebars.JavaScriptCompiler` object has a number of methods that may be customized to alter the output of the compiler:
|
||||||
|
|
||||||
- `nameLookup(parent, name, type)`
|
- `nameLookup(parent, name, type)`
|
||||||
Used to generate the code to resolve a give path component.
|
Used to generate the code to resolve a given path component.
|
||||||
|
|
||||||
- `parent` is the existing code in the path resolution
|
- `parent` is the existing code in the path resolution
|
||||||
- `name` is the current path component
|
- `name` is the current path component
|
||||||
@@ -330,7 +326,6 @@ This example changes all lookups of properties are performed by a helper (`looku
|
|||||||
|
|
||||||
There is also [a jsfiddle with this code](https://jsfiddle.net/9D88g/162/) if you want to play around with it.
|
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);
|
||||||
@@ -338,29 +333,35 @@ function MyCompiler() {
|
|||||||
MyCompiler.prototype = new Handlebars.JavaScriptCompiler();
|
MyCompiler.prototype = new Handlebars.JavaScriptCompiler();
|
||||||
|
|
||||||
// Use this compile to compile BlockStatment-Blocks
|
// Use this compile to compile BlockStatment-Blocks
|
||||||
MyCompiler.prototype.compiler = MyCompiler
|
MyCompiler.prototype.compiler = MyCompiler;
|
||||||
|
|
||||||
MyCompiler.prototype.nameLookup = function(parent, name, type) {
|
MyCompiler.prototype.nameLookup = function (parent, name, type) {
|
||||||
if (type === 'context') {
|
if (type === 'context') {
|
||||||
return this.source.functionCall('helpers.lookupLowerCase', '', [parent, JSON.stringify(name)])
|
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) {
|
env.registerHelper('lookupLowerCase', function (parent, name) {
|
||||||
return parent[name.toLowerCase()]
|
return parent[name.toLowerCase()];
|
||||||
})
|
});
|
||||||
|
|
||||||
env.JavaScriptCompiler = MyCompiler;
|
env.JavaScriptCompiler = MyCompiler;
|
||||||
|
|
||||||
var template = env.compile('{{#each Test}} ({{Value}}) {{/each}}');
|
var template = env.compile('{{#each Test}} ({{Value}}) {{/each}}');
|
||||||
console.log(template({
|
console.log(
|
||||||
test: [
|
template({
|
||||||
{value: 'a'},
|
test: [{ value: 'a' }, { value: 'b' }, { value: 'c' }],
|
||||||
{value: 'b'},
|
})
|
||||||
{value: 'c'}
|
);
|
||||||
]
|
|
||||||
}));
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
# Decorators
|
# Decorators
|
||||||
|
|
||||||
Decorators allow for blocks to be annotated with metadata or wrapped in functionality prior to execution of the block. This may be used to communicate with the containing helper or to setup a particular state in the system prior to running the block.
|
**Decorators are deprecated, please join the discussion at [#1574](https://github.com/handlebars-lang/handlebars.js/issues/1574) to see what we can do about it.**
|
||||||
|
|
||||||
|
Decorators allow for blocks to be annotated with metadata or wrapped in functionality prior to execution of the block. This may be used to communicate with the containing helper or to set up a particular state in the system prior to running the block.
|
||||||
|
|
||||||
Decorators are registered through similar methods as helpers, `registerDecorators` and `unregisterDecorators`. These can then be referenced via the friendly name in the template using the `{{* decorator}}` and `{{#* decorator}}{/decorator}}` syntaxes. These syntaxes are derivatives of the normal mustache syntax and as such have all of the same argument and whitespace behaviors.
|
Decorators are registered through similar methods as helpers, `registerDecorators` and `unregisterDecorators`. These can then be referenced via the friendly name in the template using the `{{* decorator}}` and `{{#* decorator}}{/decorator}}` syntaxes. These syntaxes are derivatives of the normal mustache syntax and as such have all of the same argument and whitespace behaviors.
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -1,6 +1,7 @@
|
|||||||
|
/* eslint-env node */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
env: {
|
env: {
|
||||||
// Handlebars should run natively in the browser
|
// Handlebars should run natively in the browser
|
||||||
node: false
|
node: false,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
+9
-8
@@ -1,15 +1,16 @@
|
|||||||
|
import {
|
||||||
|
parser as Parser,
|
||||||
|
parse,
|
||||||
|
parseWithoutProcessing,
|
||||||
|
Visitor,
|
||||||
|
} from '@handlebars/parser';
|
||||||
|
|
||||||
import runtime from './handlebars.runtime';
|
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,
|
|
||||||
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 noConflict from './handlebars/no-conflict';
|
import noConflict from './handlebars/no-conflict';
|
||||||
|
|
||||||
@@ -17,10 +18,10 @@ let _create = runtime.create;
|
|||||||
function create() {
|
function create() {
|
||||||
let hb = _create();
|
let hb = _create();
|
||||||
|
|
||||||
hb.compile = function(input, options) {
|
hb.compile = function (input, options) {
|
||||||
return compile(input, options, hb);
|
return compile(input, options, hb);
|
||||||
};
|
};
|
||||||
hb.precompile = function(input, options) {
|
hb.precompile = function (input, options) {
|
||||||
return precompile(input, options, hb);
|
return precompile(input, options, hb);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
|
import { Exception } from '@handlebars/parser';
|
||||||
import * as base from './handlebars/base';
|
import * as base from './handlebars/base';
|
||||||
|
|
||||||
// Each of these augment the Handlebars object. No need to setup here.
|
// Each of these augment the Handlebars object. No need to setup here.
|
||||||
// (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 * as Utils from './handlebars/utils';
|
import * as Utils from './handlebars/utils';
|
||||||
import * as runtime from './handlebars/runtime';
|
import * as runtime from './handlebars/runtime';
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ function create() {
|
|||||||
hb.escapeExpression = Utils.escapeExpression;
|
hb.escapeExpression = Utils.escapeExpression;
|
||||||
|
|
||||||
hb.VM = runtime;
|
hb.VM = runtime;
|
||||||
hb.template = function(spec) {
|
hb.template = function (spec) {
|
||||||
return runtime.template(spec, hb);
|
return runtime.template(spec, hb);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
import { Exception } from '@handlebars/parser';
|
||||||
import { createFrame, extend, toString } from './utils';
|
import { createFrame, extend, toString } from './utils';
|
||||||
import Exception from './exception';
|
|
||||||
import { registerDefaultHelpers } from './helpers';
|
import { registerDefaultHelpers } from './helpers';
|
||||||
import { registerDefaultDecorators } from './decorators';
|
import { registerDefaultDecorators } from './decorators';
|
||||||
import logger from './logger';
|
import logger from './logger';
|
||||||
@@ -17,7 +17,7 @@ export const REVISION_CHANGES = {
|
|||||||
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 <4.3.0',
|
7: '>= 4.0.0 <4.3.0',
|
||||||
8: '>= 4.3.0'
|
8: '>= 4.3.0',
|
||||||
};
|
};
|
||||||
|
|
||||||
const objectType = '[object Object]';
|
const objectType = '[object Object]';
|
||||||
@@ -37,7 +37,7 @@ HandlebarsEnvironment.prototype = {
|
|||||||
logger: logger,
|
logger: logger,
|
||||||
log: logger.log,
|
log: logger.log,
|
||||||
|
|
||||||
registerHelper: function(name, fn) {
|
registerHelper: function (name, fn) {
|
||||||
if (toString.call(name) === objectType) {
|
if (toString.call(name) === objectType) {
|
||||||
if (fn) {
|
if (fn) {
|
||||||
throw new Exception('Arg not supported with multiple helpers');
|
throw new Exception('Arg not supported with multiple helpers');
|
||||||
@@ -47,11 +47,11 @@ HandlebarsEnvironment.prototype = {
|
|||||||
this.helpers[name] = fn;
|
this.helpers[name] = fn;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
unregisterHelper: function(name) {
|
unregisterHelper: function (name) {
|
||||||
delete this.helpers[name];
|
delete this.helpers[name];
|
||||||
},
|
},
|
||||||
|
|
||||||
registerPartial: function(name, partial) {
|
registerPartial: function (name, partial) {
|
||||||
if (toString.call(name) === objectType) {
|
if (toString.call(name) === objectType) {
|
||||||
extend(this.partials, name);
|
extend(this.partials, name);
|
||||||
} else {
|
} else {
|
||||||
@@ -63,11 +63,11 @@ HandlebarsEnvironment.prototype = {
|
|||||||
this.partials[name] = partial;
|
this.partials[name] = partial;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
unregisterPartial: function(name) {
|
unregisterPartial: function (name) {
|
||||||
delete this.partials[name];
|
delete this.partials[name];
|
||||||
},
|
},
|
||||||
|
|
||||||
registerDecorator: function(name, fn) {
|
registerDecorator: function (name, fn) {
|
||||||
if (toString.call(name) === objectType) {
|
if (toString.call(name) === objectType) {
|
||||||
if (fn) {
|
if (fn) {
|
||||||
throw new Exception('Arg not supported with multiple decorators');
|
throw new Exception('Arg not supported with multiple decorators');
|
||||||
@@ -77,7 +77,7 @@ HandlebarsEnvironment.prototype = {
|
|||||||
this.decorators[name] = fn;
|
this.decorators[name] = fn;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
unregisterDecorator: function(name) {
|
unregisterDecorator: function (name) {
|
||||||
delete this.decorators[name];
|
delete this.decorators[name];
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -86,7 +86,7 @@ HandlebarsEnvironment.prototype = {
|
|||||||
*/
|
*/
|
||||||
resetLoggedPropertyAccesses() {
|
resetLoggedPropertyAccesses() {
|
||||||
resetLoggedProperties();
|
resetLoggedProperties();
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export let log = logger.log;
|
export let log = logger.log;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ let AST = {
|
|||||||
// a mustache is definitely a helper if:
|
// a mustache is definitely a helper if:
|
||||||
// * it is an eligible helper, and
|
// * it is an eligible helper, and
|
||||||
// * it has at least one parameter or hash segment
|
// * it has at least one parameter or hash segment
|
||||||
helperExpression: function(node) {
|
helperExpression: function (node) {
|
||||||
return (
|
return (
|
||||||
node.type === 'SubExpression' ||
|
node.type === 'SubExpression' ||
|
||||||
((node.type === 'MustacheStatement' ||
|
((node.type === 'MustacheStatement' ||
|
||||||
@@ -13,18 +13,18 @@ let AST = {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
scopedId: function(path) {
|
scopedId: function (path) {
|
||||||
return /^\.|this\b/.test(path.original);
|
return /^\.|this\b/.test(path.original);
|
||||||
},
|
},
|
||||||
|
|
||||||
// an ID is simple if it only has one part, and that part is not
|
// an ID is simple if it only has one part, and that part is not
|
||||||
// `..` or `this`.
|
// `..` or `this`.
|
||||||
simpleId: function(path) {
|
simpleId: function (path) {
|
||||||
return (
|
return (
|
||||||
path.parts.length === 1 && !AST.helpers.scopedId(path) && !path.depth
|
path.parts.length === 1 && !AST.helpers.scopedId(path) && !path.depth
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Must be exported as an object rather than the root of the module as the jison lexer
|
// Must be exported as an object rather than the root of the module as the jison lexer
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
import parser from './parser';
|
|
||||||
import WhitespaceControl from './whitespace-control';
|
|
||||||
import * as Helpers from './helpers';
|
|
||||||
import { extend } from '../utils';
|
|
||||||
|
|
||||||
export { parser };
|
|
||||||
|
|
||||||
let yy = {};
|
|
||||||
extend(yy, Helpers);
|
|
||||||
|
|
||||||
export function parseWithoutProcessing(input, options) {
|
|
||||||
// Just return if an already-compiled AST was passed in.
|
|
||||||
if (input.type === 'Program') {
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
parser.yy = yy;
|
|
||||||
|
|
||||||
// Altering the shared object here, but this is ok as parser is a sync operation
|
|
||||||
yy.locInfo = function(locInfo) {
|
|
||||||
return new yy.SourceLocation(options && options.srcName, locInfo);
|
|
||||||
};
|
|
||||||
|
|
||||||
let ast = parser.parse(input);
|
|
||||||
|
|
||||||
return ast;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function parse(input, options) {
|
|
||||||
let ast = parseWithoutProcessing(input, options);
|
|
||||||
let strip = new WhitespaceControl(options);
|
|
||||||
|
|
||||||
return strip.accept(ast);
|
|
||||||
}
|
|
||||||
@@ -17,7 +17,7 @@ try {
|
|||||||
|
|
||||||
/* istanbul ignore if: tested but not covered in istanbul due to dist build */
|
/* istanbul ignore if: tested but not covered in istanbul due to dist build */
|
||||||
if (!SourceNode) {
|
if (!SourceNode) {
|
||||||
SourceNode = function(line, column, srcFile, chunks) {
|
SourceNode = function (line, column, srcFile, chunks) {
|
||||||
this.src = '';
|
this.src = '';
|
||||||
if (chunks) {
|
if (chunks) {
|
||||||
this.add(chunks);
|
this.add(chunks);
|
||||||
@@ -25,24 +25,24 @@ if (!SourceNode) {
|
|||||||
};
|
};
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
SourceNode.prototype = {
|
SourceNode.prototype = {
|
||||||
add: function(chunks) {
|
add: function (chunks) {
|
||||||
if (isArray(chunks)) {
|
if (isArray(chunks)) {
|
||||||
chunks = chunks.join('');
|
chunks = chunks.join('');
|
||||||
}
|
}
|
||||||
this.src += chunks;
|
this.src += chunks;
|
||||||
},
|
},
|
||||||
prepend: function(chunks) {
|
prepend: function (chunks) {
|
||||||
if (isArray(chunks)) {
|
if (isArray(chunks)) {
|
||||||
chunks = chunks.join('');
|
chunks = chunks.join('');
|
||||||
}
|
}
|
||||||
this.src = chunks + this.src;
|
this.src = chunks + this.src;
|
||||||
},
|
},
|
||||||
toStringWithSourceMap: function() {
|
toStringWithSourceMap: function () {
|
||||||
return { code: this.toString() };
|
return { code: this.toString() };
|
||||||
},
|
},
|
||||||
toString: function() {
|
toString: function () {
|
||||||
return this.src;
|
return this.src;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,32 +70,32 @@ CodeGen.prototype = {
|
|||||||
isEmpty() {
|
isEmpty() {
|
||||||
return !this.source.length;
|
return !this.source.length;
|
||||||
},
|
},
|
||||||
prepend: function(source, loc) {
|
prepend: function (source, loc) {
|
||||||
this.source.unshift(this.wrap(source, loc));
|
this.source.unshift(this.wrap(source, loc));
|
||||||
},
|
},
|
||||||
push: function(source, loc) {
|
push: function (source, loc) {
|
||||||
this.source.push(this.wrap(source, loc));
|
this.source.push(this.wrap(source, loc));
|
||||||
},
|
},
|
||||||
|
|
||||||
merge: function() {
|
merge: function () {
|
||||||
let source = this.empty();
|
let source = this.empty();
|
||||||
this.each(function(line) {
|
this.each(function (line) {
|
||||||
source.add([' ', line, '\n']);
|
source.add([' ', line, '\n']);
|
||||||
});
|
});
|
||||||
return source;
|
return source;
|
||||||
},
|
},
|
||||||
|
|
||||||
each: function(iter) {
|
each: function (iter) {
|
||||||
for (let i = 0, len = this.source.length; i < len; i++) {
|
for (let i = 0, len = this.source.length; i < len; i++) {
|
||||||
iter(this.source[i]);
|
iter(this.source[i]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
empty: function() {
|
empty: function () {
|
||||||
let loc = this.currentLocation || { start: {} };
|
let loc = this.currentLocation || { start: {} };
|
||||||
return new SourceNode(loc.start.line, loc.start.column, this.srcFile);
|
return new SourceNode(loc.start.line, loc.start.column, this.srcFile);
|
||||||
},
|
},
|
||||||
wrap: function(chunk, loc = this.currentLocation || { start: {} }) {
|
wrap: function (chunk, loc = this.currentLocation || { start: {} }) {
|
||||||
if (chunk instanceof SourceNode) {
|
if (chunk instanceof SourceNode) {
|
||||||
return chunk;
|
return chunk;
|
||||||
}
|
}
|
||||||
@@ -110,12 +110,12 @@ CodeGen.prototype = {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
functionCall: function(fn, type, params) {
|
functionCall: function (fn, type, params) {
|
||||||
params = this.generateList(params);
|
params = this.generateList(params);
|
||||||
return this.wrap([fn, type ? '.' + type + '(' : '(', params, ')']);
|
return this.wrap([fn, type ? '.' + type + '(' : '(', params, ')']);
|
||||||
},
|
},
|
||||||
|
|
||||||
quotedString: function(str) {
|
quotedString: function (str) {
|
||||||
return (
|
return (
|
||||||
'"' +
|
'"' +
|
||||||
(str + '')
|
(str + '')
|
||||||
@@ -129,10 +129,10 @@ CodeGen.prototype = {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
objectLiteral: function(obj) {
|
objectLiteral: function (obj) {
|
||||||
let pairs = [];
|
let pairs = [];
|
||||||
|
|
||||||
Object.keys(obj).forEach(key => {
|
Object.keys(obj).forEach((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]);
|
||||||
@@ -145,7 +145,7 @@ CodeGen.prototype = {
|
|||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
|
|
||||||
generateList: function(entries) {
|
generateList: function (entries) {
|
||||||
let ret = this.empty();
|
let ret = this.empty();
|
||||||
|
|
||||||
for (let i = 0, len = entries.length; i < len; i++) {
|
for (let i = 0, len = entries.length; i < len; i++) {
|
||||||
@@ -159,13 +159,13 @@ CodeGen.prototype = {
|
|||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
|
|
||||||
generateArray: function(entries) {
|
generateArray: function (entries) {
|
||||||
let ret = this.generateList(entries);
|
let ret = this.generateList(entries);
|
||||||
ret.prepend('[');
|
ret.prepend('[');
|
||||||
ret.add(']');
|
ret.add(']');
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CodeGen;
|
export default CodeGen;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* eslint-disable new-cap */
|
/* eslint-disable new-cap */
|
||||||
|
|
||||||
import Exception from '../exception';
|
import { Exception } from '@handlebars/parser';
|
||||||
import { isArray, indexOf, extend } from '../utils';
|
import { isArray, indexOf, extend } from '../utils';
|
||||||
import AST from './ast';
|
import AST from './ast';
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ export function Compiler() {}
|
|||||||
Compiler.prototype = {
|
Compiler.prototype = {
|
||||||
compiler: Compiler,
|
compiler: Compiler,
|
||||||
|
|
||||||
equals: function(other) {
|
equals: function (other) {
|
||||||
let len = this.opcodes.length;
|
let len = this.opcodes.length;
|
||||||
if (other.opcodes.length !== len) {
|
if (other.opcodes.length !== len) {
|
||||||
return false;
|
return false;
|
||||||
@@ -47,13 +47,11 @@ Compiler.prototype = {
|
|||||||
|
|
||||||
guid: 0,
|
guid: 0,
|
||||||
|
|
||||||
compile: function(program, options) {
|
compile: function (program, options) {
|
||||||
this.sourceNode = [];
|
this.sourceNode = [];
|
||||||
this.opcodes = [];
|
this.opcodes = [];
|
||||||
this.children = [];
|
this.children = [];
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.stringParams = options.stringParams;
|
|
||||||
this.trackIds = options.trackIds;
|
|
||||||
|
|
||||||
options.blockParams = options.blockParams || [];
|
options.blockParams = options.blockParams || [];
|
||||||
|
|
||||||
@@ -67,7 +65,7 @@ Compiler.prototype = {
|
|||||||
unless: true,
|
unless: true,
|
||||||
with: true,
|
with: true,
|
||||||
log: true,
|
log: true,
|
||||||
lookup: true
|
lookup: true,
|
||||||
},
|
},
|
||||||
options.knownHelpers
|
options.knownHelpers
|
||||||
);
|
);
|
||||||
@@ -75,7 +73,7 @@ Compiler.prototype = {
|
|||||||
return this.accept(program);
|
return this.accept(program);
|
||||||
},
|
},
|
||||||
|
|
||||||
compileProgram: function(program) {
|
compileProgram: function (program) {
|
||||||
let childCompiler = new this.compiler(), // eslint-disable-line new-cap
|
let childCompiler = new this.compiler(), // eslint-disable-line new-cap
|
||||||
result = childCompiler.compile(program, this.options),
|
result = childCompiler.compile(program, this.options),
|
||||||
guid = this.guid++;
|
guid = this.guid++;
|
||||||
@@ -88,7 +86,7 @@ Compiler.prototype = {
|
|||||||
return guid;
|
return guid;
|
||||||
},
|
},
|
||||||
|
|
||||||
accept: function(node) {
|
accept: function (node) {
|
||||||
/* istanbul ignore next: Sanity code */
|
/* istanbul ignore next: Sanity code */
|
||||||
if (!this[node.type]) {
|
if (!this[node.type]) {
|
||||||
throw new Exception('Unknown type: ' + node.type, node);
|
throw new Exception('Unknown type: ' + node.type, node);
|
||||||
@@ -100,7 +98,7 @@ Compiler.prototype = {
|
|||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
|
|
||||||
Program: function(program) {
|
Program: function (program) {
|
||||||
this.options.blockParams.unshift(program.blockParams);
|
this.options.blockParams.unshift(program.blockParams);
|
||||||
|
|
||||||
let body = program.body,
|
let body = program.body,
|
||||||
@@ -117,7 +115,7 @@ Compiler.prototype = {
|
|||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
BlockStatement: function(block) {
|
BlockStatement: function (block) {
|
||||||
transformLiteralToPath(block);
|
transformLiteralToPath(block);
|
||||||
|
|
||||||
let program = block.program,
|
let program = block.program,
|
||||||
@@ -162,7 +160,7 @@ Compiler.prototype = {
|
|||||||
this.opcode('registerDecorator', params.length, path.original);
|
this.opcode('registerDecorator', params.length, path.original);
|
||||||
},
|
},
|
||||||
|
|
||||||
PartialStatement: function(partial) {
|
PartialStatement: function (partial) {
|
||||||
this.usePartial = true;
|
this.usePartial = true;
|
||||||
|
|
||||||
let program = partial.program;
|
let program = partial.program;
|
||||||
@@ -201,11 +199,11 @@ Compiler.prototype = {
|
|||||||
this.opcode('invokePartial', isDynamic, partialName, indent);
|
this.opcode('invokePartial', isDynamic, partialName, indent);
|
||||||
this.opcode('append');
|
this.opcode('append');
|
||||||
},
|
},
|
||||||
PartialBlockStatement: function(partialBlock) {
|
PartialBlockStatement: function (partialBlock) {
|
||||||
this.PartialStatement(partialBlock);
|
this.PartialStatement(partialBlock);
|
||||||
},
|
},
|
||||||
|
|
||||||
MustacheStatement: function(mustache) {
|
MustacheStatement: function (mustache) {
|
||||||
this.SubExpression(mustache);
|
this.SubExpression(mustache);
|
||||||
|
|
||||||
if (mustache.escaped && !this.options.noEscape) {
|
if (mustache.escaped && !this.options.noEscape) {
|
||||||
@@ -218,15 +216,15 @@ Compiler.prototype = {
|
|||||||
this.DecoratorBlock(decorator);
|
this.DecoratorBlock(decorator);
|
||||||
},
|
},
|
||||||
|
|
||||||
ContentStatement: function(content) {
|
ContentStatement: function (content) {
|
||||||
if (content.value) {
|
if (content.value) {
|
||||||
this.opcode('appendContent', content.value);
|
this.opcode('appendContent', content.value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
CommentStatement: function() {},
|
CommentStatement: function () {},
|
||||||
|
|
||||||
SubExpression: function(sexpr) {
|
SubExpression: function (sexpr) {
|
||||||
transformLiteralToPath(sexpr);
|
transformLiteralToPath(sexpr);
|
||||||
let type = this.classifySexpr(sexpr);
|
let type = this.classifySexpr(sexpr);
|
||||||
|
|
||||||
@@ -238,7 +236,7 @@ Compiler.prototype = {
|
|||||||
this.ambiguousSexpr(sexpr);
|
this.ambiguousSexpr(sexpr);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ambiguousSexpr: function(sexpr, program, inverse) {
|
ambiguousSexpr: function (sexpr, program, inverse) {
|
||||||
let path = sexpr.path,
|
let path = sexpr.path,
|
||||||
name = path.parts[0],
|
name = path.parts[0],
|
||||||
isBlock = program != null || inverse != null;
|
isBlock = program != null || inverse != null;
|
||||||
@@ -254,14 +252,14 @@ Compiler.prototype = {
|
|||||||
this.opcode('invokeAmbiguous', name, isBlock);
|
this.opcode('invokeAmbiguous', name, isBlock);
|
||||||
},
|
},
|
||||||
|
|
||||||
simpleSexpr: function(sexpr) {
|
simpleSexpr: function (sexpr) {
|
||||||
let path = sexpr.path;
|
let path = sexpr.path;
|
||||||
path.strict = true;
|
path.strict = true;
|
||||||
this.accept(path);
|
this.accept(path);
|
||||||
this.opcode('resolvePossibleLambda');
|
this.opcode('resolvePossibleLambda');
|
||||||
},
|
},
|
||||||
|
|
||||||
helperSexpr: function(sexpr, program, inverse) {
|
helperSexpr: function (sexpr, program, inverse) {
|
||||||
let params = this.setupFullMustacheParams(sexpr, program, inverse),
|
let params = this.setupFullMustacheParams(sexpr, program, inverse),
|
||||||
path = sexpr.path,
|
path = sexpr.path,
|
||||||
name = path.parts[0];
|
name = path.parts[0];
|
||||||
@@ -287,7 +285,7 @@ Compiler.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
PathExpression: function(path) {
|
PathExpression: function (path) {
|
||||||
this.addDepth(path.depth);
|
this.addDepth(path.depth);
|
||||||
this.opcode('getContext', path.depth);
|
this.opcode('getContext', path.depth);
|
||||||
|
|
||||||
@@ -314,27 +312,27 @@ Compiler.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
StringLiteral: function(string) {
|
StringLiteral: function (string) {
|
||||||
this.opcode('pushString', string.value);
|
this.opcode('pushString', string.value);
|
||||||
},
|
},
|
||||||
|
|
||||||
NumberLiteral: function(number) {
|
NumberLiteral: function (number) {
|
||||||
this.opcode('pushLiteral', number.value);
|
this.opcode('pushLiteral', number.value);
|
||||||
},
|
},
|
||||||
|
|
||||||
BooleanLiteral: function(bool) {
|
BooleanLiteral: function (bool) {
|
||||||
this.opcode('pushLiteral', bool.value);
|
this.opcode('pushLiteral', bool.value);
|
||||||
},
|
},
|
||||||
|
|
||||||
UndefinedLiteral: function() {
|
UndefinedLiteral: function () {
|
||||||
this.opcode('pushLiteral', 'undefined');
|
this.opcode('pushLiteral', 'undefined');
|
||||||
},
|
},
|
||||||
|
|
||||||
NullLiteral: function() {
|
NullLiteral: function () {
|
||||||
this.opcode('pushLiteral', 'null');
|
this.opcode('pushLiteral', 'null');
|
||||||
},
|
},
|
||||||
|
|
||||||
Hash: function(hash) {
|
Hash: function (hash) {
|
||||||
let pairs = hash.pairs,
|
let pairs = hash.pairs,
|
||||||
i = 0,
|
i = 0,
|
||||||
l = pairs.length;
|
l = pairs.length;
|
||||||
@@ -351,15 +349,15 @@ Compiler.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// HELPERS
|
// HELPERS
|
||||||
opcode: function(name) {
|
opcode: function (name) {
|
||||||
this.opcodes.push({
|
this.opcodes.push({
|
||||||
opcode: name,
|
opcode: name,
|
||||||
args: slice.call(arguments, 1),
|
args: slice.call(arguments, 1),
|
||||||
loc: this.sourceNode[0].loc
|
loc: this.sourceNode[0].loc,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
addDepth: function(depth) {
|
addDepth: function (depth) {
|
||||||
if (!depth) {
|
if (!depth) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -367,7 +365,7 @@ Compiler.prototype = {
|
|||||||
this.useDepths = true;
|
this.useDepths = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
classifySexpr: function(sexpr) {
|
classifySexpr: function (sexpr) {
|
||||||
let isSimple = AST.helpers.simpleId(sexpr.path);
|
let isSimple = AST.helpers.simpleId(sexpr.path);
|
||||||
|
|
||||||
let isBlockParam = isSimple && !!this.blockParamIndex(sexpr.path.parts[0]);
|
let isBlockParam = isSimple && !!this.blockParamIndex(sexpr.path.parts[0]);
|
||||||
@@ -402,57 +400,17 @@ Compiler.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
pushParams: function(params) {
|
pushParams: function (params) {
|
||||||
for (let i = 0, l = params.length; i < l; i++) {
|
for (let i = 0, l = params.length; i < l; i++) {
|
||||||
this.pushParam(params[i]);
|
this.pushParam(params[i]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
pushParam: function(val) {
|
pushParam: function (val) {
|
||||||
let value = val.value != null ? val.value : val.original || '';
|
|
||||||
|
|
||||||
if (this.stringParams) {
|
|
||||||
if (value.replace) {
|
|
||||||
value = value.replace(/^(\.?\.\/)*/g, '').replace(/\//g, '.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (val.depth) {
|
|
||||||
this.addDepth(val.depth);
|
|
||||||
}
|
|
||||||
this.opcode('getContext', val.depth || 0);
|
|
||||||
this.opcode('pushStringParam', value, val.type);
|
|
||||||
|
|
||||||
if (val.type === 'SubExpression') {
|
|
||||||
// SubExpressions get evaluated and passed in
|
|
||||||
// in string params mode.
|
|
||||||
this.accept(val);
|
this.accept(val);
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (this.trackIds) {
|
|
||||||
let blockParamIndex;
|
|
||||||
if (val.parts && !AST.helpers.scopedId(val) && !val.depth) {
|
|
||||||
blockParamIndex = this.blockParamIndex(val.parts[0]);
|
|
||||||
}
|
|
||||||
if (blockParamIndex) {
|
|
||||||
let blockParamChild = val.parts.slice(1).join('.');
|
|
||||||
this.opcode('pushId', 'BlockParam', blockParamIndex, blockParamChild);
|
|
||||||
} else {
|
|
||||||
value = val.original || value;
|
|
||||||
if (value.replace) {
|
|
||||||
value = value
|
|
||||||
.replace(/^this(?:\.|$)/, '')
|
|
||||||
.replace(/^\.\//, '')
|
|
||||||
.replace(/^\.$/, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.opcode('pushId', val.type, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.accept(val);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setupFullMustacheParams: function(sexpr, program, inverse, omitEmpty) {
|
setupFullMustacheParams: function (sexpr, program, inverse, omitEmpty) {
|
||||||
let params = sexpr.params;
|
let params = sexpr.params;
|
||||||
this.pushParams(params);
|
this.pushParams(params);
|
||||||
|
|
||||||
@@ -468,7 +426,7 @@ Compiler.prototype = {
|
|||||||
return params;
|
return params;
|
||||||
},
|
},
|
||||||
|
|
||||||
blockParamIndex: function(name) {
|
blockParamIndex: function (name) {
|
||||||
for (
|
for (
|
||||||
let depth = 0, len = this.options.blockParams.length;
|
let depth = 0, len = this.options.blockParams.length;
|
||||||
depth < len;
|
depth < len;
|
||||||
@@ -480,57 +438,25 @@ Compiler.prototype = {
|
|||||||
return [depth, param];
|
return [depth, param];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export function precompile(input, options, env) {
|
export function precompile(input, options = {}, env) {
|
||||||
if (
|
validateInput(input, options);
|
||||||
input == null ||
|
|
||||||
(typeof input !== 'string' && input.type !== 'Program')
|
|
||||||
) {
|
|
||||||
throw new Exception(
|
|
||||||
'You must pass a string or Handlebars AST to Handlebars.precompile. You passed ' +
|
|
||||||
input
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
options = options || {};
|
let environment = compileEnvironment(input, options, env);
|
||||||
if (!('data' in options)) {
|
|
||||||
options.data = true;
|
|
||||||
}
|
|
||||||
if (options.compat) {
|
|
||||||
options.useDepths = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let ast = env.parse(input, options),
|
|
||||||
environment = new env.Compiler().compile(ast, options);
|
|
||||||
return new env.JavaScriptCompiler().compile(environment, options);
|
return new env.JavaScriptCompiler().compile(environment, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function compile(input, options = {}, env) {
|
export function compile(input, options = {}, env) {
|
||||||
if (
|
|
||||||
input == null ||
|
|
||||||
(typeof input !== 'string' && input.type !== 'Program')
|
|
||||||
) {
|
|
||||||
throw new Exception(
|
|
||||||
'You must pass a string or Handlebars AST to Handlebars.compile. You passed ' +
|
|
||||||
input
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
options = extend({}, options);
|
options = extend({}, options);
|
||||||
if (!('data' in options)) {
|
|
||||||
options.data = true;
|
validateInput(input, options);
|
||||||
}
|
|
||||||
if (options.compat) {
|
|
||||||
options.useDepths = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let compiled;
|
let compiled;
|
||||||
|
|
||||||
function compileInput() {
|
function compileInput() {
|
||||||
let ast = env.parse(input, options),
|
let environment = compileEnvironment(input, options, env),
|
||||||
environment = new env.Compiler().compile(ast, options),
|
|
||||||
templateSpec = new env.JavaScriptCompiler().compile(
|
templateSpec = new env.JavaScriptCompiler().compile(
|
||||||
environment,
|
environment,
|
||||||
options,
|
options,
|
||||||
@@ -541,25 +467,41 @@ export function compile(input, options = {}, env) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Template is only compiled on first use and cached after that point.
|
// Template is only compiled on first use and cached after that point.
|
||||||
function ret(context, execOptions) {
|
return function (context, execOptions) {
|
||||||
if (!compiled) {
|
if (!compiled) {
|
||||||
compiled = compileInput();
|
compiled = compileInput();
|
||||||
}
|
}
|
||||||
return compiled.call(this, context, execOptions);
|
return compiled.call(this, context, execOptions);
|
||||||
}
|
|
||||||
ret._setup = function(setupOptions) {
|
|
||||||
if (!compiled) {
|
|
||||||
compiled = compileInput();
|
|
||||||
}
|
|
||||||
return compiled._setup(setupOptions);
|
|
||||||
};
|
};
|
||||||
ret._child = function(i, data, blockParams, depths) {
|
}
|
||||||
if (!compiled) {
|
|
||||||
compiled = compileInput();
|
function validateInput(input, options) {
|
||||||
|
if (
|
||||||
|
input == null ||
|
||||||
|
(typeof input !== 'string' && input.type !== 'Program')
|
||||||
|
) {
|
||||||
|
throw new Exception(
|
||||||
|
'You must pass a string or Handlebars AST to Handlebars.compile. You passed ' +
|
||||||
|
input
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return compiled._child(i, data, blockParams, depths);
|
|
||||||
};
|
if (options.trackIds || options.stringParams) {
|
||||||
return ret;
|
throw new Exception(
|
||||||
|
'TrackIds and stringParams are no longer supported. See Github #1145'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!('data' in options)) {
|
||||||
|
options.data = true;
|
||||||
|
}
|
||||||
|
if (options.compat) {
|
||||||
|
options.useDepths = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function compileEnvironment(input, options, env) {
|
||||||
|
let ast = env.parse(input, options);
|
||||||
|
return new env.Compiler().compile(ast, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
function argEquals(a, b) {
|
function argEquals(a, b) {
|
||||||
@@ -588,7 +530,7 @@ function transformLiteralToPath(sexpr) {
|
|||||||
depth: 0,
|
depth: 0,
|
||||||
parts: [literal.original + ''],
|
parts: [literal.original + ''],
|
||||||
original: literal.original + '',
|
original: literal.original + '',
|
||||||
loc: literal.loc
|
loc: literal.loc,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,219 +0,0 @@
|
|||||||
import Exception from '../exception';
|
|
||||||
|
|
||||||
function validateClose(open, close) {
|
|
||||||
close = close.path ? close.path.original : close;
|
|
||||||
|
|
||||||
if (open.path.original !== close) {
|
|
||||||
let errorNode = { loc: open.path.loc };
|
|
||||||
|
|
||||||
throw new Exception(
|
|
||||||
open.path.original + " doesn't match " + close,
|
|
||||||
errorNode
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SourceLocation(source, locInfo) {
|
|
||||||
this.source = source;
|
|
||||||
this.start = {
|
|
||||||
line: locInfo.first_line,
|
|
||||||
column: locInfo.first_column
|
|
||||||
};
|
|
||||||
this.end = {
|
|
||||||
line: locInfo.last_line,
|
|
||||||
column: locInfo.last_column
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function id(token) {
|
|
||||||
if (/^\[.*\]$/.test(token)) {
|
|
||||||
return token.substring(1, token.length - 1);
|
|
||||||
} else {
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function stripFlags(open, close) {
|
|
||||||
return {
|
|
||||||
open: open.charAt(2) === '~',
|
|
||||||
close: close.charAt(close.length - 3) === '~'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function stripComment(comment) {
|
|
||||||
return comment.replace(/^\{\{~?!-?-?/, '').replace(/-?-?~?\}\}$/, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function preparePath(data, parts, loc) {
|
|
||||||
loc = this.locInfo(loc);
|
|
||||||
|
|
||||||
let original = data ? '@' : '',
|
|
||||||
dig = [],
|
|
||||||
depth = 0;
|
|
||||||
|
|
||||||
for (let i = 0, l = parts.length; i < l; i++) {
|
|
||||||
let part = parts[i].part,
|
|
||||||
// If we have [] syntax then we do not treat path references as operators,
|
|
||||||
// i.e. foo.[this] resolves to approximately context.foo['this']
|
|
||||||
isLiteral = parts[i].original !== part;
|
|
||||||
original += (parts[i].separator || '') + part;
|
|
||||||
|
|
||||||
if (!isLiteral && (part === '..' || part === '.' || part === 'this')) {
|
|
||||||
if (dig.length > 0) {
|
|
||||||
throw new Exception('Invalid path: ' + original, { loc });
|
|
||||||
} else if (part === '..') {
|
|
||||||
depth++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
dig.push(part);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: 'PathExpression',
|
|
||||||
data,
|
|
||||||
depth,
|
|
||||||
parts: dig,
|
|
||||||
original,
|
|
||||||
loc
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function prepareMustache(path, params, hash, open, strip, locInfo) {
|
|
||||||
// Must use charAt to support IE pre-10
|
|
||||||
let escapeFlag = open.charAt(3) || open.charAt(2),
|
|
||||||
escaped = escapeFlag !== '{' && escapeFlag !== '&';
|
|
||||||
|
|
||||||
let decorator = /\*/.test(open);
|
|
||||||
return {
|
|
||||||
type: decorator ? 'Decorator' : 'MustacheStatement',
|
|
||||||
path,
|
|
||||||
params,
|
|
||||||
hash,
|
|
||||||
escaped,
|
|
||||||
strip,
|
|
||||||
loc: this.locInfo(locInfo)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function prepareRawBlock(openRawBlock, contents, close, locInfo) {
|
|
||||||
validateClose(openRawBlock, close);
|
|
||||||
|
|
||||||
locInfo = this.locInfo(locInfo);
|
|
||||||
let program = {
|
|
||||||
type: 'Program',
|
|
||||||
body: contents,
|
|
||||||
strip: {},
|
|
||||||
loc: locInfo
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: 'BlockStatement',
|
|
||||||
path: openRawBlock.path,
|
|
||||||
params: openRawBlock.params,
|
|
||||||
hash: openRawBlock.hash,
|
|
||||||
program,
|
|
||||||
openStrip: {},
|
|
||||||
inverseStrip: {},
|
|
||||||
closeStrip: {},
|
|
||||||
loc: locInfo
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function prepareBlock(
|
|
||||||
openBlock,
|
|
||||||
program,
|
|
||||||
inverseAndProgram,
|
|
||||||
close,
|
|
||||||
inverted,
|
|
||||||
locInfo
|
|
||||||
) {
|
|
||||||
if (close && close.path) {
|
|
||||||
validateClose(openBlock, close);
|
|
||||||
}
|
|
||||||
|
|
||||||
let decorator = /\*/.test(openBlock.open);
|
|
||||||
|
|
||||||
program.blockParams = openBlock.blockParams;
|
|
||||||
|
|
||||||
let inverse, inverseStrip;
|
|
||||||
|
|
||||||
if (inverseAndProgram) {
|
|
||||||
if (decorator) {
|
|
||||||
throw new Exception(
|
|
||||||
'Unexpected inverse block on decorator',
|
|
||||||
inverseAndProgram
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inverseAndProgram.chain) {
|
|
||||||
inverseAndProgram.program.body[0].closeStrip = close.strip;
|
|
||||||
}
|
|
||||||
|
|
||||||
inverseStrip = inverseAndProgram.strip;
|
|
||||||
inverse = inverseAndProgram.program;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inverted) {
|
|
||||||
inverted = inverse;
|
|
||||||
inverse = program;
|
|
||||||
program = inverted;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: decorator ? 'DecoratorBlock' : 'BlockStatement',
|
|
||||||
path: openBlock.path,
|
|
||||||
params: openBlock.params,
|
|
||||||
hash: openBlock.hash,
|
|
||||||
program,
|
|
||||||
inverse,
|
|
||||||
openStrip: openBlock.strip,
|
|
||||||
inverseStrip,
|
|
||||||
closeStrip: close && close.strip,
|
|
||||||
loc: this.locInfo(locInfo)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function prepareProgram(statements, loc) {
|
|
||||||
if (!loc && statements.length) {
|
|
||||||
const firstLoc = statements[0].loc,
|
|
||||||
lastLoc = statements[statements.length - 1].loc;
|
|
||||||
|
|
||||||
/* istanbul ignore else */
|
|
||||||
if (firstLoc && lastLoc) {
|
|
||||||
loc = {
|
|
||||||
source: firstLoc.source,
|
|
||||||
start: {
|
|
||||||
line: firstLoc.start.line,
|
|
||||||
column: firstLoc.start.column
|
|
||||||
},
|
|
||||||
end: {
|
|
||||||
line: lastLoc.end.line,
|
|
||||||
column: lastLoc.end.column
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: 'Program',
|
|
||||||
body: statements,
|
|
||||||
strip: {},
|
|
||||||
loc: loc
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function preparePartialBlock(open, program, close, locInfo) {
|
|
||||||
validateClose(open, close);
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: 'PartialBlockStatement',
|
|
||||||
name: open.path,
|
|
||||||
params: open.params,
|
|
||||||
hash: open.hash,
|
|
||||||
program,
|
|
||||||
openStrip: open.strip,
|
|
||||||
closeStrip: close && close.strip,
|
|
||||||
loc: this.locInfo(locInfo)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
|
import { Exception } from '@handlebars/parser';
|
||||||
import { COMPILER_REVISION, REVISION_CHANGES } from '../base';
|
import { COMPILER_REVISION, REVISION_CHANGES } from '../base';
|
||||||
import Exception from '../exception';
|
|
||||||
import { isArray } from '../utils';
|
import { isArray } from '../utils';
|
||||||
import CodeGen from './code-gen';
|
import CodeGen from './code-gen';
|
||||||
|
|
||||||
@@ -12,25 +12,25 @@ function JavaScriptCompiler() {}
|
|||||||
JavaScriptCompiler.prototype = {
|
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 */) {
|
||||||
return this.internalNameLookup(parent, name);
|
return this.internalNameLookup(parent, name);
|
||||||
},
|
},
|
||||||
depthedLookup: function(name) {
|
depthedLookup: function (name) {
|
||||||
return [
|
return [
|
||||||
this.aliasable('container.lookup'),
|
this.aliasable('container.lookup'),
|
||||||
'(depths, ',
|
'(depths, ',
|
||||||
JSON.stringify(name),
|
JSON.stringify(name),
|
||||||
')'
|
')',
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
|
||||||
compilerInfo: function() {
|
compilerInfo: function () {
|
||||||
const revision = COMPILER_REVISION,
|
const revision = COMPILER_REVISION,
|
||||||
versions = REVISION_CHANGES[revision];
|
versions = REVISION_CHANGES[revision];
|
||||||
return [revision, versions];
|
return [revision, versions];
|
||||||
},
|
},
|
||||||
|
|
||||||
appendToBuffer: function(source, location, explicit) {
|
appendToBuffer: function (source, location, explicit) {
|
||||||
// Force a source as this simplifies the merge logic.
|
// Force a source as this simplifies the merge logic.
|
||||||
if (!isArray(source)) {
|
if (!isArray(source)) {
|
||||||
source = [source];
|
source = [source];
|
||||||
@@ -50,22 +50,20 @@ JavaScriptCompiler.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
initializeBuffer: function() {
|
initializeBuffer: function () {
|
||||||
return this.quotedString('');
|
return this.quotedString('');
|
||||||
},
|
},
|
||||||
// END PUBLIC API
|
// END PUBLIC API
|
||||||
internalNameLookup: function(parent, name) {
|
internalNameLookup: function (parent, name) {
|
||||||
this.lookupPropertyFunctionIsUsed = true;
|
this.lookupPropertyFunctionIsUsed = true;
|
||||||
return ['lookupProperty(', parent, ',', JSON.stringify(name), ')'];
|
return ['lookupProperty(', parent, ',', JSON.stringify(name), ')'];
|
||||||
},
|
},
|
||||||
|
|
||||||
lookupPropertyFunctionIsUsed: false,
|
lookupPropertyFunctionIsUsed: false,
|
||||||
|
|
||||||
compile: function(environment, options, context, asObject) {
|
compile: function (environment, options, context, asObject) {
|
||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.stringParams = this.options.stringParams;
|
|
||||||
this.trackIds = this.options.trackIds;
|
|
||||||
this.precompile = !asObject;
|
this.precompile = !asObject;
|
||||||
|
|
||||||
this.name = this.environment.name;
|
this.name = this.environment.name;
|
||||||
@@ -73,7 +71,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
this.context = context || {
|
this.context = context || {
|
||||||
decorators: [],
|
decorators: [],
|
||||||
programs: [],
|
programs: [],
|
||||||
environments: []
|
environments: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
this.preamble();
|
this.preamble();
|
||||||
@@ -125,11 +123,12 @@ JavaScriptCompiler.prototype = {
|
|||||||
this.decorators.prepend([
|
this.decorators.prepend([
|
||||||
'var decorators = container.decorators, ',
|
'var decorators = container.decorators, ',
|
||||||
this.lookupPropertyFunctionVarDeclaration(),
|
this.lookupPropertyFunctionVarDeclaration(),
|
||||||
';\n'
|
';\n',
|
||||||
]);
|
]);
|
||||||
this.decorators.push('return fn;');
|
this.decorators.push('return fn;');
|
||||||
|
|
||||||
if (asObject) {
|
if (asObject) {
|
||||||
|
// eslint-disable-next-line no-new-func
|
||||||
this.decorators = Function.apply(this, [
|
this.decorators = Function.apply(this, [
|
||||||
'fn',
|
'fn',
|
||||||
'props',
|
'props',
|
||||||
@@ -138,7 +137,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
'data',
|
'data',
|
||||||
'blockParams',
|
'blockParams',
|
||||||
'depths',
|
'depths',
|
||||||
this.decorators.merge()
|
this.decorators.merge(),
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
this.decorators.prepend(
|
this.decorators.prepend(
|
||||||
@@ -155,7 +154,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
if (!this.isChild) {
|
if (!this.isChild) {
|
||||||
let ret = {
|
let ret = {
|
||||||
compiler: this.compilerInfo(),
|
compiler: this.compilerInfo(),
|
||||||
main: fn
|
main: fn,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.decorators) {
|
if (this.decorators) {
|
||||||
@@ -212,7 +211,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
preamble: function() {
|
preamble: function () {
|
||||||
// track the last context pushed into place to allow skipping the
|
// track the last context pushed into place to allow skipping the
|
||||||
// getContext opcode when it would be a noop
|
// getContext opcode when it would be a noop
|
||||||
this.lastContext = 0;
|
this.lastContext = 0;
|
||||||
@@ -220,7 +219,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
this.decorators = new CodeGen(this.options.srcName);
|
this.decorators = new CodeGen(this.options.srcName);
|
||||||
},
|
},
|
||||||
|
|
||||||
createFunctionContext: function(asObject) {
|
createFunctionContext: function (asObject) {
|
||||||
let varDeclarations = '';
|
let varDeclarations = '';
|
||||||
|
|
||||||
let locals = this.stackVars.concat(this.registers.list);
|
let locals = this.stackVars.concat(this.registers.list);
|
||||||
@@ -235,7 +234,7 @@ 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;
|
||||||
Object.keys(this.aliases).forEach(alias => {
|
Object.keys(this.aliases).forEach((alias) => {
|
||||||
let node = this.aliases[alias];
|
let node = this.aliases[alias];
|
||||||
if (node.children && node.referenceCount > 1) {
|
if (node.children && node.referenceCount > 1) {
|
||||||
varDeclarations += ', alias' + ++aliasCount + '=' + alias;
|
varDeclarations += ', alias' + ++aliasCount + '=' + alias;
|
||||||
@@ -262,25 +261,25 @@ JavaScriptCompiler.prototype = {
|
|||||||
if (asObject) {
|
if (asObject) {
|
||||||
params.push(source);
|
params.push(source);
|
||||||
|
|
||||||
return Function.apply(this, params);
|
return Function.apply(this, params); // eslint-disable-line no-new-func
|
||||||
} else {
|
} else {
|
||||||
return this.source.wrap([
|
return this.source.wrap([
|
||||||
'function(',
|
'function(',
|
||||||
params.join(','),
|
params.join(','),
|
||||||
') {\n ',
|
') {\n ',
|
||||||
source,
|
source,
|
||||||
'}'
|
'}',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mergeSource: function(varDeclarations) {
|
mergeSource: function (varDeclarations) {
|
||||||
let isSimple = this.environment.isSimple,
|
let isSimple = this.environment.isSimple,
|
||||||
appendOnly = !this.forceBuffer,
|
appendOnly = !this.forceBuffer,
|
||||||
appendFirst,
|
appendFirst,
|
||||||
sourceSeen,
|
sourceSeen,
|
||||||
bufferStart,
|
bufferStart,
|
||||||
bufferEnd;
|
bufferEnd;
|
||||||
this.source.each(line => {
|
this.source.each((line) => {
|
||||||
if (line.appendToBuffer) {
|
if (line.appendToBuffer) {
|
||||||
if (bufferStart) {
|
if (bufferStart) {
|
||||||
line.prepend(' + ');
|
line.prepend(' + ');
|
||||||
@@ -334,7 +333,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
return this.source.merge();
|
return this.source.merge();
|
||||||
},
|
},
|
||||||
|
|
||||||
lookupPropertyFunctionVarDeclaration: function() {
|
lookupPropertyFunctionVarDeclaration: function () {
|
||||||
return `
|
return `
|
||||||
lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||||
@@ -354,7 +353,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
// `{{#this.foo}}...{{/this.foo}}`, resolve the value of `foo`, and
|
// `{{#this.foo}}...{{/this.foo}}`, resolve the value of `foo`, and
|
||||||
// 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(
|
let blockHelperMissing = this.aliasable(
|
||||||
'container.hooks.blockHelperMissing'
|
'container.hooks.blockHelperMissing'
|
||||||
),
|
),
|
||||||
@@ -373,7 +372,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
// Compiler value, before: lastHelper=value of last found helper, if any
|
// Compiler value, before: lastHelper=value of last found helper, if any
|
||||||
// On stack, after, if no lastHelper: same as [blockValue]
|
// On stack, after, if no lastHelper: same as [blockValue]
|
||||||
// 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(
|
let blockHelperMissing = this.aliasable(
|
||||||
'container.hooks.blockHelperMissing'
|
'container.hooks.blockHelperMissing'
|
||||||
@@ -393,7 +392,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
current,
|
current,
|
||||||
' = ',
|
' = ',
|
||||||
this.source.functionCall(blockHelperMissing, 'call', params),
|
this.source.functionCall(blockHelperMissing, 'call', params),
|
||||||
'}'
|
'}',
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -403,7 +402,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
// On stack, after: ...
|
// On stack, after: ...
|
||||||
//
|
//
|
||||||
// Appends the string value of `content` to the current buffer
|
// Appends the string value of `content` to the current buffer
|
||||||
appendContent: function(content) {
|
appendContent: function (content) {
|
||||||
if (this.pendingContent) {
|
if (this.pendingContent) {
|
||||||
content = this.pendingContent + content;
|
content = this.pendingContent + content;
|
||||||
} else {
|
} else {
|
||||||
@@ -422,9 +421,9 @@ JavaScriptCompiler.prototype = {
|
|||||||
//
|
//
|
||||||
// If `value` is truthy, or 0, it is coerced into a string and appended
|
// If `value` is truthy, or 0, it is coerced into a string and appended
|
||||||
// Otherwise, the empty string is appended
|
// Otherwise, the empty string is appended
|
||||||
append: function() {
|
append: function () {
|
||||||
if (this.isInline()) {
|
if (this.isInline()) {
|
||||||
this.replaceStack(current => [' != null ? ', current, ' : ""']);
|
this.replaceStack((current) => [' != null ? ', current, ' : ""']);
|
||||||
|
|
||||||
this.pushSource(this.appendToBuffer(this.popStack()));
|
this.pushSource(this.appendToBuffer(this.popStack()));
|
||||||
} else {
|
} else {
|
||||||
@@ -434,13 +433,13 @@ JavaScriptCompiler.prototype = {
|
|||||||
local,
|
local,
|
||||||
' != null) { ',
|
' != null) { ',
|
||||||
this.appendToBuffer(local, undefined, true),
|
this.appendToBuffer(local, undefined, true),
|
||||||
' }'
|
' }',
|
||||||
]);
|
]);
|
||||||
if (this.environment.isSimple) {
|
if (this.environment.isSimple) {
|
||||||
this.pushSource([
|
this.pushSource([
|
||||||
'else { ',
|
'else { ',
|
||||||
this.appendToBuffer("''", undefined, true),
|
this.appendToBuffer("''", undefined, true),
|
||||||
' }'
|
' }',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -452,13 +451,13 @@ JavaScriptCompiler.prototype = {
|
|||||||
// On stack, after: ...
|
// On stack, after: ...
|
||||||
//
|
//
|
||||||
// Escape `value` and append it to the buffer
|
// Escape `value` and append it to the buffer
|
||||||
appendEscaped: function() {
|
appendEscaped: function () {
|
||||||
this.pushSource(
|
this.pushSource(
|
||||||
this.appendToBuffer([
|
this.appendToBuffer([
|
||||||
this.aliasable('container.escapeExpression'),
|
this.aliasable('container.escapeExpression'),
|
||||||
'(',
|
'(',
|
||||||
this.popStack(),
|
this.popStack(),
|
||||||
')'
|
')',
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -470,7 +469,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
// Compiler value, after: lastContext=depth
|
// Compiler value, after: lastContext=depth
|
||||||
//
|
//
|
||||||
// Set the value of the `lastContext` compiler value to the depth
|
// Set the value of the `lastContext` compiler value to the depth
|
||||||
getContext: function(depth) {
|
getContext: function (depth) {
|
||||||
this.lastContext = depth;
|
this.lastContext = depth;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -480,7 +479,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
// On stack, after: currentContext, ...
|
// On stack, after: currentContext, ...
|
||||||
//
|
//
|
||||||
// Pushes the value of the current context onto the stack.
|
// Pushes the value of the current context onto the stack.
|
||||||
pushContext: function() {
|
pushContext: function () {
|
||||||
this.pushStackLiteral(this.contextName(this.lastContext));
|
this.pushStackLiteral(this.contextName(this.lastContext));
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -491,7 +490,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
//
|
//
|
||||||
// Looks up the value of `name` on the current context and pushes
|
// Looks up the value of `name` on the current context and pushes
|
||||||
// it onto the stack.
|
// it onto the stack.
|
||||||
lookupOnContext: function(parts, falsy, strict, scoped) {
|
lookupOnContext: function (parts, falsy, strict, scoped) {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|
||||||
if (!scoped && this.options.compat && !this.lastContext) {
|
if (!scoped && this.options.compat && !this.lastContext) {
|
||||||
@@ -512,7 +511,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
//
|
//
|
||||||
// Looks up the value of `parts` on the given block param and pushes
|
// Looks up the value of `parts` on the given block param and pushes
|
||||||
// it onto the stack.
|
// it onto the stack.
|
||||||
lookupBlockParam: function(blockParamId, parts) {
|
lookupBlockParam: function (blockParamId, parts) {
|
||||||
this.useBlockParams = true;
|
this.useBlockParams = true;
|
||||||
|
|
||||||
this.push(['blockParams[', blockParamId[0], '][', blockParamId[1], ']']);
|
this.push(['blockParams[', blockParamId[0], '][', blockParamId[1], ']']);
|
||||||
@@ -525,7 +524,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
// On stack, after: data, ...
|
// On stack, after: data, ...
|
||||||
//
|
//
|
||||||
// Push the data lookup operator
|
// Push the data lookup operator
|
||||||
lookupData: function(depth, parts, strict) {
|
lookupData: function (depth, parts, strict) {
|
||||||
if (!depth) {
|
if (!depth) {
|
||||||
this.pushStackLiteral('data');
|
this.pushStackLiteral('data');
|
||||||
} else {
|
} else {
|
||||||
@@ -535,16 +534,24 @@ JavaScriptCompiler.prototype = {
|
|||||||
this.resolvePath('data', parts, 0, true, strict);
|
this.resolvePath('data', parts, 0, true, strict);
|
||||||
},
|
},
|
||||||
|
|
||||||
resolvePath: function(type, parts, i, falsy, strict) {
|
resolvePath: function (type, parts, startPartIndex, falsy, strict) {
|
||||||
if (this.options.strict || this.options.assumeObjects) {
|
if (this.options.strict || this.options.assumeObjects) {
|
||||||
this.push(strictLookup(this.options.strict && strict, this, parts, type));
|
this.push(
|
||||||
|
strictLookup(
|
||||||
|
this.options.strict && strict,
|
||||||
|
this,
|
||||||
|
parts,
|
||||||
|
startPartIndex,
|
||||||
|
type
|
||||||
|
)
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let len = parts.length;
|
let len = parts.length;
|
||||||
for (; i < len; i++) {
|
for (let i = startPartIndex; i < len; i++) {
|
||||||
/* eslint-disable no-loop-func */
|
/* eslint-disable no-loop-func */
|
||||||
this.replaceStack(current => {
|
this.replaceStack((current) => {
|
||||||
let lookup = this.nameLookup(current, parts[i], type);
|
let lookup = this.nameLookup(current, parts[i], type);
|
||||||
// We want to ensure that zero and false are handled properly if the context (falsy flag)
|
// We want to ensure that zero and false are handled properly if the context (falsy flag)
|
||||||
// needs to have the special handling for these values.
|
// needs to have the special handling for these values.
|
||||||
@@ -566,68 +573,30 @@ JavaScriptCompiler.prototype = {
|
|||||||
//
|
//
|
||||||
// If the `value` is a lambda, replace it on the stack by
|
// If the `value` is a lambda, replace it on the stack by
|
||||||
// the return value of the lambda
|
// the return value of the lambda
|
||||||
resolvePossibleLambda: function() {
|
resolvePossibleLambda: function () {
|
||||||
this.push([
|
this.push([
|
||||||
this.aliasable('container.lambda'),
|
this.aliasable('container.lambda'),
|
||||||
'(',
|
'(',
|
||||||
this.popStack(),
|
this.popStack(),
|
||||||
', ',
|
', ',
|
||||||
this.contextName(0),
|
this.contextName(0),
|
||||||
')'
|
')',
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
|
||||||
// [pushStringParam]
|
emptyHash: function (omitEmpty) {
|
||||||
//
|
|
||||||
// On stack, before: ...
|
|
||||||
// On stack, after: string, currentContext, ...
|
|
||||||
//
|
|
||||||
// This opcode is designed for use in string mode, which
|
|
||||||
// provides the string value of a parameter along with its
|
|
||||||
// depth rather than resolving it immediately.
|
|
||||||
pushStringParam: function(string, type) {
|
|
||||||
this.pushContext();
|
|
||||||
this.pushString(type);
|
|
||||||
|
|
||||||
// If it's a subexpression, the string result
|
|
||||||
// will be pushed after this opcode.
|
|
||||||
if (type !== 'SubExpression') {
|
|
||||||
if (typeof string === 'string') {
|
|
||||||
this.pushString(string);
|
|
||||||
} else {
|
|
||||||
this.pushStackLiteral(string);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
emptyHash: function(omitEmpty) {
|
|
||||||
if (this.trackIds) {
|
|
||||||
this.push('{}'); // hashIds
|
|
||||||
}
|
|
||||||
if (this.stringParams) {
|
|
||||||
this.push('{}'); // hashContexts
|
|
||||||
this.push('{}'); // hashTypes
|
|
||||||
}
|
|
||||||
this.pushStackLiteral(omitEmpty ? 'undefined' : '{}');
|
this.pushStackLiteral(omitEmpty ? 'undefined' : '{}');
|
||||||
},
|
},
|
||||||
pushHash: function() {
|
pushHash: function () {
|
||||||
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: {} };
|
||||||
},
|
},
|
||||||
popHash: function() {
|
popHash: function () {
|
||||||
let hash = this.hash;
|
let hash = this.hash;
|
||||||
this.hash = this.hashes.pop();
|
this.hash = this.hashes.pop();
|
||||||
|
|
||||||
if (this.trackIds) {
|
|
||||||
this.push(this.objectLiteral(hash.ids));
|
|
||||||
}
|
|
||||||
if (this.stringParams) {
|
|
||||||
this.push(this.objectLiteral(hash.contexts));
|
|
||||||
this.push(this.objectLiteral(hash.types));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.push(this.objectLiteral(hash.values));
|
this.push(this.objectLiteral(hash.values));
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -637,7 +606,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
// On stack, after: quotedString(string), ...
|
// On stack, after: quotedString(string), ...
|
||||||
//
|
//
|
||||||
// Push a quoted version of `string` onto the stack
|
// Push a quoted version of `string` onto the stack
|
||||||
pushString: function(string) {
|
pushString: function (string) {
|
||||||
this.pushStackLiteral(this.quotedString(string));
|
this.pushStackLiteral(this.quotedString(string));
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -649,7 +618,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
// Pushes a value onto the stack. This operation prevents
|
// Pushes a value onto the stack. This operation prevents
|
||||||
// the compiler from creating a temporary variable to hold
|
// the compiler from creating a temporary variable to hold
|
||||||
// it.
|
// it.
|
||||||
pushLiteral: function(value) {
|
pushLiteral: function (value) {
|
||||||
this.pushStackLiteral(value);
|
this.pushStackLiteral(value);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -661,7 +630,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
// Push a program expression onto the stack. This takes
|
// Push a program expression onto the stack. This takes
|
||||||
// a compile-time guid and converts it into a runtime-accessible
|
// a compile-time guid and converts it into a runtime-accessible
|
||||||
// expression.
|
// expression.
|
||||||
pushProgram: function(guid) {
|
pushProgram: function (guid) {
|
||||||
if (guid != null) {
|
if (guid != null) {
|
||||||
this.pushStackLiteral(this.programExpression(guid));
|
this.pushStackLiteral(this.programExpression(guid));
|
||||||
} else {
|
} else {
|
||||||
@@ -686,9 +655,9 @@ JavaScriptCompiler.prototype = {
|
|||||||
'fn',
|
'fn',
|
||||||
'props',
|
'props',
|
||||||
'container',
|
'container',
|
||||||
options
|
options,
|
||||||
]),
|
]),
|
||||||
' || fn;'
|
' || fn;',
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -701,7 +670,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
// and pushes the helper's return value onto the stack.
|
// and pushes the helper's return value onto the stack.
|
||||||
//
|
//
|
||||||
// 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);
|
||||||
|
|
||||||
@@ -722,7 +691,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
let functionLookupCode = [
|
let functionLookupCode = [
|
||||||
'(',
|
'(',
|
||||||
this.itemsSeparatedBy(possibleFunctionCalls, '||'),
|
this.itemsSeparatedBy(possibleFunctionCalls, '||'),
|
||||||
')'
|
')',
|
||||||
];
|
];
|
||||||
let functionCall = this.source.functionCall(
|
let functionCall = this.source.functionCall(
|
||||||
functionLookupCode,
|
functionLookupCode,
|
||||||
@@ -732,7 +701,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
this.push(functionCall);
|
this.push(functionCall);
|
||||||
},
|
},
|
||||||
|
|
||||||
itemsSeparatedBy: function(items, separator) {
|
itemsSeparatedBy: function (items, separator) {
|
||||||
let result = [];
|
let result = [];
|
||||||
result.push(items[0]);
|
result.push(items[0]);
|
||||||
for (let i = 1; i < items.length; i++) {
|
for (let i = 1; i < items.length; i++) {
|
||||||
@@ -747,7 +716,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
//
|
//
|
||||||
// This operation is used when the helper is known to exist,
|
// This operation is used when the helper is known to exist,
|
||||||
// so a `helperMissing` fallback is not required.
|
// so a `helperMissing` fallback is not required.
|
||||||
invokeKnownHelper: function(paramSize, name) {
|
invokeKnownHelper: function (paramSize, name) {
|
||||||
let helper = this.setupHelper(paramSize, name);
|
let helper = this.setupHelper(paramSize, name);
|
||||||
this.push(this.source.functionCall(helper.name, 'call', helper.callParams));
|
this.push(this.source.functionCall(helper.name, 'call', helper.callParams));
|
||||||
},
|
},
|
||||||
@@ -764,7 +733,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
// This operation emits more code than the other options,
|
// This operation emits more code than the other options,
|
||||||
// and can be avoided by passing the `knownHelpers` and
|
// and can be avoided by passing the `knownHelpers` and
|
||||||
// `knownHelpersOnly` flags at compile-time.
|
// `knownHelpersOnly` flags at compile-time.
|
||||||
invokeAmbiguous: function(name, helperCall) {
|
invokeAmbiguous: function (name, helperCall) {
|
||||||
this.useRegister('helper');
|
this.useRegister('helper');
|
||||||
|
|
||||||
let nonHelper = this.popStack();
|
let nonHelper = this.popStack();
|
||||||
@@ -796,7 +765,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
this.aliasable('"function"'),
|
this.aliasable('"function"'),
|
||||||
' ? ',
|
' ? ',
|
||||||
this.source.functionCall('helper', 'call', helper.callParams),
|
this.source.functionCall('helper', 'call', helper.callParams),
|
||||||
' : helper))'
|
' : helper))',
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -807,7 +776,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
//
|
//
|
||||||
// This operation pops off a context, invokes a partial with that context,
|
// This operation pops off a context, invokes a partial with that context,
|
||||||
// and pushes the result of the invocation back.
|
// and pushes the result of the invocation back.
|
||||||
invokePartial: function(isDynamic, name, indent) {
|
invokePartial: function (isDynamic, name, indent) {
|
||||||
let params = [],
|
let params = [],
|
||||||
options = this.setupParams(name, 1, params);
|
options = this.setupParams(name, 1, params);
|
||||||
|
|
||||||
@@ -844,57 +813,15 @@ JavaScriptCompiler.prototype = {
|
|||||||
// On stack, after: ..., hash, ...
|
// On stack, after: ..., hash, ...
|
||||||
//
|
//
|
||||||
// Pops a value off the stack and assigns it to the current hash
|
// Pops a value off the stack and assigns it to the current hash
|
||||||
assignToHash: function(key) {
|
assignToHash: function (key) {
|
||||||
let value = this.popStack(),
|
this.hash.values[key] = this.popStack();
|
||||||
context,
|
|
||||||
type,
|
|
||||||
id;
|
|
||||||
|
|
||||||
if (this.trackIds) {
|
|
||||||
id = this.popStack();
|
|
||||||
}
|
|
||||||
if (this.stringParams) {
|
|
||||||
type = this.popStack();
|
|
||||||
context = this.popStack();
|
|
||||||
}
|
|
||||||
|
|
||||||
let hash = this.hash;
|
|
||||||
if (context) {
|
|
||||||
hash.contexts[key] = context;
|
|
||||||
}
|
|
||||||
if (type) {
|
|
||||||
hash.types[key] = type;
|
|
||||||
}
|
|
||||||
if (id) {
|
|
||||||
hash.ids[key] = id;
|
|
||||||
}
|
|
||||||
hash.values[key] = value;
|
|
||||||
},
|
|
||||||
|
|
||||||
pushId: function(type, name, child) {
|
|
||||||
if (type === 'BlockParam') {
|
|
||||||
this.pushStackLiteral(
|
|
||||||
'blockParams[' +
|
|
||||||
name[0] +
|
|
||||||
'].path[' +
|
|
||||||
name[1] +
|
|
||||||
']' +
|
|
||||||
(child ? ' + ' + JSON.stringify('.' + child) : '')
|
|
||||||
);
|
|
||||||
} else if (type === 'PathExpression') {
|
|
||||||
this.pushString(name);
|
|
||||||
} else if (type === 'SubExpression') {
|
|
||||||
this.pushStackLiteral('true');
|
|
||||||
} else {
|
|
||||||
this.pushStackLiteral('null');
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// HELPERS
|
// HELPERS
|
||||||
|
|
||||||
compiler: JavaScriptCompiler,
|
compiler: JavaScriptCompiler,
|
||||||
|
|
||||||
compileChildren: function(environment, options) {
|
compileChildren: function (environment, options) {
|
||||||
let children = environment.children,
|
let children = environment.children,
|
||||||
child,
|
child,
|
||||||
compiler;
|
compiler;
|
||||||
@@ -932,7 +859,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
matchExistingProgram: function(child) {
|
matchExistingProgram: function (child) {
|
||||||
for (let i = 0, len = this.context.environments.length; i < len; i++) {
|
for (let i = 0, len = this.context.environments.length; i < len; i++) {
|
||||||
let environment = this.context.environments[i];
|
let environment = this.context.environments[i];
|
||||||
if (environment && environment.equals(child)) {
|
if (environment && environment.equals(child)) {
|
||||||
@@ -941,7 +868,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
programExpression: function(guid) {
|
programExpression: function (guid) {
|
||||||
let child = this.environment.children[guid],
|
let child = this.environment.children[guid],
|
||||||
programParams = [child.index, 'data', child.blockParams];
|
programParams = [child.index, 'data', child.blockParams];
|
||||||
|
|
||||||
@@ -955,14 +882,14 @@ JavaScriptCompiler.prototype = {
|
|||||||
return 'container.program(' + programParams.join(', ') + ')';
|
return 'container.program(' + programParams.join(', ') + ')';
|
||||||
},
|
},
|
||||||
|
|
||||||
useRegister: function(name) {
|
useRegister: function (name) {
|
||||||
if (!this.registers[name]) {
|
if (!this.registers[name]) {
|
||||||
this.registers[name] = true;
|
this.registers[name] = true;
|
||||||
this.registers.list.push(name);
|
this.registers.list.push(name);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
push: function(expr) {
|
push: function (expr) {
|
||||||
if (!(expr instanceof Literal)) {
|
if (!(expr instanceof Literal)) {
|
||||||
expr = this.source.wrap(expr);
|
expr = this.source.wrap(expr);
|
||||||
}
|
}
|
||||||
@@ -971,11 +898,11 @@ JavaScriptCompiler.prototype = {
|
|||||||
return expr;
|
return expr;
|
||||||
},
|
},
|
||||||
|
|
||||||
pushStackLiteral: function(item) {
|
pushStackLiteral: function (item) {
|
||||||
this.push(new Literal(item));
|
this.push(new Literal(item));
|
||||||
},
|
},
|
||||||
|
|
||||||
pushSource: function(source) {
|
pushSource: function (source) {
|
||||||
if (this.pendingContent) {
|
if (this.pendingContent) {
|
||||||
this.source.push(
|
this.source.push(
|
||||||
this.appendToBuffer(
|
this.appendToBuffer(
|
||||||
@@ -991,7 +918,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
replaceStack: function(callback) {
|
replaceStack: function (callback) {
|
||||||
let prefix = ['('],
|
let prefix = ['('],
|
||||||
stack,
|
stack,
|
||||||
createdStack,
|
createdStack,
|
||||||
@@ -1030,17 +957,17 @@ JavaScriptCompiler.prototype = {
|
|||||||
this.push(prefix.concat(item, ')'));
|
this.push(prefix.concat(item, ')'));
|
||||||
},
|
},
|
||||||
|
|
||||||
incrStack: function() {
|
incrStack: function () {
|
||||||
this.stackSlot++;
|
this.stackSlot++;
|
||||||
if (this.stackSlot > this.stackVars.length) {
|
if (this.stackSlot > this.stackVars.length) {
|
||||||
this.stackVars.push('stack' + this.stackSlot);
|
this.stackVars.push('stack' + this.stackSlot);
|
||||||
}
|
}
|
||||||
return this.topStackName();
|
return this.topStackName();
|
||||||
},
|
},
|
||||||
topStackName: function() {
|
topStackName: function () {
|
||||||
return 'stack' + this.stackSlot;
|
return 'stack' + this.stackSlot;
|
||||||
},
|
},
|
||||||
flushInline: function() {
|
flushInline: function () {
|
||||||
let inlineStack = this.inlineStack;
|
let inlineStack = this.inlineStack;
|
||||||
this.inlineStack = [];
|
this.inlineStack = [];
|
||||||
for (let i = 0, len = inlineStack.length; i < len; i++) {
|
for (let i = 0, len = inlineStack.length; i < len; i++) {
|
||||||
@@ -1055,11 +982,11 @@ JavaScriptCompiler.prototype = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isInline: function() {
|
isInline: function () {
|
||||||
return this.inlineStack.length;
|
return this.inlineStack.length;
|
||||||
},
|
},
|
||||||
|
|
||||||
popStack: function(wrapped) {
|
popStack: function (wrapped) {
|
||||||
let inline = this.isInline(),
|
let inline = this.isInline(),
|
||||||
item = (inline ? this.inlineStack : this.compileStack).pop();
|
item = (inline ? this.inlineStack : this.compileStack).pop();
|
||||||
|
|
||||||
@@ -1077,7 +1004,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
topStack: function() {
|
topStack: function () {
|
||||||
let stack = this.isInline() ? this.inlineStack : this.compileStack,
|
let stack = this.isInline() ? this.inlineStack : this.compileStack,
|
||||||
item = stack[stack.length - 1];
|
item = stack[stack.length - 1];
|
||||||
|
|
||||||
@@ -1089,7 +1016,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
contextName: function(context) {
|
contextName: function (context) {
|
||||||
if (this.useDepths && context) {
|
if (this.useDepths && context) {
|
||||||
return 'depths[' + context + ']';
|
return 'depths[' + context + ']';
|
||||||
} else {
|
} else {
|
||||||
@@ -1097,15 +1024,15 @@ JavaScriptCompiler.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
quotedString: function(str) {
|
quotedString: function (str) {
|
||||||
return this.source.quotedString(str);
|
return this.source.quotedString(str);
|
||||||
},
|
},
|
||||||
|
|
||||||
objectLiteral: function(obj) {
|
objectLiteral: function (obj) {
|
||||||
return this.source.objectLiteral(obj);
|
return this.source.objectLiteral(obj);
|
||||||
},
|
},
|
||||||
|
|
||||||
aliasable: function(name) {
|
aliasable: function (name) {
|
||||||
let ret = this.aliases[name];
|
let ret = this.aliases[name];
|
||||||
if (ret) {
|
if (ret) {
|
||||||
ret.referenceCount++;
|
ret.referenceCount++;
|
||||||
@@ -1119,7 +1046,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
|
|
||||||
setupHelper: function(paramSize, name, blockHelper) {
|
setupHelper: function (paramSize, name, blockHelper) {
|
||||||
let params = [],
|
let params = [],
|
||||||
paramsInit = this.setupHelperArgs(name, paramSize, params, blockHelper);
|
paramsInit = this.setupHelperArgs(name, paramSize, params, blockHelper);
|
||||||
let foundHelper = this.nameLookup('helpers', name, 'helper'),
|
let foundHelper = this.nameLookup('helpers', name, 'helper'),
|
||||||
@@ -1133,15 +1060,12 @@ JavaScriptCompiler.prototype = {
|
|||||||
params: params,
|
params: params,
|
||||||
paramsInit: paramsInit,
|
paramsInit: paramsInit,
|
||||||
name: foundHelper,
|
name: foundHelper,
|
||||||
callParams: [callContext].concat(params)
|
callParams: [callContext].concat(params),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
setupParams: function(helper, paramSize, params) {
|
setupParams: function (helper, paramSize, params) {
|
||||||
let options = {},
|
let options = {},
|
||||||
contexts = [],
|
|
||||||
types = [],
|
|
||||||
ids = [],
|
|
||||||
objectArgs = !params,
|
objectArgs = !params,
|
||||||
param;
|
param;
|
||||||
|
|
||||||
@@ -1152,14 +1076,6 @@ JavaScriptCompiler.prototype = {
|
|||||||
options.name = this.quotedString(helper);
|
options.name = this.quotedString(helper);
|
||||||
options.hash = this.popStack();
|
options.hash = this.popStack();
|
||||||
|
|
||||||
if (this.trackIds) {
|
|
||||||
options.hashIds = this.popStack();
|
|
||||||
}
|
|
||||||
if (this.stringParams) {
|
|
||||||
options.hashTypes = this.popStack();
|
|
||||||
options.hashContexts = this.popStack();
|
|
||||||
}
|
|
||||||
|
|
||||||
let inverse = this.popStack(),
|
let inverse = this.popStack(),
|
||||||
program = this.popStack();
|
program = this.popStack();
|
||||||
|
|
||||||
@@ -1176,28 +1092,12 @@ JavaScriptCompiler.prototype = {
|
|||||||
while (i--) {
|
while (i--) {
|
||||||
param = this.popStack();
|
param = this.popStack();
|
||||||
params[i] = param;
|
params[i] = param;
|
||||||
|
|
||||||
if (this.trackIds) {
|
|
||||||
ids[i] = this.popStack();
|
|
||||||
}
|
|
||||||
if (this.stringParams) {
|
|
||||||
types[i] = this.popStack();
|
|
||||||
contexts[i] = this.popStack();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (objectArgs) {
|
if (objectArgs) {
|
||||||
options.args = this.source.generateArray(params);
|
options.args = this.source.generateArray(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.trackIds) {
|
|
||||||
options.ids = this.source.generateArray(ids);
|
|
||||||
}
|
|
||||||
if (this.stringParams) {
|
|
||||||
options.types = this.source.generateArray(types);
|
|
||||||
options.contexts = this.source.generateArray(contexts);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.options.data) {
|
if (this.options.data) {
|
||||||
options.data = 'data';
|
options.data = 'data';
|
||||||
}
|
}
|
||||||
@@ -1207,7 +1107,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
return options;
|
return options;
|
||||||
},
|
},
|
||||||
|
|
||||||
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.loc = JSON.stringify(this.source.currentLocation);
|
||||||
options = this.objectLiteral(options);
|
options = this.objectLiteral(options);
|
||||||
@@ -1221,10 +1121,10 @@ JavaScriptCompiler.prototype = {
|
|||||||
} else {
|
} else {
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
(function() {
|
(function () {
|
||||||
const reservedWords = (
|
const reservedWords = (
|
||||||
'break else new var' +
|
'break else new var' +
|
||||||
' case finally return void' +
|
' case finally return void' +
|
||||||
@@ -1254,22 +1154,21 @@ JavaScriptCompiler.prototype = {
|
|||||||
/**
|
/**
|
||||||
* @deprecated May be removed in the next major version
|
* @deprecated May be removed in the next major version
|
||||||
*/
|
*/
|
||||||
JavaScriptCompiler.isValidJavaScriptVariableName = function(name) {
|
JavaScriptCompiler.isValidJavaScriptVariableName = function (name) {
|
||||||
return (
|
return (
|
||||||
!JavaScriptCompiler.RESERVED_WORDS[name] &&
|
!JavaScriptCompiler.RESERVED_WORDS[name] &&
|
||||||
/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(name)
|
/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(name)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function strictLookup(requireTerminal, compiler, parts, type) {
|
function strictLookup(requireTerminal, compiler, parts, startPartIndex, type) {
|
||||||
let stack = compiler.popStack(),
|
let stack = compiler.popStack(),
|
||||||
i = 0,
|
|
||||||
len = parts.length;
|
len = parts.length;
|
||||||
if (requireTerminal) {
|
if (requireTerminal) {
|
||||||
len--;
|
len--;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (; i < len; i++) {
|
for (let i = startPartIndex; i < len; i++) {
|
||||||
stack = compiler.nameLookup(stack, parts[i], type);
|
stack = compiler.nameLookup(stack, parts[i], type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1279,10 +1178,10 @@ function strictLookup(requireTerminal, compiler, parts, type) {
|
|||||||
'(',
|
'(',
|
||||||
stack,
|
stack,
|
||||||
', ',
|
', ',
|
||||||
compiler.quotedString(parts[i]),
|
compiler.quotedString(parts[len]),
|
||||||
', ',
|
', ',
|
||||||
JSON.stringify(compiler.source.currentLocation),
|
JSON.stringify(compiler.source.currentLocation),
|
||||||
' )'
|
' )',
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
return stack;
|
return stack;
|
||||||
|
|||||||
@@ -1,178 +0,0 @@
|
|||||||
/* eslint-disable new-cap */
|
|
||||||
import Visitor from './visitor';
|
|
||||||
|
|
||||||
export function print(ast) {
|
|
||||||
return new PrintVisitor().accept(ast);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function PrintVisitor() {
|
|
||||||
this.padding = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
PrintVisitor.prototype = new Visitor();
|
|
||||||
|
|
||||||
PrintVisitor.prototype.pad = function(string) {
|
|
||||||
let out = '';
|
|
||||||
|
|
||||||
for (let i = 0, l = this.padding; i < l; i++) {
|
|
||||||
out += ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
out += string + '\n';
|
|
||||||
return out;
|
|
||||||
};
|
|
||||||
|
|
||||||
PrintVisitor.prototype.Program = function(program) {
|
|
||||||
let out = '',
|
|
||||||
body = program.body,
|
|
||||||
i,
|
|
||||||
l;
|
|
||||||
|
|
||||||
if (program.blockParams) {
|
|
||||||
let blockParams = 'BLOCK PARAMS: [';
|
|
||||||
for (i = 0, l = program.blockParams.length; i < l; i++) {
|
|
||||||
blockParams += ' ' + program.blockParams[i];
|
|
||||||
}
|
|
||||||
blockParams += ' ]';
|
|
||||||
out += this.pad(blockParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0, l = body.length; i < l; i++) {
|
|
||||||
out += this.accept(body[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.padding--;
|
|
||||||
|
|
||||||
return out;
|
|
||||||
};
|
|
||||||
|
|
||||||
PrintVisitor.prototype.MustacheStatement = function(mustache) {
|
|
||||||
return this.pad('{{ ' + this.SubExpression(mustache) + ' }}');
|
|
||||||
};
|
|
||||||
PrintVisitor.prototype.Decorator = function(mustache) {
|
|
||||||
return this.pad('{{ DIRECTIVE ' + this.SubExpression(mustache) + ' }}');
|
|
||||||
};
|
|
||||||
|
|
||||||
PrintVisitor.prototype.BlockStatement = PrintVisitor.prototype.DecoratorBlock = function(
|
|
||||||
block
|
|
||||||
) {
|
|
||||||
let out = '';
|
|
||||||
|
|
||||||
out += this.pad(
|
|
||||||
(block.type === 'DecoratorBlock' ? 'DIRECTIVE ' : '') + 'BLOCK:'
|
|
||||||
);
|
|
||||||
this.padding++;
|
|
||||||
out += this.pad(this.SubExpression(block));
|
|
||||||
if (block.program) {
|
|
||||||
out += this.pad('PROGRAM:');
|
|
||||||
this.padding++;
|
|
||||||
out += this.accept(block.program);
|
|
||||||
this.padding--;
|
|
||||||
}
|
|
||||||
if (block.inverse) {
|
|
||||||
if (block.program) {
|
|
||||||
this.padding++;
|
|
||||||
}
|
|
||||||
out += this.pad('{{^}}');
|
|
||||||
this.padding++;
|
|
||||||
out += this.accept(block.inverse);
|
|
||||||
this.padding--;
|
|
||||||
if (block.program) {
|
|
||||||
this.padding--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.padding--;
|
|
||||||
|
|
||||||
return out;
|
|
||||||
};
|
|
||||||
|
|
||||||
PrintVisitor.prototype.PartialStatement = function(partial) {
|
|
||||||
let content = 'PARTIAL:' + partial.name.original;
|
|
||||||
if (partial.params[0]) {
|
|
||||||
content += ' ' + this.accept(partial.params[0]);
|
|
||||||
}
|
|
||||||
if (partial.hash) {
|
|
||||||
content += ' ' + this.accept(partial.hash);
|
|
||||||
}
|
|
||||||
return this.pad('{{> ' + content + ' }}');
|
|
||||||
};
|
|
||||||
PrintVisitor.prototype.PartialBlockStatement = function(partial) {
|
|
||||||
let content = 'PARTIAL BLOCK:' + partial.name.original;
|
|
||||||
if (partial.params[0]) {
|
|
||||||
content += ' ' + this.accept(partial.params[0]);
|
|
||||||
}
|
|
||||||
if (partial.hash) {
|
|
||||||
content += ' ' + this.accept(partial.hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
content += ' ' + this.pad('PROGRAM:');
|
|
||||||
this.padding++;
|
|
||||||
content += this.accept(partial.program);
|
|
||||||
this.padding--;
|
|
||||||
|
|
||||||
return this.pad('{{> ' + content + ' }}');
|
|
||||||
};
|
|
||||||
|
|
||||||
PrintVisitor.prototype.ContentStatement = function(content) {
|
|
||||||
return this.pad("CONTENT[ '" + content.value + "' ]");
|
|
||||||
};
|
|
||||||
|
|
||||||
PrintVisitor.prototype.CommentStatement = function(comment) {
|
|
||||||
return this.pad("{{! '" + comment.value + "' }}");
|
|
||||||
};
|
|
||||||
|
|
||||||
PrintVisitor.prototype.SubExpression = function(sexpr) {
|
|
||||||
let params = sexpr.params,
|
|
||||||
paramStrings = [],
|
|
||||||
hash;
|
|
||||||
|
|
||||||
for (let i = 0, l = params.length; i < l; i++) {
|
|
||||||
paramStrings.push(this.accept(params[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
params = '[' + paramStrings.join(', ') + ']';
|
|
||||||
|
|
||||||
hash = sexpr.hash ? ' ' + this.accept(sexpr.hash) : '';
|
|
||||||
|
|
||||||
return this.accept(sexpr.path) + ' ' + params + hash;
|
|
||||||
};
|
|
||||||
|
|
||||||
PrintVisitor.prototype.PathExpression = function(id) {
|
|
||||||
let path = id.parts.join('/');
|
|
||||||
return (id.data ? '@' : '') + 'PATH:' + path;
|
|
||||||
};
|
|
||||||
|
|
||||||
PrintVisitor.prototype.StringLiteral = function(string) {
|
|
||||||
return '"' + string.value + '"';
|
|
||||||
};
|
|
||||||
|
|
||||||
PrintVisitor.prototype.NumberLiteral = function(number) {
|
|
||||||
return 'NUMBER{' + number.value + '}';
|
|
||||||
};
|
|
||||||
|
|
||||||
PrintVisitor.prototype.BooleanLiteral = function(bool) {
|
|
||||||
return 'BOOLEAN{' + bool.value + '}';
|
|
||||||
};
|
|
||||||
|
|
||||||
PrintVisitor.prototype.UndefinedLiteral = function() {
|
|
||||||
return 'UNDEFINED';
|
|
||||||
};
|
|
||||||
|
|
||||||
PrintVisitor.prototype.NullLiteral = function() {
|
|
||||||
return 'NULL';
|
|
||||||
};
|
|
||||||
|
|
||||||
PrintVisitor.prototype.Hash = function(hash) {
|
|
||||||
let pairs = hash.pairs,
|
|
||||||
joinedPairs = [];
|
|
||||||
|
|
||||||
for (let i = 0, l = pairs.length; i < l; i++) {
|
|
||||||
joinedPairs.push(this.accept(pairs[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'HASH{' + joinedPairs.join(', ') + '}';
|
|
||||||
};
|
|
||||||
PrintVisitor.prototype.HashPair = function(pair) {
|
|
||||||
return pair.key + '=' + this.accept(pair.value);
|
|
||||||
};
|
|
||||||
/* eslint-enable new-cap */
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
import Exception from '../exception';
|
|
||||||
|
|
||||||
function Visitor() {
|
|
||||||
this.parents = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
Visitor.prototype = {
|
|
||||||
constructor: Visitor,
|
|
||||||
mutating: false,
|
|
||||||
|
|
||||||
// Visits a given value. If mutating, will replace the value if necessary.
|
|
||||||
acceptKey: function(node, name) {
|
|
||||||
let value = this.accept(node[name]);
|
|
||||||
if (this.mutating) {
|
|
||||||
// Hacky sanity check: This may have a few false positives for type for the helper
|
|
||||||
// methods but will generally do the right thing without a lot of overhead.
|
|
||||||
if (value && !Visitor.prototype[value.type]) {
|
|
||||||
throw new Exception(
|
|
||||||
'Unexpected node type "' +
|
|
||||||
value.type +
|
|
||||||
'" found when accepting ' +
|
|
||||||
name +
|
|
||||||
' on ' +
|
|
||||||
node.type
|
|
||||||
);
|
|
||||||
}
|
|
||||||
node[name] = value;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Performs an accept operation with added sanity check to ensure
|
|
||||||
// required keys are not removed.
|
|
||||||
acceptRequired: function(node, name) {
|
|
||||||
this.acceptKey(node, name);
|
|
||||||
|
|
||||||
if (!node[name]) {
|
|
||||||
throw new Exception(node.type + ' requires ' + name);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Traverses a given array. If mutating, empty respnses will be removed
|
|
||||||
// for child elements.
|
|
||||||
acceptArray: function(array) {
|
|
||||||
for (let i = 0, l = array.length; i < l; i++) {
|
|
||||||
this.acceptKey(array, i);
|
|
||||||
|
|
||||||
if (!array[i]) {
|
|
||||||
array.splice(i, 1);
|
|
||||||
i--;
|
|
||||||
l--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
accept: function(object) {
|
|
||||||
if (!object) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* istanbul ignore next: Sanity code */
|
|
||||||
if (!this[object.type]) {
|
|
||||||
throw new Exception('Unknown type: ' + object.type, object);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.current) {
|
|
||||||
this.parents.unshift(this.current);
|
|
||||||
}
|
|
||||||
this.current = object;
|
|
||||||
|
|
||||||
let ret = this[object.type](object);
|
|
||||||
|
|
||||||
this.current = this.parents.shift();
|
|
||||||
|
|
||||||
if (!this.mutating || ret) {
|
|
||||||
return ret;
|
|
||||||
} else if (ret !== false) {
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
Program: function(program) {
|
|
||||||
this.acceptArray(program.body);
|
|
||||||
},
|
|
||||||
|
|
||||||
MustacheStatement: visitSubExpression,
|
|
||||||
Decorator: visitSubExpression,
|
|
||||||
|
|
||||||
BlockStatement: visitBlock,
|
|
||||||
DecoratorBlock: visitBlock,
|
|
||||||
|
|
||||||
PartialStatement: visitPartial,
|
|
||||||
PartialBlockStatement: function(partial) {
|
|
||||||
visitPartial.call(this, partial);
|
|
||||||
|
|
||||||
this.acceptKey(partial, 'program');
|
|
||||||
},
|
|
||||||
|
|
||||||
ContentStatement: function(/* content */) {},
|
|
||||||
CommentStatement: function(/* comment */) {},
|
|
||||||
|
|
||||||
SubExpression: visitSubExpression,
|
|
||||||
|
|
||||||
PathExpression: function(/* path */) {},
|
|
||||||
|
|
||||||
StringLiteral: function(/* string */) {},
|
|
||||||
NumberLiteral: function(/* number */) {},
|
|
||||||
BooleanLiteral: function(/* bool */) {},
|
|
||||||
UndefinedLiteral: function(/* literal */) {},
|
|
||||||
NullLiteral: function(/* literal */) {},
|
|
||||||
|
|
||||||
Hash: function(hash) {
|
|
||||||
this.acceptArray(hash.pairs);
|
|
||||||
},
|
|
||||||
HashPair: function(pair) {
|
|
||||||
this.acceptRequired(pair, 'value');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function visitSubExpression(mustache) {
|
|
||||||
this.acceptRequired(mustache, 'path');
|
|
||||||
this.acceptArray(mustache.params);
|
|
||||||
this.acceptKey(mustache, 'hash');
|
|
||||||
}
|
|
||||||
function visitBlock(block) {
|
|
||||||
visitSubExpression.call(this, block);
|
|
||||||
|
|
||||||
this.acceptKey(block, 'program');
|
|
||||||
this.acceptKey(block, 'inverse');
|
|
||||||
}
|
|
||||||
function visitPartial(partial) {
|
|
||||||
this.acceptRequired(partial, 'name');
|
|
||||||
this.acceptArray(partial.params);
|
|
||||||
this.acceptKey(partial, 'hash');
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Visitor;
|
|
||||||
@@ -1,234 +0,0 @@
|
|||||||
import Visitor from './visitor';
|
|
||||||
|
|
||||||
function WhitespaceControl(options = {}) {
|
|
||||||
this.options = options;
|
|
||||||
}
|
|
||||||
WhitespaceControl.prototype = new Visitor();
|
|
||||||
|
|
||||||
WhitespaceControl.prototype.Program = function(program) {
|
|
||||||
const doStandalone = !this.options.ignoreStandalone;
|
|
||||||
|
|
||||||
let isRoot = !this.isRootSeen;
|
|
||||||
this.isRootSeen = true;
|
|
||||||
|
|
||||||
let body = program.body;
|
|
||||||
for (let i = 0, l = body.length; i < l; i++) {
|
|
||||||
let current = body[i],
|
|
||||||
strip = this.accept(current);
|
|
||||||
|
|
||||||
if (!strip) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let _isPrevWhitespace = isPrevWhitespace(body, i, isRoot),
|
|
||||||
_isNextWhitespace = isNextWhitespace(body, i, isRoot),
|
|
||||||
openStandalone = strip.openStandalone && _isPrevWhitespace,
|
|
||||||
closeStandalone = strip.closeStandalone && _isNextWhitespace,
|
|
||||||
inlineStandalone =
|
|
||||||
strip.inlineStandalone && _isPrevWhitespace && _isNextWhitespace;
|
|
||||||
|
|
||||||
if (strip.close) {
|
|
||||||
omitRight(body, i, true);
|
|
||||||
}
|
|
||||||
if (strip.open) {
|
|
||||||
omitLeft(body, i, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (doStandalone && inlineStandalone) {
|
|
||||||
omitRight(body, i);
|
|
||||||
|
|
||||||
if (omitLeft(body, i)) {
|
|
||||||
// If we are on a standalone node, save the indent info for partials
|
|
||||||
if (current.type === 'PartialStatement') {
|
|
||||||
// Pull out the whitespace from the final line
|
|
||||||
current.indent = /([ \t]+$)/.exec(body[i - 1].original)[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (doStandalone && openStandalone) {
|
|
||||||
omitRight((current.program || current.inverse).body);
|
|
||||||
|
|
||||||
// Strip out the previous content node if it's whitespace only
|
|
||||||
omitLeft(body, i);
|
|
||||||
}
|
|
||||||
if (doStandalone && closeStandalone) {
|
|
||||||
// Always strip the next node
|
|
||||||
omitRight(body, i);
|
|
||||||
|
|
||||||
omitLeft((current.inverse || current.program).body);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return program;
|
|
||||||
};
|
|
||||||
|
|
||||||
WhitespaceControl.prototype.BlockStatement = WhitespaceControl.prototype.DecoratorBlock = WhitespaceControl.prototype.PartialBlockStatement = function(
|
|
||||||
block
|
|
||||||
) {
|
|
||||||
this.accept(block.program);
|
|
||||||
this.accept(block.inverse);
|
|
||||||
|
|
||||||
// Find the inverse program that is involed with whitespace stripping.
|
|
||||||
let program = block.program || block.inverse,
|
|
||||||
inverse = block.program && block.inverse,
|
|
||||||
firstInverse = inverse,
|
|
||||||
lastInverse = inverse;
|
|
||||||
|
|
||||||
if (inverse && inverse.chained) {
|
|
||||||
firstInverse = inverse.body[0].program;
|
|
||||||
|
|
||||||
// Walk the inverse chain to find the last inverse that is actually in the chain.
|
|
||||||
while (lastInverse.chained) {
|
|
||||||
lastInverse = lastInverse.body[lastInverse.body.length - 1].program;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let strip = {
|
|
||||||
open: block.openStrip.open,
|
|
||||||
close: block.closeStrip.close,
|
|
||||||
|
|
||||||
// Determine the standalone candiacy. Basically flag our content as being possibly standalone
|
|
||||||
// so our parent can determine if we actually are standalone
|
|
||||||
openStandalone: isNextWhitespace(program.body),
|
|
||||||
closeStandalone: isPrevWhitespace((firstInverse || program).body)
|
|
||||||
};
|
|
||||||
|
|
||||||
if (block.openStrip.close) {
|
|
||||||
omitRight(program.body, null, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inverse) {
|
|
||||||
let inverseStrip = block.inverseStrip;
|
|
||||||
|
|
||||||
if (inverseStrip.open) {
|
|
||||||
omitLeft(program.body, null, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inverseStrip.close) {
|
|
||||||
omitRight(firstInverse.body, null, true);
|
|
||||||
}
|
|
||||||
if (block.closeStrip.open) {
|
|
||||||
omitLeft(lastInverse.body, null, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find standalone else statments
|
|
||||||
if (
|
|
||||||
!this.options.ignoreStandalone &&
|
|
||||||
isPrevWhitespace(program.body) &&
|
|
||||||
isNextWhitespace(firstInverse.body)
|
|
||||||
) {
|
|
||||||
omitLeft(program.body);
|
|
||||||
omitRight(firstInverse.body);
|
|
||||||
}
|
|
||||||
} else if (block.closeStrip.open) {
|
|
||||||
omitLeft(program.body, null, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return strip;
|
|
||||||
};
|
|
||||||
|
|
||||||
WhitespaceControl.prototype.Decorator = WhitespaceControl.prototype.MustacheStatement = function(
|
|
||||||
mustache
|
|
||||||
) {
|
|
||||||
return mustache.strip;
|
|
||||||
};
|
|
||||||
|
|
||||||
WhitespaceControl.prototype.PartialStatement = WhitespaceControl.prototype.CommentStatement = function(
|
|
||||||
node
|
|
||||||
) {
|
|
||||||
/* istanbul ignore next */
|
|
||||||
let strip = node.strip || {};
|
|
||||||
return {
|
|
||||||
inlineStandalone: true,
|
|
||||||
open: strip.open,
|
|
||||||
close: strip.close
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
function isPrevWhitespace(body, i, isRoot) {
|
|
||||||
if (i === undefined) {
|
|
||||||
i = body.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nodes that end with newlines are considered whitespace (but are special
|
|
||||||
// cased for strip operations)
|
|
||||||
let prev = body[i - 1],
|
|
||||||
sibling = body[i - 2];
|
|
||||||
if (!prev) {
|
|
||||||
return isRoot;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prev.type === 'ContentStatement') {
|
|
||||||
return (sibling || !isRoot ? /\r?\n\s*?$/ : /(^|\r?\n)\s*?$/).test(
|
|
||||||
prev.original
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function isNextWhitespace(body, i, isRoot) {
|
|
||||||
if (i === undefined) {
|
|
||||||
i = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let next = body[i + 1],
|
|
||||||
sibling = body[i + 2];
|
|
||||||
if (!next) {
|
|
||||||
return isRoot;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (next.type === 'ContentStatement') {
|
|
||||||
return (sibling || !isRoot ? /^\s*?\r?\n/ : /^\s*?(\r?\n|$)/).test(
|
|
||||||
next.original
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Marks the node to the right of the position as omitted.
|
|
||||||
// I.e. {{foo}}' ' will mark the ' ' node as omitted.
|
|
||||||
//
|
|
||||||
// If i is undefined, then the first child will be marked as such.
|
|
||||||
//
|
|
||||||
// If mulitple is truthy then all whitespace will be stripped out until non-whitespace
|
|
||||||
// content is met.
|
|
||||||
function omitRight(body, i, multiple) {
|
|
||||||
let current = body[i == null ? 0 : i + 1];
|
|
||||||
if (
|
|
||||||
!current ||
|
|
||||||
current.type !== 'ContentStatement' ||
|
|
||||||
(!multiple && current.rightStripped)
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let original = current.value;
|
|
||||||
current.value = current.value.replace(
|
|
||||||
multiple ? /^\s+/ : /^[ \t]*\r?\n?/,
|
|
||||||
''
|
|
||||||
);
|
|
||||||
current.rightStripped = current.value !== original;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Marks the node to the left of the position as omitted.
|
|
||||||
// I.e. ' '{{foo}} will mark the ' ' node as omitted.
|
|
||||||
//
|
|
||||||
// If i is undefined then the last child will be marked as such.
|
|
||||||
//
|
|
||||||
// If mulitple is truthy then all whitespace will be stripped out until non-whitespace
|
|
||||||
// content is met.
|
|
||||||
function omitLeft(body, i, multiple) {
|
|
||||||
let current = body[i == null ? body.length - 1 : i - 1];
|
|
||||||
if (
|
|
||||||
!current ||
|
|
||||||
current.type !== 'ContentStatement' ||
|
|
||||||
(!multiple && current.leftStripped)
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We omit the last node if it's whitespace only and not preceded by a non-content node.
|
|
||||||
let original = current.value;
|
|
||||||
current.value = current.value.replace(multiple ? /\s+$/ : /[ \t]+$/, '');
|
|
||||||
current.leftStripped = current.value !== original;
|
|
||||||
return current.leftStripped;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default WhitespaceControl;
|
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
import { extend } from '../utils';
|
import { extend } from '../utils';
|
||||||
|
|
||||||
export default function(instance) {
|
export default function (instance) {
|
||||||
instance.registerDecorator('inline', function(fn, props, container, options) {
|
instance.registerDecorator(
|
||||||
|
'inline',
|
||||||
|
function (fn, props, container, options) {
|
||||||
let ret = fn;
|
let ret = fn;
|
||||||
if (!props.partials) {
|
if (!props.partials) {
|
||||||
props.partials = {};
|
props.partials = {};
|
||||||
ret = function(context, options) {
|
ret = function (context, options) {
|
||||||
// Create a new partials stack frame prior to exec.
|
// Create a new partials stack frame prior to exec.
|
||||||
let original = container.partials;
|
let original = container.partials;
|
||||||
container.partials = extend({}, original, props.partials);
|
container.partials = extend({}, original, props.partials);
|
||||||
@@ -18,5 +20,6 @@ export default function(instance) {
|
|||||||
props.partials[options.args[0]] = options.fn;
|
props.partials[options.args[0]] = options.fn;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +0,0 @@
|
|||||||
const errorProps = [
|
|
||||||
'description',
|
|
||||||
'fileName',
|
|
||||||
'lineNumber',
|
|
||||||
'endLineNumber',
|
|
||||||
'message',
|
|
||||||
'name',
|
|
||||||
'number',
|
|
||||||
'stack'
|
|
||||||
];
|
|
||||||
|
|
||||||
function Exception(message, node) {
|
|
||||||
let loc = node && node.loc,
|
|
||||||
line,
|
|
||||||
endLineNumber,
|
|
||||||
column,
|
|
||||||
endColumn;
|
|
||||||
|
|
||||||
if (loc) {
|
|
||||||
line = loc.start.line;
|
|
||||||
endLineNumber = loc.end.line;
|
|
||||||
column = loc.start.column;
|
|
||||||
endColumn = loc.end.column;
|
|
||||||
|
|
||||||
message += ' - ' + line + ':' + column;
|
|
||||||
}
|
|
||||||
|
|
||||||
let tmp = Error.prototype.constructor.call(this, message);
|
|
||||||
|
|
||||||
// Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
|
|
||||||
for (let idx = 0; idx < errorProps.length; idx++) {
|
|
||||||
this[errorProps[idx]] = tmp[errorProps[idx]];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* istanbul ignore else */
|
|
||||||
if (Error.captureStackTrace) {
|
|
||||||
Error.captureStackTrace(this, Exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (loc) {
|
|
||||||
this.lineNumber = line;
|
|
||||||
this.endLineNumber = endLineNumber;
|
|
||||||
|
|
||||||
// Work around issue under safari where we can't directly set the column value
|
|
||||||
/* istanbul ignore next */
|
|
||||||
if (Object.defineProperty) {
|
|
||||||
Object.defineProperty(this, 'column', {
|
|
||||||
value: column,
|
|
||||||
enumerable: true
|
|
||||||
});
|
|
||||||
Object.defineProperty(this, 'endColumn', {
|
|
||||||
value: endColumn,
|
|
||||||
enumerable: true
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.column = column;
|
|
||||||
this.endColumn = endColumn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (nop) {
|
|
||||||
/* Ignore if the browser is very particular */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Exception.prototype = new Error();
|
|
||||||
|
|
||||||
export default Exception;
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { appendContextPath, createFrame, isArray } from '../utils';
|
import { isArray } from '../utils';
|
||||||
|
|
||||||
export default function(instance) {
|
export default function (instance) {
|
||||||
instance.registerHelper('blockHelperMissing', function(context, options) {
|
instance.registerHelper('blockHelperMissing', function (context, options) {
|
||||||
let inverse = options.inverse,
|
let inverse = options.inverse,
|
||||||
fn = options.fn;
|
fn = options.fn;
|
||||||
|
|
||||||
@@ -11,24 +11,11 @@ export default function(instance) {
|
|||||||
return inverse(this);
|
return inverse(this);
|
||||||
} else if (isArray(context)) {
|
} else if (isArray(context)) {
|
||||||
if (context.length > 0) {
|
if (context.length > 0) {
|
||||||
if (options.ids) {
|
|
||||||
options.ids = [options.name];
|
|
||||||
}
|
|
||||||
|
|
||||||
return instance.helpers.each(context, options);
|
return instance.helpers.each(context, options);
|
||||||
} else {
|
} else {
|
||||||
return inverse(this);
|
return inverse(this);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (options.data && options.ids) {
|
|
||||||
let data = createFrame(options.data);
|
|
||||||
data.contextPath = appendContextPath(
|
|
||||||
options.data.contextPath,
|
|
||||||
options.name
|
|
||||||
);
|
|
||||||
options = { data: data };
|
|
||||||
}
|
|
||||||
|
|
||||||
return fn(context, options);
|
return fn(context, options);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,14 +1,8 @@
|
|||||||
import {
|
import { Exception } from '@handlebars/parser';
|
||||||
appendContextPath,
|
import { createFrame, isArray, isFunction } from '../utils';
|
||||||
blockParams,
|
|
||||||
createFrame,
|
|
||||||
isArray,
|
|
||||||
isFunction
|
|
||||||
} from '../utils';
|
|
||||||
import Exception from '../exception';
|
|
||||||
|
|
||||||
export default function(instance) {
|
export default function (instance) {
|
||||||
instance.registerHelper('each', function(context, options) {
|
instance.registerHelper('each', function (context, options) {
|
||||||
if (!options) {
|
if (!options) {
|
||||||
throw new Exception('Must pass iterator to #each');
|
throw new Exception('Must pass iterator to #each');
|
||||||
}
|
}
|
||||||
@@ -17,13 +11,7 @@ export default function(instance) {
|
|||||||
inverse = options.inverse,
|
inverse = options.inverse,
|
||||||
i = 0,
|
i = 0,
|
||||||
ret = '',
|
ret = '',
|
||||||
data,
|
data;
|
||||||
contextPath;
|
|
||||||
|
|
||||||
if (options.data && options.ids) {
|
|
||||||
contextPath =
|
|
||||||
appendContextPath(options.data.contextPath, options.ids[0]) + '.';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isFunction(context)) {
|
if (isFunction(context)) {
|
||||||
context = context.call(this);
|
context = context.call(this);
|
||||||
@@ -39,20 +27,13 @@ export default function(instance) {
|
|||||||
data.index = index;
|
data.index = index;
|
||||||
data.first = index === 0;
|
data.first = index === 0;
|
||||||
data.last = !!last;
|
data.last = !!last;
|
||||||
|
|
||||||
if (contextPath) {
|
|
||||||
data.contextPath = contextPath + field;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret =
|
ret =
|
||||||
ret +
|
ret +
|
||||||
fn(context[field], {
|
fn(context[field], {
|
||||||
data: data,
|
data: data,
|
||||||
blockParams: blockParams(
|
blockParams: [context[field], field],
|
||||||
[context[field], field],
|
|
||||||
[contextPath + field, null]
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,10 +57,10 @@ export default function(instance) {
|
|||||||
} else {
|
} else {
|
||||||
let priorKey;
|
let priorKey;
|
||||||
|
|
||||||
Object.keys(context).forEach(key => {
|
Object.keys(context).forEach((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 intermediate keys array.
|
||||||
if (priorKey !== undefined) {
|
if (priorKey !== undefined) {
|
||||||
execIteration(priorKey, i - 1);
|
execIteration(priorKey, i - 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import Exception from '../exception';
|
import { Exception } from '@handlebars/parser';
|
||||||
|
|
||||||
export default function(instance) {
|
export default function (instance) {
|
||||||
instance.registerHelper('helperMissing', function(/* [args, ]options */) {
|
instance.registerHelper('helperMissing', function (/* [args, ]options */) {
|
||||||
if (arguments.length === 1) {
|
if (arguments.length === 1) {
|
||||||
// A missing field in a {{foo}} construct.
|
// A missing field in a {{foo}} construct.
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
import { Exception } from '@handlebars/parser';
|
||||||
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) {
|
if (arguments.length != 2) {
|
||||||
throw new Exception('#if requires exactly one argument');
|
throw new Exception('#if requires exactly one argument');
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,7 @@ export default function(instance) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
// The `includeZero` option may be set to treat the condtional as purely not empty based on the
|
// The `includeZero` option may be set to treat the conditional as purely not empty based on the
|
||||||
// behavior of isEmpty. Effectively this determines if 0 is handled by the positive path or negative.
|
// behavior of isEmpty. Effectively this determines if 0 is handled by the positive path or negative.
|
||||||
if ((!options.hash.includeZero && !conditional) || isEmpty(conditional)) {
|
if ((!options.hash.includeZero && !conditional) || isEmpty(conditional)) {
|
||||||
return options.inverse(this);
|
return options.inverse(this);
|
||||||
@@ -20,14 +20,14 @@ export default function(instance) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
instance.registerHelper('unless', function(conditional, options) {
|
instance.registerHelper('unless', function (conditional, options) {
|
||||||
if (arguments.length != 2) {
|
if (arguments.length != 2) {
|
||||||
throw new Exception('#unless requires exactly one argument');
|
throw new Exception('#unless requires exactly one argument');
|
||||||
}
|
}
|
||||||
return instance.helpers['if'].call(this, conditional, {
|
return instance.helpers['if'].call(this, conditional, {
|
||||||
fn: options.inverse,
|
fn: options.inverse,
|
||||||
inverse: options.fn,
|
inverse: options.fn,
|
||||||
hash: options.hash
|
hash: options.hash,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export default function(instance) {
|
export default function (instance) {
|
||||||
instance.registerHelper('log', function(/* message, options */) {
|
instance.registerHelper('log', function (/* message, options */) {
|
||||||
let args = [undefined],
|
let args = [undefined],
|
||||||
options = arguments[arguments.length - 1];
|
options = arguments[arguments.length - 1];
|
||||||
for (let i = 0; i < arguments.length - 1; i++) {
|
for (let i = 0; i < arguments.length - 1; i++) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export default function(instance) {
|
export default function (instance) {
|
||||||
instance.registerHelper('lookup', function(obj, field, options) {
|
instance.registerHelper('lookup', function (obj, field, options) {
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
// Note for 5.0: Change to "obj == null" in 5.0
|
// Note for 5.0: Change to "obj == null" in 5.0
|
||||||
return obj;
|
return obj;
|
||||||
|
|||||||
@@ -1,14 +1,8 @@
|
|||||||
import {
|
import { Exception } from '@handlebars/parser';
|
||||||
appendContextPath,
|
import { isEmpty, isFunction } from '../utils';
|
||||||
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) {
|
if (arguments.length != 2) {
|
||||||
throw new Exception('#with requires exactly one argument');
|
throw new Exception('#with requires exactly one argument');
|
||||||
}
|
}
|
||||||
@@ -20,17 +14,10 @@ export default function(instance) {
|
|||||||
|
|
||||||
if (!isEmpty(context)) {
|
if (!isEmpty(context)) {
|
||||||
let data = options.data;
|
let data = options.data;
|
||||||
if (options.data && options.ids) {
|
|
||||||
data = createFrame(options.data);
|
|
||||||
data.contextPath = appendContextPath(
|
|
||||||
options.data.contextPath,
|
|
||||||
options.ids[0]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return fn(context, {
|
return fn(context, {
|
||||||
data: data,
|
data: data,
|
||||||
blockParams: blockParams([context], [data && data.contextPath])
|
blockParams: [context],
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return options.inverse(this);
|
return options.inverse(this);
|
||||||
|
|||||||
@@ -20,15 +20,15 @@ export function createProtoAccessControl(runtimeOptions) {
|
|||||||
defaultPropertyWhiteList,
|
defaultPropertyWhiteList,
|
||||||
runtimeOptions.allowedProtoProperties
|
runtimeOptions.allowedProtoProperties
|
||||||
),
|
),
|
||||||
defaultValue: runtimeOptions.allowProtoPropertiesByDefault
|
defaultValue: runtimeOptions.allowProtoPropertiesByDefault,
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
whitelist: createNewLookupObject(
|
whitelist: createNewLookupObject(
|
||||||
defaultMethodWhiteList,
|
defaultMethodWhiteList,
|
||||||
runtimeOptions.allowedProtoMethods
|
runtimeOptions.allowedProtoMethods
|
||||||
),
|
),
|
||||||
defaultValue: runtimeOptions.allowProtoMethodsByDefault
|
defaultValue: runtimeOptions.allowProtoMethodsByDefault,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,11 +47,11 @@ function checkWhiteList(protoAccessControlForType, propertyName) {
|
|||||||
if (protoAccessControlForType.defaultValue !== undefined) {
|
if (protoAccessControlForType.defaultValue !== undefined) {
|
||||||
return protoAccessControlForType.defaultValue;
|
return protoAccessControlForType.defaultValue;
|
||||||
}
|
}
|
||||||
logUnexpecedPropertyAccessOnce(propertyName);
|
logUnexpectedPropertyAccessOnce(propertyName);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function logUnexpecedPropertyAccessOnce(propertyName) {
|
function logUnexpectedPropertyAccessOnce(propertyName) {
|
||||||
if (loggedProperties[propertyName] !== true) {
|
if (loggedProperties[propertyName] !== true) {
|
||||||
loggedProperties[propertyName] = true;
|
loggedProperties[propertyName] = true;
|
||||||
logger.log(
|
logger.log(
|
||||||
@@ -64,7 +64,7 @@ function logUnexpecedPropertyAccessOnce(propertyName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function resetLoggedProperties() {
|
export function resetLoggedProperties() {
|
||||||
Object.keys(loggedProperties).forEach(propertyName => {
|
Object.keys(loggedProperties).forEach((propertyName) => {
|
||||||
delete loggedProperties[propertyName];
|
delete loggedProperties[propertyName];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
export function wrapHelper(helper, transformOptionsFn) {
|
export function wrapHelper(helper, transformOptionsFn) {
|
||||||
if (typeof helper !== 'function') {
|
if (typeof helper !== 'function') {
|
||||||
// This should not happen, but apparently it does in https://github.com/wycats/handlebars.js/issues/1639
|
// This should not happen, but apparently it does in https://github.com/handlebars-lang/handlebars.js/issues/1639
|
||||||
// We try to make the wrapper least-invasive by not wrapping it, if the helper is not a function.
|
// We try to make the wrapper least-invasive by not wrapping it, if the helper is not a function.
|
||||||
return helper;
|
return helper;
|
||||||
}
|
}
|
||||||
let wrapper = function(/* dynamic arguments */) {
|
let wrapper = function (/* dynamic arguments */) {
|
||||||
const options = arguments[arguments.length - 1];
|
const options = arguments[arguments.length - 1];
|
||||||
arguments[arguments.length - 1] = transformOptionsFn(options);
|
arguments[arguments.length - 1] = transformOptionsFn(options);
|
||||||
return helper.apply(this, arguments);
|
return helper.apply(this, arguments);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ let logger = {
|
|||||||
level: 'info',
|
level: 'info',
|
||||||
|
|
||||||
// Maps a given level value to the `methodMap` indexes above.
|
// Maps a given level value to the `methodMap` indexes above.
|
||||||
lookupLevel: function(level) {
|
lookupLevel: function (level) {
|
||||||
if (typeof level === 'string') {
|
if (typeof level === 'string') {
|
||||||
let levelMap = indexOf(logger.methodMap, level.toLowerCase());
|
let levelMap = indexOf(logger.methodMap, level.toLowerCase());
|
||||||
if (levelMap >= 0) {
|
if (levelMap >= 0) {
|
||||||
@@ -19,7 +19,7 @@ let logger = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Can be overridden in the host environment
|
// Can be overridden in the host environment
|
||||||
log: function(level, ...message) {
|
log: function (level, ...message) {
|
||||||
level = logger.lookupLevel(level);
|
level = logger.lookupLevel(level);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -33,7 +33,7 @@ let logger = {
|
|||||||
}
|
}
|
||||||
console[method](...message); // eslint-disable-line no-console
|
console[method](...message); // eslint-disable-line no-console
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default logger;
|
export default logger;
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
export default function(Handlebars) {
|
export default function (Handlebars) {
|
||||||
|
let $Handlebars = globalThis.Handlebars;
|
||||||
|
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
let root = typeof global !== 'undefined' ? global : window, // eslint-disable-line no-undef
|
Handlebars.noConflict = function () {
|
||||||
$Handlebars = root.Handlebars;
|
if (globalThis.Handlebars === Handlebars) {
|
||||||
/* istanbul ignore next */
|
globalThis.Handlebars = $Handlebars;
|
||||||
Handlebars.noConflict = function() {
|
|
||||||
if (root.Handlebars === Handlebars) {
|
|
||||||
root.Handlebars = $Handlebars;
|
|
||||||
}
|
}
|
||||||
return Handlebars;
|
return Handlebars;
|
||||||
};
|
};
|
||||||
|
|||||||
+21
-43
@@ -1,16 +1,16 @@
|
|||||||
|
import { Exception } from '@handlebars/parser';
|
||||||
import * as Utils from './utils';
|
import * as Utils from './utils';
|
||||||
import Exception from './exception';
|
|
||||||
import {
|
import {
|
||||||
COMPILER_REVISION,
|
COMPILER_REVISION,
|
||||||
createFrame,
|
createFrame,
|
||||||
LAST_COMPATIBLE_COMPILER_REVISION,
|
LAST_COMPATIBLE_COMPILER_REVISION,
|
||||||
REVISION_CHANGES
|
REVISION_CHANGES,
|
||||||
} from './base';
|
} from './base';
|
||||||
import { moveHelperToHooks } from './helpers';
|
import { moveHelperToHooks } from './helpers';
|
||||||
import { wrapHelper } from './internal/wrapHelper';
|
import { wrapHelper } from './internal/wrapHelper';
|
||||||
import {
|
import {
|
||||||
createProtoAccessControl,
|
createProtoAccessControl,
|
||||||
resultIsAllowed
|
resultIsAllowed,
|
||||||
} from './internal/proto-access';
|
} from './internal/proto-access';
|
||||||
|
|
||||||
export function checkRevision(compilerInfo) {
|
export function checkRevision(compilerInfo) {
|
||||||
@@ -68,15 +68,12 @@ export function template(templateSpec, env) {
|
|||||||
function invokePartialWrapper(partial, context, options) {
|
function invokePartialWrapper(partial, context, options) {
|
||||||
if (options.hash) {
|
if (options.hash) {
|
||||||
context = Utils.extend({}, context, options.hash);
|
context = Utils.extend({}, context, options.hash);
|
||||||
if (options.ids) {
|
|
||||||
options.ids[0] = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
partial = env.VM.resolvePartial.call(this, partial, context, options);
|
partial = env.VM.resolvePartial.call(this, partial, context, options);
|
||||||
|
|
||||||
let extendedOptions = Utils.extend({}, options, {
|
let extendedOptions = Utils.extend({}, options, {
|
||||||
hooks: this.hooks,
|
hooks: this.hooks,
|
||||||
protoAccessControl: this.protoAccessControl
|
protoAccessControl: this.protoAccessControl,
|
||||||
});
|
});
|
||||||
|
|
||||||
let result = env.VM.invokePartial.call(
|
let result = env.VM.invokePartial.call(
|
||||||
@@ -118,15 +115,15 @@ export function template(templateSpec, env) {
|
|||||||
|
|
||||||
// Just add water
|
// Just add water
|
||||||
let container = {
|
let container = {
|
||||||
strict: function(obj, name, loc) {
|
strict: function (obj, name, loc) {
|
||||||
if (!obj || !(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
|
loc: loc,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return container.lookupProperty(obj, name);
|
return container.lookupProperty(obj, name);
|
||||||
},
|
},
|
||||||
lookupProperty: function(parent, propertyName) {
|
lookupProperty: function (parent, propertyName) {
|
||||||
let result = parent[propertyName];
|
let result = parent[propertyName];
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
return result;
|
return result;
|
||||||
@@ -140,7 +137,7 @@ export function template(templateSpec, env) {
|
|||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
},
|
},
|
||||||
lookup: function(depths, name) {
|
lookup: function (depths, name) {
|
||||||
const len = depths.length;
|
const len = depths.length;
|
||||||
for (let i = 0; i < len; i++) {
|
for (let i = 0; i < len; i++) {
|
||||||
let result = depths[i] && container.lookupProperty(depths[i], name);
|
let result = depths[i] && container.lookupProperty(depths[i], name);
|
||||||
@@ -149,21 +146,21 @@ export function template(templateSpec, env) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
lambda: function(current, context) {
|
lambda: function (current, context) {
|
||||||
return typeof current === 'function' ? current.call(context) : current;
|
return typeof current === 'function' ? current.call(context) : current;
|
||||||
},
|
},
|
||||||
|
|
||||||
escapeExpression: Utils.escapeExpression,
|
escapeExpression: Utils.escapeExpression,
|
||||||
invokePartial: invokePartialWrapper,
|
invokePartial: invokePartialWrapper,
|
||||||
|
|
||||||
fn: function(i) {
|
fn: function (i) {
|
||||||
let ret = templateSpec[i];
|
let ret = templateSpec[i];
|
||||||
ret.decorator = templateSpec[i + '_d'];
|
ret.decorator = templateSpec[i + '_d'];
|
||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
|
|
||||||
programs: [],
|
programs: [],
|
||||||
program: function(i, data, declaredBlockParams, blockParams, depths) {
|
program: function (i, data, declaredBlockParams, blockParams, depths) {
|
||||||
let programWrapper = this.programs[i],
|
let programWrapper = this.programs[i],
|
||||||
fn = this.fn(i);
|
fn = this.fn(i);
|
||||||
if (data || depths || blockParams || declaredBlockParams) {
|
if (data || depths || blockParams || declaredBlockParams) {
|
||||||
@@ -182,13 +179,13 @@ export function template(templateSpec, env) {
|
|||||||
return programWrapper;
|
return programWrapper;
|
||||||
},
|
},
|
||||||
|
|
||||||
data: function(value, depth) {
|
data: function (value, depth) {
|
||||||
while (value && depth--) {
|
while (value && depth--) {
|
||||||
value = value._parent;
|
value = value._parent;
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
},
|
},
|
||||||
mergeIfNeeded: function(param, common) {
|
mergeIfNeeded: function (param, common) {
|
||||||
let obj = param || common;
|
let obj = param || common;
|
||||||
|
|
||||||
if (param && common && param !== common) {
|
if (param && common && param !== common) {
|
||||||
@@ -201,13 +198,13 @@ export function template(templateSpec, env) {
|
|||||||
nullContext: Object.seal({}),
|
nullContext: Object.seal({}),
|
||||||
|
|
||||||
noop: env.VM.noop,
|
noop: env.VM.noop,
|
||||||
compilerInfo: templateSpec.compiler
|
compilerInfo: templateSpec.compiler,
|
||||||
};
|
};
|
||||||
|
|
||||||
function ret(context, options = {}) {
|
function ret(context, options = {}) {
|
||||||
let data = options.data;
|
let data = options.data;
|
||||||
|
|
||||||
ret._setup(options);
|
_setup(options);
|
||||||
if (!options.partial && templateSpec.useData) {
|
if (!options.partial && templateSpec.useData) {
|
||||||
data = initData(context, data);
|
data = initData(context, data);
|
||||||
}
|
}
|
||||||
@@ -252,7 +249,7 @@ export function template(templateSpec, env) {
|
|||||||
|
|
||||||
ret.isTop = true;
|
ret.isTop = true;
|
||||||
|
|
||||||
ret._setup = function(options) {
|
function _setup(options) {
|
||||||
if (!options.partial) {
|
if (!options.partial) {
|
||||||
let mergedHelpers = Utils.extend({}, env.helpers, options.helpers);
|
let mergedHelpers = Utils.extend({}, env.helpers, options.helpers);
|
||||||
wrapHelpersToPassLookupProperty(mergedHelpers, container);
|
wrapHelpersToPassLookupProperty(mergedHelpers, container);
|
||||||
@@ -288,26 +285,8 @@ export function template(templateSpec, env) {
|
|||||||
container.decorators = options.decorators;
|
container.decorators = options.decorators;
|
||||||
container.hooks = options.hooks;
|
container.hooks = options.hooks;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
ret._child = function(i, data, blockParams, depths) {
|
|
||||||
if (templateSpec.useBlockParams && !blockParams) {
|
|
||||||
throw new Exception('must pass block params');
|
|
||||||
}
|
|
||||||
if (templateSpec.useDepths && !depths) {
|
|
||||||
throw new Exception('must pass parent depths');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return wrapProgram(
|
|
||||||
container,
|
|
||||||
i,
|
|
||||||
templateSpec[i],
|
|
||||||
data,
|
|
||||||
0,
|
|
||||||
blockParams,
|
|
||||||
depths
|
|
||||||
);
|
|
||||||
};
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,9 +350,6 @@ export function invokePartial(partial, context, options) {
|
|||||||
// Use the current closure context to save the partial-block if this partial
|
// Use the current closure context to save the partial-block if this partial
|
||||||
const currentPartialBlock = options.data && options.data['partial-block'];
|
const currentPartialBlock = options.data && options.data['partial-block'];
|
||||||
options.partial = true;
|
options.partial = true;
|
||||||
if (options.ids) {
|
|
||||||
options.data.contextPath = options.ids[0] || options.data.contextPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
let partialBlock;
|
let partialBlock;
|
||||||
if (options.fn && options.fn !== noop) {
|
if (options.fn && options.fn !== noop) {
|
||||||
@@ -400,7 +376,9 @@ export function invokePartial(partial, context, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (partial === undefined) {
|
if (partial === undefined) {
|
||||||
throw new Exception('The partial ' + options.name + ' could not be found');
|
throw new Exception(
|
||||||
|
'The partial "' + options.name + '" could not be found'
|
||||||
|
);
|
||||||
} else if (partial instanceof Function) {
|
} else if (partial instanceof Function) {
|
||||||
return partial(context, options);
|
return partial(context, options);
|
||||||
}
|
}
|
||||||
@@ -436,7 +414,7 @@ function executeDecorators(fn, prog, container, depths, data, blockParams) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function wrapHelpersToPassLookupProperty(mergedHelpers, container) {
|
function wrapHelpersToPassLookupProperty(mergedHelpers, container) {
|
||||||
Object.keys(mergedHelpers).forEach(helperName => {
|
Object.keys(mergedHelpers).forEach((helperName) => {
|
||||||
let helper = mergedHelpers[helperName];
|
let helper = mergedHelpers[helperName];
|
||||||
mergedHelpers[helperName] = passLookupPropertyOption(helper, container);
|
mergedHelpers[helperName] = passLookupPropertyOption(helper, container);
|
||||||
});
|
});
|
||||||
@@ -444,7 +422,7 @@ function wrapHelpersToPassLookupProperty(mergedHelpers, container) {
|
|||||||
|
|
||||||
function passLookupPropertyOption(helper, container) {
|
function passLookupPropertyOption(helper, container) {
|
||||||
const lookupProperty = container.lookupProperty;
|
const lookupProperty = container.lookupProperty;
|
||||||
return wrapHelper(helper, options => {
|
return wrapHelper(helper, (options) => {
|
||||||
return Utils.extend({ lookupProperty }, options);
|
return Utils.extend({ lookupProperty }, options);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ function SafeString(string) {
|
|||||||
this.string = string;
|
this.string = string;
|
||||||
}
|
}
|
||||||
|
|
||||||
SafeString.prototype.toString = SafeString.prototype.toHTML = function() {
|
SafeString.prototype.toString = SafeString.prototype.toHTML = function () {
|
||||||
return '' + this.string;
|
return '' + this.string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+4
-26
@@ -5,7 +5,7 @@ const escape = {
|
|||||||
'"': '"',
|
'"': '"',
|
||||||
"'": ''',
|
"'": ''',
|
||||||
'`': '`',
|
'`': '`',
|
||||||
'=': '='
|
'=': '=',
|
||||||
};
|
};
|
||||||
|
|
||||||
const badChars = /[&<>"'`=]/g,
|
const badChars = /[&<>"'`=]/g,
|
||||||
@@ -30,28 +30,15 @@ export function extend(obj /* , ...source */) {
|
|||||||
export let toString = Object.prototype.toString;
|
export let toString = Object.prototype.toString;
|
||||||
|
|
||||||
// Sourced from lodash
|
// Sourced from lodash
|
||||||
// https://github.com/bestiejs/lodash/blob/master/LICENSE.txt
|
// https://github.com/lodash/lodash/blob/4.17.21/LICENSE
|
||||||
/* eslint-disable func-style */
|
export function isFunction(value) {
|
||||||
let isFunction = function(value) {
|
|
||||||
return typeof value === 'function';
|
return typeof value === 'function';
|
||||||
};
|
|
||||||
// fallback for older versions of Chrome and Safari
|
|
||||||
/* istanbul ignore next */
|
|
||||||
if (isFunction(/x/)) {
|
|
||||||
isFunction = function(value) {
|
|
||||||
return (
|
|
||||||
typeof value === 'function' &&
|
|
||||||
toString.call(value) === '[object Function]'
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
export { isFunction };
|
|
||||||
/* eslint-enable func-style */
|
|
||||||
|
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
export const isArray =
|
export const isArray =
|
||||||
Array.isArray ||
|
Array.isArray ||
|
||||||
function(value) {
|
function (value) {
|
||||||
return value && typeof value === 'object'
|
return value && typeof value === 'object'
|
||||||
? toString.call(value) === '[object Array]'
|
? toString.call(value) === '[object Array]'
|
||||||
: false;
|
: false;
|
||||||
@@ -105,12 +92,3 @@ export function createFrame(object) {
|
|||||||
frame._parent = object;
|
frame._parent = object;
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function blockParams(params, ids) {
|
|
||||||
params.path = ids;
|
|
||||||
return params;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function appendContextPath(contextPath, id) {
|
|
||||||
return (contextPath ? contextPath + '.' : '') + id;
|
|
||||||
}
|
|
||||||
|
|||||||
+3
-3
@@ -7,9 +7,9 @@
|
|||||||
|
|
||||||
var handlebars = require('../dist/cjs/handlebars')['default'];
|
var handlebars = require('../dist/cjs/handlebars')['default'];
|
||||||
|
|
||||||
var printer = require('../dist/cjs/handlebars/compiler/printer');
|
var parser = require('@handlebars/parser');
|
||||||
handlebars.PrintVisitor = printer.PrintVisitor;
|
handlebars.PrintVisitor = parser.PrintVisitor;
|
||||||
handlebars.print = printer.print;
|
handlebars.print = parser.print;
|
||||||
|
|
||||||
module.exports = handlebars;
|
module.exports = handlebars;
|
||||||
|
|
||||||
|
|||||||
+28
-23
@@ -6,12 +6,12 @@ import * as Handlebars from './handlebars';
|
|||||||
import { basename } from 'path';
|
import { basename } from 'path';
|
||||||
import { SourceMapConsumer, SourceNode } from 'source-map';
|
import { SourceMapConsumer, SourceNode } from 'source-map';
|
||||||
|
|
||||||
module.exports.loadTemplates = function(opts, callback) {
|
module.exports.loadTemplates = function (opts, callback) {
|
||||||
loadStrings(opts, function(err, strings) {
|
loadStrings(opts, function (err, strings) {
|
||||||
if (err) {
|
if (err) {
|
||||||
callback(err);
|
callback(err);
|
||||||
} else {
|
} else {
|
||||||
loadFiles(opts, function(err, files) {
|
loadFiles(opts, function (err, files) {
|
||||||
if (err) {
|
if (err) {
|
||||||
callback(err);
|
callback(err);
|
||||||
} else {
|
} else {
|
||||||
@@ -37,7 +37,7 @@ function loadStrings(opts, callback) {
|
|||||||
|
|
||||||
Async.map(
|
Async.map(
|
||||||
strings,
|
strings,
|
||||||
function(string, callback) {
|
function (string, callback) {
|
||||||
if (string !== '-') {
|
if (string !== '-') {
|
||||||
callback(undefined, string);
|
callback(undefined, string);
|
||||||
} else {
|
} else {
|
||||||
@@ -45,19 +45,19 @@ function loadStrings(opts, callback) {
|
|||||||
let buffer = '';
|
let buffer = '';
|
||||||
process.stdin.setEncoding('utf8');
|
process.stdin.setEncoding('utf8');
|
||||||
|
|
||||||
process.stdin.on('data', function(chunk) {
|
process.stdin.on('data', function (chunk) {
|
||||||
buffer += chunk;
|
buffer += chunk;
|
||||||
});
|
});
|
||||||
process.stdin.on('end', function() {
|
process.stdin.on('end', function () {
|
||||||
callback(undefined, buffer);
|
callback(undefined, buffer);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
function(err, strings) {
|
function (err, strings) {
|
||||||
strings = strings.map((string, index) => ({
|
strings = strings.map((string, index) => ({
|
||||||
name: names[index],
|
name: names[index],
|
||||||
path: names[index],
|
path: names[index],
|
||||||
source: string
|
source: string,
|
||||||
}));
|
}));
|
||||||
callback(err, strings);
|
callback(err, strings);
|
||||||
}
|
}
|
||||||
@@ -68,20 +68,23 @@ function loadFiles(opts, callback) {
|
|||||||
// Build file extension pattern
|
// Build file extension pattern
|
||||||
let extension = (opts.extension || 'handlebars').replace(
|
let extension = (opts.extension || 'handlebars').replace(
|
||||||
/[\\^$*+?.():=!|{}\-[\]]/g,
|
/[\\^$*+?.():=!|{}\-[\]]/g,
|
||||||
function(arg) {
|
function (arg) {
|
||||||
return '\\' + arg;
|
return '\\' + arg;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
extension = new RegExp('\\.' + extension + '$');
|
extension = new RegExp('\\.' + extension + '$');
|
||||||
|
|
||||||
let ret = [],
|
let ret = [],
|
||||||
queue = (opts.files || []).map(template => ({ template, root: opts.root }));
|
queue = (opts.files || []).map((template) => ({
|
||||||
|
template,
|
||||||
|
root: opts.root,
|
||||||
|
}));
|
||||||
Async.whilst(
|
Async.whilst(
|
||||||
() => queue.length,
|
() => queue.length,
|
||||||
function(callback) {
|
function (callback) {
|
||||||
let { template: path, root } = queue.shift();
|
let { template: path, root } = queue.shift();
|
||||||
|
|
||||||
fs.stat(path, function(err, stat) {
|
fs.stat(path, function (err, stat) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(
|
return callback(
|
||||||
new Handlebars.Exception(`Unable to open template file "${path}"`)
|
new Handlebars.Exception(`Unable to open template file "${path}"`)
|
||||||
@@ -91,12 +94,12 @@ function loadFiles(opts, callback) {
|
|||||||
if (stat.isDirectory()) {
|
if (stat.isDirectory()) {
|
||||||
opts.hasDirectory = true;
|
opts.hasDirectory = true;
|
||||||
|
|
||||||
fs.readdir(path, function(err, children) {
|
fs.readdir(path, function (err, children) {
|
||||||
/* istanbul ignore next : Race condition that being too lazy to test */
|
/* istanbul ignore next : Race condition that being too lazy to test */
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
children.forEach(function(file) {
|
children.forEach(function (file) {
|
||||||
let childPath = path + '/' + file;
|
let childPath = path + '/' + file;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -110,7 +113,7 @@ function loadFiles(opts, callback) {
|
|||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
fs.readFile(path, 'utf8', function(err, data) {
|
fs.readFile(path, 'utf8', function (err, data) {
|
||||||
/* istanbul ignore next : Race condition that being too lazy to test */
|
/* istanbul ignore next : Race condition that being too lazy to test */
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
@@ -132,7 +135,7 @@ function loadFiles(opts, callback) {
|
|||||||
ret.push({
|
ret.push({
|
||||||
path: path,
|
path: path,
|
||||||
name: name,
|
name: name,
|
||||||
source: data
|
source: data,
|
||||||
});
|
});
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
@@ -140,7 +143,7 @@ function loadFiles(opts, callback) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
function(err) {
|
function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
callback(err);
|
callback(err);
|
||||||
} else {
|
} else {
|
||||||
@@ -150,7 +153,7 @@ function loadFiles(opts, callback) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.cli = function(opts) {
|
module.exports.cli = function (opts) {
|
||||||
if (opts.version) {
|
if (opts.version) {
|
||||||
console.log(Handlebars.VERSION);
|
console.log(Handlebars.VERSION);
|
||||||
return;
|
return;
|
||||||
@@ -206,6 +209,8 @@ module.exports.cli = function(opts) {
|
|||||||
);
|
);
|
||||||
} else if (opts.commonjs) {
|
} else if (opts.commonjs) {
|
||||||
output.add('var Handlebars = require("' + opts.commonjs + '");');
|
output.add('var Handlebars = require("' + opts.commonjs + '");');
|
||||||
|
} else if (opts.esm) {
|
||||||
|
output.add(`import handlebars from "${opts.esm}";`);
|
||||||
} else {
|
} else {
|
||||||
output.add('(function() {\n');
|
output.add('(function() {\n');
|
||||||
}
|
}
|
||||||
@@ -219,10 +224,10 @@ module.exports.cli = function(opts) {
|
|||||||
output.add('{};\n');
|
output.add('{};\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
opts.templates.forEach(function(template) {
|
opts.templates.forEach(function (template) {
|
||||||
let options = {
|
let options = {
|
||||||
knownHelpers: known,
|
knownHelpers: known,
|
||||||
knownHelpersOnly: opts.o
|
knownHelpersOnly: opts.o,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (opts.map) {
|
if (opts.map) {
|
||||||
@@ -259,7 +264,7 @@ module.exports.cli = function(opts) {
|
|||||||
template.name,
|
template.name,
|
||||||
"'] = template(",
|
"'] = template(",
|
||||||
precompiled,
|
precompiled,
|
||||||
');\n'
|
');\n',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -335,7 +340,7 @@ function minify(output, sourceMapFile) {
|
|||||||
return require('uglify-js').minify(output.code, {
|
return require('uglify-js').minify(output.code, {
|
||||||
sourceMap: {
|
sourceMap: {
|
||||||
content: output.map,
|
content: output.map,
|
||||||
url: sourceMapFile
|
url: sourceMapFile,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -4,6 +4,6 @@ module.exports = {
|
|||||||
lines: 100,
|
lines: 100,
|
||||||
functions: 100,
|
functions: 100,
|
||||||
statements: 100,
|
statements: 100,
|
||||||
exclude: ['**/spec/**', '**/handlebars/compiler/parser.js'],
|
exclude: ['**/spec/**'],
|
||||||
reporter: 'html'
|
reporter: 'html',
|
||||||
};
|
};
|
||||||
|
|||||||
Generated
+5480
-6546
File diff suppressed because it is too large
Load Diff
+32
-30
@@ -1,9 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "handlebars",
|
"name": "handlebars",
|
||||||
"barename": "handlebars",
|
"version": "5.0.0-alpha.1",
|
||||||
"version": "4.7.7",
|
|
||||||
"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": "https://www.handlebarsjs.com/",
|
"homepage": "https://handlebarsjs.com/",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"handlebars",
|
"handlebars",
|
||||||
"mustache",
|
"mustache",
|
||||||
@@ -16,21 +15,22 @@
|
|||||||
},
|
},
|
||||||
"author": "Yehuda Katz",
|
"author": "Yehuda Katz",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"readmeFilename": "README.md",
|
"readmeFilename": "README.markdown",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.4.7"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"minimist": "^1.2.5",
|
"@handlebars/parser": "^2.1.0",
|
||||||
"neo-async": "^2.6.2",
|
"neo-async": "^2.6.2",
|
||||||
"source-map": "^0.6.1",
|
"source-map": "^0.6.1",
|
||||||
"wordwrap": "^1.0.0"
|
"yargs": "^16.2.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"peerDependencies": {
|
||||||
"uglify-js": "^3.1.4"
|
"uglify-js": "^3.1.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.17.1",
|
"@definitelytyped/dtslint": "^0.0.100",
|
||||||
|
"@playwright/test": "^1.36.1",
|
||||||
"aws-sdk": "^2.1.49",
|
"aws-sdk": "^2.1.49",
|
||||||
"babel-loader": "^5.0.0",
|
"babel-loader": "^5.0.0",
|
||||||
"babel-runtime": "^5.1.10",
|
"babel-runtime": "^5.1.10",
|
||||||
@@ -39,70 +39,66 @@
|
|||||||
"chai-diff": "^1.0.1",
|
"chai-diff": "^1.0.1",
|
||||||
"concurrently": "^5.0.0",
|
"concurrently": "^5.0.0",
|
||||||
"dirty-chai": "^2.0.1",
|
"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",
|
"eslint": "^8.25.0",
|
||||||
"eslint": "^6.7.2",
|
"eslint-config-prettier": "^8.9.0",
|
||||||
"eslint-config-prettier": "^6.7.0",
|
"eslint-plugin-compat": "4.0",
|
||||||
"eslint-plugin-compat": "^3.13.0",
|
|
||||||
"eslint-plugin-es5": "^1.4.1",
|
|
||||||
"fs-extra": "^8.1.0",
|
"fs-extra": "^8.1.0",
|
||||||
"grunt": "^1.0.4",
|
"grunt": "^1.0.4",
|
||||||
"grunt-babel": "^5.0.0",
|
"grunt-babel": "^5.0.0",
|
||||||
"grunt-bg-shell": "^2.3.3",
|
|
||||||
"grunt-cli": "^1",
|
"grunt-cli": "^1",
|
||||||
"grunt-contrib-clean": "^1",
|
"grunt-contrib-clean": "^1",
|
||||||
"grunt-contrib-concat": "^1",
|
"grunt-contrib-concat": "^1",
|
||||||
"grunt-contrib-connect": "^1",
|
"grunt-contrib-connect": "^3.0.0",
|
||||||
"grunt-contrib-copy": "^1",
|
"grunt-contrib-copy": "^1",
|
||||||
"grunt-contrib-requirejs": "^1",
|
"grunt-contrib-requirejs": "^1",
|
||||||
"grunt-contrib-uglify": "^1",
|
"grunt-contrib-uglify": "^1",
|
||||||
"grunt-contrib-watch": "^1.1.0",
|
"grunt-contrib-watch": "^1.1.0",
|
||||||
|
"grunt-shell": "^4.0.0",
|
||||||
"grunt-webpack": "^1.0.8",
|
"grunt-webpack": "^1.0.8",
|
||||||
"husky": "^3.1.0",
|
"husky": "^3.1.0",
|
||||||
"jison": "~0.3.0",
|
|
||||||
"lint-staged": "^9.5.0",
|
"lint-staged": "^9.5.0",
|
||||||
"mocha": "^5",
|
"mocha": "^5",
|
||||||
"mock-stdin": "^0.3.0",
|
"mock-stdin": "^0.3.0",
|
||||||
"mustache": "^2.1.3",
|
"mustache": "^2.1.3",
|
||||||
"nyc": "^14.1.1",
|
"nyc": "^14.1.1",
|
||||||
"prettier": "^1.19.1",
|
"prettier": "^3.0.0",
|
||||||
"semver": "^5.0.1",
|
"semver": "^5.0.1",
|
||||||
"sinon": "^7.5.0",
|
"sinon": "^7.5.0",
|
||||||
"typescript": "^3.4.3",
|
"typescript": "^3.4.3",
|
||||||
|
"uglify-js": "^3.1.4",
|
||||||
"underscore": "^1.5.1",
|
"underscore": "^1.5.1",
|
||||||
"webpack": "^1.12.6",
|
"webpack": "^1.12.6",
|
||||||
"webpack-dev-server": "^1.12.1"
|
"webpack-dev-server": "^1.12.1"
|
||||||
},
|
},
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"types": "types/index.d.ts",
|
"types": "types/index.d.ts",
|
||||||
"browser": {
|
"browser": "./dist/cjs/handlebars.js",
|
||||||
".": "./dist/cjs/handlebars.js",
|
|
||||||
"./runtime": "./dist/cjs/handlebars.runtime.js"
|
|
||||||
},
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"handlebars": "bin/handlebars"
|
"handlebars": "bin/handlebars.js"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "grunt build",
|
"build": "grunt build",
|
||||||
"format": "prettier --write '**/*.js' && eslint --fix .",
|
"release": "grunt release",
|
||||||
|
"publish:aws": "npm run test:tasks && grunt && grunt publish-to-aws",
|
||||||
|
"format": "prettier --write '**/*.{js,css,json,md}' && eslint --fix .",
|
||||||
"lint": "npm run lint:eslint && npm run lint:prettier && npm run lint:types",
|
"lint": "npm run lint:eslint && npm run lint:prettier && npm run lint:types",
|
||||||
"lint:eslint": "eslint --max-warnings 0 .",
|
"lint:eslint": "eslint --max-warnings 0 .",
|
||||||
"lint:prettier": "prettier --check '**/*.js'",
|
"lint:prettier": "prettier --check '**/*.js'",
|
||||||
"lint:types": "dtslint types",
|
"lint:types": "dtslint types",
|
||||||
"test": "npm run test:mocha",
|
"test": "npm run test:mocha",
|
||||||
"test:mocha": "grunt build && grunt test",
|
"test:mocha": "grunt build && grunt test",
|
||||||
"test:browser": "playwright test --config tests/browser/playwright.config.js tests/browser/spec.js",
|
"test:tasks": "mocha tasks/tests/",
|
||||||
|
"test:browser": "playwright test --config tests/browser/playwright.config.js",
|
||||||
"test:integration": "grunt integration-tests",
|
"test:integration": "grunt integration-tests",
|
||||||
"test:serve": "grunt connect:server:keepalive",
|
"test:serve": "grunt connect:server:keepalive",
|
||||||
"extensive-tests-and-publish-to-aws": "npx mocha tasks/tests/ && grunt --stack extensive-tests-and-publish-to-aws",
|
|
||||||
"--- combined tasks ---": "",
|
"--- combined tasks ---": "",
|
||||||
"check-before-pull-request": "concurrently --kill-others-on-fail npm:lint npm:test"
|
"check-before-pull-request": "concurrently --kill-others-on-fail npm:lint npm:test"
|
||||||
},
|
},
|
||||||
"jspm": {
|
"jspm": {
|
||||||
"main": "handlebars",
|
"main": "handlebars",
|
||||||
"directories": {
|
"directories": {
|
||||||
"lib": "dist/amd"
|
"lib": "dist/cjs"
|
||||||
},
|
},
|
||||||
"buildConfig": {
|
"buildConfig": {
|
||||||
"minify": true
|
"minify": true
|
||||||
@@ -111,7 +107,6 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"bin",
|
"bin",
|
||||||
"dist/*.js",
|
"dist/*.js",
|
||||||
"dist/amd/**/*.js",
|
|
||||||
"dist/cjs/**/*.js",
|
"dist/cjs/**/*.js",
|
||||||
"lib",
|
"lib",
|
||||||
"release-notes.md",
|
"release-notes.md",
|
||||||
@@ -119,13 +114,20 @@
|
|||||||
"types/*.d.ts",
|
"types/*.d.ts",
|
||||||
"runtime.d.ts"
|
"runtime.d.ts"
|
||||||
],
|
],
|
||||||
|
"browserslist": [
|
||||||
|
"last 2 versions",
|
||||||
|
"Firefox ESR",
|
||||||
|
"not dead",
|
||||||
|
"not IE 11",
|
||||||
|
"maintained node versions"
|
||||||
|
],
|
||||||
"husky": {
|
"husky": {
|
||||||
"hooks": {
|
"hooks": {
|
||||||
"pre-commit": "lint-staged"
|
"pre-commit": "lint-staged"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.{js,css,json}": [
|
"*.{js,css,json,md}": [
|
||||||
"prettier --write",
|
"prettier --write",
|
||||||
"git add"
|
"git add"
|
||||||
],
|
],
|
||||||
|
|||||||
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
tabWidth: 2,
|
tabWidth: 2,
|
||||||
semi: true,
|
semi: true,
|
||||||
singleQuote: true
|
singleQuote: true,
|
||||||
|
trailingComma: 'es5',
|
||||||
};
|
};
|
||||||
|
|||||||
+286
-274
File diff suppressed because it is too large
Load Diff
@@ -1,47 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": [
|
|
||||||
"../.eslintrc.js",
|
|
||||||
"plugin:es5/no-es2015",
|
|
||||||
"prettier"
|
|
||||||
],
|
|
||||||
"plugins": [
|
|
||||||
"es5"
|
|
||||||
],
|
|
||||||
"globals": {
|
|
||||||
"CompilerContext": true,
|
|
||||||
"Handlebars": true,
|
|
||||||
"handlebarsEnv": true,
|
|
||||||
"shouldCompileTo": true,
|
|
||||||
"shouldCompileToWithPartials": true,
|
|
||||||
"shouldThrow": true,
|
|
||||||
"expectTemplate": true,
|
|
||||||
"compileWithPartials": true,
|
|
||||||
"console": true,
|
|
||||||
"require": true,
|
|
||||||
"suite": true,
|
|
||||||
"equal": true,
|
|
||||||
"equals": true,
|
|
||||||
"test": true,
|
|
||||||
"testBoth": true,
|
|
||||||
"raises": true,
|
|
||||||
"deepEqual": true,
|
|
||||||
"start": true,
|
|
||||||
"stop": true,
|
|
||||||
"ok": true,
|
|
||||||
"sinon": true,
|
|
||||||
"strictEqual": true,
|
|
||||||
"define": true,
|
|
||||||
"expect": true,
|
|
||||||
"chai": true
|
|
||||||
},
|
|
||||||
"env": {
|
|
||||||
"mocha": true
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
// Disabling for tests, for now.
|
|
||||||
"no-path-concat": "off",
|
|
||||||
|
|
||||||
"no-var": "off",
|
|
||||||
"dot-notation": "off"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
module.exports = {
|
||||||
|
globals: {
|
||||||
|
CompilerContext: true,
|
||||||
|
Handlebars: true,
|
||||||
|
handlebarsEnv: true,
|
||||||
|
shouldCompileTo: true,
|
||||||
|
shouldCompileToWithPartials: true,
|
||||||
|
shouldThrow: true,
|
||||||
|
expectTemplate: true,
|
||||||
|
compileWithPartials: true,
|
||||||
|
suite: true,
|
||||||
|
equal: true,
|
||||||
|
equals: true,
|
||||||
|
test: true,
|
||||||
|
testBoth: true,
|
||||||
|
raises: true,
|
||||||
|
deepEqual: true,
|
||||||
|
start: true,
|
||||||
|
stop: true,
|
||||||
|
ok: true,
|
||||||
|
sinon: true,
|
||||||
|
strictEqual: true,
|
||||||
|
define: true,
|
||||||
|
expect: true,
|
||||||
|
chai: true,
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
mocha: true,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
// Disabling for tests, for now.
|
||||||
|
'no-path-concat': 'off',
|
||||||
|
|
||||||
|
'no-var': 'off',
|
||||||
|
'dot-notation': 'off',
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Mocha</title>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="/node_modules/mocha/mocha.css" />
|
|
||||||
<style>
|
|
||||||
.headless .suite > h1,
|
|
||||||
.headless .test.pass {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// Show only errors in "headless", non-interactive mode.
|
|
||||||
if (/headless=true/.test(location.href)) {
|
|
||||||
document.documentElement.className = 'headless';
|
|
||||||
}
|
|
||||||
</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>
|
|
||||||
window.expect = chai.expect;
|
|
||||||
mocha.setup('bdd');
|
|
||||||
</script>
|
|
||||||
<script src="/spec/vendor/json2.js"></script>
|
|
||||||
<script src="/spec/vendor/require.js"></script>
|
|
||||||
|
|
||||||
<script src="/spec/env/common.js"></script>
|
|
||||||
<script>
|
|
||||||
var requireFailure;
|
|
||||||
|
|
||||||
requirejs.config({
|
|
||||||
paths: {
|
|
||||||
'handlebars.runtime': '/dist/handlebars.runtime.amd'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
requirejs.onError = function (err) {
|
|
||||||
requireFailure = err;
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<script>
|
|
||||||
onload = function(){
|
|
||||||
require(['handlebars.runtime'], function(Handlebars) {
|
|
||||||
describe('runtime', function() {
|
|
||||||
it('should load', function() {
|
|
||||||
equal(!!Handlebars['default'].template, true);
|
|
||||||
equal(!!Handlebars['default'].VERSION, true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
mocha.globals(['mochaResults'])
|
|
||||||
// The test harness leaks under FF. We should have decent global leak coverage from other tests
|
|
||||||
if (!navigator.userAgent.match(/Firefox\/([\d.]+)/)) {
|
|
||||||
mocha.checkLeaks();
|
|
||||||
}
|
|
||||||
var runner = mocha.run();
|
|
||||||
|
|
||||||
// Reporting to test-runner
|
|
||||||
var failedTests = [];
|
|
||||||
runner.on('end', function(){
|
|
||||||
window.mochaResults = runner.stats;
|
|
||||||
window.mochaResults.reports = failedTests;
|
|
||||||
});
|
|
||||||
|
|
||||||
runner.on('fail', logFailure);
|
|
||||||
|
|
||||||
// Inject any require initilizer failures into the first test so they are properly
|
|
||||||
// reported.
|
|
||||||
if (requireFailure) {
|
|
||||||
runner.on('hook end', function(hook){
|
|
||||||
if (requireFailure) {
|
|
||||||
runner.uncaught(requireFailure);
|
|
||||||
requireFailure = undefined;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function logFailure(test, err){
|
|
||||||
|
|
||||||
var flattenTitles = function(test){
|
|
||||||
var titles = [];
|
|
||||||
while (test.parent.title){
|
|
||||||
titles.push(test.parent.title);
|
|
||||||
test = test.parent;
|
|
||||||
}
|
|
||||||
return titles.reverse();
|
|
||||||
};
|
|
||||||
|
|
||||||
failedTests.push({
|
|
||||||
name: test.title,
|
|
||||||
result: false,
|
|
||||||
message: err.message,
|
|
||||||
stack: err.stack,
|
|
||||||
titles: flattenTitles(test)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="mocha"></div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
-128
@@ -1,128 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Mocha</title>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="/node_modules/mocha/mocha.css" />
|
|
||||||
<style>
|
|
||||||
.headless .suite > h1,
|
|
||||||
.headless .test.pass {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// Show only errors in "headless", non-interactive mode.
|
|
||||||
if (/headless=true/.test(location.href)) {
|
|
||||||
document.documentElement.className = 'headless';
|
|
||||||
}
|
|
||||||
</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>
|
|
||||||
window.expect = chai.expect;
|
|
||||||
mocha.setup('bdd');
|
|
||||||
</script>
|
|
||||||
<script src="/spec/vendor/json2.js"></script>
|
|
||||||
<script src="/spec/vendor/require.js"></script>
|
|
||||||
|
|
||||||
<script src="/spec/env/common.js"></script>
|
|
||||||
<script>
|
|
||||||
var requireFailure;
|
|
||||||
|
|
||||||
requirejs.config({
|
|
||||||
paths: {
|
|
||||||
handlebars: '/dist/handlebars.amd',
|
|
||||||
tests: '/tmp/tests'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
requirejs.onError = function (err) {
|
|
||||||
requireFailure = err;
|
|
||||||
};
|
|
||||||
|
|
||||||
var CompilerContext = {
|
|
||||||
compile: function(template, options) {
|
|
||||||
var templateSpec = handlebarsEnv.precompile(template, options);
|
|
||||||
return handlebarsEnv.template(safeEval(templateSpec));
|
|
||||||
},
|
|
||||||
compileWithPartial: function(template, options) {
|
|
||||||
return handlebarsEnv.compile(template, options);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function safeEval(templateSpec) {
|
|
||||||
try {
|
|
||||||
var ret;
|
|
||||||
eval('ret = ' + templateSpec);
|
|
||||||
return ret;
|
|
||||||
} catch (err) {
|
|
||||||
console.error(templateSpec);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<script>
|
|
||||||
onload = function(){
|
|
||||||
|
|
||||||
require(['handlebars'], function(Handlebars) {
|
|
||||||
window.Handlebars = Handlebars['default'];
|
|
||||||
|
|
||||||
require(['tests'], function(Handlebars) {
|
|
||||||
mocha.globals(['mochaResults'])
|
|
||||||
// The test harness leaks under FF. We should have decent global leak coverage from other tests
|
|
||||||
if (!navigator.userAgent.match(/Firefox\/([\d.]+)/)) {
|
|
||||||
mocha.checkLeaks();
|
|
||||||
}
|
|
||||||
var runner = mocha.run();
|
|
||||||
|
|
||||||
// Reporting to test-runner
|
|
||||||
var failedTests = [];
|
|
||||||
runner.on('end', function(){
|
|
||||||
window.mochaResults = runner.stats;
|
|
||||||
window.mochaResults.reports = failedTests;
|
|
||||||
});
|
|
||||||
|
|
||||||
runner.on('fail', logFailure);
|
|
||||||
|
|
||||||
// Inject any require initilizer failures into the first test so they are properly
|
|
||||||
// reported.
|
|
||||||
if (requireFailure) {
|
|
||||||
runner.on('hook end', function(hook){
|
|
||||||
if (requireFailure) {
|
|
||||||
runner.uncaught(requireFailure);
|
|
||||||
requireFailure = undefined;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function logFailure(test, err){
|
|
||||||
function flattenTitles(test){
|
|
||||||
var titles = [];
|
|
||||||
while (test.parent.title){
|
|
||||||
titles.push(test.parent.title);
|
|
||||||
test = test.parent;
|
|
||||||
}
|
|
||||||
return titles.reverse();
|
|
||||||
}
|
|
||||||
|
|
||||||
failedTests.push({
|
|
||||||
name: test.title,
|
|
||||||
result: false,
|
|
||||||
message: err.message,
|
|
||||||
stack: err.stack,
|
|
||||||
titles: flattenTitles(test)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="mocha"></div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
+25
-272
@@ -1,14 +1,14 @@
|
|||||||
describe('ast', function() {
|
describe('ast', function () {
|
||||||
if (!Handlebars.AST) {
|
if (!Handlebars.AST) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var AST = Handlebars.AST;
|
var AST = Handlebars.AST;
|
||||||
|
|
||||||
describe('BlockStatement', function() {
|
describe('BlockStatement', function () {
|
||||||
it('should throw on mustache mismatch', function() {
|
it('should throw on mustache mismatch', function () {
|
||||||
shouldThrow(
|
shouldThrow(
|
||||||
function() {
|
function () {
|
||||||
handlebarsEnv.parse('\n {{#foo}}{{/bar}}');
|
handlebarsEnv.parse('\n {{#foo}}{{/bar}}');
|
||||||
},
|
},
|
||||||
Handlebars.Exception,
|
Handlebars.Exception,
|
||||||
@@ -17,14 +17,14 @@ describe('ast', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('helpers', function() {
|
describe('helpers', function () {
|
||||||
describe('#helperExpression', function() {
|
describe('#helperExpression', function () {
|
||||||
it('should handle mustache statements', function() {
|
it('should handle mustache statements', function () {
|
||||||
equals(
|
equals(
|
||||||
AST.helpers.helperExpression({
|
AST.helpers.helperExpression({
|
||||||
type: 'MustacheStatement',
|
type: 'MustacheStatement',
|
||||||
params: [],
|
params: [],
|
||||||
hash: undefined
|
hash: undefined,
|
||||||
}),
|
}),
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
@@ -32,7 +32,7 @@ describe('ast', function() {
|
|||||||
AST.helpers.helperExpression({
|
AST.helpers.helperExpression({
|
||||||
type: 'MustacheStatement',
|
type: 'MustacheStatement',
|
||||||
params: [1],
|
params: [1],
|
||||||
hash: undefined
|
hash: undefined,
|
||||||
}),
|
}),
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
@@ -40,17 +40,17 @@ describe('ast', function() {
|
|||||||
AST.helpers.helperExpression({
|
AST.helpers.helperExpression({
|
||||||
type: 'MustacheStatement',
|
type: 'MustacheStatement',
|
||||||
params: [],
|
params: [],
|
||||||
hash: {}
|
hash: {},
|
||||||
}),
|
}),
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should handle block statements', function() {
|
it('should handle block statements', function () {
|
||||||
equals(
|
equals(
|
||||||
AST.helpers.helperExpression({
|
AST.helpers.helperExpression({
|
||||||
type: 'BlockStatement',
|
type: 'BlockStatement',
|
||||||
params: [],
|
params: [],
|
||||||
hash: undefined
|
hash: undefined,
|
||||||
}),
|
}),
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
@@ -58,7 +58,7 @@ describe('ast', function() {
|
|||||||
AST.helpers.helperExpression({
|
AST.helpers.helperExpression({
|
||||||
type: 'BlockStatement',
|
type: 'BlockStatement',
|
||||||
params: [1],
|
params: [1],
|
||||||
hash: undefined
|
hash: undefined,
|
||||||
}),
|
}),
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
@@ -66,15 +66,15 @@ describe('ast', function() {
|
|||||||
AST.helpers.helperExpression({
|
AST.helpers.helperExpression({
|
||||||
type: 'BlockStatement',
|
type: 'BlockStatement',
|
||||||
params: [],
|
params: [],
|
||||||
hash: {}
|
hash: {},
|
||||||
}),
|
}),
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should handle subexpressions', function() {
|
it('should handle subexpressions', function () {
|
||||||
equals(AST.helpers.helperExpression({ type: 'SubExpression' }), true);
|
equals(AST.helpers.helperExpression({ type: 'SubExpression' }), true);
|
||||||
});
|
});
|
||||||
it('should work with non-helper nodes', function() {
|
it('should work with non-helper nodes', function () {
|
||||||
equals(AST.helpers.helperExpression({ type: 'Program' }), false);
|
equals(AST.helpers.helperExpression({ type: 'Program' }), false);
|
||||||
|
|
||||||
equals(
|
equals(
|
||||||
@@ -107,7 +107,7 @@ describe('ast', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Line Numbers', function() {
|
describe('Line Numbers', function () {
|
||||||
var ast, body;
|
var ast, body;
|
||||||
|
|
||||||
function testColumns(node, firstLine, lastLine, firstColumn, lastColumn) {
|
function testColumns(node, firstLine, lastLine, firstColumn, lastColumn) {
|
||||||
@@ -134,45 +134,45 @@ describe('ast', function() {
|
|||||||
/* eslint-enable no-multi-spaces */
|
/* eslint-enable no-multi-spaces */
|
||||||
body = ast.body;
|
body = ast.body;
|
||||||
|
|
||||||
it('gets ContentNode line numbers', function() {
|
it('gets ContentNode line numbers', function () {
|
||||||
var contentNode = body[0];
|
var contentNode = body[0];
|
||||||
testColumns(contentNode, 1, 1, 0, 7);
|
testColumns(contentNode, 1, 1, 0, 7);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('gets MustacheStatement line numbers', function() {
|
it('gets MustacheStatement line numbers', function () {
|
||||||
var mustacheNode = body[1];
|
var mustacheNode = body[1];
|
||||||
testColumns(mustacheNode, 1, 1, 7, 21);
|
testColumns(mustacheNode, 1, 1, 7, 21);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('gets line numbers correct when newlines appear', function() {
|
it('gets line numbers correct when newlines appear', function () {
|
||||||
testColumns(body[2], 1, 2, 21, 8);
|
testColumns(body[2], 1, 2, 21, 8);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('gets MustacheStatement line numbers correct across newlines', function() {
|
it('gets MustacheStatement line numbers correct across newlines', function () {
|
||||||
var secondMustacheStatement = body[3];
|
var secondMustacheStatement = body[3];
|
||||||
testColumns(secondMustacheStatement, 2, 2, 8, 22);
|
testColumns(secondMustacheStatement, 2, 2, 8, 22);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('gets the block helper information correct', function() {
|
it('gets the block helper information correct', function () {
|
||||||
var blockHelperNode = body[5];
|
var blockHelperNode = body[5];
|
||||||
testColumns(blockHelperNode, 3, 7, 8, 23);
|
testColumns(blockHelperNode, 3, 7, 8, 23);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('correctly records the line numbers the program of a block helper', function() {
|
it('correctly records the line numbers the program of a block helper', function () {
|
||||||
var blockHelperNode = body[5],
|
var blockHelperNode = body[5],
|
||||||
program = blockHelperNode.program;
|
program = blockHelperNode.program;
|
||||||
|
|
||||||
testColumns(program, 3, 5, 31, 5);
|
testColumns(program, 3, 5, 31, 5);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('correctly records the line numbers of an inverse of a block helper', function() {
|
it('correctly records the line numbers of an inverse of a block helper', function () {
|
||||||
var blockHelperNode = body[5],
|
var blockHelperNode = body[5],
|
||||||
inverse = blockHelperNode.inverse;
|
inverse = blockHelperNode.inverse;
|
||||||
|
|
||||||
testColumns(inverse, 5, 7, 13, 0);
|
testColumns(inverse, 5, 7, 13, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('correctly records the line number of chained inverses', function() {
|
it('correctly records the line number of chained inverses', function () {
|
||||||
var chainInverseNode = body[7];
|
var chainInverseNode = body[7];
|
||||||
|
|
||||||
testColumns(chainInverseNode.program, 8, 9, 9, 0);
|
testColumns(chainInverseNode.program, 8, 9, 9, 0);
|
||||||
@@ -181,251 +181,4 @@ describe('ast', function() {
|
|||||||
testColumns(chainInverseNode.inverse.body[0].inverse, 10, 11, 8, 0);
|
testColumns(chainInverseNode.inverse.body[0].inverse, 10, 11, 8, 0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
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('mustache', function() {
|
|
||||||
it('does not mark mustaches as standalone', function() {
|
|
||||||
var ast = Handlebars.parse(' {{comment}} ');
|
|
||||||
equals(!!ast.body[0].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() {
|
|
||||||
it('marks block mustaches as standalone', function() {
|
|
||||||
var ast = Handlebars.parse(
|
|
||||||
' {{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} '
|
|
||||||
),
|
|
||||||
block = ast.body[1];
|
|
||||||
|
|
||||||
equals(ast.body[0].value, '');
|
|
||||||
|
|
||||||
equals(block.program.body[0].value, 'foo\n');
|
|
||||||
equals(block.inverse.body[0].value, ' bar \n');
|
|
||||||
|
|
||||||
equals(ast.body[2].value, '');
|
|
||||||
});
|
|
||||||
it('marks initial block mustaches as standalone', function() {
|
|
||||||
var ast = Handlebars.parse('{{# comment}} \nfoo\n {{/comment}}'),
|
|
||||||
block = ast.body[0];
|
|
||||||
|
|
||||||
equals(block.program.body[0].value, 'foo\n');
|
|
||||||
});
|
|
||||||
it('marks mustaches with children as standalone', function() {
|
|
||||||
var ast = Handlebars.parse('{{# comment}} \n{{foo}}\n {{/comment}}'),
|
|
||||||
block = ast.body[0];
|
|
||||||
|
|
||||||
equals(block.program.body[0].value, '');
|
|
||||||
equals(block.program.body[1].path.original, 'foo');
|
|
||||||
equals(block.program.body[2].value, '\n');
|
|
||||||
});
|
|
||||||
it('marks nested block mustaches as standalone', function() {
|
|
||||||
var ast = Handlebars.parse(
|
|
||||||
'{{#foo}} \n{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} \n{{/foo}}'
|
|
||||||
),
|
|
||||||
body = ast.body[0].program.body,
|
|
||||||
block = body[1];
|
|
||||||
|
|
||||||
equals(body[0].value, '');
|
|
||||||
|
|
||||||
equals(block.program.body[0].value, 'foo\n');
|
|
||||||
equals(block.inverse.body[0].value, ' bar \n');
|
|
||||||
|
|
||||||
equals(body[0].value, '');
|
|
||||||
});
|
|
||||||
it('does not mark nested block mustaches as standalone', function() {
|
|
||||||
var ast = Handlebars.parse(
|
|
||||||
'{{#foo}} {{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} {{/foo}}'
|
|
||||||
),
|
|
||||||
body = ast.body[0].program.body,
|
|
||||||
block = body[1];
|
|
||||||
|
|
||||||
equals(body[0].omit, undefined);
|
|
||||||
|
|
||||||
equals(block.program.body[0].value, ' \nfoo\n');
|
|
||||||
equals(block.inverse.body[0].value, ' bar \n ');
|
|
||||||
|
|
||||||
equals(body[0].omit, undefined);
|
|
||||||
});
|
|
||||||
it('does not mark nested initial block mustaches as standalone', function() {
|
|
||||||
var ast = Handlebars.parse(
|
|
||||||
'{{#foo}}{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}}{{/foo}}'
|
|
||||||
),
|
|
||||||
body = ast.body[0].program.body,
|
|
||||||
block = body[0];
|
|
||||||
|
|
||||||
equals(block.program.body[0].value, ' \nfoo\n');
|
|
||||||
equals(block.inverse.body[0].value, ' bar \n ');
|
|
||||||
|
|
||||||
equals(body[0].omit, undefined);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('marks column 0 block mustaches as standalone', function() {
|
|
||||||
var ast = Handlebars.parse(
|
|
||||||
'test\n{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} '
|
|
||||||
),
|
|
||||||
block = ast.body[1];
|
|
||||||
|
|
||||||
equals(ast.body[0].omit, undefined);
|
|
||||||
|
|
||||||
equals(block.program.body[0].value, 'foo\n');
|
|
||||||
equals(block.inverse.body[0].value, ' bar \n');
|
|
||||||
|
|
||||||
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() {
|
|
||||||
it('marks partial as standalone', function() {
|
|
||||||
var ast = Handlebars.parse('{{> partial }} ');
|
|
||||||
equals(ast.body[1].value, '');
|
|
||||||
});
|
|
||||||
it('marks indented partial as standalone', function() {
|
|
||||||
var ast = Handlebars.parse(' {{> partial }} ');
|
|
||||||
equals(ast.body[0].value, '');
|
|
||||||
equals(ast.body[1].indent, ' ');
|
|
||||||
equals(ast.body[2].value, '');
|
|
||||||
});
|
|
||||||
it('marks those around content as not standalone', function() {
|
|
||||||
var ast = Handlebars.parse('a{{> partial }}');
|
|
||||||
equals(ast.body[0].omit, undefined);
|
|
||||||
|
|
||||||
ast = Handlebars.parse('{{> partial }}a');
|
|
||||||
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() {
|
|
||||||
it('marks comment as standalone', function() {
|
|
||||||
var ast = Handlebars.parse('{{! comment }} ');
|
|
||||||
equals(ast.body[1].value, '');
|
|
||||||
});
|
|
||||||
it('marks indented comment as standalone', function() {
|
|
||||||
var ast = Handlebars.parse(' {{! comment }} ');
|
|
||||||
equals(ast.body[0].value, '');
|
|
||||||
equals(ast.body[2].value, '');
|
|
||||||
});
|
|
||||||
it('marks those around content as not standalone', function() {
|
|
||||||
var ast = Handlebars.parse('a{{! comment }}');
|
|
||||||
equals(ast.body[0].omit, undefined);
|
|
||||||
|
|
||||||
ast = Handlebars.parse('{{! comment }}a');
|
|
||||||
equals(ast.body[1].omit, undefined);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
+102
-124
@@ -1,17 +1,15 @@
|
|||||||
global.handlebarsEnv = null;
|
global.handlebarsEnv = null;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
global.handlebarsEnv = Handlebars.create();
|
global.handlebarsEnv = Handlebars.create();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('basic context', function() {
|
describe('basic context', function () {
|
||||||
it('most basic', function() {
|
it('most basic', function () {
|
||||||
expectTemplate('{{foo}}')
|
expectTemplate('{{foo}}').withInput({ foo: 'foo' }).toCompileTo('foo');
|
||||||
.withInput({ foo: 'foo' })
|
|
||||||
.toCompileTo('foo');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('escaping', function() {
|
it('escaping', function () {
|
||||||
expectTemplate('\\{{foo}}')
|
expectTemplate('\\{{foo}}')
|
||||||
.withInput({ foo: 'food' })
|
.withInput({ foo: 'food' })
|
||||||
.toCompileTo('{{foo}}');
|
.toCompileTo('{{foo}}');
|
||||||
@@ -33,23 +31,21 @@ describe('basic context', function() {
|
|||||||
.toCompileTo('\\\\ food');
|
.toCompileTo('\\\\ food');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('compiling with a basic context', function() {
|
it('compiling with a basic context', function () {
|
||||||
expectTemplate('Goodbye\n{{cruel}}\n{{world}}!')
|
expectTemplate('Goodbye\n{{cruel}}\n{{world}}!')
|
||||||
.withInput({
|
.withInput({
|
||||||
cruel: 'cruel',
|
cruel: 'cruel',
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('It works if all the required keys are provided')
|
.withMessage('It works if all the required keys are provided')
|
||||||
.toCompileTo('Goodbye\ncruel\nworld!');
|
.toCompileTo('Goodbye\ncruel\nworld!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('compiling with a string context', function() {
|
it('compiling with a string context', function () {
|
||||||
expectTemplate('{{.}}{{length}}')
|
expectTemplate('{{.}}{{length}}').withInput('bye').toCompileTo('bye3');
|
||||||
.withInput('bye')
|
|
||||||
.toCompileTo('bye3');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('compiling with an undefined context', function() {
|
it('compiling with an undefined context', function () {
|
||||||
expectTemplate('Goodbye\n{{cruel}}\n{{world.bar}}!')
|
expectTemplate('Goodbye\n{{cruel}}\n{{world.bar}}!')
|
||||||
.withInput(undefined)
|
.withInput(undefined)
|
||||||
.toCompileTo('Goodbye\n\n!');
|
.toCompileTo('Goodbye\n\n!');
|
||||||
@@ -59,11 +55,11 @@ describe('basic context', function() {
|
|||||||
.toCompileTo('Goodbye');
|
.toCompileTo('Goodbye');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('comments', function() {
|
it('comments', function () {
|
||||||
expectTemplate('{{! Goodbye}}Goodbye\n{{cruel}}\n{{world}}!')
|
expectTemplate('{{! Goodbye}}Goodbye\n{{cruel}}\n{{world}}!')
|
||||||
.withInput({
|
.withInput({
|
||||||
cruel: 'cruel',
|
cruel: 'cruel',
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('comments are ignored')
|
.withMessage('comments are ignored')
|
||||||
.toCompileTo('Goodbye\ncruel\nworld!');
|
.toCompileTo('Goodbye\ncruel\nworld!');
|
||||||
@@ -87,12 +83,12 @@ describe('basic context', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('boolean', function() {
|
it('boolean', function () {
|
||||||
var string = '{{#goodbye}}GOODBYE {{/goodbye}}cruel {{world}}!';
|
var string = '{{#goodbye}}GOODBYE {{/goodbye}}cruel {{world}}!';
|
||||||
expectTemplate(string)
|
expectTemplate(string)
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbye: true,
|
goodbye: true,
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('booleans show the contents when true')
|
.withMessage('booleans show the contents when true')
|
||||||
.toCompileTo('GOODBYE cruel world!');
|
.toCompileTo('GOODBYE cruel world!');
|
||||||
@@ -100,41 +96,37 @@ describe('basic context', function() {
|
|||||||
expectTemplate(string)
|
expectTemplate(string)
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbye: false,
|
goodbye: false,
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('booleans do not show the contents when false')
|
.withMessage('booleans do not show the contents when false')
|
||||||
.toCompileTo('cruel world!');
|
.toCompileTo('cruel world!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('zeros', function() {
|
it('zeros', function () {
|
||||||
expectTemplate('num1: {{num1}}, num2: {{num2}}')
|
expectTemplate('num1: {{num1}}, num2: {{num2}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
num1: 42,
|
num1: 42,
|
||||||
num2: 0
|
num2: 0,
|
||||||
})
|
})
|
||||||
.toCompileTo('num1: 42, num2: 0');
|
.toCompileTo('num1: 42, num2: 0');
|
||||||
|
|
||||||
expectTemplate('num: {{.}}')
|
expectTemplate('num: {{.}}').withInput(0).toCompileTo('num: 0');
|
||||||
.withInput(0)
|
|
||||||
.toCompileTo('num: 0');
|
|
||||||
|
|
||||||
expectTemplate('num: {{num1/num2}}')
|
expectTemplate('num: {{num1/num2}}')
|
||||||
.withInput({ num1: { num2: 0 } })
|
.withInput({ num1: { num2: 0 } })
|
||||||
.toCompileTo('num: 0');
|
.toCompileTo('num: 0');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('false', function() {
|
it('false', function () {
|
||||||
/* eslint-disable no-new-wrappers */
|
/* eslint-disable no-new-wrappers */
|
||||||
expectTemplate('val1: {{val1}}, val2: {{val2}}')
|
expectTemplate('val1: {{val1}}, val2: {{val2}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
val1: false,
|
val1: false,
|
||||||
val2: new Boolean(false)
|
val2: new Boolean(false),
|
||||||
})
|
})
|
||||||
.toCompileTo('val1: false, val2: false');
|
.toCompileTo('val1: false, val2: false');
|
||||||
|
|
||||||
expectTemplate('val: {{.}}')
|
expectTemplate('val: {{.}}').withInput(false).toCompileTo('val: false');
|
||||||
.withInput(false)
|
|
||||||
.toCompileTo('val: false');
|
|
||||||
|
|
||||||
expectTemplate('val: {{val1/val2}}')
|
expectTemplate('val: {{val1/val2}}')
|
||||||
.withInput({ val1: { val2: false } })
|
.withInput({ val1: { val2: false } })
|
||||||
@@ -143,7 +135,7 @@ describe('basic context', function() {
|
|||||||
expectTemplate('val1: {{{val1}}}, val2: {{{val2}}}')
|
expectTemplate('val1: {{{val1}}}, val2: {{{val2}}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
val1: false,
|
val1: false,
|
||||||
val2: new Boolean(false)
|
val2: new Boolean(false),
|
||||||
})
|
})
|
||||||
.toCompileTo('val1: false, val2: false');
|
.toCompileTo('val1: false, val2: false');
|
||||||
|
|
||||||
@@ -153,10 +145,10 @@ describe('basic context', function() {
|
|||||||
/* eslint-enable */
|
/* eslint-enable */
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle undefined and null', function() {
|
it('should handle undefined and null', function () {
|
||||||
expectTemplate('{{awesome undefined null}}')
|
expectTemplate('{{awesome undefined null}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
awesome: function(_undefined, _null, options) {
|
awesome: function (_undefined, _null, options) {
|
||||||
return (
|
return (
|
||||||
(_undefined === undefined) +
|
(_undefined === undefined) +
|
||||||
' ' +
|
' ' +
|
||||||
@@ -164,34 +156,34 @@ describe('basic context', function() {
|
|||||||
' ' +
|
' ' +
|
||||||
typeof options
|
typeof options
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('true true object');
|
.toCompileTo('true true object');
|
||||||
|
|
||||||
expectTemplate('{{undefined}}')
|
expectTemplate('{{undefined}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
undefined: function() {
|
undefined: function () {
|
||||||
return 'undefined!';
|
return 'undefined!';
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('undefined!');
|
.toCompileTo('undefined!');
|
||||||
|
|
||||||
expectTemplate('{{null}}')
|
expectTemplate('{{null}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
null: function() {
|
null: function () {
|
||||||
return 'null!';
|
return 'null!';
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('null!');
|
.toCompileTo('null!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('newlines', function() {
|
it('newlines', function () {
|
||||||
expectTemplate("Alan's\nTest").toCompileTo("Alan's\nTest");
|
expectTemplate("Alan's\nTest").toCompileTo("Alan's\nTest");
|
||||||
|
|
||||||
expectTemplate("Alan's\rTest").toCompileTo("Alan's\rTest");
|
expectTemplate("Alan's\rTest").toCompileTo("Alan's\rTest");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('escaping text', function() {
|
it('escaping text', function () {
|
||||||
expectTemplate("Awesome's")
|
expectTemplate("Awesome's")
|
||||||
.withMessage(
|
.withMessage(
|
||||||
"text is escaped so that it doesn't get caught on single quotes"
|
"text is escaped so that it doesn't get caught on single quotes"
|
||||||
@@ -216,7 +208,7 @@ describe('basic context', function() {
|
|||||||
.toCompileTo(" ' ' ");
|
.toCompileTo(" ' ' ");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('escaping expressions', function() {
|
it('escaping expressions', function () {
|
||||||
expectTemplate('{{{awesome}}}')
|
expectTemplate('{{{awesome}}}')
|
||||||
.withInput({ awesome: "&'\\<>" })
|
.withInput({ awesome: "&'\\<>" })
|
||||||
.withMessage("expressions with 3 handlebars aren't escaped")
|
.withMessage("expressions with 3 handlebars aren't escaped")
|
||||||
@@ -238,140 +230,140 @@ describe('basic context', function() {
|
|||||||
.toCompileTo('Escaped, <b> looks like: &lt;b&gt;');
|
.toCompileTo('Escaped, <b> looks like: &lt;b&gt;');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("functions returning safestrings shouldn't be escaped", function() {
|
it("functions returning safestrings shouldn't be escaped", function () {
|
||||||
expectTemplate('{{awesome}}')
|
expectTemplate('{{awesome}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
awesome: function() {
|
awesome: function () {
|
||||||
return new Handlebars.SafeString("&'\\<>");
|
return new Handlebars.SafeString("&'\\<>");
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.withMessage("functions returning safestrings aren't escaped")
|
.withMessage("functions returning safestrings aren't escaped")
|
||||||
.toCompileTo("&'\\<>");
|
.toCompileTo("&'\\<>");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('functions', function() {
|
it('functions', function () {
|
||||||
expectTemplate('{{awesome}}')
|
expectTemplate('{{awesome}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
awesome: function() {
|
awesome: function () {
|
||||||
return 'Awesome';
|
return 'Awesome';
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.withMessage('functions are called and render their output')
|
.withMessage('functions are called and render their output')
|
||||||
.toCompileTo('Awesome');
|
.toCompileTo('Awesome');
|
||||||
|
|
||||||
expectTemplate('{{awesome}}')
|
expectTemplate('{{awesome}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
awesome: function() {
|
awesome: function () {
|
||||||
return this.more;
|
return this.more;
|
||||||
},
|
},
|
||||||
more: 'More awesome'
|
more: 'More awesome',
|
||||||
})
|
})
|
||||||
.withMessage('functions are bound to the context')
|
.withMessage('functions are bound to the context')
|
||||||
.toCompileTo('More awesome');
|
.toCompileTo('More awesome');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('functions with context argument', function() {
|
it('functions with context argument', function () {
|
||||||
expectTemplate('{{awesome frank}}')
|
expectTemplate('{{awesome frank}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
awesome: function(context) {
|
awesome: function (context) {
|
||||||
return context;
|
return context;
|
||||||
},
|
},
|
||||||
frank: 'Frank'
|
frank: 'Frank',
|
||||||
})
|
})
|
||||||
.withMessage('functions are called with context arguments')
|
.withMessage('functions are called with context arguments')
|
||||||
.toCompileTo('Frank');
|
.toCompileTo('Frank');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('pathed functions with context argument', function() {
|
it('pathed functions with context argument', function () {
|
||||||
expectTemplate('{{bar.awesome frank}}')
|
expectTemplate('{{bar.awesome frank}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
bar: {
|
bar: {
|
||||||
awesome: function(context) {
|
awesome: function (context) {
|
||||||
return context;
|
return context;
|
||||||
}
|
|
||||||
},
|
},
|
||||||
frank: 'Frank'
|
},
|
||||||
|
frank: 'Frank',
|
||||||
})
|
})
|
||||||
.withMessage('functions are called with context arguments')
|
.withMessage('functions are called with context arguments')
|
||||||
.toCompileTo('Frank');
|
.toCompileTo('Frank');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('depthed functions with context argument', function() {
|
it('depthed functions with context argument', function () {
|
||||||
expectTemplate('{{#with frank}}{{../awesome .}}{{/with}}')
|
expectTemplate('{{#with frank}}{{../awesome .}}{{/with}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
awesome: function(context) {
|
awesome: function (context) {
|
||||||
return context;
|
return context;
|
||||||
},
|
},
|
||||||
frank: 'Frank'
|
frank: 'Frank',
|
||||||
})
|
})
|
||||||
.withMessage('functions are called with context arguments')
|
.withMessage('functions are called with context arguments')
|
||||||
.toCompileTo('Frank');
|
.toCompileTo('Frank');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('block functions with context argument', function() {
|
it('block functions with context argument', function () {
|
||||||
expectTemplate('{{#awesome 1}}inner {{.}}{{/awesome}}')
|
expectTemplate('{{#awesome 1}}inner {{.}}{{/awesome}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
awesome: function(context, options) {
|
awesome: function (context, options) {
|
||||||
return options.fn(context);
|
return options.fn(context);
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.withMessage('block functions are called with context and options')
|
.withMessage('block functions are called with context and options')
|
||||||
.toCompileTo('inner 1');
|
.toCompileTo('inner 1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('depthed block functions with context argument', function() {
|
it('depthed block functions with context argument', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#with value}}{{#../awesome 1}}inner {{.}}{{/../awesome}}{{/with}}'
|
'{{#with value}}{{#../awesome 1}}inner {{.}}{{/../awesome}}{{/with}}'
|
||||||
)
|
)
|
||||||
.withInput({
|
.withInput({
|
||||||
value: true,
|
value: true,
|
||||||
awesome: function(context, options) {
|
awesome: function (context, options) {
|
||||||
return options.fn(context);
|
return options.fn(context);
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.withMessage('block functions are called with context and options')
|
.withMessage('block functions are called with context and options')
|
||||||
.toCompileTo('inner 1');
|
.toCompileTo('inner 1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('block functions without context argument', function() {
|
it('block functions without context argument', function () {
|
||||||
expectTemplate('{{#awesome}}inner{{/awesome}}')
|
expectTemplate('{{#awesome}}inner{{/awesome}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
awesome: function(options) {
|
awesome: function (options) {
|
||||||
return options.fn(this);
|
return options.fn(this);
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.withMessage('block functions are called with options')
|
.withMessage('block functions are called with options')
|
||||||
.toCompileTo('inner');
|
.toCompileTo('inner');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('pathed block functions without context argument', function() {
|
it('pathed block functions without context argument', function () {
|
||||||
expectTemplate('{{#foo.awesome}}inner{{/foo.awesome}}')
|
expectTemplate('{{#foo.awesome}}inner{{/foo.awesome}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
foo: {
|
foo: {
|
||||||
awesome: function() {
|
awesome: function () {
|
||||||
return this;
|
return this;
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.withMessage('block functions are called with options')
|
.withMessage('block functions are called with options')
|
||||||
.toCompileTo('inner');
|
.toCompileTo('inner');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('depthed block functions without context argument', function() {
|
it('depthed block functions without context argument', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#with value}}{{#../awesome}}inner{{/../awesome}}{{/with}}'
|
'{{#with value}}{{#../awesome}}inner{{/../awesome}}{{/with}}'
|
||||||
)
|
)
|
||||||
.withInput({
|
.withInput({
|
||||||
value: true,
|
value: true,
|
||||||
awesome: function() {
|
awesome: function () {
|
||||||
return this;
|
return this;
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.withMessage('block functions are called with options')
|
.withMessage('block functions are called with options')
|
||||||
.toCompileTo('inner');
|
.toCompileTo('inner');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('paths with hyphens', function() {
|
it('paths with hyphens', function () {
|
||||||
expectTemplate('{{foo-bar}}')
|
expectTemplate('{{foo-bar}}')
|
||||||
.withInput({ 'foo-bar': 'baz' })
|
.withInput({ 'foo-bar': 'baz' })
|
||||||
.withMessage('Paths can contain hyphens (-)')
|
.withMessage('Paths can contain hyphens (-)')
|
||||||
@@ -388,21 +380,21 @@ describe('basic context', function() {
|
|||||||
.toCompileTo('baz');
|
.toCompileTo('baz');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('nested paths', function() {
|
it('nested paths', function () {
|
||||||
expectTemplate('Goodbye {{alan/expression}} world!')
|
expectTemplate('Goodbye {{alan/expression}} world!')
|
||||||
.withInput({ alan: { expression: 'beautiful' } })
|
.withInput({ alan: { expression: 'beautiful' } })
|
||||||
.withMessage('Nested paths access nested objects')
|
.withMessage('Nested paths access nested objects')
|
||||||
.toCompileTo('Goodbye beautiful world!');
|
.toCompileTo('Goodbye beautiful world!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('nested paths with empty string value', function() {
|
it('nested paths with empty string value', function () {
|
||||||
expectTemplate('Goodbye {{alan/expression}} world!')
|
expectTemplate('Goodbye {{alan/expression}} world!')
|
||||||
.withInput({ alan: { expression: '' } })
|
.withInput({ alan: { expression: '' } })
|
||||||
.withMessage('Nested paths access nested objects with empty string')
|
.withMessage('Nested paths access nested objects with empty string')
|
||||||
.toCompileTo('Goodbye world!');
|
.toCompileTo('Goodbye world!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('literal paths', function() {
|
it('literal paths', function () {
|
||||||
expectTemplate('Goodbye {{[@alan]/expression}} world!')
|
expectTemplate('Goodbye {{[@alan]/expression}} world!')
|
||||||
.withInput({ '@alan': { expression: 'beautiful' } })
|
.withInput({ '@alan': { expression: 'beautiful' } })
|
||||||
.withMessage('Literal paths can be used')
|
.withMessage('Literal paths can be used')
|
||||||
@@ -414,7 +406,7 @@ describe('basic context', function() {
|
|||||||
.toCompileTo('Goodbye beautiful world!');
|
.toCompileTo('Goodbye beautiful world!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('literal references', function() {
|
it('literal references', function () {
|
||||||
expectTemplate('Goodbye {{[foo bar]}} world!')
|
expectTemplate('Goodbye {{[foo bar]}} world!')
|
||||||
.withInput({ 'foo bar': 'beautiful' })
|
.withInput({ 'foo bar': 'beautiful' })
|
||||||
.toCompileTo('Goodbye beautiful world!');
|
.toCompileTo('Goodbye beautiful world!');
|
||||||
@@ -440,24 +432,22 @@ describe('basic context', function() {
|
|||||||
.toCompileTo('Goodbye beautiful world!');
|
.toCompileTo('Goodbye beautiful world!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("that current context path ({{.}}) doesn't hit helpers", function() {
|
it("that current context path ({{.}}) doesn't hit helpers", function () {
|
||||||
expectTemplate('test: {{.}}')
|
expectTemplate('test: {{.}}')
|
||||||
.withInput(null)
|
.withInput(null)
|
||||||
.withHelpers({ helper: 'awesome' })
|
.withHelpers({ helper: 'awesome' })
|
||||||
.toCompileTo('test: ');
|
.toCompileTo('test: ');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('complex but empty paths', function() {
|
it('complex but empty paths', function () {
|
||||||
expectTemplate('{{person/name}}')
|
expectTemplate('{{person/name}}')
|
||||||
.withInput({ person: { name: null } })
|
.withInput({ person: { name: null } })
|
||||||
.toCompileTo('');
|
.toCompileTo('');
|
||||||
|
|
||||||
expectTemplate('{{person/name}}')
|
expectTemplate('{{person/name}}').withInput({ person: {} }).toCompileTo('');
|
||||||
.withInput({ person: {} })
|
|
||||||
.toCompileTo('');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('this keyword in paths', function() {
|
it('this keyword in paths', function () {
|
||||||
expectTemplate('{{#goodbyes}}{{this}}{{/goodbyes}}')
|
expectTemplate('{{#goodbyes}}{{this}}{{/goodbyes}}')
|
||||||
.withInput({ goodbyes: ['goodbye', 'Goodbye', 'GOODBYE'] })
|
.withInput({ goodbyes: ['goodbye', 'Goodbye', 'GOODBYE'] })
|
||||||
.withMessage('This keyword in paths evaluates to current context')
|
.withMessage('This keyword in paths evaluates to current context')
|
||||||
@@ -465,32 +455,30 @@ describe('basic context', function() {
|
|||||||
|
|
||||||
expectTemplate('{{#hellos}}{{this/text}}{{/hellos}}')
|
expectTemplate('{{#hellos}}{{this/text}}{{/hellos}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }]
|
hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }],
|
||||||
})
|
})
|
||||||
.withMessage('This keyword evaluates in more complex paths')
|
.withMessage('This keyword evaluates in more complex paths')
|
||||||
.toCompileTo('helloHelloHELLO');
|
.toCompileTo('helloHelloHELLO');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('this keyword nested inside path', function() {
|
it('this keyword nested inside path', function () {
|
||||||
expectTemplate('{{#hellos}}{{text/this/foo}}{{/hellos}}').toThrow(
|
expectTemplate('{{#hellos}}{{text/this/foo}}{{/hellos}}').toThrow(
|
||||||
Error,
|
Error,
|
||||||
'Invalid path: text/this - 1:13'
|
'Invalid path: text/this - 1:13'
|
||||||
);
|
);
|
||||||
|
|
||||||
expectTemplate('{{[this]}}')
|
expectTemplate('{{[this]}}').withInput({ this: 'bar' }).toCompileTo('bar');
|
||||||
.withInput({ this: 'bar' })
|
|
||||||
.toCompileTo('bar');
|
|
||||||
|
|
||||||
expectTemplate('{{text/[this]}}')
|
expectTemplate('{{text/[this]}}')
|
||||||
.withInput({ text: { this: 'bar' } })
|
.withInput({ text: { this: 'bar' } })
|
||||||
.toCompileTo('bar');
|
.toCompileTo('bar');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('this keyword in helpers', function() {
|
it('this keyword in helpers', function () {
|
||||||
var helpers = {
|
var helpers = {
|
||||||
foo: function(value) {
|
foo: function (value) {
|
||||||
return 'bar ' + value;
|
return 'bar ' + value;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
expectTemplate('{{#goodbyes}}{{foo this}}{{/goodbyes}}')
|
expectTemplate('{{#goodbyes}}{{foo this}}{{/goodbyes}}')
|
||||||
@@ -501,14 +489,14 @@ describe('basic context', function() {
|
|||||||
|
|
||||||
expectTemplate('{{#hellos}}{{foo this/text}}{{/hellos}}')
|
expectTemplate('{{#hellos}}{{foo this/text}}{{/hellos}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }]
|
hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }],
|
||||||
})
|
})
|
||||||
.withHelpers(helpers)
|
.withHelpers(helpers)
|
||||||
.withMessage('This keyword evaluates in more complex paths')
|
.withMessage('This keyword evaluates in more complex paths')
|
||||||
.toCompileTo('bar hellobar Hellobar HELLO');
|
.toCompileTo('bar hellobar Hellobar HELLO');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('this keyword nested inside helpers param', function() {
|
it('this keyword nested inside helpers param', function () {
|
||||||
expectTemplate('{{#hellos}}{{foo text/this/foo}}{{/hellos}}').toThrow(
|
expectTemplate('{{#hellos}}{{foo text/this/foo}}{{/hellos}}').toThrow(
|
||||||
Error,
|
Error,
|
||||||
'Invalid path: text/this - 1:17'
|
'Invalid path: text/this - 1:17'
|
||||||
@@ -516,79 +504,69 @@ describe('basic context', function() {
|
|||||||
|
|
||||||
expectTemplate('{{foo [this]}}')
|
expectTemplate('{{foo [this]}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
foo: function(value) {
|
foo: function (value) {
|
||||||
return value;
|
return value;
|
||||||
},
|
},
|
||||||
this: 'bar'
|
this: 'bar',
|
||||||
})
|
})
|
||||||
.toCompileTo('bar');
|
.toCompileTo('bar');
|
||||||
|
|
||||||
expectTemplate('{{foo text/[this]}}')
|
expectTemplate('{{foo text/[this]}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
foo: function(value) {
|
foo: function (value) {
|
||||||
return value;
|
return value;
|
||||||
},
|
},
|
||||||
text: { this: 'bar' }
|
text: { this: 'bar' },
|
||||||
})
|
})
|
||||||
.toCompileTo('bar');
|
.toCompileTo('bar');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('pass string literals', function() {
|
it('pass string literals', function () {
|
||||||
expectTemplate('{{"foo"}}').toCompileTo('');
|
expectTemplate('{{"foo"}}').toCompileTo('');
|
||||||
|
|
||||||
expectTemplate('{{"foo"}}')
|
expectTemplate('{{"foo"}}').withInput({ foo: 'bar' }).toCompileTo('bar');
|
||||||
.withInput({ foo: 'bar' })
|
|
||||||
.toCompileTo('bar');
|
|
||||||
|
|
||||||
expectTemplate('{{#"foo"}}{{.}}{{/"foo"}}')
|
expectTemplate('{{#"foo"}}{{.}}{{/"foo"}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
foo: ['bar', 'baz']
|
foo: ['bar', 'baz'],
|
||||||
})
|
})
|
||||||
.toCompileTo('barbaz');
|
.toCompileTo('barbaz');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('pass number literals', function() {
|
it('pass number literals', function () {
|
||||||
expectTemplate('{{12}}').toCompileTo('');
|
expectTemplate('{{12}}').toCompileTo('');
|
||||||
|
|
||||||
expectTemplate('{{12}}')
|
expectTemplate('{{12}}').withInput({ 12: 'bar' }).toCompileTo('bar');
|
||||||
.withInput({ '12': 'bar' })
|
|
||||||
.toCompileTo('bar');
|
|
||||||
|
|
||||||
expectTemplate('{{12.34}}').toCompileTo('');
|
expectTemplate('{{12.34}}').toCompileTo('');
|
||||||
|
|
||||||
expectTemplate('{{12.34}}')
|
expectTemplate('{{12.34}}').withInput({ 12.34: 'bar' }).toCompileTo('bar');
|
||||||
.withInput({ '12.34': 'bar' })
|
|
||||||
.toCompileTo('bar');
|
|
||||||
|
|
||||||
expectTemplate('{{12.34 1}}')
|
expectTemplate('{{12.34 1}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
'12.34': function(arg) {
|
12.34: function (arg) {
|
||||||
return 'bar' + arg;
|
return 'bar' + arg;
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('bar1');
|
.toCompileTo('bar1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('pass boolean literals', function() {
|
it('pass boolean literals', function () {
|
||||||
expectTemplate('{{true}}').toCompileTo('');
|
expectTemplate('{{true}}').toCompileTo('');
|
||||||
|
|
||||||
expectTemplate('{{true}}')
|
expectTemplate('{{true}}').withInput({ '': 'foo' }).toCompileTo('');
|
||||||
.withInput({ '': 'foo' })
|
|
||||||
.toCompileTo('');
|
|
||||||
|
|
||||||
expectTemplate('{{false}}')
|
expectTemplate('{{false}}').withInput({ false: 'foo' }).toCompileTo('foo');
|
||||||
.withInput({ false: 'foo' })
|
|
||||||
.toCompileTo('foo');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle literals in subexpression', function() {
|
it('should handle literals in subexpression', function () {
|
||||||
expectTemplate('{{foo (false)}}')
|
expectTemplate('{{foo (false)}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
false: function() {
|
false: function () {
|
||||||
return 'bar';
|
return 'bar';
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.withHelper('foo', function(arg) {
|
.withHelper('foo', function (arg) {
|
||||||
return arg;
|
return arg;
|
||||||
})
|
})
|
||||||
.toCompileTo('bar');
|
.toCompileTo('bar');
|
||||||
|
|||||||
+84
-84
@@ -1,5 +1,5 @@
|
|||||||
describe('blocks', function() {
|
describe('blocks', function () {
|
||||||
it('array', function() {
|
it('array', function () {
|
||||||
var string = '{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!';
|
var string = '{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!';
|
||||||
|
|
||||||
expectTemplate(string)
|
expectTemplate(string)
|
||||||
@@ -7,9 +7,9 @@ describe('blocks', function() {
|
|||||||
goodbyes: [
|
goodbyes: [
|
||||||
{ text: 'goodbye' },
|
{ text: 'goodbye' },
|
||||||
{ text: 'Goodbye' },
|
{ text: 'Goodbye' },
|
||||||
{ text: 'GOODBYE' }
|
{ text: 'GOODBYE' },
|
||||||
],
|
],
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('Arrays iterate over the contents when not empty')
|
.withMessage('Arrays iterate over the contents when not empty')
|
||||||
.toCompileTo('goodbye! Goodbye! GOODBYE! cruel world!');
|
.toCompileTo('goodbye! Goodbye! GOODBYE! cruel world!');
|
||||||
@@ -17,13 +17,13 @@ describe('blocks', function() {
|
|||||||
expectTemplate(string)
|
expectTemplate(string)
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbyes: [],
|
goodbyes: [],
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('Arrays ignore the contents when empty')
|
.withMessage('Arrays ignore the contents when empty')
|
||||||
.toCompileTo('cruel world!');
|
.toCompileTo('cruel world!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('array without data', function() {
|
it('array without data', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#goodbyes}}{{text}}{{/goodbyes}} {{#goodbyes}}{{text}}{{/goodbyes}}'
|
'{{#goodbyes}}{{text}}{{/goodbyes}} {{#goodbyes}}{{text}}{{/goodbyes}}'
|
||||||
)
|
)
|
||||||
@@ -31,15 +31,15 @@ describe('blocks', function() {
|
|||||||
goodbyes: [
|
goodbyes: [
|
||||||
{ text: 'goodbye' },
|
{ text: 'goodbye' },
|
||||||
{ text: 'Goodbye' },
|
{ text: 'Goodbye' },
|
||||||
{ text: 'GOODBYE' }
|
{ text: 'GOODBYE' },
|
||||||
],
|
],
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withCompileOptions({ compat: false })
|
.withCompileOptions({ compat: false })
|
||||||
.toCompileTo('goodbyeGoodbyeGOODBYE goodbyeGoodbyeGOODBYE');
|
.toCompileTo('goodbyeGoodbyeGOODBYE goodbyeGoodbyeGOODBYE');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('array with @index', function() {
|
it('array with @index', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#goodbyes}}{{@index}}. {{text}}! {{/goodbyes}}cruel {{world}}!'
|
'{{#goodbyes}}{{@index}}. {{text}}! {{/goodbyes}}cruel {{world}}!'
|
||||||
)
|
)
|
||||||
@@ -47,15 +47,15 @@ describe('blocks', function() {
|
|||||||
goodbyes: [
|
goodbyes: [
|
||||||
{ text: 'goodbye' },
|
{ text: 'goodbye' },
|
||||||
{ text: 'Goodbye' },
|
{ text: 'Goodbye' },
|
||||||
{ text: 'GOODBYE' }
|
{ text: 'GOODBYE' },
|
||||||
],
|
],
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('The @index variable is used')
|
.withMessage('The @index variable is used')
|
||||||
.toCompileTo('0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!');
|
.toCompileTo('0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('empty block', function() {
|
it('empty block', function () {
|
||||||
var string = '{{#goodbyes}}{{/goodbyes}}cruel {{world}}!';
|
var string = '{{#goodbyes}}{{/goodbyes}}cruel {{world}}!';
|
||||||
|
|
||||||
expectTemplate(string)
|
expectTemplate(string)
|
||||||
@@ -63,9 +63,9 @@ describe('blocks', function() {
|
|||||||
goodbyes: [
|
goodbyes: [
|
||||||
{ text: 'goodbye' },
|
{ text: 'goodbye' },
|
||||||
{ text: 'Goodbye' },
|
{ text: 'Goodbye' },
|
||||||
{ text: 'GOODBYE' }
|
{ text: 'GOODBYE' },
|
||||||
],
|
],
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('Arrays iterate over the contents when not empty')
|
.withMessage('Arrays iterate over the contents when not empty')
|
||||||
.toCompileTo('cruel world!');
|
.toCompileTo('cruel world!');
|
||||||
@@ -73,21 +73,21 @@ describe('blocks', function() {
|
|||||||
expectTemplate(string)
|
expectTemplate(string)
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbyes: [],
|
goodbyes: [],
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('Arrays ignore the contents when empty')
|
.withMessage('Arrays ignore the contents when empty')
|
||||||
.toCompileTo('cruel world!');
|
.toCompileTo('cruel world!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('block with complex lookup', function() {
|
it('block with complex lookup', function () {
|
||||||
expectTemplate('{{#goodbyes}}{{text}} cruel {{../name}}! {{/goodbyes}}')
|
expectTemplate('{{#goodbyes}}{{text}} cruel {{../name}}! {{/goodbyes}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
name: 'Alan',
|
name: 'Alan',
|
||||||
goodbyes: [
|
goodbyes: [
|
||||||
{ text: 'goodbye' },
|
{ text: 'goodbye' },
|
||||||
{ text: 'Goodbye' },
|
{ text: 'Goodbye' },
|
||||||
{ text: 'GOODBYE' }
|
{ text: 'GOODBYE' },
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
.withMessage(
|
.withMessage(
|
||||||
'Templates can access variables in contexts up the stack with relative path syntax'
|
'Templates can access variables in contexts up the stack with relative path syntax'
|
||||||
@@ -97,37 +97,37 @@ describe('blocks', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('multiple blocks with complex lookup', function() {
|
it('multiple blocks with complex lookup', function () {
|
||||||
expectTemplate('{{#goodbyes}}{{../name}}{{../name}}{{/goodbyes}}')
|
expectTemplate('{{#goodbyes}}{{../name}}{{../name}}{{/goodbyes}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
name: 'Alan',
|
name: 'Alan',
|
||||||
goodbyes: [
|
goodbyes: [
|
||||||
{ text: 'goodbye' },
|
{ text: 'goodbye' },
|
||||||
{ text: 'Goodbye' },
|
{ text: 'Goodbye' },
|
||||||
{ text: 'GOODBYE' }
|
{ text: 'GOODBYE' },
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
.toCompileTo('AlanAlanAlanAlanAlanAlan');
|
.toCompileTo('AlanAlanAlanAlanAlanAlan');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('block with complex lookup using nested context', function() {
|
it('block with complex lookup using nested context', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#goodbyes}}{{text}} cruel {{foo/../name}}! {{/goodbyes}}'
|
'{{#goodbyes}}{{text}} cruel {{foo/../name}}! {{/goodbyes}}'
|
||||||
).toThrow(Error);
|
).toThrow(Error);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('block with deep nested complex lookup', function() {
|
it('block with deep nested complex lookup', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#outer}}Goodbye {{#inner}}cruel {{../sibling}} {{../../omg}}{{/inner}}{{/outer}}'
|
'{{#outer}}Goodbye {{#inner}}cruel {{../sibling}} {{../../omg}}{{/inner}}{{/outer}}'
|
||||||
)
|
)
|
||||||
.withInput({
|
.withInput({
|
||||||
omg: 'OMG!',
|
omg: 'OMG!',
|
||||||
outer: [{ sibling: 'sad', inner: [{ text: 'goodbye' }] }]
|
outer: [{ sibling: 'sad', inner: [{ text: 'goodbye' }] }],
|
||||||
})
|
})
|
||||||
.toCompileTo('Goodbye cruel sad OMG!');
|
.toCompileTo('Goodbye cruel sad OMG!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('works with cached blocks', function() {
|
it('works with cached blocks', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#each person}}{{#with .}}{{first}} {{last}}{{/with}}{{/each}}'
|
'{{#each person}}{{#with .}}{{first}} {{last}}{{/with}}{{/each}}'
|
||||||
)
|
)
|
||||||
@@ -135,14 +135,14 @@ describe('blocks', function() {
|
|||||||
.withInput({
|
.withInput({
|
||||||
person: [
|
person: [
|
||||||
{ first: 'Alan', last: 'Johnson' },
|
{ first: 'Alan', last: 'Johnson' },
|
||||||
{ first: 'Alan', last: 'Johnson' }
|
{ first: 'Alan', last: 'Johnson' },
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
.toCompileTo('Alan JohnsonAlan Johnson');
|
.toCompileTo('Alan JohnsonAlan Johnson');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('inverted sections', function() {
|
describe('inverted sections', function () {
|
||||||
it('inverted sections with unset value', function() {
|
it('inverted sections with unset value', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}'
|
'{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}'
|
||||||
)
|
)
|
||||||
@@ -150,7 +150,7 @@ describe('blocks', function() {
|
|||||||
.toCompileTo('Right On!');
|
.toCompileTo('Right On!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('inverted section with false value', function() {
|
it('inverted section with false value', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}'
|
'{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}'
|
||||||
)
|
)
|
||||||
@@ -159,7 +159,7 @@ describe('blocks', function() {
|
|||||||
.toCompileTo('Right On!');
|
.toCompileTo('Right On!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('inverted section with empty set', function() {
|
it('inverted section with empty set', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}'
|
'{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}'
|
||||||
)
|
)
|
||||||
@@ -168,13 +168,13 @@ describe('blocks', function() {
|
|||||||
.toCompileTo('Right On!');
|
.toCompileTo('Right On!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('block inverted sections', function() {
|
it('block inverted sections', function () {
|
||||||
expectTemplate('{{#people}}{{name}}{{^}}{{none}}{{/people}}')
|
expectTemplate('{{#people}}{{name}}{{^}}{{none}}{{/people}}')
|
||||||
.withInput({ none: 'No people' })
|
.withInput({ none: 'No people' })
|
||||||
.toCompileTo('No people');
|
.toCompileTo('No people');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('chained inverted sections', function() {
|
it('chained inverted sections', function () {
|
||||||
expectTemplate('{{#people}}{{name}}{{else if none}}{{none}}{{/people}}')
|
expectTemplate('{{#people}}{{name}}{{else if none}}{{none}}{{/people}}')
|
||||||
.withInput({ none: 'No people' })
|
.withInput({ none: 'No people' })
|
||||||
.toCompileTo('No people');
|
.toCompileTo('No people');
|
||||||
@@ -192,24 +192,24 @@ describe('blocks', function() {
|
|||||||
.toCompileTo('No people');
|
.toCompileTo('No people');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('chained inverted sections with mismatch', function() {
|
it('chained inverted sections with mismatch', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#people}}{{name}}{{else if none}}{{none}}{{/if}}'
|
'{{#people}}{{name}}{{else if none}}{{none}}{{/if}}'
|
||||||
).toThrow(Error);
|
).toThrow(Error);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('block inverted sections with empty arrays', function() {
|
it('block inverted sections with empty arrays', function () {
|
||||||
expectTemplate('{{#people}}{{name}}{{^}}{{none}}{{/people}}')
|
expectTemplate('{{#people}}{{name}}{{^}}{{none}}{{/people}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
none: 'No people',
|
none: 'No people',
|
||||||
people: []
|
people: [],
|
||||||
})
|
})
|
||||||
.toCompileTo('No people');
|
.toCompileTo('No people');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('standalone sections', function() {
|
describe('standalone sections', function () {
|
||||||
it('block standalone else sections', function() {
|
it('block standalone else sections', function () {
|
||||||
expectTemplate('{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n')
|
expectTemplate('{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n')
|
||||||
.withInput({ none: 'No people' })
|
.withInput({ none: 'No people' })
|
||||||
.toCompileTo('No people\n');
|
.toCompileTo('No people\n');
|
||||||
@@ -223,7 +223,7 @@ describe('blocks', function() {
|
|||||||
.toCompileTo('No people\n');
|
.toCompileTo('No people\n');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('block standalone else sections can be disabled', function() {
|
it('block standalone else sections can be disabled', function () {
|
||||||
expectTemplate('{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n')
|
expectTemplate('{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n')
|
||||||
.withInput({ none: 'No people' })
|
.withInput({ none: 'No people' })
|
||||||
.withCompileOptions({ ignoreStandalone: true })
|
.withCompileOptions({ ignoreStandalone: true })
|
||||||
@@ -235,7 +235,7 @@ describe('blocks', function() {
|
|||||||
.toCompileTo('\nNo people\n\n');
|
.toCompileTo('\nNo people\n\n');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('block standalone chained else sections', function() {
|
it('block standalone chained else sections', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{/people}}\n'
|
'{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{/people}}\n'
|
||||||
)
|
)
|
||||||
@@ -249,17 +249,17 @@ describe('blocks', function() {
|
|||||||
.toCompileTo('No people\n');
|
.toCompileTo('No people\n');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle nesting', function() {
|
it('should handle nesting', function () {
|
||||||
expectTemplate('{{#data}}\n{{#if true}}\n{{.}}\n{{/if}}\n{{/data}}\nOK.')
|
expectTemplate('{{#data}}\n{{#if true}}\n{{.}}\n{{/if}}\n{{/data}}\nOK.')
|
||||||
.withInput({
|
.withInput({
|
||||||
data: [1, 3, 5]
|
data: [1, 3, 5],
|
||||||
})
|
})
|
||||||
.toCompileTo('1\n3\n5\nOK.');
|
.toCompileTo('1\n3\n5\nOK.');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('compat mode', function() {
|
describe('compat mode', function () {
|
||||||
it('block with deep recursive lookup lookup', function() {
|
it('block with deep recursive lookup lookup', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#outer}}Goodbye {{#inner}}cruel {{omg}}{{/inner}}{{/outer}}'
|
'{{#outer}}Goodbye {{#inner}}cruel {{omg}}{{/inner}}{{/outer}}'
|
||||||
)
|
)
|
||||||
@@ -268,108 +268,108 @@ describe('blocks', function() {
|
|||||||
.toCompileTo('Goodbye cruel OMG!');
|
.toCompileTo('Goodbye cruel OMG!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('block with deep recursive pathed lookup', function() {
|
it('block with deep recursive pathed lookup', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}'
|
'{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}'
|
||||||
)
|
)
|
||||||
.withInput({
|
.withInput({
|
||||||
omg: { yes: 'OMG!' },
|
omg: { yes: 'OMG!' },
|
||||||
outer: [{ inner: [{ yes: 'no', text: 'goodbye' }] }]
|
outer: [{ inner: [{ yes: 'no', text: 'goodbye' }] }],
|
||||||
})
|
})
|
||||||
.withCompileOptions({ compat: true })
|
.withCompileOptions({ compat: true })
|
||||||
.toCompileTo('Goodbye cruel OMG!');
|
.toCompileTo('Goodbye cruel OMG!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('block with missed recursive lookup', function() {
|
it('block with missed recursive lookup', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}'
|
'{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}'
|
||||||
)
|
)
|
||||||
.withInput({
|
.withInput({
|
||||||
omg: { no: 'OMG!' },
|
omg: { no: 'OMG!' },
|
||||||
outer: [{ inner: [{ yes: 'no', text: 'goodbye' }] }]
|
outer: [{ inner: [{ yes: 'no', text: 'goodbye' }] }],
|
||||||
})
|
})
|
||||||
.withCompileOptions({ compat: true })
|
.withCompileOptions({ compat: true })
|
||||||
.toCompileTo('Goodbye cruel ');
|
.toCompileTo('Goodbye cruel ');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('decorators', function() {
|
describe('decorators', function () {
|
||||||
it('should apply mustache decorators', function() {
|
it('should apply mustache decorators', function () {
|
||||||
expectTemplate('{{#helper}}{{*decorator}}{{/helper}}')
|
expectTemplate('{{#helper}}{{*decorator}}{{/helper}}')
|
||||||
.withHelper('helper', function(options) {
|
.withHelper('helper', function (options) {
|
||||||
return options.fn.run;
|
return options.fn.run;
|
||||||
})
|
})
|
||||||
.withDecorator('decorator', function(fn) {
|
.withDecorator('decorator', function (fn) {
|
||||||
fn.run = 'success';
|
fn.run = 'success';
|
||||||
return fn;
|
return fn;
|
||||||
})
|
})
|
||||||
.toCompileTo('success');
|
.toCompileTo('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should apply allow undefined return', function() {
|
it('should apply allow undefined return', function () {
|
||||||
expectTemplate('{{#helper}}{{*decorator}}suc{{/helper}}')
|
expectTemplate('{{#helper}}{{*decorator}}suc{{/helper}}')
|
||||||
.withHelper('helper', function(options) {
|
.withHelper('helper', function (options) {
|
||||||
return options.fn() + options.fn.run;
|
return options.fn() + options.fn.run;
|
||||||
})
|
})
|
||||||
.withDecorator('decorator', function(fn) {
|
.withDecorator('decorator', function (fn) {
|
||||||
fn.run = 'cess';
|
fn.run = 'cess';
|
||||||
})
|
})
|
||||||
.toCompileTo('success');
|
.toCompileTo('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should apply block decorators', function() {
|
it('should apply block decorators', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#helper}}{{#*decorator}}success{{/decorator}}{{/helper}}'
|
'{{#helper}}{{#*decorator}}success{{/decorator}}{{/helper}}'
|
||||||
)
|
)
|
||||||
.withHelper('helper', function(options) {
|
.withHelper('helper', function (options) {
|
||||||
return options.fn.run;
|
return options.fn.run;
|
||||||
})
|
})
|
||||||
.withDecorator('decorator', function(fn, props, container, options) {
|
.withDecorator('decorator', function (fn, props, container, options) {
|
||||||
fn.run = options.fn();
|
fn.run = options.fn();
|
||||||
return fn;
|
return fn;
|
||||||
})
|
})
|
||||||
.toCompileTo('success');
|
.toCompileTo('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support nested decorators', function() {
|
it('should support nested decorators', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#helper}}{{#*decorator}}{{#*nested}}suc{{/nested}}cess{{/decorator}}{{/helper}}'
|
'{{#helper}}{{#*decorator}}{{#*nested}}suc{{/nested}}cess{{/decorator}}{{/helper}}'
|
||||||
)
|
)
|
||||||
.withHelper('helper', function(options) {
|
.withHelper('helper', function (options) {
|
||||||
return options.fn.run;
|
return options.fn.run;
|
||||||
})
|
})
|
||||||
.withDecorators({
|
.withDecorators({
|
||||||
decorator: function(fn, props, container, options) {
|
decorator: function (fn, props, container, options) {
|
||||||
fn.run = options.fn.nested + options.fn();
|
fn.run = options.fn.nested + options.fn();
|
||||||
return fn;
|
return fn;
|
||||||
},
|
},
|
||||||
nested: function(fn, props, container, options) {
|
nested: function (fn, props, container, options) {
|
||||||
props.nested = options.fn();
|
props.nested = options.fn();
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('success');
|
.toCompileTo('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should apply multiple decorators', function() {
|
it('should apply multiple decorators', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#helper}}{{#*decorator}}suc{{/decorator}}{{#*decorator}}cess{{/decorator}}{{/helper}}'
|
'{{#helper}}{{#*decorator}}suc{{/decorator}}{{#*decorator}}cess{{/decorator}}{{/helper}}'
|
||||||
)
|
)
|
||||||
.withHelper('helper', function(options) {
|
.withHelper('helper', function (options) {
|
||||||
return options.fn.run;
|
return options.fn.run;
|
||||||
})
|
})
|
||||||
.withDecorator('decorator', function(fn, props, container, options) {
|
.withDecorator('decorator', function (fn, props, container, options) {
|
||||||
fn.run = (fn.run || '') + options.fn();
|
fn.run = (fn.run || '') + options.fn();
|
||||||
return fn;
|
return fn;
|
||||||
})
|
})
|
||||||
.toCompileTo('success');
|
.toCompileTo('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should access parent variables', function() {
|
it('should access parent variables', function () {
|
||||||
expectTemplate('{{#helper}}{{*decorator foo}}{{/helper}}')
|
expectTemplate('{{#helper}}{{*decorator foo}}{{/helper}}')
|
||||||
.withHelper('helper', function(options) {
|
.withHelper('helper', function (options) {
|
||||||
return options.fn.run;
|
return options.fn.run;
|
||||||
})
|
})
|
||||||
.withDecorator('decorator', function(fn, props, container, options) {
|
.withDecorator('decorator', function (fn, props, container, options) {
|
||||||
fn.run = options.args;
|
fn.run = options.args;
|
||||||
return fn;
|
return fn;
|
||||||
})
|
})
|
||||||
@@ -377,10 +377,10 @@ describe('blocks', function() {
|
|||||||
.toCompileTo('success');
|
.toCompileTo('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work with root program', function() {
|
it('should work with root program', function () {
|
||||||
var run;
|
var run;
|
||||||
expectTemplate('{{*decorator "success"}}')
|
expectTemplate('{{*decorator "success"}}')
|
||||||
.withDecorator('decorator', function(fn, props, container, options) {
|
.withDecorator('decorator', function (fn, props, container, options) {
|
||||||
equals(options.args[0], 'success');
|
equals(options.args[0], 'success');
|
||||||
run = true;
|
run = true;
|
||||||
return fn;
|
return fn;
|
||||||
@@ -390,10 +390,10 @@ describe('blocks', function() {
|
|||||||
equals(run, true);
|
equals(run, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail when accessing variables from root', function() {
|
it('should fail when accessing variables from root', function () {
|
||||||
var run;
|
var run;
|
||||||
expectTemplate('{{*decorator foo}}')
|
expectTemplate('{{*decorator foo}}')
|
||||||
.withDecorator('decorator', function(fn, props, container, options) {
|
.withDecorator('decorator', function (fn, props, container, options) {
|
||||||
equals(options.args[0], undefined);
|
equals(options.args[0], undefined);
|
||||||
run = true;
|
run = true;
|
||||||
return fn;
|
return fn;
|
||||||
@@ -403,11 +403,11 @@ describe('blocks', function() {
|
|||||||
equals(run, true);
|
equals(run, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('registration', function() {
|
describe('registration', function () {
|
||||||
it('unregisters', function() {
|
it('unregisters', function () {
|
||||||
handlebarsEnv.decorators = {};
|
handlebarsEnv.decorators = {};
|
||||||
|
|
||||||
handlebarsEnv.registerDecorator('foo', function() {
|
handlebarsEnv.registerDecorator('foo', function () {
|
||||||
return 'fail';
|
return 'fail';
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -416,12 +416,12 @@ describe('blocks', function() {
|
|||||||
equals(handlebarsEnv.decorators.foo, undefined);
|
equals(handlebarsEnv.decorators.foo, undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows multiple globals', function() {
|
it('allows multiple globals', function () {
|
||||||
handlebarsEnv.decorators = {};
|
handlebarsEnv.decorators = {};
|
||||||
|
|
||||||
handlebarsEnv.registerDecorator({
|
handlebarsEnv.registerDecorator({
|
||||||
foo: function() {},
|
foo: function () {},
|
||||||
bar: function() {}
|
bar: function () {},
|
||||||
});
|
});
|
||||||
|
|
||||||
equals(!!handlebarsEnv.decorators.foo, true);
|
equals(!!handlebarsEnv.decorators.foo, true);
|
||||||
@@ -432,17 +432,17 @@ describe('blocks', function() {
|
|||||||
equals(handlebarsEnv.decorators.bar, undefined);
|
equals(handlebarsEnv.decorators.bar, undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fails with multiple and args', function() {
|
it('fails with multiple and args', function () {
|
||||||
shouldThrow(
|
shouldThrow(
|
||||||
function() {
|
function () {
|
||||||
handlebarsEnv.registerDecorator(
|
handlebarsEnv.registerDecorator(
|
||||||
{
|
{
|
||||||
world: function() {
|
world: function () {
|
||||||
return 'world!';
|
return 'world!';
|
||||||
},
|
},
|
||||||
testHelper: function() {
|
testHelper: function () {
|
||||||
return 'found it!';
|
return 'found it!';
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
|||||||
+133
-122
@@ -1,12 +1,12 @@
|
|||||||
describe('builtin helpers', function() {
|
describe('builtin helpers', function () {
|
||||||
describe('#if', function() {
|
describe('#if', function () {
|
||||||
it('if', function() {
|
it('if', function () {
|
||||||
var string = '{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!';
|
var string = '{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!';
|
||||||
|
|
||||||
expectTemplate(string)
|
expectTemplate(string)
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbye: true,
|
goodbye: true,
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('if with boolean argument shows the contents when true')
|
.withMessage('if with boolean argument shows the contents when true')
|
||||||
.toCompileTo('GOODBYE cruel world!');
|
.toCompileTo('GOODBYE cruel world!');
|
||||||
@@ -14,7 +14,7 @@ describe('builtin helpers', function() {
|
|||||||
expectTemplate(string)
|
expectTemplate(string)
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbye: 'dummy',
|
goodbye: 'dummy',
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('if with string argument shows the contents')
|
.withMessage('if with string argument shows the contents')
|
||||||
.toCompileTo('GOODBYE cruel world!');
|
.toCompileTo('GOODBYE cruel world!');
|
||||||
@@ -22,7 +22,7 @@ describe('builtin helpers', function() {
|
|||||||
expectTemplate(string)
|
expectTemplate(string)
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbye: false,
|
goodbye: false,
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage(
|
.withMessage(
|
||||||
'if with boolean argument does not show the contents when false'
|
'if with boolean argument does not show the contents when false'
|
||||||
@@ -37,7 +37,7 @@ describe('builtin helpers', function() {
|
|||||||
expectTemplate(string)
|
expectTemplate(string)
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbye: ['foo'],
|
goodbye: ['foo'],
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('if with non-empty array shows the contents')
|
.withMessage('if with non-empty array shows the contents')
|
||||||
.toCompileTo('GOODBYE cruel world!');
|
.toCompileTo('GOODBYE cruel world!');
|
||||||
@@ -45,7 +45,7 @@ describe('builtin helpers', function() {
|
|||||||
expectTemplate(string)
|
expectTemplate(string)
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbye: [],
|
goodbye: [],
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('if with empty array does not show the contents')
|
.withMessage('if with empty array does not show the contents')
|
||||||
.toCompileTo('cruel world!');
|
.toCompileTo('cruel world!');
|
||||||
@@ -53,7 +53,7 @@ describe('builtin helpers', function() {
|
|||||||
expectTemplate(string)
|
expectTemplate(string)
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbye: 0,
|
goodbye: 0,
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('if with zero does not show the contents')
|
.withMessage('if with zero does not show the contents')
|
||||||
.toCompileTo('cruel world!');
|
.toCompileTo('cruel world!');
|
||||||
@@ -63,21 +63,21 @@ describe('builtin helpers', function() {
|
|||||||
)
|
)
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbye: 0,
|
goodbye: 0,
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('if with zero does not show the contents')
|
.withMessage('if with zero does not show the contents')
|
||||||
.toCompileTo('GOODBYE cruel world!');
|
.toCompileTo('GOODBYE cruel world!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('if with function argument', function() {
|
it('if with function argument', function () {
|
||||||
var string = '{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!';
|
var string = '{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!';
|
||||||
|
|
||||||
expectTemplate(string)
|
expectTemplate(string)
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbye: function() {
|
goodbye: function () {
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage(
|
.withMessage(
|
||||||
'if with function shows the contents when function returns true'
|
'if with function shows the contents when function returns true'
|
||||||
@@ -86,10 +86,10 @@ describe('builtin helpers', function() {
|
|||||||
|
|
||||||
expectTemplate(string)
|
expectTemplate(string)
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbye: function() {
|
goodbye: function () {
|
||||||
return this.world;
|
return this.world;
|
||||||
},
|
},
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage(
|
.withMessage(
|
||||||
'if with function shows the contents when function returns string'
|
'if with function shows the contents when function returns string'
|
||||||
@@ -98,10 +98,10 @@ describe('builtin helpers', function() {
|
|||||||
|
|
||||||
expectTemplate(string)
|
expectTemplate(string)
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbye: function() {
|
goodbye: function () {
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage(
|
.withMessage(
|
||||||
'if with function does not show the contents when returns false'
|
'if with function does not show the contents when returns false'
|
||||||
@@ -110,10 +110,10 @@ describe('builtin helpers', function() {
|
|||||||
|
|
||||||
expectTemplate(string)
|
expectTemplate(string)
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbye: function() {
|
goodbye: function () {
|
||||||
return this.foo;
|
return this.foo;
|
||||||
},
|
},
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage(
|
.withMessage(
|
||||||
'if with function does not show the contents when returns undefined'
|
'if with function does not show the contents when returns undefined'
|
||||||
@@ -121,61 +121,61 @@ describe('builtin helpers', function() {
|
|||||||
.toCompileTo('cruel world!');
|
.toCompileTo('cruel world!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not change the depth list', function() {
|
it('should not change the depth list', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#with foo}}{{#if goodbye}}GOODBYE cruel {{../world}}!{{/if}}{{/with}}'
|
'{{#with foo}}{{#if goodbye}}GOODBYE cruel {{../world}}!{{/if}}{{/with}}'
|
||||||
)
|
)
|
||||||
.withInput({
|
.withInput({
|
||||||
foo: { goodbye: true },
|
foo: { goodbye: true },
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.toCompileTo('GOODBYE cruel world!');
|
.toCompileTo('GOODBYE cruel world!');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#with', function() {
|
describe('#with', function () {
|
||||||
it('with', function() {
|
it('with', function () {
|
||||||
expectTemplate('{{#with person}}{{first}} {{last}}{{/with}}')
|
expectTemplate('{{#with person}}{{first}} {{last}}{{/with}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
person: {
|
person: {
|
||||||
first: 'Alan',
|
first: 'Alan',
|
||||||
last: 'Johnson'
|
last: 'Johnson',
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('Alan Johnson');
|
.toCompileTo('Alan Johnson');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('with with function argument', function() {
|
it('with with function argument', function () {
|
||||||
expectTemplate('{{#with person}}{{first}} {{last}}{{/with}}')
|
expectTemplate('{{#with person}}{{first}} {{last}}{{/with}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
person: function() {
|
person: function () {
|
||||||
return {
|
return {
|
||||||
first: 'Alan',
|
first: 'Alan',
|
||||||
last: 'Johnson'
|
last: 'Johnson',
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('Alan Johnson');
|
.toCompileTo('Alan Johnson');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('with with else', function() {
|
it('with with else', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#with person}}Person is present{{else}}Person is not present{{/with}}'
|
'{{#with person}}Person is present{{else}}Person is not present{{/with}}'
|
||||||
).toCompileTo('Person is not present');
|
).toCompileTo('Person is not present');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('with provides block parameter', function() {
|
it('with provides block parameter', function () {
|
||||||
expectTemplate('{{#with person as |foo|}}{{foo.first}} {{last}}{{/with}}')
|
expectTemplate('{{#with person as |foo|}}{{foo.first}} {{last}}{{/with}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
person: {
|
person: {
|
||||||
first: 'Alan',
|
first: 'Alan',
|
||||||
last: 'Johnson'
|
last: 'Johnson',
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('Alan Johnson');
|
.toCompileTo('Alan Johnson');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('works when data is disabled', function() {
|
it('works when data is disabled', function () {
|
||||||
expectTemplate('{{#with person as |foo|}}{{foo.first}} {{last}}{{/with}}')
|
expectTemplate('{{#with person as |foo|}}{{foo.first}} {{last}}{{/with}}')
|
||||||
.withInput({ person: { first: 'Alan', last: 'Johnson' } })
|
.withInput({ person: { first: 'Alan', last: 'Johnson' } })
|
||||||
.withCompileOptions({ data: false })
|
.withCompileOptions({ data: false })
|
||||||
@@ -183,14 +183,14 @@ describe('builtin helpers', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#each', function() {
|
describe('#each', function () {
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
handlebarsEnv.registerHelper('detectDataInsideEach', function(options) {
|
handlebarsEnv.registerHelper('detectDataInsideEach', function (options) {
|
||||||
return options.data && options.data.exclaim;
|
return options.data && options.data.exclaim;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('each', function() {
|
it('each', function () {
|
||||||
var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!';
|
var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!';
|
||||||
|
|
||||||
expectTemplate(string)
|
expectTemplate(string)
|
||||||
@@ -198,9 +198,9 @@ describe('builtin helpers', function() {
|
|||||||
goodbyes: [
|
goodbyes: [
|
||||||
{ text: 'goodbye' },
|
{ text: 'goodbye' },
|
||||||
{ text: 'Goodbye' },
|
{ text: 'Goodbye' },
|
||||||
{ text: 'GOODBYE' }
|
{ text: 'GOODBYE' },
|
||||||
],
|
],
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage(
|
.withMessage(
|
||||||
'each with array argument iterates over the contents when not empty'
|
'each with array argument iterates over the contents when not empty'
|
||||||
@@ -210,21 +210,21 @@ describe('builtin helpers', function() {
|
|||||||
expectTemplate(string)
|
expectTemplate(string)
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbyes: [],
|
goodbyes: [],
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('each with array argument ignores the contents when empty')
|
.withMessage('each with array argument ignores the contents when empty')
|
||||||
.toCompileTo('cruel world!');
|
.toCompileTo('cruel world!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('each without data', function() {
|
it('each without data', function () {
|
||||||
expectTemplate('{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!')
|
expectTemplate('{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!')
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbyes: [
|
goodbyes: [
|
||||||
{ text: 'goodbye' },
|
{ text: 'goodbye' },
|
||||||
{ text: 'Goodbye' },
|
{ text: 'Goodbye' },
|
||||||
{ text: 'GOODBYE' }
|
{ text: 'GOODBYE' },
|
||||||
],
|
],
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withRuntimeOptions({ data: false })
|
.withRuntimeOptions({ data: false })
|
||||||
.withCompileOptions({ data: false })
|
.withCompileOptions({ data: false })
|
||||||
@@ -237,13 +237,13 @@ describe('builtin helpers', function() {
|
|||||||
.toCompileTo('cruelworld');
|
.toCompileTo('cruelworld');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('each without context', function() {
|
it('each without context', function () {
|
||||||
expectTemplate('{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!')
|
expectTemplate('{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!')
|
||||||
.withInput(undefined)
|
.withInput(undefined)
|
||||||
.toCompileTo('cruel !');
|
.toCompileTo('cruel !');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('each with an object and @key', function() {
|
it('each with an object and @key', function () {
|
||||||
var string =
|
var string =
|
||||||
'{{#each goodbyes}}{{@key}}. {{text}}! {{/each}}cruel {{world}}!';
|
'{{#each goodbyes}}{{@key}}. {{text}}! {{/each}}cruel {{world}}!';
|
||||||
|
|
||||||
@@ -272,12 +272,12 @@ describe('builtin helpers', function() {
|
|||||||
expectTemplate(string)
|
expectTemplate(string)
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbyes: {},
|
goodbyes: {},
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.toCompileTo('cruel world!');
|
.toCompileTo('cruel world!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('each with @index', function() {
|
it('each with @index', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!'
|
'{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!'
|
||||||
)
|
)
|
||||||
@@ -285,15 +285,15 @@ describe('builtin helpers', function() {
|
|||||||
goodbyes: [
|
goodbyes: [
|
||||||
{ text: 'goodbye' },
|
{ text: 'goodbye' },
|
||||||
{ text: 'Goodbye' },
|
{ text: 'Goodbye' },
|
||||||
{ text: 'GOODBYE' }
|
{ text: 'GOODBYE' },
|
||||||
],
|
],
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('The @index variable is used')
|
.withMessage('The @index variable is used')
|
||||||
.toCompileTo('0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!');
|
.toCompileTo('0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('each with nested @index', function() {
|
it('each with nested @index', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#each goodbyes}}{{@index}}. {{text}}! {{#each ../goodbyes}}{{@index}} {{/each}}After {{@index}} {{/each}}{{@index}}cruel {{world}}!'
|
'{{#each goodbyes}}{{@index}}. {{text}}! {{#each ../goodbyes}}{{@index}} {{/each}}After {{@index}} {{/each}}{{@index}}cruel {{world}}!'
|
||||||
)
|
)
|
||||||
@@ -301,9 +301,9 @@ describe('builtin helpers', function() {
|
|||||||
goodbyes: [
|
goodbyes: [
|
||||||
{ text: 'goodbye' },
|
{ text: 'goodbye' },
|
||||||
{ text: 'Goodbye' },
|
{ text: 'Goodbye' },
|
||||||
{ text: 'GOODBYE' }
|
{ text: 'GOODBYE' },
|
||||||
],
|
],
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('The @index variable is used')
|
.withMessage('The @index variable is used')
|
||||||
.toCompileTo(
|
.toCompileTo(
|
||||||
@@ -311,20 +311,29 @@ describe('builtin helpers', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('each with block params', function() {
|
it('each with block params', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#each goodbyes as |value index|}}{{index}}. {{value.text}}! {{#each ../goodbyes as |childValue childIndex|}} {{index}} {{childIndex}}{{/each}} After {{index}} {{/each}}{{index}}cruel {{world}}!'
|
'{{#each goodbyes as |value index|}}{{index}}. {{value.text}}! {{#each ../goodbyes as |childValue childIndex|}} {{index}} {{childIndex}}{{/each}} After {{index}} {{/each}}{{index}}cruel {{world}}!'
|
||||||
)
|
)
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }],
|
goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }],
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.toCompileTo(
|
.toCompileTo(
|
||||||
'0. goodbye! 0 0 0 1 After 0 1. Goodbye! 1 0 1 1 After 1 cruel world!'
|
'0. goodbye! 0 0 0 1 After 0 1. Goodbye! 1 0 1 1 After 1 cruel world!'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('each object with @index', function() {
|
it('each with block params and strict compilation', function () {
|
||||||
|
expectTemplate(
|
||||||
|
'{{#each goodbyes as |value index|}}{{index}}. {{value.text}}!{{/each}}'
|
||||||
|
)
|
||||||
|
.withCompileOptions({ strict: true })
|
||||||
|
.withInput({ goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }] })
|
||||||
|
.toCompileTo('0. goodbye!1. Goodbye!');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('each object with @index', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!'
|
'{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!'
|
||||||
)
|
)
|
||||||
@@ -332,15 +341,15 @@ describe('builtin helpers', function() {
|
|||||||
goodbyes: {
|
goodbyes: {
|
||||||
a: { text: 'goodbye' },
|
a: { text: 'goodbye' },
|
||||||
b: { text: 'Goodbye' },
|
b: { text: 'Goodbye' },
|
||||||
c: { text: 'GOODBYE' }
|
c: { text: 'GOODBYE' },
|
||||||
},
|
},
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('The @index variable is used')
|
.withMessage('The @index variable is used')
|
||||||
.toCompileTo('0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!');
|
.toCompileTo('0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('each with @first', function() {
|
it('each with @first', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#each goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/each}}cruel {{world}}!'
|
'{{#each goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/each}}cruel {{world}}!'
|
||||||
)
|
)
|
||||||
@@ -348,15 +357,15 @@ describe('builtin helpers', function() {
|
|||||||
goodbyes: [
|
goodbyes: [
|
||||||
{ text: 'goodbye' },
|
{ text: 'goodbye' },
|
||||||
{ text: 'Goodbye' },
|
{ text: 'Goodbye' },
|
||||||
{ text: 'GOODBYE' }
|
{ text: 'GOODBYE' },
|
||||||
],
|
],
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('The @first variable is used')
|
.withMessage('The @first variable is used')
|
||||||
.toCompileTo('goodbye! cruel world!');
|
.toCompileTo('goodbye! cruel world!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('each with nested @first', function() {
|
it('each with nested @first', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#each goodbyes}}({{#if @first}}{{text}}! {{/if}}{{#each ../goodbyes}}{{#if @first}}{{text}}!{{/if}}{{/each}}{{#if @first}} {{text}}!{{/if}}) {{/each}}cruel {{world}}!'
|
'{{#each goodbyes}}({{#if @first}}{{text}}! {{/if}}{{#each ../goodbyes}}{{#if @first}}{{text}}!{{/if}}{{/each}}{{#if @first}} {{text}}!{{/if}}) {{/each}}cruel {{world}}!'
|
||||||
)
|
)
|
||||||
@@ -364,9 +373,9 @@ describe('builtin helpers', function() {
|
|||||||
goodbyes: [
|
goodbyes: [
|
||||||
{ text: 'goodbye' },
|
{ text: 'goodbye' },
|
||||||
{ text: 'Goodbye' },
|
{ text: 'Goodbye' },
|
||||||
{ text: 'GOODBYE' }
|
{ text: 'GOODBYE' },
|
||||||
],
|
],
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('The @first variable is used')
|
.withMessage('The @first variable is used')
|
||||||
.toCompileTo(
|
.toCompileTo(
|
||||||
@@ -374,19 +383,19 @@ describe('builtin helpers', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('each object with @first', function() {
|
it('each object with @first', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#each goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/each}}cruel {{world}}!'
|
'{{#each goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/each}}cruel {{world}}!'
|
||||||
)
|
)
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbyes: { foo: { text: 'goodbye' }, bar: { text: 'Goodbye' } },
|
goodbyes: { foo: { text: 'goodbye' }, bar: { text: 'Goodbye' } },
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('The @first variable is used')
|
.withMessage('The @first variable is used')
|
||||||
.toCompileTo('goodbye! cruel world!');
|
.toCompileTo('goodbye! cruel world!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('each with @last', function() {
|
it('each with @last', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!'
|
'{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!'
|
||||||
)
|
)
|
||||||
@@ -394,27 +403,27 @@ describe('builtin helpers', function() {
|
|||||||
goodbyes: [
|
goodbyes: [
|
||||||
{ text: 'goodbye' },
|
{ text: 'goodbye' },
|
||||||
{ text: 'Goodbye' },
|
{ text: 'Goodbye' },
|
||||||
{ text: 'GOODBYE' }
|
{ text: 'GOODBYE' },
|
||||||
],
|
],
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('The @last variable is used')
|
.withMessage('The @last variable is used')
|
||||||
.toCompileTo('GOODBYE! cruel world!');
|
.toCompileTo('GOODBYE! cruel world!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('each object with @last', function() {
|
it('each object with @last', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!'
|
'{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!'
|
||||||
)
|
)
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbyes: { foo: { text: 'goodbye' }, bar: { text: 'Goodbye' } },
|
goodbyes: { foo: { text: 'goodbye' }, bar: { text: 'Goodbye' } },
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('The @last variable is used')
|
.withMessage('The @last variable is used')
|
||||||
.toCompileTo('Goodbye! cruel world!');
|
.toCompileTo('Goodbye! cruel world!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('each with nested @last', function() {
|
it('each with nested @last', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#each goodbyes}}({{#if @last}}{{text}}! {{/if}}{{#each ../goodbyes}}{{#if @last}}{{text}}!{{/if}}{{/each}}{{#if @last}} {{text}}!{{/if}}) {{/each}}cruel {{world}}!'
|
'{{#each goodbyes}}({{#if @last}}{{text}}! {{/if}}{{#each ../goodbyes}}{{#if @last}}{{text}}!{{/if}}{{/each}}{{#if @last}} {{text}}!{{/if}}) {{/each}}cruel {{world}}!'
|
||||||
)
|
)
|
||||||
@@ -422,9 +431,9 @@ describe('builtin helpers', function() {
|
|||||||
goodbyes: [
|
goodbyes: [
|
||||||
{ text: 'goodbye' },
|
{ text: 'goodbye' },
|
||||||
{ text: 'Goodbye' },
|
{ text: 'Goodbye' },
|
||||||
{ text: 'GOODBYE' }
|
{ text: 'GOODBYE' },
|
||||||
],
|
],
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('The @last variable is used')
|
.withMessage('The @last variable is used')
|
||||||
.toCompileTo(
|
.toCompileTo(
|
||||||
@@ -432,19 +441,19 @@ describe('builtin helpers', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('each with function argument', function() {
|
it('each with function argument', function () {
|
||||||
var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!';
|
var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!';
|
||||||
|
|
||||||
expectTemplate(string)
|
expectTemplate(string)
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbyes: function() {
|
goodbyes: function () {
|
||||||
return [
|
return [
|
||||||
{ text: 'goodbye' },
|
{ text: 'goodbye' },
|
||||||
{ text: 'Goodbye' },
|
{ text: 'Goodbye' },
|
||||||
{ text: 'GOODBYE' }
|
{ text: 'GOODBYE' },
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage(
|
.withMessage(
|
||||||
'each with array function argument iterates over the contents when not empty'
|
'each with array function argument iterates over the contents when not empty'
|
||||||
@@ -454,7 +463,7 @@ describe('builtin helpers', function() {
|
|||||||
expectTemplate(string)
|
expectTemplate(string)
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbyes: [],
|
goodbyes: [],
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage(
|
.withMessage(
|
||||||
'each with array function argument ignores the contents when empty'
|
'each with array function argument ignores the contents when empty'
|
||||||
@@ -462,7 +471,7 @@ describe('builtin helpers', function() {
|
|||||||
.toCompileTo('cruel world!');
|
.toCompileTo('cruel world!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('each object when last key is an empty string', function() {
|
it('each object when last key is an empty string', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!'
|
'{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!'
|
||||||
)
|
)
|
||||||
@@ -470,15 +479,15 @@ describe('builtin helpers', function() {
|
|||||||
goodbyes: {
|
goodbyes: {
|
||||||
a: { text: 'goodbye' },
|
a: { text: 'goodbye' },
|
||||||
b: { text: 'Goodbye' },
|
b: { text: 'Goodbye' },
|
||||||
'': { text: 'GOODBYE' }
|
'': { text: 'GOODBYE' },
|
||||||
},
|
},
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('Empty string key is not skipped')
|
.withMessage('Empty string key is not skipped')
|
||||||
.toCompileTo('0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!');
|
.toCompileTo('0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('data passed to helpers', function() {
|
it('data passed to helpers', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#each letters}}{{this}}{{detectDataInsideEach}}{{/each}}'
|
'{{#each letters}}{{this}}{{detectDataInsideEach}}{{/each}}'
|
||||||
)
|
)
|
||||||
@@ -486,13 +495,13 @@ describe('builtin helpers', function() {
|
|||||||
.withMessage('should output data')
|
.withMessage('should output data')
|
||||||
.withRuntimeOptions({
|
.withRuntimeOptions({
|
||||||
data: {
|
data: {
|
||||||
exclaim: '!'
|
exclaim: '!',
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('a!b!c!');
|
.toCompileTo('a!b!c!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('each on implicit context', function() {
|
it('each on implicit context', function () {
|
||||||
expectTemplate('{{#each}}{{text}}! {{/each}}cruel world!').toThrow(
|
expectTemplate('{{#each}}{{text}}! {{/each}}cruel world!').toThrow(
|
||||||
handlebarsEnv.Exception,
|
handlebarsEnv.Exception,
|
||||||
'Must pass iterator to #each'
|
'Must pass iterator to #each'
|
||||||
@@ -500,12 +509,12 @@ describe('builtin helpers', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (global.Symbol && global.Symbol.iterator) {
|
if (global.Symbol && global.Symbol.iterator) {
|
||||||
it('each on iterable', function() {
|
it('each on iterable', function () {
|
||||||
function Iterator(arr) {
|
function Iterator(arr) {
|
||||||
this.arr = arr;
|
this.arr = arr;
|
||||||
this.index = 0;
|
this.index = 0;
|
||||||
}
|
}
|
||||||
Iterator.prototype.next = function() {
|
Iterator.prototype.next = function () {
|
||||||
var value = this.arr[this.index];
|
var value = this.arr[this.index];
|
||||||
var done = this.index === this.arr.length;
|
var done = this.index === this.arr.length;
|
||||||
if (!done) {
|
if (!done) {
|
||||||
@@ -516,7 +525,7 @@ describe('builtin helpers', function() {
|
|||||||
function Iterable(arr) {
|
function Iterable(arr) {
|
||||||
this.arr = arr;
|
this.arr = arr;
|
||||||
}
|
}
|
||||||
Iterable.prototype[global.Symbol.iterator] = function() {
|
Iterable.prototype[global.Symbol.iterator] = function () {
|
||||||
return new Iterator(this.arr);
|
return new Iterator(this.arr);
|
||||||
};
|
};
|
||||||
var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!';
|
var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!';
|
||||||
@@ -526,9 +535,9 @@ describe('builtin helpers', function() {
|
|||||||
goodbyes: new Iterable([
|
goodbyes: new Iterable([
|
||||||
{ text: 'goodbye' },
|
{ text: 'goodbye' },
|
||||||
{ text: 'Goodbye' },
|
{ text: 'Goodbye' },
|
||||||
{ text: 'GOODBYE' }
|
{ text: 'GOODBYE' },
|
||||||
]),
|
]),
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage(
|
.withMessage(
|
||||||
'each with array argument iterates over the contents when not empty'
|
'each with array argument iterates over the contents when not empty'
|
||||||
@@ -538,7 +547,7 @@ describe('builtin helpers', function() {
|
|||||||
expectTemplate(string)
|
expectTemplate(string)
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbyes: new Iterable([]),
|
goodbyes: new Iterable([]),
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage(
|
.withMessage(
|
||||||
'each with array argument ignores the contents when empty'
|
'each with array argument ignores the contents when empty'
|
||||||
@@ -548,27 +557,27 @@ describe('builtin helpers', function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#log', function() {
|
describe('#log', function () {
|
||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
if (typeof console === 'undefined') {
|
if (typeof console === 'undefined') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var $log, $info, $error;
|
var $log, $info, $error;
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
$log = console.log;
|
$log = console.log;
|
||||||
$info = console.info;
|
$info = console.info;
|
||||||
$error = console.error;
|
$error = console.error;
|
||||||
});
|
});
|
||||||
afterEach(function() {
|
afterEach(function () {
|
||||||
console.log = $log;
|
console.log = $log;
|
||||||
console.info = $info;
|
console.info = $info;
|
||||||
console.error = $error;
|
console.error = $error;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call logger at default level', function() {
|
it('should call logger at default level', function () {
|
||||||
var levelArg, logArg;
|
var levelArg, logArg;
|
||||||
handlebarsEnv.log = function(level, arg) {
|
handlebarsEnv.log = function (level, arg) {
|
||||||
levelArg = level;
|
levelArg = level;
|
||||||
logArg = arg;
|
logArg = arg;
|
||||||
};
|
};
|
||||||
@@ -581,9 +590,9 @@ describe('builtin helpers', function() {
|
|||||||
equals('whee', logArg, "should call log with 'whee'");
|
equals('whee', logArg, "should call log with 'whee'");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call logger at data level', function() {
|
it('should call logger at data level', function () {
|
||||||
var levelArg, logArg;
|
var levelArg, logArg;
|
||||||
handlebarsEnv.log = function(level, arg) {
|
handlebarsEnv.log = function (level, arg) {
|
||||||
levelArg = level;
|
levelArg = level;
|
||||||
logArg = arg;
|
logArg = arg;
|
||||||
};
|
};
|
||||||
@@ -597,16 +606,16 @@ describe('builtin helpers', function() {
|
|||||||
equals('whee', logArg);
|
equals('whee', logArg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should output to info', function() {
|
it('should output to info', function () {
|
||||||
var called;
|
var called;
|
||||||
|
|
||||||
console.info = function(info) {
|
console.info = function (info) {
|
||||||
equals('whee', info);
|
equals('whee', info);
|
||||||
called = true;
|
called = true;
|
||||||
console.info = $info;
|
console.info = $info;
|
||||||
console.log = $log;
|
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.info = $info;
|
||||||
@@ -619,10 +628,10 @@ describe('builtin helpers', function() {
|
|||||||
equals(true, called);
|
equals(true, called);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should log at data level', function() {
|
it('should log at data level', function () {
|
||||||
var called;
|
var called;
|
||||||
|
|
||||||
console.error = function(log) {
|
console.error = function (log) {
|
||||||
equals('whee', log);
|
equals('whee', log);
|
||||||
called = true;
|
called = true;
|
||||||
console.error = $error;
|
console.error = $error;
|
||||||
@@ -636,11 +645,11 @@ describe('builtin helpers', function() {
|
|||||||
equals(true, called);
|
equals(true, called);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle missing logger', function() {
|
it('should handle missing logger', function () {
|
||||||
var called = false;
|
var called = false;
|
||||||
|
|
||||||
console.error = undefined;
|
console.error = undefined;
|
||||||
console.log = function(log) {
|
console.log = function (log) {
|
||||||
equals('whee', log);
|
equals('whee', log);
|
||||||
called = true;
|
called = true;
|
||||||
console.log = $log;
|
console.log = $log;
|
||||||
@@ -654,10 +663,10 @@ describe('builtin helpers', function() {
|
|||||||
equals(true, called);
|
equals(true, called);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle string log levels', function() {
|
it('should handle string log levels', function () {
|
||||||
var called;
|
var called;
|
||||||
|
|
||||||
console.error = function(log) {
|
console.error = function (log) {
|
||||||
equals('whee', log);
|
equals('whee', log);
|
||||||
called = true;
|
called = true;
|
||||||
};
|
};
|
||||||
@@ -679,10 +688,10 @@ describe('builtin helpers', function() {
|
|||||||
equals(true, called);
|
equals(true, called);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle hash log levels', function() {
|
it('should handle hash log levels', function () {
|
||||||
var called;
|
var called;
|
||||||
|
|
||||||
console.error = function(log) {
|
console.error = function (log) {
|
||||||
equals('whee', log);
|
equals('whee', log);
|
||||||
called = true;
|
called = true;
|
||||||
};
|
};
|
||||||
@@ -693,10 +702,14 @@ describe('builtin helpers', function() {
|
|||||||
equals(true, called);
|
equals(true, called);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle hash log levels', function() {
|
it('should handle hash log levels', function () {
|
||||||
var called = false;
|
var called = false;
|
||||||
|
|
||||||
console.info = console.log = console.error = console.debug = function() {
|
console.info =
|
||||||
|
console.log =
|
||||||
|
console.error =
|
||||||
|
console.debug =
|
||||||
|
function () {
|
||||||
called = true;
|
called = true;
|
||||||
console.info = console.log = console.error = console.debug = $log;
|
console.info = console.log = console.error = console.debug = $log;
|
||||||
};
|
};
|
||||||
@@ -707,10 +720,10 @@ describe('builtin helpers', function() {
|
|||||||
equals(false, called);
|
equals(false, called);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should pass multiple log arguments', function() {
|
it('should pass multiple log arguments', function () {
|
||||||
var called;
|
var called;
|
||||||
|
|
||||||
console.info = console.log = function(log1, log2, log3) {
|
console.info = console.log = function (log1, log2, log3) {
|
||||||
equals('whee', log1);
|
equals('whee', log1);
|
||||||
equals('foo', log2);
|
equals('foo', log2);
|
||||||
equals(1, log3);
|
equals(1, log3);
|
||||||
@@ -724,31 +737,29 @@ describe('builtin helpers', function() {
|
|||||||
equals(true, called);
|
equals(true, called);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should pass zero log arguments', function() {
|
it('should pass zero log arguments', function () {
|
||||||
var called;
|
var called;
|
||||||
|
|
||||||
console.info = console.log = function() {
|
console.info = console.log = function () {
|
||||||
expect(arguments.length).to.equal(0);
|
expect(arguments.length).to.equal(0);
|
||||||
called = true;
|
called = true;
|
||||||
console.log = $log;
|
console.log = $log;
|
||||||
};
|
};
|
||||||
|
|
||||||
expectTemplate('{{log}}')
|
expectTemplate('{{log}}').withInput({ blah: 'whee' }).toCompileTo('');
|
||||||
.withInput({ blah: 'whee' })
|
|
||||||
.toCompileTo('');
|
|
||||||
expect(called).to.be.true();
|
expect(called).to.be.true();
|
||||||
});
|
});
|
||||||
/* eslint-enable no-console */
|
/* eslint-enable no-console */
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#lookup', function() {
|
describe('#lookup', function () {
|
||||||
it('should lookup arbitrary content', function() {
|
it('should lookup arbitrary content', function () {
|
||||||
expectTemplate('{{#each goodbyes}}{{lookup ../data .}}{{/each}}')
|
expectTemplate('{{#each goodbyes}}{{lookup ../data .}}{{/each}}')
|
||||||
.withInput({ goodbyes: [0, 1], data: ['foo', 'bar'] })
|
.withInput({ goodbyes: [0, 1], data: ['foo', 'bar'] })
|
||||||
.toCompileTo('foobar');
|
.toCompileTo('foobar');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not fail on undefined value', function() {
|
it('should not fail on undefined value', function () {
|
||||||
expectTemplate('{{#each goodbyes}}{{lookup ../bar .}}{{/each}}')
|
expectTemplate('{{#each goodbyes}}{{lookup ../bar .}}{{/each}}')
|
||||||
.withInput({ goodbyes: [0, 1], data: ['foo', 'bar'] })
|
.withInput({ goodbyes: [0, 1], data: ['foo', 'bar'] })
|
||||||
.toCompileTo('');
|
.toCompileTo('');
|
||||||
|
|||||||
+41
-24
@@ -1,15 +1,15 @@
|
|||||||
describe('compiler', function() {
|
describe('compiler', function () {
|
||||||
if (!Handlebars.compile) {
|
if (!Handlebars.compile) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('#equals', function() {
|
describe('#equals', function () {
|
||||||
function compile(string) {
|
function compile(string) {
|
||||||
var ast = Handlebars.parse(string);
|
var ast = Handlebars.parse(string);
|
||||||
return new Handlebars.Compiler().compile(ast, {});
|
return new Handlebars.Compiler().compile(ast, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should treat as equal', function() {
|
it('should treat as equal', function () {
|
||||||
equal(compile('foo').equals(compile('foo')), true);
|
equal(compile('foo').equals(compile('foo')), true);
|
||||||
equal(compile('{{foo}}').equals(compile('{{foo}}')), true);
|
equal(compile('{{foo}}').equals(compile('{{foo}}')), true);
|
||||||
equal(compile('{{foo.bar}}').equals(compile('{{foo.bar}}')), true);
|
equal(compile('{{foo.bar}}').equals(compile('{{foo.bar}}')), true);
|
||||||
@@ -30,7 +30,7 @@ describe('compiler', function() {
|
|||||||
true
|
true
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should treat as not equal', function() {
|
it('should treat as not equal', function () {
|
||||||
equal(compile('foo').equals(compile('bar')), false);
|
equal(compile('foo').equals(compile('bar')), false);
|
||||||
equal(compile('{{foo}}').equals(compile('{{bar}}')), false);
|
equal(compile('{{foo}}').equals(compile('{{bar}}')), false);
|
||||||
equal(compile('{{foo.bar}}').equals(compile('{{bar.bar}}')), false);
|
equal(compile('{{foo.bar}}').equals(compile('{{bar.bar}}')), false);
|
||||||
@@ -59,17 +59,17 @@ describe('compiler', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#compile', function() {
|
describe('#compile', function () {
|
||||||
it('should fail with invalid input', function() {
|
it('should fail with invalid input', function () {
|
||||||
shouldThrow(
|
shouldThrow(
|
||||||
function() {
|
function () {
|
||||||
Handlebars.compile(null);
|
Handlebars.compile(null);
|
||||||
},
|
},
|
||||||
Error,
|
Error,
|
||||||
'You must pass a string or Handlebars AST to Handlebars.compile. You passed null'
|
'You must pass a string or Handlebars AST to Handlebars.compile. You passed null'
|
||||||
);
|
);
|
||||||
shouldThrow(
|
shouldThrow(
|
||||||
function() {
|
function () {
|
||||||
Handlebars.compile({});
|
Handlebars.compile({});
|
||||||
},
|
},
|
||||||
Error,
|
Error,
|
||||||
@@ -77,7 +77,7 @@ describe('compiler', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should include the location in the error (row and column)', function() {
|
it('should include the location in the error (row and column)', function () {
|
||||||
try {
|
try {
|
||||||
Handlebars.compile(' \n {{#if}}\n{{/def}}')();
|
Handlebars.compile(' \n {{#if}}\n{{/def}}')();
|
||||||
equal(
|
equal(
|
||||||
@@ -101,7 +101,7 @@ describe('compiler', function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should include the location as enumerable property', function() {
|
it('should include the location as enumerable property', function () {
|
||||||
try {
|
try {
|
||||||
Handlebars.compile(' \n {{#if}}\n{{/def}}')();
|
Handlebars.compile(' \n {{#if}}\n{{/def}}')();
|
||||||
equal(
|
equal(
|
||||||
@@ -118,21 +118,38 @@ describe('compiler', function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can utilize AST instance', function() {
|
it('can utilize AST instance', function () {
|
||||||
equal(
|
equal(
|
||||||
Handlebars.compile({
|
Handlebars.compile({
|
||||||
type: 'Program',
|
type: 'Program',
|
||||||
body: [{ type: 'ContentStatement', value: 'Hello' }]
|
body: [{ type: 'ContentStatement', value: 'Hello' }],
|
||||||
})(),
|
})(),
|
||||||
'Hello'
|
'Hello'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can pass through an empty string', function() {
|
it('can pass through an empty string', function () {
|
||||||
equal(Handlebars.compile('')(), '');
|
equal(Handlebars.compile('')(), '');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not modify the options.data property(GH-1327)', function() {
|
it('throws on desupported options', function () {
|
||||||
|
shouldThrow(
|
||||||
|
function () {
|
||||||
|
Handlebars.compile('Dudes', { trackIds: true });
|
||||||
|
},
|
||||||
|
Error,
|
||||||
|
'TrackIds and stringParams are no longer supported. See Github #1145'
|
||||||
|
);
|
||||||
|
shouldThrow(
|
||||||
|
function () {
|
||||||
|
Handlebars.compile('Dudes', { stringParams: true });
|
||||||
|
},
|
||||||
|
Error,
|
||||||
|
'TrackIds and stringParams are no longer supported. See Github #1145'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not modify the options.data property(GH-1327)', function () {
|
||||||
var options = { data: [{ a: 'foo' }, { a: 'bar' }] };
|
var options = { data: [{ a: 'foo' }, { a: 'bar' }] };
|
||||||
Handlebars.compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)();
|
Handlebars.compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)();
|
||||||
equal(
|
equal(
|
||||||
@@ -141,7 +158,7 @@ describe('compiler', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not modify the options.knownHelpers property(GH-1327)', function() {
|
it('should not modify the options.knownHelpers property(GH-1327)', function () {
|
||||||
var options = { knownHelpers: {} };
|
var options = { knownHelpers: {} };
|
||||||
Handlebars.compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)();
|
Handlebars.compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)();
|
||||||
equal(
|
equal(
|
||||||
@@ -151,37 +168,37 @@ describe('compiler', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#precompile', function() {
|
describe('#precompile', function () {
|
||||||
it('should fail with invalid input', function() {
|
it('should fail with invalid input', function () {
|
||||||
shouldThrow(
|
shouldThrow(
|
||||||
function() {
|
function () {
|
||||||
Handlebars.precompile(null);
|
Handlebars.precompile(null);
|
||||||
},
|
},
|
||||||
Error,
|
Error,
|
||||||
'You must pass a string or Handlebars AST to Handlebars.precompile. You passed null'
|
'You must pass a string or Handlebars AST to Handlebars.compile. You passed null'
|
||||||
);
|
);
|
||||||
shouldThrow(
|
shouldThrow(
|
||||||
function() {
|
function () {
|
||||||
Handlebars.precompile({});
|
Handlebars.precompile({});
|
||||||
},
|
},
|
||||||
Error,
|
Error,
|
||||||
'You must pass a string or Handlebars AST to Handlebars.precompile. You passed [object Object]'
|
'You must pass a string or Handlebars AST to Handlebars.compile. You passed [object Object]'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can utilize AST instance', function() {
|
it('can utilize AST instance', function () {
|
||||||
equal(
|
equal(
|
||||||
/return "Hello"/.test(
|
/return "Hello"/.test(
|
||||||
Handlebars.precompile({
|
Handlebars.precompile({
|
||||||
type: 'Program',
|
type: 'Program',
|
||||||
body: [{ type: 'ContentStatement', value: 'Hello' }]
|
body: [{ type: 'ContentStatement', value: 'Hello' }],
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can pass through an empty string', function() {
|
it('can pass through an empty string', function () {
|
||||||
equal(/return ""/.test(Handlebars.precompile('')), true);
|
equal(/return ""/.test(Handlebars.precompile('')), true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+55
-55
@@ -1,8 +1,8 @@
|
|||||||
describe('data', function() {
|
describe('data', function () {
|
||||||
it('passing in data to a compiled function that expects data - works with helpers', function() {
|
it('passing in data to a compiled function that expects data - works with helpers', function () {
|
||||||
expectTemplate('{{hello}}')
|
expectTemplate('{{hello}}')
|
||||||
.withCompileOptions({ data: true })
|
.withCompileOptions({ data: true })
|
||||||
.withHelper('hello', function(options) {
|
.withHelper('hello', function (options) {
|
||||||
return options.data.adjective + ' ' + this.noun;
|
return options.data.adjective + ' ' + this.noun;
|
||||||
})
|
})
|
||||||
.withRuntimeOptions({ data: { adjective: 'happy' } })
|
.withRuntimeOptions({ data: { adjective: 'happy' } })
|
||||||
@@ -11,17 +11,17 @@ describe('data', function() {
|
|||||||
.toCompileTo('happy cat');
|
.toCompileTo('happy cat');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('data can be looked up via @foo', function() {
|
it('data can be looked up via @foo', function () {
|
||||||
expectTemplate('{{@hello}}')
|
expectTemplate('{{@hello}}')
|
||||||
.withRuntimeOptions({ data: { hello: 'hello' } })
|
.withRuntimeOptions({ data: { hello: 'hello' } })
|
||||||
.withMessage('@foo retrieves template data')
|
.withMessage('@foo retrieves template data')
|
||||||
.toCompileTo('hello');
|
.toCompileTo('hello');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('deep @foo triggers automatic top-level data', function() {
|
it('deep @foo triggers automatic top-level data', function () {
|
||||||
var helpers = Handlebars.createFrame(handlebarsEnv.helpers);
|
var helpers = Handlebars.createFrame(handlebarsEnv.helpers);
|
||||||
|
|
||||||
helpers.let = function(options) {
|
helpers.let = function (options) {
|
||||||
var frame = Handlebars.createFrame(options.data);
|
var frame = Handlebars.createFrame(options.data);
|
||||||
|
|
||||||
for (var prop in options.hash) {
|
for (var prop in options.hash) {
|
||||||
@@ -41,83 +41,83 @@ describe('data', function() {
|
|||||||
.toCompileTo('Hello world');
|
.toCompileTo('Hello world');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('parameter data can be looked up via @foo', function() {
|
it('parameter data can be looked up via @foo', function () {
|
||||||
expectTemplate('{{hello @world}}')
|
expectTemplate('{{hello @world}}')
|
||||||
.withRuntimeOptions({ data: { world: 'world' } })
|
.withRuntimeOptions({ data: { world: 'world' } })
|
||||||
.withHelper('hello', function(noun) {
|
.withHelper('hello', function (noun) {
|
||||||
return 'Hello ' + noun;
|
return 'Hello ' + noun;
|
||||||
})
|
})
|
||||||
.withMessage('@foo as a parameter retrieves template data')
|
.withMessage('@foo as a parameter retrieves template data')
|
||||||
.toCompileTo('Hello world');
|
.toCompileTo('Hello world');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('hash values can be looked up via @foo', function() {
|
it('hash values can be looked up via @foo', function () {
|
||||||
expectTemplate('{{hello noun=@world}}')
|
expectTemplate('{{hello noun=@world}}')
|
||||||
.withRuntimeOptions({ data: { world: 'world' } })
|
.withRuntimeOptions({ data: { world: 'world' } })
|
||||||
.withHelper('hello', function(options) {
|
.withHelper('hello', function (options) {
|
||||||
return 'Hello ' + options.hash.noun;
|
return 'Hello ' + options.hash.noun;
|
||||||
})
|
})
|
||||||
.withMessage('@foo as a parameter retrieves template data')
|
.withMessage('@foo as a parameter retrieves template data')
|
||||||
.toCompileTo('Hello world');
|
.toCompileTo('Hello world');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('nested parameter data can be looked up via @foo.bar', function() {
|
it('nested parameter data can be looked up via @foo.bar', function () {
|
||||||
expectTemplate('{{hello @world.bar}}')
|
expectTemplate('{{hello @world.bar}}')
|
||||||
.withRuntimeOptions({ data: { world: { bar: 'world' } } })
|
.withRuntimeOptions({ data: { world: { bar: 'world' } } })
|
||||||
.withHelper('hello', function(noun) {
|
.withHelper('hello', function (noun) {
|
||||||
return 'Hello ' + noun;
|
return 'Hello ' + noun;
|
||||||
})
|
})
|
||||||
.withMessage('@foo as a parameter retrieves template data')
|
.withMessage('@foo as a parameter retrieves template data')
|
||||||
.toCompileTo('Hello world');
|
.toCompileTo('Hello world');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('nested parameter data does not fail with @world.bar', function() {
|
it('nested parameter data does not fail with @world.bar', function () {
|
||||||
expectTemplate('{{hello @world.bar}}')
|
expectTemplate('{{hello @world.bar}}')
|
||||||
.withRuntimeOptions({ data: { foo: { bar: 'world' } } })
|
.withRuntimeOptions({ data: { foo: { bar: 'world' } } })
|
||||||
.withHelper('hello', function(noun) {
|
.withHelper('hello', function (noun) {
|
||||||
return 'Hello ' + noun;
|
return 'Hello ' + noun;
|
||||||
})
|
})
|
||||||
.withMessage('@foo as a parameter retrieves template data')
|
.withMessage('@foo as a parameter retrieves template data')
|
||||||
.toCompileTo('Hello undefined');
|
.toCompileTo('Hello undefined');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('parameter data throws when using complex scope references', function() {
|
it('parameter data throws when using complex scope references', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#goodbyes}}{{text}} cruel {{@foo/../name}}! {{/goodbyes}}'
|
'{{#goodbyes}}{{text}} cruel {{@foo/../name}}! {{/goodbyes}}'
|
||||||
).toThrow(Error);
|
).toThrow(Error);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('data can be functions', function() {
|
it('data can be functions', function () {
|
||||||
expectTemplate('{{@hello}}')
|
expectTemplate('{{@hello}}')
|
||||||
.withRuntimeOptions({
|
.withRuntimeOptions({
|
||||||
data: {
|
data: {
|
||||||
hello: function() {
|
hello: function () {
|
||||||
return 'hello';
|
return 'hello';
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('hello');
|
.toCompileTo('hello');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('data can be functions with params', function() {
|
it('data can be functions with params', function () {
|
||||||
expectTemplate('{{@hello "hello"}}')
|
expectTemplate('{{@hello "hello"}}')
|
||||||
.withRuntimeOptions({
|
.withRuntimeOptions({
|
||||||
data: {
|
data: {
|
||||||
hello: function(arg) {
|
hello: function (arg) {
|
||||||
return arg;
|
return arg;
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('hello');
|
.toCompileTo('hello');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('data is inherited downstream', function() {
|
it('data is inherited downstream', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#let foo=1 bar=2}}{{#let foo=bar.baz}}{{@bar}}{{@foo}}{{/let}}{{@foo}}{{/let}}'
|
'{{#let foo=1 bar=2}}{{#let foo=bar.baz}}{{@bar}}{{@foo}}{{/let}}{{@foo}}{{/let}}'
|
||||||
)
|
)
|
||||||
.withInput({ bar: { baz: 'hello world' } })
|
.withInput({ bar: { baz: 'hello world' } })
|
||||||
.withCompileOptions({ data: true })
|
.withCompileOptions({ data: true })
|
||||||
.withHelper('let', function(options) {
|
.withHelper('let', function (options) {
|
||||||
var frame = Handlebars.createFrame(options.data);
|
var frame = Handlebars.createFrame(options.data);
|
||||||
for (var prop in options.hash) {
|
for (var prop in options.hash) {
|
||||||
if (prop in options.hash) {
|
if (prop in options.hash) {
|
||||||
@@ -131,11 +131,11 @@ describe('data', function() {
|
|||||||
.toCompileTo('2hello world1');
|
.toCompileTo('2hello world1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('passing in data to a compiled function that expects data - works with helpers in partials', function() {
|
it('passing in data to a compiled function that expects data - works with helpers in partials', function () {
|
||||||
expectTemplate('{{>myPartial}}')
|
expectTemplate('{{>myPartial}}')
|
||||||
.withCompileOptions({ data: true })
|
.withCompileOptions({ data: true })
|
||||||
.withPartial('myPartial', '{{hello}}')
|
.withPartial('myPartial', '{{hello}}')
|
||||||
.withHelper('hello', function(options) {
|
.withHelper('hello', function (options) {
|
||||||
return options.data.adjective + ' ' + this.noun;
|
return options.data.adjective + ' ' + this.noun;
|
||||||
})
|
})
|
||||||
.withInput({ noun: 'cat' })
|
.withInput({ noun: 'cat' })
|
||||||
@@ -144,10 +144,10 @@ describe('data', function() {
|
|||||||
.toCompileTo('happy cat');
|
.toCompileTo('happy cat');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('passing in data to a compiled function that expects data - works with helpers and parameters', function() {
|
it('passing in data to a compiled function that expects data - works with helpers and parameters', function () {
|
||||||
expectTemplate('{{hello world}}')
|
expectTemplate('{{hello world}}')
|
||||||
.withCompileOptions({ data: true })
|
.withCompileOptions({ data: true })
|
||||||
.withHelper('hello', function(noun, options) {
|
.withHelper('hello', function (noun, options) {
|
||||||
return options.data.adjective + ' ' + noun + (this.exclaim ? '!' : '');
|
return options.data.adjective + ' ' + noun + (this.exclaim ? '!' : '');
|
||||||
})
|
})
|
||||||
.withInput({ exclaim: true, world: 'world' })
|
.withInput({ exclaim: true, world: 'world' })
|
||||||
@@ -156,15 +156,15 @@ describe('data', function() {
|
|||||||
.toCompileTo('happy world!');
|
.toCompileTo('happy world!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('passing in data to a compiled function that expects data - works with block helpers', function() {
|
it('passing in data to a compiled function that expects data - works with block helpers', function () {
|
||||||
expectTemplate('{{#hello}}{{world}}{{/hello}}')
|
expectTemplate('{{#hello}}{{world}}{{/hello}}')
|
||||||
.withCompileOptions({
|
.withCompileOptions({
|
||||||
data: true
|
data: true,
|
||||||
})
|
})
|
||||||
.withHelper('hello', function(options) {
|
.withHelper('hello', function (options) {
|
||||||
return options.fn(this);
|
return options.fn(this);
|
||||||
})
|
})
|
||||||
.withHelper('world', function(options) {
|
.withHelper('world', function (options) {
|
||||||
return options.data.adjective + ' world' + (this.exclaim ? '!' : '');
|
return options.data.adjective + ' world' + (this.exclaim ? '!' : '');
|
||||||
})
|
})
|
||||||
.withInput({ exclaim: true })
|
.withInput({ exclaim: true })
|
||||||
@@ -173,13 +173,13 @@ describe('data', function() {
|
|||||||
.toCompileTo('happy world!');
|
.toCompileTo('happy world!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('passing in data to a compiled function that expects data - works with block helpers that use ..', function() {
|
it('passing in data to a compiled function that expects data - works with block helpers that use ..', function () {
|
||||||
expectTemplate('{{#hello}}{{world ../zomg}}{{/hello}}')
|
expectTemplate('{{#hello}}{{world ../zomg}}{{/hello}}')
|
||||||
.withCompileOptions({ data: true })
|
.withCompileOptions({ data: true })
|
||||||
.withHelper('hello', function(options) {
|
.withHelper('hello', function (options) {
|
||||||
return options.fn({ exclaim: '?' });
|
return options.fn({ exclaim: '?' });
|
||||||
})
|
})
|
||||||
.withHelper('world', function(thing, options) {
|
.withHelper('world', function (thing, options) {
|
||||||
return options.data.adjective + ' ' + thing + (this.exclaim || '');
|
return options.data.adjective + ' ' + thing + (this.exclaim || '');
|
||||||
})
|
})
|
||||||
.withInput({ exclaim: true, zomg: 'world' })
|
.withInput({ exclaim: true, zomg: 'world' })
|
||||||
@@ -188,13 +188,13 @@ describe('data', function() {
|
|||||||
.toCompileTo('happy world?');
|
.toCompileTo('happy world?');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('passing in data to a compiled function that expects data - data is passed to with block helpers where children use ..', function() {
|
it('passing in data to a compiled function that expects data - data is passed to with block helpers where children use ..', function () {
|
||||||
expectTemplate('{{#hello}}{{world ../zomg}}{{/hello}}')
|
expectTemplate('{{#hello}}{{world ../zomg}}{{/hello}}')
|
||||||
.withCompileOptions({ data: true })
|
.withCompileOptions({ data: true })
|
||||||
.withHelper('hello', function(options) {
|
.withHelper('hello', function (options) {
|
||||||
return options.data.accessData + ' ' + options.fn({ exclaim: '?' });
|
return options.data.accessData + ' ' + options.fn({ exclaim: '?' });
|
||||||
})
|
})
|
||||||
.withHelper('world', function(thing, options) {
|
.withHelper('world', function (thing, options) {
|
||||||
return options.data.adjective + ' ' + thing + (this.exclaim || '');
|
return options.data.adjective + ' ' + thing + (this.exclaim || '');
|
||||||
})
|
})
|
||||||
.withInput({ exclaim: true, zomg: 'world' })
|
.withInput({ exclaim: true, zomg: 'world' })
|
||||||
@@ -203,41 +203,41 @@ describe('data', function() {
|
|||||||
.toCompileTo('#win happy world?');
|
.toCompileTo('#win happy world?');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('you can override inherited data when invoking a helper', function() {
|
it('you can override inherited data when invoking a helper', function () {
|
||||||
expectTemplate('{{#hello}}{{world zomg}}{{/hello}}')
|
expectTemplate('{{#hello}}{{world zomg}}{{/hello}}')
|
||||||
.withCompileOptions({ data: true })
|
.withCompileOptions({ data: true })
|
||||||
.withHelper('hello', function(options) {
|
.withHelper('hello', function (options) {
|
||||||
return options.fn(
|
return options.fn(
|
||||||
{ exclaim: '?', zomg: 'world' },
|
{ exclaim: '?', zomg: 'world' },
|
||||||
{ data: { adjective: 'sad' } }
|
{ data: { adjective: 'sad' } }
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.withHelper('world', function(thing, options) {
|
.withHelper('world', function (thing, options) {
|
||||||
return options.data.adjective + ' ' + thing + (this.exclaim || '');
|
return options.data.adjective + ' ' + thing + (this.exclaim || '');
|
||||||
})
|
})
|
||||||
.withInput({ exclaim: true, zomg: 'planet' })
|
.withInput({ exclaim: true, zomg: 'planet' })
|
||||||
.withRuntimeOptions({ data: { adjective: 'happy' } })
|
.withRuntimeOptions({ data: { adjective: 'happy' } })
|
||||||
.withMessage('Overriden data output by helper')
|
.withMessage('Overridden data output by helper')
|
||||||
.toCompileTo('sad world?');
|
.toCompileTo('sad world?');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('you can override inherited data when invoking a helper with depth', function() {
|
it('you can override inherited data when invoking a helper with depth', function () {
|
||||||
expectTemplate('{{#hello}}{{world ../zomg}}{{/hello}}')
|
expectTemplate('{{#hello}}{{world ../zomg}}{{/hello}}')
|
||||||
.withCompileOptions({ data: true })
|
.withCompileOptions({ data: true })
|
||||||
.withHelper('hello', function(options) {
|
.withHelper('hello', function (options) {
|
||||||
return options.fn({ exclaim: '?' }, { data: { adjective: 'sad' } });
|
return options.fn({ exclaim: '?' }, { data: { adjective: 'sad' } });
|
||||||
})
|
})
|
||||||
.withHelper('world', function(thing, options) {
|
.withHelper('world', function (thing, options) {
|
||||||
return options.data.adjective + ' ' + thing + (this.exclaim || '');
|
return options.data.adjective + ' ' + thing + (this.exclaim || '');
|
||||||
})
|
})
|
||||||
.withInput({ exclaim: true, zomg: 'world' })
|
.withInput({ exclaim: true, zomg: 'world' })
|
||||||
.withRuntimeOptions({ data: { adjective: 'happy' } })
|
.withRuntimeOptions({ data: { adjective: 'happy' } })
|
||||||
.withMessage('Overriden data output by helper')
|
.withMessage('Overridden data output by helper')
|
||||||
.toCompileTo('sad world?');
|
.toCompileTo('sad world?');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('@root', function() {
|
describe('@root', function () {
|
||||||
it('the root context can be looked up via @root', function() {
|
it('the root context can be looked up via @root', function () {
|
||||||
expectTemplate('{{@root.foo}}')
|
expectTemplate('{{@root.foo}}')
|
||||||
.withInput({ foo: 'hello' })
|
.withInput({ foo: 'hello' })
|
||||||
.withRuntimeOptions({ data: {} })
|
.withRuntimeOptions({ data: {} })
|
||||||
@@ -248,7 +248,7 @@ describe('data', function() {
|
|||||||
.toCompileTo('hello');
|
.toCompileTo('hello');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('passed root values take priority', function() {
|
it('passed root values take priority', function () {
|
||||||
expectTemplate('{{@root.foo}}')
|
expectTemplate('{{@root.foo}}')
|
||||||
.withInput({ foo: 'should not be used' })
|
.withInput({ foo: 'should not be used' })
|
||||||
.withRuntimeOptions({ data: { root: { foo: 'hello' } } })
|
.withRuntimeOptions({ data: { root: { foo: 'hello' } } })
|
||||||
@@ -256,21 +256,21 @@ describe('data', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('nesting', function() {
|
describe('nesting', function () {
|
||||||
it('the root context can be looked up via @root', function() {
|
it('the root context can be looked up via @root', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#helper}}{{#helper}}{{@./depth}} {{@../depth}} {{@../../depth}}{{/helper}}{{/helper}}'
|
'{{#helper}}{{#helper}}{{@./depth}} {{@../depth}} {{@../../depth}}{{/helper}}{{/helper}}'
|
||||||
)
|
)
|
||||||
.withInput({ foo: 'hello' })
|
.withInput({ foo: 'hello' })
|
||||||
.withHelper('helper', function(options) {
|
.withHelper('helper', function (options) {
|
||||||
var frame = Handlebars.createFrame(options.data);
|
var frame = Handlebars.createFrame(options.data);
|
||||||
frame.depth = options.data.depth + 1;
|
frame.depth = options.data.depth + 1;
|
||||||
return options.fn(this, { data: frame });
|
return options.fn(this, { data: frame });
|
||||||
})
|
})
|
||||||
.withRuntimeOptions({
|
.withRuntimeOptions({
|
||||||
data: {
|
data: {
|
||||||
depth: 0
|
depth: 0,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('2 1 0');
|
.toCompileTo('2 1 0');
|
||||||
});
|
});
|
||||||
|
|||||||
Vendored
+3
-3
@@ -26,13 +26,13 @@ vm.runInThisContext(distHandlebars, filename);
|
|||||||
global.CompilerContext = {
|
global.CompilerContext = {
|
||||||
browser: true,
|
browser: true,
|
||||||
|
|
||||||
compile: function(template, options) {
|
compile: function (template, options) {
|
||||||
var templateSpec = handlebarsEnv.precompile(template, options);
|
var templateSpec = handlebarsEnv.precompile(template, options);
|
||||||
return handlebarsEnv.template(safeEval(templateSpec));
|
return handlebarsEnv.template(safeEval(templateSpec));
|
||||||
},
|
},
|
||||||
compileWithPartial: function(template, options) {
|
compileWithPartial: function (template, options) {
|
||||||
return handlebarsEnv.compile(template, options);
|
return handlebarsEnv.compile(template, options);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function safeEval(templateSpec) {
|
function safeEval(templateSpec) {
|
||||||
|
|||||||
Vendored
+24
-24
@@ -1,4 +1,4 @@
|
|||||||
var global = (function() {
|
var global = (function () {
|
||||||
return this;
|
return this;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ if (Error.captureStackTrace) {
|
|||||||
/**
|
/**
|
||||||
* @deprecated Use "expectTemplate(template)...toCompileTo(output)" instead
|
* @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);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ global.shouldCompileToWithPartials = function shouldCompileToWithPartials(
|
|||||||
/**
|
/**
|
||||||
* @deprecated Use "expectTemplate(template)...toCompileTo(output)" instead
|
* @deprecated Use "expectTemplate(template)...toCompileTo(output)" instead
|
||||||
*/
|
*/
|
||||||
global.compileWithPartials = function(string, hashOrArray, partials) {
|
global.compileWithPartials = function (string, hashOrArray, partials) {
|
||||||
var template, ary, options;
|
var template, ary, options;
|
||||||
if (hashOrArray && hashOrArray.hash) {
|
if (hashOrArray && hashOrArray.hash) {
|
||||||
ary = [hashOrArray.hash, hashOrArray];
|
ary = [hashOrArray.hash, hashOrArray];
|
||||||
@@ -92,7 +92,7 @@ global.equals = global.equal = function equals(a, b, msg) {
|
|||||||
* @deprecated Use chai's expect-style API instead (`expect(actualValue).to.equal(expectedValue)`)
|
* @deprecated Use chai's expect-style API instead (`expect(actualValue).to.equal(expectedValue)`)
|
||||||
* @see https://www.chaijs.com/api/bdd/#method_throw
|
* @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 {
|
||||||
callback();
|
callback();
|
||||||
@@ -121,7 +121,7 @@ global.shouldThrow = function(callback, type, msg) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
global.expectTemplate = function(templateAsString) {
|
global.expectTemplate = function (templateAsString) {
|
||||||
return new HandlebarsTestBench(templateAsString);
|
return new HandlebarsTestBench(templateAsString);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -137,38 +137,38 @@ function HandlebarsTestBench(templateAsString) {
|
|||||||
this.runtimeOptions = {};
|
this.runtimeOptions = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
HandlebarsTestBench.prototype.withInput = function(input) {
|
HandlebarsTestBench.prototype.withInput = function (input) {
|
||||||
this.input = input;
|
this.input = input;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
HandlebarsTestBench.prototype.withHelper = function(name, helperFunction) {
|
HandlebarsTestBench.prototype.withHelper = function (name, helperFunction) {
|
||||||
this.helpers[name] = helperFunction;
|
this.helpers[name] = helperFunction;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
HandlebarsTestBench.prototype.withHelpers = function(helperFunctions) {
|
HandlebarsTestBench.prototype.withHelpers = function (helperFunctions) {
|
||||||
var self = this;
|
var self = this;
|
||||||
Object.keys(helperFunctions).forEach(function(name) {
|
Object.keys(helperFunctions).forEach(function (name) {
|
||||||
self.withHelper(name, helperFunctions[name]);
|
self.withHelper(name, helperFunctions[name]);
|
||||||
});
|
});
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
HandlebarsTestBench.prototype.withPartial = function(name, partialAsString) {
|
HandlebarsTestBench.prototype.withPartial = function (name, partialAsString) {
|
||||||
this.partials[name] = partialAsString;
|
this.partials[name] = partialAsString;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
HandlebarsTestBench.prototype.withPartials = function(partials) {
|
HandlebarsTestBench.prototype.withPartials = function (partials) {
|
||||||
var self = this;
|
var self = this;
|
||||||
Object.keys(partials).forEach(function(name) {
|
Object.keys(partials).forEach(function (name) {
|
||||||
self.withPartial(name, partials[name]);
|
self.withPartial(name, partials[name]);
|
||||||
});
|
});
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
HandlebarsTestBench.prototype.withDecorator = function(
|
HandlebarsTestBench.prototype.withDecorator = function (
|
||||||
name,
|
name,
|
||||||
decoratorFunction
|
decoratorFunction
|
||||||
) {
|
) {
|
||||||
@@ -176,30 +176,30 @@ HandlebarsTestBench.prototype.withDecorator = function(
|
|||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
HandlebarsTestBench.prototype.withDecorators = function(decorators) {
|
HandlebarsTestBench.prototype.withDecorators = function (decorators) {
|
||||||
var self = this;
|
var self = this;
|
||||||
Object.keys(decorators).forEach(function(name) {
|
Object.keys(decorators).forEach(function (name) {
|
||||||
self.withDecorator(name, decorators[name]);
|
self.withDecorator(name, decorators[name]);
|
||||||
});
|
});
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
HandlebarsTestBench.prototype.withCompileOptions = function(compileOptions) {
|
HandlebarsTestBench.prototype.withCompileOptions = function (compileOptions) {
|
||||||
this.compileOptions = compileOptions;
|
this.compileOptions = compileOptions;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
HandlebarsTestBench.prototype.withRuntimeOptions = function(runtimeOptions) {
|
HandlebarsTestBench.prototype.withRuntimeOptions = function (runtimeOptions) {
|
||||||
this.runtimeOptions = runtimeOptions;
|
this.runtimeOptions = runtimeOptions;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
HandlebarsTestBench.prototype.withMessage = function(message) {
|
HandlebarsTestBench.prototype.withMessage = function (message) {
|
||||||
this.message = message;
|
this.message = message;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
HandlebarsTestBench.prototype.toCompileTo = function(expectedOutputAsString) {
|
HandlebarsTestBench.prototype.toCompileTo = function (expectedOutputAsString) {
|
||||||
expect(this._compileAndExecute()).to.equal(
|
expect(this._compileAndExecute()).to.equal(
|
||||||
expectedOutputAsString,
|
expectedOutputAsString,
|
||||||
this.message
|
this.message
|
||||||
@@ -207,14 +207,14 @@ HandlebarsTestBench.prototype.toCompileTo = function(expectedOutputAsString) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// see chai "to.throw" (https://www.chaijs.com/api/bdd/#method_throw)
|
// see chai "to.throw" (https://www.chaijs.com/api/bdd/#method_throw)
|
||||||
HandlebarsTestBench.prototype.toThrow = function(errorLike, errMsgMatcher) {
|
HandlebarsTestBench.prototype.toThrow = function (errorLike, errMsgMatcher) {
|
||||||
var self = this;
|
var self = this;
|
||||||
expect(function() {
|
expect(function () {
|
||||||
self._compileAndExecute();
|
self._compileAndExecute();
|
||||||
}).to.throw(errorLike, errMsgMatcher, this.message);
|
}).to.throw(errorLike, errMsgMatcher, this.message);
|
||||||
};
|
};
|
||||||
|
|
||||||
HandlebarsTestBench.prototype._compileAndExecute = function() {
|
HandlebarsTestBench.prototype._compileAndExecute = function () {
|
||||||
var compile =
|
var compile =
|
||||||
Object.keys(this.partials).length > 0
|
Object.keys(this.partials).length > 0
|
||||||
? CompilerContext.compileWithPartial
|
? CompilerContext.compileWithPartial
|
||||||
@@ -226,10 +226,10 @@ HandlebarsTestBench.prototype._compileAndExecute = function() {
|
|||||||
return template(this.input, combinedRuntimeOptions);
|
return template(this.input, combinedRuntimeOptions);
|
||||||
};
|
};
|
||||||
|
|
||||||
HandlebarsTestBench.prototype._combineRuntimeOptions = function() {
|
HandlebarsTestBench.prototype._combineRuntimeOptions = function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
var combinedRuntimeOptions = {};
|
var combinedRuntimeOptions = {};
|
||||||
Object.keys(this.runtimeOptions).forEach(function(key) {
|
Object.keys(this.runtimeOptions).forEach(function (key) {
|
||||||
combinedRuntimeOptions[key] = self.runtimeOptions[key];
|
combinedRuntimeOptions[key] = self.runtimeOptions[key];
|
||||||
});
|
});
|
||||||
combinedRuntimeOptions.helpers = this.helpers;
|
combinedRuntimeOptions.helpers = this.helpers;
|
||||||
|
|||||||
Vendored
+3
-3
@@ -11,13 +11,13 @@ global.sinon = require('sinon');
|
|||||||
global.Handlebars = require('../../lib');
|
global.Handlebars = require('../../lib');
|
||||||
|
|
||||||
global.CompilerContext = {
|
global.CompilerContext = {
|
||||||
compile: function(template, options) {
|
compile: function (template, options) {
|
||||||
var templateSpec = handlebarsEnv.precompile(template, options);
|
var templateSpec = handlebarsEnv.precompile(template, options);
|
||||||
return handlebarsEnv.template(safeEval(templateSpec));
|
return handlebarsEnv.template(safeEval(templateSpec));
|
||||||
},
|
},
|
||||||
compileWithPartial: function(template, options) {
|
compileWithPartial: function (template, options) {
|
||||||
return handlebarsEnv.compile(template, options);
|
return handlebarsEnv.compile(template, options);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function safeEval(templateSpec) {
|
function safeEval(templateSpec) {
|
||||||
|
|||||||
Vendored
+9
-9
@@ -15,25 +15,25 @@ if (grep === '--min') {
|
|||||||
|
|
||||||
var files = fs
|
var files = fs
|
||||||
.readdirSync(testDir)
|
.readdirSync(testDir)
|
||||||
.filter(function(name) {
|
.filter(function (name) {
|
||||||
return /.*\.js$/.test(name);
|
return /.*\.js$/.test(name);
|
||||||
})
|
})
|
||||||
.map(function(name) {
|
.map(function (name) {
|
||||||
return testDir + path.sep + name;
|
return testDir + path.sep + name;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (global.minimizedTest) {
|
if (global.minimizedTest) {
|
||||||
run('./runtime', function() {
|
run('./runtime', function () {
|
||||||
run('./browser', function() {
|
run('./browser', function () {
|
||||||
/* eslint-disable no-process-exit */
|
/* eslint-disable no-process-exit */
|
||||||
process.exit(errors);
|
process.exit(errors);
|
||||||
/* eslint-enable no-process-exit */
|
/* eslint-enable no-process-exit */
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
run('./runtime', function() {
|
run('./runtime', function () {
|
||||||
run('./browser', function() {
|
run('./browser', function () {
|
||||||
run('./node', function() {
|
run('./node', function () {
|
||||||
/* eslint-disable no-process-exit */
|
/* eslint-disable no-process-exit */
|
||||||
process.exit(errors);
|
process.exit(errors);
|
||||||
/* eslint-enable no-process-exit */
|
/* eslint-enable no-process-exit */
|
||||||
@@ -50,13 +50,13 @@ function run(env, callback) {
|
|||||||
mocha.grep(grep);
|
mocha.grep(grep);
|
||||||
}
|
}
|
||||||
|
|
||||||
files.forEach(function(name) {
|
files.forEach(function (name) {
|
||||||
delete require.cache[name];
|
delete require.cache[name];
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('Running env: ' + env);
|
console.log('Running env: ' + env);
|
||||||
require(env);
|
require(env);
|
||||||
mocha.run(function(errorCount) {
|
mocha.run(function (errorCount) {
|
||||||
errors += errorCount;
|
errors += errorCount;
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
|
|||||||
Vendored
+9
-6
@@ -22,16 +22,19 @@ vm.runInThisContext(
|
|||||||
filename
|
filename
|
||||||
);
|
);
|
||||||
|
|
||||||
var parse = require('../../dist/cjs/handlebars/compiler/base').parse;
|
var parse = require('@handlebars/parser').parse;
|
||||||
var compiler = require('../../dist/cjs/handlebars/compiler/compiler');
|
var compiler = require('../../dist/cjs/handlebars/compiler/compiler');
|
||||||
var JavaScriptCompiler = require('../../dist/cjs/handlebars/compiler/javascript-compiler');
|
var JavaScriptCompiler = require('../../dist/cjs/handlebars/compiler/javascript-compiler');
|
||||||
|
|
||||||
global.CompilerContext = {
|
global.CompilerContext = {
|
||||||
browser: true,
|
browser: true,
|
||||||
|
|
||||||
compile: function(template, options) {
|
compile: function (template, options) {
|
||||||
// Hack the compiler on to the environment for these specific tests
|
// Hack the compiler on to the environment for these specific tests
|
||||||
handlebarsEnv.precompile = function(precompileTemplate, precompileOptions) {
|
handlebarsEnv.precompile = function (
|
||||||
|
precompileTemplate,
|
||||||
|
precompileOptions
|
||||||
|
) {
|
||||||
return compiler.precompile(
|
return compiler.precompile(
|
||||||
precompileTemplate,
|
precompileTemplate,
|
||||||
precompileOptions,
|
precompileOptions,
|
||||||
@@ -45,9 +48,9 @@ global.CompilerContext = {
|
|||||||
var templateSpec = handlebarsEnv.precompile(template, options);
|
var templateSpec = handlebarsEnv.precompile(template, options);
|
||||||
return handlebarsEnv.template(safeEval(templateSpec));
|
return handlebarsEnv.template(safeEval(templateSpec));
|
||||||
},
|
},
|
||||||
compileWithPartial: function(template, options) {
|
compileWithPartial: function (template, options) {
|
||||||
// Hack the compiler on to the environment for these specific tests
|
// Hack the compiler on to the environment for these specific tests
|
||||||
handlebarsEnv.compile = function(compileTemplate, compileOptions) {
|
handlebarsEnv.compile = function (compileTemplate, compileOptions) {
|
||||||
return compiler.compile(compileTemplate, compileOptions, handlebarsEnv);
|
return compiler.compile(compileTemplate, compileOptions, handlebarsEnv);
|
||||||
};
|
};
|
||||||
handlebarsEnv.parse = parse;
|
handlebarsEnv.parse = parse;
|
||||||
@@ -55,7 +58,7 @@ global.CompilerContext = {
|
|||||||
handlebarsEnv.JavaScriptCompiler = JavaScriptCompiler;
|
handlebarsEnv.JavaScriptCompiler = JavaScriptCompiler;
|
||||||
|
|
||||||
return handlebarsEnv.compile(template, options);
|
return handlebarsEnv.compile(template, options);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function safeEval(templateSpec) {
|
function safeEval(templateSpec) {
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
Precompile handlebar templates.
|
Precompile handlebar templates.
|
||||||
Usage: handlebars [template|directory]...
|
Usage: handlebars.js [template|directory]...
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
--help Outputs this message [boolean]
|
||||||
-f, --output Output File [string]
|
-f, --output Output File [string]
|
||||||
--map Source Map File [string]
|
--map Source Map File [string]
|
||||||
-a, --amd Exports amd style (require.js) [boolean]
|
--esm Exports ECMAScript module style, path to Handlebars module (eg. "handlebars/lib/handlebars")
|
||||||
|
[string] [default: null]
|
||||||
-c, --commonjs Exports CommonJS style, path to Handlebars module [string] [default: null]
|
-c, --commonjs Exports CommonJS style, path to Handlebars module [string] [default: null]
|
||||||
-h, --handlebarPath Path to handlebar.js (only valid for amd-style) [string] [default: ""]
|
-a, --amd Exports AMD style (require.js) [deprecated] [boolean]
|
||||||
|
-h, --handlebarPath Path to handlebar.js (only valid for AMD-style) [deprecated] [string] [default: ""]
|
||||||
-k, --known Known helpers [string]
|
-k, --known Known helpers [string]
|
||||||
-o, --knownOnly Known helpers only [boolean]
|
-o, --knownOnly Known helpers only [boolean]
|
||||||
-m, --min Minimize output [boolean]
|
-m, --min Minimize output [boolean]
|
||||||
@@ -21,5 +24,4 @@ Options:
|
|||||||
-d, --data Include data when compiling [boolean]
|
-d, --data Include data when compiling [boolean]
|
||||||
-e, --extension Template extension. [string] [default: "handlebars"]
|
-e, --extension Template extension. [string] [default: "handlebars"]
|
||||||
-b, --bom Removes the BOM (Byte Order Mark) from the beginning of the templates. [boolean]
|
-b, --bom Removes the BOM (Byte Order Mark) from the beginning of the templates. [boolean]
|
||||||
-v, --version Prints the current compiler version [boolean]
|
-v, --version Show version number [boolean]
|
||||||
--help Outputs this message [boolean]
|
|
||||||
+200
-200
File diff suppressed because it is too large
Load Diff
@@ -28,7 +28,6 @@
|
|||||||
mocha.setup('bdd');
|
mocha.setup('bdd');
|
||||||
</script>
|
</script>
|
||||||
<script src="/dist/handlebars.js"></script>
|
<script src="/dist/handlebars.js"></script>
|
||||||
<script src="/spec/vendor/json2.js"></script>
|
|
||||||
<script src="/spec/env/common.js"></script>
|
<script src="/spec/env/common.js"></script>
|
||||||
<script>
|
<script>
|
||||||
var CompilerContext = {
|
var CompilerContext = {
|
||||||
|
|||||||
+28
-26
@@ -1,19 +1,19 @@
|
|||||||
describe('javascript-compiler api', function() {
|
describe('javascript-compiler api', function () {
|
||||||
if (!Handlebars.JavaScriptCompiler) {
|
if (!Handlebars.JavaScriptCompiler) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('#nameLookup', function() {
|
describe('#nameLookup', function () {
|
||||||
var $superName;
|
var $superName;
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
$superName = handlebarsEnv.JavaScriptCompiler.prototype.nameLookup;
|
$superName = handlebarsEnv.JavaScriptCompiler.prototype.nameLookup;
|
||||||
});
|
});
|
||||||
afterEach(function() {
|
afterEach(function () {
|
||||||
handlebarsEnv.JavaScriptCompiler.prototype.nameLookup = $superName;
|
handlebarsEnv.JavaScriptCompiler.prototype.nameLookup = $superName;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow override', function() {
|
it('should allow override', function () {
|
||||||
handlebarsEnv.JavaScriptCompiler.prototype.nameLookup = function(
|
handlebarsEnv.JavaScriptCompiler.prototype.nameLookup = function (
|
||||||
parent,
|
parent,
|
||||||
name
|
name
|
||||||
) {
|
) {
|
||||||
@@ -28,27 +28,27 @@ describe('javascript-compiler api', function() {
|
|||||||
|
|
||||||
// Tests nameLookup dot vs. bracket behavior. Bracket is required in certain cases
|
// Tests nameLookup dot vs. bracket behavior. Bracket is required in certain cases
|
||||||
// to avoid errors in older browsers.
|
// to avoid errors in older browsers.
|
||||||
it('should handle reserved words', function() {
|
it('should handle reserved words', function () {
|
||||||
expectTemplate('{{foo}} {{~null~}}')
|
expectTemplate('{{foo}} {{~null~}}')
|
||||||
.withInput({ foo: 'food' })
|
.withInput({ foo: 'food' })
|
||||||
.toCompileTo('food');
|
.toCompileTo('food');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('#compilerInfo', function() {
|
describe('#compilerInfo', function () {
|
||||||
var $superCheck, $superInfo;
|
var $superCheck, $superInfo;
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
$superCheck = handlebarsEnv.VM.checkRevision;
|
$superCheck = handlebarsEnv.VM.checkRevision;
|
||||||
$superInfo = handlebarsEnv.JavaScriptCompiler.prototype.compilerInfo;
|
$superInfo = handlebarsEnv.JavaScriptCompiler.prototype.compilerInfo;
|
||||||
});
|
});
|
||||||
afterEach(function() {
|
afterEach(function () {
|
||||||
handlebarsEnv.VM.checkRevision = $superCheck;
|
handlebarsEnv.VM.checkRevision = $superCheck;
|
||||||
handlebarsEnv.JavaScriptCompiler.prototype.compilerInfo = $superInfo;
|
handlebarsEnv.JavaScriptCompiler.prototype.compilerInfo = $superInfo;
|
||||||
});
|
});
|
||||||
it('should allow compilerInfo override', function() {
|
it('should allow compilerInfo override', function () {
|
||||||
handlebarsEnv.JavaScriptCompiler.prototype.compilerInfo = function() {
|
handlebarsEnv.JavaScriptCompiler.prototype.compilerInfo = function () {
|
||||||
return 'crazy';
|
return 'crazy';
|
||||||
};
|
};
|
||||||
handlebarsEnv.VM.checkRevision = function(compilerInfo) {
|
handlebarsEnv.VM.checkRevision = function (compilerInfo) {
|
||||||
if (compilerInfo !== 'crazy') {
|
if (compilerInfo !== 'crazy') {
|
||||||
throw new Error("It didn't work");
|
throw new Error("It didn't work");
|
||||||
}
|
}
|
||||||
@@ -58,30 +58,32 @@ describe('javascript-compiler api', function() {
|
|||||||
.toCompileTo('food ');
|
.toCompileTo('food ');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('buffer', function() {
|
describe('buffer', function () {
|
||||||
var $superAppend, $superCreate;
|
var $superAppend, $superCreate;
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
handlebarsEnv.JavaScriptCompiler.prototype.forceBuffer = true;
|
handlebarsEnv.JavaScriptCompiler.prototype.forceBuffer = true;
|
||||||
$superAppend = handlebarsEnv.JavaScriptCompiler.prototype.appendToBuffer;
|
$superAppend = handlebarsEnv.JavaScriptCompiler.prototype.appendToBuffer;
|
||||||
$superCreate =
|
$superCreate =
|
||||||
handlebarsEnv.JavaScriptCompiler.prototype.initializeBuffer;
|
handlebarsEnv.JavaScriptCompiler.prototype.initializeBuffer;
|
||||||
});
|
});
|
||||||
afterEach(function() {
|
afterEach(function () {
|
||||||
handlebarsEnv.JavaScriptCompiler.prototype.forceBuffer = false;
|
handlebarsEnv.JavaScriptCompiler.prototype.forceBuffer = false;
|
||||||
handlebarsEnv.JavaScriptCompiler.prototype.appendToBuffer = $superAppend;
|
handlebarsEnv.JavaScriptCompiler.prototype.appendToBuffer = $superAppend;
|
||||||
handlebarsEnv.JavaScriptCompiler.prototype.initializeBuffer = $superCreate;
|
handlebarsEnv.JavaScriptCompiler.prototype.initializeBuffer =
|
||||||
|
$superCreate;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow init buffer override', function() {
|
it('should allow init buffer override', function () {
|
||||||
handlebarsEnv.JavaScriptCompiler.prototype.initializeBuffer = function() {
|
handlebarsEnv.JavaScriptCompiler.prototype.initializeBuffer =
|
||||||
|
function () {
|
||||||
return this.quotedString('foo_');
|
return this.quotedString('foo_');
|
||||||
};
|
};
|
||||||
expectTemplate('{{foo}} ')
|
expectTemplate('{{foo}} ')
|
||||||
.withInput({ foo: 'food' })
|
.withInput({ foo: 'food' })
|
||||||
.toCompileTo('foo_food ');
|
.toCompileTo('foo_food ');
|
||||||
});
|
});
|
||||||
it('should allow append buffer override', function() {
|
it('should allow append buffer override', function () {
|
||||||
handlebarsEnv.JavaScriptCompiler.prototype.appendToBuffer = function(
|
handlebarsEnv.JavaScriptCompiler.prototype.appendToBuffer = function (
|
||||||
string
|
string
|
||||||
) {
|
) {
|
||||||
return $superAppend.call(this, [string, ' + "_foo"']);
|
return $superAppend.call(this, [string, ' + "_foo"']);
|
||||||
@@ -92,13 +94,13 @@ describe('javascript-compiler api', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#isValidJavaScriptVariableName', function() {
|
describe('#isValidJavaScriptVariableName', function () {
|
||||||
// It is there and accessible and could be used by someone. That's why we don't remove it
|
// It is there and accessible and could be used by someone. That's why we don't remove it
|
||||||
// it 4.x. But if we keep it, we add a test
|
// it 4.x. But if we keep it, we add a test
|
||||||
// This test should not encourage you to use the function. It is not needed any more
|
// This test should not encourage you to use the function. It is not needed any more
|
||||||
// and might be removed in 5.0
|
// and might be removed in 5.0
|
||||||
['test', 'abc123', 'abc_123'].forEach(function(validVariableName) {
|
['test', 'abc123', 'abc_123'].forEach(function (validVariableName) {
|
||||||
it("should return true for '" + validVariableName + "'", function() {
|
it("should return true for '" + validVariableName + "'", function () {
|
||||||
expect(
|
expect(
|
||||||
handlebarsEnv.JavaScriptCompiler.isValidJavaScriptVariableName(
|
handlebarsEnv.JavaScriptCompiler.isValidJavaScriptVariableName(
|
||||||
validVariableName
|
validVariableName
|
||||||
@@ -106,8 +108,8 @@ describe('javascript-compiler api', function() {
|
|||||||
).to.be.true();
|
).to.be.true();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
[('123test', 'abc()', 'abc.cde')].forEach(function(invalidVariableName) {
|
[('123test', 'abc()', 'abc.cde')].forEach(function (invalidVariableName) {
|
||||||
it("should return true for '" + invalidVariableName + "'", function() {
|
it("should return true for '" + invalidVariableName + "'", function () {
|
||||||
expect(
|
expect(
|
||||||
handlebarsEnv.JavaScriptCompiler.isValidJavaScriptVariableName(
|
handlebarsEnv.JavaScriptCompiler.isValidJavaScriptVariableName(
|
||||||
invalidVariableName
|
invalidVariableName
|
||||||
|
|||||||
-455
@@ -1,455 +0,0 @@
|
|||||||
describe('parser', function() {
|
|
||||||
if (!Handlebars.print) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
function astFor(template) {
|
|
||||||
var ast = Handlebars.parse(template);
|
|
||||||
return Handlebars.print(ast);
|
|
||||||
}
|
|
||||||
|
|
||||||
it('parses simple mustaches', function() {
|
|
||||||
equals(astFor('{{123}}'), '{{ NUMBER{123} [] }}\n');
|
|
||||||
equals(astFor('{{"foo"}}'), '{{ "foo" [] }}\n');
|
|
||||||
equals(astFor('{{false}}'), '{{ BOOLEAN{false} [] }}\n');
|
|
||||||
equals(astFor('{{true}}'), '{{ BOOLEAN{true} [] }}\n');
|
|
||||||
equals(astFor('{{foo}}'), '{{ PATH:foo [] }}\n');
|
|
||||||
equals(astFor('{{foo?}}'), '{{ PATH:foo? [] }}\n');
|
|
||||||
equals(astFor('{{foo_}}'), '{{ PATH:foo_ [] }}\n');
|
|
||||||
equals(astFor('{{foo-}}'), '{{ PATH:foo- [] }}\n');
|
|
||||||
equals(astFor('{{foo:}}'), '{{ PATH:foo: [] }}\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses simple mustaches with data', function() {
|
|
||||||
equals(astFor('{{@foo}}'), '{{ @PATH:foo [] }}\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses simple mustaches with data paths', function() {
|
|
||||||
equals(astFor('{{@../foo}}'), '{{ @PATH:foo [] }}\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses mustaches with paths', function() {
|
|
||||||
equals(astFor('{{foo/bar}}'), '{{ PATH:foo/bar [] }}\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses mustaches with this/foo', function() {
|
|
||||||
equals(astFor('{{this/foo}}'), '{{ PATH:foo [] }}\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses mustaches with - in a path', function() {
|
|
||||||
equals(astFor('{{foo-bar}}'), '{{ PATH:foo-bar [] }}\n');
|
|
||||||
});
|
|
||||||
it('parses mustaches with escaped [] in a path', function() {
|
|
||||||
equals(astFor('{{[foo[\\]]}}'), '{{ PATH:foo[] [] }}\n');
|
|
||||||
});
|
|
||||||
it('parses escaped \\\\ in path', function() {
|
|
||||||
equals(astFor('{{[foo\\\\]}}'), '{{ PATH:foo\\ [] }}\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses mustaches with parameters', function() {
|
|
||||||
equals(astFor('{{foo bar}}'), '{{ PATH:foo [PATH:bar] }}\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses mustaches with string parameters', function() {
|
|
||||||
equals(astFor('{{foo bar "baz" }}'), '{{ PATH:foo [PATH:bar, "baz"] }}\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses mustaches with NUMBER parameters', function() {
|
|
||||||
equals(astFor('{{foo 1}}'), '{{ PATH:foo [NUMBER{1}] }}\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses mustaches with BOOLEAN parameters', function() {
|
|
||||||
equals(astFor('{{foo true}}'), '{{ PATH:foo [BOOLEAN{true}] }}\n');
|
|
||||||
equals(astFor('{{foo false}}'), '{{ PATH:foo [BOOLEAN{false}] }}\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses mustaches with undefined and null paths', function() {
|
|
||||||
equals(astFor('{{undefined}}'), '{{ UNDEFINED [] }}\n');
|
|
||||||
equals(astFor('{{null}}'), '{{ NULL [] }}\n');
|
|
||||||
});
|
|
||||||
it('parses mustaches with undefined and null parameters', function() {
|
|
||||||
equals(
|
|
||||||
astFor('{{foo undefined null}}'),
|
|
||||||
'{{ PATH:foo [UNDEFINED, NULL] }}\n'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses mustaches with DATA parameters', function() {
|
|
||||||
equals(astFor('{{foo @bar}}'), '{{ PATH:foo [@PATH:bar] }}\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses mustaches with hash arguments', function() {
|
|
||||||
equals(astFor('{{foo bar=baz}}'), '{{ PATH:foo [] HASH{bar=PATH:baz} }}\n');
|
|
||||||
equals(astFor('{{foo bar=1}}'), '{{ PATH:foo [] HASH{bar=NUMBER{1}} }}\n');
|
|
||||||
equals(
|
|
||||||
astFor('{{foo bar=true}}'),
|
|
||||||
'{{ PATH:foo [] HASH{bar=BOOLEAN{true}} }}\n'
|
|
||||||
);
|
|
||||||
equals(
|
|
||||||
astFor('{{foo bar=false}}'),
|
|
||||||
'{{ PATH:foo [] HASH{bar=BOOLEAN{false}} }}\n'
|
|
||||||
);
|
|
||||||
equals(
|
|
||||||
astFor('{{foo bar=@baz}}'),
|
|
||||||
'{{ PATH:foo [] HASH{bar=@PATH:baz} }}\n'
|
|
||||||
);
|
|
||||||
|
|
||||||
equals(
|
|
||||||
astFor('{{foo bar=baz bat=bam}}'),
|
|
||||||
'{{ PATH:foo [] HASH{bar=PATH:baz, bat=PATH:bam} }}\n'
|
|
||||||
);
|
|
||||||
equals(
|
|
||||||
astFor('{{foo bar=baz bat="bam"}}'),
|
|
||||||
'{{ PATH:foo [] HASH{bar=PATH:baz, bat="bam"} }}\n'
|
|
||||||
);
|
|
||||||
|
|
||||||
equals(astFor("{{foo bat='bam'}}"), '{{ PATH:foo [] HASH{bat="bam"} }}\n');
|
|
||||||
|
|
||||||
equals(
|
|
||||||
astFor('{{foo omg bar=baz bat="bam"}}'),
|
|
||||||
'{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam"} }}\n'
|
|
||||||
);
|
|
||||||
equals(
|
|
||||||
astFor('{{foo omg bar=baz bat="bam" baz=1}}'),
|
|
||||||
'{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam", baz=NUMBER{1}} }}\n'
|
|
||||||
);
|
|
||||||
equals(
|
|
||||||
astFor('{{foo omg bar=baz bat="bam" baz=true}}'),
|
|
||||||
'{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam", baz=BOOLEAN{true}} }}\n'
|
|
||||||
);
|
|
||||||
equals(
|
|
||||||
astFor('{{foo omg bar=baz bat="bam" baz=false}}'),
|
|
||||||
'{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam", baz=BOOLEAN{false}} }}\n'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses contents followed by a mustache', function() {
|
|
||||||
equals(
|
|
||||||
astFor('foo bar {{baz}}'),
|
|
||||||
"CONTENT[ 'foo bar ' ]\n{{ PATH:baz [] }}\n"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses a partial', function() {
|
|
||||||
equals(astFor('{{> foo }}'), '{{> PARTIAL:foo }}\n');
|
|
||||||
equals(astFor('{{> "foo" }}'), '{{> PARTIAL:foo }}\n');
|
|
||||||
equals(astFor('{{> 1 }}'), '{{> PARTIAL:1 }}\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses a partial with context', function() {
|
|
||||||
equals(astFor('{{> foo bar}}'), '{{> PARTIAL:foo PATH:bar }}\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses a partial with hash', function() {
|
|
||||||
equals(
|
|
||||||
astFor('{{> foo bar=bat}}'),
|
|
||||||
'{{> PARTIAL:foo HASH{bar=PATH:bat} }}\n'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses a partial with context and hash', function() {
|
|
||||||
equals(
|
|
||||||
astFor('{{> foo bar bat=baz}}'),
|
|
||||||
'{{> PARTIAL:foo PATH:bar HASH{bat=PATH:baz} }}\n'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses a partial with a complex name', function() {
|
|
||||||
equals(
|
|
||||||
astFor('{{> shared/partial?.bar}}'),
|
|
||||||
'{{> PARTIAL:shared/partial?.bar }}\n'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parsers partial blocks', function() {
|
|
||||||
equals(
|
|
||||||
astFor('{{#> foo}}bar{{/foo}}'),
|
|
||||||
"{{> PARTIAL BLOCK:foo PROGRAM:\n CONTENT[ 'bar' ]\n }}\n"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('should handle parser block mismatch', function() {
|
|
||||||
shouldThrow(
|
|
||||||
function() {
|
|
||||||
astFor('{{#> goodbyes}}{{/hellos}}');
|
|
||||||
},
|
|
||||||
Error,
|
|
||||||
/goodbyes doesn't match hellos/
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('parsers partial blocks with arguments', function() {
|
|
||||||
equals(
|
|
||||||
astFor('{{#> foo context hash=value}}bar{{/foo}}'),
|
|
||||||
"{{> PARTIAL BLOCK:foo PATH:context HASH{hash=PATH:value} PROGRAM:\n CONTENT[ 'bar' ]\n }}\n"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses a comment', function() {
|
|
||||||
equals(
|
|
||||||
astFor('{{! this is a comment }}'),
|
|
||||||
"{{! ' this is a comment ' }}\n"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses a multi-line comment', function() {
|
|
||||||
equals(
|
|
||||||
astFor('{{!\nthis is a multi-line comment\n}}'),
|
|
||||||
"{{! '\nthis is a multi-line comment\n' }}\n"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses an inverse section', function() {
|
|
||||||
equals(
|
|
||||||
astFor('{{#foo}} bar {{^}} baz {{/foo}}'),
|
|
||||||
"BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n CONTENT[ ' baz ' ]\n"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses an inverse (else-style) section', function() {
|
|
||||||
equals(
|
|
||||||
astFor('{{#foo}} bar {{else}} baz {{/foo}}'),
|
|
||||||
"BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n CONTENT[ ' baz ' ]\n"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses multiple inverse sections', function() {
|
|
||||||
equals(
|
|
||||||
astFor('{{#foo}} bar {{else if bar}}{{else}} baz {{/foo}}'),
|
|
||||||
"BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n BLOCK:\n PATH:if [PATH:bar]\n PROGRAM:\n {{^}}\n CONTENT[ ' baz ' ]\n"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses empty blocks', function() {
|
|
||||||
equals(astFor('{{#foo}}{{/foo}}'), 'BLOCK:\n PATH:foo []\n PROGRAM:\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses empty blocks with empty inverse section', function() {
|
|
||||||
equals(
|
|
||||||
astFor('{{#foo}}{{^}}{{/foo}}'),
|
|
||||||
'BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses empty blocks with empty inverse (else-style) section', function() {
|
|
||||||
equals(
|
|
||||||
astFor('{{#foo}}{{else}}{{/foo}}'),
|
|
||||||
'BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses non-empty blocks with empty inverse section', function() {
|
|
||||||
equals(
|
|
||||||
astFor('{{#foo}} bar {{^}}{{/foo}}'),
|
|
||||||
"BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses non-empty blocks with empty inverse (else-style) section', function() {
|
|
||||||
equals(
|
|
||||||
astFor('{{#foo}} bar {{else}}{{/foo}}'),
|
|
||||||
"BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses empty blocks with non-empty inverse section', function() {
|
|
||||||
equals(
|
|
||||||
astFor('{{#foo}}{{^}} bar {{/foo}}'),
|
|
||||||
"BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n CONTENT[ ' bar ' ]\n"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses empty blocks with non-empty inverse (else-style) section', function() {
|
|
||||||
equals(
|
|
||||||
astFor('{{#foo}}{{else}} bar {{/foo}}'),
|
|
||||||
"BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n CONTENT[ ' bar ' ]\n"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses a standalone inverse section', function() {
|
|
||||||
equals(
|
|
||||||
astFor('{{^foo}}bar{{/foo}}'),
|
|
||||||
"BLOCK:\n PATH:foo []\n {{^}}\n CONTENT[ 'bar' ]\n"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('throws on old inverse section', function() {
|
|
||||||
shouldThrow(function() {
|
|
||||||
astFor('{{else foo}}bar{{/foo}}');
|
|
||||||
}, Error);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses block with block params', function() {
|
|
||||||
equals(
|
|
||||||
astFor('{{#foo as |bar baz|}}content{{/foo}}'),
|
|
||||||
"BLOCK:\n PATH:foo []\n PROGRAM:\n BLOCK PARAMS: [ bar baz ]\n CONTENT[ 'content' ]\n"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses inverse block with block params', function() {
|
|
||||||
equals(
|
|
||||||
astFor('{{^foo as |bar baz|}}content{{/foo}}'),
|
|
||||||
"BLOCK:\n PATH:foo []\n {{^}}\n BLOCK PARAMS: [ bar baz ]\n CONTENT[ 'content' ]\n"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('parses chained inverse block with block params', function() {
|
|
||||||
equals(
|
|
||||||
astFor('{{#foo}}{{else foo as |bar baz|}}content{{/foo}}'),
|
|
||||||
"BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n BLOCK:\n PATH:foo []\n PROGRAM:\n BLOCK PARAMS: [ bar baz ]\n CONTENT[ 'content' ]\n"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it("raises if there's a Parse error", function() {
|
|
||||||
shouldThrow(
|
|
||||||
function() {
|
|
||||||
astFor('foo{{^}}bar');
|
|
||||||
},
|
|
||||||
Error,
|
|
||||||
/Parse error on line 1/
|
|
||||||
);
|
|
||||||
shouldThrow(
|
|
||||||
function() {
|
|
||||||
astFor('{{foo}');
|
|
||||||
},
|
|
||||||
Error,
|
|
||||||
/Parse error on line 1/
|
|
||||||
);
|
|
||||||
shouldThrow(
|
|
||||||
function() {
|
|
||||||
astFor('{{foo &}}');
|
|
||||||
},
|
|
||||||
Error,
|
|
||||||
/Parse error on line 1/
|
|
||||||
);
|
|
||||||
shouldThrow(
|
|
||||||
function() {
|
|
||||||
astFor('{{#goodbyes}}{{/hellos}}');
|
|
||||||
},
|
|
||||||
Error,
|
|
||||||
/goodbyes doesn't match hellos/
|
|
||||||
);
|
|
||||||
|
|
||||||
shouldThrow(
|
|
||||||
function() {
|
|
||||||
astFor('{{{{goodbyes}}}} {{{{/hellos}}}}');
|
|
||||||
},
|
|
||||||
Error,
|
|
||||||
/goodbyes doesn't match hellos/
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle invalid paths', function() {
|
|
||||||
shouldThrow(
|
|
||||||
function() {
|
|
||||||
astFor('{{foo/../bar}}');
|
|
||||||
},
|
|
||||||
Error,
|
|
||||||
/Invalid path: foo\/\.\. - 1:2/
|
|
||||||
);
|
|
||||||
shouldThrow(
|
|
||||||
function() {
|
|
||||||
astFor('{{foo/./bar}}');
|
|
||||||
},
|
|
||||||
Error,
|
|
||||||
/Invalid path: foo\/\. - 1:2/
|
|
||||||
);
|
|
||||||
shouldThrow(
|
|
||||||
function() {
|
|
||||||
astFor('{{foo/this/bar}}');
|
|
||||||
},
|
|
||||||
Error,
|
|
||||||
/Invalid path: foo\/this - 1:2/
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('knows how to report the correct line number in errors', function() {
|
|
||||||
shouldThrow(
|
|
||||||
function() {
|
|
||||||
astFor('hello\nmy\n{{foo}');
|
|
||||||
},
|
|
||||||
Error,
|
|
||||||
/Parse error on line 3/
|
|
||||||
);
|
|
||||||
shouldThrow(
|
|
||||||
function() {
|
|
||||||
astFor('hello\n\nmy\n\n{{foo}');
|
|
||||||
},
|
|
||||||
Error,
|
|
||||||
/Parse error on line 5/
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('knows how to report the correct line number in errors when the first character is a newline', function() {
|
|
||||||
shouldThrow(
|
|
||||||
function() {
|
|
||||||
astFor('\n\nhello\n\nmy\n\n{{foo}');
|
|
||||||
},
|
|
||||||
Error,
|
|
||||||
/Parse error on line 7/
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('externally compiled AST', function() {
|
|
||||||
it('can pass through an already-compiled AST', function() {
|
|
||||||
equals(
|
|
||||||
astFor({
|
|
||||||
type: 'Program',
|
|
||||||
body: [{ type: 'ContentStatement', value: 'Hello' }]
|
|
||||||
}),
|
|
||||||
"CONTENT[ 'Hello' ]\n"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('directives', function() {
|
|
||||||
it('should parse block directives', function() {
|
|
||||||
equals(
|
|
||||||
astFor('{{#* foo}}{{/foo}}'),
|
|
||||||
'DIRECTIVE BLOCK:\n PATH:foo []\n PROGRAM:\n'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('should parse directives', function() {
|
|
||||||
equals(astFor('{{* foo}}'), '{{ DIRECTIVE PATH:foo [] }}\n');
|
|
||||||
});
|
|
||||||
it('should fail if directives have inverse', function() {
|
|
||||||
shouldThrow(
|
|
||||||
function() {
|
|
||||||
astFor('{{#* foo}}{{^}}{{/foo}}');
|
|
||||||
},
|
|
||||||
Error,
|
|
||||||
/Unexpected inverse/
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('GH1024 - should track program location properly', function() {
|
|
||||||
var p = Handlebars.parse(
|
|
||||||
'\n' +
|
|
||||||
' {{#if foo}}\n' +
|
|
||||||
' {{bar}}\n' +
|
|
||||||
' {{else}} {{baz}}\n' +
|
|
||||||
'\n' +
|
|
||||||
' {{/if}}\n' +
|
|
||||||
' '
|
|
||||||
);
|
|
||||||
|
|
||||||
// We really need a deep equals but for now this should be stable...
|
|
||||||
equals(
|
|
||||||
JSON.stringify(p.loc),
|
|
||||||
JSON.stringify({
|
|
||||||
start: { line: 1, column: 0 },
|
|
||||||
end: { line: 7, column: 4 }
|
|
||||||
})
|
|
||||||
);
|
|
||||||
equals(
|
|
||||||
JSON.stringify(p.body[1].program.loc),
|
|
||||||
JSON.stringify({
|
|
||||||
start: { line: 2, column: 13 },
|
|
||||||
end: { line: 4, column: 7 }
|
|
||||||
})
|
|
||||||
);
|
|
||||||
equals(
|
|
||||||
JSON.stringify(p.body[1].inverse.loc),
|
|
||||||
JSON.stringify({
|
|
||||||
start: { line: 4, column: 15 },
|
|
||||||
end: { line: 6, column: 5 }
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
+125
-125
@@ -1,12 +1,12 @@
|
|||||||
describe('partials', function() {
|
describe('partials', function () {
|
||||||
it('basic partials', function() {
|
it('basic partials', function () {
|
||||||
var string = 'Dudes: {{#dudes}}{{> dude}}{{/dudes}}';
|
var string = 'Dudes: {{#dudes}}{{> dude}}{{/dudes}}';
|
||||||
var partial = '{{name}} ({{url}}) ';
|
var partial = '{{name}} ({{url}}) ';
|
||||||
var hash = {
|
var hash = {
|
||||||
dudes: [
|
dudes: [
|
||||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||||
{ name: 'Alan', url: 'http://alan' }
|
{ name: 'Alan', url: 'http://alan' },
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
expectTemplate(string)
|
expectTemplate(string)
|
||||||
@@ -22,19 +22,19 @@ describe('partials', function() {
|
|||||||
.toCompileTo('Dudes: Yehuda (http://yehuda) Alan (http://alan) ');
|
.toCompileTo('Dudes: Yehuda (http://yehuda) Alan (http://alan) ');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('dynamic partials', function() {
|
it('dynamic partials', function () {
|
||||||
var string = 'Dudes: {{#dudes}}{{> (partial)}}{{/dudes}}';
|
var string = 'Dudes: {{#dudes}}{{> (partial)}}{{/dudes}}';
|
||||||
var partial = '{{name}} ({{url}}) ';
|
var partial = '{{name}} ({{url}}) ';
|
||||||
var hash = {
|
var hash = {
|
||||||
dudes: [
|
dudes: [
|
||||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||||
{ name: 'Alan', url: 'http://alan' }
|
{ name: 'Alan', url: 'http://alan' },
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
var helpers = {
|
var helpers = {
|
||||||
partial: function() {
|
partial: function () {
|
||||||
return 'dude';
|
return 'dude';
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
expectTemplate(string)
|
expectTemplate(string)
|
||||||
@@ -52,41 +52,44 @@ describe('partials', function() {
|
|||||||
.toCompileTo('Dudes: Yehuda (http://yehuda) Alan (http://alan) ');
|
.toCompileTo('Dudes: Yehuda (http://yehuda) Alan (http://alan) ');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('failing dynamic partials', function() {
|
it('failing dynamic partials', function () {
|
||||||
expectTemplate('Dudes: {{#dudes}}{{> (partial)}}{{/dudes}}')
|
expectTemplate('Dudes: {{#dudes}}{{> (partial)}}{{/dudes}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
dudes: [
|
dudes: [
|
||||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||||
{ name: 'Alan', url: 'http://alan' }
|
{ name: 'Alan', url: 'http://alan' },
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
.withHelper('partial', function() {
|
.withHelper('partial', function () {
|
||||||
return 'missing';
|
return 'missing';
|
||||||
})
|
})
|
||||||
.withPartial('dude', '{{name}} ({{url}}) ')
|
.withPartial('dude', '{{name}} ({{url}}) ')
|
||||||
.toThrow(Handlebars.Exception, 'The partial missing could not be found');
|
.toThrow(
|
||||||
|
Handlebars.Exception,
|
||||||
|
'The partial "missing" could not be found'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('partials with context', function() {
|
it('partials with context', function () {
|
||||||
expectTemplate('Dudes: {{>dude dudes}}')
|
expectTemplate('Dudes: {{>dude dudes}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
dudes: [
|
dudes: [
|
||||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||||
{ name: 'Alan', url: 'http://alan' }
|
{ name: 'Alan', url: 'http://alan' },
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
.withPartial('dude', '{{#this}}{{name}} ({{url}}) {{/this}}')
|
.withPartial('dude', '{{#this}}{{name}} ({{url}}) {{/this}}')
|
||||||
.withMessage('Partials can be passed a context')
|
.withMessage('Partials can be passed a context')
|
||||||
.toCompileTo('Dudes: Yehuda (http://yehuda) Alan (http://alan) ');
|
.toCompileTo('Dudes: Yehuda (http://yehuda) Alan (http://alan) ');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('partials with no context', function() {
|
it('partials with no context', function () {
|
||||||
var partial = '{{name}} ({{url}}) ';
|
var partial = '{{name}} ({{url}}) ';
|
||||||
var hash = {
|
var hash = {
|
||||||
dudes: [
|
dudes: [
|
||||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||||
{ name: 'Alan', url: 'http://alan' }
|
{ name: 'Alan', url: 'http://alan' },
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
expectTemplate('Dudes: {{#dudes}}{{>dude}}{{/dudes}}')
|
expectTemplate('Dudes: {{#dudes}}{{>dude}}{{/dudes}}')
|
||||||
@@ -102,50 +105,50 @@ describe('partials', function() {
|
|||||||
.toCompileTo('Dudes: foo () foo () ');
|
.toCompileTo('Dudes: foo () foo () ');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('partials with string context', function() {
|
it('partials with string context', function () {
|
||||||
expectTemplate('Dudes: {{>dude "dudes"}}')
|
expectTemplate('Dudes: {{>dude "dudes"}}')
|
||||||
.withPartial('dude', '{{.}}')
|
.withPartial('dude', '{{.}}')
|
||||||
.toCompileTo('Dudes: dudes');
|
.toCompileTo('Dudes: dudes');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('partials with undefined context', function() {
|
it('partials with undefined context', function () {
|
||||||
expectTemplate('Dudes: {{>dude dudes}}')
|
expectTemplate('Dudes: {{>dude dudes}}')
|
||||||
.withPartial('dude', '{{foo}} Empty')
|
.withPartial('dude', '{{foo}} Empty')
|
||||||
.toCompileTo('Dudes: Empty');
|
.toCompileTo('Dudes: Empty');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('partials with duplicate parameters', function() {
|
it('partials with duplicate parameters', function () {
|
||||||
expectTemplate('Dudes: {{>dude dudes foo bar=baz}}').toThrow(
|
expectTemplate('Dudes: {{>dude dudes foo bar=baz}}').toThrow(
|
||||||
Error,
|
Error,
|
||||||
'Unsupported number of partial arguments: 2 - 1:7'
|
'Unsupported number of partial arguments: 2 - 1:7'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('partials with parameters', function() {
|
it('partials with parameters', function () {
|
||||||
expectTemplate('Dudes: {{#dudes}}{{> dude others=..}}{{/dudes}}')
|
expectTemplate('Dudes: {{#dudes}}{{> dude others=..}}{{/dudes}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
foo: 'bar',
|
foo: 'bar',
|
||||||
dudes: [
|
dudes: [
|
||||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||||
{ name: 'Alan', url: 'http://alan' }
|
{ name: 'Alan', url: 'http://alan' },
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
.withPartial('dude', '{{others.foo}}{{name}} ({{url}}) ')
|
.withPartial('dude', '{{others.foo}}{{name}} ({{url}}) ')
|
||||||
.withMessage('Basic partials output based on current context.')
|
.withMessage('Basic partials output based on current context.')
|
||||||
.toCompileTo('Dudes: barYehuda (http://yehuda) barAlan (http://alan) ');
|
.toCompileTo('Dudes: barYehuda (http://yehuda) barAlan (http://alan) ');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('partial in a partial', function() {
|
it('partial in a partial', function () {
|
||||||
expectTemplate('Dudes: {{#dudes}}{{>dude}}{{/dudes}}')
|
expectTemplate('Dudes: {{#dudes}}{{>dude}}{{/dudes}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
dudes: [
|
dudes: [
|
||||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||||
{ name: 'Alan', url: 'http://alan' }
|
{ name: 'Alan', url: 'http://alan' },
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
.withPartials({
|
.withPartials({
|
||||||
dude: '{{name}} {{> url}} ',
|
dude: '{{name}} {{> url}} ',
|
||||||
url: '<a href="{{url}}">{{url}}</a>'
|
url: '<a href="{{url}}">{{url}}</a>',
|
||||||
})
|
})
|
||||||
.withMessage('Partials are rendered inside of other partials')
|
.withMessage('Partials are rendered inside of other partials')
|
||||||
.toCompileTo(
|
.toCompileTo(
|
||||||
@@ -153,16 +156,16 @@ describe('partials', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rendering undefined partial throws an exception', function() {
|
it('rendering undefined partial throws an exception', function () {
|
||||||
expectTemplate('{{> whatever}}').toThrow(
|
expectTemplate('{{> whatever}}').toThrow(
|
||||||
Handlebars.Exception,
|
Handlebars.Exception,
|
||||||
'The partial whatever could not be found'
|
'The partial "whatever" could not be found'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('registering undefined partial throws an exception', function() {
|
it('registering undefined partial throws an exception', function () {
|
||||||
shouldThrow(
|
shouldThrow(
|
||||||
function() {
|
function () {
|
||||||
var undef;
|
var undef;
|
||||||
handlebarsEnv.registerPartial('undefined_test', undef);
|
handlebarsEnv.registerPartial('undefined_test', undef);
|
||||||
},
|
},
|
||||||
@@ -171,14 +174,14 @@ describe('partials', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rendering template partial in vm mode throws an exception', function() {
|
it('rendering template partial in vm mode throws an exception', function () {
|
||||||
expectTemplate('{{> whatever}}').toThrow(
|
expectTemplate('{{> whatever}}').toThrow(
|
||||||
Handlebars.Exception,
|
Handlebars.Exception,
|
||||||
'The partial whatever could not be found'
|
'The partial "whatever" could not be found'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rendering function partial in vm mode', function() {
|
it('rendering function partial in vm mode', function () {
|
||||||
function partial(context) {
|
function partial(context) {
|
||||||
return context.name + ' (' + context.url + ') ';
|
return context.name + ' (' + context.url + ') ';
|
||||||
}
|
}
|
||||||
@@ -186,15 +189,15 @@ describe('partials', function() {
|
|||||||
.withInput({
|
.withInput({
|
||||||
dudes: [
|
dudes: [
|
||||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||||
{ name: 'Alan', url: 'http://alan' }
|
{ name: 'Alan', url: 'http://alan' },
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
.withPartial('dude', partial)
|
.withPartial('dude', partial)
|
||||||
.withMessage('Function partials output based in VM.')
|
.withMessage('Function partials output based in VM.')
|
||||||
.toCompileTo('Dudes: Yehuda (http://yehuda) Alan (http://alan) ');
|
.toCompileTo('Dudes: Yehuda (http://yehuda) Alan (http://alan) ');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('GH-14: a partial preceding a selector', function() {
|
it('GH-14: a partial preceding a selector', function () {
|
||||||
expectTemplate('Dudes: {{>dude}} {{anotherDude}}')
|
expectTemplate('Dudes: {{>dude}} {{anotherDude}}')
|
||||||
.withInput({ name: 'Jeepers', anotherDude: 'Creepers' })
|
.withInput({ name: 'Jeepers', anotherDude: 'Creepers' })
|
||||||
.withPartial('dude', '{{name}}')
|
.withPartial('dude', '{{name}}')
|
||||||
@@ -202,7 +205,7 @@ describe('partials', function() {
|
|||||||
.toCompileTo('Dudes: Jeepers Creepers');
|
.toCompileTo('Dudes: Jeepers Creepers');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Partials with slash paths', function() {
|
it('Partials with slash paths', function () {
|
||||||
expectTemplate('Dudes: {{> shared/dude}}')
|
expectTemplate('Dudes: {{> shared/dude}}')
|
||||||
.withInput({ name: 'Jeepers', anotherDude: 'Creepers' })
|
.withInput({ name: 'Jeepers', anotherDude: 'Creepers' })
|
||||||
.withPartial('shared/dude', '{{name}}')
|
.withPartial('shared/dude', '{{name}}')
|
||||||
@@ -210,7 +213,7 @@ describe('partials', function() {
|
|||||||
.toCompileTo('Dudes: Jeepers');
|
.toCompileTo('Dudes: Jeepers');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Partials with slash and point paths', function() {
|
it('Partials with slash and point paths', function () {
|
||||||
expectTemplate('Dudes: {{> shared/dude.thing}}')
|
expectTemplate('Dudes: {{> shared/dude.thing}}')
|
||||||
.withInput({ name: 'Jeepers', anotherDude: 'Creepers' })
|
.withInput({ name: 'Jeepers', anotherDude: 'Creepers' })
|
||||||
.withPartial('shared/dude.thing', '{{name}}')
|
.withPartial('shared/dude.thing', '{{name}}')
|
||||||
@@ -218,7 +221,7 @@ describe('partials', function() {
|
|||||||
.toCompileTo('Dudes: Jeepers');
|
.toCompileTo('Dudes: Jeepers');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Global Partials', function() {
|
it('Global Partials', function () {
|
||||||
handlebarsEnv.registerPartial('globalTest', '{{anotherDude}}');
|
handlebarsEnv.registerPartial('globalTest', '{{anotherDude}}');
|
||||||
|
|
||||||
expectTemplate('Dudes: {{> shared/dude}} {{> globalTest}}')
|
expectTemplate('Dudes: {{> shared/dude}} {{> globalTest}}')
|
||||||
@@ -231,10 +234,10 @@ describe('partials', function() {
|
|||||||
equals(handlebarsEnv.partials.globalTest, undefined);
|
equals(handlebarsEnv.partials.globalTest, undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Multiple partial registration', function() {
|
it('Multiple partial registration', function () {
|
||||||
handlebarsEnv.registerPartial({
|
handlebarsEnv.registerPartial({
|
||||||
'shared/dude': '{{name}}',
|
'shared/dude': '{{name}}',
|
||||||
globalTest: '{{anotherDude}}'
|
globalTest: '{{anotherDude}}',
|
||||||
});
|
});
|
||||||
|
|
||||||
expectTemplate('Dudes: {{> shared/dude}} {{> globalTest}}')
|
expectTemplate('Dudes: {{> shared/dude}} {{> globalTest}}')
|
||||||
@@ -244,7 +247,7 @@ describe('partials', function() {
|
|||||||
.toCompileTo('Dudes: Jeepers Creepers');
|
.toCompileTo('Dudes: Jeepers Creepers');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Partials with integer path', function() {
|
it('Partials with integer path', function () {
|
||||||
expectTemplate('Dudes: {{> 404}}')
|
expectTemplate('Dudes: {{> 404}}')
|
||||||
.withInput({ name: 'Jeepers', anotherDude: 'Creepers' })
|
.withInput({ name: 'Jeepers', anotherDude: 'Creepers' })
|
||||||
.withPartial(404, '{{name}}')
|
.withPartial(404, '{{name}}')
|
||||||
@@ -252,7 +255,7 @@ describe('partials', function() {
|
|||||||
.toCompileTo('Dudes: Jeepers');
|
.toCompileTo('Dudes: Jeepers');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Partials with complex path', function() {
|
it('Partials with complex path', function () {
|
||||||
expectTemplate('Dudes: {{> 404/asdf?.bar}}')
|
expectTemplate('Dudes: {{> 404/asdf?.bar}}')
|
||||||
.withInput({ name: 'Jeepers', anotherDude: 'Creepers' })
|
.withInput({ name: 'Jeepers', anotherDude: 'Creepers' })
|
||||||
.withPartial('404/asdf?.bar', '{{name}}')
|
.withPartial('404/asdf?.bar', '{{name}}')
|
||||||
@@ -260,7 +263,7 @@ describe('partials', function() {
|
|||||||
.toCompileTo('Dudes: Jeepers');
|
.toCompileTo('Dudes: Jeepers');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Partials with escaped', function() {
|
it('Partials with escaped', function () {
|
||||||
expectTemplate('Dudes: {{> [+404/asdf?.bar]}}')
|
expectTemplate('Dudes: {{> [+404/asdf?.bar]}}')
|
||||||
.withInput({ name: 'Jeepers', anotherDude: 'Creepers' })
|
.withInput({ name: 'Jeepers', anotherDude: 'Creepers' })
|
||||||
.withPartial('+404/asdf?.bar', '{{name}}')
|
.withPartial('+404/asdf?.bar', '{{name}}')
|
||||||
@@ -268,7 +271,7 @@ describe('partials', function() {
|
|||||||
.toCompileTo('Dudes: Jeepers');
|
.toCompileTo('Dudes: Jeepers');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Partials with string', function() {
|
it('Partials with string', function () {
|
||||||
expectTemplate("Dudes: {{> '+404/asdf?.bar'}}")
|
expectTemplate("Dudes: {{> '+404/asdf?.bar'}}")
|
||||||
.withInput({ name: 'Jeepers', anotherDude: 'Creepers' })
|
.withInput({ name: 'Jeepers', anotherDude: 'Creepers' })
|
||||||
.withPartial('+404/asdf?.bar', '{{name}}')
|
.withPartial('+404/asdf?.bar', '{{name}}')
|
||||||
@@ -276,19 +279,19 @@ describe('partials', function() {
|
|||||||
.toCompileTo('Dudes: Jeepers');
|
.toCompileTo('Dudes: Jeepers');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle empty partial', function() {
|
it('should handle empty partial', function () {
|
||||||
expectTemplate('Dudes: {{#dudes}}{{> dude}}{{/dudes}}')
|
expectTemplate('Dudes: {{#dudes}}{{> dude}}{{/dudes}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
dudes: [
|
dudes: [
|
||||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||||
{ name: 'Alan', url: 'http://alan' }
|
{ name: 'Alan', url: 'http://alan' },
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
.withPartial('dude', '')
|
.withPartial('dude', '')
|
||||||
.toCompileTo('Dudes: ');
|
.toCompileTo('Dudes: ');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throw on missing partial', function() {
|
it('throw on missing partial', function () {
|
||||||
var compile = handlebarsEnv.compile;
|
var compile = handlebarsEnv.compile;
|
||||||
var compileWithPartial = CompilerContext.compileWithPartial;
|
var compileWithPartial = CompilerContext.compileWithPartial;
|
||||||
handlebarsEnv.compile = undefined;
|
handlebarsEnv.compile = undefined;
|
||||||
@@ -300,18 +303,18 @@ describe('partials', function() {
|
|||||||
CompilerContext.compileWithPartial = compileWithPartial;
|
CompilerContext.compileWithPartial = compileWithPartial;
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('partial blocks', function() {
|
describe('partial blocks', function () {
|
||||||
it('should render partial block as default', function() {
|
it('should render partial block as default', function () {
|
||||||
expectTemplate('{{#> dude}}success{{/dude}}').toCompileTo('success');
|
expectTemplate('{{#> dude}}success{{/dude}}').toCompileTo('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should execute default block with proper context', function() {
|
it('should execute default block with proper context', function () {
|
||||||
expectTemplate('{{#> dude context}}{{value}}{{/dude}}')
|
expectTemplate('{{#> dude context}}{{value}}{{/dude}}')
|
||||||
.withInput({ context: { value: 'success' } })
|
.withInput({ context: { value: 'success' } })
|
||||||
.toCompileTo('success');
|
.toCompileTo('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should propagate block parameters to default block', function() {
|
it('should propagate block parameters to default block', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#with context as |me|}}{{#> dude}}{{me.value}}{{/dude}}{{/with}}'
|
'{{#with context as |me|}}{{#> dude}}{{me.value}}{{/dude}}{{/with}}'
|
||||||
)
|
)
|
||||||
@@ -319,79 +322,76 @@ describe('partials', function() {
|
|||||||
.toCompileTo('success');
|
.toCompileTo('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not use partial block if partial exists', function() {
|
it('should not use partial block if partial exists', function () {
|
||||||
expectTemplate('{{#> dude}}fail{{/dude}}')
|
expectTemplate('{{#> dude}}fail{{/dude}}')
|
||||||
.withPartials({ dude: 'success' })
|
.withPartials({ dude: 'success' })
|
||||||
.toCompileTo('success');
|
.toCompileTo('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render block from partial', function() {
|
it('should render block from partial', function () {
|
||||||
expectTemplate('{{#> dude}}success{{/dude}}')
|
expectTemplate('{{#> dude}}success{{/dude}}')
|
||||||
.withPartials({ dude: '{{> @partial-block }}' })
|
.withPartials({ dude: '{{> @partial-block }}' })
|
||||||
.toCompileTo('success');
|
.toCompileTo('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to render the partial-block twice', function() {
|
it('should be able to render the partial-block twice', function () {
|
||||||
expectTemplate('{{#> dude}}success{{/dude}}')
|
expectTemplate('{{#> dude}}success{{/dude}}')
|
||||||
.withPartials({ dude: '{{> @partial-block }} {{> @partial-block }}' })
|
.withPartials({ dude: '{{> @partial-block }} {{> @partial-block }}' })
|
||||||
.toCompileTo('success success');
|
.toCompileTo('success success');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render block from partial with context', function() {
|
it('should render block from partial with context', function () {
|
||||||
expectTemplate('{{#> dude}}{{value}}{{/dude}}')
|
expectTemplate('{{#> dude}}{{value}}{{/dude}}')
|
||||||
.withInput({ context: { value: 'success' } })
|
.withInput({ context: { value: 'success' } })
|
||||||
.withPartials({
|
.withPartials({
|
||||||
dude: '{{#with context}}{{> @partial-block }}{{/with}}'
|
dude: '{{#with context}}{{> @partial-block }}{{/with}}',
|
||||||
})
|
})
|
||||||
.toCompileTo('success');
|
.toCompileTo('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to access the @data frame from a partial-block', function() {
|
it('should be able to access the @data frame from a partial-block', function () {
|
||||||
expectTemplate('{{#> dude}}in-block: {{@root/value}}{{/dude}}')
|
expectTemplate('{{#> dude}}in-block: {{@root/value}}{{/dude}}')
|
||||||
.withInput({ value: 'success' })
|
.withInput({ value: 'success' })
|
||||||
.withPartials({
|
.withPartials({
|
||||||
dude:
|
dude: '<code>before-block: {{@root/value}} {{> @partial-block }}</code>',
|
||||||
'<code>before-block: {{@root/value}} {{> @partial-block }}</code>'
|
|
||||||
})
|
})
|
||||||
.toCompileTo('<code>before-block: success in-block: success</code>');
|
.toCompileTo('<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 () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'<template>{{#> list value}}value = {{.}}{{/list}}</template>'
|
'<template>{{#> list value}}value = {{.}}{{/list}}</template>'
|
||||||
)
|
)
|
||||||
.withInput({
|
.withInput({
|
||||||
value: ['a', 'b', 'c']
|
value: ['a', 'b', 'c'],
|
||||||
})
|
})
|
||||||
.withPartials({
|
.withPartials({
|
||||||
list:
|
list: '<list>{{#each .}}<item>{{> @partial-block}}</item>{{/each}}</list>',
|
||||||
'<list>{{#each .}}<item>{{> @partial-block}}</item>{{/each}}</list>'
|
|
||||||
})
|
})
|
||||||
.toCompileTo(
|
.toCompileTo(
|
||||||
'<template><list><item>value = a</item><item>value = b</item><item>value = c</item></list></template>'
|
'<template><list><item>value = a</item><item>value = b</item><item>value = c</item></list></template>'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render block from partial with context (twice)', function() {
|
it('should render block from partial with context (twice)', function () {
|
||||||
expectTemplate('{{#> dude}}{{value}}{{/dude}}')
|
expectTemplate('{{#> dude}}{{value}}{{/dude}}')
|
||||||
.withInput({ context: { value: 'success' } })
|
.withInput({ context: { value: 'success' } })
|
||||||
.withPartials({
|
.withPartials({
|
||||||
dude:
|
dude: '{{#with context}}{{> @partial-block }} {{> @partial-block }}{{/with}}',
|
||||||
'{{#with context}}{{> @partial-block }} {{> @partial-block }}{{/with}}'
|
|
||||||
})
|
})
|
||||||
.toCompileTo('success success');
|
.toCompileTo('success success');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render block from partial with context', function() {
|
it('should render block from partial with context', function () {
|
||||||
expectTemplate('{{#> dude}}{{../context/value}}{{/dude}}')
|
expectTemplate('{{#> dude}}{{../context/value}}{{/dude}}')
|
||||||
.withInput({ context: { value: 'success' } })
|
.withInput({ context: { value: 'success' } })
|
||||||
.withPartials({
|
.withPartials({
|
||||||
dude: '{{#with context}}{{> @partial-block }}{{/with}}'
|
dude: '{{#with context}}{{> @partial-block }}{{/with}}',
|
||||||
})
|
})
|
||||||
.toCompileTo('success');
|
.toCompileTo('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render block from partial with block params', function() {
|
it('should render block from partial with block params', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#with context as |me|}}{{#> dude}}{{me.value}}{{/dude}}{{/with}}'
|
'{{#with context as |me|}}{{#> dude}}{{me.value}}{{/dude}}{{/with}}'
|
||||||
)
|
)
|
||||||
@@ -400,52 +400,52 @@ describe('partials', function() {
|
|||||||
.toCompileTo('success');
|
.toCompileTo('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render nested partial blocks', function() {
|
it('should render nested partial blocks', function () {
|
||||||
expectTemplate('<template>{{#> outer}}{{value}}{{/outer}}</template>')
|
expectTemplate('<template>{{#> outer}}{{value}}{{/outer}}</template>')
|
||||||
.withInput({ value: 'success' })
|
.withInput({ value: 'success' })
|
||||||
.withPartials({
|
.withPartials({
|
||||||
outer:
|
outer:
|
||||||
'<outer>{{#> nested}}<outer-block>{{> @partial-block}}</outer-block>{{/nested}}</outer>',
|
'<outer>{{#> nested}}<outer-block>{{> @partial-block}}</outer-block>{{/nested}}</outer>',
|
||||||
nested: '<nested>{{> @partial-block}}</nested>'
|
nested: '<nested>{{> @partial-block}}</nested>',
|
||||||
})
|
})
|
||||||
.toCompileTo(
|
.toCompileTo(
|
||||||
'<template><outer><nested><outer-block>success</outer-block></nested></outer></template>'
|
'<template><outer><nested><outer-block>success</outer-block></nested></outer></template>'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render nested partial blocks at different nesting levels', function() {
|
it('should render nested partial blocks at different nesting levels', function () {
|
||||||
expectTemplate('<template>{{#> outer}}{{value}}{{/outer}}</template>')
|
expectTemplate('<template>{{#> outer}}{{value}}{{/outer}}</template>')
|
||||||
.withInput({ value: 'success' })
|
.withInput({ value: 'success' })
|
||||||
.withPartials({
|
.withPartials({
|
||||||
outer:
|
outer:
|
||||||
'<outer>{{#> nested}}<outer-block>{{> @partial-block}}</outer-block>{{/nested}}{{> @partial-block}}</outer>',
|
'<outer>{{#> nested}}<outer-block>{{> @partial-block}}</outer-block>{{/nested}}{{> @partial-block}}</outer>',
|
||||||
nested: '<nested>{{> @partial-block}}</nested>'
|
nested: '<nested>{{> @partial-block}}</nested>',
|
||||||
})
|
})
|
||||||
.toCompileTo(
|
.toCompileTo(
|
||||||
'<template><outer><nested><outer-block>success</outer-block></nested>success</outer></template>'
|
'<template><outer><nested><outer-block>success</outer-block></nested>success</outer></template>'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render nested partial blocks at different nesting levels (twice)', function() {
|
it('should render nested partial blocks at different nesting levels (twice)', function () {
|
||||||
expectTemplate('<template>{{#> outer}}{{value}}{{/outer}}</template>')
|
expectTemplate('<template>{{#> outer}}{{value}}{{/outer}}</template>')
|
||||||
.withInput({ value: 'success' })
|
.withInput({ value: 'success' })
|
||||||
.withPartials({
|
.withPartials({
|
||||||
outer:
|
outer:
|
||||||
'<outer>{{#> nested}}<outer-block>{{> @partial-block}} {{> @partial-block}}</outer-block>{{/nested}}{{> @partial-block}}+{{> @partial-block}}</outer>',
|
'<outer>{{#> nested}}<outer-block>{{> @partial-block}} {{> @partial-block}}</outer-block>{{/nested}}{{> @partial-block}}+{{> @partial-block}}</outer>',
|
||||||
nested: '<nested>{{> @partial-block}}</nested>'
|
nested: '<nested>{{> @partial-block}}</nested>',
|
||||||
})
|
})
|
||||||
.toCompileTo(
|
.toCompileTo(
|
||||||
'<template><outer><nested><outer-block>success success</outer-block></nested>success+success</outer></template>'
|
'<template><outer><nested><outer-block>success success</outer-block></nested>success+success</outer></template>'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render nested partial blocks (twice at each level)', function() {
|
it('should render nested partial blocks (twice at each level)', function () {
|
||||||
expectTemplate('<template>{{#> outer}}{{value}}{{/outer}}</template>')
|
expectTemplate('<template>{{#> outer}}{{value}}{{/outer}}</template>')
|
||||||
.withInput({ value: 'success' })
|
.withInput({ value: 'success' })
|
||||||
.withPartials({
|
.withPartials({
|
||||||
outer:
|
outer:
|
||||||
'<outer>{{#> nested}}<outer-block>{{> @partial-block}} {{> @partial-block}}</outer-block>{{/nested}}</outer>',
|
'<outer>{{#> nested}}<outer-block>{{> @partial-block}} {{> @partial-block}}</outer-block>{{/nested}}</outer>',
|
||||||
nested: '<nested>{{> @partial-block}}{{> @partial-block}}</nested>'
|
nested: '<nested>{{> @partial-block}}{{> @partial-block}}</nested>',
|
||||||
})
|
})
|
||||||
.toCompileTo(
|
.toCompileTo(
|
||||||
'<template><outer>' +
|
'<template><outer>' +
|
||||||
@@ -455,60 +455,60 @@ describe('partials', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('inline partials', function() {
|
describe('inline partials', function () {
|
||||||
it('should define inline partials for template', function() {
|
it('should define inline partials for template', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}'
|
'{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}'
|
||||||
).toCompileTo('success');
|
).toCompileTo('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should overwrite multiple partials in the same template', function() {
|
it('should overwrite multiple partials in the same template', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#*inline "myPartial"}}fail{{/inline}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}'
|
'{{#*inline "myPartial"}}fail{{/inline}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}'
|
||||||
).toCompileTo('success');
|
).toCompileTo('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should define inline partials for block', function() {
|
it('should define inline partials for block', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}{{/with}}'
|
'{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}{{/with}}'
|
||||||
).toCompileTo('success');
|
).toCompileTo('success');
|
||||||
|
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{/with}}{{> myPartial}}'
|
'{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{/with}}{{> myPartial}}'
|
||||||
).toThrow(Error, /myPartial could not/);
|
).toThrow(Error, /"myPartial" could not/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should override global partials', function() {
|
it('should override global partials', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}'
|
'{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}'
|
||||||
)
|
)
|
||||||
.withPartials({
|
.withPartials({
|
||||||
myPartial: function() {
|
myPartial: function () {
|
||||||
return 'fail';
|
return 'fail';
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('success');
|
.toCompileTo('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should override template partials', function() {
|
it('should override template partials', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#*inline "myPartial"}}fail{{/inline}}{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}{{/with}}'
|
'{{#*inline "myPartial"}}fail{{/inline}}{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}{{/with}}'
|
||||||
).toCompileTo('success');
|
).toCompileTo('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should override partials down the entire stack', function() {
|
it('should override partials down the entire stack', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{#with .}}{{#with .}}{{> myPartial}}{{/with}}{{/with}}{{/with}}'
|
'{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{#with .}}{{#with .}}{{> myPartial}}{{/with}}{{/with}}{{/with}}'
|
||||||
).toCompileTo('success');
|
).toCompileTo('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should define inline partials for partial call', function() {
|
it('should define inline partials for partial call', function () {
|
||||||
expectTemplate('{{#*inline "myPartial"}}success{{/inline}}{{> dude}}')
|
expectTemplate('{{#*inline "myPartial"}}success{{/inline}}{{> dude}}')
|
||||||
.withPartials({ dude: '{{> myPartial }}' })
|
.withPartials({ dude: '{{> myPartial }}' })
|
||||||
.toCompileTo('success');
|
.toCompileTo('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should define inline partials in partial block call', function() {
|
it('should define inline partials in partial block call', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#> dude}}{{#*inline "myPartial"}}success{{/inline}}{{/dude}}'
|
'{{#> dude}}{{#*inline "myPartial"}}success{{/inline}}{{/dude}}'
|
||||||
)
|
)
|
||||||
@@ -516,7 +516,7 @@ describe('partials', function() {
|
|||||||
.toCompileTo('success');
|
.toCompileTo('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render nested inline partials', function() {
|
it('should render nested inline partials', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#*inline "outer"}}{{#>inner}}<outer-block>{{>@partial-block}}</outer-block>{{/inner}}{{/inline}}' +
|
'{{#*inline "outer"}}{{#>inner}}<outer-block>{{>@partial-block}}</outer-block>{{/inner}}{{/inline}}' +
|
||||||
'{{#*inline "inner"}}<inner>{{>@partial-block}}</inner>{{/inline}}' +
|
'{{#*inline "inner"}}<inner>{{>@partial-block}}</inner>{{/inline}}' +
|
||||||
@@ -526,7 +526,7 @@ describe('partials', function() {
|
|||||||
.toCompileTo('<inner><outer-block>success</outer-block></inner>');
|
.toCompileTo('<inner><outer-block>success</outer-block></inner>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render nested inline partials with partial-blocks on different nesting levels', function() {
|
it('should render nested inline partials with partial-blocks on different nesting levels', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#*inline "outer"}}{{#>inner}}<outer-block>{{>@partial-block}}</outer-block>{{/inner}}{{>@partial-block}}{{/inline}}' +
|
'{{#*inline "outer"}}{{#>inner}}<outer-block>{{>@partial-block}}</outer-block>{{/inner}}{{>@partial-block}}{{/inline}}' +
|
||||||
'{{#*inline "inner"}}<inner>{{>@partial-block}}</inner>{{/inline}}' +
|
'{{#*inline "inner"}}<inner>{{>@partial-block}}</inner>{{/inline}}' +
|
||||||
@@ -538,7 +538,7 @@ describe('partials', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render nested inline partials (twice at each level)', function() {
|
it('should render nested inline partials (twice at each level)', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#*inline "outer"}}{{#>inner}}<outer-block>{{>@partial-block}} {{>@partial-block}}</outer-block>{{/inner}}{{/inline}}' +
|
'{{#*inline "outer"}}{{#>inner}}<outer-block>{{>@partial-block}} {{>@partial-block}}</outer-block>{{/inner}}{{/inline}}' +
|
||||||
'{{#*inline "inner"}}<inner>{{>@partial-block}}{{>@partial-block}}</inner>{{/inline}}' +
|
'{{#*inline "inner"}}<inner>{{>@partial-block}}{{>@partial-block}}</inner>{{/inline}}' +
|
||||||
@@ -551,7 +551,7 @@ describe('partials', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should pass compiler flags', function() {
|
it('should pass compiler flags', function () {
|
||||||
if (Handlebars.compile) {
|
if (Handlebars.compile) {
|
||||||
var env = Handlebars.create();
|
var env = Handlebars.create();
|
||||||
env.registerPartial('partial', '{{foo}}');
|
env.registerPartial('partial', '{{foo}}');
|
||||||
@@ -560,47 +560,47 @@ describe('partials', function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('standalone partials', function() {
|
describe('standalone partials', function () {
|
||||||
it('indented partials', function() {
|
it('indented partials', function () {
|
||||||
expectTemplate('Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}')
|
expectTemplate('Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
dudes: [
|
dudes: [
|
||||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||||
{ name: 'Alan', url: 'http://alan' }
|
{ name: 'Alan', url: 'http://alan' },
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
.withPartial('dude', '{{name}}\n')
|
.withPartial('dude', '{{name}}\n')
|
||||||
.toCompileTo('Dudes:\n Yehuda\n Alan\n');
|
.toCompileTo('Dudes:\n Yehuda\n Alan\n');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('nested indented partials', function() {
|
it('nested indented partials', function () {
|
||||||
expectTemplate('Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}')
|
expectTemplate('Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
dudes: [
|
dudes: [
|
||||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||||
{ name: 'Alan', url: 'http://alan' }
|
{ name: 'Alan', url: 'http://alan' },
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
.withPartials({
|
.withPartials({
|
||||||
dude: '{{name}}\n {{> url}}',
|
dude: '{{name}}\n {{> url}}',
|
||||||
url: '{{url}}!\n'
|
url: '{{url}}!\n',
|
||||||
})
|
})
|
||||||
.toCompileTo(
|
.toCompileTo(
|
||||||
'Dudes:\n Yehuda\n http://yehuda!\n Alan\n http://alan!\n'
|
'Dudes:\n Yehuda\n http://yehuda!\n Alan\n http://alan!\n'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('prevent nested indented partials', function() {
|
it('prevent nested indented partials', function () {
|
||||||
expectTemplate('Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}')
|
expectTemplate('Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
dudes: [
|
dudes: [
|
||||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||||
{ name: 'Alan', url: 'http://alan' }
|
{ name: 'Alan', url: 'http://alan' },
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
.withPartials({
|
.withPartials({
|
||||||
dude: '{{name}}\n {{> url}}',
|
dude: '{{name}}\n {{> url}}',
|
||||||
url: '{{url}}!\n'
|
url: '{{url}}!\n',
|
||||||
})
|
})
|
||||||
.withCompileOptions({ preventIndent: true })
|
.withCompileOptions({ preventIndent: true })
|
||||||
.toCompileTo(
|
.toCompileTo(
|
||||||
@@ -609,15 +609,15 @@ describe('partials', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('compat mode', function() {
|
describe('compat mode', function () {
|
||||||
it('partials can access parents', function() {
|
it('partials can access parents', function () {
|
||||||
expectTemplate('Dudes: {{#dudes}}{{> dude}}{{/dudes}}')
|
expectTemplate('Dudes: {{#dudes}}{{> dude}}{{/dudes}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
root: 'yes',
|
root: 'yes',
|
||||||
dudes: [
|
dudes: [
|
||||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||||
{ name: 'Alan', url: 'http://alan' }
|
{ name: 'Alan', url: 'http://alan' },
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
.withPartials({ dude: '{{name}} ({{url}}) {{root}} ' })
|
.withPartials({ dude: '{{name}} ({{url}}) {{root}} ' })
|
||||||
.withCompileOptions({ compat: true })
|
.withCompileOptions({ compat: true })
|
||||||
@@ -626,14 +626,14 @@ describe('partials', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('partials can access parents with custom context', function() {
|
it('partials can access parents with custom context', function () {
|
||||||
expectTemplate('Dudes: {{#dudes}}{{> dude "test"}}{{/dudes}}')
|
expectTemplate('Dudes: {{#dudes}}{{> dude "test"}}{{/dudes}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
root: 'yes',
|
root: 'yes',
|
||||||
dudes: [
|
dudes: [
|
||||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||||
{ name: 'Alan', url: 'http://alan' }
|
{ name: 'Alan', url: 'http://alan' },
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
.withPartials({ dude: '{{name}} ({{url}}) {{root}} ' })
|
.withPartials({ dude: '{{name}} ({{url}}) {{root}} ' })
|
||||||
.withCompileOptions({ compat: true })
|
.withCompileOptions({ compat: true })
|
||||||
@@ -642,14 +642,14 @@ describe('partials', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('partials can access parents without data', function() {
|
it('partials can access parents without data', function () {
|
||||||
expectTemplate('Dudes: {{#dudes}}{{> dude}}{{/dudes}}')
|
expectTemplate('Dudes: {{#dudes}}{{> dude}}{{/dudes}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
root: 'yes',
|
root: 'yes',
|
||||||
dudes: [
|
dudes: [
|
||||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||||
{ name: 'Alan', url: 'http://alan' }
|
{ name: 'Alan', url: 'http://alan' },
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
.withPartials({ dude: '{{name}} ({{url}}) {{root}} ' })
|
.withPartials({ dude: '{{name}} ({{url}}) {{root}} ' })
|
||||||
.withRuntimeOptions({ data: false })
|
.withRuntimeOptions({ data: false })
|
||||||
@@ -659,17 +659,17 @@ describe('partials', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('partials inherit compat', function() {
|
it('partials inherit compat', function () {
|
||||||
expectTemplate('Dudes: {{> dude}}')
|
expectTemplate('Dudes: {{> dude}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
root: 'yes',
|
root: 'yes',
|
||||||
dudes: [
|
dudes: [
|
||||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||||
{ name: 'Alan', url: 'http://alan' }
|
{ name: 'Alan', url: 'http://alan' },
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
.withPartials({
|
.withPartials({
|
||||||
dude: '{{#dudes}}{{name}} ({{url}}) {{root}} {{/dudes}}'
|
dude: '{{#dudes}}{{name}} ({{url}}) {{root}} {{/dudes}}',
|
||||||
})
|
})
|
||||||
.withCompileOptions({ compat: true })
|
.withCompileOptions({ compat: true })
|
||||||
.toCompileTo(
|
.toCompileTo(
|
||||||
|
|||||||
+90
-83
@@ -1,5 +1,5 @@
|
|||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
describe('precompiler', function() {
|
describe('precompiler', function () {
|
||||||
// NOP Under non-node environments
|
// NOP Under non-node environments
|
||||||
if (typeof process === 'undefined') {
|
if (typeof process === 'undefined') {
|
||||||
return;
|
return;
|
||||||
@@ -19,7 +19,7 @@ describe('precompiler', function() {
|
|||||||
emptyTemplate = {
|
emptyTemplate = {
|
||||||
path: __dirname + '/artifacts/empty.handlebars',
|
path: __dirname + '/artifacts/empty.handlebars',
|
||||||
name: 'empty',
|
name: 'empty',
|
||||||
source: ''
|
source: '',
|
||||||
},
|
},
|
||||||
file,
|
file,
|
||||||
content,
|
content,
|
||||||
@@ -38,7 +38,7 @@ describe('precompiler', function() {
|
|||||||
var _resolveFilename = Module._resolveFilename;
|
var _resolveFilename = Module._resolveFilename;
|
||||||
delete require.cache[require.resolve('uglify-js')];
|
delete require.cache[require.resolve('uglify-js')];
|
||||||
delete require.cache[require.resolve('../dist/cjs/precompiler')];
|
delete require.cache[require.resolve('../dist/cjs/precompiler')];
|
||||||
Module._resolveFilename = function(request, mod) {
|
Module._resolveFilename = function (request, mod) {
|
||||||
if (request === 'uglify-js') {
|
if (request === 'uglify-js') {
|
||||||
throw loadError;
|
throw loadError;
|
||||||
}
|
}
|
||||||
@@ -53,7 +53,7 @@ describe('precompiler', function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
precompile = Handlebars.precompile;
|
precompile = Handlebars.precompile;
|
||||||
minify = uglify.minify;
|
minify = uglify.minify;
|
||||||
writeFileSync = fs.writeFileSync;
|
writeFileSync = fs.writeFileSync;
|
||||||
@@ -61,21 +61,21 @@ describe('precompiler', function() {
|
|||||||
// Mock stdout and stderr
|
// 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;
|
errorLogFunction = console.error;
|
||||||
errorLog = '';
|
errorLog = '';
|
||||||
console.error = function() {
|
console.error = function () {
|
||||||
errorLog += Array.prototype.join.call(arguments, '');
|
errorLog += Array.prototype.join.call(arguments, '');
|
||||||
};
|
};
|
||||||
|
|
||||||
fs.writeFileSync = function(_file, _content) {
|
fs.writeFileSync = function (_file, _content) {
|
||||||
file = _file;
|
file = _file;
|
||||||
content = _content;
|
content = _content;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
afterEach(function() {
|
afterEach(function () {
|
||||||
Handlebars.precompile = precompile;
|
Handlebars.precompile = precompile;
|
||||||
uglify.minify = minify;
|
uglify.minify = minify;
|
||||||
fs.writeFileSync = writeFileSync;
|
fs.writeFileSync = writeFileSync;
|
||||||
@@ -83,59 +83,59 @@ describe('precompiler', function() {
|
|||||||
console.error = errorLogFunction;
|
console.error = errorLogFunction;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should output version', function() {
|
it('should output version', function () {
|
||||||
Precompiler.cli({ templates: [], version: true });
|
Precompiler.cli({ templates: [], version: true });
|
||||||
equals(log, Handlebars.VERSION);
|
equals(log, Handlebars.VERSION);
|
||||||
});
|
});
|
||||||
it('should throw if lacking templates', function() {
|
it('should throw if lacking templates', function () {
|
||||||
shouldThrow(
|
shouldThrow(
|
||||||
function() {
|
function () {
|
||||||
Precompiler.cli({ templates: [] });
|
Precompiler.cli({ templates: [] });
|
||||||
},
|
},
|
||||||
Handlebars.Exception,
|
Handlebars.Exception,
|
||||||
'Must define at least one template or directory.'
|
'Must define at least one template or directory.'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should handle empty/filtered directories', function() {
|
it('should handle empty/filtered directories', function () {
|
||||||
Precompiler.cli({ hasDirectory: true, templates: [] });
|
Precompiler.cli({ hasDirectory: true, templates: [] });
|
||||||
// Success is not throwing
|
// Success is not throwing
|
||||||
});
|
});
|
||||||
it('should throw when combining simple and minimized', function() {
|
it('should throw when combining simple and minimized', function () {
|
||||||
shouldThrow(
|
shouldThrow(
|
||||||
function() {
|
function () {
|
||||||
Precompiler.cli({ templates: [__dirname], simple: true, min: true });
|
Precompiler.cli({ templates: [__dirname], simple: true, min: true });
|
||||||
},
|
},
|
||||||
Handlebars.Exception,
|
Handlebars.Exception,
|
||||||
'Unable to minimize simple output'
|
'Unable to minimize simple output'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should throw when combining simple and multiple templates', function() {
|
it('should throw when combining simple and multiple templates', function () {
|
||||||
shouldThrow(
|
shouldThrow(
|
||||||
function() {
|
function () {
|
||||||
Precompiler.cli({
|
Precompiler.cli({
|
||||||
templates: [
|
templates: [
|
||||||
__dirname + '/artifacts/empty.handlebars',
|
__dirname + '/artifacts/empty.handlebars',
|
||||||
__dirname + '/artifacts/empty.handlebars'
|
__dirname + '/artifacts/empty.handlebars',
|
||||||
],
|
],
|
||||||
simple: true
|
simple: true,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
Handlebars.Exception,
|
Handlebars.Exception,
|
||||||
'Unable to output multiple templates in simple mode'
|
'Unable to output multiple templates in simple mode'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should throw when missing name', function() {
|
it('should throw when missing name', function () {
|
||||||
shouldThrow(
|
shouldThrow(
|
||||||
function() {
|
function () {
|
||||||
Precompiler.cli({ templates: [{ source: '' }], amd: true });
|
Precompiler.cli({ templates: [{ source: '' }], amd: true });
|
||||||
},
|
},
|
||||||
Handlebars.Exception,
|
Handlebars.Exception,
|
||||||
'Name missing for template'
|
'Name missing for template'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should throw when combining simple and directories', function() {
|
it('should throw when combining simple and directories', function () {
|
||||||
shouldThrow(
|
shouldThrow(
|
||||||
function() {
|
function () {
|
||||||
Precompiler.cli({ hasDirectory: true, templates: [1], simple: true });
|
Precompiler.cli({ hasDirectory: true, templates: [1], simple: true });
|
||||||
},
|
},
|
||||||
Handlebars.Exception,
|
Handlebars.Exception,
|
||||||
@@ -143,70 +143,77 @@ describe('precompiler', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should output simple templates', function() {
|
it('should output simple templates', function () {
|
||||||
Handlebars.precompile = function() {
|
Handlebars.precompile = function () {
|
||||||
return 'simple';
|
return 'simple';
|
||||||
};
|
};
|
||||||
Precompiler.cli({ templates: [emptyTemplate], simple: true });
|
Precompiler.cli({ templates: [emptyTemplate], simple: true });
|
||||||
equal(log, 'simple\n');
|
equal(log, 'simple\n');
|
||||||
});
|
});
|
||||||
it('should default to simple templates', function() {
|
it('should default to simple templates', function () {
|
||||||
Handlebars.precompile = function() {
|
Handlebars.precompile = function () {
|
||||||
return 'simple';
|
return 'simple';
|
||||||
};
|
};
|
||||||
Precompiler.cli({ templates: [{ source: '' }] });
|
Precompiler.cli({ templates: [{ source: '' }] });
|
||||||
equal(log, 'simple\n');
|
equal(log, 'simple\n');
|
||||||
});
|
});
|
||||||
it('should output amd templates', function() {
|
it('should output amd templates', function () {
|
||||||
Handlebars.precompile = function() {
|
Handlebars.precompile = function () {
|
||||||
return 'amd';
|
return 'amd';
|
||||||
};
|
};
|
||||||
Precompiler.cli({ templates: [emptyTemplate], amd: true });
|
Precompiler.cli({ templates: [emptyTemplate], amd: true });
|
||||||
equal(/template\(amd\)/.test(log), true);
|
equal(/template\(amd\)/.test(log), true);
|
||||||
});
|
});
|
||||||
it('should output multiple amd', function() {
|
it('should output multiple amd', function () {
|
||||||
Handlebars.precompile = function() {
|
Handlebars.precompile = function () {
|
||||||
return 'amd';
|
return 'amd';
|
||||||
};
|
};
|
||||||
Precompiler.cli({
|
Precompiler.cli({
|
||||||
templates: [emptyTemplate, emptyTemplate],
|
templates: [emptyTemplate, emptyTemplate],
|
||||||
amd: true,
|
amd: true,
|
||||||
namespace: 'foo'
|
namespace: 'foo',
|
||||||
});
|
});
|
||||||
equal(/templates = foo = foo \|\|/.test(log), true);
|
equal(/templates = foo = foo \|\|/.test(log), true);
|
||||||
equal(/return templates/.test(log), true);
|
equal(/return templates/.test(log), true);
|
||||||
equal(/template\(amd\)/.test(log), true);
|
equal(/template\(amd\)/.test(log), true);
|
||||||
});
|
});
|
||||||
it('should output amd partials', function() {
|
it('should output amd partials', function () {
|
||||||
Handlebars.precompile = function() {
|
Handlebars.precompile = function () {
|
||||||
return 'amd';
|
return 'amd';
|
||||||
};
|
};
|
||||||
Precompiler.cli({ templates: [emptyTemplate], amd: true, partial: true });
|
Precompiler.cli({ templates: [emptyTemplate], amd: true, partial: true });
|
||||||
equal(/return Handlebars\.partials\['empty'\]/.test(log), true);
|
equal(/return Handlebars\.partials\['empty'\]/.test(log), true);
|
||||||
equal(/template\(amd\)/.test(log), true);
|
equal(/template\(amd\)/.test(log), true);
|
||||||
});
|
});
|
||||||
it('should output multiple amd partials', function() {
|
it('should output multiple amd partials', function () {
|
||||||
Handlebars.precompile = function() {
|
Handlebars.precompile = function () {
|
||||||
return 'amd';
|
return 'amd';
|
||||||
};
|
};
|
||||||
Precompiler.cli({
|
Precompiler.cli({
|
||||||
templates: [emptyTemplate, emptyTemplate],
|
templates: [emptyTemplate, emptyTemplate],
|
||||||
amd: true,
|
amd: true,
|
||||||
partial: true
|
partial: true,
|
||||||
});
|
});
|
||||||
equal(/return Handlebars\.partials\[/.test(log), false);
|
equal(/return Handlebars\.partials\[/.test(log), false);
|
||||||
equal(/template\(amd\)/.test(log), true);
|
equal(/template\(amd\)/.test(log), true);
|
||||||
});
|
});
|
||||||
it('should output commonjs templates', function() {
|
it('should output es module templates', function () {
|
||||||
Handlebars.precompile = function() {
|
Handlebars.precompile = function () {
|
||||||
|
return 'esm';
|
||||||
|
};
|
||||||
|
Precompiler.cli({ templates: [emptyTemplate], esm: true });
|
||||||
|
equal(/template\(esm\)/.test(log), true);
|
||||||
|
});
|
||||||
|
it('should output commonjs templates', function () {
|
||||||
|
Handlebars.precompile = function () {
|
||||||
return 'commonjs';
|
return 'commonjs';
|
||||||
};
|
};
|
||||||
Precompiler.cli({ templates: [emptyTemplate], commonjs: true });
|
Precompiler.cli({ templates: [emptyTemplate], commonjs: true });
|
||||||
equal(/template\(commonjs\)/.test(log), true);
|
equal(/template\(commonjs\)/.test(log), true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set data flag', function() {
|
it('should set data flag', function () {
|
||||||
Handlebars.precompile = function(data, options) {
|
Handlebars.precompile = function (data, options) {
|
||||||
equal(options.data, true);
|
equal(options.data, true);
|
||||||
return 'simple';
|
return 'simple';
|
||||||
};
|
};
|
||||||
@@ -214,45 +221,45 @@ describe('precompiler', function() {
|
|||||||
equal(log, 'simple\n');
|
equal(log, 'simple\n');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set known helpers', function() {
|
it('should set known helpers', function () {
|
||||||
Handlebars.precompile = function(data, options) {
|
Handlebars.precompile = function (data, options) {
|
||||||
equal(options.knownHelpers.foo, true);
|
equal(options.knownHelpers.foo, true);
|
||||||
return 'simple';
|
return 'simple';
|
||||||
};
|
};
|
||||||
Precompiler.cli({ templates: [emptyTemplate], simple: true, known: 'foo' });
|
Precompiler.cli({ templates: [emptyTemplate], simple: true, known: 'foo' });
|
||||||
equal(log, 'simple\n');
|
equal(log, 'simple\n');
|
||||||
});
|
});
|
||||||
it('should output to file system', function() {
|
it('should output to file system', function () {
|
||||||
Handlebars.precompile = function() {
|
Handlebars.precompile = function () {
|
||||||
return 'simple';
|
return 'simple';
|
||||||
};
|
};
|
||||||
Precompiler.cli({
|
Precompiler.cli({
|
||||||
templates: [emptyTemplate],
|
templates: [emptyTemplate],
|
||||||
simple: true,
|
simple: true,
|
||||||
output: 'file!'
|
output: 'file!',
|
||||||
});
|
});
|
||||||
equal(file, 'file!');
|
equal(file, 'file!');
|
||||||
equal(content, 'simple\n');
|
equal(content, 'simple\n');
|
||||||
equal(log, '');
|
equal(log, '');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should output minimized templates', function() {
|
it('should output minimized templates', function () {
|
||||||
Handlebars.precompile = function() {
|
Handlebars.precompile = function () {
|
||||||
return 'amd';
|
return 'amd';
|
||||||
};
|
};
|
||||||
uglify.minify = function() {
|
uglify.minify = function () {
|
||||||
return { code: 'min' };
|
return { code: 'min' };
|
||||||
};
|
};
|
||||||
Precompiler.cli({ templates: [emptyTemplate], min: true });
|
Precompiler.cli({ templates: [emptyTemplate], min: true });
|
||||||
equal(log, 'min');
|
equal(log, 'min');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should omit minimization gracefully, if uglify-js is missing', function() {
|
it('should omit minimization gracefully, if uglify-js is missing', function () {
|
||||||
var error = new Error("Cannot find module 'uglify-js'");
|
var error = new Error("Cannot find module 'uglify-js'");
|
||||||
error.code = 'MODULE_NOT_FOUND';
|
error.code = 'MODULE_NOT_FOUND';
|
||||||
mockRequireUglify(error, function() {
|
mockRequireUglify(error, function () {
|
||||||
var Precompiler = require('../dist/cjs/precompiler');
|
var Precompiler = require('../dist/cjs/precompiler');
|
||||||
Handlebars.precompile = function() {
|
Handlebars.precompile = function () {
|
||||||
return 'amd';
|
return 'amd';
|
||||||
};
|
};
|
||||||
Precompiler.cli({ templates: [emptyTemplate], min: true });
|
Precompiler.cli({ templates: [emptyTemplate], min: true });
|
||||||
@@ -262,12 +269,12 @@ describe('precompiler', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail on errors (other than missing module) while loading uglify-js', function() {
|
it('should fail on errors (other than missing module) while loading uglify-js', function () {
|
||||||
mockRequireUglify(new Error('Mock Error'), function() {
|
mockRequireUglify(new Error('Mock Error'), function () {
|
||||||
shouldThrow(
|
shouldThrow(
|
||||||
function() {
|
function () {
|
||||||
var Precompiler = require('../dist/cjs/precompiler');
|
var Precompiler = require('../dist/cjs/precompiler');
|
||||||
Handlebars.precompile = function() {
|
Handlebars.precompile = function () {
|
||||||
return 'amd';
|
return 'amd';
|
||||||
};
|
};
|
||||||
Precompiler.cli({ templates: [emptyTemplate], min: true });
|
Precompiler.cli({ templates: [emptyTemplate], min: true });
|
||||||
@@ -278,35 +285,35 @@ describe('precompiler', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
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' });
|
||||||
|
|
||||||
equal(file, 'foo.js.map');
|
equal(file, 'foo.js.map');
|
||||||
equal(log.match(/sourceMappingURL=/g).length, 1);
|
equal(log.match(/sourceMappingURL=/g).length, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should output map', function() {
|
it('should output map', function () {
|
||||||
Precompiler.cli({
|
Precompiler.cli({
|
||||||
templates: [emptyTemplate],
|
templates: [emptyTemplate],
|
||||||
min: true,
|
min: true,
|
||||||
map: 'foo.js.map'
|
map: 'foo.js.map',
|
||||||
});
|
});
|
||||||
|
|
||||||
equal(file, 'foo.js.map');
|
equal(file, 'foo.js.map');
|
||||||
equal(log.match(/sourceMappingURL=/g).length, 1);
|
equal(log.match(/sourceMappingURL=/g).length, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#loadTemplates', function() {
|
describe('#loadTemplates', function () {
|
||||||
it('should throw on missing template', function(done) {
|
it('should throw on missing template', function (done) {
|
||||||
Precompiler.loadTemplates({ files: ['foo'] }, function(err) {
|
Precompiler.loadTemplates({ files: ['foo'] }, function (err) {
|
||||||
equal(err.message, 'Unable to open template file "foo"');
|
equal(err.message, 'Unable to open template file "foo"');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('should enumerate directories by extension', function(done) {
|
it('should enumerate directories by extension', function (done) {
|
||||||
Precompiler.loadTemplates(
|
Precompiler.loadTemplates(
|
||||||
{ files: [__dirname + '/artifacts'], extension: 'hbs' },
|
{ files: [__dirname + '/artifacts'], extension: 'hbs' },
|
||||||
function(err, opts) {
|
function (err, opts) {
|
||||||
equal(opts.templates.length, 2);
|
equal(opts.templates.length, 2);
|
||||||
equal(opts.templates[0].name, 'example_2');
|
equal(opts.templates[0].name, 'example_2');
|
||||||
|
|
||||||
@@ -314,10 +321,10 @@ describe('precompiler', function() {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should enumerate all templates by extension', function(done) {
|
it('should enumerate all templates by extension', function (done) {
|
||||||
Precompiler.loadTemplates(
|
Precompiler.loadTemplates(
|
||||||
{ files: [__dirname + '/artifacts'], extension: 'handlebars' },
|
{ files: [__dirname + '/artifacts'], extension: 'handlebars' },
|
||||||
function(err, opts) {
|
function (err, opts) {
|
||||||
equal(opts.templates.length, 5);
|
equal(opts.templates.length, 5);
|
||||||
equal(opts.templates[0].name, 'bom');
|
equal(opts.templates[0].name, 'bom');
|
||||||
equal(opts.templates[1].name, 'empty');
|
equal(opts.templates[1].name, 'empty');
|
||||||
@@ -326,50 +333,50 @@ describe('precompiler', function() {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should handle regular expression characters in extensions', function(done) {
|
it('should handle regular expression characters in extensions', function (done) {
|
||||||
Precompiler.loadTemplates(
|
Precompiler.loadTemplates(
|
||||||
{ files: [__dirname + '/artifacts'], extension: 'hb(s' },
|
{ files: [__dirname + '/artifacts'], extension: 'hb(s' },
|
||||||
function(err) {
|
function (err) {
|
||||||
// Success is not throwing
|
// Success is not throwing
|
||||||
done(err);
|
done(err);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should handle BOM', function(done) {
|
it('should handle BOM', function (done) {
|
||||||
var opts = {
|
var opts = {
|
||||||
files: [__dirname + '/artifacts/bom.handlebars'],
|
files: [__dirname + '/artifacts/bom.handlebars'],
|
||||||
extension: 'handlebars',
|
extension: 'handlebars',
|
||||||
bom: true
|
bom: true,
|
||||||
};
|
};
|
||||||
Precompiler.loadTemplates(opts, function(err, opts) {
|
Precompiler.loadTemplates(opts, function (err, opts) {
|
||||||
equal(opts.templates[0].source, 'a');
|
equal(opts.templates[0].source, 'a');
|
||||||
done(err);
|
done(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle different root', function(done) {
|
it('should handle different root', function (done) {
|
||||||
var opts = {
|
var opts = {
|
||||||
files: [__dirname + '/artifacts/empty.handlebars'],
|
files: [__dirname + '/artifacts/empty.handlebars'],
|
||||||
simple: true,
|
simple: true,
|
||||||
root: 'foo/'
|
root: 'foo/',
|
||||||
};
|
};
|
||||||
Precompiler.loadTemplates(opts, function(err, opts) {
|
Precompiler.loadTemplates(opts, function (err, opts) {
|
||||||
equal(opts.templates[0].name, __dirname + '/artifacts/empty');
|
equal(opts.templates[0].name, __dirname + '/artifacts/empty');
|
||||||
done(err);
|
done(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should accept string inputs', function(done) {
|
it('should accept string inputs', function (done) {
|
||||||
var opts = { string: '' };
|
var opts = { string: '' };
|
||||||
Precompiler.loadTemplates(opts, function(err, opts) {
|
Precompiler.loadTemplates(opts, function (err, opts) {
|
||||||
equal(opts.templates[0].name, undefined);
|
equal(opts.templates[0].name, undefined);
|
||||||
equal(opts.templates[0].source, '');
|
equal(opts.templates[0].source, '');
|
||||||
done(err);
|
done(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('should accept string array inputs', function(done) {
|
it('should accept string array inputs', function (done) {
|
||||||
var opts = { string: ['', 'bar'], name: ['beep', 'boop'] };
|
var opts = { string: ['', 'bar'], name: ['beep', 'boop'] };
|
||||||
Precompiler.loadTemplates(opts, function(err, opts) {
|
Precompiler.loadTemplates(opts, function (err, opts) {
|
||||||
equal(opts.templates[0].name, 'beep');
|
equal(opts.templates[0].name, 'beep');
|
||||||
equal(opts.templates[0].source, '');
|
equal(opts.templates[0].source, '');
|
||||||
equal(opts.templates[1].name, 'boop');
|
equal(opts.templates[1].name, 'boop');
|
||||||
@@ -377,9 +384,9 @@ describe('precompiler', function() {
|
|||||||
done(err);
|
done(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('should accept stdin input', function(done) {
|
it('should accept stdin input', function (done) {
|
||||||
var stdin = require('mock-stdin').stdin();
|
var stdin = require('mock-stdin').stdin();
|
||||||
Precompiler.loadTemplates({ string: '-' }, function(err, opts) {
|
Precompiler.loadTemplates({ string: '-' }, function (err, opts) {
|
||||||
equal(opts.templates[0].source, 'foo');
|
equal(opts.templates[0].source, 'foo');
|
||||||
done(err);
|
done(err);
|
||||||
});
|
});
|
||||||
@@ -387,9 +394,9 @@ describe('precompiler', function() {
|
|||||||
stdin.send('o');
|
stdin.send('o');
|
||||||
stdin.end();
|
stdin.end();
|
||||||
});
|
});
|
||||||
it('error on name missing', function(done) {
|
it('error on name missing', function (done) {
|
||||||
var opts = { string: ['', 'bar'] };
|
var opts = { string: ['', 'bar'] };
|
||||||
Precompiler.loadTemplates(opts, function(err) {
|
Precompiler.loadTemplates(opts, function (err) {
|
||||||
equal(
|
equal(
|
||||||
err.message,
|
err.message,
|
||||||
'Number of names did not match the number of string inputs'
|
'Number of names did not match the number of string inputs'
|
||||||
@@ -398,8 +405,8 @@ describe('precompiler', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should complete when no args are passed', function(done) {
|
it('should complete when no args are passed', function (done) {
|
||||||
Precompiler.loadTemplates({}, function(err, opts) {
|
Precompiler.loadTemplates({}, function (err, opts) {
|
||||||
equal(opts.templates.length, 0);
|
equal(opts.templates.length, 0);
|
||||||
done(err);
|
done(err);
|
||||||
});
|
});
|
||||||
|
|||||||
+99
-96
@@ -1,24 +1,24 @@
|
|||||||
describe('Regressions', function() {
|
describe('Regressions', function () {
|
||||||
it('GH-94: Cannot read property of undefined', function() {
|
it('GH-94: Cannot read property of undefined', function () {
|
||||||
expectTemplate('{{#books}}{{title}}{{author.name}}{{/books}}')
|
expectTemplate('{{#books}}{{title}}{{author.name}}{{/books}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
books: [
|
books: [
|
||||||
{
|
{
|
||||||
title: 'The origin of species',
|
title: 'The origin of species',
|
||||||
author: {
|
author: {
|
||||||
name: 'Charles Darwin'
|
name: 'Charles Darwin',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Lazarillo de Tormes'
|
title: 'Lazarillo de Tormes',
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
.withMessage('Renders without an undefined property error')
|
.withMessage('Renders without an undefined property error')
|
||||||
.toCompileTo('The origin of speciesCharles DarwinLazarillo de Tormes');
|
.toCompileTo('The origin of speciesCharles DarwinLazarillo de Tormes');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("GH-150: Inverted sections print when they shouldn't", function() {
|
it("GH-150: Inverted sections print when they shouldn't", function () {
|
||||||
var string = '{{^set}}not set{{/set}} :: {{#set}}set{{/set}}';
|
var string = '{{^set}}not set{{/set}} :: {{#set}}set{{/set}}';
|
||||||
|
|
||||||
expectTemplate(string)
|
expectTemplate(string)
|
||||||
@@ -43,14 +43,14 @@ describe('Regressions', function() {
|
|||||||
.toCompileTo(' :: set');
|
.toCompileTo(' :: set');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('GH-158: Using array index twice, breaks the template', function() {
|
it('GH-158: Using array index twice, breaks the template', function () {
|
||||||
expectTemplate('{{arr.[0]}}, {{arr.[1]}}')
|
expectTemplate('{{arr.[0]}}, {{arr.[1]}}')
|
||||||
.withInput({ arr: [1, 2] })
|
.withInput({ arr: [1, 2] })
|
||||||
.withMessage('it works as expected')
|
.withMessage('it works as expected')
|
||||||
.toCompileTo('1, 2');
|
.toCompileTo('1, 2');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("bug reported by @fat where lambdas weren't being properly resolved", function() {
|
it("bug reported by @fat where lambdas weren't being properly resolved", function () {
|
||||||
var string =
|
var string =
|
||||||
'<strong>This is a slightly more complicated {{thing}}.</strong>.\n' +
|
'<strong>This is a slightly more complicated {{thing}}.</strong>.\n' +
|
||||||
'{{! Just ignore this business. }}\n' +
|
'{{! Just ignore this business. }}\n' +
|
||||||
@@ -67,17 +67,17 @@ describe('Regressions', function() {
|
|||||||
'{{/hasThings}}';
|
'{{/hasThings}}';
|
||||||
|
|
||||||
var data = {
|
var data = {
|
||||||
thing: function() {
|
thing: function () {
|
||||||
return 'blah';
|
return 'blah';
|
||||||
},
|
},
|
||||||
things: [
|
things: [
|
||||||
{ className: 'one', word: '@fat' },
|
{ className: 'one', word: '@fat' },
|
||||||
{ className: 'two', word: '@dhg' },
|
{ className: 'two', word: '@dhg' },
|
||||||
{ className: 'three', word: '@sayrer' }
|
{ className: 'three', word: '@sayrer' },
|
||||||
],
|
],
|
||||||
hasThings: function() {
|
hasThings: function () {
|
||||||
return true;
|
return true;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
var output =
|
var output =
|
||||||
@@ -89,36 +89,34 @@ describe('Regressions', function() {
|
|||||||
'<li class=three>@sayrer</li>\n' +
|
'<li class=three>@sayrer</li>\n' +
|
||||||
'</ul>.\n';
|
'</ul>.\n';
|
||||||
|
|
||||||
expectTemplate(string)
|
expectTemplate(string).withInput(data).toCompileTo(output);
|
||||||
.withInput(data)
|
|
||||||
.toCompileTo(output);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('GH-408: Multiple loops fail', function() {
|
it('GH-408: Multiple loops fail', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}'
|
'{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}'
|
||||||
)
|
)
|
||||||
.withInput([
|
.withInput([
|
||||||
{ name: 'John Doe', location: { city: 'Chicago' } },
|
{ name: 'John Doe', location: { city: 'Chicago' } },
|
||||||
{ name: 'Jane Doe', location: { city: 'New York' } }
|
{ name: 'Jane Doe', location: { city: 'New York' } },
|
||||||
])
|
])
|
||||||
.withMessage('It should output multiple times')
|
.withMessage('It should output multiple times')
|
||||||
.toCompileTo('John DoeJane DoeJohn DoeJane DoeJohn DoeJane Doe');
|
.toCompileTo('John DoeJane DoeJohn DoeJane DoeJohn DoeJane Doe');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('GS-428: Nested if else rendering', function() {
|
it('GS-428: Nested if else rendering', function () {
|
||||||
var succeedingTemplate =
|
var succeedingTemplate =
|
||||||
'{{#inverse}} {{#blk}} Unexpected {{/blk}} {{else}} {{#blk}} Expected {{/blk}} {{/inverse}}';
|
'{{#inverse}} {{#blk}} Unexpected {{/blk}} {{else}} {{#blk}} Expected {{/blk}} {{/inverse}}';
|
||||||
var failingTemplate =
|
var failingTemplate =
|
||||||
'{{#inverse}} {{#blk}} Unexpected {{/blk}} {{else}} {{#blk}} Expected {{/blk}} {{/inverse}}';
|
'{{#inverse}} {{#blk}} Unexpected {{/blk}} {{else}} {{#blk}} Expected {{/blk}} {{/inverse}}';
|
||||||
|
|
||||||
var helpers = {
|
var helpers = {
|
||||||
blk: function(block) {
|
blk: function (block) {
|
||||||
return block.fn('');
|
return block.fn('');
|
||||||
},
|
},
|
||||||
inverse: function(block) {
|
inverse: function (block) {
|
||||||
return block.inverse('');
|
return block.inverse('');
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
expectTemplate(succeedingTemplate)
|
expectTemplate(succeedingTemplate)
|
||||||
@@ -130,34 +128,30 @@ describe('Regressions', function() {
|
|||||||
.toCompileTo(' Expected ');
|
.toCompileTo(' Expected ');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('GH-458: Scoped this identifier', function() {
|
it('GH-458: Scoped this identifier', function () {
|
||||||
expectTemplate('{{./foo}}')
|
expectTemplate('{{./foo}}').withInput({ foo: 'bar' }).toCompileTo('bar');
|
||||||
.withInput({ foo: 'bar' })
|
|
||||||
.toCompileTo('bar');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('GH-375: Unicode line terminators', function() {
|
it('GH-375: Unicode line terminators', function () {
|
||||||
expectTemplate('\u2028').toCompileTo('\u2028');
|
expectTemplate('\u2028').toCompileTo('\u2028');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('GH-534: Object prototype aliases', function() {
|
it('GH-534: Object prototype aliases', function () {
|
||||||
/* eslint-disable no-extend-native */
|
/* eslint-disable no-extend-native */
|
||||||
Object.prototype[0xd834] = true;
|
Object.prototype[0xd834] = true;
|
||||||
|
|
||||||
expectTemplate('{{foo}}')
|
expectTemplate('{{foo}}').withInput({ foo: 'bar' }).toCompileTo('bar');
|
||||||
.withInput({ foo: 'bar' })
|
|
||||||
.toCompileTo('bar');
|
|
||||||
|
|
||||||
delete Object.prototype[0xd834];
|
delete Object.prototype[0xd834];
|
||||||
/* eslint-enable no-extend-native */
|
/* eslint-enable no-extend-native */
|
||||||
});
|
});
|
||||||
|
|
||||||
it('GH-437: Matching escaping', function() {
|
it('GH-437: Matching escaping', function () {
|
||||||
expectTemplate('{{{a}}').toThrow(Error, /Parse error on/);
|
expectTemplate('{{{a}}').toThrow(Error, /Parse error on/);
|
||||||
expectTemplate('{{a}}}').toThrow(Error, /Parse error on/);
|
expectTemplate('{{a}}}').toThrow(Error, /Parse error on/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('GH-676: Using array in escaping mustache fails', function() {
|
it('GH-676: Using array in escaping mustache fails', function () {
|
||||||
var data = { arr: [1, 2] };
|
var data = { arr: [1, 2] };
|
||||||
|
|
||||||
expectTemplate('{{arr}}')
|
expectTemplate('{{arr}}')
|
||||||
@@ -166,7 +160,7 @@ describe('Regressions', function() {
|
|||||||
.toCompileTo(data.arr.toString());
|
.toCompileTo(data.arr.toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Mustache man page', function() {
|
it('Mustache man page', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'Hello {{name}}. You have just won ${{value}}!{{#in_ca}} Well, ${{taxed_value}}, after taxes.{{/in_ca}}'
|
'Hello {{name}}. You have just won ${{value}}!{{#in_ca}} Well, ${{taxed_value}}, after taxes.{{/in_ca}}'
|
||||||
)
|
)
|
||||||
@@ -174,7 +168,7 @@ describe('Regressions', function() {
|
|||||||
name: 'Chris',
|
name: 'Chris',
|
||||||
value: 10000,
|
value: 10000,
|
||||||
taxed_value: 10000 - 10000 * 0.4,
|
taxed_value: 10000 - 10000 * 0.4,
|
||||||
in_ca: true
|
in_ca: true,
|
||||||
})
|
})
|
||||||
.withMessage('the hello world mustache example works')
|
.withMessage('the hello world mustache example works')
|
||||||
.toCompileTo(
|
.toCompileTo(
|
||||||
@@ -182,69 +176,67 @@ describe('Regressions', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('GH-731: zero context rendering', function() {
|
it('GH-731: zero context rendering', function () {
|
||||||
expectTemplate('{{#foo}} This is {{bar}} ~ {{/foo}}')
|
expectTemplate('{{#foo}} This is {{bar}} ~ {{/foo}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
foo: 0,
|
foo: 0,
|
||||||
bar: 'OK'
|
bar: 'OK',
|
||||||
})
|
})
|
||||||
.toCompileTo(' This is ~ ');
|
.toCompileTo(' This is ~ ');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('GH-820: zero pathed rendering', function() {
|
it('GH-820: zero pathed rendering', function () {
|
||||||
expectTemplate('{{foo.bar}}')
|
expectTemplate('{{foo.bar}}').withInput({ foo: 0 }).toCompileTo('');
|
||||||
.withInput({ foo: 0 })
|
|
||||||
.toCompileTo('');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('GH-837: undefined values for helpers', function() {
|
it('GH-837: undefined values for helpers', function () {
|
||||||
expectTemplate('{{str bar.baz}}')
|
expectTemplate('{{str bar.baz}}')
|
||||||
.withHelpers({
|
.withHelpers({
|
||||||
str: function(value) {
|
str: function (value) {
|
||||||
return value + '';
|
return value + '';
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('undefined');
|
.toCompileTo('undefined');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('GH-926: Depths and de-dupe', function() {
|
it('GH-926: Depths and de-dupe', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#if dater}}{{#each data}}{{../name}}{{/each}}{{else}}{{#each notData}}{{../name}}{{/each}}{{/if}}'
|
'{{#if dater}}{{#each data}}{{../name}}{{/each}}{{else}}{{#each notData}}{{../name}}{{/each}}{{/if}}'
|
||||||
)
|
)
|
||||||
.withInput({
|
.withInput({
|
||||||
name: 'foo',
|
name: 'foo',
|
||||||
data: [1],
|
data: [1],
|
||||||
notData: [1]
|
notData: [1],
|
||||||
})
|
})
|
||||||
.toCompileTo('foo');
|
.toCompileTo('foo');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('GH-1021: Each empty string key', function() {
|
it('GH-1021: Each empty string key', function () {
|
||||||
expectTemplate('{{#each data}}Key: {{@key}}\n{{/each}}')
|
expectTemplate('{{#each data}}Key: {{@key}}\n{{/each}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
data: {
|
data: {
|
||||||
'': 'foo',
|
'': 'foo',
|
||||||
name: 'Chris',
|
name: 'Chris',
|
||||||
value: 10000
|
value: 10000,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('Key: \nKey: name\nKey: value\n');
|
.toCompileTo('Key: \nKey: name\nKey: value\n');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('GH-1054: Should handle simple safe string responses', function() {
|
it('GH-1054: Should handle simple safe string responses', function () {
|
||||||
expectTemplate('{{#wrap}}{{>partial}}{{/wrap}}')
|
expectTemplate('{{#wrap}}{{>partial}}{{/wrap}}')
|
||||||
.withHelpers({
|
.withHelpers({
|
||||||
wrap: function(options) {
|
wrap: function (options) {
|
||||||
return new Handlebars.SafeString(options.fn());
|
return new Handlebars.SafeString(options.fn());
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.withPartials({
|
.withPartials({
|
||||||
partial: '{{#wrap}}<partial>{{/wrap}}'
|
partial: '{{#wrap}}<partial>{{/wrap}}',
|
||||||
})
|
})
|
||||||
.toCompileTo('<partial>');
|
.toCompileTo('<partial>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('GH-1065: Sparse arrays', function() {
|
it('GH-1065: Sparse arrays', function () {
|
||||||
var array = [];
|
var array = [];
|
||||||
array[1] = 'foo';
|
array[1] = 'foo';
|
||||||
array[3] = 'bar';
|
array[3] = 'bar';
|
||||||
@@ -253,11 +245,11 @@ describe('Regressions', function() {
|
|||||||
.toCompileTo('1foo3bar');
|
.toCompileTo('1foo3bar');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('GH-1093: Undefined helper context', function() {
|
it('GH-1093: Undefined helper context', function () {
|
||||||
expectTemplate('{{#each obj}}{{{helper}}}{{.}}{{/each}}')
|
expectTemplate('{{#each obj}}{{{helper}}}{{.}}{{/each}}')
|
||||||
.withInput({ obj: { foo: undefined, bar: 'bat' } })
|
.withInput({ obj: { foo: undefined, bar: 'bat' } })
|
||||||
.withHelpers({
|
.withHelpers({
|
||||||
helper: function() {
|
helper: 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) {
|
||||||
@@ -267,43 +259,43 @@ describe('Regressions', function() {
|
|||||||
}
|
}
|
||||||
// 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';
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('notfoundbat');
|
.toCompileTo('notfoundbat');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support multiple levels of inline partials', function() {
|
it('should support multiple levels of inline partials', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#> layout}}{{#*inline "subcontent"}}subcontent{{/inline}}{{/layout}}'
|
'{{#> layout}}{{#*inline "subcontent"}}subcontent{{/inline}}{{/layout}}'
|
||||||
)
|
)
|
||||||
.withPartials({
|
.withPartials({
|
||||||
doctype: 'doctype{{> content}}',
|
doctype: 'doctype{{> content}}',
|
||||||
layout:
|
layout:
|
||||||
'{{#> doctype}}{{#*inline "content"}}layout{{> subcontent}}{{/inline}}{{/doctype}}'
|
'{{#> doctype}}{{#*inline "content"}}layout{{> subcontent}}{{/inline}}{{/doctype}}',
|
||||||
})
|
})
|
||||||
.toCompileTo('doctypelayoutsubcontent');
|
.toCompileTo('doctypelayoutsubcontent');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('GH-1089: should support failover content in multiple levels of inline partials', function() {
|
it('GH-1089: should support failover content in multiple levels of inline partials', function () {
|
||||||
expectTemplate('{{#> layout}}{{/layout}}')
|
expectTemplate('{{#> layout}}{{/layout}}')
|
||||||
.withPartials({
|
.withPartials({
|
||||||
doctype: 'doctype{{> content}}',
|
doctype: 'doctype{{> content}}',
|
||||||
layout:
|
layout:
|
||||||
'{{#> doctype}}{{#*inline "content"}}layout{{#> subcontent}}subcontent{{/subcontent}}{{/inline}}{{/doctype}}'
|
'{{#> doctype}}{{#*inline "content"}}layout{{#> subcontent}}subcontent{{/subcontent}}{{/inline}}{{/doctype}}',
|
||||||
})
|
})
|
||||||
.toCompileTo('doctypelayoutsubcontent');
|
.toCompileTo('doctypelayoutsubcontent');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('GH-1099: should support greater than 3 nested levels of inline partials', function() {
|
it('GH-1099: should support greater than 3 nested levels of inline partials', function () {
|
||||||
expectTemplate('{{#> layout}}Outer{{/layout}}')
|
expectTemplate('{{#> layout}}Outer{{/layout}}')
|
||||||
.withPartials({
|
.withPartials({
|
||||||
layout: '{{#> inner}}Inner{{/inner}}{{> @partial-block }}',
|
layout: '{{#> inner}}Inner{{/inner}}{{> @partial-block }}',
|
||||||
inner: ''
|
inner: '',
|
||||||
})
|
})
|
||||||
.toCompileTo('Outer');
|
.toCompileTo('Outer');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('GH-1135 : Context handling within each iteration', function() {
|
it('GH-1135 : Context handling within each iteration', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#each array}}\n' +
|
'{{#each array}}\n' +
|
||||||
' 1. IF: {{#if true}}{{../name}}-{{../../name}}-{{../../../name}}{{/if}}\n' +
|
' 1. IF: {{#if true}}{{../name}}-{{../../name}}-{{../../../name}}{{/if}}\n' +
|
||||||
@@ -312,18 +304,18 @@ describe('Regressions', function() {
|
|||||||
)
|
)
|
||||||
.withInput({ array: [1], name: 'John' })
|
.withInput({ array: [1], name: 'John' })
|
||||||
.withHelpers({
|
.withHelpers({
|
||||||
myif: function(conditional, options) {
|
myif: function (conditional, options) {
|
||||||
if (conditional) {
|
if (conditional) {
|
||||||
return options.fn(this);
|
return options.fn(this);
|
||||||
} else {
|
} else {
|
||||||
return options.inverse(this);
|
return options.inverse(this);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo(' 1. IF: John--\n' + ' 2. MYIF: John==\n');
|
.toCompileTo(' 1. IF: John--\n' + ' 2. MYIF: John==\n');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('GH-1186: Support block params for existing programs', function() {
|
it('GH-1186: Support block params for existing programs', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#*inline "test"}}{{> @partial-block }}{{/inline}}' +
|
'{{#*inline "test"}}{{> @partial-block }}{{/inline}}' +
|
||||||
'{{#>test }}{{#each listOne as |item|}}{{ item }}{{/each}}{{/test}}' +
|
'{{#>test }}{{#each listOne as |item|}}{{ item }}{{/each}}{{/test}}' +
|
||||||
@@ -331,53 +323,64 @@ describe('Regressions', function() {
|
|||||||
)
|
)
|
||||||
.withInput({
|
.withInput({
|
||||||
listOne: ['a'],
|
listOne: ['a'],
|
||||||
listTwo: ['b']
|
listTwo: ['b'],
|
||||||
})
|
})
|
||||||
.withMessage('')
|
.withMessage('')
|
||||||
.toCompileTo('ab');
|
.toCompileTo('ab');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('GH-1319: "unless" breaks when "each" value equals "null"', function() {
|
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');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('GH-1319: "unless" breaks when "each" value equals "null"', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#each list}}{{#unless ./prop}}parent={{../value}} {{/unless}}{{/each}}'
|
'{{#each list}}{{#unless ./prop}}parent={{../value}} {{/unless}}{{/each}}'
|
||||||
)
|
)
|
||||||
.withInput({
|
.withInput({
|
||||||
value: 'parent',
|
value: 'parent',
|
||||||
list: [null, 'a']
|
list: [null, 'a'],
|
||||||
})
|
})
|
||||||
.withMessage('')
|
.withMessage('')
|
||||||
.toCompileTo('parent=parent parent=parent ');
|
.toCompileTo('parent=parent parent=parent ');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('GH-1341: 4.0.7 release breaks {{#if @partial-block}} usage', function() {
|
it('GH-1341: 4.0.7 release breaks {{#if @partial-block}} usage', function () {
|
||||||
expectTemplate('template {{>partial}} template')
|
expectTemplate('template {{>partial}} template')
|
||||||
.withPartials({
|
.withPartials({
|
||||||
partialWithBlock:
|
partialWithBlock:
|
||||||
'{{#if @partial-block}} block {{> @partial-block}} block {{/if}}',
|
'{{#if @partial-block}} block {{> @partial-block}} block {{/if}}',
|
||||||
partial: '{{#> partialWithBlock}} partial {{/partialWithBlock}}'
|
partial: '{{#> partialWithBlock}} partial {{/partialWithBlock}}',
|
||||||
})
|
})
|
||||||
.toCompileTo('template block partial block template');
|
.toCompileTo('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() {
|
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() {
|
it('should compile and execute templates', function () {
|
||||||
var newHandlebarsInstance = Handlebars.create();
|
var newHandlebarsInstance = Handlebars.create();
|
||||||
|
|
||||||
registerTemplate(newHandlebarsInstance, compiledTemplateVersion7());
|
registerTemplate(newHandlebarsInstance, compiledTemplateVersion7());
|
||||||
newHandlebarsInstance.registerHelper('loud', function(value) {
|
newHandlebarsInstance.registerHelper('loud', function (value) {
|
||||||
return value.toUpperCase();
|
return value.toUpperCase();
|
||||||
});
|
});
|
||||||
var result = newHandlebarsInstance.templates['test.hbs']({
|
var result = newHandlebarsInstance.templates['test.hbs']({
|
||||||
name: 'yehuda'
|
name: 'yehuda',
|
||||||
});
|
});
|
||||||
equals(result.trim(), 'YEHUDA');
|
equals(result.trim(), 'YEHUDA');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call "helperMissing" if a helper is missing', function() {
|
it('should call "helperMissing" if a helper is missing', function () {
|
||||||
var newHandlebarsInstance = Handlebars.create();
|
var newHandlebarsInstance = Handlebars.create();
|
||||||
|
|
||||||
shouldThrow(
|
shouldThrow(
|
||||||
function() {
|
function () {
|
||||||
registerTemplate(newHandlebarsInstance, compiledTemplateVersion7());
|
registerTemplate(newHandlebarsInstance, compiledTemplateVersion7());
|
||||||
newHandlebarsInstance.templates['test.hbs']({});
|
newHandlebarsInstance.templates['test.hbs']({});
|
||||||
},
|
},
|
||||||
@@ -386,7 +389,7 @@ describe('Regressions', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should pass "options.lookupProperty" to "lookup"-helper, even with old templates', function() {
|
it('should pass "options.lookupProperty" to "lookup"-helper, even with old templates', function () {
|
||||||
var newHandlebarsInstance = Handlebars.create();
|
var newHandlebarsInstance = Handlebars.create();
|
||||||
registerTemplate(
|
registerTemplate(
|
||||||
newHandlebarsInstance,
|
newHandlebarsInstance,
|
||||||
@@ -398,7 +401,7 @@ describe('Regressions', function() {
|
|||||||
expect(
|
expect(
|
||||||
newHandlebarsInstance.templates['test.hbs']({
|
newHandlebarsInstance.templates['test.hbs']({
|
||||||
property: 'a',
|
property: 'a',
|
||||||
test: { a: 'b' }
|
test: { a: 'b' },
|
||||||
})
|
})
|
||||||
).to.equal('b');
|
).to.equal('b');
|
||||||
});
|
});
|
||||||
@@ -412,7 +415,7 @@ describe('Regressions', function() {
|
|||||||
function compiledTemplateVersion7() {
|
function compiledTemplateVersion7() {
|
||||||
return {
|
return {
|
||||||
compiler: [7, '>= 4.0.0'],
|
compiler: [7, '>= 4.0.0'],
|
||||||
main: function(container, depth0, helpers, partials, data) {
|
main: function (container, depth0, helpers, partials, data) {
|
||||||
return (
|
return (
|
||||||
container.escapeExpression(
|
container.escapeExpression(
|
||||||
(
|
(
|
||||||
@@ -427,7 +430,7 @@ describe('Regressions', function() {
|
|||||||
) + '\n\n'
|
) + '\n\n'
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
useData: true
|
useData: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -435,7 +438,7 @@ describe('Regressions', function() {
|
|||||||
// This is the compiled version of "{{lookup test property}}"
|
// This is the compiled version of "{{lookup test property}}"
|
||||||
return {
|
return {
|
||||||
compiler: [7, '>= 4.0.0'],
|
compiler: [7, '>= 4.0.0'],
|
||||||
main: function(container, depth0, helpers, partials, data) {
|
main: function (container, depth0, helpers, partials, data) {
|
||||||
return container.escapeExpression(
|
return container.escapeExpression(
|
||||||
helpers.lookup.call(
|
helpers.lookup.call(
|
||||||
depth0 != null ? depth0 : container.nullContext || {},
|
depth0 != null ? depth0 : container.nullContext || {},
|
||||||
@@ -444,45 +447,45 @@ describe('Regressions', function() {
|
|||||||
{
|
{
|
||||||
name: 'lookup',
|
name: 'lookup',
|
||||||
hash: {},
|
hash: {},
|
||||||
data: data
|
data: data,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
useData: true
|
useData: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow hash with protected array names', function() {
|
it('should allow hash with protected array names', function () {
|
||||||
expectTemplate('{{helpa length="foo"}}')
|
expectTemplate('{{helpa length="foo"}}')
|
||||||
.withInput({ array: [1], name: 'John' })
|
.withInput({ array: [1], name: 'John' })
|
||||||
.withHelpers({
|
.withHelpers({
|
||||||
helpa: function(options) {
|
helpa: function (options) {
|
||||||
return options.hash.length;
|
return options.hash.length;
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('foo');
|
.toCompileTo('foo');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GH-1598: Performance degradation for partials since v4.3.0', function() {
|
describe('GH-1598: Performance degradation for partials since v4.3.0', function () {
|
||||||
// Do not run test for runs without compiler
|
// Do not run test for runs without compiler
|
||||||
if (!Handlebars.compile) {
|
if (!Handlebars.compile) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var newHandlebarsInstance;
|
var newHandlebarsInstance;
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
newHandlebarsInstance = Handlebars.create();
|
newHandlebarsInstance = Handlebars.create();
|
||||||
});
|
});
|
||||||
afterEach(function() {
|
afterEach(function () {
|
||||||
sinon.restore();
|
sinon.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should only compile global partials once', function() {
|
it('should only compile global partials once', function () {
|
||||||
var templateSpy = sinon.spy(newHandlebarsInstance, 'template');
|
var templateSpy = sinon.spy(newHandlebarsInstance, 'template');
|
||||||
newHandlebarsInstance.registerPartial({
|
newHandlebarsInstance.registerPartial({
|
||||||
dude: 'I am a partial'
|
dude: 'I am a partial',
|
||||||
});
|
});
|
||||||
var string = 'Dudes: {{> dude}} {{> dude}}';
|
var string = 'Dudes: {{> dude}} {{> dude}}';
|
||||||
newHandlebarsInstance.compile(string)(); // This should compile template + partial once
|
newHandlebarsInstance.compile(string)(); // This should compile template + partial once
|
||||||
@@ -492,8 +495,8 @@ describe('Regressions', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("GH-1639: TypeError: Cannot read property 'apply' of undefined\" when handlebars version > 4.6.0 (undocumented, deprecated usage)", function() {
|
describe("GH-1639: TypeError: Cannot read property 'apply' of undefined\" when handlebars version > 4.6.0 (undocumented, deprecated usage)", function () {
|
||||||
it('should treat undefined helpers like non-existing helpers', function() {
|
it('should treat undefined helpers like non-existing helpers', function () {
|
||||||
expectTemplate('{{foo}}')
|
expectTemplate('{{foo}}')
|
||||||
.withHelper('foo', undefined)
|
.withHelper('foo', undefined)
|
||||||
.withInput({ foo: 'bar' })
|
.withInput({ foo: 'bar' })
|
||||||
|
|||||||
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
if (typeof require !== 'undefined' && require.extensions['.handlebars']) {
|
if (typeof require !== 'undefined' && require.extensions['.handlebars']) {
|
||||||
describe('Require', function() {
|
describe('Require', function () {
|
||||||
it('Load .handlebars files with require()', function() {
|
it('Load .handlebars files with require()', function () {
|
||||||
var template = require('./artifacts/example_1');
|
var template = require('./artifacts/example_1');
|
||||||
equal(template, require('./artifacts/example_1.handlebars'));
|
equal(template, require('./artifacts/example_1.handlebars'));
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ if (typeof require !== 'undefined' && require.extensions['.handlebars']) {
|
|||||||
equal(result, expected);
|
equal(result, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Load .hbs files with require()', function() {
|
it('Load .hbs files with require()', function () {
|
||||||
var template = require('./artifacts/example_2');
|
var template = require('./artifacts/example_2');
|
||||||
equal(template, require('./artifacts/example_2.hbs'));
|
equal(template, require('./artifacts/example_2.hbs'));
|
||||||
|
|
||||||
|
|||||||
+15
-60
@@ -1,53 +1,53 @@
|
|||||||
describe('runtime', function() {
|
describe('runtime', function () {
|
||||||
describe('#template', function() {
|
describe('#template', function () {
|
||||||
it('should throw on invalid templates', function() {
|
it('should throw on invalid templates', function () {
|
||||||
shouldThrow(
|
shouldThrow(
|
||||||
function() {
|
function () {
|
||||||
Handlebars.template({});
|
Handlebars.template({});
|
||||||
},
|
},
|
||||||
Error,
|
Error,
|
||||||
'Unknown template object: object'
|
'Unknown template object: object'
|
||||||
);
|
);
|
||||||
shouldThrow(
|
shouldThrow(
|
||||||
function() {
|
function () {
|
||||||
Handlebars.template();
|
Handlebars.template();
|
||||||
},
|
},
|
||||||
Error,
|
Error,
|
||||||
'Unknown template object: undefined'
|
'Unknown template object: undefined'
|
||||||
);
|
);
|
||||||
shouldThrow(
|
shouldThrow(
|
||||||
function() {
|
function () {
|
||||||
Handlebars.template('');
|
Handlebars.template('');
|
||||||
},
|
},
|
||||||
Error,
|
Error,
|
||||||
'Unknown template object: string'
|
'Unknown template object: string'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should throw on version mismatch', function() {
|
it('should throw on version mismatch', function () {
|
||||||
shouldThrow(
|
shouldThrow(
|
||||||
function() {
|
function () {
|
||||||
Handlebars.template({
|
Handlebars.template({
|
||||||
main: {},
|
main: {},
|
||||||
compiler: [Handlebars.COMPILER_REVISION + 1]
|
compiler: [Handlebars.COMPILER_REVISION + 1],
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
Error,
|
Error,
|
||||||
/Template was precompiled with a newer version of Handlebars than the current runtime/
|
/Template was precompiled with a newer version of Handlebars than the current runtime/
|
||||||
);
|
);
|
||||||
shouldThrow(
|
shouldThrow(
|
||||||
function() {
|
function () {
|
||||||
Handlebars.template({
|
Handlebars.template({
|
||||||
main: {},
|
main: {},
|
||||||
compiler: [Handlebars.LAST_COMPATIBLE_COMPILER_REVISION - 1]
|
compiler: [Handlebars.LAST_COMPATIBLE_COMPILER_REVISION - 1],
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
Error,
|
Error,
|
||||||
/Template was precompiled with an older version of Handlebars than the current runtime/
|
/Template was precompiled with an older version of Handlebars than the current runtime/
|
||||||
);
|
);
|
||||||
shouldThrow(
|
shouldThrow(
|
||||||
function() {
|
function () {
|
||||||
Handlebars.template({
|
Handlebars.template({
|
||||||
main: {}
|
main: {},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
Error,
|
Error,
|
||||||
@@ -56,57 +56,12 @@ describe('runtime', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#child', function() {
|
describe('#noConflict', function () {
|
||||||
if (!Handlebars.compile) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should throw for depthed methods without depths', function() {
|
|
||||||
shouldThrow(
|
|
||||||
function() {
|
|
||||||
var template = Handlebars.compile('{{#foo}}{{../bar}}{{/foo}}');
|
|
||||||
// Calling twice to hit the non-compiled case.
|
|
||||||
template._setup({});
|
|
||||||
template._setup({});
|
|
||||||
template._child(1);
|
|
||||||
},
|
|
||||||
Error,
|
|
||||||
'must pass parent depths'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw for block param methods without params', function() {
|
|
||||||
shouldThrow(
|
|
||||||
function() {
|
|
||||||
var template = Handlebars.compile('{{#foo as |foo|}}{{foo}}{{/foo}}');
|
|
||||||
// Calling twice to hit the non-compiled case.
|
|
||||||
template._setup({});
|
|
||||||
template._setup({});
|
|
||||||
template._child(1);
|
|
||||||
},
|
|
||||||
Error,
|
|
||||||
'must pass block params'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('should expose child template', function() {
|
|
||||||
var template = Handlebars.compile('{{#foo}}bar{{/foo}}');
|
|
||||||
// Calling twice to hit the non-compiled case.
|
|
||||||
equal(template._child(1)(), 'bar');
|
|
||||||
equal(template._child(1)(), 'bar');
|
|
||||||
});
|
|
||||||
it('should render depthed content', function() {
|
|
||||||
var template = Handlebars.compile('{{#foo}}{{../bar}}{{/foo}}');
|
|
||||||
// Calling twice to hit the non-compiled case.
|
|
||||||
equal(template._child(1, undefined, [], [{ bar: 'baz' }])(), 'baz');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#noConflict', function() {
|
|
||||||
if (!CompilerContext.browser) {
|
if (!CompilerContext.browser) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should reset on no conflict', function() {
|
it('should reset on no conflict', function () {
|
||||||
var reset = Handlebars;
|
var reset = Handlebars;
|
||||||
Handlebars.noConflict();
|
Handlebars.noConflict();
|
||||||
equal(Handlebars, 'no-conflict');
|
equal(Handlebars, 'no-conflict');
|
||||||
|
|||||||
+81
-85
@@ -1,25 +1,23 @@
|
|||||||
describe('security issues', function() {
|
describe('security issues', function () {
|
||||||
describe('GH-1495: Prevent Remote Code Execution via constructor', function() {
|
describe('GH-1495: Prevent Remote Code Execution via constructor', function () {
|
||||||
it('should not allow constructors to be accessed', function() {
|
it('should not allow constructors to be accessed', function () {
|
||||||
expectTemplate('{{lookup (lookup this "constructor") "name"}}')
|
expectTemplate('{{lookup (lookup this "constructor") "name"}}')
|
||||||
.withInput({})
|
.withInput({})
|
||||||
.toCompileTo('');
|
.toCompileTo('');
|
||||||
|
|
||||||
expectTemplate('{{constructor.name}}')
|
expectTemplate('{{constructor.name}}').withInput({}).toCompileTo('');
|
||||||
.withInput({})
|
|
||||||
.toCompileTo('');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('GH-1603: should not allow constructors to be accessed (lookup via toString)', function() {
|
it('GH-1603: should not allow constructors to be accessed (lookup via toString)', function () {
|
||||||
expectTemplate('{{lookup (lookup this (list "constructor")) "name"}}')
|
expectTemplate('{{lookup (lookup this (list "constructor")) "name"}}')
|
||||||
.withInput({})
|
.withInput({})
|
||||||
.withHelper('list', function(element) {
|
.withHelper('list', function (element) {
|
||||||
return [element];
|
return [element];
|
||||||
})
|
})
|
||||||
.toCompileTo('');
|
.toCompileTo('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow the "constructor" property to be accessed if it is an "ownProperty"', function() {
|
it('should allow the "constructor" property to be accessed if it is an "ownProperty"', function () {
|
||||||
expectTemplate('{{constructor.name}}')
|
expectTemplate('{{constructor.name}}')
|
||||||
.withInput({ constructor: { name: 'here we go' } })
|
.withInput({ constructor: { name: 'here we go' } })
|
||||||
.toCompileTo('here we go');
|
.toCompileTo('here we go');
|
||||||
@@ -29,79 +27,79 @@ describe('security issues', function() {
|
|||||||
.toCompileTo('here we go');
|
.toCompileTo('here we go');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow the "constructor" property to be accessed if it is an "own property"', function() {
|
it('should allow the "constructor" property to be accessed if it is an "own property"', function () {
|
||||||
expectTemplate('{{lookup (lookup this "constructor") "name"}}')
|
expectTemplate('{{lookup (lookup this "constructor") "name"}}')
|
||||||
.withInput({ constructor: { name: 'here we go' } })
|
.withInput({ constructor: { name: 'here we go' } })
|
||||||
.toCompileTo('here we go');
|
.toCompileTo('here we go');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GH-1558: Prevent explicit call of helperMissing-helpers', function() {
|
describe('GH-1558: Prevent explicit call of helperMissing-helpers', function () {
|
||||||
if (!Handlebars.compile) {
|
if (!Handlebars.compile) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('without the option "allowExplicitCallOfHelperMissing"', function() {
|
describe('without the option "allowExplicitCallOfHelperMissing"', function () {
|
||||||
it('should throw an exception when calling "{{helperMissing}}" ', function() {
|
it('should throw an exception when calling "{{helperMissing}}" ', function () {
|
||||||
expectTemplate('{{helperMissing}}').toThrow(Error);
|
expectTemplate('{{helperMissing}}').toThrow(Error);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', function() {
|
it('should throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', function () {
|
||||||
expectTemplate('{{#helperMissing}}{{/helperMissing}}').toThrow(Error);
|
expectTemplate('{{#helperMissing}}{{/helperMissing}}').toThrow(Error);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an exception when calling "{{blockHelperMissing "abc" .}}" ', function() {
|
it('should throw an exception when calling "{{blockHelperMissing "abc" .}}" ', function () {
|
||||||
var functionCalls = [];
|
var functionCalls = [];
|
||||||
expect(function() {
|
expect(function () {
|
||||||
var template = Handlebars.compile('{{blockHelperMissing "abc" .}}');
|
var template = Handlebars.compile('{{blockHelperMissing "abc" .}}');
|
||||||
template({
|
template({
|
||||||
fn: function() {
|
fn: function () {
|
||||||
functionCalls.push('called');
|
functionCalls.push('called');
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}).to.throw(Error);
|
}).to.throw(Error);
|
||||||
expect(functionCalls.length).to.equal(0);
|
expect(functionCalls.length).to.equal(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', function() {
|
it('should throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', function () {
|
||||||
expectTemplate('{{#blockHelperMissing .}}{{/blockHelperMissing}}')
|
expectTemplate('{{#blockHelperMissing .}}{{/blockHelperMissing}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
fn: function() {
|
fn: function () {
|
||||||
return 'functionInData';
|
return 'functionInData';
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toThrow(Error);
|
.toThrow(Error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('with the option "allowCallsToHelperMissing" set to true', function() {
|
describe('with the option "allowCallsToHelperMissing" set to true', function () {
|
||||||
it('should not throw an exception when calling "{{helperMissing}}" ', function() {
|
it('should not throw an exception when calling "{{helperMissing}}" ', function () {
|
||||||
var template = Handlebars.compile('{{helperMissing}}');
|
var template = Handlebars.compile('{{helperMissing}}');
|
||||||
template({}, { allowCallsToHelperMissing: true });
|
template({}, { allowCallsToHelperMissing: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', function() {
|
it('should not throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', function () {
|
||||||
var template = Handlebars.compile(
|
var template = Handlebars.compile(
|
||||||
'{{#helperMissing}}{{/helperMissing}}'
|
'{{#helperMissing}}{{/helperMissing}}'
|
||||||
);
|
);
|
||||||
template({}, { allowCallsToHelperMissing: true });
|
template({}, { allowCallsToHelperMissing: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not throw an exception when calling "{{blockHelperMissing "abc" .}}" ', function() {
|
it('should not throw an exception when calling "{{blockHelperMissing "abc" .}}" ', function () {
|
||||||
var functionCalls = [];
|
var functionCalls = [];
|
||||||
var template = Handlebars.compile('{{blockHelperMissing "abc" .}}');
|
var template = Handlebars.compile('{{blockHelperMissing "abc" .}}');
|
||||||
template(
|
template(
|
||||||
{
|
{
|
||||||
fn: function() {
|
fn: function () {
|
||||||
functionCalls.push('called');
|
functionCalls.push('called');
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{ allowCallsToHelperMissing: true }
|
{ allowCallsToHelperMissing: true }
|
||||||
);
|
);
|
||||||
equals(functionCalls.length, 1);
|
equals(functionCalls.length, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', function() {
|
it('should not throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', function () {
|
||||||
var template = Handlebars.compile(
|
var template = Handlebars.compile(
|
||||||
'{{#blockHelperMissing true}}sdads{{/blockHelperMissing}}'
|
'{{#blockHelperMissing true}}sdads{{/blockHelperMissing}}'
|
||||||
);
|
);
|
||||||
@@ -110,8 +108,8 @@ describe('security issues', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GH-1563', function() {
|
describe('GH-1563', function () {
|
||||||
it('should not allow to access constructor after overriding via __defineGetter__', function() {
|
it('should not allow to access constructor after overriding via __defineGetter__', function () {
|
||||||
if ({}.__defineGetter__ == null || {}.__lookupGetter__ == null) {
|
if ({}.__defineGetter__ == null || {}.__lookupGetter__ == null) {
|
||||||
return this.skip(); // Browser does not support this exploit anyway
|
return this.skip(); // Browser does not support this exploit anyway
|
||||||
}
|
}
|
||||||
@@ -127,7 +125,7 @@ describe('security issues', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GH-1595: dangerous properties', function() {
|
describe('GH-1595: dangerous properties', function () {
|
||||||
var templates = [
|
var templates = [
|
||||||
'{{constructor}}',
|
'{{constructor}}',
|
||||||
'{{__defineGetter__}}',
|
'{{__defineGetter__}}',
|
||||||
@@ -138,53 +136,51 @@ describe('security issues', function() {
|
|||||||
'{{lookup this "__defineGetter__"}}',
|
'{{lookup this "__defineGetter__"}}',
|
||||||
'{{lookup this "__defineSetter__"}}',
|
'{{lookup this "__defineSetter__"}}',
|
||||||
'{{lookup this "__lookupGetter__"}}',
|
'{{lookup this "__lookupGetter__"}}',
|
||||||
'{{lookup this "__proto__"}}'
|
'{{lookup this "__proto__"}}',
|
||||||
];
|
];
|
||||||
|
|
||||||
templates.forEach(function(template) {
|
templates.forEach(function (template) {
|
||||||
describe('access should be denied to ' + template, function() {
|
describe('access should be denied to ' + template, function () {
|
||||||
it('by default', function() {
|
it('by default', function () {
|
||||||
expectTemplate(template)
|
expectTemplate(template).withInput({}).toCompileTo('');
|
||||||
.withInput({})
|
|
||||||
.toCompileTo('');
|
|
||||||
});
|
});
|
||||||
it(' with proto-access enabled', function() {
|
it(' with proto-access enabled', function () {
|
||||||
expectTemplate(template)
|
expectTemplate(template)
|
||||||
.withInput({})
|
.withInput({})
|
||||||
.withRuntimeOptions({
|
.withRuntimeOptions({
|
||||||
allowProtoPropertiesByDefault: true,
|
allowProtoPropertiesByDefault: true,
|
||||||
allowProtoMethodsByDefault: true
|
allowProtoMethodsByDefault: true,
|
||||||
})
|
})
|
||||||
.toCompileTo('');
|
.toCompileTo('');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('GH-1631: disallow access to prototype functions', function() {
|
describe('GH-1631: disallow access to prototype functions', function () {
|
||||||
function TestClass() {}
|
function TestClass() {}
|
||||||
|
|
||||||
TestClass.prototype.aProperty = 'propertyValue';
|
TestClass.prototype.aProperty = 'propertyValue';
|
||||||
TestClass.prototype.aMethod = function() {
|
TestClass.prototype.aMethod = function () {
|
||||||
return 'returnValue';
|
return 'returnValue';
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
handlebarsEnv.resetLoggedPropertyAccesses();
|
handlebarsEnv.resetLoggedPropertyAccesses();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function () {
|
||||||
sinon.restore();
|
sinon.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('control access to prototype methods via "allowedProtoMethods"', function() {
|
describe('control access to prototype methods via "allowedProtoMethods"', function () {
|
||||||
checkProtoMethodAccess({});
|
checkProtoMethodAccess({});
|
||||||
|
|
||||||
describe('in compat mode', function() {
|
describe('in compat mode', function () {
|
||||||
checkProtoMethodAccess({ compat: true });
|
checkProtoMethodAccess({ compat: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
function checkProtoMethodAccess(compileOptions) {
|
function checkProtoMethodAccess(compileOptions) {
|
||||||
it('should be prohibited by default and log a warning', function() {
|
it('should be prohibited by default and log a warning', function () {
|
||||||
var spy = sinon.spy(console, 'error');
|
var spy = sinon.spy(console, 'error');
|
||||||
|
|
||||||
expectTemplate('{{aMethod}}')
|
expectTemplate('{{aMethod}}')
|
||||||
@@ -196,7 +192,7 @@ describe('security issues', function() {
|
|||||||
expect(spy.args[0][0]).to.match(/Handlebars: Access has been denied/);
|
expect(spy.args[0][0]).to.match(/Handlebars: Access has been denied/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should only log the warning once', function() {
|
it('should only log the warning once', function () {
|
||||||
var spy = sinon.spy(console, 'error');
|
var spy = sinon.spy(console, 'error');
|
||||||
|
|
||||||
expectTemplate('{{aMethod}}')
|
expectTemplate('{{aMethod}}')
|
||||||
@@ -213,7 +209,7 @@ describe('security issues', function() {
|
|||||||
expect(spy.args[0][0]).to.match(/Handlebars: Access has been denied/);
|
expect(spy.args[0][0]).to.match(/Handlebars: Access has been denied/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can be allowed, which disables the warning', function() {
|
it('can be allowed, which disables the warning', function () {
|
||||||
var spy = sinon.spy(console, 'error');
|
var spy = sinon.spy(console, 'error');
|
||||||
|
|
||||||
expectTemplate('{{aMethod}}')
|
expectTemplate('{{aMethod}}')
|
||||||
@@ -221,89 +217,89 @@ describe('security issues', function() {
|
|||||||
.withCompileOptions(compileOptions)
|
.withCompileOptions(compileOptions)
|
||||||
.withRuntimeOptions({
|
.withRuntimeOptions({
|
||||||
allowedProtoMethods: {
|
allowedProtoMethods: {
|
||||||
aMethod: true
|
aMethod: true,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('returnValue');
|
.toCompileTo('returnValue');
|
||||||
|
|
||||||
expect(spy.callCount).to.equal(0);
|
expect(spy.callCount).to.equal(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can be turned on by default, which disables the warning', function() {
|
it('can be turned on by default, which disables the warning', function () {
|
||||||
var spy = sinon.spy(console, 'error');
|
var spy = sinon.spy(console, 'error');
|
||||||
|
|
||||||
expectTemplate('{{aMethod}}')
|
expectTemplate('{{aMethod}}')
|
||||||
.withInput(new TestClass())
|
.withInput(new TestClass())
|
||||||
.withCompileOptions(compileOptions)
|
.withCompileOptions(compileOptions)
|
||||||
.withRuntimeOptions({
|
.withRuntimeOptions({
|
||||||
allowProtoMethodsByDefault: true
|
allowProtoMethodsByDefault: true,
|
||||||
})
|
})
|
||||||
.toCompileTo('returnValue');
|
.toCompileTo('returnValue');
|
||||||
|
|
||||||
expect(spy.callCount).to.equal(0);
|
expect(spy.callCount).to.equal(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can be turned off by default, which disables the warning', function() {
|
it('can be turned off by default, which disables the warning', function () {
|
||||||
var spy = sinon.spy(console, 'error');
|
var spy = sinon.spy(console, 'error');
|
||||||
|
|
||||||
expectTemplate('{{aMethod}}')
|
expectTemplate('{{aMethod}}')
|
||||||
.withInput(new TestClass())
|
.withInput(new TestClass())
|
||||||
.withCompileOptions(compileOptions)
|
.withCompileOptions(compileOptions)
|
||||||
.withRuntimeOptions({
|
.withRuntimeOptions({
|
||||||
allowProtoMethodsByDefault: false
|
allowProtoMethodsByDefault: false,
|
||||||
})
|
})
|
||||||
.toCompileTo('');
|
.toCompileTo('');
|
||||||
|
|
||||||
expect(spy.callCount).to.equal(0);
|
expect(spy.callCount).to.equal(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can be turned off, if turned on by default', function() {
|
it('can be turned off, if turned on by default', function () {
|
||||||
expectTemplate('{{aMethod}}')
|
expectTemplate('{{aMethod}}')
|
||||||
.withInput(new TestClass())
|
.withInput(new TestClass())
|
||||||
.withCompileOptions(compileOptions)
|
.withCompileOptions(compileOptions)
|
||||||
.withRuntimeOptions({
|
.withRuntimeOptions({
|
||||||
allowProtoMethodsByDefault: true,
|
allowProtoMethodsByDefault: true,
|
||||||
allowedProtoMethods: {
|
allowedProtoMethods: {
|
||||||
aMethod: false
|
aMethod: false,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('');
|
.toCompileTo('');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should cause the recursive lookup by default (in "compat" mode)', function() {
|
it('should cause the recursive lookup by default (in "compat" mode)', function () {
|
||||||
expectTemplate('{{#aString}}{{trim}}{{/aString}}')
|
expectTemplate('{{#aString}}{{trim}}{{/aString}}')
|
||||||
.withInput({ aString: ' abc ', trim: 'trim' })
|
.withInput({ aString: ' abc ', trim: 'trim' })
|
||||||
.withCompileOptions({ compat: true })
|
.withCompileOptions({ compat: true })
|
||||||
.toCompileTo('trim');
|
.toCompileTo('trim');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not cause the recursive lookup if allowed through options(in "compat" mode)', function() {
|
it('should not cause the recursive lookup if allowed through options(in "compat" mode)', function () {
|
||||||
expectTemplate('{{#aString}}{{trim}}{{/aString}}')
|
expectTemplate('{{#aString}}{{trim}}{{/aString}}')
|
||||||
.withInput({ aString: ' abc ', trim: 'trim' })
|
.withInput({ aString: ' abc ', trim: 'trim' })
|
||||||
.withCompileOptions({ compat: true })
|
.withCompileOptions({ compat: true })
|
||||||
.withRuntimeOptions({
|
.withRuntimeOptions({
|
||||||
allowedProtoMethods: {
|
allowedProtoMethods: {
|
||||||
trim: true
|
trim: true,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('abc');
|
.toCompileTo('abc');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('control access to prototype non-methods via "allowedProtoProperties" and "allowProtoPropertiesByDefault', function() {
|
describe('control access to prototype non-methods via "allowedProtoProperties" and "allowProtoPropertiesByDefault', function () {
|
||||||
checkProtoPropertyAccess({});
|
checkProtoPropertyAccess({});
|
||||||
|
|
||||||
describe('in compat-mode', function() {
|
describe('in compat-mode', function () {
|
||||||
checkProtoPropertyAccess({ compat: true });
|
checkProtoPropertyAccess({ compat: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('in strict-mode', function() {
|
describe('in strict-mode', function () {
|
||||||
checkProtoPropertyAccess({ strict: true });
|
checkProtoPropertyAccess({ strict: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
function checkProtoPropertyAccess(compileOptions) {
|
function checkProtoPropertyAccess(compileOptions) {
|
||||||
it('should be prohibited by default and log a warning', function() {
|
it('should be prohibited by default and log a warning', function () {
|
||||||
var spy = sinon.spy(console, 'error');
|
var spy = sinon.spy(console, 'error');
|
||||||
|
|
||||||
expectTemplate('{{aProperty}}')
|
expectTemplate('{{aProperty}}')
|
||||||
@@ -315,21 +311,21 @@ describe('security issues', function() {
|
|||||||
expect(spy.args[0][0]).to.match(/Handlebars: Access has been denied/);
|
expect(spy.args[0][0]).to.match(/Handlebars: Access has been denied/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can be explicitly prohibited by default, which disables the warning', function() {
|
it('can be explicitly prohibited by default, which disables the warning', function () {
|
||||||
var spy = sinon.spy(console, 'error');
|
var spy = sinon.spy(console, 'error');
|
||||||
|
|
||||||
expectTemplate('{{aProperty}}')
|
expectTemplate('{{aProperty}}')
|
||||||
.withInput(new TestClass())
|
.withInput(new TestClass())
|
||||||
.withCompileOptions(compileOptions)
|
.withCompileOptions(compileOptions)
|
||||||
.withRuntimeOptions({
|
.withRuntimeOptions({
|
||||||
allowProtoPropertiesByDefault: false
|
allowProtoPropertiesByDefault: false,
|
||||||
})
|
})
|
||||||
.toCompileTo('');
|
.toCompileTo('');
|
||||||
|
|
||||||
expect(spy.callCount).to.equal(0);
|
expect(spy.callCount).to.equal(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can be turned on, which disables the warning', function() {
|
it('can be turned on, which disables the warning', function () {
|
||||||
var spy = sinon.spy(console, 'error');
|
var spy = sinon.spy(console, 'error');
|
||||||
|
|
||||||
expectTemplate('{{aProperty}}')
|
expectTemplate('{{aProperty}}')
|
||||||
@@ -337,63 +333,63 @@ describe('security issues', function() {
|
|||||||
.withCompileOptions(compileOptions)
|
.withCompileOptions(compileOptions)
|
||||||
.withRuntimeOptions({
|
.withRuntimeOptions({
|
||||||
allowedProtoProperties: {
|
allowedProtoProperties: {
|
||||||
aProperty: true
|
aProperty: true,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('propertyValue');
|
.toCompileTo('propertyValue');
|
||||||
|
|
||||||
expect(spy.callCount).to.equal(0);
|
expect(spy.callCount).to.equal(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can be turned on by default, which disables the warning', function() {
|
it('can be turned on by default, which disables the warning', function () {
|
||||||
var spy = sinon.spy(console, 'error');
|
var spy = sinon.spy(console, 'error');
|
||||||
|
|
||||||
expectTemplate('{{aProperty}}')
|
expectTemplate('{{aProperty}}')
|
||||||
.withInput(new TestClass())
|
.withInput(new TestClass())
|
||||||
.withCompileOptions(compileOptions)
|
.withCompileOptions(compileOptions)
|
||||||
.withRuntimeOptions({
|
.withRuntimeOptions({
|
||||||
allowProtoPropertiesByDefault: true
|
allowProtoPropertiesByDefault: true,
|
||||||
})
|
})
|
||||||
.toCompileTo('propertyValue');
|
.toCompileTo('propertyValue');
|
||||||
|
|
||||||
expect(spy.callCount).to.equal(0);
|
expect(spy.callCount).to.equal(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can be turned off, if turned on by default', function() {
|
it('can be turned off, if turned on by default', function () {
|
||||||
expectTemplate('{{aProperty}}')
|
expectTemplate('{{aProperty}}')
|
||||||
.withInput(new TestClass())
|
.withInput(new TestClass())
|
||||||
.withCompileOptions(compileOptions)
|
.withCompileOptions(compileOptions)
|
||||||
.withRuntimeOptions({
|
.withRuntimeOptions({
|
||||||
allowProtoPropertiesByDefault: true,
|
allowProtoPropertiesByDefault: true,
|
||||||
allowedProtoProperties: {
|
allowedProtoProperties: {
|
||||||
aProperty: false
|
aProperty: false,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('');
|
.toCompileTo('');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('compatibility with old runtimes, that do not provide the function "container.lookupProperty"', function() {
|
describe('compatibility with old runtimes, that do not provide the function "container.lookupProperty"', function () {
|
||||||
beforeEach(function simulateRuntimeWithoutLookupProperty() {
|
beforeEach(function simulateRuntimeWithoutLookupProperty() {
|
||||||
var oldTemplateMethod = handlebarsEnv.template;
|
var oldTemplateMethod = handlebarsEnv.template;
|
||||||
sinon.replace(handlebarsEnv, 'template', function(templateSpec) {
|
sinon.replace(handlebarsEnv, 'template', function (templateSpec) {
|
||||||
templateSpec.main = wrapToAdjustContainer(templateSpec.main);
|
templateSpec.main = wrapToAdjustContainer(templateSpec.main);
|
||||||
return oldTemplateMethod.call(this, templateSpec);
|
return oldTemplateMethod.call(this, templateSpec);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function () {
|
||||||
sinon.restore();
|
sinon.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work with simple properties', function() {
|
it('should work with simple properties', function () {
|
||||||
expectTemplate('{{aProperty}}')
|
expectTemplate('{{aProperty}}')
|
||||||
.withInput({ aProperty: 'propertyValue' })
|
.withInput({ aProperty: 'propertyValue' })
|
||||||
.toCompileTo('propertyValue');
|
.toCompileTo('propertyValue');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work with Array.prototype.length', function() {
|
it('should work with Array.prototype.length', function () {
|
||||||
expectTemplate('{{anArray.length}}')
|
expectTemplate('{{anArray.length}}')
|
||||||
.withInput({ anArray: ['a', 'b', 'c'] })
|
.withInput({ anArray: ['a', 'b', 'c'] })
|
||||||
.toCompileTo('3');
|
.toCompileTo('3');
|
||||||
@@ -401,21 +397,21 @@ describe('security issues', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('escapes template variables', function() {
|
describe('escapes template variables', function () {
|
||||||
it('in compat mode', function() {
|
it('in compat mode', function () {
|
||||||
expectTemplate("{{'a\\b'}}")
|
expectTemplate("{{'a\\b'}}")
|
||||||
.withCompileOptions({ compat: true })
|
.withCompileOptions({ compat: true })
|
||||||
.withInput({ 'a\\b': 'c' })
|
.withInput({ 'a\\b': 'c' })
|
||||||
.toCompileTo('c');
|
.toCompileTo('c');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('in default mode', function() {
|
it('in default mode', function () {
|
||||||
expectTemplate("{{'a\\b'}}")
|
expectTemplate("{{'a\\b'}}")
|
||||||
.withCompileOptions()
|
.withCompileOptions()
|
||||||
.withInput({ 'a\\b': 'c' })
|
.withInput({ 'a\\b': 'c' })
|
||||||
.toCompileTo('c');
|
.toCompileTo('c');
|
||||||
});
|
});
|
||||||
it('in default mode', function() {
|
it('in default mode', function () {
|
||||||
expectTemplate("{{'a\\b'}}")
|
expectTemplate("{{'a\\b'}}")
|
||||||
.withCompileOptions({ strict: true })
|
.withCompileOptions({ strict: true })
|
||||||
.withInput({ 'a\\b': 'c' })
|
.withInput({ 'a\\b': 'c' })
|
||||||
|
|||||||
+6
-6
@@ -7,26 +7,26 @@ try {
|
|||||||
/* NOP for in browser */
|
/* NOP for in browser */
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('source-map', function() {
|
describe('source-map', function () {
|
||||||
if (!Handlebars.precompile || !SourceMap) {
|
if (!Handlebars.precompile || !SourceMap) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should safely include source map info', function() {
|
it('should safely include source map info', function () {
|
||||||
var template = Handlebars.precompile('{{hello}}', {
|
var template = Handlebars.precompile('{{hello}}', {
|
||||||
destName: 'dest.js',
|
destName: 'dest.js',
|
||||||
srcName: 'src.hbs'
|
srcName: 'src.hbs',
|
||||||
});
|
});
|
||||||
|
|
||||||
equal(!!template.code, true);
|
equal(!!template.code, true);
|
||||||
equal(!!template.map, !CompilerContext.browser);
|
equal(!!template.map, !CompilerContext.browser);
|
||||||
});
|
});
|
||||||
it('should map source properly', function() {
|
it('should map source properly', function () {
|
||||||
var templateSource =
|
var templateSource =
|
||||||
' b{{hello}} \n {{bar}}a {{#block arg hash=(subex 1 subval)}}{{/block}}',
|
' b{{hello}} \n {{bar}}a {{#block arg hash=(subex 1 subval)}}{{/block}}',
|
||||||
template = Handlebars.precompile(templateSource, {
|
template = Handlebars.precompile(templateSource, {
|
||||||
destName: 'dest.js',
|
destName: 'dest.js',
|
||||||
srcName: 'src.hbs'
|
srcName: 'src.hbs',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (template.map) {
|
if (template.map) {
|
||||||
@@ -49,7 +49,7 @@ function grepLine(token, lines) {
|
|||||||
if (column >= 0) {
|
if (column >= 0) {
|
||||||
return {
|
return {
|
||||||
line: i + 1,
|
line: i + 1,
|
||||||
column: column
|
column: column,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-18
@@ -1,45 +1,39 @@
|
|||||||
describe('spec', function() {
|
describe('spec', function () {
|
||||||
// NOP Under non-node environments
|
// NOP Under non-node environments
|
||||||
if (typeof process === 'undefined') {
|
if (typeof process === 'undefined') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ = require('underscore'),
|
var fs = require('fs');
|
||||||
fs = require('fs');
|
|
||||||
|
|
||||||
var specDir = __dirname + '/mustache/specs/';
|
var specDir = __dirname + '/mustache/specs/';
|
||||||
var specs = _.filter(fs.readdirSync(specDir), function(name) {
|
var specs = fs.readdirSync(specDir).filter((name) => /.*\.json$/.test(name));
|
||||||
return /.*\.json$/.test(name);
|
|
||||||
});
|
|
||||||
|
|
||||||
_.each(specs, function(name) {
|
specs.forEach(function (name) {
|
||||||
var spec = require(specDir + name);
|
var spec = require(specDir + name);
|
||||||
_.each(spec.tests, function(test) {
|
spec.tests.forEach(function (test) {
|
||||||
// Our lambda implementation knowingly deviates from the optional Mustace lambda spec
|
// Our lambda implementation knowingly deviates from the optional Mustache lambda spec
|
||||||
// We also do not support alternative delimeters
|
// We also do not support alternative delimiters
|
||||||
if (
|
if (
|
||||||
name === '~lambdas.json' ||
|
name === '~lambdas.json' ||
|
||||||
// We also choose to throw if paritals are not found
|
// We also choose to throw if partials are not found
|
||||||
(name === 'partials.json' && test.name === 'Failed Lookup') ||
|
(name === 'partials.json' && test.name === 'Failed Lookup') ||
|
||||||
// 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) {
|
Object.values(test.partials || {}).some((value) => /\{\{=/.test(value))
|
||||||
return /\{\{=/.test(partial);
|
|
||||||
})
|
|
||||||
) {
|
) {
|
||||||
it.skip(name + ' - ' + test.name);
|
it.skip(name + ' - ' + test.name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var data = _.clone(test.data);
|
var data = Object.assign({}, test.data); // Shallow copy
|
||||||
if (data.lambda) {
|
if (data.lambda) {
|
||||||
// Blergh
|
// Blergh
|
||||||
/* eslint-disable no-eval */
|
/* eslint-disable-next-line no-eval */
|
||||||
data.lambda = eval('(' + data.lambda.js + ')');
|
data.lambda = eval('(' + data.lambda.js + ')');
|
||||||
/* eslint-enable no-eval */
|
|
||||||
}
|
}
|
||||||
it(name + ' - ' + test.name, function() {
|
it(name + ' - ' + test.name, function () {
|
||||||
expectTemplate(test.template)
|
expectTemplate(test.template)
|
||||||
.withInput(data)
|
.withInput(data)
|
||||||
.withPartials(test.partials || {})
|
.withPartials(test.partials || {})
|
||||||
|
|||||||
+27
-27
@@ -1,14 +1,14 @@
|
|||||||
var Exception = Handlebars.Exception;
|
var Exception = Handlebars.Exception;
|
||||||
|
|
||||||
describe('strict', function() {
|
describe('strict', function () {
|
||||||
describe('strict mode', function() {
|
describe('strict mode', function () {
|
||||||
it('should error on missing property lookup', function() {
|
it('should error on missing property lookup', function () {
|
||||||
expectTemplate('{{hello}}')
|
expectTemplate('{{hello}}')
|
||||||
.withCompileOptions({ strict: true })
|
.withCompileOptions({ strict: true })
|
||||||
.toThrow(Exception, /"hello" not defined in/);
|
.toThrow(Exception, /"hello" not defined in/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should error on missing child', function() {
|
it('should error on missing child', function () {
|
||||||
expectTemplate('{{hello.bar}}')
|
expectTemplate('{{hello.bar}}')
|
||||||
.withCompileOptions({ strict: true })
|
.withCompileOptions({ strict: true })
|
||||||
.withInput({ hello: { bar: 'foo' } })
|
.withInput({ hello: { bar: 'foo' } })
|
||||||
@@ -20,31 +20,31 @@ describe('strict', function() {
|
|||||||
.toThrow(Exception, /"bar" not defined in/);
|
.toThrow(Exception, /"bar" not defined in/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle explicit undefined', function() {
|
it('should handle explicit undefined', function () {
|
||||||
expectTemplate('{{hello.bar}}')
|
expectTemplate('{{hello.bar}}')
|
||||||
.withCompileOptions({ strict: true })
|
.withCompileOptions({ strict: true })
|
||||||
.withInput({ hello: { bar: undefined } })
|
.withInput({ hello: { bar: undefined } })
|
||||||
.toCompileTo('');
|
.toCompileTo('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should error on missing property lookup in known helpers mode', function() {
|
it('should error on missing property lookup in known helpers mode', function () {
|
||||||
expectTemplate('{{hello}}')
|
expectTemplate('{{hello}}')
|
||||||
.withCompileOptions({
|
.withCompileOptions({
|
||||||
strict: true,
|
strict: true,
|
||||||
knownHelpersOnly: true
|
knownHelpersOnly: true,
|
||||||
})
|
})
|
||||||
.toThrow(Exception, /"hello" not defined in/);
|
.toThrow(Exception, /"hello" not defined in/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should error on missing context', function() {
|
it('should error on missing context', function () {
|
||||||
expectTemplate('{{hello}}')
|
expectTemplate('{{hello}}')
|
||||||
.withCompileOptions({ strict: true })
|
.withCompileOptions({ strict: true })
|
||||||
.toThrow(Error);
|
.toThrow(Error);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should error on missing data lookup', function() {
|
it('should error on missing data lookup', function () {
|
||||||
var xt = expectTemplate('{{@hello}}').withCompileOptions({
|
var xt = expectTemplate('{{@hello}}').withCompileOptions({
|
||||||
strict: true
|
strict: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
xt.toThrow(Error);
|
xt.toThrow(Error);
|
||||||
@@ -52,7 +52,7 @@ describe('strict', function() {
|
|||||||
xt.withRuntimeOptions({ data: { hello: 'foo' } }).toCompileTo('foo');
|
xt.withRuntimeOptions({ data: { hello: 'foo' } }).toCompileTo('foo');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not run helperMissing for helper calls', function() {
|
it('should not run helperMissing for helper calls', function () {
|
||||||
expectTemplate('{{hello foo}}')
|
expectTemplate('{{hello foo}}')
|
||||||
.withCompileOptions({ strict: true })
|
.withCompileOptions({ strict: true })
|
||||||
.withInput({ foo: true })
|
.withInput({ foo: true })
|
||||||
@@ -64,7 +64,7 @@ describe('strict', function() {
|
|||||||
.toThrow(Exception, /"hello" not defined in/);
|
.toThrow(Exception, /"hello" not defined in/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw on ambiguous blocks', function() {
|
it('should throw on ambiguous blocks', function () {
|
||||||
expectTemplate('{{#hello}}{{/hello}}')
|
expectTemplate('{{#hello}}{{/hello}}')
|
||||||
.withCompileOptions({ strict: true })
|
.withCompileOptions({ strict: true })
|
||||||
.toThrow(Exception, /"hello" not defined in/);
|
.toThrow(Exception, /"hello" not defined in/);
|
||||||
@@ -79,37 +79,37 @@ describe('strict', function() {
|
|||||||
.toThrow(Exception, /"bar" not defined in/);
|
.toThrow(Exception, /"bar" not defined in/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow undefined parameters when passed to helpers', function() {
|
it('should allow undefined parameters when passed to helpers', function () {
|
||||||
expectTemplate('{{#unless foo}}success{{/unless}}')
|
expectTemplate('{{#unless foo}}success{{/unless}}')
|
||||||
.withCompileOptions({ strict: true })
|
.withCompileOptions({ strict: true })
|
||||||
.toCompileTo('success');
|
.toCompileTo('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow undefined hash when passed to helpers', function() {
|
it('should allow undefined hash when passed to helpers', function () {
|
||||||
expectTemplate('{{helper value=@foo}}')
|
expectTemplate('{{helper value=@foo}}')
|
||||||
.withCompileOptions({
|
.withCompileOptions({
|
||||||
strict: true
|
strict: true,
|
||||||
})
|
})
|
||||||
.withHelpers({
|
.withHelpers({
|
||||||
helper: function(options) {
|
helper: function (options) {
|
||||||
equals('value' in options.hash, true);
|
equals('value' in options.hash, true);
|
||||||
equals(options.hash.value, undefined);
|
equals(options.hash.value, undefined);
|
||||||
return 'success';
|
return 'success';
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('success');
|
.toCompileTo('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show error location on missing property lookup', function() {
|
it('should show error location on missing property lookup', function () {
|
||||||
expectTemplate('\n\n\n {{hello}}')
|
expectTemplate('\n\n\n {{hello}}')
|
||||||
.withCompileOptions({ strict: true })
|
.withCompileOptions({ strict: true })
|
||||||
.toThrow(Exception, '"hello" not defined in [object Object] - 4:5');
|
.toThrow(Exception, '"hello" not defined in [object Object] - 4:5');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should error contains correct location properties on missing property lookup', function() {
|
it('should error contains correct location properties on missing property lookup', function () {
|
||||||
try {
|
try {
|
||||||
var template = CompilerContext.compile('\n\n\n {{hello}}', {
|
var template = CompilerContext.compile('\n\n\n {{hello}}', {
|
||||||
strict: true
|
strict: true,
|
||||||
});
|
});
|
||||||
template({});
|
template({});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -121,41 +121,41 @@ describe('strict', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('assume objects', function() {
|
describe('assume objects', function () {
|
||||||
it('should ignore missing property', function() {
|
it('should ignore missing property', function () {
|
||||||
expectTemplate('{{hello}}')
|
expectTemplate('{{hello}}')
|
||||||
.withCompileOptions({ assumeObjects: true })
|
.withCompileOptions({ assumeObjects: true })
|
||||||
.toCompileTo('');
|
.toCompileTo('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should ignore missing child', function() {
|
it('should ignore missing child', function () {
|
||||||
expectTemplate('{{hello.bar}}')
|
expectTemplate('{{hello.bar}}')
|
||||||
.withCompileOptions({ assumeObjects: true })
|
.withCompileOptions({ assumeObjects: true })
|
||||||
.withInput({ hello: {} })
|
.withInput({ hello: {} })
|
||||||
.toCompileTo('');
|
.toCompileTo('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should error on missing object', function() {
|
it('should error on missing object', function () {
|
||||||
expectTemplate('{{hello.bar}}')
|
expectTemplate('{{hello.bar}}')
|
||||||
.withCompileOptions({ assumeObjects: true })
|
.withCompileOptions({ assumeObjects: true })
|
||||||
.toThrow(Error);
|
.toThrow(Error);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should error on missing context', function() {
|
it('should error on missing context', function () {
|
||||||
expectTemplate('{{hello}}')
|
expectTemplate('{{hello}}')
|
||||||
.withCompileOptions({ assumeObjects: true })
|
.withCompileOptions({ assumeObjects: true })
|
||||||
.withInput(undefined)
|
.withInput(undefined)
|
||||||
.toThrow(Error);
|
.toThrow(Error);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should error on missing data lookup', function() {
|
it('should error on missing data lookup', function () {
|
||||||
expectTemplate('{{@hello.bar}}')
|
expectTemplate('{{@hello.bar}}')
|
||||||
.withCompileOptions({ assumeObjects: true })
|
.withCompileOptions({ assumeObjects: true })
|
||||||
.withInput(undefined)
|
.withInput(undefined)
|
||||||
.toThrow(Error);
|
.toThrow(Error);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should execute blockHelperMissing', function() {
|
it('should execute blockHelperMissing', function () {
|
||||||
expectTemplate('{{^hello}}foo{{/hello}}')
|
expectTemplate('{{^hello}}foo{{/hello}}')
|
||||||
.withCompileOptions({ assumeObjects: true })
|
.withCompileOptions({ assumeObjects: true })
|
||||||
.toCompileTo('foo');
|
.toCompileTo('foo');
|
||||||
|
|||||||
@@ -1,207 +0,0 @@
|
|||||||
describe('string params mode', function() {
|
|
||||||
it('arguments to helpers can be retrieved from options hash in string form', function() {
|
|
||||||
expectTemplate('{{wycats is.a slave.driver}}')
|
|
||||||
.withCompileOptions({
|
|
||||||
stringParams: true
|
|
||||||
})
|
|
||||||
.withHelpers({
|
|
||||||
wycats: function(passiveVoice, noun) {
|
|
||||||
return 'HELP ME MY BOSS ' + passiveVoice + ' ' + noun;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.withMessage('String parameters output')
|
|
||||||
.toCompileTo('HELP ME MY BOSS is.a slave.driver');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('when using block form, arguments to helpers can be retrieved from options hash in string form', function() {
|
|
||||||
expectTemplate('{{#wycats is.a slave.driver}}help :({{/wycats}}')
|
|
||||||
.withCompileOptions({
|
|
||||||
stringParams: true
|
|
||||||
})
|
|
||||||
.withHelpers({
|
|
||||||
wycats: function(passiveVoice, noun, options) {
|
|
||||||
return (
|
|
||||||
'HELP ME MY BOSS ' +
|
|
||||||
passiveVoice +
|
|
||||||
' ' +
|
|
||||||
noun +
|
|
||||||
': ' +
|
|
||||||
options.fn(this)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.withMessage('String parameters output')
|
|
||||||
.toCompileTo('HELP ME MY BOSS is.a slave.driver: help :(');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('when inside a block in String mode, .. passes the appropriate context in the options hash', function() {
|
|
||||||
expectTemplate('{{#with dale}}{{tomdale ../need dad.joke}}{{/with}}')
|
|
||||||
.withCompileOptions({
|
|
||||||
stringParams: true
|
|
||||||
})
|
|
||||||
.withHelpers({
|
|
||||||
tomdale: function(desire, noun, options) {
|
|
||||||
return (
|
|
||||||
'STOP ME FROM READING HACKER NEWS I ' +
|
|
||||||
options.contexts[0][desire] +
|
|
||||||
' ' +
|
|
||||||
noun
|
|
||||||
);
|
|
||||||
},
|
|
||||||
with: function(context, options) {
|
|
||||||
return options.fn(options.contexts[0][context]);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.withInput({
|
|
||||||
dale: {},
|
|
||||||
|
|
||||||
need: 'need-a'
|
|
||||||
})
|
|
||||||
.withMessage('Proper context variable output')
|
|
||||||
.toCompileTo('STOP ME FROM READING HACKER NEWS I need-a dad.joke');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('information about the types is passed along', function() {
|
|
||||||
expectTemplate("{{tomdale 'need' dad.joke true false}}")
|
|
||||||
.withCompileOptions({
|
|
||||||
stringParams: true
|
|
||||||
})
|
|
||||||
.withHelpers({
|
|
||||||
tomdale: function(desire, noun, trueBool, falseBool, options) {
|
|
||||||
equal(options.types[0], 'StringLiteral', 'the string type is passed');
|
|
||||||
equal(
|
|
||||||
options.types[1],
|
|
||||||
'PathExpression',
|
|
||||||
'the expression type is passed'
|
|
||||||
);
|
|
||||||
equal(
|
|
||||||
options.types[2],
|
|
||||||
'BooleanLiteral',
|
|
||||||
'the expression type is passed'
|
|
||||||
);
|
|
||||||
equal(desire, 'need', 'the string form is passed for strings');
|
|
||||||
equal(noun, 'dad.joke', 'the string form is passed for expressions');
|
|
||||||
equal(trueBool, true, 'raw booleans are passed through');
|
|
||||||
equal(falseBool, false, 'raw booleans are passed through');
|
|
||||||
return 'Helper called';
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.toCompileTo('Helper called');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('hash parameters get type information', function() {
|
|
||||||
expectTemplate("{{tomdale he.says desire='need' noun=dad.joke bool=true}}")
|
|
||||||
.withCompileOptions({
|
|
||||||
stringParams: true
|
|
||||||
})
|
|
||||||
.withHelpers({
|
|
||||||
tomdale: function(exclamation, options) {
|
|
||||||
equal(exclamation, 'he.says');
|
|
||||||
equal(options.types[0], 'PathExpression');
|
|
||||||
|
|
||||||
equal(options.hashTypes.desire, 'StringLiteral');
|
|
||||||
equal(options.hashTypes.noun, 'PathExpression');
|
|
||||||
equal(options.hashTypes.bool, 'BooleanLiteral');
|
|
||||||
equal(options.hash.desire, 'need');
|
|
||||||
equal(options.hash.noun, 'dad.joke');
|
|
||||||
equal(options.hash.bool, true);
|
|
||||||
return 'Helper called';
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.toCompileTo('Helper called');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('hash parameters get context information', function() {
|
|
||||||
var context = { dale: {} };
|
|
||||||
|
|
||||||
var helpers = {
|
|
||||||
tomdale: function(exclamation, options) {
|
|
||||||
equal(exclamation, 'he.says');
|
|
||||||
equal(options.types[0], 'PathExpression');
|
|
||||||
|
|
||||||
equal(options.contexts.length, 1);
|
|
||||||
equal(options.hashContexts.noun, context);
|
|
||||||
equal(options.hash.desire, 'need');
|
|
||||||
equal(options.hash.noun, 'dad.joke');
|
|
||||||
equal(options.hash.bool, true);
|
|
||||||
return 'Helper called';
|
|
||||||
},
|
|
||||||
with: function(withContext, options) {
|
|
||||||
return options.fn(options.contexts[0][withContext]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
expectTemplate(
|
|
||||||
"{{#with dale}}{{tomdale he.says desire='need' noun=../dad/joke bool=true}}{{/with}}"
|
|
||||||
)
|
|
||||||
.withCompileOptions({ stringParams: true })
|
|
||||||
.withHelpers(helpers)
|
|
||||||
.withInput(context)
|
|
||||||
.toCompileTo('Helper called');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('when inside a block in String mode, .. passes the appropriate context in the options hash to a block helper', function() {
|
|
||||||
expectTemplate(
|
|
||||||
'{{#with dale}}{{#tomdale ../need dad.joke}}wot{{/tomdale}}{{/with}}'
|
|
||||||
)
|
|
||||||
.withCompileOptions({
|
|
||||||
stringParams: true
|
|
||||||
})
|
|
||||||
.withHelpers({
|
|
||||||
tomdale: function(desire, noun, options) {
|
|
||||||
return (
|
|
||||||
'STOP ME FROM READING HACKER NEWS I ' +
|
|
||||||
options.contexts[0][desire] +
|
|
||||||
' ' +
|
|
||||||
noun +
|
|
||||||
' ' +
|
|
||||||
options.fn(this)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
with: function(context, options) {
|
|
||||||
return options.fn(options.contexts[0][context]);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.withInput({
|
|
||||||
dale: {},
|
|
||||||
|
|
||||||
need: 'need-a'
|
|
||||||
})
|
|
||||||
.withMessage('Proper context variable output')
|
|
||||||
.toCompileTo('STOP ME FROM READING HACKER NEWS I need-a dad.joke wot');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('with nested block ambiguous', function() {
|
|
||||||
expectTemplate(
|
|
||||||
'{{#with content}}{{#view}}{{firstName}} {{lastName}}{{/view}}{{/with}}'
|
|
||||||
)
|
|
||||||
.withCompileOptions({
|
|
||||||
stringParams: true
|
|
||||||
})
|
|
||||||
.withHelpers({
|
|
||||||
with: function() {
|
|
||||||
return 'WITH';
|
|
||||||
},
|
|
||||||
view: function() {
|
|
||||||
return 'VIEW';
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.toCompileTo('WITH');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle DATA', function() {
|
|
||||||
expectTemplate('{{foo @bar}}')
|
|
||||||
.withCompileOptions({
|
|
||||||
stringParams: true
|
|
||||||
})
|
|
||||||
.withHelpers({
|
|
||||||
foo: function(bar, options) {
|
|
||||||
equal(bar, '@bar');
|
|
||||||
equal(options.types[0], 'PathExpression');
|
|
||||||
return 'Foo!';
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.toCompileTo('Foo!');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
+51
-111
@@ -1,68 +1,68 @@
|
|||||||
describe('subexpressions', function() {
|
describe('subexpressions', function () {
|
||||||
it('arg-less helper', function() {
|
it('arg-less helper', function () {
|
||||||
expectTemplate('{{foo (bar)}}!')
|
expectTemplate('{{foo (bar)}}!')
|
||||||
.withHelpers({
|
.withHelpers({
|
||||||
foo: function(val) {
|
foo: function (val) {
|
||||||
return val + val;
|
return val + val;
|
||||||
},
|
},
|
||||||
bar: function() {
|
bar: function () {
|
||||||
return 'LOL';
|
return 'LOL';
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('LOLLOL!');
|
.toCompileTo('LOLLOL!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('helper w args', function() {
|
it('helper w args', function () {
|
||||||
expectTemplate('{{blog (equal a b)}}')
|
expectTemplate('{{blog (equal a b)}}')
|
||||||
.withInput({ bar: 'LOL' })
|
.withInput({ bar: 'LOL' })
|
||||||
.withHelpers({
|
.withHelpers({
|
||||||
blog: function(val) {
|
blog: function (val) {
|
||||||
return 'val is ' + val;
|
return 'val is ' + val;
|
||||||
},
|
},
|
||||||
equal: function(x, y) {
|
equal: function (x, y) {
|
||||||
return x === y;
|
return x === y;
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('val is true');
|
.toCompileTo('val is true');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('mixed paths and helpers', function() {
|
it('mixed paths and helpers', function () {
|
||||||
expectTemplate('{{blog baz.bat (equal a b) baz.bar}}')
|
expectTemplate('{{blog baz.bat (equal a b) baz.bar}}')
|
||||||
.withInput({ bar: 'LOL', baz: { bat: 'foo!', bar: 'bar!' } })
|
.withInput({ bar: 'LOL', baz: { bat: 'foo!', bar: 'bar!' } })
|
||||||
.withHelpers({
|
.withHelpers({
|
||||||
blog: function(val, that, theOther) {
|
blog: function (val, that, theOther) {
|
||||||
return 'val is ' + val + ', ' + that + ' and ' + theOther;
|
return 'val is ' + val + ', ' + that + ' and ' + theOther;
|
||||||
},
|
},
|
||||||
equal: function(x, y) {
|
equal: function (x, y) {
|
||||||
return x === y;
|
return x === y;
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('val is foo!, true and bar!');
|
.toCompileTo('val is foo!, true and bar!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('supports much nesting', function() {
|
it('supports much nesting', function () {
|
||||||
expectTemplate('{{blog (equal (equal true true) true)}}')
|
expectTemplate('{{blog (equal (equal true true) true)}}')
|
||||||
.withInput({ bar: 'LOL' })
|
.withInput({ bar: 'LOL' })
|
||||||
.withHelpers({
|
.withHelpers({
|
||||||
blog: function(val) {
|
blog: function (val) {
|
||||||
return 'val is ' + val;
|
return 'val is ' + val;
|
||||||
},
|
},
|
||||||
equal: function(x, y) {
|
equal: function (x, y) {
|
||||||
return x === y;
|
return x === y;
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('val is true');
|
.toCompileTo('val is true');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('GH-800 : Complex subexpressions', function() {
|
it('GH-800 : Complex subexpressions', function () {
|
||||||
var context = { a: 'a', b: 'b', c: { c: 'c' }, d: 'd', e: { e: 'e' } };
|
var context = { a: 'a', b: 'b', c: { c: 'c' }, d: 'd', e: { e: 'e' } };
|
||||||
var helpers = {
|
var helpers = {
|
||||||
dash: function(a, b) {
|
dash: function (a, b) {
|
||||||
return a + '-' + b;
|
return a + '-' + b;
|
||||||
},
|
},
|
||||||
concat: function(a, b) {
|
concat: function (a, b) {
|
||||||
return a + b;
|
return a + b;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
expectTemplate("{{dash 'abc' (concat a b)}}")
|
expectTemplate("{{dash 'abc' (concat a b)}}")
|
||||||
@@ -91,55 +91,55 @@ describe('subexpressions', function() {
|
|||||||
.toCompileTo('ae-c');
|
.toCompileTo('ae-c');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('provides each nested helper invocation its own options hash', function() {
|
it('provides each nested helper invocation its own options hash', function () {
|
||||||
var lastOptions = null;
|
var lastOptions = null;
|
||||||
var helpers = {
|
var helpers = {
|
||||||
equal: function(x, y, options) {
|
equal: function (x, y, options) {
|
||||||
if (!options || options === lastOptions) {
|
if (!options || options === lastOptions) {
|
||||||
throw new Error('options hash was reused');
|
throw new Error('options hash was reused');
|
||||||
}
|
}
|
||||||
lastOptions = options;
|
lastOptions = options;
|
||||||
return x === y;
|
return x === y;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
expectTemplate('{{equal (equal true true) true}}')
|
expectTemplate('{{equal (equal true true) true}}')
|
||||||
.withHelpers(helpers)
|
.withHelpers(helpers)
|
||||||
.toCompileTo('true');
|
.toCompileTo('true');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('with hashes', function() {
|
it('with hashes', function () {
|
||||||
expectTemplate("{{blog (equal (equal true true) true fun='yes')}}")
|
expectTemplate("{{blog (equal (equal true true) true fun='yes')}}")
|
||||||
.withInput({ bar: 'LOL' })
|
.withInput({ bar: 'LOL' })
|
||||||
.withHelpers({
|
.withHelpers({
|
||||||
blog: function(val) {
|
blog: function (val) {
|
||||||
return 'val is ' + val;
|
return 'val is ' + val;
|
||||||
},
|
},
|
||||||
equal: function(x, y) {
|
equal: function (x, y) {
|
||||||
return x === y;
|
return x === y;
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('val is true');
|
.toCompileTo('val is true');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('as hashes', function() {
|
it('as hashes', function () {
|
||||||
expectTemplate("{{blog fun=(equal (blog fun=1) 'val is 1')}}")
|
expectTemplate("{{blog fun=(equal (blog fun=1) 'val is 1')}}")
|
||||||
.withHelpers({
|
.withHelpers({
|
||||||
blog: function(options) {
|
blog: function (options) {
|
||||||
return 'val is ' + options.hash.fun;
|
return 'val is ' + options.hash.fun;
|
||||||
},
|
},
|
||||||
equal: function(x, y) {
|
equal: function (x, y) {
|
||||||
return x === y;
|
return x === y;
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('val is true');
|
.toCompileTo('val is true');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('multiple subexpressions in a hash', function() {
|
it('multiple subexpressions in a hash', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{input aria-label=(t "Name") placeholder=(t "Example User")}}'
|
'{{input aria-label=(t "Name") placeholder=(t "Example User")}}'
|
||||||
)
|
)
|
||||||
.withHelpers({
|
.withHelpers({
|
||||||
input: function(options) {
|
input: function (options) {
|
||||||
var hash = options.hash;
|
var hash = options.hash;
|
||||||
var ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']);
|
var ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']);
|
||||||
var placeholder = Handlebars.Utils.escapeExpression(hash.placeholder);
|
var placeholder = Handlebars.Utils.escapeExpression(hash.placeholder);
|
||||||
@@ -151,25 +151,25 @@ describe('subexpressions', function() {
|
|||||||
'" />'
|
'" />'
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
t: function(defaultString) {
|
t: function (defaultString) {
|
||||||
return new Handlebars.SafeString(defaultString);
|
return new Handlebars.SafeString(defaultString);
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('<input aria-label="Name" placeholder="Example User" />');
|
.toCompileTo('<input aria-label="Name" placeholder="Example User" />');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('multiple subexpressions in a hash with context', function() {
|
it('multiple subexpressions in a hash with context', function () {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{input aria-label=(t item.field) placeholder=(t item.placeholder)}}'
|
'{{input aria-label=(t item.field) placeholder=(t item.placeholder)}}'
|
||||||
)
|
)
|
||||||
.withInput({
|
.withInput({
|
||||||
item: {
|
item: {
|
||||||
field: 'Name',
|
field: 'Name',
|
||||||
placeholder: 'Example User'
|
placeholder: 'Example User',
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.withHelpers({
|
.withHelpers({
|
||||||
input: function(options) {
|
input: function (options) {
|
||||||
var hash = options.hash;
|
var hash = options.hash;
|
||||||
var ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']);
|
var ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']);
|
||||||
var placeholder = Handlebars.Utils.escapeExpression(hash.placeholder);
|
var placeholder = Handlebars.Utils.escapeExpression(hash.placeholder);
|
||||||
@@ -181,97 +181,37 @@ describe('subexpressions', function() {
|
|||||||
'" />'
|
'" />'
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
t: function(defaultString) {
|
t: function (defaultString) {
|
||||||
return new Handlebars.SafeString(defaultString);
|
return new Handlebars.SafeString(defaultString);
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('<input aria-label="Name" placeholder="Example User" />');
|
.toCompileTo('<input aria-label="Name" placeholder="Example User" />');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('in string params mode,', function() {
|
it('subexpression functions on the context', function () {
|
||||||
expectTemplate('{{snog (blorg foo x=y) yeah a=b}}')
|
|
||||||
.withCompileOptions({ stringParams: true })
|
|
||||||
.withHelpers({
|
|
||||||
snog: function(a, b, options) {
|
|
||||||
equals(a, 'foo');
|
|
||||||
equals(
|
|
||||||
options.types.length,
|
|
||||||
2,
|
|
||||||
'string params for outer helper processed correctly'
|
|
||||||
);
|
|
||||||
equals(
|
|
||||||
options.types[0],
|
|
||||||
'SubExpression',
|
|
||||||
'string params for outer helper processed correctly'
|
|
||||||
);
|
|
||||||
equals(
|
|
||||||
options.types[1],
|
|
||||||
'PathExpression',
|
|
||||||
'string params for outer helper processed correctly'
|
|
||||||
);
|
|
||||||
return a + b;
|
|
||||||
},
|
|
||||||
|
|
||||||
blorg: function(a, options) {
|
|
||||||
equals(
|
|
||||||
options.types.length,
|
|
||||||
1,
|
|
||||||
'string params for inner helper processed correctly'
|
|
||||||
);
|
|
||||||
equals(
|
|
||||||
options.types[0],
|
|
||||||
'PathExpression',
|
|
||||||
'string params for inner helper processed correctly'
|
|
||||||
);
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.withInput({
|
|
||||||
foo: {},
|
|
||||||
yeah: {}
|
|
||||||
})
|
|
||||||
.toCompileTo('fooyeah');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('as hashes in string params mode', function() {
|
|
||||||
expectTemplate('{{blog fun=(bork)}}')
|
|
||||||
.withCompileOptions({ stringParams: true })
|
|
||||||
.withHelpers({
|
|
||||||
blog: function(options) {
|
|
||||||
equals(options.hashTypes.fun, 'SubExpression');
|
|
||||||
return 'val is ' + options.hash.fun;
|
|
||||||
},
|
|
||||||
bork: function() {
|
|
||||||
return 'BORK';
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.toCompileTo('val is BORK');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('subexpression functions on the context', function() {
|
|
||||||
expectTemplate('{{foo (bar)}}!')
|
expectTemplate('{{foo (bar)}}!')
|
||||||
.withInput({
|
.withInput({
|
||||||
bar: function() {
|
bar: function () {
|
||||||
return 'LOL';
|
return 'LOL';
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.withHelpers({
|
.withHelpers({
|
||||||
foo: function(val) {
|
foo: function (val) {
|
||||||
return val + val;
|
return val + val;
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('LOLLOL!');
|
.toCompileTo('LOLLOL!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("subexpressions can't just be property lookups", function() {
|
it("subexpressions can't just be property lookups", function () {
|
||||||
expectTemplate('{{foo (bar)}}!')
|
expectTemplate('{{foo (bar)}}!')
|
||||||
.withInput({
|
.withInput({
|
||||||
bar: 'LOL'
|
bar: 'LOL',
|
||||||
})
|
})
|
||||||
.withHelpers({
|
.withHelpers({
|
||||||
foo: function(val) {
|
foo: function (val) {
|
||||||
return val + val;
|
return val + val;
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toThrow();
|
.toThrow();
|
||||||
});
|
});
|
||||||
|
|||||||
+93
-93
@@ -8,7 +8,7 @@ function shouldBeToken(result, name, text) {
|
|||||||
equals(result.text, text);
|
equals(result.text, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Tokenizer', function() {
|
describe('Tokenizer', function () {
|
||||||
if (!Handlebars.Parser) {
|
if (!Handlebars.Parser) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -32,13 +32,13 @@ describe('Tokenizer', function() {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
it('tokenizes a simple mustache as "OPEN ID CLOSE"', function() {
|
it('tokenizes a simple mustache as "OPEN ID CLOSE"', function () {
|
||||||
var result = tokenize('{{foo}}');
|
var result = tokenize('{{foo}}');
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE']);
|
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE']);
|
||||||
shouldBeToken(result[1], 'ID', 'foo');
|
shouldBeToken(result[1], 'ID', 'foo');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('supports unescaping with &', function() {
|
it('supports unescaping with &', function () {
|
||||||
var result = tokenize('{{&bar}}');
|
var result = tokenize('{{&bar}}');
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE']);
|
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE']);
|
||||||
|
|
||||||
@@ -46,14 +46,14 @@ describe('Tokenizer', function() {
|
|||||||
shouldBeToken(result[1], 'ID', 'bar');
|
shouldBeToken(result[1], 'ID', 'bar');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('supports unescaping with {{{', function() {
|
it('supports unescaping with {{{', function () {
|
||||||
var result = tokenize('{{{bar}}}');
|
var result = tokenize('{{{bar}}}');
|
||||||
shouldMatchTokens(result, ['OPEN_UNESCAPED', 'ID', 'CLOSE_UNESCAPED']);
|
shouldMatchTokens(result, ['OPEN_UNESCAPED', 'ID', 'CLOSE_UNESCAPED']);
|
||||||
|
|
||||||
shouldBeToken(result[1], 'ID', 'bar');
|
shouldBeToken(result[1], 'ID', 'bar');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('supports escaping delimiters', function() {
|
it('supports escaping delimiters', function () {
|
||||||
var result = tokenize('{{foo}} \\{{bar}} {{baz}}');
|
var result = tokenize('{{foo}} \\{{bar}} {{baz}}');
|
||||||
shouldMatchTokens(result, [
|
shouldMatchTokens(result, [
|
||||||
'OPEN',
|
'OPEN',
|
||||||
@@ -63,14 +63,14 @@ describe('Tokenizer', function() {
|
|||||||
'CONTENT',
|
'CONTENT',
|
||||||
'OPEN',
|
'OPEN',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
shouldBeToken(result[3], 'CONTENT', ' ');
|
shouldBeToken(result[3], 'CONTENT', ' ');
|
||||||
shouldBeToken(result[4], 'CONTENT', '{{bar}} ');
|
shouldBeToken(result[4], 'CONTENT', '{{bar}} ');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('supports escaping multiple delimiters', function() {
|
it('supports escaping multiple delimiters', function () {
|
||||||
var result = tokenize('{{foo}} \\{{bar}} \\{{baz}}');
|
var result = tokenize('{{foo}} \\{{bar}} \\{{baz}}');
|
||||||
shouldMatchTokens(result, [
|
shouldMatchTokens(result, [
|
||||||
'OPEN',
|
'OPEN',
|
||||||
@@ -78,7 +78,7 @@ describe('Tokenizer', function() {
|
|||||||
'CLOSE',
|
'CLOSE',
|
||||||
'CONTENT',
|
'CONTENT',
|
||||||
'CONTENT',
|
'CONTENT',
|
||||||
'CONTENT'
|
'CONTENT',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
shouldBeToken(result[3], 'CONTENT', ' ');
|
shouldBeToken(result[3], 'CONTENT', ' ');
|
||||||
@@ -86,7 +86,7 @@ describe('Tokenizer', function() {
|
|||||||
shouldBeToken(result[5], 'CONTENT', '{{baz}}');
|
shouldBeToken(result[5], 'CONTENT', '{{baz}}');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('supports escaping a triple stash', function() {
|
it('supports escaping a triple stash', function () {
|
||||||
var result = tokenize('{{foo}} \\{{{bar}}} {{baz}}');
|
var result = tokenize('{{foo}} \\{{{bar}}} {{baz}}');
|
||||||
shouldMatchTokens(result, [
|
shouldMatchTokens(result, [
|
||||||
'OPEN',
|
'OPEN',
|
||||||
@@ -96,13 +96,13 @@ describe('Tokenizer', function() {
|
|||||||
'CONTENT',
|
'CONTENT',
|
||||||
'OPEN',
|
'OPEN',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
shouldBeToken(result[4], 'CONTENT', '{{{bar}}} ');
|
shouldBeToken(result[4], 'CONTENT', '{{{bar}}} ');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('supports escaping escape character', function() {
|
it('supports escaping escape character', function () {
|
||||||
var result = tokenize('{{foo}} \\\\{{bar}} {{baz}}');
|
var result = tokenize('{{foo}} \\\\{{bar}} {{baz}}');
|
||||||
shouldMatchTokens(result, [
|
shouldMatchTokens(result, [
|
||||||
'OPEN',
|
'OPEN',
|
||||||
@@ -115,14 +115,14 @@ describe('Tokenizer', function() {
|
|||||||
'CONTENT',
|
'CONTENT',
|
||||||
'OPEN',
|
'OPEN',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
shouldBeToken(result[3], 'CONTENT', ' \\');
|
shouldBeToken(result[3], 'CONTENT', ' \\');
|
||||||
shouldBeToken(result[5], 'ID', 'bar');
|
shouldBeToken(result[5], 'ID', 'bar');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('supports escaping multiple escape characters', function() {
|
it('supports escaping multiple escape characters', function () {
|
||||||
var result = tokenize('{{foo}} \\\\{{bar}} \\\\{{baz}}');
|
var result = tokenize('{{foo}} \\\\{{bar}} \\\\{{baz}}');
|
||||||
shouldMatchTokens(result, [
|
shouldMatchTokens(result, [
|
||||||
'OPEN',
|
'OPEN',
|
||||||
@@ -135,7 +135,7 @@ describe('Tokenizer', function() {
|
|||||||
'CONTENT',
|
'CONTENT',
|
||||||
'OPEN',
|
'OPEN',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
shouldBeToken(result[3], 'CONTENT', ' \\');
|
shouldBeToken(result[3], 'CONTENT', ' \\');
|
||||||
@@ -144,7 +144,7 @@ describe('Tokenizer', function() {
|
|||||||
shouldBeToken(result[9], 'ID', 'baz');
|
shouldBeToken(result[9], 'ID', 'baz');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('supports escaped mustaches after escaped escape characters', function() {
|
it('supports escaped mustaches after escaped escape characters', function () {
|
||||||
var result = tokenize('{{foo}} \\\\{{bar}} \\{{baz}}');
|
var result = tokenize('{{foo}} \\\\{{bar}} \\{{baz}}');
|
||||||
shouldMatchTokens(result, [
|
shouldMatchTokens(result, [
|
||||||
'OPEN',
|
'OPEN',
|
||||||
@@ -156,7 +156,7 @@ describe('Tokenizer', function() {
|
|||||||
'CLOSE',
|
'CLOSE',
|
||||||
'CONTENT',
|
'CONTENT',
|
||||||
'CONTENT',
|
'CONTENT',
|
||||||
'CONTENT'
|
'CONTENT',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
shouldBeToken(result[3], 'CONTENT', ' \\');
|
shouldBeToken(result[3], 'CONTENT', ' \\');
|
||||||
@@ -166,7 +166,7 @@ describe('Tokenizer', function() {
|
|||||||
shouldBeToken(result[8], 'CONTENT', '{{baz}}');
|
shouldBeToken(result[8], 'CONTENT', '{{baz}}');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('supports escaped escape characters after escaped mustaches', function() {
|
it('supports escaped escape characters after escaped mustaches', function () {
|
||||||
var result = tokenize('{{foo}} \\{{bar}} \\\\{{baz}}');
|
var result = tokenize('{{foo}} \\{{bar}} \\\\{{baz}}');
|
||||||
shouldMatchTokens(result, [
|
shouldMatchTokens(result, [
|
||||||
'OPEN',
|
'OPEN',
|
||||||
@@ -177,7 +177,7 @@ describe('Tokenizer', function() {
|
|||||||
'CONTENT',
|
'CONTENT',
|
||||||
'OPEN',
|
'OPEN',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
shouldBeToken(result[4], 'CONTENT', '{{bar}} ');
|
shouldBeToken(result[4], 'CONTENT', '{{bar}} ');
|
||||||
@@ -186,7 +186,7 @@ describe('Tokenizer', function() {
|
|||||||
shouldBeToken(result[7], 'ID', 'baz');
|
shouldBeToken(result[7], 'ID', 'baz');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('supports escaped escape character on a triple stash', function() {
|
it('supports escaped escape character on a triple stash', function () {
|
||||||
var result = tokenize('{{foo}} \\\\{{{bar}}} {{baz}}');
|
var result = tokenize('{{foo}} \\\\{{{bar}}} {{baz}}');
|
||||||
shouldMatchTokens(result, [
|
shouldMatchTokens(result, [
|
||||||
'OPEN',
|
'OPEN',
|
||||||
@@ -199,19 +199,19 @@ describe('Tokenizer', function() {
|
|||||||
'CONTENT',
|
'CONTENT',
|
||||||
'OPEN',
|
'OPEN',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
shouldBeToken(result[3], 'CONTENT', ' \\');
|
shouldBeToken(result[3], 'CONTENT', ' \\');
|
||||||
shouldBeToken(result[5], 'ID', 'bar');
|
shouldBeToken(result[5], 'ID', 'bar');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes a simple path', function() {
|
it('tokenizes a simple path', function () {
|
||||||
var result = tokenize('{{foo/bar}}');
|
var result = tokenize('{{foo/bar}}');
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
|
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows dot notation', function() {
|
it('allows dot notation', function () {
|
||||||
var result = tokenize('{{foo.bar}}');
|
var result = tokenize('{{foo.bar}}');
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
|
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||||
|
|
||||||
@@ -222,16 +222,16 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'SEP',
|
'SEP',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows path literals with []', function() {
|
it('allows path literals with []', function () {
|
||||||
var result = tokenize('{{foo.[bar]}}');
|
var result = tokenize('{{foo.[bar]}}');
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
|
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows multiple path literals on a line with []', function() {
|
it('allows multiple path literals on a line with []', function () {
|
||||||
var result = tokenize('{{foo.[bar]}}{{foo.[baz]}}');
|
var result = tokenize('{{foo.[bar]}}{{foo.[baz]}}');
|
||||||
shouldMatchTokens(result, [
|
shouldMatchTokens(result, [
|
||||||
'OPEN',
|
'OPEN',
|
||||||
@@ -243,21 +243,21 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'SEP',
|
'SEP',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows escaped literals in []', function() {
|
it('allows escaped literals in []', function () {
|
||||||
var result = tokenize('{{foo.[bar\\]]}}');
|
var result = tokenize('{{foo.[bar\\]]}}');
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
|
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes {{.}} as OPEN ID CLOSE', function() {
|
it('tokenizes {{.}} as OPEN ID CLOSE', function () {
|
||||||
var result = tokenize('{{.}}');
|
var result = tokenize('{{.}}');
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE']);
|
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes a path as "OPEN (ID SEP)* ID CLOSE"', function() {
|
it('tokenizes a path as "OPEN (ID SEP)* ID CLOSE"', function () {
|
||||||
var result = tokenize('{{../foo/bar}}');
|
var result = tokenize('{{../foo/bar}}');
|
||||||
shouldMatchTokens(result, [
|
shouldMatchTokens(result, [
|
||||||
'OPEN',
|
'OPEN',
|
||||||
@@ -266,12 +266,12 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'SEP',
|
'SEP',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
shouldBeToken(result[1], 'ID', '..');
|
shouldBeToken(result[1], 'ID', '..');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes a path with .. as a parent path', function() {
|
it('tokenizes a path with .. as a parent path', function () {
|
||||||
var result = tokenize('{{../foo.bar}}');
|
var result = tokenize('{{../foo.bar}}');
|
||||||
shouldMatchTokens(result, [
|
shouldMatchTokens(result, [
|
||||||
'OPEN',
|
'OPEN',
|
||||||
@@ -280,58 +280,58 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'SEP',
|
'SEP',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
shouldBeToken(result[1], 'ID', '..');
|
shouldBeToken(result[1], 'ID', '..');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes a path with this/foo as OPEN ID SEP ID CLOSE', function() {
|
it('tokenizes a path with this/foo as OPEN ID SEP ID CLOSE', function () {
|
||||||
var result = tokenize('{{this/foo}}');
|
var result = tokenize('{{this/foo}}');
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
|
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||||
shouldBeToken(result[1], 'ID', 'this');
|
shouldBeToken(result[1], 'ID', 'this');
|
||||||
shouldBeToken(result[3], 'ID', 'foo');
|
shouldBeToken(result[3], 'ID', 'foo');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes a simple mustache with spaces as "OPEN ID CLOSE"', function() {
|
it('tokenizes a simple mustache with spaces as "OPEN ID CLOSE"', function () {
|
||||||
var result = tokenize('{{ foo }}');
|
var result = tokenize('{{ foo }}');
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE']);
|
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE']);
|
||||||
shouldBeToken(result[1], 'ID', 'foo');
|
shouldBeToken(result[1], 'ID', 'foo');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes a simple mustache with line breaks as "OPEN ID ID CLOSE"', function() {
|
it('tokenizes a simple mustache with line breaks as "OPEN ID ID CLOSE"', function () {
|
||||||
var result = tokenize('{{ foo \n bar }}');
|
var result = tokenize('{{ foo \n bar }}');
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'CLOSE']);
|
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'CLOSE']);
|
||||||
shouldBeToken(result[1], 'ID', 'foo');
|
shouldBeToken(result[1], 'ID', 'foo');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes raw content as "CONTENT"', function() {
|
it('tokenizes raw content as "CONTENT"', function () {
|
||||||
var result = tokenize('foo {{ bar }} baz');
|
var result = tokenize('foo {{ bar }} baz');
|
||||||
shouldMatchTokens(result, ['CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT']);
|
shouldMatchTokens(result, ['CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT']);
|
||||||
shouldBeToken(result[0], 'CONTENT', 'foo ');
|
shouldBeToken(result[0], 'CONTENT', 'foo ');
|
||||||
shouldBeToken(result[4], 'CONTENT', ' baz');
|
shouldBeToken(result[4], 'CONTENT', ' baz');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes a partial as "OPEN_PARTIAL ID CLOSE"', function() {
|
it('tokenizes a partial as "OPEN_PARTIAL ID CLOSE"', function () {
|
||||||
var result = tokenize('{{> foo}}');
|
var result = tokenize('{{> foo}}');
|
||||||
shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'CLOSE']);
|
shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'CLOSE']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes a partial with context as "OPEN_PARTIAL ID ID CLOSE"', function() {
|
it('tokenizes a partial with context as "OPEN_PARTIAL ID ID CLOSE"', function () {
|
||||||
var result = tokenize('{{> foo bar }}');
|
var result = tokenize('{{> foo bar }}');
|
||||||
shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'ID', 'CLOSE']);
|
shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'ID', 'CLOSE']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes a partial without spaces as "OPEN_PARTIAL ID CLOSE"', function() {
|
it('tokenizes a partial without spaces as "OPEN_PARTIAL ID CLOSE"', function () {
|
||||||
var result = tokenize('{{>foo}}');
|
var result = tokenize('{{>foo}}');
|
||||||
shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'CLOSE']);
|
shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'CLOSE']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes a partial space at the }); as "OPEN_PARTIAL ID CLOSE"', function() {
|
it('tokenizes a partial space at the }); as "OPEN_PARTIAL ID CLOSE"', function () {
|
||||||
var result = tokenize('{{>foo }}');
|
var result = tokenize('{{>foo }}');
|
||||||
shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'CLOSE']);
|
shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'CLOSE']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes a partial space at the }); as "OPEN_PARTIAL ID CLOSE"', function() {
|
it('tokenizes a partial space at the }); as "OPEN_PARTIAL ID CLOSE"', function () {
|
||||||
var result = tokenize('{{>foo/bar.baz }}');
|
var result = tokenize('{{>foo/bar.baz }}');
|
||||||
shouldMatchTokens(result, [
|
shouldMatchTokens(result, [
|
||||||
'OPEN_PARTIAL',
|
'OPEN_PARTIAL',
|
||||||
@@ -340,15 +340,15 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'SEP',
|
'SEP',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes partial block declarations', function() {
|
it('tokenizes partial block declarations', function () {
|
||||||
var result = tokenize('{{#> foo}}');
|
var result = tokenize('{{#> foo}}');
|
||||||
shouldMatchTokens(result, ['OPEN_PARTIAL_BLOCK', 'ID', 'CLOSE']);
|
shouldMatchTokens(result, ['OPEN_PARTIAL_BLOCK', 'ID', 'CLOSE']);
|
||||||
});
|
});
|
||||||
it('tokenizes a comment as "COMMENT"', function() {
|
it('tokenizes a comment as "COMMENT"', function () {
|
||||||
var result = tokenize('foo {{! this is a comment }} bar {{ baz }}');
|
var result = tokenize('foo {{! this is a comment }} bar {{ baz }}');
|
||||||
shouldMatchTokens(result, [
|
shouldMatchTokens(result, [
|
||||||
'CONTENT',
|
'CONTENT',
|
||||||
@@ -356,12 +356,12 @@ describe('Tokenizer', function() {
|
|||||||
'CONTENT',
|
'CONTENT',
|
||||||
'OPEN',
|
'OPEN',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
shouldBeToken(result[1], 'COMMENT', '{{! this is a comment }}');
|
shouldBeToken(result[1], 'COMMENT', '{{! this is a comment }}');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes a block comment as "COMMENT"', function() {
|
it('tokenizes a block comment as "COMMENT"', function () {
|
||||||
var result = tokenize('foo {{!-- this is a {{comment}} --}} bar {{ baz }}');
|
var result = tokenize('foo {{!-- this is a {{comment}} --}} bar {{ baz }}');
|
||||||
shouldMatchTokens(result, [
|
shouldMatchTokens(result, [
|
||||||
'CONTENT',
|
'CONTENT',
|
||||||
@@ -369,12 +369,12 @@ describe('Tokenizer', function() {
|
|||||||
'CONTENT',
|
'CONTENT',
|
||||||
'OPEN',
|
'OPEN',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
shouldBeToken(result[1], 'COMMENT', '{{!-- this is a {{comment}} --}}');
|
shouldBeToken(result[1], 'COMMENT', '{{!-- this is a {{comment}} --}}');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes a block comment with whitespace as "COMMENT"', function() {
|
it('tokenizes a block comment with whitespace as "COMMENT"', function () {
|
||||||
var result = tokenize(
|
var result = tokenize(
|
||||||
'foo {{!-- this is a\n{{comment}}\n--}} bar {{ baz }}'
|
'foo {{!-- this is a\n{{comment}}\n--}} bar {{ baz }}'
|
||||||
);
|
);
|
||||||
@@ -384,12 +384,12 @@ describe('Tokenizer', function() {
|
|||||||
'CONTENT',
|
'CONTENT',
|
||||||
'OPEN',
|
'OPEN',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
shouldBeToken(result[1], 'COMMENT', '{{!-- this is a\n{{comment}}\n--}}');
|
shouldBeToken(result[1], 'COMMENT', '{{!-- this is a\n{{comment}}\n--}}');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes open and closing blocks as OPEN_BLOCK, ID, CLOSE ..., OPEN_ENDBLOCK ID CLOSE', function() {
|
it('tokenizes open and closing blocks as OPEN_BLOCK, ID, CLOSE ..., OPEN_ENDBLOCK ID CLOSE', function () {
|
||||||
var result = tokenize('{{#foo}}content{{/foo}}');
|
var result = tokenize('{{#foo}}content{{/foo}}');
|
||||||
shouldMatchTokens(result, [
|
shouldMatchTokens(result, [
|
||||||
'OPEN_BLOCK',
|
'OPEN_BLOCK',
|
||||||
@@ -398,11 +398,11 @@ describe('Tokenizer', function() {
|
|||||||
'CONTENT',
|
'CONTENT',
|
||||||
'OPEN_ENDBLOCK',
|
'OPEN_ENDBLOCK',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes directives', function() {
|
it('tokenizes directives', function () {
|
||||||
shouldMatchTokens(tokenize('{{#*foo}}content{{/foo}}'), [
|
shouldMatchTokens(tokenize('{{#*foo}}content{{/foo}}'), [
|
||||||
'OPEN_BLOCK',
|
'OPEN_BLOCK',
|
||||||
'ID',
|
'ID',
|
||||||
@@ -410,30 +410,30 @@ describe('Tokenizer', function() {
|
|||||||
'CONTENT',
|
'CONTENT',
|
||||||
'OPEN_ENDBLOCK',
|
'OPEN_ENDBLOCK',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
shouldMatchTokens(tokenize('{{*foo}}'), ['OPEN', 'ID', 'CLOSE']);
|
shouldMatchTokens(tokenize('{{*foo}}'), ['OPEN', 'ID', 'CLOSE']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes inverse sections as "INVERSE"', function() {
|
it('tokenizes inverse sections as "INVERSE"', function () {
|
||||||
shouldMatchTokens(tokenize('{{^}}'), ['INVERSE']);
|
shouldMatchTokens(tokenize('{{^}}'), ['INVERSE']);
|
||||||
shouldMatchTokens(tokenize('{{else}}'), ['INVERSE']);
|
shouldMatchTokens(tokenize('{{else}}'), ['INVERSE']);
|
||||||
shouldMatchTokens(tokenize('{{ else }}'), ['INVERSE']);
|
shouldMatchTokens(tokenize('{{ else }}'), ['INVERSE']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes inverse sections with ID as "OPEN_INVERSE ID CLOSE"', function() {
|
it('tokenizes inverse sections with ID as "OPEN_INVERSE ID CLOSE"', function () {
|
||||||
var result = tokenize('{{^foo}}');
|
var result = tokenize('{{^foo}}');
|
||||||
shouldMatchTokens(result, ['OPEN_INVERSE', 'ID', 'CLOSE']);
|
shouldMatchTokens(result, ['OPEN_INVERSE', 'ID', 'CLOSE']);
|
||||||
shouldBeToken(result[1], 'ID', 'foo');
|
shouldBeToken(result[1], 'ID', 'foo');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes inverse sections with ID and spaces as "OPEN_INVERSE ID CLOSE"', function() {
|
it('tokenizes inverse sections with ID and spaces as "OPEN_INVERSE ID CLOSE"', function () {
|
||||||
var result = tokenize('{{^ foo }}');
|
var result = tokenize('{{^ foo }}');
|
||||||
shouldMatchTokens(result, ['OPEN_INVERSE', 'ID', 'CLOSE']);
|
shouldMatchTokens(result, ['OPEN_INVERSE', 'ID', 'CLOSE']);
|
||||||
shouldBeToken(result[1], 'ID', 'foo');
|
shouldBeToken(result[1], 'ID', 'foo');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes mustaches with params as "OPEN ID ID ID CLOSE"', function() {
|
it('tokenizes mustaches with params as "OPEN ID ID ID CLOSE"', function () {
|
||||||
var result = tokenize('{{ foo bar baz }}');
|
var result = tokenize('{{ foo bar baz }}');
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'CLOSE']);
|
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'CLOSE']);
|
||||||
shouldBeToken(result[1], 'ID', 'foo');
|
shouldBeToken(result[1], 'ID', 'foo');
|
||||||
@@ -441,37 +441,37 @@ describe('Tokenizer', function() {
|
|||||||
shouldBeToken(result[3], 'ID', 'baz');
|
shouldBeToken(result[3], 'ID', 'baz');
|
||||||
});
|
});
|
||||||
|
|
||||||
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');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes String params with spaces inside as "STRING"', function() {
|
it('tokenizes String params with spaces inside as "STRING"', function () {
|
||||||
var result = tokenize('{{ foo bar "baz bat" }}');
|
var result = tokenize('{{ foo bar "baz bat" }}');
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'STRING', 'CLOSE']);
|
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'STRING', 'CLOSE']);
|
||||||
shouldBeToken(result[3], 'STRING', 'baz bat');
|
shouldBeToken(result[3], 'STRING', 'baz bat');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes String params with escapes quotes as STRING', function() {
|
it('tokenizes String params with escapes quotes as STRING', function () {
|
||||||
var result = tokenize('{{ foo "bar\\"baz" }}');
|
var result = tokenize('{{ foo "bar\\"baz" }}');
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'STRING', 'CLOSE']);
|
shouldMatchTokens(result, ['OPEN', 'ID', 'STRING', 'CLOSE']);
|
||||||
shouldBeToken(result[2], 'STRING', 'bar"baz');
|
shouldBeToken(result[2], 'STRING', 'bar"baz');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes String params using single quotes with escapes quotes as STRING', function() {
|
it('tokenizes String params using single quotes with escapes quotes as STRING', function () {
|
||||||
var result = tokenize("{{ foo 'bar\\'baz' }}");
|
var result = tokenize("{{ foo 'bar\\'baz' }}");
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'STRING', 'CLOSE']);
|
shouldMatchTokens(result, ['OPEN', 'ID', 'STRING', 'CLOSE']);
|
||||||
shouldBeToken(result[2], 'STRING', "bar'baz");
|
shouldBeToken(result[2], 'STRING', "bar'baz");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes numbers', function() {
|
it('tokenizes numbers', function () {
|
||||||
var result = tokenize('{{ foo 1 }}');
|
var result = tokenize('{{ foo 1 }}');
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'NUMBER', 'CLOSE']);
|
shouldMatchTokens(result, ['OPEN', 'ID', 'NUMBER', 'CLOSE']);
|
||||||
shouldBeToken(result[2], 'NUMBER', '1');
|
shouldBeToken(result[2], 'NUMBER', '1');
|
||||||
@@ -489,7 +489,7 @@ describe('Tokenizer', function() {
|
|||||||
shouldBeToken(result[2], 'NUMBER', '-1.1');
|
shouldBeToken(result[2], 'NUMBER', '-1.1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes booleans', function() {
|
it('tokenizes booleans', function () {
|
||||||
var result = tokenize('{{ foo true }}');
|
var result = tokenize('{{ foo true }}');
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'BOOLEAN', 'CLOSE']);
|
shouldMatchTokens(result, ['OPEN', 'ID', 'BOOLEAN', 'CLOSE']);
|
||||||
shouldBeToken(result[2], 'BOOLEAN', 'true');
|
shouldBeToken(result[2], 'BOOLEAN', 'true');
|
||||||
@@ -499,14 +499,14 @@ describe('Tokenizer', function() {
|
|||||||
shouldBeToken(result[2], 'BOOLEAN', 'false');
|
shouldBeToken(result[2], 'BOOLEAN', 'false');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes undefined and null', function() {
|
it('tokenizes undefined and null', function () {
|
||||||
var result = tokenize('{{ foo undefined null }}');
|
var result = tokenize('{{ foo undefined null }}');
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'UNDEFINED', 'NULL', 'CLOSE']);
|
shouldMatchTokens(result, ['OPEN', 'ID', 'UNDEFINED', 'NULL', 'CLOSE']);
|
||||||
shouldBeToken(result[2], 'UNDEFINED', 'undefined');
|
shouldBeToken(result[2], 'UNDEFINED', 'undefined');
|
||||||
shouldBeToken(result[3], 'NULL', 'null');
|
shouldBeToken(result[3], 'NULL', 'null');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes hash arguments', function() {
|
it('tokenizes hash arguments', function () {
|
||||||
var result = tokenize('{{ foo bar=baz }}');
|
var result = tokenize('{{ foo bar=baz }}');
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'EQUALS', 'ID', 'CLOSE']);
|
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'EQUALS', 'ID', 'CLOSE']);
|
||||||
|
|
||||||
@@ -518,7 +518,7 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'EQUALS',
|
'EQUALS',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
result = tokenize('{{ foo bar baz=1 }}');
|
result = tokenize('{{ foo bar baz=1 }}');
|
||||||
@@ -529,7 +529,7 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'EQUALS',
|
'EQUALS',
|
||||||
'NUMBER',
|
'NUMBER',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
result = tokenize('{{ foo bar baz=true }}');
|
result = tokenize('{{ foo bar baz=true }}');
|
||||||
@@ -540,7 +540,7 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'EQUALS',
|
'EQUALS',
|
||||||
'BOOLEAN',
|
'BOOLEAN',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
result = tokenize('{{ foo bar baz=false }}');
|
result = tokenize('{{ foo bar baz=false }}');
|
||||||
@@ -551,7 +551,7 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'EQUALS',
|
'EQUALS',
|
||||||
'BOOLEAN',
|
'BOOLEAN',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
result = tokenize('{{ foo bar\n baz=bat }}');
|
result = tokenize('{{ foo bar\n baz=bat }}');
|
||||||
@@ -562,7 +562,7 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'EQUALS',
|
'EQUALS',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
result = tokenize('{{ foo bar baz="bat" }}');
|
result = tokenize('{{ foo bar baz="bat" }}');
|
||||||
@@ -573,7 +573,7 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'EQUALS',
|
'EQUALS',
|
||||||
'STRING',
|
'STRING',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
result = tokenize('{{ foo bar baz="bat" bam=wot }}');
|
result = tokenize('{{ foo bar baz="bat" bam=wot }}');
|
||||||
@@ -587,7 +587,7 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'EQUALS',
|
'EQUALS',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
result = tokenize('{{foo omg bar=baz bat="bam"}}');
|
result = tokenize('{{foo omg bar=baz bat="bam"}}');
|
||||||
@@ -601,12 +601,12 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'EQUALS',
|
'EQUALS',
|
||||||
'STRING',
|
'STRING',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
shouldBeToken(result[2], 'ID', 'omg');
|
shouldBeToken(result[2], 'ID', 'omg');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes special @ identifiers', function() {
|
it('tokenizes special @ identifiers', function () {
|
||||||
var result = tokenize('{{ @foo }}');
|
var result = tokenize('{{ @foo }}');
|
||||||
shouldMatchTokens(result, ['OPEN', 'DATA', 'ID', 'CLOSE']);
|
shouldMatchTokens(result, ['OPEN', 'DATA', 'ID', 'CLOSE']);
|
||||||
shouldBeToken(result[2], 'ID', 'foo');
|
shouldBeToken(result[2], 'ID', 'foo');
|
||||||
@@ -623,20 +623,20 @@ describe('Tokenizer', function() {
|
|||||||
'EQUALS',
|
'EQUALS',
|
||||||
'DATA',
|
'DATA',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
shouldBeToken(result[5], 'ID', 'baz');
|
shouldBeToken(result[5], 'ID', 'baz');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not time out in a mustache with a single } followed by EOF', function() {
|
it('does not time out in a mustache with a single } followed by EOF', function () {
|
||||||
shouldMatchTokens(tokenize('{{foo}'), ['OPEN', 'ID']);
|
shouldMatchTokens(tokenize('{{foo}'), ['OPEN', 'ID']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not time out in a mustache when invalid ID characters are used', function() {
|
it('does not time out in a mustache when invalid ID characters are used', function () {
|
||||||
shouldMatchTokens(tokenize('{{foo & }}'), ['OPEN', 'ID']);
|
shouldMatchTokens(tokenize('{{foo & }}'), ['OPEN', 'ID']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes subexpressions', function() {
|
it('tokenizes subexpressions', function () {
|
||||||
var result = tokenize('{{foo (bar)}}');
|
var result = tokenize('{{foo (bar)}}');
|
||||||
shouldMatchTokens(result, [
|
shouldMatchTokens(result, [
|
||||||
'OPEN',
|
'OPEN',
|
||||||
@@ -644,7 +644,7 @@ describe('Tokenizer', function() {
|
|||||||
'OPEN_SEXPR',
|
'OPEN_SEXPR',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE_SEXPR',
|
'CLOSE_SEXPR',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
shouldBeToken(result[1], 'ID', 'foo');
|
shouldBeToken(result[1], 'ID', 'foo');
|
||||||
shouldBeToken(result[3], 'ID', 'bar');
|
shouldBeToken(result[3], 'ID', 'bar');
|
||||||
@@ -657,14 +657,14 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE_SEXPR',
|
'CLOSE_SEXPR',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
shouldBeToken(result[1], 'ID', 'foo');
|
shouldBeToken(result[1], 'ID', 'foo');
|
||||||
shouldBeToken(result[3], 'ID', 'a-x');
|
shouldBeToken(result[3], 'ID', 'a-x');
|
||||||
shouldBeToken(result[4], 'ID', 'b-y');
|
shouldBeToken(result[4], 'ID', 'b-y');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes nested subexpressions', function() {
|
it('tokenizes nested subexpressions', function () {
|
||||||
var result = tokenize('{{foo (bar (lol rofl)) (baz)}}');
|
var result = tokenize('{{foo (bar (lol rofl)) (baz)}}');
|
||||||
shouldMatchTokens(result, [
|
shouldMatchTokens(result, [
|
||||||
'OPEN',
|
'OPEN',
|
||||||
@@ -679,7 +679,7 @@ describe('Tokenizer', function() {
|
|||||||
'OPEN_SEXPR',
|
'OPEN_SEXPR',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE_SEXPR',
|
'CLOSE_SEXPR',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
shouldBeToken(result[3], 'ID', 'bar');
|
shouldBeToken(result[3], 'ID', 'bar');
|
||||||
shouldBeToken(result[5], 'ID', 'lol');
|
shouldBeToken(result[5], 'ID', 'lol');
|
||||||
@@ -687,7 +687,7 @@ describe('Tokenizer', function() {
|
|||||||
shouldBeToken(result[10], 'ID', 'baz');
|
shouldBeToken(result[10], 'ID', 'baz');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes nested subexpressions: literals', function() {
|
it('tokenizes nested subexpressions: literals', function () {
|
||||||
var result = tokenize(
|
var result = tokenize(
|
||||||
'{{foo (bar (lol true) false) (baz 1) (blah \'b\') (blorg "c")}}'
|
'{{foo (bar (lol true) false) (baz 1) (blah \'b\') (blorg "c")}}'
|
||||||
);
|
);
|
||||||
@@ -714,11 +714,11 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'STRING',
|
'STRING',
|
||||||
'CLOSE_SEXPR',
|
'CLOSE_SEXPR',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes block params', function() {
|
it('tokenizes block params', function () {
|
||||||
var result = tokenize('{{#foo as |bar|}}');
|
var result = tokenize('{{#foo as |bar|}}');
|
||||||
shouldMatchTokens(result, [
|
shouldMatchTokens(result, [
|
||||||
'OPEN_BLOCK',
|
'OPEN_BLOCK',
|
||||||
@@ -726,7 +726,7 @@ describe('Tokenizer', function() {
|
|||||||
'OPEN_BLOCK_PARAMS',
|
'OPEN_BLOCK_PARAMS',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE_BLOCK_PARAMS',
|
'CLOSE_BLOCK_PARAMS',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
result = tokenize('{{#foo as |bar baz|}}');
|
result = tokenize('{{#foo as |bar baz|}}');
|
||||||
@@ -737,7 +737,7 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE_BLOCK_PARAMS',
|
'CLOSE_BLOCK_PARAMS',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
result = tokenize('{{#foo as | bar baz |}}');
|
result = tokenize('{{#foo as | bar baz |}}');
|
||||||
@@ -748,7 +748,7 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE_BLOCK_PARAMS',
|
'CLOSE_BLOCK_PARAMS',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
result = tokenize('{{#foo as as | bar baz |}}');
|
result = tokenize('{{#foo as as | bar baz |}}');
|
||||||
@@ -760,7 +760,7 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE_BLOCK_PARAMS',
|
'CLOSE_BLOCK_PARAMS',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
result = tokenize('{{else foo as |bar baz|}}');
|
result = tokenize('{{else foo as |bar baz|}}');
|
||||||
@@ -771,11 +771,11 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE_BLOCK_PARAMS',
|
'CLOSE_BLOCK_PARAMS',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tokenizes raw blocks', function() {
|
it('tokenizes raw blocks', function () {
|
||||||
var result = tokenize(
|
var result = tokenize(
|
||||||
'{{{{a}}}} abc {{{{/a}}}} aaa {{{{a}}}} abc {{{{/a}}}}'
|
'{{{{a}}}} abc {{{{/a}}}} aaa {{{{a}}}} abc {{{{/a}}}}'
|
||||||
);
|
);
|
||||||
@@ -790,7 +790,7 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'CLOSE_RAW_BLOCK',
|
'CLOSE_RAW_BLOCK',
|
||||||
'CONTENT',
|
'CONTENT',
|
||||||
'END_RAW_BLOCK'
|
'END_RAW_BLOCK',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,361 +0,0 @@
|
|||||||
describe('track ids', function() {
|
|
||||||
var context;
|
|
||||||
beforeEach(function() {
|
|
||||||
context = { is: { a: 'foo' }, slave: { driver: 'bar' } };
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not include anything without the flag', function() {
|
|
||||||
expectTemplate('{{wycats is.a slave.driver}}')
|
|
||||||
.withHelpers({
|
|
||||||
wycats: function(passiveVoice, noun, options) {
|
|
||||||
equal(options.ids, undefined);
|
|
||||||
equal(options.hashIds, undefined);
|
|
||||||
|
|
||||||
return 'success';
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.toCompileTo('success');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should include argument ids', function() {
|
|
||||||
expectTemplate('{{wycats is.a slave.driver}}')
|
|
||||||
.withCompileOptions({ trackIds: true })
|
|
||||||
.withHelpers({
|
|
||||||
wycats: function(passiveVoice, noun, options) {
|
|
||||||
equal(options.ids[0], 'is.a');
|
|
||||||
equal(options.ids[1], 'slave.driver');
|
|
||||||
|
|
||||||
return (
|
|
||||||
'HELP ME MY BOSS ' +
|
|
||||||
options.ids[0] +
|
|
||||||
':' +
|
|
||||||
passiveVoice +
|
|
||||||
' ' +
|
|
||||||
options.ids[1] +
|
|
||||||
':' +
|
|
||||||
noun
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.withInput(context)
|
|
||||||
.toCompileTo('HELP ME MY BOSS is.a:foo slave.driver:bar');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should include hash ids', function() {
|
|
||||||
expectTemplate('{{wycats bat=is.a baz=slave.driver}}')
|
|
||||||
.withCompileOptions({ trackIds: true })
|
|
||||||
.withHelpers({
|
|
||||||
wycats: function(options) {
|
|
||||||
equal(options.hashIds.bat, 'is.a');
|
|
||||||
equal(options.hashIds.baz, 'slave.driver');
|
|
||||||
|
|
||||||
return (
|
|
||||||
'HELP ME MY BOSS ' +
|
|
||||||
options.hashIds.bat +
|
|
||||||
':' +
|
|
||||||
options.hash.bat +
|
|
||||||
' ' +
|
|
||||||
options.hashIds.baz +
|
|
||||||
':' +
|
|
||||||
options.hash.baz
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.withInput(context)
|
|
||||||
.toCompileTo('HELP ME MY BOSS is.a:foo slave.driver:bar');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should note ../ and ./ references', function() {
|
|
||||||
expectTemplate('{{wycats ./is.a ../slave.driver this.is.a this}}')
|
|
||||||
.withCompileOptions({ trackIds: true })
|
|
||||||
.withHelpers({
|
|
||||||
wycats: function(passiveVoice, noun, thiz, thiz2, options) {
|
|
||||||
equal(options.ids[0], 'is.a');
|
|
||||||
equal(options.ids[1], '../slave.driver');
|
|
||||||
equal(options.ids[2], 'is.a');
|
|
||||||
equal(options.ids[3], '');
|
|
||||||
|
|
||||||
return (
|
|
||||||
'HELP ME MY BOSS ' +
|
|
||||||
options.ids[0] +
|
|
||||||
':' +
|
|
||||||
passiveVoice +
|
|
||||||
' ' +
|
|
||||||
options.ids[1] +
|
|
||||||
':' +
|
|
||||||
noun
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.withInput(context)
|
|
||||||
.toCompileTo('HELP ME MY BOSS is.a:foo ../slave.driver:undefined');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should note @data references', function() {
|
|
||||||
expectTemplate('{{wycats @is.a @slave.driver}}')
|
|
||||||
.withCompileOptions({ trackIds: true })
|
|
||||||
.withHelpers({
|
|
||||||
wycats: function(passiveVoice, noun, options) {
|
|
||||||
equal(options.ids[0], '@is.a');
|
|
||||||
equal(options.ids[1], '@slave.driver');
|
|
||||||
|
|
||||||
return (
|
|
||||||
'HELP ME MY BOSS ' +
|
|
||||||
options.ids[0] +
|
|
||||||
':' +
|
|
||||||
passiveVoice +
|
|
||||||
' ' +
|
|
||||||
options.ids[1] +
|
|
||||||
':' +
|
|
||||||
noun
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.withRuntimeOptions({ data: context })
|
|
||||||
.toCompileTo('HELP ME MY BOSS @is.a:foo @slave.driver:bar');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return null for constants', function() {
|
|
||||||
expectTemplate('{{wycats 1 "foo" key=false}}')
|
|
||||||
.withCompileOptions({ trackIds: true })
|
|
||||||
.withHelpers({
|
|
||||||
wycats: function(passiveVoice, noun, options) {
|
|
||||||
equal(options.ids[0], null);
|
|
||||||
equal(options.ids[1], null);
|
|
||||||
equal(options.hashIds.key, null);
|
|
||||||
|
|
||||||
return (
|
|
||||||
'HELP ME MY BOSS ' +
|
|
||||||
passiveVoice +
|
|
||||||
' ' +
|
|
||||||
noun +
|
|
||||||
' ' +
|
|
||||||
options.hash.key
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.withInput(context)
|
|
||||||
.toCompileTo('HELP ME MY BOSS 1 foo false');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return true for subexpressions', function() {
|
|
||||||
expectTemplate('{{wycats (sub)}}')
|
|
||||||
.withCompileOptions({ trackIds: true })
|
|
||||||
.withHelpers({
|
|
||||||
sub: function() {
|
|
||||||
return 1;
|
|
||||||
},
|
|
||||||
wycats: function(passiveVoice, options) {
|
|
||||||
equal(options.ids[0], true);
|
|
||||||
|
|
||||||
return 'HELP ME MY BOSS ' + passiveVoice;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.withInput(context)
|
|
||||||
.toCompileTo('HELP ME MY BOSS 1');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should use block param paths', function() {
|
|
||||||
expectTemplate('{{#doIt as |is|}}{{wycats is.a slave.driver is}}{{/doIt}}')
|
|
||||||
.withCompileOptions({ trackIds: true })
|
|
||||||
.withHelpers({
|
|
||||||
doIt: function(options) {
|
|
||||||
var blockParams = [this.is];
|
|
||||||
blockParams.path = ['zomg'];
|
|
||||||
return options.fn(this, { blockParams: blockParams });
|
|
||||||
},
|
|
||||||
wycats: function(passiveVoice, noun, blah, options) {
|
|
||||||
equal(options.ids[0], 'zomg.a');
|
|
||||||
equal(options.ids[1], 'slave.driver');
|
|
||||||
equal(options.ids[2], 'zomg');
|
|
||||||
|
|
||||||
return (
|
|
||||||
'HELP ME MY BOSS ' +
|
|
||||||
options.ids[0] +
|
|
||||||
':' +
|
|
||||||
passiveVoice +
|
|
||||||
' ' +
|
|
||||||
options.ids[1] +
|
|
||||||
':' +
|
|
||||||
noun
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.withInput(context)
|
|
||||||
.toCompileTo('HELP ME MY BOSS zomg.a:foo slave.driver:bar');
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('builtin helpers', function() {
|
|
||||||
var helpers = {
|
|
||||||
blockParams: function(name, options) {
|
|
||||||
return name + ':' + options.ids[0] + '\n';
|
|
||||||
},
|
|
||||||
wycats: function(name, options) {
|
|
||||||
return name + ':' + options.data.contextPath + '\n';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('#each', function() {
|
|
||||||
it('should track contextPath for arrays', function() {
|
|
||||||
expectTemplate('{{#each array}}{{wycats name}}{{/each}}')
|
|
||||||
.withCompileOptions({ trackIds: true })
|
|
||||||
.withHelpers(helpers)
|
|
||||||
.withInput({ array: [{ name: 'foo' }, { name: 'bar' }] })
|
|
||||||
.toCompileTo('foo:array.0\nbar:array.1\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should track contextPath for keys', function() {
|
|
||||||
expectTemplate('{{#each object}}{{wycats name}}{{/each}}')
|
|
||||||
.withCompileOptions({ trackIds: true })
|
|
||||||
.withHelpers(helpers)
|
|
||||||
.withInput({ object: { foo: { name: 'foo' }, bar: { name: 'bar' } } })
|
|
||||||
.toCompileTo('foo:object.foo\nbar:object.bar\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle nesting', function() {
|
|
||||||
expectTemplate(
|
|
||||||
'{{#each .}}{{#each .}}{{wycats name}}{{/each}}{{/each}}'
|
|
||||||
)
|
|
||||||
.withCompileOptions({ trackIds: true })
|
|
||||||
.withHelpers(helpers)
|
|
||||||
.withInput({ array: [{ name: 'foo' }, { name: 'bar' }] })
|
|
||||||
.toCompileTo('foo:.array..0\nbar:.array..1\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle block params', function() {
|
|
||||||
expectTemplate(
|
|
||||||
'{{#each array as |value|}}{{blockParams value.name}}{{/each}}'
|
|
||||||
)
|
|
||||||
.withCompileOptions({ trackIds: true })
|
|
||||||
.withHelpers(helpers)
|
|
||||||
.withInput({ array: [{ name: 'foo' }, { name: 'bar' }] })
|
|
||||||
.toCompileTo('foo:array.0.name\nbar:array.1.name\n');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#with', function() {
|
|
||||||
it('should track contextPath', function() {
|
|
||||||
expectTemplate('{{#with field}}{{wycats name}}{{/with}}')
|
|
||||||
.withCompileOptions({ trackIds: true })
|
|
||||||
.withHelpers(helpers)
|
|
||||||
.withInput({ field: { name: 'foo' } })
|
|
||||||
.toCompileTo('foo:field\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle nesting', function() {
|
|
||||||
expectTemplate(
|
|
||||||
'{{#with bat}}{{#with field}}{{wycats name}}{{/with}}{{/with}}'
|
|
||||||
)
|
|
||||||
.withCompileOptions({ trackIds: true })
|
|
||||||
.withHelpers(helpers)
|
|
||||||
.withInput({ bat: { field: { name: 'foo' } } })
|
|
||||||
.toCompileTo('foo:bat.field\n');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#blockHelperMissing', function() {
|
|
||||||
it('should track contextPath for arrays', function() {
|
|
||||||
expectTemplate('{{#field}}{{wycats name}}{{/field}}')
|
|
||||||
.withCompileOptions({ trackIds: true })
|
|
||||||
.withHelpers(helpers)
|
|
||||||
.withInput({ field: [{ name: 'foo' }] })
|
|
||||||
.toCompileTo('foo:field.0\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should track contextPath for keys', function() {
|
|
||||||
expectTemplate('{{#field}}{{wycats name}}{{/field}}')
|
|
||||||
.withCompileOptions({ trackIds: true })
|
|
||||||
.withHelpers(helpers)
|
|
||||||
.withInput({ field: { name: 'foo' } })
|
|
||||||
.toCompileTo('foo:field\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle nesting', function() {
|
|
||||||
expectTemplate('{{#bat}}{{#field}}{{wycats name}}{{/field}}{{/bat}}')
|
|
||||||
.withCompileOptions({ trackIds: true })
|
|
||||||
.withHelpers(helpers)
|
|
||||||
.withInput({ bat: { field: { name: 'foo' } } })
|
|
||||||
.toCompileTo('foo:bat.field\n');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('partials', function() {
|
|
||||||
var helpers = {
|
|
||||||
blockParams: function(name, options) {
|
|
||||||
return name + ':' + options.ids[0] + '\n';
|
|
||||||
},
|
|
||||||
wycats: function(name, options) {
|
|
||||||
return name + ':' + options.data.contextPath + '\n';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
it('should pass track id for basic partial', function() {
|
|
||||||
var template = CompilerContext.compile(
|
|
||||||
'Dudes: {{#dudes}}{{> dude}}{{/dudes}}',
|
|
||||||
{ trackIds: true }
|
|
||||||
),
|
|
||||||
hash = {
|
|
||||||
dudes: [
|
|
||||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
|
||||||
{ name: 'Alan', url: 'http://alan' }
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
var partials = {
|
|
||||||
dude: CompilerContext.compile('{{wycats name}}', { trackIds: true })
|
|
||||||
};
|
|
||||||
|
|
||||||
equals(
|
|
||||||
template(hash, { helpers: helpers, partials: partials }),
|
|
||||||
'Dudes: Yehuda:dudes.0\nAlan:dudes.1\n'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should pass track id for context partial', function() {
|
|
||||||
var template = CompilerContext.compile('Dudes: {{> dude dudes}}', {
|
|
||||||
trackIds: true
|
|
||||||
}),
|
|
||||||
hash = {
|
|
||||||
dudes: [
|
|
||||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
|
||||||
{ name: 'Alan', url: 'http://alan' }
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
var partials = {
|
|
||||||
dude: CompilerContext.compile(
|
|
||||||
'{{#each this}}{{wycats name}}{{/each}}',
|
|
||||||
{ trackIds: true }
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
equals(
|
|
||||||
template(hash, { helpers: helpers, partials: partials }),
|
|
||||||
'Dudes: Yehuda:dudes..0\nAlan:dudes..1\n'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should invalidate context for partials with parameters', function() {
|
|
||||||
var template = CompilerContext.compile(
|
|
||||||
'Dudes: {{#dudes}}{{> dude . bar="foo"}}{{/dudes}}',
|
|
||||||
{ trackIds: true }
|
|
||||||
),
|
|
||||||
hash = {
|
|
||||||
dudes: [
|
|
||||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
|
||||||
{ name: 'Alan', url: 'http://alan' }
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
var partials = {
|
|
||||||
dude: CompilerContext.compile('{{wycats name}}', { trackIds: true })
|
|
||||||
};
|
|
||||||
|
|
||||||
equals(
|
|
||||||
template(hash, { helpers: helpers, partials: partials }),
|
|
||||||
'Dudes: Yehuda:true\nAlan:true\n'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -26,7 +26,6 @@
|
|||||||
window.expect = chai.expect;
|
window.expect = chai.expect;
|
||||||
mocha.setup('bdd');
|
mocha.setup('bdd');
|
||||||
</script>
|
</script>
|
||||||
<script src="/spec/vendor/json2.js"></script>
|
|
||||||
<script src="/spec/vendor/require.js"></script>
|
<script src="/spec/vendor/require.js"></script>
|
||||||
|
|
||||||
<script src="/spec/env/common.js"></script>
|
<script src="/spec/env/common.js"></script>
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
window.expect = chai.expect;
|
window.expect = chai.expect;
|
||||||
mocha.setup('bdd');
|
mocha.setup('bdd');
|
||||||
</script>
|
</script>
|
||||||
<script src="/spec/vendor/json2.js"></script>
|
|
||||||
<script src="/spec/vendor/require.js"></script>
|
<script src="/spec/vendor/require.js"></script>
|
||||||
|
|
||||||
<script src="/spec/env/common.js"></script>
|
<script src="/spec/env/common.js"></script>
|
||||||
|
|||||||
+16
-16
@@ -1,6 +1,6 @@
|
|||||||
describe('utils', function() {
|
describe('utils', function () {
|
||||||
describe('#SafeString', function() {
|
describe('#SafeString', function () {
|
||||||
it('constructing a safestring from a string and checking its type', function() {
|
it('constructing a safestring from a string and checking its type', function () {
|
||||||
var safe = new Handlebars.SafeString('testing 1, 2, 3');
|
var safe = new Handlebars.SafeString('testing 1, 2, 3');
|
||||||
if (!(safe instanceof Handlebars.SafeString)) {
|
if (!(safe instanceof Handlebars.SafeString)) {
|
||||||
throw new Error('Must be instance of SafeString');
|
throw new Error('Must be instance of SafeString');
|
||||||
@@ -12,7 +12,7 @@ describe('utils', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('it should not escape SafeString properties', function() {
|
it('it should not escape SafeString properties', function () {
|
||||||
var name = new Handlebars.SafeString('<em>Sean O'Malley</em>');
|
var name = new Handlebars.SafeString('<em>Sean O'Malley</em>');
|
||||||
|
|
||||||
expectTemplate('{{name}}')
|
expectTemplate('{{name}}')
|
||||||
@@ -21,26 +21,26 @@ describe('utils', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#escapeExpression', function() {
|
describe('#escapeExpression', function () {
|
||||||
it('shouhld escape html', function() {
|
it('should escape html', function () {
|
||||||
equals(
|
equals(
|
||||||
Handlebars.Utils.escapeExpression('foo<&"\'>'),
|
Handlebars.Utils.escapeExpression('foo<&"\'>'),
|
||||||
'foo<&"'>'
|
'foo<&"'>'
|
||||||
);
|
);
|
||||||
equals(Handlebars.Utils.escapeExpression('foo='), 'foo=');
|
equals(Handlebars.Utils.escapeExpression('foo='), 'foo=');
|
||||||
});
|
});
|
||||||
it('should not escape SafeString', function() {
|
it('should not escape SafeString', function () {
|
||||||
var string = new Handlebars.SafeString('foo<&"\'>');
|
var string = new Handlebars.SafeString('foo<&"\'>');
|
||||||
equals(Handlebars.Utils.escapeExpression(string), 'foo<&"\'>');
|
equals(Handlebars.Utils.escapeExpression(string), 'foo<&"\'>');
|
||||||
|
|
||||||
var obj = {
|
var obj = {
|
||||||
toHTML: function() {
|
toHTML: function () {
|
||||||
return 'foo<&"\'>';
|
return 'foo<&"\'>';
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
equals(Handlebars.Utils.escapeExpression(obj), 'foo<&"\'>');
|
equals(Handlebars.Utils.escapeExpression(obj), 'foo<&"\'>');
|
||||||
});
|
});
|
||||||
it('should handle falsy', function() {
|
it('should handle falsy', function () {
|
||||||
equals(Handlebars.Utils.escapeExpression(''), '');
|
equals(Handlebars.Utils.escapeExpression(''), '');
|
||||||
equals(Handlebars.Utils.escapeExpression(undefined), '');
|
equals(Handlebars.Utils.escapeExpression(undefined), '');
|
||||||
equals(Handlebars.Utils.escapeExpression(null), '');
|
equals(Handlebars.Utils.escapeExpression(null), '');
|
||||||
@@ -48,14 +48,14 @@ describe('utils', function() {
|
|||||||
equals(Handlebars.Utils.escapeExpression(false), 'false');
|
equals(Handlebars.Utils.escapeExpression(false), 'false');
|
||||||
equals(Handlebars.Utils.escapeExpression(0), '0');
|
equals(Handlebars.Utils.escapeExpression(0), '0');
|
||||||
});
|
});
|
||||||
it('should handle empty objects', function() {
|
it('should handle empty objects', function () {
|
||||||
equals(Handlebars.Utils.escapeExpression({}), {}.toString());
|
equals(Handlebars.Utils.escapeExpression({}), {}.toString());
|
||||||
equals(Handlebars.Utils.escapeExpression([]), [].toString());
|
equals(Handlebars.Utils.escapeExpression([]), [].toString());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#isEmpty', function() {
|
describe('#isEmpty', function () {
|
||||||
it('should not be empty', function() {
|
it('should not be empty', function () {
|
||||||
equals(Handlebars.Utils.isEmpty(undefined), true);
|
equals(Handlebars.Utils.isEmpty(undefined), true);
|
||||||
equals(Handlebars.Utils.isEmpty(null), true);
|
equals(Handlebars.Utils.isEmpty(null), true);
|
||||||
equals(Handlebars.Utils.isEmpty(false), true);
|
equals(Handlebars.Utils.isEmpty(false), true);
|
||||||
@@ -63,7 +63,7 @@ describe('utils', function() {
|
|||||||
equals(Handlebars.Utils.isEmpty([]), true);
|
equals(Handlebars.Utils.isEmpty([]), true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be empty', function() {
|
it('should be empty', function () {
|
||||||
equals(Handlebars.Utils.isEmpty(0), false);
|
equals(Handlebars.Utils.isEmpty(0), false);
|
||||||
equals(Handlebars.Utils.isEmpty([1]), false);
|
equals(Handlebars.Utils.isEmpty([1]), false);
|
||||||
equals(Handlebars.Utils.isEmpty('foo'), false);
|
equals(Handlebars.Utils.isEmpty('foo'), false);
|
||||||
@@ -71,8 +71,8 @@ describe('utils', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#extend', function() {
|
describe('#extend', function () {
|
||||||
it('should ignore prototype values', function() {
|
it('should ignore prototype values', function () {
|
||||||
function A() {
|
function A() {
|
||||||
this.a = 1;
|
this.a = 1;
|
||||||
}
|
}
|
||||||
|
|||||||
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
+1
-1
@@ -1488,7 +1488,7 @@ var requirejs, require, define;
|
|||||||
/**
|
/**
|
||||||
* Called to enable a module if it is still in the registry
|
* Called to enable a module if it is still in the registry
|
||||||
* awaiting enablement. A second arg, parent, the parent module,
|
* awaiting enablement. A second arg, parent, the parent module,
|
||||||
* is passed in for context, when this method is overriden by
|
* is passed in for context, when this method is overridden by
|
||||||
* the optimizer. Not shown here to keep code compact.
|
* the optimizer. Not shown here to keep code compact.
|
||||||
*/
|
*/
|
||||||
enable: function (depMap) {
|
enable: function (depMap) {
|
||||||
|
|||||||
-164
@@ -1,164 +0,0 @@
|
|||||||
describe('Visitor', function() {
|
|
||||||
if (!Handlebars.Visitor || !Handlebars.print) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should provide coverage', function() {
|
|
||||||
// Simply run the thing and make sure it does not fail and that all of the
|
|
||||||
// stub methods are executed
|
|
||||||
var visitor = new Handlebars.Visitor();
|
|
||||||
visitor.accept(
|
|
||||||
Handlebars.parse(
|
|
||||||
'{{foo}}{{#foo (bar 1 "1" true undefined null) foo=@data}}{{!comment}}{{> bar }} {{/foo}}'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
visitor.accept(Handlebars.parse('{{#> bar }} {{/bar}}'));
|
|
||||||
visitor.accept(Handlebars.parse('{{#* bar }} {{/bar}}'));
|
|
||||||
visitor.accept(Handlebars.parse('{{* bar }}'));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should traverse to stubs', function() {
|
|
||||||
var visitor = new Handlebars.Visitor();
|
|
||||||
|
|
||||||
visitor.StringLiteral = function(string) {
|
|
||||||
equal(string.value, '2');
|
|
||||||
};
|
|
||||||
visitor.NumberLiteral = function(number) {
|
|
||||||
equal(number.value, 1);
|
|
||||||
};
|
|
||||||
visitor.BooleanLiteral = function(bool) {
|
|
||||||
equal(bool.value, true);
|
|
||||||
|
|
||||||
equal(this.parents.length, 3);
|
|
||||||
equal(this.parents[0].type, 'SubExpression');
|
|
||||||
equal(this.parents[1].type, 'BlockStatement');
|
|
||||||
equal(this.parents[2].type, 'Program');
|
|
||||||
};
|
|
||||||
visitor.PathExpression = function(id) {
|
|
||||||
equal(/(foo\.)?bar$/.test(id.original), true);
|
|
||||||
};
|
|
||||||
visitor.ContentStatement = function(content) {
|
|
||||||
equal(content.value, ' ');
|
|
||||||
};
|
|
||||||
visitor.CommentStatement = function(comment) {
|
|
||||||
equal(comment.value, 'comment');
|
|
||||||
};
|
|
||||||
|
|
||||||
visitor.accept(
|
|
||||||
Handlebars.parse(
|
|
||||||
'{{#foo.bar (foo.bar 1 "2" true) foo=@foo.bar}}{{!comment}}{{> bar }} {{/foo.bar}}'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('mutating', function() {
|
|
||||||
describe('fields', function() {
|
|
||||||
it('should replace value', function() {
|
|
||||||
var visitor = new Handlebars.Visitor();
|
|
||||||
|
|
||||||
visitor.mutating = true;
|
|
||||||
visitor.StringLiteral = function(string) {
|
|
||||||
return { type: 'NumberLiteral', value: 42, loc: string.loc };
|
|
||||||
};
|
|
||||||
|
|
||||||
var ast = Handlebars.parse('{{foo foo="foo"}}');
|
|
||||||
visitor.accept(ast);
|
|
||||||
equals(
|
|
||||||
Handlebars.print(ast),
|
|
||||||
'{{ PATH:foo [] HASH{foo=NUMBER{42}} }}\n'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('should treat undefined resonse as identity', function() {
|
|
||||||
var visitor = new Handlebars.Visitor();
|
|
||||||
visitor.mutating = true;
|
|
||||||
|
|
||||||
var ast = Handlebars.parse('{{foo foo=42}}');
|
|
||||||
visitor.accept(ast);
|
|
||||||
equals(
|
|
||||||
Handlebars.print(ast),
|
|
||||||
'{{ PATH:foo [] HASH{foo=NUMBER{42}} }}\n'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('should remove false responses', function() {
|
|
||||||
var visitor = new Handlebars.Visitor();
|
|
||||||
|
|
||||||
visitor.mutating = true;
|
|
||||||
visitor.Hash = function() {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
var ast = Handlebars.parse('{{foo foo=42}}');
|
|
||||||
visitor.accept(ast);
|
|
||||||
equals(Handlebars.print(ast), '{{ PATH:foo [] }}\n');
|
|
||||||
});
|
|
||||||
it('should throw when removing required values', function() {
|
|
||||||
shouldThrow(
|
|
||||||
function() {
|
|
||||||
var visitor = new Handlebars.Visitor();
|
|
||||||
|
|
||||||
visitor.mutating = true;
|
|
||||||
visitor.PathExpression = function() {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
var ast = Handlebars.parse('{{foo 42}}');
|
|
||||||
visitor.accept(ast);
|
|
||||||
},
|
|
||||||
Handlebars.Exception,
|
|
||||||
'MustacheStatement requires path'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('should throw when returning non-node responses', function() {
|
|
||||||
shouldThrow(
|
|
||||||
function() {
|
|
||||||
var visitor = new Handlebars.Visitor();
|
|
||||||
|
|
||||||
visitor.mutating = true;
|
|
||||||
visitor.PathExpression = function() {
|
|
||||||
return {};
|
|
||||||
};
|
|
||||||
|
|
||||||
var ast = Handlebars.parse('{{foo 42}}');
|
|
||||||
visitor.accept(ast);
|
|
||||||
},
|
|
||||||
Handlebars.Exception,
|
|
||||||
'Unexpected node type "undefined" found when accepting path on MustacheStatement'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('arrays', function() {
|
|
||||||
it('should replace value', function() {
|
|
||||||
var visitor = new Handlebars.Visitor();
|
|
||||||
|
|
||||||
visitor.mutating = true;
|
|
||||||
visitor.StringLiteral = function(string) {
|
|
||||||
return { type: 'NumberLiteral', value: 42, loc: string.locInfo };
|
|
||||||
};
|
|
||||||
|
|
||||||
var ast = Handlebars.parse('{{foo "foo"}}');
|
|
||||||
visitor.accept(ast);
|
|
||||||
equals(Handlebars.print(ast), '{{ PATH:foo [NUMBER{42}] }}\n');
|
|
||||||
});
|
|
||||||
it('should treat undefined resonse as identity', function() {
|
|
||||||
var visitor = new Handlebars.Visitor();
|
|
||||||
visitor.mutating = true;
|
|
||||||
|
|
||||||
var ast = Handlebars.parse('{{foo 42}}');
|
|
||||||
visitor.accept(ast);
|
|
||||||
equals(Handlebars.print(ast), '{{ PATH:foo [NUMBER{42}] }}\n');
|
|
||||||
});
|
|
||||||
it('should remove false responses', function() {
|
|
||||||
var visitor = new Handlebars.Visitor();
|
|
||||||
|
|
||||||
visitor.mutating = true;
|
|
||||||
visitor.NumberLiteral = function() {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
var ast = Handlebars.parse('{{foo 42}}');
|
|
||||||
visitor.accept(ast);
|
|
||||||
equals(Handlebars.print(ast), '{{ PATH:foo [] }}\n');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
+13
-23
@@ -1,32 +1,22 @@
|
|||||||
describe('whitespace control', function() {
|
describe('whitespace control', function () {
|
||||||
it('should strip whitespace around mustache calls', function() {
|
it('should strip whitespace around mustache calls', function () {
|
||||||
var hash = { foo: 'bar<' };
|
var hash = { foo: 'bar<' };
|
||||||
|
|
||||||
expectTemplate(' {{~foo~}} ')
|
expectTemplate(' {{~foo~}} ').withInput(hash).toCompileTo('bar<');
|
||||||
.withInput(hash)
|
|
||||||
.toCompileTo('bar<');
|
|
||||||
|
|
||||||
expectTemplate(' {{~foo}} ')
|
expectTemplate(' {{~foo}} ').withInput(hash).toCompileTo('bar< ');
|
||||||
.withInput(hash)
|
|
||||||
.toCompileTo('bar< ');
|
|
||||||
|
|
||||||
expectTemplate(' {{foo~}} ')
|
expectTemplate(' {{foo~}} ').withInput(hash).toCompileTo(' bar<');
|
||||||
.withInput(hash)
|
|
||||||
.toCompileTo(' bar<');
|
|
||||||
|
|
||||||
expectTemplate(' {{~&foo~}} ')
|
expectTemplate(' {{~&foo~}} ').withInput(hash).toCompileTo('bar<');
|
||||||
.withInput(hash)
|
|
||||||
.toCompileTo('bar<');
|
|
||||||
|
|
||||||
expectTemplate(' {{~{foo}~}} ')
|
expectTemplate(' {{~{foo}~}} ').withInput(hash).toCompileTo('bar<');
|
||||||
.withInput(hash)
|
|
||||||
.toCompileTo('bar<');
|
|
||||||
|
|
||||||
expectTemplate('1\n{{foo~}} \n\n 23\n{{bar}}4').toCompileTo('1\n23\n4');
|
expectTemplate('1\n{{foo~}} \n\n 23\n{{bar}}4').toCompileTo('1\n23\n4');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('blocks', function() {
|
describe('blocks', function () {
|
||||||
it('should strip whitespace around simple block calls', function() {
|
it('should strip whitespace around simple block calls', function () {
|
||||||
var hash = { foo: 'bar<' };
|
var hash = { foo: 'bar<' };
|
||||||
|
|
||||||
expectTemplate(' {{~#if foo~}} bar {{~/if~}} ')
|
expectTemplate(' {{~#if foo~}} bar {{~/if~}} ')
|
||||||
@@ -54,7 +44,7 @@ describe('whitespace control', function() {
|
|||||||
.toCompileTo(' abara ');
|
.toCompileTo(' abara ');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should strip whitespace around inverse block calls', function() {
|
it('should strip whitespace around inverse block calls', function () {
|
||||||
expectTemplate(' {{~^if foo~}} bar {{~/if~}} ').toCompileTo('bar');
|
expectTemplate(' {{~^if foo~}} bar {{~/if~}} ').toCompileTo('bar');
|
||||||
|
|
||||||
expectTemplate(' {{^if foo~}} bar {{/if~}} ').toCompileTo(' bar ');
|
expectTemplate(' {{^if foo~}} bar {{/if~}} ').toCompileTo(' bar ');
|
||||||
@@ -68,7 +58,7 @@ describe('whitespace control', function() {
|
|||||||
).toCompileTo('bar');
|
).toCompileTo('bar');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should strip whitespace around complex block calls', function() {
|
it('should strip whitespace around complex block calls', function () {
|
||||||
var hash = { foo: 'bar<' };
|
var hash = { foo: 'bar<' };
|
||||||
|
|
||||||
expectTemplate('{{#if foo~}} bar {{~^~}} baz {{~/if}}')
|
expectTemplate('{{#if foo~}} bar {{~^~}} baz {{~/if}}')
|
||||||
@@ -127,7 +117,7 @@ describe('whitespace control', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should strip whitespace around partials', function() {
|
it('should strip whitespace around partials', function () {
|
||||||
expectTemplate('foo {{~> dude~}} ')
|
expectTemplate('foo {{~> dude~}} ')
|
||||||
.withPartials({ dude: 'bar' })
|
.withPartials({ dude: 'bar' })
|
||||||
.toCompileTo('foobar');
|
.toCompileTo('foobar');
|
||||||
@@ -149,7 +139,7 @@ describe('whitespace control', function() {
|
|||||||
.toCompileTo('foo\n bar');
|
.toCompileTo('foo\n bar');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should only strip whitespace once', function() {
|
it('should only strip whitespace once', function () {
|
||||||
expectTemplate(' {{~foo~}} {{foo}} {{foo}} ')
|
expectTemplate(' {{~foo~}} {{foo}} {{foo}} ')
|
||||||
.withInput({ foo: 'bar' })
|
.withInput({ foo: 'bar' })
|
||||||
.toCompileTo('barbar bar ');
|
.toCompileTo('barbar bar ');
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user