-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.ts
114 lines (93 loc) · 3.44 KB
/
index.ts
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
import protobuf, { Enum, Namespace, Service, Type } from 'protobufjs';
import { emptyDirSync, outputFileSync } from 'fs-extra';
import { glob } from 'glob';
import path from 'path';
import prettier from 'prettier';
import { getEnumCode } from './enums';
import { ProtoGenOptions, UserOptions, vaidateOptions } from './options';
import { getServiceInfo } from './services';
import { getTypeInfo } from './types';
import {
getImportString,
isEnumConstructor,
isNamespaceConstructor,
isServiceConstructor,
isTypeConstructor,
namespaceToPath,
} from './utils';
const root = new protobuf.Root();
export function generateProtocol(options: UserOptions) {
const finalOptions = vaidateOptions(options);
root.resolvePath = function (_origin, target) {
return path.join(finalOptions.protocolDir, target);
};
const files = glob.sync('**/*.proto', {
cwd: options.protocolDir,
ignore: options.ignoreFiles,
});
files.forEach((file) => {
root.loadSync(file, {
alternateCommentMode: true,
});
});
// Clear output directory
emptyDirSync(finalOptions.outDir);
// Generate the code
if (!root.nested) {
throw new Error('Could not find any protocols in root');
}
// We only generate namespaces in the root
Object.values(root.nested).forEach((type) => {
if (isNamespaceConstructor(type)) {
generateNamespace(type, finalOptions);
}
});
console.log(`Generated ${files.length} protocol files.`);
}
function generateNamespace(namespace: Namespace, options: ProtoGenOptions) {
if (!namespace.nested) {
console.warn(`Namespace ${namespace.name} has no nested members to be generated`);
return;
}
const types: Type[] = [];
const enums: Enum[] = [];
const services: Service[] = [];
const imports: Map<string, Set<string>> = new Map();
Object.entries(namespace.nested).forEach(([, type]) => {
if (isNamespaceConstructor(type)) {
generateNamespace(type, options);
} else if (isTypeConstructor(type)) {
types.push(type);
} else if (isEnumConstructor(type)) {
enums.push(type);
} else if (isServiceConstructor(type)) {
services.push(type);
}
});
// We have types in this namespace, we need to generate a file
if (types.length > 0 || enums.length > 0 || services.length > 0) {
const typeInfos = types.map((type) => getTypeInfo(type, options));
const serviceInfos = services.map(getServiceInfo);
[...typeInfos, ...serviceInfos].forEach((typeInfo) => {
typeInfo.imports.forEach(({ path, name }) => {
if (!imports.has(path)) {
imports.set(path, new Set());
}
imports.get(path)?.add(name);
});
});
const file = prettier.format(
`
/* eslint-disable */
${getImportString(imports)}
${enums.map(getEnumCode).join('\n\n')}
${typeInfos.map((info) => info.typeString).join('\n\n')}
${serviceInfos.map((info) => info.typeString).join('\n\n')}
`,
{ ...options.prettierConfig, parser: 'typescript' }
);
// Ensure that type's parent has a file
const filePath = namespaceToPath(namespace.fullName);
outputFileSync(`${path.join(options.outDir, filePath)}.ts`, file);
}
}