microgen
Version:
micro-generator for individual files, easy like sunday morning
221 lines (176 loc) • 5.18 kB
JavaScript
;
const args = process.argv.slice(2);
if (args.length === 0 || args[0] === '-h' || args[0] === '--help') {
var usage = `
Usage: microgen <template-file> [output-file]
Options:
-h, --help show usage help
-p, --pipe pipe output to stdout, instead of writing to file
output-file if not specified, it will be $PWD/<template-file>
* if <template-file> has an ".hbs" extension,
it will be removed from the output-file name
Version: ${require('./package.json').version}
`;
console.log(usage);
process.exit(1);
}
var Handlebars = require('handlebars');
var fs = require('fs-extra');
var path = require('path');
var readline = require('readline');
var templateFile = args[0];
var templateFileBasename = path.basename(templateFile, '.hbs');
var outputFile = args[1] || path.join(process.cwd(), templateFileBasename);
var basename = path.basename(process.cwd());
var template;
try {
template = fs.readFileSync(templateFile, 'utf8');
} catch (error) {
console.log(`\n Error: there was a problem reading ${templateFile}`);
process.exit(1);
}
var ast = Handlebars.parse(template).body;
var prompts = {};
var answers = {};
(function prepare(ast, parent) {
var id;
ast.forEach(statement => {
switch (statement.type) {
case 'CommentStatement':
id = `comment-${Math.random().toString(16).slice(2)}`;
if (parent[id]) {
return;
}
parent[id] = {
type: 'comment',
value: statement.value
};
break;
case 'MustacheStatement':
id = statement.path.original;
if (parent[id]) {
return;
}
parent[id] = {
type: 'string'
};
break;
case 'BlockStatement':
id = statement.path.original;
if (!parent[id]) {
parent[id] = {
type: 'boolean'
};
}
if (statement.program.body) {
prepare(statement.program.body, parent[id]);
}
if (statement.inverse && typeof statement.inverse === 'object') {
parent[id].inverse = {};
prepare(statement.inverse.body, parent[id].inverse);
}
break;
default:
break;
}
});
})(ast, prompts);
if (prompts.basename && prompts.basename.type === 'string') {
prompts.basename.value = basename;
}
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
console.log('');
var ids = Object.keys(prompts);
(function ask(ids, parent, depth, callback) {
let id = ids.shift();
if (!id) {
return callback(null);
}
// ignore prompt if already answered
if (Object.prototype.hasOwnProperty.call(answers, id)) {
return ask(ids, parent, depth, callback);
}
// This is just for display
var indent = '';
var _depth = 0;
while (_depth < depth) {
indent += ' ';
_depth++;
}
var variable = parent[id];
var defaultValue;
var defaultChoices;
switch (variable.type) {
case 'boolean':
defaultChoices = ' (Y/n)';
break;
case 'comment':
console.log(`${indent}${variable.value}`);
return ask(ids, parent, depth, callback);
default: // String
defaultValue = variable.value ? `${variable.value}` : '';
defaultChoices = defaultValue.length ? ` (${defaultValue})` : '';
break;
}
rl.question(`${indent}${id}${defaultChoices}: `, answer => {
answer = answer.trim();
switch (variable.type) {
case 'boolean':
var dig;
dig = answer !== 'n';
answers[id] = dig;
var digIds;
if (dig) {
digIds = (Object.keys(variable)).filter(value => {
return value !== 'type' && value !== 'value' && value !== 'inverse';
});
depth++;
return ask(digIds, variable, depth, error => {
if (error) {
throw error;
}
depth--;
ask(ids, parent, depth, callback);
});
}
// Inverse / else clause
if (variable.inverse && typeof variable.inverse === 'object' && Object.keys(variable.inverse).length) {
digIds = (Object.keys(variable.inverse)).filter(value => {
return value !== 'type' && value !== 'value';
});
depth++;
return ask(digIds, variable.inverse, depth, error => {
if (error) {
throw error;
}
depth--;
ask(ids, parent, depth, callback);
});
}
ask(ids, parent, depth, callback);
break;
case 'string':
answers[id] = answer.length ? answer : defaultValue;
ask(ids, parent, depth, callback);
break;
default:
break;
}
});
})(ids, prompts, 0, error => {
if (error) {
throw error;
}
rl.on('close', () => {
var result = Handlebars.compile(template)(answers);
if (outputFile === '-p' || outputFile === '--pipe') {
return console.log(result);
}
fs.outputFileSync(outputFile, result, 'utf8');
});
rl.close();
});