UNPKG

json-treeify

Version:

json-treeify: Get tree string(├└│─) via json, support browser|node, browser none dependencies!

152 lines (146 loc) 7.16 kB
let path = require('path'); let fs = require('fs'); let {isNill, isObject, isRegExp, isString} = require('../type-of');//这里不适用node-util检查,因为官方提示很多isXXX方法会过时 let fileExistSync = fs.existsSync;// || path.existsSync;//node@0.8.0 let colorful = require('./colorful'); let REGEXP_IGNORE_DIR = /(\.git|\.svn|\.idea|node_modules|bower_components)/g; let REGEXP_IGNORE_FILE = /(Thumbs\.db|\.DS_store)/g; /** * 将文件目录转换为json对象 * @param {String} rootPath 目标根目录 * @param {Object} options * {Object} options.exclude * {RegExp} options.exclude.all,file,directory 正则表达式去匹配 “排除项” * {Object} options.extChars * {String} options.extChars.file,directory 文件节点,文件夹节点的末尾字符 * @returns {JSON|String} */ function dir2Json(rootPath, options) { // eslint-disable-line // root rootPath = path.normalize(rootPath) || './'; if(!fileExistSync(rootPath)){ throw new Error(`The path '${rootPath}' is not existed!`); } let statRootPath = fs.statSync(rootPath); if(!statRootPath.isDirectory()){ return 'not a directory'; } // options options = options || {}; let exclude = options.exclude; let extChars = options.extChars; let preChars = options.preChars; let maxDepth = parseInt(options.maxDepth); maxDepth = maxDepth || 0; if(!maxDepth || maxDepth>5){ // “潜在的长时等待”提示 console.log(colorful.warn('Potential long-time-wait: You`d better set a max depth (less than 5) to access directories,or it will spend a long time!')); } // options.exclude: 排除某些文件或文件夹 if(isNill(exclude)){ exclude = { // all:/pom\.xml/g, file: REGEXP_IGNORE_FILE, directory: REGEXP_IGNORE_DIR, outExcludeDir: false //是否将exclude的dir进行输出 }; }else{ exclude = isObject(exclude) ? exclude : {}; exclude.file = exclude.file || REGEXP_IGNORE_FILE; exclude.directory = exclude.directory || REGEXP_IGNORE_DIR; exclude.outExcludeDir = exclude.outExcludeDir || false; } let filterGlobal = exclude.all || false; let filterFile = exclude.file; let filterDir = exclude.directory; let outExcludeDir = exclude.outExcludeDir; filterGlobal = isRegExp(filterGlobal) ? filterGlobal : isString(filterGlobal) ? new RegExp(filterGlobal, 'g') : false; filterFile = isRegExp(filterFile) ? filterFile : isString(filterFile) ? new RegExp(filterFile, 'g') : false; filterDir = isRegExp(filterDir) ? filterDir : isString(filterDir) ? new RegExp(filterDir, 'g') : false; // options.extChars:文件或文件夹后面的符号,比如一般文件夹后面的符号是'/' if(isNill(extChars)){ extChars = { file: '', directory: '' }; }else{ extChars = extChars || {}; extChars.file = isString(extChars.file) ? extChars.file : ''; extChars.directory = isString(extChars.directory) ? extChars.directory : ''; } // options.preChars:文件或文件夹后面的符号,比如一般文件夹后面的符号是'/' if(isNill(preChars)){ preChars = { file: '', directory: '' }; }else{ preChars = preChars || {}; preChars.file = isString(preChars.file) ? preChars.file : ''; preChars.directory = isString(preChars.directory) ? preChars.directory : ''; } let fPre = preChars.file; let dPre = preChars.directory; let fExt = extChars.file; let dExt = extChars.directory; // result json let json = {}; // do main function travel(pathArg, far, curDepth){ if(!maxDepth || (maxDepth && curDepth<=maxDepth)){ let dirs; try{ // todo 目录太多时候崩溃未考虑 dirs = fs.readdirSync(pathArg);//第一级子目录(文件|文件夹) }catch(e){ console.log(colorful.error(`Occur error with read dirs from '${pathArg}',${e.toString()}`)); dirs = false; } if(dirs && dirs.length){ if(dirs.length>=30){ console.log(`${colorful.warn('Too many files|directories was found, type-in an exact target to simplify or reduce result.') }\n ${colorful.yellow('path: ')}${colorful.cyan(`'${pathArg.replace(/\\/g, '/')}'`)}`); } // todo 子项太多未考虑 dirs.forEach((item) => { let curPath = path.join(pathArg, item); let stat;// 获取fsStatInfo,用以后面的判断 try{ // todo 读大文件时候情况未考虑 stat = fs.statSync(curPath);// 读取文件信息 }catch(e1){ console.log(colorful.warn(`Skip to read stat info from '${curPath}'\n${e1.toString()}`)); stat = false; } // 通配检查 if(!filterGlobal || (!!filterGlobal && !item.match(filterGlobal))){ // 文件夹类型的值为“其对应的实际对象”,文件类型值为"file", // 读取stat失败的类型对应值为"unreadableStatType",其他的为"unknownStatType" if(!stat){ far[item] = 'unreadableStatType'; return false; } if(stat.isFile()){//文件 if(!filterFile || (!!filterFile && !item.match(filterFile))){ far[fPre + item + fExt] = 'file'; } }else if(stat.isDirectory()){//文件夹 let dPropName; if(!filterDir || (!!filterDir && !item.match(filterDir))){ dPropName = dPre + item + dExt; far[dPropName] = {}; travel(curPath, far[dPropName], curDepth+1); }else{ if(!!filterDir && item.match(filterDir) && outExcludeDir){ dPropName = `${dPre + item} (ignored)${dExt}`; far[dPropName] = {}; } } }else{ // console.log("非文件也非目录:",item);//比如文件夹快捷方式,文件快捷方式 unix之下的另外的东西,管道,套接字等等 // 这里不读超链接的原因是,避免有可能出现的死循环 far[item] = 'unknownStatType'; } } }); } } } travel(rootPath, json, 1); return isObject.isEmptyOwn(json) ? null : json; } module.exports = dir2Json;