当前位置: 首页 > 工具软件 > swf util > 使用案例 >

php util.js,util.js

庄智
2023-12-01

'use strict';

var minimatch = require('minimatch');

var lodash = require('lodash');

var fs = require('fs'),

pth = require('path'),

crypto = require('crypto'),

Url = require('url'),

_exists = fs.existsSync || pth.existsSync,

toString = Object.prototype.toString,

iconv, tar;

var IS_WIN = process.platform.indexOf('win') === 0;

/*

* 后缀类型HASH

* @type {Array}

*/

var TEXT_FILE_EXTS = [

'css', 'tpl', 'js', 'php',

'txt', 'json', 'xml', 'htm',

'text', 'xhtml', 'html', 'md',

'conf', 'po', 'config', 'tmpl',

'coffee', 'less', 'sass', 'jsp',

'scss', 'pcss', 'manifest', 'bak', 'asp',

'tmp', 'haml', 'jade', 'aspx',

'ashx', 'java', 'py', 'c', 'cpp',

'h', 'cshtml', 'asax', 'master',

'ascx', 'cs', 'ftl', 'vm', 'ejs',

'styl', 'jsx', 'handlebars', 'shtml',

'ts', 'tsx', 'yml', 'sh', 'es', 'es6', 'es7',

'map'

],

IMAGE_FILE_EXTS = [

'svg', 'tif', 'tiff', 'wbmp',

'png', 'bmp', 'fax', 'gif',

'ico', 'jfif', 'jpe', 'jpeg',

'jpg', 'woff', 'cur', 'webp',

'swf', 'ttf', 'eot', 'woff2'

],

MIME_MAP = {

//text

'css': 'text/css',

'tpl': 'text/html',

'js': 'text/javascript',

'jsx': 'text/javascript',

'ts': 'text/javascript',

'tsx': 'text/javascript',

'es': 'text/javascript',

'es6': 'text/javascript',

'es7': 'text/javascript',

'php': 'text/html',

'asp': 'text/html',

'jsp': 'text/jsp',

'txt': 'text/plain',

'json': 'application/json',

'xml': 'text/xml',

'htm': 'text/html',

'text': 'text/plain',

'md': 'text/plain',

'xhtml': 'text/html',

'html': 'text/html',

'conf': 'text/plain',

'po': 'text/plain',

'config': 'text/plain',

'coffee': 'text/javascript',

'less': 'text/css',

'sass': 'text/css',

'scss': 'text/css',

'styl': 'text/css',

'pcss': 'text/css',

'manifest': 'text/cache-manifest',

//image

'svg': 'image/svg+xml',

'tif': 'image/tiff',

'tiff': 'image/tiff',

'wbmp': 'image/vnd.wap.wbmp',

'webp': 'image/webp',

'png': 'image/png',

'bmp': 'image/bmp',

'fax': 'image/fax',

'gif': 'image/gif',

'ico': 'image/x-icon',

'jfif': 'image/jpeg',

'jpg': 'image/jpeg',

'jpe': 'image/jpeg',

'jpeg': 'image/jpeg',

'eot': 'application/vnd.ms-fontobject',

'woff': 'application/font-woff',

'ttf': 'application/octet-stream',

'cur': 'application/octet-stream'

};

function getIconv() {

if (!iconv) {

iconv = require('iconv-lite');

}

return iconv;

}

function getTar() {

if (!tar) {

tar = require('tar');

}

return tar;

}

/**

* fis 中工具类操作集合。{@link https://lodash.com/ lodash} 中所有方法都挂载在此名字空间下面。

* @param {String} path

* @return {String}

* @example

* /a/b//c\d/ -> /a/b/c/d

* @namespace fis.util

*/

var _ = module.exports = function(path) {

var type = typeof path;

if (arguments.length > 1) {

path = Array.prototype.join.call(arguments, '/');

} else if (type === 'string') {

//do nothing for quickly determining.

} else if (type === 'object') {

path = Array.prototype.join.call(path, '/');

} else if (type === 'undefined') {

path = '';

}

if (path) {

path = pth.normalize(path.replace(/[\/\\]+/g, '/')).replace(/\\/g, '/');

if (path !== '/') {

path = path.replace(/\/$/, '');

}

}

return path;

};

// 将lodash内部方法的引用挂载到utils上,方便使用

lodash.assign(_, lodash);

_.is = function(source, type) {

return toString.call(source) === '[object ' + type + ']';

};

/**

* 对象枚举元素遍历,若merge为true则进行_.assign(obj, callback),若为false则回调元素的key value index

* @param {Object} obj 源对象

* @param {Function|Object} callback 回调函数|目标对象

* @param {Boolean} merge 是否为对象赋值模式

* @memberOf fis.util

* @name map

* @function

*/

_.map = function(obj, callback, merge) {

var index = 0;

for (var key in obj) {

if (obj.hasOwnProperty(key)) {

if (merge) {

callback[key] = obj[key];

} else if (callback(key, obj[key], index++)) {

break;

}

}

}

};

/**

* 固定长度字符前后缀填补方法(fillZero)

* @param {String} str 初始字符串

* @param {Number} len 固定长度

* @param {String} fill 填补的缀

* @param {Boolean} pre 前缀还是后缀

* @return {String} 填补后的字符串

* @memberOf fis.util

* @name pad

* @function

*/

