Skip to content

Commit 2baba45

Browse files
committed
#123: add first ejs helpers
1 parent 449b273 commit 2baba45

File tree

10 files changed

+201
-0
lines changed

10 files changed

+201
-0
lines changed

app.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ if (!commander.watch) {
7676

7777
/* App config */
7878

79+
require('./core/ejsHelpers');
80+
7981
// Version
8082
app.use(function (req, res, next) {
8183
res.header('X-powered-by', 'SourceJS ' + global.engineVersion);

core/ejsHelpers.js

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
'use strict';
2+
3+
var ejs = require('ejs');
4+
var fs = require('fs');
5+
var glob = require('glob');
6+
var path = require('path');
7+
var _ = require('lodash');
8+
var processMd = require(path.join(global.pathToApp,'core/lib/processMd'));
9+
10+
var originalRenderer = ejs.render;
11+
12+
var EJS_OPTS = ['cache', 'filename', 'sandbox', 'delimiter', 'scope', 'context', 'debug', 'compileDebug', 'client', '_with', 'rmWhitespace'
13+
];
14+
15+
/**
16+
* Copy properties in data object that are recognized as options to an
17+
* options object.
18+
*
19+
* This is used for compatibility with earlier versions of EJS and Express.js.
20+
*
21+
* @memberof module:ejs-internal
22+
* @param {Object} data data object
23+
* @param {Options} opts options object
24+
* @static
25+
*/
26+
27+
var cpOptsInData = function (data, opts) {
28+
EJS_OPTS.forEach(function (p) {
29+
if (typeof data[p] !== 'undefined') {
30+
opts[p] = data[p];
31+
}
32+
});
33+
};
34+
35+
var readFile = function (filePath, options) {
36+
if (options.sandbox && path.relative(options.sandbox, filePath).substring(0, 2) === '..') {
37+
throw new Error('reading files beyond sandbox is restricted, limit set to ' + options.sandbox);
38+
}
39+
40+
return fs.readFileSync(filePath, 'utf-8');
41+
};
42+
43+
var includeMD = module.exports.includeMD = function(data, options){
44+
return function(mdPath){
45+
if (!mdPath) return '';
46+
47+
var origionalFilename = options.filename;
48+
49+
if (!origionalFilename) throw new Error('`includeMD` requires the \'filename\' option.');
50+
51+
var fileToInclude = path.extname(mdPath) === '.md' ? mdPath : mdPath + '.md';
52+
var filePath = path.join(path.dirname(origionalFilename), fileToInclude);
53+
var fileContents = readFile(filePath, options);
54+
55+
options.filename = filePath;
56+
57+
var processedContents = ejs.render(fileContents, data, options);
58+
var html = processMd(processedContents);
59+
60+
// Reset filename options on return
61+
options.filename = origionalFilename;
62+
63+
return ejs.render(html, data, options);
64+
};
65+
};
66+
67+
var includeFiles = module.exports.includeFiles = function(data, options){
68+
return function(pattern){
69+
if (!pattern) return '';
70+
71+
var filename = options.filename;
72+
var output = '';
73+
74+
if (!filename) throw new Error('`includeFiles` requires the \'filename\' option.');
75+
76+
var filesToInclude = glob.sync(pattern, {
77+
cwd: path.dirname(filename),
78+
root: global.pathToApp,
79+
realpath: true
80+
});
81+
82+
filesToInclude.forEach(function(filePath){
83+
_.assign(options, {
84+
filename: filePath
85+
});
86+
87+
output += ejs.render(readFile(filePath, options), data, options);
88+
});
89+
90+
// Reset filename options on return
91+
_.assign(options, {
92+
filename: filename
93+
});
94+
95+
return output;
96+
};
97+
};
98+
99+
ejs.render = function(template, data, options){
100+
data = data || {};
101+
options = options || {};
102+
103+
// No options object -- if there are optiony names
104+
// in the data, copy them to options
105+
if (arguments.length == 2) {
106+
cpOptsInData(data, options);
107+
}
108+
109+
_.assign(data, {
110+
includeMD: includeMD(data, options),
111+
includeFiles: includeFiles(data, options)
112+
});
113+
114+
return originalRenderer(template, data, options);
115+
};

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"flat": "^1.2.1",
3030
"fs-extra": "~0.11.1",
3131
"fs-finder": "operatino/node-fs-finder#fix-windows-find-up",
32+
"glob": "^5.0.14",
3233
"grunt": "~0.4.5",
3334
"grunt-autoprefixer": "~1.0.0",
3435
"grunt-cli": "^0.1.13",
@@ -64,11 +65,14 @@
6465
"build": "npm i",
6566
"start": "node app",
6667
"test": "grunt ci-pre-run && node app.js --test",
68+
"test:unit": "grunt test",
69+
"test:func": "grunt test-func",
6770
"ci-test": "grunt ci-pre-run && node app.js --test --log trace",
6871
"ci-test-win": "grunt ci-pre-run && node app.js --post-grunt ci-post-run-win --test --log trace --no-watch"
6972
},
7073
"devDependencies": {
7174
"assert": "~1.1.1",
75+
"chai": "^3.2.0",
7276
"cross-spawn": "^0.4.0",
7377
"grunt-casperjs": "^2.1.0",
7478
"grunt-mocha-test": "^0.12.4",

test/unit/ejsHelpersSpec.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
var path = require('path');
2+
var ejs = require('ejs');
3+
var expect = require('chai').expect;
4+
5+
var pathToMasterApp = path.resolve('./');
6+
var loadOptions = require(path.join(pathToMasterApp, 'core/loadOptions'));
7+
global.opts = loadOptions(path.resolve(pathToMasterApp));
8+
9+
require(path.join(pathToMasterApp, 'core/ejsHelpers'));
10+
11+
describe('includeMD', function () {
12+
it('should properly include Markdown file', function (done) {
13+
var result = ejs.render('<%- includeMD("partials/markdown") %>', {
14+
filename: __filename
15+
});
16+
17+
expect(result).to.equal('<p>Hello <strong>world</strong>!</p>\n');
18+
19+
done();
20+
});
21+
22+
it('should properly include Markdown file with EJS includes', function (done) {
23+
var result = ejs.render('<%- includeMD("partials/markdown-ejs") %>', {
24+
world: 'world'
25+
}, {
26+
filename: __filename
27+
});
28+
29+
expect(result).to.equal('<p>Hello <strong>world</strong>!</p>\n');
30+
31+
done();
32+
});
33+
34+
it('should properly include Markdown with nested include', function (done) {
35+
var result = ejs.render('<%- includeMD("partials/markdown-ejs-nested") %>', {
36+
world: 'world'
37+
}, {
38+
filename: __filename
39+
});
40+
41+
expect(result).to.contain('<p>Nested:</p><p>Hello <strong>world</strong>!</p>');
42+
43+
done();
44+
});
45+
});
46+
47+
describe('includeFiles', function () {
48+
it('should include one file by glob', function (done) {
49+
var result = ejs.render('<%- includeFiles("partials/mask-one.html") %>', {
50+
filename: __filename
51+
});
52+
53+
expect(result).to.equal('one<br>');
54+
55+
done();
56+
});
57+
58+
it('should include multiple files by glob', function (done) {
59+
var result = ejs.render('<%- includeFiles("partials/mask-*.html") %>', {
60+
filename: __filename
61+
});
62+
63+
expect(result).to.contain('one');
64+
expect(result).to.contain('two');
65+
expect(result).to.contain('three');
66+
expect(result).to.contain('Hello');
67+
68+
done();
69+
});
70+
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Hello **<%= world %>**!
2+
3+
Nested:<%- includeMD('markdown.md') %>

test/unit/partials/markdown-ejs.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Hello **<%= world %>**!

test/unit/partials/markdown.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Hello **world**!

test/unit/partials/mask-one.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
one<br>

test/unit/partials/mask-two.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
two<br>
2+
<%- includeFiles('three.html') %>

test/unit/partials/three.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
three<br>
2+
<%- includeMD('markdown.md') %>

0 commit comments

Comments
 (0)