Migrate to oxlint and oxfmt v2 (#2130)

* Migrate to oxlint and oxfmt
* Address review comments
This commit is contained in:
Igor Savin
2026-03-10 18:27:44 +02:00
committed by GitHub
parent 169ef75066
commit 2f68d9425b
49 changed files with 3189 additions and 3564 deletions
-23
View File
@@ -1,23 +0,0 @@
.rvmrc
.DS_Store
/tmp/
*.sublime-project
*.sublime-workspace
npm-debug.log
.idea
yarn-error.log
node_modules
/handlebars-release.tgz
.nyc_output
# Generated files
/coverage/
/dist/
/tests/integration/*/dist/
# Third-party or files that must remain unchanged
/spec/expected/
/spec/vendor
# JS-Snippets
src/*.js
-64
View File
@@ -1,64 +0,0 @@
module.exports = {
extends: ['eslint:recommended', 'plugin:compat/recommended', 'prettier'],
globals: {
self: false,
},
env: {
node: true,
es2020: true,
},
parserOptions: {
sourceType: 'module',
},
rules: {
'no-console': 'warn',
// temporarily disabled until the violating places are fixed.
'no-func-assign': 'off',
'no-sparse-arrays': 'off',
// Best Practices //
//----------------//
'default-case': 'warn',
'guard-for-in': 'warn',
'no-alert': 'error',
'no-caller': 'error',
'no-div-regex': 'warn',
'no-eval': 'error',
'no-extend-native': 'error',
'no-extra-bind': 'error',
'no-floating-decimal': 'error',
'no-implied-eval': 'error',
'no-iterator': 'error',
'no-labels': 'error',
'no-lone-blocks': 'error',
'no-loop-func': 'error',
'no-multi-str': 'warn',
'no-global-assign': 'error',
'no-new': 'error',
'no-new-func': 'error',
'no-new-wrappers': 'error',
'no-octal-escape': 'error',
'no-process-env': 'error',
'no-proto': 'error',
'no-return-assign': 'error',
'no-script-url': 'error',
'no-self-compare': 'error',
'no-sequences': 'error',
'no-throw-literal': 'error',
'no-unused-expressions': 'error',
'no-warning-comments': 'warn',
'no-with': 'error',
radix: 'error',
// Variables //
//-----------//
'no-label-var': 'error',
'no-undef-init': 'error',
'no-use-before-define': ['error', 'nofunc'],
// ECMAScript 6 //
//--------------//
'no-var': 'error',
},
};
+3 -1
View File
@@ -1,4 +1,6 @@
# Upgrade to Prettier 2.7
3d228334530860a6e3f99dc10777c84bf22292c1
# Format markdown files with Prettier
dfe2eaaf20f0b679d94e5a799757c4394d80f1cc
dfe2eaaf20f0b679d94e5a799757c4394d80f1cc
# migrate to oxlint and oxfmt
0c1d00282ca619c3416ad819bdf53c0852b32415
+1 -1
View File
@@ -1,7 +1,7 @@
version: 2
updates:
- package-ecosystem: npm
directory: "/"
directory: '/'
open-pull-requests-limit: 0
schedule:
interval: weekly
+1 -1
View File
@@ -33,6 +33,6 @@ jobs:
git config --global user.name "handlebars-lang"
npm run publish:aws
env:
S3_BUCKET_NAME: "builds.handlebarsjs.com"
S3_BUCKET_NAME: 'builds.handlebarsjs.com'
S3_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY_ID }}
S3_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_ACCESS_KEY }}
+28
View File
@@ -0,0 +1,28 @@
{
"$schema": "https://raw.githubusercontent.com/nicolo-ribaudo/oxfmt-config-schema/refs/heads/main/schema.json",
"singleQuote": true,
"tabWidth": 2,
"semi": true,
"trailingComma": "es5",
"printWidth": 80,
"ignorePatterns": [
".rvmrc",
".DS_Store",
"/tmp/",
"*.sublime-project",
"*.sublime-workspace",
"npm-debug.log",
"sauce_connect.log*",
".idea",
"yarn-error.log",
"/coverage/",
".nyc_output/",
"/dist/",
"/tests/integration/*/dist/",
"/spec/expected/",
"/spec/mustache",
"/spec/vendor",
"*.handlebars",
"*.hbs"
]
}
+151
View File
@@ -0,0 +1,151 @@
{
"$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json",
"plugins": ["eslint", "typescript", "unicorn", "oxc", "node", "vitest"],
"categories": {
"correctness": "error"
},
"rules": {
"no-console": "warn",
"no-func-assign": "off",
"no-sparse-arrays": "off",
"default-case": "warn",
"guard-for-in": "warn",
"no-alert": "error",
"no-caller": "error",
"no-div-regex": "warn",
"no-eval": "error",
"no-extend-native": "error",
"no-extra-bind": "error",
"no-implied-eval": "error",
"no-iterator": "error",
"no-labels": "error",
"no-lone-blocks": "error",
"no-loop-func": "error",
"no-multi-str": "warn",
"no-global-assign": "error",
"no-new": "error",
"no-new-func": "error",
"no-new-wrappers": "error",
"no-proto": "error",
"no-return-assign": "error",
"no-script-url": "error",
"no-self-compare": "error",
"no-sequences": "error",
"no-throw-literal": "error",
"no-unused-expressions": "error",
"no-warning-comments": "warn",
"no-with": "error",
"radix": "error",
"no-label-var": "error",
"no-use-before-define": ["error", { "functions": false }],
"no-var": "error",
"node/no-process-env": "error"
},
"ignorePatterns": [
"tmp/",
"dist/",
"coverage/",
".nyc_output/",
"handlebars-release.tgz",
"tests/integration/*/dist/",
"spec/expected/",
"spec/mustache",
"spec/vendor",
"node_modules",
"types/"
],
"overrides": [
{
"files": ["lib/**/*.js"],
"env": {
"node": false,
"browser": true
}
},
{
"files": ["spec/**/*.js"],
"globals": {
"CompilerContext": "readonly",
"Handlebars": "writable",
"handlebarsEnv": "readonly",
"expectTemplate": "readonly",
"suite": "readonly",
"test": "readonly",
"testBoth": "readonly",
"raises": "readonly",
"deepEqual": "readonly",
"start": "readonly",
"stop": "readonly",
"ok": "readonly",
"vi": "readonly",
"strictEqual": "readonly",
"define": "readonly",
"expect": "readonly",
"beforeEach": "readonly",
"afterEach": "readonly",
"describe": "readonly",
"it": "readonly"
},
"rules": {
"no-var": "off",
"dot-notation": "off",
"vitest/no-conditional-tests": "off"
}
},
{
"files": ["tasks/**/*.js"],
"rules": {
"node/no-process-env": "off",
"prefer-const": "warn",
"dot-notation": "error"
}
},
{
"files": ["tasks/tests/**/*.js"],
"globals": {
"describe": "readonly",
"it": "readonly",
"expect": "readonly",
"beforeEach": "readonly",
"afterEach": "readonly",
"vi": "readonly"
}
},
{
"files": ["tests/bench/**/*.js"],
"rules": {
"no-console": "off",
"no-var": "off"
}
},
{
"files": ["tests/integration/multi-nodejs-test/**/*.js"],
"rules": {
"no-console": "off",
"no-var": "off"
}
},
{
"files": ["tests/browser/**/*.js"],
"env": {
"browser": true
}
},
{
"files": [
"tests/integration/webpack-babel-test/src/**/*.js",
"tests/integration/webpack-test/src/**/*.js"
],
"env": {
"browser": true
},
"rules": {
"no-var": "off"
}
}
]
}
-21
View File
@@ -1,21 +0,0 @@
.rvmrc
.DS_Store
/tmp/
*.sublime-project
*.sublime-workspace
npm-debug.log
sauce_connect.log*
.idea
yarn-error.log
node_modules
/handlebars-release.tgz
.nyc_output
# Generated files
/coverage/
/dist/
/tests/integration/*/dist/
# Third-party or files that must remain unchanged
/spec/expected/
/spec/vendor
+57 -63
View File
@@ -5,8 +5,7 @@
[![Bundle size](https://badgen.net/bundlephobia/minzip/handlebars?label=minified%20%2B%20gzipped)](https://bundlephobia.com/package/handlebars)
[![Install size](https://packagephobia.com/badge?p=handlebars)](https://packagephobia.com/result?p=handlebars)
Handlebars.js
=============
# Handlebars.js
Handlebars provides the power necessary to let you build **semantic templates** effectively with no frustration.
Handlebars is largely compatible with Mustache templates. In most cases it is possible to swap out Mustache with Handlebars and continue using your current templates.
@@ -14,13 +13,12 @@ Handlebars is largely compatible with Mustache templates. In most cases it is po
Checkout the official Handlebars docs site at
[handlebarsjs.com](https://handlebarsjs.com) and try our [live demo](https://handlebarsjs.com/playground.html).
Installing
----------
## Installing
See our [installation documentation](https://handlebarsjs.com/guide/installation/).
Usage
-----
## Usage
In general, the syntax of Handlebars.js templates is a superset
of Mustache templates. For basic syntax, check out the [Mustache
manpage](https://mustache.github.io/mustache.5.html).
@@ -30,13 +28,20 @@ the template into a function. The generated function takes a context
argument, which will be used to render the template.
```js
var source = "<p>Hello, my name is {{name}}. I am from {{hometown}}. I have " +
"{{kids.length}} kids:</p>" +
"<ul>{{#kids}}<li>{{name}} is {{age}}</li>{{/kids}}</ul>";
var source =
'<p>Hello, my name is {{name}}. I am from {{hometown}}. I have ' +
'{{kids.length}} kids:</p>' +
'<ul>{{#kids}}<li>{{name}} is {{age}}</li>{{/kids}}</ul>';
var template = Handlebars.compile(source);
var data = { "name": "Alan", "hometown": "Somewhere, TX",
"kids": [{"name": "Jimmy", "age": "12"}, {"name": "Sally", "age": "4"}]};
var data = {
name: 'Alan',
hometown: 'Somewhere, TX',
kids: [
{ name: 'Jimmy', age: '12' },
{ name: 'Sally', age: '4' },
],
};
var result = template(data);
// Would render:
@@ -49,13 +54,12 @@ var result = template(data);
Full documentation and more examples are at [handlebarsjs.com](https://handlebarsjs.com/).
Precompiling Templates
----------------------
## Precompiling Templates
Handlebars allows templates to be precompiled and included as javascript code rather than the handlebars template allowing for faster startup time. Full details are located [here](https://handlebarsjs.com/guide/installation/precompilation.html).
Differences Between Handlebars.js and Mustache
----------------------------------------------
## Differences Between Handlebars.js and Mustache
Handlebars.js adds a couple of additional features to make writing
templates easier and also changes a tiny detail of how partials work.
@@ -70,14 +74,13 @@ Block expressions have the same syntax as mustache sections but should not be co
### Compatibility
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.
- 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.
Supported Environments
----------------------
## Supported Environments
Handlebars has been designed to work in any ECMAScript 2020 environment. This includes
@@ -89,9 +92,7 @@ Handlebars has been designed to work in any ECMAScript 2020 environment. This in
If you need to support older environments, use Handlebars version 4.
Performance
-----------
## Performance
In a rough performance test, precompiled Handlebars.js templates (in
the original version of Handlebars.js) rendered in about half the
@@ -103,9 +104,7 @@ does have some big performance advantages. Justin Marney, a.k.a.
rewritten Handlebars (current version) is faster than the old version,
with many performance tests being 5 to 7 times faster than the Mustache equivalent.
Upgrading
---------
## Upgrading
See [release-notes.md](https://github.com/handlebars-lang/handlebars.js/blob/master/release-notes.md) for upgrade notes.
@@ -114,54 +113,49 @@ If you are using Handlebars in production, please regularly look for issues labe
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
See [FAQ.md](https://github.com/handlebars-lang/handlebars.js/blob/master/FAQ.md) for known issues and common pitfalls.
## Handlebars in the Wild
Handlebars in the Wild
----------------------
- [apiDoc](https://github.com/apidoc/apidoc) apiDoc uses handlebars as parsing engine for api documentation view generation.
- [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.
- [CoSchedule](https://coschedule.com) An editorial calendar for WordPress that uses Handlebars.js.
- [Ember.js](https://www.emberjs.com) makes Handlebars.js the primary way to structure your views, also with automatic data binding support.
- [express-handlebars](https://github.com/express-handlebars/express-handlebars) A Handlebars view engine for Express which doesn't suck.
- [express-hbs](https://github.com/TryGhost/express-hbs) Express Handlebars template engine with inheritance, partials, i18n and async helpers.
- [Ghost](https://ghost.org/) Just a blogging platform.
- [handlebars-action](https://github.com/marketplace/actions/handlebars-action) A GitHub action to transform files in your repository with Handlebars templating.
- [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 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-loader](https://github.com/pcardune/handlebars-loader) A handlebars template loader for webpack.
- [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.
- [hbs](https://github.com/pillarjs/hbs) An Express.js view engine adapter for Handlebars.js, from Don Park.
- [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).
- [incremental-bars](https://github.com/atomictag/incremental-bars) adds support for [incremental-dom](https://github.com/google/incremental-dom) as template target to Handlebars.
- [jQuery plugin](https://71104.github.io/jquery-handlebars/) allows you to use Handlebars.js with [jQuery](http://jquery.com/).
- [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.
* [apiDoc](https://github.com/apidoc/apidoc) apiDoc uses handlebars as parsing engine for api documentation view generation.
* [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.
* [CoSchedule](https://coschedule.com) An editorial calendar for WordPress that uses Handlebars.js.
* [Ember.js](https://www.emberjs.com) makes Handlebars.js the primary way to structure your views, also with automatic data binding support.
* [express-handlebars](https://github.com/express-handlebars/express-handlebars) A Handlebars view engine for Express which doesn't suck.
* [express-hbs](https://github.com/TryGhost/express-hbs) Express Handlebars template engine with inheritance, partials, i18n and async helpers.
* [Ghost](https://ghost.org/) Just a blogging platform.
* [handlebars-action](https://github.com/marketplace/actions/handlebars-action) A GitHub action to transform files in your repository with Handlebars templating.
* [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 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-loader](https://github.com/pcardune/handlebars-loader) A handlebars template loader for webpack.
* [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.
* [hbs](https://github.com/pillarjs/hbs) An Express.js view engine adapter for Handlebars.js, from Don Park.
* [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).
* [incremental-bars](https://github.com/atomictag/incremental-bars) adds support for [incremental-dom](https://github.com/google/incremental-dom) as template target to Handlebars.
* [jQuery plugin](https://71104.github.io/jquery-handlebars/) allows you to use Handlebars.js with [jQuery](http://jquery.com/).
* [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
------------------
* [Gist about Synchronous and asynchronous loading of external handlebars templates](https://gist.github.com/2287070)
- [Gist about Synchronous and asynchronous loading of external handlebars templates](https://gist.github.com/2287070)
Have a project using Handlebars? Send us a [pull request][pull-request]!
License
-------
## License
Handlebars.js is released under the MIT license.
[pull-request]: https://github.com/handlebars-lang/handlebars.js/pull/new/master
-2
View File
@@ -297,7 +297,6 @@ The `Handlebars.JavaScriptCompiler` object has a number of methods that may be c
- `nameLookup(parent, name, type)`
Used to generate the code to resolve a given path component.
- `parent` is the existing code in the path resolution
- `name` is the current path component
- `type` is the type of name being evaluated. May be one of `context`, `data`, `helper`, `decorator`, or `partial`.
@@ -312,7 +311,6 @@ The `Handlebars.JavaScriptCompiler` object has a number of methods that may be c
- `appendToBuffer(source, location, explicit)`
Allows for code buffer emitting code. Defaults behavior is string concatenation.
- `source` is the source code whose result is to be appending
- `location` is the location of the source in the source map.
- `explicit` is a flag signaling that the emit operation must occur, vs. the lazy evaled options otherwise.
+17
View File
@@ -0,0 +1,17 @@
import compat from 'eslint-plugin-compat';
export default [
{
// Ignore everything except lib/
ignores: ['**', '!lib/**'],
},
{
// Only check browser API compat in the runtime library code.
// All other linting is handled by oxlint.
...compat.configs['flat/recommended'],
files: ['lib/**/*.js'],
linterOptions: {
reportUnusedDisableDirectives: 'off',
},
},
];
-7
View File
@@ -1,7 +0,0 @@
/* eslint-env node */
module.exports = {
env: {
// Handlebars should run natively in the browser
node: false,
},
};
+2 -1
View File
@@ -8,9 +8,10 @@ try {
if (typeof define !== 'function' || !define.amd) {
// 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.
let SourceMap = require('source-map'); // eslint-disable-line no-undef
let SourceMap = require('source-map');
SourceNode = SourceMap.SourceNode;
}
// oxlint-disable-next-line no-unused-vars -- Babel 5 requires named catch param
} catch (err) {
/* NOP */
}
+1 -3
View File
@@ -1,5 +1,3 @@
/* eslint-disable new-cap */
import { Exception } from '@handlebars/parser';
import { isArray, indexOf, extend } from '../utils';
import AST from './ast';
@@ -74,7 +72,7 @@ Compiler.prototype = {
},
compileProgram: function (program) {
let childCompiler = new this.compiler(), // eslint-disable-line new-cap
let childCompiler = new this.compiler(),
result = childCompiler.compile(program, this.options),
guid = this.guid++;
@@ -158,7 +158,7 @@ JavaScriptCompiler.prototype = {
};
if (this.decorators) {
ret.main_d = this.decorators; // eslint-disable-line camelcase
ret.main_d = this.decorators;
ret.useDecorators = true;
}
@@ -828,7 +828,7 @@ JavaScriptCompiler.prototype = {
for (let i = 0, l = children.length; i < l; i++) {
child = children[i];
compiler = new this.compiler(); // eslint-disable-line new-cap
compiler = new this.compiler();
let existing = this.matchExistingProgram(child);
-1
View File
@@ -1,6 +1,5 @@
// USAGE:
// var handlebars = require('handlebars');
/* eslint-env node */
/* eslint-disable no-var */
// var local = handlebars.create();
-1
View File
@@ -1,4 +1,3 @@
/* eslint-env node */
/* eslint-disable no-console */
import Async from 'neo-async';
import fs from 'fs';
+2530 -2954
View File
File diff suppressed because it is too large Load Diff
+69 -70
View File
@@ -2,21 +2,54 @@
"name": "handlebars",
"version": "5.0.0-alpha.1",
"description": "Handlebars provides the power necessary to let you build semantic templates effectively with no frustration",
"homepage": "https://handlebarsjs.com/",
"keywords": [
"handlebars",
"html",
"mustache",
"template",
"html"
"template"
],
"homepage": "https://handlebarsjs.com/",
"license": "MIT",
"author": "Yehuda Katz",
"repository": {
"type": "git",
"url": "https://github.com/handlebars-lang/handlebars.js.git"
},
"author": "Yehuda Katz",
"license": "MIT",
"engines": {
"node": ">=20"
"bin": {
"handlebars": "bin/handlebars.js"
},
"files": [
"bin",
"dist/*.js",
"dist/cjs/**/*.js",
"lib",
"release-notes.md",
"runtime.js",
"types/*.d.ts",
"runtime.d.ts"
],
"main": "lib/index.js",
"browser": "./dist/cjs/handlebars.js",
"types": "types/index.d.ts",
"scripts": {
"build": "grunt build",
"release": "grunt release",
"publish:aws": "grunt && npm run test:tasks && grunt publish-to-aws",
"format": "oxfmt --write . && oxlint --fix .",
"lint": "npm run lint:oxlint && npm run lint:format && npm run lint:types && npm run lint:compat",
"lint:oxlint": "oxlint --max-warnings 0 .",
"lint:format": "oxfmt --check .",
"lint:types": "tsc --noEmit --project types",
"lint:compat": "eslint",
"test": "grunt build && vitest run --project node --project tasks --coverage",
"test:browser": "vitest run --project browser",
"test:unit": "vitest run --project node",
"test:tasks": "vitest run --project tasks",
"test:browser-smoke": "playwright test --config tests/browser/playwright.config.js",
"test:integration": "grunt integration-tests",
"test:serve": "grunt connect:server:keepalive",
"--- combined tasks ---": "",
"check-before-pull-request": "concurrently --kill-others-on-fail npm:lint npm:test"
},
"dependencies": {
"@handlebars/parser": "^2.1.0",
@@ -24,9 +57,6 @@
"source-map": "^0.6.1",
"yargs": "^16.2.0"
},
"peerDependencies": {
"uglify-js": "^3.1.4"
},
"devDependencies": {
"@playwright/test": "^1.58.2",
"@vitest/browser": "^4.0.18",
@@ -38,9 +68,8 @@
"benchmark": "~1.0",
"concurrently": "^5.0.0",
"dustjs-linkedin": "^2.0.2",
"eslint": "^8.25.0",
"eslint-config-prettier": "^8.9.0",
"eslint-plugin-compat": "4.0",
"eslint": "^10.0.3",
"eslint-plugin-compat": "^7.0.1",
"fs-extra": "^8.1.0",
"grunt": "^1.0.4",
"grunt-babel": "^5.0.0",
@@ -55,10 +84,11 @@
"grunt-shell": "^4.0.0",
"grunt-webpack": "^1.0.8",
"husky": "^3.1.0",
"lint-staged": "^9.5.0",
"lint-staged": "^16.3.2",
"mock-stdin": "^0.3.0",
"mustache": "^2.1.3",
"prettier": "^3.0.0",
"oxfmt": "^0.36.0",
"oxlint": "^1.51.0",
"semver": "^5.0.1",
"typescript": "^3.4.3",
"uglify-js": "^3.1.4",
@@ -67,30 +97,31 @@
"webpack": "^1.12.6",
"webpack-dev-server": "^1.12.1"
},
"main": "lib/index.js",
"types": "types/index.d.ts",
"browser": "./dist/cjs/handlebars.js",
"bin": {
"handlebars": "bin/handlebars.js"
"peerDependencies": {
"uglify-js": "^3.1.4"
},
"scripts": {
"build": "grunt build",
"release": "grunt release",
"publish:aws": "grunt && npm run test:tasks && 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:eslint": "eslint --max-warnings 0 .",
"lint:prettier": "prettier --check '**/*.js'",
"lint:types": "tsc --noEmit --project types",
"test": "grunt build && vitest run --project node --project tasks --coverage",
"test:browser": "vitest run --project browser",
"test:unit": "vitest run --project node",
"test:tasks": "vitest run --project tasks",
"test:browser-smoke": "playwright test --config tests/browser/playwright.config.js",
"test:integration": "grunt integration-tests",
"test:serve": "grunt connect:server:keepalive",
"--- combined tasks ---": "",
"check-before-pull-request": "concurrently --kill-others-on-fail npm:lint npm:test"
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,css,json,md}": [
"oxfmt --write"
],
"*.js": [
"oxlint --fix"
]
},
"browserslist": [
"last 2 versions",
"Firefox ESR",
"not dead",
"not IE 11",
"maintained node versions"
],
"engines": {
"node": ">=20"
},
"jspm": {
"main": "handlebars",
@@ -100,37 +131,5 @@
"buildConfig": {
"minify": true
}
},
"files": [
"bin",
"dist/*.js",
"dist/cjs/**/*.js",
"lib",
"release-notes.md",
"runtime.js",
"types/*.d.ts",
"runtime.d.ts"
],
"browserslist": [
"last 2 versions",
"Firefox ESR",
"not dead",
"not IE 11",
"maintained node versions"
],
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,css,json,md}": [
"prettier --write",
"git add"
],
"*.js": [
"eslint --fix",
"git add"
]
}
}
-6
View File
@@ -1,6 +0,0 @@
module.exports = {
tabWidth: 2,
semi: true,
singleQuote: true,
trailingComma: 'es5',
};
-1
View File
@@ -356,7 +356,6 @@ Breaking changes:
Compatibility notes:
- Compiler revision increased - 06b7224
- This means that template compiled with versions prior to 4.3.0 will not work with runtimes >= 4.3.0
The increase was done because the "helperMissing" and "blockHelperMissing" are now moved from the helpers
to the internal "container.hooks" object, so old templates will not be able to call them anymore. We suggest
+2 -4
View File
@@ -1,5 +1,3 @@
import Handlebars = require('handlebars')
import Handlebars = require('handlebars');
declare module "handlebars/runtime" {
}
declare module 'handlebars/runtime' {}
-31
View File
@@ -1,31 +0,0 @@
module.exports = {
globals: {
CompilerContext: true,
Handlebars: true,
handlebarsEnv: true,
expectTemplate: true,
suite: true,
test: true,
testBoth: true,
raises: true,
deepEqual: true,
start: true,
stop: true,
ok: true,
vi: true,
strictEqual: true,
define: true,
expect: true,
beforeEach: true,
afterEach: true,
describe: true,
it: true,
},
rules: {
// Disabling for tests, for now.
'no-path-concat': 'off',
'no-var': 'off',
'dot-notation': 'off',
},
};
-2
View File
@@ -115,7 +115,6 @@ describe('ast', function () {
expect(node.loc.end.column).toBe(lastColumn);
}
/* eslint-disable no-multi-spaces */
ast = Handlebars.parse(
'line 1 {{line1Token}}\n' + // 1
' line 2 {{line2token}}\n' + // 2
@@ -129,7 +128,6 @@ describe('ast', function () {
'{{else}}\n' + // 10
'{{/open}}'
); // 11
/* eslint-enable no-multi-spaces */
body = ast.body;
it('gets ContentNode line numbers', function () {
+16 -6
View File
@@ -2,8 +2,8 @@
<html>
<head>
<title>Handlebars UMD Smoke Test</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<h1>Handlebars UMD Smoke Test</h1>
@@ -26,9 +26,18 @@
try {
assert(typeof Handlebars !== 'undefined', 'Handlebars is defined');
assert(typeof Handlebars.compile === 'function', 'Handlebars.compile exists');
assert(typeof Handlebars.template === 'function', 'Handlebars.template exists');
assert(typeof Handlebars.VERSION === 'string', 'Handlebars.VERSION exists');
assert(
typeof Handlebars.compile === 'function',
'Handlebars.compile exists'
);
assert(
typeof Handlebars.template === 'function',
'Handlebars.template exists'
);
assert(
typeof Handlebars.VERSION === 'string',
'Handlebars.VERSION exists'
);
var template = Handlebars.compile('Hello {{name}}!');
var output = template({ name: 'World' });
@@ -38,7 +47,8 @@
results.textContent += 'ERROR: ' + e.message + '\n';
}
results.textContent += '\n' + tests + ' tests, ' + failures + ' failures\n';
results.textContent +=
'\n' + tests + ' tests, ' + failures + ' failures\n';
window.mochaResults = { passes: tests - failures, failures: failures };
</script>
</body>
-2
View File
@@ -19,11 +19,9 @@ describe('javascript-compiler api', function () {
) {
return parent + '.bar_' + name;
};
/* eslint-disable camelcase */
expectTemplate('{{foo}}')
.withInput({ bar_foo: 'food' })
.toCompileTo('food');
/* eslint-enable camelcase */
});
// Tests nameLookup dot vs. bracket behavior. Bracket is required in certain cases
+1 -2
View File
@@ -165,8 +165,7 @@ describe('partials', function () {
it('registering undefined partial throws an exception', function () {
expect(function () {
var undef;
handlebarsEnv.registerPartial('undefined_test', undef);
handlebarsEnv.registerPartial('undefined_test', undefined);
}).toThrow(
'Attempting to register a partial called "undefined_test" as undefined'
);
-1
View File
@@ -274,7 +274,6 @@ describe('precompiler', function () {
describe('#loadTemplates', function () {
function loadTemplatesAsync(inputOpts) {
// eslint-disable-next-line compat/compat
return new Promise(function (resolve, reject) {
Precompiler.loadTemplates(inputOpts, function (err, opts) {
if (err) {
+1 -1
View File
@@ -3,7 +3,7 @@ try {
var SourceMap = require('source-map'),
SourceMapConsumer = SourceMap.SourceMapConsumer;
}
} catch (err) {
} catch {
/* NOP for in browser */
}
+19 -9
View File
@@ -2,8 +2,8 @@
<html>
<head>
<title>Handlebars Runtime UMD Smoke Test</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<h1>Handlebars Runtime UMD Smoke Test</h1>
@@ -26,21 +26,31 @@
requirejs.config({
paths: {
'handlebars.runtime': '/dist/handlebars.runtime'
}
'handlebars.runtime': '/dist/handlebars.runtime',
},
});
require(['handlebars.runtime'], function(Handlebars) {
require(['handlebars.runtime'], function (Handlebars) {
try {
assert(typeof Handlebars !== 'undefined', 'Handlebars runtime loaded via RequireJS');
assert(typeof Handlebars.template === 'function', 'Handlebars.template exists');
assert(typeof Handlebars.VERSION === 'string', 'Handlebars.VERSION exists');
assert(
typeof Handlebars !== 'undefined',
'Handlebars runtime loaded via RequireJS'
);
assert(
typeof Handlebars.template === 'function',
'Handlebars.template exists'
);
assert(
typeof Handlebars.VERSION === 'string',
'Handlebars.VERSION exists'
);
} catch (e) {
failures++;
results.textContent += 'ERROR: ' + e.message + '\n';
}
results.textContent += '\n' + tests + ' tests, ' + failures + ' failures\n';
results.textContent +=
'\n' + tests + ' tests, ' + failures + ' failures\n';
window.mochaResults = { passes: tests - failures, failures: failures };
});
</script>
+23 -10
View File
@@ -2,8 +2,8 @@
<html>
<head>
<title>Handlebars UMD Module Smoke Test</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<h1>Handlebars UMD Module Smoke Test</h1>
@@ -26,16 +26,28 @@
requirejs.config({
paths: {
handlebars: '/dist/handlebars'
}
handlebars: '/dist/handlebars',
},
});
require(['handlebars'], function(Handlebars) {
require(['handlebars'], function (Handlebars) {
try {
assert(typeof Handlebars !== 'undefined', 'Handlebars loaded via RequireJS');
assert(typeof Handlebars.compile === 'function', 'Handlebars.compile exists');
assert(typeof Handlebars.template === 'function', 'Handlebars.template exists');
assert(typeof Handlebars.VERSION === 'string', 'Handlebars.VERSION exists');
assert(
typeof Handlebars !== 'undefined',
'Handlebars loaded via RequireJS'
);
assert(
typeof Handlebars.compile === 'function',
'Handlebars.compile exists'
);
assert(
typeof Handlebars.template === 'function',
'Handlebars.template exists'
);
assert(
typeof Handlebars.VERSION === 'string',
'Handlebars.VERSION exists'
);
var template = Handlebars.compile('Hello {{name}}!');
var output = template({ name: 'UMD' });
@@ -45,7 +57,8 @@
results.textContent += 'ERROR: ' + e.message + '\n';
}
results.textContent += '\n' + tests + ' tests, ' + failures + ' failures\n';
results.textContent +=
'\n' + tests + ' tests, ' + failures + ' failures\n';
window.mochaResults = { passes: tests - failures, failures: failures };
});
</script>
-8
View File
@@ -1,8 +0,0 @@
module.exports = {
rules: {
'no-process-env': 'off',
'prefer-const': 'warn',
'compat/compat': 'off',
'dot-notation': ['error', { allowKeywords: true }],
},
};
+1 -1
View File
@@ -9,7 +9,7 @@ module.exports = function (grunt) {
const events = {};
const promises = Object.keys(metrics).map(async (name) => {
if (/^_/.test(name)) {
if (name.startsWith('_')) {
return;
}
if (onlyExecuteName != null && name !== onlyExecuteName) {
-10
View File
@@ -1,10 +0,0 @@
module.exports = {
globals: {
describe: true,
it: true,
expect: true,
beforeEach: true,
afterEach: true,
vi: true,
},
};
+1 -1
View File
@@ -52,7 +52,7 @@ async function getTagName() {
}
const tags = trimmedStdout.split(/\n|\r\n/);
const versionTags = tags.filter((tag) => /^v/.test(tag));
const versionTags = tags.filter((tag) => tag.startsWith('v'));
if (versionTags[0] != null) {
return versionTags[0];
}
-6
View File
@@ -1,6 +0,0 @@
module.exports = {
rules: {
'no-console': 'off',
'no-var': 'off',
},
};
+2 -2
View File
@@ -6,13 +6,13 @@ var _ = require('underscore'),
try {
dust = require('dustjs-linkedin');
} catch (err) {
} catch {
/* NOP */
}
try {
Mustache = require('mustache');
} catch (err) {
} catch {
/* NOP */
}
+2 -2
View File
@@ -165,7 +165,7 @@ BenchWarmer.prototype = {
horSize = horSize + 'WINNER(S)'.length;
}
console.log('\n' + new Array(horSize + 1).join('-'));
console.log('\n' + '-'.repeat(horSize));
},
startLine: function (name) {
@@ -214,7 +214,7 @@ BenchWarmer.prototype = {
writeValue: function (out) {
var padding = this.benchSize - out.length + 1;
out = out + new Array(padding).join(' ');
out = out + ' '.repeat(Math.max(0, padding - 1));
console.log(out);
},
};
-5
View File
@@ -1,5 +0,0 @@
module.exports = {
parserOptions: {
ecmaVersion: 2018,
},
};
@@ -1,6 +0,0 @@
module.exports = {
rules: {
'no-console': 'off',
'no-var': 'off',
},
};
@@ -1,16 +1,16 @@
{
"name": "multi-nodejs-test",
"version": "1.0.0",
"private": true,
"description": "Simple integration test with all relevant NodeJS-versions.",
"keywords": [],
"author": "Nils Knappmeier",
"private": true,
"license": "MIT",
"dependencies": {
"handlebars": "file:../../.."
},
"author": "Nils Knappmeier",
"scripts": {
"test": "node run-handlebars.js",
"test-precompile": "handlebars precompile-test-template.txt.hbs"
},
"dependencies": {
"handlebars": "file:../../.."
}
}
+2 -2
View File
@@ -1,11 +1,11 @@
{
"name": "rollup-test",
"description": "Various tests with Handlebars and rollup",
"version": "1.0.0",
"private": true,
"description": "Various tests with Handlebars and rollup",
"scripts": {
"build": "rollup --config rollup.config.js"
},
"private": true,
"devDependencies": {
"@rollup/plugin-node-resolve": "^13.1.1",
"handlebars": "file:../../..",
@@ -1,11 +1,7 @@
{
"plugins": [
"@roundingwellos/babel-plugin-handlebars-inline-precompile"
// "istanbul"
// "istanbul"
],
"presets": [
[
"@babel/preset-env"
]
]
}
"presets": [["@babel/preset-env"]]
}
@@ -1,8 +1,8 @@
{
"name": "webpack-babel-test",
"description": "",
"version": "1.0.0",
"private": true,
"description": "",
"scripts": {
"build": "webpack --config webpack.config.js"
},
@@ -1,12 +0,0 @@
/* eslint-env node */
module.exports = {
root: true,
extends: ['eslint:recommended', 'prettier'],
env: {
browser: true,
},
parserOptions: {
sourceType: 'module',
ecmaVersion: 6,
},
};
+1 -1
View File
@@ -1,8 +1,8 @@
{
"name": "webpack-test",
"description": "Various tests with Handlebars and multiple webpack versions",
"version": "1.0.0",
"private": true,
"description": "Various tests with Handlebars and multiple webpack versions",
"scripts": {
"build": "webpack --config webpack.config.js"
},
@@ -1,12 +0,0 @@
module.exports = {
root: true,
extends: ['eslint:recommended', 'prettier'],
env: {
node: true,
browser: true,
},
parserOptions: {
sourceType: 'module',
ecmaVersion: 6,
},
};
+127 -109
View File
@@ -14,46 +14,54 @@
*/
// TypeScript Version: 2.3
import {
parse,
parseWithoutProcessing,
ParseOptions,
AST
parse,
parseWithoutProcessing,
ParseOptions,
AST,
} from '@handlebars/parser';
declare namespace Handlebars {
export interface TemplateDelegate<T = any> {
(context: T, options?: RuntimeOptions): string;
(context: T, options?: RuntimeOptions): string;
}
export type Template<T = any> = TemplateDelegate<T>|string;
export type Template<T = any> = TemplateDelegate<T> | string;
export interface RuntimeOptions {
partial?: boolean;
depths?: any[];
helpers?: { [name: string]: Function };
partials?: { [name: string]: Template };
decorators?: { [name: string]: Function };
data?: any;
blockParams?: any[];
allowCallsToHelperMissing?: boolean;
allowedProtoProperties?: { [name: string]: boolean };
allowedProtoMethods?: { [name: string]: boolean };
allowProtoPropertiesByDefault?: boolean;
allowProtoMethodsByDefault?: boolean;
partial?: boolean;
depths?: any[];
helpers?: { [name: string]: Function };
partials?: { [name: string]: Template };
decorators?: { [name: string]: Function };
data?: any;
blockParams?: any[];
allowCallsToHelperMissing?: boolean;
allowedProtoProperties?: { [name: string]: boolean };
allowedProtoMethods?: { [name: string]: boolean };
allowProtoPropertiesByDefault?: boolean;
allowProtoMethodsByDefault?: boolean;
}
export interface HelperOptions {
fn: TemplateDelegate;
inverse: TemplateDelegate;
hash: Record<string, any>;
data?: any;
fn: TemplateDelegate;
inverse: TemplateDelegate;
hash: Record<string, any>;
data?: any;
}
export interface HelperDelegate {
(context?: any, arg1?: any, arg2?: any, arg3?: any, arg4?: any, arg5?: any, options?: HelperOptions): any;
(
context?: any,
arg1?: any,
arg2?: any,
arg3?: any,
arg4?: any,
arg5?: any,
options?: HelperOptions
): any;
}
export interface HelperDeclareSpec {
[key: string]: HelperDelegate;
[key: string]: HelperDelegate;
}
export { parse, parseWithoutProcessing, ParseOptions };
@@ -75,9 +83,17 @@ declare namespace Handlebars {
export function blockParams(obj: any[], ids: any[]): any[];
export function log(level: number, obj: any): void;
export function compile<T = any>(input: any, options?: CompileOptions): HandlebarsTemplateDelegate<T>;
export function precompile(input: any, options?: PrecompileOptions): TemplateSpecification;
export function template<T = any>(precompilation: TemplateSpecification): HandlebarsTemplateDelegate<T>;
export function compile<T = any>(
input: any,
options?: CompileOptions
): HandlebarsTemplateDelegate<T>;
export function precompile(
input: any,
options?: PrecompileOptions
): TemplateSpecification;
export function template<T = any>(
precompilation: TemplateSpecification
): HandlebarsTemplateDelegate<T>;
export function create(): typeof Handlebars;
@@ -95,87 +111,86 @@ declare namespace Handlebars {
export function noConflict(): typeof Handlebars;
export class Exception {
constructor(message: string, node?: hbs.AST.Node);
description: string;
fileName: string;
lineNumber?: any;
endLineNumber?: any;
message: string;
name: string;
number: number;
stack?: string;
column?: any;
endColumn?: any;
constructor(message: string, node?: hbs.AST.Node);
description: string;
fileName: string;
lineNumber?: any;
endLineNumber?: any;
message: string;
name: string;
number: number;
stack?: string;
column?: any;
endColumn?: any;
}
export class SafeString {
constructor(str: string);
toString(): string;
toHTML(): string;
constructor(str: string);
toString(): string;
toHTML(): string;
}
export namespace Utils {
export function escapeExpression(str: string): string;
export function createFrame(object: any): any;
export function blockParams(obj: any[], ids: any[]): any[];
export function isEmpty(obj: any) : boolean;
export function extend(obj: any, ...source: any[]): any;
export function toString(obj: any): string;
export function isArray(obj: any): boolean;
export function isFunction(obj: any): boolean;
export function isMap(obj: any): boolean;
export function isSet(obj: any): boolean;
export function escapeExpression(str: string): string;
export function createFrame(object: any): any;
export function blockParams(obj: any[], ids: any[]): any[];
export function isEmpty(obj: any): boolean;
export function extend(obj: any, ...source: any[]): any;
export function toString(obj: any): string;
export function isArray(obj: any): boolean;
export function isFunction(obj: any): boolean;
export function isMap(obj: any): boolean;
export function isSet(obj: any): boolean;
}
export namespace AST {
export const helpers: hbs.AST.helpers;
export const helpers: hbs.AST.helpers;
}
export interface ICompiler {
accept(node: hbs.AST.Node): void;
Program(program: hbs.AST.Program): void;
BlockStatement(block: hbs.AST.BlockStatement): void;
PartialStatement(partial: hbs.AST.PartialStatement): void;
PartialBlockStatement(partial: hbs.AST.PartialBlockStatement): void;
DecoratorBlock(decorator: hbs.AST.DecoratorBlock): void;
Decorator(decorator: hbs.AST.Decorator): void;
MustacheStatement(mustache: hbs.AST.MustacheStatement): void;
ContentStatement(content: hbs.AST.ContentStatement): void;
CommentStatement(comment?: hbs.AST.CommentStatement): void;
SubExpression(sexpr: hbs.AST.SubExpression): void;
PathExpression(path: hbs.AST.PathExpression): void;
StringLiteral(str: hbs.AST.StringLiteral): void;
NumberLiteral(num: hbs.AST.NumberLiteral): void;
BooleanLiteral(bool: hbs.AST.BooleanLiteral): void;
UndefinedLiteral(): void;
NullLiteral(): void;
Hash(hash: hbs.AST.Hash): void;
accept(node: hbs.AST.Node): void;
Program(program: hbs.AST.Program): void;
BlockStatement(block: hbs.AST.BlockStatement): void;
PartialStatement(partial: hbs.AST.PartialStatement): void;
PartialBlockStatement(partial: hbs.AST.PartialBlockStatement): void;
DecoratorBlock(decorator: hbs.AST.DecoratorBlock): void;
Decorator(decorator: hbs.AST.Decorator): void;
MustacheStatement(mustache: hbs.AST.MustacheStatement): void;
ContentStatement(content: hbs.AST.ContentStatement): void;
CommentStatement(comment?: hbs.AST.CommentStatement): void;
SubExpression(sexpr: hbs.AST.SubExpression): void;
PathExpression(path: hbs.AST.PathExpression): void;
StringLiteral(str: hbs.AST.StringLiteral): void;
NumberLiteral(num: hbs.AST.NumberLiteral): void;
BooleanLiteral(bool: hbs.AST.BooleanLiteral): void;
UndefinedLiteral(): void;
NullLiteral(): void;
Hash(hash: hbs.AST.Hash): void;
}
export class Visitor implements ICompiler {
accept(node: hbs.AST.Node): void;
acceptKey(node: hbs.AST.Node, name: string): void;
acceptArray(arr: hbs.AST.Expression[]): void;
Program(program: hbs.AST.Program): void;
BlockStatement(block: hbs.AST.BlockStatement): void;
PartialStatement(partial: hbs.AST.PartialStatement): void;
PartialBlockStatement(partial: hbs.AST.PartialBlockStatement): void;
DecoratorBlock(decorator: hbs.AST.DecoratorBlock): void;
Decorator(decorator: hbs.AST.Decorator): void;
MustacheStatement(mustache: hbs.AST.MustacheStatement): void;
ContentStatement(content: hbs.AST.ContentStatement): void;
CommentStatement(comment?: hbs.AST.CommentStatement): void;
SubExpression(sexpr: hbs.AST.SubExpression): void;
PathExpression(path: hbs.AST.PathExpression): void;
StringLiteral(str: hbs.AST.StringLiteral): void;
NumberLiteral(num: hbs.AST.NumberLiteral): void;
BooleanLiteral(bool: hbs.AST.BooleanLiteral): void;
UndefinedLiteral(): void;
NullLiteral(): void;
Hash(hash: hbs.AST.Hash): void;
accept(node: hbs.AST.Node): void;
acceptKey(node: hbs.AST.Node, name: string): void;
acceptArray(arr: hbs.AST.Expression[]): void;
Program(program: hbs.AST.Program): void;
BlockStatement(block: hbs.AST.BlockStatement): void;
PartialStatement(partial: hbs.AST.PartialStatement): void;
PartialBlockStatement(partial: hbs.AST.PartialBlockStatement): void;
DecoratorBlock(decorator: hbs.AST.DecoratorBlock): void;
Decorator(decorator: hbs.AST.Decorator): void;
MustacheStatement(mustache: hbs.AST.MustacheStatement): void;
ContentStatement(content: hbs.AST.ContentStatement): void;
CommentStatement(comment?: hbs.AST.CommentStatement): void;
SubExpression(sexpr: hbs.AST.SubExpression): void;
PathExpression(path: hbs.AST.PathExpression): void;
StringLiteral(str: hbs.AST.StringLiteral): void;
NumberLiteral(num: hbs.AST.NumberLiteral): void;
BooleanLiteral(bool: hbs.AST.BooleanLiteral): void;
UndefinedLiteral(): void;
NullLiteral(): void;
Hash(hash: hbs.AST.Hash): void;
}
export interface ResolvePartialOptions {
name: string;
helpers?: { [name: string]: Function };
@@ -188,27 +203,30 @@ declare namespace Handlebars {
/**
* @deprecated
*/
export function resolvePartial<T = any>(partial: HandlebarsTemplateDelegate<T> | undefined, context: any, options: ResolvePartialOptions): HandlebarsTemplateDelegate<T>;
export function resolvePartial<T = any>(
partial: HandlebarsTemplateDelegate<T> | undefined,
context: any,
options: ResolvePartialOptions
): HandlebarsTemplateDelegate<T>;
}
}
/**
* Implement this interface on your MVW/MVVM/MVC views such as Backbone.View
**/
* Implement this interface on your MVW/MVVM/MVC views such as Backbone.View
**/
export interface HandlebarsTemplatable {
template: HandlebarsTemplateDelegate;
}
// NOTE: for backward compatibility of this typing
export type HandlebarsTemplateDelegate<T = any> = Handlebars.TemplateDelegate<T>;
export type HandlebarsTemplateDelegate<T = any> =
Handlebars.TemplateDelegate<T>;
export interface HandlebarsTemplates {
[index: string]: HandlebarsTemplateDelegate;
}
export interface TemplateSpecification {
}
export interface TemplateSpecification {}
// for backward compatibility of this typing
export type RuntimeOptions = Handlebars.RuntimeOptions;
@@ -231,14 +249,14 @@ export type KnownHelpers = {
};
export type BuiltinHelperName =
"helperMissing"|
"blockHelperMissing"|
"each"|
"if"|
"unless"|
"with"|
"log"|
"lookup";
| 'helperMissing'
| 'blockHelperMissing'
| 'each'
| 'if'
| 'unless'
| 'with'
| 'log'
| 'lookup';
export type CustomHelperName = string;
@@ -253,7 +271,7 @@ export namespace hbs {
export type Utils = typeof Handlebars.Utils;
export { AST }
export { AST };
}
export interface Logger {
@@ -268,9 +286,9 @@ export interface Logger {
log(level: number, obj: string): void;
}
export type CompilerInfo = [number/* revision */, string /* versions */];
export type CompilerInfo = [number /* revision */, string /* versions */];
declare module "handlebars/runtime" {
declare module 'handlebars/runtime' {
export = Handlebars;
}
+120 -84
View File
@@ -10,184 +10,213 @@ import { HandlebarsTemplateDelegate, hbs } from 'handlebars';
const context = {
author: { firstName: 'Alan', lastName: 'Johnson' },
body: 'I Love Handlebars',
comments: [{
author: { firstName: 'Yehuda', lastName: 'Katz' },
body: 'Me too!'
}]
comments: [
{
author: { firstName: 'Yehuda', lastName: 'Katz' },
body: 'Me too!',
},
],
};
Handlebars.registerHelper('fullName', (person: typeof context.author) => {
return person.firstName + ' ' + person.lastName;
});
Handlebars.registerHelper('agree_button', function(this: any) {
Handlebars.registerHelper('agree_button', function (this: any) {
return new Handlebars.SafeString(
'<button>I agree. I ' + this.emotion + ' ' + this.name + '</button>'
);
});
const source1 = '<p>Hello, my name is {{name}}. I am from {{hometown}}. I have ' +
'{{kids.length}} kids:</p>' +
'<ul>{{#kids}}<li>{{name}} is {{age}}</li>{{/kids}}</ul>';
const source1 =
'<p>Hello, my name is {{name}}. I am from {{hometown}}. I have ' +
'{{kids.length}} kids:</p>' +
'<ul>{{#kids}}<li>{{name}} is {{age}}</li>{{/kids}}</ul>';
const template1 = Handlebars.compile(source1);
template1({ name: "Alan", hometown: "Somewhere, TX", kids: [{name: "Jimmy", age: 12}, {name: "Sally", age: 4}]});
template1({
name: 'Alan',
hometown: 'Somewhere, TX',
kids: [
{ name: 'Jimmy', age: 12 },
{ name: 'Sally', age: 4 },
],
});
Handlebars.registerHelper('link_to', (context: typeof post) => {
return '<a href="' + context.url + '">' + context.body + '</a>';
});
const post = { url: "/hello-world", body: "Hello World!" };
const post = { url: '/hello-world', body: 'Hello World!' };
const context2 = { posts: [post] };
const source2 = '<ul>{{#posts}}<li>{{{link_to this}}}</li>{{/posts}}</ul>';
const template2: HandlebarsTemplateDelegate<{ posts: { url: string, body: string }[] }> = Handlebars.compile(source2);
const template2: HandlebarsTemplateDelegate<{
posts: { url: string; body: string }[];
}> = Handlebars.compile(source2);
template2(context2);
Handlebars.registerHelper('link_to', (title: string, context: typeof post) => {
return '<a href="/posts' + context.url + '">' + title + '!</a>';
});
const context3 = { posts: [{url: '/hello-world', body: 'Hello World!'}] };
const source3 = '<ul>{{#posts}}<li>{{{link_to "Post" this}}}</li>{{/posts}}</ul>';
const context3 = { posts: [{ url: '/hello-world', body: 'Hello World!' }] };
const source3 =
'<ul>{{#posts}}<li>{{{link_to "Post" this}}}</li>{{/posts}}</ul>';
const template3 = Handlebars.compile<typeof context3>(source3);
template3(context3);
const source4 = '<ul>{{#people}}<li>{{#link}}{{name}}{{/link}}</li>{{/people}}</ul>';
Handlebars.registerHelper('link', function(this: any, context: any) {
const source4 =
'<ul>{{#people}}<li>{{#link}}{{name}}{{/link}}</li>{{/people}}</ul>';
Handlebars.registerHelper('link', function (this: any, context: any) {
return '<a href="/people/' + this.id + '">' + context.fn(this) + '</a>';
});
const template4 = Handlebars.compile<{ people: { name: string, id: number }[] }>(source4);
const data2 = { 'people': [
{ 'name': 'Alan', 'id': 1 },
{ 'name': 'Yehuda', 'id': 2 }
]};
const template4 = Handlebars.compile<{
people: { name: string; id: number }[];
}>(source4);
const data2 = {
people: [
{ name: 'Alan', id: 1 },
{ name: 'Yehuda', id: 2 },
],
};
template4(data2);
const source5 = '<ul>{{#people}}<li>{{> link}}</li>{{/people}}</ul>';
Handlebars.registerPartial('link', '<a href="/people/{{id}}">{{name}}</a>');
const template5 = Handlebars.compile(source5);
const data3 = { 'people': [
{ 'name': 'Alan', 'id': 1 },
{ 'name': 'Yehuda', 'id': 2 }
]};
const data3 = {
people: [
{ name: 'Alan', id: 1 },
{ name: 'Yehuda', id: 2 },
],
};
template5(data3);
const source6 = '{{#list nav}}<a href="{{url}}">{{title}}</a>{{/list}}';
const template6 = Handlebars.compile(source6);
Handlebars.registerHelper('list', (context, options: Handlebars.HelperOptions) => {
let ret = "<ul>";
for(let i=0, j=context.length; i<j; i++) {
ret = ret + "<li>" + options.fn(context[i]) + "</li>";
Handlebars.registerHelper(
'list',
(context, options: Handlebars.HelperOptions) => {
let ret = '<ul>';
for (let i = 0, j = context.length; i < j; i++) {
ret = ret + '<li>' + options.fn(context[i]) + '</li>';
}
return ret + '</ul>';
}
return ret + "</ul>";
});
template6([{url:"", title:""}]);
);
template6([{ url: '', title: '' }]);
const escapedExpression = Handlebars.Utils.escapeExpression('<script>alert(\'xss\');</script>');
const escapedExpression = Handlebars.Utils.escapeExpression(
"<script>alert('xss');</script>"
);
Handlebars.helpers !== undefined;
const parsedTmpl = Handlebars.parse('<p>Hello, my name is {{name}}.</p>', {
srcName: "/foo/bar/baz.hbs",
ignoreStandalone: true
srcName: '/foo/bar/baz.hbs',
ignoreStandalone: true,
});
const parsedTmplWithoutOptions = Handlebars.parse('<p>Hello, my name is {{name}}.</p>');
const parsedTmplWithoutOptions = Handlebars.parse(
'<p>Hello, my name is {{name}}.</p>'
);
// Custom partial resolution.
const originalResolvePartial = Handlebars.VM.resolvePartial;
Handlebars.VM.resolvePartial = <T>(partial: HandlebarsTemplateDelegate<T> | undefined, context: any, options: Handlebars.ResolvePartialOptions): HandlebarsTemplateDelegate<T> => {
const name = options.name.replace(/my/,'your');
Handlebars.VM.resolvePartial = <T>(
partial: HandlebarsTemplateDelegate<T> | undefined,
context: any,
options: Handlebars.ResolvePartialOptions
): HandlebarsTemplateDelegate<T> => {
const name = options.name.replace(/my/, 'your');
// transform name.
options.name = name;
return originalResolvePartial(partial, context, options);
};
// #1544, allow custom helpers in knownHelpers
Handlebars.compile('test', {
knownHelpers: {
each: true,
customHelper: true
}
customHelper: true,
},
});
Handlebars.compile('test')({},{allowCallsToHelperMissing: true});
Handlebars.compile('test')({}, { allowCallsToHelperMissing: true });
Handlebars.compile('test')({},{});
Handlebars.compile('test')({}, {});
const allthings = {} as
| hbs.AST.MustacheStatement
| hbs.AST.BlockStatement
| hbs.AST.PartialStatement
| hbs.AST.PartialBlockStatement
| hbs.AST.ContentStatement
| hbs.AST.CommentStatement
| hbs.AST.SubExpression
| hbs.AST.PathExpression
| hbs.AST.StringLiteral
| hbs.AST.BooleanLiteral
| hbs.AST.NumberLiteral
| hbs.AST.UndefinedLiteral
| hbs.AST.NullLiteral
| hbs.AST.Hash
| hbs.AST.HashPair;
const allthings = {} as hbs.AST.MustacheStatement |
hbs.AST.BlockStatement |
hbs.AST.PartialStatement |
hbs.AST.PartialBlockStatement |
hbs.AST.ContentStatement |
hbs.AST.CommentStatement |
hbs.AST.SubExpression |
hbs.AST.PathExpression |
hbs.AST.StringLiteral |
hbs.AST.BooleanLiteral |
hbs.AST.NumberLiteral |
hbs.AST.UndefinedLiteral |
hbs.AST.NullLiteral |
hbs.AST.Hash |
hbs.AST.HashPair;
switch(allthings.type) {
case "MustacheStatement":
switch (allthings.type) {
case 'MustacheStatement':
let mustacheStatement: hbs.AST.MustacheStatement;
mustacheStatement = allthings;
break;
case "BlockStatement":
case 'BlockStatement':
let blockStatement: hbs.AST.BlockStatement;
blockStatement = allthings;
break;
case "PartialStatement":
case 'PartialStatement':
let partialStatement: hbs.AST.PartialStatement;
partialStatement = allthings;
break;
case "PartialBlockStatement":
case 'PartialBlockStatement':
let partialBlockStatement: hbs.AST.PartialBlockStatement;
partialBlockStatement = allthings;
break;
case "ContentStatement":
case 'ContentStatement':
let ContentStatement: hbs.AST.ContentStatement;
ContentStatement = allthings;
break;
case "CommentStatement":
case 'CommentStatement':
let CommentStatement: hbs.AST.CommentStatement;
CommentStatement = allthings;
break;
case "SubExpression":
case 'SubExpression':
let SubExpression: hbs.AST.SubExpression;
SubExpression = allthings;
break;
case "PathExpression":
case 'PathExpression':
let PathExpression: hbs.AST.PathExpression;
PathExpression = allthings;
break;
case "StringLiteral":
case 'StringLiteral':
let StringLiteral: hbs.AST.StringLiteral;
StringLiteral = allthings;
break;
case "BooleanLiteral":
case 'BooleanLiteral':
let BooleanLiteral: hbs.AST.BooleanLiteral;
BooleanLiteral = allthings;
break;
case "NumberLiteral":
case 'NumberLiteral':
let NumberLiteral: hbs.AST.NumberLiteral;
NumberLiteral = allthings;
break;
case "UndefinedLiteral":
case 'UndefinedLiteral':
let UndefinedLiteral: hbs.AST.UndefinedLiteral;
UndefinedLiteral = allthings;
break;
case "NullLiteral":
case 'NullLiteral':
let NullLiteral: hbs.AST.NullLiteral;
NullLiteral = allthings;
break;
case "Hash":
case 'Hash':
let Hash: hbs.AST.Hash;
Hash = allthings;
break;
case "HashPair":
case 'HashPair':
let HashPair: hbs.AST.HashPair;
HashPair = allthings;
break;
@@ -196,11 +225,15 @@ switch(allthings.type) {
}
function testParseWithoutProcessing() {
const parsedTemplate: hbs.AST.Program = Handlebars.parseWithoutProcessing('<p>Hello, my name is {{name}}.</p>', {
srcName: "/foo/bar/baz.hbs",
});
const parsedTemplate: hbs.AST.Program = Handlebars.parseWithoutProcessing(
'<p>Hello, my name is {{name}}.</p>',
{
srcName: '/foo/bar/baz.hbs',
}
);
const parsedTemplateWithoutOptions: hbs.AST.Program = Handlebars.parseWithoutProcessing('<p>Hello, my name is {{name}}.</p>');
const parsedTemplateWithoutOptions: hbs.AST.Program =
Handlebars.parseWithoutProcessing('<p>Hello, my name is {{name}}.</p>');
}
function testExceptionTypings() {
@@ -226,8 +259,8 @@ function testExceptionWithNodeTypings() {
loc: {
source: 'source',
start: { line: 1, column: 5 },
end: { line: 10, column: 2 }
}
end: { line: 10, column: 2 },
},
});
// Fields
@@ -247,12 +280,15 @@ function testProtoAccessControlControlOptions() {
{},
{
allowedProtoMethods: { allowedMethod: true, forbiddenMethod: false },
allowedProtoProperties: { allowedProperty: true, forbiddenProperty: false },
allowedProtoProperties: {
allowedProperty: true,
forbiddenProperty: false,
},
allowProtoMethodsByDefault: true,
allowProtoPropertiesByDefault: false,
partials: {
link: '<a href="/people/{{id}}">{{name}}</a>'
}
link: '<a href="/people/{{id}}">{{name}}</a>',
},
}
);
}