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