_.pad = function(str, len, fill, pre) {

if (str.length < len) {

fill = (new Array(len)).join(fill || ' ');

if (pre) {

str = (fill + str).substr(-len);

} else {

str = (str + fill).substring(0, len);

}

}

return str;

};

/**

* 将target合并到source上,新值为undefiend一样会覆盖掉原有数据

* @param {Object} source 源对象

* @param {Object} target 目标对象

* @return {Object} 合并后的对象

* @memberOf fis.util

* @name merge

* @function

*/

_.merge = function(source, target) {

if (_.is(source, 'Object') && _.is(target, 'Object')) {

_.map(target, function(key, value) {

source[key] = _.merge(source[key], value);

});

} else {

source = target;

}

return source;

};

/**

* clone一个变量

* @param {any} source 变量

* @return {any} clone值

* @memberOf fis.util

* @name clone

* @function

*/

/*_.clone = function(source) {

var ret;

switch (toString.call(source)) {

case '[object Object]':

ret = {};

_.map(source, function(k, v) {

ret[k] = _.clone(v);

});

break;

case '[object Array]':

ret = [];

source.forEach(function(ele) {

ret.push(_.clone(ele));

});

break;

default:

ret = source;

}

return ret;

};*/

/**

* 正则串编码转义

* @param {String} str 正则串

* @return {String} 普通字符串

* @memberOf fis.util

* @name escapeReg

* @function

*/

_.escapeReg = function(str) {

return str.replace(/[\.\\\+\*\?\[\^\]\$\(\){}=!<>\|:\/]/g, '\\$&');

};

/**

* shell命令编码转义

* @param {String} 命令

* @memberOf fis.util

* @name escapeShellCmd

* @function

*/

_.escapeShellCmd = function(str) {

return str.replace(/ /g, '"$&"');

};

/**

* shell编码转义

* @param {String} 命令

* @memberOf fis.util

* @name escapeShellArg

* @function

*/

_.escapeShellArg = function(cmd) {

return '"' + cmd + '"';

};

/**

* 提取字符串中的引号和一对引号包围的内容

* @param {String} str 待处理字符串

* @param {String} quotes 初始引号可选范围,缺省为[',"]

* @return {Object} {

* origin: 源字符串

* rest: 引号包围的文字内容

* quote: 引号类型

* }

* @memberOf fis.util

* @name stringQuote

* @function

*/

_.stringQuote = function(str, quotes) {

var info = {

origin: str,

rest: str = str.trim(),

quote: ''

};

if (str) {

quotes = quotes || '\'"';

var strLen = str.length - 1;

for (var i = 0, len = quotes.length; i < len; i++) {

var c = quotes[i];

if (str[0] === c && str[strLen] === c) {

info.quote = c;

info.rest = str.substring(1, strLen);

break;

}

}

}

return info;

};

/**

* 匹配文件后缀所属MimeType类型

* @param {String} ext 文件后缀

* @return {String} MimeType类型

* @memberOf fis.util

* @name getMimeType

* @function

*/

_.getMimeType = function(ext) {

if (ext[0] === '.') {

ext = ext.substring(1);

}

return MIME_MAP[ext] || 'application/x-' + ext;

};

/**

* 判断文件是否存在。

* @param {String} filepath 文件路径。

* @memberOf fis.util

* @name exist

* @function

*/

_.exists = _exists;

_.fs = fs;

/**

* 返回path的绝对路径,若path不存在则返回false

* @param {String} path 路径

* @return {String} 绝对路径

* @memberOf fis.util

* @name realpath

* @function

*/

_.realpath = function(path) {

if (path && _exists(path)) {

path = fs.realpathSync(path);

if (IS_WIN) {

path = path.replace(/\\/g, '/');

}

if (path !== '/') {

path = path.replace(/\/$/, '');

}

return path;

} else {

return false;

}

};

/**

* 多功能path处理

* @param {String} path 路径

* @return {String} 处理后的路径

* @memberOf fis.util

* @name realpathSafe

* @function

*/

_.realpathSafe = function(path) {

return _.realpath(path) || _(path);

};

/**

* 判断路径是否为绝对路径

* @param {String} path 路径

* @return {Boolean} true为是

* @memberOf fis.util

* @name isAbsolute

* @function

*/

// _.isAbsolute = function(path) {

// if (!IS_WIN && path && path[0] === '~') {

// return true;

// }

// return pth.isAbsolute ? pth.isAbsolute(path) : pth.resolve(path) === pth.normalize(path);

// };

_.isAbsolute = function(path) {

if (IS_WIN) {

return /^[a-z]:/i.test(path);

} else {

if (path === '/') {

return true;

} else {

var split = path.split('/');

if (split[0] === '~') {

return true;

} else if (split[0] === '' && split[1]) {

return _.isDir('/' + split[1] + '/' + split[2]);

} else {

return false;

}

}

}

};

/**

* 是否为一个文件

* @param {String} path 路径

* @return {Boolean} true为是

* @memberOf fis.util

* @name isFile

* @function

*/

_.isFile = function(path) {

return _exists(path) && fs.statSync(path).isFile();

};

