actionhero
Version:
actionhero.js is a multi-transport API Server with integrated cluster capabilities and delayed tasks
210 lines (180 loc) • 6.26 kB
text/typescript
import * as path from "path";
import * as fs from "fs";
import * as optimist from "optimist";
import { spawn } from "child_process";
interface RunnerInputs {
[propName: string]: any;
}
interface Runner {
name: string;
inputs: RunnerInputs;
}
// cannot import this until we know where to load from!
function determineProjectRoot() {
let projectRoot = process.cwd();
if (process.env.project_root) {
projectRoot = process.env.project_root;
} else if (process.env.projectRoot) {
projectRoot = process.env.projectRoot;
} else if (process.env.PROJECT_ROOT) {
projectRoot = process.env.PROJECT_ROOT;
}
return projectRoot;
}
const projectRoot = determineProjectRoot();
(async () => {
const formatParams = (runner: Runner) => {
const params: any = {};
if (!runner.inputs) {
runner.inputs = {};
}
Object.keys(runner.inputs).forEach((inputName) => {
const collection = runner.inputs[inputName];
let value = optimist.argv[inputName];
if (
collection.default &&
(value === undefined || value === null || value === "")
) {
if (typeof collection.default === "function") {
value = collection.default();
} else {
value = collection.default;
}
console.log(`using default value of \`${value}\` for \`${inputName}\``);
}
if (
collection.required === true &&
(value === undefined || value === null || value === "")
) {
console.error(
`Error: \`${inputName}\` is a required command-line argument to the method \`${runner.name}\``
);
// console.log(`You can provide it via \`${runner.name} --${inputName}=value\``)
process.exit(1);
}
params[inputName] = value;
});
return params;
};
const handleUnbuiltProject = async (commands: Array<string>) => {
try {
// when generating the project from scratch, we cannot rely on the normal initializers
const ExportedRunnerClasses = require(path.join(
__dirname,
"methods",
commands.join(path.sep)
));
if (Object.keys(ExportedRunnerClasses).length > 1) {
throw new Error("actionhero CLI files should only export one method");
}
const runner = new ExportedRunnerClasses[
Object.keys(ExportedRunnerClasses)[0]
]();
const params = formatParams(runner);
await runner.run({ params: params });
setTimeout(process.exit, 100, 0);
} catch (error) {
console.error(error.toString());
process.exit(1);
}
};
const handleMethod = async (commands: Array<string>) => {
console.log("₋₋₋₋₋₋₋₋₋₋₋₋₋₋₋₋₋₋₋₋₋₋₋₋₋₋₋₋₋₋₋₋₋₋₋₋₋₋");
console.log(`ACTIONHERO COMMAND >> ${commands.join(" ")}`);
console.log("⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻");
const { config, Process } = require(path.join(__dirname, "..", "index"));
const actionHeroProcess = new Process();
await actionHeroProcess.initialize();
try {
let ExportedClasses;
let p: string;
p = path.join(__dirname, "methods", commands.join(path.sep) + ".js");
if (fs.existsSync(p) && config.general.cliIncludeInternal !== false) {
ExportedClasses = require(p);
}
p = path.join(__dirname, "methods", commands.join(path.sep) + ".ts");
if (fs.existsSync(p) && config.general.cliIncludeInternal !== false) {
ExportedClasses = require(p);
}
if (!ExportedClasses) {
config.general.paths.cli.forEach((cliPath: string) => {
p = path.join(cliPath, commands.join(path.sep) + ".js");
if (fs.existsSync(p)) {
ExportedClasses = require(p);
}
p = path.join(cliPath, commands.join(path.sep) + ".ts");
if (fs.existsSync(p)) {
ExportedClasses = require(p);
}
});
}
if (!ExportedClasses) {
for (const pluginName in config.plugins) {
if (config.plugins[pluginName].cli !== false) {
const pluginPath = config.plugins[pluginName].path;
p = path.join(pluginPath, "bin", commands.join(path.sep) + ".js");
if (fs.existsSync(p)) {
ExportedClasses = require(p);
}
p = path.join(
pluginPath,
"dist",
"bin",
commands.join(path.sep) + ".js"
);
if (fs.existsSync(p)) {
ExportedClasses = require(p);
}
}
}
}
if (!ExportedClasses) {
console.error(
`Error: \`${commands.join(" ")}\` is not a method I can perform.`
);
console.error("run `actionhero help` to learn more");
setTimeout(process.exit, 500, 1);
} else if (Object.keys(ExportedClasses).length > 1) {
throw new Error("actionhero CLI files should only export one method");
} else {
const runner = new ExportedClasses[Object.keys(ExportedClasses)[0]]();
const params = formatParams(runner);
const toStop = await runner.run({ params: params });
if (toStop || toStop === null || toStop === undefined) {
setTimeout(process.exit, 500, 0);
}
}
} catch (error) {
console.error(error);
process.exit(1);
}
};
const commands = [];
if (!optimist.argv._ || optimist.argv._.length === 0) {
commands.push("start");
}
optimist.argv._.forEach(function (arg: string) {
commands.push(arg);
});
const argsMatch = (a: Array<string>, b: Array<string>) => {
if (a.length !== b.length) {
return false;
}
for (const i in a) {
if (a[i] !== b[i]) {
return false;
}
}
return true;
};
if (argsMatch(commands, ["version"])) {
handleUnbuiltProject(commands);
} else if (argsMatch(commands, ["generate"])) {
handleUnbuiltProject(commands);
} else if (argsMatch(commands, ["generate", "plugin"])) {
handleUnbuiltProject(commands);
} else {
handleMethod(commands);
}
})();