appcenter-cli
Version:
Command line tool for Visual Studio App Center
135 lines (134 loc) • 5.1 kB
JavaScript
;
// Command dispatcher
Object.defineProperty(exports, "__esModule", { value: true });
exports.finder = void 0;
const path = require("path");
const fs = require("fs");
const util_1 = require("util");
const debug = require("debug")("appcenter-cli:util:commandline:command-finder");
// Helpers for file system checks
function toFullPath(dispatchRoot, pathParts) {
return path.join.apply(null, [dispatchRoot].concat(pathParts));
}
function checkStats(dispatchRoot, pathParts, check) {
try {
const filePath = toFullPath(dispatchRoot, pathParts);
debug(`Checking stats for ${filePath}`);
const stats = fs.statSync(filePath);
return check(stats);
}
catch (err) {
if (err.code === "ENOENT") {
return false;
}
throw err;
}
}
function stripExtension(name) {
const extLen = path.extname(name).length;
if (extLen > 0) {
return name.slice(0, -extLen);
}
return name;
}
function isDir(dispatchRoot, pathParts) {
return checkStats(dispatchRoot, pathParts, (s) => s.isDirectory());
}
function normalizeCommandNames(command) {
return command.map((part) => part.toLowerCase());
}
const legalCommandRegex = /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/;
function isLegalCommandName(commandName) {
debug(`Checking legality of '${commandName}'`);
return legalCommandRegex.test(commandName) && commandName !== "lib";
}
function splitCommandLine(command) {
let partitionPoint = command.findIndex((cmd) => !isLegalCommandName(cmd));
if (partitionPoint === -1) {
partitionPoint = command.length;
}
return [command.slice(0, partitionPoint), command.slice(partitionPoint)];
}
// Helper functions to construct results
function commandNotFound(commandParts) {
debug(`No command found at '${commandParts.join(" ")}'`);
return {
found: false,
isCategory: false,
commandPath: null,
commandParts,
unusedArgs: [],
};
}
function commandFound(commandPath, commandParts, unusedArgs) {
debug(`Command '${commandParts.join(" ")}' found at ${commandPath}`);
return {
found: true,
isCategory: false,
commandPath,
commandParts: commandParts.map(stripExtension),
unusedArgs,
};
}
function categoryFound(commandPath, commandParts, unusedArgs) {
debug(`Category '${commandParts.join(" ")}' found at ${commandPath}`);
return {
found: true,
isCategory: true,
commandPath,
commandParts,
unusedArgs,
};
}
function finder(dispatchRoot) {
if (!isDir(dispatchRoot, [])) {
throw new Error("Invalid dispatch root");
}
return function commandFinder(commandLineArgs) {
debug(`Looking for command ${util_1.inspect(commandLineArgs)}`);
const [command, args] = splitCommandLine(commandLineArgs);
if (command.length === 0) {
return categoryFound(toFullPath(dispatchRoot, []), [], commandLineArgs);
}
function findFile(commandDir, commandName) {
debug(`Looking for '${commandName}' in directory '${toFullPath(dispatchRoot, commandDir)}'`);
if (commandDir.length > 0 && !isDir(dispatchRoot, commandDir)) {
return null;
}
const fullCommand = commandDir.concat([commandName]);
if (checkStats(dispatchRoot, fullCommand, (stats) => stats.isDirectory())) {
return [toFullPath(dispatchRoot, fullCommand), fullCommand, false];
}
// Have to look through the directory so that we
// can ignore any potential file extensions.
const files = fs.readdirSync(toFullPath(dispatchRoot, commandDir));
const matching = files.filter((file) => path.parse(file).name === commandName);
if (matching.length > 1) {
throw new Error(`Ambiguous match for command '${commandLineArgs.join(" ")}'`);
}
if (matching.length === 0) {
return null;
}
const commandParts = commandDir.concat([matching[0]]);
const commandPath = toFullPath(dispatchRoot, commandDir.concat([matching[0]]));
return [commandPath, commandParts, true];
}
while (command.length > 0) {
const commandName = normalizeCommandNames(command.slice(-1))[0];
const commandDir = normalizeCommandNames(command.slice(0, -1));
const result = findFile(commandDir, commandName);
if (result !== null) {
if (result[2]) {
return commandFound(result[0], result[1], args);
}
return categoryFound(result[0], result[1], args);
}
// Not found, push the last arg in command name into unused pile.
args.unshift(command.pop());
debug(`Not found, unused args = ${args}, new command = ${command}`);
}
// Got here, nothing found
return commandNotFound(commandLineArgs);
};
}
exports.finder = finder;