/**

* 是否为文件夹

* @param {String} path 路径

* @return {Boolean} true为是

* @memberOf fis.util

* @name isDir

* @function

*/

_.isDir = function(path) {

return _exists(path) && fs.statSync(path).isDirectory();

};

/**

* 获取路径最近修改时间

* @param {String} path 路径

* @return {Date} 时间(GMT+0800)

* @memberOf fis.util

* @name mtime

* @function

*/

_.mtime = function(path) {

var time = 0;

if (_exists(path)) {

time = fs.statSync(path).mtime;

}

return time;

};

/**

* 修改文件时间戳

* @param {String} path 路径

* @param {Date|Number} mtime 时间戳

* @memberOf fis.util

* @name touch

* @function

*/

_.touch = function(path, mtime) {

if (!_exists(path)) {

_.write(path, '');

}

if (mtime instanceof Date) {

//do nothing for quickly determining.

} else if (typeof mtime === 'number') {

var time = new Date();

time.setTime(mtime);

mtime = time;

} else {

fis.log.error('invalid argument [mtime]');

}

fs.utimesSync(path, mtime, mtime);

};

/**

* 是否为windows系统

* @return {Boolean}

* @memberOf fis.util

* @name isWin

* @function

*/

_.isWin = function() {

return IS_WIN;

};

/*

* 生成对应文件类型判断的正则

* @param {String} type 文件类型

* @return {RegExp} 对应判断的正则表达式

*/

function getFileTypeReg(type) {

var map = [],

ext = fis.config.get('project.fileType.' + type);

if (type === 'text') {

map = TEXT_FILE_EXTS;

} else if (type === 'image') {

map = IMAGE_FILE_EXTS;

} else {

fis.log.error('invalid file type [%s]', type);

}

if (ext && ext.length) {

if (typeof ext === 'string') {

ext = ext.split(/\s*,\s*/);

}

map = map.concat(ext);

}

map = map.join('|');

return new RegExp('\\.(?:' + map + ')$', 'i');

}

/**

* 是否为配置中的text文件类型

* @param {String} path 路径

* @return {Boolean}

* @memberOf fis.util

* @name isTextFile

* @function

*/

_.isTextFile = function(path) {

return getFileTypeReg('text').test(path || '');

};

/**

* 是否为配置中的image文件类型

* @param {String} path 路径

* @return {Boolean}

* @memberOf fis.util

* @name isImageFile

* @function

*/

_.isImageFile = function(path) {

return getFileTypeReg('image').test(path || '');

};

/**

* 按位数生成md5串

* @param {String|Buffer} data 数据源

* @param {Number} len 长度

* @return {String} md5串

* @memberOf fis.util

* @name md5

* @function

*/

_.md5 = function(data, len) {

var md5sum = crypto.createHash('md5'),

encoding = typeof data === 'string' ? 'utf8' : 'binary';

md5sum.update(data, encoding);

len = len || fis.config.get('project.md5Length', 7);

return md5sum.digest('hex').substring(0, len);

};

/**

* 生成base64串

* @param {String|Buffer|Array} data 数据源

* @return {String} base64串

* @memberOf fis.util

* @name base64

* @function

*/

_.base64 = function(data) {

if (data instanceof Buffer) {

//do nothing for quickly determining.

} else if (data instanceof Array) {

data = new Buffer(data);

} else {

//convert to string.

data = new Buffer(String(data || ''));

}

return data.toString('base64');

};

/**

* 递归创建文件夹

* @param {String} path 路径

* @param {Number} mode 创建模式

* @memberOf fis.util

* @name mkdir

* @function

*/

_.mkdir = function(path, mode) {

if (typeof mode === 'undefined') {

//511 === 0777

mode = 511 & (~process.umask());

}

if (_exists(path)) return;

path.split('/').reduce(function(prev, next) {

if (prev && !_exists(prev)) {

fs.mkdirSync(prev, mode);

}

return prev + '/' + next;

});

if (!_exists(path)) {

fs.mkdirSync(path, mode);

}

};

/**

* 字符串编码转换

* @param {String|Number|Array|Buffer} str 待处理的字符串

* @param {String} encoding 编码格式

* @return {String} 编码转换后的字符串

* @memberOf fis.util

* @name toEncoding

* @function

*/

_.toEncoding = function(str, encoding) {

return getIconv().toEncoding(String(str), encoding);

};

/**

* 判断Buffer是否为utf8

* @param {Buffer} bytes 待检数据

* @return {Boolean} true为utf8

* @memberOf fis.util

* @name isUtf8

* @function

*/

