-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcli.ts
170 lines (144 loc) · 5.43 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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
import * as commander from "commander";
import { ExitCode } from "../codes";
import { FileSystem, IFileSystem } from "../fileSystem";
import { ILogger, parseVerbosity, wrapLoggerForVerbosity } from "../logger";
import { IMain, main } from "../main";
import { globAllAsync, IGlobAllAsync } from "../utils/glob";
import { defaultValue } from "../utils/values";
import { getExcludes } from "./exclude";
import { printCliVersions } from "./version";
/**
* Dependencies to run the CLI.
*/
export interface ICliDependencies {
/**
* Raw argv-style string args from a command-line.
*/
argv: ReadonlyArray<string>;
/**
* System to read and write files, if not an fs-based default.
*/
fileSystem?: IFileSystem;
/**
* Finds file names from glob patterns, if not a glob-based default.
*/
globber?: IGlobAllAsync;
/**
* Logs information, if not the console.
*/
logger?: ILogger;
/**
* Main method to pass parsed arguments into.
*/
main?: IMain;
}
/**
* Commander-parsed arguments passed in.
*/
interface IParsedArguments {
/**
* Raw args to be used as file globs.
*/
args: ReadonlyArray<string>;
/**
* Base or root directory to ignore from the beginning of file paths, such as "src/", if not "".
*/
baseDirectory?: string;
/**
* File glob(s) to exclude.
*/
exclude?: string | ReadonlyArray<string>;
/**
* Output language(s) to convert to.
*/
language?: string | ReadonlyArray<string>;
/**
* Budgie configuration project, if provided.
*/
project?: string;
/**
* Namespace before path names, such as "Budgie", if not "".
*/
namespace?: string;
/**
* TypeScript configuration project, if provided.
*/
tsconfig?: string;
/**
* Minimum importance level of logs to print.
*/
verbosity?: string;
/**
* Displays help text via the logger.
*/
help(): void;
}
/**
* Parses raw string arguments and, if they're valid, calls to a main method.
*
* @param dependencies Raw string arguments and any system dependency overrides.
* @returns Promise for the result of the main method.
*/
export const cli = async (dependencies: ICliDependencies): Promise<ExitCode> => {
const { argv } = dependencies;
const fileSystem = defaultValue(dependencies.fileSystem, () => new FileSystem());
const globber = defaultValue(dependencies.globber, () => globAllAsync);
const logger = defaultValue(dependencies.logger, () => console);
const mainExecutor = defaultValue(dependencies.main, () => main);
const command = new commander.Command()
.usage("[options] <file ...> --language [language]")
.option("-b, --base-directory [base-directory]", "base directory to ignore from the beginning of file paths")
.option("-e, --exclude [exclude...]", "file glob(s) to exclude")
.option("-l, --language [language...]", "language(s) to convert to")
.option("-l, --project [project]", "budgie.json project metadata file")
.option("-n, --namespace [namespace]", "namespace before output path names")
.option("-t, --tsconfig [tsconfig]", "(TypeScript only) configuration project")
.option("-v, --verbosity [verbosity]", `Minimum logged verbosity level: "error" (default) or "log"`)
.option("-V, --version", "output the CLI and Budgie version numbers")
.on("--help", (): void => {
logger.log();
logger.log(" Basic Budgie conversion:");
logger.log();
logger.log(" $ budgie --language Python file.bg");
logger.log();
logger.log(" Converting a TypeScript project to Budgie, then to Python and Ruby:");
logger.log();
logger.log(" $ budgie --language Python --language Ruby --tsconfig ./tsconfig ./*.ts");
logger.log();
logger.log(" Converting a TypeScript project to Budgie, then to C#, replacing the 'src' path with 'Budgie':");
logger.log();
logger.log(" $ budgie --base-directory src/ --language C# --namespace Budgie --tsconfig ./tsconfig ./**/*.ts");
logger.log();
})
.parse(argv as string[]) as IParsedArguments;
if ({}.hasOwnProperty.call(command, "version")) {
await printCliVersions(logger);
return ExitCode.Ok;
}
if (command.args.length === 0) {
command.help();
return ExitCode.Ok;
}
const [includes, excludes] = await Promise.all([globber(command.args), getExcludes(command.exclude, globber)]);
const filePaths = new Set(includes);
for (const exclude of excludes) {
filePaths.delete(exclude);
}
const languageNames = command.language !== undefined && typeof command.language === "string" ? [command.language] : command.language;
const project = command.project === "false" ? undefined : command.project === undefined ? "budgie.json" : command.project;
const verbosity = parseVerbosity(command.verbosity);
if (verbosity === undefined) {
logger.error(`Unknown verbosity requested: '${command.verbosity}'.`);
return ExitCode.Error;
}
return mainExecutor({
baseDirectory: command.baseDirectory,
filePaths,
fileSystem,
languageNames,
logger: wrapLoggerForVerbosity(logger, verbosity),
namespace: command.namespace,
project,
typescriptConfig: command.tsconfig,
});
};