page-app
Version:
Builder for rich single-page js apps (frontend)
273 lines (230 loc) • 7.53 kB
JavaScript
var sys = require('util'),
path = require('path'),
fs = require('fs.extra'),
extend = require('extend'),
dust = require('dustjs-linkedin'),
dustHelpers = require('dustjs-helpers'),
modulePath = path.resolve(module.filename, '../../src'),
customHelpers = require(path.resolve(modulePath, 'apps/common/js/helpers')),
jshint = require('jshint').JSHINT,
Jscs = require('jscs'),
jscsConfig = require('jscs/lib/cli-config'),
autoprefixer = require('autoprefixer'),
less = require('less');
module.exports = function (settings) {
var scriptTypes = [
'js',
'css',
'less',
'dust',
'json'
],
jscs = new Jscs();
// JSCS config
jscs.registerDefaultRules();
if (settings.config && settings.config.jscsRules) {
jscs.configure(jscsConfig.load(path.join(__dirname, settings.config.jscsRules)));
}
var File = function (filepath, params) {
this.path = filepath;
this.basename = path.basename(filepath);
this.extname = path.extname(filepath);
this.params = extend({
lint: false,
jscs: false,
dust: false,
ie: false,
test: false,
testDeploy: false,
binary: false,
vendor: false,
deploy: true
}, params);
this.applyParams();
this.process();
};
File.prototype.applyParams = function () {
//различные проверки
var relativePath = this.path.split(path.resolve(settings.source))[1] || this.path,
isIE = (/\.ie\./i).test(this.basename),
isTest = (/(\/|\\)test(\/|\\)/i).test(relativePath),
isVendor = (/(\/|\\)vendor(\/|\\)/i).test(relativePath);
//файл для версии под ИЕ
this.params.ie = isIE;
//файл для тестов
this.params.test = isTest;
//"чужой" файл – библиотека, плагин, фреймворк или типа того
this.params.vendor = isVendor;
this.params.type = this.extname.split('.')[1];
if (this.params.type === 'dust') {
//если это dust-шаблон
this.params.dust = this.params.dust || true;
}
if (this.params.type === 'js' && !this.params.vendor && !this.params.test) {
//если это свой js-файл – то его надо будет проверить
this.params.lint = true;
this.params.jscs = true;
}
if (scriptTypes.indexOf(this.params.type) < 0) {
//если файл не подпадает под "скриптовые" типы – считаем его бинарным
//и далее тупо копируем
this.params.binary = true;
}
if (this.params.type === 'md' || (this.params.test && !this.params.testDeploy) || this.params.type === 'json') {
this.params.deploy = false;
}
};
/**
* Check file with JSLint and get array of error information objects
* @param content file content
* @param filename file name
* @returns {Array} error information
*/
function jsLintFile(content, filename) {
var result,
jshintResults = [];
result = jshint(content, {
"undef": true,
"unused": true,
"plusplus": true,
"predef": {
"window": false,
"jQuery": false,
"dust": false,
"extend": false
}
});
if (!result) {
jshintResults = jshintResults.concat(jshint.errors.map(function (error) {
if (error) {
error.file = error.file || filename;
//error.line -= 1;
return error;
}
}));
}
return jshintResults;
}
/**
* Check file with JSCS and get array of error information objects
* @param content file content
* @param filename file name
* @returns {Array} error information
*/
function jscsFile(content, filename) {
var result = [];
try {
jscs.checkString(content, filename).getErrorList().forEach(function (err) {
err.file = filename;
result.push({
line: err.line,
character: err.column,
reason: err.message,
file: filename
});
});
} catch (err) {
result.push(err.message.replace('null:', filename + ':'));
}
return result;
}
File.prototype.processors = {
'js': function () {
if (this.params.lint) {
this.errors = jsLintFile(this.content, this.path);
}
if (this.params.jscs) {
this.csErrors = jscsFile(this.content, this.path);
}
return this.content;
},
'css': function () {
if (!this.params.vendor) {
return autoprefixer(settings.config.autoprefixerOptions).process(this.content);
}
return this.content;
},
'less': function () {
var file = this,
syncReady = false,
parser = new (less.Parser)({
syncImport: true,
paths: [ // указывает пути поиска для директив @import
this.path,
path.resolve(settings.source, 'apps', this.params.app, 'css'),
path.resolve(settings.source, 'apps', 'common', 'css'),
path.resolve(settings.source, 'modules')
],
filename: this.path // указывает имя файла, для улучшения сообщений об ошибках
});
parser.parse(this.content, function (e, tree) {
syncReady = true;
file.content = tree.toCSS();
});
if (!syncReady) {
throw new Error('Can`t synchronous render less file!');
}
this.params.type = 'css';
this.content = this.processors[this.params.type].call(this);
return this.content;
},
'dust': function () {
var file = this,
name = path.join(this.params.parent || '', this.params.path.replace(/^templates(\/|\\|$)/, ''), path.basename(this.basename, '.dust')),
template,
moduleRx = /\{@my([^\}]*)\}/g,
moduleNameRx = /module="([^"]*)"/,
BOMcheck = /^\uFEFF/,
modules;
if (this.content.match(BOMcheck)) {
this.errors = [
{
reason: 'BOM mark detected!',
file: this.path,
line: 1,
character: 1
}
];
this.content = this.content.replace(BOMcheck, '');
}
name = name.replace(/\\/g, '/');
this.modules = [];
// проверяем, подключаются ли в шаблоне модули
modules = this.content.match(moduleRx);
if (modules) {
// если подключаются, находим их названия
modules.forEach(function (module) {
module = module.match(moduleNameRx)[1];
if (module && file.modules.indexOf(module) === -1) {
file.modules.push(module);
}
});
}
try {
template = dust.compile(this.content, name);
dust.loadSource(template);
} catch (e) {
sys.log('Dust syntax error\n\tFile: ' + this.path);
sys.puts('\t' + e.message);
throw e;
}
this.params.type = 'js';
return template;
}
};
File.prototype.process = function () {
if (this.params.deploy) {
//onlyBinaries нужна, чтобы можно было скопировать
//бинарники уже ранее обработанного модуля
//и не читать заново другие файлы
if (!this.params.onlyBinaries || this.params.binary) {
this.content = fs.readFileSync(this.path, this.params.binary ? null : {encoding: 'utf8'});
//если нужно как-то обработать контент файла – делаем это
if (this.processors[this.params.type]) {
this.content = this.processors[this.params.type].call(this);
}
}
}
};
return File;
};