datagrok-tools 4.14.5 → 4.14.7

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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # Datagrok-tools changelog
2
2
 
3
+ ## 4.14.6 (2025-05-29)
4
+
5
+ ### Features
6
+
7
+ * Decorators added ability to set hardcoded values
8
+ * Decorators added ability to get outputs from multiple parameters
9
+
10
+
3
11
  ## 4.14.2 (2025-05-16)
4
12
 
5
13
  ### Features
@@ -21,20 +21,21 @@ var testUtils = _interopRequireWildcard(require("../utils/test-utils"));
21
21
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
22
22
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
23
23
  const warns = ['Latest package version', 'Datagrok API version should contain'];
24
- const forbidenNames = ['function', 'class', 'export'];
24
+ const forbiddenNames = ['function', 'class', 'export'];
25
+ const namesInFiles = new Map();
25
26
  function check(args) {
26
27
  const nOptions = Object.keys(args).length - 1;
27
28
  if (args['_'].length !== 1 || nOptions > 2 || nOptions > 0 && !args.r && !args.recursive) return false;
28
29
  const curDir = process.cwd();
29
- if (args.recursive) return runChecksRec(curDir);else {
30
+ if (args.recursive) return runChecksRec(curDir, args.soft ?? false);else {
30
31
  if (!utils.isPackageDir(curDir)) {
31
32
  color.error('File `package.json` not found. Run the command from the package directory');
32
33
  return false;
33
34
  }
34
- return runChecks(curDir);
35
+ return runChecks(curDir, args.soft ?? false);
35
36
  }
36
37
  }
37
- function runChecks(packagePath) {
38
+ function runChecks(packagePath, soft = false) {
38
39
  const files = _ignoreWalk.default.sync({
39
40
  path: packagePath,
40
41
  ignoreFiles: ['.npmignore', '.gitignore']
@@ -77,20 +78,20 @@ function runChecks(packagePath) {
77
78
  if (errors.length) {
78
79
  console.log(`Checking package ${_path.default.basename(packagePath)}...`);
79
80
  showError(errors);
80
- if (json.version.startsWith('0') || errors.every(w => warns.some(ww => w.includes(ww)))) return true;
81
+ if (soft || json.version.startsWith('0') || errors.every(w => warns.some(ww => w.includes(ww)))) return true;
81
82
  testUtils.exitWithCode(1);
82
83
  }
83
84
  console.log(`Checking package ${_path.default.basename(packagePath)}...\t\t\t\u2713 OK`);
84
85
  return true;
85
86
  }
86
- function runChecksRec(dir) {
87
+ function runChecksRec(dir, soft = false) {
87
88
  const files = _fs.default.readdirSync(dir);
88
89
  for (const file of files) {
89
90
  const filepath = _path.default.join(dir, file);
90
91
  const stats = _fs.default.statSync(filepath);
91
92
  if (stats.isDirectory()) {
92
- if (utils.isPackageDir(filepath)) return runChecks(filepath);else {
93
- if (file !== 'node_modules' && !file.startsWith('.')) runChecksRec(_path.default.join(dir, file));
93
+ if (utils.isPackageDir(filepath)) return runChecks(filepath, soft);else {
94
+ if (file !== 'node_modules' && !file.startsWith('.')) runChecksRec(_path.default.join(dir, file), soft);
94
95
  }
95
96
  }
96
97
  }
@@ -315,13 +316,20 @@ function checkFuncSignatures(packagePath, files) {
315
316
  warnings.push(`File ${file}, function ${f.name}:\n${vr.message}`);
316
317
  }
317
318
  }
318
- let wrongInputNames = f.inputs.filter(e => forbidenNames.includes(e?.name ?? ''));
319
+ let wrongInputNames = f.inputs.filter(e => forbiddenNames.includes(e?.name ?? ''));
320
+ if (f.name) {
321
+ console.log(f);
322
+ if (namesInFiles.has(f.name)) namesInFiles.get(f.name)?.push(file);else namesInFiles.set(f.name, [file]);
323
+ }
319
324
  if (wrongInputNames.length > 0) warnings.push(`File ${file}, function ${f.name}: Wrong input names: (${wrongInputNames.map(e => e.name).join(', ')})`);
320
325
  if (f.isInvalidateOnWithoutCache) warnings.push(`File ${file}, function ${f.name}: Can't use invalidateOn without cache, please follow this example: 'meta.cache.invalidateOn'`);
321
- if (f.cache) if (!utils.cahceValues.includes(f.cache)) warnings.push(`File ${file}, function ${f.name}: unsupposed variable for cache : ${f.cache}`);
326
+ if (f.cache) if (!utils.cacheValues.includes(f.cache)) warnings.push(`File ${file}, function ${f.name}: unsupposed variable for cache : ${f.cache}`);
322
327
  if (f.invalidateOn) if (!utils.isValidCron(f.invalidateOn)) warnings.push(`File ${file}, function ${f.name}: unsupposed variable for invalidateOn : ${f.invalidateOn}`);
323
328
  }
324
329
  }
330
+ for (const [name, files] of namesInFiles) {
331
+ if (files.length > 1) warnings.push(`Duplicate names ('${name}'): \n ${files.join('\n ')}`);
332
+ }
325
333
  return warnings;
326
334
  }
327
335
  const sharedLibExternals = {
@@ -519,7 +527,8 @@ function getFuncMetadata(script, fileExtention) {
519
527
  if (match) {
520
528
  if (!isHeader) isHeader = true;
521
529
  const param = match[1];
522
- if (param === 'name') data.name = line.match(utils.nameAnnRegex)?.[2];else if (param === 'description') data.description = match[2];else if (param === 'input') {
530
+ console.log(param);
531
+ if (param === 'name') data.name = line.match(utils.nameAnnRegex)?.[2]?.toLocaleLowerCase();else if (param === 'description') data.description = match[2];else if (param === 'input') {
523
532
  data.inputs.push({
524
533
  type: match[2],
525
534
  name: match[3]
@@ -539,8 +548,8 @@ function getFuncMetadata(script, fileExtention) {
539
548
  }
540
549
  if (isHeader) {
541
550
  const nm = line.match(utils.nameRegex);
542
- if (nm && !match) {
543
- data.name = data.name || nm[1];
551
+ if (nm) data.name = nm[1]?.toLocaleLowerCase();
552
+ if (data.name && !match) {
544
553
  funcData.push(data);
545
554
  data = {
546
555
  name: '',
@@ -4,7 +4,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.cahceValues = exports.absUrlRegex = exports.TemplateBuilder = void 0;
7
+ exports.cacheValues = exports.absUrlRegex = exports.TemplateBuilder = void 0;
8
8
  exports.camelCaseToKebab = camelCaseToKebab;
9
9
  exports.checkScriptLocation = checkScriptLocation;
10
10
  exports.commentMap = void 0;
@@ -165,7 +165,7 @@ function getParam(name, script, comment = '#') {
165
165
  return match ? match[1]?.trim() : null;
166
166
  }
167
167
  ;
168
- const cahceValues = exports.cahceValues = ['all', 'server', 'client', 'true'];
168
+ const cacheValues = exports.cacheValues = ['all', 'server', 'client', 'true'];
169
169
  function isValidCron(cronExpression) {
170
170
  const cronRegex = /^(\*|([0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])|\*\/([0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])) (\*|([0-9]|1[0-9]|2[0-3])|\*\/([0-9]|1[0-9]|2[0-3])) (\*|([1-9]|1[0-9]|2[0-9]|3[0-1])|\*\/([1-9]|1[0-9]|2[0-9]|3[0-1])) (\*|([1-9]|1[0-2])|\*\/([1-9]|1[0-2])) (\*|([0-6])|\*\/([0-6]))$/;
171
171
  return cronRegex.test(cronExpression);
@@ -183,15 +183,15 @@ const dgToTsTypeMap = exports.dgToTsTypeMap = {
183
183
  view: 'DG.View'
184
184
  };
185
185
  const propertyTypes = exports.propertyTypes = ['bool', 'int', 'double', 'string', 'datetime', 'object', 'column', 'dataframe', 'bitset', 'cell', 'string_list', 'map'];
186
- const headerTags = exports.headerTags = ['name', 'description', 'help-url', 'input', 'output', 'tags', 'sample', 'language', 'returns', 'test', 'sidebar', 'condition', 'top-menu', 'environment', 'require', 'editor-for', 'schedule', 'reference', 'editor', 'meta'];
186
+ const headerTags = exports.headerTags = ['name', 'description', 'help-url', 'input', 'output', 'tags', 'sample', 'language', 'returns', 'test', 'sidebar', 'condition', 'top-menu', 'environment', 'require', 'editor-for', 'schedule', 'reference', 'editor', 'meta', 'connection', 'friendlyName'];
187
187
  const fileParamRegex = exports.fileParamRegex = {
188
188
  py: new RegExp(`^\#\\s*((?:${headerTags.join('|')})[^:]*): *([^\\s\\[\\{]+) ?([^\\s\\[\\{]+)?[^\\n]*$`),
189
189
  ts: new RegExp(`^\/\/\\s*((?:${headerTags.join('|')})[^:]*): *([^\\s\\[\\{]+) ?([^\\s\\[\\{]+)?[^\\n]*$`),
190
190
  js: new RegExp(`^\/\/\\s*((?:${headerTags.join('|')})[^:]*): *([^\\s\\[\\{]+) ?([^\\s\\[\\{]+)?[^\\n]*$`),
191
191
  sql: new RegExp(`^--\\s*((?:${headerTags.join('|')})[^:]*): *([^\\s\\[\\{]+) ?([^\\s\\[\\{]+)?[^\\n]*$`)
192
192
  };
193
- const nameAnnRegex = exports.nameAnnRegex = /\/\/\s*(name[^:]*): ([^\n\r\[\{]+)/;
194
- const nameRegex = exports.nameRegex = /(?:|(?:static)(?:export )(?:async )function )\s+([a-zA-Z_][a-zA-Z0-9_$]*)\s*\((.*?).*/;
193
+ const nameAnnRegex = exports.nameAnnRegex = /\s*(name[^:]*): ([^\n\r\[\{]+)/;
194
+ const nameRegex = exports.nameRegex = /(?:|(?:static)(?:export )(?:async ))\s+function\s+([a-zA-Z_][a-zA-Z0-9_$]*)\s*\((.*?).*/;
195
195
  const absUrlRegex = exports.absUrlRegex = new RegExp('^(?:[a-z+]+:)?//', 'i');
196
196
  function getScriptOutputType(script, comment = '#') {
197
197
  const regex = new RegExp(`${comment}\\s*output:\\s?([a-z_]+)\\s*`);
@@ -245,7 +245,7 @@ async function runScript(script, path, verbose = false) {
245
245
  }
246
246
  } catch (error) {
247
247
  console.error(`Execution failed: ${error.message}`);
248
- throw new Error(`Cant run script ${script}`);
248
+ throw new Error(`Error executing '${script}'. Error message: ${error.message}`);
249
249
  }
250
250
  }
251
251
  function setHost(host, configFile) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "datagrok-tools",
3
- "version": "4.14.5",
3
+ "version": "4.14.7",
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": {
@@ -125,12 +125,7 @@ class FuncGeneratorPlugin {
125
125
  ]);
126
126
  const functionParams =
127
127
  node?.type === "MethodDefinition" ? this._readMethodParamas(node) : [];
128
- const annotationByReturnType = node?.type === "MethodDefinition" ? this._readReturnType(node) : "";
129
-
130
- const annotationByReturnTypeObj = {
131
- name: "result",
132
- type: annotationByReturnType,
133
- };
128
+ const annotationByReturnObj = node?.type === "MethodDefinition" ? this._readOutputsFromReturnType(node) : undefined;
134
129
  const isMethodAsync = this._isMethodAsync(node);
135
130
  let importString = generateImport(
136
131
  node?.type === "MethodDefinition" ? className : identifierName,
@@ -142,8 +137,8 @@ class FuncGeneratorPlugin {
142
137
  }${identifierName}`;
143
138
  const funcAnnotaionOptions = {
144
139
  ...reservedDecorators[name]["metadata"],
145
- ...(annotationByReturnType
146
- ? { outputs: [annotationByReturnTypeObj ?? {}] }
140
+ ...(annotationByReturnObj
141
+ ? { outputs: annotationByReturnObj ?? [] }
147
142
  : {}),
148
143
  ...Object.fromEntries(decoratorOptions),
149
144
  ...{ inputs: functionParams },
@@ -345,21 +340,14 @@ class FuncGeneratorPlugin {
345
340
  }
346
341
  }
347
342
 
348
- _readReturnType(node) {
343
+ _readReturnType(annotation) {
349
344
  let resultType = "dynamic";
350
- let isArray = false;
351
- let annotation = node.value?.returnType?.typeAnnotation;
345
+ let isArray = false;
352
346
  if (
353
347
  annotation &&
354
348
  annotation.type !== "TSUnionType" &&
355
349
  annotation.type !== "TSIntersectionType"
356
350
  ) {
357
- // if (annotation?.typeName?.name === "Promise") {
358
- // const argumnets = annotation.typeArguments?.params;
359
- // if (argumnets && argumnets.length === 1) {
360
- // annotation = argumnets[0];
361
- // } else annotation = {};
362
- // }
363
351
 
364
352
  if (annotation.typeName || annotation.type === "TSTypeReference")
365
353
  resultType =
@@ -377,12 +365,49 @@ class FuncGeneratorPlugin {
377
365
  annotation?.elementType?.typeName?.right?.name;
378
366
  }
379
367
  }
380
-
381
368
  resultType = typesToAnnotation[resultType];
382
369
  if (isArray && resultType) resultType = `list`;
383
370
  return resultType ?? 'dynamic';
384
371
  }
385
372
 
373
+ _readOutputsFromReturnType(node) {
374
+ let results = [];
375
+ let annotation = node.value?.returnType?.typeAnnotation;
376
+
377
+ if (node?.type === 'ClassDeclaration')
378
+ return [];
379
+
380
+ if (annotation?.typeName?.name === "Promise") {
381
+ const argumnets = annotation.typeArguments?.params;
382
+ if (argumnets && argumnets.length === 1) {
383
+ annotation = argumnets[0];
384
+ } else annotation = {};
385
+ }
386
+
387
+ if (annotation?.type === "TSTypeLiteral"){
388
+ results = this._readOutputsFromReturnTypeObject(annotation);
389
+ }
390
+ else{
391
+ let resultType = this._readReturnType(annotation);
392
+ results.push({name: 'result', type: resultType});
393
+ if (resultType === 'void')
394
+ results = [];
395
+ }
396
+ return results;
397
+ }
398
+
399
+ _readOutputsFromReturnTypeObject(node) {
400
+ let i = 0;
401
+ let results = [];
402
+ for (let member of node.members) {
403
+ results.push({
404
+ name: member?.key?.name ?? `result${i}`,
405
+ type: this._readReturnType(member.typeAnnotation.typeAnnotation),
406
+ });
407
+ i++;
408
+ }
409
+ return results;
410
+ }
386
411
 
387
412
  _getTypeNameFromNode(typeNode) {
388
413
  if (typeNode.type === "TSTypeReference") {