_.isUtf8 = function(bytes) {

var i = 0;

while (i < bytes.length) {

if (( // ASCII

0x00 <= bytes[i] && bytes[i] <= 0x7F

)) {

i += 1;

continue;

}

if (( // non-overlong 2-byte

(0xC2 <= bytes[i] && bytes[i] <= 0xDF) &&

(0x80 <= bytes[i + 1] && bytes[i + 1] <= 0xBF)

)) {

i += 2;

continue;

}

if (

( // excluding overlongs

bytes[i] == 0xE0 &&

(0xA0 <= bytes[i + 1] && bytes[i + 1] <= 0xBF) &&

(0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF)

) || ( // straight 3-byte

((0xE1 <= bytes[i] && bytes[i] <= 0xEC) ||

bytes[i] == 0xEE ||

bytes[i] == 0xEF) &&

(0x80 <= bytes[i + 1] && bytes[i + 1] <= 0xBF) &&

(0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF)

) || ( // excluding surrogates

bytes[i] == 0xED &&

(0x80 <= bytes[i + 1] && bytes[i + 1] <= 0x9F) &&

(0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF)

)

) {

i += 3;

continue;

}

if (

( // planes 1-3

bytes[i] == 0xF0 &&

(0x90 <= bytes[i + 1] && bytes[i + 1] <= 0xBF) &&

(0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF) &&

(0x80 <= bytes[i + 3] && bytes[i + 3] <= 0xBF)

) || ( // planes 4-15

(0xF1 <= bytes[i] && bytes[i] <= 0xF3) &&

(0x80 <= bytes[i + 1] && bytes[i + 1] <= 0xBF) &&

(0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF) &&

(0x80 <= bytes[i + 3] && bytes[i + 3] <= 0xBF)

) || ( // plane 16

bytes[i] == 0xF4 &&

(0x80 <= bytes[i + 1] && bytes[i + 1] <= 0x8F) &&

(0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF) &&

(0x80 <= bytes[i + 3] && bytes[i + 3] <= 0xBF)

)

) {

i += 4;

continue;

}

return false;

}

return true;

};

/**

* 处理Buffer编码方式

* @param {Buffer} buffer 待读取的Buffer

* @return {String} 判断若为utf8可识别的编码则去掉bom返回utf8编码后的String,若不为utf8可识别编码则返回gbk编码后的String

* @memberOf fis.util

* @name readBuffer

* @function

*/

_.readBuffer = function(buffer) {

if (_.isUtf8(buffer)) {

buffer = buffer.toString('utf8');

if (buffer.charCodeAt(0) === 0xFEFF) {

buffer = buffer.substring(1);

}

} else {

buffer = getIconv().decode(buffer, 'gbk');

}

return buffer;

};

/**

* 读取文件内容

* @param {String} path 路径

* @param {Boolean} convert 是否使用text方式转换文件内容的编码 @see readBuffer

* @return {String} 文件内容

* @memberOf fis.util

* @name read

* @function

*/

_.read = function(path, convert) {

var content = false;

if (_exists(path)) {

content = fs.readFileSync(path);

if (convert || _.isTextFile(path)) {

content = _.readBuffer(content);

}

} else {

fis.log.error('unable to read file[%s]: No such file or directory.', path);

}

return content;

};

/**

* 写文件,若路径不存在则创建

* @param {String} path 路径

* @param {String} data 写入内容

* @param {String} charset 编码方式

* @param {Boolean} append 是否为追加模式

* @memberOf fis.util

* @name write

* @function

*/

_.write = function(path, data, charset, append) {

if (!_exists(path)) {

_.mkdir(_.pathinfo(path).dirname);

}

if (charset) {

data = getIconv().encode(data, charset);

}

if (append) {

fs.appendFileSync(path, data, null);

} else {

fs.writeFileSync(path, data, null);

}

};

/**

* str过滤处理,判断include中匹配str为true,exclude中不匹配str为true

* @param {String} str 待处理的字符串

* @param {Array} include include匹配规则

* @param {Array} exclude exclude匹配规则

* @return {Boolean} 是否匹配

* @memberOf fis.util

* @name filter

* @function

*/

_.filter = function(str, include, exclude) {

// pattern处理,若不为正则则调用glob处理生成正则

function normalize(pattern) {

var type = toString.call(pattern);

switch (type) {

case '[object String]':

return _.glob(pattern);

case '[object RegExp]':

return pattern;

default:

fis.log.error('invalid regexp [%s].', pattern);

}

}

// 判断str是否符合patterns中的匹配规则

function match(str, patterns) {

var matched = false;

if (!_.is(patterns, 'Array')) {

patterns = [patterns];

}

patterns.every(function(pattern) {

if (!pattern) {

return true;

}

matched = matched || str.search(normalize(pattern)) > -1;

return !matched;

});

return matched;

}

var isInclude, isExclude;

if (include) {

isInclude = match(str, include);

} else {

isInclude = true;

}

if (exclude) {

isExclude = match(str, exclude);

}

return isInclude && !isExclude;

};

/**

* 若rPath为文件夹,夹遍历目录下符合include和exclude规则的全部文件;若rPath为文件,直接匹配该文件路径是否符合include和exclude规则

* @param {String} rPath 要查找的目录

* @param {Array} include 包含匹配正则集合,可传null

* @param {Array} exclude 排除匹配正则集合,可传null

* @param {String} root 根目录

* @return {Array} 符合规则的文件路径的集合

* @memberOf fis.util

* @name find

* @function

*/

_.find = function(rPath, include, exclude, root) {

var list = [],

path = _.realpath(rPath),

filterPath = root ? path.substring(root.length) : path;

if (path) {

var stat = fs.statSync(path);

if (stat.isDirectory() && (include || _.filter(filterPath, include, exclude))) {

fs.readdirSync(path).forEach(function(p) {

if (p[0] != '.') {

list = list.concat(_.find(path + '/' + p, include, exclude, root));

}

});

} else if (stat.isFile() && _.filter(filterPath, include, exclude)) {

list.push(path);

}

} else {

fis.log.error('unable to find [%s]: No such file or directory.', rPath);

}

return list.sort();

};

