TypeScriptで作成したコンポーネントの依存関係をさっと見るためのツール
$ cd <project_folder>
$ yarn build
$ cd -
$ node parser.js referenced <project_folder>/tsconfig.tsbuildinfo --parent=organisms/component.vue
| const fs = require('fs') | |
| const usage = "node parser.js referenced|exported path --reverse(-r) --depth(-d)=0 --last(-l) --include(-i)=path --exclude(-e)=path --parent(-p)=path --json(-j)" | |
| let options = { | |
| target: "referencedMap", | |
| isReverse: false, | |
| isPrintLastOnly: false, | |
| isPrintJson: false, | |
| printDepth: null, | |
| parent: "", | |
| includeConditions: ["/src/"], | |
| excludeConditions: ["/node_modules/"] | |
| } | |
| let path = '' | |
| let arguments = process.argv.slice(2) | |
| if (arguments.length === 0) { | |
| console.log(usage) | |
| return | |
| } | |
| if (arguments[0] === 'exported') { | |
| options.target = 'exportedModulesMap' | |
| } | |
| let isUpdateIncludeConditions = false | |
| let isUpdateExcludeConditions = false | |
| arguments.slice(1).forEach(argument => { | |
| if (argument[0] === '-') { | |
| let splitted = argument.split("=") | |
| switch (splitted[0]) { | |
| case '--reverse': | |
| case '-r': | |
| options.isReverse = true | |
| break | |
| case '--last': | |
| case '-l': | |
| options.isPrintLastOnly = true | |
| break | |
| case '--json': | |
| case '-j': | |
| options.isPrintJson = true | |
| break | |
| case '--depth': | |
| case '-d': | |
| options.printDepth = parseInt(splitted[1]) | |
| break | |
| case '--parent': | |
| case '-p': | |
| options.parent = splitted[1] | |
| break | |
| case '--include': | |
| case '-i': | |
| if (!isUpdateIncludeConditions) options.includeConditions = [] | |
| if (splitted.length > 1) options.includeConditions.push(splitted[1]) | |
| isUpdateIncludeConditions = true | |
| break | |
| case '--exclude': | |
| case '-e': | |
| if (!isUpdateExcludeConditions) options.excludeConditions = [] | |
| if (splitted.length > 1) options.excludeConditions.push(splitted[1]) | |
| isUpdateExcludeConditions = true | |
| break | |
| } | |
| } else { | |
| path = argument | |
| } | |
| }) | |
| if (path.length === 0) { | |
| console.log(usage) | |
| return | |
| } | |
| // よみこみ | |
| function readFile(path) { | |
| let buff = null | |
| try { | |
| buff = fs.readFileSync(path, "utf8"); | |
| } | |
| catch(e) { | |
| console.log('ERROR: readFile', e.message); | |
| } | |
| return buff | |
| } | |
| // 絞込み | |
| function isInclude(filePath) { | |
| const includes = options.includeConditions.length === 0 || options.includeConditions.some(w => filePath.indexOf(w) > -1) | |
| const excludes = options.excludeConditions.some(w => filePath.indexOf(w) > -1) | |
| return includes && !excludes | |
| } | |
| function narrowing(map) { | |
| for (let k in map) { | |
| if (isInclude(k)) { | |
| map[k] = map[k].filter(path => isInclude(path)) | |
| } else { | |
| delete map[k] | |
| } | |
| } | |
| } | |
| // 反転 | |
| function reverse(map) { | |
| let rmap = {} | |
| for (let k in map) { | |
| let parent = k | |
| map[k].forEach(child => { | |
| if (child in rmap) { | |
| rmap[child].push(parent) | |
| } else { | |
| rmap[child] = [parent] | |
| } | |
| }); | |
| } | |
| return rmap | |
| } | |
| // ツリー | |
| function generateTree(map) { | |
| let ret = {} | |
| // {parent: { child: {}}} | |
| for (let k in map) { | |
| if (k.indexOf(options.parent) > -1) { | |
| ret[k] = search(map, k, 0) | |
| } | |
| } | |
| return ret | |
| } | |
| let memo = {} | |
| let count = 0 | |
| function search(map, key, level) { | |
| let ret = {} | |
| count++ | |
| // if (level >= options.printDepth) return ret | |
| let childMap = map[key] | |
| for (let i in childMap) { | |
| let child = childMap[i] | |
| // 同じやつがいる | |
| if (child === key) continue | |
| if (child in memo) { | |
| ret[child] = memo[child] | |
| } else { | |
| memo[child] = {} | |
| memo[child] = search(map, child, level + 1) | |
| ret[child] = memo[child] | |
| } | |
| } | |
| return ret | |
| } | |
| // プリント | |
| function printMap(map, level) { | |
| let blank = '' | |
| const noPrintNext = level === options.printDepth | |
| if (level > 0) { | |
| blank = '|'.repeat(level-1) + '-' | |
| } | |
| for (let key in map) { | |
| let childMap = map[key] | |
| let currentBlank = blank | |
| if(noPrintNext) { | |
| if (Object.keys(childMap).length) { | |
| currentBlank = currentBlank.replace('-', '+') | |
| } else { | |
| currentBlank = currentBlank.replace('-', ' ') | |
| } | |
| } else if (!Object.keys(childMap).length) { | |
| currentBlank = currentBlank.replace('-', ' ') | |
| } | |
| console.log(`${currentBlank}${key}`) | |
| if (!noPrintNext) { | |
| printMap(childMap, level + 1) | |
| } | |
| } | |
| } | |
| // 最後だけ、プリント | |
| let children = {} | |
| function searchChild(map) { | |
| for (let key in map) { | |
| let childMap = map[key] | |
| if (Object.keys(map[key]).length) { | |
| searchChild(childMap) | |
| } else { | |
| children[key] = key | |
| } | |
| } | |
| } | |
| const buildinfo = JSON.parse(readFile(path)) | |
| let targetMap = buildinfo.program[options.target] | |
| narrowing(targetMap) | |
| if (options.isReverse) { | |
| targetMap = reverse(targetMap) | |
| } | |
| let treeMap = generateTree(targetMap) | |
| // console.log(count) | |
| if (options.isPrintLastOnly) { | |
| searchChild(treeMap) | |
| sortedLastComponents = Object.keys(children).sort() | |
| if (options.isPrintJson) { | |
| console.log(JSON.stringify(sortedLastComponents)) | |
| } else { | |
| for (let c of sortedLastComponents) { | |
| console.log(c) | |
| } | |
| } | |
| } else if (options.isPrintJson) { | |
| console.log(JSON.stringify(treeMap)) | |
| } else { | |
| printMap(treeMap, 0) | |
| } |