UNPKG

@pega/inquirer-fuzzy-path

Version:

Fuzzy file/directory search and select prompt for inquirer.

128 lines (112 loc) 3.1 kB
import path from 'path'; import fs from 'fs'; import util from 'util'; // eslint-disable-next-line import/extensions import Choices from 'inquirer/lib/objects/choices.js'; import InquirerAutocomplete from 'inquirer-autocomplete-prompt'; import stripAnsi from 'strip-ansi'; import style from 'ansi-styles'; import fuzzy from 'fuzzy'; const readdir = util.promisify(fs.readdir); function getPaths( rootPath, pattern, excludePath, excludeFilter, itemType, defaultItem, depthLimit, ) { const fuzzOptions = { pre: style.green.open, post: style.green.close, }; async function listNodes(nodePath, level) { try { if (excludePath(nodePath)) { return []; } const nodes = await readdir(nodePath); const currentNode = (itemType !== 'file' ? [nodePath] : []); if ( nodes.length > 0 && (depthLimit === undefined || level >= 0) ) { const nodesWithPath = nodes.map( nodeName => listNodes( path.join(nodePath, nodeName), depthLimit ? level - 1 : undefined, ), ); const subNodes = await Promise.all(nodesWithPath); return subNodes.reduce((acc, val) => acc.concat(val), currentNode); } return currentNode; } catch (err) { if (err.code === 'ENOTDIR') { return itemType !== 'directory' ? [nodePath] : []; } return []; } } const nodes = listNodes(rootPath, depthLimit); const filterPromise = nodes.then( (nodeList) => { const preFilteredNodes = !excludeFilter ? nodeList : nodeList.filter(node => !excludeFilter(node)); const filteredNodes = fuzzy .filter(pattern || '', preFilteredNodes, fuzzOptions) .map(e => e.string); if (!pattern && defaultItem) { filteredNodes.unshift(defaultItem); } return filteredNodes; }, ); return filterPromise; } class InquirerFuzzyPath extends InquirerAutocomplete { constructor(question, rl, answers) { const { depthLimit, itemType = 'any', rootPath = '.', excludePath = () => false, excludeFilter = false, } = question; const questionBase = Object.assign( {}, question, { source: (_, pattern) => getPaths( rootPath, pattern, excludePath, excludeFilter, itemType, question.default, depthLimit, ), }, ); super(questionBase, rl, answers); } search(searchTerm) { return super.search(searchTerm).then(() => { this.currentChoices.getChoice = (choiceIndex) => { const choice = Choices.prototype.getChoice.call(this.currentChoices, choiceIndex); this.searching = false; return { value: stripAnsi(choice ? choice.value : ''), name: stripAnsi(choice ? choice.name : ''), short: stripAnsi(choice ? choice.name : ''), }; }; }); } onSubmit(line) { super.onSubmit(stripAnsi(line)); } } export default InquirerFuzzyPath;