datagrok-tools 4.13.77 → 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/bin/utils/test-utils.js +22 -5
- package/package-template/package.json +1 -1
- package/package.json +5 -2
- 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/bin/utils/test-utils.js
CHANGED
|
@@ -90,6 +90,22 @@ async function getBrowserPage(puppeteer, params = defaultLaunchParameters) {
|
|
|
90
90
|
url = await getWebUrl(url, token);
|
|
91
91
|
console.log(`Using web root: ${url}`);
|
|
92
92
|
const browser = await puppeteer.launch(params);
|
|
93
|
+
if (params.debug) {
|
|
94
|
+
const targets = await browser.targets();
|
|
95
|
+
const devtoolsTarget = targets.find(t => {
|
|
96
|
+
return t.type() === 'other' && t.url().startsWith('devtools://');
|
|
97
|
+
});
|
|
98
|
+
if (devtoolsTarget) {
|
|
99
|
+
const client = await devtoolsTarget.createCDPSession();
|
|
100
|
+
await client.send('Runtime.enable');
|
|
101
|
+
await client.send('Runtime.evaluate', {
|
|
102
|
+
expression: `
|
|
103
|
+
window.UI.viewManager.showView('network');
|
|
104
|
+
window.UI.dockController.setDockSide('bottom')
|
|
105
|
+
`
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
93
109
|
const page = await browser.newPage();
|
|
94
110
|
await page.setViewport({
|
|
95
111
|
width: 1920,
|
|
@@ -304,7 +320,7 @@ function saveCsvResults(stringToSave, csvReportDir) {
|
|
|
304
320
|
_fs.default.writeFileSync(csvReportDir, modifiedStrings.join('\n'), 'utf8');
|
|
305
321
|
color.info('Saved `test-report.csv`\n');
|
|
306
322
|
}
|
|
307
|
-
async function
|
|
323
|
+
async function runTests(testsParams, stopOnFail) {
|
|
308
324
|
let failed = false;
|
|
309
325
|
let verbosePassed = "";
|
|
310
326
|
let verboseSkipped = "";
|
|
@@ -348,7 +364,8 @@ async function helloTests(testsParams, stopOnFail) {
|
|
|
348
364
|
row["flaking"] = success && window.DG.Test.isReproducing;
|
|
349
365
|
df.changeColumnType('result', window.DG.COLUMN_TYPE.STRING);
|
|
350
366
|
df.changeColumnType('logs', window.DG.COLUMN_TYPE.STRING);
|
|
351
|
-
df.changeColumnType('memoryDelta', window.DG.COLUMN_TYPE.BIG_INT);
|
|
367
|
+
// df.changeColumnType('memoryDelta', (<any>window).DG.COLUMN_TYPE.BIG_INT);
|
|
368
|
+
|
|
352
369
|
if (resultDF === undefined) resultDF = df;else resultDF = resultDF.append(df);
|
|
353
370
|
if (row["skipped"]) {
|
|
354
371
|
verboseSkipped += `Test result : Skipped : ${time} : ${category}: ${testName} : ${result}\n`;
|
|
@@ -398,7 +415,7 @@ async function helloTests(testsParams, stopOnFail) {
|
|
|
398
415
|
}
|
|
399
416
|
async function runBrowser(testExecutionData, browserOptions, browsersId, testInvocationTimeout = 3600000) {
|
|
400
417
|
let testsToRun = {
|
|
401
|
-
func:
|
|
418
|
+
func: runTests.toString(),
|
|
402
419
|
tests: testExecutionData
|
|
403
420
|
};
|
|
404
421
|
return await timeout(async () => {
|
|
@@ -433,8 +450,8 @@ async function runBrowser(testExecutionData, browserOptions, browsersId, testInv
|
|
|
433
450
|
if (options.ciCd) window.DG.Test.isCiCd = true;
|
|
434
451
|
if (options.debug) window.DG.Test.isInDebug = true;
|
|
435
452
|
return new Promise((resolve, reject) => {
|
|
436
|
-
window.
|
|
437
|
-
window.
|
|
453
|
+
window.runTests = eval('(' + testData.func + ')');
|
|
454
|
+
window.runTests(testData.tests, options.stopOnTimeout).then(results => {
|
|
438
455
|
resolve(results);
|
|
439
456
|
}).catch(e => {
|
|
440
457
|
resolve({
|
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",
|
|
@@ -19,7 +22,7 @@
|
|
|
19
22
|
"os": "^0.1.2",
|
|
20
23
|
"papaparse": "^5.4.1",
|
|
21
24
|
"path": "^0.12.7",
|
|
22
|
-
"puppeteer": "
|
|
25
|
+
"puppeteer": "22.10.0",
|
|
23
26
|
"puppeteer-screen-recorder": "3.0.3"
|
|
24
27
|
},
|
|
25
28
|
"scripts": {
|
|
@@ -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;
|