datagrok-tools 4.14.2 → 4.14.4

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.
@@ -12,18 +12,24 @@ var utils = _interopRequireWildcard(require("../utils/utils"));
12
12
  var color = _interopRequireWildcard(require("../utils/color-utils"));
13
13
  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); }
14
14
  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; }
15
+ const sep = '\n';
16
+ const packageFuncDirs = ['package.ts', 'package.g.ts'];
17
+ const apiFile = 'package-api.ts';
18
+ const curDir = process.cwd();
19
+ const srcDir = _path.default.join(curDir, 'src');
20
+ const funcFilePath = _path.default.join(_fs.default.existsSync(srcDir) ? srcDir : curDir, apiFile);
21
+ const packagePath = _path.default.join(curDir, 'package.json');
22
+ const names = new Set();
23
+ const _package = JSON.parse(_fs.default.readFileSync(packagePath, {
24
+ encoding: 'utf-8'
25
+ }));
15
26
  function generateQueryWrappers() {
16
- const curDir = process.cwd();
17
27
  const queriesDir = _path.default.join(curDir, 'queries');
18
28
  if (!_fs.default.existsSync(queriesDir)) {
19
29
  color.warn(`Directory ${queriesDir} not found`);
20
30
  console.log('Skipping API generation for queries...');
21
31
  return;
22
32
  }
23
- const packagePath = _path.default.join(curDir, 'package.json');
24
- const _package = JSON.parse(_fs.default.readFileSync(packagePath, {
25
- encoding: 'utf-8'
26
- }));
27
33
  const files = _ignoreWalk.default.sync({
28
34
  path: './queries',
29
35
  ignoreFiles: ['.npmignore', '.gitignore']
@@ -34,44 +40,30 @@ function generateQueryWrappers() {
34
40
  const filepath = _path.default.join(queriesDir, file);
35
41
  const script = _fs.default.readFileSync(filepath, 'utf8');
36
42
  if (!script) continue;
37
- const queries = script.split('--end').map(q => q.trim()).filter(q => q.length > 0);
43
+ const queries = script.split(/--\s*end/).map(q => q.trim()).filter(q => q.length > 0);
38
44
  for (const q of queries) {
39
45
  const name = utils.getScriptName(q, utils.commentMap[utils.queryExtension]);
40
46
  if (!name) continue;
47
+ checkNameColision(name);
41
48
  const tb = new utils.TemplateBuilder(utils.queryWrapperTemplate).replace('FUNC_NAME', name).replace('FUNC_NAME_LOWERCASE', name).replace('PACKAGE_NAMESPACE', _package.name);
49
+ const description = utils.getScriptDescription(q, utils.commentMap[utils.queryExtension]);
42
50
  const inputs = utils.getScriptInputs(q, utils.commentMap[utils.queryExtension]);
43
51
  const outputType = utils.getScriptOutputType(q, utils.commentMap[utils.queryExtension]);
44
- tb.replace('PARAMS_OBJECT', inputs).replace('TYPED_PARAMS', inputs)
45
- // The query output, if omitted, is a dataframe
46
- .replace('OUTPUT_TYPE', outputType === 'void' ? utils.dgToTsTypeMap['dataframe'] : outputType);
47
- wrappers.push(tb.build());
52
+ tb.replace('PARAMS_OBJECT', inputs).replace('TYPED_PARAMS', inputs).replace('FUNC_DESCRIPTION', description).replace('OUTPUT_TYPE', outputType === 'void' ? utils.dgToTsTypeMap['dataframe'] : outputType);
53
+ wrappers.push(tb.build(1));
48
54
  }
49
55
  }
50
- const srcDir = _path.default.join(curDir, 'src');
51
- const queryFileName = 'queries-api.ts';
52
- const queryFilePath = _path.default.join(_fs.default.existsSync(srcDir) ? srcDir : curDir, queryFileName);
53
- if (_fs.default.existsSync(queryFilePath)) {
54
- color.warn(`The file ${queryFilePath} already exists`);
55
- console.log('Rewriting its contents...');
56
- }
57
- const sep = '\n';
58
- _fs.default.writeFileSync(queryFilePath, utils.dgImports + sep + wrappers.join(sep.repeat(2)) + sep, 'utf8');
59
- color.success(`Successfully generated file ${queryFileName}${sep}`);
56
+ saveWrappersToFile('queries', wrappers);
60
57
  }
61
58
  function generateScriptWrappers() {
62
- const curDir = process.cwd();
63
59
  const scriptsDir = _path.default.join(curDir, 'scripts');
64
60
  if (!_fs.default.existsSync(scriptsDir)) {
65
61
  color.warn(`Directory ${scriptsDir} not found`);
66
62
  console.log('Skipping API generation for scripts...');
67
63
  return;
68
64
  }
69
- const packagePath = _path.default.join(curDir, 'package.json');
70
- const _package = JSON.parse(_fs.default.readFileSync(packagePath, {
71
- encoding: 'utf-8'
72
- }));
73
65
  const files = _ignoreWalk.default.sync({
74
- path: './scripts',
66
+ path: scriptsDir,
75
67
  ignoreFiles: ['.npmignore', '.gitignore']
76
68
  });
77
69
  const wrappers = [];
@@ -83,23 +75,62 @@ function generateScriptWrappers() {
83
75
  if (!script) continue;
84
76
  const name = utils.getScriptName(script, utils.commentMap[extension]);
85
77
  if (!name) continue;
78
+ const description = utils.getScriptDescription(script);
79
+ checkNameColision(name);
86
80
  const tb = new utils.TemplateBuilder(utils.scriptWrapperTemplate).replace('FUNC_NAME', name).replace('FUNC_NAME_LOWERCASE', name).replace('PACKAGE_NAMESPACE', _package.name);
87
81
  const inputs = utils.getScriptInputs(script);
88
82
  const outputType = utils.getScriptOutputType(script);
89
- tb.replace('PARAMS_OBJECT', inputs).replace('TYPED_PARAMS', inputs).replace('OUTPUT_TYPE', outputType);
83
+ tb.replace('PARAMS_OBJECT', inputs).replace('TYPED_PARAMS', inputs).replace('FUNC_DESCRIPTION', description).replace('OUTPUT_TYPE', outputType);
90
84
  wrappers.push(tb.build(1));
91
85
  }
92
- const srcDir = _path.default.join(curDir, 'src');
93
- const funcFileName = 'scripts-api.ts';
94
- const funcFilePath = _path.default.join(_fs.default.existsSync(srcDir) ? srcDir : curDir, funcFileName);
86
+ saveWrappersToFile('scripts', wrappers);
87
+ }
88
+ function generateFunctionWrappers() {
89
+ let filesToParse = packageFuncDirs.map(e => _path.default.join(curDir, 'src', e)).filter(e => _fs.default.existsSync(e));
90
+ const annotaionRegex = /(?:\/\/[^\n]*\n)+export[^{]*/g;
91
+ const nameRegex = /\s*export(?:\sasync)?\s*function\s*([^\s(]*)/;
92
+ const wrappers = [];
93
+ for (const file of filesToParse) {
94
+ const fileData = _fs.default.readFileSync(file, 'utf8');
95
+ const annotations = fileData.matchAll(annotaionRegex);
96
+ if (annotations === null) return;
97
+ for (let annotation of annotations) {
98
+ const name = (annotation[0].match(nameRegex) ?? [undefined, undefined])[1];
99
+ const description = utils.getScriptDescription(annotation[0], utils.commentMap[utils.jsExtention]);
100
+ if (!name) continue;
101
+ const annotationInputs = utils.getScriptInputs(annotation[0], utils.commentMap[utils.jsExtention]);
102
+ const annotationOutputDir = utils.getScriptOutputType(annotation[0], utils.commentMap[utils.jsExtention]);
103
+ let outputType = '';
104
+ for (let outputAnnotation of annotationOutputDir) {
105
+ if (outputType != '') {
106
+ outputType = 'any';
107
+ break;
108
+ }
109
+ outputType = utils.dgToTsTypeMap[outputAnnotation[1]] ?? 'any';
110
+ }
111
+ checkNameColision(name);
112
+ const tb = new utils.TemplateBuilder(utils.scriptWrapperTemplate).replace('FUNC_NAME', name).replace('FUNC_NAME_LOWERCASE', name).replace('PACKAGE_NAMESPACE', _package.name).replace('PARAMS_OBJECT', annotationInputs).replace('FUNC_DESCRIPTION', description).replace('TYPED_PARAMS', annotationInputs).replace('OUTPUT_TYPE', outputType);
113
+ wrappers.push(tb.build(1));
114
+ }
115
+ }
116
+ saveWrappersToFile('funcs', wrappers);
117
+ }
118
+ function saveWrappersToFile(namespaceName, wrappers) {
119
+ if (!_fs.default.existsSync(funcFilePath)) createApiFile();
120
+ const scriptApi = new utils.TemplateBuilder(utils.namespaceTemplate).replace('PACKAGE_NAMESPACE', namespaceName).replace('NAME', wrappers.join(sep.repeat(2)));
121
+ _fs.default.appendFileSync(funcFilePath, sep + scriptApi.build() + sep);
122
+ color.success(`Successfully generated file ${apiFile}${sep}`);
123
+ }
124
+ function createApiFile() {
95
125
  if (_fs.default.existsSync(funcFilePath)) {
96
126
  color.warn(`The file ${funcFilePath} already exists`);
97
127
  console.log('Rewriting its contents...');
98
128
  }
99
- const sep = '\n';
100
- const scriptApi = new utils.TemplateBuilder(utils.namespaceTemplate).replace('PACKAGE_NAMESPACE', _package.name).replace('NAME', wrappers.join(sep.repeat(2)));
101
- _fs.default.writeFileSync(funcFilePath, utils.dgImports + sep + scriptApi.build() + sep, 'utf8');
102
- color.success(`Successfully generated file ${funcFileName}${sep}`);
129
+ _fs.default.writeFileSync(funcFilePath, utils.dgImports + sep, 'utf8');
130
+ }
131
+ function checkNameColision(name) {
132
+ if (names.has(name)) console.log('There is collision in name ' + name);
133
+ names.add(name);
103
134
  }
104
135
  function api(args) {
105
136
  const nOptions = Object.keys(args).length - 1;
@@ -108,7 +139,9 @@ function api(args) {
108
139
  color.error('File `package.json` not found. Run the command from the package directory');
109
140
  return false;
110
141
  }
142
+ createApiFile();
111
143
  generateScriptWrappers();
112
144
  generateQueryWrappers();
145
+ generateFunctionWrappers();
113
146
  return true;
114
147
  }
@@ -21,6 +21,7 @@ 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
25
  function check(args) {
25
26
  const nOptions = Object.keys(args).length - 1;
26
27
  if (args['_'].length !== 1 || nOptions > 2 || nOptions > 0 && !args.r && !args.recursive) return false;
@@ -314,6 +315,8 @@ function checkFuncSignatures(packagePath, files) {
314
315
  warnings.push(`File ${file}, function ${f.name}:\n${vr.message}`);
315
316
  }
316
317
  }
318
+ let wrongInputNames = f.inputs.filter(e => forbidenNames.includes(e?.name ?? ''));
319
+ if (wrongInputNames.length > 0) warnings.push(`File ${file}, function ${f.name}: Wrong input names: (${wrongInputNames.map(e => e.name).join(', ')})`);
317
320
  if (f.isInvalidateOnWithoutCache) warnings.push(`File ${file}, function ${f.name}: Can't use invalidateOn without cache, please follow this example: 'meta.cache.invalidateOn'`);
318
321
  if (f.cache) if (!utils.cahceValues.includes(f.cache)) warnings.push(`File ${file}, function ${f.name}: unsupposed variable for cache : ${f.cache}`);
319
322
  if (f.invalidateOn) if (!utils.isValidCron(f.invalidateOn)) warnings.push(`File ${file}, function ${f.name}: unsupposed variable for invalidateOn : ${f.invalidateOn}`);
@@ -357,8 +360,11 @@ function checkPackageFile(packagePath, json, options) {
357
360
  if (api) {
358
361
  if (api === '../../js-api') {} else if (api === 'latest') warnings.push('File "package.json": you should specify Datagrok API version constraint (for example ^1.16.0, >=1.16.0).');else if (options?.isReleaseCandidateVersion === false && !/^(\^|>|<|~).+/.test(api)) warnings.push('File "package.json": Datagrok API version should starts with > | >= | ~ | ^ | < | <=');
359
362
  }
360
- const dt = json.devDependencies?.['datagrok-tools'] ?? json.dependencies?.['datagrok-tools'];
361
- if (dt && dt !== 'latest') warnings.push('File "package.json": "datagrok-tools" dependency must be "latest" version.');
363
+
364
+ // const dt = json.devDependencies?.['datagrok-tools'] ?? json.dependencies?.['datagrok-tools'];
365
+ // if (dt && dt !== 'latest')
366
+ // warnings.push('File "package.json": "datagrok-tools" dependency must be "latest" version.');
367
+
362
368
  if (Array.isArray(json.sources) && json.sources.length > 0) {
363
369
  for (const source of json.sources) {
364
370
  if (typeof source !== 'string') warnings.push(`File "package.json": Only file paths and URLs are allowed in sources. Modify the source ${source}`);
@@ -394,8 +400,6 @@ function checkPackageFile(packagePath, json, options) {
394
400
  }
395
401
  if (!hasRCDependency) {
396
402
  for (let dependency of Object.keys(json.dependencies ?? {})) {
397
- console.log(dependency);
398
- console.log((json.dependencies ?? {})[dependency]);
399
403
  if (/\d+.\d+.\d+-rc(.[A-Za-z0-9]*.[A-Za-z0-9]*)?/.test((json.devDependencies ?? {})[dependency])) {
400
404
  hasRCDependency = true;
401
405
  break;
@@ -191,12 +191,12 @@ https://datagrok.ai/help/develop/how-to/test-packages#local-testing
191
191
  const HELP_LINK = `
192
192
  Usage: grok link
193
193
 
194
- Link \`datagrok-api\` and libraries for local development
194
+ Link \`datagrok-api\`, all necessary libraries and packages for local development by \`npm link\` command
195
195
 
196
196
  Options:
197
197
  --dev Links also dev dependencies
198
198
  --path Instead of npm linking sets dependecies in package.json to local
199
- --unlink Unlinks packages and sets last versions instead of local path in package.json dependecies
199
+ --unlink Unlinks packages and sets last versions instead of local path in package.json dependencies
200
200
  --verbose Prints detailed information about linked packages
201
201
  `;
202
202
 
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.FUNC_TYPES = void 0;
6
+ exports.dgAnnotationTypes = exports.FUNC_TYPES = void 0;
7
7
  exports.generateClassFunc = generateClassFunc;
8
8
  exports.generateExport = generateExport;
9
9
  exports.generateFunc = generateFunc;
@@ -22,6 +22,53 @@ let pseudoParams = exports.pseudoParams = /*#__PURE__*/function (pseudoParams) {
22
22
  }({});
23
23
  const nonMetaData = ['sidebar', 'editor'];
24
24
  const decoratorOptionToAnnotation = new Map([['initialValue', 'default']]);
25
+ const dgAnnotationTypes = exports.dgAnnotationTypes = {
26
+ INT: "int",
27
+ BIG_INT: "bigint",
28
+ FLOAT: "double",
29
+ NUM: "num",
30
+ QNUM: "qnum",
31
+ BOOL: "bool",
32
+ STRING: "string",
33
+ STRING_LIST: "string_list",
34
+ DATE_TIME: "datetime",
35
+ OBJECT: "object",
36
+ BYTE_ARRAY: "byte_array",
37
+ DATA_FRAME: "dataframe",
38
+ DATA_FRAME_LIST: "dataframe_list",
39
+ CELL: "cell",
40
+ COLUMN: "column",
41
+ COLUMN_LIST: "column_list",
42
+ GRAPHICS: "graphics",
43
+ FILE: "file",
44
+ BLOB: "blob",
45
+ ROW_FILTER: "tablerowfiltercall",
46
+ COLUMN_FILTER: "colfiltercall",
47
+ BIT_SET: "bitset",
48
+ MAP: "map",
49
+ DYNAMIC: "dynamic",
50
+ VIEWER: "viewer",
51
+ LIST: "list",
52
+ SEM_VALUE: "semantic_value",
53
+ FUNC: "func",
54
+ FUNC_CALL: "funccall",
55
+ PROPERTY: "property",
56
+ CATEGORICAL: "categorical",
57
+ NUMERICAL: "numerical",
58
+ GRID_CELL_RENDER_ARGS: "GridCellRenderArgs",
59
+ ELEMENT: "element",
60
+ VIEW: "view",
61
+ TABLE_VIEW: "TableView",
62
+ USER: "User",
63
+ MENU: "Menu",
64
+ PROJECT: "Project",
65
+ SEMANTIC_VALUE: "semantic_value",
66
+ EVENT_DATA: "event_data",
67
+ PROGRESS_INDICATOR: "progressindicator",
68
+ CREDENTIALS: "Credentials",
69
+ SCRIPT_ENVIRONMENT: "ScriptEnvironment",
70
+ NOTEBOOK: "Notebook"
71
+ };
25
72
  let FUNC_TYPES = exports.FUNC_TYPES = /*#__PURE__*/function (FUNC_TYPES) {
26
73
  FUNC_TYPES["APP"] = "app";
27
74
  FUNC_TYPES["CELL_RENDERER"] = "cellRenderer";
@@ -379,7 +426,7 @@ const primitives = new Set(['string', 'string[]', 'number', 'number[]', 'boolean
379
426
 
380
427
  /** Generates a DG function. */
381
428
  function generateFunc(annotation, funcName, sep = '\n', className = '', inputs = [], isAsync = false) {
382
- let funcSigNature = inputs.map(e => `${e.name}: ${primitives.has(e.type ?? '') ? e.type : 'any'}`).join(', ');
429
+ let funcSigNature = inputs.map(e => `${e.name}: ${primitives.has(e.type ?? '') ? e.type : typesToAnnotation[e.type?.replace('[]', '') ?? ''] ? e.type : 'any'}`).join(', ');
383
430
  let funcArguments = inputs.map(e => e.name).join(', ');
384
431
  return annotation + `export ${isAsync ? 'async ' : ''}function ${funcName}(${funcSigNature}) {${sep} return ${className.length > 0 ? `${className}.` : ''}${funcName}(${funcArguments});${sep}}${sep.repeat(2)}`;
385
432
  }
@@ -9,9 +9,11 @@ exports.camelCaseToKebab = camelCaseToKebab;
9
9
  exports.checkScriptLocation = checkScriptLocation;
10
10
  exports.commentMap = void 0;
11
11
  exports.delay = delay;
12
+ exports.descriptionToComment = descriptionToComment;
12
13
  exports.fileParamRegex = exports.dgToTsTypeMap = exports.dgImports = void 0;
13
14
  exports.friendlyNameToName = friendlyNameToName;
14
15
  exports.getParam = getParam;
16
+ exports.getScriptDescription = getScriptDescription;
15
17
  exports.getScriptInputs = getScriptInputs;
16
18
  exports.getScriptName = getScriptName;
17
19
  exports.getScriptOutputType = getScriptOutputType;
@@ -19,6 +21,7 @@ exports.headerTags = void 0;
19
21
  exports.isEmpty = isEmpty;
20
22
  exports.isPackageDir = isPackageDir;
21
23
  exports.isValidCron = isValidCron;
24
+ exports.jsExtention = void 0;
22
25
  exports.kebabToCamelCase = kebabToCamelCase;
23
26
  exports.mapURL = mapURL;
24
27
  exports.queryWrapperTemplate = exports.queryExtension = exports.propertyTypes = exports.namespaceTemplate = exports.nameRegex = exports.nameAnnRegex = void 0;
@@ -49,6 +52,10 @@ function kebabToCamelCase(s, firstUpper = true) {
49
52
  s = s.replace(/-./g, x => x.toUpperCase()[1]);
50
53
  return (firstUpper ? s[0].toUpperCase() : s[0].toLowerCase()) + s.slice(1);
51
54
  }
55
+ function descriptionToComment(s) {
56
+ if (s.length === 0) return '';
57
+ return '//' + s + '\n';
58
+ }
52
59
  function spaceToCamelCase(s, firstUpper = true) {
53
60
  s = s.replace(/\s+./g, x => x[x.length - 1].toUpperCase());
54
61
  return (firstUpper ? s[0].toUpperCase() : s[0].toLowerCase()) + s.slice(1);
@@ -97,6 +104,7 @@ const replacers = exports.replacers = {
97
104
  NAME_PREFIX: (s, name) => s.replace(/#{NAME_PREFIX}/g, name.slice(0, 3)),
98
105
  PACKAGE_DETECTORS_NAME: (s, name) => s.replace(/#{PACKAGE_DETECTORS_NAME}/g, kebabToCamelCase(name)),
99
106
  PACKAGE_NAMESPACE: (s, name) => s.replace(/#{PACKAGE_NAMESPACE}/g, kebabToCamelCase(removeScope(name))),
107
+ FUNC_DESCRIPTION: (s, desc) => s.replace(/#{FUNC_DESCRIPTION}/g, descriptionToComment(desc)),
100
108
  FUNC_NAME: (s, name) => s.replace(/#{FUNC_NAME}/g, friendlyNameToName(name)),
101
109
  FUNC_NAME_LOWERCASE: (s, name) => s.replace(/#{FUNC_NAME_LOWERCASE}/g, friendlyNameToName(name, false)),
102
110
  PARAMS_OBJECT: (s, params) => s.replace(/#{PARAMS_OBJECT}/g, params.length ? `{ ${params.map(p => p.name).join(', ')} }` : `{}`),
@@ -138,6 +146,7 @@ const commentMap = exports.commentMap = {
138
146
  '.sql': '--'
139
147
  };
140
148
  const queryExtension = exports.queryExtension = '.sql';
149
+ const jsExtention = exports.jsExtention = '.js';
141
150
  const scriptExtensions = exports.scriptExtensions = ['.jl', '.m', '.py', '.R'];
142
151
  function checkScriptLocation(filepath) {
143
152
  if (!(filepath.startsWith('scripts/') || filepath.startsWith('projects/') || filepath.startsWith('dockerfiles/')) && scriptExtensions.some(ext => filepath.endsWith(ext))) return false;
@@ -170,7 +179,8 @@ const dgToTsTypeMap = exports.dgToTsTypeMap = {
170
179
  dataframe: 'DG.DataFrame',
171
180
  column: 'DG.Column',
172
181
  column_list: 'string[]',
173
- file: 'DG.FileInfo'
182
+ file: 'DG.FileInfo',
183
+ view: 'DG.View'
174
184
  };
175
185
  const propertyTypes = exports.propertyTypes = ['bool', 'int', 'double', 'string', 'datetime', 'object', 'column', 'dataframe', 'bitset', 'cell', 'string_list', 'map'];
176
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'];
@@ -204,11 +214,18 @@ function getScriptInputs(script, comment = '#') {
204
214
  return inputs;
205
215
  }
206
216
  ;
217
+ function getScriptDescription(script, comment = '#') {
218
+ const regex = new RegExp(`${comment}\\s*description:\\s([^\n]*)`);
219
+ const rexegRes = script.match(regex) || [];
220
+ const desc = rexegRes[1] || '';
221
+ return desc;
222
+ }
223
+ ;
207
224
  const dgImports = exports.dgImports = `import * as grok from 'datagrok-api/grok';\nimport * as DG from 'datagrok-api/dg';\n\n`;
208
- const scriptWrapperTemplate = exports.scriptWrapperTemplate = `export async function #{FUNC_NAME_LOWERCASE}(#{TYPED_PARAMS}): Promise<#{OUTPUT_TYPE}> {
225
+ const scriptWrapperTemplate = exports.scriptWrapperTemplate = `#{FUNC_DESCRIPTION}export async function #{FUNC_NAME_LOWERCASE}(#{TYPED_PARAMS}): Promise<#{OUTPUT_TYPE}> {
209
226
  return await grok.functions.call('#{PACKAGE_NAMESPACE}:#{FUNC_NAME}', #{PARAMS_OBJECT});
210
227
  }`;
211
- const queryWrapperTemplate = exports.queryWrapperTemplate = `export async function #{FUNC_NAME_LOWERCASE}(#{TYPED_PARAMS}): Promise<#{OUTPUT_TYPE}> {
228
+ const queryWrapperTemplate = exports.queryWrapperTemplate = `#{FUNC_DESCRIPTION}export async function #{FUNC_NAME_LOWERCASE}(#{TYPED_PARAMS}): Promise<#{OUTPUT_TYPE}> {
212
229
  return await grok.data.query('#{PACKAGE_NAMESPACE}:#{FUNC_NAME}', #{PARAMS_OBJECT});
213
230
  }`;
214
231
  const namespaceTemplate = exports.namespaceTemplate = `export namespace #{PACKAGE_NAMESPACE} {\n#{NAME}\n}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "datagrok-tools",
3
- "version": "4.14.2",
3
+ "version": "4.14.4",
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": {
@@ -2,17 +2,18 @@ const fs = require("fs");
2
2
  const path = require("path");
3
3
 
4
4
  const tsParser = require("@typescript-eslint/typescript-estree");
5
- const generate = require('@babel/generator').default;
5
+ const generate = require("@babel/generator").default;
6
6
 
7
7
  const {
8
8
  reservedDecorators,
9
9
  getFuncAnnotation,
10
10
  generateImport,
11
11
  generateExport,
12
- typesToAnnotation
12
+ typesToAnnotation,
13
+ dgAnnotationTypes,
13
14
  } = require("../bin/utils/func-generation");
14
15
 
15
- const baseImport = '\n';
16
+ const baseImport = "import * as DG from 'datagrok-api/dg';\n";
16
17
 
17
18
  class FuncGeneratorPlugin {
18
19
  constructor(options = { outputPath: "./src/package.g.ts" }) {
@@ -33,9 +34,8 @@ class FuncGeneratorPlugin {
33
34
 
34
35
  for (const file of tsFiles) {
35
36
  const content = fs.readFileSync(file, "utf-8");
36
- if(!content.includes('@grok.decorators.'))
37
- continue;
38
-
37
+ if (!content.includes("@grok.decorators.")) continue;
38
+
39
39
  if (!content) continue;
40
40
  const ast = tsParser.parse(content, {
41
41
  sourceType: "module",
@@ -51,73 +51,134 @@ class FuncGeneratorPlugin {
51
51
  const decorators = node.decorators;
52
52
  if (!decorators || decorators.length === 0) return;
53
53
 
54
- if (node?.type === "ClassDeclaration")
55
- this._addNodeData(node, file, srcDirPath, functions, imports, genImports, genExports);
54
+ if (node?.type === "ClassDeclaration")
55
+ this._addNodeData(
56
+ node,
57
+ file,
58
+ srcDirPath,
59
+ functions,
60
+ imports,
61
+ genImports,
62
+ genExports
63
+ );
56
64
 
57
65
  if (node?.type === "MethodDefinition")
58
- this._addNodeData(node, file, srcDirPath, functions, imports, genImports, genExports, parentClass);
66
+ this._addNodeData(
67
+ node,
68
+ file,
69
+ srcDirPath,
70
+ functions,
71
+ imports,
72
+ genImports,
73
+ genExports,
74
+ parentClass
75
+ );
59
76
  });
60
77
  this._insertImports([...imports]);
61
78
  fs.appendFileSync(this.options.outputPath, functions.join(""), "utf-8");
62
79
  }
63
80
  this._checkPackageFileForDecoratorsExport(packageFilePath);
64
- // Uncommment to add obvious import/export
81
+ // Uncommment to add obvious import/export
65
82
  // this._writeToPackageFile(packageFilePath, genImports, genExports);
66
-
67
83
  });
68
84
  }
69
85
 
70
- _addNodeData(node, file, srcDirPath, functions, imports, genImports, genExports, parent = undefined) {
71
- if (!node.decorators || !node.decorators.length || node.decorators?.length === 0)
86
+ _addNodeData(
87
+ node,
88
+ file,
89
+ srcDirPath,
90
+ functions,
91
+ imports,
92
+ genImports,
93
+ genExports,
94
+ parent = undefined
95
+ ) {
96
+ if (
97
+ !node.decorators ||
98
+ !node.decorators.length ||
99
+ node.decorators?.length === 0
100
+ )
72
101
  return;
73
102
 
74
103
  function modifyImportPath(dirPath, filePath) {
75
104
  const relativePath = path.relative(dirPath, filePath);
76
- return `./${relativePath.slice(0, relativePath.length - 3).replace(/\\/g, '/')}`;
105
+ return `./${relativePath
106
+ .slice(0, relativePath.length - 3)
107
+ .replace(/\\/g, "/")}`;
77
108
  }
78
109
 
79
- const decorator = node.decorators[0];
80
- const exp = decorator.expression;
110
+ const decorator = node.decorators[0];
111
+ const exp = decorator.expression;
81
112
  const name = exp.callee?.property?.name || exp.callee?.name;
82
113
  const identifierName = node.id?.name || node.key?.name;
83
114
  const className = parent?.id?.name || parent?.key?.name;
84
115
 
85
116
  if (!name) return;
86
117
 
87
- const decoratorOptions = this._readDecoratorOptions(exp.arguments[0].properties);
88
- decoratorOptions.set('tags', [...(reservedDecorators[name]['metadata']['tags'] ?? [] ), ...(decoratorOptions.get('tags') ?? [])])
89
- const functionParams = node?.type === 'MethodDefinition' ? this._readMethodParamas(node) : [];
90
- const annotationByReturnType = node?.type === 'MethodDefinition' ? this._readFunctionReturnTypeInfo(node) : '';
91
- const annotationByReturnTypeObj = { name: 'result', type: annotationByReturnType };
118
+ const decoratorOptions = this._readDecoratorOptions(
119
+ exp.arguments[0].properties
120
+ );
121
+
122
+ decoratorOptions.set("tags", [
123
+ ...(reservedDecorators[name]["metadata"]["tags"] ?? []),
124
+ ...(decoratorOptions.get("tags") ?? []),
125
+ ]);
126
+ const functionParams =
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
+ };
92
134
  const isMethodAsync = this._isMethodAsync(node);
93
- let importString = generateImport(node?.type === 'MethodDefinition' ? className : identifierName, modifyImportPath(path.dirname(this.options.outputPath), file));
135
+ let importString = generateImport(
136
+ node?.type === "MethodDefinition" ? className : identifierName,
137
+ modifyImportPath(path.dirname(this.options.outputPath), file)
138
+ );
94
139
  imports.add(importString);
95
- const funcName = `${node?.type === 'MethodDefinition' ? '' : '_'}${identifierName}`;
140
+ const funcName = `${
141
+ node?.type === "MethodDefinition" ? "" : "_"
142
+ }${identifierName}`;
96
143
  const funcAnnotaionOptions = {
97
- ...reservedDecorators[name]['metadata'],
98
- ...(annotationByReturnType ? {outputs: [annotationByReturnTypeObj ?? {}]} : {}),
144
+ ...reservedDecorators[name]["metadata"],
145
+ ...(annotationByReturnType
146
+ ? { outputs: [annotationByReturnTypeObj ?? {}] }
147
+ : {}),
99
148
  ...Object.fromEntries(decoratorOptions),
100
- ...{inputs: functionParams},
101
- ...{isAsync: isMethodAsync}
102
- };
103
- if (!funcAnnotaionOptions.name)
104
- funcAnnotaionOptions.name = identifierName;
105
- functions.push(reservedDecorators[name]['genFunc'](getFuncAnnotation(funcAnnotaionOptions), identifierName,'\n', (className ?? ''), functionParams, funcAnnotaionOptions.isAsync ?? false));
106
- genImports.push(generateImport(funcName, modifyImportPath(srcDirPath, this.options.outputPath)));
149
+ ...{ inputs: functionParams },
150
+ ...{ isAsync: isMethodAsync },
151
+ };
152
+ if (!funcAnnotaionOptions.name) funcAnnotaionOptions.name = identifierName;
153
+ functions.push(
154
+ reservedDecorators[name]["genFunc"](
155
+ getFuncAnnotation(funcAnnotaionOptions),
156
+ identifierName,
157
+ "\n",
158
+ className ?? "",
159
+ functionParams,
160
+ funcAnnotaionOptions.isAsync ?? false
161
+ )
162
+ );
163
+ genImports.push(
164
+ generateImport(
165
+ funcName,
166
+ modifyImportPath(srcDirPath, this.options.outputPath)
167
+ )
168
+ );
107
169
  genExports.push(generateExport(funcName));
108
170
  }
109
171
 
110
- _isMethodAsync(node){
172
+ _isMethodAsync(node) {
111
173
  let result = false;
112
- if (node.type === 'MethodDefinition')
113
- result = node.value.async;
174
+ if (node.type === "MethodDefinition") result = node.value.async;
114
175
  return result;
115
176
  }
116
177
 
117
- _readImports (content) {
118
- const importRegex = /(import(?:[\s\w{},*]+from\s*)?['"]([^'"]+)['"];)/g;
178
+ _readImports(content) {
179
+ const importRegex = /(import(?:[\s\w{},*]+from\s*)?['']([^'']+)[''];)/g;
119
180
  const results = [];
120
-
181
+
121
182
  let match;
122
183
  while ((match = importRegex.exec(content)) !== null) {
123
184
  results.push(`${match[1]}\n`);
@@ -125,68 +186,79 @@ class FuncGeneratorPlugin {
125
186
  return results;
126
187
  }
127
188
 
128
- _readDecoratorOptions(properties){
189
+ _readDecoratorOptions(properties) {
129
190
  const resultMap = new Map();
130
191
 
131
- for(let prop of properties)
192
+ for (let prop of properties)
132
193
  resultMap.set(prop.key.name, this._evalLiteral(prop.value));
133
-
134
194
  return resultMap;
135
195
  }
136
196
 
137
197
  _evalLiteral(node) {
138
198
  if (!node) return null;
139
-
140
199
  switch (node.type) {
141
- case 'Literal':
200
+ case "Literal":
142
201
  return node.value;
143
202
 
144
- case 'ArrayExpression':
145
- return node.elements.map(el => this._evalLiteral(el));
203
+ case "ArrayExpression":
204
+ return node.elements.map((el) => this._evalLiteral(el));
205
+
206
+ case "MemberExpression":
207
+ return dgAnnotationTypes[node?.property?.name ?? ""] ?? "dynamic";
146
208
 
147
- case 'ObjectExpression':
209
+ case "ObjectExpression":
148
210
  return Object.fromEntries(
149
- node.properties.map(p => {
211
+ node.properties.map((p) => {
150
212
  return [p.key.name || p.key.value, this._evalLiteral(p.value)];
151
213
  })
152
214
  );
153
215
 
154
216
  default:
155
- return '';
217
+ return "";
156
218
  }
157
219
  }
158
220
 
159
221
  _readMethodParamas(node) {
160
- const params = node?.value?.params?.map(param => {
161
- let baseParam = param.type === 'TSNonNullExpression' ? param.expression : param;
162
- const options = param.decorators?.length > 0? Object.fromEntries(this._readDecoratorOptions(param.decorators[0]?.expression?.arguments[0].properties)) : undefined;
163
-
164
- // Commented code finds value by default of function's variable
165
- // let defaultValue = undefined;
166
- if(baseParam.type === 'AssignmentPattern')
167
- {
222
+ const params = node?.value?.params?.map((param) => {
223
+ let baseParam =
224
+ param.type === "TSNonNullExpression" ? param.expression : param;
225
+ const options =
226
+ param.decorators?.length > 0
227
+ ? Object.fromEntries(
228
+ this._readDecoratorOptions(
229
+ param.decorators[0]?.expression?.arguments[0].properties
230
+ )
231
+ )
232
+ : undefined;
233
+
234
+ // Commented code finds value by default of function's variable
235
+ // let defaultValue = undefined;
236
+ if (baseParam.type === "AssignmentPattern") {
168
237
  // if (baseParam?.right?.type === 'Literal')
169
238
  // defaultValue = baseParam?.right?.raw;
170
239
  baseParam = baseParam?.left;
171
- }
240
+ }
172
241
 
173
- if (baseParam.type === 'RestElement' || baseParam.type === 'Identifier') {
242
+ if (baseParam.type === "RestElement" || baseParam.type === "Identifier") {
174
243
  let name =
175
- baseParam.type === 'RestElement'
244
+ baseParam.type === "RestElement"
176
245
  ? `...${baseParam.argument.name}`
177
246
  : baseParam.name;
178
247
  if (baseParam?.argument?.typeAnnotation)
179
- name += ': ' + generate(baseParam.argument.typeAnnotation.typeAnnotation).code;
180
-
181
- let type = '';
248
+ name +=
249
+ ": " +
250
+ generate(baseParam.argument.typeAnnotation.typeAnnotation).code;
251
+
252
+ let type = "";
182
253
  if (baseParam?.typeAnnotation?.typeAnnotation)
183
254
  type = generate(baseParam.typeAnnotation.typeAnnotation).code;
184
- else
185
- type = 'any';
186
-
187
- let params = baseParam.typeAnnotation.typeAnnotation.typeArguments?.params;
188
- if(type !== 'any' && params && params.length > 0)
189
- type += `<${params.map((e)=>e.typeName.name).join(',')}>`;
255
+ else type = "any";
256
+
257
+ let params =
258
+ baseParam.typeAnnotation.typeAnnotation.typeArguments?.params;
259
+ if (type !== "any" && params && params.length > 0)
260
+ type += `<${params.map((e) => e.typeName.name).join(",")}>`;
261
+
190
262
  return { name: name, type: type, options: options };
191
263
  }
192
264
  // Commented code belove sets more strong types for ObjectPatterns and ArrayPatterns
@@ -213,7 +285,7 @@ class FuncGeneratorPlugin {
213
285
  // });
214
286
  // name = `[${elements.join(', ')}]`;
215
287
  // }
216
-
288
+
217
289
  // let type = '';
218
290
  // if (baseParam.typeAnnotation)
219
291
  // type = generate(baseParam.typeAnnotation.typeAnnotation).code;
@@ -222,7 +294,7 @@ class FuncGeneratorPlugin {
222
294
 
223
295
  // return { name: name, type: type, options: options };
224
296
  // }
225
- return { name: 'value', type: 'any', options: undefined };
297
+ return { name: "value", type: "any", options: undefined };
226
298
  });
227
299
  return params;
228
300
  }
@@ -248,79 +320,81 @@ class FuncGeneratorPlugin {
248
320
 
249
321
  _walk(node, visitor, parent = null) {
250
322
  if (!node || typeof node !== "object") return;
251
-
323
+
252
324
  visitor(node, parent);
253
-
325
+
254
326
  for (const key in node) {
255
327
  const value = node[key];
256
328
  if (Array.isArray(value)) {
257
329
  value.forEach((child) => {
258
330
  if (child && typeof child.type === "string") {
259
- this._walk(child, visitor, node.type === 'ClassDeclaration'? node : parent );
331
+ this._walk(
332
+ child,
333
+ visitor,
334
+ node.type === "ClassDeclaration" ? node : parent
335
+ );
260
336
  }
261
337
  });
262
338
  } else if (value && typeof value.type === "string") {
263
- this._walk(value, visitor, node.type === 'ClassDeclaration'? node : parent );
339
+ this._walk(
340
+ value,
341
+ visitor,
342
+ node.type === "ClassDeclaration" ? node : parent
343
+ );
264
344
  }
265
345
  }
266
346
  }
267
347
 
268
- _readFunctionReturnTypeInfo(node) {
269
- let resultType = 'any';
348
+ _readReturnType(node) {
349
+ let resultType = "dynamic";
270
350
  let isArray = false;
271
- const annotation = node.value?.returnType?.typeAnnotation;
272
- if (annotation) {
273
- if (annotation?.typeName?.name === 'Promise')
274
- {
275
- const argumnets = annotation.typeArguments?.params;
276
- if (argumnets && argumnets.length===1)
277
- {
278
- if (argumnets[0].typeName)
279
- resultType = argumnets[0].typeName?.right?.name ?? argumnets[0].typeName?.name;
280
- else if (argumnets[0].type !== 'TSArrayType')
281
- resultType = this._getTypeNameFromNode(argumnets[0]);
282
- else if (argumnets[0].elementType.type !== 'TSTypeReference'){
283
- isArray = true;
284
- resultType = this._getTypeNameFromNode(argumnets[0]?.elementType);
285
- }
286
- else{
287
- isArray = true;
288
- resultType = argumnets[0].elementType?.typeName?.name || argumnets[0].elementType?.typeName?.right?.name;
289
- }
290
- }
291
- }
292
- else{
293
- if (annotation.type === 'TSTypeReference')
294
- resultType = annotation.typeName?.right?.name ?? annotation.typeName?.name;
295
- else if (annotation.type !== 'TSArrayType')
296
- resultType = this._getTypeNameFromNode(annotation);
297
- else if (annotation.elementType.type !== 'TSTypeReference'){
298
- isArray = true;
299
- resultType = this._getTypeNameFromNode(annotation?.elementType);
300
- }
301
- else{
302
- isArray = true;
303
- resultType = (annotation?.elementType?.typeName?.name || annotation?.elementType?.typeName?.right?.name);
304
- }
351
+ let annotation = node.value?.returnType?.typeAnnotation;
352
+ if (
353
+ annotation &&
354
+ annotation.type !== "TSUnionType" &&
355
+ annotation.type !== "TSIntersectionType"
356
+ ) {
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
+
364
+ if (annotation.typeName || annotation.type === "TSTypeReference")
365
+ resultType =
366
+ annotation.typeName?.right?.name ?? annotation.typeName?.name;
367
+ else if (annotation.type !== "TSArrayType"){
368
+ resultType = this._getTypeNameFromNode(annotation);
305
369
  }
306
- }
370
+ else if (annotation.elementType.type !== "TSTypeReference") {
371
+ isArray = true;
372
+ resultType = this._getTypeNameFromNode(annotation?.elementType);
373
+ } else {
374
+ isArray = true;
375
+ resultType =
376
+ annotation?.elementType?.typeName?.name ||
377
+ annotation?.elementType?.typeName?.right?.name;
378
+ }
379
+ }
380
+
307
381
  resultType = typesToAnnotation[resultType];
308
- if (isArray && resultType)
309
- resultType = `list<${resultType}>`
310
- return resultType;
382
+ if (isArray && resultType) resultType = `list`;
383
+ return resultType ?? 'dynamic';
311
384
  }
312
385
 
386
+
313
387
  _getTypeNameFromNode(typeNode) {
314
- if (typeNode.type === 'TSTypeReference') {
388
+ if (typeNode.type === "TSTypeReference") {
315
389
  return typeNode.typeName.name;
316
- } else if (typeNode.type === 'TSVoidKeyword') {
317
- return 'void';
318
- } else if (typeNode.type === 'TSNumberKeyword') {
319
- return 'number';
320
- } else if (typeNode.type === 'TSStringKeyword') {
321
- return 'string';
322
- } else if (typeNode.type === 'TSBooleanKeyword') {
323
- return 'boolean';
390
+ } else if (typeNode.type === "TSVoidKeyword") {
391
+ return "void";
392
+ } else if (typeNode.type === "TSNumberKeyword") {
393
+ return "number";
394
+ } else if (typeNode.type === "TSStringKeyword") {
395
+ return "string";
396
+ } else if (typeNode.type === "TSBooleanKeyword") {
397
+ return "boolean";
324
398
  } else {
325
399
  return typeNode.type;
326
400
  }
@@ -330,11 +404,13 @@ class FuncGeneratorPlugin {
330
404
  fs.writeFileSync(this.options.outputPath, baseImport);
331
405
  }
332
406
 
333
- _checkPackageFileForDecoratorsExport(packagePath){
407
+ _checkPackageFileForDecoratorsExport(packagePath) {
334
408
  const content = fs.readFileSync(packagePath, "utf-8");
335
409
  const decoratorsExportRegex = /export\s*\*\s*from\s*'\.\/package\.g';/;
336
410
  if (!decoratorsExportRegex.test(content))
337
- console.warn(`\nWARNING: Your package doesn't export package.g.ts file to package.ts \n please add "export * from './package.g';" to the package.ts file.\n`);
411
+ console.warn(
412
+ `\nWARNING: Your package doesn't export package.g.ts file to package.ts \n please add 'export * from './package.g';' to the package.ts file.\n`
413
+ );
338
414
  }
339
415
 
340
416
  _writeToPackageFile(filePath, imports, exp) {
@@ -352,14 +428,16 @@ class FuncGeneratorPlugin {
352
428
  _insertImports(importArray) {
353
429
  if (fs.existsSync(this.options.outputPath)) {
354
430
  const content = fs.readFileSync(this.options.outputPath, "utf-8");
355
- if (content)
356
- importArray.push(content);
431
+ if (content) importArray.push(content);
357
432
  const output = importArray.join("");
358
433
  fs.writeFileSync(this.options.outputPath, `${output}`, "utf-8");
359
- }
360
- else
361
- fs.writeFileSync(this.options.outputPath, `${baseImport}\n${importArray.join("")}`, "utf-8");
434
+ } else
435
+ fs.writeFileSync(
436
+ this.options.outputPath,
437
+ `${baseImport}\n${importArray.join("")}`,
438
+ "utf-8"
439
+ );
362
440
  }
363
441
  }
364
442
 
365
- module.exports = FuncGeneratorPlugin;
443
+ module.exports = FuncGeneratorPlugin;