datagrok-tools 4.13.78 → 4.14.0
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/CHANGELOG.md +6 -0
- package/bin/utils/func-generation.js +249 -15
- package/package-template/package.json +1 -1
- package/package.json +4 -1
- package/plugins/func-gen-plugin.js +310 -92
package/CHANGELOG.md
CHANGED
|
@@ -9,7 +9,7 @@ exports.generateExport = generateExport;
|
|
|
9
9
|
exports.generateFunc = generateFunc;
|
|
10
10
|
exports.generateImport = generateImport;
|
|
11
11
|
exports.getFuncAnnotation = getFuncAnnotation;
|
|
12
|
-
exports.reservedDecorators = exports.pseudoParams = exports.headerParams = void 0;
|
|
12
|
+
exports.typesToAnnotation = exports.reservedDecorators = exports.pseudoParams = exports.headerParams = void 0;
|
|
13
13
|
/* eslint-disable no-unused-vars */
|
|
14
14
|
/* eslint-disable valid-jsdoc */
|
|
15
15
|
|
|
@@ -20,42 +20,117 @@ let pseudoParams = exports.pseudoParams = /*#__PURE__*/function (pseudoParams) {
|
|
|
20
20
|
pseudoParams["INPUT_TYPE"] = "inputType";
|
|
21
21
|
return pseudoParams;
|
|
22
22
|
}({});
|
|
23
|
+
const nonMetaData = ['sidebar', 'editor'];
|
|
24
|
+
const decoratorOptionToAnnotation = new Map([['initialValue', 'default']]);
|
|
23
25
|
let FUNC_TYPES = exports.FUNC_TYPES = /*#__PURE__*/function (FUNC_TYPES) {
|
|
26
|
+
FUNC_TYPES["APP"] = "app";
|
|
24
27
|
FUNC_TYPES["CELL_RENDERER"] = "cellRenderer";
|
|
25
28
|
FUNC_TYPES["FILE_EXPORTER"] = "fileExporter";
|
|
26
|
-
FUNC_TYPES["
|
|
29
|
+
FUNC_TYPES["FILE_HANDLER"] = "file-handler";
|
|
27
30
|
FUNC_TYPES["FILE_VIEWER"] = "fileViewer";
|
|
28
31
|
FUNC_TYPES["SETTINGS_EDITOR"] = "packageSettingsEditor";
|
|
29
32
|
FUNC_TYPES["VIEWER"] = "viewer";
|
|
30
33
|
FUNC_TYPES["FILTER"] = "filter";
|
|
34
|
+
FUNC_TYPES["AUTOSTART"] = "autostart";
|
|
35
|
+
FUNC_TYPES["INIT"] = "init";
|
|
36
|
+
FUNC_TYPES["EDITOR"] = "editor";
|
|
37
|
+
FUNC_TYPES["PANEL"] = "panel";
|
|
38
|
+
FUNC_TYPES["FOLDER_VIEWER"] = "folderViewer";
|
|
39
|
+
FUNC_TYPES["SEM_TYPE_DETECTOR"] = "semTypeDetector";
|
|
40
|
+
FUNC_TYPES["DASHBOARD"] = "dashboard";
|
|
41
|
+
FUNC_TYPES["FUNCTION_ANALYSIS"] = "functionAnalysis";
|
|
42
|
+
FUNC_TYPES["CONVERTER"] = "converter";
|
|
43
|
+
FUNC_TYPES["MODEL"] = "model";
|
|
31
44
|
return FUNC_TYPES;
|
|
32
45
|
}({});
|
|
46
|
+
const typesToAnnotation = exports.typesToAnnotation = {
|
|
47
|
+
'DataFrame': 'dataframe',
|
|
48
|
+
'DG.DataFrame': 'dataframe',
|
|
49
|
+
'Column': 'column',
|
|
50
|
+
'DG.Column': 'column',
|
|
51
|
+
'ColumnList': 'column_list',
|
|
52
|
+
'DG.ColumnList': 'column_list',
|
|
53
|
+
'FileInfo': 'file',
|
|
54
|
+
'DG.FileInfo': 'file',
|
|
55
|
+
'Uint8Array': 'blob',
|
|
56
|
+
'number': 'double',
|
|
57
|
+
'boolean': 'bool',
|
|
58
|
+
'dayjs.Dayjs': 'datetime',
|
|
59
|
+
'Dayjs': 'datetime',
|
|
60
|
+
'graphics': 'graphics',
|
|
61
|
+
'DG.View': 'view',
|
|
62
|
+
'View': 'view',
|
|
63
|
+
'DG.Widget': 'widget',
|
|
64
|
+
'Widget': 'widget',
|
|
65
|
+
'DG.FuncCall': 'funccall',
|
|
66
|
+
'FuncCall': 'funccall',
|
|
67
|
+
'DG.SemanticValue': 'semantic_value',
|
|
68
|
+
'SemanticValue': 'semantic_value',
|
|
69
|
+
'any': 'dynamic',
|
|
70
|
+
'void': 'void',
|
|
71
|
+
'string': 'string'
|
|
72
|
+
};
|
|
73
|
+
|
|
33
74
|
/** Generates an annotation header for a function based on provided metadata. */
|
|
34
75
|
function getFuncAnnotation(data, comment = '//', sep = '\n') {
|
|
35
76
|
const isFileViewer = data.tags?.includes(FUNC_TYPES.FILE_VIEWER) ?? false;
|
|
36
|
-
const isFileImporter = data.tags?.includes(FUNC_TYPES.
|
|
77
|
+
const isFileImporter = data.tags?.includes(FUNC_TYPES.FILE_HANDLER) ?? false;
|
|
37
78
|
let s = '';
|
|
38
79
|
if (data.name) s += `${comment}name: ${data.name}${sep}`;
|
|
39
80
|
if (pseudoParams.EXTENSION in data && data.tags != null && data.tags.includes(FUNC_TYPES.FILE_EXPORTER)) s += `${comment}description: Save as ${data[pseudoParams.EXTENSION]}${sep}`;else if (data.description) s += `${comment}description: ${data.description}${sep}`;
|
|
40
|
-
if (data.tags) {
|
|
41
|
-
s += `${comment}tags: ${isFileViewer && data[pseudoParams.EXTENSIONS] ? data.tags.concat(data[pseudoParams.EXTENSIONS].map(ext => 'fileViewer-' + ext)).join() : data.tags.join()}${sep}`;
|
|
81
|
+
if (data.tags && data.tags?.length > 0) {
|
|
82
|
+
s += `${comment}tags: ${isFileViewer && data[pseudoParams.EXTENSIONS] ? data.tags.concat(data[pseudoParams.EXTENSIONS].map(ext => 'fileViewer-' + ext)).join(', ') : data.tags.join(', ')}${sep}`;
|
|
42
83
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
84
|
+
for (let input of data.inputs ?? []) {
|
|
85
|
+
if (!input) continue;
|
|
86
|
+
let type = input?.type;
|
|
87
|
+
let isArray = false;
|
|
88
|
+
if (type?.includes(`[]`)) {
|
|
89
|
+
type = type.replace(/\[\]$/, '');
|
|
90
|
+
isArray = true;
|
|
46
91
|
}
|
|
92
|
+
const annotationType = typesToAnnotation[type ?? ''];
|
|
93
|
+
if (input?.options?.type) type = input?.options?.type;else if (annotationType) {
|
|
94
|
+
if (isArray) type = `list<${annotationType}>`;else type = annotationType;
|
|
95
|
+
} else type = 'dynamic';
|
|
96
|
+
const options = input?.options?.options ? buildStringOfOptions(input.options.options ?? {}) : '';
|
|
97
|
+
const functionName = (input.options?.name ? input?.options?.name : ` ${input.name?.replaceAll('.', '')}`)?.trim();
|
|
98
|
+
s += comment + 'input: ' + type + ' ' + functionName + (input.defaultValue !== undefined ? `= ${input.defaultValue}` : '') + ' ' + options.replaceAll('"', '\'') + sep;
|
|
47
99
|
}
|
|
48
100
|
if (data.outputs) {
|
|
49
|
-
for (const output of data.outputs) s += comment + 'output: ' + output.type + (output.name ? ` ${output.name}` : '') + sep;
|
|
101
|
+
for (const output of data.outputs) if (output.type !== 'void') s += comment + 'output: ' + output.type + (output.name ? ` ${output.name}${output.options ? ` ${buildStringOfOptions(output.options)}` : ''}` : '') + sep;
|
|
102
|
+
}
|
|
103
|
+
if (data.meta) {
|
|
104
|
+
for (let entry of Object.entries(data.meta)) s += `${comment}meta.${entry[0]}: ${entry[1]}${sep}`;
|
|
50
105
|
}
|
|
51
106
|
for (const parameter in data) {
|
|
52
|
-
if (parameter === pseudoParams.EXTENSION || parameter === pseudoParams.INPUT_TYPE) continue;else if (parameter === pseudoParams.EXTENSIONS) {
|
|
107
|
+
if (parameter === pseudoParams.EXTENSION || parameter === pseudoParams.INPUT_TYPE || parameter === 'meta' || parameter === 'isAsync' || parameter === 'test') continue;else if (parameter === pseudoParams.EXTENSIONS) {
|
|
53
108
|
if (isFileViewer) continue;
|
|
54
109
|
s += `${comment}meta.ext: ${data[parameter]}${sep}`;
|
|
55
|
-
} else if (!headerParams.includes(parameter))
|
|
110
|
+
} else if (!headerParams.includes(parameter)) {
|
|
111
|
+
if (nonMetaData.includes(parameter)) s += `${comment}${parameter}: ${data[parameter]}${sep}`;else s += `${comment}meta.${parameter}: ${data[parameter]}${sep}`;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (data.test) {
|
|
115
|
+
for (let entry of Object.entries(data.test)) {
|
|
116
|
+
if (entry[0] === 'test' || entry[0] === 'wait') s += `${comment}`;else s += `, `;
|
|
117
|
+
s += `${entry[0]}: ${entry[1]} `;
|
|
118
|
+
}
|
|
119
|
+
s += `${sep}`;
|
|
56
120
|
}
|
|
57
121
|
return s;
|
|
58
122
|
}
|
|
123
|
+
function buildStringOfOptions(options) {
|
|
124
|
+
let optionsInString = [];
|
|
125
|
+
for (const [key, value] of Object.entries(options ?? {})) {
|
|
126
|
+
let val = value;
|
|
127
|
+
let option = key;
|
|
128
|
+
option = decoratorOptionToAnnotation.get(option) ?? option;
|
|
129
|
+
if (Array.isArray(value)) val = JSON.stringify(value);
|
|
130
|
+
optionsInString.push(`${option}: ${val}`);
|
|
131
|
+
}
|
|
132
|
+
return `{ ${optionsInString.join('; ')} }`;
|
|
133
|
+
}
|
|
59
134
|
const reservedDecorators = exports.reservedDecorators = {
|
|
60
135
|
viewer: {
|
|
61
136
|
metadata: {
|
|
@@ -98,9 +173,9 @@ const reservedDecorators = exports.reservedDecorators = {
|
|
|
98
173
|
},
|
|
99
174
|
genFunc: generateFunc
|
|
100
175
|
},
|
|
101
|
-
|
|
176
|
+
fileHandler: {
|
|
102
177
|
metadata: {
|
|
103
|
-
tags: [FUNC_TYPES.
|
|
178
|
+
tags: [FUNC_TYPES.FILE_HANDLER],
|
|
104
179
|
inputs: [{
|
|
105
180
|
name: 'content',
|
|
106
181
|
type: 'string'
|
|
@@ -136,6 +211,163 @@ const reservedDecorators = exports.reservedDecorators = {
|
|
|
136
211
|
}]
|
|
137
212
|
},
|
|
138
213
|
genFunc: generateFunc
|
|
214
|
+
},
|
|
215
|
+
func: {
|
|
216
|
+
metadata: {
|
|
217
|
+
tags: [],
|
|
218
|
+
inputs: [],
|
|
219
|
+
outputs: [{
|
|
220
|
+
name: 'result',
|
|
221
|
+
type: 'dynamic'
|
|
222
|
+
}]
|
|
223
|
+
},
|
|
224
|
+
genFunc: generateFunc
|
|
225
|
+
},
|
|
226
|
+
app: {
|
|
227
|
+
metadata: {
|
|
228
|
+
tags: [FUNC_TYPES.APP],
|
|
229
|
+
inputs: [],
|
|
230
|
+
outputs: [{
|
|
231
|
+
name: 'result',
|
|
232
|
+
type: 'view'
|
|
233
|
+
}]
|
|
234
|
+
},
|
|
235
|
+
genFunc: generateFunc
|
|
236
|
+
},
|
|
237
|
+
autostart: {
|
|
238
|
+
metadata: {
|
|
239
|
+
tags: [FUNC_TYPES.AUTOSTART],
|
|
240
|
+
inputs: [],
|
|
241
|
+
outputs: []
|
|
242
|
+
},
|
|
243
|
+
genFunc: generateFunc
|
|
244
|
+
},
|
|
245
|
+
init: {
|
|
246
|
+
metadata: {
|
|
247
|
+
tags: [FUNC_TYPES.INIT],
|
|
248
|
+
inputs: [],
|
|
249
|
+
outputs: []
|
|
250
|
+
},
|
|
251
|
+
genFunc: generateFunc
|
|
252
|
+
},
|
|
253
|
+
editor: {
|
|
254
|
+
metadata: {
|
|
255
|
+
tags: [FUNC_TYPES.EDITOR],
|
|
256
|
+
inputs: [{
|
|
257
|
+
name: 'call',
|
|
258
|
+
type: 'funccall'
|
|
259
|
+
}],
|
|
260
|
+
outputs: []
|
|
261
|
+
},
|
|
262
|
+
genFunc: generateFunc
|
|
263
|
+
},
|
|
264
|
+
panel: {
|
|
265
|
+
metadata: {
|
|
266
|
+
tags: [FUNC_TYPES.PANEL],
|
|
267
|
+
inputs: [],
|
|
268
|
+
outputs: [{
|
|
269
|
+
name: 'result',
|
|
270
|
+
type: 'widget'
|
|
271
|
+
}]
|
|
272
|
+
},
|
|
273
|
+
genFunc: generateFunc
|
|
274
|
+
},
|
|
275
|
+
folderViewer: {
|
|
276
|
+
metadata: {
|
|
277
|
+
tags: [FUNC_TYPES.FOLDER_VIEWER],
|
|
278
|
+
inputs: [{
|
|
279
|
+
name: 'folder',
|
|
280
|
+
type: 'file'
|
|
281
|
+
}, {
|
|
282
|
+
name: 'files',
|
|
283
|
+
type: 'list<file>'
|
|
284
|
+
}],
|
|
285
|
+
outputs: [{
|
|
286
|
+
name: 'result',
|
|
287
|
+
type: 'widget'
|
|
288
|
+
}]
|
|
289
|
+
},
|
|
290
|
+
genFunc: generateFunc
|
|
291
|
+
},
|
|
292
|
+
semTypeDetector: {
|
|
293
|
+
metadata: {
|
|
294
|
+
tags: [FUNC_TYPES.SEM_TYPE_DETECTOR],
|
|
295
|
+
inputs: [{
|
|
296
|
+
name: 'col',
|
|
297
|
+
type: 'column'
|
|
298
|
+
}],
|
|
299
|
+
outputs: [{
|
|
300
|
+
name: 'result',
|
|
301
|
+
type: 'string'
|
|
302
|
+
}]
|
|
303
|
+
},
|
|
304
|
+
genFunc: generateFunc
|
|
305
|
+
},
|
|
306
|
+
dashboard: {
|
|
307
|
+
metadata: {
|
|
308
|
+
tags: [FUNC_TYPES.DASHBOARD],
|
|
309
|
+
inputs: [],
|
|
310
|
+
outputs: [{
|
|
311
|
+
name: 'result',
|
|
312
|
+
type: 'widget'
|
|
313
|
+
}]
|
|
314
|
+
},
|
|
315
|
+
genFunc: generateFunc
|
|
316
|
+
},
|
|
317
|
+
functionAnalysis: {
|
|
318
|
+
metadata: {
|
|
319
|
+
tags: [FUNC_TYPES.FUNCTION_ANALYSIS],
|
|
320
|
+
inputs: [],
|
|
321
|
+
outputs: [{
|
|
322
|
+
name: 'result',
|
|
323
|
+
type: 'view'
|
|
324
|
+
}]
|
|
325
|
+
},
|
|
326
|
+
genFunc: generateFunc
|
|
327
|
+
},
|
|
328
|
+
converter: {
|
|
329
|
+
metadata: {
|
|
330
|
+
tags: [FUNC_TYPES.CONVERTER],
|
|
331
|
+
inputs: [{
|
|
332
|
+
name: 'value',
|
|
333
|
+
type: 'dynamic'
|
|
334
|
+
}],
|
|
335
|
+
outputs: [{
|
|
336
|
+
name: 'result',
|
|
337
|
+
type: 'dynamic'
|
|
338
|
+
}]
|
|
339
|
+
},
|
|
340
|
+
genFunc: generateFunc
|
|
341
|
+
},
|
|
342
|
+
demo: {
|
|
343
|
+
metadata: {
|
|
344
|
+
tags: [],
|
|
345
|
+
inputs: [],
|
|
346
|
+
outputs: []
|
|
347
|
+
},
|
|
348
|
+
genFunc: generateFunc
|
|
349
|
+
},
|
|
350
|
+
treeBrowser: {
|
|
351
|
+
metadata: {
|
|
352
|
+
tags: [],
|
|
353
|
+
inputs: [{
|
|
354
|
+
name: 'treeNode',
|
|
355
|
+
type: 'dynamic'
|
|
356
|
+
}, {
|
|
357
|
+
name: 'browseView',
|
|
358
|
+
type: 'view'
|
|
359
|
+
}],
|
|
360
|
+
outputs: []
|
|
361
|
+
},
|
|
362
|
+
genFunc: generateFunc
|
|
363
|
+
},
|
|
364
|
+
model: {
|
|
365
|
+
metadata: {
|
|
366
|
+
tags: [FUNC_TYPES.MODEL],
|
|
367
|
+
inputs: [],
|
|
368
|
+
outputs: []
|
|
369
|
+
},
|
|
370
|
+
genFunc: generateFunc
|
|
139
371
|
}
|
|
140
372
|
};
|
|
141
373
|
|
|
@@ -145,8 +377,10 @@ function generateClassFunc(annotation, className, sep = '\n') {
|
|
|
145
377
|
}
|
|
146
378
|
|
|
147
379
|
/** Generates a DG function. */
|
|
148
|
-
function generateFunc(annotation, funcName, sep = '\n') {
|
|
149
|
-
|
|
380
|
+
function generateFunc(annotation, funcName, sep = '\n', className = '', inputs = [], isAsync = false) {
|
|
381
|
+
let funcSigNature = inputs.map(e => `${e.name}: ${e.type}`).join(', ');
|
|
382
|
+
let funcArguments = inputs.map(e => e.name).join(', ');
|
|
383
|
+
return annotation + `export ${isAsync ? 'async ' : ''}function ${funcName}(${funcSigNature}) {${sep} return ${className.length > 0 ? `${className}.` : ''}${funcName}(${funcArguments});${sep}}${sep.repeat(2)}`;
|
|
150
384
|
}
|
|
151
385
|
function generateImport(className, path, sep = '\n') {
|
|
152
386
|
return `import {${className}} from '${path}';${sep}`;
|
package/package.json
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "datagrok-tools",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.14.0",
|
|
4
4
|
"description": "Utility to upload and publish packages to Datagrok",
|
|
5
5
|
"homepage": "https://github.com/datagrok-ai/public/tree/master/tools#readme",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@babel/parser": "^7.26.10",
|
|
8
8
|
"@babel/runtime": "^7.23.8",
|
|
9
9
|
"@babel/traverse": "^7.23.7",
|
|
10
|
+
"@typescript-eslint/typescript-estree": "^8.31.1",
|
|
11
|
+
"@typescript-eslint/visitor-keys": "^8.31.1",
|
|
10
12
|
"archiver": "^4.0.2",
|
|
11
13
|
"archiver-promise": "^1.0.0",
|
|
14
|
+
"estraverse": "^5.3.0",
|
|
12
15
|
"fs": "^0.0.1-security",
|
|
13
16
|
"ignore-walk": "^3.0.4",
|
|
14
17
|
"inquirer": "^7.3.3",
|
|
@@ -1,75 +1,243 @@
|
|
|
1
|
-
const fs = require(
|
|
2
|
-
const path = require(
|
|
3
|
-
const {parse} = require('@babel/parser');
|
|
4
|
-
const traverse = require('@babel/traverse').default;
|
|
5
|
-
const {reservedDecorators, getFuncAnnotation, generateImport, generateExport} = require('../bin/utils/func-generation');
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
6
3
|
|
|
4
|
+
const tsParser = require("@typescript-eslint/typescript-estree");
|
|
5
|
+
const generate = require('@babel/generator').default;
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
reservedDecorators,
|
|
9
|
+
getFuncAnnotation,
|
|
10
|
+
generateImport,
|
|
11
|
+
generateExport,
|
|
12
|
+
typesToAnnotation
|
|
13
|
+
} = require("../bin/utils/func-generation");
|
|
14
|
+
|
|
15
|
+
const baseImport = '\n';
|
|
7
16
|
|
|
8
17
|
class FuncGeneratorPlugin {
|
|
9
|
-
constructor(options = {outputPath:
|
|
18
|
+
constructor(options = { outputPath: "./src/package.g.ts" }) {
|
|
10
19
|
this.options = options;
|
|
11
20
|
}
|
|
12
21
|
|
|
13
22
|
apply(compiler) {
|
|
14
|
-
const srcDirPath = path.join(compiler.context,
|
|
15
|
-
let packageFilePath = path.join(srcDirPath,
|
|
16
|
-
if(!fs.existsSync(packageFilePath))
|
|
17
|
-
packageFilePath = path.join(srcDirPath,
|
|
23
|
+
const srcDirPath = path.join(compiler.context, "src");
|
|
24
|
+
let packageFilePath = path.join(srcDirPath, "package.ts");
|
|
25
|
+
if (!fs.existsSync(packageFilePath))
|
|
26
|
+
packageFilePath = path.join(srcDirPath, "package.js");
|
|
18
27
|
const tsFiles = this._getTsFiles(srcDirPath);
|
|
19
28
|
const genImports = [];
|
|
20
29
|
const genExports = [];
|
|
21
30
|
|
|
22
|
-
compiler.hooks.compilation.tap(
|
|
31
|
+
compiler.hooks.compilation.tap("FuncGeneratorPlugin", (_compilation) => {
|
|
23
32
|
this._clearGeneratedFile();
|
|
24
33
|
|
|
25
34
|
for (const file of tsFiles) {
|
|
26
|
-
const content = fs.readFileSync(file,
|
|
27
|
-
if
|
|
35
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
36
|
+
if(!content.includes('@grok.decorators.'))
|
|
28
37
|
continue;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
38
|
+
|
|
39
|
+
if (!content) continue;
|
|
40
|
+
const ast = tsParser.parse(content, {
|
|
41
|
+
sourceType: "module",
|
|
42
|
+
plugins: [
|
|
43
|
+
["decorators", { decoratorsBeforeExport: true }],
|
|
44
|
+
"classProperties",
|
|
45
|
+
"typescript",
|
|
46
|
+
],
|
|
35
47
|
});
|
|
36
48
|
const functions = [];
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
49
|
+
let imports = new Set();
|
|
50
|
+
this._walk(ast, (node, parentClass) => {
|
|
51
|
+
const decorators = node.decorators;
|
|
52
|
+
if (!decorators || decorators.length === 0) return;
|
|
53
|
+
|
|
54
|
+
if (node?.type === "ClassDeclaration")
|
|
55
|
+
this._addNodeData(node, file, srcDirPath, functions, imports, genImports, genExports);
|
|
56
|
+
|
|
57
|
+
if (node?.type === "MethodDefinition")
|
|
58
|
+
this._addNodeData(node, file, srcDirPath, functions, imports, genImports, genExports, parentClass);
|
|
42
59
|
});
|
|
60
|
+
this._insertImports([...imports]);
|
|
61
|
+
fs.appendFileSync(this.options.outputPath, functions.join(""), "utf-8");
|
|
62
|
+
}
|
|
63
|
+
this._checkPackageFileForDecoratorsExport(packageFilePath);
|
|
64
|
+
// Uncommment to add obvious import/export
|
|
65
|
+
// this._writeToPackageFile(packageFilePath, genImports, genExports);
|
|
66
|
+
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
_addNodeData(node, file, srcDirPath, functions, imports, genImports, genExports, parent = undefined) {
|
|
71
|
+
if (!node.decorators || !node.decorators.length || node.decorators?.length === 0)
|
|
72
|
+
return;
|
|
73
|
+
|
|
74
|
+
function modifyImportPath(dirPath, filePath) {
|
|
75
|
+
const relativePath = path.relative(dirPath, filePath);
|
|
76
|
+
return `./${relativePath.slice(0, relativePath.length - 3).replace(/\\/g, '/')}`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const decorator = node.decorators[0];
|
|
80
|
+
const exp = decorator.expression;
|
|
81
|
+
const name = exp.callee?.property?.name || exp.callee?.name;
|
|
82
|
+
const identifierName = node.id?.name || node.key?.name;
|
|
83
|
+
const className = parent?.id?.name || parent?.key?.name;
|
|
84
|
+
|
|
85
|
+
if (!name) return;
|
|
86
|
+
|
|
87
|
+
const decoratorOptions = this._readDecoratorOptions(exp.arguments[0].properties);
|
|
88
|
+
decoratorOptions.set('tags', [...(reservedDecorators[name]['metadata']['tags'] ?? [] ), ...(decoratorOptions.get('tags') ?? [])])
|
|
89
|
+
const functionParams = node?.type === 'MethodDefinition' ? this._readMethodParamas(node) : [];
|
|
90
|
+
const annotationByReturnType = node?.type === 'MethodDefinition' ? this._readFunctionReturnTypeInfo(node) : '';
|
|
91
|
+
const annotationByReturnTypeObj = { name: 'result', type: annotationByReturnType };
|
|
92
|
+
const isMethodAsync = this._isMethodAsync(node);
|
|
93
|
+
let importString = generateImport(node?.type === 'MethodDefinition' ? className : identifierName, modifyImportPath(path.dirname(this.options.outputPath), file));
|
|
94
|
+
imports.add(importString);
|
|
95
|
+
const funcName = `${node?.type === 'MethodDefinition' ? '' : '_'}${identifierName}`;
|
|
96
|
+
const funcAnnotaionOptions = {
|
|
97
|
+
...reservedDecorators[name]['metadata'],
|
|
98
|
+
...(annotationByReturnType ? {outputs: [annotationByReturnTypeObj ?? {}]} : {}),
|
|
99
|
+
...Object.fromEntries(decoratorOptions),
|
|
100
|
+
...{inputs: functionParams},
|
|
101
|
+
...{isAsync: isMethodAsync}
|
|
102
|
+
};
|
|
103
|
+
if (!funcAnnotaionOptions.name)
|
|
104
|
+
funcAnnotaionOptions.name = identifierName;
|
|
105
|
+
functions.push(reservedDecorators[name]['genFunc'](getFuncAnnotation(funcAnnotaionOptions), identifierName,'\n', (className ?? ''), functionParams, funcAnnotaionOptions.isAsync ?? false));
|
|
106
|
+
genImports.push(generateImport(funcName, modifyImportPath(srcDirPath, this.options.outputPath)));
|
|
107
|
+
genExports.push(generateExport(funcName));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
_isMethodAsync(node){
|
|
111
|
+
let result = false;
|
|
112
|
+
if (node.type === 'MethodDefinition')
|
|
113
|
+
result = node.value.async;
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
_readImports (content) {
|
|
118
|
+
const importRegex = /(import(?:[\s\w{},*]+from\s*)?['"]([^'"]+)['"];)/g;
|
|
119
|
+
const results = [];
|
|
43
120
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
121
|
+
let match;
|
|
122
|
+
while ((match = importRegex.exec(content)) !== null) {
|
|
123
|
+
results.push(`${match[1]}\n`);
|
|
124
|
+
}
|
|
125
|
+
return results;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
_readDecoratorOptions(properties){
|
|
129
|
+
const resultMap = new Map();
|
|
130
|
+
|
|
131
|
+
for(let prop of properties)
|
|
132
|
+
resultMap.set(prop.key.name, this._evalLiteral(prop.value));
|
|
133
|
+
|
|
134
|
+
return resultMap;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
_evalLiteral(node) {
|
|
138
|
+
if (!node) return null;
|
|
139
|
+
|
|
140
|
+
switch (node.type) {
|
|
141
|
+
case 'Literal':
|
|
142
|
+
return node.value;
|
|
143
|
+
|
|
144
|
+
case 'ArrayExpression':
|
|
145
|
+
return node.elements.map(el => this._evalLiteral(el));
|
|
146
|
+
|
|
147
|
+
case 'ObjectExpression':
|
|
148
|
+
return Object.fromEntries(
|
|
149
|
+
node.properties.map(p => {
|
|
150
|
+
return [p.key.name || p.key.value, this._evalLiteral(p.value)];
|
|
151
|
+
})
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
default:
|
|
155
|
+
return '';
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
_readMethodParamas(node) {
|
|
160
|
+
const params = node?.value?.params?.map(param => {
|
|
161
|
+
let baseParam = param.type === 'TSNonNullExpression' ? param.expression : param;
|
|
162
|
+
const options = param.decorators?.length > 0? Object.fromEntries(this._readDecoratorOptions(param.decorators[0]?.expression?.arguments[0].properties)) : undefined;
|
|
163
|
+
|
|
164
|
+
// Commented code finds value by default of function's variable
|
|
165
|
+
// let defaultValue = undefined;
|
|
166
|
+
if(baseParam.type === 'AssignmentPattern')
|
|
167
|
+
{
|
|
168
|
+
// if (baseParam?.right?.type === 'Literal')
|
|
169
|
+
// defaultValue = baseParam?.right?.raw;
|
|
170
|
+
baseParam = baseParam?.left;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (baseParam.type === 'RestElement' || baseParam.type === 'Identifier') {
|
|
174
|
+
let name =
|
|
175
|
+
baseParam.type === 'RestElement'
|
|
176
|
+
? `...${baseParam.argument.name}`
|
|
177
|
+
: baseParam.name;
|
|
178
|
+
if (baseParam?.argument?.typeAnnotation)
|
|
179
|
+
name += ': ' + generate(baseParam.argument.typeAnnotation.typeAnnotation).code;
|
|
180
|
+
|
|
181
|
+
let type = '';
|
|
182
|
+
if (baseParam?.typeAnnotation?.typeAnnotation?.typeName || baseParam?.typeAnnotation?.typeAnnotation?.elementType?.typeName)
|
|
183
|
+
type = 'any';
|
|
184
|
+
else if (baseParam?.typeAnnotation?.typeAnnotation)
|
|
185
|
+
type = generate(baseParam.typeAnnotation.typeAnnotation).code;
|
|
186
|
+
let params = baseParam.typeAnnotation.typeAnnotation.typeArguments?.params;
|
|
187
|
+
if(type !== 'any' && params && params.length > 0)
|
|
188
|
+
type += `<${params.map((e)=>e.typeName.name).join(',')}>`;
|
|
189
|
+
return { name: name, type: type, options: options };
|
|
53
190
|
}
|
|
191
|
+
// Commented code belove sets more strong types for ObjectPatterns and ArrayPatterns
|
|
192
|
+
// else if (baseParam.type === 'ObjectPattern' || baseParam.type === 'ArrayPattern') {
|
|
193
|
+
// let name = '';
|
|
194
|
+
// if (baseParam.type === 'ObjectPattern') {
|
|
195
|
+
// const properties = baseParam.properties.map(prop => {
|
|
196
|
+
// if (prop.type === 'Property' && prop.key.type === 'Identifier')
|
|
197
|
+
// return prop.key.name;
|
|
198
|
+
// else if (prop.type === 'RestElement' && prop.argument.type === 'Identifier')
|
|
199
|
+
// return `...${prop.argument.name}`;
|
|
200
|
+
// else
|
|
201
|
+
// return generate(prop).code;
|
|
202
|
+
// });
|
|
203
|
+
// name = `{ ${properties.join(', ')} }`;
|
|
204
|
+
// } else {
|
|
205
|
+
// const elements = baseParam.elements.map(elem => {
|
|
206
|
+
// if (elem) {
|
|
207
|
+
// if (elem.type === 'Identifier')
|
|
208
|
+
// return elem.name;
|
|
209
|
+
// else
|
|
210
|
+
// return generate(elem).code;
|
|
211
|
+
// } else return '';
|
|
212
|
+
// });
|
|
213
|
+
// name = `[${elements.join(', ')}]`;
|
|
214
|
+
// }
|
|
215
|
+
|
|
216
|
+
// let type = '';
|
|
217
|
+
// if (baseParam.typeAnnotation)
|
|
218
|
+
// type = generate(baseParam.typeAnnotation.typeAnnotation).code;
|
|
219
|
+
// else
|
|
220
|
+
// type = 'any';
|
|
54
221
|
|
|
55
|
-
|
|
222
|
+
// return { name: name, type: type, options: options };
|
|
223
|
+
// }
|
|
224
|
+
return { name: 'value', type: 'any', options: undefined };
|
|
56
225
|
});
|
|
226
|
+
return params;
|
|
57
227
|
}
|
|
58
228
|
|
|
59
229
|
_getTsFiles(root) {
|
|
60
230
|
const tsFiles = [];
|
|
61
231
|
const extPattern = /\.tsx?$/;
|
|
62
|
-
const excludedFiles = [
|
|
232
|
+
const excludedFiles = ["package-test.ts", "package.g.ts"];
|
|
63
233
|
|
|
64
234
|
function findFiles(dir) {
|
|
65
235
|
const files = fs.readdirSync(dir);
|
|
66
236
|
for (const file of files) {
|
|
67
237
|
const fullPath = path.join(dir, file);
|
|
68
|
-
if (fs.statSync(fullPath).isDirectory())
|
|
69
|
-
|
|
70
|
-
else if (extPattern.test(file) && !excludedFiles.includes(file))
|
|
238
|
+
if (fs.statSync(fullPath).isDirectory()) findFiles(fullPath);
|
|
239
|
+
else if (extPattern.test(file) && !excludedFiles.includes(file))
|
|
71
240
|
tsFiles.push(fullPath);
|
|
72
|
-
|
|
73
241
|
}
|
|
74
242
|
}
|
|
75
243
|
|
|
@@ -77,70 +245,120 @@ class FuncGeneratorPlugin {
|
|
|
77
245
|
return tsFiles;
|
|
78
246
|
}
|
|
79
247
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
248
|
+
_walk(node, visitor, parent = null) {
|
|
249
|
+
if (!node || typeof node !== "object") return;
|
|
250
|
+
|
|
251
|
+
visitor(node, parent);
|
|
252
|
+
|
|
253
|
+
for (const key in node) {
|
|
254
|
+
const value = node[key];
|
|
255
|
+
if (Array.isArray(value)) {
|
|
256
|
+
value.forEach((child) => {
|
|
257
|
+
if (child && typeof child.type === "string") {
|
|
258
|
+
this._walk(child, visitor, node.type === 'ClassDeclaration'? node : parent );
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
} else if (value && typeof value.type === "string") {
|
|
262
|
+
this._walk(value, visitor, node.type === 'ClassDeclaration'? node : parent );
|
|
263
|
+
}
|
|
93
264
|
}
|
|
94
|
-
fs.writeFileSync(filePath, content, 'utf-8');
|
|
95
265
|
}
|
|
96
266
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
267
|
+
_readFunctionReturnTypeInfo(node) {
|
|
268
|
+
let resultType = 'any';
|
|
269
|
+
let isArray = false;
|
|
270
|
+
const annotation = node.value?.returnType?.typeAnnotation;
|
|
271
|
+
if (annotation) {
|
|
272
|
+
if (annotation?.typeName?.name === 'Promise')
|
|
273
|
+
{
|
|
274
|
+
const argumnets = annotation.typeArguments?.params;
|
|
275
|
+
if (argumnets && argumnets.length===1)
|
|
276
|
+
{
|
|
277
|
+
if (argumnets[0].typeName)
|
|
278
|
+
resultType = argumnets[0].typeName?.right?.name ?? argumnets[0].typeName?.name;
|
|
279
|
+
else if (argumnets[0].type !== 'TSArrayType')
|
|
280
|
+
resultType = this._getTypeNameFromNode(argumnets[0]);
|
|
281
|
+
else if (argumnets[0].elementType.type !== 'TSTypeReference'){
|
|
282
|
+
isArray = true;
|
|
283
|
+
resultType = this._getTypeNameFromNode(argumnets[0]?.elementType);
|
|
284
|
+
}
|
|
285
|
+
else{
|
|
286
|
+
isArray = true;
|
|
287
|
+
resultType = argumnets[0].elementType?.typeName?.name || argumnets[0].elementType?.typeName?.right?.name;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
else{
|
|
292
|
+
if (annotation.type === 'TSTypeReference')
|
|
293
|
+
resultType = annotation.typeName?.right?.name ?? annotation.typeName?.name;
|
|
294
|
+
else if (annotation.type !== 'TSArrayType')
|
|
295
|
+
resultType = this._getTypeNameFromNode(annotation);
|
|
296
|
+
else if (annotation.elementType.type !== 'TSTypeReference'){
|
|
297
|
+
isArray = true;
|
|
298
|
+
resultType = this._getTypeNameFromNode(annotation?.elementType);
|
|
299
|
+
}
|
|
300
|
+
else{
|
|
301
|
+
isArray = true;
|
|
302
|
+
resultType = (annotation?.elementType?.typeName?.name || annotation?.elementType?.typeName?.right?.name);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
resultType = typesToAnnotation[resultType];
|
|
307
|
+
if (isArray && resultType)
|
|
308
|
+
resultType = `list<${resultType}>`
|
|
309
|
+
return resultType;
|
|
310
|
+
}
|
|
100
311
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
return
|
|
312
|
+
_getTypeNameFromNode(typeNode) {
|
|
313
|
+
if (typeNode.type === 'TSTypeReference') {
|
|
314
|
+
return typeNode.typeName.name;
|
|
315
|
+
} else if (typeNode.type === 'TSVoidKeyword') {
|
|
316
|
+
return 'void';
|
|
317
|
+
} else if (typeNode.type === 'TSNumberKeyword') {
|
|
318
|
+
return 'number';
|
|
319
|
+
} else if (typeNode.type === 'TSStringKeyword') {
|
|
320
|
+
return 'string';
|
|
321
|
+
} else if (typeNode.type === 'TSBooleanKeyword') {
|
|
322
|
+
return 'boolean';
|
|
323
|
+
} else {
|
|
324
|
+
return typeNode.type;
|
|
104
325
|
}
|
|
326
|
+
}
|
|
105
327
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const options = {};
|
|
110
|
-
if (name in reservedDecorators) {
|
|
111
|
-
if (exp.arguments && exp.arguments.length === 1) {
|
|
112
|
-
const props = exp.arguments[0].properties;
|
|
113
|
-
for (const prop of props)
|
|
114
|
-
options[prop.key.name] = prop.value.value;
|
|
115
|
-
}
|
|
328
|
+
_clearGeneratedFile() {
|
|
329
|
+
fs.writeFileSync(this.options.outputPath, baseImport);
|
|
330
|
+
}
|
|
116
331
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}), className));
|
|
332
|
+
_checkPackageFileForDecoratorsExport(packagePath){
|
|
333
|
+
const content = fs.readFileSync(packagePath, "utf-8");
|
|
334
|
+
const decoratorsExportRegex = /export\s*\*\s*from\s*'\.\/package\.g';/;
|
|
335
|
+
if (!decoratorsExportRegex.test(content))
|
|
336
|
+
console.warn(`\nWARNING: Your package doesn't export package.g.ts file to package.ts \n please add "export * from './package.g';" to the package.ts file.\n`);
|
|
337
|
+
}
|
|
124
338
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
339
|
+
_writeToPackageFile(filePath, imports, exp) {
|
|
340
|
+
if (imports.length !== exp.length) return;
|
|
341
|
+
let content = fs.readFileSync(filePath, "utf-8");
|
|
342
|
+
for (let i = 0; i < imports.length; i++) {
|
|
343
|
+
const importStatement = imports[i];
|
|
344
|
+
const exportStatement = exp[i];
|
|
345
|
+
if (!content.includes(importStatement.trim()))
|
|
346
|
+
content = importStatement + content + exportStatement;
|
|
128
347
|
}
|
|
348
|
+
fs.writeFileSync(filePath, content, "utf-8");
|
|
129
349
|
}
|
|
130
350
|
|
|
131
|
-
_insertImports(
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
(content[lastImportLoc] === '\n' ? '' : '\n') +
|
|
142
|
-
content.slice(lastImportLoc, content.length);
|
|
351
|
+
_insertImports(importArray) {
|
|
352
|
+
if (fs.existsSync(this.options.outputPath)) {
|
|
353
|
+
const content = fs.readFileSync(this.options.outputPath, "utf-8");
|
|
354
|
+
if (content)
|
|
355
|
+
importArray.push(content);
|
|
356
|
+
const output = importArray.join("");
|
|
357
|
+
fs.writeFileSync(this.options.outputPath, `${output}`, "utf-8");
|
|
358
|
+
}
|
|
359
|
+
else
|
|
360
|
+
fs.writeFileSync(this.options.outputPath, `${baseImport}\n${importArray.join("")}`, "utf-8");
|
|
143
361
|
}
|
|
144
362
|
}
|
|
145
363
|
|
|
146
|
-
module.exports = FuncGeneratorPlugin;
|
|
364
|
+
module.exports = FuncGeneratorPlugin;
|