@cicctencent/tars2node-cli 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # Tars2Node CLI
2
+ 该项目fork by`@tencent/jce2node-cli`
3
+ 提供了node环境的tars2node编译器,方便和框架进行工程化集成。
4
+
5
+ ## Install
6
+ `npm i -D @jv/tars2node-cli`
7
+
8
+ ## Usage
9
+
10
+ ### With CLI:
11
+ Command: `tars [options] <entry-file>`
12
+
13
+ Options:
14
+ ```
15
+ -T --type 编译出的文件类型,默认为es5,可选项:ts | es5
16
+ -W --watch 监听指定文件或文件夹,支持模式匹配
17
+ -O --output 指定存放编译后文件的文件夹的路径
18
+ -M --mode 指定编译的目标服务类型,默认为all,可选项:client | service | all | web
19
+ -R --return 仅对生成service代码有效,service代码调用中添加ret参数
20
+ -G --gather 仅对生成service代码有效,service代码调用中的参数根据入参和出参聚合
21
+ -I --interface 仅对生成ts格式的service代码有效,service中附带含有servant所有的方法的interface
22
+ -E --entry (deprected) 入口文件路径
23
+ -L --long long转译的类型,默认为string,可选项:string | number | bigint
24
+ ```
25
+
26
+ Example: `tars -T ts -W ./demo/**/*.tars -O ./dist -M web demo/interface.tar`
27
+
28
+
29
+ tars -T ts -W ./_Proto/**/*.tars -O ./dist -M web
30
+ ### With Node:
31
+ ```javascript
32
+ const tars = require('tars2node-cli');
33
+ const ast = tars.ast('./foo/bar/your-tars-entry.tar');
34
+ /*
35
+ * type与options中的type相同
36
+ * mode与options中的mode相同,默认为'service'
37
+ * config支持的选项:
38
+ * * withReturn: 与options中-R相同
39
+ * * gather: 与options中-G相同
40
+ * * interface: 与options中-I相同
41
+ * * long: 与options中-L相同
42
+ *
43
+ */
44
+ const runCompiler = tars.run('./foo/bar/your-tars-entry.tar', 'ts', './dist', 'client', { withReturn: true });
45
+ ```
46
+
47
+ ## Featrue List
48
+ [x] 生成服务端代码
49
+ [x] Interface -> js
50
+ [x] Enum -> js
51
+ [x] Struct -> js
52
+ [x] 生成typescript代码
53
+ [x] 生成客户端代码
54
+ [x] 添加默认值
55
+ [x] 添加const类型
56
+
57
+ ## TODO
58
+
59
+ ### P0
60
+ [ ] 生成d.ts代码
61
+
62
+ ### P1
63
+ [ ] 添加key类型
64
+
65
+
66
+ ## Author
67
+ @tencent/kealxiao
68
+
69
+ ## Contributors
70
+ * @tencent/shanelv
71
+ * @tencent/carvenzhang
72
+
73
+ ## License
74
+ MIT
package/bin/tars.js ADDED
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env node
2
+
3
+ 'use strict';
4
+
5
+ const program = require('commander');
6
+ const pkg = require('../package.json');
7
+
8
+
9
+ program
10
+ .version(pkg.version)
11
+ .usage('[options] <file>')
12
+ .option('-T, --type <type>', '输出文件类型')
13
+ .option('-M, --mode <type>', '目标服务类型')
14
+ .option('-W, --watch <path>', '监听文件夹')
15
+ .option('-O, --output <path>', '输出文件夹')
16
+ .option('-R, --return', '添加ret参数')
17
+ .option('-G, --gather', '聚合出入参数')
18
+ .option('-I, --interface', '生成服务接口类型')
19
+ .option('-L, --long <type>', 'long转译的类型')
20
+ .action(action)
21
+ .parse(process.argv);
22
+
23
+
24
+ function action(file, cmd) {
25
+ console.info('\x1b[36m', `☕️ Generating JCE files from entry: ${file}.\n`);
26
+ const startTime = new Date();
27
+
28
+ /* eslint-disable global-require */
29
+ const type = cmd.type || 'es5';
30
+ const mode = cmd.mode || 'all';
31
+ const long = cmd.long || 'string';
32
+
33
+ const Generator = require('../lib/generator/ts/generator');
34
+ const TsParser = require('../lib/parser/parser');
35
+
36
+ const Parser = TsParser;
37
+
38
+ const options = {
39
+ mode,
40
+ type,
41
+ long,
42
+ withReturn: Boolean(cmd.return),
43
+ gather: Boolean(cmd.gather),
44
+ interface: Boolean(cmd.interface),
45
+ output: cmd.output,
46
+ };
47
+
48
+ const build = parser => new Generator(parser.buildAST(), file, options).build();
49
+
50
+ const buildOnce = async () => {
51
+ try {
52
+ const parser = new Parser(file);
53
+
54
+ await build(parser);
55
+ const endTime = new Date();
56
+ const timeDiff = (endTime - startTime).toFixed(2);
57
+ console.info();
58
+ console.info('\x1b[32m', `⚡️ Spend Time: ${timeDiff}ms.`);
59
+ } catch (e) {
60
+ console.error('\x1b[31m', '💔 An error occur!');
61
+ console.error('\x1b[31m', `💔 ${e.message}\n`);
62
+ console.error('\x1b[31m', `💔 ${e.stack}\n`);
63
+ }
64
+ };
65
+
66
+ if (cmd.watch) {
67
+ const chokidar = require('chokidar');
68
+
69
+ const watcher = chokidar.watch(cmd.watch);
70
+ watcher.on('ready', () => {
71
+ buildOnce();
72
+ watcher.on('all', () => {
73
+ buildOnce();
74
+ });
75
+ });
76
+ } else {
77
+ buildOnce();
78
+ }
79
+
80
+ /* eslint-enable global-require */
81
+ }
package/jce.pegjs ADDED
@@ -0,0 +1,359 @@
1
+ Tars
2
+ = refs:RefDefinition* m:ModuleDefinition+ {
3
+ return [...refs, ...m];
4
+ }
5
+
6
+ RefDefinition
7
+ = _* '#include' _* '"' cs:Char+ '"' _* {
8
+ return {
9
+ type: 'ref',
10
+ target: cs.join(''),
11
+ };
12
+ }
13
+
14
+ ModuleDefinition
15
+ = _* 'module' _+ id:Identifier _* '{' value:Definition+ _* '}' _* ';'? _* {
16
+ return {
17
+ type: 'module',
18
+ id,
19
+ value,
20
+ }
21
+ }
22
+
23
+ Definition
24
+ = ConstDefinition
25
+ / EnumDefinition
26
+ / StructDefinition
27
+ / InterfaceDefinition
28
+ / CompareDefinition
29
+ / KeyDeclaration
30
+
31
+ TypeSpecifier
32
+ = VectorSpecifier
33
+ / MapSpecifier
34
+ / SimpleTypeSpecifier
35
+
36
+ SimpleTypeSpecifier
37
+ = 'void'
38
+ / 'bool'
39
+ / 'byte'
40
+ / 'short'
41
+ / 'int'
42
+ / type:('unsigned' blank 'int') { return type.join("") }
43
+ / type:('unsigned' blank 'short') { return type.join("") }
44
+ / 'long'
45
+ / 'float'
46
+ / 'double'
47
+ / 'string'
48
+ / nest:((Identifier _* '::' _* )*)? target:Identifier {
49
+ return nest.reduce((acc, [key]) => Object.assign({}, acc, { nested: [...acc.nested, key] }), { target, nested:[] });
50
+ }
51
+
52
+ VectorSpecifier
53
+ = 'vector' blank* '<' blank* target:TypeSpecifier blank* '>' {
54
+ return {
55
+ type: 'vector',
56
+ target,
57
+ };
58
+ }
59
+
60
+ MapSpecifier
61
+ = 'map' blank* '<' blank* key:TypeSpecifier _* ',' _* value:TypeSpecifier blank* '>' {
62
+ return {
63
+ type: 'map',
64
+ key,
65
+ value,
66
+ };
67
+ }
68
+
69
+ ConstDefinition
70
+ = beforeComment:_* 'const' _+ t:TypeSpecifier _+ id:Identifier _* init:ConstInitializer _* ';' {
71
+ return {
72
+ id,
73
+ type: 'const',
74
+ beforeComment: beforeComment.join(''),
75
+ value: {
76
+ type: t,
77
+ init,
78
+ },
79
+ };
80
+ }
81
+
82
+ ConstInitializer
83
+ = '=' _* value:ConstValuer {
84
+ return value;
85
+ }
86
+
87
+ ConstValuer
88
+ = value:(FloatingLiteral / IntegerLiteral / StringLiteral / BooleanLiteral) {
89
+ return value;
90
+ }
91
+ / nest:((Identifier _* '::' _* )*)? target:Identifier {
92
+ return nest.reduce((acc, [key]) => Object.assign({}, acc, { nested: [...acc.nested, key] }), { target, nested:[] });
93
+ }
94
+
95
+ EnumDefinition
96
+ = beforeComment:_* 'enum' _+ id:Identifier _* '{' _* es:EnumeratorSequence _* '}' _* ';' {
97
+ return {
98
+ id,
99
+ type: 'enum',
100
+ value: es,
101
+ beforeComment: beforeComment.join(''),
102
+ };
103
+ }
104
+
105
+ EnumeratorSequence
106
+ = head:Enumerator _* tail:EnumeratorSequence {
107
+ return [head, ...tail];
108
+ }
109
+ / e:Enumerator {
110
+ return [e];
111
+ }
112
+
113
+ Enumerator
114
+ = id:Identifier _* '=' _* v:IntegerLiteral _* ','? comment: _* {
115
+ return {
116
+ id,
117
+ value: v,
118
+ comment: comment.join('').replace(/^[\n\r]+|\s+$/g, '')
119
+ };
120
+ }
121
+ / id:Identifier ','? comment: _* {
122
+ return {
123
+ id,
124
+ value: undefined,
125
+ };
126
+ }
127
+ / id:Identifier {
128
+ return {
129
+ id,
130
+ value: undefined,
131
+ };
132
+ }
133
+
134
+ StructDefinition
135
+ = beforeComment:_* 'struct' _+ id:Identifier _* '{' _* ms:MemberDeclaration* _* '}' _* ';' {
136
+ return {
137
+ id,
138
+ type: 'struct',
139
+ members: ms,
140
+ beforeComment: beforeComment.join(''),
141
+ };
142
+ }
143
+
144
+ KeyDeclaration
145
+ = beforeComment:_* 'key' _* '[' v:KeyMemberList _* ']' _* ';' mock:Mockcomment? comment:_* {
146
+ comment = comment.join('').replace(/^[\n\r]+|\s+$/g, '');
147
+ return {
148
+ type: 'key',
149
+ value:v,
150
+ beforeComment: beforeComment.join(''),
151
+ mock: mock,
152
+ comment,
153
+ };
154
+ }
155
+
156
+ MemberDeclaration
157
+ = i:IntegerLiteral _+ r:('require' / 'optional') _+ t:TypeSpecifier _+ id:Identifier _* init:ConstInitializer? _* ';' mock:Mockcomment? comment:_* {
158
+ comment = comment.join('').replace(/^[\n\r]+|\s+$/g, '');
159
+ if(mock ){
160
+ return {
161
+ index: parseInt(i, 10),
162
+ id,
163
+ require: r === 'require',
164
+ type: t,
165
+ init,
166
+ mock: mock,
167
+ comment,
168
+ };
169
+ }
170
+ return {
171
+ index: parseInt(i, 10),
172
+ id,
173
+ require: r === 'require',
174
+ type: t,
175
+ init,
176
+ comment,
177
+ };
178
+ }
179
+
180
+ InterfaceDefinition
181
+ = beforeComment:_* 'interface' _+ id:Identifier _* '{' _* fn:FunctionDefinition* _* '}' _* ';' {
182
+ return {
183
+ id,
184
+ type: 'interface',
185
+ methods: fn,
186
+ };
187
+ }
188
+
189
+ FunctionDefinition
190
+ = t:TypeSpecifier _+ id:Identifier _* '(' _* ps:ParameterDefinitionList? _* ')' _* ';' _* {
191
+ return {
192
+ id,
193
+ type: 'func',
194
+ output: t,
195
+ params: ps,
196
+ };
197
+ }
198
+
199
+ ParameterDefinitionList
200
+ = head:ParameterDefinition _* ',' _* tail:ParameterDefinitionList {
201
+ return [head, ...tail];
202
+ }
203
+ / p:ParameterDefinition {
204
+ return [p];
205
+ }
206
+
207
+ ParameterDefinition
208
+ = 'out' _+ type:TypeSpecifier _+ id:Identifier {
209
+ return {
210
+ id,
211
+ io: 'out',
212
+ type,
213
+ };
214
+ }
215
+ / 'routekey' _+ type:TypeSpecifier _+ id:Identifier {
216
+ return {
217
+ id,
218
+ io: 'routekey',
219
+ type,
220
+ };
221
+ }
222
+ / type:TypeSpecifier _+ id:Identifier {
223
+ return {
224
+ id,
225
+ io: 'in',
226
+ type,
227
+ }
228
+ }
229
+
230
+ CompareDefinition
231
+ = beforeComment:_* 'key' _+ '[' _* nest:((Identifier '::')*)? target:Identifier _* ',' _* v:KeyMemberList _* ']' _* ';' {
232
+ const t = nest.reduce((acc, [key]) => Object.assign({}, acc, { nested: [...acc.nested, key] }), { target, nested:[] });
233
+ return {
234
+ type: 'key',
235
+ value: t,
236
+ seq: v,
237
+ beforeComment: beforeComment.join(''),
238
+ };
239
+ }
240
+
241
+ KeyMemberList
242
+ = head:Identifier _* ',' _* tail:KeyMemberList {
243
+ return [head, ...tail];
244
+ }
245
+ / id:Identifier {
246
+ return [id];
247
+ }
248
+
249
+ IntegerLiteral
250
+ = num:(HexLiteral / OctalLiteral / DecimalLiteral) { return Number(num); }
251
+
252
+
253
+ DecimalLiteral
254
+ = head:(Sign? NonezeroDigit) tail:Digit* {
255
+ return [head.join(''), ...tail].join('');
256
+ }
257
+
258
+ OctalLiteral
259
+ = head:(Sign? [0]+) tail:OctalDigit* {
260
+ return [head.join(''), ...tail].join('');
261
+ }
262
+
263
+ HexLiteral
264
+ = head:(Sign? "0x"i) tail:HexDigit+ {
265
+ return [head.join(''), ...tail].join('');
266
+ }
267
+
268
+ FloatingLiteral
269
+ = f:FractionalConstant e:ExponentPart? FloatingSuffix? {
270
+ if (e !== null) return (f + e);
271
+ return f;
272
+ }
273
+ / d:Digit+ e:ExponentPart? FloatingSuffix? {
274
+ const ds = d.join('');
275
+ if (e !== null) return (ds + e);
276
+ return ds;
277
+ }
278
+
279
+ FractionalConstant
280
+ = s:Sign? l:Digit* '.' r:Digit+ {
281
+ if (s === '-') {
282
+ return [s, ...l, '.', ...r].join('');
283
+ }
284
+ return [...l, '.', ...r].join('');
285
+ }
286
+ / s:Sign? head:Digit+ '.' {
287
+ if (s === '-') {
288
+ return [s, ...head].join('');
289
+ }
290
+ return head.join('');
291
+ }
292
+
293
+ Digit
294
+ = [0-9]
295
+
296
+ NonezeroDigit
297
+ = [1-9]
298
+
299
+ OctalDigit
300
+ = [1-6]
301
+
302
+ HexDigit
303
+ = [0-9a-fA-F]
304
+
305
+ ExponentPart
306
+ = 'e'i s:Sign? d:Digit+ {
307
+ if (s === null) {
308
+ return ['e', ...d].join('');
309
+ }
310
+ return ['e', s, ...d].join('');
311
+ }
312
+
313
+ Sign
314
+ = [+-]
315
+
316
+ FloatingSuffix
317
+ = 'f'i
318
+
319
+ BooleanLiteral
320
+ = 'true'
321
+ / 'false'
322
+
323
+ StringLiteral
324
+ = '"' str:Char* '"' {
325
+ return `"${str.join('')}"`;
326
+ }
327
+
328
+ Char
329
+ = [^\n\"]
330
+
331
+ Identifier
332
+ = head:NonDigit tail:(NonDigit / Digit)* {
333
+ return [head, ...tail].join('');
334
+ }
335
+
336
+ NonDigit
337
+ = [_a-zA-Z]
338
+
339
+
340
+ Mockcomment
341
+ = [^\n\/]*'//' [^\n\{]* '{{' str:[^\n\{\}]+ '}}' [^\n\}]* {
342
+ return str.join('');
343
+ }
344
+
345
+ comment
346
+ = c:('/*' (!'*/' .)* '*/'
347
+ / '//' [^\n]*){
348
+ // console.log('comment', c);
349
+ const toString = (a) => typeof a === 'string' ? a : ( a && a.length ? a.map(toString).join('') : '');
350
+ return toString(c);
351
+ }
352
+
353
+ blank
354
+ = [ ]
355
+
356
+ _
357
+ = c:([ \t\n\r] / comment) {
358
+ return c;
359
+ }
@@ -0,0 +1,122 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ *
5
+ * 声明JCE-Struct节点类型
6
+ * @typedef { refType | vecType | string | mapType } tarsType
7
+ *
8
+ * @typedef {object} refType
9
+ * @property {string} target
10
+ * @property {string[]} nested
11
+ *
12
+ * @typedef {object} vecType
13
+ * @property {'vector'} type
14
+ * @property {tarsType} target
15
+ *
16
+ * @typedef {object} mapType
17
+ * @property {'map'} type
18
+ * @property {tarsType} key
19
+ * @property {tarsType} value
20
+ *
21
+ * @typedef {object} constValue
22
+ * @property {tarsType} type
23
+ * @property {*} init
24
+ *
25
+ * @typedef {object} TarsConstNode
26
+ * @property {string} id
27
+ * @property {'const'} type
28
+ * @property {constValue} value
29
+ *
30
+ * @typedef {object} enumValue
31
+ * @property {string} id
32
+ * @property {number} value
33
+ *
34
+ * @typedef {object} TarsEnumNode
35
+ * @property {string} id
36
+ * @property {'enum'} type
37
+ * @property {enumValue[]} value
38
+ *
39
+ * @typedef {object} param
40
+ * @property {string} id
41
+ * @property {'in'|'out'} io
42
+ * @property {tarsType} type
43
+ *
44
+ * @typedef {object} TarsInterfMethod
45
+ * @property {string} id
46
+ * @property {'func'} type
47
+ * @property {tarsType} output
48
+ * @property {param[]} params
49
+ *
50
+ * @typedef {object} TarsInterfNode
51
+ * @property {string} id
52
+ * @property {'interface'} type
53
+ * @property {TarsInterfMethod[]} methods
54
+ *
55
+ * @typedef {object} member
56
+ * @property {number} index
57
+ * @property {string} id
58
+ * @property {boolean} require
59
+ * @property {tarsType} type
60
+ * @property {null|string} init
61
+ *
62
+ * @typedef {object} TarsStructNode
63
+ * @property {string} id
64
+ * @property {'struct'} type
65
+ * @property {member[]} members
66
+ *
67
+ * @typedef {TarsStructNode|TarsInterfNode|TarsEnumNode|TarsConstNode} TarsNode
68
+ *
69
+ * @typedef {object} TarsModule
70
+ * @property {string} pathname
71
+ * @property {TarsNode[]} value
72
+ *
73
+ * @typedef {Object.<string, TarsModule>} TarsTree
74
+ *
75
+ * 工具类类型
76
+ * @typedef {import("./printer")} Printer
77
+ * @typedef {import("./typing")} Typing
78
+ *
79
+ */
80
+ class Finder {
81
+ /**
82
+ * @param {TarsTree} tree
83
+ */
84
+ constructor(tree) {
85
+ this._tree = tree;
86
+ /** @type {Map<string, *>} */
87
+ this._memo = new Map();
88
+ }
89
+
90
+ refresh() {
91
+ this._memo = new Map();
92
+ }
93
+
94
+ /**
95
+ * @param {refType} type
96
+ * @param {string} current
97
+ * @return {*}
98
+ */
99
+ find(type, current) {
100
+ const jsonType = JSON.stringify(type);
101
+ if (!this._memo.has(jsonType)) {
102
+ if (!type.target || !type.nested) throw new Error(`Type: #${JSON.stringify(type)}# is not a ref type`);
103
+
104
+ let currentPath;
105
+ if (type.nested.length === 0) currentPath = [current];
106
+ else currentPath = [...type.nested];
107
+
108
+ try {
109
+ const [moduleName] = currentPath;
110
+ const targetModule = this._tree[moduleName];
111
+ const target = targetModule.value.find(({ id: mid }) => mid === type.target);
112
+ this._memo.set(jsonType, target);
113
+ } catch (e) {
114
+ throw new Error(`Cannot find target for type: #${JSON.stringify(type)}#`);
115
+ }
116
+ }
117
+
118
+ return this._memo.get(jsonType);
119
+ }
120
+ }
121
+
122
+ module.exports = Finder;