Compare commits
210 Commits
| 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.
|
||||||
+34
-11
@@ -12,12 +12,12 @@ jobs:
|
|||||||
runs-on: 'ubuntu-latest'
|
runs-on: 'ubuntu-latest'
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
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,16 +58,16 @@ 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: ['16', '18', '20', '22']
|
node-version: ['12', '14', '16', '18', '20']
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
|
|
||||||
@@ -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 == '16' }}
|
|
||||||
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 -
|
||||||
@@ -65,14 +88,14 @@ jobs:
|
|||||||
runs-on: 'ubuntu-22.04'
|
runs-on: 'ubuntu-22.04'
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
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
|
||||||
|
|||||||
@@ -22,7 +22,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
|
||||||
|
|||||||
@@ -12,9 +12,7 @@ node_modules
|
|||||||
.nyc_output
|
.nyc_output
|
||||||
|
|
||||||
# Generated files
|
# Generated files
|
||||||
lib/handlebars/compiler/parser.js
|
|
||||||
/coverage/
|
/coverage/
|
||||||
/dist/
|
/dist/
|
||||||
/test-results/
|
|
||||||
/tests/integration/*/dist/
|
/tests/integration/*/dist/
|
||||||
/spec/tmp/*
|
/spec/tmp/*
|
||||||
|
|||||||
@@ -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/
|
||||||
|
|||||||
+20
-83
@@ -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
|
||||||
|
|
||||||
@@ -93,97 +101,26 @@ You can use the following scripts to make sure that the CI job does not fail:
|
|||||||
|
|
||||||
Before attempting the release Handlebars, please make sure that you have the following authorizations:
|
Before attempting the release Handlebars, please make sure that you have the following authorizations:
|
||||||
|
|
||||||
- Push-access to [handlebars-lang/handlebars.js](https://github.com/handlebars-lang/handlebars.js/)
|
- Push-access to `handlebars-lang/handlebars.js`
|
||||||
- Publishing rights on npmjs.com for the [handlebars](https://www.npmjs.com/package/handlebars) package
|
- Publishing rights on npmjs.com for the `handlebars` package
|
||||||
- Publishing rights on rubygems for the [handlebars-source](https://rubygems.org/gems/handlebars-source) package
|
- Publishing rights on gemfury for the `handlebars-source` package
|
||||||
- Push-access to the repo for legacy package managers: [components/handlebars.js](https://github.com/components/handlebars.js)
|
- Push-access to the repo for legacy package managers: `components/handlebars`
|
||||||
- Push-access to the production-repo of the handlebars site: [handlebars-lang/docs](https://github.com/handlebars-lang/docs)
|
- Push-access to the production-repo of the handlebars site: `handlebars-lang/handlebarsjs.com-github-pages`
|
||||||
|
|
||||||
_When releasing a previous version of Handlebars, please look into the CONTRIBUNG.md in the corresponding branch._
|
_When releasing a previous version of Handlebars, please look into the CONTRIBUNG.md in the corresponding branch._
|
||||||
|
|
||||||
A full release via Docker may be completed with the following:
|
A full release may be completed with the following:
|
||||||
|
|
||||||
1. Create a `Dockerfile` in this folder for releasing
|
|
||||||
```Dockerfile
|
|
||||||
FROM node:10-slim
|
|
||||||
|
|
||||||
ENV EDITOR=vim
|
|
||||||
|
|
||||||
# Update stretch repositories
|
|
||||||
RUN sed -i -e 's/deb.debian.org/archive.debian.org/g' \
|
|
||||||
-e 's|security.debian.org|archive.debian.org/|g' \
|
|
||||||
-e '/stretch-updates/d' /etc/apt/sources.list
|
|
||||||
|
|
||||||
# Install release dependencies
|
|
||||||
RUN apt-get update
|
|
||||||
RUN apt-get install -y git vim
|
|
||||||
|
|
||||||
# Work around deprecated npm dependency install via unauthenticated git-protocol:
|
|
||||||
# https://github.com/kpdecker/generator-release/blob/87aab9b84c9f083635c3fcc822f18acce1f48736/package.json#L31
|
|
||||||
RUN git config --system url."https://github.com/".insteadOf git://github.com/
|
|
||||||
|
|
||||||
# Configure git
|
|
||||||
RUN git config --system user.email "release@handlebarsjs.com"
|
|
||||||
RUN git config --system user.name "handlebars-lang"
|
|
||||||
|
|
||||||
RUN mkdir /home/node/.config
|
|
||||||
RUN mkdir /home/node/.ssh
|
|
||||||
RUN mkdir /home/node/tmp
|
|
||||||
|
|
||||||
# Generate config for yo generator-release:
|
|
||||||
# https://github.com/kpdecker/generator-release#example
|
|
||||||
# You have to add a valid GitHub access token! (Used for reading issues and pull requests.)
|
|
||||||
RUN echo "module.exports = {\n auth: 'oauth',\n token: 'GitHub personal access token'\n};" > /home/node/.config/generator-release
|
|
||||||
RUN chown -R node:node /home/node/.config
|
|
||||||
RUN chown -R node:node /home/node/.ssh
|
|
||||||
RUN chown -R node:node /home/node/tmp
|
|
||||||
|
|
||||||
# Add the generated key to GitHub: https://github.com/settings/keys
|
|
||||||
RUN ssh-keygen -q -t ed25519 -N '' -f /home/node/.ssh/id_ed25519 -C "release@handlebarsjs.com"
|
|
||||||
RUN chmod 0600 /home/node/.ssh/id_ed25519*
|
|
||||||
RUN chown node:node /home/node/.ssh/id_ed25519*
|
|
||||||
```
|
```
|
||||||
2. Build and run the Docker image
|
npm ci
|
||||||
```bash
|
npx grunt
|
||||||
docker build --tag handlebars:release .
|
|
||||||
docker run --rm --interactive --tty \
|
|
||||||
--volume $PWD:/app \
|
|
||||||
--workdir /app \
|
|
||||||
--user $(id -u):$(id -g) \
|
|
||||||
--env NPM_CONFIG_PREFIX=/home/node/.npm-global \
|
|
||||||
handlebars:release bash -c 'export PATH=$PATH:/home/node/.npm-global/bin; bash'
|
|
||||||
```
|
|
||||||
* Add SSH key to GitHub: `cat /home/node/.ssh/id_ed25519.pub` (https://github.com/settings/keys)
|
|
||||||
* Add GitHub API token: `vi /home/node/.config/generator-release`
|
|
||||||
* Execute the following steps:
|
|
||||||
```bash
|
|
||||||
npm install
|
|
||||||
npm install -g yo@1 grunt@1 generator-release
|
|
||||||
npm run release
|
|
||||||
# Warning! This step will collect data from GitHub, bump the version,
|
|
||||||
# create a new commit, create a new tag and push it to GitHub.
|
|
||||||
# https://github.com/kpdecker/generator-release?tab=readme-ov-file#usage
|
|
||||||
yo release
|
|
||||||
npm login
|
|
||||||
npm publish
|
npm publish
|
||||||
yo release:publish components handlebars.js dist/components/
|
|
||||||
```
|
|
||||||
6. Publish Ruby `handlebars-source` gem:
|
|
||||||
```bash
|
|
||||||
docker run --rm --interactive --tty \
|
|
||||||
--volume $PWD:/app \
|
|
||||||
--workdir /app \
|
|
||||||
ruby:3.2-slim bash
|
|
||||||
```
|
|
||||||
* Execute the following steps:
|
|
||||||
```bash
|
|
||||||
cd dist/components/
|
cd dist/components/
|
||||||
gem build handlebars-source.gemspec
|
gem build handlebars-source.gemspec
|
||||||
gem push handlebars-source-*.gem
|
gem push handlebars-source-*.gem
|
||||||
```
|
```
|
||||||
|
|
||||||
### After the release
|
|
||||||
|
|
||||||
After the release, you should check that all places have really been updated. Especially verify that the `latest`-tags
|
After the release, you should check that all places have really been updated. Especially verify that the `latest`-tags
|
||||||
in those places still point to the latest version
|
in those places still point to the latest version
|
||||||
|
|
||||||
@@ -203,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,22 +1,22 @@
|
|||||||
# 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:
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
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:
|
||||||
|
|
||||||
@@ -49,14 +49,6 @@
|
|||||||
|
|
||||||
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.
|
|
||||||
|
|||||||
+45
-106
@@ -3,12 +3,7 @@ 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: {
|
||||||
@@ -16,17 +11,14 @@ module.exports = function(grunt) {
|
|||||||
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: [
|
||||||
@@ -143,17 +93,17 @@ module.exports = function(grunt) {
|
|||||||
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,27 +111,27 @@ module.exports = function(grunt) {
|
|||||||
options: {
|
options: {
|
||||||
base: '.',
|
base: '.',
|
||||||
hostname: '*',
|
hostname: '*',
|
||||||
port: 9999
|
port: 9999,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
shell: {
|
shell: {
|
||||||
integrationTests: {
|
integrationTests: {
|
||||||
command: './tests/integration/run-integration-tests.sh'
|
command: './tests/integration/run-integration-tests.sh',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
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
|
||||||
@@ -189,7 +139,6 @@ 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');
|
||||||
@@ -199,22 +148,12 @@ module.exports = function(grunt) {
|
|||||||
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',
|
|
||||||
'shell:integrationTests',
|
|
||||||
'metrics',
|
|
||||||
'publish-to-aws'
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
grunt.registerTask('on-file-change', ['build', 'concat:tests', 'test']);
|
grunt.registerTask('on-file-change', ['build', 'concat:tests', 'test']);
|
||||||
@@ -228,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',
|
||||||
'shell:integrationTests'
|
'shell:integrationTests',
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|||||||
+39
-38
@@ -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.9",
|
"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"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "components/handlebars.js",
|
"name": "components/handlebars.js",
|
||||||
"description": "Handlebars.js and Mustache are both logicless templating languages that keep the view and the code separated like we all know they should be.",
|
"description": "Handlebars.js and Mustache are both logicless templating languages that keep the view and the code separated like we all know they should be.",
|
||||||
"homepage": "https://handlebarsjs.com",
|
"homepage": "http://handlebarsjs.com",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "component",
|
"type": "component",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
@@ -11,9 +11,13 @@
|
|||||||
],
|
],
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "Chris Wanstrath"
|
"name": "Chris Wanstrath",
|
||||||
|
"homepage": "http://chriswanstrath.com"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"require": {
|
||||||
|
"robloach/component-installer": "*"
|
||||||
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"component": {
|
"component": {
|
||||||
"name": "handlebars",
|
"name": "handlebars",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<package>
|
<package>
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>handlebars.js</id>
|
<id>handlebars.js</id>
|
||||||
<version>4.7.9</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.9",
|
"version": "5.0.0-alpha.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"jspm": {
|
"jspm": {
|
||||||
"main": "handlebars",
|
"main": "handlebars",
|
||||||
|
|||||||
+23
-22
@@ -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
|
||||||
@@ -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,5 +1,7 @@
|
|||||||
# Decorators
|
# Decorators
|
||||||
|
|
||||||
|
**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 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
-4
@@ -1,8 +1,7 @@
|
|||||||
|
/* eslint-env node */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
env: {
|
env: {
|
||||||
// Handlebars should not use node or browser-specific APIs
|
// Handlebars should run natively in the browser
|
||||||
'shared-node-browser': true,
|
|
||||||
node: false,
|
node: false,
|
||||||
browser: false
|
},
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|||||||
+7
-6
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
|
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';
|
||||||
import { resetLoggedProperties } from './internal/proto-access';
|
import { resetLoggedProperties } from './internal/proto-access';
|
||||||
|
|
||||||
export const VERSION = '4.7.9';
|
export const VERSION = '4.7.7';
|
||||||
export const COMPILER_REVISION = 8;
|
export const COMPILER_REVISION = 8;
|
||||||
export const LAST_COMPATIBLE_COMPILER_REVISION = 7;
|
export const LAST_COMPATIBLE_COMPILER_REVISION = 7;
|
||||||
|
|
||||||
@@ -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]';
|
||||||
@@ -86,7 +86,7 @@ HandlebarsEnvironment.prototype = {
|
|||||||
*/
|
*/
|
||||||
resetLoggedPropertyAccesses() {
|
resetLoggedPropertyAccesses() {
|
||||||
resetLoggedProperties();
|
resetLoggedProperties();
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export let log = logger.log;
|
export let log = logger.log;
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ let AST = {
|
|||||||
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);
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
/* global define, require */
|
/* global define */
|
||||||
import { isArray } from '../utils';
|
import { isArray } from '../utils';
|
||||||
|
|
||||||
let SourceNode;
|
let SourceNode;
|
||||||
@@ -8,7 +8,7 @@ try {
|
|||||||
if (typeof define !== 'function' || !define.amd) {
|
if (typeof define !== 'function' || !define.amd) {
|
||||||
// We don't support this in AMD environments. For these environments, we assume that
|
// We don't support this in AMD environments. For these environments, we assume that
|
||||||
// they are running on the browser and thus have no need for the source-map library.
|
// they are running on the browser and thus have no need for the source-map library.
|
||||||
let SourceMap = require('source-map');
|
let SourceMap = require('source-map'); // eslint-disable-line no-undef
|
||||||
SourceNode = SourceMap.SourceNode;
|
SourceNode = SourceMap.SourceNode;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -42,7 +42,7 @@ if (!SourceNode) {
|
|||||||
},
|
},
|
||||||
toString: function () {
|
toString: function () {
|
||||||
return this.src;
|
return this.src;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +132,7 @@ 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]);
|
||||||
@@ -165,7 +165,7 @@ CodeGen.prototype = {
|
|||||||
ret.add(']');
|
ret.add(']');
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CodeGen;
|
export default CodeGen;
|
||||||
|
|||||||
@@ -1,13 +1,7 @@
|
|||||||
/* eslint-disable new-cap */
|
/* eslint-disable new-cap */
|
||||||
|
|
||||||
import Exception from '../exception';
|
import { Exception } from '@handlebars/parser';
|
||||||
import {
|
import { isArray, indexOf, extend } from '../utils';
|
||||||
isArray,
|
|
||||||
indexOf,
|
|
||||||
extend,
|
|
||||||
sanitizeDepth,
|
|
||||||
sanitizeParts
|
|
||||||
} from '../utils';
|
|
||||||
import AST from './ast';
|
import AST from './ast';
|
||||||
|
|
||||||
const slice = [].slice;
|
const slice = [].slice;
|
||||||
@@ -58,8 +52,6 @@ Compiler.prototype = {
|
|||||||
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 || [];
|
||||||
|
|
||||||
@@ -73,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
|
||||||
);
|
);
|
||||||
@@ -249,7 +241,7 @@ Compiler.prototype = {
|
|||||||
name = path.parts[0],
|
name = path.parts[0],
|
||||||
isBlock = program != null || inverse != null;
|
isBlock = program != null || inverse != null;
|
||||||
|
|
||||||
this.opcode('getContext', sanitizeDepth(path.depth));
|
this.opcode('getContext', path.depth);
|
||||||
|
|
||||||
this.opcode('pushProgram', program);
|
this.opcode('pushProgram', program);
|
||||||
this.opcode('pushProgram', inverse);
|
this.opcode('pushProgram', inverse);
|
||||||
@@ -294,32 +286,29 @@ Compiler.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
PathExpression: function (path) {
|
PathExpression: function (path) {
|
||||||
// Sanitize untrusted AST values at the compiler boundary.
|
this.addDepth(path.depth);
|
||||||
// javascript-compiler.js trusts all opcode arguments to be safe.
|
this.opcode('getContext', path.depth);
|
||||||
const depth = sanitizeDepth(path.depth);
|
|
||||||
const parts = sanitizeParts(path.parts);
|
|
||||||
|
|
||||||
this.addDepth(depth);
|
let name = path.parts[0],
|
||||||
this.opcode('getContext', depth);
|
|
||||||
|
|
||||||
let name = parts[0],
|
|
||||||
scoped = AST.helpers.scopedId(path),
|
scoped = AST.helpers.scopedId(path),
|
||||||
blockParamId = !depth && !scoped && this.blockParamIndex(name);
|
blockParamId = !path.depth && !scoped && this.blockParamIndex(name);
|
||||||
|
|
||||||
if (blockParamId) {
|
if (blockParamId) {
|
||||||
this.opcode(
|
this.opcode('lookupBlockParam', blockParamId, path.parts);
|
||||||
'lookupBlockParam',
|
|
||||||
[Number(blockParamId[0]), Number(blockParamId[1])],
|
|
||||||
parts
|
|
||||||
);
|
|
||||||
} else if (!name) {
|
} else if (!name) {
|
||||||
// Context reference, i.e. `{{foo .}}` or `{{foo ..}}`
|
// Context reference, i.e. `{{foo .}}` or `{{foo ..}}`
|
||||||
this.opcode('pushContext');
|
this.opcode('pushContext');
|
||||||
} else if (path.data) {
|
} else if (path.data) {
|
||||||
this.options.data = true;
|
this.options.data = true;
|
||||||
this.opcode('lookupData', depth, parts, path.strict);
|
this.opcode('lookupData', path.depth, path.parts, path.strict);
|
||||||
} else {
|
} else {
|
||||||
this.opcode('lookupOnContext', parts, path.falsy, path.strict, scoped);
|
this.opcode(
|
||||||
|
'lookupOnContext',
|
||||||
|
path.parts,
|
||||||
|
path.falsy,
|
||||||
|
path.strict,
|
||||||
|
scoped
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -328,11 +317,11 @@ Compiler.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
NumberLiteral: function (number) {
|
NumberLiteral: function (number) {
|
||||||
this.opcode('pushLiteral', Number(number.value));
|
this.opcode('pushLiteral', number.value);
|
||||||
},
|
},
|
||||||
|
|
||||||
BooleanLiteral: function (bool) {
|
BooleanLiteral: function (bool) {
|
||||||
this.opcode('pushLiteral', bool.value === true ? 'true' : 'false');
|
this.opcode('pushLiteral', bool.value);
|
||||||
},
|
},
|
||||||
|
|
||||||
UndefinedLiteral: function () {
|
UndefinedLiteral: function () {
|
||||||
@@ -364,7 +353,7 @@ Compiler.prototype = {
|
|||||||
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,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -418,47 +407,7 @@ Compiler.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
pushParam: function (val) {
|
pushParam: function (val) {
|
||||||
let value = val.value != null ? val.value : val.original || '';
|
|
||||||
let depth = sanitizeDepth(val.depth);
|
|
||||||
|
|
||||||
if (this.stringParams) {
|
|
||||||
if (value.replace) {
|
|
||||||
value = value.replace(/^(\.?\.\/)*/g, '').replace(/\//g, '.');
|
|
||||||
}
|
|
||||||
if (depth) {
|
|
||||||
this.addDepth(depth);
|
|
||||||
}
|
|
||||||
this.opcode('getContext', depth);
|
|
||||||
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) {
|
||||||
@@ -489,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,
|
||||||
@@ -550,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();
|
|
||||||
}
|
}
|
||||||
return compiled._child(i, data, blockParams, depths);
|
|
||||||
};
|
function validateInput(input, options) {
|
||||||
return ret;
|
if (
|
||||||
|
input == null ||
|
||||||
|
(typeof input !== 'string' && input.type !== 'Program')
|
||||||
|
) {
|
||||||
|
throw new Exception(
|
||||||
|
'You must pass a string or Handlebars AST to Handlebars.compile. You passed ' +
|
||||||
|
input
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.trackIds || options.stringParams) {
|
||||||
|
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) {
|
||||||
@@ -597,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';
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
this.aliasable('container.lookup'),
|
this.aliasable('container.lookup'),
|
||||||
'(depths, ',
|
'(depths, ',
|
||||||
JSON.stringify(name),
|
JSON.stringify(name),
|
||||||
')'
|
')',
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -64,8 +64,6 @@ JavaScriptCompiler.prototype = {
|
|||||||
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) {
|
||||||
@@ -165,12 +164,14 @@ JavaScriptCompiler.prototype = {
|
|||||||
|
|
||||||
let { programs, decorators } = this.context;
|
let { programs, decorators } = this.context;
|
||||||
for (i = 0, l = programs.length; i < l; i++) {
|
for (i = 0, l = programs.length; i < l; i++) {
|
||||||
|
if (programs[i]) {
|
||||||
ret[i] = programs[i];
|
ret[i] = programs[i];
|
||||||
if (decorators[i]) {
|
if (decorators[i]) {
|
||||||
ret[i + '_d'] = decorators[i];
|
ret[i + '_d'] = decorators[i];
|
||||||
ret.useDecorators = true;
|
ret.useDecorators = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.environment.usePartial) {
|
if (this.environment.usePartial) {
|
||||||
ret.usePartial = true;
|
ret.usePartial = true;
|
||||||
@@ -233,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;
|
||||||
@@ -260,14 +261,14 @@ 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,
|
||||||
'}'
|
'}',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -278,7 +279,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
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(' + ');
|
||||||
@@ -391,7 +392,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
current,
|
current,
|
||||||
' = ',
|
' = ',
|
||||||
this.source.functionCall(blockHelperMissing, 'call', params),
|
this.source.functionCall(blockHelperMissing, 'call', params),
|
||||||
'}'
|
'}',
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -422,7 +423,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
// 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 {
|
||||||
@@ -432,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),
|
||||||
' }'
|
' }',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -456,7 +457,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
this.aliasable('container.escapeExpression'),
|
this.aliasable('container.escapeExpression'),
|
||||||
'(',
|
'(',
|
||||||
this.popStack(),
|
this.popStack(),
|
||||||
')'
|
')',
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -550,7 +551,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
let len = parts.length;
|
let len = parts.length;
|
||||||
for (let i = startPartIndex; 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.
|
||||||
@@ -579,61 +580,23 @@ JavaScriptCompiler.prototype = {
|
|||||||
this.popStack(),
|
this.popStack(),
|
||||||
', ',
|
', ',
|
||||||
this.contextName(0),
|
this.contextName(0),
|
||||||
')'
|
')',
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
|
||||||
// [pushStringParam]
|
|
||||||
//
|
|
||||||
// On stack, before: ...
|
|
||||||
// On stack, after: string, currentContext, ...
|
|
||||||
//
|
|
||||||
// This opcode is designed for use in string mode, which
|
|
||||||
// provides the string value of a parameter along with its
|
|
||||||
// depth rather than resolving it immediately.
|
|
||||||
pushStringParam: function(string, type) {
|
|
||||||
this.pushContext();
|
|
||||||
this.pushString(type);
|
|
||||||
|
|
||||||
// If it's a subexpression, the string result
|
|
||||||
// will be pushed after this opcode.
|
|
||||||
if (type !== 'SubExpression') {
|
|
||||||
if (typeof string === 'string') {
|
|
||||||
this.pushString(string);
|
|
||||||
} else {
|
|
||||||
this.pushStackLiteral(string);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
emptyHash: function (omitEmpty) {
|
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));
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -686,24 +649,15 @@ JavaScriptCompiler.prototype = {
|
|||||||
let foundDecorator = this.nameLookup('decorators', name, 'decorator'),
|
let foundDecorator = this.nameLookup('decorators', name, 'decorator'),
|
||||||
options = this.setupHelperArgs(name, paramSize);
|
options = this.setupHelperArgs(name, paramSize);
|
||||||
|
|
||||||
// Store the resolved decorator in a variable and verify it is a function before
|
|
||||||
// calling it. Without this, unregistered decorators can cause an unhandled TypeError
|
|
||||||
// (calling undefined), which crashes the process — enabling Denial of Service.
|
|
||||||
this.decorators.push(['var decorator = ', foundDecorator, ';']);
|
|
||||||
this.decorators.push([
|
|
||||||
'if (typeof decorator !== "function") { throw new Error(',
|
|
||||||
this.quotedString('Missing decorator: "' + name + '"'),
|
|
||||||
'); }'
|
|
||||||
]);
|
|
||||||
this.decorators.push([
|
this.decorators.push([
|
||||||
'fn = ',
|
'fn = ',
|
||||||
this.decorators.functionCall('decorator', '', [
|
this.decorators.functionCall(foundDecorator, '', [
|
||||||
'fn',
|
'fn',
|
||||||
'props',
|
'props',
|
||||||
'container',
|
'container',
|
||||||
options
|
options,
|
||||||
]),
|
]),
|
||||||
' || fn;'
|
' || fn;',
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -737,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,
|
||||||
@@ -811,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))',
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -860,49 +814,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
//
|
//
|
||||||
// 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
|
||||||
@@ -921,8 +833,8 @@ JavaScriptCompiler.prototype = {
|
|||||||
let existing = this.matchExistingProgram(child);
|
let existing = this.matchExistingProgram(child);
|
||||||
|
|
||||||
if (existing == null) {
|
if (existing == null) {
|
||||||
// Placeholder to prevent name conflicts for nested children
|
this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children
|
||||||
let index = this.context.programs.push('') - 1;
|
let index = this.context.programs.length;
|
||||||
child.index = index;
|
child.index = index;
|
||||||
child.name = 'program' + index;
|
child.name = 'program' + index;
|
||||||
this.context.programs[index] = compiler.compile(
|
this.context.programs[index] = compiler.compile(
|
||||||
@@ -1148,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;
|
||||||
|
|
||||||
@@ -1167,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();
|
||||||
|
|
||||||
@@ -1191,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';
|
||||||
}
|
}
|
||||||
@@ -1236,7 +1121,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
} else {
|
} else {
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
@@ -1296,7 +1181,7 @@ function strictLookup(requireTerminal, compiler, parts, startPartIndex, type) {
|
|||||||
compiler.quotedString(parts[len]),
|
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,7 +1,9 @@
|
|||||||
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 = {};
|
||||||
@@ -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;
|
|
||||||
@@ -20,8 +20,7 @@ export function moveHelperToHooks(instance, helperName, keepHelper) {
|
|||||||
if (instance.helpers[helperName]) {
|
if (instance.helpers[helperName]) {
|
||||||
instance.hooks[helperName] = instance.helpers[helperName];
|
instance.hooks[helperName] = instance.helpers[helperName];
|
||||||
if (!keepHelper) {
|
if (!keepHelper) {
|
||||||
// Using delete is slow
|
delete instance.helpers[helperName];
|
||||||
instance.helpers[helperName] = undefined;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
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) {
|
||||||
@@ -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,11 +1,5 @@
|
|||||||
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) {
|
||||||
@@ -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,4 +1,4 @@
|
|||||||
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 */) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
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) {
|
||||||
@@ -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);
|
||||||
@@ -27,7 +27,7 @@ export default function(instance) {
|
|||||||
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,11 +1,5 @@
|
|||||||
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) {
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { extend } from '../utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new object with "null"-prototype to avoid truthy results on prototype properties.
|
||||||
|
* The resulting object can be used with "object[property]" to check if a property exists
|
||||||
|
* @param {...object} sources a varargs parameter of source objects that will be merged
|
||||||
|
* @returns {object}
|
||||||
|
*/
|
||||||
|
export function createNewLookupObject(...sources) {
|
||||||
|
return extend(Object.create(null), ...sources);
|
||||||
|
}
|
||||||
@@ -1,33 +1,34 @@
|
|||||||
import { extend } from '../utils';
|
import { createNewLookupObject } from './create-new-lookup-object';
|
||||||
import logger from '../logger';
|
import logger from '../logger';
|
||||||
|
|
||||||
const loggedProperties = Object.create(null);
|
const loggedProperties = Object.create(null);
|
||||||
|
|
||||||
export function createProtoAccessControl(runtimeOptions) {
|
export function createProtoAccessControl(runtimeOptions) {
|
||||||
// Create an object with "null"-prototype to avoid truthy results on
|
let defaultMethodWhiteList = Object.create(null);
|
||||||
// prototype properties.
|
defaultMethodWhiteList['constructor'] = false;
|
||||||
const propertyWhiteList = Object.create(null);
|
defaultMethodWhiteList['__defineGetter__'] = false;
|
||||||
// eslint-disable-next-line no-proto
|
defaultMethodWhiteList['__defineSetter__'] = false;
|
||||||
propertyWhiteList['__proto__'] = false;
|
defaultMethodWhiteList['__lookupGetter__'] = false;
|
||||||
extend(propertyWhiteList, runtimeOptions.allowedProtoProperties);
|
|
||||||
|
|
||||||
const methodWhiteList = Object.create(null);
|
let defaultPropertyWhiteList = Object.create(null);
|
||||||
methodWhiteList['constructor'] = false;
|
// eslint-disable-next-line no-proto
|
||||||
methodWhiteList['__defineGetter__'] = false;
|
defaultPropertyWhiteList['__proto__'] = false;
|
||||||
methodWhiteList['__defineSetter__'] = false;
|
|
||||||
methodWhiteList['__lookupGetter__'] = false;
|
|
||||||
methodWhiteList['__lookupSetter__'] = false;
|
|
||||||
extend(methodWhiteList, runtimeOptions.allowedProtoMethods);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
properties: {
|
properties: {
|
||||||
whitelist: propertyWhiteList,
|
whitelist: createNewLookupObject(
|
||||||
defaultValue: runtimeOptions.allowProtoPropertiesByDefault
|
defaultPropertyWhiteList,
|
||||||
|
runtimeOptions.allowedProtoProperties
|
||||||
|
),
|
||||||
|
defaultValue: runtimeOptions.allowProtoPropertiesByDefault,
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
whitelist: methodWhiteList,
|
whitelist: createNewLookupObject(
|
||||||
defaultValue: runtimeOptions.allowProtoMethodsByDefault
|
defaultMethodWhiteList,
|
||||||
}
|
runtimeOptions.allowedProtoMethods
|
||||||
|
),
|
||||||
|
defaultValue: runtimeOptions.allowProtoMethodsByDefault,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,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(
|
||||||
@@ -63,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,6 +1,6 @@
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,17 +1,5 @@
|
|||||||
/* global globalThis */
|
|
||||||
export default function (Handlebars) {
|
export default function (Handlebars) {
|
||||||
/* istanbul ignore next */
|
let $Handlebars = globalThis.Handlebars;
|
||||||
// https://mathiasbynens.be/notes/globalthis
|
|
||||||
(function() {
|
|
||||||
if (typeof globalThis === 'object') return;
|
|
||||||
Object.prototype.__defineGetter__('__magic__', function() {
|
|
||||||
return this;
|
|
||||||
});
|
|
||||||
__magic__.globalThis = __magic__; // eslint-disable-line no-undef
|
|
||||||
delete Object.prototype.__magic__;
|
|
||||||
})();
|
|
||||||
|
|
||||||
const $Handlebars = globalThis.Handlebars;
|
|
||||||
|
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
Handlebars.noConflict = function () {
|
Handlebars.noConflict = function () {
|
||||||
|
|||||||
+33
-57
@@ -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,16 +68,20 @@ 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);
|
||||||
|
|
||||||
options.hooks = this.hooks;
|
let extendedOptions = Utils.extend({}, options, {
|
||||||
options.protoAccessControl = this.protoAccessControl;
|
hooks: this.hooks,
|
||||||
|
protoAccessControl: this.protoAccessControl,
|
||||||
|
});
|
||||||
|
|
||||||
let result = env.VM.invokePartial.call(this, partial, context, options);
|
let result = env.VM.invokePartial.call(
|
||||||
|
this,
|
||||||
|
partial,
|
||||||
|
context,
|
||||||
|
extendedOptions
|
||||||
|
);
|
||||||
|
|
||||||
if (result == null && env.compile) {
|
if (result == null && env.compile) {
|
||||||
options.partials[options.name] = env.compile(
|
options.partials[options.name] = env.compile(
|
||||||
@@ -85,7 +89,7 @@ export function template(templateSpec, env) {
|
|||||||
templateSpec.compilerOptions,
|
templateSpec.compilerOptions,
|
||||||
env
|
env
|
||||||
);
|
);
|
||||||
result = options.partials[options.name](context, options);
|
result = options.partials[options.name](context, extendedOptions);
|
||||||
}
|
}
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
if (options.indent) {
|
if (options.indent) {
|
||||||
@@ -114,7 +118,7 @@ export function template(templateSpec, env) {
|
|||||||
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);
|
||||||
@@ -138,7 +142,7 @@ export function template(templateSpec, env) {
|
|||||||
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);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
return result;
|
return depths[i][name];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -194,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);
|
||||||
}
|
}
|
||||||
@@ -245,11 +249,10 @@ 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 = {};
|
let mergedHelpers = Utils.extend({}, env.helpers, options.helpers);
|
||||||
addHelpers(mergedHelpers, env.helpers, container);
|
wrapHelpersToPassLookupProperty(mergedHelpers, container);
|
||||||
addHelpers(mergedHelpers, options.helpers, container);
|
|
||||||
container.helpers = mergedHelpers;
|
container.helpers = mergedHelpers;
|
||||||
|
|
||||||
if (templateSpec.usePartial) {
|
if (templateSpec.usePartial) {
|
||||||
@@ -282,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,25 +334,22 @@ export function wrapProgram(
|
|||||||
export function resolvePartial(partial, context, options) {
|
export function resolvePartial(partial, context, options) {
|
||||||
if (!partial) {
|
if (!partial) {
|
||||||
if (options.name === '@partial-block') {
|
if (options.name === '@partial-block') {
|
||||||
partial = lookupOwnProperty(options.data, 'partial-block');
|
partial = options.data['partial-block'];
|
||||||
} else {
|
} else {
|
||||||
partial = lookupOwnProperty(options.partials, options.name);
|
partial = options.partials[options.name];
|
||||||
}
|
}
|
||||||
} else if (!partial.call && !options.name) {
|
} else if (!partial.call && !options.name) {
|
||||||
// This is a dynamic partial that returned a string
|
// This is a dynamic partial that returned a string
|
||||||
options.name = partial;
|
options.name = partial;
|
||||||
partial = lookupOwnProperty(options.partials, partial);
|
partial = options.partials[partial];
|
||||||
}
|
}
|
||||||
return partial;
|
return partial;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function invokePartial(partial, context, options) {
|
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 = lookupOwnProperty(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) {
|
||||||
@@ -394,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);
|
||||||
}
|
}
|
||||||
@@ -404,12 +388,6 @@ export function noop() {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
function lookupOwnProperty(obj, name) {
|
|
||||||
if (obj && Object.prototype.hasOwnProperty.call(obj, name)) {
|
|
||||||
return obj[name];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function initData(context, data) {
|
function initData(context, data) {
|
||||||
if (!data || !('root' in data)) {
|
if (!data || !('root' in data)) {
|
||||||
data = data ? createFrame(data) : {};
|
data = data ? createFrame(data) : {};
|
||||||
@@ -435,18 +413,16 @@ function executeDecorators(fn, prog, container, depths, data, blockParams) {
|
|||||||
return prog;
|
return prog;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addHelpers(mergedHelpers, helpers, container) {
|
function wrapHelpersToPassLookupProperty(mergedHelpers, container) {
|
||||||
if (!helpers) return;
|
Object.keys(mergedHelpers).forEach((helperName) => {
|
||||||
Object.keys(helpers).forEach(helperName => {
|
let helper = mergedHelpers[helperName];
|
||||||
let helper = helpers[helperName];
|
|
||||||
mergedHelpers[helperName] = passLookupPropertyOption(helper, container);
|
mergedHelpers[helperName] = passLookupPropertyOption(helper, 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) => {
|
||||||
options.lookupProperty = lookupProperty;
|
return Utils.extend({ lookupProperty }, options);
|
||||||
return options;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-52
@@ -5,7 +5,7 @@ const escape = {
|
|||||||
'"': '"',
|
'"': '"',
|
||||||
"'": ''',
|
"'": ''',
|
||||||
'`': '`',
|
'`': '`',
|
||||||
'=': '='
|
'=': '=',
|
||||||
};
|
};
|
||||||
|
|
||||||
const badChars = /[&<>"'`=]/g,
|
const badChars = /[&<>"'`=]/g,
|
||||||
@@ -30,23 +30,10 @@ 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 =
|
||||||
@@ -105,39 +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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Coerce an untrusted depth value to a safe non-negative integer.
|
|
||||||
* Returns `0` for any value that is not a finite, non-negative number.
|
|
||||||
*
|
|
||||||
* @param {unknown} depth - The depth value to sanitize.
|
|
||||||
* @returns {number} A non-negative integer.
|
|
||||||
*/
|
|
||||||
export function sanitizeDepth(depth) {
|
|
||||||
let number = Number(depth);
|
|
||||||
if (!Number.isFinite(number) || number < 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return Math.floor(number);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a sanitized copy of a PathExpression AST node's parts array.
|
|
||||||
* Coerces each element to a string, or returns an empty array if parts
|
|
||||||
* is not an array.
|
|
||||||
*
|
|
||||||
* @param {unknown} parts - The parts value to sanitize.
|
|
||||||
* @returns {string[]} A safe string array.
|
|
||||||
*/
|
|
||||||
export function sanitizeParts(parts) {
|
|
||||||
return Array.isArray(parts) ? parts.map(String) : [];
|
|
||||||
}
|
|
||||||
|
|||||||
+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;
|
||||||
|
|
||||||
|
|||||||
+20
-52
@@ -57,7 +57,7 @@ function loadStrings(opts, callback) {
|
|||||||
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);
|
||||||
}
|
}
|
||||||
@@ -75,7 +75,10 @@ function loadFiles(opts, callback) {
|
|||||||
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) {
|
||||||
@@ -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();
|
||||||
@@ -196,24 +199,18 @@ module.exports.cli = function(opts) {
|
|||||||
|
|
||||||
const objectName = opts.partial ? 'Handlebars.partials' : 'templates';
|
const objectName = opts.partial ? 'Handlebars.partials' : 'templates';
|
||||||
|
|
||||||
if (opts.namespace && !isValidNamespace(opts.namespace)) {
|
|
||||||
throw new Handlebars.Exception('Invalid namespace format');
|
|
||||||
}
|
|
||||||
|
|
||||||
let output = new SourceNode();
|
let output = new SourceNode();
|
||||||
if (!opts.simple) {
|
if (!opts.simple) {
|
||||||
if (opts.amd) {
|
if (opts.amd) {
|
||||||
const runtimeModulePath =
|
|
||||||
(opts.handlebarPath || '') + 'handlebars.runtime';
|
|
||||||
output.add(
|
output.add(
|
||||||
'define([' +
|
"define(['" +
|
||||||
quoteForJavaScript(runtimeModulePath) +
|
opts.handlebarPath +
|
||||||
'], function(Handlebars) {\n Handlebars = Handlebars["default"];'
|
'handlebars.runtime\'], function(Handlebars) {\n Handlebars = Handlebars["default"];'
|
||||||
);
|
);
|
||||||
} else if (opts.commonjs) {
|
} else if (opts.commonjs) {
|
||||||
output.add(
|
output.add('var Handlebars = require("' + opts.commonjs + '");');
|
||||||
'var Handlebars = require(' + quoteForJavaScript(opts.commonjs) + ');'
|
} else if (opts.esm) {
|
||||||
);
|
output.add(`import handlebars from "${opts.esm}";`);
|
||||||
} else {
|
} else {
|
||||||
output.add('(function() {\n');
|
output.add('(function() {\n');
|
||||||
}
|
}
|
||||||
@@ -230,7 +227,7 @@ module.exports.cli = function(opts) {
|
|||||||
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) {
|
||||||
@@ -263,11 +260,11 @@ module.exports.cli = function(opts) {
|
|||||||
}
|
}
|
||||||
output.add([
|
output.add([
|
||||||
objectName,
|
objectName,
|
||||||
'[',
|
"['",
|
||||||
quoteForJavaScript(template.name),
|
template.name,
|
||||||
'] = template(',
|
"'] = template(",
|
||||||
precompiled,
|
precompiled,
|
||||||
');\n'
|
');\n',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -285,9 +282,7 @@ module.exports.cli = function(opts) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (opts.map) {
|
if (opts.map) {
|
||||||
output.add(
|
output.add('\n//# sourceMappingURL=' + opts.map + '\n');
|
||||||
'\n//# sourceMappingURL=' + sanitizeSourceMapComment(opts.map) + '\n'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
output = output.toStringWithSourceMap();
|
output = output.toStringWithSourceMap();
|
||||||
@@ -317,33 +312,6 @@ function arrayCast(value) {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Safely quotes a value for embedding in generated JavaScript strings
|
|
||||||
*
|
|
||||||
* Uses JSON.stringify which handles all special characters.
|
|
||||||
*/
|
|
||||||
function quoteForJavaScript(value) {
|
|
||||||
return JSON.stringify(String(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates that a namespace is a legitimate dotted JavaScript identifier
|
|
||||||
* (e.g. "App.templates") to prevent arbitrary code injection
|
|
||||||
*/
|
|
||||||
function isValidNamespace(namespace) {
|
|
||||||
return /^[A-Za-z_$][A-Za-z0-9_$]*(\.[A-Za-z_$][A-Za-z0-9_$]*)*$/.test(
|
|
||||||
namespace
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Strips line terminators from source map URLs to prevent injection of new
|
|
||||||
* JavaScript lines via the sourceMappingURL comment
|
|
||||||
*/
|
|
||||||
function sanitizeSourceMapComment(value) {
|
|
||||||
return String(value).replace(/[\r\n\u2028\u2029]/g, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run uglify to minify the compiled template, if uglify exists in the dependencies.
|
* Run uglify to minify the compiled template, if uglify exists in the dependencies.
|
||||||
*
|
*
|
||||||
@@ -372,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
+13323
-6355
File diff suppressed because it is too large
Load Diff
+31
-26
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "handlebars",
|
"name": "handlebars",
|
||||||
"barename": "handlebars",
|
"version": "5.0.0-alpha.1",
|
||||||
"version": "4.7.9",
|
|
||||||
"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://handlebarsjs.com/",
|
"homepage": "https://handlebarsjs.com/",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
@@ -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.44.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",
|
||||||
@@ -40,18 +40,16 @@
|
|||||||
"concurrently": "^5.0.0",
|
"concurrently": "^5.0.0",
|
||||||
"dirty-chai": "^2.0.1",
|
"dirty-chai": "^2.0.1",
|
||||||
"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.5.3",
|
"grunt": "^1.0.4",
|
||||||
"grunt-babel": "^5.0.0",
|
"grunt-babel": "^5.0.0",
|
||||||
"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",
|
||||||
@@ -59,16 +57,16 @@
|
|||||||
"grunt-shell": "^4.0.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"
|
||||||
@@ -77,29 +75,30 @@
|
|||||||
"types": "types/index.d.ts",
|
"types": "types/index.d.ts",
|
||||||
"browser": "./dist/cjs/handlebars.js",
|
"browser": "./dist/cjs/handlebars.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
"handlebars": "bin/handlebars"
|
"handlebars": "bin/handlebars.js"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "grunt build",
|
"build": "grunt build",
|
||||||
"release": "npm run build && grunt release",
|
"release": "grunt release",
|
||||||
"format": "prettier --write '**/*.js' && eslint --fix .",
|
"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": "tsc --noEmit --project 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
|
||||||
@@ -108,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",
|
||||||
@@ -116,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',
|
||||||
};
|
};
|
||||||
|
|||||||
+287
-296
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>
|
|
||||||
+6
-253
@@ -24,7 +24,7 @@ describe('ast', function() {
|
|||||||
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,7 +40,7 @@ describe('ast', function() {
|
|||||||
AST.helpers.helperExpression({
|
AST.helpers.helperExpression({
|
||||||
type: 'MustacheStatement',
|
type: 'MustacheStatement',
|
||||||
params: [],
|
params: [],
|
||||||
hash: {}
|
hash: {},
|
||||||
}),
|
}),
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
@@ -50,7 +50,7 @@ describe('ast', function() {
|
|||||||
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,7 +66,7 @@ describe('ast', function() {
|
|||||||
AST.helpers.helperExpression({
|
AST.helpers.helperExpression({
|
||||||
type: 'BlockStatement',
|
type: 'BlockStatement',
|
||||||
params: [],
|
params: [],
|
||||||
hash: {}
|
hash: {},
|
||||||
}),
|
}),
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
@@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
+43
-65
@@ -6,9 +6,7 @@ beforeEach(function() {
|
|||||||
|
|
||||||
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 () {
|
||||||
@@ -37,16 +35,14 @@ describe('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 () {
|
||||||
@@ -63,7 +59,7 @@ describe('basic context', 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!');
|
||||||
@@ -92,7 +88,7 @@ describe('basic context', function() {
|
|||||||
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,7 +96,7 @@ 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!');
|
||||||
@@ -110,13 +106,11 @@ describe('basic context', 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 } })
|
||||||
@@ -128,13 +122,11 @@ 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');
|
||||||
|
|
||||||
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');
|
||||||
|
|
||||||
@@ -164,7 +156,7 @@ describe('basic context', function() {
|
|||||||
' ' +
|
' ' +
|
||||||
typeof options
|
typeof options
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('true true object');
|
.toCompileTo('true true object');
|
||||||
|
|
||||||
@@ -172,7 +164,7 @@ describe('basic context', function() {
|
|||||||
.withInput({
|
.withInput({
|
||||||
undefined: function () {
|
undefined: function () {
|
||||||
return 'undefined!';
|
return 'undefined!';
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('undefined!');
|
.toCompileTo('undefined!');
|
||||||
|
|
||||||
@@ -180,7 +172,7 @@ describe('basic context', function() {
|
|||||||
.withInput({
|
.withInput({
|
||||||
null: function () {
|
null: function () {
|
||||||
return 'null!';
|
return 'null!';
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('null!');
|
.toCompileTo('null!');
|
||||||
});
|
});
|
||||||
@@ -243,7 +235,7 @@ describe('basic context', function() {
|
|||||||
.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("&'\\<>");
|
||||||
@@ -254,7 +246,7 @@ describe('basic context', function() {
|
|||||||
.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');
|
||||||
@@ -264,7 +256,7 @@ describe('basic context', function() {
|
|||||||
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');
|
||||||
@@ -276,7 +268,7 @@ describe('basic context', function() {
|
|||||||
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');
|
||||||
@@ -288,9 +280,9 @@ describe('basic context', function() {
|
|||||||
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');
|
||||||
@@ -302,7 +294,7 @@ describe('basic context', function() {
|
|||||||
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');
|
||||||
@@ -313,7 +305,7 @@ describe('basic context', function() {
|
|||||||
.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');
|
||||||
@@ -327,7 +319,7 @@ describe('basic context', function() {
|
|||||||
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');
|
||||||
@@ -338,7 +330,7 @@ describe('basic context', function() {
|
|||||||
.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');
|
||||||
@@ -350,8 +342,8 @@ describe('basic context', function() {
|
|||||||
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');
|
||||||
@@ -365,7 +357,7 @@ describe('basic context', function() {
|
|||||||
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');
|
||||||
@@ -452,9 +444,7 @@ describe('basic context', function() {
|
|||||||
.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 () {
|
||||||
@@ -465,7 +455,7 @@ 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');
|
||||||
@@ -477,9 +467,7 @@ describe('basic context', function() {
|
|||||||
'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' } })
|
||||||
@@ -490,7 +478,7 @@ describe('basic context', 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,7 +489,7 @@ 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')
|
||||||
@@ -519,7 +507,7 @@ describe('basic context', function() {
|
|||||||
foo: function (value) {
|
foo: function (value) {
|
||||||
return value;
|
return value;
|
||||||
},
|
},
|
||||||
this: 'bar'
|
this: 'bar',
|
||||||
})
|
})
|
||||||
.toCompileTo('bar');
|
.toCompileTo('bar');
|
||||||
|
|
||||||
@@ -528,7 +516,7 @@ describe('basic context', function() {
|
|||||||
foo: function (value) {
|
foo: function (value) {
|
||||||
return value;
|
return value;
|
||||||
},
|
},
|
||||||
text: { this: 'bar' }
|
text: { this: 'bar' },
|
||||||
})
|
})
|
||||||
.toCompileTo('bar');
|
.toCompileTo('bar');
|
||||||
});
|
});
|
||||||
@@ -536,13 +524,11 @@ describe('basic context', function() {
|
|||||||
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');
|
||||||
});
|
});
|
||||||
@@ -550,21 +536,17 @@ describe('basic context', function() {
|
|||||||
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');
|
||||||
});
|
});
|
||||||
@@ -572,13 +554,9 @@ describe('basic context', function() {
|
|||||||
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 () {
|
||||||
@@ -586,7 +564,7 @@ describe('basic context', function() {
|
|||||||
.withInput({
|
.withInput({
|
||||||
false: function () {
|
false: function () {
|
||||||
return 'bar';
|
return 'bar';
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.withHelper('foo', function (arg) {
|
.withHelper('foo', function (arg) {
|
||||||
return arg;
|
return arg;
|
||||||
|
|||||||
+24
-24
@@ -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,7 +17,7 @@ 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!');
|
||||||
@@ -31,9 +31,9 @@ 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');
|
||||||
@@ -47,9 +47,9 @@ 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!');
|
||||||
@@ -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,7 +73,7 @@ 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!');
|
||||||
@@ -86,8 +86,8 @@ describe('blocks', function() {
|
|||||||
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'
|
||||||
@@ -104,8 +104,8 @@ describe('blocks', function() {
|
|||||||
goodbyes: [
|
goodbyes: [
|
||||||
{ text: 'goodbye' },
|
{ text: 'goodbye' },
|
||||||
{ text: 'Goodbye' },
|
{ text: 'Goodbye' },
|
||||||
{ text: 'GOODBYE' }
|
{ text: 'GOODBYE' },
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
.toCompileTo('AlanAlanAlanAlanAlanAlan');
|
.toCompileTo('AlanAlanAlanAlanAlanAlan');
|
||||||
});
|
});
|
||||||
@@ -122,7 +122,7 @@ describe('blocks', function() {
|
|||||||
)
|
)
|
||||||
.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!');
|
||||||
});
|
});
|
||||||
@@ -135,8 +135,8 @@ 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');
|
||||||
});
|
});
|
||||||
@@ -202,7 +202,7 @@ describe('blocks', 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');
|
||||||
});
|
});
|
||||||
@@ -252,7 +252,7 @@ describe('blocks', function() {
|
|||||||
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.');
|
||||||
});
|
});
|
||||||
@@ -274,7 +274,7 @@ describe('blocks', function() {
|
|||||||
)
|
)
|
||||||
.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!');
|
||||||
@@ -286,7 +286,7 @@ describe('blocks', function() {
|
|||||||
)
|
)
|
||||||
.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 ');
|
||||||
@@ -345,7 +345,7 @@ describe('blocks', function() {
|
|||||||
},
|
},
|
||||||
nested: function (fn, props, container, options) {
|
nested: function (fn, props, container, options) {
|
||||||
props.nested = options.fn();
|
props.nested = options.fn();
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('success');
|
.toCompileTo('success');
|
||||||
});
|
});
|
||||||
@@ -421,7 +421,7 @@ describe('blocks', function() {
|
|||||||
|
|
||||||
handlebarsEnv.registerDecorator({
|
handlebarsEnv.registerDecorator({
|
||||||
foo: function () {},
|
foo: function () {},
|
||||||
bar: function() {}
|
bar: function () {},
|
||||||
});
|
});
|
||||||
|
|
||||||
equals(!!handlebarsEnv.decorators.foo, true);
|
equals(!!handlebarsEnv.decorators.foo, true);
|
||||||
@@ -442,7 +442,7 @@ describe('blocks', function() {
|
|||||||
},
|
},
|
||||||
testHelper: function () {
|
testHelper: function () {
|
||||||
return 'found it!';
|
return 'found it!';
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
|||||||
+57
-55
@@ -6,7 +6,7 @@ describe('builtin helpers', function() {
|
|||||||
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,7 +63,7 @@ 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!');
|
||||||
@@ -77,7 +77,7 @@ describe('builtin helpers', function() {
|
|||||||
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'
|
||||||
@@ -89,7 +89,7 @@ describe('builtin helpers', function() {
|
|||||||
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'
|
||||||
@@ -101,7 +101,7 @@ describe('builtin helpers', function() {
|
|||||||
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'
|
||||||
@@ -113,7 +113,7 @@ describe('builtin helpers', function() {
|
|||||||
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'
|
||||||
@@ -127,7 +127,7 @@ describe('builtin helpers', function() {
|
|||||||
)
|
)
|
||||||
.withInput({
|
.withInput({
|
||||||
foo: { goodbye: true },
|
foo: { goodbye: true },
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.toCompileTo('GOODBYE cruel world!');
|
.toCompileTo('GOODBYE cruel world!');
|
||||||
});
|
});
|
||||||
@@ -139,8 +139,8 @@ describe('builtin helpers', function() {
|
|||||||
.withInput({
|
.withInput({
|
||||||
person: {
|
person: {
|
||||||
first: 'Alan',
|
first: 'Alan',
|
||||||
last: 'Johnson'
|
last: 'Johnson',
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('Alan Johnson');
|
.toCompileTo('Alan Johnson');
|
||||||
});
|
});
|
||||||
@@ -151,9 +151,9 @@ describe('builtin helpers', function() {
|
|||||||
person: function () {
|
person: function () {
|
||||||
return {
|
return {
|
||||||
first: 'Alan',
|
first: 'Alan',
|
||||||
last: 'Johnson'
|
last: 'Johnson',
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('Alan Johnson');
|
.toCompileTo('Alan Johnson');
|
||||||
});
|
});
|
||||||
@@ -169,8 +169,8 @@ describe('builtin helpers', function() {
|
|||||||
.withInput({
|
.withInput({
|
||||||
person: {
|
person: {
|
||||||
first: 'Alan',
|
first: 'Alan',
|
||||||
last: 'Johnson'
|
last: 'Johnson',
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('Alan Johnson');
|
.toCompileTo('Alan Johnson');
|
||||||
});
|
});
|
||||||
@@ -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,7 +210,7 @@ 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!');
|
||||||
@@ -222,9 +222,9 @@ describe('builtin helpers', function() {
|
|||||||
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 })
|
||||||
@@ -272,7 +272,7 @@ describe('builtin helpers', function() {
|
|||||||
expectTemplate(string)
|
expectTemplate(string)
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbyes: {},
|
goodbyes: {},
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.toCompileTo('cruel world!');
|
.toCompileTo('cruel world!');
|
||||||
});
|
});
|
||||||
@@ -285,9 +285,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('0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!');
|
.toCompileTo('0. goodbye! 1. Goodbye! 2. GOODBYE! 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(
|
||||||
@@ -317,7 +317,7 @@ describe('builtin helpers', function() {
|
|||||||
)
|
)
|
||||||
.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!'
|
||||||
@@ -341,9 +341,9 @@ 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!');
|
||||||
@@ -357,9 +357,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('goodbye! cruel world!');
|
.toCompileTo('goodbye! cruel world!');
|
||||||
@@ -373,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(
|
||||||
@@ -389,7 +389,7 @@ describe('builtin helpers', function() {
|
|||||||
)
|
)
|
||||||
.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!');
|
||||||
@@ -403,9 +403,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('GOODBYE! cruel world!');
|
.toCompileTo('GOODBYE! cruel world!');
|
||||||
@@ -417,7 +417,7 @@ describe('builtin helpers', function() {
|
|||||||
)
|
)
|
||||||
.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!');
|
||||||
@@ -431,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(
|
||||||
@@ -450,10 +450,10 @@ describe('builtin helpers', 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'
|
||||||
@@ -463,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'
|
||||||
@@ -479,9 +479,9 @@ 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!');
|
||||||
@@ -495,8 +495,8 @@ 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!');
|
||||||
});
|
});
|
||||||
@@ -535,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'
|
||||||
@@ -547,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'
|
||||||
@@ -705,7 +705,11 @@ describe('builtin helpers', function() {
|
|||||||
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;
|
||||||
};
|
};
|
||||||
@@ -742,9 +746,7 @@ describe('builtin helpers', function() {
|
|||||||
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 */
|
||||||
|
|||||||
+21
-113
@@ -122,125 +122,33 @@ describe('compiler', function() {
|
|||||||
equal(
|
equal(
|
||||||
Handlebars.compile({
|
Handlebars.compile({
|
||||||
type: 'Program',
|
type: 'Program',
|
||||||
body: [{ type: 'ContentStatement', value: 'Hello' }]
|
body: [{ type: 'ContentStatement', value: 'Hello' }],
|
||||||
})(),
|
})(),
|
||||||
'Hello'
|
'Hello'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
function createPathExpressionAST(depth, parts) {
|
|
||||||
return {
|
|
||||||
type: 'Program',
|
|
||||||
body: [
|
|
||||||
{
|
|
||||||
type: 'MustacheStatement',
|
|
||||||
escaped: true,
|
|
||||||
strip: { open: false, close: false },
|
|
||||||
path: {
|
|
||||||
type: 'PathExpression',
|
|
||||||
data: false,
|
|
||||||
depth: depth,
|
|
||||||
parts: parts,
|
|
||||||
original: 'this'
|
|
||||||
},
|
|
||||||
params: []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should safely handle AST with non-integer PathExpression depth', function() {
|
|
||||||
// depth '0' is coerced to 0 via Number(), compiles safely
|
|
||||||
var result = Handlebars.compile(createPathExpressionAST('0', ['this']))();
|
|
||||||
expect(result).to.be.a('string');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should safely handle AST with negative PathExpression depth', function() {
|
|
||||||
// Negative depth is clamped to 0
|
|
||||||
var result = Handlebars.compile(createPathExpressionAST(-1, ['this']))();
|
|
||||||
expect(result).to.be.a('string');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should safely handle AST with fractional PathExpression depth', function() {
|
|
||||||
// Fractional depth is floored to an integer
|
|
||||||
var result = Handlebars.compile(createPathExpressionAST(0.5, ['this']))();
|
|
||||||
expect(result).to.be.a('string');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should safely handle AST with non-array PathExpression parts', function() {
|
|
||||||
// Non-array parts are coerced to empty array, compiles safely
|
|
||||||
var result = Handlebars.compile(createPathExpressionAST(0, 'this'))();
|
|
||||||
expect(result).to.be.a('string');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should safely handle AST with non-string PathExpression part', function() {
|
|
||||||
// Non-string parts are coerced to strings via String()
|
|
||||||
var result = Handlebars.compile(createPathExpressionAST(0, [1]))();
|
|
||||||
expect(result).to.be.a('string');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should safely handle AST with non-boolean BooleanLiteral value type', function() {
|
|
||||||
// The compiler coerces BooleanLiteral.value via === true before
|
|
||||||
// emitting a pushLiteral opcode, so a non-boolean value like the
|
|
||||||
// string 'true' becomes the literal 'false'.
|
|
||||||
var loc = {
|
|
||||||
source: null,
|
|
||||||
start: { line: 1, column: 0 },
|
|
||||||
end: { line: 1, column: 10 }
|
|
||||||
};
|
|
||||||
var result = Handlebars.compile({
|
|
||||||
type: 'Program',
|
|
||||||
body: [
|
|
||||||
{
|
|
||||||
type: 'MustacheStatement',
|
|
||||||
escaped: true,
|
|
||||||
strip: { open: false, close: false },
|
|
||||||
loc: loc,
|
|
||||||
path: {
|
|
||||||
type: 'BooleanLiteral',
|
|
||||||
value: 'true',
|
|
||||||
original: true,
|
|
||||||
loc: loc
|
|
||||||
},
|
|
||||||
params: []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})();
|
|
||||||
// 'true' !== true, so the compiler emits pushLiteral('false').
|
|
||||||
// Handlebars does not render falsy values, so the output is empty.
|
|
||||||
expect(result).to.equal('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should ignore loc metadata in AST nodes', function() {
|
|
||||||
equal(
|
|
||||||
Handlebars.compile({
|
|
||||||
type: 'Program',
|
|
||||||
meta: null,
|
|
||||||
loc: { source: 'fake', start: { line: 1, column: 0 } },
|
|
||||||
body: [{ type: 'ContentStatement', value: 'Hello' }]
|
|
||||||
})(),
|
|
||||||
'Hello'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should accept AST with valid NumberLiteral values', function() {
|
|
||||||
equal(
|
|
||||||
Handlebars.compile(Handlebars.parse('{{lookup this 1}}'))(['a', 'b']),
|
|
||||||
'b'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should accept AST with valid BooleanLiteral values', function() {
|
|
||||||
equal(
|
|
||||||
Handlebars.compile(Handlebars.parse('{{#if true}}ok{{/if}}'))({}),
|
|
||||||
'ok'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can pass through an empty string', function () {
|
it('can pass through an empty string', function () {
|
||||||
equal(Handlebars.compile('')(), '');
|
equal(Handlebars.compile('')(), '');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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 () {
|
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)();
|
||||||
@@ -267,14 +175,14 @@ describe('compiler', 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]'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -283,7 +191,7 @@ describe('compiler', function() {
|
|||||||
/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
|
||||||
|
|||||||
+9
-9
@@ -93,8 +93,8 @@ describe('data', function() {
|
|||||||
data: {
|
data: {
|
||||||
hello: function () {
|
hello: function () {
|
||||||
return 'hello';
|
return 'hello';
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('hello');
|
.toCompileTo('hello');
|
||||||
});
|
});
|
||||||
@@ -105,8 +105,8 @@ describe('data', function() {
|
|||||||
data: {
|
data: {
|
||||||
hello: function (arg) {
|
hello: function (arg) {
|
||||||
return arg;
|
return arg;
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('hello');
|
.toCompileTo('hello');
|
||||||
});
|
});
|
||||||
@@ -159,7 +159,7 @@ describe('data', function() {
|
|||||||
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);
|
||||||
@@ -217,7 +217,7 @@ describe('data', function() {
|
|||||||
})
|
})
|
||||||
.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?');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -232,7 +232,7 @@ describe('data', function() {
|
|||||||
})
|
})
|
||||||
.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?');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -269,8 +269,8 @@ describe('data', function() {
|
|||||||
})
|
})
|
||||||
.withRuntimeOptions({
|
.withRuntimeOptions({
|
||||||
data: {
|
data: {
|
||||||
depth: 0
|
depth: 0,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('2 1 0');
|
.toCompileTo('2 1 0');
|
||||||
});
|
});
|
||||||
|
|||||||
Vendored
+1
-1
@@ -32,7 +32,7 @@ global.CompilerContext = {
|
|||||||
},
|
},
|
||||||
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
+1
-1
@@ -17,7 +17,7 @@ global.CompilerContext = {
|
|||||||
},
|
},
|
||||||
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
+6
-3
@@ -22,7 +22,7 @@ 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');
|
||||||
|
|
||||||
@@ -31,7 +31,10 @@ global.CompilerContext = {
|
|||||||
|
|
||||||
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,
|
||||||
@@ -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,6 +1,6 @@
|
|||||||
define(["handlebars.runtime"], function(Handlebars) {
|
define(['handlebars.runtime'], function(Handlebars) {
|
||||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||||
return templates["bom"] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
return templates['bom'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||||
return "a";
|
return "a";
|
||||||
},"useData":true});
|
},"useData":true});
|
||||||
});
|
});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
define(["handlebars.runtime"], function(Handlebars) {
|
define(['handlebars.runtime'], function(Handlebars) {
|
||||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||||
return templates["empty"] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
return templates['empty'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||||
return "";
|
return "";
|
||||||
},"useData":true});
|
},"useData":true});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
define(["handlebars.runtime"], function(Handlebars) {
|
define(['handlebars.runtime'], function(Handlebars) {
|
||||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = CustomNamespace.templates = CustomNamespace.templates || {};
|
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = CustomNamespace.templates = CustomNamespace.templates || {};
|
||||||
return templates["empty"] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
return templates['empty'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||||
return "";
|
return "";
|
||||||
},"useData":true});
|
},"useData":true});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
(function() {
|
(function() {
|
||||||
var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||||
templates["empty"] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
templates['empty'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||||
return "";
|
return "";
|
||||||
},"useData":true});
|
},"useData":true});
|
||||||
})();
|
})();
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
define(["handlebars.runtime"], function(Handlebars) {
|
define(['handlebars.runtime'], function(Handlebars) {
|
||||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||||
templates["firstTemplate"] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
templates['firstTemplate'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||||
return "<div>1</div>";
|
return "<div>1</div>";
|
||||||
},"useData":true});
|
},"useData":true});
|
||||||
templates["secondTemplate"] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
templates['secondTemplate'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||||
return "<div>2</div>";
|
return "<div>2</div>";
|
||||||
},"useData":true});
|
},"useData":true});
|
||||||
return templates;
|
return templates;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
define(["handlebars.runtime"], function(Handlebars) {
|
define(['handlebars.runtime'], function(Handlebars) {
|
||||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||||
return templates["artifacts/partial.template"] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
return templates['artifacts/partial.template'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||||
return "<div>Test Partial</div>";
|
return "<div>Test Partial</div>";
|
||||||
},"useData":true});
|
},"useData":true});
|
||||||
});
|
});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
define(["some-path/handlebars.runtime"], function(Handlebars) {
|
define(['some-path/handlebars.runtime'], function(Handlebars) {
|
||||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||||
return templates["empty"] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
return templates['empty'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||||
return "";
|
return "";
|
||||||
},"useData":true});
|
},"useData":true});
|
||||||
});
|
});
|
||||||
@@ -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]
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
define(["handlebars.runtime"], function(Handlebars) {
|
define(['handlebars.runtime'], function(Handlebars) {
|
||||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = someNameSpace = someNameSpace || {};
|
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = someNameSpace = someNameSpace || {};
|
||||||
templates["empty"] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
templates['empty'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||||
return "";
|
return "";
|
||||||
},"useData":true});
|
},"useData":true});
|
||||||
templates["empty"] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
templates['empty'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||||
return "";
|
return "";
|
||||||
},"useData":true});
|
},"useData":true});
|
||||||
return templates;
|
return templates;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
define(["handlebars.runtime"], function(Handlebars) {
|
define(['handlebars.runtime'], function(Handlebars) {
|
||||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||||
return templates["non.default.extension"] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
return templates['non.default.extension'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||||
return "<div>This is a test</div>";
|
return "<div>This is a test</div>";
|
||||||
},"useData":true});
|
},"useData":true});
|
||||||
});
|
});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
define(["handlebars.runtime"], function(Handlebars) {
|
define(['handlebars.runtime'], function(Handlebars) {
|
||||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||||
return templates["known.helpers"] = template({"0":function(container,depth0,helpers,partials,data) {
|
return templates['known.helpers'] = template({"1":function(container,depth0,helpers,partials,data) {
|
||||||
var stack1, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
var stack1, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||||
return parent[propertyName];
|
return parent[propertyName];
|
||||||
@@ -8,8 +8,8 @@ return parent[propertyName];
|
|||||||
return undefined
|
return undefined
|
||||||
};
|
};
|
||||||
return " <div>Some known helper</div>\n"
|
return " <div>Some known helper</div>\n"
|
||||||
+ ((stack1 = lookupProperty(helpers,"anotherHelper").call(depth0 != null ? depth0 : (container.nullContext || {}),true,{"name":"anotherHelper","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":3,"column":4},"end":{"line":5,"column":22}}})) != null ? stack1 : "");
|
+ ((stack1 = lookupProperty(helpers,"anotherHelper").call(depth0 != null ? depth0 : (container.nullContext || {}),true,{"name":"anotherHelper","hash":{},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":3,"column":4},"end":{"line":5,"column":22}}})) != null ? stack1 : "");
|
||||||
},"1":function(container,depth0,helpers,partials,data) {
|
},"2":function(container,depth0,helpers,partials,data) {
|
||||||
return " <div>Another known helper</div>\n";
|
return " <div>Another known helper</div>\n";
|
||||||
},"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
},"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||||
var stack1, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
var stack1, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||||
@@ -18,7 +18,7 @@ return parent[propertyName];
|
|||||||
}
|
}
|
||||||
return undefined
|
return undefined
|
||||||
};
|
};
|
||||||
return ((stack1 = lookupProperty(helpers,"someHelper").call(depth0 != null ? depth0 : (container.nullContext || {}),true,{"name":"someHelper","hash":{},"fn":container.program(0, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":1,"column":0},"end":{"line":6,"column":15}}})) != null ? stack1 : "");
|
return ((stack1 = lookupProperty(helpers,"someHelper").call(depth0 != null ? depth0 : (container.nullContext || {}),true,{"name":"someHelper","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":1,"column":0},"end":{"line":6,"column":15}}})) != null ? stack1 : "");
|
||||||
},"useData":true});
|
},"useData":true});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
define(["handlebars.runtime"], function(Handlebars) {
|
define(['handlebars.runtime'], function(Handlebars) {
|
||||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||||
return Handlebars.partials["partial.template"] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
return Handlebars.partials['partial.template'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||||
return "<div>Test Partial</div>";
|
return "<div>Test Partial</div>";
|
||||||
},"useData":true});
|
},"useData":true});
|
||||||
});
|
});
|
||||||
+36
-36
@@ -3,7 +3,7 @@ describe('helpers', function() {
|
|||||||
expectTemplate('{{#goodbyes}}{{{link ../prefix}}}{{/goodbyes}}')
|
expectTemplate('{{#goodbyes}}{{{link ../prefix}}}{{/goodbyes}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
prefix: '/root',
|
prefix: '/root',
|
||||||
goodbyes: [{ text: 'Goodbye', url: 'goodbye' }]
|
goodbyes: [{ text: 'Goodbye', url: 'goodbye' }],
|
||||||
})
|
})
|
||||||
.withHelper('link', function (prefix) {
|
.withHelper('link', function (prefix) {
|
||||||
return (
|
return (
|
||||||
@@ -111,7 +111,7 @@ describe('helpers', function() {
|
|||||||
)
|
)
|
||||||
.withInput({
|
.withInput({
|
||||||
prefix: '/root',
|
prefix: '/root',
|
||||||
goodbyes: [{ text: 'Goodbye', url: 'goodbye' }]
|
goodbyes: [{ text: 'Goodbye', url: 'goodbye' }],
|
||||||
})
|
})
|
||||||
.withHelper('link', function (prefix, options) {
|
.withHelper('link', function (prefix, options) {
|
||||||
return (
|
return (
|
||||||
@@ -133,7 +133,7 @@ describe('helpers', function() {
|
|||||||
)
|
)
|
||||||
.withInput({
|
.withInput({
|
||||||
prefix: '/root',
|
prefix: '/root',
|
||||||
goodbyes: [{ text: 'Goodbye', url: 'goodbye' }]
|
goodbyes: [{ text: 'Goodbye', url: 'goodbye' }],
|
||||||
})
|
})
|
||||||
.withHelper('link', function (prefix, options) {
|
.withHelper('link', function (prefix, options) {
|
||||||
return (
|
return (
|
||||||
@@ -152,13 +152,13 @@ describe('helpers', function() {
|
|||||||
it('helper returning undefined value', function () {
|
it('helper returning undefined value', function () {
|
||||||
expectTemplate(' {{nothere}}')
|
expectTemplate(' {{nothere}}')
|
||||||
.withHelpers({
|
.withHelpers({
|
||||||
nothere: function() {}
|
nothere: function () {},
|
||||||
})
|
})
|
||||||
.toCompileTo(' ');
|
.toCompileTo(' ');
|
||||||
|
|
||||||
expectTemplate(' {{#nothere}}{{/nothere}}')
|
expectTemplate(' {{#nothere}}{{/nothere}}')
|
||||||
.withHelpers({
|
.withHelpers({
|
||||||
nothere: function() {}
|
nothere: function () {},
|
||||||
})
|
})
|
||||||
.toCompileTo(' ');
|
.toCompileTo(' ');
|
||||||
});
|
});
|
||||||
@@ -194,8 +194,8 @@ describe('helpers', function() {
|
|||||||
.withInput({
|
.withInput({
|
||||||
people: [
|
people: [
|
||||||
{ name: 'Alan', id: 1 },
|
{ name: 'Alan', id: 1 },
|
||||||
{ name: 'Yehuda', id: 2 }
|
{ name: 'Yehuda', id: 2 },
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
.withHelper('link', link)
|
.withHelper('link', link)
|
||||||
.toCompileTo(
|
.toCompileTo(
|
||||||
@@ -232,7 +232,7 @@ describe('helpers', function() {
|
|||||||
'{{#form yehuda}}<p>{{name}}</p>{{#link}}Hello{{/link}}{{/form}}'
|
'{{#form yehuda}}<p>{{name}}</p>{{#link}}Hello{{/link}}{{/form}}'
|
||||||
)
|
)
|
||||||
.withInput({
|
.withInput({
|
||||||
yehuda: { name: 'Yehuda' }
|
yehuda: { name: 'Yehuda' },
|
||||||
})
|
})
|
||||||
.withHelper('link', function (options) {
|
.withHelper('link', function (options) {
|
||||||
return '<a href="' + this.name + '">' + options.fn(this) + '</a>';
|
return '<a href="' + this.name + '">' + options.fn(this) + '</a>';
|
||||||
@@ -278,7 +278,7 @@ describe('helpers', function() {
|
|||||||
expectTemplate('{{#list people}}Hello{{^}}{{message}}{{/list}}')
|
expectTemplate('{{#list people}}Hello{{^}}{{message}}{{/list}}')
|
||||||
.withInput({
|
.withInput({
|
||||||
people: [],
|
people: [],
|
||||||
message: "Nobody's here"
|
message: "Nobody's here",
|
||||||
})
|
})
|
||||||
.withHelpers({ list: list })
|
.withHelpers({ list: list })
|
||||||
.withMessage('the context of an inverse is the parent of the block')
|
.withMessage('the context of an inverse is the parent of the block')
|
||||||
@@ -289,13 +289,13 @@ describe('helpers', function() {
|
|||||||
var hash = {
|
var hash = {
|
||||||
helper: function () {
|
helper: function () {
|
||||||
return 'winning';
|
return 'winning';
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
hash.hash = hash;
|
hash.hash = hash;
|
||||||
var helpers = {
|
var helpers = {
|
||||||
'./helper': function () {
|
'./helper': function () {
|
||||||
return 'fail';
|
return 'fail';
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
expectTemplate('{{./helper 1}}')
|
expectTemplate('{{./helper 1}}')
|
||||||
@@ -316,7 +316,7 @@ describe('helpers', function() {
|
|||||||
.withHelpers({
|
.withHelpers({
|
||||||
world: function () {
|
world: function () {
|
||||||
return 'world';
|
return 'world';
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.withMessage('helpers hash is available')
|
.withMessage('helpers hash is available')
|
||||||
.toCompileTo('Goodbye cruel world!');
|
.toCompileTo('Goodbye cruel world!');
|
||||||
@@ -326,7 +326,7 @@ describe('helpers', function() {
|
|||||||
.withHelpers({
|
.withHelpers({
|
||||||
world: function () {
|
world: function () {
|
||||||
return 'world';
|
return 'world';
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.withMessage('helpers hash is available inside other blocks')
|
.withMessage('helpers hash is available inside other blocks')
|
||||||
.toCompileTo('Goodbye cruel world!');
|
.toCompileTo('Goodbye cruel world!');
|
||||||
@@ -338,7 +338,7 @@ describe('helpers', function() {
|
|||||||
.withHelpers({
|
.withHelpers({
|
||||||
lookup: function () {
|
lookup: function () {
|
||||||
return 'helpers';
|
return 'helpers';
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.withMessage('helpers hash has precedence escaped expansion')
|
.withMessage('helpers hash has precedence escaped expansion')
|
||||||
.toCompileTo('helpers');
|
.toCompileTo('helpers');
|
||||||
@@ -348,7 +348,7 @@ describe('helpers', function() {
|
|||||||
.withHelpers({
|
.withHelpers({
|
||||||
lookup: function () {
|
lookup: function () {
|
||||||
return 'helpers';
|
return 'helpers';
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.withMessage('helpers hash has precedence simple expansion')
|
.withMessage('helpers hash has precedence simple expansion')
|
||||||
.toCompileTo('helpers');
|
.toCompileTo('helpers');
|
||||||
@@ -360,7 +360,7 @@ describe('helpers', function() {
|
|||||||
.withHelpers({
|
.withHelpers({
|
||||||
helper: function () {
|
helper: function () {
|
||||||
return 'helper';
|
return 'helper';
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.withMessage('helpers hash is available in nested contexts.')
|
.withMessage('helpers hash is available in nested contexts.')
|
||||||
.toCompileTo('helper');
|
.toCompileTo('helper');
|
||||||
@@ -378,7 +378,7 @@ describe('helpers', function() {
|
|||||||
.withHelpers({
|
.withHelpers({
|
||||||
world: function () {
|
world: function () {
|
||||||
return 'world!';
|
return 'world!';
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('found it! Goodbye cruel world!!');
|
.toCompileTo('found it! Goodbye cruel world!!');
|
||||||
});
|
});
|
||||||
@@ -406,7 +406,7 @@ describe('helpers', function() {
|
|||||||
},
|
},
|
||||||
testHelper: function () {
|
testHelper: function () {
|
||||||
return 'found it!';
|
return 'found it!';
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
@@ -426,7 +426,7 @@ describe('helpers', function() {
|
|||||||
},
|
},
|
||||||
testHelper: function () {
|
testHelper: function () {
|
||||||
return 'found it!';
|
return 'found it!';
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
@@ -675,7 +675,7 @@ describe('helpers', function() {
|
|||||||
it('Known helper should render helper', function () {
|
it('Known helper should render helper', function () {
|
||||||
expectTemplate('{{hello}}')
|
expectTemplate('{{hello}}')
|
||||||
.withCompileOptions({
|
.withCompileOptions({
|
||||||
knownHelpers: { hello: true }
|
knownHelpers: { hello: true },
|
||||||
})
|
})
|
||||||
.withHelper('hello', function () {
|
.withHelper('hello', function () {
|
||||||
return 'foo';
|
return 'foo';
|
||||||
@@ -687,7 +687,7 @@ describe('helpers', function() {
|
|||||||
expectTemplate('{{typeof hello}}')
|
expectTemplate('{{typeof hello}}')
|
||||||
.withCompileOptions({
|
.withCompileOptions({
|
||||||
knownHelpers: { typeof: true },
|
knownHelpers: { typeof: true },
|
||||||
knownHelpersOnly: true
|
knownHelpersOnly: true,
|
||||||
})
|
})
|
||||||
.withHelper('typeof', function (arg) {
|
.withHelper('typeof', function (arg) {
|
||||||
return typeof arg;
|
return typeof arg;
|
||||||
@@ -701,7 +701,7 @@ describe('helpers', function() {
|
|||||||
it('Builtin helpers available in knownHelpers only mode', function () {
|
it('Builtin helpers available in knownHelpers only mode', function () {
|
||||||
expectTemplate('{{#unless foo}}bar{{/unless}}')
|
expectTemplate('{{#unless foo}}bar{{/unless}}')
|
||||||
.withCompileOptions({
|
.withCompileOptions({
|
||||||
knownHelpersOnly: true
|
knownHelpersOnly: true,
|
||||||
})
|
})
|
||||||
.toCompileTo('bar');
|
.toCompileTo('bar');
|
||||||
});
|
});
|
||||||
@@ -709,7 +709,7 @@ describe('helpers', function() {
|
|||||||
it('Field lookup works in knownHelpers only mode', function () {
|
it('Field lookup works in knownHelpers only mode', function () {
|
||||||
expectTemplate('{{foo}}')
|
expectTemplate('{{foo}}')
|
||||||
.withCompileOptions({
|
.withCompileOptions({
|
||||||
knownHelpersOnly: true
|
knownHelpersOnly: true,
|
||||||
})
|
})
|
||||||
.withInput({ foo: 'bar' })
|
.withInput({ foo: 'bar' })
|
||||||
.toCompileTo('bar');
|
.toCompileTo('bar');
|
||||||
@@ -718,7 +718,7 @@ describe('helpers', function() {
|
|||||||
it('Conditional blocks work in knownHelpers only mode', function () {
|
it('Conditional blocks work in knownHelpers only mode', function () {
|
||||||
expectTemplate('{{#foo}}bar{{/foo}}')
|
expectTemplate('{{#foo}}bar{{/foo}}')
|
||||||
.withCompileOptions({
|
.withCompileOptions({
|
||||||
knownHelpersOnly: true
|
knownHelpersOnly: true,
|
||||||
})
|
})
|
||||||
.withInput({ foo: 'baz' })
|
.withInput({ foo: 'baz' })
|
||||||
.toCompileTo('bar');
|
.toCompileTo('bar');
|
||||||
@@ -727,7 +727,7 @@ describe('helpers', function() {
|
|||||||
it('Invert blocks work in knownHelpers only mode', function () {
|
it('Invert blocks work in knownHelpers only mode', function () {
|
||||||
expectTemplate('{{^foo}}bar{{/foo}}')
|
expectTemplate('{{^foo}}bar{{/foo}}')
|
||||||
.withCompileOptions({
|
.withCompileOptions({
|
||||||
knownHelpersOnly: true
|
knownHelpersOnly: true,
|
||||||
})
|
})
|
||||||
.withInput({ foo: false })
|
.withInput({ foo: false })
|
||||||
.toCompileTo('bar');
|
.toCompileTo('bar');
|
||||||
@@ -736,13 +736,13 @@ describe('helpers', function() {
|
|||||||
it('Functions are bound to the context in knownHelpers only mode', function () {
|
it('Functions are bound to the context in knownHelpers only mode', function () {
|
||||||
expectTemplate('{{foo}}')
|
expectTemplate('{{foo}}')
|
||||||
.withCompileOptions({
|
.withCompileOptions({
|
||||||
knownHelpersOnly: true
|
knownHelpersOnly: true,
|
||||||
})
|
})
|
||||||
.withInput({
|
.withInput({
|
||||||
foo: function () {
|
foo: function () {
|
||||||
return this.bar;
|
return this.bar;
|
||||||
},
|
},
|
||||||
bar: 'bar'
|
bar: 'bar',
|
||||||
})
|
})
|
||||||
.toCompileTo('bar');
|
.toCompileTo('bar');
|
||||||
});
|
});
|
||||||
@@ -760,7 +760,7 @@ describe('helpers', function() {
|
|||||||
.withInput({
|
.withInput({
|
||||||
truthy: function () {
|
truthy: function () {
|
||||||
return true;
|
return true;
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('yep');
|
.toCompileTo('yep');
|
||||||
});
|
});
|
||||||
@@ -773,7 +773,7 @@ describe('helpers', function() {
|
|||||||
},
|
},
|
||||||
truthiness: function () {
|
truthiness: function () {
|
||||||
return false;
|
return false;
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('');
|
.toCompileTo('');
|
||||||
});
|
});
|
||||||
@@ -789,7 +789,7 @@ describe('helpers', function() {
|
|||||||
},
|
},
|
||||||
helper: function () {
|
helper: function () {
|
||||||
return 'ran: ' + arguments[arguments.length - 1].name;
|
return 'ran: ' + arguments[arguments.length - 1].name;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
it('should include in ambiguous mustache calls', function () {
|
it('should include in ambiguous mustache calls', function () {
|
||||||
@@ -826,7 +826,7 @@ describe('helpers', function() {
|
|||||||
expectTemplate('{{helper}}')
|
expectTemplate('{{helper}}')
|
||||||
.withCompileOptions({
|
.withCompileOptions({
|
||||||
knownHelpers: { helper: true },
|
knownHelpers: { helper: true },
|
||||||
knownHelpersOnly: true
|
knownHelpersOnly: true,
|
||||||
})
|
})
|
||||||
.withHelpers(helpers)
|
.withHelpers(helpers)
|
||||||
.toCompileTo('ran: helper');
|
.toCompileTo('ran: helper');
|
||||||
@@ -858,7 +858,7 @@ describe('helpers', function() {
|
|||||||
})
|
})
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbye: 'goodbye',
|
goodbye: 'goodbye',
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('Helper executed')
|
.withMessage('Helper executed')
|
||||||
.toCompileTo('GOODBYE cruel WORLD');
|
.toCompileTo('GOODBYE cruel WORLD');
|
||||||
@@ -874,7 +874,7 @@ describe('helpers', function() {
|
|||||||
})
|
})
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbye: 'goodbye',
|
goodbye: 'goodbye',
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('Helper executed')
|
.withMessage('Helper executed')
|
||||||
.toCompileTo('GOODBYE cruel WORLD');
|
.toCompileTo('GOODBYE cruel WORLD');
|
||||||
@@ -890,7 +890,7 @@ describe('helpers', function() {
|
|||||||
})
|
})
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbye: 'goodbye',
|
goodbye: 'goodbye',
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('Helper not executed')
|
.withMessage('Helper not executed')
|
||||||
.toCompileTo('goodbye cruel WORLD cruel GOODBYE');
|
.toCompileTo('goodbye cruel WORLD cruel GOODBYE');
|
||||||
@@ -908,7 +908,7 @@ describe('helpers', function() {
|
|||||||
})
|
})
|
||||||
.withInput({
|
.withInput({
|
||||||
goodbye: 'goodbye',
|
goodbye: 'goodbye',
|
||||||
world: 'world'
|
world: 'world',
|
||||||
})
|
})
|
||||||
.withMessage('Helper executed')
|
.withMessage('Helper executed')
|
||||||
.toCompileTo('GOODBYE cruel WORLD goodbye');
|
.toCompileTo('GOODBYE cruel WORLD goodbye');
|
||||||
@@ -964,7 +964,7 @@ describe('helpers', function() {
|
|||||||
{ value: 'bar' },
|
{ value: 'bar' },
|
||||||
{
|
{
|
||||||
blockParams:
|
blockParams:
|
||||||
options.fn.blockParams === 1 ? [value++, value++] : undefined
|
options.fn.blockParams === 1 ? [value++, value++] : undefined,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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 = {
|
||||||
|
|||||||
@@ -69,11 +69,13 @@ describe('javascript-compiler api', function() {
|
|||||||
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}} ')
|
||||||
|
|||||||
-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 }
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
+56
-56
@@ -5,8 +5,8 @@ describe('partials', function() {
|
|||||||
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)
|
||||||
@@ -28,13 +28,13 @@ describe('partials', function() {
|
|||||||
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)
|
||||||
@@ -57,14 +57,17 @@ 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' },
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
.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 () {
|
||||||
@@ -72,8 +75,8 @@ 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', '{{#this}}{{name}} ({{url}}) {{/this}}')
|
.withPartial('dude', '{{#this}}{{name}} ({{url}}) {{/this}}')
|
||||||
.withMessage('Partials can be passed a context')
|
.withMessage('Partials can be passed a context')
|
||||||
@@ -85,8 +88,8 @@ describe('partials', function() {
|
|||||||
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}}')
|
||||||
@@ -127,8 +130,8 @@ describe('partials', function() {
|
|||||||
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.')
|
||||||
@@ -140,12 +143,12 @@ 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' },
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
.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(
|
||||||
@@ -156,7 +159,7 @@ 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'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -174,7 +177,7 @@ 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'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -186,8 +189,8 @@ 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.')
|
||||||
@@ -234,7 +237,7 @@ describe('partials', function() {
|
|||||||
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}}')
|
||||||
@@ -281,8 +284,8 @@ 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', '')
|
.withPartial('dude', '')
|
||||||
.toCompileTo('Dudes: ');
|
.toCompileTo('Dudes: ');
|
||||||
@@ -341,7 +344,7 @@ describe('partials', 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');
|
||||||
});
|
});
|
||||||
@@ -350,8 +353,7 @@ describe('partials', 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>');
|
||||||
});
|
});
|
||||||
@@ -361,11 +363,10 @@ describe('partials', function() {
|
|||||||
'<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>'
|
||||||
@@ -376,8 +377,7 @@ describe('partials', 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');
|
||||||
});
|
});
|
||||||
@@ -386,7 +386,7 @@ describe('partials', 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');
|
||||||
});
|
});
|
||||||
@@ -406,7 +406,7 @@ describe('partials', function() {
|
|||||||
.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>'
|
||||||
@@ -419,7 +419,7 @@ describe('partials', function() {
|
|||||||
.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>'
|
||||||
@@ -432,7 +432,7 @@ describe('partials', function() {
|
|||||||
.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>'
|
||||||
@@ -445,7 +445,7 @@ describe('partials', function() {
|
|||||||
.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>' +
|
||||||
@@ -475,7 +475,7 @@ describe('partials', function() {
|
|||||||
|
|
||||||
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 () {
|
||||||
@@ -485,7 +485,7 @@ describe('partials', function() {
|
|||||||
.withPartials({
|
.withPartials({
|
||||||
myPartial: function () {
|
myPartial: function () {
|
||||||
return 'fail';
|
return 'fail';
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('success');
|
.toCompileTo('success');
|
||||||
});
|
});
|
||||||
@@ -566,8 +566,8 @@ 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', '{{name}}\n')
|
.withPartial('dude', '{{name}}\n')
|
||||||
.toCompileTo('Dudes:\n Yehuda\n Alan\n');
|
.toCompileTo('Dudes:\n Yehuda\n Alan\n');
|
||||||
@@ -578,12 +578,12 @@ 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' },
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
.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'
|
||||||
@@ -595,12 +595,12 @@ 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' },
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
.withPartials({
|
.withPartials({
|
||||||
dude: '{{name}}\n {{> url}}',
|
dude: '{{name}}\n {{> url}}',
|
||||||
url: '{{url}}!\n'
|
url: '{{url}}!\n',
|
||||||
})
|
})
|
||||||
.withCompileOptions({ preventIndent: true })
|
.withCompileOptions({ preventIndent: true })
|
||||||
.toCompileTo(
|
.toCompileTo(
|
||||||
@@ -616,8 +616,8 @@ describe('partials', function() {
|
|||||||
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 })
|
||||||
@@ -632,8 +632,8 @@ describe('partials', function() {
|
|||||||
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 })
|
||||||
@@ -648,8 +648,8 @@ describe('partials', function() {
|
|||||||
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 })
|
||||||
@@ -665,11 +665,11 @@ describe('partials', function() {
|
|||||||
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(
|
||||||
|
|||||||
+17
-91
@@ -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,
|
||||||
@@ -115,9 +115,9 @@ describe('precompiler', 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,
|
||||||
@@ -171,7 +171,7 @@ describe('precompiler', function() {
|
|||||||
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);
|
||||||
@@ -182,7 +182,7 @@ describe('precompiler', 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 () {
|
||||||
@@ -192,11 +192,18 @@ describe('precompiler', function() {
|
|||||||
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 es module templates', function () {
|
||||||
|
Handlebars.precompile = function () {
|
||||||
|
return 'esm';
|
||||||
|
};
|
||||||
|
Precompiler.cli({ templates: [emptyTemplate], esm: true });
|
||||||
|
equal(/template\(esm\)/.test(log), true);
|
||||||
|
});
|
||||||
it('should output commonjs templates', function () {
|
it('should output commonjs templates', function () {
|
||||||
Handlebars.precompile = function () {
|
Handlebars.precompile = function () {
|
||||||
return 'commonjs';
|
return 'commonjs';
|
||||||
@@ -229,7 +236,7 @@ describe('precompiler', function() {
|
|||||||
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');
|
||||||
@@ -289,7 +296,7 @@ describe('precompiler', 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');
|
||||||
@@ -339,7 +346,7 @@ describe('precompiler', function() {
|
|||||||
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');
|
||||||
@@ -351,7 +358,7 @@ describe('precompiler', function() {
|
|||||||
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');
|
||||||
@@ -405,85 +412,4 @@ describe('precompiler', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GHSA-xjpj-3mr7-gcpf: precompiler output escaping', function() {
|
|
||||||
var FullHandlebars = require('../dist/cjs/handlebars')['default'];
|
|
||||||
|
|
||||||
function runCliAndCaptureOutput(options) {
|
|
||||||
var output = '';
|
|
||||||
var oldLog = console.log;
|
|
||||||
console.log = function() {
|
|
||||||
output += Array.prototype.join.call(arguments, '');
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
Precompiler.cli(options);
|
|
||||||
} finally {
|
|
||||||
console.log = oldLog;
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should not inject raw template names into generated code', function() {
|
|
||||||
var output = runCliAndCaptureOutput({
|
|
||||||
templates: [
|
|
||||||
{
|
|
||||||
name: "evil'];global.__xjpjName=1;//",
|
|
||||||
source: ''
|
|
||||||
}
|
|
||||||
],
|
|
||||||
amd: true
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(output).to.not.match(/\['evil'\];global\.__xjpjName=1/);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not inject raw commonjs option values into generated code', function() {
|
|
||||||
var output = runCliAndCaptureOutput({
|
|
||||||
templates: [{ name: 'safe', source: '' }],
|
|
||||||
commonjs: 'handlebars");global.__xjpjCommon=1;//'
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(output).to.not.match(
|
|
||||||
/require\("handlebars"\);global\.__xjpjCommon=1/
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should reject invalid namespace expressions', function() {
|
|
||||||
expect(function() {
|
|
||||||
runCliAndCaptureOutput({
|
|
||||||
templates: [{ name: 'safe', source: '' }],
|
|
||||||
namespace: 'App.ns;global.__xjpjNamespace=1;//'
|
|
||||||
});
|
|
||||||
}).to.throw(/Invalid namespace/);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should sanitize sourceMappingURL comment values', function() {
|
|
||||||
var oldPrecompile = FullHandlebars.precompile;
|
|
||||||
var oldWriteFileSync = fs.writeFileSync;
|
|
||||||
FullHandlebars.precompile = function() {
|
|
||||||
return {
|
|
||||||
code: '""',
|
|
||||||
map: '{"version":3,"sources":[],"names":[],"mappings":""}'
|
|
||||||
};
|
|
||||||
};
|
|
||||||
fs.writeFileSync = function() {};
|
|
||||||
|
|
||||||
var output;
|
|
||||||
try {
|
|
||||||
output = runCliAndCaptureOutput({
|
|
||||||
templates: [{ name: 'safe', source: '' }],
|
|
||||||
map: 'good.js.map\n;global.__xjpjMap=1;//'
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
FullHandlebars.precompile = oldPrecompile;
|
|
||||||
fs.writeFileSync = oldWriteFileSync;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(output).to.not.match(
|
|
||||||
/sourceMappingURL=[^\n]*\n;global\.__xjpjMap=1/
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
+47
-44
@@ -6,13 +6,13 @@ describe('Regressions', function() {
|
|||||||
{
|
{
|
||||||
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');
|
||||||
@@ -73,11 +73,11 @@ describe('Regressions', function() {
|
|||||||
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,9 +89,7 @@ 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 () {
|
||||||
@@ -100,7 +98,7 @@ describe('Regressions', function() {
|
|||||||
)
|
)
|
||||||
.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');
|
||||||
@@ -118,7 +116,7 @@ describe('Regressions', function() {
|
|||||||
},
|
},
|
||||||
inverse: function (block) {
|
inverse: function (block) {
|
||||||
return block.inverse('');
|
return block.inverse('');
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
expectTemplate(succeedingTemplate)
|
expectTemplate(succeedingTemplate)
|
||||||
@@ -131,9 +129,7 @@ describe('Regressions', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
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 () {
|
||||||
@@ -144,9 +140,7 @@ describe('Regressions', 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 */
|
||||||
@@ -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(
|
||||||
@@ -186,15 +180,13 @@ describe('Regressions', 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 () {
|
||||||
@@ -202,7 +194,7 @@ describe('Regressions', function() {
|
|||||||
.withHelpers({
|
.withHelpers({
|
||||||
str: function (value) {
|
str: function (value) {
|
||||||
return value + '';
|
return value + '';
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('undefined');
|
.toCompileTo('undefined');
|
||||||
});
|
});
|
||||||
@@ -214,7 +206,7 @@ describe('Regressions', function() {
|
|||||||
.withInput({
|
.withInput({
|
||||||
name: 'foo',
|
name: 'foo',
|
||||||
data: [1],
|
data: [1],
|
||||||
notData: [1]
|
notData: [1],
|
||||||
})
|
})
|
||||||
.toCompileTo('foo');
|
.toCompileTo('foo');
|
||||||
});
|
});
|
||||||
@@ -225,8 +217,8 @@ describe('Regressions', function() {
|
|||||||
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');
|
||||||
});
|
});
|
||||||
@@ -236,10 +228,10 @@ describe('Regressions', function() {
|
|||||||
.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>');
|
||||||
});
|
});
|
||||||
@@ -267,7 +259,7 @@ 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');
|
||||||
});
|
});
|
||||||
@@ -279,7 +271,7 @@ describe('Regressions', function() {
|
|||||||
.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');
|
||||||
});
|
});
|
||||||
@@ -289,7 +281,7 @@ describe('Regressions', function() {
|
|||||||
.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');
|
||||||
});
|
});
|
||||||
@@ -298,7 +290,7 @@ describe('Regressions', 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');
|
||||||
});
|
});
|
||||||
@@ -318,7 +310,7 @@ describe('Regressions', function() {
|
|||||||
} 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');
|
||||||
});
|
});
|
||||||
@@ -331,19 +323,30 @@ describe('Regressions', function() {
|
|||||||
)
|
)
|
||||||
.withInput({
|
.withInput({
|
||||||
listOne: ['a'],
|
listOne: ['a'],
|
||||||
listTwo: ['b']
|
listTwo: ['b'],
|
||||||
})
|
})
|
||||||
.withMessage('')
|
.withMessage('')
|
||||||
.toCompileTo('ab');
|
.toCompileTo('ab');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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 () {
|
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 ');
|
||||||
@@ -354,7 +357,7 @@ describe('Regressions', function() {
|
|||||||
.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');
|
||||||
});
|
});
|
||||||
@@ -368,7 +371,7 @@ describe('Regressions', function() {
|
|||||||
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');
|
||||||
});
|
});
|
||||||
@@ -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');
|
||||||
});
|
});
|
||||||
@@ -427,7 +430,7 @@ describe('Regressions', function() {
|
|||||||
) + '\n\n'
|
) + '\n\n'
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
useData: true
|
useData: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -444,12 +447,12 @@ describe('Regressions', function() {
|
|||||||
{
|
{
|
||||||
name: 'lookup',
|
name: 'lookup',
|
||||||
hash: {},
|
hash: {},
|
||||||
data: data
|
data: data,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
useData: true
|
useData: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -460,7 +463,7 @@ describe('Regressions', function() {
|
|||||||
.withHelpers({
|
.withHelpers({
|
||||||
helpa: function (options) {
|
helpa: function (options) {
|
||||||
return options.hash.length;
|
return options.hash.length;
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('foo');
|
.toCompileTo('foo');
|
||||||
});
|
});
|
||||||
@@ -482,7 +485,7 @@ describe('Regressions', function() {
|
|||||||
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
|
||||||
|
|||||||
+3
-55
@@ -28,7 +28,7 @@ describe('runtime', function() {
|
|||||||
function () {
|
function () {
|
||||||
Handlebars.template({
|
Handlebars.template({
|
||||||
main: {},
|
main: {},
|
||||||
compiler: [Handlebars.COMPILER_REVISION + 1]
|
compiler: [Handlebars.COMPILER_REVISION + 1],
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
Error,
|
Error,
|
||||||
@@ -38,7 +38,7 @@ describe('runtime', function() {
|
|||||||
function () {
|
function () {
|
||||||
Handlebars.template({
|
Handlebars.template({
|
||||||
main: {},
|
main: {},
|
||||||
compiler: [Handlebars.LAST_COMPATIBLE_COMPILER_REVISION - 1]
|
compiler: [Handlebars.LAST_COMPATIBLE_COMPILER_REVISION - 1],
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
Error,
|
Error,
|
||||||
@@ -47,65 +47,13 @@ describe('runtime', function() {
|
|||||||
shouldThrow(
|
shouldThrow(
|
||||||
function () {
|
function () {
|
||||||
Handlebars.template({
|
Handlebars.template({
|
||||||
main: {}
|
main: {},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
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/
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should safely resolve missing partial map entries', function() {
|
|
||||||
equal(
|
|
||||||
Handlebars.VM.resolvePartial(undefined, {}, { name: 'missing' }),
|
|
||||||
undefined
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#child', function() {
|
|
||||||
if (!Handlebars.compile) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should throw for depthed methods without depths', function() {
|
|
||||||
shouldThrow(
|
|
||||||
function() {
|
|
||||||
var template = Handlebars.compile('{{#foo}}{{../bar}}{{/foo}}');
|
|
||||||
// Calling twice to hit the non-compiled case.
|
|
||||||
template._setup({});
|
|
||||||
template._setup({});
|
|
||||||
template._child(1);
|
|
||||||
},
|
|
||||||
Error,
|
|
||||||
'must pass parent depths'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw for block param methods without params', function() {
|
|
||||||
shouldThrow(
|
|
||||||
function() {
|
|
||||||
var template = Handlebars.compile('{{#foo as |foo|}}{{foo}}{{/foo}}');
|
|
||||||
// Calling twice to hit the non-compiled case.
|
|
||||||
template._setup({});
|
|
||||||
template._setup({});
|
|
||||||
template._child(1);
|
|
||||||
},
|
|
||||||
Error,
|
|
||||||
'must pass block params'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('should expose child template', function() {
|
|
||||||
var template = Handlebars.compile('{{#foo}}bar{{/foo}}');
|
|
||||||
// Calling twice to hit the non-compiled case.
|
|
||||||
equal(template._child(0)(), 'bar');
|
|
||||||
equal(template._child(0)(), '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(0, undefined, [], [{ bar: 'baz' }])(), 'baz');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#noConflict', function () {
|
describe('#noConflict', function () {
|
||||||
|
|||||||
+21
-245
@@ -5,9 +5,7 @@ describe('security issues', function() {
|
|||||||
.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 () {
|
||||||
@@ -57,7 +55,7 @@ describe('security issues', function() {
|
|||||||
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);
|
||||||
@@ -68,7 +66,7 @@ describe('security issues', function() {
|
|||||||
.withInput({
|
.withInput({
|
||||||
fn: function () {
|
fn: function () {
|
||||||
return 'functionInData';
|
return 'functionInData';
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toThrow(Error);
|
.toThrow(Error);
|
||||||
});
|
});
|
||||||
@@ -94,7 +92,7 @@ describe('security issues', function() {
|
|||||||
{
|
{
|
||||||
fn: function () {
|
fn: function () {
|
||||||
functionCalls.push('called');
|
functionCalls.push('called');
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{ allowCallsToHelperMissing: true }
|
{ allowCallsToHelperMissing: true }
|
||||||
);
|
);
|
||||||
@@ -133,29 +131,25 @@ describe('security issues', function() {
|
|||||||
'{{__defineGetter__}}',
|
'{{__defineGetter__}}',
|
||||||
'{{__defineSetter__}}',
|
'{{__defineSetter__}}',
|
||||||
'{{__lookupGetter__}}',
|
'{{__lookupGetter__}}',
|
||||||
'{{__lookupSetter__}}',
|
|
||||||
'{{__proto__}}',
|
'{{__proto__}}',
|
||||||
'{{lookup this "constructor"}}',
|
'{{lookup this "constructor"}}',
|
||||||
'{{lookup this "__defineGetter__"}}',
|
'{{lookup this "__defineGetter__"}}',
|
||||||
'{{lookup this "__defineSetter__"}}',
|
'{{lookup this "__defineSetter__"}}',
|
||||||
'{{lookup this "__lookupGetter__"}}',
|
'{{lookup this "__lookupGetter__"}}',
|
||||||
'{{lookup this "__lookupSetter__"}}',
|
'{{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('');
|
||||||
});
|
});
|
||||||
@@ -223,8 +217,8 @@ describe('security issues', function() {
|
|||||||
.withCompileOptions(compileOptions)
|
.withCompileOptions(compileOptions)
|
||||||
.withRuntimeOptions({
|
.withRuntimeOptions({
|
||||||
allowedProtoMethods: {
|
allowedProtoMethods: {
|
||||||
aMethod: true
|
aMethod: true,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('returnValue');
|
.toCompileTo('returnValue');
|
||||||
|
|
||||||
@@ -238,7 +232,7 @@ describe('security issues', function() {
|
|||||||
.withInput(new TestClass())
|
.withInput(new TestClass())
|
||||||
.withCompileOptions(compileOptions)
|
.withCompileOptions(compileOptions)
|
||||||
.withRuntimeOptions({
|
.withRuntimeOptions({
|
||||||
allowProtoMethodsByDefault: true
|
allowProtoMethodsByDefault: true,
|
||||||
})
|
})
|
||||||
.toCompileTo('returnValue');
|
.toCompileTo('returnValue');
|
||||||
|
|
||||||
@@ -252,7 +246,7 @@ describe('security issues', function() {
|
|||||||
.withInput(new TestClass())
|
.withInput(new TestClass())
|
||||||
.withCompileOptions(compileOptions)
|
.withCompileOptions(compileOptions)
|
||||||
.withRuntimeOptions({
|
.withRuntimeOptions({
|
||||||
allowProtoMethodsByDefault: false
|
allowProtoMethodsByDefault: false,
|
||||||
})
|
})
|
||||||
.toCompileTo('');
|
.toCompileTo('');
|
||||||
|
|
||||||
@@ -266,8 +260,8 @@ describe('security issues', function() {
|
|||||||
.withRuntimeOptions({
|
.withRuntimeOptions({
|
||||||
allowProtoMethodsByDefault: true,
|
allowProtoMethodsByDefault: true,
|
||||||
allowedProtoMethods: {
|
allowedProtoMethods: {
|
||||||
aMethod: false
|
aMethod: false,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('');
|
.toCompileTo('');
|
||||||
});
|
});
|
||||||
@@ -286,8 +280,8 @@ describe('security issues', function() {
|
|||||||
.withCompileOptions({ compat: true })
|
.withCompileOptions({ compat: true })
|
||||||
.withRuntimeOptions({
|
.withRuntimeOptions({
|
||||||
allowedProtoMethods: {
|
allowedProtoMethods: {
|
||||||
trim: true
|
trim: true,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('abc');
|
.toCompileTo('abc');
|
||||||
});
|
});
|
||||||
@@ -324,7 +318,7 @@ describe('security issues', function() {
|
|||||||
.withInput(new TestClass())
|
.withInput(new TestClass())
|
||||||
.withCompileOptions(compileOptions)
|
.withCompileOptions(compileOptions)
|
||||||
.withRuntimeOptions({
|
.withRuntimeOptions({
|
||||||
allowProtoPropertiesByDefault: false
|
allowProtoPropertiesByDefault: false,
|
||||||
})
|
})
|
||||||
.toCompileTo('');
|
.toCompileTo('');
|
||||||
|
|
||||||
@@ -339,8 +333,8 @@ describe('security issues', function() {
|
|||||||
.withCompileOptions(compileOptions)
|
.withCompileOptions(compileOptions)
|
||||||
.withRuntimeOptions({
|
.withRuntimeOptions({
|
||||||
allowedProtoProperties: {
|
allowedProtoProperties: {
|
||||||
aProperty: true
|
aProperty: true,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('propertyValue');
|
.toCompileTo('propertyValue');
|
||||||
|
|
||||||
@@ -354,7 +348,7 @@ describe('security issues', function() {
|
|||||||
.withInput(new TestClass())
|
.withInput(new TestClass())
|
||||||
.withCompileOptions(compileOptions)
|
.withCompileOptions(compileOptions)
|
||||||
.withRuntimeOptions({
|
.withRuntimeOptions({
|
||||||
allowProtoPropertiesByDefault: true
|
allowProtoPropertiesByDefault: true,
|
||||||
})
|
})
|
||||||
.toCompileTo('propertyValue');
|
.toCompileTo('propertyValue');
|
||||||
|
|
||||||
@@ -368,8 +362,8 @@ describe('security issues', function() {
|
|||||||
.withRuntimeOptions({
|
.withRuntimeOptions({
|
||||||
allowProtoPropertiesByDefault: true,
|
allowProtoPropertiesByDefault: true,
|
||||||
allowedProtoProperties: {
|
allowedProtoProperties: {
|
||||||
aProperty: false
|
aProperty: false,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('');
|
.toCompileTo('');
|
||||||
});
|
});
|
||||||
@@ -424,224 +418,6 @@ describe('security issues', function() {
|
|||||||
.toCompileTo('c');
|
.toCompileTo('c');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GHSA-2qvq-rjwj-gvw9: partial resolution must not use polluted prototypes', function() {
|
|
||||||
if (!Handlebars.compile) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
afterEach(function() {
|
|
||||||
delete Object.prototype.widget;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not resolve partial names from Object.prototype', function() {
|
|
||||||
// eslint-disable-next-line no-extend-native
|
|
||||||
Object.prototype.widget = '<img src=x onerror="alert(1)">';
|
|
||||||
|
|
||||||
expect(function() {
|
|
||||||
Handlebars.compile('<div>{{> widget}}</div>')({});
|
|
||||||
}).to.throw(/could not be found/);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('GHSA-2w6w-674q-4c4q, GHSA-xhpv-hc6g-r9c6, GHSA-3mfm-83xf-c92r: untrusted AST inputs', function() {
|
|
||||||
if (!Handlebars.compile) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createInjectedProgram() {
|
|
||||||
var loc = {
|
|
||||||
source: null,
|
|
||||||
start: { line: 1, column: 0 },
|
|
||||||
end: { line: 1, column: 20 }
|
|
||||||
};
|
|
||||||
return {
|
|
||||||
type: 'Program',
|
|
||||||
body: [
|
|
||||||
{
|
|
||||||
type: 'MustacheStatement',
|
|
||||||
escaped: true,
|
|
||||||
strip: {
|
|
||||||
open: false,
|
|
||||||
close: false
|
|
||||||
},
|
|
||||||
loc: loc,
|
|
||||||
path: {
|
|
||||||
type: 'PathExpression',
|
|
||||||
data: false,
|
|
||||||
depth: 0,
|
|
||||||
parts: ['lookup'],
|
|
||||||
original: 'lookup',
|
|
||||||
loc: loc
|
|
||||||
},
|
|
||||||
params: [
|
|
||||||
{
|
|
||||||
type: 'PathExpression',
|
|
||||||
data: false,
|
|
||||||
depth: 0,
|
|
||||||
parts: [],
|
|
||||||
original: 'this',
|
|
||||||
loc: loc
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'NumberLiteral',
|
|
||||||
value: '{},{})) + (Function) + (({}',
|
|
||||||
original: 1,
|
|
||||||
loc: loc
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should neutralize AST NumberLiteral type confusion in compile()', function() {
|
|
||||||
// The compiler coerces NumberLiteral.value via Number() before
|
|
||||||
// emitting a pushLiteral opcode, so a type-confused string value
|
|
||||||
// becomes NaN, preventing code injection.
|
|
||||||
var template = Handlebars.compile(createInjectedProgram());
|
|
||||||
var result = template({});
|
|
||||||
expect(result).to.not.contain('Function');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should reject AST objects passed via dynamic partial lookup', function() {
|
|
||||||
expect(function() {
|
|
||||||
var template = Handlebars.compile('{{> (lookup . "payload")}}');
|
|
||||||
template({
|
|
||||||
payload: createInjectedProgram()
|
|
||||||
});
|
|
||||||
}).to.throw(/could not be found/);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should sanitize param depth in stringParams mode', function() {
|
|
||||||
// pushParam passes val.depth directly to the getContext opcode.
|
|
||||||
// In stringParams mode, getContext stores the depth in lastContext,
|
|
||||||
// which contextName interpolates into generated code as
|
|
||||||
// 'depths[' + depth + ']'. A malicious depth string can escape the
|
|
||||||
// bracket expression and inject arbitrary code at template runtime.
|
|
||||||
//
|
|
||||||
// With sanitization the depth becomes 0, producing 'depth0' (safe).
|
|
||||||
// Without sanitization the injected expression executes and throws.
|
|
||||||
var loc = {
|
|
||||||
source: null,
|
|
||||||
start: { line: 1, column: 0 },
|
|
||||||
end: { line: 1, column: 20 }
|
|
||||||
};
|
|
||||||
var maliciousAST = {
|
|
||||||
type: 'Program',
|
|
||||||
body: [
|
|
||||||
{
|
|
||||||
type: 'MustacheStatement',
|
|
||||||
escaped: true,
|
|
||||||
strip: { open: false, close: false },
|
|
||||||
loc: loc,
|
|
||||||
path: {
|
|
||||||
type: 'PathExpression',
|
|
||||||
data: false,
|
|
||||||
depth: 0,
|
|
||||||
parts: ['lookup'],
|
|
||||||
original: 'lookup',
|
|
||||||
loc: loc
|
|
||||||
},
|
|
||||||
params: [
|
|
||||||
{
|
|
||||||
type: 'PathExpression',
|
|
||||||
data: false,
|
|
||||||
depth: 'function(){throw new Error("INJECTION")}()',
|
|
||||||
parts: [],
|
|
||||||
original: '',
|
|
||||||
loc: loc
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
var template = Handlebars.compile(maliciousAST, {
|
|
||||||
stringParams: true
|
|
||||||
});
|
|
||||||
// After sanitization the depth is 0, so the template runs without
|
|
||||||
// executing the injected throw expression.
|
|
||||||
expect(function() {
|
|
||||||
template({});
|
|
||||||
}).to.not.throw();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('GHSA-442j-39wm-28r2: lookup must return checked value', function() {
|
|
||||||
it('should use the validated value from lookupProperty() in compat mode', function() {
|
|
||||||
var input = { child: {} };
|
|
||||||
var readCount = 0;
|
|
||||||
Object.defineProperty(input, 'unstable', {
|
|
||||||
enumerable: true,
|
|
||||||
get: function() {
|
|
||||||
readCount++;
|
|
||||||
return readCount === 1 ? 'first-read' : 'second-read';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
expectTemplate('{{#with child}}{{unstable}}{{/with}}')
|
|
||||||
.withInput(input)
|
|
||||||
.withCompileOptions({ compat: true })
|
|
||||||
.toCompileTo('first-read');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('GHSA-9cx6-37pm-9jff: malformed decorators should fail safely', function() {
|
|
||||||
if (!Handlebars.compile) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should throw a controlled error for unknown decorators', function() {
|
|
||||||
var template = Handlebars.compile('{{*notRegistered}}');
|
|
||||||
expect(function() {
|
|
||||||
template({});
|
|
||||||
}).to.throw(/Missing decorator|not registered/);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('GHSA-new: @partial-block must not resolve from polluted prototype', function() {
|
|
||||||
if (!Handlebars.compile) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
afterEach(function() {
|
|
||||||
delete Object.prototype['partial-block'];
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not resolve @partial-block from Object.prototype', function() {
|
|
||||||
// eslint-disable-next-line no-extend-native
|
|
||||||
Object.prototype['partial-block'] = '<img src=x onerror="alert(1)">';
|
|
||||||
|
|
||||||
expect(function() {
|
|
||||||
Handlebars.compile('{{> @partial-block}}')({});
|
|
||||||
}).to.throw(/could not be found/);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not resolve @partial-block from Object.prototype inside a partial', function() {
|
|
||||||
// eslint-disable-next-line no-extend-native
|
|
||||||
Object.prototype['partial-block'] = '<img src=x onerror="alert(1)">';
|
|
||||||
|
|
||||||
Handlebars.registerPartial('testPartial', '{{> @partial-block}}');
|
|
||||||
try {
|
|
||||||
expect(function() {
|
|
||||||
Handlebars.compile('{{> testPartial}}')({});
|
|
||||||
}).to.throw(/could not be found/);
|
|
||||||
} finally {
|
|
||||||
Handlebars.unregisterPartial('testPartial');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should still render legitimate @partial-block content', function() {
|
|
||||||
Handlebars.registerPartial('wrapper', '<div>{{> @partial-block}}</div>');
|
|
||||||
try {
|
|
||||||
var result = Handlebars.compile('{{#> wrapper}}hello{{/wrapper}}')({});
|
|
||||||
expect(result).to.equal('<div>hello</div>');
|
|
||||||
} finally {
|
|
||||||
Handlebars.unregisterPartial('wrapper');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function wrapToAdjustContainer(precompiledTemplateFunction) {
|
function wrapToAdjustContainer(precompiledTemplateFunction) {
|
||||||
|
|||||||
+3
-3
@@ -15,7 +15,7 @@ describe('source-map', function() {
|
|||||||
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);
|
||||||
@@ -26,7 +26,7 @@ describe('source-map', function() {
|
|||||||
' 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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-16
@@ -4,40 +4,34 @@ describe('spec', function() {
|
|||||||
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)
|
||||||
|
|||||||
+5
-5
@@ -31,7 +31,7 @@ describe('strict', 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/);
|
||||||
});
|
});
|
||||||
@@ -44,7 +44,7 @@ describe('strict', function() {
|
|||||||
|
|
||||||
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);
|
||||||
@@ -88,14 +88,14 @@ describe('strict', function() {
|
|||||||
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');
|
||||||
});
|
});
|
||||||
@@ -109,7 +109,7 @@ describe('strict', function() {
|
|||||||
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) {
|
||||||
|
|||||||
@@ -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!');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
+16
-76
@@ -7,7 +7,7 @@ describe('subexpressions', function() {
|
|||||||
},
|
},
|
||||||
bar: function () {
|
bar: function () {
|
||||||
return 'LOL';
|
return 'LOL';
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('LOLLOL!');
|
.toCompileTo('LOLLOL!');
|
||||||
});
|
});
|
||||||
@@ -21,7 +21,7 @@ describe('subexpressions', function() {
|
|||||||
},
|
},
|
||||||
equal: function (x, y) {
|
equal: function (x, y) {
|
||||||
return x === y;
|
return x === y;
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('val is true');
|
.toCompileTo('val is true');
|
||||||
});
|
});
|
||||||
@@ -35,7 +35,7 @@ describe('subexpressions', function() {
|
|||||||
},
|
},
|
||||||
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!');
|
||||||
});
|
});
|
||||||
@@ -49,7 +49,7 @@ describe('subexpressions', function() {
|
|||||||
},
|
},
|
||||||
equal: function (x, y) {
|
equal: function (x, y) {
|
||||||
return x === y;
|
return x === y;
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('val is true');
|
.toCompileTo('val is true');
|
||||||
});
|
});
|
||||||
@@ -62,7 +62,7 @@ describe('subexpressions', function() {
|
|||||||
},
|
},
|
||||||
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)}}")
|
||||||
@@ -100,7 +100,7 @@ describe('subexpressions', function() {
|
|||||||
}
|
}
|
||||||
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)
|
||||||
@@ -116,7 +116,7 @@ describe('subexpressions', function() {
|
|||||||
},
|
},
|
||||||
equal: function (x, y) {
|
equal: function (x, y) {
|
||||||
return x === y;
|
return x === y;
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('val is true');
|
.toCompileTo('val is true');
|
||||||
});
|
});
|
||||||
@@ -129,7 +129,7 @@ describe('subexpressions', function() {
|
|||||||
},
|
},
|
||||||
equal: function (x, y) {
|
equal: function (x, y) {
|
||||||
return x === y;
|
return x === y;
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.toCompileTo('val is true');
|
.toCompileTo('val is true');
|
||||||
});
|
});
|
||||||
@@ -153,7 +153,7 @@ 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" />');
|
||||||
});
|
});
|
||||||
@@ -165,8 +165,8 @@ describe('subexpressions', function() {
|
|||||||
.withInput({
|
.withInput({
|
||||||
item: {
|
item: {
|
||||||
field: 'Name',
|
field: 'Name',
|
||||||
placeholder: 'Example User'
|
placeholder: 'Example User',
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.withHelpers({
|
.withHelpers({
|
||||||
input: function (options) {
|
input: function (options) {
|
||||||
@@ -183,82 +183,22 @@ 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() {
|
|
||||||
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 () {
|
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!');
|
||||||
});
|
});
|
||||||
@@ -266,12 +206,12 @@ describe('subexpressions', function() {
|
|||||||
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();
|
||||||
});
|
});
|
||||||
|
|||||||
+37
-37
@@ -63,7 +63,7 @@ describe('Tokenizer', function() {
|
|||||||
'CONTENT',
|
'CONTENT',
|
||||||
'OPEN',
|
'OPEN',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
shouldBeToken(result[3], 'CONTENT', ' ');
|
shouldBeToken(result[3], 'CONTENT', ' ');
|
||||||
@@ -78,7 +78,7 @@ describe('Tokenizer', function() {
|
|||||||
'CLOSE',
|
'CLOSE',
|
||||||
'CONTENT',
|
'CONTENT',
|
||||||
'CONTENT',
|
'CONTENT',
|
||||||
'CONTENT'
|
'CONTENT',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
shouldBeToken(result[3], 'CONTENT', ' ');
|
shouldBeToken(result[3], 'CONTENT', ' ');
|
||||||
@@ -96,7 +96,7 @@ describe('Tokenizer', function() {
|
|||||||
'CONTENT',
|
'CONTENT',
|
||||||
'OPEN',
|
'OPEN',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
shouldBeToken(result[4], 'CONTENT', '{{{bar}}} ');
|
shouldBeToken(result[4], 'CONTENT', '{{{bar}}} ');
|
||||||
@@ -115,7 +115,7 @@ describe('Tokenizer', function() {
|
|||||||
'CONTENT',
|
'CONTENT',
|
||||||
'OPEN',
|
'OPEN',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
shouldBeToken(result[3], 'CONTENT', ' \\');
|
shouldBeToken(result[3], 'CONTENT', ' \\');
|
||||||
@@ -135,7 +135,7 @@ describe('Tokenizer', function() {
|
|||||||
'CONTENT',
|
'CONTENT',
|
||||||
'OPEN',
|
'OPEN',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
shouldBeToken(result[3], 'CONTENT', ' \\');
|
shouldBeToken(result[3], 'CONTENT', ' \\');
|
||||||
@@ -156,7 +156,7 @@ describe('Tokenizer', function() {
|
|||||||
'CLOSE',
|
'CLOSE',
|
||||||
'CONTENT',
|
'CONTENT',
|
||||||
'CONTENT',
|
'CONTENT',
|
||||||
'CONTENT'
|
'CONTENT',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
shouldBeToken(result[3], 'CONTENT', ' \\');
|
shouldBeToken(result[3], 'CONTENT', ' \\');
|
||||||
@@ -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}} ');
|
||||||
@@ -199,7 +199,7 @@ describe('Tokenizer', function() {
|
|||||||
'CONTENT',
|
'CONTENT',
|
||||||
'OPEN',
|
'OPEN',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
shouldBeToken(result[3], 'CONTENT', ' \\');
|
shouldBeToken(result[3], 'CONTENT', ' \\');
|
||||||
@@ -222,7 +222,7 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'SEP',
|
'SEP',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -243,7 +243,7 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'SEP',
|
'SEP',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -266,7 +266,7 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'SEP',
|
'SEP',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
shouldBeToken(result[1], 'ID', '..');
|
shouldBeToken(result[1], 'ID', '..');
|
||||||
});
|
});
|
||||||
@@ -280,7 +280,7 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'SEP',
|
'SEP',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
shouldBeToken(result[1], 'ID', '..');
|
shouldBeToken(result[1], 'ID', '..');
|
||||||
});
|
});
|
||||||
@@ -340,7 +340,7 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'SEP',
|
'SEP',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -356,7 +356,7 @@ 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 }}');
|
||||||
});
|
});
|
||||||
@@ -369,7 +369,7 @@ 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}} --}}');
|
||||||
});
|
});
|
||||||
@@ -384,7 +384,7 @@ 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--}}');
|
||||||
});
|
});
|
||||||
@@ -398,7 +398,7 @@ describe('Tokenizer', function() {
|
|||||||
'CONTENT',
|
'CONTENT',
|
||||||
'OPEN_ENDBLOCK',
|
'OPEN_ENDBLOCK',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -410,7 +410,7 @@ 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']);
|
||||||
});
|
});
|
||||||
@@ -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,7 +601,7 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'EQUALS',
|
'EQUALS',
|
||||||
'STRING',
|
'STRING',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
shouldBeToken(result[2], 'ID', 'omg');
|
shouldBeToken(result[2], 'ID', 'omg');
|
||||||
});
|
});
|
||||||
@@ -623,7 +623,7 @@ describe('Tokenizer', function() {
|
|||||||
'EQUALS',
|
'EQUALS',
|
||||||
'DATA',
|
'DATA',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
shouldBeToken(result[5], 'ID', 'baz');
|
shouldBeToken(result[5], 'ID', 'baz');
|
||||||
});
|
});
|
||||||
@@ -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,7 +657,7 @@ 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');
|
||||||
@@ -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');
|
||||||
@@ -714,7 +714,7 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'STRING',
|
'STRING',
|
||||||
'CLOSE_SEXPR',
|
'CLOSE_SEXPR',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -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,7 +771,7 @@ describe('Tokenizer', function() {
|
|||||||
'ID',
|
'ID',
|
||||||
'ID',
|
'ID',
|
||||||
'CLOSE_BLOCK_PARAMS',
|
'CLOSE_BLOCK_PARAMS',
|
||||||
'CLOSE'
|
'CLOSE',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -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'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user