Skip to content

Commit

Permalink
Merge pull request #425 from phillipj/feature/cli
Browse files Browse the repository at this point in the history
Created command line tool
  • Loading branch information
phillipj committed Feb 13, 2015
2 parents 7e49944 + 65387a5 commit 05d7146
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 0 deletions.
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,36 @@ These may be built using [Rake](http://rake.rubyforge.org/) and one of the follo
$ rake yui3
$ rake qooxdoo

## Command line tool

mustache.js is shipped with a node based command line tool. It might be installed as a global tool on your computer to render a mustache template of some kind

```bash
$ npm install -g mustache
$ mustache dataView.json myTemplate.mustache > output.html

# also supports stdin
$ cat dataView.json | mustache - myTemplate.mustache > output.html
```

or as a package.json `devDependency` in a build process maybe?

```bash
$ npm install mustache --save-dev
```
```json
{
"scripts": {
"build": "mustache dataView.json myTemplate.mustache > public/output.html"
}
}
```
```bash
$ npm run build
```

The command line tool is basically a wrapper around `Mustache.render` so you get all the aformentioned features.

## Testing

In order to run the tests you'll need to install [node](http://nodejs.org/).
Expand Down
89 changes: 89 additions & 0 deletions bin/mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!/usr/bin/env node

var fs = require('fs');

var Mustache = require('..');
var pkg = require('../package');

var viewArg = process.argv[2];
var templateArg = process.argv[3];

if (process.argv.indexOf('--version') > -1) {
return console.log(pkg.version);
}

if (!templateArg || !viewArg) {
console.error('Syntax: mustache <view> <template>');
process.exit(1);
}

run(readView, readTemplate, render, toStdout);

/**
* Runs a list of functions as a waterfall.
* Functions are runned one after the other in order, providing each
* function the returned values of all the previously invoked functions.
* Each function is expected to exit the process if an error occurs.
*/
function run(/*args*/) {
var values = [];
var fns = Array.prototype.slice.call(arguments);

function invokeNextFn(val) {
values.unshift(val);
if (fns.length == 0) return;
invoke(fns.shift());
}

function invoke(fn) {
fn.apply(null, [invokeNextFn].concat(values));
}

invoke(fns.shift());
}

function readView(cb) {
var view = isStdin(viewArg) ? process.openStdin() : fs.createReadStream(viewArg);

streamToStr(view, function(str) {
cb(JSON.parse(str));
});
}

function readTemplate(cb) {
var template = fs.createReadStream(templateArg);
streamToStr(template, cb);
}

function render(cb, templateStr, jsonView) {
cb(Mustache.render(templateStr, jsonView));
}

function toStdout(cb, str) {
cb(process.stdout.write(str));
}

function streamToStr(stream, cb) {
var data = '';

stream.on('data', function(chunk) {
data += chunk;
}).once('end', function() {
cb(data.toString());
}).on('error', function(err) {
if (wasNotFound(err)) {
console.error('Could not find file:', err.path);
} else {
console.error('Error while reading file:', err.message);
}
process.exit(1);
});
}

function isStdin(viewArg) {
return viewArg === '-';
}

function wasNotFound(err) {
return err.code && err.code === 'ENOENT';
}
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
"ejs"
],
"main": "./mustache.js",
"bin": {
"mustache": "./bin/mustache"
},
"volo": {
"url": "https://raw.github.com/janl/mustache.js/{version}/mustache.js"
},
Expand Down
3 changes: 3 additions & 0 deletions test/_files/cli.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "LeBron"
}
1 change: 1 addition & 0 deletions test/_files/cli.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Howdy {{name}}, CLI rox
1 change: 1 addition & 0 deletions test/_files/cli.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Howdy LeBron, CLI rox
70 changes: 70 additions & 0 deletions test/cli-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
require('./helper');

var fs = require('fs');
var path = require('path');
var _files = path.join(__dirname, '_files');
var cliTxt = path.resolve(_files, 'cli.txt');
var moduleVersion = require('../package').version;

var exec = require('child_process').exec;

describe('Mustache CLI', function () {

var expectedOutput;

before(function(done) {
fs.readFile(cliTxt, function onFsEnd(err, data) {
if (err) return done(err);

expectedOutput = data.toString();
done();
});
});

it('writes syntax hints into stderr when runned with wrong number of arguments', function(done) {
exec('bin/mustache', function(err, stdout, stderr) {
assert.notEqual(stderr.indexOf('Syntax'), -1);
done();
});
});

it('writes module version into stdout when runned with --version', function(done){
exec('bin/mustache --version', function(err, stdout, stderr) {
assert.notEqual(stdout.indexOf(moduleVersion), -1);
done();
});
});

it('writes rendered template into stdout when successfull', function(done) {
exec('bin/mustache test/_files/cli.json test/_files/cli.mustache', function(err, stdout, stderr) {
assert.equal(err, null);
assert.equal(stderr, '');
assert.equal(stdout, expectedOutput);
done();
});
});

it('reads view data from stdin when first argument equals "-"', function(done){
exec('cat test/_files/cli.json | bin/mustache - test/_files/cli.mustache', function(err, stdout, stderr) {
assert.equal(err, null);
assert.equal(stderr, '');
assert.equal(stdout, expectedOutput);
done();
});
});

it('writes it couldnt find template into stderr when second argument doesnt resolve to a file', function(done) {
exec('bin/mustache test/_files/cli.json test/_files/non-existing-template.mustache', function(err, stdout, stderr) {
assert.notEqual(stderr.indexOf('Could not find file: test/_files/non-existing-template.mustache'), -1);
done();
});
});

it('writes it couldnt find view into stderr when first argument doesnt resolve to a file', function(done) {
exec('bin/mustache test/_files/non-existing-view.json test/_files/cli.mustache', function(err, stdout, stderr) {
assert.notEqual(stderr.indexOf('Could not find file: test/_files/non-existing-view.json'), -1);
done();
});
});

});

0 comments on commit 05d7146

Please sign in to comment.