UNPKG

snakeskin

Version:

Компилятор блочных шаблонов c поддержкой наследования.

520 lines (396 loc) 11.8 kB
//#!/usr/bin/env node global.Snakeskin = require('../snakeskin'); var program = require('commander'), beautify = require('js-beautify'), monocle = require('monocle')(); var path = require('path'), fs = require('fs'); program .version(Snakeskin.VERSION.join('.')) .usage('[options] [dir|file ...]') .option('-p, --params [src]', 'path to the options file or JS options object') .option('-s, --source [src]', 'path to the template file or directory') .option('-f, --file [src]', 'path to the template file (meta-information)') .option('-m, --mask [mask]', 'mask for a template files (RegExp)') .option('-w, --watch', 'watch files for changes and automatically re-render') .option('-o, --output [src]', 'path to the file to save') .option('--extname [ext]', 'file extension for saving (if "output" is a directory)') .option('--exports [type]', 'export type') .option('-e, --exec', 'execute compiled template') .option('-d, --data [src]', 'path to the data file or JS data object') .option('-t, --tpl [name]', 'name of the main template') .option('--disable-localization', 'disable support for localization') .option('--i18n-fn [name]', 'i18n function name') .option('--language [src]', 'path to the localization file or localization object') .option('--words [src]', 'path to the localization file to save') .option('--bem-filter [name]', 'bem filter name') .option('--line-separator [char]', 'newline character (eol)') .option('--tolerate-whitespace', 'tolerate whitespace characters in the template') .option('--ignore', 'regular expression to ignore the empty space') .option('--auto-replace', 'enable macros support') .option('--macros [src]', 'path to the macros file or JS macros object') .option('--doctype [type]', 'xml doctype (html or xml)') .option('--inline-iterators', 'inline forEach and forIn') .option('--disable-escape-output', 'disable default "html" filter') .option('--disable-replace-undef', 'disable default "undef" filter') .option('--render-as [mode]', 'render all templates as "interface" or "placeholder"') .option('--render-mode [mode]', 'render all templates in "stringConcat", "stringBuffer" or "dom"') .option('--pretty-print', 'formatting output') .parse(process.argv); var params = Snakeskin.toObj(program['params']) || {}; if (params instanceof Object === false) { params = {}; } params.doctype = 'doctype' in program ? program['doctype'] : params.doctype; params.exports = 'exports' in program ? program['exports'] : params.exports; params.localization = 'disableLocalization' in program ? !program['disableLocalization'] : params.localization; params.i18nFn = 'i18nFn' in program ? program['i18nFn'] : params.i18nFn; params.language = 'language' in program ? program['language'] : params.language; params.words = 'words' in program ? program['words'] : params.words; params.renderAs = 'renderAs' in program ? program['renderAs'] : params.renderAs; params.renderMode = 'renderMode' in program ? program['renderMode'] : params.renderMode; params.inlineIterators = 'inlineIterators' in program ? program['inlineIterators'] : params.inlineIterators; params.replaceUndef = 'disableReplaceUndef' in program ? !program['disableReplaceUndef'] : params.replaceUndef; params.escapeOutput = 'disableEscapeOutput' in program ? !program['disableEscapeOutput'] : params.escapeOutput; params.prettyPrint = 'prettyPrint' in program ? program['prettyPrint'] : params.prettyPrint; params.bemFilter = 'bemFilter' in program ? program['bemFilter'] : params.bemFilter; params.lineSeparator = 'lineSeparator' in program ? program['lineSeparator'] : params.lineSeparator; params.tolerateWhitespace = 'tolerateWhitespace' in program ? program['tolerateWhitespace'] : params.tolerateWhitespace; params.ignore = 'ignore' in program ? program['ignore'] : params.ignore; params.autoReplace = 'autoReplace' in program ? program['autoReplace'] : params.autoReplace; params.macros = 'macros' in program ? program['macros'] : params.macros; for (var key in params) { if (!params.hasOwnProperty(key)) { continue; } var el = params[key]; switch (el) { case 'true': el = true; break; case 'false': el = false; break; case 'null': el = null; break; default: if (typeof el === 'string') { var nm = Number(el); el = isNaN(nm) ? el : nm; } } params[key] = el; } params.debug = {}; params.cache = false; var include = {}, fMap = {}, watch = program['watch']; var prettyPrint = params.prettyPrint, language = params.language, macros = params.macros; var exec = program['exec'], tplData = program['data'], mainTpl = program['tpl']; var words = params.words; if (words) { params.words = {}; } var args = program['args'], input; var file = program['source'], out = program['output']; if (!file && args.length) { input = args.join(' '); if (fs.existsSync(input)) { file = input; input = false; } } var calls = {}; function testDir(src) { src = path.normalize(path.resolve(src)); (path.extname(src) ? path.dirname(src) : src).split(path.sep).forEach(function (el, i, data) { var src = data.slice(0, i + 1).join(path.sep); if (!fs.existsSync(src)) { fs.mkdirSync(src); } }); } function action(data, file) { console.time('Time'); file = file || program['file'] || ''; var tpls = {}, fileName = ''; if (file) { fileName = path.basename(file, path.extname(file)); } if (tplData || mainTpl || exec) { params.context = tpls; params.prettyPrint = false; } function pathTpl(src) { return src .replace(/%fileDir%/g, path.dirname(file)) .replace(/%fileName%/g, fileName) .replace(/%file%/g, path.basename(file)) .replace(/%filePath%/g, file); } function load(val) { val = pathTpl(val); var tmp = val; val = path.normalize( path.resolve(val) ); if (fs.existsSync(val) && fileName && fs.statSync(val).isDirectory()) { tmp = path.join(val, fileName) + '.js'; if (!fs.existsSync(tmp)) { tmp += 'on'; } } return Snakeskin.toObj(tmp, null, function (src) { if (file) { include[src] = include[src] || {}; include[src][file] = true; } }); } if (language) { params.language = load(language); } if (macros) { params.macros = load(macros); } function line() { console.log(new Array(80).join('~')); } function success() { line(); console.log('File "' + file + '" has been successfully compiled "' + outFile + '".'); console.timeEnd('Time'); line(); } var outFile = out, execTpl = tplData || mainTpl || exec; if (outFile) { outFile = path.normalize(path.resolve(pathTpl(outFile))); testDir(outFile); if (fs.existsSync(outFile) && fs.statSync(outFile).isDirectory()) { outFile = path.join(outFile, path.basename(file)) + (program['extname'] || (execTpl ? '.html' : '.js')); } if (file && (!words || fs.existsSync(words)) && params.cache !== false) { var includes = Snakeskin.check(file, outFile, Snakeskin.compile(null, params, null, {cacheKey: true}), true); if (includes) { success(); include[file] = include[file] || {}; include[file][file] = true; includes.forEach(function (key) { include[key] = include[key] || {}; include[key][file] = true; }); return; } } } var res = Snakeskin.compile( String(data), params, {file: file} ); var toConsole = input && !program['output'] || !outFile; if (res !== false) { if (execTpl) { var tpl; if (mainTpl && mainTpl !== true) { tpl = tpls[mainTpl]; } else { if (file) { tpl = tpls[fileName] || tpls.main || tpls[Object.keys(tpls)[0]]; } else { tpl = tpls.main || tpls[Object.keys(tpls)[0]]; } } if (!tpl) { console.error('Template to run is not defined'); res = ''; if (!watch) { process.exit(1); } } else { var dataObj, cache; if (tplData && tplData !== true) { dataObj = load(tplData); } cache = res = tpl(dataObj); if (prettyPrint) { if (toConsole) { res = beautify['html'](res); } else { res = (beautify[path.extname(outFile).replace(/^\./, '')] || beautify['html'])(res); } if (!res || !res.trim()) { res = cache; } } } } if (toConsole) { console.log(res); } else { fs.writeFileSync(outFile, res); success(); var tmp = params.debug.files; include[file] = include[file] || {}; include[file][file] = true; if (tmp) { for (var key in tmp) { if (!tmp.hasOwnProperty(key)) { continue; } include[key] = include[key] || {}; include[key][file] = true; } } } } else { if (!watch) { process.exit(1); } } } function end() { if (words) { testDir(words); fs.writeFileSync(words, JSON.stringify(params.words, null, '\t')); } } if (!file && input == null) { var buf = ''; var stdin = process.stdin, stdout = process.stdout; stdin.setEncoding('utf8'); stdin.on('data', function (chunk) { buf += chunk; }); stdin.on('end', function () { action(buf); end(); }).resume(); var nl = params.lineSeparator || '\n'; process.on('SIGINT', function () { stdout.write(nl); stdin.emit('end'); stdout.write(nl); process.exit(); }); } else { if (file) { file = path.normalize(path.resolve(file)); var isDir = fs.statSync(file).isDirectory(), mask = program['mask']; mask = mask && new RegExp(mask); var watchDir = function () { monocle.watchDirectory({ root: file, listener: function (f) { var src = f.fullPath; if (!fMap[src] && fs.existsSync(src) && !f.stat.isDirectory() && (mask ? mask.test(src) : path.extname(src) === '.ss') ) { monocle.unwatchAll(); action(fs.readFileSync(src), src); wacthFiles(); } } }); }; var wacthFiles = function () { var files = []; for (var key in include) { if (!include.hasOwnProperty(key)) { continue; } fMap[key] = true; files.push(key); } monocle.watchFiles({ files: files, listener: function (f) { var src = f.fullPath, files = include[src]; if (files && !calls[src]) { calls[src] = setTimeout(function () { monocle.unwatchAll(); for (var key in files) { if (!files.hasOwnProperty(key)) { continue; } if ((!mask || mask.test(key))) { if (fs.existsSync(key)) { action(fs.readFileSync(key), key); } else { delete include[key]; delete fMap[key]; } } } delete calls[src]; wacthFiles(); }, 60); } end(); } }); if (isDir) { watchDir(); } }; if (fs.statSync(file).isDirectory()) { var renderDir = function (dir) { fs.readdirSync(dir).forEach(function (el) { var src = path.join(dir, el); if (fs.statSync(src).isDirectory()) { renderDir(src); } else if (mask ? mask.test(src) : path.extname(el) === '.ss') { action(fs.readFileSync(src), src); } }); }; renderDir(file); } else if (!mask || mask.test(file)) { action(fs.readFileSync(file), file); } if (watch) { wacthFiles(); } } else { action(input); } end(); }