datagrok-tools 4.14.69 → 4.14.71
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 +22 -0
- package/CLAUDE.md +208 -0
- package/bin/commands/add.js +6 -5
- package/bin/commands/check.js +278 -241
- package/bin/commands/help.js +12 -0
- package/bin/commands/migrate.js +74 -0
- package/bin/grok.js +2 -1
- package/bin/utils/func-generation.js +19 -19
- package/bin/utils/utils.js +9 -17
- package/entity-template/app.js +1 -1
- package/entity-template/init.js +1 -2
- package/entity-template/panel.ts +1 -1
- package/entity-template/sem-type-detector.js +1 -1
- package/entity-template/test.ts +2 -2
- package/entity-template/view.js +1 -1
- package/package.json +4 -3
- package/plugins/func-gen-plugin.js +16 -8
- package/script-template/javascript.js +0 -1
- package/script-template/julia.jl +0 -1
- package/script-template/node.js +0 -1
- package/script-template/octave.m +0 -1
- package/script-template/python.py +0 -1
- package/script-template/r.R +0 -1
package/bin/commands/check.js
CHANGED
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
});
|
|
7
7
|
exports.check = check;
|
|
8
8
|
exports.checkChangelog = checkChangelog;
|
|
9
|
+
exports.checkDatagrokApiImports = checkDatagrokApiImports;
|
|
9
10
|
exports.checkFuncSignatures = checkFuncSignatures;
|
|
10
11
|
exports.checkImportStatements = checkImportStatements;
|
|
11
12
|
exports.checkNpmIgnore = checkNpmIgnore;
|
|
@@ -18,6 +19,8 @@ var _ignoreWalk = _interopRequireDefault(require("ignore-walk"));
|
|
|
18
19
|
var utils = _interopRequireWildcard(require("../utils/utils"));
|
|
19
20
|
var color = _interopRequireWildcard(require("../utils/color-utils"));
|
|
20
21
|
var testUtils = _interopRequireWildcard(require("../utils/test-utils"));
|
|
22
|
+
var _const = require("datagrok-api/src/const");
|
|
23
|
+
var _child_process = require("child_process");
|
|
21
24
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
22
25
|
const warns = ['Latest package version', 'Datagrok API version should contain'];
|
|
23
26
|
const forbiddenNames = ['function', 'class', 'export'];
|
|
@@ -33,7 +36,7 @@ function check(args) {
|
|
|
33
36
|
return runChecks(curDir, args.soft ?? false);
|
|
34
37
|
}
|
|
35
38
|
}
|
|
36
|
-
function runChecks(packagePath, soft = false) {
|
|
39
|
+
function runChecks(packagePath, soft = false, noExit = false) {
|
|
37
40
|
if (packagePath.includes(`${_path.default.sep}node_modules${_path.default.sep}`)) return true;
|
|
38
41
|
const files = _ignoreWalk.default.sync({
|
|
39
42
|
path: packagePath,
|
|
@@ -50,8 +53,12 @@ function runChecks(packagePath, soft = false) {
|
|
|
50
53
|
}));
|
|
51
54
|
const webpackConfigPath = _path.default.join(packagePath, 'webpack.config.js');
|
|
52
55
|
const isWebpack = _fs.default.existsSync(webpackConfigPath);
|
|
56
|
+
const semver = json.version.split('-')[0];
|
|
57
|
+
const [major, minor, patch] = semver.split('.').map(Number);
|
|
58
|
+
let isPre1Version = false;
|
|
53
59
|
let isReleaseCandidateVersion = false;
|
|
54
60
|
let externals = null;
|
|
61
|
+
if (major === 0) isPre1Version = true;
|
|
55
62
|
if (/\d+.\d+.\d+-rc(.[A-Za-z0-9]*.[A-Za-z0-9]*)?/.test(json.version)) isReleaseCandidateVersion = true;
|
|
56
63
|
if (isWebpack) {
|
|
57
64
|
const content = _fs.default.readFileSync(webpackConfigPath, {
|
|
@@ -60,6 +67,7 @@ function runChecks(packagePath, soft = false) {
|
|
|
60
67
|
externals = extractExternals(content);
|
|
61
68
|
if (externals) errors.push(...checkImportStatements(packagePath, jsTsFiles, externals));
|
|
62
69
|
}
|
|
70
|
+
errors.push(...checkDatagrokApiImports(packagePath, jsTsFiles));
|
|
63
71
|
if (!soft) errors.push(...checkSourceMap(packagePath));
|
|
64
72
|
errors.push(...checkNpmIgnore(packagePath));
|
|
65
73
|
warnings.push(...checkScriptNames(packagePath));
|
|
@@ -71,7 +79,7 @@ function runChecks(packagePath, soft = false) {
|
|
|
71
79
|
externals,
|
|
72
80
|
isReleaseCandidateVersion
|
|
73
81
|
}));
|
|
74
|
-
if (!isReleaseCandidateVersion) warnings.push(...checkChangelog(packagePath, json));
|
|
82
|
+
if (!isReleaseCandidateVersion && !isPre1Version) warnings.push(...checkChangelog(packagePath, json));
|
|
75
83
|
if (warnings.length) {
|
|
76
84
|
console.log(`${_path.default.basename(packagePath)} warnings`);
|
|
77
85
|
warn(warnings);
|
|
@@ -80,23 +88,30 @@ function runChecks(packagePath, soft = false) {
|
|
|
80
88
|
console.log(`Checking package ${_path.default.basename(packagePath)}...`);
|
|
81
89
|
showError(errors);
|
|
82
90
|
if (soft || json.version.startsWith('0') || errors.every(w => warns.some(ww => w.includes(ww)))) return true;
|
|
83
|
-
testUtils.exitWithCode(1);
|
|
91
|
+
if (noExit) return false;else testUtils.exitWithCode(1);
|
|
84
92
|
}
|
|
85
93
|
console.log(`Checking package ${_path.default.basename(packagePath)}...\t\t\t\u2713 OK`);
|
|
86
94
|
return true;
|
|
87
95
|
}
|
|
88
96
|
function runChecksRec(dir, soft = false) {
|
|
89
97
|
const files = _fs.default.readdirSync(dir);
|
|
98
|
+
let allPassed = true;
|
|
90
99
|
for (const file of files) {
|
|
91
100
|
const filepath = _path.default.join(dir, file);
|
|
92
101
|
const stats = _fs.default.statSync(filepath);
|
|
93
102
|
if (stats.isDirectory()) {
|
|
94
|
-
if (utils.isPackageDir(filepath))
|
|
95
|
-
|
|
103
|
+
if (utils.isPackageDir(filepath)) {
|
|
104
|
+
const passed = runChecks(filepath, soft, true);
|
|
105
|
+
allPassed = allPassed && passed;
|
|
106
|
+
} else {
|
|
107
|
+
if (file !== 'node_modules' && !file.startsWith('.')) {
|
|
108
|
+
const passed = runChecksRec(_path.default.join(dir, file), soft);
|
|
109
|
+
allPassed = allPassed && passed;
|
|
110
|
+
}
|
|
96
111
|
}
|
|
97
112
|
}
|
|
98
113
|
}
|
|
99
|
-
return
|
|
114
|
+
return allPassed;
|
|
100
115
|
}
|
|
101
116
|
function extractExternals(config) {
|
|
102
117
|
const externalsRegex = /(?<=externals)\s*:\s*(\{[\S\s]*?\})/;
|
|
@@ -144,197 +159,174 @@ function checkImportStatements(packagePath, files, externals) {
|
|
|
144
159
|
}
|
|
145
160
|
return warnings;
|
|
146
161
|
}
|
|
147
|
-
function
|
|
148
|
-
const warnings = [];
|
|
162
|
+
function checkDatagrokApiImports(packagePath, files) {
|
|
149
163
|
const errors = [];
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}) => {
|
|
176
|
-
let value = true;
|
|
177
|
-
let message = '';
|
|
178
|
-
if (inputs.length !== 1 || inputs[0].type !== 'column') {
|
|
179
|
-
value = false;
|
|
180
|
-
message += 'Semantic type detectors must have one input of type "column"\n';
|
|
181
|
-
}
|
|
182
|
-
if (outputs.length !== 1 || outputs[0].type !== 'string') {
|
|
183
|
-
value = false;
|
|
184
|
-
message += 'Semantic type detectors must have one output of type "string"\n';
|
|
185
|
-
}
|
|
186
|
-
return {
|
|
187
|
-
value,
|
|
188
|
-
message
|
|
189
|
-
};
|
|
190
|
-
},
|
|
191
|
-
cellRenderer: ({
|
|
192
|
-
inputs,
|
|
193
|
-
outputs
|
|
194
|
-
}) => {
|
|
195
|
-
let value = true;
|
|
196
|
-
let message = '';
|
|
197
|
-
if (inputs.length !== 0) {
|
|
198
|
-
value = false;
|
|
199
|
-
message += 'Cell renderer functions should take no arguments\n';
|
|
200
|
-
}
|
|
201
|
-
if (outputs.length !== 1 || outputs[0].type !== 'grid_cell_renderer') {
|
|
202
|
-
value = false;
|
|
203
|
-
message += 'Cell renderer functions must have one output of type "grid_cell_renderer"\n';
|
|
204
|
-
}
|
|
205
|
-
return {
|
|
206
|
-
value,
|
|
207
|
-
message
|
|
208
|
-
};
|
|
209
|
-
},
|
|
210
|
-
viewer: ({
|
|
211
|
-
inputs,
|
|
212
|
-
outputs
|
|
213
|
-
}) => {
|
|
214
|
-
let value = true;
|
|
215
|
-
let message = '';
|
|
216
|
-
if (inputs.length !== 0) {
|
|
217
|
-
value = false;
|
|
218
|
-
message += 'Viewer functions should take no arguments\n';
|
|
219
|
-
}
|
|
220
|
-
if (outputs.length > 1 || outputs.length === 1 && outputs[0].type !== 'viewer') {
|
|
221
|
-
value = false;
|
|
222
|
-
message += 'Viewers must have one output of type "viewer"\n';
|
|
223
|
-
}
|
|
224
|
-
return {
|
|
225
|
-
value,
|
|
226
|
-
message
|
|
227
|
-
};
|
|
228
|
-
},
|
|
229
|
-
fileViewer: ({
|
|
230
|
-
inputs,
|
|
231
|
-
outputs,
|
|
232
|
-
tags
|
|
233
|
-
}) => {
|
|
234
|
-
let value = true;
|
|
235
|
-
let message = '';
|
|
236
|
-
if (tags == null || tags.length !== 1 && tags[0] !== 'fileViewer') {
|
|
237
|
-
value = false;
|
|
238
|
-
message += 'File viewers must have only one tag: "fileViewer"\n';
|
|
239
|
-
}
|
|
240
|
-
if (inputs.length !== 1 || inputs[0].type !== 'file') {
|
|
241
|
-
value = false;
|
|
242
|
-
message += 'File viewers must have one input of type "file"\n';
|
|
243
|
-
}
|
|
244
|
-
if (outputs.length !== 1 || outputs[0].type !== 'view') {
|
|
245
|
-
value = false;
|
|
246
|
-
message += 'File viewers must have one output of type "view"\n';
|
|
247
|
-
}
|
|
248
|
-
return {
|
|
249
|
-
value,
|
|
250
|
-
message
|
|
251
|
-
};
|
|
252
|
-
},
|
|
253
|
-
fileExporter: ({
|
|
254
|
-
description
|
|
255
|
-
}) => {
|
|
256
|
-
let value = true;
|
|
257
|
-
let message = '';
|
|
258
|
-
if (description == null || description === '') {
|
|
259
|
-
value = false;
|
|
260
|
-
message += 'File exporters should have a description parameter\n';
|
|
261
|
-
}
|
|
262
|
-
return {
|
|
263
|
-
value,
|
|
264
|
-
message
|
|
265
|
-
};
|
|
266
|
-
},
|
|
267
|
-
packageSettingsEditor: ({
|
|
268
|
-
outputs
|
|
269
|
-
}) => {
|
|
270
|
-
let value = true;
|
|
271
|
-
let message = '';
|
|
272
|
-
if (!(outputs.length === 1 && outputs[0].type === 'widget')) {
|
|
273
|
-
value = false;
|
|
274
|
-
message += 'Package settings editors must have one output of type "widget"\n';
|
|
275
|
-
}
|
|
276
|
-
return {
|
|
277
|
-
value,
|
|
278
|
-
message
|
|
279
|
-
};
|
|
280
|
-
},
|
|
281
|
-
params: ({
|
|
282
|
-
inputs,
|
|
283
|
-
outputs
|
|
284
|
-
}) => {
|
|
285
|
-
let value = true;
|
|
286
|
-
let message = '';
|
|
287
|
-
for (const input of inputs) {
|
|
288
|
-
if (!(input.name && input.type)) {
|
|
289
|
-
value = false;
|
|
290
|
-
message += `Function has no name or type of input parameter\n`;
|
|
164
|
+
|
|
165
|
+
// Regex to find all datagrok-api imports/exports (including re-exports)
|
|
166
|
+
const datagrokApiImportRegex = /^\s*(import|export)\s+.*['"]datagrok-api\/[^'"]+['"]/gm;
|
|
167
|
+
|
|
168
|
+
// Regex to validate if import/export is allowed (only dg, grok, ui)
|
|
169
|
+
const allowedImportRegex = /^\s*(import|export)\s+.*['"]datagrok-api\/(dg|grok|ui)['"]/;
|
|
170
|
+
|
|
171
|
+
// Regex to extract the import path for error messages
|
|
172
|
+
const importPathRegex = /['"]datagrok-api\/([^'"]+)['"]/;
|
|
173
|
+
for (const file of files) {
|
|
174
|
+
const content = _fs.default.readFileSync(_path.default.join(packagePath, file), {
|
|
175
|
+
encoding: 'utf-8'
|
|
176
|
+
});
|
|
177
|
+
const matchedImports = content.match(datagrokApiImportRegex);
|
|
178
|
+
if (matchedImports) {
|
|
179
|
+
for (const match of matchedImports) {
|
|
180
|
+
// Check if this import/export is allowed
|
|
181
|
+
if (!allowedImportRegex.test(match)) {
|
|
182
|
+
// Extract the problematic path for error message
|
|
183
|
+
const pathMatch = match.match(importPathRegex);
|
|
184
|
+
const importedPath = pathMatch ? `datagrok-api/${pathMatch[1]}` : 'unknown';
|
|
185
|
+
errors.push(`File "${file}": Invalid datagrok-api import.
|
|
186
|
+
` + ` Found: ${match.trim()}
|
|
187
|
+
` + ` Only these paths are allowed: 'datagrok-api/dg', 'datagrok-api/grok', 'datagrok-api/ui'
|
|
188
|
+
` + ` Deep imports like '${importedPath}' are not permitted.`);
|
|
291
189
|
}
|
|
292
190
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return errors;
|
|
194
|
+
}
|
|
195
|
+
const TYPE_ALIASES = {
|
|
196
|
+
file: ['fileinfo'],
|
|
197
|
+
dynamic: ['searchprovider']
|
|
198
|
+
};
|
|
199
|
+
function normalizeType(type) {
|
|
200
|
+
return type.toLowerCase().replace(/_/g, '');
|
|
201
|
+
}
|
|
202
|
+
function typesMatch(actual, expected) {
|
|
203
|
+
const a = normalizeType(actual);
|
|
204
|
+
const e = normalizeType(expected);
|
|
205
|
+
if (a === e) return true;
|
|
206
|
+
const aliases = TYPE_ALIASES[a];
|
|
207
|
+
return aliases ? aliases.includes(e) : false;
|
|
208
|
+
}
|
|
209
|
+
function parseSignature(sig) {
|
|
210
|
+
const match = sig.match(/^[^(]+\(([^)]*)\)\s*:\s*(.+)$/);
|
|
211
|
+
if (!match) throw new Error(`Invalid signature format: ${sig}`);
|
|
212
|
+
const paramsStr = match[1].trim();
|
|
213
|
+
const returnTypeStr = match[2].trim();
|
|
214
|
+
let variadicIndex = -1;
|
|
215
|
+
const parseParam = (p, index) => {
|
|
216
|
+
const trimmed = p.trim();
|
|
217
|
+
const isVariadic = trimmed.startsWith('...');
|
|
218
|
+
if (isVariadic) variadicIndex = index;
|
|
219
|
+
const paramBody = isVariadic ? trimmed.slice(3) : trimmed;
|
|
220
|
+
const [name, type] = paramBody.split(':').map(s => s.trim());
|
|
221
|
+
return {
|
|
222
|
+
name,
|
|
223
|
+
type: type || 'any'
|
|
224
|
+
};
|
|
225
|
+
};
|
|
226
|
+
const inputs = paramsStr ? paramsStr.split(',').map((p, i) => parseParam(p, i)) : [];
|
|
227
|
+
const outputs = returnTypeStr ? returnTypeStr.split('|').map(t => ({
|
|
228
|
+
name: '',
|
|
229
|
+
type: t.trim()
|
|
230
|
+
})) : [];
|
|
231
|
+
return {
|
|
232
|
+
inputs,
|
|
233
|
+
outputs,
|
|
234
|
+
variadicIndex
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
function validateFunctionSignature(func, roleDesc) {
|
|
238
|
+
let valid = true;
|
|
239
|
+
const messages = [];
|
|
240
|
+
const addError = msg => {
|
|
241
|
+
valid = false;
|
|
242
|
+
messages.push(msg);
|
|
243
|
+
};
|
|
244
|
+
const normalize = t => t?.toLowerCase();
|
|
245
|
+
const fmtParam = p => p ? `${p.name ?? '<unnamed>'}: ${p.type ?? '<unknown>'}` : '<missing>';
|
|
246
|
+
const fmtTypes = types => types.join(' | ');
|
|
247
|
+
const matchesExpected = (actual, expected) => {
|
|
248
|
+
if (!actual || !expected) return false;
|
|
249
|
+
const actualType = normalize(actual);
|
|
250
|
+
const expectedTypes = expected.split('|').map(t => normalize(t.trim()));
|
|
251
|
+
return expectedTypes.some(t => t === 'any' || typesMatch(actualType, t));
|
|
252
|
+
};
|
|
253
|
+
if (roleDesc.role === 'app' && func.name) {
|
|
254
|
+
const name = func.name.toLowerCase();
|
|
255
|
+
if (name.startsWith('app')) addError('Prefix "App" is not needed. Consider removing it.');
|
|
256
|
+
if (name.endsWith('app')) addError('Postfix "App" is not needed. Consider removing it.');
|
|
257
|
+
}
|
|
258
|
+
if (roleDesc.role === 'fileExporter' && (!func.description || func.description === '')) addError('File exporters should have a description parameter');
|
|
259
|
+
if (roleDesc.role === 'fileViewer') {
|
|
260
|
+
const hasFileViewerTag = func.tags?.length === 1 && func.tags[0] === 'fileViewer';
|
|
261
|
+
const hasFileViewerRole = func.meta?.role === 'fileViewer';
|
|
262
|
+
if (!(hasFileViewerTag || hasFileViewerRole)) addError('File viewers must have only one tag: "fileViewer"');
|
|
263
|
+
}
|
|
264
|
+
const parsed = parseSignature(roleDesc.signature);
|
|
265
|
+
const maxInputs = parsed.variadicIndex >= 0 ? parsed.variadicIndex : parsed.inputs.length;
|
|
266
|
+
for (let i = 0; i < maxInputs; i++) {
|
|
267
|
+
const expected = parsed.inputs[i];
|
|
268
|
+
const actual = func.inputs[i];
|
|
269
|
+
if (!actual) {
|
|
270
|
+
addError(`Input ${i} missing: expected ${fmtParam(expected)}`);
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
if (!matchesExpected(actual.type, expected?.type)) addError(`Input ${i} mismatch: expected ${fmtTypes(expected?.type?.split('|').map(t => t.trim()) ?? [])}, got ${actual.type}`);
|
|
274
|
+
}
|
|
275
|
+
if (parsed.outputs.length > 0) {
|
|
276
|
+
if (!func.outputs?.length) {
|
|
277
|
+
if (!parsed.outputs.some(o => o.type === 'void')) addError(`Output missing: expected one of (${fmtTypes(parsed.outputs.map(o => o.type))})`);
|
|
278
|
+
} else {
|
|
279
|
+
const matches = func.outputs.some(actual => parsed.outputs.some(expected => matchesExpected(actual.type, expected.type)));
|
|
280
|
+
if (!matches) {
|
|
281
|
+
addError(`Output mismatch: expected one of (${fmtTypes(parsed.outputs.map(o => o.type))}),
|
|
282
|
+
got (${fmtTypes(func.outputs.map(o => o.type))})`);
|
|
298
283
|
}
|
|
299
|
-
return {
|
|
300
|
-
value,
|
|
301
|
-
message
|
|
302
|
-
};
|
|
303
284
|
}
|
|
285
|
+
}
|
|
286
|
+
[...func.inputs, ...(func.outputs ?? [])].forEach((p, i) => {
|
|
287
|
+
if (!p.name || !p.type) addError(`Parameter ${i} is incomplete: name=${p.name ?? '<missing>'}, type=${p.type ?? '<missing>'}`);
|
|
288
|
+
});
|
|
289
|
+
return {
|
|
290
|
+
value: valid,
|
|
291
|
+
message: messages.join('\n')
|
|
304
292
|
};
|
|
305
|
-
|
|
293
|
+
}
|
|
294
|
+
function checkFuncSignatures(packagePath, files) {
|
|
295
|
+
const warnings = [];
|
|
296
|
+
const errors = [];
|
|
297
|
+
const namesInFiles = new Map();
|
|
298
|
+
const roleMap = new Map(_const.functionRoles.map(r => [r.role, r]));
|
|
306
299
|
for (const file of files) {
|
|
307
300
|
if (file.includes('.min.')) continue;
|
|
308
|
-
const content = _fs.default.readFileSync(_path.default.join(packagePath, file),
|
|
309
|
-
encoding: 'utf-8'
|
|
310
|
-
});
|
|
301
|
+
const content = _fs.default.readFileSync(_path.default.join(packagePath, file), 'utf-8');
|
|
311
302
|
const functions = getFuncMetadata(content, file.split('.').pop() ?? 'ts');
|
|
312
303
|
for (const f of functions.meta) {
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
const vr =
|
|
318
|
-
if (!vr.value) {
|
|
319
|
-
warnings.push(`File ${file}, function ${f.name}:\n${vr.message}`);
|
|
320
|
-
}
|
|
304
|
+
const allRoles = new Set([...(f.tags ?? []), ...(f.meta?.role?.split(',').map(r => r.trim()) ?? [])]);
|
|
305
|
+
const roles = [...allRoles].filter(r => roleMap.has(r));
|
|
306
|
+
for (const role of roles) {
|
|
307
|
+
const roleDesc = roleMap.get(role);
|
|
308
|
+
const vr = validateFunctionSignature(f, roleDesc);
|
|
309
|
+
if (!vr.value) warnings.push(`File ${file}, function ${f.name}:\n${vr.message}`);
|
|
321
310
|
}
|
|
322
|
-
|
|
311
|
+
const invalidNames = f.inputs.filter(e => forbiddenNames.includes(e?.name ?? ''));
|
|
312
|
+
if (invalidNames.length) errors.push(`File ${file}, function ${f.name}: Wrong input names: (${invalidNames.map(e => e.name).join(', ')})`);
|
|
323
313
|
if (f.name && f.name !== 'postprocess') {
|
|
324
|
-
if (namesInFiles.has(f.name)) namesInFiles.
|
|
314
|
+
if (!namesInFiles.has(f.name)) namesInFiles.set(f.name, []);
|
|
315
|
+
namesInFiles.get(f.name).push(file);
|
|
325
316
|
}
|
|
326
|
-
if (
|
|
327
|
-
if (f.
|
|
328
|
-
if (f.
|
|
329
|
-
if (f.invalidateOn) if (!utils.isValidCron(f.invalidateOn)) errors.push(`File ${file}, function ${f.name}: unsupposed variable for invalidateOn : ${f.invalidateOn}`);
|
|
317
|
+
if (f.isInvalidateOnWithoutCache) errors.push(`File ${file}, function ${f.name}: Can't use invalidateOn without cache`);
|
|
318
|
+
if (f.cache && !utils.cacheValues.includes(f.cache)) errors.push(`File ${file}, function ${f.name}: unsupported cache variable: ${f.cache}`);
|
|
319
|
+
if (f.invalidateOn && !utils.isValidCron(f.invalidateOn)) errors.push(`File ${file}, function ${f.name}: unsupported invalidateOn: ${f.invalidateOn}`);
|
|
330
320
|
}
|
|
331
|
-
functions.warnings.forEach(
|
|
332
|
-
warnings.push(`${e} In the file: ${file}.`);
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
for (const [name, files] of namesInFiles) {
|
|
336
|
-
if (files.length > 1) errors.push(`Duplicate names ('${name}'): \n ${files.join('\n ')}`);
|
|
321
|
+
functions.warnings.forEach(w => warnings.push(`${w} In the file: ${file}.`));
|
|
337
322
|
}
|
|
323
|
+
|
|
324
|
+
// Temporarily skip name-checking logic, as duplicate names are allowed
|
|
325
|
+
// for (const [name, files] of namesInFiles) {
|
|
326
|
+
// if (files.length > 1)
|
|
327
|
+
// errors.push(`Duplicate names ('${name}'): \n ${files.join('\n ')}`);
|
|
328
|
+
// }
|
|
329
|
+
|
|
338
330
|
return [warnings, errors];
|
|
339
331
|
}
|
|
340
332
|
const sharedLibExternals = {
|
|
@@ -405,14 +397,14 @@ function checkPackageFile(packagePath, json, options) {
|
|
|
405
397
|
}
|
|
406
398
|
if (options?.isReleaseCandidateVersion === true) {
|
|
407
399
|
let hasRCDependency = false;
|
|
408
|
-
for (
|
|
400
|
+
for (const dependency of Object.keys(json.dependencies ?? {})) {
|
|
409
401
|
if (/\d+.\d+.\d+-rc(.[A-Za-z0-9]*.[A-Za-z0-9]*)?/.test((json.dependencies ?? {})[dependency])) {
|
|
410
402
|
hasRCDependency = true;
|
|
411
403
|
break;
|
|
412
404
|
}
|
|
413
405
|
}
|
|
414
406
|
if (!hasRCDependency) {
|
|
415
|
-
for (
|
|
407
|
+
for (const dependency of Object.keys(json.dependencies ?? {})) {
|
|
416
408
|
if (/\d+.\d+.\d+-rc(.[A-Za-z0-9]*.[A-Za-z0-9]*)?/.test((json.devDependencies ?? {})[dependency])) {
|
|
417
409
|
hasRCDependency = true;
|
|
418
410
|
break;
|
|
@@ -463,8 +455,27 @@ function checkSourceMap(packagePath) {
|
|
|
463
455
|
}); // cant convert to json because file contains comments
|
|
464
456
|
|
|
465
457
|
if (!new RegExp(`devtool\\s*:\\s*(([^\\n]*?[^\\n]*source-map[^\\n]*:[^\\n]*source-map[^\\n]*)|('(inline-)?source-map'))\\s*`).test(webpackConfigJson)) warnings.push('webpack config doesnt contain source map');
|
|
466
|
-
|
|
467
|
-
|
|
458
|
+
|
|
459
|
+
// Check if dist files exist
|
|
460
|
+
const distPackage = _path.default.join(packagePath, 'dist', 'package.js');
|
|
461
|
+
const distPackageTest = _path.default.join(packagePath, 'dist', 'package-test.js');
|
|
462
|
+
let missingFiles = [distPackage, distPackageTest].filter(f => !_fs.default.existsSync(f));
|
|
463
|
+
|
|
464
|
+
// If any dist files are missing, try to build automatically
|
|
465
|
+
if (missingFiles.length > 0) {
|
|
466
|
+
try {
|
|
467
|
+
(0, _child_process.execSync)('npm run build', {
|
|
468
|
+
cwd: packagePath,
|
|
469
|
+
stdio: 'inherit'
|
|
470
|
+
});
|
|
471
|
+
} catch (e) {
|
|
472
|
+
console.warn('Build failed:', e);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Recheck dist files after build
|
|
476
|
+
missingFiles = [distPackage, distPackageTest].filter(f => !_fs.default.existsSync(f));
|
|
477
|
+
missingFiles.forEach(f => warnings.push(`${_path.default.relative(packagePath, f)} file doesnt exist even after build`));
|
|
478
|
+
}
|
|
468
479
|
}
|
|
469
480
|
return warnings;
|
|
470
481
|
}
|
|
@@ -502,9 +513,7 @@ function getAllFilesInDirectory(directoryPath) {
|
|
|
502
513
|
entries.forEach(entry => {
|
|
503
514
|
const entryPath = _path.default.join(directoryPath, entry);
|
|
504
515
|
const stat = _fs.default.statSync(entryPath);
|
|
505
|
-
if (stat.isFile()) {
|
|
506
|
-
fileNames.push(entry);
|
|
507
|
-
} else if (stat.isDirectory() && !excludedFilesToCheck.includes(entry)) {
|
|
516
|
+
if (stat.isFile()) fileNames.push(entry);else if (stat.isDirectory() && !excludedFilesToCheck.includes(entry)) {
|
|
508
517
|
const subDirectoryFiles = getAllFilesInDirectory(entryPath);
|
|
509
518
|
fileNames = fileNames.concat(subDirectoryFiles);
|
|
510
519
|
}
|
|
@@ -517,66 +526,94 @@ function showError(errors) {
|
|
|
517
526
|
function warn(warnings) {
|
|
518
527
|
warnings.forEach(w => color.warn(w));
|
|
519
528
|
}
|
|
520
|
-
function getFuncMetadata(script,
|
|
529
|
+
function getFuncMetadata(script, fileExtension) {
|
|
521
530
|
const funcData = [];
|
|
522
531
|
const warnings = [];
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
const
|
|
533
|
-
|
|
534
|
-
if (!
|
|
532
|
+
const lines = script.split('\n');
|
|
533
|
+
const funcStartRegex = /^\s*function\s+\w+\s*\(|^\s*(export\s+)?(async\s+)?function\s+\w+\s*\(|^\s*(export\s+)?(const|let)\s+\w+\s*=\s*\(.*\)\s*=>/;
|
|
534
|
+
function parseHeaderLines(headerLines) {
|
|
535
|
+
const data = {
|
|
536
|
+
name: '',
|
|
537
|
+
inputs: [],
|
|
538
|
+
outputs: []
|
|
539
|
+
};
|
|
540
|
+
let hasContent = false;
|
|
541
|
+
for (const headerLine of headerLines) {
|
|
542
|
+
const match = headerLine.match(utils.fileParamRegex[fileExtension]);
|
|
543
|
+
if (!match) continue;
|
|
535
544
|
const param = match[1];
|
|
536
|
-
if (!utils.headerTags.includes(param) && !param.
|
|
537
|
-
warnings.push(`Unknown header tag: ${param}
|
|
545
|
+
if (!utils.headerTags.includes(param) && !param.startsWith('meta.')) {
|
|
546
|
+
warnings.push(`Unknown header tag: ${param}`);
|
|
538
547
|
continue;
|
|
539
548
|
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
549
|
+
switch (param) {
|
|
550
|
+
case 'name':
|
|
551
|
+
data.name = headerLine.match(utils.nameAnnRegex)?.[2]?.toLocaleLowerCase() || '';
|
|
552
|
+
hasContent = true;
|
|
553
|
+
break;
|
|
554
|
+
case 'description':
|
|
555
|
+
data.description = match[2];
|
|
556
|
+
hasContent = true;
|
|
557
|
+
break;
|
|
558
|
+
case 'input':
|
|
559
|
+
data.inputs.push({
|
|
560
|
+
type: match[2],
|
|
561
|
+
name: match[3]
|
|
562
|
+
});
|
|
563
|
+
hasContent = true;
|
|
564
|
+
break;
|
|
565
|
+
case 'output':
|
|
566
|
+
data.outputs.push({
|
|
567
|
+
type: match[2],
|
|
568
|
+
name: match[3]
|
|
569
|
+
});
|
|
570
|
+
hasContent = true;
|
|
571
|
+
break;
|
|
572
|
+
case 'tags':
|
|
573
|
+
data.tags = match.input && match[3] ? match.input.split(':')[1].split(',').map(t => t.trim()) : [match[2]];
|
|
574
|
+
hasContent = true;
|
|
575
|
+
break;
|
|
576
|
+
case 'meta.role':
|
|
577
|
+
data.meta = data.meta || {};
|
|
578
|
+
data.meta.role = match[2];
|
|
579
|
+
hasContent = true;
|
|
580
|
+
break;
|
|
581
|
+
case 'meta.cache':
|
|
582
|
+
data.cache = headerLine.split(':').pop()?.trim();
|
|
583
|
+
hasContent = true;
|
|
584
|
+
break;
|
|
585
|
+
case 'meta.cache.invalidateOn':
|
|
586
|
+
data.invalidateOn = headerLine.split(':').pop()?.trim();
|
|
587
|
+
hasContent = true;
|
|
588
|
+
break;
|
|
589
|
+
case 'meta.invalidateOn':
|
|
590
|
+
data.isInvalidateOnWithoutCache = true;
|
|
591
|
+
hasContent = true;
|
|
592
|
+
break;
|
|
556
593
|
}
|
|
557
594
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
isHeader = false;
|
|
578
|
-
}
|
|
595
|
+
return hasContent ? data : null;
|
|
596
|
+
}
|
|
597
|
+
let i = 0;
|
|
598
|
+
const scriptHeaderLines = [];
|
|
599
|
+
while (i < lines.length) {
|
|
600
|
+
const line = lines[i].trim();
|
|
601
|
+
if (line.startsWith('//')) scriptHeaderLines.push(line);else if (line === '') break;else break;
|
|
602
|
+
i++;
|
|
603
|
+
}
|
|
604
|
+
const scriptData = parseHeaderLines(scriptHeaderLines);
|
|
605
|
+
if (scriptData) funcData.push(scriptData);
|
|
606
|
+
for (; i < lines.length; i++) {
|
|
607
|
+
const line = lines[i].trim();
|
|
608
|
+
if (!line.match(funcStartRegex)) continue;
|
|
609
|
+
const headerLines = [];
|
|
610
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
611
|
+
const prevLine = lines[j].trim();
|
|
612
|
+
if (!prevLine.startsWith('//')) break;
|
|
613
|
+
headerLines.unshift(prevLine);
|
|
579
614
|
}
|
|
615
|
+
const funcMeta = parseHeaderLines(headerLines);
|
|
616
|
+
if (funcMeta) funcData.push(funcMeta);
|
|
580
617
|
}
|
|
581
618
|
return {
|
|
582
619
|
meta: funcData,
|