UNPKG

@pnp/cli-microsoft365

Version:

Manage Microsoft 365 and SharePoint Framework projects on any platform

186 lines • 7.53 kB
#!/usr/bin/env node import fs from 'fs'; import omelette from 'omelette'; import os from 'os'; import path from 'path'; import url from 'url'; import { cli } from './cli/cli.js'; const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); class Autocomplete { constructor() { this.commands = {}; this.init(); } init() { if (fs.existsSync(Autocomplete.autocompleteFilePath)) { try { const data = fs.readFileSync(Autocomplete.autocompleteFilePath, 'utf-8'); this.commands = JSON.parse(data); } catch { } } this.omelette = omelette('m365_comp|m365|microsoft365'); this.omelette.on('complete', this.handleAutocomplete.bind(this)); this.omelette.init(); } handleAutocomplete(fragment, data) { let replies = {}; let allWords = []; if (data.fragment === 1) { replies = Object.keys(this.commands); } else { allWords = data.line.split(/\s+/).slice(1, -1); // build array of words to use as a path to retrieve completion // options from the commands tree const words = allWords .filter((e, i) => { if (e.indexOf('-') !== 0) { // if the word is not an option check if it's not // option's value, eg. --output json, in which case // the suggestion should be command options return i === 0 || allWords[i - 1].indexOf('-') !== 0; } else { // remove all options but last one return i === allWords.length - 1; } }); let accessor = new Function('_', "return _['" + (words.join("']['")) + "']"); try { replies = accessor(this.commands); // if the last word is an option without autocomplete // suggest other options from the same command if (words[words.length - 1].indexOf('-') === 0 && !Array.isArray(replies)) { accessor = new Function('_', "return _['" + (words.filter(w => w.indexOf('-') !== 0).join("']['")) + "']"); replies = accessor(this.commands); replies = Object.keys(replies); } } catch { } } if (!replies) { replies = []; } if (!Array.isArray(replies)) { replies = Object.keys(replies); } // remove options that already have been used replies = replies.filter(r => r.indexOf('-') !== 0 || allWords.indexOf(r) === -1); data.reply(replies); } generateShCompletion() { const commandsInfo = this.getCommandsInfo(); fs.writeFileSync(Autocomplete.autocompleteFilePath, JSON.stringify(commandsInfo)); } setupShCompletion() { this.omelette.setupShellInitFile(); } getClinkCompletion() { const cmd = this.getCommandsInfo(); const lua = ['local parser = clink.arg.new_parser']; const functions = {}; this.buildClinkForBranch(cmd, functions, 'm365'); Object.keys(functions).forEach(k => { functions[k] = functions[k].replace(/#([^#]+)#/g, (m, p1) => functions[p1]); }); lua.push('local m365_parser = ' + functions['m365'], '', 'clink.arg.register_parser("m365", m365_parser)', 'clink.arg.register_parser("microsoft365", m365_parser)'); return lua.join(os.EOL); } buildClinkForBranch(branch, functions, luaFunctionName) { if (!Array.isArray(branch)) { const keys = Object.keys(branch); keys.forEach(k => { if (Object.keys(branch[k]).length > 0) { this.buildClinkForBranch(branch[k], functions, this.getLuaFunctionName(`${luaFunctionName}_${k}`)); } }); } const parser = []; parser.push(`parser({`); let printingArgs = false; if (Array.isArray(branch)) { branch.sort().forEach((c, i) => { const separator = i < branch.length - 1 ? ',' : ''; parser.push(`"${c}"${separator}`); }); } else { const keys = Object.keys(branch); if (keys.find(c => c.indexOf('-') === 0)) { printingArgs = true; const tmp = []; keys.sort().forEach((k) => { if (Object.keys(branch[k]).length > 0) { tmp.push(`"${k}"..#${this.getLuaFunctionName(`${luaFunctionName}_${k}`)}#`); } else { tmp.push(`"${k}"`); } }); parser.push(`},${tmp.join(', ')}`); } else { keys.sort().forEach((k, i) => { const separator = i < keys.length - 1 ? ',' : ''; parser.push(`"${k}"..#${this.getLuaFunctionName(`${luaFunctionName}_${k}`)}#${separator}`); }); } } parser.push(`${printingArgs ? '' : '}'})`); functions[luaFunctionName] = parser.join(''); } getLuaFunctionName(functionName) { return functionName.replace(/-/g, '_'); } getCommandsInfo() { const commandsInfo = {}; const commands = cli.commands; commands.forEach(c => { Autocomplete.processCommand(c.name, c, commandsInfo); if (c.aliases) { c.aliases.forEach(a => Autocomplete.processCommand(a, c, commandsInfo)); } }); return commandsInfo; } static processCommand(commandName, commandInfo, autocomplete) { const chunks = commandName.split(' '); let parent = autocomplete; for (let i = 0; i < chunks.length; i++) { const current = chunks[i]; if (!parent[current]) { if (i < chunks.length - 1) { parent[current] = {}; } else { // last chunk, add options const optionsArr = commandInfo.options .map(o => o.short) .concat(commandInfo.options.map(o => o.long)) .filter(o => o) .map(o => o.length === 1 ? `-${o}` : `--${o}`); optionsArr.push('--help'); optionsArr.push('-h'); const optionsObj = {}; optionsArr.forEach(o => { const optionName = o.replace(/^-+/, ''); const option = commandInfo.options.filter(opt => opt.long === optionName || opt.short === optionName)[0]; if (option && option.autocomplete) { optionsObj[o] = option.autocomplete; } else { optionsObj[o] = {}; } }); parent[current] = optionsObj; } } parent = parent[current]; } } } Autocomplete.autocompleteFilePath = path.join(__dirname, `..${path.sep}commands.json`); export const autocomplete = new Autocomplete(); //# sourceMappingURL=autocomplete.js.map