import * as LibFs from 'mz/fs';
import * as program from 'commander';
import * as LibPath from 'path';
import * as LibShell from 'shelljs';
import {mkdir, Proto, ProtoFile, readProtoList} from './lib/lib';
const pkg = require('../../package.json');
program.version(pkg.version)
.option('-p, --proto
', 'directory of source proto files')
.option('-o, --output ', 'directory to output codes')
.option('-i, --import ', 'third party proto import path: e.g path1,path2,path3', function list(val) {
return val.split(',');
})
.option('-e, --exclude ', 'files or paths in -p shall be excluded: e.g file1,path1,path2,file2', function list(val) {
return val.split(',');
})
.option('-j, --javascript', 'add -j to output javascript codes')
.option('-t, --typescript', 'add -t to output typescript d.ts definitions')
.option('-s, --swagger', 'add -s to output swagger json')
.option('-a, --all', 'also parse & output all proto files in import path?')
.parse(process.argv);
const PROTO_DIR = (program as any).proto === undefined ? undefined : LibPath.normalize((program as any).proto);
const OUTPUT_DIR = (program as any).output === undefined ? undefined : LibPath.normalize((program as any).output);
const JS = (program as any).javascript !== undefined;
const DTS = (program as any).typescript !== undefined;
const SWAGGER = (program as any).swagger !== undefined;
const ALL = (program as any).all !== undefined;
const EXCLUDES = (program as any).exclude === undefined ? [] : (program as any).exclude;
const IMPORTS = (program as any).import === undefined ? [] : (program as any).import;
class ProtoCLI {
private _protoFiles: Array = [];
static instance() {
return new ProtoCLI();
}
public async run() {
console.log('ProtoCLI start.');
await this._validate();
await this._loadProtos();
await this._genProtos();
}
private async _validate() {
console.log('ProtoCLI validate.');
if (!PROTO_DIR) {
throw new Error('--proto is required');
}
if (!OUTPUT_DIR) {
throw new Error('--output is required');
}
let protoStat = await LibFs.stat(PROTO_DIR);
if (!protoStat.isDirectory()) {
throw new Error('--proto is not a directory');
}
let outputStat = await LibFs.stat(OUTPUT_DIR);
if (!outputStat.isDirectory()) {
throw new Error('--output is not a directory');
}
}
private async _loadProtos() {
console.log('ProtoCLI load proto files.');
this._protoFiles = this._protoFiles.concat(await readProtoList(PROTO_DIR, OUTPUT_DIR, EXCLUDES));
if (ALL && IMPORTS.length > 0) {
for (let i = 0; i < IMPORTS.length; i++) {
this._protoFiles = this._protoFiles.concat(await readProtoList(LibPath.normalize(IMPORTS[i]), OUTPUT_DIR, EXCLUDES));
}
}
if (this._protoFiles.length === 0) {
throw new Error('no proto files found');
}
}
private async _genProtos() {
console.log('ProtoCLI generate proto codes.');
const outputDir = LibPath.join(OUTPUT_DIR, 'proto');
await mkdir(outputDir);
for (let i = 0; i < this._protoFiles.length; i++) {
let protoFile = this._protoFiles[i];
console.log(`ProtoCLI generate proto: ${protoFile.fileName}`);
let cmds = [];
let imports = '';
IMPORTS.forEach((importPath: string) => {
imports += ` --proto_path ${importPath}`;
});
if (JS) {
let cmd = '';
cmd += 'grpc_tools_node_protoc';
cmd += ` --js_out=import_style=commonjs,binary:${outputDir}`;
cmd += ` --grpc_out=${outputDir}`;
cmd += ' --plugin=protoc-gen-grpc=`which grpc_tools_node_protoc_plugin`';
cmd += ` --proto_path ${PROTO_DIR}`;
cmd += imports;
cmd += ` ${Proto.genFullProtoFilePath(protoFile)}`;
cmds.push(cmd);
}
if (DTS) {
let cmd = '';
cmd += 'protoc';
cmd += ' --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts';
cmd += ` --ts_out=:${outputDir}`;
cmd += ` --proto_path ${PROTO_DIR}`;
cmd += imports;
cmd += ` ${Proto.genFullProtoFilePath(protoFile)}`;
cmds.push(cmd);
}
if (SWAGGER) {
let cmd = '';
cmd += 'protoc';
cmd += ` --swagger_out=:${outputDir}`;
cmd += ` --proto_path ${PROTO_DIR}`;
cmd += imports;
cmd += ` ${Proto.genFullProtoFilePath(protoFile)}`;
cmds.push(cmd);
}
if (cmds.length === 0) {
throw new Error('Choose one of --javascript | --typescript | --swagger to output');
}
cmds.forEach((cmd: string) => {
if (LibShell.exec(cmd).code !== 0) {
throw new Error(`err in generating proto: ${protoFile.fileName}`);
}
});
}
}
}
ProtoCLI.instance().run().catch((err: Error) => {
console.log('err: ', err.message);
});