|
- #!/usr/bin/env node
-
- var fs = require('fs'),
- path = require('path');
-
- var Mustache = require('..');
- var pkg = require('../package');
- var partials = {};
-
- var partialsPaths = [];
- var partialArgIndex = -1;
-
- while ((partialArgIndex = process.argv.indexOf('-p')) > -1) {
- partialsPaths.push(process.argv.splice(partialArgIndex, 2)[1]);
- }
-
- var viewArg = process.argv[2];
- var templateArg = process.argv[3];
- var outputArg = process.argv[4];
-
- if (hasVersionArg()) {
- return console.log(pkg.version);
- }
-
- if (!templateArg || !viewArg) {
- console.error('Syntax: mustache <view> <template> [output]');
- process.exit(1);
- }
-
- run(readPartials, 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 onDone (str) {
- cb(parseView(str));
- });
- }
-
- function parseView (str) {
- try {
- return JSON.parse(str);
- } catch (ex) {
- console.error(
- 'Shooot, could not parse view as JSON.\n' +
- 'Tips: functions are not valid JSON and keys / values must be surround with double quotes.\n\n' +
- ex.stack);
-
- process.exit(1);
- }
- }
-
- function readPartials (cb) {
- if (!partialsPaths.length) return cb();
- var partialPath = partialsPaths.pop();
- var partial = fs.createReadStream(partialPath);
- streamToStr(partial, function onDone (str) {
-
- var keysArray = getPartialNames(partialPath);
-
- keysArray.forEach(function addPartialNames (key) {
- partials[key] = str;
- });
-
- readPartials(cb);
- });
- }
-
- function readTemplate (cb) {
- var template = fs.createReadStream(templateArg);
- streamToStr(template, cb);
- }
-
- function render (cb, templateStr, jsonView) {
- cb(Mustache.render(templateStr, jsonView, partials));
- }
-
- function toStdout (cb, str) {
- if (outputArg) {
- cb(fs.writeFileSync(outputArg, str));
- } else {
- cb(process.stdout.write(str));
- }
- }
-
- function streamToStr (stream, cb) {
- var data = '';
-
- stream.on('data', function onData (chunk) {
- data += chunk;
- }).once('end', function onEnd () {
- cb(data.toString());
- }).on('error', function onError (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 (view) {
- return view === '-';
- }
-
- function wasNotFound (err) {
- return err.code && err.code === 'ENOENT';
- }
-
- function hasVersionArg () {
- return ['--version', '-v'].some(function matchInArgs (opt) {
- return process.argv.indexOf(opt) > -1;
- });
- }
-
- function getPartialNames (filename) {
- // get path relative to the template file
- // in order to use e.g. {{> ../common/footer }}
-
- // before, {{> footer }} used the -p file whose streamToStr happened to
- // finish first, as seen above in readPartials
-
- // this can be extended to the mustache API by using the relative path as
- // the key in the partials object
- // e.g. mustache.render(template, view, { '../common/footer': '...' })
- var relativePath = path
- .relative(templateArg, filename)
- // without this, you would have to use {{> ..\\common\\footer }} for windows
- // and {{> ../common/footer }} for *nix
- .replace(/\\{1,2}/g, '/')
- // since path.relative shows paths relative to the file, and not the
- // directory (i.e. what everyone is used to), change the reference to the
- // current directory from '../' to './'
- .replace(/^\.\.\//, './')
- // obviously if we want parents of the current directory, we want '../../'
- // as opposed to './../../', so this replace fixes that
- .replace(/^\.\/\.\.\//, '../')
- .replace('.mustache', '');
-
- return [
- path.basename(filename, '.mustache'),
- relativePath
- ];
- }
|