/**

* 删除指定目录下面的文件。

* @memberOf fis.util

* @name del

* @function

*/

_.del = function(rPath, include, exclude) {

var removedAll = true;

var path;

if (rPath && _.exists(rPath)) {

var stat = fs.lstatSync(rPath);

var isFile = stat.isFile() || stat.isSymbolicLink();

if (stat.isSymbolicLink()) {

path = rPath;

} else {

path = _.realpath(rPath);

}

if (/^(?:\w:)?\/$/.test(path)) {

fis.log.error('unable to delete directory [%s].', rPath);

}

if (stat.isDirectory()) {

fs.readdirSync(path).forEach(function(name) {

if (name != '.' && name != '..') {

removedAll = _.del(path + '/' + name, include, exclude) && removedAll;

}

});

if (removedAll) {

fs.rmdirSync(path);

}

} else if (isFile && _.filter(path, include, exclude)) {

fs.unlinkSync(path);

} else {

removedAll = false;

}

} else {

//fis.log.error('unable to delete [' + rPath + ']: No such file or directory.');

}

return removedAll;

};

/**

* 复制符合include和exclude规则的文件到目标目录,若rSource为文件夹则递归其下属每个文件

* @param {String} rSource 源路径

* @param {String} target 目标路径

* @param {Array} include 包含匹配规则

* @param {Array} exclude 排除匹配规则

* @param {Boolean} uncover 是否覆盖

* @param {Boolean} move 是否移动

* @memberOf fis.util

* @name copy

* @function

*/

_.copy = function(rSource, target, include, exclude, uncover, move) {

var removedAll = true,

source = _.realpath(rSource);

target = _(target);

if (source) {

var stat = fs.statSync(source);

if (stat.isDirectory()) {

fs.readdirSync(source).forEach(function(name) {

if (name != '.' && name != '..') {

removedAll = _.copy(

source + '/' + name,

target + '/' + name,

include, exclude,

uncover, move

) && removedAll;

}

});

if (move && removedAll) {

fs.rmdirSync(source);

}

} else if (stat.isFile() && _.filter(source, include, exclude)) {

if (uncover && _exists(target)) {

//uncover

removedAll = false;

} else {

_.write(target, fs.readFileSync(source, null));

if (move) {

fs.unlinkSync(source);

}

}

} else {

removedAll = false;

}

} else {

fis.log.error('unable to copy [%s]: No such file or directory.', rSource);

}

return removedAll;

};

/**

* path处理

* @param {String} str 待处理的路径

* @return {Object}

* @example

* str = /a.b.c/f.php?kw=%B2%E5%BB%AD#addfhubqwek

* {

* origin: '/a.b.c/f.php?kw=%B2%E5%BB%AD#addfhubqwek',

* rest: '/a.b.c/f',

* hash: '#addfhubqwek',

* query: '?kw=%B2%E5%BB%AD',

* fullname: '/a.b.c/f.php',

* dirname: '/a.b.c',

* ext: '.php',

* filename: 'f',

* basename: 'f.php'

* }

* @memberOf fis.util

* @name ext

* @function

*/

_.ext = function(str) {

var info = _.query(str),

pos;

str = info.fullname = info.rest;

if ((pos = str.lastIndexOf('/')) > -1) {

if (pos === 0) {

info.rest = info.dirname = '/';

} else {

info.dirname = str.substring(0, pos);

info.rest = info.dirname + '/';

}

str = str.substring(pos + 1);

} else {

info.rest = info.dirname = '';

}

if ((pos = str.lastIndexOf('.')) > -1) {

info.ext = str.substring(pos).toLowerCase();

info.filename = str.substring(0, pos);

info.basename = info.filename + info.ext;

} else {

info.basename = info.filename = str;

info.ext = '';

}

info.rest += info.filename;

return info;

};

/**

* path处理,提取path中rest部分(?之前)、query部分(?#之间)、hash部分(#之后)

* @param {String} str 待处理的url

* @return {Object} {

* origin: 原始串

* rest: path部分(?之前)

* query: query部分(?#之间)

* hash: hash部分(#之后)

* }

* @memberOf fis.util

* @name query

* @function

*/

_.query = function(str) {

var rest = str,

pos = rest.indexOf('#'),

hash = '',

query = '';

if (pos > -1) {

hash = rest.substring(pos);

rest = rest.substring(0, pos);

}

pos = rest.indexOf('?');

if (pos > -1) {

query = rest.substring(pos);

rest = rest.substring(0, pos);

}

rest = rest.replace(/\\/g, '/');

if (rest !== '/') {

// 排除由于.造成路径查找时因filename为""而产生bug,以及隐藏文件问题

rest = rest.replace(/\/\.?$/, '');

}

return {

origin: str,

rest: rest,

hash: hash,

query: query

};

};

/**

* 生成路径信息

* @param {String|Array} path 路径,可使用多参数传递:pathinfo('a', 'b', 'c')

* @return {Object} @see ext()

* @memberOf fis.util

* @name pathinfo

* @function

*/

