-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.js
More file actions
210 lines (190 loc) · 5.58 KB
/
index.js
File metadata and controls
210 lines (190 loc) · 5.58 KB
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
const path = require("path");
const xdg = require("@folder/xdg");
const winston = require("winston");
const ora = require("ora");
const PluginManager = require("./lib/plugin/manager");
const ErrorHandler = require("./lib/error-handler");
const InputService = require("./lib/io/input-service");
const { createLogger } = require("./lib/io/logs");
const OutputService = require("./lib/io/output-service");
const { registerCommands } = require("./lib/commander");
const { Builder } = require("./lib/container");
const ConfigCommand = require("./lib/config/command");
const PluginCommand = require("./lib/plugin/command");
const ConfigService = require("./lib/config/service");
const PluginService = require("./lib/plugin/service");
const SecretService = require("./lib/secret/service");
const ERROR_HANDLER = Symbol("errorHandler");
const CONTAINER = Symbol("container");
const LOGGER = Symbol("logger");
const PROGRAM = Symbol("program");
const SPINNER = Symbol("spinner");
/**
* The whole shebang.
*/
class Miles {
/**
* Create a new Miles instance.
*
* @param {commander.Command} program - The Commander object.
* @param {string} [configDir] - Configuration directory.
*/
constructor(program, configDir) {
this[PROGRAM] = program;
this.configDir = path.normalize(configDir || Miles.getDefaultConfigDir());
}
/**
* Gets the configuration directory according to the operating system.
*
* @return {string} - The directory name.
*/
static getDefaultConfigDir() {
return xdg({ subdir: "miles" }).config;
}
/**
* @return {ErrorHandler} the error handler.
*/
get errorHandler() {
return this[ERROR_HANDLER];
}
/**
* @return {Container} the dependency injection container.
*/
get container() {
return this[CONTAINER];
}
/**
* @return {winston.Logger} the Winston logger.
*/
get logger() {
return this[LOGGER];
}
/**
* @return {commander.Command} the Commander object.
*/
get program() {
return this[PROGRAM];
}
/**
* @return {ora.Ora} the ora spinner.
*/
get spinner() {
return this[SPINNER];
}
/**
* The journey of a thousand miles begins with a single step.
*/
async start() {
// This first try block is considered the "bootstrap" tasks
try {
// We need to register the global options, like logging verbosity.
this.addGlobalOptions();
// Load up the Ora spinner, uses stderr.
this.loadSpinner();
// Load up the Winston logging, which uses stderr, too.
this.loadLogger();
// Loads up the error handler.
this.loadErrorHandler();
} catch (bootstrapError) {
// Logging isn't set up yet, so just write error to stderr and exit.
console.error(bootstrapError);
process.exit(1);
return; // Only needed in unit tests where we've stubbed process.exit.
}
try {
// Build the dependency injection container.
this[CONTAINER] = await this.buildContainer();
// Register commands with Commander.
await registerCommands(this[PROGRAM], this[CONTAINER]);
} catch (startupError) {
// Any errors which occur during the batch load can be handled here.
this.handleError(startupError);
}
}
/**
* Build the dependency injection container.
*
* @return {Container} The built container.
*/
async buildContainer() {
const builder = new Builder(this[LOGGER]);
const pluginService = await PluginService.create(this.configDir);
const pluginManager = await PluginManager.create(pluginService, builder);
builder.constant("logger", this[LOGGER]);
builder.constant("commander", this[PROGRAM]);
builder.constant("spinner", this[SPINNER]);
builder.constant("plugin.manager", pluginManager);
builder.constant("plugin.service", pluginService);
builder.register("io.input-service", () => new InputService());
builder.register("io.output-service", async (c) => {
const spinner = await c.get("spinner");
return new OutputService(spinner);
});
builder.register(
"config.service",
async () => await ConfigService.create(this.configDir)
);
builder.register(
"secret.service",
async () => await SecretService.create(this.configDir)
);
builder.register("config.command", ConfigCommand.create, [
"commander-visitor",
]);
builder.register("plugin.command", PluginCommand.create, [
"commander-visitor",
]);
return await builder.build();
}
/**
* Parses the command line arguments and executes the program.
*/
async parseCommand() {
try {
await this[PROGRAM].parseAsync();
} catch (e) {
this.handleError(e);
}
}
/**
* Loads the ora spinner.
*/
loadSpinner() {
this[SPINNER] = ora({ spinner: "dots2" });
}
/**
* Loads the Winston logger.
*
* We need to parse the verbosity so we can provide it to the stderr logger.
*/
loadLogger() {
this[PROGRAM].parse();
this[LOGGER] = createLogger(this[SPINNER], this[PROGRAM].opts().verbose);
}
/**
* Loads the error handler.
*/
loadErrorHandler(logger) {
this[ERROR_HANDLER] = new ErrorHandler(this[SPINNER], this[LOGGER]);
this[ERROR_HANDLER].register();
}
/**
* Adds global options to Commander.
*/
addGlobalOptions() {
this[PROGRAM].passThroughOptions();
this[PROGRAM].option(
"-v, --verbose",
"Increases the verbosity of logs sent to stderr. Can be specified up to three times.",
(dummy, previous) => previous + 1,
4
);
}
/**
* @ignore
*/
handleError(e) {
this[ERROR_HANDLER].handleError(e);
}
}
module.exports = Miles;