enb
Version:
Faster BEM/BEViS assembler
339 lines (317 loc) • 11.4 kB
JavaScript
/**
* ProjectConfig
* =============
*/
var NodeConfig = require('./node-config');
var NodeMaskConfig = require('./node-mask-config');
var TaskConfig = require('./task-config');
var ModeConfig = require('./mode-config');
var inherit = require('inherit');
var dirGlob = require('../fs/dir-glob');
var path = require('path');
var dropRequireCache = require('../fs/drop-require-cache');
/**
* Инстанции класса ProjectConfig передаются в enb-make-файлы.
* С помощью инстанций этого класса производится настройка сборки проекта.
* @name ProjectConfig
*/
module.exports = inherit( /** @lends ProjectConfig.prototype */ {
/**
* Конструктор.
* @param {String} rootPath Путь к корню проекта.
* @private
*/
__constructor: function (rootPath) {
this._rootPath = rootPath;
this._nodeConfigs = {};
this._tasks = {};
this._nodeMaskConfigs = [];
this._languages = null;
this._levelNamingSchemes = [];
/**
* @type {{ModuleConfig}}
* @private
*/
this._modules = {};
var env = this._env = {};
this._modes = {};
var processEnv = process.env;
Object.keys(processEnv).forEach(function (key) {
env[key] = processEnv[key];
});
},
/**
* Возвращает языки для проекта.
* @returns {String[]}
*/
getLanguages: function () {
return this._languages;
},
/**
* Устанавливает языки для проекта.
* @param {String[]} languages
* @returns {ProjectConfig}
*/
setLanguages: function (languages) {
this._languages = languages;
return this;
},
/**
* Возвращает путь к корню проекта.
* @returns {String}
*/
getRootPath: function () {
return this._rootPath;
},
/**
* Возвращает абсолютный путь к файлу на основе пути, относительно корня проекта.
* @param {String|Object} path
* @returns {String}
*/
resolvePath: function (sourcePath) {
if (sourcePath) {
if (typeof sourcePath === 'string') {
return path.resolve(this._rootPath, sourcePath);
} else {
sourcePath.path = path.resolve(this._rootPath, sourcePath.path);
return sourcePath;
}
} else {
return this._rootPath;
}
},
/**
* Объявляет ноду.
* Необходимо объявить все ноды, используемые в сборке.
* @param {String} path
* @param {Function} func Конфигуратор ноды.
* @returns {ProjectConfig}
*/
node: function (nodePath, func) {
var slashRegExp = new RegExp('^\\' + path.sep + '+|\\' + path.sep + '+$', 'g');
nodePath = nodePath.replace(slashRegExp, '');
if (!this._nodeConfigs[nodePath]) {
this._nodeConfigs[nodePath] = new NodeConfig(nodePath, this._rootPath, this);
}
if (func) {
this._nodeConfigs[nodePath].addChain(func);
}
return this;
},
/**
* Объявляет набор нод.
* Ноды можно объявлять по shell-маске.
* @param {String} path1 ,..
* @param {Function} [func]
* @returns {ProjectConfig}
*/
nodes: function () {
var result;
var input = arguments;
var flat = false;
var root = this._rootPath;
var fn;
function toRelative(nodePath) {
return nodePath.replace(root + path.sep, '');
}
while (!flat) {
flat = true;
result = [];
for (var i = 0, l = input.length; i < l; i++) {
var item = input[i];
if (typeof item === 'function') {
fn = item;
} else if (Array.isArray(item)) {
result = result.concat(item);
flat = false;
} else if (item && typeof item === 'string') {
if (~item.indexOf('*')) {
result = result.concat(dirGlob.globSync(path.join(root, item)).map(toRelative));
} else {
result.push(item);
}
}
}
input = result;
}
var _this = this;
result.forEach(function (nodePath) {
_this.node(nodePath, fn);
});
return this;
},
/**
* Настраивает ноды по маске. Маска задается с помощью регулярного выражения.
* Имейте ввиду, что ноды должны быть добавлены в конфигурацию проекта с помощью метода node или аналогов.
* @param {RegExp} mask
* @param {Function} func
* @returns {ProjectConfig}
*/
nodeMask: function (mask, func) {
var nodeMask = new NodeMaskConfig(mask);
nodeMask.addChain(func);
this._nodeMaskConfigs.push(nodeMask);
return this;
},
/**
* Объявляет таск. Таск может быть выполнен с помощью ./node_modules/.bin/enb make task_name
* @param {String} name
* @param {Function} func
* @returns {ProjectConfig}
*/
task: function (name, func) {
if (!this._tasks[name]) {
this._tasks[name] = new TaskConfig(name);
}
this._tasks[name].addChain(func);
return this;
},
/**
* Конфигурирует проект для конкретного режима.
* Режим задается с помощью ENV-переменной YENV.
* @param {String} name
* @param {Function} func
* @returns {ProjectConfig}
*/
mode: function (name, func) {
if (!this._modes[name]) {
this._modes[name] = new ModeConfig(name);
}
this._modes[name].addChain(func);
return this;
},
/**
* Регистрирует модуль.
* @param {String} name
* @param {ModuleConfig} moduleConfig
*/
registerModule: function (name, moduleConfig) {
if (!this._modules[name]) {
this._modules[name] = moduleConfig;
} else {
throw new Error('Module "' + name + '" is already registered.');
}
},
/**
* Настраивает модуль.
* @param {String} name
* @param {Function} func
* @returns {ProjectConfig|ModuleConfig}
*/
module: function (name, func) {
var module = this._modules[name];
if (module) {
if (func) {
module.addChain(func);
return this;
} else {
return module;
}
} else {
throw new Error('Module "' + name + '" is not registered.');
}
},
getTaskConfigs: function () {
return this._tasks;
},
getTaskConfig: function (taskName) {
return this._tasks[taskName];
},
getModeConfigs: function () {
return this._modes;
},
getModeConfig: function (modeName) {
return this._modes[modeName];
},
getNodeConfigs: function () {
return this._nodeConfigs;
},
getNodeConfig: function (nodeName) {
return this._nodeConfigs[nodeName];
},
getNodeMaskConfigs: function (nodePath) {
return nodePath ? this._nodeMaskConfigs.filter(function (nodeMask) {
return nodeMask.getMask().test(nodePath);
}) : this._nodeMaskConfigs;
},
/**
* Возвращает установленную переменную среды.
* @param {String} key
* @returns {String}
*/
getEnv: function (key) {
return this._env[key];
},
/**
* Устанавливает переменные среды.
* Переменные среды попадают в shell-команды в тасках.
* @param {String} key
* @param {String} value
*/
setEnv: function (key, value) {
var _this = this;
if (typeof key === 'object') {
Object.keys(key).forEach(function (name) {
_this._env[name] = key[name];
});
} else {
_this._env[key] = value;
}
},
getEnvValues: function () {
return this._env;
},
/**
* Подключает другой enb-make-файл с конфигурацией сборки.
* @param {String} filename
* @returns {ProjectConfig}
*/
includeConfig: function (filename) {
filename = require.resolve(filename);
(this._includedConfigFilenames || (this._includedConfigFilenames = [])).push(filename);
dropRequireCache(require, filename);
require(filename)(this);
return this;
},
/**
* Возвращает список подключенных enb-make-файлов.
* @returns {String[]}
*/
getIncludedConfigFilenames: function () {
return this._includedConfigFilenames || [];
},
/**
* Устанавливает схему именования для уровня переопределения.
* В функцию формирования схемы именования первым аргументом указывается абсолютный путь до уровня переопределения,
* а вторым аргументом — инстанция LevelBuilder.
* Схема именования должна содержать два метода:
* ```javascript
* // Выполняет построение структуры файлов уровня переопределения, используя методы инстанции класса LevelBuilder.
* {Promise} buildLevel( {String} levelPath, {LevelBuilder} levelBuilder )
* // Возвращает путь к файлу на основе пути к уровню переопределения и BEM-описания.
* {String} buildFilePath(
* {String} levelPath, {String} blockName, {String} elemName, {String} modName, {String} modVal
* )
* ```
* @param {String|Array} level
* @param {Function} schemeBuilder
*/
setLevelNamingScheme: function (level, schemeBuilder) {
var levels = Array.isArray(level) ? level : [level];
var _this = this;
levels.forEach(function (levelPath) {
if (levelPath.charAt(0) !== '/') {
levelPath = _this.resolvePath(levelPath);
}
_this._levelNamingSchemes[levelPath] = schemeBuilder;
});
return this;
},
/**
* Возвращает схемы именования для уровней переопределения.
* @returns {Array}
*/
getLevelNamingSchemes: function () {
return this._levelNamingSchemes;
}
});