make-command-runnable
Version:
Call a module's exported functions directly from the command line, with arguments.
136 lines (118 loc) • 4.85 kB
JavaScript
var argv = require("yargs").argv;
var Bluebird = require("bluebird");
var path = require("path");
// since old versions of node can't do Object.assign :(
var mergeObjects = require("./utils/mergeObjects");
var requiringModule = module.parent.parent;
module.exports = function(inputOptions) {
var defaultOptions = {
printOutputFrame: true
};
var options = mergeObjects(defaultOptions, inputOptions || {});
function printOutput(returnValue) {
Bluebird.resolve(returnValue)
.catch(function() {
return null;
});
}
function printError(error) {
if (options.printOutputFrame) {
console.log("--------make-runnable-error--------");
}
console.log(error);
if (options.printOutputFrame) {
console.log("------------------------------------");
}
}
// if the requiring file is being run directly from the command line
if (require.main === requiringModule) {
var targetPropertyFound = false;
function runFuncWithArgs(func, unnamedArgs) {
var namedArgs = Object.assign({}, argv);
delete namedArgs._;
delete namedArgs.$0;
if (Object.keys(namedArgs).length > 0 && unnamedArgs.length > 0) {
console.error(
"You cannot specify both named and unnamed arguments to the function"
);
process.exit(1);
} else if (Object.keys(namedArgs).length > 0) {
//we have named arguments. Let's pass these as an object to the function
printOutput(func(namedArgs));
} else if (unnamedArgs.length > 0) {
//we have 1 or more unnamed arguments. let's pass those as individual args to function
printOutput(func.apply(null, unnamedArgs));
} else {
//no extra arguments given to pass to function. let's just run it
printOutput(func());
}
}
// if the module exports a function, then evidently no target property could be specified
// all the rest of the target properties must then be the unnamed arguments to the function
if (requiringModule.exports instanceof Function) {
targetPropertyFound = true;
runFuncWithArgs(requiringModule.exports, argv._.slice(0));
} else if (argv._.length > 0) {
// there must be at least one argument which specifies the target property to run/show
var targetProperty = argv._[0];
if (targetProperty in requiringModule.exports) {
targetPropertyFound = true;
// if the target is a function, we need to run it with any provided args
if (requiringModule.exports[targetProperty] instanceof Function) {
runFuncWithArgs(
requiringModule.exports[targetProperty].bind(
requiringModule.exports
),
argv._.slice(1)
);
} else {
// if the target isn't a function, we simply print it
printOutput(requiringModule.exports[targetProperty]);
}
}
}
// the specified first arg didn't match up to any of the exported properties
if (!targetPropertyFound) {
var validProperties = Object.keys(requiringModule.exports);
if (validProperties.length === 0) {
printError(
"The module you're trying to make runnable doesn't export anything."
);
} else {
// let's give an example of how this should be used
var validFunctionProperties = validProperties.filter(function(prop) {
return requiringModule.exports[prop] instanceof Function;
});
// ideally the examples should use a function so it makes sense
var egProperty =
validFunctionProperties.length > 0
? validFunctionProperties[0]
: validProperties[0];
// the example changes based on whether the property name contains a space
var egPropertyHasSpace = egProperty.indexOf(" ") > 0; //if example property
var egPropertyAsArg = egPropertyHasSpace
? '"' + egProperty + '"'
: egProperty;
var example = "node " + argv.$0 + " " + egPropertyAsArg + " xyz";
example += " → ";
example +=
"module.exports" +
(egPropertyHasSpace
? "[" + egPropertyAsArg + "]"
: "." + egProperty) +
'("xyz")';
var errorString =
"One of the following must be specified as an argument, to run/print the corresponding function/property:";
errorString += "\n\n" + Object.keys(requiringModule.exports);
errorString += "\n\nExample:";
errorString += "\n\t" + example;
printError(errorString);
}
}
} else {
// needed with nested make-runnables
delete require.cache[path.join(__dirname, "engine.js")];
delete require.cache[path.join(__dirname, "index.js")];
delete require.cache[path.join(__dirname, "custom.js")];
}
};