_.pathinfo = function(path) {

//can not use _() method directly for the case _.pathinfo('a/').

var type = typeof path;

if (arguments.length > 1) {

path = Array.prototype.join.call(arguments, '/');

} else if (type === 'string') {

//do nothing for quickly determining.

} else if (type === 'object') {

path = Array.prototype.join.call(path, '/');

}

return _.ext(path);

};

/**

* 驼峰写法转换

* @param {String} str 待转换字符串

* @return {String} 转换后的字符串

* @memberOf fis.util

* @name camelcase

* @function

*/

_.camelcase = function(str) {

var ret = '';

if (str) {

str.split(/[-_ ]+/).forEach(function(ele) {

ret += ele[0].toUpperCase() + ele.substring(1);

});

} else {

ret = str;

}

return ret;

};

/**

* 加载处理fis模块下的全部插件,如fis3-plugin-*

* @param {String} type 模块名

* @param {Function} callback 回调

* @param {Object} def 模块获取的默认值 @see fis.config.get def

* @memberOf fis.util

* @name pipe

* @function

*/

_.pipe = function(type, callback, def) {

var processors = fis.media().get('modules.' + type, def);

if (processors) {

// 兼容处理[]、String、'String1, String2, ...'的配置写法

if ('string' === typeof processors) {

processors = processors.trim().split(/\s*,\s*/);

} else if (!Array.isArray(processors)) {

processors = [processors];

}

// 过滤掉同名的插件, 没必要重复操作。

processors = processors.filter(function(item, idx, arr) {

item = item.__name || item;

return idx === _.findIndex(arr, function(target) {

target = target.__name || target;

return target === item;

});

});

// 若type为多层级(ex: 'a.b'),获取fis.config中groups[defaultGroup]['modules']下一层配置项的名称

type = type.split('.')[0];

processors.forEach(function(obj, index) {

var processor = obj.__name || obj;

var key;

if (typeof processor === 'string') {

key = type + '.' + processor;

processor = fis.require(type, processor);

} else {

key = type + '.' + index;

}

if (typeof processor === 'function') {

var settings = {};

_.assign(settings, processor.defaultOptions || processor.options || {});

_.assign(settings, fis.media().get('settings.' + key, {}));

typeof obj === 'object' && _.assign(settings, obj);

// 删除隐藏配置

delete settings.__name;

delete settings.__plugin;

delete settings.__pos;

delete settings.__isPlugin;

callback(processor, settings, key, type);

} else {

fis.log.warning('invalid processor [modules.' + key + ']');

}

});

}

};

/**

* url解析函数,规则类似require('url').parse

* @param {String} url 待解析的url

* @param {Object} opt 解析配置参数 { host|hostname, port, path, method, agent }

* @return {Object} { protocol, host, port, path, method, agent }

* @memberOf fis.util

* @name parseUrl

* @function

*/

_.parseUrl = function(url, opt) {

opt = opt || {};

url = Url.parse(url);

var ssl = url.protocol === 'https:';

opt.host = opt.host || opt.hostname || ((ssl || url.protocol === 'http:') ? url.hostname : 'localhost');

opt.port = opt.port || (url.port || (ssl ? 443 : 80));

opt.path = opt.path || (url.pathname + (url.search ? url.search : ''));

opt.method = opt.method || 'GET';

opt.agent = opt.agent || false;

return opt;

};

/**

* 下载功能实现

* @param {String} url 下载的url

* @param {Function} callback 回调

* @param {String} extract 压缩提取路径

* @param {Object} opt 配置

* @memberOf fis.util

* @name download

* @function

*/

_.download = function(url, callback, extract, opt) {

opt = _.parseUrl(url, opt || {});

var http = opt.protocol === 'https:' ? require('https') : require('http'),

name = _.md5(url, 8) + _.ext(url).ext,

tmp = fis.project.getTempPath('downloads', name),

data = opt.data;

delete opt.data;

_.write(tmp, '');

var writer = fs.createWriteStream(tmp),

http_err_handler = function(err) {

writer.destroy();

fs.unlinkSync(tmp);

var msg = typeof err === 'object' ? err.message : err;

if (callback) {

callback(msg);

} else {

fis.log.error('request error [%s]', msg);

}

},

req = http.request(opt, function(res) {

var status = res.statusCode;

res

.on('data', function(chunk) {

writer.write(chunk);

})

.on('end', function() {

if (status >= 200 && status < 300 || status === 304) {

if (extract) {

fs

.createReadStream(tmp)

.pipe(getTar().Extract({

path: extract

}))

.on('error', function(err) {

if (callback) {

callback(err);

} else {

fis.log.error('extract tar file [%s] fail, error [%s]', tmp, err);

}

})

.on('end', function() {

if (callback && (typeof callback(null, tmp, res) === 'undefined')) {

fs.unlinkSync(tmp);

}

});

} else if (callback && (typeof callback(null, tmp, res) === 'undefined')) {

fs.unlinkSync(tmp);

}

} else {

http_err_handler(status);

}

})

.on('error', http_err_handler);

});

req.on('error', http_err_handler);

if (data) {

req.write(data);

}

req.end();

};

/**

* 遵从RFC规范的文件上传功能实现

* @param {String} url 上传的url

* @param {Object} opt 配置

* @param {Object} data 要上传的formdata,可传null

* @param {String} content 上传文件的内容

* @param {String} subpath 上传文件的文件名

* @param {Function} callback 上传后的回调

* @memberOf fis.util

* @name upload

* @function

*/

