ractive
Version:
Next-generation DOM manipulation
317 lines (275 loc) • 8.21 kB
JavaScript
/* eslint-env node */
/* eslint-disable no-console */
const path = require('path');
const fs = require('fs');
const util = require('../lib/util');
const parse = require('../lib/parse');
const component = require('../lib/component');
const args = process.argv.slice();
if (~args[0].indexOf('node')) args.shift();
args.shift(); // program
const cmd = args.shift();
const opts = {
output: process.stdout,
base: process.cwd()
};
function mkReadFile(file) {
const base = path.isAbsolute(file)
? path.dirname(file)
: path.join(opts.base, path.dirname(file));
return function readFile(names) {
if (Array.isArray(names)) names = path.join.apply(path, names);
return Promise.resolve(
fs.readFileSync(path.isAbsolute(names) ? names : path.join(base, names), { encoding: 'utf8' })
);
};
}
function dirIndexOrFile(name) {
const stats = util.stat(name);
if (stats && stats.isDirectory()) {
opts.base = name;
if (util.stat(path.resolve(name, './index.ractive'))) {
opts.input = fs.createReadStream(path.resolve(name, './index.ractive'));
} else {
opts.input = fs.createReadStream(path.resolve(name, './index.html'));
}
} else {
opts.base = path.dirname(name);
opts.input = fs.createReadStream(name);
}
}
const commands = {
parse: {
run(opts) {
util.readToString(opts.input).then(
string => {
return parse.parse(string, opts).then(string => util.writeToStream(opts.output, string));
},
err => {
console.error(err.message);
process.exit(1);
}
);
},
help: parse.help,
args(list, opts) {
for (let i = 0; i < args.length; i++) {
if (args[i] === '-t' || args[i] === '--text-only') {
opts.textOnlyMode = true;
args.splice(i--, 1);
}
}
}
},
component: {
run(opts) {
if (opts.directory) {
const ext = new RegExp((opts.extension || '.ractive.html').replace('.', '\\.') + '$', 'i');
const files = util.lsr(opts.directory).filter(f => ext.test(f));
/* eslint-disable-next-line no-inner-declarations */
function step() {
const f = files.pop();
if (f) {
console.error('Ractive: compiling component ' + path.join(opts.directory, f));
util
.readToString(fs.createReadStream(path.join(opts.directory, f)))
.then(string => {
return component
.build(string, opts, mkReadFile(path.join(opts.directory, f)))
.then(string => {
const file = path.join(
opts.output,
f.replace(ext, opts.outputExtension || '.js')
);
util.mkdirp(path.dirname(file));
util.writeToStream(fs.createWriteStream(file), string);
});
})
.then(step, err => {
console.error(err && typeof err === 'object' ? err.stack : err);
process.exit(1);
});
}
}
step();
} else {
util
.readToString(opts.input)
.then(string => {
return component
.build(string, opts, mkReadFile(''))
.then(string => util.writeToStream(opts.output, string));
})
.then(null, err => {
console.error(err && typeof err === 'object' ? err.stack : err);
process.exit(1);
});
}
},
help: component.help
}
};
const config = {
addCommand(name, obj) {
if (!obj.help) console.warn(`Ractive: Command ${name} does not provide any help.`);
commands[name] = obj;
},
addStyleProcessor(fn) {
const list = opts.styleProcessors || (opts.styleProcessors = []);
list.push(fn);
},
addTemplateProcessor(fn) {
const list = opts.templateProcessors || (opts.templateProcessors = []);
list.push(fn);
},
addPartialProcessor(fn) {
const list = opts.partialProcessors || (opts.partialProcessors = []);
list.push(fn);
},
addScriptProcessor(fn) {
const list = opts.scriptProcessors || (opts.scriptProcessors = []);
list.push(fn);
},
Ractive: util.Ractive,
util,
parse,
component
};
function loadConfig() {
let dir = process.cwd();
let prev;
while (dir !== prev) {
const file = path.join(dir, '.ractive.config.js');
if (util.stat(file)) {
try {
const fn = require(file);
fn.call(config, config, util.Ractive);
} catch (e) {
console.error(`Ractive: Error while trying to read Ractive config file at ${dir}:`);
console.error(e);
process.exit(2);
}
break;
} else {
prev = dir;
dir = path.join(dir, '..');
}
}
}
// try to find a config file
loadConfig();
if (cmd === 'help') {
console.error(`Usage: ractive [command] [options] [file]
Available commands: ${Object.keys(commands)
.sort()
.join(', ')}
${Object.keys(commands)
.filter(c => commands[c].help)
.map(c => commands[c].help)
.join('')}`);
process.exit(1);
}
if (args[0] === 'help' || !cmd || !commands[cmd]) {
console.error(`Usage: ractive [command] [options] [file]
Available commands: ${Object.keys(commands)
.sort()
.join(', ')}`);
if (!cmd)
console.error(`\n Find out more about a particular command with ractive [command] help`);
if (cmd && !commands[cmd]) console.error(`\n Unknown command: ${cmd}`);
if (commands[cmd] && commands[cmd].help) console.error(commands[cmd].help);
process.exit(1);
}
// let the command process the args if it wants to
if (typeof commands[cmd].preArgs === 'function') commands[cmd].preArgs(args, opts);
// process out common args
for (let i = 0; i < args.length; i++) {
const arg = args[i];
switch (arg) {
case '-i':
case '--input':
dirIndexOrFile(args[i + 1]);
args.splice(i--, 2);
i--;
break;
case '-o':
case '--output':
opts.output = args[i + 1];
args.splice(i--, 2);
break;
case '-x':
case '--nocsp':
opts.csp = false;
args.splice(i--, 1);
break;
case '-d':
case '--delimiters':
opts.delimiters = [args[i + 1], args[i + 1]];
args.splice(i--, 3);
break;
case '-s':
case '--static':
opts.staticDelimiters = [args[i + 1], args[i + 1]];
args.splice(i--, 3);
break;
case '-r':
case '--triple':
opts.tripleDelimiters = [args[i + 1], args[i + 1]];
args.splice(i--, 3);
break;
case '-p':
case '--static-triple':
opts.staticTripleDelimiters = [args[i + 1], args[i + 1]];
args.splice(i--, 3);
break;
case '-u':
case '--escape-unicode':
opts.escapeUnicode = true;
args.splice(i--, 1);
break;
case '-id':
case '--input-dir':
opts.directory = args[i + 1];
args.splice(i--, 2);
break;
case '-e':
case '--extension':
opts.extension = args[i + 1];
args.splice(i--, 2);
break;
case '-oe':
case '-output-extension':
opts.outputExtension = args[i + 1];
args.splice(i--, 2);
break;
case '-ae':
case '--auto-export':
opts.autoExport = true;
args.splice(i--, 1);
break;
}
}
if (typeof commands[cmd].args === 'function') commands[cmd].args(args, opts);
if (args.length === 1 && util.stat(args[0])) {
// last arg may be the input file
opts.input = fs.createReadStream(args[0]);
} else if (args.length) {
console.error(`Ractive: Unknown arguments: ${args.join(' ')}`);
}
if (!opts.directory && !opts.input) {
try {
opts.input = process.stdin;
} catch (e) {
console.error(`Ractive: No input was provided.`);
process.exit(2);
}
}
if (!opts.directory && typeof opts.output === 'string') {
util.mkdirp(path.dirname(opts.output));
opts.output = fs.createWriteStream(opts.output);
} else if (opts.directory && typeof opts.output !== 'string') {
console.error(`Ractive: Directory transforms require an output directory`);
process.exit(2);
}
commands[cmd].run(opts);