datagrok-tools 4.14.68 → 4.14.70
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 +8 -0
- package/CLAUDE.md +208 -0
- package/bin/commands/add.js +6 -5
- package/bin/commands/check.js +246 -244
- package/bin/commands/help.js +12 -0
- package/bin/commands/migrate.js +74 -0
- package/bin/commands/stress-tests.js +26 -2
- 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
|
@@ -18,6 +18,8 @@ var _ignoreWalk = _interopRequireDefault(require("ignore-walk"));
|
|
|
18
18
|
var utils = _interopRequireWildcard(require("../utils/utils"));
|
|
19
19
|
var color = _interopRequireWildcard(require("../utils/color-utils"));
|
|
20
20
|
var testUtils = _interopRequireWildcard(require("../utils/test-utils"));
|
|
21
|
+
var _const = require("datagrok-api/src/const");
|
|
22
|
+
var _child_process = require("child_process");
|
|
21
23
|
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
24
|
const warns = ['Latest package version', 'Datagrok API version should contain'];
|
|
23
25
|
const forbiddenNames = ['function', 'class', 'export'];
|
|
@@ -33,7 +35,7 @@ function check(args) {
|
|
|
33
35
|
return runChecks(curDir, args.soft ?? false);
|
|
34
36
|
}
|
|
35
37
|
}
|
|
36
|
-
function runChecks(packagePath, soft = false) {
|
|
38
|
+
function runChecks(packagePath, soft = false, noExit = false) {
|
|
37
39
|
if (packagePath.includes(`${_path.default.sep}node_modules${_path.default.sep}`)) return true;
|
|
38
40
|
const files = _ignoreWalk.default.sync({
|
|
39
41
|
path: packagePath,
|
|
@@ -50,8 +52,12 @@ function runChecks(packagePath, soft = false) {
|
|
|
50
52
|
}));
|
|
51
53
|
const webpackConfigPath = _path.default.join(packagePath, 'webpack.config.js');
|
|
52
54
|
const isWebpack = _fs.default.existsSync(webpackConfigPath);
|
|
55
|
+
const semver = json.version.split('-')[0];
|
|
56
|
+
const [major, minor, patch] = semver.split('.').map(Number);
|
|
57
|
+
let isPre1Version = false;
|
|
53
58
|
let isReleaseCandidateVersion = false;
|
|
54
59
|
let externals = null;
|
|
60
|
+
if (major === 0) isPre1Version = true;
|
|
55
61
|
if (/\d+.\d+.\d+-rc(.[A-Za-z0-9]*.[A-Za-z0-9]*)?/.test(json.version)) isReleaseCandidateVersion = true;
|
|
56
62
|
if (isWebpack) {
|
|
57
63
|
const content = _fs.default.readFileSync(webpackConfigPath, {
|
|
@@ -71,7 +77,7 @@ function runChecks(packagePath, soft = false) {
|
|
|
71
77
|
externals,
|
|
72
78
|
isReleaseCandidateVersion
|
|
73
79
|
}));
|
|
74
|
-
if (!isReleaseCandidateVersion) warnings.push(...checkChangelog(packagePath, json));
|
|
80
|
+
if (!isReleaseCandidateVersion && !isPre1Version) warnings.push(...checkChangelog(packagePath, json));
|
|
75
81
|
if (warnings.length) {
|
|
76
82
|
console.log(`${_path.default.basename(packagePath)} warnings`);
|
|
77
83
|
warn(warnings);
|
|
@@ -80,23 +86,30 @@ function runChecks(packagePath, soft = false) {
|
|
|
80
86
|
console.log(`Checking package ${_path.default.basename(packagePath)}...`);
|
|
81
87
|
showError(errors);
|
|
82
88
|
if (soft || json.version.startsWith('0') || errors.every(w => warns.some(ww => w.includes(ww)))) return true;
|
|
83
|
-
testUtils.exitWithCode(1);
|
|
89
|
+
if (noExit) return false;else testUtils.exitWithCode(1);
|
|
84
90
|
}
|
|
85
91
|
console.log(`Checking package ${_path.default.basename(packagePath)}...\t\t\t\u2713 OK`);
|
|
86
92
|
return true;
|
|
87
93
|
}
|
|
88
94
|
function runChecksRec(dir, soft = false) {
|
|
89
95
|
const files = _fs.default.readdirSync(dir);
|
|
96
|
+
let allPassed = true;
|
|
90
97
|
for (const file of files) {
|
|
91
98
|
const filepath = _path.default.join(dir, file);
|
|
92
99
|
const stats = _fs.default.statSync(filepath);
|
|
93
100
|
if (stats.isDirectory()) {
|
|
94
|
-
if (utils.isPackageDir(filepath))
|
|
95
|
-
|
|
101
|
+
if (utils.isPackageDir(filepath)) {
|
|
102
|
+
const passed = runChecks(filepath, soft, true);
|
|
103
|
+
allPassed = allPassed && passed;
|
|
104
|
+
} else {
|
|
105
|
+
if (file !== 'node_modules' && !file.startsWith('.')) {
|
|
106
|
+
const passed = runChecksRec(_path.default.join(dir, file), soft);
|
|
107
|
+
allPassed = allPassed && passed;
|
|
108
|
+
}
|
|
96
109
|
}
|
|
97
110
|
}
|
|
98
111
|
}
|
|
99
|
-
return
|
|
112
|
+
return allPassed;
|
|
100
113
|
}
|
|
101
114
|
function extractExternals(config) {
|
|
102
115
|
const externalsRegex = /(?<=externals)\s*:\s*(\{[\S\s]*?\})/;
|
|
@@ -144,197 +157,141 @@ function checkImportStatements(packagePath, files, externals) {
|
|
|
144
157
|
}
|
|
145
158
|
return warnings;
|
|
146
159
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
outputs,
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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`;
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
for (const output of outputs) {
|
|
294
|
-
if (!(output.name && output.type)) {
|
|
295
|
-
value = false;
|
|
296
|
-
message += `Function has no name or type of output parameter\n`;
|
|
297
|
-
}
|
|
160
|
+
const TYPE_ALIASES = {
|
|
161
|
+
file: ['fileinfo'],
|
|
162
|
+
dynamic: ['searchprovider']
|
|
163
|
+
};
|
|
164
|
+
function normalizeType(type) {
|
|
165
|
+
return type.toLowerCase().replace(/_/g, '');
|
|
166
|
+
}
|
|
167
|
+
function typesMatch(actual, expected) {
|
|
168
|
+
const a = normalizeType(actual);
|
|
169
|
+
const e = normalizeType(expected);
|
|
170
|
+
if (a === e) return true;
|
|
171
|
+
const aliases = TYPE_ALIASES[a];
|
|
172
|
+
return aliases ? aliases.includes(e) : false;
|
|
173
|
+
}
|
|
174
|
+
function parseSignature(sig) {
|
|
175
|
+
const match = sig.match(/^[^(]+\(([^)]*)\)\s*:\s*(.+)$/);
|
|
176
|
+
if (!match) throw new Error(`Invalid signature format: ${sig}`);
|
|
177
|
+
const paramsStr = match[1].trim();
|
|
178
|
+
const returnTypeStr = match[2].trim();
|
|
179
|
+
let variadicIndex = -1;
|
|
180
|
+
const parseParam = (p, index) => {
|
|
181
|
+
const trimmed = p.trim();
|
|
182
|
+
const isVariadic = trimmed.startsWith('...');
|
|
183
|
+
if (isVariadic) variadicIndex = index;
|
|
184
|
+
const paramBody = isVariadic ? trimmed.slice(3) : trimmed;
|
|
185
|
+
const [name, type] = paramBody.split(':').map(s => s.trim());
|
|
186
|
+
return {
|
|
187
|
+
name,
|
|
188
|
+
type: type || 'any'
|
|
189
|
+
};
|
|
190
|
+
};
|
|
191
|
+
const inputs = paramsStr ? paramsStr.split(',').map((p, i) => parseParam(p, i)) : [];
|
|
192
|
+
const outputs = returnTypeStr ? returnTypeStr.split('|').map(t => ({
|
|
193
|
+
name: '',
|
|
194
|
+
type: t.trim()
|
|
195
|
+
})) : [];
|
|
196
|
+
return {
|
|
197
|
+
inputs,
|
|
198
|
+
outputs,
|
|
199
|
+
variadicIndex
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function validateFunctionSignature(func, roleDesc) {
|
|
203
|
+
let valid = true;
|
|
204
|
+
const messages = [];
|
|
205
|
+
const addError = msg => {
|
|
206
|
+
valid = false;
|
|
207
|
+
messages.push(msg);
|
|
208
|
+
};
|
|
209
|
+
const normalize = t => t?.toLowerCase();
|
|
210
|
+
const fmtParam = p => p ? `${p.name ?? '<unnamed>'}: ${p.type ?? '<unknown>'}` : '<missing>';
|
|
211
|
+
const fmtTypes = types => types.join(' | ');
|
|
212
|
+
const matchesExpected = (actual, expected) => {
|
|
213
|
+
if (!actual || !expected) return false;
|
|
214
|
+
const actualType = normalize(actual);
|
|
215
|
+
const expectedTypes = expected.split('|').map(t => normalize(t.trim()));
|
|
216
|
+
return expectedTypes.some(t => t === 'any' || typesMatch(actualType, t));
|
|
217
|
+
};
|
|
218
|
+
if (roleDesc.role === 'app' && func.name) {
|
|
219
|
+
const name = func.name.toLowerCase();
|
|
220
|
+
if (name.startsWith('app')) addError('Prefix "App" is not needed. Consider removing it.');
|
|
221
|
+
if (name.endsWith('app')) addError('Postfix "App" is not needed. Consider removing it.');
|
|
222
|
+
}
|
|
223
|
+
if (roleDesc.role === 'fileExporter' && (!func.description || func.description === '')) addError('File exporters should have a description parameter');
|
|
224
|
+
if (roleDesc.role === 'fileViewer') {
|
|
225
|
+
const hasFileViewerTag = func.tags?.length === 1 && func.tags[0] === 'fileViewer';
|
|
226
|
+
const hasFileViewerRole = func.meta?.role === 'fileViewer';
|
|
227
|
+
if (!(hasFileViewerTag || hasFileViewerRole)) addError('File viewers must have only one tag: "fileViewer"');
|
|
228
|
+
}
|
|
229
|
+
const parsed = parseSignature(roleDesc.signature);
|
|
230
|
+
const maxInputs = parsed.variadicIndex >= 0 ? parsed.variadicIndex : parsed.inputs.length;
|
|
231
|
+
for (let i = 0; i < maxInputs; i++) {
|
|
232
|
+
const expected = parsed.inputs[i];
|
|
233
|
+
const actual = func.inputs[i];
|
|
234
|
+
if (!actual) {
|
|
235
|
+
addError(`Input ${i} missing: expected ${fmtParam(expected)}`);
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
if (!matchesExpected(actual.type, expected?.type)) addError(`Input ${i} mismatch: expected ${fmtTypes(expected?.type?.split('|').map(t => t.trim()) ?? [])}, got ${actual.type}`);
|
|
239
|
+
}
|
|
240
|
+
if (parsed.outputs.length > 0) {
|
|
241
|
+
if (!func.outputs?.length) {
|
|
242
|
+
if (!parsed.outputs.some(o => o.type === 'void')) addError(`Output missing: expected one of (${fmtTypes(parsed.outputs.map(o => o.type))})`);
|
|
243
|
+
} else {
|
|
244
|
+
const matches = func.outputs.some(actual => parsed.outputs.some(expected => matchesExpected(actual.type, expected.type)));
|
|
245
|
+
if (!matches) {
|
|
246
|
+
addError(`Output mismatch: expected one of (${fmtTypes(parsed.outputs.map(o => o.type))}),
|
|
247
|
+
got (${fmtTypes(func.outputs.map(o => o.type))})`);
|
|
298
248
|
}
|
|
299
|
-
return {
|
|
300
|
-
value,
|
|
301
|
-
message
|
|
302
|
-
};
|
|
303
249
|
}
|
|
250
|
+
}
|
|
251
|
+
[...func.inputs, ...(func.outputs ?? [])].forEach((p, i) => {
|
|
252
|
+
if (!p.name || !p.type) addError(`Parameter ${i} is incomplete: name=${p.name ?? '<missing>'}, type=${p.type ?? '<missing>'}`);
|
|
253
|
+
});
|
|
254
|
+
return {
|
|
255
|
+
value: valid,
|
|
256
|
+
message: messages.join('\n')
|
|
304
257
|
};
|
|
305
|
-
|
|
258
|
+
}
|
|
259
|
+
function checkFuncSignatures(packagePath, files) {
|
|
260
|
+
const warnings = [];
|
|
261
|
+
const errors = [];
|
|
262
|
+
const namesInFiles = new Map();
|
|
263
|
+
const roleMap = new Map(_const.functionRoles.map(r => [r.role, r]));
|
|
306
264
|
for (const file of files) {
|
|
307
265
|
if (file.includes('.min.')) continue;
|
|
308
|
-
const content = _fs.default.readFileSync(_path.default.join(packagePath, file),
|
|
309
|
-
encoding: 'utf-8'
|
|
310
|
-
});
|
|
266
|
+
const content = _fs.default.readFileSync(_path.default.join(packagePath, file), 'utf-8');
|
|
311
267
|
const functions = getFuncMetadata(content, file.split('.').pop() ?? 'ts');
|
|
312
268
|
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
|
-
}
|
|
269
|
+
const allRoles = new Set([...(f.tags ?? []), ...(f.meta?.role?.split(',').map(r => r.trim()) ?? [])]);
|
|
270
|
+
const roles = [...allRoles].filter(r => roleMap.has(r));
|
|
271
|
+
for (const role of roles) {
|
|
272
|
+
const roleDesc = roleMap.get(role);
|
|
273
|
+
const vr = validateFunctionSignature(f, roleDesc);
|
|
274
|
+
if (!vr.value) warnings.push(`File ${file}, function ${f.name}:\n${vr.message}`);
|
|
321
275
|
}
|
|
322
|
-
|
|
276
|
+
const invalidNames = f.inputs.filter(e => forbiddenNames.includes(e?.name ?? ''));
|
|
277
|
+
if (invalidNames.length) errors.push(`File ${file}, function ${f.name}: Wrong input names: (${invalidNames.map(e => e.name).join(', ')})`);
|
|
323
278
|
if (f.name && f.name !== 'postprocess') {
|
|
324
|
-
if (namesInFiles.has(f.name)) namesInFiles.
|
|
279
|
+
if (!namesInFiles.has(f.name)) namesInFiles.set(f.name, []);
|
|
280
|
+
namesInFiles.get(f.name).push(file);
|
|
325
281
|
}
|
|
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}`);
|
|
282
|
+
if (f.isInvalidateOnWithoutCache) errors.push(`File ${file}, function ${f.name}: Can't use invalidateOn without cache`);
|
|
283
|
+
if (f.cache && !utils.cacheValues.includes(f.cache)) errors.push(`File ${file}, function ${f.name}: unsupported cache variable: ${f.cache}`);
|
|
284
|
+
if (f.invalidateOn && !utils.isValidCron(f.invalidateOn)) errors.push(`File ${file}, function ${f.name}: unsupported invalidateOn: ${f.invalidateOn}`);
|
|
330
285
|
}
|
|
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 ')}`);
|
|
286
|
+
functions.warnings.forEach(w => warnings.push(`${w} In the file: ${file}.`));
|
|
337
287
|
}
|
|
288
|
+
|
|
289
|
+
// Temporarily skip name-checking logic, as duplicate names are allowed
|
|
290
|
+
// for (const [name, files] of namesInFiles) {
|
|
291
|
+
// if (files.length > 1)
|
|
292
|
+
// errors.push(`Duplicate names ('${name}'): \n ${files.join('\n ')}`);
|
|
293
|
+
// }
|
|
294
|
+
|
|
338
295
|
return [warnings, errors];
|
|
339
296
|
}
|
|
340
297
|
const sharedLibExternals = {
|
|
@@ -405,14 +362,14 @@ function checkPackageFile(packagePath, json, options) {
|
|
|
405
362
|
}
|
|
406
363
|
if (options?.isReleaseCandidateVersion === true) {
|
|
407
364
|
let hasRCDependency = false;
|
|
408
|
-
for (
|
|
365
|
+
for (const dependency of Object.keys(json.dependencies ?? {})) {
|
|
409
366
|
if (/\d+.\d+.\d+-rc(.[A-Za-z0-9]*.[A-Za-z0-9]*)?/.test((json.dependencies ?? {})[dependency])) {
|
|
410
367
|
hasRCDependency = true;
|
|
411
368
|
break;
|
|
412
369
|
}
|
|
413
370
|
}
|
|
414
371
|
if (!hasRCDependency) {
|
|
415
|
-
for (
|
|
372
|
+
for (const dependency of Object.keys(json.dependencies ?? {})) {
|
|
416
373
|
if (/\d+.\d+.\d+-rc(.[A-Za-z0-9]*.[A-Za-z0-9]*)?/.test((json.devDependencies ?? {})[dependency])) {
|
|
417
374
|
hasRCDependency = true;
|
|
418
375
|
break;
|
|
@@ -463,8 +420,27 @@ function checkSourceMap(packagePath) {
|
|
|
463
420
|
}); // cant convert to json because file contains comments
|
|
464
421
|
|
|
465
422
|
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
|
-
|
|
423
|
+
|
|
424
|
+
// Check if dist files exist
|
|
425
|
+
const distPackage = _path.default.join(packagePath, 'dist', 'package.js');
|
|
426
|
+
const distPackageTest = _path.default.join(packagePath, 'dist', 'package-test.js');
|
|
427
|
+
let missingFiles = [distPackage, distPackageTest].filter(f => !_fs.default.existsSync(f));
|
|
428
|
+
|
|
429
|
+
// If any dist files are missing, try to build automatically
|
|
430
|
+
if (missingFiles.length > 0) {
|
|
431
|
+
try {
|
|
432
|
+
(0, _child_process.execSync)('npm run build', {
|
|
433
|
+
cwd: packagePath,
|
|
434
|
+
stdio: 'inherit'
|
|
435
|
+
});
|
|
436
|
+
} catch (e) {
|
|
437
|
+
console.warn('Build failed:', e);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Recheck dist files after build
|
|
441
|
+
missingFiles = [distPackage, distPackageTest].filter(f => !_fs.default.existsSync(f));
|
|
442
|
+
missingFiles.forEach(f => warnings.push(`${_path.default.relative(packagePath, f)} file doesnt exist even after build`));
|
|
443
|
+
}
|
|
468
444
|
}
|
|
469
445
|
return warnings;
|
|
470
446
|
}
|
|
@@ -502,9 +478,7 @@ function getAllFilesInDirectory(directoryPath) {
|
|
|
502
478
|
entries.forEach(entry => {
|
|
503
479
|
const entryPath = _path.default.join(directoryPath, entry);
|
|
504
480
|
const stat = _fs.default.statSync(entryPath);
|
|
505
|
-
if (stat.isFile()) {
|
|
506
|
-
fileNames.push(entry);
|
|
507
|
-
} else if (stat.isDirectory() && !excludedFilesToCheck.includes(entry)) {
|
|
481
|
+
if (stat.isFile()) fileNames.push(entry);else if (stat.isDirectory() && !excludedFilesToCheck.includes(entry)) {
|
|
508
482
|
const subDirectoryFiles = getAllFilesInDirectory(entryPath);
|
|
509
483
|
fileNames = fileNames.concat(subDirectoryFiles);
|
|
510
484
|
}
|
|
@@ -517,66 +491,94 @@ function showError(errors) {
|
|
|
517
491
|
function warn(warnings) {
|
|
518
492
|
warnings.forEach(w => color.warn(w));
|
|
519
493
|
}
|
|
520
|
-
function getFuncMetadata(script,
|
|
494
|
+
function getFuncMetadata(script, fileExtension) {
|
|
521
495
|
const funcData = [];
|
|
522
496
|
const warnings = [];
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
const
|
|
533
|
-
|
|
534
|
-
if (!
|
|
497
|
+
const lines = script.split('\n');
|
|
498
|
+
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*=>/;
|
|
499
|
+
function parseHeaderLines(headerLines) {
|
|
500
|
+
const data = {
|
|
501
|
+
name: '',
|
|
502
|
+
inputs: [],
|
|
503
|
+
outputs: []
|
|
504
|
+
};
|
|
505
|
+
let hasContent = false;
|
|
506
|
+
for (const headerLine of headerLines) {
|
|
507
|
+
const match = headerLine.match(utils.fileParamRegex[fileExtension]);
|
|
508
|
+
if (!match) continue;
|
|
535
509
|
const param = match[1];
|
|
536
|
-
if (!utils.headerTags.includes(param) && !param.
|
|
537
|
-
warnings.push(`Unknown header tag: ${param}
|
|
510
|
+
if (!utils.headerTags.includes(param) && !param.startsWith('meta.')) {
|
|
511
|
+
warnings.push(`Unknown header tag: ${param}`);
|
|
538
512
|
continue;
|
|
539
513
|
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
514
|
+
switch (param) {
|
|
515
|
+
case 'name':
|
|
516
|
+
data.name = headerLine.match(utils.nameAnnRegex)?.[2]?.toLocaleLowerCase() || '';
|
|
517
|
+
hasContent = true;
|
|
518
|
+
break;
|
|
519
|
+
case 'description':
|
|
520
|
+
data.description = match[2];
|
|
521
|
+
hasContent = true;
|
|
522
|
+
break;
|
|
523
|
+
case 'input':
|
|
524
|
+
data.inputs.push({
|
|
525
|
+
type: match[2],
|
|
526
|
+
name: match[3]
|
|
527
|
+
});
|
|
528
|
+
hasContent = true;
|
|
529
|
+
break;
|
|
530
|
+
case 'output':
|
|
531
|
+
data.outputs.push({
|
|
532
|
+
type: match[2],
|
|
533
|
+
name: match[3]
|
|
534
|
+
});
|
|
535
|
+
hasContent = true;
|
|
536
|
+
break;
|
|
537
|
+
case 'tags':
|
|
538
|
+
data.tags = match.input && match[3] ? match.input.split(':')[1].split(',').map(t => t.trim()) : [match[2]];
|
|
539
|
+
hasContent = true;
|
|
540
|
+
break;
|
|
541
|
+
case 'meta.role':
|
|
542
|
+
data.meta = data.meta || {};
|
|
543
|
+
data.meta.role = match[2];
|
|
544
|
+
hasContent = true;
|
|
545
|
+
break;
|
|
546
|
+
case 'meta.cache':
|
|
547
|
+
data.cache = headerLine.split(':').pop()?.trim();
|
|
548
|
+
hasContent = true;
|
|
549
|
+
break;
|
|
550
|
+
case 'meta.cache.invalidateOn':
|
|
551
|
+
data.invalidateOn = headerLine.split(':').pop()?.trim();
|
|
552
|
+
hasContent = true;
|
|
553
|
+
break;
|
|
554
|
+
case 'meta.invalidateOn':
|
|
555
|
+
data.isInvalidateOnWithoutCache = true;
|
|
556
|
+
hasContent = true;
|
|
557
|
+
break;
|
|
556
558
|
}
|
|
557
559
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
isHeader = false;
|
|
578
|
-
}
|
|
560
|
+
return hasContent ? data : null;
|
|
561
|
+
}
|
|
562
|
+
let i = 0;
|
|
563
|
+
const scriptHeaderLines = [];
|
|
564
|
+
while (i < lines.length) {
|
|
565
|
+
const line = lines[i].trim();
|
|
566
|
+
if (line.startsWith('//')) scriptHeaderLines.push(line);else if (line === '') break;else break;
|
|
567
|
+
i++;
|
|
568
|
+
}
|
|
569
|
+
const scriptData = parseHeaderLines(scriptHeaderLines);
|
|
570
|
+
if (scriptData) funcData.push(scriptData);
|
|
571
|
+
for (; i < lines.length; i++) {
|
|
572
|
+
const line = lines[i].trim();
|
|
573
|
+
if (!line.match(funcStartRegex)) continue;
|
|
574
|
+
const headerLines = [];
|
|
575
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
576
|
+
const prevLine = lines[j].trim();
|
|
577
|
+
if (!prevLine.startsWith('//')) break;
|
|
578
|
+
headerLines.unshift(prevLine);
|
|
579
579
|
}
|
|
580
|
+
const funcMeta = parseHeaderLines(headerLines);
|
|
581
|
+
if (funcMeta) funcData.push(funcMeta);
|
|
580
582
|
}
|
|
581
583
|
return {
|
|
582
584
|
meta: funcData,
|
package/bin/commands/help.js
CHANGED
|
@@ -20,6 +20,7 @@ Commands:
|
|
|
20
20
|
publish Upload a package
|
|
21
21
|
test Run package tests
|
|
22
22
|
testall Run packages tests
|
|
23
|
+
migrate Migrate legacy tags to meta.role
|
|
23
24
|
|
|
24
25
|
To get help on a particular command, use:
|
|
25
26
|
grok <command> --help
|
|
@@ -209,6 +210,16 @@ Options:
|
|
|
209
210
|
--verbose Prints detailed information about linked packages
|
|
210
211
|
--all Links all available packages(run in packages directory)
|
|
211
212
|
`;
|
|
213
|
+
const HELP_MIGRATE = `
|
|
214
|
+
Usage: grok migrate
|
|
215
|
+
|
|
216
|
+
Migrates legacy function tags into the meta.role field.
|
|
217
|
+
|
|
218
|
+
Example:
|
|
219
|
+
tags: ['viewer', 'ml']
|
|
220
|
+
⟶
|
|
221
|
+
meta: { role: 'viewer,ml' }
|
|
222
|
+
`;
|
|
212
223
|
|
|
213
224
|
// const HELP_MIGRATE = `
|
|
214
225
|
// Usage: grok migrate
|
|
@@ -228,5 +239,6 @@ const help = exports.help = {
|
|
|
228
239
|
publish: HELP_PUBLISH,
|
|
229
240
|
test: HELP_TEST,
|
|
230
241
|
testall: HELP_TESTALL,
|
|
242
|
+
migrate: HELP_MIGRATE,
|
|
231
243
|
help: HELP
|
|
232
244
|
};
|