Skip to content

Commit

Permalink
Merge branch 'master' of github.com:mde/ejs
Browse files Browse the repository at this point in the history
mde committed Nov 24, 2019
2 parents 7c15b78 + 1fe67b4 commit 18c03ab
Showing 11 changed files with 190 additions and 59 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -4,4 +4,5 @@ node_js:
- "4"
- "6"
- "8"
- "9"
- "10"
- "12"
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ Embedded JavaScript templates

[![Build Status](https://img.shields.io/travis/mde/ejs/master.svg?style=flat)](https://travis-ci.org/mde/ejs)
[![Developing Dependencies](https://img.shields.io/david/dev/mde/ejs.svg?style=flat)](https://david-dm.org/mde/ejs?type=dev)
[![Known Vulnerabilities](https://snyk.io/test/npm/ejs/badge.svg?style=flat-square)](https://snyk.io/test/npm/ejs)
[![Known Vulnerabilities](https://snyk.io/test/npm/ejs/badge.svg?style=flat)](https://snyk.io/test/npm/ejs)

## Installation

@@ -28,7 +28,7 @@ $ npm install ejs

## Example

```html
```ejs
<% if (user) { %>
<h2><%= user.name %></h2>
<% } %>
@@ -73,6 +73,8 @@ Therefore, we do not recommend using this shortcut.
- `strict` When set to `true`, generated function is in strict mode
- `_with` Whether or not to use `with() {}` constructs. If `false`
then the locals will be stored in the `locals` object. Set to `false` in strict mode.
- `destructuredLocals` An array of local variables that are always destructured from
the locals object, available even in strict mode.
- `localsName` Name to use for the object storing local variables when not using
`with` Defaults to `locals`
- `rmWhitespace` Remove all safe-to-remove whitespace, including leading
@@ -120,7 +122,7 @@ call unless you are using `renderFile()`.
You'll likely want to use the raw output tag (`<%-`) with your include to avoid
double-escaping the HTML output.

```html
```ejs
<ul>
<% users.forEach(function(user){ %>
<%- include('user/show', {user: user}) %>
@@ -191,7 +193,7 @@ EJS does not specifically support blocks, but layouts can be implemented by
including headers and footers, like so:


```html
```ejs
<%- include('header') -%>
<h1>
Title
3 changes: 2 additions & 1 deletion benchmark/bench-ejs.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
'use strict';

var ejs = require('..');
var path = require('path');

ejs.fileLoader = function(n) { return files[n.replace(/^\//, '').replace(/\.ejs$/, '')]; };
ejs.fileLoader = function(n) { return files[path.basename(n, '.ejs')]; };

var loops = 10000;
var runs = 9; // min 4 for median
16 changes: 16 additions & 0 deletions docs/jsdoc/options.jsdoc
Original file line number Diff line number Diff line change
@@ -17,6 +17,14 @@
* whose name is specified by {@link module:ejs.localsName} (default to
* `locals`).
*
* @property {Boolean} [strict=false]
* Whether to run in strict mode or not.
* Enforces `_with=false`.
*
* @property {String[]} [destructuredLocals=[]]
* An array of local variables that are always destructured from {@link module:ejs.localsName},
* available even in strict mode.
*
* @property {Boolean} [rmWhitespace=false]
* Remove all safe-to-remove whitespace, including leading and trailing
* whitespace. It also enables a safer version of `-%>` line slurping for all
@@ -71,6 +79,14 @@
* Whether or not to create an async function instead of a regular function.
* This requires language support.
*
* @property {boolean} [legacyInclude=true]
* Whether to enable legacy preprocessor include directives.
*
* **Example:**
* ```ejs
* <%- include foo %>
* ```
*
* @static
* @global
*/
34 changes: 17 additions & 17 deletions docs/syntax.md
Original file line number Diff line number Diff line change
@@ -65,7 +65,7 @@ The content of the tag can be any valid JavaScript operators, so tags like

##### EJS

```html
```ejs
<p>Hello, <%= name %>.</p>
<p>Hello, <%= 'the Most Honorable ' + name %>.</p>
```
@@ -97,7 +97,7 @@ cross-site scripting (XSS) attacks.

##### EJS

```html
```ejs
<p>Hello, <%- myHtml %>.</p>
<p>Hello, <%= myHtml %>.</p>
@@ -138,7 +138,7 @@ example below. You can trim it using the `-%>` ending tag.

##### EJS

```html
```ejs
<div>
<%# comment %>
</div>
@@ -171,7 +171,7 @@ All types of JavaScript comments are allowed, although it is preferable to use
the `<%#` tag for comments. For example, the following three code blocks are
equivalent, though `<%#` is the shortest.

```js
```ejs
<%# comment %>
<%/* comment */%>
<%// comment %>
@@ -185,7 +185,7 @@ statements, but the behavior is undefined and subject to change.

It is not necessary to use curly brackets for scriptlet-only code.

```html
```ejs
<%# Bad practice %>
<% if (true) %>
<p>Yay it's true!</p>
@@ -196,7 +196,7 @@ It is not necessary to use curly brackets for scriptlet-only code.
<% } %>
```

```js
```ejs
<%# These are all valid statements %>
<% var output
, exclamation = ''
@@ -223,15 +223,15 @@ Line breaks are allowed in `<%` tags.
Unless the statement involves mixing EJS and JavaScript scriptlet, always put
complete statements in a tag. For example, the following works:

```js
```ejs
<% var stringToShow = thisIsABooleanVariableWithAVeryLongName
? 'OK'
: 'not OK' %>
```

While the following does not:

```js
```ejs
<% var stringToShow = thisIsABooleanVariableWithAVeryLongName %>
<% ? 'OK' %>
<% : 'not OK' %>
@@ -258,7 +258,7 @@ It does *not* mean that we recommend mixing coding styles in your own project.

##### EJS

```html
```ejs
<dl>
<%for (var i = 0; i < users.length; i++) { %><%
var user = users[i]
@@ -314,7 +314,7 @@ This tag is the same as a Scriptlet, except that it removes all whitespace befor

##### EJS

```html
```ejs
<ul>
<% users.forEach(function(user, i, arr){ -%>
<li><%= user %></li>
@@ -362,7 +362,7 @@ not have any effect on output tags.

##### EJS

```html
```ejs
Beginning of template
<% 'this is a statement'
+ ' that is long'
@@ -412,7 +412,7 @@ escape `<` or `>` at all.

##### EJS

```html
```ejs
<pre>This is literal: <%%</pre>
<pre>This is literal too: <%% %></pre>
<pre>This is literal as well: %%></pre>
@@ -461,13 +461,13 @@ directive, as it trims the whitespace after the included file.

##### included.ejs

```html
```ejs
<li><%= pet.name %></li>
```

##### main.ejs

```html
```ejs
<ul>
<% pets.forEach(function (pet) { -%>
<% include included %>
@@ -495,7 +495,7 @@ directive, as it trims the whitespace after the included file.

##### “Preprocessor" output

```js
```ejs
<ul>
<% pets.forEach(function (pet) { -%>
<li><%= pet.name %></li>
@@ -542,13 +542,13 @@ This has the exact same effect as the example for the `include` directive.

##### included.ejs

```html
```ejs
<li><%= pet.name %></li>
```

##### main.ejs

```html
```ejs
<ul>
<% pets.forEach(function (pet) { -%>
<%- include('included', {
10 changes: 7 additions & 3 deletions Jakefile → jakefile.js
Original file line number Diff line number Diff line change
@@ -18,8 +18,8 @@ task('clean', ['clobber'], function () {
});

desc('Lints the source code');
task('lint', function () {
exec('./node_modules/.bin/eslint "**/*.js" Jakefile');
task('lint', ['clean'], function () {
exec('./node_modules/.bin/eslint "**/*.js"');
console.log('Linting completed.');
});

@@ -33,30 +33,34 @@ task('minify', function () {
console.log('Minification completed.');
});

desc('Generates the EJS API docs');
task('doc', function (dev) {
jake.rmRf('out');
var p = dev ? '-p' : '';
exec('./node_modules/.bin/jsdoc ' + p + ' -c jsdoc.json lib/* docs/jsdoc/*');
console.log('Documentation generated.');
});

desc('Publishes the EJS API docs');
task('docPublish', ['doc'], function () {
fs.writeFileSync('out/CNAME', 'api.ejs.co');
console.log('Pushing docs to gh-pages...');
exec('./node_modules/.bin/git-directory-deploy --directory out/');
console.log('Docs published to gh-pages.');
});

desc('Runs the EJS test suite');
task('test', ['lint'], function () {
exec('./node_modules/.bin/mocha');
});

publishTask('ejs', ['build'], function () {
this.packageFiles.include([
'Jakefile',
'jakefile.js',
'README.md',
'LICENSE',
'package.json',
'postinstall.js',
'ejs.js',
'ejs.min.js',
'lib/**'
48 changes: 37 additions & 11 deletions lib/ejs.js
Original file line number Diff line number Diff line change
@@ -526,6 +526,8 @@ function Template(text, opts) {
options.localsName = opts.localsName || exports.localsName || _DEFAULT_LOCALS_NAME;
options.views = opts.views;
options.async = opts.async;
options.destructuredLocals = opts.destructuredLocals;
options.legacyInclude = typeof opts.legacyInclude != 'undefined' ? !!opts.legacyInclude : true;

if (options.strict) {
options._with = false;
@@ -570,15 +572,28 @@ Template.prototype = {

if (!this.source) {
this.generateSource();
prepended += ' var __output = [], __append = __output.push.bind(__output);' + '\n';
prepended +=
' var __output = "";\n' +
' function __append(s) { if (s !== undefined && s !== null) __output += s }\n';
if (opts.outputFunctionName) {
prepended += ' var ' + opts.outputFunctionName + ' = __append;' + '\n';
}
if (opts.destructuredLocals && opts.destructuredLocals.length) {
var destructuring = ' var __locals = (' + opts.localsName + ' || {}),\n';
for (var i = 0; i < opts.destructuredLocals.length; i++) {
var name = opts.destructuredLocals[i];
if (i > 0) {
destructuring += ',\n ';
}
destructuring += name + ' = __locals.' + name;
}
prepended += destructuring + ';\n';
}
if (opts._with !== false) {
prepended += ' with (' + opts.localsName + ' || {}) {' + '\n';
appended += ' }' + '\n';
}
appended += ' return __output.join("");' + '\n';
appended += ' return __output;' + '\n';
this.source = prepended + this.source + appended;
}

@@ -610,6 +625,10 @@ Template.prototype = {
if (opts.debug) {
console.log(src);
}
if (opts.compileDebug && opts.filename) {
src = src + '\n'
+ '//# sourceURL=' + opts.filename + '\n';
}

try {
if (opts.async) {
@@ -641,23 +660,18 @@ Template.prototype = {
e.message += ' while compiling ejs\n\n';
e.message += 'If the above error is not helpful, you may want to try EJS-Lint:\n';
e.message += 'https://github.com/RyanZim/EJS-Lint';
if (!e.async) {
if (!opts.async) {
e.message += '\n';
e.message += 'Or, if you meant to create an async function, pass async: true as an option.';
e.message += 'Or, if you meant to create an async function, pass `async: true` as an option.';
}
}
throw e;
}

if (opts.client) {
fn.dependencies = this.dependencies;
return fn;
}

// Return a callable function which will execute the function
// created by the source-code, with the passed data as locals
// Adds a local `include` function which allows full recursive include
var returnedFn = function (data) {
var returnedFn = opts.client ? fn : function anonymous(data) {
var include = function (path, includeData) {
var d = utils.shallowCopy({}, data);
if (includeData) {
@@ -668,6 +682,18 @@ Template.prototype = {
return fn.apply(opts.context, [data || {}, escapeFn, include, rethrow]);
};
returnedFn.dependencies = this.dependencies;
if (opts.filename && typeof Object.defineProperty === 'function') {
var filename = opts.filename;
var basename = path.basename(filename, path.extname(filename));
try {
Object.defineProperty(returnedFn, 'name', {
value: basename,
writable: false,
enumerable: false,
configurable: true
});
} catch (e) {/* ignore */}
}
return returnedFn;
},

@@ -711,7 +737,7 @@ Template.prototype = {
}
}
// HACK: backward-compat `include` preprocessor directives
if ((include = line.match(/^\s*include\s+(\S+)/))) {
if (opts.legacyInclude && (include = line.match(/^\s*include\s+(\S+)/))) {
opening = matches[index - 1];
// Must be in EVAL or RAW mode
if (opening && (opening == o + d || opening == o + d + '-' || opening == o + d + '_')) {
Loading

0 comments on commit 18c03ab

Please sign in to comment.