├── .editorconfig ├── .eslintrc.json ├── .gitattributes ├── .gitignore ├── .travis.yml ├── .verb.md ├── LICENSE ├── README.md ├── docs └── toc.md ├── index.js ├── lib ├── helpers │ ├── array.js │ ├── code.js │ ├── collection.js │ ├── conditional.js │ ├── fs.js │ ├── html.js │ ├── index.js │ ├── math.js │ ├── object.js │ ├── path.js │ └── string.js ├── iterator.js └── utils.js ├── package.json ├── recipes └── tips-and-tricks.md ├── test ├── array.js ├── code.js ├── collection.js ├── conditional.js ├── fixtures │ ├── README.md │ ├── a.js │ ├── a.md │ └── b.js ├── fs.js ├── html.js ├── math.js ├── object.js ├── path.js ├── string.js └── test.js └── verbfile.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | end_of_line = lf 6 | charset = utf-8 7 | indent_size = 2 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [{,**/}{actual,fixtures,expected,templates}/**/*.*] 12 | trim_trailing_whitespace = false 13 | insert_final_newline = false 14 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "eslint:recommended" 4 | ], 5 | 6 | "env": { 7 | "browser": false, 8 | "es6": true, 9 | "node": true, 10 | "mocha": true 11 | }, 12 | 13 | "parserOptions":{ 14 | "ecmaVersion": 9, 15 | "sourceType": "module", 16 | "ecmaFeatures": { 17 | "modules": true, 18 | "experimentalObjectRestSpread": true 19 | } 20 | }, 21 | 22 | "globals": { 23 | "document": false, 24 | "navigator": false, 25 | "window": false 26 | }, 27 | 28 | "rules": { 29 | "accessor-pairs": 2, 30 | "arrow-spacing": [2, { "before": true, "after": true }], 31 | "block-spacing": [2, "always"], 32 | "brace-style": [2, "1tbs", { "allowSingleLine": true }], 33 | "comma-dangle": [2, "never"], 34 | "comma-spacing": [2, { "before": false, "after": true }], 35 | "comma-style": [2, "last"], 36 | "constructor-super": 2, 37 | "curly": [2, "multi-line"], 38 | "dot-location": [2, "property"], 39 | "eol-last": 2, 40 | "eqeqeq": [2, "allow-null"], 41 | "generator-star-spacing": [2, { "before": true, "after": true }], 42 | "handle-callback-err": [2, "^(err|error)$" ], 43 | "indent": [2, 2, { "SwitchCase": 1 }], 44 | "key-spacing": [2, { "beforeColon": false, "afterColon": true }], 45 | "keyword-spacing": [2, { "before": true, "after": true }], 46 | "new-cap": [2, { "newIsCap": true, "capIsNew": false }], 47 | "new-parens": 2, 48 | "no-array-constructor": 2, 49 | "no-caller": 2, 50 | "no-class-assign": 2, 51 | "no-cond-assign": 2, 52 | "no-const-assign": 2, 53 | "no-control-regex": 2, 54 | "no-debugger": 2, 55 | "no-delete-var": 2, 56 | "no-dupe-args": 2, 57 | "no-dupe-class-members": 2, 58 | "no-dupe-keys": 2, 59 | "no-duplicate-case": 2, 60 | "no-empty-character-class": 2, 61 | "no-eval": 2, 62 | "no-ex-assign": 2, 63 | "no-extend-native": 2, 64 | "no-extra-bind": 2, 65 | "no-extra-boolean-cast": 2, 66 | "no-extra-parens": [2, "functions"], 67 | "no-fallthrough": 2, 68 | "no-floating-decimal": 2, 69 | "no-func-assign": 2, 70 | "no-implied-eval": 2, 71 | "no-inner-declarations": [2, "functions"], 72 | "no-invalid-regexp": 2, 73 | "no-irregular-whitespace": 2, 74 | "no-iterator": 2, 75 | "no-label-var": 2, 76 | "no-labels": 2, 77 | "no-lone-blocks": 2, 78 | "no-mixed-spaces-and-tabs": 2, 79 | "no-multi-spaces": 2, 80 | "no-multi-str": 2, 81 | "no-multiple-empty-lines": [2, { "max": 1 }], 82 | "no-native-reassign": 0, 83 | "no-negated-in-lhs": 2, 84 | "no-new": 2, 85 | "no-new-func": 2, 86 | "no-new-object": 2, 87 | "no-new-require": 2, 88 | "no-new-wrappers": 2, 89 | "no-obj-calls": 2, 90 | "no-octal": 2, 91 | "no-octal-escape": 2, 92 | "no-proto": 0, 93 | "no-redeclare": 2, 94 | "no-regex-spaces": 2, 95 | "no-return-assign": 2, 96 | "no-self-compare": 2, 97 | "no-sequences": 2, 98 | "no-shadow-restricted-names": 2, 99 | "no-spaced-func": 2, 100 | "no-sparse-arrays": 2, 101 | "no-this-before-super": 2, 102 | "no-throw-literal": 2, 103 | "no-trailing-spaces": 0, 104 | "no-undef": 2, 105 | "no-undef-init": 2, 106 | "no-unexpected-multiline": 2, 107 | "no-unneeded-ternary": [2, { "defaultAssignment": false }], 108 | "no-unreachable": 2, 109 | "no-unused-vars": [2, { "vars": "all", "args": "none" }], 110 | "no-useless-call": 0, 111 | "no-with": 2, 112 | "one-var": [0, { "initialized": "never" }], 113 | "operator-linebreak": [0, "after", { "overrides": { "?": "before", ":": "before" } }], 114 | "padded-blocks": [0, "never"], 115 | "quotes": [2, "single", "avoid-escape"], 116 | "radix": 2, 117 | "semi": [2, "always"], 118 | "semi-spacing": [2, { "before": false, "after": true }], 119 | "space-before-blocks": [2, "always"], 120 | "space-before-function-paren": [2, "never"], 121 | "space-in-parens": [2, "never"], 122 | "space-infix-ops": 2, 123 | "space-unary-ops": [2, { "words": true, "nonwords": false }], 124 | "spaced-comment": [0, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }], 125 | "use-isnan": 2, 126 | "valid-typeof": 2, 127 | "wrap-iife": [2, "any"], 128 | "yoda": [2, "never"] 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Enforce Unix newlines 2 | * text eol=lf 3 | 4 | # binaries 5 | *.ai binary 6 | *.psd binary 7 | *.jpg binary 8 | *.gif binary 9 | *.png binary 10 | *.jpeg binary -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | *.DS_store 3 | *.sublime-* 4 | _gh_pages 5 | bower_components 6 | node_modules 7 | npm-debug.log 8 | actual 9 | test/actual 10 | temp 11 | tmp 12 | TODO.md 13 | vendor 14 | staging 15 | coverage 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | os: 3 | - linux 4 | - osx 5 | - windows 6 | language: node_js 7 | node_js: 8 | - 'node' 9 | - '11' 10 | - '10' 11 | - '9' 12 | - '8' 13 | - '7' 14 | - '6' 15 | -------------------------------------------------------------------------------- /.verb.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | To get all helpers: 4 | 5 | ```js 6 | const helpers = require('{%= name %}')(); 7 | console.log(helpers); 8 | ``` 9 | 10 | **Get a specific helper category** 11 | 12 | ```js 13 | // get only the math helpers 14 | const helpers = require('{%= name %}')('math'); 15 | ``` 16 | 17 | **Get multiple helper categories** 18 | 19 | ```js 20 | // get only the math helpers 21 | const helpers = require('{%= name %}')(['math', 'string']); 22 | ``` 23 | 24 | ### Template-engine agnostic 25 | 26 | **Lo-Dash Example** 27 | 28 | ```js 29 | const helpers = require('{%= name %}')('array'); 30 | 31 | // pass helpers on `imports` 32 | const imports = { imports: helpers }; 33 | 34 | // compile a template 35 | const fn = _.template('<%= first(foo) %>', imports); 36 | 37 | // render 38 | fn({ foo: ['a', 'b', 'c'] }); 39 | //=> 'a' 40 | ``` 41 | 42 | ### Namespacing 43 | 44 | Handlebars and Lo-Dash both allow **dot notation** to be used for referencing helpers. I'd be happy to add examples for other engines if someone wants to do a PR. 45 | 46 | **Example** 47 | 48 | ```js 49 | <%= path.dirname("a/b/c/d.js") %> 50 | ``` 51 | 52 | This can be used as a way of working around potential naming conflicts. 53 | 54 | ## Helpers 55 | 56 | _(The following **API Table of Contents** is generated by [verb][]. See the [verbfile.js](verbfile.js) for more details.)_ 57 | 58 | {%= include("docs/toc.md") %} 59 | 60 | ### array 61 | 62 | {%= increaseHeadings(apidocs("lib/helpers/array.js")) %} 63 | 64 | ### code 65 | 66 | {%= increaseHeadings(apidocs("lib/helpers/code.js")) %} 67 | 68 | ### collection 69 | 70 | {%= increaseHeadings(apidocs("lib/helpers/collection.js")) %} 71 | 72 | ### conditional 73 | 74 | {%= increaseHeadings(apidocs("lib/helpers/conditional.js")) %} 75 | 76 | ### fs 77 | 78 | {%= increaseHeadings(apidocs("lib/helpers/fs.js")) %} 79 | 80 | ### html 81 | 82 | {%= increaseHeadings(apidocs("lib/helpers/html.js")) %} 83 | 84 | ### index 85 | 86 | {%= increaseHeadings(apidocs("lib/helpers/index.js")) %} 87 | 88 | ### math 89 | 90 | {%= increaseHeadings(apidocs("lib/helpers/math.js")) %} 91 | 92 | ### object 93 | 94 | {%= increaseHeadings(apidocs("lib/helpers/object.js")) %} 95 | 96 | ### path 97 | 98 | {%= increaseHeadings(apidocs("lib/helpers/path.js")) %} 99 | 100 | ### string 101 | 102 | {%= increaseHeadings(apidocs("lib/helpers/string.js")) %} 103 | 104 | ## Coverage 105 | 106 | ``` 107 | {%= coverage("coverage/summary.txt") %} 108 | ``` 109 | 110 | [path]: https://nodejs.org/api/path.html 111 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-present, Jon Schlinkert. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # template-helpers [![NPM version](https://img.shields.io/npm/v/template-helpers.svg?style=flat)](https://www.npmjs.com/package/template-helpers) [![NPM monthly downloads](https://img.shields.io/npm/dm/template-helpers.svg?style=flat)](https://npmjs.org/package/template-helpers) [![NPM total downloads](https://img.shields.io/npm/dt/template-helpers.svg?style=flat)](https://npmjs.org/package/template-helpers) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/template-helpers.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/template-helpers) 2 | 3 | > Generic JavaScript helpers that can be used with any template engine. Handlebars, Lo-Dash, Underscore, or any engine that supports helper functions. 4 | 5 | Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support. 6 | 7 | - [Install](#install) 8 | - [Usage](#usage) 9 | * [Template-engine agnostic](#template-engine-agnostic) 10 | * [Namespacing](#namespacing) 11 | - [Helpers](#helpers) 12 | * [array](#array) 13 | * [code](#code) 14 | * [collection](#collection) 15 | * [conditional](#conditional) 16 | * [fs](#fs) 17 | * [html](#html) 18 | * [index](#index) 19 | * [math](#math) 20 | * [object](#object) 21 | * [path](#path) 22 | * [string](#string) 23 | - [Coverage](#coverage) 24 | - [About](#about) 25 | 26 | ## Install 27 | 28 | Install with [npm](https://www.npmjs.com/): 29 | 30 | ```sh 31 | $ npm install --save template-helpers 32 | ``` 33 | 34 | ## Usage 35 | 36 | To get all helpers: 37 | 38 | ```js 39 | const helpers = require('template-helpers')(); 40 | console.log(helpers); 41 | ``` 42 | 43 | **Get a specific helper category** 44 | 45 | ```js 46 | // get only the math helpers 47 | const helpers = require('template-helpers')('math'); 48 | ``` 49 | 50 | **Get multiple helper categories** 51 | 52 | ```js 53 | // get only the math helpers 54 | const helpers = require('template-helpers')(['math', 'string']); 55 | ``` 56 | 57 | ### Template-engine agnostic 58 | 59 | **Lo-Dash Example** 60 | 61 | ```js 62 | const helpers = require('template-helpers')('array'); 63 | 64 | // pass helpers on `imports` 65 | const imports = { imports: helpers }; 66 | 67 | // compile a template 68 | const fn = _.template('<%= first(foo) %>', imports); 69 | 70 | // render 71 | fn({ foo: ['a', 'b', 'c'] }); 72 | //=> 'a' 73 | ``` 74 | 75 | ### Namespacing 76 | 77 | Handlebars and Lo-Dash both allow **dot notation** to be used for referencing helpers. I'd be happy to add examples for other engines if someone wants to do a PR. 78 | 79 | **Example** 80 | 81 | ```js 82 | <%= path.dirname("a/b/c/d.js") %> 83 | ``` 84 | 85 | This can be used as a way of working around potential naming conflicts. 86 | 87 | ## Helpers 88 | 89 | _(The following **API Table of Contents** is generated by [verb](https://github.com/verbose/verb). See the [verbfile.js](verbfile.js) for more details.)_ 90 | 91 | ## Categories 92 | 93 | Currently **101 helpers** in **10 categories**: 94 | 95 | * **[array](#array)** ([code](lib/helpers/array.js) | [unit tests](test/array.js)) 96 | * **[code](#code)** ([code](lib/helpers/code.js) | [unit tests](test/code.js)) 97 | * **[collection](#collection)** ([code](lib/helpers/collection.js) | [unit tests](test/collection.js)) 98 | * **[conditional](#conditional)** ([code](lib/helpers/conditional.js) | [unit tests](test/conditional.js)) 99 | * **[fs](#fs)** ([code](lib/helpers/fs.js) | [unit tests](test/fs.js)) 100 | * **[html](#html)** ([code](lib/helpers/html.js) | [unit tests](test/html.js)) 101 | * **[math](#math)** ([code](lib/helpers/math.js) | [unit tests](test/math.js)) 102 | * **[object](#object)** ([code](lib/helpers/object.js) | [unit tests](test/object.js)) 103 | * **[path](#path)** ([code](lib/helpers/path.js) | [unit tests](test/path.js)) 104 | * **[string](#string)** ([code](lib/helpers/string.js) | [unit tests](test/string.js)) 105 | 106 | ## All helpers 107 | 108 | ### [array helpers](#array) 109 | 110 | Visit the: [code](lib/helpers/array.js) | [unit tests](test/array.js) | [issues](https://github.com/jonschlinkert/template-helpers/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+array+helpers)) 111 | 112 | * **[isArray](#isArray)** ([code](lib/helpers/array.js#L21) | [unit tests](test/array.js#L18)) 113 | * **[arrayify](#arrayify)** ([code](lib/helpers/array.js#L42) | [unit tests](test/array.js#L25)) 114 | * **[first](#first)** ([code](lib/helpers/array.js#L58) | [unit tests](test/array.js#L32)) 115 | * **[last](#last)** ([code](lib/helpers/array.js#L79) | [unit tests](test/array.js#L48)) 116 | * **[before](#before)** ([code](lib/helpers/array.js#L102) | [unit tests](test/array.js#L61)) 117 | * **[after](#after)** ([code](lib/helpers/array.js#L121) | [unit tests](test/array.js#L71)) 118 | * **[each](#each)** ([code](lib/helpers/array.js#L147) | [unit tests](test/array.js#L98)) 119 | * **[map](#map)** ([code](lib/helpers/array.js#L188) | [unit tests](test/array.js#L122)) 120 | * **[join](#join)** ([code](lib/helpers/array.js#L218) | [unit tests](test/array.js#L82)) 121 | * **[sort](#sort)** ([code](lib/helpers/array.js#L241) | [unit tests](test/array.js#L137)) 122 | * **[length](#length)** ([code](lib/helpers/array.js#L272) | [unit tests](test/array.js#L162)) 123 | * **[compact](#compact)** ([code](lib/helpers/array.js#L289) | [unit tests](test/array.js#L178)) 124 | * **[difference](#difference)** ([code](lib/helpers/array.js#L307) | [unit tests](test/array.js#L189)) 125 | * **[unique](#unique)** ([code](lib/helpers/array.js#L344) | [unit tests](test/array.js#L205)) 126 | * **[union](#union)** ([code](lib/helpers/array.js#L373) | [unit tests](test/array.js#L215)) 127 | * **[shuffle](#shuffle)** ([code](lib/helpers/array.js#L389) | [no tests]) 128 | 129 | ### [code helpers](#code) 130 | 131 | Visit the: [code](lib/helpers/code.js) | [unit tests](test/code.js) | [issues](https://github.com/jonschlinkert/template-helpers/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+code+helpers)) 132 | 133 | * **[embed](#embed)** ([code](lib/helpers/code.js#L23) | [unit tests](test/code.js#L33)) 134 | * **[jsfiddle](#jsfiddle)** ([code](lib/helpers/code.js#L45) | [unit tests](test/code.js#L24)) 135 | 136 | ### [collection helpers](#collection) 137 | 138 | Visit the: [code](lib/helpers/collection.js) | [unit tests](test/collection.js) | [issues](https://github.com/jonschlinkert/template-helpers/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+collection+helpers)) 139 | 140 | * **[any](#any)** ([code](lib/helpers/collection.js#L17) | [unit tests](test/collection.js#L17)) 141 | * **[filter](#filter)** ([code](lib/helpers/collection.js#L32) | [unit tests](test/collection.js#L34)) 142 | 143 | ### [conditional helpers](#conditional) 144 | 145 | Visit the: [code](lib/helpers/conditional.js) | [unit tests](test/conditional.js) | [issues](https://github.com/jonschlinkert/template-helpers/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+conditional+helpers)) 146 | 147 | * **[and](#and)** ([code](lib/helpers/conditional.js#L13) | [no tests]) 148 | * **[compare](#compare)** ([code](lib/helpers/conditional.js#L30) | [unit tests](test/conditional.js#L35)) 149 | * **[find](#find)** ([code](lib/helpers/conditional.js#L72) | [no tests]) 150 | * **[every](#every)** ([code](lib/helpers/conditional.js#L83) | [no tests]) 151 | * **[gt](#gt)** ([code](lib/helpers/conditional.js#L98) | [no tests]) 152 | * **[gte](#gte)** ([code](lib/helpers/conditional.js#L110) | [no tests]) 153 | * **[_if](#_if)** ([code](lib/helpers/conditional.js#L122) | [no tests]) 154 | * **[is](#is)** ([code](lib/helpers/conditional.js#L142) | [unit tests](test/conditional.js#L47)) 155 | * **[eq](#eq)** ([code](lib/helpers/conditional.js#L157) | [no tests]) 156 | * **[isnt](#isnt)** ([code](lib/helpers/conditional.js#L169) | [unit tests](test/conditional.js#L59)) 157 | * **[notEq](#notEq)** ([code](lib/helpers/conditional.js#L183) | [no tests]) 158 | * **[lt](#lt)** ([code](lib/helpers/conditional.js#L195) | [no tests]) 159 | * **[lte](#lte)** ([code](lib/helpers/conditional.js#L207) | [no tests]) 160 | * **[or](#or)** ([code](lib/helpers/conditional.js#L219) | [no tests]) 161 | * **[some](#some)** ([code](lib/helpers/conditional.js#L230) | [no tests]) 162 | 163 | ### [fs helpers](#fs) 164 | 165 | Visit the: [code](lib/helpers/fs.js) | [unit tests](test/fs.js) | [issues](https://github.com/jonschlinkert/template-helpers/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+fs+helpers)) 166 | 167 | * **[exists](#exists)** ([code](lib/helpers/fs.js#L16) | [unit tests](test/fs.js#L17)) 168 | * **[read](#read)** ([code](lib/helpers/fs.js#L29) | [unit tests](test/fs.js#L23)) 169 | 170 | ### [html helpers](#html) 171 | 172 | Visit the: [code](lib/helpers/html.js) | [unit tests](test/html.js) | [issues](https://github.com/jonschlinkert/template-helpers/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+html+helpers)) 173 | 174 | * **[escapeHtml](#escapeHtml)** ([code](lib/helpers/html.js#L18) | [unit tests](test/html.js#L17)) 175 | * **[sanitize](#sanitize)** ([code](lib/helpers/html.js#L46) | [unit tests](test/html.js#L27)) 176 | 177 | ### [math helpers](#math) 178 | 179 | Visit the: [code](lib/helpers/math.js) | [unit tests](test/math.js) | [issues](https://github.com/jonschlinkert/template-helpers/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+math+helpers)) 180 | 181 | * **[add](#add)** ([code](lib/helpers/math.js#L17) | [unit tests](test/math.js#L17)) 182 | * **[subtract](#subtract)** ([code](lib/helpers/math.js#L31) | [unit tests](test/math.js#L23)) 183 | * **[divide](#divide)** ([code](lib/helpers/math.js#L46) | [unit tests](test/math.js#L29)) 184 | * **[multiply](#multiply)** ([code](lib/helpers/math.js#L61) | [unit tests](test/math.js#L35)) 185 | * **[floor](#floor)** ([code](lib/helpers/math.js#L76) | [unit tests](test/math.js#L41)) 186 | * **[ceil](#ceil)** ([code](lib/helpers/math.js#L91) | [unit tests](test/math.js#L47)) 187 | * **[round](#round)** ([code](lib/helpers/math.js#L109) | [unit tests](test/math.js#L53)) 188 | * **[sum](#sum)** ([code](lib/helpers/math.js#L123) | [unit tests](test/math.js#L60)) 189 | 190 | ### [object helpers](#object) 191 | 192 | Visit the: [code](lib/helpers/object.js) | [unit tests](test/object.js) | [issues](https://github.com/jonschlinkert/template-helpers/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+object+helpers)) 193 | 194 | * **[fallback](#fallback)** ([code](lib/helpers/object.js#L22) | [unit tests](test/object.js#L18)) 195 | * **[stringify](#stringify)** ([code](lib/helpers/object.js#L36) | [unit tests](test/object.js#L26)) 196 | * **[parse](#parse)** ([code](lib/helpers/object.js#L50) | [unit tests](test/object.js#L33)) 197 | * **[get](#get)** ([code](lib/helpers/object.js#L65) | [no tests]) 198 | * **[keys](#keys)** ([code](lib/helpers/object.js#L79) | [unit tests](test/object.js#L64)) 199 | * **[isObject](#isObject)** ([code](lib/helpers/object.js#L97) | [unit tests](test/object.js#L40)) 200 | * **[isPlainObject](#isPlainObject)** ([code](lib/helpers/object.js#L117) | [unit tests](test/object.js#L48)) 201 | * **[hasOwn](#hasOwn)** ([code](lib/helpers/object.js#L129) | [unit tests](test/object.js#L57)) 202 | * **[omit](#omit)** ([code](lib/helpers/object.js#L144) | [unit tests](test/object.js#L98)) 203 | * **[forIn](#forIn)** ([code](lib/helpers/object.js#L163) | [unit tests](test/object.js#L70)) 204 | * **[forOwn](#forOwn)** ([code](lib/helpers/object.js#L188) | [unit tests](test/object.js#L84)) 205 | * **[extend](#extend)** ([code](lib/helpers/object.js#L205) | [unit tests](test/object.js#L105)) 206 | * **[merge](#merge)** ([code](lib/helpers/object.js#L241) | [unit tests](test/object.js#L132)) 207 | 208 | ### [path helpers](#path) 209 | 210 | Visit the: [code](lib/helpers/path.js) | [unit tests](test/path.js) | [issues](https://github.com/jonschlinkert/template-helpers/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+path+helpers)) 211 | 212 | * **[dirname](#dirname)** ([code](lib/helpers/path.js#L19) | [unit tests](test/path.js#L17)) 213 | * **[basename](#basename)** ([code](lib/helpers/path.js#L34) | [unit tests](test/path.js#L23)) 214 | * **[filename](#filename)** ([code](lib/helpers/path.js#L49) | [unit tests](test/path.js#L29)) 215 | * **[stem](#stem)** ([code](lib/helpers/path.js#L65) | [no tests]) 216 | * **[extname](#extname)** ([code](lib/helpers/path.js#L80) | [unit tests](test/path.js#L35)) 217 | * **[ext](#ext)** ([code](lib/helpers/path.js#L80) | [unit tests](test/path.js#L35)) 218 | * **[resolve](#resolve)** ([code](lib/helpers/path.js#L110) | [unit tests](test/path.js#L47)) 219 | * **[relative](#relative)** ([code](lib/helpers/path.js#L126) | [unit tests](test/path.js#L53)) 220 | * **[segments](#segments)** ([code](lib/helpers/path.js#L162) | [unit tests](test/path.js#L107)) 221 | * **[join](#join)** ([code](lib/helpers/path.js#L183) | [unit tests](test/path.js#L100)) 222 | * **[isAbsolute](#isAbsolute)** ([code](lib/helpers/path.js#L215) | [unit tests](test/path.js#L81)) 223 | * **[isRelative](#isRelative)** ([code](lib/helpers/path.js#L247) | [unit tests](test/path.js#L62)) 224 | 225 | ### [string helpers](#string) 226 | 227 | Visit the: [code](lib/helpers/string.js) | [unit tests](test/string.js) | [issues](https://github.com/jonschlinkert/template-helpers/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+string+helpers)) 228 | 229 | * **[camelcase](#camelcase)** ([code](lib/helpers/string.js#L24) | [unit tests](test/string.js#L98)) 230 | * **[centerAlign](#centerAlign)** ([code](lib/helpers/string.js#L46) | [no tests]) 231 | * **[chop](#chop)** ([code](lib/helpers/string.js#L69) | [unit tests](test/string.js#L50)) 232 | * **[count](#count)** ([code](lib/helpers/string.js#L88) | [unit tests](test/string.js#L277)) 233 | * **[dotcase](#dotcase)** ([code](lib/helpers/string.js#L107) | [unit tests](test/string.js#L162)) 234 | * **[ellipsis](#ellipsis)** ([code](lib/helpers/string.js#L128) | [unit tests](test/string.js#L368)) 235 | * **[isString](#isString)** ([code](lib/helpers/string.js#L147) | [no tests]) 236 | * **[lower](#lower)** ([code](lib/helpers/string.js#L161) | [unit tests](test/string.js#L16)) 237 | * **[lowercase](#lowercase)** ([code](lib/helpers/string.js#L161) | [unit tests](test/string.js#L16)) 238 | * **[pascalcase](#pascalcase)** ([code](lib/helpers/string.js#L177) | [unit tests](test/string.js#L119)) 239 | * **[snakecase](#snakecase)** ([code](lib/helpers/string.js#L196) | [unit tests](test/string.js#L141)) 240 | * **[split](#split)** ([code](lib/helpers/string.js#L214) | [no tests]) 241 | * **[strip](#strip)** ([code](lib/helpers/string.js#L230) | [unit tests](test/string.js#L72)) 242 | * **[stripIndent](#stripIndent)** ([code](lib/helpers/string.js#L248) | [unit tests](test/string.js#L86)) 243 | * **[trim](#trim)** ([code](lib/helpers/string.js#L275) | [unit tests](test/string.js#L36)) 244 | * **[dashcase](#dashcase)** ([code](lib/helpers/string.js#L291) | [unit tests](test/string.js#L183)) 245 | * **[pathcase](#pathcase)** ([code](lib/helpers/string.js#L309) | [unit tests](test/string.js#L204)) 246 | * **[sentencecase](#sentencecase)** ([code](lib/helpers/string.js#L327) | [unit tests](test/string.js#L225)) 247 | * **[hyphenate](#hyphenate)** ([code](lib/helpers/string.js#L345) | [unit tests](test/string.js#L239)) 248 | * **[slugify](#slugify)** ([code](lib/helpers/string.js#L363) | [unit tests](test/string.js#L257)) 249 | * **[reverse](#reverse)** ([code](lib/helpers/string.js#L377) | [unit tests](test/string.js#L291)) 250 | * **[rightAlign](#rightAlign)** ([code](lib/helpers/string.js#L394) | [no tests]) 251 | * **[replace](#replace)** ([code](lib/helpers/string.js#L412) | [unit tests](test/string.js#L330)) 252 | * **[titleize](#titleize)** ([code](lib/helpers/string.js#Lundefined) | [no tests]) 253 | * **[titlecase](#titlecase)** ([code](lib/helpers/string.js#L433) | [unit tests](test/string.js#L348)) 254 | * **[truncate](#truncate)** ([code](lib/helpers/string.js#L451) | [unit tests](test/string.js#L358)) 255 | * **[upper](#upper)** ([code](lib/helpers/string.js#L467) | [unit tests](test/string.js#L26)) 256 | * **[uppercase](#uppercase)** ([code](lib/helpers/string.js#L467) | [unit tests](test/string.js#L26)) 257 | * **[wordwrap](#wordwrap)** ([code](lib/helpers/string.js#L484) | [unit tests](test/string.js#L300)) 258 | 259 | ### array 260 | 261 | #### [isArray](lib/helpers/array.js#L21) 262 | 263 | Returns true if `value` is an array. 264 | 265 | **Params** 266 | 267 | * `value` **{any}**: The value to test. 268 | * `returns` **{Boolean}** 269 | 270 | **Example** 271 | 272 | ```js 273 | <%= isArray('a, b, c') %> 274 | //=> 'false' 275 | 276 | <%= isArray(['a, b, c']) %> 277 | //=> 'true' 278 | ``` 279 | 280 | #### [arrayify](lib/helpers/array.js#L42) 281 | 282 | Cast `val` to an array. 283 | 284 | **Params** 285 | 286 | * `val` **{any}**: The value to arrayify. 287 | * `returns` **{Array}**: An array. 288 | * `returns` **{Array}** 289 | 290 | **Example** 291 | 292 | ```js 293 | <%= arrayify('a') %> 294 | //=> '["a"]' 295 | 296 | <%= arrayify({a: 'b'}) %> 297 | //=> '[{a: "b"}]' 298 | 299 | <%= arrayify(['a']) %> 300 | //=> '["a"]' 301 | ``` 302 | 303 | #### [first](lib/helpers/array.js#L58) 304 | 305 | Returns the first item, or first `n` items of an array. 306 | 307 | **Params** 308 | 309 | * `array` **{Array}** 310 | * `n` **{Number}**: Number of items to return, starting at `0`. 311 | * `returns` **{Array}** 312 | 313 | **Example** 314 | 315 | ```js 316 | <%= first(['a', 'b', 'c', 'd', 'e'], 2) %> 317 | //=> '["a", "b"]' 318 | ``` 319 | 320 | #### [last](lib/helpers/array.js#L79) 321 | 322 | Returns the last item, or last `n` items of an array. 323 | 324 | **Params** 325 | 326 | * `array` **{Array}** 327 | * `n` **{Number}**: Number of items to return, starting with the last item. 328 | * `returns` **{Array}** 329 | 330 | **Example** 331 | 332 | ```js 333 | <%= last(['a', 'b', 'c', 'd', 'e'], 2) %> 334 | //=> '["d", "e"]' 335 | ``` 336 | 337 | #### [before](lib/helpers/array.js#L102) 338 | 339 | Returns all of the items in an array up to the specified number Opposite of `<%= after() %`. 340 | 341 | **Params** 342 | 343 | * `array` **{Array}** 344 | * `n` **{Number}** 345 | * `returns` **{Array}**: Array excluding items after the given number. 346 | 347 | **Example** 348 | 349 | ```js 350 | <%= before(['a', 'b', 'c'], 2) %> 351 | //=> '["a", "b"]' 352 | ``` 353 | 354 | #### [after](lib/helpers/array.js#L121) 355 | 356 | Returns all of the items in an arry after the specified index. Opposite of `<%= before() %`. 357 | 358 | **Params** 359 | 360 | * `array` **{Array}**: Collection 361 | * `n` **{Number}**: Starting index (number of items to exclude) 362 | * `returns` **{Array}**: Array exluding `n` items. 363 | 364 | **Example** 365 | 366 | ```js 367 | <%= after(['a', 'b', 'c'], 1) %> 368 | //=> '["c"]' 369 | ``` 370 | 371 | #### [each](lib/helpers/array.js#L147) 372 | 373 | Calling `fn` on each element of the given `array` with the given `context`. 374 | 375 | Assuming that `double` has been registered as a helper: 376 | 377 | **Params** 378 | 379 | * `array` **{Array}** 380 | * `fn` **{String}**: The function to call on each element in the given array. 381 | * `returns` **{String}** 382 | 383 | **Examples** 384 | 385 | ```js 386 | function double(str) { 387 | return str + str; 388 | } 389 | ``` 390 | 391 | ```js 392 | <%= each(['a', 'b', 'c'], double, ctx) %> 393 | //=> '["aa", "bb", "cc"]' 394 | ``` 395 | 396 | #### [map](lib/helpers/array.js#L188) 397 | 398 | Returns a new array, created by calling `function` on each element of the given `array`. 399 | 400 | Assuming that `double` has been registered as a helper: 401 | 402 | **Params** 403 | 404 | * `array` **{Array}** 405 | * `fn` **{String}**: The function to call on each element in the given array. 406 | * `returns` **{String}** 407 | 408 | **Examples** 409 | 410 | ```js 411 | function double(str) { 412 | return str + str; 413 | } 414 | ``` 415 | 416 | ```js 417 | <%= map(['a', 'b', 'c'], double) %> 418 | //=> '["aa", "bb", "cc"]' 419 | ``` 420 | 421 | #### [join](lib/helpers/array.js#L218) 422 | 423 | Join all elements of array into a string, optionally using a given separator. 424 | 425 | **Params** 426 | 427 | * `array` **{Array}** 428 | * `sep` **{String}**: The separator to use. 429 | * `returns` **{String}** 430 | 431 | **Example** 432 | 433 | ```js 434 | <%= join(['a', 'b', 'c']) %> 435 | //=> 'a, b, c' 436 | 437 | <%= join(['a', 'b', 'c'], '-') %> 438 | //=> 'a-b-c' 439 | ``` 440 | 441 | #### [sort](lib/helpers/array.js#L241) 442 | 443 | Sort the given `array`. If an array of objects is passed, you may optionally pass a `key` to sort on as the second argument. You may alternatively pass a sorting function as the second argument. 444 | 445 | **Params** 446 | 447 | * `array` **{Array}**: the array to sort. 448 | * `key` **{String|Function}**: The object key to sort by, or sorting function. 449 | 450 | **Example** 451 | 452 | ```js 453 | <%= sort(["b", "a", "c"]) %> 454 | //=> 'a,b,c' 455 | 456 | <%= sort([{a: "zzz"}, {a: "aaa"}], "a") %> 457 | //=> '[{"a":"aaa"},{"a":"zzz"}]' 458 | ``` 459 | 460 | #### [length](lib/helpers/array.js#L272) 461 | 462 | Returns the length of the given array. 463 | 464 | **Params** 465 | 466 | * `array` **{Array}** 467 | * `returns` **{Number}**: The length of the array. 468 | 469 | **Example** 470 | 471 | ```js 472 | <%= length(['a', 'b', 'c']) %> 473 | //=> 3 474 | ``` 475 | 476 | #### [compact](lib/helpers/array.js#L289) 477 | 478 | Returns an array with all falsey values removed. 479 | 480 | **Params** 481 | 482 | * `arr` **{Array}** 483 | * `returns` **{Array}** 484 | 485 | **Example** 486 | 487 | ```js 488 | <%= compact([null, a, undefined, 0, false, b, c, '']) %> 489 | //=> '["a", "b", "c"]' 490 | ``` 491 | 492 | #### [difference](lib/helpers/array.js#L307) 493 | 494 | Return the difference between the first array and additional arrays. 495 | 496 | **Params** 497 | 498 | * `array` **{Array}**: The array to compare againts. 499 | * `arrays` **{Array}**: One or more additional arrays. 500 | * `returns` **{Array}** 501 | 502 | **Example** 503 | 504 | ```js 505 | <%= difference(["a", "c"], ["a", "b"]) %> 506 | //=> '["c"]' 507 | ``` 508 | 509 | #### [unique](lib/helpers/array.js#L344) 510 | 511 | Return an array, free of duplicate values. 512 | 513 | **Params** 514 | 515 | * `array` **{Array}**: The array to uniquify 516 | * `returns` **{Array}**: Duplicate-free array 517 | 518 | **Example** 519 | 520 | ```js 521 | <%= unique(['a', 'b', 'c', 'c']) % 522 | => '["a", "b", "c"]' 523 | ``` 524 | 525 | #### [union](lib/helpers/array.js#L373) 526 | 527 | Returns an array of unique values using strict equality for comparisons. 528 | 529 | **Params** 530 | 531 | * `arr` **{Array}** 532 | * `returns` **{Array}** 533 | 534 | **Example** 535 | 536 | ```js 537 | <%= union(["a"], ["b"], ["c"]) %> 538 | //=> '["a", "b", "c"]' 539 | ``` 540 | 541 | #### [shuffle](lib/helpers/array.js#L389) 542 | 543 | Shuffle the items in an array. 544 | 545 | **Params** 546 | 547 | * `arr` **{Array}** 548 | * `returns` **{Array}** 549 | 550 | **Example** 551 | 552 | ```js 553 | <%= shuffle(["a", "b", "c"]) %> 554 | //=> ["c", "a", "b"] 555 | ``` 556 | 557 | ### code 558 | 559 | #### [embed](lib/helpers/code.js#L23) 560 | 561 | Embed code from an external file as preformatted text. 562 | 563 | **Params** 564 | 565 | * `fp` **{String}**: filepath to the file to embed. 566 | * `language` **{String}**: Optionally specify the language to use for syntax highlighting. 567 | * `returns` **{String}** 568 | 569 | **Example** 570 | 571 | ```js 572 | <%= embed('path/to/file.js') %> 573 | 574 | // specify the language to use 575 | <%= embed('path/to/file.hbs', 'html') %> 576 | ``` 577 | 578 | #### [jsfiddle](lib/helpers/code.js#L45) 579 | 580 | Generate the HTML for a jsFiddle link with the given `params` 581 | 582 | **Params** 583 | 584 | * `params` **{Object}** 585 | * `returns` **{String}** 586 | 587 | **Example** 588 | 589 | ```js 590 | <%= jsfiddle({id: '0dfk10ks', {tabs: true}}) %> 591 | ``` 592 | 593 | ### collection 594 | 595 | #### [any](lib/helpers/collection.js#L17) 596 | 597 | Returns `true` if `value` exists in the given string, array 598 | or object. See [any](https://github.com/jonschlinkert/any) for documentation. 599 | 600 | **Params** 601 | 602 | * `value` **{any}** 603 | * `target` **{any}** 604 | * `options` **{Object}** 605 | 606 | #### [filter](lib/helpers/collection.js#L32) 607 | 608 | Filter the given array or object to contain only the matching values. 609 | 610 | **Params** 611 | 612 | * `arr` **{Array}** 613 | * `returns` **{Array}** 614 | 615 | **Example** 616 | 617 | ```js 618 | <%= filter(['foo', 'bar', 'baz']) %> 619 | //=> '["a", "b", "c"]' 620 | ``` 621 | 622 | ### conditional 623 | 624 | #### [and](lib/helpers/conditional.js#L13) 625 | 626 | Returns true when both `valueA` and `valueB` are truthy. 627 | 628 | **Params** 629 | 630 | * `valueA` **{any}** 631 | * `valueB` **{any}** 632 | * `returns` **{Boolean}** 633 | 634 | #### [compare](lib/helpers/conditional.js#L30) 635 | 636 | Render a block when a comparison of the first and third arguments returns true. 637 | 638 | **Params** 639 | 640 | * `valueA` **{String}** 641 | * `operator` **{String}**: The operator to use for the comparison (must be a quoted string). 642 | * `valueB` **{String}** 643 | * `returns` **{Boolean}** 644 | 645 | **Example** 646 | 647 | ```js 648 | <%= compare("foo", "!==", "bar") %> 649 | ``` 650 | 651 | #### [find](lib/helpers/conditional.js#L72) 652 | 653 | Returns the first truthy value. 654 | 655 | **Params** 656 | 657 | * `...values` **{...args}** 658 | * `returns` **{any}** 659 | 660 | #### [every](lib/helpers/conditional.js#L83) 661 | 662 | Returns true when all provided values are truthy. 663 | 664 | **Params** 665 | 666 | * `...values` **{...any}** 667 | * `returns` **{Boolean}** 668 | 669 | #### [gt](lib/helpers/conditional.js#L98) 670 | 671 | Returns true when `valueA` is greater than `valueB`. 672 | 673 | **Params** 674 | 675 | * `valueA` **{String}** 676 | * `valueB` **{String}** 677 | * `returns` **{Boolean}** 678 | 679 | #### [gte](lib/helpers/conditional.js#L110) 680 | 681 | Returns true when `valueA` is greater than or equal to `valueB`. 682 | 683 | **Params** 684 | 685 | * `valueA` **{String}** 686 | * `valueB` **{String}** 687 | * `returns` **{Boolean}** 688 | 689 | #### [_if](lib/helpers/conditional.js#L122) 690 | 691 | Return true if `key` is an own, enumerable property 692 | of the given `obj`. 693 | 694 | **Params** 695 | 696 | * `object` **{Object}** 697 | * `key` **{String}** 698 | * `returns` **{Boolean}** 699 | 700 | #### [is](lib/helpers/conditional.js#L142) 701 | 702 | Returns true when `valueA` equals `valueB`. 703 | 704 | **Params** 705 | 706 | * `valueA` **{String}** 707 | * `valueB` **{String}** 708 | * `strict` **{String}** 709 | * `returns` **{Boolean}** 710 | 711 | #### [eq](lib/helpers/conditional.js#L157) 712 | 713 | Alias for [is](#is). 714 | 715 | **Params** 716 | 717 | * `valueA` **{String}** 718 | * `valueB` **{String}** 719 | * `strict` **{String}** 720 | * `returns` **{Boolean}** 721 | 722 | #### [isnt](lib/helpers/conditional.js#L169) 723 | 724 | Returns true when `valueA` does not equal `valueB`. 725 | 726 | **Params** 727 | 728 | * `valueA` **{String}** 729 | * `valueB` **{String}** 730 | * `returns` **{Boolean}** 731 | 732 | #### [notEq](lib/helpers/conditional.js#L183) 733 | 734 | Alias for [isnt](#isnt). 735 | 736 | **Params** 737 | 738 | * `valueA` **{String}** 739 | * `valueB` **{String}** 740 | * `returns` **{Boolean}** 741 | 742 | #### [lt](lib/helpers/conditional.js#L195) 743 | 744 | Returns true when `valueA` is less than `valueB`. 745 | 746 | **Params** 747 | 748 | * `valueA` **{String}** 749 | * `valueB` **{String}** 750 | * `returns` **{Boolean}** 751 | 752 | #### [lte](lib/helpers/conditional.js#L207) 753 | 754 | Returns true when `valueA` is less than or equal to `valueB`. 755 | 756 | **Params** 757 | 758 | * `valueA` **{String}** 759 | * `valueB` **{String}** 760 | * `returns` **{Boolean}** 761 | 762 | #### [or](lib/helpers/conditional.js#L219) 763 | 764 | Returns `valueA` if thruthy, otherwise `valueB`. 765 | 766 | **Params** 767 | 768 | * `valueA` **{any}** 769 | * `valueB` **{any}** 770 | * `returns` **{any}** 771 | 772 | #### [some](lib/helpers/conditional.js#L230) 773 | 774 | Returns true when at least one value is truthy. 775 | 776 | **Params** 777 | 778 | * `...values` **{...any}** 779 | * `returns` **{Boolean}** 780 | 781 | ### fs 782 | 783 | #### [exists](lib/helpers/fs.js#L16) 784 | 785 | Return true if a file exists 786 | 787 | **Params** 788 | 789 | * `filepath` **{String}**: Path of the file to check. 790 | * `returns` **{Boolean}**: True if the file exists 791 | 792 | **Example** 793 | 794 | ```js 795 | <%= exists("foo.js") %> 796 | ``` 797 | 798 | #### [read](lib/helpers/fs.js#L29) 799 | 800 | Read a file from the file system and inject its content 801 | 802 | **Params** 803 | 804 | * `filepath` **{String}**: Path of the file to read. 805 | * `returns` **{String}**: Contents of the given file. 806 | 807 | **Example** 808 | 809 | ```js 810 | <%= read("foo.js") %> 811 | ``` 812 | 813 | ### html 814 | 815 | #### [escapeHtml](lib/helpers/html.js#L18) 816 | 817 | Escape HTML characters in a string. 818 | 819 | **Params** 820 | 821 | * `str` **{String}**: String of HTML with characters to escape. 822 | * `returns` **{String}** 823 | 824 | **Example** 825 | 826 | ```js 827 | <%= escapeHtml("foo") %> 828 | //=> <span>foo</span> 829 | ``` 830 | 831 | #### [sanitize](lib/helpers/html.js#L46) 832 | 833 | Strip HTML tags from a string, so that only the text nodes are preserved. 834 | 835 | **Params** 836 | 837 | * `str` **{String}**: The string of HTML to sanitize. 838 | * `returns` **{String}** 839 | 840 | **Example** 841 | 842 | ```js 843 | <%= sanitize("foo") %> 844 | //=> 'foo' 845 | ``` 846 | 847 | ### math 848 | 849 | #### [add](lib/helpers/math.js#L17) 850 | 851 | Return the product of `a` plus `b`. 852 | 853 | **Params** 854 | 855 | * `a` **{Number}** 856 | * `b` **{Number}** 857 | 858 | **Example** 859 | 860 | ```js 861 | <%= add(1, 2) %> 862 | //=> '3' 863 | ``` 864 | 865 | #### [subtract](lib/helpers/math.js#L31) 866 | 867 | Subtract `b` from `a`. 868 | 869 | **Params** 870 | 871 | * `a` **{Number}** 872 | * `b` **{Number}** 873 | 874 | **Example** 875 | 876 | ```js 877 | <%= subtract(5, 2) %> 878 | //=> '3' 879 | ``` 880 | 881 | #### [divide](lib/helpers/math.js#L46) 882 | 883 | Divide `a` (the numerator) by `b` (the divisor). 884 | 885 | **Params** 886 | 887 | * `a` **{Number}**: the numerator. 888 | * `b` **{Number}**: the divisor. 889 | * `returns` **{Number}**: The quotient of `a` divided by `b`. 890 | 891 | **Example** 892 | 893 | ```js 894 | <%= divide(10, 2) %> 895 | //=> '5' 896 | ``` 897 | 898 | #### [multiply](lib/helpers/math.js#L61) 899 | 900 | Multiply `a` by `b`. 901 | 902 | **Params** 903 | 904 | * `a` **{Number}** 905 | * `b` **{Number}** 906 | * `returns` **{Number}**: The product of `a` times `b`. 907 | 908 | **Example** 909 | 910 | ```js 911 | <%= divide(10, 2) %> 912 | //=> '5' 913 | ``` 914 | 915 | #### [floor](lib/helpers/math.js#L76) 916 | 917 | Returns the largest integer less than or equal to the given `number`. 918 | 919 | **Params** 920 | 921 | * `number` **{Number}** 922 | * `returns` **{Number}** 923 | 924 | **Example** 925 | 926 | ```js 927 | <%= floor(10.6) %> 928 | //=> '10' 929 | ``` 930 | 931 | #### [ceil](lib/helpers/math.js#L91) 932 | 933 | Returns the smallest integer greater than or equal to the given `number`. 934 | 935 | **Params** 936 | 937 | * `number` **{Number}** 938 | * `returns` **{Number}** 939 | 940 | **Example** 941 | 942 | ```js 943 | <%= ceil(10.1) %> 944 | //=> '11' 945 | ``` 946 | 947 | #### [round](lib/helpers/math.js#L109) 948 | 949 | Returns the value of the given `number` rounded to the nearest integer. 950 | 951 | **Params** 952 | 953 | * `number` **{Number}** 954 | * `returns` **{Number}** 955 | 956 | **Example** 957 | 958 | ```js 959 | <%= round(10.1) %> 960 | //=> '10' 961 | 962 | <%= round(10.5) %> 963 | //=> '11' 964 | ``` 965 | 966 | #### [sum](lib/helpers/math.js#L123) 967 | 968 | Returns the sum of all numbers in the given array. 969 | 970 | **Params** 971 | 972 | * `number` **{Number}** 973 | * `returns` **{Number}** 974 | 975 | **Example** 976 | 977 | ```js 978 | <%= sum([1, 2, 3, 4, 5]) %> 979 | //=> '15' 980 | ``` 981 | 982 | ### object 983 | 984 | #### [fallback](lib/helpers/object.js#L22) 985 | 986 | Specify a fallback value to use when the desired value is undefined. Note that undefined variables that are _not object properties_ with throw an error. 987 | 988 | **Params** 989 | 990 | * `a` **{any}**: The desired value. 991 | * `b` **{any}**: The fallback ("default") value 992 | * `returns` **{any}**: Either `a` or `b` 993 | 994 | **Example** 995 | 996 | ```js 997 | // when `title` is undefined, use the generic `site.title` 998 | <%= fallback(page.title, site.title) %> 999 | ``` 1000 | 1001 | #### [stringify](lib/helpers/object.js#L36) 1002 | 1003 | Stringify an object using `JSON.stringify()`. 1004 | 1005 | **Params** 1006 | 1007 | * `object` **{Object}** 1008 | * `returns` **{String}** 1009 | 1010 | **Example** 1011 | 1012 | ```js 1013 | <%= stringify({a: "a"}) %> 1014 | //=> '{"a":"a"}' 1015 | ``` 1016 | 1017 | #### [parse](lib/helpers/object.js#L50) 1018 | 1019 | Parse a string into an object using `JSON.parse()`. 1020 | 1021 | **Params** 1022 | 1023 | * `str` **{String}**: The string to parse. 1024 | * `returns` **{Object}**: The parsed object. 1025 | 1026 | **Example** 1027 | 1028 | ```js 1029 | <%= parse('{"foo":"bar"}')["foo"] %> 1030 | //=> 'bar' 1031 | ``` 1032 | 1033 | #### [get](lib/helpers/object.js#L65) 1034 | 1035 | Use property paths (`a.b.c`) get a nested value from an object. 1036 | 1037 | **Params** 1038 | 1039 | * `object` **{Object}** 1040 | * `path` **{String}**: Dot notation for the property to get. 1041 | * `returns` **{String}** 1042 | 1043 | **Example** 1044 | 1045 | ```js 1046 | <%= get({a: {b: 'c'}}, 'a.b') %> 1047 | //=> 'c' 1048 | ``` 1049 | 1050 | #### [keys](lib/helpers/object.js#L79) 1051 | 1052 | Returns an array of keys from the given `object`. 1053 | 1054 | **Params** 1055 | 1056 | * `object` **{Object}** 1057 | * `returns` **{Array}**: Keys from `object` 1058 | 1059 | **Example** 1060 | 1061 | ```js 1062 | <%= keys({a: 'b', c: 'd'}) %> 1063 | //=> '["a", "c"]' 1064 | ``` 1065 | 1066 | #### [isObject](lib/helpers/object.js#L97) 1067 | 1068 | Return true if the given `value` is an object, and not `null` or an array. 1069 | 1070 | **Params** 1071 | 1072 | * `value` **{Object}**: The value to check. 1073 | * `returns` **{Boolean}** 1074 | 1075 | **Example** 1076 | 1077 | ```js 1078 | <%= isObject(['a', 'b', 'c']) %> 1079 | //=> 'false' 1080 | 1081 | <%= isObject({a: 'b'}) %> 1082 | //=> 'true' 1083 | ``` 1084 | 1085 | #### [isPlainObject](lib/helpers/object.js#L117) 1086 | 1087 | Return true if the given `value` is a plain object. 1088 | 1089 | **Params** 1090 | 1091 | * `value` **{Object}**: The value to check. 1092 | * `returns` **{Boolean}** 1093 | 1094 | **Example** 1095 | 1096 | ```js 1097 | <%= isPlainObject(['a', 'b', 'c']) %> 1098 | //=> 'false' 1099 | 1100 | <%= isPlainObject({a: 'b'}) %> 1101 | //=> 'true' 1102 | 1103 | <%= isPlainObject(/foo/g) %> 1104 | //=> 'false' 1105 | ``` 1106 | 1107 | #### [hasOwn](lib/helpers/object.js#L129) 1108 | 1109 | Return true if `key` is an own, enumerable property 1110 | of the given `obj`. 1111 | 1112 | **Params** 1113 | 1114 | * `object` **{Object}** 1115 | * `key` **{String}** 1116 | * `returns` **{Boolean}** 1117 | 1118 | #### [omit](lib/helpers/object.js#L144) 1119 | 1120 | Return a copy of `object` exclusing the given `keys`. 1121 | 1122 | **Params** 1123 | 1124 | * `object` **{Object}**: Object with keys to omit. 1125 | * `keys` **{String}**: Keys to omit. 1126 | * `returns` **{Boolean}** 1127 | 1128 | **Example** 1129 | 1130 | ```js 1131 | <%= omit({a: 'a', b: 'b', c: 'c'}, ['a', 'c']) %> 1132 | //=> '{b: "b"}' 1133 | ``` 1134 | 1135 | #### [forIn](lib/helpers/object.js#L163) 1136 | 1137 | Iterate over the own and inherited enumerable properties of an object, and return an object with properties that evaluate to true from the callback. Exit early by returning `false`. 1138 | 1139 | **Params** 1140 | 1141 | * `object` **{Object}**: Object with keys to omit. 1142 | * `keys` **{String}**: Keys to omit. 1143 | * `returns` **{Boolean}** 1144 | 1145 | **Example** 1146 | 1147 | ```js 1148 | const context = { values: { a: 'b', c: 'd' } }; 1149 | const str = '<% forIn(values, function(val, key) { %><%= val %><% }) %>'; 1150 | const fn = _.template(str, { imports: helpers }); 1151 | assert.equal(fn(context), 'bd'); 1152 | ``` 1153 | 1154 | #### [forOwn](lib/helpers/object.js#L188) 1155 | 1156 | Iterate over the own enumerable properties of an object, and return an object with properties that evaluate to true from the callback. Exit early by returning `false` 1157 | 1158 | **Params** 1159 | 1160 | * `object` **{Object}**: Object with keys to omit. 1161 | * `keys` **{String}**: Keys to omit. 1162 | * `returns` **{Boolean}** 1163 | 1164 | **Example** 1165 | 1166 | ```js 1167 | const context = { values: { a: 'b', c: 'd' } }; 1168 | const str = '<% forOwn(values, function(val, key) { %><%= key %><% }) %>'; 1169 | const fn = _.template(str, { imports: helpers }); 1170 | console.log(fn(context)) //=> 'ac' 1171 | ``` 1172 | 1173 | #### [extend](lib/helpers/object.js#L205) 1174 | 1175 | Extend `o` with properties of other `objects`. 1176 | 1177 | **Params** 1178 | 1179 | * `o` **{Object}**: The target object. Pass an empty object to shallow clone. 1180 | * `objects` **{Object}** 1181 | * `returns` **{Object}** 1182 | 1183 | #### [merge](lib/helpers/object.js#L241) 1184 | 1185 | Recursively combine the properties of `o` with the 1186 | properties of other `objects`. 1187 | 1188 | **Params** 1189 | 1190 | * `o` **{Object}**: The target object. Pass an empty object to shallow clone. 1191 | * `objects` **{Object}** 1192 | * `returns` **{Object}** 1193 | 1194 | ### path 1195 | 1196 | #### [dirname](lib/helpers/path.js#L19) 1197 | 1198 | Return the dirname for the given `filepath`. Uses the node.js [path](https://nodejs.org/api/path.html) module. 1199 | 1200 | **Params** 1201 | 1202 | * `filepath` **{String}** 1203 | * `returns` **{String}**: Returns the directory part of the file path. 1204 | 1205 | **Example** 1206 | 1207 | ```js 1208 | <%= dirname("a/b/c/d") %> 1209 | //=> 'a/b/c' 1210 | ``` 1211 | 1212 | #### [basename](lib/helpers/path.js#L34) 1213 | 1214 | Return the basename for the given `filepath`. Uses the node.js [path](https://nodejs.org/api/path.html) module. 1215 | 1216 | **Params** 1217 | 1218 | * `filepath` **{String}** 1219 | * `returns` **{String}**: Returns the basename part of the file path. 1220 | 1221 | **Example** 1222 | 1223 | ```js 1224 | <%= basename("a/b/c/d.js") %> 1225 | //=> 'd.js' 1226 | ``` 1227 | 1228 | #### [filename](lib/helpers/path.js#L49) 1229 | 1230 | Returns the filename for the given `filepath`, excluding extension. Aliased as `stem`. 1231 | 1232 | **Params** 1233 | 1234 | * `filepath` **{String}** 1235 | * `returns` **{String}**: Returns the file name part of the file path. 1236 | 1237 | **Example** 1238 | 1239 | ```js 1240 | <%= filename("a/b/c/d.js") %> 1241 | //=> 'd' 1242 | ``` 1243 | 1244 | #### [stem](lib/helpers/path.js#L65) 1245 | 1246 | Alias for [filename](#filename). 1247 | 1248 | **Params** 1249 | 1250 | * `filepath` **{String}** 1251 | * `returns` **{String}**: Returns the file name part of the file path. 1252 | 1253 | **Example** 1254 | 1255 | ```js 1256 | <%= stem("a/b/c/d.js") %> 1257 | //=> 'd' 1258 | ``` 1259 | 1260 | #### [extname](lib/helpers/path.js#L80) 1261 | 1262 | Return the file extension for the given `filepath`. Uses the node.js [path](https://nodejs.org/api/path.html) module. 1263 | 1264 | **Params** 1265 | 1266 | * `filepath` **{String}** 1267 | * `returns` **{String}**: Returns a file extension 1268 | 1269 | **Example** 1270 | 1271 | ```js 1272 | <%= extname("foo.js") %> 1273 | //=> '.js' 1274 | ``` 1275 | 1276 | #### [ext](lib/helpers/path.js#L95) 1277 | 1278 | Return the file extension for the given `filepath`, excluding the `.`. 1279 | 1280 | **Params** 1281 | 1282 | * `filepath` **{String}** 1283 | * `returns` **{String}**: Returns a file extension without dot. 1284 | 1285 | **Example** 1286 | 1287 | ```js 1288 | <%= ext("foo.js") %> 1289 | //=> 'js' 1290 | ``` 1291 | 1292 | #### [resolve](lib/helpers/path.js#L110) 1293 | 1294 | Resolves the given paths to an absolute path. Uses the node.js [path](https://nodejs.org/api/path.html) module. 1295 | 1296 | **Params** 1297 | 1298 | * `filepath` **{String}** 1299 | * `returns` **{String}**: Returns a resolve 1300 | 1301 | **Example** 1302 | 1303 | ```js 1304 | <%= resolve('/foo/bar', './baz') %> 1305 | //=> '/foo/bar/baz' 1306 | ``` 1307 | 1308 | #### [relative](lib/helpers/path.js#L126) 1309 | 1310 | Get the relative path from file `a` to file `b`. Typically `a` and `b` would be variables passed on the context. Uses the node.js [path](https://nodejs.org/api/path.html) module. 1311 | 1312 | **Params** 1313 | 1314 | * `a` **{String}**: The "from" file path. 1315 | * `b` **{String}**: The "to" file path. 1316 | * `returns` **{String}**: Returns a relative path. 1317 | 1318 | **Example** 1319 | 1320 | ```js 1321 | <%= relative(a, b) %> 1322 | ``` 1323 | 1324 | #### [segments](lib/helpers/path.js#L162) 1325 | 1326 | Get specific (joined) segments of a file path by passing a range of array indices. 1327 | 1328 | **Params** 1329 | 1330 | * `filepath` **{String}**: The file path to split into segments. 1331 | * `returns` **{String}**: Returns a single, joined file path. 1332 | 1333 | **Example** 1334 | 1335 | ```js 1336 | <%= segments("a/b/c/d", "2", "3") %> 1337 | //=> 'c/d' 1338 | 1339 | <%= segments("a/b/c/d", "1", "3") %> 1340 | //=> 'b/c/d' 1341 | 1342 | <%= segments("a/b/c/d", "1", "2") %> 1343 | //=> 'b/c' 1344 | ``` 1345 | 1346 | #### [join](lib/helpers/path.js#L183) 1347 | 1348 | Join all arguments together and normalize the resulting `filepath`. Uses the node.js [path](https://nodejs.org/api/path.html) module. 1349 | 1350 | **Note**: there is also a `join()` array helper, dot notation 1351 | can be used with helpers to differentiate. Example: `<%= path.join() %>`. 1352 | 1353 | **Params** 1354 | 1355 | * `filepaths` **{String}**: List of file paths. 1356 | * `returns` **{String}**: Returns a single, joined file path. 1357 | 1358 | **Example** 1359 | 1360 | ```js 1361 | <%= join("a", "b") %> 1362 | //=> 'a/b' 1363 | ``` 1364 | 1365 | #### [isAbsolute](lib/helpers/path.js#L215) 1366 | 1367 | Returns true if a file path is an absolute path. An absolute path will always resolve to the same location, regardless of the working directory. Uses the node.js [path](https://nodejs.org/api/path.html) module. 1368 | 1369 | **Params** 1370 | 1371 | * `filepath` **{String}** 1372 | * `returns` **{String}**: Returns a resolve 1373 | 1374 | **Example** 1375 | 1376 | ```js 1377 | // posix 1378 | <%= isAbsolute('/foo/bar') %> 1379 | //=> 'true' 1380 | <%= isAbsolute('qux/') %> 1381 | //=> 'false' 1382 | <%= isAbsolute('.') %> 1383 | //=> 'false' 1384 | 1385 | // Windows 1386 | <%= isAbsolute('//server') %> 1387 | //=> 'true' 1388 | <%= isAbsolute('C:/foo/..') %> 1389 | //=> 'true' 1390 | <%= isAbsolute('bar\\baz') %> 1391 | //=> 'false' 1392 | <%= isAbsolute('.') %> 1393 | //=> 'false' 1394 | ``` 1395 | 1396 | #### [isRelative](lib/helpers/path.js#L247) 1397 | 1398 | Returns true if a file path is an absolute path. An absolute path will always resolve to the same location, regardless of the working directory. Uses the node.js [path](https://nodejs.org/api/path.html) module. 1399 | 1400 | **Params** 1401 | 1402 | * `filepath` **{String}** 1403 | * `returns` **{String}**: Returns a resolve 1404 | 1405 | **Example** 1406 | 1407 | ```js 1408 | // posix 1409 | <%= isRelative('/foo/bar') %> 1410 | //=> 'false' 1411 | <%= isRelative('qux/') %> 1412 | //=> 'true' 1413 | <%= isRelative('.') %> 1414 | //=> 'true' 1415 | 1416 | // Windows 1417 | <%= isRelative('//server') %> 1418 | //=> 'false' 1419 | <%= isRelative('C:/foo/..') %> 1420 | //=> 'false' 1421 | <%= isRelative('bar\\baz') %> 1422 | //=> 'true' 1423 | <%= isRelative('.') %> 1424 | //=> 'true' 1425 | ``` 1426 | 1427 | ### string 1428 | 1429 | #### [camelcase](lib/helpers/string.js#L24) 1430 | 1431 | camelCase the characters in `string`. 1432 | 1433 | **Params** 1434 | 1435 | * `string` **{String}**: The string to camelcase. 1436 | * `returns` **{String}** 1437 | 1438 | **Example** 1439 | 1440 | ```js 1441 | <%= camelcase("foo bar baz") %> 1442 | //=> 'fooBarBaz' 1443 | ``` 1444 | 1445 | #### [centerAlign](lib/helpers/string.js#L46) 1446 | 1447 | Center align the characters in a string using non-breaking spaces. 1448 | 1449 | **Params** 1450 | 1451 | * `str` **{String}**: The string to reverse. 1452 | * `returns` **{String}**: Centered string. 1453 | 1454 | **Example** 1455 | 1456 | ```js 1457 | <%= centerAlign("abc") %> 1458 | ``` 1459 | 1460 | #### [chop](lib/helpers/string.js#L69) 1461 | 1462 | Like trim, but removes both extraneous whitespace and non-word characters from the beginning and end of a string. 1463 | 1464 | **Params** 1465 | 1466 | * `string` **{String}**: The string to chop. 1467 | * `returns` **{String}** 1468 | 1469 | **Example** 1470 | 1471 | ```js 1472 | <%= chop("_ABC_") %> 1473 | //=> 'ABC' 1474 | 1475 | <%= chop("-ABC-") %> 1476 | //=> 'ABC' 1477 | 1478 | <%= chop(" ABC ") %> 1479 | //=> 'ABC' 1480 | ``` 1481 | 1482 | #### [count](lib/helpers/string.js#L88) 1483 | 1484 | Count the number of occurrances of a substring within a string. 1485 | 1486 | **Params** 1487 | 1488 | * `string` **{String}** 1489 | * `substring` **{String}** 1490 | * `returns` **{Number}**: The occurances of `substring` in `string` 1491 | 1492 | **Example** 1493 | 1494 | ```js 1495 | <%= count("abcabcabc", "a") %> 1496 | //=> '3' 1497 | ``` 1498 | 1499 | #### [dotcase](lib/helpers/string.js#L107) 1500 | 1501 | dot.case the characters in `string`. 1502 | 1503 | **Params** 1504 | 1505 | * `string` **{String}** 1506 | * `returns` **{String}** 1507 | 1508 | **Example** 1509 | 1510 | ```js 1511 | <%= dotcase("a-b-c d_e") %> 1512 | //=> 'a.b.c.d.e' 1513 | ``` 1514 | 1515 | #### [ellipsis](lib/helpers/string.js#L128) 1516 | 1517 | Truncate a string to the specified `length`, and append it with an elipsis, `…`. 1518 | 1519 | **Params** 1520 | 1521 | * `str` **{String}** 1522 | * `length` **{Number}**: The desired length of the returned string. 1523 | * `ch` **{String}**: Optionally pass custom characters to append. Default is `…`. 1524 | * `returns` **{String}**: The truncated string. 1525 | 1526 | **Example** 1527 | 1528 | ```js 1529 | <%= ellipsis("foo bar baz", 7) %> 1530 | //=> 'foo bar…' 1531 | ``` 1532 | 1533 | #### [isString](lib/helpers/string.js#L147) 1534 | 1535 | Returns true if the value is a string. 1536 | 1537 | **Params** 1538 | 1539 | * `val` **{String}** 1540 | * `returns` **{Boolean}**: True if the value is a string. 1541 | 1542 | **Example** 1543 | 1544 | ```js 1545 | <%= isString('abc') %> 1546 | //=> 'true' 1547 | 1548 | <%= isString(null) %> 1549 | //=> 'false' 1550 | ``` 1551 | 1552 | #### [lowercase](lib/helpers/string.js#L161) 1553 | 1554 | Lowercase the characters in the given `string`. 1555 | 1556 | **Params** 1557 | 1558 | * `string` **{String}**: The string to lowercase. 1559 | * `returns` **{String}** 1560 | 1561 | **Example** 1562 | 1563 | ```js 1564 | <%= lowercase("ABC") %> 1565 | //=> 'abc' 1566 | ``` 1567 | 1568 | #### [pascalcase](lib/helpers/string.js#L177) 1569 | 1570 | PascalCase the characters in `string`. 1571 | 1572 | **Params** 1573 | 1574 | * `string` **{String}** 1575 | * `returns` **{String}** 1576 | 1577 | **Example** 1578 | 1579 | ```js 1580 | <%= pascalcase("foo bar baz") %> 1581 | //=> 'FooBarBaz' 1582 | ``` 1583 | 1584 | #### [snakecase](lib/helpers/string.js#L196) 1585 | 1586 | snake_case the characters in `string`. 1587 | 1588 | **Params** 1589 | 1590 | * `string` **{String}** 1591 | * `returns` **{String}** 1592 | 1593 | **Example** 1594 | 1595 | ```js 1596 | <%= snakecase("a-b-c d_e") %> 1597 | //=> 'a_b_c_d_e' 1598 | ``` 1599 | 1600 | #### [split](lib/helpers/string.js#L214) 1601 | 1602 | Split `string` by the given `character`. 1603 | 1604 | **Params** 1605 | 1606 | * `string` **{String}**: The string to split. 1607 | * `returns` **{String}** `character`: Default is `,` 1608 | 1609 | **Example** 1610 | 1611 | ```js 1612 | <%= split("a,b,c", ",") %> 1613 | //=> ['a', 'b', 'c'] 1614 | ``` 1615 | 1616 | #### [strip](lib/helpers/string.js#L230) 1617 | 1618 | Strip `substring` from the given `string`. 1619 | 1620 | **Params** 1621 | 1622 | * `substring` **{String|RegExp}**: The string or regex pattern of the substring to remove. 1623 | * `string` **{String}**: The target string. 1624 | 1625 | **Example** 1626 | 1627 | ```js 1628 | <%= strip("foo-bar", "foo-") %> 1629 | //=> 'bar' 1630 | ``` 1631 | 1632 | #### [stripIndent](lib/helpers/string.js#L248) 1633 | 1634 | Strip the indentation from a `string`. 1635 | 1636 | **Params** 1637 | 1638 | * `string` **{String}**: The string to strip indentation from. 1639 | * `returns` **{String}** 1640 | 1641 | **Example** 1642 | 1643 | ```js 1644 | <%= stripIndent(" _ABC_") %> 1645 | //=> 'ABC' 1646 | ``` 1647 | 1648 | #### [trim](lib/helpers/string.js#L275) 1649 | 1650 | Trim extraneous whitespace from the beginning and end of a string. 1651 | 1652 | **Params** 1653 | 1654 | * `string` **{String}**: The string to trim. 1655 | * `returns` **{String}** 1656 | 1657 | **Example** 1658 | 1659 | ```js 1660 | <%= trim(" ABC ") %> 1661 | //=> 'ABC' 1662 | ``` 1663 | 1664 | #### [dashcase](lib/helpers/string.js#L291) 1665 | 1666 | dash-case the characters in `string`. This is similar to [slugify](https://github.com/simov/slugify), but [slugify](https://github.com/simov/slugify) makes the string compatible to be used as a URL slug. 1667 | 1668 | **Params** 1669 | 1670 | * `string` **{String}** 1671 | * `returns` **{String}** 1672 | 1673 | **Example** 1674 | 1675 | ```js 1676 | <%= dashcase("a b.c d_e") %> 1677 | //=> 'a-b-c-d-e' 1678 | ``` 1679 | 1680 | #### [pathcase](lib/helpers/string.js#L309) 1681 | 1682 | path/case the characters in `string`. 1683 | 1684 | **Params** 1685 | 1686 | * `string` **{String}** 1687 | * `returns` **{String}** 1688 | 1689 | **Example** 1690 | 1691 | ```js 1692 | <%= pathcase("a-b-c d_e") %> 1693 | //=> 'a/b/c/d/e' 1694 | ``` 1695 | 1696 | #### [sentencecase](lib/helpers/string.js#L327) 1697 | 1698 | Sentence-case the characters in `string`. 1699 | 1700 | **Params** 1701 | 1702 | * `string` **{String}** 1703 | * `returns` **{String}** 1704 | 1705 | **Example** 1706 | 1707 | ```js 1708 | <%= sentencecase("foo bar baz.") %> 1709 | //=> 'Foo bar baz.' 1710 | ``` 1711 | 1712 | #### [hyphenate](lib/helpers/string.js#L345) 1713 | 1714 | Replace spaces in a string with hyphens. This 1715 | 1716 | **Params** 1717 | 1718 | * `string` **{String}** 1719 | * `returns` **{String}** 1720 | 1721 | **Example** 1722 | 1723 | ```js 1724 | <%= hyphenate("a b c") %> 1725 | //=> 'a-b-c' 1726 | ``` 1727 | 1728 | #### [reverse](lib/helpers/string.js#L377) 1729 | 1730 | Reverse the characters in a string. 1731 | 1732 | **Params** 1733 | 1734 | * `str` **{String}**: The string to reverse. 1735 | * `returns` **{String}** 1736 | 1737 | **Example** 1738 | 1739 | ```js 1740 | <%= reverse("abc") %> 1741 | //=> 'cba' 1742 | ``` 1743 | 1744 | #### [rightAlign](lib/helpers/string.js#L394) 1745 | 1746 | Right align the characters in a string using non-breaking spaces. 1747 | 1748 | **Params** 1749 | 1750 | * `str` **{String}**: The string to reverse. 1751 | * `returns` **{String}**: Right-aligned string. 1752 | 1753 | **Example** 1754 | 1755 | ```js 1756 | <%= rightAlign(str) %> 1757 | ``` 1758 | 1759 | #### [replace](lib/helpers/string.js#L412) 1760 | 1761 | Replace occurrences of `a` with `b`. 1762 | 1763 | **Params** 1764 | 1765 | * `str` **{String}** 1766 | * `a` **{String|RegExp}**: Can be a string or regexp. 1767 | * `b` **{String}** 1768 | * `returns` **{String}** 1769 | 1770 | **Example** 1771 | 1772 | ```js 1773 | <%= replace("abcabc", /a/, "z") %> 1774 | //=> 'zbczbc' 1775 | ``` 1776 | 1777 | #### [titlecase](lib/helpers/string.js#L433) 1778 | 1779 | Truncate a string by removing all HTML tags and limiting the result to the specified `length`. 1780 | 1781 | **Params** 1782 | 1783 | * `str` **{String}** 1784 | * `length` **{Number}**: The desired length of the returned string. 1785 | * `returns` **{String}**: The truncated string. 1786 | 1787 | **Example** 1788 | 1789 | ```js 1790 | <%= titlecase("big deal") %> 1791 | //=> 'foo bar' 1792 | ``` 1793 | 1794 | #### [truncate](lib/helpers/string.js#L451) 1795 | 1796 | Truncate a string by removing all HTML tags and limiting the result to the specified `length`. 1797 | 1798 | **Params** 1799 | 1800 | * `str` **{String}** 1801 | * `length` **{Number}**: The desired length of the returned string. 1802 | * `returns` **{String}**: The truncated string. 1803 | 1804 | **Example** 1805 | 1806 | ```js 1807 | <%= truncate("foo bar baz", 7) %> 1808 | //=> 'foo bar' 1809 | ``` 1810 | 1811 | #### [uppercase](lib/helpers/string.js#L467) 1812 | 1813 | Uppercase the characters in a string. 1814 | 1815 | **Params** 1816 | 1817 | * `string` **{String}**: The string to uppercase. 1818 | * `returns` **{String}** 1819 | 1820 | **Example** 1821 | 1822 | ```js 1823 | <%= uppercase("abc") %> 1824 | //=> 'ABC' 1825 | ``` 1826 | 1827 | #### [wordwrap](lib/helpers/string.js#L484) 1828 | 1829 | Wrap words to a specified width using [word-wrap](https://github.com/jonschlinkert/word-wrap). 1830 | 1831 | **Params** 1832 | 1833 | * `string` **{String}**: The string with words to wrap. 1834 | * `object` **{Options}**: Options to pass to [word-wrap](https://github.com/jonschlinkert/word-wrap) 1835 | * `returns` **{String}**: Formatted string. 1836 | 1837 | **Example** 1838 | 1839 | ```js 1840 | <%= wordwrap("a b c d e f", {width: 5, newline: '
'}) %> 1841 | //=> ' a b c
d e f' 1842 | ``` 1843 | 1844 | ## Coverage 1845 | 1846 | ``` 1847 | Statements : 94.61% ( 439/464 ) 1848 | Branches : 88.37% ( 190/215 ) 1849 | Functions : 96.94% ( 95/98 ) 1850 | Lines : 94.42% ( 389/412 ) 1851 | ``` 1852 | 1853 | ## About 1854 | 1855 |
1856 | Contributing 1857 | 1858 | Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new). 1859 | 1860 |
1861 | 1862 |
1863 | Running Tests 1864 | 1865 | Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command: 1866 | 1867 | ```sh 1868 | $ npm install && npm test 1869 | ``` 1870 | 1871 |
1872 | 1873 |
1874 | Building docs 1875 | 1876 | _(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_ 1877 | 1878 | To generate the readme, run the following command: 1879 | 1880 | ```sh 1881 | $ npm install -g verbose/verb#dev verb-generate-readme && verb 1882 | ``` 1883 | 1884 |
1885 | 1886 | ### Related projects 1887 | 1888 | You might also be interested in the following projects (also visit the [github.com/helpers](https://github.com/helpers), where you can find 60+ additional standalone helpers!): 1889 | 1890 | * [assemble](https://www.npmjs.com/package/assemble): Get the rocks out of your socks! Assemble makes you fast at creating web projects… [more](https://github.com/assemble/assemble) | [homepage](https://github.com/assemble/assemble "Get the rocks out of your socks! Assemble makes you fast at creating web projects. Assemble is used by thousands of projects for rapid prototyping, creating themes, scaffolds, boilerplates, e-books, UI components, API documentation, blogs, building websit") 1891 | * [handlebars-helpers](https://www.npmjs.com/package/handlebars-helpers): More than 130 Handlebars helpers in ~20 categories. Helpers can be used with Assemble, Generate… [more](https://github.com/helpers/handlebars-helpers) | [homepage](https://github.com/helpers/handlebars-helpers "More than 130 Handlebars helpers in ~20 categories. Helpers can be used with Assemble, Generate, Verb, Ghost, gulp-handlebars, grunt-handlebars, consolidate, or any node.js/Handlebars project.") 1892 | * [templates](https://www.npmjs.com/package/templates): System for creating and managing template collections, and rendering templates with any node.js template engine… [more](https://github.com/jonschlinkert/templates) | [homepage](https://github.com/jonschlinkert/templates "System for creating and managing template collections, and rendering templates with any node.js template engine. Can be used as the basis for creating a static site generator or blog framework.") 1893 | 1894 | ### Contributors 1895 | 1896 | ### Author 1897 | 1898 | **Jon Schlinkert** 1899 | 1900 | * [GitHub Profile](https://github.com/jonschlinkert) 1901 | * [Twitter Profile](https://twitter.com/jonschlinkert) 1902 | * [LinkedIn Profile](https://linkedin.com/in/jonschlinkert) 1903 | 1904 | ### License 1905 | 1906 | Copyright © 2018, [Jon Schlinkert](https://github.com/jonschlinkert). 1907 | Released under the [MIT License](LICENSE). 1908 | 1909 | *** 1910 | 1911 | _This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.8.0, on November 24, 2018._ -------------------------------------------------------------------------------- /docs/toc.md: -------------------------------------------------------------------------------- 1 | ## Categories 2 | 3 | Currently **{%= total.helpers %} helpers** in **{%= total.categories %} categories**: 4 | 5 | {% Object.keys(methods).forEach(function(key) { %}{% var file = methods[key] %} 6 | - **{%= anchor(file) %}** ({%= link("code", file.path) %} | {%= link("unit tests", file.test.path) %}) {% }) %} 7 | 8 | ## All helpers 9 | 10 | {% Object.keys(methods).forEach(function(key) { %} 11 | {% var file = methods[key] %} 12 | 13 | ### {%= link(key + ' helpers', '#' + file.stem) %} 14 | Visit the: {%= link("code", file.path) %} | {%= link("unit tests", file.test.path) %} | {%= sectionIssue(file.stem) %}) 15 | {% Object.keys(file.data.methods).forEach(function(k) { %}{% var obj = file.data.methods[k] %} 16 | {%= bullet(obj) %} ({%= codeLink(obj) %} | {%= testLink(obj) %}){% }) %} 17 | {% }) %} 18 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /*! 4 | * template-helpers 5 | * 6 | * Copyright (c) 2015-present, Jon Schlinkert. 7 | * Licensed under the MIT License. 8 | */ 9 | 10 | const helpers = require('./lib/helpers'); 11 | 12 | module.exports = key => { 13 | let res = {}; 14 | 15 | if (typeof key === 'string') { 16 | res = helpers[key]; 17 | res[key] = res; 18 | return res; 19 | } 20 | 21 | if (Array.isArray(key)) { 22 | return key.reduce((acc, k) => { 23 | acc[k] = helpers[k]; 24 | for (let prop of Object.keys(acc[k])) { 25 | acc[prop] = acc[k][prop]; 26 | } 27 | return acc; 28 | }, {}); 29 | } 30 | 31 | for (let prop of Object.keys(helpers)) { 32 | let group = helpers[prop]; 33 | res[prop] = group; 34 | 35 | for (let k of Object.keys(group)) { 36 | res[k] = group[k]; 37 | } 38 | } 39 | return res; 40 | }; 41 | -------------------------------------------------------------------------------- /lib/helpers/array.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const object = require('./object'); 4 | const utils = require('../utils'); 5 | 6 | /** 7 | * Returns true if `value` is an array. 8 | * 9 | * ```js 10 | * <%= isArray('a, b, c') %> 11 | * //=> 'false' 12 | * 13 | * <%= isArray(['a, b, c']) %> 14 | * //=> 'true' 15 | * ``` 16 | * @param {*} `value` The value to test. 17 | * @return {Boolean} 18 | * @api public 19 | */ 20 | 21 | exports.isArray = val => Array.isArray(val); 22 | 23 | /** 24 | * Cast `val` to an array. 25 | * 26 | * ```js 27 | * <%= arrayify('a') %> 28 | * //=> '["a"]' 29 | * 30 | * <%= arrayify({a: 'b'}) %> 31 | * //=> '[{a: "b"}]' 32 | * 33 | * <%= arrayify(['a']) %> 34 | * //=> '["a"]' 35 | * ``` 36 | * @param {*} `val` The value to arrayify. 37 | * @return {Array} An array. 38 | * @return {Array} 39 | * @api public 40 | */ 41 | 42 | exports.arrayify = val => [].concat(val != null ? val : []); 43 | 44 | /** 45 | * Returns the first item, or first `n` items of an array. 46 | * 47 | * ```js 48 | * <%= first(['a', 'b', 'c', 'd', 'e'], 2) %> 49 | * //=> '["a", "b"]' 50 | * ``` 51 | * @name .first 52 | * @param {Array} `array` 53 | * @param {Number} `n` Number of items to return, starting at `0`. 54 | * @return {Array} 55 | * @api public 56 | */ 57 | 58 | exports.first = (arr, n) => { 59 | if (utils.isEmpty(arr)) return ''; 60 | if (utils.isNumber(n)) { 61 | return arr.slice(0, n); 62 | } 63 | return arr[0]; 64 | }; 65 | 66 | /** 67 | * Returns the last item, or last `n` items of an array. 68 | * 69 | * ```js 70 | * <%= last(['a', 'b', 'c', 'd', 'e'], 2) %> 71 | * //=> '["d", "e"]' 72 | * ``` 73 | * @param {Array} `array` 74 | * @param {Number} `n` Number of items to return, starting with the last item. 75 | * @return {Array} 76 | * @api public 77 | */ 78 | 79 | exports.last = (arr, n) => { 80 | if (utils.isEmpty(arr)) return ''; 81 | if (!utils.isNumber(n)) { 82 | return arr[arr.length - 1]; 83 | } 84 | return arr.slice(-n); 85 | }; 86 | 87 | /** 88 | * Returns all of the items in an array up to the specified number 89 | * Opposite of `<%= after() %`. 90 | * 91 | * ```js 92 | * <%= before(['a', 'b', 'c'], 2) %> 93 | * //=> '["a", "b"]' 94 | * ``` 95 | * @param {Array} `array` 96 | * @param {Number} `n` 97 | * @return {Array} Array excluding items after the given number. 98 | * @crosslink after 99 | * @api public 100 | */ 101 | 102 | exports.before = (arr, n) => { 103 | return !utils.isEmpty(arr) ? arr.slice(0, -n) : ''; 104 | }; 105 | 106 | /** 107 | * Returns all of the items in an arry after the specified index. 108 | * Opposite of `<%= before() %`. 109 | * 110 | * ```js 111 | * <%= after(['a', 'b', 'c'], 1) %> 112 | * //=> '["c"]' 113 | * ``` 114 | * @param {Array} `array` Collection 115 | * @param {Number} `n` Starting index (number of items to exclude) 116 | * @return {Array} Array exluding `n` items. 117 | * @crosslink before 118 | * @api public 119 | */ 120 | 121 | exports.after = (arr, n) => { 122 | return !utils.isEmpty(arr) ? arr.slice(n) : ''; 123 | }; 124 | 125 | /** 126 | * Calling `fn` on each element of the given `array` with 127 | * the given `context`. 128 | * 129 | * ```js 130 | * function double(str) { 131 | * return str + str; 132 | * } 133 | * ``` 134 | * 135 | * Assuming that `double` has been registered as a helper: 136 | * 137 | * ```js 138 | * <%= each(['a', 'b', 'c'], double, ctx) %> 139 | * //=> '["aa", "bb", "cc"]' 140 | * ``` 141 | * @param {Array} `array` 142 | * @param {String} `fn` The function to call on each element in the given array. 143 | * @return {String} 144 | * @api public 145 | */ 146 | 147 | exports.each = (arr, fn, context) => { 148 | if (utils.isEmpty(arr)) { 149 | return ''; 150 | } 151 | 152 | let len = arr.length; 153 | let idx = -1; 154 | let res = ''; 155 | let val; 156 | 157 | while (++idx < len) { 158 | if ((val = fn.call(context, arr[idx], idx, arr)) === false) { 159 | break; 160 | } 161 | res += val; 162 | } 163 | return res; 164 | }; 165 | 166 | /** 167 | * Returns a new array, created by calling `function` 168 | * on each element of the given `array`. 169 | * 170 | * ```js 171 | * function double(str) { 172 | * return str + str; 173 | * } 174 | * ``` 175 | * 176 | * Assuming that `double` has been registered as a helper: 177 | * 178 | * ```js 179 | * <%= map(['a', 'b', 'c'], double) %> 180 | * //=> '["aa", "bb", "cc"]' 181 | * ``` 182 | * @param {Array} `array` 183 | * @param {String} `fn` The function to call on each element in the given array. 184 | * @return {String} 185 | * @api public 186 | */ 187 | 188 | exports.map = (arr, fn, context) => { 189 | if (utils.isEmpty(arr)) return ''; 190 | 191 | let len = arr.length; 192 | let res = new Array(len); 193 | let idx = -1; 194 | 195 | while (++idx < len) { 196 | res[idx] = fn.call(context, arr[idx], idx, arr); 197 | } 198 | return res; 199 | }; 200 | 201 | /** 202 | * Join all elements of array into a string, optionally using a 203 | * given separator. 204 | * 205 | * ```js 206 | * <%= join(['a', 'b', 'c']) %> 207 | * //=> 'a, b, c' 208 | * 209 | * <%= join(['a', 'b', 'c'], '-') %> 210 | * //=> 'a-b-c' 211 | * ``` 212 | * @param {Array} `array` 213 | * @param {String} `sep` The separator to use. 214 | * @return {String} 215 | * @api public 216 | */ 217 | 218 | exports.join = (arr, sep) => { 219 | if (utils.isEmpty(arr)) return ''; 220 | return arr.join(typeof sep !== 'string' ? ', ' : sep); 221 | }; 222 | 223 | /** 224 | * Sort the given `array`. If an array of objects is passed, 225 | * you may optionally pass a `key` to sort on as the second 226 | * argument. You may alternatively pass a sorting function as 227 | * the second argument. 228 | * 229 | * ```js 230 | * <%= sort(["b", "a", "c"]) %> 231 | * //=> 'a,b,c' 232 | * 233 | * <%= sort([{a: "zzz"}, {a: "aaa"}], "a") %> 234 | * //=> '[{"a":"aaa"},{"a":"zzz"}]' 235 | * ``` 236 | * @param {Array} `array` the array to sort. 237 | * @param {String|Function} `key` The object key to sort by, or sorting function. 238 | * @api public 239 | */ 240 | 241 | exports.sort = (arr, key) => { 242 | if (utils.isEmpty(arr)) return ''; 243 | if (typeof key === 'function') { 244 | return arr.sort(key); 245 | } 246 | if (typeof key !== 'string') { 247 | return arr.sort(); 248 | } 249 | return arr.sort((a, b) => { 250 | if (object.isObject(a) && typeof a[key] === 'string') { 251 | return a[key].localeCompare(b[key]); 252 | } 253 | if (typeof a === 'string') { 254 | return a.localeCompare(b); 255 | } 256 | return a > b; 257 | }); 258 | }; 259 | 260 | /** 261 | * Returns the length of the given array. 262 | * 263 | * ```js 264 | * <%= length(['a', 'b', 'c']) %> 265 | * //=> 3 266 | * ``` 267 | * @param {Array} `array` 268 | * @return {Number} The length of the array. 269 | * @api public 270 | */ 271 | 272 | exports.length = arr => { 273 | if (utils.isEmpty(arr)) return ''; 274 | return Array.isArray(arr) ? arr.length : 0; 275 | }; 276 | 277 | /** 278 | * Returns an array with all falsey values removed. 279 | * 280 | * ```js 281 | * <%= compact([null, a, undefined, 0, false, b, c, '']) %> 282 | * //=> '["a", "b", "c"]' 283 | * ``` 284 | * @param {Array} `arr` 285 | * @return {Array} 286 | * @api public 287 | */ 288 | 289 | exports.compact = arr => { 290 | return !utils.isEmpty(arr) ? arr.filter(Boolean) : ''; 291 | }; 292 | 293 | /** 294 | * Return the difference between the first array and 295 | * additional arrays. 296 | * 297 | * ```js 298 | * <%= difference(["a", "c"], ["a", "b"]) %> 299 | * //=> '["c"]' 300 | * ``` 301 | * @param {Array} `array` The array to compare againts. 302 | * @param {Array} `arrays` One or more additional arrays. 303 | * @return {Array} 304 | * @api public 305 | */ 306 | 307 | exports.difference = (...args) => { 308 | if (utils.isEmpty(args)) return ''; 309 | let [ a, b, c ] = args; 310 | let len = a.length; 311 | let arr = []; 312 | let rest; 313 | 314 | if (b == null) { 315 | return a; 316 | } 317 | 318 | if (c == null) { 319 | rest = b; 320 | } else { 321 | rest = utils.flatten(args.slice(1)); 322 | } 323 | 324 | while (len--) { 325 | if (rest.indexOf(a[len]) === -1) { 326 | arr.unshift(a[len]); 327 | } 328 | } 329 | return arr; 330 | }; 331 | 332 | /** 333 | * Return an array, free of duplicate values. 334 | * 335 | * ```js 336 | * <%= unique(['a', 'b', 'c', 'c']) % 337 | * => '["a", "b", "c"]' 338 | * ``` 339 | * @param {Array} `array` The array to uniquify 340 | * @return {Array} Duplicate-free array 341 | * @api public 342 | */ 343 | 344 | exports.unique = arr => { 345 | if (utils.isEmpty(arr)) return ''; 346 | let len = arr.length; 347 | let i = -1; 348 | 349 | while (i++ < len) { 350 | let j = i + 1; 351 | 352 | for (; j < arr.length; ++j) { 353 | if (arr[i] === arr[j]) { 354 | arr.splice(j--, 1); 355 | } 356 | } 357 | } 358 | return arr; 359 | }; 360 | 361 | /** 362 | * Returns an array of unique values using strict equality for comparisons. 363 | * 364 | * ```js 365 | * <%= union(["a"], ["b"], ["c"]) %> 366 | * //=> '["a", "b", "c"]' 367 | * ``` 368 | * @param {Array} `arr` 369 | * @return {Array} 370 | * @api public 371 | */ 372 | 373 | exports.union = (...args) => { 374 | return !utils.isEmpty(args) ? utils.union([], [].concat.apply([], args)) : ''; 375 | }; 376 | 377 | /** 378 | * Shuffle the items in an array. 379 | * 380 | * ```js 381 | * <%= shuffle(["a", "b", "c"]) %> 382 | * //=> ["c", "a", "b"] 383 | * ``` 384 | * @param {Array} `arr` 385 | * @return {Array} 386 | * @api public 387 | */ 388 | 389 | exports.shuffle = arr => { 390 | let len = arr.length; 391 | let res = new Array(len); 392 | let i = -1; 393 | 394 | while (++i < len) { 395 | let rand = utils.random(0, i); 396 | if (i !== rand) { 397 | res[i] = res[rand]; 398 | } 399 | res[rand] = arr[i]; 400 | } 401 | return res; 402 | }; 403 | -------------------------------------------------------------------------------- /lib/helpers/code.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const utils = require('../utils'); 6 | const object = require('./object'); 7 | 8 | /** 9 | * Embed code from an external file as preformatted text. 10 | * 11 | * ```js 12 | * <%= embed('path/to/file.js') %> 13 | * 14 | * // specify the language to use 15 | * <%= embed('path/to/file.hbs', 'html') %> 16 | * ``` 17 | * @param {String} `fp` filepath to the file to embed. 18 | * @param {String} `language` Optionally specify the language to use for syntax highlighting. 19 | * @return {String} 20 | * @api public 21 | */ 22 | 23 | exports.embed = (fp, ext) => { 24 | ext = typeof ext !== 'string' ? path.extname(fp).slice(1) : ext; 25 | let code = fs.readFileSync(fp, 'utf8'); 26 | 27 | // if the string is markdown, escape backticks 28 | if (ext === 'markdown' || ext === 'md') { 29 | code = code.split('`').join('`'); 30 | } 31 | return utils.toCodeBlock(code, ext) + '\n'; 32 | }; 33 | 34 | /** 35 | * Generate the HTML for a jsFiddle link with the given `params` 36 | * 37 | * ```js 38 | * <%= jsfiddle({id: '0dfk10ks', {tabs: true}}) %> 39 | * ``` 40 | * @param {Object} `params` 41 | * @return {String} 42 | * @api public 43 | */ 44 | 45 | exports.jsfiddle = attr => { 46 | if (!attr || !object.isPlainObject(attr)) return ''; 47 | attr.id = 'http://jsfiddle.net/' + (attr.id || ''); 48 | attr.width = attr.width || '100%'; 49 | attr.height = attr.height || '300'; 50 | attr.skin = attr.skin || '/presentation/'; 51 | attr.tabs = (attr.tabs || 'result,js,html,css') + attr.skin; 52 | attr.src = attr.id + '/embedded/' + attr.tabs; 53 | attr.allowfullscreen = attr.allowfullscreen || 'allowfullscreen'; 54 | attr.frameborder = attr.frameborder || '0'; 55 | attr = object.omit(attr, ['id', 'tabs', 'skin']); 56 | return ''; 57 | }; 58 | -------------------------------------------------------------------------------- /lib/helpers/collection.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const any = require('any'); 4 | const iterator = require('../iterator'); 5 | const utils = require('../utils'); 6 | 7 | /** 8 | * Returns `true` if `value` exists in the given string, array 9 | * or object. See [any] for documentation. 10 | * 11 | * @param {*} `value` 12 | * @param {*} `target` 13 | * @param {Object} `options` 14 | * @api public 15 | */ 16 | 17 | exports.any = any; 18 | 19 | /** 20 | * Filter the given array or object to contain only the matching values. 21 | * 22 | * ```js 23 | * <%= filter(['foo', 'bar', 'baz']) %> 24 | * //=> '["a", "b", "c"]' 25 | * ``` 26 | * 27 | * @param {Array} `arr` 28 | * @return {Array} 29 | * @api public 30 | */ 31 | 32 | exports.filter = (val, fn, context) => { 33 | if (utils.isEmpty(val)) return ''; 34 | let iter = () => {}; 35 | 36 | if (typeof fn === 'string') { 37 | let prop = fn; 38 | iter = target => { 39 | return typeof target === 'string' ? target === prop : target[prop]; 40 | }; 41 | } else { 42 | iter = iterator(fn, context); 43 | } 44 | 45 | if (typeof val === 'string') { 46 | return iter(val); 47 | } 48 | 49 | if (Array.isArray(val)) { 50 | return val.filter(iter); 51 | } 52 | 53 | if (utils.isObject(val)) { 54 | let obj = val; 55 | let res = {}; 56 | for (let key in obj) { 57 | if (obj.hasOwnProperty(key) && iter(key)) { 58 | res[key] = obj[key]; 59 | } 60 | } 61 | return res; 62 | } 63 | }; 64 | -------------------------------------------------------------------------------- /lib/helpers/conditional.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Returns true when both `valueA` and `valueB` are truthy. 5 | * 6 | * @name .and 7 | * @param {any} `valueA` 8 | * @param {any} `valueB` 9 | * @return {Boolean} 10 | * @api public 11 | */ 12 | 13 | exports.and = (valueA, valueB) => !!valueA && !!valueB; 14 | 15 | /** 16 | * Render a block when a comparison of the first and third arguments 17 | * returns true. 18 | * 19 | * ```js 20 | * <%= compare("foo", "!==", "bar") %> 21 | * ``` 22 | * @name .compare 23 | * @param {String} `valueA` 24 | * @param {String} `operator` The operator to use for the comparison (must be a quoted string). 25 | * @param {String} `valueB` 26 | * @return {Boolean} 27 | * @api public 28 | */ 29 | 30 | exports.compare = function(a, operator, b) { 31 | /* eslint-disable eqeqeq */ 32 | 33 | if (arguments.length < 3) { 34 | throw new Error('"compare" helper - expected 3 arguments'); 35 | } 36 | 37 | switch (operator) { 38 | case '!=': 39 | return a != b; 40 | case '!==': 41 | return a !== b; 42 | case '<': 43 | return a < b; 44 | case '<=': 45 | return a <= b; 46 | case '==': 47 | return a == b; 48 | case '===': 49 | return a === b; 50 | case '>': 51 | return a > b; 52 | case '>=': 53 | return a >= b; 54 | case 'typeof': 55 | return typeof a === b; 56 | default: { 57 | throw new Error(`"compare" helper - invalid operator: "${operator}"`); 58 | } 59 | } 60 | return false; 61 | }; 62 | 63 | /** 64 | * Returns the first truthy value. 65 | * 66 | * @name .find 67 | * @param {...args} `...values` 68 | * @return {any} 69 | * @api public 70 | */ 71 | 72 | exports.find = (...values) => values.find(v => !!v); 73 | 74 | /** 75 | * Returns true when all provided values are truthy. 76 | * 77 | * @name .every 78 | * @param {...any} `...values` 79 | * @return {Boolean} 80 | * @api public 81 | */ 82 | 83 | exports.every = (...args) => { 84 | for (let ele of args) if (!ele) return false; 85 | return true; 86 | }; 87 | 88 | /** 89 | * Returns true when `valueA` is greater than `valueB`. 90 | * 91 | * @name .gt 92 | * @param {String} `valueA` 93 | * @param {String} `valueB` 94 | * @return {Boolean} 95 | * @api public 96 | */ 97 | 98 | exports.gt = (a, b) => a > b; 99 | 100 | /** 101 | * Returns true when `valueA` is greater than or equal to `valueB`. 102 | * 103 | * @name .gte 104 | * @param {String} `valueA` 105 | * @param {String} `valueB` 106 | * @return {Boolean} 107 | * @api public 108 | */ 109 | 110 | exports.gte = (a, b) => a >= b; 111 | 112 | /** 113 | * Return true if `key` is an own, enumerable property 114 | * of the given `obj`. 115 | * 116 | * @param {Object} `object` 117 | * @param {String} `key` 118 | * @return {Boolean} 119 | * @api public 120 | */ 121 | 122 | exports._if = function if_(fn, a, b, thisArg) { 123 | if (typeof fn !== 'function') return ''; 124 | 125 | thisArg = thisArg || this || null; 126 | let res = fn.call(thisArg, a, b); 127 | if (res) return a; 128 | return b; 129 | }; 130 | 131 | /** 132 | * Returns true when `valueA` equals `valueB`. 133 | * 134 | * @name .is 135 | * @param {String} `valueA` 136 | * @param {String} `valueB` 137 | * @param {String} `strict` 138 | * @return {Boolean} 139 | * @api public 140 | */ 141 | 142 | exports.is = (a, b, strict = true) => { 143 | return strict ? a === b : a == b; 144 | }; 145 | 146 | /** 147 | * Alias for [is](#is). 148 | * 149 | * @name .eq 150 | * @param {String} `valueA` 151 | * @param {String} `valueB` 152 | * @param {String} `strict` 153 | * @return {Boolean} 154 | * @api public 155 | */ 156 | 157 | exports.eq = exports.is; 158 | 159 | /** 160 | * Returns true when `valueA` does not equal `valueB`. 161 | * 162 | * @name .isnt 163 | * @param {String} `valueA` 164 | * @param {String} `valueB` 165 | * @return {Boolean} 166 | * @api public 167 | */ 168 | 169 | exports.isnt = (a, b, strict = true) => { 170 | return strict ? a !== b : a != b; 171 | }; 172 | 173 | /** 174 | * Alias for [isnt](#isnt). 175 | * 176 | * @name .notEq 177 | * @param {String} `valueA` 178 | * @param {String} `valueB` 179 | * @return {Boolean} 180 | * @api public 181 | */ 182 | 183 | exports.notEq = exports.isnt; 184 | 185 | /** 186 | * Returns true when `valueA` is less than `valueB`. 187 | * 188 | * @name .lt 189 | * @param {String} `valueA` 190 | * @param {String} `valueB` 191 | * @return {Boolean} 192 | * @api public 193 | */ 194 | 195 | exports.lt = (a, b) => a < b; 196 | 197 | /** 198 | * Returns true when `valueA` is less than or equal to `valueB`. 199 | * 200 | * @name .lte 201 | * @param {String} `valueA` 202 | * @param {String} `valueB` 203 | * @return {Boolean} 204 | * @api public 205 | */ 206 | 207 | exports.lte = (a, b) => a <= b; 208 | 209 | /** 210 | * Returns `valueA` if thruthy, otherwise `valueB`. 211 | * 212 | * @name .or 213 | * @param {any} `valueA` 214 | * @param {any} `valueB` 215 | * @return {any} 216 | * @api public 217 | */ 218 | 219 | exports.or = (valueA, valueB) => !!valueA ? valueA : valueB; 220 | 221 | /** 222 | * Returns true when at least one value is truthy. 223 | * 224 | * @name .some 225 | * @param {...any} `...values` 226 | * @return {Boolean} 227 | * @api public 228 | */ 229 | 230 | exports.some = (...args) => { 231 | for (let ele of args) if (!!ele) return true; 232 | return false; 233 | }; 234 | -------------------------------------------------------------------------------- /lib/helpers/fs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | 5 | /** 6 | * Return true if a file exists 7 | * 8 | * ```js 9 | * <%= exists("foo.js") %> 10 | * ``` 11 | * @param {String} `filepath` Path of the file to check. 12 | * @return {Boolean} True if the file exists 13 | * @api public 14 | */ 15 | 16 | exports.exists = filepath => filepath && fs.existsSync(filepath); 17 | 18 | /** 19 | * Read a file from the file system and inject its content 20 | * 21 | * ```js 22 | * <%= read("foo.js") %> 23 | * ``` 24 | * @param {String} `filepath` Path of the file to read. 25 | * @return {String} Contents of the given file. 26 | * @api public 27 | */ 28 | 29 | exports.read = filepath => { 30 | if (!exports.exists(filepath)) return ''; 31 | return fs.readFileSync(filepath, 'utf8'); 32 | }; 33 | -------------------------------------------------------------------------------- /lib/helpers/html.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const string = require('./string'); 4 | 5 | /** 6 | * Escape HTML characters in a string. 7 | * 8 | * ```js 9 | * <%= escapeHtml("foo") %> 10 | * //=> <span>foo</span> 11 | * ``` 12 | * 13 | * @param {String} `str` String of HTML with characters to escape. 14 | * @return {String} 15 | * @api public 16 | */ 17 | 18 | exports.escapeHtml = str => { 19 | if (!string.isString(str)) return ''; 20 | return str.replace(/[/"'&<>]/g, ch => { 21 | return ({ 22 | '"': '"', 23 | '&': '&', 24 | '/': '/', 25 | '<': '<', 26 | '>': '>', 27 | '\'': ''' 28 | })[ch]; 29 | }); 30 | }; 31 | 32 | /** 33 | * Strip HTML tags from a string, so that only the text nodes 34 | * are preserved. 35 | * 36 | * ```js 37 | * <%= sanitize("foo") %> 38 | * //=> 'foo' 39 | * ``` 40 | * 41 | * @param {String} `str` The string of HTML to sanitize. 42 | * @return {String} 43 | * @api public 44 | */ 45 | 46 | exports.sanitize = str => { 47 | return string.isString(str) ? str.replace(/(<([^>]+)>)/g, '').trim() : ''; 48 | }; 49 | -------------------------------------------------------------------------------- /lib/helpers/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const define = (obj, k, fn) => { 4 | Reflect.defineProperty(obj, k, { 5 | enumerable: true, 6 | get: fn 7 | }); 8 | }; 9 | 10 | define(exports, 'string', () => require('./string')); 11 | define(exports, 'path', () => require('./path')); 12 | define(exports, 'object', () => require('./object')); 13 | define(exports, 'math', () => require('./math')); 14 | define(exports, 'html', () => require('./html')); 15 | define(exports, 'fs', () => require('./fs')); 16 | define(exports, 'conditional', () => require('./conditional')); 17 | define(exports, 'collection', () => require('./collection')); 18 | define(exports, 'code', () => require('./code')); 19 | define(exports, 'array', () => require('./array')); 20 | -------------------------------------------------------------------------------- /lib/helpers/math.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const utils = require('../utils'); 4 | 5 | /** 6 | * Return the product of `a` plus `b`. 7 | * 8 | * ```js 9 | * <%= add(1, 2) %> 10 | * //=> '3' 11 | * ``` 12 | * @param {Number} `a` 13 | * @param {Number} `b` 14 | * @api public 15 | */ 16 | 17 | exports.add = (a, b) => a + b; 18 | 19 | /** 20 | * Subtract `b` from `a`. 21 | * 22 | * ```js 23 | * <%= subtract(5, 2) %> 24 | * //=> '3' 25 | * ``` 26 | * @param {Number} `a` 27 | * @param {Number} `b` 28 | * @api public 29 | */ 30 | 31 | exports.subtract = (a, b) => Number(a) - Number(b); 32 | 33 | /** 34 | * Divide `a` (the numerator) by `b` (the divisor). 35 | * 36 | * ```js 37 | * <%= divide(10, 2) %> 38 | * //=> '5' 39 | * ``` 40 | * @param {Number} `a` the numerator. 41 | * @param {Number} `b` the divisor. 42 | * @return {Number} The quotient of `a` divided by `b`. 43 | * @api public 44 | */ 45 | 46 | exports.divide = (a, b) => Number(a) / Number(b); 47 | 48 | /** 49 | * Multiply `a` by `b`. 50 | * 51 | * ```js 52 | * <%= divide(10, 2) %> 53 | * //=> '5' 54 | * ``` 55 | * @param {Number} `a` 56 | * @param {Number} `b` 57 | * @return {Number} The product of `a` times `b`. 58 | * @api public 59 | */ 60 | 61 | exports.multiply = (a, b) => Number(a) * Number(b); 62 | 63 | /** 64 | * Returns the largest integer less than or equal to the 65 | * given `number`. 66 | * 67 | * ```js 68 | * <%= floor(10.6) %> 69 | * //=> '10' 70 | * ``` 71 | * @param {Number} `number` 72 | * @return {Number} 73 | * @api public 74 | */ 75 | 76 | exports.floor = n => Math.floor(n); 77 | 78 | /** 79 | * Returns the smallest integer greater than or equal to the 80 | * given `number`. 81 | * 82 | * ```js 83 | * <%= ceil(10.1) %> 84 | * //=> '11' 85 | * ``` 86 | * @param {Number} `number` 87 | * @return {Number} 88 | * @api public 89 | */ 90 | 91 | exports.ceil = n => Math.ceil(n); 92 | 93 | /** 94 | * Returns the value of the given `number` rounded to the 95 | * nearest integer. 96 | * 97 | * ```js 98 | * <%= round(10.1) %> 99 | * //=> '10' 100 | * 101 | * <%= round(10.5) %> 102 | * //=> '11' 103 | * ``` 104 | * @param {Number} `number` 105 | * @return {Number} 106 | * @api public 107 | */ 108 | 109 | exports.round = n => Math.round(n); 110 | 111 | /** 112 | * Returns the sum of all numbers in the given array. 113 | * 114 | * ```js 115 | * <%= sum([1, 2, 3, 4, 5]) %> 116 | * //=> '15' 117 | * ``` 118 | * @param {Number} `number` 119 | * @return {Number} 120 | * @api public 121 | */ 122 | 123 | exports.sum = (...args) => { 124 | let arr = [].concat.apply([], args); 125 | let len = arr.length; 126 | let idx = -1; 127 | let num = 0; 128 | 129 | while (++idx < len) { 130 | if (!utils.isNumber(arr[idx])) { 131 | continue; 132 | } 133 | num += (+arr[idx]); 134 | } 135 | return num; 136 | }; 137 | -------------------------------------------------------------------------------- /lib/helpers/object.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const hasOwn = Object.prototype.hasOwnProperty; 4 | const get = require('get-value'); 5 | const utils = require('../utils'); 6 | 7 | /** 8 | * Specify a fallback value to use when the desired 9 | * value is undefined. Note that undefined variables 10 | * that are _not object properties_ with throw an error. 11 | * 12 | * ```js 13 | * // when `title` is undefined, use the generic `site.title` 14 | * <%= fallback(page.title, site.title) %> 15 | * ``` 16 | * @param {*} `a` The desired value. 17 | * @param {*} `b` The fallback ("default") value 18 | * @return {*} Either `a` or `b` 19 | * @api public 20 | */ 21 | 22 | exports.fallback = (a, b) => a != null ? a : b; 23 | 24 | /** 25 | * Stringify an object using `JSON.stringify()`. 26 | * 27 | * ```js 28 | * <%= stringify({a: "a"}) %> 29 | * //=> '{"a":"a"}' 30 | * ``` 31 | * @param {Object} `object` 32 | * @return {String} 33 | * @api public 34 | */ 35 | 36 | exports.stringify = obj => JSON.stringify(obj); 37 | 38 | /** 39 | * Parse a string into an object using `JSON.parse()`. 40 | * 41 | * ```js 42 | * <%= parse('{"foo":"bar"}')["foo"] %> 43 | * //=> 'bar' 44 | * ``` 45 | * @param {String} `str` The string to parse. 46 | * @return {Object} The parsed object. 47 | * @api public 48 | */ 49 | 50 | exports.parse = str => utils.isString(str) ? JSON.parse(str) : void 0; 51 | 52 | /** 53 | * Use property paths (`a.b.c`) get a nested value from an object. 54 | * 55 | * ```js 56 | * <%= get({a: {b: 'c'}}, 'a.b') %> 57 | * //=> 'c' 58 | * ``` 59 | * @param {Object} `object` 60 | * @param {String} `path` Dot notation for the property to get. 61 | * @return {String} 62 | * @api public 63 | */ 64 | 65 | exports.get = (obj, prop, options) => get(obj, prop, options); 66 | 67 | /** 68 | * Returns an array of keys from the given `object`. 69 | * 70 | * ```js 71 | * <%= keys({a: 'b', c: 'd'}) %> 72 | * //=> '["a", "c"]' 73 | * ``` 74 | * @param {Object} `object` 75 | * @return {Array} Keys from `object` 76 | * @api public 77 | */ 78 | 79 | exports.keys = obj => Object.keys(obj); 80 | 81 | /** 82 | * Return true if the given `value` is an object, and 83 | * not `null` or an array. 84 | * 85 | * ```js 86 | * <%= isObject(['a', 'b', 'c']) %> 87 | * //=> 'false' 88 | * 89 | * <%= isObject({a: 'b'}) %> 90 | * //=> 'true' 91 | * ``` 92 | * @param {Object} `value` The value to check. 93 | * @return {Boolean} 94 | * @api public 95 | */ 96 | 97 | exports.isObject = utils.isObject; 98 | 99 | /** 100 | * Return true if the given `value` is a plain object. 101 | * 102 | * ```js 103 | * <%= isPlainObject(['a', 'b', 'c']) %> 104 | * //=> 'false' 105 | * 106 | * <%= isPlainObject({a: 'b'}) %> 107 | * //=> 'true' 108 | * 109 | * <%= isPlainObject(/foo/g) %> 110 | * //=> 'false' 111 | * ``` 112 | * @param {Object} `value` The value to check. 113 | * @return {Boolean} 114 | * @api public 115 | */ 116 | 117 | exports.isPlainObject = val => utils.isPlainObject(val); 118 | 119 | /** 120 | * Return true if `key` is an own, enumerable property 121 | * of the given `obj`. 122 | * 123 | * @param {Object} `object` 124 | * @param {String} `key` 125 | * @return {Boolean} 126 | * @api public 127 | */ 128 | 129 | exports.hasOwn = (obj, key) => hasOwn.call(obj, key); 130 | 131 | /** 132 | * Return a copy of `object` exclusing the given `keys`. 133 | * 134 | * ```js 135 | * <%= omit({a: 'a', b: 'b', c: 'c'}, ['a', 'c']) %> 136 | * //=> '{b: "b"}' 137 | * ``` 138 | * @param {Object} `object` Object with keys to omit. 139 | * @param {String} `keys` Keys to omit. 140 | * @return {Boolean} 141 | * @api public 142 | */ 143 | 144 | exports.omit = (obj, keys) => utils.omit(obj, keys); 145 | 146 | /** 147 | * Iterate over the own and inherited enumerable properties of an object, 148 | * and return an object with properties that evaluate to true from the 149 | * callback. Exit early by returning `false`. 150 | * 151 | * ```js 152 | * const context = { values: { a: 'b', c: 'd' } }; 153 | * const str = '<% forIn(values, function(val, key) { %><%= val %><% }) %>'; 154 | * const fn = _.template(str, { imports: helpers }); 155 | * assert.equal(fn(context), 'bd'); 156 | * ``` 157 | * @param {Object} `object` Object with keys to omit. 158 | * @param {String} `keys` Keys to omit. 159 | * @return {Boolean} 160 | * @api public 161 | */ 162 | 163 | exports.forIn = (obj, fn, context) => { 164 | for (let key in obj) { 165 | if (fn.call(context, obj[key], key, obj) === false) { 166 | break; 167 | } 168 | } 169 | }; 170 | 171 | /** 172 | * Iterate over the own enumerable properties of an object, 173 | * and return an object with properties that evaluate to true 174 | * from the callback. Exit early by returning `false` 175 | * 176 | * ```js 177 | * const context = { values: { a: 'b', c: 'd' } }; 178 | * const str = '<% forOwn(values, function(val, key) { %><%= key %><% }) %>'; 179 | * const fn = _.template(str, { imports: helpers }); 180 | * console.log(fn(context)) //=> 'ac' 181 | * ``` 182 | * @param {Object} `object` Object with keys to omit. 183 | * @param {String} `keys` Keys to omit. 184 | * @return {Boolean} 185 | * @api public 186 | */ 187 | 188 | exports.forOwn = (obj, fn, context) => { 189 | exports.forIn(obj, (val, key) => { 190 | if (hasOwn.call(obj, key)) { 191 | return fn.call(context, obj[key], key, obj); 192 | } 193 | }); 194 | }; 195 | 196 | /** 197 | * Extend `o` with properties of other `objects`. 198 | * 199 | * @param {Object} `o` The target object. Pass an empty object to shallow clone. 200 | * @param {Object} `objects` 201 | * @return {Object} 202 | * @api public 203 | */ 204 | 205 | exports.extend = (obj, ...rest) => { 206 | if (!utils.isObject(obj)) return ''; 207 | let last = rest[rest.length - 1]; 208 | 209 | if (utils.isObject(last) && last.hash) { 210 | rest.pop(); 211 | } 212 | 213 | let len = rest.length; 214 | if (len === 0) { 215 | return obj; 216 | } 217 | 218 | for (let ele of rest) { 219 | if (utils.isObject(ele)) { 220 | for (let key in ele) { 221 | if (exports.hasOwn(ele, key)) { 222 | obj[key] = ele[key]; 223 | } 224 | } 225 | } 226 | } 227 | 228 | return obj; 229 | }; 230 | 231 | /** 232 | * Recursively combine the properties of `o` with the 233 | * properties of other `objects`. 234 | * 235 | * @param {Object} `o` The target object. Pass an empty object to shallow clone. 236 | * @param {Object} `objects` 237 | * @return {Object} 238 | * @api public 239 | */ 240 | 241 | exports.merge = (obj, ...rest) => { 242 | if (!utils.isObject(obj)) return ''; 243 | let last = rest[rest.length - 1]; 244 | 245 | if (utils.isObject(last) && last.hash) { 246 | rest.pop(); 247 | } 248 | 249 | let len = rest.length; 250 | if (len === 0) { 251 | return obj; 252 | } 253 | 254 | for (let ele of rest) { 255 | if (utils.isObject(ele)) { 256 | for (let key in ele) { 257 | if (exports.hasOwn(ele, key)) { 258 | if (utils.isObject(ele[key])) { 259 | obj[key] = exports.merge(obj[key], ele[key]); 260 | } else { 261 | obj[key] = ele[key]; 262 | } 263 | } 264 | } 265 | } 266 | } 267 | 268 | return obj; 269 | }; 270 | -------------------------------------------------------------------------------- /lib/helpers/path.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const relative = require('relative'); 5 | 6 | /** 7 | * Return the dirname for the given `filepath`. Uses 8 | * the node.js [path] module. 9 | * 10 | * ```js 11 | * <%= dirname("a/b/c/d") %> 12 | * //=> 'a/b/c' 13 | * ``` 14 | * @param {String} `filepath` 15 | * @return {String} Returns the directory part of the file path. 16 | * @api public 17 | */ 18 | 19 | exports.dirname = path.dirname; 20 | 21 | /** 22 | * Return the basename for the given `filepath`. Uses 23 | * the node.js [path] module. 24 | * 25 | * ```js 26 | * <%= basename("a/b/c/d.js") %> 27 | * //=> 'd.js' 28 | * ``` 29 | * @param {String} `filepath` 30 | * @return {String} Returns the basename part of the file path. 31 | * @api public 32 | */ 33 | 34 | exports.basename = path.basename; 35 | 36 | /** 37 | * Returns the filename for the given `filepath`, excluding 38 | * extension. Aliased as `stem`. 39 | * 40 | * ```js 41 | * <%= filename("a/b/c/d.js") %> 42 | * //=> 'd' 43 | * ``` 44 | * @param {String} `filepath` 45 | * @return {String} Returns the file name part of the file path. 46 | * @api public 47 | */ 48 | 49 | exports.filename = filepath => { 50 | return path.basename(filepath, path.extname(filepath)); 51 | }; 52 | 53 | /** 54 | * Alias for [filename](#filename). 55 | * 56 | * ```js 57 | * <%= stem("a/b/c/d.js") %> 58 | * //=> 'd' 59 | * ``` 60 | * @param {String} `filepath` 61 | * @return {String} Returns the file name part of the file path. 62 | * @api public 63 | */ 64 | 65 | exports.stem = exports.filename; 66 | 67 | /** 68 | * Return the file extension for the given `filepath`. 69 | * Uses the node.js [path] module. 70 | * 71 | * ```js 72 | * <%= extname("foo.js") %> 73 | * //=> '.js' 74 | * ``` 75 | * @param {String} `filepath` 76 | * @return {String} Returns a file extension 77 | * @api public 78 | */ 79 | 80 | exports.extname = path.extname; 81 | 82 | /** 83 | * Return the file extension for the given `filepath`, 84 | * excluding the `.`. 85 | * 86 | * ```js 87 | * <%= ext("foo.js") %> 88 | * //=> 'js' 89 | * ``` 90 | * @param {String} `filepath` 91 | * @return {String} Returns a file extension without dot. 92 | * @api public 93 | */ 94 | 95 | exports.ext = filepath => path.extname(filepath).replace(/^\./, ''); 96 | 97 | /** 98 | * Resolves the given paths to an absolute path. Uses 99 | * the node.js [path] module. 100 | * 101 | * ```js 102 | * <%= resolve('/foo/bar', './baz') %> 103 | * //=> '/foo/bar/baz' 104 | * ``` 105 | * @param {String} `filepath` 106 | * @return {String} Returns a resolve 107 | * @api public 108 | */ 109 | 110 | exports.resolve = path.resolve; 111 | 112 | /** 113 | * Get the relative path from file `a` to file `b`. 114 | * Typically `a` and `b` would be variables passed 115 | * on the context. Uses the node.js [path] module. 116 | * 117 | * ```js 118 | * <%= relative(a, b) %> 119 | * ``` 120 | * @param {String} `a` The "from" file path. 121 | * @param {String} `b` The "to" file path. 122 | * @return {String} Returns a relative path. 123 | * @api public 124 | */ 125 | 126 | exports.relative = (a, b) => { 127 | if (b === void 0) { 128 | b = a; 129 | 130 | if (typeof process !== 'undefined' && typeof process.cwd === 'function') { 131 | a = process.cwd(); 132 | } else { 133 | a = '.'; 134 | } 135 | } 136 | 137 | a = a || ''; 138 | b = b || ''; 139 | let rel = relative(a, b); 140 | return rel === '.' ? './' : rel; 141 | }; 142 | 143 | /** 144 | * Get specific (joined) segments of a file path by passing a 145 | * range of array indices. 146 | * 147 | * ```js 148 | * <%= segments("a/b/c/d", "2", "3") %> 149 | * //=> 'c/d' 150 | * 151 | * <%= segments("a/b/c/d", "1", "3") %> 152 | * //=> 'b/c/d' 153 | * 154 | * <%= segments("a/b/c/d", "1", "2") %> 155 | * //=> 'b/c' 156 | * ``` 157 | * @param {String} `filepath` The file path to split into segments. 158 | * @return {String} Returns a single, joined file path. 159 | * @api public 160 | */ 161 | 162 | exports.segments = (filepath, a, b) => { 163 | return filepath.split(/[\\/]+/).slice(a, b).join('/'); 164 | }; 165 | 166 | /** 167 | * Join all arguments together and normalize the resulting 168 | * `filepath`. Uses the node.js [path] module. 169 | * 170 | * **Note**: there is also a `join()` array helper, dot notation 171 | * can be used with helpers to differentiate. Example: `<%= path.join() %>`. 172 | * 173 | * 174 | * ```js 175 | * <%= join("a", "b") %> 176 | * //=> 'a/b' 177 | * ``` 178 | * @param {String} `filepaths` List of file paths. 179 | * @return {String} Returns a single, joined file path. 180 | * @api public 181 | */ 182 | 183 | exports.join = path.join; 184 | 185 | /** 186 | * Returns true if a file path is an absolute path. An 187 | * absolute path will always resolve to the same location, 188 | * regardless of the working directory. Uses the node.js 189 | * [path] module. 190 | * 191 | * ```js 192 | * // posix 193 | * <%= isAbsolute('/foo/bar') %> 194 | * //=> 'true' 195 | * <%= isAbsolute('qux/') %> 196 | * //=> 'false' 197 | * <%= isAbsolute('.') %> 198 | * //=> 'false' 199 | * 200 | * // Windows 201 | * <%= isAbsolute('//server') %> 202 | * //=> 'true' 203 | * <%= isAbsolute('C:/foo/..') %> 204 | * //=> 'true' 205 | * <%= isAbsolute('bar\\baz') %> 206 | * //=> 'false' 207 | * <%= isAbsolute('.') %> 208 | * //=> 'false' 209 | * ``` 210 | * @param {String} `filepath` 211 | * @return {String} Returns a resolve 212 | * @api public 213 | */ 214 | 215 | exports.isAbsolute = path.isAbsolute; 216 | 217 | /** 218 | * Returns true if a file path is an absolute path. An 219 | * absolute path will always resolve to the same location, 220 | * regardless of the working directory. Uses the node.js 221 | * [path] module. 222 | * 223 | * ```js 224 | * // posix 225 | * <%= isRelative('/foo/bar') %> 226 | * //=> 'false' 227 | * <%= isRelative('qux/') %> 228 | * //=> 'true' 229 | * <%= isRelative('.') %> 230 | * //=> 'true' 231 | * 232 | * // Windows 233 | * <%= isRelative('//server') %> 234 | * //=> 'false' 235 | * <%= isRelative('C:/foo/..') %> 236 | * //=> 'false' 237 | * <%= isRelative('bar\\baz') %> 238 | * //=> 'true' 239 | * <%= isRelative('.') %> 240 | * //=> 'true' 241 | * ``` 242 | * @param {String} `filepath` 243 | * @return {String} Returns a resolve 244 | * @api public 245 | */ 246 | 247 | exports.isRelative = filepath => !path.isAbsolute(filepath); 248 | -------------------------------------------------------------------------------- /lib/helpers/string.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const wrap = require('word-wrap'); 4 | const slugify = require('helper-slugify'); 5 | const alignCenter = require('center-align'); 6 | const alignRight = require('right-align'); 7 | const titlecase = require('titlecase'); 8 | const { isString } = require('../utils'); 9 | const chopRe = () => /^[-_.\W\s]+|[-_.\W\s]+$/g; 10 | const nonWordRe = () => /[-_.\W\s]+(\w|$)/g; 11 | 12 | /** 13 | * camelCase the characters in `string`. 14 | * 15 | * ```js 16 | * <%= camelcase("foo bar baz") %> 17 | * //=> 'fooBarBaz' 18 | * ``` 19 | * @param {String} `string` The string to camelcase. 20 | * @return {String} 21 | * @api public 22 | */ 23 | 24 | exports.camelcase = input => { 25 | if (!isString(input)) return ''; 26 | if (input.length === 1) { 27 | return input.toLowerCase(); 28 | } 29 | let str = exports.chop(input).toLowerCase(); 30 | return str.replace(nonWordRe(), (_, ch) => ch.toUpperCase()); 31 | }; 32 | 33 | /** 34 | * Center align the characters in a string using 35 | * non-breaking spaces. 36 | * 37 | * ```js 38 | * <%= centerAlign("abc") %> 39 | * ``` 40 | * @param {String} `str` The string to reverse. 41 | * @return {String} Centered string. 42 | * @related rightAlign 43 | * @api public 44 | */ 45 | 46 | exports.centerAlign = str => { 47 | return isString(str) ? alignCenter(str, ' ') : ''; 48 | }; 49 | 50 | /** 51 | * Like trim, but removes both extraneous whitespace and 52 | * non-word characters from the beginning and end of a string. 53 | * 54 | * ```js 55 | * <%= chop("_ABC_") %> 56 | * //=> 'ABC' 57 | * 58 | * <%= chop("-ABC-") %> 59 | * //=> 'ABC' 60 | * 61 | * <%= chop(" ABC ") %> 62 | * //=> 'ABC' 63 | * ``` 64 | * @param {String} `string` The string to chop. 65 | * @return {String} 66 | * @api public 67 | */ 68 | 69 | exports.chop = str => { 70 | if (!isString(str)) return ''; 71 | return str.trim().replace(chopRe(), ''); 72 | }; 73 | 74 | /** 75 | * Count the number of occurrances of a substring 76 | * within a string. 77 | * 78 | * ```js 79 | * <%= count("abcabcabc", "a") %> 80 | * //=> '3' 81 | * ``` 82 | * @param {String} `string` 83 | * @param {String} `substring` 84 | * @return {Number} The occurances of `substring` in `string` 85 | * @api public 86 | */ 87 | 88 | exports.count = (str, sub) => { 89 | if (isString(str)) { 90 | return (sub ? (str.split(sub).length - 1) : '0'); 91 | } 92 | return ''; 93 | }; 94 | 95 | /** 96 | * dot.case the characters in `string`. 97 | * 98 | * ```js 99 | * <%= dotcase("a-b-c d_e") %> 100 | * //=> 'a.b.c.d.e' 101 | * ``` 102 | * @param {String} `string` 103 | * @return {String} 104 | * @api public 105 | */ 106 | 107 | exports.dotcase = str => { 108 | if (!isString(str)) return ''; 109 | if (str.length === 1) return str.toLowerCase(); 110 | return exports.chop(str).replace(nonWordRe(), (_, ch) => '.' + ch); 111 | }; 112 | 113 | /** 114 | * Truncate a string to the specified `length`, and append 115 | * it with an elipsis, `…`. 116 | * 117 | * ```js 118 | * <%= ellipsis("foo bar baz", 7) %> 119 | * //=> 'foo bar…' 120 | * ``` 121 | * @param {String} `str` 122 | * @param {Number} `length` The desired length of the returned string. 123 | * @param {String} `ch` Optionally pass custom characters to append. Default is `…`. 124 | * @return {String} The truncated string. 125 | * @api public 126 | */ 127 | 128 | exports.ellipsis = (str, limit, ch) => { 129 | return isString(str) ? (exports.truncate(str, limit) + (ch || '…')) : ''; 130 | }; 131 | 132 | /** 133 | * Returns true if the value is a string. 134 | * 135 | * ```js 136 | * <%= isString('abc') %> 137 | * //=> 'true' 138 | * 139 | * <%= isString(null) %> 140 | * //=> 'false' 141 | * ``` 142 | * @param {String} `val` 143 | * @return {Boolean} True if the value is a string. 144 | * @api public 145 | */ 146 | 147 | exports.isString = isString; 148 | 149 | /** 150 | * Lowercase the characters in the given `string`. 151 | * 152 | * ```js 153 | * <%= lowercase("ABC") %> 154 | * //=> 'abc' 155 | * ``` 156 | * @param {String} `string` The string to lowercase. 157 | * @return {String} 158 | * @api public 159 | */ 160 | 161 | exports.lowercase = exports.lower = str => { 162 | return isString(str) ? str.toLowerCase() : ''; 163 | }; 164 | 165 | /** 166 | * PascalCase the characters in `string`. 167 | * 168 | * ```js 169 | * <%= pascalcase("foo bar baz") %> 170 | * //=> 'FooBarBaz' 171 | * ``` 172 | * @param {String} `string` 173 | * @return {String} 174 | * @api public 175 | */ 176 | 177 | exports.pascalcase = input => { 178 | if (!isString(input)) return ''; 179 | if (input.length === 1) { return input.toUpperCase(); } 180 | let str = exports.camelcase(input); 181 | return str[0].toUpperCase() + str.slice(1); 182 | }; 183 | 184 | /** 185 | * snake_case the characters in `string`. 186 | * 187 | * ```js 188 | * <%= snakecase("a-b-c d_e") %> 189 | * //=> 'a_b_c_d_e' 190 | * ``` 191 | * @param {String} `string` 192 | * @return {String} 193 | * @api public 194 | */ 195 | 196 | exports.snakecase = str => { 197 | if (!isString(str)) return ''; 198 | if (str.length === 1) return str.toLowerCase(); 199 | return exports.chop(str).replace(nonWordRe(), (_, ch) => '_' + ch); 200 | }; 201 | 202 | /** 203 | * Split `string` by the given `character`. 204 | * 205 | * ```js 206 | * <%= split("a,b,c", ",") %> 207 | * //=> ['a', 'b', 'c'] 208 | * ``` 209 | * @param {String} `string` The string to split. 210 | * @return {String} `character` Default is `,` 211 | * @api public 212 | */ 213 | 214 | exports.split = (str, ch) => { 215 | return isString(str) ? str.split(ch || ',') : ''; 216 | }; 217 | 218 | /** 219 | * Strip `substring` from the given `string`. 220 | * 221 | * ```js 222 | * <%= strip("foo-bar", "foo-") %> 223 | * //=> 'bar' 224 | * ``` 225 | * @param {String|RegExp} `substring` The string or regex pattern of the substring to remove. 226 | * @param {String} `string` The target string. 227 | * @api public 228 | */ 229 | 230 | exports.strip = (val, str) => { 231 | if (!isString(val) && !(val instanceof RegExp)) return ''; 232 | if (!isString(str)) return ''; 233 | return str.split(val).join(''); 234 | }; 235 | 236 | /** 237 | * Strip the indentation from a `string`. 238 | * 239 | * ```js 240 | * <%= stripIndent(" _ABC_") %> 241 | * //=> 'ABC' 242 | * ``` 243 | * @param {String} `string` The string to strip indentation from. 244 | * @return {String} 245 | * @api public 246 | */ 247 | 248 | exports.stripIndent = str => { 249 | if (!isString(str)) return ''; 250 | let matches = str.match(/^[ \t]+(?!\s)/gm); 251 | if (!matches) return str; 252 | 253 | let whitespace = matches.reverse().pop(); 254 | let len = whitespace.length; 255 | if (len) { 256 | let re = new RegExp(`^[ \\t]{${len}}`, 'gm'); 257 | return str.replace(re, ''); 258 | } 259 | return str; 260 | }; 261 | 262 | /** 263 | * Trim extraneous whitespace from the beginning and end 264 | * of a string. 265 | * 266 | * ```js 267 | * <%= trim(" ABC ") %> 268 | * //=> 'ABC' 269 | * ``` 270 | * @param {String} `string` The string to trim. 271 | * @return {String} 272 | * @api public 273 | */ 274 | 275 | exports.trim = str => isString(str) ? str.trim() : ''; 276 | 277 | /** 278 | * dash-case the characters in `string`. This is similar to [slugify], 279 | * but [slugify] makes the string compatible to be used as a URL slug. 280 | * 281 | * ```js 282 | * <%= dashcase("a b.c d_e") %> 283 | * //=> 'a-b-c-d-e' 284 | * ``` 285 | * @param {String} `string` 286 | * @return {String} 287 | * @tags dasherize, dashify, hyphenate, case 288 | * @api public 289 | */ 290 | 291 | exports.dashcase = str => { 292 | if (!isString(str)) return ''; 293 | if (str.length === 1) return str.toLowerCase(); 294 | return exports.chop(str).replace(nonWordRe(), (_, ch) => '-' + ch); 295 | }; 296 | 297 | /** 298 | * path/case the characters in `string`. 299 | * 300 | * ```js 301 | * <%= pathcase("a-b-c d_e") %> 302 | * //=> 'a/b/c/d/e' 303 | * ``` 304 | * @param {String} `string` 305 | * @return {String} 306 | * @api public 307 | */ 308 | 309 | exports.pathcase = str => { 310 | if (!isString(str)) return ''; 311 | if (str.length === 1) { return str.toLowerCase(); } 312 | return exports.chop(str).replace(nonWordRe(), (_, ch) => '/' + ch); 313 | }; 314 | 315 | /** 316 | * Sentence-case the characters in `string`. 317 | * 318 | * ```js 319 | * <%= sentencecase("foo bar baz.") %> 320 | * //=> 'Foo bar baz.' 321 | * ``` 322 | * @param {String} `string` 323 | * @return {String} 324 | * @api public 325 | */ 326 | 327 | exports.sentencecase = str => { 328 | if (!isString(str)) return ''; 329 | if (str.length === 1) { return str.toUpperCase(); } 330 | return str.charAt(0).toUpperCase() + str.slice(1); 331 | }; 332 | 333 | /** 334 | * Replace spaces in a string with hyphens. This 335 | * 336 | * ```js 337 | * <%= hyphenate("a b c") %> 338 | * //=> 'a-b-c' 339 | * ``` 340 | * @param {String} `string` 341 | * @return {String} 342 | * @api public 343 | */ 344 | 345 | exports.hyphenate = str => { 346 | return isString(str) ? exports.chop(str).split(' ').join('-') : ''; 347 | }; 348 | 349 | /** 350 | * TODO: Need to use a proper slugifier for this. 351 | * I don't want to use `node-slug`, it's way too 352 | * huge for this. 353 | * 354 | * ```js 355 | * <%= slugify('This is a post title.') %> 356 | * //=> 'this-is-a-post-title' 357 | * ``` 358 | * @param {String} `string` The string to slugify. 359 | * @return {String} Slug. 360 | * @api draft 361 | */ 362 | 363 | exports.slugify = slugify; 364 | 365 | /** 366 | * Reverse the characters in a string. 367 | * 368 | * ```js 369 | * <%= reverse("abc") %> 370 | * //=> 'cba' 371 | * ``` 372 | * @param {String} `str` The string to reverse. 373 | * @return {String} 374 | * @api public 375 | */ 376 | 377 | exports.reverse = str => { 378 | return isString(str) ? str.split('').reverse().join('') : ''; 379 | }; 380 | 381 | /** 382 | * Right align the characters in a string using 383 | * non-breaking spaces. 384 | * 385 | * ```js 386 | * <%= rightAlign(str) %> 387 | * ``` 388 | * @param {String} `str` The string to reverse. 389 | * @return {String} Right-aligned string. 390 | * @related centerAlign 391 | * @api public 392 | */ 393 | 394 | exports.rightAlign = str => { 395 | return isString(str) ? alignRight(str) : ''; 396 | }; 397 | 398 | /** 399 | * Replace occurrences of `a` with `b`. 400 | * 401 | * ```js 402 | * <%= replace("abcabc", /a/, "z") %> 403 | * //=> 'zbczbc' 404 | * ``` 405 | * @param {String} `str` 406 | * @param {String|RegExp} `a` Can be a string or regexp. 407 | * @param {String} `b` 408 | * @return {String} 409 | * @api public 410 | */ 411 | 412 | exports.replace = (str, a, b) => { 413 | if (!isString(str)) return ''; 414 | if (!a) { return str; } 415 | if (!b) { b = ''; } 416 | return str.split(a).join(b); 417 | }; 418 | 419 | /** 420 | * Truncate a string by removing all HTML tags and limiting the result 421 | * to the specified `length`. 422 | * 423 | * ```js 424 | * <%= titlecase("big deal") %> 425 | * //=> 'foo bar' 426 | * ``` 427 | * @param {String} `str` 428 | * @param {Number} `length` The desired length of the returned string. 429 | * @return {String} The truncated string. 430 | * @api public 431 | */ 432 | 433 | exports.titlecase = exports.titleize = (str, length) => { 434 | return isString(str) ? titlecase(str) : ''; 435 | }; 436 | 437 | /** 438 | * Truncate a string by removing all HTML tags and limiting the result 439 | * to the specified `length`. 440 | * 441 | * ```js 442 | * <%= truncate("foo bar baz", 7) %> 443 | * //=> 'foo bar' 444 | * ``` 445 | * @param {String} `str` 446 | * @param {Number} `length` The desired length of the returned string. 447 | * @return {String} The truncated string. 448 | * @api public 449 | */ 450 | 451 | exports.truncate = (str, length) => { 452 | return isString(str) ? str.substr(0, length) : ''; 453 | }; 454 | 455 | /** 456 | * Uppercase the characters in a string. 457 | * 458 | * ```js 459 | * <%= uppercase("abc") %> 460 | * //=> 'ABC' 461 | * ``` 462 | * @param {String} `string` The string to uppercase. 463 | * @return {String} 464 | * @api public 465 | */ 466 | 467 | exports.uppercase = exports.upper = str => { 468 | return isString(str) ? str.toUpperCase() : ''; 469 | }; 470 | 471 | /** 472 | * Wrap words to a specified width using [word-wrap]. 473 | * 474 | * ```js 475 | * <%= wordwrap("a b c d e f", {width: 5, newline: '
'}) %> 476 | * //=> ' a b c
d e f' 477 | * ``` 478 | * @param {String} `string` The string with words to wrap. 479 | * @param {Options} `object` Options to pass to [word-wrap] 480 | * @return {String} Formatted string. 481 | * @api public 482 | */ 483 | 484 | exports.wordwrap = (str, options) => { 485 | return isString(str) ? wrap(str, options) : ''; 486 | }; 487 | -------------------------------------------------------------------------------- /lib/iterator.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const typeOf = require('kind-of'); 4 | const prop = name => obj => obj[name]; 5 | const noop = val => val; 6 | 7 | module.exports = (target, thisArg) => { 8 | const invoke = (val, i, arr) => target.call(thisArg, val, i, arr); 9 | switch (typeOf(target)) { 10 | case 'undefined': 11 | case 'null': 12 | return noop; 13 | case 'function': 14 | return (thisArg === void 0) ? target : invoke; 15 | case 'object': 16 | return val => isMatch(val, target); 17 | case 'regexp': 18 | return str => target.test(str); 19 | case 'string': 20 | case 'number': 21 | default: { 22 | return prop(target); 23 | } 24 | } 25 | }; 26 | 27 | function containsMatch(array, value) { 28 | for (let i = 0; i < array.length; i++) { 29 | if (isMatch(array[i], value)) { 30 | return true; 31 | } 32 | } 33 | return false; 34 | } 35 | 36 | function matchArray(array, value) { 37 | for (let i = 0; i < array.length; i++) { 38 | if (!containsMatch(array, value[i])) { 39 | return false; 40 | } 41 | } 42 | return true; 43 | } 44 | 45 | function matchObject(obj, value) { 46 | for (let key in value) { 47 | if (value.hasOwnProperty(key)) { 48 | if (isMatch(obj[key], value[key]) === false) { 49 | return false; 50 | } 51 | } 52 | } 53 | return true; 54 | } 55 | 56 | function isMatch(val, value) { 57 | if (typeOf(val) === 'object') { 58 | if (Array.isArray(val) && Array.isArray(value)) { 59 | return matchArray(val, value); 60 | } 61 | return matchObject(val, value); 62 | } 63 | return val === value; 64 | } 65 | -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const typeOf = require('kind-of'); 4 | 5 | exports.isEmpty = val => { 6 | if (typeof val === 'function') { 7 | return false; 8 | } 9 | if (exports.isObject(val)) { 10 | val = Object.keys(val); 11 | } 12 | if (Array.isArray(val)) { 13 | return val.length === 0; 14 | } 15 | if (typeof val === 'undefined') { 16 | return true; 17 | } 18 | if (val === 0) { 19 | return false; 20 | } 21 | if (val == null) { 22 | return true; 23 | } 24 | }; 25 | 26 | exports.omit = (obj, keys = []) => { 27 | let res = {}; 28 | 29 | for (let key of Object.keys(obj)) { 30 | if (!keys.includes(key)) { 31 | res[key] = obj[key]; 32 | } 33 | } 34 | 35 | return res; 36 | }; 37 | 38 | /** 39 | * Get the "title" from a markdown link 40 | */ 41 | 42 | exports.getTitle = str => { 43 | if (/^\[[^\]]+\]\(/.test(str)) { 44 | let m = /^\[([^\]]+)\]/.exec(str); 45 | if (m) return m[1]; 46 | } 47 | return str; 48 | }; 49 | 50 | exports.isString = str => str !== '' && typeof str === 'string'; 51 | 52 | exports.isNumber = num => { 53 | if (typeof num === 'number') { 54 | return num - num === 0; 55 | } 56 | if (typeof num === 'string' && num.trim() !== '') { 57 | return Number.isFinite ? Number.isFinite(+num) : isFinite(+num); 58 | } 59 | return false; 60 | }; 61 | 62 | exports.isObject = val => typeOf(val) === 'object'; 63 | 64 | exports.isPlainObject = val => { 65 | return exports.isObject(val) && val.constructor === Object; 66 | }; 67 | 68 | /** 69 | * Returns true if the given `val` is an object. 70 | * 71 | * ```js 72 | * utils.isObject('foo'); 73 | * //=> false 74 | * 75 | * utils.isObject({}); 76 | * //=> true 77 | * ``` 78 | * @param {any} `val` 79 | * @return {Boolean} 80 | * @api public 81 | */ 82 | 83 | exports.isObject = val => { 84 | return typeOf(val) === 'object'; 85 | }; 86 | 87 | /** 88 | * Stringify HTML tag attributes from the given `object`. 89 | * 90 | * @param {Object} `object` Object of attributes as key-value pairs. 91 | * @return {String} Stringified attributes, e.g. `foo="bar"` 92 | * @api public 93 | */ 94 | 95 | exports.toAttributes = obj => { 96 | return Object.keys(obj).map(key => `${key}="${obj[key]}"`).join(' '); 97 | }; 98 | 99 | exports.toCodeBlock = (str, lang) => { 100 | if (typeof str !== 'string') { 101 | throw new TypeError('expected a string.'); 102 | } 103 | let code = ''; 104 | code += '```' + (typeof lang === 'string' ? lang : ''); 105 | code += '\n'; 106 | code += str; 107 | code += '\n'; 108 | code += '```'; 109 | return code; 110 | }; 111 | 112 | /** 113 | * Generate a random number 114 | * 115 | * @param {Number} `min` 116 | * @param {Number} `max` 117 | * @return {Number} 118 | */ 119 | 120 | exports.random = (min, max) => { 121 | return min + Math.floor(Math.random() * (max - min + 1)); 122 | }; 123 | 124 | exports.flatten = arr => { 125 | const flat = (a, res) => { 126 | let len = a.length; 127 | let i = -1; 128 | while (len--) { 129 | let cur = a[++i]; 130 | if (Array.isArray(cur)) { 131 | flat(cur, res); 132 | } else { 133 | res.push(cur); 134 | } 135 | } 136 | return res; 137 | }; 138 | return flat(arr, []); 139 | }; 140 | 141 | exports.union = (...args) => { 142 | return [...new Set(exports.flatten(args))]; 143 | }; 144 | 145 | /** 146 | * Expose `exports` modules 147 | */ 148 | 149 | module.exports = exports; 150 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "template-helpers", 3 | "description": "Generic JavaScript helpers that can be used with any template engine. Handlebars, Lo-Dash, Underscore, or any engine that supports helper functions.", 4 | "version": "1.0.1", 5 | "homepage": "https://github.com/jonschlinkert/template-helpers", 6 | "author": "Jon Schlinkert (https://github.com/jonschlinkert)", 7 | "repository": "jonschlinkert/template-helpers", 8 | "bugs": { 9 | "url": "https://github.com/jonschlinkert/template-helpers/issues" 10 | }, 11 | "license": "MIT", 12 | "files": [ 13 | "index.js", 14 | "lib" 15 | ], 16 | "main": "index.js", 17 | "engines": { 18 | "node": ">=6" 19 | }, 20 | "scripts": { 21 | "test": "mocha" 22 | }, 23 | "dependencies": { 24 | "any": "^1.0.0", 25 | "center-align": "^1.0.1", 26 | "get-value": "^3.0.1", 27 | "helper-slugify": "^0.2.0", 28 | "kind-of": "^6.0.2", 29 | "relative": "^3.0.2", 30 | "right-align": "^0.1.3", 31 | "titlecase": "^1.1.2", 32 | "word-wrap": "^1.2.3" 33 | }, 34 | "devDependencies": { 35 | "gulp-format-md": "^2.0.0", 36 | "helper-coverage": "^0.1.3", 37 | "lodash.template": "^4.4.0", 38 | "markdown-link": "^0.1.1", 39 | "mocha": "^5.2.0", 40 | "through2": "^3.0.0", 41 | "verb-generate-readme": "^0.8.0" 42 | }, 43 | "keywords": [ 44 | "compile", 45 | "engine", 46 | "helper", 47 | "helpers", 48 | "imports", 49 | "lodash", 50 | "process", 51 | "render", 52 | "template", 53 | "underscore" 54 | ], 55 | "verb": { 56 | "toc": true, 57 | "layout": "default", 58 | "tasks": [ 59 | "readme" 60 | ], 61 | "plugins": [ 62 | "gulp-format-md" 63 | ], 64 | "helpers": [ 65 | "helper-coverage" 66 | ], 67 | "related": { 68 | "description": "You might also be interested in the following projects (also visit the [github.com/helpers](https://github.com/helpers), where you can find 60+ additional standalone helpers!):", 69 | "list": [ 70 | "assemble", 71 | "handlebars-helpers", 72 | "templates" 73 | ] 74 | }, 75 | "lint": { 76 | "reflinks": true 77 | }, 78 | "reflinks": [ 79 | "any", 80 | "slugify", 81 | "verb", 82 | "word-wrap" 83 | ] 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /recipes/tips-and-tricks.md: -------------------------------------------------------------------------------- 1 | # Tips and tricks 2 | 3 | > Some things you might not know you can do with helpers ;) 4 | 5 | Some of these only work with one template engine, others can be used anywhere. Please make sure to read any notes and pay special attention to the **Engines** section of each tip. 6 | 7 | 8 | ## Define a variable 9 | 10 | Add a variable to the context, but only for a single template. 11 | 12 | **Lo-Dash Example** 13 | 14 | ```js 15 | // define the variable 16 | <% foo = "bar" %> 17 | 18 | // use it 19 | <%= foo %> 20 | //=> "bar" 21 | ``` 22 | 23 | Works with non-string variables too, of course. 24 | 25 | **Engines** 26 | 27 | Should work with: 28 | 29 | - [Lo-dash] 30 | 31 | 32 | ## Vanilla node.js libs as helpers 33 | 34 | Here is a trick for using any plain old node.js library as a helper on-the-fly. To do so, we'll create a special helper that isn't in the library: 35 | 36 | **The `require` helper** 37 | 38 | ```js 39 | var helpers = { 40 | require: function() { 41 | // take whatever args are passed 42 | return require.apply(require, arguments); 43 | }; 44 | }; 45 | ``` 46 | 47 | **Lo-Dash Example** 48 | 49 | Now you can use the `require` helper in your templates, allowing you to use any other node.js library without registering it as a helper first. 50 | 51 | ```js 52 | // globbing is already included in this lib, but this is a good example 53 | _.template('<%= require("glob").sync("*.js") %>', {imports: helpers}); 54 | ``` 55 | 56 | **Engines** 57 | 58 | Should work with: 59 | 60 | - [Lo-Dash] 61 | 62 | 63 | 64 | ## Chain helpers 65 | 66 | Each helper should only have a single responsibility. Chaining allows us to stick to that rule and keep things simple. 67 | 68 | **Lo-Dash Example** 69 | 70 | 1. Read a file from the file system (that happens to be markdown) 71 | 2. Convert it to HTML 72 | 73 | ```js 74 | <%= markdown(read("README.md")) %> 75 | ``` 76 | 77 | **Engines** 78 | 79 | Should work with: 80 | 81 | - Any 82 | 83 | 84 | *** 85 | 86 | # Contributing 87 | 88 | If you contribute to this page, please follow the conventions. Thanks! 89 | -------------------------------------------------------------------------------- /test/array.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * template-helpers 3 | * 4 | * Copyright (c) 2015, Jon Schlinkert. 5 | * Licensed under the MIT License. 6 | */ 7 | 8 | 'use strict'; 9 | 10 | require('mocha'); 11 | var assert = require('assert'); 12 | var helpers = require('..')('array'); 13 | var template = require('lodash.template'); 14 | 15 | var context = {arr: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']}; 16 | var imports = {imports: helpers}; 17 | 18 | describe('isArray', function() { 19 | it('should return true if the value is an array.', function() { 20 | assert.equal(template('<%= isArray("foo") %>', imports)(), 'false'); 21 | assert.equal(template('<%= isArray(["foo"]) %>', imports)(), 'true'); 22 | }); 23 | }); 24 | 25 | describe('arrayify', function() { 26 | it('should coerce a value to an array.', function() { 27 | assert.equal(template('<%= isArray(arrayify("foo")) %>', imports)(), 'true'); 28 | assert.equal(template('<%= isArray(arrayify(["foo"])) %>', imports)(), 'true'); 29 | }); 30 | }); 31 | 32 | describe('first', function() { 33 | it('should return an empty string when undefined.', function() { 34 | assert.equal(template('<%= first() %>', imports)(), ''); 35 | }); 36 | 37 | it('should return the first item in an array.', function() { 38 | var fn = template('<%= first(foo) %>', imports); 39 | assert.equal(fn({foo: ['a', 'b', 'c']}), 'a'); 40 | }); 41 | 42 | it('should return an array with the first two items in an array.', function() { 43 | var fn = template('<%= first(foo, 2) %>', imports); 44 | assert.deepEqual(fn({foo: ['a', 'b', 'c']}), ['a', 'b'].toString()); 45 | }); 46 | }); 47 | 48 | describe('last', function() { 49 | it('should return an empty string when undefined.', function() { 50 | assert.equal(template('<%= last() %>', imports)(), ''); 51 | }); 52 | 53 | it('should return the last item in an array.', function() { 54 | assert.equal(template('<%= last(arr) %>', imports)(context), 'h'); 55 | }); 56 | it('should return an array with the last two items in an array.', function() { 57 | assert.deepEqual(template('<%= last(arr, 2) %>', imports)(context), ['g', 'h'].toString()); 58 | }); 59 | }); 60 | 61 | describe('before', function() { 62 | it('should return an empty string when undefined.', function() { 63 | assert.equal(template('<%= before() %>', imports)(), ''); 64 | }); 65 | it('should return all of the items in an array before the given index.', function() { 66 | var fn = template('<%= before(arr, 5) %>', imports); 67 | assert.deepEqual(fn(context), ['a', 'b', 'c'].toString()); 68 | }); 69 | }); 70 | 71 | describe('after', function() { 72 | it('should return an empty string when undefined.', function() { 73 | assert.equal(template('<%= after() %>', imports)(), ''); 74 | }); 75 | 76 | it('should return all of the items in an array after the given index.', function() { 77 | var fn = template('<%= after(arr, 5) %>', imports); 78 | assert.deepEqual(fn(context), ['f', 'g', 'h'].toString()); 79 | }); 80 | }); 81 | 82 | describe('join', function() { 83 | it('should return an empty string when undefined.', function() { 84 | assert.equal(template('<%= join() %>', imports)(), ''); 85 | }); 86 | 87 | it('should return all items in an array joined by the default separator.', function() { 88 | var fn = template('<%= join(arr) %>', imports); 89 | assert.equal(fn(context), 'a, b, c, d, e, f, g, h'); 90 | }); 91 | 92 | it('should return all items in an array joined by the given separator.', function() { 93 | var fn = template('<%= join(arr, " | ") %>', imports); 94 | assert.equal(fn(context), 'a | b | c | d | e | f | g | h'); 95 | }); 96 | }); 97 | 98 | describe('each', function() { 99 | it('should return an empty string when undefined.', function() { 100 | assert.equal(template('<%= each() %>', imports)(), ''); 101 | }); 102 | 103 | it('should iterate over items in the array and return new values.', function() { 104 | var o = {}; 105 | o.double = function(str) { 106 | return str + str; 107 | }; 108 | var fn = template('<%= each(["a","b","c"], double) %>', imports); 109 | assert.equal(fn(o), 'aabbcc'); 110 | }); 111 | 112 | it('should expose the given context to each item', function() { 113 | var o = {ctx: {sep: ' '}}; 114 | o.double = function(str) { 115 | return str + this.sep + str; 116 | }; 117 | var fn = template('<%= each(["a","b","c"], double, ctx) %>', imports); 118 | assert.equal(fn(o), 'a ab bc c'); 119 | }); 120 | }); 121 | 122 | describe('map', function() { 123 | it('should return an empty string when undefined.', function() { 124 | assert.equal(template('<%= map() %>', imports)(), ''); 125 | }); 126 | 127 | it('should map the items in the array and return new values.', function() { 128 | var o = {}; 129 | o.double = function(str) { 130 | return str + str; 131 | }; 132 | var fn = template('<%= map(["a","b","c"], double) %>', imports); 133 | assert.equal(fn(o), 'aa,bb,cc'); 134 | }); 135 | }); 136 | 137 | describe('sort', function() { 138 | it('should return an empty string when undefined.', function() { 139 | assert.equal(template('<%= sort() %>', imports)(), ''); 140 | }); 141 | 142 | it('should sort the items in an array.', function() { 143 | var fn = template('<%= sort(["b", "c", "a"]) %>', imports); 144 | assert.equal(fn(context), 'a,b,c'); 145 | }); 146 | 147 | it('should take a compare function.', function() { 148 | var o = {}; 149 | o.compare = function(a, b) { 150 | return b.localeCompare(a); 151 | }; 152 | var fn = template('<%= sort(["b", "c", "a"], compare) %>', imports); 153 | assert.equal(fn(o), 'c,b,a'); 154 | }); 155 | 156 | it('should sort based on object key:', function() { 157 | var fn = template('<%= JSON.stringify(sort([{a: "zzz"}, {a: "aaa"}], "a")) %>', imports); 158 | assert.equal(fn(), '[{"a":"aaa"},{"a":"zzz"}]'); 159 | }); 160 | }); 161 | 162 | describe('length', function() { 163 | it('should return an empty string when undefined.', function() { 164 | assert.equal(template('<%= length() %>', imports)(), ''); 165 | }); 166 | 167 | it('should return zero when the value is not an array.', function() { 168 | var fn = template('<%= length("foo") %>', imports); 169 | assert.equal(fn(context), '0'); 170 | }); 171 | 172 | it('should return the length of an array.', function() { 173 | var fn = template('<%= length(["b", "c", "a"]) %>', imports); 174 | assert.equal(fn(context), '3'); 175 | }); 176 | }); 177 | 178 | describe('compact', function() { 179 | it('should return an empty string when undefined.', function() { 180 | assert.equal(template('<%= compact() %>', imports)(), ''); 181 | }); 182 | 183 | it('should remove falsey values from an array.', function() { 184 | var fn = template('<%= compact([null, "a", undefined, 0, false, "b", "c", ""]) %>', imports); 185 | assert.equal(fn(context), 'a,b,c'); 186 | }); 187 | }); 188 | 189 | describe('difference', function() { 190 | it('should return an empty string when undefined.', function() { 191 | assert.equal(template('<%= difference() %>', imports)(), ''); 192 | }); 193 | 194 | it('should return the difference from multiple arrays', function() { 195 | var o = {}; 196 | o.a = ['a', 'b', 'c', 'd']; 197 | o.b = ['b', 'c']; 198 | o.c = ['x', 'y']; 199 | assert.equal(template('<%= difference(a, b, c) %>', imports)(o), 'a,d'); 200 | assert.equal(template('<%= difference(a, b) %>', imports)(o), 'a,d'); 201 | assert.equal(template('<%= difference(a) %>', imports)(o), 'a,b,c,d'); 202 | }); 203 | }); 204 | 205 | describe('unique', function() { 206 | it('should return an empty string when undefined.', function() { 207 | assert.equal(template('<%= unique() %>', imports)(), ''); 208 | }); 209 | it('should unique items from multiple arrays:', function() { 210 | var fn = template('<%= unique(["a", "b", "c", "c"]) %>', imports); 211 | assert.equal(fn(context), 'a,b,c'); 212 | }); 213 | }); 214 | 215 | describe('union', function() { 216 | it('should return an empty string when undefined.', function() { 217 | assert.equal(template('<%= union() %>', imports)(), ''); 218 | }); 219 | 220 | it('should union items from multiple arrays:', function() { 221 | var fn = template('<%= union(["a", "c"], ["b", "b"]) %>', imports); 222 | assert.equal(fn(context), 'a,c,b'); 223 | }); 224 | 225 | it('should union items from multiple arrays:', function() { 226 | var fn = template('<%= union(["a"], ["b"]) %>', imports); 227 | assert.equal(fn(context), 'a,b'); 228 | }); 229 | }); 230 | -------------------------------------------------------------------------------- /test/code.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * template-helpers 3 | * 4 | * Copyright (c) 2015, Jon Schlinkert. 5 | * Licensed under the MIT License. 6 | */ 7 | 8 | 'use strict'; 9 | 10 | var helpers = require('..')('code'); 11 | var assert = require('assert'); 12 | var template = require('lodash.template'); 13 | var imports = {imports: helpers}; 14 | 15 | describe('code', function() { 16 | var orig = process.cwd(); 17 | before(function() { 18 | process.chdir(__dirname + '/fixtures'); 19 | }); 20 | after(function() { 21 | process.chdir(orig); 22 | }); 23 | 24 | describe('jsfiddle', function() { 25 | it('should return an empty string with no args.', function() { 26 | assert.equal(template('<%= jsfiddle() %>', imports)(), ''); 27 | }); 28 | 29 | it('should return create a jsfiddle link.', function() { 30 | assert.equal(template('<%= jsfiddle({id: "c0th6weq", tabs: true}) %>', imports)(), ''); 31 | }); 32 | }); 33 | describe('embed', function() { 34 | it('should return create a code example from the given file.', function() { 35 | assert.equal(template('<%= embed("a.js") %>', imports)({}), [ 36 | '```js', 37 | 'function foo(a, b, c) {', 38 | ' return a + b + c;', 39 | '}', 40 | '```\n' 41 | ].join('\n')); 42 | }); 43 | 44 | it('should use the extension specified as a second argument.', function() { 45 | assert.equal(template('<%= embed("a.js", "javascript") %>', imports)(), [ 46 | '```javascript', 47 | 'function foo(a, b, c) {', 48 | ' return a + b + c;', 49 | '}', 50 | '```\n' 51 | ].join('\n')); 52 | }); 53 | 54 | it('should escape backticks in markdown.', function() { 55 | assert.equal(template('<%= embed("a.md", "md") %>', imports)(), [ 56 | '```md', 57 | '```', 58 | 'foo', 59 | '```', 60 | '```\n' 61 | ].join('\n')); 62 | }); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /test/collection.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * template-helpers 3 | * 4 | * Copyright (c) 2015, Jon Schlinkert. 5 | * Licensed under the MIT License. 6 | */ 7 | 8 | 'use strict'; 9 | 10 | var assert = require('assert'); 11 | var helpers = require('..')(['collection', 'object']); 12 | var template = require('lodash.template'); 13 | 14 | var imports = {imports: helpers}; 15 | 16 | describe('collections', function() { 17 | describe('any', function() { 18 | it('should be false undefined.', function() { 19 | assert.equal(template('<%= any() %>', imports)(), 'false'); 20 | }); 21 | it('should return if a value exists in the given string.', function() { 22 | assert.equal(template('<%= any("a-b-c", "a") %>', imports)(context), 'true'); 23 | assert.equal(template('<%= any("a-b-c", "d") %>', imports)(context), 'false'); 24 | }); 25 | it('should return if a value exists in the given object.', function() { 26 | assert.equal(template('<%= any({a: "b", c: "d"}, "a") %>', imports)(context), 'true'); 27 | assert.equal(template('<%= any([{a: "b", c: "d"}], {a: "b"}) %>', imports)(context), 'true'); 28 | }); 29 | it('should return if a value exists in the given array.', function() { 30 | assert.equal(template('<%= any("a-b-c", "d") %>', imports)(context), 'false'); 31 | }); 32 | }); 33 | 34 | describe('filter', function() { 35 | it('should return an empty string when undefined.', function() { 36 | assert.equal(template('<%= filter() %>', imports)(), ''); 37 | }); 38 | it('should return a string value if it exists in the given array', function() { 39 | assert.equal(template('<%= filter(["a", "b", "c"], "a") %>', imports)(context), 'a'); 40 | }); 41 | it('should return an empty string if a string value does not exists', function() { 42 | assert.equal(template('<%= filter(["a", "b", "c"], "d") %>', imports)(context), ''); 43 | }); 44 | it('should filter out values that match the given regex', function() { 45 | assert.equal(template('<%= filter(["a", "b", "c", "d", "z"], /[a-c]/) %>', imports)(context), 'a,b,c'); 46 | }); 47 | it('should filter out values using a function', function() { 48 | assert.equal(template('<%= filter(["a", "b", "c", "d", "z"], function(key) { return /(a|z)/.test(key) }) %>', imports)(context), 'a,z'); 49 | }); 50 | it('should return true if the value equals given string', function() { 51 | assert.equal(template('<%= filter("a", "a") %>', imports)(context), 'true'); 52 | }); 53 | it('should return false if the value does not equal the given string', function() { 54 | assert.equal(template('<%= filter("a-b-c", "d") %>', imports)(context), 'false'); 55 | }); 56 | it('should filter out properties that match the given value', function() { 57 | assert.equal(template('<%= stringify(filter({a: "b", c: "d"}, "a")) %>', imports)(context), '{"a":"b"}'); 58 | }); 59 | it('should filter out objects that match the given value', function() { 60 | assert.equal(template('<%= stringify(filter([{a: "b", c: "d"}], {a: "b"})) %>', imports)(context), '[{"a":"b","c":"d"}]'); 61 | }); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /test/conditional.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * template-helpers 3 | * 4 | * Copyright (c) 2015, Jon Schlinkert. 5 | * Licensed under the MIT License. 6 | */ 7 | 8 | 'use strict'; 9 | 10 | var assert = require('assert'); 11 | var helpers = require('..')('conditional'); 12 | var template = require('lodash.template'); 13 | 14 | var imports = {imports: helpers}; 15 | var context = { 16 | fn: function(a, b) { 17 | return this && this.foo === 'abc'; 18 | }, 19 | thisArg: {foo: 'abc', bar: 'xyz'} 20 | }; 21 | 22 | describe('conditional', function() { 23 | describe('if', function() { 24 | it('should return an empty string when the first arg is not a function.', function() { 25 | assert.equal(template('<%= _if("foo", "bar", thisArg) %>', imports)(context), ''); 26 | }); 27 | it('should return the first value when `fn` returns true.', function() { 28 | assert.equal(template('<%= _if(fn, "foo", "bar", thisArg) %>', imports)(context), 'foo'); 29 | }); 30 | it('should return the second value when `fn` returns false.', function() { 31 | assert.equal(template('<%= _if(fn, "foo", "bar") %>', imports)(context), 'bar'); 32 | }); 33 | }); 34 | 35 | describe('compare', function() { 36 | it('should be true when values are equal', function() { 37 | assert.equal(template('<%= compare("foo", "===", "foo") %>', imports)(context), 'true'); 38 | assert.equal(template('<%= compare("foo", "==", "foo") %>', imports)(context), 'true'); 39 | }); 40 | 41 | it('should be false when values are not equal', function() { 42 | assert.equal(template('<%= compare("foo", "===", "bar") %>', imports)(context), 'false'); 43 | assert.equal(template('<%= compare("foo", "==", "bar") %>', imports)(context), 'false'); 44 | }); 45 | }); 46 | 47 | describe('is / eq', function() { 48 | it('should be true when values are equal', function() { 49 | assert.equal(template('<%= is("foo", "foo") %>', imports)(context), 'true'); 50 | assert.equal(template('<%= eq("foo", "foo") %>', imports)(context), 'true'); 51 | }); 52 | 53 | it('should be false when values are not equal', function() { 54 | assert.equal(template('<%= is("foo", "bar") %>', imports)(context), 'false'); 55 | assert.equal(template('<%= eq("foo", "bar") %>', imports)(context), 'false'); 56 | }); 57 | }); 58 | 59 | describe('isnt / notEq', function() { 60 | it('should be false when values are equal', function() { 61 | assert.equal(template('<%= isnt("foo", "foo") %>', imports)(context), 'false'); 62 | assert.equal(template('<%= notEq("foo", "foo") %>', imports)(context), 'false'); 63 | }); 64 | 65 | it('should be true when values are not equal', function() { 66 | assert.equal(template('<%= isnt("foo", "bar") %>', imports)(context), 'true'); 67 | assert.equal(template('<%= notEq("foo", "bar") %>', imports)(context), 'true'); 68 | }); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /test/fixtures/README.md: -------------------------------------------------------------------------------- 1 | # template-helpers [![NPM version](https://badge.fury.io/js/template-helpers.svg)](http://badge.fury.io/js/template-helpers) [![Build Status](https://travis-ci.org/jonschlinkert/template-helpers.svg)](https://travis-ci.org/jonschlinkert/template-helpers) 2 | 3 | > Generic JavaScript helpers that can be used with any template engine. Handlebars, Lo-Dash, Underscore, or any engine that supports helper functions. 4 | 5 | ## Install with [npm](npmjs.org) 6 | 7 | ```bash 8 | npm i template-helpers --save 9 | ``` 10 | 11 | ## TOC 12 | 13 | 14 | 15 | - [Usage](#usage) 16 | * [Use with any template engine](#use-with-any-template-engine) 17 | * [Namespacing](#namespacing) 18 | - [Helpers](#helpers) 19 | - [Docs](#docs) 20 | - [Run tests](#run-tests) 21 | - [Related](#related) 22 | 23 | 24 | 25 | ## Usage 26 | 27 | To get all helpers grouped by collection: 28 | 29 | ```js 30 | var helpers = require('template-helpers'); 31 | 32 | // All helpers are on the `_` property 33 | console.log(helpers._); 34 | ``` 35 | 36 | **Get a specific collection** 37 | 38 | ```js 39 | var helpers = require('template-helpers'); 40 | 41 | // get only the math helpers 42 | var math = helpers.math; 43 | ``` 44 | 45 | ### Use with any template engine 46 | 47 | **Lo-Dash Example** 48 | 49 | ```js 50 | var context = {arr: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']}; 51 | 52 | // pass helpers on `imports` 53 | var imports = {imports: helpers.arrays}; 54 | var template = _.template('<%= first(foo) %>', imports); 55 | 56 | // pass context 57 | template({foo: ['a', 'b', 'c']}); 58 | //=> 'a' 59 | ``` 60 | 61 | ### Namespacing 62 | 63 | Handlebars and Lo-Dash both allow **dot notation** to be used for referencing helpers. Other engines may allow this too, I'd be happy to add this information to readme if someone wants to do a PR. 64 | 65 | **Example** 66 | 67 | ```js 68 | <%= path.dirname("a/b/c/d.js") %> 69 | ``` 70 | 71 | This can be used as a way of working around potential naming conflicts. 72 | 73 | 74 | ## Helpers 75 | Currently 87 helpers: 76 | 77 | + **[array](lib/array.js)** 78 | - [after](lib/array.js#L131) 79 | - [arrayify](lib/array.js#L44) 80 | - [before](lib/array.js#L110) 81 | - [compact](lib/array.js#L260) 82 | - [difference](lib/array.js#L280) 83 | - [first](lib/array.js#L62) 84 | - [isArray](lib/array.js#L21) 85 | - [join](lib/array.js#L191) 86 | - [last](lib/array.js#L85) 87 | - [length](lib/array.js#L242) 88 | - [map](lib/array.js#L159) 89 | - [sort](lib/array.js#L217) 90 | - [union](lib/array.js#L347) 91 | - [unique](lib/array.js#L316) 92 | + **[code](lib/code.js)** 93 | - [embed](lib/code.js#L25) 94 | - [jsfiddle](lib/code.js#L51) 95 | + **[collection](lib/collection.js)** 96 | - [any](lib/collection.js#L15) 97 | + **[conditional](lib/conditional.js)** 98 | - [_if](lib/conditional.js#L13) 99 | + **[fs](lib/fs.js)** 100 | - [concat](lib/fs.js#L40) 101 | - [read](lib/fs.js#L19) 102 | + **[html](lib/html.js)** 103 | - [escapeHtml](lib/html.js#L18) 104 | - [sanitize](lib/html.js#L46) 105 | + **[math](lib/math.js)** 106 | - [add](lib/math.js#L19) 107 | - [ceil](lib/math.js#L108) 108 | - [divide](lib/math.js#L54) 109 | - [floor](lib/math.js#L90) 110 | - [multiply](lib/math.js#L72) 111 | - [round](lib/math.js#L129) 112 | - [subtract](lib/math.js#L36) 113 | - [sum](lib/math.js#L146) 114 | + **[object](lib/object.js)** 115 | - [extend](lib/object.js#L224) 116 | - [fallback](lib/object.js#L25) 117 | - [forIn](lib/object.js#L187) 118 | - [forOwn](lib/object.js#L207) 119 | - [get](lib/object.js#L77) 120 | - [hasOwn](lib/object.js#L152) 121 | - [isObject](lib/object.js#L115) 122 | - [isPlainObject](lib/object.js#L138) 123 | - [keys](lib/object.js#L94) 124 | - [merge](lib/object.js#L253) 125 | - [omit](lib/object.js#L170) 126 | - [parse](lib/object.js#L59) 127 | - [stringify](lib/object.js#L42) 128 | + **[path](lib/path.js)** 129 | - [basename](lib/path.js#L38) 130 | - [dirname](lib/path.js#L20) 131 | - [ext](lib/path.js#L92) 132 | - [extname](lib/path.js#L74) 133 | - [filename](lib/path.js#L56) 134 | - [isAbsolute](lib/path.js#L210) 135 | - [isRelative](lib/path.js#L245) 136 | - [join](lib/path.js#L175) 137 | - [relative](lib/path.js#L129) 138 | - [resolve](lib/path.js#L110) 139 | - [segments](lib/path.js#L153) 140 | + **[string](lib/string.js)** 141 | - [camelcase](lib/string.js#L142) 142 | - [centerAlign](lib/string.js#L408) 143 | - [chop](lib/string.js#L123) 144 | - [count](lib/string.js#L352) 145 | - [dashcase](lib/string.js#L230) 146 | - [dotcase](lib/string.js#L206) 147 | - [ellipsis](lib/string.js#L476) 148 | - [hyphenate](lib/string.js#L293) 149 | - [isString](lib/string.js#L23) 150 | - [lowercase](lib/string.js#L61) 151 | - [pascalcase](lib/string.js#L164) 152 | - [pathcase](lib/string.js#L252) 153 | - [replace](lib/string.js#L428) 154 | - [reverse](lib/string.js#L370) 155 | - [rightAlign](lib/string.js#L389) 156 | - [sentencecase](lib/string.js#L274) 157 | - [slugify](lib/string.js#L313) 158 | - [snakecase](lib/string.js#L184) 159 | - [toString](lib/string.js#L44) 160 | - [trim](lib/string.js#L98) 161 | - [truncate](lib/string.js#L455) 162 | - [uppercase](lib/string.js#L79) 163 | - [wordwrap](lib/string.js#L332) 164 | 165 | ## Docs 166 | 167 | 168 | ## Run tests 169 | Install dev dependencies. 170 | 171 | ```bash 172 | npm i -d && npm test 173 | ``` 174 | 175 | ## Related 176 | * [handlebars-helpers](https://github.com/assemble/handlebars-helpers): 120+ Handlebars helpers in ~20 categories, for Assemble, YUI, Ghost or any Handlebars project. Includes helpers like {{i18}}, {{markdown}}, {{relative}}, {{extend}}, {{moment}}, and so on. 177 | 178 | ## Contributing 179 | Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/jonschlinkert/template-helpers/issues) 180 | 181 | ## Author 182 | 183 | **Jon Schlinkert** 184 | 185 | + [github/jonschlinkert](https://github.com/jonschlinkert) 186 | + [twitter/jonschlinkert](http://twitter.com/jonschlinkert) 187 | 188 | ## License 189 | Copyright (c) 2015 Jon Schlinkert 190 | Released under the MIT license 191 | 192 | *** 193 | 194 | _This file was generated by [verb-cli](https://github.com/assemble/verb-cli) on March 11, 2015._ 195 | 196 | [assemble]: https://github.com/assemble/assemble 197 | [verb]: https://github.com/assemble/verb 198 | [template]: https://github.com/jonschlinkert/template 199 | [word-wrap]: https://github.com/jonschlinkert/word-wrap 200 | [helper-concat]: https://github.com/helpers/helper-concat 201 | [path]: https://nodejs.org/api/path.html 202 | -------------------------------------------------------------------------------- /test/fixtures/a.js: -------------------------------------------------------------------------------- 1 | function foo(a, b, c) { 2 | return a + b + c; 3 | } -------------------------------------------------------------------------------- /test/fixtures/a.md: -------------------------------------------------------------------------------- 1 | ``` 2 | foo 3 | ``` -------------------------------------------------------------------------------- /test/fixtures/b.js: -------------------------------------------------------------------------------- 1 | function bar(x, y, z) { 2 | return x + y + z; 3 | } -------------------------------------------------------------------------------- /test/fs.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * template-helpers 3 | * 4 | * Copyright (c) 2015, Jon Schlinkert. 5 | * Licensed under the MIT License. 6 | */ 7 | 8 | 'use strict'; 9 | 10 | var assert = require('assert'); 11 | var helpers = require('..')('fs'); 12 | var template = require('lodash.template'); 13 | 14 | var imports = { imports: helpers }; 15 | 16 | describe('fs', function() { 17 | describe('exists', function() { 18 | it('should return false when the file does not exist.', function() { 19 | assert.equal(template('<%= exists("fooosos.js") %>', imports)(), 'false'); 20 | }); 21 | }); 22 | 23 | describe('read', function() { 24 | it('should return an empty string when the file does not exist.', function() { 25 | assert.equal(template('<%= read("fooosos.js") %>', imports)(), ''); 26 | }); 27 | 28 | it('should read a file and inject its content.', function() { 29 | assert.equal(template('<%= read("test/fixtures/a.js") %>', imports)(), [ 30 | 'function foo(a, b, c) {', 31 | ' return a + b + c;', 32 | '}' 33 | ].join('\n')); 34 | }); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/html.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * template-helpers 3 | * 4 | * Copyright (c) 2015, Jon Schlinkert. 5 | * Licensed under the MIT License. 6 | */ 7 | 8 | 'use strict'; 9 | 10 | var assert = require('assert'); 11 | var helpers = require('..')('html'); 12 | var template = require('lodash.template'); 13 | 14 | var imports = {imports: helpers}; 15 | 16 | describe('html', function() { 17 | describe('escapeHtml', function() { 18 | it('should return an empty string when undefined.', function() { 19 | assert.equal(template('<%= escapeHtml() %>', imports)(), ''); 20 | }); 21 | 22 | it('should return create a code example from the given file.', function() { 23 | assert.equal(template('<%= escapeHtml("foo") %>', imports)(), '<span>foo</span>'); 24 | }); 25 | }); 26 | 27 | describe('sanitize', function() { 28 | it('should return an empty string when undefined.', function() { 29 | assert.equal(template('<%= sanitize() %>', imports)(), ''); 30 | }); 31 | it('should strip html from a string.', function() { 32 | var actual = template('<%= sanitize("foo") %>', imports)(); 33 | assert.equal(actual, 'foo'); 34 | }); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/math.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * template-helpers 3 | * 4 | * Copyright (c) 2015, Jon Schlinkert. 5 | * Licensed under the MIT License. 6 | */ 7 | 8 | 'use strict'; 9 | 10 | var assert = require('assert'); 11 | var helpers = require('..')('math'); 12 | var template = require('lodash.template'); 13 | 14 | var imports = {imports: helpers}; 15 | 16 | describe('math helpers', function() { 17 | describe('add', function() { 18 | it('should return a plus b', function() { 19 | assert.equal(template('<%= add(5, 5) %>', imports)(), '10'); 20 | }); 21 | }); 22 | 23 | describe('subtract', function() { 24 | it('should return a minus b.', function() { 25 | assert.equal(template('<%= subtract(10, 2) %>', imports)(), '8'); 26 | }); 27 | }); 28 | 29 | describe('divide', function() { 30 | it('should divide a by b.', function() { 31 | assert.equal(template('<%= divide(30, 10) %>', imports)(), '3'); 32 | }); 33 | }); 34 | 35 | describe('multiply', function() { 36 | it('should multiply a by b.', function() { 37 | assert.equal(template('<%= multiply(30, 10) %>', imports)(), '300'); 38 | }); 39 | }); 40 | 41 | describe('floor', function() { 42 | it('should floor `num`.', function() { 43 | assert.equal(template('<%= floor(55.5) %>', imports)(), '55'); 44 | }); 45 | }); 46 | 47 | describe('ceil', function() { 48 | it('should ceil `num`.', function() { 49 | assert.equal(template('<%= ceil(55.5) %>', imports)(), '56'); 50 | }); 51 | }); 52 | 53 | describe('round', function() { 54 | it('should round a number.', function() { 55 | assert.equal(template('<%= round(21.1) %>', imports)(), '21'); 56 | assert.equal(template('<%= round(21.5) %>', imports)(), '22'); 57 | }); 58 | }); 59 | 60 | describe('sum.', function() { 61 | it('should reduce-sum the numbers in the array.', function() { 62 | assert.equal(template('<%= sum([1, 2, 3, 4, 5]) %>', imports)(), '15'); 63 | }); 64 | it('should ignore non-numbers.', function() { 65 | assert.equal(template('<%= sum([1, 2, "foo", 3, 4, 5]) %>', imports)(), '15'); 66 | }); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /test/object.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * template-helpers 3 | * 4 | * Copyright (c) 2015, Jon Schlinkert. 5 | * Licensed under the MIT License. 6 | */ 7 | 8 | 'use strict'; 9 | 10 | var assert = require('assert'); 11 | var helpers = require('..')('object'); 12 | var template = require('lodash.template'); 13 | 14 | var context = {obj: {a: 'a', b: 'b', c: {d: {e: 'e'}}}}; 15 | var imports = {imports: helpers}; 16 | 17 | describe('objects', function() { 18 | describe('fallback', function() { 19 | it('should use the fallback value when the first value is undefined.', function() { 20 | assert.equal(template('<%= fallback(a.b) %>', imports)({a: {b: 'b'}}), 'b'); 21 | assert.equal(template('<%= fallback(a.z, a.b) %>', imports)({a: {b: 'b'}}), 'b'); 22 | assert.equal(template('<%= fallback(x.k, x.z) %>', imports)({x: {z: 'z'}}), 'z'); 23 | }); 24 | }); 25 | 26 | describe('stringify', function() { 27 | it('should stringify an object.', function() { 28 | assert.equal(template('<%= stringify({a: "a"}) %>', imports)(context), '{"a":"a"}'); 29 | assert.equal(template('<%= stringify(obj.c) %>', imports)(context), '{"d":{"e":"e"}}'); 30 | }); 31 | }); 32 | 33 | describe('parse', function() { 34 | it('should parse a string to an object:', function() { 35 | assert.equal(template('<%= parse(\'{"foo":"bar"}\') %>', imports)(context), '[object Object]'); 36 | assert.equal(template('<%= parse(\'{"foo":"bar"}\')["foo"] %>', imports)(context), 'bar'); 37 | }); 38 | }); 39 | 40 | describe('isObject', function() { 41 | it('should return true if the value is an object.', function() { 42 | assert.equal(template('<%= isObject(obj) %>', imports)(context), 'true'); 43 | assert.equal(template('<%= isObject([]) %>', imports)(context), 'false'); 44 | assert.equal(template('<%= isObject("foo") %>', imports)(context), 'false'); 45 | }); 46 | }); 47 | 48 | describe('isPlainObject', function() { 49 | it('should return true if the value is a plain object.', function() { 50 | assert.equal(template('<%= isPlainObject(obj) %>', imports)(context), 'true'); 51 | assert.equal(template('<%= isPlainObject([]) %>', imports)(context), 'false'); 52 | assert.equal(template('<%= isPlainObject(/foo/) %>', imports)(context), 'false'); 53 | assert.equal(template('<%= isPlainObject("foo") %>', imports)(context), 'false'); 54 | }); 55 | }); 56 | 57 | describe('hasOwn', function() { 58 | it('should return true when an object has own property `key`.', function() { 59 | assert.equal(template('<%= hasOwn(obj, "a") %>', imports)(context), 'true'); 60 | assert.equal(template('<%= hasOwn(obj, "k") %>', imports)(context), 'false'); 61 | }); 62 | }); 63 | 64 | describe('keys', function() { 65 | it('should return the keys of an object.', function() { 66 | assert.equal(template('<%= keys(obj) %>', imports)(context), ['a', 'b', 'c'].toString()); 67 | }); 68 | }); 69 | 70 | describe('forIn', function() { 71 | it('should expose the keys on an object.', function() { 72 | var context = {values: {a: 'b', c: 'd'}} 73 | var actual = template('<% forIn(values, function(val, key) { %><%= key %><% }) %>', imports)(context); 74 | assert.equal(actual, 'ac'); 75 | }); 76 | 77 | it('should expose the values on an object.', function() { 78 | var context = {values: {a: 'b', c: 'd'}} 79 | var actual = template('<% forIn(values, function(val, key) { %><%= val %><% }) %>', imports)(context); 80 | assert.equal(actual, 'bd'); 81 | }); 82 | }); 83 | 84 | describe('forOwn', function() { 85 | it('should expose the keys on an object.', function() { 86 | var context = { values: { a: 'b', c: 'd' } }; 87 | var actual = template('<% forOwn(values, function(val, key) { %><%= key %><% }) %>', imports)(context); 88 | assert.equal(actual, 'ac'); 89 | }); 90 | 91 | it('should expose the values on an object.', function() { 92 | var context = {values: {a: 'b', c: 'd'}} 93 | var actual = template('<% forOwn(values, function(val, key) { %><%= val %><% }) %>', imports)(context); 94 | assert.equal(actual, 'bd'); 95 | }); 96 | }); 97 | 98 | describe('omit', function() { 99 | it('should omit keys from an object.', function() { 100 | var actual = template('<%= stringify(omit(obj, ["b", "c"])) %>', imports)(context); 101 | assert.equal(actual, '{"a":"a"}'); 102 | }); 103 | }); 104 | 105 | describe('extend', function() { 106 | beforeEach(function() { 107 | context.foo = {aaa: 'bbb'}; 108 | context.bar = {ccc: 'ddd'}; 109 | }); 110 | it('should return an empty string when undefined.', function() { 111 | assert.equal(template('<%= extend() %>', imports)(), ''); 112 | }); 113 | 114 | it('should ignore non-objects.', function() { 115 | var actual = template('<%= stringify(extend(foo, bar, "baz")) %>', imports)(context); 116 | assert.equal(actual, '{"aaa":"bbb","ccc":"ddd"}'); 117 | }); 118 | 119 | it('should extend the first object with the second.', function() { 120 | var actual = template('<%= stringify(extend(foo, bar)) %>', imports)(context); 121 | assert.equal(actual, '{"aaa":"bbb","ccc":"ddd"}'); 122 | }); 123 | 124 | it('should use the extended object as context.', function() { 125 | // overwrite `foo` 126 | context.bar = {aaa: 'ddd'}; 127 | var actual = template('<%= get(extend(foo, bar), "aaa") %>', imports)(context); 128 | assert.equal(actual, 'ddd'); 129 | }); 130 | }); 131 | 132 | describe('merge', function() { 133 | beforeEach(function() { 134 | context.foo = {aaa: 'bbb', bbb: {ccc: {ddd: 'eee'}}}; 135 | context.bar = {aaa: 'bbb', bbb: {ccc: {eee: 'fff'}}}; 136 | context.baz = {aaa: 'bbb', bbb: {ccc: {fff: 'ggg'}}}; 137 | }); 138 | it('should return an empty string when undefined.', function() { 139 | assert.equal(template('<%= merge() %>', imports)(), ''); 140 | }); 141 | it('should merge objects.', function() { 142 | var actual = template('<%= stringify(merge(foo, bar)) %>', imports)(context); 143 | assert.equal(actual, '{"aaa":"bbb","bbb":{"ccc":{"ddd":"eee","eee":"fff"}}}'); 144 | }); 145 | it('should merge multiple objects:', function() { 146 | var actual = template('<%= stringify(merge(foo, bar, baz)) %>', imports)(context); 147 | assert.equal(actual, '{"aaa":"bbb","bbb":{"ccc":{"ddd":"eee","eee":"fff","fff":"ggg"}}}'); 148 | }); 149 | }); 150 | }); 151 | -------------------------------------------------------------------------------- /test/path.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * template-helpers 3 | * 4 | * Copyright (c) 2015, Jon Schlinkert. 5 | * Licensed under the MIT License. 6 | */ 7 | 8 | 'use strict'; 9 | 10 | const path = require('path'); 11 | const assert = require('assert'); 12 | const template = require('lodash.template'); 13 | const helpers = require('..')('path'); 14 | const imports = { imports: helpers }; 15 | 16 | describe('path helpers', function() { 17 | if (process.platform === 'win32') { 18 | this.skip(); 19 | return; 20 | } 21 | 22 | describe('dirname', function() { 23 | it('should return the dirname:', function() { 24 | assert.strictEqual(template('<%= dirname("a/b/c/e.js") %>', imports)(), 'a/b/c'); 25 | assert.strictEqual(template('<%= path.dirname("a/b/c/e.js") %>', imports)(), 'a/b/c'); 26 | }); 27 | }); 28 | describe('basename', function() { 29 | it('should return the basename:', function() { 30 | assert.strictEqual(template('<%= basename("a/b/c/e.js") %>', imports)(), 'e.js'); 31 | assert.strictEqual(template('<%= path.basename("a/b/c/e.js") %>', imports)(), 'e.js'); 32 | }); 33 | }); 34 | describe('filename', function() { 35 | it('should return the filename:', function() { 36 | assert.strictEqual(template('<%= filename("a/b/c/e.js") %>', imports)(), 'e'); 37 | assert.strictEqual(template('<%= path.filename("a/b/c/e.js") %>', imports)(), 'e'); 38 | }); 39 | }); 40 | describe('extname', function() { 41 | it('should return the extname with dot:', function() { 42 | assert.strictEqual(template('<%= extname("a/b/c/e.js") %>', imports)(), '.js'); 43 | assert.strictEqual(template('<%= path.extname("a/b/c/e.js") %>', imports)(), '.js'); 44 | }); 45 | }); 46 | describe('ext', function() { 47 | it('should return the extension without dot:', function() { 48 | assert.strictEqual(template('<%= ext("a/b/c/e.js") %>', imports)(), 'js'); 49 | assert.strictEqual(template('<%= path.ext("a/b/c/e.js") %>', imports)(), 'js'); 50 | }); 51 | }); 52 | describe('resolve', function() { 53 | it('should resolve the given path(s):', function() { 54 | assert.strictEqual(template('<%= resolve("c/e.js") %>', imports)(), path.resolve('c/e.js')); 55 | assert.strictEqual(template('<%= path.resolve("c/e.js") %>', imports)(), path.resolve('c/e.js')); 56 | }); 57 | }); 58 | describe('relative', function() { 59 | it('should return a relative file path:', function() { 60 | assert.strictEqual(template('<%= relative("docs/", "docs/a/b.js") %>', imports)(), 'a/b.js'); 61 | assert.strictEqual(template('<%= path.relative("docs/", "docs/a/b.js") %>', imports)(), 'a/b.js'); 62 | assert.strictEqual(template('<%= relative("docs/a/b.js", "docs/") %>', imports)(), '..'); 63 | assert.strictEqual(template('<%= path.relative("docs/a/b.js", "docs/") %>', imports)(), '..'); 64 | }); 65 | }); 66 | 67 | describe('isRelative', function() { 68 | it('should return true if a file path is isRelative:', function() { 69 | assert.strictEqual(template('<%= isRelative("/foo/bar") %>', imports)(), 'false'); 70 | assert.strictEqual(template('<%= path.isRelative("/foo/bar") %>', imports)(), 'false'); 71 | assert.strictEqual(template('<%= isRelative("/baz/..") %>', imports)(), 'false'); 72 | assert.strictEqual(template('<%= path.isRelative("/baz/..") %>', imports)(), 'false'); 73 | assert.strictEqual(template('<%= isRelative("qux/") %>', imports)(), 'true'); 74 | assert.strictEqual(template('<%= path.isRelative("qux/") %>', imports)(), 'true'); 75 | assert.strictEqual(template('<%= isRelative(".") %>', imports)(), 'true'); 76 | assert.strictEqual(template('<%= path.isRelative(".") %>', imports)(), 'true'); 77 | assert.strictEqual(template('<%= isRelative("//server") %>', imports)(), 'false'); 78 | assert.strictEqual(template('<%= path.isRelative("//server") %>', imports)(), 'false'); 79 | assert.strictEqual(template('<%= isRelative("bar\\baz") %>', imports)(), 'true'); 80 | assert.strictEqual(template('<%= path.isRelative("bar\\baz") %>', imports)(), 'true'); 81 | assert.strictEqual(template('<%= isRelative(".") %>', imports)(), 'true'); 82 | assert.strictEqual(template('<%= path.isRelative(".") %>', imports)(), 'true'); 83 | }); 84 | }); 85 | 86 | describe('isAbsolute', function() { 87 | it('should return true if a file path is absolute:', function() { 88 | assert.strictEqual(template('<%= isAbsolute("/foo/bar") %>', imports)(), 'true'); 89 | assert.strictEqual(template('<%= path.isAbsolute("/foo/bar") %>', imports)(), 'true'); 90 | assert.strictEqual(template('<%= isAbsolute("/baz/..") %>', imports)(), 'true'); 91 | assert.strictEqual(template('<%= path.isAbsolute("/baz/..") %>', imports)(), 'true'); 92 | assert.strictEqual(template('<%= isAbsolute("qux/") %>', imports)(), 'false'); 93 | assert.strictEqual(template('<%= path.isAbsolute("qux/") %>', imports)(), 'false'); 94 | assert.strictEqual(template('<%= isAbsolute(".") %>', imports)(), 'false'); 95 | assert.strictEqual(template('<%= path.isAbsolute(".") %>', imports)(), 'false'); 96 | assert.strictEqual(template('<%= isAbsolute("//server") %>', imports)(), 'true'); 97 | assert.strictEqual(template('<%= path.isAbsolute("//server") %>', imports)(), 'true'); 98 | assert.strictEqual(template('<%= isAbsolute("bar\\baz") %>', imports)(), 'false'); 99 | assert.strictEqual(template('<%= path.isAbsolute("bar\\baz") %>', imports)(), 'false'); 100 | assert.strictEqual(template('<%= isAbsolute(".") %>', imports)(), 'false'); 101 | assert.strictEqual(template('<%= path.isAbsolute(".") %>', imports)(), 'false'); 102 | }); 103 | }); 104 | 105 | describe('join', function() { 106 | it('should join the given paths:', function() { 107 | assert.strictEqual(template('<%= join("a", "b") %>', imports)(), 'a/b'); 108 | assert.strictEqual(template('<%= path.join("a", "b") %>', imports)(), 'a/b'); 109 | }); 110 | }); 111 | 112 | describe('segments', function() { 113 | it('should return specified path segments:', function() { 114 | assert.strictEqual(template('<%= segments("a/b/c/e.js", 1, 3) %>', imports)(), 'b/c'); 115 | assert.strictEqual(template('<%= path.segments("a/b/c/e.js", 1, 3) %>', imports)(), 'b/c'); 116 | assert.strictEqual(template('<%= segments("a/b/c/e.js", 1, 2) %>', imports)(), 'b'); 117 | assert.strictEqual(template('<%= path.segments("a/b/c/e.js", 1, 2) %>', imports)(), 'b'); 118 | assert.strictEqual(template('<%= segments("a/b/c/e.js", 0, 3) %>', imports)(), 'a/b/c'); 119 | assert.strictEqual(template('<%= path.segments("a/b/c/e.js", 0, 3) %>', imports)(), 'a/b/c'); 120 | assert.strictEqual(template('<%= segments("a/b/c/e.js", 2, 3) %>', imports)(), 'c'); 121 | assert.strictEqual(template('<%= path.segments("a/b/c/e.js", 2, 3) %>', imports)(), 'c'); 122 | assert.strictEqual(template('<%= segments("a/b/c/e.js", 0, 3) %>', imports)(), 'a/b/c'); 123 | assert.strictEqual(template('<%= path.segments("a/b/c/e.js", 0, 3) %>', imports)(), 'a/b/c'); 124 | }); 125 | 126 | it('should disregard extra slashes:', function() { 127 | assert.strictEqual(template('<%= segments("a///b\\/c\\/e.js", 1, 3) %>', imports)(), 'b/c'); 128 | assert.strictEqual(template('<%= path.segments("a///b\\/c\\/e.js", 1, 3) %>', imports)(), 'b/c'); 129 | assert.strictEqual(template('<%= segments("a///b/\\//c/e.js", 1, 2) %>', imports)(), 'b'); 130 | assert.strictEqual(template('<%= path.segments("a///b/\\//c/e.js", 1, 2) %>', imports)(), 'b'); 131 | }); 132 | }); 133 | }); 134 | -------------------------------------------------------------------------------- /test/string.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * template-helpers 3 | * 4 | * Copyright (c) 2015, Jon Schlinkert. 5 | * Licensed under the MIT License. 6 | */ 7 | 8 | 'use strict'; 9 | 10 | const assert = require('assert'); 11 | const template = require('lodash.template'); 12 | const helpers = require('..')(['string', 'html']); 13 | const imports = { imports: helpers }; 14 | 15 | describe('string helpers', function() { 16 | describe('lowercase', function() { 17 | it('should return an empty string when undefined.', function() { 18 | assert.equal(template('<%= lowercase() %>', imports)(), ''); 19 | }); 20 | it('should lower case the characters in a string.', function() { 21 | var fn = template('<%= lowercase("ABC") %>', imports); 22 | assert.equal(fn(), 'abc'); 23 | }); 24 | }); 25 | 26 | describe('uppercase', function() { 27 | it('should return an empty string when undefined.', function() { 28 | assert.equal(template('<%= uppercase() %>', imports)(), ''); 29 | }); 30 | it('should upper case the characters in a string.', function() { 31 | var fn = template('<%= uppercase("abc") %>', imports); 32 | assert.equal(fn(), 'ABC'); 33 | }); 34 | }); 35 | 36 | describe('trim', function() { 37 | it('should return an empty string when undefined.', function() { 38 | assert.equal(template('<%= trim() %>', imports)(), ''); 39 | }); 40 | it('should strip leading whitespace from a string.', function() { 41 | var fn = template('<%= trim(" abc") %>', imports); 42 | assert.equal(fn(), 'abc'); 43 | }); 44 | it('should strip trailing whitespace from a string.', function() { 45 | var fn = template('<%= trim("abc ") %>', imports); 46 | assert.equal(fn(), 'abc'); 47 | }); 48 | }); 49 | 50 | describe('chop', function() { 51 | it('should return an empty string when undefined.', function() { 52 | assert.equal(template('<%= chop() %>', imports)(), ''); 53 | }); 54 | it('should strip leading whitespace from a string.', function() { 55 | var fn = template('<%= chop(" abc") %>', imports); 56 | assert.equal(fn(), 'abc'); 57 | }); 58 | it('should strip trailing whitespace from a string.', function() { 59 | var fn = template('<%= chop("abc ") %>', imports); 60 | assert.equal(fn(), 'abc'); 61 | }); 62 | it('should strip leading non-word characters from a string.', function() { 63 | var fn = template('<%= chop("_-abc") %>', imports); 64 | assert.equal(fn(), 'abc'); 65 | }); 66 | it('should strip trailing non-word characters from a string.', function() { 67 | var fn = template('<%= chop("abc_-") %>', imports); 68 | assert.equal(fn(), 'abc'); 69 | }); 70 | }); 71 | 72 | describe('strip', function() { 73 | it('should return an empty string when undefined.', function() { 74 | assert.equal(template('<%= strip() %>', imports)(), ''); 75 | }); 76 | it('should strip the given substring from a string.', function() { 77 | var fn = template('<%= strip("foo", "foobar") %>', imports); 78 | assert.equal(fn(), 'bar'); 79 | }); 80 | it('should strip the given regex match from a string.', function() { 81 | var fn = template('<%= strip(/^foo/, "foobarfoo") %>', imports); 82 | assert.equal(fn(), 'barfoo'); 83 | }); 84 | }); 85 | 86 | describe('stripIndent', function() { 87 | it('should return an empty string when undefined.', function() { 88 | assert.equal(template('<%= stripIndent() %>', imports)(), ''); 89 | }); 90 | it('should strip indentation from a string.', function() { 91 | var str = template('<%= stripIndent(str) %>', imports)({ 92 | str: [' - a', ' - b', ' * c', ' * d', ' + e', ' + f'].join('\n') 93 | }); 94 | assert.equal(str, ['- a', '- b', ' * c', ' * d', ' + e', ' + f'].join('\n')); 95 | }); 96 | }); 97 | 98 | describe('camelcase', function() { 99 | it('should return an empty string when undefined.', function() { 100 | assert.equal(template('<%= camelcase() %>', imports)(), ''); 101 | }); 102 | it('should camel-case the characters in a string.', function() { 103 | assert.equal(template('<%= camelcase("foo bar baz") %>', imports)(), 'fooBarBaz'); 104 | }); 105 | it('should camel-case the characters in a string.', function() { 106 | assert.equal(template('<%= camelcase("A") %>', imports)(), 'a'); 107 | }); 108 | it('should work with hyphens.', function() { 109 | assert.equal(template('<%= camelcase("foo-bar-baz") %>', imports)(), 'fooBarBaz'); 110 | assert.equal(template('<%= camelcase("-foo bar baz-") %>', imports)(), 'fooBarBaz'); 111 | }); 112 | 113 | it('should work with other non-word characters.', function() { 114 | assert.equal(template('<%= camelcase("9foo-bar_baz") %>', imports)(), '9fooBarBaz'); 115 | assert.equal(template('<%= camelcase("_foo_bar_baz-") %>', imports)(), 'fooBarBaz'); 116 | }); 117 | }); 118 | 119 | describe('pascalcase', function() { 120 | it('should return an empty string when undefined.', function() { 121 | assert.equal(template('<%= pascalcase() %>', imports)(), ''); 122 | }); 123 | it('should camel-case the characters in a string.', function() { 124 | assert.equal(template('<%= pascalcase("a") %>', imports)(), 'A'); 125 | assert.equal(template('<%= pascalcase("A") %>', imports)(), 'A'); 126 | }); 127 | it('should pascal-case the characters in a string.', function() { 128 | assert.equal(template('<%= pascalcase("foo bar baz") %>', imports)(), 'FooBarBaz'); 129 | }); 130 | it('should work with hyphens.', function() { 131 | assert.equal(template('<%= pascalcase("foo-bar-baz") %>', imports)(), 'FooBarBaz'); 132 | assert.equal(template('<%= pascalcase("-foo bar baz-") %>', imports)(), 'FooBarBaz'); 133 | }); 134 | 135 | it('should work with other non-word characters.', function() { 136 | assert.equal(template('<%= pascalcase("9foo-bar_baz") %>', imports)(), '9fooBarBaz'); 137 | assert.equal(template('<%= pascalcase("_foo_bar_baz-") %>', imports)(), 'FooBarBaz'); 138 | }); 139 | }); 140 | 141 | describe('snakecase', function() { 142 | it('should return an empty string when undefined.', function() { 143 | assert.equal(template('<%= snakecase() %>', imports)(), ''); 144 | }); 145 | it('should camel-case the characters in a string.', function() { 146 | assert.equal(template('<%= snakecase("A") %>', imports)(), 'a'); 147 | }); 148 | it('should snake-case the characters in a string.', function() { 149 | assert.equal(template('<%= snakecase("foo bar baz") %>', imports)(), 'foo_bar_baz'); 150 | }); 151 | it('should work with hyphens.', function() { 152 | assert.equal(template('<%= snakecase("foo-bar-baz") %>', imports)(), 'foo_bar_baz'); 153 | assert.equal(template('<%= snakecase("-foo bar baz-") %>', imports)(), 'foo_bar_baz'); 154 | }); 155 | 156 | it('should work with other non-word characters.', function() { 157 | assert.equal(template('<%= snakecase("9foo-bar_baz") %>', imports)(), '9foo_bar_baz'); 158 | assert.equal(template('<%= snakecase("_foo_bar_baz-") %>', imports)(), 'foo_bar_baz'); 159 | }); 160 | }); 161 | 162 | describe('dotcase', function() { 163 | it('should return an empty string when undefined.', function() { 164 | assert.equal(template('<%= dotcase() %>', imports)(), ''); 165 | }); 166 | it('should camel-case the characters in a string.', function() { 167 | assert.equal(template('<%= dotcase("A") %>', imports)(), 'a'); 168 | }); 169 | it('should dot-case the characters in a string.', function() { 170 | assert.equal(template('<%= dotcase("foo bar baz") %>', imports)(), 'foo.bar.baz'); 171 | }); 172 | it('should work with hyphens.', function() { 173 | assert.equal(template('<%= dotcase("foo-bar-baz") %>', imports)(), 'foo.bar.baz'); 174 | assert.equal(template('<%= dotcase("-foo bar baz-") %>', imports)(), 'foo.bar.baz'); 175 | }); 176 | 177 | it('should work with other non-word characters.', function() { 178 | assert.equal(template('<%= dotcase("9foo-bar_baz") %>', imports)(), '9foo.bar.baz'); 179 | assert.equal(template('<%= dotcase("_foo_bar_baz-") %>', imports)(), 'foo.bar.baz'); 180 | }); 181 | }); 182 | 183 | describe('dashcase', function() { 184 | it('should return an empty string when undefined.', function() { 185 | assert.equal(template('<%= dashcase() %>', imports)(), ''); 186 | }); 187 | it('should camel-case the characters in a string.', function() { 188 | assert.equal(template('<%= dashcase("A") %>', imports)(), 'a'); 189 | }); 190 | it('should dash-case the characters in a string.', function() { 191 | assert.equal(template('<%= dashcase("foo bar baz") %>', imports)(), 'foo-bar-baz'); 192 | }); 193 | it('should work with hyphens.', function() { 194 | assert.equal(template('<%= dashcase("foo-bar-baz") %>', imports)(), 'foo-bar-baz'); 195 | assert.equal(template('<%= dashcase("-foo bar baz-") %>', imports)(), 'foo-bar-baz'); 196 | }); 197 | 198 | it('should work with other non-word characters.', function() { 199 | assert.equal(template('<%= dashcase("9foo-bar_baz") %>', imports)(), '9foo-bar-baz'); 200 | assert.equal(template('<%= dashcase("_foo_bar_baz-") %>', imports)(), 'foo-bar-baz'); 201 | }); 202 | }); 203 | 204 | describe('pathcase', function() { 205 | it('should return an empty string when undefined.', function() { 206 | assert.equal(template('<%= pathcase() %>', imports)(), ''); 207 | }); 208 | it('should camel-case the characters in a string.', function() { 209 | assert.equal(template('<%= pathcase("A") %>', imports)(), 'a'); 210 | }); 211 | it('should path-case the characters in a string.', function() { 212 | assert.equal(template('<%= pathcase("foo bar baz") %>', imports)(), 'foo/bar/baz'); 213 | }); 214 | it('should work with hyphens.', function() { 215 | assert.equal(template('<%= pathcase("foo-bar-baz") %>', imports)(), 'foo/bar/baz'); 216 | assert.equal(template('<%= pathcase("-foo bar baz-") %>', imports)(), 'foo/bar/baz'); 217 | }); 218 | 219 | it('should work with other non-word characters.', function() { 220 | assert.equal(template('<%= pathcase("9foo-bar_baz") %>', imports)(), '9foo/bar/baz'); 221 | assert.equal(template('<%= pathcase("_foo_bar_baz-") %>', imports)(), 'foo/bar/baz'); 222 | }); 223 | }); 224 | 225 | describe('sentencecase', function() { 226 | it('should return an empty string when undefined.', function() { 227 | assert.equal(template('<%= sentencecase() %>', imports)(), ''); 228 | }); 229 | it('should camel-case the characters in a string.', function() { 230 | assert.equal(template('<%= sentencecase("A") %>', imports)(), 'A'); 231 | assert.equal(template('<%= sentencecase("a") %>', imports)(), 'A'); 232 | }); 233 | it('should sentence-case the characters in a string.', function() { 234 | assert.equal(template('<%= sentencecase("foo bar baz.") %>', imports)(), 'Foo bar baz.'); 235 | assert.equal(template('<%= sentencecase("a") %>', imports)(), 'A'); 236 | }); 237 | }); 238 | 239 | describe('hyphenate', function() { 240 | it('should return an empty string when undefined.', function() { 241 | assert.equal(template('<%= hyphenate() %>', imports)(), ''); 242 | }); 243 | it('should hyphenate the characters in a string.', function() { 244 | assert.equal(template('<%= hyphenate("foo bar baz") %>', imports)(), 'foo-bar-baz'); 245 | }); 246 | it('should work with hyphens.', function() { 247 | assert.equal(template('<%= hyphenate("foo-bar-baz") %>', imports)(), 'foo-bar-baz'); 248 | assert.equal(template('<%= hyphenate("-foo bar baz-") %>', imports)(), 'foo-bar-baz'); 249 | }); 250 | 251 | it('should work with other non-word characters.', function() { 252 | assert.equal(template('<%= hyphenate("9foo-bar_baz") %>', imports)(), '9foo-bar_baz'); 253 | assert.equal(template('<%= hyphenate("_foo_bar_baz-") %>', imports)(), 'foo_bar_baz'); 254 | }); 255 | }); 256 | 257 | describe('slugify', function() { 258 | it('should return an empty string when undefined.', function() { 259 | assert.equal(template('<%= slugify() %>', imports)(), ''); 260 | }); 261 | 262 | it('should slugify the characters in a string.', function() { 263 | assert.equal(template('<%= slugify("foo bar baz") %>', imports)(), 'foo-bar-baz'); 264 | }); 265 | 266 | it('should work with hyphens.', function() { 267 | assert.equal(template('<%= slugify("foo-bar-baz") %>', imports)(), 'foo-bar-baz'); 268 | assert.equal(template('<%= slugify("-foo bar baz-") %>', imports)(), '-foo-bar-baz-'); 269 | }); 270 | 271 | it('should work with other non-word characters.', function() { 272 | assert.equal(template('<%= slugify("9foo-bar_baz") %>', imports)(), '9foo-bar_baz'); 273 | assert.equal(template('<%= slugify("_foo_bar_baz-") %>', imports)(), '_foo_bar_baz-'); 274 | }); 275 | }); 276 | 277 | describe('count', function() { 278 | it('should return an empty string when undefined.', function() { 279 | assert.equal(template('<%= count() %>', imports)(), ''); 280 | }); 281 | it('should return zero when the substring is undefined.', function() { 282 | assert.equal(template('<%= count("ababa") %>', imports)(), '0'); 283 | }); 284 | it('should count the occurrances of a substring.', function() { 285 | assert.equal(template('<%= count("ababa", "a") %>', imports)(), '3'); 286 | assert.equal(template('<%= count("abab", "a") %>', imports)(), '2'); 287 | assert.equal(template('<%= count("ababaa", "a") %>', imports)(), '4'); 288 | }); 289 | }); 290 | 291 | describe('reverse', function() { 292 | it('should return an empty string when undefined.', function() { 293 | assert.equal(template('<%= reverse() %>', imports)(), ''); 294 | }); 295 | it('should reverse the characters in a string.', function() { 296 | assert.equal(template('<%= reverse("abc") %>', imports)(), 'cba'); 297 | }); 298 | }); 299 | 300 | describe('wordwrap', function() { 301 | it('should return an empty string when undefined.', function() { 302 | assert.equal(template('<%= wordwrap() %>', imports)(), ''); 303 | }); 304 | it('should wrap words to the specified width.', function() { 305 | var actual = template('<%= wordwrap("a b c d e f", {width: 5}) %>', imports)(); 306 | assert.equal(actual, ' a b c \n d e f'); 307 | }); 308 | 309 | it('should use custom newline characters.', function() { 310 | var actual = template('<%= wordwrap("a b c d e f", {width: 5, newline: "
"}) %>', imports)(); 311 | assert.equal(actual, ' a b c
d e f'); 312 | }); 313 | }); 314 | 315 | describe('align', function() { 316 | it('should return an empty string when undefined.', function() { 317 | assert.equal(template('<%= rightAlign() %>', imports)(), ''); 318 | assert.equal(template('<%= centerAlign() %>', imports)(), ''); 319 | }); 320 | it('should right align the characters in a string.', function() { 321 | var actual = template('<%= rightAlign("foo\\nbarbazb") %>', imports)(); 322 | assert.equal(actual, ' foo\nbarbazb'); 323 | }); 324 | it('should center align the characters in a string.', function() { 325 | var actual = template('<%= centerAlign("foo\\nbarbazb") %>', imports)(); 326 | assert.equal(actual, ' foo\nbarbazb'); 327 | }); 328 | }); 329 | 330 | describe('replace', function() { 331 | it('should return an empty string when undefined.', function() { 332 | assert.equal(template('<%= replace() %>', imports)(), ''); 333 | }); 334 | it('should return the string when no replacement pattern is passed.', function() { 335 | var actual = template('<%= replace("abcabc") %>', imports)(); 336 | assert.equal(actual, 'abcabc'); 337 | }); 338 | it('should replace characters in a string with nothing.', function() { 339 | var actual = template('<%= replace("abcabc", "a") %>', imports)(); 340 | assert.equal(actual, 'bcbc'); 341 | }); 342 | it('should replace characters in a string with a string', function() { 343 | var actual = template('<%= replace("abcabc", "a", "z") %>', imports)(); 344 | assert.equal(actual, 'zbczbc'); 345 | }); 346 | }); 347 | 348 | describe('titlecase', function() { 349 | it('should return an empty string when undefined.', function() { 350 | assert.equal(template('<%= titlecase("foo") %>', imports)(), 'Foo'); 351 | }); 352 | it('should upper case the characters in a string.', function() { 353 | var fn = template('<%= titlecase("one two three") %>', imports); 354 | assert.equal(fn(), 'One Two Three'); 355 | }); 356 | }); 357 | 358 | describe('truncate', function() { 359 | it('should return an empty string when undefined.', function() { 360 | assert.equal(template('<%= truncate() %>', imports)(), ''); 361 | }); 362 | it('should truncate a string to the specified `length`', function() { 363 | assert.equal(template('<%= truncate("foo bar baz", 7) %>', imports)(), 'foo bar'); 364 | assert.equal(template('<%= truncate(sanitize("foo bar baz"), 7) %>', imports)(), 'foo bar'); 365 | }); 366 | }); 367 | 368 | describe('ellipsis', function() { 369 | it('should return an empty string when undefined.', function() { 370 | assert.equal(template('<%= ellipsis() %>', imports)(), ''); 371 | }); 372 | it('should truncate a string to the specified `length` and add an ellipsis.', function() { 373 | assert.equal(template('<%= ellipsis("foo bar baz", 7) %>', imports)(), 'foo bar…'); 374 | assert.equal(template('<%= ellipsis(sanitize("foo bar baz"), 7) %>', imports)(), 'foo bar…'); 375 | }); 376 | }); 377 | }); 378 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * template-helpers 3 | * 4 | * Copyright (c) 2015, Jon Schlinkert. 5 | * Licensed under the MIT License. 6 | */ 7 | 8 | 'use strict'; 9 | 10 | const assert = require('assert'); 11 | const lib = require('..'); 12 | let helpers; 13 | 14 | describe('helpers', function() { 15 | before(function() { 16 | helpers = lib(); 17 | }); 18 | 19 | it('should expose all helpers on main export', function() { 20 | let keys = Object.keys(helpers); 21 | assert(keys.length > 70); 22 | }); 23 | 24 | it('should expose array helpers on `array`', function() { 25 | assert(helpers.array); 26 | assert(typeof helpers.array === 'object'); 27 | }); 28 | 29 | it('should filter out array helpers when passed as an argument to lib', function() { 30 | let array = lib('array'); 31 | assert(typeof array === 'object'); 32 | }); 33 | 34 | it('should expose code helpers on `code`', function() { 35 | assert(helpers.code); 36 | assert(typeof helpers.code === 'object'); 37 | }); 38 | 39 | it('should filter out code helpers when passed as an argument to lib', function() { 40 | let code = lib('code'); 41 | assert(typeof code === 'object'); 42 | }); 43 | 44 | it('should expose collection helpers on `collection`', function() { 45 | assert(helpers.collection); 46 | assert(typeof helpers.collection === 'object'); 47 | }); 48 | 49 | it('should filter out collection helpers when passed as an argument to lib', function() { 50 | let collection = lib('collection'); 51 | assert(typeof collection === 'object'); 52 | }); 53 | 54 | it('should expose conditional helpers on `conditional`', function() { 55 | assert(helpers.conditional); 56 | assert(typeof helpers.conditional === 'object'); 57 | }); 58 | 59 | it('should filter out conditional helpers when passed as an argument to lib', function() { 60 | let conditional = lib('conditional'); 61 | assert(typeof conditional === 'object'); 62 | }); 63 | 64 | it('should expose fs helpers on `fs`', function() { 65 | assert(helpers.fs); 66 | assert(typeof helpers.fs === 'object'); 67 | }); 68 | 69 | it('should filter out fs helpers when passed as an argument to lib', function() { 70 | let fs = lib('fs'); 71 | assert(typeof fs === 'object'); 72 | }); 73 | 74 | it('should expose html helpers on `html`', function() { 75 | assert(helpers.html); 76 | assert(typeof helpers.html === 'object'); 77 | }); 78 | 79 | it('should filter out html helpers when passed as an argument to lib', function() { 80 | let html = lib('html'); 81 | assert(typeof html === 'object'); 82 | }); 83 | 84 | it('should expose math helpers on `math`', function() { 85 | assert(helpers.math); 86 | assert(typeof helpers.math === 'object'); 87 | }); 88 | 89 | it('should filter out math helpers when passed as an argument to lib', function() { 90 | let math = lib('math'); 91 | assert(typeof math === 'object'); 92 | }); 93 | 94 | it('should expose object helpers on `object`', function() { 95 | assert(helpers.object); 96 | assert(typeof helpers.object === 'object'); 97 | }); 98 | 99 | it('should filter out object helpers when passed as an argument to lib', function() { 100 | let object = lib('object'); 101 | assert(typeof object === 'object'); 102 | }); 103 | 104 | it('should expose path helpers on `path`', function() { 105 | assert(helpers.path); 106 | assert(typeof helpers.path === 'object'); 107 | }); 108 | 109 | it('should filter out path helpers when passed as an argument to lib', function() { 110 | let path = lib('path'); 111 | assert(typeof path === 'object'); 112 | }); 113 | 114 | it('should expose string helpers on `string`', function() { 115 | assert(helpers.string); 116 | assert(typeof helpers.string === 'object'); 117 | }); 118 | 119 | it('should filter out string helpers when passed as an argument to lib', function() { 120 | let string = lib('string'); 121 | assert(typeof string === 'object'); 122 | assert(typeof string.lower === 'function'); 123 | assert(typeof string.upper === 'function'); 124 | }); 125 | 126 | it('should filter out an array of groups', function() { 127 | let helpers = lib(['string', 'array']); 128 | assert(typeof helpers === 'object'); 129 | assert(typeof helpers.last === 'function'); 130 | assert(typeof helpers.upper === 'function'); 131 | }); 132 | }); 133 | -------------------------------------------------------------------------------- /verbfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const link = require('markdown-link'); 6 | const through = require('through2'); 7 | const cache = {}; 8 | 9 | /** 10 | * TODO: externalize to `verb-generate-toc` 11 | */ 12 | 13 | module.exports = function(verb, base, env) { 14 | verb.on('error', console.log); 15 | 16 | verb.use(require('verb-generate-readme')); 17 | verb.helpers(require('./')()); 18 | helpers(verb); 19 | 20 | verb.preRender(/\.md$/, function(file, next) { 21 | file.options.stripEmpty = false; 22 | next(); 23 | }); 24 | 25 | verb.task('toc', function(cb) { 26 | let total = { categories: 0, helpers: 0 }; 27 | let sections = []; 28 | let summary = []; 29 | let methods = {}; 30 | let toc = []; 31 | 32 | return verb.src('lib/helpers/*.js') 33 | .pipe(through.obj(function(file, enc, next) { 34 | if (file.stem === 'index') { 35 | next(); 36 | return; 37 | } 38 | 39 | try { 40 | file.base = verb.cwd; 41 | file.code = require(file.path); 42 | let heading = listItem(file); 43 | let newFile = { 44 | methods: require(file.path), 45 | test: { 46 | path: path.join('test', file.basename), 47 | code: {} 48 | }, 49 | code: {}, 50 | path: file.relative, 51 | base: file.base, 52 | cwd: file.cwd, 53 | relative: file.relative, 54 | stem: file.stem || path.basename(file.path, path.extname(filepath)), 55 | data: { 56 | methods: {} 57 | } 58 | }; 59 | 60 | let testLine = matchTest(newFile.test.path); 61 | let codeLine = matchCode(newFile.path); 62 | 63 | for (let key in newFile.methods) { 64 | total.helpers++; 65 | if (newFile.methods.hasOwnProperty(key)) { 66 | newFile.data.methods[key] = { 67 | method: newFile.stem, 68 | stem: key, 69 | code: { 70 | path: newFile.relative, 71 | line: codeLine(key) 72 | }, 73 | test: { 74 | path: newFile.test.path, 75 | line: testLine(key) 76 | } 77 | }; 78 | } 79 | } 80 | 81 | methods[newFile.stem] = newFile; 82 | total.categories++; 83 | next(); 84 | } catch (err) { 85 | next(err); 86 | } 87 | }, function(next) { 88 | verb.data({ total }); 89 | verb.data({ methods }); 90 | next(); 91 | })); 92 | }); 93 | 94 | verb.task('sectionToc', function() { 95 | let toc = ''; 96 | return verb.src('lib/helpers/*.js') 97 | .pipe(through.obj(function(file, enc, next) { 98 | if (file.stem !== 'index') { 99 | file.base = verb.cwd; 100 | toc += '- ' + link(file.stem, '#' + file.stem) + ' '; 101 | toc += '(code '+ link(file.stem, file.relative) + ')\n'; 102 | } 103 | next(); 104 | }, function(next) { 105 | // create an `include` from the toc 106 | verb.include('toc.md', { contents: Buffer.from(toc) }); 107 | next(); 108 | })) 109 | .pipe(verb.dest('.')); 110 | }); 111 | 112 | verb.task('fix-headings', function() { 113 | return verb.src('README.md') 114 | .pipe(through.obj(function(file, enc, next) { 115 | let str = file.contents.toString(); 116 | str = str.replace(/\n### index/, ''); 117 | str = str.replace(/^(#{2,}\s*\[)\./gm, '$1'); 118 | file.contents = Buffer.from(str); 119 | next(null, file); 120 | })) 121 | .pipe(verb.dest('.')); 122 | }); 123 | 124 | verb.task('default', ['toc', 'readme', 'fix-headings']); 125 | }; 126 | 127 | function section(name) { 128 | return `## ${name}\n{%= apidocs("lib/helpers/${name}.js") %}`; 129 | } 130 | 131 | function matchTest(fp) { 132 | if (!fp || !fs.existsSync(fp)) return function() {}; 133 | let str = fs.readFileSync(fp, 'utf8'); 134 | let lines = str.split('\n'); 135 | return function(method) { 136 | let re = new RegExp('\\s*(describe|it)\\([\'"]\\.?(' + method + ')'); 137 | let len = lines.length, i = -1; 138 | while (++i < len) { 139 | let line = lines[i]; 140 | if (re.test(line)) { 141 | return i + 1; 142 | } 143 | } 144 | return; 145 | }; 146 | } 147 | 148 | function matchCode(fp) { 149 | if (!fp || !fs.existsSync(fp)) return function() {}; 150 | let str = fs.readFileSync(fp, 'utf8'); 151 | let lines = str.split('\n'); 152 | return method => { 153 | let re = new RegExp(`^exports\\.(${method})`, 'gm'); 154 | let len = lines.length, i = -1; 155 | while (++i < len) { 156 | let line = lines[i]; 157 | if (re.test(line)) { 158 | return i + 1; 159 | } 160 | } 161 | }; 162 | } 163 | 164 | function helpers(app) { 165 | app.helper('bullet', function(file) { 166 | return '- **' + link(file.stem, '#' + file.stem) + '**'; 167 | }); 168 | 169 | app.helper('issue', function(name) { 170 | let repo = app.cache.data.repository; 171 | return '[create issue](https://github.com/' + repo + '/issues/new?title=' + name + '%20helper)'; 172 | }); 173 | 174 | app.helper('sectionIssue', function(section) { 175 | let repo = app.cache.data.repository; 176 | let url = 'https://github.com/' + repo + '/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+'; 177 | return '[issues](' + url + section + '+helpers)'; 178 | }); 179 | 180 | app.helper('anchor', function(file) { 181 | return link(file.stem, '#' + file.stem); 182 | }); 183 | 184 | app.helper('codeLink', function(file) { 185 | return codeLink('code', file.code.path, file.code.line); 186 | }); 187 | 188 | app.helper('testLink', function(file) { 189 | let line = file.test.line; 190 | return line ? codeLink('unit tests', file.test.path, line) : '[no tests]'; 191 | }); 192 | 193 | app.helper('link', function(name, filepath) { 194 | return link(name, filepath); 195 | }); 196 | } 197 | 198 | function listItem(file) { 199 | return '- **' + link(file.stem, '#' + file.stem) + '** (code ' + link(file.stem, file.relative) + ')'; 200 | } 201 | 202 | function codeLink(title, path, start) { 203 | return link(title, path + '#L' + start); 204 | } 205 | 206 | --------------------------------------------------------------------------------