Added documentation on cli
This commit is contained in:
+10
-91
@@ -1,15 +1,17 @@
|
|||||||
import { parseArgs, type ParseOptions } from "@std/cli/parse-args";
|
import { type ParseOptions } from "@std/cli/parse-args";
|
||||||
import { createModule } from "$root/toolbox/module/create.ts";
|
import { createModule } from "$root/toolbox/module/create.ts";
|
||||||
import { listModules } from "$root/toolbox/module/list.ts";
|
import { listModules } from "$root/toolbox/module/list.ts";
|
||||||
|
import { CLI, displayHelp } from "$root/toolbox/cli/help.ts";
|
||||||
|
import { main } from "$root/toolbox/cli/main.ts";
|
||||||
|
|
||||||
type CLICommand = (...args: Array<string>) => void;
|
/**
|
||||||
type CLIAsyncCommand = (...args: Array<string>) => Promise<void>;
|
* CLI will use `args._`, but you can define options for global CLI.
|
||||||
interface CLI {
|
*/
|
||||||
[command: string]: CLI | CLICommand | CLIAsyncCommand;
|
|
||||||
}
|
|
||||||
|
|
||||||
const argSpec: ParseOptions = {};
|
const argSpec: ParseOptions = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure CLI commands here.
|
||||||
|
*/
|
||||||
const cli: CLI = {
|
const cli: CLI = {
|
||||||
help: () => displayHelp(cli),
|
help: () => displayHelp(cli),
|
||||||
module: {
|
module: {
|
||||||
@@ -18,87 +20,4 @@ const cli: CLI = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function displayHelp(
|
main(cli, argSpec);
|
||||||
cli: CLI | CLICommand | CLIAsyncCommand,
|
|
||||||
errorMessage?: string,
|
|
||||||
): never {
|
|
||||||
const loggingFunction = errorMessage ? console.error : console.log;
|
|
||||||
if (errorMessage) {
|
|
||||||
console.error(errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof cli == "function") {
|
|
||||||
loggingFunction("Usage:");
|
|
||||||
displayFunctionHelp(cli, loggingFunction);
|
|
||||||
} else {
|
|
||||||
loggingFunction("Commands:");
|
|
||||||
displayObjectHelp(cli, loggingFunction);
|
|
||||||
}
|
|
||||||
|
|
||||||
Deno.exit(errorMessage ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function displayObjectHelp(
|
|
||||||
cli: CLI,
|
|
||||||
loggingFunction: typeof console.log,
|
|
||||||
level: number = 1,
|
|
||||||
) {
|
|
||||||
for (const [key, value] of Object.entries(cli)) {
|
|
||||||
if (typeof value == "function") {
|
|
||||||
displayFunctionHelp(value, loggingFunction, level);
|
|
||||||
} else {
|
|
||||||
loggingFunction(`${" ".repeat(level * 2)}${key}`);
|
|
||||||
displayObjectHelp(value, loggingFunction, level + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function displayFunctionHelp(
|
|
||||||
cli: CLICommand | CLIAsyncCommand,
|
|
||||||
loggingFunction: typeof console.log,
|
|
||||||
level: number = 1,
|
|
||||||
) {
|
|
||||||
const command = cli.name;
|
|
||||||
const matched = cli.toString().match(/(?<=^\()[^\)]+(?=\))/);
|
|
||||||
const args = matched?.[0].split(",").map((arg) => `<${arg.trim()}>`);
|
|
||||||
loggingFunction(
|
|
||||||
`${" ".repeat(level * 2)}${command} ${args?.join(" ") ?? ""}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function runCommand(
|
|
||||||
commands: Array<string | number>,
|
|
||||||
cli: CLI,
|
|
||||||
): never | {
|
|
||||||
command: CLICommand | CLIAsyncCommand;
|
|
||||||
args: Array<string | number>;
|
|
||||||
} {
|
|
||||||
if (commands.length == 0) {
|
|
||||||
displayHelp(cli, `No command provided.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const command = commands.shift()!.toString();
|
|
||||||
|
|
||||||
if (cli[command] == undefined) {
|
|
||||||
displayHelp(cli, `Command "${command}" doesn't exist.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof cli[command] == "object") {
|
|
||||||
return runCommand(commands, cli[command]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cli[command].length != commands.length) {
|
|
||||||
displayHelp(cli[command], `Wrong usage of command "${command}".`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { command: cli[command], args: commands };
|
|
||||||
}
|
|
||||||
|
|
||||||
function main() {
|
|
||||||
const argv = parseArgs(Deno.args, argSpec);
|
|
||||||
const { command, args } = runCommand(argv._, cli);
|
|
||||||
|
|
||||||
command(...args.map((element) => element.toString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
main();
|
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import {
|
||||||
|
CLI,
|
||||||
|
CLIAsyncCommand,
|
||||||
|
CLICommand,
|
||||||
|
displayHelp,
|
||||||
|
} from "$root/toolbox/cli/help.ts";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the command given by arguments.
|
||||||
|
* @param commands The given arguments.
|
||||||
|
* @param cli The CLI (sub-)configuration object.
|
||||||
|
* @returns The command or the help if commands are not valid.
|
||||||
|
*/
|
||||||
|
export function runCommand(
|
||||||
|
commands: Array<string | number>,
|
||||||
|
cli: CLI,
|
||||||
|
): never | {
|
||||||
|
command: CLICommand | CLIAsyncCommand;
|
||||||
|
args: Array<string | number>;
|
||||||
|
} {
|
||||||
|
if (commands.length == 0) {
|
||||||
|
displayHelp(cli, `No command provided.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const command = commands.shift()!.toString();
|
||||||
|
|
||||||
|
if (cli[command] == undefined) {
|
||||||
|
displayHelp(cli, `Command "${command}" doesn't exist.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof cli[command] == "object") {
|
||||||
|
return runCommand(commands, cli[command]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cli[command].length != commands.length) {
|
||||||
|
displayHelp(cli[command], `Wrong usage of command "${command}".`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { command: cli[command], args: commands };
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
export type CLICommand = (...args: Array<string>) => void;
|
||||||
|
export type CLIAsyncCommand = (...args: Array<string>) => Promise<void>;
|
||||||
|
export interface CLI {
|
||||||
|
[command: string]: CLI | CLICommand | CLIAsyncCommand;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the help message for the CLI.
|
||||||
|
* @param cli The CLI (sub-)configuration object.
|
||||||
|
* @param errorMessage The error message to display.
|
||||||
|
*/
|
||||||
|
export function displayHelp(
|
||||||
|
cli: CLI | CLICommand | CLIAsyncCommand,
|
||||||
|
errorMessage?: string,
|
||||||
|
): never {
|
||||||
|
const loggingFunction = errorMessage ? console.error : console.log;
|
||||||
|
if (errorMessage) {
|
||||||
|
console.error(errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof cli == "function") {
|
||||||
|
loggingFunction("Usage:");
|
||||||
|
displayFunctionHelp(cli, loggingFunction);
|
||||||
|
} else {
|
||||||
|
loggingFunction("Commands:");
|
||||||
|
displayObjectHelp(cli, loggingFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
Deno.exit(errorMessage ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display object help recursivelly.
|
||||||
|
* @param cli The CLI (sub-)configuration object.
|
||||||
|
* @param loggingFunction The logging function to use.
|
||||||
|
* @param level The tab level.
|
||||||
|
*/
|
||||||
|
function displayObjectHelp(
|
||||||
|
cli: CLI,
|
||||||
|
loggingFunction: typeof console.log,
|
||||||
|
level: number = 1,
|
||||||
|
) {
|
||||||
|
for (const [key, value] of Object.entries(cli)) {
|
||||||
|
if (typeof value == "function") {
|
||||||
|
displayFunctionHelp(value, loggingFunction, level);
|
||||||
|
} else {
|
||||||
|
loggingFunction(`${" ".repeat(level * 2)}${key}`);
|
||||||
|
displayObjectHelp(value, loggingFunction, level + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display function help.
|
||||||
|
* @param cli The CLI function.
|
||||||
|
* @param loggingFunction The logging function to use.
|
||||||
|
* @param level The tab level.
|
||||||
|
*/
|
||||||
|
function displayFunctionHelp(
|
||||||
|
cli: CLICommand | CLIAsyncCommand,
|
||||||
|
loggingFunction: typeof console.log,
|
||||||
|
level: number = 1,
|
||||||
|
) {
|
||||||
|
const command = cli.name;
|
||||||
|
const matched = cli.toString().match(/(?<=^\()[^\)]+(?=\))/);
|
||||||
|
const args = matched?.[0].split(",").map((arg) => `<${arg.trim()}>`);
|
||||||
|
loggingFunction(
|
||||||
|
`${" ".repeat(level * 2)}${command} ${args?.join(" ") ?? ""}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { parseArgs, ParseOptions } from "@std/cli/parse-args";
|
||||||
|
import { runCommand } from "$root/toolbox/cli/command.ts";
|
||||||
|
import { CLI } from "$root/toolbox/cli/help.ts";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the CLI.
|
||||||
|
* @param cli The CLI configuration object.
|
||||||
|
* @param argSpec The Parse options for args.
|
||||||
|
*/
|
||||||
|
export function main(cli: CLI, argSpec: ParseOptions) {
|
||||||
|
const argv = parseArgs(Deno.args, argSpec);
|
||||||
|
const { command, args } = runCommand(argv._, cli);
|
||||||
|
command(...args.map((element) => element.toString()));
|
||||||
|
}
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* Creates a new module.
|
||||||
|
* @param name The module name.
|
||||||
|
*/
|
||||||
export async function createModule(name: string): Promise<void> {
|
export async function createModule(name: string): Promise<void> {
|
||||||
if (!name.match(/^[a-zA-Z0-9](?:(?:\-(?!\-))?[a-zA-Z0-9]*)*[a-zA-Z0-9]$/)) {
|
if (!name.match(/^[a-zA-Z0-9](?:(?:\-(?!\-))?[a-zA-Z0-9]*)*[a-zA-Z0-9]$/)) {
|
||||||
console.error("Module names must be in kebab case.");
|
console.error("Module names must be in kebab case.");
|
||||||
@@ -58,16 +62,32 @@ export async function createModule(name: string): Promise<void> {
|
|||||||
formatter.output();
|
formatter.output();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new file at given path.
|
||||||
|
* @param path The file path.
|
||||||
|
* @param content The file content.
|
||||||
|
* @returns The creation promise.
|
||||||
|
*/
|
||||||
function createFile(path: string, content: string): Promise<void> {
|
function createFile(path: string, content: string): Promise<void> {
|
||||||
console.log(`Creating file ${path}...`);
|
console.log(`Creating file ${path}...`);
|
||||||
return Deno.writeTextFile(path, content);
|
return Deno.writeTextFile(path, content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new directory at given path.
|
||||||
|
* @param path The directory path.
|
||||||
|
* @returns The creation promise.
|
||||||
|
*/
|
||||||
function createDir(path: string): Promise<void> {
|
function createDir(path: string): Promise<void> {
|
||||||
console.log(`Creating directory ${path}...`);
|
console.log(`Creating directory ${path}...`);
|
||||||
return Deno.mkdir(path);
|
return Deno.mkdir(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the index content.
|
||||||
|
* @param _name The module capitalized name.
|
||||||
|
* @returns The index content.
|
||||||
|
*/
|
||||||
function getIndexContent(_name: string) {
|
function getIndexContent(_name: string) {
|
||||||
return `
|
return `
|
||||||
import makeIndex from "$root/defaults/makeIndex.ts";
|
import makeIndex from "$root/defaults/makeIndex.ts";
|
||||||
@@ -75,6 +95,11 @@ function getIndexContent(_name: string) {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the partials index content.
|
||||||
|
* @param name The module capitalized name.
|
||||||
|
* @returns The partials index content.
|
||||||
|
*/
|
||||||
function getPartialIndexContent(name: string) {
|
function getPartialIndexContent(name: string) {
|
||||||
return `
|
return `
|
||||||
import {
|
import {
|
||||||
@@ -93,6 +118,11 @@ function getPartialIndexContent(name: string) {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the props content.
|
||||||
|
* @param name The module capitalized name.
|
||||||
|
* @returns The props content.
|
||||||
|
*/
|
||||||
function getPropsContent(name: string) {
|
function getPropsContent(name: string) {
|
||||||
return `
|
return `
|
||||||
import { AppProperties } from "$root/defaults/interfaces.ts";
|
import { AppProperties } from "$root/defaults/interfaces.ts";
|
||||||
@@ -111,6 +141,11 @@ function getPropsContent(name: string) {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the API example content.
|
||||||
|
* @param _name The module capitalized name.
|
||||||
|
* @returns The API example content.
|
||||||
|
*/
|
||||||
function getApiExampleContent(_name: string) {
|
function getApiExampleContent(_name: string) {
|
||||||
return `
|
return `
|
||||||
import { Handlers } from "$fresh/server.ts";
|
import { Handlers } from "$fresh/server.ts";
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* List all modules of PolyMPR.
|
||||||
|
*/
|
||||||
export async function listModules(): Promise<void> {
|
export async function listModules(): Promise<void> {
|
||||||
for await (const path of Deno.readDir("routes/(apps)")) {
|
for await (const path of Deno.readDir("routes/(apps)")) {
|
||||||
if (path.isDirectory) {
|
if (path.isDirectory) {
|
||||||
|
|||||||
Reference in New Issue
Block a user