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.
@@ -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
- if (data.inputs) {
44
- for (const input of data.inputs) {
45
- s += comment + 'input: ' + (isFileImporter && data[pseudoParams.INPUT_TYPE] ? data[pseudoParams.INPUT_TYPE] : input.type) + (input.name ? ` ${input.name}` : '') + sep;
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
- return annotation + `export function _${funcName}() {${sep} return ${funcName}();${sep}}${sep.repeat(2)}`;
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}`;
@@ -7,7 +7,7 @@
7
7
  "datagrok-api": "^1.21.3",
8
8
  "cash-dom": "^8.1.5",
9
9
  "dayjs": "^1.11.13",
10
- "@datagrok-libraries/utils": "^4.3.6"
10
+ "@datagrok-libraries/utils": "^4.5.7"
11
11
  },
12
12
  "devDependencies": {
13
13
  "datagrok-tools": "latest",
package/package.json CHANGED
@@ -1,14 +1,17 @@
1
1
  {
2
2
  "name": "datagrok-tools",
3
- "version": "4.13.78",
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('fs');
2
- const path = require('path');
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: './src/package.g.ts'}) {
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, 'src');
15
- let packageFilePath = path.join(srcDirPath, 'package.ts');
16
- if(!fs.existsSync(packageFilePath))
17
- packageFilePath = path.join(srcDirPath, 'package.js');
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('FuncGeneratorPlugin', (_compilation) => {
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, 'utf-8');
27
- if (!content)
28
- continue;
29
- const ast = parse(content, {
30
- sourceType: 'module',
31
- plugins: ['typescript', [
32
- 'decorators',
33
- {decoratorsBeforeExport: true},
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
- const imports = [];
38
-
39
- traverse(ast, {
40
- ClassDeclaration: (nodePath) => this._addNodeData(nodePath.node, file,
41
- srcDirPath, functions, imports, genImports, genExports),
42
- });
43
-
44
- if (fs.existsSync(this.options.outputPath)) {
45
- const content = fs.readFileSync(this.options.outputPath, 'utf-8');
46
- const output = content ? this._insertImports(content, imports) : imports.join('\n');
47
- fs.writeFileSync(this.options.outputPath, output, 'utf-8');
48
- } else
49
- fs.writeFileSync(this.options.outputPath, imports.join('\n'), 'utf-8');
50
-
51
-
52
- fs.appendFileSync(this.options.outputPath, functions.join('\n'), 'utf-8');
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 = ['package.ts', 'package-test.ts', 'package.g.ts'];
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
- findFiles(fullPath);
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
- return;
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, 'utf-8');
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(content, imports) {
132
- const ast = parse(content, {sourceType: 'module', plugins: ['typescript', 'decorators']});
133
- let lastImportLoc = null;
134
- traverse(ast, {
135
- ImportDeclaration(nodePath) {
136
- lastImportLoc = nodePath.node.end + 1;
137
- },
138
- });
139
- return lastImportLoc === null ? imports.join('\n') + content :
140
- content.slice(0, lastImportLoc) + imports.join('\n') +
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;