'use strict';
var _ = require('./util.js');
var glob = require('glob');
var path = require('path');
/**
* fis 项目相关
* @namespace fis.project
*/
exports.DEFAULT_REMOTE_REPOS = 'http://fis.baidu.com/repos';
/**
* 获取项目目录中,需要编译的文件集合,返回格式为对象,`key` 为文件基于项目目录的绝对路径, `value` 为文件对象。
*
* 当前 media 环境下面设置有 `project.files` 列表时,走 {@link fis.project.getSourceByPatterns}, 否则兼容 fis 2 中
* 的用法,基于 `project.include` 和 `fis.exclude` 走 {@link fis.util.find} 查找。
*
* @name getSource
* @function
* @memberOf fis.project
*/
exports.getSource = function() {
var patterns = fis.media().get('project.files');
if (patterns && patterns.length) {
return exports.getSourceByPatterns(patterns);
}
var root = exports.getProjectPath(),
source = {},
project_exclude = /^(\/output\b|\/fis-[^\/]+)$/,
include = fis.config.get('project.include'),
exclude = fis.config.get('project.exclude');
if (fis.util.is(exclude, 'Array')) {
project_exclude = [project_exclude].concat(exclude);
} else if (exclude) {
project_exclude = [project_exclude, exclude];
}
fis.util.find(root, include, project_exclude, root).forEach(function(file) {
// 产生对应的 File对象
file = fis.file(file);
if (file.release) {
source[file.subpath] = file;
}
});
return source;
};
/**
* 返回项目目录下符合检索规则的文件。以 `project.files` 为匹配规则,`project.ignore` 为排除规则进行检索
* @param {String} patterns 匹配规则,glob文法
* @param {Object} opts 配置 @see glob.sync options
* @return {Object} 返回
* @memberOf fis.project
* @name getSourceByPatterns
*/
exports.getSourceByPatterns = function(patterns, opts) {
patterns = patterns || fis.media().get('project.files');
if (!Array.isArray(patterns)) {
patterns = [patterns];
}
opts = _.assign({
cwd: exports.getProjectPath(),
matchBase: true,
ignore: fis.media().get('project.ignore', []).map(function(pattern) {
return pattern[0] === '/' ? pattern.substring(1) : pattern;
})
}, opts || {});
opts.nodir = true;
opts.sync = true;
var ret = [];
var source = {};
patterns.forEach(function(pattern, index) {
var exclude = pattern.substring(0, 1) === '!';
if (exclude) {
pattern = pattern.substring(1);
// 如果第一个规则就是排除用法,都没有获取结果就排除,这是不合理的用法。
// 不过为了保证程序的正确性,在排除之前,通过 `**` 先把所有文件获取到。
// 至于性能问题,请用户使用时规避。
(index === 0) && (ret = glob.sync('**', opts));
}
// glob 列文件规则带 / 表现不对。
pattern[0] === '/' && (pattern = pattern.substring(1));
var mathes = glob.sync(pattern, opts);
ret = _[exclude ? 'difference' : 'union'](ret, mathes);
});
ret.forEach(function(file) {
file = fis.file(opts.cwd, file);
if (file.release && !file.isDir()) {
source[file.subpath] = file;
}
});
return source;
};
//paths
var PROJECT_ROOT;
var TEMP_ROOT;
/*
* 返回由 root 和 args 拼接成的标准路径。
* @param {String} root rootPath
* @param {String|Array} args 后续路径
* @return {String} 标准路径格式
* @example
* getPath('/Users/', ['apple', '/someone/', '/Destop/']) === getPath('/Users/', 'apple/someone//Destop/')
* /Users/apple/someone/Destop
*/
function getPath(root, args) {
if (args && args.length > 0) {
args = root + '/' + Array.prototype.join.call(args, '/');
return fis.util(args);
} else {
return fis.util(root);
}
}
/*
* 初始化文件夹
* @param {String} path 文件夹路径
* @param {String} title
* @return {String} 若文件夹已存在返回其觉得对路径,若不存在新建病返回绝对路径,若为文件则打印错误信息,返回path绝对路径
*/
function initDir(path, title) {
if (fis.util.exists(path)) {
if (!fis.util.isDir(path)) {
fis.log.error('unable to set path[%s] as %s directory.', path, title);
}
} else {
fis.util.mkdir(path);
}
path = fis.util.realpath(path);
if (path) {
return path;
} else {
fis.log.error('unable to create dir [%s] for %s directory.', path, title);
}
}
/**
* 获取项目所在目录。
* 注意:返回的文件路径,已经被 normalize 了。
* @param {String} [subpath] 如果指定了子目录,将返回子目录路径。
* @return {String}
* @function
* @memberOf fis.project
* @name getProjectPath
*/
exports.getProjectPath = function() {
if (PROJECT_ROOT) {
return getPath(PROJECT_ROOT, arguments);
} else {
fis.log.error('undefined project root');
}
};
/**
* 设置项目根目录
* @param {String} path 项目根目录
* @function
* @memberOf fis.project
* @name setProjectRoot
*/
exports.setProjectRoot = function(path) {
if (fis.util.isDir(path)) {
PROJECT_ROOT = fis.util.realpath(path);
} else {
fis.log.error('invalid project root path [%s]', path);
}
};
/**
* 设置并创建临时文件夹
* @param {String} tmp 临时文件夹路径
* @function
* @memberOf fis.project
* @name setTempRoot
*/
exports.setTempRoot = function(tmp) {
TEMP_ROOT = initDir(tmp);
};
/**
* 获取临时文件夹根目录,若未手动设置,则遍历用户环境变量中['FIS_TEMP_DIR', 'LOCALAPPDATA', 'APPDATA', 'HOME']这几项是否有设置,有则作为临时目录path,没有则以fis3/.fis-tmp作为临时文件夹
* @function
* @memberOf fis.project
* @name getTempRoot
*/
exports.getTempRoot = function() {
if (!TEMP_ROOT) {
var list = ['FIS_TEMP_DIR', 'LOCALAPPDATA', 'APPDATA', 'HOME'];
var name = fis.cli && fis.cli.name ? fis.cli.name : 'fis';
var tmp;
for (var i = 0, len = list.length; i < len; i++) {
if ((tmp = process.env[list[i]])) {
break;
}
}
tmp = tmp || __dirname + '/../';
exports.setTempRoot(tmp + '/.' + name + '-tmp');
}
return TEMP_ROOT;
};
/**
* 获取临时文件夹路径
* @return {String}
* @function
* @memberOf fis.project
* @name getTempPath
*/
exports.getTempPath = function() {
return getPath(exports.getTempRoot(), arguments);
};
/**
* 获取对应缓存的文件路径,缓存存放在 TEMP_ROOT/cache 下
* @return {String} 对应缓存的路径
* @function
* @memberOf fis.project
* @name getCachePath
*/
exports.getCachePath = function() {
return getPath(exports.getTempPath('cache'), arguments);
};
/**
* 根据路径查找路径,支持相对路径和基于项目文件夹的绝对路径。
*
* @param {string} path 文件路径。
* @param {File} file 文件对象,当路径为相对路径时,此文件对象所在目录将用于查找目标文件。
* @return {Object} 返回查找结果对象。
* @function
* @memberOf fis.project
* @name lookup
*/
exports.lookup = function(path, file, silent) {
var info = fis.uri(path, file ? file.dirname : exports.getProjectPath());
/**
* 当查找文件时派送, 可以扩展 fis 默认的查找文件功能。如:支持无后缀文件查找,支持 components 文件查找。
* @event lookup:file
* @property {Object} info 包含查找路径信息。
* @property {File} file 文件对象。
* @memberOf fis
*/
fis.emit('lookup:file', info, file, silent);
if (!info.id) {
info.id = info.file ? info.file.id : info.rest;
}
if (!info.moduleId) {
info.moduleId = info.file && info.file.moduleId || info.id;
}
return info;
};
var mediaName;
/**
* 返回当前用户所在的 media 名称。
* @return {String} 当前用户所在分支
* @function
* @memberOf fis.project
* @name currentMedia
*/
exports.currentMedia = function (value) {
if (arguments.length) {
mediaName = value;
}
return mediaName || 'dev';
};
Namespaces
- fis
- cache
- cli
- compile
- lang
- config
- emitter
- file
- log
- project
- uri
- util
- applyMatches
- base64
- camelcase
- clone
- copy
- del
- download
- escapeReg
- escapeShellArg
- escapeShellCmd
- exist
- ext
- filter
- find
- getMimeType
- glob
- install
- isAbsolute
- isDir
- isEmpty
- isFile
- isImageFile
- isTextFile
- isUtf8
- isWin
- map
- md5
- merge
- mkdir
- mtime
- nohup
- pad
- parseUrl
- pathinfo
- pipe
- query
- read
- readBuffer
- readJson
- realpath
- realpathSafe
- stringQuote
- toEncoding
- touch
- upload
- write