_.upload = function(url, opt, data, content, subpath, callback) {

if (typeof content === 'string') {

content = new Buffer(content, 'utf8');

} else if (!(content instanceof Buffer)) {

fis.log.error('unable to upload content [%s]', (typeof content));

}

opt = opt || {};

data = data || {};

var endl = '\r\n';

var boundary = '-----np' + Math.random();

var collect = [];

_.map(data, function(key, value) {

collect.push('--' + boundary + endl);

collect.push('Content-Disposition: form-data; name="' + key + '"' + endl);

collect.push(endl);

collect.push(value + endl);

});

collect.push('--' + boundary + endl);

collect.push('Content-Disposition: form-data; name="' + (opt.uploadField || "file") + '"; filename="' + subpath + '"' + endl);

collect.push(endl);

collect.push(content);

collect.push(endl);

collect.push('--' + boundary + '--' + endl);

var length = 0;

collect.forEach(function(ele) {

if (typeof ele === 'string') {

length += new Buffer(ele).length;

} else {

length += ele.length;

}

});

opt.method = opt.method || 'POST';

opt.headers = _.assign({

'Content-Type': 'multipart/form-data; boundary=' + boundary,

'Content-Length': length

}, opt.headers || {});

opt = _.parseUrl(url, opt);

var http = opt.protocol === 'https:' ? require('https') : require('http');

var req = http.request(opt, function(res) {

var status = res.statusCode;

var body = '';

res

.on('data', function(chunk) {

body += chunk;

})

.on('end', function() {

if (status >= 200 && status < 300 || status === 304) {

callback(null, body);

} else {

callback(status);

}

})

.on('error', function(err) {

callback(err.message || err);

});

});

collect.forEach(function(d) {

req.write(d);

});

req.end();

};

/**

* 读取fis组件安装

* @param {String} name 组件名称

* @param {String} version 版本标识

* @param {Object} opt 安装配置 { remote, extract, before, error, done, }

* @memberOf fis.util

* @name install

* @function

*/

_.install = function(name, version, opt) {

version = version === '*' ? 'latest' : (version || 'latest');

var remote = opt.remote.replace(/^\/$/, '');

var url = remote + '/' + name + '/' + version + '.tar';

var extract = opt.extract || process.cwd();

if (opt.before) {

opt.before(name, version);

}

_.download(url, function(err) {

if (err) {

if (opt.error) {

opt.error(err);

} else {

fis.log.error('unable to download component [%s@%s] from [%s], error [%s].', name, version, url, err);

}

} else {

if (opt.done) {

opt.done(name, version);

}

process.stdout.write('install [' + name + '@' + version + ']\n');

var pkg = _(extract, 'package.json');

if (_.isFile(pkg)) {

var info = _.readJSON(pkg);

fs.unlinkSync(pkg);

_.map(info.dependencies || {}, function(depName, depVersion) {

_.install(depName, depVersion, opt);

});

}

}

}, extract);

};

/**

* 读取JSON文件

* @param {String} path 路径

* @return {Object} JSON文件内容JSON.parse后得到的对象

* @memberOf fis.util

* @name readJson

* @function

*/

_.readJSON = function(path) {

var json = _.read(path),

result = {};

try {

result = JSON.parse(json);

} catch (e) {

fis.log.error('parse json file[%s] fail, error [%s]', path, e.message);

}

return result;

};

/**

* 模拟linux glob文法实现,但()为捕获匹配模式

* @param {String} pattern 符合fis匹配文法的正则串

* @param {String} str 待匹配的字符串

* @param {Object} options 匹配设置参数 @see minimatch.makeRe

* @return {Boolean|RegExp} 若str参数为String则返回str是否可被pattern匹配

* 若str参数不为String,则返回正则表达式

* @memberOf fis.util

* @name glob

* @function

*/

