UNPKG

18.3 kBJavaScriptView Raw
1#!/usr/bin/env node
2"use strict";
3Object.defineProperty(exports, "__esModule", { value: true });
4const path = require("path");
5const Api = require("sywac/api");
6const platformUtils = require("@kano/kit-app-shell-core/lib/util/platform");
7const process_state_1 = require("@kano/kit-app-shell-core/lib/process-state");
8const rc_1 = require("@kano/kit-app-shell-core/lib/rc");
9const check_1 = require("@kano/kit-app-shell-core/lib/check");
10const types_1 = require("@kano/kit-app-shell-core/lib/types");
11const tmp = require("@kano/kit-app-shell-core/lib/tmp");
12const prettyBytes = require("pretty-bytes");
13const chalk_1 = require("chalk");
14class CLI {
15 constructor(processArgv) {
16 this.processState = process_state_1.processState;
17 this.processArgv = processArgv;
18 }
19 static parseEnv(sywac) {
20 return sywac
21 .string('--env, -e', {
22 desc: 'Target environment',
23 defaultValue: 'development',
24 });
25 }
26 static parseAppRoot(sywac) {
27 return sywac
28 .positional('<app=./>', {
29 params: [{
30 required: true,
31 desc: 'Path to the root of the app',
32 coerce: path.resolve,
33 }],
34 });
35 }
36 static parseAppBuild(sywac) {
37 return sywac
38 .positional('<build>', {
39 params: [{
40 required: true,
41 desc: 'Path to the built app',
42 coerce: path.resolve,
43 }],
44 });
45 }
46 static parseOverrideAppConfig(sywac) {
47 return sywac
48 .array('-C, --override-app-config <values..>', {
49 desc: 'Override app configuration (syntax key.subkey=value)',
50 coerce: (strings) => {
51 const overrides = {};
52 strings.forEach((s) => {
53 const [key, value] = s.split('=');
54 const keyComponents = key.split('.');
55 let currentLevel = overrides;
56 while (keyComponents.length > 1) {
57 const keyComponent = keyComponents.shift();
58 currentLevel[keyComponent] = currentLevel[keyComponent] || {};
59 currentLevel = currentLevel[keyComponent];
60 }
61 let v = value;
62 if (value === 'true') {
63 v = true;
64 }
65 else if (value === 'false') {
66 v = false;
67 }
68 else if (value.match(/^[0-9]+$/)) {
69 v = parseInt(value, 10);
70 }
71 const topLevelKey = keyComponents[0];
72 currentLevel[topLevelKey] = v;
73 });
74 return overrides;
75 },
76 });
77 }
78 static parseRequireAppConfig(sywac) {
79 return sywac
80 .array('-R, --require-config <path>', {
81 desc: 'Require app configuration file',
82 coerce: (strings) => {
83 return strings.map((s) => path.resolve(s));
84 },
85 });
86 }
87 static checkRequireAppConfig(sywac) {
88 return sywac
89 .check((argv) => {
90 if (!argv.R)
91 return;
92 return argv.R.reduce((p, path) => {
93 return p.then(() => {
94 return rc_1.RcLoader.check(path).then((exists) => {
95 if (!exists) {
96 throw new Error(`-R: App config file does not exist: ${path}`);
97 }
98 });
99 });
100 }, Promise.resolve(true));
101 });
102 }
103 static applyStyles(sywac) {
104 return sywac.style({
105 group: (s) => chalk_1.default.cyan.bold(s),
106 desc: (s) => chalk_1.default.white(s),
107 hints: (s) => chalk_1.default.dim(s),
108 flagsError: (s) => chalk_1.default.red(s),
109 });
110 }
111 static patchSywacOptions(sywac, forcedOptions) {
112 const originalOptions = sywac._addOptionType.bind(sywac);
113 sywac._addOptionType = (flags, opts, type) => originalOptions(flags, Object.assign({}, opts, forcedOptions), type);
114 return {
115 dispose() {
116 sywac._addOptionType = originalOptions;
117 },
118 };
119 }
120 start() {
121 this.startedAt = Date.now();
122 return this.firstPass()
123 .then((result) => {
124 const [, platform] = result.argv._;
125 if (platform) {
126 return this.secondPass(platform);
127 }
128 console.log(result.output);
129 return this.end(result.code);
130 });
131 }
132 end(code) {
133 this.duration = Date.now() - this.startedAt;
134 const totalTime = this.duration / 1000;
135 const totalMinutes = Math.floor(totalTime / 60);
136 const totalSeconds = (totalTime % 60).toFixed(2);
137 const msg = `Done in ${totalMinutes}m${totalSeconds.toString().padStart(5, '0')}s.`;
138 if (this.reporter) {
139 this.reporter.onInfo(msg);
140 }
141 process.exit(code);
142 }
143 setTask(task) {
144 if (!this.processState) {
145 return;
146 }
147 task.catch((e) => {
148 this.processState.setFailure(e);
149 return this.end(1);
150 });
151 }
152 mountReporter(argv) {
153 if (argv.quiet) {
154 return Promise.resolve();
155 }
156 let p;
157 if (process.stdout.isTTY) {
158 p = Promise.resolve().then(() => require('./reporters/ora'));
159 }
160 else {
161 p = Promise.resolve().then(() => require('./reporters/console'));
162 }
163 return p
164 .then((ReporterModule) => {
165 this.reporter = (new ReporterModule.default());
166 this.processState.on('step', ({ message = '' }) => this.reporter.onStep(message));
167 this.processState.on('success', ({ message = '' }) => this.reporter.onSuccess(message));
168 this.processState.on('failure', ({ message = new Error('') }) => this.reporter.onFailure(message));
169 this.processState.on('warning', ({ message = '' }) => this.reporter.onWarning(message));
170 this.processState.on('info', ({ message = '' }) => this.reporter.onInfo(message));
171 });
172 }
173 runDoctor() {
174 return Promise.resolve().then(() => require('@kano/kit-app-shell-core/lib/checks/index')).then((checkModule) => {
175 return check_1.runChecks(checkModule.default);
176 });
177 }
178 displayDoctorResults(results) {
179 const DOCTOR_SUCCESS_DEFAULT = 'All good';
180 const DOCTOR_WARNING_DEFAULT = 'Warning';
181 const DOCTOR_FAILURE_DEFAULT = 'Error';
182 let failed = false;
183 results.forEach((result) => {
184 const message = result.message ? result.message.replace(/\n/g, '\n ') : null;
185 if (result.status === check_1.CheckResultSatus.Success) {
186 process_state_1.processState.setSuccess(`${result.title}: ${message || DOCTOR_SUCCESS_DEFAULT}`);
187 }
188 else if (result.status === check_1.CheckResultSatus.Warning) {
189 process_state_1.processState.setWarning(`${result.title}: ${message || DOCTOR_WARNING_DEFAULT}`);
190 }
191 else {
192 process_state_1.processState.setFailure(`${result.title}: ${message || DOCTOR_FAILURE_DEFAULT}`);
193 failed = true;
194 }
195 });
196 this.end(failed ? 1 : 0);
197 }
198 firstPass() {
199 const sywac = new Api();
200 const commands = ['run', 'build', 'sign', 'configure'];
201 sywac.configure({ name: 'kash' });
202 commands.forEach((cmd) => {
203 sywac.command(`${cmd} <platform> --help`, {
204 desc: `Show help for the ${cmd} command`,
205 run: (argv) => this.secondPass(argv.platform),
206 });
207 });
208 sywac.command('doctor [platform] --help', {
209 desc: 'Show help for the doctor command',
210 run: (argv) => {
211 if (!argv.platform) {
212 return this.runDoctor();
213 }
214 return this.secondPass(argv.platform);
215 },
216 });
217 sywac.command('cache', {
218 desc: 'Manage the cache files used by kash',
219 setup: (setup) => {
220 setup.command('status', {
221 desc: 'Displays the status of the cache directory',
222 run: () => {
223 return tmp.status()
224 .then((status) => {
225 let total = 0;
226 Object.keys(status).forEach((key) => {
227 const st = status[key];
228 total += st.size;
229 console.log(` ${key} size: ${prettyBytes(st.size)}`);
230 });
231 console.log(` Total size: ${prettyBytes(total)}`);
232 if (total !== 0) {
233 console.log(`Run ${chalk_1.default.cyan('kash cache clear')} to free up some space`);
234 }
235 else {
236 console.log('No cache to clear');
237 }
238 })
239 .then(() => this.end(0));
240 },
241 });
242 setup.command('clear', {
243 desc: 'Deletes the contents of the cache directory',
244 run: (argv) => {
245 return this.mountReporter(argv)
246 .then(() => tmp.status())
247 .then((status) => {
248 const total = Object.keys(status).reduce((acc, key) => {
249 return acc + status[key].size;
250 }, 0);
251 return tmp.clear()
252 .then(() => process_state_1.processState.setSuccess(`Cleared ${prettyBytes(total)}`));
253 });
254 },
255 });
256 setup.command('dir', {
257 desc: 'Displays the cache directory location',
258 run: () => {
259 console.log(tmp.getRootPath());
260 },
261 });
262 },
263 });
264 sywac.command('open config', {
265 desc: 'Open the location of your configuration',
266 run: () => {
267 return Promise.resolve().then(() => require('./open-config')).then((openConfig) => openConfig.default());
268 },
269 });
270 sywac.help('-h, --help');
271 sywac.showHelpByDefault();
272 sywac.version();
273 CLI.applyStyles(sywac);
274 return sywac.parse(this.processArgv);
275 }
276 secondPass(platformId) {
277 const sywac = new Api();
278 return platformUtils.loadPlatformKey(platformId, 'cli')
279 .then((platformCli) => {
280 const platform = {
281 cli: platformCli || {},
282 };
283 sywac.command('build <platform>', {
284 desc: 'build the application',
285 setup: (s) => {
286 CLI.parseAppRoot(s);
287 CLI.parseEnv(s);
288 CLI.parseOverrideAppConfig(s);
289 CLI.parseRequireAppConfig(s);
290 CLI.checkRequireAppConfig(s);
291 s.array('--resources')
292 .string('--out, -o', {
293 desc: 'Output directory',
294 coerce: path.resolve,
295 required: true,
296 })
297 .number('--build-number, -n', {
298 aliases: ['n', 'build-number', 'buildNumber'],
299 defaultValue: 0,
300 })
301 .boolean('--bundle-only', {
302 aliases: ['bundle-only', 'bundleOnly'],
303 defaultValue: false,
304 })
305 .boolean('--skip-minify-html', {
306 aliases: ['skip-minify-html', 'skipMinifyHtml'],
307 defaultValue: false,
308 })
309 .boolean('--skip-babel', {
310 aliases: ['skip-babel', 'skipBabel'],
311 defaultValue: false,
312 })
313 .boolean('--skip-terser', {
314 aliases: ['skip-terser', 'skipTerser'],
315 defaultValue: false,
316 });
317 const sywacPatch = CLI.patchSywacOptions(s, {
318 group: platform.cli.group || 'Platform: ',
319 });
320 platformUtils.registerOptions(s, platform, types_1.ICommand.Build);
321 sywacPatch.dispose();
322 },
323 run: (argv) => {
324 this.mountReporter(argv);
325 return Promise.resolve().then(() => require('./command')).then((runCommand) => {
326 const task = runCommand.default('build', platformId, argv);
327 this.setTask(task);
328 return task;
329 });
330 },
331 });
332 sywac.command('run <platform>', {
333 desc: 'run the application',
334 setup: (s) => {
335 CLI.parseAppRoot(s);
336 CLI.parseEnv(s);
337 CLI.parseOverrideAppConfig(s);
338 CLI.parseRequireAppConfig(s);
339 CLI.checkRequireAppConfig(s);
340 const sywacPatch = CLI.patchSywacOptions(s, {
341 group: platform.cli.group || 'Platform: ',
342 });
343 platformUtils.registerOptions(s, platform, types_1.ICommand.Run);
344 sywacPatch.dispose();
345 },
346 run: (argv) => {
347 this.mountReporter(argv);
348 return Promise.resolve().then(() => require('./command')).then((runCommand) => {
349 const task = runCommand.default('run', platformId, argv);
350 this.setTask(task);
351 return task;
352 });
353 },
354 });
355 sywac.command('sign <platform>', {
356 desc: 'sign an application package',
357 setup: (s) => {
358 CLI.parseAppBuild(s);
359 CLI.parseEnv(s);
360 const sywacPatch = CLI.patchSywacOptions(s, {
361 group: platform.cli.group || 'Platform: ',
362 });
363 platformUtils.registerOptions(s, platform, types_1.ICommand.Sign);
364 sywacPatch.dispose();
365 },
366 run: (argv) => {
367 this.mountReporter(argv);
368 return Promise.resolve().then(() => require('./command')).then((runCommand) => {
369 const task = runCommand.default('sign', platformId, argv);
370 this.setTask(task);
371 return task;
372 });
373 },
374 });
375 sywac.command('configure <platform>', {
376 desc: 'configure kash',
377 setup: (s) => {
378 const sywacPatch = CLI.patchSywacOptions(s, {
379 group: platform.cli.group || 'Platform: ',
380 });
381 platformUtils.registerOptions(s, platform, types_1.ICommand.Configure);
382 sywacPatch.dispose();
383 },
384 run: (argv) => {
385 this.mountReporter(argv);
386 return Promise.resolve().then(() => require('./configure')).then((configure) => {
387 const task = configure.default(platformId);
388 this.setTask(task);
389 return task;
390 });
391 },
392 });
393 sywac.command('doctor <platform>', {
394 desc: 'run system checks for a platform',
395 setup: (s) => {
396 const sywacPatch = CLI.patchSywacOptions(s, {
397 group: platform.cli.group || 'Platform: ',
398 });
399 platformUtils.registerOptions(s, platform, types_1.ICommand.Doctor);
400 sywacPatch.dispose();
401 },
402 run: (argv) => {
403 this.mountReporter(argv);
404 return Promise.resolve().then(() => require('./doctor')).then((doctor) => {
405 const task = doctor.default(platformId)
406 .then((checks) => check_1.runChecks(checks))
407 .then((results) => this.displayDoctorResults(results));
408 this.setTask(task);
409 return task;
410 });
411 },
412 });
413 sywac.boolean('--quiet, -q', {
414 desc: 'Silence all outputs',
415 defaultValue: false,
416 });
417 sywac.boolean('--verbose', {
418 desc: 'Displays verbose logs',
419 defaultValue: false,
420 });
421 sywac.help('-h, --help');
422 sywac.showHelpByDefault();
423 sywac.version();
424 sywac.configure({ name: 'kash' });
425 const sywacPatcher = CLI.patchSywacOptions(sywac, {
426 group: platform.cli.group || 'Platform: ',
427 });
428 platformUtils.registerCommands(sywac, platform);
429 sywacPatcher.dispose();
430 CLI.applyStyles(sywac);
431 return sywac.parse(this.processArgv)
432 .then((result) => {
433 if (result.output.length) {
434 console.log(result.output);
435 }
436 this.end(result.code);
437 });
438 });
439 }
440}
441const cli = new CLI(process.argv.slice(2));
442cli.start();
443//# sourceMappingURL=cli.js.map
\No newline at end of file