-
Notifications
You must be signed in to change notification settings - Fork 1
/
cli.ts
111 lines (99 loc) · 3.04 KB
/
cli.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import { docopt, fs, path } from "./deps.ts";
export { docopt } from "./deps.ts";
//TODO: generalize this so it will work for any repo
const repoVersionRegExp = /shah\/vscode-team\/v?(?<version>\d+\.\d+\.\d+)\//;
//TODO: merge this into CommandHandlerContext.version
export function determineVersion(
importMetaURL: string,
isMain?: boolean,
): string {
const fileURL = importMetaURL.startsWith("file://")
? importMetaURL.substr("file://".length)
: importMetaURL;
if (fs.existsSync(fileURL)) {
return `v0.0.0-local${isMain ? ".main" : ""}`;
}
const matched = importMetaURL.match(repoVersionRegExp);
if (matched) {
return `v${matched.groups!["version"]}`;
}
return `v0.0.0-remote(no version tag/branch in ${importMetaURL})`;
}
export interface CommandHandlerContext {
readonly calledFromMetaURL: string;
readonly cliOptions: docopt.DocOptions;
readonly isDryRun: boolean;
readonly isVerbose: boolean;
readonly shouldOverwrite: boolean;
}
export interface CommandHandler<T extends CommandHandlerContext> {
(ctx: T): Promise<true | void>;
}
export class TypicalCommandHandlerContext implements CommandHandlerContext {
constructor(
readonly calledFromMetaURL: string,
readonly cliOptions: docopt.DocOptions,
) {
}
get isDryRun(): boolean {
const { "--dry-run": dryRun } = this.cliOptions;
return dryRun ? true : false;
}
get isVerbose(): boolean {
const { "--verbose": verbose } = this.cliOptions;
return verbose ? true : false;
}
get shouldOverwrite(): boolean {
const { "--overwrite": overwrite } = this.cliOptions;
return overwrite ? true : false;
}
forceExtension(forceExtn: string, fileName: string): string {
const fileUrlPrefix = "file://";
if (fileName.startsWith(fileUrlPrefix)) {
fileName = fileName.substr(fileUrlPrefix.length);
}
const extn = path.extname(fileName);
if (extn && extn.length > 0) {
return fileName.substr(0, fileName.length - extn.length) +
forceExtn;
}
return fileName + forceExtn;
}
}
export async function versionHandler(
ctx: CommandHandlerContext,
): Promise<true | void> {
const { "--version": version } = ctx.cliOptions;
if (version) {
console.log(determineVersion(ctx.calledFromMetaURL));
return true;
}
}
export const commonHandlers = [versionHandler];
export async function CLI<T extends CommandHandlerContext>(
docoptSpec: string,
handlers: CommandHandler<T>[],
prepareContext: (options: docopt.DocOptions) => T,
): Promise<void> {
try {
const options = docopt.default(docoptSpec);
const context = prepareContext(options);
let handled: true | void;
for (const handler of handlers) {
handled = await handler(context);
if (handled) break;
}
if (!handled) {
for (const handler of commonHandlers) {
handled = await handler(context);
if (handled) break;
}
}
if (!handled) {
console.error("Unable to handle validly parsed docoptSpec:");
console.dir(options);
}
} catch (e) {
console.error(e.message);
}
}