_.glob = function(pattern, str, options) {

var regex;

// 推荐使用 ::text 和 ::image

// text 和 image 后续也许不会再支持。

if (~['::text', '::image', 'text', 'image'].indexOf(pattern)) {

regex = getFileTypeReg(pattern.replace(/^::/, ''));

// 当以下用法时,$0 应该是拿到文件路径的全部,而不是只有后缀,所以需要修改 regex。

// fis.match('::image', {

// url: '$0'

// })

//

regex = new RegExp( '^.*' + regex.source, regex.ignoreCase ? 'i' : '');

} else {

// 由于minimatch提供的glob支持中()语法不符合fis glob的需求,因此只针对()单独处理

var hasBracket = ~pattern.indexOf('(');

// 当用户配置 *.js 这种写法的时候,需要让其命中所有所有目录下面的。

if (/^(\(*?)(?!\:|\/|\(|\*\*)(.*)$/.test(pattern)) {

pattern = '**/' + pattern;

}

var special = /^(\(+?)\*\*/.test(pattern);

// support special global star

// 保留原来的 **/ 和 /** 用法,只扩展 **.ext 这种用法。

pattern = pattern.replace(/\*\*(?!\/|$)/g, '\uFFF0gs\uFFF1');

if (hasBracket) {

if (special) {

pattern = pattern.replace(/\(/g, '\uFFF0/').replace(/\)/g, '/\uFFF1');

} else {

pattern = pattern.replace(/\(/g, '\uFFF0').replace(/\)/g, '\uFFF1');

}

}

regex = minimatch.makeRe(pattern, options || {

matchBase: true,

// nocase: true

});

pattern = regex.source;

pattern = pattern.replace(/\uFFF0gs\uFFF1/g, '(?!\\.)(?=.).*');

if (hasBracket) {

if (special) {

pattern = pattern.replace(/\uFFF0\\\//g, '(').replace(/\\\/\uFFF1/g, ')');

} else {

pattern = pattern.replace(/\uFFF0/g, '(').replace(/\uFFF1/g, ')');

}

}

regex = new RegExp(pattern, regex.ignoreCase ? 'i' : '');

}

if (typeof str === 'string') {

return regex.test(str);

}

return regex;

};

/**

* 调起nohup命令

* @param {String} cmd 执行的语句

* @param {Object} options 配置参数,可传可不传 @see [child_process.exec options](https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback)

* @param {Function} callback nohup执行完毕的回调函数

* @memberOf fis.util

* @name nohup

* @function

*/

_.nohup = function(cmd, options, callback) {

if (typeof options === 'function') {

callback = options;

options = null;

}

var exec = require('child_process').exec;

if (IS_WIN) {

var cmdEscape = cmd.replace(/"/g, '""'),

file = fis.project.getTempPath('nohup-' + _.md5(cmd) + '.vbs'),

script = '';

script += 'Dim shell\n';

script += 'Set shell = Wscript.CreateObject("WScript.Shell")\n';

script += 'ret = shell.Run("cmd.exe /c start /b ' + cmdEscape + '", 0, TRUE)\n';

script += 'WScript.StdOut.Write(ret)\n';

script += 'Set shell = NoThing';

_.write(file, script);

return exec('cscript.exe /nologo "' + file + '"', options, function(error, stdout) {

if (stdout != '0') {

fis.log.error('exec command [%s] fail.', cmd);

}

fs.unlinkSync(file);

if (typeof callback === 'function') {

callback();

}

});

} else {

return exec('nohup ' + cmd + ' > /dev/null 2>&1 &', options, function(error, stdout) {

if (error !== null) {

fis.log.error('exec command [%s] fail, stdout [%s].', cmd, stdout);

}

if (typeof callback === 'function') {

callback();

}

});

}

};

/**

* 判断对象是否为null,[],{},0

* @param {Object} obj 待测对象

* @return {Boolean} 是否为空

* @memberOf fis.util

* @name isEmpty

* @function

*/

_.isEmpty = function(obj) {

if (obj == null) return true;

if (_.is(obj, 'Array')) return obj.length == 0;

for (var key in obj) {

if (obj.hasOwnProperty(key)) {

return false;

}

}

return true

};

/**

* 将 matches 规则应用到某个对象上面。

*

* @param {String} path 路径。用来与 match 规则匹配

* @param {Array} matches 规则数组

* @param {Array} allowed 可以用来过滤掉不关心的字段。

* @memberOf fis.util

* @name applyMatches

* @function

*/

_.applyMatches = function(path, matches, allowed) {

var obj = {};

matches.forEach(function(item, index) {

var properties = item.properties || {};

var keys = Object.keys(properties);

if (!keys.length) {

return;

}

var m;

var set = item.reg;

if (!Array.isArray(set)) {

set = [set];

}

set.every(function(reg) {

reg.lastIndex = 0; // reset

if (m = reg.exec(path)) {

return false;

}

return true;

});

var match = !!m;

if (match !== item.negate) {

// 当用 negate 模式时,排除命中特殊选择器

if (item.negate && ~path.indexOf(':')) {

return;

}

m = m || {

0: path

};

keys.forEach(function(key) {

// 如果指定了允许的属性名,走白名单规则规则。

if (allowed && !~allowed.indexOf(key)) {

return;

}

var value = typeof properties[key] === 'object' ? fis.util.cloneDeep(properties[key]) : properties[key];

// 将 property 中 $1 $2... 替换为本来的值

if (typeof value === 'string') {

value = value.replace(/\$(\d+|&)/g, function(_, k) {

k = k === '&' ? 0 : k;

return m[k] || '';

});

} else if (typeof(value) === 'function' &&

~[

'release',

'url',

'relative',

'moduleId'

].indexOf(key)) {

value = value.call(null, m, path);

}

// 记录是命中的 match 的位置。

obj['__' + key + 'Index'] = index;

// 调整 plugin 顺序

if (value && value.__plugin && value.__pos) {

if (!obj[key]) {

obj[key] = value;

} else {

if (!Array.isArray(obj[key])) {

obj[key] = [obj[key]];

}

var pos = value.__pos;

pos = pos === 'prepend' ? 0 : (pos === 'append' ? obj[key].length : (parseInt(pos) || 0));

obj[key].splice(pos, 0, value);

}

} else if (_.isPlainObject(value) && _.isPlainObject(obj[key]) && !value.__isPlugin && !obj[key].__isPlugin) {

fis.util.assign(obj[key], value);

} else {

obj[key] = value;

}

});

}

});

return obj;

};

 类似资料:

相关阅读

相关文章

相关问答