1 | #!/usr/bin/env node
|
2 | "use strict";
|
3 | Object.defineProperty(exports, "__esModule", { value: true });
|
4 | const path = require("path");
|
5 | const Api = require("sywac/api");
|
6 | const platformUtils = require("@kano/kit-app-shell-core/lib/util/platform");
|
7 | const process_state_1 = require("@kano/kit-app-shell-core/lib/process-state");
|
8 | const rc_1 = require("@kano/kit-app-shell-core/lib/rc");
|
9 | const check_1 = require("@kano/kit-app-shell-core/lib/check");
|
10 | const types_1 = require("@kano/kit-app-shell-core/lib/types");
|
11 | const tmp = require("@kano/kit-app-shell-core/lib/tmp");
|
12 | const prettyBytes = require("pretty-bytes");
|
13 | const chalk_1 = require("chalk");
|
14 | class 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 | }
|
441 | const cli = new CLI(process.argv.slice(2));
|
442 | cli.start();
|
443 |
|
\ | No newline at end of file |