forked from asyncapi/generator
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cli.js
executable file
·163 lines (147 loc) · 5.97 KB
/
cli.js
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
#!/usr/bin/env node
const path = require('path');
const os = require('os');
const program = require('commander');
const xfs = require('fs.extra');
const packageInfo = require('./package.json');
const Generator = require('./lib/generator');
const Watcher = require('./lib/watcher');
const { isLocalTemplate, isFilePath } = require('./lib/utils');
const red = text => `\x1b[31m${text}\x1b[0m`;
const magenta = text => `\x1b[35m${text}\x1b[0m`;
const yellow = text => `\x1b[33m${text}\x1b[0m`;
const green = text => `\x1b[32m${text}\x1b[0m`;
let asyncapiDocPath;
let template;
const params = {};
const noOverwriteGlobs = [];
const disabledHooks = [];
const parseOutput = dir => path.resolve(dir);
const paramParser = v => {
if (!v.includes('=')) throw new Error(`Invalid param ${v}. It must be in the format of --param name=value.`);
const chunks = v.split(/=(.+)/, 2);
const paramName = chunks[0];
const paramValue = chunks[1];
params[paramName] = paramValue;
return v;
};
const noOverwriteParser = v => noOverwriteGlobs.push(v);
const disableHooksParser = v => disabledHooks.push(v);
const showError = err => {
console.error(red('Something went wrong:'));
console.error(red(err.stack || err.message));
if (err.errors) {
console.error(red(JSON.stringify(err.errors)));
}
};
const showErrorAndExit = err => {
showError(err);
process.exit(1);
};
program
.version(packageInfo.version)
.arguments('<asyncapi> <template>')
.action((fileLoc, tmpl) => {
asyncapiDocPath = fileLoc;
template = tmpl;
})
.option('-d, --disable-hook <hookType>', 'disable a specific hook type', disableHooksParser)
.option('--debug', 'enable more specific errors in the console')
.option('-i, --install', 'installs the template and its dependencies (defaults to false)')
.option('-n, --no-overwrite <glob>', 'glob or path of the file(s) to skip when regenerating', noOverwriteParser)
.option('-o, --output <outputDir>', 'directory where to put the generated files (defaults to current directory)', parseOutput, process.cwd())
.option('-p, --param <name=value>', 'additional param to pass to templates', paramParser)
.option('--force-write', 'force writing of the generated files to given directory even if it is a git repo with unstaged files or not empty dir (defaults to false)')
.option('--watch-template', 'watches the template directory and the AsyncAPI document, and re-generate the files when changes occur. Ignores the output directory. This flag should be used only for template development.')
.parse(process.argv);
if (!asyncapiDocPath) {
console.error(red('> Path or URL to AsyncAPI file not provided.'));
program.help(); // This exits the process
}
const isAsyncapiDocLocal = isFilePath(asyncapiDocPath);
xfs.mkdirp(program.output, async err => {
if (err) return showErrorAndExit(err);
try {
await generate(program.output);
} catch (e) {
return showErrorAndExit(e);
}
// If we want to watch for changes do that
if (program.watchTemplate) {
let watcher;
const watchDir = path.resolve(template);
const outputPath = path.resolve(watchDir, program.output);
// Template name is needed as it is not always a part of the cli commad
// There is a use case that you run generator from a root of the template with `./` path
const templateName = require(path.resolve(watchDir,'package.json')).name;
if (isAsyncapiDocLocal) {
console.log(`[WATCHER] Watching for changes in the template directory ${magenta(watchDir)} and in the AsyncAPI file ${magenta(asyncapiDocPath)}`);
watcher = new Watcher([asyncapiDocPath, watchDir], outputPath);
} else {
console.log(`[WATCHER] Watching for changes in the template directory ${magenta(watchDir)}`);
watcher = new Watcher(watchDir, outputPath);
}
// Must check template in its installation path in generator to use isLocalTemplate function
if (!await isLocalTemplate(path.resolve(Generator.DEFAULT_TEMPLATES_DIR, templateName))) {
console.warn(`WARNING: ${template} is a remote template. Changes may be lost on subsequent installations.`);
}
watcher.watch(async (changedFiles) => {
console.clear();
console.log('[WATCHER] Change detected');
for (const [, value] of Object.entries(changedFiles)) {
let eventText;
switch (value.eventType) {
case 'changed':
eventText = green(value.eventType);
break;
case 'removed':
eventText = red(value.eventType);
break;
case 'renamed':
eventText = yellow(value.eventType);
break;
default:
eventText = yellow(value.eventType);
}
console.log(`\t${magenta(value.path)} was ${eventText}`);
}
console.log('Generating files');
try {
await generate(program.output);
} catch (e) {
showError(e);
}
}, (paths) => {
showErrorAndExit({ message: `[WATCHER] Could not find the file path ${paths}, are you sure it still exists? If it has been deleted or moved please rerun the generator.` });
});
}
});
/**
* Generates the files based on the template.
* @param {*} targetDir The path to the target directory.
*/
function generate(targetDir) {
return new Promise(async (resolve, reject) => {
try {
const generator = new Generator(template, targetDir || path.resolve(os.tmpdir(), 'asyncapi-generator'), {
templateParams: params,
noOverwriteGlobs,
disabledHooks,
forceWrite: program.forceWrite,
install: program.install,
debug: program.debug
});
if (isAsyncapiDocLocal) {
await generator.generateFromFile(path.resolve(asyncapiDocPath));
} else {
await generator.generateFromURL(asyncapiDocPath);
}
console.log(green('\n\nDone! ✨'));
console.log(`${yellow('Check out your shiny new generated files at ') + magenta(program.output) + yellow('.')}\n`);
resolve();
} catch (e) {
reject(e);
}
});
}
process.on('unhandledRejection', showErrorAndExit);